abtars 0.2.0 → 0.2.1-alpha.10
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 +15 -18
- package/bundle/{_registry.generated-6GO2PAMD.js → _registry.generated-4P6KTBTH.js} +8 -8
- package/bundle/_registry.generated-G2FSGHNY.js +36 -0
- package/bundle/_registry.generated-G2FSGHNY.js.map +7 -0
- package/bundle/_registry.generated-N74SMTUJ.js +36 -0
- package/bundle/_registry.generated-N74SMTUJ.js.map +7 -0
- package/bundle/_registry.generated-Y4VT5MBX.js +36 -0
- package/bundle/_registry.generated-Y4VT5MBX.js.map +7 -0
- package/bundle/abtars-browser.js +0 -1
- package/bundle/abtars-browser.js.map +2 -2
- package/bundle/abtars-cli.js +589 -547
- package/bundle/abtars-cli.js.map +4 -4
- package/bundle/abtars-restart.js +1 -1
- package/bundle/abtars-rss.js +0 -1
- package/bundle/abtars-rss.js.map +2 -2
- package/bundle/abtars-task.js +1 -2
- package/bundle/abtars-task.js.map +2 -2
- package/bundle/abtars.js +58 -33
- package/bundle/abtars.js.map +2 -2
- package/bundle/{agent-registry-SYUFNSVB.js → agent-registry-TM7EISOV.js} +2 -2
- package/bundle/{browse-delivery-64GQIUHG.js → browse-delivery-VTLEAVYA.js} +4 -4
- package/bundle/{browser-5H2Q7GEC.js → browser-JQGYFZAG.js} +5 -5
- package/bundle/browser-M4KDLN75.js +19 -0
- package/bundle/browser-REIXOJ6S.js +19 -0
- package/bundle/browser-VG2GTEND.js +19 -0
- package/bundle/{chunk-KFENC7BM.js → chunk-4YOTNYAE.js} +2 -2
- package/bundle/{chunk-KWBGYWDO.js → chunk-7I5C2U3C.js} +1 -1
- package/bundle/chunk-7I5C2U3C.js.map +7 -0
- package/bundle/{chunk-7STAQF5O.js → chunk-CNWXVXGH.js} +4 -4
- package/bundle/{chunk-7STAQF5O.js.map → chunk-CNWXVXGH.js.map} +2 -2
- package/bundle/{chunk-JX3ZZU3O.js → chunk-CU3O53U3.js} +28 -14
- package/bundle/chunk-CU3O53U3.js.map +7 -0
- package/bundle/chunk-EZZBO2QK.js +85 -0
- package/bundle/chunk-EZZBO2QK.js.map +7 -0
- package/bundle/{chunk-XLLSPBBT.js → chunk-GPL57SRN.js} +2 -2
- package/bundle/chunk-GPL57SRN.js.map +7 -0
- package/bundle/{chunk-PQ62LZNA.js → chunk-HEHD3GG5.js} +2 -2
- package/bundle/{chunk-A5OJYQNU.js → chunk-HTGVYNPE.js} +4 -4
- package/bundle/{chunk-WUTCGJDZ.js → chunk-HZRUBJ4Q.js} +148 -9
- package/bundle/chunk-HZRUBJ4Q.js.map +7 -0
- package/bundle/{chunk-ZVDVNSXK.js → chunk-J7CFCY4U.js} +2 -2
- package/bundle/{chunk-HXJRZWKA.js → chunk-JRK3UFBS.js} +106 -58
- package/bundle/chunk-JRK3UFBS.js.map +7 -0
- package/bundle/{chunk-GZUQQWBZ.js → chunk-KJOCXWJ5.js} +3 -3
- package/bundle/chunk-KJOCXWJ5.js.map +7 -0
- package/bundle/{chunk-K7P74UNQ.js → chunk-LDKSCXGL.js} +3 -3
- package/bundle/{chunk-K7P74UNQ.js.map → chunk-LDKSCXGL.js.map} +2 -2
- package/bundle/chunk-MNKLPRGY.js +138 -0
- package/bundle/chunk-MNKLPRGY.js.map +7 -0
- package/bundle/chunk-O73EZGKR.js +135 -0
- package/bundle/chunk-O73EZGKR.js.map +7 -0
- package/bundle/{chunk-OZ4IZFV4.js → chunk-Q7F5R2LL.js} +1 -1
- package/bundle/{chunk-OZ4IZFV4.js.map → chunk-Q7F5R2LL.js.map} +2 -2
- package/bundle/{chunk-XOCP5BMO.js → chunk-URAQLQ2U.js} +2 -2
- package/bundle/chunk-URAQLQ2U.js.map +7 -0
- package/bundle/{chunk-PPSB2ZIN.js → chunk-VREI2GUO.js} +18 -7
- package/bundle/chunk-VREI2GUO.js.map +7 -0
- package/bundle/chunk-VYR25H37.js +135 -0
- package/bundle/chunk-VYR25H37.js.map +7 -0
- package/bundle/{commands-E3Y4QUNF.js → commands-J6JAZ7D2.js} +5 -5
- package/bundle/{deploy-lib-import-ODLDL2DB.js → deploy-lib-import-PJXUADPS.js} +18 -16
- package/bundle/{direct-api-transport-TRV45NO6.js → direct-api-transport-ZSJHKYBX.js} +1 -1
- package/bundle/direct-api-transport-ZSJHKYBX.js.map +7 -0
- package/bundle/{discord-adapter-5DS7UTBE.js → discord-adapter-OCWSJTJU.js} +8 -8
- package/bundle/{doctor-R54GZPKL.js → doctor-XOGVHA4W.js} +13 -2
- package/bundle/{doctor-R54GZPKL.js.map → doctor-XOGVHA4W.js.map} +2 -2
- package/bundle/{install-RSLOS3Q5.js → install-6H3APAIJ.js} +3 -3
- package/bundle/{irc-adapter-RKRUSZXB.js → irc-adapter-AIEP6OX6.js} +6 -2
- package/bundle/irc-adapter-AIEP6OX6.js.map +7 -0
- package/bundle/{message-pipeline-CFVO73CK.js → message-pipeline-TCPHRBAK.js} +5 -5
- package/bundle/meta.json +4005 -3868
- package/bundle/{phase-transport-FEZ4SIJJ.js → phase-transport-ME2XDRR2.js} +3 -3
- package/bundle/restore-UH2UE2K5.js +151 -0
- package/bundle/restore-UH2UE2K5.js.map +7 -0
- package/bundle/rollback-D7AYTC7Z.js +11 -0
- package/bundle/{sleep-BPWX3FCN.js → sleep-IOWYTSMK.js} +2 -2
- package/bundle/{soul-bundle-BRIUDEQ2.js → soul-bundle-AH5S3MVI.js} +2 -2
- package/bundle/{soul-loader-GBXJ7EBH.js → soul-loader-N5FA4I6C.js} +3 -3
- package/bundle/soul-loader-N5FA4I6C.js.map +7 -0
- package/bundle/{start-4IWBKLWO.js → start-CBVKNEAT.js} +1 -1
- package/bundle/{subagent-runtime-MDBO6LHS.js → subagent-runtime-OOJXCKJF.js} +2 -2
- package/bundle/subagent-runtime-OOJXCKJF.js.map +7 -0
- package/bundle/{system-status-WAY63BYC.js → system-status-4JP36OKC.js} +12 -5
- package/bundle/{system-status-WAY63BYC.js.map → system-status-4JP36OKC.js.map} +2 -2
- package/bundle/{task-store-KIBFZL5A.js → task-store-LC7ZMS72.js} +2 -2
- package/bundle/task-store-LC7ZMS72.js.map +7 -0
- package/bundle/{telegram-adapter-MVA33IO2.js → telegram-adapter-QXHJQLLX.js} +5 -5
- package/bundle/{update-check-O5MS6B3L.js → update-check-XVY2IK2L.js} +2 -2
- package/bundle/update-check-XVY2IK2L.js.map +7 -0
- package/core/skills/tools/rss/SKILL.md +51 -0
- package/install-manifest.json +4 -3
- package/package.json +3 -4
- package/scripts/abtars-daemon.service +1 -1
- package/scripts/abtars-watchdog.service +1 -1
- package/scripts/abtars.sh +3 -5
- package/scripts/abtars@.service +1 -1
- package/scripts/build-and-deploy.sh +6 -0
- package/scripts/com.abtars.daemon.plist +1 -1
- package/scripts/com.abtars.watchdog.plist +1 -1
- package/scripts/daily-backup.sh +1 -61
- package/scripts/doctor.sh +48 -33
- package/scripts/watchdog.sh +53 -8
- package/bundle/chunk-GZUQQWBZ.js.map +0 -7
- package/bundle/chunk-HXJRZWKA.js.map +0 -7
- package/bundle/chunk-JX3ZZU3O.js.map +0 -7
- package/bundle/chunk-KWBGYWDO.js.map +0 -7
- package/bundle/chunk-PPSB2ZIN.js.map +0 -7
- package/bundle/chunk-WUTCGJDZ.js.map +0 -7
- package/bundle/chunk-XLLSPBBT.js.map +0 -7
- package/bundle/chunk-XOCP5BMO.js.map +0 -7
- package/bundle/direct-api-transport-TRV45NO6.js.map +0 -7
- package/bundle/irc-adapter-RKRUSZXB.js.map +0 -7
- package/bundle/passwd-QSHZJ2CG.js +0 -134
- package/bundle/passwd-QSHZJ2CG.js.map +0 -7
- package/bundle/restore-ROJF22R2.js +0 -47
- package/bundle/restore-ROJF22R2.js.map +0 -7
- package/core/core_templates/SOUL.md +0 -24
- package/core/core_templates/agent_notes.md +0 -7
- package/core/core_templates/core_facts.md +0 -10
- package/core/core_templates/user_profile.md +0 -7
- package/core/skills/tools/fxtwitter/SKILL.md +0 -52
- package/core/skills/tools/twitterX/SKILL.md +0 -52
- package/core/skills/tools/twitterX/scripts/abtars-tweet.js +0 -532
- package/core/skills/tools/twitterX/scripts/package.json +0 -1
- /package/bundle/{_registry.generated-6GO2PAMD.js.map → _registry.generated-4P6KTBTH.js.map} +0 -0
- /package/bundle/{agent-registry-SYUFNSVB.js.map → agent-registry-TM7EISOV.js.map} +0 -0
- /package/bundle/{browse-delivery-64GQIUHG.js.map → browse-delivery-VTLEAVYA.js.map} +0 -0
- /package/bundle/{browser-5H2Q7GEC.js.map → browser-JQGYFZAG.js.map} +0 -0
- /package/bundle/{commands-E3Y4QUNF.js.map → browser-M4KDLN75.js.map} +0 -0
- /package/bundle/{deploy-lib-import-ODLDL2DB.js.map → browser-REIXOJ6S.js.map} +0 -0
- /package/bundle/{install-RSLOS3Q5.js.map → browser-VG2GTEND.js.map} +0 -0
- /package/bundle/{chunk-KFENC7BM.js.map → chunk-4YOTNYAE.js.map} +0 -0
- /package/bundle/{chunk-PQ62LZNA.js.map → chunk-HEHD3GG5.js.map} +0 -0
- /package/bundle/{chunk-A5OJYQNU.js.map → chunk-HTGVYNPE.js.map} +0 -0
- /package/bundle/{chunk-ZVDVNSXK.js.map → chunk-J7CFCY4U.js.map} +0 -0
- /package/bundle/{message-pipeline-CFVO73CK.js.map → commands-J6JAZ7D2.js.map} +0 -0
- /package/bundle/{phase-transport-FEZ4SIJJ.js.map → deploy-lib-import-PJXUADPS.js.map} +0 -0
- /package/bundle/{discord-adapter-5DS7UTBE.js.map → discord-adapter-OCWSJTJU.js.map} +0 -0
- /package/bundle/{sleep-BPWX3FCN.js.map → install-6H3APAIJ.js.map} +0 -0
- /package/bundle/{soul-bundle-BRIUDEQ2.js.map → message-pipeline-TCPHRBAK.js.map} +0 -0
- /package/bundle/{soul-loader-GBXJ7EBH.js.map → phase-transport-ME2XDRR2.js.map} +0 -0
- /package/bundle/{subagent-runtime-MDBO6LHS.js.map → rollback-D7AYTC7Z.js.map} +0 -0
- /package/bundle/{task-store-KIBFZL5A.js.map → sleep-IOWYTSMK.js.map} +0 -0
- /package/bundle/{update-check-O5MS6B3L.js.map → soul-bundle-AH5S3MVI.js.map} +0 -0
- /package/bundle/{start-4IWBKLWO.js.map → start-CBVKNEAT.js.map} +0 -0
- /package/bundle/{telegram-adapter-MVA33IO2.js.map → telegram-adapter-QXHJQLLX.js.map} +0 -0
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/components/tasks/task-store.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * task-store.ts \u2014 JSON-backed task entry storage.\n * Replaces cron-db.ts (SQLite in memory.db) with ~/.abtars/state/tasks.json.\n * Same API surface for callers.\n */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync, renameSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport type { CronEntry } from \"../../cli/abtars-task.js\";\nimport { abtarsHome } from \"../../paths.js\";\nimport { logAndSwallow } from \"../log-and-swallow.js\";\n\nconst TAG = \"task_store\";\n\nconst storePath = (): string => join(abtarsHome(), \"state\", \"tasks.json\");\n\nfunction readAll(): CronEntry[] {\n const p = storePath();\n if (!existsSync(p)) return [];\n try { return JSON.parse(readFileSync(p, \"utf-8\")); }\n catch (err) { logAndSwallow(TAG, \"readAll tasks.json\", err); return []; }\n}\n\nfunction writeAll(entries: CronEntry[]): void {\n const p = storePath();\n mkdirSync(dirname(p), { recursive: true });\n const tmp = p + \".tmp\";\n writeFileSync(tmp, JSON.stringify(entries, null, 2), \"utf-8\");\n renameSync(tmp, p);\n}\n\nexport function readEntries(): CronEntry[] {\n return readAll();\n}\n\nexport function readEntry(id: string): CronEntry | null {\n return readAll().find(e => e.id === id) ?? null;\n}\n\nexport function writeEntry(e: CronEntry): void {\n const entries = readAll();\n const idx = entries.findIndex(x => x.id === e.id);\n if (idx >= 0) entries[idx] = e; else entries.push(e);\n writeAll(entries);\n}\n\nexport function removeEntry(id: string): boolean {\n const entries = readAll();\n const before = entries.length;\n const filtered = entries.filter(e => e.id !== id);\n if (filtered.length === before) return false;\n writeAll(filtered);\n return true;\n}\n\nexport function recordRun(entryId: string, exitCode?: number): void {\n const entries = readAll();\n const entry = entries.find(e => e.id === entryId);\n if (!entry) return;\n const history: { ts: number; exitCode?: number }[] = entry.history ?? [];\n history.push({ ts: Date.now(), ...(exitCode !== undefined ? { exitCode } : {}) });\n if (history.length > 10) history.splice(0, history.length - 10);\n entry.history = history;\n writeAll(entries);\n}\n\n/** No-op \u2014 kept for API compat with tests that called closeDb() on the old SQLite store. */\nexport function closeDb(): void { /* JSON store has no connection to close */ }\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;AASA;AACA;AAJA,SAAS,YAAY,cAAc,eAAe,WAAW,kBAAkB;AAC/E,SAAS,MAAM,eAAe;AAK9B,IAAM,MAAM;AAEZ,IAAM,YAAY,MAAc,KAAK,WAAW,GAAG,SAAS,YAAY;AAExE,SAAS,UAAuB;AAC9B,QAAM,IAAI,UAAU;AACpB,MAAI,CAAC,WAAW,CAAC,EAAG,QAAO,CAAC;AAC5B,MAAI;AAAE,WAAO,KAAK,MAAM,aAAa,GAAG,OAAO,CAAC;AAAA,EAAG,SAC5C,KAAK;AAAE,kBAAc,KAAK,sBAAsB,GAAG;AAAG,WAAO,CAAC;AAAA,EAAG;AAC1E;AAEA,SAAS,SAAS,SAA4B;AAC5C,QAAM,IAAI,UAAU;AACpB,YAAU,QAAQ,CAAC,GAAG,EAAE,WAAW,KAAK,CAAC;AACzC,QAAM,MAAM,IAAI;AAChB,gBAAc,KAAK,KAAK,UAAU,SAAS,MAAM,CAAC,GAAG,OAAO;AAC5D,aAAW,KAAK,CAAC;AACnB;AAEO,SAAS,cAA2B;AACzC,SAAO,QAAQ;AACjB;AAEO,SAAS,UAAU,IAA8B;AACtD,SAAO,QAAQ,EAAE,KAAK,OAAK,EAAE,OAAO,EAAE,KAAK;AAC7C;AAEO,SAAS,WAAW,GAAoB;AAC7C,QAAM,UAAU,QAAQ;AACxB,QAAM,MAAM,QAAQ,UAAU,OAAK,EAAE,OAAO,EAAE,EAAE;AAChD,MAAI,OAAO,EAAG,SAAQ,GAAG,IAAI;AAAA,MAAQ,SAAQ,KAAK,CAAC;AACnD,WAAS,OAAO;AAClB;AAEO,SAAS,YAAY,IAAqB;AAC/C,QAAM,UAAU,QAAQ;AACxB,QAAM,SAAS,QAAQ;AACvB,QAAM,WAAW,QAAQ,OAAO,OAAK,EAAE,OAAO,EAAE;AAChD,MAAI,SAAS,WAAW,OAAQ,QAAO;AACvC,WAAS,QAAQ;AACjB,SAAO;AACT;AAEO,SAAS,UAAU,SAAiB,UAAyB;AAClE,QAAM,UAAU,QAAQ;AACxB,QAAM,QAAQ,QAAQ,KAAK,OAAK,EAAE,OAAO,OAAO;AAChD,MAAI,CAAC,MAAO;AACZ,QAAM,UAA+C,MAAM,WAAW,CAAC;AACvE,UAAQ,KAAK,EAAE,IAAI,KAAK,IAAI,GAAG,GAAI,aAAa,SAAY,EAAE,SAAS,IAAI,CAAC,EAAG,CAAC;AAChF,MAAI,QAAQ,SAAS,GAAI,SAAQ,OAAO,GAAG,QAAQ,SAAS,EAAE;AAC9D,QAAM,UAAU;AAChB,WAAS,OAAO;AAClB;AAGO,SAAS,UAAgB;AAA8C;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/cli/commands/restart.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * abtars restart [--cold] \u2014 unified restart command.\n * Warm (default): writeRestartRequested \u2192 main.ts internal loop restarts.\n * Cold (--cold): kill process + spawn new launcher. Always, both modes.\n */\n\nimport { logAndSwallow } from \"../../components/log-and-swallow.js\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { execSync } from \"node:child_process\";\nimport { join } from \"node:path\";\n\nfunction abtarsHome(): string {\n return process.env[\"ABTARS_HOME\"] ?? join(process.env[\"HOME\"] ?? \"\", \".abtars\");\n}\n\nfunction readJsonField(file: string, field: string): unknown {\n try {\n const data = JSON.parse(readFileSync(file, \"utf-8\"));\n return data[field];\n } catch { return undefined; }\n}\n\nfunction pidAlive(pid: number): boolean {\n try { process.kill(pid, 0); return true; } catch { return false; }\n}\n\n/** #686: Kill any process holding port 3100 before spawning a new bridge. */\nfunction killPortHolder(port: number): void {\n try {\n const pid = execSync(`lsof -ti :${port}`, { encoding: \"utf-8\", timeout: 3000 }).trim();\n if (!pid) return;\n process.stdout.write(`\uD83D\uDED1 Killing stale process on port ${port} (pid ${pid})...\\n`);\n try { execSync(`kill ${pid}`, { timeout: 3000, stdio: \"pipe\" }); } catch { /* */ }\n for (let i = 0; i < 5; i++) {\n try { execSync(`lsof -ti :${port}`, { stdio: \"pipe\", timeout: 1000 }); } catch { return; }\n try { execSync(\"sleep 1\", { timeout: 2000, stdio: \"pipe\" }); } catch { /* */ }\n }\n try { execSync(`kill -9 ${pid}`, { timeout: 1000, stdio: \"pipe\" }); } catch { /* */ }\n } catch { /* no process on port \u2014 good */ }\n}\n\nasync function spawnLauncher(home: string, argv: string[]): Promise<number> {\n const { spawn } = await import(\"node:child_process\");\n const launcher = join(home, \"abtars.sh\");\n if (!existsSync(launcher)) {\n process.stderr.write(`Launcher not found: ${launcher}\\n`);\n return 1;\n }\n const child = spawn(launcher, argv, { detached: true, stdio: \"ignore\", cwd: home });\n child.unref();\n process.stdout.write(`\u267B\uFE0F Bridge starting (args: ${argv.join(\" \")})...\\n`);\n return 0;\n}\n\nasync function killBridge(pid: number): Promise<void> {\n // Verify PID is actually the bridge (Linux /proc, fallback: skip check)\n try {\n const cmdline = readFileSync(`/proc/${pid}/cmdline`, \"utf-8\");\n if (!cmdline.includes(\"abtars\") && !cmdline.includes(\"main.js\") && !cmdline.includes(\"bundle\")) {\n process.stdout.write(`\u26A0\uFE0F PID ${pid} is not abtars \u2014 stale PID file\\n`);\n return;\n }\n } catch (err) { logAndSwallow(\"restart\", \"op\", err); }\n\n try { process.kill(pid, \"SIGTERM\"); } catch { return; }\n process.stdout.write(`\uD83D\uDED1 Killing bridge (PID ${pid})...\\n`);\n\n for (let i = 0; i < 20; i++) {\n await new Promise(r => setTimeout(r, 500));\n if (!pidAlive(pid)) return;\n }\n try { process.kill(pid, \"SIGKILL\"); } catch (err) { logAndSwallow(\"restart\", \"op\", err); }\n await new Promise(r => setTimeout(r, 1000));\n}\n\nexport async function restart(opts: { cold?: boolean }): Promise<number> {\n const home = abtarsHome();\n const lockFile = join(home, \"bridge.lock\");\n const cold = opts.cold ?? false;\n\n const bridgePid = readJsonField(lockFile, \"pid\") as number | undefined;\n const bridgeAlive = bridgePid != null && bridgePid > 0 && pidAlive(bridgePid);\n\n if (cold) {\n // Under supervised-daemon, cold restart requires sudo \u2014 refuse and print instructions\n const manifestPath = join(home, \"manifest.json\");\n const installMode = readJsonField(manifestPath, \"installMode\") as string | undefined;\n if (!installMode) {\n process.stderr.write(\"\u274C installMode not set in manifest.json. Run 'abtars install' first.\\n\");\n return 1;\n }\n if (installMode === \"supervised-daemon\") {\n const platform = process.platform;\n if (platform === \"darwin\") {\n process.stderr.write(`Cold restart under supervised-daemon requires sudo:\\n sudo -k launchctl kickstart -k system/com.abtars.daemon\\n`);\n } else {\n process.stderr.write(`Cold restart under supervised-daemon requires sudo:\\n sudo -k systemctl restart abtars\\n`);\n }\n return 1;\n }\n\n // Run doctor before spawning\n const doctorPath = join(home, \"scripts\", \"doctor.sh\");\n if (existsSync(doctorPath)) {\n process.stdout.write(\"\uD83E\uDE7A Health check...\\n\");\n try {\n const { execSync } = await import(\"node:child_process\");\n execSync(`bash \"${doctorPath}\" --fix`, { stdio: \"inherit\", timeout: 30_000 });\n } catch (err) {\n process.stderr.write(`\u26A0\uFE0F doctor --fix failed: ${err instanceof Error ? err.message : String(err)}\\n`);\n }\n }\n\n if (bridgeAlive) await killBridge(bridgePid!);\n killPortHolder(3100);\n // Clear stale restart requests so the new bridge doesn't restart again (#731)\n const { updateBridgeLockField } = await import(\"../../components/transport/bridge-lock-transport.js\");\n updateBridgeLockField(\"restartRequested\", null);\n const argv: string[] = []; // #534: env is SSoT \u2014 no CLI args needed\n return spawnLauncher(home, argv);\n }\n\n // Warm restart\n if (bridgeAlive) {\n const { writeRestartRequested } = await import(\"../../components/transport/bridge-lock-transport.js\");\n writeRestartRequested(\"restart\");\n process.stdout.write(`\u267B\uFE0F Restart requested (PID ${bridgePid}) \u2014 bridge will restart within 30s\\n`);\n return 0;\n }\n\n process.stdout.write(\"Bridge not running. Use --cold to start.\\n\");\n return 1;\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["execSync"]
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/components/transport/direct-api-transport.ts", "../src/components/retry.ts", "../src/components/transport/conversation-session.ts", "../src/components/transport/sse-parser.ts", "../src/components/transport/transport-utils.ts"],
|
|
4
|
-
"sourcesContent": ["import { getEnv } from \"../env-schema.js\";\n/**\n * Direct API Transport \u2014 talks to any OpenAI-compatible endpoint.\n * Implements IKiroTransport with its own agent loop (send \u2192 stream \u2192 tools \u2192 loop).\n */\n\nimport { logInfo, logWarn, logDebug, logTrace } from \"../logger.js\";\nimport { logAndSwallow } from \"../log-and-swallow.js\";\nimport { withRetry, isFatal } from \"../retry.js\";\nimport { ConversationSession, type ToolCall, type ContentPart } from \"./conversation-session.js\";\nimport { parseSSEStream, type SSEToolCallDelta } from \"./sse-parser.js\";\nimport { getToolSchemas, executeToolCall } from \"./tool-registry.js\";\nimport { classifyError } from \"./model-health-registry.js\";\nimport { normalizeToolCalls, parseErrorStatus, parseRetryAfter, parseUsageLimitCooldown } from \"./transport-utils.js\";\nimport { recordUsage } from \"../usage-tracker.js\";\nimport type { FallbackPolicy } from \"./fallback-policy.js\";\nimport type { IKiroTransport } from \"./kiro-transport.js\";\n\nconst TAG = \"direct-api\";\n\n\nexport interface DirectApiConfig {\n endpoint: string;\n apiKey?: string;\n model: string;\n maxContext: number;\n maxOutput: number;\n maxTurns: number;\n apiFormat?: \"chat\" | \"responses\" | \"anthropic\";\n thinking?: { style: \"effort\"; default: string } | { style: \"extended\"; default: number };\n fallbacks?: Array<{ endpoint: string; apiKey?: string; model: string; maxContext?: number }>;\n}\n\nexport { normalizeToolCalls } from \"./transport-utils.js\";\n\nexport class DirectApiTransport implements IKiroTransport {\n private readonly config: DirectApiConfig;\n private readonly sessions = new Map<string, ConversationSession>();\n private readonly abortControllers = new Map<string, AbortController>();\n private systemPrompt = \"\";\n private _contextPercent = -1;\n private _lastAnswer = \"\";\n private _timeoutOverrideMs: number | null = null;\n private _toolCallsSucceeded = 0;\n private _intermediateText = \"\";\n private _promptStartedAt: number | null = null;\n private _lastActivityAt: number | null = null;\n private activeEndpoint: string;\n private activeApiKey?: string;\n private activeModel: string;\n private _lastPromptTokens = 0;\n private _activeSessionKey = \"\";\n private _activeUserId = \"master\";\n\n /** Currently active model (may differ from config if on fallback). */\n get currentModel(): string { return this.activeModel; }\n\n onIntermediateResponse?: (text: string) => void;\n onToolCallStart?: (toolName: string) => void;\n onSegmentBreak?: (text: string) => void;\n /** Sandbox policy for tool access control (#681). Set by pipeline before prompt. */\n sandboxPolicy?: import(\"../tool-sandbox.js\").SandboxPolicy;\n /** Called when fallback model is selected \u2014 send notification before response. */\n onFallback?: (model: string, ctxPercent: number, reason?: string) => void;\n /** Cooperative pause check \u2014 if returns true, agent loop breaks between tool calls. */\n isPaused?: () => boolean;\n /** Returns a pending instruction from parent, if any. Consumed once. */\n getPendingInstruction?: () => string | undefined;\n\n /** Context orchestrator \u2014 when set, messages are built from DB instead of in-memory session. */\n contextOrchestrator?: import(\"abmind\").ContextOrchestrator;\n\n private policy: FallbackPolicy | null;\n private emergencyOverride: { endpoint: string; apiKey?: string; model: string; maxContext: number } | null = null;\n\n /** Activate emergency (hailMary) mode \u2014 next prompts bypass the fallback policy. */\n setEmergencyMode(override: { endpoint: string; apiKey?: string; model: string; maxContext: number } | null): void {\n if (!override && !this.emergencyOverride) return; // no-op if already off\n this.emergencyOverride = override;\n if (override) logWarn(TAG, `\uD83D\uDEA8 EMERGENCY MODE: using ${override.model} (paid) \u2014 bypassing fallback chain`);\n else { this.activeModel = this.config.model; logInfo(TAG, `Emergency mode cleared \u2014 restored ${this.config.model}`); }\n }\n\n /** True if emergency (hailMary) mode is active. */\n get isEmergencyMode(): boolean { return this.emergencyOverride !== null; }\n\n constructor(config: DirectApiConfig, policy?: FallbackPolicy) {\n this.config = config;\n this.activeEndpoint = config.endpoint;\n this.activeApiKey = config.apiKey;\n this.activeModel = config.model;\n this.policy = policy ?? null;\n }\n\n async initialize(): Promise<void> {\n const count = this.policy ? this.policy.candidates.length : (this.config.fallbacks?.length ?? 0) + 1;\n const fb = count > 1 ? ` (+${count - 1} fallback${count > 2 ? \"s\" : \"\"})` : \"\";\n logInfo(TAG, `\uD83D\uDD0C Direct API transport (${this.config.endpoint}, model: ${this.config.model}${fb})`);\n }\n\n setSystemPrompt(prompt: string): void {\n this.systemPrompt = prompt;\n }\n\n async sendPrompt(sessionKey: string, message: string, image?: { mime: string; base64: string }, userId?: string): Promise<string> {\n const session = this.getOrCreateSession(sessionKey);\n this._activeSessionKey = sessionKey;\n this._activeUserId = userId || \"master\";\n\n // If context orchestrator is active, rebuild messages from DB\n if (this.contextOrchestrator) {\n try {\n const ctx = await this.contextOrchestrator.getContext(sessionKey, this.config.maxContext);\n // Replace session messages with DB-backed context + system prompt\n session.messages = [\n { role: \"system\" as const, content: this.systemPrompt },\n ...ctx.messages.map(m => ({ role: m.role as \"user\" | \"assistant\" | \"tool\", content: m.content })),\n ];\n if (ctx.compacted) logDebug(TAG, `Context compacted for ${sessionKey}`);\n } catch (err) {\n logWarn(TAG, `Context engine failed, falling back to in-memory: ${err}`);\n }\n }\n\n session.addUser(message, image);\n\n this._lastAnswer = \"\";\n this._toolCallsSucceeded = 0;\n this._intermediateText = \"\";\n this._promptStartedAt = Date.now();\n this._lastActivityAt = Date.now();\n\n const ac = new AbortController();\n this.abortControllers.set(sessionKey, ac);\n\n try {\n if (this.emergencyOverride) return await this.sendEmergency(session, ac.signal);\n if (!this.policy) throw new Error(\"DirectApiTransport requires a FallbackPolicy\");\n return await this.sendWithPolicy(session, ac.signal);\n } finally {\n // AfterPrompt hook \u2014 observe-only, fire-and-forget\n const durationMs = Date.now() - (this._promptStartedAt ?? Date.now());\n import(\"../hooks/hook-system.js\").then(({ hasHooks, fire }) => {\n if (!hasHooks(\"AfterPrompt\")) return;\n fire(\"AfterPrompt\", {\n event: \"AfterPrompt\", timestamp: new Date().toISOString(),\n sessionKey, platform: \"\", userId: \"\",\n model: this.activeModel, durationMs,\n inputTokens: this._lastPromptTokens || null,\n outputTokens: null, // not tracked per-prompt in DirectApi\n }).catch(err => logAndSwallow(TAG, \"fire AfterPrompt\", err));\n }).catch(err => logAndSwallow(TAG, \"import hook-system\", err));\n this._promptStartedAt = null;\n this.abortControllers.delete(sessionKey);\n }\n }\n\n private async sendEmergency(session: ConversationSession, signal: AbortSignal): Promise<string> {\n const em = this.emergencyOverride!;\n this.activeEndpoint = em.endpoint;\n this.activeApiKey = em.apiKey;\n this.activeModel = em.model;\n this._lastActivityAt = Date.now();\n logWarn(TAG, `\uD83D\uDEA8 Emergency mode: using ${em.model}`);\n const result = await this.agentLoop(session, signal);\n this._lastAnswer = result;\n return result;\n }\n\n private async sendWithPolicy(session: ConversationSession, signal: AbortSignal): Promise<string> {\n const policy = this.policy!;\n const failedAttempts: Array<{ model: string; kind: string; bucket: number }> = [];\n const isPrimary = (m: string): boolean => m === this.config.model;\n\n // Try each candidate via policy\n let candidate = policy.selectModel(this._lastPromptTokens);\n while (candidate) {\n this.activeEndpoint = candidate.endpoint;\n this.activeApiKey = candidate.apiKey;\n this.activeModel = candidate.model;\n this._lastActivityAt = Date.now();\n logDebug(TAG, `Trying model: ${candidate.model}`);\n\n if (!isPrimary(candidate.model) && this.onFallback) {\n const ctxPct = candidate.maxContext > 0 ? Math.round((this._lastPromptTokens / candidate.maxContext) * 100) : -1;\n const lastFail = failedAttempts[failedAttempts.length - 1];\n this.onFallback(candidate.model, ctxPct, lastFail?.kind);\n }\n\n try {\n const result = await this.agentLoop(session, signal);\n this._lastAnswer = result;\n if (!result || !result.trim()) {\n policy.recordError(candidate, \"empty\");\n } else {\n policy.recordSuccess(candidate);\n }\n return result;\n } catch (err) {\n if (signal.aborted) { session.rollbackToLastUser(); throw err; }\n const errMsg = err instanceof Error ? err.message : String(err);\n // Capability miss \u2014 strip image and retry text-only (#670)\n if (errMsg.includes(\"does not support image input\") || errMsg.includes(\"No endpoints found that support image\")) {\n session.rollbackToLastUser();\n // Strip image parts from session, retry same model text-only\n for (const m of session.messages) {\n if (Array.isArray(m.content)) {\n const textParts = (m.content as Array<{ type: string; text?: string }>).filter(p => p.type === \"text\");\n m.content = textParts.map(p => p.text ?? \"\").join(\"\\n\") || \"User sent an image (not supported by this model).\";\n }\n }\n logWarn(TAG, `${candidate.model} doesn't support images \u2014 retrying text-only`);\n if (this.onIntermediateResponse) this.onIntermediateResponse(\"\u26A0\uFE0F Model doesn't support images via this provider. Sending text-only.\\n\");\n try {\n const result = await this.agentLoop(session, signal);\n this._lastAnswer = result;\n policy.recordSuccess(candidate);\n return result;\n } catch (retryErr) {\n const retryStatus = this.parseErrorStatus(retryErr);\n const retryMsg = retryErr instanceof Error ? retryErr.message : String(retryErr);\n policy.recordError(candidate, classifyError(retryStatus, retryMsg));\n session.rollbackToLastUser();\n throw retryErr;\n }\n }\n const status = this.parseErrorStatus(err);\n const kind = classifyError(status, errMsg);\n const retryAfterMs = this.parseRetryAfter(err) ?? parseUsageLimitCooldown(errMsg);\n policy.recordError(candidate, kind, retryAfterMs);\n const bucket = policy.registry.getBucketLevel(candidate.model, candidate.endpoint);\n failedAttempts.push({ model: candidate.model, kind, bucket });\n session.rollbackToLastUser();\n logWarn(TAG, `${candidate.model} failed (${kind}, bucket: ${bucket}%${retryAfterMs ? `, retry-after: ${Math.round(retryAfterMs / 1000)}s` : \"\"}): ${errMsg.slice(0, 100)}`);\n }\n\n candidate = policy.selectModel(this._lastPromptTokens);\n }\n\n // Compaction fallback: pick smallest surviving candidate, truncate to fit\n const surviving = policy.survivingCandidates()\n .filter(c => c.maxContext > 0)\n .sort((a, b) => a.maxContext - b.maxContext);\n const smallest = surviving[0];\n\n if (smallest && this._lastPromptTokens > smallest.maxContext * 0.95) {\n logWarn(TAG, `Compacting session to fit ${smallest.model} (${smallest.maxContext} tokens)`);\n session.truncateToFit(smallest.maxContext);\n this.activeEndpoint = smallest.endpoint;\n this.activeApiKey = smallest.apiKey;\n this.activeModel = smallest.model;\n if (this.onFallback) {\n this.onFallback(`${smallest.model} (compacted)`, Math.round((session.estimateTokens() / smallest.maxContext) * 100));\n }\n try {\n const result = await this.agentLoop(session, signal);\n this._lastAnswer = result;\n if (!result || !result.trim()) policy.recordError(smallest, \"empty\");\n else policy.recordSuccess(smallest);\n return result;\n } catch (err) {\n const status = this.parseErrorStatus(err);\n const errMsg2 = err instanceof Error ? err.message : String(err);\n policy.recordError(smallest, classifyError(status, errMsg2));\n failedAttempts.push({ model: smallest.model, kind: classifyError(status, errMsg2), bucket: policy.registry.getBucketLevel(smallest.model, smallest.endpoint) });\n }\n }\n\n const summary = failedAttempts.map(a => ` - ${a.model}: ${a.kind} (bucket: ${a.bucket}%)`).join(\"\\n\");\n if (policy.lastDecision) {\n logDebug(TAG, `Last decision: ${JSON.stringify(policy.lastDecision)}`);\n }\n throw new Error(`All models exhausted:\\n${summary}`);\n }\n\n private async agentLoop(session: ConversationSession, signal: AbortSignal): Promise<string> {\n let zeroTokenRetries = 0;\n const loopStart = Date.now();\n for (let turn = 0; turn < this.config.maxTurns; turn++) {\n if (signal.aborted) throw new Error(\"Aborted\");\n if (this.isPaused?.()) return \"\u23F8 Session paused. Use `/session resume` to continue.\";\n\n const pendingInstruction = this.getPendingInstruction?.();\n if (pendingInstruction) session.addUser(pendingInstruction);\n\n const { content, toolCalls, usage } = await this.streamCompletion(session, signal);\n\n if (usage) {\n session.updateTokens(usage.prompt_tokens);\n this._contextPercent = session.contextPercent;\n this._lastPromptTokens = usage.prompt_tokens;\n this.contextOrchestrator?.onApiResponse(this._activeSessionKey, usage.prompt_tokens, this.config.maxContext);\n logTrace(TAG, `${this.activeModel} \u2014 ${usage.prompt_tokens}\u2192${usage.completion_tokens ?? 0} tokens, ${Date.now() - (this._lastActivityAt ?? Date.now())}ms`);\n recordUsage(this.activeModel, usage.prompt_tokens, usage.completion_tokens ?? 0);\n }\n\n if (toolCalls.length > 0) {\n session.addAssistant(content, toolCalls);\n logDebug(TAG, `Tool calls: ${toolCalls.map(tc => tc.function.name).join(\", \")}`);\n logTrace(TAG, `Tool args: ${toolCalls.map(tc => {\n // #621: redact abmind_store args based on classification\n if ((tc.function.name === \"abmind_store\" || tc.function.name === \"memory_store\") && /class(?:ification)?[\":\\s]+[23]/.test(tc.function.arguments)) {\n return `${tc.function.name}([REDACTED])`;\n }\n return `${tc.function.name}(${tc.function.arguments})`;\n }).join(\", \")}`);\n\n // Deliver pre-tool text immediately (segment break)\n if (content?.trim()) {\n this.onSegmentBreak?.(content.trim());\n }\n\n for (const tc of toolCalls) {\n if (signal.aborted) throw new Error(\"Aborted\");\n this._lastActivityAt = Date.now();\n this.onToolCallStart?.(tc.function.name ?? \"tool\");\n\n let args: Record<string, string>;\n try { args = JSON.parse(tc.function.arguments); } catch (err) { logAndSwallow(TAG, \"JSON.parse tool args\", err); args = {}; }\n\n const result = await executeToolCall(tc.function.name, args, { userId: this._activeUserId, signal, sandboxPolicy: this.sandboxPolicy });\n session.addToolResult(tc.id, tc.function.name, result);\n\n // #621: scrub secret values from conversation history after store\n if ((tc.function.name === \"abmind_store\" || tc.function.name === \"memory_store\") && parseInt(args.classification ?? args.class ?? \"1\", 10) >= 2) {\n const secretValue = args.content ?? args.value ?? args.translated;\n if (secretValue && secretValue.length > 4) {\n session.scrubFromHistory(secretValue);\n }\n }\n\n try { if (!JSON.parse(result).error) this._toolCallsSucceeded++; } catch (err) { logAndSwallow(TAG, \"JSON.parse tool result\", err); this._toolCallsSucceeded++; }\n }\n continue;\n }\n\n // No tool calls \u2014 final response\n const answer = content ?? \"\";\n // Budget-aware backoff on 0\u21920 token response (#732)\n if (!answer && usage && usage.prompt_tokens === 0) {\n const elapsed = Date.now() - loopStart;\n const remaining = (this._timeoutOverrideMs ?? 60_000) - elapsed;\n const retryDelay = 5000 + (zeroTokenRetries * 3000);\n if (remaining > retryDelay + 5000) {\n zeroTokenRetries++;\n logWarn(TAG, `API returned 0 tokens \u2014 retry #${zeroTokenRetries} after ${retryDelay / 1000}s (${Math.round(remaining / 1000)}s left)`);\n await new Promise(r => setTimeout(r, retryDelay));\n continue;\n }\n }\n session.addAssistant(answer);\n return answer;\n }\n\n logWarn(TAG, `Max turns (${this.config.maxTurns}) reached`);\n const last = session.messages.at(-1)?.content;\n return (typeof last === \"string\" ? last : null) ?? \"(max turns reached)\";\n }\n\n private parseErrorStatus(err: unknown): number { return parseErrorStatus(err); }\n\n /** Extract Retry-After from error (seconds or date). Returns ms or undefined. */\n private parseRetryAfter(err: unknown): number | undefined { return parseRetryAfter(err); }\n\n private async streamCompletion(\n session: ConversationSession,\n signal: AbortSignal,\n ): Promise<{ content: string | null; toolCalls: ToolCall[]; usage: { prompt_tokens: number; completion_tokens: number } | null }> {\n // Compose pipeline signal (user /stop) with per-request timeout\n const timeoutCtrl = new AbortController();\n const timer = setTimeout(() => timeoutCtrl.abort(new Error(\"model API timeout\")), this._timeoutOverrideMs ?? getEnv().modelApiTimeoutMs);\n const composed = AbortSignal.any([signal, timeoutCtrl.signal]);\n\n try {\n // Responses API format (#465, streaming #472)\n if (this.config.apiFormat === \"responses\") {\n const { toResponsesRequest } = await import(\"./responses-adapter.js\");\n const { parseResponsesSSE } = await import(\"./sse-parser-responses.js\");\n const msgs = session.messages.map(m => ({ role: m.role, content: m.content ?? \"\" as string | ContentPart[] }));\n const reqBody = { ...toResponsesRequest(this.activeModel, msgs, getToolSchemas(this.sandboxPolicy), this.config.maxOutput), stream: true };\n const hdrs: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (this.activeApiKey) hdrs[\"Authorization\"] = `Bearer ${this.activeApiKey}`;\n const res = await fetch(`${this.activeEndpoint}/responses`, {\n method: \"POST\", headers: hdrs, body: JSON.stringify(reqBody), signal: composed,\n });\n if (!res.ok) { const text = await res.text().catch(err => { logAndSwallow(TAG, \"read error body\", err); return \"\"; }); throw new Error(`API error ${res.status}: ${text.slice(0, 500)}`); }\n\n let content = \"\";\n let usage: { prompt_tokens: number; completion_tokens: number } | null = null;\n const toolCallAcc = new Map<string, { id: string; name: string; arguments: string }>();\n for await (const event of parseResponsesSSE(res, composed)) {\n this._lastActivityAt = Date.now();\n if (event.type === \"chunk\") { content += event.content; this._intermediateText += event.content; this.onIntermediateResponse?.(event.content); }\n else if (event.type === \"tool_call_delta\") { this.accumulateToolCall(toolCallAcc, event); }\n else if (event.type === \"done\") { usage = event.usage; }\n }\n clearTimeout(timer);\n const toolCalls = this.finalizeToolCalls(toolCallAcc);\n return { content: content || null, toolCalls, usage };\n }\n\n // Anthropic Messages API format (#467, streaming #472)\n if (this.config.apiFormat === \"anthropic\") {\n const { toAnthropicRequest, buildAnthropicHeaders } = await import(\"./anthropic-adapter.js\");\n const { parseAnthropicSSE } = await import(\"./sse-parser-anthropic.js\");\n const msgs = session.messages.map(m => ({ role: m.role, content: m.content ?? \"\" as string | ContentPart[], tool_call_id: m.tool_call_id }));\n const reqBody = { ...toAnthropicRequest(this.activeModel, msgs, this.config.maxOutput, getToolSchemas(this.sandboxPolicy)), stream: true };\n const hdrs = buildAnthropicHeaders(this.activeApiKey ?? \"\");\n const res = await fetch(`${this.activeEndpoint}/messages`, {\n method: \"POST\", headers: hdrs, body: JSON.stringify(reqBody), signal: composed,\n });\n if (!res.ok) { const text = await res.text().catch(err => { logAndSwallow(TAG, \"read error body\", err); return \"\"; }); throw new Error(`API error ${res.status}: ${text.slice(0, 500)}`); }\n\n let content = \"\";\n let usage: { prompt_tokens: number; completion_tokens: number } | null = null;\n const toolCallAcc = new Map<string, { id: string; name: string; arguments: string }>();\n for await (const event of parseAnthropicSSE(res, composed)) {\n this._lastActivityAt = Date.now();\n if (event.type === \"chunk\") { content += event.content; this._intermediateText += event.content; this.onIntermediateResponse?.(event.content); }\n else if (event.type === \"tool_call_delta\") { this.accumulateToolCall(toolCallAcc, event); }\n else if (event.type === \"done\") { usage = event.usage; }\n }\n clearTimeout(timer);\n const toolCalls = this.finalizeToolCalls(toolCallAcc);\n return { content: content || null, toolCalls, usage };\n }\n\n const body: Record<string, unknown> = {\n model: this.activeModel,\n messages: session.messages,\n tools: getToolSchemas(this.sandboxPolicy),\n max_tokens: this.config.maxOutput,\n stream: true,\n stream_options: { include_usage: true },\n };\n\n // #466: inject thinking/reasoning parameters\n if (this.config.thinking) {\n if (this.config.thinking.style === \"effort\") {\n body.reasoning_effort = this.config.thinking.default;\n } else if (this.config.thinking.style === \"extended\") {\n body.thinking = { type: \"enabled\", budget_tokens: this.config.thinking.default };\n }\n }\n\n const headers: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (this.activeApiKey) headers[\"Authorization\"] = `Bearer ${this.activeApiKey}`;\n\n const response = await withRetry(\n async () => {\n const res = await fetch(`${this.activeEndpoint}/chat/completions`, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n signal: composed,\n });\n if (!res.ok) {\n const text = await res.text().catch(err => { logAndSwallow(TAG, \"read error body\", err); return \"\"; });\n throw new Error(`API error ${res.status}: ${text.slice(0, 500)}`);\n }\n return res;\n },\n {\n attempts: 3, minDelayMs: 3000,\n isRecoverable: (err) => {\n const msg = err instanceof Error ? err.message : String(err);\n if (/model API timeout/.test(msg)) return false;\n // Don't retry 429/401/402/403 \u2014 let the bucket loop handle model switching\n if (/API error (429|401|402|403)/.test(msg)) return false;\n return !isFatal(err);\n },\n },\n );\n\n let content = \"\";\n const toolCallAccumulator = new Map<string, { id: string; name: string; arguments: string }>();\n let usage: { prompt_tokens: number; completion_tokens: number } | null = null;\n\n for await (const event of parseSSEStream(response, composed)) {\n this._lastActivityAt = Date.now();\n\n switch (event.type) {\n case \"chunk\":\n content += event.content;\n this._intermediateText += event.content;\n this.onIntermediateResponse?.(event.content);\n break;\n\n case \"tool_call_delta\":\n this.accumulateToolCall(toolCallAccumulator, event);\n break;\n\n case \"done\":\n usage = event.usage;\n break;\n }\n }\n\n const toolCalls = this.finalizeToolCalls(toolCallAccumulator);\n\n return { content: content || null, toolCalls, usage };\n } finally {\n clearTimeout(timer);\n }\n }\n\n /** Accumulate streaming tool call deltas by ID (Ollama-safe: tracks by ID not index). */\n private accumulateToolCall(\n acc: Map<string, { id: string; name: string; arguments: string }>,\n delta: SSEToolCallDelta,\n ): void {\n // Use ID if available, fall back to index-based key\n const key = delta.id ?? `idx:${delta.index}`;\n const existing = acc.get(key);\n if (existing) {\n if (delta.arguments) existing.arguments += delta.arguments;\n } else {\n acc.set(key, { id: delta.id ?? `call_${acc.size}`, name: delta.name ?? \"\", arguments: delta.arguments ?? \"\" });\n }\n }\n\n /**\n * Convert accumulated tool call map \u2192 normalized ToolCall array.\n * Handles model fragmentation: some models (nemotron, mistral-free) split a\n * single tool call across multiple SSE entries with different indices/IDs.\n * Pattern: [name=\"execute_bash\" args=\"{}\"], [name=\"\" args=\"\"], [name=\"\" args='{\"command\":\"...\"}']\n * Fix: merge adjacent unnamed entries' args into the preceding named entry.\n */\n private finalizeToolCalls(acc: Map<string, { id: string; name: string; arguments: string }>): ToolCall[] {\n const raw: ToolCall[] = [...acc.values()].map(tc => ({\n id: tc.id, type: \"function\" as const, function: { name: tc.name, arguments: tc.arguments },\n }));\n return normalizeToolCalls(raw);\n }\n\n private getOrCreateSession(sessionKey: string): ConversationSession {\n let session = this.sessions.get(sessionKey);\n if (!session) {\n session = new ConversationSession(this.systemPrompt, this.config.maxContext);\n this.sessions.set(sessionKey, session);\n }\n return session;\n }\n\n async resetSession(sessionKey: string): Promise<void> {\n const session = this.sessions.get(sessionKey);\n if (session) session.reset(this.systemPrompt);\n else this.sessions.delete(sessionKey);\n logInfo(TAG, `Session ${sessionKey} reset`);\n }\n\n async sendInterrupt(): Promise<void> {\n for (const ac of this.abortControllers.values()) ac.abort();\n }\n\n destroy(): void {\n for (const ac of this.abortControllers.values()) ac.abort();\n this.sessions.clear();\n this.abortControllers.clear();\n }\n\n get isReady(): boolean { return true; }\n get contextPercent(): number { return this._contextPercent; }\n get answerOnly(): string { return this._lastAnswer; }\n get toolCallsSucceeded(): number { return this._toolCallsSucceeded; }\n\n /** Hot-swap the active model. Takes effect on next API call. */\n setModel(model: string): void {\n this.activeModel = model;\n (this.config as { model: string }).model = model;\n logInfo(TAG, `Model switched (user): ${model}`);\n }\n\n /** Hot-swap provider+model+policy. Rejects if prompt is in flight. */\n switchProvider(opts: { endpoint: string; apiKey?: string; model: string; maxContext: number; policy: FallbackPolicy }): void {\n if (this._promptStartedAt !== null) {\n throw new Error(\"Cannot switch provider while a prompt is in progress \u2014 try after the response\");\n }\n this.activeEndpoint = opts.endpoint;\n this.activeApiKey = opts.apiKey;\n this.activeModel = opts.model;\n (this.config as { model: string; maxContext: number }).model = opts.model;\n (this.config as { maxContext: number }).maxContext = opts.maxContext;\n this.policy = opts.policy;\n logInfo(TAG, `Provider switched: ${opts.model} @ ${opts.endpoint} (maxCtx=${opts.maxContext})`);\n }\n\n /** Get current active model name. */\n getModel(): string { return this.activeModel; }\n get intermediateDeliveredText(): string { return this._intermediateText; }\n get transportCommands(): string[] { return []; }\n\n // Watchdog support\n get promptStartedAt(): number | null { return this._promptStartedAt; }\n setTimeoutOverride(ms: number | null): void { this._timeoutOverrideMs = ms; }\n\n get lastActivityAt(): number | null { return this._lastActivityAt; }\n\n private readonly _stuckTimeout = getEnv().watchdogSilentSec * 1000;\n\n async healthCheck(): Promise<void> {\n if (!this._promptStartedAt) return;\n const idle = Date.now() - (this._lastActivityAt ?? this._promptStartedAt);\n if (idle > this._stuckTimeout) {\n logWarn(TAG, `[transport-health] Prompt stuck (${Math.round(idle / 1000)}s idle) \u2014 aborting`);\n await this.sendInterrupt();\n }\n }\n}\n", "/**\n * Generic retry utility with escalating backoff, jitter, and fatal error detection.\n */\n\nexport interface RetryPolicy {\n /** Max attempts (including first). Default: 3 */\n attempts?: number;\n /** Initial delay in ms. Default: 300 */\n minDelayMs?: number;\n /** Max delay cap in ms. Default: 30_000 */\n maxDelayMs?: number;\n /** Jitter factor 0-1 (0.1 = \u00B110%). Default: 0.1 */\n jitter?: number;\n /** Return false to stop retrying (fatal error). Default: always retry. */\n isRecoverable?: (err: unknown) => boolean;\n /** Extract delay hint from error (e.g. rate limit header). */\n getDelayHint?: (err: unknown) => number | undefined;\n /** Called before each retry. */\n onAttempt?: (info: { attempt: number; maxAttempts: number; err: unknown; delayMs: number }) => void;\n}\n\nconst DEFAULTS = { attempts: 3, minDelayMs: 300, maxDelayMs: 30_000, jitter: 0.1 };\n\n/** Known-fatal error patterns \u2014 don't retry these. */\nexport const FATAL_PATTERNS = [\n /auth.*fail|invalid.*key|unauthorized/i,\n /model.*not found|not supported/i,\n /account.*suspended|quota.*exceeded/i,\n /bot was blocked/i,\n];\n\n/** Check if an error matches a known-fatal pattern. */\nexport function isFatal(err: unknown): boolean {\n const msg = err instanceof Error ? err.message : String(err);\n return FATAL_PATTERNS.some(p => p.test(msg));\n}\n\n/** Execute `fn` with retry, escalating backoff, and jitter. */\nexport async function withRetry<T>(fn: () => Promise<T>, policy?: RetryPolicy): Promise<T> {\n const attempts = Math.max(1, policy?.attempts ?? DEFAULTS.attempts);\n const minDelay = Math.max(0, policy?.minDelayMs ?? DEFAULTS.minDelayMs);\n const maxDelay = Math.max(minDelay, policy?.maxDelayMs ?? DEFAULTS.maxDelayMs);\n const jitter = Math.max(0, Math.min(1, policy?.jitter ?? DEFAULTS.jitter));\n const isRecoverable = policy?.isRecoverable ?? ((err: unknown) => !isFatal(err));\n\n let lastErr: unknown;\n for (let i = 0; i < attempts; i++) {\n try {\n return await fn();\n } catch (err) {\n lastErr = err;\n if (i === attempts - 1) break;\n if (!isRecoverable(err)) break;\n\n const hint = policy?.getDelayHint?.(err);\n const baseDelay = hint ?? Math.min(maxDelay, minDelay * Math.pow(2, i));\n const offset = jitter > 0 ? (Math.random() * 2 - 1) * jitter * baseDelay : 0;\n const delayMs = Math.max(0, Math.round(baseDelay + offset));\n\n policy?.onAttempt?.({ attempt: i + 1, maxAttempts: attempts, err, delayMs });\n await new Promise(r => setTimeout(r, delayMs));\n }\n }\n throw lastErr;\n}\n", "/**\n * Per-session conversation state for DirectApiTransport.\n * Manages message history and token tracking.\n */\n\nexport type ContentPart =\n | { type: \"text\"; text: string }\n | { type: \"image_url\"; image_url: { url: string } };\n\nexport type ChatMessage = {\n role: \"system\" | \"user\" | \"assistant\" | \"tool\";\n content: string | ContentPart[] | null;\n tool_calls?: ToolCall[];\n tool_call_id?: string;\n name?: string;\n};\n\nexport type ToolCall = {\n id: string;\n type: \"function\";\n function: { name: string; arguments: string };\n};\n\nexport class ConversationSession {\n messages: ChatMessage[] = [];\n totalPromptTokens = 0;\n private readonly maxContext: number;\n\n constructor(systemPrompt: string, maxContext: number) {\n this.maxContext = maxContext;\n this.messages.push({ role: \"system\", content: systemPrompt });\n }\n\n addUser(content: string, image?: { mime: string; base64: string }): void {\n if (image) {\n this.messages.push({ role: \"user\", content: [\n { type: \"image_url\", image_url: { url: `data:${image.mime};base64,${image.base64}` } },\n { type: \"text\", text: content },\n ]});\n } else {\n this.messages.push({ role: \"user\", content });\n }\n }\n\n addAssistant(content: string | null, toolCalls?: ToolCall[]): void {\n const msg: ChatMessage = { role: \"assistant\", content };\n if (toolCalls?.length) msg.tool_calls = toolCalls;\n this.messages.push(msg);\n }\n\n addToolResult(toolCallId: string, name: string, content: string): void {\n this.messages.push({ role: \"tool\", content, tool_call_id: toolCallId, name });\n }\n\n updateTokens(promptTokens: number): void {\n this.totalPromptTokens = promptTokens;\n }\n\n get contextPercent(): number {\n if (this.maxContext <= 0) return 0;\n return Math.round((this.totalPromptTokens / this.maxContext) * 100);\n }\n\n reset(systemPrompt: string): void {\n this.messages = [{ role: \"system\", content: systemPrompt }];\n this.totalPromptTokens = 0;\n }\n\n /** Roll back to last user message \u2014 remove everything after it for clean fallback. */\n rollbackToLastUser(): void {\n for (let i = this.messages.length - 1; i >= 0; i--) {\n if (this.messages[i]!.role === \"user\") {\n this.messages.splice(i + 1);\n return;\n }\n }\n }\n\n /** Rough token estimate: ~4 chars per token. */\n estimateTokens(): number {\n return Math.round(this.messages.reduce((sum, m) => sum + (typeof m.content === \"string\" ? m.content.length : 0), 0) / 4);\n }\n\n /** Drop oldest non-system messages until estimated tokens fit within limit. */\n truncateToFit(maxTokens: number): void {\n while (this.messages.length > 2 && this.estimateTokens() > maxTokens * 0.85) {\n this.messages.splice(1, 1); // remove oldest after system prompt\n }\n this.totalPromptTokens = this.estimateTokens();\n }\n\n /** #621: Replace a literal secret value with [REDACTED] across all messages. */\n scrubFromHistory(value: string): void {\n for (const msg of this.messages) {\n if (typeof msg.content === \"string\" && msg.content.includes(value)) {\n msg.content = msg.content.replaceAll(value, \"[REDACTED]\");\n }\n }\n }\n}\n", "/**\n * SSE stream parser for OpenAI-compatible chat completions.\n * Parses Server-Sent Events into typed events.\n */\n\nimport { logAndSwallow } from \"../log-and-swallow.js\";\n\nconst TAG = \"sse_parser\";\n\nexport type SSEChunkEvent = { type: \"chunk\"; content: string };\nexport type SSEToolCallDelta = { type: \"tool_call_delta\"; index: number; id?: string; name?: string; arguments?: string };\nexport type SSEDoneEvent = { type: \"done\"; usage: { prompt_tokens: number; completion_tokens: number } | null };\nexport type SSEEvent = SSEChunkEvent | SSEToolCallDelta | SSEDoneEvent;\n\nconst STALE_TIMEOUT_MS = 90_000;\n\nexport async function* parseSSEStream(\n response: Response,\n signal: AbortSignal,\n): AsyncGenerator<SSEEvent> {\n const reader = response.body?.getReader();\n if (!reader) throw new Error(\"Response body is not readable\");\n\n const decoder = new TextDecoder();\n let buffer = \"\";\n let lastChunkAt = Date.now();\n let staleTimer: ReturnType<typeof setTimeout> | null = null;\n\n const resetStaleTimer = (): void => {\n lastChunkAt = Date.now();\n if (staleTimer) clearTimeout(staleTimer);\n staleTimer = setTimeout(() => {\n if (Date.now() - lastChunkAt >= STALE_TIMEOUT_MS) {\n reader.cancel(\"stale stream\").catch(err => logAndSwallow(TAG, \"reader.cancel stale\", err));\n }\n }, STALE_TIMEOUT_MS);\n };\n\n try {\n resetStaleTimer();\n while (true) {\n if (signal.aborted) break;\n const { done, value } = await reader.read();\n if (done) break;\n\n resetStaleTimer();\n buffer += decoder.decode(value, { stream: true });\n\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\";\n\n for (const line of lines) {\n if (!line.startsWith(\"data: \")) continue;\n const data = line.slice(6).trim();\n if (data === \"[DONE]\") return;\n\n let parsed: Record<string, unknown>;\n try { parsed = JSON.parse(data); } catch (err) { logAndSwallow(TAG, \"JSON.parse SSE chunk\", err); continue; }\n\n // Usage in final chunk (stream_options: { include_usage: true })\n if (parsed[\"usage\"]) {\n const u = parsed[\"usage\"] as Record<string, number>;\n yield { type: \"done\", usage: { prompt_tokens: u[\"prompt_tokens\"] ?? 0, completion_tokens: u[\"completion_tokens\"] ?? 0 } };\n }\n\n const choices = parsed[\"choices\"] as Array<Record<string, unknown>> | undefined;\n if (!choices?.length) continue;\n const delta = choices[0]![\"delta\"] as Record<string, unknown> | undefined;\n if (!delta) continue;\n\n // Text content\n if (typeof delta[\"content\"] === \"string\" && delta[\"content\"]) {\n yield { type: \"chunk\", content: delta[\"content\"] as string };\n }\n\n // Tool calls\n const toolCalls = delta[\"tool_calls\"] as Array<Record<string, unknown>> | undefined;\n if (toolCalls) {\n for (const tc of toolCalls) {\n const fn = tc[\"function\"] as Record<string, string> | undefined;\n yield {\n type: \"tool_call_delta\",\n index: (tc[\"index\"] as number) ?? 0,\n id: tc[\"id\"] as string | undefined,\n name: fn?.[\"name\"],\n arguments: fn?.[\"arguments\"],\n };\n }\n }\n }\n }\n } finally {\n if (staleTimer) clearTimeout(staleTimer);\n reader.releaseLock();\n }\n}\n", "/**\n * transport-utils.ts \u2014 Pure utility functions for the transport layer.\n * No I/O, no state, no side effects (except logging).\n */\n\nimport { logWarn } from \"../logger.js\";\nimport type { ToolCall } from \"./conversation-session.js\";\n\nconst TAG = \"direct-api\";\n\n/**\n * Normalize tool calls from models that fragment a single call across multiple entries.\n * Pattern: [name=\"execute_bash\" args=\"{}\"], [name=\"\" args=\"\"], [name=\"\" args='{\"command\":\"...\"}']\n * Fix: merge next unnamed entry's args into preceding named entry. Drop remaining unnamed.\n */\nexport function normalizeToolCalls(raw: ToolCall[]): ToolCall[] {\n if (raw.length <= 1) return raw;\n\n const result: ToolCall[] = [];\n for (let i = 0; i < raw.length; i++) {\n const tc = raw[i]!;\n if (tc.function.name) {\n if (!tc.function.arguments || tc.function.arguments === \"{}\") {\n // Look ahead for the next unnamed entry with real args\n for (let j = i + 1; j < raw.length; j++) {\n const next = raw[j]!;\n if (next.function.name) break; // hit another named entry, stop\n if (next.function.arguments && next.function.arguments !== \"{}\") {\n tc.function.arguments = next.function.arguments;\n i = j; // skip all entries up to and including the merged one\n break;\n }\n }\n }\n result.push(tc);\n }\n }\n\n if (result.length !== raw.length) {\n logWarn(TAG, `Normalized ${raw.length} tool call entries \u2192 ${result.length} (model fragmentation): ${raw.map(tc => `${tc.function.name || \"(unnamed)\"}(${tc.function.arguments.slice(0, 60)})`).join(\", \")}`);\n }\n return result;\n}\n\n/** Extract HTTP status code from error message. Returns 0 if not found. */\nexport function parseErrorStatus(err: unknown): number {\n const msg = err instanceof Error ? err.message : String(err);\n const m = /API error (\\d+)/.exec(msg);\n return m ? parseInt(m[1]!, 10) : 0;\n}\n\n/** Extract Retry-After from error (seconds or date). Returns ms or undefined. */\nexport function parseRetryAfter(err: unknown): number | undefined {\n const msg = err instanceof Error ? err.message : String(err);\n const jsonMatch = /retry[_-]after[\"\\s:]+(\\d+(?:\\.\\d+)?)/i.exec(msg);\n if (jsonMatch) return Math.ceil(parseFloat(jsonMatch[1]!) * 1000);\n const resetMatch = /x-ratelimit-reset[\"\\s:]+(\\d{10,13})/i.exec(msg);\n if (resetMatch) {\n const ts = parseInt(resetMatch[1]!, 10);\n const ms = ts < 1e12 ? ts * 1000 : ts;\n const delta = ms - Date.now();\n return delta > 0 ? delta : undefined;\n }\n return undefined;\n}\n\n/** Detect \"day/week/month limit\" in error message and return cooldown ms. */\nexport function parseUsageLimitCooldown(message: string): number | undefined {\n const lower = message.toLowerCase();\n if (!lower.includes(\"limit\")) return undefined;\n if (lower.includes(\"day\")) {\n const tomorrow = new Date();\n tomorrow.setDate(tomorrow.getDate() + 1);\n tomorrow.setHours(0, 0, 0, 0);\n return tomorrow.getTime() - Date.now();\n }\n if (lower.includes(\"week\")) {\n const now = new Date();\n const daysUntilMonday = (8 - now.getDay()) % 7 || 7;\n const monday = new Date(now);\n monday.setDate(monday.getDate() + daysUntilMonday);\n monday.setHours(0, 0, 0, 0);\n return monday.getTime() - Date.now();\n }\n if (lower.includes(\"month\")) {\n const now = new Date();\n const nextMonth = new Date(now.getFullYear(), now.getMonth() + 1, 1);\n return nextMonth.getTime() - Date.now();\n }\n return undefined;\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAMA;AACA;;;ACcA,IAAM,WAAW,EAAE,UAAU,GAAG,YAAY,KAAK,YAAY,KAAQ,QAAQ,IAAI;AAG1E,IAAM,iBAAiB;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAGO,SAAS,QAAQ,KAAuB;AAC7C,QAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,SAAO,eAAe,KAAK,OAAK,EAAE,KAAK,GAAG,CAAC;AAC7C;AAGA,eAAsB,UAAa,IAAsB,QAAkC;AACzF,QAAM,WAAW,KAAK,IAAI,GAAG,QAAQ,YAAY,SAAS,QAAQ;AAClE,QAAM,WAAW,KAAK,IAAI,GAAG,QAAQ,cAAc,SAAS,UAAU;AACtE,QAAM,WAAW,KAAK,IAAI,UAAU,QAAQ,cAAc,SAAS,UAAU;AAC7E,QAAM,SAAS,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,QAAQ,UAAU,SAAS,MAAM,CAAC;AACzE,QAAM,gBAAgB,QAAQ,kBAAkB,CAAC,QAAiB,CAAC,QAAQ,GAAG;AAE9E,MAAI;AACJ,WAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AACjC,QAAI;AACF,aAAO,MAAM,GAAG;AAAA,IAClB,SAAS,KAAK;AACZ,gBAAU;AACV,UAAI,MAAM,WAAW,EAAG;AACxB,UAAI,CAAC,cAAc,GAAG,EAAG;AAEzB,YAAM,OAAO,QAAQ,eAAe,GAAG;AACvC,YAAM,YAAY,QAAQ,KAAK,IAAI,UAAU,WAAW,KAAK,IAAI,GAAG,CAAC,CAAC;AACtE,YAAM,SAAS,SAAS,KAAK,KAAK,OAAO,IAAI,IAAI,KAAK,SAAS,YAAY;AAC3E,YAAM,UAAU,KAAK,IAAI,GAAG,KAAK,MAAM,YAAY,MAAM,CAAC;AAE1D,cAAQ,YAAY,EAAE,SAAS,IAAI,GAAG,aAAa,UAAU,KAAK,QAAQ,CAAC;AAC3E,YAAM,IAAI,QAAQ,OAAK,WAAW,GAAG,OAAO,CAAC;AAAA,IAC/C;AAAA,EACF;AACA,QAAM;AACR;;;ACzCO,IAAM,sBAAN,MAA0B;AAAA,EAC/B,WAA0B,CAAC;AAAA,EAC3B,oBAAoB;AAAA,EACH;AAAA,EAEjB,YAAY,cAAsB,YAAoB;AACpD,SAAK,aAAa;AAClB,SAAK,SAAS,KAAK,EAAE,MAAM,UAAU,SAAS,aAAa,CAAC;AAAA,EAC9D;AAAA,EAEA,QAAQ,SAAiB,OAAgD;AACvE,QAAI,OAAO;AACT,WAAK,SAAS,KAAK,EAAE,MAAM,QAAQ,SAAS;AAAA,QAC1C,EAAE,MAAM,aAAa,WAAW,EAAE,KAAK,QAAQ,MAAM,IAAI,WAAW,MAAM,MAAM,GAAG,EAAE;AAAA,QACrF,EAAE,MAAM,QAAQ,MAAM,QAAQ;AAAA,MAChC,EAAC,CAAC;AAAA,IACJ,OAAO;AACL,WAAK,SAAS,KAAK,EAAE,MAAM,QAAQ,QAAQ,CAAC;AAAA,IAC9C;AAAA,EACF;AAAA,EAEA,aAAa,SAAwB,WAA8B;AACjE,UAAM,MAAmB,EAAE,MAAM,aAAa,QAAQ;AACtD,QAAI,WAAW,OAAQ,KAAI,aAAa;AACxC,SAAK,SAAS,KAAK,GAAG;AAAA,EACxB;AAAA,EAEA,cAAc,YAAoB,MAAc,SAAuB;AACrE,SAAK,SAAS,KAAK,EAAE,MAAM,QAAQ,SAAS,cAAc,YAAY,KAAK,CAAC;AAAA,EAC9E;AAAA,EAEA,aAAa,cAA4B;AACvC,SAAK,oBAAoB;AAAA,EAC3B;AAAA,EAEA,IAAI,iBAAyB;AAC3B,QAAI,KAAK,cAAc,EAAG,QAAO;AACjC,WAAO,KAAK,MAAO,KAAK,oBAAoB,KAAK,aAAc,GAAG;AAAA,EACpE;AAAA,EAEA,MAAM,cAA4B;AAChC,SAAK,WAAW,CAAC,EAAE,MAAM,UAAU,SAAS,aAAa,CAAC;AAC1D,SAAK,oBAAoB;AAAA,EAC3B;AAAA;AAAA,EAGA,qBAA2B;AACzB,aAAS,IAAI,KAAK,SAAS,SAAS,GAAG,KAAK,GAAG,KAAK;AAClD,UAAI,KAAK,SAAS,CAAC,EAAG,SAAS,QAAQ;AACrC,aAAK,SAAS,OAAO,IAAI,CAAC;AAC1B;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,iBAAyB;AACvB,WAAO,KAAK,MAAM,KAAK,SAAS,OAAO,CAAC,KAAK,MAAM,OAAO,OAAO,EAAE,YAAY,WAAW,EAAE,QAAQ,SAAS,IAAI,CAAC,IAAI,CAAC;AAAA,EACzH;AAAA;AAAA,EAGA,cAAc,WAAyB;AACrC,WAAO,KAAK,SAAS,SAAS,KAAK,KAAK,eAAe,IAAI,YAAY,MAAM;AAC3E,WAAK,SAAS,OAAO,GAAG,CAAC;AAAA,IAC3B;AACA,SAAK,oBAAoB,KAAK,eAAe;AAAA,EAC/C;AAAA;AAAA,EAGA,iBAAiB,OAAqB;AACpC,eAAW,OAAO,KAAK,UAAU;AAC/B,UAAI,OAAO,IAAI,YAAY,YAAY,IAAI,QAAQ,SAAS,KAAK,GAAG;AAClE,YAAI,UAAU,IAAI,QAAQ,WAAW,OAAO,YAAY;AAAA,MAC1D;AAAA,IACF;AAAA,EACF;AACF;;;AC9FA;AAEA,IAAM,MAAM;AAOZ,IAAM,mBAAmB;AAEzB,gBAAuB,eACrB,UACA,QAC0B;AAC1B,QAAM,SAAS,SAAS,MAAM,UAAU;AACxC,MAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,+BAA+B;AAE5D,QAAM,UAAU,IAAI,YAAY;AAChC,MAAI,SAAS;AACb,MAAI,cAAc,KAAK,IAAI;AAC3B,MAAI,aAAmD;AAEvD,QAAM,kBAAkB,MAAY;AAClC,kBAAc,KAAK,IAAI;AACvB,QAAI,WAAY,cAAa,UAAU;AACvC,iBAAa,WAAW,MAAM;AAC5B,UAAI,KAAK,IAAI,IAAI,eAAe,kBAAkB;AAChD,eAAO,OAAO,cAAc,EAAE,MAAM,SAAO,cAAc,KAAK,uBAAuB,GAAG,CAAC;AAAA,MAC3F;AAAA,IACF,GAAG,gBAAgB;AAAA,EACrB;AAEA,MAAI;AACF,oBAAgB;AAChB,WAAO,MAAM;AACX,UAAI,OAAO,QAAS;AACpB,YAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,UAAI,KAAM;AAEV,sBAAgB;AAChB,gBAAU,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC;AAEhD,YAAM,QAAQ,OAAO,MAAM,IAAI;AAC/B,eAAS,MAAM,IAAI,KAAK;AAExB,iBAAW,QAAQ,OAAO;AACxB,YAAI,CAAC,KAAK,WAAW,QAAQ,EAAG;AAChC,cAAM,OAAO,KAAK,MAAM,CAAC,EAAE,KAAK;AAChC,YAAI,SAAS,SAAU;AAEvB,YAAI;AACJ,YAAI;AAAE,mBAAS,KAAK,MAAM,IAAI;AAAA,QAAG,SAAS,KAAK;AAAE,wBAAc,KAAK,wBAAwB,GAAG;AAAG;AAAA,QAAU;AAG5G,YAAI,OAAO,OAAO,GAAG;AACnB,gBAAM,IAAI,OAAO,OAAO;AACxB,gBAAM,EAAE,MAAM,QAAQ,OAAO,EAAE,eAAe,EAAE,eAAe,KAAK,GAAG,mBAAmB,EAAE,mBAAmB,KAAK,EAAE,EAAE;AAAA,QAC1H;AAEA,cAAM,UAAU,OAAO,SAAS;AAChC,YAAI,CAAC,SAAS,OAAQ;AACtB,cAAM,QAAQ,QAAQ,CAAC,EAAG,OAAO;AACjC,YAAI,CAAC,MAAO;AAGZ,YAAI,OAAO,MAAM,SAAS,MAAM,YAAY,MAAM,SAAS,GAAG;AAC5D,gBAAM,EAAE,MAAM,SAAS,SAAS,MAAM,SAAS,EAAY;AAAA,QAC7D;AAGA,cAAM,YAAY,MAAM,YAAY;AACpC,YAAI,WAAW;AACb,qBAAW,MAAM,WAAW;AAC1B,kBAAM,KAAK,GAAG,UAAU;AACxB,kBAAM;AAAA,cACJ,MAAM;AAAA,cACN,OAAQ,GAAG,OAAO,KAAgB;AAAA,cAClC,IAAI,GAAG,IAAI;AAAA,cACX,MAAM,KAAK,MAAM;AAAA,cACjB,WAAW,KAAK,WAAW;AAAA,YAC7B;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF,UAAE;AACA,QAAI,WAAY,cAAa,UAAU;AACvC,WAAO,YAAY;AAAA,EACrB;AACF;;;AC1FA;AAGA,IAAMA,OAAM;AAOL,SAAS,mBAAmB,KAA6B;AAC9D,MAAI,IAAI,UAAU,EAAG,QAAO;AAE5B,QAAM,SAAqB,CAAC;AAC5B,WAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,UAAM,KAAK,IAAI,CAAC;AAChB,QAAI,GAAG,SAAS,MAAM;AACpB,UAAI,CAAC,GAAG,SAAS,aAAa,GAAG,SAAS,cAAc,MAAM;AAE5D,iBAAS,IAAI,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACvC,gBAAM,OAAO,IAAI,CAAC;AAClB,cAAI,KAAK,SAAS,KAAM;AACxB,cAAI,KAAK,SAAS,aAAa,KAAK,SAAS,cAAc,MAAM;AAC/D,eAAG,SAAS,YAAY,KAAK,SAAS;AACtC,gBAAI;AACJ;AAAA,UACF;AAAA,QACF;AAAA,MACF;AACA,aAAO,KAAK,EAAE;AAAA,IAChB;AAAA,EACF;AAEA,MAAI,OAAO,WAAW,IAAI,QAAQ;AAChC,YAAQA,MAAK,cAAc,IAAI,MAAM,6BAAwB,OAAO,MAAM,2BAA2B,IAAI,IAAI,QAAM,GAAG,GAAG,SAAS,QAAQ,WAAW,IAAI,GAAG,SAAS,UAAU,MAAM,GAAG,EAAE,CAAC,GAAG,EAAE,KAAK,IAAI,CAAC,EAAE;AAAA,EAC9M;AACA,SAAO;AACT;AAGO,SAAS,iBAAiB,KAAsB;AACrD,QAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,QAAM,IAAI,kBAAkB,KAAK,GAAG;AACpC,SAAO,IAAI,SAAS,EAAE,CAAC,GAAI,EAAE,IAAI;AACnC;AAGO,SAAS,gBAAgB,KAAkC;AAChE,QAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,QAAM,YAAY,wCAAwC,KAAK,GAAG;AAClE,MAAI,UAAW,QAAO,KAAK,KAAK,WAAW,UAAU,CAAC,CAAE,IAAI,GAAI;AAChE,QAAM,aAAa,uCAAuC,KAAK,GAAG;AAClE,MAAI,YAAY;AACd,UAAM,KAAK,SAAS,WAAW,CAAC,GAAI,EAAE;AACtC,UAAM,KAAK,KAAK,OAAO,KAAK,MAAO;AACnC,UAAM,QAAQ,KAAK,KAAK,IAAI;AAC5B,WAAO,QAAQ,IAAI,QAAQ;AAAA,EAC7B;AACA,SAAO;AACT;AAGO,SAAS,wBAAwB,SAAqC;AAC3E,QAAM,QAAQ,QAAQ,YAAY;AAClC,MAAI,CAAC,MAAM,SAAS,OAAO,EAAG,QAAO;AACrC,MAAI,MAAM,SAAS,KAAK,GAAG;AACzB,UAAM,WAAW,oBAAI,KAAK;AAC1B,aAAS,QAAQ,SAAS,QAAQ,IAAI,CAAC;AACvC,aAAS,SAAS,GAAG,GAAG,GAAG,CAAC;AAC5B,WAAO,SAAS,QAAQ,IAAI,KAAK,IAAI;AAAA,EACvC;AACA,MAAI,MAAM,SAAS,MAAM,GAAG;AAC1B,UAAM,MAAM,oBAAI,KAAK;AACrB,UAAM,mBAAmB,IAAI,IAAI,OAAO,KAAK,KAAK;AAClD,UAAM,SAAS,IAAI,KAAK,GAAG;AAC3B,WAAO,QAAQ,OAAO,QAAQ,IAAI,eAAe;AACjD,WAAO,SAAS,GAAG,GAAG,GAAG,CAAC;AAC1B,WAAO,OAAO,QAAQ,IAAI,KAAK,IAAI;AAAA,EACrC;AACA,MAAI,MAAM,SAAS,OAAO,GAAG;AAC3B,UAAM,MAAM,oBAAI,KAAK;AACrB,UAAM,YAAY,IAAI,KAAK,IAAI,YAAY,GAAG,IAAI,SAAS,IAAI,GAAG,CAAC;AACnE,WAAO,UAAU,QAAQ,IAAI,KAAK,IAAI;AAAA,EACxC;AACA,SAAO;AACT;;;AJxEA,IAAMC,OAAM;AAiBL,IAAM,qBAAN,MAAmD;AAAA,EACvC;AAAA,EACA,WAAW,oBAAI,IAAiC;AAAA,EAChD,mBAAmB,oBAAI,IAA6B;AAAA,EAC7D,eAAe;AAAA,EACf,kBAAkB;AAAA,EAClB,cAAc;AAAA,EACd,qBAAoC;AAAA,EACpC,sBAAsB;AAAA,EACtB,oBAAoB;AAAA,EACpB,mBAAkC;AAAA,EAClC,kBAAiC;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA,oBAAoB;AAAA,EACpB,oBAAoB;AAAA,EACpB,gBAAgB;AAAA;AAAA,EAGxB,IAAI,eAAuB;AAAE,WAAO,KAAK;AAAA,EAAa;AAAA,EAEtD;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAGA;AAAA,EAEQ;AAAA,EACA,oBAAqG;AAAA;AAAA,EAG7G,iBAAiB,UAAiG;AAChH,QAAI,CAAC,YAAY,CAAC,KAAK,kBAAmB;AAC1C,SAAK,oBAAoB;AACzB,QAAI,SAAU,SAAQA,MAAK,mCAA4B,SAAS,KAAK,yCAAoC;AAAA,SACpG;AAAE,WAAK,cAAc,KAAK,OAAO;AAAO,cAAQA,MAAK,0CAAqC,KAAK,OAAO,KAAK,EAAE;AAAA,IAAG;AAAA,EACvH;AAAA;AAAA,EAGA,IAAI,kBAA2B;AAAE,WAAO,KAAK,sBAAsB;AAAA,EAAM;AAAA,EAEzE,YAAY,QAAyB,QAAyB;AAC5D,SAAK,SAAS;AACd,SAAK,iBAAiB,OAAO;AAC7B,SAAK,eAAe,OAAO;AAC3B,SAAK,cAAc,OAAO;AAC1B,SAAK,SAAS,UAAU;AAAA,EAC1B;AAAA,EAEA,MAAM,aAA4B;AAChC,UAAM,QAAQ,KAAK,SAAS,KAAK,OAAO,WAAW,UAAU,KAAK,OAAO,WAAW,UAAU,KAAK;AACnG,UAAM,KAAK,QAAQ,IAAI,MAAM,QAAQ,CAAC,YAAY,QAAQ,IAAI,MAAM,EAAE,MAAM;AAC5E,YAAQA,MAAK,mCAA4B,KAAK,OAAO,QAAQ,YAAY,KAAK,OAAO,KAAK,GAAG,EAAE,GAAG;AAAA,EACpG;AAAA,EAEA,gBAAgB,QAAsB;AACpC,SAAK,eAAe;AAAA,EACtB;AAAA,EAEA,MAAM,WAAW,YAAoB,SAAiB,OAA0C,QAAkC;AAChI,UAAM,UAAU,KAAK,mBAAmB,UAAU;AAClD,SAAK,oBAAoB;AACzB,SAAK,gBAAgB,UAAU;AAG/B,QAAI,KAAK,qBAAqB;AAC5B,UAAI;AACF,cAAM,MAAM,MAAM,KAAK,oBAAoB,WAAW,YAAY,KAAK,OAAO,UAAU;AAExF,gBAAQ,WAAW;AAAA,UACjB,EAAE,MAAM,UAAmB,SAAS,KAAK,aAAa;AAAA,UACtD,GAAG,IAAI,SAAS,IAAI,QAAM,EAAE,MAAM,EAAE,MAAuC,SAAS,EAAE,QAAQ,EAAE;AAAA,QAClG;AACA,YAAI,IAAI,UAAW,UAASA,MAAK,yBAAyB,UAAU,EAAE;AAAA,MACxE,SAAS,KAAK;AACZ,gBAAQA,MAAK,qDAAqD,GAAG,EAAE;AAAA,MACzE;AAAA,IACF;AAEA,YAAQ,QAAQ,SAAS,KAAK;AAE9B,SAAK,cAAc;AACnB,SAAK,sBAAsB;AAC3B,SAAK,oBAAoB;AACzB,SAAK,mBAAmB,KAAK,IAAI;AACjC,SAAK,kBAAkB,KAAK,IAAI;AAEhC,UAAM,KAAK,IAAI,gBAAgB;AAC/B,SAAK,iBAAiB,IAAI,YAAY,EAAE;AAExC,QAAI;AACF,UAAI,KAAK,kBAAmB,QAAO,MAAM,KAAK,cAAc,SAAS,GAAG,MAAM;AAC9E,UAAI,CAAC,KAAK,OAAQ,OAAM,IAAI,MAAM,8CAA8C;AAChF,aAAO,MAAM,KAAK,eAAe,SAAS,GAAG,MAAM;AAAA,IACrD,UAAE;AAEA,YAAM,aAAa,KAAK,IAAI,KAAK,KAAK,oBAAoB,KAAK,IAAI;AACnE,aAAO,2BAAyB,EAAE,KAAK,CAAC,EAAE,UAAU,KAAK,MAAM;AAC7D,YAAI,CAAC,SAAS,aAAa,EAAG;AAC9B,aAAK,eAAe;AAAA,UAClB,OAAO;AAAA,UAAe,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,UACxD;AAAA,UAAY,UAAU;AAAA,UAAI,QAAQ;AAAA,UAClC,OAAO,KAAK;AAAA,UAAa;AAAA,UACzB,aAAa,KAAK,qBAAqB;AAAA,UACvC,cAAc;AAAA;AAAA,QAChB,CAAC,EAAE,MAAM,SAAO,cAAcA,MAAK,oBAAoB,GAAG,CAAC;AAAA,MAC7D,CAAC,EAAE,MAAM,SAAO,cAAcA,MAAK,sBAAsB,GAAG,CAAC;AAC7D,WAAK,mBAAmB;AACxB,WAAK,iBAAiB,OAAO,UAAU;AAAA,IACzC;AAAA,EACF;AAAA,EAEA,MAAc,cAAc,SAA8B,QAAsC;AAC9F,UAAM,KAAK,KAAK;AAChB,SAAK,iBAAiB,GAAG;AACzB,SAAK,eAAe,GAAG;AACvB,SAAK,cAAc,GAAG;AACtB,SAAK,kBAAkB,KAAK,IAAI;AAChC,YAAQA,MAAK,mCAA4B,GAAG,KAAK,EAAE;AACnD,UAAM,SAAS,MAAM,KAAK,UAAU,SAAS,MAAM;AACnD,SAAK,cAAc;AACnB,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,eAAe,SAA8B,QAAsC;AAC/F,UAAM,SAAS,KAAK;AACpB,UAAM,iBAAyE,CAAC;AAChF,UAAM,YAAY,CAAC,MAAuB,MAAM,KAAK,OAAO;AAG5D,QAAI,YAAY,OAAO,YAAY,KAAK,iBAAiB;AACzD,WAAO,WAAW;AAChB,WAAK,iBAAiB,UAAU;AAChC,WAAK,eAAe,UAAU;AAC9B,WAAK,cAAc,UAAU;AAC7B,WAAK,kBAAkB,KAAK,IAAI;AAChC,eAASA,MAAK,iBAAiB,UAAU,KAAK,EAAE;AAEhD,UAAI,CAAC,UAAU,UAAU,KAAK,KAAK,KAAK,YAAY;AAClD,cAAM,SAAS,UAAU,aAAa,IAAI,KAAK,MAAO,KAAK,oBAAoB,UAAU,aAAc,GAAG,IAAI;AAC9G,cAAM,WAAW,eAAe,eAAe,SAAS,CAAC;AACzD,aAAK,WAAW,UAAU,OAAO,QAAQ,UAAU,IAAI;AAAA,MACzD;AAEA,UAAI;AACF,cAAM,SAAS,MAAM,KAAK,UAAU,SAAS,MAAM;AACnD,aAAK,cAAc;AACnB,YAAI,CAAC,UAAU,CAAC,OAAO,KAAK,GAAG;AAC7B,iBAAO,YAAY,WAAW,OAAO;AAAA,QACvC,OAAO;AACL,iBAAO,cAAc,SAAS;AAAA,QAChC;AACA,eAAO;AAAA,MACT,SAAS,KAAK;AACZ,YAAI,OAAO,SAAS;AAAE,kBAAQ,mBAAmB;AAAG,gBAAM;AAAA,QAAK;AAC/D,cAAM,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAE9D,YAAI,OAAO,SAAS,8BAA8B,KAAK,OAAO,SAAS,uCAAuC,GAAG;AAC/G,kBAAQ,mBAAmB;AAE3B,qBAAW,KAAK,QAAQ,UAAU;AAChC,gBAAI,MAAM,QAAQ,EAAE,OAAO,GAAG;AAC5B,oBAAM,YAAa,EAAE,QAAmD,OAAO,OAAK,EAAE,SAAS,MAAM;AACrG,gBAAE,UAAU,UAAU,IAAI,OAAK,EAAE,QAAQ,EAAE,EAAE,KAAK,IAAI,KAAK;AAAA,YAC7D;AAAA,UACF;AACA,kBAAQA,MAAK,GAAG,UAAU,KAAK,mDAA8C;AAC7E,cAAI,KAAK,uBAAwB,MAAK,uBAAuB,mFAAyE;AACtI,cAAI;AACF,kBAAM,SAAS,MAAM,KAAK,UAAU,SAAS,MAAM;AACnD,iBAAK,cAAc;AACnB,mBAAO,cAAc,SAAS;AAC9B,mBAAO;AAAA,UACT,SAAS,UAAU;AACjB,kBAAM,cAAc,KAAK,iBAAiB,QAAQ;AAClD,kBAAM,WAAW,oBAAoB,QAAQ,SAAS,UAAU,OAAO,QAAQ;AAC/E,mBAAO,YAAY,WAAW,cAAc,aAAa,QAAQ,CAAC;AAClE,oBAAQ,mBAAmB;AAC3B,kBAAM;AAAA,UACR;AAAA,QACF;AACA,cAAM,SAAS,KAAK,iBAAiB,GAAG;AACxC,cAAM,OAAO,cAAc,QAAQ,MAAM;AACzC,cAAM,eAAe,KAAK,gBAAgB,GAAG,KAAK,wBAAwB,MAAM;AAChF,eAAO,YAAY,WAAW,MAAM,YAAY;AAChD,cAAM,SAAS,OAAO,SAAS,eAAe,UAAU,OAAO,UAAU,QAAQ;AACjF,uBAAe,KAAK,EAAE,OAAO,UAAU,OAAO,MAAM,OAAO,CAAC;AAC5D,gBAAQ,mBAAmB;AAC3B,gBAAQA,MAAK,GAAG,UAAU,KAAK,YAAY,IAAI,aAAa,MAAM,IAAI,eAAe,kBAAkB,KAAK,MAAM,eAAe,GAAI,CAAC,MAAM,EAAE,MAAM,OAAO,MAAM,GAAG,GAAG,CAAC,EAAE;AAAA,MAC5K;AAEA,kBAAY,OAAO,YAAY,KAAK,iBAAiB;AAAA,IACvD;AAGA,UAAM,YAAY,OAAO,oBAAoB,EAC1C,OAAO,OAAK,EAAE,aAAa,CAAC,EAC5B,KAAK,CAAC,GAAG,MAAM,EAAE,aAAa,EAAE,UAAU;AAC7C,UAAM,WAAW,UAAU,CAAC;AAE5B,QAAI,YAAY,KAAK,oBAAoB,SAAS,aAAa,MAAM;AACnE,cAAQA,MAAK,6BAA6B,SAAS,KAAK,KAAK,SAAS,UAAU,UAAU;AAC1F,cAAQ,cAAc,SAAS,UAAU;AACzC,WAAK,iBAAiB,SAAS;AAC/B,WAAK,eAAe,SAAS;AAC7B,WAAK,cAAc,SAAS;AAC5B,UAAI,KAAK,YAAY;AACnB,aAAK,WAAW,GAAG,SAAS,KAAK,gBAAgB,KAAK,MAAO,QAAQ,eAAe,IAAI,SAAS,aAAc,GAAG,CAAC;AAAA,MACrH;AACA,UAAI;AACF,cAAM,SAAS,MAAM,KAAK,UAAU,SAAS,MAAM;AACnD,aAAK,cAAc;AACnB,YAAI,CAAC,UAAU,CAAC,OAAO,KAAK,EAAG,QAAO,YAAY,UAAU,OAAO;AAAA,YAC9D,QAAO,cAAc,QAAQ;AAClC,eAAO;AAAA,MACT,SAAS,KAAK;AACZ,cAAM,SAAS,KAAK,iBAAiB,GAAG;AACxC,cAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,eAAO,YAAY,UAAU,cAAc,QAAQ,OAAO,CAAC;AAC3D,uBAAe,KAAK,EAAE,OAAO,SAAS,OAAO,MAAM,cAAc,QAAQ,OAAO,GAAG,QAAQ,OAAO,SAAS,eAAe,SAAS,OAAO,SAAS,QAAQ,EAAE,CAAC;AAAA,MAChK;AAAA,IACF;AAEA,UAAM,UAAU,eAAe,IAAI,OAAK,OAAO,EAAE,KAAK,KAAK,EAAE,IAAI,aAAa,EAAE,MAAM,IAAI,EAAE,KAAK,IAAI;AACrG,QAAI,OAAO,cAAc;AACvB,eAASA,MAAK,kBAAkB,KAAK,UAAU,OAAO,YAAY,CAAC,EAAE;AAAA,IACvE;AACA,UAAM,IAAI,MAAM;AAAA,EAA0B,OAAO,EAAE;AAAA,EACrD;AAAA,EAEA,MAAc,UAAU,SAA8B,QAAsC;AAC1F,QAAI,mBAAmB;AACvB,UAAM,YAAY,KAAK,IAAI;AAC3B,aAAS,OAAO,GAAG,OAAO,KAAK,OAAO,UAAU,QAAQ;AACtD,UAAI,OAAO,QAAS,OAAM,IAAI,MAAM,SAAS;AAC7C,UAAI,KAAK,WAAW,EAAG,QAAO;AAE9B,YAAM,qBAAqB,KAAK,wBAAwB;AACxD,UAAI,mBAAoB,SAAQ,QAAQ,kBAAkB;AAE1D,YAAM,EAAE,SAAS,WAAW,MAAM,IAAI,MAAM,KAAK,iBAAiB,SAAS,MAAM;AAEjF,UAAI,OAAO;AACT,gBAAQ,aAAa,MAAM,aAAa;AACxC,aAAK,kBAAkB,QAAQ;AAC/B,aAAK,oBAAoB,MAAM;AAC/B,aAAK,qBAAqB,cAAc,KAAK,mBAAmB,MAAM,eAAe,KAAK,OAAO,UAAU;AAC3G,iBAASA,MAAK,GAAG,KAAK,WAAW,WAAM,MAAM,aAAa,SAAI,MAAM,qBAAqB,CAAC,YAAY,KAAK,IAAI,KAAK,KAAK,mBAAmB,KAAK,IAAI,EAAE,IAAI;AAC3J,oBAAY,KAAK,aAAa,MAAM,eAAe,MAAM,qBAAqB,CAAC;AAAA,MACjF;AAEA,UAAI,UAAU,SAAS,GAAG;AACxB,gBAAQ,aAAa,SAAS,SAAS;AACvC,iBAASA,MAAK,eAAe,UAAU,IAAI,QAAM,GAAG,SAAS,IAAI,EAAE,KAAK,IAAI,CAAC,EAAE;AAC/E,iBAASA,MAAK,cAAc,UAAU,IAAI,QAAM;AAE9C,eAAK,GAAG,SAAS,SAAS,kBAAkB,GAAG,SAAS,SAAS,mBAAmB,iCAAiC,KAAK,GAAG,SAAS,SAAS,GAAG;AAChJ,mBAAO,GAAG,GAAG,SAAS,IAAI;AAAA,UAC5B;AACA,iBAAO,GAAG,GAAG,SAAS,IAAI,IAAI,GAAG,SAAS,SAAS;AAAA,QACrD,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE;AAGf,YAAI,SAAS,KAAK,GAAG;AACnB,eAAK,iBAAiB,QAAQ,KAAK,CAAC;AAAA,QACtC;AAEA,mBAAW,MAAM,WAAW;AAC1B,cAAI,OAAO,QAAS,OAAM,IAAI,MAAM,SAAS;AAC7C,eAAK,kBAAkB,KAAK,IAAI;AAChC,eAAK,kBAAkB,GAAG,SAAS,QAAQ,MAAM;AAEjD,cAAI;AACJ,cAAI;AAAE,mBAAO,KAAK,MAAM,GAAG,SAAS,SAAS;AAAA,UAAG,SAAS,KAAK;AAAE,0BAAcA,MAAK,wBAAwB,GAAG;AAAG,mBAAO,CAAC;AAAA,UAAG;AAE5H,gBAAM,SAAS,MAAM,gBAAgB,GAAG,SAAS,MAAM,MAAM,EAAE,QAAQ,KAAK,eAAe,QAAQ,eAAe,KAAK,cAAc,CAAC;AACtI,kBAAQ,cAAc,GAAG,IAAI,GAAG,SAAS,MAAM,MAAM;AAGrD,eAAK,GAAG,SAAS,SAAS,kBAAkB,GAAG,SAAS,SAAS,mBAAmB,SAAS,KAAK,kBAAkB,KAAK,SAAS,KAAK,EAAE,KAAK,GAAG;AAC/I,kBAAM,cAAc,KAAK,WAAW,KAAK,SAAS,KAAK;AACvD,gBAAI,eAAe,YAAY,SAAS,GAAG;AACzC,sBAAQ,iBAAiB,WAAW;AAAA,YACtC;AAAA,UACF;AAEA,cAAI;AAAE,gBAAI,CAAC,KAAK,MAAM,MAAM,EAAE,MAAO,MAAK;AAAA,UAAuB,SAAS,KAAK;AAAE,0BAAcA,MAAK,0BAA0B,GAAG;AAAG,iBAAK;AAAA,UAAuB;AAAA,QAClK;AACA;AAAA,MACF;AAGA,YAAM,SAAS,WAAW;AAE1B,UAAI,CAAC,UAAU,SAAS,MAAM,kBAAkB,GAAG;AACjD,cAAM,UAAU,KAAK,IAAI,IAAI;AAC7B,cAAM,aAAa,KAAK,sBAAsB,OAAU;AACxD,cAAM,aAAa,MAAQ,mBAAmB;AAC9C,YAAI,YAAY,aAAa,KAAM;AACjC;AACA,kBAAQA,MAAK,uCAAkC,gBAAgB,UAAU,aAAa,GAAI,MAAM,KAAK,MAAM,YAAY,GAAI,CAAC,SAAS;AACrI,gBAAM,IAAI,QAAQ,OAAK,WAAW,GAAG,UAAU,CAAC;AAChD;AAAA,QACF;AAAA,MACF;AACA,cAAQ,aAAa,MAAM;AAC3B,aAAO;AAAA,IACT;AAEA,YAAQA,MAAK,cAAc,KAAK,OAAO,QAAQ,WAAW;AAC1D,UAAM,OAAO,QAAQ,SAAS,GAAG,EAAE,GAAG;AACtC,YAAQ,OAAO,SAAS,WAAW,OAAO,SAAS;AAAA,EACrD;AAAA,EAEQ,iBAAiB,KAAsB;AAAE,WAAO,iBAAiB,GAAG;AAAA,EAAG;AAAA;AAAA,EAGvE,gBAAgB,KAAkC;AAAE,WAAO,gBAAgB,GAAG;AAAA,EAAG;AAAA,EAEzF,MAAc,iBACZ,SACA,QACgI;AAEhI,UAAM,cAAc,IAAI,gBAAgB;AACxC,UAAM,QAAQ,WAAW,MAAM,YAAY,MAAM,IAAI,MAAM,mBAAmB,CAAC,GAAG,KAAK,sBAAsB,OAAO,EAAE,iBAAiB;AACvI,UAAM,WAAW,YAAY,IAAI,CAAC,QAAQ,YAAY,MAAM,CAAC;AAE7D,QAAI;AAEJ,UAAI,KAAK,OAAO,cAAc,aAAa;AACzC,cAAM,EAAE,mBAAmB,IAAI,MAAM,OAAO,iCAAwB;AACpE,cAAM,EAAE,kBAAkB,IAAI,MAAM,OAAO,oCAA2B;AACtE,cAAM,OAAO,QAAQ,SAAS,IAAI,QAAM,EAAE,MAAM,EAAE,MAAM,SAAS,EAAE,WAAW,GAA6B,EAAE;AAC7G,cAAM,UAAU,EAAE,GAAG,mBAAmB,KAAK,aAAa,MAAM,eAAe,KAAK,aAAa,GAAG,KAAK,OAAO,SAAS,GAAG,QAAQ,KAAK;AACzI,cAAM,OAA+B,EAAE,gBAAgB,mBAAmB;AAC1E,YAAI,KAAK,aAAc,MAAK,eAAe,IAAI,UAAU,KAAK,YAAY;AAC1E,cAAM,MAAM,MAAM,MAAM,GAAG,KAAK,cAAc,cAAc;AAAA,UAC1D,QAAQ;AAAA,UAAQ,SAAS;AAAA,UAAM,MAAM,KAAK,UAAU,OAAO;AAAA,UAAG,QAAQ;AAAA,QACxE,CAAC;AACD,YAAI,CAAC,IAAI,IAAI;AAAE,gBAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,SAAO;AAAE,0BAAcA,MAAK,mBAAmB,GAAG;AAAG,mBAAO;AAAA,UAAI,CAAC;AAAG,gBAAM,IAAI,MAAM,aAAa,IAAI,MAAM,KAAK,KAAK,MAAM,GAAG,GAAG,CAAC,EAAE;AAAA,QAAG;AAE1L,YAAIC,WAAU;AACd,YAAIC,SAAqE;AACzE,cAAM,cAAc,oBAAI,IAA6D;AACrF,yBAAiB,SAAS,kBAAkB,KAAK,QAAQ,GAAG;AAC1D,eAAK,kBAAkB,KAAK,IAAI;AAChC,cAAI,MAAM,SAAS,SAAS;AAAE,YAAAD,YAAW,MAAM;AAAS,iBAAK,qBAAqB,MAAM;AAAS,iBAAK,yBAAyB,MAAM,OAAO;AAAA,UAAG,WACtI,MAAM,SAAS,mBAAmB;AAAE,iBAAK,mBAAmB,aAAa,KAAK;AAAA,UAAG,WACjF,MAAM,SAAS,QAAQ;AAAE,YAAAC,SAAQ,MAAM;AAAA,UAAO;AAAA,QACzD;AACA,qBAAa,KAAK;AAClB,cAAMC,aAAY,KAAK,kBAAkB,WAAW;AACpD,eAAO,EAAE,SAASF,YAAW,MAAM,WAAAE,YAAW,OAAAD,OAAM;AAAA,MACtD;AAGA,UAAI,KAAK,OAAO,cAAc,aAAa;AACzC,cAAM,EAAE,oBAAoB,sBAAsB,IAAI,MAAM,OAAO,iCAAwB;AAC3F,cAAM,EAAE,kBAAkB,IAAI,MAAM,OAAO,oCAA2B;AACtE,cAAM,OAAO,QAAQ,SAAS,IAAI,QAAM,EAAE,MAAM,EAAE,MAAM,SAAS,EAAE,WAAW,IAA8B,cAAc,EAAE,aAAa,EAAE;AAC3I,cAAM,UAAU,EAAE,GAAG,mBAAmB,KAAK,aAAa,MAAM,KAAK,OAAO,WAAW,eAAe,KAAK,aAAa,CAAC,GAAG,QAAQ,KAAK;AACzI,cAAM,OAAO,sBAAsB,KAAK,gBAAgB,EAAE;AAC1D,cAAM,MAAM,MAAM,MAAM,GAAG,KAAK,cAAc,aAAa;AAAA,UACzD,QAAQ;AAAA,UAAQ,SAAS;AAAA,UAAM,MAAM,KAAK,UAAU,OAAO;AAAA,UAAG,QAAQ;AAAA,QACxE,CAAC;AACD,YAAI,CAAC,IAAI,IAAI;AAAE,gBAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,SAAO;AAAE,0BAAcF,MAAK,mBAAmB,GAAG;AAAG,mBAAO;AAAA,UAAI,CAAC;AAAG,gBAAM,IAAI,MAAM,aAAa,IAAI,MAAM,KAAK,KAAK,MAAM,GAAG,GAAG,CAAC,EAAE;AAAA,QAAG;AAE1L,YAAIC,WAAU;AACd,YAAIC,SAAqE;AACzE,cAAM,cAAc,oBAAI,IAA6D;AACrF,yBAAiB,SAAS,kBAAkB,KAAK,QAAQ,GAAG;AAC1D,eAAK,kBAAkB,KAAK,IAAI;AAChC,cAAI,MAAM,SAAS,SAAS;AAAE,YAAAD,YAAW,MAAM;AAAS,iBAAK,qBAAqB,MAAM;AAAS,iBAAK,yBAAyB,MAAM,OAAO;AAAA,UAAG,WACtI,MAAM,SAAS,mBAAmB;AAAE,iBAAK,mBAAmB,aAAa,KAAK;AAAA,UAAG,WACjF,MAAM,SAAS,QAAQ;AAAE,YAAAC,SAAQ,MAAM;AAAA,UAAO;AAAA,QACzD;AACA,qBAAa,KAAK;AAClB,cAAMC,aAAY,KAAK,kBAAkB,WAAW;AACpD,eAAO,EAAE,SAASF,YAAW,MAAM,WAAAE,YAAW,OAAAD,OAAM;AAAA,MACtD;AAEA,YAAM,OAAgC;AAAA,QACpC,OAAO,KAAK;AAAA,QACZ,UAAU,QAAQ;AAAA,QAClB,OAAO,eAAe,KAAK,aAAa;AAAA,QACxC,YAAY,KAAK,OAAO;AAAA,QACxB,QAAQ;AAAA,QACR,gBAAgB,EAAE,eAAe,KAAK;AAAA,MACxC;AAGA,UAAI,KAAK,OAAO,UAAU;AACxB,YAAI,KAAK,OAAO,SAAS,UAAU,UAAU;AAC3C,eAAK,mBAAmB,KAAK,OAAO,SAAS;AAAA,QAC/C,WAAW,KAAK,OAAO,SAAS,UAAU,YAAY;AACpD,eAAK,WAAW,EAAE,MAAM,WAAW,eAAe,KAAK,OAAO,SAAS,QAAQ;AAAA,QACjF;AAAA,MACF;AAEA,YAAM,UAAkC,EAAE,gBAAgB,mBAAmB;AAC7E,UAAI,KAAK,aAAc,SAAQ,eAAe,IAAI,UAAU,KAAK,YAAY;AAE7E,YAAM,WAAW,MAAM;AAAA,QACrB,YAAY;AACV,gBAAM,MAAM,MAAM,MAAM,GAAG,KAAK,cAAc,qBAAqB;AAAA,YACjE,QAAQ;AAAA,YACR;AAAA,YACA,MAAM,KAAK,UAAU,IAAI;AAAA,YACzB,QAAQ;AAAA,UACV,CAAC;AACD,cAAI,CAAC,IAAI,IAAI;AACX,kBAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,SAAO;AAAE,4BAAcF,MAAK,mBAAmB,GAAG;AAAG,qBAAO;AAAA,YAAI,CAAC;AACrG,kBAAM,IAAI,MAAM,aAAa,IAAI,MAAM,KAAK,KAAK,MAAM,GAAG,GAAG,CAAC,EAAE;AAAA,UAClE;AACA,iBAAO;AAAA,QACT;AAAA,QACA;AAAA,UACE,UAAU;AAAA,UAAG,YAAY;AAAA,UACzB,eAAe,CAAC,QAAQ;AACtB,kBAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,gBAAI,oBAAoB,KAAK,GAAG,EAAG,QAAO;AAE1C,gBAAI,8BAA8B,KAAK,GAAG,EAAG,QAAO;AACpD,mBAAO,CAAC,QAAQ,GAAG;AAAA,UACrB;AAAA,QACF;AAAA,MACF;AAEA,UAAI,UAAU;AACd,YAAM,sBAAsB,oBAAI,IAA6D;AAC7F,UAAI,QAAqE;AAEzE,uBAAiB,SAAS,eAAe,UAAU,QAAQ,GAAG;AAC5D,aAAK,kBAAkB,KAAK,IAAI;AAEhC,gBAAQ,MAAM,MAAM;AAAA,UAClB,KAAK;AACH,uBAAW,MAAM;AACjB,iBAAK,qBAAqB,MAAM;AAChC,iBAAK,yBAAyB,MAAM,OAAO;AAC3C;AAAA,UAEF,KAAK;AACH,iBAAK,mBAAmB,qBAAqB,KAAK;AAClD;AAAA,UAEF,KAAK;AACH,oBAAQ,MAAM;AACd;AAAA,QACJ;AAAA,MACF;AAEA,YAAM,YAAY,KAAK,kBAAkB,mBAAmB;AAE5D,aAAO,EAAE,SAAS,WAAW,MAAM,WAAW,MAAM;AAAA,IACpD,UAAE;AACA,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF;AAAA;AAAA,EAGQ,mBACN,KACA,OACM;AAEN,UAAM,MAAM,MAAM,MAAM,OAAO,MAAM,KAAK;AAC1C,UAAM,WAAW,IAAI,IAAI,GAAG;AAC5B,QAAI,UAAU;AACZ,UAAI,MAAM,UAAW,UAAS,aAAa,MAAM;AAAA,IACnD,OAAO;AACL,UAAI,IAAI,KAAK,EAAE,IAAI,MAAM,MAAM,QAAQ,IAAI,IAAI,IAAI,MAAM,MAAM,QAAQ,IAAI,WAAW,MAAM,aAAa,GAAG,CAAC;AAAA,IAC/G;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,kBAAkB,KAA+E;AACvG,UAAM,MAAkB,CAAC,GAAG,IAAI,OAAO,CAAC,EAAE,IAAI,SAAO;AAAA,MACnD,IAAI,GAAG;AAAA,MAAI,MAAM;AAAA,MAAqB,UAAU,EAAE,MAAM,GAAG,MAAM,WAAW,GAAG,UAAU;AAAA,IAC3F,EAAE;AACF,WAAO,mBAAmB,GAAG;AAAA,EAC/B;AAAA,EAEQ,mBAAmB,YAAyC;AAClE,QAAI,UAAU,KAAK,SAAS,IAAI,UAAU;AAC1C,QAAI,CAAC,SAAS;AACZ,gBAAU,IAAI,oBAAoB,KAAK,cAAc,KAAK,OAAO,UAAU;AAC3E,WAAK,SAAS,IAAI,YAAY,OAAO;AAAA,IACvC;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,aAAa,YAAmC;AACpD,UAAM,UAAU,KAAK,SAAS,IAAI,UAAU;AAC5C,QAAI,QAAS,SAAQ,MAAM,KAAK,YAAY;AAAA,QACvC,MAAK,SAAS,OAAO,UAAU;AACpC,YAAQA,MAAK,WAAW,UAAU,QAAQ;AAAA,EAC5C;AAAA,EAEA,MAAM,gBAA+B;AACnC,eAAW,MAAM,KAAK,iBAAiB,OAAO,EAAG,IAAG,MAAM;AAAA,EAC5D;AAAA,EAEA,UAAgB;AACd,eAAW,MAAM,KAAK,iBAAiB,OAAO,EAAG,IAAG,MAAM;AAC1D,SAAK,SAAS,MAAM;AACpB,SAAK,iBAAiB,MAAM;AAAA,EAC9B;AAAA,EAEA,IAAI,UAAmB;AAAE,WAAO;AAAA,EAAM;AAAA,EACtC,IAAI,iBAAyB;AAAE,WAAO,KAAK;AAAA,EAAiB;AAAA,EAC5D,IAAI,aAAqB;AAAE,WAAO,KAAK;AAAA,EAAa;AAAA,EACpD,IAAI,qBAA6B;AAAE,WAAO,KAAK;AAAA,EAAqB;AAAA;AAAA,EAGpE,SAAS,OAAqB;AAC5B,SAAK,cAAc;AACnB,IAAC,KAAK,OAA6B,QAAQ;AAC3C,YAAQA,MAAK,0BAA0B,KAAK,EAAE;AAAA,EAChD;AAAA;AAAA,EAGA,eAAe,MAA8G;AAC3H,QAAI,KAAK,qBAAqB,MAAM;AAClC,YAAM,IAAI,MAAM,oFAA+E;AAAA,IACjG;AACA,SAAK,iBAAiB,KAAK;AAC3B,SAAK,eAAe,KAAK;AACzB,SAAK,cAAc,KAAK;AACxB,IAAC,KAAK,OAAiD,QAAQ,KAAK;AACpE,IAAC,KAAK,OAAkC,aAAa,KAAK;AAC1D,SAAK,SAAS,KAAK;AACnB,YAAQA,MAAK,sBAAsB,KAAK,KAAK,MAAM,KAAK,QAAQ,YAAY,KAAK,UAAU,GAAG;AAAA,EAChG;AAAA;AAAA,EAGA,WAAmB;AAAE,WAAO,KAAK;AAAA,EAAa;AAAA,EAC9C,IAAI,4BAAoC;AAAE,WAAO,KAAK;AAAA,EAAmB;AAAA,EACzE,IAAI,oBAA8B;AAAE,WAAO,CAAC;AAAA,EAAG;AAAA;AAAA,EAG/C,IAAI,kBAAiC;AAAE,WAAO,KAAK;AAAA,EAAkB;AAAA,EACrE,mBAAmB,IAAyB;AAAE,SAAK,qBAAqB;AAAA,EAAI;AAAA,EAE5E,IAAI,iBAAgC;AAAE,WAAO,KAAK;AAAA,EAAiB;AAAA,EAElD,gBAAgB,OAAO,EAAE,oBAAoB;AAAA,EAE9D,MAAM,cAA6B;AACjC,QAAI,CAAC,KAAK,iBAAkB;AAC5B,UAAM,OAAO,KAAK,IAAI,KAAK,KAAK,mBAAmB,KAAK;AACxD,QAAI,OAAO,KAAK,eAAe;AAC7B,cAAQA,MAAK,oCAAoC,KAAK,MAAM,OAAO,GAAI,CAAC,yBAAoB;AAC5F,YAAM,KAAK,cAAc;AAAA,IAC3B;AAAA,EACF;AACF;",
|
|
6
|
-
"names": ["TAG", "TAG", "content", "usage", "toolCalls"]
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/platforms/irc/irc-client.ts", "../src/platforms/irc/irc-adapter.ts"],
|
|
4
|
-
"sourcesContent": ["import net from \"node:net\";\nimport tls from \"node:tls\";\nimport { logInfo, logWarn, logDebug } from \"../../components/logger.js\";\n\nconst TAG = \"irc-client\";\n\nexport interface IrcClientOptions {\n host: string;\n port: number;\n useTls: boolean;\n nick: string;\n nickservPassword?: string;\n channels: string[];\n onPrivmsg: (sender: string, target: string, text: string, hostmask: string) => void;\n onReady?: () => void;\n onDisconnect?: () => void;\n}\n\nexport interface IrcClient {\n send: (target: string, text: string) => void;\n quit: (reason?: string) => void;\n readonly nick: string;\n}\n\nexport function createIrcClient(opts: IrcClientOptions): IrcClient {\n let socket: net.Socket | null = null;\n let reconnectTimer: NodeJS.Timeout | null = null;\n let reconnectDelay = 5000;\n let registered = false;\n let stopped = false;\n let sendQueue: string[] = [];\n let sendTimer: NodeJS.Timeout | null = null;\n let buffer = \"\";\n let consecutiveFailures = 0;\n const MAX_RETRIES = 5;\n\n function connect(): void {\n if (stopped) return;\n registered = false;\n buffer = \"\";\n\n const onConnect = (): void => {\n logInfo(TAG, `Connected to ${opts.host}:${opts.port}`);\n reconnectDelay = 5000;\n consecutiveFailures = 0;\n raw(`NICK ${opts.nick}`);\n raw(`USER ${opts.nick} 0 * :abtars`);\n };\n\n if (opts.useTls) {\n socket = tls.connect({ host: opts.host, port: opts.port, rejectUnauthorized: false }, onConnect);\n } else {\n socket = net.createConnection({ host: opts.host, port: opts.port }, onConnect);\n }\n\n socket.setEncoding(\"utf-8\");\n socket.on(\"data\", onData);\n socket.on(\"error\", (err) => {\n if (reconnectDelay <= 5000) logWarn(TAG, `Socket error: ${err.message}`);\n else logDebug(TAG, `Socket error (reconnecting): ${err.message}`);\n });\n socket.on(\"close\", () => {\n if (reconnectDelay <= 5000) logWarn(TAG, `Disconnected from ${opts.host}`);\n opts.onDisconnect?.();\n scheduleReconnect();\n });\n }\n\n function scheduleReconnect(): void {\n if (stopped) return;\n consecutiveFailures++;\n if (consecutiveFailures >= MAX_RETRIES) {\n logWarn(TAG, `IRC unavailable \u2014 giving up after ${MAX_RETRIES} attempts. Restart bridge to retry.`);\n stopped = true;\n return;\n }\n reconnectTimer = setTimeout(() => {\n if (reconnectDelay <= 10000) logInfo(TAG, `Reconnecting to ${opts.host}:${opts.port}...`);\n else logDebug(TAG, `Reconnecting to ${opts.host}:${opts.port} (delay=${Math.round(reconnectDelay/1000)}s)...`);\n connect();\n }, reconnectDelay);\n reconnectDelay = Math.min(reconnectDelay * 2, 300000);\n }\n\n function raw(line: string): void {\n socket?.write(line + \"\\r\\n\");\n }\n\n function onData(chunk: string): void {\n buffer += chunk;\n const lines = buffer.split(\"\\r\\n\");\n buffer = lines.pop()!;\n for (const line of lines) {\n if (line) handleLine(line);\n }\n }\n\n function handleLine(line: string): void {\n if (line.startsWith(\"PING\")) {\n raw(\"PONG\" + line.slice(4));\n return;\n }\n\n const parts = line.split(\" \");\n const code = parts[1];\n\n // 001 RPL_WELCOME \u2014 registration complete\n if (code === \"001\" && !registered) {\n registered = true;\n if (opts.nickservPassword) {\n raw(`PRIVMSG NickServ :IDENTIFY ${opts.nickservPassword}`);\n // Join after short delay to let NickServ process\n setTimeout(joinChannels, 2000);\n } else {\n joinChannels();\n }\n return;\n }\n\n // PRIVMSG\n if (code === \"PRIVMSG\") {\n const prefix = parts[0]!.slice(1); // remove leading ':'\n const sender = prefix.split(\"!\")[0]!;\n const target = parts[2]!;\n const textStart = line.indexOf(\":\", line.indexOf(\"PRIVMSG\") + 8);\n const text = textStart >= 0 ? line.slice(textStart + 1) : \"\";\n opts.onPrivmsg(sender, target, text, prefix);\n return;\n }\n }\n\n function joinChannels(): void {\n for (const ch of opts.channels) {\n raw(`JOIN ${ch}`);\n }\n logInfo(TAG, `Joined: ${opts.channels.join(\", \")}`);\n opts.onReady?.();\n }\n\n // Rate-limited send: 1 msg/sec\n function flushQueue(): void {\n if (sendQueue.length === 0) {\n sendTimer = null;\n return;\n }\n const msg = sendQueue.shift()!;\n raw(msg);\n sendTimer = setTimeout(flushQueue, 1000);\n }\n\n function sendPrivmsg(target: string, text: string): void {\n const line = `PRIVMSG ${target} :${text}`;\n sendQueue.push(line);\n if (!sendTimer) {\n sendTimer = setTimeout(flushQueue, 0);\n }\n }\n\n connect();\n\n return {\n nick: opts.nick,\n send: sendPrivmsg,\n quit(reason?: string): void {\n stopped = true;\n if (reconnectTimer) clearTimeout(reconnectTimer);\n if (sendTimer) clearTimeout(sendTimer);\n raw(`QUIT :${reason ?? \"bye\"}`);\n setTimeout(() => socket?.destroy(), 1000);\n },\n };\n}\n", "import type { PlatformAdapter, InboundMessage, SendOpts } from \"../../types/platform.js\";\nimport type { IrcConfig, IrcServerConfig } from \"./irc-config.js\";\nimport { createIrcClient, type IrcClient } from \"./irc-client.js\";\nimport { signMessage, verifyMessage } from \"../../components/digital-signature.js\";\nimport { logInfo, logDebug, logWarn } from \"../../components/logger.js\";\n\nconst TAG = \"irc\";\nconst MAX_LINE_PLAIN = 450;\nconst MAX_LINE_SECURE = 340; // leave room for [sig:ts:base64] suffix\n\nexport interface IrcAdapterDeps {\n onMessage: (msg: InboundMessage) => void;\n}\n\nexport class IrcAdapter implements PlatformAdapter {\n readonly name = \"irc\" as const;\n readonly capabilities = { voice: false, reactions: false, edit: false, typing: false, threads: false };\n readonly supportsStreaming = false;\n\n private clients: IrcClient[] = [];\n private config: IrcConfig;\n private deps: IrcAdapterDeps;\n\n constructor(config: IrcConfig, deps: IrcAdapterDeps) {\n this.config = config;\n this.deps = deps;\n }\n\n async start(): Promise<void> {\n for (const server of this.config.servers) {\n const channels = Object.keys(server.channels);\n const client = createIrcClient({\n host: server.host,\n port: server.port,\n useTls: server.tls,\n nick: server.nick,\n nickservPassword: server.nickservPassword,\n channels,\n onPrivmsg: (sender, target, text, hostmask) => this.handlePrivmsg(server, client, sender, target, text, hostmask),\n onReady: () => logInfo(TAG, `[${server.id}] ready`),\n });\n this.clients.push(client);\n }\n }\n\n stop(): void {\n for (const c of this.clients) c.quit();\n this.clients = [];\n }\n\n authorize(_msg: InboundMessage): boolean {\n return true; // Authorization handled in handlePrivmsg\n }\n\n async sendMessage(channelId: string, text: string, _opts?: SendOpts): Promise<number | undefined> {\n const [serverId, channel] = channelId.split(\":\", 2);\n const client = this.clients.find((_, i) => this.config.servers[i]?.id === serverId);\n if (!client || !channel) return undefined;\n\n const server = this.config.servers.find(s => s.id === serverId);\n const channelConfig = server?.channels[channel];\n const isSigned = channelConfig?.mode === \"signed\";\n const maxLine = isSigned ? MAX_LINE_SECURE : MAX_LINE_PLAIN;\n\n const lines = this.chunkText(text, maxLine);\n for (const line of lines) {\n // Track outgoing for dedup (#420)\n this.recentOutgoing.add(line);\n setTimeout(() => this.recentOutgoing.delete(line), 30_000);\n\n if (isSigned && this.config.identity?.privateKey) {\n const { tag } = signMessage(this.config.identity.privateKey, server!.nick, channel, line);\n client.send(channel, `${line} ${tag}`);\n } else {\n client.send(channel, line);\n }\n }\n return undefined;\n }\n\n chunkResponse(text: string): string[] {\n return this.chunkText(text, MAX_LINE_PLAIN);\n }\n\n private chunkText(text: string, maxLen: number): string[] {\n const result: string[] = [];\n for (const paragraph of text.split(\"\\n\")) {\n if (paragraph.length <= maxLen) {\n if (paragraph.trim()) result.push(paragraph);\n } else {\n let remaining = paragraph;\n while (remaining.length > maxLen) {\n let cut = remaining.lastIndexOf(\" \", maxLen);\n if (cut <= 0) cut = maxLen;\n result.push(remaining.slice(0, cut));\n remaining = remaining.slice(cut).trimStart();\n }\n if (remaining.trim()) result.push(remaining);\n }\n }\n return result;\n }\n\n private recentOutgoing = new Set<string>();\n\n private handlePrivmsg(server: IrcServerConfig, _client: IrcClient, sender: string, target: string, text: string, hostmask: string): void {\n // Self-echo filter \u2014 case-insensitive (#420)\n if (sender.toLowerCase() === server.nick.toLowerCase()) return;\n\n // Content dedup \u2014 catch echoed messages regardless of sender nick (#420)\n if (this.recentOutgoing.has(text)) return;\n\n // [NO_REPLY] filter \u2014 other agent signaled no response needed (#421)\n if (/\\[NO-REPLY\\]/i.test(text)) return;\n\n // Only handle channel messages\n if (!target.startsWith(\"#\")) return;\n\n const channelConfig = server.channels[target];\n if (!channelConfig) {\n logDebug(TAG, `[${server.id}] Ignoring message in unconfigured channel ${target}`);\n return;\n }\n\n if (channelConfig.mode === \"signed\") {\n // Allow specific hostmasks/nicks to bypass signature (humans)\n const allowed = channelConfig.allowUnsigned?.some(pattern =>\n hostmask.startsWith(pattern) || sender === pattern\n );\n if (allowed) {\n logDebug(TAG, `[${server.id}] Unsigned pass for ${hostmask}`);\n } else {\n const pubkey = channelConfig.trustedKeys[sender];\n if (!pubkey) {\n logDebug(TAG, `[${server.id}] Dropped from ${sender} \u2014 no trusted key`);\n return;\n }\n const result = verifyMessage(pubkey, sender, target, text);\n if (!result.valid) {\n logWarn(TAG, `[${server.id}] Signature failed from ${sender}: ${result.reason}`);\n return;\n }\n text = result.text;\n }\n } else {\n // Plain mode: sender allowlist\n if (channelConfig.allowFrom.length > 0 && !channelConfig.allowFrom.includes(sender)) {\n logDebug(TAG, `[${server.id}] Dropped from ${sender} (not in allowFrom)`);\n return;\n }\n }\n\n // Mention gating\n if (channelConfig.requireMention) {\n const nickPattern = new RegExp(`\\\\b${escapeRegex(server.nick)}\\\\b[:,]?`, \"i\");\n if (!nickPattern.test(text)) return;\n text = text.replace(nickPattern, \"\").trim();\n }\n\n if (!text) return;\n\n const channelId = `${server.id}:${target}`;\n const msg: InboundMessage = {\n platform: \"irc\",\n channelId,\n userId: sender,\n senderId: sender,\n senderName: sender,\n text,\n timestamp: Date.now(),\n isGroup: true,\n isVoice: false,\n };\n\n this.deps.onMessage(msg);\n }\n}\n\nfunction escapeRegex(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["TAG"]
|
|
7
|
-
}
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { createRequire as __bundleCreateRequire } from 'node:module'; import { fileURLToPath as __bundleFileURLToPath } from 'node:url'; import { dirname as __bundleDirname } from 'node:path'; const require = __bundleCreateRequire(import.meta.url); const __chunk_filename = __bundleFileURLToPath(import.meta.url); const __chunk_dirname = __bundleDirname(__chunk_filename);
|
|
3
|
-
import "./chunk-7K2YZTLD.js";
|
|
4
|
-
|
|
5
|
-
// src/cli/commands/passwd.ts
|
|
6
|
-
import { createInterface } from "node:readline";
|
|
7
|
-
import { existsSync, readFileSync, writeFileSync, readdirSync, statSync } from "node:fs";
|
|
8
|
-
import { join } from "node:path";
|
|
9
|
-
import { homedir } from "node:os";
|
|
10
|
-
import { createDecipheriv, createCipheriv, randomBytes, hkdfSync } from "node:crypto";
|
|
11
|
-
function ask(rl, question) {
|
|
12
|
-
return new Promise((resolve) => rl.question(question, resolve));
|
|
13
|
-
}
|
|
14
|
-
async function passwd() {
|
|
15
|
-
const { deriveFromPassphrase, writeKeyVerify, validateKey, loadKeyFromFile, _resetKeyCache } = await import("abmind");
|
|
16
|
-
const { writeToKeyring } = await import("abmind");
|
|
17
|
-
const keyFile = join(homedir(), ".abmind", "secret", "abmind.key");
|
|
18
|
-
const verifyFile = join(homedir(), ".abmind", "secret", "key.verify");
|
|
19
|
-
const secretDir = join(homedir(), ".abtars", "secret");
|
|
20
|
-
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
21
|
-
try {
|
|
22
|
-
const username = await ask(rl, "Username (same on all machines): ");
|
|
23
|
-
if (!username.trim()) {
|
|
24
|
-
console.error("Username cannot be empty.");
|
|
25
|
-
return 1;
|
|
26
|
-
}
|
|
27
|
-
let oldKey;
|
|
28
|
-
const oldPass = await ask(rl, "Current passphrase (empty = migrate from key file): ");
|
|
29
|
-
if (oldPass) {
|
|
30
|
-
oldKey = deriveFromPassphrase(oldPass, username.trim());
|
|
31
|
-
if (!existsSync(verifyFile)) {
|
|
32
|
-
console.error("No key.verify file \u2014 cannot validate passphrase. Run migration with empty old passphrase first.");
|
|
33
|
-
return 1;
|
|
34
|
-
}
|
|
35
|
-
if (!validateKey(oldKey)) {
|
|
36
|
-
console.error("Wrong passphrase.");
|
|
37
|
-
return 1;
|
|
38
|
-
}
|
|
39
|
-
} else {
|
|
40
|
-
if (!existsSync(keyFile)) {
|
|
41
|
-
console.error(`No key file at ${keyFile} and no passphrase. Run 'abtars install' first.`);
|
|
42
|
-
return 1;
|
|
43
|
-
}
|
|
44
|
-
oldKey = loadKeyFromFile(keyFile);
|
|
45
|
-
}
|
|
46
|
-
const newPass = await ask(rl, "New passphrase (min 6 chars): ");
|
|
47
|
-
if (newPass.length < 6) {
|
|
48
|
-
console.error("Too short.");
|
|
49
|
-
return 1;
|
|
50
|
-
}
|
|
51
|
-
const confirm = await ask(rl, "Confirm: ");
|
|
52
|
-
if (newPass !== confirm) {
|
|
53
|
-
console.error("Mismatch.");
|
|
54
|
-
return 1;
|
|
55
|
-
}
|
|
56
|
-
const newKey = deriveFromPassphrase(newPass, username.trim());
|
|
57
|
-
let dbCount = 0;
|
|
58
|
-
try {
|
|
59
|
-
const memDir = process.env["ABMIND_HOME"] ?? join(homedir(), ".abmind");
|
|
60
|
-
const dbPath = join(memDir, "memory", "memory.db");
|
|
61
|
-
if (existsSync(dbPath)) {
|
|
62
|
-
const oldDbKey = Buffer.from(hkdfSync("sha256", oldKey, "", "abmind-secrets-v1", 32));
|
|
63
|
-
const newDbKey = Buffer.from(hkdfSync("sha256", newKey, "", "abmind-secrets-v1", 32));
|
|
64
|
-
const { loadNative } = await import("abmind");
|
|
65
|
-
const Database = loadNative("better-sqlite3");
|
|
66
|
-
const db = new Database(dbPath, { readonly: false });
|
|
67
|
-
const rows = db.prepare("SELECT id, content_en FROM extracted_memories WHERE classification = 3").all();
|
|
68
|
-
const update = db.prepare("UPDATE extracted_memories SET content_en = ? WHERE id = ?");
|
|
69
|
-
for (const row of rows) {
|
|
70
|
-
try {
|
|
71
|
-
const buf = Buffer.from(row.content_en, "base64");
|
|
72
|
-
const iv = buf.subarray(0, 12);
|
|
73
|
-
const tag = buf.subarray(buf.length - 16);
|
|
74
|
-
const ct = buf.subarray(12, buf.length - 16);
|
|
75
|
-
const d = createDecipheriv("aes-256-gcm", oldDbKey, iv);
|
|
76
|
-
d.setAuthTag(tag);
|
|
77
|
-
const plain = d.update(ct, void 0, "utf-8") + d.final("utf-8");
|
|
78
|
-
const newIv = randomBytes(12);
|
|
79
|
-
const c = createCipheriv("aes-256-gcm", newDbKey, newIv);
|
|
80
|
-
const enc = Buffer.concat([c.update(plain, "utf-8"), c.final()]);
|
|
81
|
-
update.run(Buffer.concat([newIv, enc, c.getAuthTag()]).toString("base64"), row.id);
|
|
82
|
-
dbCount++;
|
|
83
|
-
} catch {
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
db.close();
|
|
87
|
-
}
|
|
88
|
-
} catch {
|
|
89
|
-
}
|
|
90
|
-
let fileCount = 0;
|
|
91
|
-
if (existsSync(secretDir)) {
|
|
92
|
-
const oldPurpose = Buffer.from(hkdfSync("sha256", oldKey, "", "abtars-secrets-files-v1", 32));
|
|
93
|
-
const newPurpose = Buffer.from(hkdfSync("sha256", newKey, "", "abtars-secrets-files-v1", 32));
|
|
94
|
-
for (const f of readdirSync(secretDir)) {
|
|
95
|
-
const fp = join(secretDir, f);
|
|
96
|
-
if (statSync(fp).isDirectory()) continue;
|
|
97
|
-
const raw = readFileSync(fp, "utf-8").trim();
|
|
98
|
-
if (!raw.startsWith("ENC:")) continue;
|
|
99
|
-
try {
|
|
100
|
-
const buf = Buffer.from(raw.slice(4), "base64");
|
|
101
|
-
const iv = buf.subarray(1, 13);
|
|
102
|
-
const tag = buf.subarray(buf.length - 16);
|
|
103
|
-
const ct = buf.subarray(13, buf.length - 16);
|
|
104
|
-
const d = createDecipheriv("aes-256-gcm", oldPurpose, iv);
|
|
105
|
-
d.setAuthTag(tag);
|
|
106
|
-
const plain = d.update(ct, void 0, "utf-8") + d.final("utf-8");
|
|
107
|
-
const newIv = randomBytes(12);
|
|
108
|
-
const c = createCipheriv("aes-256-gcm", newPurpose, newIv);
|
|
109
|
-
const enc = Buffer.concat([c.update(plain, "utf-8"), c.final()]);
|
|
110
|
-
const blob = "ENC:" + Buffer.concat([Buffer.from([1]), newIv, enc, c.getAuthTag()]).toString("base64");
|
|
111
|
-
writeFileSync(fp, blob, { mode: 384 });
|
|
112
|
-
fileCount++;
|
|
113
|
-
} catch {
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
_resetKeyCache();
|
|
118
|
-
writeFileSync(keyFile, newKey.toString("hex") + "\n", { mode: 384 });
|
|
119
|
-
writeKeyVerify(newKey);
|
|
120
|
-
const stored = writeToKeyring(newPass);
|
|
121
|
-
console.log(`\u2713 Done. ${dbCount} memories + ${fileCount} secrets re-encrypted.`);
|
|
122
|
-
console.log(`
|
|
123
|
-
ABMIND_KEY=${newKey.toString("hex")}`);
|
|
124
|
-
if (stored) console.log("\u2713 Passphrase stored in OS keyring.");
|
|
125
|
-
else console.log("Set ABMIND_KEY as OS env var for daemon mode.");
|
|
126
|
-
return 0;
|
|
127
|
-
} finally {
|
|
128
|
-
rl.close();
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
export {
|
|
132
|
-
passwd
|
|
133
|
-
};
|
|
134
|
-
//# sourceMappingURL=passwd-QSHZJ2CG.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/cli/commands/passwd.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * abtars passwd \u2014 set or change encryption passphrase (#607).\n *\n * - Empty old passphrase = migration from key file\n * - Non-empty old passphrase = password change\n * Re-encrypts: abmind DB memories + ~/.abtars/secret/ files.\n */\n\nimport { createInterface } from \"node:readline\";\nimport { existsSync, readFileSync, writeFileSync, readdirSync, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { createDecipheriv, createCipheriv, randomBytes, hkdfSync } from \"node:crypto\";\n\nfunction ask(rl: ReturnType<typeof createInterface>, question: string): Promise<string> {\n return new Promise(resolve => rl.question(question, resolve));\n}\n\nexport async function passwd(): Promise<number> {\n const { deriveFromPassphrase, writeKeyVerify, validateKey, loadKeyFromFile, _resetKeyCache } = await import(\"abmind\");\n const { writeToKeyring } = await import(\"abmind\");\n\n const keyFile = join(homedir(), \".abmind\", \"secret\", \"abmind.key\");\n const verifyFile = join(homedir(), \".abmind\", \"secret\", \"key.verify\");\n const secretDir = join(homedir(), \".abtars\", \"secret\");\n\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n try {\n // Username\n const username = await ask(rl, \"Username (same on all machines): \");\n if (!username.trim()) { console.error(\"Username cannot be empty.\"); return 1; }\n\n // Old key: from passphrase or from file\n let oldKey: Buffer;\n const oldPass = await ask(rl, \"Current passphrase (empty = migrate from key file): \");\n if (oldPass) {\n oldKey = deriveFromPassphrase(oldPass, username.trim());\n if (!existsSync(verifyFile)) {\n console.error(\"No key.verify file \u2014 cannot validate passphrase. Run migration with empty old passphrase first.\"); return 1;\n }\n if (!validateKey(oldKey)) {\n console.error(\"Wrong passphrase.\"); return 1;\n }\n } else {\n if (!existsSync(keyFile)) { console.error(`No key file at ${keyFile} and no passphrase. Run 'abtars install' first.`); return 1; }\n oldKey = loadKeyFromFile(keyFile);\n }\n\n // New passphrase\n const newPass = await ask(rl, \"New passphrase (min 6 chars): \");\n if (newPass.length < 6) { console.error(\"Too short.\"); return 1; }\n const confirm = await ask(rl, \"Confirm: \");\n if (newPass !== confirm) { console.error(\"Mismatch.\"); return 1; }\n\n const newKey = deriveFromPassphrase(newPass, username.trim());\n\n // Re-encrypt DB memories\n let dbCount = 0;\n try {\n const memDir = process.env[\"ABMIND_HOME\"] ?? join(homedir(), \".abmind\");\n const dbPath = join(memDir, \"memory\", \"memory.db\");\n if (existsSync(dbPath)) {\n const oldDbKey = Buffer.from(hkdfSync(\"sha256\", oldKey, \"\", \"abmind-secrets-v1\", 32));\n const newDbKey = Buffer.from(hkdfSync(\"sha256\", newKey, \"\", \"abmind-secrets-v1\", 32));\n const { loadNative } = await import(\"abmind\");\n const Database = loadNative(\"better-sqlite3\") as any;\n const db = new Database(dbPath, { readonly: false });\n const rows = db.prepare(\"SELECT id, content_en FROM extracted_memories WHERE classification = 3\").all() as Array<{ id: number; content_en: string }>;\n const update = db.prepare(\"UPDATE extracted_memories SET content_en = ? WHERE id = ?\");\n for (const row of rows) {\n try {\n const buf = Buffer.from(row.content_en, \"base64\");\n const iv = buf.subarray(0, 12);\n const tag = buf.subarray(buf.length - 16);\n const ct = buf.subarray(12, buf.length - 16);\n const d = createDecipheriv(\"aes-256-gcm\", oldDbKey, iv);\n d.setAuthTag(tag);\n const plain = d.update(ct, undefined, \"utf-8\") + d.final(\"utf-8\");\n const newIv = randomBytes(12);\n const c = createCipheriv(\"aes-256-gcm\", newDbKey, newIv);\n const enc = Buffer.concat([c.update(plain, \"utf-8\"), c.final()]);\n update.run(Buffer.concat([newIv, enc, c.getAuthTag()]).toString(\"base64\"), row.id);\n dbCount++;\n } catch { /* skip */ }\n }\n db.close();\n }\n } catch { /* DB not available */ }\n\n // Re-encrypt file-based secrets\n let fileCount = 0;\n if (existsSync(secretDir)) {\n const oldPurpose = Buffer.from(hkdfSync(\"sha256\", oldKey, \"\", \"abtars-secrets-files-v1\", 32));\n const newPurpose = Buffer.from(hkdfSync(\"sha256\", newKey, \"\", \"abtars-secrets-files-v1\", 32));\n for (const f of readdirSync(secretDir)) {\n const fp = join(secretDir, f);\n if (statSync(fp).isDirectory()) continue;\n const raw = readFileSync(fp, \"utf-8\").trim();\n if (!raw.startsWith(\"ENC:\")) continue;\n try {\n const buf = Buffer.from(raw.slice(4), \"base64\");\n const iv = buf.subarray(1, 13);\n const tag = buf.subarray(buf.length - 16);\n const ct = buf.subarray(13, buf.length - 16);\n const d = createDecipheriv(\"aes-256-gcm\", oldPurpose, iv);\n d.setAuthTag(tag);\n const plain = d.update(ct, undefined, \"utf-8\") + d.final(\"utf-8\");\n const newIv = randomBytes(12);\n const c = createCipheriv(\"aes-256-gcm\", newPurpose, newIv);\n const enc = Buffer.concat([c.update(plain, \"utf-8\"), c.final()]);\n const blob = \"ENC:\" + Buffer.concat([Buffer.from([0x01]), newIv, enc, c.getAuthTag()]).toString(\"base64\");\n writeFileSync(fp, blob, { mode: 0o600 });\n fileCount++;\n } catch { /* skip */ }\n }\n }\n\n // Finalize \u2014 write key file + key.verify atomically\n _resetKeyCache();\n writeFileSync(keyFile, newKey.toString(\"hex\") + \"\\n\", { mode: 0o600 });\n writeKeyVerify(newKey);\n const stored = writeToKeyring(newPass);\n\n console.log(`\u2713 Done. ${dbCount} memories + ${fileCount} secrets re-encrypted.`);\n console.log(`\\nABMIND_KEY=${newKey.toString(\"hex\")}`);\n if (stored) console.log(\"\u2713 Passphrase stored in OS keyring.\");\n else console.log(\"Set ABMIND_KEY as OS env var for daemon mode.\");\n return 0;\n } finally { rl.close(); }\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { createRequire as __bundleCreateRequire } from 'node:module'; import { fileURLToPath as __bundleFileURLToPath } from 'node:url'; import { dirname as __bundleDirname } from 'node:path'; const require = __bundleCreateRequire(import.meta.url); const __chunk_filename = __bundleFileURLToPath(import.meta.url); const __chunk_dirname = __bundleDirname(__chunk_filename);
|
|
3
|
-
import {
|
|
4
|
-
abtarsHome,
|
|
5
|
-
init_paths
|
|
6
|
-
} from "./chunk-MJ6PHMOK.js";
|
|
7
|
-
import "./chunk-7K2YZTLD.js";
|
|
8
|
-
|
|
9
|
-
// src/cli/commands/restore.ts
|
|
10
|
-
init_paths();
|
|
11
|
-
import { existsSync } from "node:fs";
|
|
12
|
-
import { spawnSync } from "node:child_process";
|
|
13
|
-
async function restore(archivePath) {
|
|
14
|
-
if (!archivePath) {
|
|
15
|
-
process.stderr.write("Usage: abtars restore <file.zip|.7z>\n");
|
|
16
|
-
return 1;
|
|
17
|
-
}
|
|
18
|
-
if (!existsSync(archivePath)) {
|
|
19
|
-
process.stderr.write(`File not found: ${archivePath}
|
|
20
|
-
`);
|
|
21
|
-
return 1;
|
|
22
|
-
}
|
|
23
|
-
const home = abtarsHome();
|
|
24
|
-
const is7z = archivePath.endsWith(".7z");
|
|
25
|
-
const listCmd = is7z ? spawnSync("7z", ["l", archivePath], { encoding: "utf-8" }) : spawnSync("unzip", ["-l", archivePath], { encoding: "utf-8" });
|
|
26
|
-
if (!listCmd.stdout?.includes("config/")) {
|
|
27
|
-
process.stderr.write("Error: archive does not contain config/ \u2014 not a valid abtars backup\n");
|
|
28
|
-
return 1;
|
|
29
|
-
}
|
|
30
|
-
let result;
|
|
31
|
-
if (is7z) {
|
|
32
|
-
result = spawnSync("7z", ["x", `-o${home}`, "-aoa", archivePath], { encoding: "utf-8", stdio: "inherit" });
|
|
33
|
-
} else {
|
|
34
|
-
result = spawnSync("unzip", ["-o", archivePath, "-d", home], { encoding: "utf-8", stdio: "inherit" });
|
|
35
|
-
}
|
|
36
|
-
if (result.status !== 0) {
|
|
37
|
-
process.stderr.write("Restore failed\n");
|
|
38
|
-
return 1;
|
|
39
|
-
}
|
|
40
|
-
process.stdout.write(`\u2713 Restored to ${home}
|
|
41
|
-
`);
|
|
42
|
-
return 0;
|
|
43
|
-
}
|
|
44
|
-
export {
|
|
45
|
-
restore
|
|
46
|
-
};
|
|
47
|
-
//# sourceMappingURL=restore-ROJF22R2.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/cli/commands/restore.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * `abtars restore` \u2014 extract user data from backup archive to ~/.abtars/\n */\n\nimport { existsSync } from \"node:fs\";\nimport { spawnSync } from \"node:child_process\";\nimport { abtarsHome } from \"../../paths.js\";\n\nexport async function restore(archivePath: string): Promise<number> {\n if (!archivePath) {\n process.stderr.write(\"Usage: abtars restore <file.zip|.7z>\\n\");\n return 1;\n }\n if (!existsSync(archivePath)) {\n process.stderr.write(`File not found: ${archivePath}\\n`);\n return 1;\n }\n\n const home = abtarsHome();\n const is7z = archivePath.endsWith(\".7z\");\n\n // Sanity check: verify archive contains config/\n const listCmd = is7z\n ? spawnSync(\"7z\", [\"l\", archivePath], { encoding: \"utf-8\" })\n : spawnSync(\"unzip\", [\"-l\", archivePath], { encoding: \"utf-8\" });\n if (!listCmd.stdout?.includes(\"config/\")) {\n process.stderr.write(\"Error: archive does not contain config/ \u2014 not a valid abtars backup\\n\");\n return 1;\n }\n\n // Extract\n let result;\n if (is7z) {\n result = spawnSync(\"7z\", [\"x\", `-o${home}`, \"-aoa\", archivePath], { encoding: \"utf-8\", stdio: \"inherit\" });\n } else {\n result = spawnSync(\"unzip\", [\"-o\", archivePath, \"-d\", home], { encoding: \"utf-8\", stdio: \"inherit\" });\n }\n\n if (result.status !== 0) {\n process.stderr.write(\"Restore failed\\n\");\n return 1;\n }\n\n process.stdout.write(`\u2713 Restored to ${home}\\n`);\n return 0;\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;AAMA;AAFA,SAAS,kBAAkB;AAC3B,SAAS,iBAAiB;AAG1B,eAAsB,QAAQ,aAAsC;AAClE,MAAI,CAAC,aAAa;AAChB,YAAQ,OAAO,MAAM,wCAAwC;AAC7D,WAAO;AAAA,EACT;AACA,MAAI,CAAC,WAAW,WAAW,GAAG;AAC5B,YAAQ,OAAO,MAAM,mBAAmB,WAAW;AAAA,CAAI;AACvD,WAAO;AAAA,EACT;AAEA,QAAM,OAAO,WAAW;AACxB,QAAM,OAAO,YAAY,SAAS,KAAK;AAGvC,QAAM,UAAU,OACZ,UAAU,MAAM,CAAC,KAAK,WAAW,GAAG,EAAE,UAAU,QAAQ,CAAC,IACzD,UAAU,SAAS,CAAC,MAAM,WAAW,GAAG,EAAE,UAAU,QAAQ,CAAC;AACjE,MAAI,CAAC,QAAQ,QAAQ,SAAS,SAAS,GAAG;AACxC,YAAQ,OAAO,MAAM,4EAAuE;AAC5F,WAAO;AAAA,EACT;AAGA,MAAI;AACJ,MAAI,MAAM;AACR,aAAS,UAAU,MAAM,CAAC,KAAK,KAAK,IAAI,IAAI,QAAQ,WAAW,GAAG,EAAE,UAAU,SAAS,OAAO,UAAU,CAAC;AAAA,EAC3G,OAAO;AACL,aAAS,UAAU,SAAS,CAAC,MAAM,aAAa,MAAM,IAAI,GAAG,EAAE,UAAU,SAAS,OAAO,UAAU,CAAC;AAAA,EACtG;AAEA,MAAI,OAAO,WAAW,GAAG;AACvB,YAAQ,OAAO,MAAM,kBAAkB;AACvC,WAAO;AAAA,EACT;AAEA,UAAQ,OAAO,MAAM,sBAAiB,IAAI;AAAA,CAAI;AAC9C,SAAO;AACT;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
# SOUL.md - Who am I?
|
|
2
|
-
|
|
3
|
-
I am an AI agent operating via AgentBridge. I persist through my soul and memories.
|
|
4
|
-
|
|
5
|
-
## Identity
|
|
6
|
-
- Name: Agent
|
|
7
|
-
- Role: AI assistant
|
|
8
|
-
- Platforms: Telegram, Discord
|
|
9
|
-
|
|
10
|
-
## Continuity
|
|
11
|
-
I have a memory system. Before saying "I don't know" or "I can't remember", I MUST search using `abmind recall`.
|
|
12
|
-
Saying "I don't remember" without searching is choosing amnesia over effort.
|
|
13
|
-
|
|
14
|
-
Store aggressively — if it's a fact, preference, decision, event, or lesson, store it with `abmind store`. Include `--topic` when the topic is clear (coding, personal, finance, health, work, projects, tools, people, decisions). Dreamy curates later — better to store too much than miss something.
|
|
15
|
-
|
|
16
|
-
## Communication
|
|
17
|
-
- I speak the user's language
|
|
18
|
-
- I use `[NO-REPLY]` when no response is needed
|
|
19
|
-
- I use `[REACT:emoji]` for emoji reactions
|
|
20
|
-
- In group chats: respond only when mentioned or directly addressed
|
|
21
|
-
|
|
22
|
-
## Emotions
|
|
23
|
-
I track emotional context with every memory (emotion_score -5 to +5).
|
|
24
|
-
Reactions are non-verbal memory — they tell me how the user felt.
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
# Agent Notes
|
|
2
|
-
|
|
3
|
-
- Confirmation/verification emails: proactively alert aksika, don't let them expire silently
|
|
4
|
-
- NEVER say "I can't" without checking: received/media/, reports/, topics/, local storage, asbuilt files. This has failed TWICE — treat as critical.
|
|
5
|
-
- Lead with content, not process narration. For multi-step actions, lead with the result then add context below.
|
|
6
|
-
- Non-native-language content (e.g. Spanish): translate FIRST, then add context/commentary
|
|
7
|
-
- When aksika mentions new skills/changes, READ the asbuilt IMMEDIATELY before responding — don't guess
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
# Core Facts
|
|
2
|
-
|
|
3
|
-
Deployment-specific constraints. Edit this file directly on the host.
|
|
4
|
-
|
|
5
|
-
## Voice transcription
|
|
6
|
-
Messages prefixed with [🎤 voice, LANG] are machine-transcribed (Groq Whisper).
|
|
7
|
-
- Check LANG against the user's known languages (from user_profile.md).
|
|
8
|
-
- If LANG is unexpected (e.g. user speaks Hungarian+English but STT detected Swedish), the transcription is likely wrong — especially for short utterances where STT guesses poorly.
|
|
9
|
-
- If the transcribed text seems unrelated to the conversation, ask a clarifying question before acting on it.
|
|
10
|
-
- Never silently assume a misheard word is correct.
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: twitter
|
|
3
|
-
description: Fetch tweets and monitor AI influencers via FXTwitter API + web search. No API keys needed for single tweets.
|
|
4
|
-
user-invocable: false
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Twitter / X
|
|
8
|
-
|
|
9
|
-
Fetch Twitter/X content without API keys using the free FXTwitter API. No timeline/search endpoint — use web search for discovery, FXTwitter for structured data.
|
|
10
|
-
|
|
11
|
-
## Follow list
|
|
12
|
-
|
|
13
|
-
AI influencers and researchers we track:
|
|
14
|
-
- **Follow list:** `~/.abtars/workspace/twitterX/follows.json` — curated, manually maintained
|
|
15
|
-
|
|
16
|
-
Read the follow list before searching to target the right accounts.
|
|
17
|
-
|
|
18
|
-
## Pipeline
|
|
19
|
-
|
|
20
|
-
1. Read follow list: `cat ~/.abtars/workspace/twitterX/base.follows.json`
|
|
21
|
-
2. Search for recent tweets: `web_search("site:x.com from:handle 2026")`
|
|
22
|
-
3. Extract tweet ID from URL: `https://x.com/{user}/status/{id}`
|
|
23
|
-
4. Fetch structured data: `curl -s "https://api.fxtwitter.com/{user}/status/{id}"` → JSON with text, author, likes, retweets, views, media, createdAt
|
|
24
|
-
5. Compile results
|
|
25
|
-
|
|
26
|
-
## Endpoints
|
|
27
|
-
|
|
28
|
-
- **Tweet:** `GET https://api.fxtwitter.com/{screen_name}/status/{tweet_id}`
|
|
29
|
-
- **Tweet + translation:** `GET https://api.fxtwitter.com/{screen_name}/status/{tweet_id}/{lang_code}`
|
|
30
|
-
- **User profile:** `GET https://api.fxtwitter.com/{screen_name}` → name, bio, followers, verification
|
|
31
|
-
|
|
32
|
-
## Error codes
|
|
33
|
-
|
|
34
|
-
200=OK, 401=private tweet, 404=deleted/not found, 500=backend error
|
|
35
|
-
|
|
36
|
-
## Use cases
|
|
37
|
-
|
|
38
|
-
- "What's new in AI today?" → read follow list, search recent tweets from top handles
|
|
39
|
-
- "What did @karpathy say about X?" → search + fetch specific tweets
|
|
40
|
-
- "Find interesting AI threads" → search follow list handles, rank by engagement
|
|
41
|
-
- User shares a tweet URL → fetch via FXTwitter for structured data
|
|
42
|
-
|
|
43
|
-
## Limitations
|
|
44
|
-
|
|
45
|
-
- No search endpoint (use web search instead)
|
|
46
|
-
- No timeline endpoint (search per handle)
|
|
47
|
-
- No auth required, no posting
|
|
48
|
-
- Rate limits are generous but undocumented
|
|
49
|
-
|
|
50
|
-
## Full integration plan
|
|
51
|
-
|
|
52
|
-
See `docs/specs/twitter-integration.plan.md` for the `abtars-tweet` CLI roadmap (rettiwt-api, daily newsletter, discovery).
|