pikiloom 0.4.26 → 0.4.28
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/dashboard/dist/assets/{AgentTab-BNJ73P0m.js → AgentTab-DtjuI8NU.js} +1 -1
- package/dashboard/dist/assets/ConnectionModal-BIx6a5o1.js +1 -0
- package/dashboard/dist/assets/{DirBrowser-D-GT-G-w.js → DirBrowser-BINh_7NB.js} +1 -1
- package/dashboard/dist/assets/{ExtensionsTab-BrJMulJR.js → ExtensionsTab-CHpd147b.js} +1 -1
- package/dashboard/dist/assets/{IMAccessTab-auPnx468.js → IMAccessTab-C4DbGxbG.js} +1 -1
- package/dashboard/dist/assets/{Modal-lYfO9m-I.js → Modal-D_PUi-5O.js} +1 -1
- package/dashboard/dist/assets/{Modals-PRdWOIY9.js → Modals-BahDEMqf.js} +1 -1
- package/dashboard/dist/assets/{Select-BhbU-rll.js → Select-Cp5VXb_2.js} +1 -1
- package/dashboard/dist/assets/SessionPanel-DZVNXptx.js +1 -0
- package/dashboard/dist/assets/SystemTab-CLznS31f.js +1 -0
- package/dashboard/dist/assets/index-CLGovkkw.js +3 -0
- package/dashboard/dist/assets/index-CXIN3nTr.css +1 -0
- package/dashboard/dist/assets/index-DcYOI6rL.js +23 -0
- package/dashboard/dist/assets/{shared-C5uXvhZJ.js → shared-C8DCTJ44.js} +1 -1
- package/dashboard/dist/index.html +2 -2
- package/dist/agent/acp-client.js +0 -59
- package/dist/agent/artifacts.js +1 -38
- package/dist/agent/auto-update.js +2 -78
- package/dist/agent/await-resume.js +0 -20
- package/dist/agent/cli/auth.js +3 -51
- package/dist/agent/cli/catalog.js +0 -3
- package/dist/agent/cli/detector.js +1 -19
- package/dist/agent/cli/index.js +0 -3
- package/dist/agent/cli/registry.js +0 -25
- package/dist/agent/driver.js +0 -14
- package/dist/agent/drivers/claude-tui.js +2 -769
- package/dist/agent/drivers/claude.js +12 -631
- package/dist/agent/drivers/codex.js +11 -162
- package/dist/agent/drivers/gemini.js +6 -114
- package/dist/agent/drivers/hermes.js +0 -128
- package/dist/agent/goal.js +0 -45
- package/dist/agent/handover.js +1 -46
- package/dist/agent/images.js +7 -133
- package/dist/agent/index.js +1 -29
- package/dist/agent/mcp/bridge.js +1 -197
- package/dist/agent/mcp/extensions.js +5 -84
- package/dist/agent/mcp/oauth.js +2 -69
- package/dist/agent/mcp/registry.js +2 -33
- package/dist/agent/mcp/session-server.js +2 -45
- package/dist/agent/mcp/tools/ask-user.js +0 -6
- package/dist/agent/mcp/tools/await-resume.js +0 -15
- package/dist/agent/mcp/tools/goal.js +0 -25
- package/dist/agent/mcp/tools/types.js +0 -5
- package/dist/agent/mcp/tools/workspace.js +0 -23
- package/dist/agent/npm.js +0 -14
- package/dist/agent/session.js +86 -196
- package/dist/agent/skill-installer.js +3 -70
- package/dist/agent/skills.js +0 -43
- package/dist/agent/stream.js +5 -148
- package/dist/agent/types.js +0 -12
- package/dist/agent/utils.js +2 -120
- package/dist/bot/bot.js +3 -468
- package/dist/bot/command-ui.js +0 -45
- package/dist/bot/commands.js +0 -51
- package/dist/bot/headless-bot.js +0 -21
- package/dist/bot/host.js +1 -5
- package/dist/bot/human-loop.js +0 -8
- package/dist/bot/menu.js +0 -3
- package/dist/bot/orchestration.js +0 -3
- package/dist/bot/render-shared.js +0 -63
- package/dist/bot/session-hub.js +14 -92
- package/dist/bot/session-status.js +1 -5
- package/dist/bot/streaming.js +0 -53
- package/dist/browser-profile.js +0 -51
- package/dist/browser-supervisor.js +0 -75
- package/dist/catalog/cli-tools.js +0 -53
- package/dist/catalog/index.js +0 -18
- package/dist/catalog/local-models.js +0 -18
- package/dist/catalog/mcp-servers.js +0 -35
- package/dist/catalog/skill-repos.js +0 -32
- package/dist/channels/base.js +0 -19
- package/dist/channels/dingtalk/bot.js +0 -3
- package/dist/channels/dingtalk/channel.js +0 -33
- package/dist/channels/discord/bot.js +0 -7
- package/dist/channels/discord/channel.js +0 -22
- package/dist/channels/feishu/bot.js +0 -71
- package/dist/channels/feishu/channel.js +0 -78
- package/dist/channels/feishu/markdown.js +0 -5
- package/dist/channels/feishu/render.js +0 -45
- package/dist/channels/health.js +0 -46
- package/dist/channels/slack/bot.js +0 -7
- package/dist/channels/slack/channel.js +1 -27
- package/dist/channels/states.js +0 -3
- package/dist/channels/telegram/bot.js +0 -60
- package/dist/channels/telegram/channel.js +2 -99
- package/dist/channels/telegram/directory.js +0 -3
- package/dist/channels/telegram/live-preview.js +0 -8
- package/dist/channels/telegram/render.js +0 -30
- package/dist/channels/wecom/bot.js +0 -3
- package/dist/channels/wecom/channel.js +0 -58
- package/dist/channels/weixin/api.js +0 -9
- package/dist/channels/weixin/bot.js +0 -88
- package/dist/channels/weixin/channel.js +0 -6
- package/dist/cli/autostart.js +1 -44
- package/dist/cli/channel-supervisor.js +1 -59
- package/dist/cli/channels.js +0 -6
- package/dist/cli/main.js +1 -145
- package/dist/cli/onboarding.js +0 -6
- package/dist/cli/run.js +0 -11
- package/dist/cli/setup-wizard.js +0 -3
- package/dist/core/config/runtime-config.js +0 -38
- package/dist/core/config/user-config.js +0 -63
- package/dist/core/config/validation.js +0 -9
- package/dist/core/constants.js +0 -268
- package/dist/core/git.js +0 -31
- package/dist/core/legacy-compat.js +1 -28
- package/dist/core/logging.js +0 -3
- package/dist/core/platform.js +0 -28
- package/dist/core/process-control.js +0 -8
- package/dist/core/secrets/index.js +0 -17
- package/dist/core/secrets/inline-seal.js +0 -7
- package/dist/core/secrets/ref.js +0 -19
- package/dist/core/secrets/resolver.js +0 -8
- package/dist/core/secrets/store.js +1 -12
- package/dist/core/utils.js +1 -37
- package/dist/core/version.js +0 -3
- package/dist/dashboard/platform.js +1 -35
- package/dist/dashboard/routes/agents.js +2 -71
- package/dist/dashboard/routes/cli.js +0 -27
- package/dist/dashboard/routes/config.js +0 -54
- package/dist/dashboard/routes/extensions.js +0 -132
- package/dist/dashboard/routes/local-models.js +0 -68
- package/dist/dashboard/routes/models.js +0 -46
- package/dist/dashboard/routes/sessions.js +0 -100
- package/dist/dashboard/runtime.js +1 -58
- package/dist/dashboard/server.js +0 -49
- package/dist/dashboard/session-control.js +2 -69
- package/dist/model/catalog.js +1 -17
- package/dist/model/index.js +0 -15
- package/dist/model/injector.js +1 -181
- package/dist/model/provider-models.js +1 -48
- package/dist/model/responses-bridge.js +3 -45
- package/dist/model/store.js +0 -17
- package/dist/model/types.js +0 -12
- package/dist/model/validation.js +1 -18
- package/dist/pikichannel/adapter-pikiloom.js +0 -31
- package/dist/pikichannel/code.js +0 -12
- package/dist/pikichannel/codec.js +0 -14
- package/dist/pikichannel/host.js +4 -33
- package/dist/pikichannel/protocol.js +0 -52
- package/dist/pikichannel/rendezvous-broker.js +5 -26
- package/dist/pikichannel/rendezvous-host.js +3 -16
- package/dist/pikichannel/server.js +6 -55
- package/dist/pikichannel/transport.js +0 -25
- package/dist/pikichannel/transports/webrtc-host.js +0 -18
- package/dist/pikichannel/transports/webrtc-shared.js +4 -34
- package/dist/pikichannel/transports/websocket-host.js +1 -14
- package/dist/pikichannel/turn.js +2 -69
- package/dist/pikichannel/web/sdk.js +14 -71
- package/package.json +1 -1
- package/dashboard/dist/assets/ConnectionModal-Dt05H2f9.js +0 -1
- package/dashboard/dist/assets/SessionPanel-_0dLWvEb.js +0 -1
- package/dashboard/dist/assets/SystemTab-CN_QrRKt.js +0 -1
- package/dashboard/dist/assets/index-CP3LYJR2.js +0 -23
- package/dashboard/dist/assets/index-C_neLnaO.js +0 -3
- package/dashboard/dist/assets/index-CyCspgtc.css +0 -1
package/dist/cli/main.js
CHANGED
|
@@ -1,14 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* cli.ts — CLI entry point for pikiloom.
|
|
4
|
-
*/
|
|
5
|
-
// Mark this process as a Claude Code context so nested claude launches are blocked.
|
|
6
|
-
// The spawn framework in code-agent.ts strips this before launching agent subprocesses.
|
|
7
2
|
process.env.CLAUDECODE = '1';
|
|
8
3
|
import { hydrateLegacyEnv, migrateLegacyStateDir } from '../core/legacy-compat.js';
|
|
9
|
-
// Backward-compat for the pikiclaw → pikiloom rename. Runs before any config is
|
|
10
|
-
// read or lock taken: mirror PIKICLAW_* → PIKILOOM_* and move ~/.pikiclaw →
|
|
11
|
-
// ~/.pikiloom. Both are idempotent no-ops once an install has migrated.
|
|
12
4
|
hydrateLegacyEnv();
|
|
13
5
|
migrateLegacyStateDir();
|
|
14
6
|
import { spawn } from 'node:child_process';
|
|
@@ -27,7 +19,6 @@ import { runSetupWizard } from './setup-wizard.js';
|
|
|
27
19
|
import { FROM_LAUNCHD_ENV, maybePromptAutostart } from './autostart.js';
|
|
28
20
|
import { applyUserConfig, loadUserConfig, startUserConfigSync, updateUserConfig, } from '../core/config/user-config.js';
|
|
29
21
|
import { VERSION } from '../core/version.js';
|
|
30
|
-
/* ── Daemon (watchdog) mode ─────────────────────────────────────────── */
|
|
31
22
|
const DAEMON_RESTART_DELAY_MS = DAEMON_TIMEOUTS.restartDelay;
|
|
32
23
|
const DAEMON_MAX_RESTART_DELAY_MS = DAEMON_TIMEOUTS.maxRestartDelay;
|
|
33
24
|
const DAEMON_RAPID_CRASH_WINDOW_MS = DAEMON_TIMEOUTS.rapidCrashWindow;
|
|
@@ -35,28 +26,13 @@ function daemonLog(msg) {
|
|
|
35
26
|
const ts = new Date().toTimeString().slice(0, 8);
|
|
36
27
|
process.stdout.write(`[daemon ${ts}] ${msg}\n`);
|
|
37
28
|
}
|
|
38
|
-
/** Args that are daemon-specific and should not be forwarded to the child. */
|
|
39
29
|
const DAEMON_STRIP_ARGS = new Set(['--daemon', '--no-daemon']);
|
|
40
|
-
/**
|
|
41
|
-
* Runs the bot as a supervised child process. On non-zero exit the child is
|
|
42
|
-
* restarted with exponential back-off. A clean exit (code 0) stops the daemon.
|
|
43
|
-
* Restart requests use a dedicated exit code and are respawned immediately.
|
|
44
|
-
*/
|
|
45
30
|
async function runDaemon(userArgs) {
|
|
46
|
-
// Forward user's CLI args (strip daemon-related flags).
|
|
47
31
|
const forwardedArgs = userArgs.filter(a => !DAEMON_STRIP_ARGS.has(a));
|
|
48
32
|
const restartCmd = process.env.PIKILOOM_RESTART_CMD;
|
|
49
33
|
const restartStateFile = createRestartStateFilePath(process.pid);
|
|
50
|
-
// Publish the daemon PID so `pikiloom stop` can find it. Clean up on any
|
|
51
|
-
// exit path so a stale file never points at someone else's PID.
|
|
52
34
|
writeDaemonPidFile(process.pid);
|
|
53
35
|
process.once('exit', clearDaemonPidFile);
|
|
54
|
-
// Auto-start enrollment: only when the user explicitly typed `--daemon`
|
|
55
|
-
// (the watchdog itself is on by default, so we use the explicit flag as
|
|
56
|
-
// the signal of "I'm settling in for long-term use"). Fire-and-forget so
|
|
57
|
-
// the bot still comes up immediately; the dialog appears a few seconds
|
|
58
|
-
// later. No-op when already enrolled, declined, non-interactive, or
|
|
59
|
-
// already running under launchd — see src/cli/autostart.ts.
|
|
60
36
|
if (userArgs.includes('--daemon')) {
|
|
61
37
|
maybePromptAutostart(daemonLog);
|
|
62
38
|
}
|
|
@@ -67,7 +43,6 @@ async function runDaemon(userArgs) {
|
|
|
67
43
|
clearRestartStateFile(restartStateFile);
|
|
68
44
|
const { bin, args } = buildRestartCommand(forwardedArgs, restartCmd);
|
|
69
45
|
daemonLog(`exec: ${bin} ${args.join(' ')}`);
|
|
70
|
-
// npx/npx.cmd needs shell resolution; node.exe does not
|
|
71
46
|
const needsShell = process.platform === 'win32' && !bin.endsWith('node.exe');
|
|
72
47
|
return spawn(needsShell ? `"${bin}"` : bin, args, {
|
|
73
48
|
stdio: 'inherit',
|
|
@@ -90,7 +65,6 @@ async function runDaemon(userArgs) {
|
|
|
90
65
|
daemonLog(`child running (pid=${child.pid})`);
|
|
91
66
|
const startedAt = Date.now();
|
|
92
67
|
let shutdownSignal = null;
|
|
93
|
-
// Forward termination and restart signals to the active child.
|
|
94
68
|
const forwardShutdownSignal = (sig) => {
|
|
95
69
|
shutdownSignal = sig;
|
|
96
70
|
child.kill(sig);
|
|
@@ -118,15 +92,13 @@ async function runDaemon(userArgs) {
|
|
|
118
92
|
daemonLog('child requested restart, respawning immediately');
|
|
119
93
|
continue;
|
|
120
94
|
}
|
|
121
|
-
// Clean exit → stop daemon.
|
|
122
95
|
if (code === 0 || code === null) {
|
|
123
96
|
daemonLog(`child exited cleanly (code=${code}), daemon stopping`);
|
|
124
97
|
process.exit(0);
|
|
125
98
|
}
|
|
126
|
-
// Exponential back-off for rapid crashes.
|
|
127
99
|
const uptime = Date.now() - startedAt;
|
|
128
100
|
if (uptime > DAEMON_RAPID_CRASH_WINDOW_MS) {
|
|
129
|
-
restartDelay = DAEMON_RESTART_DELAY_MS;
|
|
101
|
+
restartDelay = DAEMON_RESTART_DELAY_MS;
|
|
130
102
|
}
|
|
131
103
|
else {
|
|
132
104
|
restartDelay = Math.min(restartDelay * 2, DAEMON_MAX_RESTART_DELAY_MS);
|
|
@@ -215,15 +187,12 @@ function parseArgs(argv) {
|
|
|
215
187
|
}
|
|
216
188
|
return args;
|
|
217
189
|
}
|
|
218
|
-
/* ── Shared helpers ────────────────────────────────────────────────── */
|
|
219
190
|
function processLog(message) {
|
|
220
191
|
const ts = new Date().toTimeString().slice(0, 8);
|
|
221
192
|
process.stdout.write(`[pikiloom ${ts}] ${message}\n`);
|
|
222
193
|
}
|
|
223
194
|
const listStartupAgents = () => listAgents().agents;
|
|
224
195
|
const listVerboseAgents = () => listAgents({ includeVersion: true }).agents;
|
|
225
|
-
/* ── Phase: early exits (MCP serve, --version, --help) ────────────── */
|
|
226
|
-
/** If launched as an MCP stdio server, run that and exit. */
|
|
227
196
|
async function handleMcpServeMode() {
|
|
228
197
|
if (process.argv.includes('--mcp-serve')) {
|
|
229
198
|
await import('../agent/mcp/session-server.js');
|
|
@@ -231,7 +200,6 @@ async function handleMcpServeMode() {
|
|
|
231
200
|
}
|
|
232
201
|
return false;
|
|
233
202
|
}
|
|
234
|
-
/** Print help text and exit. */
|
|
235
203
|
function printHelp() {
|
|
236
204
|
process.stdout.write(`pikiloom v${VERSION} — Run local coding agents through IM.
|
|
237
205
|
|
|
@@ -321,28 +289,11 @@ Docs: https://github.com/xiaotonng/pikiloom
|
|
|
321
289
|
`);
|
|
322
290
|
process.exit(0);
|
|
323
291
|
}
|
|
324
|
-
/* ── Phase: workdir persistence & daemon handoff ──────────────────── */
|
|
325
|
-
/**
|
|
326
|
-
* For a fresh CLI launch (not a daemon-managed child), persist the working
|
|
327
|
-
* directory into setting.json so the bot defaults to where the user invoked
|
|
328
|
-
* the command. An explicit `-w` always wins and is saved. Without `-w`, a
|
|
329
|
-
* previously saved workdir (including one chosen later in the dashboard) is
|
|
330
|
-
* kept as-is; we only fall back to the launch cwd on first run, when nothing
|
|
331
|
-
* has been saved yet, so relaunching never silently reverts the workdir.
|
|
332
|
-
*/
|
|
333
292
|
function persistWorkdir(args, userConfig) {
|
|
334
293
|
if (process.env.PIKILOOM_DAEMON_CHILD)
|
|
335
294
|
return userConfig;
|
|
336
|
-
// launchd launches the process from `/`; without `-w`, that would clobber
|
|
337
|
-
// the user's saved workdir to "/". Skip persistence so the config stays
|
|
338
|
-
// whatever the user previously chose interactively.
|
|
339
295
|
if (process.env[FROM_LAUNCHD_ENV])
|
|
340
296
|
return userConfig;
|
|
341
|
-
// An explicit `-w` is a deliberate choice and always wins. Without it, a
|
|
342
|
-
// workdir the user already saved (e.g. picked in the dashboard via
|
|
343
|
-
// `/api/switch-workdir`) must survive relaunch — otherwise every fresh
|
|
344
|
-
// `npx pikiloom` silently reverts the workdir to its launch cwd. Only fall
|
|
345
|
-
// back to cwd on first run, when nothing has been saved yet.
|
|
346
297
|
const explicitWorkdir = typeof args.workdir === 'string' && args.workdir.trim()
|
|
347
298
|
? args.workdir.trim()
|
|
348
299
|
: '';
|
|
@@ -354,21 +305,14 @@ function persistWorkdir(args, userConfig) {
|
|
|
354
305
|
updateUserConfig({ workdir: nextWorkdir });
|
|
355
306
|
return loadUserConfig();
|
|
356
307
|
}
|
|
357
|
-
/**
|
|
358
|
-
* If daemon mode is active and we are the top-level process, become the
|
|
359
|
-
* watchdog. This function never returns in daemon mode.
|
|
360
|
-
*/
|
|
361
308
|
async function enterDaemonIfNeeded(args) {
|
|
362
309
|
if (args.daemon && !process.env.PIKILOOM_DAEMON_CHILD) {
|
|
363
310
|
await runDaemon(process.argv.slice(2));
|
|
364
311
|
}
|
|
365
312
|
if (!args.daemon) {
|
|
366
|
-
// --no-daemon: clear inherited env so requestProcessRestart uses the
|
|
367
|
-
// direct-spawn path instead of handing off to a non-existent daemon.
|
|
368
313
|
delete process.env.PIKILOOM_DAEMON_CHILD;
|
|
369
314
|
}
|
|
370
315
|
}
|
|
371
|
-
/** Install SIGUSR2 restart handler and clean it up on exit. */
|
|
372
316
|
function installRestartSignalHandler() {
|
|
373
317
|
const onSigusr2 = () => {
|
|
374
318
|
processLog('SIGUSR2 received, restarting...');
|
|
@@ -379,21 +323,12 @@ function installRestartSignalHandler() {
|
|
|
379
323
|
process.off('SIGUSR2', onSigusr2);
|
|
380
324
|
});
|
|
381
325
|
}
|
|
382
|
-
/**
|
|
383
|
-
* Top-level shutdown safety net. Channels install their own SIGINT/SIGTERM
|
|
384
|
-
* handlers that do per-channel cleanup with a 3 s unref-ed force-exit timer,
|
|
385
|
-
* but those handlers only exist while a channel is running and silently fail
|
|
386
|
-
* if cleanup throws before the timer is set. This handler is the last-resort
|
|
387
|
-
* guarantee: once the user hits Ctrl+C, the process exits within the grace
|
|
388
|
-
* window no matter what state we're in.
|
|
389
|
-
*/
|
|
390
326
|
function installTopLevelShutdownHandler() {
|
|
391
327
|
const GRACE_MS = 5_000;
|
|
392
328
|
let shuttingDown = false;
|
|
393
329
|
const onSignal = (sig) => {
|
|
394
330
|
const exitCode = sig === 'SIGINT' ? 130 : 143;
|
|
395
331
|
if (shuttingDown) {
|
|
396
|
-
// Second Ctrl+C — bail immediately.
|
|
397
332
|
processLog(`${sig} again, forcing immediate exit`);
|
|
398
333
|
process.exit(exitCode);
|
|
399
334
|
}
|
|
@@ -404,12 +339,6 @@ function installTopLevelShutdownHandler() {
|
|
|
404
339
|
process.on('SIGINT', () => onSignal('SIGINT'));
|
|
405
340
|
process.on('SIGTERM', () => onSignal('SIGTERM'));
|
|
406
341
|
}
|
|
407
|
-
/* ── Phase: stop subcommand ───────────────────────────────────────── */
|
|
408
|
-
/**
|
|
409
|
-
* Find and terminate the running daemon. Reads the PID file written by
|
|
410
|
-
* `runDaemon`, sends SIGTERM, waits briefly, escalates to SIGKILL if the
|
|
411
|
-
* process is still alive. Never returns — always exits.
|
|
412
|
-
*/
|
|
413
342
|
async function handleStopCommand() {
|
|
414
343
|
const pid = readDaemonPidFile();
|
|
415
344
|
if (!pid) {
|
|
@@ -435,7 +364,6 @@ async function handleStopCommand() {
|
|
|
435
364
|
process.exit(1);
|
|
436
365
|
}
|
|
437
366
|
process.stdout.write(`pikiloom stop: SIGTERM → pid ${pid}\n`);
|
|
438
|
-
// Poll for up to 8 s; daemon's child needs ~3 s for its force-exit timer.
|
|
439
367
|
const deadline = Date.now() + 8_000;
|
|
440
368
|
while (Date.now() < deadline) {
|
|
441
369
|
if (!isProcessAlive(pid)) {
|
|
@@ -445,7 +373,6 @@ async function handleStopCommand() {
|
|
|
445
373
|
}
|
|
446
374
|
await new Promise(resolve => setTimeout(resolve, 250));
|
|
447
375
|
}
|
|
448
|
-
// Escalate to SIGKILL.
|
|
449
376
|
process.stderr.write(`pikiloom stop: daemon (pid ${pid}) still alive after 8s, sending SIGKILL\n`);
|
|
450
377
|
try {
|
|
451
378
|
process.kill(pid, 'SIGKILL');
|
|
@@ -454,8 +381,6 @@ async function handleStopCommand() {
|
|
|
454
381
|
clearDaemonPidFile();
|
|
455
382
|
process.exit(0);
|
|
456
383
|
}
|
|
457
|
-
/* ── Phase: doctor check ──────────────────────────────────────────── */
|
|
458
|
-
/** Run setup diagnostics and exit (--doctor). */
|
|
459
384
|
function runDoctorCheck(channel, tokenProvided) {
|
|
460
385
|
const setupState = collectSetupState({
|
|
461
386
|
agents: listVerboseAgents(),
|
|
@@ -470,11 +395,6 @@ function runDoctorCheck(channel, tokenProvided) {
|
|
|
470
395
|
process.stderr.write(guide);
|
|
471
396
|
process.exit(ready ? 0 : 1);
|
|
472
397
|
}
|
|
473
|
-
/* ── Phase: setup (dashboard / wizard / guide) ────────────────────── */
|
|
474
|
-
/**
|
|
475
|
-
* Poll the dashboard until the user completes configuration.
|
|
476
|
-
* Mutates `ctx` in place with freshly resolved channels.
|
|
477
|
-
*/
|
|
478
398
|
async function awaitDashboardConfig(dashboard, ctx) {
|
|
479
399
|
const ts = new Date().toTimeString().slice(0, 8);
|
|
480
400
|
process.stdout.write(`[pikiloom ${ts}] waiting for configuration via dashboard...\n`);
|
|
@@ -493,9 +413,6 @@ async function awaitDashboardConfig(dashboard, ctx) {
|
|
|
493
413
|
channel,
|
|
494
414
|
tokenProvided: channels.length > 0 && hasConfiguredChannelToken({ ...ctx.userConfig, ...ctx.configOverrides }, channel, ctx.args.token),
|
|
495
415
|
});
|
|
496
|
-
// Dashboard-as-terminal: an installed agent is the only prerequisite to
|
|
497
|
-
// start. IM channels are optional — the dashboard is itself a terminal, so
|
|
498
|
-
// don't block startup waiting for a channel token.
|
|
499
416
|
const nextNeedsSetup = !hasReadyAgent(nextSetupState);
|
|
500
417
|
if (!nextNeedsSetup) {
|
|
501
418
|
const resumeTs = new Date().toTimeString().slice(0, 8);
|
|
@@ -504,10 +421,6 @@ async function awaitDashboardConfig(dashboard, ctx) {
|
|
|
504
421
|
}
|
|
505
422
|
}
|
|
506
423
|
}
|
|
507
|
-
/**
|
|
508
|
-
* Run the setup phase: dashboard wait-loop, terminal wizard, or guide printout.
|
|
509
|
-
* Returns the dashboard instance (if started) and possibly-updated userConfig.
|
|
510
|
-
*/
|
|
511
424
|
async function runSetupPhase(args, userConfig, configOverrides, channels, channel, tokenProvided) {
|
|
512
425
|
const setupState = collectSetupState({
|
|
513
426
|
agents: listStartupAgents(),
|
|
@@ -516,16 +429,10 @@ async function runSetupPhase(args, userConfig, configOverrides, channels, channe
|
|
|
516
429
|
});
|
|
517
430
|
const useDashboard = !args.noDashboard && !args.setup;
|
|
518
431
|
let dashboard = null;
|
|
519
|
-
// With the dashboard as a terminal, an installed agent is enough to start.
|
|
520
|
-
// Without the dashboard (headless server / --no-dashboard), an IM channel is
|
|
521
|
-
// still required as the only terminal.
|
|
522
432
|
const needsSetup = useDashboard
|
|
523
433
|
? !hasReadyAgent(setupState)
|
|
524
434
|
: (channels.length === 0 || !tokenProvided || !hasReadyAgent(setupState));
|
|
525
435
|
if (useDashboard) {
|
|
526
|
-
// Suppress the browser pop on auto-start when there's no user-facing
|
|
527
|
-
// terminal: launchd-spawned bots, Docker/headless server runs, or when
|
|
528
|
-
// the user explicitly set PIKILOOM_OPEN_BROWSER=0.
|
|
529
436
|
const openBrowser = !args.server
|
|
530
437
|
&& !process.env[FROM_LAUNCHD_ENV]
|
|
531
438
|
&& !envBool('PIKILOOM_DOCKER', false)
|
|
@@ -568,7 +475,6 @@ async function runSetupPhase(args, userConfig, configOverrides, channels, channe
|
|
|
568
475
|
}
|
|
569
476
|
return { dashboard, userConfig, channels, channel };
|
|
570
477
|
}
|
|
571
|
-
/** Print the shareable connection code for `--server` (headless) mode. */
|
|
572
478
|
function printServerConnectionCode(dashboard) {
|
|
573
479
|
const c = loadUserConfig();
|
|
574
480
|
const sc = buildServerCode({
|
|
@@ -590,13 +496,6 @@ function printServerConnectionCode(dashboard) {
|
|
|
590
496
|
process.stdout.write(' paste it into a client → 连接 → 互联网/局域网.\n\n');
|
|
591
497
|
}
|
|
592
498
|
}
|
|
593
|
-
/* ── Phase: post-setup validation ─────────────────────────────────── */
|
|
594
|
-
/**
|
|
595
|
-
* Re-resolve channels after the setup phase and validate we have a runnable
|
|
596
|
-
* terminal: an installed agent is mandatory; an IM channel is required only
|
|
597
|
-
* when the dashboard isn't serving as the terminal. Exits on failure.
|
|
598
|
-
* Returns the (possibly empty) channel set — empty is valid in dashboard mode.
|
|
599
|
-
*/
|
|
600
499
|
function validatePostSetupChannels(configOverrides, userConfig, args, useDashboard) {
|
|
601
500
|
const effectiveConfig = { ...userConfig, ...configOverrides };
|
|
602
501
|
const channels = resolveConfiguredChannels({
|
|
@@ -609,28 +508,18 @@ function validatePostSetupChannels(configOverrides, userConfig, args, useDashboa
|
|
|
609
508
|
channel,
|
|
610
509
|
tokenProvided: channels.length > 0,
|
|
611
510
|
});
|
|
612
|
-
// An installed agent is the hard requirement — no terminal can run a session
|
|
613
|
-
// without one.
|
|
614
511
|
if (!hasReadyAgent(refreshedSetupState)) {
|
|
615
512
|
process.stderr.write(buildSetupGuide(refreshedSetupState, VERSION, { doctor: true }));
|
|
616
513
|
process.exit(1);
|
|
617
514
|
}
|
|
618
|
-
// Zero IM channels is fine when the dashboard is the terminal; only bail when
|
|
619
|
-
// there's no terminal at all (dashboard disabled AND no channel configured).
|
|
620
515
|
if (channels.length === 0 && !useDashboard) {
|
|
621
516
|
process.stdout.write(buildSetupGuide(refreshedSetupState, VERSION));
|
|
622
517
|
process.exit(0);
|
|
623
518
|
}
|
|
624
519
|
return { channels, channel };
|
|
625
520
|
}
|
|
626
|
-
/* ── Phase: runtime config & env setup ────────────────────────────── */
|
|
627
|
-
/**
|
|
628
|
-
* Build the final runtime config, apply token/model/permission overrides to
|
|
629
|
-
* the environment, start config file sync, and kick off agent auto-update.
|
|
630
|
-
*/
|
|
631
521
|
function applyRuntimeConfig(args, userConfig, configOverrides, channel) {
|
|
632
522
|
const runtimeConfig = { ...userConfig, ...configOverrides };
|
|
633
|
-
// Inject CLI token into channel-specific config fields.
|
|
634
523
|
if (args.token) {
|
|
635
524
|
if (channel === 'telegram')
|
|
636
525
|
runtimeConfig.telegramBotToken = args.token;
|
|
@@ -648,7 +537,6 @@ function applyRuntimeConfig(args, userConfig, configOverrides, channel) {
|
|
|
648
537
|
agents: listAgents({ includeVersion: true, refresh: true }).agents,
|
|
649
538
|
log: processLog,
|
|
650
539
|
});
|
|
651
|
-
// Model override: route to the correct agent env var.
|
|
652
540
|
if (args.model) {
|
|
653
541
|
const ag = args.agent || runtimeConfig.defaultAgent || 'codex';
|
|
654
542
|
if (ag === 'codex')
|
|
@@ -660,7 +548,6 @@ function applyRuntimeConfig(args, userConfig, configOverrides, channel) {
|
|
|
660
548
|
}
|
|
661
549
|
if (args.timeout != null)
|
|
662
550
|
process.env.PIKILOOM_TIMEOUT = String(args.timeout);
|
|
663
|
-
// Permission mode: safe vs full-access.
|
|
664
551
|
if (args.safeMode) {
|
|
665
552
|
process.env.CODEX_FULL_ACCESS = 'false';
|
|
666
553
|
process.env.CLAUDE_PERMISSION_MODE = 'default';
|
|
@@ -673,12 +560,6 @@ function applyRuntimeConfig(args, userConfig, configOverrides, channel) {
|
|
|
673
560
|
process.env.GEMINI_APPROVAL_MODE = 'yolo';
|
|
674
561
|
process.env.GEMINI_SANDBOX = 'false';
|
|
675
562
|
}
|
|
676
|
-
// Live-reload config file sync.
|
|
677
|
-
//
|
|
678
|
-
// Only pass overrides that came from CLI flags / explicit args, NOT the full
|
|
679
|
-
// runtimeConfig. Otherwise a snapshot of every user-managed field (model,
|
|
680
|
-
// effort, workdir, etc.) gets re-applied on every sync tick, silently
|
|
681
|
-
// reverting changes the user just made via the menu or dashboard.
|
|
682
563
|
const syncOverrides = {};
|
|
683
564
|
if (args.agent)
|
|
684
565
|
syncOverrides.defaultAgent = args.agent;
|
|
@@ -700,25 +581,12 @@ function applyRuntimeConfig(args, userConfig, configOverrides, channel) {
|
|
|
700
581
|
process.once('exit', stopUserConfigSync);
|
|
701
582
|
return runtimeConfig;
|
|
702
583
|
}
|
|
703
|
-
/* ── Phase: channel launch ────────────────────────────────────────── */
|
|
704
|
-
/**
|
|
705
|
-
* Hand off channel lifecycle to ChannelSupervisor and block forever. The
|
|
706
|
-
* supervisor reconciles bots against the user config — adding, removing,
|
|
707
|
-
* or replacing channels in response to dashboard saves without restarting
|
|
708
|
-
* the pikiloom process.
|
|
709
|
-
*
|
|
710
|
-
* Per-bot signal handlers (and the daemon supervisor when present) drive
|
|
711
|
-
* process exit; this promise is just a foreground keep-alive.
|
|
712
|
-
*/
|
|
713
584
|
async function launchChannels(channels, dashboard) {
|
|
714
585
|
processLog(`launching channels: ${channels.join(', ')}`);
|
|
715
586
|
const supervisor = new ChannelSupervisor({ dashboard, log: processLog });
|
|
716
587
|
await supervisor.start();
|
|
717
|
-
// Block forever — the dashboard HTTP listener and per-channel signal
|
|
718
|
-
// handlers keep the process alive and drive shutdown.
|
|
719
588
|
await new Promise(() => { });
|
|
720
589
|
}
|
|
721
|
-
/* ── main() ───────────────────────────────────────────────────────── */
|
|
722
590
|
export async function main() {
|
|
723
591
|
if (await handleMcpServeMode())
|
|
724
592
|
return;
|
|
@@ -732,37 +600,25 @@ export async function main() {
|
|
|
732
600
|
printHelp();
|
|
733
601
|
if (args.stop)
|
|
734
602
|
await handleStopCommand();
|
|
735
|
-
// Persist workdir for fresh (non-daemon-child) launches.
|
|
736
603
|
userConfig = persistWorkdir(args, userConfig);
|
|
737
|
-
// Daemon mode: become watchdog (never returns in daemon mode).
|
|
738
604
|
await enterDaemonIfNeeded(args);
|
|
739
|
-
// Child / no-daemon process: install restart signal handler + top-level
|
|
740
|
-
// shutdown safety net so Ctrl+C always brings the process down.
|
|
741
605
|
installRestartSignalHandler();
|
|
742
606
|
installTopLevelShutdownHandler();
|
|
743
|
-
// Apply config overrides from CLI args.
|
|
744
607
|
const configOverrides = {};
|
|
745
608
|
if (args.agent)
|
|
746
609
|
configOverrides.defaultAgent = args.agent;
|
|
747
610
|
applyUserConfig({ ...userConfig, ...configOverrides }, undefined, { overwrite: true, clearMissing: true });
|
|
748
|
-
// Resolve initial channels.
|
|
749
611
|
const effectiveConfig = () => ({ ...userConfig, ...configOverrides });
|
|
750
612
|
let channels = resolveConfiguredChannels({ config: effectiveConfig(), tokenOverride: args.token });
|
|
751
613
|
let channel = channels[0] || 'feishu';
|
|
752
614
|
const tokenProvided = channels.length > 0 && hasConfiguredChannelToken(effectiveConfig(), channel, args.token);
|
|
753
|
-
// Doctor mode: check and exit.
|
|
754
615
|
if (args.doctor)
|
|
755
616
|
runDoctorCheck(channel, tokenProvided);
|
|
756
|
-
// Setup phase: dashboard, wizard, or guide.
|
|
757
617
|
const useDashboard = !args.noDashboard && !args.setup;
|
|
758
618
|
let dashboard;
|
|
759
619
|
({ dashboard, userConfig, channels, channel } = await runSetupPhase(args, userConfig, configOverrides, channels, channel, tokenProvided));
|
|
760
|
-
// Validate the terminal is runnable after setup (channels may be empty when
|
|
761
|
-
// the dashboard is serving as the terminal).
|
|
762
620
|
({ channels, channel } = validatePostSetupChannels(configOverrides, userConfig, args, useDashboard));
|
|
763
|
-
// Apply runtime config, env overrides, and start config sync.
|
|
764
621
|
applyRuntimeConfig(args, userConfig, configOverrides, channel);
|
|
765
|
-
// Launch bot channel(s).
|
|
766
622
|
await launchChannels(channels, dashboard);
|
|
767
623
|
}
|
|
768
624
|
main().catch(err => { console.error(err); process.exit(1); });
|
package/dist/cli/onboarding.js
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Setup/doctor state assessment and messaging.
|
|
3
|
-
*/
|
|
4
1
|
import { getAgentInstallCommand, getAgentLabel } from '../agent/npm.js';
|
|
5
2
|
function enrichAgent(agent) {
|
|
6
3
|
const label = getAgentLabel(agent.agent);
|
|
@@ -118,9 +115,6 @@ export function hasInstalledAgent(state) {
|
|
|
118
115
|
return state.agents.some(agent => agent.installed);
|
|
119
116
|
}
|
|
120
117
|
export function isSetupReady(state) {
|
|
121
|
-
// The Web Dashboard is always an available terminal, so an installed agent is
|
|
122
|
-
// the only hard prerequisite. IM channels are optional add-ons — their
|
|
123
|
-
// readiness is surfaced separately (per-channel status), not gated here.
|
|
124
118
|
return hasReadyAgent(state);
|
|
125
119
|
}
|
|
126
120
|
export function buildSetupGuide(state, version, options) {
|
package/dist/cli/run.js
CHANGED
|
@@ -1,12 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* run.ts — Standalone CLI commands for pikiloom.
|
|
4
|
-
*
|
|
5
|
-
* Usage:
|
|
6
|
-
* npm run command -- status
|
|
7
|
-
* npm run command -- claude-models
|
|
8
|
-
* npm run command -- codex-models
|
|
9
|
-
*/
|
|
10
2
|
import { ensureGitignore, formatThinkingForDisplay, DEFAULT_RUN_TIMEOUT_S } from '../bot/bot.js';
|
|
11
3
|
import { initializeProjectSkills, listAgents, listModels, listSkills, getUsage, doStream } from '../agent/index.js';
|
|
12
4
|
import { querySessions, querySessionTail } from '../bot/session-hub.js';
|
|
@@ -57,7 +49,6 @@ function parseArgs(argv) {
|
|
|
57
49
|
}
|
|
58
50
|
}
|
|
59
51
|
args.command = positional[0] ?? null;
|
|
60
|
-
// If no -p flag, treat remaining positional args as the prompt
|
|
61
52
|
if (!args.prompt && positional.length > 1)
|
|
62
53
|
args.prompt = positional.slice(1).join(' ');
|
|
63
54
|
return args;
|
|
@@ -227,7 +218,6 @@ async function main() {
|
|
|
227
218
|
? 'gemini'
|
|
228
219
|
: 'claude';
|
|
229
220
|
let sessionId = args.session;
|
|
230
|
-
// Default: find the latest session
|
|
231
221
|
if (!sessionId) {
|
|
232
222
|
const sessions = await querySessions({ agent, workdir, limit: 1 });
|
|
233
223
|
if (!sessions.ok || !sessions.sessions.length) {
|
|
@@ -278,7 +268,6 @@ async function main() {
|
|
|
278
268
|
};
|
|
279
269
|
process.stdout.write(`Running ${agent}${args.model ? ` (model: ${args.model})` : ''}...\n`);
|
|
280
270
|
const result = await doStream(opts);
|
|
281
|
-
// Clear the streaming line and print final result
|
|
282
271
|
process.stdout.write('\r\x1b[K');
|
|
283
272
|
process.stdout.write(`--- ${agent} result ---\n`);
|
|
284
273
|
process.stdout.write(`ok: ${result.ok}\n`);
|
package/dist/cli/setup-wizard.js
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Runtime resolution of agent model and effort preferences.
|
|
3
|
-
*/
|
|
4
1
|
import { normalizeClaudeModelId } from '../../agent/index.js';
|
|
5
2
|
export const DEFAULT_AGENT_MODELS = {
|
|
6
3
|
claude: 'claude-opus-4-8',
|
|
@@ -17,7 +14,6 @@ export const DEFAULT_AGENT_EFFORTS = {
|
|
|
17
14
|
function trimmed(value) {
|
|
18
15
|
return typeof value === 'string' ? value.trim() : '';
|
|
19
16
|
}
|
|
20
|
-
/** Parse a boolean-ish string (env var / loose config). Empty → null (unset). */
|
|
21
17
|
function parseBoolish(value) {
|
|
22
18
|
const v = value.trim().toLowerCase();
|
|
23
19
|
if (!v)
|
|
@@ -85,17 +81,6 @@ export function resolveAgentEffort(config, agent) {
|
|
|
85
81
|
}
|
|
86
82
|
return null;
|
|
87
83
|
}
|
|
88
|
-
// ---------------------------------------------------------------------------
|
|
89
|
-
// Workflow (multi-agent orchestration) toggle
|
|
90
|
-
//
|
|
91
|
-
// Orthogonal to effort: effort tunes how deeply a *single* agent reasons;
|
|
92
|
-
// workflow grants the agent permission to author + run multi-agent Workflow
|
|
93
|
-
// orchestrations (fan-out / pipeline / verify). Only agents whose driver
|
|
94
|
-
// advertises `capabilities.workflow` honor it (claude today). Default OFF —
|
|
95
|
-
// the claude driver hard-disables the Workflow tool unless this is true, so a
|
|
96
|
-
// bare "workflow" keyword can never auto-spawn a fleet of sub-agents under the
|
|
97
|
-
// bypassPermissions mode pikiloom runs by default.
|
|
98
|
-
// ---------------------------------------------------------------------------
|
|
99
84
|
export function agentWorkflowEnv(agent, env = process.env) {
|
|
100
85
|
switch (agent) {
|
|
101
86
|
case 'claude': return trimmed(env.CLAUDE_WORKFLOW);
|
|
@@ -121,16 +106,7 @@ export function setAgentWorkflowEnv(agent, value, env = process.env) {
|
|
|
121
106
|
break;
|
|
122
107
|
}
|
|
123
108
|
}
|
|
124
|
-
// Default `api` = headless `claude -p`, the standard integration path. The
|
|
125
|
-
// interactive TUI ('subscription', Pro/Max quota) is opt-in via the dashboard
|
|
126
|
-
// 接入模式 toggle or PIKILOOM_CLAUDE_TUI=1.
|
|
127
109
|
export const DEFAULT_CLAUDE_ACCESS_MODE = 'api';
|
|
128
|
-
/**
|
|
129
|
-
* Access mode implied by env, or null when neither var has an opinion.
|
|
130
|
-
* PIKILOOM_CLAUDE_PRINT takes precedence over the legacy PIKILOOM_CLAUDE_TUI so
|
|
131
|
-
* a freshly-written print var resolves a stale tui var (setClaudeAccessModeEnv
|
|
132
|
-
* relies on this). Mirrors isClaudePrintModeForced() in the claude driver.
|
|
133
|
-
*/
|
|
134
110
|
export function claudeAccessModeEnv(env = process.env) {
|
|
135
111
|
const print = parseBoolish(trimmed(env.PIKILOOM_CLAUDE_PRINT));
|
|
136
112
|
if (print != null)
|
|
@@ -146,8 +122,6 @@ export function resolveClaudeAccessMode(config) {
|
|
|
146
122
|
return raw;
|
|
147
123
|
return claudeAccessModeEnv() ?? DEFAULT_CLAUDE_ACCESS_MODE;
|
|
148
124
|
}
|
|
149
|
-
/** Mirror the choice into PIKILOOM_CLAUDE_PRINT so any env-only fallback path
|
|
150
|
-
* (and tooling that inspects the env) agrees with the persisted config. */
|
|
151
125
|
export function setClaudeAccessModeEnv(value, env = process.env) {
|
|
152
126
|
env.PIKILOOM_CLAUDE_PRINT = value === 'api' ? '1' : '0';
|
|
153
127
|
}
|
|
@@ -183,18 +157,6 @@ export function setAgentEffortEnv(agent, value, env = process.env) {
|
|
|
183
157
|
break;
|
|
184
158
|
}
|
|
185
159
|
}
|
|
186
|
-
// ---------------------------------------------------------------------------
|
|
187
|
-
// "Ultra" effort rung — the single user-facing knob that folds workflow in
|
|
188
|
-
//
|
|
189
|
-
// Surfaced as the top rung of every effort picker (IM /models + dashboard),
|
|
190
|
-
// "ultra" means "max reasoning depth + permit multi-agent Workflow
|
|
191
|
-
// orchestration" — the same bundle as Claude's native `ultracode` mode. It is
|
|
192
|
-
// NOT a real --effort value (the CLI hard-rejects anything outside
|
|
193
|
-
// low|medium|high|xhigh|max), so every effort-write path decomposes it into a
|
|
194
|
-
// concrete effort plus the orthogonal workflow flag via this single helper.
|
|
195
|
-
// Because the rungs are mutually exclusive, picking any concrete level clears
|
|
196
|
-
// the orchestration opt-in. See Bot.switchEffortForChat for the IM mirror.
|
|
197
|
-
// ---------------------------------------------------------------------------
|
|
198
160
|
export const ULTRA_EFFORT = 'ultra';
|
|
199
161
|
export function decomposeEffortSelection(raw) {
|
|
200
162
|
const value = trimmed(raw).toLowerCase();
|