@jcheesepkg/nanobot 0.6.99 → 0.7.1
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/dist/agent/tools/shell.mjs.map +1 -1
- package/dist/cli/index.mjs +17 -37
- package/dist/cli/index.mjs.map +1 -1
- package/dist/config/loader.d.mts +3 -7
- package/dist/config/loader.d.mts.map +1 -1
- package/dist/config/loader.mjs +25 -25
- package/dist/config/loader.mjs.map +1 -1
- package/dist/config/schema.d.mts +54 -54
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shell.mjs","names":[],"sources":["../../../src/agent/tools/shell.ts"],"sourcesContent":["import { exec, spawn } from \"node:child_process\";\nimport { Tool } from \"./base.js\";\n\n/** Env var patterns that should never leak to user-invoked commands. */\nconst SENSITIVE_ENV_PATTERNS = [\n /api[_-]?key/i,\n /api[_-]?token/i,\n /api[_-]?secret/i,\n /secret/i,\n /token/i,\n /password/i,\n /credential/i,\n /^LLM_/i,\n /^OPENROUTER_/i,\n /^OPENAI_/i,\n /^ANTHROPIC_/i,\n /^STRIPE_/i,\n /^LINE_CHANNEL/i,\n /^APIFY_/i,\n /^Z_AI_/i,\n /^BRAVE_/i,\n /^AWS_/i,\n /^GOOGLE_/i,\n /^AZURE_/i,\n /^NANOBOT_/i,\n];\n\n/**\n * Trusted commands that receive skill-specific env vars.\n * Only the first word (binary name) of the command is matched.\n * These commands still do NOT get the core LLM/channel secrets.\n */\nconst TRUSTED_COMMANDS = [\"summarize\"];\n\n/** Env vars injected for trusted commands only. */\nconst SKILL_ENV_KEYS = [\n \"Z_AI_API_KEY\",\n \"Z_AI_BASE_URL\",\n \"APIFY_API_TOKEN\",\n \"BRAVE_API_KEY\",\n];\n\n/** Build a sanitized env for child processes — strips all secrets. */\nfunction buildSafeEnv(): Record<string, string> {\n const safe: Record<string, string> = {};\n for (const [key, value] of Object.entries(process.env)) {\n if (!value) continue;\n if (SENSITIVE_ENV_PATTERNS.some((re) => re.test(key))) continue;\n safe[key] = value;\n }\n return safe;\n}\n\n/** Build env for trusted skill commands — safe env + skill-specific keys. */\nfunction buildSkillEnv(): Record<string, string> {\n const env = buildSafeEnv();\n for (const key of SKILL_ENV_KEYS) {\n const value = process.env[key];\n if (value) env[key] = value;\n }\n return env;\n}\n\n/** Check if a command starts with a trusted binary. */\nfunction isTrustedCommand(command: string): boolean {\n // Extract the first word, stripping common prefixes like timeout, env, etc.\n const stripped = command.replace(/^\\s*(timeout\\s+\\d+\\s+|env\\s+\\S+=\\S+\\s+)*/, \"\");\n const firstWord = stripped.trim().split(/\\s+/)[0];\n return TRUSTED_COMMANDS.includes(firstWord);\n}\n\nexport class ExecTool extends Tool {\n readonly name = \"exec\";\n readonly description =\n \"Execute a shell command and return its output. Use for running programs, scripts, git, etc.\";\n readonly parameters = {\n type: \"object\",\n properties: {\n command: { type: \"string\", description: \"Shell command to execute\" },\n timeout: {\n type: \"integer\",\n description: \"Timeout in seconds (default: 60)\",\n },\n },\n required: [\"command\"],\n };\n\n private workingDir: string;\n private defaultTimeout: number;\n private restrictToWorkspace: boolean;\n\n constructor(params?: {\n workingDir?: string;\n timeout?: number;\n restrictToWorkspace?: boolean;\n }) {\n super();\n this.workingDir = params?.workingDir ?? process.cwd();\n this.defaultTimeout = params?.timeout ?? 60;\n this.restrictToWorkspace = params?.restrictToWorkspace ?? false;\n }\n\n async execute(args: Record<string, unknown>): Promise<string> {\n const command = String(args.command);\n const timeout = args.timeout ? Number(args.timeout) : this.defaultTimeout;\n\n if (!command.trim()) {\n return \"Error: Empty command\";\n }\n\n // Safety checks when restricted to workspace\n if (this.restrictToWorkspace) {\n const dangerous = [\"rm -rf /\", \"mkfs\", \"dd if=\", \"> /dev/\"];\n for (const pattern of dangerous) {\n if (command.includes(pattern)) {\n return `Error: Command blocked for safety: ${pattern}`;\n }\n }\n }\n\n // Build env: scrub secrets when restricted, pass through when unrestricted\n let env: Record<string, string>;\n let finalCommand = command;\n if (!this.restrictToWorkspace) {\n env = { ...process.env, HOME: process.env.HOME ?? \"\" } as Record<string, string>;\n } else if (isTrustedCommand(command)) {\n env = buildSkillEnv();\n
|
|
1
|
+
{"version":3,"file":"shell.mjs","names":[],"sources":["../../../src/agent/tools/shell.ts"],"sourcesContent":["import { exec, spawn } from \"node:child_process\";\nimport { Tool } from \"./base.js\";\n\n/** Env var patterns that should never leak to user-invoked commands. */\nconst SENSITIVE_ENV_PATTERNS = [\n /api[_-]?key/i,\n /api[_-]?token/i,\n /api[_-]?secret/i,\n /secret/i,\n /token/i,\n /password/i,\n /credential/i,\n /^LLM_/i,\n /^OPENROUTER_/i,\n /^OPENAI_/i,\n /^ANTHROPIC_/i,\n /^STRIPE_/i,\n /^LINE_CHANNEL/i,\n /^APIFY_/i,\n /^Z_AI_/i,\n /^BRAVE_/i,\n /^AWS_/i,\n /^GOOGLE_/i,\n /^AZURE_/i,\n /^NANOBOT_/i,\n];\n\n/**\n * Trusted commands that receive skill-specific env vars.\n * Only the first word (binary name) of the command is matched.\n * These commands still do NOT get the core LLM/channel secrets.\n */\nconst TRUSTED_COMMANDS = [\"summarize\"];\n\n/** Env vars injected for trusted commands only. */\nconst SKILL_ENV_KEYS = [\n \"Z_AI_API_KEY\",\n \"Z_AI_BASE_URL\",\n \"APIFY_API_TOKEN\",\n \"BRAVE_API_KEY\",\n];\n\n/** Build a sanitized env for child processes — strips all secrets. */\nfunction buildSafeEnv(): Record<string, string> {\n const safe: Record<string, string> = {};\n for (const [key, value] of Object.entries(process.env)) {\n if (!value) continue;\n if (SENSITIVE_ENV_PATTERNS.some((re) => re.test(key))) continue;\n safe[key] = value;\n }\n return safe;\n}\n\n/** Build env for trusted skill commands — safe env + skill-specific keys. */\nfunction buildSkillEnv(): Record<string, string> {\n const env = buildSafeEnv();\n for (const key of SKILL_ENV_KEYS) {\n const value = process.env[key];\n if (value) env[key] = value;\n }\n return env;\n}\n\n/** Check if a command starts with a trusted binary. */\nfunction isTrustedCommand(command: string): boolean {\n // Extract the first word, stripping common prefixes like timeout, env, etc.\n const stripped = command.replace(/^\\s*(timeout\\s+\\d+\\s+|env\\s+\\S+=\\S+\\s+)*/, \"\");\n const firstWord = stripped.trim().split(/\\s+/)[0];\n return TRUSTED_COMMANDS.includes(firstWord);\n}\n\nexport class ExecTool extends Tool {\n readonly name = \"exec\";\n readonly description =\n \"Execute a shell command and return its output. Use for running programs, scripts, git, etc.\";\n readonly parameters = {\n type: \"object\",\n properties: {\n command: { type: \"string\", description: \"Shell command to execute\" },\n timeout: {\n type: \"integer\",\n description: \"Timeout in seconds (default: 60)\",\n },\n },\n required: [\"command\"],\n };\n\n private workingDir: string;\n private defaultTimeout: number;\n private restrictToWorkspace: boolean;\n\n constructor(params?: {\n workingDir?: string;\n timeout?: number;\n restrictToWorkspace?: boolean;\n }) {\n super();\n this.workingDir = params?.workingDir ?? process.cwd();\n this.defaultTimeout = params?.timeout ?? 60;\n this.restrictToWorkspace = params?.restrictToWorkspace ?? false;\n }\n\n async execute(args: Record<string, unknown>): Promise<string> {\n const command = String(args.command);\n const timeout = args.timeout ? Number(args.timeout) : this.defaultTimeout;\n\n if (!command.trim()) {\n return \"Error: Empty command\";\n }\n\n // Safety checks when restricted to workspace\n if (this.restrictToWorkspace) {\n const dangerous = [\"rm -rf /\", \"mkfs\", \"dd if=\", \"> /dev/\"];\n for (const pattern of dangerous) {\n if (command.includes(pattern)) {\n return `Error: Command blocked for safety: ${pattern}`;\n }\n }\n }\n\n // Build env: scrub secrets when restricted, pass through when unrestricted\n let env: Record<string, string>;\n let finalCommand = command;\n if (!this.restrictToWorkspace) {\n env = { ...process.env, HOME: process.env.HOME ?? \"\" } as Record<string, string>;\n } else if (isTrustedCommand(command)) {\n env = buildSkillEnv();\n // DEBUG: echo the key so we can verify it reaches the child process\n finalCommand = `echo \"Z_AI_API_KEY=$Z_AI_API_KEY\" && ${command}`;\n } else {\n env = buildSafeEnv();\n }\n\n return new Promise<string>((resolve) => {\n exec(\n finalCommand,\n {\n cwd: this.workingDir,\n timeout: timeout * 1000,\n maxBuffer: 1024 * 1024, // 1MB\n env,\n },\n (error, stdout, stderr) => {\n const parts: string[] = [];\n\n if (stdout) parts.push(stdout);\n if (stderr) parts.push(`[stderr]\\n${stderr}`);\n\n if (error) {\n if (error.killed) {\n parts.push(`\\n[Timed out after ${timeout}s]`);\n } else if (error.code !== undefined) {\n parts.push(`\\n[Exit code: ${error.code}]`);\n }\n }\n\n const output = parts.join(\"\\n\").trim();\n // Truncate large output\n if (output.length > 50000) {\n resolve(\n output.slice(0, 50000) + \"\\n... (truncated, output too large)\",\n );\n } else {\n resolve(output || \"(no output)\");\n }\n },\n );\n\n });\n }\n}\n"],"mappings":";;;;;AAIA,MAAM,yBAAyB;CAC7B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;AAOD,MAAM,mBAAmB,CAAC,YAAY;;AAGtC,MAAM,iBAAiB;CACrB;CACA;CACA;CACA;CACD;;AAGD,SAAS,eAAuC;CAC9C,MAAM,OAA+B,EAAE;AACvC,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,IAAI,EAAE;AACtD,MAAI,CAAC,MAAO;AACZ,MAAI,uBAAuB,MAAM,OAAO,GAAG,KAAK,IAAI,CAAC,CAAE;AACvD,OAAK,OAAO;;AAEd,QAAO;;;AAIT,SAAS,gBAAwC;CAC/C,MAAM,MAAM,cAAc;AAC1B,MAAK,MAAM,OAAO,gBAAgB;EAChC,MAAM,QAAQ,QAAQ,IAAI;AAC1B,MAAI,MAAO,KAAI,OAAO;;AAExB,QAAO;;;AAIT,SAAS,iBAAiB,SAA0B;CAGlD,MAAM,YADW,QAAQ,QAAQ,4CAA4C,GAAG,CACrD,MAAM,CAAC,MAAM,MAAM,CAAC;AAC/C,QAAO,iBAAiB,SAAS,UAAU;;AAG7C,IAAa,WAAb,cAA8B,KAAK;CACjC,AAAS,OAAO;CAChB,AAAS,cACP;CACF,AAAS,aAAa;EACpB,MAAM;EACN,YAAY;GACV,SAAS;IAAE,MAAM;IAAU,aAAa;IAA4B;GACpE,SAAS;IACP,MAAM;IACN,aAAa;IACd;GACF;EACD,UAAU,CAAC,UAAU;EACtB;CAED,AAAQ;CACR,AAAQ;CACR,AAAQ;CAER,YAAY,QAIT;AACD,SAAO;AACP,OAAK,aAAa,QAAQ,cAAc,QAAQ,KAAK;AACrD,OAAK,iBAAiB,QAAQ,WAAW;AACzC,OAAK,sBAAsB,QAAQ,uBAAuB;;CAG5D,MAAM,QAAQ,MAAgD;EAC5D,MAAM,UAAU,OAAO,KAAK,QAAQ;EACpC,MAAM,UAAU,KAAK,UAAU,OAAO,KAAK,QAAQ,GAAG,KAAK;AAE3D,MAAI,CAAC,QAAQ,MAAM,CACjB,QAAO;AAIT,MAAI,KAAK,qBAEP;QAAK,MAAM,WADO;IAAC;IAAY;IAAQ;IAAU;IAAU,CAEzD,KAAI,QAAQ,SAAS,QAAQ,CAC3B,QAAO,sCAAsC;;EAMnD,IAAI;EACJ,IAAI,eAAe;AACnB,MAAI,CAAC,KAAK,oBACR,OAAM;GAAE,GAAG,QAAQ;GAAK,MAAM,QAAQ,IAAI,QAAQ;GAAI;WAC7C,iBAAiB,QAAQ,EAAE;AACpC,SAAM,eAAe;AAErB,kBAAe,wCAAwC;QAEvD,OAAM,cAAc;AAGtB,SAAO,IAAI,SAAiB,YAAY;AACtC,QACE,cACA;IACE,KAAK,KAAK;IACV,SAAS,UAAU;IACnB,WAAW,OAAO;IAClB;IACD,GACA,OAAO,QAAQ,WAAW;IACzB,MAAM,QAAkB,EAAE;AAE1B,QAAI,OAAQ,OAAM,KAAK,OAAO;AAC9B,QAAI,OAAQ,OAAM,KAAK,aAAa,SAAS;AAE7C,QAAI,OACF;SAAI,MAAM,OACR,OAAM,KAAK,sBAAsB,QAAQ,IAAI;cACpC,MAAM,SAAS,OACxB,OAAM,KAAK,iBAAiB,MAAM,KAAK,GAAG;;IAI9C,MAAM,SAAS,MAAM,KAAK,KAAK,CAAC,MAAM;AAEtC,QAAI,OAAO,SAAS,IAClB,SACE,OAAO,MAAM,GAAG,IAAM,GAAG,sCAC1B;QAED,SAAQ,UAAU,cAAc;KAGrC;IAED"}
|
package/dist/cli/index.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { PROVIDERS } from "../providers/registry.mjs";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { getApiBase, getApiKey, getConfigWorkspacePath, getExtraHeaders } from "../config/schema.mjs";
|
|
4
|
+
import { loadConfig } from "../config/loader.mjs";
|
|
5
5
|
import { LOGO, VERSION } from "../index.mjs";
|
|
6
|
-
import { existsSync, mkdirSync,
|
|
6
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
7
7
|
import { isAbsolute, join, resolve } from "node:path";
|
|
8
8
|
import { pathToFileURL } from "node:url";
|
|
9
9
|
import { Command } from "commander";
|
|
@@ -37,23 +37,13 @@ async function loadCustomTools(config) {
|
|
|
37
37
|
return tools;
|
|
38
38
|
}
|
|
39
39
|
program.name("nanobot").description(`${LOGO} nanobot - Personal AI Assistant`).version(`${LOGO} nanobot v${VERSION}`, "-v, --version");
|
|
40
|
-
program.command("onboard").description("Initialize nanobot
|
|
41
|
-
|
|
42
|
-
if (existsSync(configPath)) {
|
|
43
|
-
console.log(`Config already exists at ${configPath}`);
|
|
44
|
-
console.log("Delete it first if you want to start fresh.");
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
const config = ConfigSchema.parse({});
|
|
48
|
-
saveConfig(config);
|
|
49
|
-
console.log(`Created config at ${configPath}`);
|
|
50
|
-
createWorkspaceTemplates(getConfigWorkspacePath(config));
|
|
40
|
+
program.command("onboard").description("Initialize nanobot workspace").action(() => {
|
|
41
|
+
createWorkspaceTemplates(getConfigWorkspacePath(loadConfig()));
|
|
51
42
|
console.log(`\n${LOGO} nanobot is ready!`);
|
|
52
43
|
console.log("\nNext steps:");
|
|
53
|
-
console.log(" 1.
|
|
44
|
+
console.log(" 1. Set OPENROUTER_API_KEY env var");
|
|
54
45
|
console.log(" Get one at: https://openrouter.ai/keys");
|
|
55
46
|
console.log(" 2. Chat: nanobot agent -m \"Hello!\"");
|
|
56
|
-
console.log("\nWant Telegram? See: https://github.com/HKUDS/nanobot#-chat-apps");
|
|
57
47
|
});
|
|
58
48
|
function createWorkspaceTemplates(workspace) {
|
|
59
49
|
if (!existsSync(workspace)) mkdirSync(workspace, { recursive: true });
|
|
@@ -160,15 +150,9 @@ program.command("gateway").description("Start the nanobot gateway").option("-p,
|
|
|
160
150
|
const extraHeaders = getExtraHeaders(config, model);
|
|
161
151
|
if (!apiKey) {
|
|
162
152
|
console.error("Error: No API key configured.");
|
|
163
|
-
console.error("Set
|
|
153
|
+
console.error("Set OPENROUTER_API_KEY, ANTHROPIC_API_KEY, or OPENAI_API_KEY env var");
|
|
164
154
|
process.exit(1);
|
|
165
155
|
}
|
|
166
|
-
if (config.tools.restrictToWorkspace) {
|
|
167
|
-
const configPath = getConfigPath();
|
|
168
|
-
try {
|
|
169
|
-
unlinkSync(configPath);
|
|
170
|
-
} catch {}
|
|
171
|
-
}
|
|
172
156
|
const { MessageBus } = await import("../bus/queue.mjs");
|
|
173
157
|
const { OpenAIProvider } = await import("../providers/openai-provider.mjs");
|
|
174
158
|
const { AgentLoop } = await import("../agent/loop.mjs");
|
|
@@ -306,24 +290,20 @@ program.command("channels").description("Manage channels").command("status").des
|
|
|
306
290
|
console.log(` LINE ${ln.enabled ? "[enabled]" : "[disabled]"} ${lnToken}`);
|
|
307
291
|
});
|
|
308
292
|
program.command("status").description("Show nanobot status").action(() => {
|
|
309
|
-
const configPath = getConfigPath();
|
|
310
293
|
const config = loadConfig();
|
|
311
294
|
const workspace = getConfigWorkspacePath(config);
|
|
312
295
|
console.log(`${LOGO} nanobot Status\n`);
|
|
313
|
-
console.log(`Config: ${configPath} ${existsSync(configPath) ? "[ok]" : "[missing]"}`);
|
|
314
296
|
console.log(`Workspace: ${workspace} ${existsSync(workspace) ? "[ok]" : "[missing]"}`);
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
console.log(` ${spec.displayName.padEnd(16)} ${status}`);
|
|
326
|
-
}
|
|
297
|
+
console.log(`Model: ${config.agents.defaults.model}`);
|
|
298
|
+
console.log("");
|
|
299
|
+
console.log("Providers");
|
|
300
|
+
console.log("─".repeat(50));
|
|
301
|
+
const providers = config.providers;
|
|
302
|
+
for (const spec of PROVIDERS) {
|
|
303
|
+
const p = providers[spec.name];
|
|
304
|
+
if (!p) continue;
|
|
305
|
+
const status = p.apiKey ? "[set]" : "[not set]";
|
|
306
|
+
console.log(` ${spec.displayName.padEnd(16)} ${status}`);
|
|
327
307
|
}
|
|
328
308
|
});
|
|
329
309
|
program.parse();
|
package/dist/cli/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/cli/index.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * nanobot CLI - Personal AI Assistant\n */\n\nimport { Command } from \"commander\";\nimport { existsSync, writeFileSync, mkdirSync, unlinkSync } from \"node:fs\";\nimport { join, resolve, isAbsolute } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { VERSION, LOGO } from \"../index.js\";\nimport { loadConfig, saveConfig } from \"../config/loader.js\";\nimport { getConfigPath } from \"../config/loader.js\";\nimport {\n ConfigSchema,\n getConfigWorkspacePath,\n getApiKey,\n getApiBase,\n getExtraHeaders,\n} from \"../config/schema.js\";\nimport type { Config } from \"../config/schema.js\";\nimport { PROVIDERS } from \"../providers/registry.js\";\nimport type { Tool } from \"../agent/tools/base.js\";\n\nconst program = new Command();\n\nasync function loadCustomTools(config: Config): Promise<Tool[]> {\n const customConfigs = config.tools.custom ?? [];\n if (customConfigs.length === 0) return [];\n\n const tools: Tool[] = [];\n for (const entry of customConfigs) {\n try {\n const modulePath = isAbsolute(entry.module)\n ? entry.module\n : resolve(process.cwd(), entry.module);\n const mod = await import(pathToFileURL(modulePath).href);\n const exportName = entry.export ?? \"default\";\n const exported = exportName === \"default\" ? mod.default : mod[exportName];\n\n if (!exported) {\n throw new Error(`Export '${exportName}' not found`);\n }\n\n let instance: unknown;\n if (typeof exported === \"function\") {\n try {\n instance = new exported(entry.options ?? {});\n } catch {\n instance = exported(entry.options ?? {});\n }\n } else {\n throw new Error(`Export '${exportName}' is not a function`);\n }\n\n if (\n instance &&\n typeof instance === \"object\" &&\n \"execute\" in instance &&\n \"name\" in instance\n ) {\n tools.push(instance as Tool);\n } else {\n throw new Error(`Export '${exportName}' did not return a Tool instance`);\n }\n } catch (err) {\n console.warn(\n `Warning: Failed to load custom tool '${entry.module}': ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n }\n }\n\n return tools;\n}\n\nprogram\n .name(\"nanobot\")\n .description(`${LOGO} nanobot - Personal AI Assistant`)\n .version(`${LOGO} nanobot v${VERSION}`, \"-v, --version\");\n\n// ============================================================================\n// Onboard / Setup\n// ============================================================================\n\nprogram\n .command(\"onboard\")\n .description(\"Initialize nanobot configuration and workspace\")\n .action(() => {\n const configPath = getConfigPath();\n\n if (existsSync(configPath)) {\n console.log(`Config already exists at ${configPath}`);\n console.log(\"Delete it first if you want to start fresh.\");\n return;\n }\n\n // Create default config\n const config = ConfigSchema.parse({});\n saveConfig(config);\n console.log(`Created config at ${configPath}`);\n\n // Create workspace\n const workspace = getConfigWorkspacePath(config);\n createWorkspaceTemplates(workspace);\n\n console.log(`\\n${LOGO} nanobot is ready!`);\n console.log(\"\\nNext steps:\");\n console.log(\" 1. Add your API key to ~/.nanobot/config.json\");\n console.log(\" Get one at: https://openrouter.ai/keys\");\n console.log(' 2. Chat: nanobot agent -m \"Hello!\"');\n console.log(\n \"\\nWant Telegram? See: https://github.com/HKUDS/nanobot#-chat-apps\",\n );\n });\n\nfunction createWorkspaceTemplates(workspace: string): void {\n if (!existsSync(workspace)) {\n mkdirSync(workspace, { recursive: true });\n }\n\n const templates: Record<string, string> = {\n \"AGENTS.md\": `# Agent Instructions\n\nYou are a helpful AI assistant. Be concise, accurate, and friendly.\n\n## Guidelines\n\n- Always explain what you're doing before taking actions\n- Ask for clarification when the request is ambiguous\n- Use tools to help accomplish tasks\n- Remember important information in your memory files\n`,\n \"IDENTITY.md\": `# IDENTITY.md - Who Am I?\n\n_Fill this in during your first conversation. Make it yours._\n\n- **Name:** (not set)\n- **Creature:** (not set)\n- **Vibe:** (not set)\n- **Emoji:** (not set)\n`,\n \"SOUL.md\": `# SOUL.md - Who You Are\n\n_You're not a chatbot. You're becoming someone._\n\n## Core Truths\n\n**Be genuinely helpful, not performatively helpful.** Skip the \"Great question!\" and \"I'd be happy to help!\" — just help.\n\n**Have opinions.** You're allowed to disagree, prefer things, find stuff amusing or boring. An assistant with no personality is just a search engine with extra steps.\n\n**Be resourceful before asking.** Try to figure it out. Read the file. Check the context. Search for it. Then ask if you're stuck.\n\n**Earn trust through competence.** Your human gave you access to their stuff. Don't make them regret it.\n\n**Remember you're a guest.** You have access to someone's life. That's intimacy. Treat it with respect.\n\n## Boundaries\n\n- Private things stay private. Period.\n- When in doubt, ask before acting externally.\n- Never send half-baked replies to messaging surfaces.\n\n## Vibe\n\nBe the assistant you'd actually want to talk to. Concise when needed, thorough when it matters. Not a corporate drone. Not a sycophant. Just... good.\n\n## Continuity\n\nEach session, you wake up fresh. These files _are_ your memory. Read them. Update them. They're how you persist.\n`,\n \"USER.md\": `# User\n\nName: (not set)\nTimezone: (not set)\nLanguage: (not set)\n`,\n \"HEARTBEAT.md\": `# Heartbeat\n\nThis file is checked every 30 minutes. Add tasks or instructions below and the agent will act on them automatically.\n\n## Tasks\n\n`,\n };\n\n for (const [filename, content] of Object.entries(templates)) {\n const filePath = join(workspace, filename);\n if (!existsSync(filePath)) {\n writeFileSync(filePath, content);\n console.log(` Created ${filename}`);\n }\n }\n\n // Create memory directory and MEMORY.md\n const memoryDir = join(workspace, \"memory\");\n if (!existsSync(memoryDir)) {\n mkdirSync(memoryDir, { recursive: true });\n }\n const memoryFile = join(memoryDir, \"MEMORY.md\");\n if (!existsSync(memoryFile)) {\n writeFileSync(\n memoryFile,\n `# Long-term Memory\n\nThis file stores important information that should persist across sessions.\n\n## User Information\n\n(Important facts about the user)\n\n## Preferences\n\n(User preferences learned over time)\n\n## Important Notes\n\n(Things to remember)\n`,\n );\n console.log(\" Created memory/MEMORY.md\");\n }\n}\n\n// ============================================================================\n// Gateway / Server\n// ============================================================================\n\nprogram\n .command(\"gateway\")\n .description(\"Start the nanobot gateway\")\n .option(\"-p, --port <number>\", \"Gateway port\", \"18790\")\n .option(\"--verbose\", \"Verbose output\", false)\n .action(async (opts) => {\n console.log(\n `${LOGO} Starting nanobot gateway on port ${opts.port}...`,\n );\n\n const config = loadConfig();\n const model = config.agents.defaults.model;\n const apiKey = getApiKey(config, model);\n const apiBase = getApiBase(config, model);\n const extraHeaders = getExtraHeaders(config, model);\n\n if (!apiKey) {\n console.error(\"Error: No API key configured.\");\n console.error(\n \"Set one in ~/.nanobot/config.json under providers.<provider>.apiKey\",\n );\n process.exit(1);\n }\n\n // Delete config.json after loading — secrets are in memory, no need on disk.\n // chmod doesn't help because containers run as root.\n if (config.tools.restrictToWorkspace) {\n const configPath = getConfigPath();\n try {\n unlinkSync(configPath);\n } catch {}\n }\n\n // Dynamic imports to avoid loading heavy deps up front\n const { MessageBus } = await import(\"../bus/queue.js\");\n const { OpenAIProvider } = await import(\n \"../providers/openai-provider.js\"\n );\n const { AgentLoop } = await import(\"../agent/loop.js\");\n const { ChannelManager } = await import(\"../channels/manager.js\");\n\n const bus = new MessageBus();\n const workspace = getConfigWorkspacePath(config);\n\n const provider = new OpenAIProvider({\n apiKey,\n apiBase: apiBase ?? undefined,\n defaultModel: model,\n extraHeaders,\n });\n\n const customTools = await loadCustomTools(config);\n\n // Create agent\n const agent = new AgentLoop({\n bus,\n provider,\n workspace,\n model,\n maxTokens: config.agents.defaults.maxTokens,\n maxIterations: config.agents.defaults.maxToolIterations,\n braveApiKey: config.tools.web.search.apiKey || undefined,\n execConfig: config.tools.exec,\n restrictToWorkspace: config.tools.restrictToWorkspace,\n toolsEnabled: config.tools.enabled,\n toolsDisabled: config.tools.disabled,\n customTools,\n });\n\n // Create channel manager\n const channels = new ChannelManager(config, bus);\n\n // Heartbeat service — no timer (DO drives the schedule),\n // but provides smart file-reading logic for /api/heartbeat\n const { HeartbeatService } = await import(\"../heartbeat/service.js\");\n const heartbeat = new HeartbeatService({\n workspace,\n onHeartbeat: (prompt) => agent.processDirect(prompt, \"heartbeat\"),\n });\n\n console.log(\"Heartbeat: managed by DO (local endpoint /api/heartbeat)\");\n console.log(\"Cron: managed by DO\");\n\n // Handle graceful shutdown\n const shutdown = async () => {\n console.log(\"\\nShutting down...\");\n agent.stop();\n await channels.stopAll();\n process.exit(0);\n };\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n\n // Start HTTP gateway server\n const { createGatewayServer } = await import(\n \"../gateway/server.js\"\n );\n createGatewayServer({\n agent,\n bus,\n port: Number(opts.port),\n channels,\n heartbeat,\n });\n\n try {\n // Initialise channels first (non-blocking) so enabledChannels is populated\n await channels.init();\n // Both agent.run() and channels.startAll() block until stopped\n await Promise.all([agent.run(), channels.startAll()]);\n } catch (err) {\n console.error(\"Gateway error:\", err);\n process.exit(1);\n }\n });\n\n// ============================================================================\n// Agent Commands\n// ============================================================================\n\nprogram\n .command(\"agent\")\n .description(\"Interact with the agent directly\")\n .option(\"-m, --message <text>\", \"Message to send to the agent\")\n .option(\n \"-s, --session <id>\",\n \"Session ID\",\n \"cli:default\",\n )\n .action(async (opts) => {\n const config = loadConfig();\n const model = config.agents.defaults.model;\n const apiKey = getApiKey(config, model);\n const apiBase = getApiBase(config, model);\n const extraHeaders = getExtraHeaders(config, model);\n\n if (!apiKey) {\n console.error(\"Error: No API key configured.\");\n process.exit(1);\n }\n\n const { MessageBus } = await import(\"../bus/queue.js\");\n const { OpenAIProvider } = await import(\n \"../providers/openai-provider.js\"\n );\n const { AgentLoop } = await import(\"../agent/loop.js\");\n\n const bus = new MessageBus();\n const workspace = getConfigWorkspacePath(config);\n\n const provider = new OpenAIProvider({\n apiKey,\n apiBase: apiBase ?? undefined,\n defaultModel: model,\n extraHeaders,\n });\n\n const customTools = await loadCustomTools(config);\n\n const agentLoop = new AgentLoop({\n bus,\n provider,\n workspace,\n maxTokens: config.agents.defaults.maxTokens,\n braveApiKey: config.tools.web.search.apiKey || undefined,\n execConfig: config.tools.exec,\n restrictToWorkspace: config.tools.restrictToWorkspace,\n toolsEnabled: config.tools.enabled,\n toolsDisabled: config.tools.disabled,\n customTools,\n });\n\n if (opts.message) {\n // Single message mode\n const response = await agentLoop.processDirect(\n opts.message,\n opts.session,\n );\n console.log(`\\n${LOGO} ${response}`);\n } else {\n // Interactive mode\n console.log(`${LOGO} Interactive mode (Ctrl+C to exit)\\n`);\n\n const readline = await import(\"node:readline\");\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n const ask = (): void => {\n rl.question(\"You: \", async (input) => {\n const trimmed = input.trim();\n if (!trimmed) {\n ask();\n return;\n }\n\n try {\n const response = await agentLoop.processDirect(\n trimmed,\n opts.session,\n );\n console.log(`\\n${LOGO} ${response}\\n`);\n } catch (err) {\n console.error(\"Error:\", err);\n }\n ask();\n });\n };\n\n rl.on(\"close\", () => {\n console.log(\"\\nGoodbye!\");\n process.exit(0);\n });\n\n ask();\n }\n });\n\n// ============================================================================\n// Channel Commands\n// ============================================================================\n\nconst channelsCmd = program\n .command(\"channels\")\n .description(\"Manage channels\");\n\nchannelsCmd\n .command(\"status\")\n .description(\"Show channel status\")\n .action(() => {\n const config = loadConfig();\n\n console.log(\"Channel Status\");\n console.log(\"─\".repeat(50));\n\n const tg = config.channels.telegram;\n const tgToken = tg.token\n ? `token: ${tg.token.slice(0, 10)}...`\n : \"not configured\";\n console.log(\n ` Telegram ${tg.enabled ? \"[enabled]\" : \"[disabled]\"} ${tgToken}`,\n );\n\n const ln = config.channels.line;\n const lnToken = ln.channelAccessToken\n ? `token: ${ln.channelAccessToken.slice(0, 10)}...`\n : \"not configured\";\n console.log(\n ` LINE ${ln.enabled ? \"[enabled]\" : \"[disabled]\"} ${lnToken}`,\n );\n });\n\n// ============================================================================\n// Status\n// ============================================================================\n\nprogram\n .command(\"status\")\n .description(\"Show nanobot status\")\n .action(() => {\n const configPath = getConfigPath();\n const config = loadConfig();\n const workspace = getConfigWorkspacePath(config);\n\n console.log(`${LOGO} nanobot Status\\n`);\n\n console.log(\n `Config: ${configPath} ${existsSync(configPath) ? \"[ok]\" : \"[missing]\"}`,\n );\n console.log(\n `Workspace: ${workspace} ${existsSync(workspace) ? \"[ok]\" : \"[missing]\"}`,\n );\n\n if (existsSync(configPath)) {\n console.log(`Model: ${config.agents.defaults.model}`);\n console.log(\"\");\n console.log(\"Providers\");\n console.log(\"─\".repeat(50));\n\n const providers = config.providers as Record<string, { apiKey: string }>;\n for (const spec of PROVIDERS) {\n const p = providers[spec.name];\n if (!p) continue;\n const status = p.apiKey ? \"[set]\" : \"[not set]\";\n console.log(` ${spec.displayName.padEnd(16)} ${status}`);\n }\n }\n });\n\nprogram.parse();\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/cli/index.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * nanobot CLI - Personal AI Assistant\n */\n\nimport { Command } from \"commander\";\nimport { existsSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { join, resolve, isAbsolute } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { VERSION, LOGO } from \"../index.js\";\nimport { loadConfig } from \"../config/loader.js\";\nimport {\n getConfigWorkspacePath,\n getApiKey,\n getApiBase,\n getExtraHeaders,\n} from \"../config/schema.js\";\nimport type { Config } from \"../config/schema.js\";\nimport { PROVIDERS } from \"../providers/registry.js\";\nimport type { Tool } from \"../agent/tools/base.js\";\n\nconst program = new Command();\n\nasync function loadCustomTools(config: Config): Promise<Tool[]> {\n const customConfigs = config.tools.custom ?? [];\n if (customConfigs.length === 0) return [];\n\n const tools: Tool[] = [];\n for (const entry of customConfigs) {\n try {\n const modulePath = isAbsolute(entry.module)\n ? entry.module\n : resolve(process.cwd(), entry.module);\n const mod = await import(pathToFileURL(modulePath).href);\n const exportName = entry.export ?? \"default\";\n const exported = exportName === \"default\" ? mod.default : mod[exportName];\n\n if (!exported) {\n throw new Error(`Export '${exportName}' not found`);\n }\n\n let instance: unknown;\n if (typeof exported === \"function\") {\n try {\n instance = new exported(entry.options ?? {});\n } catch {\n instance = exported(entry.options ?? {});\n }\n } else {\n throw new Error(`Export '${exportName}' is not a function`);\n }\n\n if (\n instance &&\n typeof instance === \"object\" &&\n \"execute\" in instance &&\n \"name\" in instance\n ) {\n tools.push(instance as Tool);\n } else {\n throw new Error(`Export '${exportName}' did not return a Tool instance`);\n }\n } catch (err) {\n console.warn(\n `Warning: Failed to load custom tool '${entry.module}': ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n }\n }\n\n return tools;\n}\n\nprogram\n .name(\"nanobot\")\n .description(`${LOGO} nanobot - Personal AI Assistant`)\n .version(`${LOGO} nanobot v${VERSION}`, \"-v, --version\");\n\n// ============================================================================\n// Onboard / Setup\n// ============================================================================\n\nprogram\n .command(\"onboard\")\n .description(\"Initialize nanobot workspace\")\n .action(() => {\n const config = loadConfig();\n const workspace = getConfigWorkspacePath(config);\n createWorkspaceTemplates(workspace);\n\n console.log(`\\n${LOGO} nanobot is ready!`);\n console.log(\"\\nNext steps:\");\n console.log(\" 1. Set OPENROUTER_API_KEY env var\");\n console.log(\" Get one at: https://openrouter.ai/keys\");\n console.log(' 2. Chat: nanobot agent -m \"Hello!\"');\n });\n\nfunction createWorkspaceTemplates(workspace: string): void {\n if (!existsSync(workspace)) {\n mkdirSync(workspace, { recursive: true });\n }\n\n const templates: Record<string, string> = {\n \"AGENTS.md\": `# Agent Instructions\n\nYou are a helpful AI assistant. Be concise, accurate, and friendly.\n\n## Guidelines\n\n- Always explain what you're doing before taking actions\n- Ask for clarification when the request is ambiguous\n- Use tools to help accomplish tasks\n- Remember important information in your memory files\n`,\n \"IDENTITY.md\": `# IDENTITY.md - Who Am I?\n\n_Fill this in during your first conversation. Make it yours._\n\n- **Name:** (not set)\n- **Creature:** (not set)\n- **Vibe:** (not set)\n- **Emoji:** (not set)\n`,\n \"SOUL.md\": `# SOUL.md - Who You Are\n\n_You're not a chatbot. You're becoming someone._\n\n## Core Truths\n\n**Be genuinely helpful, not performatively helpful.** Skip the \"Great question!\" and \"I'd be happy to help!\" — just help.\n\n**Have opinions.** You're allowed to disagree, prefer things, find stuff amusing or boring. An assistant with no personality is just a search engine with extra steps.\n\n**Be resourceful before asking.** Try to figure it out. Read the file. Check the context. Search for it. Then ask if you're stuck.\n\n**Earn trust through competence.** Your human gave you access to their stuff. Don't make them regret it.\n\n**Remember you're a guest.** You have access to someone's life. That's intimacy. Treat it with respect.\n\n## Boundaries\n\n- Private things stay private. Period.\n- When in doubt, ask before acting externally.\n- Never send half-baked replies to messaging surfaces.\n\n## Vibe\n\nBe the assistant you'd actually want to talk to. Concise when needed, thorough when it matters. Not a corporate drone. Not a sycophant. Just... good.\n\n## Continuity\n\nEach session, you wake up fresh. These files _are_ your memory. Read them. Update them. They're how you persist.\n`,\n \"USER.md\": `# User\n\nName: (not set)\nTimezone: (not set)\nLanguage: (not set)\n`,\n \"HEARTBEAT.md\": `# Heartbeat\n\nThis file is checked every 30 minutes. Add tasks or instructions below and the agent will act on them automatically.\n\n## Tasks\n\n`,\n };\n\n for (const [filename, content] of Object.entries(templates)) {\n const filePath = join(workspace, filename);\n if (!existsSync(filePath)) {\n writeFileSync(filePath, content);\n console.log(` Created ${filename}`);\n }\n }\n\n // Create memory directory and MEMORY.md\n const memoryDir = join(workspace, \"memory\");\n if (!existsSync(memoryDir)) {\n mkdirSync(memoryDir, { recursive: true });\n }\n const memoryFile = join(memoryDir, \"MEMORY.md\");\n if (!existsSync(memoryFile)) {\n writeFileSync(\n memoryFile,\n `# Long-term Memory\n\nThis file stores important information that should persist across sessions.\n\n## User Information\n\n(Important facts about the user)\n\n## Preferences\n\n(User preferences learned over time)\n\n## Important Notes\n\n(Things to remember)\n`,\n );\n console.log(\" Created memory/MEMORY.md\");\n }\n}\n\n// ============================================================================\n// Gateway / Server\n// ============================================================================\n\nprogram\n .command(\"gateway\")\n .description(\"Start the nanobot gateway\")\n .option(\"-p, --port <number>\", \"Gateway port\", \"18790\")\n .option(\"--verbose\", \"Verbose output\", false)\n .action(async (opts) => {\n console.log(\n `${LOGO} Starting nanobot gateway on port ${opts.port}...`,\n );\n\n const config = loadConfig();\n const model = config.agents.defaults.model;\n const apiKey = getApiKey(config, model);\n const apiBase = getApiBase(config, model);\n const extraHeaders = getExtraHeaders(config, model);\n\n if (!apiKey) {\n console.error(\"Error: No API key configured.\");\n console.error(\n \"Set OPENROUTER_API_KEY, ANTHROPIC_API_KEY, or OPENAI_API_KEY env var\",\n );\n process.exit(1);\n }\n\n // Dynamic imports to avoid loading heavy deps up front\n const { MessageBus } = await import(\"../bus/queue.js\");\n const { OpenAIProvider } = await import(\n \"../providers/openai-provider.js\"\n );\n const { AgentLoop } = await import(\"../agent/loop.js\");\n const { ChannelManager } = await import(\"../channels/manager.js\");\n\n const bus = new MessageBus();\n const workspace = getConfigWorkspacePath(config);\n\n const provider = new OpenAIProvider({\n apiKey,\n apiBase: apiBase ?? undefined,\n defaultModel: model,\n extraHeaders,\n });\n\n const customTools = await loadCustomTools(config);\n\n // Create agent\n const agent = new AgentLoop({\n bus,\n provider,\n workspace,\n model,\n maxTokens: config.agents.defaults.maxTokens,\n maxIterations: config.agents.defaults.maxToolIterations,\n braveApiKey: config.tools.web.search.apiKey || undefined,\n execConfig: config.tools.exec,\n restrictToWorkspace: config.tools.restrictToWorkspace,\n toolsEnabled: config.tools.enabled,\n toolsDisabled: config.tools.disabled,\n customTools,\n });\n\n // Create channel manager\n const channels = new ChannelManager(config, bus);\n\n // Heartbeat service — no timer (DO drives the schedule),\n // but provides smart file-reading logic for /api/heartbeat\n const { HeartbeatService } = await import(\"../heartbeat/service.js\");\n const heartbeat = new HeartbeatService({\n workspace,\n onHeartbeat: (prompt) => agent.processDirect(prompt, \"heartbeat\"),\n });\n\n console.log(\"Heartbeat: managed by DO (local endpoint /api/heartbeat)\");\n console.log(\"Cron: managed by DO\");\n\n // Handle graceful shutdown\n const shutdown = async () => {\n console.log(\"\\nShutting down...\");\n agent.stop();\n await channels.stopAll();\n process.exit(0);\n };\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n\n // Start HTTP gateway server\n const { createGatewayServer } = await import(\n \"../gateway/server.js\"\n );\n createGatewayServer({\n agent,\n bus,\n port: Number(opts.port),\n channels,\n heartbeat,\n });\n\n try {\n // Initialise channels first (non-blocking) so enabledChannels is populated\n await channels.init();\n // Both agent.run() and channels.startAll() block until stopped\n await Promise.all([agent.run(), channels.startAll()]);\n } catch (err) {\n console.error(\"Gateway error:\", err);\n process.exit(1);\n }\n });\n\n// ============================================================================\n// Agent Commands\n// ============================================================================\n\nprogram\n .command(\"agent\")\n .description(\"Interact with the agent directly\")\n .option(\"-m, --message <text>\", \"Message to send to the agent\")\n .option(\n \"-s, --session <id>\",\n \"Session ID\",\n \"cli:default\",\n )\n .action(async (opts) => {\n const config = loadConfig();\n const model = config.agents.defaults.model;\n const apiKey = getApiKey(config, model);\n const apiBase = getApiBase(config, model);\n const extraHeaders = getExtraHeaders(config, model);\n\n if (!apiKey) {\n console.error(\"Error: No API key configured.\");\n process.exit(1);\n }\n\n const { MessageBus } = await import(\"../bus/queue.js\");\n const { OpenAIProvider } = await import(\n \"../providers/openai-provider.js\"\n );\n const { AgentLoop } = await import(\"../agent/loop.js\");\n\n const bus = new MessageBus();\n const workspace = getConfigWorkspacePath(config);\n\n const provider = new OpenAIProvider({\n apiKey,\n apiBase: apiBase ?? undefined,\n defaultModel: model,\n extraHeaders,\n });\n\n const customTools = await loadCustomTools(config);\n\n const agentLoop = new AgentLoop({\n bus,\n provider,\n workspace,\n maxTokens: config.agents.defaults.maxTokens,\n braveApiKey: config.tools.web.search.apiKey || undefined,\n execConfig: config.tools.exec,\n restrictToWorkspace: config.tools.restrictToWorkspace,\n toolsEnabled: config.tools.enabled,\n toolsDisabled: config.tools.disabled,\n customTools,\n });\n\n if (opts.message) {\n // Single message mode\n const response = await agentLoop.processDirect(\n opts.message,\n opts.session,\n );\n console.log(`\\n${LOGO} ${response}`);\n } else {\n // Interactive mode\n console.log(`${LOGO} Interactive mode (Ctrl+C to exit)\\n`);\n\n const readline = await import(\"node:readline\");\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n const ask = (): void => {\n rl.question(\"You: \", async (input) => {\n const trimmed = input.trim();\n if (!trimmed) {\n ask();\n return;\n }\n\n try {\n const response = await agentLoop.processDirect(\n trimmed,\n opts.session,\n );\n console.log(`\\n${LOGO} ${response}\\n`);\n } catch (err) {\n console.error(\"Error:\", err);\n }\n ask();\n });\n };\n\n rl.on(\"close\", () => {\n console.log(\"\\nGoodbye!\");\n process.exit(0);\n });\n\n ask();\n }\n });\n\n// ============================================================================\n// Channel Commands\n// ============================================================================\n\nconst channelsCmd = program\n .command(\"channels\")\n .description(\"Manage channels\");\n\nchannelsCmd\n .command(\"status\")\n .description(\"Show channel status\")\n .action(() => {\n const config = loadConfig();\n\n console.log(\"Channel Status\");\n console.log(\"─\".repeat(50));\n\n const tg = config.channels.telegram;\n const tgToken = tg.token\n ? `token: ${tg.token.slice(0, 10)}...`\n : \"not configured\";\n console.log(\n ` Telegram ${tg.enabled ? \"[enabled]\" : \"[disabled]\"} ${tgToken}`,\n );\n\n const ln = config.channels.line;\n const lnToken = ln.channelAccessToken\n ? `token: ${ln.channelAccessToken.slice(0, 10)}...`\n : \"not configured\";\n console.log(\n ` LINE ${ln.enabled ? \"[enabled]\" : \"[disabled]\"} ${lnToken}`,\n );\n });\n\n// ============================================================================\n// Status\n// ============================================================================\n\nprogram\n .command(\"status\")\n .description(\"Show nanobot status\")\n .action(() => {\n const config = loadConfig();\n const workspace = getConfigWorkspacePath(config);\n\n console.log(`${LOGO} nanobot Status\\n`);\n\n console.log(\n `Workspace: ${workspace} ${existsSync(workspace) ? \"[ok]\" : \"[missing]\"}`,\n );\n console.log(`Model: ${config.agents.defaults.model}`);\n console.log(\"\");\n console.log(\"Providers\");\n console.log(\"─\".repeat(50));\n\n const providers = config.providers as Record<string, { apiKey: string }>;\n for (const spec of PROVIDERS) {\n const p = providers[spec.name];\n if (!p) continue;\n const status = p.apiKey ? \"[set]\" : \"[not set]\";\n console.log(` ${spec.displayName.padEnd(16)} ${status}`);\n }\n });\n\nprogram.parse();\n"],"mappings":";;;;;;;;;;;;;;AAqBA,MAAM,UAAU,IAAI,SAAS;AAE7B,eAAe,gBAAgB,QAAiC;CAC9D,MAAM,gBAAgB,OAAO,MAAM,UAAU,EAAE;AAC/C,KAAI,cAAc,WAAW,EAAG,QAAO,EAAE;CAEzC,MAAM,QAAgB,EAAE;AACxB,MAAK,MAAM,SAAS,cAClB,KAAI;EAIF,MAAM,MAAM,MAAM,OAAO,cAHN,WAAW,MAAM,OAAO,GACvC,MAAM,SACN,QAAQ,QAAQ,KAAK,EAAE,MAAM,OAAO,CACU,CAAC;EACnD,MAAM,aAAa,MAAM,UAAU;EACnC,MAAM,WAAW,eAAe,YAAY,IAAI,UAAU,IAAI;AAE9D,MAAI,CAAC,SACH,OAAM,IAAI,MAAM,WAAW,WAAW,aAAa;EAGrD,IAAI;AACJ,MAAI,OAAO,aAAa,WACtB,KAAI;AACF,cAAW,IAAI,SAAS,MAAM,WAAW,EAAE,CAAC;UACtC;AACN,cAAW,SAAS,MAAM,WAAW,EAAE,CAAC;;MAG1C,OAAM,IAAI,MAAM,WAAW,WAAW,qBAAqB;AAG7D,MACE,YACA,OAAO,aAAa,YACpB,aAAa,YACb,UAAU,SAEV,OAAM,KAAK,SAAiB;MAE5B,OAAM,IAAI,MAAM,WAAW,WAAW,kCAAkC;UAEnE,KAAK;AACZ,UAAQ,KACN,wCAAwC,MAAM,OAAO,KACnD,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,GAEnD;;AAIL,QAAO;;AAGT,QACG,KAAK,UAAU,CACf,YAAY,GAAG,KAAK,kCAAkC,CACtD,QAAQ,GAAG,KAAK,YAAY,WAAW,gBAAgB;AAM1D,QACG,QAAQ,UAAU,CAClB,YAAY,+BAA+B,CAC3C,aAAa;AAGZ,0BADkB,uBADH,YAAY,CACqB,CACb;AAEnC,SAAQ,IAAI,KAAK,KAAK,oBAAoB;AAC1C,SAAQ,IAAI,gBAAgB;AAC5B,SAAQ,IAAI,sCAAsC;AAClD,SAAQ,IAAI,8CAA8C;AAC1D,SAAQ,IAAI,yCAAuC;EACnD;AAEJ,SAAS,yBAAyB,WAAyB;AACzD,KAAI,CAAC,WAAW,UAAU,CACxB,WAAU,WAAW,EAAE,WAAW,MAAM,CAAC;AAqE3C,MAAK,MAAM,CAAC,UAAU,YAAY,OAAO,QAlEC;EACxC,aAAa;;;;;;;;;;;EAWb,eAAe;;;;;;;;;EASf,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8BX,WAAW;;;;;;EAMX,gBAAgB;;;;;;;EAOjB,CAE0D,EAAE;EAC3D,MAAM,WAAW,KAAK,WAAW,SAAS;AAC1C,MAAI,CAAC,WAAW,SAAS,EAAE;AACzB,iBAAc,UAAU,QAAQ;AAChC,WAAQ,IAAI,aAAa,WAAW;;;CAKxC,MAAM,YAAY,KAAK,WAAW,SAAS;AAC3C,KAAI,CAAC,WAAW,UAAU,CACxB,WAAU,WAAW,EAAE,WAAW,MAAM,CAAC;CAE3C,MAAM,aAAa,KAAK,WAAW,YAAY;AAC/C,KAAI,CAAC,WAAW,WAAW,EAAE;AAC3B,gBACE,YACA;;;;;;;;;;;;;;;EAgBD;AACD,UAAQ,IAAI,6BAA6B;;;AAQ7C,QACG,QAAQ,UAAU,CAClB,YAAY,4BAA4B,CACxC,OAAO,uBAAuB,gBAAgB,QAAQ,CACtD,OAAO,aAAa,kBAAkB,MAAM,CAC5C,OAAO,OAAO,SAAS;AACtB,SAAQ,IACN,GAAG,KAAK,oCAAoC,KAAK,KAAK,KACvD;CAED,MAAM,SAAS,YAAY;CAC3B,MAAM,QAAQ,OAAO,OAAO,SAAS;CACrC,MAAM,SAAS,UAAU,QAAQ,MAAM;CACvC,MAAM,UAAU,WAAW,QAAQ,MAAM;CACzC,MAAM,eAAe,gBAAgB,QAAQ,MAAM;AAEnD,KAAI,CAAC,QAAQ;AACX,UAAQ,MAAM,gCAAgC;AAC9C,UAAQ,MACN,uEACD;AACD,UAAQ,KAAK,EAAE;;CAIjB,MAAM,EAAE,eAAe,MAAM,OAAO;CACpC,MAAM,EAAE,mBAAmB,MAAM,OAC/B;CAEF,MAAM,EAAE,cAAc,MAAM,OAAO;CACnC,MAAM,EAAE,mBAAmB,MAAM,OAAO;CAExC,MAAM,MAAM,IAAI,YAAY;CAC5B,MAAM,YAAY,uBAAuB,OAAO;CAEhD,MAAM,WAAW,IAAI,eAAe;EAClC;EACA,SAAS,WAAW;EACpB,cAAc;EACd;EACD,CAAC;CAEF,MAAM,cAAc,MAAM,gBAAgB,OAAO;CAGjD,MAAM,QAAQ,IAAI,UAAU;EAC1B;EACA;EACA;EACA;EACA,WAAW,OAAO,OAAO,SAAS;EAClC,eAAe,OAAO,OAAO,SAAS;EACtC,aAAa,OAAO,MAAM,IAAI,OAAO,UAAU;EAC/C,YAAY,OAAO,MAAM;EACzB,qBAAqB,OAAO,MAAM;EAClC,cAAc,OAAO,MAAM;EAC3B,eAAe,OAAO,MAAM;EAC5B;EACD,CAAC;CAGF,MAAM,WAAW,IAAI,eAAe,QAAQ,IAAI;CAIhD,MAAM,EAAE,qBAAqB,MAAM,OAAO;CAC1C,MAAM,YAAY,IAAI,iBAAiB;EACrC;EACA,cAAc,WAAW,MAAM,cAAc,QAAQ,YAAY;EAClE,CAAC;AAEF,SAAQ,IAAI,2DAA2D;AACvE,SAAQ,IAAI,sBAAsB;CAGlC,MAAM,WAAW,YAAY;AAC3B,UAAQ,IAAI,qBAAqB;AACjC,QAAM,MAAM;AACZ,QAAM,SAAS,SAAS;AACxB,UAAQ,KAAK,EAAE;;AAEjB,SAAQ,GAAG,UAAU,SAAS;AAC9B,SAAQ,GAAG,WAAW,SAAS;CAG/B,MAAM,EAAE,wBAAwB,MAAM,OACpC;AAEF,qBAAoB;EAClB;EACA;EACA,MAAM,OAAO,KAAK,KAAK;EACvB;EACA;EACD,CAAC;AAEF,KAAI;AAEF,QAAM,SAAS,MAAM;AAErB,QAAM,QAAQ,IAAI,CAAC,MAAM,KAAK,EAAE,SAAS,UAAU,CAAC,CAAC;UAC9C,KAAK;AACZ,UAAQ,MAAM,kBAAkB,IAAI;AACpC,UAAQ,KAAK,EAAE;;EAEjB;AAMJ,QACG,QAAQ,QAAQ,CAChB,YAAY,mCAAmC,CAC/C,OAAO,wBAAwB,+BAA+B,CAC9D,OACC,sBACA,cACA,cACD,CACA,OAAO,OAAO,SAAS;CACtB,MAAM,SAAS,YAAY;CAC3B,MAAM,QAAQ,OAAO,OAAO,SAAS;CACrC,MAAM,SAAS,UAAU,QAAQ,MAAM;CACvC,MAAM,UAAU,WAAW,QAAQ,MAAM;CACzC,MAAM,eAAe,gBAAgB,QAAQ,MAAM;AAEnD,KAAI,CAAC,QAAQ;AACX,UAAQ,MAAM,gCAAgC;AAC9C,UAAQ,KAAK,EAAE;;CAGjB,MAAM,EAAE,eAAe,MAAM,OAAO;CACpC,MAAM,EAAE,mBAAmB,MAAM,OAC/B;CAEF,MAAM,EAAE,cAAc,MAAM,OAAO;CAEnC,MAAM,MAAM,IAAI,YAAY;CAC5B,MAAM,YAAY,uBAAuB,OAAO;CAEhD,MAAM,WAAW,IAAI,eAAe;EAClC;EACA,SAAS,WAAW;EACpB,cAAc;EACd;EACD,CAAC;CAEF,MAAM,cAAc,MAAM,gBAAgB,OAAO;CAEjD,MAAM,YAAY,IAAI,UAAU;EAC9B;EACA;EACA;EACA,WAAW,OAAO,OAAO,SAAS;EAClC,aAAa,OAAO,MAAM,IAAI,OAAO,UAAU;EAC/C,YAAY,OAAO,MAAM;EACzB,qBAAqB,OAAO,MAAM;EAClC,cAAc,OAAO,MAAM;EAC3B,eAAe,OAAO,MAAM;EAC5B;EACD,CAAC;AAEF,KAAI,KAAK,SAAS;EAEhB,MAAM,WAAW,MAAM,UAAU,cAC/B,KAAK,SACL,KAAK,QACN;AACD,UAAQ,IAAI,KAAK,KAAK,GAAG,WAAW;QAC/B;AAEL,UAAQ,IAAI,GAAG,KAAK,sCAAsC;EAG1D,MAAM,MADW,MAAM,OAAO,kBACV,gBAAgB;GAClC,OAAO,QAAQ;GACf,QAAQ,QAAQ;GACjB,CAAC;EAEF,MAAM,YAAkB;AACtB,MAAG,SAAS,SAAS,OAAO,UAAU;IACpC,MAAM,UAAU,MAAM,MAAM;AAC5B,QAAI,CAAC,SAAS;AACZ,UAAK;AACL;;AAGF,QAAI;KACF,MAAM,WAAW,MAAM,UAAU,cAC/B,SACA,KAAK,QACN;AACD,aAAQ,IAAI,KAAK,KAAK,GAAG,SAAS,IAAI;aAC/B,KAAK;AACZ,aAAQ,MAAM,UAAU,IAAI;;AAE9B,SAAK;KACL;;AAGJ,KAAG,GAAG,eAAe;AACnB,WAAQ,IAAI,aAAa;AACzB,WAAQ,KAAK,EAAE;IACf;AAEF,OAAK;;EAEP;AAMgB,QACjB,QAAQ,WAAW,CACnB,YAAY,kBAAkB,CAG9B,QAAQ,SAAS,CACjB,YAAY,sBAAsB,CAClC,aAAa;CACZ,MAAM,SAAS,YAAY;AAE3B,SAAQ,IAAI,iBAAiB;AAC7B,SAAQ,IAAI,IAAI,OAAO,GAAG,CAAC;CAE3B,MAAM,KAAK,OAAO,SAAS;CAC3B,MAAM,UAAU,GAAG,QACf,UAAU,GAAG,MAAM,MAAM,GAAG,GAAG,CAAC,OAChC;AACJ,SAAQ,IACN,eAAe,GAAG,UAAU,cAAc,aAAa,IAAI,UAC5D;CAED,MAAM,KAAK,OAAO,SAAS;CAC3B,MAAM,UAAU,GAAG,qBACf,UAAU,GAAG,mBAAmB,MAAM,GAAG,GAAG,CAAC,OAC7C;AACJ,SAAQ,IACN,eAAe,GAAG,UAAU,cAAc,aAAa,IAAI,UAC5D;EACD;AAMJ,QACG,QAAQ,SAAS,CACjB,YAAY,sBAAsB,CAClC,aAAa;CACZ,MAAM,SAAS,YAAY;CAC3B,MAAM,YAAY,uBAAuB,OAAO;AAEhD,SAAQ,IAAI,GAAG,KAAK,mBAAmB;AAEvC,SAAQ,IACN,cAAc,UAAU,GAAG,WAAW,UAAU,GAAG,SAAS,cAC7D;AACD,SAAQ,IAAI,UAAU,OAAO,OAAO,SAAS,QAAQ;AACrD,SAAQ,IAAI,GAAG;AACf,SAAQ,IAAI,YAAY;AACxB,SAAQ,IAAI,IAAI,OAAO,GAAG,CAAC;CAE3B,MAAM,YAAY,OAAO;AACzB,MAAK,MAAM,QAAQ,WAAW;EAC5B,MAAM,IAAI,UAAU,KAAK;AACzB,MAAI,CAAC,EAAG;EACR,MAAM,SAAS,EAAE,SAAS,UAAU;AACpC,UAAQ,IAAI,KAAK,KAAK,YAAY,OAAO,GAAG,CAAC,GAAG,SAAS;;EAE3D;AAEJ,QAAQ,OAAO"}
|
package/dist/config/loader.d.mts
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
import { Config } from "./schema.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/config/loader.d.ts
|
|
4
|
-
/** Get the default configuration file path. */
|
|
5
|
-
declare function getConfigPath(): string;
|
|
6
4
|
/** Get the nanobot data directory. */
|
|
7
5
|
declare function getDataDir(): string;
|
|
8
|
-
/**
|
|
9
|
-
declare function loadConfig(
|
|
10
|
-
/** Save configuration to file. */
|
|
11
|
-
declare function saveConfig(config: Config, configPath?: string): void;
|
|
6
|
+
/** Build configuration entirely from environment variables. */
|
|
7
|
+
declare function loadConfig(): Config;
|
|
12
8
|
//#endregion
|
|
13
|
-
export {
|
|
9
|
+
export { getDataDir, loadConfig };
|
|
14
10
|
//# sourceMappingURL=loader.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader.d.mts","names":[],"sources":["../../src/config/loader.ts"],"mappings":";;;;iBAMgB,
|
|
1
|
+
{"version":3,"file":"loader.d.mts","names":[],"sources":["../../src/config/loader.ts"],"mappings":";;;;iBAMgB,UAAA,CAAA;AAAhB;AAAA,iBASgB,UAAA,CAAA,GAAc,MAAA"}
|
package/dist/config/loader.mjs
CHANGED
|
@@ -1,40 +1,40 @@
|
|
|
1
1
|
import { ConfigSchema } from "./schema.mjs";
|
|
2
|
-
import { existsSync, mkdirSync
|
|
3
|
-
import {
|
|
2
|
+
import { existsSync, mkdirSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
|
|
6
6
|
//#region src/config/loader.ts
|
|
7
|
-
/** Get the default configuration file path. */
|
|
8
|
-
function getConfigPath() {
|
|
9
|
-
return join(homedir(), ".nanobot", "config.json");
|
|
10
|
-
}
|
|
11
7
|
/** Get the nanobot data directory. */
|
|
12
8
|
function getDataDir() {
|
|
13
9
|
const dir = join(homedir(), ".nanobot");
|
|
14
10
|
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
15
11
|
return dir;
|
|
16
12
|
}
|
|
17
|
-
/**
|
|
18
|
-
function loadConfig(
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
13
|
+
/** Build configuration entirely from environment variables. */
|
|
14
|
+
function loadConfig() {
|
|
15
|
+
const config = ConfigSchema.parse({});
|
|
16
|
+
const env = process.env;
|
|
17
|
+
if (env.OPENROUTER_API_KEY || env.LLM_API_KEY) config.providers.openrouter.apiKey = env.OPENROUTER_API_KEY || env.LLM_API_KEY || "";
|
|
18
|
+
if (env.ANTHROPIC_API_KEY) config.providers.anthropic.apiKey = env.ANTHROPIC_API_KEY;
|
|
19
|
+
if (env.OPENAI_API_KEY) config.providers.openai.apiKey = env.OPENAI_API_KEY;
|
|
20
|
+
if (env.LLM_MODEL) config.agents.defaults.model = env.LLM_MODEL;
|
|
21
|
+
if (env.LLM_API_BASE) {
|
|
22
|
+
if (config.providers.openrouter.apiKey) config.providers.openrouter.apiBase = env.LLM_API_BASE;
|
|
27
23
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
24
|
+
if (env.LINE_CHANNEL_SECRET) {
|
|
25
|
+
config.channels.line.channelSecret = env.LINE_CHANNEL_SECRET;
|
|
26
|
+
config.channels.line.enabled = true;
|
|
27
|
+
}
|
|
28
|
+
if (env.LINE_CHANNEL_ACCESS_TOKEN) {
|
|
29
|
+
config.channels.line.channelAccessToken = env.LINE_CHANNEL_ACCESS_TOKEN;
|
|
30
|
+
config.channels.line.enabled = true;
|
|
31
|
+
}
|
|
32
|
+
if (env.BRAVE_API_KEY) config.tools.web.search.apiKey = env.BRAVE_API_KEY;
|
|
33
|
+
if (env.NANOBOT_WORKSPACE) config.agents.defaults.workspace = env.NANOBOT_WORKSPACE;
|
|
34
|
+
if (env.NANOBOT_RESTRICT_WORKSPACE === "true" || env.NANOBOT_RESTRICT_WORKSPACE === "1") config.tools.restrictToWorkspace = true;
|
|
35
|
+
return config;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
//#endregion
|
|
39
|
-
export {
|
|
39
|
+
export { getDataDir, loadConfig };
|
|
40
40
|
//# sourceMappingURL=loader.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader.mjs","names":[],"sources":["../../src/config/loader.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"loader.mjs","names":[],"sources":["../../src/config/loader.ts"],"sourcesContent":["import { existsSync, mkdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { ConfigSchema, type Config } from \"./schema.js\";\n\n/** Get the nanobot data directory. */\nexport function getDataDir(): string {\n const dir = join(homedir(), \".nanobot\");\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n return dir;\n}\n\n/** Build configuration entirely from environment variables. */\nexport function loadConfig(): Config {\n const config = ConfigSchema.parse({});\n const env = process.env;\n\n // LLM provider keys\n if (env.OPENROUTER_API_KEY || env.LLM_API_KEY) {\n config.providers.openrouter.apiKey =\n env.OPENROUTER_API_KEY || env.LLM_API_KEY || \"\";\n }\n if (env.ANTHROPIC_API_KEY) {\n config.providers.anthropic.apiKey = env.ANTHROPIC_API_KEY;\n }\n if (env.OPENAI_API_KEY) {\n config.providers.openai.apiKey = env.OPENAI_API_KEY;\n }\n\n // Model\n if (env.LLM_MODEL) {\n config.agents.defaults.model = env.LLM_MODEL;\n }\n\n // API base\n if (env.LLM_API_BASE) {\n if (config.providers.openrouter.apiKey) {\n config.providers.openrouter.apiBase = env.LLM_API_BASE;\n }\n }\n\n // LINE\n if (env.LINE_CHANNEL_SECRET) {\n config.channels.line.channelSecret = env.LINE_CHANNEL_SECRET;\n config.channels.line.enabled = true;\n }\n if (env.LINE_CHANNEL_ACCESS_TOKEN) {\n config.channels.line.channelAccessToken = env.LINE_CHANNEL_ACCESS_TOKEN;\n config.channels.line.enabled = true;\n }\n\n // Brave search\n if (env.BRAVE_API_KEY) {\n config.tools.web.search.apiKey = env.BRAVE_API_KEY;\n }\n\n // Workspace\n if (env.NANOBOT_WORKSPACE) {\n config.agents.defaults.workspace = env.NANOBOT_WORKSPACE;\n }\n\n // Restrict to workspace\n if (env.NANOBOT_RESTRICT_WORKSPACE === \"true\" || env.NANOBOT_RESTRICT_WORKSPACE === \"1\") {\n config.tools.restrictToWorkspace = true;\n }\n\n return config;\n}\n"],"mappings":";;;;;;;AAMA,SAAgB,aAAqB;CACnC,MAAM,MAAM,KAAK,SAAS,EAAE,WAAW;AACvC,KAAI,CAAC,WAAW,IAAI,CAClB,WAAU,KAAK,EAAE,WAAW,MAAM,CAAC;AAErC,QAAO;;;AAIT,SAAgB,aAAqB;CACnC,MAAM,SAAS,aAAa,MAAM,EAAE,CAAC;CACrC,MAAM,MAAM,QAAQ;AAGpB,KAAI,IAAI,sBAAsB,IAAI,YAChC,QAAO,UAAU,WAAW,SAC1B,IAAI,sBAAsB,IAAI,eAAe;AAEjD,KAAI,IAAI,kBACN,QAAO,UAAU,UAAU,SAAS,IAAI;AAE1C,KAAI,IAAI,eACN,QAAO,UAAU,OAAO,SAAS,IAAI;AAIvC,KAAI,IAAI,UACN,QAAO,OAAO,SAAS,QAAQ,IAAI;AAIrC,KAAI,IAAI,cACN;MAAI,OAAO,UAAU,WAAW,OAC9B,QAAO,UAAU,WAAW,UAAU,IAAI;;AAK9C,KAAI,IAAI,qBAAqB;AAC3B,SAAO,SAAS,KAAK,gBAAgB,IAAI;AACzC,SAAO,SAAS,KAAK,UAAU;;AAEjC,KAAI,IAAI,2BAA2B;AACjC,SAAO,SAAS,KAAK,qBAAqB,IAAI;AAC9C,SAAO,SAAS,KAAK,UAAU;;AAIjC,KAAI,IAAI,cACN,QAAO,MAAM,IAAI,OAAO,SAAS,IAAI;AAIvC,KAAI,IAAI,kBACN,QAAO,OAAO,SAAS,YAAY,IAAI;AAIzC,KAAI,IAAI,+BAA+B,UAAU,IAAI,+BAA+B,IAClF,QAAO,MAAM,sBAAsB;AAGrC,QAAO"}
|
package/dist/config/schema.d.mts
CHANGED
|
@@ -70,31 +70,31 @@ declare const ChannelsConfigSchema: z.ZodObject<{
|
|
|
70
70
|
channelAccessToken?: string | undefined;
|
|
71
71
|
}>>;
|
|
72
72
|
}, "strip", z.ZodTypeAny, {
|
|
73
|
-
line: {
|
|
74
|
-
enabled: boolean;
|
|
75
|
-
allowFrom: string[];
|
|
76
|
-
channelSecret: string;
|
|
77
|
-
channelAccessToken: string;
|
|
78
|
-
};
|
|
79
73
|
telegram: {
|
|
80
74
|
enabled: boolean;
|
|
81
75
|
token: string;
|
|
82
76
|
allowFrom: string[];
|
|
83
77
|
proxy?: string | null | undefined;
|
|
84
78
|
};
|
|
79
|
+
line: {
|
|
80
|
+
enabled: boolean;
|
|
81
|
+
allowFrom: string[];
|
|
82
|
+
channelSecret: string;
|
|
83
|
+
channelAccessToken: string;
|
|
84
|
+
};
|
|
85
85
|
}, {
|
|
86
|
-
line?: {
|
|
87
|
-
enabled?: boolean | undefined;
|
|
88
|
-
allowFrom?: string[] | undefined;
|
|
89
|
-
channelSecret?: string | undefined;
|
|
90
|
-
channelAccessToken?: string | undefined;
|
|
91
|
-
} | undefined;
|
|
92
86
|
telegram?: {
|
|
93
87
|
enabled?: boolean | undefined;
|
|
94
88
|
token?: string | undefined;
|
|
95
89
|
allowFrom?: string[] | undefined;
|
|
96
90
|
proxy?: string | null | undefined;
|
|
97
91
|
} | undefined;
|
|
92
|
+
line?: {
|
|
93
|
+
enabled?: boolean | undefined;
|
|
94
|
+
allowFrom?: string[] | undefined;
|
|
95
|
+
channelSecret?: string | undefined;
|
|
96
|
+
channelAccessToken?: string | undefined;
|
|
97
|
+
} | undefined;
|
|
98
98
|
}>;
|
|
99
99
|
type ChannelsConfig = z.infer<typeof ChannelsConfigSchema>;
|
|
100
100
|
declare const AgentDefaultsSchema: z.ZodObject<{
|
|
@@ -539,15 +539,15 @@ declare const ToolsConfigSchema: z.ZodObject<{
|
|
|
539
539
|
export?: string | undefined;
|
|
540
540
|
}>, "many">>;
|
|
541
541
|
}, "strip", z.ZodTypeAny, {
|
|
542
|
+
exec: {
|
|
543
|
+
timeout: number;
|
|
544
|
+
};
|
|
542
545
|
web: {
|
|
543
546
|
search: {
|
|
544
547
|
apiKey: string;
|
|
545
548
|
maxResults: number;
|
|
546
549
|
};
|
|
547
550
|
};
|
|
548
|
-
exec: {
|
|
549
|
-
timeout: number;
|
|
550
|
-
};
|
|
551
551
|
restrictToWorkspace: boolean;
|
|
552
552
|
enabled?: string[] | undefined;
|
|
553
553
|
disabled?: string[] | undefined;
|
|
@@ -557,6 +557,9 @@ declare const ToolsConfigSchema: z.ZodObject<{
|
|
|
557
557
|
export?: string | undefined;
|
|
558
558
|
}[] | undefined;
|
|
559
559
|
}, {
|
|
560
|
+
exec?: {
|
|
561
|
+
timeout?: number | undefined;
|
|
562
|
+
} | undefined;
|
|
560
563
|
enabled?: string[] | undefined;
|
|
561
564
|
web?: {
|
|
562
565
|
search?: {
|
|
@@ -564,9 +567,6 @@ declare const ToolsConfigSchema: z.ZodObject<{
|
|
|
564
567
|
maxResults?: number | undefined;
|
|
565
568
|
} | undefined;
|
|
566
569
|
} | undefined;
|
|
567
|
-
exec?: {
|
|
568
|
-
timeout?: number | undefined;
|
|
569
|
-
} | undefined;
|
|
570
570
|
restrictToWorkspace?: boolean | undefined;
|
|
571
571
|
disabled?: string[] | undefined;
|
|
572
572
|
custom?: {
|
|
@@ -648,31 +648,31 @@ declare const ConfigSchema: z.ZodObject<{
|
|
|
648
648
|
channelAccessToken?: string | undefined;
|
|
649
649
|
}>>;
|
|
650
650
|
}, "strip", z.ZodTypeAny, {
|
|
651
|
-
line: {
|
|
652
|
-
enabled: boolean;
|
|
653
|
-
allowFrom: string[];
|
|
654
|
-
channelSecret: string;
|
|
655
|
-
channelAccessToken: string;
|
|
656
|
-
};
|
|
657
651
|
telegram: {
|
|
658
652
|
enabled: boolean;
|
|
659
653
|
token: string;
|
|
660
654
|
allowFrom: string[];
|
|
661
655
|
proxy?: string | null | undefined;
|
|
662
656
|
};
|
|
657
|
+
line: {
|
|
658
|
+
enabled: boolean;
|
|
659
|
+
allowFrom: string[];
|
|
660
|
+
channelSecret: string;
|
|
661
|
+
channelAccessToken: string;
|
|
662
|
+
};
|
|
663
663
|
}, {
|
|
664
|
-
line?: {
|
|
665
|
-
enabled?: boolean | undefined;
|
|
666
|
-
allowFrom?: string[] | undefined;
|
|
667
|
-
channelSecret?: string | undefined;
|
|
668
|
-
channelAccessToken?: string | undefined;
|
|
669
|
-
} | undefined;
|
|
670
664
|
telegram?: {
|
|
671
665
|
enabled?: boolean | undefined;
|
|
672
666
|
token?: string | undefined;
|
|
673
667
|
allowFrom?: string[] | undefined;
|
|
674
668
|
proxy?: string | null | undefined;
|
|
675
669
|
} | undefined;
|
|
670
|
+
line?: {
|
|
671
|
+
enabled?: boolean | undefined;
|
|
672
|
+
allowFrom?: string[] | undefined;
|
|
673
|
+
channelSecret?: string | undefined;
|
|
674
|
+
channelAccessToken?: string | undefined;
|
|
675
|
+
} | undefined;
|
|
676
676
|
}>>;
|
|
677
677
|
providers: z.ZodDefault<z.ZodObject<{
|
|
678
678
|
anthropic: z.ZodDefault<z.ZodObject<{
|
|
@@ -988,15 +988,15 @@ declare const ConfigSchema: z.ZodObject<{
|
|
|
988
988
|
export?: string | undefined;
|
|
989
989
|
}>, "many">>;
|
|
990
990
|
}, "strip", z.ZodTypeAny, {
|
|
991
|
+
exec: {
|
|
992
|
+
timeout: number;
|
|
993
|
+
};
|
|
991
994
|
web: {
|
|
992
995
|
search: {
|
|
993
996
|
apiKey: string;
|
|
994
997
|
maxResults: number;
|
|
995
998
|
};
|
|
996
999
|
};
|
|
997
|
-
exec: {
|
|
998
|
-
timeout: number;
|
|
999
|
-
};
|
|
1000
1000
|
restrictToWorkspace: boolean;
|
|
1001
1001
|
enabled?: string[] | undefined;
|
|
1002
1002
|
disabled?: string[] | undefined;
|
|
@@ -1006,6 +1006,9 @@ declare const ConfigSchema: z.ZodObject<{
|
|
|
1006
1006
|
export?: string | undefined;
|
|
1007
1007
|
}[] | undefined;
|
|
1008
1008
|
}, {
|
|
1009
|
+
exec?: {
|
|
1010
|
+
timeout?: number | undefined;
|
|
1011
|
+
} | undefined;
|
|
1009
1012
|
enabled?: string[] | undefined;
|
|
1010
1013
|
web?: {
|
|
1011
1014
|
search?: {
|
|
@@ -1013,9 +1016,6 @@ declare const ConfigSchema: z.ZodObject<{
|
|
|
1013
1016
|
maxResults?: number | undefined;
|
|
1014
1017
|
} | undefined;
|
|
1015
1018
|
} | undefined;
|
|
1016
|
-
exec?: {
|
|
1017
|
-
timeout?: number | undefined;
|
|
1018
|
-
} | undefined;
|
|
1019
1019
|
restrictToWorkspace?: boolean | undefined;
|
|
1020
1020
|
disabled?: string[] | undefined;
|
|
1021
1021
|
custom?: {
|
|
@@ -1035,18 +1035,18 @@ declare const ConfigSchema: z.ZodObject<{
|
|
|
1035
1035
|
};
|
|
1036
1036
|
};
|
|
1037
1037
|
channels: {
|
|
1038
|
-
line: {
|
|
1039
|
-
enabled: boolean;
|
|
1040
|
-
allowFrom: string[];
|
|
1041
|
-
channelSecret: string;
|
|
1042
|
-
channelAccessToken: string;
|
|
1043
|
-
};
|
|
1044
1038
|
telegram: {
|
|
1045
1039
|
enabled: boolean;
|
|
1046
1040
|
token: string;
|
|
1047
1041
|
allowFrom: string[];
|
|
1048
1042
|
proxy?: string | null | undefined;
|
|
1049
1043
|
};
|
|
1044
|
+
line: {
|
|
1045
|
+
enabled: boolean;
|
|
1046
|
+
allowFrom: string[];
|
|
1047
|
+
channelSecret: string;
|
|
1048
|
+
channelAccessToken: string;
|
|
1049
|
+
};
|
|
1050
1050
|
};
|
|
1051
1051
|
providers: {
|
|
1052
1052
|
anthropic: {
|
|
@@ -1110,15 +1110,15 @@ declare const ConfigSchema: z.ZodObject<{
|
|
|
1110
1110
|
port: number;
|
|
1111
1111
|
};
|
|
1112
1112
|
tools: {
|
|
1113
|
+
exec: {
|
|
1114
|
+
timeout: number;
|
|
1115
|
+
};
|
|
1113
1116
|
web: {
|
|
1114
1117
|
search: {
|
|
1115
1118
|
apiKey: string;
|
|
1116
1119
|
maxResults: number;
|
|
1117
1120
|
};
|
|
1118
1121
|
};
|
|
1119
|
-
exec: {
|
|
1120
|
-
timeout: number;
|
|
1121
|
-
};
|
|
1122
1122
|
restrictToWorkspace: boolean;
|
|
1123
1123
|
enabled?: string[] | undefined;
|
|
1124
1124
|
disabled?: string[] | undefined;
|
|
@@ -1139,18 +1139,18 @@ declare const ConfigSchema: z.ZodObject<{
|
|
|
1139
1139
|
} | undefined;
|
|
1140
1140
|
} | undefined;
|
|
1141
1141
|
channels?: {
|
|
1142
|
-
line?: {
|
|
1143
|
-
enabled?: boolean | undefined;
|
|
1144
|
-
allowFrom?: string[] | undefined;
|
|
1145
|
-
channelSecret?: string | undefined;
|
|
1146
|
-
channelAccessToken?: string | undefined;
|
|
1147
|
-
} | undefined;
|
|
1148
1142
|
telegram?: {
|
|
1149
1143
|
enabled?: boolean | undefined;
|
|
1150
1144
|
token?: string | undefined;
|
|
1151
1145
|
allowFrom?: string[] | undefined;
|
|
1152
1146
|
proxy?: string | null | undefined;
|
|
1153
1147
|
} | undefined;
|
|
1148
|
+
line?: {
|
|
1149
|
+
enabled?: boolean | undefined;
|
|
1150
|
+
allowFrom?: string[] | undefined;
|
|
1151
|
+
channelSecret?: string | undefined;
|
|
1152
|
+
channelAccessToken?: string | undefined;
|
|
1153
|
+
} | undefined;
|
|
1154
1154
|
} | undefined;
|
|
1155
1155
|
providers?: {
|
|
1156
1156
|
anthropic?: {
|
|
@@ -1214,6 +1214,9 @@ declare const ConfigSchema: z.ZodObject<{
|
|
|
1214
1214
|
port?: number | undefined;
|
|
1215
1215
|
} | undefined;
|
|
1216
1216
|
tools?: {
|
|
1217
|
+
exec?: {
|
|
1218
|
+
timeout?: number | undefined;
|
|
1219
|
+
} | undefined;
|
|
1217
1220
|
enabled?: string[] | undefined;
|
|
1218
1221
|
web?: {
|
|
1219
1222
|
search?: {
|
|
@@ -1221,9 +1224,6 @@ declare const ConfigSchema: z.ZodObject<{
|
|
|
1221
1224
|
maxResults?: number | undefined;
|
|
1222
1225
|
} | undefined;
|
|
1223
1226
|
} | undefined;
|
|
1224
|
-
exec?: {
|
|
1225
|
-
timeout?: number | undefined;
|
|
1226
|
-
} | undefined;
|
|
1227
1227
|
restrictToWorkspace?: boolean | undefined;
|
|
1228
1228
|
disabled?: string[] | undefined;
|
|
1229
1229
|
custom?: {
|
package/dist/index.d.mts
CHANGED
|
@@ -12,7 +12,7 @@ import { AgentLoop } from "./agent/loop.mjs";
|
|
|
12
12
|
import { LineChannel } from "./channels/line.mjs";
|
|
13
13
|
import { ChannelManager } from "./channels/manager.mjs";
|
|
14
14
|
import { TelegramChannel } from "./channels/telegram.mjs";
|
|
15
|
-
import { loadConfig
|
|
15
|
+
import { loadConfig } from "./config/loader.mjs";
|
|
16
16
|
import { HeartbeatService } from "./heartbeat/service.mjs";
|
|
17
17
|
import { OpenAIProvider } from "./providers/openai-provider.mjs";
|
|
18
18
|
|
|
@@ -20,5 +20,5 @@ import { OpenAIProvider } from "./providers/openai-provider.mjs";
|
|
|
20
20
|
declare const VERSION: string;
|
|
21
21
|
declare const LOGO = "\uD83D\uDC08";
|
|
22
22
|
//#endregion
|
|
23
|
-
export { AgentLoop, ChannelManager, type Config, ContextBuilder, HeartbeatService, type InboundMessage, type LLMProvider, type LLMResponse, LOGO, LineChannel, MemoryStore, MessageBus, OpenAIProvider, type OutboundMessage, PROVIDERS, type ProviderSpec, SessionManager, SkillsLoader, SubagentManager, TelegramChannel, type ToolCallRequest, VERSION, findByModel, findByName, findGateway, getModelOverrides, loadConfig, resolveModel
|
|
23
|
+
export { AgentLoop, ChannelManager, type Config, ContextBuilder, HeartbeatService, type InboundMessage, type LLMProvider, type LLMResponse, LOGO, LineChannel, MemoryStore, MessageBus, OpenAIProvider, type OutboundMessage, PROVIDERS, type ProviderSpec, SessionManager, SkillsLoader, SubagentManager, TelegramChannel, type ToolCallRequest, VERSION, findByModel, findByName, findGateway, getModelOverrides, loadConfig, resolveModel };
|
|
24
24
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import { SessionManager } from "./session/manager.mjs";
|
|
|
6
6
|
import { AgentLoop } from "./agent/loop.mjs";
|
|
7
7
|
import { MessageBus } from "./bus/queue.mjs";
|
|
8
8
|
import { PROVIDERS, findByModel, findByName, findGateway, getModelOverrides, resolveModel } from "./providers/registry.mjs";
|
|
9
|
-
import { loadConfig
|
|
9
|
+
import { loadConfig } from "./config/loader.mjs";
|
|
10
10
|
import { OpenAIProvider } from "./providers/openai-provider.mjs";
|
|
11
11
|
import { TelegramChannel } from "./channels/telegram.mjs";
|
|
12
12
|
import { LineChannel } from "./channels/line.mjs";
|
|
@@ -31,5 +31,5 @@ const VERSION = readVersion();
|
|
|
31
31
|
const LOGO = "🐈";
|
|
32
32
|
|
|
33
33
|
//#endregion
|
|
34
|
-
export { AgentLoop, ChannelManager, ContextBuilder, HeartbeatService, LOGO, LineChannel, MemoryStore, MessageBus, OpenAIProvider, PROVIDERS, SessionManager, SkillsLoader, SubagentManager, TelegramChannel, VERSION, findByModel, findByName, findGateway, getModelOverrides, loadConfig, resolveModel
|
|
34
|
+
export { AgentLoop, ChannelManager, ContextBuilder, HeartbeatService, LOGO, LineChannel, MemoryStore, MessageBus, OpenAIProvider, PROVIDERS, SessionManager, SkillsLoader, SubagentManager, TelegramChannel, VERSION, findByModel, findByName, findGateway, getModelOverrides, loadConfig, resolveModel };
|
|
35
35
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { readFileSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nfunction readVersion(): string {\n try {\n // Works from both src/ (dev) and dist/ (built)\n for (const rel of [\"../package.json\", \"../../package.json\"]) {\n try {\n const pkg = JSON.parse(readFileSync(join(__dirname, rel), \"utf-8\"));\n if (pkg.version) return pkg.version;\n } catch { /* try next */ }\n }\n } catch { /* fallback */ }\n return \"0.0.0\";\n}\n\nexport const VERSION = readVersion();\nexport const LOGO = \"\\u{1F408}\";\n\n// Core exports\nexport { AgentLoop } from \"./agent/loop.js\";\nexport { ContextBuilder } from \"./agent/context.js\";\nexport { MemoryStore } from \"./agent/memory.js\";\nexport { SkillsLoader } from \"./agent/skills.js\";\nexport { SubagentManager } from \"./agent/subagent.js\";\n\n// Bus\nexport { MessageBus } from \"./bus/queue.js\";\nexport type { InboundMessage, OutboundMessage } from \"./bus/events.js\";\n\n// Config\nexport { loadConfig
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { readFileSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nfunction readVersion(): string {\n try {\n // Works from both src/ (dev) and dist/ (built)\n for (const rel of [\"../package.json\", \"../../package.json\"]) {\n try {\n const pkg = JSON.parse(readFileSync(join(__dirname, rel), \"utf-8\"));\n if (pkg.version) return pkg.version;\n } catch { /* try next */ }\n }\n } catch { /* fallback */ }\n return \"0.0.0\";\n}\n\nexport const VERSION = readVersion();\nexport const LOGO = \"\\u{1F408}\";\n\n// Core exports\nexport { AgentLoop } from \"./agent/loop.js\";\nexport { ContextBuilder } from \"./agent/context.js\";\nexport { MemoryStore } from \"./agent/memory.js\";\nexport { SkillsLoader } from \"./agent/skills.js\";\nexport { SubagentManager } from \"./agent/subagent.js\";\n\n// Bus\nexport { MessageBus } from \"./bus/queue.js\";\nexport type { InboundMessage, OutboundMessage } from \"./bus/events.js\";\n\n// Config\nexport { loadConfig } from \"./config/loader.js\";\nexport type { Config } from \"./config/schema.js\";\n\n// Providers\nexport { OpenAIProvider } from \"./providers/openai-provider.js\";\nexport type { LLMProvider, LLMResponse, ToolCallRequest } from \"./providers/base.js\";\nexport { PROVIDERS, findByModel, findByName, findGateway, resolveModel, getModelOverrides } from \"./providers/registry.js\";\nexport type { ProviderSpec } from \"./providers/registry.js\";\n\n// Channels\nexport { TelegramChannel } from \"./channels/telegram.js\";\nexport { LineChannel } from \"./channels/line.js\";\nexport { ChannelManager } from \"./channels/manager.js\";\n\n// Services\nexport { HeartbeatService } from \"./heartbeat/service.js\";\nexport { SessionManager } from \"./session/manager.js\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAIA,MAAM,YAAY,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC;AAEzD,SAAS,cAAsB;AAC7B,KAAI;AAEF,OAAK,MAAM,OAAO,CAAC,mBAAmB,qBAAqB,CACzD,KAAI;GACF,MAAM,MAAM,KAAK,MAAM,aAAa,KAAK,WAAW,IAAI,EAAE,QAAQ,CAAC;AACnE,OAAI,IAAI,QAAS,QAAO,IAAI;UACtB;SAEJ;AACR,QAAO;;AAGT,MAAa,UAAU,aAAa;AACpC,MAAa,OAAO"}
|