@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,188 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { CLI_VERSION, CliApiError } from '../api.js';
|
|
7
|
+
import { flagBoolean, flagString } from '../args.js';
|
|
8
|
+
import { userConfigPath } from '../config.js';
|
|
9
|
+
import { loadManifest } from '../manifest.js';
|
|
10
|
+
import { ArgumentError } from '../schema.js';
|
|
11
|
+
import { requireProject } from './context.js';
|
|
12
|
+
/** `martini tools`: the manifest this build carries, and whether the server's differs. */
|
|
13
|
+
export const tools = {
|
|
14
|
+
name: 'tools',
|
|
15
|
+
summary: 'List the tools this build knows (and warn when the server has a newer manifest)',
|
|
16
|
+
usage: 'martini tools [--remote] [--fields name,description]',
|
|
17
|
+
async run(context) {
|
|
18
|
+
const local = loadManifest();
|
|
19
|
+
if (flagBoolean(context.flags, 'remote'))
|
|
20
|
+
return context.client.listTools();
|
|
21
|
+
let serverHash = null;
|
|
22
|
+
try {
|
|
23
|
+
serverHash = (await context.client.listTools()).hash;
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
serverHash = null;
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
hash: local.hash,
|
|
30
|
+
serverHash,
|
|
31
|
+
upToDate: serverHash === null ? null : serverHash === local.hash,
|
|
32
|
+
tools: local.tools.map(tool => ({
|
|
33
|
+
name: tool.name,
|
|
34
|
+
description: tool.description,
|
|
35
|
+
readOnly: tool.annotations.readOnlyHint,
|
|
36
|
+
requiresActions: tool.requiresActions,
|
|
37
|
+
})),
|
|
38
|
+
};
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
const MODELS_CACHE_TTL_MS = 60 * 60 * 1000;
|
|
42
|
+
/** `martini models`: get_models with a one-hour local cache — the catalog is ~10 KB and was fetched in 153 of 194 runs. */
|
|
43
|
+
export const models = {
|
|
44
|
+
name: 'models',
|
|
45
|
+
summary: 'The model catalog (cached for an hour; --refresh to refetch; --query/--capability to narrow)',
|
|
46
|
+
usage: 'martini models [--query <text>] [--capability <name>] [--detail summary|full] [--refresh]',
|
|
47
|
+
async run(context) {
|
|
48
|
+
const args = {};
|
|
49
|
+
const query = flagString(context.flags, 'query');
|
|
50
|
+
const capability = flagString(context.flags, 'capability');
|
|
51
|
+
const detail = flagString(context.flags, 'detail');
|
|
52
|
+
if (query)
|
|
53
|
+
args.query = query;
|
|
54
|
+
if (capability)
|
|
55
|
+
args.capability = capability;
|
|
56
|
+
if (detail)
|
|
57
|
+
args.detailLevel = detail;
|
|
58
|
+
const cacheDir = join(context.env.XDG_CACHE_HOME || join(context.env.HOME || tmpdir(), '.cache'), 'martini');
|
|
59
|
+
const cachePath = join(cacheDir, `models-${Buffer.from(JSON.stringify(args)).toString('base64url')}.json`);
|
|
60
|
+
if (!flagBoolean(context.flags, 'refresh') && existsSync(cachePath)) {
|
|
61
|
+
try {
|
|
62
|
+
const cached = JSON.parse(readFileSync(cachePath, 'utf8'));
|
|
63
|
+
if (context.now() - cached.fetchedAt < MODELS_CACHE_TTL_MS)
|
|
64
|
+
return cached.result;
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// A corrupt cache is just a miss.
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const result = await context.client.callTool('get_models', args);
|
|
71
|
+
mkdirSync(cacheDir, { recursive: true });
|
|
72
|
+
writeFileSync(cachePath, JSON.stringify({ fetchedAt: context.now(), result }));
|
|
73
|
+
return result;
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
/** Run ffprobe on one local file and reduce its JSON to what a session usually wants. */
|
|
77
|
+
export function probeFile(path, run = spawnSync) {
|
|
78
|
+
const probe = run('ffprobe', ['-v', 'quiet', '-print_format', 'json', '-show_format', '-show_streams', path], {
|
|
79
|
+
encoding: 'utf8',
|
|
80
|
+
});
|
|
81
|
+
if (probe.error || probe.status !== 0) {
|
|
82
|
+
throw new CliApiError(`ffprobe failed for ${path}${probe.stderr ? `: ${String(probe.stderr).trim()}` : ''}`, {
|
|
83
|
+
code: 'PROBE_FAILED',
|
|
84
|
+
status: 0,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
const parsed = JSON.parse(String(probe.stdout));
|
|
88
|
+
const video = parsed.streams?.find(stream => stream.codec_type === 'video');
|
|
89
|
+
const audio = parsed.streams?.find(stream => stream.codec_type === 'audio');
|
|
90
|
+
const fps = (() => {
|
|
91
|
+
const rate = video?.avg_frame_rate ?? video?.r_frame_rate;
|
|
92
|
+
if (typeof rate !== 'string')
|
|
93
|
+
return null;
|
|
94
|
+
const [num, den] = rate.split('/').map(Number);
|
|
95
|
+
return num && den ? Math.round((num / den) * 1000) / 1000 : num || null;
|
|
96
|
+
})();
|
|
97
|
+
const bytes = readFileSync(path);
|
|
98
|
+
return {
|
|
99
|
+
path,
|
|
100
|
+
bytes: bytes.byteLength,
|
|
101
|
+
durationSec: parsed.format?.duration ? Number(parsed.format.duration) : null,
|
|
102
|
+
width: typeof video?.width === 'number' ? video.width : null,
|
|
103
|
+
height: typeof video?.height === 'number' ? video.height : null,
|
|
104
|
+
fps,
|
|
105
|
+
videoCodec: typeof video?.codec_name === 'string' ? video.codec_name : null,
|
|
106
|
+
audioCodec: typeof audio?.codec_name === 'string' ? audio.codec_name : null,
|
|
107
|
+
audioChannels: typeof audio?.channels === 'number' ? audio.channels : null,
|
|
108
|
+
sampleRate: audio?.sample_rate ? Number(audio.sample_rate) : null,
|
|
109
|
+
sha256: createHash('sha256').update(bytes).digest('hex'),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* `martini probe <path|asset-id>…`: the measurement loop (download, ffprobe,
|
|
114
|
+
* checksum) in one command, so a session stops re-deriving it in shell steps.
|
|
115
|
+
*/
|
|
116
|
+
export const probe = {
|
|
117
|
+
name: 'probe',
|
|
118
|
+
summary: 'Measure local files or assets: duration, resolution, fps, codecs, channels, sha256 (needs ffprobe)',
|
|
119
|
+
usage: 'martini probe <path|asset-id>… [--dir <download-dir>]',
|
|
120
|
+
async run(context) {
|
|
121
|
+
if (context.positionals.length === 0)
|
|
122
|
+
throw new ArgumentError('Give file paths or asset ids to probe.');
|
|
123
|
+
const dir = flagString(context.flags, 'dir') ?? join(tmpdir(), 'martini-probe');
|
|
124
|
+
const reports = [];
|
|
125
|
+
for (const target of context.positionals) {
|
|
126
|
+
if (existsSync(target)) {
|
|
127
|
+
reports.push(probeFile(target));
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
const projectId = requireProject(context);
|
|
131
|
+
const link = await context.client.callTool('get_asset_download_url', {
|
|
132
|
+
projectId,
|
|
133
|
+
assetId: target,
|
|
134
|
+
});
|
|
135
|
+
const { bytes } = await context.client.fetchBytes(link.downloadUrl);
|
|
136
|
+
mkdirSync(dir, { recursive: true });
|
|
137
|
+
const path = join(dir, link.suggestedFilename ?? target);
|
|
138
|
+
writeFileSync(path, bytes);
|
|
139
|
+
reports.push({ ...probeFile(path), assetId: target });
|
|
140
|
+
}
|
|
141
|
+
return { reports };
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
const AGENTS_SNIPPET = `## Martini CLI
|
|
145
|
+
|
|
146
|
+
Martini is available as the \`martini\` command (every MCP tool is a subcommand; \`martini --help\` lists them,
|
|
147
|
+
\`martini <tool> --help\` shows a tool's flags). Prefer it over MCP tool calls for bulk, file, and wait work:
|
|
148
|
+
|
|
149
|
+
- \`martini upload <files…> --canvas <id>\` uploads local files as assets in one step.
|
|
150
|
+
- \`martini download <asset-id…> --dir out/\` saves completed assets; \`martini probe <path|id>\` measures them.
|
|
151
|
+
- \`martini <generation tool> … --wait\` blocks until the asset is ready; \`martini wait <ids…>\` resumes a wait.
|
|
152
|
+
- \`martini files pull dir/\` / \`martini files push dir/\` edit project memory files locally with conflict checks.
|
|
153
|
+
- Any argument can be JSON: \`--json @args.json\`. Results are JSON on stdout; \`--fields a,b\` trims them; \`--out f\` writes them.
|
|
154
|
+
|
|
155
|
+
Exit codes: 0 ok · 1 tool error · 2 usage · 3 auth · 4 not ready or timed out · 5 insufficient olives.
|
|
156
|
+
`;
|
|
157
|
+
/** `martini init`: the AGENTS.md snippet that teaches a repo's agents to use the CLI. */
|
|
158
|
+
export const init = {
|
|
159
|
+
name: 'init',
|
|
160
|
+
summary: 'Print (or append to AGENTS.md with --write) the snippet that teaches agents to use the CLI',
|
|
161
|
+
usage: 'martini init [--write [path]]',
|
|
162
|
+
async run(context) {
|
|
163
|
+
if (flagBoolean(context.flags, 'write')) {
|
|
164
|
+
const target = context.positionals[0] ?? join(context.cwd, 'AGENTS.md');
|
|
165
|
+
const existing = existsSync(target) ? readFileSync(target, 'utf8') : '';
|
|
166
|
+
if (existing.includes('## Martini CLI'))
|
|
167
|
+
return { ok: true, path: target, changed: false };
|
|
168
|
+
writeFileSync(target, `${existing.trimEnd()}${existing ? '\n\n' : ''}${AGENTS_SNIPPET}`);
|
|
169
|
+
return { ok: true, path: target, changed: true };
|
|
170
|
+
}
|
|
171
|
+
return AGENTS_SNIPPET;
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
export const version = {
|
|
175
|
+
name: 'version',
|
|
176
|
+
summary: 'The CLI version, the manifest hash it was built from, and the config file paths',
|
|
177
|
+
usage: 'martini version',
|
|
178
|
+
async run(context) {
|
|
179
|
+
const manifest = loadManifest();
|
|
180
|
+
return {
|
|
181
|
+
version: CLI_VERSION,
|
|
182
|
+
manifestHash: manifest.hash,
|
|
183
|
+
tools: manifest.tools.length,
|
|
184
|
+
userConfig: userConfigPath(context.env),
|
|
185
|
+
};
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
//# sourceMappingURL=tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../../src/commands/tools.ts"],"names":[],"mappings":"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"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where the CLI's credential and context come from, most specific first:
|
|
3
|
+
*
|
|
4
|
+
* 1. flags --api-key, --api-url, --project, --canvas, --run, --profile
|
|
5
|
+
* 2. environment MARTINI_API_KEY, MARTINI_API_URL, MARTINI_PROJECT_ID, MARTINI_CANVAS_ID,
|
|
6
|
+
* MARTINI_RUN_ID, MARTINI_PROFILE, MARTINI_CONFIG_FILE
|
|
7
|
+
* 3. the nearest .martini/config.json above the working directory — what the workflow
|
|
8
|
+
* workspace file harness writes into a run's sandbox (a `run` profile)
|
|
9
|
+
* 4. the user file ~/.config/martini/config.json — profiles `martini login` saves, one per
|
|
10
|
+
* organization, with a default
|
|
11
|
+
*
|
|
12
|
+
* A run's sandbox therefore needs nothing but its working directory; a laptop
|
|
13
|
+
* needs one `martini login`; a script sets two environment variables.
|
|
14
|
+
*/
|
|
15
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
16
|
+
import { homedir } from 'node:os';
|
|
17
|
+
import { dirname, join, resolve } from 'node:path';
|
|
18
|
+
import { flagString } from './args.js';
|
|
19
|
+
export const DEFAULT_API_URL = 'https://api.martini.film';
|
|
20
|
+
export const WORKSPACE_CONFIG_DIR = '.martini';
|
|
21
|
+
export const WORKSPACE_CONFIG_FILE = 'config.json';
|
|
22
|
+
export function userConfigPath(env = process.env) {
|
|
23
|
+
if (env.MARTINI_CONFIG_FILE)
|
|
24
|
+
return env.MARTINI_CONFIG_FILE;
|
|
25
|
+
const base = env.XDG_CONFIG_HOME || join(env.HOME || homedir(), '.config');
|
|
26
|
+
return join(base, 'martini', 'config.json');
|
|
27
|
+
}
|
|
28
|
+
export function readJsonFile(path) {
|
|
29
|
+
if (!existsSync(path))
|
|
30
|
+
return null;
|
|
31
|
+
try {
|
|
32
|
+
return JSON.parse(readFileSync(path, 'utf8'));
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/** The closest `.martini/config.json` at or above `cwd`. */
|
|
39
|
+
export function findWorkspaceConfig(cwd) {
|
|
40
|
+
let dir = resolve(cwd);
|
|
41
|
+
for (;;) {
|
|
42
|
+
const candidate = join(dir, WORKSPACE_CONFIG_DIR, WORKSPACE_CONFIG_FILE);
|
|
43
|
+
const profile = readJsonFile(candidate);
|
|
44
|
+
if (profile)
|
|
45
|
+
return { path: candidate, profile };
|
|
46
|
+
const parent = dirname(dir);
|
|
47
|
+
if (parent === dir)
|
|
48
|
+
return null;
|
|
49
|
+
dir = parent;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export function resolveConfig(flags, options = {}) {
|
|
53
|
+
const env = options.env ?? process.env;
|
|
54
|
+
const cwd = options.cwd ?? process.cwd();
|
|
55
|
+
const defined = (profile) => Object.fromEntries(Object.entries(profile).filter(([, value]) => value !== undefined && value !== ''));
|
|
56
|
+
const fromFlags = defined({
|
|
57
|
+
apiKey: flagString(flags, 'api-key'),
|
|
58
|
+
apiUrl: flagString(flags, 'api-url'),
|
|
59
|
+
projectId: flagString(flags, 'project'),
|
|
60
|
+
canvasId: flagString(flags, 'canvas'),
|
|
61
|
+
runId: flagString(flags, 'run'),
|
|
62
|
+
});
|
|
63
|
+
const fromEnv = defined({
|
|
64
|
+
apiKey: env.MARTINI_API_KEY,
|
|
65
|
+
apiUrl: env.MARTINI_API_URL,
|
|
66
|
+
projectId: env.MARTINI_PROJECT_ID,
|
|
67
|
+
canvasId: env.MARTINI_CANVAS_ID,
|
|
68
|
+
runId: env.MARTINI_RUN_ID,
|
|
69
|
+
});
|
|
70
|
+
let source = 'flags/env';
|
|
71
|
+
let fileProfile = {};
|
|
72
|
+
const workspace = findWorkspaceConfig(cwd);
|
|
73
|
+
const requestedProfile = flagString(flags, 'profile') ?? env.MARTINI_PROFILE;
|
|
74
|
+
if (workspace && !requestedProfile) {
|
|
75
|
+
fileProfile = workspace.profile;
|
|
76
|
+
source = workspace.path;
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
const user = readJsonFile(userConfigPath(env));
|
|
80
|
+
const name = requestedProfile ?? user?.default;
|
|
81
|
+
const profile = name && user?.profiles ? user.profiles[name] : undefined;
|
|
82
|
+
if (profile) {
|
|
83
|
+
fileProfile = profile;
|
|
84
|
+
source = `${userConfigPath(env)} (profile ${name})`;
|
|
85
|
+
}
|
|
86
|
+
else if (requestedProfile) {
|
|
87
|
+
source = `profile ${requestedProfile} (not found)`;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const merged = { ...fileProfile, ...fromEnv, ...fromFlags };
|
|
91
|
+
return {
|
|
92
|
+
apiKey: merged.apiKey ?? null,
|
|
93
|
+
apiUrl: (merged.apiUrl ?? DEFAULT_API_URL).replace(/\/+$/, ''),
|
|
94
|
+
projectId: merged.projectId,
|
|
95
|
+
canvasId: merged.canvasId,
|
|
96
|
+
runId: merged.runId,
|
|
97
|
+
source,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/** Save a profile from `martini login`; the first saved profile becomes the default. */
|
|
101
|
+
export function saveProfile(name, profile, options = {}) {
|
|
102
|
+
const path = userConfigPath(options.env ?? process.env);
|
|
103
|
+
const current = readJsonFile(path) ?? {};
|
|
104
|
+
const profiles = { ...(current.profiles ?? {}), [name]: profile };
|
|
105
|
+
const next = {
|
|
106
|
+
default: options.makeDefault || !current.default ? name : current.default,
|
|
107
|
+
profiles,
|
|
108
|
+
};
|
|
109
|
+
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
110
|
+
writeFileSync(path, JSON.stringify(next, null, 2) + '\n', { mode: 0o600 });
|
|
111
|
+
return path;
|
|
112
|
+
}
|
|
113
|
+
export function removeProfile(name, options = {}) {
|
|
114
|
+
const path = userConfigPath(options.env ?? process.env);
|
|
115
|
+
const current = readJsonFile(path);
|
|
116
|
+
if (!current?.profiles?.[name])
|
|
117
|
+
return false;
|
|
118
|
+
const { [name]: _removed, ...profiles } = current.profiles;
|
|
119
|
+
const remaining = Object.keys(profiles);
|
|
120
|
+
const next = {
|
|
121
|
+
...(current.default === name ? (remaining[0] ? { default: remaining[0] } : {}) : { default: current.default }),
|
|
122
|
+
profiles,
|
|
123
|
+
};
|
|
124
|
+
writeFileSync(path, JSON.stringify(next, null, 2) + '\n', { mode: 0o600 });
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|