@thinkingai/ae-cli 6.0.47 → 6.0.48-workbuddy
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 +93 -36
- package/README.zh.md +93 -36
- package/assets/workbuddy-host-page/thinkingai.png +0 -0
- package/assets/workbuddy-host-page/workbuddy.png +0 -0
- package/bin/ae-cli-workbuddy.js +7 -0
- package/dist/auth-HJG2L6DR.js +910 -0
- package/dist/{capability-RUSV77CO.js → capability-JHYVQNQ4.js} +47 -34
- package/dist/chunk-2BDQB42E.js +15 -0
- package/dist/{chunk-GJXRF7Y6.js → chunk-6MXFJVNP.js} +47 -78
- package/dist/chunk-6ZIQV5GW.js +26 -0
- package/dist/{chunk-KZ6ZHF5J.js → chunk-ARTIHWVD.js} +27 -25
- package/dist/{chunk-6QK7SYYY.js → chunk-DU5MZQFQ.js} +29 -37
- package/dist/{chunk-BVDST5YT.js → chunk-JFOGWBAT.js} +8 -8
- package/dist/{chunk-JYMGHGCE.js → chunk-KSQGGXGA.js} +24 -13
- package/dist/{chunk-FR2EDAQM.js → chunk-NXRKDG6E.js} +89 -3
- package/dist/{chunk-NSIVXKJH.js → chunk-QSO4SA6C.js} +5 -5
- package/dist/{chunk-QL6R6TSK.js → chunk-QYJ252CT.js} +6 -5
- package/dist/{chunk-B5HMQAOM.js → chunk-UL6SJDYE.js} +1 -1
- package/dist/chunk-Y3S2KNNR.js +623 -0
- package/dist/{chunk-KEWVSO64.js → chunk-ZCM47ROU.js} +5 -16
- package/dist/{community-report-client-6OL6GBQW.js → community-report-client-QE4RC2XG.js} +2 -4
- package/dist/{config-XNMACCKX.js → config-XMRP5NDA.js} +8 -9
- package/dist/{data-integration-4NZGZ45E.js → data-integration-SGRITHJS.js} +19 -18
- package/dist/index.js +53 -80
- package/dist/{local-data-upload-client-EFHDLXFG.js → local-data-upload-client-LMVS7YAR.js} +2 -4
- package/dist/{memory-HBJQ2GC5.js → memory-5ZC73XBC.js} +4 -6
- package/dist/{metadata-PGDJE5RQ.js → metadata-LBX6O24P.js} +7 -9
- package/dist/{model-BHLJ2QMT.js → model-CRDIVYN5.js} +4 -6
- package/dist/{personal-semantic-preference-E5NZ56DN.js → personal-semantic-preference-7FVUTJZF.js} +6 -8
- package/dist/{sync-MUWPRM5I.js → sync-VVNDHU7R.js} +9 -11
- package/dist/{te-agent-WQZTMEK4.js → te-agent-4T5TFHD6.js} +393 -90
- package/dist/{te-analysis-YWCSSLYL.js → te-analysis-TPYFOIFD.js} +97 -31
- package/dist/{te-community-BNE6KREL.js → te-community-PWOTHSEC.js} +9 -32
- package/dist/{te-dataops-TXLKM2W6.js → te-dataops-ZJV5QVJU.js} +19 -20
- package/dist/{te-engage-VEW4CJHK.js → te-engage-WVHBZAXH.js} +11 -13
- package/dist/{te-kb-BUD6N3NI.js → te-kb-PNM2ZKBP.js} +595 -85
- package/dist/{te-system-44LZJDTQ.js → te-system-MRCMLAWW.js} +6 -8
- package/dist/{te-team-XMP47YXE.js → te-team-Y36TG6AQ.js} +6 -8
- package/dist/{update-UODFOZ4P.js → update-UNKCZ5U5.js} +7 -8
- package/package.json +15 -10
- 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-QPEVVBKT.js +0 -465
- 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-QPEVVBKT.js
DELETED
|
@@ -1,465 +0,0 @@
|
|
|
1
|
-
import { createRequire as __createRequire } from 'node:module'; const require = __createRequire(import.meta.url);
|
|
2
|
-
import {
|
|
3
|
-
getLocalCliPackageInfo
|
|
4
|
-
} from "./chunk-E2JKXMVO.js";
|
|
5
|
-
import {
|
|
6
|
-
printError,
|
|
7
|
-
printOutput,
|
|
8
|
-
runHostCompatCheck
|
|
9
|
-
} from "./chunk-KEWVSO64.js";
|
|
10
|
-
import {
|
|
11
|
-
CliTokenValidationUnavailableError,
|
|
12
|
-
clearCliToken,
|
|
13
|
-
mintCliToken,
|
|
14
|
-
validateCliTokenOnServer
|
|
15
|
-
} from "./chunk-RWXIXOZG.js";
|
|
16
|
-
import {
|
|
17
|
-
SecureStoreAuthError,
|
|
18
|
-
clear,
|
|
19
|
-
load,
|
|
20
|
-
missingAeHostGuidance,
|
|
21
|
-
missingAeHostHint,
|
|
22
|
-
resolveHost,
|
|
23
|
-
save
|
|
24
|
-
} from "./chunk-O6K2ODUE.js";
|
|
25
|
-
import {
|
|
26
|
-
getFallbackCliToken,
|
|
27
|
-
loadConfig,
|
|
28
|
-
normalizeUrl,
|
|
29
|
-
saveConfig
|
|
30
|
-
} from "./chunk-KZ6ZHF5J.js";
|
|
31
|
-
import {
|
|
32
|
-
PermissionError
|
|
33
|
-
} from "./chunk-533JK3YX.js";
|
|
34
|
-
import {
|
|
35
|
-
logger
|
|
36
|
-
} from "./chunk-FR2EDAQM.js";
|
|
37
|
-
|
|
38
|
-
// src/core/device-auth.ts
|
|
39
|
-
import { execFileSync } from "child_process";
|
|
40
|
-
var DeviceFlowUnsupportedError = class extends Error {
|
|
41
|
-
constructor() {
|
|
42
|
-
super("This AE server does not support device code login (it may be an older server version).");
|
|
43
|
-
this.name = "DeviceFlowUnsupportedError";
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
function looksLikeMissingEndpoint(status, contentType, body) {
|
|
47
|
-
if (status === 404) return true;
|
|
48
|
-
if (contentType && contentType.toLowerCase().includes("text/html")) return true;
|
|
49
|
-
const head = body.trimStart().toLowerCase();
|
|
50
|
-
return head.startsWith("<!doctype html") || head.startsWith("<html");
|
|
51
|
-
}
|
|
52
|
-
function openBrowser(url, _execFile) {
|
|
53
|
-
let parsedUrl;
|
|
54
|
-
try {
|
|
55
|
-
parsedUrl = new URL(url);
|
|
56
|
-
} catch {
|
|
57
|
-
logger.warn(`openBrowser: invalid URL, not opening`);
|
|
58
|
-
return false;
|
|
59
|
-
}
|
|
60
|
-
if (!["http:", "https:"].includes(parsedUrl.protocol)) {
|
|
61
|
-
logger.warn(`openBrowser: unsafe protocol "${parsedUrl.protocol}", not opening`);
|
|
62
|
-
return false;
|
|
63
|
-
}
|
|
64
|
-
const safeLogUrl = `${parsedUrl.origin}${parsedUrl.pathname}`;
|
|
65
|
-
const exec = _execFile ?? execFileSync;
|
|
66
|
-
try {
|
|
67
|
-
if (process.platform === "darwin") {
|
|
68
|
-
exec("open", [url], { timeout: 5e3 });
|
|
69
|
-
return true;
|
|
70
|
-
}
|
|
71
|
-
if (process.platform === "win32") {
|
|
72
|
-
exec("cmd", ["/c", "start", "", url], { timeout: 5e3 });
|
|
73
|
-
return true;
|
|
74
|
-
}
|
|
75
|
-
exec("xdg-open", [url], { timeout: 5e3 });
|
|
76
|
-
return true;
|
|
77
|
-
} catch (e) {
|
|
78
|
-
logger.warn(`openBrowser: failed to open ${safeLogUrl}: ${e.message}`);
|
|
79
|
-
return false;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
async function authorizeDevice(host) {
|
|
83
|
-
const base = host.replace(/\/+$/, "");
|
|
84
|
-
const url = `${base}/api/auth/device/authorize`;
|
|
85
|
-
let resp;
|
|
86
|
-
try {
|
|
87
|
-
resp = await fetch(url, {
|
|
88
|
-
method: "POST",
|
|
89
|
-
headers: { "Content-Type": "application/json" }
|
|
90
|
-
});
|
|
91
|
-
} catch (e) {
|
|
92
|
-
throw new Error(`Device authorize request failed: ${e.message}`);
|
|
93
|
-
}
|
|
94
|
-
const contentType = resp.headers.get("content-type");
|
|
95
|
-
const text = await resp.text().catch(() => "");
|
|
96
|
-
if (looksLikeMissingEndpoint(resp.status, contentType, text)) {
|
|
97
|
-
throw new DeviceFlowUnsupportedError();
|
|
98
|
-
}
|
|
99
|
-
if (!resp.ok) {
|
|
100
|
-
throw new Error(`Device authorize returned HTTP ${resp.status}: ${text}`);
|
|
101
|
-
}
|
|
102
|
-
let body;
|
|
103
|
-
try {
|
|
104
|
-
body = JSON.parse(text);
|
|
105
|
-
} catch {
|
|
106
|
-
throw new Error("Device authorize returned invalid JSON");
|
|
107
|
-
}
|
|
108
|
-
if (!body.device_code || !body.user_code) {
|
|
109
|
-
throw new Error("Device authorize response missing required fields");
|
|
110
|
-
}
|
|
111
|
-
return body;
|
|
112
|
-
}
|
|
113
|
-
async function pollDeviceToken(host, deviceCode) {
|
|
114
|
-
const base = host.replace(/\/+$/, "");
|
|
115
|
-
const url = `${base}/api/auth/device/token`;
|
|
116
|
-
let resp;
|
|
117
|
-
try {
|
|
118
|
-
resp = await fetch(url, {
|
|
119
|
-
method: "POST",
|
|
120
|
-
headers: { "Content-Type": "application/json" },
|
|
121
|
-
body: JSON.stringify({ device_code: deviceCode })
|
|
122
|
-
});
|
|
123
|
-
} catch (e) {
|
|
124
|
-
return { status: "error", message: `Network error: ${e.message}` };
|
|
125
|
-
}
|
|
126
|
-
if (resp.status === 200) {
|
|
127
|
-
let body;
|
|
128
|
-
try {
|
|
129
|
-
body = await resp.json();
|
|
130
|
-
} catch {
|
|
131
|
-
return { status: "error", message: "Token response is not valid JSON" };
|
|
132
|
-
}
|
|
133
|
-
if (!body.access_token) {
|
|
134
|
-
return { status: "error", message: "Token response missing access_token" };
|
|
135
|
-
}
|
|
136
|
-
return { status: "approved", tokens: body };
|
|
137
|
-
}
|
|
138
|
-
if (resp.status === 400) {
|
|
139
|
-
let errorBody;
|
|
140
|
-
try {
|
|
141
|
-
errorBody = await resp.json();
|
|
142
|
-
} catch {
|
|
143
|
-
errorBody = {};
|
|
144
|
-
}
|
|
145
|
-
const err = errorBody.error;
|
|
146
|
-
if (err === "authorization_pending") return { status: "pending" };
|
|
147
|
-
if (err === "slow_down") return { status: "slow_down" };
|
|
148
|
-
if (err === "expired_token") return { status: "expired" };
|
|
149
|
-
return { status: "error", message: `Unknown error: ${err ?? "unknown"}` };
|
|
150
|
-
}
|
|
151
|
-
if (resp.status === 403) {
|
|
152
|
-
let errorBody = {};
|
|
153
|
-
try {
|
|
154
|
-
errorBody = await resp.json();
|
|
155
|
-
} catch {
|
|
156
|
-
}
|
|
157
|
-
return {
|
|
158
|
-
status: "denied",
|
|
159
|
-
code: errorBody.code ?? "CLI_ACCESS_DISABLED",
|
|
160
|
-
message: errorBody.message ?? errorBody.error ?? "CLI access is disabled for this account.",
|
|
161
|
-
hint: errorBody.hint
|
|
162
|
-
};
|
|
163
|
-
}
|
|
164
|
-
const contentType = resp.headers.get("content-type");
|
|
165
|
-
const text = await resp.text().catch(() => "");
|
|
166
|
-
if (looksLikeMissingEndpoint(resp.status, contentType, text)) {
|
|
167
|
-
return { status: "unsupported" };
|
|
168
|
-
}
|
|
169
|
-
return { status: "error", message: `Unexpected HTTP ${resp.status} from token endpoint` };
|
|
170
|
-
}
|
|
171
|
-
function buildVerificationUrl(host, userCode) {
|
|
172
|
-
return `${host.replace(/\/+$/, "")}/device-activate?user_code=${encodeURIComponent(userCode)}`;
|
|
173
|
-
}
|
|
174
|
-
async function pollDeviceFlow(host, deviceCode, options = {}, onStatus) {
|
|
175
|
-
const emit = (msg) => {
|
|
176
|
-
if (onStatus) onStatus(msg);
|
|
177
|
-
};
|
|
178
|
-
let intervalMs = options.intervalMs ?? 5e3;
|
|
179
|
-
const expiresIn = options.expiresIn ?? 300;
|
|
180
|
-
const deadline = Date.now() + expiresIn * 1e3;
|
|
181
|
-
let pollCount = 0;
|
|
182
|
-
while (Date.now() < deadline) {
|
|
183
|
-
await sleep(intervalMs);
|
|
184
|
-
pollCount++;
|
|
185
|
-
const result = await pollDeviceToken(host, deviceCode);
|
|
186
|
-
if (result.status === "approved") {
|
|
187
|
-
emit(`Authorization successful! (polled ${pollCount} time(s))`);
|
|
188
|
-
return result.tokens;
|
|
189
|
-
}
|
|
190
|
-
if (result.status === "pending") {
|
|
191
|
-
if (pollCount % 5 === 0) {
|
|
192
|
-
const remaining = Math.max(0, Math.round((deadline - Date.now()) / 1e3));
|
|
193
|
-
emit(`Waiting for authorization... (expires in ${remaining}s)`);
|
|
194
|
-
}
|
|
195
|
-
logger.info(`device-auth: poll #${pollCount} pending`);
|
|
196
|
-
continue;
|
|
197
|
-
}
|
|
198
|
-
if (result.status === "slow_down") {
|
|
199
|
-
intervalMs += 5e3;
|
|
200
|
-
logger.info(`device-auth: slow_down received, new interval=${intervalMs}ms`);
|
|
201
|
-
continue;
|
|
202
|
-
}
|
|
203
|
-
if (result.status === "expired") {
|
|
204
|
-
throw new Error(
|
|
205
|
-
`Device code has expired. Please run ae-cli auth login again to restart the authorization flow.`
|
|
206
|
-
);
|
|
207
|
-
}
|
|
208
|
-
if (result.status === "denied") {
|
|
209
|
-
throw new PermissionError(result.message, result.code, result.hint);
|
|
210
|
-
}
|
|
211
|
-
if (result.status === "unsupported") {
|
|
212
|
-
throw new DeviceFlowUnsupportedError();
|
|
213
|
-
}
|
|
214
|
-
if (result.status === "error") {
|
|
215
|
-
logger.warn(`device-auth: poll error: ${result.message}`);
|
|
216
|
-
emit(`Poll error: ${result.message}`);
|
|
217
|
-
if (result.message.includes("HTTP 401") || result.message.includes("HTTP 403")) {
|
|
218
|
-
throw new Error(`Authorization denied (${result.message}). Please check your configuration or run ae-cli auth login again.`);
|
|
219
|
-
}
|
|
220
|
-
continue;
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
throw new Error(
|
|
224
|
-
`Authorization timed out (not completed within ${expiresIn}s). Please run ae-cli auth login again.`
|
|
225
|
-
);
|
|
226
|
-
}
|
|
227
|
-
async function runDeviceFlow(host, options = {}, onStatus) {
|
|
228
|
-
const emit = (msg) => {
|
|
229
|
-
if (onStatus) onStatus(msg);
|
|
230
|
-
};
|
|
231
|
-
emit("Requesting authorization code from server...");
|
|
232
|
-
const auth = await authorizeDevice(host);
|
|
233
|
-
const userCode = auth.user_code;
|
|
234
|
-
const verifyUrl = buildVerificationUrl(host, userCode);
|
|
235
|
-
const expiresIn = auth.expires_in ?? 300;
|
|
236
|
-
emit("");
|
|
237
|
-
emit(`Verification code: ${userCode}`);
|
|
238
|
-
emit(`Open the following link in your browser to complete authorization:`);
|
|
239
|
-
emit(` ${verifyUrl}`);
|
|
240
|
-
emit("");
|
|
241
|
-
if (!options.noBrowser) {
|
|
242
|
-
const opened = openBrowser(verifyUrl);
|
|
243
|
-
if (opened) {
|
|
244
|
-
emit("(Attempted to open the link in your browser)");
|
|
245
|
-
} else {
|
|
246
|
-
emit("(Could not open browser automatically; please copy the link above manually)");
|
|
247
|
-
}
|
|
248
|
-
emit("");
|
|
249
|
-
}
|
|
250
|
-
return pollDeviceFlow(host, auth.device_code, { intervalMs: (auth.interval ?? 5) * 1e3, expiresIn }, onStatus);
|
|
251
|
-
}
|
|
252
|
-
function sleep(ms) {
|
|
253
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// src/commands/auth.ts
|
|
257
|
-
var HOST_OPTION_DESC = "Override active AE host URL (e.g., https://ta.thinkingdata.cn)";
|
|
258
|
-
function getExplicitAuthHostOverride(program, opts) {
|
|
259
|
-
return opts.host || program.opts().host;
|
|
260
|
-
}
|
|
261
|
-
function resolveAuthHost(program, opts) {
|
|
262
|
-
return resolveHost(getExplicitAuthHostOverride(program, opts));
|
|
263
|
-
}
|
|
264
|
-
async function validateExistingCliToken(host, cliToken) {
|
|
265
|
-
try {
|
|
266
|
-
await validateCliTokenOnServer(host, cliToken);
|
|
267
|
-
return true;
|
|
268
|
-
} catch (error) {
|
|
269
|
-
if (error instanceof CliTokenValidationUnavailableError) {
|
|
270
|
-
logger.warn(`auth status: ${error.message}; trusting the stored CLI token for compatibility`);
|
|
271
|
-
return true;
|
|
272
|
-
}
|
|
273
|
-
if (!(error instanceof SecureStoreAuthError)) {
|
|
274
|
-
throw error;
|
|
275
|
-
}
|
|
276
|
-
printError("auth", error.message);
|
|
277
|
-
process.exitCode = 1;
|
|
278
|
-
return false;
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
function activateHostAfterLogin(host, explicitHostOverride) {
|
|
282
|
-
if (!explicitHostOverride) return;
|
|
283
|
-
const normalized = normalizeUrl(host);
|
|
284
|
-
const config = loadConfig();
|
|
285
|
-
if (!config.hosts[normalized]) {
|
|
286
|
-
config.hosts[normalized] = { label: normalized };
|
|
287
|
-
}
|
|
288
|
-
config.activeHost = normalized;
|
|
289
|
-
saveConfig(config);
|
|
290
|
-
logger.info(`Active host set to ${normalized} after login`);
|
|
291
|
-
}
|
|
292
|
-
async function persistDeviceTokens(host, tokens) {
|
|
293
|
-
const DEFAULT_EXPIRES_IN = 72e3;
|
|
294
|
-
const MAX_EXPIRES_IN = 86400 * 365;
|
|
295
|
-
let expiresInSeconds = tokens.expires_in ?? DEFAULT_EXPIRES_IN;
|
|
296
|
-
if (!Number.isFinite(expiresInSeconds) || expiresInSeconds <= 0 || expiresInSeconds > MAX_EXPIRES_IN) {
|
|
297
|
-
logger.warn(`auth login: expires_in=${tokens.expires_in} is out of range, falling back to ${DEFAULT_EXPIRES_IN}s`);
|
|
298
|
-
expiresInSeconds = DEFAULT_EXPIRES_IN;
|
|
299
|
-
}
|
|
300
|
-
const expiresAt = new Date(Date.now() + expiresInSeconds * 1e3).toISOString();
|
|
301
|
-
const cliToken = await mintCliToken(host, tokens.access_token, false);
|
|
302
|
-
save(host, {
|
|
303
|
-
accessToken: tokens.access_token,
|
|
304
|
-
refreshToken: tokens.refresh_token ?? "",
|
|
305
|
-
accessExpiresAt: expiresAt,
|
|
306
|
-
cliToken
|
|
307
|
-
});
|
|
308
|
-
await runHostCompatCheck(getLocalCliPackageInfo(), host);
|
|
309
|
-
}
|
|
310
|
-
function loginSummary(host, tokens) {
|
|
311
|
-
return {
|
|
312
|
-
authenticated: true,
|
|
313
|
-
host,
|
|
314
|
-
token: tokens.access_token.slice(0, 8) + "..."
|
|
315
|
-
};
|
|
316
|
-
}
|
|
317
|
-
function resolveLoginTeClaudeBase(host) {
|
|
318
|
-
const base = host.replace(/\/+$/, "");
|
|
319
|
-
const basePath = process.env.TE_CLAUDE_BASE_PATH || "/agent";
|
|
320
|
-
return base.endsWith(basePath) ? base : base + basePath;
|
|
321
|
-
}
|
|
322
|
-
function registerAuth(program) {
|
|
323
|
-
const auth = program.command("auth").description("Authentication management");
|
|
324
|
-
auth.command("login").description("Log in to AE using the device code flow (cross-platform; --no-browser to print URL only)").option("--host <url>", HOST_OPTION_DESC).option("--no-browser", "Do not auto-open the browser; only print the authorization URL (headless / Linux)").option("--no-wait", "Request the device code and print the authorization URL as JSON, then exit without polling (for AI agents / split-flow). Resume with --device-code").option("--device-code <code>", "Resume a split-flow login: poll for the given device code until authorized, then save the token").action(async (opts) => {
|
|
325
|
-
const explicitHost = getExplicitAuthHostOverride(program, opts);
|
|
326
|
-
const host = resolveAuthHost(program, opts);
|
|
327
|
-
if (!host) {
|
|
328
|
-
printError("config", "No AE host configured.", missingAeHostHint());
|
|
329
|
-
process.exit(1);
|
|
330
|
-
}
|
|
331
|
-
const config = loadConfig();
|
|
332
|
-
if (!config.hosts[host]) {
|
|
333
|
-
config.hosts[host] = { label: host };
|
|
334
|
-
if (!config.activeHost) {
|
|
335
|
-
config.activeHost = host;
|
|
336
|
-
}
|
|
337
|
-
saveConfig(config);
|
|
338
|
-
logger.info(`Host config auto-saved for ${host}`);
|
|
339
|
-
process.stderr.write(`[ae-cli] Config saved for ${host}
|
|
340
|
-
`);
|
|
341
|
-
}
|
|
342
|
-
const noBrowser = !opts.browser;
|
|
343
|
-
const fmt = program.opts().format || "json";
|
|
344
|
-
const teClaudeBase = resolveLoginTeClaudeBase(host);
|
|
345
|
-
const emit = (msg) => process.stderr.write(`[ae-cli] ${msg}
|
|
346
|
-
`);
|
|
347
|
-
try {
|
|
348
|
-
if (opts.deviceCode) {
|
|
349
|
-
const tokens2 = await pollDeviceFlow(teClaudeBase, opts.deviceCode, {}, emit);
|
|
350
|
-
await persistDeviceTokens(host, tokens2);
|
|
351
|
-
activateHostAfterLogin(host, explicitHost);
|
|
352
|
-
logger.info(`Device flow login successful for ${host} (resumed)`);
|
|
353
|
-
emit("Login successful! Token saved securely.");
|
|
354
|
-
await printOutput(loginSummary(host, tokens2), fmt);
|
|
355
|
-
return;
|
|
356
|
-
}
|
|
357
|
-
if (opts.wait === false) {
|
|
358
|
-
const authResp = await authorizeDevice(teClaudeBase);
|
|
359
|
-
await printOutput(
|
|
360
|
-
{
|
|
361
|
-
device_code: authResp.device_code,
|
|
362
|
-
user_code: authResp.user_code,
|
|
363
|
-
verification_url: buildVerificationUrl(teClaudeBase, authResp.user_code),
|
|
364
|
-
interval: authResp.interval,
|
|
365
|
-
expires_in: authResp.expires_in,
|
|
366
|
-
next: `Open verification_url in a browser to authorize, then run: ae-cli auth login --device-code ${authResp.device_code}`
|
|
367
|
-
},
|
|
368
|
-
fmt
|
|
369
|
-
);
|
|
370
|
-
return;
|
|
371
|
-
}
|
|
372
|
-
const tokens = await runDeviceFlow(teClaudeBase, { noBrowser }, emit);
|
|
373
|
-
await persistDeviceTokens(host, tokens);
|
|
374
|
-
activateHostAfterLogin(host, explicitHost);
|
|
375
|
-
logger.info(`Device flow login successful for ${host}`);
|
|
376
|
-
emit("Login successful! Token saved securely.");
|
|
377
|
-
await printOutput(loginSummary(host, tokens), fmt);
|
|
378
|
-
} catch (err) {
|
|
379
|
-
if (err instanceof DeviceFlowUnsupportedError) {
|
|
380
|
-
printError(
|
|
381
|
-
"auth",
|
|
382
|
-
err.message,
|
|
383
|
-
"Upgrade the AE server to a version that supports device login."
|
|
384
|
-
);
|
|
385
|
-
process.exit(1);
|
|
386
|
-
}
|
|
387
|
-
if (err instanceof PermissionError) {
|
|
388
|
-
printError("permission", err.message, err.hint, err.code);
|
|
389
|
-
process.exit(1);
|
|
390
|
-
}
|
|
391
|
-
printError("auth", err.message);
|
|
392
|
-
process.exit(1);
|
|
393
|
-
}
|
|
394
|
-
});
|
|
395
|
-
auth.command("status").description("Show authentication status for a host (--host or active host)").option("--host <url>", HOST_OPTION_DESC).action(async (opts) => {
|
|
396
|
-
const host = resolveAuthHost(program, opts);
|
|
397
|
-
if (!host) {
|
|
398
|
-
await printOutput(
|
|
399
|
-
{
|
|
400
|
-
authenticated: false,
|
|
401
|
-
host: "(none)",
|
|
402
|
-
hint: missingAeHostHint(),
|
|
403
|
-
next_steps: missingAeHostGuidance()
|
|
404
|
-
},
|
|
405
|
-
program.opts().format || "json"
|
|
406
|
-
);
|
|
407
|
-
return;
|
|
408
|
-
}
|
|
409
|
-
const secureEntry = load(host);
|
|
410
|
-
if (secureEntry) {
|
|
411
|
-
if (secureEntry.cliToken && !await validateExistingCliToken(host, secureEntry.cliToken)) {
|
|
412
|
-
return;
|
|
413
|
-
}
|
|
414
|
-
const pastStaticExpiry = new Date(secureEntry.accessExpiresAt).getTime() < Date.now();
|
|
415
|
-
await printOutput(
|
|
416
|
-
{
|
|
417
|
-
authenticated: true,
|
|
418
|
-
host,
|
|
419
|
-
source: "secure-store",
|
|
420
|
-
accessExpiresAt: secureEntry.accessExpiresAt,
|
|
421
|
-
accessExpiresAtNote: "advisory: access token auto-slides on use server-side; re-login only if a command returns an auth error",
|
|
422
|
-
pastStaticExpiry,
|
|
423
|
-
hasCliToken: !!secureEntry.cliToken
|
|
424
|
-
},
|
|
425
|
-
program.opts().format || "json"
|
|
426
|
-
);
|
|
427
|
-
return;
|
|
428
|
-
}
|
|
429
|
-
const fallbackCliToken = getFallbackCliToken(host);
|
|
430
|
-
if (fallbackCliToken) {
|
|
431
|
-
if (!await validateExistingCliToken(host, fallbackCliToken)) {
|
|
432
|
-
return;
|
|
433
|
-
}
|
|
434
|
-
await printOutput(
|
|
435
|
-
{
|
|
436
|
-
authenticated: true,
|
|
437
|
-
host,
|
|
438
|
-
source: "sandbox-cli-token",
|
|
439
|
-
hasCliToken: true,
|
|
440
|
-
note: "sandbox-provisioned cli-token.json; no user access token in secure-store"
|
|
441
|
-
},
|
|
442
|
-
program.opts().format || "json"
|
|
443
|
-
);
|
|
444
|
-
return;
|
|
445
|
-
}
|
|
446
|
-
await printOutput({ authenticated: false, host, source: "none" }, program.opts().format || "json");
|
|
447
|
-
});
|
|
448
|
-
auth.command("logout").description("Clear stored credentials (access token, refresh token, and CLI token) for a host (--host or active host)").option("--host <url>", HOST_OPTION_DESC).action(async (opts) => {
|
|
449
|
-
const host = resolveAuthHost(program, opts);
|
|
450
|
-
if (!host) {
|
|
451
|
-
printError("config", "No AE host configured.", missingAeHostHint());
|
|
452
|
-
process.exit(1);
|
|
453
|
-
}
|
|
454
|
-
clearCliToken(host);
|
|
455
|
-
clear(host);
|
|
456
|
-
process.stderr.write(`[ae-cli] Credentials cleared for ${host}
|
|
457
|
-
`);
|
|
458
|
-
await printOutput({ cleared: true, host }, program.opts().format || "json");
|
|
459
|
-
});
|
|
460
|
-
}
|
|
461
|
-
export {
|
|
462
|
-
activateHostAfterLogin,
|
|
463
|
-
registerAuth,
|
|
464
|
-
resolveLoginTeClaudeBase
|
|
465
|
-
};
|
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
|
-
};
|