@sulala/agent 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/README.md +120 -0
- package/bin/sulala.mjs +7 -0
- package/dist/agent/loop.d.ts +38 -0
- package/dist/agent/loop.d.ts.map +1 -0
- package/dist/agent/loop.js +384 -0
- package/dist/agent/loop.js.map +1 -0
- package/dist/agent/session-queue.d.ts +2 -0
- package/dist/agent/session-queue.d.ts.map +1 -0
- package/dist/agent/session-queue.js +13 -0
- package/dist/agent/session-queue.js.map +1 -0
- package/dist/agent/skill-install.d.ts +41 -0
- package/dist/agent/skill-install.d.ts.map +1 -0
- package/dist/agent/skill-install.js +211 -0
- package/dist/agent/skill-install.js.map +1 -0
- package/dist/agent/skills-config.d.ts +24 -0
- package/dist/agent/skills-config.d.ts.map +1 -0
- package/dist/agent/skills-config.js +126 -0
- package/dist/agent/skills-config.js.map +1 -0
- package/dist/agent/skills-watcher.d.ts +7 -0
- package/dist/agent/skills-watcher.d.ts.map +1 -0
- package/dist/agent/skills-watcher.js +32 -0
- package/dist/agent/skills-watcher.js.map +1 -0
- package/dist/agent/skills.d.ts +32 -0
- package/dist/agent/skills.d.ts.map +1 -0
- package/dist/agent/skills.js +208 -0
- package/dist/agent/skills.js.map +1 -0
- package/dist/agent/skills.test.d.ts +2 -0
- package/dist/agent/skills.test.d.ts.map +1 -0
- package/dist/agent/skills.test.js +59 -0
- package/dist/agent/skills.test.js.map +1 -0
- package/dist/agent/tools.d.ts +8 -0
- package/dist/agent/tools.d.ts.map +1 -0
- package/dist/agent/tools.js +147 -0
- package/dist/agent/tools.js.map +1 -0
- package/dist/ai/orchestrator.d.ts +38 -0
- package/dist/ai/orchestrator.d.ts.map +1 -0
- package/dist/ai/orchestrator.js +360 -0
- package/dist/ai/orchestrator.js.map +1 -0
- package/dist/ai/orchestrator.test.d.ts +2 -0
- package/dist/ai/orchestrator.test.d.ts.map +1 -0
- package/dist/ai/orchestrator.test.js +29 -0
- package/dist/ai/orchestrator.test.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +278 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +4 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +77 -0
- package/dist/config.js.map +1 -0
- package/dist/config.test.d.ts +2 -0
- package/dist/config.test.d.ts.map +1 -0
- package/dist/config.test.js +16 -0
- package/dist/config.test.js.map +1 -0
- package/dist/db/index.d.ts +42 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +121 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/schema.sql +74 -0
- package/dist/gateway/server.d.ts +13 -0
- package/dist/gateway/server.d.ts.map +1 -0
- package/dist/gateway/server.js +566 -0
- package/dist/gateway/server.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +78 -0
- package/dist/index.js.map +1 -0
- package/dist/plugins/index.d.ts +52 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +176 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/scheduler/cron.d.ts +8 -0
- package/dist/scheduler/cron.d.ts.map +1 -0
- package/dist/scheduler/cron.js +31 -0
- package/dist/scheduler/cron.js.map +1 -0
- package/dist/scheduler/queue.d.ts +13 -0
- package/dist/scheduler/queue.d.ts.map +1 -0
- package/dist/scheduler/queue.js +75 -0
- package/dist/scheduler/queue.js.map +1 -0
- package/dist/scheduler/queue.test.d.ts +2 -0
- package/dist/scheduler/queue.test.d.ts.map +1 -0
- package/dist/scheduler/queue.test.js +41 -0
- package/dist/scheduler/queue.test.js.map +1 -0
- package/dist/types.d.ts +149 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/watcher/index.d.ts +15 -0
- package/dist/watcher/index.d.ts.map +1 -0
- package/dist/watcher/index.js +87 -0
- package/dist/watcher/index.js.map +1 -0
- package/dist/webhooks.d.ts +2 -0
- package/dist/webhooks.d.ts.map +1 -0
- package/dist/webhooks.js +38 -0
- package/dist/webhooks.js.map +1 -0
- package/package.json +62 -0
- package/src/db/schema.sql +74 -0
- package/src/index.ts +83 -0
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/** App config (env + config file) */
|
|
2
|
+
export interface Config {
|
|
3
|
+
port: number;
|
|
4
|
+
host: string;
|
|
5
|
+
dbPath: string;
|
|
6
|
+
watchFolders: string[];
|
|
7
|
+
debug: boolean;
|
|
8
|
+
gatewayApiKey: string | null;
|
|
9
|
+
webhookUrls: string[];
|
|
10
|
+
webhookSecret: string | null;
|
|
11
|
+
rateLimitMax: number;
|
|
12
|
+
rateLimitWindowMs: number;
|
|
13
|
+
/** Default system prompt for agent (env AGENT_SYSTEM_PROMPT). */
|
|
14
|
+
agentSystemPrompt: string | null;
|
|
15
|
+
/** Default agent run timeout in ms (env AGENT_TIMEOUT_MS). 0 = no timeout. */
|
|
16
|
+
agentTimeoutMs: number;
|
|
17
|
+
/** Optional path to file or directory; contents are appended to the agent system prompt (env AGENT_CONTEXT_PATH). */
|
|
18
|
+
agentContextPath: string | null;
|
|
19
|
+
/** If set, read_file tool can only read paths under this directory (env AGENT_WORKSPACE_ROOT). */
|
|
20
|
+
agentWorkspaceRoot: string | null;
|
|
21
|
+
/** Bundled skills dir, e.g. <project>/context. */
|
|
22
|
+
skillsBundledDir: string;
|
|
23
|
+
/** Managed skills dir, e.g. ~/.sulala/skills (env SULALA_SKILLS_DIR). */
|
|
24
|
+
skillsManagedDir: string;
|
|
25
|
+
/** Extra skill dirs (env SKILLS_EXTRA_DIRS). */
|
|
26
|
+
skillsExtraDirs: string[];
|
|
27
|
+
/** Plugin skill dirs (plugins/name/skills). */
|
|
28
|
+
skillsPluginDirs: string[];
|
|
29
|
+
/** Watch skill dirs (env SKILLS_WATCH). */
|
|
30
|
+
skillsWatch: boolean;
|
|
31
|
+
}
|
|
32
|
+
/** Task row from DB */
|
|
33
|
+
export interface TaskRow {
|
|
34
|
+
id: string;
|
|
35
|
+
type: string;
|
|
36
|
+
payload: string | null;
|
|
37
|
+
status: string;
|
|
38
|
+
scheduled_at: number | null;
|
|
39
|
+
started_at: number | null;
|
|
40
|
+
finished_at: number | null;
|
|
41
|
+
retry_count: number;
|
|
42
|
+
max_retries: number;
|
|
43
|
+
error: string | null;
|
|
44
|
+
created_at: number;
|
|
45
|
+
updated_at: number;
|
|
46
|
+
}
|
|
47
|
+
/** Insert task payload */
|
|
48
|
+
export interface InsertTaskPayload {
|
|
49
|
+
id: string;
|
|
50
|
+
type: string;
|
|
51
|
+
payload?: unknown;
|
|
52
|
+
scheduled_at?: number | null;
|
|
53
|
+
max_retries?: number;
|
|
54
|
+
}
|
|
55
|
+
/** Tool call from model */
|
|
56
|
+
export interface ToolCallSpec {
|
|
57
|
+
id: string;
|
|
58
|
+
name: string;
|
|
59
|
+
arguments: string;
|
|
60
|
+
}
|
|
61
|
+
/** AI adapter: one provider */
|
|
62
|
+
export interface AIAdapter {
|
|
63
|
+
defaultModel: string;
|
|
64
|
+
complete(opts: {
|
|
65
|
+
model?: string;
|
|
66
|
+
messages: Array<{
|
|
67
|
+
role: string;
|
|
68
|
+
content?: string | null;
|
|
69
|
+
tool_calls?: ToolCallSpec[];
|
|
70
|
+
tool_call_id?: string;
|
|
71
|
+
name?: string;
|
|
72
|
+
}>;
|
|
73
|
+
max_tokens?: number;
|
|
74
|
+
tools?: Array<{
|
|
75
|
+
name: string;
|
|
76
|
+
description: string;
|
|
77
|
+
parameters?: Record<string, unknown>;
|
|
78
|
+
}>;
|
|
79
|
+
signal?: AbortSignal;
|
|
80
|
+
}): Promise<{
|
|
81
|
+
content: string;
|
|
82
|
+
usage?: Record<string, number>;
|
|
83
|
+
tool_calls?: ToolCallSpec[];
|
|
84
|
+
}>;
|
|
85
|
+
}
|
|
86
|
+
/** Complete options for orchestrator */
|
|
87
|
+
export interface CompleteOptions {
|
|
88
|
+
provider?: string;
|
|
89
|
+
model?: string;
|
|
90
|
+
messages?: Array<{
|
|
91
|
+
role: string;
|
|
92
|
+
content?: string | null;
|
|
93
|
+
tool_calls?: ToolCallSpec[];
|
|
94
|
+
tool_call_id?: string;
|
|
95
|
+
name?: string;
|
|
96
|
+
}>;
|
|
97
|
+
max_tokens?: number;
|
|
98
|
+
task_id?: string | null;
|
|
99
|
+
tools?: Array<{
|
|
100
|
+
name: string;
|
|
101
|
+
description: string;
|
|
102
|
+
parameters?: Record<string, unknown>;
|
|
103
|
+
}>;
|
|
104
|
+
signal?: AbortSignal;
|
|
105
|
+
}
|
|
106
|
+
/** Express app with optional locals */
|
|
107
|
+
export interface AppLocals {
|
|
108
|
+
wsBroadcast?: (data: unknown) => void;
|
|
109
|
+
enqueueTaskId?: (id: string) => void;
|
|
110
|
+
}
|
|
111
|
+
/** Agent session (DB row) */
|
|
112
|
+
export interface AgentSessionRow {
|
|
113
|
+
id: string;
|
|
114
|
+
session_key: string;
|
|
115
|
+
meta: string | null;
|
|
116
|
+
created_at: number;
|
|
117
|
+
updated_at: number;
|
|
118
|
+
}
|
|
119
|
+
/** Agent message (DB row or in-memory) */
|
|
120
|
+
export interface AgentMessageRow {
|
|
121
|
+
id?: number;
|
|
122
|
+
session_id: string;
|
|
123
|
+
role: string;
|
|
124
|
+
content: string | null;
|
|
125
|
+
tool_calls: string | null;
|
|
126
|
+
tool_call_id: string | null;
|
|
127
|
+
name: string | null;
|
|
128
|
+
created_at: number;
|
|
129
|
+
}
|
|
130
|
+
/** Message format for AI / agent loop */
|
|
131
|
+
export interface AgentTurnMessage {
|
|
132
|
+
role: string;
|
|
133
|
+
content?: string | null;
|
|
134
|
+
tool_calls?: Array<{
|
|
135
|
+
id: string;
|
|
136
|
+
name: string;
|
|
137
|
+
arguments: string;
|
|
138
|
+
}>;
|
|
139
|
+
tool_call_id?: string;
|
|
140
|
+
name?: string;
|
|
141
|
+
}
|
|
142
|
+
/** Tool definition for registry */
|
|
143
|
+
export interface ToolDef {
|
|
144
|
+
name: string;
|
|
145
|
+
description: string;
|
|
146
|
+
parameters?: Record<string, unknown>;
|
|
147
|
+
execute: (args: Record<string, unknown>) => Promise<unknown> | unknown;
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type FSWatcher } from 'chokidar';
|
|
2
|
+
export interface FileEventPayload {
|
|
3
|
+
event: string;
|
|
4
|
+
path: string;
|
|
5
|
+
ts: number;
|
|
6
|
+
mtimeMs?: number;
|
|
7
|
+
size?: number;
|
|
8
|
+
}
|
|
9
|
+
export declare function setEventCallback(fn: (payload: FileEventPayload) => void): void;
|
|
10
|
+
export declare function startWatcher(folders?: string[] | null, options?: {
|
|
11
|
+
ignoreInitial?: boolean;
|
|
12
|
+
}): FSWatcher | null;
|
|
13
|
+
export declare function enqueueTaskOnEvent(payload: FileEventPayload): void;
|
|
14
|
+
export declare function stopWatcher(): void;
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/watcher/index.ts"],"names":[],"mappings":"AAAA,OAAiB,EAAE,KAAK,SAAS,EAAE,MAAM,UAAU,CAAC;AAMpD,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAKD,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,IAAI,GAAG,IAAI,CAE9E;AA0BD,wBAAgB,YAAY,CAC1B,OAAO,GAAE,MAAM,EAAE,GAAG,IAAW,EAC/B,OAAO,GAAE;IAAE,aAAa,CAAC,EAAE,OAAO,CAAA;CAAO,GACxC,SAAS,GAAG,IAAI,CAwClB;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAOlE;AAED,wBAAgB,WAAW,IAAI,IAAI,CAKlC"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import chokidar from 'chokidar';
|
|
2
|
+
import { statSync } from 'fs';
|
|
3
|
+
import { config } from '../config.js';
|
|
4
|
+
import { initDb, getDb, log, upsertFileState, insertTask } from '../db/index.js';
|
|
5
|
+
let watcher = null;
|
|
6
|
+
let eventCallback = null;
|
|
7
|
+
export function setEventCallback(fn) {
|
|
8
|
+
eventCallback = fn;
|
|
9
|
+
}
|
|
10
|
+
function normalizeEvent(event, path, stats = null) {
|
|
11
|
+
const payload = { event, path, ts: Date.now() };
|
|
12
|
+
if (stats) {
|
|
13
|
+
payload.mtimeMs = stats.mtimeMs;
|
|
14
|
+
payload.size = stats.size;
|
|
15
|
+
}
|
|
16
|
+
return payload;
|
|
17
|
+
}
|
|
18
|
+
function emit(event, path, stats) {
|
|
19
|
+
const payload = normalizeEvent(event, path, stats);
|
|
20
|
+
try {
|
|
21
|
+
getDb();
|
|
22
|
+
if (event !== 'unlink' && payload.mtimeMs != null) {
|
|
23
|
+
upsertFileState(path, payload.mtimeMs, payload.size ?? null);
|
|
24
|
+
}
|
|
25
|
+
log('watcher', 'info', `${event}: ${path}`, payload);
|
|
26
|
+
if (eventCallback)
|
|
27
|
+
eventCallback(payload);
|
|
28
|
+
return payload;
|
|
29
|
+
}
|
|
30
|
+
catch (e) {
|
|
31
|
+
log('watcher', 'error', e.message, { path, stack: e.stack });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export function startWatcher(folders = null, options = {}) {
|
|
35
|
+
if (watcher)
|
|
36
|
+
return watcher;
|
|
37
|
+
initDb(config.dbPath);
|
|
38
|
+
const paths = folders?.length ? folders : config.watchFolders;
|
|
39
|
+
if (!paths.length) {
|
|
40
|
+
log('watcher', 'info', 'No watch folders configured; skipping file watcher');
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
watcher = chokidar.watch(paths, {
|
|
44
|
+
ignored: /(^|[/\\])\../,
|
|
45
|
+
persistent: true,
|
|
46
|
+
ignoreInitial: options.ignoreInitial !== false,
|
|
47
|
+
...options,
|
|
48
|
+
});
|
|
49
|
+
watcher
|
|
50
|
+
.on('add', (path) => {
|
|
51
|
+
try {
|
|
52
|
+
const stats = statSync(path);
|
|
53
|
+
emit('add', path, stats);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
emit('add', path, null);
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
.on('change', (path) => {
|
|
60
|
+
try {
|
|
61
|
+
const stats = statSync(path);
|
|
62
|
+
emit('change', path, stats);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
emit('change', path, null);
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
.on('unlink', (path) => emit('unlink', path, null))
|
|
69
|
+
.on('error', (err) => log('watcher', 'error', err instanceof Error ? err.message : String(err), err instanceof Error ? { stack: err.stack } : null));
|
|
70
|
+
log('watcher', 'info', 'Watching folders', { paths });
|
|
71
|
+
return watcher;
|
|
72
|
+
}
|
|
73
|
+
export function enqueueTaskOnEvent(payload) {
|
|
74
|
+
insertTask({
|
|
75
|
+
id: `ev_${payload.ts}_${Math.random().toString(36).slice(2, 9)}`,
|
|
76
|
+
type: 'file_event',
|
|
77
|
+
payload,
|
|
78
|
+
max_retries: 2,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
export function stopWatcher() {
|
|
82
|
+
if (watcher) {
|
|
83
|
+
watcher.close();
|
|
84
|
+
watcher = null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/watcher/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../src/webhooks.ts"],"names":[],"mappings":"AA6BA,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAatE"}
|
package/dist/webhooks.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { config } from './config.js';
|
|
2
|
+
import { log } from './db/index.js';
|
|
3
|
+
const MAX_RETRIES = 3;
|
|
4
|
+
const RETRY_DELAY_MS = 1000;
|
|
5
|
+
async function postWithRetry(url, body, headers, eventType, attempt = 1) {
|
|
6
|
+
try {
|
|
7
|
+
const res = await fetch(url, { method: 'POST', headers, body });
|
|
8
|
+
if (!res.ok && attempt < MAX_RETRIES) {
|
|
9
|
+
await new Promise((r) => setTimeout(r, RETRY_DELAY_MS * attempt));
|
|
10
|
+
return postWithRetry(url, body, headers, eventType, attempt + 1);
|
|
11
|
+
}
|
|
12
|
+
if (!res.ok)
|
|
13
|
+
log('webhook', 'warn', `Webhook ${url} returned ${res.status} after ${MAX_RETRIES} tries`, { eventType });
|
|
14
|
+
}
|
|
15
|
+
catch (err) {
|
|
16
|
+
if (attempt < MAX_RETRIES) {
|
|
17
|
+
await new Promise((r) => setTimeout(r, RETRY_DELAY_MS * attempt));
|
|
18
|
+
return postWithRetry(url, body, headers, eventType, attempt + 1);
|
|
19
|
+
}
|
|
20
|
+
log('webhook', 'error', `Webhook ${url} failed after ${MAX_RETRIES} tries: ${err.message}`, { eventType });
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export function fireWebhooks(eventType, payload) {
|
|
24
|
+
const urls = config.webhookUrls ?? [];
|
|
25
|
+
if (!urls.length)
|
|
26
|
+
return;
|
|
27
|
+
const body = JSON.stringify({ event: eventType, payload, ts: Date.now() });
|
|
28
|
+
const secret = config.webhookSecret ?? null;
|
|
29
|
+
const headers = {
|
|
30
|
+
'Content-Type': 'application/json',
|
|
31
|
+
'User-Agent': 'Sulala-Agent-Webhook',
|
|
32
|
+
...(secret && { 'X-Webhook-Secret': secret }),
|
|
33
|
+
};
|
|
34
|
+
for (const url of urls) {
|
|
35
|
+
postWithRetry(url, body, headers, eventType);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=webhooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../src/webhooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAEpC,MAAM,WAAW,GAAG,CAAC,CAAC;AACtB,MAAM,cAAc,GAAG,IAAI,CAAC;AAE5B,KAAK,UAAU,aAAa,CAC1B,GAAW,EACX,IAAY,EACZ,OAA+B,EAC/B,SAAiB,EACjB,OAAO,GAAG,CAAC;IAEX,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QAChE,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,OAAO,GAAG,WAAW,EAAE,CAAC;YACrC,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC;YAClE,OAAO,aAAa,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,GAAG,CAAC,CAAC,CAAC;QACnE,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,GAAG,aAAa,GAAG,CAAC,MAAM,UAAU,WAAW,QAAQ,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC;IACzH,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,IAAI,OAAO,GAAG,WAAW,EAAE,CAAC;YAC1B,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC;YAClE,OAAO,aAAa,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,GAAG,CAAC,CAAC,CAAC;QACnE,CAAC;QACD,GAAG,CAAC,SAAS,EAAE,OAAO,EAAE,WAAW,GAAG,iBAAiB,WAAW,WAAY,GAAa,CAAC,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC;IACxH,CAAC;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,SAAiB,EAAE,OAAgB;IAC9D,MAAM,IAAI,GAAG,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC;IACtC,IAAI,CAAC,IAAI,CAAC,MAAM;QAAE,OAAO;IACzB,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IAC3E,MAAM,MAAM,GAAG,MAAM,CAAC,aAAa,IAAI,IAAI,CAAC;IAC5C,MAAM,OAAO,GAA2B;QACtC,cAAc,EAAE,kBAAkB;QAClC,YAAY,EAAE,sBAAsB;QACpC,GAAG,CAAC,MAAM,IAAI,EAAE,kBAAkB,EAAE,MAAM,EAAE,CAAC;KAC9C,CAAC;IACF,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,aAAa,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;IAC/C,CAAC;AACH,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sulala/agent",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Local AI orchestration platform — file watcher, task scheduler, AI layer, plugins",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"start": "tsx src/index.ts",
|
|
9
|
+
"gateway": "tsx src/gateway/server.ts",
|
|
10
|
+
"dashboard": "cd dashboard && npm run dev",
|
|
11
|
+
"dashboard:build": "cd dashboard && npm run build",
|
|
12
|
+
"cli": "tsx src/cli.ts",
|
|
13
|
+
"dev": "tsx watch src/index.ts",
|
|
14
|
+
"build": "tsc && cp src/db/schema.sql dist/db/",
|
|
15
|
+
"test": "vitest run",
|
|
16
|
+
"test:watch": "vitest",
|
|
17
|
+
"lint": "eslint src"
|
|
18
|
+
},
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=18"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"ai",
|
|
24
|
+
"orchestration",
|
|
25
|
+
"automation",
|
|
26
|
+
"file-watcher",
|
|
27
|
+
"local"
|
|
28
|
+
],
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"files": ["dist", "bin", "src/db/schema.sql"],
|
|
31
|
+
"prepublishOnly": "pnpm run build",
|
|
32
|
+
"publishConfig": { "access": "public" },
|
|
33
|
+
"bin": {
|
|
34
|
+
"sulala": "bin/sulala.mjs"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"better-sqlite3": "^11.6.0",
|
|
38
|
+
"chokidar": "^4.0.1",
|
|
39
|
+
"cors": "^2.8.5",
|
|
40
|
+
"dotenv": "^16.4.5",
|
|
41
|
+
"express": "^4.21.1",
|
|
42
|
+
"node-cron": "^3.0.3",
|
|
43
|
+
"openai": "^6.25.0",
|
|
44
|
+
"ws": "^8.18.0"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@eslint/js": "^10.0.1",
|
|
48
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
49
|
+
"@types/cors": "^2.8.19",
|
|
50
|
+
"@types/express": "^5.0.6",
|
|
51
|
+
"@types/node": "^25.3.1",
|
|
52
|
+
"@types/node-cron": "^3.0.11",
|
|
53
|
+
"@types/ws": "^8.18.1",
|
|
54
|
+
"@typescript-eslint/eslint-plugin": "^8.56.1",
|
|
55
|
+
"@typescript-eslint/parser": "^8.56.1",
|
|
56
|
+
"eslint": "^10.0.2",
|
|
57
|
+
"globals": "^17.3.0",
|
|
58
|
+
"tsx": "^4.21.0",
|
|
59
|
+
"typescript": "^5.9.3",
|
|
60
|
+
"vitest": "^4.0.18"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
-- Sulala Agent — SQLite schema
|
|
2
|
+
-- Tasks, Logs, File States, AI Results
|
|
3
|
+
|
|
4
|
+
CREATE TABLE IF NOT EXISTS tasks (
|
|
5
|
+
id TEXT PRIMARY KEY,
|
|
6
|
+
type TEXT NOT NULL,
|
|
7
|
+
payload TEXT,
|
|
8
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
9
|
+
scheduled_at INTEGER,
|
|
10
|
+
started_at INTEGER,
|
|
11
|
+
finished_at INTEGER,
|
|
12
|
+
retry_count INTEGER DEFAULT 0,
|
|
13
|
+
max_retries INTEGER DEFAULT 3,
|
|
14
|
+
error TEXT,
|
|
15
|
+
created_at INTEGER NOT NULL,
|
|
16
|
+
updated_at INTEGER NOT NULL
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
CREATE TABLE IF NOT EXISTS logs (
|
|
20
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
21
|
+
source TEXT NOT NULL,
|
|
22
|
+
level TEXT NOT NULL,
|
|
23
|
+
message TEXT NOT NULL,
|
|
24
|
+
meta TEXT,
|
|
25
|
+
created_at INTEGER NOT NULL
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
CREATE TABLE IF NOT EXISTS file_states (
|
|
29
|
+
path TEXT PRIMARY KEY,
|
|
30
|
+
mtime_ms INTEGER NOT NULL,
|
|
31
|
+
size INTEGER,
|
|
32
|
+
hash TEXT,
|
|
33
|
+
last_seen INTEGER NOT NULL,
|
|
34
|
+
meta TEXT
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
CREATE TABLE IF NOT EXISTS ai_results (
|
|
38
|
+
id TEXT PRIMARY KEY,
|
|
39
|
+
provider TEXT NOT NULL,
|
|
40
|
+
model TEXT,
|
|
41
|
+
task_id TEXT,
|
|
42
|
+
request_meta TEXT,
|
|
43
|
+
response_meta TEXT,
|
|
44
|
+
created_at INTEGER NOT NULL,
|
|
45
|
+
FOREIGN KEY (task_id) REFERENCES tasks(id)
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
-- Agent runner: sessions and message history
|
|
49
|
+
CREATE TABLE IF NOT EXISTS agent_sessions (
|
|
50
|
+
id TEXT PRIMARY KEY,
|
|
51
|
+
session_key TEXT UNIQUE NOT NULL,
|
|
52
|
+
meta TEXT,
|
|
53
|
+
created_at INTEGER NOT NULL,
|
|
54
|
+
updated_at INTEGER NOT NULL
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
CREATE TABLE IF NOT EXISTS agent_messages (
|
|
58
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
59
|
+
session_id TEXT NOT NULL,
|
|
60
|
+
role TEXT NOT NULL,
|
|
61
|
+
content TEXT,
|
|
62
|
+
tool_calls TEXT,
|
|
63
|
+
tool_call_id TEXT,
|
|
64
|
+
name TEXT,
|
|
65
|
+
created_at INTEGER NOT NULL,
|
|
66
|
+
FOREIGN KEY (session_id) REFERENCES agent_sessions(id)
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
CREATE INDEX IF NOT EXISTS idx_agent_messages_session ON agent_messages(session_id);
|
|
70
|
+
|
|
71
|
+
CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
|
|
72
|
+
CREATE INDEX IF NOT EXISTS idx_tasks_scheduled ON tasks(scheduled_at);
|
|
73
|
+
CREATE INDEX IF NOT EXISTS idx_logs_created ON logs(created_at);
|
|
74
|
+
CREATE INDEX IF NOT EXISTS idx_ai_results_created ON ai_results(created_at);
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import 'dotenv/config';
|
|
2
|
+
import { createServer } from 'http';
|
|
3
|
+
import { loadFullConfig } from './agent/skills-config.js';
|
|
4
|
+
import { config } from './config.js';
|
|
5
|
+
import { initDb, log } from './db/index.js';
|
|
6
|
+
import { createGateway, attachWebSocket } from './gateway/server.js';
|
|
7
|
+
import { startWatcher, setEventCallback, enqueueTaskOnEvent } from './watcher/index.js';
|
|
8
|
+
import { setTaskHandler, loadPendingFromDb, enqueue } from './scheduler/queue.js';
|
|
9
|
+
import { scheduleCron } from './scheduler/cron.js';
|
|
10
|
+
import { loadAllPlugins, onFileEvent, onTask } from './plugins/index.js';
|
|
11
|
+
import { fireWebhooks } from './webhooks.js';
|
|
12
|
+
import { registerBuiltInTools } from './agent/tools.js';
|
|
13
|
+
import { startSkillsWatcher } from './agent/skills-watcher.js';
|
|
14
|
+
import type { AppLocals } from './types.js';
|
|
15
|
+
|
|
16
|
+
async function main(): Promise<void> {
|
|
17
|
+
initDb(config.dbPath);
|
|
18
|
+
// Inject skill config into process.env so run_command (e.g. curl with $PERIGON_API_KEY) sees them
|
|
19
|
+
const full = loadFullConfig();
|
|
20
|
+
const entries = full.skills?.entries;
|
|
21
|
+
if (entries && typeof entries === 'object') {
|
|
22
|
+
for (const entry of Object.values(entries)) {
|
|
23
|
+
if (!entry || typeof entry !== 'object') continue;
|
|
24
|
+
for (const [key, value] of Object.entries(entry)) {
|
|
25
|
+
if (key === 'enabled') continue;
|
|
26
|
+
if (typeof value === 'string' && value.trim() && !process.env[key]) process.env[key] = value;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
log('main', 'info', 'Starting Sulala Agent', { port: config.port });
|
|
31
|
+
|
|
32
|
+
registerBuiltInTools(enqueue);
|
|
33
|
+
|
|
34
|
+
const app = createGateway();
|
|
35
|
+
(app.locals as AppLocals).enqueueTaskId = enqueue;
|
|
36
|
+
const http = createServer(app);
|
|
37
|
+
const { broadcast } = attachWebSocket(http);
|
|
38
|
+
(app.locals as AppLocals).wsBroadcast = broadcast;
|
|
39
|
+
const server = http.listen(config.port, config.host, () => {
|
|
40
|
+
console.log(`Gateway http://${config.host}:${config.port} (WS /ws)`);
|
|
41
|
+
});
|
|
42
|
+
const shutdown = () => {
|
|
43
|
+
log('main', 'info', 'Shutting down');
|
|
44
|
+
server.close(() => {
|
|
45
|
+
log('main', 'info', 'Gateway closed');
|
|
46
|
+
process.exit(0);
|
|
47
|
+
});
|
|
48
|
+
setTimeout(() => process.exit(1), 10000);
|
|
49
|
+
};
|
|
50
|
+
process.on('SIGTERM', shutdown);
|
|
51
|
+
process.on('SIGINT', shutdown);
|
|
52
|
+
|
|
53
|
+
await loadAllPlugins();
|
|
54
|
+
|
|
55
|
+
setEventCallback((payload) => {
|
|
56
|
+
onFileEvent(payload);
|
|
57
|
+
enqueueTaskOnEvent(payload);
|
|
58
|
+
broadcast({ type: 'file_event', payload });
|
|
59
|
+
fireWebhooks('file_event', payload);
|
|
60
|
+
});
|
|
61
|
+
startWatcher();
|
|
62
|
+
startSkillsWatcher(config, (ev) => broadcast(ev));
|
|
63
|
+
|
|
64
|
+
setTaskHandler(async (task) => {
|
|
65
|
+
const handled = await onTask(task);
|
|
66
|
+
if (handled) return;
|
|
67
|
+
if (task.type === 'file_event') {
|
|
68
|
+
log('worker', 'info', 'File event task', (task.payload ?? null) as Record<string, unknown> | null);
|
|
69
|
+
}
|
|
70
|
+
broadcast({ type: 'task_done', taskId: task.id });
|
|
71
|
+
fireWebhooks('task_done', { taskId: task.id, type: task.type });
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
loadPendingFromDb();
|
|
75
|
+
scheduleCron('* * * * *', 'heartbeat', { ts: Date.now() });
|
|
76
|
+
|
|
77
|
+
log('main', 'info', 'Sulala Agent ready');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
main().catch((err) => {
|
|
81
|
+
console.error(err);
|
|
82
|
+
process.exit(1);
|
|
83
|
+
});
|