dsh-agy 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -1
- package/lib/cli/index.d.mts.map +1 -1
- package/lib/cli/index.mjs +52 -14
- package/lib/cli/index.mjs.map +1 -1
- package/lib/{exchange-MhFUiBAm.mjs → exchange-B1UGNtrv.mjs} +2 -2
- package/lib/{exchange-MhFUiBAm.mjs.map → exchange-B1UGNtrv.mjs.map} +1 -1
- package/lib/{import-DnKzgr-0.mjs → import-DjCR0KEj.mjs} +21 -4
- package/lib/{import-DnKzgr-0.mjs.map → import-DjCR0KEj.mjs.map} +1 -1
- package/lib/index.mjs +1 -1
- package/lib/{models-Cu1g7AXV.mjs → models-DagpZ75_.mjs} +2 -2
- package/lib/{models-Cu1g7AXV.mjs.map → models-DagpZ75_.mjs.map} +1 -1
- package/lib/{plugin-common-I56kmGjr.mjs → plugin-common-YSDKp1DG.mjs} +4 -4
- package/lib/{plugin-common-I56kmGjr.mjs.map → plugin-common-YSDKp1DG.mjs.map} +1 -1
- package/lib/{session-tawi2_uh.mjs → session-DFFF5_Yg.mjs} +3 -3
- package/lib/{session-tawi2_uh.mjs.map → session-DFFF5_Yg.mjs.map} +1 -1
- package/lib/{translate-h2bjH7ub.mjs → translate-DgqRjGFH.mjs} +53 -10
- package/lib/translate-DgqRjGFH.mjs.map +1 -0
- package/lib/web/plugin.mjs +71 -30
- package/lib/web/plugin.mjs.map +1 -1
- package/package.json +2 -2
- package/lib/translate-h2bjH7ub.mjs.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
-
import { g as generateAntigravityRequestId } from "./session-
|
|
2
|
+
import { g as generateAntigravityRequestId } from "./session-DFFF5_Yg.mjs";
|
|
3
3
|
const DEFAULT_TTL_MS = 36e5;
|
|
4
4
|
const MAX_ENTRIES = 2e3;
|
|
5
5
|
const store = /* @__PURE__ */ new Map();
|
|
@@ -55,12 +55,53 @@ function stripTrailingModelTurn(contents) {
|
|
|
55
55
|
while (contents.length > 1 && contents[contents.length - 1]?.role === "model") contents.pop();
|
|
56
56
|
return contents;
|
|
57
57
|
}
|
|
58
|
-
/**
|
|
59
|
-
|
|
58
|
+
/**
|
|
59
|
+
* The Antigravity backend parses tool `parameters` as a strict protobuf
|
|
60
|
+
* schema and rejects ANY unknown keyword with 400 (verified empirically:
|
|
61
|
+
* `$schema`, `propertyNames`, `pattern`, `minLength`, ... each fail in turn).
|
|
62
|
+
* Denylisting is whack-a-mole, so keep only the keywords the upstream
|
|
63
|
+
* accepts. Container shapes are handled distinctly: `properties` is a
|
|
64
|
+
* name->schema map (keys preserved), `items`/`additionalProperties` are
|
|
65
|
+
* nested schemas, `required`/`enum` are plain arrays.
|
|
66
|
+
*/
|
|
67
|
+
const AGY_SCHEMA_ALLOWLIST = /* @__PURE__ */ new Set([
|
|
68
|
+
"type",
|
|
69
|
+
"format",
|
|
70
|
+
"title",
|
|
71
|
+
"description",
|
|
72
|
+
"nullable",
|
|
73
|
+
"items",
|
|
74
|
+
"enum",
|
|
75
|
+
"default",
|
|
76
|
+
"properties",
|
|
77
|
+
"required",
|
|
78
|
+
"additionalProperties"
|
|
79
|
+
]);
|
|
80
|
+
const AGY_SCHEMA_MAP_KEYS = /* @__PURE__ */ new Set(["properties"]);
|
|
81
|
+
const AGY_SCHEMA_NESTED_KEYS = /* @__PURE__ */ new Set(["items", "additionalProperties"]);
|
|
82
|
+
const AGY_SCHEMA_LIST_KEYS = /* @__PURE__ */ new Set(["required", "enum"]);
|
|
83
|
+
function sanitizeToolSchema(schema) {
|
|
60
84
|
if (!schema || typeof schema !== "object") return schema;
|
|
61
|
-
if (Array.isArray(schema)) return schema.map((entry) =>
|
|
85
|
+
if (Array.isArray(schema)) return schema.map((entry) => sanitizeToolSchema(entry));
|
|
62
86
|
const result = {};
|
|
63
|
-
for (const [key, value] of Object.entries(schema))
|
|
87
|
+
for (const [key, value] of Object.entries(schema)) {
|
|
88
|
+
if (!AGY_SCHEMA_ALLOWLIST.has(key)) continue;
|
|
89
|
+
if (AGY_SCHEMA_MAP_KEYS.has(key)) {
|
|
90
|
+
const map = {};
|
|
91
|
+
for (const [name, child] of Object.entries(value)) map[name] = sanitizeToolSchema(child);
|
|
92
|
+
result[key] = map;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (AGY_SCHEMA_NESTED_KEYS.has(key)) {
|
|
96
|
+
result[key] = sanitizeToolSchema(value);
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (AGY_SCHEMA_LIST_KEYS.has(key)) {
|
|
100
|
+
result[key] = value;
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
result[key] = value;
|
|
104
|
+
}
|
|
64
105
|
return result;
|
|
65
106
|
}
|
|
66
107
|
/** Collect tool-call names by id so tool-result blocks can name their function. */
|
|
@@ -77,9 +118,11 @@ function blockToParts(block, toolNames) {
|
|
|
77
118
|
text: block.text
|
|
78
119
|
}];
|
|
79
120
|
case "tool-call": {
|
|
80
|
-
let args =
|
|
81
|
-
|
|
82
|
-
|
|
121
|
+
let args = {};
|
|
122
|
+
if (typeof block.arguments === "object" && block.arguments !== null && !Array.isArray(block.arguments)) args = block.arguments;
|
|
123
|
+
else if (typeof block.arguments === "string") try {
|
|
124
|
+
const parsed = JSON.parse(block.arguments);
|
|
125
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) args = parsed;
|
|
83
126
|
} catch {}
|
|
84
127
|
return [{
|
|
85
128
|
thoughtSignature: getThoughtSignature(block.id) ?? "skip_thought_signature_validator",
|
|
@@ -113,7 +156,7 @@ function toolsToDeclarations(tools) {
|
|
|
113
156
|
return [{ functionDeclarations: tools.map((tool) => ({
|
|
114
157
|
name: tool.name,
|
|
115
158
|
description: tool.description,
|
|
116
|
-
parameters:
|
|
159
|
+
parameters: sanitizeToolSchema(tool.parameters)
|
|
117
160
|
})) }];
|
|
118
161
|
}
|
|
119
162
|
/** Build the wrapped Antigravity request body for one call. */
|
|
@@ -145,4 +188,4 @@ function toAgyRequestBody(options, context) {
|
|
|
145
188
|
//#endregion
|
|
146
189
|
export { translate_exports as n, setThoughtSignature as r, toAgyRequestBody as t };
|
|
147
190
|
|
|
148
|
-
//# sourceMappingURL=translate-
|
|
191
|
+
//# sourceMappingURL=translate-DgqRjGFH.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"translate-DgqRjGFH.mjs","names":[],"sources":["../src/runtime/signature-cache.ts","../src/adapter/translate.ts"],"sourcesContent":["/**\n * Gemini thought-signature cache for Antigravity tool calls.\n *\n * The Antigravity backend requires every outbound `functionCall` part to carry\n * a `thoughtSignature` (400 \"Function call is missing a thought_signature\"\n * otherwise). Signatures arrive on the *response* side — as a sibling field of\n * functionCall parts, and on thought parts — and must be replayed on the next\n * turn for the same tool call id. When nothing is cached, the translator\n * injects the `skip_thought_signature_validator` sentinel (the established\n * bypass both reference implementations use by default).\n *\n * Process-local with TTL; a cold process falls back to the sentinel, which the\n * upstream accepts, so cross-process persistence is not required for\n * correctness.\n */\n\nexport const THOUGHT_SIGNATURE_SENTINEL = 'skip_thought_signature_validator'\n\ninterface CacheEntry {\n signature: string\n expiresAt: number\n}\n\nconst DEFAULT_TTL_MS = 60 * 60 * 1000\nconst MAX_ENTRIES = 2000\n\nconst store = new Map<string, CacheEntry>()\n\nfunction pruneExpired(now = Date.now()): void {\n if (store.size < MAX_ENTRIES / 2) return\n for (const [key, entry] of store) {\n if (entry.expiresAt <= now) store.delete(key)\n }\n}\n\n/** Store the signature observed for one tool call id (response side). */\nexport function setThoughtSignature(toolCallId: string, signature: string, ttlMs = DEFAULT_TTL_MS): void {\n if (!toolCallId || !signature) return\n pruneExpired()\n if (store.size >= MAX_ENTRIES) {\n // Evict the oldest entry to keep the cache bounded.\n let oldestKey: string | null = null\n let oldestExpiry = Infinity\n for (const [key, entry] of store) {\n if (entry.expiresAt < oldestExpiry) {\n oldestExpiry = entry.expiresAt\n oldestKey = key\n }\n }\n if (oldestKey) store.delete(oldestKey)\n }\n store.set(toolCallId, { signature, expiresAt: Date.now() + ttlMs })\n}\n\n/** Resolve the signature for one tool call id, or null when unknown/expired. */\nexport function getThoughtSignature(toolCallId: string): string | null {\n if (!toolCallId) return null\n const entry = store.get(toolCallId)\n if (!entry) return null\n if (entry.expiresAt <= Date.now()) {\n store.delete(toolCallId)\n return null\n }\n return entry.signature\n}\n","/**\n * Translate a DSH GenerateOptions into the Antigravity wrapped request.\n *\n * Envelope shape follows the actively-maintained OmniRoute wire format\n * (the archived opencode reference predates it): top-level `project`,\n * `requestId`, `model`, `userAgent`, `requestType`, with the Gemini-style\n * body under `request` (contents/systemInstruction/tools/generationConfig/\n * sessionId). `toolConfig` VALIDATED is attached when tools are present, and\n * Claude-path requests strip trailing model turns (Vertex rejects \"assistant\n * message prefill\").\n *\n * Thinking blocks are carried as-is (Gemini `thought` parts); nothing is\n * stripped or re-signed — that signature dance was an artifact of the\n * reference plugin's interception architecture (see docs/ARCHITECTURE.md).\n */\n\nimport type { ContentBlock, GenerateOptions, Message, ToolSchema } from '@deepseek-ai/dsh-llm'\nimport { generateAntigravityRequestId } from '../runtime/identity.ts'\nimport { getThoughtSignature, THOUGHT_SIGNATURE_SENTINEL } from '../runtime/signature-cache.ts'\n\nexport type AgyPart =\n | { text: string }\n | { thought: true; text: string }\n | { thoughtSignature: string; functionCall: { id: string; name: string; args: unknown } }\n | { functionResponse: { name: string; response: unknown } }\n\nexport interface AgyContent {\n role: 'user' | 'model'\n parts: AgyPart[]\n}\n\nexport interface AgyRequestBody {\n project?: string\n requestId?: string\n model: string\n userAgent?: string\n requestType?: 'agent'\n request: {\n contents: AgyContent[]\n systemInstruction?: { parts: Array<{ text: string }> }\n tools?: Array<{ functionDeclarations: Array<{ name: string; description: string; parameters: unknown }> }>\n toolConfig?: { functionCallingConfig: { mode: 'VALIDATED' } }\n generationConfig?: {\n temperature?: number\n maxOutputTokens?: number\n stopSequences?: string[]\n }\n sessionId?: string\n }\n}\n\n/** Whether a model id belongs to a Claude-branded model (Vertex-hosted). */\nexport function isClaudeModel(model: string): boolean {\n return model.startsWith('claude-') || model.includes('/claude')\n}\n\n/**\n * Vertex (the Antigravity Claude backend) rejects conversations ending on an\n * assistant/model turn (\"assistant message prefill\"); never strip to empty.\n */\nexport function stripTrailingModelTurn(contents: AgyContent[]): AgyContent[] {\n while (contents.length > 1 && contents[contents.length - 1]?.role === 'model') {\n contents.pop()\n }\n return contents\n}\n\n/**\n * The Antigravity backend parses tool `parameters` as a strict protobuf\n * schema and rejects ANY unknown keyword with 400 (verified empirically:\n * `$schema`, `propertyNames`, `pattern`, `minLength`, ... each fail in turn).\n * Denylisting is whack-a-mole, so keep only the keywords the upstream\n * accepts. Container shapes are handled distinctly: `properties` is a\n * name->schema map (keys preserved), `items`/`additionalProperties` are\n * nested schemas, `required`/`enum` are plain arrays.\n */\nconst AGY_SCHEMA_ALLOWLIST = new Set([\n 'type', 'format', 'title', 'description', 'nullable',\n 'items', 'enum', 'default', 'properties', 'required', 'additionalProperties',\n])\nconst AGY_SCHEMA_MAP_KEYS = new Set(['properties'])\nconst AGY_SCHEMA_NESTED_KEYS = new Set(['items', 'additionalProperties'])\nconst AGY_SCHEMA_LIST_KEYS = new Set(['required', 'enum'])\n\nfunction sanitizeToolSchema(schema: unknown): unknown {\n if (!schema || typeof schema !== 'object') return schema\n if (Array.isArray(schema)) return schema.map((entry) => sanitizeToolSchema(entry))\n const result: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(schema as Record<string, unknown>)) {\n if (!AGY_SCHEMA_ALLOWLIST.has(key)) continue\n if (AGY_SCHEMA_MAP_KEYS.has(key)) {\n const map: Record<string, unknown> = {}\n for (const [name, child] of Object.entries(value as Record<string, unknown>)) {\n map[name] = sanitizeToolSchema(child)\n }\n result[key] = map\n continue\n }\n if (AGY_SCHEMA_NESTED_KEYS.has(key)) {\n result[key] = sanitizeToolSchema(value)\n continue\n }\n if (AGY_SCHEMA_LIST_KEYS.has(key)) {\n result[key] = value\n continue\n }\n result[key] = value\n }\n return result\n}\n\n/** Collect tool-call names by id so tool-result blocks can name their function. */\nfunction buildToolNameIndex(messages: readonly Message[]): Map<string, string> {\n const index = new Map<string, string>()\n for (const message of messages) {\n for (const block of message.content) {\n if (block.type === 'tool-call') {\n index.set(block.id, block.name)\n }\n }\n }\n return index\n}\n\nfunction blockToParts(block: ContentBlock, toolNames: Map<string, string>): AgyPart[] {\n switch (block.type) {\n case 'text':\n return [{ text: block.text }]\n case 'reasoning':\n return [{ thought: true, text: block.text }]\n case 'tool-call': {\n // Upstream parses functionCall.args as google.protobuf.Struct and\n // rejects a raw string with 400. Guarantee an object: parse the string\n // form, fall back to {} when it is truncated/malformed (the failure\n // mode for long multi-turn histories).\n let args: unknown = {}\n if (typeof block.arguments === 'object' && block.arguments !== null && !Array.isArray(block.arguments)) {\n args = block.arguments\n } else if (typeof block.arguments === 'string') {\n try {\n const parsed: unknown = JSON.parse(block.arguments)\n if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {\n args = parsed\n }\n } catch {\n // truncated/malformed JSON -> empty object\n }\n }\n // Antigravity rejects functionCall parts without a thoughtSignature\n // (400). Replay the signature captured for this tool call id on the\n // previous turn; the sentinel is the established bypass when nothing is\n // cached (both reference implementations default to it).\n const signature = getThoughtSignature(block.id) ?? THOUGHT_SIGNATURE_SENTINEL\n return [{\n thoughtSignature: signature,\n functionCall: { id: block.id, name: block.name, args },\n }]\n }\n case 'tool-result': {\n const name = toolNames.get(block.toolCallId) ?? block.toolCallId\n const text = block.content\n .filter((b): b is Extract<ContentBlock, { type: 'text' }> => b.type === 'text')\n .map((b) => b.text)\n .join('\\n')\n return [{\n functionResponse: {\n name,\n response: { result: text, is_error: block.isError === true },\n },\n }]\n }\n default:\n return [] // unknown block types (merge-extensible) are skipped\n }\n}\n\nfunction messageToContent(message: Message, toolNames: Map<string, string>): AgyContent | null {\n const parts = message.content.flatMap((block) => blockToParts(block, toolNames))\n if (parts.length === 0) return null\n const role = message.role === 'assistant' ? 'model' : 'user'\n return { role, parts }\n}\n\nfunction toolsToDeclarations(tools: ToolSchema[] | undefined): AgyRequestBody['request']['tools'] {\n if (!tools || tools.length === 0) return undefined\n return [{\n functionDeclarations: tools.map((tool) => ({\n name: tool.name,\n description: tool.description,\n parameters: sanitizeToolSchema(tool.parameters),\n })),\n }]\n}\n\n/** Build the wrapped Antigravity request body for one call. */\nexport function toAgyRequestBody(\n options: GenerateOptions,\n context: { projectId?: string; sessionId?: string },\n): AgyRequestBody {\n const toolNames = buildToolNameIndex(options.messages)\n let contents = options.messages\n .map((message) => messageToContent(message, toolNames))\n .filter((c): c is AgyContent => c !== null)\n if (isClaudeModel(options.model)) {\n contents = stripTrailingModelTurn(contents)\n }\n\n const tools = toolsToDeclarations(options.tools)\n const generationConfig: NonNullable<AgyRequestBody['request']['generationConfig']> = {}\n if (options.temperature !== undefined) generationConfig.temperature = options.temperature\n if (options.maxTokens !== undefined) generationConfig.maxOutputTokens = options.maxTokens\n if (options.stop !== undefined && options.stop.length > 0) generationConfig.stopSequences = options.stop\n\n return {\n project: context.projectId || undefined,\n requestId: generateAntigravityRequestId(),\n model: options.model,\n userAgent: 'antigravity',\n requestType: 'agent',\n request: {\n contents,\n ...(options.system ? { systemInstruction: { parts: [{ text: options.system }] } } : {}),\n ...(tools ? { tools } : {}),\n ...(tools ? { toolConfig: { functionCallingConfig: { mode: 'VALIDATED' } } } : {}),\n ...(Object.keys(generationConfig).length > 0 ? { generationConfig } : {}),\n ...(context.sessionId ? { sessionId: context.sessionId } : {}),\n },\n }\n}\n"],"mappings":"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"}
|
package/lib/web/plugin.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { m as resolveAntigravityVersionBounded, n as generateFingerprint, r as recordFingerprintVersion } from "../session-
|
|
2
|
-
import { t as createAgyRuntime } from "../plugin-common-
|
|
3
|
-
import { t as exchangeAntigravity } from "../exchange-
|
|
4
|
-
import { n as upsertImportedAccount, r as authorizeAntigravity, t as
|
|
1
|
+
import { m as resolveAntigravityVersionBounded, n as generateFingerprint, r as recordFingerprintVersion } from "../session-DFFF5_Yg.mjs";
|
|
2
|
+
import { t as createAgyRuntime } from "../plugin-common-YSDKp1DG.mjs";
|
|
3
|
+
import { t as exchangeAntigravity } from "../exchange-B1UGNtrv.mjs";
|
|
4
|
+
import { n as upsertImportedAccount, r as authorizeAntigravity, t as importManySources } from "../import-DjCR0KEj.mjs";
|
|
5
5
|
//#region src/web/i18n.ts
|
|
6
6
|
const I18N_DICT = {
|
|
7
7
|
en: {
|
|
@@ -38,11 +38,14 @@ const I18N_DICT = {
|
|
|
38
38
|
testing: "Testing...",
|
|
39
39
|
copied: "Copied to clipboard!",
|
|
40
40
|
importTitle: "Import Credentials",
|
|
41
|
-
importDesc: "Paste an agy auth.json token document, or a credential blob (from dsh-agy login --blob):",
|
|
42
|
-
importPlaceholder: "{\"token\":{\"access_token\":\"...\",\"refresh_token\":\"...\"}} or dsh-agy-cred-v1....",
|
|
41
|
+
importDesc: "Paste an agy auth.json token document, or a credential blob (from dsh-agy login --blob). Paste multiple blobs, one per line, for a batch import:",
|
|
42
|
+
importPlaceholder: "{\"token\":{\"access_token\":\"...\",\"refresh_token\":\"...\"}} or dsh-agy-cred-v1.... (one blob per line for batch)",
|
|
43
43
|
importJson: "Import JSON",
|
|
44
44
|
importBlobBtn: "Import Blob",
|
|
45
45
|
importSuccess: "Successfully imported account: ",
|
|
46
|
+
exportAll: "Export All",
|
|
47
|
+
importBatchResult: "Imported {imported}, replaced {replaced}",
|
|
48
|
+
copyAllMsg: "All credential blobs exported. Copied to clipboard!",
|
|
46
49
|
fingerprintTitle: "Device Fingerprint",
|
|
47
50
|
fingerprintDevice: "Device ID",
|
|
48
51
|
fingerprintUserAgent: "User Agent",
|
|
@@ -97,11 +100,14 @@ const I18N_DICT = {
|
|
|
97
100
|
testing: "测试中...",
|
|
98
101
|
copied: "已复制到剪贴板!",
|
|
99
102
|
importTitle: "导入凭据",
|
|
100
|
-
importDesc: "粘贴 agy auth.json 文件内容,或输入凭据 blob(来自 dsh-agy login --blob
|
|
101
|
-
importPlaceholder: "{\"token\":{\"access_token\":\"...\",\"refresh_token\":\"...\"}} 或 dsh-agy-cred-v1
|
|
103
|
+
importDesc: "粘贴 agy auth.json 文件内容,或输入凭据 blob(来自 dsh-agy login --blob)。多个 blob 每行一个即可批量导入:",
|
|
104
|
+
importPlaceholder: "{\"token\":{\"access_token\":\"...\",\"refresh_token\":\"...\"}} 或 dsh-agy-cred-v1....(批量:每行一个 blob)",
|
|
102
105
|
importJson: "导入 JSON",
|
|
103
106
|
importBlobBtn: "导入 Blob",
|
|
104
107
|
importSuccess: "成功导入账号:",
|
|
108
|
+
exportAll: "导出全部",
|
|
109
|
+
importBatchResult: "导入 {imported} 个,替换 {replaced} 个",
|
|
110
|
+
copyAllMsg: "全部凭据 Blob 已导出并复制到剪贴板!",
|
|
105
111
|
fingerprintTitle: "设备指纹信息",
|
|
106
112
|
fingerprintDevice: "设备 ID (DeviceId)",
|
|
107
113
|
fingerprintUserAgent: "用户代理 (User-Agent)",
|
|
@@ -669,9 +675,9 @@ function renderDashboardHtml() {
|
|
|
669
675
|
<span id="txt-import-title">Import Credentials</span>
|
|
670
676
|
</div>
|
|
671
677
|
<p style="font-size:12.5px;color:var(--text-secondary);margin-bottom:8px;" id="txt-import-desc">
|
|
672
|
-
Paste an agy auth.json token document, or a credential blob (from dsh-agy login --blob):
|
|
678
|
+
Paste an agy auth.json token document, or a credential blob (from dsh-agy login --blob). Paste multiple blobs, one per line, for a batch import:
|
|
673
679
|
</p>
|
|
674
|
-
<textarea id="input-import" class="import-textarea" placeholder='{"token":{"access_token":"...","refresh_token":"..."}} or dsh-agy-cred-v1....'></textarea>
|
|
680
|
+
<textarea id="input-import" class="import-textarea" placeholder='{"token":{"access_token":"...","refresh_token":"..."}} or dsh-agy-cred-v1.... (one blob per line for batch)'></textarea>
|
|
675
681
|
<div style="display:flex;gap:8px;">
|
|
676
682
|
<button id="btn-import-json" class="btn btn-secondary">
|
|
677
683
|
<span id="txt-import-json">Import JSON</span>
|
|
@@ -679,6 +685,10 @@ function renderDashboardHtml() {
|
|
|
679
685
|
<button id="btn-import-blob" class="btn btn-secondary">
|
|
680
686
|
<span id="txt-import-blob">Import Blob</span>
|
|
681
687
|
</button>
|
|
688
|
+
<span style="flex:1"></span>
|
|
689
|
+
<button id="btn-export-all" class="btn btn-secondary">
|
|
690
|
+
<span id="txt-export-all">Export All</span>
|
|
691
|
+
</button>
|
|
682
692
|
</div>
|
|
683
693
|
</section>
|
|
684
694
|
</div>
|
|
@@ -782,6 +792,7 @@ function renderDashboardHtml() {
|
|
|
782
792
|
$('txt-import-desc').textContent = t('importDesc');
|
|
783
793
|
$('txt-import-json').textContent = t('importJson');
|
|
784
794
|
$('txt-import-blob').textContent = t('importBlobBtn');
|
|
795
|
+
$('txt-export-all').textContent = t('exportAll');
|
|
785
796
|
$('input-import').placeholder = t('importPlaceholder');
|
|
786
797
|
}
|
|
787
798
|
|
|
@@ -1073,25 +1084,34 @@ function renderDashboardHtml() {
|
|
|
1073
1084
|
} catch (e) { toast(String(e), 'error'); }
|
|
1074
1085
|
};
|
|
1075
1086
|
|
|
1076
|
-
$('
|
|
1077
|
-
|
|
1078
|
-
|
|
1087
|
+
const importLines = () => $('input-import').value.split('\n')
|
|
1088
|
+
.map((s) => s.trim())
|
|
1089
|
+
.filter(Boolean);
|
|
1090
|
+
|
|
1091
|
+
const doImport = async (kind) => {
|
|
1092
|
+
const sources = importLines();
|
|
1093
|
+
if (sources.length === 0) return;
|
|
1079
1094
|
try {
|
|
1080
|
-
const r = await api('/import', { method: 'POST', body: JSON.stringify({ kind
|
|
1081
|
-
|
|
1095
|
+
const r = await api('/import', { method: 'POST', body: JSON.stringify({ kind, sources }) });
|
|
1096
|
+
if (r.errors && r.errors.length > 0) toast(r.errors[0], 'error');
|
|
1097
|
+
toast(t('importBatchResult').replace('{imported}', r.imported).replace('{replaced}', r.replaced), 'success');
|
|
1082
1098
|
$('input-import').value = '';
|
|
1083
1099
|
render();
|
|
1084
1100
|
} catch (e) { toast(String(e), 'error'); }
|
|
1085
1101
|
};
|
|
1086
1102
|
|
|
1087
|
-
$('btn-import-
|
|
1088
|
-
|
|
1089
|
-
|
|
1103
|
+
$('btn-import-json').onclick = () => doImport('json');
|
|
1104
|
+
$('btn-import-blob').onclick = () => doImport('blob');
|
|
1105
|
+
|
|
1106
|
+
const btnExportAll = $('btn-export-all');
|
|
1107
|
+
if (btnExportAll) btnExportAll.onclick = async () => {
|
|
1090
1108
|
try {
|
|
1091
|
-
const r = await api('/
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1109
|
+
const r = await api('/export-all', { method: 'POST' });
|
|
1110
|
+
if (!r.blobs || r.blobs.length === 0) { toast('No accounts to export', 'info'); return; }
|
|
1111
|
+
const text = r.blobs.map((b) => b.blob).join('\n');
|
|
1112
|
+
copyToClipboard(text, t('copyAllMsg'));
|
|
1113
|
+
$('diagnostics-panel').open = true;
|
|
1114
|
+
$('diagnostics-output').textContent = text;
|
|
1095
1115
|
} catch (e) { toast(String(e), 'error'); }
|
|
1096
1116
|
};
|
|
1097
1117
|
|
|
@@ -1298,7 +1318,7 @@ function createAgyWebRoutes(options) {
|
|
|
1298
1318
|
const session = await sessions.getSession().catch(() => void 0);
|
|
1299
1319
|
if (!session || session.index !== entry.index) continue;
|
|
1300
1320
|
try {
|
|
1301
|
-
const { fetchAvailableModels } = await import("../models-
|
|
1321
|
+
const { fetchAvailableModels } = await import("../models-DagpZ75_.mjs").then((n) => n.n);
|
|
1302
1322
|
const discovered = await fetchAvailableModels(session.auth.access, session.account.projectId);
|
|
1303
1323
|
const models = Object.entries(discovered.models ?? {});
|
|
1304
1324
|
if (models.length > 0) {
|
|
@@ -1381,12 +1401,28 @@ function createAgyWebRoutes(options) {
|
|
|
1381
1401
|
try {
|
|
1382
1402
|
const body = await readJson(req);
|
|
1383
1403
|
const kind = body.kind === "blob" ? "blob" : "json";
|
|
1384
|
-
const
|
|
1385
|
-
|
|
1386
|
-
sendJson(res, 200, {
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
});
|
|
1404
|
+
const sources = Array.isArray(body.sources) ? body.sources : body.source !== void 0 ? [body.source] : [];
|
|
1405
|
+
if (sources.length === 0) throw new Error("nothing to import");
|
|
1406
|
+
sendJson(res, 200, await importManySources(sources.map((source) => ({
|
|
1407
|
+
source,
|
|
1408
|
+
kind
|
|
1409
|
+
})), store, { overwriteExisting: true }));
|
|
1410
|
+
} catch (error) {
|
|
1411
|
+
sendJson(res, 400, { error: error instanceof Error ? error.message : String(error) });
|
|
1412
|
+
}
|
|
1413
|
+
};
|
|
1414
|
+
const handleExportAll = async (_req, res) => {
|
|
1415
|
+
try {
|
|
1416
|
+
const storage = await store.load();
|
|
1417
|
+
const blobs = [];
|
|
1418
|
+
for (let index = 0; index < storage.accounts.length; index++) {
|
|
1419
|
+
const result = await sessions.exportBlob(index);
|
|
1420
|
+
if (result.blob) blobs.push({
|
|
1421
|
+
index,
|
|
1422
|
+
blob: result.blob
|
|
1423
|
+
});
|
|
1424
|
+
}
|
|
1425
|
+
sendJson(res, 200, { blobs });
|
|
1390
1426
|
} catch (error) {
|
|
1391
1427
|
sendJson(res, 400, { error: error instanceof Error ? error.message : String(error) });
|
|
1392
1428
|
}
|
|
@@ -1438,7 +1474,7 @@ function createAgyWebRoutes(options) {
|
|
|
1438
1474
|
return;
|
|
1439
1475
|
}
|
|
1440
1476
|
try {
|
|
1441
|
-
const { listAgyModels } = await import("../models-
|
|
1477
|
+
const { listAgyModels } = await import("../models-DagpZ75_.mjs").then((n) => n.n);
|
|
1442
1478
|
const models = await listAgyModels(session.auth.access, session.account.projectId);
|
|
1443
1479
|
sendJson(res, 200, {
|
|
1444
1480
|
account: session.account.email ?? null,
|
|
@@ -1544,6 +1580,11 @@ function createAgyWebRoutes(options) {
|
|
|
1544
1580
|
path: "/agy/api/import",
|
|
1545
1581
|
handler: handleImport
|
|
1546
1582
|
},
|
|
1583
|
+
{
|
|
1584
|
+
kind: "exact",
|
|
1585
|
+
path: "/agy/api/export-all",
|
|
1586
|
+
handler: handleExportAll
|
|
1587
|
+
},
|
|
1547
1588
|
{
|
|
1548
1589
|
kind: "exact",
|
|
1549
1590
|
path: "/agy/api/verify",
|