pikiloom 0.4.26 → 0.4.27

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 (155) hide show
  1. package/dashboard/dist/assets/{AgentTab-BNJ73P0m.js → AgentTab-DIP-y4dl.js} +1 -1
  2. package/dashboard/dist/assets/ConnectionModal-BCfvhkiI.js +1 -0
  3. package/dashboard/dist/assets/{DirBrowser-D-GT-G-w.js → DirBrowser-Dot7ua9-.js} +1 -1
  4. package/dashboard/dist/assets/{ExtensionsTab-BrJMulJR.js → ExtensionsTab-GioMx4S6.js} +1 -1
  5. package/dashboard/dist/assets/{IMAccessTab-auPnx468.js → IMAccessTab-C9_r8jJP.js} +1 -1
  6. package/dashboard/dist/assets/{Modal-lYfO9m-I.js → Modal-M1AP1sgR.js} +1 -1
  7. package/dashboard/dist/assets/{Modals-PRdWOIY9.js → Modals-CCm3UUfJ.js} +1 -1
  8. package/dashboard/dist/assets/{Select-BhbU-rll.js → Select-YubDM16P.js} +1 -1
  9. package/dashboard/dist/assets/{SessionPanel-_0dLWvEb.js → SessionPanel-Cnv9o96n.js} +1 -1
  10. package/dashboard/dist/assets/SystemTab-VgNivIe3.js +1 -0
  11. package/dashboard/dist/assets/index-BY7l20bi.js +23 -0
  12. package/dashboard/dist/assets/index-BlqvzDUL.js +3 -0
  13. package/dashboard/dist/assets/index-CXIN3nTr.css +1 -0
  14. package/dashboard/dist/assets/{shared-C5uXvhZJ.js → shared-C0SAInnT.js} +1 -1
  15. package/dashboard/dist/index.html +2 -2
  16. package/dist/agent/acp-client.js +0 -59
  17. package/dist/agent/artifacts.js +1 -38
  18. package/dist/agent/auto-update.js +2 -78
  19. package/dist/agent/await-resume.js +0 -20
  20. package/dist/agent/cli/auth.js +3 -51
  21. package/dist/agent/cli/catalog.js +0 -3
  22. package/dist/agent/cli/detector.js +1 -19
  23. package/dist/agent/cli/index.js +0 -3
  24. package/dist/agent/cli/registry.js +0 -25
  25. package/dist/agent/driver.js +0 -14
  26. package/dist/agent/drivers/claude-tui.js +2 -769
  27. package/dist/agent/drivers/claude.js +12 -631
  28. package/dist/agent/drivers/codex.js +11 -162
  29. package/dist/agent/drivers/gemini.js +6 -114
  30. package/dist/agent/drivers/hermes.js +0 -128
  31. package/dist/agent/goal.js +0 -45
  32. package/dist/agent/handover.js +1 -46
  33. package/dist/agent/images.js +7 -133
  34. package/dist/agent/index.js +1 -29
  35. package/dist/agent/mcp/bridge.js +1 -197
  36. package/dist/agent/mcp/extensions.js +5 -84
  37. package/dist/agent/mcp/oauth.js +2 -69
  38. package/dist/agent/mcp/registry.js +2 -33
  39. package/dist/agent/mcp/session-server.js +2 -45
  40. package/dist/agent/mcp/tools/ask-user.js +0 -6
  41. package/dist/agent/mcp/tools/await-resume.js +0 -15
  42. package/dist/agent/mcp/tools/goal.js +0 -25
  43. package/dist/agent/mcp/tools/types.js +0 -5
  44. package/dist/agent/mcp/tools/workspace.js +0 -23
  45. package/dist/agent/npm.js +0 -14
  46. package/dist/agent/session.js +86 -196
  47. package/dist/agent/skill-installer.js +3 -70
  48. package/dist/agent/skills.js +0 -43
  49. package/dist/agent/stream.js +5 -148
  50. package/dist/agent/types.js +0 -12
  51. package/dist/agent/utils.js +2 -120
  52. package/dist/bot/bot.js +3 -468
  53. package/dist/bot/command-ui.js +0 -45
  54. package/dist/bot/commands.js +0 -51
  55. package/dist/bot/headless-bot.js +0 -21
  56. package/dist/bot/host.js +1 -5
  57. package/dist/bot/human-loop.js +0 -8
  58. package/dist/bot/menu.js +0 -3
  59. package/dist/bot/orchestration.js +0 -3
  60. package/dist/bot/render-shared.js +0 -63
  61. package/dist/bot/session-hub.js +14 -92
  62. package/dist/bot/session-status.js +1 -5
  63. package/dist/bot/streaming.js +0 -53
  64. package/dist/browser-profile.js +0 -51
  65. package/dist/browser-supervisor.js +0 -75
  66. package/dist/catalog/cli-tools.js +0 -53
  67. package/dist/catalog/index.js +0 -18
  68. package/dist/catalog/local-models.js +0 -18
  69. package/dist/catalog/mcp-servers.js +0 -35
  70. package/dist/catalog/skill-repos.js +0 -32
  71. package/dist/channels/base.js +0 -19
  72. package/dist/channels/dingtalk/bot.js +0 -3
  73. package/dist/channels/dingtalk/channel.js +0 -33
  74. package/dist/channels/discord/bot.js +0 -7
  75. package/dist/channels/discord/channel.js +0 -22
  76. package/dist/channels/feishu/bot.js +0 -71
  77. package/dist/channels/feishu/channel.js +0 -78
  78. package/dist/channels/feishu/markdown.js +0 -5
  79. package/dist/channels/feishu/render.js +0 -45
  80. package/dist/channels/health.js +0 -46
  81. package/dist/channels/slack/bot.js +0 -7
  82. package/dist/channels/slack/channel.js +1 -27
  83. package/dist/channels/states.js +0 -3
  84. package/dist/channels/telegram/bot.js +0 -60
  85. package/dist/channels/telegram/channel.js +2 -99
  86. package/dist/channels/telegram/directory.js +0 -3
  87. package/dist/channels/telegram/live-preview.js +0 -8
  88. package/dist/channels/telegram/render.js +0 -30
  89. package/dist/channels/wecom/bot.js +0 -3
  90. package/dist/channels/wecom/channel.js +0 -58
  91. package/dist/channels/weixin/api.js +0 -9
  92. package/dist/channels/weixin/bot.js +0 -88
  93. package/dist/channels/weixin/channel.js +0 -6
  94. package/dist/cli/autostart.js +1 -44
  95. package/dist/cli/channel-supervisor.js +1 -59
  96. package/dist/cli/channels.js +0 -6
  97. package/dist/cli/main.js +1 -145
  98. package/dist/cli/onboarding.js +0 -6
  99. package/dist/cli/run.js +0 -11
  100. package/dist/cli/setup-wizard.js +0 -3
  101. package/dist/core/config/runtime-config.js +0 -38
  102. package/dist/core/config/user-config.js +0 -63
  103. package/dist/core/config/validation.js +0 -9
  104. package/dist/core/constants.js +0 -268
  105. package/dist/core/git.js +0 -31
  106. package/dist/core/legacy-compat.js +1 -28
  107. package/dist/core/logging.js +0 -3
  108. package/dist/core/platform.js +0 -28
  109. package/dist/core/process-control.js +0 -8
  110. package/dist/core/secrets/index.js +0 -17
  111. package/dist/core/secrets/inline-seal.js +0 -7
  112. package/dist/core/secrets/ref.js +0 -19
  113. package/dist/core/secrets/resolver.js +0 -8
  114. package/dist/core/secrets/store.js +1 -12
  115. package/dist/core/utils.js +1 -37
  116. package/dist/core/version.js +0 -3
  117. package/dist/dashboard/platform.js +1 -35
  118. package/dist/dashboard/routes/agents.js +2 -71
  119. package/dist/dashboard/routes/cli.js +0 -27
  120. package/dist/dashboard/routes/config.js +0 -54
  121. package/dist/dashboard/routes/extensions.js +0 -132
  122. package/dist/dashboard/routes/local-models.js +0 -68
  123. package/dist/dashboard/routes/models.js +0 -46
  124. package/dist/dashboard/routes/sessions.js +0 -100
  125. package/dist/dashboard/runtime.js +1 -58
  126. package/dist/dashboard/server.js +0 -49
  127. package/dist/dashboard/session-control.js +2 -69
  128. package/dist/model/catalog.js +1 -17
  129. package/dist/model/index.js +0 -15
  130. package/dist/model/injector.js +1 -181
  131. package/dist/model/provider-models.js +1 -48
  132. package/dist/model/responses-bridge.js +3 -45
  133. package/dist/model/store.js +0 -17
  134. package/dist/model/types.js +0 -12
  135. package/dist/model/validation.js +1 -18
  136. package/dist/pikichannel/adapter-pikiloom.js +0 -31
  137. package/dist/pikichannel/code.js +0 -12
  138. package/dist/pikichannel/codec.js +0 -14
  139. package/dist/pikichannel/host.js +4 -33
  140. package/dist/pikichannel/protocol.js +0 -52
  141. package/dist/pikichannel/rendezvous-broker.js +5 -26
  142. package/dist/pikichannel/rendezvous-host.js +3 -16
  143. package/dist/pikichannel/server.js +6 -55
  144. package/dist/pikichannel/transport.js +0 -25
  145. package/dist/pikichannel/transports/webrtc-host.js +0 -18
  146. package/dist/pikichannel/transports/webrtc-shared.js +4 -34
  147. package/dist/pikichannel/transports/websocket-host.js +1 -14
  148. package/dist/pikichannel/turn.js +2 -69
  149. package/dist/pikichannel/web/sdk.js +14 -71
  150. package/package.json +1 -1
  151. package/dashboard/dist/assets/ConnectionModal-Dt05H2f9.js +0 -1
  152. package/dashboard/dist/assets/SystemTab-CN_QrRKt.js +0 -1
  153. package/dashboard/dist/assets/index-CP3LYJR2.js +0 -23
  154. package/dashboard/dist/assets/index-C_neLnaO.js +0 -3
  155. package/dashboard/dist/assets/index-CyCspgtc.css +0 -1
@@ -1,17 +1,3 @@
1
- /**
2
- * MCP extension management — CRUD, catalog merge, health check, session merge.
3
- *
4
- * Global extensions live in ~/.pikiloom/setting.json under extensions.mcp.
5
- * Workspace extensions live in <workdir>/.mcp.json (standard format).
6
- *
7
- * getCatalogItems() produces the unified list the dashboard renders:
8
- * recommended-registry entries merged with installed entries, with a single
9
- * state field per item (recommended | needs_auth | disabled | ready | unhealthy).
10
- *
11
- * mergeExtensionsForSession() is called by bridge.ts before spawning an agent —
12
- * it resolves disabled flags, expands OAuth Bearer headers from the token store,
13
- * and hands the final config map to the agent CLI.
14
- */
15
1
  import fs from 'node:fs';
16
2
  import os from 'node:os';
17
3
  import path from 'node:path';
@@ -20,9 +6,6 @@ import { loadUserConfig, saveUserConfig } from '../../core/config/user-config.js
20
6
  import { terminateProcessTree } from '../../core/process-control.js';
21
7
  import { getRecommendedMcpServers, } from './registry.js';
22
8
  import { hasValidMcpToken, injectOAuthHeaders } from './oauth.js';
23
- // ---------------------------------------------------------------------------
24
- // Global extensions (setting.json)
25
- // ---------------------------------------------------------------------------
26
9
  export function loadGlobalMcpExtensions() {
27
10
  const config = loadUserConfig();
28
11
  const mcp = config.extensions?.mcp;
@@ -65,9 +48,6 @@ export function updateGlobalMcpExtension(name, patch) {
65
48
  });
66
49
  return true;
67
50
  }
68
- // ---------------------------------------------------------------------------
69
- // Workspace extensions (.mcp.json)
70
- // ---------------------------------------------------------------------------
71
51
  function workspaceMcpJsonPath(workdir) {
72
52
  return path.join(workdir, '.mcp.json');
73
53
  }
@@ -80,7 +60,7 @@ function readMcpJson(filePath) {
80
60
  return servers;
81
61
  }
82
62
  }
83
- catch { /* not found or invalid */ }
63
+ catch { }
84
64
  return {};
85
65
  }
86
66
  function writeMcpJson(filePath, servers) {
@@ -122,9 +102,6 @@ export function updateWorkspaceMcpExtension(workdir, name, patch) {
122
102
  writeMcpJson(mcpPath, servers);
123
103
  return true;
124
104
  }
125
- // ---------------------------------------------------------------------------
126
- // Unified listing
127
- // ---------------------------------------------------------------------------
128
105
  export function listAllMcpExtensions(workdir) {
129
106
  const global = loadGlobalMcpExtensions();
130
107
  const workspace = workdir ? loadWorkspaceMcpExtensions(workdir) : [];
@@ -138,9 +115,6 @@ export function listAllMcpExtensions(workdir) {
138
115
  }
139
116
  return [...global, ...workspace, ...claudeMcp];
140
117
  }
141
- // ---------------------------------------------------------------------------
142
- // Catalog — merged recommended + installed with state computation
143
- // ---------------------------------------------------------------------------
144
118
  function cmdSummary(config) {
145
119
  if (config.type === 'http' && config.url)
146
120
  return config.url;
@@ -148,13 +122,6 @@ function cmdSummary(config) {
148
122
  const args = (config.args || []).filter(a => a !== '-y');
149
123
  return [cmd, ...args].join(' ').trim();
150
124
  }
151
- /**
152
- * Generic @modelcontextprotocol/server-* demos that historically shipped in the
153
- * recommended list but were later removed (no product identity, overlap with
154
- * built-in agent capabilities like search/time). We hide them from the catalog
155
- * UI so old installs don't clutter the Connected section. The configs are kept
156
- * in setting.json untouched — users can still edit by hand if they want.
157
- */
158
125
  const HIDDEN_GENERIC_DEMO_PACKAGES = new Set([
159
126
  '@modelcontextprotocol/server-time',
160
127
  '@modelcontextprotocol/server-fetch',
@@ -198,23 +165,12 @@ function computeStateForInstalled(config, auth, id, unhealthyIds) {
198
165
  return 'unhealthy';
199
166
  return 'ready';
200
167
  }
201
- /**
202
- * Produce the unified catalog for the dashboard: every recommended registry
203
- * entry, plus any custom installed entries the user added, each with a
204
- * computed state field.
205
- *
206
- * When `scope` is provided, recommended entries are filtered to those whose
207
- * `recommendedScope` matches (or is `'both'`). Custom entries are filtered
208
- * by where they are installed — `scope: 'global'` excludes workspace entries
209
- * and vice versa.
210
- */
211
168
  export function getCatalogItems(opts = {}) {
212
169
  const recommended = getRecommendedMcpServers();
213
170
  const installed = [
214
171
  ...loadGlobalMcpExtensions(),
215
172
  ...(opts.workdir ? loadWorkspaceMcpExtensions(opts.workdir) : []),
216
173
  ];
217
- // Build lookup: catalogId -> installed entry (preferring global).
218
174
  const installedByCatalogId = new Map();
219
175
  const customEntries = [];
220
176
  for (const entry of installed) {
@@ -240,9 +196,6 @@ export function getCatalogItems(opts = {}) {
240
196
  return entry.scope === opts.scope;
241
197
  };
242
198
  const items = [];
243
- // Builtin items derive their installed/state from a top-level config flag
244
- // rather than `extensions.mcp`. Each catalogId maps to one flag — extend the
245
- // switch when adding a new builtin.
246
199
  const userConfig = loadUserConfig();
247
200
  const builtinInstalled = (catalogId) => {
248
201
  if (catalogId === 'pikiloom-browser')
@@ -251,7 +204,6 @@ export function getCatalogItems(opts = {}) {
251
204
  return userConfig.peekabooEnabled === true;
252
205
  return false;
253
206
  };
254
- // 1. Registry entries — preserve registry ordering.
255
207
  for (const rec of recommended) {
256
208
  if (!scopeMatchesRec(rec))
257
209
  continue;
@@ -265,8 +217,6 @@ export function getCatalogItems(opts = {}) {
265
217
  installed = builtinInstalled(rec.id);
266
218
  state = installed ? 'ready' : 'recommended';
267
219
  installedKey = installed ? rec.id : undefined;
268
- // Leave scope undefined: builtins aren't tied to global/workspace
269
- // storage, and the catalog UI's scope filter ignores items with no scope.
270
220
  scope = undefined;
271
221
  }
272
222
  else {
@@ -299,7 +249,6 @@ export function getCatalogItems(opts = {}) {
299
249
  isBuiltin: rec.isBuiltin,
300
250
  });
301
251
  }
302
- // 2. Custom entries — user-added servers not in the recommended registry.
303
252
  for (const entry of customEntries) {
304
253
  if (!scopeMatchesEntry(entry))
305
254
  continue;
@@ -331,10 +280,6 @@ export function getCatalogItems(opts = {}) {
331
280
  export function getCatalogItem(id, opts = {}) {
332
281
  return getCatalogItems(opts).find(i => i.id === id);
333
282
  }
334
- /**
335
- * Build an `McpServerConfig` from a recommended entry plus user-supplied
336
- * credentials. Used when installing a recommended server via the catalog flow.
337
- */
338
283
  export function buildInstalledConfigFromRecommended(rec, opts = { enabled: false }) {
339
284
  const creds = opts.credentials || {};
340
285
  if (rec.transport.type === 'stdio') {
@@ -355,8 +300,6 @@ export function buildInstalledConfigFromRecommended(rec, opts = { enabled: false
355
300
  }
356
301
  const headers = {};
357
302
  if (rec.auth.type === 'credentials') {
358
- // Convention: first non-empty credential becomes Authorization: Bearer <value>.
359
- // Matches how Stripe, Perplexity, and similar providers expect the token.
360
303
  const first = rec.auth.fields.find(f => creds[f.key]);
361
304
  if (first)
362
305
  headers.Authorization = `Bearer ${creds[first.key]}`;
@@ -369,18 +312,8 @@ export function buildInstalledConfigFromRecommended(rec, opts = { enabled: false
369
312
  catalogId: rec.id,
370
313
  };
371
314
  }
372
- // ---------------------------------------------------------------------------
373
- // Merge for session — called by bridge.ts
374
- // ---------------------------------------------------------------------------
375
- /**
376
- * Build the merged MCP server list for a session.
377
- * Priority (low → high): global → workspace .mcp.json → .claude/.mcp.json → ~/.claude/.mcp.json → builtins.
378
- * Disabled servers are filtered out. OAuth Bearer headers are injected for
379
- * any http-type server that has a valid token in the token store.
380
- */
381
315
  export function mergeExtensionsForSession(builtinServers, workdir) {
382
316
  const merged = {};
383
- // 1. Global extensions from setting.json (lowest priority)
384
317
  const userConfig = loadUserConfig();
385
318
  const globalMcp = userConfig.extensions?.mcp;
386
319
  if (globalMcp) {
@@ -406,7 +339,6 @@ export function mergeExtensionsForSession(builtinServers, workdir) {
406
339
  }
407
340
  }
408
341
  }
409
- // 2. Workspace .mcp.json files (overwrite global)
410
342
  if (workdir) {
411
343
  for (const candidate of [
412
344
  path.join(workdir, '.mcp.json'),
@@ -428,10 +360,9 @@ export function mergeExtensionsForSession(builtinServers, workdir) {
428
360
  }
429
361
  }
430
362
  }
431
- catch { /* skip */ }
363
+ catch { }
432
364
  }
433
365
  }
434
- // 3. Built-in servers (highest priority)
435
366
  for (const server of builtinServers) {
436
367
  merged[server.name] = {
437
368
  type: 'stdio',
@@ -440,7 +371,6 @@ export function mergeExtensionsForSession(builtinServers, workdir) {
440
371
  ...(server.env ? { env: server.env } : {}),
441
372
  };
442
373
  }
443
- // Filter out any remaining disabled entries
444
374
  for (const [name, cfg] of Object.entries(merged)) {
445
375
  if (cfg?.disabled === true || cfg?.enabled === false) {
446
376
  delete merged[name];
@@ -448,15 +378,6 @@ export function mergeExtensionsForSession(builtinServers, workdir) {
448
378
  }
449
379
  return merged;
450
380
  }
451
- /**
452
- * Convert global + workspace extensions to RegisteredMcpServer[] for Codex
453
- * and Gemini agents that consume server arrays instead of merged configs.
454
- *
455
- * Supports both stdio and HTTP transports. For HTTP entries, OAuth Bearer
456
- * headers are injected from the token store (same path as
457
- * mergeExtensionsForSession), so a one-time global authorization carries
458
- * across every workspace.
459
- */
460
381
  export function getGlobalExtensionsAsServers(workdir) {
461
382
  const merged = new Map();
462
383
  const toEntry = (name, cfg) => {
@@ -615,7 +536,7 @@ export async function checkMcpHealth(config, timeoutMs = 10_000) {
615
536
  try {
616
537
  child.stdin?.write(toolsHeader + toolsRequest);
617
538
  }
618
- catch { /* best-effort */ }
539
+ catch { }
619
540
  setTimeout(() => {
620
541
  stopChildTree();
621
542
  const tools = [];
@@ -636,11 +557,11 @@ export async function checkMcpHealth(config, timeoutMs = 10_000) {
636
557
  tools.push(tool.name);
637
558
  }
638
559
  }
639
- catch { /* try next match */ }
560
+ catch { }
640
561
  }
641
562
  }
642
563
  }
643
- catch { /* best effort */ }
564
+ catch { }
644
565
  finish({ ok: true, tools: tools.length ? tools : undefined, elapsedMs: Date.now() - start });
645
566
  }, 1500);
646
567
  }, 100);
@@ -1,22 +1,5 @@
1
- /**
2
- * MCP OAuth 2.1 + Dynamic Client Registration subsystem.
3
- *
4
- * Implements the MCP auth spec flow for remote HTTP MCP servers:
5
- * 1. Discover auth server (Protected Resource Metadata + WWW-Authenticate header).
6
- * 2. Fetch Authorization Server Metadata.
7
- * 3. Register client dynamically (RFC 7591) if no pre-registered client_id.
8
- * 4. Drive authorization_code + PKCE — returns auth URL for user's browser.
9
- * 5. Exchange code for tokens on callback.
10
- * 6. Refresh tokens when expired.
11
- *
12
- * Tokens are persisted via the token store (setting.json `extensions.mcpTokens`).
13
- * State for in-flight authorizations is kept in memory only.
14
- */
15
1
  import crypto from 'node:crypto';
16
2
  import { loadUserConfig, saveUserConfig, } from '../../core/config/user-config.js';
17
- // ---------------------------------------------------------------------------
18
- // Token store (setting.json-backed)
19
- // ---------------------------------------------------------------------------
20
3
  export function getMcpToken(serverId) {
21
4
  const cfg = loadUserConfig();
22
5
  return cfg.extensions?.mcpTokens?.[serverId];
@@ -45,18 +28,12 @@ export function hasValidMcpToken(serverId) {
45
28
  return false;
46
29
  return true;
47
30
  }
48
- // ---------------------------------------------------------------------------
49
- // PKCE helpers
50
- // ---------------------------------------------------------------------------
51
31
  function randomUrlSafe(bytes) {
52
32
  return crypto.randomBytes(bytes).toString('base64url');
53
33
  }
54
34
  function pkceChallenge(verifier) {
55
35
  return crypto.createHash('sha256').update(verifier).digest('base64url');
56
36
  }
57
- // ---------------------------------------------------------------------------
58
- // Discovery
59
- // ---------------------------------------------------------------------------
60
37
  async function fetchJson(url, init, timeoutMs = 8_000) {
61
38
  const controller = new AbortController();
62
39
  const timer = setTimeout(() => controller.abort(), timeoutMs);
@@ -70,17 +47,6 @@ async function fetchJson(url, init, timeoutMs = 8_000) {
70
47
  clearTimeout(timer);
71
48
  }
72
49
  }
73
- /**
74
- * Discover the authorization server for a remote MCP resource and the
75
- * canonical `resource` indicator the AS will validate against.
76
- *
77
- * Strategy:
78
- * 1. Try GET {resourceBase}/.well-known/oauth-protected-resource (RFC 9728).
79
- * 2. Fall back to probing the MCP endpoint for a 401 with WWW-Authenticate
80
- * and follow the resource_metadata link.
81
- * 3. Last resort: treat the origin as the AS and the input URL as the
82
- * canonical resource.
83
- */
84
50
  async function discoverAuthorizationServer(resourceUrl) {
85
51
  const origin = new URL(resourceUrl).origin;
86
52
  const adopt = (meta) => {
@@ -92,15 +58,13 @@ async function discoverAuthorizationServer(resourceUrl) {
92
58
  canonicalResource: meta.resource || resourceUrl,
93
59
  };
94
60
  };
95
- // Strategy 1: Protected Resource Metadata at the origin
96
61
  try {
97
62
  const meta = await fetchJson(`${origin}/.well-known/oauth-protected-resource`);
98
63
  const out = adopt(meta);
99
64
  if (out)
100
65
  return out;
101
66
  }
102
- catch { /* try next */ }
103
- // Strategy 2: probe resource for WWW-Authenticate, follow resource_metadata
67
+ catch { }
104
68
  try {
105
69
  const controller = new AbortController();
106
70
  const timer = setTimeout(() => controller.abort(), 8_000);
@@ -113,9 +77,6 @@ async function discoverAuthorizationServer(resourceUrl) {
113
77
  clearTimeout(timer);
114
78
  if (res.status === 401) {
115
79
  const auth = res.headers.get('www-authenticate') || '';
116
- // A header may declare resource_metadata twice; prefer the most specific
117
- // (last) match since servers commonly emit both origin-level and
118
- // per-resource paths in one header.
119
80
  const matches = [...auth.matchAll(/resource_metadata="([^"]+)"/gi)];
120
81
  const link = matches.length ? matches[matches.length - 1][1] : null;
121
82
  if (link) {
@@ -126,12 +87,10 @@ async function discoverAuthorizationServer(resourceUrl) {
126
87
  }
127
88
  }
128
89
  }
129
- catch { /* fall through */ }
130
- // Last resort: assume the resource origin is itself the authorization server
90
+ catch { }
131
91
  return { authorizationServer: origin, canonicalResource: resourceUrl };
132
92
  }
133
93
  async function fetchAuthorizationServerMetadata(issuer) {
134
- // Try OAuth AS metadata first (RFC 8414), then OIDC fallback
135
94
  const candidates = [
136
95
  `${issuer.replace(/\/$/, '')}/.well-known/oauth-authorization-server`,
137
96
  `${issuer.replace(/\/$/, '')}/.well-known/openid-configuration`,
@@ -183,9 +142,6 @@ async function resolveEndpoints(auth, resourceUrl) {
183
142
  canonicalResource: discovered.canonicalResource,
184
143
  };
185
144
  }
186
- // ---------------------------------------------------------------------------
187
- // Pending state (in-memory, short-lived)
188
- // ---------------------------------------------------------------------------
189
145
  const pendingFlows = new Map();
190
146
  const PENDING_TTL_MS = 10 * 60 * 1000;
191
147
  function sweepPending() {
@@ -195,18 +151,6 @@ function sweepPending() {
195
151
  pendingFlows.delete(state);
196
152
  }
197
153
  }
198
- // ---------------------------------------------------------------------------
199
- // Public API: start authorization, handle callback, refresh
200
- // ---------------------------------------------------------------------------
201
- /**
202
- * Kick off an OAuth flow for the given MCP server.
203
- *
204
- * @param serverId The catalog id / installed-extension key.
205
- * @param auth The auth spec from registry.
206
- * @param resourceUrl The MCP server URL (http transport url).
207
- * @param redirectUri Callback URL (http://localhost:<port>/api/extensions/mcp/oauth/callback).
208
- * @param clientName Human-readable client name shown to the provider.
209
- */
210
154
  export async function startAuthorization(opts) {
211
155
  sweepPending();
212
156
  const { serverId, auth, resourceUrl, redirectUri, clientName } = opts;
@@ -254,9 +198,6 @@ export async function startAuthorization(opts) {
254
198
  });
255
199
  return { authUrl, state };
256
200
  }
257
- /**
258
- * Exchange the authorization code for tokens and persist the result.
259
- */
260
201
  export async function completeAuthorization(opts) {
261
202
  const flow = pendingFlows.get(opts.state);
262
203
  if (!flow)
@@ -298,10 +239,6 @@ export async function completeAuthorization(opts) {
298
239
  saveMcpToken(flow.serverId, record);
299
240
  return { ok: true, serverId: flow.serverId };
300
241
  }
301
- /**
302
- * Refresh an expired token. Returns the updated record or null if refresh failed
303
- * (e.g., no refresh_token available, or refresh rejected).
304
- */
305
242
  export async function refreshMcpToken(serverId) {
306
243
  const token = getMcpToken(serverId);
307
244
  if (!token?.refreshToken)
@@ -338,10 +275,6 @@ export async function refreshMcpToken(serverId) {
338
275
  return null;
339
276
  }
340
277
  }
341
- /**
342
- * Inject Authorization: Bearer headers for remote MCP configs that have a token.
343
- * Called by bridge.ts before handing merged config to agents.
344
- */
345
278
  export function injectOAuthHeaders(name, config) {
346
279
  const token = getMcpToken(name);
347
280
  if (!token?.accessToken)
@@ -1,30 +1,6 @@
1
- /**
2
- * MCP extension registry — curated recommended servers, recommended skills, community search.
3
- *
4
- * Schema supports three transport × auth combinations:
5
- * - stdio + none (local, zero-config)
6
- * - stdio + credentials (local, needs API key/token)
7
- * - http + none (rare)
8
- * - http + credentials (remote with API key)
9
- * - http + mcp-oauth (remote SaaS via MCP OAuth spec)
10
- *
11
- * For MCP-OAuth servers, `authorizationEndpoint` / `tokenEndpoint` can be
12
- * pre-declared (fast path) or omitted (discovered via MCP Protected Resource
13
- * Metadata at `<url>/.well-known/oauth-protected-resource`).
14
- */
15
- // ---------------------------------------------------------------------------
16
- // Recommended MCP servers + skill repos — data lives in src/catalog/
17
- //
18
- // This module owns the *types* and helper functions. Edit
19
- // `src/catalog/mcp-servers.ts` and `src/catalog/skill-repos.ts` to add or hide
20
- // entries; the arrays below are just pointers back at that catalog.
21
- // ---------------------------------------------------------------------------
22
1
  import { MCP_SERVERS, SKILL_REPOS } from '../../catalog/index.js';
23
2
  const RECOMMENDED_MCP_SERVERS = MCP_SERVERS;
24
3
  const RECOMMENDED_SKILL_REPOS = SKILL_REPOS;
25
- // ---------------------------------------------------------------------------
26
- // Public API
27
- // ---------------------------------------------------------------------------
28
4
  export function getRecommendedMcpServers() {
29
5
  return RECOMMENDED_MCP_SERVERS;
30
6
  }
@@ -34,10 +10,6 @@ export function getRecommendedMcpServer(id) {
34
10
  export function getRecommendedSkillRepos() {
35
11
  return RECOMMENDED_SKILL_REPOS;
36
12
  }
37
- /**
38
- * Search the official MCP Registry API for servers.
39
- * Falls back to npm search if the registry is unreachable.
40
- */
41
13
  export async function searchMcpServers(query, limit = 20) {
42
14
  if (!query.trim())
43
15
  return [];
@@ -87,12 +59,9 @@ export async function searchMcpServers(query, limit = 20) {
87
59
  }
88
60
  }
89
61
  }
90
- catch { /* fallback failed */ }
62
+ catch { }
91
63
  return [];
92
64
  }
93
- /**
94
- * Search for skills via npm search (`agent skill` keywords).
95
- */
96
65
  export async function searchSkills(query, limit = 20) {
97
66
  if (!query.trim())
98
67
  return [];
@@ -114,6 +83,6 @@ export async function searchSkills(query, limit = 20) {
114
83
  })).filter((s) => s.name);
115
84
  }
116
85
  }
117
- catch { /* unreachable */ }
86
+ catch { }
118
87
  return [];
119
88
  }
@@ -1,29 +1,9 @@
1
- /**
2
- * mcp-session-server.ts — MCP server process for pikiloom session bridge.
3
- *
4
- * Spawned by the agent CLI (claude/codex/gemini) via --mcp-config or codex mcp add.
5
- * Communicates with the agent over stdio using the MCP protocol (JSON-RPC 2.0).
6
- *
7
- * Supports two stdio transports (auto-detected from first byte):
8
- * - Content-Length framing (Claude, Gemini — standard MCP/LSP)
9
- * - Newline-delimited JSON (Codex)
10
- *
11
- * Context is injected via environment variables:
12
- * MCP_WORKSPACE_PATH — absolute path to the session workspace
13
- * MCP_STAGED_FILES — JSON array of staged file relative paths
14
- * MCP_CALLBACK_URL — HTTP URL for the pikiloom callback server
15
- *
16
- * Tools are defined in src/tools/ — each module exports definitions + handlers.
17
- */
18
1
  import path from 'node:path';
19
2
  import { createRetainedLogSink, writeScopedLog } from '../../core/logging.js';
20
3
  import { workspaceTools } from './tools/workspace.js';
21
4
  import { goalTools } from './tools/goal.js';
22
5
  import { awaitResumeTools } from './tools/await-resume.js';
23
6
  import { askUserTools } from './tools/ask-user.js';
24
- // ---------------------------------------------------------------------------
25
- // Logging — writes to stderr + file so it doesn't interfere with stdio MCP transport
26
- // ---------------------------------------------------------------------------
27
7
  const _logSink = (() => {
28
8
  try {
29
9
  const ws = process.env.MCP_WORKSPACE_PATH || '';
@@ -53,9 +33,6 @@ function summarizeArgs(args, max = 200) {
53
33
  return '{}';
54
34
  return text.length <= max ? text : `${text.slice(0, Math.max(0, max - 3)).trimEnd()}...`;
55
35
  }
56
- // ---------------------------------------------------------------------------
57
- // Context from environment
58
- // ---------------------------------------------------------------------------
59
36
  const ctx = {
60
37
  workspace: process.env.MCP_WORKSPACE_PATH || '',
61
38
  workdir: process.env.MCP_WORKDIR || undefined,
@@ -70,35 +47,21 @@ const ctx = {
70
47
  callbackUrl: process.env.MCP_CALLBACK_URL || '',
71
48
  };
72
49
  log(`started workspace=${ctx.workspace} stagedFiles=${ctx.stagedFiles.length} callbackUrl=${ctx.callbackUrl ? 'set' : 'MISSING'}`);
73
- // ---------------------------------------------------------------------------
74
- // Tool registry — collect all tool modules
75
- // ---------------------------------------------------------------------------
76
- // `MCP_TOOLS_AVAILABLE` lists tool families the bridge has wired up. Codex
77
- // has a native `/goal` implementation and native user-input, so it skips
78
- // `goalTools` and never receives `ask-user` via the bridge.
79
50
  const AVAILABLE = new Set((process.env.MCP_TOOLS_AVAILABLE || '').split(',').map(s => s.trim()).filter(Boolean));
80
51
  const IS_CODEX = process.env.MCP_AGENT === 'codex';
81
52
  const TOOL_MODULES = [
82
53
  ...(AVAILABLE.has('workspace') ? [workspaceTools] : []),
83
54
  ...(IS_CODEX ? [] : [goalTools]),
84
- // Codex parks/resumes via its own native goal machinery; the pikiloom
85
- // awaiting marker is for the `claude -p`-style drivers whose turn process
86
- // exits at `result`.
87
55
  ...(IS_CODEX ? [] : [awaitResumeTools]),
88
56
  ...(AVAILABLE.has('ask-user') ? [askUserTools] : []),
89
57
  ];
90
58
  const ALL_TOOLS = TOOL_MODULES.flatMap(m => m.tools);
91
- /** Lookup: tool name → module that handles it. */
92
59
  const TOOL_HANDLERS = new Map();
93
60
  for (const mod of TOOL_MODULES) {
94
61
  for (const t of mod.tools) {
95
62
  TOOL_HANDLERS.set(t.name, mod);
96
63
  }
97
64
  }
98
- // ---------------------------------------------------------------------------
99
- // MCP protocol — auto-detect transport format
100
- // ---------------------------------------------------------------------------
101
- /** 'framed' = Content-Length (Claude/Gemini), 'ndjson' = newline-delimited (Codex) */
102
65
  let transport = null;
103
66
  function send(msg) {
104
67
  const body = JSON.stringify(msg);
@@ -115,9 +78,6 @@ function respond(id, result) {
115
78
  function respondError(id, code, message) {
116
79
  send({ jsonrpc: '2.0', id, error: { code, message } });
117
80
  }
118
- // ---------------------------------------------------------------------------
119
- // Stdio reader — auto-detecting Content-Length framed vs NDJSON
120
- // ---------------------------------------------------------------------------
121
81
  let buffer = '';
122
82
  function processFramed() {
123
83
  while (true) {
@@ -139,7 +99,7 @@ function processFramed() {
139
99
  try {
140
100
  handleMessage(JSON.parse(body));
141
101
  }
142
- catch { /* ignore parse errors */ }
102
+ catch { }
143
103
  }
144
104
  }
145
105
  function processNdjson() {
@@ -154,7 +114,7 @@ function processNdjson() {
154
114
  try {
155
115
  handleMessage(JSON.parse(line));
156
116
  }
157
- catch { /* ignore parse errors */ }
117
+ catch { }
158
118
  }
159
119
  }
160
120
  function processBuffer() {
@@ -175,9 +135,6 @@ process.stdin.on('data', (chunk) => {
175
135
  processBuffer();
176
136
  });
177
137
  process.stdin.on('end', () => process.exit(0));
178
- // ---------------------------------------------------------------------------
179
- // Message dispatcher
180
- // ---------------------------------------------------------------------------
181
138
  function handleMessage(msg) {
182
139
  const { id, method, params } = msg;
183
140
  switch (method) {
@@ -1,8 +1,3 @@
1
- /**
2
- * tools/ask-user.ts — im_ask_user: block the current turn and route a question
3
- * to the user via IM or dashboard. Used in place of Claude's native
4
- * `AskUserQuestion`, which is unusable in `-p` mode.
5
- */
6
1
  import http from 'node:http';
7
2
  import { toolResult, toolLog } from './types.js';
8
3
  const tools = [
@@ -97,7 +92,6 @@ function callbackAskUser(callbackUrl, body) {
97
92
  });
98
93
  });
99
94
  req.on('error', e => reject(e));
100
- // User reply has no upper bound — never time out the socket.
101
95
  req.setTimeout(0);
102
96
  req.write(payload);
103
97
  req.end();
@@ -1,18 +1,3 @@
1
- /**
2
- * tools/await-resume.ts — "waiting for background work" marker MCP tool.
3
- *
4
- * await_background — Lets the model declare that this turn is ending while
5
- * detached/background work it launched keeps running, and
6
- * it intends to report back later. Purely a UI hint: it
7
- * changes nothing about execution, it only lets the
8
- * dashboard show a "waiting" state instead of "completed"
9
- * for the interval until the session next runs.
10
- *
11
- * State lives at <sessionRoot>/awaiting.json; this server resolves the session
12
- * root from MCP_WORKSPACE_PATH (which points to <sessionRoot>/workspace). The
13
- * parent reads & clears it (see agent/await-resume.ts) — clearing happens
14
- * automatically the next time the session runs.
15
- */
16
1
  import fs from 'node:fs';
17
2
  import path from 'node:path';
18
3
  import { toolResult, toolLog } from './types.js';
@@ -1,22 +1,6 @@
1
- /**
2
- * tools/goal.ts — Persistent thread goal MCP tools.
3
- *
4
- * goal_get — Returns the current goal (objective, status, usage, budget).
5
- * goal_update — Lets the model mark the goal complete after a completion audit.
6
- * The schema only accepts `status: "complete"`. Pause / resume /
7
- * clear / budget_limited transitions are user- or runtime-controlled
8
- * and not exposed to the model.
9
- *
10
- * Goal state lives at <sessionRoot>/goal.json; this server resolves the session
11
- * root from MCP_WORKSPACE_PATH (which points to <sessionRoot>/workspace).
12
- */
13
1
  import fs from 'node:fs';
14
2
  import path from 'node:path';
15
3
  import { toolResult, toolLog } from './types.js';
16
- // ---------------------------------------------------------------------------
17
- // Tool definitions — descriptions match Codex's analogous `get_goal` / `update_goal`
18
- // behaviour but use the pikiloom tool name.
19
- // ---------------------------------------------------------------------------
20
4
  const tools = [
21
5
  {
22
6
  name: 'goal_get',
@@ -46,9 +30,6 @@ const tools = [
46
30
  },
47
31
  },
48
32
  ];
49
- // ---------------------------------------------------------------------------
50
- // Path resolution — workspace → session root → goal.json
51
- // ---------------------------------------------------------------------------
52
33
  function sessionRootFromCtx(ctx) {
53
34
  const workspace = path.resolve(ctx.workspace || '');
54
35
  if (!workspace)
@@ -91,9 +72,6 @@ function serialize(goal) {
91
72
  updated_at: goal.updatedAt,
92
73
  };
93
74
  }
94
- // ---------------------------------------------------------------------------
95
- // Handlers
96
- // ---------------------------------------------------------------------------
97
75
  function handleGoalGet(ctx) {
98
76
  const file = goalPathFromCtx(ctx);
99
77
  toolLog('goal_get', `file=${file || '(unresolved)'}`);
@@ -129,9 +107,6 @@ function handleGoalUpdate(args, ctx) {
129
107
  : `tokens used: ${goal.tokensUsed}`,
130
108
  }, null, 2));
131
109
  }
132
- // ---------------------------------------------------------------------------
133
- // Module export
134
- // ---------------------------------------------------------------------------
135
110
  export const goalTools = {
136
111
  tools,
137
112
  handle(name, args, ctx) {
@@ -1,12 +1,7 @@
1
- /**
2
- * MCP tool type definitions and helper utilities.
3
- */
4
1
  import { writeScopedLog } from '../../../core/logging.js';
5
- /** Helper to build a text tool result. */
6
2
  export function toolResult(text, isError = false) {
7
3
  return { content: [{ type: 'text', text }], ...(isError ? { isError: true } : {}) };
8
4
  }
9
- /** Shared logger for tool modules — writes to stderr to avoid interfering with stdio MCP transport. */
10
5
  export function toolLog(tool, msg) {
11
6
  writeScopedLog(`tool:${tool}`, msg, { level: 'debug', stream: 'stderr' });
12
7
  }