@lumenkey/cli 0.3.1 → 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.
Files changed (182) hide show
  1. package/README.md +36 -2
  2. package/dist/cli/copied.js +86 -85
  3. package/dist/cli/index.js +56 -44
  4. package/dist/cli/render.js +3 -2
  5. package/dist/core/asset-stream.d.ts +16 -0
  6. package/dist/core/asset-stream.js +64 -0
  7. package/dist/core/atomic-config-file.js +8 -7
  8. package/dist/core/chinese-messages.d.ts +4 -0
  9. package/dist/core/chinese-messages.js +907 -0
  10. package/dist/core/chinese-script.d.ts +7 -0
  11. package/dist/core/chinese-script.js +28 -0
  12. package/dist/core/client.js +2 -1
  13. package/dist/core/config-dir.js +2 -1
  14. package/dist/core/config.js +7 -6
  15. package/dist/core/download-root.d.ts +5 -0
  16. package/dist/core/download-root.js +64 -0
  17. package/dist/core/download.js +15 -12
  18. package/dist/core/generation-input.js +3 -2
  19. package/dist/core/generation.d.ts +8 -1
  20. package/dist/core/generation.js +13 -10
  21. package/dist/core/job-display.js +7 -6
  22. package/dist/core/locale.js +163 -148
  23. package/dist/core/outcome.js +6 -4
  24. package/dist/core/output-path.d.ts +3 -1
  25. package/dist/core/output-path.js +5 -2
  26. package/dist/core/params.js +26 -25
  27. package/dist/core/profiles.js +5 -4
  28. package/dist/core/set-values.d.ts +0 -1
  29. package/dist/core/set-values.js +7 -7
  30. package/dist/core/submission-journal.js +31 -16
  31. package/dist/core/version.js +2 -3
  32. package/dist/mcp/server.js +30 -29
  33. package/dist/studio/asset-integrity-hash.d.ts +28 -0
  34. package/dist/studio/asset-integrity-hash.js +93 -0
  35. package/dist/studio/asset-integrity-queue.d.ts +24 -0
  36. package/dist/studio/asset-integrity-queue.js +94 -0
  37. package/dist/studio/asset-store.d.ts +52 -0
  38. package/dist/studio/asset-store.js +352 -0
  39. package/dist/studio/canvas/LICENSES.txt +1590 -0
  40. package/dist/studio/canvas/dependencies.json +862 -0
  41. package/dist/studio/canvas/director-source-manifest.json +47 -0
  42. package/dist/studio/canvas/editor.css +239 -0
  43. package/dist/studio/canvas/editor.js +4613 -0
  44. package/dist/studio/canvas/integration-source-manifest.json +384 -0
  45. package/dist/studio/canvas/source-manifest.json +240 -0
  46. package/dist/studio/canvas-store.d.ts +1254 -0
  47. package/dist/studio/canvas-store.js +205 -0
  48. package/dist/studio/capture-placement.d.ts +11 -0
  49. package/dist/studio/capture-placement.js +85 -0
  50. package/dist/studio/cloud-contract.d.ts +128 -0
  51. package/dist/studio/cloud-contract.js +229 -0
  52. package/dist/studio/cloud-service.d.ts +178 -0
  53. package/dist/studio/cloud-service.js +202 -0
  54. package/dist/studio/director-captures.d.ts +59 -0
  55. package/dist/studio/director-captures.js +141 -0
  56. package/dist/studio/director-contract.d.ts +1489 -0
  57. package/dist/studio/director-contract.js +288 -0
  58. package/dist/studio/director-png.d.ts +6 -0
  59. package/dist/studio/director-png.js +41 -0
  60. package/dist/studio/director-relations.d.ts +40 -0
  61. package/dist/studio/director-relations.js +103 -0
  62. package/dist/studio/document-authority.d.ts +7 -0
  63. package/dist/studio/document-authority.js +50 -0
  64. package/dist/studio/draft-store.d.ts +95 -0
  65. package/dist/studio/draft-store.js +100 -0
  66. package/dist/studio/errors.d.ts +85 -0
  67. package/dist/studio/errors.js +94 -0
  68. package/dist/studio/launch.d.ts +5 -0
  69. package/dist/studio/launch.js +31 -0
  70. package/dist/studio/media.d.ts +9 -0
  71. package/dist/studio/media.js +50 -0
  72. package/dist/studio/output-save-contract.d.ts +1321 -0
  73. package/dist/studio/output-save-contract.js +26 -0
  74. package/dist/studio/output-save-journal.d.ts +971 -0
  75. package/dist/studio/output-save-journal.js +287 -0
  76. package/dist/studio/output-save-recovery.d.ts +18 -0
  77. package/dist/studio/output-save-recovery.js +161 -0
  78. package/dist/studio/output-save-service.d.ts +436 -0
  79. package/dist/studio/output-save-service.js +251 -0
  80. package/dist/studio/output-save-worker.d.ts +40 -0
  81. package/dist/studio/output-save-worker.js +360 -0
  82. package/dist/studio/paths.d.ts +6 -0
  83. package/dist/studio/paths.js +68 -0
  84. package/dist/studio/run-journal.d.ts +993 -0
  85. package/dist/studio/run-journal.js +125 -0
  86. package/dist/studio/server.d.ts +17 -0
  87. package/dist/studio/server.js +440 -0
  88. package/dist/studio/session.d.ts +6 -0
  89. package/dist/studio/session.js +26 -0
  90. package/dist/studio/shell.d.ts +3 -0
  91. package/dist/studio/shell.js +23 -0
  92. package/dist/studio/style.d.ts +2 -0
  93. package/dist/studio/style.js +29 -0
  94. package/dist/studio/types.d.ts +26 -0
  95. package/dist/studio/types.js +2 -0
  96. package/dist/studio/web/app.d.ts +2 -0
  97. package/dist/studio/web/app.js +662 -0
  98. package/dist/studio/web/asset-controller.d.ts +37 -0
  99. package/dist/studio/web/asset-controller.js +191 -0
  100. package/dist/studio/web/asset-model.d.ts +29 -0
  101. package/dist/studio/web/asset-model.js +51 -0
  102. package/dist/studio/web/controller.d.ts +113 -0
  103. package/dist/studio/web/controller.js +401 -0
  104. package/dist/studio/web/messages.d.ts +2 -0
  105. package/dist/studio/web/messages.js +74 -0
  106. package/dist/studio/web/model.d.ts +75 -0
  107. package/dist/studio/web/model.js +182 -0
  108. package/dist/studio/web/save-controller.d.ts +85 -0
  109. package/dist/studio/web/save-controller.js +495 -0
  110. package/dist/studio/web/save-model.d.ts +63 -0
  111. package/dist/studio/web/save-model.js +64 -0
  112. package/dist/studio/web/save-queue.d.ts +7 -0
  113. package/dist/studio/web/save-queue.js +62 -0
  114. package/dist/studio/web/save-view.d.ts +9 -0
  115. package/dist/studio/web/save-view.js +19 -0
  116. package/dist/studio/writer-lock.d.ts +5 -0
  117. package/dist/studio/writer-lock.js +88 -0
  118. package/package.json +11 -5
  119. package/dist/cli/copied.d.ts.map +0 -1
  120. package/dist/cli/copied.js.map +0 -1
  121. package/dist/cli/index.d.ts.map +0 -1
  122. package/dist/cli/index.js.map +0 -1
  123. package/dist/cli/render.d.ts.map +0 -1
  124. package/dist/cli/render.js.map +0 -1
  125. package/dist/core/atomic-config-file.d.ts.map +0 -1
  126. package/dist/core/atomic-config-file.js.map +0 -1
  127. package/dist/core/chat.d.ts.map +0 -1
  128. package/dist/core/chat.js.map +0 -1
  129. package/dist/core/client.d.ts.map +0 -1
  130. package/dist/core/client.js.map +0 -1
  131. package/dist/core/config-dir.d.ts.map +0 -1
  132. package/dist/core/config-dir.js.map +0 -1
  133. package/dist/core/config-error.d.ts.map +0 -1
  134. package/dist/core/config-error.js.map +0 -1
  135. package/dist/core/config.d.ts.map +0 -1
  136. package/dist/core/config.js.map +0 -1
  137. package/dist/core/credit-cap.d.ts.map +0 -1
  138. package/dist/core/credit-cap.js.map +0 -1
  139. package/dist/core/download.d.ts.map +0 -1
  140. package/dist/core/download.js.map +0 -1
  141. package/dist/core/error-result.d.ts.map +0 -1
  142. package/dist/core/error-result.js.map +0 -1
  143. package/dist/core/explain.d.ts.map +0 -1
  144. package/dist/core/explain.js.map +0 -1
  145. package/dist/core/generation-input.d.ts.map +0 -1
  146. package/dist/core/generation-input.js.map +0 -1
  147. package/dist/core/generation-result.d.ts.map +0 -1
  148. package/dist/core/generation-result.js.map +0 -1
  149. package/dist/core/generation.d.ts.map +0 -1
  150. package/dist/core/generation.js.map +0 -1
  151. package/dist/core/job-display.d.ts.map +0 -1
  152. package/dist/core/job-display.js.map +0 -1
  153. package/dist/core/locale.d.ts.map +0 -1
  154. package/dist/core/locale.js.map +0 -1
  155. package/dist/core/origin.d.ts.map +0 -1
  156. package/dist/core/origin.js.map +0 -1
  157. package/dist/core/outcome.d.ts.map +0 -1
  158. package/dist/core/outcome.js.map +0 -1
  159. package/dist/core/output-path.d.ts.map +0 -1
  160. package/dist/core/output-path.js.map +0 -1
  161. package/dist/core/params.d.ts.map +0 -1
  162. package/dist/core/params.js.map +0 -1
  163. package/dist/core/poller.d.ts.map +0 -1
  164. package/dist/core/poller.js.map +0 -1
  165. package/dist/core/profiles.d.ts.map +0 -1
  166. package/dist/core/profiles.js.map +0 -1
  167. package/dist/core/set-values.d.ts.map +0 -1
  168. package/dist/core/set-values.js.map +0 -1
  169. package/dist/core/submission-journal.d.ts.map +0 -1
  170. package/dist/core/submission-journal.js.map +0 -1
  171. package/dist/core/types.d.ts.map +0 -1
  172. package/dist/core/types.js.map +0 -1
  173. package/dist/core/version.d.ts.map +0 -1
  174. package/dist/core/version.js.map +0 -1
  175. package/dist/core/wait.d.ts.map +0 -1
  176. package/dist/core/wait.js.map +0 -1
  177. package/dist/index.d.ts.map +0 -1
  178. package/dist/index.js.map +0 -1
  179. package/dist/mcp/render.d.ts.map +0 -1
  180. package/dist/mcp/render.js.map +0 -1
  181. package/dist/mcp/server.d.ts.map +0 -1
  182. 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]', '用法:lumenkey <命令> [选项]'),
44
- ui('Run lumenkey --help to see commands.', '运行 lumenkey --help 查看命令。'),
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)}.`, `未知选项 ${JSON.stringify(unknown)}。`), ...usage]
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.', '这不是 lumenkey 命令。')
56
- : ui(`'${shown}' is not a lumenkey command.`, `'${shown}' 不是 lumenkey 命令。`);
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])}.`, `未知选项 ${JSON.stringify(original.split('=')[0])}。`) : err.message), { confirm: argv.includes('--confirm') });
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, `命令 \`${missingArgs[1]}\` 缺少必填参数。`));
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', '界面语言:zh 或 en'));
93
+ cli.option('--lang <lang>', ui('Message language: zh or en', zhText("m400")));
93
94
  cli
94
- .command('login', ui('Configure LumenKey API Key', '配置 LumenKey 密钥'))
95
- .option('--key <apiKey>', ui('API Key to configure (or leave empty to prompt via env)', '要保存的密钥;留空则改用环境变量'))
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.', '这个密钥是用 --key 传入的,可能留在 shell 历史里。请改用 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', '✔ 密钥已保存到 ~/.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', '按类型筛选: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:', '\n可用模型:')));
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}`, `${p.credits} 算力点/${p.unit}`)).join(', ') || '';
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.)', '生成视频')), videoParams)
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>.`, `视频命令多了参数 ${JSON.stringify(extra[0])}。请使用:lumenkey video -m <模型ID> -p <提示词>。`));
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:', '\nLumenKey 账户余额:')));
170
- console.log(ui(` Available Credits: ${pc.green(pc.bold(String(balance.availableCredits ?? 0)))}`, ` 可用算力点:${pc.green(pc.bold(String(balance.availableCredits ?? 0)))}`));
171
- console.log(ui(` Reserved Credits: ${pc.yellow(String(balance.reservedCredits ?? 0))}`, ` 冻结算力点:${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)}`, ` 更新时间:${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', '发起计费对话')), chatParams)
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:', '\n回复:')));
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})]`, `\n[词元:${total}(输入:${promptTokens},输出:${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}`, `推理词元(已计入输出):${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}`, `幂等键:${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', '通过标准输入输出启动 LumenKey MCP 服务'))
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': '查看某个命令的说明,请运行该命令并加上 --help',
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, ' (默认:$1)'),
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);
@@ -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';
@@ -30,9 +31,9 @@ export function renderFailureHuman(failed) {
30
31
  if (failed.job)
31
32
  console.log(formatJobDetail(failed.job));
32
33
  const line = failed.submission === 'unknown'
33
- ? `${failed.message} (${failed.code})${failed.httpStatus ? ` (HTTP ${failed.httpStatus})` : ''}${failed.idempotencyKey ? `\n${ui('Idempotency key', '幂等键')}: ${failed.idempotencyKey}` : ''}`
34
+ ? `${failed.message} (${failed.code})${failed.httpStatus ? ` (HTTP ${failed.httpStatus})` : ''}${failed.idempotencyKey ? `\n${ui('Idempotency key', zhText("m408"))}: ${failed.idempotencyKey}` : ''}`
34
35
  : failed.error;
35
- console.error(pc.red(`${ui('Error: ', '错误:')}${line}`));
36
+ console.error(pc.red(`${ui('Error: ', zhText("m407"))}${line}`));
36
37
  markFailed();
37
38
  }
38
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.', '该路径存在,但不是普通文件(目录、FIFO、套接字或设备);请换成普通文件。') };
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
@@ -0,0 +1,4 @@
1
+ import { type ChineseScript } from './chinese-script.js';
2
+ export declare const CHINESE_MESSAGES: Record<ChineseScript, Record<string, string>>;
3
+ export declare function zhText(id: string, script?: ChineseScript): string;
4
+ //# sourceMappingURL=chinese-messages.d.ts.map