@usejarvis/brain 0.1.0
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 +153 -0
- package/README.md +278 -0
- package/bin/jarvis.ts +413 -0
- package/package.json +74 -0
- package/scripts/ensure-bun.cjs +8 -0
- package/src/actions/README.md +421 -0
- package/src/actions/app-control/desktop-controller.test.ts +26 -0
- package/src/actions/app-control/desktop-controller.ts +438 -0
- package/src/actions/app-control/interface.ts +64 -0
- package/src/actions/app-control/linux.ts +273 -0
- package/src/actions/app-control/macos.ts +54 -0
- package/src/actions/app-control/sidecar-launcher.test.ts +23 -0
- package/src/actions/app-control/sidecar-launcher.ts +286 -0
- package/src/actions/app-control/windows.ts +44 -0
- package/src/actions/browser/cdp.ts +138 -0
- package/src/actions/browser/chrome-launcher.ts +252 -0
- package/src/actions/browser/session.ts +437 -0
- package/src/actions/browser/stealth.ts +49 -0
- package/src/actions/index.ts +20 -0
- package/src/actions/terminal/executor.ts +157 -0
- package/src/actions/terminal/wsl-bridge.ts +126 -0
- package/src/actions/test.ts +93 -0
- package/src/actions/tools/agents.ts +321 -0
- package/src/actions/tools/builtin.ts +846 -0
- package/src/actions/tools/commitments.ts +192 -0
- package/src/actions/tools/content.ts +217 -0
- package/src/actions/tools/delegate.ts +147 -0
- package/src/actions/tools/desktop.test.ts +55 -0
- package/src/actions/tools/desktop.ts +305 -0
- package/src/actions/tools/goals.ts +376 -0
- package/src/actions/tools/local-tools-guard.ts +20 -0
- package/src/actions/tools/registry.ts +171 -0
- package/src/actions/tools/research.ts +111 -0
- package/src/actions/tools/sidecar-list.ts +57 -0
- package/src/actions/tools/sidecar-route.ts +105 -0
- package/src/actions/tools/workflows.ts +216 -0
- package/src/agents/agent.ts +132 -0
- package/src/agents/delegation.ts +107 -0
- package/src/agents/hierarchy.ts +113 -0
- package/src/agents/index.ts +19 -0
- package/src/agents/messaging.ts +125 -0
- package/src/agents/orchestrator.ts +576 -0
- package/src/agents/role-discovery.ts +61 -0
- package/src/agents/sub-agent-runner.ts +307 -0
- package/src/agents/task-manager.ts +151 -0
- package/src/authority/approval-delivery.ts +59 -0
- package/src/authority/approval.ts +196 -0
- package/src/authority/audit.ts +158 -0
- package/src/authority/authority.test.ts +519 -0
- package/src/authority/deferred-executor.ts +103 -0
- package/src/authority/emergency.ts +66 -0
- package/src/authority/engine.ts +297 -0
- package/src/authority/index.ts +12 -0
- package/src/authority/learning.ts +111 -0
- package/src/authority/tool-action-map.ts +74 -0
- package/src/awareness/analytics.ts +466 -0
- package/src/awareness/awareness.test.ts +332 -0
- package/src/awareness/capture-engine.ts +305 -0
- package/src/awareness/context-graph.ts +130 -0
- package/src/awareness/context-tracker.ts +349 -0
- package/src/awareness/index.ts +25 -0
- package/src/awareness/intelligence.ts +321 -0
- package/src/awareness/ocr-engine.ts +88 -0
- package/src/awareness/service.ts +528 -0
- package/src/awareness/struggle-detector.ts +342 -0
- package/src/awareness/suggestion-engine.ts +476 -0
- package/src/awareness/types.ts +201 -0
- package/src/cli/autostart.ts +241 -0
- package/src/cli/deps.ts +449 -0
- package/src/cli/doctor.ts +230 -0
- package/src/cli/helpers.ts +401 -0
- package/src/cli/onboard.ts +580 -0
- package/src/comms/README.md +329 -0
- package/src/comms/auth-error.html +48 -0
- package/src/comms/channels/discord.ts +228 -0
- package/src/comms/channels/signal.ts +56 -0
- package/src/comms/channels/telegram.ts +316 -0
- package/src/comms/channels/whatsapp.ts +60 -0
- package/src/comms/channels.test.ts +173 -0
- package/src/comms/desktop-notify.ts +114 -0
- package/src/comms/example.ts +129 -0
- package/src/comms/index.ts +129 -0
- package/src/comms/streaming.ts +142 -0
- package/src/comms/voice.test.ts +152 -0
- package/src/comms/voice.ts +291 -0
- package/src/comms/websocket.test.ts +409 -0
- package/src/comms/websocket.ts +473 -0
- package/src/config/README.md +387 -0
- package/src/config/index.ts +6 -0
- package/src/config/loader.test.ts +137 -0
- package/src/config/loader.ts +142 -0
- package/src/config/types.ts +260 -0
- package/src/daemon/README.md +232 -0
- package/src/daemon/agent-service-interface.ts +9 -0
- package/src/daemon/agent-service.ts +600 -0
- package/src/daemon/api-routes.ts +2119 -0
- package/src/daemon/background-agent-service.ts +396 -0
- package/src/daemon/background-agent.test.ts +78 -0
- package/src/daemon/channel-service.ts +201 -0
- package/src/daemon/commitment-executor.ts +297 -0
- package/src/daemon/event-classifier.ts +239 -0
- package/src/daemon/event-coalescer.ts +123 -0
- package/src/daemon/event-reactor.ts +214 -0
- package/src/daemon/health.ts +220 -0
- package/src/daemon/index.ts +1004 -0
- package/src/daemon/llm-settings.ts +316 -0
- package/src/daemon/observer-service.ts +150 -0
- package/src/daemon/pid.ts +98 -0
- package/src/daemon/research-queue.ts +155 -0
- package/src/daemon/services.ts +175 -0
- package/src/daemon/ws-service.ts +788 -0
- package/src/goals/accountability.ts +240 -0
- package/src/goals/awareness-bridge.ts +185 -0
- package/src/goals/estimator.ts +185 -0
- package/src/goals/events.ts +28 -0
- package/src/goals/goals.test.ts +400 -0
- package/src/goals/integration.test.ts +329 -0
- package/src/goals/nl-builder.test.ts +220 -0
- package/src/goals/nl-builder.ts +256 -0
- package/src/goals/rhythm.test.ts +177 -0
- package/src/goals/rhythm.ts +275 -0
- package/src/goals/service.test.ts +135 -0
- package/src/goals/service.ts +348 -0
- package/src/goals/types.ts +106 -0
- package/src/goals/workflow-bridge.ts +96 -0
- package/src/integrations/google-api.ts +134 -0
- package/src/integrations/google-auth.ts +175 -0
- package/src/llm/README.md +291 -0
- package/src/llm/anthropic.ts +386 -0
- package/src/llm/gemini.ts +371 -0
- package/src/llm/index.ts +19 -0
- package/src/llm/manager.ts +153 -0
- package/src/llm/ollama.ts +307 -0
- package/src/llm/openai.ts +350 -0
- package/src/llm/provider.test.ts +231 -0
- package/src/llm/provider.ts +60 -0
- package/src/llm/test.ts +87 -0
- package/src/observers/README.md +278 -0
- package/src/observers/calendar.ts +113 -0
- package/src/observers/clipboard.ts +136 -0
- package/src/observers/email.ts +109 -0
- package/src/observers/example.ts +58 -0
- package/src/observers/file-watcher.ts +124 -0
- package/src/observers/index.ts +159 -0
- package/src/observers/notifications.ts +197 -0
- package/src/observers/observers.test.ts +203 -0
- package/src/observers/processes.ts +225 -0
- package/src/personality/README.md +61 -0
- package/src/personality/adapter.ts +196 -0
- package/src/personality/index.ts +20 -0
- package/src/personality/learner.ts +209 -0
- package/src/personality/model.ts +132 -0
- package/src/personality/personality.test.ts +236 -0
- package/src/roles/README.md +252 -0
- package/src/roles/authority.ts +119 -0
- package/src/roles/example-usage.ts +198 -0
- package/src/roles/index.ts +42 -0
- package/src/roles/loader.ts +143 -0
- package/src/roles/prompt-builder.ts +194 -0
- package/src/roles/test-multi.ts +102 -0
- package/src/roles/test-role.yaml +77 -0
- package/src/roles/test-utils.ts +93 -0
- package/src/roles/test.ts +106 -0
- package/src/roles/tool-guide.ts +190 -0
- package/src/roles/types.ts +36 -0
- package/src/roles/utils.ts +200 -0
- package/src/scripts/google-setup.ts +168 -0
- package/src/sidecar/connection.ts +179 -0
- package/src/sidecar/index.ts +6 -0
- package/src/sidecar/manager.ts +542 -0
- package/src/sidecar/protocol.ts +85 -0
- package/src/sidecar/rpc.ts +161 -0
- package/src/sidecar/scheduler.ts +136 -0
- package/src/sidecar/types.ts +112 -0
- package/src/sidecar/validator.ts +144 -0
- package/src/vault/README.md +110 -0
- package/src/vault/awareness.ts +341 -0
- package/src/vault/commitments.ts +299 -0
- package/src/vault/content-pipeline.ts +260 -0
- package/src/vault/conversations.ts +173 -0
- package/src/vault/entities.ts +180 -0
- package/src/vault/extractor.test.ts +356 -0
- package/src/vault/extractor.ts +345 -0
- package/src/vault/facts.ts +190 -0
- package/src/vault/goals.ts +477 -0
- package/src/vault/index.ts +87 -0
- package/src/vault/keychain.ts +99 -0
- package/src/vault/observations.ts +115 -0
- package/src/vault/relationships.ts +178 -0
- package/src/vault/retrieval.test.ts +126 -0
- package/src/vault/retrieval.ts +227 -0
- package/src/vault/schema.ts +658 -0
- package/src/vault/settings.ts +38 -0
- package/src/vault/vectors.ts +92 -0
- package/src/vault/workflows.ts +403 -0
- package/src/workflows/auto-suggest.ts +290 -0
- package/src/workflows/engine.ts +366 -0
- package/src/workflows/events.ts +24 -0
- package/src/workflows/executor.ts +207 -0
- package/src/workflows/nl-builder.ts +198 -0
- package/src/workflows/nodes/actions/agent-task.ts +73 -0
- package/src/workflows/nodes/actions/calendar-action.ts +85 -0
- package/src/workflows/nodes/actions/code-execution.ts +73 -0
- package/src/workflows/nodes/actions/discord.ts +77 -0
- package/src/workflows/nodes/actions/file-write.ts +73 -0
- package/src/workflows/nodes/actions/gmail.ts +69 -0
- package/src/workflows/nodes/actions/http-request.ts +117 -0
- package/src/workflows/nodes/actions/notification.ts +85 -0
- package/src/workflows/nodes/actions/run-tool.ts +55 -0
- package/src/workflows/nodes/actions/send-message.ts +82 -0
- package/src/workflows/nodes/actions/shell-command.ts +76 -0
- package/src/workflows/nodes/actions/telegram.ts +60 -0
- package/src/workflows/nodes/builtin.ts +119 -0
- package/src/workflows/nodes/error/error-handler.ts +37 -0
- package/src/workflows/nodes/error/fallback.ts +47 -0
- package/src/workflows/nodes/error/retry.ts +82 -0
- package/src/workflows/nodes/logic/delay.ts +42 -0
- package/src/workflows/nodes/logic/if-else.ts +41 -0
- package/src/workflows/nodes/logic/loop.ts +90 -0
- package/src/workflows/nodes/logic/merge.ts +38 -0
- package/src/workflows/nodes/logic/race.ts +40 -0
- package/src/workflows/nodes/logic/switch.ts +59 -0
- package/src/workflows/nodes/logic/template-render.ts +53 -0
- package/src/workflows/nodes/logic/variable-get.ts +37 -0
- package/src/workflows/nodes/logic/variable-set.ts +59 -0
- package/src/workflows/nodes/registry.ts +99 -0
- package/src/workflows/nodes/transform/aggregate.ts +99 -0
- package/src/workflows/nodes/transform/csv-parse.ts +70 -0
- package/src/workflows/nodes/transform/json-parse.ts +63 -0
- package/src/workflows/nodes/transform/map-filter.ts +84 -0
- package/src/workflows/nodes/transform/regex-match.ts +89 -0
- package/src/workflows/nodes/triggers/calendar.ts +33 -0
- package/src/workflows/nodes/triggers/clipboard.ts +32 -0
- package/src/workflows/nodes/triggers/cron.ts +40 -0
- package/src/workflows/nodes/triggers/email.ts +40 -0
- package/src/workflows/nodes/triggers/file-change.ts +45 -0
- package/src/workflows/nodes/triggers/git.ts +46 -0
- package/src/workflows/nodes/triggers/manual.ts +23 -0
- package/src/workflows/nodes/triggers/poll.ts +81 -0
- package/src/workflows/nodes/triggers/process.ts +44 -0
- package/src/workflows/nodes/triggers/screen-event.ts +37 -0
- package/src/workflows/nodes/triggers/webhook.ts +39 -0
- package/src/workflows/safe-eval.ts +139 -0
- package/src/workflows/template.ts +118 -0
- package/src/workflows/triggers/cron.ts +311 -0
- package/src/workflows/triggers/manager.ts +285 -0
- package/src/workflows/triggers/observer-bridge.ts +172 -0
- package/src/workflows/triggers/poller.ts +201 -0
- package/src/workflows/triggers/screen-condition.ts +218 -0
- package/src/workflows/triggers/triggers.test.ts +740 -0
- package/src/workflows/triggers/webhook.ts +191 -0
- package/src/workflows/types.ts +133 -0
- package/src/workflows/variables.ts +72 -0
- package/src/workflows/workflows.test.ts +383 -0
- package/src/workflows/yaml.ts +104 -0
- package/ui/dist/index-j75njzc1.css +1199 -0
- package/ui/dist/index-p2zh407q.js +80603 -0
- package/ui/dist/index.html +13 -0
- package/ui/public/openwakeword/models/embedding_model.onnx +0 -0
- package/ui/public/openwakeword/models/hey_jarvis_v0.1.onnx +0 -0
- package/ui/public/openwakeword/models/melspectrogram.onnx +0 -0
- package/ui/public/openwakeword/models/silero_vad.onnx +0 -0
- package/ui/public/ort/ort-wasm-simd-threaded.jsep.mjs +106 -0
- package/ui/public/ort/ort-wasm-simd-threaded.jsep.wasm +0 -0
- package/ui/public/ort/ort-wasm-simd-threaded.mjs +59 -0
- package/ui/public/ort/ort-wasm-simd-threaded.wasm +0 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { getDb, generateId } from './schema.ts';
|
|
2
|
+
|
|
3
|
+
export type VectorRecord = {
|
|
4
|
+
id: string;
|
|
5
|
+
ref_type: string;
|
|
6
|
+
ref_id: string;
|
|
7
|
+
embedding: Float32Array;
|
|
8
|
+
model: string;
|
|
9
|
+
created_at: number;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
type VectorRow = {
|
|
13
|
+
id: string;
|
|
14
|
+
ref_type: string;
|
|
15
|
+
ref_id: string;
|
|
16
|
+
embedding: ArrayBuffer;
|
|
17
|
+
model: string;
|
|
18
|
+
created_at: number;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Parse vector row from database, converting BLOB to Float32Array
|
|
23
|
+
*/
|
|
24
|
+
function parseVector(row: VectorRow): VectorRecord {
|
|
25
|
+
return {
|
|
26
|
+
...row,
|
|
27
|
+
embedding: new Float32Array(row.embedding),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Store a vector embedding for a reference entity or fact
|
|
33
|
+
*/
|
|
34
|
+
export function storeVector(
|
|
35
|
+
ref_type: string,
|
|
36
|
+
ref_id: string,
|
|
37
|
+
embedding: Float32Array,
|
|
38
|
+
model: string
|
|
39
|
+
): VectorRecord {
|
|
40
|
+
const db = getDb();
|
|
41
|
+
const id = generateId();
|
|
42
|
+
const now = Date.now();
|
|
43
|
+
|
|
44
|
+
// Convert Float32Array to Buffer for SQLite BLOB storage
|
|
45
|
+
const buffer = Buffer.from(embedding.buffer);
|
|
46
|
+
|
|
47
|
+
const stmt = db.prepare(
|
|
48
|
+
'INSERT INTO vectors (id, ref_type, ref_id, embedding, model, created_at) VALUES (?, ?, ?, ?, ?, ?)'
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
stmt.run(id, ref_type, ref_id, buffer, model, now);
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
id,
|
|
55
|
+
ref_type,
|
|
56
|
+
ref_id,
|
|
57
|
+
embedding,
|
|
58
|
+
model,
|
|
59
|
+
created_at: now,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Find similar vectors using cosine similarity
|
|
65
|
+
*
|
|
66
|
+
* TODO: This is a stub implementation. For production use, integrate sqlite-vec extension
|
|
67
|
+
* which provides optimized vector similarity search with HNSW indexing.
|
|
68
|
+
*
|
|
69
|
+
* See: https://github.com/asg017/sqlite-vec
|
|
70
|
+
*
|
|
71
|
+
* Example with sqlite-vec:
|
|
72
|
+
* SELECT ref_type, ref_id, vec_distance_cosine(embedding, ?) as similarity
|
|
73
|
+
* FROM vectors
|
|
74
|
+
* ORDER BY similarity DESC
|
|
75
|
+
* LIMIT ?
|
|
76
|
+
*/
|
|
77
|
+
export function findSimilar(
|
|
78
|
+
embedding: Float32Array,
|
|
79
|
+
limit: number = 10
|
|
80
|
+
): Array<{ ref_type: string; ref_id: string; similarity: number }> {
|
|
81
|
+
// TODO: Implement vector similarity search with sqlite-vec extension
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Delete all vectors for a given reference
|
|
87
|
+
*/
|
|
88
|
+
export function deleteVectors(ref_type: string, ref_id: string): void {
|
|
89
|
+
const db = getDb();
|
|
90
|
+
const stmt = db.prepare('DELETE FROM vectors WHERE ref_type = ? AND ref_id = ?');
|
|
91
|
+
stmt.run(ref_type, ref_id);
|
|
92
|
+
}
|
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow Vault — CRUD operations for workflow automation engine
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { getDb, generateId } from './schema.ts';
|
|
6
|
+
import type {
|
|
7
|
+
Workflow, WorkflowVersion, WorkflowExecution, WorkflowStepResult,
|
|
8
|
+
WorkflowDefinition, ExecutionStatus, StepStatus,
|
|
9
|
+
} from '../workflows/types.ts';
|
|
10
|
+
|
|
11
|
+
// ── Row types (raw DB) ──
|
|
12
|
+
|
|
13
|
+
type WorkflowRow = Omit<Workflow, 'enabled' | 'authority_approved' | 'tags'> & {
|
|
14
|
+
enabled: number;
|
|
15
|
+
authority_approved: number;
|
|
16
|
+
tags: string | null;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
type VersionRow = Omit<WorkflowVersion, 'definition'> & { definition: string };
|
|
20
|
+
type ExecutionRow = Omit<WorkflowExecution, 'trigger_data' | 'variables'> & {
|
|
21
|
+
trigger_data: string | null;
|
|
22
|
+
variables: string | null;
|
|
23
|
+
};
|
|
24
|
+
type StepRow = Omit<WorkflowStepResult, 'input_data' | 'output_data'> & {
|
|
25
|
+
input_data: string | null;
|
|
26
|
+
output_data: string | null;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// ── Parsers ──
|
|
30
|
+
|
|
31
|
+
function parseWorkflow(row: WorkflowRow): Workflow {
|
|
32
|
+
return {
|
|
33
|
+
...row,
|
|
34
|
+
enabled: row.enabled === 1,
|
|
35
|
+
authority_approved: row.authority_approved === 1,
|
|
36
|
+
tags: row.tags ? JSON.parse(row.tags) : [],
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function parseVersion(row: VersionRow): WorkflowVersion {
|
|
41
|
+
return { ...row, definition: JSON.parse(row.definition) };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function parseExecution(row: ExecutionRow): WorkflowExecution {
|
|
45
|
+
return {
|
|
46
|
+
...row,
|
|
47
|
+
trigger_data: row.trigger_data ? JSON.parse(row.trigger_data) : null,
|
|
48
|
+
variables: row.variables ? JSON.parse(row.variables) : {},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function parseStep(row: StepRow): WorkflowStepResult {
|
|
53
|
+
return {
|
|
54
|
+
...row,
|
|
55
|
+
input_data: row.input_data ? JSON.parse(row.input_data) : null,
|
|
56
|
+
output_data: row.output_data ? JSON.parse(row.output_data) : null,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ── Workflows ──
|
|
61
|
+
|
|
62
|
+
export function createWorkflow(
|
|
63
|
+
name: string,
|
|
64
|
+
opts?: {
|
|
65
|
+
description?: string;
|
|
66
|
+
authority_level?: number;
|
|
67
|
+
tags?: string[];
|
|
68
|
+
enabled?: boolean;
|
|
69
|
+
}
|
|
70
|
+
): Workflow {
|
|
71
|
+
const db = getDb();
|
|
72
|
+
const id = generateId();
|
|
73
|
+
const now = Date.now();
|
|
74
|
+
|
|
75
|
+
db.prepare(
|
|
76
|
+
`INSERT INTO workflows (id, name, description, enabled, authority_level, authority_approved, tags, current_version, execution_count, created_at, updated_at)
|
|
77
|
+
VALUES (?, ?, ?, ?, ?, 0, ?, 1, 0, ?, ?)`
|
|
78
|
+
).run(
|
|
79
|
+
id, name,
|
|
80
|
+
opts?.description ?? '',
|
|
81
|
+
opts?.enabled !== false ? 1 : 0,
|
|
82
|
+
opts?.authority_level ?? 3,
|
|
83
|
+
opts?.tags ? JSON.stringify(opts.tags) : null,
|
|
84
|
+
now, now,
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
id, name,
|
|
89
|
+
description: opts?.description ?? '',
|
|
90
|
+
enabled: opts?.enabled !== false,
|
|
91
|
+
authority_level: opts?.authority_level ?? 3,
|
|
92
|
+
authority_approved: false,
|
|
93
|
+
approved_at: null,
|
|
94
|
+
approved_by: null,
|
|
95
|
+
tags: opts?.tags ?? [],
|
|
96
|
+
current_version: 1,
|
|
97
|
+
execution_count: 0,
|
|
98
|
+
last_executed_at: null,
|
|
99
|
+
last_success_at: null,
|
|
100
|
+
last_failure_at: null,
|
|
101
|
+
created_at: now,
|
|
102
|
+
updated_at: now,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function getWorkflow(id: string): Workflow | null {
|
|
107
|
+
const row = getDb().prepare('SELECT * FROM workflows WHERE id = ?').get(id) as WorkflowRow | null;
|
|
108
|
+
return row ? parseWorkflow(row) : null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function findWorkflows(query?: {
|
|
112
|
+
enabled?: boolean;
|
|
113
|
+
tag?: string;
|
|
114
|
+
limit?: number;
|
|
115
|
+
}): Workflow[] {
|
|
116
|
+
const conditions: string[] = [];
|
|
117
|
+
const params: unknown[] = [];
|
|
118
|
+
|
|
119
|
+
if (query?.enabled !== undefined) {
|
|
120
|
+
conditions.push('enabled = ?');
|
|
121
|
+
params.push(query.enabled ? 1 : 0);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
|
|
125
|
+
const limitVal = query?.limit ? Math.max(1, Math.min(parseInt(String(query.limit), 10) || 100, 1000)) : null;
|
|
126
|
+
const limitClause = limitVal ? 'LIMIT ?' : '';
|
|
127
|
+
if (limitVal) params.push(limitVal);
|
|
128
|
+
const rows = getDb().prepare(`SELECT * FROM workflows ${where} ORDER BY updated_at DESC ${limitClause}`).all(...params as any[]) as WorkflowRow[];
|
|
129
|
+
|
|
130
|
+
let result = rows.map(parseWorkflow);
|
|
131
|
+
if (query?.tag) {
|
|
132
|
+
result = result.filter(w => w.tags.includes(query.tag!));
|
|
133
|
+
}
|
|
134
|
+
return result;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function updateWorkflow(
|
|
138
|
+
id: string,
|
|
139
|
+
updates: Partial<Pick<Workflow, 'name' | 'description' | 'enabled' | 'authority_level' | 'authority_approved' | 'approved_at' | 'approved_by' | 'tags' | 'current_version' | 'execution_count' | 'last_executed_at' | 'last_success_at' | 'last_failure_at'>>
|
|
140
|
+
): Workflow | null {
|
|
141
|
+
const existing = getWorkflow(id);
|
|
142
|
+
if (!existing) return null;
|
|
143
|
+
|
|
144
|
+
const db = getDb();
|
|
145
|
+
const sets: string[] = ['updated_at = ?'];
|
|
146
|
+
const params: unknown[] = [Date.now()];
|
|
147
|
+
|
|
148
|
+
if (updates.name !== undefined) { sets.push('name = ?'); params.push(updates.name); }
|
|
149
|
+
if (updates.description !== undefined) { sets.push('description = ?'); params.push(updates.description); }
|
|
150
|
+
if (updates.enabled !== undefined) { sets.push('enabled = ?'); params.push(updates.enabled ? 1 : 0); }
|
|
151
|
+
if (updates.authority_level !== undefined) { sets.push('authority_level = ?'); params.push(updates.authority_level); }
|
|
152
|
+
if (updates.authority_approved !== undefined) { sets.push('authority_approved = ?'); params.push(updates.authority_approved ? 1 : 0); }
|
|
153
|
+
if (updates.approved_at !== undefined) { sets.push('approved_at = ?'); params.push(updates.approved_at); }
|
|
154
|
+
if (updates.approved_by !== undefined) { sets.push('approved_by = ?'); params.push(updates.approved_by); }
|
|
155
|
+
if (updates.tags !== undefined) { sets.push('tags = ?'); params.push(JSON.stringify(updates.tags)); }
|
|
156
|
+
if (updates.current_version !== undefined) { sets.push('current_version = ?'); params.push(updates.current_version); }
|
|
157
|
+
if (updates.execution_count !== undefined) { sets.push('execution_count = ?'); params.push(updates.execution_count); }
|
|
158
|
+
if (updates.last_executed_at !== undefined) { sets.push('last_executed_at = ?'); params.push(updates.last_executed_at); }
|
|
159
|
+
if (updates.last_success_at !== undefined) { sets.push('last_success_at = ?'); params.push(updates.last_success_at); }
|
|
160
|
+
if (updates.last_failure_at !== undefined) { sets.push('last_failure_at = ?'); params.push(updates.last_failure_at); }
|
|
161
|
+
|
|
162
|
+
params.push(id);
|
|
163
|
+
db.prepare(`UPDATE workflows SET ${sets.join(', ')} WHERE id = ?`).run(...params as any[]);
|
|
164
|
+
return getWorkflow(id);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function deleteWorkflow(id: string): boolean {
|
|
168
|
+
const result = getDb().prepare('DELETE FROM workflows WHERE id = ?').run(id);
|
|
169
|
+
return result.changes > 0;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// ── Versions ──
|
|
173
|
+
|
|
174
|
+
export function createVersion(
|
|
175
|
+
workflowId: string,
|
|
176
|
+
definition: WorkflowDefinition,
|
|
177
|
+
changelog?: string,
|
|
178
|
+
createdBy?: string,
|
|
179
|
+
): WorkflowVersion {
|
|
180
|
+
const db = getDb();
|
|
181
|
+
const id = generateId();
|
|
182
|
+
const now = Date.now();
|
|
183
|
+
|
|
184
|
+
// Get next version number
|
|
185
|
+
const latest = db.prepare(
|
|
186
|
+
'SELECT MAX(version) as max_v FROM workflow_versions WHERE workflow_id = ?'
|
|
187
|
+
).get(workflowId) as { max_v: number | null } | null;
|
|
188
|
+
const version = (latest?.max_v ?? 0) + 1;
|
|
189
|
+
|
|
190
|
+
db.prepare(
|
|
191
|
+
`INSERT INTO workflow_versions (id, workflow_id, version, definition, changelog, created_by, created_at)
|
|
192
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`
|
|
193
|
+
).run(id, workflowId, version, JSON.stringify(definition), changelog ?? null, createdBy ?? 'user', now);
|
|
194
|
+
|
|
195
|
+
// Update current_version on workflow
|
|
196
|
+
db.prepare('UPDATE workflows SET current_version = ?, updated_at = ? WHERE id = ?').run(version, now, workflowId);
|
|
197
|
+
|
|
198
|
+
return { id, workflow_id: workflowId, version, definition, changelog: changelog ?? null, created_by: createdBy ?? 'user', created_at: now };
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function getVersion(workflowId: string, version: number): WorkflowVersion | null {
|
|
202
|
+
const row = getDb().prepare(
|
|
203
|
+
'SELECT * FROM workflow_versions WHERE workflow_id = ? AND version = ?'
|
|
204
|
+
).get(workflowId, version) as VersionRow | null;
|
|
205
|
+
return row ? parseVersion(row) : null;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export function getLatestVersion(workflowId: string): WorkflowVersion | null {
|
|
209
|
+
const row = getDb().prepare(
|
|
210
|
+
'SELECT * FROM workflow_versions WHERE workflow_id = ? ORDER BY version DESC LIMIT 1'
|
|
211
|
+
).get(workflowId) as VersionRow | null;
|
|
212
|
+
return row ? parseVersion(row) : null;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export function getVersionHistory(workflowId: string): WorkflowVersion[] {
|
|
216
|
+
const rows = getDb().prepare(
|
|
217
|
+
'SELECT * FROM workflow_versions WHERE workflow_id = ? ORDER BY version DESC'
|
|
218
|
+
).all(workflowId) as VersionRow[];
|
|
219
|
+
return rows.map(parseVersion);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// ── Executions ──
|
|
223
|
+
|
|
224
|
+
export function createExecution(
|
|
225
|
+
workflowId: string,
|
|
226
|
+
version: number,
|
|
227
|
+
triggerType: string,
|
|
228
|
+
triggerData?: Record<string, unknown>,
|
|
229
|
+
): WorkflowExecution {
|
|
230
|
+
const db = getDb();
|
|
231
|
+
const id = generateId();
|
|
232
|
+
const now = Date.now();
|
|
233
|
+
|
|
234
|
+
db.prepare(
|
|
235
|
+
`INSERT INTO workflow_executions (id, workflow_id, version, trigger_type, trigger_data, status, variables, started_at)
|
|
236
|
+
VALUES (?, ?, ?, ?, ?, 'running', '{}', ?)`
|
|
237
|
+
).run(id, workflowId, version, triggerType, triggerData ? JSON.stringify(triggerData) : null, now);
|
|
238
|
+
|
|
239
|
+
// Bump execution count
|
|
240
|
+
db.prepare(
|
|
241
|
+
'UPDATE workflows SET execution_count = execution_count + 1, last_executed_at = ?, updated_at = ? WHERE id = ?'
|
|
242
|
+
).run(now, now, workflowId);
|
|
243
|
+
|
|
244
|
+
return {
|
|
245
|
+
id, workflow_id: workflowId, version, trigger_type: triggerType,
|
|
246
|
+
trigger_data: triggerData ?? null, status: 'running', variables: {},
|
|
247
|
+
error_message: null, started_at: now, completed_at: null, duration_ms: null,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export function getExecution(id: string): WorkflowExecution | null {
|
|
252
|
+
const row = getDb().prepare('SELECT * FROM workflow_executions WHERE id = ?').get(id) as ExecutionRow | null;
|
|
253
|
+
return row ? parseExecution(row) : null;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export function updateExecution(
|
|
257
|
+
id: string,
|
|
258
|
+
updates: Partial<Pick<WorkflowExecution, 'status' | 'variables' | 'error_message' | 'completed_at' | 'duration_ms'>>
|
|
259
|
+
): WorkflowExecution | null {
|
|
260
|
+
const db = getDb();
|
|
261
|
+
const sets: string[] = [];
|
|
262
|
+
const params: unknown[] = [];
|
|
263
|
+
|
|
264
|
+
if (updates.status !== undefined) { sets.push('status = ?'); params.push(updates.status); }
|
|
265
|
+
if (updates.variables !== undefined) { sets.push('variables = ?'); params.push(JSON.stringify(updates.variables)); }
|
|
266
|
+
if (updates.error_message !== undefined) { sets.push('error_message = ?'); params.push(updates.error_message); }
|
|
267
|
+
if (updates.completed_at !== undefined) { sets.push('completed_at = ?'); params.push(updates.completed_at); }
|
|
268
|
+
if (updates.duration_ms !== undefined) { sets.push('duration_ms = ?'); params.push(updates.duration_ms); }
|
|
269
|
+
|
|
270
|
+
if (sets.length === 0) return getExecution(id);
|
|
271
|
+
|
|
272
|
+
params.push(id);
|
|
273
|
+
db.prepare(`UPDATE workflow_executions SET ${sets.join(', ')} WHERE id = ?`).run(...params as any[]);
|
|
274
|
+
|
|
275
|
+
// Update workflow success/failure timestamps
|
|
276
|
+
if (updates.status === 'completed' || updates.status === 'failed') {
|
|
277
|
+
const exec = getExecution(id);
|
|
278
|
+
if (exec) {
|
|
279
|
+
const field = updates.status === 'completed' ? 'last_success_at' : 'last_failure_at';
|
|
280
|
+
db.prepare(`UPDATE workflows SET ${field} = ?, updated_at = ? WHERE id = ?`).run(Date.now(), Date.now(), exec.workflow_id);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return getExecution(id);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export function findExecutions(query: {
|
|
288
|
+
workflow_id?: string;
|
|
289
|
+
status?: ExecutionStatus;
|
|
290
|
+
limit?: number;
|
|
291
|
+
}): WorkflowExecution[] {
|
|
292
|
+
const conditions: string[] = [];
|
|
293
|
+
const params: unknown[] = [];
|
|
294
|
+
|
|
295
|
+
if (query.workflow_id) { conditions.push('workflow_id = ?'); params.push(query.workflow_id); }
|
|
296
|
+
if (query.status) { conditions.push('status = ?'); params.push(query.status); }
|
|
297
|
+
|
|
298
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
|
|
299
|
+
const limitVal = Math.max(1, Math.min(parseInt(String(query.limit ?? 100), 10) || 100, 1000));
|
|
300
|
+
params.push(limitVal);
|
|
301
|
+
const rows = getDb().prepare(`SELECT * FROM workflow_executions ${where} ORDER BY started_at DESC LIMIT ?`).all(...params as any[]) as ExecutionRow[];
|
|
302
|
+
return rows.map(parseExecution);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// ── Step Results ──
|
|
306
|
+
|
|
307
|
+
export function createStepResult(
|
|
308
|
+
executionId: string,
|
|
309
|
+
nodeId: string,
|
|
310
|
+
nodeType: string,
|
|
311
|
+
): WorkflowStepResult {
|
|
312
|
+
const db = getDb();
|
|
313
|
+
const id = generateId();
|
|
314
|
+
|
|
315
|
+
db.prepare(
|
|
316
|
+
`INSERT INTO workflow_step_results (id, execution_id, node_id, node_type, status, retry_count)
|
|
317
|
+
VALUES (?, ?, ?, ?, 'pending', 0)`
|
|
318
|
+
).run(id, executionId, nodeId, nodeType);
|
|
319
|
+
|
|
320
|
+
return {
|
|
321
|
+
id, execution_id: executionId, node_id: nodeId, node_type: nodeType,
|
|
322
|
+
status: 'pending', input_data: null, output_data: null, error_message: null,
|
|
323
|
+
retry_count: 0, started_at: null, completed_at: null, duration_ms: null,
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
export function updateStepResult(
|
|
328
|
+
id: string,
|
|
329
|
+
updates: Partial<Pick<WorkflowStepResult, 'status' | 'input_data' | 'output_data' | 'error_message' | 'retry_count' | 'started_at' | 'completed_at' | 'duration_ms'>>
|
|
330
|
+
): WorkflowStepResult | null {
|
|
331
|
+
const db = getDb();
|
|
332
|
+
const sets: string[] = [];
|
|
333
|
+
const params: unknown[] = [];
|
|
334
|
+
|
|
335
|
+
if (updates.status !== undefined) { sets.push('status = ?'); params.push(updates.status); }
|
|
336
|
+
if (updates.input_data !== undefined) { sets.push('input_data = ?'); params.push(JSON.stringify(updates.input_data)); }
|
|
337
|
+
if (updates.output_data !== undefined) { sets.push('output_data = ?'); params.push(JSON.stringify(updates.output_data)); }
|
|
338
|
+
if (updates.error_message !== undefined) { sets.push('error_message = ?'); params.push(updates.error_message); }
|
|
339
|
+
if (updates.retry_count !== undefined) { sets.push('retry_count = ?'); params.push(updates.retry_count); }
|
|
340
|
+
if (updates.started_at !== undefined) { sets.push('started_at = ?'); params.push(updates.started_at); }
|
|
341
|
+
if (updates.completed_at !== undefined) { sets.push('completed_at = ?'); params.push(updates.completed_at); }
|
|
342
|
+
if (updates.duration_ms !== undefined) { sets.push('duration_ms = ?'); params.push(updates.duration_ms); }
|
|
343
|
+
|
|
344
|
+
if (sets.length === 0) return null;
|
|
345
|
+
|
|
346
|
+
params.push(id);
|
|
347
|
+
db.prepare(`UPDATE workflow_step_results SET ${sets.join(', ')} WHERE id = ?`).run(...params as any[]);
|
|
348
|
+
|
|
349
|
+
const row = db.prepare('SELECT * FROM workflow_step_results WHERE id = ?').get(id) as StepRow | null;
|
|
350
|
+
return row ? parseStep(row) : null;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
export function getStepResults(executionId: string): WorkflowStepResult[] {
|
|
354
|
+
const rows = getDb().prepare(
|
|
355
|
+
'SELECT * FROM workflow_step_results WHERE execution_id = ? ORDER BY started_at ASC'
|
|
356
|
+
).all(executionId) as StepRow[];
|
|
357
|
+
return rows.map(parseStep);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// ── Persistent Variables ──
|
|
361
|
+
|
|
362
|
+
export function getVariable(workflowId: string, key: string): unknown | null {
|
|
363
|
+
const row = getDb().prepare(
|
|
364
|
+
'SELECT value FROM workflow_variables WHERE workflow_id = ? AND key = ?'
|
|
365
|
+
).get(workflowId, key) as { value: string } | null;
|
|
366
|
+
return row ? JSON.parse(row.value) : null;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
export function setVariable(workflowId: string, key: string, value: unknown): void {
|
|
370
|
+
const db = getDb();
|
|
371
|
+
const now = Date.now();
|
|
372
|
+
const existing = db.prepare(
|
|
373
|
+
'SELECT id FROM workflow_variables WHERE workflow_id = ? AND key = ?'
|
|
374
|
+
).get(workflowId, key) as { id: string } | null;
|
|
375
|
+
|
|
376
|
+
if (existing) {
|
|
377
|
+
db.prepare('UPDATE workflow_variables SET value = ?, updated_at = ? WHERE id = ?')
|
|
378
|
+
.run(JSON.stringify(value), now, existing.id);
|
|
379
|
+
} else {
|
|
380
|
+
db.prepare(
|
|
381
|
+
'INSERT INTO workflow_variables (id, workflow_id, key, value, updated_at) VALUES (?, ?, ?, ?, ?)'
|
|
382
|
+
).run(generateId(), workflowId, key, JSON.stringify(value), now);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
export function getVariables(workflowId: string): Record<string, unknown> {
|
|
387
|
+
const rows = getDb().prepare(
|
|
388
|
+
'SELECT key, value FROM workflow_variables WHERE workflow_id = ?'
|
|
389
|
+
).all(workflowId) as { key: string; value: string }[];
|
|
390
|
+
|
|
391
|
+
const result: Record<string, unknown> = {};
|
|
392
|
+
for (const row of rows) {
|
|
393
|
+
result[row.key] = JSON.parse(row.value);
|
|
394
|
+
}
|
|
395
|
+
return result;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
export function deleteVariable(workflowId: string, key: string): boolean {
|
|
399
|
+
const result = getDb().prepare(
|
|
400
|
+
'DELETE FROM workflow_variables WHERE workflow_id = ? AND key = ?'
|
|
401
|
+
).run(workflowId, key);
|
|
402
|
+
return result.changes > 0;
|
|
403
|
+
}
|