@kafca/agentdock 0.1.61 → 0.1.63
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 +18 -0
- package/dist/renderer/assets/AutomationList-DR-micIT.js +16 -0
- package/dist/renderer/assets/{Badge-BLI4LHse.js → Badge-COE1Zm2i.js} +1 -1
- package/dist/renderer/assets/{Card-DJeLDdL3.js → Card-F6UNFxnQ.js} +1 -1
- package/dist/renderer/assets/{Config-D1awc81A.js → Config-BKqxfhZi.js} +2 -2
- package/dist/renderer/assets/Dashboard-Bvp7-_2z.js +36 -0
- package/dist/renderer/assets/{EmptyState-CkI1RwPX.js → EmptyState-BwV_J0Og.js} +1 -1
- package/dist/renderer/assets/{HighlightedMarkdown-BUhDlhgW.js → HighlightedMarkdown-CBIBaWyM.js} +1 -1
- package/dist/renderer/assets/{Input-DHYrn-Ne.js → Input-C8LZd4Bi.js} +1 -1
- package/dist/renderer/assets/{KnowledgeDetail-Bij-Xo90.js → KnowledgeDetail-DjXYNMIs.js} +2 -2
- package/dist/renderer/assets/{KnowledgeHome-DmPZ2IlI.js → KnowledgeHome-Cqk5_nJh.js} +2 -2
- package/dist/renderer/assets/{Logs-ISY99heA.js → Logs-B6cXFvW4.js} +2 -2
- package/dist/renderer/assets/{Modal-DAQfz0ot.js → Modal-CAj7soll.js} +3 -3
- package/dist/renderer/assets/{Page-Dw43UYDT.js → Page-DlnMnu4F.js} +1 -1
- package/dist/renderer/assets/{Select-DzmN5OGQ.js → Select-CVay_LUX.js} +1 -1
- package/dist/renderer/assets/{ThreadChat-B5CWqqMK.js → ThreadChat-BxXRpYa1.js} +3 -3
- package/dist/renderer/assets/{Workspace-C5g9LBj9.js → Workspace-YC0pC7cM.js} +1 -1
- package/dist/renderer/assets/{arrow-left-BLXh56Ip.js → arrow-left-Cwm09dxx.js} +1 -1
- package/dist/renderer/assets/{book-open-DhPGbc-7.js → book-open-BwcGC_im.js} +1 -1
- package/dist/renderer/assets/{channels-DfQrmuYO.js → channels-BPytmVCd.js} +2 -2
- package/dist/renderer/assets/{chevron-down-CEFtpPDc.js → chevron-down-2LP9oGHG.js} +1 -1
- package/dist/renderer/assets/en-Bnr4nMzg.js +1 -0
- package/dist/renderer/assets/es-B1GIFh3i.js +1 -0
- package/dist/renderer/assets/{index-BIKL3fTP.css → index-B6WcpkJF.css} +1 -1
- package/dist/renderer/assets/index-eoVERL5K.js +162 -0
- package/dist/renderer/assets/{index-CYhDUAR_.js → index-llCPCiYk.js} +1 -1
- package/dist/renderer/assets/ja-CDWPWqEW.js +1 -0
- package/dist/renderer/assets/{knowledge-uHRQ80sR.js → knowledge-QujAv-Zh.js} +1 -1
- package/dist/renderer/assets/{pencil-BD2Y31YC.js → pencil-0P2INETc.js} +1 -1
- package/dist/renderer/assets/{plus-DDcldp_c.js → plus-tiGRgE2v.js} +1 -1
- package/dist/renderer/assets/{save-CMW_cZI9.js → save-D8U93DNa.js} +1 -1
- package/dist/renderer/assets/{search-BM-0CvJr.js → search-C-9J9J3f.js} +1 -1
- package/dist/renderer/assets/{shield-check-DaMB0FKG.js → shield-check-BCKkPPTJ.js} +1 -1
- package/dist/renderer/assets/{threads-DZgt9tWj.js → threads-Bsrwwnpm.js} +1 -1
- package/dist/renderer/assets/{trash-2-DTXVbC5y.js → trash-2-CP4sn15n.js} +1 -1
- package/dist/renderer/assets/zh-MSddxMDX.js +1 -0
- package/dist/renderer/assets/zh-TW-C_LNWX0o.js +1 -0
- package/dist/renderer/index.html +2 -2
- package/dist-electron/electron/managed-skills/agent-browser/SKILL.md +41 -0
- package/dist-electron/electron/managed-skills/agent-browser/references/authentication.md +83 -0
- package/dist-electron/electron/managed-skills/agent-browser/references/commands.md +50 -0
- package/dist-electron/electron/managed-skills/agent-browser/references/profiling.md +20 -0
- package/dist-electron/electron/managed-skills/agent-browser/references/proxy-support.md +18 -0
- package/dist-electron/electron/managed-skills/agent-browser/references/session-management.md +17 -0
- package/dist-electron/electron/managed-skills/agent-browser/references/snapshot-refs.md +17 -0
- package/dist-electron/electron/managed-skills/agent-browser/references/video-recording.md +21 -0
- package/dist-electron/electron/managed-skills/agent-browser/templates/authenticated-session.sh +11 -0
- package/dist-electron/electron/managed-skills/agent-browser/templates/capture-workflow.sh +12 -0
- package/dist-electron/electron/managed-skills/agent-browser/templates/form-automation.sh +10 -0
- package/dist-electron/electron/managed-skills/condition-trigger/SKILL.md +21 -0
- package/dist-electron/electron/managed-skills/condition-trigger/scripts/register-condition-trigger.sh +106 -0
- package/dist-electron/electron/managed-skills/knowledge-base/SKILL.md +13 -0
- package/dist-electron/electron/managed-skills/knowledge-base/scripts/search-knowledge.sh +80 -0
- package/dist-electron/packages/contracts/src/automations.js +211 -0
- package/dist-electron/packages/contracts/src/index.js +1 -0
- package/dist-electron/packages/core-sdk/src/automations.js +87 -0
- package/dist-electron/packages/core-sdk/src/client.js +101 -0
- package/dist-electron/packages/core-sdk/src/index.js +2 -1
- package/dist-electron/services/local-ai-core/src/acp/store/automation-script-store.js +415 -0
- package/dist-electron/services/local-ai-core/src/acp/store/automation-store.js +829 -0
- package/dist-electron/services/local-ai-core/src/acp/store/local-core-acp-store.js +158 -0
- package/dist-electron/services/local-ai-core/src/acp/store/schema.js +81 -0
- package/dist-electron/services/local-ai-core/src/automation/automation-action-executor.js +144 -0
- package/dist-electron/services/local-ai-core/src/automation/automation-condition-engine.js +79 -0
- package/dist-electron/services/local-ai-core/src/automation/automation-conversation-executor.js +39 -73
- package/dist-electron/services/local-ai-core/src/automation/automation-monitor-service.js +695 -260
- package/dist-electron/services/local-ai-core/src/automation/automation-script-service.js +507 -0
- package/dist-electron/services/local-ai-core/src/automation/automation-service.js +1019 -0
- package/dist-electron/services/local-ai-core/src/automation/automation-trigger-engine.js +141 -0
- package/dist-electron/services/local-ai-core/src/automation/condition-evaluator.js +60 -17
- package/dist-electron/services/local-ai-core/src/automation/legacy-automation-mappers.js +286 -0
- package/dist-electron/services/local-ai-core/src/automation/scripts/anthropic-sandbox-runner.js +792 -0
- package/dist-electron/services/local-ai-core/src/automation/scripts/sandbox-runner.js +2 -0
- package/dist-electron/services/local-ai-core/src/automation/scripts/script-package.js +475 -0
- package/dist-electron/services/local-ai-core/src/automation/scripts/script-protocol-runner.js +266 -0
- package/dist-electron/services/local-ai-core/src/automation/scripts/secret-resolver.js +37 -0
- package/dist-electron/services/local-ai-core/src/channel/weixin/inbound-poller.js +1 -2
- package/dist-electron/services/local-ai-core/src/channel/weixin/transport.js +9 -8
- package/dist-electron/services/local-ai-core/src/cli/lac.js +229 -0
- package/dist-electron/services/local-ai-core/src/kernel/bootstrap.js +21 -0
- package/dist-electron/services/local-ai-core/src/runtime/deployment-diagnostics.js +76 -0
- package/dist-electron/services/local-ai-core/src/runtime/handlers/automations-handler.js +322 -0
- package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +20 -0
- package/dist-electron/services/local-ai-core/src/runtime/managed-skill-catalog.js +37 -0
- package/dist-electron/services/local-ai-core/src/runtime/server-helpers.js +14 -4
- package/dist-electron/services/local-ai-core/src/runtime/server-routes.js +75 -0
- package/dist-electron/services/local-ai-core/src/runtime/server.js +21 -0
- package/dist-electron/services/local-ai-core/src/runtime/standalone.js +1 -0
- package/dist-electron/services/local-ai-core/src/scheduler/cron.js +151 -39
- package/dist-electron/services/local-ai-core/src/scheduler/scheduled-conversation-executor.js +2 -4
- package/dist-electron/services/local-ai-core/src/scheduler/scheduled-job-application-service.js +64 -12
- package/dist-electron/services/local-ai-core/src/scheduler/scheduler-service.js +71 -4
- package/dist-electron/services/local-ai-core/src/scheduler/thread-resolution.js +10 -0
- package/dist-electron/services/local-ai-core/src/thread/agent-message-policy.js +16 -1
- package/dist-electron/src/pages/Automation/automation-page-model.js +76 -0
- package/dist-electron/tests/contracts/agent-message-policy.test.js +32 -0
- package/dist-electron/tests/contracts/architecture-docs.test.js +30 -0
- package/dist-electron/tests/contracts/automation-contracts.test.js +154 -0
- package/dist-electron/tests/contracts/automation-renderer-model.test.js +47 -0
- package/dist-electron/tests/electron/automation-action-executor.test.js +75 -0
- package/dist-electron/tests/electron/automation-compatibility.test.js +463 -0
- package/dist-electron/tests/electron/automation-deployment.test.js +66 -0
- package/dist-electron/tests/electron/automation-engine.test.js +264 -0
- package/dist-electron/tests/electron/automation-monitor.test.js +1333 -0
- package/dist-electron/tests/electron/automation-sandbox-runner.test.js +344 -0
- package/dist-electron/tests/electron/automation-script-approval.test.js +553 -0
- package/dist-electron/tests/electron/automation-script-package.test.js +499 -0
- package/dist-electron/tests/electron/automation-script-runner.test.js +120 -0
- package/dist-electron/tests/electron/automation-service.test.js +1078 -0
- package/dist-electron/tests/electron/automation-store.test.js +593 -0
- package/dist-electron/tests/electron/condition-trigger-skill.test.js +93 -0
- package/dist-electron/tests/electron/core-client.test.js +99 -0
- package/dist-electron/tests/electron/lac-cli.test.js +110 -0
- package/dist-electron/tests/electron/workspace-task-store.test.js +25 -0
- package/dist-electron/tests/integration/automation-routes.test.js +186 -0
- package/dist-electron/tests/integration/automation-sandbox-runtime.test.js +92 -0
- package/package.json +4 -3
- package/dist/renderer/assets/CronList-CupZoNwW.js +0 -1
- package/dist/renderer/assets/Dashboard-nLZnOtcs.js +0 -41
- package/dist/renderer/assets/MonitorList-BlX82rXF.js +0 -1
- package/dist/renderer/assets/en-CmAr_0e6.js +0 -1
- package/dist/renderer/assets/es-Cs9x52ca.js +0 -1
- package/dist/renderer/assets/index-Dd00oqgY.js +0 -167
- package/dist/renderer/assets/ja-DtvtBRTW.js +0 -1
- package/dist/renderer/assets/play-DBJ8wsSy.js +0 -6
- package/dist/renderer/assets/zh-CcJ9vBZj.js +0 -1
- package/dist/renderer/assets/zh-TW-elI72AC1.js +0 -1
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LocalAutomationScriptStore = void 0;
|
|
4
|
+
exports.parseAutomationScriptVersionRow = parseAutomationScriptVersionRow;
|
|
5
|
+
const node_crypto_1 = require("node:crypto");
|
|
6
|
+
const node_fs_1 = require("node:fs");
|
|
7
|
+
const node_path_1 = require("node:path");
|
|
8
|
+
const script_package_js_1 = require("../../automation/scripts/script-package.js");
|
|
9
|
+
const SCRIPT_COLUMNS = 'id, workspace_id, title, description, created_at, updated_at';
|
|
10
|
+
const VERSION_COLUMNS = `
|
|
11
|
+
id, script_id, status, package_sha256, package_path, shebang, interpreter_path, interpreter_version,
|
|
12
|
+
created_at, updated_at, version_json
|
|
13
|
+
`;
|
|
14
|
+
class LocalAutomationScriptStore {
|
|
15
|
+
db;
|
|
16
|
+
userDataPath;
|
|
17
|
+
constructor(db, userDataPath) {
|
|
18
|
+
this.db = db;
|
|
19
|
+
this.userDataPath = userDataPath;
|
|
20
|
+
}
|
|
21
|
+
createScript(input) {
|
|
22
|
+
const now = new Date().toISOString();
|
|
23
|
+
const script = {
|
|
24
|
+
id: `script:${(0, node_crypto_1.randomUUID)()}`,
|
|
25
|
+
workspaceId: requiredString(input.workspaceId, 'Automation script workspaceId'),
|
|
26
|
+
title: requiredString(input.title, 'Automation script title'),
|
|
27
|
+
...(input.description === undefined ? {} : { description: optionalDescription(input.description) }),
|
|
28
|
+
createdAt: now,
|
|
29
|
+
updatedAt: now,
|
|
30
|
+
};
|
|
31
|
+
this.db.prepare(`
|
|
32
|
+
INSERT INTO automation_scripts (id, workspace_id, title, description, created_at, updated_at)
|
|
33
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
34
|
+
`).run(script.id, script.workspaceId, script.title, script.description ?? null, script.createdAt, script.updatedAt);
|
|
35
|
+
return this.getScript(script.id);
|
|
36
|
+
}
|
|
37
|
+
updateScript(scriptId, input) {
|
|
38
|
+
const existing = this.requireScript(scriptId);
|
|
39
|
+
const updated = {
|
|
40
|
+
...existing,
|
|
41
|
+
...(input.title === undefined ? {} : { title: requiredString(input.title, 'Automation script title') }),
|
|
42
|
+
...(input.description === undefined ? {} : { description: optionalDescription(input.description) }),
|
|
43
|
+
updatedAt: new Date().toISOString(),
|
|
44
|
+
};
|
|
45
|
+
this.db.prepare(`
|
|
46
|
+
UPDATE automation_scripts
|
|
47
|
+
SET title = ?, description = ?, updated_at = ?
|
|
48
|
+
WHERE id = ?
|
|
49
|
+
`).run(updated.title, updated.description ?? null, updated.updatedAt, scriptId);
|
|
50
|
+
return this.getScript(scriptId);
|
|
51
|
+
}
|
|
52
|
+
listScripts(workspaceId) {
|
|
53
|
+
const rows = this.db.prepare(`
|
|
54
|
+
SELECT ${SCRIPT_COLUMNS}
|
|
55
|
+
FROM automation_scripts
|
|
56
|
+
${workspaceId ? 'WHERE workspace_id = ?' : ''}
|
|
57
|
+
ORDER BY updated_at DESC, id DESC
|
|
58
|
+
`).all(...(workspaceId ? [workspaceId] : []));
|
|
59
|
+
return rows.map((row) => this.toScript(row));
|
|
60
|
+
}
|
|
61
|
+
getScript(scriptId) {
|
|
62
|
+
const row = this.db.prepare(`SELECT ${SCRIPT_COLUMNS} FROM automation_scripts WHERE id = ?`)
|
|
63
|
+
.get(scriptId);
|
|
64
|
+
return row ? this.toScript(row) : undefined;
|
|
65
|
+
}
|
|
66
|
+
createVersionFromPackage(input) {
|
|
67
|
+
this.requireScript(input.scriptId);
|
|
68
|
+
const staged = (0, script_package_js_1.stageImmutableScriptPackage)({
|
|
69
|
+
userDataPath: this.userDataPath,
|
|
70
|
+
scriptId: input.scriptId,
|
|
71
|
+
sourceDir: input.sourceDir,
|
|
72
|
+
});
|
|
73
|
+
return this.createVersionFromStaged(input.scriptId, staged, input.interpreterPath, input.interpreterVersion);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Stages an API-uploaded source bundle through a server-owned temporary path.
|
|
77
|
+
* The public route deliberately never receives a filesystem path or interpreter.
|
|
78
|
+
*/
|
|
79
|
+
stageSource(input) {
|
|
80
|
+
this.requireScript(input.scriptId);
|
|
81
|
+
const stagingRoot = (0, node_path_1.join)((0, node_path_1.resolve)(this.userDataPath), 'automations', 'staging');
|
|
82
|
+
(0, node_fs_1.mkdirSync)(stagingRoot, { recursive: true });
|
|
83
|
+
const sourceDir = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)(stagingRoot, 'upload-'));
|
|
84
|
+
try {
|
|
85
|
+
writeUploadedSource(sourceDir, input.files);
|
|
86
|
+
return (0, script_package_js_1.stageImmutableScriptPackage)({ userDataPath: this.userDataPath, scriptId: input.scriptId, sourceDir });
|
|
87
|
+
}
|
|
88
|
+
finally {
|
|
89
|
+
(0, node_fs_1.rmSync)(sourceDir, { recursive: true, force: true });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
createVersionFromStaged(scriptId, staged, interpreterPath, interpreterVersion) {
|
|
93
|
+
const now = new Date().toISOString();
|
|
94
|
+
const version = toVersion({
|
|
95
|
+
id: `script-version:${(0, node_crypto_1.randomUUID)()}`,
|
|
96
|
+
scriptId,
|
|
97
|
+
status: 'draft',
|
|
98
|
+
staged,
|
|
99
|
+
interpreterPath: requiredString(interpreterPath, 'Automation script interpreterPath'),
|
|
100
|
+
interpreterVersion: requiredString(interpreterVersion, 'Automation script interpreterVersion'),
|
|
101
|
+
createdAt: now,
|
|
102
|
+
updatedAt: now,
|
|
103
|
+
});
|
|
104
|
+
this.db.prepare(`
|
|
105
|
+
INSERT INTO automation_script_versions (
|
|
106
|
+
id, script_id, status, package_sha256, package_path, shebang, interpreter_path, interpreter_version,
|
|
107
|
+
created_at, updated_at, version_json
|
|
108
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
109
|
+
`).run(version.id, version.scriptId, version.status, version.packageSha256, version.packagePath, version.shebang, version.interpreterPath, version.interpreterVersion, version.createdAt, version.updatedAt, JSON.stringify(version));
|
|
110
|
+
return this.getVersion(version.id);
|
|
111
|
+
}
|
|
112
|
+
discardUnreferencedPackage(scriptId, staged) {
|
|
113
|
+
this.requireScript(scriptId);
|
|
114
|
+
const referenced = this.db.prepare(`
|
|
115
|
+
SELECT 1 FROM automation_script_versions WHERE script_id = ? AND package_sha256 = ? LIMIT 1
|
|
116
|
+
`).get(scriptId, staged.packageSha256);
|
|
117
|
+
if (referenced)
|
|
118
|
+
return;
|
|
119
|
+
const expectedRoot = (0, node_path_1.join)((0, node_path_1.resolve)(this.userDataPath), 'automations', 'scripts', scriptId);
|
|
120
|
+
const expectedPath = (0, node_path_1.join)(expectedRoot, staged.packageSha256);
|
|
121
|
+
if (staged.packagePath !== expectedPath)
|
|
122
|
+
throw new Error('Refusing to discard an unexpected automation script package path.');
|
|
123
|
+
makeWritableForDiscard(staged.packagePath);
|
|
124
|
+
(0, node_fs_1.rmSync)(staged.packagePath, { recursive: true, force: true, maxRetries: 2 });
|
|
125
|
+
}
|
|
126
|
+
listVersions(scriptId) {
|
|
127
|
+
this.requireScript(scriptId);
|
|
128
|
+
const rows = this.db.prepare(`
|
|
129
|
+
SELECT ${VERSION_COLUMNS}
|
|
130
|
+
FROM automation_script_versions
|
|
131
|
+
WHERE script_id = ?
|
|
132
|
+
ORDER BY created_at DESC, id DESC
|
|
133
|
+
`).all(scriptId);
|
|
134
|
+
return rows.map((row) => this.toVersion(row));
|
|
135
|
+
}
|
|
136
|
+
getVersion(versionId) {
|
|
137
|
+
const row = this.db.prepare(`SELECT ${VERSION_COLUMNS} FROM automation_script_versions WHERE id = ?`)
|
|
138
|
+
.get(versionId);
|
|
139
|
+
return row ? this.toVersion(row) : undefined;
|
|
140
|
+
}
|
|
141
|
+
requireScript(scriptId) {
|
|
142
|
+
const script = this.getScript(scriptId);
|
|
143
|
+
if (!script)
|
|
144
|
+
throw new Error(`Automation script not found: ${scriptId}`);
|
|
145
|
+
return script;
|
|
146
|
+
}
|
|
147
|
+
toScript(row) {
|
|
148
|
+
return {
|
|
149
|
+
id: row.id,
|
|
150
|
+
workspaceId: row.workspace_id,
|
|
151
|
+
title: row.title,
|
|
152
|
+
...(row.description === null ? {} : { description: row.description }),
|
|
153
|
+
createdAt: row.created_at,
|
|
154
|
+
updatedAt: row.updated_at,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
toVersion(row) {
|
|
158
|
+
return parseAutomationScriptVersionRow(row);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
exports.LocalAutomationScriptStore = LocalAutomationScriptStore;
|
|
162
|
+
const MAX_UPLOAD_FILES = 64;
|
|
163
|
+
const MAX_UPLOAD_BYTES = 1024 * 1024;
|
|
164
|
+
function writeUploadedSource(sourceDir, files) {
|
|
165
|
+
if (!Array.isArray(files) || files.length === 0 || files.length > MAX_UPLOAD_FILES) {
|
|
166
|
+
throw new Error(`Automation script source must contain between 1 and ${MAX_UPLOAD_FILES} files.`);
|
|
167
|
+
}
|
|
168
|
+
const seen = new Set();
|
|
169
|
+
let totalBytes = 0;
|
|
170
|
+
for (const file of files) {
|
|
171
|
+
const path = normalizeUploadPath(file.path);
|
|
172
|
+
if (seen.has(path))
|
|
173
|
+
throw new Error(`Automation script source has duplicate file path: ${path}`);
|
|
174
|
+
seen.add(path);
|
|
175
|
+
if (typeof file.content !== 'string')
|
|
176
|
+
throw new Error(`Automation script source file ${path} must be text.`);
|
|
177
|
+
const content = Buffer.from(file.content, 'utf8');
|
|
178
|
+
totalBytes += content.byteLength;
|
|
179
|
+
if (totalBytes > MAX_UPLOAD_BYTES)
|
|
180
|
+
throw new Error(`Automation script source exceeds ${MAX_UPLOAD_BYTES} bytes.`);
|
|
181
|
+
const target = (0, node_path_1.join)(sourceDir, ...path.split('/'));
|
|
182
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(target), { recursive: true });
|
|
183
|
+
(0, node_fs_1.writeFileSync)(target, content, { encoding: 'utf8', mode: 0o600, flag: 'wx' });
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function normalizeUploadPath(value) {
|
|
187
|
+
if (typeof value !== 'string' || !value.trim())
|
|
188
|
+
throw new Error('Automation script source file path is required.');
|
|
189
|
+
const path = value.trim();
|
|
190
|
+
if (path.includes('\\') || path.startsWith('/') || node_path_1.posix.isAbsolute(path) || node_path_1.win32.isAbsolute(path)) {
|
|
191
|
+
throw new Error('Automation script source file path must be relative POSIX path.');
|
|
192
|
+
}
|
|
193
|
+
const normalized = node_path_1.posix.normalize(path);
|
|
194
|
+
if (normalized !== path || path.split('/').some((part) => !part || part === '.' || part === '..')) {
|
|
195
|
+
throw new Error('Automation script source file path must not contain traversal.');
|
|
196
|
+
}
|
|
197
|
+
return path;
|
|
198
|
+
}
|
|
199
|
+
function makeWritableForDiscard(path) {
|
|
200
|
+
const stat = (0, node_fs_1.lstatSync)(path);
|
|
201
|
+
if (stat.isSymbolicLink())
|
|
202
|
+
throw new Error('Refusing to discard a symlinked automation script package.');
|
|
203
|
+
if (stat.isDirectory()) {
|
|
204
|
+
for (const child of (0, node_fs_1.readdirSync)(path))
|
|
205
|
+
makeWritableForDiscard((0, node_path_1.join)(path, child));
|
|
206
|
+
(0, node_fs_1.chmodSync)(path, 0o700);
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
if (!stat.isFile())
|
|
210
|
+
throw new Error('Refusing to discard a non-regular automation script package entry.');
|
|
211
|
+
(0, node_fs_1.chmodSync)(path, 0o600);
|
|
212
|
+
}
|
|
213
|
+
function parseAutomationScriptVersionRow(row) {
|
|
214
|
+
try {
|
|
215
|
+
const version = parseVersion(row.version_json);
|
|
216
|
+
assertDuplicatedField('id', row.id, version.id);
|
|
217
|
+
assertDuplicatedField('scriptId', row.script_id, version.scriptId);
|
|
218
|
+
assertDuplicatedField('status', row.status, version.status);
|
|
219
|
+
assertDuplicatedField('packageSha256', row.package_sha256, version.packageSha256);
|
|
220
|
+
assertDuplicatedField('packagePath', row.package_path, version.packagePath);
|
|
221
|
+
assertDuplicatedField('shebang', row.shebang, version.shebang);
|
|
222
|
+
assertDuplicatedField('interpreterPath', row.interpreter_path, version.interpreterPath);
|
|
223
|
+
assertDuplicatedField('interpreterVersion', row.interpreter_version, version.interpreterVersion);
|
|
224
|
+
assertDuplicatedField('createdAt', row.created_at, version.createdAt);
|
|
225
|
+
assertDuplicatedField('updatedAt', row.updated_at, version.updatedAt);
|
|
226
|
+
return version;
|
|
227
|
+
}
|
|
228
|
+
catch (error) {
|
|
229
|
+
throw new Error(`Automation script version ${row.id} contains invalid persisted data: ${error instanceof Error ? error.message : String(error)}`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
function toVersion(input) {
|
|
233
|
+
const manifest = input.staged.manifest;
|
|
234
|
+
return {
|
|
235
|
+
id: input.id,
|
|
236
|
+
scriptId: input.scriptId,
|
|
237
|
+
status: input.status,
|
|
238
|
+
packageSha256: input.staged.packageSha256,
|
|
239
|
+
packagePath: input.staged.packagePath,
|
|
240
|
+
shebang: input.staged.shebang,
|
|
241
|
+
interpreterPath: input.interpreterPath,
|
|
242
|
+
interpreterVersion: input.interpreterVersion,
|
|
243
|
+
capabilities: asRecord(manifest.capabilities ?? {}, 'Automation script capabilities'),
|
|
244
|
+
config: asRecord(manifest.config, 'Automation script config'),
|
|
245
|
+
configSchema: asRecord(manifest.configSchema, 'Automation script configSchema'),
|
|
246
|
+
networkMode: manifest.capabilities.network,
|
|
247
|
+
internalAccess: manifest.capabilities.internalAccess,
|
|
248
|
+
allowedReadDirs: stringArray(manifest.capabilities.allowedReadDirs, 'Automation script allowedReadDirs'),
|
|
249
|
+
secretRefs: stringArray(manifest.secretRefs, 'Automation script secretRefs'),
|
|
250
|
+
env: stringArray(manifest.env, 'Automation script env'),
|
|
251
|
+
limits: parseLimits(manifest.limits, 'Automation script limits'),
|
|
252
|
+
staticCheck: {
|
|
253
|
+
packageSha256: input.staged.packageSha256,
|
|
254
|
+
entries: input.staged.entries,
|
|
255
|
+
},
|
|
256
|
+
testPlan: asRecord(manifest.testPlan ?? {}, 'Automation script testPlan'),
|
|
257
|
+
createdAt: input.createdAt,
|
|
258
|
+
updatedAt: input.updatedAt,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
function parseVersion(value) {
|
|
262
|
+
const parsed = JSON.parse(value);
|
|
263
|
+
return {
|
|
264
|
+
id: requiredString(parsed.id, 'Automation script version id'),
|
|
265
|
+
scriptId: requiredString(parsed.scriptId, 'Automation script version scriptId'),
|
|
266
|
+
status: parseStatus(parsed.status),
|
|
267
|
+
packageSha256: requiredString(parsed.packageSha256, 'Automation script version packageSha256'),
|
|
268
|
+
packagePath: requiredString(parsed.packagePath, 'Automation script version packagePath'),
|
|
269
|
+
shebang: requiredString(parsed.shebang, 'Automation script version shebang'),
|
|
270
|
+
interpreterPath: requiredString(parsed.interpreterPath, 'Automation script version interpreterPath'),
|
|
271
|
+
interpreterVersion: requiredString(parsed.interpreterVersion, 'Automation script version interpreterVersion'),
|
|
272
|
+
capabilities: asRecord(parsed.capabilities, 'Automation script version capabilities'),
|
|
273
|
+
config: asRecord(parsed.config, 'Automation script version config'),
|
|
274
|
+
configSchema: asRecord(parsed.configSchema, 'Automation script version configSchema'),
|
|
275
|
+
networkMode: parseNetworkMode(parsed.networkMode, 'Automation script version networkMode'),
|
|
276
|
+
internalAccess: booleanValue(parsed.internalAccess, 'Automation script version internalAccess'),
|
|
277
|
+
allowedReadDirs: stringArray(parsed.allowedReadDirs, 'Automation script version allowedReadDirs'),
|
|
278
|
+
secretRefs: stringArray(parsed.secretRefs, 'Automation script version secretRefs'),
|
|
279
|
+
env: stringArray(parsed.env, 'Automation script version env'),
|
|
280
|
+
limits: parseLimits(parsed.limits, 'Automation script version limits'),
|
|
281
|
+
staticCheck: parseStaticCheck(parsed.staticCheck, 'Automation script version staticCheck'),
|
|
282
|
+
testPlan: asRecord(parsed.testPlan, 'Automation script version testPlan'),
|
|
283
|
+
...(parsed.testReport === undefined ? {} : { testReport: asRecord(parsed.testReport, 'Automation script version testReport') }),
|
|
284
|
+
...(parsed.pendingTestApprovalId === undefined ? {} : { pendingTestApprovalId: optionalString(parsed.pendingTestApprovalId, 'Automation script version pendingTestApprovalId') }),
|
|
285
|
+
...(parsed.pendingApprovalId === undefined ? {} : { pendingApprovalId: optionalString(parsed.pendingApprovalId, 'Automation script version pendingApprovalId') }),
|
|
286
|
+
...(parsed.testAuthorization === undefined ? {} : { testAuthorization: parseAuditActor(parsed.testAuthorization, 'Automation script version testAuthorization') }),
|
|
287
|
+
...(parsed.approval === undefined ? {} : { approval: parseAuditActor(parsed.approval, 'Automation script version approval') }),
|
|
288
|
+
...(parsed.rejection === undefined ? {} : { rejection: parseAuditActor(parsed.rejection, 'Automation script version rejection') }),
|
|
289
|
+
...(parsed.revocation === undefined ? {} : { revocation: parseAuditActor(parsed.revocation, 'Automation script version revocation') }),
|
|
290
|
+
createdAt: requiredString(parsed.createdAt, 'Automation script version createdAt'),
|
|
291
|
+
updatedAt: requiredString(parsed.updatedAt, 'Automation script version updatedAt'),
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
function parseStatus(value) {
|
|
295
|
+
if (value === 'draft' ||
|
|
296
|
+
value === 'pending_test_approval' ||
|
|
297
|
+
value === 'test_authorized' ||
|
|
298
|
+
value === 'testing' ||
|
|
299
|
+
value === 'tested' ||
|
|
300
|
+
value === 'pending_approval' ||
|
|
301
|
+
value === 'approved' ||
|
|
302
|
+
value === 'rejected' ||
|
|
303
|
+
value === 'revoked') {
|
|
304
|
+
return value;
|
|
305
|
+
}
|
|
306
|
+
throw new Error('Automation script version status is invalid.');
|
|
307
|
+
}
|
|
308
|
+
function requiredString(value, label) {
|
|
309
|
+
if (typeof value !== 'string' || !value.trim())
|
|
310
|
+
throw new Error(`${label} is required.`);
|
|
311
|
+
return value.trim();
|
|
312
|
+
}
|
|
313
|
+
function optionalString(value, label) {
|
|
314
|
+
if (value === undefined)
|
|
315
|
+
return undefined;
|
|
316
|
+
return requiredString(value, label);
|
|
317
|
+
}
|
|
318
|
+
function optionalDescription(value) {
|
|
319
|
+
if (value === undefined)
|
|
320
|
+
return undefined;
|
|
321
|
+
if (typeof value !== 'string')
|
|
322
|
+
throw new Error('Automation script description must be a string.');
|
|
323
|
+
const normalized = value.trim();
|
|
324
|
+
return normalized || undefined;
|
|
325
|
+
}
|
|
326
|
+
function asRecord(value, label) {
|
|
327
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
328
|
+
throw new Error(`${label} must be an object.`);
|
|
329
|
+
return value;
|
|
330
|
+
}
|
|
331
|
+
function stringArray(value, label) {
|
|
332
|
+
if (!Array.isArray(value) || value.some((item) => typeof item !== 'string' || !item.trim())) {
|
|
333
|
+
throw new Error(`${label} must be an array of strings.`);
|
|
334
|
+
}
|
|
335
|
+
return value.map((item) => item.trim());
|
|
336
|
+
}
|
|
337
|
+
function booleanValue(value, label) {
|
|
338
|
+
if (typeof value !== 'boolean')
|
|
339
|
+
throw new Error(`${label} must be a boolean.`);
|
|
340
|
+
return value;
|
|
341
|
+
}
|
|
342
|
+
function parseNetworkMode(value, label) {
|
|
343
|
+
if (value === 'none' || value === 'public')
|
|
344
|
+
return value;
|
|
345
|
+
throw new Error(`${label} must be none or public.`);
|
|
346
|
+
}
|
|
347
|
+
function parseLimits(value, label) {
|
|
348
|
+
const limits = asRecord(value, label);
|
|
349
|
+
return {
|
|
350
|
+
timeoutMs: positiveSafeInteger(limits.timeoutMs, `${label}.timeoutMs`),
|
|
351
|
+
stdoutBytes: positiveSafeInteger(limits.stdoutBytes, `${label}.stdoutBytes`),
|
|
352
|
+
stderrBytes: positiveSafeInteger(limits.stderrBytes, `${label}.stderrBytes`),
|
|
353
|
+
payloadBytes: positiveSafeInteger(limits.payloadBytes, `${label}.payloadBytes`),
|
|
354
|
+
stateBytes: positiveSafeInteger(limits.stateBytes, `${label}.stateBytes`),
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
function positiveSafeInteger(value, label) {
|
|
358
|
+
if (typeof value !== 'number' || !Number.isSafeInteger(value) || value <= 0) {
|
|
359
|
+
throw new Error(`${label} must be a positive integer.`);
|
|
360
|
+
}
|
|
361
|
+
return value;
|
|
362
|
+
}
|
|
363
|
+
function parseStaticCheck(value, label) {
|
|
364
|
+
const staticCheck = asRecord(value, label);
|
|
365
|
+
requiredString(staticCheck.packageSha256, `${label}.packageSha256`);
|
|
366
|
+
if (!Array.isArray(staticCheck.entries))
|
|
367
|
+
throw new Error(`${label}.entries must be an array.`);
|
|
368
|
+
for (const [index, entry] of staticCheck.entries.entries()) {
|
|
369
|
+
const entryRecord = asRecord(entry, `${label}.entries[${index}]`);
|
|
370
|
+
requiredString(entryRecord.path, `${label}.entries[${index}].path`);
|
|
371
|
+
requiredString(entryRecord.sha256, `${label}.entries[${index}].sha256`);
|
|
372
|
+
nonNegativeSafeInteger(entryRecord.size, `${label}.entries[${index}].size`);
|
|
373
|
+
}
|
|
374
|
+
return staticCheck;
|
|
375
|
+
}
|
|
376
|
+
function nonNegativeSafeInteger(value, label) {
|
|
377
|
+
if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < 0) {
|
|
378
|
+
throw new Error(`${label} must be a non-negative integer.`);
|
|
379
|
+
}
|
|
380
|
+
return value;
|
|
381
|
+
}
|
|
382
|
+
function parseAuditActor(value, label) {
|
|
383
|
+
const actor = asRecord(value, label);
|
|
384
|
+
return {
|
|
385
|
+
actor: requiredString(actor.actor, `${label}.actor`),
|
|
386
|
+
at: isoTimestamp(actor.at, `${label}.at`),
|
|
387
|
+
...(actor.approvalId === undefined ? {} : { approvalId: optionalString(actor.approvalId, `${label}.approvalId`) }),
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
function isoTimestamp(value, label) {
|
|
391
|
+
const normalized = requiredString(value, label);
|
|
392
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d{1,3})?(?:Z|[+-]\d{2}:\d{2})$/.exec(normalized);
|
|
393
|
+
if (!match || Number.isNaN(Date.parse(normalized))) {
|
|
394
|
+
throw new Error(`${label} must be a valid ISO timestamp.`);
|
|
395
|
+
}
|
|
396
|
+
const [, yearText, monthText, dayText, hourText, minuteText, secondText] = match;
|
|
397
|
+
const year = Number(yearText);
|
|
398
|
+
const month = Number(monthText);
|
|
399
|
+
const day = Number(dayText);
|
|
400
|
+
const calendarDate = new Date(Date.UTC(year, month - 1, day));
|
|
401
|
+
if (calendarDate.getUTCFullYear() !== year ||
|
|
402
|
+
calendarDate.getUTCMonth() !== month - 1 ||
|
|
403
|
+
calendarDate.getUTCDate() !== day ||
|
|
404
|
+
Number(hourText) > 23 ||
|
|
405
|
+
Number(minuteText) > 59 ||
|
|
406
|
+
Number(secondText) > 59) {
|
|
407
|
+
throw new Error(`${label} must be a valid ISO timestamp.`);
|
|
408
|
+
}
|
|
409
|
+
return normalized;
|
|
410
|
+
}
|
|
411
|
+
function assertDuplicatedField(field, stored, json) {
|
|
412
|
+
if (stored !== json) {
|
|
413
|
+
throw new Error(`${field} mismatch between duplicated columns and JSON body.`);
|
|
414
|
+
}
|
|
415
|
+
}
|