skimpyclaw 0.3.14 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) hide show
  1. package/README.md +47 -37
  2. package/dist/__tests__/adapter-types.test.d.ts +4 -0
  3. package/dist/__tests__/adapter-types.test.js +63 -0
  4. package/dist/__tests__/anthropic-adapter.test.d.ts +4 -0
  5. package/dist/__tests__/anthropic-adapter.test.js +264 -0
  6. package/dist/__tests__/api.test.js +0 -1
  7. package/dist/__tests__/cli.integration.test.js +2 -4
  8. package/dist/__tests__/cli.test.js +0 -1
  9. package/dist/__tests__/code-agents-notifications.test.js +137 -0
  10. package/dist/__tests__/code-agents-parser.test.js +19 -1
  11. package/dist/__tests__/code-agents-preflight.test.js +3 -28
  12. package/dist/__tests__/code-agents-utils.test.js +34 -9
  13. package/dist/__tests__/code-agents-worktrees.test.js +116 -0
  14. package/dist/__tests__/codex-adapter.test.js +184 -0
  15. package/dist/__tests__/codex-auth.test.js +66 -0
  16. package/dist/__tests__/codex-provider-gating.test.js +35 -0
  17. package/dist/__tests__/codex-unified-loop.test.js +111 -0
  18. package/dist/__tests__/config-security.test.js +127 -0
  19. package/dist/__tests__/config.test.js +23 -0
  20. package/dist/__tests__/context-manager.test.js +243 -164
  21. package/dist/__tests__/cron-run.test.js +250 -0
  22. package/dist/__tests__/cron.test.js +12 -38
  23. package/dist/__tests__/digests.test.js +67 -0
  24. package/dist/__tests__/discord-attachments.test.js +211 -0
  25. package/dist/__tests__/discord-docs.test.d.ts +1 -0
  26. package/dist/__tests__/discord-docs.test.js +27 -0
  27. package/dist/__tests__/discord-thread-agents.test.d.ts +1 -0
  28. package/dist/__tests__/discord-thread-agents.test.js +115 -0
  29. package/dist/__tests__/discord-thread-context.test.d.ts +1 -0
  30. package/dist/__tests__/discord-thread-context.test.js +42 -0
  31. package/dist/__tests__/doctor.formatters.test.js +4 -4
  32. package/dist/__tests__/doctor.index.test.js +1 -1
  33. package/dist/__tests__/doctor.runner.test.js +3 -15
  34. package/dist/__tests__/env-sanitizer.test.d.ts +1 -0
  35. package/dist/__tests__/env-sanitizer.test.js +45 -0
  36. package/dist/__tests__/exec-approval.test.js +61 -0
  37. package/dist/__tests__/fetch-tool.test.d.ts +1 -0
  38. package/dist/__tests__/fetch-tool.test.js +85 -0
  39. package/dist/__tests__/gateway-status-auth.test.d.ts +1 -0
  40. package/dist/__tests__/gateway-status-auth.test.js +72 -0
  41. package/dist/__tests__/heartbeat.test.js +3 -3
  42. package/dist/__tests__/interactive-sessions.test.d.ts +1 -0
  43. package/dist/__tests__/interactive-sessions.test.js +96 -0
  44. package/dist/__tests__/langfuse.test.js +6 -18
  45. package/dist/__tests__/model-selection.test.js +3 -4
  46. package/dist/__tests__/providers-init.test.js +2 -8
  47. package/dist/__tests__/providers-routing.test.js +1 -1
  48. package/dist/__tests__/providers-utils.test.js +13 -3
  49. package/dist/__tests__/sessions.test.js +14 -10
  50. package/dist/__tests__/setup.test.js +12 -29
  51. package/dist/__tests__/skills.test.js +10 -7
  52. package/dist/__tests__/stream-formatter.test.d.ts +1 -0
  53. package/dist/__tests__/stream-formatter.test.js +114 -0
  54. package/dist/__tests__/token-efficiency.test.js +131 -15
  55. package/dist/__tests__/tool-loop.test.d.ts +4 -0
  56. package/dist/__tests__/tool-loop.test.js +505 -0
  57. package/dist/__tests__/tools.test.js +101 -276
  58. package/dist/__tests__/utils.test.d.ts +1 -0
  59. package/dist/__tests__/utils.test.js +14 -0
  60. package/dist/__tests__/voice.test.js +21 -0
  61. package/dist/agent.js +35 -4
  62. package/dist/api.js +113 -37
  63. package/dist/channels/discord/attachments.d.ts +50 -0
  64. package/dist/channels/discord/attachments.js +137 -0
  65. package/dist/channels/discord/delegation.d.ts +5 -0
  66. package/dist/channels/discord/delegation.js +136 -0
  67. package/dist/channels/discord/handlers.js +694 -7
  68. package/dist/channels/discord/index.d.ts +16 -1
  69. package/dist/channels/discord/index.js +64 -1
  70. package/dist/channels/discord/thread-agents.d.ts +54 -0
  71. package/dist/channels/discord/thread-agents.js +323 -0
  72. package/dist/channels/discord/threads.d.ts +58 -0
  73. package/dist/channels/discord/threads.js +192 -0
  74. package/dist/channels/discord/types.js +4 -2
  75. package/dist/channels/discord/utils.d.ts +16 -0
  76. package/dist/channels/discord/utils.js +86 -6
  77. package/dist/channels/telegram/index.d.ts +1 -1
  78. package/dist/channels/telegram/types.js +1 -1
  79. package/dist/channels/telegram/utils.js +9 -3
  80. package/dist/channels.d.ts +1 -1
  81. package/dist/cli.js +20 -400
  82. package/dist/code-agents/executor.d.ts +1 -1
  83. package/dist/code-agents/executor.js +101 -45
  84. package/dist/code-agents/index.d.ts +2 -7
  85. package/dist/code-agents/index.js +111 -80
  86. package/dist/code-agents/interactive-resume.d.ts +6 -0
  87. package/dist/code-agents/interactive-resume.js +98 -0
  88. package/dist/code-agents/interactive-sessions.d.ts +20 -0
  89. package/dist/code-agents/interactive-sessions.js +132 -0
  90. package/dist/code-agents/parser.js +5 -1
  91. package/dist/code-agents/registry.d.ts +7 -1
  92. package/dist/code-agents/registry.js +11 -23
  93. package/dist/code-agents/stream-formatter.d.ts +8 -0
  94. package/dist/code-agents/stream-formatter.js +92 -0
  95. package/dist/code-agents/types.d.ts +16 -24
  96. package/dist/code-agents/utils.d.ts +35 -11
  97. package/dist/code-agents/utils.js +349 -95
  98. package/dist/code-agents/worktrees.d.ts +37 -0
  99. package/dist/code-agents/worktrees.js +116 -0
  100. package/dist/config.d.ts +2 -4
  101. package/dist/config.js +123 -23
  102. package/dist/cron.d.ts +1 -6
  103. package/dist/cron.js +175 -82
  104. package/dist/dashboard/assets/index-B345aOO-.js +65 -0
  105. package/dist/dashboard/assets/index-ZWK4dalJ.css +1 -0
  106. package/dist/dashboard/index.html +2 -2
  107. package/dist/digests.d.ts +1 -0
  108. package/dist/digests.js +132 -42
  109. package/dist/doctor/checks.d.ts +0 -3
  110. package/dist/doctor/checks.js +1 -108
  111. package/dist/doctor/runner.js +1 -4
  112. package/dist/env-sanitizer.d.ts +2 -0
  113. package/dist/env-sanitizer.js +61 -0
  114. package/dist/exec-approval.d.ts +11 -1
  115. package/dist/exec-approval.js +17 -4
  116. package/dist/gateway.d.ts +3 -1
  117. package/dist/gateway.js +17 -7
  118. package/dist/heartbeat.js +1 -6
  119. package/dist/langfuse.js +3 -29
  120. package/dist/model-selection.js +3 -1
  121. package/dist/providers/adapter.d.ts +118 -0
  122. package/dist/providers/adapter.js +6 -0
  123. package/dist/providers/adapters/anthropic-adapter.d.ts +22 -0
  124. package/dist/providers/adapters/anthropic-adapter.js +204 -0
  125. package/dist/providers/adapters/codex-adapter.d.ts +26 -0
  126. package/dist/providers/adapters/codex-adapter.js +203 -0
  127. package/dist/providers/anthropic.d.ts +1 -0
  128. package/dist/providers/anthropic.js +10 -272
  129. package/dist/providers/codex.d.ts +21 -0
  130. package/dist/providers/codex.js +149 -330
  131. package/dist/providers/content.d.ts +1 -1
  132. package/dist/providers/content.js +2 -2
  133. package/dist/providers/context-manager.d.ts +18 -6
  134. package/dist/providers/context-manager.js +199 -223
  135. package/dist/providers/index.d.ts +9 -1
  136. package/dist/providers/index.js +73 -64
  137. package/dist/providers/loop-utils.d.ts +20 -0
  138. package/dist/providers/loop-utils.js +30 -0
  139. package/dist/providers/tool-loop.d.ts +12 -0
  140. package/dist/providers/tool-loop.js +251 -0
  141. package/dist/providers/utils.d.ts +19 -3
  142. package/dist/providers/utils.js +100 -29
  143. package/dist/secure-store.d.ts +8 -0
  144. package/dist/secure-store.js +80 -0
  145. package/dist/service.js +3 -28
  146. package/dist/sessions.d.ts +3 -0
  147. package/dist/sessions.js +147 -18
  148. package/dist/setup-templates.js +13 -25
  149. package/dist/setup.d.ts +10 -6
  150. package/dist/setup.js +84 -292
  151. package/dist/skills.js +3 -11
  152. package/dist/tools/agent-delegation.d.ts +19 -0
  153. package/dist/tools/agent-delegation.js +49 -0
  154. package/dist/tools/bash-tool.js +89 -34
  155. package/dist/tools/definitions.d.ts +199 -302
  156. package/dist/tools/definitions.js +70 -123
  157. package/dist/tools/execute-context.d.ts +13 -4
  158. package/dist/tools/fetch-tool.js +109 -13
  159. package/dist/tools/file-tools.js +7 -1
  160. package/dist/tools.d.ts +7 -7
  161. package/dist/tools.js +133 -151
  162. package/dist/types.d.ts +37 -30
  163. package/dist/utils.js +4 -6
  164. package/dist/voice.d.ts +1 -1
  165. package/dist/voice.js +17 -4
  166. package/package.json +33 -23
  167. package/templates/TOOLS.md +0 -27
  168. package/dist/__tests__/audit.test.js +0 -122
  169. package/dist/__tests__/code-agents-orchestrator.test.js +0 -216
  170. package/dist/__tests__/code-agents-sandbox.test.js +0 -163
  171. package/dist/__tests__/orchestrator.test.js +0 -425
  172. package/dist/__tests__/sandbox-bridge.test.js +0 -116
  173. package/dist/__tests__/sandbox-manager.test.js +0 -144
  174. package/dist/__tests__/sandbox-mount-security.test.js +0 -139
  175. package/dist/__tests__/sandbox-runtime.test.js +0 -176
  176. package/dist/__tests__/subagent.test.js +0 -240
  177. package/dist/__tests__/telegram.test.js +0 -42
  178. package/dist/code-agents/orchestrator.d.ts +0 -29
  179. package/dist/code-agents/orchestrator.js +0 -694
  180. package/dist/code-agents/worktree.d.ts +0 -40
  181. package/dist/code-agents/worktree.js +0 -215
  182. package/dist/dashboard/assets/index-BoTHPby4.js +0 -65
  183. package/dist/dashboard/assets/index-D4mufvBg.css +0 -1
  184. package/dist/dashboard.d.ts +0 -8
  185. package/dist/dashboard.js +0 -4071
  186. package/dist/discord.d.ts +0 -8
  187. package/dist/discord.js +0 -792
  188. package/dist/mcp-context-a8c.d.ts +0 -13
  189. package/dist/mcp-context-a8c.js +0 -34
  190. package/dist/orchestrator.d.ts +0 -15
  191. package/dist/orchestrator.js +0 -676
  192. package/dist/providers/openai.d.ts +0 -10
  193. package/dist/providers/openai.js +0 -355
  194. package/dist/sandbox/bridge.d.ts +0 -5
  195. package/dist/sandbox/bridge.js +0 -63
  196. package/dist/sandbox/index.d.ts +0 -5
  197. package/dist/sandbox/index.js +0 -4
  198. package/dist/sandbox/manager.d.ts +0 -7
  199. package/dist/sandbox/manager.js +0 -100
  200. package/dist/sandbox/mount-security.d.ts +0 -12
  201. package/dist/sandbox/mount-security.js +0 -122
  202. package/dist/sandbox/runtime.d.ts +0 -39
  203. package/dist/sandbox/runtime.js +0 -192
  204. package/dist/sandbox-utils.d.ts +0 -6
  205. package/dist/sandbox-utils.js +0 -36
  206. package/dist/subagent.d.ts +0 -19
  207. package/dist/subagent.js +0 -407
  208. package/dist/telegram.d.ts +0 -2
  209. package/dist/telegram.js +0 -11
  210. package/dist/tools/browser-tool.d.ts +0 -3
  211. package/dist/tools/browser-tool.js +0 -266
  212. package/sandbox/Dockerfile +0 -40
  213. /package/dist/__tests__/{audit.test.d.ts → code-agents-notifications.test.d.ts} +0 -0
  214. /package/dist/__tests__/{code-agents-orchestrator.test.d.ts → code-agents-worktrees.test.d.ts} +0 -0
  215. /package/dist/__tests__/{code-agents-sandbox.test.d.ts → codex-adapter.test.d.ts} +0 -0
  216. /package/dist/__tests__/{orchestrator.test.d.ts → codex-auth.test.d.ts} +0 -0
  217. /package/dist/__tests__/{sandbox-bridge.test.d.ts → codex-provider-gating.test.d.ts} +0 -0
  218. /package/dist/__tests__/{sandbox-manager.test.d.ts → codex-unified-loop.test.d.ts} +0 -0
  219. /package/dist/__tests__/{sandbox-mount-security.test.d.ts → config-security.test.d.ts} +0 -0
  220. /package/dist/__tests__/{sandbox-runtime.test.d.ts → cron-run.test.d.ts} +0 -0
  221. /package/dist/__tests__/{subagent.test.d.ts → digests.test.d.ts} +0 -0
  222. /package/dist/__tests__/{telegram.test.d.ts → discord-attachments.test.d.ts} +0 -0
package/dist/cli.js CHANGED
@@ -8,9 +8,8 @@ import { loadConfig, loadRawConfig, getConfigPath, saveConfig, resolveAllowedPat
8
8
  import { startRuntime } from './service.js';
9
9
  import { runSetup, renderGatewayPlist } from './setup.js';
10
10
  import { runDoctor as runDoctorCommand } from './doctor/index.js';
11
- import { executeTool, getToolDefinitions, BUILTIN_TOOL_DEFINITIONS, BROWSER_TOOL_DEFINITION } from './tools.js';
11
+ import { getToolDefinitions, BUILTIN_TOOL_DEFINITIONS } from './tools.js';
12
12
  import { formatModelSelectionError, getModelSelectionUsage, resolveModelSelection } from './model-selection.js';
13
- import { detectSandboxRuntime, isSandboxRuntimeRunning, sandboxNetworkExists, defaultSandboxNetwork, sandboxImageExists, } from './sandbox-utils.js';
14
13
  const APP_NAME = 'skimpyclaw';
15
14
  const DEFAULT_PORT = 18790;
16
15
  const LAUNCHD_LABEL = 'com.skimpyclaw.gateway';
@@ -39,18 +38,12 @@ Commands:
39
38
  cron list List cron jobs from gateway status
40
39
  cron run <id> Trigger cron job by id
41
40
  doctor [--json] Run preflight checks
42
- browser <action> ... Run browser tool action (open/click/type/select/hover/scroll/waitFor/evaluate/getText/screenshot/wait/close)
43
- browser login [url] Open real Chrome (no automation) for manual login. Cookies persist for agent use.
44
41
  tools list List available tools (built-in + MCP)
45
42
  tools install <name> Add MCP server (--command <cmd> [--args ...] or --url <url>)
46
43
  tools remove <name> Remove MCP server
47
44
  agents List coding agents (active + recent)
48
45
  agents <id> Show details for a coding agent (with live output)
49
46
  agents <id> --follow Follow live output for an agent
50
- sandbox status Show active sandbox containers
51
- sandbox prune Force-prune all sandbox containers
52
- sandbox init Auto-setup sandbox runtime/image/config (supports --profile)
53
- sandbox doctor Sandbox-specific diagnostics and hints
54
47
  help Show this help
55
48
  `);
56
49
  }
@@ -106,9 +99,7 @@ function getCliToolConfig(config) {
106
99
  if (config.channels.telegram.tools) {
107
100
  return {
108
101
  ...config.channels.telegram.tools,
109
- allowedPaths: config.channels.telegram.tools.allowedPaths?.length
110
- ? config.channels.telegram.tools.allowedPaths
111
- : resolveAllowedPaths(config),
102
+ allowedPaths: resolveAllowedPaths(config, config.channels.telegram.tools.allowedPaths),
112
103
  };
113
104
  }
114
105
  return {
@@ -249,10 +240,18 @@ function daemonStatus() {
249
240
  async function requestGateway(path, init, port) {
250
241
  const cfgPort = port ?? loadConfig().gateway.port;
251
242
  const url = `http://127.0.0.1:${cfgPort}${path}`;
243
+ let dashboardToken = '';
244
+ try {
245
+ dashboardToken = String(loadConfig()?.dashboard?.token || '');
246
+ }
247
+ catch {
248
+ // best effort
249
+ }
252
250
  const res = await fetch(url, {
253
251
  ...init,
254
252
  headers: {
255
253
  'content-type': 'application/json',
254
+ ...(dashboardToken ? { authorization: `Bearer ${dashboardToken}` } : {}),
256
255
  ...(init?.headers || {}),
257
256
  },
258
257
  signal: AbortSignal.timeout(5000),
@@ -291,12 +290,12 @@ async function commandStatus() {
291
290
  let port = DEFAULT_PORT;
292
291
  let dashboardToken = '';
293
292
  try {
294
- const raw = loadRawConfig();
295
- const rawPort = Number(raw?.gateway?.port);
296
- if (Number.isFinite(rawPort) && rawPort > 0) {
297
- port = rawPort;
293
+ const cfg = loadConfig();
294
+ const cfgPort = Number(cfg?.gateway?.port);
295
+ if (Number.isFinite(cfgPort) && cfgPort > 0) {
296
+ port = cfgPort;
298
297
  }
299
- dashboardToken = String(raw?.dashboard?.token || '');
298
+ dashboardToken = String(cfg?.dashboard?.token || '');
300
299
  }
301
300
  catch {
302
301
  // Keep default when config does not exist yet.
@@ -499,159 +498,6 @@ async function commandCron(args) {
499
498
  console.error('Usage: skimpyclaw cron <list|run>');
500
499
  return 1;
501
500
  }
502
- async function commandBrowserLogin(args, config) {
503
- const url = args[0] || 'about:blank';
504
- const toolConfig = getCliToolConfig(config);
505
- const profileDir = toolConfig.browser?.profileDir || join(homedir(), '.skimpyclaw', 'browser-profile');
506
- const executablePath = toolConfig.browser?.executablePath || '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';
507
- if (!existsSync(profileDir)) {
508
- mkdirSync(profileDir, { recursive: true });
509
- }
510
- console.log(`Opening Chrome for login (profile: ${profileDir})`);
511
- console.log('Log in manually, then close the browser window when done.');
512
- console.log('Cookies will persist for agent use.');
513
- const chromeArgs = [
514
- `--user-data-dir=${profileDir}`,
515
- '--no-first-run',
516
- '--no-default-browser-check',
517
- url,
518
- ];
519
- const child = spawn(executablePath, chromeArgs, {
520
- stdio: 'ignore',
521
- detached: false,
522
- });
523
- return new Promise((resolve) => {
524
- child.on('exit', (code) => {
525
- console.log('Browser closed. Login session saved.');
526
- resolve(code ?? 0);
527
- });
528
- child.on('error', (err) => {
529
- console.error(`Failed to launch Chrome: ${err.message}`);
530
- resolve(1);
531
- });
532
- });
533
- }
534
- async function commandBrowser(args) {
535
- const action = args[0];
536
- if (!action) {
537
- console.error('Usage: skimpyclaw browser <open|click|type|select|hover|scroll|waitFor|evaluate|getText|screenshot|wait|close|login> ...');
538
- return 1;
539
- }
540
- const config = loadConfig();
541
- if (action.toLowerCase() === 'login') {
542
- return commandBrowserLogin(args.slice(1), config);
543
- }
544
- const toolConfig = getCliToolConfig(config);
545
- if (!toolConfig.browser?.enabled) {
546
- console.error('Browser tool is disabled. Enable tools.browser.enabled in config.');
547
- return 1;
548
- }
549
- const normalizedAction = action.toLowerCase();
550
- const input = { action: normalizedAction };
551
- if (normalizedAction === 'open') {
552
- input.url = args[1];
553
- if (!input.url) {
554
- console.error('Usage: skimpyclaw browser open <url>');
555
- return 1;
556
- }
557
- }
558
- else if (normalizedAction === 'click') {
559
- input.selector = args[1];
560
- if (!input.selector) {
561
- console.error('Usage: skimpyclaw browser click <selector>');
562
- return 1;
563
- }
564
- }
565
- else if (normalizedAction === 'type') {
566
- input.selector = args[1];
567
- input.text = args.slice(2).join(' ');
568
- if (!input.selector || !input.text) {
569
- console.error('Usage: skimpyclaw browser type <selector> <text>');
570
- return 1;
571
- }
572
- }
573
- else if (normalizedAction === 'waitfor') {
574
- input.selector = args[1];
575
- if (hasFlag(args, '--text')) {
576
- const idx = args.indexOf('--text');
577
- input.text = args[idx + 1];
578
- }
579
- }
580
- else if (normalizedAction === 'screenshot') {
581
- input.file_path = args[1];
582
- }
583
- else if (normalizedAction === 'wait') {
584
- const idx = args.indexOf('--ms');
585
- if (idx !== -1)
586
- input.timeMs = Number(args[idx + 1]);
587
- }
588
- else if (normalizedAction === 'evaluate') {
589
- const scriptIdx = args.indexOf('--script');
590
- if (scriptIdx !== -1) {
591
- input.script = args.slice(scriptIdx + 1).join(' ');
592
- }
593
- else {
594
- input.script = args[1];
595
- }
596
- if (!input.script) {
597
- console.error('Usage: skimpyclaw browser evaluate --script "document.title"');
598
- return 1;
599
- }
600
- }
601
- else if (normalizedAction === 'gettext') {
602
- input.selector = args[1]; // optional
603
- }
604
- else if (normalizedAction === 'scroll') {
605
- if (args[1] && !args[1].startsWith('--')) {
606
- input.selector = args[1]; // scrollIntoView target
607
- }
608
- const dirIdx = args.indexOf('--direction');
609
- if (dirIdx !== -1)
610
- input.direction = args[dirIdx + 1];
611
- const amtIdx = args.indexOf('--amount');
612
- if (amtIdx !== -1)
613
- input.amount = Number(args[amtIdx + 1]);
614
- }
615
- else if (normalizedAction === 'select') {
616
- input.selector = args[1];
617
- input.text = args[2];
618
- if (!input.selector || !input.text) {
619
- console.error('Usage: skimpyclaw browser select <selector> <value>');
620
- return 1;
621
- }
622
- }
623
- else if (normalizedAction === 'hover') {
624
- input.selector = args[1];
625
- if (!input.selector) {
626
- console.error('Usage: skimpyclaw browser hover <selector>');
627
- return 1;
628
- }
629
- }
630
- if (hasFlag(args, '--headful'))
631
- input.headless = false;
632
- if (hasFlag(args, '--headless'))
633
- input.headless = true;
634
- const browserIdx = args.indexOf('--browser');
635
- if (browserIdx !== -1)
636
- input.type = args[browserIdx + 1];
637
- const slowIdx = args.indexOf('--slowmo');
638
- if (slowIdx !== -1)
639
- input.slowMoMs = Number(args[slowIdx + 1]);
640
- const uaIdx = args.indexOf('--user-agent');
641
- if (uaIdx !== -1)
642
- input.userAgent = args[uaIdx + 1];
643
- const exeIdx = args.indexOf('--executable');
644
- if (exeIdx !== -1)
645
- input.executablePath = args[exeIdx + 1];
646
- const wIdx = args.indexOf('--width');
647
- const hIdx = args.indexOf('--height');
648
- if (wIdx !== -1 && hIdx !== -1) {
649
- input.viewport = { width: Number(args[wIdx + 1]), height: Number(args[hIdx + 1]) };
650
- }
651
- const result = await executeTool('Browser', input, toolConfig);
652
- console.log(result);
653
- return result.startsWith('Error') ? 1 : 0;
654
- }
655
501
  async function commandTools(args) {
656
502
  const sub = args[0];
657
503
  if (sub === 'list' || !sub) {
@@ -659,20 +505,11 @@ async function commandTools(args) {
659
505
  const toolConfig = getCliToolConfig(config);
660
506
  const tools = await getToolDefinitions(toolConfig);
661
507
  // Group tools
662
- const builtinNames = new Set(BUILTIN_TOOL_DEFINITIONS.map(t => t.name));
663
- const browserName = BROWSER_TOOL_DEFINITION.name;
508
+ const builtinNames = new Set([...BUILTIN_TOOL_DEFINITIONS.map(t => t.name), 'Fetch']);
664
509
  console.log('Built-in tools:');
665
510
  for (const t of tools.filter(t => builtinNames.has(t.name))) {
666
511
  console.log(` ${t.name.padEnd(20)} ${(t.description || '').split('\n')[0]}`);
667
512
  }
668
- const browser = tools.find(t => t.name === browserName);
669
- if (browser) {
670
- console.log('\nBrowser tool:');
671
- console.log(` ${browser.name.padEnd(20)} ${(browser.description || '').split('\n')[0]}`);
672
- }
673
- else {
674
- console.log('\nBrowser tool: disabled');
675
- }
676
513
  const mcpTools = tools.filter(t => t.name.startsWith('mcp__'));
677
514
  if (mcpTools.length > 0) {
678
515
  // Group by server
@@ -765,44 +602,6 @@ async function commandTools(args) {
765
602
  console.error('Usage: skimpyclaw tools <list|install|remove>');
766
603
  return 1;
767
604
  }
768
- const SANDBOX_CLI_BY_PROFILE = {
769
- minimal: ['bash', 'curl', 'git', 'gh', 'jq', 'python3', 'rg', 'pnpm'],
770
- dev: ['bash', 'curl', 'git', 'gh', 'jq', 'python3', 'rg', 'pnpm', 'gcc', 'g++', 'make'],
771
- full: ['bash', 'curl', 'git', 'gh', 'jq', 'python3', 'rg', 'pnpm', 'gcc', 'g++', 'make', 'pip3', 'sqlite3'],
772
- };
773
- function resolveSandboxDir() {
774
- // 1. Check CWD (user is in repo root)
775
- const cwdSandbox = join(process.cwd(), 'sandbox');
776
- if (existsSync(join(cwdSandbox, 'Dockerfile'))) {
777
- return cwdSandbox;
778
- }
779
- // 2. Check relative to package root (global/npm install)
780
- const thisFile = fileURLToPath(import.meta.url);
781
- const pkgRoot = join(thisFile, '..', '..'); // dist/src/cli.js -> repo root
782
- const pkgSandbox = join(pkgRoot, 'sandbox');
783
- if (existsSync(join(pkgSandbox, 'Dockerfile'))) {
784
- return pkgSandbox;
785
- }
786
- return null;
787
- }
788
- function parseSandboxOption(args, flag) {
789
- return parseOption(args, flag, '') || undefined;
790
- }
791
- function runSandboxImageCheck(runtime, image, network, cmd) {
792
- const result = spawnSync(runtime, ['run', '--rm', '--network', network, image, 'sh', '-lc', cmd], { encoding: 'utf-8' });
793
- if (result.status === 0) {
794
- return { ok: true, detail: (result.stdout || '').trim() || 'ok' };
795
- }
796
- const detail = `${(result.stderr || '').trim()} ${(result.stdout || '').trim()}`.trim() || `exit ${result.status ?? 1}`;
797
- return { ok: false, detail };
798
- }
799
- function printSandboxCheck(ok, name, detail, hint) {
800
- const prefix = ok ? '✓' : '✗';
801
- console.log(`${prefix} ${name}: ${detail}`);
802
- if (!ok && hint) {
803
- console.log(` → ${hint}`);
804
- }
805
- }
806
605
  async function commandAgents(args) {
807
606
  const { getAllCodeAgents, getCodeAgent, restoreCodeAgentTasks } = await import('./code-agents/index.js');
808
607
  // Restore tasks from disk so we can see them
@@ -829,24 +628,6 @@ async function commandAgents(args) {
829
628
  console.log(`Model: ${agent.model}`);
830
629
  console.log(`Workdir: ${agent.workdir}`);
831
630
  console.log(`Task: ${agent.task.slice(0, 200)}${agent.task.length > 200 ? '...' : ''}`);
832
- // Show children for team coordinator
833
- if (agent.childTaskIds && agent.childTaskIds.length > 0) {
834
- console.log(`\n\x1b[1mChildren:\x1b[0m`);
835
- for (const childId of agent.childTaskIds) {
836
- const child = getCodeAgent(childId);
837
- if (!child)
838
- continue;
839
- const cElapsed = child.durationSeconds != null
840
- ? child.durationSeconds
841
- : Math.round((Date.now() - new Date(child.startedAt).getTime()) / 1000);
842
- const cStr = cElapsed < 60 ? `${cElapsed}s` : `${Math.floor(cElapsed / 60)}m${cElapsed % 60}s`;
843
- const waveLabel = child.wave != null ? ` [wave ${child.wave + 1}]` : '';
844
- const icon = child.status === 'completed' ? '✅' : child.status === 'failed' ? '❌' : child.status === 'running' ? '🔄' : child.status === 'pending' ? '⏳' : '❓';
845
- console.log(` ${icon} ${child.id} ${child.status} (${cStr})${waveLabel}`);
846
- const subtask = (child.subtask || child.task).slice(0, 120);
847
- console.log(` ${subtask}${(child.subtask || child.task).length > 120 ? '...' : ''}`);
848
- }
849
- }
850
631
  // Show live output
851
632
  if (agent.liveOutput) {
852
633
  console.log(`\n\x1b[1mLive Output:\x1b[0m`);
@@ -895,8 +676,7 @@ async function commandAgents(args) {
895
676
  const elapsed = Math.round((Date.now() - new Date(a.startedAt).getTime()) / 1000);
896
677
  const elapsedStr = elapsed < 60 ? `${elapsed}s` : `${Math.floor(elapsed / 60)}m${elapsed % 60}s`;
897
678
  const taskPreview = a.task.slice(0, 80) + (a.task.length > 80 ? '...' : '');
898
- const children = a.childTaskIds ? ` (${a.childTaskIds.length} children)` : '';
899
- console.log(` ${a.id}: \x1b[33m${a.status}\x1b[0m ${a.agent} (${elapsedStr})${children} — ${taskPreview}`);
679
+ console.log(` ${a.id}: \x1b[33m${a.status}\x1b[0m ${a.agent} (${elapsedStr})${taskPreview ? ` ${taskPreview}` : ''}`);
900
680
  }
901
681
  }
902
682
  if (finished.length > 0) {
@@ -912,165 +692,8 @@ async function commandAgents(args) {
912
692
  }
913
693
  return 0;
914
694
  }
915
- async function commandSandbox(args) {
916
- const sub = args[0];
917
- if (sub === 'status') {
918
- const rt = detectSandboxRuntime();
919
- if (!rt) {
920
- console.log('No container runtime found (install Docker or Apple Containers).');
921
- return 1;
922
- }
923
- const result = spawnSync(rt, ['ps', '--format', '{{.Names}}'], { encoding: 'utf-8' });
924
- const lines = (result.stdout || '').trim().split('\n').filter(Boolean);
925
- const containers = lines.filter((line) => line.includes('skimpyclaw-sbx'));
926
- if (containers.length === 0) {
927
- console.log('No active sandbox containers.');
928
- }
929
- else {
930
- console.log(`Active sandbox containers (${containers.length}):`);
931
- containers.forEach((c) => console.log(` ${c}`));
932
- }
933
- return 0;
934
- }
935
- if (sub === 'prune') {
936
- const { cleanupOrphans } = await import('./sandbox/index.js');
937
- const count = await cleanupOrphans();
938
- console.log(`Pruned ${count} sandbox container(s).`);
939
- return 0;
940
- }
941
- if (sub === 'init') {
942
- const runtimeFlag = parseSandboxOption(args, '--runtime');
943
- const profileFlag = parseSandboxOption(args, '--profile') || 'minimal';
944
- const imageFlag = parseSandboxOption(args, '--image');
945
- const networkFlag = parseSandboxOption(args, '--network');
946
- const validProfiles = ['minimal', 'dev', 'full'];
947
- if (!validProfiles.includes(profileFlag)) {
948
- console.error(`Invalid profile "${profileFlag}". Use one of: minimal, dev, full`);
949
- return 1;
950
- }
951
- const profile = profileFlag;
952
- const runtime = detectSandboxRuntime(runtimeFlag);
953
- if (!runtime) {
954
- console.error('No supported runtime found. Install Apple Containers or Docker.');
955
- return 1;
956
- }
957
- const network = networkFlag || defaultSandboxNetwork(runtime);
958
- const image = imageFlag || 'skimpyclaw-sandbox:latest';
959
- if (!isSandboxRuntimeRunning(runtime)) {
960
- const hint = runtime === 'container' ? 'Run: container system start' : 'Start Docker Desktop (or run `docker info`).';
961
- console.error(`Runtime "${runtime}" is not running.`);
962
- console.error(hint);
963
- return 1;
964
- }
965
- if (!sandboxNetworkExists(runtime, network)) {
966
- const hint = runtime === 'container'
967
- ? 'Create/list networks with `container network ls`.'
968
- : 'Create/list networks with `docker network ls`.';
969
- console.error(`Sandbox network "${network}" not found for runtime "${runtime}".`);
970
- console.error(hint);
971
- return 1;
972
- }
973
- const sandboxDir = resolveSandboxDir();
974
- if (!sandboxDir) {
975
- console.error('Could not find sandbox/Dockerfile from current directory.');
976
- console.error('Run from repo root (contains ./sandbox) or build image manually.');
977
- return 1;
978
- }
979
- console.log(`Building sandbox image "${image}" (runtime=${runtime}, profile=${profile})...`);
980
- const build = spawnSync(runtime, ['build', '--build-arg', `SKIMPY_PROFILE=${profile}`, '-t', image, sandboxDir], { stdio: 'inherit' });
981
- if (build.status !== 0) {
982
- console.error('Sandbox image build failed.');
983
- return 1;
984
- }
985
- const raw = loadRawConfig();
986
- const sandbox = raw.sandbox ?? {};
987
- sandbox.enabled = true;
988
- sandbox.runtime = runtime;
989
- sandbox.network = network;
990
- sandbox.image = image;
991
- raw.sandbox = sandbox;
992
- saveConfig(raw);
993
- console.log('Updated config: sandbox.enabled=true');
994
- console.log(`Updated config: sandbox.runtime="${runtime}"`);
995
- console.log(`Updated config: sandbox.network="${network}"`);
996
- console.log(`Updated config: sandbox.image="${image}"`);
997
- const required = SANDBOX_CLI_BY_PROFILE[profile];
998
- const checkCmd = `for c in ${required.join(' ')}; do command -v "$c" >/dev/null || { echo "missing:$c"; exit 1; }; done; echo cli-ok`;
999
- const cliCheck = runSandboxImageCheck(runtime, image, network, checkCmd);
1000
- const netCheck = runSandboxImageCheck(runtime, image, network, 'curl -fsS --max-time 8 https://example.com >/dev/null && echo net-ok');
1001
- const hostCheck = runSandboxImageCheck(runtime, image, network, 'hostname');
1002
- printSandboxCheck(hostCheck.ok, 'sandbox_hostname', hostCheck.detail);
1003
- printSandboxCheck(cliCheck.ok, 'sandbox_tools', cliCheck.detail, 'Rebuild image or choose a lighter profile.');
1004
- printSandboxCheck(netCheck.ok, 'sandbox_network_egress', netCheck.detail, 'Try a different sandbox.network or check runtime DNS/network settings.');
1005
- if (!hostCheck.ok || !cliCheck.ok || !netCheck.ok) {
1006
- return 1;
1007
- }
1008
- console.log('\nSandbox init complete. Restart Skimpy to apply runtime config.');
1009
- return 0;
1010
- }
1011
- if (sub === 'doctor') {
1012
- const config = loadConfig();
1013
- const runtime = detectSandboxRuntime(config.sandbox?.runtime);
1014
- const image = config.sandbox?.image || 'skimpyclaw-sandbox:latest';
1015
- const network = config.sandbox?.network || (runtime ? defaultSandboxNetwork(runtime) : 'unknown');
1016
- const profileFlag = parseSandboxOption(args, '--profile') || 'minimal';
1017
- const profile = (['minimal', 'dev', 'full'].includes(profileFlag) ? profileFlag : 'minimal');
1018
- let failed = false;
1019
- printSandboxCheck(config.sandbox?.enabled === true, 'sandbox_enabled', config.sandbox?.enabled ? 'enabled' : 'disabled', 'Run: skimpyclaw sandbox init');
1020
- if (!config.sandbox?.enabled)
1021
- failed = true;
1022
- printSandboxCheck(!!runtime, 'runtime_detected', runtime || 'none', 'Install Docker or Apple Containers.');
1023
- if (!runtime)
1024
- return 1;
1025
- printSandboxCheck(isSandboxRuntimeRunning(runtime), 'runtime_running', runtime, runtime === 'container' ? 'Run: container system start' : 'Start Docker Desktop.');
1026
- if (!isSandboxRuntimeRunning(runtime))
1027
- failed = true;
1028
- const networkOk = sandboxNetworkExists(runtime, network);
1029
- printSandboxCheck(networkOk, 'network_exists', network, `Use "${runtime === 'container' ? 'container' : 'docker'} network ls" and update sandbox.network.`);
1030
- if (!networkOk)
1031
- failed = true;
1032
- const imageOk = sandboxImageExists(runtime, image);
1033
- printSandboxCheck(imageOk, 'image_exists', image, `Build image: ${runtime} build -t ${image} sandbox/`);
1034
- if (!imageOk)
1035
- failed = true;
1036
- if (imageOk && networkOk) {
1037
- const required = SANDBOX_CLI_BY_PROFILE[profile];
1038
- const checkCmd = `for c in ${required.join(' ')}; do command -v "$c" >/dev/null || { echo "missing:$c"; exit 1; }; done; echo cli-ok`;
1039
- const cliCheck = runSandboxImageCheck(runtime, image, network, checkCmd);
1040
- printSandboxCheck(cliCheck.ok, 'image_toolchain', cliCheck.detail, 'Rebuild with: skimpyclaw sandbox init --profile dev');
1041
- if (!cliCheck.ok)
1042
- failed = true;
1043
- const netCheck = runSandboxImageCheck(runtime, image, network, 'curl -fsS --max-time 8 https://api.duckduckgo.com/?q=skimpyclaw&format=json >/dev/null && echo net-ok');
1044
- printSandboxCheck(netCheck.ok, 'network_egress', netCheck.detail, 'Some sources may timeout; verify DNS/network in runtime.');
1045
- if (!netCheck.ok)
1046
- failed = true;
1047
- }
1048
- return failed ? 1 : 0;
1049
- }
1050
- console.log(`Usage: skimpyclaw sandbox <command>
1051
-
1052
- Commands:
1053
- init Build sandbox image and enable in config
1054
- status List active sandbox containers
1055
- prune Remove orphaned sandbox containers
1056
- doctor Run targeted sandbox diagnostics
1057
-
1058
- Init options:
1059
- --runtime <container|docker> Container runtime (default: auto-detect)
1060
- --profile <minimal|dev|full> Package set (default: minimal)
1061
- --image <name> Image name (default: skimpyclaw-sandbox:latest)
1062
- --network <name> Network name (default: auto per runtime)
1063
-
1064
- Profiles:
1065
- minimal bash, curl, git, gh, jq, python3, ripgrep, pnpm
1066
- dev minimal + gcc, g++, make
1067
- full dev + pip3, sqlite3, unzip, less
1068
-
1069
- Which runtime?
1070
- Apple Containers (macOS 26+) — lighter, faster startup, no daemon.
1071
- Docker — cross-platform, use if you already run Docker.
1072
- Auto-detect prefers Apple Containers, falls back to Docker.
1073
- `);
695
+ async function commandSandboxRemoved() {
696
+ console.error("The sandbox subsystem has been removed. Use exec-approval for command safety.");
1074
697
  return 1;
1075
698
  }
1076
699
  export async function runCli(argv = process.argv.slice(2)) {
@@ -1141,9 +764,6 @@ export async function runCli(argv = process.argv.slice(2)) {
1141
764
  console.log(result.output);
1142
765
  return result.exitCode;
1143
766
  }
1144
- if (command === 'browser') {
1145
- return await commandBrowser(args);
1146
- }
1147
767
  if (command === 'tools') {
1148
768
  return await commandTools(args);
1149
769
  }
@@ -1151,7 +771,7 @@ export async function runCli(argv = process.argv.slice(2)) {
1151
771
  return await commandAgents(args);
1152
772
  }
1153
773
  if (command === 'sandbox') {
1154
- return await commandSandbox(args);
774
+ return await commandSandboxRemoved();
1155
775
  }
1156
776
  console.error(`Unknown command: ${command}`);
1157
777
  printHelp();
@@ -20,7 +20,7 @@ export declare function detectPackageManager(workdir: string): PackageManager;
20
20
  * 4. Empty string (skip validation) if no scripts found
21
21
  */
22
22
  export declare function buildValidationCommand(workdir: string, validationCommands?: Record<string, string>): string;
23
- /** Run build/test validation. Shared by solo agents and team orchestrator. */
23
+ /** Run build/test validation. */
24
24
  export declare function runValidation(workdir: string, validationCommands?: Record<string, string>): Promise<ValidationResult>;
25
25
  /** Background execution of a coding agent. Updates task status throughout. */
26
26
  export declare function runCodeAgentBackground(id: string, agent: string, task: string, workdir: string, validate: boolean, input: Record<string, any>, startedAt: Date, options?: CodeAgentBackgroundOptions): Promise<void>;