@martini-film/cli 0.1.0 → 0.3.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 +49 -28
- package/dist/api.js +38 -14
- package/dist/api.js.map +1 -1
- package/dist/approve.js +83 -0
- package/dist/approve.js.map +1 -0
- package/dist/args.js +33 -12
- package/dist/args.js.map +1 -1
- package/dist/cli.js +202 -64
- package/dist/cli.js.map +1 -1
- package/dist/commands/assets.js +255 -46
- package/dist/commands/assets.js.map +1 -1
- package/dist/commands/context.js +42 -8
- package/dist/commands/context.js.map +1 -1
- package/dist/commands/files.js +135 -95
- package/dist/commands/files.js.map +1 -1
- package/dist/commands/login.js +171 -30
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/tools.js +73 -12
- package/dist/commands/tools.js.map +1 -1
- package/dist/config.js +10 -19
- package/dist/config.js.map +1 -1
- package/dist/generated/manifest.json +18393 -5431
- package/dist/help.js +203 -18
- package/dist/help.js.map +1 -1
- package/dist/host.js +0 -6
- package/dist/host.js.map +1 -1
- package/dist/manifest.js +9 -10
- package/dist/manifest.js.map +1 -1
- package/dist/output.js +149 -20
- package/dist/output.js.map +1 -1
- package/dist/schema.js +103 -30
- package/dist/schema.js.map +1 -1
- package/dist/wait.js +211 -29
- package/dist/wait.js.map +1 -1
- package/package.json +1 -1
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,21 @@ 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
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
object
|
|
27
|
-
|
|
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 take the values
|
|
24
|
+
after the flag (`--node-ids a b`), a repeated flag, or 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. A tool argument named like a global flag (`fields` on `get_models`) is
|
|
28
|
+
spelled `--tool-<name>`. `--no-context` sends only what you typed, for tools that refuse a
|
|
29
|
+
`projectId` next to another selector. `martini <tool> --help` prints the tool's own description, every flag with
|
|
30
|
+
its type and whether it is required, the result fields when the tool declares them (as `--fields` paths), and one
|
|
31
|
+
example.
|
|
28
32
|
|
|
29
33
|
`projectId`, `canvasId`, and `runId` are filled from context when a tool takes them and the caller did not say:
|
|
30
34
|
`--project` / `--canvas` / `--run`, else `MARTINI_PROJECT_ID` / `MARTINI_CANVAS_ID` / `MARTINI_RUN_ID`, else the
|
|
31
|
-
config file.
|
|
35
|
+
config file. A typed selector that replaces one of them keeps it out (`get_board_overview --project-query`,
|
|
36
|
+
`run_workflow --workflow-id`, which runs a saved workflow on a canvas of its own).
|
|
32
37
|
|
|
33
38
|
## Credentials and context
|
|
34
39
|
|
|
@@ -36,18 +41,26 @@ Most specific first:
|
|
|
36
41
|
|
|
37
42
|
1. Flags: `--api-key`, `--api-url`, `--profile`.
|
|
38
43
|
2. Environment: `MARTINI_API_KEY`, `MARTINI_API_URL`, `MARTINI_PROFILE`, `MARTINI_CONFIG_FILE`.
|
|
39
|
-
3. The nearest `.martini/config.json` above the working directory —
|
|
40
|
-
4. `~/.config/martini/config.json` — the profiles `martini login` saves,
|
|
44
|
+
3. The nearest `.martini/config.json` above the working directory — a Martini workflow run provides one.
|
|
45
|
+
4. `~/.config/martini/config.json` — the profiles `martini login` saves, with a default (the key `default`). Approving "as you" saves the profile `account`, a key that acts in every organization you belong to; approving for one organization saves a profile named after it (`test-team`). Tools run as you either way and the project you name decides who is billed; an organization key additionally reaches that organization's REST API and is listed in its Settings.
|
|
41
46
|
|
|
42
|
-
|
|
43
|
-
|
|
47
|
+
`--project-id`, `--canvas-id`, and `--run-id` (the tools' spelling) are accepted wherever `--project`, `--canvas`,
|
|
48
|
+
and `--run` are.
|
|
49
|
+
|
|
50
|
+
Inside a workflow run the CLI is therefore already signed in; a laptop needs one `martini login`;
|
|
51
|
+
a script sets `MARTINI_API_KEY`. A raw Martini API key (`mtn_live_…`) always works. Signing in as you
|
|
52
|
+
needs CLI 0.3.0 or newer; the approval page offers only what the CLI that asked can collect.
|
|
44
53
|
|
|
45
54
|
## Output and exit codes
|
|
46
55
|
|
|
47
56
|
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
|
|
49
|
-
|
|
50
|
-
|
|
57
|
+
dotted paths, applied on the server so a large listing never crosses the wire; a `*` segment steps into every value of
|
|
58
|
+
an id-keyed map (`assets.*.status`) and is applied in the CLI. With `--wait` (including default
|
|
59
|
+
waits), the CLI keeps the submission identifiers for polling and applies `--fields` to the final result.
|
|
60
|
+
`--out <path>` writes the result
|
|
61
|
+
(or, with `--raw`, the media bytes of tools like `view_asset`) to a file and prints one summary line. Printed to
|
|
62
|
+
stdout, a media block is a stub (`mimeType`, `bytes`, a hint) rather than base64, and `--raw` to a terminal without
|
|
63
|
+
`--out` is a usage error. `--quiet` prints nothing on success. Errors are one JSON object on stderr: `{ "error": { "code", "message", "hint", "tool",
|
|
51
64
|
"exitCode" } }`, with the tool's own MCP error code (`ASSET_NOT_READY`, `NODE_NOT_FOUND`, …).
|
|
52
65
|
|
|
53
66
|
| Exit | Meaning |
|
|
@@ -63,14 +76,30 @@ prints nothing on success. Errors are one JSON object on stderr: `{ "error": { "
|
|
|
63
76
|
|
|
64
77
|
Generation and rendering are asynchronous. `--wait` blocks until the produced assets (or the render) settle and
|
|
65
78
|
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.
|
|
67
|
-
|
|
79
|
+
tools, where you fan out and then `martini wait <ids…>` once. `waited` has one shape whatever was waited on:
|
|
80
|
+
`{ kind, status, assets? | job? | run? | delivery? }` — `status` is the terminal status (for assets, `failed` when
|
|
81
|
+
any failed) and the settled records sit under the key that names them; `martini wait` prints the same object.
|
|
82
|
+
`--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
|
|
83
|
+
result the command produced is still printed on stdout, with `pending` (and `settled`) ids, and the error on
|
|
84
|
+
stderr names the `martini wait …` command that resumes it. Size `--timeout` to what your shell step allows — a
|
|
85
|
+
video generation takes minutes.
|
|
86
|
+
|
|
87
|
+
## Approving a quote
|
|
88
|
+
|
|
89
|
+
`generate_nodes`, `upscale_assets`, and `enhance_draft` answer a quote first (`approvalNeeded: true`, `planHash`,
|
|
90
|
+
the estimated olives) and start nothing until called again with that `planHash` (plus `batchRequestId` for
|
|
91
|
+
upscales) and the approval argument. `--approve --max-olives <n>` does both in one command: it takes the quote,
|
|
92
|
+
refuses when the estimated total is over the cap (`PLAN_OVER_BUDGET`) or has no number (`PLAN_UNPRICED`), with
|
|
93
|
+
the quote on stdout and nothing started, and otherwise re-calls the tool with exactly what it asked for; the
|
|
94
|
+
result carries `approvedPlan`. The cap is required: an approval always states how much it is for. A tool with an
|
|
95
|
+
approval argument but no quote step (`create_nodes_and_generate`) is only attested by `--approve` and cannot be
|
|
96
|
+
capped — for a capped batch, `create_nodes` then `generate_nodes --approve --max-olives <n>`.
|
|
68
97
|
|
|
69
98
|
## Compound commands
|
|
70
99
|
|
|
71
100
|
| Command | Does |
|
|
72
101
|
| ------------------------------------ | ----------------------------------------------------------------------------------------------------- |
|
|
73
|
-
| `login`, `logout`, `whoami` | device-code pairing, forget a profile, who the CLI acts as
|
|
102
|
+
| `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
103
|
| `tools`, `models` | the manifest this build knows (and whether the server has a newer one); the cached model catalog |
|
|
75
104
|
| `upload <paths…>` | prepare → PUT → complete from local files, then wait for processing |
|
|
76
105
|
| `download <ids…>` | presigned URL → bytes → file |
|
|
@@ -79,18 +108,10 @@ sandbox command has) ends the wait with exit 4 and the pending ids, so the next
|
|
|
79
108
|
| `probe <path\|id>…` | ffprobe + sha256 in one JSON report |
|
|
80
109
|
| `init [--write]` | the AGENTS.md snippet that teaches a repo's agents to use the CLI |
|
|
81
110
|
|
|
82
|
-
## Inside a workflow run
|
|
83
|
-
|
|
84
|
-
The sandbox of a Martini workflow run carries a run key in `.martini/config.json`, written when the run's agent
|
|
85
|
-
environment opens and revoked when it closes. Calls made with it go through the run's own supervisor — the same
|
|
86
|
-
budgets, spend fences, and trace as the run's tool calls — and the tool set is the run's. A run that is not
|
|
87
|
-
executing answers `RUN_NOT_ACTIVE` (exit 4).
|
|
88
|
-
|
|
89
111
|
## Single-file binary
|
|
90
112
|
|
|
91
113
|
`pnpm run build:binary` compiles `dist/bin.js` into `dist/martini-linux-x64` with Bun for environments without
|
|
92
|
-
Node
|
|
93
|
-
|
|
94
|
-
(`.github/workflows/cli-release.yml`); the npm package is published by hand (`AGENTS.md` → Releasing).
|
|
114
|
+
Node. Bun is only needed on the build machine. Every release publishes it at `https://downloads.martini.film/cli-releases/<version>/martini-linux-x64`;
|
|
115
|
+
the npm package is published separately.
|
|
95
116
|
|
|
96
117
|
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.
|
|
2
|
+
export const CLI_VERSION = '0.3.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
|
-
|
|
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`, {
|
|
@@ -121,6 +120,33 @@ export function createApiClient(config, options = {}) {
|
|
|
121
120
|
requireKey();
|
|
122
121
|
return requestJson('/v1/tools', { method: 'GET' });
|
|
123
122
|
},
|
|
123
|
+
async serverManifestHash(localHash) {
|
|
124
|
+
requireKey();
|
|
125
|
+
const response = await fetchImpl(`${config.apiUrl}/v1/tools`, {
|
|
126
|
+
method: 'GET',
|
|
127
|
+
headers: headers({ 'If-None-Match': `"${localHash}"` }),
|
|
128
|
+
});
|
|
129
|
+
if (response.status === 304)
|
|
130
|
+
return localHash;
|
|
131
|
+
if (!response.ok)
|
|
132
|
+
throw await readError(response);
|
|
133
|
+
const etag = response.headers
|
|
134
|
+
.get('etag')
|
|
135
|
+
?.replace(/^W\//, '')
|
|
136
|
+
.replace(/^"(.*)"$/, '$1');
|
|
137
|
+
if (etag) {
|
|
138
|
+
await response.body?.cancel().catch(() => undefined);
|
|
139
|
+
return etag;
|
|
140
|
+
}
|
|
141
|
+
const body = (await response.json());
|
|
142
|
+
if (typeof body.hash !== 'string') {
|
|
143
|
+
throw new CliApiError('The server answered GET /v1/tools without a manifest hash.', {
|
|
144
|
+
code: 'INVALID_RESPONSE',
|
|
145
|
+
status: response.status,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
return body.hash;
|
|
149
|
+
},
|
|
124
150
|
async me() {
|
|
125
151
|
requireKey();
|
|
126
152
|
return requestJson('/v1/me', { method: 'GET' });
|
|
@@ -131,7 +157,6 @@ export function createApiClient(config, options = {}) {
|
|
|
131
157
|
async pollLogin(pollToken) {
|
|
132
158
|
return requestJson(`/v1/cli/login/${encodeURIComponent(pollToken)}`, { method: 'GET' });
|
|
133
159
|
},
|
|
134
|
-
/** A plain GET of an absolute URL (presigned downloads), with no Martini headers. */
|
|
135
160
|
async fetchBytes(url) {
|
|
136
161
|
const response = await fetchImpl(url);
|
|
137
162
|
if (!response.ok) {
|
|
@@ -145,7 +170,6 @@ export function createApiClient(config, options = {}) {
|
|
|
145
170
|
contentType: response.headers.get('content-type') ?? 'application/octet-stream',
|
|
146
171
|
};
|
|
147
172
|
},
|
|
148
|
-
/** A plain PUT of bytes to a presigned URL with the exact headers the server signed. */
|
|
149
173
|
async putBytes(url, bytes, putHeaders) {
|
|
150
174
|
const response = await fetchImpl(url, { method: 'PUT', headers: putHeaders, body: bytes });
|
|
151
175
|
if (!response.ok) {
|
package/dist/api.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|
package/dist/approve.js
ADDED
|
@@ -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,12 +1,9 @@
|
|
|
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, takesList = () => false) {
|
|
6
2
|
const flags = new Map();
|
|
7
3
|
const positionals = [];
|
|
8
4
|
let command;
|
|
9
5
|
let onlyPositionals = false;
|
|
6
|
+
const pendingNegations = [];
|
|
10
7
|
const push = (name, value) => {
|
|
11
8
|
const existing = flags.get(name);
|
|
12
9
|
if (existing === undefined) {
|
|
@@ -21,6 +18,16 @@ export function parseArgs(argv) {
|
|
|
21
18
|
}
|
|
22
19
|
flags.set(name, Array.isArray(existing) ? [...existing, value] : [existing, value]);
|
|
23
20
|
};
|
|
21
|
+
const takeListValues = (name, index) => {
|
|
22
|
+
if (command === undefined || !takesList(name, command))
|
|
23
|
+
return index;
|
|
24
|
+
let last = index;
|
|
25
|
+
while (last + 1 < argv.length && !argv[last + 1].startsWith('--')) {
|
|
26
|
+
last += 1;
|
|
27
|
+
push(name, argv[last]);
|
|
28
|
+
}
|
|
29
|
+
return last;
|
|
30
|
+
};
|
|
24
31
|
for (let i = 0; i < argv.length; i += 1) {
|
|
25
32
|
const arg = argv[i];
|
|
26
33
|
if (onlyPositionals) {
|
|
@@ -35,31 +42,45 @@ export function parseArgs(argv) {
|
|
|
35
42
|
const body = arg.slice(2);
|
|
36
43
|
const eq = body.indexOf('=');
|
|
37
44
|
if (eq >= 0) {
|
|
38
|
-
|
|
45
|
+
const name = body.slice(0, eq);
|
|
46
|
+
push(name, body.slice(eq + 1));
|
|
47
|
+
i = takeListValues(name, i);
|
|
39
48
|
continue;
|
|
40
49
|
}
|
|
41
|
-
if (body.startsWith('no-')) {
|
|
42
|
-
|
|
50
|
+
if (body.startsWith('no-') && !isBoolean(body, command)) {
|
|
51
|
+
if (command === undefined)
|
|
52
|
+
pendingNegations.push(body);
|
|
53
|
+
else
|
|
54
|
+
push(body.slice(3), 'false');
|
|
43
55
|
continue;
|
|
44
56
|
}
|
|
45
57
|
const next = argv[i + 1];
|
|
46
|
-
|
|
58
|
+
const boolean = isBoolean(body, command);
|
|
59
|
+
if (next !== undefined && !next.startsWith('--') && (!boolean || /^(true|false|1|0|yes|no)$/.test(next))) {
|
|
47
60
|
push(body, next);
|
|
48
|
-
i
|
|
61
|
+
i = takeListValues(body, i + 1);
|
|
49
62
|
}
|
|
50
63
|
else {
|
|
51
64
|
push(body, true);
|
|
52
65
|
}
|
|
53
66
|
continue;
|
|
54
67
|
}
|
|
55
|
-
if (command === undefined)
|
|
68
|
+
if (command === undefined) {
|
|
56
69
|
command = arg;
|
|
70
|
+
for (const body of pendingNegations.splice(0)) {
|
|
71
|
+
if (isBoolean(body, command))
|
|
72
|
+
push(body, true);
|
|
73
|
+
else
|
|
74
|
+
push(body.slice(3), 'false');
|
|
75
|
+
}
|
|
76
|
+
}
|
|
57
77
|
else
|
|
58
78
|
positionals.push(arg);
|
|
59
79
|
}
|
|
80
|
+
for (const body of pendingNegations)
|
|
81
|
+
push(body.slice(3), 'false');
|
|
60
82
|
return { command, positionals, flags };
|
|
61
83
|
}
|
|
62
|
-
/** One value for a flag, or undefined; an array flag yields its last value. */
|
|
63
84
|
export function flagString(flags, name) {
|
|
64
85
|
const value = flags.get(name);
|
|
65
86
|
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":"
|
|
1
|
+
{"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"AAcA,MAAM,UAAU,SAAS,CACvB,IAAuB,EACvB,YAAoE,GAAG,EAAE,CAAC,KAAK,EAC/E,YAAwD,GAAG,EAAE,CAAC,KAAK;IAEnE,MAAM,KAAK,GAAG,IAAI,GAAG,EAAqB,CAAA;IAC1C,MAAM,WAAW,GAAa,EAAE,CAAA;IAChC,IAAI,OAA2B,CAAA;IAC/B,IAAI,eAAe,GAAG,KAAK,CAAA;IAG3B,MAAM,gBAAgB,GAAa,EAAE,CAAA;IAErC,MAAM,IAAI,GAAG,CAAC,IAAY,EAAE,KAAoB,EAAE,EAAE;QAClD,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QAChC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;YACtB,OAAM;QACR,CAAC;QACD,IAAI,KAAK,KAAK,IAAI;YAAE,OAAM;QAC1B,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YACtB,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;YACtB,OAAM;QACR,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAA;IACrF,CAAC,CAAA;IAID,MAAM,cAAc,GAAG,CAAC,IAAY,EAAE,KAAa,EAAU,EAAE;QAC7D,IAAI,OAAO,KAAK,SAAS,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC;YAAE,OAAO,KAAK,CAAA;QACpE,IAAI,IAAI,GAAG,KAAK,CAAA;QAChB,OAAO,IAAI,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAE,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACnE,IAAI,IAAI,CAAC,CAAA;YACT,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAE,CAAC,CAAA;QACzB,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC,CAAA;IAED,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACxC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAE,CAAA;QACpB,IAAI,eAAe,EAAE,CAAC;YACpB,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;YACrB,SAAQ;QACV,CAAC;QACD,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;YACjB,eAAe,GAAG,IAAI,CAAA;YACtB,SAAQ;QACV,CAAC;QACD,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACzB,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;YACzB,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;YAC5B,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;gBACZ,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;gBAC9B,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAA;gBAC9B,CAAC,GAAG,cAAc,CAAC,IAAI,EAAE,CAAC,CAAC,CAAA;gBAC3B,SAAQ;YACV,CAAC;YACD,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,CAAC;gBACxD,IAAI,OAAO,KAAK,SAAS;oBAAE,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;;oBACjD,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAA;gBACjC,SAAQ;YACV,CAAC;YACD,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;YACxB,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;YACxC,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,OAAO,IAAI,2BAA2B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;gBACzG,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;gBAChB,CAAC,GAAG,cAAc,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAA;YACjC,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;YAClB,CAAC;YACD,SAAQ;QACV,CAAC;QACD,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,OAAO,GAAG,GAAG,CAAA;YACb,KAAK,MAAM,IAAI,IAAI,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC9C,IAAI,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC;oBAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;;oBACzC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAA;YACnC,CAAC;QACH,CAAC;;YAAM,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAC9B,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,gBAAgB;QAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAA;IACjE,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,CAAA;AACxC,CAAC;AAGD,MAAM,UAAU,UAAU,CAAC,KAA6B,EAAE,IAAY;IACpE,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IAC7B,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,SAAS,CAAA;IAC3D,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;AAC/D,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAA6B,EAAE,IAAY;IACrE,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IAC7B,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAA;IACzC,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,IAAI,CAAA;IAC/B,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;IACnE,OAAO,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,GAAG,IAAI,IAAI,KAAK,IAAI,CAAA;AAC1D,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,KAA6B,EAAE,IAAY;IAClE,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IAC7B,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,EAAE,CAAA;IACpD,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;AAC/C,CAAC"}
|