@thinkingai/ae-cli 6.0.20 → 6.0.22

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.
Files changed (70) hide show
  1. package/README.md +1 -4
  2. package/README.zh.md +1 -4
  3. package/dist/{auth-DYC2BGLM.js → auth-I2G7OAU2.js} +2 -4
  4. package/dist/{auth-GHBMJ5ZR.js → auth-RDN6D5HU.js} +32 -104
  5. package/dist/{chunk-5OC7CYCG.js → chunk-2W7HULUD.js} +14 -14
  6. package/dist/chunk-4EUZRJPO.js +163 -0
  7. package/dist/{chunk-DMMEPCST.js → chunk-5FBLRSMU.js} +25 -115
  8. package/dist/{chunk-BJX3GXWC.js → chunk-E34XH4GW.js} +2 -2
  9. package/dist/chunk-FPE5LGVU.js +105 -0
  10. package/dist/{chunk-I5CRVM2S.js → chunk-H52CSLJW.js} +44 -17
  11. package/dist/{chunk-RSHMZ3LG.js → chunk-U6QPKCN3.js} +6 -20
  12. package/dist/{chunk-6W4QOZAL.js → chunk-WPXHCP4A.js} +23 -13
  13. package/dist/{chunk-PN3OS34E.js → chunk-XE2PI5DS.js} +1 -1
  14. package/dist/{chunk-2DUEAXV3.js → chunk-YACCPUNQ.js} +1 -1
  15. package/dist/cli-token-6AGEDJF4.js +14 -0
  16. package/dist/{client-2UJ63MVQ.js → client-VTJMUZ4O.js} +3 -3
  17. package/dist/{config-NCUGK6NS.js → config-2SDTLGOC.js} +3 -3
  18. package/dist/index.js +309 -197
  19. package/dist/metadata-JZ2OTWBK.js +420 -0
  20. package/dist/{model-SJSOZTRS.js → model-ZYMRN4VS.js} +2 -2
  21. package/dist/{raw-7NVWSVJP.js → raw-NGYHAL3F.js} +4 -4
  22. package/dist/{sync-GRVO4K3U.js → sync-JVLX6TGW.js} +3 -3
  23. package/dist/{te-agent-CYZUC4US.js → te-agent-QPZTLHPH.js} +2 -2
  24. package/dist/{te-analysis-HJ22TUAA.js → te-analysis-WWX3Z6RP.js} +46 -24
  25. package/dist/{te-audience-MYDE5WEH.js → te-audience-UXGHUVBB.js} +27 -18
  26. package/dist/{te-common-WNCZB7R7.js → te-common-7XXH73MF.js} +5 -4
  27. package/dist/{te-community-JEEFXPB3.js → te-community-QKE67HR2.js} +5 -4
  28. package/dist/{te-dataops-ZTS7XMXM.js → te-dataops-6W7NNYST.js} +4 -5
  29. package/dist/{te-engage-4XHCTS6I.js → te-engage-NCFAQFX3.js} +5 -4
  30. package/dist/{te-kb-OWM3RQRH.js → te-kb-K5BQYUCM.js} +4 -5
  31. package/dist/{te-meta-JVO4QJBH.js → te-meta-LJZBOSMO.js} +26 -19
  32. package/dist/{te-team-YY24VORI.js → te-team-DHK5R7MV.js} +5 -6
  33. package/package.json +3 -2
  34. package/skills/ae-analysis/SKILL.md +9 -2
  35. package/skills/ae-analysis/references/build_attribution_analysis_qp.md +2 -0
  36. package/skills/ae-analysis/references/build_cluster_definition.md +2 -0
  37. package/skills/ae-analysis/references/build_distribution_analysis_qp.md +2 -0
  38. package/skills/ae-analysis/references/build_event_analysis_qp.md +2 -0
  39. package/skills/ae-analysis/references/build_funnel_analysis_qp.md +2 -0
  40. package/skills/ae-analysis/references/build_heat_map_analysis_qp.md +2 -0
  41. package/skills/ae-analysis/references/build_interval_analysis_qp.md +2 -0
  42. package/skills/ae-analysis/references/build_path_analysis_qp.md +2 -0
  43. package/skills/ae-analysis/references/build_prop_analysis_qp.md +2 -0
  44. package/skills/ae-analysis/references/build_rank_list_analysis_qp.md +2 -0
  45. package/skills/ae-analysis/references/build_retention_analysis_qp.md +2 -0
  46. package/skills/ae-analysis/references/build_tag_definition.md +2 -0
  47. package/skills/ae-analysis/references/list_clusters.md +3 -1
  48. package/skills/ae-analysis/references/list_events.md +5 -2
  49. package/skills/ae-analysis/references/list_metrics.md +4 -2
  50. package/skills/ae-analysis/references/list_properties.md +4 -2
  51. package/skills/ae-analysis/references/list_tags.md +3 -1
  52. package/skills/ae-generate-tracking-code/SKILL.md +68 -31
  53. package/skills/ae-generate-tracking-code/references/snippet-delivery.md +26 -1
  54. package/skills/ae-generate-tracking-code/references/te-api.md +47 -164
  55. package/skills/ae-generate-tracking-plan/SKILL.md +109 -32
  56. package/skills/ae-generate-tracking-plan/references/te-api.md +57 -27
  57. package/skills/ae-metadata/SKILL.md +90 -0
  58. package/skills/ae-metadata/references/metadata_data_table_csv_delete.md +15 -0
  59. package/skills/ae-metadata/references/metadata_data_table_csv_write.md +29 -0
  60. package/skills/ae-metadata/references/metadata_data_table_download.md +17 -0
  61. package/skills/ae-metadata/references/metadata_data_table_get.md +24 -0
  62. package/skills/ae-metadata/references/metadata_data_table_list.md +27 -0
  63. package/skills/ae-metadata/references/metadata_data_table_property_bindings_update.md +17 -0
  64. package/skills/ae-metadata/references/metadata_data_table_sql_delete.md +15 -0
  65. package/skills/ae-metadata/references/metadata_data_table_sql_write.md +31 -0
  66. package/skills/ae-metadata/references/metadata_event_get.md +52 -0
  67. package/skills/ae-metadata/references/metadata_input_file_upload.md +29 -0
  68. package/skills/ae-metadata/references/metadata_property_dimension_table_bind_existing.md +27 -0
  69. package/skills/ae-metadata/references/metadata_property_dimension_table_create_and_bind_csv.md +30 -0
  70. package/skills/ae-metadata/references/metadata_property_get.md +54 -0
package/README.md CHANGED
@@ -67,12 +67,9 @@ ae-cli kb +read --source '{"scope":"company","name":"engineering-handbook"}' \
67
67
  Authentication is handled per-host. Each AE host URL maintains its own token.
68
68
 
69
69
  ```bash
70
- # Auto-login for active host (macOS, extracts token from Chrome)
70
+ # Device code login (cross-platform)
71
71
  ae-cli auth login
72
72
 
73
- # Manually set token
74
- ae-cli auth set-token <your-token>
75
-
76
73
  # Check status
77
74
  ae-cli auth status
78
75
 
package/README.zh.md CHANGED
@@ -67,12 +67,9 @@ ae-cli kb +read --source '{"scope":"company","name":"engineering-handbook"}' \
67
67
  每个 AE 主机 URL 维护独立的身份验证 token。
68
68
 
69
69
  ```bash
70
- # 自动登录当前主机(macOS,从 Chrome 提取 token)
70
+ # 设备码登录(跨平台)
71
71
  ae-cli auth login
72
72
 
73
- # 手动设置 token
74
- ae-cli auth set-token <your-token>
75
-
76
73
  # 检查状态
77
74
  ae-cli auth status
78
75
 
@@ -5,10 +5,9 @@ import {
5
5
  loadToken,
6
6
  resolveHost,
7
7
  saveToken,
8
- setTokenManual,
9
8
  validateToken
10
- } from "./chunk-RSHMZ3LG.js";
11
- import "./chunk-6W4QOZAL.js";
9
+ } from "./chunk-U6QPKCN3.js";
10
+ import "./chunk-WPXHCP4A.js";
12
11
  export {
13
12
  clearToken,
14
13
  getAuthStatus,
@@ -16,6 +15,5 @@ export {
16
15
  loadToken,
17
16
  resolveHost,
18
17
  saveToken,
19
- setTokenManual,
20
18
  validateToken
21
19
  };
@@ -1,30 +1,26 @@
1
1
  import {
2
2
  printError,
3
3
  printOutput
4
- } from "./chunk-2DUEAXV3.js";
4
+ } from "./chunk-YACCPUNQ.js";
5
5
  import {
6
- clearMcpToken,
7
- setMcpTokenManual,
8
- validateMcpToken
9
- } from "./chunk-DMMEPCST.js";
10
- import "./chunk-PN3OS34E.js";
11
- import "./chunk-NOO24N7W.js";
6
+ clearCliToken,
7
+ mintCliToken
8
+ } from "./chunk-FPE5LGVU.js";
12
9
  import {
13
10
  clear,
14
11
  clearToken,
15
12
  getAuthStatus,
16
13
  load,
17
14
  resolveHost,
18
- save,
19
- setTokenManual,
20
- validateToken
21
- } from "./chunk-RSHMZ3LG.js";
15
+ save
16
+ } from "./chunk-U6QPKCN3.js";
22
17
  import {
18
+ getFallbackCliToken,
23
19
  loadConfig,
24
20
  logger,
25
21
  removeHost,
26
22
  saveConfig
27
- } from "./chunk-6W4QOZAL.js";
23
+ } from "./chunk-WPXHCP4A.js";
28
24
 
29
25
  // src/core/device-auth.ts
30
26
  import { execFileSync } from "child_process";
@@ -246,7 +242,7 @@ function activateHostAfterLogin(host, explicitHostOverride) {
246
242
  saveConfig(config);
247
243
  logger.info(`Active host set to ${host} after login`);
248
244
  }
249
- function persistDeviceTokens(host, tokens) {
245
+ async function persistDeviceTokens(host, tokens) {
250
246
  const DEFAULT_EXPIRES_IN = 72e3;
251
247
  const MAX_EXPIRES_IN = 86400 * 365;
252
248
  let expiresInSeconds = tokens.expires_in ?? DEFAULT_EXPIRES_IN;
@@ -258,16 +254,15 @@ function persistDeviceTokens(host, tokens) {
258
254
  save(host, {
259
255
  accessToken: tokens.access_token,
260
256
  refreshToken: tokens.refresh_token ?? "",
261
- accessExpiresAt: expiresAt,
262
- mcpToken: tokens.mcp_token ?? void 0
257
+ accessExpiresAt: expiresAt
263
258
  });
259
+ await mintCliToken(host);
264
260
  }
265
261
  function loginSummary(host, tokens) {
266
262
  return {
267
263
  authenticated: true,
268
264
  host,
269
- token: tokens.access_token.slice(0, 8) + "...",
270
- mcpToken: tokens.mcp_token ? tokens.mcp_token.slice(0, 8) + "..." : null
265
+ token: tokens.access_token.slice(0, 8) + "..."
271
266
  };
272
267
  }
273
268
  function resolveLoginTeClaudeBase(host) {
@@ -303,7 +298,7 @@ function registerAuth(program) {
303
298
  try {
304
299
  if (opts.deviceCode) {
305
300
  const tokens2 = await pollDeviceFlow(teClaudeBase, opts.deviceCode, {}, emit);
306
- persistDeviceTokens(host, tokens2);
301
+ await persistDeviceTokens(host, tokens2);
307
302
  activateHostAfterLogin(host, explicitHost);
308
303
  logger.info(`Device flow login successful for ${host} (resumed)`);
309
304
  emit("Login successful! Token saved securely.");
@@ -326,7 +321,7 @@ function registerAuth(program) {
326
321
  return;
327
322
  }
328
323
  const tokens = await runDeviceFlow(teClaudeBase, { noBrowser }, emit);
329
- persistDeviceTokens(host, tokens);
324
+ await persistDeviceTokens(host, tokens);
330
325
  activateHostAfterLogin(host, explicitHost);
331
326
  logger.info(`Device flow login successful for ${host}`);
332
327
  emit("Login successful! Token saved securely.");
@@ -336,7 +331,7 @@ function registerAuth(program) {
336
331
  printError(
337
332
  "auth",
338
333
  err.message,
339
- "Use `ae-cli auth set-token <token>` to authenticate, or upgrade the AE server to a version that supports device login."
334
+ "Upgrade the AE server to a version that supports device login."
340
335
  );
341
336
  process.exit(1);
342
337
  }
@@ -344,39 +339,6 @@ function registerAuth(program) {
344
339
  process.exit(1);
345
340
  }
346
341
  });
347
- auth.command("set-token <token>").description("Manually set authentication token for a host (--host or active host)").option("--host <url>", HOST_OPTION_DESC).action(async (token, opts) => {
348
- const host = resolveAuthHost(program, opts);
349
- if (!host) {
350
- printError("config", "No AE host configured.", "Run: ae-cli config set-host <url>");
351
- process.exit(1);
352
- }
353
- const config = loadConfig();
354
- if (!config.hosts[host]) {
355
- config.hosts[host] = { label: host };
356
- if (!config.activeHost) {
357
- config.activeHost = host;
358
- }
359
- saveConfig(config);
360
- process.stderr.write(`[ae-cli] Config saved for ${host}
361
- `);
362
- printOutput({ saved: true, config }, program.opts().format || "json");
363
- }
364
- process.stderr.write(`[ae-cli] Validating token...
365
- `);
366
- const isValid = await validateToken(token, host);
367
- if (!isValid) {
368
- logger.warn(`Token validation failed for ${host}`);
369
- process.stderr.write(`[ae-cli] Token validation failed
370
- `);
371
- printError("auth", "Invalid token", "Please check your token and try again");
372
- process.exit(1);
373
- }
374
- setTokenManual(token, host);
375
- logger.info(`Token manually set and validated for ${host}`);
376
- process.stderr.write(`[ae-cli] Token verified and saved for ${host}
377
- `);
378
- printOutput({ saved: true, host, verified: true }, program.opts().format || "json");
379
- });
380
342
  auth.command("status").description("Show authentication status for a host (--host or active host)").option("--host <url>", HOST_OPTION_DESC).action(async (opts) => {
381
343
  const host = resolveAuthHost(program, opts);
382
344
  if (!host) {
@@ -394,7 +356,20 @@ function registerAuth(program) {
394
356
  accessExpiresAt: secureEntry.accessExpiresAt,
395
357
  accessExpiresAtNote: "advisory: access token auto-slides on use server-side; re-login only if a command returns an auth error",
396
358
  pastStaticExpiry,
397
- hasMcpToken: !!secureEntry.mcpToken
359
+ hasCliToken: !!secureEntry.cliToken
360
+ },
361
+ program.opts().format || "json"
362
+ );
363
+ return;
364
+ }
365
+ const fallbackCliToken = getFallbackCliToken(host);
366
+ if (fallbackCliToken) {
367
+ printOutput(
368
+ {
369
+ authenticated: true,
370
+ host,
371
+ source: "sandbox-cli-token",
372
+ hasCliToken: true
398
373
  },
399
374
  program.opts().format || "json"
400
375
  );
@@ -404,64 +379,17 @@ function registerAuth(program) {
404
379
  const authenticated = tokenStatus.authenticated;
405
380
  printOutput({ authenticated, host, source: tokenStatus.source ?? "none" }, program.opts().format || "json");
406
381
  });
407
- auth.command("logout").description("Clear stored token for a host (--host or active host)").option("--host <url>", HOST_OPTION_DESC).action((opts) => {
382
+ auth.command("logout").description("Clear stored credentials (access token, CLI token, and host config) for a host (--host or active host)").option("--host <url>", HOST_OPTION_DESC).action((opts) => {
408
383
  const host = resolveAuthHost(program, opts);
409
384
  if (!host) {
410
385
  printError("config", "No AE host configured.", "Run: ae-cli config set-host <url>");
411
386
  process.exit(1);
412
387
  }
388
+ clearCliToken(host);
413
389
  clearToken(host);
414
390
  clear(host);
415
391
  removeHost(host);
416
- process.stderr.write(`[ae-cli] Token and config cleared for ${host}
417
- `);
418
- printOutput({ cleared: true, host }, program.opts().format || "json");
419
- });
420
- auth.command("set-mcp-token <token>").description("Manually set MCP token for a host (--host or active host)").option("--host <url>", HOST_OPTION_DESC).action(async (token, opts) => {
421
- const host = resolveAuthHost(program, opts);
422
- if (!host) {
423
- printError("config", "No AE host configured.", "Use --host <url> or run: ae-cli config set-host <url>");
424
- process.exit(1);
425
- }
426
- const config = loadConfig();
427
- if (!config.hosts[host]) {
428
- config.hosts[host] = { label: host };
429
- if (!config.activeHost) {
430
- config.activeHost = host;
431
- }
432
- saveConfig(config);
433
- process.stderr.write(`[ae-cli] Host config saved for ${host}
434
- `);
435
- }
436
- process.stderr.write(`[ae-cli] Validating MCP token...
437
- `);
438
- const isValid = await validateMcpToken(token, host);
439
- if (!isValid) {
440
- process.stderr.write(`[ae-cli] MCP token validation failed
441
- `);
442
- printError("auth", "Invalid MCP token", "Please check your MCP token and try again");
443
- process.exit(1);
444
- }
445
- setMcpTokenManual(token, host);
446
- process.stderr.write(`[ae-cli] MCP token verified and saved for ${host}
447
- `);
448
- printOutput({ saved: true, host, verified: true, type: "mcp-token" }, program.opts().format || "json");
449
- });
450
- auth.command("clear-mcp-token").description("Clear stored MCP token for a host (--host or active host; --all clears all)").option("--host <url>", HOST_OPTION_DESC).option("--all", "Clear MCP tokens for all hosts").action((opts) => {
451
- if (opts.all) {
452
- clearMcpToken();
453
- process.stderr.write(`[ae-cli] All MCP tokens cleared
454
- `);
455
- printOutput({ cleared: true, type: "all-mcp-tokens" }, program.opts().format || "json");
456
- return;
457
- }
458
- const host = resolveAuthHost(program, opts);
459
- if (!host) {
460
- printError("config", "No AE host configured.", "Use --host <url> or run: ae-cli config set-host <url>");
461
- process.exit(1);
462
- }
463
- clearMcpToken(host);
464
- process.stderr.write(`[ae-cli] MCP token cleared for ${host}
392
+ process.stderr.write(`[ae-cli] Credentials and config cleared for ${host}
465
393
  `);
466
394
  printOutput({ cleared: true, host }, program.opts().format || "json");
467
395
  });
@@ -1,12 +1,12 @@
1
- import {
2
- getMcpToken
3
- } from "./chunk-DMMEPCST.js";
4
1
  import {
5
2
  PermissionError
6
3
  } from "./chunk-NOO24N7W.js";
4
+ import {
5
+ getCliToken
6
+ } from "./chunk-FPE5LGVU.js";
7
7
  import {
8
8
  safeJsonParse
9
- } from "./chunk-6W4QOZAL.js";
9
+ } from "./chunk-WPXHCP4A.js";
10
10
 
11
11
  // src/core/mcp-access.ts
12
12
  function buildUrl(host, path, params = {}) {
@@ -52,21 +52,21 @@ function parseKbResponse(resp, text) {
52
52
  }
53
53
  return data.data !== void 0 ? data.data : data;
54
54
  }
55
- async function fetchWithMcpToken(input, init, mcpToken) {
55
+ async function fetchWithCliToken(input, init, cliToken) {
56
56
  const headers = new Headers(init.headers);
57
- headers.set("mcp-token", mcpToken);
57
+ headers.set("cli-token", cliToken);
58
58
  const resp = await fetch(input, { ...init, headers });
59
59
  return parseKbResponse(resp, await resp.text());
60
60
  }
61
61
  async function getAuthHeaders(ctx) {
62
- const mcpToken = await getMcpToken(ctx.host());
63
- return { "mcp-token": mcpToken };
62
+ const cliToken = await getCliToken(ctx.host());
63
+ return { "cli-token": cliToken };
64
64
  }
65
65
  async function kbApi(ctx, method, path, params = {}, body) {
66
66
  const host = ctx.host();
67
- const mcpToken = await getMcpToken(host);
67
+ const cliToken = await getCliToken(host);
68
68
  const upperMethod = method.toUpperCase();
69
- return fetchWithMcpToken(
69
+ return fetchWithCliToken(
70
70
  buildUrl(host, path, params),
71
71
  {
72
72
  method: upperMethod,
@@ -75,19 +75,19 @@ async function kbApi(ctx, method, path, params = {}, body) {
75
75
  },
76
76
  body: upperMethod === "GET" ? void 0 : JSON.stringify(body ?? {})
77
77
  },
78
- mcpToken
78
+ cliToken
79
79
  );
80
80
  }
81
81
  async function kbUpload(ctx, path, form, params = {}) {
82
82
  const host = ctx.host();
83
- const mcpToken = await getMcpToken(host);
84
- return fetchWithMcpToken(
83
+ const cliToken = await getCliToken(host);
84
+ return fetchWithCliToken(
85
85
  buildUrl(host, path, params),
86
86
  {
87
87
  method: "POST",
88
88
  body: form
89
89
  },
90
- mcpToken
90
+ cliToken
91
91
  );
92
92
  }
93
93
 
@@ -0,0 +1,163 @@
1
+ import {
2
+ PermissionError
3
+ } from "./chunk-NOO24N7W.js";
4
+ import {
5
+ clearCliToken,
6
+ getCliToken
7
+ } from "./chunk-FPE5LGVU.js";
8
+ import {
9
+ logger,
10
+ safeJsonParse
11
+ } from "./chunk-WPXHCP4A.js";
12
+
13
+ // src/core/capability-api.ts
14
+ import { readFile } from "fs/promises";
15
+ import { basename } from "path";
16
+ var CapabilityGatewayError = class extends Error {
17
+ constructor(message, code, httpStatus) {
18
+ super(message);
19
+ this.code = code;
20
+ this.httpStatus = httpStatus;
21
+ this.name = "CapabilityGatewayError";
22
+ }
23
+ code;
24
+ httpStatus;
25
+ };
26
+ function buildCapabilityGatewayUrl(host, domain, pathAfterV1, queryParams = {}) {
27
+ const base = host.replace(/\/+$/, "");
28
+ const cleanDomain = domain.replace(/^\/+|\/+$/g, "");
29
+ const cleanPath = pathAfterV1.replace(/^\/+|\/+$/g, "");
30
+ const url = new URL(`${base}/api/cli/${cleanDomain}/v1/${cleanPath}`);
31
+ for (const [key, value] of Object.entries(queryParams)) {
32
+ if (value !== void 0 && value !== null) {
33
+ url.searchParams.set(key, String(value));
34
+ }
35
+ }
36
+ return url.toString();
37
+ }
38
+ async function permissionMessage(resp) {
39
+ const text = await resp.text().catch(() => "");
40
+ try {
41
+ const d = safeJsonParse(text);
42
+ const msg = d && (d.error?.message || (typeof d.error === "string" ? d.error : void 0) || d.message);
43
+ if (msg && typeof msg === "string") return msg;
44
+ } catch {
45
+ }
46
+ return "Permission denied for this resource (HTTP 403)";
47
+ }
48
+ function parseCapabilityResponse(text) {
49
+ if (!text) return void 0;
50
+ try {
51
+ return safeJsonParse(text);
52
+ } catch {
53
+ return text;
54
+ }
55
+ }
56
+ function unwrapCapabilityEnvelope(body) {
57
+ if (body == null) {
58
+ throw new CapabilityGatewayError("Empty capability gateway response");
59
+ }
60
+ if (typeof body.ok === "boolean") {
61
+ if (body.ok) {
62
+ return body.data;
63
+ }
64
+ const err = body.error ?? {};
65
+ const message = err.message || err.code || "Capability gateway request failed";
66
+ throw new CapabilityGatewayError(message, err.code, err.http_status ?? err.httpStatus);
67
+ }
68
+ return body;
69
+ }
70
+ async function requestOnce(url, method, token, body) {
71
+ const headers = {
72
+ "cli-token": token,
73
+ "Accept": "application/json"
74
+ };
75
+ const init = { method, headers };
76
+ if (body !== void 0 && method !== "GET") {
77
+ headers["Content-Type"] = "application/json";
78
+ init.body = JSON.stringify(body);
79
+ }
80
+ return fetch(url, init);
81
+ }
82
+ async function requestMultipartOnce(url, token, form) {
83
+ return fetch(url, {
84
+ method: "POST",
85
+ headers: {
86
+ "cli-token": token,
87
+ "Accept": "application/json"
88
+ },
89
+ body: form
90
+ });
91
+ }
92
+ async function callGateway(host, domain, pathAfterV1, method, body) {
93
+ const url = buildCapabilityGatewayUrl(host, domain, pathAfterV1);
94
+ const token = await getCliToken(host);
95
+ let resp = await requestOnce(url, method, token, body);
96
+ if (resp.status === 403) {
97
+ throw new PermissionError(await permissionMessage(resp));
98
+ }
99
+ if (resp.status === 401) {
100
+ logger.warn(`Capability gateway request failed (HTTP 401) for ${host}, refreshing CLI token`);
101
+ clearCliToken(host);
102
+ const newToken = await getCliToken(host);
103
+ process.stderr.write(`[ae-cli] CLI token refreshed for ${host}
104
+ `);
105
+ resp = await requestOnce(url, method, newToken, body);
106
+ if (resp.status === 403) {
107
+ throw new PermissionError(await permissionMessage(resp));
108
+ }
109
+ if (!resp.ok) {
110
+ throw new Error(`Capability gateway HTTP error: ${resp.status} ${resp.statusText}`);
111
+ }
112
+ } else if (!resp.ok) {
113
+ throw new Error(`Capability gateway HTTP error: ${resp.status} ${resp.statusText}`);
114
+ }
115
+ return unwrapCapabilityEnvelope(parseCapabilityResponse(await resp.text()));
116
+ }
117
+ async function executeCapability(host, domain, capabilityId, input = {}) {
118
+ return callGateway(host, domain, `capabilities/${capabilityId}/execute`, "POST", { input });
119
+ }
120
+ async function uploadInputFile(host, domain, projectId, purpose, filePath) {
121
+ const fileBytes = await readFile(filePath);
122
+ return uploadInputFileBytes(host, domain, projectId, purpose, fileBytes, basename(filePath));
123
+ }
124
+ async function uploadInputFileBytes(host, domain, projectId, purpose, bytes, filename, contentType = "application/octet-stream") {
125
+ const url = buildCapabilityGatewayUrl(host, domain, "input-files");
126
+ const fileBuffer = bytes instanceof ArrayBuffer ? bytes : bytes.buffer.slice(
127
+ bytes.byteOffset,
128
+ bytes.byteOffset + bytes.byteLength
129
+ );
130
+ const form = new FormData();
131
+ form.append("project_id", String(projectId));
132
+ form.append("purpose", purpose);
133
+ form.append("file", new Blob([fileBuffer], { type: contentType }), filename);
134
+ const token = await getCliToken(host);
135
+ let resp = await requestMultipartOnce(url, token, form);
136
+ if (resp.status === 403) {
137
+ throw new PermissionError(await permissionMessage(resp));
138
+ }
139
+ if (resp.status === 401) {
140
+ logger.warn(`Capability gateway request failed (HTTP 401) for ${host}, refreshing CLI token`);
141
+ clearCliToken(host);
142
+ const newToken = await getCliToken(host);
143
+ process.stderr.write(`[ae-cli] CLI token refreshed for ${host}
144
+ `);
145
+ resp = await requestMultipartOnce(url, newToken, form);
146
+ if (resp.status === 403) {
147
+ throw new PermissionError(await permissionMessage(resp));
148
+ }
149
+ if (!resp.ok) {
150
+ throw new Error(`Capability gateway HTTP error: ${resp.status} ${resp.statusText}`);
151
+ }
152
+ } else if (!resp.ok) {
153
+ throw new Error(`Capability gateway HTTP error: ${resp.status} ${resp.statusText}`);
154
+ }
155
+ return unwrapCapabilityEnvelope(parseCapabilityResponse(await resp.text()));
156
+ }
157
+
158
+ export {
159
+ buildCapabilityGatewayUrl,
160
+ executeCapability,
161
+ uploadInputFile,
162
+ uploadInputFileBytes
163
+ };