@kafca/agentdock 0.1.61 → 0.1.62

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 (127) hide show
  1. package/README.md +18 -0
  2. package/dist/renderer/assets/AutomationList-BgJgAdVO.js +16 -0
  3. package/dist/renderer/assets/{Badge-BLI4LHse.js → Badge-Bq2hPVcR.js} +1 -1
  4. package/dist/renderer/assets/{Card-DJeLDdL3.js → Card-BPJNSZce.js} +1 -1
  5. package/dist/renderer/assets/{Config-D1awc81A.js → Config-aSFqlI88.js} +2 -2
  6. package/dist/renderer/assets/Dashboard-D3ezFmb7.js +36 -0
  7. package/dist/renderer/assets/{EmptyState-CkI1RwPX.js → EmptyState-DK496gBq.js} +1 -1
  8. package/dist/renderer/assets/{HighlightedMarkdown-BUhDlhgW.js → HighlightedMarkdown-Ckre5jNv.js} +1 -1
  9. package/dist/renderer/assets/{Input-DHYrn-Ne.js → Input-C5C1d0ba.js} +1 -1
  10. package/dist/renderer/assets/{KnowledgeDetail-Bij-Xo90.js → KnowledgeDetail-N8Ey1ikc.js} +2 -2
  11. package/dist/renderer/assets/{KnowledgeHome-DmPZ2IlI.js → KnowledgeHome-9aF8nJEf.js} +2 -2
  12. package/dist/renderer/assets/{Logs-ISY99heA.js → Logs-sgAdwEeS.js} +2 -2
  13. package/dist/renderer/assets/{Modal-DAQfz0ot.js → Modal-BB10hTRc.js} +3 -3
  14. package/dist/renderer/assets/{Page-Dw43UYDT.js → Page-DfC319Ck.js} +1 -1
  15. package/dist/renderer/assets/{Select-DzmN5OGQ.js → Select-RHJBmNER.js} +1 -1
  16. package/dist/renderer/assets/{ThreadChat-B5CWqqMK.js → ThreadChat-BU1zCdr4.js} +3 -3
  17. package/dist/renderer/assets/{Workspace-C5g9LBj9.js → Workspace-BoZCz7S8.js} +1 -1
  18. package/dist/renderer/assets/{arrow-left-BLXh56Ip.js → arrow-left-D2V1eZSD.js} +1 -1
  19. package/dist/renderer/assets/{book-open-DhPGbc-7.js → book-open-DUcBn7Bb.js} +1 -1
  20. package/dist/renderer/assets/{channels-DfQrmuYO.js → channels-DMhDSlf2.js} +2 -2
  21. package/dist/renderer/assets/{chevron-down-CEFtpPDc.js → chevron-down-Ca2FQDB7.js} +1 -1
  22. package/dist/renderer/assets/en-Bnr4nMzg.js +1 -0
  23. package/dist/renderer/assets/es-B1GIFh3i.js +1 -0
  24. package/dist/renderer/assets/{index-BIKL3fTP.css → index-B6WcpkJF.css} +1 -1
  25. package/dist/renderer/assets/{index-CYhDUAR_.js → index-BlnvlekJ.js} +1 -1
  26. package/dist/renderer/assets/index-DRJ5wgw_.js +162 -0
  27. package/dist/renderer/assets/ja-CDWPWqEW.js +1 -0
  28. package/dist/renderer/assets/{knowledge-uHRQ80sR.js → knowledge-D1-k56eY.js} +1 -1
  29. package/dist/renderer/assets/{pencil-BD2Y31YC.js → pencil-BhWdv_86.js} +1 -1
  30. package/dist/renderer/assets/{plus-DDcldp_c.js → plus-BacLMlK3.js} +1 -1
  31. package/dist/renderer/assets/{save-CMW_cZI9.js → save-Bpoxbzau.js} +1 -1
  32. package/dist/renderer/assets/{search-BM-0CvJr.js → search-BVJcpdFX.js} +1 -1
  33. package/dist/renderer/assets/{shield-check-DaMB0FKG.js → shield-check-C2cw_Ip6.js} +1 -1
  34. package/dist/renderer/assets/{threads-DZgt9tWj.js → threads-DOsBQBg_.js} +1 -1
  35. package/dist/renderer/assets/{trash-2-DTXVbC5y.js → trash-2-Bs3rEm3g.js} +1 -1
  36. package/dist/renderer/assets/zh-MSddxMDX.js +1 -0
  37. package/dist/renderer/assets/zh-TW-C_LNWX0o.js +1 -0
  38. package/dist/renderer/index.html +2 -2
  39. package/dist-electron/electron/managed-skills/agent-browser/SKILL.md +41 -0
  40. package/dist-electron/electron/managed-skills/agent-browser/references/authentication.md +83 -0
  41. package/dist-electron/electron/managed-skills/agent-browser/references/commands.md +50 -0
  42. package/dist-electron/electron/managed-skills/agent-browser/references/profiling.md +20 -0
  43. package/dist-electron/electron/managed-skills/agent-browser/references/proxy-support.md +18 -0
  44. package/dist-electron/electron/managed-skills/agent-browser/references/session-management.md +17 -0
  45. package/dist-electron/electron/managed-skills/agent-browser/references/snapshot-refs.md +17 -0
  46. package/dist-electron/electron/managed-skills/agent-browser/references/video-recording.md +21 -0
  47. package/dist-electron/electron/managed-skills/agent-browser/templates/authenticated-session.sh +11 -0
  48. package/dist-electron/electron/managed-skills/agent-browser/templates/capture-workflow.sh +12 -0
  49. package/dist-electron/electron/managed-skills/agent-browser/templates/form-automation.sh +10 -0
  50. package/dist-electron/electron/managed-skills/condition-trigger/SKILL.md +21 -0
  51. package/dist-electron/electron/managed-skills/condition-trigger/scripts/register-condition-trigger.sh +106 -0
  52. package/dist-electron/electron/managed-skills/knowledge-base/SKILL.md +13 -0
  53. package/dist-electron/electron/managed-skills/knowledge-base/scripts/search-knowledge.sh +80 -0
  54. package/dist-electron/packages/contracts/src/automations.js +211 -0
  55. package/dist-electron/packages/contracts/src/index.js +1 -0
  56. package/dist-electron/packages/core-sdk/src/automations.js +87 -0
  57. package/dist-electron/packages/core-sdk/src/client.js +101 -0
  58. package/dist-electron/packages/core-sdk/src/index.js +2 -1
  59. package/dist-electron/services/local-ai-core/src/acp/store/automation-script-store.js +415 -0
  60. package/dist-electron/services/local-ai-core/src/acp/store/automation-store.js +829 -0
  61. package/dist-electron/services/local-ai-core/src/acp/store/local-core-acp-store.js +158 -0
  62. package/dist-electron/services/local-ai-core/src/acp/store/schema.js +81 -0
  63. package/dist-electron/services/local-ai-core/src/automation/automation-action-executor.js +144 -0
  64. package/dist-electron/services/local-ai-core/src/automation/automation-condition-engine.js +79 -0
  65. package/dist-electron/services/local-ai-core/src/automation/automation-conversation-executor.js +39 -73
  66. package/dist-electron/services/local-ai-core/src/automation/automation-monitor-service.js +695 -260
  67. package/dist-electron/services/local-ai-core/src/automation/automation-script-service.js +507 -0
  68. package/dist-electron/services/local-ai-core/src/automation/automation-service.js +1019 -0
  69. package/dist-electron/services/local-ai-core/src/automation/automation-trigger-engine.js +141 -0
  70. package/dist-electron/services/local-ai-core/src/automation/condition-evaluator.js +60 -17
  71. package/dist-electron/services/local-ai-core/src/automation/legacy-automation-mappers.js +286 -0
  72. package/dist-electron/services/local-ai-core/src/automation/scripts/anthropic-sandbox-runner.js +792 -0
  73. package/dist-electron/services/local-ai-core/src/automation/scripts/sandbox-runner.js +2 -0
  74. package/dist-electron/services/local-ai-core/src/automation/scripts/script-package.js +475 -0
  75. package/dist-electron/services/local-ai-core/src/automation/scripts/script-protocol-runner.js +266 -0
  76. package/dist-electron/services/local-ai-core/src/automation/scripts/secret-resolver.js +37 -0
  77. package/dist-electron/services/local-ai-core/src/channel/weixin/inbound-poller.js +1 -2
  78. package/dist-electron/services/local-ai-core/src/channel/weixin/transport.js +9 -8
  79. package/dist-electron/services/local-ai-core/src/cli/lac.js +229 -0
  80. package/dist-electron/services/local-ai-core/src/kernel/bootstrap.js +21 -0
  81. package/dist-electron/services/local-ai-core/src/runtime/deployment-diagnostics.js +76 -0
  82. package/dist-electron/services/local-ai-core/src/runtime/handlers/automations-handler.js +322 -0
  83. package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +20 -0
  84. package/dist-electron/services/local-ai-core/src/runtime/managed-skill-catalog.js +37 -0
  85. package/dist-electron/services/local-ai-core/src/runtime/server-helpers.js +14 -4
  86. package/dist-electron/services/local-ai-core/src/runtime/server-routes.js +75 -0
  87. package/dist-electron/services/local-ai-core/src/runtime/server.js +21 -0
  88. package/dist-electron/services/local-ai-core/src/runtime/standalone.js +1 -0
  89. package/dist-electron/services/local-ai-core/src/scheduler/cron.js +151 -39
  90. package/dist-electron/services/local-ai-core/src/scheduler/scheduled-conversation-executor.js +2 -4
  91. package/dist-electron/services/local-ai-core/src/scheduler/scheduled-job-application-service.js +64 -12
  92. package/dist-electron/services/local-ai-core/src/scheduler/scheduler-service.js +71 -4
  93. package/dist-electron/services/local-ai-core/src/scheduler/thread-resolution.js +10 -0
  94. package/dist-electron/services/local-ai-core/src/thread/agent-message-policy.js +16 -1
  95. package/dist-electron/src/pages/Automation/automation-page-model.js +76 -0
  96. package/dist-electron/tests/contracts/agent-message-policy.test.js +32 -0
  97. package/dist-electron/tests/contracts/architecture-docs.test.js +30 -0
  98. package/dist-electron/tests/contracts/automation-contracts.test.js +154 -0
  99. package/dist-electron/tests/contracts/automation-renderer-model.test.js +47 -0
  100. package/dist-electron/tests/electron/automation-action-executor.test.js +75 -0
  101. package/dist-electron/tests/electron/automation-compatibility.test.js +463 -0
  102. package/dist-electron/tests/electron/automation-deployment.test.js +66 -0
  103. package/dist-electron/tests/electron/automation-engine.test.js +264 -0
  104. package/dist-electron/tests/electron/automation-monitor.test.js +1333 -0
  105. package/dist-electron/tests/electron/automation-sandbox-runner.test.js +333 -0
  106. package/dist-electron/tests/electron/automation-script-approval.test.js +553 -0
  107. package/dist-electron/tests/electron/automation-script-package.test.js +499 -0
  108. package/dist-electron/tests/electron/automation-script-runner.test.js +120 -0
  109. package/dist-electron/tests/electron/automation-service.test.js +1078 -0
  110. package/dist-electron/tests/electron/automation-store.test.js +593 -0
  111. package/dist-electron/tests/electron/condition-trigger-skill.test.js +93 -0
  112. package/dist-electron/tests/electron/core-client.test.js +99 -0
  113. package/dist-electron/tests/electron/lac-cli.test.js +110 -0
  114. package/dist-electron/tests/electron/workspace-task-store.test.js +25 -0
  115. package/dist-electron/tests/integration/automation-routes.test.js +186 -0
  116. package/dist-electron/tests/integration/automation-sandbox-runtime.test.js +92 -0
  117. package/package.json +4 -3
  118. package/dist/renderer/assets/CronList-CupZoNwW.js +0 -1
  119. package/dist/renderer/assets/Dashboard-nLZnOtcs.js +0 -41
  120. package/dist/renderer/assets/MonitorList-BlX82rXF.js +0 -1
  121. package/dist/renderer/assets/en-CmAr_0e6.js +0 -1
  122. package/dist/renderer/assets/es-Cs9x52ca.js +0 -1
  123. package/dist/renderer/assets/index-Dd00oqgY.js +0 -167
  124. package/dist/renderer/assets/ja-DtvtBRTW.js +0 -1
  125. package/dist/renderer/assets/play-DBJ8wsSy.js +0 -6
  126. package/dist/renderer/assets/zh-CcJ9vBZj.js +0 -1
  127. package/dist/renderer/assets/zh-TW-elI72AC1.js +0 -1
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,475 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.stageImmutableScriptPackage = stageImmutableScriptPackage;
4
+ exports.verifyStagedScriptPackage = verifyStagedScriptPackage;
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
+ function stageImmutableScriptPackage(input) {
9
+ const scriptId = validatePathToken(input.scriptId, 'Automation script id');
10
+ const sourceDir = (0, node_path_1.resolve)(input.sourceDir);
11
+ const sourceStat = (0, node_fs_1.lstatSync)(sourceDir);
12
+ if (sourceStat.isSymbolicLink() || !sourceStat.isDirectory()) {
13
+ throw new Error('Automation script package source must be a real directory.');
14
+ }
15
+ const entries = collectPackageEntries(sourceDir);
16
+ const manifestEntry = entries.find((entry) => entry.path === 'manifest.json');
17
+ if (!manifestEntry)
18
+ throw new Error('Automation script package manifest.json is required.');
19
+ const manifest = parseManifest(manifestEntry.content);
20
+ const entrypoint = normalizeRelativePosixPath(manifest.entrypoint, 'Automation script entrypoint');
21
+ const entry = entries.find((candidate) => candidate.path === entrypoint);
22
+ if (!entry)
23
+ throw new Error(`Automation script entrypoint not found: ${entrypoint}`);
24
+ const shebang = validateTextEntrypoint(entry.content);
25
+ manifest.entrypoint = entrypoint;
26
+ const packageSha256 = hashEntries(entries);
27
+ const root = (0, node_path_1.join)((0, node_path_1.resolve)(input.userDataPath), 'automations', 'scripts', scriptId);
28
+ const packagePath = (0, node_path_1.join)(root, packageSha256);
29
+ if ((0, node_fs_1.existsSync)(packagePath)) {
30
+ assertRealDirectory(packagePath, 'Existing automation script package');
31
+ verifyPackageHash(packagePath, packageSha256);
32
+ chmodPackageTreeReadOnly(packagePath);
33
+ return stagedPackage(packageSha256, packagePath, shebang, manifest, entries);
34
+ }
35
+ (0, node_fs_1.mkdirSync)(root, { recursive: true });
36
+ const tempPath = (0, node_path_1.join)(root, `.${packageSha256}.tmp-${(0, node_crypto_1.randomUUID)()}`);
37
+ try {
38
+ copyEntriesReadOnly(entries, tempPath);
39
+ verifyPackageHash(tempPath, packageSha256);
40
+ try {
41
+ (0, node_fs_1.renameSync)(tempPath, packagePath);
42
+ chmodPackageTreeReadOnly(packagePath);
43
+ }
44
+ catch (error) {
45
+ if (!(0, node_fs_1.existsSync)(packagePath))
46
+ throw error;
47
+ assertRealDirectory(packagePath, 'Existing automation script package');
48
+ verifyPackageHash(packagePath, packageSha256);
49
+ chmodPackageTreeReadOnly(packagePath);
50
+ (0, node_fs_1.rmSync)(tempPath, { recursive: true, force: true });
51
+ }
52
+ return stagedPackage(packageSha256, packagePath, shebang, manifest, entries);
53
+ }
54
+ catch (error) {
55
+ (0, node_fs_1.rmSync)(tempPath, { recursive: true, force: true });
56
+ throw error;
57
+ }
58
+ }
59
+ /** Re-check immutable package contents immediately before every execution. */
60
+ function verifyStagedScriptPackage(packagePath, expectedSha256) {
61
+ verifyPackageHash(packagePath, expectedSha256);
62
+ }
63
+ function collectPackageEntries(sourceDir) {
64
+ assertRealDirectory(sourceDir, 'Automation script package source');
65
+ const rootRealPath = (0, node_fs_1.realpathSync)(sourceDir);
66
+ const entries = [];
67
+ const visit = (directory) => {
68
+ for (const item of readDirectoryEntriesStable(directory, rootRealPath)) {
69
+ const absolutePath = (0, node_path_1.join)(directory, item.name);
70
+ const stat = (0, node_fs_1.lstatSync)(absolutePath);
71
+ if (stat.isSymbolicLink()) {
72
+ throw new Error(`Automation script packages cannot contain symlinks: ${item.name}`);
73
+ }
74
+ if (stat.isDirectory()) {
75
+ assertContainedRealPath(absolutePath, rootRealPath, 'Automation script package directory');
76
+ visit(absolutePath);
77
+ continue;
78
+ }
79
+ if (!stat.isFile()) {
80
+ throw new Error(`Automation script packages can only contain regular files: ${item.name}`);
81
+ }
82
+ assertContainedRealPath(absolutePath, rootRealPath, 'Automation script package file');
83
+ const packagePath = normalizeFilesystemPackagePath(sourceDir, absolutePath);
84
+ const content = readRegularFileNoFollow(absolutePath, packagePath, rootRealPath);
85
+ if (content.includes(0)) {
86
+ throw new Error(`Automation script package file must be text, not binary: ${packagePath}`);
87
+ }
88
+ entries.push({
89
+ path: packagePath,
90
+ absolutePath,
91
+ content,
92
+ sha256: (0, node_crypto_1.createHash)('sha256').update(content).digest('hex'),
93
+ size: content.byteLength,
94
+ });
95
+ }
96
+ };
97
+ visit(sourceDir);
98
+ if (entries.length === 0)
99
+ throw new Error('Automation script package cannot be empty.');
100
+ return entries.sort(compareEntries);
101
+ }
102
+ function normalizeFilesystemPackagePath(sourceDir, absolutePath) {
103
+ const rawRelative = (0, node_path_1.relative)(sourceDir, absolutePath);
104
+ const parts = rawRelative.split(node_path_1.sep);
105
+ if (parts.some((part) => !part || part === '.' || part === '..')) {
106
+ throw new Error(`Automation script package path must be relative and normalized: ${rawRelative}`);
107
+ }
108
+ return normalizeRelativePosixPath(parts.join(node_path_1.posix.sep), 'Automation script package path');
109
+ }
110
+ function normalizeRelativePosixPath(value, label) {
111
+ if (typeof value !== 'string' || !value.trim())
112
+ throw new Error(`${label} is required.`);
113
+ const input = value.trim();
114
+ if ((0, node_path_1.isAbsolute)(input) || node_path_1.posix.isAbsolute(input) || node_path_1.win32.isAbsolute(input)) {
115
+ throw new Error(`${label} must be a relative POSIX path, not an absolute path.`);
116
+ }
117
+ if (input.includes('\\'))
118
+ throw new Error(`${label} must be a relative POSIX path.`);
119
+ const segments = input.split('/');
120
+ if (segments.some((segment) => !segment || segment === '.' || segment === '..')) {
121
+ throw new Error(`${label} must not contain path traversal.`);
122
+ }
123
+ const normalized = node_path_1.posix.normalize(input);
124
+ if (normalized !== input)
125
+ throw new Error(`${label} must already be normalized.`);
126
+ return normalized;
127
+ }
128
+ function validatePathToken(value, label) {
129
+ if (typeof value !== 'string' || !value.trim())
130
+ throw new Error(`${label} is required.`);
131
+ const token = value.trim();
132
+ if ((0, node_path_1.isAbsolute)(token) ||
133
+ node_path_1.posix.isAbsolute(token) ||
134
+ node_path_1.win32.isAbsolute(token) ||
135
+ token.includes('/') ||
136
+ token.includes('\\') ||
137
+ token === '.' ||
138
+ token === '..' ||
139
+ token.includes('..')) {
140
+ throw new Error(`${label} cannot contain path separators or traversal.`);
141
+ }
142
+ return token;
143
+ }
144
+ function parseManifest(content) {
145
+ let parsed;
146
+ try {
147
+ parsed = JSON.parse(content.toString('utf8'));
148
+ }
149
+ catch (error) {
150
+ throw withContext('Automation script manifest must be valid JSON', error);
151
+ }
152
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
153
+ throw new Error('Automation script manifest must be an object.');
154
+ }
155
+ const manifest = parsed;
156
+ if (manifest.protocolVersion !== 1) {
157
+ throw new Error('Automation script manifest protocolVersion must be 1.');
158
+ }
159
+ const capabilities = parseCapabilities(manifest.capabilities);
160
+ const limits = parseLimits(manifest.limits);
161
+ return {
162
+ ...manifest,
163
+ protocolVersion: 1,
164
+ entrypoint: normalizeRelativePosixPath(manifest.entrypoint, 'Automation script entrypoint'),
165
+ config: asRecord(manifest.config, 'Automation script manifest config'),
166
+ configSchema: asRecord(manifest.configSchema, 'Automation script manifest configSchema'),
167
+ capabilities,
168
+ secretRefs: stringArray(manifest.secretRefs, 'Automation script manifest secretRefs'),
169
+ env: stringArray(manifest.env, 'Automation script manifest env'),
170
+ limits,
171
+ };
172
+ }
173
+ function parseCapabilities(value) {
174
+ const capabilities = asRecord(value, 'Automation script manifest capabilities');
175
+ const network = requiredString(capabilities.network, 'Automation script manifest capabilities.network');
176
+ if (network !== 'none' && network !== 'public') {
177
+ throw new Error('Automation script manifest capabilities.network must be none or public.');
178
+ }
179
+ if (typeof capabilities.internalAccess !== 'boolean') {
180
+ throw new Error('Automation script manifest capabilities.internalAccess must be a boolean.');
181
+ }
182
+ return {
183
+ ...capabilities,
184
+ network,
185
+ internalAccess: capabilities.internalAccess,
186
+ allowedReadDirs: stringArray(capabilities.allowedReadDirs, 'Automation script manifest capabilities.allowedReadDirs'),
187
+ };
188
+ }
189
+ function parseLimits(value) {
190
+ const limits = asRecord(value, 'Automation script manifest limits');
191
+ return {
192
+ ...limits,
193
+ timeoutMs: positiveSafeInteger(limits.timeoutMs, 'Automation script manifest limits.timeoutMs'),
194
+ stdoutBytes: positiveSafeInteger(limits.stdoutBytes, 'Automation script manifest limits.stdoutBytes'),
195
+ stderrBytes: positiveSafeInteger(limits.stderrBytes, 'Automation script manifest limits.stderrBytes'),
196
+ payloadBytes: positiveSafeInteger(limits.payloadBytes, 'Automation script manifest limits.payloadBytes'),
197
+ stateBytes: positiveSafeInteger(limits.stateBytes, 'Automation script manifest limits.stateBytes'),
198
+ };
199
+ }
200
+ function validateTextEntrypoint(content) {
201
+ if (content.includes(0))
202
+ throw new Error('Automation script entrypoint must be text, not binary.');
203
+ const text = content.toString('utf8');
204
+ const firstLine = text.split(/\r?\n/, 1)[0] ?? '';
205
+ if (!firstLine.startsWith('#!') || firstLine.trim().length <= 2) {
206
+ throw new Error('Automation script entrypoint must start with a valid shebang.');
207
+ }
208
+ return firstLine.trim();
209
+ }
210
+ function hashEntries(entries, root) {
211
+ const hash = (0, node_crypto_1.createHash)('sha256');
212
+ const rootRealPath = root ? (0, node_fs_1.realpathSync)(root) : undefined;
213
+ for (const entry of [...entries].sort(compareEntries)) {
214
+ const content = 'content' in entry
215
+ ? entry.content
216
+ : readPackageFileFromRoot(root || '', rootRealPath, entry.path);
217
+ hash.update(entry.path, 'utf8');
218
+ hash.update('\0');
219
+ hash.update((0, node_crypto_1.createHash)('sha256').update(content).digest('hex'), 'utf8');
220
+ hash.update('\0');
221
+ hash.update(String(content.byteLength), 'utf8');
222
+ hash.update('\0');
223
+ }
224
+ return hash.digest('hex');
225
+ }
226
+ function copyEntriesReadOnly(entries, destination) {
227
+ (0, node_fs_1.mkdirSync)(destination, { recursive: true });
228
+ const destinationRealPath = (0, node_fs_1.realpathSync)(destination);
229
+ for (const entry of entries) {
230
+ const target = (0, node_path_1.join)(destination, entry.path);
231
+ (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(target), { recursive: true });
232
+ (0, node_fs_1.writeFileSync)(target, entry.content, { mode: 0o444 });
233
+ chmodPathWithStableParent(target, 0o444, destinationRealPath, 'Automation script package file');
234
+ }
235
+ }
236
+ function verifyPackageHash(packagePath, expectedSha256) {
237
+ assertRealDirectory(packagePath, 'Automation script package');
238
+ const entries = collectPackageEntries(packagePath);
239
+ const actualSha256 = hashEntries(entries);
240
+ if (actualSha256 !== expectedSha256) {
241
+ throw new Error(`Copied automation script package hash mismatch for ${(0, node_path_1.basename)(packagePath)}.`);
242
+ }
243
+ }
244
+ function stagedPackage(packageSha256, packagePath, shebang, manifest, entries) {
245
+ return {
246
+ packageSha256,
247
+ packagePath,
248
+ shebang,
249
+ manifest,
250
+ entries: entries.map(({ path, sha256, size }) => ({ path, sha256, size })),
251
+ };
252
+ }
253
+ function compareEntries(left, right) {
254
+ return Buffer.compare(Buffer.from(left.path, 'utf8'), Buffer.from(right.path, 'utf8'));
255
+ }
256
+ function withContext(message, error) {
257
+ return new Error(`${message}: ${error instanceof Error ? error.message : String(error)}`);
258
+ }
259
+ function assertRealDirectory(path, label) {
260
+ const stat = (0, node_fs_1.lstatSync)(path);
261
+ if (stat.isSymbolicLink() || !stat.isDirectory()) {
262
+ throw new Error(`${label} must be a real directory.`);
263
+ }
264
+ }
265
+ function readRegularFileNoFollow(path, packagePath, rootRealPath) {
266
+ const parentDirectory = (0, node_path_1.dirname)(path);
267
+ const before = rootRealPath
268
+ ? assertDirectorySnapshot(parentDirectory, rootRealPath, 'Automation script package file parent directory')
269
+ : undefined;
270
+ if (rootRealPath) {
271
+ assertContainedRealPath(path, rootRealPath, 'Automation script package file');
272
+ }
273
+ const fileBefore = rootRealPath
274
+ ? assertPackagePathSnapshot(path, rootRealPath, 'Automation script package file')
275
+ : undefined;
276
+ runBeforeFileReadHook(path);
277
+ const noFollow = typeof node_fs_1.constants.O_NOFOLLOW === 'number' ? node_fs_1.constants.O_NOFOLLOW : 0;
278
+ const fd = (0, node_fs_1.openSync)(path, node_fs_1.constants.O_RDONLY | noFollow);
279
+ try {
280
+ const stat = (0, node_fs_1.fstatSync)(fd);
281
+ if (!stat.isFile()) {
282
+ throw new Error(`Automation script packages can only contain regular files: ${packagePath}`);
283
+ }
284
+ if (fileBefore) {
285
+ assertSameDeviceInode(fileBefore, stat, `Automation script package file changed before open: ${path}`);
286
+ }
287
+ const content = (0, node_fs_1.readFileSync)(fd);
288
+ if (rootRealPath && before) {
289
+ const after = assertDirectorySnapshot(parentDirectory, rootRealPath, 'Automation script package file parent directory');
290
+ assertSameDirectorySnapshot(before, after, `Automation script package file parent directory changed during read: ${parentDirectory}`);
291
+ const fileAfter = assertPackagePathSnapshot(path, rootRealPath, 'Automation script package file');
292
+ if (fileBefore) {
293
+ assertSameDirectorySnapshot(fileBefore, fileAfter, `Automation script package file changed during read: ${path}`);
294
+ }
295
+ assertContainedRealPath(path, rootRealPath, 'Automation script package file');
296
+ }
297
+ return content;
298
+ }
299
+ finally {
300
+ (0, node_fs_1.closeSync)(fd);
301
+ }
302
+ }
303
+ function chmodPackageTreeReadOnly(packagePath) {
304
+ const rootRealPath = (0, node_fs_1.realpathSync)(packagePath);
305
+ const directories = [];
306
+ const visit = (directory) => {
307
+ assertDirectorySnapshot(directory, rootRealPath, 'Automation script package directory');
308
+ directories.push(directory);
309
+ for (const item of readDirectoryEntriesStable(directory, rootRealPath)) {
310
+ const absolutePath = (0, node_path_1.join)(directory, item.name);
311
+ const stat = (0, node_fs_1.lstatSync)(absolutePath);
312
+ if (stat.isSymbolicLink()) {
313
+ throw new Error(`Automation script packages cannot contain symlinks: ${item.name}`);
314
+ }
315
+ if (stat.isDirectory()) {
316
+ assertContainedRealPath(absolutePath, rootRealPath, 'Automation script package directory');
317
+ visit(absolutePath);
318
+ continue;
319
+ }
320
+ if (!stat.isFile()) {
321
+ throw new Error(`Automation script packages can only contain regular files: ${item.name}`);
322
+ }
323
+ assertContainedRealPath(absolutePath, rootRealPath, 'Automation script package file');
324
+ chmodPathWithStableParent(absolutePath, 0o444, rootRealPath, 'Automation script package file');
325
+ }
326
+ };
327
+ visit(packagePath);
328
+ for (const directory of directories.reverse()) {
329
+ chmodPathWithStableParent(directory, 0o555, rootRealPath, 'Automation script package directory');
330
+ }
331
+ }
332
+ function asRecord(value, label) {
333
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
334
+ throw new Error(`${label} must be an object.`);
335
+ }
336
+ return value;
337
+ }
338
+ function requiredString(value, label) {
339
+ if (typeof value !== 'string' || !value.trim())
340
+ throw new Error(`${label} is required.`);
341
+ return value.trim();
342
+ }
343
+ function stringArray(value, label) {
344
+ if (!Array.isArray(value) || value.some((item) => typeof item !== 'string' || !item.trim())) {
345
+ throw new Error(`${label} must be an array of strings.`);
346
+ }
347
+ return value.map((item) => item.trim());
348
+ }
349
+ function positiveSafeInteger(value, label) {
350
+ if (typeof value !== 'number' || !Number.isSafeInteger(value) || value <= 0) {
351
+ throw new Error(`${label} must be a positive integer.`);
352
+ }
353
+ return value;
354
+ }
355
+ function readDirectoryEntriesStable(directory, rootRealPath) {
356
+ const before = assertDirectorySnapshot(directory, rootRealPath, 'Automation script package directory');
357
+ runBeforeDirectoryReadHook(directory);
358
+ const entries = (0, node_fs_1.readdirSync)(directory, { withFileTypes: true });
359
+ const after = assertDirectorySnapshot(directory, rootRealPath, 'Automation script package directory');
360
+ assertSameDirectorySnapshot(before, after, `Automation script package directory changed during traversal: ${directory}`);
361
+ return entries;
362
+ }
363
+ function assertDirectorySnapshot(directory, rootRealPath, label) {
364
+ const stat = (0, node_fs_1.lstatSync)(directory);
365
+ if (stat.isSymbolicLink() || !stat.isDirectory()) {
366
+ throw new Error(`${label} must be a real directory.`);
367
+ }
368
+ const realPath = assertContainedRealPath(directory, rootRealPath, label);
369
+ return {
370
+ dev: stat.dev,
371
+ ino: stat.ino,
372
+ realPath,
373
+ };
374
+ }
375
+ function assertContainedRealPath(path, rootRealPath, label) {
376
+ const realPath = (0, node_fs_1.realpathSync)(path);
377
+ const relativePath = (0, node_path_1.relative)(rootRealPath, realPath);
378
+ if (relativePath && (relativePath === '..' || relativePath.startsWith(`..${node_path_1.sep}`) || (0, node_path_1.isAbsolute)(relativePath))) {
379
+ throw new Error(`${label} must stay within the automation script package root.`);
380
+ }
381
+ return realPath;
382
+ }
383
+ function readPackageFileFromRoot(root, rootRealPath, entryPath) {
384
+ const absolutePath = (0, node_path_1.join)(root, entryPath);
385
+ if (rootRealPath) {
386
+ assertContainedRealPath(absolutePath, rootRealPath, 'Automation script package file');
387
+ }
388
+ return readRegularFileNoFollow(absolutePath, entryPath, rootRealPath);
389
+ }
390
+ function chmodPathWithStableParent(path, mode, rootRealPath, label) {
391
+ const pathRealBefore = assertContainedRealPath(path, rootRealPath, label);
392
+ const protectsRootItself = pathRealBefore === rootRealPath;
393
+ const snapshotPath = protectsRootItself ? path : (0, node_path_1.dirname)(path);
394
+ const snapshotLabel = protectsRootItself ? label : `${label} parent directory`;
395
+ const before = assertDirectorySnapshot(snapshotPath, rootRealPath, snapshotLabel);
396
+ const targetBefore = assertPackagePathSnapshot(path, rootRealPath, label);
397
+ const fd = (0, node_fs_1.openSync)(path, chmodOpenFlags(targetBefore.kind));
398
+ try {
399
+ const fdStat = (0, node_fs_1.fstatSync)(fd);
400
+ if (targetBefore.kind === 'directory' && !fdStat.isDirectory()) {
401
+ throw new Error(`${label} changed before chmod: ${path}`);
402
+ }
403
+ if (targetBefore.kind === 'file' && !fdStat.isFile()) {
404
+ throw new Error(`${label} changed before chmod: ${path}`);
405
+ }
406
+ assertSameDeviceInode(targetBefore, fdStat, `${label} changed before chmod: ${path}`);
407
+ runBeforeChmodHook(path);
408
+ (0, node_fs_1.fchmodSync)(fd, mode);
409
+ }
410
+ catch (error) {
411
+ throw withContext(`${label} could not be chmodded safely`, error);
412
+ }
413
+ finally {
414
+ (0, node_fs_1.closeSync)(fd);
415
+ }
416
+ const after = assertDirectorySnapshot(snapshotPath, rootRealPath, snapshotLabel);
417
+ assertSameDirectorySnapshot(before, after, `${snapshotLabel} changed during chmod: ${snapshotPath}`);
418
+ const targetAfter = assertPackagePathSnapshot(path, rootRealPath, label);
419
+ assertSameDirectorySnapshot(targetBefore, targetAfter, `${label} changed during chmod: ${path}`);
420
+ assertContainedRealPath(path, rootRealPath, label);
421
+ }
422
+ function assertSameDirectorySnapshot(before, after, message) {
423
+ if (before.dev !== after.dev || before.ino !== after.ino || before.realPath !== after.realPath) {
424
+ throw new Error(message);
425
+ }
426
+ }
427
+ function assertSameDeviceInode(before, after, message) {
428
+ if (before.dev !== after.dev || before.ino !== after.ino) {
429
+ throw new Error(message);
430
+ }
431
+ }
432
+ function assertPackagePathSnapshot(path, rootRealPath, label) {
433
+ const stat = (0, node_fs_1.lstatSync)(path);
434
+ if (stat.isSymbolicLink()) {
435
+ throw new Error(`${label} must not be a symlink.`);
436
+ }
437
+ let kind;
438
+ if (stat.isDirectory()) {
439
+ kind = 'directory';
440
+ }
441
+ else if (stat.isFile()) {
442
+ kind = 'file';
443
+ }
444
+ else {
445
+ throw new Error(`${label} must be a regular file or directory.`);
446
+ }
447
+ const realPath = assertContainedRealPath(path, rootRealPath, label);
448
+ return {
449
+ dev: stat.dev,
450
+ ino: stat.ino,
451
+ realPath,
452
+ kind,
453
+ };
454
+ }
455
+ function chmodOpenFlags(kind) {
456
+ const noFollow = typeof node_fs_1.constants.O_NOFOLLOW === 'number' ? node_fs_1.constants.O_NOFOLLOW : 0;
457
+ if (kind === 'file')
458
+ return node_fs_1.constants.O_RDONLY | noFollow;
459
+ if (typeof node_fs_1.constants.O_DIRECTORY !== 'number') {
460
+ throw new Error('Directory chmod requires O_DIRECTORY support.');
461
+ }
462
+ return node_fs_1.constants.O_RDONLY | node_fs_1.constants.O_DIRECTORY | noFollow;
463
+ }
464
+ function runBeforeDirectoryReadHook(directory) {
465
+ const hooks = globalThis.__automationScriptPackageTestHooks;
466
+ hooks?.beforeDirectoryRead?.(directory);
467
+ }
468
+ function runBeforeFileReadHook(path) {
469
+ const hooks = globalThis.__automationScriptPackageTestHooks;
470
+ hooks?.beforeFileRead?.(path);
471
+ }
472
+ function runBeforeChmodHook(path) {
473
+ const hooks = globalThis.__automationScriptPackageTestHooks;
474
+ hooks?.beforeChmod?.(path);
475
+ }