dsh-image-gen 0.8.6 → 0.8.7
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.en.md +436 -423
- package/README.md +436 -423
- package/lib/client.js +587 -76
- package/lib/index.js +493 -98
- package/lib/types/client/conversation-image-revisions.d.ts +4 -1
- package/lib/types/client/conversation-regenerate.d.ts +4 -1
- package/lib/types/client/gallery-store.d.ts +4 -1
- package/lib/types/client/index.d.ts +6 -0
- package/lib/types/client/multi-model-compare.d.ts +8 -1
- package/lib/types/client/output-settings.d.ts +3 -0
- package/lib/types/client/studio-style.d.ts +1 -1
- package/lib/types/client/watermark-control.d.ts +9 -0
- package/lib/types/config.d.ts +13 -2
- package/lib/types/dashscope.d.ts +1 -0
- package/lib/types/openai-compatible.d.ts +2 -1
- package/lib/types/openai-output.d.ts +12 -0
- package/lib/types/shared.d.ts +20 -0
- package/lib/types/subscription/vendors/antigravity.d.ts +18 -8
- package/lib/types/watermark.d.ts +20 -0
- package/package.json +139 -140
- package/skills/install-dsh-image-gen/SKILL.md +22 -22
- package/README.zh-CN.md +0 -3
package/lib/index.js
CHANGED
|
@@ -5581,6 +5581,111 @@ function toolAbortedBeforeDispatchResult(prior) {
|
|
|
5581
5581
|
};
|
|
5582
5582
|
}
|
|
5583
5583
|
//#endregion
|
|
5584
|
+
//#region lib/types/watermark.js
|
|
5585
|
+
const WATERMARK_OPTIONS = [
|
|
5586
|
+
{
|
|
5587
|
+
value: "default",
|
|
5588
|
+
label: "渠道默认"
|
|
5589
|
+
},
|
|
5590
|
+
{
|
|
5591
|
+
value: "off",
|
|
5592
|
+
label: "关闭"
|
|
5593
|
+
},
|
|
5594
|
+
{
|
|
5595
|
+
value: "on",
|
|
5596
|
+
label: "开启"
|
|
5597
|
+
}
|
|
5598
|
+
];
|
|
5599
|
+
function configuredField(extra) {
|
|
5600
|
+
if (typeof extra?.watermark === "boolean") return "watermark";
|
|
5601
|
+
if (typeof extra?.watermark_enabled === "boolean") return "watermark_enabled";
|
|
5602
|
+
const parameters = extra?.parameters;
|
|
5603
|
+
if (parameters && typeof parameters === "object" && !Array.isArray(parameters) && typeof parameters.watermark === "boolean") return "parameters.watermark";
|
|
5604
|
+
}
|
|
5605
|
+
/** Model names alone do not establish the protocol of an unknown relay. */
|
|
5606
|
+
function officialContract(config, mode) {
|
|
5607
|
+
const model = (mode === "generate" ? config.openaiCompatExtraBody : config.openaiCompatEditExtra)?.model ?? config.openaiCompatModel;
|
|
5608
|
+
if (typeof model !== "string") return void 0;
|
|
5609
|
+
try {
|
|
5610
|
+
const url = new URL(config.openaiCompatBaseURL ?? "");
|
|
5611
|
+
if (url.protocol !== "https:" || url.port || url.search || url.hash) return void 0;
|
|
5612
|
+
const path = url.pathname.replace(/\/+$/, "");
|
|
5613
|
+
if (url.hostname === "token.sensenova.cn" && path === "/v1" && model === "sensenova-u1.5-lite") return "sensenova";
|
|
5614
|
+
if (/^[a-z0-9-]+\.(?:cn-beijing|ap-southeast-1|us-east-1|eu-central-1|ap-northeast-1|cn-hongkong)\.maas\.aliyuncs\.com$/.test(url.hostname) && path === "/compatible-mode/v1" && /^qwen-image-(?:3\.0(?:-pro)?|2\.1-(?:pro|turbo))$/.test(model)) return "qwen";
|
|
5615
|
+
} catch {}
|
|
5616
|
+
}
|
|
5617
|
+
/** Known official edit contracts take precedence over relay-specific format settings. */
|
|
5618
|
+
function compatEditFormat(config) {
|
|
5619
|
+
const contract = officialContract(config, "edit");
|
|
5620
|
+
if (contract === "sensenova") return "jsonImageUrlArray";
|
|
5621
|
+
if (contract === "qwen") return "qwenJson";
|
|
5622
|
+
return config.openaiCompatEditFormat ?? "multipart";
|
|
5623
|
+
}
|
|
5624
|
+
function compatWatermarkField(config, mode = "generate") {
|
|
5625
|
+
if (officialContract(config, mode) !== void 0) return "watermark";
|
|
5626
|
+
if (mode === "generate") return configuredField(config.openaiCompatExtraBody);
|
|
5627
|
+
return compatEditFormat(config) === "jsonImageUrlArray" ? configuredField(config.openaiCompatEditExtra) : void 0;
|
|
5628
|
+
}
|
|
5629
|
+
function overrideField(extra, field, selected) {
|
|
5630
|
+
const watermark = selected === "on";
|
|
5631
|
+
return field === "parameters.watermark" ? {
|
|
5632
|
+
...extra,
|
|
5633
|
+
parameters: {
|
|
5634
|
+
...extra?.parameters,
|
|
5635
|
+
watermark
|
|
5636
|
+
}
|
|
5637
|
+
} : {
|
|
5638
|
+
...extra,
|
|
5639
|
+
[field]: watermark
|
|
5640
|
+
};
|
|
5641
|
+
}
|
|
5642
|
+
/** Preserve all advanced fields; only an explicit workbench choice overrides its own watermark. */
|
|
5643
|
+
function compatGenerationExtraBody(config, selected) {
|
|
5644
|
+
const extra = config.openaiCompatExtraBody;
|
|
5645
|
+
if (selected === void 0 || selected === "default") return extra;
|
|
5646
|
+
const field = compatWatermarkField(config);
|
|
5647
|
+
if (field === void 0) throw new Error("该渠道尚未确认文生图水印支持,请刷新工作台");
|
|
5648
|
+
return overrideField(extra, field, selected);
|
|
5649
|
+
}
|
|
5650
|
+
function compatEditExtraBody(config, selected) {
|
|
5651
|
+
if (selected === void 0 || selected === "default") return void 0;
|
|
5652
|
+
const field = compatWatermarkField(config, "edit");
|
|
5653
|
+
if (field === void 0) throw new Error("该渠道尚未确认图生图水印支持,请刷新工作台");
|
|
5654
|
+
return overrideField(config.openaiCompatEditExtra, field, selected);
|
|
5655
|
+
}
|
|
5656
|
+
function selectedWatermark(selected, channelDefault) {
|
|
5657
|
+
return selected === "off" ? false : selected === "on" ? true : channelDefault;
|
|
5658
|
+
}
|
|
5659
|
+
function watermarkProfile(config, provider, model) {
|
|
5660
|
+
const capability = (mode) => {
|
|
5661
|
+
if (provider === "seedream" || provider === "dashscope" && /^qwen-image(?:$|-)/.test(model)) return { status: "supported" };
|
|
5662
|
+
if (provider === "openai-compat") return compatWatermarkField(config, mode) === void 0 ? {
|
|
5663
|
+
status: "unknown",
|
|
5664
|
+
reason: mode === "edit" && configuredField(config.openaiCompatEditExtra) !== void 0 ? "edit-format" : "channel-unconfirmed"
|
|
5665
|
+
} : { status: "supported" };
|
|
5666
|
+
if (provider === "zhipu") return {
|
|
5667
|
+
status: "unknown",
|
|
5668
|
+
reason: "account-unconfirmed"
|
|
5669
|
+
};
|
|
5670
|
+
if (provider === "dashscope") return {
|
|
5671
|
+
status: "unknown",
|
|
5672
|
+
reason: "model-unconfirmed"
|
|
5673
|
+
};
|
|
5674
|
+
return {
|
|
5675
|
+
status: "unavailable",
|
|
5676
|
+
reason: "no-api-control"
|
|
5677
|
+
};
|
|
5678
|
+
};
|
|
5679
|
+
const generate = capability("generate");
|
|
5680
|
+
return {
|
|
5681
|
+
watermarkCapabilities: {
|
|
5682
|
+
generate,
|
|
5683
|
+
edit: capability("edit")
|
|
5684
|
+
},
|
|
5685
|
+
...generate.status === "supported" ? { watermarkOptions: WATERMARK_OPTIONS } : {}
|
|
5686
|
+
};
|
|
5687
|
+
}
|
|
5688
|
+
//#endregion
|
|
5584
5689
|
//#region lib/types/shared.js
|
|
5585
5690
|
/** Browser route used by the generated-image card. */
|
|
5586
5691
|
const IMAGE_ROUTE = "/plugins/dsh-image-gen/image";
|
|
@@ -5717,6 +5822,17 @@ const SUBSCRIPTION_PROVIDER_DISPLAY_NAMES = {
|
|
|
5717
5822
|
"grok-sub": "Grok 订阅",
|
|
5718
5823
|
"google-sub": "Google 订阅"
|
|
5719
5824
|
};
|
|
5825
|
+
const STUDIO_WATERMARK_VALUES = [
|
|
5826
|
+
"default",
|
|
5827
|
+
"off",
|
|
5828
|
+
"on"
|
|
5829
|
+
];
|
|
5830
|
+
function studioWatermarkCapability(profile, mode) {
|
|
5831
|
+
return profile?.watermarkCapabilities?.[mode] ?? (mode === "generate" && profile?.watermarkOptions?.length ? { status: "supported" } : {
|
|
5832
|
+
status: "unknown",
|
|
5833
|
+
reason: "channel-unconfirmed"
|
|
5834
|
+
});
|
|
5835
|
+
}
|
|
5720
5836
|
/** Default endpoints and base URLs. */
|
|
5721
5837
|
const DEFAULT_GOOGLE_ENDPOINT = "https://generativelanguage.googleapis.com/v1beta/interactions";
|
|
5722
5838
|
const DEFAULT_OPENAI_BASE_URL = "https://api.openai.com/v1";
|
|
@@ -5841,8 +5957,18 @@ const Config = z.object({
|
|
|
5841
5957
|
googleEndpoint: volatile(z.string().default(DEFAULT_GOOGLE_ENDPOINT)),
|
|
5842
5958
|
openaiBaseURL: volatile(z.string().default(DEFAULT_OPENAI_BASE_URL)),
|
|
5843
5959
|
openaiModel: volatile(z.string().default(DEFAULT_OPENAI_MODEL)),
|
|
5960
|
+
openaiSizes: volatile(z.dict(z.dict(z.string())).default({})),
|
|
5844
5961
|
openaiCompatBaseURL: volatile(z.string().default("")),
|
|
5845
5962
|
openaiCompatModel: volatile(z.string().default("")),
|
|
5963
|
+
openaiCompatQuality: volatile(z.string().default("")),
|
|
5964
|
+
openaiCompatQualityOptions: volatile(z.array(z.string()).default([
|
|
5965
|
+
"auto",
|
|
5966
|
+
"low",
|
|
5967
|
+
"medium",
|
|
5968
|
+
"high"
|
|
5969
|
+
])),
|
|
5970
|
+
openaiCompatExtraBody: volatile(z.dict(z.any()).default({})),
|
|
5971
|
+
openaiCompatEditSizeMode: volatile(z.union(["auto", "selected"]).default("auto")),
|
|
5846
5972
|
openaiCompatEditFormat: volatile(z.union([
|
|
5847
5973
|
z.const("multipart"),
|
|
5848
5974
|
z.const("jsonImageUrlArray"),
|
|
@@ -5896,7 +6022,7 @@ function resolveProvider(config) {
|
|
|
5896
6022
|
model,
|
|
5897
6023
|
baseURL,
|
|
5898
6024
|
imageSize: "1024x1024",
|
|
5899
|
-
editFormat: config
|
|
6025
|
+
editFormat: compatEditFormat(config),
|
|
5900
6026
|
editExtra: config.openaiCompatEditExtra ?? {}
|
|
5901
6027
|
};
|
|
5902
6028
|
}
|
|
@@ -7268,7 +7394,10 @@ async function generateDashScopeImage(options) {
|
|
|
7268
7394
|
role: "user",
|
|
7269
7395
|
content: [{ text: options.prompt }]
|
|
7270
7396
|
}] },
|
|
7271
|
-
parameters: {
|
|
7397
|
+
parameters: {
|
|
7398
|
+
...formattedSize === void 0 ? {} : { size: formattedSize },
|
|
7399
|
+
...options.watermark === void 0 ? {} : { watermark: options.watermark }
|
|
7400
|
+
}
|
|
7272
7401
|
},
|
|
7273
7402
|
operation: "generation"
|
|
7274
7403
|
});
|
|
@@ -7287,7 +7416,8 @@ async function editDashScopeImage(options) {
|
|
|
7287
7416
|
}] },
|
|
7288
7417
|
parameters: {
|
|
7289
7418
|
prompt_extend: true,
|
|
7290
|
-
...formattedSize === void 0 ? {} : { size: formattedSize }
|
|
7419
|
+
...formattedSize === void 0 ? {} : { size: formattedSize },
|
|
7420
|
+
...options.watermark === void 0 ? {} : { watermark: options.watermark }
|
|
7291
7421
|
}
|
|
7292
7422
|
},
|
|
7293
7423
|
operation: "editing"
|
|
@@ -7993,6 +8123,24 @@ async function generateOpenAICompatibleImage(input) {
|
|
|
7993
8123
|
}), input.provider, input);
|
|
7994
8124
|
}
|
|
7995
8125
|
async function editOpenAICompatibleImage(input) {
|
|
8126
|
+
if (input.editFormat === "qwenJson") return parseImageResponse(await fetch(imageEndpoint$1(input.baseURL, "generations"), {
|
|
8127
|
+
method: "POST",
|
|
8128
|
+
redirect: "error",
|
|
8129
|
+
signal: input.signal,
|
|
8130
|
+
headers: {
|
|
8131
|
+
authorization: `Bearer ${input.apiKey}`,
|
|
8132
|
+
"content-type": "application/json"
|
|
8133
|
+
},
|
|
8134
|
+
body: JSON.stringify({
|
|
8135
|
+
model: input.model,
|
|
8136
|
+
prompt: input.prompt,
|
|
8137
|
+
image: input.sourceImages.map(toDataUrl$2),
|
|
8138
|
+
...input.size === void 0 ? {} : { size: input.size },
|
|
8139
|
+
...input.quality === void 0 ? {} : { quality: input.quality },
|
|
8140
|
+
...input.editExtra ?? {},
|
|
8141
|
+
...input.extraBody ?? {}
|
|
8142
|
+
})
|
|
8143
|
+
}), "openai-compat", input);
|
|
7996
8144
|
if (input.editFormat === "xaiJson") {
|
|
7997
8145
|
const references = input.sourceImages.map((image) => ({
|
|
7998
8146
|
type: "image_url",
|
|
@@ -8021,7 +8169,7 @@ async function editOpenAICompatibleImage(input) {
|
|
|
8021
8169
|
images: input.sourceImages.map((sourceImage) => ({ image_url: toDataUrl$2(sourceImage) })),
|
|
8022
8170
|
prompt: input.prompt,
|
|
8023
8171
|
n: 1,
|
|
8024
|
-
size: "auto",
|
|
8172
|
+
size: input.editSizeMode === "selected" ? input.size ?? "auto" : "auto",
|
|
8025
8173
|
response_format: "url",
|
|
8026
8174
|
...input.quality === void 0 ? {} : { quality: input.quality },
|
|
8027
8175
|
...input.editExtra ?? {},
|
|
@@ -8749,19 +8897,22 @@ const ANTIGRAVITY_CLIENT_ID = "1071006060591-tmhssin2h21lcre235vtolojh4g403ep.ap
|
|
|
8749
8897
|
const ANTIGRAVITY_CLIENT_SECRET = "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";
|
|
8750
8898
|
const ANTIGRAVITY_REDIRECT_URI = "http://localhost:51121/oauth-callback";
|
|
8751
8899
|
/**
|
|
8752
|
-
*
|
|
8753
|
-
*
|
|
8900
|
+
* Preserve the working sandbox routes, then try ordinary daily (consumer
|
|
8901
|
+
* accounts) before the production compatibility fallback. A Gmail address
|
|
8902
|
+
* alone does not establish which backend an account can use.
|
|
8754
8903
|
*/
|
|
8755
8904
|
const ANTIGRAVITY_ENDPOINTS = [
|
|
8756
8905
|
"https://daily-cloudcode-pa.sandbox.googleapis.com",
|
|
8757
8906
|
"https://autopush-cloudcode-pa.sandbox.googleapis.com",
|
|
8907
|
+
"https://daily-cloudcode-pa.googleapis.com",
|
|
8758
8908
|
"https://cloudcode-pa.googleapis.com"
|
|
8759
8909
|
];
|
|
8760
8910
|
/** loadCodeAssist is best supported on prod; discovery tries prod first. */
|
|
8761
8911
|
const LOAD_ENDPOINTS = [
|
|
8762
8912
|
"https://cloudcode-pa.googleapis.com",
|
|
8763
8913
|
"https://daily-cloudcode-pa.sandbox.googleapis.com",
|
|
8764
|
-
"https://autopush-cloudcode-pa.sandbox.googleapis.com"
|
|
8914
|
+
"https://autopush-cloudcode-pa.sandbox.googleapis.com",
|
|
8915
|
+
"https://daily-cloudcode-pa.googleapis.com"
|
|
8765
8916
|
];
|
|
8766
8917
|
/** The image model served through this channel: Nano Banana 2. */
|
|
8767
8918
|
const ANTIGRAVITY_IMAGE_MODEL = DEFAULT_SUBSCRIPTION_MODELS["google-sub"];
|
|
@@ -8803,7 +8954,7 @@ async function fetchVersionText(url, maxChars) {
|
|
|
8803
8954
|
return;
|
|
8804
8955
|
}
|
|
8805
8956
|
}
|
|
8806
|
-
/**
|
|
8957
|
+
/** Legacy compatibility project: not an account project or proof of entitlement. */
|
|
8807
8958
|
const DEFAULT_PROJECT_ID = "rising-fact-p41fc";
|
|
8808
8959
|
/** Generation timeout mirroring the reference implementation. */
|
|
8809
8960
|
const IMAGE_TIMEOUT_MS = 12e4;
|
|
@@ -8869,16 +9020,10 @@ function antigravityAuthorizeUrl(cfg, pkce) {
|
|
|
8869
9020
|
* plain SDK identity here, mirroring its project.js loadHeaders.
|
|
8870
9021
|
*/
|
|
8871
9022
|
function loadHeaders(projectId) {
|
|
8872
|
-
const platform = process.platform === "win32" ? "WINDOWS" : process.platform === "darwin" ? "MACOS" : "LINUX";
|
|
8873
9023
|
return {
|
|
8874
9024
|
"User-Agent": "google-api-nodejs-client/9.15.1",
|
|
8875
9025
|
"X-Goog-Api-Client": "google-cloud-sdk vscode_cloudshelleditor/0.1",
|
|
8876
|
-
"Client-Metadata": JSON.stringify(
|
|
8877
|
-
ideType: "ANTIGRAVITY",
|
|
8878
|
-
platform,
|
|
8879
|
-
pluginType: "GEMINI",
|
|
8880
|
-
...projectId.length > 0 ? { duetProject: projectId } : {}
|
|
8881
|
-
})
|
|
9026
|
+
"Client-Metadata": JSON.stringify(clientMetadata(projectId))
|
|
8882
9027
|
};
|
|
8883
9028
|
}
|
|
8884
9029
|
/**
|
|
@@ -8888,17 +9033,10 @@ function loadHeaders(projectId) {
|
|
|
8888
9033
|
* SDK UA here is what produced 403 #3501 on personal accounts.
|
|
8889
9034
|
*/
|
|
8890
9035
|
function contentHeaders(projectId) {
|
|
8891
|
-
const uaPlatform = process.platform === "darwin" ? "Macintosh; Intel Mac OS X 10_15_7" : "Windows NT 10.0; Win64; x64";
|
|
8892
|
-
const platform = process.platform === "win32" ? "WINDOWS" : process.platform === "darwin" ? "MACOS" : "LINUX";
|
|
8893
9036
|
return {
|
|
8894
|
-
"User-Agent": `Mozilla/5.0 (${
|
|
9037
|
+
"User-Agent": `Mozilla/5.0 (${process.platform === "darwin" ? "Macintosh; Intel Mac OS X 10_15_7" : "Windows NT 10.0; Win64; x64"}) AppleWebKit/537.36 (KHTML, like Gecko) Antigravity/${getAntigravityVersion()} Chrome/138.0.7204.235 Electron/37.3.1 Safari/537.36`,
|
|
8895
9038
|
"X-Goog-Api-Client": "google-cloud-sdk vscode_cloudshelleditor/0.1",
|
|
8896
|
-
"Client-Metadata": JSON.stringify(
|
|
8897
|
-
ideType: "ANTIGRAVITY",
|
|
8898
|
-
platform,
|
|
8899
|
-
pluginType: "GEMINI",
|
|
8900
|
-
...projectId.length > 0 ? { duetProject: projectId } : {}
|
|
8901
|
-
})
|
|
9039
|
+
"Client-Metadata": JSON.stringify(clientMetadata(projectId))
|
|
8902
9040
|
};
|
|
8903
9041
|
}
|
|
8904
9042
|
function tokenBlobFromOAuth(json) {
|
|
@@ -8953,41 +9091,113 @@ async function antigravityRefresh(blob) {
|
|
|
8953
9091
|
};
|
|
8954
9092
|
}
|
|
8955
9093
|
/**
|
|
8956
|
-
*
|
|
8957
|
-
*
|
|
8958
|
-
*
|
|
8959
|
-
*
|
|
9094
|
+
* Discover an actual account project, preserving HTTP and eligibility failures.
|
|
9095
|
+
* Keep the legacy fallback for accounts whose image route still accepts it;
|
|
9096
|
+
* UNSUPPORTED_CLIENT here describes Code Assist, not a universal image ban.
|
|
9097
|
+
* Do not invoke onboarding or accept account terms during image generation.
|
|
8960
9098
|
*/
|
|
8961
|
-
async function
|
|
8962
|
-
|
|
9099
|
+
async function antigravityResolveProjectContext(blob, signal) {
|
|
9100
|
+
signal?.throwIfAborted();
|
|
9101
|
+
const metadata = clientMetadata("");
|
|
9102
|
+
const diagnostics = [];
|
|
9103
|
+
for (const base of LOAD_ENDPOINTS) {
|
|
9104
|
+
const label = `loadCodeAssist ${new URL(base).hostname}`;
|
|
9105
|
+
try {
|
|
9106
|
+
const response = await fetch(`${base}/v1internal:loadCodeAssist`, {
|
|
9107
|
+
method: "POST",
|
|
9108
|
+
headers: {
|
|
9109
|
+
"Content-Type": "application/json",
|
|
9110
|
+
Authorization: `Bearer ${blob.accessToken}`,
|
|
9111
|
+
...loadHeaders("")
|
|
9112
|
+
},
|
|
9113
|
+
body: JSON.stringify({ metadata }),
|
|
9114
|
+
signal: signal === void 0 ? AbortSignal.timeout(25e3) : AbortSignal.any([signal, AbortSignal.timeout(25e3)])
|
|
9115
|
+
});
|
|
9116
|
+
if (!response.ok) {
|
|
9117
|
+
diagnostics.push(`${label}: ${httpDiagnostic(response.status, await response.text().catch(() => ""), blob)}`);
|
|
9118
|
+
continue;
|
|
9119
|
+
}
|
|
9120
|
+
const payload = recordOf(await response.json().catch(() => null));
|
|
9121
|
+
if (payload === void 0) {
|
|
9122
|
+
diagnostics.push(`${label}: HTTP ${String(response.status)}, invalid response`);
|
|
9123
|
+
continue;
|
|
9124
|
+
}
|
|
9125
|
+
const project = payload.cloudaicompanionProject;
|
|
9126
|
+
const projectRecord = recordOf(project);
|
|
9127
|
+
const id = typeof project === "string" ? project : typeof projectRecord?.id === "string" ? projectRecord.id : "";
|
|
9128
|
+
if (id.trim().length > 0) return {
|
|
9129
|
+
projectId: id.trim(),
|
|
9130
|
+
source: "account",
|
|
9131
|
+
diagnostics
|
|
9132
|
+
};
|
|
9133
|
+
diagnostics.push(`${label}: HTTP ${String(response.status)}, ${missingProjectDiagnostic(payload, blob)}`);
|
|
9134
|
+
} catch (error) {
|
|
9135
|
+
if (signal?.aborted === true) throw error;
|
|
9136
|
+
diagnostics.push(`${label}: ${diagnosticText(error instanceof Error ? error.message : "request failed", blob)}`);
|
|
9137
|
+
}
|
|
9138
|
+
}
|
|
9139
|
+
return {
|
|
9140
|
+
projectId: DEFAULT_PROJECT_ID,
|
|
9141
|
+
source: "fallback",
|
|
9142
|
+
diagnostics
|
|
9143
|
+
};
|
|
9144
|
+
}
|
|
9145
|
+
/** Google ClientMetadata.Platform enum, shared by request body and headers. */
|
|
9146
|
+
function platformOf() {
|
|
9147
|
+
if (process.platform === "win32" && process.arch === "x64") return "WINDOWS_AMD64";
|
|
9148
|
+
if (process.platform === "darwin" && process.arch === "x64") return "DARWIN_AMD64";
|
|
9149
|
+
if (process.platform === "darwin" && process.arch === "arm64") return "DARWIN_ARM64";
|
|
9150
|
+
if (process.platform === "linux" && process.arch === "x64") return "LINUX_AMD64";
|
|
9151
|
+
if (process.platform === "linux" && process.arch === "arm64") return "LINUX_ARM64";
|
|
9152
|
+
return "PLATFORM_UNSPECIFIED";
|
|
9153
|
+
}
|
|
9154
|
+
function clientMetadata(projectId) {
|
|
9155
|
+
return {
|
|
8963
9156
|
ideType: "ANTIGRAVITY",
|
|
8964
9157
|
platform: platformOf(),
|
|
8965
|
-
pluginType: "GEMINI"
|
|
9158
|
+
pluginType: "GEMINI",
|
|
9159
|
+
...projectId.length > 0 ? { duetProject: projectId } : {}
|
|
8966
9160
|
};
|
|
8967
|
-
for (const base of LOAD_ENDPOINTS) try {
|
|
8968
|
-
const response = await fetch(`${base}/v1internal:loadCodeAssist`, {
|
|
8969
|
-
method: "POST",
|
|
8970
|
-
headers: {
|
|
8971
|
-
"Content-Type": "application/json",
|
|
8972
|
-
Authorization: `Bearer ${blob.accessToken}`,
|
|
8973
|
-
...loadHeaders("")
|
|
8974
|
-
},
|
|
8975
|
-
body: JSON.stringify({ metadata }),
|
|
8976
|
-
signal: AbortSignal.timeout(25e3)
|
|
8977
|
-
});
|
|
8978
|
-
if (!response.ok) continue;
|
|
8979
|
-
const payload = await response.json().catch(() => null);
|
|
8980
|
-
if (payload === null) continue;
|
|
8981
|
-
const project = payload.cloudaicompanionProject;
|
|
8982
|
-
const id = typeof project === "string" ? project : typeof project === "object" && project !== null && typeof project.id === "string" ? project.id : "";
|
|
8983
|
-
if (id.length > 0) return id;
|
|
8984
|
-
} catch {}
|
|
8985
|
-
return DEFAULT_PROJECT_ID;
|
|
8986
9161
|
}
|
|
8987
|
-
function
|
|
8988
|
-
|
|
8989
|
-
|
|
8990
|
-
|
|
9162
|
+
function recordOf(value) {
|
|
9163
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
9164
|
+
}
|
|
9165
|
+
/** Upstream errors may echo request identity; redact before bounding the text. */
|
|
9166
|
+
function diagnosticText(value, blob) {
|
|
9167
|
+
let text = typeof value === "string" ? value : "";
|
|
9168
|
+
for (const secret of [
|
|
9169
|
+
blob.accessToken,
|
|
9170
|
+
blob.refreshToken,
|
|
9171
|
+
blob.accountId,
|
|
9172
|
+
blob.email
|
|
9173
|
+
]) if (secret.length > 0) text = text.replaceAll(secret, "[redacted]");
|
|
9174
|
+
return text.replace(/Bearer\s+\S+/gi, "Bearer [redacted]").replace(/\s+/g, " ").trim().slice(0, 200);
|
|
9175
|
+
}
|
|
9176
|
+
function httpDiagnostic(status, text, blob) {
|
|
9177
|
+
let detail = text;
|
|
9178
|
+
try {
|
|
9179
|
+
const error = recordOf(recordOf(JSON.parse(text))?.error);
|
|
9180
|
+
if (typeof error?.message === "string") detail = `${typeof error.status === "string" ? `${error.status}: ` : ""}${error.message}`;
|
|
9181
|
+
} catch {}
|
|
9182
|
+
const safe = diagnosticText(detail, blob);
|
|
9183
|
+
return `HTTP ${String(status)}${safe.length > 0 ? `: ${safe}` : ""}`;
|
|
9184
|
+
}
|
|
9185
|
+
function missingProjectDiagnostic(payload, blob) {
|
|
9186
|
+
const reasons = Array.isArray(payload.ineligibleTiers) ? payload.ineligibleTiers.flatMap((value) => {
|
|
9187
|
+
const tier = recordOf(value);
|
|
9188
|
+
return typeof tier?.reasonCode === "string" ? [`${tier.reasonCode}${typeof tier.reasonMessage === "string" ? `: ${tier.reasonMessage}` : ""}`] : [];
|
|
9189
|
+
}) : [];
|
|
9190
|
+
const allowed = Array.isArray(payload.allowedTiers) ? payload.allowedTiers.flatMap((value) => {
|
|
9191
|
+
const tier = recordOf(value);
|
|
9192
|
+
return typeof tier?.id === "string" ? [tier.id] : [];
|
|
9193
|
+
}) : [];
|
|
9194
|
+
const current = recordOf(payload.paidTier)?.id ?? recordOf(payload.currentTier)?.id;
|
|
9195
|
+
const detail = [
|
|
9196
|
+
...reasons,
|
|
9197
|
+
...typeof current === "string" ? [`tier=${current}`] : [],
|
|
9198
|
+
...allowed.length > 0 ? [`allowedTiers=${allowed.join(",")}`] : []
|
|
9199
|
+
];
|
|
9200
|
+
return `未返回账号项目${detail.length > 0 ? `; ${diagnosticText(detail.join("; "), blob)}` : ""}`;
|
|
8991
9201
|
}
|
|
8992
9202
|
/**
|
|
8993
9203
|
* Build the streamGenerateContent request body for one image generation.
|
|
@@ -9039,6 +9249,7 @@ function antigravityEnvelope(projectId, request) {
|
|
|
9039
9249
|
* parse the SSE stream for the inlineData image part.
|
|
9040
9250
|
*/
|
|
9041
9251
|
async function antigravityGenerateImage(options) {
|
|
9252
|
+
options.signal?.throwIfAborted();
|
|
9042
9253
|
await initAntigravityVersion();
|
|
9043
9254
|
const inner = antigravityImageBody({
|
|
9044
9255
|
prompt: options.prompt,
|
|
@@ -9047,7 +9258,7 @@ async function antigravityGenerateImage(options) {
|
|
|
9047
9258
|
...options.referenceImages !== void 0 ? { referenceImages: options.referenceImages } : {}
|
|
9048
9259
|
});
|
|
9049
9260
|
const body = antigravityEnvelope(options.projectId, inner);
|
|
9050
|
-
|
|
9261
|
+
const failures = [];
|
|
9051
9262
|
for (const base of ANTIGRAVITY_ENDPOINTS) {
|
|
9052
9263
|
const url = `${base}/v1internal:streamGenerateContent?alt=sse`;
|
|
9053
9264
|
const timeoutSignal = AbortSignal.timeout(IMAGE_TIMEOUT_MS);
|
|
@@ -9066,23 +9277,23 @@ async function antigravityGenerateImage(options) {
|
|
|
9066
9277
|
});
|
|
9067
9278
|
if (!response.ok) {
|
|
9068
9279
|
const text = await response.text().catch(() => "");
|
|
9069
|
-
|
|
9070
|
-
|
|
9071
|
-
throw new Error(`antigravity ${lastError}`);
|
|
9280
|
+
failures.push(`${new URL(base).hostname}: ${httpDiagnostic(response.status, text, options.blob)}`);
|
|
9281
|
+
continue;
|
|
9072
9282
|
}
|
|
9073
9283
|
const text = await response.text();
|
|
9074
9284
|
const parsed = parseSseImage(text);
|
|
9075
9285
|
if (parsed !== void 0) return parsed;
|
|
9076
9286
|
const reason = parseSseError(text);
|
|
9077
|
-
|
|
9287
|
+
failures.push(`${new URL(base).hostname}: ${diagnosticText(reason ?? "response contained no image", options.blob)}`);
|
|
9078
9288
|
continue;
|
|
9079
9289
|
} catch (error) {
|
|
9080
9290
|
if (options.signal?.aborted === true) throw error;
|
|
9081
|
-
|
|
9291
|
+
failures.push(`${new URL(base).hostname}: ${diagnosticText(error instanceof Error ? error.message : "request failed", options.blob)}`);
|
|
9082
9292
|
continue;
|
|
9083
9293
|
}
|
|
9084
9294
|
}
|
|
9085
|
-
|
|
9295
|
+
const discovery = options.projectContext?.source === "fallback" ? ["项目发现未返回账号项目,已尝试兼容项目。", ...options.projectContext.diagnostics] : [];
|
|
9296
|
+
throw new Error(`antigravity image generation failed: ${[...discovery, ...failures].join(" | ")}`);
|
|
9086
9297
|
}
|
|
9087
9298
|
/** Scan an SSE body for the first image part in any data: line. */
|
|
9088
9299
|
function parseSseImage(text) {
|
|
@@ -9310,11 +9521,12 @@ var SubscriptionManager = class {
|
|
|
9310
9521
|
const references = options.referenceImages ?? [];
|
|
9311
9522
|
if (references.length > 5) throw new Error(`订阅生图最多支持 ${String(5)} 张参考图,当前 ${String(references.length)} 张`);
|
|
9312
9523
|
if (vendor === "antigravity") {
|
|
9313
|
-
const
|
|
9524
|
+
const projectContext = await this.ensureAntigravityProject(session, options.signal);
|
|
9314
9525
|
const aspectRatio = antigravityAspectRatioOf(options.size);
|
|
9315
9526
|
return [{ b64_json: (await antigravityGenerateImage({
|
|
9316
9527
|
blob: session,
|
|
9317
|
-
projectId,
|
|
9528
|
+
projectId: projectContext.projectId,
|
|
9529
|
+
projectContext,
|
|
9318
9530
|
prompt: text,
|
|
9319
9531
|
...aspectRatio !== void 0 ? { aspectRatio } : {},
|
|
9320
9532
|
hd: options.quality === "hd" || options.quality === "high",
|
|
@@ -9376,13 +9588,13 @@ var SubscriptionManager = class {
|
|
|
9376
9588
|
* Antigravity generation envelope needs. Re-resolved when the account
|
|
9377
9589
|
* signs out or a different account logs in.
|
|
9378
9590
|
*/
|
|
9379
|
-
async ensureAntigravityProject(blob) {
|
|
9591
|
+
async ensureAntigravityProject(blob, signal) {
|
|
9380
9592
|
const cacheKey = blob.refreshToken.length > 0 ? blob.refreshToken : blob.accessToken.slice(0, 32);
|
|
9381
9593
|
const cached = this.projectCache.get(cacheKey);
|
|
9382
9594
|
if (cached !== void 0) return cached;
|
|
9383
|
-
const
|
|
9384
|
-
this.projectCache.set(cacheKey,
|
|
9385
|
-
return
|
|
9595
|
+
const context = await antigravityResolveProjectContext(blob, signal);
|
|
9596
|
+
if (context.source === "account") this.projectCache.set(cacheKey, context);
|
|
9597
|
+
return context;
|
|
9386
9598
|
}
|
|
9387
9599
|
};
|
|
9388
9600
|
/** The private Codex backend can ignore size while following a canvas ratio in
|
|
@@ -27187,6 +27399,103 @@ async function clearDiskCache() {
|
|
|
27187
27399
|
} catch {}
|
|
27188
27400
|
}
|
|
27189
27401
|
//#endregion
|
|
27402
|
+
//#region lib/types/openai-output.js
|
|
27403
|
+
const PROVIDER_DEFAULT_QUALITY = "__default__";
|
|
27404
|
+
const DEFAULT_COMPAT_QUALITIES = [
|
|
27405
|
+
"auto",
|
|
27406
|
+
"low",
|
|
27407
|
+
"medium",
|
|
27408
|
+
"high"
|
|
27409
|
+
];
|
|
27410
|
+
const LEGACY_SIZES = [
|
|
27411
|
+
"1024x1024",
|
|
27412
|
+
"1536x1024",
|
|
27413
|
+
"1024x1536"
|
|
27414
|
+
];
|
|
27415
|
+
const OPENAI_SIZES = {
|
|
27416
|
+
"1:1": {
|
|
27417
|
+
"1K": "1024x1024",
|
|
27418
|
+
"2K": "2048x2048"
|
|
27419
|
+
},
|
|
27420
|
+
"3:2": {
|
|
27421
|
+
"1K": "1536x1024",
|
|
27422
|
+
"2K": "2304x1536"
|
|
27423
|
+
},
|
|
27424
|
+
"2:3": {
|
|
27425
|
+
"1K": "1024x1536",
|
|
27426
|
+
"2K": "1536x2304"
|
|
27427
|
+
},
|
|
27428
|
+
"4:3": {
|
|
27429
|
+
"1K": "1152x864",
|
|
27430
|
+
"2K": "2048x1536"
|
|
27431
|
+
},
|
|
27432
|
+
"3:4": {
|
|
27433
|
+
"1K": "864x1152",
|
|
27434
|
+
"2K": "1536x2048"
|
|
27435
|
+
},
|
|
27436
|
+
"16:9": {
|
|
27437
|
+
"1K": "1536x864",
|
|
27438
|
+
"2K": "2048x1152",
|
|
27439
|
+
"4K": "3840x2160"
|
|
27440
|
+
},
|
|
27441
|
+
"9:16": {
|
|
27442
|
+
"1K": "864x1536",
|
|
27443
|
+
"2K": "1152x2048",
|
|
27444
|
+
"4K": "2160x3840"
|
|
27445
|
+
}
|
|
27446
|
+
};
|
|
27447
|
+
function flexibleSizeModel(model) {
|
|
27448
|
+
return /^gpt-image-2(?:$|[-.])/.test(model);
|
|
27449
|
+
}
|
|
27450
|
+
function officialSizeTable(config, model) {
|
|
27451
|
+
if (config.openaiSizes && Object.keys(config.openaiSizes).length > 0) return config.openaiSizes;
|
|
27452
|
+
if (flexibleSizeModel(model)) return OPENAI_SIZES;
|
|
27453
|
+
return {
|
|
27454
|
+
"1:1": { "1K": LEGACY_SIZES[0] },
|
|
27455
|
+
"3:2": { "1K": LEGACY_SIZES[1] },
|
|
27456
|
+
"2:3": { "1K": LEGACY_SIZES[2] }
|
|
27457
|
+
};
|
|
27458
|
+
}
|
|
27459
|
+
/** Check the official model contract before spending a request. Relays own their size contract. */
|
|
27460
|
+
function validateOfficialSize(model, size) {
|
|
27461
|
+
if (size === "auto") return;
|
|
27462
|
+
if (!flexibleSizeModel(model)) {
|
|
27463
|
+
if (!LEGACY_SIZES.includes(size)) throw new Error(`模型 ${model} 不支持尺寸 ${size};请使用 1024x1024、1536x1024 或 1024x1536`);
|
|
27464
|
+
return;
|
|
27465
|
+
}
|
|
27466
|
+
const match = /^(\d+)x(\d+)$/.exec(size);
|
|
27467
|
+
const width = Number(match?.[1]);
|
|
27468
|
+
const height = Number(match?.[2]);
|
|
27469
|
+
const pixels = width * height;
|
|
27470
|
+
if (!match || width % 16 !== 0 || height % 16 !== 0 || Math.max(width, height) > 3840 || pixels < 655360 || pixels > 8294400 || Math.max(width, height) > 3 * Math.min(width, height)) throw new Error(`OpenAI 尺寸 ${size} 无效:边长须为 16 的倍数且不超过 3840,比例在 1:3 到 3:1,像素数在 655360 到 8294400`);
|
|
27471
|
+
}
|
|
27472
|
+
function officialQualityOptions(model) {
|
|
27473
|
+
return [
|
|
27474
|
+
"auto",
|
|
27475
|
+
"low",
|
|
27476
|
+
"medium",
|
|
27477
|
+
"high",
|
|
27478
|
+
.../^gpt-image-2\.5-(sunburst|flare)(?:$|-)/.test(model) ? ["xhigh", "max"] : []
|
|
27479
|
+
].map((value) => ({
|
|
27480
|
+
value,
|
|
27481
|
+
label: value === "auto" ? "自动" : value
|
|
27482
|
+
}));
|
|
27483
|
+
}
|
|
27484
|
+
function compatQualityOptions(config) {
|
|
27485
|
+
const values = [...new Set([...config.openaiCompatQualityOptions ?? DEFAULT_COMPAT_QUALITIES, config.openaiCompatQuality ?? ""].map((value) => value.trim()).filter((value) => value && value !== "__default__"))];
|
|
27486
|
+
return [{
|
|
27487
|
+
value: PROVIDER_DEFAULT_QUALITY,
|
|
27488
|
+
label: "渠道默认(不发送)"
|
|
27489
|
+
}, ...values.map((value) => ({
|
|
27490
|
+
value,
|
|
27491
|
+
label: value
|
|
27492
|
+
}))];
|
|
27493
|
+
}
|
|
27494
|
+
function compatRequestQuality(config, selected) {
|
|
27495
|
+
const value = (selected ?? config.openaiCompatQuality)?.trim();
|
|
27496
|
+
return value && value !== "__default__" ? value : void 0;
|
|
27497
|
+
}
|
|
27498
|
+
//#endregion
|
|
27190
27499
|
//#region lib/types/studio.js
|
|
27191
27500
|
const LEGACY_OPENAI_TABLE = {
|
|
27192
27501
|
"1:1": { "1K": "1024x1024" },
|
|
@@ -27333,7 +27642,8 @@ function subscriptionStudioProfile(provider, signedIn) {
|
|
|
27333
27642
|
ratioOptions: cap.ratioOptions,
|
|
27334
27643
|
qualityOptions: cap.qualityOptions,
|
|
27335
27644
|
defaultRatio: cap.defaultRatio,
|
|
27336
|
-
defaultQuality: cap.defaultQuality
|
|
27645
|
+
defaultQuality: cap.defaultQuality,
|
|
27646
|
+
...watermarkProfile({}, provider, DEFAULT_SUBSCRIPTION_MODELS[provider])
|
|
27337
27647
|
};
|
|
27338
27648
|
}
|
|
27339
27649
|
/** Short display names for the workbench subscription rows. */
|
|
@@ -27352,6 +27662,7 @@ async function generateFromStudio(ctx, config, input, signal, fallbackWorkspaceR
|
|
|
27352
27662
|
assertAllowed(studioProfile(config, input.provider, true), input);
|
|
27353
27663
|
const active = resolveProvider(withProviderOverrides(config, input.provider, input.model));
|
|
27354
27664
|
if (active.provider === "comfyui") throw new Error("ComfyUI 暂未接入工作台");
|
|
27665
|
+
if (isSubscriptionProvider(active.provider)) throw new Error("订阅生图应通过订阅渠道执行");
|
|
27355
27666
|
const wired = active;
|
|
27356
27667
|
const credential = await requireApiKey(ctx, input.provider);
|
|
27357
27668
|
const rawRefs = input.references ?? (input.reference ? [input.reference] : []);
|
|
@@ -27388,12 +27699,16 @@ async function generateFromStudio(ctx, config, input, signal, fallbackWorkspaceR
|
|
|
27388
27699
|
output = `${aspectRatio}, ${imageSize}`;
|
|
27389
27700
|
} else if (wired.provider === "openai" || wired.provider === "openai-compat" || wired.provider === "xai" || wired.provider === "zhipu") {
|
|
27390
27701
|
const p = wired.provider;
|
|
27391
|
-
const
|
|
27392
|
-
const
|
|
27702
|
+
const officialTable = p === "openai" ? officialSizeTable(config, wired.model) : void 0;
|
|
27703
|
+
const officialResolution = input.resolution ?? (officialTable ? openAICompatTable({ openaiCompatSizes: officialTable }).find((group) => group.ratio === input.ratio)?.tiers[0]?.tier : void 0) ?? "1K";
|
|
27704
|
+
const size = p === "openai-compat" ? openAIRequestSize(config, input.ratio, input.resolution ?? input.quality) : p === "openai" ? openAIRequestSize({ openaiCompatSizes: officialTable }, input.ratio, officialResolution) : p === "xai" ? void 0 : CLOUD_CAPABILITIES[p].sizeFor?.(input.ratio, input.quality);
|
|
27705
|
+
if (p === "openai" && size !== void 0) validateOfficialSize(wired.model, size);
|
|
27706
|
+
const requestedQuality = input.generationQuality ?? input.quality;
|
|
27707
|
+
const quality = p === "openai" ? requestedQuality === "auto" ? void 0 : requestedQuality : p === "openai-compat" ? compatRequestQuality(config, input.generationQuality) : p === "zhipu" ? "hd" : void 0;
|
|
27393
27708
|
const extraBody = p === "xai" ? {
|
|
27394
27709
|
aspect_ratio: input.ratio,
|
|
27395
27710
|
resolution: input.quality
|
|
27396
|
-
} : void 0;
|
|
27711
|
+
} : p === "openai-compat" ? input.mode === "generate" ? compatGenerationExtraBody(config, input.watermark) : compatEditExtraBody(config, input.watermark) : void 0;
|
|
27397
27712
|
generated = input.mode === "edit" ? await editOpenAICompatibleImage({
|
|
27398
27713
|
apiKey: credential,
|
|
27399
27714
|
baseURL: wired.baseURL,
|
|
@@ -27407,7 +27722,8 @@ async function generateFromStudio(ctx, config, input, signal, fallbackWorkspaceR
|
|
|
27407
27722
|
...extraBody === void 0 ? {} : { extraBody },
|
|
27408
27723
|
...p === "openai-compat" ? {
|
|
27409
27724
|
editFormat: wired.editFormat,
|
|
27410
|
-
editExtra: wired.editExtra
|
|
27725
|
+
editExtra: wired.editExtra,
|
|
27726
|
+
editSizeMode: config.openaiCompatEditSizeMode ?? "auto"
|
|
27411
27727
|
} : p === "xai" ? { editFormat: "xaiJson" } : {}
|
|
27412
27728
|
}) : await generateOpenAICompatibleImage({
|
|
27413
27729
|
provider: p,
|
|
@@ -27424,6 +27740,10 @@ async function generateFromStudio(ctx, config, input, signal, fallbackWorkspaceR
|
|
|
27424
27740
|
output = size ?? `${input.ratio}, ${input.quality}`;
|
|
27425
27741
|
} else if (wired.provider === "seedream") {
|
|
27426
27742
|
const size = CLOUD_CAPABILITIES.seedream.sizeFor?.(input.ratio, input.quality) ?? input.quality;
|
|
27743
|
+
const arkOptions = {
|
|
27744
|
+
...wired.arkOptions,
|
|
27745
|
+
watermark: selectedWatermark(input.watermark, wired.arkOptions.watermark)
|
|
27746
|
+
};
|
|
27427
27747
|
generated = input.mode === "edit" ? await editSeedreamImage({
|
|
27428
27748
|
apiKey: credential,
|
|
27429
27749
|
baseURL: wired.baseURL,
|
|
@@ -27433,7 +27753,7 @@ async function generateFromStudio(ctx, config, input, signal, fallbackWorkspaceR
|
|
|
27433
27753
|
size,
|
|
27434
27754
|
maxBytes: ctx.attachments.imageLimits.maxImageBytes,
|
|
27435
27755
|
signal,
|
|
27436
|
-
arkOptions
|
|
27756
|
+
arkOptions
|
|
27437
27757
|
}) : await generateOpenAICompatibleImage({
|
|
27438
27758
|
provider: "seedream",
|
|
27439
27759
|
apiKey: credential,
|
|
@@ -27443,12 +27763,13 @@ async function generateFromStudio(ctx, config, input, signal, fallbackWorkspaceR
|
|
|
27443
27763
|
size,
|
|
27444
27764
|
maxBytes: ctx.attachments.imageLimits.maxImageBytes,
|
|
27445
27765
|
signal,
|
|
27446
|
-
arkOptions
|
|
27766
|
+
arkOptions
|
|
27447
27767
|
});
|
|
27448
27768
|
output = size;
|
|
27449
|
-
} else {
|
|
27769
|
+
} else if (wired.provider === "dashscope") {
|
|
27450
27770
|
if (input.mode === "edit" && sourceImages.length > 3) throw new Error("DashScope (通义万相) 图生图目前最多支持 3 张参考图,请精简后重试");
|
|
27451
27771
|
const size = CLOUD_CAPABILITIES.dashscope.sizeFor?.(input.ratio, input.quality) ?? "1024x1024";
|
|
27772
|
+
const watermark = selectedWatermark(input.watermark);
|
|
27452
27773
|
generated = input.mode === "edit" ? await editDashScopeImage({
|
|
27453
27774
|
apiKey: credential,
|
|
27454
27775
|
endpoint: wired.endpoint,
|
|
@@ -27457,7 +27778,8 @@ async function generateFromStudio(ctx, config, input, signal, fallbackWorkspaceR
|
|
|
27457
27778
|
sourceImages,
|
|
27458
27779
|
size,
|
|
27459
27780
|
maxBytes: ctx.attachments.imageLimits.maxImageBytes,
|
|
27460
|
-
signal
|
|
27781
|
+
signal,
|
|
27782
|
+
...watermark === void 0 ? {} : { watermark }
|
|
27461
27783
|
}) : await generateDashScopeImage({
|
|
27462
27784
|
apiKey: credential,
|
|
27463
27785
|
endpoint: wired.endpoint,
|
|
@@ -27465,10 +27787,11 @@ async function generateFromStudio(ctx, config, input, signal, fallbackWorkspaceR
|
|
|
27465
27787
|
prompt: input.prompt,
|
|
27466
27788
|
size,
|
|
27467
27789
|
maxBytes: ctx.attachments.imageLimits.maxImageBytes,
|
|
27468
|
-
signal
|
|
27790
|
+
signal,
|
|
27791
|
+
...watermark === void 0 ? {} : { watermark }
|
|
27469
27792
|
});
|
|
27470
27793
|
output = size;
|
|
27471
|
-
}
|
|
27794
|
+
} else throw new Error("当前渠道未接入 API 工作台");
|
|
27472
27795
|
if (!ctx.attachments.imageLimits.mediaTypes.includes(generated.mediaType)) throw new Error(`当前 DSH 不支持保存 ${generated.mediaType} 图片`);
|
|
27473
27796
|
return {
|
|
27474
27797
|
attachment: await ctx.attachments.saveImage({
|
|
@@ -27639,18 +27962,19 @@ function subscriptionResponse(input, startedAt, requestedCount, failedCount, ite
|
|
|
27639
27962
|
};
|
|
27640
27963
|
}
|
|
27641
27964
|
function studioProfile(config, provider, configured) {
|
|
27965
|
+
const result = cloudStudioProfile(config, provider, configured);
|
|
27966
|
+
return {
|
|
27967
|
+
...result,
|
|
27968
|
+
...watermarkProfile(config, provider, result.model)
|
|
27969
|
+
};
|
|
27970
|
+
}
|
|
27971
|
+
function cloudStudioProfile(config, provider, configured) {
|
|
27642
27972
|
let active;
|
|
27643
27973
|
try {
|
|
27644
27974
|
active = resolveProvider(withProviderOverrides(config, provider));
|
|
27645
|
-
} catch {
|
|
27646
|
-
|
|
27647
|
-
|
|
27648
|
-
"3:2",
|
|
27649
|
-
"2:3"
|
|
27650
|
-
].map(option), [{
|
|
27651
|
-
value: "standard",
|
|
27652
|
-
label: "标准(推荐)"
|
|
27653
|
-
}], "1:1", "standard");
|
|
27975
|
+
} catch (cause) {
|
|
27976
|
+
if (provider !== "openai-compat") throw cause;
|
|
27977
|
+
return compatibleStudioProfile(config, "", false);
|
|
27654
27978
|
}
|
|
27655
27979
|
if (active.provider === "comfyui") throw new Error("Invalid cloud provider profile");
|
|
27656
27980
|
const model = active.model;
|
|
@@ -27658,19 +27982,55 @@ function studioProfile(config, provider, configured) {
|
|
|
27658
27982
|
const cap = CLOUD_CAPABILITIES.google;
|
|
27659
27983
|
return profile(provider, model, configured, cap.ratioOptions, cap.qualityOptions, cap.defaultRatio, cap.defaultQuality);
|
|
27660
27984
|
}
|
|
27661
|
-
if (provider === "openai-compat")
|
|
27662
|
-
|
|
27985
|
+
if (provider === "openai-compat") return compatibleStudioProfile(config, model, configured);
|
|
27986
|
+
if (provider === "openai") {
|
|
27987
|
+
const table = officialSizeTable(config, model);
|
|
27988
|
+
const order = Object.keys(table);
|
|
27989
|
+
const groups = openAICompatTable({ openaiCompatSizes: table }).sort((a, b) => order.indexOf(a.ratio) - order.indexOf(b.ratio));
|
|
27990
|
+
const resolutionOptions = Object.fromEntries(groups.map((group) => [group.ratio, group.tiers.map((entry) => ({
|
|
27991
|
+
value: entry.tier,
|
|
27992
|
+
label: `${entry.tier} · ${entry.size}`
|
|
27993
|
+
}))]));
|
|
27994
|
+
const defaultRatio = groups.some((group) => group.ratio === "1:1") ? "1:1" : groups[0]?.ratio ?? "1:1";
|
|
27995
|
+
return {
|
|
27996
|
+
...profile(provider, model, configured, groups.map((group) => option(group.ratio)), officialQualityOptions(model), defaultRatio, "auto"),
|
|
27997
|
+
resolutionOptions,
|
|
27998
|
+
defaultResolution: resolutionOptions[defaultRatio]?.[0]?.value ?? "1K"
|
|
27999
|
+
};
|
|
28000
|
+
}
|
|
28001
|
+
const cap = CLOUD_CAPABILITIES[provider];
|
|
28002
|
+
return profile(provider, model, configured, cap.ratioOptions, cap.qualityOptions, cap.defaultRatio, cap.defaultQuality);
|
|
28003
|
+
}
|
|
28004
|
+
/** Output capabilities remain visible before credentials, address or model are configured. */
|
|
28005
|
+
function compatibleStudioProfile(config, model, configured) {
|
|
28006
|
+
const groups = openAICompatTable(config);
|
|
28007
|
+
const qualityProfile = {
|
|
28008
|
+
generationQualityOptions: compatQualityOptions(config),
|
|
28009
|
+
defaultGenerationQuality: compatRequestQuality(config) ?? "__default__",
|
|
28010
|
+
resolutionOptions: Object.fromEntries(groups.map((group) => [group.ratio, group.tiers.map((entry) => ({
|
|
28011
|
+
value: entry.tier,
|
|
28012
|
+
label: `${entry.tier} · ${entry.size}`
|
|
28013
|
+
}))]))
|
|
28014
|
+
};
|
|
28015
|
+
if (config.openaiCompatSizes === void 0 || Object.keys(config.openaiCompatSizes).length === 0) return {
|
|
28016
|
+
...profile("openai-compat", model, configured, [
|
|
27663
28017
|
"1:1",
|
|
27664
28018
|
"3:2",
|
|
27665
28019
|
"2:3"
|
|
27666
28020
|
].map(option), [{
|
|
27667
28021
|
value: "standard",
|
|
27668
28022
|
label: "标准(推荐)"
|
|
27669
|
-
}], "1:1", "standard")
|
|
27670
|
-
|
|
27671
|
-
|
|
27672
|
-
|
|
27673
|
-
|
|
28023
|
+
}], "1:1", "standard"),
|
|
28024
|
+
...qualityProfile,
|
|
28025
|
+
resolutionOptions: Object.fromEntries(groups.map((group) => [group.ratio, [{
|
|
28026
|
+
value: "standard",
|
|
28027
|
+
label: `标准 · ${group.tiers[0].size}`
|
|
28028
|
+
}]]))
|
|
28029
|
+
};
|
|
28030
|
+
return {
|
|
28031
|
+
...openAICompatStudioProfile(config, model, configured),
|
|
28032
|
+
...qualityProfile
|
|
28033
|
+
};
|
|
27674
28034
|
}
|
|
27675
28035
|
function profile(provider, model, configured, ratioOptions, qualityOptions, defaultRatio, defaultQuality) {
|
|
27676
28036
|
return {
|
|
@@ -27704,7 +28064,12 @@ function option(value) {
|
|
|
27704
28064
|
function assertAllowed(profile, input) {
|
|
27705
28065
|
if (input.model !== profile.model) throw new Error("模型配置已变化,请刷新工作台后重试");
|
|
27706
28066
|
if (!profile.ratioOptions.some((option) => option.value === input.ratio)) throw new Error("该 Provider 不支持所选比例");
|
|
27707
|
-
|
|
28067
|
+
const selectedQuality = input.provider === "openai" ? input.generationQuality ?? input.quality : input.resolution ?? input.quality;
|
|
28068
|
+
if (!profile.qualityOptions.some((option) => option.value === selectedQuality)) throw new Error("该 Provider 不支持所选清晰度");
|
|
28069
|
+
if (input.resolution !== void 0 && profile.resolutionOptions !== void 0 && !profile.resolutionOptions[input.ratio]?.some((option) => option.value === input.resolution)) throw new Error("该比例不支持所选分辨率");
|
|
28070
|
+
if (input.generationQuality !== void 0 && input.provider === "openai-compat" && !profile.generationQualityOptions?.some((option) => option.value === input.generationQuality)) throw new Error("该 Provider 不支持所选生成质量");
|
|
28071
|
+
if ((input.resolution !== void 0 || input.generationQuality !== void 0) && input.provider !== "openai" && input.provider !== "openai-compat") throw new Error("该 Provider 不支持独立的 OpenAI 输出参数");
|
|
28072
|
+
if (input.watermark !== void 0 && input.watermark !== "default" && studioWatermarkCapability(profile, input.mode).status !== "supported") throw new Error("该渠道尚未确认当前操作的水印控制,请刷新工作台");
|
|
27708
28073
|
if (input.mode === "edit" && !profile.supportsEditing) throw new Error("该 Provider 暂不支持图生图");
|
|
27709
28074
|
}
|
|
27710
28075
|
async function readStudioReference(ctx, reference, signal) {
|
|
@@ -27808,6 +28173,10 @@ function parseStudioGenerateRequest(value) {
|
|
|
27808
28173
|
const model = requiredText(input.model, "请选择模型", 200);
|
|
27809
28174
|
const ratio = requiredText(input.ratio, "请选择比例", 32);
|
|
27810
28175
|
const quality = requiredText(input.quality, "请选择清晰度", 32);
|
|
28176
|
+
const resolution = input.resolution === void 0 ? void 0 : requiredText(input.resolution, "请选择分辨率", 32);
|
|
28177
|
+
const generationQuality = input.generationQuality === void 0 ? void 0 : requiredText(input.generationQuality, "请选择生成质量", 100);
|
|
28178
|
+
const watermark = input.watermark === void 0 ? void 0 : STUDIO_WATERMARK_VALUES.find((value) => value === input.watermark);
|
|
28179
|
+
if (input.watermark !== void 0 && watermark === void 0) throw new Error("请选择有效的水印选项");
|
|
27811
28180
|
const references = (Array.isArray(input.references) ? input.references : input.reference !== void 0 ? [input.reference] : []).map(parseReference);
|
|
27812
28181
|
if (input.mode === "edit" && references.length === 0) throw new Error("图生图需要至少一张参考图");
|
|
27813
28182
|
if (references.length > 5) throw new Error("最多支持上传 5 张参考图");
|
|
@@ -27824,6 +28193,9 @@ function parseStudioGenerateRequest(value) {
|
|
|
27824
28193
|
prompt,
|
|
27825
28194
|
ratio,
|
|
27826
28195
|
quality,
|
|
28196
|
+
...resolution === void 0 ? {} : { resolution },
|
|
28197
|
+
...generationQuality === void 0 ? {} : { generationQuality },
|
|
28198
|
+
...watermark === void 0 ? {} : { watermark },
|
|
27827
28199
|
...count !== void 0 ? { count } : {},
|
|
27828
28200
|
...references.length > 0 ? {
|
|
27829
28201
|
references,
|
|
@@ -28501,6 +28873,9 @@ function apply(ctx, config = {}) {
|
|
|
28501
28873
|
}
|
|
28502
28874
|
const size = args.size ?? active.imageSize;
|
|
28503
28875
|
const arkOptions = active.provider === "seedream" ? active.arkOptions : void 0;
|
|
28876
|
+
if (active.provider === "openai") validateOfficialSize(active.model, size);
|
|
28877
|
+
const quality = active.provider === "openai-compat" ? compatRequestQuality(current(), args.quality) : active.provider === "openai" ? args.quality : void 0;
|
|
28878
|
+
if (active.provider === "openai" && quality !== void 0 && !officialQualityOptions(active.model).some((option) => option.value === quality)) throw new Error("该 OpenAI 模型不支持所选生成质量");
|
|
28504
28879
|
return saveGenerated(ctx, await generateOpenAICompatibleImage({
|
|
28505
28880
|
provider: active.provider,
|
|
28506
28881
|
apiKey: credential,
|
|
@@ -28510,8 +28885,10 @@ function apply(ctx, config = {}) {
|
|
|
28510
28885
|
size,
|
|
28511
28886
|
maxBytes: ctx.attachments.imageLimits.maxImageBytes,
|
|
28512
28887
|
signal: exec.signal,
|
|
28888
|
+
...quality === void 0 ? {} : { quality },
|
|
28889
|
+
...active.provider === "openai-compat" ? { extraBody: current().openaiCompatExtraBody ?? {} } : {},
|
|
28513
28890
|
...arkOptions === void 0 ? {} : { arkOptions }
|
|
28514
|
-
}), active.provider, active.model, size, current(), exec, knownWorkspaceRoots);
|
|
28891
|
+
}), active.provider, active.model, quality === void 0 ? size : `${size}, quality=${quality}`, current(), exec, knownWorkspaceRoots);
|
|
28515
28892
|
};
|
|
28516
28893
|
ctx.tools.register(defineTool({
|
|
28517
28894
|
name: "generate_image",
|
|
@@ -28572,6 +28949,10 @@ function apply(ctx, config = {}) {
|
|
|
28572
28949
|
type: "string",
|
|
28573
28950
|
description: "Optional dimensions or size tier for OpenAI, Seedream, or DashScope; WIDTHxHEIGHT maps to aspect_ratio for xAI Grok."
|
|
28574
28951
|
},
|
|
28952
|
+
quality: {
|
|
28953
|
+
type: "string",
|
|
28954
|
+
description: "Optional generation quality for OpenAI or compatible APIs, independent of pixel size (e.g. auto, low, medium, high). Omit to use the configured default."
|
|
28955
|
+
},
|
|
28575
28956
|
workflow: {
|
|
28576
28957
|
type: "string",
|
|
28577
28958
|
description: "Optional name of the ComfyUI workflow to run; omit to use the active workflow from settings. Only meaningful when the ComfyUI provider is selected."
|
|
@@ -28643,6 +29024,10 @@ function apply(ctx, config = {}) {
|
|
|
28643
29024
|
type: "string",
|
|
28644
29025
|
description: "Optional dimensions or size tier for OpenAI, Seedream, or DashScope; WIDTHxHEIGHT maps to aspect_ratio for xAI Grok."
|
|
28645
29026
|
},
|
|
29027
|
+
quality: {
|
|
29028
|
+
type: "string",
|
|
29029
|
+
description: "Optional OpenAI or compatible generation quality, applied to every item independently of size."
|
|
29030
|
+
},
|
|
28646
29031
|
workflow: {
|
|
28647
29032
|
type: "string",
|
|
28648
29033
|
description: "Optional name of the ComfyUI workflow to run; omit to use the active workflow from settings."
|
|
@@ -28780,6 +29165,7 @@ function apply(ctx, config = {}) {
|
|
|
28780
29165
|
...args.aspect_ratio !== void 0 ? { aspect_ratio: args.aspect_ratio } : {},
|
|
28781
29166
|
...args.image_size !== void 0 ? { image_size: args.image_size } : {},
|
|
28782
29167
|
...args.size !== void 0 ? { size: args.size } : {},
|
|
29168
|
+
...args.quality !== void 0 ? { quality: args.quality } : {},
|
|
28783
29169
|
...args.workflow !== void 0 ? { workflow: args.workflow } : {}
|
|
28784
29170
|
}, exec);
|
|
28785
29171
|
images.push({
|
|
@@ -28883,6 +29269,10 @@ function apply(ctx, config = {}) {
|
|
|
28883
29269
|
type: "string",
|
|
28884
29270
|
description: "Optional output size for OpenAI, Seedream, or DashScope; WIDTHxHEIGHT maps to aspect_ratio for xAI Grok."
|
|
28885
29271
|
},
|
|
29272
|
+
quality: {
|
|
29273
|
+
type: "string",
|
|
29274
|
+
description: "Optional generation quality for OpenAI or compatible edits, independent of size."
|
|
29275
|
+
},
|
|
28886
29276
|
workflow: {
|
|
28887
29277
|
type: "string",
|
|
28888
29278
|
description: "Optional name of the ComfyUI workflow to run; omit to use the active workflow from settings. Only meaningful when the ComfyUI provider is selected."
|
|
@@ -28962,6 +29352,9 @@ function apply(ctx, config = {}) {
|
|
|
28962
29352
|
}
|
|
28963
29353
|
const size = args.size ?? active.imageSize;
|
|
28964
29354
|
if (active.provider === "openai" || active.provider === "openai-compat" || active.provider === "xai" || active.provider === "zhipu") {
|
|
29355
|
+
if (active.provider === "openai") validateOfficialSize(active.model, size);
|
|
29356
|
+
const quality = active.provider === "openai-compat" ? compatRequestQuality(current(), args.quality) : active.provider === "openai" ? args.quality : void 0;
|
|
29357
|
+
if (active.provider === "openai" && quality !== void 0 && !officialQualityOptions(active.model).some((option) => option.value === quality)) throw new Error("该 OpenAI 模型不支持所选生成质量");
|
|
28965
29358
|
const xaiExtraBody = active.provider === "xai" ? xaiToolParameters({
|
|
28966
29359
|
...args.aspect_ratio !== void 0 ? { aspectRatio: args.aspect_ratio } : {},
|
|
28967
29360
|
...args.image_size !== void 0 ? { imageSize: args.image_size } : {},
|
|
@@ -28974,16 +29367,18 @@ function apply(ctx, config = {}) {
|
|
|
28974
29367
|
prompt: args.prompt,
|
|
28975
29368
|
sourceImages,
|
|
28976
29369
|
...active.provider === "xai" ? {} : { size },
|
|
29370
|
+
...quality === void 0 ? {} : { quality },
|
|
28977
29371
|
maxBytes: ctx.attachments.imageLimits.maxImageBytes,
|
|
28978
29372
|
signal: exec.signal,
|
|
28979
29373
|
...active.provider === "openai-compat" ? {
|
|
28980
29374
|
editFormat: active.editFormat,
|
|
28981
|
-
editExtra: active.editExtra
|
|
29375
|
+
editExtra: active.editExtra,
|
|
29376
|
+
editSizeMode: current().openaiCompatEditSizeMode ?? "auto"
|
|
28982
29377
|
} : active.provider === "xai" ? {
|
|
28983
29378
|
editFormat: "xaiJson",
|
|
28984
29379
|
...xaiExtraBody === void 0 ? {} : { extraBody: xaiExtraBody }
|
|
28985
29380
|
} : {}
|
|
28986
|
-
}), active.provider, active.model, active.provider === "xai" ? xaiExtraBody?.aspect_ratio ?? "auto" : size, current(), exec, knownWorkspaceRoots);
|
|
29381
|
+
}), active.provider, active.model, active.provider === "xai" ? xaiExtraBody?.aspect_ratio ?? "auto" : quality === void 0 ? size : `${size}, quality=${quality}`, current(), exec, knownWorkspaceRoots);
|
|
28987
29382
|
}
|
|
28988
29383
|
if (active.provider === "seedream") return saveGenerated(ctx, await editSeedreamImage({
|
|
28989
29384
|
apiKey: credential,
|