reeboot 1.0.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.
Files changed (110) hide show
  1. package/README.md +361 -0
  2. package/container/Dockerfile +48 -0
  3. package/container/entrypoint.sh +8 -0
  4. package/dist/agent-runner/index.d.ts +9 -0
  5. package/dist/agent-runner/index.d.ts.map +1 -0
  6. package/dist/agent-runner/index.js +21 -0
  7. package/dist/agent-runner/index.js.map +1 -0
  8. package/dist/agent-runner/interface.d.ts +56 -0
  9. package/dist/agent-runner/interface.d.ts.map +1 -0
  10. package/dist/agent-runner/interface.js +5 -0
  11. package/dist/agent-runner/interface.js.map +1 -0
  12. package/dist/agent-runner/pi-runner.d.ts +41 -0
  13. package/dist/agent-runner/pi-runner.d.ts.map +1 -0
  14. package/dist/agent-runner/pi-runner.js +162 -0
  15. package/dist/agent-runner/pi-runner.js.map +1 -0
  16. package/dist/channels/interface.d.ts +63 -0
  17. package/dist/channels/interface.d.ts.map +1 -0
  18. package/dist/channels/interface.js +33 -0
  19. package/dist/channels/interface.js.map +1 -0
  20. package/dist/channels/registry.d.ts +30 -0
  21. package/dist/channels/registry.d.ts.map +1 -0
  22. package/dist/channels/registry.js +71 -0
  23. package/dist/channels/registry.js.map +1 -0
  24. package/dist/channels/signal.d.ts +51 -0
  25. package/dist/channels/signal.d.ts.map +1 -0
  26. package/dist/channels/signal.js +263 -0
  27. package/dist/channels/signal.js.map +1 -0
  28. package/dist/channels/web.d.ts +35 -0
  29. package/dist/channels/web.d.ts.map +1 -0
  30. package/dist/channels/web.js +65 -0
  31. package/dist/channels/web.js.map +1 -0
  32. package/dist/channels/whatsapp.d.ts +25 -0
  33. package/dist/channels/whatsapp.d.ts.map +1 -0
  34. package/dist/channels/whatsapp.js +150 -0
  35. package/dist/channels/whatsapp.js.map +1 -0
  36. package/dist/config.d.ts +366 -0
  37. package/dist/config.d.ts.map +1 -0
  38. package/dist/config.js +140 -0
  39. package/dist/config.js.map +1 -0
  40. package/dist/context.d.ts +69 -0
  41. package/dist/context.d.ts.map +1 -0
  42. package/dist/context.js +166 -0
  43. package/dist/context.js.map +1 -0
  44. package/dist/credential-proxy.d.ts +25 -0
  45. package/dist/credential-proxy.d.ts.map +1 -0
  46. package/dist/credential-proxy.js +96 -0
  47. package/dist/credential-proxy.js.map +1 -0
  48. package/dist/daemon.d.ts +25 -0
  49. package/dist/daemon.d.ts.map +1 -0
  50. package/dist/daemon.js +138 -0
  51. package/dist/daemon.js.map +1 -0
  52. package/dist/db/index.d.ts +23 -0
  53. package/dist/db/index.d.ts.map +1 -0
  54. package/dist/db/index.js +113 -0
  55. package/dist/db/index.js.map +1 -0
  56. package/dist/db/schema.d.ts +408 -0
  57. package/dist/db/schema.d.ts.map +1 -0
  58. package/dist/db/schema.js +55 -0
  59. package/dist/db/schema.js.map +1 -0
  60. package/dist/doctor.d.ts +23 -0
  61. package/dist/doctor.d.ts.map +1 -0
  62. package/dist/doctor.js +217 -0
  63. package/dist/doctor.js.map +1 -0
  64. package/dist/extensions/loader.d.ts +19 -0
  65. package/dist/extensions/loader.d.ts.map +1 -0
  66. package/dist/extensions/loader.js +124 -0
  67. package/dist/extensions/loader.js.map +1 -0
  68. package/dist/index.d.ts +3 -0
  69. package/dist/index.d.ts.map +1 -0
  70. package/dist/index.js +561 -0
  71. package/dist/index.js.map +1 -0
  72. package/dist/orchestrator.d.ts +60 -0
  73. package/dist/orchestrator.d.ts.map +1 -0
  74. package/dist/orchestrator.js +313 -0
  75. package/dist/orchestrator.js.map +1 -0
  76. package/dist/packages.d.ts +21 -0
  77. package/dist/packages.d.ts.map +1 -0
  78. package/dist/packages.js +116 -0
  79. package/dist/packages.js.map +1 -0
  80. package/dist/scheduler-registry.d.ts +8 -0
  81. package/dist/scheduler-registry.d.ts.map +1 -0
  82. package/dist/scheduler-registry.js +14 -0
  83. package/dist/scheduler-registry.js.map +1 -0
  84. package/dist/scheduler.d.ts +60 -0
  85. package/dist/scheduler.d.ts.map +1 -0
  86. package/dist/scheduler.js +143 -0
  87. package/dist/scheduler.js.map +1 -0
  88. package/dist/server.d.ts +18 -0
  89. package/dist/server.d.ts.map +1 -0
  90. package/dist/server.js +489 -0
  91. package/dist/server.js.map +1 -0
  92. package/dist/setup-wizard.d.ts +12 -0
  93. package/dist/setup-wizard.d.ts.map +1 -0
  94. package/dist/setup-wizard.js +163 -0
  95. package/dist/setup-wizard.js.map +1 -0
  96. package/extensions/confirm-destructive.ts +59 -0
  97. package/extensions/custom-compaction.ts +114 -0
  98. package/extensions/protected-paths.ts +30 -0
  99. package/extensions/sandbox/index.ts +317 -0
  100. package/extensions/sandbox/package-lock.json +92 -0
  101. package/extensions/sandbox/package.json +19 -0
  102. package/extensions/scheduler-tool.ts +65 -0
  103. package/extensions/session-name.ts +27 -0
  104. package/extensions/token-meter.ts +55 -0
  105. package/package.json +68 -0
  106. package/skills/send-message/SKILL.md +27 -0
  107. package/skills/web-search/SKILL.md +32 -0
  108. package/templates/global-agents.md +23 -0
  109. package/templates/main-agents.md +28 -0
  110. package/webchat/index.html +421 -0
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Context system
3
+ *
4
+ * Manages context workspace directories, AGENTS.md files, session file paths,
5
+ * and context/session metadata in SQLite.
6
+ *
7
+ * ~/.reeboot/
8
+ * contexts/
9
+ * global/
10
+ * AGENTS.md — prepended to every context's system prompt
11
+ * <contextId>/
12
+ * workspace/ — cwd for agent (project files go here)
13
+ * .pi/extensions/ — context-local extensions
14
+ * .pi/skills/ — context-local skills
15
+ * AGENTS.md — context-specific system prompt additions
16
+ * sessions/
17
+ * <contextId>/
18
+ * session-<timestamp>-<id>.json
19
+ */
20
+ import { existsSync, mkdirSync, writeFileSync, readdirSync, statSync } from 'fs';
21
+ import { join } from 'path';
22
+ import { homedir } from 'os';
23
+ import { nanoid } from 'nanoid';
24
+ // ─── DB helpers ───────────────────────────────────────────────────────────────
25
+ export function createContextsTable(db) {
26
+ db.exec(`
27
+ CREATE TABLE IF NOT EXISTS contexts (
28
+ id TEXT PRIMARY KEY,
29
+ name TEXT NOT NULL,
30
+ model_provider TEXT NOT NULL DEFAULT '',
31
+ model_id TEXT NOT NULL DEFAULT '',
32
+ status TEXT NOT NULL DEFAULT 'active',
33
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
34
+ )
35
+ `);
36
+ }
37
+ export function createContext(db, params) {
38
+ const id = params.id ?? nanoid();
39
+ db.prepare(`INSERT INTO contexts (id, name, model_provider, model_id, status)
40
+ VALUES (?, ?, ?, ?, 'active')`).run(id, params.name, params.modelProvider, params.modelId);
41
+ return db.prepare('SELECT * FROM contexts WHERE id = ?').get(id);
42
+ }
43
+ export function listContexts(db) {
44
+ return db.prepare('SELECT * FROM contexts ORDER BY created_at ASC').all();
45
+ }
46
+ export function getContextById(db, id) {
47
+ return db.prepare('SELECT * FROM contexts WHERE id = ?').get(id);
48
+ }
49
+ // ─── Workspace helpers ────────────────────────────────────────────────────────
50
+ const DEFAULT_AGENTS_MD = `# Agent Instructions
51
+
52
+ You are a helpful personal AI assistant.
53
+
54
+ - Be concise and practical
55
+ - When in doubt, ask for clarification
56
+ - Respect privacy — never share information outside this conversation
57
+ `;
58
+ const GLOBAL_AGENTS_MD = `# Global Instructions
59
+
60
+ These instructions apply to every context.
61
+
62
+ - Follow user instructions precisely
63
+ - Be honest about your capabilities and limitations
64
+ `;
65
+ /**
66
+ * Ensures the context workspace directory structure exists.
67
+ * Does NOT overwrite an existing AGENTS.md.
68
+ */
69
+ export async function initContextWorkspace(contextId, reebotDir = join(homedir(), '.reeboot')) {
70
+ const contextDir = join(reebotDir, 'contexts', contextId);
71
+ const workspaceDir = join(contextDir, 'workspace');
72
+ const piExtensionsDir = join(contextDir, '.pi', 'extensions');
73
+ const piSkillsDir = join(contextDir, '.pi', 'skills');
74
+ const agentsPath = join(contextDir, 'AGENTS.md');
75
+ mkdirSync(workspaceDir, { recursive: true });
76
+ mkdirSync(piExtensionsDir, { recursive: true });
77
+ mkdirSync(piSkillsDir, { recursive: true });
78
+ if (!existsSync(agentsPath)) {
79
+ writeFileSync(agentsPath, DEFAULT_AGENTS_MD, 'utf-8');
80
+ }
81
+ }
82
+ /**
83
+ * Ensures the global context exists with its AGENTS.md.
84
+ */
85
+ export async function initGlobalContext(reebotDir = join(homedir(), '.reeboot')) {
86
+ const globalDir = join(reebotDir, 'contexts', 'global');
87
+ const agentsPath = join(globalDir, 'AGENTS.md');
88
+ mkdirSync(globalDir, { recursive: true });
89
+ if (!existsSync(agentsPath)) {
90
+ writeFileSync(agentsPath, GLOBAL_AGENTS_MD, 'utf-8');
91
+ }
92
+ }
93
+ // ─── Session path ─────────────────────────────────────────────────────────────
94
+ /**
95
+ * Returns a deterministic-ish session file path for a context.
96
+ * The session directory is `~/.reeboot/sessions/<contextId>/`.
97
+ * The file name encodes a timestamp + random id so each invocation is unique.
98
+ */
99
+ export function getActiveSessionPath(contextId, reebotDir = join(homedir(), '.reeboot')) {
100
+ const sessionsDir = join(reebotDir, 'sessions', contextId);
101
+ mkdirSync(sessionsDir, { recursive: true });
102
+ const ts = Date.now();
103
+ const id = nanoid(8);
104
+ return join(sessionsDir, `session-${ts}-${id}.json`);
105
+ }
106
+ // ─── Session listing ──────────────────────────────────────────────────────────
107
+ export async function listSessions(contextId, reebotDir = join(homedir(), '.reeboot')) {
108
+ const sessionsDir = join(reebotDir, 'sessions', contextId);
109
+ if (!existsSync(sessionsDir))
110
+ return [];
111
+ const files = readdirSync(sessionsDir)
112
+ .filter(f => f.startsWith('session-') && f.endsWith('.json'))
113
+ .sort();
114
+ return files.map(f => {
115
+ const fullPath = join(sessionsDir, f);
116
+ const stat = statSync(fullPath);
117
+ // Parse timestamp from filename: session-<ts>-<id>.json
118
+ const match = f.match(/^session-(\d+)-/);
119
+ const startedAt = match ? new Date(parseInt(match[1], 10)).toISOString() : stat.mtime.toISOString();
120
+ return {
121
+ sessionId: f.replace('.json', ''),
122
+ startedAt,
123
+ messageCount: 0, // Would need to parse the JSON to get actual count; skip for now
124
+ };
125
+ });
126
+ }
127
+ // ─── Session resume ───────────────────────────────────────────────────────────
128
+ /**
129
+ * Returns the most recent session file path for a context if it was updated
130
+ * within the inactivity window; otherwise returns null (start fresh).
131
+ */
132
+ export function getResumedSessionPath(contextId, inactivityTimeoutMs, reebotDir = join(homedir(), '.reeboot')) {
133
+ const sessionsDir = join(reebotDir, 'sessions', contextId);
134
+ if (!existsSync(sessionsDir))
135
+ return null;
136
+ const files = readdirSync(sessionsDir)
137
+ .filter(f => f.startsWith('session-') && f.endsWith('.json'))
138
+ .sort()
139
+ .reverse(); // most recent first
140
+ if (files.length === 0)
141
+ return null;
142
+ const latest = files[0];
143
+ const fullPath = join(sessionsDir, latest);
144
+ const stat = statSync(fullPath);
145
+ const age = Date.now() - stat.mtimeMs;
146
+ return age < inactivityTimeoutMs ? fullPath : null;
147
+ }
148
+ // ─── initContexts ─────────────────────────────────────────────────────────────
149
+ /**
150
+ * Called on startup — ensures the main context and global context exist.
151
+ */
152
+ export async function initContexts(db, reebotDir = join(homedir(), '.reeboot')) {
153
+ await initGlobalContext(reebotDir);
154
+ // Ensure "main" context exists in DB
155
+ const existing = getContextById(db, 'main');
156
+ if (!existing) {
157
+ createContext(db, {
158
+ id: 'main',
159
+ name: 'main',
160
+ modelProvider: '',
161
+ modelId: '',
162
+ });
163
+ }
164
+ await initContextWorkspace('main', reebotDir);
165
+ }
166
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Credential Proxy
3
+ *
4
+ * A lightweight Fastify instance on 127.0.0.1:3001 (or configured port).
5
+ * Intercepts LLM API calls from a sandboxed agent process, injects the real
6
+ * API key, and forwards the request to the actual provider.
7
+ *
8
+ * Only started when config.credentialProxy.enabled === true.
9
+ */
10
+ import { FastifyInstance } from 'fastify';
11
+ export interface CredentialProxyConfig {
12
+ credentialProxy?: {
13
+ enabled?: boolean;
14
+ port?: number;
15
+ };
16
+ agent?: {
17
+ model?: {
18
+ provider?: string;
19
+ apiKey?: string;
20
+ };
21
+ };
22
+ }
23
+ export declare function startProxy(config: CredentialProxyConfig): Promise<FastifyInstance | null>;
24
+ export declare function stopProxy(): Promise<void>;
25
+ //# sourceMappingURL=credential-proxy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credential-proxy.d.ts","sourceRoot":"","sources":["../src/credential-proxy.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAgB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAanD,MAAM,WAAW,qBAAqB;IACpC,eAAe,CAAC,EAAE;QAChB,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;IACF,KAAK,CAAC,EAAE;QACN,KAAK,CAAC,EAAE;YACN,QAAQ,CAAC,EAAE,MAAM,CAAC;YAClB,MAAM,CAAC,EAAE,MAAM,CAAC;SACjB,CAAC;KACH,CAAC;CACH;AAQD,wBAAsB,UAAU,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAkF/F;AAID,wBAAsB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAK/C"}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Credential Proxy
3
+ *
4
+ * A lightweight Fastify instance on 127.0.0.1:3001 (or configured port).
5
+ * Intercepts LLM API calls from a sandboxed agent process, injects the real
6
+ * API key, and forwards the request to the actual provider.
7
+ *
8
+ * Only started when config.credentialProxy.enabled === true.
9
+ */
10
+ import Fastify from 'fastify';
11
+ // ─── Provider URL map ─────────────────────────────────────────────────────────
12
+ const PROVIDER_BASE_URLS = {
13
+ anthropic: 'https://api.anthropic.com',
14
+ openai: 'https://api.openai.com',
15
+ google: 'https://generativelanguage.googleapis.com',
16
+ openrouter: 'https://openrouter.ai/api',
17
+ };
18
+ // ─── Singleton ────────────────────────────────────────────────────────────────
19
+ let _proxyServer = null;
20
+ // ─── startProxy ───────────────────────────────────────────────────────────────
21
+ export async function startProxy(config) {
22
+ if (!config.credentialProxy?.enabled) {
23
+ return null;
24
+ }
25
+ const port = config.credentialProxy?.port ?? 3001;
26
+ const defaultProvider = config.agent?.model?.provider ?? 'anthropic';
27
+ const defaultApiKey = config.agent?.model?.apiKey ?? '';
28
+ const server = Fastify({ logger: false });
29
+ // Forward all routes to the provider
30
+ server.all('/*', async (req, reply) => {
31
+ // Determine provider from header or default
32
+ const providerHeader = req.headers['x-reeboot-provider'] ?? defaultProvider;
33
+ const provider = providerHeader.toLowerCase();
34
+ const baseUrl = PROVIDER_BASE_URLS[provider] ?? PROVIDER_BASE_URLS['anthropic'];
35
+ // Resolve the API key (use default key; in future could look up per-provider)
36
+ const apiKey = defaultApiKey;
37
+ // Build target URL
38
+ const path = (req.url ?? '/').replace(/^\/?/, '/');
39
+ const targetUrl = `${baseUrl}${path}`;
40
+ // Build forwarded headers (strip proxy-specific headers, inject real auth)
41
+ const forwardHeaders = {};
42
+ for (const [k, v] of Object.entries(req.headers)) {
43
+ if (k.toLowerCase() === 'host' ||
44
+ k.toLowerCase() === 'x-reeboot-provider' ||
45
+ k.toLowerCase() === 'connection') {
46
+ continue;
47
+ }
48
+ if (typeof v === 'string')
49
+ forwardHeaders[k] = v;
50
+ else if (Array.isArray(v))
51
+ forwardHeaders[k] = v[0];
52
+ }
53
+ // Inject real API key
54
+ forwardHeaders['Authorization'] = `Bearer ${apiKey}`;
55
+ // Forward request
56
+ const method = req.method;
57
+ const hasBody = method !== 'GET' && method !== 'HEAD' && method !== 'DELETE';
58
+ let bodyText;
59
+ if (hasBody && req.body) {
60
+ bodyText = typeof req.body === 'string' ? req.body : JSON.stringify(req.body);
61
+ if (!forwardHeaders['content-type']) {
62
+ forwardHeaders['content-type'] = 'application/json';
63
+ }
64
+ }
65
+ try {
66
+ const response = await fetch(targetUrl, {
67
+ method,
68
+ headers: forwardHeaders,
69
+ body: hasBody ? bodyText : undefined,
70
+ });
71
+ reply.status(response.status);
72
+ // Forward response headers
73
+ for (const [k, v] of response.headers.entries()) {
74
+ if (k.toLowerCase() !== 'transfer-encoding') {
75
+ reply.header(k, v);
76
+ }
77
+ }
78
+ const responseText = await response.text();
79
+ return reply.send(responseText);
80
+ }
81
+ catch (err) {
82
+ return reply.status(502).send({ error: `Proxy error: ${err.message}` });
83
+ }
84
+ });
85
+ await server.listen({ port, host: '127.0.0.1' });
86
+ _proxyServer = server;
87
+ return server;
88
+ }
89
+ // ─── stopProxy ────────────────────────────────────────────────────────────────
90
+ export async function stopProxy() {
91
+ if (_proxyServer) {
92
+ await _proxyServer.close();
93
+ _proxyServer = null;
94
+ }
95
+ }
96
+ //# sourceMappingURL=credential-proxy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credential-proxy.js","sourceRoot":"","sources":["../src/credential-proxy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Daemon mode
3
+ *
4
+ * Generates and registers service unit files for macOS (launchd) and Linux (systemd).
5
+ * Logs to ~/.reeboot/logs/.
6
+ */
7
+ export interface StartDaemonOptions {
8
+ platform?: 'darwin' | 'linux' | string;
9
+ reebotBin?: string;
10
+ reebotDir?: string;
11
+ /** macOS: override ~/Library/LaunchAgents dir (for testing) */
12
+ launchAgentsDir?: string;
13
+ /** Linux: override ~/.config/systemd/user dir (for testing) */
14
+ systemdDir?: string;
15
+ }
16
+ export interface StopDaemonOptions {
17
+ platform?: 'darwin' | 'linux' | string;
18
+ /** macOS: override ~/Library/LaunchAgents dir (for testing) */
19
+ launchAgentsDir?: string;
20
+ /** Linux: override ~/.config/systemd/user dir (for testing) */
21
+ systemdDir?: string;
22
+ }
23
+ export declare function startDaemon(opts?: StartDaemonOptions): Promise<void>;
24
+ export declare function stopDaemon(opts?: StopDaemonOptions): Promise<void>;
25
+ //# sourceMappingURL=daemon.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+DAA+D;IAC/D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,+DAA+D;IAC/D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC;IACvC,+DAA+D;IAC/D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,+DAA+D;IAC/D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AA8DD,wBAAsB,WAAW,CAAC,IAAI,GAAE,kBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC,CA+C9E;AAID,wBAAsB,UAAU,CAAC,IAAI,GAAE,iBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAwB5E"}
package/dist/daemon.js ADDED
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Daemon mode
3
+ *
4
+ * Generates and registers service unit files for macOS (launchd) and Linux (systemd).
5
+ * Logs to ~/.reeboot/logs/.
6
+ */
7
+ import { writeFileSync, mkdirSync } from 'fs';
8
+ import { execSync } from 'child_process';
9
+ import { join } from 'path';
10
+ import { homedir } from 'os';
11
+ // ─── macOS plist ──────────────────────────────────────────────────────────────
12
+ function generatePlist(reebotBin, reebotDir) {
13
+ const logsDir = join(reebotDir, 'logs');
14
+ return `<?xml version="1.0" encoding="UTF-8"?>
15
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
16
+ <plist version="1.0">
17
+ <dict>
18
+ <key>Label</key>
19
+ <string>com.reeboot.agent</string>
20
+
21
+ <key>ProgramArguments</key>
22
+ <array>
23
+ <string>${reebotBin}</string>
24
+ <string>start</string>
25
+ </array>
26
+
27
+ <key>RunAtLoad</key>
28
+ <true/>
29
+
30
+ <key>KeepAlive</key>
31
+ <true/>
32
+
33
+ <key>StandardOutPath</key>
34
+ <string>${logsDir}/reeboot.log</string>
35
+
36
+ <key>StandardErrorPath</key>
37
+ <string>${logsDir}/reeboot-error.log</string>
38
+
39
+ <key>WorkingDirectory</key>
40
+ <string>${reebotDir}</string>
41
+ </dict>
42
+ </plist>
43
+ `;
44
+ }
45
+ // ─── Linux systemd unit ───────────────────────────────────────────────────────
46
+ function generateSystemdUnit(reebotBin, reebotDir) {
47
+ const logsDir = join(reebotDir, 'logs');
48
+ return `[Unit]
49
+ Description=Reeboot Personal AI Agent
50
+ After=network.target
51
+
52
+ [Service]
53
+ Type=simple
54
+ ExecStart=${reebotBin} start
55
+ Restart=on-failure
56
+ RestartSec=5
57
+ WorkingDirectory=${reebotDir}
58
+ StandardOutput=append:${logsDir}/reeboot.log
59
+ StandardError=append:${logsDir}/reeboot-error.log
60
+
61
+ [Install]
62
+ WantedBy=default.target
63
+ `;
64
+ }
65
+ // ─── startDaemon ─────────────────────────────────────────────────────────────
66
+ export async function startDaemon(opts = {}) {
67
+ const platform = opts.platform ?? process.platform;
68
+ const reebotDir = opts.reebotDir ?? join(homedir(), '.reeboot');
69
+ const reebotBin = opts.reebotBin ?? process.execPath.replace('node', 'reeboot');
70
+ const logsDir = join(reebotDir, 'logs');
71
+ // Ensure logs directory exists
72
+ mkdirSync(logsDir, { recursive: true });
73
+ if (platform === 'darwin') {
74
+ const launchAgentsDir = opts.launchAgentsDir ?? join(homedir(), 'Library', 'LaunchAgents');
75
+ mkdirSync(launchAgentsDir, { recursive: true });
76
+ const plistPath = join(launchAgentsDir, 'com.reeboot.agent.plist');
77
+ const plist = generatePlist(reebotBin, reebotDir);
78
+ writeFileSync(plistPath, plist, 'utf-8');
79
+ console.log(`[daemon] Wrote ${plistPath}`);
80
+ try {
81
+ execSync(`launchctl load -w ${plistPath}`, { stdio: ['pipe', 'pipe', 'pipe'] });
82
+ console.log('[daemon] Registered with launchd — reeboot will start on login.');
83
+ }
84
+ catch (err) {
85
+ console.error(`[daemon] launchctl load failed: ${err.message}`);
86
+ throw err;
87
+ }
88
+ }
89
+ else if (platform === 'linux') {
90
+ const systemdDir = opts.systemdDir ?? join(homedir(), '.config', 'systemd', 'user');
91
+ mkdirSync(systemdDir, { recursive: true });
92
+ const unitPath = join(systemdDir, 'reeboot.service');
93
+ const unit = generateSystemdUnit(reebotBin, reebotDir);
94
+ writeFileSync(unitPath, unit, 'utf-8');
95
+ console.log(`[daemon] Wrote ${unitPath}`);
96
+ try {
97
+ execSync('systemctl --user daemon-reload', { stdio: ['pipe', 'pipe', 'pipe'] });
98
+ execSync('systemctl --user enable --now reeboot', { stdio: ['pipe', 'pipe', 'pipe'] });
99
+ console.log('[daemon] Registered with systemd — reeboot enabled for current user.');
100
+ }
101
+ catch (err) {
102
+ console.error(`[daemon] systemctl failed: ${err.message}`);
103
+ throw err;
104
+ }
105
+ }
106
+ else {
107
+ throw new Error(`Daemon mode not supported on platform: ${platform}. Use a process manager like pm2.`);
108
+ }
109
+ }
110
+ // ─── stopDaemon ──────────────────────────────────────────────────────────────
111
+ export async function stopDaemon(opts = {}) {
112
+ const platform = opts.platform ?? process.platform;
113
+ if (platform === 'darwin') {
114
+ const launchAgentsDir = opts.launchAgentsDir ?? join(homedir(), 'Library', 'LaunchAgents');
115
+ const plistPath = join(launchAgentsDir, 'com.reeboot.agent.plist');
116
+ try {
117
+ execSync(`launchctl unload ${plistPath}`, { stdio: ['pipe', 'pipe', 'pipe'] });
118
+ console.log('[daemon] Stopped reeboot (launchd). Service remains registered for next login.');
119
+ }
120
+ catch (err) {
121
+ // Not running — that's fine
122
+ console.warn(`[daemon] launchctl unload: ${err.message}`);
123
+ }
124
+ }
125
+ else if (platform === 'linux') {
126
+ try {
127
+ execSync('systemctl --user stop reeboot', { stdio: ['pipe', 'pipe', 'pipe'] });
128
+ console.log('[daemon] Stopped reeboot (systemd). Service remains enabled for next boot.');
129
+ }
130
+ catch (err) {
131
+ console.warn(`[daemon] systemctl stop: ${err.message}`);
132
+ }
133
+ }
134
+ else {
135
+ throw new Error(`Daemon stop not supported on platform: ${platform}.`);
136
+ }
137
+ }
138
+ //# sourceMappingURL=daemon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon.js","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import Database from 'better-sqlite3';
2
+ import * as schema from './schema.js';
3
+ export type { Database as BetterSQLite3Database };
4
+ /**
5
+ * Opens (or re-uses) a SQLite database at the given path.
6
+ * Creates the file and applies the schema if it does not exist.
7
+ * Returns the raw better-sqlite3 Database instance.
8
+ */
9
+ export declare function openDatabase(dbPath?: string): Database.Database;
10
+ /**
11
+ * Returns the singleton database instance.
12
+ * Throws if the database has not been opened or has been closed.
13
+ */
14
+ export declare function getDb(): Database.Database;
15
+ /**
16
+ * Closes the database connection cleanly.
17
+ */
18
+ export declare function closeDb(): void;
19
+ /**
20
+ * Returns a Drizzle ORM instance wrapping the singleton connection.
21
+ */
22
+ export declare function getDrizzle(): import("drizzle-orm/better-sqlite3").BetterSQLite3Database<typeof schema>;
23
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAKtC,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AAEtC,YAAY,EAAE,QAAQ,IAAI,qBAAqB,EAAE,CAAC;AASlD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC,QAAQ,CAyB/D;AAID;;;GAGG;AACH,wBAAgB,KAAK,IAAI,QAAQ,CAAC,QAAQ,CAKzC;AAID;;GAEG;AACH,wBAAgB,OAAO,IAAI,IAAI,CAM9B;AAID;;GAEG;AACH,wBAAgB,UAAU,8EAEzB"}
@@ -0,0 +1,113 @@
1
+ import Database from 'better-sqlite3';
2
+ import { drizzle } from 'drizzle-orm/better-sqlite3';
3
+ import { mkdirSync } from 'fs';
4
+ import { dirname, join } from 'path';
5
+ import { homedir } from 'os';
6
+ import * as schema from './schema.js';
7
+ // ─── Singleton state ──────────────────────────────────────────────────────────
8
+ let _db = null;
9
+ let _dbClosed = false;
10
+ // ─── openDatabase ─────────────────────────────────────────────────────────────
11
+ /**
12
+ * Opens (or re-uses) a SQLite database at the given path.
13
+ * Creates the file and applies the schema if it does not exist.
14
+ * Returns the raw better-sqlite3 Database instance.
15
+ */
16
+ export function openDatabase(dbPath) {
17
+ const path = dbPath ?? join(homedir(), '.reeboot', 'reeboot.db');
18
+ if (_db && !_dbClosed) {
19
+ return _db;
20
+ }
21
+ // Ensure directory exists
22
+ mkdirSync(dirname(path), { recursive: true });
23
+ const db = new Database(path);
24
+ // Enable WAL mode for better concurrency
25
+ db.pragma('journal_mode = WAL');
26
+ // Enforce foreign keys
27
+ db.pragma('foreign_keys = ON');
28
+ // Apply schema (CREATE TABLE IF NOT EXISTS via Drizzle push equivalent)
29
+ applySchema(db);
30
+ _db = db;
31
+ _dbClosed = false;
32
+ return db;
33
+ }
34
+ // ─── getDb ───────────────────────────────────────────────────────────────────
35
+ /**
36
+ * Returns the singleton database instance.
37
+ * Throws if the database has not been opened or has been closed.
38
+ */
39
+ export function getDb() {
40
+ if (_dbClosed || !_db) {
41
+ throw new Error('Database is closed. Call openDatabase() first.');
42
+ }
43
+ return _db;
44
+ }
45
+ // ─── closeDb ─────────────────────────────────────────────────────────────────
46
+ /**
47
+ * Closes the database connection cleanly.
48
+ */
49
+ export function closeDb() {
50
+ if (_db) {
51
+ _db.close();
52
+ _db = null;
53
+ _dbClosed = true;
54
+ }
55
+ }
56
+ // ─── getDrizzle ──────────────────────────────────────────────────────────────
57
+ /**
58
+ * Returns a Drizzle ORM instance wrapping the singleton connection.
59
+ */
60
+ export function getDrizzle() {
61
+ return drizzle(getDb(), { schema });
62
+ }
63
+ // ─── Schema application ──────────────────────────────────────────────────────
64
+ function applySchema(db) {
65
+ db.exec(`
66
+ CREATE TABLE IF NOT EXISTS contexts (
67
+ id TEXT PRIMARY KEY,
68
+ name TEXT NOT NULL,
69
+ model_provider TEXT NOT NULL DEFAULT '',
70
+ model_id TEXT NOT NULL DEFAULT '',
71
+ status TEXT NOT NULL DEFAULT 'active',
72
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
73
+ );
74
+
75
+ CREATE TABLE IF NOT EXISTS messages (
76
+ id TEXT PRIMARY KEY,
77
+ context_id TEXT NOT NULL REFERENCES contexts(id),
78
+ channel TEXT NOT NULL,
79
+ peer_id TEXT NOT NULL,
80
+ role TEXT NOT NULL,
81
+ content TEXT NOT NULL,
82
+ tokens_used INTEGER DEFAULT 0,
83
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
84
+ );
85
+
86
+ CREATE TABLE IF NOT EXISTS tasks (
87
+ id TEXT PRIMARY KEY,
88
+ context_id TEXT NOT NULL REFERENCES contexts(id),
89
+ schedule TEXT NOT NULL,
90
+ prompt TEXT NOT NULL,
91
+ enabled INTEGER NOT NULL DEFAULT 1,
92
+ last_run TEXT,
93
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
94
+ );
95
+
96
+ CREATE TABLE IF NOT EXISTS channels (
97
+ type TEXT PRIMARY KEY,
98
+ status TEXT NOT NULL DEFAULT 'disconnected',
99
+ config TEXT NOT NULL DEFAULT '{}',
100
+ connected_at TEXT
101
+ );
102
+
103
+ CREATE TABLE IF NOT EXISTS usage (
104
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
105
+ context_id TEXT NOT NULL REFERENCES contexts(id),
106
+ input_tokens INTEGER NOT NULL DEFAULT 0,
107
+ output_tokens INTEGER NOT NULL DEFAULT 0,
108
+ model TEXT NOT NULL,
109
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
110
+ );
111
+ `);
112
+ }
113
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAC;AACrD,OAAO,EAAE,SAAS,EAAc,MAAM,IAAI,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AAItC,iFAAiF;AAEjF,IAAI,GAAG,GAA6B,IAAI,CAAC;AACzC,IAAI,SAAS,GAAG,KAAK,CAAC;AAEtB,iFAAiF;AAEjF;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,MAAe;IAC1C,MAAM,IAAI,GAAG,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,UAAU,EAAE,YAAY,CAAC,CAAC;IAEjE,IAAI,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QACtB,OAAO,GAAG,CAAC;IACb,CAAC;IAED,0BAA0B;IAC1B,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE9C,MAAM,EAAE,GAAG,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC;IAE9B,yCAAyC;IACzC,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAEhC,uBAAuB;IACvB,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAE/B,wEAAwE;IACxE,WAAW,CAAC,EAAE,CAAC,CAAC;IAEhB,GAAG,GAAG,EAAE,CAAC;IACT,SAAS,GAAG,KAAK,CAAC;IAElB,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,gFAAgF;AAEhF;;;GAGG;AACH,MAAM,UAAU,KAAK;IACnB,IAAI,SAAS,IAAI,CAAC,GAAG,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;IACpE,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,gFAAgF;AAEhF;;GAEG;AACH,MAAM,UAAU,OAAO;IACrB,IAAI,GAAG,EAAE,CAAC;QACR,GAAG,CAAC,KAAK,EAAE,CAAC;QACZ,GAAG,GAAG,IAAI,CAAC;QACX,SAAS,GAAG,IAAI,CAAC;IACnB,CAAC;AACH,CAAC;AAED,gFAAgF;AAEhF;;GAEG;AACH,MAAM,UAAU,UAAU;IACxB,OAAO,OAAO,CAAC,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;AACtC,CAAC;AAED,gFAAgF;AAEhF,SAAS,WAAW,CAAC,EAAqB;IACxC,EAAE,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CP,CAAC,CAAC;AACL,CAAC"}