botinabox 2.5.0 → 2.5.2
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/LICENSE +21 -21
- package/README.md +190 -190
- package/bin/botinabox.mjs +2 -2
- package/dist/channels/discord/adapter.d.ts +32 -0
- package/dist/channels/discord/adapter.js +70 -0
- package/dist/channels/discord/inbound.d.ts +25 -0
- package/dist/channels/discord/inbound.js +24 -0
- package/dist/channels/discord/models.d.ts +8 -0
- package/dist/channels/discord/models.js +5 -0
- package/dist/channels/discord/outbound.d.ts +14 -0
- package/dist/channels/discord/outbound.js +38 -0
- package/dist/channels/slack/adapter.d.ts +33 -0
- package/dist/channels/slack/adapter.js +74 -0
- package/dist/channels/slack/inbound.d.ts +59 -0
- package/dist/channels/slack/inbound.js +96 -0
- package/dist/channels/slack/models.d.ts +9 -0
- package/dist/channels/slack/models.js +5 -0
- package/dist/channels/slack/outbound.d.ts +12 -0
- package/dist/channels/slack/outbound.js +18 -0
- package/dist/channels/slack/transcribe.d.ts +41 -0
- package/dist/channels/slack/transcribe.js +106 -0
- package/dist/channels/webhook/adapter.d.ts +23 -0
- package/dist/channels/webhook/adapter.js +86 -0
- package/dist/channels/webhook/hmac.d.ts +13 -0
- package/dist/channels/webhook/hmac.js +26 -0
- package/dist/channels/webhook/models.d.ts +9 -0
- package/dist/channels/webhook/models.js +5 -0
- package/dist/channels/webhook/server.d.ts +20 -0
- package/dist/channels/webhook/server.js +91 -0
- package/dist/chat-pipeline-BWrtVqEP.d.ts +652 -0
- package/dist/chat-pipeline-C-XlLGNl.d.ts +648 -0
- package/dist/chat-pipeline-CR1KF6eX.d.ts +652 -0
- package/dist/chat-pipeline-DisuC8SB.d.ts +643 -0
- package/dist/chunk-2LGXQPEA.js +41 -0
- package/dist/chunk-3X3YKI4T.js +357 -0
- package/dist/chunk-D47AIFOD.js +351 -0
- package/dist/chunk-DSNJKNEW.js +328 -0
- package/dist/chunk-GS2JFL6I.js +144 -0
- package/dist/chunk-J6S6QMUY.js +144 -0
- package/dist/chunk-QLA6YOFN.js +22 -0
- package/dist/chunk-UACT2WXX.js +381 -0
- package/dist/cli/templates/config.yml.d.ts +7 -0
- package/dist/cli/templates/config.yml.js +61 -0
- package/dist/cli/templates/env.d.ts +1 -0
- package/dist/cli/templates/env.js +30 -0
- package/dist/cli/templates/index.ts.d.ts +2 -0
- package/dist/cli/templates/index.ts.js +30 -0
- package/dist/cli/templates/package.json.d.ts +5 -0
- package/dist/cli/templates/package.json.js +28 -0
- package/dist/cli.js +0 -0
- package/dist/connector-DDahQw-2.d.ts +63 -0
- package/dist/connectors/google/calendar-connector.d.ts +40 -0
- package/dist/connectors/google/calendar-connector.js +243 -0
- package/dist/connectors/google/gmail-connector.d.ts +42 -0
- package/dist/connectors/google/gmail-connector.js +345 -0
- package/dist/connectors/google/index.d.ts +67 -1
- package/dist/connectors/google/index.js +240 -0
- package/dist/connectors/google/oauth.d.ts +48 -0
- package/dist/connectors/google/oauth.js +112 -0
- package/dist/connectors/google/types.d.ts +78 -0
- package/dist/connectors/google/types.js +2 -0
- package/dist/core/chat/auto-discovery.d.ts +16 -0
- package/dist/core/chat/auto-discovery.js +54 -0
- package/dist/core/chat/channel-registry.d.ts +45 -0
- package/dist/core/chat/channel-registry.js +96 -0
- package/dist/core/chat/chat-pipeline.d.ts +113 -0
- package/dist/core/chat/chat-pipeline.js +395 -0
- package/dist/core/chat/chat-responder.d.ts +90 -0
- package/dist/core/chat/chat-responder.js +185 -0
- package/dist/core/chat/formatter.d.ts +11 -0
- package/dist/core/chat/formatter.js +60 -0
- package/dist/core/chat/index.d.ts +24 -0
- package/dist/core/chat/index.js +18 -0
- package/dist/core/chat/message-interpreter.d.ts +91 -0
- package/dist/core/chat/message-interpreter.js +166 -0
- package/dist/core/chat/message-store.d.ts +66 -0
- package/dist/core/chat/message-store.js +131 -0
- package/dist/core/chat/notification-queue.d.ts +34 -0
- package/dist/core/chat/notification-queue.js +111 -0
- package/dist/core/chat/pipeline.d.ts +38 -0
- package/dist/core/chat/pipeline.js +89 -0
- package/dist/core/chat/policies.d.ts +16 -0
- package/dist/core/chat/policies.js +25 -0
- package/dist/core/chat/routing.d.ts +17 -0
- package/dist/core/chat/routing.js +36 -0
- package/dist/core/chat/session-key.d.ts +30 -0
- package/dist/core/chat/session-key.js +65 -0
- package/dist/core/chat/session-manager.d.ts +17 -0
- package/dist/core/chat/session-manager.js +23 -0
- package/dist/core/chat/text-chunker.d.ts +9 -0
- package/dist/core/chat/text-chunker.js +48 -0
- package/dist/core/chat/triage-router.d.ts +75 -0
- package/dist/core/chat/triage-router.js +142 -0
- package/dist/core/chat/types.d.ts +5 -0
- package/dist/core/chat/types.js +5 -0
- package/dist/core/config/defaults.d.ts +2 -0
- package/dist/core/config/defaults.js +38 -0
- package/dist/core/config/index.d.ts +6 -0
- package/dist/core/config/index.js +4 -0
- package/dist/core/config/interpolate.d.ts +5 -0
- package/dist/core/config/interpolate.js +27 -0
- package/dist/core/config/loader.d.ts +24 -0
- package/dist/core/config/loader.js +59 -0
- package/dist/core/config/schema.d.ts +5 -0
- package/dist/core/config/schema.js +119 -0
- package/dist/core/data/core-entity-contexts.d.ts +14 -0
- package/dist/core/data/core-entity-contexts.js +197 -0
- package/dist/core/data/core-migrations.d.ts +5 -0
- package/dist/core/data/core-migrations.js +45 -0
- package/dist/core/data/core-schema.d.ts +6 -0
- package/dist/core/data/core-schema.js +454 -0
- package/dist/core/data/data-store.d.ts +67 -0
- package/dist/core/data/data-store.js +218 -0
- package/dist/core/data/domain-entity-contexts.d.ts +29 -0
- package/dist/core/data/domain-entity-contexts.js +321 -0
- package/dist/core/data/domain-schema.d.ts +36 -0
- package/dist/core/data/domain-schema.js +323 -0
- package/dist/core/data/index.d.ts +7 -0
- package/dist/core/data/index.js +7 -0
- package/dist/core/data/types.d.ts +111 -0
- package/dist/core/data/types.js +1 -0
- package/dist/core/hooks/hook-bus.d.ts +18 -0
- package/dist/core/hooks/hook-bus.js +120 -0
- package/dist/core/hooks/index.d.ts +2 -0
- package/dist/core/hooks/index.js +1 -0
- package/dist/core/hooks/types.d.ts +19 -0
- package/dist/core/hooks/types.js +1 -0
- package/dist/core/index.d.ts +4 -0
- package/dist/core/index.js +4 -0
- package/dist/core/llm/auto-discovery.d.ts +11 -0
- package/dist/core/llm/auto-discovery.js +49 -0
- package/dist/core/llm/cost-tracker.d.ts +6 -0
- package/dist/core/llm/cost-tracker.js +38 -0
- package/dist/core/llm/index.d.ts +4 -0
- package/dist/core/llm/index.js +3 -0
- package/dist/core/llm/model-router.d.ts +25 -0
- package/dist/core/llm/model-router.js +49 -0
- package/dist/core/llm/provider-registry.d.ts +9 -0
- package/dist/core/llm/provider-registry.js +25 -0
- package/dist/core/llm/types.d.ts +2 -0
- package/dist/core/llm/types.js +2 -0
- package/dist/core/orchestrator/adapters/api-adapter.d.ts +34 -0
- package/dist/core/orchestrator/adapters/api-adapter.js +88 -0
- package/dist/core/orchestrator/adapters/cli-adapter.d.ts +22 -0
- package/dist/core/orchestrator/adapters/cli-adapter.js +69 -0
- package/dist/core/orchestrator/adapters/deterministic-adapter.d.ts +35 -0
- package/dist/core/orchestrator/adapters/deterministic-adapter.js +75 -0
- package/dist/core/orchestrator/adapters/env-whitelist.d.ts +4 -0
- package/dist/core/orchestrator/adapters/env-whitelist.js +27 -0
- package/dist/core/orchestrator/adapters/output-extractor.d.ts +11 -0
- package/dist/core/orchestrator/adapters/output-extractor.js +59 -0
- package/dist/core/orchestrator/adapters/process-manager.d.ts +15 -0
- package/dist/core/orchestrator/adapters/process-manager.js +26 -0
- package/dist/core/orchestrator/adapters/tool-loop.d.ts +22 -0
- package/dist/core/orchestrator/adapters/tool-loop.js +66 -0
- package/dist/core/orchestrator/agent-registry.d.ts +31 -0
- package/dist/core/orchestrator/agent-registry.js +135 -0
- package/dist/core/orchestrator/budget-controller.d.ts +19 -0
- package/dist/core/orchestrator/budget-controller.js +73 -0
- package/dist/core/orchestrator/chain-guard.d.ts +14 -0
- package/dist/core/orchestrator/chain-guard.js +23 -0
- package/dist/core/orchestrator/circuit-breaker.d.ts +65 -0
- package/dist/core/orchestrator/circuit-breaker.js +159 -0
- package/dist/core/orchestrator/claude-stream-parser.d.ts +31 -0
- package/dist/core/orchestrator/claude-stream-parser.js +99 -0
- package/dist/core/orchestrator/config-revisions.d.ts +6 -0
- package/dist/core/orchestrator/config-revisions.js +17 -0
- package/dist/core/orchestrator/dependency-resolver.d.ts +20 -0
- package/dist/core/orchestrator/dependency-resolver.js +78 -0
- package/dist/core/orchestrator/governance-gate.d.ts +110 -0
- package/dist/core/orchestrator/governance-gate.js +170 -0
- package/dist/core/orchestrator/learning-pipeline.d.ts +109 -0
- package/dist/core/orchestrator/learning-pipeline.js +249 -0
- package/dist/core/orchestrator/loop-detector.d.ts +51 -0
- package/dist/core/orchestrator/loop-detector.js +133 -0
- package/dist/core/orchestrator/ndjson-logger.d.ts +6 -0
- package/dist/core/orchestrator/ndjson-logger.js +18 -0
- package/dist/core/orchestrator/permission-relay.d.ts +72 -0
- package/dist/core/orchestrator/permission-relay.js +164 -0
- package/dist/core/orchestrator/run-manager.d.ts +31 -0
- package/dist/core/orchestrator/run-manager.js +178 -0
- package/dist/core/orchestrator/scheduler.d.ts +70 -0
- package/dist/core/orchestrator/scheduler.js +198 -0
- package/dist/core/orchestrator/secret-store.d.ts +57 -0
- package/dist/core/orchestrator/secret-store.js +171 -0
- package/dist/core/orchestrator/session-manager.d.ts +13 -0
- package/dist/core/orchestrator/session-manager.js +66 -0
- package/dist/core/orchestrator/task-queue.d.ts +34 -0
- package/dist/core/orchestrator/task-queue.js +83 -0
- package/dist/core/orchestrator/template-interpolate.d.ts +5 -0
- package/dist/core/orchestrator/template-interpolate.js +18 -0
- package/dist/core/orchestrator/user-registry.d.ts +47 -0
- package/dist/core/orchestrator/user-registry.js +76 -0
- package/dist/core/orchestrator/wakeup-queue.d.ts +9 -0
- package/dist/core/orchestrator/wakeup-queue.js +45 -0
- package/dist/core/orchestrator/workflow-engine.d.ts +47 -0
- package/dist/core/orchestrator/workflow-engine.js +204 -0
- package/dist/core/security/audit.d.ts +20 -0
- package/dist/core/security/audit.js +33 -0
- package/dist/core/security/column-validator.d.ts +20 -0
- package/dist/core/security/column-validator.js +37 -0
- package/dist/core/security/index.d.ts +5 -0
- package/dist/core/security/index.js +5 -0
- package/dist/core/security/process-env.d.ts +13 -0
- package/dist/core/security/process-env.js +49 -0
- package/dist/core/security/sanitizer.d.ts +11 -0
- package/dist/core/security/sanitizer.js +39 -0
- package/dist/core/security/types.d.ts +11 -0
- package/dist/core/security/types.js +1 -0
- package/dist/core/update/auto-update.d.ts +21 -0
- package/dist/core/update/auto-update.js +102 -0
- package/dist/core/update/backup-manager.d.ts +7 -0
- package/dist/core/update/backup-manager.js +24 -0
- package/dist/core/update/index.d.ts +8 -0
- package/dist/core/update/index.js +6 -0
- package/dist/core/update/migration-hooks.d.ts +11 -0
- package/dist/core/update/migration-hooks.js +10 -0
- package/dist/core/update/types.d.ts +11 -0
- package/dist/core/update/types.js +1 -0
- package/dist/core/update/update-checker.d.ts +11 -0
- package/dist/core/update/update-checker.js +63 -0
- package/dist/core/update/update-manager.d.ts +25 -0
- package/dist/core/update/update-manager.js +101 -0
- package/dist/core/update/version-utils.d.ts +6 -0
- package/dist/core/update/version-utils.js +34 -0
- package/dist/gmail-connector-2FVYTQJH.js +6 -0
- package/dist/gmail-connector-MNUBRNFM.js +6 -0
- package/dist/gmail-connector-PS2VLGNE.js +6 -0
- package/dist/gmail-connector-ULSMN6X2.js +6 -0
- package/dist/gmail-connector-URRFX6A3.js +6 -0
- package/dist/inbound-AFBUPSPG.js +10 -0
- package/dist/inbound-AFOHYNUY.js +6 -0
- package/dist/inbound-CGIXRXGC.js +8 -0
- package/dist/inbound-MCOLRH6U.js +10 -0
- package/dist/inbound-SNEMBLGA.js +6 -0
- package/dist/inbound-ZJHAYVMF.js +10 -0
- package/dist/index.d.ts +11 -1
- package/dist/index.js +27 -11
- package/dist/provider-qqJYv9nv.d.ts +75 -0
- package/dist/providers/anthropic/models.d.ts +2 -0
- package/dist/providers/anthropic/models.js +29 -0
- package/dist/providers/anthropic/provider.d.ts +13 -0
- package/dist/providers/anthropic/provider.js +119 -0
- package/dist/providers/anthropic/tool-converter.d.ts +10 -0
- package/dist/providers/anthropic/tool-converter.js +7 -0
- package/dist/providers/ollama/provider.d.ts +17 -0
- package/dist/providers/ollama/provider.js +185 -0
- package/dist/providers/openai/models.d.ts +2 -0
- package/dist/providers/openai/models.js +29 -0
- package/dist/providers/openai/provider.d.ts +13 -0
- package/dist/providers/openai/provider.js +163 -0
- package/dist/providers/openai/tool-converter.d.ts +10 -0
- package/dist/providers/openai/tool-converter.js +10 -0
- package/dist/shared/constants.d.ts +50 -0
- package/dist/shared/constants.js +64 -0
- package/dist/shared/index.d.ts +14 -0
- package/dist/shared/index.js +14 -0
- package/dist/shared/types/agent.d.ts +36 -0
- package/dist/shared/types/agent.js +2 -0
- package/dist/shared/types/channel.d.ts +70 -0
- package/dist/shared/types/channel.js +2 -0
- package/dist/shared/types/config.d.ts +111 -0
- package/dist/shared/types/config.js +2 -0
- package/dist/shared/types/connector.d.ts +77 -0
- package/dist/shared/types/connector.js +2 -0
- package/dist/shared/types/execution.d.ts +29 -0
- package/dist/shared/types/execution.js +2 -0
- package/dist/shared/types/provider.d.ts +73 -0
- package/dist/shared/types/provider.js +2 -0
- package/dist/shared/types/task.d.ts +47 -0
- package/dist/shared/types/task.js +2 -0
- package/dist/shared/types/workflow.d.ts +39 -0
- package/dist/shared/types/workflow.js +2 -0
- package/dist/shared/utils.d.ts +6 -0
- package/dist/shared/utils.js +13 -0
- package/dist/update-check.d.ts +5 -0
- package/dist/update-check.js +56 -0
- package/package.json +100 -100
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { detectCycle } from './dependency-resolver.js';
|
|
2
|
+
import { interpolate } from './template-interpolate.js';
|
|
3
|
+
export class WorkflowEngine {
|
|
4
|
+
db;
|
|
5
|
+
hooks;
|
|
6
|
+
taskQueue;
|
|
7
|
+
constructor(db, hooks, taskQueue) {
|
|
8
|
+
this.db = db;
|
|
9
|
+
this.hooks = hooks;
|
|
10
|
+
this.taskQueue = taskQueue;
|
|
11
|
+
// Subscribe to task.completed to advance workflow steps
|
|
12
|
+
this.hooks.register('task.completed', async (ctx) => {
|
|
13
|
+
const taskId = ctx['taskId'];
|
|
14
|
+
const output = ctx['output'] ?? '';
|
|
15
|
+
if (taskId) {
|
|
16
|
+
await this.onStepCompleted(taskId, output);
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Define/register a workflow.
|
|
22
|
+
*/
|
|
23
|
+
async define(slug, def) {
|
|
24
|
+
// Validate: no duplicate step IDs
|
|
25
|
+
const ids = def.steps.map((s) => s.id);
|
|
26
|
+
const unique = new Set(ids);
|
|
27
|
+
if (unique.size !== ids.length) {
|
|
28
|
+
throw new Error('Workflow has duplicate step IDs');
|
|
29
|
+
}
|
|
30
|
+
// Validate: no invalid dependsOn references
|
|
31
|
+
for (const step of def.steps) {
|
|
32
|
+
for (const dep of step.dependsOn ?? []) {
|
|
33
|
+
if (!unique.has(dep)) {
|
|
34
|
+
throw new Error(`Step "${step.id}" depends on unknown step "${dep}"`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// Validate: no cycles
|
|
39
|
+
if (detectCycle(def.steps)) {
|
|
40
|
+
throw new Error('Workflow has cyclic step dependencies');
|
|
41
|
+
}
|
|
42
|
+
// Store in workflows table
|
|
43
|
+
const existing = await this.db.query('workflows', { where: { slug } });
|
|
44
|
+
if (existing.length > 0) {
|
|
45
|
+
await this.db.update('workflows', { slug }, {
|
|
46
|
+
name: def.name,
|
|
47
|
+
description: def.description,
|
|
48
|
+
definition: JSON.stringify(def),
|
|
49
|
+
updated_at: new Date().toISOString(),
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
await this.db.insert('workflows', {
|
|
54
|
+
slug,
|
|
55
|
+
name: def.name,
|
|
56
|
+
description: def.description,
|
|
57
|
+
definition: JSON.stringify(def),
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Start a workflow run.
|
|
63
|
+
*/
|
|
64
|
+
async start(workflowSlug, context) {
|
|
65
|
+
const workflows = await this.db.query('workflows', { where: { slug: workflowSlug } });
|
|
66
|
+
if (workflows.length === 0) {
|
|
67
|
+
throw new Error(`Workflow not found: ${workflowSlug}`);
|
|
68
|
+
}
|
|
69
|
+
const workflow = workflows[0];
|
|
70
|
+
const def = JSON.parse(workflow['definition']);
|
|
71
|
+
// Create workflow_run record
|
|
72
|
+
const runRow = await this.db.insert('workflow_runs', {
|
|
73
|
+
workflow_id: workflow['id'],
|
|
74
|
+
status: 'running',
|
|
75
|
+
step_results: JSON.stringify({}),
|
|
76
|
+
context: JSON.stringify(context),
|
|
77
|
+
});
|
|
78
|
+
const workflowRunId = runRow['id'];
|
|
79
|
+
// Find initial steps (no dependsOn or empty dependsOn)
|
|
80
|
+
const initialSteps = def.steps.filter((s) => !s.dependsOn || s.dependsOn.length === 0);
|
|
81
|
+
for (const step of initialSteps) {
|
|
82
|
+
await this._createStepTask(step, workflowRunId, workflow['id'], context);
|
|
83
|
+
}
|
|
84
|
+
return workflowRunId;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Called when a task with workflow_run_id completes.
|
|
88
|
+
*/
|
|
89
|
+
async onStepCompleted(taskId, output) {
|
|
90
|
+
const task = await this.db.get('tasks', { id: taskId });
|
|
91
|
+
if (!task || !task['workflow_run_id'])
|
|
92
|
+
return;
|
|
93
|
+
const workflowRunId = task['workflow_run_id'];
|
|
94
|
+
const stepId = task['workflow_step_id'];
|
|
95
|
+
const run = await this.db.get('workflow_runs', { id: workflowRunId });
|
|
96
|
+
if (!run || run['status'] !== 'running')
|
|
97
|
+
return;
|
|
98
|
+
// Update step_results
|
|
99
|
+
const stepResults = JSON.parse(run['step_results'] ?? '{}');
|
|
100
|
+
if (stepId) {
|
|
101
|
+
stepResults[stepId] = { output, taskId };
|
|
102
|
+
}
|
|
103
|
+
await this.db.update('workflow_runs', { id: workflowRunId }, {
|
|
104
|
+
step_results: JSON.stringify(stepResults),
|
|
105
|
+
current_step: stepId,
|
|
106
|
+
});
|
|
107
|
+
// Find workflow definition
|
|
108
|
+
const workflow = await this.db.get('workflows', { id: run['workflow_id'] });
|
|
109
|
+
if (!workflow)
|
|
110
|
+
return;
|
|
111
|
+
const def = JSON.parse(workflow['definition']);
|
|
112
|
+
// Build context with step outputs
|
|
113
|
+
const runContext = JSON.parse(run['context'] ?? '{}');
|
|
114
|
+
const stepsContext = {};
|
|
115
|
+
for (const [sid, result] of Object.entries(stepResults)) {
|
|
116
|
+
stepsContext[sid] = result;
|
|
117
|
+
}
|
|
118
|
+
const fullContext = { ...runContext, steps: stepsContext };
|
|
119
|
+
// Find completed step IDs (all tasks for this run that succeeded)
|
|
120
|
+
const allRunTasks = await this.db.query('tasks', { where: { workflow_run_id: workflowRunId } });
|
|
121
|
+
const completedStepIds = new Set(allRunTasks
|
|
122
|
+
.filter((t) => t['status'] === 'done' || t['id'] === taskId)
|
|
123
|
+
.map((t) => t['workflow_step_id'])
|
|
124
|
+
.filter(Boolean));
|
|
125
|
+
// Mark current task's step as complete
|
|
126
|
+
if (stepId)
|
|
127
|
+
completedStepIds.add(stepId);
|
|
128
|
+
// Find next steps whose all dependsOn are satisfied and haven't been started
|
|
129
|
+
const startedStepIds = new Set(allRunTasks.map((t) => t['workflow_step_id']).filter(Boolean));
|
|
130
|
+
const nextSteps = def.steps.filter((s) => {
|
|
131
|
+
if (startedStepIds.has(s.id) && s.id !== stepId)
|
|
132
|
+
return false;
|
|
133
|
+
if (s.id === stepId)
|
|
134
|
+
return false; // Current step
|
|
135
|
+
if (!s.dependsOn || s.dependsOn.length === 0)
|
|
136
|
+
return false; // Already started as initial
|
|
137
|
+
return s.dependsOn.every((dep) => completedStepIds.has(dep));
|
|
138
|
+
});
|
|
139
|
+
if (nextSteps.length === 0) {
|
|
140
|
+
// Check if all steps are done
|
|
141
|
+
const allStepIds = new Set(def.steps.map((s) => s.id));
|
|
142
|
+
const allDone = [...allStepIds].every((id) => completedStepIds.has(id));
|
|
143
|
+
if (allDone) {
|
|
144
|
+
await this.db.update('workflow_runs', { id: workflowRunId }, {
|
|
145
|
+
status: 'completed',
|
|
146
|
+
completed_at: new Date().toISOString(),
|
|
147
|
+
});
|
|
148
|
+
await this.hooks.emit('workflow.completed', { workflowRunId });
|
|
149
|
+
}
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
for (const step of nextSteps) {
|
|
153
|
+
await this._createStepTask(step, workflowRunId, workflow['id'], fullContext);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Mark a workflow run as failed.
|
|
158
|
+
*/
|
|
159
|
+
async onStepFailed(taskId, error) {
|
|
160
|
+
const task = await this.db.get('tasks', { id: taskId });
|
|
161
|
+
if (!task || !task['workflow_run_id'])
|
|
162
|
+
return;
|
|
163
|
+
const workflowRunId = task['workflow_run_id'];
|
|
164
|
+
const stepId = task['workflow_step_id'];
|
|
165
|
+
const run = await this.db.get('workflow_runs', { id: workflowRunId });
|
|
166
|
+
if (!run || run['status'] !== 'running')
|
|
167
|
+
return;
|
|
168
|
+
// Find workflow definition to check onFail behavior
|
|
169
|
+
const workflow = await this.db.get('workflows', { id: run['workflow_id'] });
|
|
170
|
+
if (!workflow)
|
|
171
|
+
return;
|
|
172
|
+
const def = JSON.parse(workflow['definition']);
|
|
173
|
+
const step = stepId ? def.steps.find((s) => s.id === stepId) : undefined;
|
|
174
|
+
if (!step || step.onFail === 'abort' || !step.onFail) {
|
|
175
|
+
await this.db.update('workflow_runs', { id: workflowRunId }, {
|
|
176
|
+
status: 'failed',
|
|
177
|
+
error,
|
|
178
|
+
completed_at: new Date().toISOString(),
|
|
179
|
+
});
|
|
180
|
+
await this.hooks.emit('workflow.failed', { workflowRunId, error });
|
|
181
|
+
}
|
|
182
|
+
// skip/retry handled by retry policy in run-manager
|
|
183
|
+
}
|
|
184
|
+
async _createStepTask(step, workflowRunId, workflowId, context) {
|
|
185
|
+
// Find agent id by slug if agentSlug provided
|
|
186
|
+
let assigneeId;
|
|
187
|
+
if (step.agentSlug) {
|
|
188
|
+
const agents = await this.db.query('agents', { where: { slug: step.agentSlug } });
|
|
189
|
+
if (agents.length > 0) {
|
|
190
|
+
assigneeId = agents[0]['id'];
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
const title = interpolate(step.taskTemplate.title, context);
|
|
194
|
+
const description = interpolate(step.taskTemplate.description, context);
|
|
195
|
+
const taskId = await this.taskQueue.create({
|
|
196
|
+
title,
|
|
197
|
+
description,
|
|
198
|
+
assignee_id: assigneeId,
|
|
199
|
+
workflow_run_id: workflowRunId,
|
|
200
|
+
workflow_step_id: step.id,
|
|
201
|
+
});
|
|
202
|
+
return taskId;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { HookBus } from '../hooks/hook-bus.js';
|
|
2
|
+
import type { AuditEvent } from './types.js';
|
|
3
|
+
interface AuditEmitterOptions {
|
|
4
|
+
auditTables?: string[];
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Emits audit events via the HookBus for tracked tables.
|
|
8
|
+
*
|
|
9
|
+
* - auditTables defaults to [] (nothing audited)
|
|
10
|
+
* - '*' wildcard audits all tables
|
|
11
|
+
* - emit is fire-and-forget — errors are swallowed, no awaiting
|
|
12
|
+
*/
|
|
13
|
+
export declare class AuditEmitter {
|
|
14
|
+
private readonly hooks;
|
|
15
|
+
private readonly auditTables;
|
|
16
|
+
constructor(hooks: HookBus, opts?: AuditEmitterOptions);
|
|
17
|
+
shouldAudit(table: string): boolean;
|
|
18
|
+
emit(event: AuditEvent): void;
|
|
19
|
+
}
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Emits audit events via the HookBus for tracked tables.
|
|
3
|
+
*
|
|
4
|
+
* - auditTables defaults to [] (nothing audited)
|
|
5
|
+
* - '*' wildcard audits all tables
|
|
6
|
+
* - emit is fire-and-forget — errors are swallowed, no awaiting
|
|
7
|
+
*/
|
|
8
|
+
export class AuditEmitter {
|
|
9
|
+
hooks;
|
|
10
|
+
auditTables;
|
|
11
|
+
constructor(hooks, opts) {
|
|
12
|
+
this.hooks = hooks;
|
|
13
|
+
this.auditTables = opts?.auditTables ?? [];
|
|
14
|
+
}
|
|
15
|
+
shouldAudit(table) {
|
|
16
|
+
return this.auditTables.includes('*') || this.auditTables.includes(table);
|
|
17
|
+
}
|
|
18
|
+
emit(event) {
|
|
19
|
+
// Fire and forget — never throw, never await
|
|
20
|
+
try {
|
|
21
|
+
const context = event;
|
|
22
|
+
if (typeof this.hooks.emitSync === 'function') {
|
|
23
|
+
this.hooks.emitSync('audit', context);
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
void this.hooks.emit('audit', context);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// Swallow errors — audit must never break normal operation
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { DataStore } from '../data/data-store.js';
|
|
2
|
+
export interface ColumnValidator {
|
|
3
|
+
validateWrite(table: string, row: Record<string, unknown>): Record<string, unknown>;
|
|
4
|
+
validateRead(table: string, columns: string[]): void;
|
|
5
|
+
invalidateCache(table: string): void;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Validates column names against the live SQLite schema.
|
|
9
|
+
*
|
|
10
|
+
* - validateWrite: strips unknown columns silently
|
|
11
|
+
* - validateRead: throws on unknown columns
|
|
12
|
+
*/
|
|
13
|
+
export declare class ColumnValidatorImpl implements ColumnValidator {
|
|
14
|
+
private readonly db;
|
|
15
|
+
constructor(db: DataStore);
|
|
16
|
+
private getValidColumns;
|
|
17
|
+
validateWrite(table: string, row: Record<string, unknown>): Record<string, unknown>;
|
|
18
|
+
validateRead(table: string, columns: string[]): void;
|
|
19
|
+
invalidateCache(_table: string): void;
|
|
20
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validates column names against the live SQLite schema.
|
|
3
|
+
*
|
|
4
|
+
* - validateWrite: strips unknown columns silently
|
|
5
|
+
* - validateRead: throws on unknown columns
|
|
6
|
+
*/
|
|
7
|
+
export class ColumnValidatorImpl {
|
|
8
|
+
db;
|
|
9
|
+
constructor(db) {
|
|
10
|
+
this.db = db;
|
|
11
|
+
}
|
|
12
|
+
getValidColumns(table) {
|
|
13
|
+
const rows = this.db.tableInfo(table);
|
|
14
|
+
return new Set(rows.map(r => r.name));
|
|
15
|
+
}
|
|
16
|
+
validateWrite(table, row) {
|
|
17
|
+
const valid = this.getValidColumns(table);
|
|
18
|
+
const result = {};
|
|
19
|
+
for (const [col, val] of Object.entries(row)) {
|
|
20
|
+
if (valid.has(col)) {
|
|
21
|
+
result[col] = val;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
validateRead(table, columns) {
|
|
27
|
+
const valid = this.getValidColumns(table);
|
|
28
|
+
for (const col of columns) {
|
|
29
|
+
if (!valid.has(col)) {
|
|
30
|
+
throw new Error(`Unknown column: ${col} in table ${table}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
invalidateCache(_table) {
|
|
35
|
+
// No-op: DataStore.tableInfo() queries SQLite directly each time (no cache)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build a clean environment for spawned subprocesses.
|
|
3
|
+
* Strips secrets and passes only safe system variables.
|
|
4
|
+
* Used by the CLI execution adapter when spawning agent processes.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Build a filtered environment for subprocess execution.
|
|
8
|
+
* Only passes explicitly allowed variables — all secrets are stripped.
|
|
9
|
+
*
|
|
10
|
+
* @param allowedKeys - Additional keys to allow beyond the defaults
|
|
11
|
+
* @param inject - Extra key-value pairs to inject into the env
|
|
12
|
+
*/
|
|
13
|
+
export declare function buildProcessEnv(allowedKeys?: string[], inject?: Record<string, string>): Record<string, string>;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build a clean environment for spawned subprocesses.
|
|
3
|
+
* Strips secrets and passes only safe system variables.
|
|
4
|
+
* Used by the CLI execution adapter when spawning agent processes.
|
|
5
|
+
*/
|
|
6
|
+
const DEFAULT_ALLOWED_KEYS = new Set([
|
|
7
|
+
"PATH",
|
|
8
|
+
"HOME",
|
|
9
|
+
"USER",
|
|
10
|
+
"SHELL",
|
|
11
|
+
"LANG",
|
|
12
|
+
"TERM",
|
|
13
|
+
"TMPDIR",
|
|
14
|
+
"XDG_RUNTIME_DIR",
|
|
15
|
+
"NODE_ENV",
|
|
16
|
+
// Git
|
|
17
|
+
"GIT_AUTHOR_NAME",
|
|
18
|
+
"GIT_AUTHOR_EMAIL",
|
|
19
|
+
"GIT_COMMITTER_NAME",
|
|
20
|
+
"GIT_COMMITTER_EMAIL",
|
|
21
|
+
// Homebrew / system
|
|
22
|
+
"HOMEBREW_PREFIX",
|
|
23
|
+
"HOMEBREW_CELLAR",
|
|
24
|
+
"HOMEBREW_REPOSITORY",
|
|
25
|
+
]);
|
|
26
|
+
/**
|
|
27
|
+
* Build a filtered environment for subprocess execution.
|
|
28
|
+
* Only passes explicitly allowed variables — all secrets are stripped.
|
|
29
|
+
*
|
|
30
|
+
* @param allowedKeys - Additional keys to allow beyond the defaults
|
|
31
|
+
* @param inject - Extra key-value pairs to inject into the env
|
|
32
|
+
*/
|
|
33
|
+
export function buildProcessEnv(allowedKeys, inject) {
|
|
34
|
+
const allowed = new Set(DEFAULT_ALLOWED_KEYS);
|
|
35
|
+
if (allowedKeys) {
|
|
36
|
+
for (const k of allowedKeys)
|
|
37
|
+
allowed.add(k);
|
|
38
|
+
}
|
|
39
|
+
const env = {};
|
|
40
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
41
|
+
if (allowed.has(key) && value !== undefined) {
|
|
42
|
+
env[key] = value;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (inject) {
|
|
46
|
+
Object.assign(env, inject);
|
|
47
|
+
}
|
|
48
|
+
return env;
|
|
49
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SanitizerOptions } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Sanitizes a row object by:
|
|
4
|
+
* 1. Stripping null bytes from string values
|
|
5
|
+
* 2. Stripping control characters (preserving \n, \r, \t)
|
|
6
|
+
* 3. Truncating fields that exceed their byte length limit
|
|
7
|
+
*
|
|
8
|
+
* Non-string values pass through unchanged.
|
|
9
|
+
* Returns a new object — input is never mutated.
|
|
10
|
+
*/
|
|
11
|
+
export declare function sanitize(row: Record<string, unknown>, opts?: SanitizerOptions): Record<string, unknown>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Buffer } from 'node:buffer';
|
|
2
|
+
const DEFAULT_FIELD_LIMIT = 65535;
|
|
3
|
+
const DEFAULT_SUFFIX = '[truncated]';
|
|
4
|
+
/**
|
|
5
|
+
* Sanitizes a row object by:
|
|
6
|
+
* 1. Stripping null bytes from string values
|
|
7
|
+
* 2. Stripping control characters (preserving \n, \r, \t)
|
|
8
|
+
* 3. Truncating fields that exceed their byte length limit
|
|
9
|
+
*
|
|
10
|
+
* Non-string values pass through unchanged.
|
|
11
|
+
* Returns a new object — input is never mutated.
|
|
12
|
+
*/
|
|
13
|
+
export function sanitize(row, opts) {
|
|
14
|
+
const limits = opts?.fieldLengthLimits ?? {};
|
|
15
|
+
const suffix = opts?.truncateSuffix ?? DEFAULT_SUFFIX;
|
|
16
|
+
const result = {};
|
|
17
|
+
for (const [col, val] of Object.entries(row)) {
|
|
18
|
+
if (typeof val !== 'string') {
|
|
19
|
+
result[col] = val;
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
// 1. Strip null bytes
|
|
23
|
+
let s = val.replace(/\x00/g, '');
|
|
24
|
+
// 2. Strip control chars, preserving \n (0x0a), \r (0x0d), \t (0x09)
|
|
25
|
+
s = s.replace(/[\x01-\x08\x0b\x0c\x0e-\x1f\x7f]/g, '');
|
|
26
|
+
// 3. Enforce field length limit
|
|
27
|
+
const limit = limits[col] ?? DEFAULT_FIELD_LIMIT;
|
|
28
|
+
if (Buffer.byteLength(s) > limit) {
|
|
29
|
+
// Truncate to fit within limit bytes (accounting for suffix)
|
|
30
|
+
const suffixBytes = Buffer.byteLength(suffix);
|
|
31
|
+
const maxContentBytes = limit - suffixBytes;
|
|
32
|
+
// Slice the string to fit within maxContentBytes
|
|
33
|
+
const buf = Buffer.from(s);
|
|
34
|
+
s = buf.slice(0, maxContentBytes).toString() + suffix;
|
|
35
|
+
}
|
|
36
|
+
result[col] = s;
|
|
37
|
+
}
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface SanitizerOptions {
|
|
2
|
+
fieldLengthLimits?: Record<string, number>;
|
|
3
|
+
truncateSuffix?: string;
|
|
4
|
+
}
|
|
5
|
+
export interface AuditEvent {
|
|
6
|
+
table: string;
|
|
7
|
+
operation: 'insert' | 'update' | 'delete';
|
|
8
|
+
pk: unknown;
|
|
9
|
+
timestamp: number;
|
|
10
|
+
changedColumns?: string[];
|
|
11
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
interface UpdateResult {
|
|
2
|
+
updated: boolean;
|
|
3
|
+
packages: Array<{
|
|
4
|
+
name: string;
|
|
5
|
+
from: string;
|
|
6
|
+
to: string;
|
|
7
|
+
}>;
|
|
8
|
+
restartRequired: boolean;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Check npm for newer versions of framework packages and install them.
|
|
12
|
+
* Returns what was updated. Safe to call on every startup — skips if
|
|
13
|
+
* already on latest.
|
|
14
|
+
*
|
|
15
|
+
* @param packages - Package names to check (default: botinabox + latticesql)
|
|
16
|
+
* @param opts.quiet - Suppress console output (default: false)
|
|
17
|
+
*/
|
|
18
|
+
export declare function autoUpdate(packages?: string[], opts?: {
|
|
19
|
+
quiet?: boolean;
|
|
20
|
+
}): Promise<UpdateResult>;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-update — checks npm for newer versions of botinabox and its
|
|
3
|
+
* dependencies, installs them if outdated. Call at app startup before
|
|
4
|
+
* initializing the framework.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* import { autoUpdate } from 'botinabox';
|
|
8
|
+
* await autoUpdate(); // checks + installs if needed
|
|
9
|
+
*/
|
|
10
|
+
import { execFileSync } from "node:child_process";
|
|
11
|
+
import { readFileSync } from "node:fs";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
/** Strict semver pattern — rejects anything that isn't a clean version string */
|
|
14
|
+
const SEMVER_RE = /^\d+\.\d+\.\d+(-[\w.]+)?$/;
|
|
15
|
+
/**
|
|
16
|
+
* Get the installed version of a package from node_modules.
|
|
17
|
+
*/
|
|
18
|
+
function getInstalledVersion(pkgName) {
|
|
19
|
+
try {
|
|
20
|
+
const pkgPath = join(process.cwd(), "node_modules", pkgName, "package.json");
|
|
21
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
22
|
+
return pkg.version;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Get the latest version from the npm registry.
|
|
30
|
+
*/
|
|
31
|
+
async function getLatestVersion(pkgName) {
|
|
32
|
+
try {
|
|
33
|
+
const res = await fetch(`https://registry.npmjs.org/${pkgName}/latest`, {
|
|
34
|
+
headers: { accept: "application/json" },
|
|
35
|
+
signal: AbortSignal.timeout(5000),
|
|
36
|
+
});
|
|
37
|
+
if (!res.ok)
|
|
38
|
+
return null;
|
|
39
|
+
const data = (await res.json());
|
|
40
|
+
return data.version;
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function isNewer(latest, current) {
|
|
47
|
+
const a = latest.split(".").map(Number);
|
|
48
|
+
const b = current.split(".").map(Number);
|
|
49
|
+
for (let i = 0; i < Math.max(a.length, b.length); i++) {
|
|
50
|
+
if ((a[i] ?? 0) > (b[i] ?? 0))
|
|
51
|
+
return true;
|
|
52
|
+
if ((a[i] ?? 0) < (b[i] ?? 0))
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Check npm for newer versions of framework packages and install them.
|
|
59
|
+
* Returns what was updated. Safe to call on every startup — skips if
|
|
60
|
+
* already on latest.
|
|
61
|
+
*
|
|
62
|
+
* @param packages - Package names to check (default: botinabox + latticesql)
|
|
63
|
+
* @param opts.quiet - Suppress console output (default: false)
|
|
64
|
+
*/
|
|
65
|
+
export async function autoUpdate(packages = ["botinabox", "latticesql"], opts) {
|
|
66
|
+
const log = opts?.quiet ? () => { } : console.log;
|
|
67
|
+
const result = { updated: false, packages: [], restartRequired: false };
|
|
68
|
+
const toInstall = [];
|
|
69
|
+
for (const pkg of packages) {
|
|
70
|
+
const installed = getInstalledVersion(pkg);
|
|
71
|
+
if (!installed)
|
|
72
|
+
continue;
|
|
73
|
+
const latest = await getLatestVersion(pkg);
|
|
74
|
+
if (!latest)
|
|
75
|
+
continue;
|
|
76
|
+
if (isNewer(latest, installed)) {
|
|
77
|
+
if (!SEMVER_RE.test(latest)) {
|
|
78
|
+
console.error(`[autoUpdate] Rejecting invalid version "${latest}" for ${pkg}`);
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
toInstall.push(`${pkg}@${latest}`);
|
|
82
|
+
result.packages.push({ name: pkg, from: installed, to: latest });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (toInstall.length === 0)
|
|
86
|
+
return result;
|
|
87
|
+
log(`[autoUpdate] Updating: ${toInstall.join(", ")}`);
|
|
88
|
+
try {
|
|
89
|
+
execFileSync("npm", ["install", ...toInstall], {
|
|
90
|
+
cwd: process.cwd(),
|
|
91
|
+
stdio: opts?.quiet ? "ignore" : "inherit",
|
|
92
|
+
timeout: 60_000,
|
|
93
|
+
});
|
|
94
|
+
result.updated = true;
|
|
95
|
+
result.restartRequired = true;
|
|
96
|
+
log(`[autoUpdate] Updated successfully. Restart required for changes to take effect.`);
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
console.error("[autoUpdate] Failed to install updates:", err);
|
|
100
|
+
}
|
|
101
|
+
return result;
|
|
102
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { copyFileSync, mkdirSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { rmSync } from 'fs';
|
|
4
|
+
export class BackupManager {
|
|
5
|
+
projectRoot;
|
|
6
|
+
constructor(projectRoot) {
|
|
7
|
+
this.projectRoot = projectRoot;
|
|
8
|
+
}
|
|
9
|
+
async backup() {
|
|
10
|
+
const backupDir = join(this.projectRoot, '.botinabox-backup');
|
|
11
|
+
mkdirSync(backupDir, { recursive: true });
|
|
12
|
+
const src = join(this.projectRoot, 'pnpm-lock.yaml');
|
|
13
|
+
const dest = join(backupDir, 'pnpm-lock.yaml.bak');
|
|
14
|
+
copyFileSync(src, dest);
|
|
15
|
+
return dest;
|
|
16
|
+
}
|
|
17
|
+
async restore(backupPath) {
|
|
18
|
+
const dest = join(this.projectRoot, 'pnpm-lock.yaml');
|
|
19
|
+
copyFileSync(backupPath, dest);
|
|
20
|
+
}
|
|
21
|
+
async cleanup(backupPath) {
|
|
22
|
+
rmSync(backupPath, { force: true });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type { PackageUpdate, UpdateManifest } from './types.js';
|
|
2
|
+
export { parseVersion, compareVersions, classifyUpdate } from './version-utils.js';
|
|
3
|
+
export { UpdateChecker } from './update-checker.js';
|
|
4
|
+
export { BackupManager } from './backup-manager.js';
|
|
5
|
+
export { UpdateManager } from './update-manager.js';
|
|
6
|
+
export { runPackageMigrations } from './migration-hooks.js';
|
|
7
|
+
export type { PackageMigration } from './migration-hooks.js';
|
|
8
|
+
export { autoUpdate } from './auto-update.js';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { parseVersion, compareVersions, classifyUpdate } from './version-utils.js';
|
|
2
|
+
export { UpdateChecker } from './update-checker.js';
|
|
3
|
+
export { BackupManager } from './backup-manager.js';
|
|
4
|
+
export { UpdateManager } from './update-manager.js';
|
|
5
|
+
export { runPackageMigrations } from './migration-hooks.js';
|
|
6
|
+
export { autoUpdate } from './auto-update.js';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { DataStore } from '../data/data-store.js';
|
|
2
|
+
export interface PackageMigration {
|
|
3
|
+
version: string;
|
|
4
|
+
package: string;
|
|
5
|
+
sql: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Runs package migrations using the __lattice_migrations table for tracking.
|
|
9
|
+
* Each migration is keyed by "{package}:{version}" to ensure idempotency.
|
|
10
|
+
*/
|
|
11
|
+
export declare function runPackageMigrations(db: DataStore, migrations: PackageMigration[]): Promise<void>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runs package migrations using the __lattice_migrations table for tracking.
|
|
3
|
+
* Each migration is keyed by "{package}:{version}" to ensure idempotency.
|
|
4
|
+
*/
|
|
5
|
+
export async function runPackageMigrations(db, migrations) {
|
|
6
|
+
await db.migrate(migrations.map((m) => ({
|
|
7
|
+
version: `${m.package}:${m.version}`,
|
|
8
|
+
sql: m.sql,
|
|
9
|
+
})));
|
|
10
|
+
}
|