@thinkingai/ae-cli 6.1.24 → 6.1.26
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 +35 -10
- package/README.zh.md +32 -10
- package/bin/ae-cli.js +6 -1
- package/cli-installation-guide.md +91 -0
- package/dist/{auth-XZNXFRJS.js → auth-YHMC3GDH.js} +168 -10
- package/dist/automation-NWWVLWG2.js +459 -0
- package/dist/{capability-FYTU3X6L.js → capability-3QLLIVFB.js} +11 -6
- package/dist/chunk-4655PUNX.js +43 -0
- package/dist/{chunk-4NRCTUZJ.js → chunk-5XJYLEVC.js} +4 -4
- package/dist/{chunk-JOCDD4ON.js → chunk-7QXISROD.js} +1 -1
- package/dist/{chunk-EQ255HKO.js → chunk-AP7YP2NX.js} +2 -2
- package/dist/{chunk-DVMLWQD4.js → chunk-CT7QDJS6.js} +2 -2
- package/dist/chunk-D6ACFILC.js +56 -0
- package/dist/{chunk-HFGZGYCG.js → chunk-EOYSKMXN.js} +264 -10
- package/dist/{chunk-57RRFUEZ.js → chunk-FMNNGNYO.js} +43 -8
- package/dist/{chunk-7XGFSUOM.js → chunk-GIK6EDUG.js} +291 -84
- package/dist/chunk-HVHR4SCP.js +133 -0
- package/dist/{chunk-CCS66K4K.js → chunk-KAEHZZH4.js} +3 -3
- package/dist/{chunk-DX5CDO34.js → chunk-KFNMQTZI.js} +580 -214
- package/dist/{chunk-HBGADCKA.js → chunk-PLE722AJ.js} +28 -11
- package/dist/chunk-RK6GLDGI.js +27 -0
- package/dist/{chunk-V2NUHPXF.js → chunk-S6M42KND.js} +1 -1
- package/dist/{chunk-7FTTULED.js → chunk-UQ77JXYH.js} +7 -3
- package/dist/{chunk-MAPZ7VB6.js → chunk-WNGFSIOB.js} +20 -1
- package/dist/{chunk-VYXXOKYS.js → chunk-XCHAYMSF.js} +7 -4
- package/dist/{community-report-client-CU22W53K.js → community-report-client-PEERQCX2.js} +1 -1
- package/dist/{config-XO3LI3SA.js → config-AHPMEW5A.js} +25 -6
- package/dist/context-YAE3QXP3.js +136 -0
- package/dist/{data-integration-HNGUKFIC.js → data-integration-HESZET7E.js} +6 -6
- package/dist/index.js +167 -53
- package/dist/{local-data-upload-client-GTRIT64F.js → local-data-upload-client-TWMHFLUA.js} +1 -1
- package/dist/memory-62BJOULV.js +580 -0
- package/dist/{metadata-DI5BMYFU.js → metadata-C7CEDHOJ.js} +7 -7
- package/dist/{model-RNPQXIBI.js → model-SBV2JFJI.js} +9 -5
- package/dist/{personal-semantic-preference-5C3WUXFI.js → personal-semantic-preference-QECDPHND.js} +119 -11
- package/dist/project-semantic-Z4QAMBWF.js +356 -0
- package/dist/{sync-O45NXEQY.js → sync-SU33KYX7.js} +12 -8
- package/dist/{te-agent-K5MSPNHJ.js → te-agent-F5QBZSQA.js} +423 -494
- package/dist/{te-analysis-AISWCOOZ.js → te-analysis-2ECAAIFK.js} +1227 -154
- package/dist/{te-community-4OYU3BDA.js → te-community-PFLDLPP4.js} +3 -3
- package/dist/{te-dataops-NFIPB6FX.js → te-dataops-FOQSRIAZ.js} +644 -276
- package/dist/{te-engage-BAM3GBFX.js → te-engage-QKUGIWSR.js} +6 -6
- package/dist/{te-experiment-GUTZXZ5Z.js → te-experiment-26PXR2UO.js} +6 -6
- package/dist/{te-kb-3TS73NO6.js → te-kb-MHDOST62.js} +666 -244
- package/dist/{te-system-STMHLS5J.js → te-system-J2FQF5ER.js} +6 -6
- package/dist/{te-team-3I5AOX3N.js → te-team-5UOZPTU3.js} +6 -6
- package/dist/things-VPPXXLHB.js +1091 -0
- package/dist/{update-RZXLPCRH.js → update-EEUTEI6R.js} +59 -51
- package/dist/workspace-O3EEIYQM.js +554 -0
- package/package.json +30 -11
- package/skills/ae-agent/SKILL.md +95 -140
- package/skills/ae-agent/references/add-model.md +1 -1
- package/skills/ae-agent/references/create-agent.md +1 -1
- package/skills/ae-agent/references/find-archived-conversations.md +12 -5
- package/skills/ae-agent/references/get-agent.md +1 -1
- package/skills/ae-agent/references/list-agents.md +2 -2
- package/skills/ae-agent/references/list-models.md +2 -2
- package/skills/ae-agent/references/notification.md +40 -0
- package/skills/ae-agent/references/restore-conversation.md +3 -1
- package/skills/ae-analysis/SKILL.md +55 -23
- package/skills/ae-analysis/references/adhoc_export.md +1 -1
- package/skills/ae-analysis/references/adhoc_run.md +1 -1
- package/skills/ae-analysis/references/agent_review_submit_to_page.md +2 -0
- package/skills/ae-analysis/references/ai_models/event.md +6 -1
- package/skills/ae-analysis/references/ai_models/funnel.md +12 -4
- package/skills/ae-analysis/references/ai_models/heat_map.md +23 -0
- package/skills/ae-analysis/references/ai_models/interval.md +24 -0
- package/skills/ae-analysis/references/ai_models/path.md +20 -0
- package/skills/ae-analysis/references/ai_models/prop_analysis.md +2 -0
- package/skills/ae-analysis/references/ai_models/rank_list.md +30 -0
- package/skills/ae-analysis/references/ai_models/retention.md +45 -1
- package/skills/ae-analysis/references/ai_models/revenue.md +20 -2
- package/skills/ae-analysis/references/ai_models/session.md +76 -0
- package/skills/ae-analysis/references/ai_models/tag.md +2 -0
- package/skills/ae-analysis/references/ai_models.md +100 -3
- package/skills/ae-analysis/references/analysis_data_retrieval.md +4 -0
- package/skills/ae-analysis/references/analysis_drilldown_contract.md +4 -1
- package/skills/ae-analysis/references/asset_batch_info_export.md +10 -0
- package/skills/ae-analysis/references/asset_batch_sql_export.md +13 -1
- package/skills/ae-analysis/references/asset_export.md +12 -1
- package/skills/ae-analysis/references/asset_list.md +1 -0
- package/skills/ae-analysis/references/asset_search.md +11 -5
- package/skills/ae-analysis/references/bi_panel_list.md +1 -1
- package/skills/ae-analysis/references/catalog_list.md +1 -1
- package/skills/ae-analysis/references/collaboration.md +48 -0
- package/skills/ae-analysis/references/command_index.md +29 -19
- package/skills/ae-analysis/references/dashboard_list.md +4 -4
- package/skills/ae-analysis/references/dashboard_move.md +21 -0
- package/skills/ae-analysis/references/dashboard_report_add.md +2 -0
- package/skills/ae-analysis/references/dashboard_report_data_export.md +2 -0
- package/skills/ae-analysis/references/dashboard_report_get.md +19 -0
- package/skills/ae-analysis/references/dashboard_report_update.md +27 -0
- package/skills/ae-analysis/references/dashboard_update.md +1 -1
- package/skills/ae-analysis/references/drilldown_session_details_run.md +41 -0
- package/skills/ae-analysis/references/event_export.md +3 -1
- package/skills/ae-analysis/references/event_report_presentation.md +76 -0
- package/skills/ae-analysis/references/feedback_submit.md +37 -0
- package/skills/ae-analysis/references/governance_recommendation_auto_review.md +77 -0
- package/skills/ae-analysis/references/governance_recommendation_export.md +11 -0
- package/skills/ae-analysis/references/metadata_resolution.md +18 -3
- package/skills/ae-analysis/references/metric_export.md +3 -1
- package/skills/ae-analysis/references/metric_list.md +1 -1
- package/skills/ae-analysis/references/operation_record_export.md +10 -0
- package/skills/ae-analysis/references/personal_semantic_preference_add.md +2 -2
- package/skills/ae-analysis/references/personal_semantic_preference_get.md +4 -4
- package/skills/ae-analysis/references/personal_semantic_preference_list.md +4 -4
- package/skills/ae-analysis/references/personal_semantic_preference_review.md +9 -0
- package/skills/ae-analysis/references/personal_semantic_preference_review_list.md +7 -0
- package/skills/ae-analysis/references/personal_semantic_preference_submit.md +7 -0
- package/skills/ae-analysis/references/project_semantic_knowledge_wiki.md +91 -32
- package/skills/ae-analysis/references/project_semantic_knowledge_wiki_plan_schema.md +99 -1
- package/skills/ae-analysis/references/property_export.md +3 -1
- package/skills/ae-analysis/references/report_create.md +18 -4
- package/skills/ae-analysis/references/report_data_export.md +6 -0
- package/skills/ae-analysis/references/report_get.md +7 -1
- package/skills/ae-analysis/references/report_list.md +5 -5
- package/skills/ae-analysis/references/report_update.md +21 -4
- package/skills/ae-analysis/references/sql_table_columns.md +4 -4
- package/skills/ae-analysis/references/sql_table_list.md +5 -5
- package/skills/ae-analysis/references/system_query_monitor_overview.md +2 -0
- package/skills/ae-analysis/references/system_query_task_cancel.md +2 -0
- package/skills/ae-analysis/references/system_query_task_export.md +6 -2
- package/skills/ae-analysis/references/system_query_task_get.md +2 -0
- package/skills/ae-analysis/references/system_query_task_list.md +7 -3
- package/skills/ae-analysis/references/system_query_task_options.md +2 -2
- package/skills/ae-analysis/references/system_query_task_realtime_list.md +15 -0
- package/skills/ae-analysis/references/user_cluster_models.md +8 -0
- package/skills/ae-analysis/references/user_tag_create.md +7 -1
- package/skills/ae-analysis/references/user_tag_get.md +1 -1
- package/skills/ae-analysis/references/user_tag_models.md +20 -2
- package/skills/ae-analysis/references/user_tag_refresh.md +1 -1
- package/skills/ae-analysis/references/user_tag_update.md +2 -2
- package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/build-empty-skeleton.mjs +105 -0
- package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/build-project-semantic-wiki.mjs +669 -29
- package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/company-kb.mjs +21 -8
- package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/default-compile-rules.md +28 -10
- package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/default-compile-schema.md +66 -0
- package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/generate-build-ir.mjs +285 -24
- package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/package-wiki-source-zip.mjs +262 -30
- package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/plan-kb-source-sync.mjs +1 -1
- package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/precompiled-source.mjs +46 -2
- package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/prepare-compile-schema.mjs +64 -0
- package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/refresh-state-parts.mjs +91 -0
- package/skills/ae-automation/SKILL.md +16 -0
- package/skills/ae-automation/references/cli.md +53 -0
- package/skills/ae-automation/references/command_index.md +17 -0
- package/skills/ae-capability/SKILL.md +73 -2
- package/skills/ae-capability/references/collaboration.md +48 -0
- package/skills/ae-community/SKILL.md +5 -1
- package/skills/ae-community/references/collaboration.md +48 -0
- package/skills/ae-current-context/SKILL.md +3 -2
- package/skills/ae-data-integration/SKILL.md +4 -0
- package/skills/ae-data-integration/references/collaboration.md +48 -0
- package/skills/ae-dataops/SKILL.md +54 -74
- package/skills/ae-dataops/references/collaboration.md +48 -0
- package/skills/ae-dataops/references/dataops-backfill.md +23 -18
- package/skills/ae-dataops/references/dataops-flow-create.md +74 -23
- package/skills/ae-dataops/references/dataops-flow-monitor.md +45 -13
- package/skills/ae-dataops/references/dataops-integration.md +57 -30
- package/skills/ae-dataops/references/dataops-query.md +21 -4
- package/skills/ae-dataops/references/dataops-table.md +143 -11
- package/skills/ae-engage/SKILL.md +21 -6
- package/skills/ae-engage/references/channel-mgmt.md +3 -3
- package/skills/ae-engage/references/collaboration.md +48 -0
- package/skills/ae-engage/references/scene-config-channel.md +4 -4
- package/skills/ae-generate-tracking-plan/SKILL.md +4 -0
- package/skills/ae-generate-tracking-plan/references/collaboration.md +48 -0
- package/skills/ae-kb/SKILL.md +70 -12
- package/skills/ae-kb/references/analysis-workflow.md +73 -36
- package/skills/ae-kb/references/collaboration.md +48 -0
- package/skills/ae-kb/references/query-workflow.md +67 -33
- package/skills/ae-kb/references/schema-import.md +32 -0
- package/skills/ae-kb/references/source-mutations.md +6 -4
- package/skills/ae-kb/references/text-sources.md +53 -0
- package/skills/ae-kb-discovery/SKILL.md +8 -3
- package/skills/ae-kb-discovery/references/collaboration.md +48 -0
- package/skills/ae-metadata/SKILL.md +20 -12
- package/skills/ae-metadata/references/collaboration.md +48 -0
- package/skills/ae-team/SKILL.md +23 -17
- package/skills/ae-things/SKILL.md +85 -0
- package/skills/ae-things/references/cli.md +103 -0
- package/skills/ae-things-progress/SKILL.md +43 -0
- package/skills/ae-things-review/SKILL.md +42 -0
- package/skills/ae-workspace/SKILL.md +43 -0
- package/skills/ae-workspace/references/commands.md +51 -0
- package/dist/chunk-E2JKXMVO.js +0 -29
- package/dist/context-IFJI3LIT.js +0 -46
- package/dist/memory-QGPSNCQS.js +0 -892
- package/dist/project-semantic-3LF6Q6JZ.js +0 -1119
- package/skills/ae-agent/references/create-automation.md +0 -125
- package/skills/ae-agent/references/list-automations.md +0 -56
- package/skills/ae-agent/references/update-automation.md +0 -107
- package/skills/ae-project-semantic/SKILL.md +0 -193
- package/skills/ae-project-semantic/references/query-routing-v5.md +0 -165
- package/skills/ae-project-semantic/references/recommendation-quality.md +0 -68
|
@@ -1,21 +1,105 @@
|
|
|
1
1
|
import { createRequire as __createRequire } from 'node:module'; const require = __createRequire(import.meta.url);
|
|
2
2
|
import {
|
|
3
3
|
peekCliToken
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-GIK6EDUG.js";
|
|
5
5
|
import {
|
|
6
6
|
getActiveHost,
|
|
7
7
|
getConfigDir,
|
|
8
8
|
isAeSandboxRuntime,
|
|
9
9
|
normalizeUrl
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-UQ77JXYH.js";
|
|
11
11
|
import {
|
|
12
12
|
logger
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-WNGFSIOB.js";
|
|
14
14
|
|
|
15
|
-
// src/core/
|
|
16
|
-
import
|
|
15
|
+
// src/core/install-process.ts
|
|
16
|
+
import spawn from "cross-spawn";
|
|
17
17
|
import fs from "fs";
|
|
18
18
|
import path from "path";
|
|
19
|
+
var INSTALL_TIMEOUT_MS = 12e4;
|
|
20
|
+
var SKILLS_TOOL = "skills@1.5.12";
|
|
21
|
+
var defaultInstallRunner = (command, args, timeoutMs) => {
|
|
22
|
+
const result = spawn.sync(command, args, {
|
|
23
|
+
encoding: "utf8",
|
|
24
|
+
maxBuffer: 8 * 1024 * 1024,
|
|
25
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
26
|
+
timeout: timeoutMs,
|
|
27
|
+
windowsHide: true,
|
|
28
|
+
env: {
|
|
29
|
+
...process.env,
|
|
30
|
+
DISABLE_TELEMETRY: "1",
|
|
31
|
+
NO_COLOR: "1",
|
|
32
|
+
// Select installed clients, not only the AI that launched AE CLI.
|
|
33
|
+
...args[1] === SKILLS_TOOL && args[2] === "add" ? { AI_AGENT: "ae-cli" } : {}
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
return { ...result, stdout: result.stdout || "", stderr: result.stderr || "" };
|
|
37
|
+
};
|
|
38
|
+
function resolveNodeTool(tool, execPath = process.execPath, platform = process.platform) {
|
|
39
|
+
const filename = platform === "win32" ? `${tool}.cmd` : tool;
|
|
40
|
+
const sibling = path.join(path.dirname(execPath), filename);
|
|
41
|
+
return fs.existsSync(sibling) ? sibling : filename;
|
|
42
|
+
}
|
|
43
|
+
function commandSucceeded(result) {
|
|
44
|
+
return !result.error && !result.signal && result.status === 0;
|
|
45
|
+
}
|
|
46
|
+
function exactVersion(raw) {
|
|
47
|
+
const version = raw.trim().replace(/^v/i, "");
|
|
48
|
+
if (!/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/.test(version)) {
|
|
49
|
+
throw new Error("Expected an exact version such as 6.0.49 or 6.1.9.");
|
|
50
|
+
}
|
|
51
|
+
return version;
|
|
52
|
+
}
|
|
53
|
+
function installHost(raw) {
|
|
54
|
+
if (!raw) return null;
|
|
55
|
+
const normalized = normalizeUrl(raw.trim());
|
|
56
|
+
try {
|
|
57
|
+
if (raw.includes("://") && !/^https?:\/\//.test(raw.trim())) throw new Error();
|
|
58
|
+
const url = new URL(normalized);
|
|
59
|
+
if (!["https:", "http:"].includes(url.protocol) || !url.hostname || url.username || url.password || url.search || url.hash || /[\s'"`$\\<>]/.test(normalized)) throw new Error();
|
|
60
|
+
return normalized;
|
|
61
|
+
} catch {
|
|
62
|
+
throw new Error("Host must be an HTTP(S) URL without credentials, query parameters, or fragments.");
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function classifyInstallFailure(result) {
|
|
66
|
+
const text = `${result.error?.message || ""}
|
|
67
|
+
${result.stderr}
|
|
68
|
+
${result.stdout}`;
|
|
69
|
+
if (result.error?.code === "ETIMEDOUT" || /timed?\s*out|ETIMEDOUT/i.test(text)) return "timeout";
|
|
70
|
+
if (/EACCES|EPERM|permission denied|access is denied/i.test(text)) return "permission";
|
|
71
|
+
if (/EAI_AGAIN|ENOTFOUND|ECONNRESET|ECONNREFUSED|network|unable to access|could not resolve|certificate|SSL/i.test(text)) return "network";
|
|
72
|
+
if (/E404|404 Not Found|no matching version|couldn't find remote ref/i.test(text)) return "package_missing";
|
|
73
|
+
return "unknown";
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// src/core/package-info.ts
|
|
77
|
+
import fs2 from "fs";
|
|
78
|
+
import path2 from "path";
|
|
79
|
+
import { fileURLToPath } from "url";
|
|
80
|
+
function getLocalCliPackageInfo() {
|
|
81
|
+
let dir = path2.dirname(fileURLToPath(import.meta.url));
|
|
82
|
+
for (let i = 0; i < 8; i += 1) {
|
|
83
|
+
const candidate = path2.join(dir, "package.json");
|
|
84
|
+
try {
|
|
85
|
+
if (fs2.existsSync(candidate)) {
|
|
86
|
+
const pkg = JSON.parse(fs2.readFileSync(candidate, "utf8"));
|
|
87
|
+
if (pkg.name && pkg.version && String(pkg.name).includes("ae-cli")) {
|
|
88
|
+
return { name: pkg.name, version: pkg.version, root: dir };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
} catch {
|
|
92
|
+
}
|
|
93
|
+
const parent = path2.dirname(dir);
|
|
94
|
+
if (parent === dir) break;
|
|
95
|
+
dir = parent;
|
|
96
|
+
}
|
|
97
|
+
return { name: "@tant/ae-cli", version: "0.0.0", root: "" };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// src/core/version-sync.ts
|
|
101
|
+
import fs5 from "fs";
|
|
102
|
+
import path5 from "path";
|
|
19
103
|
|
|
20
104
|
// src/core/update-check.ts
|
|
21
105
|
function shouldSkipUpdateCheck(argv = process.argv) {
|
|
@@ -84,8 +168,6 @@ var HISTORICAL_CLUSTER_TO_PUBLIC_PIN = {
|
|
|
84
168
|
|
|
85
169
|
// src/core/version-compat.ts
|
|
86
170
|
var OPEN_SOURCE_AE_CLI_PACKAGE = "@thinkingai/ae-cli";
|
|
87
|
-
var OPEN_SOURCE_NPM_REGISTRY = "https://registry.npmjs.org";
|
|
88
|
-
var AE_CLI_SKILLS_REPO = "ThinkingAIAgenticEngine/ae-cli";
|
|
89
171
|
var PUBLISHED_EXTERNAL_VERSIONS = /* @__PURE__ */ new Set([
|
|
90
172
|
"1.0.15",
|
|
91
173
|
"1.0.18",
|
|
@@ -183,16 +265,18 @@ function formatPinCommands(expectedVersion) {
|
|
|
183
265
|
const ver = resolvePinTarget(expectedVersion);
|
|
184
266
|
if (!ver) return [];
|
|
185
267
|
return [
|
|
186
|
-
`
|
|
187
|
-
`npx skills add ${AE_CLI_SKILLS_REPO}#v${ver} -g -y`
|
|
268
|
+
`npx ${OPEN_SOURCE_AE_CLI_PACKAGE}@latest update --target ${ver}`
|
|
188
269
|
];
|
|
189
270
|
}
|
|
190
271
|
function formatUpgradeCommands(expectedVersion) {
|
|
191
|
-
|
|
192
|
-
|
|
272
|
+
let ver;
|
|
273
|
+
try {
|
|
274
|
+
ver = exactVersion(expectedVersion);
|
|
275
|
+
} catch {
|
|
276
|
+
return [];
|
|
277
|
+
}
|
|
193
278
|
return [
|
|
194
|
-
`
|
|
195
|
-
`npx skills add ${AE_CLI_SKILLS_REPO}#v${ver} -g -y`
|
|
279
|
+
`npx ${OPEN_SOURCE_AE_CLI_PACKAGE}@latest update --target ${ver}`
|
|
196
280
|
];
|
|
197
281
|
}
|
|
198
282
|
function formatUnifiedUpdateCommand() {
|
|
@@ -241,22 +325,335 @@ function formatCompatNotice(verdict) {
|
|
|
241
325
|
].join("\n");
|
|
242
326
|
}
|
|
243
327
|
|
|
328
|
+
// src/core/installation.ts
|
|
329
|
+
import fs4 from "fs";
|
|
330
|
+
import path4 from "path";
|
|
331
|
+
|
|
332
|
+
// src/core/skills-install.ts
|
|
333
|
+
import fs3 from "fs";
|
|
334
|
+
import os from "os";
|
|
335
|
+
import path3 from "path";
|
|
336
|
+
import { parseDocument } from "yaml";
|
|
337
|
+
function globalSkillsDirectory() {
|
|
338
|
+
return path3.join(os.homedir(), ".agents", "skills");
|
|
339
|
+
}
|
|
340
|
+
function skillName(content) {
|
|
341
|
+
const frontmatter = content.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/);
|
|
342
|
+
if (!frontmatter) throw new Error("Missing YAML frontmatter.");
|
|
343
|
+
const parsed = parseDocument(frontmatter[1], { uniqueKeys: true });
|
|
344
|
+
if (parsed.errors.length) throw new Error("Invalid YAML frontmatter.");
|
|
345
|
+
const data = parsed.toJS({ maxAliasCount: 100 });
|
|
346
|
+
if (!data || typeof data.name !== "string" || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(data.name) || typeof data.description !== "string" || !data.description.trim()) throw new Error("Invalid Skill metadata.");
|
|
347
|
+
return data.name;
|
|
348
|
+
}
|
|
349
|
+
function readFiles(root, prefix = "", files = /* @__PURE__ */ new Map()) {
|
|
350
|
+
for (const entry of fs3.readdirSync(path3.join(root, prefix), { withFileTypes: true })) {
|
|
351
|
+
if (entry.name.startsWith(".") || ["metadata.json", "__pycache__", "__pypackages__"].includes(entry.name)) continue;
|
|
352
|
+
const relative = path3.join(prefix, entry.name);
|
|
353
|
+
if (entry.isSymbolicLink()) throw new Error("Bundled Skills must contain regular files.");
|
|
354
|
+
if (entry.isDirectory()) readFiles(root, relative, files);
|
|
355
|
+
else if (entry.isFile()) files.set(relative, fs3.readFileSync(path3.join(root, relative)));
|
|
356
|
+
}
|
|
357
|
+
return files;
|
|
358
|
+
}
|
|
359
|
+
function readBundledSkills(directory) {
|
|
360
|
+
const skills = fs3.readdirSync(directory, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => {
|
|
361
|
+
try {
|
|
362
|
+
const files = readFiles(path3.join(directory, entry.name));
|
|
363
|
+
return { name: skillName(files.get("SKILL.md").toString("utf8")), files };
|
|
364
|
+
} catch {
|
|
365
|
+
const name = /^[a-z0-9-]+$/.test(entry.name) ? entry.name : "(invalid name)";
|
|
366
|
+
throw new Error(`Invalid bundled Skill ${name}: missing files or YAML/frontmatter error.`);
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
if (!skills.length || new Set(skills.map((skill) => skill.name)).size !== skills.length) {
|
|
370
|
+
throw new Error("Bundled Skills are empty or contain duplicate names.");
|
|
371
|
+
}
|
|
372
|
+
return skills;
|
|
373
|
+
}
|
|
374
|
+
function verifyGlobalSkills(skills, directory) {
|
|
375
|
+
return skills.flatMap((skill) => {
|
|
376
|
+
let reason = "";
|
|
377
|
+
try {
|
|
378
|
+
const target = path3.join(directory, skill.name);
|
|
379
|
+
if (skillName(fs3.readFileSync(path3.join(target, "SKILL.md"), "utf8")) !== skill.name) {
|
|
380
|
+
reason = "Installed Skill name does not match the bundled Skill.";
|
|
381
|
+
} else {
|
|
382
|
+
for (const [file, expected] of skill.files) {
|
|
383
|
+
if (!fs3.readFileSync(path3.join(target, file)).equals(expected)) {
|
|
384
|
+
reason = "Installed files do not match the CLI package version.";
|
|
385
|
+
break;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
} catch (error) {
|
|
390
|
+
reason = error.code === "ENOENT" ? "Expected Skill file is missing." : "Installed Skill has invalid YAML/frontmatter or unreadable files.";
|
|
391
|
+
}
|
|
392
|
+
return reason ? [{ skill: skill.name, reason }] : [];
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
function skillProcessProblem(result) {
|
|
396
|
+
const output = `${result.stdout}
|
|
397
|
+
${result.stderr}`.replace(/\x1b\[[0-9;]*m/g, "");
|
|
398
|
+
if (/(?:YAML|frontmatter)[^\n]*(?:parse|invalid|error)|(?:parse|invalid|error)[^\n]*(?:YAML|frontmatter)/i.test(output)) {
|
|
399
|
+
return "Skills reported a YAML/frontmatter parsing error.";
|
|
400
|
+
}
|
|
401
|
+
const lines = output.split(/\r?\n/);
|
|
402
|
+
const ignored = lines.filter((line) => /(?:PromptScript|Eve) does not support global skill installation\s*$/.test(line));
|
|
403
|
+
const failures = [...output.matchAll(/Failed to install (\d+)/g)].reduce((sum, match) => sum + Number(match[1]), 0);
|
|
404
|
+
const remaining = lines.filter((line) => !ignored.includes(line) && !/Failed to install \d+/.test(line)).join("\n");
|
|
405
|
+
if (failures > ignored.length || /✗|(?:^|\n)[^\w\n]*(?:Error\b|Installation failed\b|Failed\b)/i.test(remaining)) {
|
|
406
|
+
return "Skills installation failed for a supported client. Check directory permissions and retry.";
|
|
407
|
+
}
|
|
408
|
+
if (commandSucceeded(result)) return null;
|
|
409
|
+
if (!result.error && !result.signal && ignored.length > 0 && failures === ignored.length && !/EACCES|EPERM|ETIMEDOUT|ENOTFOUND|ECONN|network|permission denied/i.test(remaining)) return null;
|
|
410
|
+
return "Skills process failed. Check network access and directory permissions, then retry.";
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// src/core/installation.ts
|
|
414
|
+
function distribution(packageName) {
|
|
415
|
+
if (packageName === "@thinkingai/ae-cli") return { registry: "https://registry.npmjs.org", repository: "ThinkingAIAgenticEngine/ae-cli" };
|
|
416
|
+
if (packageName === "@tant/ae-cli") return { registry: "https://npm.thinkingdata.cn:3443", repository: "http://10.27.249.150:8888/te-ai/te-cli.git" };
|
|
417
|
+
throw new Error("Unsupported CLI package. Use an official AE CLI distribution.");
|
|
418
|
+
}
|
|
419
|
+
function newInstallationReport(packageName, version, dryRun = false) {
|
|
420
|
+
return {
|
|
421
|
+
ok: false,
|
|
422
|
+
dry_run: dryRun,
|
|
423
|
+
cli: { package: packageName, version, path: null, verified: false, action: "pending", warnings: [] },
|
|
424
|
+
skills: {
|
|
425
|
+
directory: globalSkillsDirectory(),
|
|
426
|
+
expected_count: 0,
|
|
427
|
+
installed_count: 0,
|
|
428
|
+
missing: [],
|
|
429
|
+
source: null,
|
|
430
|
+
version,
|
|
431
|
+
status: "pending",
|
|
432
|
+
failures: [],
|
|
433
|
+
attempts: []
|
|
434
|
+
},
|
|
435
|
+
auth: { host: null, authenticated: null, warnings: [] },
|
|
436
|
+
next_steps: []
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
function failInstallation(report, stage, message, cause = "validation") {
|
|
440
|
+
report.ok = false;
|
|
441
|
+
report.error = { type: "config", code: "AE_CLI_INSTALL_FAILED", message, stage, cause };
|
|
442
|
+
if (!report.next_steps.length) report.next_steps.push("Fix the reported problem and rerun the same install or update command.");
|
|
443
|
+
return report;
|
|
444
|
+
}
|
|
445
|
+
function installPlan(packageName, version, root = "<npm-root>") {
|
|
446
|
+
const config = distribution(packageName);
|
|
447
|
+
const target = exactVersion(version);
|
|
448
|
+
const source = path4.join(root, packageName, "skills");
|
|
449
|
+
return [
|
|
450
|
+
`npm install -g ${packageName}@${target} --registry=${config.registry}`,
|
|
451
|
+
`npx -y ${SKILLS_TOOL} add ${JSON.stringify(source)} -g -y --agent universal`,
|
|
452
|
+
`npx -y ${SKILLS_TOOL} add ${config.repository}#v${target} -g -y --agent universal`,
|
|
453
|
+
`npx -y ${SKILLS_TOOL} add ${JSON.stringify(globalSkillsDirectory())} -g -y --copy`
|
|
454
|
+
];
|
|
455
|
+
}
|
|
456
|
+
function verifySkillsAttempt(report, skills, result, stage, source) {
|
|
457
|
+
const problem = skillProcessProblem(result);
|
|
458
|
+
report.skills.failures = verifyGlobalSkills(skills, report.skills.directory);
|
|
459
|
+
if (problem) {
|
|
460
|
+
for (const skill of skills) {
|
|
461
|
+
if (!report.skills.failures.some((failure) => failure.skill === skill.name)) {
|
|
462
|
+
report.skills.failures.push({ skill: skill.name, reason: problem });
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
report.skills.missing = report.skills.failures.map((failure) => failure.skill);
|
|
467
|
+
report.skills.installed_count = skills.length - report.skills.missing.length;
|
|
468
|
+
const cause = commandSucceeded(result) ? "validation" : classifyInstallFailure(result);
|
|
469
|
+
report.skills.attempts.push({
|
|
470
|
+
source,
|
|
471
|
+
stage,
|
|
472
|
+
cause: report.skills.missing.length ? cause : null,
|
|
473
|
+
failures: [...report.skills.failures]
|
|
474
|
+
});
|
|
475
|
+
report.skills.status = !report.skills.missing.length ? "success" : report.skills.installed_count ? "partial" : "failed";
|
|
476
|
+
return report.skills.status === "success";
|
|
477
|
+
}
|
|
478
|
+
function readPackage(root) {
|
|
479
|
+
try {
|
|
480
|
+
return JSON.parse(fs4.readFileSync(path4.join(root, "package.json"), "utf8"));
|
|
481
|
+
} catch {
|
|
482
|
+
return null;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
function linked(root) {
|
|
486
|
+
try {
|
|
487
|
+
return fs4.lstatSync(root).isSymbolicLink();
|
|
488
|
+
} catch {
|
|
489
|
+
return false;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
function cliBinBelongsToPackage(bin, packageRoot, platform) {
|
|
493
|
+
const entry = fs4.realpathSync(path4.join(packageRoot, "bin", "ae-cli.js"));
|
|
494
|
+
if (platform !== "win32") return fs4.realpathSync(bin) === entry;
|
|
495
|
+
const shim = fs4.readFileSync(bin, "utf8");
|
|
496
|
+
const targets = [...shim.matchAll(/"%(?:dp0%|~dp0)[\\/]([^"\r\n]+)"/gi)];
|
|
497
|
+
return targets.some(([, relative]) => {
|
|
498
|
+
try {
|
|
499
|
+
const target = path4.resolve(path4.dirname(bin), relative.replace(/\\/g, path4.sep));
|
|
500
|
+
return fs4.realpathSync(target).toLowerCase() === entry.toLowerCase();
|
|
501
|
+
} catch {
|
|
502
|
+
return false;
|
|
503
|
+
}
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
function runInstallation(options = {}) {
|
|
507
|
+
const local = getLocalCliPackageInfo();
|
|
508
|
+
const packageName = options.packageName ?? local.name;
|
|
509
|
+
const report = newInstallationReport(packageName, "", Boolean(options.dryRun));
|
|
510
|
+
const runner = options.runner ?? defaultInstallRunner;
|
|
511
|
+
const progress = options.progress ?? (() => {
|
|
512
|
+
});
|
|
513
|
+
const platform = options.platform ?? process.platform;
|
|
514
|
+
report.skills.directory = options.skillsDirectory ?? globalSkillsDirectory();
|
|
515
|
+
let stage = "environment";
|
|
516
|
+
try {
|
|
517
|
+
const target = exactVersion(options.target ?? local.version);
|
|
518
|
+
report.cli.version = target;
|
|
519
|
+
report.skills.version = target;
|
|
520
|
+
const config = distribution(packageName);
|
|
521
|
+
report.auth.host = installHost(options.host);
|
|
522
|
+
const major = Number((options.nodeVersion ?? process.versions.node).split(".")[0]);
|
|
523
|
+
if (!Number.isInteger(major) || major < 20) return failInstallation(report, stage, "AE CLI requires Node.js 20 or later. Select an existing supported Node runtime and rerun.");
|
|
524
|
+
if (options.dryRun) {
|
|
525
|
+
report.ok = true;
|
|
526
|
+
report.commands = installPlan(packageName, target);
|
|
527
|
+
report.next_steps.push("Dry run only; CLI, global Skills, and authentication have not been verified.");
|
|
528
|
+
return report;
|
|
529
|
+
}
|
|
530
|
+
const npm = resolveNodeTool("npm", process.execPath, platform);
|
|
531
|
+
progress("[ae-cli] [1/4] Checking Node.js, npm, and npx...");
|
|
532
|
+
for (const tool of [npm, resolveNodeTool("npx", process.execPath, platform)]) {
|
|
533
|
+
const result = runner(tool, ["--version"], 15e3);
|
|
534
|
+
if (!commandSucceeded(result)) return failInstallation(report, stage, "npm and npx must be available alongside the selected Node.js runtime.", classifyInstallFailure(result));
|
|
535
|
+
}
|
|
536
|
+
stage = "package_validation";
|
|
537
|
+
const rootResult = runner(npm, ["root", "-g"], 15e3);
|
|
538
|
+
const prefixResult = runner(npm, ["prefix", "-g"], 15e3);
|
|
539
|
+
const globalRoot = rootResult.stdout.trim();
|
|
540
|
+
const prefix = prefixResult.stdout.trim();
|
|
541
|
+
if (!commandSucceeded(rootResult) || !commandSucceeded(prefixResult) || !path4.isAbsolute(globalRoot) || !path4.isAbsolute(prefix) || /[\r\n]/.test(globalRoot + prefix)) return failInstallation(report, stage, "Cannot resolve npm global root and prefix. Check npm configuration.");
|
|
542
|
+
const packageRoot = path4.join(globalRoot, packageName);
|
|
543
|
+
const bin = platform === "win32" ? path4.join(prefix, "ae-cli.cmd") : path4.join(prefix, "bin", "ae-cli");
|
|
544
|
+
report.cli.path = bin;
|
|
545
|
+
const env = options.env ?? process.env;
|
|
546
|
+
const installedBefore = readPackage(packageRoot);
|
|
547
|
+
if (linked(packageRoot) && !options.skipCliInstall) return failInstallation(report, stage, "The global CLI is npm-linked to source. Preserve the link; use a separate npm prefix for a packaged installation.");
|
|
548
|
+
if (options.skipCliInstall || installedBefore?.name === packageName && installedBefore.version === target && fs4.existsSync(bin)) {
|
|
549
|
+
report.cli.action = "skipped";
|
|
550
|
+
progress("[ae-cli] [2/4] Verifying the existing CLI...");
|
|
551
|
+
} else {
|
|
552
|
+
stage = "npm_install";
|
|
553
|
+
progress(`[ae-cli] [2/4] Installing ${packageName}@${target}...`);
|
|
554
|
+
const result = runner(npm, ["install", "-g", `${packageName}@${target}`, "--registry", config.registry], INSTALL_TIMEOUT_MS);
|
|
555
|
+
if (!commandSucceeded(result)) return failInstallation(report, stage, "npm installation failed. Check network access and a user-writable npm prefix; do not use sudo.", classifyInstallFailure(result));
|
|
556
|
+
report.cli.action = "installed";
|
|
557
|
+
}
|
|
558
|
+
stage = "package_validation";
|
|
559
|
+
const installed = readPackage(packageRoot);
|
|
560
|
+
if (installed?.name !== packageName || installed.version !== target) return failInstallation(report, stage, "Installed CLI package identity or version does not match the requested distribution.");
|
|
561
|
+
if (!fs4.existsSync(bin)) return failInstallation(report, stage, "npm did not create the global ae-cli executable. Enable npm bin-links, reinstall, and add the global executable directory to PATH.");
|
|
562
|
+
if (!cliBinBelongsToPackage(bin, packageRoot, platform)) return failInstallation(report, stage, "The global executable does not belong to the installed CLI package.");
|
|
563
|
+
const versionResult = runner(bin, ["--version"], 15e3);
|
|
564
|
+
if (!commandSucceeded(versionResult) || versionResult.stdout.trim() !== target) return failInstallation(report, stage, "ae-cli --version did not return the requested version. Refresh the shell command cache and check npm bin links.");
|
|
565
|
+
report.cli.verified = true;
|
|
566
|
+
const pathKey = platform === "win32" ? Object.keys(env).find((key) => key.toLowerCase() === "path") : "PATH";
|
|
567
|
+
const installedPathBin = (env[pathKey ?? "PATH"] ?? "").split(platform === "win32" ? ";" : path4.delimiter).filter(Boolean).map((dir) => path4.join(dir.replace(/^"(.*)"$/, "$1"), platform === "win32" ? "ae-cli.cmd" : "ae-cli")).find((file) => fs4.existsSync(file) && !file.includes(`${path4.sep}_npx${path4.sep}`));
|
|
568
|
+
if (!installedPathBin || fs4.realpathSync(installedPathBin) !== fs4.realpathSync(bin)) {
|
|
569
|
+
report.cli.warnings.push(`Add ${path4.dirname(bin)} to the front of PATH and refresh the shell command cache. The absolute executable was verified.`);
|
|
570
|
+
report.next_steps.push(`Use the verified executable at ${bin}, or correct PATH before invoking ae-cli.`);
|
|
571
|
+
}
|
|
572
|
+
stage = "skills_local";
|
|
573
|
+
const skillsPath = path4.join(packageRoot, "skills");
|
|
574
|
+
const skills = readBundledSkills(skillsPath);
|
|
575
|
+
report.skills.expected_count = skills.length;
|
|
576
|
+
for (const source of [skillsPath, `${config.repository}#v${target}`]) {
|
|
577
|
+
stage = source === skillsPath ? "skills_local" : "skills_github";
|
|
578
|
+
report.skills.source = source === skillsPath ? "installed-package" : source;
|
|
579
|
+
progress("[ae-cli] [3/4] Installing and verifying global Skills...");
|
|
580
|
+
const result = runner(resolveNodeTool("npx", process.execPath, platform), [
|
|
581
|
+
"-y",
|
|
582
|
+
SKILLS_TOOL,
|
|
583
|
+
"add",
|
|
584
|
+
source,
|
|
585
|
+
"-g",
|
|
586
|
+
"-y",
|
|
587
|
+
"--agent",
|
|
588
|
+
"universal",
|
|
589
|
+
"--skill",
|
|
590
|
+
...skills.map((skill) => skill.name)
|
|
591
|
+
], INSTALL_TIMEOUT_MS);
|
|
592
|
+
if (verifySkillsAttempt(report, skills, result, stage, report.skills.source)) break;
|
|
593
|
+
}
|
|
594
|
+
if (report.skills.status !== "success") {
|
|
595
|
+
report.skills.status = report.skills.installed_count ? "partial" : "failed";
|
|
596
|
+
const first = report.skills.attempts[0];
|
|
597
|
+
return failInstallation(
|
|
598
|
+
report,
|
|
599
|
+
first.stage,
|
|
600
|
+
"Global Skills installation is incomplete. See skills.attempts for the original and fallback failures.",
|
|
601
|
+
first.cause ?? "validation"
|
|
602
|
+
);
|
|
603
|
+
}
|
|
604
|
+
stage = "skills_clients";
|
|
605
|
+
progress("[ae-cli] [3/4] Distributing Skills to detected clients...");
|
|
606
|
+
const clientResult = runner(
|
|
607
|
+
resolveNodeTool("npx", process.execPath, platform),
|
|
608
|
+
["-y", SKILLS_TOOL, "add", report.skills.directory, "-g", "-y", "--copy", "--skill", ...skills.map((skill) => skill.name)],
|
|
609
|
+
INSTALL_TIMEOUT_MS
|
|
610
|
+
);
|
|
611
|
+
if (!verifySkillsAttempt(report, skills, clientResult, stage, report.skills.directory)) {
|
|
612
|
+
return failInstallation(
|
|
613
|
+
report,
|
|
614
|
+
stage,
|
|
615
|
+
"Global Skills were installed, but client distribution is incomplete. See skills.attempts for details.",
|
|
616
|
+
report.skills.attempts.at(-1).cause ?? "validation"
|
|
617
|
+
);
|
|
618
|
+
}
|
|
619
|
+
progress("[ae-cli] [4/4] Checking authentication guidance...");
|
|
620
|
+
if (report.auth.host) {
|
|
621
|
+
const result = runner(bin, ["--no-update-check", "--format", "json", "auth", "status", "--host", report.auth.host], INSTALL_TIMEOUT_MS);
|
|
622
|
+
try {
|
|
623
|
+
const body = JSON.parse(result.stdout);
|
|
624
|
+
if (!commandSucceeded(result) || !body.ok || typeof body.data?.authenticated !== "boolean") throw new Error();
|
|
625
|
+
report.auth.authenticated = body.data.authenticated;
|
|
626
|
+
} catch {
|
|
627
|
+
report.auth.warnings.push("Authentication status could not be verified. Run auth status for this host.");
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
if (report.auth.authenticated !== true) {
|
|
631
|
+
report.next_steps.push(`ae-cli auth login --host ${report.auth.host ?? "<AE_HOST>"} --no-wait`);
|
|
632
|
+
report.next_steps.push("Complete browser authorization, then run ae-cli auth login --host <AE_HOST> --device-code <DEVICE_CODE>.");
|
|
633
|
+
}
|
|
634
|
+
report.ok = true;
|
|
635
|
+
return report;
|
|
636
|
+
} catch (error) {
|
|
637
|
+
const safe = error instanceof Error && /^(Expected an exact|Unsupported CLI package|Host must|Invalid bundled Skill|Bundled Skills are empty)/.test(error.message);
|
|
638
|
+
return failInstallation(report, stage, safe ? error.message : "Installation could not complete at this stage. Check file permissions, package contents, and global Skills directory.");
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
244
642
|
// src/core/version-sync.ts
|
|
245
|
-
var COMMAND_TIMEOUT_MS = 12e4;
|
|
246
643
|
var AUTO_ATTEMPT_INTERVAL_MS = 60 * 60 * 1e3;
|
|
247
644
|
var MAX_AUTO_ATTEMPTS_PER_DAY = 3;
|
|
248
645
|
var LOCK_STALE_MS = 10 * 60 * 1e3;
|
|
249
646
|
var SYNC_STATE_FILE = "version-sync.json";
|
|
250
647
|
var SYNC_LOCK_FILE = "version-sync.lock";
|
|
251
648
|
function syncStatePath() {
|
|
252
|
-
return
|
|
649
|
+
return path5.join(getConfigDir(), SYNC_STATE_FILE);
|
|
253
650
|
}
|
|
254
651
|
function syncLockPath() {
|
|
255
|
-
return
|
|
652
|
+
return path5.join(getConfigDir(), SYNC_LOCK_FILE);
|
|
256
653
|
}
|
|
257
654
|
function readSyncStore() {
|
|
258
655
|
try {
|
|
259
|
-
const raw = JSON.parse(
|
|
656
|
+
const raw = JSON.parse(fs5.readFileSync(syncStatePath(), "utf8"));
|
|
260
657
|
if (raw && typeof raw === "object" && raw.hosts && typeof raw.hosts === "object") {
|
|
261
658
|
return raw;
|
|
262
659
|
}
|
|
@@ -266,8 +663,8 @@ function readSyncStore() {
|
|
|
266
663
|
}
|
|
267
664
|
function writeSyncStore(store) {
|
|
268
665
|
try {
|
|
269
|
-
|
|
270
|
-
|
|
666
|
+
fs5.mkdirSync(getConfigDir(), { recursive: true });
|
|
667
|
+
fs5.writeFileSync(syncStatePath(), JSON.stringify(store, null, 2));
|
|
271
668
|
} catch {
|
|
272
669
|
}
|
|
273
670
|
}
|
|
@@ -343,61 +740,9 @@ function isAutoSyncTargetEligible(version) {
|
|
|
343
740
|
function isPublicAeCliPackage(packageName) {
|
|
344
741
|
return packageName === OPEN_SOURCE_AE_CLI_PACKAGE;
|
|
345
742
|
}
|
|
346
|
-
function
|
|
347
|
-
const fileName = platform === "win32" ? `${tool}.cmd` : tool;
|
|
348
|
-
const sibling = path.join(path.dirname(execPath), fileName);
|
|
349
|
-
return fs.existsSync(sibling) ? sibling : fileName;
|
|
350
|
-
}
|
|
351
|
-
function buildVersionInstallPlan(targetRaw, globalRoot = "<npm-root>") {
|
|
743
|
+
function buildVersionInstallPlan(targetRaw, globalRoot = "<npm-root>", packageName = OPEN_SOURCE_AE_CLI_PACKAGE) {
|
|
352
744
|
const target = normalizeCliVersion(targetRaw);
|
|
353
|
-
|
|
354
|
-
return {
|
|
355
|
-
target,
|
|
356
|
-
commands: [
|
|
357
|
-
`npm install -g ${OPEN_SOURCE_AE_CLI_PACKAGE}@${target} --registry=${OPEN_SOURCE_NPM_REGISTRY}`,
|
|
358
|
-
`npx -y skills add ${localSkills} -g -y`,
|
|
359
|
-
`npx -y skills add ${AE_CLI_SKILLS_REPO}#v${target} -g -y`
|
|
360
|
-
],
|
|
361
|
-
skillsSources: ["installed-package", "github-fallback"]
|
|
362
|
-
};
|
|
363
|
-
}
|
|
364
|
-
function defaultRunner(command, args, timeoutMs) {
|
|
365
|
-
const result = spawnSync(command, args, {
|
|
366
|
-
encoding: "utf8",
|
|
367
|
-
maxBuffer: 2 * 1024 * 1024,
|
|
368
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
369
|
-
timeout: timeoutMs,
|
|
370
|
-
windowsHide: true
|
|
371
|
-
});
|
|
372
|
-
return {
|
|
373
|
-
status: result.status,
|
|
374
|
-
stdout: result.stdout || "",
|
|
375
|
-
stderr: result.stderr || "",
|
|
376
|
-
error: result.error,
|
|
377
|
-
signal: result.signal
|
|
378
|
-
};
|
|
379
|
-
}
|
|
380
|
-
function resultMessage(result) {
|
|
381
|
-
if (result.error?.message) return result.error.message;
|
|
382
|
-
const detail = (result.stderr || result.stdout).trim().split(/\r?\n/).filter(Boolean).slice(-3).join(" ");
|
|
383
|
-
if (detail) return detail;
|
|
384
|
-
if (result.signal) return `Command terminated by ${result.signal}`;
|
|
385
|
-
return `Command exited with status ${result.status ?? "unknown"}`;
|
|
386
|
-
}
|
|
387
|
-
function classifyFailure(result) {
|
|
388
|
-
const text = `${result.error?.message || ""}
|
|
389
|
-
${result.stderr}
|
|
390
|
-
${result.stdout}`;
|
|
391
|
-
if (result.error && result.error.code === "ETIMEDOUT") return "timeout";
|
|
392
|
-
if (/timed?\s*out|ETIMEDOUT/i.test(text)) return "timeout";
|
|
393
|
-
if (/EACCES|EPERM|permission denied|access is denied/i.test(text)) return "permission";
|
|
394
|
-
if (/EAI_AGAIN|ENOTFOUND|ECONNRESET|ECONNREFUSED|network|unable to access|could not resolve|certificate|SSL/i.test(text)) {
|
|
395
|
-
return "network";
|
|
396
|
-
}
|
|
397
|
-
if (/E404|404 Not Found|no matching version|couldn't find remote ref|not found/i.test(text)) {
|
|
398
|
-
return "package_missing";
|
|
399
|
-
}
|
|
400
|
-
return "unknown";
|
|
745
|
+
return { target, commands: installPlan(packageName, target, globalRoot), skillsSources: ["installed-package", "github-fallback"] };
|
|
401
746
|
}
|
|
402
747
|
function friendlyVersionSyncFailure(result) {
|
|
403
748
|
if (result.cause === "busy") {
|
|
@@ -420,24 +765,21 @@ function friendlyVersionSyncFailure(result) {
|
|
|
420
765
|
}
|
|
421
766
|
return `Version synchronization failed at ${result.stage}.`;
|
|
422
767
|
}
|
|
423
|
-
function commandSucceeded(result) {
|
|
424
|
-
return !result.error && result.status === 0;
|
|
425
|
-
}
|
|
426
768
|
function acquireInstallLock(now) {
|
|
427
|
-
|
|
769
|
+
fs5.mkdirSync(getConfigDir(), { recursive: true });
|
|
428
770
|
const file = syncLockPath();
|
|
429
771
|
try {
|
|
430
|
-
return
|
|
772
|
+
return fs5.openSync(file, "wx");
|
|
431
773
|
} catch {
|
|
432
774
|
try {
|
|
433
|
-
const age = now.getTime() -
|
|
775
|
+
const age = now.getTime() - fs5.statSync(file).mtimeMs;
|
|
434
776
|
if (age >= LOCK_STALE_MS) {
|
|
435
|
-
|
|
436
|
-
return
|
|
777
|
+
fs5.unlinkSync(file);
|
|
778
|
+
return fs5.openSync(file, "wx");
|
|
437
779
|
}
|
|
438
780
|
} catch {
|
|
439
781
|
try {
|
|
440
|
-
return
|
|
782
|
+
return fs5.openSync(file, "wx");
|
|
441
783
|
} catch {
|
|
442
784
|
return null;
|
|
443
785
|
}
|
|
@@ -447,121 +789,53 @@ function acquireInstallLock(now) {
|
|
|
447
789
|
}
|
|
448
790
|
function releaseInstallLock(fd) {
|
|
449
791
|
try {
|
|
450
|
-
|
|
792
|
+
fs5.closeSync(fd);
|
|
451
793
|
} catch {
|
|
452
794
|
}
|
|
453
795
|
try {
|
|
454
|
-
|
|
796
|
+
fs5.unlinkSync(syncLockPath());
|
|
455
797
|
} catch {
|
|
456
798
|
}
|
|
457
799
|
}
|
|
458
|
-
function validateInstalledPackage(npm, target, runner) {
|
|
459
|
-
const rootResult = runner(npm, ["root", "-g"], COMMAND_TIMEOUT_MS);
|
|
460
|
-
if (!commandSucceeded(rootResult)) {
|
|
461
|
-
return { ok: false, message: resultMessage(rootResult) };
|
|
462
|
-
}
|
|
463
|
-
const globalRoot = rootResult.stdout.trim();
|
|
464
|
-
const packageRoot = path.join(globalRoot, "@thinkingai", "ae-cli");
|
|
465
|
-
const packageJson = path.join(packageRoot, "package.json");
|
|
466
|
-
const skillsPath = path.join(packageRoot, "skills");
|
|
467
|
-
try {
|
|
468
|
-
const installed = JSON.parse(fs.readFileSync(packageJson, "utf8"));
|
|
469
|
-
if (normalizeCliVersion(installed.version || "") !== target) {
|
|
470
|
-
return {
|
|
471
|
-
ok: false,
|
|
472
|
-
message: `Installed package version ${installed.version || "unknown"} does not match ${target}`
|
|
473
|
-
};
|
|
474
|
-
}
|
|
475
|
-
if (!fs.statSync(skillsPath).isDirectory()) {
|
|
476
|
-
return { ok: false, message: `Installed package has no skills directory: ${skillsPath}` };
|
|
477
|
-
}
|
|
478
|
-
return { ok: true, skillsPath };
|
|
479
|
-
} catch (error) {
|
|
480
|
-
return { ok: false, message: error?.message || String(error) };
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
800
|
function installVersion(targetRaw, options = {}) {
|
|
484
|
-
const
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
801
|
+
const run = () => {
|
|
802
|
+
const report = runInstallation({ ...options, target: targetRaw });
|
|
803
|
+
const cliInstalled = report.cli.verified || report.cli.action === "installed";
|
|
804
|
+
if (report.ok) return { ok: true, cliInstalled, skillsSource: report.skills.source === "installed-package" ? "local" : "github", report };
|
|
805
|
+
return {
|
|
806
|
+
ok: false,
|
|
807
|
+
stage: report.error?.stage ?? "environment",
|
|
808
|
+
cliInstalled,
|
|
809
|
+
skillsPending: cliInstalled,
|
|
810
|
+
cause: report.error?.cause ?? "validation",
|
|
811
|
+
message: report.error?.message ?? "Installation failed.",
|
|
812
|
+
report
|
|
813
|
+
};
|
|
814
|
+
};
|
|
815
|
+
if (options.dryRun) return run();
|
|
816
|
+
let lock;
|
|
817
|
+
try {
|
|
818
|
+
lock = acquireInstallLock(options.now ?? /* @__PURE__ */ new Date());
|
|
819
|
+
} catch {
|
|
492
820
|
return {
|
|
493
821
|
ok: false,
|
|
494
822
|
stage: "lock",
|
|
495
823
|
cliInstalled: false,
|
|
496
824
|
skillsPending: Boolean(options.skipCliInstall),
|
|
497
|
-
cause: "
|
|
498
|
-
message: "
|
|
825
|
+
cause: "permission",
|
|
826
|
+
message: "The installation lock directory is not writable."
|
|
499
827
|
};
|
|
500
828
|
}
|
|
501
|
-
|
|
829
|
+
if (lock === null) return {
|
|
830
|
+
ok: false,
|
|
831
|
+
stage: "lock",
|
|
832
|
+
cliInstalled: false,
|
|
833
|
+
skillsPending: Boolean(options.skipCliInstall),
|
|
834
|
+
cause: "busy",
|
|
835
|
+
message: "Another ae-cli process is already synchronizing the global installation."
|
|
836
|
+
};
|
|
502
837
|
try {
|
|
503
|
-
|
|
504
|
-
progress(`[ae-cli] [1/2] Installing ae-cli ${target}...`);
|
|
505
|
-
const npmResult = runner(
|
|
506
|
-
npm,
|
|
507
|
-
[
|
|
508
|
-
"install",
|
|
509
|
-
"-g",
|
|
510
|
-
`${OPEN_SOURCE_AE_CLI_PACKAGE}@${target}`,
|
|
511
|
-
"--registry",
|
|
512
|
-
OPEN_SOURCE_NPM_REGISTRY
|
|
513
|
-
],
|
|
514
|
-
COMMAND_TIMEOUT_MS
|
|
515
|
-
);
|
|
516
|
-
if (!commandSucceeded(npmResult)) {
|
|
517
|
-
return {
|
|
518
|
-
ok: false,
|
|
519
|
-
stage: "npm_install",
|
|
520
|
-
cliInstalled: false,
|
|
521
|
-
skillsPending: false,
|
|
522
|
-
cause: classifyFailure(npmResult),
|
|
523
|
-
message: resultMessage(npmResult)
|
|
524
|
-
};
|
|
525
|
-
}
|
|
526
|
-
cliInstalled = true;
|
|
527
|
-
}
|
|
528
|
-
const installed = validateInstalledPackage(npm, target, runner);
|
|
529
|
-
if (!installed.ok) {
|
|
530
|
-
return {
|
|
531
|
-
ok: false,
|
|
532
|
-
stage: "package_validation",
|
|
533
|
-
cliInstalled,
|
|
534
|
-
skillsPending: cliInstalled,
|
|
535
|
-
cause: "validation",
|
|
536
|
-
message: installed.message
|
|
537
|
-
};
|
|
538
|
-
}
|
|
539
|
-
progress("[ae-cli] [2/2] Synchronizing Skills from the installed npm package...");
|
|
540
|
-
const localResult = runner(
|
|
541
|
-
npx,
|
|
542
|
-
["-y", "skills", "add", installed.skillsPath, "-g", "-y"],
|
|
543
|
-
COMMAND_TIMEOUT_MS
|
|
544
|
-
);
|
|
545
|
-
if (commandSucceeded(localResult)) {
|
|
546
|
-
return { ok: true, cliInstalled, skillsSource: "local" };
|
|
547
|
-
}
|
|
548
|
-
progress("[ae-cli] Local Skills sync did not complete; trying the GitHub fallback...");
|
|
549
|
-
const githubResult = runner(
|
|
550
|
-
npx,
|
|
551
|
-
["-y", "skills", "add", `${AE_CLI_SKILLS_REPO}#v${target}`, "-g", "-y"],
|
|
552
|
-
COMMAND_TIMEOUT_MS
|
|
553
|
-
);
|
|
554
|
-
if (commandSucceeded(githubResult)) {
|
|
555
|
-
return { ok: true, cliInstalled, skillsSource: "github" };
|
|
556
|
-
}
|
|
557
|
-
return {
|
|
558
|
-
ok: false,
|
|
559
|
-
stage: "skills_github",
|
|
560
|
-
cliInstalled,
|
|
561
|
-
skillsPending: cliInstalled,
|
|
562
|
-
cause: classifyFailure(githubResult),
|
|
563
|
-
message: resultMessage(githubResult) || resultMessage(localResult)
|
|
564
|
-
};
|
|
838
|
+
return run();
|
|
565
839
|
} finally {
|
|
566
840
|
releaseInstallLock(lock);
|
|
567
841
|
}
|
|
@@ -579,46 +853,107 @@ function autoSyncDirection(current, expected) {
|
|
|
579
853
|
return "switch";
|
|
580
854
|
}
|
|
581
855
|
|
|
856
|
+
// src/core/feature-config.ts
|
|
857
|
+
import crypto from "crypto";
|
|
858
|
+
import fs6 from "fs";
|
|
859
|
+
import path6 from "path";
|
|
860
|
+
var FEATURE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
861
|
+
var UNAVAILABLE_TTL_MS = 5 * 60 * 1e3;
|
|
862
|
+
function cachePath() {
|
|
863
|
+
return path6.join(getConfigDir(), "feature-config.json");
|
|
864
|
+
}
|
|
865
|
+
function cacheKey(host, token) {
|
|
866
|
+
const fingerprint = crypto.createHash("sha256").update(token).digest("hex");
|
|
867
|
+
return `${normalizeUrl(host)}#${fingerprint}`;
|
|
868
|
+
}
|
|
869
|
+
function readStore() {
|
|
870
|
+
try {
|
|
871
|
+
const value = JSON.parse(fs6.readFileSync(cachePath(), "utf8"));
|
|
872
|
+
return value?.entries && typeof value.entries === "object" ? value : { entries: {} };
|
|
873
|
+
} catch {
|
|
874
|
+
return { entries: {} };
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
function writeStore(store) {
|
|
878
|
+
const target = cachePath();
|
|
879
|
+
const temp = `${target}.${process.pid}.${crypto.randomBytes(6).toString("hex")}.tmp`;
|
|
880
|
+
try {
|
|
881
|
+
fs6.mkdirSync(path6.dirname(target), { recursive: true });
|
|
882
|
+
fs6.writeFileSync(temp, JSON.stringify(store), { encoding: "utf8", mode: 384 });
|
|
883
|
+
fs6.renameSync(temp, target);
|
|
884
|
+
fs6.chmodSync(target, 384);
|
|
885
|
+
} catch {
|
|
886
|
+
try {
|
|
887
|
+
fs6.rmSync(temp, { force: true });
|
|
888
|
+
} catch {
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
function remember(host, token, entry) {
|
|
893
|
+
const store = readStore();
|
|
894
|
+
store.entries[cacheKey(host, token)] = entry;
|
|
895
|
+
writeStore(store);
|
|
896
|
+
}
|
|
897
|
+
function rememberKbAutoDiscoveryFeature(host, token, enabled) {
|
|
898
|
+
remember(host, token, { enabled, available: true, fetchedAt: Date.now() });
|
|
899
|
+
}
|
|
900
|
+
async function resolveKbAutoDiscoveryFeature(host, token, fetchRemote) {
|
|
901
|
+
const entry = readStore().entries[cacheKey(host, token)];
|
|
902
|
+
const ttl = entry?.available ? FEATURE_TTL_MS : UNAVAILABLE_TTL_MS;
|
|
903
|
+
if (entry && Date.now() >= entry.fetchedAt && Date.now() - entry.fetchedAt < ttl) {
|
|
904
|
+
return { enabled: entry.enabled, source: entry.available ? "cache" : "unavailable" };
|
|
905
|
+
}
|
|
906
|
+
const remote = await fetchRemote().catch(() => null);
|
|
907
|
+
if (!remote) {
|
|
908
|
+
remember(host, token, { enabled: false, available: false, fetchedAt: Date.now() });
|
|
909
|
+
return { enabled: false, source: "unavailable" };
|
|
910
|
+
}
|
|
911
|
+
const enabled = remote.projectSemanticKbAutoDiscovery === true;
|
|
912
|
+
remember(host, token, { enabled, available: true, fetchedAt: Date.now() });
|
|
913
|
+
return { enabled, source: "remote" };
|
|
914
|
+
}
|
|
915
|
+
|
|
582
916
|
// src/core/compat-check.ts
|
|
583
|
-
import
|
|
584
|
-
import
|
|
585
|
-
var CHECK_INTERVAL_MS =
|
|
917
|
+
import fs7 from "fs";
|
|
918
|
+
import path7 from "path";
|
|
919
|
+
var CHECK_INTERVAL_MS = 60 * 60 * 1e3;
|
|
920
|
+
var NOTIFY_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
586
921
|
var FETCH_TIMEOUT_MS = 3e3;
|
|
587
922
|
var CLI_CONFIG_PATH = "/v1/ta/cli/config";
|
|
588
923
|
function cacheFilePath() {
|
|
589
|
-
return
|
|
924
|
+
return path7.join(getConfigDir(), "compat-check.json");
|
|
590
925
|
}
|
|
591
|
-
function
|
|
926
|
+
function readStore2() {
|
|
592
927
|
try {
|
|
593
928
|
const file = cacheFilePath();
|
|
594
|
-
if (!
|
|
595
|
-
const raw = JSON.parse(
|
|
929
|
+
if (!fs7.existsSync(file)) return { hosts: {} };
|
|
930
|
+
const raw = JSON.parse(fs7.readFileSync(file, "utf8"));
|
|
596
931
|
if (!raw || typeof raw !== "object" || !raw.hosts) return { hosts: {} };
|
|
597
932
|
return raw;
|
|
598
933
|
} catch {
|
|
599
934
|
return { hosts: {} };
|
|
600
935
|
}
|
|
601
936
|
}
|
|
602
|
-
function
|
|
937
|
+
function writeStore2(store) {
|
|
603
938
|
try {
|
|
604
939
|
const dir = getConfigDir();
|
|
605
|
-
if (!
|
|
606
|
-
|
|
940
|
+
if (!fs7.existsSync(dir)) fs7.mkdirSync(dir, { recursive: true });
|
|
941
|
+
fs7.writeFileSync(cacheFilePath(), JSON.stringify(store, null, 2));
|
|
607
942
|
} catch {
|
|
608
943
|
}
|
|
609
944
|
}
|
|
610
945
|
function hostKey(host) {
|
|
611
946
|
return normalizeUrl(host);
|
|
612
947
|
}
|
|
613
|
-
function
|
|
948
|
+
function isCompatCacheStale(entry, now = Date.now()) {
|
|
614
949
|
if (!entry?.lastFetchedAt) return true;
|
|
615
|
-
const age =
|
|
950
|
+
const age = now - new Date(entry.lastFetchedAt).getTime();
|
|
616
951
|
return age >= CHECK_INTERVAL_MS;
|
|
617
952
|
}
|
|
618
|
-
function
|
|
953
|
+
function shouldNotifyCompat(entry, now = Date.now()) {
|
|
619
954
|
if (!entry?.lastNotifiedAt) return true;
|
|
620
|
-
const age =
|
|
621
|
-
return age >=
|
|
955
|
+
const age = now - new Date(entry.lastNotifiedAt).getTime();
|
|
956
|
+
return age >= NOTIFY_INTERVAL_MS;
|
|
622
957
|
}
|
|
623
958
|
function shouldSkipCompatCheck(argv = process.argv) {
|
|
624
959
|
if (process.env.AE_CLI_NO_COMPAT_CHECK === "1") return true;
|
|
@@ -642,7 +977,7 @@ function rootCommand(argv) {
|
|
|
642
977
|
}
|
|
643
978
|
function shouldSkipAutoSync(argv = process.argv) {
|
|
644
979
|
const command = rootCommand(argv);
|
|
645
|
-
return command === "update" || command === "auth" || command === "config";
|
|
980
|
+
return command === "update" || command === "install" || command === "auth" || command === "config";
|
|
646
981
|
}
|
|
647
982
|
async function fetchCliConfig(host, cliToken) {
|
|
648
983
|
const url = new URL(CLI_CONFIG_PATH, host.endsWith("/") ? host : `${host}/`);
|
|
@@ -663,7 +998,8 @@ async function fetchCliConfig(host, cliToken) {
|
|
|
663
998
|
const aeCliVersion = versions.aeCliVersion ?? versions.ae_cli_version ?? null;
|
|
664
999
|
return {
|
|
665
1000
|
clusterVersion: clusterVersion?.trim() || null,
|
|
666
|
-
aeCliVersion: aeCliVersion?.trim() || null
|
|
1001
|
+
aeCliVersion: aeCliVersion?.trim() || null,
|
|
1002
|
+
...typeof data.features?.projectSemanticKbAutoDiscovery === "boolean" ? { projectSemanticKbAutoDiscovery: data.features.projectSemanticKbAutoDiscovery } : {}
|
|
667
1003
|
};
|
|
668
1004
|
} catch {
|
|
669
1005
|
return null;
|
|
@@ -684,7 +1020,7 @@ function getPendingUpdateNotice() {
|
|
|
684
1020
|
return pendingUpdateNotice;
|
|
685
1021
|
}
|
|
686
1022
|
function getCachedCompatForHost(host) {
|
|
687
|
-
return
|
|
1023
|
+
return readStore2().hosts[hostKey(host)];
|
|
688
1024
|
}
|
|
689
1025
|
function formatCapabilityMissCompatHint(host) {
|
|
690
1026
|
if (shouldSkipCompatCheck()) return void 0;
|
|
@@ -725,14 +1061,15 @@ function setUpdateNotice(pkg, expected, cluster, verdict, state, stage, message)
|
|
|
725
1061
|
async function refreshAndMaybeNotify(host, pkg, allowAutoSync) {
|
|
726
1062
|
const token = peekCliToken(host);
|
|
727
1063
|
if (!token) return { status: "continue" };
|
|
728
|
-
const store =
|
|
1064
|
+
const store = readStore2();
|
|
729
1065
|
const key = hostKey(host);
|
|
730
1066
|
const existing = store.hosts[key];
|
|
731
1067
|
let expected = existing?.expectedVersion;
|
|
732
1068
|
let cluster = existing?.clusterVersion;
|
|
733
1069
|
let fetchedAt = existing?.lastFetchedAt;
|
|
734
|
-
if (
|
|
1070
|
+
if (isCompatCacheStale(existing)) {
|
|
735
1071
|
const remote = await fetchCliConfig(host, token);
|
|
1072
|
+
if (remote) rememberKbAutoDiscoveryFeature(host, token, remote.projectSemanticKbAutoDiscovery ?? false);
|
|
736
1073
|
if (!remote?.aeCliVersion) {
|
|
737
1074
|
return { status: "continue" };
|
|
738
1075
|
}
|
|
@@ -747,7 +1084,7 @@ async function refreshAndMaybeNotify(host, pkg, allowAutoSync) {
|
|
|
747
1084
|
lastFetchedAt: fetchedAt,
|
|
748
1085
|
lastNotifiedAt: existing?.lastNotifiedAt
|
|
749
1086
|
};
|
|
750
|
-
|
|
1087
|
+
writeStore2(store);
|
|
751
1088
|
}
|
|
752
1089
|
if (!expected) return { status: "continue" };
|
|
753
1090
|
let verdict = evaluateCompat(pkg.version, expected, cluster);
|
|
@@ -770,6 +1107,7 @@ async function refreshAndMaybeNotify(host, pkg, allowAutoSync) {
|
|
|
770
1107
|
const canAutoSync = allowAutoSync && isPublicAeCliPackage(pkg.name) && isAutoSyncTargetEligible(expected);
|
|
771
1108
|
if (canAutoSync) {
|
|
772
1109
|
const confirmed = await fetchCliConfig(host, token);
|
|
1110
|
+
if (confirmed) rememberKbAutoDiscoveryFeature(host, token, confirmed.projectSemanticKbAutoDiscovery ?? false);
|
|
773
1111
|
if (confirmed?.aeCliVersion) {
|
|
774
1112
|
expected = confirmed.aeCliVersion;
|
|
775
1113
|
cluster = confirmed.clusterVersion ?? cluster ?? "";
|
|
@@ -783,7 +1121,7 @@ async function refreshAndMaybeNotify(host, pkg, allowAutoSync) {
|
|
|
783
1121
|
lastFetchedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
784
1122
|
};
|
|
785
1123
|
store.hosts[key] = entry;
|
|
786
|
-
|
|
1124
|
+
writeStore2(store);
|
|
787
1125
|
if (verdict.kind === "ok" && !pendingSkills) {
|
|
788
1126
|
pendingHostCompatNotice = void 0;
|
|
789
1127
|
pendingUpdateNotice = void 0;
|
|
@@ -823,7 +1161,7 @@ async function refreshAndMaybeNotify(host, pkg, allowAutoSync) {
|
|
|
823
1161
|
setUpdateNotice(pkg, expected, cluster || "", verdict, state, result.stage, failure);
|
|
824
1162
|
entry.lastNotifiedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
825
1163
|
store.hosts[key] = entry;
|
|
826
|
-
|
|
1164
|
+
writeStore2(store);
|
|
827
1165
|
return { status: "continue" };
|
|
828
1166
|
}
|
|
829
1167
|
}
|
|
@@ -832,12 +1170,12 @@ async function refreshAndMaybeNotify(host, pkg, allowAutoSync) {
|
|
|
832
1170
|
const notice = verdict.kind === "ok" && pendingSkills ? `[ae-cli] CLI ${expected} is installed, but its Skills are not synchronized.
|
|
833
1171
|
Run: ae-cli update` : formatCompatNotice(verdict);
|
|
834
1172
|
if (!notice) return { status: "continue" };
|
|
835
|
-
const dueForTip =
|
|
1173
|
+
const dueForTip = shouldNotifyCompat(entry);
|
|
836
1174
|
if (!dueForTip) {
|
|
837
1175
|
pendingHostCompatNotice = void 0;
|
|
838
1176
|
pendingUpdateNotice = void 0;
|
|
839
1177
|
store.hosts[key] = entry;
|
|
840
|
-
|
|
1178
|
+
writeStore2(store);
|
|
841
1179
|
return { status: "continue" };
|
|
842
1180
|
}
|
|
843
1181
|
setUpdateNotice(
|
|
@@ -852,7 +1190,7 @@ async function refreshAndMaybeNotify(host, pkg, allowAutoSync) {
|
|
|
852
1190
|
printNotice(notice);
|
|
853
1191
|
entry.lastNotifiedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
854
1192
|
store.hosts[key] = entry;
|
|
855
|
-
|
|
1193
|
+
writeStore2(store);
|
|
856
1194
|
return { status: "continue" };
|
|
857
1195
|
}
|
|
858
1196
|
async function runHostCompatCheck(pkg, hostOverride, argv = process.argv) {
|
|
@@ -1007,30 +1345,51 @@ async function formatOutput(data, format, jqExpr) {
|
|
|
1007
1345
|
};
|
|
1008
1346
|
return JSON.stringify(envelope, null, 2);
|
|
1009
1347
|
}
|
|
1010
|
-
function formatError(type, message, hint, code, meta) {
|
|
1348
|
+
function formatError(type, message, hint, code, meta, details) {
|
|
1011
1349
|
const envelope = {
|
|
1012
1350
|
ok: false,
|
|
1013
|
-
error: { type, message, hint, code },
|
|
1351
|
+
error: { type, message, hint, code, ...details === void 0 ? {} : { details } },
|
|
1014
1352
|
...meta && Object.keys(meta).length > 0 ? { meta } : {}
|
|
1015
1353
|
};
|
|
1016
1354
|
return JSON.stringify(envelope, null, 2);
|
|
1017
1355
|
}
|
|
1018
1356
|
function printError(type, message, hint, code, meta, options = {}) {
|
|
1019
|
-
const formatted = formatError(type, message, hint, code, meta);
|
|
1357
|
+
const formatted = formatError(type, message, hint, code, meta, options.details);
|
|
1020
1358
|
process.stderr.write(formatted + "\n");
|
|
1021
1359
|
if (options.log !== false) {
|
|
1022
1360
|
logger.error(`[${type}] ${message}${hint ? " | " + hint : ""}`);
|
|
1023
1361
|
}
|
|
1024
1362
|
}
|
|
1025
|
-
|
|
1363
|
+
function sensitiveRecoveryData(data) {
|
|
1364
|
+
const safe = {};
|
|
1365
|
+
const item = {};
|
|
1366
|
+
if (typeof data?.item?.id === "string") item.id = data.item.id;
|
|
1367
|
+
if (Number.isSafeInteger(data?.item?.contentRevision)) item.contentRevision = data.item.contentRevision;
|
|
1368
|
+
if (Object.keys(item).length > 0) safe.item = item;
|
|
1369
|
+
if (["markdown", "text"].includes(data?.contentFormat)) safe.contentFormat = data.contentFormat;
|
|
1370
|
+
if (typeof data?.out === "string") safe.out = data.out;
|
|
1371
|
+
return safe;
|
|
1372
|
+
}
|
|
1373
|
+
async function printOutput(data, format, jqExpr, options = {}) {
|
|
1026
1374
|
try {
|
|
1027
1375
|
process.stdout.write(await formatOutput(data, format, jqExpr) + "\n");
|
|
1028
1376
|
} catch (error) {
|
|
1029
1377
|
if (error?.code === "OUTPUT_PROJECTION_FAILED") {
|
|
1030
|
-
|
|
1378
|
+
if (options.redactErrors) {
|
|
1379
|
+
error.message = "Invalid --jq expression: output projection failed; source content is omitted from diagnostics.";
|
|
1380
|
+
error.hint = "The read completed. Fix --jq and explicitly run read-text again without --out, or inspect the file at data.out if present. This error result excludes source content.";
|
|
1381
|
+
}
|
|
1382
|
+
const recovery = JSON.parse(formatError(
|
|
1383
|
+
"validation",
|
|
1384
|
+
error.message,
|
|
1385
|
+
error.hint,
|
|
1386
|
+
error.code,
|
|
1387
|
+
options.redactErrors ? void 0 : readOutputMetadata(data)
|
|
1388
|
+
));
|
|
1389
|
+
const recoveryData = options.redactErrors ? sensitiveRecoveryData(unwrapOutputData(data)) : unwrapOutputData(data);
|
|
1031
1390
|
await new Promise((resolve, reject) => {
|
|
1032
1391
|
process.stdout.write(
|
|
1033
|
-
JSON.stringify({ ...recovery, data:
|
|
1392
|
+
JSON.stringify({ ...recovery, data: recoveryData }, null, 2) + "\n",
|
|
1034
1393
|
(error2) => error2 ? reject(error2) : resolve()
|
|
1035
1394
|
);
|
|
1036
1395
|
});
|
|
@@ -1040,10 +1399,17 @@ async function printOutput(data, format, jqExpr) {
|
|
|
1040
1399
|
}
|
|
1041
1400
|
|
|
1042
1401
|
export {
|
|
1402
|
+
exactVersion,
|
|
1403
|
+
installHost,
|
|
1404
|
+
getLocalCliPackageInfo,
|
|
1405
|
+
newInstallationReport,
|
|
1406
|
+
failInstallation,
|
|
1043
1407
|
recordVersionSyncResult,
|
|
1044
1408
|
buildVersionInstallPlan,
|
|
1045
1409
|
friendlyVersionSyncFailure,
|
|
1046
1410
|
installVersion,
|
|
1411
|
+
resolveKbAutoDiscoveryFeature,
|
|
1412
|
+
shouldSkipAutoSync,
|
|
1047
1413
|
fetchCliConfig,
|
|
1048
1414
|
getCachedCompatForHost,
|
|
1049
1415
|
formatCapabilityMissCompatHint,
|