@thinkingai/ae-cli 6.0.52 → 6.0.54
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-MBWRZTB6.js} +171 -13
- package/dist/automation-NWWVLWG2.js +459 -0
- package/dist/{capability-RJZ4KRGP.js → capability-7SSN6PNZ.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-WEPZ5M3Z.js → chunk-HBLK3ZHW.js} +7 -4
- package/dist/chunk-HVHR4SCP.js +133 -0
- 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-L2QUIZTS.js → chunk-ZIQWCZTS.js} +3 -3
- package/dist/{community-report-client-CU22W53K.js → community-report-client-PEERQCX2.js} +1 -1
- package/dist/{config-XO3LI3SA.js → config-FF6YVJKI.js} +29 -10
- package/dist/context-YAE3QXP3.js +136 -0
- package/dist/{data-integration-HNGUKFIC.js → data-integration-HESZET7E.js} +6 -6
- package/dist/index.js +169 -55
- 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-JAQW7KGZ.js → metadata-7CUMWIB4.js} +7 -7
- package/dist/{model-RNPQXIBI.js → model-SBV2JFJI.js} +9 -5
- package/dist/{personal-semantic-preference-QCZ2IZTK.js → personal-semantic-preference-6SRCYPGE.js} +119 -11
- package/dist/{project-semantic-64JURXKF.js → project-semantic-YYEM4OSU.js} +7 -7
- 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-KQKCAI5H.js → te-analysis-U4I2W2WC.js} +1090 -67
- 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-HSVSRW22.js → te-engage-ZX76CNAB.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 +76 -15
- 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/tag.md +2 -0
- package/skills/ae-analysis/references/ai_models.md +96 -0
- package/skills/ae-analysis/references/analysis_data_retrieval.md +4 -0
- 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 +26 -17
- 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/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 +71 -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 +6 -4
- package/skills/ae-analysis/references/personal_semantic_preference_list.md +5 -3
- 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 +17 -3
- 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 +20 -3
- 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/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
|
@@ -6,11 +6,11 @@ import {
|
|
|
6
6
|
normalizeUrl,
|
|
7
7
|
safeJsonParse,
|
|
8
8
|
safeReadJsonFile
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-UQ77JXYH.js";
|
|
10
10
|
import {
|
|
11
11
|
PermissionError,
|
|
12
12
|
logger
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-WNGFSIOB.js";
|
|
14
14
|
|
|
15
15
|
// src/core/secure-store.ts
|
|
16
16
|
import crypto from "crypto";
|
|
@@ -28,7 +28,7 @@ var NONCE_LEN = 12;
|
|
|
28
28
|
function getMachineId() {
|
|
29
29
|
if (process.platform === "darwin") {
|
|
30
30
|
try {
|
|
31
|
-
const out = execFileSync("ioreg", ["-rd1", "-c", "IOPlatformExpertDevice"], {
|
|
31
|
+
const out = execFileSync("/usr/sbin/ioreg", ["-rd1", "-c", "IOPlatformExpertDevice"], {
|
|
32
32
|
encoding: "utf8",
|
|
33
33
|
timeout: 3e3
|
|
34
34
|
});
|
|
@@ -47,36 +47,42 @@ function getMachineId() {
|
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
if (process.platform === "win32") {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
50
|
+
const systemRoot = process.env.SystemRoot || process.env.WINDIR;
|
|
51
|
+
const commands = systemRoot && path.win32.isAbsolute(systemRoot) ? [path.win32.join(systemRoot, "System32", "reg.exe"), "reg"] : ["reg"];
|
|
52
|
+
for (const command of commands) {
|
|
53
|
+
try {
|
|
54
|
+
const out = execFileSync(
|
|
55
|
+
command,
|
|
56
|
+
["query", "HKLM\\SOFTWARE\\Microsoft\\Cryptography", "/v", "MachineGuid"],
|
|
57
|
+
{ encoding: "utf8", timeout: 3e3 }
|
|
58
|
+
);
|
|
59
|
+
const match = out.match(/MachineGuid\s+REG_SZ\s+([0-9a-f-]+)/i);
|
|
60
|
+
if (match?.[1]) return `win32:${match[1]}`;
|
|
61
|
+
} catch {
|
|
62
|
+
}
|
|
59
63
|
}
|
|
60
64
|
}
|
|
61
65
|
logger.warn("secure-store: using fallback machine-id (HOME+hostname+platform)");
|
|
66
|
+
return getFallbackMachineId();
|
|
67
|
+
}
|
|
68
|
+
function getFallbackMachineId() {
|
|
62
69
|
return `fallback:${os.homedir()}|${os.hostname()}|${os.platform()}`;
|
|
63
70
|
}
|
|
64
|
-
var
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
cachedKey = crypto.scryptSync(cachedMachineId, SCRYPT_SALT, KEY_LEN, {
|
|
71
|
+
var cachedKeys;
|
|
72
|
+
function getKeys() {
|
|
73
|
+
if (!cachedKeys) {
|
|
74
|
+
const machineIds = [.../* @__PURE__ */ new Set([getMachineId(), getFallbackMachineId()])];
|
|
75
|
+
cachedKeys = machineIds.map((machineId) => crypto.scryptSync(machineId, SCRYPT_SALT, KEY_LEN, {
|
|
70
76
|
N: SCRYPT_N,
|
|
71
77
|
r: SCRYPT_R,
|
|
72
78
|
p: SCRYPT_P
|
|
73
|
-
});
|
|
79
|
+
}));
|
|
74
80
|
}
|
|
75
|
-
return
|
|
81
|
+
return cachedKeys;
|
|
76
82
|
}
|
|
77
|
-
function encrypt(plaintext) {
|
|
83
|
+
function encrypt(plaintext, key) {
|
|
78
84
|
const nonce = crypto.randomBytes(NONCE_LEN);
|
|
79
|
-
const cipher = crypto.createCipheriv("aes-256-gcm",
|
|
85
|
+
const cipher = crypto.createCipheriv("aes-256-gcm", key, nonce);
|
|
80
86
|
const data = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
|
|
81
87
|
return {
|
|
82
88
|
nonce: nonce.toString("hex"),
|
|
@@ -84,8 +90,8 @@ function encrypt(plaintext) {
|
|
|
84
90
|
data: data.toString("hex")
|
|
85
91
|
};
|
|
86
92
|
}
|
|
87
|
-
function decrypt(blob) {
|
|
88
|
-
const decipher = crypto.createDecipheriv("aes-256-gcm",
|
|
93
|
+
function decrypt(blob, key) {
|
|
94
|
+
const decipher = crypto.createDecipheriv("aes-256-gcm", key, Buffer.from(blob.nonce, "hex"));
|
|
89
95
|
decipher.setAuthTag(Buffer.from(blob.tag, "hex"));
|
|
90
96
|
return Buffer.concat([
|
|
91
97
|
decipher.update(Buffer.from(blob.data, "hex")),
|
|
@@ -112,41 +118,81 @@ function ensureStoreDir() {
|
|
|
112
118
|
} catch {
|
|
113
119
|
}
|
|
114
120
|
}
|
|
115
|
-
function
|
|
121
|
+
function stageFile(filePath, contents) {
|
|
116
122
|
ensureStoreDir();
|
|
117
123
|
const tempPath = `${filePath}.${process.pid}.${crypto.randomBytes(6).toString("hex")}.tmp`;
|
|
118
|
-
const blob = encrypt(JSON.stringify(value));
|
|
119
124
|
try {
|
|
120
|
-
fs.writeFileSync(tempPath,
|
|
125
|
+
fs.writeFileSync(tempPath, contents, { encoding: "utf8", mode: 384, flag: "wx" });
|
|
121
126
|
try {
|
|
122
127
|
fs.chmodSync(tempPath, 384);
|
|
123
128
|
} catch {
|
|
124
129
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
+
return tempPath;
|
|
131
|
+
} catch (error) {
|
|
132
|
+
removeFile(tempPath);
|
|
133
|
+
throw error;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function prepareEncrypted(filePath, value, companionPath) {
|
|
137
|
+
const existing = readEncryptedFile(filePath);
|
|
138
|
+
const companion = companionPath ? readEncryptedFile(companionPath) : null;
|
|
139
|
+
const key = existing?.key ?? companion?.key ?? getKeys()[0];
|
|
140
|
+
const blob = encrypt(JSON.stringify(value), key);
|
|
141
|
+
return { tempPath: stageFile(filePath, JSON.stringify(blob, null, 2)), previous: existing?.raw ?? null };
|
|
142
|
+
}
|
|
143
|
+
function replaceFile(tempPath, filePath) {
|
|
144
|
+
fs.renameSync(tempPath, filePath);
|
|
145
|
+
try {
|
|
146
|
+
fs.chmodSync(filePath, 384);
|
|
147
|
+
} catch {
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
function atomicWriteEncrypted(filePath, value, companionPath) {
|
|
151
|
+
const { tempPath } = prepareEncrypted(filePath, value, companionPath);
|
|
152
|
+
try {
|
|
153
|
+
replaceFile(tempPath, filePath);
|
|
130
154
|
} finally {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
155
|
+
removeFile(tempPath);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function readEncryptedFile(filePath) {
|
|
159
|
+
let raw;
|
|
160
|
+
try {
|
|
161
|
+
raw = fs.readFileSync(filePath, "utf8");
|
|
162
|
+
} catch (error) {
|
|
163
|
+
if (error.code === "ENOENT") {
|
|
164
|
+
try {
|
|
165
|
+
if (!fs.lstatSync(filePath, { throwIfNoEntry: false })) return null;
|
|
166
|
+
} catch {
|
|
167
|
+
}
|
|
134
168
|
}
|
|
169
|
+
throw new CredentialStoreUnreadableError(filePath);
|
|
135
170
|
}
|
|
171
|
+
return decodeEncryptedFile(filePath, raw);
|
|
136
172
|
}
|
|
137
|
-
function
|
|
138
|
-
if (!fs.existsSync(filePath)) return null;
|
|
173
|
+
function decodeEncryptedFile(filePath, raw) {
|
|
139
174
|
try {
|
|
140
|
-
const blob = safeJsonParse(
|
|
141
|
-
if (
|
|
142
|
-
|
|
143
|
-
return null;
|
|
175
|
+
const blob = safeJsonParse(raw);
|
|
176
|
+
if (typeof blob?.nonce !== "string" || !/^[0-9a-f]{24}$/i.test(blob.nonce) || typeof blob?.tag !== "string" || !/^[0-9a-f]{32}$/i.test(blob.tag) || typeof blob?.data !== "string" || !/^(?:[0-9a-f]{2})+$/i.test(blob.data)) {
|
|
177
|
+
throw new Error("Malformed encrypted credential");
|
|
144
178
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
179
|
+
for (const key of getKeys()) {
|
|
180
|
+
try {
|
|
181
|
+
return { value: safeJsonParse(decrypt(blob, key)), key, raw };
|
|
182
|
+
} catch {
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
} catch {
|
|
186
|
+
}
|
|
187
|
+
throw new CredentialStoreUnreadableError(filePath);
|
|
188
|
+
}
|
|
189
|
+
function readEncrypted(filePath) {
|
|
190
|
+
const result = readEncryptedFile(filePath);
|
|
191
|
+
if (!result) return null;
|
|
192
|
+
if (!result.value || typeof result.value !== "object" || Array.isArray(result.value)) {
|
|
193
|
+
throw new CredentialStoreUnreadableError(filePath);
|
|
149
194
|
}
|
|
195
|
+
return result.value;
|
|
150
196
|
}
|
|
151
197
|
function tokenFingerprint(token) {
|
|
152
198
|
return crypto.createHash("sha256").update(token).digest("hex").slice(0, 24);
|
|
@@ -162,23 +208,139 @@ function legacyProjectionPayload(cliToken) {
|
|
|
162
208
|
cliToken
|
|
163
209
|
};
|
|
164
210
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
return {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
211
|
+
var activeWrites = /* @__PURE__ */ new Set();
|
|
212
|
+
function recoveryFilePath(host) {
|
|
213
|
+
return `${vaultFilePath(host)}.rollback`;
|
|
214
|
+
}
|
|
215
|
+
function restoreFile(filePath, previous) {
|
|
216
|
+
let current;
|
|
217
|
+
try {
|
|
218
|
+
current = fs.readFileSync(filePath, "utf8");
|
|
219
|
+
} catch (error) {
|
|
220
|
+
if (error.code !== "ENOENT") throw error;
|
|
221
|
+
current = null;
|
|
222
|
+
}
|
|
223
|
+
if (current === previous) return;
|
|
224
|
+
if (previous === null) {
|
|
225
|
+
fs.rmSync(filePath, { force: true });
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
const tempPath = stageFile(filePath, previous);
|
|
229
|
+
try {
|
|
230
|
+
replaceFile(tempPath, filePath);
|
|
231
|
+
} finally {
|
|
232
|
+
removeFile(tempPath);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
function restoreCredentialWrite(host, recovery) {
|
|
236
|
+
restoreFile(legacyFilePath(host), recovery.legacy);
|
|
237
|
+
restoreFile(vaultFilePath(host), recovery.vault);
|
|
238
|
+
for (const name of [recovery.legacyTemp, recovery.vaultTemp]) {
|
|
239
|
+
fs.rmSync(path.join(storeDir(), name), { force: true });
|
|
240
|
+
}
|
|
241
|
+
fs.unlinkSync(recoveryFilePath(host));
|
|
242
|
+
}
|
|
243
|
+
function isRecoveryTemp(name, destination, pid) {
|
|
244
|
+
const prefix = `${path.basename(destination)}.${pid}.`;
|
|
245
|
+
return typeof name === "string" && name.startsWith(prefix) && /^[0-9a-f]{12}\.tmp$/.test(name.slice(prefix.length));
|
|
246
|
+
}
|
|
247
|
+
function recoverCredentialWrite(host) {
|
|
248
|
+
const filePath = recoveryFilePath(host);
|
|
249
|
+
let raw;
|
|
250
|
+
try {
|
|
251
|
+
raw = fs.readFileSync(filePath, "utf8");
|
|
252
|
+
} catch (error) {
|
|
253
|
+
if (error.code === "ENOENT") {
|
|
254
|
+
try {
|
|
255
|
+
if (!fs.lstatSync(filePath, { throwIfNoEntry: false })) return;
|
|
256
|
+
} catch {
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
throw new CredentialStoreUnreadableError(filePath);
|
|
260
|
+
}
|
|
261
|
+
try {
|
|
262
|
+
const recovery = safeJsonParse(raw);
|
|
263
|
+
if (recovery?.version !== 1 || !Number.isSafeInteger(recovery.pid) || recovery.pid <= 0 || !(recovery.legacy === null || typeof recovery.legacy === "string") || !(recovery.vault === null || typeof recovery.vault === "string") || !isRecoveryTemp(recovery.legacyTemp, legacyFilePath(host), recovery.pid) || !isRecoveryTemp(recovery.vaultTemp, vaultFilePath(host), recovery.pid) || activeWrites.has(filePath)) throw new Error("Invalid or active credential recovery");
|
|
264
|
+
if (recovery.pid !== process.pid) {
|
|
265
|
+
let ownerExited = false;
|
|
266
|
+
try {
|
|
267
|
+
process.kill(recovery.pid, 0);
|
|
268
|
+
} catch (error) {
|
|
269
|
+
ownerExited = error.code === "ESRCH";
|
|
270
|
+
}
|
|
271
|
+
if (!ownerExited) throw new Error("Credential writer is still running");
|
|
272
|
+
}
|
|
273
|
+
if (recovery.legacy !== null) decodeEncryptedFile(legacyFilePath(host), recovery.legacy);
|
|
274
|
+
if (recovery.vault !== null) decodeEncryptedFile(vaultFilePath(host), recovery.vault);
|
|
275
|
+
restoreCredentialWrite(host, recovery);
|
|
276
|
+
} catch {
|
|
277
|
+
throw new CredentialStoreUnreadableError(filePath);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
function writeVaultAndProjection(host, vault) {
|
|
281
|
+
const active = vault.credentials.find((entry) => entry.id === vault.activeCredentialId);
|
|
282
|
+
vault.legacyProjection = { credentialId: active.id, tokenFingerprint: tokenFingerprint(active.cliToken) };
|
|
283
|
+
const legacyPath = legacyFilePath(host);
|
|
284
|
+
const vaultPath = vaultFilePath(host);
|
|
285
|
+
const recoveryPath = recoveryFilePath(host);
|
|
286
|
+
const staged = [];
|
|
287
|
+
try {
|
|
288
|
+
const legacy = prepareEncrypted(legacyPath, legacyProjectionPayload(active.cliToken), vaultPath);
|
|
289
|
+
staged.push(legacy.tempPath);
|
|
290
|
+
const nextVault = prepareEncrypted(vaultPath, vault, legacyPath);
|
|
291
|
+
staged.push(nextVault.tempPath);
|
|
292
|
+
const recovery = {
|
|
293
|
+
version: 1,
|
|
294
|
+
pid: process.pid,
|
|
295
|
+
legacy: legacy.previous,
|
|
296
|
+
vault: nextVault.previous,
|
|
297
|
+
legacyTemp: path.basename(legacy.tempPath),
|
|
298
|
+
vaultTemp: path.basename(nextVault.tempPath)
|
|
299
|
+
};
|
|
300
|
+
const fd = fs.openSync(recoveryPath, "wx", 384);
|
|
301
|
+
try {
|
|
302
|
+
try {
|
|
303
|
+
fs.writeFileSync(fd, JSON.stringify(recovery), "utf8");
|
|
304
|
+
fs.fsyncSync(fd);
|
|
305
|
+
} finally {
|
|
306
|
+
fs.closeSync(fd);
|
|
307
|
+
}
|
|
308
|
+
} catch (error) {
|
|
309
|
+
removeFile(recoveryPath);
|
|
310
|
+
throw error;
|
|
311
|
+
}
|
|
312
|
+
activeWrites.add(recoveryPath);
|
|
313
|
+
try {
|
|
314
|
+
replaceFile(legacy.tempPath, legacyPath);
|
|
315
|
+
replaceFile(nextVault.tempPath, vaultPath);
|
|
316
|
+
fs.unlinkSync(recoveryPath);
|
|
317
|
+
} catch (error) {
|
|
318
|
+
try {
|
|
319
|
+
restoreCredentialWrite(host, recovery);
|
|
320
|
+
} catch {
|
|
321
|
+
throw new CredentialStoreUnreadableError(recoveryPath);
|
|
322
|
+
}
|
|
323
|
+
throw error;
|
|
324
|
+
} finally {
|
|
325
|
+
activeWrites.delete(recoveryPath);
|
|
326
|
+
}
|
|
327
|
+
} finally {
|
|
328
|
+
for (const tempPath of staged) removeFile(tempPath);
|
|
329
|
+
}
|
|
171
330
|
}
|
|
172
331
|
function readVaultRaw(host) {
|
|
173
332
|
const normalizedHost = normalizeUrl(host);
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
const
|
|
177
|
-
if (
|
|
178
|
-
|
|
333
|
+
recoverCredentialWrite(normalizedHost);
|
|
334
|
+
const filePath = vaultFilePath(normalizedHost);
|
|
335
|
+
const vault = readEncrypted(filePath);
|
|
336
|
+
if (!vault) return null;
|
|
337
|
+
if (vault.version !== 1 || !Array.isArray(vault.credentials) || vault.credentials.length === 0 || !vault.credentials.every((entry) => typeof entry?.id === "string" && entry.id && typeof entry.cliToken === "string" && entry.cliToken.trim()) || !vault.credentials.some((entry) => entry.id === vault.activeCredentialId) || vault.legacyProjection !== void 0 && (!vault.legacyProjection || typeof vault.legacyProjection.credentialId !== "string" || typeof vault.legacyProjection.tokenFingerprint !== "string")) {
|
|
338
|
+
throw new CredentialStoreUnreadableError(filePath);
|
|
339
|
+
}
|
|
340
|
+
return { ...vault, host: normalizedHost };
|
|
179
341
|
}
|
|
180
342
|
function writeVault(host, vault) {
|
|
181
|
-
atomicWriteEncrypted(vaultFilePath(host), vault);
|
|
343
|
+
atomicWriteEncrypted(vaultFilePath(host), vault, legacyFilePath(host));
|
|
182
344
|
}
|
|
183
345
|
function removeFile(filePath) {
|
|
184
346
|
try {
|
|
@@ -187,12 +349,23 @@ function removeFile(filePath) {
|
|
|
187
349
|
}
|
|
188
350
|
}
|
|
189
351
|
function load(host) {
|
|
190
|
-
|
|
352
|
+
recoverCredentialWrite(host);
|
|
353
|
+
const filePath = legacyFilePath(host);
|
|
354
|
+
const payload = readEncrypted(filePath);
|
|
355
|
+
if (payload && (typeof payload.accessToken !== "string" || typeof payload.refreshToken !== "string" || typeof payload.accessExpiresAt !== "string" || payload.cliToken !== void 0 && typeof payload.cliToken !== "string")) {
|
|
356
|
+
throw new CredentialStoreUnreadableError(filePath);
|
|
357
|
+
}
|
|
358
|
+
return payload;
|
|
359
|
+
}
|
|
360
|
+
function assertCredentialsReadable(host) {
|
|
361
|
+
readVaultRaw(host);
|
|
362
|
+
load(host);
|
|
191
363
|
}
|
|
192
364
|
function loadCredentialVault(host) {
|
|
193
365
|
const normalizedHost = normalizeUrl(host);
|
|
194
366
|
let vault = readVaultRaw(normalizedHost);
|
|
195
|
-
const
|
|
367
|
+
const legacy = load(normalizedHost);
|
|
368
|
+
const legacyToken = legacy?.cliToken?.trim();
|
|
196
369
|
if (!vault) {
|
|
197
370
|
if (!legacyToken) return null;
|
|
198
371
|
const imported = { id: credentialId(legacyToken), cliToken: legacyToken };
|
|
@@ -228,7 +401,7 @@ function loadCredentialVault(host) {
|
|
|
228
401
|
if (changed) writeVault(normalizedHost, vault);
|
|
229
402
|
return vault;
|
|
230
403
|
}
|
|
231
|
-
if (vault.legacyProjection) {
|
|
404
|
+
if (vault.legacyProjection && !legacy) {
|
|
232
405
|
const projection = vault.legacyProjection;
|
|
233
406
|
const projected = vault.credentials.find((entry) => entry.id === projection.credentialId && tokenFingerprint(entry.cliToken) === projection.tokenFingerprint);
|
|
234
407
|
if (projected) {
|
|
@@ -244,9 +417,7 @@ function loadCredentialVault(host) {
|
|
|
244
417
|
vault.activeCredentialId = vault.credentials[0].id;
|
|
245
418
|
}
|
|
246
419
|
}
|
|
247
|
-
|
|
248
|
-
vault.legacyProjection = writeLegacyProjection(normalizedHost, active);
|
|
249
|
-
writeVault(normalizedHost, vault);
|
|
420
|
+
writeVaultAndProjection(normalizedHost, vault);
|
|
250
421
|
return vault;
|
|
251
422
|
}
|
|
252
423
|
function getActiveCredential(host) {
|
|
@@ -261,7 +432,7 @@ function saveCredential(host, credential, options = {}) {
|
|
|
261
432
|
const existingVault = loadCredentialVault(normalizedHost);
|
|
262
433
|
if (options.add && !credential.account) {
|
|
263
434
|
throw new SecureStoreAuthError(
|
|
264
|
-
`This host does not return account identity from CLI token validation. Upgrade the server before
|
|
435
|
+
`This host does not return account identity from CLI token validation. Upgrade the server before adding another account for ${normalizedHost}.`
|
|
265
436
|
);
|
|
266
437
|
}
|
|
267
438
|
const sameAccount = credential.account ? existingVault?.credentials.find((entry) => entry.account?.openId === credential.account.openId || entry.account?.loginName === credential.account.loginName) : void 0;
|
|
@@ -277,13 +448,11 @@ function saveCredential(host, credential, options = {}) {
|
|
|
277
448
|
} else {
|
|
278
449
|
credentials = [saved];
|
|
279
450
|
}
|
|
280
|
-
|
|
281
|
-
writeVault(normalizedHost, {
|
|
451
|
+
writeVaultAndProjection(normalizedHost, {
|
|
282
452
|
version: 1,
|
|
283
453
|
host: normalizedHost,
|
|
284
454
|
activeCredentialId: saved.id,
|
|
285
|
-
credentials
|
|
286
|
-
legacyProjection
|
|
455
|
+
credentials
|
|
287
456
|
});
|
|
288
457
|
logger.info(`secure-store: saved CLI credential for ${normalizedHost}`);
|
|
289
458
|
return saved;
|
|
@@ -322,8 +491,7 @@ function activateCredential(host, account) {
|
|
|
322
491
|
}
|
|
323
492
|
const selected = matches[0];
|
|
324
493
|
vault.activeCredentialId = selected.id;
|
|
325
|
-
|
|
326
|
-
writeVault(normalizedHost, vault);
|
|
494
|
+
writeVaultAndProjection(normalizedHost, vault);
|
|
327
495
|
return selected;
|
|
328
496
|
}
|
|
329
497
|
function removeActiveCredential(host) {
|
|
@@ -339,12 +507,12 @@ function removeActiveCredential(host) {
|
|
|
339
507
|
}
|
|
340
508
|
const active = vault.credentials[0];
|
|
341
509
|
vault.activeCredentialId = active.id;
|
|
342
|
-
|
|
343
|
-
writeVault(normalizedHost, vault);
|
|
510
|
+
writeVaultAndProjection(normalizedHost, vault);
|
|
344
511
|
return { removed, active };
|
|
345
512
|
}
|
|
346
513
|
function clearAllCredentials(host) {
|
|
347
514
|
const normalizedHost = normalizeUrl(host);
|
|
515
|
+
recoverCredentialWrite(normalizedHost);
|
|
348
516
|
removeFile(legacyFilePath(normalizedHost));
|
|
349
517
|
removeFile(vaultFilePath(normalizedHost));
|
|
350
518
|
logger.info(`secure-store: all CLI credentials cleared for ${normalizedHost}`);
|
|
@@ -353,10 +521,20 @@ function loadCliToken(host) {
|
|
|
353
521
|
return getActiveCredential(host)?.cliToken ?? null;
|
|
354
522
|
}
|
|
355
523
|
var SecureStoreAuthError = class extends Error {
|
|
356
|
-
constructor(message) {
|
|
524
|
+
constructor(message, code) {
|
|
357
525
|
super(message);
|
|
526
|
+
this.code = code;
|
|
358
527
|
this.name = "SecureStoreAuthError";
|
|
359
528
|
}
|
|
529
|
+
code;
|
|
530
|
+
};
|
|
531
|
+
var CredentialStoreUnreadableError = class extends Error {
|
|
532
|
+
code = "CREDENTIAL_STORE_UNREADABLE";
|
|
533
|
+
hint = "Keep the existing credential files. Retry in the original OS user and runtime with access to the machine identifier and credential directory. Do not log in again, replace tokens, or log out to fix a local credential read failure.";
|
|
534
|
+
constructor(filePath) {
|
|
535
|
+
super(`Cannot read or decrypt the existing credential file: ${filePath}`);
|
|
536
|
+
this.name = "CredentialStoreUnreadableError";
|
|
537
|
+
}
|
|
360
538
|
};
|
|
361
539
|
|
|
362
540
|
// src/core/cli-token.ts
|
|
@@ -366,6 +544,9 @@ import path2 from "path";
|
|
|
366
544
|
var CLI_TOKEN_GENERATE_PATH = "/v1/ta/cli/token/generate";
|
|
367
545
|
var CLI_TOKEN_RENEW_PATH = "/v1/ta/cli/token/renew";
|
|
368
546
|
var CLI_TOKEN_VALIDATE_PATH = "/v1/ta/cli/token/validate";
|
|
547
|
+
function safeReturnCode(value) {
|
|
548
|
+
return typeof value === "number" && Number.isFinite(value) ? String(value) : void 0;
|
|
549
|
+
}
|
|
369
550
|
var CliTokenValidationUnavailableError = class extends Error {
|
|
370
551
|
constructor(message) {
|
|
371
552
|
super(message);
|
|
@@ -377,6 +558,15 @@ function isExplicitCliTokenRejection(data, responseText) {
|
|
|
377
558
|
const message = String(data?.return_message ?? data?.message ?? data?.error?.message ?? responseText);
|
|
378
559
|
return /cli[_-]?token[_-]?(invalid|expired)/i.test(code) || /cli[-_\s]?token.*\b(invalid|expired)\b/i.test(message) || /\b(invalid|expired)\b.*cli[-_\s]?token/i.test(message);
|
|
379
560
|
}
|
|
561
|
+
function cliAccessDisabledDetails(data, responseText) {
|
|
562
|
+
const code = String(data?.code ?? data?.error?.code ?? "");
|
|
563
|
+
const message = String(data?.return_message ?? data?.message ?? data?.error?.message ?? responseText);
|
|
564
|
+
if (code !== "CLI_ACCESS_DISABLED" && !message.includes("CLI_ACCESS_DISABLED")) return void 0;
|
|
565
|
+
return {
|
|
566
|
+
message: message || "CLI access is disabled for this account.",
|
|
567
|
+
hint: data?.hint ?? data?.error?.hint
|
|
568
|
+
};
|
|
569
|
+
}
|
|
380
570
|
function optionalNonEmptyString(...values) {
|
|
381
571
|
return values.find((value) => typeof value === "string" && value.trim());
|
|
382
572
|
}
|
|
@@ -475,19 +665,26 @@ async function generateCliToken(hostUrl, accessToken) {
|
|
|
475
665
|
body = text ? safeJsonParse(text) : {};
|
|
476
666
|
} catch {
|
|
477
667
|
}
|
|
478
|
-
const
|
|
479
|
-
const code = body.code
|
|
480
|
-
throw new PermissionError(
|
|
668
|
+
const serviceMessage = body.message ?? body.return_message;
|
|
669
|
+
const code = body.code === "CLI_ACCESS_DISABLED" || String(serviceMessage).includes("CLI_ACCESS_DISABLED") ? "CLI_ACCESS_DISABLED" : void 0;
|
|
670
|
+
throw new PermissionError(
|
|
671
|
+
code === "CLI_ACCESS_DISABLED" ? "CLI access is disabled for this account." : "You do not have permission to generate a CLI token.",
|
|
672
|
+
code
|
|
673
|
+
);
|
|
481
674
|
}
|
|
482
675
|
throw new Error(`CLI token generate HTTP error: ${resp.status} ${resp.statusText}`);
|
|
483
676
|
}
|
|
484
677
|
const data = safeJsonParse(await resp.text());
|
|
485
678
|
if (data.return_code !== 0) {
|
|
486
|
-
const
|
|
487
|
-
if (data.code === "CLI_ACCESS_DISABLED" || String(
|
|
488
|
-
throw new PermissionError(
|
|
679
|
+
const serviceMessage = data.return_message || data.message;
|
|
680
|
+
if (data.code === "CLI_ACCESS_DISABLED" || String(serviceMessage).includes("CLI_ACCESS_DISABLED")) {
|
|
681
|
+
throw new PermissionError(
|
|
682
|
+
"CLI access is disabled for this account.",
|
|
683
|
+
"CLI_ACCESS_DISABLED"
|
|
684
|
+
);
|
|
489
685
|
}
|
|
490
|
-
|
|
686
|
+
const code = safeReturnCode(data.return_code);
|
|
687
|
+
throw new Error(`CLI token generation failed${code ? ` (code: ${code})` : ""}`);
|
|
491
688
|
}
|
|
492
689
|
const cliToken = data.data?.userSecret ?? data.data?.user_secret;
|
|
493
690
|
if (!cliToken) throw new Error("CLI token generate error: empty userSecret in response");
|
|
@@ -511,10 +708,12 @@ async function validateCliTokenOnServer(hostUrl, cliToken) {
|
|
|
511
708
|
} catch {
|
|
512
709
|
body = void 0;
|
|
513
710
|
}
|
|
711
|
+
const accessDisabled = cliAccessDisabledDetails(body, responseText);
|
|
712
|
+
if (accessDisabled) {
|
|
713
|
+
throw new PermissionError(accessDisabled.message, "CLI_ACCESS_DISABLED", accessDisabled.hint);
|
|
714
|
+
}
|
|
514
715
|
if (isExplicitCliTokenRejection(body, responseText)) {
|
|
515
|
-
throw new SecureStoreAuthError(
|
|
516
|
-
`CLI token is invalid or expired for ${hostUrl}. Run: ae-cli auth logout --host ${hostUrl}, then ae-cli auth login --host ${hostUrl}`
|
|
517
|
-
);
|
|
716
|
+
throw new SecureStoreAuthError(`CLI token is invalid or expired for ${hostUrl}.`);
|
|
518
717
|
}
|
|
519
718
|
if (!resp.ok) {
|
|
520
719
|
throw new CliTokenValidationUnavailableError(
|
|
@@ -578,7 +777,13 @@ function peekCliToken(hostOverride) {
|
|
|
578
777
|
const hostUrl = hostOverride || getActiveHost();
|
|
579
778
|
if (!hostUrl) return void 0;
|
|
580
779
|
const normalizedHost = normalizeUrl(hostUrl);
|
|
581
|
-
|
|
780
|
+
let stored;
|
|
781
|
+
try {
|
|
782
|
+
stored = loadCliToken(normalizedHost);
|
|
783
|
+
} catch (error) {
|
|
784
|
+
if (error instanceof CredentialStoreUnreadableError) return void 0;
|
|
785
|
+
throw error;
|
|
786
|
+
}
|
|
582
787
|
if (stored) {
|
|
583
788
|
cliTokenCache.set(normalizedHost, stored);
|
|
584
789
|
return stored;
|
|
@@ -606,6 +811,7 @@ async function mintCliToken(hostUrl, accessToken) {
|
|
|
606
811
|
}
|
|
607
812
|
|
|
608
813
|
export {
|
|
814
|
+
assertCredentialsReadable,
|
|
609
815
|
getActiveCredential,
|
|
610
816
|
listCredentials,
|
|
611
817
|
saveCredential,
|
|
@@ -614,6 +820,7 @@ export {
|
|
|
614
820
|
removeActiveCredential,
|
|
615
821
|
clearAllCredentials,
|
|
616
822
|
SecureStoreAuthError,
|
|
823
|
+
CredentialStoreUnreadableError,
|
|
617
824
|
CliTokenValidationUnavailableError,
|
|
618
825
|
clearCliTokenCache,
|
|
619
826
|
validateCliTokenOnServer,
|
|
@@ -6,18 +6,19 @@ import {
|
|
|
6
6
|
CapabilityGatewayError,
|
|
7
7
|
fetchCapabilityGateway,
|
|
8
8
|
requestCapabilityGateway
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-EOYSKMXN.js";
|
|
10
10
|
import {
|
|
11
11
|
readOutputMetadata,
|
|
12
12
|
unwrapOutputData,
|
|
13
13
|
withOutputMetadata
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-KFNMQTZI.js";
|
|
15
15
|
import {
|
|
16
16
|
PermissionError
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-WNGFSIOB.js";
|
|
18
18
|
|
|
19
19
|
// src/core/analysis-async-artifact.ts
|
|
20
20
|
import { randomBytes } from "crypto";
|
|
21
|
+
import { once } from "events";
|
|
21
22
|
import { createWriteStream } from "fs";
|
|
22
23
|
import { access, link, mkdir, rename, stat, unlink } from "fs/promises";
|
|
23
24
|
import { Readable } from "stream";
|
|
@@ -261,9 +262,11 @@ async function downloadAnalysisArtifact(host, runId, artifactId, output, options
|
|
|
261
262
|
if (!response.body) {
|
|
262
263
|
throw protocolError("Artifact download response has no body.", runId);
|
|
263
264
|
}
|
|
265
|
+
const destination = createWriteStream(tempPath, { flags: "wx", mode: options.mode });
|
|
266
|
+
await once(destination, "open");
|
|
264
267
|
tempPresent = true;
|
|
265
268
|
const source = Readable.fromWeb(response.body);
|
|
266
|
-
await pipeline(source,
|
|
269
|
+
await pipeline(source, destination, {
|
|
267
270
|
signal: options.signal
|
|
268
271
|
});
|
|
269
272
|
const file = await stat(tempPath);
|