@thinkingai/ae-cli 6.1.19 → 6.1.20
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 +95 -38
- package/README.zh.md +95 -38
- package/dist/{auth-QPEVVBKT.js → auth-FUM37MHF.js} +236 -127
- package/dist/{capability-ASI62I4A.js → capability-AXFQW5WM.js} +45 -32
- package/dist/{chunk-FR2EDAQM.js → chunk-4P355ZWB.js} +69 -1
- package/dist/chunk-6ZIQV5GW.js +26 -0
- package/dist/chunk-ATSM5XAW.js +623 -0
- package/dist/{chunk-6QK7SYYY.js → chunk-BBEFP4SB.js} +29 -37
- package/dist/{chunk-KEWVSO64.js → chunk-CZU3V3DQ.js} +4 -15
- package/dist/chunk-ECBLHAZO.js +15 -0
- package/dist/{chunk-B5HMQAOM.js → chunk-I4WQAEYB.js} +1 -1
- package/dist/{chunk-QL6R6TSK.js → chunk-LNZBEQXW.js} +6 -5
- package/dist/{chunk-GJXRF7Y6.js → chunk-QJQZH5GQ.js} +47 -78
- package/dist/{chunk-S5PONFBM.js → chunk-RSG4ONOI.js} +7 -7
- package/dist/{chunk-DKEXH6Q5.js → chunk-TAL6CZH6.js} +6 -6
- package/dist/{chunk-JYMGHGCE.js → chunk-VXNMYUXU.js} +24 -13
- package/dist/{chunk-KZ6ZHF5J.js → chunk-WZ6YVQSF.js} +14 -14
- package/dist/{community-report-client-6OL6GBQW.js → community-report-client-XXWGSBSD.js} +2 -4
- package/dist/{config-XNMACCKX.js → config-EXUGQN5T.js} +8 -9
- package/dist/{data-integration-4NZGZ45E.js → data-integration-4NZ53OMT.js} +17 -15
- package/dist/index.js +52 -79
- package/dist/{local-data-upload-client-EFHDLXFG.js → local-data-upload-client-KYOKVYZV.js} +2 -4
- package/dist/{memory-HBJQ2GC5.js → memory-ATNKZNW5.js} +4 -6
- package/dist/{metadata-62EWBEIG.js → metadata-VZVC7YMH.js} +7 -9
- package/dist/{model-BHLJ2QMT.js → model-E4JMQ4V2.js} +4 -6
- package/dist/{personal-semantic-preference-JBVMAI7L.js → personal-semantic-preference-7S2SQ3UV.js} +6 -8
- package/dist/{project-semantic-BQ4MPUS5.js → project-semantic-2SZP2OPO.js} +11 -13
- package/dist/{sync-MUWPRM5I.js → sync-YV3E66IF.js} +6 -8
- package/dist/{te-agent-WQZTMEK4.js → te-agent-JB5T3PO7.js} +393 -90
- package/dist/{te-analysis-MNCG65LU.js → te-analysis-3YJAAT2D.js} +99 -33
- package/dist/{te-community-BNE6KREL.js → te-community-UDBI672N.js} +9 -32
- package/dist/{te-dataops-TXLKM2W6.js → te-dataops-ZLYOCXZ4.js} +19 -20
- package/dist/{te-engage-TPKLRVTL.js → te-engage-4XG6OJML.js} +12 -14
- package/dist/{te-experiment-6BYKTCY7.js → te-experiment-VXUWPINJ.js} +6 -8
- package/dist/{te-kb-BUD6N3NI.js → te-kb-WYQWHFSC.js} +595 -85
- package/dist/{te-system-44LZJDTQ.js → te-system-7G6F2LJA.js} +6 -8
- package/dist/{te-team-XMP47YXE.js → te-team-E7FBBXMQ.js} +6 -8
- package/dist/{update-UODFOZ4P.js → update-D47BUG25.js} +7 -8
- package/package.json +12 -9
- package/skills/ae-agent/SKILL.md +30 -13
- package/skills/ae-agent/references/agent-distribution.md +94 -0
- package/skills/ae-agent/references/approval-request.md +4 -0
- package/skills/ae-agent/references/command_index.md +9 -2
- package/skills/ae-agent/references/create-automation.md +20 -2
- package/skills/ae-agent/references/get-agent-context.md +70 -0
- package/skills/ae-agent/references/list-automations.md +18 -3
- package/skills/ae-agent/references/list-mcp-credentials.md +1 -1
- package/skills/ae-agent/references/mcp-token.md +3 -3
- package/skills/ae-agent/references/set-mcp-credential.md +0 -1
- package/skills/ae-agent/references/update-automation.md +18 -0
- package/skills/ae-analysis/SKILL.md +11 -2
- package/skills/ae-analysis/references/adhoc_run.md +2 -0
- package/skills/ae-analysis/references/ai_models.md +23 -3
- package/skills/ae-analysis/references/analysis_gateway_assets.md +2 -2
- package/skills/ae-analysis/references/command_index.md +7 -7
- package/skills/ae-analysis/references/cross_source_config.md +84 -0
- package/skills/ae-analysis/references/dashboard_update.md +1 -1
- package/skills/ae-analysis/references/user_tag_create.md +23 -1
- package/skills/ae-analysis/references/user_tag_models.md +9 -1
- package/skills/ae-analysis/references/user_tag_update.md +14 -2
- package/skills/ae-data-integration/SKILL.md +2 -1
- package/skills/ae-data-integration/references/dimension-routing.md +36 -0
- package/skills/ae-data-integration/references/local-analysis.md +2 -0
- package/skills/ae-data-integration/references/source-inspect.md +1 -1
- package/skills/ae-data-integration/references/tracking-plan.md +1 -1
- package/skills/ae-data-integration/references/transform.md +1 -1
- package/skills/ae-data-integration/references/ue-mapping.md +5 -2
- package/skills/ae-data-integration/references/ue-routing.md +17 -1
- package/skills/ae-generate-tracking-plan/SKILL.md +25 -13
- package/skills/ae-generate-tracking-plan/references/business-dimension-mapping.md +1 -1
- package/skills/ae-kb/SKILL.md +107 -36
- package/skills/ae-kb/references/query-workflow.md +59 -29
- package/skills/ae-kb/references/versions.md +46 -0
- package/skills/ae-system/SKILL.md +1 -1
- package/skills/ae-use-agent/SKILL.md +42 -0
- package/skills/ae-use-agent/references/local-agent.md +114 -0
- package/dist/auth-UMJAA72O.js +0 -15
- package/dist/chunk-533JK3YX.js +0 -72
- package/dist/chunk-O6K2ODUE.js +0 -318
- package/dist/chunk-RWXIXOZG.js +0 -326
- package/dist/client-N7NGZUNI.js +0 -204
- package/skills/ae-agent/references/auto-provision-mcp-credentials.md +0 -57
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# Local Agent command guide
|
|
2
|
+
|
|
3
|
+
This guide is included in ae-use-agent; installing ae-agent is optional. Use Node.js 20+
|
|
4
|
+
and a local client that can run shell commands and read files.
|
|
5
|
+
|
|
6
|
+
## Setup and recovery
|
|
7
|
+
|
|
8
|
+
Run `ae-cli --version`. If the command is missing, obtain the AE host and supported CLI
|
|
9
|
+
version from the user's environment administrator. Give the appropriate installation
|
|
10
|
+
command with that version substituted; do not install automatically:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
# Customer/public distribution
|
|
14
|
+
npm install -g @thinkingai/ae-cli@<supported-version>
|
|
15
|
+
# Internal ThinkingData distribution
|
|
16
|
+
npm install -g @tant/ae-cli@<supported-version> --registry=https://npm.thinkingdata.cn:3443
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Use the environment URL supplied by the user or configured in ae-cli; never guess a host.
|
|
20
|
+
Keep that original host on every request, including authentication:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
ae-cli auth status --host <original-host>
|
|
24
|
+
ae-cli auth login --host <original-host>
|
|
25
|
+
ae-cli agent +get-agent-context --help
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Login is needed only when the session is missing or expired. Let the user complete the
|
|
29
|
+
browser login; never request tokens in chat. If the command is unknown, the CLI is too old.
|
|
30
|
+
For the public package, `ae-cli update --dry-run` previews host-compatible updates;
|
|
31
|
+
perform an update only under the client's existing permissions. Internal builds use the
|
|
32
|
+
internal registry above. If the host-supported release lacks this command or endpoint,
|
|
33
|
+
ask the environment administrator for a compatible deployment; do not repeatedly update.
|
|
34
|
+
|
|
35
|
+
## Discover and load
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
ae-cli agent +list-agents --q <agent-name> --host <original-host>
|
|
39
|
+
ae-cli agent +get-agent-context --id <agent-id> --host <original-host>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Default JSON output is `{ "ok": true, "data": ... }`. Check `ok` before reading `data`.
|
|
43
|
+
Name search results are in `data.agents`. Match name, scope and description; ask the user
|
|
44
|
+
when ambiguous. Search by name instead of assuming an unfiltered list contains every Agent.
|
|
45
|
+
An explicit ID can go directly to the context command. Ordinary members can read their
|
|
46
|
+
own personal Agents and shared Agents visible to their account; no admin role is required.
|
|
47
|
+
|
|
48
|
+
Context fields are `data.schema_version`, `data.context_version`, `data.agent.instructions`,
|
|
49
|
+
`data.model`, `data.dependencies.skills`, `data.dependencies.mcps` and `data.summary`.
|
|
50
|
+
This guide supports schema version 1; stop and obtain matching documentation for other
|
|
51
|
+
versions. Use your current client model. An unavailable platform model does not block it.
|
|
52
|
+
`availability=available` means visible metadata, not a working local dependency.
|
|
53
|
+
|
|
54
|
+
## Read required Skill files
|
|
55
|
+
|
|
56
|
+
For an available Skill, use its `id` and `files` descriptors. No extra list-skills call is
|
|
57
|
+
needed. Each descriptor has `executable` and `argv`; pass arguments separately, never as
|
|
58
|
+
interpolated shell code. Append `--host <original-host>`. Supported read commands are:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
ae-cli agent +get-skill-content --id <skill-id> --host <original-host>
|
|
62
|
+
ae-cli agent +list-skill-references --id <skill-id> --host <original-host>
|
|
63
|
+
ae-cli agent +list-skill-scripts --id <skill-id> --host <original-host>
|
|
64
|
+
ae-cli agent +list-skill-assets --id <skill-id> --host <original-host>
|
|
65
|
+
ae-cli agent +read-skill-reference --id <skill-id> --path <relative-path> --host <original-host>
|
|
66
|
+
ae-cli agent +read-skill-script --id <skill-id> --path <relative-path> --host <original-host>
|
|
67
|
+
ae-cli agent +read-skill-asset --id <skill-id> --path <relative-path> --output <local-file> --host <original-host>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Skill body is in `data.item.content`; directory entries are in `data.items`. Text file
|
|
71
|
+
reads return `data.content`. Use a path returned by the corresponding directory listing;
|
|
72
|
+
`--path` is relative to references, scripts or assets, without that directory prefix.
|
|
73
|
+
All three read commands support `--output <local-file>` to preserve binary bytes. Use a
|
|
74
|
+
new workspace file path and inspect scripts before any execution. Check reported versions
|
|
75
|
+
against the context and re-fetch if they change. An empty listing does not prove that a
|
|
76
|
+
file required by the Skill is unnecessary: report a missing required file and stop that step.
|
|
77
|
+
|
|
78
|
+
## MCP dependencies and failures
|
|
79
|
+
|
|
80
|
+
If the user explicitly requests exporting current-user MCP credentials, use:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
ae-cli agent +get-agent-context --id <agent-id> --include-mcp-credentials --host <original-host>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
This is sensitive output: do not quote it in chat or logs, or commit it. Put the connection
|
|
87
|
+
only in the intended local client configuration. Require `data.credentials_included=true`;
|
|
88
|
+
its absence means an older server, not success. For each MCP, check `connection_config`:
|
|
89
|
+
`exported` supplies `connection_name` and the same `connection` configuration shown in the
|
|
90
|
+
current user's MCP market: `type`, `url`, `headers` for remote MCPs, or `type`, `command`,
|
|
91
|
+
`args`, `env` for stdio. Header and environment keys are unchanged; system URLs use the AE
|
|
92
|
+
host. `credential_expires_at=null` means expiry is not exposed. `unavailable` supplies a safe
|
|
93
|
+
`connection_error`. No refresh tokens or platform-internal keys are included. Re-export
|
|
94
|
+
after credentials expire or are revoked. Platform managed bridges require the platform
|
|
95
|
+
runtime. Do not assume a successful export proves connectivity.
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
Use `ae-cli agent +mcp-tools --id <mcp-id> --host <original-host>` for platform tool metadata.
|
|
99
|
+
For OAuth MCPs only, `ae-cli agent +mcp-auth-status --id <mcp-id> --host <original-host>`
|
|
100
|
+
reports platform authentication. Neither establishes a local connection. Reuse an existing
|
|
101
|
+
local capability when suitable; otherwise obtain connection setup through the client's
|
|
102
|
+
normal credential flow. Do not ask for secrets in chat or claim platform OAuth transfers.
|
|
103
|
+
|
|
104
|
+
- 401/auth error: log in on the same host and retry once; stop if authentication still fails.
|
|
105
|
+
- 403: permission denied; do not refresh repeatedly or require an admin account by default.
|
|
106
|
+
- 404 on context: run `ae-cli agent +get-agent --id <agent-id> --host <original-host>`.
|
|
107
|
+
If it also fails, verify the ID/account/host. If it succeeds, verify endpoint deployment
|
|
108
|
+
with the administrator. Raw Agent configuration is not a complete context fallback.
|
|
109
|
+
- 409 `invalid_agent_configuration`: ask the Agent owner to repair dependency configuration.
|
|
110
|
+
- 500/network error: report the failed step and host; no silent success or fallback.
|
|
111
|
+
- Unavailable dependency or file: stop only the step that needs it; explain what is missing.
|
|
112
|
+
|
|
113
|
+
Never claim the full Agent is ready from prompt loading alone. Context is live metadata,
|
|
114
|
+
not a frozen package; re-fetch it for a new task.
|
package/dist/auth-UMJAA72O.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { createRequire as __createRequire } from 'node:module'; const require = __createRequire(import.meta.url);
|
|
2
|
-
import {
|
|
3
|
-
getToken,
|
|
4
|
-
invalidateAccessTokenForRetry,
|
|
5
|
-
resolveHost,
|
|
6
|
-
validateToken
|
|
7
|
-
} from "./chunk-O6K2ODUE.js";
|
|
8
|
-
import "./chunk-KZ6ZHF5J.js";
|
|
9
|
-
import "./chunk-FR2EDAQM.js";
|
|
10
|
-
export {
|
|
11
|
-
getToken,
|
|
12
|
-
invalidateAccessTokenForRetry,
|
|
13
|
-
resolveHost,
|
|
14
|
-
validateToken
|
|
15
|
-
};
|
package/dist/chunk-533JK3YX.js
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { createRequire as __createRequire } from 'node:module'; const require = __createRequire(import.meta.url);
|
|
2
|
-
|
|
3
|
-
// src/core/errors.ts
|
|
4
|
-
var PermissionError = class extends Error {
|
|
5
|
-
constructor(message, code, hint) {
|
|
6
|
-
super(message);
|
|
7
|
-
this.code = code;
|
|
8
|
-
this.hint = hint;
|
|
9
|
-
this.name = "PermissionError";
|
|
10
|
-
}
|
|
11
|
-
code;
|
|
12
|
-
hint;
|
|
13
|
-
};
|
|
14
|
-
var CliValidationError = class extends Error {
|
|
15
|
-
code;
|
|
16
|
-
hint;
|
|
17
|
-
location;
|
|
18
|
-
constructor(message, options = {}) {
|
|
19
|
-
super(message);
|
|
20
|
-
this.name = "CliValidationError";
|
|
21
|
-
this.code = options.code;
|
|
22
|
-
this.hint = options.hint;
|
|
23
|
-
if (options.location) {
|
|
24
|
-
const location = {};
|
|
25
|
-
if (typeof options.location.segment === "number" && Number.isFinite(options.location.segment)) {
|
|
26
|
-
location.segment = options.location.segment;
|
|
27
|
-
}
|
|
28
|
-
if (typeof options.location.record === "number" && Number.isFinite(options.location.record)) {
|
|
29
|
-
location.record = options.location.record;
|
|
30
|
-
}
|
|
31
|
-
if (typeof options.location.field === "string" && options.location.field.length > 0) {
|
|
32
|
-
location.field = options.location.field;
|
|
33
|
-
}
|
|
34
|
-
if (Object.keys(location).length > 0) this.location = location;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
|
-
var CommunityReportError = class extends Error {
|
|
39
|
-
code;
|
|
40
|
-
httpStatus;
|
|
41
|
-
hint;
|
|
42
|
-
meta;
|
|
43
|
-
constructor(message, options = {}) {
|
|
44
|
-
super(message, options.cause === void 0 ? void 0 : { cause: options.cause });
|
|
45
|
-
this.name = "CommunityReportError";
|
|
46
|
-
this.code = options.code;
|
|
47
|
-
this.httpStatus = options.httpStatus;
|
|
48
|
-
this.hint = options.hint;
|
|
49
|
-
this.meta = options.meta;
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
var LocalDataUploadError = class extends Error {
|
|
53
|
-
code;
|
|
54
|
-
httpStatus;
|
|
55
|
-
hint;
|
|
56
|
-
meta;
|
|
57
|
-
constructor(message, options = {}) {
|
|
58
|
-
super(message, options.cause === void 0 ? void 0 : { cause: options.cause });
|
|
59
|
-
this.name = "LocalDataUploadError";
|
|
60
|
-
this.code = options.code;
|
|
61
|
-
this.httpStatus = options.httpStatus;
|
|
62
|
-
this.hint = options.hint;
|
|
63
|
-
this.meta = options.meta;
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
export {
|
|
68
|
-
PermissionError,
|
|
69
|
-
CliValidationError,
|
|
70
|
-
CommunityReportError,
|
|
71
|
-
LocalDataUploadError
|
|
72
|
-
};
|
package/dist/chunk-O6K2ODUE.js
DELETED
|
@@ -1,318 +0,0 @@
|
|
|
1
|
-
import { createRequire as __createRequire } from 'node:module'; const require = __createRequire(import.meta.url);
|
|
2
|
-
import {
|
|
3
|
-
getActiveHost,
|
|
4
|
-
getConfigDir,
|
|
5
|
-
normalizeUrl,
|
|
6
|
-
safeJsonParse
|
|
7
|
-
} from "./chunk-KZ6ZHF5J.js";
|
|
8
|
-
import {
|
|
9
|
-
logger
|
|
10
|
-
} from "./chunk-FR2EDAQM.js";
|
|
11
|
-
|
|
12
|
-
// src/core/secure-store.ts
|
|
13
|
-
import crypto from "crypto";
|
|
14
|
-
import fs from "fs";
|
|
15
|
-
import os from "os";
|
|
16
|
-
import path from "path";
|
|
17
|
-
import { execFileSync } from "child_process";
|
|
18
|
-
var STORE_DIR_SUFFIX = "secure-tokens";
|
|
19
|
-
var SCRYPT_SALT = Buffer.from("ae-cli-secure-store-v1");
|
|
20
|
-
var SCRYPT_N = 16384;
|
|
21
|
-
var SCRYPT_R = 8;
|
|
22
|
-
var SCRYPT_P = 1;
|
|
23
|
-
var KEY_LEN = 32;
|
|
24
|
-
var NONCE_LEN = 12;
|
|
25
|
-
var REFRESH_TOKEN_PATH = "/v1/oauth/refreshForToken";
|
|
26
|
-
function getMachineId() {
|
|
27
|
-
if (process.platform === "darwin") {
|
|
28
|
-
try {
|
|
29
|
-
const out = execFileSync(
|
|
30
|
-
"ioreg",
|
|
31
|
-
["-rd1", "-c", "IOPlatformExpertDevice"],
|
|
32
|
-
{ encoding: "utf8", timeout: 3e3 }
|
|
33
|
-
);
|
|
34
|
-
const m = out.match(/IOPlatformUUID.*?=.*?"([0-9A-F-]{36})"/i);
|
|
35
|
-
if (m?.[1]) return `darwin:${m[1]}`;
|
|
36
|
-
} catch {
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
if (process.platform === "linux") {
|
|
40
|
-
for (const p of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
|
|
41
|
-
try {
|
|
42
|
-
const id = fs.readFileSync(p, "utf8").trim();
|
|
43
|
-
if (id) return `linux:${id}`;
|
|
44
|
-
} catch {
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
if (process.platform === "win32") {
|
|
49
|
-
try {
|
|
50
|
-
const out = execFileSync(
|
|
51
|
-
"reg",
|
|
52
|
-
["query", "HKLM\\SOFTWARE\\Microsoft\\Cryptography", "/v", "MachineGuid"],
|
|
53
|
-
{ encoding: "utf8", timeout: 3e3 }
|
|
54
|
-
);
|
|
55
|
-
const m = out.match(/MachineGuid\s+REG_SZ\s+([0-9a-f-]+)/i);
|
|
56
|
-
if (m?.[1]) return `win32:${m[1]}`;
|
|
57
|
-
} catch {
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
const fallback = `${os.homedir()}|${os.hostname()}|${os.platform()}`;
|
|
61
|
-
logger.warn("secure-store: using fallback machine-id (HOME+hostname+platform)");
|
|
62
|
-
return `fallback:${fallback}`;
|
|
63
|
-
}
|
|
64
|
-
var _cachedMachineId;
|
|
65
|
-
function getCachedMachineId() {
|
|
66
|
-
if (!_cachedMachineId) _cachedMachineId = getMachineId();
|
|
67
|
-
return _cachedMachineId;
|
|
68
|
-
}
|
|
69
|
-
function deriveKey(machineId) {
|
|
70
|
-
return crypto.scryptSync(machineId, SCRYPT_SALT, KEY_LEN, {
|
|
71
|
-
N: SCRYPT_N,
|
|
72
|
-
r: SCRYPT_R,
|
|
73
|
-
p: SCRYPT_P
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
var _cachedKey;
|
|
77
|
-
function getKey() {
|
|
78
|
-
if (!_cachedKey) _cachedKey = deriveKey(getCachedMachineId());
|
|
79
|
-
return _cachedKey;
|
|
80
|
-
}
|
|
81
|
-
function encrypt(plaintext) {
|
|
82
|
-
const key = getKey();
|
|
83
|
-
const nonce = crypto.randomBytes(NONCE_LEN);
|
|
84
|
-
const cipher = crypto.createCipheriv("aes-256-gcm", key, nonce);
|
|
85
|
-
const encrypted = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
|
|
86
|
-
const tag = cipher.getAuthTag();
|
|
87
|
-
return {
|
|
88
|
-
nonce: nonce.toString("hex"),
|
|
89
|
-
tag: tag.toString("hex"),
|
|
90
|
-
data: encrypted.toString("hex")
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
function decrypt(blob) {
|
|
94
|
-
const key = getKey();
|
|
95
|
-
const nonce = Buffer.from(blob.nonce, "hex");
|
|
96
|
-
const tag = Buffer.from(blob.tag, "hex");
|
|
97
|
-
const data = Buffer.from(blob.data, "hex");
|
|
98
|
-
const decipher = crypto.createDecipheriv("aes-256-gcm", key, nonce);
|
|
99
|
-
decipher.setAuthTag(tag);
|
|
100
|
-
const decrypted = Buffer.concat([decipher.update(data), decipher.final()]);
|
|
101
|
-
return decrypted.toString("utf8");
|
|
102
|
-
}
|
|
103
|
-
function storeDir() {
|
|
104
|
-
return path.join(getConfigDir(), STORE_DIR_SUFFIX);
|
|
105
|
-
}
|
|
106
|
-
function tokenFilePath(host) {
|
|
107
|
-
const safe = host.replace(/^https?:\/\//, "").replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
108
|
-
return path.join(storeDir(), `${safe}.enc.json`);
|
|
109
|
-
}
|
|
110
|
-
function ensureStoreDir() {
|
|
111
|
-
const dir = storeDir();
|
|
112
|
-
if (!fs.existsSync(dir)) {
|
|
113
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
114
|
-
try {
|
|
115
|
-
fs.chmodSync(dir, 448);
|
|
116
|
-
} catch {
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
function save(host, payload) {
|
|
121
|
-
ensureStoreDir();
|
|
122
|
-
const plaintext = JSON.stringify(payload);
|
|
123
|
-
const blob = encrypt(plaintext);
|
|
124
|
-
const filePath = tokenFilePath(host);
|
|
125
|
-
fs.writeFileSync(filePath, JSON.stringify(blob, null, 2), { encoding: "utf8" });
|
|
126
|
-
try {
|
|
127
|
-
fs.chmodSync(filePath, 384);
|
|
128
|
-
} catch {
|
|
129
|
-
}
|
|
130
|
-
logger.info(`secure-store: token saved for ${host}`);
|
|
131
|
-
}
|
|
132
|
-
function load(host) {
|
|
133
|
-
const filePath = tokenFilePath(host);
|
|
134
|
-
if (!fs.existsSync(filePath)) return null;
|
|
135
|
-
try {
|
|
136
|
-
const raw = fs.readFileSync(filePath, "utf8");
|
|
137
|
-
const blob = safeJsonParse(raw);
|
|
138
|
-
if (!blob?.nonce || !blob?.tag || !blob?.data) {
|
|
139
|
-
logger.warn(`secure-store: malformed blob for ${host}`);
|
|
140
|
-
return null;
|
|
141
|
-
}
|
|
142
|
-
const plaintext = decrypt(blob);
|
|
143
|
-
return JSON.parse(plaintext);
|
|
144
|
-
} catch (e) {
|
|
145
|
-
logger.error(`secure-store: decrypt failed for ${host}: ${e.message}`);
|
|
146
|
-
return null;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
function clear(host) {
|
|
150
|
-
const filePath = tokenFilePath(host);
|
|
151
|
-
if (fs.existsSync(filePath)) {
|
|
152
|
-
fs.rmSync(filePath);
|
|
153
|
-
logger.info(`secure-store: token cleared for ${host}`);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
async function getValidAccessToken(host) {
|
|
157
|
-
const payload = load(host);
|
|
158
|
-
if (!payload) {
|
|
159
|
-
throw new SecureStoreAuthError(
|
|
160
|
-
`No stored token for ${host}. Please run: ae-cli auth login`
|
|
161
|
-
);
|
|
162
|
-
}
|
|
163
|
-
const expiresAt = new Date(payload.accessExpiresAt).getTime();
|
|
164
|
-
const now = Date.now();
|
|
165
|
-
const needsRefresh = expiresAt - now < 6e4;
|
|
166
|
-
if (!needsRefresh) {
|
|
167
|
-
return payload.accessToken;
|
|
168
|
-
}
|
|
169
|
-
if (payload.refreshToken) {
|
|
170
|
-
logger.info(`secure-store: access token past local expiry for ${host}, refreshing\u2026`);
|
|
171
|
-
const refreshed = await doRefresh(host, payload.refreshToken);
|
|
172
|
-
save(host, { ...refreshed, cliToken: payload.cliToken });
|
|
173
|
-
logger.info(`secure-store: token refreshed for ${host}`);
|
|
174
|
-
return refreshed.accessToken;
|
|
175
|
-
}
|
|
176
|
-
logger.info(`secure-store: ${host} past static local expiry, no refresh token \u2014 returning stored token, server validates (lazy)`);
|
|
177
|
-
return payload.accessToken;
|
|
178
|
-
}
|
|
179
|
-
function loadCliToken(host) {
|
|
180
|
-
return load(host)?.cliToken ?? null;
|
|
181
|
-
}
|
|
182
|
-
async function doRefresh(host, refreshToken) {
|
|
183
|
-
const base = host.replace(/\/+$/, "");
|
|
184
|
-
const url = `${base}${REFRESH_TOKEN_PATH}`;
|
|
185
|
-
let resp;
|
|
186
|
-
try {
|
|
187
|
-
resp = await fetch(url, {
|
|
188
|
-
method: "POST",
|
|
189
|
-
headers: { "Content-Type": "application/json" },
|
|
190
|
-
body: JSON.stringify({ refreshToken })
|
|
191
|
-
});
|
|
192
|
-
} catch (e) {
|
|
193
|
-
throw new SecureStoreAuthError(
|
|
194
|
-
`Token refresh network error for ${host}: ${e.message}. Please run: ae-cli auth login`
|
|
195
|
-
);
|
|
196
|
-
}
|
|
197
|
-
if (!resp.ok) {
|
|
198
|
-
throw new SecureStoreAuthError(
|
|
199
|
-
`Token refresh failed (HTTP ${resp.status}) for ${host}. Please run: ae-cli auth login`
|
|
200
|
-
);
|
|
201
|
-
}
|
|
202
|
-
let body;
|
|
203
|
-
try {
|
|
204
|
-
body = safeJsonParse(await resp.text());
|
|
205
|
-
} catch {
|
|
206
|
-
throw new SecureStoreAuthError(
|
|
207
|
-
`Token refresh returned invalid JSON for ${host}. Please run: ae-cli auth login`
|
|
208
|
-
);
|
|
209
|
-
}
|
|
210
|
-
if (body.return_code !== 0) {
|
|
211
|
-
throw new SecureStoreAuthError(
|
|
212
|
-
`Token refresh error (${body.return_code}: ${body.return_message ?? "unknown"}) for ${host}. Please run: ae-cli auth login`
|
|
213
|
-
);
|
|
214
|
-
}
|
|
215
|
-
const d = body.data;
|
|
216
|
-
const newAccess = d?.accessToken ?? d?.access_token ?? "";
|
|
217
|
-
const newRefresh = d?.refreshToken ?? d?.refresh_token ?? refreshToken;
|
|
218
|
-
const expiresIn = d?.expiresIn ?? d?.expires_in ?? 72e3;
|
|
219
|
-
if (!newAccess) {
|
|
220
|
-
throw new SecureStoreAuthError(
|
|
221
|
-
`Token refresh returned empty accessToken for ${host}. Please run: ae-cli auth login`
|
|
222
|
-
);
|
|
223
|
-
}
|
|
224
|
-
return {
|
|
225
|
-
accessToken: newAccess,
|
|
226
|
-
refreshToken: newRefresh,
|
|
227
|
-
accessExpiresAt: new Date(Date.now() + expiresIn * 1e3).toISOString()
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
var SecureStoreAuthError = class extends Error {
|
|
231
|
-
constructor(message) {
|
|
232
|
-
super(message);
|
|
233
|
-
this.name = "SecureStoreAuthError";
|
|
234
|
-
}
|
|
235
|
-
};
|
|
236
|
-
|
|
237
|
-
// src/core/host-guidance.ts
|
|
238
|
-
var AE_TRIAL_URL = "https://thinkingai.cn/request-demo";
|
|
239
|
-
var AE_HOST_SETUP_COMMAND = "ae-cli config set-host <url>";
|
|
240
|
-
function missingAeHostHint(additionalAction) {
|
|
241
|
-
const actions = [
|
|
242
|
-
`If your organization already uses AgenticEngine, ask your administrator for its AE URL, then run: ${AE_HOST_SETUP_COMMAND}.`,
|
|
243
|
-
`If you do not have an AgenticEngine environment, request a trial: ${AE_TRIAL_URL}`
|
|
244
|
-
];
|
|
245
|
-
if (additionalAction) actions.push(additionalAction);
|
|
246
|
-
return actions.join(" ");
|
|
247
|
-
}
|
|
248
|
-
function missingAeHostGuidance() {
|
|
249
|
-
return {
|
|
250
|
-
configure_host: AE_HOST_SETUP_COMMAND,
|
|
251
|
-
existing_customer: "Ask your AgenticEngine administrator for the AE environment URL.",
|
|
252
|
-
request_trial_url: AE_TRIAL_URL
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// src/core/auth.ts
|
|
257
|
-
function resolveHost(hostOverride) {
|
|
258
|
-
if (hostOverride) return normalizeUrl(hostOverride);
|
|
259
|
-
return getActiveHost();
|
|
260
|
-
}
|
|
261
|
-
async function validateToken(token, hostUrl) {
|
|
262
|
-
const url = `${hostUrl}/v1/oauth/checkToken`;
|
|
263
|
-
try {
|
|
264
|
-
const resp = await fetch(url, {
|
|
265
|
-
method: "POST",
|
|
266
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
267
|
-
body: new URLSearchParams({ accessToken: token }).toString()
|
|
268
|
-
});
|
|
269
|
-
const respJson = safeJsonParse(await resp.text());
|
|
270
|
-
return respJson?.return_code === 0;
|
|
271
|
-
} catch {
|
|
272
|
-
return false;
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
function invalidateAccessTokenForRetry(hostUrl) {
|
|
276
|
-
const payload = load(hostUrl);
|
|
277
|
-
if (!payload) return;
|
|
278
|
-
save(hostUrl, { ...payload, accessExpiresAt: (/* @__PURE__ */ new Date(0)).toISOString() });
|
|
279
|
-
logger.info(`auth: marked access token stale for retry on ${hostUrl}`);
|
|
280
|
-
}
|
|
281
|
-
async function getToken(hostUrl) {
|
|
282
|
-
if (!hostUrl) {
|
|
283
|
-
throw new Error(
|
|
284
|
-
`No AE host configured.
|
|
285
|
-
` + missingAeHostHint()
|
|
286
|
-
);
|
|
287
|
-
}
|
|
288
|
-
try {
|
|
289
|
-
const secureToken = await getValidAccessToken(hostUrl);
|
|
290
|
-
logger.info(`Using secure-store token for ${hostUrl}`);
|
|
291
|
-
return secureToken;
|
|
292
|
-
} catch (e) {
|
|
293
|
-
if (e instanceof SecureStoreAuthError) {
|
|
294
|
-
logger.info(`secure-store: ${e.message}`);
|
|
295
|
-
} else {
|
|
296
|
-
logger.warn(`secure-store unexpected error: ${e.message}`);
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
throw new Error(
|
|
300
|
-
`Cannot obtain token for ${hostUrl}.
|
|
301
|
-
Run: ae-cli auth login (device code flow, cross-platform)`
|
|
302
|
-
);
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
export {
|
|
306
|
-
save,
|
|
307
|
-
load,
|
|
308
|
-
clear,
|
|
309
|
-
loadCliToken,
|
|
310
|
-
SecureStoreAuthError,
|
|
311
|
-
AE_TRIAL_URL,
|
|
312
|
-
missingAeHostHint,
|
|
313
|
-
missingAeHostGuidance,
|
|
314
|
-
resolveHost,
|
|
315
|
-
validateToken,
|
|
316
|
-
invalidateAccessTokenForRetry,
|
|
317
|
-
getToken
|
|
318
|
-
};
|