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.
- package/README.md +361 -0
- package/container/Dockerfile +48 -0
- package/container/entrypoint.sh +8 -0
- package/dist/agent-runner/index.d.ts +9 -0
- package/dist/agent-runner/index.d.ts.map +1 -0
- package/dist/agent-runner/index.js +21 -0
- package/dist/agent-runner/index.js.map +1 -0
- package/dist/agent-runner/interface.d.ts +56 -0
- package/dist/agent-runner/interface.d.ts.map +1 -0
- package/dist/agent-runner/interface.js +5 -0
- package/dist/agent-runner/interface.js.map +1 -0
- package/dist/agent-runner/pi-runner.d.ts +41 -0
- package/dist/agent-runner/pi-runner.d.ts.map +1 -0
- package/dist/agent-runner/pi-runner.js +162 -0
- package/dist/agent-runner/pi-runner.js.map +1 -0
- package/dist/channels/interface.d.ts +63 -0
- package/dist/channels/interface.d.ts.map +1 -0
- package/dist/channels/interface.js +33 -0
- package/dist/channels/interface.js.map +1 -0
- package/dist/channels/registry.d.ts +30 -0
- package/dist/channels/registry.d.ts.map +1 -0
- package/dist/channels/registry.js +71 -0
- package/dist/channels/registry.js.map +1 -0
- package/dist/channels/signal.d.ts +51 -0
- package/dist/channels/signal.d.ts.map +1 -0
- package/dist/channels/signal.js +263 -0
- package/dist/channels/signal.js.map +1 -0
- package/dist/channels/web.d.ts +35 -0
- package/dist/channels/web.d.ts.map +1 -0
- package/dist/channels/web.js +65 -0
- package/dist/channels/web.js.map +1 -0
- package/dist/channels/whatsapp.d.ts +25 -0
- package/dist/channels/whatsapp.d.ts.map +1 -0
- package/dist/channels/whatsapp.js +150 -0
- package/dist/channels/whatsapp.js.map +1 -0
- package/dist/config.d.ts +366 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +140 -0
- package/dist/config.js.map +1 -0
- package/dist/context.d.ts +69 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +166 -0
- package/dist/context.js.map +1 -0
- package/dist/credential-proxy.d.ts +25 -0
- package/dist/credential-proxy.d.ts.map +1 -0
- package/dist/credential-proxy.js +96 -0
- package/dist/credential-proxy.js.map +1 -0
- package/dist/daemon.d.ts +25 -0
- package/dist/daemon.d.ts.map +1 -0
- package/dist/daemon.js +138 -0
- package/dist/daemon.js.map +1 -0
- package/dist/db/index.d.ts +23 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +113 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/schema.d.ts +408 -0
- package/dist/db/schema.d.ts.map +1 -0
- package/dist/db/schema.js +55 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/doctor.d.ts +23 -0
- package/dist/doctor.d.ts.map +1 -0
- package/dist/doctor.js +217 -0
- package/dist/doctor.js.map +1 -0
- package/dist/extensions/loader.d.ts +19 -0
- package/dist/extensions/loader.d.ts.map +1 -0
- package/dist/extensions/loader.js +124 -0
- package/dist/extensions/loader.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +561 -0
- package/dist/index.js.map +1 -0
- package/dist/orchestrator.d.ts +60 -0
- package/dist/orchestrator.d.ts.map +1 -0
- package/dist/orchestrator.js +313 -0
- package/dist/orchestrator.js.map +1 -0
- package/dist/packages.d.ts +21 -0
- package/dist/packages.d.ts.map +1 -0
- package/dist/packages.js +116 -0
- package/dist/packages.js.map +1 -0
- package/dist/scheduler-registry.d.ts +8 -0
- package/dist/scheduler-registry.d.ts.map +1 -0
- package/dist/scheduler-registry.js +14 -0
- package/dist/scheduler-registry.js.map +1 -0
- package/dist/scheduler.d.ts +60 -0
- package/dist/scheduler.d.ts.map +1 -0
- package/dist/scheduler.js +143 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/server.d.ts +18 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +489 -0
- package/dist/server.js.map +1 -0
- package/dist/setup-wizard.d.ts +12 -0
- package/dist/setup-wizard.d.ts.map +1 -0
- package/dist/setup-wizard.js +163 -0
- package/dist/setup-wizard.js.map +1 -0
- package/extensions/confirm-destructive.ts +59 -0
- package/extensions/custom-compaction.ts +114 -0
- package/extensions/protected-paths.ts +30 -0
- package/extensions/sandbox/index.ts +317 -0
- package/extensions/sandbox/package-lock.json +92 -0
- package/extensions/sandbox/package.json +19 -0
- package/extensions/scheduler-tool.ts +65 -0
- package/extensions/session-name.ts +27 -0
- package/extensions/token-meter.ts +55 -0
- package/package.json +68 -0
- package/skills/send-message/SKILL.md +27 -0
- package/skills/web-search/SKILL.md +32 -0
- package/templates/global-agents.md +23 -0
- package/templates/main-agents.md +28 -0
- package/webchat/index.html +421 -0
package/dist/context.js
ADDED
|
@@ -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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"}
|
package/dist/daemon.d.ts
ADDED
|
@@ -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"}
|
package/dist/db/index.js
ADDED
|
@@ -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"}
|