@lumenkey/cli 0.3.0 → 0.4.0-rc.1
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 +51 -2
- package/dist/cli/copied.js +86 -85
- package/dist/cli/index.js +56 -44
- package/dist/cli/render.js +5 -1
- package/dist/core/asset-stream.d.ts +16 -0
- package/dist/core/asset-stream.js +64 -0
- package/dist/core/atomic-config-file.js +8 -7
- package/dist/core/chinese-messages.d.ts +4 -0
- package/dist/core/chinese-messages.js +907 -0
- package/dist/core/chinese-script.d.ts +7 -0
- package/dist/core/chinese-script.js +28 -0
- package/dist/core/client.js +2 -1
- package/dist/core/config-dir.js +2 -1
- package/dist/core/config.js +7 -6
- package/dist/core/download-root.d.ts +5 -0
- package/dist/core/download-root.js +64 -0
- package/dist/core/download.js +15 -12
- package/dist/core/generation-input.js +3 -2
- package/dist/core/generation.d.ts +8 -1
- package/dist/core/generation.js +13 -10
- package/dist/core/job-display.js +7 -6
- package/dist/core/locale.d.ts +2 -0
- package/dist/core/locale.js +172 -142
- package/dist/core/outcome.d.ts +6 -0
- package/dist/core/outcome.js +17 -4
- package/dist/core/output-path.d.ts +3 -1
- package/dist/core/output-path.js +5 -2
- package/dist/core/params.js +26 -25
- package/dist/core/profiles.js +5 -4
- package/dist/core/set-values.d.ts +0 -1
- package/dist/core/set-values.js +7 -7
- package/dist/core/submission-journal.js +43 -17
- package/dist/core/version.js +2 -3
- package/dist/mcp/server.js +30 -29
- package/dist/studio/asset-integrity-hash.d.ts +28 -0
- package/dist/studio/asset-integrity-hash.js +93 -0
- package/dist/studio/asset-integrity-queue.d.ts +24 -0
- package/dist/studio/asset-integrity-queue.js +94 -0
- package/dist/studio/asset-store.d.ts +52 -0
- package/dist/studio/asset-store.js +352 -0
- package/dist/studio/canvas/LICENSES.txt +1590 -0
- package/dist/studio/canvas/dependencies.json +862 -0
- package/dist/studio/canvas/director-source-manifest.json +47 -0
- package/dist/studio/canvas/editor.css +239 -0
- package/dist/studio/canvas/editor.js +4613 -0
- package/dist/studio/canvas/integration-source-manifest.json +384 -0
- package/dist/studio/canvas/source-manifest.json +240 -0
- package/dist/studio/canvas-store.d.ts +1254 -0
- package/dist/studio/canvas-store.js +205 -0
- package/dist/studio/capture-placement.d.ts +11 -0
- package/dist/studio/capture-placement.js +85 -0
- package/dist/studio/cloud-contract.d.ts +128 -0
- package/dist/studio/cloud-contract.js +229 -0
- package/dist/studio/cloud-service.d.ts +178 -0
- package/dist/studio/cloud-service.js +202 -0
- package/dist/studio/director-captures.d.ts +59 -0
- package/dist/studio/director-captures.js +141 -0
- package/dist/studio/director-contract.d.ts +1489 -0
- package/dist/studio/director-contract.js +288 -0
- package/dist/studio/director-png.d.ts +6 -0
- package/dist/studio/director-png.js +41 -0
- package/dist/studio/director-relations.d.ts +40 -0
- package/dist/studio/director-relations.js +103 -0
- package/dist/studio/document-authority.d.ts +7 -0
- package/dist/studio/document-authority.js +50 -0
- package/dist/studio/draft-store.d.ts +95 -0
- package/dist/studio/draft-store.js +100 -0
- package/dist/studio/errors.d.ts +85 -0
- package/dist/studio/errors.js +94 -0
- package/dist/studio/launch.d.ts +5 -0
- package/dist/studio/launch.js +31 -0
- package/dist/studio/media.d.ts +9 -0
- package/dist/studio/media.js +50 -0
- package/dist/studio/output-save-contract.d.ts +1321 -0
- package/dist/studio/output-save-contract.js +26 -0
- package/dist/studio/output-save-journal.d.ts +971 -0
- package/dist/studio/output-save-journal.js +287 -0
- package/dist/studio/output-save-recovery.d.ts +18 -0
- package/dist/studio/output-save-recovery.js +161 -0
- package/dist/studio/output-save-service.d.ts +436 -0
- package/dist/studio/output-save-service.js +251 -0
- package/dist/studio/output-save-worker.d.ts +40 -0
- package/dist/studio/output-save-worker.js +360 -0
- package/dist/studio/paths.d.ts +6 -0
- package/dist/studio/paths.js +68 -0
- package/dist/studio/run-journal.d.ts +993 -0
- package/dist/studio/run-journal.js +125 -0
- package/dist/studio/server.d.ts +17 -0
- package/dist/studio/server.js +440 -0
- package/dist/studio/session.d.ts +6 -0
- package/dist/studio/session.js +26 -0
- package/dist/studio/shell.d.ts +3 -0
- package/dist/studio/shell.js +23 -0
- package/dist/studio/style.d.ts +2 -0
- package/dist/studio/style.js +29 -0
- package/dist/studio/types.d.ts +26 -0
- package/dist/studio/types.js +2 -0
- package/dist/studio/web/app.d.ts +2 -0
- package/dist/studio/web/app.js +662 -0
- package/dist/studio/web/asset-controller.d.ts +37 -0
- package/dist/studio/web/asset-controller.js +191 -0
- package/dist/studio/web/asset-model.d.ts +29 -0
- package/dist/studio/web/asset-model.js +51 -0
- package/dist/studio/web/controller.d.ts +113 -0
- package/dist/studio/web/controller.js +401 -0
- package/dist/studio/web/messages.d.ts +2 -0
- package/dist/studio/web/messages.js +74 -0
- package/dist/studio/web/model.d.ts +75 -0
- package/dist/studio/web/model.js +182 -0
- package/dist/studio/web/save-controller.d.ts +85 -0
- package/dist/studio/web/save-controller.js +495 -0
- package/dist/studio/web/save-model.d.ts +63 -0
- package/dist/studio/web/save-model.js +64 -0
- package/dist/studio/web/save-queue.d.ts +7 -0
- package/dist/studio/web/save-queue.js +62 -0
- package/dist/studio/web/save-view.d.ts +9 -0
- package/dist/studio/web/save-view.js +19 -0
- package/dist/studio/writer-lock.d.ts +5 -0
- package/dist/studio/writer-lock.js +88 -0
- package/package.json +11 -5
- package/dist/cli/copied.d.ts.map +0 -1
- package/dist/cli/copied.js.map +0 -1
- package/dist/cli/index.d.ts.map +0 -1
- package/dist/cli/index.js.map +0 -1
- package/dist/cli/render.d.ts.map +0 -1
- package/dist/cli/render.js.map +0 -1
- package/dist/core/atomic-config-file.d.ts.map +0 -1
- package/dist/core/atomic-config-file.js.map +0 -1
- package/dist/core/chat.d.ts.map +0 -1
- package/dist/core/chat.js.map +0 -1
- package/dist/core/client.d.ts.map +0 -1
- package/dist/core/client.js.map +0 -1
- package/dist/core/config-dir.d.ts.map +0 -1
- package/dist/core/config-dir.js.map +0 -1
- package/dist/core/config-error.d.ts.map +0 -1
- package/dist/core/config-error.js.map +0 -1
- package/dist/core/config.d.ts.map +0 -1
- package/dist/core/config.js.map +0 -1
- package/dist/core/credit-cap.d.ts.map +0 -1
- package/dist/core/credit-cap.js.map +0 -1
- package/dist/core/download.d.ts.map +0 -1
- package/dist/core/download.js.map +0 -1
- package/dist/core/error-result.d.ts.map +0 -1
- package/dist/core/error-result.js.map +0 -1
- package/dist/core/explain.d.ts.map +0 -1
- package/dist/core/explain.js.map +0 -1
- package/dist/core/generation-input.d.ts.map +0 -1
- package/dist/core/generation-input.js.map +0 -1
- package/dist/core/generation-result.d.ts.map +0 -1
- package/dist/core/generation-result.js.map +0 -1
- package/dist/core/generation.d.ts.map +0 -1
- package/dist/core/generation.js.map +0 -1
- package/dist/core/job-display.d.ts.map +0 -1
- package/dist/core/job-display.js.map +0 -1
- package/dist/core/locale.d.ts.map +0 -1
- package/dist/core/locale.js.map +0 -1
- package/dist/core/origin.d.ts.map +0 -1
- package/dist/core/origin.js.map +0 -1
- package/dist/core/outcome.d.ts.map +0 -1
- package/dist/core/outcome.js.map +0 -1
- package/dist/core/output-path.d.ts.map +0 -1
- package/dist/core/output-path.js.map +0 -1
- package/dist/core/params.d.ts.map +0 -1
- package/dist/core/params.js.map +0 -1
- package/dist/core/poller.d.ts.map +0 -1
- package/dist/core/poller.js.map +0 -1
- package/dist/core/profiles.d.ts.map +0 -1
- package/dist/core/profiles.js.map +0 -1
- package/dist/core/set-values.d.ts.map +0 -1
- package/dist/core/set-values.js.map +0 -1
- package/dist/core/submission-journal.d.ts.map +0 -1
- package/dist/core/submission-journal.js.map +0 -1
- package/dist/core/types.d.ts.map +0 -1
- package/dist/core/types.js.map +0 -1
- package/dist/core/version.d.ts.map +0 -1
- package/dist/core/version.js.map +0 -1
- package/dist/core/wait.d.ts.map +0 -1
- package/dist/core/wait.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/mcp/render.d.ts.map +0 -1
- package/dist/mcp/render.js.map +0 -1
- package/dist/mcp/server.d.ts.map +0 -1
- package/dist/mcp/server.js.map +0 -1
package/dist/cli/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { zhText } from '../core/chinese-messages.js';
|
|
1
2
|
import { cac } from 'cac';
|
|
2
3
|
import pc from 'picocolors';
|
|
3
4
|
import { loadConfig, saveConfig } from '../core/config.js';
|
|
@@ -40,20 +41,20 @@ function commandToken(argv) {
|
|
|
40
41
|
}
|
|
41
42
|
function usageLines(argv) {
|
|
42
43
|
const usage = [
|
|
43
|
-
ui('Usage: lumenkey <command> [options]',
|
|
44
|
-
ui('Run lumenkey --help to see commands.',
|
|
44
|
+
ui('Usage: lumenkey <command> [options]', zhText("m406")),
|
|
45
|
+
ui('Run lumenkey --help to see commands.', zhText("m405")),
|
|
45
46
|
];
|
|
46
47
|
const token = commandToken(argv);
|
|
47
48
|
if (!token) {
|
|
48
49
|
const unknown = argv.slice(2).find(arg => arg.startsWith('-') && arg !== '--json' && arg !== '--lang' && !arg.startsWith('--lang=') && arg !== '--');
|
|
49
50
|
return unknown
|
|
50
|
-
? [ui(`Unknown option ${JSON.stringify(unknown)}.`,
|
|
51
|
+
? [ui(`Unknown option ${JSON.stringify(unknown)}.`, (zhText("m402") + (JSON.stringify(unknown)) + "。")), ...usage]
|
|
51
52
|
: usage;
|
|
52
53
|
}
|
|
53
54
|
const shown = token.replace(/[\u0000-\u001f\u007f]/g, '');
|
|
54
55
|
const named = /^(?:lk_|sk_|Bearer\b)/i.test(token)
|
|
55
|
-
? ui('That is not a lumenkey command.',
|
|
56
|
-
: ui(`'${shown}' is not a lumenkey command.`,
|
|
56
|
+
? ui('That is not a lumenkey command.', zhText("m404"))
|
|
57
|
+
: ui(`'${shown}' is not a lumenkey command.`, ("'" + (shown) + zhText("m403")));
|
|
57
58
|
return [named, ...usage];
|
|
58
59
|
}
|
|
59
60
|
/** CAC's own parse errors become argument errors with readable, localized text. */
|
|
@@ -80,19 +81,19 @@ function normalizeCliError(err) {
|
|
|
80
81
|
return modelParameterError('MODEL_PARAM_INVALID', 'count');
|
|
81
82
|
}
|
|
82
83
|
// Same as the MCP surface: with a confirm requested, the outcome still says nothing was sent.
|
|
83
|
-
return nothingSentIfConfirmed(new LumenKeyError('INPUT_INVALID', undefined, undefined, original ? ui(`Unknown option ${JSON.stringify(original.split('=')[0])}.`,
|
|
84
|
+
return nothingSentIfConfirmed(new LumenKeyError('INPUT_INVALID', undefined, undefined, original ? ui(`Unknown option ${JSON.stringify(original.split('=')[0])}.`, (zhText("m402") + (JSON.stringify(original.split('=')[0])) + "。")) : err.message), { confirm: argv.includes('--confirm') });
|
|
84
85
|
}
|
|
85
86
|
const missingArgs = err.message.match(/^missing required args for command `([^`]+)`$/);
|
|
86
87
|
if (missingArgs)
|
|
87
|
-
return new UsageError(ui(err.message,
|
|
88
|
+
return new UsageError(ui(err.message, (zhText("m401") + (missingArgs[1]) + zhText("m320"))));
|
|
88
89
|
return err;
|
|
89
90
|
}
|
|
90
91
|
export async function runCli() {
|
|
91
92
|
const cli = cac('lumenkey');
|
|
92
|
-
cli.option('--lang <lang>', ui('Message language: zh or en',
|
|
93
|
+
cli.option('--lang <lang>', ui('Message language: zh or en', zhText("m400")));
|
|
93
94
|
cli
|
|
94
|
-
.command('login', ui('Configure
|
|
95
|
-
.option('--key <apiKey>', ui('API Key to configure (
|
|
95
|
+
.command('login', ui('Configure cloud connection', zhText("m399")))
|
|
96
|
+
.option('--key <apiKey>', ui('API Key to configure (omit to enter interactively)', zhText("m398")))
|
|
96
97
|
.action(async (options) => {
|
|
97
98
|
applyRaw(options, 'key', ['--key']);
|
|
98
99
|
const keyFromCommandLine = typeof options.key === 'string' && options.key.length > 0;
|
|
@@ -104,23 +105,23 @@ export async function runCli() {
|
|
|
104
105
|
if (options.key) {
|
|
105
106
|
saveConfig({ apiKey: options.key });
|
|
106
107
|
if (keyFromCommandLine)
|
|
107
|
-
console.error(ui('This key was passed with --key and can remain in shell history. Prefer LUMENKEY_API_KEY.',
|
|
108
|
+
console.error(ui('This key was passed with --key and can remain in shell history. Prefer LUMENKEY_API_KEY.', zhText("m397")));
|
|
108
109
|
if (wantsJson())
|
|
109
110
|
console.log(JSON.stringify({ saved: true }));
|
|
110
111
|
else
|
|
111
|
-
console.log(pc.green(ui('✔ API Key saved to ~/.lumenkey/config.json',
|
|
112
|
+
console.log(pc.green(ui('✔ API Key saved to ~/.lumenkey/config.json', zhText("m396"))));
|
|
112
113
|
}
|
|
113
114
|
else if (wantsJson()) {
|
|
114
115
|
console.log(JSON.stringify({ saved: false }));
|
|
115
116
|
}
|
|
116
117
|
else {
|
|
117
|
-
console.log(pc.yellow(ui('Tip: You can also set the environment variable:',
|
|
118
|
+
console.log(pc.yellow(ui('Tip: You can also set the environment variable:', zhText("m395"))));
|
|
118
119
|
console.log(pc.cyan(' export LUMENKEY_API_KEY="lk_live_..."'));
|
|
119
120
|
}
|
|
120
121
|
});
|
|
121
122
|
cli
|
|
122
|
-
.command('models', ui('List all available AI models from catalog',
|
|
123
|
-
.option('--type <type>', ui('Filter models by type: video | image | text | audio',
|
|
123
|
+
.command('models', ui('List all available AI models from catalog', zhText("m394")))
|
|
124
|
+
.option('--type <type>', ui('Filter models by type: video | image | text | audio', zhText("m393")))
|
|
124
125
|
.action(async (options) => {
|
|
125
126
|
applyRaw(options, 'type', ['--type']);
|
|
126
127
|
const config = loadConfig();
|
|
@@ -130,34 +131,34 @@ export async function runCli() {
|
|
|
130
131
|
console.log(JSON.stringify({ count: models.length, models }));
|
|
131
132
|
return;
|
|
132
133
|
}
|
|
133
|
-
console.log(pc.cyan(ui('Fetching dynamic model catalog from LumenKey...',
|
|
134
|
+
console.log(pc.cyan(ui('Fetching dynamic model catalog from LumenKey...', zhText("m392"))));
|
|
134
135
|
if (models.length === 0) {
|
|
135
|
-
console.log(pc.dim(ui('No models available for this request.',
|
|
136
|
+
console.log(pc.dim(ui('No models available for this request.', zhText("m330"))));
|
|
136
137
|
return;
|
|
137
138
|
}
|
|
138
|
-
console.log(pc.bold(ui('\nAvailable Models:',
|
|
139
|
+
console.log(pc.bold(ui('\nAvailable Models:', zhText("m391"))));
|
|
139
140
|
for (const m of models) {
|
|
140
141
|
const title = m.displayName || m.name || m.id;
|
|
141
142
|
const typeBadge = m.type ? `[${m.type}]` : (m.capabilities?.length ? `[${m.capabilities.join(',')}]` : '');
|
|
142
|
-
const pricingText = m.pricing?.map((p) => ui(`${p.credits} credits/${p.unit}`,
|
|
143
|
+
const pricingText = m.pricing?.map((p) => ui(`${p.credits} credits/${p.unit}`, ("" + (p.credits) + zhText("m390") + (p.unit) + ""))).join(', ') || '';
|
|
143
144
|
console.log(` ${pc.green(m.id.padEnd(26))} ${pc.dim(typeBadge.padEnd(12))} ${title} ${pricingText ? pc.yellow(`(${pricingText})`) : ''}`);
|
|
144
145
|
}
|
|
145
146
|
});
|
|
146
147
|
registerOptions(cli
|
|
147
|
-
.command('video [...extra]', ui('Generate AI video (Seedance 2.0 / Wan 3.0 / etc.)',
|
|
148
|
+
.command('video [...extra]', ui('Generate AI video (Seedance 2.0 / Wan 3.0 / etc.)', zhText("m389"))), videoParams)
|
|
148
149
|
.action(async (extra, options) => {
|
|
149
150
|
if (extra.length)
|
|
150
|
-
throw new Error(ui(`Unexpected video argument ${JSON.stringify(extra[0])}. Use lumenkey video -m <modelId> -p <prompt>.`,
|
|
151
|
+
throw new Error(ui(`Unexpected video argument ${JSON.stringify(extra[0])}. Use lumenkey video -m <modelId> -p <prompt>.`, (zhText("m387") + (JSON.stringify(extra[0])) + zhText("m388"))));
|
|
151
152
|
await runCliGeneration('video', videoParams, options);
|
|
152
153
|
});
|
|
153
154
|
cli
|
|
154
|
-
.command('status <jobId>', ui('Check status of an asynchronous generation job',
|
|
155
|
+
.command('status <jobId>', ui('Check status of an asynchronous generation job', zhText("m386")))
|
|
155
156
|
.action(async (jobId) => {
|
|
156
157
|
const outcome = await readJob(new LumenKeyClient(loadConfig()), jobId);
|
|
157
158
|
renderOutcome(outcome, ok => console.log(formatJobDetail(ok.job)));
|
|
158
159
|
});
|
|
159
160
|
cli
|
|
160
|
-
.command('balance', ui('Check current account credits balance',
|
|
161
|
+
.command('balance', ui('Check current account credits balance', zhText("m385")))
|
|
161
162
|
.action(async () => {
|
|
162
163
|
const config = loadConfig();
|
|
163
164
|
const client = new LumenKeyClient(config);
|
|
@@ -166,15 +167,15 @@ export async function runCli() {
|
|
|
166
167
|
console.log(JSON.stringify(balance));
|
|
167
168
|
return;
|
|
168
169
|
}
|
|
169
|
-
console.log(pc.bold(ui('\nLumenKey Account Balance:',
|
|
170
|
-
console.log(ui(` Available Credits: ${pc.green(pc.bold(String(balance.availableCredits ?? 0)))}`,
|
|
171
|
-
console.log(ui(` Reserved Credits: ${pc.yellow(String(balance.reservedCredits ?? 0))}`,
|
|
170
|
+
console.log(pc.bold(ui('\nLumenKey Account Balance:', zhText("m384"))));
|
|
171
|
+
console.log(ui(` Available Credits: ${pc.green(pc.bold(String(balance.availableCredits ?? 0)))}`, (zhText("m383") + (pc.green(pc.bold(String(balance.availableCredits ?? 0)))) + "")));
|
|
172
|
+
console.log(ui(` Reserved Credits: ${pc.yellow(String(balance.reservedCredits ?? 0))}`, (zhText("m382") + (pc.yellow(String(balance.reservedCredits ?? 0))) + "")));
|
|
172
173
|
if (balance.updatedAt) {
|
|
173
|
-
console.log(ui(` Updated At: ${pc.gray(balance.updatedAt)}`,
|
|
174
|
+
console.log(ui(` Updated At: ${pc.gray(balance.updatedAt)}`, (zhText("m381") + (pc.gray(balance.updatedAt)) + "")));
|
|
174
175
|
}
|
|
175
176
|
});
|
|
176
177
|
registerOptions(cli
|
|
177
|
-
.command('chat', ui('Run billable LLM chat inference',
|
|
178
|
+
.command('chat', ui('Run billable LLM chat inference', zhText("m380"))), chatParams)
|
|
178
179
|
.action(async (options) => {
|
|
179
180
|
for (const param of forSurface(chatParams, 'cli'))
|
|
180
181
|
if (param.flag && param.kind === 'string')
|
|
@@ -183,45 +184,56 @@ export async function runCli() {
|
|
|
183
184
|
const client = new LumenKeyClient(loadConfig());
|
|
184
185
|
const outcome = await runChat(client, chatArgs({ ...argsFromCac(chatParams, options), maxCredits: rawOption(['--max-credits']) }, 'cli'));
|
|
185
186
|
renderOutcome(outcome, res => {
|
|
186
|
-
console.log(pc.green(ui('\nResponse:',
|
|
187
|
+
console.log(pc.green(ui('\nResponse:', zhText("m379"))));
|
|
187
188
|
console.log(res.text);
|
|
188
189
|
console.log(formatChatBilling(res.billing ?? undefined));
|
|
189
190
|
const usage = res.usage;
|
|
190
191
|
if (usage) {
|
|
191
|
-
const total = usage.total_tokens ?? ui('not reported',
|
|
192
|
-
const promptTokens = usage.prompt_tokens ?? ui('not reported',
|
|
193
|
-
const completionTokens = usage.completion_tokens ?? ui('not reported',
|
|
194
|
-
console.log(pc.gray(ui(`\n[Tokens: ${total} (prompt: ${promptTokens}, completion: ${completionTokens})]`,
|
|
192
|
+
const total = usage.total_tokens ?? ui('not reported', zhText("m49"));
|
|
193
|
+
const promptTokens = usage.prompt_tokens ?? ui('not reported', zhText("m49"));
|
|
194
|
+
const completionTokens = usage.completion_tokens ?? ui('not reported', zhText("m49"));
|
|
195
|
+
console.log(pc.gray(ui(`\n[Tokens: ${total} (prompt: ${promptTokens}, completion: ${completionTokens})]`, (zhText("m376") + (total) + zhText("m377") + (promptTokens) + zhText("m378") + (completionTokens) + ")]"))));
|
|
195
196
|
const reasoning = usage.completion_tokens_details?.reasoning_tokens;
|
|
196
197
|
if (reasoning !== undefined)
|
|
197
|
-
console.log(ui(`Reasoning tokens (included in completion): ${reasoning}`,
|
|
198
|
+
console.log(ui(`Reasoning tokens (included in completion): ${reasoning}`, (zhText("m375") + (reasoning) + "")));
|
|
198
199
|
}
|
|
199
|
-
console.log(pc.gray(ui(`Idempotency key: ${res.idempotencyKey}`,
|
|
200
|
+
console.log(pc.gray(ui(`Idempotency key: ${res.idempotencyKey}`, (zhText("m374") + (res.idempotencyKey) + ""))));
|
|
200
201
|
});
|
|
201
202
|
});
|
|
202
203
|
cli
|
|
203
|
-
.command('mcp', ui('Start the LumenKey Model Context Protocol (MCP) server over stdio',
|
|
204
|
+
.command('mcp', ui('Start the LumenKey Model Context Protocol (MCP) server over stdio', zhText("m373")))
|
|
204
205
|
.action(async () => {
|
|
205
206
|
await runMcpStdio();
|
|
206
207
|
});
|
|
208
|
+
cli
|
|
209
|
+
.command('studio [...extra]', ui('Start the local Studio service', zhText("m372")))
|
|
210
|
+
.option('--output-dir <absolutePath>', ui('Absolute local directory for saved assets', zhText("m371")))
|
|
211
|
+
.option('--no-open', ui('Headless diagnostics; do not open a browser', zhText("m370")))
|
|
212
|
+
.action(async (extra, options) => {
|
|
213
|
+
if (extra.length)
|
|
214
|
+
throw new Error(ui('Studio does not accept positional arguments.', zhText("m369")));
|
|
215
|
+
applyRaw(options, 'outputDir', ['--output-dir']);
|
|
216
|
+
const { runStudioCommand } = await import('../studio/launch.js');
|
|
217
|
+
await runStudioCommand({ outputDir: options.outputDir, noOpen: options.open === false });
|
|
218
|
+
});
|
|
207
219
|
registerCopied(cli);
|
|
208
220
|
cli.help((sections) => {
|
|
209
221
|
if (resolveLang() !== 'zh')
|
|
210
222
|
return;
|
|
211
223
|
const titles = {
|
|
212
|
-
Usage:
|
|
213
|
-
Commands:
|
|
214
|
-
Options:
|
|
215
|
-
Examples:
|
|
216
|
-
'For more info, run any command with the `--help` flag':
|
|
224
|
+
Usage: zhText("m368"),
|
|
225
|
+
Commands: zhText("m367"),
|
|
226
|
+
Options: zhText("m366"),
|
|
227
|
+
Examples: zhText("m365"),
|
|
228
|
+
'For more info, run any command with the `--help` flag': zhText("m364"),
|
|
217
229
|
};
|
|
218
230
|
return sections.map((section) => ({
|
|
219
231
|
...section,
|
|
220
232
|
...(section.title === undefined ? {} : { title: titles[section.title] ?? section.title }),
|
|
221
233
|
body: section.body
|
|
222
|
-
.replaceAll('Display this message',
|
|
223
|
-
.replaceAll('Display version number',
|
|
224
|
-
.replace(/ \(default: ([^)\n]+)\)/g,
|
|
234
|
+
.replaceAll('Display this message', zhText("m363"))
|
|
235
|
+
.replaceAll('Display version number', zhText("m362"))
|
|
236
|
+
.replace(/ \(default: ([^)\n]+)\)/g, zhText("m361")),
|
|
225
237
|
}));
|
|
226
238
|
});
|
|
227
239
|
cli.version(packageVersion);
|
package/dist/cli/render.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { zhText } from '../core/chinese-messages.js';
|
|
1
2
|
import pc from 'picocolors';
|
|
2
3
|
import { formatJobDetail } from '../core/job-display.js';
|
|
3
4
|
import { ui } from '../core/locale.js';
|
|
@@ -29,7 +30,10 @@ export function renderOutcome(outcome, human, argv = process.argv) {
|
|
|
29
30
|
export function renderFailureHuman(failed) {
|
|
30
31
|
if (failed.job)
|
|
31
32
|
console.log(formatJobDetail(failed.job));
|
|
32
|
-
|
|
33
|
+
const line = failed.submission === 'unknown'
|
|
34
|
+
? `${failed.message} (${failed.code})${failed.httpStatus ? ` (HTTP ${failed.httpStatus})` : ''}${failed.idempotencyKey ? `\n${ui('Idempotency key', zhText("m408"))}: ${failed.idempotencyKey}` : ''}`
|
|
35
|
+
: failed.error;
|
|
36
|
+
console.error(pc.red(`${ui('Error: ', zhText("m407"))}${line}`));
|
|
33
37
|
markFailed();
|
|
34
38
|
}
|
|
35
39
|
//# sourceMappingURL=render.js.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { FileHandle } from 'node:fs/promises';
|
|
2
|
+
/** Streams into an already exclusively owned temporary file. Caller owns
|
|
3
|
+
* path authorization, publication, recovery ownership and closing the handle. */
|
|
4
|
+
export declare function streamAssetBodyToFile(body: ReadableStream<Uint8Array>, handle: FileHandle, options: {
|
|
5
|
+
limit: number;
|
|
6
|
+
signal?: AbortSignal;
|
|
7
|
+
expectedBytes?: number;
|
|
8
|
+
requireNonEmpty?: boolean;
|
|
9
|
+
onProgress?(bytes: number): void | Promise<void>;
|
|
10
|
+
onReadError?(): void;
|
|
11
|
+
}): Promise<{
|
|
12
|
+
byteLength: number;
|
|
13
|
+
prefix: Buffer;
|
|
14
|
+
sha256: string;
|
|
15
|
+
}>;
|
|
16
|
+
//# sourceMappingURL=asset-stream.d.ts.map
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { Readable, Writable } from 'node:stream';
|
|
3
|
+
import { pipeline } from 'node:stream/promises';
|
|
4
|
+
import { LumenKeyError } from './client.js';
|
|
5
|
+
/** Streams into an already exclusively owned temporary file. Caller owns
|
|
6
|
+
* path authorization, publication, recovery ownership and closing the handle. */
|
|
7
|
+
export async function streamAssetBodyToFile(body, handle, options) {
|
|
8
|
+
if (!Number.isSafeInteger(options.limit) || options.limit < 1 || (options.expectedBytes !== undefined && (!Number.isSafeInteger(options.expectedBytes) || options.expectedBytes < 0)))
|
|
9
|
+
throw new LumenKeyError('DOWNLOAD_LENGTH_MISMATCH');
|
|
10
|
+
if (options.expectedBytes !== undefined && options.expectedBytes > options.limit)
|
|
11
|
+
throw new LumenKeyError('DOWNLOAD_TOO_LARGE');
|
|
12
|
+
let byteLength = 0;
|
|
13
|
+
let prefix = Buffer.alloc(0);
|
|
14
|
+
const hash = createHash('sha256');
|
|
15
|
+
options.signal?.throwIfAborted();
|
|
16
|
+
const reader = body.getReader();
|
|
17
|
+
const abort = () => { void reader.cancel(options.signal?.reason).catch(() => { }); };
|
|
18
|
+
options.signal?.addEventListener('abort', abort, { once: true });
|
|
19
|
+
const source = Readable.from((async function* () {
|
|
20
|
+
for (;;) {
|
|
21
|
+
let part;
|
|
22
|
+
try {
|
|
23
|
+
part = await reader.read();
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
options.onReadError?.();
|
|
27
|
+
throw error;
|
|
28
|
+
}
|
|
29
|
+
if (part.done)
|
|
30
|
+
return;
|
|
31
|
+
yield part.value;
|
|
32
|
+
}
|
|
33
|
+
})());
|
|
34
|
+
const sink = new Writable({ write(chunk, _encoding, done) {
|
|
35
|
+
(async () => {
|
|
36
|
+
byteLength += chunk.byteLength;
|
|
37
|
+
if (!Number.isSafeInteger(byteLength) || byteLength > options.limit)
|
|
38
|
+
throw new LumenKeyError('DOWNLOAD_TOO_LARGE');
|
|
39
|
+
if (prefix.length < 4096)
|
|
40
|
+
prefix = Buffer.concat([prefix, chunk.subarray(0, 4096 - prefix.length)]);
|
|
41
|
+
hash.update(chunk);
|
|
42
|
+
await handle.writeFile(chunk);
|
|
43
|
+
await options.onProgress?.(byteLength);
|
|
44
|
+
})().then(() => done(), error => done(error));
|
|
45
|
+
} });
|
|
46
|
+
// A local write/progress failure can leave the async source waiting on its
|
|
47
|
+
// next web read. Cancel it here, not after pipeline (which waits for it).
|
|
48
|
+
sink.on('error', error => { void reader.cancel(error).catch(() => { }); });
|
|
49
|
+
try {
|
|
50
|
+
await pipeline(source, sink, { signal: options.signal });
|
|
51
|
+
}
|
|
52
|
+
finally {
|
|
53
|
+
options.signal?.removeEventListener('abort', abort);
|
|
54
|
+
await reader.cancel().catch(() => { });
|
|
55
|
+
reader.releaseLock();
|
|
56
|
+
}
|
|
57
|
+
if (options.expectedBytes !== undefined && byteLength !== options.expectedBytes)
|
|
58
|
+
throw new LumenKeyError('DOWNLOAD_LENGTH_MISMATCH');
|
|
59
|
+
if (options.requireNonEmpty && !byteLength)
|
|
60
|
+
throw new LumenKeyError('DOWNLOAD_EMPTY');
|
|
61
|
+
await handle.sync();
|
|
62
|
+
return { byteLength, prefix, sha256: hash.digest('hex') };
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=asset-stream.js.map
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { zhText } from '../core/chinese-messages.js';
|
|
1
2
|
import fs from 'node:fs';
|
|
2
3
|
import path from 'node:path';
|
|
3
4
|
import crypto from 'node:crypto';
|
|
@@ -39,20 +40,20 @@ export function assertSafeConfigFile(target) {
|
|
|
39
40
|
export function classifyConfigFailure(error) {
|
|
40
41
|
if (error instanceof UnsafeConfigPathError) {
|
|
41
42
|
if (error.kind === 'link')
|
|
42
|
-
return { code: 'CONFIG_UNSAFE_PATH', reason: ui('Symbolic links and hard links are not supported for configuration; use a regular private file and directory.',
|
|
43
|
+
return { code: 'CONFIG_UNSAFE_PATH', reason: ui('Symbolic links and hard links are not supported for configuration; use a regular private file and directory.', zhText("m6")) };
|
|
43
44
|
if (error.kind === 'not-regular-file')
|
|
44
|
-
return { code: 'CONFIG_NOT_A_FILE', reason: ui('The path exists but is not a regular file (a directory, FIFO, socket or device); replace it with a regular file.',
|
|
45
|
-
return { code: 'CONFIG_NOT_A_FILE', reason: ui('The configuration directory exists but is not a directory.',
|
|
45
|
+
return { code: 'CONFIG_NOT_A_FILE', reason: ui('The path exists but is not a regular file (a directory, FIFO, socket or device); replace it with a regular file.', zhText("m5")) };
|
|
46
|
+
return { code: 'CONFIG_NOT_A_FILE', reason: ui('The configuration directory exists but is not a directory.', zhText("m4")) };
|
|
46
47
|
}
|
|
47
48
|
// User-facing reasons name the condition, never the errno: scripts match the code, people read the sentence.
|
|
48
49
|
const sys = error;
|
|
49
50
|
if (sys?.code === 'ENOTDIR')
|
|
50
|
-
return { code: 'CONFIG_NOT_A_FILE', reason: ui('A component of the configuration path is not a directory.',
|
|
51
|
+
return { code: 'CONFIG_NOT_A_FILE', reason: ui('A component of the configuration path is not a directory.', zhText("m3")) };
|
|
51
52
|
if (sys?.code === 'EISDIR')
|
|
52
|
-
return { code: 'CONFIG_NOT_A_FILE', reason: ui('The path is a directory, not a regular file.',
|
|
53
|
+
return { code: 'CONFIG_NOT_A_FILE', reason: ui('The path is a directory, not a regular file.', zhText("m2")) };
|
|
53
54
|
if (sys?.code === 'EACCES' || sys?.code === 'EPERM')
|
|
54
|
-
return { code: 'CONFIG_UNREADABLE', reason: ui('Permission denied for the configuration path.',
|
|
55
|
-
return { code: 'CONFIG_INVALID', reason: ui('The config file could not be read.',
|
|
55
|
+
return { code: 'CONFIG_UNREADABLE', reason: ui('Permission denied for the configuration path.', zhText("m1")) };
|
|
56
|
+
return { code: 'CONFIG_INVALID', reason: ui('The config file could not be read.', zhText("m0")) };
|
|
56
57
|
}
|
|
57
58
|
/**
|
|
58
59
|
* Create the private configuration directory chain (0700) and tighten it if it already exists
|