@lumenkey/cli 0.2.1 → 0.2.2
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 +13 -3
- package/dist/cli/copied.d.ts +5 -0
- package/dist/cli/copied.d.ts.map +1 -1
- package/dist/cli/copied.js +121 -75
- package/dist/cli/copied.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +22 -26
- package/dist/cli/index.js.map +1 -1
- package/dist/core/atomic-config-file.d.ts +5 -0
- package/dist/core/atomic-config-file.d.ts.map +1 -0
- package/dist/core/atomic-config-file.js +54 -0
- package/dist/core/atomic-config-file.js.map +1 -0
- package/dist/core/client.d.ts +9 -1
- package/dist/core/client.d.ts.map +1 -1
- package/dist/core/client.js +18 -4
- package/dist/core/client.js.map +1 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +19 -4
- package/dist/core/config.js.map +1 -1
- package/dist/core/download.d.ts +3 -0
- package/dist/core/download.d.ts.map +1 -0
- package/dist/core/download.js +100 -0
- package/dist/core/download.js.map +1 -0
- package/dist/core/error-result.d.ts +14 -0
- package/dist/core/error-result.d.ts.map +1 -0
- package/dist/core/error-result.js +25 -0
- package/dist/core/error-result.js.map +1 -0
- package/dist/core/generation-result.d.ts +18 -0
- package/dist/core/generation-result.d.ts.map +1 -0
- package/dist/core/generation-result.js +22 -0
- package/dist/core/generation-result.js.map +1 -0
- package/dist/core/locale.d.ts.map +1 -1
- package/dist/core/locale.js +30 -0
- package/dist/core/locale.js.map +1 -1
- package/dist/core/poller.d.ts +13 -4
- package/dist/core/poller.d.ts.map +1 -1
- package/dist/core/poller.js +38 -53
- package/dist/core/poller.js.map +1 -1
- package/dist/core/profiles.d.ts +5 -1
- package/dist/core/profiles.d.ts.map +1 -1
- package/dist/core/profiles.js +59 -12
- package/dist/core/profiles.js.map +1 -1
- package/dist/core/set-values.d.ts.map +1 -1
- package/dist/core/set-values.js +6 -1
- package/dist/core/set-values.js.map +1 -1
- package/dist/core/version.d.ts +2 -0
- package/dist/core/version.d.ts.map +1 -0
- package/dist/core/version.js +3 -0
- package/dist/core/version.js.map +1 -0
- package/dist/core/wait.d.ts +1 -0
- package/dist/core/wait.d.ts.map +1 -1
- package/dist/core/wait.js +2 -40
- package/dist/core/wait.js.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +50 -55
- package/dist/mcp/server.js.map +1 -1
- package/package.json +1 -1
package/dist/core/profiles.js
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import os from 'node:os';
|
|
4
|
-
import { ui } from './locale.js';
|
|
4
|
+
import { errorSentence, ui } from './locale.js';
|
|
5
5
|
import { assertConfigDirReadable } from './config-dir.js';
|
|
6
|
-
|
|
6
|
+
import { assertSafeConfigFile, writeAtomicConfigFile } from './atomic-config-file.js';
|
|
7
|
+
export class ProfileError extends Error {
|
|
8
|
+
code;
|
|
9
|
+
constructor(code, reason) {
|
|
10
|
+
super(`${errorSentence(code)} (${code}) ${reason}`);
|
|
11
|
+
this.code = code;
|
|
12
|
+
this.name = 'LumenKeyError';
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export class ProfileMissingError extends ProfileError {
|
|
7
16
|
constructor(name) {
|
|
8
|
-
super(ui(`Profile ${name} does not exist.`, `档案 ${name} 不存在。`));
|
|
9
|
-
this.name = 'ProfileMissingError';
|
|
17
|
+
super('PROFILE_MISSING', ui(`Profile ${name} does not exist.`, `档案 ${name} 不存在。`));
|
|
10
18
|
}
|
|
11
19
|
}
|
|
12
20
|
function filePath(home = os.homedir()) {
|
|
@@ -15,20 +23,55 @@ function filePath(home = os.homedir()) {
|
|
|
15
23
|
function read(home = os.homedir()) {
|
|
16
24
|
const target = filePath(home);
|
|
17
25
|
assertConfigDirReadable(target);
|
|
18
|
-
if (!fs.existsSync(target))
|
|
19
|
-
return { profiles: {} };
|
|
20
26
|
try {
|
|
27
|
+
assertSafeConfigFile(target);
|
|
28
|
+
if (!fs.existsSync(target))
|
|
29
|
+
return { profiles: Object.create(null) };
|
|
21
30
|
const parsed = JSON.parse(fs.readFileSync(target, 'utf8'));
|
|
22
|
-
return
|
|
31
|
+
return validate(parsed);
|
|
23
32
|
}
|
|
24
|
-
catch {
|
|
25
|
-
|
|
33
|
+
catch (error) {
|
|
34
|
+
if (error instanceof ProfileError)
|
|
35
|
+
throw error;
|
|
36
|
+
throw new ProfileError(error instanceof SyntaxError ? 'PROFILE_INVALID' : 'PROFILE_UNREADABLE', target);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function object(value) {
|
|
40
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
41
|
+
}
|
|
42
|
+
function validName(name) {
|
|
43
|
+
if (!name.trim() || Object.prototype.hasOwnProperty.call(Object.prototype, name)) {
|
|
44
|
+
throw new ProfileError('PROFILE_INVALID', ui('Invalid profile name.', '档案名称无效。'));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function validate(value) {
|
|
48
|
+
if (!object(value) || !object(value.profiles) || (value.current !== undefined && typeof value.current !== 'string')) {
|
|
49
|
+
throw new ProfileError('PROFILE_INVALID', ui('profiles.json must contain a profiles object.', 'profiles.json 必须包含 profiles 对象。'));
|
|
50
|
+
}
|
|
51
|
+
const profiles = Object.create(null);
|
|
52
|
+
for (const [name, record] of Object.entries(value.profiles)) {
|
|
53
|
+
validName(name);
|
|
54
|
+
if (!object(record) || ['key', 'host', 'projectId'].some(key => record[key] !== undefined && typeof record[key] !== 'string')) {
|
|
55
|
+
throw new ProfileError('PROFILE_INVALID', ui('Profile records must be objects with string fields.', '档案记录必须是包含字符串字段的对象。'));
|
|
56
|
+
}
|
|
57
|
+
profiles[name] = Object.fromEntries(['key', 'host', 'projectId'].filter(key => record[key] !== undefined).map(key => [key, record[key]]));
|
|
26
58
|
}
|
|
59
|
+
if (value.current !== undefined) {
|
|
60
|
+
validName(value.current);
|
|
61
|
+
if (!Object.hasOwn(profiles, value.current))
|
|
62
|
+
throw new ProfileMissingError(value.current);
|
|
63
|
+
}
|
|
64
|
+
return { current: value.current, profiles };
|
|
27
65
|
}
|
|
28
66
|
function write(data, home = os.homedir()) {
|
|
29
67
|
const target = filePath(home);
|
|
30
|
-
|
|
31
|
-
|
|
68
|
+
validate(data);
|
|
69
|
+
try {
|
|
70
|
+
writeAtomicConfigFile(target, data);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
throw new ProfileError('PROFILE_UNREADABLE', target);
|
|
74
|
+
}
|
|
32
75
|
}
|
|
33
76
|
export function currentProfile(home = os.homedir()) {
|
|
34
77
|
const data = read(home);
|
|
@@ -55,6 +98,7 @@ export function publicProfile(record) {
|
|
|
55
98
|
return copy;
|
|
56
99
|
}
|
|
57
100
|
export function createProfile(name, home = os.homedir(), activate = true) {
|
|
101
|
+
validName(name);
|
|
58
102
|
const data = read(home);
|
|
59
103
|
if (data.profiles[name])
|
|
60
104
|
return 'exists';
|
|
@@ -65,6 +109,7 @@ export function createProfile(name, home = os.homedir(), activate = true) {
|
|
|
65
109
|
return 'created';
|
|
66
110
|
}
|
|
67
111
|
export function setProfile(name, home = os.homedir()) {
|
|
112
|
+
validName(name);
|
|
68
113
|
const data = read(home);
|
|
69
114
|
if (!data.profiles[name])
|
|
70
115
|
throw new ProfileMissingError(name);
|
|
@@ -77,6 +122,7 @@ export function unsetProfile(home = os.homedir()) {
|
|
|
77
122
|
write(data, home);
|
|
78
123
|
}
|
|
79
124
|
export function deleteProfile(name, home = os.homedir()) {
|
|
125
|
+
validName(name);
|
|
80
126
|
const data = read(home);
|
|
81
127
|
if (!data.profiles[name])
|
|
82
128
|
return false;
|
|
@@ -87,6 +133,7 @@ export function deleteProfile(name, home = os.homedir()) {
|
|
|
87
133
|
return true;
|
|
88
134
|
}
|
|
89
135
|
export function setNamedProfileFields(name, fields, home = os.homedir()) {
|
|
136
|
+
validName(name);
|
|
90
137
|
const data = read(home);
|
|
91
138
|
if (!data.profiles[name])
|
|
92
139
|
throw new ProfileMissingError(name);
|
|
@@ -97,7 +144,7 @@ export function setProfileFields(fields, home = os.homedir()) {
|
|
|
97
144
|
const data = read(home);
|
|
98
145
|
const name = data.current;
|
|
99
146
|
if (!name || !data.profiles[name])
|
|
100
|
-
throw new
|
|
147
|
+
throw new ProfileMissingError(name ?? '');
|
|
101
148
|
data.profiles[name] = { ...data.profiles[name], ...fields };
|
|
102
149
|
write(data, home);
|
|
103
150
|
return name;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"profiles.js","sourceRoot":"","sources":["../../src/core/profiles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"profiles.js","sourceRoot":"","sources":["../../src/core/profiles.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"set-values.d.ts","sourceRoot":"","sources":["../../src/core/set-values.ts"],"names":[],"mappings":"AAAA,0FAA0F;AAC1F,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG5C,kFAAkF;AAClF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,qBAAqB,GAAG,qBAAqB,EAAE,GAAG,EAAE,MAAM,GAAG,aAAa,
|
|
1
|
+
{"version":3,"file":"set-values.d.ts","sourceRoot":"","sources":["../../src/core/set-values.ts"],"names":[],"mappings":"AAAA,0FAA0F;AAC1F,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG5C,kFAAkF;AAClF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,qBAAqB,GAAG,qBAAqB,EAAE,GAAG,EAAE,MAAM,GAAG,aAAa,CAanH;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAUjD;AAED,KAAK,QAAQ,GACT;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC7B;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAC;AAIvB,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAQpE;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,EAAE,CAmCjD;AA0CD,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAK7F;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAOzG;AAED,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAS5G;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,CAGhD"}
|
package/dist/core/set-values.js
CHANGED
|
@@ -3,9 +3,14 @@ import { LumenKeyError } from './client.js';
|
|
|
3
3
|
import { ui } from './locale.js';
|
|
4
4
|
/** Identify a parameter without echoing its value, a whole pair, or a payload. */
|
|
5
5
|
export function modelParameterError(code, key) {
|
|
6
|
+
if (!/^[a-zA-Z][a-zA-Z0-9_]{0,63}$/.test(key)) {
|
|
7
|
+
return new LumenKeyError(code, undefined, undefined, code === 'MODEL_PARAM_UNKNOWN'
|
|
8
|
+
? ui('This model does not support the supplied parameter.', '该模型不支持所提供的参数。')
|
|
9
|
+
: ui('The supplied parameter is invalid. Check lumenkey model explain.', '所提供的参数值无效。请运行 lumenkey model explain 查看允许的参数。'));
|
|
10
|
+
}
|
|
6
11
|
const label = key === 'aspect_ratio' || key === 'ratio'
|
|
7
12
|
? ui('aspect ratio', '宽高比')
|
|
8
|
-
:
|
|
13
|
+
: key;
|
|
9
14
|
const detail = code === 'MODEL_PARAM_UNKNOWN'
|
|
10
15
|
? ui(`This model has no ${label} parameter.`, `该模型不支持 ${label} 参数。`)
|
|
11
16
|
: ui(`Invalid value for ${label}. Check this model's allowed parameters with lumenkey model explain.`, `${label} 参数值无效。请运行 lumenkey model explain 查看该模型允许的参数。`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"set-values.js","sourceRoot":"","sources":["../../src/core/set-values.ts"],"names":[],"mappings":"AAAA,0FAA0F;AAC1F,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAEjC,kFAAkF;AAClF,MAAM,UAAU,mBAAmB,CAAC,IAAmD,EAAE,GAAW;IAClG,
|
|
1
|
+
{"version":3,"file":"set-values.js","sourceRoot":"","sources":["../../src/core/set-values.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../src/core/version.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,cAAc,EAAE,MAAoG,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/core/version.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AAEzB,MAAM,CAAC,MAAM,cAAc,GAAW,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,GAAG,CAAC,oBAAoB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC"}
|
package/dist/core/wait.d.ts
CHANGED
|
@@ -3,5 +3,6 @@ import type { GenerationJobDetail } from './types.js';
|
|
|
3
3
|
export declare function waitForJob(client: LumenKeyClient, jobId: string, options?: {
|
|
4
4
|
intervalMs?: number;
|
|
5
5
|
timeoutMs?: number;
|
|
6
|
+
signal?: AbortSignal;
|
|
6
7
|
}): Promise<GenerationJobDetail>;
|
|
7
8
|
//# sourceMappingURL=wait.d.ts.map
|
package/dist/core/wait.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wait.d.ts","sourceRoot":"","sources":["../../src/core/wait.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"wait.d.ts","sourceRoot":"","sources":["../../src/core/wait.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACtD,wBAAsB,UAAU,CAAC,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,EACpE,OAAO,GAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAO,GAC9E,OAAO,CAAC,mBAAmB,CAAC,CAE9B"}
|
package/dist/core/wait.js
CHANGED
|
@@ -1,43 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { assertJobResult } from './poller.js';
|
|
3
|
-
import { waitTimeoutDetail } from './locale.js';
|
|
4
|
-
const TERMINAL = new Set(['succeeded', 'failed', 'rejected', 'cancelled', 'expired']);
|
|
5
|
-
const MAX_TIMER_MS = 2_147_483_647;
|
|
1
|
+
import { runPolling } from './poller.js';
|
|
6
2
|
export async function waitForJob(client, jobId, options = {}) {
|
|
7
|
-
|
|
8
|
-
const timeoutMs = options.timeoutMs ?? 1_800_000;
|
|
9
|
-
if (!Number.isFinite(intervalMs) || intervalMs < 0 || intervalMs > MAX_TIMER_MS || !Number.isFinite(timeoutMs) || timeoutMs < 0 || timeoutMs > Number.MAX_SAFE_INTEGER - Date.now())
|
|
10
|
-
throw new LumenKeyError('WAIT_OPTIONS_INVALID');
|
|
11
|
-
const deadline = Date.now() + timeoutMs;
|
|
12
|
-
const timedOut = () => new LumenKeyError('WAIT_TIMEOUT', undefined, undefined, waitTimeoutDetail(jobId, timeoutMs));
|
|
13
|
-
let delay = intervalMs;
|
|
14
|
-
let first = true;
|
|
15
|
-
for (;;) {
|
|
16
|
-
if (!first && Date.now() >= deadline)
|
|
17
|
-
throw timedOut();
|
|
18
|
-
first = false;
|
|
19
|
-
let detail;
|
|
20
|
-
try {
|
|
21
|
-
detail = await client.getJob(jobId);
|
|
22
|
-
}
|
|
23
|
-
catch (error) {
|
|
24
|
-
if (!(error instanceof LumenKeyError) || !(error.status === 429 || error.status === 502 || error.status === 503 || error.status === 504))
|
|
25
|
-
throw error;
|
|
26
|
-
const remaining = deadline - Date.now();
|
|
27
|
-
if (remaining <= 0)
|
|
28
|
-
throw timedOut();
|
|
29
|
-
await new Promise((resolve) => setTimeout(resolve, Math.min(remaining, Math.max(delay, error.retryAfterMs ?? 0))));
|
|
30
|
-
delay = Math.min(delay * 1.5, Math.max(intervalMs, 10_000));
|
|
31
|
-
continue;
|
|
32
|
-
}
|
|
33
|
-
if (TERMINAL.has(detail.status)) {
|
|
34
|
-
assertJobResult(detail, detail.status === 'succeeded');
|
|
35
|
-
return detail;
|
|
36
|
-
}
|
|
37
|
-
const remaining = deadline - Date.now();
|
|
38
|
-
if (remaining <= 0)
|
|
39
|
-
throw timedOut();
|
|
40
|
-
await new Promise((resolve) => setTimeout(resolve, Math.min(remaining, intervalMs)));
|
|
41
|
-
}
|
|
3
|
+
return runPolling(client, jobId, { ...options, requireOutput: true });
|
|
42
4
|
}
|
|
43
5
|
//# sourceMappingURL=wait.js.map
|
package/dist/core/wait.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wait.js","sourceRoot":"","sources":["../../src/core/wait.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"wait.js","sourceRoot":"","sources":["../../src/core/wait.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,MAAsB,EAAE,KAAa,EACpE,UAA6E,EAAE;IAE/E,OAAO,UAAU,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,GAAG,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;AACxE,CAAC"}
|
package/dist/mcp/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAyDpE,wBAAgB,eAAe,IAAI,SAAS,CA+W3C;AAED,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAKjD"}
|
package/dist/mcp/server.js
CHANGED
|
@@ -5,7 +5,10 @@ import { z } from 'zod';
|
|
|
5
5
|
import { loadConfig } from '../core/config.js';
|
|
6
6
|
import { assertSupportedLang, LumenKeyClient, LumenKeyError, parseChatBilling } from '../core/client.js';
|
|
7
7
|
import { blankPromptSentence, idempotencyKeyMessages, isBlankPrompt, jobStatusText, missingModelSentence, ui, waitTimeoutDetail } from '../core/locale.js';
|
|
8
|
-
import { pollJob,
|
|
8
|
+
import { pollJob, assertJobResult } from '../core/poller.js';
|
|
9
|
+
import { chatText, errorResult } from '../core/error-result.js';
|
|
10
|
+
import { generationResult } from '../core/generation-result.js';
|
|
11
|
+
import { packageVersion } from '../core/version.js';
|
|
9
12
|
import { explainModel } from '../core/explain.js';
|
|
10
13
|
import { waitForJob } from '../core/wait.js';
|
|
11
14
|
import { buildGenerationInput, quoteCreditLimitError } from '../cli/copied.js';
|
|
@@ -13,6 +16,18 @@ import { validateKnownEnums } from '../core/set-values.js';
|
|
|
13
16
|
function errorText(error) {
|
|
14
17
|
return `${ui('Error: ', '错误:')}${error instanceof Error ? error.message : ui('Request failed', '请求失败')}`;
|
|
15
18
|
}
|
|
19
|
+
function jobToolResult(detail) {
|
|
20
|
+
let failure;
|
|
21
|
+
try {
|
|
22
|
+
assertJobResult(detail, detail.status === 'succeeded');
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
failure = error;
|
|
26
|
+
}
|
|
27
|
+
return { ...(failure ? { isError: true } : {}), content: [{ type: 'text', text: JSON.stringify({ ...detail,
|
|
28
|
+
...(failure ? { failureMessage: errorText(failure) } : {}),
|
|
29
|
+
}) }] };
|
|
30
|
+
}
|
|
16
31
|
function requiredPrompt(description) {
|
|
17
32
|
return z.string().superRefine((value, ctx) => {
|
|
18
33
|
if (isBlankPrompt(value)) {
|
|
@@ -22,8 +37,8 @@ function requiredPrompt(description) {
|
|
|
22
37
|
}
|
|
23
38
|
function idempotencyKeyField(description) {
|
|
24
39
|
const text = idempotencyKeyMessages();
|
|
25
|
-
const field = z.string().
|
|
26
|
-
if (!value) {
|
|
40
|
+
const field = z.string().superRefine((value, ctx) => {
|
|
41
|
+
if (!value.trim()) {
|
|
27
42
|
ctx.addIssue({ code: z.ZodIssueCode.custom, message: text.required });
|
|
28
43
|
return;
|
|
29
44
|
}
|
|
@@ -43,7 +58,7 @@ export function createMcpServer() {
|
|
|
43
58
|
assertSupportedLang();
|
|
44
59
|
const server = new McpServer({
|
|
45
60
|
name: 'lumenkey-mcp-server',
|
|
46
|
-
version:
|
|
61
|
+
version: packageVersion,
|
|
47
62
|
});
|
|
48
63
|
const config = loadConfig();
|
|
49
64
|
const client = new LumenKeyClient(config);
|
|
@@ -93,7 +108,9 @@ export function createMcpServer() {
|
|
|
93
108
|
idempotencyKey: idempotencyKeyField(ui('Optional. Generated when omitted. Do not ask the user for it', '可选,省略时自动生成,无需向用户索取')),
|
|
94
109
|
maxCredits: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER).optional().describe(ui('Maximum accepted quote credits', '可接受的报价算力点上限')),
|
|
95
110
|
downloadPath: z.string().optional().describe(ui('Local file path to save the generated MP4 video', '保存生成 MP4 视频的本地文件路径')),
|
|
96
|
-
|
|
111
|
+
intervalMs: z.number().positive().finite().max(2_147_483_647).optional().describe(ui('Polling interval in milliseconds; defaults to 5000', '查询间隔,单位为毫秒;默认 5000')),
|
|
112
|
+
timeoutMs: z.number().nonnegative().finite().max(Number.MAX_SAFE_INTEGER - Date.now()).optional().describe(ui('Wait timeout in milliseconds; defaults to 1800000. Does not cancel the task', '等待超时,单位为毫秒;默认 1800000,超时不取消任务')),
|
|
113
|
+
}, async ({ modelId, prompt, imageUrl, duration, resolution, ratio, set, projectId, downloadPath, confirm, idempotencyKey, maxCredits, intervalMs, timeoutMs }) => {
|
|
97
114
|
let createdJob;
|
|
98
115
|
try {
|
|
99
116
|
const built = await buildGenerationInput(modelId, {
|
|
@@ -112,7 +129,7 @@ export function createMcpServer() {
|
|
|
112
129
|
throw quoteCreditLimitError('maxCredits');
|
|
113
130
|
if (!confirm)
|
|
114
131
|
return { content: [{ type: 'text', text: JSON.stringify({ quote, requiresConfirmation: true, submitted: false }) }] };
|
|
115
|
-
const key =
|
|
132
|
+
const key = idempotencyKey ?? crypto.randomUUID();
|
|
116
133
|
// Step 2: Create Job
|
|
117
134
|
process.stderr.write(`[LumenKey MCP] ${ui(`Submitting job with quote ${quote.quoteId} (${quote.credits} credits)...`, `正在提交任务,报价编号:${quote.quoteId}(${quote.credits} 算力点)...`)}\n`);
|
|
118
135
|
const job = await client.createJob({
|
|
@@ -124,29 +141,21 @@ export function createMcpServer() {
|
|
|
124
141
|
// Step 3: Poll
|
|
125
142
|
process.stderr.write(`[LumenKey MCP] ${ui(`Polling job ${job.jobId}...`, `正在查询任务 ${job.jobId} 的进度...`)}\n`);
|
|
126
143
|
const detail = await pollJob(client, job.jobId, {
|
|
144
|
+
initialIntervalMs: intervalMs, timeoutMs,
|
|
127
145
|
onProgress: (d) => {
|
|
128
146
|
process.stderr.write(`[LumenKey MCP] ${ui(`Job ${job.jobId} status: ${d.status}`, `任务 ${job.jobId} 状态:${jobStatusText(d.status)}`)}\n`);
|
|
129
147
|
},
|
|
130
148
|
});
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
if (outputUrl && downloadPath) {
|
|
135
|
-
process.stderr.write(`[LumenKey MCP] ${ui(`Downloading video to ${downloadPath}...`, `正在下载视频到 ${downloadPath}...`)}\n`);
|
|
136
|
-
savedPath = await downloadFile(outputUrl, downloadPath);
|
|
137
|
-
}
|
|
149
|
+
if (downloadPath)
|
|
150
|
+
process.stderr.write(`[LumenKey MCP] ${ui('Downloading video...', '正在下载视频...')}\n`);
|
|
151
|
+
const result = await generationResult(detail, key, downloadPath);
|
|
138
152
|
return {
|
|
139
153
|
content: [
|
|
140
154
|
{
|
|
141
155
|
type: 'text',
|
|
142
156
|
text: JSON.stringify({
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
status: detail.status,
|
|
146
|
-
idempotencyKey: key,
|
|
147
|
-
videoUrl: outputUrl,
|
|
148
|
-
urls: (detail.outputs ?? []).map((item) => item.url).filter((url) => typeof url === 'string'),
|
|
149
|
-
localFile: savedPath,
|
|
157
|
+
...result,
|
|
158
|
+
videoUrl: result.urls[0],
|
|
150
159
|
}, null, 2),
|
|
151
160
|
},
|
|
152
161
|
],
|
|
@@ -156,7 +165,7 @@ export function createMcpServer() {
|
|
|
156
165
|
const message = errorText(err);
|
|
157
166
|
return {
|
|
158
167
|
isError: true,
|
|
159
|
-
content: [{ type: 'text', text: createdJob ? JSON.stringify({ error: message, ...createdJob }) : message }],
|
|
168
|
+
content: [{ type: 'text', text: createdJob ? JSON.stringify({ ...errorResult(err), error: message, ...createdJob }) : message }],
|
|
160
169
|
};
|
|
161
170
|
}
|
|
162
171
|
});
|
|
@@ -176,7 +185,9 @@ export function createMcpServer() {
|
|
|
176
185
|
maxCredits: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER).optional().describe(ui('Maximum accepted quote credits', '可接受的报价算力点上限')),
|
|
177
186
|
set: z.array(z.string()).optional().describe(ui('Extra key=value pairs', '额外的 key=value 参数')),
|
|
178
187
|
downloadPath: z.string().optional().describe(ui('Local file path to save the generated image', '保存生成图片的本地文件路径')),
|
|
179
|
-
|
|
188
|
+
intervalMs: z.number().positive().finite().max(2_147_483_647).optional().describe(ui('Polling interval in milliseconds; defaults to 5000', '查询间隔,单位为毫秒;默认 5000')),
|
|
189
|
+
timeoutMs: z.number().nonnegative().finite().max(Number.MAX_SAFE_INTEGER - Date.now()).optional().describe(ui('Wait timeout in milliseconds; defaults to 1800000. Does not cancel the task', '等待超时,单位为毫秒;默认 1800000,超时不取消任务')),
|
|
190
|
+
}, async ({ modelId, prompt, imageUrl, resolution, ratio, size, set, downloadPath, projectId, confirm, idempotencyKey, maxCredits, intervalMs, timeoutMs }) => {
|
|
180
191
|
let createdJob;
|
|
181
192
|
try {
|
|
182
193
|
const built = await buildGenerationInput(modelId, {
|
|
@@ -188,33 +199,28 @@ export function createMcpServer() {
|
|
|
188
199
|
}, set ?? []);
|
|
189
200
|
const input = built.input;
|
|
190
201
|
validateKnownEnums(built.properties, input);
|
|
202
|
+
process.stderr.write(`[LumenKey MCP] ${ui(`Quoting image generation for ${modelId}...`, `正在获取 ${modelId} 的图片生成报价...`)}\n`);
|
|
191
203
|
const quote = await client.getQuote({ modelId, input });
|
|
192
204
|
if (maxCredits !== undefined && quote.credits > maxCredits)
|
|
193
205
|
throw quoteCreditLimitError('maxCredits');
|
|
194
206
|
if (!confirm)
|
|
195
207
|
return { content: [{ type: 'text', text: JSON.stringify({ quote, requiresConfirmation: true, submitted: false }) }] };
|
|
196
|
-
const key =
|
|
208
|
+
const key = idempotencyKey ?? crypto.randomUUID();
|
|
209
|
+
process.stderr.write(`[LumenKey MCP] ${ui('Submitting generation job...', '正在提交生成任务...')}\n`);
|
|
197
210
|
const job = await client.createJob({ quoteId: quote.quoteId, input, projectId }, key);
|
|
198
211
|
createdJob = { jobId: job.jobId, idempotencyKey: key };
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
savedPath = await downloadFile(outputUrl, downloadPath);
|
|
205
|
-
}
|
|
212
|
+
process.stderr.write(`[LumenKey MCP] ${ui(`Polling job ${job.jobId}...`, `正在查询任务 ${job.jobId} 的进度...`)}\n`);
|
|
213
|
+
const detail = await pollJob(client, job.jobId, { initialIntervalMs: intervalMs, timeoutMs,
|
|
214
|
+
onProgress: row => process.stderr.write(`[LumenKey MCP] ${ui('Status: ', '状态:')}${jobStatusText(row.status)}\n`),
|
|
215
|
+
});
|
|
216
|
+
const result = await generationResult(detail, key, downloadPath);
|
|
206
217
|
return {
|
|
207
218
|
content: [
|
|
208
219
|
{
|
|
209
220
|
type: 'text',
|
|
210
221
|
text: JSON.stringify({
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
status: detail.status,
|
|
214
|
-
idempotencyKey: key,
|
|
215
|
-
imageUrl: outputUrl,
|
|
216
|
-
urls: (detail.outputs ?? []).map((item) => item.url).filter((url) => typeof url === 'string'),
|
|
217
|
-
localFile: savedPath,
|
|
222
|
+
...result,
|
|
223
|
+
imageUrl: result.urls[0],
|
|
218
224
|
}, null, 2),
|
|
219
225
|
},
|
|
220
226
|
],
|
|
@@ -224,7 +230,7 @@ export function createMcpServer() {
|
|
|
224
230
|
const message = errorText(err);
|
|
225
231
|
return {
|
|
226
232
|
isError: true,
|
|
227
|
-
content: [{ type: 'text', text: createdJob ? JSON.stringify({ error: message, ...createdJob }) : message }],
|
|
233
|
+
content: [{ type: 'text', text: createdJob ? JSON.stringify({ ...errorResult(err), error: message, ...createdJob }) : message }],
|
|
228
234
|
};
|
|
229
235
|
}
|
|
230
236
|
});
|
|
@@ -236,15 +242,7 @@ export function createMcpServer() {
|
|
|
236
242
|
}, async ({ jobId }) => {
|
|
237
243
|
try {
|
|
238
244
|
const detail = await client.getJob(jobId);
|
|
239
|
-
|
|
240
|
-
return {
|
|
241
|
-
content: [
|
|
242
|
-
{
|
|
243
|
-
type: 'text',
|
|
244
|
-
text: JSON.stringify(detail, null, 2),
|
|
245
|
-
},
|
|
246
|
-
],
|
|
247
|
-
};
|
|
245
|
+
return jobToolResult(detail);
|
|
248
246
|
}
|
|
249
247
|
catch (err) {
|
|
250
248
|
return {
|
|
@@ -296,9 +294,7 @@ export function createMcpServer() {
|
|
|
296
294
|
messages,
|
|
297
295
|
...(maxCredits === undefined ? {} : { max_credits: maxCredits }),
|
|
298
296
|
}, idempotencyKey);
|
|
299
|
-
const
|
|
300
|
-
const answer = typeof message?.content === 'string' && message.content.trim()
|
|
301
|
-
? message.content : message?.refusal;
|
|
297
|
+
const answer = chatText(res);
|
|
302
298
|
return {
|
|
303
299
|
content: [
|
|
304
300
|
{ type: 'text', text: typeof answer === 'string' ? answer : '' },
|
|
@@ -338,8 +334,7 @@ export function createMcpServer() {
|
|
|
338
334
|
}, async ({ jobId }) => {
|
|
339
335
|
try {
|
|
340
336
|
const detail = await client.getJob(jobId);
|
|
341
|
-
|
|
342
|
-
return { content: [{ type: 'text', text: JSON.stringify(detail) }] };
|
|
337
|
+
return jobToolResult(detail);
|
|
343
338
|
}
|
|
344
339
|
catch (err) {
|
|
345
340
|
return { isError: true, content: [{ type: 'text', text: errorText(err) }] };
|
|
@@ -347,7 +342,7 @@ export function createMcpServer() {
|
|
|
347
342
|
});
|
|
348
343
|
tool('lumenkey_tasks_wait', ui('Wait for an existing job without creating one', '等待已有生成任务,不创建新任务。'), {
|
|
349
344
|
jobId: z.string().describe(ui('The existing task ID to wait for', '要等待的已有任务 ID')),
|
|
350
|
-
intervalMs: z.number().
|
|
345
|
+
intervalMs: z.number().positive().finite().max(2_147_483_647).optional().describe(ui('Polling interval in milliseconds; defaults to 5000', '查询间隔,单位为毫秒;默认 5000')),
|
|
351
346
|
timeoutMs: z.number().nonnegative().finite().optional().describe(ui('Wait timeout in milliseconds; defaults to 1800000. Does not cancel the task', '等待超时,单位为毫秒;默认 1800000,超时不取消任务')),
|
|
352
347
|
}, async ({ jobId, intervalMs, timeoutMs }) => {
|
|
353
348
|
try {
|
|
@@ -364,11 +359,11 @@ export function createMcpServer() {
|
|
|
364
359
|
});
|
|
365
360
|
tool('lumenkey_tasks_cancel', ui('Request cancellation with yes: true. Cancellation is not a refund; held credits are released or charged only when gateway settlement completes.', '取消已有任务必须传 yes: true。取消不等于退款;冻结算力点是否释放或扣除由网关最终结算决定。'), {
|
|
366
361
|
jobId: z.string().describe(ui('The existing task ID to cancel', '要取消的已有任务 ID')),
|
|
367
|
-
yes: z.boolean({
|
|
362
|
+
yes: z.boolean({ invalid_type_error: ui('Cancellation requires yes: true.', '取消任务必须传入 yes: true。') }).optional().describe(ui('Must be true to authorize cancellation; cancellation is not a refund', '必须为 true 才授权取消;取消不等于退款')),
|
|
368
363
|
}, async ({ jobId, yes }) => {
|
|
369
364
|
try {
|
|
370
365
|
if (yes !== true)
|
|
371
|
-
throw new
|
|
366
|
+
throw new LumenKeyError('CANCEL_CONFIRMATION_REQUIRED', undefined, undefined, ui('Cancellation requires yes: true.', '取消任务必须传入 yes: true。'));
|
|
372
367
|
const result = await client.cancelJob(jobId);
|
|
373
368
|
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
374
369
|
}
|