@martini-film/cli 0.1.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/dist/wait.js ADDED
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Blocking on asynchronous work so the model spends one step, not a polling
3
+ * loop (docs/plans/agent-cli.md, decision 5). Assets and jobs poll with a
4
+ * jittered backoff; runs use get_run's server-side waitSeconds. On timeout the
5
+ * caller gets the ids still pending, so `martini wait` can resume.
6
+ */
7
+ import { CliApiError } from './api.js';
8
+ export const DEFAULT_WAIT_TIMEOUT_MS = 50_000;
9
+ const TERMINAL_ASSET_STATUSES = new Set(['completed', 'failed', 'cancelled']);
10
+ const TERMINAL_RUN_STATUSES = new Set(['completed', 'failed', 'cancelled', 'needs_revision']);
11
+ export class WaitTimeoutError extends CliApiError {
12
+ pending;
13
+ constructor(kind, pending) {
14
+ super(`Timed out waiting for ${pending.length} ${kind}${pending.length === 1 ? '' : 's'}: ${pending.join(', ')}`, {
15
+ code: 'TIMEOUT',
16
+ status: 0,
17
+ });
18
+ this.pending = pending;
19
+ }
20
+ }
21
+ function backoff(attempt) {
22
+ const base = Math.min(1000 * 1.5 ** attempt, 8000);
23
+ return Math.round(base * (0.8 + Math.random() * 0.4));
24
+ }
25
+ const defaultSleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
26
+ /** Poll get_asset for each id until every one is terminal. Returns the final asset records. */
27
+ export async function waitForAssets(client, projectId, assetIds, options = {}) {
28
+ const timeoutMs = options.timeoutMs ?? DEFAULT_WAIT_TIMEOUT_MS;
29
+ const now = options.now ?? Date.now;
30
+ const sleep = options.sleep ?? defaultSleep;
31
+ const started = now();
32
+ const settled = new Map();
33
+ let pending = [...new Set(assetIds)];
34
+ let attempt = 0;
35
+ for (;;) {
36
+ for (const assetId of pending) {
37
+ const result = await client.callTool('get_asset', { projectId, assetId }, { select: ['asset.id', 'asset.status', 'asset.imageUrl', 'asset.uri', 'asset.error'] });
38
+ const asset = result.asset;
39
+ if (asset?.status && TERMINAL_ASSET_STATUSES.has(asset.status)) {
40
+ settled.set(assetId, { ...asset, id: asset.id ?? assetId, status: asset.status });
41
+ }
42
+ }
43
+ pending = pending.filter(id => !settled.has(id));
44
+ if (pending.length === 0)
45
+ return assetIds.map(id => settled.get(id));
46
+ if (now() - started >= timeoutMs)
47
+ throw new WaitTimeoutError('asset', pending);
48
+ await sleep(backoff(attempt));
49
+ attempt += 1;
50
+ }
51
+ }
52
+ /** Poll get_jobs for one job until it is terminal. */
53
+ export async function waitForJob(client, jobId, options = {}) {
54
+ const timeoutMs = options.timeoutMs ?? DEFAULT_WAIT_TIMEOUT_MS;
55
+ const now = options.now ?? Date.now;
56
+ const sleep = options.sleep ?? defaultSleep;
57
+ const started = now();
58
+ let attempt = 0;
59
+ for (;;) {
60
+ const result = await client.callTool('get_jobs', { jobId });
61
+ const status = statusOf(result);
62
+ if (status && TERMINAL_ASSET_STATUSES.has(status))
63
+ return result;
64
+ if (now() - started >= timeoutMs)
65
+ throw new WaitTimeoutError('job', [jobId]);
66
+ await sleep(backoff(attempt));
67
+ attempt += 1;
68
+ }
69
+ }
70
+ /** get_run long-polls on the server (waitSeconds); loop until terminal or the client timeout. */
71
+ export async function waitForRun(client, runId, options = {}) {
72
+ const timeoutMs = options.timeoutMs ?? DEFAULT_WAIT_TIMEOUT_MS;
73
+ const now = options.now ?? Date.now;
74
+ const started = now();
75
+ for (;;) {
76
+ const remaining = timeoutMs - (now() - started);
77
+ if (remaining <= 0)
78
+ throw new WaitTimeoutError('run', [runId]);
79
+ const waitSeconds = Math.max(1, Math.min(30, Math.floor(remaining / 1000)));
80
+ const result = await client.callTool('get_run', { runId, view: 'status', waitSeconds });
81
+ const status = statusOf(result);
82
+ if (status && TERMINAL_RUN_STATUSES.has(status))
83
+ return result;
84
+ if (options.sleep && now() - started < timeoutMs)
85
+ await options.sleep(0);
86
+ }
87
+ }
88
+ /** Poll get_timeline_delivery_status until the render or export settles. */
89
+ export async function waitForDelivery(client, projectId, ids, options = {}) {
90
+ const timeoutMs = options.timeoutMs ?? DEFAULT_WAIT_TIMEOUT_MS;
91
+ const now = options.now ?? Date.now;
92
+ const sleep = options.sleep ?? defaultSleep;
93
+ const started = now();
94
+ let attempt = 0;
95
+ const label = ids.renderId ?? ids.exportId ?? 'delivery';
96
+ for (;;) {
97
+ const result = await client.callTool('get_timeline_delivery_status', { projectId, ...ids });
98
+ const status = statusOf(result);
99
+ if (status && TERMINAL_ASSET_STATUSES.has(status))
100
+ return result;
101
+ if (now() - started >= timeoutMs)
102
+ throw new WaitTimeoutError('render', [label]);
103
+ await sleep(backoff(attempt));
104
+ attempt += 1;
105
+ }
106
+ }
107
+ /** The status of a tool result, wherever the tool put it: top level, or under job / run / render / asset. */
108
+ export function statusOf(result) {
109
+ if (!result)
110
+ return null;
111
+ if (typeof result.status === 'string')
112
+ return result.status;
113
+ for (const key of ['job', 'run', 'render', 'export', 'asset', 'delivery']) {
114
+ const nested = result[key];
115
+ if (nested && typeof nested === 'object' && typeof nested.status === 'string') {
116
+ return nested.status;
117
+ }
118
+ }
119
+ return null;
120
+ }
121
+ /** Asset ids a generation tool's result names, so --wait knows what to wait for. */
122
+ export function producedAssetIds(result) {
123
+ if (!result || typeof result !== 'object')
124
+ return [];
125
+ const record = result;
126
+ const ids = [];
127
+ const pushId = (value) => {
128
+ if (typeof value === 'string' && value.length > 0)
129
+ ids.push(value);
130
+ };
131
+ pushId(record.assetId);
132
+ pushId(record.nodeId);
133
+ const asset = record.asset;
134
+ if (asset)
135
+ pushId(asset.id);
136
+ for (const key of ['nodes', 'results', 'created', 'assets', 'items']) {
137
+ const list = record[key];
138
+ if (!Array.isArray(list))
139
+ continue;
140
+ for (const item of list) {
141
+ if (!item || typeof item !== 'object')
142
+ continue;
143
+ const entry = item;
144
+ if (entry.ok === false)
145
+ continue;
146
+ pushId(entry.assetId ?? entry.nodeId ?? entry.id);
147
+ }
148
+ }
149
+ return [...new Set(ids)];
150
+ }
151
+ //# sourceMappingURL=wait.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wait.js","sourceRoot":"","sources":["../src/wait.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@martini-film/cli",
3
+ "version": "0.1.0",
4
+ "description": "The Martini command-line interface for agents and scripts: every MCP tool as a command, plus upload, download, wait, files, probe, and login",
5
+ "license": "MIT",
6
+ "author": {
7
+ "name": "Martini",
8
+ "email": "support@martini.film",
9
+ "url": "https://www.martini.film"
10
+ },
11
+ "homepage": "https://www.martini.film/docs/cli",
12
+ "bugs": "https://www.martini.film/support",
13
+ "type": "module",
14
+ "bin": {
15
+ "martini": "./dist/bin.js"
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "README.md"
20
+ ],
21
+ "engines": {
22
+ "node": ">=20.10"
23
+ },
24
+ "scripts": {
25
+ "clean": "node --input-type=module -e \"import { rmSync } from 'node:fs'; rmSync('dist', { recursive: true, force: true })\"",
26
+ "build": "pnpm run clean && tsc -p tsconfig.json",
27
+ "test": "pnpm run build && node --import tsx --test src/*.test.ts src/**/*.test.ts",
28
+ "build:binary": "./scripts/build-binary.sh",
29
+ "prepublishOnly": "pnpm run test"
30
+ },
31
+ "devDependencies": {
32
+ "@types/node": "20.19.43",
33
+ "tsx": "4.23.1",
34
+ "typescript": "5.9.3"
35
+ }
36
+ }