{"record":{"id":"bbc475cabc93362b","repo":"moeru-ai/airi","slug":"use-invoke-with-a-desktop-command-or-help","errorCode":null,"errorMessage":"Use invoke with a desktop command or --help.","messagePattern":"Use invoke with a desktop command or --help\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/stage-tamagotchi/src/main/services/airi/computer-use/runtime.ts","lineNumber":88,"sourceCode":"      socketDirectory = undefined\n    }\n  }\n\n  function enqueue<T>(operation: () => Promise<T>): Promise<T> {\n    const next = queue.then(() => {\n      if (disposed)\n        throw new Error('Computer use has stopped.')\n      return operation()\n    })\n    // A failed command must not prevent later commands from running.\n    queue = next.then(() => undefined, () => undefined)\n    return next\n  }\n\n  async function run(input: unknown): Promise<ComputerUseResult> {\n    const { argv } = v.parse(inputSchema, input)\n    if (argv[0] !== 'invoke' || !/^(?:--help|-h|help|(?:app|display|screen|window|input|mediaControl)\\.[a-zA-Z]+)$/.test(argv[1]))\n      throw new Error('Use invoke with a desktop command or --help.')\n\n    // The host owns routing and artifact storage. CLI global flags must not replace them.\n    const managedFlags = ['--store-root', '--endpoint', '--device', '--device-id', '--run', '--config', '--profile', '--discovery-file', '--pairing-store']\n    if (argv.some(arg => managedFlags.some(flag => arg === flag || arg.startsWith(`${flag}=`))))\n      throw new Error('Computer use routing and storage are managed by AIRI.')\n\n    return enqueue(async () => {\n      const active = await connect()\n      const command = [...argv]\n      if (!command.includes('--json') && !command.includes('--help') && !command.includes('-h') && command[1] !== 'help')\n        command.push('--json')\n      command.push('--store-root', options.storeRoot)\n      let stdout: string\n      let stderr: string\n      let exitCode = 0\n      try {\n        ({ stdout, stderr } = await execFileAsync(options.binaryPath, command, {\n          env: { ...process.env, AUV_ENDPOINT: active.connectionOptions.endpoint, AUV_CONTEXT: undefined },","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/moeru-ai/airi/blob/438a067dde47aa0bdb46c2323d1fe293dc805218/apps/stage-tamagotchi/src/main/services/airi/computer-use/runtime.ts#L70-L106","documentation":"run() validates the parsed argv against the small supported surface: it must start with 'invoke' followed by --help/-h/help or a command of the form app.*/display.*/screen.*/window.*/input.*/mediaControl.*. Anything else is rejected because the host only exposes a curated subset of the computer-use CLI.","triggerScenarios":"Passing argv[0] other than 'invoke', an unknown command namespace (e.g. 'browser.open'), or a bare subcommand name not matching the regex as argv[1].","commonSituations":"Copy-pasting CLI examples from upstream docs that use subcommands AIRI does not expose; typos like 'invok'; passing empty or malformed argv from an LLM tool call.","solutions":["Use argv = ['invoke', '<command>'] where command matches app.*/display.*/screen.*/window.*/input.*/mediaControl.* or --help/-h/help","Check the exposed command list in the runtime/CLI help before constructing argv","Validate the LLM-generated command against the regex before calling run"],"exampleFix":"// before\nawait run({ argv: ['devices', 'list'] })\n// after\nawait run({ argv: ['invoke', 'app.list'] })","handlingStrategy":"validation","validationCode":"const COMMAND_RE = /^(?:--help|-h|help|(?:app|display|screen|window|input|mediaControl)\\.[a-zA-Z]+)$/nfunction isValidInvoke(argv: unknown[]): boolean {\n  return argv[0] === 'invoke' && COMMAND_RE.test(String(argv[1]))\n}","typeGuard":"function isComputerUseArgv(v: unknown): v is ['invoke', string, ...string[]] {\n  return Array.isArray(v) && v[0] === 'invoke' && typeof v[1] === 'string'\n    && /^(?:--help|-h|help|(?:app|display|screen|window|input|mediaControl)\\.[a-zA-Z]+)$/.test(v[1])\n}","tryCatchPattern":"try {\n  return await run({ argv })\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('Use invoke with a desktop command')) {\n    console.warn(`Unsupported command: ${argv.join(' ')}; see help for allowed commands`)\n    return helpResult\n  }\n  throw e\n}","preventionTips":["Keep a whitelist of allowed commands and generate argv from it, not from free-form strings","Sanitize LLM tool-call output against the same regex the runtime uses","Prefer calling --help first to discover the supported command surface"],"tags":["validation","cli","whitelist"],"backgroundTag":"invalid-cli-argument","analyzedSha":"438a067dde47aa0bdb46c2323d1fe293dc805218","analyzedAt":"2026-09-17T01:14:42.644Z","contentChangedAt":"2026-09-17T01:14:42.644Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}