@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/README.md +96 -0
- package/dist/api.js +160 -0
- package/dist/api.js.map +1 -0
- package/dist/args.js +84 -0
- package/dist/args.js.map +1 -0
- package/dist/bin.js +5 -0
- package/dist/bin.js.map +1 -0
- package/dist/cli.js +163 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/assets.js +163 -0
- package/dist/commands/assets.js.map +1 -0
- package/dist/commands/context.js +35 -0
- package/dist/commands/context.js.map +1 -0
- package/dist/commands/files.js +152 -0
- package/dist/commands/files.js.map +1 -0
- package/dist/commands/login.js +109 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/tools.js +188 -0
- package/dist/commands/tools.js.map +1 -0
- package/dist/config.js +127 -0
- package/dist/config.js.map +1 -0
- package/dist/generated/manifest.json +11100 -0
- package/dist/help.js +127 -0
- package/dist/help.js.map +1 -0
- package/dist/host.js +30 -0
- package/dist/host.js.map +1 -0
- package/dist/manifest.js +48 -0
- package/dist/manifest.js.map +1 -0
- package/dist/output.js +128 -0
- package/dist/output.js.map +1 -0
- package/dist/schema.js +169 -0
- package/dist/schema.js.map +1 -0
- package/dist/wait.js +151 -0
- package/dist/wait.js.map +1 -0
- package/package.json +36 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { basename, extname, join } from 'node:path';
|
|
3
|
+
import { CliApiError } from '../api.js';
|
|
4
|
+
import { flagString } from '../args.js';
|
|
5
|
+
import { ArgumentError } from '../schema.js';
|
|
6
|
+
import { waitForAssets, waitForDelivery, waitForJob, waitForRun } from '../wait.js';
|
|
7
|
+
import { requireCanvas, requireProject, timeoutMs, wantsWait, } from './context.js';
|
|
8
|
+
const CONTENT_TYPES = {
|
|
9
|
+
'.png': 'image/png',
|
|
10
|
+
'.jpg': 'image/jpeg',
|
|
11
|
+
'.jpeg': 'image/jpeg',
|
|
12
|
+
'.webp': 'image/webp',
|
|
13
|
+
'.mp4': 'video/mp4',
|
|
14
|
+
'.mov': 'video/quicktime',
|
|
15
|
+
'.webm': 'video/webm',
|
|
16
|
+
'.mp3': 'audio/mpeg',
|
|
17
|
+
'.wav': 'audio/wav',
|
|
18
|
+
'.m4a': 'audio/mp4',
|
|
19
|
+
};
|
|
20
|
+
export function contentTypeFor(path, override) {
|
|
21
|
+
if (override)
|
|
22
|
+
return override;
|
|
23
|
+
const type = CONTENT_TYPES[extname(path).toLowerCase()];
|
|
24
|
+
if (!type)
|
|
25
|
+
throw new ArgumentError(`Cannot infer a content type for ${path}; pass --content-type.`);
|
|
26
|
+
return type;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* `martini upload <paths…>`: prepare → PUT → complete, in one command, from
|
|
30
|
+
* local files. Replaces the three-step dance (and the base64 fallback) that
|
|
31
|
+
* agents got wrong most often.
|
|
32
|
+
*/
|
|
33
|
+
export const upload = {
|
|
34
|
+
name: 'upload',
|
|
35
|
+
summary: 'Upload local files as canvas assets (prepare → PUT → complete in one step)',
|
|
36
|
+
usage: 'martini upload <path…> [--canvas <id>] [--bin <id>] [--content-type <mime>] [--wait]',
|
|
37
|
+
async run(context) {
|
|
38
|
+
const paths = context.positionals;
|
|
39
|
+
if (paths.length === 0)
|
|
40
|
+
throw new ArgumentError('Give at least one file path to upload.');
|
|
41
|
+
if (paths.length > 50)
|
|
42
|
+
throw new ArgumentError('At most 50 files per upload.');
|
|
43
|
+
const projectId = requireProject(context);
|
|
44
|
+
const canvasId = requireCanvas(context);
|
|
45
|
+
const binId = flagString(context.flags, 'bin');
|
|
46
|
+
const contentTypeOverride = flagString(context.flags, 'content-type');
|
|
47
|
+
const files = paths.map(path => {
|
|
48
|
+
const size = statSync(path).size;
|
|
49
|
+
return { path, filename: basename(path), contentType: contentTypeFor(path, contentTypeOverride), sizeBytes: size };
|
|
50
|
+
});
|
|
51
|
+
const prepared = await context.client.callTool('upload_assets_prepare', {
|
|
52
|
+
projectId,
|
|
53
|
+
canvasId,
|
|
54
|
+
...(binId ? { binId } : {}),
|
|
55
|
+
files: files.map(({ filename, contentType, sizeBytes }) => ({ filename, contentType, sizeBytes })),
|
|
56
|
+
});
|
|
57
|
+
const uploaded = [];
|
|
58
|
+
const failed = [];
|
|
59
|
+
for (const result of prepared.results) {
|
|
60
|
+
const file = files[result.index];
|
|
61
|
+
if (!result.ok) {
|
|
62
|
+
failed.push({
|
|
63
|
+
filename: result.filename ?? file?.filename ?? String(result.index),
|
|
64
|
+
code: result.code,
|
|
65
|
+
error: result.error,
|
|
66
|
+
});
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (!file)
|
|
70
|
+
continue;
|
|
71
|
+
if (!result.uploadUrl) {
|
|
72
|
+
failed.push({
|
|
73
|
+
filename: file.filename,
|
|
74
|
+
code: 'NO_DIRECT_UPLOAD',
|
|
75
|
+
error: 'Storage did not offer a direct upload URL.',
|
|
76
|
+
});
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
await context.client.putBytes(result.uploadUrl, readFileSync(file.path), result.uploadHeaders);
|
|
80
|
+
uploaded.push(result.assetId);
|
|
81
|
+
}
|
|
82
|
+
let completed = null;
|
|
83
|
+
if (uploaded.length > 0) {
|
|
84
|
+
completed = await context.client.callTool('upload_assets_complete', { projectId, assetIds: uploaded });
|
|
85
|
+
}
|
|
86
|
+
let assets = null;
|
|
87
|
+
if (uploaded.length > 0 && wantsWait(context, true)) {
|
|
88
|
+
assets = await waitForAssets(context.client, projectId, uploaded, {
|
|
89
|
+
timeoutMs: timeoutMs(context),
|
|
90
|
+
now: context.now,
|
|
91
|
+
sleep: context.sleep,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
return { projectId, canvasId, uploaded, failed, completed, ...(assets ? { assets } : {}) };
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
/** `martini download <asset-id…>`: presigned URL → bytes → file. */
|
|
98
|
+
export const download = {
|
|
99
|
+
name: 'download',
|
|
100
|
+
summary: 'Download completed assets to files (one --out for a single asset, else into --dir)',
|
|
101
|
+
usage: 'martini download <asset-id…> [--out <path> | --dir <directory>] [--wait]',
|
|
102
|
+
async run(context) {
|
|
103
|
+
const ids = context.positionals;
|
|
104
|
+
if (ids.length === 0)
|
|
105
|
+
throw new ArgumentError('Give at least one asset id to download.');
|
|
106
|
+
const projectId = requireProject(context);
|
|
107
|
+
const out = flagString(context.flags, 'out');
|
|
108
|
+
const dir = flagString(context.flags, 'dir') ?? '.';
|
|
109
|
+
if (out && ids.length > 1)
|
|
110
|
+
throw new ArgumentError('--out names one file; use --dir for several assets.');
|
|
111
|
+
if (wantsWait(context, true)) {
|
|
112
|
+
await waitForAssets(context.client, projectId, ids, {
|
|
113
|
+
timeoutMs: timeoutMs(context),
|
|
114
|
+
now: context.now,
|
|
115
|
+
sleep: context.sleep,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
const written = [];
|
|
119
|
+
for (const assetId of ids) {
|
|
120
|
+
const link = await context.client.callTool('get_asset_download_url', {
|
|
121
|
+
projectId,
|
|
122
|
+
assetId,
|
|
123
|
+
});
|
|
124
|
+
const { bytes, contentType } = await context.client.fetchBytes(link.downloadUrl);
|
|
125
|
+
const path = out ?? join(dir, link.suggestedFilename ?? assetId);
|
|
126
|
+
if (!out)
|
|
127
|
+
mkdirSync(dir, { recursive: true });
|
|
128
|
+
writeFileSync(path, bytes);
|
|
129
|
+
written.push({ assetId, path, bytes: bytes.byteLength, contentType });
|
|
130
|
+
}
|
|
131
|
+
// The bytes went to disk already; the JSON result is the receipt.
|
|
132
|
+
delete context.output.out;
|
|
133
|
+
return { written };
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
/** `martini wait <id…>`: block until assets (default), a job, a run, or a render settles. */
|
|
137
|
+
export const wait = {
|
|
138
|
+
name: 'wait',
|
|
139
|
+
summary: 'Block until assets, a job, a run, or a render settle (exit 4 with the pending ids on timeout)',
|
|
140
|
+
usage: 'martini wait <asset-id…> | --job <id> | --run <id> | --render <id> [--timeout <seconds>]',
|
|
141
|
+
async run(context) {
|
|
142
|
+
const options = { timeoutMs: timeoutMs(context), now: context.now, sleep: context.sleep };
|
|
143
|
+
const job = flagString(context.flags, 'job');
|
|
144
|
+
const run = flagString(context.flags, 'run') ?? (context.positionals.length === 0 ? context.config.runId : undefined);
|
|
145
|
+
const render = flagString(context.flags, 'render');
|
|
146
|
+
if (job)
|
|
147
|
+
return waitForJob(context.client, job, options);
|
|
148
|
+
if (render)
|
|
149
|
+
return waitForDelivery(context.client, requireProject(context), { renderId: render }, options);
|
|
150
|
+
if (context.positionals.length > 0) {
|
|
151
|
+
const assets = await waitForAssets(context.client, requireProject(context), context.positionals, options);
|
|
152
|
+
return { assets };
|
|
153
|
+
}
|
|
154
|
+
if (run)
|
|
155
|
+
return waitForRun(context.client, run, options);
|
|
156
|
+
throw new ArgumentError('Say what to wait for: asset ids, --job <id>, --run <id>, or --render <id>.');
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
export function assertWaitable(value, what) {
|
|
160
|
+
if (typeof value !== 'string' || !value)
|
|
161
|
+
throw new CliApiError(`No ${what} to wait for in the result.`, { code: 'NOTHING_TO_WAIT_FOR', status: 0 });
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=assets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assets.js","sourceRoot":"","sources":["../../src/commands/assets.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAC1E,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAEnD,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AACnF,OAAO,EACL,aAAa,EACb,cAAc,EACd,SAAS,EACT,SAAS,GAGV,MAAM,cAAc,CAAA;AAErB,MAAM,aAAa,GAA2B;IAC5C,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,YAAY;IACpB,OAAO,EAAE,YAAY;IACrB,OAAO,EAAE,YAAY;IACrB,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,iBAAiB;IACzB,OAAO,EAAE,YAAY;IACrB,MAAM,EAAE,YAAY;IACpB,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,WAAW;CACpB,CAAA;AAED,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,QAAiB;IAC5D,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAA;IAC7B,MAAM,IAAI,GAAG,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC,CAAA;IACvD,IAAI,CAAC,IAAI;QAAE,MAAM,IAAI,aAAa,CAAC,mCAAmC,IAAI,wBAAwB,CAAC,CAAA;IACnG,OAAO,IAAI,CAAA;AACb,CAAC;AAiBD;;;;GAIG;AACH,MAAM,CAAC,MAAM,MAAM,GAAoB;IACrC,IAAI,EAAE,QAAQ;IACd,OAAO,EAAE,4EAA4E;IACrF,KAAK,EAAE,sFAAsF;IAC7F,KAAK,CAAC,GAAG,CAAC,OAAuB;QAC/B,MAAM,KAAK,GAAG,OAAO,CAAC,WAAW,CAAA;QACjC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM,IAAI,aAAa,CAAC,wCAAwC,CAAC,CAAA;QACzF,IAAI,KAAK,CAAC,MAAM,GAAG,EAAE;YAAE,MAAM,IAAI,aAAa,CAAC,8BAA8B,CAAC,CAAA;QAC9E,MAAM,SAAS,GAAG,cAAc,CAAC,OAAO,CAAC,CAAA;QACzC,MAAM,QAAQ,GAAG,aAAa,CAAC,OAAO,CAAC,CAAA;QACvC,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;QAC9C,MAAM,mBAAmB,GAAG,UAAU,CAAC,OAAO,CAAC,KAAK,EAAE,cAAc,CAAC,CAAA;QAErE,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;YAC7B,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CAAA;YAChC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,WAAW,EAAE,cAAc,CAAC,IAAI,EAAE,mBAAmB,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAA;QACpH,CAAC,CAAC,CAAA;QACF,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAgB,uBAAuB,EAAE;YACrF,SAAS;YACT,QAAQ;YACR,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3B,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC,CAAC;SACnG,CAAC,CAAA;QAEF,MAAM,QAAQ,GAAa,EAAE,CAAA;QAC7B,MAAM,MAAM,GAAuF,EAAE,CAAA;QACrG,KAAK,MAAM,MAAM,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;YACtC,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YAChC,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;gBACf,MAAM,CAAC,IAAI,CAAC;oBACV,QAAQ,EAAE,MAAM,CAAC,QAAQ,IAAI,IAAI,EAAE,QAAQ,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC;oBACnE,IAAI,EAAE,MAAM,CAAC,IAAI;oBACjB,KAAK,EAAE,MAAM,CAAC,KAAK;iBACpB,CAAC,CAAA;gBACF,SAAQ;YACV,CAAC;YACD,IAAI,CAAC,IAAI;gBAAE,SAAQ;YACnB,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;gBACtB,MAAM,CAAC,IAAI,CAAC;oBACV,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,IAAI,EAAE,kBAAkB;oBACxB,KAAK,EAAE,4CAA4C;iBACpD,CAAC,CAAA;gBACF,SAAQ;YACV,CAAC;YACD,MAAM,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,aAAa,CAAC,CAAA;YAC9F,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;QAC/B,CAAC;QAED,IAAI,SAAS,GAAY,IAAI,CAAA;QAC7B,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxB,SAAS,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,wBAAwB,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAA;QACxG,CAAC;QACD,IAAI,MAAM,GAAY,IAAI,CAAA;QAC1B,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC;YACpD,MAAM,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE;gBAChE,SAAS,EAAE,SAAS,CAAC,OAAO,CAAC;gBAC7B,GAAG,EAAE,OAAO,CAAC,GAAG;gBAChB,KAAK,EAAE,OAAO,CAAC,KAAK;aACrB,CAAC,CAAA;QACJ,CAAC;QACD,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAA;IAC5F,CAAC;CACF,CAAA;AAED,oEAAoE;AACpE,MAAM,CAAC,MAAM,QAAQ,GAAoB;IACvC,IAAI,EAAE,UAAU;IAChB,OAAO,EAAE,oFAAoF;IAC7F,KAAK,EAAE,0EAA0E;IACjF,KAAK,CAAC,GAAG,CAAC,OAAuB;QAC/B,MAAM,GAAG,GAAG,OAAO,CAAC,WAAW,CAAA;QAC/B,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM,IAAI,aAAa,CAAC,yCAAyC,CAAC,CAAA;QACxF,MAAM,SAAS,GAAG,cAAc,CAAC,OAAO,CAAC,CAAA;QACzC,MAAM,GAAG,GAAG,UAAU,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;QAC5C,MAAM,GAAG,GAAG,UAAU,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,GAAG,CAAA;QACnD,IAAI,GAAG,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC;YAAE,MAAM,IAAI,aAAa,CAAC,qDAAqD,CAAC,CAAA;QACzG,IAAI,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC;YAC7B,MAAM,aAAa,CAAC,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE;gBAClD,SAAS,EAAE,SAAS,CAAC,OAAO,CAAC;gBAC7B,GAAG,EAAE,OAAO,CAAC,GAAG;gBAChB,KAAK,EAAE,OAAO,CAAC,KAAK;aACrB,CAAC,CAAA;QACJ,CAAC;QACD,MAAM,OAAO,GAAiF,EAAE,CAAA;QAChG,KAAK,MAAM,OAAO,IAAI,GAAG,EAAE,CAAC;YAC1B,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,QAAQ,CACxC,wBAAwB,EACxB;gBACE,SAAS;gBACT,OAAO;aACR,CACF,CAAA;YACD,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;YAChF,MAAM,IAAI,GAAG,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,iBAAiB,IAAI,OAAO,CAAC,CAAA;YAChE,IAAI,CAAC,GAAG;gBAAE,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;YAC7C,aAAa,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;YAC1B,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,UAAU,EAAE,WAAW,EAAE,CAAC,CAAA;QACvE,CAAC;QACD,kEAAkE;QAClE,OAAO,OAAO,CAAC,MAAM,CAAC,GAAG,CAAA;QACzB,OAAO,EAAE,OAAO,EAAE,CAAA;IACpB,CAAC;CACF,CAAA;AAED,6FAA6F;AAC7F,MAAM,CAAC,MAAM,IAAI,GAAoB;IACnC,IAAI,EAAE,MAAM;IACZ,OAAO,EAAE,+FAA+F;IACxG,KAAK,EAAE,0FAA0F;IACjG,KAAK,CAAC,GAAG,CAAC,OAAuB;QAC/B,MAAM,OAAO,GAAG,EAAE,SAAS,EAAE,SAAS,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAA;QACzF,MAAM,GAAG,GAAG,UAAU,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;QAC5C,MAAM,GAAG,GACP,UAAU,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;QAC3G,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAA;QAClD,IAAI,GAAG;YAAE,OAAO,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,CAAA;QACxD,IAAI,MAAM;YAAE,OAAO,eAAe,CAAC,OAAO,CAAC,MAAM,EAAE,cAAc,CAAC,OAAO,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,CAAA;QAC1G,IAAI,OAAO,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACnC,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,MAAM,EAAE,cAAc,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,WAAW,EAAE,OAAO,CAAC,CAAA;YACzG,OAAO,EAAE,MAAM,EAAE,CAAA;QACnB,CAAC;QACD,IAAI,GAAG;YAAE,OAAO,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,CAAA;QACxD,MAAM,IAAI,aAAa,CAAC,4EAA4E,CAAC,CAAA;IACvG,CAAC;CACF,CAAA;AAED,MAAM,UAAU,cAAc,CAAC,KAAc,EAAE,IAAY;IACzD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK;QACrC,MAAM,IAAI,WAAW,CAAC,MAAM,IAAI,6BAA6B,EAAE,EAAE,IAAI,EAAE,qBAAqB,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAA;AAC9G,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { flagBoolean, flagList, flagString } from '../args.js';
|
|
2
|
+
import { ArgumentError } from '../schema.js';
|
|
3
|
+
import { DEFAULT_WAIT_TIMEOUT_MS } from '../wait.js';
|
|
4
|
+
export function requireProject(context) {
|
|
5
|
+
const projectId = flagString(context.flags, 'project') ?? context.config.projectId;
|
|
6
|
+
if (!projectId) {
|
|
7
|
+
throw new ArgumentError('A project is needed: pass --project <id>, or set MARTINI_PROJECT_ID (a run sandbox has it in .martini/config.json).');
|
|
8
|
+
}
|
|
9
|
+
return projectId;
|
|
10
|
+
}
|
|
11
|
+
export function requireCanvas(context) {
|
|
12
|
+
const canvasId = flagString(context.flags, 'canvas') ?? context.config.canvasId;
|
|
13
|
+
if (!canvasId)
|
|
14
|
+
throw new ArgumentError('A canvas is needed: pass --canvas <id>, or set MARTINI_CANVAS_ID.');
|
|
15
|
+
return canvasId;
|
|
16
|
+
}
|
|
17
|
+
export function timeoutMs(context) {
|
|
18
|
+
const raw = flagString(context.flags, 'timeout');
|
|
19
|
+
if (raw === undefined)
|
|
20
|
+
return DEFAULT_WAIT_TIMEOUT_MS;
|
|
21
|
+
const seconds = Number(raw);
|
|
22
|
+
if (!Number.isFinite(seconds) || seconds <= 0)
|
|
23
|
+
throw new ArgumentError('--timeout must be a positive number of seconds.');
|
|
24
|
+
return Math.round(seconds * 1000);
|
|
25
|
+
}
|
|
26
|
+
export function wantsWait(context, defaultOn) {
|
|
27
|
+
return flagBoolean(context.flags, 'wait') ?? defaultOn;
|
|
28
|
+
}
|
|
29
|
+
export function selectedFields(context) {
|
|
30
|
+
return flagList(context.flags, 'fields')
|
|
31
|
+
.flatMap(value => value.split(','))
|
|
32
|
+
.map(value => value.trim())
|
|
33
|
+
.filter(Boolean);
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/commands/context.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAI9D,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAA;AAwBpD,MAAM,UAAU,cAAc,CAAC,OAAuB;IACpD,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,CAAC,KAAK,EAAE,SAAS,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,SAAS,CAAA;IAClF,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,aAAa,CACrB,qHAAqH,CACtH,CAAA;IACH,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,OAAuB;IACnD,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAA;IAC/E,IAAI,CAAC,QAAQ;QAAE,MAAM,IAAI,aAAa,CAAC,mEAAmE,CAAC,CAAA;IAC3G,OAAO,QAAQ,CAAA;AACjB,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,OAAuB;IAC/C,MAAM,GAAG,GAAG,UAAU,CAAC,OAAO,CAAC,KAAK,EAAE,SAAS,CAAC,CAAA;IAChD,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,uBAAuB,CAAA;IACrD,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,CAAA;IAC3B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,OAAO,IAAI,CAAC;QAC3C,MAAM,IAAI,aAAa,CAAC,iDAAiD,CAAC,CAAA;IAC5E,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,CAAA;AACnC,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,OAAuB,EAAE,SAAkB;IACnE,OAAO,WAAW,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,SAAS,CAAA;AACxD,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,OAAuB;IACpD,OAAO,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC;SACrC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;SAClC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;SAC1B,MAAM,CAAC,OAAO,CAAC,CAAA;AACpB,CAAC"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `martini files`: the project's memory files as a directory.
|
|
3
|
+
*
|
|
4
|
+
* ls list_project_files
|
|
5
|
+
* cat <path> read_project_file (content to stdout)
|
|
6
|
+
* write <path> [@file|-] write_project_file (replace; --append; --create-only)
|
|
7
|
+
* pull <dir> every file to disk plus .martini/files.json (path → updatedAt)
|
|
8
|
+
* push <dir> changed files back, each guarded by expectedUpdatedAt from the
|
|
9
|
+
* manifest: a file edited server-side since the pull is refused
|
|
10
|
+
* (PROJECT_FILE_CHANGED) unless --force. Never deletes.
|
|
11
|
+
*
|
|
12
|
+
* project.md is human-owned and Instructions files are read-only during a run;
|
|
13
|
+
* the server refuses them and push reports them as skipped.
|
|
14
|
+
*/
|
|
15
|
+
import { createHash } from 'node:crypto';
|
|
16
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from 'node:fs';
|
|
17
|
+
import { dirname, join, relative, resolve, sep } from 'node:path';
|
|
18
|
+
import { CliApiError } from '../api.js';
|
|
19
|
+
import { flagBoolean } from '../args.js';
|
|
20
|
+
import { ArgumentError } from '../schema.js';
|
|
21
|
+
import { requireProject } from './context.js';
|
|
22
|
+
export const FILES_MANIFEST = join('.martini', 'files.json');
|
|
23
|
+
function sha256(content) {
|
|
24
|
+
return createHash('sha256').update(content).digest('hex');
|
|
25
|
+
}
|
|
26
|
+
function readContentArgument(raw) {
|
|
27
|
+
if (raw === undefined)
|
|
28
|
+
throw new ArgumentError('Give the content: a literal, @file, or - for stdin.');
|
|
29
|
+
if (raw === '-')
|
|
30
|
+
return readFileSync(0, 'utf8');
|
|
31
|
+
if (raw.startsWith('@'))
|
|
32
|
+
return readFileSync(raw.slice(1), 'utf8');
|
|
33
|
+
return raw;
|
|
34
|
+
}
|
|
35
|
+
async function listFiles(context, projectId) {
|
|
36
|
+
const result = await context.client.callTool('list_project_files', { projectId });
|
|
37
|
+
return result.files ?? [];
|
|
38
|
+
}
|
|
39
|
+
function walk(dir, root = dir) {
|
|
40
|
+
const out = [];
|
|
41
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
42
|
+
if (entry.name === '.martini' || entry.name.startsWith('.'))
|
|
43
|
+
continue;
|
|
44
|
+
const abs = join(dir, entry.name);
|
|
45
|
+
if (entry.isDirectory())
|
|
46
|
+
out.push(...walk(abs, root));
|
|
47
|
+
else if (entry.isFile())
|
|
48
|
+
out.push(relative(root, abs).split(sep).join('/'));
|
|
49
|
+
}
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
export const files = {
|
|
53
|
+
name: 'files',
|
|
54
|
+
summary: 'Project memory files: ls, cat, write, pull <dir>, push <dir>',
|
|
55
|
+
usage: 'martini files ls | cat <path> | write <path> <content|@file|-> [--append|--create-only] | pull <dir> | push <dir> [--force]',
|
|
56
|
+
async run(context) {
|
|
57
|
+
const [sub, ...rest] = context.positionals;
|
|
58
|
+
const projectId = requireProject(context);
|
|
59
|
+
switch (sub) {
|
|
60
|
+
case 'ls': {
|
|
61
|
+
return { projectId, files: await listFiles(context, projectId) };
|
|
62
|
+
}
|
|
63
|
+
case 'cat': {
|
|
64
|
+
const path = rest[0];
|
|
65
|
+
if (!path)
|
|
66
|
+
throw new ArgumentError('martini files cat <path>');
|
|
67
|
+
const file = await context.client.callTool('read_project_file', { projectId, path });
|
|
68
|
+
return file.content;
|
|
69
|
+
}
|
|
70
|
+
case 'write': {
|
|
71
|
+
const path = rest[0];
|
|
72
|
+
if (!path)
|
|
73
|
+
throw new ArgumentError('martini files write <path> <content|@file|->');
|
|
74
|
+
const content = readContentArgument(rest[1]);
|
|
75
|
+
return context.client.callTool('write_project_file', {
|
|
76
|
+
projectId,
|
|
77
|
+
path,
|
|
78
|
+
content,
|
|
79
|
+
...(flagBoolean(context.flags, 'append') ? { append: true } : {}),
|
|
80
|
+
...(flagBoolean(context.flags, 'create-only') ? { createOnly: true } : {}),
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
case 'pull': {
|
|
84
|
+
const dir = resolve(context.cwd, rest[0] ?? '.');
|
|
85
|
+
const listed = await listFiles(context, projectId);
|
|
86
|
+
const manifest = { projectId, pulledAt: new Date(context.now()).toISOString(), files: {} };
|
|
87
|
+
for (const entry of listed) {
|
|
88
|
+
const file = await context.client.callTool('read_project_file', {
|
|
89
|
+
projectId,
|
|
90
|
+
path: entry.path,
|
|
91
|
+
});
|
|
92
|
+
const target = join(dir, ...entry.path.split('/'));
|
|
93
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
94
|
+
writeFileSync(target, file.content);
|
|
95
|
+
manifest.files[entry.path] = { updatedAt: file.updatedAt, sha256: sha256(file.content) };
|
|
96
|
+
}
|
|
97
|
+
mkdirSync(join(dir, '.martini'), { recursive: true });
|
|
98
|
+
writeFileSync(join(dir, FILES_MANIFEST), JSON.stringify(manifest, null, 2) + '\n');
|
|
99
|
+
return { projectId, dir, pulled: Object.keys(manifest.files).length, manifest: join(dir, FILES_MANIFEST) };
|
|
100
|
+
}
|
|
101
|
+
case 'push': {
|
|
102
|
+
const dir = resolve(context.cwd, rest[0] ?? '.');
|
|
103
|
+
const manifestPath = join(dir, FILES_MANIFEST);
|
|
104
|
+
if (!existsSync(manifestPath)) {
|
|
105
|
+
throw new ArgumentError(`${dir} was not pulled with martini files pull (no ${FILES_MANIFEST}); pull first so pushes can detect concurrent edits.`);
|
|
106
|
+
}
|
|
107
|
+
const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
|
|
108
|
+
if (manifest.projectId !== projectId) {
|
|
109
|
+
throw new ArgumentError(`${dir} was pulled from project ${manifest.projectId}, not ${projectId}.`);
|
|
110
|
+
}
|
|
111
|
+
const force = flagBoolean(context.flags, 'force') ?? false;
|
|
112
|
+
const pushed = [];
|
|
113
|
+
const unchanged = [];
|
|
114
|
+
const skipped = [];
|
|
115
|
+
for (const path of walk(dir)) {
|
|
116
|
+
const abs = join(dir, ...path.split('/'));
|
|
117
|
+
if (!statSync(abs).isFile())
|
|
118
|
+
continue;
|
|
119
|
+
const content = readFileSync(abs, 'utf8');
|
|
120
|
+
const known = manifest.files[path];
|
|
121
|
+
if (known && known.sha256 === sha256(content)) {
|
|
122
|
+
unchanged.push(path);
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
const written = await context.client.callTool('write_project_file', {
|
|
127
|
+
projectId,
|
|
128
|
+
path,
|
|
129
|
+
content,
|
|
130
|
+
...(known && !force ? { expectedUpdatedAt: known.updatedAt } : {}),
|
|
131
|
+
});
|
|
132
|
+
manifest.files[path] = { updatedAt: written.updatedAt, sha256: sha256(content) };
|
|
133
|
+
pushed.push(path);
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
if (error instanceof CliApiError && error.status < 500) {
|
|
137
|
+
skipped.push({ path, code: error.code, error: error.message });
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
throw error;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
manifest.pulledAt = new Date(context.now()).toISOString();
|
|
144
|
+
writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\n');
|
|
145
|
+
return { projectId, dir, pushed, unchanged: unchanged.length, skipped };
|
|
146
|
+
}
|
|
147
|
+
default:
|
|
148
|
+
throw new ArgumentError('martini files <ls | cat | write | pull | push>');
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
//# sourceMappingURL=files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files.js","sourceRoot":"","sources":["../../src/commands/files.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { hostname } from 'node:os';
|
|
2
|
+
import { CliApiError } from '../api.js';
|
|
3
|
+
import { flagBoolean, flagString } from '../args.js';
|
|
4
|
+
import { removeProfile, saveProfile } from '../config.js';
|
|
5
|
+
import { detectHostAgent } from '../host.js';
|
|
6
|
+
export function defaultDeviceLabel(env = process.env) {
|
|
7
|
+
const host = detectHostAgent(env);
|
|
8
|
+
return `${hostname()}${host ? ` (${host})` : ''}`;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* `martini login`: device-code pairing. Prints a code and a URL; the person
|
|
12
|
+
* types the code in the signed-in app; the poll collects the minted key and
|
|
13
|
+
* saves it as a profile named after the organization.
|
|
14
|
+
*/
|
|
15
|
+
export const login = {
|
|
16
|
+
name: 'login',
|
|
17
|
+
summary: 'Sign in: pair this device through the browser and save a 90-day key',
|
|
18
|
+
usage: 'martini login [--device <label>] [--profile <name>] [--no-default]',
|
|
19
|
+
async run(context) {
|
|
20
|
+
const deviceLabel = flagString(context.flags, 'device') ?? defaultDeviceLabel(context.env);
|
|
21
|
+
const started = await context.client.startLogin(deviceLabel);
|
|
22
|
+
const stderr = context.output.stderr ?? (text => process.stderr.write(text));
|
|
23
|
+
stderr([
|
|
24
|
+
'',
|
|
25
|
+
` Open ${started.verificationUrl}`,
|
|
26
|
+
` and enter the code ${started.code}`,
|
|
27
|
+
'',
|
|
28
|
+
` Waiting for approval (expires ${started.expiresAt})…`,
|
|
29
|
+
'',
|
|
30
|
+
].join('\n'));
|
|
31
|
+
const deadline = new Date(started.expiresAt).getTime();
|
|
32
|
+
for (;;) {
|
|
33
|
+
await context.sleep(Math.max(1000, started.interval * 1000));
|
|
34
|
+
const polled = await context.client.pollLogin(started.pollToken);
|
|
35
|
+
if (polled.status === 'pending') {
|
|
36
|
+
if (context.now() > deadline)
|
|
37
|
+
throw new CliApiError('The login request expired before it was approved.', {
|
|
38
|
+
code: 'LOGIN_EXPIRED',
|
|
39
|
+
status: 0,
|
|
40
|
+
});
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (polled.status === 'denied')
|
|
44
|
+
throw new CliApiError('The login was denied in the browser.', { code: 'LOGIN_DENIED', status: 0 });
|
|
45
|
+
if (polled.status === 'expired')
|
|
46
|
+
throw new CliApiError('The login request expired. Run martini login again.', {
|
|
47
|
+
code: 'LOGIN_EXPIRED',
|
|
48
|
+
status: 0,
|
|
49
|
+
});
|
|
50
|
+
if (polled.status === 'claimed')
|
|
51
|
+
throw new CliApiError('This login was already collected elsewhere. Run martini login again.', {
|
|
52
|
+
code: 'LOGIN_CLAIMED',
|
|
53
|
+
status: 0,
|
|
54
|
+
});
|
|
55
|
+
if (polled.status !== 'approved')
|
|
56
|
+
continue;
|
|
57
|
+
const profileName = flagString(context.flags, 'profile') ?? polled.organizationId;
|
|
58
|
+
const path = saveProfile(profileName, {
|
|
59
|
+
apiKey: polled.key,
|
|
60
|
+
apiUrl: context.config.apiUrl,
|
|
61
|
+
organizationId: polled.organizationId,
|
|
62
|
+
...(polled.expiresAt ? { expiresAt: polled.expiresAt } : {}),
|
|
63
|
+
}, { env: context.env, makeDefault: flagBoolean(context.flags, 'default') ?? true });
|
|
64
|
+
return {
|
|
65
|
+
ok: true,
|
|
66
|
+
profile: profileName,
|
|
67
|
+
organizationId: polled.organizationId,
|
|
68
|
+
keyId: polled.keyId,
|
|
69
|
+
expiresAt: polled.expiresAt,
|
|
70
|
+
savedTo: path,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
export const logout = {
|
|
76
|
+
name: 'logout',
|
|
77
|
+
summary: 'Forget a saved profile (the key itself is revoked from Settings → Martini API)',
|
|
78
|
+
usage: 'martini logout [--profile <name>]',
|
|
79
|
+
async run(context) {
|
|
80
|
+
const name = flagString(context.flags, 'profile');
|
|
81
|
+
if (!name) {
|
|
82
|
+
const source = context.config.source;
|
|
83
|
+
const match = /profile ([^)]+)\)/.exec(source);
|
|
84
|
+
if (!match)
|
|
85
|
+
return { ok: false, message: 'No profile is active; pass --profile <name>.' };
|
|
86
|
+
return { ok: removeProfile(match[1], { env: context.env }), profile: match[1] };
|
|
87
|
+
}
|
|
88
|
+
return { ok: removeProfile(name, { env: context.env }), profile: name };
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
export const whoami = {
|
|
92
|
+
name: 'whoami',
|
|
93
|
+
summary: 'Which organization and key this CLI acts as, and where the config came from',
|
|
94
|
+
usage: 'martini whoami',
|
|
95
|
+
async run(context) {
|
|
96
|
+
const me = await context.client.me();
|
|
97
|
+
return {
|
|
98
|
+
organization: me.organization,
|
|
99
|
+
key: me.key,
|
|
100
|
+
apiVersion: me.apiVersion,
|
|
101
|
+
apiUrl: context.config.apiUrl,
|
|
102
|
+
projectId: context.config.projectId ?? null,
|
|
103
|
+
canvasId: context.config.canvasId ?? null,
|
|
104
|
+
runId: context.config.runId ?? null,
|
|
105
|
+
configSource: context.config.source,
|
|
106
|
+
};
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
//# sourceMappingURL=login.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"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"}
|