@thinkingai/ae-cli 6.1.7 → 6.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/README.md +219 -150
  2. package/README.zh.md +227 -159
  3. package/dist/{auth-UPGUOKTW.js → auth-56Z45UVR.js} +1 -1
  4. package/dist/{capability-TQ5KU5Q6.js → capability-2H6PAOA3.js} +1 -1
  5. package/dist/{capability-AJC5CBRT.js → capability-YPOQX6PL.js} +1 -1
  6. package/dist/{chunk-4Q5TNP4Q.js → chunk-J7MZHDHQ.js} +48 -24
  7. package/dist/chunk-KTYR3U6D.js +1012 -0
  8. package/dist/{chunk-6CHYBI64.js → chunk-LHVM35J4.js} +1 -1
  9. package/dist/{chunk-ISY6HMHM.js → chunk-PVBYJWC2.js} +1 -1
  10. package/dist/{config-PMPISN5Y.js → config-BSSALXEN.js} +1 -1
  11. package/dist/index.js +79 -22
  12. package/dist/{metadata-P5GIVMZ6.js → metadata-AN3YFZEV.js} +2 -2
  13. package/dist/{metadata-ELUAR3EJ.js → metadata-JIQ77HFY.js} +2 -2
  14. package/dist/{model-OYQLXCQY.js → model-JASQVOFD.js} +1 -1
  15. package/dist/{raw-TPB7KSZO.js → raw-XJCAT3HX.js} +1 -1
  16. package/dist/{sync-EFJKFZK2.js → sync-QFP4XFN3.js} +2 -2
  17. package/dist/{te-agent-BOQ7ZY56.js → te-agent-JHUG6DVV.js} +28 -1
  18. package/dist/{te-analysis-HZIP6SYW.js → te-analysis-IWQZO423.js} +213 -129
  19. package/dist/{te-analysis-ZT2MYAHL.js → te-analysis-X222IRYR.js} +213 -129
  20. package/dist/{te-dataops-VYN3IOKO.js → te-dataops-PZQ5NQLY.js} +1 -1
  21. package/dist/{te-dataops-Y5QAPPPF.js → te-dataops-XTWVTJCA.js} +1 -1
  22. package/dist/{te-engage-2FSDUOIZ.js → te-engage-E7F4HTXU.js} +87 -4
  23. package/dist/{te-engage-BMJ6UOUU.js → te-engage-NLZUPSBK.js} +87 -4
  24. package/dist/{te-experiment-KYRWAUZY.js → te-experiment-D32TB6ZB.js} +2 -2
  25. package/dist/{te-experiment-4JC7WUKW.js → te-experiment-N63WF7XA.js} +2 -2
  26. package/dist/te-system-AH7DMCAQ.js +1706 -0
  27. package/dist/{te-team-PDKKW7Q5.js → te-team-BQ3SKSZV.js} +1 -1
  28. package/dist/update-DKG6UXEM.js +123 -0
  29. package/package.json +8 -3
  30. package/skills/ae-agent/SKILL.md +2 -2
  31. package/skills/ae-agent/references/create-automation.md +12 -0
  32. package/skills/ae-agent/references/update-automation.md +11 -3
  33. package/skills/ae-analysis/SKILL.md +1 -1
  34. package/skills/ae-analysis/references/alert_create.md +2 -3
  35. package/skills/ae-analysis/references/alert_update.md +2 -3
  36. package/skills/ae-analysis/references/analysis_gateway_assets.md +6 -4
  37. package/skills/ae-analysis/references/bi_panel_create.md +12 -4
  38. package/skills/ae-analysis/references/bi_panel_update.md +11 -8
  39. package/skills/ae-analysis/references/bi_panel_version_publish.md +3 -1
  40. package/skills/ae-analysis/references/command_index.md +10 -7
  41. package/skills/ae-analysis/references/dashboard_daily_report_get.md +18 -0
  42. package/skills/ae-analysis/references/dashboard_daily_report_send.md +12 -9
  43. package/skills/ae-analysis/references/dashboard_daily_report_send_status.md +22 -0
  44. package/skills/ae-analysis/references/dashboard_daily_report_update.md +13 -6
  45. package/skills/ae-analysis/references/drilldown_user_events_export.md +5 -0
  46. package/skills/ae-analysis/references/drilldown_user_events_run.md +6 -0
  47. package/skills/ae-analysis/references/super_metadata_batch_create.md +29 -0
  48. package/skills/ae-analysis/references/super_metadata_batch_edit.md +24 -0
  49. package/skills/ae-engage/SKILL.md +32 -5
  50. package/skills/ae-engage/references/activity-topic.md +34 -1
  51. package/skills/ae-engage/references/add-channel.md +2 -3
  52. package/skills/ae-engage/references/build-task-save-guide.md +17 -3
  53. package/skills/ae-engage/references/channel-detail.md +2 -3
  54. package/skills/ae-engage/references/channel-list.md +2 -5
  55. package/skills/ae-engage/references/channel-mgmt.md +17 -2
  56. package/skills/ae-engage/references/channel-test-send.md +1 -1
  57. package/skills/ae-engage/references/channel-update-config.md +1 -1
  58. package/skills/ae-engage/references/delete-channel.md +2 -3
  59. package/skills/ae-engage/references/save-flow.md +6 -3
  60. package/skills/ae-engage/references/save-task.md +12 -15
  61. package/skills/ae-engage/references/scene-config-channel.md +28 -4
  62. package/skills/ae-engage/references/scene-config-group.md +1 -1
  63. package/skills/ae-engage/references/scene-config-item.md +1 -1
  64. package/skills/ae-engage/references/scene-config-metric.md +1 -1
  65. package/skills/ae-engage/references/scene-config-param.md +1 -1
  66. package/skills/ae-engage/references/scene-preset-metric.md +1 -1
  67. package/skills/ae-engage/references/scene-strategy-audience.md +680 -0
  68. package/skills/ae-engage/references/scene-strategy.md +16 -1
  69. package/skills/ae-engage/references/scene-template.md +1 -1
  70. package/skills/ae-engage/references/update-channel-status.md +2 -3
  71. package/skills/ae-generate-tracking-code/SKILL.md +5 -5
  72. package/skills/ae-generate-tracking-code/references/autotrack-enum.md +4 -4
  73. package/skills/ae-generate-tracking-code/references/client-sdk-insert.md +1 -1
  74. package/skills/ae-generate-tracking-code/references/debug-script.md +2 -2
  75. package/skills/ae-generate-tracking-code/references/logbus-config.md +1 -1
  76. package/skills/ae-generate-tracking-code/references/restful-call.md +1 -1
  77. package/skills/ae-generate-tracking-code/references/sdk-index.md +36 -36
  78. package/skills/ae-system/SKILL.md +357 -0
  79. package/dist/chunk-IMMGMU54.js +0 -674
  80. package/skills/ae-analysis/references/alert_definition_schema_get.md +0 -24
@@ -0,0 +1,1012 @@
1
+ import {
2
+ peekCliToken
3
+ } from "./chunk-E7UXXHO3.js";
4
+ import {
5
+ getToken
6
+ } from "./chunk-IR4ZLVPW.js";
7
+ import {
8
+ getActiveHost,
9
+ getConfigDir,
10
+ isAeSandboxRuntime,
11
+ normalizeUrl
12
+ } from "./chunk-V6FR6WTW.js";
13
+ import {
14
+ logger
15
+ } from "./chunk-LYVNONC4.js";
16
+
17
+ // src/core/version-sync.ts
18
+ import { spawnSync } from "child_process";
19
+ import fs from "fs";
20
+ import path from "path";
21
+
22
+ // src/core/update-check.ts
23
+ function shouldSkipUpdateCheck(argv = process.argv) {
24
+ if (process.env.AE_CLI_NO_UPDATE_CHECK === "1") return true;
25
+ if (process.env.CI === "true" || process.env.CI === "1") return true;
26
+ return argv.slice(2).some((arg) => {
27
+ if (arg === "--no-update-check") return true;
28
+ if (arg === "--version" || arg === "-V" || arg === "--help" || arg === "-h") return true;
29
+ return false;
30
+ });
31
+ }
32
+ function parseVersion(version) {
33
+ const core = version.trim().split("-")[0];
34
+ return core.split(".").map((part) => {
35
+ const n = Number.parseInt(part, 10);
36
+ return Number.isFinite(n) ? n : 0;
37
+ });
38
+ }
39
+ function isNewer(latest, current) {
40
+ const a = parseVersion(latest);
41
+ const b = parseVersion(current);
42
+ const len = Math.max(a.length, b.length, 3);
43
+ for (let i = 0; i < len; i += 1) {
44
+ const av = a[i] ?? 0;
45
+ const bv = b[i] ?? 0;
46
+ if (av > bv) return true;
47
+ if (av < bv) return false;
48
+ }
49
+ return false;
50
+ }
51
+
52
+ // src/core/legacy-cli-version-map.ts
53
+ var HISTORICAL_CLUSTER_TO_PUBLIC_PIN = {
54
+ "6.0.0": "1.0.15",
55
+ "6.0.1": "1.0.15",
56
+ "6.0.2": "1.0.18",
57
+ "6.0.3": "1.0.18",
58
+ "6.0.4": "1.0.20",
59
+ "6.0.5": "1.0.21",
60
+ "6.0.6": "1.0.22",
61
+ "6.0.7": "1.0.22",
62
+ "6.0.8": "1.0.22",
63
+ "6.0.9": "1.0.22",
64
+ "6.0.10": "1.0.24",
65
+ "6.0.11": "1.0.24",
66
+ "6.0.12": "1.0.27",
67
+ "6.0.13": "1.0.28",
68
+ "6.0.14": "1.0.28",
69
+ "6.0.15": "1.0.30",
70
+ "6.0.16": "6.0.16",
71
+ "6.0.17": "6.0.17",
72
+ "6.0.18": "6.0.18",
73
+ "6.0.19": "6.0.18",
74
+ "6.0.20": "6.0.20",
75
+ "6.0.21": "6.0.20",
76
+ "6.0.22": "6.0.22",
77
+ "6.0.23": "6.0.22",
78
+ "6.0.24": "6.0.24",
79
+ "6.0.25": "6.0.24",
80
+ "6.0.26": "6.0.24"
81
+ };
82
+
83
+ // src/core/version-compat.ts
84
+ var OPEN_SOURCE_AE_CLI_PACKAGE = "@thinkingai/ae-cli";
85
+ var AE_CLI_SKILLS_REPO = "ThinkingAIAgenticEngine/ae-cli";
86
+ var PUBLISHED_EXTERNAL_VERSIONS = /* @__PURE__ */ new Set([
87
+ "1.0.15",
88
+ "1.0.18",
89
+ "1.0.20",
90
+ "1.0.21",
91
+ "1.0.22",
92
+ "1.0.24",
93
+ "1.0.27",
94
+ "1.0.28",
95
+ "1.0.30",
96
+ "6.0.16",
97
+ "6.0.17",
98
+ "6.0.18",
99
+ "6.0.20",
100
+ "6.0.22",
101
+ "6.0.24",
102
+ "6.0.27",
103
+ "6.0.28",
104
+ "6.0.29",
105
+ "6.0.30",
106
+ "6.0.31"
107
+ ]);
108
+ function normalizeCliVersion(version) {
109
+ return version.trim().replace(/^v/i, "");
110
+ }
111
+ function versionLine(version) {
112
+ const parts = normalizeCliVersion(version).split(/[.-]/);
113
+ if (parts.length < 2) return normalizeCliVersion(version);
114
+ return `${parts[0]}.${parts[1]}`;
115
+ }
116
+ function sameVersionLine(a, b) {
117
+ return versionLine(a) === versionLine(b);
118
+ }
119
+ function parseVersionParts(version) {
120
+ const core = normalizeCliVersion(version).split("-")[0];
121
+ return core.split(".").map((p) => {
122
+ const n = Number.parseInt(p, 10);
123
+ return Number.isFinite(n) ? n : 0;
124
+ });
125
+ }
126
+ function compareNormalized(a, b) {
127
+ const pa = parseVersionParts(a);
128
+ const pb = parseVersionParts(b);
129
+ const len = Math.max(pa.length, pb.length, 3);
130
+ for (let i = 0; i < len; i += 1) {
131
+ const av = pa[i] ?? 0;
132
+ const bv = pb[i] ?? 0;
133
+ if (av > bv) return 1;
134
+ if (av < bv) return -1;
135
+ }
136
+ return 0;
137
+ }
138
+ function evaluateCompat(localRaw, expectedRaw, clusterLine) {
139
+ const local = normalizeCliVersion(localRaw);
140
+ const expected = normalizeCliVersion(expectedRaw);
141
+ if (!local || !expected) return { kind: "ok" };
142
+ const line = clusterLine && clusterLine.trim() || versionLine(expected);
143
+ if (!sameVersionLine(local, expected)) {
144
+ return { kind: "line_mismatch", local, expected, line };
145
+ }
146
+ if (clusterLine && clusterLine.trim()) {
147
+ const clusterNorm = clusterLine.trim();
148
+ const localLine = versionLine(local);
149
+ if (localLine !== clusterNorm && localLine !== versionLine(clusterNorm)) {
150
+ return { kind: "line_mismatch", local, expected, line: clusterNorm };
151
+ }
152
+ }
153
+ if (local === expected) return { kind: "ok" };
154
+ if (isNewer(local, expected)) {
155
+ return { kind: "local_newer", local, expected, line };
156
+ }
157
+ if (isNewer(expected, local)) {
158
+ return { kind: "local_older", local, expected, line };
159
+ }
160
+ return { kind: "ok" };
161
+ }
162
+ function resolvePinTarget(expectedVersion) {
163
+ const expected = expectedVersion.trim().replace(/^v/i, "");
164
+ if (!expected) return null;
165
+ if (PUBLISHED_EXTERNAL_VERSIONS.has(expected)) return expected;
166
+ const historical = HISTORICAL_CLUSTER_TO_PUBLIC_PIN[expected];
167
+ if (historical && PUBLISHED_EXTERNAL_VERSIONS.has(historical)) {
168
+ return historical;
169
+ }
170
+ const line = versionLine(expected);
171
+ const candidates = [...PUBLISHED_EXTERNAL_VERSIONS].filter((tag) => {
172
+ if (versionLine(tag) !== line) return false;
173
+ return compareNormalized(tag, expected) <= 0;
174
+ });
175
+ if (candidates.length === 0) return null;
176
+ candidates.sort((a, b) => compareNormalized(a, b));
177
+ return candidates[candidates.length - 1] ?? null;
178
+ }
179
+ function formatPinCommands(expectedVersion) {
180
+ const ver = resolvePinTarget(expectedVersion);
181
+ if (!ver) return [];
182
+ return [
183
+ `npm i -g ${OPEN_SOURCE_AE_CLI_PACKAGE}@${ver}`,
184
+ `npx skills add ${AE_CLI_SKILLS_REPO}#v${ver} -g -y`
185
+ ];
186
+ }
187
+ function formatUpgradeCommands(expectedVersion) {
188
+ const ver = expectedVersion.trim().replace(/^v/i, "");
189
+ if (!/^\d+\.\d+\.\d+(?:[-+].*)?$/.test(ver)) return [];
190
+ return [
191
+ `npm i -g ${OPEN_SOURCE_AE_CLI_PACKAGE}@${ver}`,
192
+ `npx skills add ${AE_CLI_SKILLS_REPO}#v${ver} -g -y`
193
+ ];
194
+ }
195
+ function formatUnifiedUpdateCommand() {
196
+ return "ae-cli update";
197
+ }
198
+ function formatCompatNotice(verdict) {
199
+ if (verdict.kind === "ok") return void 0;
200
+ const canUpdateToHost = formatUpgradeCommands(verdict.expected).length > 0;
201
+ if (verdict.kind === "local_newer") {
202
+ if (canUpdateToHost) {
203
+ return [
204
+ `[ae-cli] Host compat: local ${verdict.local} > environment ${verdict.expected} (cluster ${verdict.line}).`,
205
+ ` Run: ${formatUnifiedUpdateCommand()} to sync CLI + skills to this host.`,
206
+ ` Or update cluster components to the latest version.`
207
+ ].join("\n");
208
+ }
209
+ return [
210
+ `[ae-cli] Host compat: local ${verdict.local} > environment ${verdict.expected} (cluster ${verdict.line}).`,
211
+ ` No public GitHub/npm release on this line is \u2264 environment ${verdict.expected}.`,
212
+ ` Update cluster components to the latest version to match your CLI.`
213
+ ].join("\n");
214
+ }
215
+ if (verdict.kind === "local_older") {
216
+ if (canUpdateToHost) {
217
+ return [
218
+ `[ae-cli] Host compat: local ${verdict.local} < environment ${verdict.expected} (cluster ${verdict.line}).`,
219
+ ` Run: ${formatUnifiedUpdateCommand()} to sync CLI + skills to this host.`
220
+ ].join("\n");
221
+ }
222
+ return [
223
+ `[ae-cli] Host compat: local ${verdict.local} < environment ${verdict.expected} (cluster ${verdict.line}).`,
224
+ ` No installable semver target for environment ${verdict.expected}.`,
225
+ ` Ask the platform owner for the install package, or wait for a published build.`
226
+ ].join("\n");
227
+ }
228
+ if (canUpdateToHost) {
229
+ return [
230
+ `[ae-cli] Host compat: local ${verdict.local} line mismatches environment ${verdict.expected} (cluster ${verdict.line}).`,
231
+ ` Run: ${formatUnifiedUpdateCommand()} to sync CLI + skills to this host.`,
232
+ ` Or update cluster components to the latest version to match your CLI line.`
233
+ ].join("\n");
234
+ }
235
+ return [
236
+ `[ae-cli] Host compat: local ${verdict.local} line mismatches environment ${verdict.expected} (cluster ${verdict.line}).`,
237
+ ` No public GitHub/npm release for this line; update cluster components to the latest version, or install a published build on the same line.`
238
+ ].join("\n");
239
+ }
240
+
241
+ // src/core/version-sync.ts
242
+ var COMMAND_TIMEOUT_MS = 12e4;
243
+ var AUTO_ATTEMPT_INTERVAL_MS = 60 * 60 * 1e3;
244
+ var MAX_AUTO_ATTEMPTS_PER_DAY = 3;
245
+ var LOCK_STALE_MS = 10 * 60 * 1e3;
246
+ var SYNC_STATE_FILE = "version-sync.json";
247
+ var SYNC_LOCK_FILE = "version-sync.lock";
248
+ function syncStatePath() {
249
+ return path.join(getConfigDir(), SYNC_STATE_FILE);
250
+ }
251
+ function syncLockPath() {
252
+ return path.join(getConfigDir(), SYNC_LOCK_FILE);
253
+ }
254
+ function readSyncStore() {
255
+ try {
256
+ const raw = JSON.parse(fs.readFileSync(syncStatePath(), "utf8"));
257
+ if (raw && typeof raw === "object" && raw.hosts && typeof raw.hosts === "object") {
258
+ return raw;
259
+ }
260
+ } catch {
261
+ }
262
+ return { hosts: {} };
263
+ }
264
+ function writeSyncStore(store) {
265
+ try {
266
+ fs.mkdirSync(getConfigDir(), { recursive: true });
267
+ fs.writeFileSync(syncStatePath(), JSON.stringify(store, null, 2));
268
+ } catch {
269
+ }
270
+ }
271
+ function localDay(now) {
272
+ const year = now.getFullYear();
273
+ const month = String(now.getMonth() + 1).padStart(2, "0");
274
+ const day = String(now.getDate()).padStart(2, "0");
275
+ return `${year}-${month}-${day}`;
276
+ }
277
+ function freshAttemptEntry(target, now) {
278
+ return {
279
+ target,
280
+ attemptDay: localDay(now),
281
+ attemptsToday: 0
282
+ };
283
+ }
284
+ function evaluateAutoAttempt(existing, target, now = /* @__PURE__ */ new Date()) {
285
+ const today = localDay(now);
286
+ const entry = !existing || existing.target !== target || existing.attemptDay !== today ? freshAttemptEntry(target, now) : { ...existing };
287
+ if (entry.attemptsToday >= MAX_AUTO_ATTEMPTS_PER_DAY) {
288
+ return { allowed: false, reason: "daily_limit", entry };
289
+ }
290
+ if (entry.lastAttemptAt) {
291
+ const elapsed = now.getTime() - new Date(entry.lastAttemptAt).getTime();
292
+ if (Number.isFinite(elapsed) && elapsed < AUTO_ATTEMPT_INTERVAL_MS) {
293
+ return { allowed: false, reason: "hourly_limit", entry };
294
+ }
295
+ }
296
+ return { allowed: true, entry };
297
+ }
298
+ function reserveAutoAttempt(host, target, now = /* @__PURE__ */ new Date()) {
299
+ const store = readSyncStore();
300
+ const key = normalizeUrl(host);
301
+ const decision = evaluateAutoAttempt(store.hosts[key], target, now);
302
+ if (!decision.allowed) return decision;
303
+ decision.entry.attemptsToday += 1;
304
+ decision.entry.lastAttemptAt = now.toISOString();
305
+ store.hosts[key] = decision.entry;
306
+ writeSyncStore(store);
307
+ return decision;
308
+ }
309
+ function getPendingSkillsTarget(host) {
310
+ const entry = readSyncStore().hosts[normalizeUrl(host)];
311
+ return entry?.pendingSkills ? entry.target : void 0;
312
+ }
313
+ function recordVersionSyncResult(host, target, result) {
314
+ const store = readSyncStore();
315
+ const key = normalizeUrl(host);
316
+ const existing = store.hosts[key] ?? freshAttemptEntry(target, /* @__PURE__ */ new Date());
317
+ const entry = existing.target === target ? existing : freshAttemptEntry(target, /* @__PURE__ */ new Date());
318
+ if (result.ok) {
319
+ entry.pendingSkills = false;
320
+ delete entry.lastFailureStage;
321
+ } else {
322
+ entry.pendingSkills = result.skillsPending;
323
+ entry.lastFailureStage = result.stage;
324
+ }
325
+ store.hosts[key] = entry;
326
+ writeSyncStore(store);
327
+ }
328
+ function isAutoSyncTargetEligible(version) {
329
+ const normalized = normalizeCliVersion(version);
330
+ const parts = normalized.split("-")[0].split(".").map((part) => Number.parseInt(part, 10));
331
+ if (parts.length < 3 || parts.some((part) => !Number.isFinite(part))) return false;
332
+ const [major, minor, patch] = parts;
333
+ if (major > 6) return true;
334
+ if (major !== 6) return false;
335
+ if (minor > 1) return true;
336
+ if (minor === 1) return patch >= 5;
337
+ if (minor === 0) return patch >= 33;
338
+ return false;
339
+ }
340
+ function isPublicAeCliPackage(packageName) {
341
+ return packageName === OPEN_SOURCE_AE_CLI_PACKAGE;
342
+ }
343
+ function resolveNodeTool(tool, execPath = process.execPath, platform = process.platform) {
344
+ const fileName = platform === "win32" ? `${tool}.cmd` : tool;
345
+ const sibling = path.join(path.dirname(execPath), fileName);
346
+ return fs.existsSync(sibling) ? sibling : fileName;
347
+ }
348
+ function buildVersionInstallPlan(targetRaw, globalRoot = "<npm-root>") {
349
+ const target = normalizeCliVersion(targetRaw);
350
+ const localSkills = path.join(globalRoot, "@thinkingai", "ae-cli", "skills");
351
+ return {
352
+ target,
353
+ commands: [
354
+ `npm install -g ${OPEN_SOURCE_AE_CLI_PACKAGE}@${target}`,
355
+ `npx -y skills add ${localSkills} -g -y`,
356
+ `npx -y skills add ${AE_CLI_SKILLS_REPO}#v${target} -g -y`
357
+ ],
358
+ skillsSources: ["installed-package", "github-fallback"]
359
+ };
360
+ }
361
+ function defaultRunner(command, args, timeoutMs) {
362
+ const result = spawnSync(command, args, {
363
+ encoding: "utf8",
364
+ maxBuffer: 2 * 1024 * 1024,
365
+ stdio: ["ignore", "pipe", "pipe"],
366
+ timeout: timeoutMs,
367
+ windowsHide: true
368
+ });
369
+ return {
370
+ status: result.status,
371
+ stdout: result.stdout || "",
372
+ stderr: result.stderr || "",
373
+ error: result.error,
374
+ signal: result.signal
375
+ };
376
+ }
377
+ function resultMessage(result) {
378
+ if (result.error?.message) return result.error.message;
379
+ const detail = (result.stderr || result.stdout).trim().split(/\r?\n/).filter(Boolean).slice(-3).join(" ");
380
+ if (detail) return detail;
381
+ if (result.signal) return `Command terminated by ${result.signal}`;
382
+ return `Command exited with status ${result.status ?? "unknown"}`;
383
+ }
384
+ function classifyFailure(result) {
385
+ const text = `${result.error?.message || ""}
386
+ ${result.stderr}
387
+ ${result.stdout}`;
388
+ if (result.error && result.error.code === "ETIMEDOUT") return "timeout";
389
+ if (/timed?\s*out|ETIMEDOUT/i.test(text)) return "timeout";
390
+ if (/EACCES|EPERM|permission denied|access is denied/i.test(text)) return "permission";
391
+ if (/EAI_AGAIN|ENOTFOUND|ECONNRESET|ECONNREFUSED|network|unable to access|could not resolve|certificate|SSL/i.test(text)) {
392
+ return "network";
393
+ }
394
+ if (/E404|404 Not Found|no matching version|couldn't find remote ref|not found/i.test(text)) {
395
+ return "package_missing";
396
+ }
397
+ return "unknown";
398
+ }
399
+ function friendlyVersionSyncFailure(result) {
400
+ if (result.cause === "busy") {
401
+ return "Another ae-cli process is synchronizing the global installation.";
402
+ }
403
+ if (result.cause === "permission") {
404
+ return "The global npm installation directory is not writable.";
405
+ }
406
+ if (result.cause === "timeout") {
407
+ return "The npm or Skills download timed out.";
408
+ }
409
+ if (result.cause === "network") {
410
+ return "The npm registry or Skills source could not be reached.";
411
+ }
412
+ if (result.cause === "package_missing") {
413
+ return "The required CLI package or Skills tag is not available.";
414
+ }
415
+ if (result.cause === "validation") {
416
+ return "The installed package could not be validated.";
417
+ }
418
+ return `Version synchronization failed at ${result.stage}.`;
419
+ }
420
+ function commandSucceeded(result) {
421
+ return !result.error && result.status === 0;
422
+ }
423
+ function acquireInstallLock(now) {
424
+ fs.mkdirSync(getConfigDir(), { recursive: true });
425
+ const file = syncLockPath();
426
+ try {
427
+ return fs.openSync(file, "wx");
428
+ } catch {
429
+ try {
430
+ const age = now.getTime() - fs.statSync(file).mtimeMs;
431
+ if (age >= LOCK_STALE_MS) {
432
+ fs.unlinkSync(file);
433
+ return fs.openSync(file, "wx");
434
+ }
435
+ } catch {
436
+ try {
437
+ return fs.openSync(file, "wx");
438
+ } catch {
439
+ return null;
440
+ }
441
+ }
442
+ return null;
443
+ }
444
+ }
445
+ function releaseInstallLock(fd) {
446
+ try {
447
+ fs.closeSync(fd);
448
+ } catch {
449
+ }
450
+ try {
451
+ fs.unlinkSync(syncLockPath());
452
+ } catch {
453
+ }
454
+ }
455
+ function validateInstalledPackage(npm, target, runner) {
456
+ const rootResult = runner(npm, ["root", "-g"], COMMAND_TIMEOUT_MS);
457
+ if (!commandSucceeded(rootResult)) {
458
+ return { ok: false, message: resultMessage(rootResult) };
459
+ }
460
+ const globalRoot = rootResult.stdout.trim();
461
+ const packageRoot = path.join(globalRoot, "@thinkingai", "ae-cli");
462
+ const packageJson = path.join(packageRoot, "package.json");
463
+ const skillsPath = path.join(packageRoot, "skills");
464
+ try {
465
+ const installed = JSON.parse(fs.readFileSync(packageJson, "utf8"));
466
+ if (normalizeCliVersion(installed.version || "") !== target) {
467
+ return {
468
+ ok: false,
469
+ message: `Installed package version ${installed.version || "unknown"} does not match ${target}`
470
+ };
471
+ }
472
+ if (!fs.statSync(skillsPath).isDirectory()) {
473
+ return { ok: false, message: `Installed package has no skills directory: ${skillsPath}` };
474
+ }
475
+ return { ok: true, skillsPath };
476
+ } catch (error) {
477
+ return { ok: false, message: error?.message || String(error) };
478
+ }
479
+ }
480
+ function installVersion(targetRaw, options = {}) {
481
+ const target = normalizeCliVersion(targetRaw);
482
+ const runner = options.runner ?? defaultRunner;
483
+ const progress = options.progress ?? (() => {
484
+ });
485
+ const npm = resolveNodeTool("npm");
486
+ const npx = resolveNodeTool("npx");
487
+ const lock = acquireInstallLock(options.now ?? /* @__PURE__ */ new Date());
488
+ if (lock === null) {
489
+ return {
490
+ ok: false,
491
+ stage: "lock",
492
+ cliInstalled: false,
493
+ skillsPending: Boolean(options.skipCliInstall),
494
+ cause: "busy",
495
+ message: "Another ae-cli process is already synchronizing the global installation."
496
+ };
497
+ }
498
+ let cliInstalled = Boolean(options.skipCliInstall);
499
+ try {
500
+ if (!options.skipCliInstall) {
501
+ progress(`[ae-cli] [1/2] Installing ae-cli ${target}...`);
502
+ const npmResult = runner(
503
+ npm,
504
+ ["install", "-g", `${OPEN_SOURCE_AE_CLI_PACKAGE}@${target}`],
505
+ COMMAND_TIMEOUT_MS
506
+ );
507
+ if (!commandSucceeded(npmResult)) {
508
+ return {
509
+ ok: false,
510
+ stage: "npm_install",
511
+ cliInstalled: false,
512
+ skillsPending: false,
513
+ cause: classifyFailure(npmResult),
514
+ message: resultMessage(npmResult)
515
+ };
516
+ }
517
+ cliInstalled = true;
518
+ }
519
+ const installed = validateInstalledPackage(npm, target, runner);
520
+ if (!installed.ok) {
521
+ return {
522
+ ok: false,
523
+ stage: "package_validation",
524
+ cliInstalled,
525
+ skillsPending: cliInstalled,
526
+ cause: "validation",
527
+ message: installed.message
528
+ };
529
+ }
530
+ progress("[ae-cli] [2/2] Synchronizing Skills from the installed npm package...");
531
+ const localResult = runner(
532
+ npx,
533
+ ["-y", "skills", "add", installed.skillsPath, "-g", "-y"],
534
+ COMMAND_TIMEOUT_MS
535
+ );
536
+ if (commandSucceeded(localResult)) {
537
+ return { ok: true, cliInstalled, skillsSource: "local" };
538
+ }
539
+ progress("[ae-cli] Local Skills sync did not complete; trying the GitHub fallback...");
540
+ const githubResult = runner(
541
+ npx,
542
+ ["-y", "skills", "add", `${AE_CLI_SKILLS_REPO}#v${target}`, "-g", "-y"],
543
+ COMMAND_TIMEOUT_MS
544
+ );
545
+ if (commandSucceeded(githubResult)) {
546
+ return { ok: true, cliInstalled, skillsSource: "github" };
547
+ }
548
+ return {
549
+ ok: false,
550
+ stage: "skills_github",
551
+ cliInstalled,
552
+ skillsPending: cliInstalled,
553
+ cause: classifyFailure(githubResult),
554
+ message: resultMessage(githubResult) || resultMessage(localResult)
555
+ };
556
+ } finally {
557
+ releaseInstallLock(lock);
558
+ }
559
+ }
560
+ function autoSyncDirection(current, expected) {
561
+ if (versionLine(current) !== versionLine(expected)) return "switch";
562
+ const currentParts = normalizeCliVersion(current).split("-")[0].split(".").map(Number);
563
+ const expectedParts = normalizeCliVersion(expected).split("-")[0].split(".").map(Number);
564
+ for (let i = 0; i < Math.max(currentParts.length, expectedParts.length, 3); i += 1) {
565
+ const local = currentParts[i] ?? 0;
566
+ const target = expectedParts[i] ?? 0;
567
+ if (local < target) return "upgrade";
568
+ if (local > target) return "downgrade";
569
+ }
570
+ return "switch";
571
+ }
572
+
573
+ // src/core/compat-check.ts
574
+ import fs2 from "fs";
575
+ import path2 from "path";
576
+ var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
577
+ var FETCH_TIMEOUT_MS = 3e3;
578
+ var CLI_CONFIG_PATH = "/v1/ta/cli/config";
579
+ function cacheFilePath() {
580
+ return path2.join(getConfigDir(), "compat-check.json");
581
+ }
582
+ function readStore() {
583
+ try {
584
+ const file = cacheFilePath();
585
+ if (!fs2.existsSync(file)) return { hosts: {} };
586
+ const raw = JSON.parse(fs2.readFileSync(file, "utf8"));
587
+ if (!raw || typeof raw !== "object" || !raw.hosts) return { hosts: {} };
588
+ return raw;
589
+ } catch {
590
+ return { hosts: {} };
591
+ }
592
+ }
593
+ function writeStore(store) {
594
+ try {
595
+ const dir = getConfigDir();
596
+ if (!fs2.existsSync(dir)) fs2.mkdirSync(dir, { recursive: true });
597
+ fs2.writeFileSync(cacheFilePath(), JSON.stringify(store, null, 2));
598
+ } catch {
599
+ }
600
+ }
601
+ function hostKey(host) {
602
+ return normalizeUrl(host);
603
+ }
604
+ function isStale(entry) {
605
+ if (!entry?.lastFetchedAt) return true;
606
+ const age = Date.now() - new Date(entry.lastFetchedAt).getTime();
607
+ return age >= CHECK_INTERVAL_MS;
608
+ }
609
+ function shouldNotify(entry) {
610
+ if (!entry?.lastNotifiedAt) return true;
611
+ const age = Date.now() - new Date(entry.lastNotifiedAt).getTime();
612
+ return age >= CHECK_INTERVAL_MS;
613
+ }
614
+ function shouldSkipCompatCheck(argv = process.argv) {
615
+ if (process.env.AE_CLI_NO_COMPAT_CHECK === "1") return true;
616
+ if (isAeSandboxRuntime()) return true;
617
+ return shouldSkipUpdateCheck(argv);
618
+ }
619
+ function rootCommand(argv) {
620
+ const args = argv.slice(2);
621
+ for (let i = 0; i < args.length; i += 1) {
622
+ const token = args[i];
623
+ if (token === "--") return void 0;
624
+ if (token.startsWith("-")) {
625
+ if (!token.includes("=") && ["--host", "--mcp-url", "--format", "--jq"].includes(token)) {
626
+ i += 1;
627
+ }
628
+ continue;
629
+ }
630
+ return token;
631
+ }
632
+ return void 0;
633
+ }
634
+ function shouldSkipAutoSync(argv = process.argv) {
635
+ const command = rootCommand(argv);
636
+ return command === "update" || command === "auth" || command === "config";
637
+ }
638
+ async function fetchCliConfig(host, cliToken) {
639
+ const url = new URL(CLI_CONFIG_PATH, host.endsWith("/") ? host : `${host}/`);
640
+ url.searchParams.set("cli-token", cliToken);
641
+ const headers = { Accept: "application/json" };
642
+ try {
643
+ const accessToken = await getToken(host);
644
+ if (accessToken) {
645
+ headers.Authorization = `bearer ${accessToken}`;
646
+ }
647
+ } catch {
648
+ }
649
+ const controller = new AbortController();
650
+ const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
651
+ try {
652
+ const resp = await fetch(url.toString(), {
653
+ signal: controller.signal,
654
+ headers
655
+ });
656
+ if (!resp.ok) return null;
657
+ const body = await resp.json();
658
+ const data = body.data ?? body;
659
+ const versions = data.versions;
660
+ if (!versions) return null;
661
+ const clusterVersion = versions.clusterVersion ?? versions.cluster_version ?? null;
662
+ const aeCliVersion = versions.aeCliVersion ?? versions.ae_cli_version ?? null;
663
+ return {
664
+ clusterVersion: clusterVersion?.trim() || null,
665
+ aeCliVersion: aeCliVersion?.trim() || null
666
+ };
667
+ } catch {
668
+ return null;
669
+ } finally {
670
+ clearTimeout(timer);
671
+ }
672
+ }
673
+ function printNotice(text) {
674
+ process.stderr.write(`${text}
675
+ `);
676
+ }
677
+ var pendingHostCompatNotice;
678
+ var pendingUpdateNotice;
679
+ function getPendingHostCompatNotice() {
680
+ return pendingHostCompatNotice;
681
+ }
682
+ function getPendingUpdateNotice() {
683
+ return pendingUpdateNotice;
684
+ }
685
+ function getCachedCompatForHost(host) {
686
+ return readStore().hosts[hostKey(host)];
687
+ }
688
+ function formatCapabilityMissCompatHint(host) {
689
+ if (shouldSkipCompatCheck()) return void 0;
690
+ const active = host || getActiveHost();
691
+ if (!active) {
692
+ return "This capability may be missing on the current private cluster. Update cluster components to the latest version, or pin this machine to the environment ae-cli version.";
693
+ }
694
+ const cached = getCachedCompatForHost(active);
695
+ if (cached?.expectedVersion) {
696
+ const pins = formatPinCommands(cached.expectedVersion);
697
+ if (pins.length > 0) {
698
+ return [
699
+ `Host may be older than this CLI (environment ae-cli ${cached.expectedVersion}, cluster ${cached.clusterVersion || "?"}).`,
700
+ `Update cluster components to the latest version, or pin locally:`,
701
+ ...pins.map((c) => ` ${c}`)
702
+ ].join("\n");
703
+ }
704
+ return [
705
+ `Host may be older than this CLI (environment ae-cli ${cached.expectedVersion}, cluster ${cached.clusterVersion || "?"}).`,
706
+ `No public GitHub/npm release matches that environment version; update cluster components to the latest version instead of pinning.`
707
+ ].join("\n");
708
+ }
709
+ return "This capability may be missing on the current private cluster. Update cluster components to the latest version, or pin this machine CLI + skills to the environment version (see ae-cli host compat notices after auth login).";
710
+ }
711
+ function setUpdateNotice(pkg, expected, cluster, verdict, state, stage, message) {
712
+ pendingHostCompatNotice = void 0;
713
+ pendingUpdateNotice = {
714
+ command: "ae-cli update",
715
+ current: pkg.version,
716
+ expected,
717
+ cluster,
718
+ reason: verdict.kind === "ok" && state === "skills_pending" ? "skills_pending" : verdict.kind,
719
+ state,
720
+ ...stage ? { stage } : {},
721
+ message: message ?? `Current ae-cli is ${pkg.version}; this host requires ${expected}. Run: ae-cli update`
722
+ };
723
+ }
724
+ async function refreshAndMaybeNotify(host, pkg, allowAutoSync) {
725
+ const token = peekCliToken(host);
726
+ if (!token) return { status: "continue" };
727
+ const store = readStore();
728
+ const key = hostKey(host);
729
+ const existing = store.hosts[key];
730
+ let expected = existing?.expectedVersion;
731
+ let cluster = existing?.clusterVersion;
732
+ let fetchedAt = existing?.lastFetchedAt;
733
+ if (isStale(existing)) {
734
+ const remote = await fetchCliConfig(host, token);
735
+ if (!remote?.aeCliVersion) {
736
+ return { status: "continue" };
737
+ }
738
+ expected = remote.aeCliVersion;
739
+ cluster = remote.clusterVersion ?? "";
740
+ fetchedAt = (/* @__PURE__ */ new Date()).toISOString();
741
+ store.hosts[key] = {
742
+ host: key,
743
+ localVersion: pkg.version,
744
+ expectedVersion: expected,
745
+ clusterVersion: cluster,
746
+ lastFetchedAt: fetchedAt,
747
+ lastNotifiedAt: existing?.lastNotifiedAt
748
+ };
749
+ writeStore(store);
750
+ }
751
+ if (!expected) return { status: "continue" };
752
+ let verdict = evaluateCompat(pkg.version, expected, cluster);
753
+ let pendingSkills = getPendingSkillsTarget(host) === expected;
754
+ if (verdict.kind === "ok" && !pendingSkills) {
755
+ pendingHostCompatNotice = void 0;
756
+ pendingUpdateNotice = void 0;
757
+ return { status: "continue" };
758
+ }
759
+ let entry = store.hosts[key] ?? {
760
+ host: key,
761
+ localVersion: pkg.version,
762
+ expectedVersion: expected,
763
+ clusterVersion: cluster || "",
764
+ lastFetchedAt: fetchedAt || (/* @__PURE__ */ new Date()).toISOString()
765
+ };
766
+ entry.localVersion = pkg.version;
767
+ entry.expectedVersion = expected;
768
+ entry.clusterVersion = cluster || entry.clusterVersion;
769
+ const canAutoSync = allowAutoSync && isPublicAeCliPackage(pkg.name) && isAutoSyncTargetEligible(expected);
770
+ if (canAutoSync) {
771
+ const confirmed = await fetchCliConfig(host, token);
772
+ if (confirmed?.aeCliVersion) {
773
+ expected = confirmed.aeCliVersion;
774
+ cluster = confirmed.clusterVersion ?? cluster ?? "";
775
+ verdict = evaluateCompat(pkg.version, expected, cluster);
776
+ pendingSkills = getPendingSkillsTarget(host) === expected;
777
+ entry = {
778
+ ...entry,
779
+ localVersion: pkg.version,
780
+ expectedVersion: expected,
781
+ clusterVersion: cluster || "",
782
+ lastFetchedAt: (/* @__PURE__ */ new Date()).toISOString()
783
+ };
784
+ store.hosts[key] = entry;
785
+ writeStore(store);
786
+ if (verdict.kind === "ok" && !pendingSkills) {
787
+ pendingHostCompatNotice = void 0;
788
+ pendingUpdateNotice = void 0;
789
+ return { status: "continue" };
790
+ }
791
+ if (isAutoSyncTargetEligible(expected)) {
792
+ const attempt = reserveAutoAttempt(host, expected);
793
+ if (attempt.allowed) {
794
+ const direction = verdict.kind === "ok" && pendingSkills ? "skills" : autoSyncDirection(pkg.version, expected);
795
+ const verb = direction === "upgrade" ? "upgrading" : direction === "downgrade" ? "downgrading" : direction === "skills" ? "repairing" : "switching";
796
+ printNotice(
797
+ `[ae-cli] Current version ${pkg.version}; this host requires ${expected}. Automatically ${verb} CLI and Skills, please wait...`
798
+ );
799
+ const result = installVersion(expected, {
800
+ skipCliInstall: verdict.kind === "ok" && pendingSkills,
801
+ progress: printNotice
802
+ });
803
+ recordVersionSyncResult(host, expected, result);
804
+ if (result.ok) {
805
+ printNotice(
806
+ `[ae-cli] Synchronized to ${expected}. Re-run the previous command to use the new version.`
807
+ );
808
+ pendingHostCompatNotice = void 0;
809
+ pendingUpdateNotice = void 0;
810
+ return {
811
+ status: "synced",
812
+ current: pkg.version,
813
+ expected,
814
+ cluster: cluster || "",
815
+ direction
816
+ };
817
+ }
818
+ const state = result.skillsPending ? "skills_pending" : "auto_sync_failed";
819
+ const friendlyCause = friendlyVersionSyncFailure(result);
820
+ const failure = result.skillsPending ? `CLI switched to ${expected}, but Skills synchronization failed. ${friendlyCause} This command will continue; run ae-cli update after access is restored.` : `${friendlyCause} This command will continue; run ae-cli update after fixing access.`;
821
+ printNotice(`[ae-cli] ${failure}`);
822
+ setUpdateNotice(pkg, expected, cluster || "", verdict, state, result.stage, failure);
823
+ entry.lastNotifiedAt = (/* @__PURE__ */ new Date()).toISOString();
824
+ store.hosts[key] = entry;
825
+ writeStore(store);
826
+ return { status: "continue" };
827
+ }
828
+ }
829
+ }
830
+ }
831
+ const notice = verdict.kind === "ok" && pendingSkills ? `[ae-cli] CLI ${expected} is installed, but its Skills are not synchronized.
832
+ Run: ae-cli update` : formatCompatNotice(verdict);
833
+ if (!notice) return { status: "continue" };
834
+ const dueForTip = shouldNotify(entry);
835
+ if (!dueForTip) {
836
+ pendingHostCompatNotice = void 0;
837
+ pendingUpdateNotice = void 0;
838
+ store.hosts[key] = entry;
839
+ writeStore(store);
840
+ return { status: "continue" };
841
+ }
842
+ setUpdateNotice(
843
+ pkg,
844
+ expected,
845
+ cluster || "",
846
+ verdict,
847
+ pendingSkills ? "skills_pending" : "prompt_only",
848
+ void 0,
849
+ pendingSkills ? `Skills for ae-cli ${expected} are not synchronized. Run: ae-cli update` : void 0
850
+ );
851
+ printNotice(notice);
852
+ entry.lastNotifiedAt = (/* @__PURE__ */ new Date()).toISOString();
853
+ store.hosts[key] = entry;
854
+ writeStore(store);
855
+ return { status: "continue" };
856
+ }
857
+ async function runHostCompatCheck(pkg, hostOverride, argv = process.argv) {
858
+ pendingHostCompatNotice = void 0;
859
+ pendingUpdateNotice = void 0;
860
+ if (shouldSkipCompatCheck(argv)) return { status: "continue" };
861
+ const host = hostOverride || getActiveHost();
862
+ if (!host) return { status: "continue" };
863
+ try {
864
+ return await refreshAndMaybeNotify(host, pkg, !shouldSkipAutoSync(argv));
865
+ } catch {
866
+ return { status: "continue" };
867
+ }
868
+ }
869
+
870
+ // src/framework/output.ts
871
+ import Table from "cli-table3";
872
+ import { JqError, json as jqJson } from "jq-wasm";
873
+ var KNOWN_ARRAY_FIELDS = [
874
+ "items",
875
+ "events",
876
+ "reports",
877
+ "dashboards",
878
+ "tags",
879
+ "clusters",
880
+ "flows",
881
+ "tasks",
882
+ "channels",
883
+ "nodes",
884
+ "members",
885
+ "records",
886
+ "entities",
887
+ "metrics",
888
+ "tables",
889
+ "columns",
890
+ "properties"
891
+ ];
892
+ var OUTPUT_METADATA = /* @__PURE__ */ Symbol("ae-cli-output-metadata");
893
+ function withOutputMetadata(data, meta) {
894
+ if (!meta || Object.keys(meta).length === 0) return data;
895
+ return {
896
+ [OUTPUT_METADATA]: true,
897
+ data,
898
+ meta
899
+ };
900
+ }
901
+ function isOutputWithMetadata(value) {
902
+ return Boolean(value?.[OUTPUT_METADATA]);
903
+ }
904
+ function findArrayField(data) {
905
+ if (Array.isArray(data)) return data;
906
+ if (typeof data !== "object" || data === null) return null;
907
+ for (const key of KNOWN_ARRAY_FIELDS) {
908
+ if (Array.isArray(data[key])) return data[key];
909
+ }
910
+ for (const key of Object.keys(data)) {
911
+ if (Array.isArray(data[key])) return data[key];
912
+ }
913
+ return null;
914
+ }
915
+ function formatTable(data) {
916
+ const rows = findArrayField(data);
917
+ if (!rows || rows.length === 0) {
918
+ return JSON.stringify(data, null, 2);
919
+ }
920
+ const allKeys = /* @__PURE__ */ new Set();
921
+ for (const row of rows) {
922
+ if (typeof row === "object" && row !== null) {
923
+ Object.keys(row).forEach((k) => allKeys.add(k));
924
+ }
925
+ }
926
+ const keys = Array.from(allKeys);
927
+ if (keys.length === 0) return JSON.stringify(rows, null, 2);
928
+ const table = new Table({ head: keys, wordWrap: true });
929
+ for (const row of rows) {
930
+ table.push(keys.map((k) => {
931
+ const v = row?.[k];
932
+ if (v === void 0 || v === null) return "";
933
+ if (typeof v === "object") return JSON.stringify(v);
934
+ return String(v);
935
+ }));
936
+ }
937
+ return table.toString();
938
+ }
939
+ async function applyJq(data, expr) {
940
+ const trimmed = expr.trim();
941
+ if (!trimmed) return data;
942
+ let results;
943
+ try {
944
+ results = await jqJson(data, trimmed);
945
+ } catch (err) {
946
+ const message = err instanceof JqError ? err.message : err?.message || String(err);
947
+ const error = new Error(`Invalid --jq expression: ${message}`);
948
+ error.type = "validation";
949
+ error.hint = "Use standard jq syntax, e.g. .status or {status,pendingQuestion}";
950
+ throw error;
951
+ }
952
+ if (results.length === 0) return null;
953
+ if (results.length === 1) return results[0];
954
+ return results;
955
+ }
956
+ async function formatOutput(data, format, jqExpr) {
957
+ const outputData = isOutputWithMetadata(data) ? data.data : data;
958
+ const meta = isOutputWithMetadata(data) ? data.meta : void 0;
959
+ let processed = outputData;
960
+ if (jqExpr) {
961
+ processed = await applyJq(outputData, jqExpr);
962
+ }
963
+ if (format === "table") {
964
+ return formatTable(processed);
965
+ }
966
+ const hostCompat = getPendingHostCompatNotice();
967
+ const updateNotice = getPendingUpdateNotice();
968
+ const notices = {
969
+ ...updateNotice ? {} : hostCompat ? { host_compat: hostCompat } : {},
970
+ ...updateNotice ? { update: updateNotice } : {}
971
+ };
972
+ const envelope = {
973
+ ok: true,
974
+ data: processed,
975
+ ...meta ? { meta } : {},
976
+ // Soft tip lives only under _notice (not mirrored into meta) to avoid duplicate fields.
977
+ ...Object.keys(notices).length > 0 ? { _notice: notices } : {}
978
+ };
979
+ return JSON.stringify(envelope, null, 2);
980
+ }
981
+ function formatError(type, message, hint, code, meta) {
982
+ const envelope = {
983
+ ok: false,
984
+ error: { type, message, hint, code },
985
+ ...meta && Object.keys(meta).length > 0 ? { meta } : {}
986
+ };
987
+ return JSON.stringify(envelope, null, 2);
988
+ }
989
+ function printError(type, message, hint, code, meta, options = {}) {
990
+ const formatted = formatError(type, message, hint, code, meta);
991
+ process.stderr.write(formatted + "\n");
992
+ if (options.log !== false) {
993
+ logger.error(`[${type}] ${message}${hint ? " | " + hint : ""}`);
994
+ }
995
+ }
996
+ async function printOutput(data, format, jqExpr) {
997
+ process.stdout.write(await formatOutput(data, format, jqExpr) + "\n");
998
+ }
999
+
1000
+ export {
1001
+ recordVersionSyncResult,
1002
+ buildVersionInstallPlan,
1003
+ friendlyVersionSyncFailure,
1004
+ installVersion,
1005
+ fetchCliConfig,
1006
+ getCachedCompatForHost,
1007
+ formatCapabilityMissCompatHint,
1008
+ runHostCompatCheck,
1009
+ withOutputMetadata,
1010
+ printError,
1011
+ printOutput
1012
+ };