@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
@@ -0,0 +1,100 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { link, open, readdir, rename, unlink } from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { z } from 'zod';
5
+ import { assertRegularFile, assertStudioRoot } from './paths.js';
6
+ import { hasFsCode, StudioError } from './errors.js';
7
+ import { isSafeJson, isoDate } from './run-journal.js';
8
+ import { prepareDocumentAuthority } from './document-authority.js';
9
+ const MAX_BYTES = 512 * 1024;
10
+ const revision = z.number().int().nonnegative().safe();
11
+ const fields = z.unknown().refine(v => isSafeJson(v) && !!v && typeof v === 'object' && !Array.isArray(v) && Buffer.byteLength(JSON.stringify(v)) <= 128 * 1024).pipe(z.record(z.string()));
12
+ const draft = z.object({ modelId: z.string().max(200), fields, savedAt: isoDate }).strict();
13
+ const document = z.object({ schemaVersion: z.literal(1), revision, drafts: z.object({ image: draft.optional(), video: draft.optional() }).strict() }).strict();
14
+ const request = z.object({ revision, mode: z.enum(['image', 'video']), modelId: z.string().max(200), fields }).strict();
15
+ /** Uses the asset store's exclusive directory writer and lifecycle, not a second lock. */
16
+ export function createDraftStore(root, serial) {
17
+ const file = path.join(root.managedDir, 'drafts.json');
18
+ let current;
19
+ async function disk() {
20
+ await assertStudioRoot(root);
21
+ try {
22
+ await assertRegularFile(file);
23
+ const handle = await open(file, 'r');
24
+ try {
25
+ if ((await handle.stat()).size > MAX_BYTES)
26
+ throw new Error();
27
+ return document.parse(JSON.parse(await handle.readFile('utf8')));
28
+ }
29
+ finally {
30
+ await handle.close();
31
+ }
32
+ }
33
+ catch (error) {
34
+ if (hasFsCode(error, 'ENOENT'))
35
+ return undefined;
36
+ throw new StudioError('STUDIO_DRAFT_INVALID', 409);
37
+ }
38
+ }
39
+ async function commit(next, initial = false) {
40
+ await assertStudioRoot(root);
41
+ const text = JSON.stringify(document.parse(next));
42
+ if (Buffer.byteLength(text) > MAX_BYTES)
43
+ throw new StudioError('STUDIO_DRAFT_INVALID', 409);
44
+ const temp = path.join(root.managedDir, `drafts.${randomUUID()}.tmp`);
45
+ const handle = await open(temp, 'wx', 0o600);
46
+ try {
47
+ await handle.writeFile(text);
48
+ await handle.sync();
49
+ }
50
+ finally {
51
+ await handle.close();
52
+ }
53
+ await assertStudioRoot(root);
54
+ if (initial) {
55
+ await link(temp, file);
56
+ await unlink(temp);
57
+ }
58
+ else {
59
+ await assertRegularFile(file);
60
+ await rename(temp, file);
61
+ }
62
+ }
63
+ async function authority() {
64
+ const loaded = await disk();
65
+ if (loaded)
66
+ await prepareDocumentAuthority(root, 'drafts', true, 'STUDIO_DRAFT_INVALID');
67
+ if (current) {
68
+ if (!loaded || JSON.stringify(loaded) !== JSON.stringify(current))
69
+ throw new StudioError('STUDIO_DRAFT_INVALID', 409);
70
+ }
71
+ else if (loaded)
72
+ current = loaded;
73
+ else {
74
+ if ((await readdir(root.managedDir)).some(n => /^drafts\..+\.tmp$/.test(n)))
75
+ throw new StudioError('STUDIO_DRAFT_INVALID', 409);
76
+ await prepareDocumentAuthority(root, 'drafts', false, 'STUDIO_DRAFT_INVALID');
77
+ const empty = { schemaVersion: 1, revision: 0, drafts: {} };
78
+ await commit(empty, true);
79
+ current = empty;
80
+ }
81
+ return current;
82
+ }
83
+ return {
84
+ read: () => serial(async () => structuredClone(await authority())),
85
+ save: value => serial(async () => {
86
+ const parsed = request.safeParse(value);
87
+ if (!parsed.success)
88
+ throw new StudioError('STUDIO_INPUT_INVALID');
89
+ const input = parsed.data;
90
+ const before = await authority();
91
+ if (input.revision !== before.revision)
92
+ throw new StudioError('STUDIO_DRAFT_CONFLICT', 409);
93
+ const next = document.parse({ ...before, revision: before.revision + 1, drafts: { ...before.drafts, [input.mode]: { modelId: input.modelId, fields: input.fields, savedAt: new Date().toISOString() } } });
94
+ await commit(next);
95
+ current = next;
96
+ return structuredClone(next);
97
+ }),
98
+ };
99
+ }
100
+ //# sourceMappingURL=draft-store.js.map
@@ -0,0 +1,85 @@
1
+ declare const messages: {
2
+ readonly SAVE_REGISTRATION_PENDING: "完整文件待登记到本机素材库。 / Local asset registration pending.";
3
+ readonly SAVE_RELATION_PENDING: "本机素材已登记,输出关联待恢复。 / Local output relation pending.";
4
+ readonly SAVE_FILE_MISSING: "本机素材文件缺失。 / Local asset file missing.";
5
+ readonly SAVE_FILE_MODIFIED: "本机素材文件已变更。 / Local asset file modified.";
6
+ readonly SAVE_FILE_UNSAFE: "本机素材文件不安全,已保留。 / Unsafe local asset file preserved.";
7
+ readonly SAVE_FILE_READ_FAILED: "暂时无法核对本机素材。 / Local asset check failed.";
8
+ readonly SAVE_TARGET_CONFLICT: "目标文件归属无法确认,已保留。 / Save target conflict.";
9
+ readonly SAVE_PATH_UNSAFE: "保存路径不安全。 / Unsafe save path.";
10
+ readonly SAVE_WRITE_FAILED: "保存写入失败,未确认成功。 / Save write failed.";
11
+ readonly SAVE_NETWORK_ERROR: "保存下载暂时失败。 / Save network error.";
12
+ readonly SAVE_DOWNLOAD_TIMEOUT: "保存下载超时。 / Save download timeout.";
13
+ readonly SAVE_MEDIA_TOO_LARGE: "输出超过保存上限。 / Save media exceeds limit.";
14
+ readonly SAVE_LENGTH_MISMATCH: "下载长度不匹配。 / Save length mismatch.";
15
+ readonly SAVE_RETRY_NOT_ALLOWED: "当前保存操作不能安全重试。 / Save retry is not allowed.";
16
+ readonly SAVE_OUTPUT_IDENTITY_UNVERIFIED: "输出身份尚未核实。 / Output identity unverified.";
17
+ readonly SAVE_OUTPUT_IDENTITY_CONFLICT: "输出身份存在冲突。 / Output identity conflict.";
18
+ readonly SAVE_JOB_NOT_TERMINAL: "云任务尚未结束。 / Job is not terminal.";
19
+ readonly SAVE_MEDIA_UNSUPPORTED: "该输出格式不支持保存。 / Unsupported save media.";
20
+ readonly SAVE_SOURCE_UNAVAILABLE: "输出来源当前不可用。 / Save source unavailable.";
21
+ readonly SAVE_REQUEST_CONFLICT: "该保存请求标识已用于其他输出。 / Save request identity conflict.";
22
+ readonly SAVE_REVISION_CONFLICT: "保存记录已更新,请重新核对。 / Save revision conflict.";
23
+ readonly SAVE_TRANSITION_INVALID: "保存状态变更无效,未写入。 / Invalid save transition.";
24
+ readonly SAVE_OPERATION_NOT_FOUND: "未找到保存操作;这不证明从未受理。 / Save operation not found.";
25
+ readonly SAVE_CLOUD_CONFIG_REQUIRED: "请先配置本机云端连接。 / Cloud configuration required.";
26
+ readonly SAVE_OUTPUT_ROOT_REQUIRED: "请先指定本机保存目录。 / Save output root required.";
27
+ readonly SAVE_ROOT_NOT_ALLOWED: "本机保存目录未获准。 / Save root is not allowed.";
28
+ readonly SAVE_BINDING_REQUIRED: "请先绑定本机保存连接。 / Save binding required.";
29
+ readonly SAVE_BINDING_MISMATCH: "记录不属于当前保存连接。 / Save binding mismatch.";
30
+ readonly SAVE_AUTHORITY_INVALID: "本机保存记录不可核对;不会重建空记录。 / Save authority invalid.";
31
+ readonly SAVE_AUTHORITY_UNAVAILABLE: "暂时无法核对本机保存记录。 / Save authority unavailable.";
32
+ readonly STUDIO_DIRECTOR_INVALID: "3D 场景数据无效或超出容量;当前编辑不会被替换。 / Invalid director scene.";
33
+ readonly STUDIO_CAPTURE_PNG_INVALID: "镜头截图结构或尺寸无效,未登记素材。 / Invalid capture PNG structure or dimensions.";
34
+ readonly STUDIO_CAPTURE_ID_CONFLICT: "该截图标识已用于不同内容。 / Capture identity conflict.";
35
+ readonly STUDIO_CAPTURE_BINDING_CONFLICT: "截图回填条件已变化;素材保留,不会恢复被删除的节点或关系。 / Capture binding conflict.";
36
+ readonly STUDIO_CAPTURE_CAPACITY_EXCEEDED: "截图或画布已达容量上限。 / Capture or canvas capacity exceeded.";
37
+ readonly STUDIO_CAPTURE_NOT_FOUND: "未找到该画布的截图记录;这不代表磁盘从未写入文件。 / Capture record not found.";
38
+ readonly STUDIO_CAPTURE_ASSET_UNAVAILABLE: "截图文件缺失或校验失败,不能自动回填。 / Capture asset unavailable.";
39
+ readonly STUDIO_CAPTURE_OUTCOME_UNKNOWN: "截图写入结果无法确认;请核对本机素材与恢复记录,勿重复导出。 / Capture outcome unknown.";
40
+ readonly STUDIO_CANVAS_INVALID: "画布文件损坏、丢失或已被外部修改;不会覆盖原文件。 / Canvas authority is invalid or changed.";
41
+ readonly STUDIO_CANVAS_CONFLICT: "画布已在另一窗口更新;请保留当前编辑并重新读取。 / Canvas revision conflict.";
42
+ readonly STUDIO_CANVAS_CAPACITY_EXCEEDED: "本机画布文件已达容量上限,未写入本次修改。 / Canvas collection capacity exceeded; write rejected.";
43
+ readonly STUDIO_CANVAS_NOT_FOUND: "未找到本机画布。 / Local canvas not found.";
44
+ readonly STUDIO_DRAFT_INVALID: "草稿文件损坏或已被外部修改;不会覆盖原文件。 / Draft authority is invalid or changed.";
45
+ readonly STUDIO_DRAFT_CONFLICT: "草稿已在另一窗口更新;请保留当前编辑并重新读取。 / Draft revision conflict.";
46
+ readonly STUDIO_JOURNAL_INVALID: "任务记录损坏、冲突或超出容量;未提交新任务。 / Invalid or conflicting run journal.";
47
+ readonly STUDIO_CLOUD_DISCONNECTED: "尚未连接云端;本机会话不代表云端已连接。 / Cloud is not connected.";
48
+ readonly STUDIO_CLOUD_CONFIG_INVALID: "本机 CLI 云端配置未就绪,请先配置 CLI。 / CLI cloud configuration is not ready.";
49
+ readonly STUDIO_CLOUD_RESPONSE_INVALID: "云端响应不符合契约,已停止当前操作。 / Invalid cloud response.";
50
+ readonly STUDIO_CLOUD_FAILED: "云端请求失败。 / Cloud request failed.";
51
+ readonly STUDIO_RUN_CONFLICT: "该任务标识已用于不同请求。 / Run identity conflict.";
52
+ readonly STUDIO_RUN_NOT_FOUND: "当前连接下未找到该任务。 / Run not found for this connection.";
53
+ readonly STUDIO_MODEL_NOT_AVAILABLE: "目录中没有该可用图像或视频模型。 / Media model unavailable in catalog.";
54
+ readonly STUDIO_CONFIRM_REQUIRED: "需要明确确认本次报价才能提交。 / Explicit quote confirmation required.";
55
+ readonly STUDIO_QUOTE_EXPIRED: "报价已过期,请重新报价并确认。 / Quote expired; obtain and confirm a new quote.";
56
+ readonly STUDIO_SUBMISSION_UNRESOLVED: "此前提交结果待确认,已阻止重复下单;请核对平台记录。 / Submission unresolved; repeat submission blocked.";
57
+ readonly STUDIO_MEDIA_TYPE_INVALID: "媒体类型或文件头不受支持。 / Unsupported media type or signature.";
58
+ readonly STUDIO_CATALOG_INVALID: "素材清单损坏或版本不受支持;原文件已保留。 / Invalid or unsupported catalog; files preserved.";
59
+ readonly STUDIO_ASSET_ID_INVALID: "素材 ID 无效。 / Invalid asset ID.";
60
+ readonly STUDIO_OUTPUT_ROOT_REQUIRED: "请先指定绝对保存目录。 / An absolute output directory is required.";
61
+ readonly STUDIO_PATH_UNSAFE: "保存路径包含链接或已发生变化。 / Unsafe or changed storage path.";
62
+ readonly STUDIO_STORAGE_BUSY: "该目录正在使用或无法确认旧写手已退出。 / Storage is busy or ownership cannot be established.";
63
+ readonly STUDIO_STORAGE_FAILED: "本机文件操作失败;未确认保存成功。 / Local storage operation failed.";
64
+ readonly STUDIO_STORAGE_CLOSED: "本机存储已关闭。 / Storage is closed.";
65
+ readonly STUDIO_MEDIA_TOO_LARGE: "素材超过保存上限。 / Media exceeds the size limit.";
66
+ readonly STUDIO_INPUT_INVALID: "输入参数无效或数据不完整。 / Invalid or incomplete input.";
67
+ readonly STUDIO_ASSET_NOT_FOUND: "未找到该素材。 / Asset not found.";
68
+ readonly STUDIO_ASSET_CORRUPT: "素材缺失或校验失败。 / Asset missing or integrity check failed.";
69
+ readonly STUDIO_SESSION_REQUIRED: "请从本次启动的浏览器窗口访问。 / Open the browser launched by this runtime.";
70
+ readonly STUDIO_REQUEST_FORBIDDEN: "请求来源不被允许。 / Request origin is not allowed.";
71
+ readonly STUDIO_ROUTE_NOT_FOUND: "接口不存在。 / Route not found.";
72
+ readonly STUDIO_RANGE_INVALID: "读取范围无效。 / Invalid byte range.";
73
+ readonly STUDIO_BROWSER_OPEN_FAILED: "无法打开浏览器;请确认系统默认浏览器后重试。 / Could not open the default browser.";
74
+ };
75
+ export type StudioErrorCode = keyof typeof messages;
76
+ export declare class StudioError extends Error {
77
+ readonly code: StudioErrorCode;
78
+ readonly status: number;
79
+ constructor(code: StudioErrorCode, status?: number);
80
+ }
81
+ export declare function storageError(error: unknown): StudioError;
82
+ export declare function hasFsCode(error: unknown, code: string): boolean;
83
+ export declare function isSaveAuthorityError(error: unknown): error is StudioError;
84
+ export {};
85
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1,94 @@
1
+ const messages = {
2
+ SAVE_REGISTRATION_PENDING: '完整文件待登记到本机素材库。 / Local asset registration pending.',
3
+ SAVE_RELATION_PENDING: '本机素材已登记,输出关联待恢复。 / Local output relation pending.',
4
+ SAVE_FILE_MISSING: '本机素材文件缺失。 / Local asset file missing.',
5
+ SAVE_FILE_MODIFIED: '本机素材文件已变更。 / Local asset file modified.',
6
+ SAVE_FILE_UNSAFE: '本机素材文件不安全,已保留。 / Unsafe local asset file preserved.',
7
+ SAVE_FILE_READ_FAILED: '暂时无法核对本机素材。 / Local asset check failed.',
8
+ SAVE_TARGET_CONFLICT: '目标文件归属无法确认,已保留。 / Save target conflict.',
9
+ SAVE_PATH_UNSAFE: '保存路径不安全。 / Unsafe save path.',
10
+ SAVE_WRITE_FAILED: '保存写入失败,未确认成功。 / Save write failed.',
11
+ SAVE_NETWORK_ERROR: '保存下载暂时失败。 / Save network error.',
12
+ SAVE_DOWNLOAD_TIMEOUT: '保存下载超时。 / Save download timeout.',
13
+ SAVE_MEDIA_TOO_LARGE: '输出超过保存上限。 / Save media exceeds limit.',
14
+ SAVE_LENGTH_MISMATCH: '下载长度不匹配。 / Save length mismatch.',
15
+ SAVE_RETRY_NOT_ALLOWED: '当前保存操作不能安全重试。 / Save retry is not allowed.',
16
+ SAVE_OUTPUT_IDENTITY_UNVERIFIED: '输出身份尚未核实。 / Output identity unverified.',
17
+ SAVE_OUTPUT_IDENTITY_CONFLICT: '输出身份存在冲突。 / Output identity conflict.',
18
+ SAVE_JOB_NOT_TERMINAL: '云任务尚未结束。 / Job is not terminal.',
19
+ SAVE_MEDIA_UNSUPPORTED: '该输出格式不支持保存。 / Unsupported save media.',
20
+ SAVE_SOURCE_UNAVAILABLE: '输出来源当前不可用。 / Save source unavailable.',
21
+ SAVE_REQUEST_CONFLICT: '该保存请求标识已用于其他输出。 / Save request identity conflict.',
22
+ SAVE_REVISION_CONFLICT: '保存记录已更新,请重新核对。 / Save revision conflict.',
23
+ SAVE_TRANSITION_INVALID: '保存状态变更无效,未写入。 / Invalid save transition.',
24
+ SAVE_OPERATION_NOT_FOUND: '未找到保存操作;这不证明从未受理。 / Save operation not found.',
25
+ SAVE_CLOUD_CONFIG_REQUIRED: '请先配置本机云端连接。 / Cloud configuration required.',
26
+ SAVE_OUTPUT_ROOT_REQUIRED: '请先指定本机保存目录。 / Save output root required.',
27
+ SAVE_ROOT_NOT_ALLOWED: '本机保存目录未获准。 / Save root is not allowed.',
28
+ SAVE_BINDING_REQUIRED: '请先绑定本机保存连接。 / Save binding required.',
29
+ SAVE_BINDING_MISMATCH: '记录不属于当前保存连接。 / Save binding mismatch.',
30
+ SAVE_AUTHORITY_INVALID: '本机保存记录不可核对;不会重建空记录。 / Save authority invalid.',
31
+ SAVE_AUTHORITY_UNAVAILABLE: '暂时无法核对本机保存记录。 / Save authority unavailable.',
32
+ STUDIO_DIRECTOR_INVALID: '3D 场景数据无效或超出容量;当前编辑不会被替换。 / Invalid director scene.',
33
+ STUDIO_CAPTURE_PNG_INVALID: '镜头截图结构或尺寸无效,未登记素材。 / Invalid capture PNG structure or dimensions.',
34
+ STUDIO_CAPTURE_ID_CONFLICT: '该截图标识已用于不同内容。 / Capture identity conflict.',
35
+ STUDIO_CAPTURE_BINDING_CONFLICT: '截图回填条件已变化;素材保留,不会恢复被删除的节点或关系。 / Capture binding conflict.',
36
+ STUDIO_CAPTURE_CAPACITY_EXCEEDED: '截图或画布已达容量上限。 / Capture or canvas capacity exceeded.',
37
+ STUDIO_CAPTURE_NOT_FOUND: '未找到该画布的截图记录;这不代表磁盘从未写入文件。 / Capture record not found.',
38
+ STUDIO_CAPTURE_ASSET_UNAVAILABLE: '截图文件缺失或校验失败,不能自动回填。 / Capture asset unavailable.',
39
+ STUDIO_CAPTURE_OUTCOME_UNKNOWN: '截图写入结果无法确认;请核对本机素材与恢复记录,勿重复导出。 / Capture outcome unknown.',
40
+ STUDIO_CANVAS_INVALID: '画布文件损坏、丢失或已被外部修改;不会覆盖原文件。 / Canvas authority is invalid or changed.',
41
+ STUDIO_CANVAS_CONFLICT: '画布已在另一窗口更新;请保留当前编辑并重新读取。 / Canvas revision conflict.',
42
+ STUDIO_CANVAS_CAPACITY_EXCEEDED: '本机画布文件已达容量上限,未写入本次修改。 / Canvas collection capacity exceeded; write rejected.',
43
+ STUDIO_CANVAS_NOT_FOUND: '未找到本机画布。 / Local canvas not found.',
44
+ STUDIO_DRAFT_INVALID: '草稿文件损坏或已被外部修改;不会覆盖原文件。 / Draft authority is invalid or changed.',
45
+ STUDIO_DRAFT_CONFLICT: '草稿已在另一窗口更新;请保留当前编辑并重新读取。 / Draft revision conflict.',
46
+ STUDIO_JOURNAL_INVALID: '任务记录损坏、冲突或超出容量;未提交新任务。 / Invalid or conflicting run journal.',
47
+ STUDIO_CLOUD_DISCONNECTED: '尚未连接云端;本机会话不代表云端已连接。 / Cloud is not connected.',
48
+ STUDIO_CLOUD_CONFIG_INVALID: '本机 CLI 云端配置未就绪,请先配置 CLI。 / CLI cloud configuration is not ready.',
49
+ STUDIO_CLOUD_RESPONSE_INVALID: '云端响应不符合契约,已停止当前操作。 / Invalid cloud response.',
50
+ STUDIO_CLOUD_FAILED: '云端请求失败。 / Cloud request failed.',
51
+ STUDIO_RUN_CONFLICT: '该任务标识已用于不同请求。 / Run identity conflict.',
52
+ STUDIO_RUN_NOT_FOUND: '当前连接下未找到该任务。 / Run not found for this connection.',
53
+ STUDIO_MODEL_NOT_AVAILABLE: '目录中没有该可用图像或视频模型。 / Media model unavailable in catalog.',
54
+ STUDIO_CONFIRM_REQUIRED: '需要明确确认本次报价才能提交。 / Explicit quote confirmation required.',
55
+ STUDIO_QUOTE_EXPIRED: '报价已过期,请重新报价并确认。 / Quote expired; obtain and confirm a new quote.',
56
+ STUDIO_SUBMISSION_UNRESOLVED: '此前提交结果待确认,已阻止重复下单;请核对平台记录。 / Submission unresolved; repeat submission blocked.',
57
+ STUDIO_MEDIA_TYPE_INVALID: '媒体类型或文件头不受支持。 / Unsupported media type or signature.',
58
+ STUDIO_CATALOG_INVALID: '素材清单损坏或版本不受支持;原文件已保留。 / Invalid or unsupported catalog; files preserved.',
59
+ STUDIO_ASSET_ID_INVALID: '素材 ID 无效。 / Invalid asset ID.',
60
+ STUDIO_OUTPUT_ROOT_REQUIRED: '请先指定绝对保存目录。 / An absolute output directory is required.',
61
+ STUDIO_PATH_UNSAFE: '保存路径包含链接或已发生变化。 / Unsafe or changed storage path.',
62
+ STUDIO_STORAGE_BUSY: '该目录正在使用或无法确认旧写手已退出。 / Storage is busy or ownership cannot be established.',
63
+ STUDIO_STORAGE_FAILED: '本机文件操作失败;未确认保存成功。 / Local storage operation failed.',
64
+ STUDIO_STORAGE_CLOSED: '本机存储已关闭。 / Storage is closed.',
65
+ STUDIO_MEDIA_TOO_LARGE: '素材超过保存上限。 / Media exceeds the size limit.',
66
+ STUDIO_INPUT_INVALID: '输入参数无效或数据不完整。 / Invalid or incomplete input.',
67
+ STUDIO_ASSET_NOT_FOUND: '未找到该素材。 / Asset not found.',
68
+ STUDIO_ASSET_CORRUPT: '素材缺失或校验失败。 / Asset missing or integrity check failed.',
69
+ STUDIO_SESSION_REQUIRED: '请从本次启动的浏览器窗口访问。 / Open the browser launched by this runtime.',
70
+ STUDIO_REQUEST_FORBIDDEN: '请求来源不被允许。 / Request origin is not allowed.',
71
+ STUDIO_ROUTE_NOT_FOUND: '接口不存在。 / Route not found.',
72
+ STUDIO_RANGE_INVALID: '读取范围无效。 / Invalid byte range.',
73
+ STUDIO_BROWSER_OPEN_FAILED: '无法打开浏览器;请确认系统默认浏览器后重试。 / Could not open the default browser.',
74
+ };
75
+ export class StudioError extends Error {
76
+ code;
77
+ status;
78
+ constructor(code, status = 400) {
79
+ super(`${messages[code]} (${code})`);
80
+ this.code = code;
81
+ this.status = status;
82
+ this.name = 'StudioError';
83
+ }
84
+ }
85
+ export function storageError(error) {
86
+ return error instanceof StudioError ? error : new StudioError('STUDIO_STORAGE_FAILED', 500);
87
+ }
88
+ export function hasFsCode(error, code) {
89
+ return typeof error === 'object' && error !== null && 'code' in error && error.code === code;
90
+ }
91
+ export function isSaveAuthorityError(error) {
92
+ return error instanceof StudioError && ['SAVE_AUTHORITY_INVALID', 'SAVE_AUTHORITY_UNAVAILABLE'].includes(error.code);
93
+ }
94
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1,5 @@
1
+ export declare function runStudioCommand(options: {
2
+ outputDir?: string;
3
+ noOpen?: boolean;
4
+ }): Promise<void>;
5
+ //# sourceMappingURL=launch.d.ts.map
@@ -0,0 +1,31 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { startStudioServer } from './server.js';
3
+ import { StudioError } from './errors.js';
4
+ import { ui } from '../core/locale.js';
5
+ async function openBrowser(url) {
6
+ const command = process.platform === 'win32' ? 'rundll32.exe' : process.platform === 'darwin' ? 'open' : 'xdg-open';
7
+ const args = process.platform === 'win32' ? ['url.dll,FileProtocolHandler', url] : [url];
8
+ await new Promise((resolve, reject) => execFile(command, args, { windowsHide: true, timeout: 15_000 }, error => error ? reject(new StudioError('STUDIO_BROWSER_OPEN_FAILED', 500)) : resolve()));
9
+ }
10
+ export async function runStudioCommand(options) {
11
+ const server = await startStudioServer({ outputDir: options.outputDir });
12
+ let stop;
13
+ const stopped = new Promise(resolve => { stop = resolve; });
14
+ process.once('SIGINT', stop);
15
+ process.once('SIGTERM', stop);
16
+ try {
17
+ console.log(`${ui('Local Studio:', '本机工作台:')} ${server.origin}/studio/`);
18
+ console.log(options.outputDir ? `${ui('Save location:', '保存位置:')} ${options.outputDir}` : ui('Save location: not configured', '保存位置:未设置'));
19
+ if (options.noOpen)
20
+ console.log(ui('Headless diagnostic mode. The printed address does not authorize access. Restart without --no-open for browser access.', '无浏览器诊断模式。打印的地址不带访问授权;交互使用请去掉 --no-open 重新启动。'));
21
+ else
22
+ await openBrowser(server.launchUrl);
23
+ await stopped;
24
+ }
25
+ finally {
26
+ process.removeListener('SIGINT', stop);
27
+ process.removeListener('SIGTERM', stop);
28
+ await server.close();
29
+ }
30
+ }
31
+ //# sourceMappingURL=launch.js.map
@@ -0,0 +1,9 @@
1
+ import { z } from 'zod';
2
+ import type { StudioCatalog, StudioMediaType } from './types.js';
3
+ export declare const mediaExtensions: Record<StudioMediaType, string>;
4
+ export declare const displayNameSchema: z.ZodEffects<z.ZodString, string, string>;
5
+ export declare function validateStudioCatalog(value: unknown): StudioCatalog;
6
+ export declare function validateStudioAssetId(value: unknown): string;
7
+ export declare function detectStudioMediaType(prefix: Uint8Array): StudioMediaType;
8
+ export declare function assertStudioMediaType(prefix: Uint8Array, declared: string): StudioMediaType;
9
+ //# sourceMappingURL=media.d.ts.map
@@ -0,0 +1,50 @@
1
+ import { z } from 'zod';
2
+ import { StudioError } from './errors.js';
3
+ import { directorProvenanceSchema } from './director-contract.js';
4
+ export const mediaExtensions = {
5
+ 'image/png': 'png', 'image/jpeg': 'jpg', 'image/webp': 'webp', 'video/mp4': 'mp4', 'video/webm': 'webm',
6
+ };
7
+ const uuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
8
+ export const displayNameSchema = z.string().trim().min(1).max(200).refine(v => !/[\u0000-\u001f\u007f]/.test(v));
9
+ const assetSchema = z.object({
10
+ directorCapture: directorProvenanceSchema.optional(),
11
+ assetId: z.string().regex(uuid), displayName: displayNameSchema,
12
+ mediaType: z.enum(['image/png', 'image/jpeg', 'image/webp', 'video/mp4', 'video/webm']),
13
+ relativePath: z.string(), byteLength: z.number().int().positive().safe(),
14
+ sha256: z.string().regex(/^[0-9a-f]{64}$/), savedAt: z.string().datetime(),
15
+ }).strict().refine(a => a.relativePath === `assets/${a.assetId}.${mediaExtensions[a.mediaType]}` && (!a.directorCapture || (a.mediaType === 'image/png' && a.byteLength <= 8 * 1024 * 1024)));
16
+ const catalogSchema = z.object({
17
+ schemaVersion: z.literal(1), revision: z.number().int().nonnegative().safe(), assets: z.array(assetSchema).max(100_000),
18
+ }).strict().refine(c => new Set(c.assets.map(a => a.assetId)).size === c.assets.length && new Set(c.assets.flatMap(a => a.directorCapture ? [a.directorCapture.captureId] : [])).size === c.assets.filter(a => a.directorCapture).length);
19
+ export function validateStudioCatalog(value) {
20
+ const parsed = catalogSchema.safeParse(value);
21
+ if (!parsed.success)
22
+ throw new StudioError('STUDIO_CATALOG_INVALID', 500);
23
+ return parsed.data;
24
+ }
25
+ export function validateStudioAssetId(value) {
26
+ if (typeof value !== 'string' || !uuid.test(value))
27
+ throw new StudioError('STUDIO_ASSET_ID_INVALID');
28
+ return value;
29
+ }
30
+ export function detectStudioMediaType(prefix) {
31
+ const b = Buffer.from(prefix);
32
+ if (b.length >= 16 && b.subarray(0, 8).equals(Buffer.from('89504e470d0a1a0a', 'hex')) && b.toString('ascii', 12, 16) === 'IHDR')
33
+ return 'image/png';
34
+ if (b.length >= 4 && b[0] === 0xff && b[1] === 0xd8 && b[2] === 0xff && b[3] !== 0x00 && b[3] !== 0xff)
35
+ return 'image/jpeg';
36
+ if (b.length >= 16 && b.toString('ascii', 0, 4) === 'RIFF' && b.toString('ascii', 8, 12) === 'WEBP' && ['VP8 ', 'VP8L', 'VP8X'].includes(b.toString('ascii', 12, 16)))
37
+ return 'image/webp';
38
+ if (b.length >= 16 && b.readUInt32BE(0) >= 16 && b.toString('ascii', 4, 8) === 'ftyp' && /^(isom|iso[2-9]|mp4[12]|avc1|M4V |dash)$/.test(b.toString('ascii', 8, 12)))
39
+ return 'video/mp4';
40
+ if (b.length >= 11 && b.subarray(0, 4).equals(Buffer.from('1a45dfa3', 'hex')) && b.includes(Buffer.from('4282847765626d', 'hex')))
41
+ return 'video/webm';
42
+ throw new StudioError('STUDIO_MEDIA_TYPE_INVALID', 415);
43
+ }
44
+ export function assertStudioMediaType(prefix, declared) {
45
+ const type = detectStudioMediaType(prefix);
46
+ if (declared !== type)
47
+ throw new StudioError('STUDIO_MEDIA_TYPE_INVALID', 415);
48
+ return type;
49
+ }
50
+ //# sourceMappingURL=media.js.map