@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.
- package/README.md +1 -4
- package/README.zh.md +1 -4
- package/dist/{auth-DYC2BGLM.js → auth-I2G7OAU2.js} +2 -4
- package/dist/{auth-GHBMJ5ZR.js → auth-RDN6D5HU.js} +32 -104
- package/dist/{chunk-5OC7CYCG.js → chunk-2W7HULUD.js} +14 -14
- package/dist/chunk-4EUZRJPO.js +163 -0
- package/dist/{chunk-DMMEPCST.js → chunk-5FBLRSMU.js} +25 -115
- package/dist/{chunk-BJX3GXWC.js → chunk-E34XH4GW.js} +2 -2
- package/dist/chunk-FPE5LGVU.js +105 -0
- package/dist/{chunk-I5CRVM2S.js → chunk-H52CSLJW.js} +44 -17
- package/dist/{chunk-RSHMZ3LG.js → chunk-U6QPKCN3.js} +6 -20
- package/dist/{chunk-6W4QOZAL.js → chunk-WPXHCP4A.js} +23 -13
- package/dist/{chunk-PN3OS34E.js → chunk-XE2PI5DS.js} +1 -1
- package/dist/{chunk-2DUEAXV3.js → chunk-YACCPUNQ.js} +1 -1
- package/dist/cli-token-6AGEDJF4.js +14 -0
- package/dist/{client-2UJ63MVQ.js → client-VTJMUZ4O.js} +3 -3
- package/dist/{config-NCUGK6NS.js → config-2SDTLGOC.js} +3 -3
- package/dist/index.js +309 -197
- package/dist/metadata-JZ2OTWBK.js +420 -0
- package/dist/{model-SJSOZTRS.js → model-ZYMRN4VS.js} +2 -2
- package/dist/{raw-7NVWSVJP.js → raw-NGYHAL3F.js} +4 -4
- package/dist/{sync-GRVO4K3U.js → sync-JVLX6TGW.js} +3 -3
- package/dist/{te-agent-CYZUC4US.js → te-agent-QPZTLHPH.js} +2 -2
- package/dist/{te-analysis-HJ22TUAA.js → te-analysis-WWX3Z6RP.js} +46 -24
- package/dist/{te-audience-MYDE5WEH.js → te-audience-UXGHUVBB.js} +27 -18
- package/dist/{te-common-WNCZB7R7.js → te-common-7XXH73MF.js} +5 -4
- package/dist/{te-community-JEEFXPB3.js → te-community-QKE67HR2.js} +5 -4
- package/dist/{te-dataops-ZTS7XMXM.js → te-dataops-6W7NNYST.js} +4 -5
- package/dist/{te-engage-4XHCTS6I.js → te-engage-NCFAQFX3.js} +5 -4
- package/dist/{te-kb-OWM3RQRH.js → te-kb-K5BQYUCM.js} +4 -5
- package/dist/{te-meta-JVO4QJBH.js → te-meta-LJZBOSMO.js} +26 -19
- package/dist/{te-team-YY24VORI.js → te-team-DHK5R7MV.js} +5 -6
- package/package.json +3 -2
- package/skills/ae-analysis/SKILL.md +9 -2
- package/skills/ae-analysis/references/build_attribution_analysis_qp.md +2 -0
- package/skills/ae-analysis/references/build_cluster_definition.md +2 -0
- package/skills/ae-analysis/references/build_distribution_analysis_qp.md +2 -0
- package/skills/ae-analysis/references/build_event_analysis_qp.md +2 -0
- package/skills/ae-analysis/references/build_funnel_analysis_qp.md +2 -0
- package/skills/ae-analysis/references/build_heat_map_analysis_qp.md +2 -0
- package/skills/ae-analysis/references/build_interval_analysis_qp.md +2 -0
- package/skills/ae-analysis/references/build_path_analysis_qp.md +2 -0
- package/skills/ae-analysis/references/build_prop_analysis_qp.md +2 -0
- package/skills/ae-analysis/references/build_rank_list_analysis_qp.md +2 -0
- package/skills/ae-analysis/references/build_retention_analysis_qp.md +2 -0
- package/skills/ae-analysis/references/build_tag_definition.md +2 -0
- package/skills/ae-analysis/references/list_clusters.md +3 -1
- package/skills/ae-analysis/references/list_events.md +5 -2
- package/skills/ae-analysis/references/list_metrics.md +4 -2
- package/skills/ae-analysis/references/list_properties.md +4 -2
- package/skills/ae-analysis/references/list_tags.md +3 -1
- package/skills/ae-generate-tracking-code/SKILL.md +68 -31
- package/skills/ae-generate-tracking-code/references/snippet-delivery.md +26 -1
- package/skills/ae-generate-tracking-code/references/te-api.md +47 -164
- package/skills/ae-generate-tracking-plan/SKILL.md +109 -32
- package/skills/ae-generate-tracking-plan/references/te-api.md +57 -27
- package/skills/ae-metadata/SKILL.md +90 -0
- package/skills/ae-metadata/references/metadata_data_table_csv_delete.md +15 -0
- package/skills/ae-metadata/references/metadata_data_table_csv_write.md +29 -0
- package/skills/ae-metadata/references/metadata_data_table_download.md +17 -0
- package/skills/ae-metadata/references/metadata_data_table_get.md +24 -0
- package/skills/ae-metadata/references/metadata_data_table_list.md +27 -0
- package/skills/ae-metadata/references/metadata_data_table_property_bindings_update.md +17 -0
- package/skills/ae-metadata/references/metadata_data_table_sql_delete.md +15 -0
- package/skills/ae-metadata/references/metadata_data_table_sql_write.md +31 -0
- package/skills/ae-metadata/references/metadata_event_get.md +52 -0
- package/skills/ae-metadata/references/metadata_input_file_upload.md +29 -0
- package/skills/ae-metadata/references/metadata_property_dimension_table_bind_existing.md +27 -0
- package/skills/ae-metadata/references/metadata_property_dimension_table_create_and_bind_csv.md +30 -0
- 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
|
-
#
|
|
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
|
-
#
|
|
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-
|
|
11
|
-
import "./chunk-
|
|
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-
|
|
4
|
+
} from "./chunk-YACCPUNQ.js";
|
|
5
5
|
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
|
|
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-
|
|
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
|
-
"
|
|
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
|
-
|
|
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]
|
|
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-
|
|
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
|
|
55
|
+
async function fetchWithCliToken(input, init, cliToken) {
|
|
56
56
|
const headers = new Headers(init.headers);
|
|
57
|
-
headers.set("
|
|
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
|
|
63
|
-
return { "
|
|
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
|
|
67
|
+
const cliToken = await getCliToken(host);
|
|
68
68
|
const upperMethod = method.toUpperCase();
|
|
69
|
-
return
|
|
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
|
-
|
|
78
|
+
cliToken
|
|
79
79
|
);
|
|
80
80
|
}
|
|
81
81
|
async function kbUpload(ctx, path, form, params = {}) {
|
|
82
82
|
const host = ctx.host();
|
|
83
|
-
const
|
|
84
|
-
return
|
|
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
|
-
|
|
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
|
+
};
|