@lingjingai/scriptctl 0.63.0 → 0.63.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/changes/0.63.1.md +15 -0
- package/changes/0.63.2.md +4 -0
- package/changes/unreleased.md +1 -3
- package/dist/bin.js +15 -181
- package/dist/bin.js.map +3 -3
- package/dist/cli/register-batch.d.ts +3 -0
- package/dist/cli/register-batch.js +28 -0
- package/dist/cli/register-batch.js.map +1 -0
- package/dist/cli/register-edits.d.ts +4 -0
- package/dist/cli/register-edits.js +234 -0
- package/dist/cli/register-edits.js.map +1 -0
- package/dist/cli/register-query.d.ts +4 -0
- package/dist/cli/register-query.js +95 -0
- package/dist/cli/register-query.js.map +1 -0
- package/dist/infra/llm/doubao-video-model.d.ts +56 -0
- package/dist/infra/llm/doubao-video-model.js +231 -0
- package/dist/infra/llm/doubao-video-model.js.map +1 -0
- package/dist/llm/config.js +1 -1
- package/dist/llm/router.d.ts +2 -5
- package/dist/llm/router.js +11 -184
- package/dist/llm/router.js.map +1 -1
- package/dist/review-agent.js +1 -1
- package/dist/review-agent.js.map +2 -2
- package/package.json +1 -1
- package/skills/scriptctl/SKILL.md +2 -2
- package/skills/scriptctl/references/ingest-workflow.md +2 -2
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# 0.63.1
|
|
2
|
+
|
|
3
|
+
## Added
|
|
4
|
+
- None.
|
|
5
|
+
|
|
6
|
+
## Changed
|
|
7
|
+
- `scriptctl ingest` 默认视频路由使用官方 `gemini-3.8-flash → gemini-3.7-flash → 豆包`,暂时清空默认次卡候选列表;显式指定次卡仍可使用。
|
|
8
|
+
|
|
9
|
+
## Fixed
|
|
10
|
+
- Gemini 次卡渠道(`-c1`、`-c2`、`-c3` 等)的视频结构化输出通过 prompt 携带完整字段定义,并支持从附带说明或代码块的响应中提取 JSON;官方渠道继续使用原生 JSON Schema。
|
|
11
|
+
- `@lingjingai/screenplay` publishes its `sc` executable and defaults to public npm access.
|
|
12
|
+
- `scriptctl ingest` now requires every video shot and voice to include a non-empty `MM:SS` timestamp in its structured model output.
|
|
13
|
+
|
|
14
|
+
## Breaking
|
|
15
|
+
- None.
|
package/changes/unreleased.md
CHANGED
|
@@ -7,9 +7,7 @@
|
|
|
7
7
|
- None.
|
|
8
8
|
|
|
9
9
|
## Fixed
|
|
10
|
-
-
|
|
11
|
-
- `@lingjingai/screenplay` publishes its `sc` executable and defaults to public npm access.
|
|
12
|
-
- `scriptctl ingest` now requires every video shot and voice to include a non-empty `MM:SS` timestamp in its structured model output.
|
|
10
|
+
- None.
|
|
13
11
|
|
|
14
12
|
## Breaking
|
|
15
13
|
- None.
|
package/dist/bin.js
CHANGED
|
@@ -265,23 +265,6 @@ function contextOf(failure) {
|
|
|
265
265
|
...failure.details !== void 0 ? { details: failure.details } : {}
|
|
266
266
|
};
|
|
267
267
|
}
|
|
268
|
-
function aggregateFailures(failures, message2) {
|
|
269
|
-
if (failures.length === 0) {
|
|
270
|
-
return createOperationalFailure(
|
|
271
|
-
{ failure_type: "unknown", failure_code: "unknown" },
|
|
272
|
-
{ retryable: false, message: message2 }
|
|
273
|
-
);
|
|
274
|
-
}
|
|
275
|
-
const first = failures[0];
|
|
276
|
-
const sameClassification = failures.every(
|
|
277
|
-
(failure) => failure.failure_type === first.failure_type && failure.failure_code === first.failure_code
|
|
278
|
-
);
|
|
279
|
-
const classification = sameClassification ? classificationOf(first) : { failure_type: "provider", failure_code: "attempts_exhausted" };
|
|
280
|
-
return createOperationalFailure(classification, {
|
|
281
|
-
retryable: failures.some((failure) => failure.retryable),
|
|
282
|
-
message: message2
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
268
|
function formatFailure(failure) {
|
|
286
269
|
const route = failure.provider || failure.model ? `${failure.provider ?? "provider"}/${failure.model ?? "model"} ` : "";
|
|
287
270
|
const reason = failure.provider_reason ? ` provider_reason=${failure.provider_reason}` : "";
|
|
@@ -6539,7 +6522,7 @@ var BUILTIN_LLM_CONFIG = {
|
|
|
6539
6522
|
// reliably than grok did (skeleton leads split correctly, no paren-polluted
|
|
6540
6523
|
// names), which markdown/text mode could not hold on this model.
|
|
6541
6524
|
"ingest-normalize": { provider: "gw", model: "claude-sonnet-4-6-aihubmix", temperature: 0 },
|
|
6542
|
-
"ingest-video": { provider: "google-gemini", model: "gemini-3.
|
|
6525
|
+
"ingest-video": { provider: "google-gemini", model: "gemini-3.8-flash" },
|
|
6543
6526
|
// `scriptctl review` names this profile directly rather than through a
|
|
6544
6527
|
// task id: it drives its own agent loop instead of the router, and one
|
|
6545
6528
|
// Gemini credential serves both the loop and its video checks.
|
|
@@ -7794,15 +7777,6 @@ var OpenAiCompatibleModel = class {
|
|
|
7794
7777
|
};
|
|
7795
7778
|
|
|
7796
7779
|
// src/llm/router.ts
|
|
7797
|
-
var VIDEO_FALLBACK_MODELS = ["gemini-3.7-flash"];
|
|
7798
|
-
var PROXY_GEMINI_MODELS = [
|
|
7799
|
-
"gemini-3.7-flash-c3",
|
|
7800
|
-
"gemini-3.6-flash-c3",
|
|
7801
|
-
"gemini-3.5-flash-c3",
|
|
7802
|
-
"gemini-3.1-pro-preview-think-c3"
|
|
7803
|
-
];
|
|
7804
|
-
var PROXY_GEMINI_API_KEY_ENVS = ["GEMINI_PROXY_API_KEY", "REDRAW_GEMINI_PROXY_API_KEY"];
|
|
7805
|
-
var PROXY_GEMINI_BASE_URL_ENVS = ["GEMINI_PROXY_BASE_URL", "REDRAW_GEMINI_PROXY_BASE_URL"];
|
|
7806
7780
|
var DOUBAO_FALLBACK = {
|
|
7807
7781
|
model: "ep-20260721150136-98dj9",
|
|
7808
7782
|
modelEnv: "ARK_MODEL",
|
|
@@ -7842,26 +7816,6 @@ function credentialError(provider) {
|
|
|
7842
7816
|
data: { expected: ["credential environment variable referenced by the selected provider"], actual: [`provider: ${provider}`, "credential: missing"] }
|
|
7843
7817
|
});
|
|
7844
7818
|
}
|
|
7845
|
-
function routedFailure(error, route) {
|
|
7846
|
-
if (error instanceof LlmTransportError) {
|
|
7847
|
-
return createOperationalFailure(classificationOf(error.failure), {
|
|
7848
|
-
...contextOf(error.failure),
|
|
7849
|
-
provider: route.provider,
|
|
7850
|
-
model: route.model,
|
|
7851
|
-
retryable: error.failure.retryable,
|
|
7852
|
-
message: error.failure.message
|
|
7853
|
-
});
|
|
7854
|
-
}
|
|
7855
|
-
return createOperationalFailure(
|
|
7856
|
-
{ failure_type: "unknown", failure_code: "unknown" },
|
|
7857
|
-
{
|
|
7858
|
-
provider: route.provider,
|
|
7859
|
-
model: route.model,
|
|
7860
|
-
retryable: false,
|
|
7861
|
-
message: error instanceof Error ? error.message : String(error)
|
|
7862
|
-
}
|
|
7863
|
-
);
|
|
7864
|
-
}
|
|
7865
7819
|
var ModelRouter = class {
|
|
7866
7820
|
constructor(config, options = {}) {
|
|
7867
7821
|
this.config = config;
|
|
@@ -8032,52 +7986,6 @@ var ModelRouter = class {
|
|
|
8032
7986
|
throw error;
|
|
8033
7987
|
}
|
|
8034
7988
|
}
|
|
8035
|
-
videoRoute(route, model) {
|
|
8036
|
-
const modelConfig = model === route.info.model ? route.modelConfig : { ...route.modelConfig, model };
|
|
8037
|
-
return {
|
|
8038
|
-
...route.info,
|
|
8039
|
-
model,
|
|
8040
|
-
fingerprint: routeFingerprint(route.info.task, route.providerConfig, modelConfig)
|
|
8041
|
-
};
|
|
8042
|
-
}
|
|
8043
|
-
resolvedVideoRoute(route, model) {
|
|
8044
|
-
const modelConfig = model === route.info.model ? route.modelConfig : { ...route.modelConfig, model };
|
|
8045
|
-
return {
|
|
8046
|
-
info: this.videoRoute(route, model),
|
|
8047
|
-
providerConfig: route.providerConfig,
|
|
8048
|
-
modelConfig
|
|
8049
|
-
};
|
|
8050
|
-
}
|
|
8051
|
-
proxyVideoRoutes(task) {
|
|
8052
|
-
if (this.options.videoProvider) return [];
|
|
8053
|
-
const apiKeyEnv = PROXY_GEMINI_API_KEY_ENVS.find((name) => (process.env[name] ?? "").trim());
|
|
8054
|
-
const baseUrlEnv = PROXY_GEMINI_BASE_URL_ENVS.find((name) => (process.env[name] ?? "").trim());
|
|
8055
|
-
if (!apiKeyEnv || !baseUrlEnv) return [];
|
|
8056
|
-
const baseUrl = normalizeProviderBaseUrl(process.env[baseUrlEnv], baseUrlEnv);
|
|
8057
|
-
return PROXY_GEMINI_MODELS.map((model, index) => {
|
|
8058
|
-
const providerConfig = {
|
|
8059
|
-
protocol: "google-gemini",
|
|
8060
|
-
baseUrl,
|
|
8061
|
-
apiKeyEnv,
|
|
8062
|
-
timeoutMs: 9e4,
|
|
8063
|
-
maxAttempts: index < 2 ? 2 : 1
|
|
8064
|
-
};
|
|
8065
|
-
const modelConfig = { provider: "gemini-proxy", model };
|
|
8066
|
-
return {
|
|
8067
|
-
info: {
|
|
8068
|
-
task,
|
|
8069
|
-
profile: "gemini-proxy",
|
|
8070
|
-
provider: "gemini-proxy",
|
|
8071
|
-
protocol: "google-gemini",
|
|
8072
|
-
baseUrl,
|
|
8073
|
-
model,
|
|
8074
|
-
fingerprint: routeFingerprint(task, providerConfig, modelConfig)
|
|
8075
|
-
},
|
|
8076
|
-
providerConfig,
|
|
8077
|
-
modelConfig
|
|
8078
|
-
};
|
|
8079
|
-
});
|
|
8080
|
-
}
|
|
8081
7989
|
resolveDoubao(task, explicit) {
|
|
8082
7990
|
const overrides = explicit ? this.options.videoProvider : void 0;
|
|
8083
7991
|
const modelName = explicit ? (overrides?.model ?? process.env["ARK_MODEL"] ?? "").trim() : this.doubaoModel();
|
|
@@ -8119,9 +8027,7 @@ var ModelRouter = class {
|
|
|
8119
8027
|
this.adapters.set(key, adapter);
|
|
8120
8028
|
return adapter;
|
|
8121
8029
|
}
|
|
8122
|
-
|
|
8123
|
-
// that shares the provider/credential but targets a fallback model.
|
|
8124
|
-
videoAdapter(route, model) {
|
|
8030
|
+
videoAdapter(route) {
|
|
8125
8031
|
if (route.providerConfig.protocol !== "google-gemini") {
|
|
8126
8032
|
throw configRuntimeError(
|
|
8127
8033
|
"Video tasks require a google-gemini provider.",
|
|
@@ -8129,14 +8035,13 @@ var ModelRouter = class {
|
|
|
8129
8035
|
[`provider: ${route.info.provider}`, `protocol: ${route.providerConfig.protocol}`]
|
|
8130
8036
|
);
|
|
8131
8037
|
}
|
|
8132
|
-
const modelName =
|
|
8038
|
+
const modelName = route.info.model;
|
|
8133
8039
|
const key = `${route.info.provider} ${route.providerConfig.baseUrl ?? "official"} ${modelName}`;
|
|
8134
8040
|
const cached2 = this.videoAdapters.get(key);
|
|
8135
8041
|
if (cached2) return cached2;
|
|
8136
8042
|
const apiKey = this.videoCredential(route);
|
|
8137
8043
|
if (!apiKey) throw credentialError(route.info.provider);
|
|
8138
|
-
const
|
|
8139
|
-
const adapter = new GeminiVideoModel(route.providerConfig, modelConfig, apiKey);
|
|
8044
|
+
const adapter = new GeminiVideoModel(route.providerConfig, route.modelConfig, apiKey);
|
|
8140
8045
|
this.videoAdapters.set(key, adapter);
|
|
8141
8046
|
return adapter;
|
|
8142
8047
|
}
|
|
@@ -8151,8 +8056,8 @@ var ModelRouter = class {
|
|
|
8151
8056
|
if (spec2.mode !== "video") throw configRuntimeError("The task does not support video generation.", ["video task"], [`task: ${task}`]);
|
|
8152
8057
|
const route = this.resolve(task);
|
|
8153
8058
|
if (this.options.videoProvider?.provider === "doubao") {
|
|
8154
|
-
const
|
|
8155
|
-
if (!
|
|
8059
|
+
const adapter2 = this.doubaoAdapter(route);
|
|
8060
|
+
if (!adapter2) throw credentialError("doubao");
|
|
8156
8061
|
return {
|
|
8157
8062
|
prepare: async (localPath, mimeType, mode, signal) => {
|
|
8158
8063
|
if (mode !== "file-api") {
|
|
@@ -8167,7 +8072,7 @@ var ModelRouter = class {
|
|
|
8167
8072
|
}
|
|
8168
8073
|
request2.signal?.throwIfAborted();
|
|
8169
8074
|
const { path: localPath, mimeType: localMime } = request2.media;
|
|
8170
|
-
return this.tracked(route.info, () =>
|
|
8075
|
+
return this.tracked(route.info, () => adapter2.run({
|
|
8171
8076
|
localPath,
|
|
8172
8077
|
localMime,
|
|
8173
8078
|
user: request2.user,
|
|
@@ -8189,88 +8094,17 @@ var ModelRouter = class {
|
|
|
8189
8094
|
[`provider: ${route.info.provider}`, `protocol: ${route.providerConfig.protocol}`]
|
|
8190
8095
|
);
|
|
8191
8096
|
}
|
|
8192
|
-
const
|
|
8193
|
-
const proxyRoutes = this.proxyVideoRoutes(task);
|
|
8194
|
-
if (this.options.videoProvider || proxyRoutes.length === 0) void this.videoAdapter(route);
|
|
8097
|
+
const adapter = this.videoAdapter(route);
|
|
8195
8098
|
return {
|
|
8196
|
-
prepare:
|
|
8197
|
-
|
|
8198
|
-
|
|
8199
|
-
|
|
8200
|
-
|
|
8201
|
-
|
|
8202
|
-
const failure = routedFailure(error, route.info);
|
|
8203
|
-
process.stderr.write(`\u21AA\uFE0F Gemini \u6587\u4EF6\u4E0A\u4F20\u5931\u8D25,\u6539\u7531\u8C46\u5305\u8BFB\u53D6\u672C\u5730\u89C6\u9891 (${formatFailure(failure)})
|
|
8204
|
-
`);
|
|
8205
|
-
return { kind: "local-fallback", path: localPath, mimeType, primaryFailure: failure };
|
|
8206
|
-
}
|
|
8207
|
-
},
|
|
8208
|
-
release: (media, signal) => media.kind === "base64" || media.kind === "local-fallback" ? Promise.resolve() : this.videoAdapter(route).release(media, signal),
|
|
8209
|
-
generate: async (request2) => {
|
|
8210
|
-
const failures = request2.media.kind === "local-fallback" ? [request2.media.primaryFailure] : [];
|
|
8211
|
-
const skipped = [];
|
|
8212
|
-
const geminiChain = request2.media.kind === "local-fallback" ? [] : request2.media.kind === "base64" ? [...proxyRoutes, ...officialRoutes] : officialRoutes;
|
|
8213
|
-
for (const [index, attemptRoute] of geminiChain.entries()) {
|
|
8214
|
-
try {
|
|
8215
|
-
const response = await this.tracked(
|
|
8216
|
-
attemptRoute.info,
|
|
8217
|
-
() => this.videoAdapter(attemptRoute).generate({ ...request2, maxOutputTokens: spec2.maxOutputTokens })
|
|
8218
|
-
);
|
|
8219
|
-
if (index > 0) process.stderr.write(`\u21AA\uFE0F \u5DF2\u5207\u6362\u5230\u5907\u7528\u6A21\u578B ${attemptRoute.info.model}
|
|
8220
|
-
`);
|
|
8221
|
-
return response;
|
|
8222
|
-
} catch (error) {
|
|
8223
|
-
if (request2.signal?.aborted) throw error;
|
|
8224
|
-
const failure = routedFailure(error, attemptRoute.info);
|
|
8225
|
-
failures.push(failure);
|
|
8226
|
-
const detail = formatFailure(failure);
|
|
8227
|
-
if (index < geminiChain.length - 1) {
|
|
8228
|
-
process.stderr.write(`\u21AA\uFE0F ${attemptRoute.info.model} \u5F53\u524D\u4E0D\u53EF\u7528\uFF0C\u5207\u6362\u4E0B\u4E00\u7EBF\u8DEF (${detail})
|
|
8229
|
-
`);
|
|
8230
|
-
}
|
|
8231
|
-
}
|
|
8232
|
-
}
|
|
8233
|
-
const doubao = this.options.videoProvider ? null : this.doubaoAdapter();
|
|
8234
|
-
const localPath = request2.media.kind === "local-fallback" ? request2.media.path : request2.localPath;
|
|
8235
|
-
if (doubao && localPath) {
|
|
8236
|
-
const attemptRoute = this.doubaoRoute(task);
|
|
8237
|
-
try {
|
|
8238
|
-
process.stderr.write(`\u21AA\uFE0F Gemini \u5168\u90E8\u5931\u8D25,\u56DE\u9000\u5230\u8C46\u5305 ${this.doubaoModel()}
|
|
8239
|
-
`);
|
|
8240
|
-
const response = await this.tracked(attemptRoute, () => doubao.run({
|
|
8241
|
-
localPath,
|
|
8242
|
-
localMime: request2.media.kind === "local-fallback" ? request2.media.mimeType : request2.localMime ?? "video/mp4",
|
|
8243
|
-
user: request2.user,
|
|
8244
|
-
fps: request2.fps,
|
|
8245
|
-
maxOutputTokens: spec2.maxOutputTokens,
|
|
8246
|
-
...request2.system ? { system: request2.system } : {},
|
|
8247
|
-
...request2.responseJsonSchema ? { responseJsonSchema: request2.responseJsonSchema } : {},
|
|
8248
|
-
...request2.signal ? { signal: request2.signal } : {}
|
|
8249
|
-
}));
|
|
8250
|
-
return response;
|
|
8251
|
-
} catch (error) {
|
|
8252
|
-
if (request2.signal?.aborted) throw error;
|
|
8253
|
-
failures.push(routedFailure(error, attemptRoute));
|
|
8254
|
-
}
|
|
8255
|
-
} else if (!doubao) {
|
|
8256
|
-
if (!this.options.videoProvider) {
|
|
8257
|
-
skipped.push("doubao: skipped (ARK_API_KEY not set)");
|
|
8258
|
-
}
|
|
8259
|
-
} else {
|
|
8260
|
-
skipped.push("doubao: skipped (no local file to re-upload)");
|
|
8261
|
-
}
|
|
8262
|
-
const aggregate = aggregateFailures(failures, "All configured video model attempts failed.");
|
|
8263
|
-
throw new LlmTransportError("request_failed", aggregate.message, {
|
|
8264
|
-
retryable: aggregate.retryable,
|
|
8265
|
-
classification: classificationOf(aggregate),
|
|
8266
|
-
context: contextOf(aggregate),
|
|
8267
|
-
modelFailures: failures,
|
|
8268
|
-
details: [...failures.map(formatFailure), ...skipped]
|
|
8269
|
-
});
|
|
8270
|
-
}
|
|
8099
|
+
prepare: (localPath, mimeType, mode, signal) => adapter.prepare(localPath, mimeType, mode, signal),
|
|
8100
|
+
release: (media, signal) => adapter.release(media, signal),
|
|
8101
|
+
generate: (request2) => this.tracked(route.info, () => adapter.generate({
|
|
8102
|
+
...request2,
|
|
8103
|
+
maxOutputTokens: spec2.maxOutputTokens
|
|
8104
|
+
}))
|
|
8271
8105
|
};
|
|
8272
8106
|
}
|
|
8273
|
-
// Resolve
|
|
8107
|
+
// Resolve explicitly selected Doubao video.
|
|
8274
8108
|
// Overridable in tests to inject a fake Ark client.
|
|
8275
8109
|
doubaoAdapter(route) {
|
|
8276
8110
|
return this.arkAdapter(route);
|