@tencentcloudadp/cli 0.1.4 → 0.1.5

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/dist/cli.js CHANGED
@@ -153,12 +153,12 @@ var CommandRegistry = class {
153
153
  };
154
154
  }
155
155
  schemaHint(tokens) {
156
- const path14 = [];
156
+ const path18 = [];
157
157
  for (const token of tokens) {
158
- if (!this.knownTokensAt(path14).has(token)) break;
159
- path14.push(token);
158
+ if (!this.knownTokensAt(path18).has(token)) break;
159
+ path18.push(token);
160
160
  }
161
- return path14.length > 0 ? `Run: adp schema --command "${path14.join(" ")}" --format json` : "Run: adp schema --compact --format json";
161
+ return path18.length > 0 ? `Run: adp schema --command "${path18.join(" ")}" --format json` : "Run: adp schema --compact --format json";
162
162
  }
163
163
  /**
164
164
  * Resolves argv's command tokens against the real command tree, returning a
@@ -191,26 +191,26 @@ var CommandRegistry = class {
191
191
  return void 0;
192
192
  }
193
193
  /** True when `path` is a group, i.e. something must follow it. */
194
- isGroupPath(path14) {
195
- const prefix = path14.join(" ");
194
+ isGroupPath(path18) {
195
+ const prefix = path18.join(" ");
196
196
  return this.commands.some((definition) => {
197
197
  const parts = literalTokens(definition.command);
198
- return parts.length > path14.length && parts.slice(0, path14.length).join(" ") === prefix;
198
+ return parts.length > path18.length && parts.slice(0, path18.length).join(" ") === prefix;
199
199
  });
200
200
  }
201
201
  /** Every valid next token at a command path: leaf names and group names. */
202
- knownTokensAt(path14) {
203
- const prefix = path14.join(" ");
202
+ knownTokensAt(path18) {
203
+ const prefix = path18.join(" ");
204
204
  const tokens = /* @__PURE__ */ new Set();
205
205
  for (const definition of this.commands) {
206
206
  const parts = literalTokens(definition.command);
207
- if (parts.length <= path14.length) {
207
+ if (parts.length <= path18.length) {
208
208
  continue;
209
209
  }
210
- if (parts.slice(0, path14.length).join(" ") !== prefix) {
210
+ if (parts.slice(0, path18.length).join(" ") !== prefix) {
211
211
  continue;
212
212
  }
213
- const next = parts[path14.length];
213
+ const next = parts[path18.length];
214
214
  if (next !== void 0) {
215
215
  tokens.add(next);
216
216
  }
@@ -583,7 +583,7 @@ function fitsRegisteredCommand(command, definitions) {
583
583
  }
584
584
 
585
585
  // src/infrastructure/logging/logger.ts
586
- var SECRET_RE = /secret[_-]?id|secret[_-]?key|secret[_-]?token|tmp[_-]?secret|app[_-]?key|x-cos-security-token|TENCENTCLOUD_/gi;
586
+ var SECRET_RE = /secret[_-]?id|secret[_-]?key|secret[_-]?token|tmp[_-]?secret|app[_-]?key|tool[_-]?key|x-cos-security-token|TENCENTCLOUD_/gi;
587
587
  var SECRET_ID_RE = /\bAKID[0-9A-Za-z]+\b/g;
588
588
  function redact(value) {
589
589
  return value.replace(SECRET_ID_RE, "[redacted-secret-id]").replace(SECRET_RE, "[redacted]");
@@ -1145,24 +1145,11 @@ function assertNumericId(value, name, listCommand) {
1145
1145
  }
1146
1146
  return trimmed;
1147
1147
  }
1148
- function canonicalizeLocalPath(input, cwd) {
1148
+ function assertLocalPath(input) {
1149
1149
  if (!input || hasControlChars(input)) {
1150
1150
  throw usageError("Invalid local path", "Pass a filesystem path without control characters");
1151
1151
  }
1152
- const { resolve, normalize } = pathFromNode();
1153
- return normalize(resolve(cwd, input));
1154
- }
1155
- function pathFromNode() {
1156
- const posixJoin = (left, right) => {
1157
- if (right.startsWith("/") || /^[A-Za-z]:[\\/]/.test(right)) {
1158
- return right;
1159
- }
1160
- return `${left.replace(/[/\\]+$/, "")}/${right.replace(/^[/\\]+/, "")}`;
1161
- };
1162
- return {
1163
- resolve: (...parts) => parts.reduce((acc, part) => posixJoin(acc, part)),
1164
- normalize: (p) => p.replace(/\\/g, "/").replace(/\/{2,}/g, "/")
1165
- };
1152
+ return input;
1166
1153
  }
1167
1154
 
1168
1155
  // src/interfaces/cli/core/id-flags.ts
@@ -1890,13 +1877,15 @@ ${input.error ? `<p class="err">${escapeHtml(input.error)}</p>` : ""}
1890
1877
 
1891
1878
  // src/infrastructure/child-process/detached-login.ts
1892
1879
  import { spawn } from "child_process";
1880
+ import { win32 } from "path";
1893
1881
  var DETACHED_LOGIN_ENV = "ADP_WEB_LOGIN_IPC";
1894
1882
  async function launchDetachedLogin(input) {
1895
- const entry = process.argv[1];
1883
+ const entry = input.entry ?? process.argv[1];
1896
1884
  if (!entry) {
1897
1885
  throw new DomainError("Cannot locate the adp entry script", "upstream", "DetachFailed");
1898
1886
  }
1899
- const child = spawn(process.execPath, [...process.execArgv, entry, ...input.args], {
1887
+ const child = (input.spawnImpl ?? spawn)(input.execPath ?? process.execPath, [...input.execArgv ?? process.execArgv, entry, ...input.args], {
1888
+ shell: false,
1900
1889
  detached: true,
1901
1890
  stdio: ["ignore", "ignore", "ignore", "ipc"],
1902
1891
  env: { ...input.env, [DETACHED_LOGIN_ENV]: "1" },
@@ -1909,9 +1898,10 @@ async function launchDetachedLogin(input) {
1909
1898
  }, input.handshakeTimeoutMs ?? 15e3);
1910
1899
  child.once("message", (message) => {
1911
1900
  clearTimeout(timer);
1912
- const parsed = message;
1913
- if (parsed.type !== "auth_url" || !parsed.url || !parsed.expiresAt) {
1914
- reject(new DomainError(parsed.error ?? "The login server failed to start", "upstream", "DetachFailed"));
1901
+ const parsed = message && typeof message === "object" ? message : {};
1902
+ if (parsed.type !== "auth_url" || typeof parsed.url !== "string" || typeof parsed.expiresAt !== "string" || !parsed.url || !parsed.expiresAt) {
1903
+ child.kill();
1904
+ reject(new DomainError(typeof parsed.error === "string" ? parsed.error : "The login server failed to start", "upstream", "DetachFailed"));
1915
1905
  return;
1916
1906
  }
1917
1907
  child.disconnect();
@@ -1928,10 +1918,27 @@ async function launchDetachedLogin(input) {
1928
1918
  });
1929
1919
  });
1930
1920
  }
1931
- function openInBrowser(url) {
1932
- const [command, args] = process.platform === "darwin" ? ["open", [url]] : process.platform === "win32" ? ["cmd", ["/c", "start", "", url]] : ["xdg-open", [url]];
1921
+ function openInBrowser(url, options = {}) {
1922
+ let target;
1923
+ try {
1924
+ target = new URL(url);
1925
+ } catch {
1926
+ throw new DomainError("Browser URL must be an absolute HTTP(S) URL", "usage", "InvalidBrowserUrl");
1927
+ }
1928
+ if (!["http:", "https:"].includes(target.protocol) || [...url].some((char) => char.charCodeAt(0) < 32)) {
1929
+ throw new DomainError("Browser URL must use HTTP(S) without control characters", "usage", "InvalidBrowserUrl");
1930
+ }
1931
+ const platform = options.platform ?? process.platform;
1932
+ const env = options.env ?? process.env;
1933
+ const systemRoot = Object.entries(env).find(([key]) => key.toLowerCase() === "systemroot")?.[1] ?? "C:\\Windows";
1934
+ const [command, args] = platform === "darwin" ? ["open", [target.href]] : platform === "win32" ? [win32.join(systemRoot, "System32", "rundll32.exe"), [`${win32.join(systemRoot, "System32", "url.dll")},FileProtocolHandler`, target.href]] : ["xdg-open", [target.href]];
1933
1935
  try {
1934
- const child = spawn(command, args, { detached: true, stdio: "ignore", windowsHide: true });
1936
+ const child = (options.spawnImpl ?? spawn)(command, args, {
1937
+ detached: true,
1938
+ stdio: "ignore",
1939
+ windowsHide: true,
1940
+ shell: false
1941
+ });
1935
1942
  child.on("error", () => void 0);
1936
1943
  child.unref();
1937
1944
  } catch {
@@ -2007,27 +2014,51 @@ async function resolveIdentity(deps, input) {
2007
2014
  }
2008
2015
 
2009
2016
  // src/infrastructure/fs/profile-file-store.ts
2010
- import { mkdir, readFile, readdir, rm, writeFile, chmod } from "fs/promises";
2011
- import { homedir } from "os";
2017
+ import { readFile, readdir, rm } from "fs/promises";
2018
+
2019
+ // src/infrastructure/fs/private-file.ts
2020
+ import { chmod, mkdir, writeFile } from "fs/promises";
2012
2021
  import path from "path";
2022
+ async function writePrivateFile(filePath, contents, options = {}) {
2023
+ const fs = { mkdir, writeFile, chmod, ...options.fs };
2024
+ const posix = (options.platform ?? process.platform) !== "win32";
2025
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
2026
+ if (posix) {
2027
+ try {
2028
+ await fs.chmod(filePath, 384);
2029
+ } catch (error) {
2030
+ if (error.code !== "ENOENT") {
2031
+ throw error;
2032
+ }
2033
+ }
2034
+ }
2035
+ await fs.writeFile(filePath, contents, { encoding: "utf8", ...posix ? { mode: 384 } : {} });
2036
+ if (posix) {
2037
+ await fs.chmod(filePath, 384);
2038
+ }
2039
+ }
2040
+
2041
+ // src/infrastructure/fs/profile-file-store.ts
2042
+ import { homedir } from "os";
2043
+ import path2 from "path";
2013
2044
  function adpProfilesDir(home = homedir()) {
2014
- return path.join(home, ".adp", "profiles");
2045
+ return path2.join(home, ".adp", "profiles");
2015
2046
  }
2016
2047
  function adpActiveProfilePath(home = homedir()) {
2017
- return path.join(home, ".adp", "active");
2048
+ return path2.join(home, ".adp", "active");
2018
2049
  }
2019
2050
  function profileFileName(name) {
2020
- if (!name || name !== path.basename(name) || name.startsWith(".")) {
2051
+ if (!name || name.startsWith(".") || /[<>:"/\\|?*]/u.test(name) || [...name].some((character) => character.charCodeAt(0) < 32) || /[. ]$/u.test(name) || /^(?:con|prn|aux|nul|conin\$|conout\$|com[1-9¹²³]|lpt[1-9¹²³]) *(?:\.|$)/iu.test(name)) {
2021
2052
  throw new Error(`Invalid profile name: ${name}`);
2022
2053
  }
2023
2054
  return `${name}.json`;
2024
2055
  }
2025
- function createProfileStore(dir = adpProfilesDir(), activePath = adpActiveProfilePath()) {
2056
+ function createProfileStore(dir = adpProfilesDir(), activePath = adpActiveProfilePath(), privateFileOptions = {}) {
2026
2057
  return {
2027
2058
  async load(name) {
2028
2059
  let text13;
2029
2060
  try {
2030
- text13 = await readFile(path.join(dir, profileFileName(name)), "utf8");
2061
+ text13 = await readFile(path2.join(dir, profileFileName(name)), "utf8");
2031
2062
  } catch (error) {
2032
2063
  if (error.code === "ENOENT") {
2033
2064
  return void 0;
@@ -2037,17 +2068,12 @@ function createProfileStore(dir = adpProfilesDir(), activePath = adpActiveProfil
2037
2068
  return parseProfile(name, text13);
2038
2069
  },
2039
2070
  async save(profile) {
2040
- await mkdir(dir, { recursive: true });
2041
- const target = path.join(dir, profileFileName(profile.name));
2042
- await writeFile(target, `${JSON.stringify(profile, null, 2)}
2043
- `, {
2044
- encoding: "utf8",
2045
- mode: 384
2046
- });
2047
- await chmod(target, 384);
2071
+ const target = path2.join(dir, profileFileName(profile.name));
2072
+ await writePrivateFile(target, `${JSON.stringify(profile, null, 2)}
2073
+ `, privateFileOptions);
2048
2074
  },
2049
2075
  async remove(name) {
2050
- await rm(path.join(dir, profileFileName(name)), { force: true });
2076
+ await rm(path2.join(dir, profileFileName(name)), { force: true });
2051
2077
  },
2052
2078
  async list() {
2053
2079
  let entries;
@@ -2082,15 +2108,13 @@ function createProfileStore(dir = adpProfilesDir(), activePath = adpActiveProfil
2082
2108
  }
2083
2109
  throw error;
2084
2110
  }
2085
- const name = text13.trim();
2086
- return name.length > 0 ? name : void 0;
2111
+ const name = text13.replace(/[\r\n]+$/u, "");
2112
+ return name.trim().length > 0 ? name : void 0;
2087
2113
  },
2088
2114
  async setActive(name) {
2089
2115
  profileFileName(name);
2090
- await mkdir(path.dirname(activePath), { recursive: true });
2091
- await writeFile(activePath, `${name}
2092
- `, { encoding: "utf8", mode: 384 });
2093
- await chmod(activePath, 384);
2116
+ await writePrivateFile(activePath, `${name}
2117
+ `, privateFileOptions);
2094
2118
  }
2095
2119
  };
2096
2120
  }
@@ -2401,7 +2425,7 @@ function mapSdkError(error) {
2401
2425
  if (/SecretIdNotFound/i.test(blob)) {
2402
2426
  return new DomainError(message, "auth", code, {
2403
2427
  ...options,
2404
- hint: "This SecretId is not a Tencent Cloud CAM key. Doc import (SaveDoc / RetryDocParse) and workflow canvas commands authenticate as LKE 2023-11-30. Independent-site OneID keys only work if the ADP endpoint proxies that LKE version; otherwise use a cloud profile (`adp login --site cloud`).",
2428
+ hint: "This SecretId was not found on the host that received it. SaveDoc, RetryDocParse, and workflow canvas use the same LKE 2023-11-30 actions on every site: public cloud calls lke.tencentcloudapi.com, and an independent or custom profile sends them to its ADP host. Log in with the site that issued the key.",
2405
2429
  retryable: false
2406
2430
  });
2407
2431
  }
@@ -3096,11 +3120,11 @@ function mergePluginBindings(spec) {
3096
3120
  byId.set(tool.pluginId, { pluginId: tool.pluginId });
3097
3121
  }
3098
3122
  }
3099
- for (const pluginId2 of spec.pluginIds) {
3100
- byId.set(pluginId2, { pluginId: pluginId2 });
3123
+ for (const pluginId3 of spec.pluginIds) {
3124
+ byId.set(pluginId3, { pluginId: pluginId3 });
3101
3125
  }
3102
- for (const pluginId2 of spec.connectorIds ?? []) {
3103
- byId.set(pluginId2, { pluginId: pluginId2 });
3126
+ for (const pluginId3 of spec.connectorIds ?? []) {
3127
+ byId.set(pluginId3, { pluginId: pluginId3 });
3104
3128
  }
3105
3129
  for (const plugin of spec.plugins ?? []) {
3106
3130
  byId.set(plugin.pluginId, plugin);
@@ -3208,7 +3232,9 @@ function toSummary5(raw) {
3208
3232
  return {
3209
3233
  agentId: String(raw.AgentId ?? raw.agentId ?? ""),
3210
3234
  name: optionalString3(raw.Name ?? raw.name ?? profile.Name) ?? "",
3211
- description: optionalString3(raw.Description ?? raw.description ?? profile.Description)
3235
+ description: optionalString3(raw.Description ?? raw.description ?? profile.Description),
3236
+ role: roleOf(raw.Role ?? profile.Role),
3237
+ parentAgentId: optionalString3(raw.ParentAgentId ?? profile.ParentAgentId)
3212
3238
  };
3213
3239
  }
3214
3240
  function toDetail3(response, fallbackId) {
@@ -3255,9 +3281,14 @@ function toDetail3(response, fallbackId) {
3255
3281
  agentId: String(response.AgentId ?? agent.AgentId ?? fallbackId),
3256
3282
  name: optionalString3(profile.Name),
3257
3283
  description: optionalString3(profile.Description),
3284
+ role: roleOf(profile.Role),
3285
+ parentAgentId: optionalString3(profile.ParentAgentId),
3258
3286
  spec
3259
3287
  };
3260
3288
  }
3289
+ function roleOf(value) {
3290
+ return typeof value === "number" ? value : void 0;
3291
+ }
3261
3292
  function optionalString3(value) {
3262
3293
  return typeof value === "string" ? value : void 0;
3263
3294
  }
@@ -3463,14 +3494,14 @@ function isApiCreate(input) {
3463
3494
  return "spec" in input;
3464
3495
  }
3465
3496
  function createPluginGateway(client) {
3466
- async function describeRaw(pluginId2, spaceId) {
3497
+ async function describeRaw(pluginId3, spaceId) {
3467
3498
  const response = await callSdk(
3468
- () => client.request("DescribePlugin", compact({ PluginId: pluginId2, SpaceId: spaceId }))
3499
+ () => client.request("DescribePlugin", compact({ PluginId: pluginId3, SpaceId: spaceId }))
3469
3500
  );
3470
3501
  return asRecord2(response.Plugin ?? response);
3471
3502
  }
3472
- async function describePlugin2(pluginId2, spaceId) {
3473
- return toSummary7(await describeRaw(pluginId2, spaceId));
3503
+ async function describePlugin2(pluginId3, spaceId) {
3504
+ return toSummary7(await describeRaw(pluginId3, spaceId));
3474
3505
  }
3475
3506
  return {
3476
3507
  async list(input) {
@@ -4540,6 +4571,376 @@ function createPluginAccessGateway(client) {
4540
4571
  };
4541
4572
  }
4542
4573
 
4574
+ // src/infrastructure/tencent/tool-key-gateway.ts
4575
+ function asText(value) {
4576
+ if (typeof value === "string" && value) {
4577
+ return value;
4578
+ }
4579
+ if (typeof value === "number" && Number.isFinite(value)) {
4580
+ return String(value);
4581
+ }
4582
+ return void 0;
4583
+ }
4584
+ function toRecord(raw) {
4585
+ const record = asRecord2(raw);
4586
+ const apiKeyId = asText(record.ApiKeyId);
4587
+ if (!apiKeyId) {
4588
+ return void 0;
4589
+ }
4590
+ const status = Number(record.Status);
4591
+ return {
4592
+ apiKeyId,
4593
+ apiKey: asText(record.ApiKey) ?? "",
4594
+ description: asText(record.Description) ?? "",
4595
+ ...Number.isFinite(status) ? { status } : {},
4596
+ ...asText(record.CreateTime) ? { createTime: asText(record.CreateTime) } : {},
4597
+ ...asText(record.LastAccessTime) ? { lastAccessTime: asText(record.LastAccessTime) } : {}
4598
+ };
4599
+ }
4600
+ function createToolKeyGateway(client) {
4601
+ return {
4602
+ async create() {
4603
+ const response = await callSdk(() => client.request("CreateApiKey", {}));
4604
+ return { apiKey: String(response.ApiKey ?? ""), apiKeyId: String(response.ApiKeyId ?? "") };
4605
+ },
4606
+ async list() {
4607
+ const response = await callSdk(() => client.request("DescribeApiKeyList", {}));
4608
+ const items = Array.isArray(response.ApiKeyList) ? response.ApiKeyList : [];
4609
+ return items.map(toRecord).filter((item) => item !== void 0);
4610
+ },
4611
+ async update(input) {
4612
+ await callSdk(
4613
+ () => client.request("ModifyApiKey", { ApiKeyId: input.apiKeyId, Description: input.description })
4614
+ );
4615
+ },
4616
+ async setStatus(input) {
4617
+ await callSdk(
4618
+ () => client.request("ModifyApiKeyStatus", { ApiKeyId: input.apiKeyId, Status: input.status })
4619
+ );
4620
+ },
4621
+ async delete(input) {
4622
+ await callSdk(() => client.request("DeleteApiKey", { ApiKeyId: input.apiKeyId }));
4623
+ }
4624
+ };
4625
+ }
4626
+
4627
+ // src/domain/plugin/mcp-url.ts
4628
+ var PLUGIN_MCP_URL_PREFIX = "https://adp.cloud.tencent.com/";
4629
+ var PLUGIN_ID = /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/;
4630
+ function pluginMcpUrl(pluginId3) {
4631
+ if (!PLUGIN_ID.test(pluginId3)) {
4632
+ throw usageError(
4633
+ "Invalid plugin-id for an MCP URL",
4634
+ "Pass the plugin id from adp plugin list. It must be one path segment, without slashes."
4635
+ );
4636
+ }
4637
+ return `${PLUGIN_MCP_URL_PREFIX}plugin/mcp/${pluginId3}/mcp`;
4638
+ }
4639
+ function assertPluginMcpUrl(url) {
4640
+ let parsed;
4641
+ try {
4642
+ parsed = new URL(url);
4643
+ } catch {
4644
+ throw mcpUrlRejected();
4645
+ }
4646
+ const allowed = new URL(PLUGIN_MCP_URL_PREFIX);
4647
+ if (parsed.origin !== allowed.origin || parsed.username || parsed.password || parsed.search || parsed.hash) {
4648
+ throw mcpUrlRejected();
4649
+ }
4650
+ if (!/^\/plugin\/mcp\/[A-Za-z0-9][A-Za-z0-9_-]{0,127}\/mcp$/.test(parsed.pathname)) {
4651
+ throw mcpUrlRejected();
4652
+ }
4653
+ }
4654
+ function mcpUrlRejected() {
4655
+ return new DomainError(
4656
+ "Refusing to send a tool key to a URL outside the plugin MCP allowlist",
4657
+ "usage",
4658
+ "McpUrlNotAllowed",
4659
+ {
4660
+ hint: "Calls only go to https://adp.cloud.tencent.com/plugin/mcp/<pluginId>/mcp",
4661
+ retryable: false
4662
+ }
4663
+ );
4664
+ }
4665
+
4666
+ // src/infrastructure/mcp/plugin-mcp-client.ts
4667
+ var PROTOCOL_VERSION = "2024-11-05";
4668
+ var CLIENT_INFO = { name: "adp-cli", version: "0.0.0" };
4669
+ var ERROR_BODY_PREVIEW = 400;
4670
+ function createPluginMcpGateway(options = {}) {
4671
+ const fetchImpl = options.fetchImpl ?? fetch;
4672
+ async function open(input) {
4673
+ const url = pluginMcpUrl(input.pluginId);
4674
+ assertPluginMcpUrl(url);
4675
+ const session = createSession({ url, apiKey: input.apiKey, fetchImpl, timeoutMs: input.timeoutMs });
4676
+ await session.initialize();
4677
+ return session;
4678
+ }
4679
+ return {
4680
+ async listTools(input) {
4681
+ const session = await open(input);
4682
+ return readTools(await session.sendRequest("tools/list", {}));
4683
+ },
4684
+ async callTool(input) {
4685
+ const session = await open(input);
4686
+ return unwrapToolPayload(
4687
+ await session.sendRequest("tools/call", { name: input.toolName, arguments: input.arguments }),
4688
+ input.apiKey
4689
+ );
4690
+ }
4691
+ };
4692
+ }
4693
+ function createSession(options) {
4694
+ let sessionId = "";
4695
+ let nextId = 1;
4696
+ async function post(payload) {
4697
+ assertPluginMcpUrl(options.url);
4698
+ const headers = {
4699
+ "Content-Type": "application/json",
4700
+ Accept: "application/json, text/event-stream",
4701
+ Authorization: `Bearer ${options.apiKey}`
4702
+ };
4703
+ if (sessionId) {
4704
+ headers["Mcp-Session-Id"] = sessionId;
4705
+ }
4706
+ const controller = new AbortController();
4707
+ const timer = setTimeout(() => controller.abort(), options.timeoutMs);
4708
+ let response;
4709
+ try {
4710
+ response = await options.fetchImpl(options.url, {
4711
+ method: "POST",
4712
+ headers,
4713
+ body: JSON.stringify(payload),
4714
+ signal: controller.signal,
4715
+ redirect: "error"
4716
+ });
4717
+ } catch (cause) {
4718
+ const aborted = cause instanceof Error && cause.name === "AbortError";
4719
+ throw new DomainError(
4720
+ aborted ? `MCP request timed out after ${options.timeoutMs}ms` : "MCP request failed",
4721
+ "upstream",
4722
+ aborted ? "Timeout" : "McpTransport",
4723
+ { retryable: true, cause }
4724
+ );
4725
+ } finally {
4726
+ clearTimeout(timer);
4727
+ }
4728
+ const seen = response.headers.get("Mcp-Session-Id");
4729
+ if (seen) {
4730
+ sessionId = seen;
4731
+ }
4732
+ const body = await response.text();
4733
+ if (response.status === 401 || response.status === 403) {
4734
+ throw new DomainError(`MCP plugin rejected the tool key (HTTP ${response.status})`, "auth", "ToolKeyRejected", {
4735
+ hint: "The key in ADP_API_KEY, --api-key-env, or ~/.adp/tool-key was rejected. Create a new one with adp plugin key create.",
4736
+ retryable: false,
4737
+ nextActions: [
4738
+ { command: "adp plugin key create --format json", reason: "issue a new tool key and save it" }
4739
+ ]
4740
+ });
4741
+ }
4742
+ if (response.status >= 400) {
4743
+ throw new DomainError(
4744
+ `MCP request failed (HTTP ${response.status}): ${scrub(body, options.apiKey) || "(empty)"}`,
4745
+ "upstream",
4746
+ "McpTransport",
4747
+ { retryable: response.status >= 500 }
4748
+ );
4749
+ }
4750
+ return { status: response.status, body };
4751
+ }
4752
+ function unwrap(raw) {
4753
+ if (!raw || typeof raw !== "object") {
4754
+ return raw;
4755
+ }
4756
+ const frame = raw;
4757
+ if (frame.error) {
4758
+ throw new DomainError(
4759
+ `MCP error: ${scrub(JSON.stringify(frame.error), options.apiKey)}`,
4760
+ "upstream",
4761
+ "McpServerError",
4762
+ { retryable: false }
4763
+ );
4764
+ }
4765
+ return frame.result ?? raw;
4766
+ }
4767
+ async function sendRequest(method, params) {
4768
+ const id = nextId;
4769
+ nextId += 1;
4770
+ const { body } = await post({ jsonrpc: "2.0", id, method, params });
4771
+ if (!body.trim()) {
4772
+ throw new DomainError("MCP response was empty", "upstream", "McpProtocol", { retryable: true });
4773
+ }
4774
+ return unwrap(parseMcpBody(body));
4775
+ }
4776
+ return {
4777
+ async initialize() {
4778
+ await sendRequest("initialize", {
4779
+ protocolVersion: PROTOCOL_VERSION,
4780
+ capabilities: {},
4781
+ clientInfo: CLIENT_INFO
4782
+ });
4783
+ await post({ jsonrpc: "2.0", method: "notifications/initialized", params: {} });
4784
+ },
4785
+ sendRequest
4786
+ };
4787
+ }
4788
+ function parseMcpBody(body) {
4789
+ try {
4790
+ return JSON.parse(body);
4791
+ } catch {
4792
+ }
4793
+ let lastData = [];
4794
+ for (const chunk of body.split("\n\n")) {
4795
+ const dataLines = chunk.split(/\r?\n/).filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart());
4796
+ if (dataLines.length > 0) {
4797
+ lastData = dataLines;
4798
+ }
4799
+ }
4800
+ if (lastData.length === 0) {
4801
+ throw new DomainError("MCP response was neither JSON nor SSE", "upstream", "McpProtocol", { retryable: true });
4802
+ }
4803
+ try {
4804
+ return JSON.parse(lastData.join("\n").trim());
4805
+ } catch (cause) {
4806
+ throw new DomainError("MCP response frame is not valid JSON", "upstream", "McpProtocol", {
4807
+ retryable: true,
4808
+ cause
4809
+ });
4810
+ }
4811
+ }
4812
+ function unwrapToolPayload(result, secret = "") {
4813
+ if (!result || typeof result !== "object") {
4814
+ return result;
4815
+ }
4816
+ const payload = result;
4817
+ if (payload.isError) {
4818
+ const parts = [];
4819
+ if (Array.isArray(payload.content)) {
4820
+ for (const item of payload.content) {
4821
+ const text13 = item ?? {};
4822
+ if (text13.type === "text") {
4823
+ parts.push(String(text13.text ?? ""));
4824
+ }
4825
+ }
4826
+ }
4827
+ const message = scrub(parts.join("\n").trim(), secret);
4828
+ throw new DomainError(`MCP tool failed: ${message || "(no text)"}`, "upstream", "McpToolError", { retryable: false });
4829
+ }
4830
+ if (Array.isArray(payload.content)) {
4831
+ for (const item of payload.content) {
4832
+ const text13 = item ?? {};
4833
+ if (text13.type !== "text") {
4834
+ continue;
4835
+ }
4836
+ const raw = String(text13.text ?? "");
4837
+ try {
4838
+ return JSON.parse(raw);
4839
+ } catch {
4840
+ return raw;
4841
+ }
4842
+ }
4843
+ }
4844
+ return result;
4845
+ }
4846
+ function readTools(result) {
4847
+ const record = result && typeof result === "object" ? result : {};
4848
+ const tools = Array.isArray(record.tools) ? record.tools : [];
4849
+ return tools.flatMap((item) => {
4850
+ if (!item || typeof item !== "object") {
4851
+ return [];
4852
+ }
4853
+ const tool = item;
4854
+ if (typeof tool.name !== "string" || !tool.name) {
4855
+ return [];
4856
+ }
4857
+ const inputSchema = tool.inputSchema && typeof tool.inputSchema === "object" && !Array.isArray(tool.inputSchema) ? tool.inputSchema : void 0;
4858
+ return [
4859
+ {
4860
+ name: tool.name,
4861
+ ...typeof tool.description === "string" ? { description: tool.description } : {},
4862
+ ...inputSchema ? { inputSchema } : {}
4863
+ }
4864
+ ];
4865
+ });
4866
+ }
4867
+ function scrub(text13, secret) {
4868
+ let out = text13;
4869
+ const trimmed = secret.trim();
4870
+ if (trimmed.length >= 4) {
4871
+ out = out.split(trimmed).join("[redacted]");
4872
+ }
4873
+ return out.replace(/Bearer\s+\S+/gi, "Bearer [redacted]").slice(0, ERROR_BODY_PREVIEW);
4874
+ }
4875
+
4876
+ // src/infrastructure/fs/tool-key-file-store.ts
4877
+ import { readFile as readFile2, rm as rm2 } from "fs/promises";
4878
+ import { homedir as homedir2 } from "os";
4879
+ import path3 from "path";
4880
+
4881
+ // src/infrastructure/fs/ini-credentials.ts
4882
+ function parseIni(text13) {
4883
+ const result = {};
4884
+ for (const rawLine of text13.split(/\r?\n/)) {
4885
+ const line = rawLine.trim();
4886
+ if (!line || line.startsWith("#") || line.startsWith(";") || line.startsWith("[")) {
4887
+ continue;
4888
+ }
4889
+ const eq = line.indexOf("=");
4890
+ if (eq < 0) {
4891
+ continue;
4892
+ }
4893
+ const key = line.slice(0, eq).trim().toLowerCase();
4894
+ const value = line.slice(eq + 1).trim();
4895
+ if (key) {
4896
+ result[key] = value;
4897
+ }
4898
+ }
4899
+ return result;
4900
+ }
4901
+ function renderIni(section, entries) {
4902
+ const body = entries.filter((entry) => entry[1] !== void 0 && entry[1] !== "").map(([key, value]) => `${key} = ${value}`);
4903
+ return [`[${section}]`, ...body, ""].join("\n");
4904
+ }
4905
+
4906
+ // src/infrastructure/fs/tool-key-file-store.ts
4907
+ function adpToolKeyPath(home = homedir2()) {
4908
+ return path3.join(home, ".adp", "tool-key");
4909
+ }
4910
+ function createToolKeyFileStore(filePath, privateFileOptions = {}) {
4911
+ return {
4912
+ async load() {
4913
+ try {
4914
+ const parsed = parseIni(await readFile2(filePath, "utf8"));
4915
+ const value = (parsed.tool_key ?? parsed.api_key ?? "").trim();
4916
+ if (!value) {
4917
+ return void 0;
4918
+ }
4919
+ const apiKeyId = (parsed.api_key_id ?? "").trim();
4920
+ return { value, ...apiKeyId ? { apiKeyId } : {} };
4921
+ } catch (error) {
4922
+ if (error.code === "ENOENT") {
4923
+ return void 0;
4924
+ }
4925
+ throw error;
4926
+ }
4927
+ },
4928
+ async save(input) {
4929
+ await writePrivateFile(
4930
+ filePath,
4931
+ renderIni("default", [
4932
+ ["tool_key", input.value],
4933
+ ["api_key_id", input.apiKeyId]
4934
+ ]),
4935
+ privateFileOptions
4936
+ );
4937
+ },
4938
+ async clear() {
4939
+ await rm2(filePath, { force: true });
4940
+ }
4941
+ };
4942
+ }
4943
+
4543
4944
  // src/infrastructure/tencent/skill-gateway.ts
4544
4945
  var CREATE_TYPE_FILE_UPLOAD = 1;
4545
4946
  function createSkillGateway(client) {
@@ -5060,7 +5461,7 @@ var DEFAULT_TIMEOUT_MS = 18e4;
5060
5461
  function createSseChatGateway(client, options = {}) {
5061
5462
  const chatUrl = options.chatUrl ?? DEFAULT_ADP_CHAT_SSE_URL;
5062
5463
  const fetchImpl = options.fetchImpl ?? fetch;
5063
- const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
5464
+ const timeoutMs2 = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
5064
5465
  return {
5065
5466
  async createSession(input) {
5066
5467
  if (input.conversationId && !isValidConversationId(input.conversationId)) {
@@ -5094,13 +5495,13 @@ function createSseChatGateway(client, options = {}) {
5094
5495
  };
5095
5496
  },
5096
5497
  async sendMessage(session, input, onEvent) {
5097
- await postSseChat(fetchImpl, chatUrl, session, input, onEvent, timeoutMs);
5498
+ await postSseChat(fetchImpl, chatUrl, session, input, onEvent, timeoutMs2);
5098
5499
  },
5099
5500
  async close() {
5100
5501
  }
5101
5502
  };
5102
5503
  }
5103
- async function postSseChat(fetchImpl, chatUrl, session, input, onEvent, timeoutMs) {
5504
+ async function postSseChat(fetchImpl, chatUrl, session, input, onEvent, timeoutMs2) {
5104
5505
  const body = buildChatRequest({
5105
5506
  conversationId: session.conversationId,
5106
5507
  appKey: session.appKey,
@@ -5110,7 +5511,7 @@ async function postSseChat(fetchImpl, chatUrl, session, input, onEvent, timeoutM
5110
5511
  attachments: input.attachments
5111
5512
  });
5112
5513
  const controller = new AbortController();
5113
- const timer = setTimeout(() => controller.abort(), timeoutMs);
5514
+ const timer = setTimeout(() => controller.abort(), timeoutMs2);
5114
5515
  let response;
5115
5516
  try {
5116
5517
  response = await fetchImpl(chatUrl, {
@@ -5148,7 +5549,7 @@ async function postSseChat(fetchImpl, chatUrl, session, input, onEvent, timeoutM
5148
5549
  await readSseStream(stream, onEvent, seen);
5149
5550
  } catch (error) {
5150
5551
  if (isAbortError(error)) {
5151
- throw chatTimeoutError(timeoutMs, seen);
5552
+ throw chatTimeoutError(timeoutMs2, seen);
5152
5553
  }
5153
5554
  if (error instanceof DomainError) {
5154
5555
  throw error;
@@ -5162,8 +5563,8 @@ async function postSseChat(fetchImpl, chatUrl, session, input, onEvent, timeoutM
5162
5563
  }
5163
5564
  }
5164
5565
  var MISSING_TOOL = /(mcp__[\w.-]*?(\w+)) not found/;
5165
- function chatTimeoutError(timeoutMs, seen) {
5166
- const seconds2 = Math.round(timeoutMs / 1e3);
5566
+ function chatTimeoutError(timeoutMs2, seen) {
5567
+ const seconds2 = Math.round(timeoutMs2 / 1e3);
5167
5568
  if (seen.missingTool) {
5168
5569
  return new DomainError(
5169
5570
  `Chat timed out after ${seconds2}s; the agent's tool ${seen.missingTool} was not found by the runtime`,
@@ -5573,12 +5974,12 @@ function toTask2(raw) {
5573
5974
  }
5574
5975
  function toMcpApp(raw) {
5575
5976
  const mcp = {};
5576
- const pluginId2 = optionalString8(raw.PluginId);
5977
+ const pluginId3 = optionalString8(raw.PluginId);
5577
5978
  const resourceUri = optionalString8(raw.ResourceUri);
5578
5979
  const threadId = optionalString8(raw.ThreadId);
5579
5980
  const toolResult = optionalString8(raw.ToolResult);
5580
- if (pluginId2) {
5581
- mcp.pluginId = pluginId2;
5981
+ if (pluginId3) {
5982
+ mcp.pluginId = pluginId3;
5582
5983
  }
5583
5984
  if (resourceUri) {
5584
5985
  mcp.resourceUri = resourceUri;
@@ -6705,8 +7106,8 @@ function hmacSha1Hex(key, value) {
6705
7106
  }
6706
7107
 
6707
7108
  // src/infrastructure/fs/local-document-reader.ts
6708
- import { readFile as readFile2 } from "fs/promises";
6709
- import path2 from "path";
7109
+ import { readFile as readFile3 } from "fs/promises";
7110
+ import path4 from "path";
6710
7111
 
6711
7112
  // src/domain/knowledge/doc.ts
6712
7113
  var DOC_ENABLE_SCOPE_BOTH = 4;
@@ -6769,7 +7170,7 @@ function createFsLocalDocumentReader() {
6769
7170
  async read(absolutePath) {
6770
7171
  let bytes;
6771
7172
  try {
6772
- bytes = await readFile2(absolutePath);
7173
+ bytes = await readFile3(absolutePath);
6773
7174
  } catch (error) {
6774
7175
  const code = error.code;
6775
7176
  throw usageError(
@@ -6777,7 +7178,7 @@ function createFsLocalDocumentReader() {
6777
7178
  code === "ENOENT" ? "Check --file points at an existing document" : "Check file permissions"
6778
7179
  );
6779
7180
  }
6780
- const fileName = path2.basename(absolutePath);
7181
+ const fileName = path4.basename(absolutePath);
6781
7182
  return {
6782
7183
  fileName,
6783
7184
  fileType: fileTypeFromName(fileName),
@@ -6788,38 +7189,11 @@ function createFsLocalDocumentReader() {
6788
7189
  }
6789
7190
 
6790
7191
  // src/infrastructure/fs/credential-file-store.ts
6791
- import { homedir as homedir2 } from "os";
6792
- import { chmod as chmod2, mkdir as mkdir2, readFile as readFile3, rm as rm2, writeFile as writeFile2 } from "fs/promises";
6793
- import path3 from "path";
6794
-
6795
- // src/infrastructure/fs/ini-credentials.ts
6796
- function parseIni(text13) {
6797
- const result = {};
6798
- for (const rawLine of text13.split(/\r?\n/)) {
6799
- const line = rawLine.trim();
6800
- if (!line || line.startsWith("#") || line.startsWith(";") || line.startsWith("[")) {
6801
- continue;
6802
- }
6803
- const eq = line.indexOf("=");
6804
- if (eq < 0) {
6805
- continue;
6806
- }
6807
- const key = line.slice(0, eq).trim().toLowerCase();
6808
- const value = line.slice(eq + 1).trim();
6809
- if (key) {
6810
- result[key] = value;
6811
- }
6812
- }
6813
- return result;
6814
- }
6815
- function renderIni(section, entries) {
6816
- const body = entries.filter((entry) => entry[1] !== void 0 && entry[1] !== "").map(([key, value]) => `${key} = ${value}`);
6817
- return [`[${section}]`, ...body, ""].join("\n");
6818
- }
6819
-
6820
- // src/infrastructure/fs/credential-file-store.ts
6821
- function tencentCloudCredentialsPath(home = homedir2()) {
6822
- return path3.join(home, ".tencentcloud", "credentials");
7192
+ import { homedir as homedir3 } from "os";
7193
+ import { readFile as readFile4, rm as rm3 } from "fs/promises";
7194
+ import path5 from "path";
7195
+ function tencentCloudCredentialsPath(home = homedir3()) {
7196
+ return path5.join(home, ".tencentcloud", "credentials");
6823
7197
  }
6824
7198
  function createEnvCredentialReader(env = process.env) {
6825
7199
  return {
@@ -6833,11 +7207,11 @@ function createEnvCredentialReader(env = process.env) {
6833
7207
  }
6834
7208
  };
6835
7209
  }
6836
- function createCredentialFileStore(filePath, source2) {
7210
+ function createCredentialFileStore(filePath, source2, privateFileOptions = {}) {
6837
7211
  return {
6838
7212
  async load() {
6839
7213
  try {
6840
- const text13 = await readFile3(filePath, "utf8");
7214
+ const text13 = await readFile4(filePath, "utf8");
6841
7215
  const parsed = parseIni(text13);
6842
7216
  const secretId = parsed.secret_id ?? parsed.secretid;
6843
7217
  const secretKey = parsed.secret_key ?? parsed.secretkey;
@@ -6853,26 +7227,30 @@ function createCredentialFileStore(filePath, source2) {
6853
7227
  }
6854
7228
  },
6855
7229
  async save(input) {
6856
- await mkdir2(path3.dirname(filePath), { recursive: true });
6857
7230
  const body = renderIni("default", [
6858
7231
  ["secret_id", input.secretId],
6859
7232
  ["secret_key", input.secretKey],
6860
7233
  ["token", input.token]
6861
7234
  ]);
6862
- await writeFile2(filePath, body, { encoding: "utf8", mode: 384 });
6863
- await chmod2(filePath, 384);
7235
+ await writePrivateFile(filePath, body, privateFileOptions);
6864
7236
  },
6865
7237
  async clear() {
6866
- await rm2(filePath, { force: true });
7238
+ await rm3(filePath, { force: true });
6867
7239
  }
6868
7240
  };
6869
7241
  }
6870
7242
 
6871
7243
  // src/infrastructure/fs/jsonc-config-store.ts
6872
- import { mkdir as mkdir3, readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
6873
- import path4 from "path";
7244
+ import { mkdir as mkdir2, readFile as readFile5, writeFile as writeFile2 } from "fs/promises";
7245
+ import path7 from "path";
6874
7246
  import { parse as parseJsonc } from "jsonc-parser";
6875
7247
 
7248
+ // src/infrastructure/fs/local-path.ts
7249
+ import path6 from "path";
7250
+ function canonicalizeLocalPath(input, cwd, paths = path6) {
7251
+ return paths.resolve(cwd, assertLocalPath(input));
7252
+ }
7253
+
6876
7254
  // src/domain/project-config/env-overlay.ts
6877
7255
  function mergeEnvOverlay(base, overlay) {
6878
7256
  return {
@@ -7263,27 +7641,27 @@ function createJsoncConfigStore() {
7263
7641
  return {
7264
7642
  async load(options) {
7265
7643
  const cwd = options.cwd;
7266
- const configPath = path4.resolve(cwd, options.configPath ?? "adp.jsonc");
7644
+ const configPath = canonicalizeLocalPath(options.configPath ?? "adp.jsonc", cwd);
7267
7645
  const { config: base, warnings } = await readConfigFile(configPath);
7268
7646
  const extra = warnings.length > 0 ? { warnings } : {};
7269
7647
  if (!options.envName) {
7270
7648
  return { config: base, configPath, ...extra };
7271
7649
  }
7272
- const overlayPath = path4.resolve(cwd, `adp.env.${options.envName}.jsonc`);
7650
+ const overlayPath = canonicalizeLocalPath(`adp.env.${options.envName}.jsonc`, cwd);
7273
7651
  const overlay = await readOptionalConfig(overlayPath);
7274
7652
  return { config: overlay ? mergeEnvOverlay(base, overlay) : base, configPath, ...extra };
7275
7653
  },
7276
7654
  async write(configPath, config2) {
7277
- await mkdir3(path4.dirname(configPath), { recursive: true });
7655
+ await mkdir2(path7.dirname(configPath), { recursive: true });
7278
7656
  const body = `${JSON.stringify(adpConfigToFile(config2), null, 2)}
7279
7657
  `;
7280
- await writeFile3(configPath, body, "utf8");
7658
+ await writeFile2(configPath, body, "utf8");
7281
7659
  },
7282
7660
  async writeSidecar(cwd, relativePath, contents, options) {
7283
7661
  const resolved2 = canonicalizeLocalPath(relativePath, cwd);
7284
- await mkdir3(path4.dirname(resolved2), { recursive: true });
7662
+ await mkdir2(path7.dirname(resolved2), { recursive: true });
7285
7663
  try {
7286
- await writeFile3(resolved2, contents, { encoding: "utf8", flag: options?.overwrite === false ? "wx" : "w" });
7664
+ await writeFile2(resolved2, contents, { encoding: "utf8", flag: options?.overwrite === false ? "wx" : "w" });
7287
7665
  } catch (error) {
7288
7666
  if (error.code === "EEXIST") {
7289
7667
  return false;
@@ -7295,7 +7673,7 @@ function createJsoncConfigStore() {
7295
7673
  async readInstructions(cwd, relativePath) {
7296
7674
  const resolved2 = canonicalizeLocalPath(relativePath, cwd);
7297
7675
  try {
7298
- return await readFile4(resolved2, "utf8");
7676
+ return await readFile5(resolved2, "utf8");
7299
7677
  } catch {
7300
7678
  throw new DomainError(`instructions file not found: ${relativePath} (resolved to ${resolved2})`, "usage", "InstructionsFileMissing", {
7301
7679
  hint: `Create ${resolved2} with the agent's system prompt, or replace agent.instructions_file with inline agent.instructions in adp.jsonc`,
@@ -7322,7 +7700,7 @@ function unknownConfigFields(parsed) {
7322
7700
  async function readConfigFile(configPath) {
7323
7701
  let text13;
7324
7702
  try {
7325
- text13 = await readFile4(configPath, "utf8");
7703
+ text13 = await readFile5(configPath, "utf8");
7326
7704
  } catch {
7327
7705
  throw new DomainError(`Config not found: ${configPath}`, "usage", "UsageError", {
7328
7706
  hint: "Run: adp init or pass --config path/to/adp.jsonc",
@@ -7356,7 +7734,7 @@ async function readConfigFile(configPath) {
7356
7734
  async function readOptionalConfig(configPath) {
7357
7735
  let text13;
7358
7736
  try {
7359
- text13 = await readFile4(configPath, "utf8");
7737
+ text13 = await readFile5(configPath, "utf8");
7360
7738
  } catch (error) {
7361
7739
  if (error.code === "ENOENT") {
7362
7740
  return void 0;
@@ -7375,8 +7753,8 @@ async function readOptionalConfig(configPath) {
7375
7753
  }
7376
7754
 
7377
7755
  // src/infrastructure/fs/companion-skill-pack.ts
7378
- import { cp, readFile as readFile5, stat } from "fs/promises";
7379
- import path6 from "path";
7756
+ import { cp, readFile as readFile6, stat } from "fs/promises";
7757
+ import path9 from "path";
7380
7758
  import { existsSync as existsSync2 } from "fs";
7381
7759
 
7382
7760
  // src/domain/companion-skills/companion-skill.ts
@@ -7412,13 +7790,13 @@ function parseAgentKinds(value) {
7412
7790
 
7413
7791
  // src/infrastructure/fs/package-root.ts
7414
7792
  import { existsSync, readFileSync } from "fs";
7415
- import path5 from "path";
7793
+ import path8 from "path";
7416
7794
  import { fileURLToPath } from "url";
7417
7795
  var CLI_PACKAGE_NAME = "@tencentcloudadp/cli";
7418
- function resolvePackageRoot(startDir = path5.dirname(fileURLToPath(import.meta.url))) {
7796
+ function resolvePackageRoot(startDir = path8.dirname(fileURLToPath(import.meta.url))) {
7419
7797
  let dir = startDir;
7420
7798
  for (let i = 0; i < 10; i += 1) {
7421
- const pkgPath = path5.join(dir, "package.json");
7799
+ const pkgPath = path8.join(dir, "package.json");
7422
7800
  if (existsSync(pkgPath)) {
7423
7801
  try {
7424
7802
  const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
@@ -7428,7 +7806,7 @@ function resolvePackageRoot(startDir = path5.dirname(fileURLToPath(import.meta.u
7428
7806
  } catch {
7429
7807
  }
7430
7808
  }
7431
- const parent = path5.dirname(dir);
7809
+ const parent = path8.dirname(dir);
7432
7810
  if (parent === dir) {
7433
7811
  break;
7434
7812
  }
@@ -7439,9 +7817,9 @@ function resolvePackageRoot(startDir = path5.dirname(fileURLToPath(import.meta.u
7439
7817
  "Run adp from the installed npm package or the adp-cli repository"
7440
7818
  );
7441
7819
  }
7442
- function readCliPackageIdentity(startDir = path5.dirname(fileURLToPath(import.meta.url))) {
7820
+ function readCliPackageIdentity(startDir = path8.dirname(fileURLToPath(import.meta.url))) {
7443
7821
  const root = resolvePackageRoot(startDir);
7444
- const pkgPath = path5.join(root, "package.json");
7822
+ const pkgPath = path8.join(root, "package.json");
7445
7823
  const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
7446
7824
  if (typeof pkg.version !== "string" || pkg.version.trim().length === 0) {
7447
7825
  throw usageError(
@@ -7455,12 +7833,12 @@ function readCliPackageIdentity(startDir = path5.dirname(fileURLToPath(import.me
7455
7833
  // src/infrastructure/fs/companion-skill-pack.ts
7456
7834
  function createFsCompanionSkillPack(options = {}) {
7457
7835
  const packageRoot = options.packageRoot ?? resolvePackageRoot();
7458
- const skillsRoot = path6.join(packageRoot, "skills");
7836
+ const skillsRoot = path9.join(packageRoot, "skills");
7459
7837
  return {
7460
7838
  async listSkillIds() {
7461
7839
  const ids4 = [];
7462
7840
  for (const name of COMPANION_SKILL_IDS) {
7463
- if (existsSync2(path6.join(skillsRoot, name, "SKILL.md"))) {
7841
+ if (existsSync2(path9.join(skillsRoot, name, "SKILL.md"))) {
7464
7842
  ids4.push(name);
7465
7843
  }
7466
7844
  }
@@ -7473,18 +7851,18 @@ function createFsCompanionSkillPack(options = {}) {
7473
7851
  return ids4;
7474
7852
  },
7475
7853
  async skillSourceDir(skillId) {
7476
- const dir = path6.join(skillsRoot, skillId);
7477
- const skillFile = path6.join(dir, "SKILL.md");
7854
+ const dir = path9.join(skillsRoot, skillId);
7855
+ const skillFile = path9.join(dir, "SKILL.md");
7478
7856
  if (!existsSync2(skillFile)) {
7479
7857
  throw usageError(`Companion skill not found: ${skillId}`, `Looked for ${skillFile}`);
7480
7858
  }
7481
7859
  return dir;
7482
7860
  },
7483
7861
  async readDescription(sourceDir) {
7484
- const file = path6.join(sourceDir, "SKILL.md");
7862
+ const file = path9.join(sourceDir, "SKILL.md");
7485
7863
  let raw;
7486
7864
  try {
7487
- raw = await readFile5(file, "utf8");
7865
+ raw = await readFile6(file, "utf8");
7488
7866
  } catch {
7489
7867
  return "";
7490
7868
  }
@@ -7499,11 +7877,11 @@ function createFsCompanionSkillPack(options = {}) {
7499
7877
  }
7500
7878
  },
7501
7879
  joinPath(...parts) {
7502
- return path6.join(...parts);
7880
+ return path9.join(...parts);
7503
7881
  },
7504
7882
  async copySkill(sourceDir, destDir, dryRun) {
7505
- const skillId = path6.basename(sourceDir);
7506
- if (path6.resolve(sourceDir) === path6.resolve(destDir)) {
7883
+ const skillId = path9.basename(sourceDir);
7884
+ if (path9.resolve(sourceDir) === path9.resolve(destDir)) {
7507
7885
  return { skillId, destDir, copied: false };
7508
7886
  }
7509
7887
  if (dryRun) {
@@ -7534,12 +7912,12 @@ function parseFrontmatterDescription(raw) {
7534
7912
 
7535
7913
  // src/infrastructure/fs/config-schema-locator.ts
7536
7914
  import { existsSync as existsSync3 } from "fs";
7537
- import path7 from "path";
7915
+ import path10 from "path";
7538
7916
  function createFsConfigSchemaLocator(options = {}) {
7539
7917
  return {
7540
7918
  async resolvePath() {
7541
7919
  const root = options.packageRoot ?? resolvePackageRoot();
7542
- const schemaPath = path7.join(root, "config-schema.json");
7920
+ const schemaPath = path10.join(root, "config-schema.json");
7543
7921
  if (!existsSync3(schemaPath)) {
7544
7922
  throw usageError(
7545
7923
  `config-schema.json not found at ${schemaPath}`,
@@ -7559,7 +7937,7 @@ var NETWORK_HINT = `Check network access to ${NPM_REGISTRY_ORIGIN}`;
7559
7937
  function createNpmRegistryGateway(options = {}) {
7560
7938
  const origin = options.origin?.trim() || NPM_REGISTRY_ORIGIN;
7561
7939
  const fetchImpl = options.fetchImpl ?? fetch;
7562
- const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
7940
+ const timeoutMs2 = options.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
7563
7941
  return {
7564
7942
  async getRelease(request) {
7565
7943
  const packageName = request.packageName.trim();
@@ -7573,7 +7951,7 @@ function createNpmRegistryGateway(options = {}) {
7573
7951
  response = await fetchImpl(url, {
7574
7952
  method: "GET",
7575
7953
  headers: { accept: "application/json", "user-agent": NPM_USER_AGENT },
7576
- signal: AbortSignal.timeout(timeoutMs)
7954
+ signal: AbortSignal.timeout(timeoutMs2)
7577
7955
  });
7578
7956
  } catch (error) {
7579
7957
  throw registryError(
@@ -7625,12 +8003,83 @@ function trimTrailingSlash(origin) {
7625
8003
 
7626
8004
  // src/infrastructure/child-process/spawn-command-runner.ts
7627
8005
  import { spawn as spawn2 } from "child_process";
7628
- function createSpawnCommandRunner(options = {}) {
7629
- const stdio = options.stdio ?? "inherit";
7630
- return (request) => new Promise((resolve, reject) => {
7631
- const child = spawn2(request.command, request.args, { stdio });
7632
- child.on("error", reject);
7633
- child.on("close", (code, signal) => {
8006
+
8007
+ // src/infrastructure/child-process/windows-command.ts
8008
+ import { statSync } from "fs";
8009
+ import { win32 as win322 } from "path";
8010
+ function isFile(path18) {
8011
+ try {
8012
+ return statSync(path18).isFile();
8013
+ } catch {
8014
+ return false;
8015
+ }
8016
+ }
8017
+ function envValue(env, name) {
8018
+ const key = Object.keys(env).find((key2) => key2.toLowerCase() === name.toLowerCase());
8019
+ return key === void 0 ? void 0 : env[key];
8020
+ }
8021
+ function windowsCommand(command, args, options = {}) {
8022
+ const env = options.env ?? process.env;
8023
+ const fileExists = options.isFile ?? isFile;
8024
+ const cwd = options.cwd ?? process.cwd();
8025
+ const extensions = [".exe", ".com", ".cmd", ".bat"];
8026
+ const configured = (envValue(env, "PATHEXT") ?? ".COM;.EXE;.BAT;.CMD").split(";").map((ext) => ext.toLowerCase()).filter((ext) => extensions.includes(ext));
8027
+ const suffixes = win322.extname(command) ? [""] : configured;
8028
+ const dirs = /[\\/]/.test(command) ? [""] : [cwd, ...(envValue(env, "PATH") ?? "").split(";")];
8029
+ let resolved2;
8030
+ for (const dir of dirs) {
8031
+ for (const suffix of suffixes) {
8032
+ const candidate = win322.resolve(cwd, dir.replace(/^"|"$/g, ""), command + suffix);
8033
+ if (fileExists(candidate)) {
8034
+ resolved2 = candidate;
8035
+ break;
8036
+ }
8037
+ }
8038
+ if (resolved2) break;
8039
+ }
8040
+ const executable = resolved2 ?? command;
8041
+ if (!/\.(cmd|bat)$/i.test(executable)) {
8042
+ return { command: executable, args: [...args], windowsVerbatimArguments: false };
8043
+ }
8044
+ const manager = win322.basename(executable).replace(/\.(cmd|bat)$/i, "").toLowerCase();
8045
+ if (resolved2 && ["npm", "pnpm", "yarn"].includes(manager)) {
8046
+ const root = win322.dirname(executable);
8047
+ const entries = manager === "npm" ? ["node_modules/npm/bin/npm-cli.js"] : manager === "pnpm" ? ["node_modules/pnpm/bin/pnpm.cjs", "node_modules/pnpm/bin/pnpm.js"] : ["node_modules/yarn/bin/yarn.js"];
8048
+ entries.push(`node_modules/corepack/dist/${manager}.js`);
8049
+ const entry = entries.map((path18) => win322.join(root, path18)).find(fileExists);
8050
+ if (entry) {
8051
+ return { command: options.execPath ?? process.execPath, args: [entry, ...args], windowsVerbatimArguments: false };
8052
+ }
8053
+ }
8054
+ const quote = (value) => {
8055
+ if ([...value].some((char) => char.charCodeAt(0) < 32) || /["%!?^&|<>()]/.test(value) || value.endsWith("\\")) {
8056
+ throw new Error("Unsupported character in Windows batch command; use a Node.js entry or executable instead");
8057
+ }
8058
+ return `"${value}"`;
8059
+ };
8060
+ const line = `"${[executable, ...args].map(quote).join(" ")}"`;
8061
+ if (line.length > 8e3) throw new Error("Windows batch command is too long");
8062
+ if (!resolved2) throw Object.assign(new Error(`Executable not found: ${command}`), { code: "ENOENT" });
8063
+ return {
8064
+ command: win322.join(envValue(env, "SystemRoot") ?? "C:\\Windows", "System32", "cmd.exe"),
8065
+ args: ["/d", "/s", "/v:off", "/c", line],
8066
+ windowsVerbatimArguments: true
8067
+ };
8068
+ }
8069
+
8070
+ // src/infrastructure/child-process/spawn-command-runner.ts
8071
+ function createSpawnCommandRunner(options = {}) {
8072
+ const stdio = options.stdio ?? "inherit";
8073
+ return (request) => new Promise((resolve, reject) => {
8074
+ const invocation = (options.platform ?? process.platform) === "win32" ? windowsCommand(request.command, request.args, options.windows) : { command: request.command, args: request.args, windowsVerbatimArguments: false };
8075
+ const child = (options.spawnImpl ?? spawn2)(invocation.command, invocation.args, {
8076
+ stdio,
8077
+ shell: false,
8078
+ windowsVerbatimArguments: invocation.windowsVerbatimArguments,
8079
+ ...options.windows?.env ? { env: options.windows.env } : {}
8080
+ });
8081
+ child.on("error", reject);
8082
+ child.on("close", (code, signal) => {
7634
8083
  if (code === null) {
7635
8084
  reject(new Error(`${request.command} exited via signal ${signal ?? "unknown"}`));
7636
8085
  return;
@@ -7641,17 +8090,17 @@ function createSpawnCommandRunner(options = {}) {
7641
8090
  }
7642
8091
 
7643
8092
  // src/infrastructure/fs/update-check-store.ts
7644
- import { homedir as homedir3 } from "os";
7645
- import { mkdir as mkdir4, readFile as readFile6, writeFile as writeFile4 } from "fs/promises";
7646
- import path8 from "path";
7647
- function adpUpdateCheckPath(home = homedir3()) {
7648
- return path8.join(home, ".adp", "update-check.json");
8093
+ import { homedir as homedir4 } from "os";
8094
+ import { readFile as readFile7 } from "fs/promises";
8095
+ import path11 from "path";
8096
+ function adpUpdateCheckPath(home = homedir4()) {
8097
+ return path11.join(home, ".adp", "update-check.json");
7649
8098
  }
7650
- function createUpdateCheckStore(filePath = adpUpdateCheckPath()) {
8099
+ function createUpdateCheckStore(filePath = adpUpdateCheckPath(), privateFileOptions = {}) {
7651
8100
  return {
7652
8101
  async load() {
7653
8102
  try {
7654
- const record = JSON.parse(await readFile6(filePath, "utf8"));
8103
+ const record = JSON.parse(await readFile7(filePath, "utf8"));
7655
8104
  if (typeof record?.latestVersion !== "string" || record.latestVersion.length === 0) {
7656
8105
  return void 0;
7657
8106
  }
@@ -7672,23 +8121,16 @@ function createUpdateCheckStore(filePath = adpUpdateCheckPath()) {
7672
8121
  }
7673
8122
  },
7674
8123
  async save(record) {
7675
- try {
7676
- await mkdir4(path8.dirname(filePath), { recursive: true });
7677
- await writeFile4(filePath, `${JSON.stringify(record, null, 2)}
7678
- `, {
7679
- encoding: "utf8",
7680
- mode: 384
7681
- });
7682
- } catch {
7683
- }
8124
+ await writePrivateFile(filePath, `${JSON.stringify(record, null, 2)}
8125
+ `, privateFileOptions);
7684
8126
  }
7685
8127
  };
7686
8128
  }
7687
8129
 
7688
8130
  // src/infrastructure/fs/install-source-probe.ts
7689
- import { homedir as homedir4 } from "os";
7690
- import { readFile as readFile7, realpath, writeFile as writeFile5 } from "fs/promises";
7691
- import path9 from "path";
8131
+ import { homedir as homedir5 } from "os";
8132
+ import { readFile as readFile8, realpath, writeFile as writeFile3 } from "fs/promises";
8133
+ import path12 from "path";
7692
8134
 
7693
8135
  // src/domain/update/install-source.ts
7694
8136
  var INSTALL_SOURCES = ["npm", "workbuddy"];
@@ -7724,7 +8166,7 @@ function createInstallSourceProbe(options) {
7724
8166
  return fromEnv;
7725
8167
  }
7726
8168
  const resolved2 = await realpath(root).catch(() => root);
7727
- const prefix = workbuddyConnectorPrefix(options.env, options.homeDir ?? homedir4());
8169
+ const prefix = workbuddyConnectorPrefix(options.env, options.homeDir ?? homedir5());
7728
8170
  const resolvedPrefix = await realpath(prefix).catch(() => prefix);
7729
8171
  return isWithin(resolved2, resolvedPrefix, platform) ? "workbuddy" : "npm";
7730
8172
  } catch {
@@ -7732,8 +8174,8 @@ function createInstallSourceProbe(options) {
7732
8174
  }
7733
8175
  },
7734
8176
  async mark(source2) {
7735
- const filePath = path9.join(packageRoot(), INSTALL_SOURCE_MARKER);
7736
- await writeFile5(
8177
+ const filePath = path12.join(packageRoot(), INSTALL_SOURCE_MARKER);
8178
+ await writeFile3(
7737
8179
  filePath,
7738
8180
  `${JSON.stringify({ source: source2, markedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)}
7739
8181
  `,
@@ -7745,23 +8187,23 @@ function createInstallSourceProbe(options) {
7745
8187
  }
7746
8188
  async function readMarker(root) {
7747
8189
  try {
7748
- const parsed = JSON.parse(await readFile7(path9.join(root, INSTALL_SOURCE_MARKER), "utf8"));
8190
+ const parsed = JSON.parse(await readFile8(path12.join(root, INSTALL_SOURCE_MARKER), "utf8"));
7749
8191
  return isInstallSource(parsed.source) ? parsed.source : void 0;
7750
8192
  } catch {
7751
8193
  return void 0;
7752
8194
  }
7753
8195
  }
7754
8196
  function workbuddyConnectorPrefix(env, homeDir) {
7755
- const configDir = env.WORKBUDDY_CONFIG_DIR?.trim() || env.CODEBUDDY_CONFIG_DIR?.trim() || path9.join(homeDir, ".workbuddy");
7756
- return path9.join(configDir, "binaries", "node", "cli-connector-packages");
8197
+ const configDir = env.WORKBUDDY_CONFIG_DIR?.trim() || env.CODEBUDDY_CONFIG_DIR?.trim() || path12.join(homeDir, ".workbuddy");
8198
+ return path12.join(configDir, "binaries", "node", "cli-connector-packages");
7757
8199
  }
7758
8200
  function isWithin(target, prefix, platform) {
7759
8201
  const normalize = (value) => {
7760
- const resolved2 = path9.resolve(value);
8202
+ const resolved2 = path12.resolve(value);
7761
8203
  return platform === "win32" ? resolved2.toLowerCase() : resolved2;
7762
8204
  };
7763
- const relative = path9.relative(normalize(prefix), normalize(target));
7764
- return relative.length > 0 && !relative.startsWith("..") && !path9.isAbsolute(relative);
8205
+ const relative = path12.relative(normalize(prefix), normalize(target));
8206
+ return relative.length > 0 && !relative.startsWith("..") && !path12.isAbsolute(relative);
7765
8207
  }
7766
8208
 
7767
8209
  // src/infrastructure/tencent/workflow-gateway.ts
@@ -8274,10 +8716,10 @@ function createMemoryGateway(clients) {
8274
8716
  ];
8275
8717
  const patch = {};
8276
8718
  const paths = [];
8277
- for (const [key, field, path14] of fields) {
8719
+ for (const [key, field, path18] of fields) {
8278
8720
  if (input.patch[key] !== void 0 && (rich || key === "type" || key === "content")) {
8279
8721
  patch[field] = input.patch[key];
8280
- paths.push(`entry_patch.${branch}.${path14}`);
8722
+ paths.push(`entry_patch.${branch}.${path18}`);
8281
8723
  }
8282
8724
  }
8283
8725
  await request("ModifyMemoryEntry", {
@@ -9499,8 +9941,8 @@ function createKbShareGateway(client) {
9499
9941
  }
9500
9942
 
9501
9943
  // src/infrastructure/tencent/volume-gateway.ts
9502
- import { mkdir as mkdir5, readFile as readFile8, writeFile as writeFile6 } from "fs/promises";
9503
- import path10 from "path";
9944
+ import { mkdir as mkdir3, readFile as readFile9, writeFile as writeFile4 } from "fs/promises";
9945
+ import path13 from "path";
9504
9946
 
9505
9947
  // src/domain/volume/volume.ts
9506
9948
  var VOLUME_STATUS_LABELS = { 1: "creating", 2: "ready", 3: "failed", 4: "deleting" };
@@ -9511,14 +9953,14 @@ function normalizeVolumePath(input, options = {}) {
9511
9953
  if (segments.some((segment) => segment === "..")) {
9512
9954
  throw new DomainError("Volume paths cannot contain '..'", "usage", "UsageError");
9513
9955
  }
9514
- const path14 = segments.join("/");
9515
- if (!path14 && !options.allowRoot) {
9956
+ const path18 = segments.join("/");
9957
+ if (!path18 && !options.allowRoot) {
9516
9958
  throw new DomainError("Pass a path inside the volume, e.g. docs/report.pdf", "usage", "UsageError");
9517
9959
  }
9518
- return path14;
9960
+ return path18;
9519
9961
  }
9520
- function baseName(path14) {
9521
- return path14.slice(path14.lastIndexOf("/") + 1);
9962
+ function baseName(path18) {
9963
+ return path18.slice(path18.lastIndexOf("/") + 1);
9522
9964
  }
9523
9965
 
9524
9966
  // src/infrastructure/tencent/volume-gateway.ts
@@ -9658,7 +10100,7 @@ function createVolumeGateway(client, options = {}) {
9658
10100
  await request("DeleteFileVolumeEntry", { ...ref3(input), RelativePath: input.path, ExpectedType: ENTRY_TYPES[input.type] });
9659
10101
  },
9660
10102
  async upload(input) {
9661
- const body = await readFile8(input.localPath).catch((error) => {
10103
+ const body = await readFile9(input.localPath).catch((error) => {
9662
10104
  throw new DomainError(`Cannot read ${input.localPath}`, "usage", "LocalFileUnreadable", {
9663
10105
  hint: error.code === "ENOENT" ? "Check the --file path" : error.message
9664
10106
  });
@@ -9681,8 +10123,8 @@ function createVolumeGateway(client, options = {}) {
9681
10123
  throw await transferError(response, "download");
9682
10124
  }
9683
10125
  const bytes = new Uint8Array(await response.arrayBuffer());
9684
- await mkdir5(path10.dirname(input.localPath), { recursive: true });
9685
- await writeFile6(input.localPath, bytes);
10126
+ await mkdir3(path13.dirname(input.localPath), { recursive: true });
10127
+ await writeFile4(input.localPath, bytes);
9686
10128
  return { bytes: bytes.byteLength };
9687
10129
  },
9688
10130
  async previewUrl(input) {
@@ -10071,8 +10513,8 @@ function positive3(value) {
10071
10513
  }
10072
10514
 
10073
10515
  // src/infrastructure/cos/package-downloader.ts
10074
- import { mkdir as mkdir6, readFile as readFile9, writeFile as writeFile7 } from "fs/promises";
10075
- import path11 from "path";
10516
+ import { mkdir as mkdir4, readFile as readFile10, writeFile as writeFile5 } from "fs/promises";
10517
+ import path14 from "path";
10076
10518
  function createPackageDownloader(options = {}) {
10077
10519
  const fetchImpl = options.fetchImpl ?? globalThis.fetch;
10078
10520
  return {
@@ -10085,12 +10527,12 @@ function createPackageDownloader(options = {}) {
10085
10527
  });
10086
10528
  }
10087
10529
  const bytes = new Uint8Array(await response.arrayBuffer());
10088
- await mkdir6(path11.dirname(absolutePath), { recursive: true });
10089
- await writeFile7(absolutePath, bytes);
10530
+ await mkdir4(path14.dirname(absolutePath), { recursive: true });
10531
+ await writeFile5(absolutePath, bytes);
10090
10532
  return { bytes: bytes.byteLength };
10091
10533
  },
10092
10534
  async upload(url, absolutePath) {
10093
- const body = await readFile9(absolutePath).catch((error) => {
10535
+ const body = await readFile10(absolutePath).catch((error) => {
10094
10536
  throw new DomainError(`Cannot read package file ${absolutePath}`, "usage", "PackageFileUnreadable", {
10095
10537
  hint: error.code === "ENOENT" ? "Check the --file path" : error.message
10096
10538
  });
@@ -10201,7 +10643,7 @@ var PAGE_NOT_EXIST = "Generic.PageNotExist";
10201
10643
  function createTencentDocsPortal(options = {}) {
10202
10644
  const origin = options.origin ?? DOCS_PORTAL_ORIGIN;
10203
10645
  const fetchImpl = options.fetchImpl ?? fetch;
10204
- const timeoutMs = options.timeoutMs ?? DEFAULT_DOCS_TIMEOUT_MS;
10646
+ const timeoutMs2 = options.timeoutMs ?? DEFAULT_DOCS_TIMEOUT_MS;
10205
10647
  return {
10206
10648
  async search(input) {
10207
10649
  if (!input.keyword.trim()) {
@@ -10212,7 +10654,7 @@ function createTencentDocsPortal(options = {}) {
10212
10654
  url.searchParams.set("keyword", input.keyword.trim());
10213
10655
  url.searchParams.set("page", "1");
10214
10656
  url.searchParams.set("pagesize", String(pagesize));
10215
- const payload = await requestJson(fetchImpl, url, { method: "GET" }, timeoutMs, origin);
10657
+ const payload = await requestJson(fetchImpl, url, { method: "GET" }, timeoutMs2, origin);
10216
10658
  return parseSearchHits(payload, origin);
10217
10659
  },
10218
10660
  async listPages(input) {
@@ -10225,7 +10667,7 @@ function createTencentDocsPortal(options = {}) {
10225
10667
  isPreview: false
10226
10668
  }
10227
10669
  };
10228
- const payload = await postCgi(fetchImpl, origin, LIST_MENU_ACTION, body, timeoutMs);
10670
+ const payload = await postCgi(fetchImpl, origin, LIST_MENU_ACTION, body, timeoutMs2);
10229
10671
  const nodes = extractMenuNodes(payload);
10230
10672
  const pages = flattenMenu(nodes, productId);
10231
10673
  if (!input.docType) {
@@ -10249,12 +10691,12 @@ function createTencentDocsPortal(options = {}) {
10249
10691
  catId: Number(productId) || productId
10250
10692
  }
10251
10693
  };
10252
- const payload = await postCgi(fetchImpl, origin, GET_DOC_ACTION, body, timeoutMs);
10694
+ const payload = await postCgi(fetchImpl, origin, GET_DOC_ACTION, body, timeoutMs2);
10253
10695
  return parseDocContent(payload, documentId, productId);
10254
10696
  }
10255
10697
  };
10256
10698
  }
10257
- async function postCgi(fetchImpl, origin, action, body, timeoutMs) {
10699
+ async function postCgi(fetchImpl, origin, action, body, timeoutMs2) {
10258
10700
  const url = new URL(`${DOCS_CGI_PREFIX}/${action}`, origin);
10259
10701
  return requestJson(
10260
10702
  fetchImpl,
@@ -10264,11 +10706,11 @@ async function postCgi(fetchImpl, origin, action, body, timeoutMs) {
10264
10706
  headers: { "content-type": "application/json" },
10265
10707
  body: JSON.stringify(body)
10266
10708
  },
10267
- timeoutMs,
10709
+ timeoutMs2,
10268
10710
  origin
10269
10711
  );
10270
10712
  }
10271
- async function requestJson(fetchImpl, url, init, timeoutMs, origin) {
10713
+ async function requestJson(fetchImpl, url, init, timeoutMs2, origin) {
10272
10714
  const referer = productDocsUrl(DEFAULT_DOCS_PRODUCT_ID);
10273
10715
  let response;
10274
10716
  try {
@@ -10280,7 +10722,7 @@ async function requestJson(fetchImpl, url, init, timeoutMs, origin) {
10280
10722
  referer,
10281
10723
  ...init.headers
10282
10724
  },
10283
- signal: AbortSignal.timeout(timeoutMs)
10725
+ signal: AbortSignal.timeout(timeoutMs2)
10284
10726
  });
10285
10727
  } catch (error) {
10286
10728
  throw portalError(
@@ -10564,7 +11006,7 @@ function normalizedChoiceName(value) {
10564
11006
 
10565
11007
  // src/interfaces/cli/prompter.ts
10566
11008
  var PROMPT_TIMEOUT_MS = 6e4;
10567
- async function readSecret(message, timeoutMs) {
11009
+ async function readSecret(message, timeoutMs2) {
10568
11010
  const stdin = process.stdin;
10569
11011
  process.stdout.write(message);
10570
11012
  if (!stdin.isTTY) {
@@ -10583,7 +11025,7 @@ async function readSecret(message, timeoutMs) {
10583
11025
  process.stdout.write("\n");
10584
11026
  resolve(result);
10585
11027
  };
10586
- const timer = setTimeout(() => finish(value), timeoutMs);
11028
+ const timer = setTimeout(() => finish(value), timeoutMs2);
10587
11029
  const onData = (chunk) => {
10588
11030
  for (const char of chunk.toString("utf8")) {
10589
11031
  if (char === "\r" || char === "\n") {
@@ -10607,9 +11049,9 @@ async function readSecret(message, timeoutMs) {
10607
11049
  stdin.on("data", onData);
10608
11050
  });
10609
11051
  }
10610
- async function readLine(message, timeoutMs) {
11052
+ async function readLine(message, timeoutMs2) {
10611
11053
  const rl = createInterface({ input: process.stdin, output: process.stdout });
10612
- const timer = setTimeout(() => rl.close(), timeoutMs);
11054
+ const timer = setTimeout(() => rl.close(), timeoutMs2);
10613
11055
  try {
10614
11056
  return await rl.question(message);
10615
11057
  } catch (error) {
@@ -10727,6 +11169,8 @@ async function createProductionDeps(env = process.env) {
10727
11169
  launchDetachedLogin,
10728
11170
  openBrowser: openInBrowser,
10729
11171
  env,
11172
+ toolKeyStore: createToolKeyFileStore(adpToolKeyPath()),
11173
+ pluginMcp: createPluginMcpGateway(),
10730
11174
  resolveIdentity: (flags) => resolveIdentityFor(flags),
10731
11175
  async resolveGateways(flags) {
10732
11176
  const identity = await resolveIdentityFor(flags);
@@ -10748,6 +11192,7 @@ async function createProductionDeps(env = process.env) {
10748
11192
  release: createReleaseGateway(client),
10749
11193
  plugin: createPluginGateway(client),
10750
11194
  pluginAccess: createPluginAccessGateway(client),
11195
+ toolKey: createToolKeyGateway(lke),
10751
11196
  skillLifecycle: createSkillLifecycleGateway(client),
10752
11197
  workflowRunControl: createWorkflowRunControlGateway(client),
10753
11198
  agentTool: createAgentToolGateway(client),
@@ -10782,25 +11227,13 @@ async function createProductionDeps(env = process.env) {
10782
11227
  usage: createUsageGateway(client),
10783
11228
  objectStore: createPlatformObjectStore(),
10784
11229
  packageDownloader: createPackageDownloader(),
10785
- knowledge: independent ? unsupportedGateway(
10786
- "adp kb doc is not supported by the independent-site profile",
10787
- "Knowledge document transport uses the public-cloud LKE and platform COS planes"
10788
- ) : createKnowledgeGateway({ adp: client, lke }),
11230
+ knowledge: createKnowledgeGateway({ adp: client, lke }),
10789
11231
  kb: createKbGateway(client),
10790
11232
  channel: createChannelGateway(client)
10791
11233
  };
10792
11234
  }
10793
11235
  };
10794
11236
  }
10795
- function unsupportedGateway(message, hint) {
10796
- return new Proxy({}, {
10797
- get() {
10798
- return async () => {
10799
- throw usageError(message, hint);
10800
- };
10801
- }
10802
- });
10803
- }
10804
11237
  function canPrompt(deps) {
10805
11238
  return deps.stdinIsTTY && !deps.agentName;
10806
11239
  }
@@ -11364,7 +11797,7 @@ function registerIdentityCommands(registry, ctx) {
11364
11797
  }
11365
11798
 
11366
11799
  // src/interfaces/cli/commands/init.ts
11367
- import path12 from "path";
11800
+ import path15 from "path";
11368
11801
 
11369
11802
  // src/domain/project-config/adp-config.ts
11370
11803
  var APP_MODES = {
@@ -11682,7 +12115,7 @@ function registerInitCommand(registry, ctx) {
11682
12115
  plugins: []
11683
12116
  }
11684
12117
  };
11685
- const configPath = path12.resolve(ctx.flags.cwd, ctx.flags.config ?? "adp.jsonc");
12118
+ const configPath = path15.resolve(ctx.flags.cwd, ctx.flags.config ?? "adp.jsonc");
11686
12119
  const createRemote = Boolean(args["create-remote"]);
11687
12120
  const gateways = createRemote && !ctx.flags.dryRun ? await ctx.deps.resolveGateways(ctx.flags) : void 0;
11688
12121
  const previewModels = createRemote && ctx.flags.dryRun && config2.appMode === APP_MODES.claw && modelId === PLACEHOLDER_MODEL_ID ? await ctx.deps.resolveGateways(ctx.flags).then((resolved2) => resolved2.model).catch(() => void 0) : void 0;
@@ -13646,7 +14079,7 @@ function registerSpaceCommands(registry, ctx) {
13646
14079
  }
13647
14080
 
13648
14081
  // src/infrastructure/fs/app-package-reader.ts
13649
- import { readFile as readFile10 } from "fs/promises";
14082
+ import { readFile as readFile11 } from "fs/promises";
13650
14083
  import { inflateRawSync } from "zlib";
13651
14084
  var END_OF_CENTRAL_DIRECTORY = 101010256;
13652
14085
  var CENTRAL_FILE_HEADER = 33639248;
@@ -13727,7 +14160,7 @@ function createAppPackageReader() {
13727
14160
  }
13728
14161
  async function readInventory(file) {
13729
14162
  try {
13730
- const zip = await readFile10(file);
14163
+ const zip = await readFile11(file);
13731
14164
  const entries = listEntries(zip);
13732
14165
  if (entries.length === 0 && zip.length > 0 && !looksLikeZip(zip)) {
13733
14166
  return EMPTY_INVENTORY;
@@ -15430,28 +15863,126 @@ function afterAppCreate(payload) {
15430
15863
  };
15431
15864
  }
15432
15865
 
15866
+ // src/interfaces/cli/commands/agent/index.ts
15867
+ import { readFile as readFile12 } from "fs/promises";
15868
+ import path16 from "path";
15869
+
15433
15870
  // src/application/agent/agent-use-cases.ts
15871
+ var CLAW_AGENT_LIMIT = 20;
15434
15872
  async function listAgents(gateway, input) {
15435
15873
  return collectPages(
15436
15874
  (page3, pageSize) => gateway.list({ appId: input.appId, limit: pageSize, page: page3 }),
15437
15875
  { limit: input.limit, page: input.page, firstPage: 0 }
15438
15876
  );
15439
15877
  }
15440
- function agentAlreadyExists(appId3, agentId) {
15878
+ function specForAgentCreate(config2, projectInstructions, overrides) {
15879
+ const subViaFlags = overrides.subAgent === true || overrides.role === 1;
15880
+ const role = subViaFlags ? 1 : overrides.role ?? config2?.agent.profile.role ?? 0;
15881
+ if (role !== 0 && role !== 1) {
15882
+ throw usageError(
15883
+ `Agent role must be 0 (main) or 1 (sub-agent); got ${role}`,
15884
+ "Pass --role 0 or --role 1 (or --sub-agent)"
15885
+ );
15886
+ }
15887
+ if (subViaFlags && !overrides.name?.trim()) {
15888
+ throw usageError(
15889
+ "A Claw sub-agent needs --name",
15890
+ "Pass --name. adp.jsonc agent.profile.name is the main agent and is not reused for --role 1"
15891
+ );
15892
+ }
15893
+ const name = (overrides.name ?? config2?.agent.profile.name ?? "").trim();
15894
+ if (!name) {
15895
+ throw usageError("Agent name is required", "Pass --name, or set agent.profile.name in adp.jsonc");
15896
+ }
15897
+ const description = overrides.description !== void 0 ? overrides.description : subViaFlags ? name : config2?.agent.profile.description;
15898
+ const configuredModel = overrides.modelId ?? config2?.agent.model.modelId ?? "";
15899
+ const modelId = configuredModel === PLACEHOLDER_MODEL_ID ? "" : configuredModel;
15900
+ return {
15901
+ profile: {
15902
+ name,
15903
+ description,
15904
+ iconUrl: subViaFlags ? void 0 : config2?.agent.profile.iconUrl,
15905
+ role
15906
+ },
15907
+ instructions: instructionsForCreate(name, description, subViaFlags, config2, projectInstructions, overrides.instructions),
15908
+ model: {
15909
+ modelId,
15910
+ ...subViaFlags ? {} : {
15911
+ alias: config2?.agent.model.alias,
15912
+ contextWordsLimit: config2?.agent.model.contextWordsLimit,
15913
+ instructionsWordsLimit: config2?.agent.model.instructionsWordsLimit
15914
+ }
15915
+ },
15916
+ skillIds: overrides.skillIds ?? (subViaFlags ? [] : config2?.agent.skillIds ?? []),
15917
+ pluginIds: subViaFlags ? [] : config2?.agent.pluginIds ?? [],
15918
+ connectorIds: subViaFlags ? void 0 : config2?.agent.connectorIds,
15919
+ tools: subViaFlags ? [{ ...KNOWLEDGE_RETRIEVAL_TOOL }] : config2?.agent.tools ?? [],
15920
+ plugins: subViaFlags ? void 0 : config2?.agent.plugins,
15921
+ maxReasoningRound: subViaFlags ? void 0 : config2?.agent.maxReasoningRound
15922
+ };
15923
+ }
15924
+ function instructionsForCreate(name, description, subViaFlags, config2, projectInstructions, explicit) {
15925
+ if (explicit !== void 0) {
15926
+ return explicit;
15927
+ }
15928
+ if (subViaFlags) {
15929
+ return defaultSubAgentInstructions(name, description);
15930
+ }
15931
+ if (projectInstructions !== void 0) {
15932
+ return projectInstructions;
15933
+ }
15934
+ if (config2?.agent.instructions) {
15935
+ return config2.agent.instructions;
15936
+ }
15937
+ if (!config2) {
15938
+ return defaultSubAgentInstructions(name, description);
15939
+ }
15940
+ return "";
15941
+ }
15942
+ function defaultSubAgentInstructions(name, description) {
15943
+ if (description && description !== name) {
15944
+ return `You are ${name}. ${description}`;
15945
+ }
15946
+ return `You are ${name}.`;
15947
+ }
15948
+ function shellWord4(value) {
15949
+ return /^[\w@%+=:,./-]+$/.test(value) ? value : `'${value.replace(/'/g, "'\\''")}'`;
15950
+ }
15951
+ function subAgentCreateCommand(appId3, name) {
15952
+ const shown = name?.trim() ? name.trim() : "sub-agent";
15953
+ return `adp agent create --app-id ${appId3} --role 1 --name ${shellWord4(shown)} --dry-run --format json`;
15954
+ }
15955
+ function agentAlreadyExists(appId3, agentId, name) {
15441
15956
  return new DomainError(
15442
- `App ${appId3} already has agent ${agentId}; a Claw app cannot get a second agent from CreateAgent`,
15957
+ `App ${appId3} already has main agent ${agentId}. CreateAgent with role 0 returns that agent instead of creating another one`,
15443
15958
  "conflict",
15444
15959
  "AgentAlreadyExists",
15445
15960
  {
15446
- hint: "A Claw app (app_mode 4) has exactly one config-side agent. Change it with adp agent update, or create another app for a second agent",
15961
+ hint: "A Claw app (app_mode 4) has one main agent (role 0) and up to 19 sub-agents (role 1). Edit the main agent with adp agent update, or pass --role 1 (or --sub-agent) on adp agent create to add a sub-agent",
15447
15962
  nextActions: [
15448
15963
  {
15449
15964
  command: `adp agent update --app-id ${appId3} --agent-id ${agentId} --format json`,
15450
- reason: "edit the existing agent from the same adp.jsonc instead of creating one (this writes the agent draft); do not re-run agent create"
15965
+ reason: "edit the existing main agent (role 0) instead of creating another one"
15451
15966
  },
15452
15967
  {
15453
- command: `adp agent update --app-id ${appId3} --agent-id ${agentId} --dry-run --format json`,
15454
- reason: "or preview that update first"
15968
+ command: subAgentCreateCommand(appId3, name),
15969
+ reason: "add a Claw sub-agent (role 1); set --description to when the main agent should delegate, and --instructions for how the sub-agent works"
15970
+ }
15971
+ ]
15972
+ }
15973
+ );
15974
+ }
15975
+ function agentLimitReached(appId3) {
15976
+ return new DomainError(
15977
+ `App ${appId3} already has ${CLAW_AGENT_LIMIT} agents. A Claw app allows 1 main agent and 19 sub-agents`,
15978
+ "conflict",
15979
+ "AgentLimitExceeded",
15980
+ {
15981
+ hint: "Delete a sub-agent before creating another. Keep the main agent (role 0).",
15982
+ nextActions: [
15983
+ {
15984
+ command: `adp agent list --app-id ${appId3} --format json`,
15985
+ reason: "list agents and their roles before deleting a sub-agent"
15455
15986
  }
15456
15987
  ]
15457
15988
  }
@@ -15472,7 +16003,7 @@ async function describeAgent(gateway, input) {
15472
16003
  throw error;
15473
16004
  }
15474
16005
  }
15475
- async function createAgent(gateway, input, apps2) {
16006
+ async function createAgent(gateway, input, apps2, models) {
15476
16007
  const app = await apps2.describe({ appId: input.appId });
15477
16008
  if (app.appMode !== void 0 && app.appMode !== APP_MODES.claw) {
15478
16009
  throw usageError(
@@ -15480,26 +16011,110 @@ async function createAgent(gateway, input, apps2) {
15480
16011
  app.appMode === APP_MODES.standard ? "A Standard app has no extra agents: edit its settings in adp.jsonc and run adp deploy" : "Edit the existing agents with adp agent update, or create a Claw app (adp init --app-mode 4) for a separate agent"
15481
16012
  );
15482
16013
  }
16014
+ const role = input.spec.profile?.role ?? 0;
16015
+ if (role !== 0 && role !== 1) {
16016
+ throw usageError(
16017
+ `Agent role must be 0 (main) or 1 (sub-agent); got ${role}`,
16018
+ "Pass --role 0 or --role 1 (or --sub-agent)"
16019
+ );
16020
+ }
15483
16021
  const before = await gateway.list({ appId: input.appId, limit: 100, page: 0 });
16022
+ const main2 = before.items.find((agent) => (agent.role ?? 0) === 0);
16023
+ if (role === 0 && main2) {
16024
+ throw agentAlreadyExists(input.appId, main2.agentId, input.spec.profile?.name);
16025
+ }
16026
+ if (before.items.length >= CLAW_AGENT_LIMIT) {
16027
+ throw agentLimitReached(input.appId);
16028
+ }
16029
+ const spec = await withInheritedModel(gateway, models, {
16030
+ appId: input.appId,
16031
+ spaceId: input.spaceId,
16032
+ spec: input.spec,
16033
+ agents: before.items
16034
+ });
15484
16035
  if (input.dryRun) {
15485
- const existing = before.items[0];
15486
- if (app.appMode === APP_MODES.claw && existing) {
15487
- throw agentAlreadyExists(input.appId, existing.agentId);
16036
+ if (role === 1) {
16037
+ return { dryRun: true, appId: input.appId, role: 1, name: spec.profile.name, modelId: spec.model.modelId };
15488
16038
  }
15489
16039
  return { dryRun: true, appId: input.appId };
15490
16040
  }
15491
- const created = await gateway.create(input);
16041
+ const created = await gateway.create({ appId: input.appId, spec });
15492
16042
  if (before.items.some((agent) => agent.agentId === created.agentId)) {
15493
- throw agentAlreadyExists(input.appId, created.agentId);
16043
+ throw agentAlreadyExists(input.appId, created.agentId, spec.profile.name);
16044
+ }
16045
+ if (role === 1) {
16046
+ return { dryRun: false, ...created, role: 1, name: spec.profile.name, modelId: spec.model.modelId };
15494
16047
  }
15495
16048
  return { dryRun: false, ...created };
15496
16049
  }
16050
+ async function withInheritedModel(gateway, models, input) {
16051
+ const current = input.spec.model.modelId;
16052
+ if (current && current !== PLACEHOLDER_MODEL_ID) {
16053
+ return input.spec;
16054
+ }
16055
+ const main2 = input.agents.find((agent) => (agent.role ?? 0) === 0);
16056
+ if (main2) {
16057
+ try {
16058
+ const detail = await gateway.describe({ appId: input.appId, agentId: main2.agentId });
16059
+ const modelId = detail.spec?.model.modelId;
16060
+ if (modelId && modelId !== PLACEHOLDER_MODEL_ID) {
16061
+ return { ...input.spec, model: { ...input.spec.model, modelId } };
16062
+ }
16063
+ } catch {
16064
+ }
16065
+ }
16066
+ if (models && input.spaceId) {
16067
+ try {
16068
+ const listed = await models.list({ limit: 1, page: 0, scene: MODEL_SCENE_CLAW, spaceId: input.spaceId });
16069
+ const modelId = listed.items[0]?.modelId;
16070
+ if (modelId) {
16071
+ return { ...input.spec, model: { ...input.spec.model, modelId } };
16072
+ }
16073
+ } catch {
16074
+ }
16075
+ }
16076
+ return { ...input.spec, model: { ...input.spec.model, modelId: "Auto/auto" } };
16077
+ }
16078
+ function hasPatch(patch) {
16079
+ return Boolean(patch && Object.values(patch).some((value) => value !== void 0));
16080
+ }
16081
+ function applyAgentPatch(spec, patch) {
16082
+ return {
16083
+ ...spec,
16084
+ profile: {
16085
+ ...spec.profile,
16086
+ ...patch.name !== void 0 ? { name: patch.name } : {},
16087
+ ...patch.description !== void 0 ? { description: patch.description } : {}
16088
+ },
16089
+ ...patch.instructions !== void 0 ? { instructions: patch.instructions } : {},
16090
+ model: patch.modelId !== void 0 ? { ...spec.model, modelId: patch.modelId } : spec.model
16091
+ };
16092
+ }
15497
16093
  async function modifyAgent(gateway, input) {
16094
+ const patching = hasPatch(input.patch);
16095
+ let spec = input.spec;
16096
+ if (patching || !spec) {
16097
+ if (!patching) {
16098
+ throw usageError(
16099
+ "Agent update needs adp.jsonc or a field to change",
16100
+ "Pass --name, --description, --instructions, --instructions-file, or --model-id"
16101
+ );
16102
+ }
16103
+ const current = await describeAgent(gateway, { appId: input.appId, agentId: input.agentId });
16104
+ if (!current.spec) {
16105
+ throw usageError(
16106
+ `Agent ${input.agentId} has no config to update`,
16107
+ "Run adp agent get and pass --name, --description, --instructions, or --model-id"
16108
+ );
16109
+ }
16110
+ spec = applyAgentPatch(current.spec, input.patch ?? {});
16111
+ }
16112
+ const patched = patching ? { name: spec.profile.name, description: spec.profile.description, modelId: spec.model.modelId } : {};
15498
16113
  if (input.dryRun) {
15499
- return { dryRun: true, agentId: input.agentId };
16114
+ return { dryRun: true, agentId: input.agentId, ...patched };
15500
16115
  }
15501
- await gateway.modify({ ...input, updateMask: defaultUpdateMask() });
15502
- return { dryRun: false, agentId: input.agentId };
16116
+ await gateway.modify({ appId: input.appId, agentId: input.agentId, spec, updateMask: defaultUpdateMask() });
16117
+ return { dryRun: false, agentId: input.agentId, ...patched };
15503
16118
  }
15504
16119
  async function deleteAgent(gateway, input) {
15505
16120
  await assertExisting([input.agentId], async (agentId) => (await describeAgent(gateway, { appId: input.appId, agentId })).agentId, {
@@ -15570,28 +16185,82 @@ function registerAgentCommands(registry, ctx) {
15570
16185
  registry.define(
15571
16186
  createCommand({
15572
16187
  command: "agent create",
15573
- describe: "Create an agent from the local spec",
16188
+ describe: "Create a Claw agent (role 0 main, or role 1 sub-agent)",
15574
16189
  effects: "non_idempotent",
15575
16190
  outputKind: "data",
15576
16191
  examples: [
15577
16192
  "adp agent create --app-id app-1 --format json",
15578
- "adp agent create --app-id app-1 --dry-run"
16193
+ "adp agent create --app-id app-1 --dry-run",
16194
+ "adp agent create --app-id app-1 --role 1 --name ray\u7684\u6D4B\u8BD5agent --description delegates-ray-tests --dry-run --format json",
16195
+ "adp agent create --app-id app-1 --sub-agent --name ray\u7684\u6D4B\u8BD5agent --instructions You-are-ray --model-id m1 --dry-run --format json"
16196
+ ],
16197
+ flags: [
16198
+ { name: "app-id", type: "string", describe: "App ID (default: adp.jsonc app_id)" },
16199
+ {
16200
+ name: "name",
16201
+ type: "string",
16202
+ describe: "Agent name. Overrides adp.jsonc agent.profile.name. Required when adp.jsonc is absent, and required with --role 1 or --sub-agent"
16203
+ },
16204
+ {
16205
+ name: "description",
16206
+ type: "string",
16207
+ describe: "Routing description: the main agent uses it to decide when to delegate. Overrides adp.jsonc agent.profile.description"
16208
+ },
16209
+ {
16210
+ name: "role",
16211
+ type: "number",
16212
+ describe: "0 = main agent (default), 1 = Claw sub-agent. A second role 0 fails with AgentAlreadyExists; role 1 adds a sub-agent (up to 19)"
16213
+ },
16214
+ {
16215
+ name: "sub-agent",
16216
+ type: "boolean",
16217
+ describe: "Create a Claw sub-agent (same as --role 1). Does not copy the main agent's instructions.md, skill_ids, or plugin_ids"
16218
+ },
16219
+ {
16220
+ name: "instructions",
16221
+ type: "string",
16222
+ describe: "System prompt. With --role 1 this is the sub-agent prompt; instructions.md is not copied unless you pass it here or via --instructions-file"
16223
+ },
16224
+ {
16225
+ name: "instructions-file",
16226
+ type: "string",
16227
+ describe: "Read the system prompt from this file instead of --instructions or instructions.md"
16228
+ },
16229
+ {
16230
+ name: "model-id",
16231
+ type: "string",
16232
+ describe: "Model ID. Default: a real adp.jsonc agent.model.model_id, else the main agent's model, else the first ModelScene=18 model, else Auto/auto"
16233
+ },
16234
+ {
16235
+ name: "skill-id",
16236
+ type: "string",
16237
+ list: true,
16238
+ describe: "Skill ID to mount (repeatable). With --role 1, defaults to none instead of copying the main agent"
16239
+ }
15579
16240
  ],
15580
- flags: [{ name: "app-id", type: "string", describe: "App ID (default: adp.jsonc app_id)" }],
15581
16241
  async handler(args) {
15582
- const loaded = await ctx.deps.configStore.load({
16242
+ const instructionsFlag = await readInstructionsFlag(ctx, args);
16243
+ const overrides = readCreateOverrides(args, instructionsFlag);
16244
+ const loaded = await tryLoadConfig(ctx.deps.configStore, {
15583
16245
  cwd: ctx.flags.cwd,
15584
16246
  configPath: ctx.flags.config,
15585
16247
  envName: ctx.flags.env
15586
16248
  });
15587
- const instructions = await resolveInstructions(ctx.deps.configStore, ctx.flags.cwd, loaded.config);
16249
+ const projectInstructions = loaded && !overrides.subAgent && !instructionsFlag.explicit ? await resolveInstructions(ctx.deps.configStore, ctx.flags.cwd, loaded.config) : void 0;
16250
+ const spec = specForAgentCreate(loaded?.config, projectInstructions, overrides);
15588
16251
  const gateways = await ctx.deps.resolveGateways(ctx.flags);
15589
16252
  return mutationPayload(
15590
- await createAgent(gateways.agent, {
15591
- appId: await agentAppId(ctx, args),
15592
- spec: toAgentSpec(loaded.config, instructions),
15593
- dryRun: ctx.flags.dryRun
15594
- }, gateways.app),
16253
+ await createAgent(
16254
+ gateways.agent,
16255
+ {
16256
+ appId: await agentAppId(ctx, args),
16257
+ spec,
16258
+ dryRun: ctx.flags.dryRun,
16259
+ spaceId: loaded?.config.spaceId
16260
+ },
16261
+ gateways.app,
16262
+ gateways.model
16263
+ ),
15595
16264
  "create",
15596
16265
  "agent"
15597
16266
  );
@@ -15601,28 +16270,50 @@ function registerAgentCommands(registry, ctx) {
15601
16270
  registry.define(
15602
16271
  createCommand({
15603
16272
  command: "agent update",
15604
- describe: "Modify an agent from the local spec",
16273
+ describe: "Modify an agent from adp.jsonc, or patch fields passed as flags",
15605
16274
  effects: "idempotent",
15606
16275
  outputKind: "data",
15607
16276
  examples: [
15608
16277
  "adp agent update --app-id app-1 --agent-id agt-1 --format json",
15609
- "adp agent update --app-id app-1 --agent-id agt-1 --dry-run"
16278
+ "adp agent update --app-id app-1 --agent-id agt-1 --dry-run",
16279
+ "adp agent update --app-id app-1 --agent-id agt-1 --name ray\u7684\u6D4B\u8BD5agent --description delegates-ray-tests --dry-run --format json"
15610
16280
  ],
15611
16281
  flags: [
15612
16282
  { name: "app-id", type: "string", describe: "App ID (default: adp.jsonc app_id)" },
15613
- { name: "agent-id", type: "string", describe: "Agent ID (default: adp.jsonc agent_id when the app is the bound one)" }
16283
+ { name: "agent-id", type: "string", describe: "Agent ID (default: adp.jsonc agent_id when the app is the bound one)" },
16284
+ { name: "name", type: "string", describe: "New agent name. With any field flag, other fields stay as they are on the agent" },
16285
+ { name: "description", type: "string", describe: "New routing description. With any field flag, other fields stay as they are on the agent" },
16286
+ { name: "instructions", type: "string", describe: "New system prompt" },
16287
+ { name: "instructions-file", type: "string", describe: "Read the new system prompt from this file" },
16288
+ { name: "model-id", type: "string", describe: "New model ID" }
15614
16289
  ],
15615
16290
  async handler(args) {
15616
- const loaded = await ctx.deps.configStore.load({
16291
+ const instructionsFlag = await readInstructionsFlag(ctx, args);
16292
+ const patch = readUpdatePatch(args, instructionsFlag);
16293
+ const loaded = await tryLoadConfig(ctx.deps.configStore, {
15617
16294
  cwd: ctx.flags.cwd,
15618
16295
  configPath: ctx.flags.config,
15619
16296
  envName: ctx.flags.env
15620
16297
  });
15621
- const instructions = await resolveInstructions(ctx.deps.configStore, ctx.flags.cwd, loaded.config);
15622
16298
  const gateways = await ctx.deps.resolveGateways(ctx.flags);
16299
+ const ids4 = await agentIds(ctx, args, "agent update modifies one agent");
16300
+ if (patch) {
16301
+ return mutationPayload(
16302
+ await modifyAgent(gateways.agent, { ...ids4, patch, dryRun: ctx.flags.dryRun }),
16303
+ "update",
16304
+ "agent"
16305
+ );
16306
+ }
16307
+ if (!loaded) {
16308
+ throw usageError(
16309
+ "Agent update needs adp.jsonc or a field to change",
16310
+ "Pass --name, --description, --instructions, --instructions-file, or --model-id"
16311
+ );
16312
+ }
16313
+ const instructions = await resolveInstructions(ctx.deps.configStore, ctx.flags.cwd, loaded.config);
15623
16314
  return mutationPayload(
15624
16315
  await modifyAgent(gateways.agent, {
15625
- ...await agentIds(ctx, args, "agent update modifies one agent"),
16316
+ ...ids4,
15626
16317
  spec: toAgentSpec(loaded.config, instructions),
15627
16318
  dryRun: ctx.flags.dryRun
15628
16319
  }),
@@ -15663,6 +16354,72 @@ function registerAgentCommands(registry, ctx) {
15663
16354
  })
15664
16355
  );
15665
16356
  }
16357
+ async function readInstructionsFlag(ctx, args) {
16358
+ const inline = typeof args.instructions === "string" ? args.instructions : void 0;
16359
+ const file = typeof args["instructions-file"] === "string" ? args["instructions-file"] : void 0;
16360
+ if (inline !== void 0 && file !== void 0) {
16361
+ throw usageError("Pass only one of --instructions and --instructions-file", "One system prompt is enough");
16362
+ }
16363
+ if (file !== void 0) {
16364
+ const filePath = path16.resolve(ctx.flags.cwd, file);
16365
+ try {
16366
+ return { text: await readFile12(filePath, "utf8"), explicit: true };
16367
+ } catch {
16368
+ throw usageError(`Cannot read --instructions-file ${file}`, "Pass a path to a text file, or use --instructions");
16369
+ }
16370
+ }
16371
+ if (inline !== void 0) {
16372
+ return { text: inline, explicit: true };
16373
+ }
16374
+ return { explicit: false };
16375
+ }
16376
+ function readCreateOverrides(args, instructions) {
16377
+ const subFlag = args["sub-agent"] === true;
16378
+ let role;
16379
+ if (args.role !== void 0) {
16380
+ const parsed = typeof args.role === "number" ? args.role : Number(args.role);
16381
+ if (parsed !== 0 && parsed !== 1) {
16382
+ throw usageError("`--role` must be 0 (main agent) or 1 (sub-agent)", "Pass --role 0, --role 1, or --sub-agent");
16383
+ }
16384
+ role = parsed;
16385
+ }
16386
+ if (subFlag && role === 0) {
16387
+ throw usageError("`--sub-agent` is role 1", "Drop --role 0, or drop --sub-agent");
16388
+ }
16389
+ const subAgent = subFlag || role === 1;
16390
+ return {
16391
+ name: optionalText3(args.name),
16392
+ description: optionalText3(args.description),
16393
+ role: subAgent ? 1 : role,
16394
+ subAgent,
16395
+ instructions: instructions.explicit ? instructions.text ?? "" : void 0,
16396
+ modelId: optionalText3(args["model-id"]),
16397
+ skillIds: args["skill-id"] === void 0 ? void 0 : stringList2(args["skill-id"])
16398
+ };
16399
+ }
16400
+ function readUpdatePatch(args, instructions) {
16401
+ const patch = {};
16402
+ if (typeof args.name === "string") {
16403
+ patch.name = args.name;
16404
+ }
16405
+ if (typeof args.description === "string") {
16406
+ patch.description = args.description;
16407
+ }
16408
+ if (instructions.explicit) {
16409
+ patch.instructions = instructions.text ?? "";
16410
+ }
16411
+ if (typeof args["model-id"] === "string") {
16412
+ patch.modelId = args["model-id"];
16413
+ }
16414
+ return Object.values(patch).some((value) => value !== void 0) ? patch : void 0;
16415
+ }
16416
+ function optionalText3(value) {
16417
+ return typeof value === "string" ? value : void 0;
16418
+ }
16419
+ function stringList2(value) {
16420
+ const raw = Array.isArray(value) ? value : value === void 0 || value === null ? [] : [value];
16421
+ return raw.flatMap((item) => String(item).split(",")).map((item) => item.trim()).filter((item) => item.length > 0);
16422
+ }
15666
16423
 
15667
16424
  // src/application/agent/agent-tool-use-cases.ts
15668
16425
  function explainAgentToolError(error) {
@@ -17386,7 +18143,7 @@ async function modifyPlugin(gateway, input) {
17386
18143
  async function deletePlugin(gateway, input) {
17387
18144
  const scope = input.spaceId;
17388
18145
  if (scope) {
17389
- await assertExisting([input.pluginId], async (pluginId2) => (await gateway.describe({ pluginId: pluginId2, spaceId: scope })).pluginId, {
18146
+ await assertExisting([input.pluginId], async (pluginId3) => (await gateway.describe({ pluginId: pluginId3, spaceId: scope })).pluginId, {
17390
18147
  code: "PluginNotFound",
17391
18148
  noun: "plugin",
17392
18149
  listCommand: `adp plugin list --space-id ${scope} --fields pluginId,name --format json`
@@ -17402,7 +18159,7 @@ async function deletePlugin(gateway, input) {
17402
18159
  throw error;
17403
18160
  }
17404
18161
  const spaceId = input.spaceId;
17405
- const missing = spaceId ? await missingIds([input.pluginId], async (pluginId2) => (await gateway.describe({ pluginId: pluginId2, spaceId })).pluginId).catch(() => []) : [input.pluginId];
18162
+ const missing = spaceId ? await missingIds([input.pluginId], async (pluginId3) => (await gateway.describe({ pluginId: pluginId3, spaceId })).pluginId).catch(() => []) : [input.pluginId];
17406
18163
  if (missing.length === 0) {
17407
18164
  throw error;
17408
18165
  }
@@ -17419,8 +18176,8 @@ async function deletePlugin(gateway, input) {
17419
18176
  }
17420
18177
 
17421
18178
  // src/infrastructure/fs/plugin-spec-file.ts
17422
- import { readFile as readFile11 } from "fs/promises";
17423
- import path13 from "path";
18179
+ import { readFile as readFile13 } from "fs/promises";
18180
+ import path17 from "path";
17424
18181
  import { parse as parseJsonc2 } from "jsonc-parser";
17425
18182
  import { z as z2 } from "zod";
17426
18183
  var paramTypeSchema = z2.enum(Object.keys(PARAM_TYPES));
@@ -17544,10 +18301,10 @@ function parsePluginSpec(text13, sourcePath, env = process.env) {
17544
18301
  };
17545
18302
  }
17546
18303
  async function loadPluginSpec(cwd, relativePath) {
17547
- const resolved2 = path13.resolve(cwd, relativePath);
18304
+ const resolved2 = path17.resolve(cwd, relativePath);
17548
18305
  let text13;
17549
18306
  try {
17550
- text13 = await readFile11(resolved2, "utf8");
18307
+ text13 = await readFile13(resolved2, "utf8");
17551
18308
  } catch {
17552
18309
  throw usageError(
17553
18310
  `Plugin spec not found: ${resolved2}`,
@@ -17603,22 +18360,28 @@ function registerPluginCommands(registry, ctx) {
17603
18360
  async handler(args) {
17604
18361
  const spaceId = await resolveSpaceId(ctx, args, "plugin get reads the plugin in a space");
17605
18362
  const gateways = await ctx.deps.resolveGateways(ctx.flags);
17606
- const pluginId2 = assertResourceId(String(args["plugin-id"]), "plugin-id");
17607
- const plugin = await describePlugin(gateways.plugin, { pluginId: pluginId2, spaceId: assertResourceId(spaceId, "space-id") });
17608
- const toolId = plugin.tools?.[0]?.toolId;
18363
+ const pluginId3 = assertResourceId(String(args["plugin-id"]), "plugin-id");
18364
+ const plugin = await describePlugin(gateways.plugin, { pluginId: pluginId3, spaceId: assertResourceId(spaceId, "space-id") });
18365
+ const tool = plugin.tools?.[0];
18366
+ const toolId = tool?.toolId;
18367
+ const mcpCall = plugin.pluginKind === PLUGIN_KIND_MCP && toolId ? {
18368
+ command: mcpCallCommand(pluginId3, { toolId, name: tool?.name }),
18369
+ reason: "preview a direct MCP call; adp plugin key create saves the tool key that call reads"
18370
+ } : void 0;
17609
18371
  return {
17610
18372
  result: plugin,
17611
18373
  nextActions: toolId ? [
17612
18374
  {
17613
- command: `adp agent tool bind --plugin-id ${pluginId2} --tool-id ${toolId} --format json`,
17614
- reason: `bind ${plugin.tools[0].name ?? "the first tool"} to an agent (pick another toolId from tools[] for a different tool); also add {"plugin_id":"${pluginId2}","tool_id":"${toolId}"} to agent.tools in adp.jsonc, or the next adp deploy removes it`,
18375
+ command: `adp agent tool bind --plugin-id ${pluginId3} --tool-id ${toolId} --format json`,
18376
+ reason: `bind ${tool?.name ?? "the first tool"} to an agent (pick another toolId from tools[] for a different tool); also add {"plugin_id":"${pluginId3}","tool_id":"${toolId}"} to agent.tools in adp.jsonc, or the next adp deploy removes it`,
17615
18377
  needs_input: { flag: "agent-id", question: "Agent ID to bind the tool to (adp agent list)" }
17616
18378
  },
17617
18379
  {
17618
18380
  command: "adp deploy --dry-run --format json",
17619
- reason: `the project-file route: add {"plugin_id":"${pluginId2}","tool_id":"${toolId}"} to agent.tools in adp.jsonc, then preview the release`,
18381
+ reason: `the project-file route: add {"plugin_id":"${pluginId3}","tool_id":"${toolId}"} to agent.tools in adp.jsonc, then preview the release`,
17620
18382
  needs_edit: { file: "adp.jsonc" }
17621
- }
18383
+ },
18384
+ ...mcpCall ? [mcpCall] : []
17622
18385
  ] : void 0
17623
18386
  };
17624
18387
  }
@@ -17830,17 +18593,24 @@ function registerPluginCommands(registry, ctx) {
17830
18593
  })
17831
18594
  );
17832
18595
  }
18596
+ function mcpCallCommand(pluginId3, tool) {
18597
+ const name = tool.name?.trim();
18598
+ if (name && /^[A-Za-z0-9_-]+$/.test(name)) {
18599
+ return `adp plugin tool call --plugin-id ${pluginId3} --tool-name ${name} --dry-run --format json`;
18600
+ }
18601
+ return `adp plugin tool call --plugin-id ${pluginId3} --tool-id ${tool.toolId} --dry-run --format json`;
18602
+ }
17833
18603
  function afterPluginCreate(payload, spaceId) {
17834
- const pluginId2 = payload.result.pluginId;
17835
- if (payload.result.dryRun || !pluginId2) {
18604
+ const pluginId3 = payload.result.pluginId;
18605
+ if (payload.result.dryRun || !pluginId3) {
17836
18606
  return payload;
17837
18607
  }
17838
18608
  return {
17839
18609
  ...payload,
17840
18610
  nextActions: [
17841
- { command: `adp plugin get --plugin-id ${pluginId2} --space-id ${spaceId} --format json`, reason: "list the plugin's tool IDs" },
18611
+ { command: `adp plugin get --plugin-id ${pluginId3} --space-id ${spaceId} --format json`, reason: "list the plugin's tool IDs" },
17842
18612
  {
17843
- command: `adp agent tool bind --plugin-id ${pluginId2} --format json`,
18613
+ command: `adp agent tool bind --plugin-id ${pluginId3} --format json`,
17844
18614
  reason: "bind one of those tools to an agent's draft, then adp deploy",
17845
18615
  needs_input: { flag: "tool-id", question: "Tool ID from adp plugin get" }
17846
18616
  }
@@ -17986,76 +18756,481 @@ function strings6(value) {
17986
18756
  function pluginId(args) {
17987
18757
  return assertResourceId(String(args["plugin-id"]), "plugin-id");
17988
18758
  }
17989
- function registerPluginAccessCommands(registry, ctx) {
18759
+ function registerPluginAccessCommands(registry, ctx) {
18760
+ registry.define(
18761
+ createCommand({
18762
+ command: "plugin share get",
18763
+ describe: "Show who may use a plugin across spaces (active config, or --pending for the one awaiting approval)",
18764
+ effects: "read_only",
18765
+ outputKind: "data",
18766
+ flags: [pluginIdFlag, { name: "pending", type: "boolean", describe: "Show the config awaiting approval" }],
18767
+ examples: ["adp plugin share get --plugin-id plg-1 --format json"],
18768
+ async handler(args) {
18769
+ const gateways = await ctx.deps.resolveGateways(ctx.flags);
18770
+ if (args.pending === true) {
18771
+ return { result: await describePendingPluginShare(gateways.pluginAccess, { pluginId: pluginId(args) }) };
18772
+ }
18773
+ return { result: await describePluginShare(gateways.pluginAccess, { pluginId: pluginId(args), pending: false }) };
18774
+ }
18775
+ })
18776
+ );
18777
+ registry.define(
18778
+ createCommand({
18779
+ command: "plugin share set",
18780
+ describe: "Change plugin or credential share scope; may need approval, and is blocked while apps use what it removes",
18781
+ effects: "idempotent",
18782
+ outputKind: "data",
18783
+ flags: [
18784
+ pluginIdFlag,
18785
+ { name: "scope", type: "string", describe: "Plugin scope: none | all | spaces" },
18786
+ { name: "space", type: "string", describe: "Target space ID for --scope spaces (repeatable)" },
18787
+ { name: "credential", type: "string", describe: "<credential-id>=none|all|spaces:<id>,<id> (repeatable)" },
18788
+ { name: "remark", type: "string", describe: "Note for the approver" },
18789
+ { name: "force", type: "boolean", describe: "Admin only: apply even though referrers lose access" },
18790
+ { name: "confirm-name", type: "string", describe: "Plugin name, required with --force" }
18791
+ ],
18792
+ examples: [
18793
+ "adp plugin share set --plugin-id plg-1 --scope spaces --space sp-2 --yes",
18794
+ "adp plugin share set --plugin-id plg-1 --credential cred-1=all --yes --format json"
18795
+ ],
18796
+ async handler(args) {
18797
+ const id = pluginId(args);
18798
+ let plugin;
18799
+ if (args.scope !== void 0) {
18800
+ const scope = String(args.scope);
18801
+ if (!SCOPES.includes(scope)) {
18802
+ throw usageError(`--scope must be one of ${SCOPES.join(", ")}`);
18803
+ }
18804
+ plugin = { scope, spaceIds: [...new Set(strings6(args.space).map((s) => assertResourceId(s, "space")))] };
18805
+ } else if (args.space !== void 0) {
18806
+ throw usageError("--space needs --scope spaces");
18807
+ }
18808
+ const credentials = strings6(args.credential).map(parseCredentialShare);
18809
+ await requireYes(ctx, `change share settings of plugin ${id}`);
18810
+ if (ctx.flags.dryRun) {
18811
+ return { result: { dryRun: true, pluginId: id, plugin, credentials }, message: `Would change share settings of plugin ${id}.` };
18812
+ }
18813
+ const gateways = await ctx.deps.resolveGateways(ctx.flags);
18814
+ const result = await setPluginShare(gateways.pluginAccess, {
18815
+ pluginId: id,
18816
+ plugin,
18817
+ credentials,
18818
+ remark: typeof args.remark === "string" ? args.remark : void 0,
18819
+ force: args.force === true,
18820
+ confirmedName: typeof args["confirm-name"] === "string" ? args["confirm-name"] : void 0
18821
+ });
18822
+ return {
18823
+ result,
18824
+ message: result.status === "pending_approval" ? `Submitted for approval: plugin ${id}.` : `Share settings applied: plugin ${id}.`,
18825
+ nextActions: [
18826
+ {
18827
+ command: `adp plugin share get --plugin-id ${id}${result.status === "pending_approval" ? " --pending" : ""}`,
18828
+ reason: result.status === "pending_approval" ? "Review the config awaiting approval" : "Confirm the active config"
18829
+ }
18830
+ ]
18831
+ };
18832
+ }
18833
+ })
18834
+ );
18835
+ registry.define(
18836
+ createCommand({
18837
+ command: "plugin share approval",
18838
+ describe: "Show a plugin share approval (scope and credential changes)",
18839
+ effects: "read_only",
18840
+ outputKind: "data",
18841
+ flags: [pluginIdFlag, { name: "approval-id", type: "string", describe: "Approval ID", required: true }],
18842
+ examples: ["adp plugin share approval --plugin-id plg-1 --approval-id 2096851869377474240 --format json"],
18843
+ async handler(args) {
18844
+ const gateways = await ctx.deps.resolveGateways(ctx.flags);
18845
+ return {
18846
+ result: await describePluginShareApproval(gateways.pluginAccess, {
18847
+ pluginId: pluginId(args),
18848
+ approvalId: assertResourceId(String(args["approval-id"]), "approval-id")
18849
+ })
18850
+ };
18851
+ }
18852
+ })
18853
+ );
18854
+ registry.define(
18855
+ createCommand({
18856
+ command: "plugin tool get",
18857
+ describe: "Show one plugin tool with its input/output schema",
18858
+ effects: "read_only",
18859
+ outputKind: "data",
18860
+ flags: [
18861
+ pluginIdFlag,
18862
+ { name: "tool-id", type: "string", describe: "Tool ID (see adp plugin get)", required: true },
18863
+ { name: "space-id", type: "string", describe: "Space ID (defaults to adp.jsonc)" }
18864
+ ],
18865
+ examples: ["adp plugin tool get --plugin-id plg-1 --tool-id tool-1 --format json"],
18866
+ async handler(args) {
18867
+ const spaceId = await optionalSpaceId(ctx, args);
18868
+ const gateways = await ctx.deps.resolveGateways(ctx.flags);
18869
+ const id = pluginId(args);
18870
+ const toolId = assertResourceId(String(args["tool-id"]), "tool-id");
18871
+ const tool = await describePluginTool(gateways.pluginAccess, {
18872
+ pluginId: id,
18873
+ toolId,
18874
+ ...spaceId ? { spaceId: assertResourceId(spaceId, "space-id") } : {}
18875
+ });
18876
+ const toolName = tool.name.trim();
18877
+ return {
18878
+ result: tool,
18879
+ nextActions: [
18880
+ {
18881
+ command: /^[A-Za-z0-9_-]+$/.test(toolName) ? `adp plugin tool call --plugin-id ${id} --tool-name ${toolName} --dry-run --format json` : `adp plugin tool call --plugin-id ${id} --tool-id ${toolId} --dry-run --format json`,
18882
+ reason: "preview a direct MCP call when this plugin is pluginKind=2; adp plugin key create saves the tool key"
18883
+ }
18884
+ ]
18885
+ };
18886
+ }
18887
+ })
18888
+ );
18889
+ registry.define(
18890
+ createCommand({
18891
+ command: "plugin auth check",
18892
+ describe: "Check whether the caller has authorized OAuth plugins",
18893
+ effects: "read_only",
18894
+ outputKind: "data",
18895
+ flags: [
18896
+ { name: "plugin-id", type: "string", describe: "Plugin ID (repeatable)", required: true, list: true },
18897
+ { name: "credential-id", type: "string", describe: "OAuth credential ID, paired by position with --plugin-id" }
18898
+ ],
18899
+ examples: ["adp plugin auth check --plugin-id plg-1 --credential-id cred-1 --format json"],
18900
+ async handler(args) {
18901
+ const pluginIds = strings6(args["plugin-id"]).map((id) => assertResourceId(id, "plugin-id"));
18902
+ const credentialIds = strings6(args["credential-id"]);
18903
+ const gateways = await ctx.deps.resolveGateways(ctx.flags);
18904
+ const items = await checkPluginAuth(gateways.pluginAccess, {
18905
+ items: pluginIds.map((id, i) => ({ pluginId: id, ...credentialIds[i] ? { credentialId: credentialIds[i] } : {} }))
18906
+ });
18907
+ return { result: { items } };
18908
+ }
18909
+ })
18910
+ );
18911
+ registry.define(
18912
+ createCommand({
18913
+ command: "plugin oauth-url",
18914
+ describe: "Create an OAuth consent link for an OAuth plugin credential; open it in a browser",
18915
+ effects: "non_idempotent",
18916
+ outputKind: "data",
18917
+ flags: [pluginIdFlag, { name: "credential-id", type: "string", describe: "OAuth credential ID", required: true }],
18918
+ examples: ["adp plugin oauth-url --plugin-id plg-1 --credential-id cred-1"],
18919
+ async handler(args) {
18920
+ const input = { pluginId: pluginId(args), credentialId: assertResourceId(String(args["credential-id"]), "credential-id") };
18921
+ if (ctx.flags.dryRun) {
18922
+ return { result: { dryRun: true, ...input }, message: `Would create an OAuth consent link for credential ${input.credentialId}.` };
18923
+ }
18924
+ const gateways = await ctx.deps.resolveGateways(ctx.flags);
18925
+ const { url } = await pluginOAuthUrl(gateways.pluginAccess, input);
18926
+ return { result: { url }, message: url };
18927
+ }
18928
+ })
18929
+ );
18930
+ }
18931
+
18932
+ // src/domain/plugin/tool-key.ts
18933
+ function maskToolKey(value) {
18934
+ if (value.includes("*")) {
18935
+ return value;
18936
+ }
18937
+ if (value.length <= 8) {
18938
+ return "****";
18939
+ }
18940
+ return `${value.slice(0, 4)}****${value.slice(-4)}`;
18941
+ }
18942
+
18943
+ // src/application/plugin/tool-key-use-cases.ts
18944
+ var CREATE_COMMAND = {
18945
+ command: "adp plugin key create --format json",
18946
+ reason: "create a tool key and save it for adp plugin tool call"
18947
+ };
18948
+ var LIST_COMMAND = {
18949
+ command: "adp plugin key list --format json",
18950
+ reason: "list tool key ids; the plaintext is only returned when a key is created"
18951
+ };
18952
+ function toolKeyLimitReached(error) {
18953
+ const message = error instanceof Error ? error.message : "";
18954
+ if (!message.includes("4509400")) {
18955
+ return void 0;
18956
+ }
18957
+ return new DomainError("This account already has the maximum of 2 tool keys (4509400)", "conflict", "ToolKeyLimitReached", {
18958
+ hint: "An existing key's plaintext cannot be read back. Run adp plugin key list. A lastAccessTime of 0 has never been used. After you confirm which key to drop, disable it, then delete it, and create again. Or set a plaintext you already hold as ADP_API_KEY.",
18959
+ retryable: false,
18960
+ requestId: error instanceof DomainError ? error.requestId : void 0,
18961
+ cause: error,
18962
+ nextActions: [
18963
+ {
18964
+ command: "adp plugin key list --format json",
18965
+ reason: "see key ids and lastAccessTime; 0 means unused. Confirm with the user before disabling one"
18966
+ },
18967
+ {
18968
+ command: "adp plugin key disable --format json",
18969
+ reason: "disable the confirmed key first; delete rejects a key that is still enabled",
18970
+ needs_input: { flag: "key-id", question: "Tool key ID from adp plugin key list" }
18971
+ },
18972
+ {
18973
+ command: "adp plugin key delete --yes --format json",
18974
+ reason: "after disable succeeds, pass the same --key-id, then adp plugin key create",
18975
+ needs_input: { flag: "key-id", question: "Tool key ID from adp plugin key list" }
18976
+ }
18977
+ ]
18978
+ });
18979
+ }
18980
+ function toolKeyNotDisabled(error, apiKeyId) {
18981
+ const message = error instanceof Error ? error.message : "";
18982
+ if (!message.includes("4509404")) {
18983
+ return void 0;
18984
+ }
18985
+ return new DomainError(`Tool key ${apiKeyId} must be disabled before it can be deleted (4509404)`, "conflict", "ToolKeyNotDisabled", {
18986
+ hint: "Disable the key, then delete it. Disabling stops clients that use this key.",
18987
+ retryable: false,
18988
+ requestId: error instanceof DomainError ? error.requestId : void 0,
18989
+ cause: error,
18990
+ nextActions: [
18991
+ {
18992
+ command: `adp plugin key disable --key-id ${apiKeyId} --format json`,
18993
+ reason: "disable the key; DeleteApiKey rejects a key that is still enabled"
18994
+ },
18995
+ {
18996
+ command: `adp plugin key delete --key-id ${apiKeyId} --yes --format json`,
18997
+ reason: "delete the key after it is disabled"
18998
+ }
18999
+ ]
19000
+ });
19001
+ }
19002
+ function toolKeyMissingError() {
19003
+ return new DomainError("MCP plugin calls need an ADP API Key (tool key)", "auth", "ToolKeyMissing", {
19004
+ hint: "Create one with adp plugin key create (it is saved to ~/.adp/tool-key), set ADP_API_KEY, or pass --api-key-env. Do not put the secret on the command line.",
19005
+ retryable: false,
19006
+ nextActions: [CREATE_COMMAND, LIST_COMMAND]
19007
+ });
19008
+ }
19009
+ async function resolveToolKey(input) {
19010
+ if (input.envName) {
19011
+ const value = (input.env[input.envName] ?? "").trim();
19012
+ if (!value) {
19013
+ throw new DomainError(`Environment variable ${input.envName} is empty`, "usage", "UsageError", {
19014
+ hint: "Export the ADP API Key in that variable, or omit --api-key-env to use ADP_API_KEY or ~/.adp/tool-key",
19015
+ retryable: false
19016
+ });
19017
+ }
19018
+ return { value, source: "api-key-env", envName: input.envName };
19019
+ }
19020
+ const fromEnv = (input.env.ADP_API_KEY ?? "").trim();
19021
+ if (fromEnv) {
19022
+ return { value: fromEnv, source: "env", envName: "ADP_API_KEY" };
19023
+ }
19024
+ const stored = await input.store.load();
19025
+ if (!stored?.value.trim()) {
19026
+ return void 0;
19027
+ }
19028
+ return { value: stored.value.trim(), source: "file", ...stored.apiKeyId ? { apiKeyId: stored.apiKeyId } : {} };
19029
+ }
19030
+ function viewOf(record) {
19031
+ return {
19032
+ apiKeyId: record.apiKeyId,
19033
+ apiKey: maskToolKey(record.apiKey),
19034
+ masked: true,
19035
+ description: record.description,
19036
+ ...record.status !== void 0 ? { status: record.status, enabled: record.status === 1 } : {},
19037
+ ...record.createTime ? { createTime: record.createTime } : {},
19038
+ ...record.lastAccessTime ? { lastAccessTime: record.lastAccessTime } : {}
19039
+ };
19040
+ }
19041
+ async function listToolKeys(gateway) {
19042
+ const records20 = await gateway.list();
19043
+ return records20.map(viewOf);
19044
+ }
19045
+ async function requireKey(gateway, apiKeyId) {
19046
+ const found = (await listToolKeys(gateway)).find((item) => item.apiKeyId === apiKeyId);
19047
+ if (!found) {
19048
+ throw new DomainError(`No tool key with ID ${apiKeyId}`, "not_found", "ToolKeyNotFound", {
19049
+ hint: "List ids with adp plugin key list. The plaintext cannot be recovered from the list.",
19050
+ retryable: false,
19051
+ nextActions: [LIST_COMMAND]
19052
+ });
19053
+ }
19054
+ return found;
19055
+ }
19056
+ async function createToolKey(gateway, store, input) {
19057
+ const description = input.description?.trim() || void 0;
19058
+ if (input.dryRun) {
19059
+ return { dryRun: true, save: input.save, ...description ? { description } : {} };
19060
+ }
19061
+ let created;
19062
+ try {
19063
+ created = await gateway.create();
19064
+ } catch (error) {
19065
+ const limit = toolKeyLimitReached(error);
19066
+ if (limit) {
19067
+ throw limit;
19068
+ }
19069
+ throw error;
19070
+ }
19071
+ if (!created.apiKey || !created.apiKeyId) {
19072
+ throw new DomainError("CreateApiKey did not return a tool key", "upstream", "ToolKeyNotCreated", {
19073
+ hint: "Nothing was saved locally. Retry adp plugin key create.",
19074
+ retryable: true,
19075
+ nextActions: [CREATE_COMMAND]
19076
+ });
19077
+ }
19078
+ if (input.save) {
19079
+ await store.save({ value: created.apiKey, apiKeyId: created.apiKeyId });
19080
+ }
19081
+ if (description) {
19082
+ try {
19083
+ await gateway.update({ apiKeyId: created.apiKeyId, description });
19084
+ } catch (error) {
19085
+ throw new DomainError(
19086
+ input.save ? `Tool key ${created.apiKeyId} was saved locally, but the description was not updated` : `Tool key ${created.apiKeyId} was created, but the description was not updated. Plaintext: ${created.apiKey}`,
19087
+ "upstream",
19088
+ "ToolKeyDescriptionFailed",
19089
+ {
19090
+ cause: error,
19091
+ hint: input.save ? "The key is in ~/.adp/tool-key. Set the description with adp plugin key update." : "This is the only time the plaintext is available.",
19092
+ retryable: false,
19093
+ nextActions: [
19094
+ {
19095
+ command: `adp plugin key update --key-id ${created.apiKeyId} --format json`,
19096
+ reason: "retry the description",
19097
+ needs_input: { flag: "description", question: "Description for this tool key" }
19098
+ }
19099
+ ]
19100
+ }
19101
+ );
19102
+ }
19103
+ }
19104
+ return {
19105
+ dryRun: false,
19106
+ apiKeyId: created.apiKeyId,
19107
+ apiKey: input.reveal ? created.apiKey : maskToolKey(created.apiKey),
19108
+ masked: !input.reveal,
19109
+ saved: input.save,
19110
+ ...description ? { description } : {},
19111
+ ...input.keepSecret ? { secret: created.apiKey } : {}
19112
+ };
19113
+ }
19114
+ async function setToolKeyStatus(gateway, input) {
19115
+ await requireKey(gateway, input.apiKeyId);
19116
+ if (!input.dryRun) {
19117
+ await gateway.setStatus({ apiKeyId: input.apiKeyId, status: input.status });
19118
+ }
19119
+ return { dryRun: input.dryRun, apiKeyId: input.apiKeyId, status: input.status, enabled: input.status === 1 };
19120
+ }
19121
+ async function updateToolKey(gateway, input) {
19122
+ await requireKey(gateway, input.apiKeyId);
19123
+ if (!input.dryRun) {
19124
+ await gateway.update({ apiKeyId: input.apiKeyId, description: input.description });
19125
+ }
19126
+ return { dryRun: input.dryRun, apiKeyId: input.apiKeyId, description: input.description };
19127
+ }
19128
+ async function deleteToolKey(gateway, store, input) {
19129
+ await requireKey(gateway, input.apiKeyId);
19130
+ if (input.dryRun) {
19131
+ return { dryRun: true, apiKeyId: input.apiKeyId, localKeyCleared: false };
19132
+ }
19133
+ try {
19134
+ await gateway.delete({ apiKeyId: input.apiKeyId });
19135
+ } catch (error) {
19136
+ const notDisabled = toolKeyNotDisabled(error, input.apiKeyId);
19137
+ if (notDisabled) {
19138
+ throw notDisabled;
19139
+ }
19140
+ throw error;
19141
+ }
19142
+ const stored = await store.load();
19143
+ const localKeyCleared = stored?.apiKeyId === input.apiKeyId;
19144
+ if (localKeyCleared) {
19145
+ await store.clear();
19146
+ }
19147
+ return { dryRun: false, apiKeyId: input.apiKeyId, localKeyCleared };
19148
+ }
19149
+
19150
+ // src/interfaces/cli/commands/plugin/key.ts
19151
+ var keyIdFlag = { name: "key-id", type: "string", describe: "Tool key ID (adp plugin key list)", required: true };
19152
+ var revealFlag4 = {
19153
+ name: "reveal",
19154
+ type: "boolean",
19155
+ describe: "Print the plaintext tool key. Otherwise it is masked. The plaintext is only available at creation."
19156
+ };
19157
+ function storeOf(ctx) {
19158
+ return ctx.deps.toolKeyStore ?? createToolKeyFileStore(adpToolKeyPath());
19159
+ }
19160
+ function savedNote(saved) {
19161
+ return saved ? " Saved to ~/.adp/tool-key for later adp plugin tool call." : "";
19162
+ }
19163
+ function registerPluginKeyCommands(registry, ctx) {
17990
19164
  registry.define(
17991
19165
  createCommand({
17992
- command: "plugin share get",
17993
- describe: "Show who may use a plugin across spaces (active config, or --pending for the one awaiting approval)",
19166
+ command: "plugin key list",
19167
+ describe: "List ADP API keys (tool keys). Values are masked; plaintext exists only at creation.",
17994
19168
  effects: "read_only",
17995
19169
  outputKind: "data",
17996
- flags: [pluginIdFlag, { name: "pending", type: "boolean", describe: "Show the config awaiting approval" }],
17997
- examples: ["adp plugin share get --plugin-id plg-1 --format json"],
17998
- async handler(args) {
19170
+ flags: [],
19171
+ examples: ["adp plugin key list --format json"],
19172
+ async handler() {
17999
19173
  const gateways = await ctx.deps.resolveGateways(ctx.flags);
18000
- if (args.pending === true) {
18001
- return { result: await describePendingPluginShare(gateways.pluginAccess, { pluginId: pluginId(args) }) };
18002
- }
18003
- return { result: await describePluginShare(gateways.pluginAccess, { pluginId: pluginId(args), pending: false }) };
19174
+ const items = await listToolKeys(gateways.toolKey);
19175
+ return applyListShape(
19176
+ ctx,
19177
+ sliceResult(items, { limit: ctx.flags.limit, page: ctx.flags.page }),
19178
+ "adp plugin key list"
19179
+ );
18004
19180
  }
18005
19181
  })
18006
19182
  );
18007
19183
  registry.define(
18008
19184
  createCommand({
18009
- command: "plugin share set",
18010
- describe: "Change plugin or credential share scope; may need approval, and is blocked while apps use what it removes",
18011
- effects: "idempotent",
19185
+ command: "plugin key create",
19186
+ describe: "Create an ADP API key (tool key) and save it to ~/.adp/tool-key so adp plugin tool call can use it",
19187
+ effects: "non_idempotent",
18012
19188
  outputKind: "data",
18013
19189
  flags: [
18014
- pluginIdFlag,
18015
- { name: "scope", type: "string", describe: "Plugin scope: none | all | spaces" },
18016
- { name: "space", type: "string", describe: "Target space ID for --scope spaces (repeatable)" },
18017
- { name: "credential", type: "string", describe: "<credential-id>=none|all|spaces:<id>,<id> (repeatable)" },
18018
- { name: "remark", type: "string", describe: "Note for the approver" },
18019
- { name: "force", type: "boolean", describe: "Admin only: apply even though referrers lose access" },
18020
- { name: "confirm-name", type: "string", describe: "Plugin name, required with --force" }
19190
+ { name: "description", type: "string", describe: "Note stored on the key (ModifyApiKey)" },
19191
+ revealFlag4,
19192
+ {
19193
+ name: "save",
19194
+ type: "boolean",
19195
+ default: true,
19196
+ describe: "Save the new key to ~/.adp/tool-key (mode 0600). Pass --no-save to skip; then --reveal is required, because the plaintext is returned only once."
19197
+ }
18021
19198
  ],
18022
19199
  examples: [
18023
- "adp plugin share set --plugin-id plg-1 --scope spaces --space sp-2 --yes",
18024
- "adp plugin share set --plugin-id plg-1 --credential cred-1=all --yes --format json"
19200
+ "adp plugin key create --format json",
19201
+ "adp plugin key create --description kling --reveal --format json",
19202
+ "adp plugin key create --no-save --reveal --format json"
18025
19203
  ],
18026
19204
  async handler(args) {
18027
- const id = pluginId(args);
18028
- let plugin;
18029
- if (args.scope !== void 0) {
18030
- const scope = String(args.scope);
18031
- if (!SCOPES.includes(scope)) {
18032
- throw usageError(`--scope must be one of ${SCOPES.join(", ")}`);
18033
- }
18034
- plugin = { scope, spaceIds: [...new Set(strings6(args.space).map((s) => assertResourceId(s, "space")))] };
18035
- } else if (args.space !== void 0) {
18036
- throw usageError("--space needs --scope spaces");
19205
+ const save = args.save !== false;
19206
+ const reveal = args.reveal === true;
19207
+ if (!save && !reveal) {
19208
+ throw usageError(
19209
+ "Refusing to create a tool key that cannot be recovered",
19210
+ "The plaintext is returned only once. Omit --no-save to store it in ~/.adp/tool-key, or pass --reveal to print it."
19211
+ );
18037
19212
  }
18038
- const credentials = strings6(args.credential).map(parseCredentialShare);
18039
- await requireYes(ctx, `change share settings of plugin ${id}`);
18040
- if (ctx.flags.dryRun) {
18041
- return { result: { dryRun: true, pluginId: id, plugin, credentials }, message: `Would change share settings of plugin ${id}.` };
19213
+ const description = typeof args.description === "string" ? args.description : void 0;
19214
+ const created = await createToolKey(
19215
+ (await ctx.deps.resolveGateways(ctx.flags)).toolKey,
19216
+ storeOf(ctx),
19217
+ { description, reveal, save, dryRun: ctx.flags.dryRun }
19218
+ );
19219
+ if (created.dryRun) {
19220
+ return {
19221
+ result: created,
19222
+ message: save ? "Would create a tool key and save it to ~/.adp/tool-key." : "Would create a tool key and print it. Nothing is saved."
19223
+ };
18042
19224
  }
18043
- const gateways = await ctx.deps.resolveGateways(ctx.flags);
18044
- const result = await setPluginShare(gateways.pluginAccess, {
18045
- pluginId: id,
18046
- plugin,
18047
- credentials,
18048
- remark: typeof args.remark === "string" ? args.remark : void 0,
18049
- force: args.force === true,
18050
- confirmedName: typeof args["confirm-name"] === "string" ? args["confirm-name"] : void 0
18051
- });
19225
+ const { secret: _secret, ...result } = created;
18052
19226
  return {
18053
19227
  result,
18054
- message: result.status === "pending_approval" ? `Submitted for approval: plugin ${id}.` : `Share settings applied: plugin ${id}.`,
19228
+ message: `Created tool key ${result.apiKey}.${savedNote(result.saved)}`,
18055
19229
  nextActions: [
19230
+ { command: "adp plugin key list --format json", reason: "confirm the new key id; the list stays masked" },
18056
19231
  {
18057
- command: `adp plugin share get --plugin-id ${id}${result.status === "pending_approval" ? " --pending" : ""}`,
18058
- reason: result.status === "pending_approval" ? "Review the config awaiting approval" : "Confirm the active config"
19232
+ command: "adp plugin list --fields pluginId,name,pluginKind --format json",
19233
+ reason: "pick a pluginKind=2 plugin, then adp plugin tool call"
18059
19234
  }
18060
19235
  ]
18061
19236
  };
@@ -18064,86 +19239,397 @@ function registerPluginAccessCommands(registry, ctx) {
18064
19239
  );
18065
19240
  registry.define(
18066
19241
  createCommand({
18067
- command: "plugin share approval",
18068
- describe: "Show a plugin share approval (scope and credential changes)",
18069
- effects: "read_only",
19242
+ command: "plugin key update",
19243
+ describe: "Change an ADP API key's description",
19244
+ effects: "idempotent",
18070
19245
  outputKind: "data",
18071
- flags: [pluginIdFlag, { name: "approval-id", type: "string", describe: "Approval ID", required: true }],
18072
- examples: ["adp plugin share approval --plugin-id plg-1 --approval-id 2096851869377474240 --format json"],
19246
+ flags: [
19247
+ keyIdFlag,
19248
+ { name: "description", type: "string", describe: "New note", required: true }
19249
+ ],
19250
+ examples: ["adp plugin key update --key-id key-1 --description kling --format json"],
18073
19251
  async handler(args) {
19252
+ const apiKeyId = assertResourceId(String(args["key-id"]), "key-id");
19253
+ const description = String(args.description ?? "").trim();
19254
+ if (!description) {
19255
+ throw usageError("Missing --description", "Pass the note to store on this tool key");
19256
+ }
18074
19257
  const gateways = await ctx.deps.resolveGateways(ctx.flags);
19258
+ const result = await updateToolKey(gateways.toolKey, { apiKeyId, description, dryRun: ctx.flags.dryRun });
18075
19259
  return {
18076
- result: await describePluginShareApproval(gateways.pluginAccess, {
18077
- pluginId: pluginId(args),
18078
- approvalId: assertResourceId(String(args["approval-id"]), "approval-id")
18079
- })
19260
+ result,
19261
+ message: ctx.flags.dryRun ? `Would update the description of tool key ${apiKeyId}.` : `Updated tool key ${apiKeyId}.`
18080
19262
  };
18081
19263
  }
18082
19264
  })
18083
19265
  );
19266
+ for (const enabled of [true, false]) {
19267
+ const verb = enabled ? "enable" : "disable";
19268
+ const status = enabled ? 1 : 2;
19269
+ registry.define(
19270
+ createCommand({
19271
+ command: `plugin key ${verb}`,
19272
+ describe: enabled ? "Enable an ADP API key (status 1). A disabled key cannot call MCP plugins." : "Disable an ADP API key (status 2). Delete requires the key to be disabled first.",
19273
+ effects: "idempotent",
19274
+ outputKind: "data",
19275
+ flags: [keyIdFlag],
19276
+ examples: [`adp plugin key ${verb} --key-id key-1 --format json`],
19277
+ async handler(args) {
19278
+ const apiKeyId = assertResourceId(String(args["key-id"]), "key-id");
19279
+ const gateways = await ctx.deps.resolveGateways(ctx.flags);
19280
+ const result = await setToolKeyStatus(gateways.toolKey, { apiKeyId, status, dryRun: ctx.flags.dryRun });
19281
+ if (ctx.flags.dryRun) {
19282
+ return { result, message: `Would ${verb} tool key ${apiKeyId}.` };
19283
+ }
19284
+ return { result, message: `${enabled ? "Enabled" : "Disabled"} tool key ${apiKeyId}.` };
19285
+ }
19286
+ })
19287
+ );
19288
+ }
18084
19289
  registry.define(
18085
19290
  createCommand({
18086
- command: "plugin tool get",
18087
- describe: "Show one plugin tool with its input/output schema",
18088
- effects: "read_only",
19291
+ command: "plugin key delete",
19292
+ describe: "Delete an ADP API key. Clients using it stop working. A matching ~/.adp/tool-key is removed.",
19293
+ effects: "non_idempotent",
18089
19294
  outputKind: "data",
18090
- flags: [
18091
- pluginIdFlag,
18092
- { name: "tool-id", type: "string", describe: "Tool ID (see adp plugin get)", required: true },
18093
- { name: "space-id", type: "string", describe: "Space ID (defaults to adp.jsonc)" }
18094
- ],
18095
- examples: ["adp plugin tool get --plugin-id plg-1 --tool-id tool-1 --format json"],
19295
+ flags: [keyIdFlag],
19296
+ examples: ["adp plugin key delete --key-id key-1 --yes"],
18096
19297
  async handler(args) {
18097
- const spaceId = await optionalSpaceId(ctx, args);
19298
+ const apiKeyId = assertResourceId(String(args["key-id"]), "key-id");
19299
+ await requireYes(ctx, `delete tool key ${apiKeyId}`);
18098
19300
  const gateways = await ctx.deps.resolveGateways(ctx.flags);
19301
+ const result = await deleteToolKey(gateways.toolKey, storeOf(ctx), { apiKeyId, dryRun: ctx.flags.dryRun });
18099
19302
  return {
18100
- result: await describePluginTool(gateways.pluginAccess, {
18101
- pluginId: pluginId(args),
18102
- toolId: assertResourceId(String(args["tool-id"]), "tool-id"),
18103
- ...spaceId ? { spaceId: assertResourceId(spaceId, "space-id") } : {}
18104
- })
19303
+ result,
19304
+ message: ctx.flags.dryRun ? `Would delete tool key ${apiKeyId}.` : `Deleted tool key ${apiKeyId}.`
18105
19305
  };
18106
19306
  }
18107
19307
  })
18108
19308
  );
19309
+ }
19310
+
19311
+ // src/application/plugin/plugin-mcp-use-cases.ts
19312
+ async function prepareToolKey(gateway, store, input) {
19313
+ const existing = await resolveToolKey({ env: input.env, envName: input.envName, store });
19314
+ if (existing) {
19315
+ return {
19316
+ value: existing.value,
19317
+ source: existing.source,
19318
+ ...existing.apiKeyId ? { apiKeyId: existing.apiKeyId } : {},
19319
+ created: false,
19320
+ wouldCreate: false,
19321
+ hasKey: true
19322
+ };
19323
+ }
19324
+ if (!input.autoCreate) {
19325
+ if (input.dryRun) {
19326
+ return { created: false, wouldCreate: false, hasKey: false };
19327
+ }
19328
+ throw toolKeyMissingError();
19329
+ }
19330
+ if (input.dryRun) {
19331
+ return { created: false, wouldCreate: true, hasKey: false };
19332
+ }
19333
+ if (!gateway) {
19334
+ throw new DomainError("Creating a tool key needs cloud credentials", "auth", "ToolKeyMissing", {
19335
+ hint: "Run adp auth status, then retry with --auto-create-key, or create a key with adp plugin key create.",
19336
+ retryable: false,
19337
+ nextActions: [
19338
+ { command: "adp auth status --format json", reason: "check the CAM credential used to call CreateApiKey" },
19339
+ { command: "adp plugin key create --format json", reason: "create and save a tool key" }
19340
+ ]
19341
+ });
19342
+ }
19343
+ const created = await createToolKey(gateway, store, { reveal: false, save: true, dryRun: false, keepSecret: true });
19344
+ if (created.dryRun || !created.secret) {
19345
+ throw new DomainError("CreateApiKey did not return a tool key", "upstream", "ToolKeyNotCreated", { retryable: true });
19346
+ }
19347
+ return { value: created.secret, source: "created", apiKeyId: created.apiKeyId, created: true, wouldCreate: false, hasKey: true };
19348
+ }
19349
+ async function resolveToolName(tools, input) {
19350
+ const name = input.toolName?.trim() || void 0;
19351
+ if (!input.toolId) {
19352
+ if (!name) {
19353
+ throw new DomainError("Pass --tool-name or --tool-id", "usage", "UsageError", {
19354
+ hint: "Tool names are on adp plugin get. --tool-id is resolved to that name.",
19355
+ retryable: false,
19356
+ nextActions: [
19357
+ {
19358
+ command: "adp plugin list --fields pluginId,name,pluginKind --format json",
19359
+ reason: "find a pluginKind=2 plugin, then read its tools"
19360
+ }
19361
+ ]
19362
+ });
19363
+ }
19364
+ return name;
19365
+ }
19366
+ if (!tools) {
19367
+ throw new DomainError("--tool-id needs cloud credentials to look up the tool name", "auth", "AuthFailure", {
19368
+ hint: "Pass --tool-name to skip the lookup, or run adp auth status and retry.",
19369
+ retryable: false,
19370
+ nextActions: [{ command: "adp auth status --format json", reason: "check the CAM credential used to describe the tool" }]
19371
+ });
19372
+ }
19373
+ const detail = await describePluginTool(tools, {
19374
+ pluginId: input.pluginId,
19375
+ toolId: input.toolId,
19376
+ ...input.spaceId ? { spaceId: input.spaceId } : {}
19377
+ });
19378
+ if (name && detail.name !== name) {
19379
+ throw new DomainError(
19380
+ `--tool-name ${name} does not match tool ${input.toolId} (${detail.name})`,
19381
+ "usage",
19382
+ "UsageError",
19383
+ { hint: "Omit one of the two flags, or pass the name adp plugin tool get prints.", retryable: false }
19384
+ );
19385
+ }
19386
+ if (!detail.name) {
19387
+ throw new DomainError(`Tool ${input.toolId} has no name`, "not_found", "PluginToolNotFound", { retryable: false });
19388
+ }
19389
+ return detail.name;
19390
+ }
19391
+ async function callPluginMcpTool(deps, input) {
19392
+ const url = pluginMcpUrl(input.pluginId);
19393
+ const toolName = await resolveToolName(deps.tools, input);
19394
+ const key = await prepareToolKey(deps.toolKeys, deps.store, input);
19395
+ const base = {
19396
+ dryRun: input.dryRun,
19397
+ url,
19398
+ pluginId: input.pluginId,
19399
+ toolName,
19400
+ ...input.toolId ? { toolId: input.toolId } : {},
19401
+ arguments: input.arguments,
19402
+ ...key.source ? { apiKeySource: key.source } : {},
19403
+ ...key.apiKeyId ? { apiKeyId: key.apiKeyId } : {},
19404
+ apiKeyCreated: key.created,
19405
+ wouldCreateKey: key.wouldCreate,
19406
+ hasKey: key.hasKey
19407
+ };
19408
+ if (input.dryRun || !key.value) {
19409
+ return base;
19410
+ }
19411
+ const output = await deps.mcp.callTool({
19412
+ pluginId: input.pluginId,
19413
+ apiKey: key.value,
19414
+ toolName,
19415
+ arguments: input.arguments,
19416
+ timeoutMs: input.timeoutMs
19417
+ });
19418
+ return { ...base, output };
19419
+ }
19420
+ async function listPluginMcpTools(deps, input) {
19421
+ const url = pluginMcpUrl(input.pluginId);
19422
+ const key = await prepareToolKey(deps.toolKeys, deps.store, input);
19423
+ const base = {
19424
+ dryRun: input.dryRun,
19425
+ url,
19426
+ pluginId: input.pluginId,
19427
+ ...key.source ? { apiKeySource: key.source } : {},
19428
+ ...key.apiKeyId ? { apiKeyId: key.apiKeyId } : {},
19429
+ apiKeyCreated: key.created,
19430
+ wouldCreateKey: key.wouldCreate,
19431
+ hasKey: key.hasKey
19432
+ };
19433
+ if (input.dryRun || !key.value) {
19434
+ return base;
19435
+ }
19436
+ const tools = await deps.mcp.listTools({ pluginId: input.pluginId, apiKey: key.value, timeoutMs: input.timeoutMs });
19437
+ return { ...base, tools };
19438
+ }
19439
+
19440
+ // src/interfaces/cli/commands/plugin/tool-call.ts
19441
+ var ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
19442
+ var DEFAULT_TIMEOUT_MS3 = 6e4;
19443
+ var MAX_TIMEOUT_MS = 6e5;
19444
+ var pluginIdFlag2 = {
19445
+ name: "plugin-id",
19446
+ type: "string",
19447
+ describe: "Plugin ID. The call goes to https://adp.cloud.tencent.com/plugin/mcp/<pluginId>/mcp",
19448
+ required: true
19449
+ };
19450
+ var apiKeyEnvFlag = {
19451
+ name: "api-key-env",
19452
+ type: "string",
19453
+ describe: "Environment variable holding the ADP API Key. Default: ADP_API_KEY, then ~/.adp/tool-key. The secret is never a flag."
19454
+ };
19455
+ var autoCreateFlag = {
19456
+ name: "auto-create-key",
19457
+ type: "boolean",
19458
+ describe: "When no tool key is available, create one, save it to ~/.adp/tool-key, and use it. The plaintext is not printed."
19459
+ };
19460
+ var timeoutFlag = {
19461
+ name: "timeout-ms",
19462
+ type: "number",
19463
+ default: DEFAULT_TIMEOUT_MS3,
19464
+ describe: "MCP HTTP timeout in milliseconds"
19465
+ };
19466
+ function dryRunMessage(result, preview, done) {
19467
+ if (!result.dryRun) {
19468
+ return done;
19469
+ }
19470
+ if (result.wouldCreateKey) {
19471
+ return `Would create a tool key and ${preview}.`;
19472
+ }
19473
+ if (!result.hasKey) {
19474
+ return `Would ${preview}. No tool key is loaded; create one or pass --auto-create-key before the real call.`;
19475
+ }
19476
+ return `Would ${preview}.`;
19477
+ }
19478
+ function keyNeeded(result) {
19479
+ if (!result.dryRun || result.hasKey || result.wouldCreateKey) {
19480
+ return void 0;
19481
+ }
19482
+ return [
19483
+ {
19484
+ command: "adp plugin key create --format json",
19485
+ reason: "save a tool key before the real call, or rerun with --auto-create-key"
19486
+ }
19487
+ ];
19488
+ }
19489
+ function storeOf2(ctx) {
19490
+ return ctx.deps.toolKeyStore ?? createToolKeyFileStore(adpToolKeyPath());
19491
+ }
19492
+ function mcpOf(ctx) {
19493
+ return ctx.deps.pluginMcp ?? createPluginMcpGateway();
19494
+ }
19495
+ function envName(value) {
19496
+ if (typeof value !== "string" || !value.trim()) {
19497
+ return void 0;
19498
+ }
19499
+ const name = value.trim();
19500
+ if (!ENV_NAME.test(name)) {
19501
+ throw usageError("--api-key-env must be an environment variable name", "Example: --api-key-env ADP_API_KEY");
19502
+ }
19503
+ return name;
19504
+ }
19505
+ function timeoutMs(value) {
19506
+ const parsed = value === void 0 ? DEFAULT_TIMEOUT_MS3 : Number(value);
19507
+ if (!Number.isInteger(parsed) || parsed < 1 || parsed > MAX_TIMEOUT_MS) {
19508
+ throw usageError("--timeout-ms must be an integer from 1 to 600000");
19509
+ }
19510
+ return parsed;
19511
+ }
19512
+ function toolArguments(args) {
19513
+ const input = args.input;
19514
+ const alias = args.args;
19515
+ if (typeof input === "string" && typeof alias === "string" && input !== alias) {
19516
+ throw usageError("Pass only one of --input or --args", "Both are the JSON object sent as the tool arguments");
19517
+ }
19518
+ const raw = typeof input === "string" ? input : typeof alias === "string" ? alias : void 0;
19519
+ if (raw === void 0 || !raw.trim()) {
19520
+ return {};
19521
+ }
19522
+ let parsed;
19523
+ try {
19524
+ parsed = JSON.parse(raw);
19525
+ } catch {
19526
+ throw usageError("--input must be a JSON object", `Example: --input '{"prompt":"a cat"}'`);
19527
+ }
19528
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
19529
+ throw usageError("--input must be a JSON object", "Arrays and scalars are rejected. Pass {} for no arguments.");
19530
+ }
19531
+ return parsed;
19532
+ }
19533
+ function pluginId2(args) {
19534
+ return assertResourceId(String(args["plugin-id"]), "plugin-id");
19535
+ }
19536
+ function registerPluginToolCallCommands(registry, ctx) {
18109
19537
  registry.define(
18110
19538
  createCommand({
18111
- command: "plugin auth check",
18112
- describe: "Check whether the caller has authorized OAuth plugins",
19539
+ command: "plugin tool list",
19540
+ describe: "List tools on a platform MCP plugin over Streamable HTTP (not the catalog from plugin get)",
18113
19541
  effects: "read_only",
18114
19542
  outputKind: "data",
18115
- flags: [
18116
- { name: "plugin-id", type: "string", describe: "Plugin ID (repeatable)", required: true, list: true },
18117
- { name: "credential-id", type: "string", describe: "OAuth credential ID, paired by position with --plugin-id" }
18118
- ],
18119
- examples: ["adp plugin auth check --plugin-id plg-1 --credential-id cred-1 --format json"],
18120
- async handler(args) {
18121
- const pluginIds = strings6(args["plugin-id"]).map((id) => assertResourceId(id, "plugin-id"));
18122
- const credentialIds = strings6(args["credential-id"]);
18123
- const gateways = await ctx.deps.resolveGateways(ctx.flags);
18124
- const items = await checkPluginAuth(gateways.pluginAccess, {
18125
- items: pluginIds.map((id, i) => ({ pluginId: id, ...credentialIds[i] ? { credentialId: credentialIds[i] } : {} }))
18126
- });
18127
- return { result: { items } };
19543
+ flags: [pluginIdFlag2, apiKeyEnvFlag, autoCreateFlag, timeoutFlag],
19544
+ examples: ["adp plugin tool list --plugin-id plg-1 --format json"],
19545
+ async handler(args) {
19546
+ const id = pluginId2(args);
19547
+ const name = envName(args["api-key-env"]);
19548
+ const store = storeOf2(ctx);
19549
+ const autoCreate = args["auto-create-key"] === true;
19550
+ const existing = await resolveToolKey({ env: ctx.deps.env, envName: name, store });
19551
+ const gateways = autoCreate && !existing && !ctx.flags.dryRun ? await ctx.deps.resolveGateways(ctx.flags) : void 0;
19552
+ const result = await listPluginMcpTools(
19553
+ { mcp: mcpOf(ctx), store, ...gateways ? { toolKeys: gateways.toolKey } : {} },
19554
+ {
19555
+ pluginId: id,
19556
+ env: ctx.deps.env,
19557
+ ...name ? { envName: name } : {},
19558
+ autoCreate,
19559
+ timeoutMs: timeoutMs(args["timeout-ms"]),
19560
+ dryRun: ctx.flags.dryRun
19561
+ }
19562
+ );
19563
+ return {
19564
+ result,
19565
+ message: dryRunMessage(result, `list tools at ${result.url}`, `Listed tools at ${result.url}.`),
19566
+ nextActions: keyNeeded(result)
19567
+ };
18128
19568
  }
18129
19569
  })
18130
19570
  );
18131
19571
  registry.define(
18132
19572
  createCommand({
18133
- command: "plugin oauth-url",
18134
- describe: "Create an OAuth consent link for an OAuth plugin credential; open it in a browser",
19573
+ command: "plugin tool call",
19574
+ describe: "Call one tool on a platform MCP plugin (pluginKind=2). Auth is a tool key, not a command-line secret.",
18135
19575
  effects: "non_idempotent",
18136
19576
  outputKind: "data",
18137
- flags: [pluginIdFlag, { name: "credential-id", type: "string", describe: "OAuth credential ID", required: true }],
18138
- examples: ["adp plugin oauth-url --plugin-id plg-1 --credential-id cred-1"],
19577
+ flags: [
19578
+ pluginIdFlag2,
19579
+ { name: "tool-name", type: "string", describe: "Tool name, such as SubmitKlingTextToVideoJob. Required unless --tool-id is set." },
19580
+ { name: "tool-id", type: "string", describe: "Tool ID from adp plugin get. Resolved to the tool name when --tool-name is omitted." },
19581
+ { name: "input", type: "string", describe: "JSON object of tool arguments. Default {}." },
19582
+ { name: "args", type: "string", describe: "Alias of --input" },
19583
+ { name: "space-id", type: "string", describe: "Space ID used only to resolve --tool-id (defaults to adp.jsonc)" },
19584
+ apiKeyEnvFlag,
19585
+ autoCreateFlag,
19586
+ timeoutFlag
19587
+ ],
19588
+ examples: [
19589
+ "adp plugin tool call --plugin-id plg-1 --tool-name SubmitJob --input '{}' --format json",
19590
+ "adp plugin tool call --plugin-id plg-1 --tool-id tool-1 --dry-run --format json",
19591
+ "adp plugin tool call --plugin-id plg-1 --tool-name SubmitJob --auto-create-key --format json"
19592
+ ],
18139
19593
  async handler(args) {
18140
- const input = { pluginId: pluginId(args), credentialId: assertResourceId(String(args["credential-id"]), "credential-id") };
18141
- if (ctx.flags.dryRun) {
18142
- return { result: { dryRun: true, ...input }, message: `Would create an OAuth consent link for credential ${input.credentialId}.` };
18143
- }
18144
- const gateways = await ctx.deps.resolveGateways(ctx.flags);
18145
- const { url } = await pluginOAuthUrl(gateways.pluginAccess, input);
18146
- return { result: { url }, message: url };
19594
+ const id = pluginId2(args);
19595
+ const toolName = typeof args["tool-name"] === "string" && args["tool-name"].trim() ? args["tool-name"].trim() : void 0;
19596
+ const toolId = typeof args["tool-id"] === "string" && args["tool-id"].trim() ? assertResourceId(args["tool-id"].trim(), "tool-id") : void 0;
19597
+ const name = envName(args["api-key-env"]);
19598
+ const store = storeOf2(ctx);
19599
+ const autoCreate = args["auto-create-key"] === true;
19600
+ const spaceId = await optionalSpaceId(ctx, args);
19601
+ const existing = await resolveToolKey({ env: ctx.deps.env, envName: name, store });
19602
+ const needCloud = Boolean(toolId) || autoCreate && !existing && !ctx.flags.dryRun;
19603
+ const gateways = needCloud ? await ctx.deps.resolveGateways(ctx.flags) : void 0;
19604
+ const result = await callPluginMcpTool(
19605
+ {
19606
+ mcp: mcpOf(ctx),
19607
+ store,
19608
+ ...gateways ? { tools: gateways.pluginAccess, toolKeys: gateways.toolKey } : {}
19609
+ },
19610
+ {
19611
+ pluginId: id,
19612
+ ...toolName ? { toolName } : {},
19613
+ ...toolId ? { toolId } : {},
19614
+ ...spaceId ? { spaceId: assertResourceId(spaceId, "space-id") } : {},
19615
+ arguments: toolArguments(args),
19616
+ env: ctx.deps.env,
19617
+ ...name ? { envName: name } : {},
19618
+ autoCreate,
19619
+ timeoutMs: timeoutMs(args["timeout-ms"]),
19620
+ dryRun: ctx.flags.dryRun
19621
+ }
19622
+ );
19623
+ return {
19624
+ result,
19625
+ message: dryRunMessage(result, `call ${result.toolName} at ${result.url}`, `Called ${result.toolName}.`),
19626
+ nextActions: keyNeeded(result) ?? (!result.dryRun && result.toolName.startsWith("Submit") ? [
19627
+ {
19628
+ command: `adp plugin get --plugin-id ${id} --format json`,
19629
+ reason: "find the matching Query tool and call it with the task id in output"
19630
+ }
19631
+ ] : void 0)
19632
+ };
18147
19633
  }
18148
19634
  })
18149
19635
  );
@@ -18685,7 +20171,7 @@ function registerEnvCommands(registry, ctx) {
18685
20171
  }
18686
20172
 
18687
20173
  // src/interfaces/cli/commands/skills-install.ts
18688
- import { homedir as homedir5 } from "os";
20174
+ import { homedir as homedir6 } from "os";
18689
20175
 
18690
20176
  // src/application/companion-skills/discover-companion-skills.ts
18691
20177
  async function discoverCompanionSkills(pack, input) {
@@ -18830,7 +20316,7 @@ function registerCompanionSkillCommands(registry, ctx) {
18830
20316
  await requireYes(ctx, `install companion skills (${scope})`);
18831
20317
  }
18832
20318
  const result = await installCompanionSkills(ctx.deps.companionSkills, {
18833
- homeDir: homedir5(),
20319
+ homeDir: homedir6(),
18834
20320
  cwd: ctx.flags.cwd,
18835
20321
  scope,
18836
20322
  agents: typeof args.agent === "string" ? args.agent : void 0,
@@ -18862,7 +20348,7 @@ function registerCompanionSkillCommands(registry, ctx) {
18862
20348
  ],
18863
20349
  async handler() {
18864
20350
  const result = await discoverCompanionSkills(ctx.deps.companionSkills, {
18865
- homeDir: homedir5(),
20351
+ homeDir: homedir6(),
18866
20352
  cwd: ctx.flags.cwd
18867
20353
  });
18868
20354
  if (isManagedInstall(await detectInstallSource(ctx.deps))) {
@@ -22071,7 +23557,7 @@ async function modifyChannel(deps, input) {
22071
23557
  if (paths.length === 0) {
22072
23558
  throw usageError("Nothing to update", `Pass --description or --spec-json. Updatable paths: ${allowed.join(", ")}`);
22073
23559
  }
22074
- const rejected = paths.filter((path14) => !allowed.includes(path14));
23560
+ const rejected = paths.filter((path18) => !allowed.includes(path18));
22075
23561
  if (rejected.length > 0) {
22076
23562
  throw usageError(
22077
23563
  `ModifyChannel cannot change ${rejected.join(", ")}`,
@@ -22357,7 +23843,7 @@ function registerModelCommands(registry, ctx) {
22357
23843
  }
22358
23844
 
22359
23845
  // src/interfaces/cli/commands/workflow/index.ts
22360
- import { readFile as readFile13 } from "fs/promises";
23846
+ import { readFile as readFile15 } from "fs/promises";
22361
23847
 
22362
23848
  // src/domain/workflow/canvas.ts
22363
23849
  var WORKFLOW_NODE_TYPES = [
@@ -22615,17 +24101,17 @@ function checkReferences(nodes, problems) {
22615
24101
  const ref3 = isRecord(value.Reference) ? value.Reference : !root && "JsonPath" in value && text4(value.NodeID) ? value : void 0;
22616
24102
  if (ref3 && text4(ref3.NodeID)) {
22617
24103
  const target = byId.get(text4(ref3.NodeID));
22618
- const path14 = text4(ref3.JsonPath);
24104
+ const path18 = text4(ref3.JsonPath);
22619
24105
  if (!target) {
22620
24106
  problems.push(
22621
24107
  `node ${id}: "${here}" references node "${text4(ref3.NodeID)}", which does not exist; point Reference.NodeID at one of ${[...byId.keys()].join(", ")}`
22622
24108
  );
22623
24109
  } else {
22624
- const field = /^Output\.([^.[\]]+)/.exec(path14)?.[1];
24110
+ const field = /^Output\.([^.[\]]+)/.exec(path18)?.[1];
22625
24111
  const declared = declaredOutputs(target);
22626
24112
  if (field && declared && !declared.includes(field)) {
22627
24113
  problems.push(
22628
- `node ${id}: "${here}" reads ${text4(ref3.NodeID)}.${path14}, but ${text4(ref3.NodeID)} (${text4(target.NodeType)}) declares only ${declared.map((name) => `Output.${name}`).join(", ")}; rename the reference or the ${text4(target.NodeType) === "PARAMETER_EXTRACTOR" ? "parameter" : "output"}`
24114
+ `node ${id}: "${here}" reads ${text4(ref3.NodeID)}.${path18}, but ${text4(ref3.NodeID)} (${text4(target.NodeType)}) declares only ${declared.map((name) => `Output.${name}`).join(", ")}; rename the reference or the ${text4(target.NodeType) === "PARAMETER_EXTRACTOR" ? "parameter" : "output"}`
22629
24115
  );
22630
24116
  }
22631
24117
  }
@@ -22883,18 +24369,18 @@ var COMPARISON_OPERATORS = /* @__PURE__ */ new Set([
22883
24369
  "NOT_SET"
22884
24370
  ]);
22885
24371
  var NUMERIC_OPERATORS = /* @__PURE__ */ new Set(["LT", "LE", "GT", "GE"]);
22886
- function checkLogical(logical, path14, fixes, problems) {
24372
+ function checkLogical(logical, path18, fixes, problems) {
22887
24373
  const compound = Array.isArray(logical.Compound) ? logical.Compound.filter(isRecord) : [];
22888
24374
  const combined = logical.LogicalOperator === "AND" || logical.LogicalOperator === "OR";
22889
24375
  if (combined) {
22890
24376
  if (compound.length === 0) {
22891
- problems.push(`${path14}: LogicalOperator ${text4(logical.LogicalOperator)} needs conditions in Compound[]`);
24377
+ problems.push(`${path18}: LogicalOperator ${text4(logical.LogicalOperator)} needs conditions in Compound[]`);
22892
24378
  }
22893
24379
  if ("Comparison" in logical) {
22894
24380
  delete logical.Comparison;
22895
- fixes.push(`removed ${path14}.Comparison (an ${text4(logical.LogicalOperator)} group uses Compound only)`);
24381
+ fixes.push(`removed ${path18}.Comparison (an ${text4(logical.LogicalOperator)} group uses Compound only)`);
22896
24382
  }
22897
- compound.forEach((child, index) => checkLogical(child, `${path14}.Compound[${index}]`, fixes, problems));
24383
+ compound.forEach((child, index) => checkLogical(child, `${path18}.Compound[${index}]`, fixes, problems));
22898
24384
  return;
22899
24385
  }
22900
24386
  const comparison = isRecord(logical.Comparison) ? logical.Comparison : void 0;
@@ -22904,10 +24390,10 @@ function checkLogical(logical, path14, fixes, problems) {
22904
24390
  const operator = text4(comparison.Operator);
22905
24391
  if (!COMPARISON_OPERATORS.has(operator)) {
22906
24392
  problems.push(
22907
- `${path14}.Comparison: Operator "${operator || "none"}" is not supported; use one of ${[...COMPARISON_OPERATORS].join(", ")}`
24393
+ `${path18}.Comparison: Operator "${operator || "none"}" is not supported; use one of ${[...COMPARISON_OPERATORS].join(", ")}`
22908
24394
  );
22909
24395
  } else if (NUMERIC_OPERATORS.has(operator) && !["INT", "FLOAT"].includes(text4(comparison.LeftType))) {
22910
- problems.push(`${path14}.Comparison: ${operator} compares numbers; LeftType must be INT or FLOAT, got ${text4(comparison.LeftType) || "none"}`);
24396
+ problems.push(`${path18}.Comparison: ${operator} compares numbers; LeftType must be INT or FLOAT, got ${text4(comparison.LeftType) || "none"}`);
22911
24397
  }
22912
24398
  }
22913
24399
  function dynamicCard(node) {
@@ -23049,9 +24535,9 @@ function rewriteEndReferences(node, fixes) {
23049
24535
  const value = isRecord(property.Value) ? property.Value : void 0;
23050
24536
  if (value?.InputType === "INPUT_REF" && isRecord(value.InputRef)) {
23051
24537
  const ref3 = value.InputRef;
23052
- const path14 = text4(ref3.JsonPath) || `Output.${text4(ref3.Name)}`;
23053
- property.Value = { InputType: "REFERENCE_OUTPUT", Reference: { NodeID: text4(ref3.NodeID), JsonPath: path14 } };
23054
- fixes.push(`END ${text4(node.NodeID)}: output ${text4(property.Title)} INPUT_REF -> REFERENCE_OUTPUT ${path14}`);
24538
+ const path18 = text4(ref3.JsonPath) || `Output.${text4(ref3.Name)}`;
24539
+ property.Value = { InputType: "REFERENCE_OUTPUT", Reference: { NodeID: text4(ref3.NodeID), JsonPath: path18 } };
24540
+ fixes.push(`END ${text4(node.NodeID)}: output ${text4(property.Title)} INPUT_REF -> REFERENCE_OUTPUT ${path18}`);
23055
24541
  }
23056
24542
  visit(property.Properties);
23057
24543
  }
@@ -23254,7 +24740,7 @@ function recheckAction(input) {
23254
24740
  };
23255
24741
  }
23256
24742
  return {
23257
- command: rerunOf(input, "dry-run") ?? `adp workflow deploy --app-id ${input.appId}${workflow} --canvas-file ${shellWord4(input.canvasFile)}${input.bind === false ? " --no-bind" : input.bind ? " --bind" : ""} --dry-run --format json`,
24743
+ command: rerunOf(input, "dry-run") ?? `adp workflow deploy --app-id ${input.appId}${workflow} --canvas-file ${shellWord5(input.canvasFile)}${input.bind === false ? " --no-bind" : input.bind ? " --bind" : ""} --dry-run --format json`,
23258
24744
  reason: `after editing ${input.canvasFile} to fix the problems above, re-run this local check (no cloud write)`,
23259
24745
  needs_edit: { file: input.canvasFile }
23260
24746
  };
@@ -23347,7 +24833,7 @@ function platformRejectionActions(input, canvas, errors) {
23347
24833
  const workflow = input.workflowId ? ` --workflow-id ${input.workflowId}` : "";
23348
24834
  actions.push(
23349
24835
  input.canvasFile ? {
23350
- command: rerunOf(input, "yes") ?? `adp workflow deploy --app-id ${input.appId}${workflow} --canvas-file ${shellWord4(input.canvasFile)}${input.bind === false ? " --no-bind" : input.bind ? " --bind" : ""} --yes --format json`,
24836
+ command: rerunOf(input, "yes") ?? `adp workflow deploy --app-id ${input.appId}${workflow} --canvas-file ${shellWord5(input.canvasFile)}${input.bind === false ? " --no-bind" : input.bind ? " --bind" : ""} --yes --format json`,
23351
24837
  reason: `after editing ${named ? `node ${named}` : "the canvas"} in ${input.canvasFile} as the platform message says, deploy again; this writes the workflow draft (the local --dry-run check already passes)`,
23352
24838
  needs_edit: { file: input.canvasFile }
23353
24839
  } : recheckAction(input)
@@ -23357,7 +24843,7 @@ function platformRejectionActions(input, canvas, errors) {
23357
24843
  function rerunOf(input, mode2) {
23358
24844
  return input.rerun?.(mode2);
23359
24845
  }
23360
- function shellWord4(value) {
24846
+ function shellWord5(value) {
23361
24847
  return /^[\w@%+=:,./-]+$/.test(value) ? value : `'${value.replace(/'/g, "'\\''")}'`;
23362
24848
  }
23363
24849
  async function withShellCleanup(error, gateway, appId3, workflowId2, name) {
@@ -23587,13 +25073,13 @@ function signatureExpiry2(url) {
23587
25073
  }
23588
25074
 
23589
25075
  // src/infrastructure/fs/workflow-canvas-extractor.ts
23590
- import { readFile as readFile12, writeFile as writeFile8 } from "fs/promises";
25076
+ import { readFile as readFile14, writeFile as writeFile6 } from "fs/promises";
23591
25077
  import { basename, dirname, join } from "path";
23592
25078
  var CANVAS_ENTRY = /(?:^|\/)([^/]+_workflow\.json)$/;
23593
25079
  function createWorkflowCanvasExtractor() {
23594
25080
  return {
23595
25081
  async extractCanvases(zipPath) {
23596
- const zip = await readFile12(zipPath);
25082
+ const zip = await readFile14(zipPath);
23597
25083
  const written = [];
23598
25084
  for (const entry of listEntries(zip)) {
23599
25085
  const match = CANVAS_ENTRY.exec(entry.name);
@@ -23602,7 +25088,7 @@ function createWorkflowCanvasExtractor() {
23602
25088
  continue;
23603
25089
  }
23604
25090
  const target = join(dirname(zipPath), basename(match[1]));
23605
- await writeFile8(target, body);
25091
+ await writeFile6(target, body);
23606
25092
  written.push(target);
23607
25093
  }
23608
25094
  return written;
@@ -23913,7 +25399,7 @@ function parseTimeout(args) {
23913
25399
  }
23914
25400
  return timeout * 1e3;
23915
25401
  }
23916
- function shellWord5(value) {
25402
+ function shellWord6(value) {
23917
25403
  return /^[\w@%+=:,./-]+$/.test(value) ? value : `'${value.replace(/'/g, "'\\''")}'`;
23918
25404
  }
23919
25405
  function waitingActions(appId3, workflowRunId, question, options = []) {
@@ -23934,7 +25420,7 @@ function waitingActions(appId3, workflowRunId, question, options = []) {
23934
25420
  }
23935
25421
  return [
23936
25422
  {
23937
- command: `adp workflow run resume --app-id ${appId3} --run-id ${workflowRunId} --query ${shellWord5(options[0])} --format json`,
25423
+ command: `adp workflow run resume --app-id ${appId3} --run-id ${workflowRunId} --query ${shellWord6(options[0])} --format json`,
23938
25424
  reason: `${ask}options: ${options.join(" / ")}; this picks the first, change --query to choose another`
23939
25425
  },
23940
25426
  get
@@ -23985,7 +25471,7 @@ function registerWorkflowRunCommands(registry, ctx) {
23985
25471
  message: `Would start a workflow run of app ${id}.`
23986
25472
  };
23987
25473
  }
23988
- const timeoutMs = parseTimeout(args);
25474
+ const timeoutMs2 = parseTimeout(args);
23989
25475
  const gateways = await ctx.deps.resolveGateways(ctx.flags);
23990
25476
  const result = await runWorkflow(
23991
25477
  { runs: gateways.workflowRun, latestMessage: (ref3) => gateways.workflowRunControl.latestMessage(ref3) },
@@ -23996,7 +25482,7 @@ function registerWorkflowRunCommands(registry, ctx) {
23996
25482
  variables,
23997
25483
  visitorId: typeof args["visitor-id"] === "string" ? args["visitor-id"] : void 0,
23998
25484
  wait: args.wait !== false,
23999
- timeoutMs
25485
+ timeoutMs: timeoutMs2
24000
25486
  }
24001
25487
  );
24002
25488
  if (result.status === "submitted") {
@@ -24168,7 +25654,7 @@ function registerWorkflowRunCommands(registry, ctx) {
24168
25654
  if (ctx.flags.dryRun) {
24169
25655
  return { result: { dryRun: true, ...input }, message: `Would resume workflow run ${input.workflowRunId}.` };
24170
25656
  }
24171
- const timeoutMs = parseTimeout(args);
25657
+ const timeoutMs2 = parseTimeout(args);
24172
25658
  const gateways = await ctx.deps.resolveGateways(ctx.flags);
24173
25659
  if (args.wait !== false) {
24174
25660
  return waitedOutput(
@@ -24176,7 +25662,7 @@ function registerWorkflowRunCommands(registry, ctx) {
24176
25662
  gateways.workflowRunControl,
24177
25663
  await resumeWorkflowRunAndWait(
24178
25664
  { runs: gateways.workflowRun, control: gateways.workflowRunControl },
24179
- { ...input, timeoutMs }
25665
+ { ...input, timeoutMs: timeoutMs2 }
24180
25666
  )
24181
25667
  )
24182
25668
  );
@@ -24217,22 +25703,22 @@ function registerWorkflowRunCommands(registry, ctx) {
24217
25703
  }
24218
25704
 
24219
25705
  // src/interfaces/cli/commands/workflow/index.ts
24220
- async function readCanvas(path14) {
25706
+ async function readCanvas(path18) {
24221
25707
  let raw;
24222
25708
  try {
24223
- raw = await readFile13(path14, "utf8");
25709
+ raw = await readFile15(path18, "utf8");
24224
25710
  } catch {
24225
- throw usageError(`Cannot read canvas file ${path14}`, "Pass a readable JSON file via --canvas-file");
25711
+ throw usageError(`Cannot read canvas file ${path18}`, "Pass a readable JSON file via --canvas-file");
24226
25712
  }
24227
25713
  try {
24228
25714
  const parsed = JSON.parse(raw);
24229
25715
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
24230
- throw usageError(`Canvas file ${path14} must contain a JSON object`, "Pass a workflow canvas object");
25716
+ throw usageError(`Canvas file ${path18} must contain a JSON object`, "Pass a workflow canvas object");
24231
25717
  }
24232
25718
  return parsed;
24233
25719
  } catch (error) {
24234
25720
  if (error instanceof SyntaxError) {
24235
- throw usageError(`Canvas file ${path14} is not valid JSON`, "Fix the canvas JSON and retry");
25721
+ throw usageError(`Canvas file ${path18} is not valid JSON`, "Fix the canvas JSON and retry");
24236
25722
  }
24237
25723
  throw error;
24238
25724
  }
@@ -25272,7 +26758,7 @@ function registerMemoryCommands(registry, ctx) {
25272
26758
  examples: ["adp memory export --space-id sp-1 --store-id rms_abc --output ./memory.csv"],
25273
26759
  async handler(args) {
25274
26760
  const ref3 = await storeRef(ctx, args);
25275
- const output = text5(args, "output") ? canonicalizeLocalPath(String(args.output), ctx.flags.cwd) : void 0;
26761
+ const output = typeof args.output === "string" ? canonicalizeLocalPath(args.output, ctx.flags.cwd) : void 0;
25276
26762
  if (ctx.flags.dryRun) {
25277
26763
  return { result: { dryRun: true, ...ref3, output }, message: `Would export memory store ${ref3.storeId}.` };
25278
26764
  }
@@ -25491,11 +26977,11 @@ async function credentialRef(ctx, args) {
25491
26977
  };
25492
26978
  }
25493
26979
  async function readToken(ctx, args, required3) {
25494
- const envName = text6(args, "token-env");
25495
- if (envName) {
25496
- const value = ctx.deps.env[envName]?.trim();
26980
+ const envName2 = text6(args, "token-env");
26981
+ if (envName2) {
26982
+ const value = ctx.deps.env[envName2]?.trim();
25497
26983
  if (!value) {
25498
- throw new DomainError(`Environment variable ${envName} is empty or unset`, "usage", "UsageError");
26984
+ throw new DomainError(`Environment variable ${envName2} is empty or unset`, "usage", "UsageError");
25499
26985
  }
25500
26986
  return value;
25501
26987
  }
@@ -25773,7 +27259,7 @@ ${auth.authorizationUrl}`);
25773
27259
  }
25774
27260
 
25775
27261
  // src/interfaces/cli/commands/timer/index.ts
25776
- import { readFile as readFile14 } from "fs/promises";
27262
+ import { readFile as readFile16 } from "fs/promises";
25777
27263
 
25778
27264
  // src/application/timer/timer-use-cases.ts
25779
27265
  var RUN_POLL_MS = 3e3;
@@ -25905,7 +27391,7 @@ async function prompt(args) {
25905
27391
  return inline;
25906
27392
  }
25907
27393
  try {
25908
- const content = (await readFile14(file, "utf8")).trim();
27394
+ const content = (await readFile16(file, "utf8")).trim();
25909
27395
  if (!content) {
25910
27396
  throw new Error("empty");
25911
27397
  }
@@ -26934,49 +28420,49 @@ async function deleteVolume(gateway, input) {
26934
28420
  return { volumeId: volume.volumeId, name: volume.name, deleted: true, referenceApps: volume.referenceApps };
26935
28421
  }
26936
28422
  function listVolumeEntries(gateway, input) {
26937
- const path14 = normalizeVolumePath(input.path, { allowRoot: true });
26938
- return guard10(gateway.listEntries({ ...input, path: path14 }), { ...input, path: path14 });
28423
+ const path18 = normalizeVolumePath(input.path, { allowRoot: true });
28424
+ return guard10(gateway.listEntries({ ...input, path: path18 }), { ...input, path: path18 });
26939
28425
  }
26940
28426
  function makeVolumeDirectory(gateway, input) {
26941
- const path14 = normalizeVolumePath(input.path);
26942
- return guard10(gateway.mkdir({ ...input, path: path14 }), { ...input, path: path14 });
28427
+ const path18 = normalizeVolumePath(input.path);
28428
+ return guard10(gateway.mkdir({ ...input, path: path18 }), { ...input, path: path18 });
26943
28429
  }
26944
28430
  async function renameVolumeFile(gateway, input) {
26945
- const path14 = normalizeVolumePath(input.path);
28431
+ const path18 = normalizeVolumePath(input.path);
26946
28432
  const name = input.name.trim();
26947
28433
  if (!name || name.includes("/") || name === "." || name === "..") {
26948
28434
  throw new DomainError("--name is a single file name; files cannot move between directories", "usage", "UsageError");
26949
28435
  }
26950
- const renamed = await guard10(gateway.rename({ ...input, path: path14, name }), { ...input, path: path14, operation: "rename" });
28436
+ const renamed = await guard10(gateway.rename({ ...input, path: path18, name }), { ...input, path: path18, operation: "rename" });
26951
28437
  if (renamed.sizeBytes > 0) {
26952
28438
  return renamed;
26953
28439
  }
26954
- const finalPath = renamed.path || [...path14.split("/").slice(0, -1), name].join("/");
28440
+ const finalPath = renamed.path || [...path18.split("/").slice(0, -1), name].join("/");
26955
28441
  const parent = finalPath.split("/").slice(0, -1).join("/");
26956
28442
  const listing = await gateway.listEntries({ spaceId: input.spaceId, volumeId: input.volumeId, path: parent, query: baseName(finalPath), limit: 50 }).catch(() => void 0);
26957
28443
  const entry = listing?.items.find((item) => item.path === finalPath);
26958
28444
  return { path: finalPath, sizeBytes: entry?.sizeBytes ?? renamed.sizeBytes };
26959
28445
  }
26960
28446
  async function deleteVolumeEntry(gateway, input) {
26961
- const path14 = normalizeVolumePath(input.path);
28447
+ const path18 = normalizeVolumePath(input.path);
26962
28448
  const type = input.recursive ? "directory" : "file";
26963
- await guard10(gateway.deleteEntry({ ...input, path: path14, type }), { ...input, path: path14, operation: "delete" });
26964
- return { volumeId: input.volumeId, path: path14, type, deleted: true };
28449
+ await guard10(gateway.deleteEntry({ ...input, path: path18, type }), { ...input, path: path18, operation: "delete" });
28450
+ return { volumeId: input.volumeId, path: path18, type, deleted: true };
26965
28451
  }
26966
28452
  function uploadToVolume(gateway, input) {
26967
28453
  const local = baseName(input.localPath.replace(/\\/g, "/"));
26968
28454
  const raw = input.to === void 0 || input.to === "" ? local : input.to.endsWith("/") ? `${input.to}${local}` : input.to;
26969
- const path14 = normalizeVolumePath(raw);
26970
- return guard10(gateway.upload({ ...input, path: path14 }), { ...input, path: path14 });
28455
+ const path18 = normalizeVolumePath(raw);
28456
+ return guard10(gateway.upload({ ...input, path: path18 }), { ...input, path: path18 });
26971
28457
  }
26972
28458
  async function downloadFromVolume(gateway, input) {
26973
- const path14 = normalizeVolumePath(input.path);
26974
- const result = await guard10(gateway.download({ ...input, path: path14 }), { ...input, path: path14 });
26975
- return { volumeId: input.volumeId, path: path14, output: input.localPath, ...result };
28459
+ const path18 = normalizeVolumePath(input.path);
28460
+ const result = await guard10(gateway.download({ ...input, path: path18 }), { ...input, path: path18 });
28461
+ return { volumeId: input.volumeId, path: path18, output: input.localPath, ...result };
26976
28462
  }
26977
28463
  function volumePreviewUrl(gateway, input) {
26978
- const path14 = normalizeVolumePath(input.path);
26979
- return guard10(gateway.previewUrl({ ...input, path: path14 }), { ...input, path: path14 });
28464
+ const path18 = normalizeVolumePath(input.path);
28465
+ return guard10(gateway.previewUrl({ ...input, path: path18 }), { ...input, path: path18 });
26980
28466
  }
26981
28467
 
26982
28468
  // src/interfaces/cli/commands/volume/index.ts
@@ -27103,14 +28589,14 @@ function registerVolumeCommands(registry, ctx) {
27103
28589
  examples: ["adp volume ls --space-id sp-1 --volume-id v-1 --path docs --format json"],
27104
28590
  async handler(args) {
27105
28591
  const target = await ref2(ctx, args);
27106
- const path14 = text10(args.path) ?? "";
28592
+ const path18 = text10(args.path) ?? "";
27107
28593
  const query = text10(args.query);
27108
28594
  const gateways = await ctx.deps.resolveGateways(ctx.flags);
27109
- const page3 = await listVolumeEntries(gateways.volume, { ...target, path: path14, query, limit: ctx.flags.limit, page: ctx.flags.page });
28595
+ const page3 = await listVolumeEntries(gateways.volume, { ...target, path: path18, query, limit: ctx.flags.limit, page: ctx.flags.page });
27110
28596
  return applyListShape(
27111
28597
  ctx,
27112
28598
  pageResult(page3, { limit: ctx.flags.limit, page: ctx.flags.page }),
27113
- `adp volume ls --space-id ${target.spaceId} --volume-id ${target.volumeId}${path14 ? ` --path ${JSON.stringify(path14)}` : ""}`
28599
+ `adp volume ls --space-id ${target.spaceId} --volume-id ${target.volumeId}${path18 ? ` --path ${JSON.stringify(path18)}` : ""}`
27114
28600
  );
27115
28601
  }
27116
28602
  })
@@ -27937,7 +29423,7 @@ function registerSynonymCommands(registry, ctx) {
27937
29423
  examples: ["adp synonym export --app-id app-1 --output ./synonyms.xlsx"],
27938
29424
  async handler(args) {
27939
29425
  const appId3 = await resolveAppId(ctx, args, WHY_APP7);
27940
- const output = text11(args, "output") ? canonicalizeLocalPath(String(args.output), ctx.flags.cwd) : void 0;
29426
+ const output = typeof args.output === "string" ? canonicalizeLocalPath(args.output, ctx.flags.cwd) : void 0;
27941
29427
  const input = { appId: appId3, query: text11(args, "query"), categoryId: categoryId(args), unreleasedOnly: args.unreleased === true };
27942
29428
  if (ctx.flags.dryRun) {
27943
29429
  return { result: { dryRun: true, ...input, output }, message: "Would export synonyms." };
@@ -28166,7 +29652,7 @@ function registerUnsatisfiedCommands(registry, ctx) {
28166
29652
  examples: ["adp unsatisfied export --app-id app-1 --status pending --output ./unsatisfied.xlsx"],
28167
29653
  async handler(args) {
28168
29654
  const appId3 = await resolveAppId(ctx, args, WHY_APP8);
28169
- const output = text12(args, "output") ? canonicalizeLocalPath(String(args.output), ctx.flags.cwd) : void 0;
29655
+ const output = typeof args.output === "string" ? canonicalizeLocalPath(args.output, ctx.flags.cwd) : void 0;
28170
29656
  const input = { appId: appId3, ...filters5(args), replyIds: list4(args, "reply-id").map((id) => assertResourceId(id, "reply-id")) };
28171
29657
  if (ctx.flags.dryRun) {
28172
29658
  return { result: { dryRun: true, ...input, output }, message: "Would export unsatisfied replies." };
@@ -29009,6 +30495,8 @@ async function runCli(options) {
29009
30495
  registerAppOpsCommands(registry, ctx);
29010
30496
  registerPluginCommands(registry, ctx);
29011
30497
  registerPluginAccessCommands(registry, ctx);
30498
+ registerPluginKeyCommands(registry, ctx);
30499
+ registerPluginToolCallCommands(registry, ctx);
29012
30500
  registerSkillCommands(registry, ctx);
29013
30501
  registerSkillLifecycleCommands(registry, ctx);
29014
30502
  registerEnvCommands(registry, ctx);