@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.
- package/README.md +18 -0
- package/dist/renderer/assets/AutomationList-BgJgAdVO.js +16 -0
- package/dist/renderer/assets/{Badge-BLI4LHse.js → Badge-Bq2hPVcR.js} +1 -1
- package/dist/renderer/assets/{Card-DJeLDdL3.js → Card-BPJNSZce.js} +1 -1
- package/dist/renderer/assets/{Config-D1awc81A.js → Config-aSFqlI88.js} +2 -2
- package/dist/renderer/assets/Dashboard-D3ezFmb7.js +36 -0
- package/dist/renderer/assets/{EmptyState-CkI1RwPX.js → EmptyState-DK496gBq.js} +1 -1
- package/dist/renderer/assets/{HighlightedMarkdown-BUhDlhgW.js → HighlightedMarkdown-Ckre5jNv.js} +1 -1
- package/dist/renderer/assets/{Input-DHYrn-Ne.js → Input-C5C1d0ba.js} +1 -1
- package/dist/renderer/assets/{KnowledgeDetail-Bij-Xo90.js → KnowledgeDetail-N8Ey1ikc.js} +2 -2
- package/dist/renderer/assets/{KnowledgeHome-DmPZ2IlI.js → KnowledgeHome-9aF8nJEf.js} +2 -2
- package/dist/renderer/assets/{Logs-ISY99heA.js → Logs-sgAdwEeS.js} +2 -2
- package/dist/renderer/assets/{Modal-DAQfz0ot.js → Modal-BB10hTRc.js} +3 -3
- package/dist/renderer/assets/{Page-Dw43UYDT.js → Page-DfC319Ck.js} +1 -1
- package/dist/renderer/assets/{Select-DzmN5OGQ.js → Select-RHJBmNER.js} +1 -1
- package/dist/renderer/assets/{ThreadChat-B5CWqqMK.js → ThreadChat-BU1zCdr4.js} +3 -3
- package/dist/renderer/assets/{Workspace-C5g9LBj9.js → Workspace-BoZCz7S8.js} +1 -1
- package/dist/renderer/assets/{arrow-left-BLXh56Ip.js → arrow-left-D2V1eZSD.js} +1 -1
- package/dist/renderer/assets/{book-open-DhPGbc-7.js → book-open-DUcBn7Bb.js} +1 -1
- package/dist/renderer/assets/{channels-DfQrmuYO.js → channels-DMhDSlf2.js} +2 -2
- package/dist/renderer/assets/{chevron-down-CEFtpPDc.js → chevron-down-Ca2FQDB7.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-CYhDUAR_.js → index-BlnvlekJ.js} +1 -1
- package/dist/renderer/assets/index-DRJ5wgw_.js +162 -0
- package/dist/renderer/assets/ja-CDWPWqEW.js +1 -0
- package/dist/renderer/assets/{knowledge-uHRQ80sR.js → knowledge-D1-k56eY.js} +1 -1
- package/dist/renderer/assets/{pencil-BD2Y31YC.js → pencil-BhWdv_86.js} +1 -1
- package/dist/renderer/assets/{plus-DDcldp_c.js → plus-BacLMlK3.js} +1 -1
- package/dist/renderer/assets/{save-CMW_cZI9.js → save-Bpoxbzau.js} +1 -1
- package/dist/renderer/assets/{search-BM-0CvJr.js → search-BVJcpdFX.js} +1 -1
- package/dist/renderer/assets/{shield-check-DaMB0FKG.js → shield-check-C2cw_Ip6.js} +1 -1
- package/dist/renderer/assets/{threads-DZgt9tWj.js → threads-DOsBQBg_.js} +1 -1
- package/dist/renderer/assets/{trash-2-DTXVbC5y.js → trash-2-Bs3rEm3g.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 +333 -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,463 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
7
|
+
const node_fs_1 = require("node:fs");
|
|
8
|
+
const node_os_1 = require("node:os");
|
|
9
|
+
const node_path_1 = require("node:path");
|
|
10
|
+
const node_test_1 = __importDefault(require("node:test"));
|
|
11
|
+
const local_core_acp_store_js_1 = require("../../services/local-ai-core/src/acp/local-core-acp-store.js");
|
|
12
|
+
const automation_monitor_service_js_1 = require("../../services/local-ai-core/src/automation/automation-monitor-service.js");
|
|
13
|
+
const automation_service_js_1 = require("../../services/local-ai-core/src/automation/automation-service.js");
|
|
14
|
+
const legacy_automation_mappers_js_1 = require("../../services/local-ai-core/src/automation/legacy-automation-mappers.js");
|
|
15
|
+
const event_bus_js_1 = require("../../services/local-ai-core/src/kernel/event-bus.js");
|
|
16
|
+
const scheduled_job_application_service_js_1 = require("../../services/local-ai-core/src/scheduler/scheduled-job-application-service.js");
|
|
17
|
+
const scheduler_service_js_1 = require("../../services/local-ai-core/src/scheduler/scheduler-service.js");
|
|
18
|
+
function fixture(executeAction, conditionEvaluator) {
|
|
19
|
+
const path = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'automation-compatibility-'));
|
|
20
|
+
const store = new local_core_acp_store_js_1.LocalCoreAcpStore(path);
|
|
21
|
+
const eventBus = new event_bus_js_1.LocalCoreEventBus();
|
|
22
|
+
const actions = [];
|
|
23
|
+
const automations = new automation_service_js_1.AutomationService({
|
|
24
|
+
store,
|
|
25
|
+
eventBus,
|
|
26
|
+
actionExecutor: {
|
|
27
|
+
async execute({ automation, promptVariables }) {
|
|
28
|
+
actions.push({ automationId: automation.id, promptVariables });
|
|
29
|
+
if (executeAction)
|
|
30
|
+
return executeAction();
|
|
31
|
+
return { threadId: 'thread-1', acpRunId: 'run-1', deliveryStatus: 'succeeded' };
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
ownershipPolicy: { executes: () => true },
|
|
35
|
+
conditionEvaluator,
|
|
36
|
+
});
|
|
37
|
+
const scheduler = new scheduler_service_js_1.SchedulerService({ store, automations, triggers: [], executors: [], eventBus });
|
|
38
|
+
const jobs = new scheduled_job_application_service_js_1.ScheduledJobApplicationService({ store, scheduler, automations, eventBus });
|
|
39
|
+
return {
|
|
40
|
+
path, store, eventBus, automations, scheduler, jobs, actions,
|
|
41
|
+
close() { store.close(); (0, node_fs_1.rmSync)(path, { recursive: true, force: true }); },
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
(0, node_test_1.default)('legacy scheduler mappings validate origin and preserve the public shape', () => {
|
|
45
|
+
const input = (0, legacy_automation_mappers_js_1.scheduledJobToAutomationInput)({
|
|
46
|
+
workspaceId: 'workspace-1',
|
|
47
|
+
platform: 'lark:tenant-1',
|
|
48
|
+
route: { type: 'lark.chat', channelId: 'chat-1' },
|
|
49
|
+
executionMode: 'same-thread',
|
|
50
|
+
triggerType: 'cron',
|
|
51
|
+
cronExpr: '0 9 * * *',
|
|
52
|
+
promptTemplate: 'daily report',
|
|
53
|
+
description: 'Daily report',
|
|
54
|
+
enabled: true,
|
|
55
|
+
});
|
|
56
|
+
strict_1.default.deepEqual(input.activation, { kind: 'cron', expression: '0 9 * * *', timezone: 'UTC' });
|
|
57
|
+
strict_1.default.deepEqual(input.condition, { kind: 'always' });
|
|
58
|
+
strict_1.default.equal(input.originKind, 'scheduled-job');
|
|
59
|
+
strict_1.default.equal(input.action.executionMode, 'same-thread');
|
|
60
|
+
strict_1.default.deepEqual(input.legacyMetadata, { scheduledDescription: 'Daily report' });
|
|
61
|
+
strict_1.default.equal((0, legacy_automation_mappers_js_1.scheduledJobToAutomationInput)({
|
|
62
|
+
workspaceId: 'workspace-1', platform: 'local', route: { type: 'local.thread', channelId: 'workspace-1' },
|
|
63
|
+
triggerType: 'cron', cronExpr: '* * * * *', promptTemplate: 'hello', executionMode: '',
|
|
64
|
+
}).action.executionMode, 'same-thread');
|
|
65
|
+
strict_1.default.throws(() => (0, legacy_automation_mappers_js_1.scheduledJobToAutomationInput)({
|
|
66
|
+
workspaceId: 'workspace-1', platform: 'local', route: { type: 'local.thread', channelId: 'workspace-1' },
|
|
67
|
+
triggerType: 'cron', cronExpr: '* * * * *', promptTemplate: 'hello', executionMode: 'bogus',
|
|
68
|
+
}), /same-thread or side-thread/);
|
|
69
|
+
strict_1.default.deepEqual((0, legacy_automation_mappers_js_1.scheduledJobToAutomationInput)({
|
|
70
|
+
workspaceId: 'workspace-1', platform: 'local', route: { type: 'local.thread', channelId: 'workspace-1' },
|
|
71
|
+
triggerType: 'One_Time', runAt: '2026-07-06T00:00:00.000Z', promptTemplate: 'hello', description: ' ',
|
|
72
|
+
}).activation, { kind: 'once', runAt: '2026-07-06T00:00:00.000Z' });
|
|
73
|
+
strict_1.default.equal((0, legacy_automation_mappers_js_1.scheduledJobToAutomationInput)({
|
|
74
|
+
workspaceId: 'workspace-1', platform: 'local', route: { type: 'local.thread', channelId: 'workspace-1' },
|
|
75
|
+
triggerType: 'cron', cronExpr: '* * * * *', promptTemplate: 'hello', description: ' ',
|
|
76
|
+
}).title, 'hello');
|
|
77
|
+
const definition = {
|
|
78
|
+
id: 'abcd1234', health: 'healthy', consecutiveEvaluationFailures: 0,
|
|
79
|
+
createdAt: '2026-07-05T00:00:00.000Z', updatedAt: '2026-07-05T00:00:00.000Z',
|
|
80
|
+
...input,
|
|
81
|
+
};
|
|
82
|
+
strict_1.default.equal((0, legacy_automation_mappers_js_1.automationToScheduledJob)(definition).id, 'abcd1234');
|
|
83
|
+
strict_1.default.throws(() => (0, legacy_automation_mappers_js_1.automationToScheduledJob)({ ...definition, originKind: 'native' }), /scheduled-job origin/);
|
|
84
|
+
});
|
|
85
|
+
(0, node_test_1.default)('legacy monitor mappings use provider events and expression conditions', () => {
|
|
86
|
+
const input = (0, legacy_automation_mappers_js_1.monitorToAutomationInput)({
|
|
87
|
+
workspaceId: 'workspace-1', title: 'Price alert', sourceType: 'stock.quote', sourceConfig: { symbol: 'AAPL' },
|
|
88
|
+
condition: { metric: 'latestPrice', operator: '>=', value: 200 }, promptTemplate: 'Price {{latestPrice}}',
|
|
89
|
+
platform: 'local', route: { type: 'local.thread', channelId: 'workspace-1' }, executionMode: 'side-thread',
|
|
90
|
+
enabled: true, cooldownMs: 60_000,
|
|
91
|
+
});
|
|
92
|
+
strict_1.default.deepEqual(input.activation, { kind: 'provider-event', sourceType: 'stock.quote', sourceConfig: { symbol: 'AAPL' } });
|
|
93
|
+
strict_1.default.deepEqual(input.condition, { kind: 'expression', expression: 'latestPrice >= 200' });
|
|
94
|
+
strict_1.default.equal(input.originKind, 'automation-monitor');
|
|
95
|
+
strict_1.default.equal(input.policies.cooldownMs, 60_000);
|
|
96
|
+
strict_1.default.equal(input.action.executionMode, 'side-thread');
|
|
97
|
+
strict_1.default.equal((0, legacy_automation_mappers_js_1.monitorToAutomationInput)({
|
|
98
|
+
workspaceId: 'workspace-1', title: 'Default', sourceType: 'stock.quote', condition: { metric: 'x', operator: '>', value: 1 },
|
|
99
|
+
promptTemplate: 'hello', platform: 'local', route: { type: 'local.thread', channelId: 'workspace-1' }, executionMode: '',
|
|
100
|
+
}).action.executionMode, 'side-thread');
|
|
101
|
+
strict_1.default.equal((0, legacy_automation_mappers_js_1.monitorToAutomationInput)({
|
|
102
|
+
workspaceId: 'workspace-1', title: 'Default', sourceType: 'stock.quote', condition: { metric: 'x', operator: '>', value: 1 },
|
|
103
|
+
promptTemplate: 'hello', platform: 'local', route: { type: 'local.thread', channelId: 'workspace-1' },
|
|
104
|
+
}).policies.cooldownMs, 15 * 60 * 1_000);
|
|
105
|
+
strict_1.default.throws(() => (0, legacy_automation_mappers_js_1.monitorToAutomationInput)({
|
|
106
|
+
workspaceId: 'workspace-1', title: 'Default', sourceType: 'stock.quote', condition: { metric: 'x', operator: '>', value: 1 },
|
|
107
|
+
promptTemplate: 'hello', platform: 'local', route: { type: 'local.thread', channelId: 'workspace-1' }, executionMode: 'bogus',
|
|
108
|
+
}), /same-thread or side-thread/);
|
|
109
|
+
strict_1.default.throws(() => (0, legacy_automation_mappers_js_1.monitorToAutomationInput)({
|
|
110
|
+
workspaceId: 'workspace-1', title: 'Default', sourceType: 'stock.quote',
|
|
111
|
+
condition: { metric: 'x', operator: 'contains', value: 1 }, promptTemplate: 'hello',
|
|
112
|
+
platform: 'local', route: { type: 'local.thread', channelId: 'workspace-1' },
|
|
113
|
+
}), /operator/i);
|
|
114
|
+
strict_1.default.throws(() => (0, legacy_automation_mappers_js_1.monitorToAutomationInput)({
|
|
115
|
+
workspaceId: 'workspace-1', title: 'Default', sourceType: 'stock.quote',
|
|
116
|
+
condition: { metric: 'x', operator: '>', value: { unsafe: true } }, promptTemplate: 'hello',
|
|
117
|
+
platform: 'local', route: { type: 'local.thread', channelId: 'workspace-1' },
|
|
118
|
+
}), /number, string, or boolean/);
|
|
119
|
+
const projected = (0, legacy_automation_mappers_js_1.automationToMonitor)({
|
|
120
|
+
id: 'monitor:one', health: 'healthy', consecutiveEvaluationFailures: 0,
|
|
121
|
+
createdAt: '2026-07-05T00:00:00.000Z', updatedAt: '2026-07-05T00:00:00.000Z', ...input,
|
|
122
|
+
});
|
|
123
|
+
strict_1.default.deepEqual(projected.condition, { metric: 'latestPrice', operator: '>=', value: 200 });
|
|
124
|
+
});
|
|
125
|
+
(0, node_test_1.default)('scheduler facade writes and runs only unified records while projecting legacy events', async () => {
|
|
126
|
+
const context = fixture();
|
|
127
|
+
const events = [];
|
|
128
|
+
context.eventBus.on('scheduler.job.updated', () => events.push('job'));
|
|
129
|
+
context.eventBus.on('scheduler.run.updated', () => events.push('run'));
|
|
130
|
+
try {
|
|
131
|
+
const job = context.jobs.createJob({
|
|
132
|
+
workspaceId: 'workspace-1', threadId: 'thread-1', triggerType: 'once', runAt: '2099-01-01T00:00:00.000Z',
|
|
133
|
+
promptTemplate: 'hello', description: 'One shot', enabled: true,
|
|
134
|
+
});
|
|
135
|
+
strict_1.default.match(job.id, /^[0-9a-f]{8}$/);
|
|
136
|
+
strict_1.default.equal(context.store.listScheduledJobs().length, 0);
|
|
137
|
+
strict_1.default.equal(context.automations.get(job.id)?.originKind, 'scheduled-job');
|
|
138
|
+
strict_1.default.equal(context.jobs.getJob(job.id)?.description, 'One shot');
|
|
139
|
+
context.jobs.updateJob(job.id, { description: 'Updated', enabled: false });
|
|
140
|
+
strict_1.default.equal(context.jobs.listJobs()[0]?.description, 'Updated');
|
|
141
|
+
const run = await context.jobs.runJobNow(job.id);
|
|
142
|
+
strict_1.default.equal(run.jobId, job.id);
|
|
143
|
+
strict_1.default.equal(run.status, 'succeeded');
|
|
144
|
+
strict_1.default.equal(context.store.listScheduledJobRuns(job.id).length, 0);
|
|
145
|
+
strict_1.default.equal(context.jobs.listJobRuns(job.id).length, 1);
|
|
146
|
+
strict_1.default.equal(context.actions.length, 1);
|
|
147
|
+
strict_1.default.equal(events.filter((event) => event === 'job').length, 3);
|
|
148
|
+
strict_1.default.equal(events.filter((event) => event === 'run').length, 3);
|
|
149
|
+
context.jobs.deleteJob(job.id);
|
|
150
|
+
strict_1.default.equal(context.jobs.getJob(job.id), undefined);
|
|
151
|
+
}
|
|
152
|
+
finally {
|
|
153
|
+
context.close();
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
(0, node_test_1.default)('scheduler empty description survives unified persistence and reopen', () => {
|
|
157
|
+
const context = fixture();
|
|
158
|
+
const path = context.path;
|
|
159
|
+
let id = '';
|
|
160
|
+
try {
|
|
161
|
+
const created = context.jobs.createJob({
|
|
162
|
+
workspaceId: 'workspace-1', triggerType: 'cron', cronExpr: '0 9 * * *', promptTemplate: 'hello',
|
|
163
|
+
});
|
|
164
|
+
id = created.id;
|
|
165
|
+
strict_1.default.equal(created.description, '');
|
|
166
|
+
strict_1.default.equal(context.automations.get(id)?.title, 'hello');
|
|
167
|
+
context.store.close();
|
|
168
|
+
const store = new local_core_acp_store_js_1.LocalCoreAcpStore(path);
|
|
169
|
+
const eventBus = new event_bus_js_1.LocalCoreEventBus();
|
|
170
|
+
const automations = new automation_service_js_1.AutomationService({
|
|
171
|
+
store, eventBus,
|
|
172
|
+
actionExecutor: { async execute() { return { threadId: 'thread', acpRunId: 'run' }; } },
|
|
173
|
+
ownershipPolicy: { executes: () => true },
|
|
174
|
+
});
|
|
175
|
+
const scheduler = new scheduler_service_js_1.SchedulerService({ store, automations, triggers: [], executors: [], eventBus });
|
|
176
|
+
const jobs = new scheduled_job_application_service_js_1.ScheduledJobApplicationService({ store, scheduler, automations, eventBus });
|
|
177
|
+
strict_1.default.equal(jobs.getJob(id)?.description, '');
|
|
178
|
+
strict_1.default.equal(jobs.listJobs()[0]?.description, '');
|
|
179
|
+
store.close();
|
|
180
|
+
}
|
|
181
|
+
finally {
|
|
182
|
+
(0, node_fs_1.rmSync)(path, { recursive: true, force: true });
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
(0, node_test_1.default)('monitor facade delegates provider snapshots to one unified evaluation and run', async () => {
|
|
186
|
+
const context = fixture();
|
|
187
|
+
const events = [];
|
|
188
|
+
context.eventBus.on('automation.monitor.updated', () => events.push('monitor'));
|
|
189
|
+
context.eventBus.on('automation.monitor.run.updated', () => events.push('run'));
|
|
190
|
+
const snapshot = {
|
|
191
|
+
id: 'event-1', sourceType: 'stock.quote', occurredAt: '2026-07-05T01:00:00.000Z', subject: 'AAPL',
|
|
192
|
+
payload: { latestPrice: 205, previousPrice: 190 },
|
|
193
|
+
};
|
|
194
|
+
const monitors = new automation_monitor_service_js_1.AutomationMonitorService({
|
|
195
|
+
store: context.store,
|
|
196
|
+
automations: context.automations,
|
|
197
|
+
providers: [{ sourceType: 'stock.quote', modes: ['poll'], async poll() { return snapshot; } }],
|
|
198
|
+
eventBus: context.eventBus,
|
|
199
|
+
});
|
|
200
|
+
try {
|
|
201
|
+
const monitor = await monitors.createMonitor({
|
|
202
|
+
workspaceId: 'workspace-1', title: 'Price alert', sourceType: 'stock.quote', sourceConfig: { symbol: 'AAPL' },
|
|
203
|
+
condition: { metric: 'latestPrice', operator: '>=', value: 200 }, promptTemplate: '{{subject}} {{latestPrice}}',
|
|
204
|
+
threadId: 'thread-1', enabled: true, cooldownMs: 60_000,
|
|
205
|
+
});
|
|
206
|
+
strict_1.default.equal(context.store.listAutomationMonitors().length, 0);
|
|
207
|
+
strict_1.default.equal(context.automations.get(monitor.id)?.originKind, 'automation-monitor');
|
|
208
|
+
await strict_1.default.rejects(context.automations.checkNow(monitor.id), /requires an event snapshot/);
|
|
209
|
+
const run = await monitors.runMonitorNow(monitor.id);
|
|
210
|
+
strict_1.default.equal(run.monitorId, monitor.id);
|
|
211
|
+
strict_1.default.deepEqual(run.eventSnapshot, snapshot);
|
|
212
|
+
strict_1.default.equal(context.automations.listEvaluations(monitor.id).length, 1);
|
|
213
|
+
strict_1.default.equal(context.automations.listEvaluations(monitor.id)[0]?.startedAt, snapshot.occurredAt);
|
|
214
|
+
strict_1.default.equal(context.automations.get(monitor.id)?.lastTriggeredAt, snapshot.occurredAt);
|
|
215
|
+
strict_1.default.equal(context.automations.listRuns(monitor.id).length, 1);
|
|
216
|
+
strict_1.default.equal(context.actions.length, 1);
|
|
217
|
+
strict_1.default.equal(context.actions[0]?.promptVariables.latestPrice, 205);
|
|
218
|
+
strict_1.default.deepEqual(context.actions[0]?.promptVariables.previous, {});
|
|
219
|
+
strict_1.default.deepEqual(context.actions[0]?.promptVariables.eventSnapshot, snapshot);
|
|
220
|
+
strict_1.default.equal(events.filter((event) => event === 'monitor').length, 2);
|
|
221
|
+
strict_1.default.equal(events.filter((event) => event === 'run').length, 3);
|
|
222
|
+
const notMatched = await monitors.runMonitorNow(monitor.id, {
|
|
223
|
+
...snapshot,
|
|
224
|
+
id: 'event-2',
|
|
225
|
+
occurredAt: '2026-07-05T01:01:00.000Z',
|
|
226
|
+
payload: { latestPrice: 190, previousPrice: 205 },
|
|
227
|
+
});
|
|
228
|
+
strict_1.default.equal(notMatched.status, 'skipped');
|
|
229
|
+
strict_1.default.equal(context.automations.listEvaluations(monitor.id).length, 2);
|
|
230
|
+
strict_1.default.equal(context.automations.listRuns(monitor.id).length, 1);
|
|
231
|
+
strict_1.default.equal(context.actions.length, 1);
|
|
232
|
+
strict_1.default.equal(monitors.getMonitor(monitor.id)?.lastState?.latestPrice, 190);
|
|
233
|
+
strict_1.default.equal(events.filter((event) => event === 'run').length, 4);
|
|
234
|
+
const latestLegacyRun = monitors.listRuns(monitor.id)[0];
|
|
235
|
+
strict_1.default.equal(latestLegacyRun?.status, 'skipped');
|
|
236
|
+
strict_1.default.equal(latestLegacyRun?.eventSnapshot?.id, 'event-2');
|
|
237
|
+
context.automations.recordUnavailableProviderEvent(monitor.id, 'temporarily unavailable');
|
|
238
|
+
strict_1.default.equal(monitors.getMonitor(monitor.id)?.lastState?.latestPrice, 190);
|
|
239
|
+
}
|
|
240
|
+
finally {
|
|
241
|
+
await monitors.stop();
|
|
242
|
+
context.close();
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
(0, node_test_1.default)('monitor facade preserves legacy fields across stable CRUD projections', async () => {
|
|
246
|
+
const context = fixture();
|
|
247
|
+
const monitors = new automation_monitor_service_js_1.AutomationMonitorService({
|
|
248
|
+
store: context.store,
|
|
249
|
+
automations: context.automations,
|
|
250
|
+
providers: [{ sourceType: 'stock.quote', modes: ['poll'] }],
|
|
251
|
+
eventBus: context.eventBus,
|
|
252
|
+
});
|
|
253
|
+
try {
|
|
254
|
+
const created = await monitors.createMonitor({
|
|
255
|
+
workspaceId: 'workspace-1', title: 'Price alert', sourceType: 'stock.quote', sourceConfig: { symbol: 'AAPL' },
|
|
256
|
+
condition: { metric: 'latestPrice', operator: '>=', value: 200 }, promptTemplate: 'price',
|
|
257
|
+
platform: 'lark:tenant-1', route: { type: 'lark.chat', channelId: 'chat-1', threadId: 'ignored' },
|
|
258
|
+
executionMode: 'same-thread', enabled: true, cooldownMs: 30_000,
|
|
259
|
+
});
|
|
260
|
+
strict_1.default.equal(monitors.listMonitors()[0]?.id, created.id);
|
|
261
|
+
strict_1.default.equal(monitors.getMonitor(publicMonitorId(created.id))?.id, created.id);
|
|
262
|
+
strict_1.default.equal(created.platform, 'lark:tenant-1');
|
|
263
|
+
strict_1.default.equal(created.route.channelId, 'chat-1');
|
|
264
|
+
strict_1.default.equal(created.route.threadId, undefined);
|
|
265
|
+
const updated = await monitors.updateMonitor(created.id, { title: 'Updated', enabled: false, cooldownMs: 90_000 });
|
|
266
|
+
strict_1.default.equal(updated.id, created.id);
|
|
267
|
+
strict_1.default.equal(updated.title, 'Updated');
|
|
268
|
+
strict_1.default.equal(updated.enabled, false);
|
|
269
|
+
strict_1.default.equal(updated.cooldownMs, 90_000);
|
|
270
|
+
strict_1.default.deepEqual(updated.sourceConfig, { symbol: 'AAPL' });
|
|
271
|
+
strict_1.default.deepEqual(await monitors.deleteMonitor(created.id), { deleted: true });
|
|
272
|
+
strict_1.default.equal(monitors.getMonitor(created.id), undefined);
|
|
273
|
+
strict_1.default.equal(context.store.listAutomationMonitors().length, 0);
|
|
274
|
+
}
|
|
275
|
+
finally {
|
|
276
|
+
await monitors.stop();
|
|
277
|
+
context.close();
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
(0, node_test_1.default)('manual monitor polling without a snapshot records one unified skipped evaluation', async () => {
|
|
281
|
+
const context = fixture();
|
|
282
|
+
const events = [];
|
|
283
|
+
context.eventBus.on('automation.monitor.run.updated', () => events.push('run'));
|
|
284
|
+
const monitors = new automation_monitor_service_js_1.AutomationMonitorService({
|
|
285
|
+
store: context.store,
|
|
286
|
+
automations: context.automations,
|
|
287
|
+
providers: [{ sourceType: 'stock.quote', modes: ['poll'], async poll() { return null; } }],
|
|
288
|
+
eventBus: context.eventBus,
|
|
289
|
+
});
|
|
290
|
+
try {
|
|
291
|
+
const monitor = await monitors.createMonitor({
|
|
292
|
+
workspaceId: 'workspace-1', title: 'No quote', sourceType: 'stock.quote',
|
|
293
|
+
condition: { metric: 'latestPrice', operator: '>=', value: 200 }, promptTemplate: 'price',
|
|
294
|
+
});
|
|
295
|
+
const run = await monitors.runMonitorNow(monitor.id);
|
|
296
|
+
strict_1.default.equal(run.status, 'skipped');
|
|
297
|
+
strict_1.default.match(run.error || '', /No event snapshot/);
|
|
298
|
+
strict_1.default.equal(context.automations.listEvaluations(monitor.id).length, 1);
|
|
299
|
+
strict_1.default.equal(context.automations.listRuns(monitor.id).length, 0);
|
|
300
|
+
strict_1.default.equal(context.store.listAutomationMonitorRuns(monitor.id).length, 0);
|
|
301
|
+
strict_1.default.deepEqual(events, ['run']);
|
|
302
|
+
}
|
|
303
|
+
finally {
|
|
304
|
+
await monitors.stop();
|
|
305
|
+
context.close();
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
(0, node_test_1.default)('provider evaluation errors preserve snapshot, prior state, and one failed compatibility run', async () => {
|
|
309
|
+
let calls = 0;
|
|
310
|
+
const context = fixture(undefined, () => {
|
|
311
|
+
calls += 1;
|
|
312
|
+
if (calls > 1)
|
|
313
|
+
throw new Error('condition exploded');
|
|
314
|
+
return { kind: 'evaluated', matched: true };
|
|
315
|
+
});
|
|
316
|
+
const failedRuns = [];
|
|
317
|
+
context.eventBus.on('automation.monitor.run.updated', (run) => {
|
|
318
|
+
if (run.status === 'failed')
|
|
319
|
+
failedRuns.push({ error: run.error, eventId: run.eventSnapshot?.id });
|
|
320
|
+
});
|
|
321
|
+
const monitors = new automation_monitor_service_js_1.AutomationMonitorService({
|
|
322
|
+
store: context.store, automations: context.automations, providers: [], eventBus: context.eventBus,
|
|
323
|
+
});
|
|
324
|
+
try {
|
|
325
|
+
const monitor = await monitors.createMonitor({
|
|
326
|
+
workspaceId: 'workspace-1', title: 'Price', sourceType: 'stock.quote',
|
|
327
|
+
condition: { metric: 'latestPrice', operator: '>', value: 1 }, promptTemplate: 'price',
|
|
328
|
+
});
|
|
329
|
+
await monitors.runMonitorNow(monitor.id, {
|
|
330
|
+
id: 'event-ok', sourceType: 'stock.quote', occurredAt: '2026-07-05T01:00:00.000Z', subject: 'AAPL', payload: { latestPrice: 2 },
|
|
331
|
+
});
|
|
332
|
+
const failed = await monitors.runMonitorNow(monitor.id, {
|
|
333
|
+
id: 'event-error', sourceType: 'stock.quote', occurredAt: '2026-07-05T01:01:00.000Z', subject: 'AAPL', payload: { latestPrice: 3 },
|
|
334
|
+
});
|
|
335
|
+
strict_1.default.equal(failed.status, 'failed');
|
|
336
|
+
strict_1.default.match(failed.error || '', /condition exploded/);
|
|
337
|
+
strict_1.default.equal(failed.eventSnapshot?.id, 'event-error');
|
|
338
|
+
strict_1.default.deepEqual(failedRuns, [{ error: 'condition exploded', eventId: 'event-error' }]);
|
|
339
|
+
const latest = context.automations.listEvaluations(monitor.id)[0];
|
|
340
|
+
strict_1.default.equal(latest?.status, 'finished');
|
|
341
|
+
if (latest?.status === 'finished') {
|
|
342
|
+
strict_1.default.equal(latest.payload?.eventSnapshot && latest.payload.eventSnapshot.id, 'event-error');
|
|
343
|
+
strict_1.default.equal(latest.payload?.previous?.latestPrice, 2);
|
|
344
|
+
strict_1.default.equal(latest.nextState?.latestPrice, 3);
|
|
345
|
+
}
|
|
346
|
+
context.automations.recordUnavailableProviderEvent(monitor.id, 'no snapshot');
|
|
347
|
+
strict_1.default.equal(monitors.getMonitor(monitor.id)?.lastState?.latestPrice, 3);
|
|
348
|
+
}
|
|
349
|
+
finally {
|
|
350
|
+
await monitors.stop();
|
|
351
|
+
context.close();
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
(0, node_test_1.default)('provider cooldown and trigger timestamps use event occurrence time', async () => {
|
|
355
|
+
const context = fixture();
|
|
356
|
+
const monitors = new automation_monitor_service_js_1.AutomationMonitorService({
|
|
357
|
+
store: context.store, automations: context.automations, providers: [], eventBus: context.eventBus,
|
|
358
|
+
});
|
|
359
|
+
try {
|
|
360
|
+
const monitor = await monitors.createMonitor({
|
|
361
|
+
workspaceId: 'workspace-1', title: 'Price', sourceType: 'stock.quote', cooldownMs: 5 * 60_000,
|
|
362
|
+
condition: { metric: 'latestPrice', operator: '>', value: 1 }, promptTemplate: 'price',
|
|
363
|
+
});
|
|
364
|
+
const snapshot = (id, occurredAt, latestPrice) => ({
|
|
365
|
+
id, sourceType: 'stock.quote', occurredAt, subject: 'AAPL', payload: { latestPrice },
|
|
366
|
+
});
|
|
367
|
+
const first = await monitors.runMonitorNow(monitor.id, snapshot('one', '2026-07-05T01:00:00.000Z', 2));
|
|
368
|
+
await monitors.runMonitorNow(monitor.id, snapshot('rearm', '2026-07-05T01:00:30.000Z', 0));
|
|
369
|
+
const cooled = await monitors.runMonitorNow(monitor.id, snapshot('cooled', '2026-07-05T01:01:00.000Z', 2));
|
|
370
|
+
strict_1.default.equal(first.triggeredAt, '2026-07-05T01:00:00.000Z');
|
|
371
|
+
strict_1.default.equal(cooled.status, 'skipped');
|
|
372
|
+
strict_1.default.equal(context.automations.listEvaluations(monitor.id)[0]?.triggerDecision, 'skipped_cooldown');
|
|
373
|
+
strict_1.default.equal(context.automations.get(monitor.id)?.lastTriggeredAt, '2026-07-05T01:00:00.000Z');
|
|
374
|
+
strict_1.default.equal(context.actions.length, 1);
|
|
375
|
+
}
|
|
376
|
+
finally {
|
|
377
|
+
await monitors.stop();
|
|
378
|
+
context.close();
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
(0, node_test_1.default)('legacy scheduler loop is disabled after unified ownership cutover', async () => {
|
|
382
|
+
const context = fixture();
|
|
383
|
+
try {
|
|
384
|
+
await context.scheduler.start();
|
|
385
|
+
strict_1.default.equal(context.store.listScheduledJobRuns('missing').length, 0);
|
|
386
|
+
await context.scheduler.stop();
|
|
387
|
+
}
|
|
388
|
+
finally {
|
|
389
|
+
context.close();
|
|
390
|
+
}
|
|
391
|
+
});
|
|
392
|
+
(0, node_test_1.default)('concurrent scheduler manual run emits one compatibility skipped run without a unified run', async () => {
|
|
393
|
+
let release;
|
|
394
|
+
const blocked = new Promise((resolve) => { release = resolve; });
|
|
395
|
+
const context = fixture(async () => {
|
|
396
|
+
await blocked;
|
|
397
|
+
return { threadId: 'thread-1', acpRunId: 'run-1', deliveryStatus: 'succeeded' };
|
|
398
|
+
});
|
|
399
|
+
const skippedEvents = [];
|
|
400
|
+
context.eventBus.on('scheduler.run.updated', (run) => {
|
|
401
|
+
if (run.status === 'skipped')
|
|
402
|
+
skippedEvents.push(run.id);
|
|
403
|
+
});
|
|
404
|
+
try {
|
|
405
|
+
const job = context.jobs.createJob({
|
|
406
|
+
workspaceId: 'workspace-1', triggerType: 'cron', cronExpr: '* * * * *', promptTemplate: 'hello',
|
|
407
|
+
});
|
|
408
|
+
const first = context.jobs.runJobNow(job.id);
|
|
409
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
410
|
+
const skipped = await context.jobs.runJobNow(job.id);
|
|
411
|
+
strict_1.default.equal(skipped.status, 'skipped');
|
|
412
|
+
strict_1.default.equal(context.automations.listEvaluations(job.id).length, 2);
|
|
413
|
+
strict_1.default.equal(context.automations.listRuns(job.id).length, 1);
|
|
414
|
+
strict_1.default.deepEqual(skippedEvents, [skipped.id]);
|
|
415
|
+
release();
|
|
416
|
+
await first;
|
|
417
|
+
}
|
|
418
|
+
finally {
|
|
419
|
+
context.close();
|
|
420
|
+
}
|
|
421
|
+
});
|
|
422
|
+
(0, node_test_1.default)('concurrent monitor manual run emits one compatibility skipped run with its snapshot', async () => {
|
|
423
|
+
let release;
|
|
424
|
+
const blocked = new Promise((resolve) => { release = resolve; });
|
|
425
|
+
const context = fixture(async () => {
|
|
426
|
+
await blocked;
|
|
427
|
+
return { threadId: 'thread-1', acpRunId: 'run-1', deliveryStatus: 'succeeded' };
|
|
428
|
+
});
|
|
429
|
+
const skippedEvents = [];
|
|
430
|
+
context.eventBus.on('automation.monitor.run.updated', (run) => {
|
|
431
|
+
if (run.status === 'skipped')
|
|
432
|
+
skippedEvents.push({ id: run.id, eventId: run.eventSnapshot?.id });
|
|
433
|
+
});
|
|
434
|
+
const snapshot = {
|
|
435
|
+
id: 'event-1', sourceType: 'stock.quote', occurredAt: '2026-07-05T01:00:00.000Z', subject: 'AAPL',
|
|
436
|
+
payload: { latestPrice: 205 },
|
|
437
|
+
};
|
|
438
|
+
const monitors = new automation_monitor_service_js_1.AutomationMonitorService({
|
|
439
|
+
store: context.store, automations: context.automations, providers: [], eventBus: context.eventBus,
|
|
440
|
+
});
|
|
441
|
+
try {
|
|
442
|
+
const monitor = await monitors.createMonitor({
|
|
443
|
+
workspaceId: 'workspace-1', title: 'Price', sourceType: 'stock.quote',
|
|
444
|
+
condition: { metric: 'latestPrice', operator: '>=', value: 200 }, promptTemplate: 'price',
|
|
445
|
+
});
|
|
446
|
+
const first = monitors.runMonitorNow(monitor.id, snapshot);
|
|
447
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
448
|
+
const skipped = await monitors.runMonitorNow(monitor.id, { ...snapshot, id: 'event-2' });
|
|
449
|
+
strict_1.default.equal(skipped.status, 'skipped');
|
|
450
|
+
strict_1.default.equal(context.automations.listEvaluations(monitor.id).length, 2);
|
|
451
|
+
strict_1.default.equal(context.automations.listRuns(monitor.id).length, 1);
|
|
452
|
+
strict_1.default.deepEqual(skippedEvents, [{ id: skipped.id, eventId: 'event-2' }]);
|
|
453
|
+
release();
|
|
454
|
+
await first;
|
|
455
|
+
}
|
|
456
|
+
finally {
|
|
457
|
+
await monitors.stop();
|
|
458
|
+
context.close();
|
|
459
|
+
}
|
|
460
|
+
});
|
|
461
|
+
function publicMonitorId(monitorId) {
|
|
462
|
+
return monitorId.replace(/^monitor:/, '').split('-')[0] || monitorId;
|
|
463
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const node_test_1 = __importDefault(require("node:test"));
|
|
7
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
8
|
+
const node_fs_1 = require("node:fs");
|
|
9
|
+
const node_path_1 = require("node:path");
|
|
10
|
+
const node_process_1 = __importDefault(require("node:process"));
|
|
11
|
+
const deployment_diagnostics_js_1 = require("../../services/local-ai-core/src/runtime/deployment-diagnostics.js");
|
|
12
|
+
(0, node_test_1.default)('Linux automation diagnostics report every sandbox prerequisite independently', () => {
|
|
13
|
+
const checks = (0, deployment_diagnostics_js_1.automationSandboxDiagnosticChecks)({
|
|
14
|
+
available: false,
|
|
15
|
+
platform: 'linux',
|
|
16
|
+
missing: ['bwrap', 'socat', 'rg', 'apparmor.userns', 'network.namespace', 'seccomp'],
|
|
17
|
+
unverified: ['apparmor.userns', 'network.namespace', 'seccomp'],
|
|
18
|
+
});
|
|
19
|
+
strict_1.default.deepEqual(checks.map((check) => check.id), [
|
|
20
|
+
'automation.sandbox',
|
|
21
|
+
'automation.runtime',
|
|
22
|
+
'automation.linux.bwrap',
|
|
23
|
+
'automation.linux.socat',
|
|
24
|
+
'automation.linux.rg',
|
|
25
|
+
'automation.linux.userns-apparmor',
|
|
26
|
+
'automation.linux.network-namespace',
|
|
27
|
+
'automation.linux.seccomp',
|
|
28
|
+
]);
|
|
29
|
+
strict_1.default.equal(checks[0]?.status, 'fail', 'the stable aggregate follows capability.available');
|
|
30
|
+
strict_1.default.equal(checks[1]?.status, 'pass', 'package/runtime presence remains independently diagnosable');
|
|
31
|
+
strict_1.default.ok(checks.slice(2).every((check) => check.status === 'fail'));
|
|
32
|
+
strict_1.default.match(checks.find((check) => check.id === 'automation.linux.network-namespace')?.summary || '', /could not be behaviorally verified/);
|
|
33
|
+
});
|
|
34
|
+
(0, node_test_1.default)('macOS automation diagnostics distinguish runtime, sandbox-exec, and rg', () => {
|
|
35
|
+
const checks = (0, deployment_diagnostics_js_1.automationSandboxDiagnosticChecks)({
|
|
36
|
+
available: false,
|
|
37
|
+
platform: 'macos',
|
|
38
|
+
missing: ['sandbox_runtime', 'sandbox-exec', 'rg'],
|
|
39
|
+
});
|
|
40
|
+
strict_1.default.deepEqual(checks.map((check) => check.id), [
|
|
41
|
+
'automation.sandbox',
|
|
42
|
+
'automation.runtime',
|
|
43
|
+
'automation.macos.sandbox-exec',
|
|
44
|
+
'automation.macos.rg',
|
|
45
|
+
]);
|
|
46
|
+
strict_1.default.ok(checks.every((check) => check.status === 'fail'));
|
|
47
|
+
});
|
|
48
|
+
(0, node_test_1.default)('Windows automation diagnostics fail closed as unsupported', () => {
|
|
49
|
+
const checks = (0, deployment_diagnostics_js_1.automationSandboxDiagnosticChecks)({
|
|
50
|
+
available: false,
|
|
51
|
+
platform: 'windows',
|
|
52
|
+
missing: ['sandbox_unavailable'],
|
|
53
|
+
});
|
|
54
|
+
strict_1.default.equal(checks.length, 1);
|
|
55
|
+
strict_1.default.equal(checks[0]?.id, 'automation.sandbox');
|
|
56
|
+
strict_1.default.equal(checks[0]?.status, 'fail');
|
|
57
|
+
strict_1.default.match(checks[0]?.summary || '', /unsupported|fail-closed/i);
|
|
58
|
+
});
|
|
59
|
+
(0, node_test_1.default)('Linux core image installs conditional automation sandbox dependencies', () => {
|
|
60
|
+
const dockerfile = (0, node_fs_1.readFileSync)((0, node_path_1.join)(node_process_1.default.cwd(), 'docker', 'agentdock', 'core.Dockerfile'), 'utf8');
|
|
61
|
+
strict_1.default.match(dockerfile, /apt-get\s+update/);
|
|
62
|
+
for (const dependency of ['bubblewrap', 'socat', 'ripgrep']) {
|
|
63
|
+
strict_1.default.match(dockerfile, new RegExp(`\\b${dependency}\\b`));
|
|
64
|
+
}
|
|
65
|
+
strict_1.default.match(dockerfile, /rm -rf \/var\/lib\/apt\/lists\/\*/);
|
|
66
|
+
});
|