gaoding-cli 1.0.0-alpha.17 → 1.0.0-alpha.19

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.
@@ -1,4 +1,4 @@
1
- import { buildCreativeRequest, creativeContextId, creativeTurnMessages, normalizeCreativeMessages } from "./creative-protocol.js";
1
+ import { buildCreativeRequest, creativeContextId, creativeTurnMessages, normalizeCreativeMessages, restoreCreativeResourceUrls } from "./creative-protocol.js";
2
2
  import { parseCreativeStream } from "./creative-stream.js";
3
3
  export function createCreativeAgentAdapter(dependencies) {
4
4
  return {
@@ -29,7 +29,7 @@ export function createCreativeAgentAdapter(dependencies) {
29
29
  organizationId: access.organizationId,
30
30
  signal
31
31
  });
32
- return normalizeCreativeMessages(creativeTurnMessages(messages, ids.localMessageId));
32
+ return normalizeCreativeMessages(restoreCreativeResourceUrls(creativeTurnMessages(messages, ids.localMessageId), streamed));
33
33
  });
34
34
  }
35
35
  };
@@ -190,6 +190,115 @@ function functionResponseParts(raw) {
190
190
  }
191
191
  return parts;
192
192
  }
193
+ function hasAuthKey(url) {
194
+ try {
195
+ const value = new URL(url).searchParams.get("auth_key");
196
+ return value !== null && value.trim() !== "";
197
+ }
198
+ catch {
199
+ return false;
200
+ }
201
+ }
202
+ function resourceUrlIdentity(url) {
203
+ try {
204
+ const parsed = new URL(url);
205
+ parsed.searchParams.delete("auth_key");
206
+ return parsed.toString();
207
+ }
208
+ catch {
209
+ return undefined;
210
+ }
211
+ }
212
+ function requiresAuthKey(url) {
213
+ try {
214
+ return /^gd-ai-[a-z0-9-]+\.dancf\.com$/iu.test(new URL(url).hostname);
215
+ }
216
+ catch {
217
+ return false;
218
+ }
219
+ }
220
+ export function restoreCreativeResourceUrls(messages, streamed) {
221
+ const signedUrls = new Map();
222
+ for (const message of streamed) {
223
+ const messageId = nonEmptyString(message.message_id);
224
+ if (messageId === undefined)
225
+ continue;
226
+ const content = contentOf(message);
227
+ if (content?.type !== "function_response")
228
+ continue;
229
+ for (const part of functionResponseParts(content.text)) {
230
+ if (!("url" in part) || !hasAuthKey(part.url))
231
+ continue;
232
+ const identity = resourceUrlIdentity(part.url);
233
+ if (identity === undefined)
234
+ continue;
235
+ let messageUrls = signedUrls.get(messageId);
236
+ if (messageUrls === undefined) {
237
+ messageUrls = new Map();
238
+ signedUrls.set(messageId, messageUrls);
239
+ }
240
+ messageUrls.set(identity, part.url);
241
+ }
242
+ }
243
+ const restored = messages.map((message) => {
244
+ const content = contentOf(message);
245
+ const messageId = nonEmptyString(message.message_id);
246
+ if (content?.type !== "function_response" || messageId === undefined)
247
+ return message;
248
+ const messageUrls = signedUrls.get(messageId);
249
+ if (messageUrls === undefined)
250
+ return message;
251
+ const parsed = parseJson(content.text);
252
+ const values = Array.isArray(parsed) ? parsed : parsed === undefined ? [] : [parsed];
253
+ let changed = false;
254
+ const nextValues = values.map((value) => {
255
+ const item = record(value);
256
+ if (item === undefined)
257
+ return value;
258
+ const uri = nonEmptyString(item.uri);
259
+ const fallbackUrl = nonEmptyString(item.url);
260
+ let field;
261
+ let url;
262
+ if (uri !== undefined) {
263
+ field = "uri";
264
+ url = uri;
265
+ }
266
+ else if (fallbackUrl !== undefined) {
267
+ field = "url";
268
+ url = fallbackUrl;
269
+ }
270
+ else {
271
+ return value;
272
+ }
273
+ if (hasAuthKey(url))
274
+ return value;
275
+ const identity = resourceUrlIdentity(url);
276
+ const signedUrl = identity === undefined ? undefined : messageUrls.get(identity);
277
+ if (signedUrl === undefined)
278
+ return value;
279
+ changed = true;
280
+ return { ...item, [field]: signedUrl };
281
+ });
282
+ if (!changed)
283
+ return message;
284
+ const nextValue = Array.isArray(parsed) ? nextValues : nextValues[0];
285
+ return {
286
+ ...message,
287
+ content: {
288
+ ...content,
289
+ text: typeof content.text === "string" ? JSON.stringify(nextValue) : nextValue
290
+ }
291
+ };
292
+ });
293
+ for (const message of restored) {
294
+ const content = contentOf(message);
295
+ if (content?.type !== "function_response")
296
+ continue;
297
+ if (functionResponseParts(content.text).some((part) => ("url" in part && requiresAuthKey(part.url) && !hasAuthKey(part.url))))
298
+ return fail();
299
+ }
300
+ return restored;
301
+ }
193
302
  function parseQuestionPart(message) {
194
303
  const payload = functionPayload(message);
195
304
  if (payload?.name !== "ask_user_question")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gaoding-cli",
3
- "version": "1.0.0-alpha.17",
3
+ "version": "1.0.0-alpha.19",
4
4
  "description": "Gaoding command-line interface for agents and people.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -7,7 +7,7 @@ description: Use when creating images, videos or text with GD CLI, operating the
7
7
 
8
8
  按用户目标只读取对应参考:
9
9
 
10
- - 选择创作路由、发现 Model 并生成图片、视频或文本:[创作](references/creation.md)
10
+ - 按业务意图选择 Agent / Tool 创作路由、发现 Model 并生成图片、视频或文本:[创作](references/creation.md)
11
11
  - 操作当前 AI+ Editor 作品:[编辑器](references/editor.md)
12
12
  - 查询、上传或删除 DAM 素材:[素材管理](references/dam.md)
13
13
  - 登录或选择组织:[登录与组织](references/auth-org.md)
@@ -4,9 +4,28 @@
4
4
 
5
5
  先判断 Creation Route,再选择或校验 Model。Model 和参数不决定 Agent 或 Tool 路由;两条路由都支持指定 Model 及其参数,`model list` 和 `model get` 是共享的实时发现能力。
6
6
 
7
- - Agent:用户要的是业务交付物,或过程需要澄清、规划、多步骤、多产物或继续已有创作。具体 Business Skill 由服务端 Agent 选择,CLI 不猜测或复制服务端匹配规则。
7
+ - Agent:用户要的是业务交付物,或过程需要澄清、规划、多步骤、多产物或继续已有创作。下列十类业务意图通常优先走 Agent;它们只帮助选择 Creation Route,具体 Business Skill 由服务端 Agent 选择。
8
8
  - Tool:任务是一个无需业务编排的原子单次图片、视频或文本调用,或用户明确要求直接调用 Tool。
9
9
 
10
+ 业务意图参考(语义匹配,不是只能逐字命中):
11
+
12
+ | 业务意图 | 常见说法 | 素材与边界 |
13
+ | --- | --- | --- |
14
+ | 电商主图套图 | 主图、主图套图、一套主图、主图设计、商品图 | 通常带商品图;区分场景图、海报、详情页、改图、包装设计和多角度图 |
15
+ | 电商详情页 | 详情页、电商详情、宝贝详情、产品详情、长图、卖点页、参数页、功能介绍、对比说明、购买理由 | 通常带商品图;出现“详情”类词时优先按电商详情页判断,不按主图套图处理 |
16
+ | 商品精修 | 精修、修一下、翻新、白底图、纯白背景、修复使用痕迹 | 必须提供商品实物图;加文字、去水印、抠图、局部编辑、海报、套版和仿图不按商品精修判断 |
17
+ | 电商模特 | 模特、试穿、上身、穿戴、佩戴、换模特、换姿势、换背景 | 商品图可选;没有商品图时可以生成纯模特 |
18
+ | 商品场景图 | 生成场景、换场景、换个背景、整合到一张图+背景 | 必须提供商品实物图;人像、风景和设计稿不算商品图,带卖点文字的主图不按商品场景图判断 |
19
+ | 主题照 | 主题照、写真、风格人像 | 人像图可选 |
20
+ | 形象照 | 形象照、职业照 | 人像图可选 |
21
+ | 证件照 | 证件照、报名照、头像照,包括换底色和改尺寸 | 人像图可选 |
22
+ | 电商图片提示词 | 写电商图片提示词、产品主图提示词 | 服务端按当前组织的 Business Skill 可用性处理 |
23
+ | 电商视频提示词 | 电商视频提示词、带货视频、产品宣传视频 | 服务端按当前组织的 Business Skill 可用性处理 |
24
+
25
+ 这些参考不在 CLI 选择具体 Business Skill,也不代替服务端实时可用性。业务意图成立但缺少必需素材时,仍使用 Agent 并请用户提供素材;不要因此降级为 Tool。指定 Model、分辨率、比例、时长等参数也不改变路由。
26
+
27
+ 状态映射:原“商品白底图”Business Skill 已下线,白底图或纯白背景需求按商品精修意图走 Agent;原“小红书人像封面”Business Skill 已下线,相关种草人像封面需求走 Agent 兜底。状态只解释路由,不表示 CLI 指定了服务端 Skill。
28
+
10
29
  典型判断:
11
30
 
12
31
  - “指定 Model 的电商主图套图”仍使用 Agent,因为目标是业务交付物且可能需要多产物编排。