refactor(cli): split oversized command modules (#9775)
This commit is contained in:
parent
7ca3e670c5
commit
f1c84d3858
|
|
@ -22,7 +22,7 @@
|
|||
{
|
||||
"name": "linear-tickets",
|
||||
"sourcePath": "skills/linear-tickets",
|
||||
"releaseRevision": 5,
|
||||
"releaseRevision": 6,
|
||||
"packageDigest": "ff9f085631f753f059c631d874177ddd4fa847c5eca85a420dc85fb2bece6ff6",
|
||||
"gitTreeSha": "e35ac3c0c583661983d3fc1352ff3aec74e67e8c",
|
||||
"files": [
|
||||
|
|
@ -94,7 +94,7 @@
|
|||
{
|
||||
"name": "orca-linear",
|
||||
"sourcePath": "skills/orca-linear",
|
||||
"releaseRevision": 3,
|
||||
"releaseRevision": 4,
|
||||
"packageDigest": "5e9622bd3883c0f53e6bd349758096deafceebd2fa260d3e90d677e64d06416d",
|
||||
"gitTreeSha": "f3727995a4719fd522119eca6d1b57542cb5fe23",
|
||||
"files": [
|
||||
|
|
|
|||
|
|
@ -548,6 +548,19 @@
|
|||
"orca-per-workspace-env": 2,
|
||||
"orchestration": 25
|
||||
}
|
||||
},
|
||||
{
|
||||
"appVersion": "1.4.149-rc.1.issue7936",
|
||||
"skills": {
|
||||
"computer-use": 5,
|
||||
"linear-tickets": 5,
|
||||
"orca-cli": 35,
|
||||
"orca-emulator": 4,
|
||||
"orca-emulator-android": 2,
|
||||
"orca-linear": 3,
|
||||
"orca-per-workspace-env": 2,
|
||||
"orchestration": 25
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1198,6 +1198,22 @@
|
|||
},
|
||||
{
|
||||
"releaseRevision": 5,
|
||||
"packageDigest": "30f836d836df611c0c974ac0ee48d817fbe6711a49465d139f0981cbd75f828e",
|
||||
"gitTreeSha": "8e862333bdeb9970e11dcd6634e493ecc6ff6698",
|
||||
"files": [
|
||||
{
|
||||
"path": "SKILL.md",
|
||||
"size": 11514,
|
||||
"executable": false,
|
||||
"classification": "text",
|
||||
"exactSha256": "922c1740ffe8be685ed35e6f51c7baa81f246e1738c8ecff224fca2b64a415b9",
|
||||
"textNormalizedSha256": "922c1740ffe8be685ed35e6f51c7baa81f246e1738c8ecff224fca2b64a415b9",
|
||||
"identitySha256": "922c1740ffe8be685ed35e6f51c7baa81f246e1738c8ecff224fca2b64a415b9"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"releaseRevision": 6,
|
||||
"packageDigest": "ff9f085631f753f059c631d874177ddd4fa847c5eca85a420dc85fb2bece6ff6",
|
||||
"gitTreeSha": "e35ac3c0c583661983d3fc1352ff3aec74e67e8c",
|
||||
"files": [
|
||||
|
|
@ -1248,6 +1264,22 @@
|
|||
},
|
||||
{
|
||||
"releaseRevision": 3,
|
||||
"packageDigest": "2fcccfe7c3166ca0b78a50ccdfd7acad4c6e6eeffa14e48ed1edee53c796e1f0",
|
||||
"gitTreeSha": "761d4427e9f330607664da5d6ff55400be8c690b",
|
||||
"files": [
|
||||
{
|
||||
"path": "SKILL.md",
|
||||
"size": 11238,
|
||||
"executable": false,
|
||||
"classification": "text",
|
||||
"exactSha256": "04ef6cb0377bc7c2dd38dc713b4ef5196ee6851dc90e9fdcddbc2347faa678e6",
|
||||
"textNormalizedSha256": "04ef6cb0377bc7c2dd38dc713b4ef5196ee6851dc90e9fdcddbc2347faa678e6",
|
||||
"identitySha256": "04ef6cb0377bc7c2dd38dc713b4ef5196ee6851dc90e9fdcddbc2347faa678e6"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"releaseRevision": 4,
|
||||
"packageDigest": "5e9622bd3883c0f53e6bd349758096deafceebd2fa260d3e90d677e64d06416d",
|
||||
"gitTreeSha": "f3727995a4719fd522119eca6d1b57542cb5fe23",
|
||||
"files": [
|
||||
|
|
|
|||
|
|
@ -0,0 +1,96 @@
|
|||
import type { CommandSpec } from '../args'
|
||||
import { GLOBAL_FLAGS } from '../args'
|
||||
|
||||
export const LINEAR_MCP_COMMAND_SPECS: CommandSpec[] = [
|
||||
{
|
||||
path: ['linear', 'save-issue'],
|
||||
summary: 'Create or update a Linear issue',
|
||||
usage:
|
||||
'orca linear save-issue [<id>] [--current] [--team <key|id>] [--title <title>] [--description <text> | --body-file <path|->] [--state <state>] [--assignee me|<user>|null] [--priority none|low|medium|high|urgent] [--estimate <number>|null] [--due-date <yyyy-mm-dd>|null] [--label <label>...] [--project <project>|null] [--parent-id <issue>|null] [--write-id <uuid>] [--workspace <id>] [--json]',
|
||||
allowedFlags: [
|
||||
...GLOBAL_FLAGS,
|
||||
'current',
|
||||
'team',
|
||||
'title',
|
||||
'description',
|
||||
'body',
|
||||
'body-file',
|
||||
'state',
|
||||
'assignee',
|
||||
'priority',
|
||||
'estimate',
|
||||
'due-date',
|
||||
'label',
|
||||
'project',
|
||||
'parent-id',
|
||||
'write-id',
|
||||
'workspace',
|
||||
'id'
|
||||
],
|
||||
positionalArgs: ['id'],
|
||||
examples: [
|
||||
'orca linear save-issue --team ENG --title "Fix auth" --priority high --json',
|
||||
'orca linear save-issue ENG-123 --title "Fix OAuth callback" --assignee me --json',
|
||||
'orca linear save-issue --current --project null --due-date null --json'
|
||||
],
|
||||
notes: [
|
||||
'Without <id> or --current, creates an issue and requires --team and --title.',
|
||||
'Labels replace the complete label set, matching Linear MCP save_issue semantics.',
|
||||
'Use the literal null to clear assignee, estimate, due date, project, or parent.'
|
||||
]
|
||||
},
|
||||
{
|
||||
path: ['linear', 'list-issues'],
|
||||
summary: 'List Linear issues with MCP-compatible filters',
|
||||
usage:
|
||||
'orca linear list-issues [--team <team>] [--cycle <cycle>] [--label <label>] [--limit <n>] [--query <text>] [--state <state>] [--cursor <cursor>] [--order-by createdAt|updatedAt] [--project <project>] [--release <release>] [--assignee <user|me|null>] [--delegate <user|me|null>] [--parent-id <issue|null>] [--priority <0-4>] [--created-at <datetime|duration>] [--updated-at <datetime|duration>] [--include-archived] [--workspace <id>|all] [--json]',
|
||||
allowedFlags: [
|
||||
...GLOBAL_FLAGS,
|
||||
'team',
|
||||
'cycle',
|
||||
'label',
|
||||
'limit',
|
||||
'query',
|
||||
'state',
|
||||
'cursor',
|
||||
'order-by',
|
||||
'project',
|
||||
'release',
|
||||
'assignee',
|
||||
'delegate',
|
||||
'parent-id',
|
||||
'priority',
|
||||
'created-at',
|
||||
'updated-at',
|
||||
'include-archived',
|
||||
'workspace'
|
||||
],
|
||||
examples: [
|
||||
'orca linear list-issues --team ENG --state started --assignee me --json',
|
||||
'orca linear list-issues --query auth --updated-at -P7D --limit 100 --json',
|
||||
'orca linear list-issues --cursor <cursor> --workspace <id> --json'
|
||||
]
|
||||
},
|
||||
{
|
||||
path: ['linear', 'relation', 'add'],
|
||||
summary: 'Add a Linear issue relation',
|
||||
usage:
|
||||
'orca linear relation add [<id>] [--current] --related <issue> --type blocks|blocked-by|related|duplicate-of [--workspace <id>] [--json]',
|
||||
allowedFlags: [...GLOBAL_FLAGS, 'current', 'related', 'type', 'workspace', 'id'],
|
||||
positionalArgs: ['id'],
|
||||
examples: [
|
||||
'orca linear relation add ENG-1 --related ENG-2 --type blocks --json',
|
||||
'orca linear relation add --current --related ENG-2 --type blocked-by --json'
|
||||
]
|
||||
},
|
||||
{
|
||||
path: ['linear', 'relation', 'remove'],
|
||||
aliases: [['linear', 'relation', 'rm']],
|
||||
summary: 'Remove a Linear issue relation',
|
||||
usage:
|
||||
'orca linear relation remove [<id>] [--current] --related <issue> --type blocks|blocked-by|related|duplicate-of [--workspace <id>] [--json]',
|
||||
allowedFlags: [...GLOBAL_FLAGS, 'current', 'related', 'type', 'workspace', 'id'],
|
||||
positionalArgs: ['id'],
|
||||
examples: ['orca linear relation remove ENG-1 --related ENG-2 --type related --json']
|
||||
}
|
||||
]
|
||||
|
|
@ -1,98 +1,9 @@
|
|||
import type { CommandSpec } from '../args'
|
||||
import { GLOBAL_FLAGS } from '../args'
|
||||
import { LINEAR_MCP_COMMAND_SPECS } from './linear-mcp'
|
||||
|
||||
export const LINEAR_COMMAND_SPECS: CommandSpec[] = [
|
||||
{
|
||||
path: ['linear', 'save-issue'],
|
||||
summary: 'Create or update a Linear issue',
|
||||
usage:
|
||||
'orca linear save-issue [<id>] [--current] [--team <key|id>] [--title <title>] [--description <text> | --body-file <path|->] [--state <state>] [--assignee me|<user>|null] [--priority none|low|medium|high|urgent] [--estimate <number>|null] [--due-date <yyyy-mm-dd>|null] [--label <label>...] [--project <project>|null] [--parent-id <issue>|null] [--write-id <uuid>] [--workspace <id>] [--json]',
|
||||
allowedFlags: [
|
||||
...GLOBAL_FLAGS,
|
||||
'current',
|
||||
'team',
|
||||
'title',
|
||||
'description',
|
||||
'body',
|
||||
'body-file',
|
||||
'state',
|
||||
'assignee',
|
||||
'priority',
|
||||
'estimate',
|
||||
'due-date',
|
||||
'label',
|
||||
'project',
|
||||
'parent-id',
|
||||
'write-id',
|
||||
'workspace',
|
||||
'id'
|
||||
],
|
||||
positionalArgs: ['id'],
|
||||
examples: [
|
||||
'orca linear save-issue --team ENG --title "Fix auth" --priority high --json',
|
||||
'orca linear save-issue ENG-123 --title "Fix OAuth callback" --assignee me --json',
|
||||
'orca linear save-issue --current --project null --due-date null --json'
|
||||
],
|
||||
notes: [
|
||||
'Without <id> or --current, creates an issue and requires --team and --title.',
|
||||
'Labels replace the complete label set, matching Linear MCP save_issue semantics.',
|
||||
'Use the literal null to clear assignee, estimate, due date, project, or parent.'
|
||||
]
|
||||
},
|
||||
{
|
||||
path: ['linear', 'list-issues'],
|
||||
summary: 'List Linear issues with MCP-compatible filters',
|
||||
usage:
|
||||
'orca linear list-issues [--team <team>] [--cycle <cycle>] [--label <label>] [--limit <n>] [--query <text>] [--state <state>] [--cursor <cursor>] [--order-by createdAt|updatedAt] [--project <project>] [--release <release>] [--assignee <user|me|null>] [--delegate <user|me|null>] [--parent-id <issue|null>] [--priority <0-4>] [--created-at <datetime|duration>] [--updated-at <datetime|duration>] [--include-archived] [--workspace <id>|all] [--json]',
|
||||
allowedFlags: [
|
||||
...GLOBAL_FLAGS,
|
||||
'team',
|
||||
'cycle',
|
||||
'label',
|
||||
'limit',
|
||||
'query',
|
||||
'state',
|
||||
'cursor',
|
||||
'order-by',
|
||||
'project',
|
||||
'release',
|
||||
'assignee',
|
||||
'delegate',
|
||||
'parent-id',
|
||||
'priority',
|
||||
'created-at',
|
||||
'updated-at',
|
||||
'include-archived',
|
||||
'workspace'
|
||||
],
|
||||
examples: [
|
||||
'orca linear list-issues --team ENG --state started --assignee me --json',
|
||||
'orca linear list-issues --query auth --updated-at -P7D --limit 100 --json',
|
||||
'orca linear list-issues --cursor <cursor> --workspace <id> --json'
|
||||
]
|
||||
},
|
||||
{
|
||||
path: ['linear', 'relation', 'add'],
|
||||
summary: 'Add a Linear issue relation',
|
||||
usage:
|
||||
'orca linear relation add [<id>] [--current] --related <issue> --type blocks|blocked-by|related|duplicate-of [--workspace <id>] [--json]',
|
||||
allowedFlags: [...GLOBAL_FLAGS, 'current', 'related', 'type', 'workspace', 'id'],
|
||||
positionalArgs: ['id'],
|
||||
examples: [
|
||||
'orca linear relation add ENG-1 --related ENG-2 --type blocks --json',
|
||||
'orca linear relation add --current --related ENG-2 --type blocked-by --json'
|
||||
]
|
||||
},
|
||||
{
|
||||
path: ['linear', 'relation', 'remove'],
|
||||
aliases: [['linear', 'relation', 'rm']],
|
||||
summary: 'Remove a Linear issue relation',
|
||||
usage:
|
||||
'orca linear relation remove [<id>] [--current] --related <issue> --type blocks|blocked-by|related|duplicate-of [--workspace <id>] [--json]',
|
||||
allowedFlags: [...GLOBAL_FLAGS, 'current', 'related', 'type', 'workspace', 'id'],
|
||||
positionalArgs: ['id'],
|
||||
examples: ['orca linear relation remove ENG-1 --related ENG-2 --type related --json']
|
||||
},
|
||||
...LINEAR_MCP_COMMAND_SPECS,
|
||||
{
|
||||
path: ['linear', 'issue'],
|
||||
summary: 'Read Linear issue context for agents',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,117 @@
|
|||
import { RemoteCliArgumentError, type ParsedRemoteCli } from './ssh-remote-cli-argument-error'
|
||||
|
||||
const REMOTE_BOOLEAN_FLAGS = new Set([
|
||||
'all',
|
||||
'attachments',
|
||||
'children',
|
||||
'comments',
|
||||
'current',
|
||||
'full',
|
||||
'help',
|
||||
'inject',
|
||||
'include-archived',
|
||||
'json',
|
||||
'me',
|
||||
'relations',
|
||||
'parent-current',
|
||||
'unread',
|
||||
'wait'
|
||||
])
|
||||
const REPEATED_FLAG_SEPARATOR = '\u0000'
|
||||
const REPEATABLE_REMOTE_STRING_FLAGS = new Set(['label'])
|
||||
|
||||
export function parseRemoteCliArgs(argv: string[]): ParsedRemoteCli {
|
||||
const commandPath: string[] = []
|
||||
const flags = new Map<string, string | boolean>()
|
||||
for (let i = 0; i < argv.length; i += 1) {
|
||||
const token = argv[i]
|
||||
if (!token.startsWith('--')) {
|
||||
commandPath.push(token)
|
||||
continue
|
||||
}
|
||||
const assignment = token.slice(2)
|
||||
// Why: the SSH relay-backed shim should accept values beginning with `--` via `--flag=value`.
|
||||
const equalsIndex = assignment.indexOf('=')
|
||||
if (equalsIndex !== -1) {
|
||||
setRemoteFlag(flags, assignment.slice(0, equalsIndex), assignment.slice(equalsIndex + 1))
|
||||
continue
|
||||
}
|
||||
|
||||
const flag = assignment
|
||||
const next = argv[i + 1]
|
||||
if (!isRemoteBooleanFlag(flag, commandPath) && next && !next.startsWith('--')) {
|
||||
setRemoteFlag(flags, flag, next)
|
||||
i += 1
|
||||
} else {
|
||||
setRemoteFlag(flags, flag, true)
|
||||
}
|
||||
}
|
||||
return { commandPath, flags }
|
||||
}
|
||||
|
||||
export function resolveRemoteCliHandle(
|
||||
flags: Map<string, string | boolean>,
|
||||
env: Record<string, string>,
|
||||
flagName: string
|
||||
): string {
|
||||
return optionalRemoteCliString(flags, flagName) ?? env.ORCA_TERMINAL_HANDLE ?? 'unknown'
|
||||
}
|
||||
|
||||
export function requiredRemoteCliString(
|
||||
flags: Map<string, string | boolean>,
|
||||
name: string
|
||||
): string {
|
||||
const value = optionalRemoteCliString(flags, name)
|
||||
if (!value) {
|
||||
throw new Error(`Missing --${name}`)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
export function optionalRemoteCliString(
|
||||
flags: Map<string, string | boolean>,
|
||||
name: string
|
||||
): string | undefined {
|
||||
const value = flags.get(name)
|
||||
return typeof value === 'string' && value.length > 0 ? value : undefined
|
||||
}
|
||||
|
||||
export function optionalRemoteCliNumber(
|
||||
flags: Map<string, string | boolean>,
|
||||
name: string
|
||||
): number | undefined {
|
||||
const value = optionalRemoteCliString(flags, name)
|
||||
if (value === undefined) {
|
||||
return undefined
|
||||
}
|
||||
const parsed = Number(value)
|
||||
if (!Number.isFinite(parsed)) {
|
||||
throw new RemoteCliArgumentError('invalid_argument', `Invalid numeric value for --${name}`)
|
||||
}
|
||||
return parsed
|
||||
}
|
||||
|
||||
function isRemoteBooleanFlag(flag: string, commandPath: string[]): boolean {
|
||||
// Why: Android launch already uses --activity <name>; only Linear issue reads use it as a boolean.
|
||||
return (
|
||||
REMOTE_BOOLEAN_FLAGS.has(flag) ||
|
||||
(flag === 'activity' && commandPath[0] === 'linear' && commandPath[1] === 'issue')
|
||||
)
|
||||
}
|
||||
|
||||
function setRemoteFlag(
|
||||
flags: Map<string, string | boolean>,
|
||||
name: string,
|
||||
value: string | boolean
|
||||
): void {
|
||||
const previous = flags.get(name)
|
||||
if (
|
||||
typeof previous === 'string' &&
|
||||
typeof value === 'string' &&
|
||||
REPEATABLE_REMOTE_STRING_FLAGS.has(name)
|
||||
) {
|
||||
flags.set(name, `${previous}${REPEATED_FLAG_SEPARATOR}${value}`)
|
||||
return
|
||||
}
|
||||
flags.set(name, value)
|
||||
}
|
||||
|
|
@ -11,6 +11,13 @@ import {
|
|||
type RemoteOrcaCliResult
|
||||
} from './ssh-remote-cli-host-passthrough'
|
||||
import { RemoteCliArgumentError, type ParsedRemoteCli } from './ssh-remote-cli-argument-error'
|
||||
import {
|
||||
optionalRemoteCliNumber,
|
||||
optionalRemoteCliString,
|
||||
parseRemoteCliArgs,
|
||||
requiredRemoteCliString,
|
||||
resolveRemoteCliHandle
|
||||
} from './ssh-remote-cli-args'
|
||||
import { getRemoteLinearHelp, tryDispatchRemoteLinearCli } from './ssh-remote-linear-cli'
|
||||
import {
|
||||
getRemoteOrchestrationPayload,
|
||||
|
|
@ -32,26 +39,6 @@ const HOST_INTERACTIVE_COMMANDS: Record<string, string> = {
|
|||
'orca agent-teams-tmux is a tmux pane shim for the Orca host machine and cannot run through the SSH relay bridge.'
|
||||
}
|
||||
|
||||
const REMOTE_BOOLEAN_FLAGS = new Set([
|
||||
'all',
|
||||
'attachments',
|
||||
'children',
|
||||
'comments',
|
||||
'current',
|
||||
'full',
|
||||
'help',
|
||||
'inject',
|
||||
'include-archived',
|
||||
'json',
|
||||
'me',
|
||||
'relations',
|
||||
'parent-current',
|
||||
'unread',
|
||||
'wait'
|
||||
])
|
||||
const REPEATED_FLAG_SEPARATOR = '\u0000'
|
||||
const REPEATABLE_REMOTE_STRING_FLAGS = new Set(['label'])
|
||||
|
||||
export async function runRemoteOrcaCli(
|
||||
runtime: OrcaRuntimeService,
|
||||
request: RemoteOrcaCliRequest,
|
||||
|
|
@ -175,19 +162,19 @@ async function dispatchRemoteCli(
|
|||
}
|
||||
case 'terminal list':
|
||||
return await call(dispatcher, 'terminal.list', {
|
||||
worktree: optionalString(parsed.flags, 'worktree'),
|
||||
limit: optionalNumber(parsed.flags, 'limit')
|
||||
worktree: optionalRemoteCliString(parsed.flags, 'worktree'),
|
||||
limit: optionalRemoteCliNumber(parsed.flags, 'limit')
|
||||
})
|
||||
case 'orchestration send': {
|
||||
const type = optionalString(parsed.flags, 'type')
|
||||
const type = optionalRemoteCliString(parsed.flags, 'type')
|
||||
return await call(dispatcher, 'orchestration.send', {
|
||||
from: resolveRemoteOrchestrationSender(parsed.flags, env, type),
|
||||
to: requiredString(parsed.flags, 'to'),
|
||||
subject: requiredString(parsed.flags, 'subject'),
|
||||
body: optionalString(parsed.flags, 'body'),
|
||||
to: requiredRemoteCliString(parsed.flags, 'to'),
|
||||
subject: requiredRemoteCliString(parsed.flags, 'subject'),
|
||||
body: optionalRemoteCliString(parsed.flags, 'body'),
|
||||
type,
|
||||
priority: optionalString(parsed.flags, 'priority'),
|
||||
threadId: optionalString(parsed.flags, 'thread-id'),
|
||||
priority: optionalRemoteCliString(parsed.flags, 'priority'),
|
||||
threadId: optionalRemoteCliString(parsed.flags, 'thread-id'),
|
||||
payload: getRemoteOrchestrationPayload(parsed.flags),
|
||||
// Why: the legacy in-process bridge must preserve the same pane
|
||||
// authority as the full host CLI passthrough.
|
||||
|
|
@ -196,24 +183,24 @@ async function dispatchRemoteCli(
|
|||
}
|
||||
case 'orchestration check':
|
||||
return await call(dispatcher, 'orchestration.check', {
|
||||
terminal: resolveHandle(parsed.flags, env, 'terminal'),
|
||||
terminal: resolveRemoteCliHandle(parsed.flags, env, 'terminal'),
|
||||
unread: parsed.flags.has('unread') ? true : undefined,
|
||||
all: parsed.flags.has('all') ? true : undefined,
|
||||
types: optionalString(parsed.flags, 'types'),
|
||||
types: optionalRemoteCliString(parsed.flags, 'types'),
|
||||
inject: parsed.flags.has('inject') ? true : undefined,
|
||||
wait: parsed.flags.has('wait') ? true : undefined,
|
||||
timeoutMs: optionalNumber(parsed.flags, 'timeout-ms')
|
||||
timeoutMs: optionalRemoteCliNumber(parsed.flags, 'timeout-ms')
|
||||
})
|
||||
case 'orchestration reply':
|
||||
return await call(dispatcher, 'orchestration.reply', {
|
||||
id: requiredString(parsed.flags, 'id'),
|
||||
body: requiredString(parsed.flags, 'body'),
|
||||
from: resolveHandle(parsed.flags, env, 'from')
|
||||
id: requiredRemoteCliString(parsed.flags, 'id'),
|
||||
body: requiredRemoteCliString(parsed.flags, 'body'),
|
||||
from: resolveRemoteCliHandle(parsed.flags, env, 'from')
|
||||
})
|
||||
case 'orchestration inbox':
|
||||
return await call(dispatcher, 'orchestration.inbox', {
|
||||
limit: optionalNumber(parsed.flags, 'limit'),
|
||||
terminal: optionalString(parsed.flags, 'terminal')
|
||||
limit: optionalRemoteCliNumber(parsed.flags, 'limit'),
|
||||
terminal: optionalRemoteCliString(parsed.flags, 'terminal')
|
||||
})
|
||||
default:
|
||||
// Why: only reachable when the full host CLI could not be launched;
|
||||
|
|
@ -238,94 +225,6 @@ async function call(
|
|||
})
|
||||
}
|
||||
|
||||
function parseRemoteCliArgs(argv: string[]): ParsedRemoteCli {
|
||||
const commandPath: string[] = []
|
||||
const flags = new Map<string, string | boolean>()
|
||||
for (let i = 0; i < argv.length; i += 1) {
|
||||
const token = argv[i]
|
||||
if (!token.startsWith('--')) {
|
||||
commandPath.push(token)
|
||||
continue
|
||||
}
|
||||
const assignment = token.slice(2)
|
||||
// Why: the SSH relay-backed shim should accept the same `--flag=value`
|
||||
// form as the local CLI, including values that themselves start with `--`.
|
||||
const equalsIndex = assignment.indexOf('=')
|
||||
if (equalsIndex !== -1) {
|
||||
setRemoteFlag(flags, assignment.slice(0, equalsIndex), assignment.slice(equalsIndex + 1))
|
||||
continue
|
||||
}
|
||||
|
||||
const flag = assignment
|
||||
const next = argv[i + 1]
|
||||
if (!isRemoteBooleanFlag(flag, commandPath) && next && !next.startsWith('--')) {
|
||||
setRemoteFlag(flags, flag, next)
|
||||
i += 1
|
||||
} else {
|
||||
setRemoteFlag(flags, flag, true)
|
||||
}
|
||||
}
|
||||
return { commandPath, flags }
|
||||
}
|
||||
|
||||
function isRemoteBooleanFlag(flag: string, commandPath: string[]): boolean {
|
||||
// Why: Android launch already uses --activity <name>; only Linear issue reads use it as a boolean.
|
||||
return (
|
||||
REMOTE_BOOLEAN_FLAGS.has(flag) ||
|
||||
(flag === 'activity' && commandPath[0] === 'linear' && commandPath[1] === 'issue')
|
||||
)
|
||||
}
|
||||
|
||||
function setRemoteFlag(
|
||||
flags: Map<string, string | boolean>,
|
||||
name: string,
|
||||
value: string | boolean
|
||||
): void {
|
||||
const previous = flags.get(name)
|
||||
if (
|
||||
typeof previous === 'string' &&
|
||||
typeof value === 'string' &&
|
||||
REPEATABLE_REMOTE_STRING_FLAGS.has(name)
|
||||
) {
|
||||
flags.set(name, `${previous}${REPEATED_FLAG_SEPARATOR}${value}`)
|
||||
return
|
||||
}
|
||||
flags.set(name, value)
|
||||
}
|
||||
|
||||
function resolveHandle(
|
||||
flags: Map<string, string | boolean>,
|
||||
env: Record<string, string>,
|
||||
flagName: string
|
||||
): string {
|
||||
return optionalString(flags, flagName) ?? env.ORCA_TERMINAL_HANDLE ?? 'unknown'
|
||||
}
|
||||
|
||||
function requiredString(flags: Map<string, string | boolean>, name: string): string {
|
||||
const value = optionalString(flags, name)
|
||||
if (!value) {
|
||||
throw new Error(`Missing --${name}`)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
function optionalString(flags: Map<string, string | boolean>, name: string): string | undefined {
|
||||
const value = flags.get(name)
|
||||
return typeof value === 'string' && value.length > 0 ? value : undefined
|
||||
}
|
||||
|
||||
function optionalNumber(flags: Map<string, string | boolean>, name: string): number | undefined {
|
||||
const value = optionalString(flags, name)
|
||||
if (value === undefined) {
|
||||
return undefined
|
||||
}
|
||||
const parsed = Number(value)
|
||||
if (!Number.isFinite(parsed)) {
|
||||
throw new RemoteCliArgumentError('invalid_argument', `Invalid numeric value for --${name}`)
|
||||
}
|
||||
return parsed
|
||||
}
|
||||
|
||||
function buildLocalError(message: string, code = 'runtime_error'): RpcResponse {
|
||||
return {
|
||||
id: 'remote-cli-local',
|
||||
|
|
|
|||
Loading…
Reference in New Issue