@kafca/agentdock 0.1.58 → 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-BfY1VaHS.js → Badge-Bq2hPVcR.js} +1 -1
  4. package/dist/renderer/assets/{Card-DWA_Tww_.js → Card-BPJNSZce.js} +1 -1
  5. package/dist/renderer/assets/{Config-nHj3g1U5.js → Config-aSFqlI88.js} +2 -2
  6. package/dist/renderer/assets/Dashboard-D3ezFmb7.js +36 -0
  7. package/dist/renderer/assets/{EmptyState-zatDjLj_.js → EmptyState-DK496gBq.js} +1 -1
  8. package/dist/renderer/assets/{HighlightedMarkdown-CsM5n70d.js → HighlightedMarkdown-Ckre5jNv.js} +1 -1
  9. package/dist/renderer/assets/{Input-R1XdFK1K.js → Input-C5C1d0ba.js} +1 -1
  10. package/dist/renderer/assets/{KnowledgeDetail-Co5sxeGd.js → KnowledgeDetail-N8Ey1ikc.js} +2 -2
  11. package/dist/renderer/assets/{KnowledgeHome-DwziPw5m.js → KnowledgeHome-9aF8nJEf.js} +2 -2
  12. package/dist/renderer/assets/{Logs-NwZUtR_G.js → Logs-sgAdwEeS.js} +2 -2
  13. package/dist/renderer/assets/{Modal-CrcIwIFL.js → Modal-BB10hTRc.js} +3 -3
  14. package/dist/renderer/assets/{Page-Dp0obeSw.js → Page-DfC319Ck.js} +1 -1
  15. package/dist/renderer/assets/{Select-6mVQaf84.js → Select-RHJBmNER.js} +1 -1
  16. package/dist/renderer/assets/{ThreadChat-BFzbn3L6.js → ThreadChat-BU1zCdr4.js} +3 -3
  17. package/dist/renderer/assets/{Workspace-DCmo8BMm.js → Workspace-BoZCz7S8.js} +1 -1
  18. package/dist/renderer/assets/{arrow-left-9rjg_Hp4.js → arrow-left-D2V1eZSD.js} +1 -1
  19. package/dist/renderer/assets/{book-open-Bu8GCHnG.js → book-open-DUcBn7Bb.js} +1 -1
  20. package/dist/renderer/assets/{channels-BxVbG-6j.js → channels-DMhDSlf2.js} +2 -2
  21. package/dist/renderer/assets/{chevron-down-BE8uQmWM.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-B7TeF6Es.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-Kl19SqGS.js → knowledge-D1-k56eY.js} +1 -1
  29. package/dist/renderer/assets/{pencil-CPB7SK1m.js → pencil-BhWdv_86.js} +1 -1
  30. package/dist/renderer/assets/{plus-Bt26z-4q.js → plus-BacLMlK3.js} +1 -1
  31. package/dist/renderer/assets/{save-Une7GMxT.js → save-Bpoxbzau.js} +1 -1
  32. package/dist/renderer/assets/{search-DE0agJjJ.js → search-BVJcpdFX.js} +1 -1
  33. package/dist/renderer/assets/{shield-check-De7xaXZh.js → shield-check-C2cw_Ip6.js} +1 -1
  34. package/dist/renderer/assets/{threads-BnsNL5ei.js → threads-DOsBQBg_.js} +1 -1
  35. package/dist/renderer/assets/{trash-2-6iGF59Ac.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 +5 -6
  118. package/dist/renderer/assets/CronList-BwkA_I7v.js +0 -1
  119. package/dist/renderer/assets/Dashboard-D0_A7k02.js +0 -41
  120. package/dist/renderer/assets/MonitorList-CS1uLS5A.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-BkI9POxj.js +0 -167
  124. package/dist/renderer/assets/ja-DtvtBRTW.js +0 -1
  125. package/dist/renderer/assets/play-DGAO4OCu.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,266 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ScriptProtocolRunner = exports.ScriptProtocolError = void 0;
4
+ const node_path_1 = require("node:path");
5
+ const node_fs_1 = require("node:fs");
6
+ const script_package_js_1 = require("./script-package.js");
7
+ const anthropic_sandbox_runner_js_1 = require("./anthropic-sandbox-runner.js");
8
+ const secret_resolver_js_1 = require("./secret-resolver.js");
9
+ const DEFAULT_TIMEOUT_MS = 30_000;
10
+ const MAX_TIMEOUT_MS = 5 * 60_000;
11
+ class ScriptProtocolError extends Error {
12
+ code;
13
+ blockAutomation;
14
+ networkAudit;
15
+ constructor(code, message, blockAutomation = false, networkAudit) {
16
+ super(message);
17
+ this.code = code;
18
+ this.blockAutomation = blockAutomation;
19
+ this.networkAudit = networkAudit;
20
+ this.name = 'ScriptProtocolError';
21
+ }
22
+ }
23
+ exports.ScriptProtocolError = ScriptProtocolError;
24
+ /** Executes only the exact approved script version using protocol v1. */
25
+ class ScriptProtocolRunner {
26
+ options;
27
+ secrets;
28
+ constructor(options) {
29
+ this.options = options;
30
+ this.secrets = options.secretResolver || new secret_resolver_js_1.EnvironmentSecretResolver();
31
+ }
32
+ async run(request) {
33
+ return this.runForStatus(request, 'approved');
34
+ }
35
+ /** Runs the one-shot server-authorized test path without granting enable approval. */
36
+ async runTest(request) {
37
+ return this.runForStatus(request, 'testing');
38
+ }
39
+ async runForStatus(request, expectedStatus) {
40
+ const version = this.requireVersionForStatus(request, expectedStatus);
41
+ let capability;
42
+ try {
43
+ capability = await this.options.sandbox.probe();
44
+ }
45
+ catch {
46
+ throw new ScriptProtocolError('sandbox_unavailable', 'sandbox_unavailable', true);
47
+ }
48
+ if (!capability.available) {
49
+ throw new ScriptProtocolError('sandbox_unavailable', `sandbox_unavailable: ${capability.missing.join(', ') || capability.platform}`, true);
50
+ }
51
+ this.assertVersionFacts(version);
52
+ let interpreterVersion;
53
+ try {
54
+ const interpreterCheck = await this.options.sandbox.run({
55
+ command: `${quoteShell(version.interpreterPath)} --version`,
56
+ interpreterPath: version.interpreterPath,
57
+ cwd: version.packagePath,
58
+ packagePath: version.packagePath,
59
+ network: 'none',
60
+ timeoutMs: 5_000,
61
+ stdoutBytes: 16_384,
62
+ stderrBytes: 16_384,
63
+ });
64
+ if (interpreterCheck.exitCode !== 0 || interpreterCheck.signal || interpreterCheck.outputLimitExceeded) {
65
+ throw new Error('interpreter check failed');
66
+ }
67
+ interpreterVersion = interpreterCheck.stdout.trim();
68
+ }
69
+ catch {
70
+ throw new ScriptProtocolError('interpreter_unavailable', 'Approved interpreter validation is unavailable.', true);
71
+ }
72
+ if (interpreterVersion !== version.interpreterVersion) {
73
+ throw new ScriptProtocolError('interpreter_mismatch', 'Approved interpreter version does not match the installed interpreter.', true);
74
+ }
75
+ let secrets;
76
+ try {
77
+ secrets = await (0, secret_resolver_js_1.resolveDeclaredEnvironmentSecrets)(version.secretRefs, version.env, this.secrets);
78
+ }
79
+ catch (error) {
80
+ if (error instanceof secret_resolver_js_1.SecretUnavailableError)
81
+ throw new ScriptProtocolError('secret_unavailable', error.message);
82
+ throw error;
83
+ }
84
+ // Re-read after checks so a concurrently revoked/replaced record cannot run.
85
+ const current = this.options.getVersion(request.approvedVersionId);
86
+ if (!current || !sameExecutionFacts(version, current, expectedStatus)) {
87
+ throw new ScriptProtocolError('approval_mismatch', 'Approved script facts changed before execution.', true);
88
+ }
89
+ const entrypoint = (this.options.entrypointFor || entrypointFromManifest)(version);
90
+ const stdin = JSON.stringify({
91
+ protocolVersion: 1,
92
+ evaluationId: request.evaluationId,
93
+ triggeredAt: request.triggeredAt,
94
+ config: version.config,
95
+ previousState: request.previousState,
96
+ });
97
+ let result;
98
+ try {
99
+ result = await this.options.sandbox.run({
100
+ command: `${quoteShell(version.interpreterPath)} ${quoteShell((0, node_path_1.join)(version.packagePath, entrypoint))}`,
101
+ interpreterPath: version.interpreterPath,
102
+ cwd: version.packagePath,
103
+ packagePath: version.packagePath,
104
+ network: version.networkMode,
105
+ allowedReadDirs: version.allowedReadDirs,
106
+ env: secrets,
107
+ allowedEnv: version.env,
108
+ stdin,
109
+ timeoutMs: clampTimeout(version.limits.timeoutMs),
110
+ stdoutBytes: version.limits.stdoutBytes,
111
+ stderrBytes: version.limits.stderrBytes,
112
+ });
113
+ }
114
+ catch (error) {
115
+ if (error instanceof anthropic_sandbox_runner_js_1.SandboxUnavailableError || (typeof error === 'object' && error !== null && error.code === 'sandbox_unavailable')) {
116
+ throw new ScriptProtocolError('sandbox_unavailable', 'sandbox_unavailable', true);
117
+ }
118
+ throw error;
119
+ }
120
+ const redacted = redactResult(result.stdout, result.stderr, Object.values(secrets).filter((value) => value !== undefined));
121
+ if (result.outputLimitExceeded) {
122
+ throw new ScriptProtocolError('output_limit', `${result.outputLimitExceeded} output limit exceeded.`, false, result.networkAudit);
123
+ }
124
+ if (result.exitCode !== 0) {
125
+ throw new ScriptProtocolError('script_exit', `Script exited with exit code ${result.exitCode ?? 'unknown'}.`, false, result.networkAudit);
126
+ }
127
+ if (result.signal)
128
+ throw new ScriptProtocolError('script_signal', `Script terminated by ${result.signal}.`, false, result.networkAudit);
129
+ let response;
130
+ let payload;
131
+ let nextState;
132
+ try {
133
+ response = parseResponse(redacted.stdout);
134
+ payload = response.payload === undefined ? undefined : sanitizeRecord(response.payload, Object.values(secrets));
135
+ nextState = response.nextState === undefined ? undefined : sanitizeRecord(response.nextState, Object.values(secrets));
136
+ assertBounded(payload, version.limits.payloadBytes, 'payload');
137
+ assertBounded(nextState, version.limits.stateBytes, 'nextState');
138
+ }
139
+ catch (error) {
140
+ if (error instanceof ScriptProtocolError)
141
+ throw new ScriptProtocolError(error.code, error.message, error.blockAutomation, result.networkAudit);
142
+ throw error;
143
+ }
144
+ return {
145
+ matched: response.matched,
146
+ ...(response.summary === undefined ? {} : { summary: sanitizeText(response.summary, Object.values(secrets)) }),
147
+ ...(payload === undefined ? {} : { payload }),
148
+ ...(nextState === undefined ? {} : { nextState }),
149
+ // JSON response fields were redacted structurally above. Raw diagnostic
150
+ // strings can encode a secret (for example with JSON escapes), so do
151
+ // not persist them for secret-bearing runs.
152
+ stdout: Object.keys(secrets).length === 0 ? redacted.stdout : '',
153
+ stderr: Object.keys(secrets).length === 0 ? redacted.stderr : '',
154
+ exitCode: result.exitCode,
155
+ outputTruncated: false,
156
+ ...(result.networkAudit === undefined ? {} : { networkAudit: result.networkAudit }),
157
+ };
158
+ }
159
+ requireVersionForStatus(request, expectedStatus) {
160
+ const version = this.options.getVersion(request.approvedVersionId);
161
+ if (!version) {
162
+ throw new ScriptProtocolError('script_unavailable', 'Approved script runtime is unavailable.', true);
163
+ }
164
+ if (version.scriptId !== request.scriptId || version.status !== expectedStatus) {
165
+ throw new ScriptProtocolError('approval_mismatch', `The requested script version is not ${expectedStatus}.`, true);
166
+ }
167
+ return version;
168
+ }
169
+ assertVersionFacts(version) {
170
+ if (!/^[a-f0-9]{64}$/i.test(version.packageSha256) || (0, node_path_1.basename)(version.packagePath) !== version.packageSha256) {
171
+ throw new ScriptProtocolError('package_mismatch', 'Approved script package facts do not match.', true);
172
+ }
173
+ try {
174
+ (this.options.verifyPackage || ((candidate) => (0, script_package_js_1.verifyStagedScriptPackage)(candidate.packagePath, candidate.packageSha256)))(version);
175
+ }
176
+ catch {
177
+ throw new ScriptProtocolError('package_mismatch', 'Approved script package integrity verification failed.', true);
178
+ }
179
+ }
180
+ }
181
+ exports.ScriptProtocolRunner = ScriptProtocolRunner;
182
+ function sameExecutionFacts(left, right, expectedStatus) {
183
+ return left.id === right.id && left.scriptId === right.scriptId && left.status === expectedStatus && right.status === expectedStatus
184
+ && left.packageSha256 === right.packageSha256 && left.packagePath === right.packagePath
185
+ && left.interpreterPath === right.interpreterPath && left.interpreterVersion === right.interpreterVersion;
186
+ }
187
+ function entrypointFromManifest(version) {
188
+ let parsed;
189
+ try {
190
+ parsed = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(version.packagePath, 'manifest.json'), 'utf8'));
191
+ }
192
+ catch {
193
+ throw new ScriptProtocolError('package_mismatch', 'Approved script manifest is unavailable.', true);
194
+ }
195
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed) || typeof parsed.entrypoint !== 'string') {
196
+ throw new ScriptProtocolError('protocol_invalid', 'Approved script manifest has no valid entrypoint.', true);
197
+ }
198
+ const entrypoint = parsed.entrypoint;
199
+ if (!entrypoint || entrypoint.startsWith('/') || entrypoint.split('/').includes('..')) {
200
+ throw new ScriptProtocolError('protocol_invalid', 'Approved script manifest entrypoint is invalid.', true);
201
+ }
202
+ return entrypoint;
203
+ }
204
+ function parseResponse(stdout) {
205
+ const trimmed = stdout.trim();
206
+ if (!trimmed)
207
+ throw new ScriptProtocolError('protocol_invalid', 'Script stdout must contain exactly one JSON response.');
208
+ let parsed;
209
+ try {
210
+ parsed = JSON.parse(trimmed);
211
+ }
212
+ catch {
213
+ throw new ScriptProtocolError('protocol_invalid', 'Script stdout must contain exactly one JSON response.');
214
+ }
215
+ if (!isRecord(parsed) || parsed.protocolVersion !== 1 || typeof parsed.matched !== 'boolean') {
216
+ throw new ScriptProtocolError('protocol_invalid', 'Script response must be protocolVersion 1 with a boolean matched field.');
217
+ }
218
+ for (const key of Object.keys(parsed)) {
219
+ if (!['protocolVersion', 'matched', 'summary', 'payload', 'nextState'].includes(key)) {
220
+ throw new ScriptProtocolError('protocol_invalid', `Script response includes unsupported field ${key}.`);
221
+ }
222
+ }
223
+ if (parsed.summary !== undefined && typeof parsed.summary !== 'string')
224
+ throw new ScriptProtocolError('protocol_invalid', 'Script response summary must be a string.');
225
+ if (parsed.payload !== undefined && !isRecord(parsed.payload))
226
+ throw new ScriptProtocolError('protocol_invalid', 'Script response payload must be an object.');
227
+ if (parsed.nextState !== undefined && !isRecord(parsed.nextState))
228
+ throw new ScriptProtocolError('protocol_invalid', 'Script response nextState must be an object.');
229
+ return { matched: parsed.matched, ...(parsed.summary === undefined ? {} : { summary: parsed.summary }), ...(parsed.payload === undefined ? {} : { payload: parsed.payload }), ...(parsed.nextState === undefined ? {} : { nextState: parsed.nextState }) };
230
+ }
231
+ function isRecord(value) {
232
+ return !!value && typeof value === 'object' && !Array.isArray(value) && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
233
+ }
234
+ function assertBounded(value, limit, name) {
235
+ if (value !== undefined && Buffer.byteLength(JSON.stringify(value), 'utf8') > limit) {
236
+ throw new ScriptProtocolError('protocol_limit', `${name} exceeds its approved size limit.`);
237
+ }
238
+ }
239
+ function clampTimeout(value) {
240
+ if (!value || !Number.isSafeInteger(value) || value < 1)
241
+ return DEFAULT_TIMEOUT_MS;
242
+ return Math.min(value, MAX_TIMEOUT_MS);
243
+ }
244
+ function quoteShell(value) { return `'${value.replace(/'/g, "'\\''")}'`; }
245
+ function redactResult(stdout, stderr, values) {
246
+ return { stdout: sanitizeText(stdout, values), stderr: sanitizeText(stderr, values) };
247
+ }
248
+ function sanitizeText(value, values) {
249
+ let result = value.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g, '');
250
+ for (const secret of values)
251
+ if (secret)
252
+ result = result.split(secret).join('[REDACTED]');
253
+ return result;
254
+ }
255
+ function sanitizeRecord(value, secrets) {
256
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, sanitizeJsonValue(item, secrets)]));
257
+ }
258
+ function sanitizeJsonValue(value, secrets) {
259
+ if (typeof value === 'string')
260
+ return sanitizeText(value, secrets);
261
+ if (Array.isArray(value))
262
+ return value.map((item) => sanitizeJsonValue(item, secrets));
263
+ if (isRecord(value))
264
+ return sanitizeRecord(value, secrets);
265
+ return value;
266
+ }
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SecretUnavailableError = exports.EnvironmentSecretResolver = void 0;
4
+ exports.resolveDeclaredEnvironmentSecrets = resolveDeclaredEnvironmentSecrets;
5
+ class EnvironmentSecretResolver {
6
+ get(name) {
7
+ return process.env[name];
8
+ }
9
+ }
10
+ exports.EnvironmentSecretResolver = EnvironmentSecretResolver;
11
+ class SecretUnavailableError extends Error {
12
+ name;
13
+ code = 'secret_unavailable';
14
+ constructor(name) {
15
+ super(`secret_unavailable: ${name}`);
16
+ this.name = name;
17
+ this.name = 'SecretUnavailableError';
18
+ }
19
+ }
20
+ exports.SecretUnavailableError = SecretUnavailableError;
21
+ async function resolveDeclaredEnvironmentSecrets(secretRefs, envNames, resolver) {
22
+ const declared = new Set(envNames);
23
+ const result = {};
24
+ for (const ref of secretRefs) {
25
+ if (!ref.startsWith('env://'))
26
+ throw new SecretUnavailableError('unsupported_reference');
27
+ const name = ref.slice('env://'.length);
28
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name) || !declared.has(name)) {
29
+ throw new SecretUnavailableError(name || 'invalid_reference');
30
+ }
31
+ const value = await resolver.get(name);
32
+ if (value === undefined)
33
+ throw new SecretUnavailableError(name);
34
+ result[name] = value;
35
+ }
36
+ return result;
37
+ }
@@ -41,8 +41,7 @@ async function runWeixinInboundPoller(input) {
41
41
  while (!signal.aborted) {
42
42
  try {
43
43
  const resp = await (0, transport_js_1.getWeixinUpdates)(binding, buf, signal);
44
- const isApiError = (resp.ret !== undefined && resp.ret !== 0) || (resp.errcode !== undefined && resp.errcode !== 0);
45
- if (isApiError) {
44
+ if ((0, transport_js_1.isWeixinApiError)(resp)) {
46
45
  consecutiveFailures += 1;
47
46
  const state = input.getRuntimeState();
48
47
  const retryDelayMs = computeRetryDelay(consecutiveFailures);
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.UPLOAD_MEDIA_TYPE_FILE = exports.FILE_ITEM_TYPE = exports.VOICE_ITEM_TYPE = exports.IMAGE_ITEM_TYPE = exports.TEXT_ITEM_TYPE = exports.WEIXIN_ILINK_APP_CLIENT_VERSION = exports.WEIXIN_ILINK_APP_ID = exports.WEIXIN_CHANNEL_VERSION = exports.API_TIMEOUT_MS = void 0;
7
7
  exports.createWechatUin = createWechatUin;
8
8
  exports.createIlinkHeaders = createIlinkHeaders;
9
+ exports.applyWeixinAuthHeaders = applyWeixinAuthHeaders;
9
10
  exports.weixinApiPost = weixinApiPost;
10
11
  exports.weixinApiGet = weixinApiGet;
11
12
  exports.getWeixinUpdates = getWeixinUpdates;
@@ -33,6 +34,12 @@ function createIlinkHeaders() {
33
34
  'iLink-App-ClientVersion': exports.WEIXIN_ILINK_APP_CLIENT_VERSION,
34
35
  };
35
36
  }
37
+ function applyWeixinAuthHeaders(headers, binding) {
38
+ if (binding.token) {
39
+ headers.AuthorizationType = 'ilink_bot_token';
40
+ headers.Authorization = `Bearer ${binding.token}`;
41
+ }
42
+ }
36
43
  async function weixinApiPost(binding, endpoint, bodyObj, timeoutMs, signal) {
37
44
  const url = `${binding.baseUrl.replace(/\/$/, '')}/${endpoint}`;
38
45
  const body = JSON.stringify(bodyObj);
@@ -47,10 +54,7 @@ async function weixinApiPost(binding, endpoint, bodyObj, timeoutMs, signal) {
47
54
  'X-WECHAT-UIN': createWechatUin(),
48
55
  ...createIlinkHeaders(),
49
56
  };
50
- if (binding.token) {
51
- headers.AuthorizationType = 'ilink_bot_token';
52
- headers.Authorization = `Bearer ${binding.token}`;
53
- }
57
+ applyWeixinAuthHeaders(headers, binding);
54
58
  const res = await fetch(url, {
55
59
  method: 'POST',
56
60
  headers,
@@ -72,10 +76,7 @@ async function weixinApiGet(binding, endpoint, timeoutMs = exports.API_TIMEOUT_M
72
76
  const timer = setTimeout(() => controller.abort(), timeoutMs);
73
77
  try {
74
78
  const headers = createIlinkHeaders();
75
- if (binding.token) {
76
- headers.AuthorizationType = 'ilink_bot_token';
77
- headers.Authorization = `Bearer ${binding.token}`;
78
- }
79
+ applyWeixinAuthHeaders(headers, binding);
79
80
  const res = await fetch(url, {
80
81
  method: 'GET',
81
82
  headers,
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.runCli = runCli;
7
7
  const node_process_1 = __importDefault(require("node:process"));
8
+ const node_fs_1 = require("node:fs");
8
9
  const superai_contracts_1 = require("../../../../packages/contracts/src/index.js");
9
10
  const job_id_js_1 = require("../scheduler/job-id.js");
10
11
  const scheduled_job_route_js_1 = require("../scheduler/scheduled-job-route.js");
@@ -46,6 +47,37 @@ async function runCli(argv, env = node_process_1.default.env, io = node_process_
46
47
  return 2;
47
48
  }
48
49
  }
50
+ if (domain === 'automation') {
51
+ switch (action) {
52
+ case 'add': return await handleAutomationAdd(flags, env, io, json);
53
+ case 'list': return await handleAutomationList(flags, env, io, json);
54
+ case 'info': return await handleAutomationInfo(maybeId, flags, env, io, json);
55
+ case 'edit': return await handleAutomationEdit(maybeId, flags, env, io, json);
56
+ case 'del':
57
+ case 'delete': return await handleAutomationDelete(maybeId, flags, env, io, json);
58
+ case 'check': return await handleAutomationCheck(maybeId, flags, env, io, json);
59
+ default:
60
+ printUsage(io.stderr);
61
+ return 2;
62
+ }
63
+ }
64
+ if (domain === 'script') {
65
+ switch (action) {
66
+ case 'list': return await handleScriptList(flags, env, io, json);
67
+ case 'create': return await handleScriptCreate(flags, env, io, json);
68
+ case 'stage': return await handleScriptStage(flags, env, io, json);
69
+ case 'status': return await handleScriptStatus(maybeId, flags, env, io, json);
70
+ case 'test-approval': return await handleScriptTransition('test-approval', maybeId, flags, env, io, json);
71
+ case 'test': return await handleScriptTest(maybeId, flags, env, io, json);
72
+ case 'enable-approval': return await handleScriptTransition('enable-approval', maybeId, flags, env, io, json);
73
+ case 'approve': return await handleScriptApprovalDecision('approve', maybeId, flags, env, io, json);
74
+ case 'reject': return await handleScriptApprovalDecision('reject', maybeId, flags, env, io, json);
75
+ case 'revoke': return await handleScriptTransition('revoke', maybeId, flags, env, io, json);
76
+ default:
77
+ printUsage(io.stderr);
78
+ return 2;
79
+ }
80
+ }
49
81
  if (domain !== 'scheduler') {
50
82
  printUsage(io.stderr);
51
83
  return 2;
@@ -74,6 +106,198 @@ async function runCli(argv, env = node_process_1.default.env, io = node_process_
74
106
  return 1;
75
107
  }
76
108
  }
109
+ async function handleAutomationAdd(flags, env, io, json) {
110
+ const context = requireWorkspaceContext(flags, env, 'automation add');
111
+ const scriptId = getRequiredFlag(flags, 'script-id');
112
+ const versionId = getRequiredFlag(flags, 'script-version');
113
+ const version = await request(context.baseUrl, 'GET', scriptVersionPath(versionId, context));
114
+ if (version.status !== 'approved')
115
+ throw new Error(`automation add requires an approved script version, got ${version.status}.`);
116
+ if (version.scriptId !== scriptId)
117
+ throw new Error('automation add script id does not match the approved script version.');
118
+ const intervalMs = (0, monitor_cli_parsers_js_1.parseDurationMs)(getFlag(flags, 'interval') || '1m');
119
+ const hasChannelRoute = Boolean(context.platform && context.chatId);
120
+ const platform = hasChannelRoute ? context.platform : 'local';
121
+ const route = hasChannelRoute
122
+ ? { type: context.routeType || 'channel.chat', channelId: context.chatId, ...(context.platformInstanceId ? { instanceId: context.platformInstanceId } : {}), ...(context.platformUserId ? { participantId: context.platformUserId } : {}), ...(context.threadId ? { threadId: context.threadId } : {}) }
123
+ : { type: 'local.thread', channelId: context.workspaceId, ...(context.threadId ? { threadId: context.threadId } : {}) };
124
+ const automation = await request(context.baseUrl, 'POST', '/automations', {
125
+ workspaceId: context.workspaceId,
126
+ title: getRequiredFlag(flags, 'title'),
127
+ enabled: getBooleanFlag(flags, 'enabled', true),
128
+ activation: { kind: 'interval', intervalMs },
129
+ condition: { kind: 'approved-script', scriptId, approvedVersionId: versionId, edge: getFlag(flags, 'edge') || 'rising' },
130
+ action: { kind: 'agent-prompt', promptTemplate: getRequiredFlag(flags, 'message'), executionMode: getFlag(flags, 'execution-mode') || 'side-thread' },
131
+ delivery: { platform, route },
132
+ policies: { concurrency: 'skip-if-running', cooldownMs: getFlag(flags, 'cooldown') ? (0, monitor_cli_parsers_js_1.parseDurationMs)(getFlag(flags, 'cooldown')) : 0 },
133
+ });
134
+ print(json, io.stdout, automation, `Created automation ${automation.id || ''}`.trim());
135
+ return 0;
136
+ }
137
+ async function handleAutomationList(flags, env, io, json) {
138
+ const context = requireWorkspaceContext(flags, env, 'automation list');
139
+ const response = await request(context.baseUrl, 'GET', `/automations?workspace_id=${encodeURIComponent(context.workspaceId)}`);
140
+ print(json, io.stdout, response, response.automations.length ? response.automations.map((item) => JSON.stringify(item)).join('\n') : 'No automations.');
141
+ return 0;
142
+ }
143
+ async function handleAutomationInfo(id, flags, env, io, json) {
144
+ if (!id)
145
+ throw new Error('automation info requires an automation id.');
146
+ const context = requireWorkspaceContext(flags, env, 'automation info');
147
+ const automation = await request(context.baseUrl, 'GET', `/automations/${encodeURIComponent(id)}?workspace_id=${encodeURIComponent(context.workspaceId)}`);
148
+ print(json, io.stdout, automation, JSON.stringify(automation, null, 2));
149
+ return 0;
150
+ }
151
+ async function handleAutomationEdit(id, flags, env, io, json) {
152
+ if (!id)
153
+ throw new Error('automation edit requires an automation id.');
154
+ const context = requireWorkspaceContext(flags, env, 'automation edit');
155
+ const body = {};
156
+ const title = getFlag(flags, 'title');
157
+ const enabled = getOptionalBooleanFlag(flags, 'enabled');
158
+ if (title)
159
+ body.title = title;
160
+ if (enabled !== undefined)
161
+ body.enabled = enabled;
162
+ if (!Object.keys(body).length)
163
+ throw new Error('automation edit requires --title or --enabled.');
164
+ const automation = await request(context.baseUrl, 'PATCH', `/automations/${encodeURIComponent(id)}?workspace_id=${encodeURIComponent(context.workspaceId)}`, body);
165
+ print(json, io.stdout, automation, `Updated automation ${id}`);
166
+ return 0;
167
+ }
168
+ async function handleAutomationDelete(id, flags, env, io, json) {
169
+ if (!id)
170
+ throw new Error('automation del requires an automation id.');
171
+ const context = requireWorkspaceContext(flags, env, 'automation del');
172
+ const result = await request(context.baseUrl, 'DELETE', `/automations/${encodeURIComponent(id)}?workspace_id=${encodeURIComponent(context.workspaceId)}`);
173
+ print(json, io.stdout, result, `Deleted automation ${id}`);
174
+ return 0;
175
+ }
176
+ async function handleAutomationCheck(id, flags, env, io, json) {
177
+ if (!id)
178
+ throw new Error('automation check requires an automation id.');
179
+ const context = requireWorkspaceContext(flags, env, 'automation check');
180
+ const result = await request(context.baseUrl, 'POST', `/automations/${encodeURIComponent(id)}/check?workspace_id=${encodeURIComponent(context.workspaceId)}`);
181
+ print(json, io.stdout, result, `Checked automation ${id}`);
182
+ return 0;
183
+ }
184
+ async function handleScriptList(flags, env, io, json) {
185
+ const context = requireWorkspaceContext(flags, env, 'script list');
186
+ const result = await request(context.baseUrl, 'GET', `/automation-scripts?workspace_id=${encodeURIComponent(context.workspaceId)}`);
187
+ print(json, io.stdout, result, JSON.stringify(result, null, 2));
188
+ return 0;
189
+ }
190
+ async function handleScriptCreate(flags, env, io, json) {
191
+ const context = requireWorkspaceContext(flags, env, 'script create');
192
+ const result = await request(context.baseUrl, 'POST', '/automation-scripts', { workspaceId: context.workspaceId, title: getRequiredFlag(flags, 'title'), ...(getFlag(flags, 'desc') ? { description: getFlag(flags, 'desc') } : {}) });
193
+ print(json, io.stdout, result, `Created script ${result.id || ''}`.trim());
194
+ return 0;
195
+ }
196
+ async function handleScriptStage(flags, env, io, json) {
197
+ const context = requireWorkspaceContext(flags, env, 'script stage');
198
+ const scriptId = getRequiredFlag(flags, 'script');
199
+ const sourceJson = getFlag(flags, 'source-json');
200
+ const sourceFile = getFlag(flags, 'source-file');
201
+ if (Boolean(sourceJson) === Boolean(sourceFile))
202
+ throw new Error('script stage requires exactly one of --source-json or --source-file.');
203
+ let source;
204
+ if (sourceFile) {
205
+ source = readStableSourceFile(sourceFile);
206
+ }
207
+ else {
208
+ source = sourceJson;
209
+ }
210
+ if (Buffer.byteLength(source, 'utf8') > 1_200_000)
211
+ throw new Error('script stage source bundle exceeds 1,200,000 bytes.');
212
+ let files;
213
+ try {
214
+ files = JSON.parse(source);
215
+ }
216
+ catch {
217
+ throw new Error('script stage --source-json must be a JSON array.');
218
+ }
219
+ if (!Array.isArray(files) || files.some((file) => !file || typeof file !== 'object' || Array.isArray(file) || Object.keys(file).some((key) => key !== 'path' && key !== 'content'))) {
220
+ throw new Error('script stage accepts only source file path and content fields.');
221
+ }
222
+ const result = await request(context.baseUrl, 'POST', `/automation-scripts/${encodeURIComponent(scriptId)}/versions?workspace_id=${encodeURIComponent(context.workspaceId)}`, { files });
223
+ print(json, io.stdout, result, `Staged script version ${result.id || ''}`.trim());
224
+ return 0;
225
+ }
226
+ function readStableSourceFile(path) {
227
+ if (!node_fs_1.constants.O_NOFOLLOW)
228
+ throw new Error('script stage --source-file requires O_NOFOLLOW support.');
229
+ const fd = (0, node_fs_1.openSync)(path, node_fs_1.constants.O_RDONLY | node_fs_1.constants.O_NOFOLLOW);
230
+ try {
231
+ const before = (0, node_fs_1.fstatSync)(fd);
232
+ if (!before.isFile())
233
+ throw new Error('script stage --source-file must be a regular file.');
234
+ if (before.size > 1_200_000)
235
+ throw new Error('script stage source bundle exceeds 1,200,000 bytes.');
236
+ const buffer = Buffer.allocUnsafe(before.size);
237
+ let offset = 0;
238
+ while (offset < buffer.length) {
239
+ const read = (0, node_fs_1.readSync)(fd, buffer, offset, buffer.length - offset, null);
240
+ if (!read)
241
+ throw new Error('script stage source file changed while reading.');
242
+ offset += read;
243
+ }
244
+ const after = (0, node_fs_1.fstatSync)(fd);
245
+ if (before.dev !== after.dev || before.ino !== after.ino || before.size !== after.size || before.mtimeMs !== after.mtimeMs || before.ctimeMs !== after.ctimeMs) {
246
+ throw new Error('script stage source file changed while reading.');
247
+ }
248
+ return new TextDecoder('utf-8', { fatal: true }).decode(buffer);
249
+ }
250
+ finally {
251
+ (0, node_fs_1.closeSync)(fd);
252
+ }
253
+ }
254
+ async function handleScriptStatus(versionId, flags, env, io, json) {
255
+ if (!versionId)
256
+ throw new Error('script status requires a version id.');
257
+ const context = requireWorkspaceContext(flags, env, 'script status');
258
+ const version = await request(context.baseUrl, 'GET', scriptVersionPath(versionId, context));
259
+ print(json, io.stdout, version, JSON.stringify(version, null, 2));
260
+ return 0;
261
+ }
262
+ async function handleScriptTransition(action, versionId, flags, env, io, json) {
263
+ if (!versionId)
264
+ throw new Error(`script ${action} requires a version id.`);
265
+ const context = requireWorkspaceContext(flags, env, `script ${action}`);
266
+ const result = await request(context.baseUrl, 'POST', scriptVersionActionPath(versionId, action, context), { actor: getRequiredFlag(flags, 'actor') });
267
+ print(json, io.stdout, result, `Requested script ${action} for ${versionId}`);
268
+ return 0;
269
+ }
270
+ async function handleScriptTest(versionId, flags, env, io, json) {
271
+ if (!versionId)
272
+ throw new Error('script test requires a version id.');
273
+ const context = requireWorkspaceContext(flags, env, 'script test');
274
+ const version = await request(context.baseUrl, 'GET', scriptVersionPath(versionId, context));
275
+ if (version.status !== 'test_authorized')
276
+ throw new Error(`script test requires a live unconsumed test authorization, got ${version.status}.`);
277
+ const result = await request(context.baseUrl, 'POST', scriptVersionActionPath(versionId, 'test', context), { actor: getRequiredFlag(flags, 'actor') });
278
+ print(json, io.stdout, result, `Ran script test for ${versionId}`);
279
+ return 0;
280
+ }
281
+ async function handleScriptApprovalDecision(action, versionId, flags, env, io, json) {
282
+ if (!versionId)
283
+ throw new Error(`script ${action} requires a version id.`);
284
+ const context = requireWorkspaceContext(flags, env, `script ${action}`);
285
+ const result = await request(context.baseUrl, 'POST', scriptVersionActionPath(versionId, action, context), { approvalId: getRequiredFlag(flags, 'approval'), actor: getRequiredFlag(flags, 'actor') });
286
+ print(json, io.stdout, result, `Applied script ${action} for ${versionId}`);
287
+ return 0;
288
+ }
289
+ function scriptVersionPath(versionId, context) {
290
+ return `/automation-scripts/versions/${encodeURIComponent(versionId)}?workspace_id=${encodeURIComponent(context.workspaceId)}`;
291
+ }
292
+ function scriptVersionActionPath(versionId, action, context) {
293
+ return `/automation-scripts/versions/${encodeURIComponent(versionId)}/${action}?workspace_id=${encodeURIComponent(context.workspaceId)}`;
294
+ }
295
+ function requireWorkspaceContext(flags, env, operation) {
296
+ const context = resolveContext(flags, env);
297
+ if (!context.workspaceId)
298
+ throw new Error(`${operation} requires a workspace context. Set LOCAL_AI_WORKSPACE_ID or pass --workspace.`);
299
+ return context;
300
+ }
77
301
  async function handleMonitorAdd(flags, env, io, json) {
78
302
  const context = resolveContext(flags, env);
79
303
  if (!context.workspaceId) {
@@ -444,6 +668,11 @@ function printUsage(output) {
444
668
  ' lac monitor edit <monitor-id> [--title "<title>"] [--condition "<expr>"] [--message "<text>"] [--enabled true|false] [--cooldown 15m] [--execution-mode same-thread|side-thread] [--json]',
445
669
  ' lac monitor del <monitor-id> [--json]',
446
670
  ' lac monitor run <monitor-id> [--json]',
671
+ ' lac automation add --title "<title>" --script-id <script-id> --script-version <approved-version-id> --interval <duration> --message "<prompt>" [--json]',
672
+ ' lac automation list|info <id>|edit <id> [--title "<title>"] [--enabled true|false]|del <id>|check <id> [--json]',
673
+ ' lac script list|create --title "<title>"|stage --script <script-id> --source-json "<files-json>"|status <version-id> [--json]',
674
+ ' lac script test-approval|test|enable-approval|revoke <version-id> --actor <actor> [--json]',
675
+ ' lac script approve|reject <version-id> --approval <approval-id> --actor <actor> [--json]',
447
676
  ' lac channel send-file --path "<file>" [--target <chat-or-user-id>] [--workspace <id>] [--workspace-path <path>] [--platform lark] [--name <filename>] [--json]',
448
677
  ].join('\n') + '\n');
449
678
  }