@martini-film/cli 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,7 +7,7 @@ in. One HTTPS request per call, a static key, no MCP session.
7
7
 
8
8
  ```bash
9
9
  npx @martini-film/cli --help # or: pnpm add -g @martini-film/cli → martini
10
- martini login # pair this device through the browser (90-day key)
10
+ martini login # pair this device through the browser (90-day key); --no-wait to return at once, rerun to collect
11
11
  martini get_board_overview --project <id> # any MCP tool, flags from its arguments
12
12
  martini get_asset --asset-id <id> --fields asset.status,asset.imageUrl
13
13
  martini upload ./ref.png ./take.mp4 --canvas <id>
@@ -19,16 +19,20 @@ martini probe <path|asset-id>… # duration, resolution, fps, c
19
19
 
20
20
  ## How commands are generated
21
21
 
22
- `martini <tool>` exists for every entry of the tool manifest (`src/generated/manifest.json`), which the backend
23
- exports from its MCP registry (`pnpm --dir backend run export:tool-manifest`) and drift-tests against the live
24
- registry. Flags are derived from each tool's JSON Schema: `assetId` becomes `--asset-id`, arrays repeat the flag
25
- (or take a JSON list), objects take JSON, and `--json '{…}'` / `--json @args.json` passes the whole arguments
26
- object — so a plan a script computed is submitted without retyping it. `martini <tool> --help` prints the tool's
27
- own description, every flag with its type and whether it is required, and one example.
22
+ `martini <tool>` exists for every entry of the tool manifest the build carries — the same list the Martini
23
+ MCP server publishes; `martini tools` says when the server has a newer one. Flags are derived from each tool's JSON Schema: `assetId` becomes `--asset-id`, arrays repeat the flag
24
+ (or take a JSON list), objects take JSON, a `string | null` argument takes the literal `null` to clear it, a
25
+ `"start" | "end" | {…}` union takes the bare word, and `--args '{…}'` / `--args @args.json` passes the whole
26
+ arguments object (`--json` is an alias, except on a tool whose own argument is named `json`) — so a plan a script
27
+ computed is submitted without retyping it. `--no-context` sends only what you typed, for tools that refuse a
28
+ `projectId` next to another selector. `martini <tool> --help` prints the tool's own description, every flag with
29
+ its type and whether it is required, the result fields when the tool declares them (as `--fields` paths), and one
30
+ example.
28
31
 
29
32
  `projectId`, `canvasId`, and `runId` are filled from context when a tool takes them and the caller did not say:
30
33
  `--project` / `--canvas` / `--run`, else `MARTINI_PROJECT_ID` / `MARTINI_CANVAS_ID` / `MARTINI_RUN_ID`, else the
31
- config file.
34
+ config file. A typed selector that replaces one of them keeps it out (`get_board_overview --project-query`,
35
+ `run_workflow --workflow-id`, which runs a saved workflow on a canvas of its own).
32
36
 
33
37
  ## Credentials and context
34
38
 
@@ -37,7 +41,10 @@ Most specific first:
37
41
  1. Flags: `--api-key`, `--api-url`, `--profile`.
38
42
  2. Environment: `MARTINI_API_KEY`, `MARTINI_API_URL`, `MARTINI_PROFILE`, `MARTINI_CONFIG_FILE`.
39
43
  3. The nearest `.martini/config.json` above the working directory — what a workflow run's sandbox has.
40
- 4. `~/.config/martini/config.json` — the profiles `martini login` saves, one per organization, with a default.
44
+ 4. `~/.config/martini/config.json` — the profiles `martini login` saves, one per organization (named after it, `test-team`), with a default.
45
+
46
+ `--project-id`, `--canvas-id`, and `--run-id` (the tools' spelling) are accepted wherever `--project`, `--canvas`,
47
+ and `--run` are.
41
48
 
42
49
  A workflow run's sandbox therefore needs nothing but its working directory; a laptop needs one `martini login`;
43
50
  a script sets `MARTINI_API_KEY`. A raw Martini API key (`mtn_live_…`) always works.
@@ -45,7 +52,10 @@ a script sets `MARTINI_API_KEY`. A raw Martini API key (`mtn_live_…`) always w
45
52
  ## Output and exit codes
46
53
 
47
54
  Results are JSON on stdout — compact when piped, indented on a terminal. `--fields a,b.c` keeps only those
48
- dotted paths, applied on the server so a large listing never crosses the wire. `--out <path>` writes the result
55
+ dotted paths, applied on the server so a large listing never crosses the wire; a `*` segment steps into every value of
56
+ an id-keyed map (`assets.*.status`) and is applied in the CLI. With `--wait` (including default
57
+ waits), the CLI keeps the submission identifiers for polling and applies `--fields` to the final result.
58
+ `--out <path>` writes the result
49
59
  (or, with `--raw`, the media bytes of tools like `view_asset`) to a file and prints one summary line. `--quiet`
50
60
  prints nothing on success. Errors are one JSON object on stderr: `{ "error": { "code", "message", "hint", "tool",
51
61
  "exitCode" } }`, with the tool's own MCP error code (`ASSET_NOT_READY`, `NODE_NOT_FOUND`, …).
@@ -63,14 +73,30 @@ prints nothing on success. Errors are one JSON object on stderr: `{ "error": { "
63
73
 
64
74
  Generation and rendering are asynchronous. `--wait` blocks until the produced assets (or the render) settle and
65
75
  adds `waited` to the result; it is on by default for single-item generation and rendering tools and off for batch
66
- tools, where you fan out and then `martini wait <ids…>` once. `--timeout <seconds>` (default 50, under the 60 s a
67
- sandbox command has) ends the wait with exit 4 and the pending ids, so the next command can resume it.
76
+ tools, where you fan out and then `martini wait <ids…>` once. `waited` has one shape whatever was waited on:
77
+ `{ kind, status, assets? | job? | run? | delivery? }` — `status` is the terminal status (for assets, `failed` when
78
+ any failed) and the settled records sit under the key that names them; `martini wait` prints the same object.
79
+ `--timeout <seconds>` (default `MARTINI_WAIT_TIMEOUT_SECONDS` when set, else 50; a malformed value is a usage error) ends the wait with exit 4: the
80
+ result the command produced is still printed on stdout, with `pending` (and `settled`) ids, and the error on
81
+ stderr names the `martini wait …` command that resumes it. Size `--timeout` to what your shell step allows — a
82
+ video generation takes minutes.
83
+
84
+ ## Approving a quote
85
+
86
+ `generate_nodes`, `upscale_assets`, and `enhance_draft` answer a quote first (`approvalNeeded: true`, `planHash`,
87
+ the estimated olives) and start nothing until called again with that `planHash` (plus `batchRequestId` for
88
+ upscales) and the approval argument. `--approve --max-olives <n>` does both in one command: it takes the quote,
89
+ refuses when the estimated total is over the cap (`PLAN_OVER_BUDGET`) or has no number (`PLAN_UNPRICED`), with
90
+ the quote on stdout and nothing started, and otherwise re-calls the tool with exactly what it asked for; the
91
+ result carries `approvedPlan`. The cap is required: an approval always states how much it is for. A tool with an
92
+ approval argument but no quote step (`create_nodes_and_generate`) is only attested by `--approve` and cannot be
93
+ capped — for a capped batch, `create_nodes` then `generate_nodes --approve --max-olives <n>`.
68
94
 
69
95
  ## Compound commands
70
96
 
71
97
  | Command | Does |
72
98
  | ------------------------------------ | ----------------------------------------------------------------------------------------------------- |
73
- | `login`, `logout`, `whoami` | device-code pairing, forget a profile, who the CLI acts as |
99
+ | `login`, `logout`, `whoami` | device-code pairing (a started pairing is resumed by running `login` again; `--no-wait` returns at once), forget a profile, who the CLI acts as |
74
100
  | `tools`, `models` | the manifest this build knows (and whether the server has a newer one); the cached model catalog |
75
101
  | `upload <paths…>` | prepare → PUT → complete from local files, then wait for processing |
76
102
  | `download <ids…>` | presigned URL → bytes → file |
@@ -89,8 +115,7 @@ executing answers `RUN_NOT_ACTIVE` (exit 4).
89
115
  ## Single-file binary
90
116
 
91
117
  `pnpm run build:binary` compiles `dist/bin.js` into `dist/martini-linux-x64` with Bun for environments without
92
- Node (the agent-environment sandbox). Bun is only needed on the build machine. Every `cli-v<version>` tag builds
93
- it in CI and publishes it at `https://downloads.martini.film/cli-releases/<version>/martini-linux-x64`
94
- (`.github/workflows/cli-release.yml`); the npm package is published by hand (`AGENTS.md` → Releasing).
118
+ Node (the agent-environment sandbox). Bun is only needed on the build machine. Every release publishes it at `https://downloads.martini.film/cli-releases/<version>/martini-linux-x64`;
119
+ the npm package is published separately.
95
120
 
96
121
  Docs: <https://www.martini.film/docs/cli>.
package/dist/api.js CHANGED
@@ -1,17 +1,10 @@
1
- /**
2
- * The wire: one HTTPS request per call against the API-key surface —
3
- * `POST /v1/tools/{name}` for every tool, `/v1/tools` for the manifest,
4
- * `/v1/me`, and the two login routes. Static key, no session. The CLI names
5
- * itself with `X-Martini-Client` and a User-Agent that also carries the host
6
- * agent it runs inside, so analytics can tell "the CLI under Claude Code" from
7
- * "Claude Code over MCP".
8
- */
9
1
  import { detectHostAgent } from './host.js';
10
- export const CLI_VERSION = '0.1.0';
2
+ export const CLI_VERSION = '0.2.0';
11
3
  export class CliApiError extends Error {
12
4
  code;
13
5
  status;
14
6
  tool;
7
+ result;
15
8
  constructor(message, details) {
16
9
  super(message);
17
10
  this.name = 'CliApiError';
@@ -35,6 +28,10 @@ export function createApiClient(config, options = {}) {
35
28
  };
36
29
  if (config.apiKey)
37
30
  base.Authorization = `Bearer ${config.apiKey}`;
31
+ if (config.workerId)
32
+ base['X-Martini-Worker'] = config.workerId;
33
+ if (config.executionId)
34
+ base['X-Martini-Execution'] = config.executionId;
38
35
  return base;
39
36
  }
40
37
  async function readError(response, tool) {
@@ -44,7 +41,11 @@ export function createApiClient(config, options = {}) {
44
41
  body = JSON.parse(text);
45
42
  }
46
43
  catch {
47
- body = { error: text };
44
+ const html = /^\s*<(!doctype|html)/i.test(text);
45
+ const title = html ? /<title>([^<]*)<\/title>/i.exec(text)?.[1]?.trim() : undefined;
46
+ body = {
47
+ error: html ? `The API did not answer (HTTP ${response.status}${title ? `: ${title}` : ''}). Try again.` : text,
48
+ };
48
49
  }
49
50
  const message = typeof body.error === 'string' && body.error ? body.error : `Request failed (${response.status})`;
50
51
  return new CliApiError(message, {
@@ -77,7 +78,6 @@ export function createApiClient(config, options = {}) {
77
78
  }
78
79
  return {
79
80
  config,
80
- /** `POST /v1/tools/{name}` → the tool's JSON result. */
81
81
  async callTool(name, args, options = {}) {
82
82
  requireKey();
83
83
  const query = new URLSearchParams();
@@ -91,7 +91,6 @@ export function createApiClient(config, options = {}) {
91
91
  ...(options.signal ? { signal: options.signal } : {}),
92
92
  });
93
93
  },
94
- /** `POST /v1/tools/{name}?format=raw` → the media bytes of a single-media result. */
95
94
  async callToolRaw(name, args) {
96
95
  requireKey();
97
96
  const response = await fetchImpl(`${config.apiUrl}/v1/tools/${encodeURIComponent(name)}?format=raw`, {
@@ -131,7 +130,6 @@ export function createApiClient(config, options = {}) {
131
130
  async pollLogin(pollToken) {
132
131
  return requestJson(`/v1/cli/login/${encodeURIComponent(pollToken)}`, { method: 'GET' });
133
132
  },
134
- /** A plain GET of an absolute URL (presigned downloads), with no Martini headers. */
135
133
  async fetchBytes(url) {
136
134
  const response = await fetchImpl(url);
137
135
  if (!response.ok) {
@@ -145,7 +143,6 @@ export function createApiClient(config, options = {}) {
145
143
  contentType: response.headers.get('content-type') ?? 'application/octet-stream',
146
144
  };
147
145
  },
148
- /** A plain PUT of bytes to a presigned URL with the exact headers the server signed. */
149
146
  async putBytes(url, bytes, putHeaders) {
150
147
  const response = await fetchImpl(url, { method: 'PUT', headers: putHeaders, body: bytes });
151
148
  if (!response.ok) {
package/dist/api.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ import { CliApiError } from './api.js';
2
+ import { ArgumentError, kebab } from './schema.js';
3
+ export function approvalSupport(tool) {
4
+ const properties = tool.inputSchema.properties ?? {};
5
+ const argument = 'batchApproval' in properties ? 'batchApproval' : 'approval' in properties ? 'approval' : null;
6
+ if (!argument)
7
+ return null;
8
+ return {
9
+ kind: 'planHash' in properties ? 'quote' : 'attest',
10
+ argument,
11
+ echoesBatchRequestId: 'batchRequestId' in properties,
12
+ };
13
+ }
14
+ export function isQuote(result) {
15
+ if (!result || typeof result !== 'object' || Array.isArray(result))
16
+ return false;
17
+ const record = result;
18
+ return record.approvalNeeded === true || record.generated === false;
19
+ }
20
+ function quotedOlives(quote) {
21
+ const total = quote.totalEstimatedOlives ?? quote.estimatedOlives;
22
+ return typeof total === 'number' && Number.isFinite(total) ? total : null;
23
+ }
24
+ function itemCount(quote, args) {
25
+ if (Array.isArray(quote?.items))
26
+ return quote.items.length;
27
+ for (const key of ['nodeIds', 'nodes'])
28
+ if (Array.isArray(args[key]))
29
+ return args[key].length;
30
+ return 1;
31
+ }
32
+ export async function runWithApproval(tool, client, args, support, options) {
33
+ const { maxOlives } = options;
34
+ if (support.kind === 'attest') {
35
+ if (maxOlives !== undefined) {
36
+ throw new ArgumentError(`${tool.name} has no quote step, so --max-olives cannot be enforced on it. For a capped batch, create the drafts (create_nodes) and run generate_nodes --approve --max-olives ${maxOlives}.`);
37
+ }
38
+ const count = itemCount(undefined, args);
39
+ const summary = `Approved by martini CLI --approve: ${count} item(s), no server quote.`;
40
+ const approval = support.argument === 'batchApproval' ? { approved: true, summary, itemCount: count } : { approved: true, summary };
41
+ return client.callTool(tool.name, { ...args, [support.argument]: approval });
42
+ }
43
+ if (maxOlives === undefined) {
44
+ throw new ArgumentError(`--approve needs --max-olives <olives> on ${tool.name}: the quote is approved only when its estimated total is at or under that cap.`);
45
+ }
46
+ const quote = await client.callTool(tool.name, args);
47
+ if (!isQuote(quote))
48
+ return quote;
49
+ const total = quotedOlives(quote);
50
+ if (total === null) {
51
+ const error = new CliApiError(`${tool.name} quoted no total (an item has no server-side estimate), so --max-olives ${maxOlives} cannot be checked; nothing was started. Approve by hand: re-run with --plan-hash and --${kebab(support.argument)}.`, { code: 'PLAN_UNPRICED', status: 0, tool: tool.name });
52
+ error.result = quote;
53
+ throw error;
54
+ }
55
+ if (total > maxOlives) {
56
+ const error = new CliApiError(`${tool.name} quoted ${total} olives, over the --max-olives ${maxOlives} cap; nothing was started.`, { code: 'PLAN_OVER_BUDGET', status: 0, tool: tool.name });
57
+ error.result = quote;
58
+ throw error;
59
+ }
60
+ const count = itemCount(quote, args);
61
+ const summary = `Approved by martini CLI --approve: ${count} item(s), ${total} olives estimated, cap ${maxOlives}.`;
62
+ const approval = support.argument === 'batchApproval'
63
+ ? { approved: true, summary, itemCount: count, estimatedTotalCost: String(total) }
64
+ : { approved: true, summary, estimatedCost: String(total) };
65
+ const batchRequestId = support.echoesBatchRequestId && typeof quote.batchRequestId === 'string' ? quote.batchRequestId : undefined;
66
+ const result = await client.callTool(tool.name, {
67
+ ...args,
68
+ planHash: quote.planHash,
69
+ ...(batchRequestId ? { batchRequestId } : {}),
70
+ [support.argument]: approval,
71
+ });
72
+ const approvedPlan = {
73
+ planHash: quote.planHash,
74
+ ...(batchRequestId ? { batchRequestId } : {}),
75
+ itemCount: count,
76
+ estimatedOlives: total,
77
+ maxOlives,
78
+ };
79
+ return result && typeof result === 'object' && !Array.isArray(result)
80
+ ? { ...result, approvedPlan }
81
+ : { result, approvedPlan };
82
+ }
83
+ //# sourceMappingURL=approve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approve.js","sourceRoot":"","sources":["../src/approve.ts"],"names":[],"mappings":"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"}
package/dist/args.js CHANGED
@@ -1,8 +1,4 @@
1
- /**
2
- * Argument parsing without a framework: `martini <command> [positionals] [--flag value | --flag=value | --bool]`.
3
- * Repeated flags collect into arrays. Everything after `--` is positional.
4
- */
5
- export function parseArgs(argv) {
1
+ export function parseArgs(argv, isBoolean = () => false) {
6
2
  const flags = new Map();
7
3
  const positionals = [];
8
4
  let command;
@@ -43,7 +39,8 @@ export function parseArgs(argv) {
43
39
  continue;
44
40
  }
45
41
  const next = argv[i + 1];
46
- if (next !== undefined && !next.startsWith('--')) {
42
+ const boolean = isBoolean(body, command);
43
+ if (next !== undefined && !next.startsWith('--') && (!boolean || /^(true|false|1|0|yes|no)$/.test(next))) {
47
44
  push(body, next);
48
45
  i += 1;
49
46
  }
@@ -59,7 +56,6 @@ export function parseArgs(argv) {
59
56
  }
60
57
  return { command, positionals, flags };
61
58
  }
62
- /** One value for a flag, or undefined; an array flag yields its last value. */
63
59
  export function flagString(flags, name) {
64
60
  const value = flags.get(name);
65
61
  if (value === undefined || value === true)
package/dist/args.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"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"}
package/dist/cli.js CHANGED
@@ -1,22 +1,17 @@
1
- /**
2
- * The dispatcher. A command is either one of the compound commands (what no
3
- * single tool does) or any tool from the manifest by its MCP name — the same
4
- * vocabulary agents already know from the MCP, so nothing has to be translated
5
- * (docs/plans/agent-cli.md, decision 18).
6
- */
7
- import { createApiClient } from './api.js';
1
+ import { CliApiError, createApiClient } from './api.js';
2
+ import { approvalSupport, runWithApproval } from './approve.js';
8
3
  import { flagBoolean, flagString, parseArgs } from './args.js';
9
4
  import { download, upload, wait } from './commands/assets.js';
10
- import { selectedFields, timeoutMs, wantsWait } from './commands/context.js';
5
+ import { assertKnownFlags, compoundBooleanFlags, CONTEXT_FLAG_ALIASES, needsLocalProjection, selectedFields, timeoutMs, wantsWait, } from './commands/context.js';
11
6
  import { files } from './commands/files.js';
12
7
  import { login, logout, whoami } from './commands/login.js';
13
8
  import { init, models, probe, tools, version } from './commands/tools.js';
14
9
  import { resolveConfig } from './config.js';
15
- import { renderHelp, renderToolHelp } from './help.js';
10
+ import { renderCompoundHelp, renderHelp, renderToolHelp } from './help.js';
16
11
  import { findTool, loadManifest } from './manifest.js';
17
- import { EXIT, printBytes, printError, printResult } from './output.js';
18
- import { ArgumentError, buildArguments } from './schema.js';
19
- import { producedAssetIds, waitForAssets, waitForDelivery } from './wait.js';
12
+ import { EXIT, printBytes, printError, printResult, selectFields, warnNoFieldsMatched, } from './output.js';
13
+ import { ArgumentError, buildArguments, flagSpecs } from './schema.js';
14
+ import { assetsOutcome, handleOutcome, producedAssetIds, waitForAssets, waitForDelivery, waitForJob, waitForRun, WaitTimeoutError, } from './wait.js';
20
15
  export const COMPOUND_COMMANDS = [
21
16
  login,
22
17
  logout,
@@ -31,7 +26,6 @@ export const COMPOUND_COMMANDS = [
31
26
  init,
32
27
  version,
33
28
  ];
34
- /** Flags the dispatcher owns; a tool never sees them as arguments. */
35
29
  export const GLOBAL_FLAG_NAMES = new Set([
36
30
  'help',
37
31
  'version',
@@ -47,8 +41,15 @@ export const GLOBAL_FLAG_NAMES = new Set([
47
41
  'api-key',
48
42
  'api-url',
49
43
  'profile',
44
+ 'context',
45
+ 'approve',
46
+ 'max-olives',
47
+ ]);
48
+ const TOOL_ONLY_GLOBAL_FLAGS = new Set(['approve', 'max-olives']);
49
+ const GLOBAL_FLAGS_FOR_COMPOUNDS = new Set([
50
+ ...[...GLOBAL_FLAG_NAMES].filter(flag => !TOOL_ONLY_GLOBAL_FLAGS.has(flag)),
51
+ ...CONTEXT_FLAG_ALIASES,
50
52
  ]);
51
- /** Single-item generation and rendering block by default; batches fan out and `martini wait` later (decision 5). */
52
53
  const WAIT_BY_DEFAULT = new Set([
53
54
  'create_node_and_generate',
54
55
  'generate',
@@ -57,7 +58,7 @@ const WAIT_BY_DEFAULT = new Set([
57
58
  'upscale_assets',
58
59
  'render_blender_take',
59
60
  ]);
60
- const RENDER_TOOLS = new Set(['render_timeline']);
61
+ const BOOLEAN_GLOBAL_FLAGS = new Set(['help', 'version', 'wait', 'quiet', 'raw', 'context', 'approve']);
61
62
  export async function main(options) {
62
63
  const env = options.env ?? process.env;
63
64
  const cwd = options.cwd ?? process.cwd();
@@ -66,36 +67,47 @@ export async function main(options) {
66
67
  ...(options.stderr ? { stderr: options.stderr } : {}),
67
68
  ...(options.isTTY !== undefined ? { isTTY: options.isTTY } : {}),
68
69
  };
69
- const parsed = parseArgs(options.argv);
70
70
  const manifest = options.manifest ?? loadManifest();
71
+ const parsed = parseArgs(options.argv, (flag, command) => {
72
+ if (BOOLEAN_GLOBAL_FLAGS.has(flag))
73
+ return true;
74
+ const compound = COMPOUND_COMMANDS.find(candidate => candidate.name === command);
75
+ if (compound)
76
+ return compoundBooleanFlags(compound).includes(flag);
77
+ const tool = command ? findTool(command, manifest) : undefined;
78
+ return tool ? flagSpecs(tool).some(spec => spec.flag === flag && spec.kind === 'boolean') : false;
79
+ });
71
80
  try {
81
+ if (flagBoolean(parsed.flags, 'version') && parsed.command === undefined)
82
+ parsed.command = 'version';
72
83
  if (parsed.command === undefined ||
73
84
  parsed.command === 'help' ||
74
85
  (flagBoolean(parsed.flags, 'help') && !parsed.command)) {
75
86
  printResult(renderHelp(manifest, COMPOUND_COMMANDS), output);
76
87
  return EXIT.ok;
77
88
  }
78
- if (parsed.command === '--version' || (flagBoolean(parsed.flags, 'version') && parsed.command === undefined)) {
79
- parsed.command = 'version';
80
- }
81
89
  const compound = COMPOUND_COMMANDS.find(command => command.name === parsed.command);
82
90
  const tool = compound ? undefined : findTool(parsed.command, manifest);
83
91
  if (!compound && !tool) {
84
92
  throw new ArgumentError(`Unknown command "${parsed.command}". Run martini --help for the list.`);
85
93
  }
86
94
  if (flagBoolean(parsed.flags, 'help')) {
87
- printResult(tool ? renderToolHelp(tool) : `${compound.usage}\n\n${compound.summary}\n`, output);
95
+ printResult(tool ? renderToolHelp(tool) : renderCompoundHelp(compound), output);
88
96
  return EXIT.ok;
89
97
  }
98
+ if (compound)
99
+ assertKnownFlags(compound, parsed.flags, GLOBAL_FLAGS_FOR_COMPOUNDS);
90
100
  const config = resolveConfig(parsed.flags, { env, cwd });
91
- const client = (options.createClient ??
92
- (resolved => createApiClient(resolved, { ...(options.fetch ? { fetch: options.fetch } : {}), env })))(config);
101
+ const clientFor = options.createClient ??
102
+ (resolved => createApiClient(resolved, { ...(options.fetch ? { fetch: options.fetch } : {}), env }));
93
103
  const out = flagString(parsed.flags, 'out');
94
104
  if (out)
95
105
  output.out = out;
106
+ output.inRun = Boolean(config.runId);
96
107
  output.quiet = flagBoolean(parsed.flags, 'quiet') ?? false;
97
108
  const context = {
98
- client,
109
+ client: clientFor(config),
110
+ clientFor,
99
111
  config,
100
112
  manifest,
101
113
  flags: parsed.flags,
@@ -106,58 +118,122 @@ export async function main(options) {
106
118
  now: options.now ?? Date.now,
107
119
  sleep: options.sleep ?? (ms => new Promise(resolve => setTimeout(resolve, ms))),
108
120
  };
121
+ const select = selectedFields(context);
122
+ timeoutMs(context);
109
123
  if (compound) {
110
124
  const result = await compound.run(context);
111
- printResult(result, output);
125
+ const projected = select.length && result && typeof result === 'object' ? selectFields(result, select) : result;
126
+ warnNoFieldsMatched(projected, select, output);
127
+ printResult(projected, output);
112
128
  return EXIT.ok;
113
129
  }
114
- printResult(await runTool(tool, context, parsed.flags), output);
130
+ const result = await runTool(tool, context, parsed.flags);
131
+ warnNoFieldsMatched(result, select, output, knownResultFields(tool));
132
+ printResult(result, output);
115
133
  return EXIT.ok;
116
134
  }
117
135
  catch (error) {
136
+ if (error instanceof CliApiError && error.result !== undefined)
137
+ printResult(error.result, output);
118
138
  return printError(error, output);
119
139
  }
120
140
  }
121
141
  async function runTool(tool, context, flags) {
142
+ const properties = tool.inputSchema.properties ?? {};
143
+ const ignore = new Set(GLOBAL_FLAG_NAMES);
144
+ for (const alias of CONTEXT_FLAG_ALIASES) {
145
+ if (!(alias.replace(/-id$/, 'Id') in properties))
146
+ ignore.add(alias);
147
+ }
148
+ const useContext = flagBoolean(flags, 'context') !== false;
122
149
  const args = buildArguments(tool, flags, {
123
- ignore: GLOBAL_FLAG_NAMES,
124
- context: {
125
- ...(context.config.projectId ? { projectId: context.config.projectId } : {}),
126
- ...(context.config.canvasId ? { canvasId: context.config.canvasId } : {}),
127
- ...(context.config.runId ? { runId: context.config.runId } : {}),
128
- },
150
+ ignore,
151
+ context: useContext
152
+ ? {
153
+ ...(context.config.projectId ? { projectId: context.config.projectId } : {}),
154
+ ...(context.config.canvasId ? { canvasId: context.config.canvasId } : {}),
155
+ ...(context.config.runId ? { runId: context.config.runId } : {}),
156
+ }
157
+ : {},
129
158
  });
130
159
  if (flagBoolean(flags, 'raw')) {
131
160
  const raw = await context.client.callToolRaw(tool.name, args);
132
161
  printBytes(raw.bytes, { ...context.output, contentType: raw.contentType });
133
- // The bytes are the result; nothing else goes to the file or stdout.
134
162
  delete context.output.out;
135
163
  context.output.quiet = true;
136
164
  return raw.meta;
137
165
  }
138
166
  const select = selectedFields(context);
139
- const result = await context.client.callTool(tool.name, args, select.length ? { select } : {});
140
- if (!wantsWait(context, WAIT_BY_DEFAULT.has(tool.name)))
141
- return result;
167
+ const shouldWait = wantsWait(context, WAIT_BY_DEFAULT.has(tool.name));
168
+ const approve = approvalFlags(tool, flags);
169
+ const local = shouldWait || approve !== null || needsLocalProjection(select);
170
+ const result = approve
171
+ ? await runWithApproval(tool, context.client, args, approve.support, approve.maxOlives === undefined ? {} : { maxOlives: approve.maxOlives })
172
+ : await context.client.callTool(tool.name, args, !local && select.length ? { select } : {});
173
+ if (!shouldWait)
174
+ return local ? selectFields(result, select) : result;
142
175
  const projectId = typeof args.projectId === 'string' ? args.projectId : context.config.projectId;
143
- if (!projectId)
144
- return result;
145
- const waitOptions = { timeoutMs: timeoutMs(context), now: context.now, sleep: context.sleep };
146
- if (RENDER_TOOLS.has(tool.name)) {
147
- const renderId = result?.renderId;
148
- if (typeof renderId === 'string') {
149
- const delivery = await waitForDelivery(context.client, projectId, { renderId }, waitOptions);
150
- return { ...result, delivery };
176
+ try {
177
+ return selectFields(await waitForToolResult(result, context, projectId), select);
178
+ }
179
+ catch (error) {
180
+ if (error instanceof CliApiError) {
181
+ const kept = selectFields(result, select);
182
+ const base = kept && typeof kept === 'object' && !Array.isArray(kept) ? kept : { result: kept };
183
+ error.result =
184
+ error instanceof WaitTimeoutError
185
+ ? { ...base, pending: error.pending, ...(error.settled.length ? { settled: error.settled } : {}) }
186
+ : { ...base, waitError: error.code };
151
187
  }
188
+ throw error;
189
+ }
190
+ }
191
+ async function waitForToolResult(result, context, projectId) {
192
+ if (!result || typeof result !== 'object')
152
193
  return result;
194
+ const record = result;
195
+ if (record.approvalNeeded === true || record.generated === false)
196
+ return result;
197
+ const waitOptions = { timeoutMs: timeoutMs(context), now: context.now, sleep: context.sleep };
198
+ if (typeof record.runId === 'string') {
199
+ return { ...record, waited: handleOutcome('run', await waitForRun(context.client, record.runId, waitOptions)) };
200
+ }
201
+ if (typeof record.renderId === 'string' || typeof record.exportId === 'string') {
202
+ const ids = {
203
+ ...(typeof record.renderId === 'string' ? { renderId: record.renderId } : {}),
204
+ ...(typeof record.exportId === 'string' ? { exportId: record.exportId } : {}),
205
+ };
206
+ return { ...record, waited: handleOutcome('render', await waitForDelivery(context.client, ids, waitOptions)) };
207
+ }
208
+ if (typeof record.jobId === 'string') {
209
+ return { ...record, waited: handleOutcome('job', await waitForJob(context.client, record.jobId, waitOptions)) };
153
210
  }
211
+ if (!projectId)
212
+ return result;
154
213
  const ids = producedAssetIds(result);
155
214
  if (ids.length === 0)
156
215
  return result;
157
- const assets = await waitForAssets(context.client, projectId, ids, waitOptions);
158
- return {
159
- ...(typeof result === 'object' && result ? result : { result }),
160
- waited: assets,
161
- };
216
+ return { ...record, waited: assetsOutcome(await waitForAssets(context.client, projectId, ids, waitOptions)) };
217
+ }
218
+ function approvalFlags(tool, flags) {
219
+ const approve = flagBoolean(flags, 'approve') === true;
220
+ const rawMax = flagString(flags, 'max-olives');
221
+ if (!approve && rawMax === undefined)
222
+ return null;
223
+ const support = approvalSupport(tool);
224
+ if (!support) {
225
+ throw new ArgumentError(`${tool.name} takes no approval; --approve and --max-olives apply to generate_nodes, upscale_assets, enhance_draft, and the tools with an approval argument (see --help).`);
226
+ }
227
+ if (!approve)
228
+ throw new ArgumentError('--max-olives only applies together with --approve.');
229
+ if (rawMax === undefined)
230
+ return { support };
231
+ const maxOlives = Number(rawMax);
232
+ if (!Number.isFinite(maxOlives) || maxOlives < 0)
233
+ throw new ArgumentError('--max-olives must be a number of olives.');
234
+ return { support, maxOlives };
235
+ }
236
+ function knownResultFields(tool) {
237
+ return Object.keys(tool.outputSchema?.properties ?? {});
162
238
  }
163
239
  //# sourceMappingURL=cli.js.map