dsh-image-gen 0.6.7 → 0.6.9
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/LICENSE +201 -21
- package/README.en.md +391 -375
- package/README.md +391 -375
- package/README.zh-CN.md +3 -3
- package/docs/assets/readme/chat-generate.webp +0 -0
- package/docs/assets/readme/other-features.webp +0 -0
- package/lib/client.js +315 -49
- package/lib/client.js.map +1 -1
- package/lib/index.js +259 -49
- package/lib/types/client/conversation-image-revisions.d.ts +2 -2
- package/lib/types/client/gallery-view.d.ts +4 -0
- package/lib/types/client/index.d.ts +7 -1
- package/lib/types/client/multi-model-compare.d.ts +3 -3
- package/lib/types/client/provider-pill.d.ts +1 -1
- package/lib/types/client/studio-chat-panel.d.ts +23 -0
- package/lib/types/client/studio-view.d.ts +4 -0
- package/lib/types/client/tl/diagram-landing.d.ts +75 -0
- package/lib/types/client/tl/diagram-layout.d.ts +76 -0
- package/lib/types/client/tl/diagram-measure.d.ts +40 -0
- package/lib/types/config.d.ts +19 -2
- package/lib/types/diagram.d.ts +42 -0
- package/lib/types/openai-compatible.d.ts +3 -0
- package/lib/types/seedream.d.ts +7 -0
- package/lib/types/shared.d.ts +55 -4
- package/lib/types/studio-chat.d.ts +48 -0
- package/lib/types/studio.d.ts +3 -2
- package/lib/types/subscription/manager.d.ts +15 -0
- package/lib/types/subscription/vendors/antigravity.d.ts +10 -1
- package/lib/types/subscription/vendors/codex.d.ts +2 -0
- package/lib/types/subscription/vendors/grok.d.ts +2 -0
- package/lib/types/subscription.d.ts +7 -5
- package/package.json +11 -9
- package/skills/install-dsh-image-gen/SKILL.md +22 -22
- package/lib/types/client/inspiration-api.d.ts +0 -10
- package/lib/types/client/inspiration-detail.d.ts +0 -14
- package/lib/types/client/tldraw/generated/tldraw-assets.d.ts +0 -12
- package/lib/types/client/tldraw/generated/tldraw-css.d.ts +0 -1
- package/lib/types/client/tldraw/tldraw-style.d.ts +0 -1
- package/lib/types/client/tldraw/tldraw-workspace.d.ts +0 -1
- package/lib/types/inspiration/cache.d.ts +0 -20
- package/lib/types/inspiration/downloader.d.ts +0 -27
- package/lib/types/inspiration/routes.d.ts +0 -13
- package/lib/types/inspiration/sources.d.ts +0 -33
- package/lib/types/inspiration/types.d.ts +0 -65
- package/lib/types/inspiration/updater.d.ts +0 -7
package/lib/index.js
CHANGED
|
@@ -6006,6 +6006,15 @@ const CLOUD_IMAGE_PROVIDERS = [
|
|
|
6006
6006
|
"zhipu"
|
|
6007
6007
|
];
|
|
6008
6008
|
/**
|
|
6009
|
+
* Providers the browser workbench can drive: the BYOK cloud set plus the
|
|
6010
|
+
* logged-in subscription channels. ComfyUI stays out; it has its own workflow
|
|
6011
|
+
* pipeline and no shared request shape.
|
|
6012
|
+
*/
|
|
6013
|
+
const STUDIO_PROVIDERS = [...CLOUD_IMAGE_PROVIDERS, ...SUBSCRIPTION_PROVIDERS];
|
|
6014
|
+
/** Timeout for one subscription image call, shared by the tool path and the
|
|
6015
|
+
* studio route so neither drifts from the other. */
|
|
6016
|
+
const SUBSCRIPTION_TIMEOUT_MS = 3e5;
|
|
6017
|
+
/**
|
|
6009
6018
|
* Credential references resolved through the DSH Credentials service (BYOK).
|
|
6010
6019
|
* These are POSIX-style reference names, not environment variables: the host
|
|
6011
6020
|
* layers the process environment and its managed store behind them, so a name
|
|
@@ -6068,6 +6077,33 @@ const DEFAULT_ZHIPU_BASE_URL = "https://open.bigmodel.cn/api/paas/v4";
|
|
|
6068
6077
|
const DEFAULT_COMFYUI_BASE_URL = "http://127.0.0.1:8188";
|
|
6069
6078
|
const DEFAULT_COMFYUI_TIMEOUT_MS = 3e5;
|
|
6070
6079
|
const DEFAULT_COMFYUI_WORKFLOW_LABEL = "API workflow";
|
|
6080
|
+
/** Content types Ark's Seedream endpoint can return. */
|
|
6081
|
+
const ARK_OUTPUT_FORMATS = ["png", "jpeg"];
|
|
6082
|
+
/** Whether Ark stamps an "AI generated" watermark on the result. */
|
|
6083
|
+
const ARK_BACKGROUND_MODES = ["opaque", "transparent"];
|
|
6084
|
+
/**
|
|
6085
|
+
* Map the Ark output controls onto request-body fields.
|
|
6086
|
+
*
|
|
6087
|
+
* `background` is opt-in per call site because Ark restricts `transparent` to
|
|
6088
|
+
* image-to-image with a single alpha-bearing reference and rejects the whole
|
|
6089
|
+
* request otherwise:
|
|
6090
|
+
*
|
|
6091
|
+
* text-to-image + transparent → 400 InvalidParameter
|
|
6092
|
+
* "transparent background requires exactly one input image"
|
|
6093
|
+
*
|
|
6094
|
+
* So the generation endpoint must never carry it, and that caller passes
|
|
6095
|
+
* `{ background: false }`. `background: opaque` is never emitted anywhere: it
|
|
6096
|
+
* is already Ark's default, so sending it would change nothing while adding a
|
|
6097
|
+
* field only the edit path can act on.
|
|
6098
|
+
*/
|
|
6099
|
+
function arkOutputBody(options, { background = true } = {}) {
|
|
6100
|
+
if (options === void 0) return {};
|
|
6101
|
+
const body = {};
|
|
6102
|
+
if (options.outputFormat !== void 0) body.output_format = options.outputFormat;
|
|
6103
|
+
if (options.watermark !== void 0) body.watermark = options.watermark;
|
|
6104
|
+
if (background && options.background === "transparent") body.background = "transparent";
|
|
6105
|
+
return body;
|
|
6106
|
+
}
|
|
6071
6107
|
/** Default model names. */
|
|
6072
6108
|
const DEFAULT_GOOGLE_MODEL = "gemini-3.1-flash-image";
|
|
6073
6109
|
const DEFAULT_OPENAI_MODEL = "gpt-image-2";
|
|
@@ -6161,6 +6197,9 @@ const Config = z.object({
|
|
|
6161
6197
|
openaiCompatEditExtra: z.dict(z.any()).default({}),
|
|
6162
6198
|
seedreamBaseURL: z.string().default(DEFAULT_SEEDREAM_BASE_URL),
|
|
6163
6199
|
seedreamModel: z.string().default(DEFAULT_SEEDREAM_MODEL),
|
|
6200
|
+
seedreamOutputFormat: z.union(ARK_OUTPUT_FORMATS).default("jpeg"),
|
|
6201
|
+
seedreamWatermark: z.boolean().default(true),
|
|
6202
|
+
seedreamBackground: z.union(ARK_BACKGROUND_MODES).default("opaque"),
|
|
6164
6203
|
dashscopeEndpoint: z.string().default(DEFAULT_DASHSCOPE_ENDPOINT),
|
|
6165
6204
|
dashscopeModel: z.string().default(DEFAULT_DASHSCOPE_MODEL),
|
|
6166
6205
|
xaiBaseURL: z.string().default(DEFAULT_XAI_BASE_URL),
|
|
@@ -6205,13 +6244,21 @@ function resolveProvider(config) {
|
|
|
6205
6244
|
editExtra: config.openaiCompatEditExtra ?? {}
|
|
6206
6245
|
};
|
|
6207
6246
|
}
|
|
6208
|
-
case "seedream":
|
|
6209
|
-
|
|
6210
|
-
|
|
6211
|
-
|
|
6212
|
-
|
|
6213
|
-
|
|
6214
|
-
|
|
6247
|
+
case "seedream": {
|
|
6248
|
+
const seedreamBackground = config.seedreamBackground ?? "opaque";
|
|
6249
|
+
return {
|
|
6250
|
+
provider: "seedream",
|
|
6251
|
+
apiKeyEnv: SEEDREAM_API_KEY_ENV,
|
|
6252
|
+
model: config.seedreamModel ?? "doubao-seedream-5-0-260128",
|
|
6253
|
+
baseURL: config.seedreamBaseURL ?? "https://ark.cn-beijing.volces.com/api/v3",
|
|
6254
|
+
imageSize: "2K",
|
|
6255
|
+
arkOptions: {
|
|
6256
|
+
outputFormat: seedreamBackground === "transparent" ? "png" : config.seedreamOutputFormat ?? "jpeg",
|
|
6257
|
+
watermark: config.seedreamWatermark ?? true,
|
|
6258
|
+
background: seedreamBackground
|
|
6259
|
+
}
|
|
6260
|
+
};
|
|
6261
|
+
}
|
|
6215
6262
|
case "dashscope": return {
|
|
6216
6263
|
provider: "dashscope",
|
|
6217
6264
|
apiKeyEnv: DASHSCOPE_API_KEY_ENV,
|
|
@@ -7510,7 +7557,7 @@ async function editDashScopeImage(options) {
|
|
|
7510
7557
|
model: options.model,
|
|
7511
7558
|
input: { messages: [{
|
|
7512
7559
|
role: "user",
|
|
7513
|
-
content: [...options.sourceImages.map((sourceImage) => ({ image: toDataUrl$
|
|
7560
|
+
content: [...options.sourceImages.map((sourceImage) => ({ image: toDataUrl$3(sourceImage) })), { text: options.prompt }]
|
|
7514
7561
|
}] },
|
|
7515
7562
|
parameters: {
|
|
7516
7563
|
prompt_extend: true,
|
|
@@ -7527,7 +7574,7 @@ function formatSize(size) {
|
|
|
7527
7574
|
if (size === void 0 || size.length === 0) return void 0;
|
|
7528
7575
|
return size.replace("x", "*");
|
|
7529
7576
|
}
|
|
7530
|
-
function toDataUrl$
|
|
7577
|
+
function toDataUrl$3(image) {
|
|
7531
7578
|
return `data:${image.mediaType};base64,${Buffer.from(image.data).toString("base64")}`;
|
|
7532
7579
|
}
|
|
7533
7580
|
async function requestQwenImage(options) {
|
|
@@ -8109,7 +8156,10 @@ async function generateOpenAICompatibleImage(input) {
|
|
|
8109
8156
|
model: input.model,
|
|
8110
8157
|
prompt: input.prompt,
|
|
8111
8158
|
size: input.size,
|
|
8112
|
-
...input.provider === "seedream" ? {
|
|
8159
|
+
...input.provider === "seedream" ? {
|
|
8160
|
+
response_format: "url",
|
|
8161
|
+
...arkOutputBody(input.arkOptions, { background: false })
|
|
8162
|
+
} : {}
|
|
8113
8163
|
})
|
|
8114
8164
|
}), input.provider, input);
|
|
8115
8165
|
}
|
|
@@ -8117,7 +8167,7 @@ async function editOpenAICompatibleImage(input) {
|
|
|
8117
8167
|
if (input.editFormat === "jsonImageUrlArray") {
|
|
8118
8168
|
const body = {
|
|
8119
8169
|
model: input.model,
|
|
8120
|
-
images: input.sourceImages.map((sourceImage) => ({ image_url: toDataUrl$
|
|
8170
|
+
images: input.sourceImages.map((sourceImage) => ({ image_url: toDataUrl$2(sourceImage) })),
|
|
8121
8171
|
prompt: input.prompt,
|
|
8122
8172
|
n: 1,
|
|
8123
8173
|
size: "auto",
|
|
@@ -8178,7 +8228,7 @@ function imageEndpoint$1(baseURL, operation) {
|
|
|
8178
8228
|
throw new Error("Image endpoint must be an absolute URL");
|
|
8179
8229
|
}
|
|
8180
8230
|
}
|
|
8181
|
-
function toDataUrl$
|
|
8231
|
+
function toDataUrl$2(image) {
|
|
8182
8232
|
return `data:${image.mediaType};base64,${Buffer.from(image.data).toString("base64")}`;
|
|
8183
8233
|
}
|
|
8184
8234
|
function firstImage$1(value) {
|
|
@@ -8295,8 +8345,9 @@ async function editSeedreamImage(input) {
|
|
|
8295
8345
|
body: JSON.stringify({
|
|
8296
8346
|
model: input.model,
|
|
8297
8347
|
prompt: input.prompt,
|
|
8298
|
-
image: input.sourceImages.map(toDataUrl),
|
|
8348
|
+
image: input.sourceImages.map(toDataUrl$1),
|
|
8299
8349
|
...input.size === void 0 || input.size.length === 0 ? {} : { size: input.size },
|
|
8350
|
+
...arkOutputBody(input.arkOptions),
|
|
8300
8351
|
response_format: "b64_json"
|
|
8301
8352
|
})
|
|
8302
8353
|
});
|
|
@@ -8323,7 +8374,7 @@ function imageEndpoint(baseURL) {
|
|
|
8323
8374
|
throw new Error("Seedream image endpoint must be an absolute URL");
|
|
8324
8375
|
}
|
|
8325
8376
|
}
|
|
8326
|
-
function toDataUrl(image) {
|
|
8377
|
+
function toDataUrl$1(image) {
|
|
8327
8378
|
return `data:${image.mediaType};base64,${Buffer.from(image.data).toString("base64")}`;
|
|
8328
8379
|
}
|
|
8329
8380
|
function firstImage(value) {
|
|
@@ -8598,6 +8649,8 @@ const CODEX_TOKEN = "https://auth.openai.com/oauth/token";
|
|
|
8598
8649
|
const CODEX_SCOPE = "openid profile email offline_access api.connectors.read api.connectors.invoke";
|
|
8599
8650
|
/** Where the ChatGPT subscription image request goes. */
|
|
8600
8651
|
const CODEX_IMAGE_URL = "https://chatgpt.com/backend-api/codex/images/generations";
|
|
8652
|
+
/** Where the ChatGPT subscription image edit request goes. */
|
|
8653
|
+
const CODEX_IMAGE_EDIT_URL = "https://chatgpt.com/backend-api/codex/images/edits";
|
|
8601
8654
|
/** The model served by this endpoint (probed live: gpt-image-2.5-flare works
|
|
8602
8655
|
* on the ChatGPT internal generations route as of 2026-09-14). */
|
|
8603
8656
|
const CODEX_IMAGE_MODEL = "gpt-image-2.5-flare";
|
|
@@ -8695,6 +8748,8 @@ const GROK_TOKEN = "https://auth.x.ai/oauth2/token";
|
|
|
8695
8748
|
const GROK_SCOPE = "openid profile email offline_access grok-cli:access api:access conversations:read conversations:write";
|
|
8696
8749
|
/** Where the Grok subscription image request goes. */
|
|
8697
8750
|
const GROK_IMAGE_URL = "https://api.x.ai/v1/images/generations";
|
|
8751
|
+
/** Where the Grok subscription image edit request goes. */
|
|
8752
|
+
const GROK_IMAGE_EDIT_URL = "https://api.x.ai/v1/images/edits";
|
|
8698
8753
|
/** The model served by this endpoint. */
|
|
8699
8754
|
const GROK_IMAGE_MODEL = "grok-imagine-image-2.0";
|
|
8700
8755
|
/** Public client id of the grok-cli; vendor-fixed redirect on 127.0.0.1:56121. */
|
|
@@ -9039,15 +9094,22 @@ function platformOf() {
|
|
|
9039
9094
|
* Build the streamGenerateContent request body for one image generation.
|
|
9040
9095
|
* Ported from the reference buildImageRequest + the wrapping envelope: no
|
|
9041
9096
|
* tools, no systemInstruction, no thinkingConfig; responseModalities asks
|
|
9042
|
-
* for TEXT+IMAGE.
|
|
9097
|
+
* for TEXT+IMAGE. Reference images (edits) ride along as inlineData parts
|
|
9098
|
+
* before the text part, mirroring the reference implementation.
|
|
9043
9099
|
*/
|
|
9044
9100
|
function antigravityImageBody(options) {
|
|
9045
9101
|
const imageConfig = { aspectRatio: options.aspectRatio !== void 0 && ASPECT_RATIOS.includes(options.aspectRatio) ? options.aspectRatio : "1:1" };
|
|
9046
9102
|
if (options.hd === true) imageConfig.imageSize = "4K";
|
|
9103
|
+
const parts = [];
|
|
9104
|
+
for (const image of options.referenceImages ?? []) parts.push({ inlineData: {
|
|
9105
|
+
mimeType: image.mediaType,
|
|
9106
|
+
data: Buffer.from(image.data).toString("base64")
|
|
9107
|
+
} });
|
|
9108
|
+
parts.push({ text: options.prompt });
|
|
9047
9109
|
return {
|
|
9048
9110
|
contents: [{
|
|
9049
9111
|
role: "user",
|
|
9050
|
-
parts
|
|
9112
|
+
parts
|
|
9051
9113
|
}],
|
|
9052
9114
|
generationConfig: {
|
|
9053
9115
|
candidateCount: 1,
|
|
@@ -9081,7 +9143,8 @@ async function antigravityGenerateImage(options) {
|
|
|
9081
9143
|
const inner = antigravityImageBody({
|
|
9082
9144
|
prompt: options.prompt,
|
|
9083
9145
|
...options.aspectRatio !== void 0 ? { aspectRatio: options.aspectRatio } : {},
|
|
9084
|
-
...options.hd !== void 0 ? { hd: options.hd } : {}
|
|
9146
|
+
...options.hd !== void 0 ? { hd: options.hd } : {},
|
|
9147
|
+
...options.referenceImages !== void 0 ? { referenceImages: options.referenceImages } : {}
|
|
9085
9148
|
});
|
|
9086
9149
|
const body = antigravityEnvelope(options.projectId, inner);
|
|
9087
9150
|
let lastError = "no endpoint succeeded";
|
|
@@ -9296,6 +9359,8 @@ var SubscriptionManager = class {
|
|
|
9296
9359
|
const session = await this.ensureFresh(vendor);
|
|
9297
9360
|
const text = prompt.trim();
|
|
9298
9361
|
if (text.length === 0) throw new Error("prompt must be a non-empty string");
|
|
9362
|
+
const references = options.referenceImages ?? [];
|
|
9363
|
+
if (references.length > 5) throw new Error(`订阅生图最多支持 ${String(5)} 张参考图,当前 ${String(references.length)} 张`);
|
|
9299
9364
|
if (vendor === "antigravity") {
|
|
9300
9365
|
const projectId = await this.ensureAntigravityProject(session);
|
|
9301
9366
|
const aspectRatio = antigravityAspectRatioOf(options.size);
|
|
@@ -9305,6 +9370,7 @@ var SubscriptionManager = class {
|
|
|
9305
9370
|
prompt: text,
|
|
9306
9371
|
...aspectRatio !== void 0 ? { aspectRatio } : {},
|
|
9307
9372
|
hd: options.quality === "hd" || options.quality === "high",
|
|
9373
|
+
...references.length > 0 ? { referenceImages: references } : {},
|
|
9308
9374
|
...options.signal !== void 0 ? { signal: options.signal } : {}
|
|
9309
9375
|
})).b64 }];
|
|
9310
9376
|
}
|
|
@@ -9312,16 +9378,17 @@ var SubscriptionManager = class {
|
|
|
9312
9378
|
let headers;
|
|
9313
9379
|
let body;
|
|
9314
9380
|
if (vendor === "codex") {
|
|
9315
|
-
url = CODEX_IMAGE_URL;
|
|
9381
|
+
url = references.length > 0 ? CODEX_IMAGE_EDIT_URL : CODEX_IMAGE_URL;
|
|
9316
9382
|
headers = codexIdentityHeaders(session);
|
|
9317
9383
|
body = {
|
|
9318
9384
|
prompt: text,
|
|
9319
9385
|
model: CODEX_IMAGE_MODEL,
|
|
9386
|
+
...references.length > 0 ? { images: references.map((image) => ({ image_url: toDataUrl(image) })) } : {},
|
|
9320
9387
|
...options.size !== void 0 && options.size.length > 0 ? { size: options.size } : {},
|
|
9321
9388
|
...options.quality !== void 0 && options.quality.length > 0 ? { quality: options.quality } : {}
|
|
9322
9389
|
};
|
|
9323
9390
|
} else {
|
|
9324
|
-
url = GROK_IMAGE_URL;
|
|
9391
|
+
url = references.length > 0 ? GROK_IMAGE_EDIT_URL : GROK_IMAGE_URL;
|
|
9325
9392
|
headers = grokIdentityHeaders(session);
|
|
9326
9393
|
const aspect = {
|
|
9327
9394
|
"1024x1024": "1:1",
|
|
@@ -9334,6 +9401,10 @@ var SubscriptionManager = class {
|
|
|
9334
9401
|
prompt: text,
|
|
9335
9402
|
model: GROK_IMAGE_MODEL,
|
|
9336
9403
|
response_format: "b64_json",
|
|
9404
|
+
...references.length > 0 ? { images: references.map((image) => ({
|
|
9405
|
+
type: "image_url",
|
|
9406
|
+
image_url: toDataUrl(image)
|
|
9407
|
+
})) } : {},
|
|
9337
9408
|
...options.size !== void 0 && aspect[options.size] !== void 0 ? { aspect_ratio: aspect[options.size] } : {},
|
|
9338
9409
|
...level !== void 0 ? { quality: level } : {}
|
|
9339
9410
|
};
|
|
@@ -9380,6 +9451,10 @@ function antigravityAspectRatioOf(size) {
|
|
|
9380
9451
|
if (mapped !== void 0) return mapped;
|
|
9381
9452
|
return /^\d+:\d+$/.test(size) ? size : "1:1";
|
|
9382
9453
|
}
|
|
9454
|
+
/** Encode one reference image as the data URL the edit endpoints accept. */
|
|
9455
|
+
function toDataUrl(image) {
|
|
9456
|
+
return `data:${image.mediaType};base64,${Buffer.from(image.data).toString("base64")}`;
|
|
9457
|
+
}
|
|
9383
9458
|
/** Response parsing: both vendors reply in the same `{data:[{b64_json}]}` shape. */
|
|
9384
9459
|
function parseImages(payload) {
|
|
9385
9460
|
const body = typeof payload === "object" && payload !== null ? payload : {};
|
|
@@ -9524,20 +9599,21 @@ function jsonError$3(res, status, code) {
|
|
|
9524
9599
|
}
|
|
9525
9600
|
//#endregion
|
|
9526
9601
|
//#region lib/types/subscription.js
|
|
9527
|
-
/** Timeout for one subscription image call, mirroring the other adapters. */
|
|
9528
|
-
const SUBSCRIPTION_TIMEOUT_MS = 3e5;
|
|
9529
9602
|
/**
|
|
9530
9603
|
* Generate one image through a logged-in subscription account. Returns the
|
|
9531
9604
|
* same `{ data, mediaType }` contract as the API-key adapters so the caller
|
|
9532
|
-
* can feed it straight into `saveGenerated`.
|
|
9605
|
+
* can feed it straight into `saveGenerated`. With sourceImages the call is
|
|
9606
|
+
* an edit: the vendor layer picks the channel's edit endpoint.
|
|
9533
9607
|
*/
|
|
9534
9608
|
async function generateSubscriptionImage(options) {
|
|
9535
9609
|
const { manager, provider, prompt, maxBytes, signal } = options;
|
|
9536
9610
|
const size = options.size?.trim();
|
|
9611
|
+
const sourceImages = options.sourceImages ?? [];
|
|
9537
9612
|
const result = await withTimeout(manager.generate({
|
|
9538
9613
|
vendor: vendorOf(provider),
|
|
9539
9614
|
prompt,
|
|
9540
9615
|
...size !== void 0 && size.length > 0 ? { size } : {},
|
|
9616
|
+
...sourceImages.length > 0 ? { referenceImages: sourceImages } : {},
|
|
9541
9617
|
signal
|
|
9542
9618
|
}), signal);
|
|
9543
9619
|
const first = Array.isArray(result) ? result[0] : void 0;
|
|
@@ -9552,10 +9628,6 @@ async function generateSubscriptionImage(options) {
|
|
|
9552
9628
|
...typeof first.revisedPrompt === "string" && first.revisedPrompt.length > 0 ? { revisedPrompt: first.revisedPrompt } : {}
|
|
9553
9629
|
};
|
|
9554
9630
|
}
|
|
9555
|
-
/** Subscription channels are prompt-only today; edit_image is rejected outright. */
|
|
9556
|
-
function assertSubscriptionEditUnsupported(provider) {
|
|
9557
|
-
throw new Error(`edit_image with ${SUBSCRIPTION_PROVIDER_DISPLAY_NAMES[provider]} is not supported yet: the subscription channel only accepts text prompts. Retry with an API-key provider (for example google or openai) through the provider argument.`);
|
|
9558
|
-
}
|
|
9559
9631
|
/** Run one promise with the subscription timeout and the call's abort signal. */
|
|
9560
9632
|
async function withTimeout(promise, signal) {
|
|
9561
9633
|
let timer;
|
|
@@ -20467,24 +20539,63 @@ const RATIO_LABELS = {
|
|
|
20467
20539
|
"9:16": "9:16 竖屏"
|
|
20468
20540
|
};
|
|
20469
20541
|
/** Return only browser-safe capability data. */
|
|
20470
|
-
async function describeStudio(ctx, config) {
|
|
20542
|
+
async function describeStudio(ctx, config, subscriptions) {
|
|
20471
20543
|
const configuredEntries = await Promise.all(CLOUD_IMAGE_PROVIDERS.map(async (provider) => {
|
|
20472
20544
|
return [provider, await resolveApiKey(ctx, provider) !== void 0];
|
|
20473
20545
|
}));
|
|
20474
20546
|
const configured = Object.fromEntries(configuredEntries);
|
|
20475
|
-
const
|
|
20547
|
+
const cloudProfiles = CLOUD_IMAGE_PROVIDERS.map((provider) => studioProfile(config, provider, configured[provider]));
|
|
20548
|
+
const subStatuses = await Promise.all(SUBSCRIPTION_PROVIDERS.map(async (provider) => {
|
|
20549
|
+
return [provider, subscriptions !== void 0 && (await subscriptions.loginStatus(vendorOf(provider))).state === "logged-in"];
|
|
20550
|
+
}));
|
|
20551
|
+
const subProfiles = SUBSCRIPTION_PROVIDERS.map((provider) => {
|
|
20552
|
+
return subscriptionStudioProfile(provider, subStatuses.find((entry) => entry[0] === provider)?.[1] === true);
|
|
20553
|
+
});
|
|
20554
|
+
const profiles = [...cloudProfiles, ...subProfiles];
|
|
20476
20555
|
const preferred = config.provider;
|
|
20477
20556
|
return {
|
|
20478
20557
|
providers: profiles,
|
|
20479
|
-
activeProvider:
|
|
20558
|
+
activeProvider: isStudioPreferred(preferred) ? preferred : profiles.find((profile) => profile.configured)?.provider ?? "google"
|
|
20559
|
+
};
|
|
20560
|
+
}
|
|
20561
|
+
/** The persisted default provider is usable directly when the workbench can drive it. */
|
|
20562
|
+
function isStudioPreferred(preferred) {
|
|
20563
|
+
return preferred !== void 0 && CLOUD_IMAGE_PROVIDERS.includes(preferred) || preferred !== void 0 && SUBSCRIPTION_PROVIDERS.includes(preferred);
|
|
20564
|
+
}
|
|
20565
|
+
/** Fixed parameter shape for subscription channels: one "channel default" pair. */
|
|
20566
|
+
function subscriptionStudioProfile(provider, signedIn) {
|
|
20567
|
+
const channelDefault = {
|
|
20568
|
+
value: "auto",
|
|
20569
|
+
label: "通道默认"
|
|
20570
|
+
};
|
|
20571
|
+
return {
|
|
20572
|
+
provider,
|
|
20573
|
+
label: SUBSCRIPTION_DISPLAY_LABELS[provider],
|
|
20574
|
+
model: DEFAULT_SUBSCRIPTION_MODELS[provider],
|
|
20575
|
+
configured: signedIn,
|
|
20576
|
+
supportsEditing: true,
|
|
20577
|
+
ratioOptions: [channelDefault],
|
|
20578
|
+
qualityOptions: [channelDefault],
|
|
20579
|
+
defaultRatio: "auto",
|
|
20580
|
+
defaultQuality: "auto"
|
|
20480
20581
|
};
|
|
20481
20582
|
}
|
|
20583
|
+
/** Short display names for the workbench subscription rows. */
|
|
20584
|
+
const SUBSCRIPTION_DISPLAY_LABELS = {
|
|
20585
|
+
"chatgpt-sub": "ChatGPT 订阅",
|
|
20586
|
+
"grok-sub": "Grok 订阅",
|
|
20587
|
+
"google-sub": "Google 订阅"
|
|
20588
|
+
};
|
|
20482
20589
|
/** Execute one validated browser workbench request using the existing provider adapters. */
|
|
20483
|
-
async function generateFromStudio(ctx, config, input, signal, fallbackWorkspaceRoot) {
|
|
20590
|
+
async function generateFromStudio(ctx, config, input, signal, fallbackWorkspaceRoot, subscriptions) {
|
|
20591
|
+
if (isSubscriptionProvider(input.provider)) {
|
|
20592
|
+
if (subscriptions === void 0) throw new Error("订阅生图不可用:订阅管理器未初始化");
|
|
20593
|
+
assertAllowed(subscriptionStudioProfile(input.provider, true), input);
|
|
20594
|
+
return generateSubscriptionFromStudio(ctx, subscriptions, input, signal);
|
|
20595
|
+
}
|
|
20484
20596
|
assertAllowed(studioProfile(config, input.provider, true), input);
|
|
20485
20597
|
const active = resolveProvider(withProviderOverrides(config, input.provider, input.model));
|
|
20486
20598
|
if (active.provider === "comfyui") throw new Error("ComfyUI 暂未接入工作台");
|
|
20487
|
-
if (isSubscriptionProvider(active.provider)) throw new Error("订阅生图暂未接入工作台,请通过对话或设置切换默认 Provider 使用");
|
|
20488
20599
|
const wired = active;
|
|
20489
20600
|
const credential = await requireApiKey(ctx, input.provider);
|
|
20490
20601
|
const rawRefs = input.references ?? (input.reference ? [input.reference] : []);
|
|
@@ -20555,7 +20666,8 @@ async function generateFromStudio(ctx, config, input, signal, fallbackWorkspaceR
|
|
|
20555
20666
|
sourceImages,
|
|
20556
20667
|
size,
|
|
20557
20668
|
maxBytes: ctx.attachments.imageLimits.maxImageBytes,
|
|
20558
|
-
signal
|
|
20669
|
+
signal,
|
|
20670
|
+
arkOptions: wired.arkOptions
|
|
20559
20671
|
}) : await generateOpenAICompatibleImage({
|
|
20560
20672
|
provider: "seedream",
|
|
20561
20673
|
apiKey: credential,
|
|
@@ -20564,7 +20676,8 @@ async function generateFromStudio(ctx, config, input, signal, fallbackWorkspaceR
|
|
|
20564
20676
|
prompt: input.prompt,
|
|
20565
20677
|
size,
|
|
20566
20678
|
maxBytes: ctx.attachments.imageLimits.maxImageBytes,
|
|
20567
|
-
signal
|
|
20679
|
+
signal,
|
|
20680
|
+
arkOptions: wired.arkOptions
|
|
20568
20681
|
});
|
|
20569
20682
|
output = size;
|
|
20570
20683
|
} else {
|
|
@@ -20672,6 +20785,76 @@ async function runPool(tasks, concurrency = 2) {
|
|
|
20672
20785
|
await Promise.all(workers);
|
|
20673
20786
|
return results;
|
|
20674
20787
|
}
|
|
20788
|
+
/**
|
|
20789
|
+
* Subscription workbench generation: one prompt, channel-default parameters,
|
|
20790
|
+
* through the shared generateSubscriptionImage wrapper (timeout, b64 decode,
|
|
20791
|
+
* size check) so the downstream attachment flow is identical to API keys.
|
|
20792
|
+
*/
|
|
20793
|
+
async function generateSubscriptionFromStudio(ctx, subscriptions, input, signal) {
|
|
20794
|
+
const provider = input.provider;
|
|
20795
|
+
const rawRefs = input.references ?? (input.reference ? [input.reference] : []);
|
|
20796
|
+
if (input.mode === "edit" && rawRefs.length === 0) throw new Error("图生图需要至少一张参考图");
|
|
20797
|
+
const sourceImages = input.mode === "edit" ? await Promise.all(rawRefs.map((ref) => readStudioReference(ctx, ref, signal))) : [];
|
|
20798
|
+
const startedAt = Date.now();
|
|
20799
|
+
const count = input.count ?? 1;
|
|
20800
|
+
const generateSingle = async (index) => {
|
|
20801
|
+
const generated = await generateSubscriptionImage({
|
|
20802
|
+
manager: subscriptions,
|
|
20803
|
+
provider,
|
|
20804
|
+
prompt: input.prompt,
|
|
20805
|
+
...sourceImages.length > 0 ? { sourceImages } : {},
|
|
20806
|
+
maxBytes: ctx.attachments.imageLimits.maxImageBytes,
|
|
20807
|
+
signal
|
|
20808
|
+
});
|
|
20809
|
+
if (!ctx.attachments.imageLimits.mediaTypes.includes(generated.mediaType)) throw new Error(`当前 DSH 不支持保存 ${generated.mediaType} 图片`);
|
|
20810
|
+
return {
|
|
20811
|
+
attachment: await ctx.attachments.saveImage({
|
|
20812
|
+
data: generated.data,
|
|
20813
|
+
mediaType: generated.mediaType,
|
|
20814
|
+
name: count > 1 ? `studio-image-${index + 1}` : "studio-image"
|
|
20815
|
+
}),
|
|
20816
|
+
output: "通道默认"
|
|
20817
|
+
};
|
|
20818
|
+
};
|
|
20819
|
+
if (count === 1) {
|
|
20820
|
+
const single = await generateSingle(0);
|
|
20821
|
+
return subscriptionResponse(input, startedAt, 1, 0, [single], single);
|
|
20822
|
+
}
|
|
20823
|
+
const poolResults = await runPool(Array.from({ length: count }, (_, i) => () => generateSingle(i)), 2);
|
|
20824
|
+
const successes = [];
|
|
20825
|
+
const errors = [];
|
|
20826
|
+
for (let i = 0; i < poolResults.length; i++) {
|
|
20827
|
+
const r = poolResults[i];
|
|
20828
|
+
if (r.status === "fulfilled") successes.push(r.value);
|
|
20829
|
+
else errors.push({
|
|
20830
|
+
index: i,
|
|
20831
|
+
message: r.reason instanceof Error ? r.reason.message : String(r.reason)
|
|
20832
|
+
});
|
|
20833
|
+
}
|
|
20834
|
+
if (successes.length === 0) {
|
|
20835
|
+
const firstReason = poolResults[0] && poolResults[0].status === "rejected" ? poolResults[0].reason : /* @__PURE__ */ new Error("订阅生图全部失败");
|
|
20836
|
+
throw firstReason instanceof Error ? firstReason : new Error(String(firstReason));
|
|
20837
|
+
}
|
|
20838
|
+
const first = successes[0];
|
|
20839
|
+
return subscriptionResponse(input, startedAt, count, errors.length, successes, first, errors);
|
|
20840
|
+
}
|
|
20841
|
+
/** Assemble the subscription workbench response with exactOptionalPropertyTypes-safe spreads. */
|
|
20842
|
+
function subscriptionResponse(input, startedAt, requestedCount, failedCount, items, primary, errors) {
|
|
20843
|
+
return {
|
|
20844
|
+
attachment: primary.attachment,
|
|
20845
|
+
output: primary.output,
|
|
20846
|
+
provider: input.provider,
|
|
20847
|
+
model: input.model,
|
|
20848
|
+
prompt: input.prompt,
|
|
20849
|
+
createdAt: Date.now(),
|
|
20850
|
+
elapsedMs: Date.now() - startedAt,
|
|
20851
|
+
requestedCount,
|
|
20852
|
+
failedCount,
|
|
20853
|
+
items,
|
|
20854
|
+
...primary.savedTo ? { savedTo: primary.savedTo } : {},
|
|
20855
|
+
...errors !== void 0 && errors.length > 0 ? { errors } : {}
|
|
20856
|
+
};
|
|
20857
|
+
}
|
|
20675
20858
|
function studioProfile(config, provider, configured) {
|
|
20676
20859
|
let active;
|
|
20677
20860
|
try {
|
|
@@ -20725,7 +20908,7 @@ function studioProfile(config, provider, configured) {
|
|
|
20725
20908
|
function profile(provider, model, configured, ratioOptions, qualityOptions, defaultRatio, defaultQuality) {
|
|
20726
20909
|
return {
|
|
20727
20910
|
provider,
|
|
20728
|
-
label:
|
|
20911
|
+
label: CLOUD_DISPLAY_LABELS[provider],
|
|
20729
20912
|
model,
|
|
20730
20913
|
configured,
|
|
20731
20914
|
supportsEditing: true,
|
|
@@ -20735,6 +20918,16 @@ function profile(provider, model, configured, ratioOptions, qualityOptions, defa
|
|
|
20735
20918
|
defaultQuality
|
|
20736
20919
|
};
|
|
20737
20920
|
}
|
|
20921
|
+
/** Display names for the BYOK cloud rows, mirroring the settings card. */
|
|
20922
|
+
const CLOUD_DISPLAY_LABELS = {
|
|
20923
|
+
google: "Google Gemini",
|
|
20924
|
+
openai: "OpenAI",
|
|
20925
|
+
"openai-compat": "OpenAI 兼容",
|
|
20926
|
+
seedream: "Seedream",
|
|
20927
|
+
dashscope: "DashScope",
|
|
20928
|
+
xai: "xAI Grok",
|
|
20929
|
+
zhipu: "智谱 GLM"
|
|
20930
|
+
};
|
|
20738
20931
|
function option(value) {
|
|
20739
20932
|
return {
|
|
20740
20933
|
value,
|
|
@@ -20788,9 +20981,6 @@ function dashScopeSize(ratio) {
|
|
|
20788
20981
|
"9:16": "928*1664"
|
|
20789
20982
|
}[ratio] ?? "1024*1024";
|
|
20790
20983
|
}
|
|
20791
|
-
function cloudProvider$1(value) {
|
|
20792
|
-
return CLOUD_IMAGE_PROVIDERS.includes(value);
|
|
20793
|
-
}
|
|
20794
20984
|
//#endregion
|
|
20795
20985
|
//#region lib/types/studio-route.js
|
|
20796
20986
|
/** Serve workbench capabilities and generation requests without exposing provider credentials. */
|
|
@@ -20804,8 +20994,10 @@ async function serveStudio(req, res, deps) {
|
|
|
20804
20994
|
if (req.method !== "POST") return jsonError$1(res, 405, "method-not-allowed");
|
|
20805
20995
|
if (!(req.headers["content-type"] ?? "").toLowerCase().startsWith("application/json")) return jsonError$1(res, 415, "json-required");
|
|
20806
20996
|
const controller = new AbortController();
|
|
20807
|
-
const
|
|
20808
|
-
const
|
|
20997
|
+
const watchdog = new AbortController();
|
|
20998
|
+
const CLOUD_WATCHDOG_MS = 15e4;
|
|
20999
|
+
let subscriptionChannel = false;
|
|
21000
|
+
const signal = AbortSignal.any([controller.signal, watchdog.signal]);
|
|
20809
21001
|
const onConnectionClose = () => {
|
|
20810
21002
|
if (!res.writableEnded) controller.abort(/* @__PURE__ */ new Error("The browser closed the image generation request."));
|
|
20811
21003
|
};
|
|
@@ -20821,11 +21013,15 @@ async function serveStudio(req, res, deps) {
|
|
|
20821
21013
|
} catch (error) {
|
|
20822
21014
|
return jsonError$1(res, 400, errorMessage(error, "invalid-request"));
|
|
20823
21015
|
}
|
|
21016
|
+
subscriptionChannel = isSubscriptionProvider(input.provider);
|
|
21017
|
+
setTimeout(() => {
|
|
21018
|
+
watchdog.abort();
|
|
21019
|
+
}, subscriptionChannel ? SUBSCRIPTION_TIMEOUT_MS : CLOUD_WATCHDOG_MS).unref?.();
|
|
20824
21020
|
const output = await deps.generate(input, signal);
|
|
20825
21021
|
if (!res.headersSent && !res.writableEnded && !res.destroyed) json$1(res, 200, output);
|
|
20826
21022
|
} catch (error) {
|
|
20827
21023
|
if (res.headersSent || res.writableEnded || res.destroyed) return;
|
|
20828
|
-
if (
|
|
21024
|
+
if (watchdog.signal.aborted) return jsonError$1(res, 504, subscriptionChannel ? "上游生成超时(订阅通道 300 秒未返回),请稍后重试" : "上游生成超时(150 秒未返回),请检查网络或代理后重试");
|
|
20829
21025
|
jsonError$1(res, controller.signal.aborted ? 499 : 502, errorMessage(error, "generation-failed"));
|
|
20830
21026
|
} finally {
|
|
20831
21027
|
res.off("close", onConnectionClose);
|
|
@@ -20837,7 +21033,7 @@ function parseStudioGenerateRequest(value) {
|
|
|
20837
21033
|
const input = record$1(value);
|
|
20838
21034
|
if (input === void 0) throw new Error("请求格式无效");
|
|
20839
21035
|
if (input.mode !== "generate" && input.mode !== "edit") throw new Error("请选择生成类型");
|
|
20840
|
-
if (!
|
|
21036
|
+
if (!studioProvider(input.provider)) throw new Error("不支持该图像 Provider");
|
|
20841
21037
|
const prompt = requiredText(input.prompt, "请输入提示词", 2e3);
|
|
20842
21038
|
const model = requiredText(input.model, "请选择模型", 200);
|
|
20843
21039
|
const ratio = requiredText(input.ratio, "请选择比例", 32);
|
|
@@ -20889,8 +21085,8 @@ function requiredText(value, message, maxLength) {
|
|
|
20889
21085
|
if (text.length > maxLength) throw new Error(`${message}(最多 ${String(maxLength)} 个字符)`);
|
|
20890
21086
|
return text;
|
|
20891
21087
|
}
|
|
20892
|
-
function
|
|
20893
|
-
return typeof value === "string" &&
|
|
21088
|
+
function studioProvider(value) {
|
|
21089
|
+
return typeof value === "string" && STUDIO_PROVIDERS.includes(value);
|
|
20894
21090
|
}
|
|
20895
21091
|
function imageMediaType(value) {
|
|
20896
21092
|
return value === "image/png" || value === "image/jpeg" || value === "image/webp" || value === "image/gif";
|
|
@@ -21386,7 +21582,7 @@ function apply(ctx, config = {}) {
|
|
|
21386
21582
|
path: STUDIO_ROUTE,
|
|
21387
21583
|
handler: (req, res) => serveStudio(req, res, {
|
|
21388
21584
|
describe: async () => {
|
|
21389
|
-
const base = await describeStudio(ctx, current());
|
|
21585
|
+
const base = await describeStudio(ctx, current(), subscriptionManager);
|
|
21390
21586
|
const workspaces = await getDshWorkspacesFull().catch(() => []);
|
|
21391
21587
|
const activeRoot = Array.from(knownWorkspaceRoots)[0] || workspaces[0]?.path || process.cwd();
|
|
21392
21588
|
return {
|
|
@@ -21397,7 +21593,7 @@ function apply(ctx, config = {}) {
|
|
|
21397
21593
|
},
|
|
21398
21594
|
generate: (input, signal) => {
|
|
21399
21595
|
const fallbackRoot = Array.from(knownWorkspaceRoots)[0] || process.cwd();
|
|
21400
|
-
return generateFromStudio(ctx, current(), input, signal, fallbackRoot);
|
|
21596
|
+
return generateFromStudio(ctx, current(), input, signal, fallbackRoot, subscriptionManager);
|
|
21401
21597
|
},
|
|
21402
21598
|
maxBodyBytes: Math.ceil(ctx.attachments.imageLimits.maxImageBytes * 1.4 * 5) + 262144
|
|
21403
21599
|
})
|
|
@@ -21523,6 +21719,7 @@ function apply(ctx, config = {}) {
|
|
|
21523
21719
|
}), active.provider, active.model, size, current(), exec, knownWorkspaceRoots);
|
|
21524
21720
|
}
|
|
21525
21721
|
const size = args.size ?? active.imageSize;
|
|
21722
|
+
const arkOptions = active.provider === "seedream" ? active.arkOptions : void 0;
|
|
21526
21723
|
return saveGenerated(ctx, await generateOpenAICompatibleImage({
|
|
21527
21724
|
provider: active.provider,
|
|
21528
21725
|
apiKey: credential,
|
|
@@ -21531,7 +21728,8 @@ function apply(ctx, config = {}) {
|
|
|
21531
21728
|
prompt: args.prompt,
|
|
21532
21729
|
size,
|
|
21533
21730
|
maxBytes: ctx.attachments.imageLimits.maxImageBytes,
|
|
21534
|
-
signal: exec.signal
|
|
21731
|
+
signal: exec.signal,
|
|
21732
|
+
...arkOptions === void 0 ? {} : { arkOptions }
|
|
21535
21733
|
}), active.provider, active.model, size, current(), exec, knownWorkspaceRoots);
|
|
21536
21734
|
},
|
|
21537
21735
|
presentResult: (_args, result) => imagePresentation(result)
|
|
@@ -21560,7 +21758,7 @@ function apply(ctx, config = {}) {
|
|
|
21560
21758
|
"grok-sub",
|
|
21561
21759
|
"google-sub"
|
|
21562
21760
|
],
|
|
21563
|
-
description: "Optional provider for this call only (for example when the user asks to use a specific provider); omit to use the configured default. chatgpt-sub, grok-sub, and google-sub
|
|
21761
|
+
description: "Optional provider for this call only (for example when the user asks to use a specific provider); omit to use the configured default. chatgpt-sub, grok-sub, and google-sub edit images through the logged-in subscription account instead of an API key."
|
|
21564
21762
|
},
|
|
21565
21763
|
model: {
|
|
21566
21764
|
type: "string",
|
|
@@ -21623,7 +21821,6 @@ function apply(ctx, config = {}) {
|
|
|
21623
21821
|
output: imageOutput("Edited"),
|
|
21624
21822
|
async execute(args, exec) {
|
|
21625
21823
|
const active = resolveProvider(withProviderOverrides(current(), providerOverrideOf(args.provider), args.model));
|
|
21626
|
-
if (active.provider === "chatgpt-sub" || active.provider === "grok-sub" || active.provider === "google-sub") assertSubscriptionEditUnsupported(active.provider);
|
|
21627
21824
|
const canvasSelection = args.source === "canvas_selection";
|
|
21628
21825
|
if (canvasSelection && (args.source_attachment_id !== void 0 || Array.isArray(args.source_attachment_ids) || args.source_path !== void 0 || Array.isArray(args.source_paths))) throw new Error("edit_image source=canvas_selection cannot be combined with source_attachment_id, source_attachment_ids, source_path, or source_paths; provide exactly one selector");
|
|
21629
21826
|
const sourceImages = canvasSelection ? await resolveCanvasSelectionReferences({
|
|
@@ -21660,6 +21857,18 @@ function apply(ctx, config = {}) {
|
|
|
21660
21857
|
signal: exec.signal
|
|
21661
21858
|
}), active.provider, workflow.name, "API workflow", current(), exec, knownWorkspaceRoots);
|
|
21662
21859
|
}
|
|
21860
|
+
if (active.provider === "chatgpt-sub" || active.provider === "grok-sub" || active.provider === "google-sub") {
|
|
21861
|
+
if (sourceImages.length === 0) throw new Error("edit_image requires a reference image");
|
|
21862
|
+
return saveGenerated(ctx, await generateSubscriptionImage({
|
|
21863
|
+
manager: subscriptionManager,
|
|
21864
|
+
provider: active.provider,
|
|
21865
|
+
prompt: args.prompt,
|
|
21866
|
+
sourceImages,
|
|
21867
|
+
...args.size !== void 0 ? { size: args.size } : {},
|
|
21868
|
+
maxBytes: ctx.attachments.imageLimits.maxImageBytes,
|
|
21869
|
+
signal: exec.signal
|
|
21870
|
+
}), active.provider, active.model, "subscription edit", current(), exec, knownWorkspaceRoots);
|
|
21871
|
+
}
|
|
21663
21872
|
const credential = await requireApiKey(ctx, active.provider, "edit_image");
|
|
21664
21873
|
if (active.provider === "google") {
|
|
21665
21874
|
const aspectRatio = args.aspect_ratio ?? active.aspectRatio;
|
|
@@ -21699,7 +21908,8 @@ function apply(ctx, config = {}) {
|
|
|
21699
21908
|
sourceImages,
|
|
21700
21909
|
size,
|
|
21701
21910
|
maxBytes: ctx.attachments.imageLimits.maxImageBytes,
|
|
21702
|
-
signal: exec.signal
|
|
21911
|
+
signal: exec.signal,
|
|
21912
|
+
arkOptions: active.arkOptions
|
|
21703
21913
|
}), active.provider, active.model, size, current(), exec, knownWorkspaceRoots);
|
|
21704
21914
|
return saveGenerated(ctx, await editDashScopeImage({
|
|
21705
21915
|
apiKey: credential,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { ImageAttachmentRef } from '@deepseek-ai/dsh-attachment';
|
|
2
|
-
import { type
|
|
2
|
+
import { type StudioProvider } from '../shared.js';
|
|
3
3
|
/** One regenerated version displayed in place of an original conversation image. */
|
|
4
4
|
export interface ConversationImageRevision {
|
|
5
5
|
attachment: ImageAttachmentRef;
|
|
6
6
|
prompt: string;
|
|
7
|
-
provider:
|
|
7
|
+
provider: StudioProvider;
|
|
8
8
|
model: string;
|
|
9
9
|
output: string;
|
|
10
10
|
createdAt: number;
|
|
@@ -192,6 +192,10 @@ export interface GalleryViewTabProps {
|
|
|
192
192
|
useSession?: (selector: (state: any) => any) => any;
|
|
193
193
|
useSessions?: (selector: (state: any) => any) => any;
|
|
194
194
|
useWorkspaces?: (selector: (state: any) => any) => any;
|
|
195
|
+
/** Host credential-change notifier; forwarded to the studio workbench. */
|
|
196
|
+
credentialEvents?: {
|
|
197
|
+
listen(callback: () => void): () => void;
|
|
198
|
+
} | undefined;
|
|
195
199
|
/**
|
|
196
200
|
* Right-sidebar variant (DSH official `sidebar.right.pane.tab` seat). The
|
|
197
201
|
* native conversation stays in the main area, so the composer must NOT be
|