@schoolai/shipyard 3.7.0 → 3.8.0-rc.20260529.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 (104) hide show
  1. package/dist/{auth-SS7LV5XK.js → auth-EXHO3AG5.js} +4 -4
  2. package/dist/capability-detector-worker.js +142 -0
  3. package/dist/capability-detector-worker.js.map +1 -0
  4. package/dist/{chunk-DKMDBOFU.js → chunk-2CNIEBKO.js} +21 -11
  5. package/dist/chunk-2CNIEBKO.js.map +1 -0
  6. package/dist/chunk-4T2OQAVL.js +51 -0
  7. package/dist/chunk-4T2OQAVL.js.map +1 -0
  8. package/dist/chunk-5ER6ZHA2.js +46 -0
  9. package/dist/chunk-5ER6ZHA2.js.map +1 -0
  10. package/dist/chunk-7LSEE26O.js +24227 -0
  11. package/dist/chunk-7LSEE26O.js.map +1 -0
  12. package/dist/{chunk-7AHRFPAL.js → chunk-7YOU7MBN.js} +183 -17
  13. package/dist/chunk-7YOU7MBN.js.map +1 -0
  14. package/dist/chunk-CMGJGK6R.js +382 -0
  15. package/dist/chunk-CMGJGK6R.js.map +1 -0
  16. package/dist/{chunk-VPMN47TL.js → chunk-CNR7O5YH.js} +1 -2
  17. package/dist/{chunk-2J3WSIAF.js → chunk-EF2DAODF.js} +18 -3
  18. package/dist/chunk-EF2DAODF.js.map +1 -0
  19. package/dist/chunk-HQ43PHOH.js +1203 -0
  20. package/dist/chunk-HQ43PHOH.js.map +1 -0
  21. package/dist/chunk-KITSAHTX.js +134 -0
  22. package/dist/chunk-KITSAHTX.js.map +1 -0
  23. package/dist/chunk-LESHN5J5.js +6898 -0
  24. package/dist/chunk-LESHN5J5.js.map +1 -0
  25. package/dist/{chunk-LW2MS4T5.js → chunk-LMJFHKRD.js} +15 -12
  26. package/dist/chunk-LMJFHKRD.js.map +1 -0
  27. package/dist/{chunk-SNYEQHUK.js → chunk-NACJENDW.js} +14 -21
  28. package/dist/chunk-NACJENDW.js.map +1 -0
  29. package/dist/{chunk-IISLTKYY.js → chunk-TU63KZFW.js} +2 -2
  30. package/dist/chunk-TX6DK4PK.js +186 -0
  31. package/dist/chunk-TX6DK4PK.js.map +1 -0
  32. package/dist/chunk-UQVXWOPT.js +48 -0
  33. package/dist/chunk-UQVXWOPT.js.map +1 -0
  34. package/dist/{chunk-3MNPDCO5.js → chunk-WBB4XHLH.js} +139 -140
  35. package/dist/chunk-WBB4XHLH.js.map +1 -0
  36. package/dist/chunk-X3MULCV5.js +11 -0
  37. package/dist/chunk-X3MULCV5.js.map +1 -0
  38. package/dist/chunk-YZ3Z3ZYI.js +787 -0
  39. package/dist/chunk-YZ3Z3ZYI.js.map +1 -0
  40. package/dist/{chunk-2UN5AR7V.js → chunk-ZAOPND5G.js} +2 -2
  41. package/dist/chunk-ZFKJAYAN.js +542 -0
  42. package/dist/chunk-ZFKJAYAN.js.map +1 -0
  43. package/dist/cursor-hook-shim.js +316 -0
  44. package/dist/cursor-hook-shim.js.map +1 -0
  45. package/dist/cursor-runner.js +358 -0
  46. package/dist/cursor-runner.js.map +1 -0
  47. package/dist/electron-utility.js +111 -0
  48. package/dist/electron-utility.js.map +1 -0
  49. package/dist/git-pool-V73Q53NX.js +18 -0
  50. package/dist/{git-repo-VRT57DGC.js → git-repo-TN3VZXQV.js} +9 -6
  51. package/dist/index.js +12 -12
  52. package/dist/index.js.map +1 -1
  53. package/dist/{logger-GQCSLSZH.js → logger-QHPTO22N.js} +4 -4
  54. package/dist/login-Q7SZI7JJ.js +20 -0
  55. package/dist/{logout-VUNCW5B2.js → logout-O4AVMO5S.js} +6 -6
  56. package/dist/mcp-servers-F64M5T4I.js +24 -0
  57. package/dist/{roi-Y3MX5UW4.js → roi-EYDLPOCS.js} +5 -5
  58. package/dist/rss-worker.js +159 -0
  59. package/dist/rss-worker.js.map +1 -0
  60. package/dist/{serve-O53FNK64.js → serve-6A7RJWEF.js} +89862 -102999
  61. package/dist/{serve-O53FNK64.js.map → serve-6A7RJWEF.js.map} +1 -1
  62. package/dist/skills-ZHEPSBHW.js +11 -0
  63. package/dist/{start-IDFDHRD6.js → start-YGYYIK53.js} +229 -27
  64. package/dist/start-YGYYIK53.js.map +1 -0
  65. package/dist/vault-crypto-BKDOA65F.js +13 -0
  66. package/dist/vault-crypto-BKDOA65F.js.map +1 -0
  67. package/dist/worker.js +6 -3
  68. package/dist/worker.js.map +1 -1
  69. package/package.json +17 -10
  70. package/dist/chunk-2J3WSIAF.js.map +0 -1
  71. package/dist/chunk-3MNPDCO5.js.map +0 -1
  72. package/dist/chunk-66OBOZ3X.js +0 -79
  73. package/dist/chunk-66OBOZ3X.js.map +0 -1
  74. package/dist/chunk-7AHRFPAL.js.map +0 -1
  75. package/dist/chunk-DKMDBOFU.js.map +0 -1
  76. package/dist/chunk-L2WQMPWS.js +0 -666
  77. package/dist/chunk-L2WQMPWS.js.map +0 -1
  78. package/dist/chunk-LW2MS4T5.js.map +0 -1
  79. package/dist/chunk-PI77CUEP.js +0 -49
  80. package/dist/chunk-PI77CUEP.js.map +0 -1
  81. package/dist/chunk-RXI4637N.js +0 -395
  82. package/dist/chunk-RXI4637N.js.map +0 -1
  83. package/dist/chunk-SNYEQHUK.js.map +0 -1
  84. package/dist/chunk-VBPHGPBR.js +0 -126
  85. package/dist/chunk-VBPHGPBR.js.map +0 -1
  86. package/dist/index.d.ts +0 -2
  87. package/dist/login-L4BBPUYO.js +0 -20
  88. package/dist/mcp-servers-MXS5VAWI.js +0 -18
  89. package/dist/shell-V36EX2IJ.js +0 -27
  90. package/dist/skills-GPGRNV4R.js +0 -9
  91. package/dist/start-IDFDHRD6.js.map +0 -1
  92. package/dist/worker.d.ts +0 -49
  93. /package/dist/{auth-SS7LV5XK.js.map → auth-EXHO3AG5.js.map} +0 -0
  94. /package/dist/{chunk-VPMN47TL.js.map → chunk-CNR7O5YH.js.map} +0 -0
  95. /package/dist/{chunk-IISLTKYY.js.map → chunk-TU63KZFW.js.map} +0 -0
  96. /package/dist/{chunk-2UN5AR7V.js.map → chunk-ZAOPND5G.js.map} +0 -0
  97. /package/dist/{git-repo-VRT57DGC.js.map → git-pool-V73Q53NX.js.map} +0 -0
  98. /package/dist/{logger-GQCSLSZH.js.map → git-repo-TN3VZXQV.js.map} +0 -0
  99. /package/dist/{login-L4BBPUYO.js.map → logger-QHPTO22N.js.map} +0 -0
  100. /package/dist/{mcp-servers-MXS5VAWI.js.map → login-Q7SZI7JJ.js.map} +0 -0
  101. /package/dist/{logout-VUNCW5B2.js.map → logout-O4AVMO5S.js.map} +0 -0
  102. /package/dist/{shell-V36EX2IJ.js.map → mcp-servers-F64M5T4I.js.map} +0 -0
  103. /package/dist/{roi-Y3MX5UW4.js.map → roi-EYDLPOCS.js.map} +0 -0
  104. /package/dist/{skills-GPGRNV4R.js.map → skills-ZHEPSBHW.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/shared/capabilities/mcp-servers.ts","../src/shared/mcp/claude-code-credentials.ts","../src/shared/mcp/schemas.ts","../src/shared/mcp/resolve-servers.ts","../src/shared/auth/anthropic-credentials.ts","../src/shared/capabilities/account-integrations.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport type { AnthropicAuthMethod } from '@shipyard/loro-schema';\nimport type { GitRepoInfo, MCPServerInfo, MCPServerSource } from '@shipyard/session';\nimport { z } from 'zod';\nimport { readClaudeCodeCredentials } from '../mcp/claude-code-credentials.js';\nimport { VAULT_REF_PATTERN } from '../mcp/resolve-servers.js';\nimport { fetchClaudeAiIntegrations } from './account-integrations.js';\n\nconst MCPStdioEntrySchema = z\n .object({\n type: z.literal('stdio').optional(),\n command: z.string(),\n args: z.array(z.string()).optional(),\n env: z.record(z.string(), z.string()).optional(),\n })\n .passthrough();\n\nconst MCPOAuthConfigSchema = z\n .object({\n clientId: z.string().optional(),\n callbackPort: z.number().optional(),\n })\n .passthrough();\n\nconst MCPHttpEntrySchema = z\n .object({\n type: z.literal('http'),\n url: z.string(),\n headers: z.record(z.string(), z.string()).optional(),\n oauth: MCPOAuthConfigSchema.optional(),\n })\n .passthrough();\n\nconst MCPSSEEntrySchema = z\n .object({\n type: z.literal('sse'),\n url: z.string(),\n headers: z.record(z.string(), z.string()).optional(),\n })\n .passthrough();\n\nconst MCPServerEntrySchema = z.union([MCPStdioEntrySchema, MCPHttpEntrySchema, MCPSSEEntrySchema]);\n\n/**\n * Pure decision: should `fetchClaudeAiIntegrations` (the claude.ai Connectors\n * fetch) actually run for this user?\n *\n * Only the claude.ai OAuth method has access to that endpoint. `null` or\n * `undefined` (preference unknown — pre-backfill or caller didn't plumb the\n * value through) returns true to preserve the legacy behaviour for OAuth\n * users during the rollout window before the boot backfill writes a real\n * value. After backfill, `null` should be vanishingly rare.\n */\nexport function shouldFetchClaudeAiIntegrations(\n preferredAuth: AnthropicAuthMethod | null | undefined\n): boolean {\n if (preferredAuth == null) return true;\n return preferredAuth === 'claude-ai';\n}\n\nconst SECRET_PATTERNS = /^(sk-|ghp_|gho_|glpat-|xoxb-|xoxp-|Bearer\\s|token\\s)/i;\nconst SECRET_FLAGS = new Set(['--api-key', '--token', '--secret', '--password', '--key', '-k']);\n\nexport function redactArgs(args: string[]): string[] {\n return args.map((arg, i) => {\n if (SECRET_PATTERNS.test(arg)) return '***';\n const prevArg = i > 0 ? args[i - 1] : undefined;\n if (prevArg && SECRET_FLAGS.has(prevArg)) return '***';\n const eqIdx = arg.indexOf('=');\n if (eqIdx > 0 && SECRET_FLAGS.has(arg.slice(0, eqIdx))) {\n return `${arg.slice(0, eqIdx + 1)}***`;\n }\n return arg;\n });\n}\n\n/**\n * Redact all environment variable values. Env vars passed to MCP servers\n * frequently contain secrets (API keys, tokens) and should never be\n * exposed to the browser.\n */\nexport function redactEnv(env: Record<string, string>): Record<string, string> {\n const result: Record<string, string> = {};\n for (const key of Object.keys(env)) {\n result[key] = '***';\n }\n return result;\n}\n\ntype MCPStdioEntry = z.infer<typeof MCPStdioEntrySchema>;\ntype MCPHttpEntry = z.infer<typeof MCPHttpEntrySchema>;\ntype MCPSSEEntry = z.infer<typeof MCPSSEEntrySchema>;\n\nfunction isHttpEntry(data: MCPStdioEntry | MCPHttpEntry | MCPSSEEntry): data is MCPHttpEntry {\n return 'type' in data && data.type === 'http';\n}\n\nfunction isSSEEntry(data: MCPStdioEntry | MCPHttpEntry | MCPSSEEntry): data is MCPSSEEntry {\n return 'type' in data && data.type === 'sse';\n}\n\nfunction entryToServerInfo(\n name: string,\n data: MCPStdioEntry | MCPHttpEntry | MCPSSEEntry,\n source: MCPServerSource\n): MCPServerInfo {\n if (isHttpEntry(data)) {\n return {\n name,\n type: 'http',\n url: data.url,\n headers: data.headers,\n oauth: data.oauth,\n enabled: true,\n source,\n authStatus: 'unknown' as const,\n };\n }\n if (isSSEEntry(data)) {\n return {\n name,\n type: 'sse',\n url: data.url,\n headers: data.headers,\n enabled: true,\n source,\n authStatus: 'unknown' as const,\n };\n }\n return {\n name,\n type: 'stdio',\n command: data.command,\n args: data.args,\n env: data.env,\n enabled: true,\n source,\n authStatus: 'unknown' as const,\n };\n}\n\nasync function readMCPConfig(\n filePath: string,\n source: MCPServerSource,\n log?: (entry: { event: string; [key: string]: unknown }) => void\n): Promise<MCPServerInfo[]> {\n try {\n const raw = await readFile(filePath, 'utf-8');\n // eslint-disable-next-line no-restricted-syntax -- JSON.parse returns unknown; validated per-entry below\n const json = JSON.parse(raw) as Record<string, unknown>;\n\n const entries =\n typeof json.mcpServers === 'object' && json.mcpServers !== null ? json.mcpServers : json;\n\n const servers: MCPServerInfo[] = [];\n for (const [name, value] of Object.entries(entries)) {\n if (name === 'mcpServers') continue;\n /** Skip non-object values — settings files may have non-MCP keys (e.g. enabledMcpjsonServers: []). */\n if (typeof value !== 'object' || value === null || Array.isArray(value)) continue;\n const result = MCPServerEntrySchema.safeParse(value);\n if (!result.success) {\n const entry = {\n event: 'mcp_config_entry_invalid',\n name,\n source,\n filePath,\n error: result.error.message,\n };\n if (log) log(entry);\n // biome-ignore lint/suspicious/noConsole: diagnostic fallback when no structured logger is injected\n else console.warn('[mcp-servers]', entry.event, entry.name, entry.error);\n continue;\n }\n servers.push(entryToServerInfo(name, result.data, source));\n }\n return servers;\n } catch {\n return [];\n }\n}\n\nasync function readPluginMCPServers(): Promise<MCPServerInfo[]> {\n const servers: MCPServerInfo[] = [];\n try {\n const settingsPath = join(homedir(), '.claude', 'settings.json');\n const settingsRaw = await readFile(settingsPath, 'utf-8');\n // eslint-disable-next-line no-restricted-syntax -- JSON.parse returns unknown; checking enabledPlugins field only\n const settings = JSON.parse(settingsRaw) as { enabledPlugins?: Record<string, boolean> };\n if (!settings.enabledPlugins) return [];\n\n const installedPath = join(homedir(), '.claude', 'plugins', 'installed_plugins.json');\n const installedRaw = await readFile(installedPath, 'utf-8');\n // eslint-disable-next-line no-restricted-syntax -- JSON.parse returns unknown; checking plugins field only\n const installed = JSON.parse(installedRaw) as {\n plugins?: Record<string, Array<{ installPath?: string }>>;\n };\n if (!installed.plugins) return [];\n\n for (const [pluginId, enabled] of Object.entries(settings.enabledPlugins)) {\n if (!enabled) continue;\n const installs = installed.plugins[pluginId];\n if (!installs || installs.length === 0) continue;\n const installPath = installs[0]?.installPath;\n if (!installPath) continue;\n\n const mcpJsonPath = join(installPath, '.mcp.json');\n const pluginServers = await readMCPConfig(mcpJsonPath, 'plugin');\n servers.push(...pluginServers);\n }\n } catch {}\n return servers;\n}\n\n/** Priority order for source-based deduplication (higher index wins). */\nconst SOURCE_PRIORITY: readonly MCPServerSource[] = [\n 'claudeai',\n 'plugin',\n 'user',\n 'project',\n 'local',\n 'mcp-json',\n];\n\n/**\n * Check Claude Code's credential store for servers that still have\n * `authStatus === 'unknown'` after checking Shipyard's own token store.\n * If Claude Code has a valid (non-expired) token, mark the server as authenticated.\n */\nasync function resolveClaudeCodeAuthStatuses(servers: Map<string, MCPServerInfo>): Promise<void> {\n for (const server of servers.values()) {\n if (server.authStatus !== 'unknown') continue;\n if (server.type === 'stdio') continue;\n const ccCreds = await readClaudeCodeCredentials(server.name, server.url ?? '');\n if (ccCreds?.accessToken && ccCreds.expiresAt && ccCreds.expiresAt > Date.now()) {\n server.authStatus = 'authenticated';\n }\n }\n}\n\nfunction resolveStdioAuthStatus(\n server: MCPServerInfo,\n tokens: Record<string, import('../mcp/token-store.js').MCPOAuthToken>,\n tokenStore: import('../mcp/token-store.js').MCPTokenStore\n): void {\n if (!server.env) return;\n for (const value of Object.values(server.env)) {\n const match = VAULT_REF_PATTERN.exec(value);\n if (!match) continue;\n const vaultKey = match[1] ?? '';\n const token = tokens[vaultKey];\n if (token) {\n server.authStatus = tokenStore.isExpired(token) ? 'unauthenticated' : 'authenticated';\n return;\n }\n }\n}\n\nasync function resolveAuthStatuses(\n servers: Map<string, MCPServerInfo>,\n tokenStore: import('../mcp/token-store.js').MCPTokenStore\n): Promise<void> {\n const tokens = await tokenStore.getAllTokens();\n for (const server of servers.values()) {\n if (server.type === 'stdio') {\n resolveStdioAuthStatus(server, tokens, tokenStore);\n continue;\n }\n const token = tokens[server.name];\n if (token) {\n server.authStatus = tokenStore.isExpired(token) ? 'unauthenticated' : 'authenticated';\n }\n }\n\n await resolveClaudeCodeAuthStatuses(servers);\n}\n\n/**\n * Detect MCP servers across user/project/plugin/claude.ai sources.\n *\n * `lastKnown` preserves the previously-detected MCP server list when the\n * detection itself throws (rare — readMCPConfig swallows its own errors —\n * but defense in depth against future regressions and unhandled rejections\n * inside `fetchClaudeAiIntegrations`). Mirror of the `lastKnownAgents`\n * pattern in `agents.ts`.\n *\n * Note: under normal operation, errors are swallowed inside the per-file\n * readers, so this function reaches the dedupe step with a possibly-empty\n * but well-shaped `allServers`. The `try/catch` here is the chokepoint that\n * catches anything escaping that — particularly account-integrations\n * fetches that hit the network.\n */\nexport async function detectMCPServers(\n environments: GitRepoInfo[],\n tokenStore?: import('../mcp/token-store.js').MCPTokenStore,\n log?: (entry: { event: string; [key: string]: unknown }) => void,\n lastKnown?: MCPServerInfo[],\n /**\n * Gates the `fetchClaudeAiIntegrations` network fetch (Connectors page on\n * claude.ai). The fetch only succeeds with claude.ai OAuth tokens; for\n * console / api-key / SSO users it 401s and floods logs every refresh tick.\n *\n * Skipped when the user's chosen method is anything other than 'claude-ai'.\n * `null`/`undefined` (preference unknown — not yet backfilled) keeps the\n * current behaviour of attempting the fetch so OAuth users still get their\n * connectors before the boot-time backfill lands.\n */\n preferredAuth?: AnthropicAuthMethod | null\n): Promise<MCPServerInfo[]> {\n try {\n return await detectMCPServersInner(environments, tokenStore, log, preferredAuth);\n } catch (err) {\n /**\n * Preserve the original \"always resolves\" contract: inner readers\n * already swallow per-file errors and return [], so this catch only\n * fires for unhandled rejections (network failure in\n * `fetchClaudeAiIntegrations`, future regressions). Returning\n * lastKnown if available, otherwise [] — never rethrow, because the\n * Promise.all in detectCapabilities would tear down the whole refresh\n * for a single slice's failure.\n */\n const { logger } = await import('../logger.js');\n if (lastKnown && lastKnown.length > 0) {\n logger.warn(\n { err, lastKnownCount: lastKnown.length },\n 'detectMCPServers threw — preserving lastKnown'\n );\n return lastKnown;\n }\n logger.debug({ err }, 'detectMCPServers threw with no lastKnown — returning []');\n return [];\n }\n}\n\nasync function detectMCPServersInner(\n environments: GitRepoInfo[],\n tokenStore?: import('../mcp/token-store.js').MCPTokenStore,\n log?: (entry: { event: string; [key: string]: unknown }) => void,\n preferredAuth?: AnthropicAuthMethod | null\n): Promise<MCPServerInfo[]> {\n const allServers: MCPServerInfo[] = [];\n\n const userSettingsPath = join(homedir(), '.claude', 'settings.json');\n const userLocalSettingsPath = join(homedir(), '.claude', 'settings.local.json');\n const userMcpJsonPath = join(homedir(), '.mcp.json');\n const userClaudeJsonPath = join(homedir(), '.claude.json');\n\n /**\n * Only the claude.ai OAuth flow exposes the Connectors page that\n * `fetchClaudeAiIntegrations` reads. Console / api-key / SSO / cloud-provider\n * users would 401 and produce log noise on every refresh tick. `null` (not\n * yet backfilled) and `undefined` (caller didn't plumb through) keep the\n * legacy behaviour to preserve OAuth users' connectors during rollout.\n */\n const fetchClaudeAi = shouldFetchClaudeAiIntegrations(preferredAuth);\n const claudeAiPromise: Promise<MCPServerInfo[]> = fetchClaudeAi\n ? fetchClaudeAiIntegrations(log)\n : Promise.resolve([]);\n if (!fetchClaudeAi && log) {\n log({ event: 'claudeai_integrations_skipped_by_method', method: preferredAuth ?? null });\n }\n\n const [\n userServers,\n userLocalServers,\n userMcpJsonServers,\n userClaudeJsonServers,\n pluginServers,\n claudeAiServers,\n ] = await Promise.all([\n readMCPConfig(userSettingsPath, 'user', log),\n readMCPConfig(userLocalSettingsPath, 'user', log),\n readMCPConfig(userMcpJsonPath, 'user', log),\n readMCPConfig(userClaudeJsonPath, 'user', log),\n readPluginMCPServers(),\n claudeAiPromise,\n ]);\n allServers.push(\n ...claudeAiServers,\n ...pluginServers,\n ...userServers,\n ...userLocalServers,\n ...userMcpJsonServers,\n ...userClaudeJsonServers\n );\n\n const envResults = await Promise.all(\n environments.map((env) => {\n const projectPath = join(env.path, '.claude', 'settings.json');\n const localPath = join(env.path, '.claude', 'settings.local.json');\n const mcpJsonPath = join(env.path, '.mcp.json');\n return Promise.all([\n readMCPConfig(projectPath, 'project', log),\n readMCPConfig(localPath, 'local', log),\n readMCPConfig(mcpJsonPath, 'mcp-json', log),\n ]);\n })\n );\n for (const [projectServers, localServers, mcpJsonServers] of envResults) {\n allServers.push(...projectServers, ...localServers, ...mcpJsonServers);\n }\n\n const deduped = new Map<string, MCPServerInfo>();\n for (const server of allServers) {\n const existing = deduped.get(server.name);\n if (\n !existing ||\n SOURCE_PRIORITY.indexOf(server.source) >= SOURCE_PRIORITY.indexOf(existing.source)\n ) {\n deduped.set(server.name, server);\n }\n }\n\n if (tokenStore) {\n await resolveAuthStatuses(deduped, tokenStore);\n }\n\n return [...deduped.values()];\n}\n","import { execFile as execFileCb } from 'node:child_process';\nimport { readFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { promisify } from 'node:util';\nimport type { OAuthDiscoveryState } from '@modelcontextprotocol/sdk/client/auth.js';\nimport { z } from 'zod';\n\nimport { logger } from '../logger.js';\nimport { DiscoveryStateSchema } from './schemas.js';\n\nconst execFile = promisify(execFileCb);\n\n/**\n * Represents a single OAuth entry from Claude Code's credential store.\n *\n * Claude Code v2.1.81+ stores credentials in macOS Keychain under\n * service name \"Claude Code-credentials\". Older versions used\n * `~/.claude/.credentials.json`.\n *\n * The format is owned by Claude Code and may change without notice.\n * All fields are optional to tolerate schema drift.\n */\nexport interface ClaudeCodeOAuthEntry {\n serverName: string;\n serverUrl: string;\n clientId?: string;\n clientSecret?: string;\n accessToken?: string;\n refreshToken?: string;\n expiresAt?: number;\n discoveryState?: OAuthDiscoveryState;\n}\n\n/**\n * Schema for a single entry in `mcpOAuth`. We use `.passthrough()` so\n * unknown future fields are preserved.\n *\n * CC stores `null` for cleared fields (e.g., `accessToken: null` after\n * logout). Zod's `.optional()` only accepts `undefined`, not `null`,\n * so we must use `.nullable().optional()` to tolerate both.\n */\nconst ClaudeCodeOAuthEntrySchema = z\n .object({\n serverName: z.string().nullable().optional(),\n serverUrl: z.string().nullable().optional(),\n clientId: z.string().nullable().optional(),\n clientSecret: z.string().nullable().optional(),\n accessToken: z.string().nullable().optional(),\n refreshToken: z.string().nullable().optional(),\n expiresAt: z.number().nullable().optional(),\n discoveryState: DiscoveryStateSchema.nullable().optional(),\n })\n .passthrough();\n\nconst CredentialsFileSchema = z\n .object({\n mcpOAuth: z.record(z.string(), z.unknown()).optional(),\n })\n .passthrough();\n\nconst CREDENTIALS_PATH = join(homedir(), '.claude', '.credentials.json');\nconst KEYCHAIN_SERVICE = 'Claude Code-credentials';\n\nconst PluginMcpJsonSchema = z.record(\n z.string(),\n z\n .object({\n url: z.string().optional(),\n oauth: z.object({ clientId: z.string(), callbackPort: z.number().optional() }).optional(),\n })\n .passthrough()\n);\n\n/**\n * Read OAuth clientIds from CC plugin `.mcp.json` files.\n * Returns a Map from server URL to clientId.\n */\nasync function extractClientIdsFromPlugin(\n installPath: string,\n result: Map<string, string>\n): Promise<void> {\n const raw = await readFile(join(installPath, '.mcp.json'), 'utf-8');\n const json: unknown = JSON.parse(raw);\n const outer = z.record(z.string(), z.unknown()).safeParse(json);\n if (!outer.success) return;\n const servers = outer.data.mcpServers ?? outer.data;\n const validated = PluginMcpJsonSchema.safeParse(servers);\n if (!validated.success) return;\n\n for (const config of Object.values(validated.data)) {\n if (config.url && config.oauth?.clientId) {\n result.set(config.url, config.oauth.clientId);\n }\n }\n}\n\nexport async function readPluginClientIds(): Promise<Map<string, string>> {\n const result = new Map<string, string>();\n try {\n const installedPath = join(homedir(), '.claude', 'plugins', 'installed_plugins.json');\n const InstalledPluginsSchema = z.object({\n plugins: z\n .record(z.string(), z.array(z.object({ installPath: z.string().optional() })))\n .optional(),\n });\n const parsed = InstalledPluginsSchema.safeParse(\n JSON.parse(await readFile(installedPath, 'utf-8'))\n );\n if (!parsed.success || !parsed.data.plugins) return result;\n\n for (const installs of Object.values(parsed.data.plugins)) {\n const installPath = installs?.[0]?.installPath;\n if (!installPath) continue;\n await extractClientIdsFromPlugin(installPath, result).catch(() => {});\n }\n } catch {}\n return result;\n}\n\n/**\n * Attempt to read Claude Code's credential blob from macOS Keychain.\n * Returns the parsed JSON object or null if unavailable.\n */\nasync function readKeychainCredentials(): Promise<Record<string, unknown> | null> {\n if (process.platform !== 'darwin') return null;\n\n try {\n const { stdout } = await execFile(\n 'security',\n ['find-generic-password', '-s', KEYCHAIN_SERVICE, '-w'],\n { timeout: 5_000 }\n );\n const result = z.record(z.string(), z.unknown()).safeParse(JSON.parse(stdout.trim()));\n return result.success ? result.data : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Search a parsed credentials object for the best MCP OAuth entry\n * matching the given `serverUrl`. Prefers entries with an access token;\n * separately gathers clientId from any matching entry.\n *\n * CC stores entries under both bare (`slack`) and plugin-prefixed\n * (`plugin:slack:slack`) keys. The prefixed entries typically have\n * tokens while the bare entries may only have clientId. We merge\n * across all matching entries to get the most complete picture.\n */\n/** Scan a flat mcpOAuth record for entries matching serverUrl. */\nfunction scanEntries(\n mcpOAuth: Record<string, unknown>,\n serverUrl: string\n): {\n bestWithToken: z.infer<typeof ClaudeCodeOAuthEntrySchema> | null;\n clientId?: string;\n clientSecret?: string;\n discoveryState?: OAuthDiscoveryState;\n} {\n let bestWithToken: z.infer<typeof ClaudeCodeOAuthEntrySchema> | null = null;\n let clientId: string | undefined;\n let clientSecret: string | undefined;\n let discoveryState: OAuthDiscoveryState | undefined;\n\n for (const value of Object.values(mcpOAuth)) {\n const entryResult = ClaudeCodeOAuthEntrySchema.safeParse(value);\n if (!entryResult.success) continue;\n\n const entry = entryResult.data;\n if (entry.serverUrl !== serverUrl) continue;\n\n if (entry.clientId && !clientId) {\n clientId = entry.clientId;\n clientSecret = entry.clientSecret || undefined;\n }\n if (entry.discoveryState && !discoveryState) {\n discoveryState = entry.discoveryState;\n }\n if (entry.accessToken && !bestWithToken) {\n bestWithToken = entry;\n }\n }\n\n return { bestWithToken, clientId, clientSecret, discoveryState };\n}\n\nfunction findMatchingEntry(\n credentialsData: Record<string, unknown>,\n serverName: string,\n serverUrl: string\n): ClaudeCodeOAuthEntry | null {\n const fileResult = CredentialsFileSchema.safeParse(credentialsData);\n if (!fileResult.success) return null;\n\n const mcpOAuth = fileResult.data.mcpOAuth;\n if (!mcpOAuth) return null;\n\n const { bestWithToken, clientId, clientSecret, discoveryState } = scanEntries(\n mcpOAuth,\n serverUrl\n );\n if (!bestWithToken && !clientId) return null;\n\n return {\n serverName: bestWithToken?.serverName ?? serverName,\n serverUrl,\n clientId: bestWithToken?.clientId || clientId,\n clientSecret: bestWithToken?.clientSecret || clientSecret || undefined,\n accessToken: bestWithToken?.accessToken || undefined,\n refreshToken: bestWithToken?.refreshToken || undefined,\n expiresAt: bestWithToken?.expiresAt ?? undefined,\n discoveryState: bestWithToken?.discoveryState ?? discoveryState ?? undefined,\n };\n}\n\n/**\n * Search Claude Code's credential store for an MCP OAuth entry\n * matching the given `serverUrl`.\n *\n * Tries macOS Keychain first (Claude Code v2.1.81+), then falls back\n * to the legacy file at `~/.claude/.credentials.json`.\n *\n * Keys in the store use hashes we cannot reproduce, so we match\n * by `serverUrl` instead. Returns the first entry whose `serverUrl`\n * matches and that has a non-empty `clientId`.\n */\nexport async function readClaudeCodeCredentials(\n serverName: string,\n serverUrl: string\n): Promise<ClaudeCodeOAuthEntry | null> {\n if (!serverUrl) return null;\n\n try {\n const keychainData = await readKeychainCredentials();\n if (keychainData) {\n const entry = findMatchingEntry(keychainData, serverName, serverUrl);\n if (entry) {\n logger.debug({ serverUrl }, 'Found Claude Code credentials in macOS Keychain');\n return entry;\n }\n }\n } catch (err: unknown) {\n logger.debug({ err }, 'Failed to read Claude Code credentials from Keychain');\n }\n\n try {\n const raw = await readFile(CREDENTIALS_PATH, 'utf-8');\n const fileResult = CredentialsFileSchema.safeParse(JSON.parse(raw));\n if (fileResult.success) {\n const entry = findMatchingEntry(fileResult.data, serverName, serverUrl);\n if (entry) {\n logger.debug({ serverUrl }, 'Found Claude Code credentials in legacy file');\n return entry;\n }\n }\n\n return null;\n } catch (err: unknown) {\n logger.debug({ err }, 'Failed to read Claude Code credentials from file');\n return null;\n }\n}\n","import {\n OAuthMetadataSchema,\n OAuthProtectedResourceMetadataSchema,\n OpenIdProviderDiscoveryMetadataSchema,\n} from '@modelcontextprotocol/sdk/shared/auth.js';\nimport { z } from 'zod';\n\nexport const AuthorizationServerMetadataSchema = z.union([\n OAuthMetadataSchema,\n OpenIdProviderDiscoveryMetadataSchema,\n]);\n\nexport const DiscoveryStateSchema = z\n .object({\n authorizationServerUrl: z.string(),\n authorizationServerMetadata: AuthorizationServerMetadataSchema.optional(),\n resourceMetadata: OAuthProtectedResourceMetadataSchema.optional(),\n resourceMetadataUrl: z.string().optional(),\n })\n .passthrough();\n","import type { McpServerConfig } from '@anthropic-ai/claude-agent-sdk';\nimport type { MCPServerInfo } from '@shipyard/session';\n\nimport type { MCPTokenStore } from './token-store.js';\n\nexport const ENV_VAR_PATTERN = /\\$\\{([^}]+)\\}/g;\nexport const VAULT_REF_PATTERN = /^\\$\\{vault:([^}]+)\\}$/;\n\nexport function interpolateEnvVars(headers: Record<string, string>): Record<string, string> {\n const result: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n result[key] = value.replace(\n ENV_VAR_PATTERN,\n (_, varName: string) => process.env[varName] ?? ''\n );\n }\n return result;\n}\n\n/**\n * Pass through user-configured static headers only. OAuth tokens for HTTP\n * servers are managed by the SDK subprocess via CC's Keychain — injecting\n * Shipyard's own tokens here would conflict with the SDK's OAuth transport.\n * Token store injection is only used for stdio env vars (resolveStdioEnv).\n */\nexport function resolveHeaders(server: MCPServerInfo): Record<string, string> | undefined {\n if (!server.headers) return undefined;\n return interpolateEnvVars(server.headers);\n}\n\nexport function resolveStdioEnv(\n env: Record<string, string> | undefined,\n store?: MCPTokenStore,\n log?: (entry: { event: string; [key: string]: unknown }) => void\n): Record<string, string> | undefined {\n if (!env) return undefined;\n if (!store) return env;\n\n const result: Record<string, string> = {};\n let changed = false;\n\n for (const [key, value] of Object.entries(env)) {\n const match = VAULT_REF_PATTERN.exec(value);\n if (match) {\n const vaultKey = match[1] ?? '';\n const token = store.getTokenSync(vaultKey);\n if (token && !store.isExpired(token)) {\n result[key] = token.accessToken;\n changed = true;\n log?.({\n event: 'vault_ref_resolved',\n envKey: key,\n vaultKey,\n tokenType: token.tokenType,\n hasExpiry: token.expiresAt !== undefined,\n });\n continue;\n }\n log?.({\n event: 'vault_ref_unresolved',\n envKey: key,\n vaultKey,\n tokenFound: token !== null,\n tokenExpired: token ? store.isExpired(token) : false,\n });\n }\n result[key] = value;\n }\n\n return changed ? result : env;\n}\n\nexport function resolveEnabledMcpServers(\n overrides: Array<{ name: string; enabled: boolean }> | undefined,\n available: MCPServerInfo[] | undefined,\n mcpTokenStore?: MCPTokenStore\n): Record<string, McpServerConfig> | undefined {\n if (!overrides || !available) return undefined;\n const enabledNames = new Set(overrides.filter((o) => o.enabled).map((o) => o.name));\n if (enabledNames.size === 0) return {};\n const result: Record<string, McpServerConfig> = {};\n for (const server of available) {\n if (!enabledNames.has(server.name)) continue;\n if (server.type === 'http' && server.url) {\n result[server.name] = {\n type: 'http',\n url: server.url,\n headers: resolveHeaders(server),\n };\n } else if (server.type === 'sse' && server.url) {\n result[server.name] = {\n type: 'sse',\n url: server.url,\n headers: resolveHeaders(server),\n };\n } else if (server.command) {\n result[server.name] = {\n command: server.command,\n args: server.args ?? undefined,\n env: resolveStdioEnv(server.env, mcpTokenStore),\n };\n }\n }\n return result;\n}\n","import { execFile as execFileCb } from 'node:child_process';\nimport { promisify } from 'node:util';\n\nimport { logger } from '../logger.js';\n\nconst execFile = promisify(execFileCb);\n\nexport type AnthropicCredentialsLog = (entry: {\n event: string;\n reason?: string;\n [key: string]: unknown;\n}) => void;\n\nconst defaultLog: AnthropicCredentialsLog = (entry) => logger.debug(entry, entry.event);\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nasync function readKeychainEntry(service: string): Promise<string | null> {\n if (process.platform !== 'darwin') return null;\n try {\n const { stdout } = await execFile('security', ['find-generic-password', '-s', service, '-w'], {\n timeout: 5_000,\n });\n const value = stdout.trim();\n return value || null;\n } catch {\n return null;\n }\n}\n\n/**\n * Read the OAuth access token from the `Claude Code-credentials` Keychain entry,\n * stored as `claudeAiOauth.accessToken` by `claude setup-token` / `claude auth login`.\n *\n * This is the canonical token for talking to claude.ai-hosted services\n * (e.g. `https://api.anthropic.com/v1/mcp_servers`) on behalf of an OAuth-authed user.\n */\nexport async function readKeychainOAuthToken(\n log: AnthropicCredentialsLog = defaultLog\n): Promise<string | null> {\n if (process.platform !== 'darwin') {\n log({ event: 'anthropic_keychain_read_skipped', reason: 'non_darwin' });\n return null;\n }\n const raw = await readKeychainEntry('Claude Code-credentials');\n if (!raw) {\n log({ event: 'anthropic_keychain_oauth_missing', reason: 'no_credentials_entry' });\n return null;\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n log({ event: 'anthropic_keychain_oauth_corrupt', reason: 'json_parse_error' });\n return null;\n }\n if (!isRecord(parsed)) {\n log({ event: 'anthropic_keychain_oauth_corrupt', reason: 'json_parse_error' });\n return null;\n }\n const oauth = parsed.claudeAiOauth;\n if (!isRecord(oauth)) {\n log({ event: 'anthropic_keychain_oauth_missing', reason: 'no_oauth_field' });\n return null;\n }\n const token = oauth.accessToken;\n if (typeof token !== 'string' || token.length === 0) {\n log({ event: 'anthropic_keychain_oauth_missing', reason: 'no_oauth_field' });\n return null;\n }\n return token;\n}\n\n/**\n * Read the managed API key from the `Claude Code` Keychain entry —\n * the value stored by `claude login` for direct Messages API calls\n * (paired with the billing header prefix).\n */\nexport async function readKeychainManagedKey(): Promise<string | null> {\n const value = await readKeychainEntry('Claude Code');\n if (!value || !value.startsWith('sk-')) return null;\n return value;\n}\n","import type { MCPServerInfo } from '@shipyard/session';\n\nimport { readKeychainOAuthToken } from '../auth/anthropic-credentials.js';\nimport { logger } from '../logger.js';\n\ntype Log = (entry: { event: string; [key: string]: unknown }) => void;\n\n/**\n * Minimal shape we expect from the Anthropic MCP servers API response.\n * Hand-rolled instead of Zod to keep best-effort parsing lightweight.\n */\nfunction parseClaudeAiServers(data: unknown): Array<{ name: string; url: string }> {\n if (typeof data !== 'object' || data === null) return [];\n // eslint-disable-next-line no-restricted-syntax -- JSON.parse returns unknown; manually validated below\n const obj = data as Record<string, unknown>;\n if (!Array.isArray(obj.data)) return [];\n const results: Array<{ name: string; url: string }> = [];\n for (const entry of obj.data) {\n if (typeof entry !== 'object' || entry === null) continue;\n // eslint-disable-next-line no-restricted-syntax -- narrowing unknown entry fields\n const rec = entry as Record<string, unknown>;\n if (typeof rec.name === 'string' && typeof rec.url === 'string') {\n results.push({ name: rec.name, url: rec.url });\n }\n }\n return results;\n}\n\n/**\n * Fetch MCP server integrations from the user's Claude.ai account.\n * Best-effort: returns [] on any failure.\n */\nexport async function fetchClaudeAiIntegrations(log?: Log): Promise<MCPServerInfo[]> {\n const debugLog: Log = log ?? ((entry) => logger.debug(entry, entry.event));\n try {\n const token = await readKeychainOAuthToken(debugLog);\n if (!token) {\n debugLog({ event: 'claudeai_integrations_skipped', reason: 'no_oauth_token' });\n return [];\n }\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 10_000);\n\n try {\n const response = await fetch('https://api.anthropic.com/v1/mcp_servers?limit=1000', {\n headers: {\n Authorization: `Bearer ${token}`,\n },\n signal: controller.signal,\n });\n\n if (!response.ok) {\n debugLog({\n event: 'claudeai_integrations_fetch_failed',\n status: response.status,\n });\n return [];\n }\n\n const body: unknown = await response.json();\n const servers = parseClaudeAiServers(body);\n\n return servers.map((s) => ({\n name: s.name,\n type: 'http' as const,\n url: s.url,\n enabled: true,\n source: 'claudeai' as const,\n authStatus: 'unknown' as const,\n }));\n } finally {\n clearTimeout(timeout);\n }\n } catch (err: unknown) {\n debugLog({\n event: 'claudeai_integrations_fetch_error',\n error: err instanceof Error ? err.message : String(err),\n });\n return [];\n }\n}\n"],"mappings":"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","names":["readFile","homedir","join","execFileCb","promisify","execFile","promisify","execFileCb","readFile","join","homedir","logger"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/shared/commands/login.ts"],"sourcesContent":["import {\n DeviceExchangeCodeResponseSchema,\n DevicePollPendingSchema,\n DevicePollResponseSchema,\n DeviceStartResponseSchema,\n ROUTES,\n} from '@shipyard/session';\nimport { z } from 'zod';\nimport {\n getConfigPath,\n loadAuthToken,\n readConfig,\n type ShipyardConfig,\n writeConfig,\n} from '../../services/bootstrap/auth.js';\nimport { isDevMode } from '../env.js';\nimport { print, printError } from './output.js';\n\nconst DEFAULT_SIGNALING_URL = 'https://shipyard-session-server.jacob-191.workers.dev';\nconst DEFAULT_DEV_SIGNALING_URL = 'http://localhost:4444';\n\nexport function getSignalingUrl(): string {\n return (\n process.env.SHIPYARD_SIGNALING_URL ??\n (isDevMode() ? DEFAULT_DEV_SIGNALING_URL : DEFAULT_SIGNALING_URL)\n );\n}\n\nexport interface AuthConfig {\n token: string;\n userId: string;\n displayName: string;\n signalingUrl: string;\n}\n\nexport interface AuthResult extends AuthConfig {\n deviceFlowRan: boolean;\n}\n\n/**\n * Ensure the user is authenticated, running the device flow interactively if needed.\n * Returns the auth config on success, or calls process.exit(1) on failure.\n */\nexport async function ensureAuthenticated(opts: {\n signalingUrl: string;\n webCode?: string;\n}): Promise<AuthResult> {\n const existing = await loadAuthToken();\n\n if (existing.status === 'ok') {\n return {\n token: existing.token,\n userId: existing.userId,\n displayName: existing.displayName,\n signalingUrl: existing.signalingUrl ?? opts.signalingUrl,\n deviceFlowRan: false,\n };\n }\n\n if (existing.status === 'expired') {\n print(' Token expired. Starting re-authentication...\\n');\n }\n\n if (opts.webCode) {\n try {\n return await runWebCodeFlow(opts.signalingUrl, opts.webCode);\n } catch {\n print(' Web code expired or invalid. Starting standard login...\\n');\n }\n }\n\n return runDeviceFlow(opts.signalingUrl);\n}\n\nasync function runDeviceFlow(signalingUrl: string): Promise<AuthResult> {\n const startData = await startDeviceFlow(signalingUrl);\n\n print(` Open this URL in your browser:\\n`);\n print(` ${startData.verificationUri}\\n`);\n print(` Your code: ${startData.userCode}\\n`);\n print(' Waiting for authorization...');\n\n if (!process.env.SHIPYARD_NO_OPEN) {\n import('open').then(({ default: open }) => open(startData.verificationUri)).catch(() => {});\n }\n\n const interval = (startData.interval ?? 5) * 1000;\n const deadline = Date.now() + startData.expiresIn * 1000;\n\n while (Date.now() < deadline) {\n await new Promise((resolve) => setTimeout(resolve, interval));\n\n try {\n const pollRes = await fetch(`${signalingUrl}${ROUTES.AUTH_DEVICE_POLL}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ deviceCode: startData.deviceCode }),\n });\n\n if (pollRes.ok) {\n const result = await handleSuccessfulPoll(pollRes, signalingUrl);\n return result;\n }\n\n await handlePollError(pollRes, interval);\n } catch (err: unknown) {\n printError('Device flow poll failed', err instanceof Error ? err : undefined);\n }\n }\n\n printError('\\n Authorization timed out. Run `shipyard login` again.');\n process.exit(1);\n}\n\nasync function runWebCodeFlow(signalingUrl: string, userCode: string): Promise<AuthResult> {\n print(' Authenticating via web app code...\\n');\n\n let deviceCode: string | undefined;\n for (let attempt = 0; attempt < 3; attempt++) {\n if (attempt > 0) await new Promise((r) => setTimeout(r, 1000));\n\n const exchangeRes = await fetch(`${signalingUrl}${ROUTES.AUTH_DEVICE_EXCHANGE_CODE}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ userCode }),\n });\n\n if (exchangeRes.ok) {\n const parsed = DeviceExchangeCodeResponseSchema.parse(await exchangeRes.json());\n deviceCode = parsed.deviceCode;\n break;\n }\n\n if (attempt === 2) {\n throw new Error(`Exchange failed after 3 attempts: ${exchangeRes.status}`);\n }\n }\n\n if (!deviceCode) throw new Error('Exchange failed: no device code');\n\n const pollRes = await fetch(`${signalingUrl}${ROUTES.AUTH_DEVICE_POLL}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ deviceCode }),\n });\n\n if (!pollRes.ok) {\n throw new Error(`Poll failed: ${pollRes.status}`);\n }\n\n const result = await handleSuccessfulPoll(pollRes, signalingUrl);\n print(' Authenticated via web app\\n');\n return result;\n}\n\nexport async function loginCommand(options: { check?: boolean }): Promise<void> {\n if (options.check) {\n return checkLogin();\n }\n\n const signalingUrl = getSignalingUrl();\n await runDeviceFlow(signalingUrl);\n}\n\nasync function startDeviceFlow(signalingUrl: string) {\n print('Starting device authorization...\\n');\n\n let startRes: Response;\n try {\n startRes = await fetch(`${signalingUrl}${ROUTES.AUTH_DEVICE_START}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n });\n } catch (err) {\n printError(\n `Could not reach signaling server at ${signalingUrl}`,\n err instanceof Error ? err : undefined\n );\n printError('Check your internet connection or set SHIPYARD_SIGNALING_URL.');\n process.exit(1);\n }\n\n if (!startRes.ok) {\n printError(`Failed to start device flow: ${startRes.status}`);\n process.exit(1);\n }\n\n return DeviceStartResponseSchema.parse(await startRes.json());\n}\n\nfunction extractTokenExpiry(token: string): number {\n const fallback = Date.now() + 30 * 24 * 60 * 60 * 1000;\n try {\n const payloadB64 = token.split('.')[1];\n if (!payloadB64) return fallback;\n const payload: unknown = JSON.parse(Buffer.from(payloadB64, 'base64url').toString());\n const exp = z.object({ exp: z.number() }).safeParse(payload);\n return exp.success ? exp.data.exp * 1000 : fallback;\n } catch {\n return fallback;\n }\n}\n\nasync function handleSuccessfulPoll(pollRes: Response, signalingUrl: string): Promise<AuthResult> {\n const pollData = DevicePollResponseSchema.parse(await pollRes.json());\n const expiresAt = extractTokenExpiry(pollData.token);\n\n const config: ShipyardConfig = {\n auth: {\n token: pollData.token,\n userId: pollData.user.id,\n displayName: pollData.user.displayName,\n providers: pollData.user.providers,\n expiresAt,\n signalingUrl,\n },\n };\n\n await writeConfig(config);\n\n print(`\\n Logged in as ${pollData.user.displayName} (${pollData.user.providers.join(', ')})`);\n print(` Token saved to ${getConfigPath()}`);\n\n return {\n token: pollData.token,\n userId: pollData.user.id,\n displayName: pollData.user.displayName,\n signalingUrl,\n deviceFlowRan: true,\n };\n}\n\nasync function handlePollError(pollRes: Response, interval: number): Promise<void> {\n try {\n const errorData = DevicePollPendingSchema.parse(await pollRes.json());\n\n if (errorData.error === 'expired_token') {\n printError('\\n Authorization expired. Run `shipyard login` again.');\n process.exit(1);\n }\n\n if (errorData.error === 'slow_down') {\n await new Promise((resolve) => setTimeout(resolve, interval));\n }\n } catch (err: unknown) {\n printError('Failed to parse poll error response', err instanceof Error ? err : undefined);\n }\n}\n\nasync function checkLogin(): Promise<void> {\n const config = await readConfig();\n\n if (!config?.auth?.token) {\n print('Not logged in. Run `shipyard login` to authenticate.');\n process.exit(1);\n }\n\n if (config.auth.expiresAt < Date.now()) {\n print('Token expired. Run `shipyard login` to re-authenticate.');\n process.exit(1);\n }\n\n const daysLeft = Math.ceil((config.auth.expiresAt - Date.now()) / (24 * 60 * 60 * 1000));\n print(`Logged in as ${config.auth.displayName} (${config.auth.providers.join(', ')})`);\n print(`Token expires in ${daysLeft} days`);\n}\n"],"mappings":"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","names":[]}
@@ -1,49 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- external_exports
4
- } from "./chunk-VPMN47TL.js";
5
-
6
- // src/shared/env.ts
7
- import { homedir } from "os";
8
- import { join } from "path";
9
- process.env.CLAUDE_CODE_ENABLE_FINE_GRAINED_TOOL_STREAMING = "1";
10
- process.env.CLAUDE_CODE_ENABLE_TASKS = "1";
11
- function isDevMode() {
12
- return process.env.SHIPYARD_DEV === "1" || process.env.SHIPYARD_DEV === "true";
13
- }
14
- function isVanillaAgentMode() {
15
- return process.env.SHIPYARD_VANILLA_AGENT === "1" || process.env.SHIPYARD_VANILLA_AGENT === "true";
16
- }
17
- function shipyardDirName() {
18
- return isDevMode() ? ".shipyard-dev" : ".shipyard";
19
- }
20
- function getShipyardHome() {
21
- if (process.env.SHIPYARD_HOME) return process.env.SHIPYARD_HOME;
22
- return join(homedir(), shipyardDirName());
23
- }
24
- var EnvSchema = external_exports.object({
25
- ANTHROPIC_API_KEY: external_exports.string().min(1).optional(),
26
- SHIPYARD_DEV: external_exports.enum(["1", "true", "0", "false", ""]).optional().transform((v) => v === "1" || v === "true"),
27
- SHIPYARD_DATA_DIR: external_exports.string().optional().transform((v) => v ?? `~/${shipyardDirName()}/data`),
28
- LOG_LEVEL: external_exports.enum(["debug", "info", "warn", "error"]).default("warn"),
29
- SHIPYARD_SIGNALING_URL: external_exports.string().url().optional(),
30
- SHIPYARD_USER_TOKEN: external_exports.string().optional(),
31
- SHIPYARD_USER_ID: external_exports.string().optional(),
32
- SHIPYARD_MACHINE_ID: external_exports.string().optional(),
33
- SHIPYARD_MACHINE_NAME: external_exports.string().optional(),
34
- SHIPYARD_USER_DISPLAY_NAME: external_exports.string().optional(),
35
- SHIPYARD_HOME: external_exports.string().optional(),
36
- SHIPYARD_WORKTREE_NAME: external_exports.string().optional(),
37
- SHIPYARD_VANILLA_AGENT: external_exports.enum(["1", "true", "0", "false", ""]).optional().transform((v) => v === "1" || v === "true")
38
- });
39
- function validateEnv() {
40
- return EnvSchema.parse(process.env);
41
- }
42
-
43
- export {
44
- isDevMode,
45
- isVanillaAgentMode,
46
- getShipyardHome,
47
- validateEnv
48
- };
49
- //# sourceMappingURL=chunk-PI77CUEP.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/shared/env.ts"],"sourcesContent":["import { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { z } from 'zod';\n\n/**\n * Enable fine-grained tool streaming so MCP tool inputs stream token-by-token\n * via input_json_delta events instead of being buffered as complete JSON.\n * Set early so the subprocess inherits it.\n */\nprocess.env.CLAUDE_CODE_ENABLE_FINE_GRAINED_TOOL_STREAMING = '1';\n\n/**\n * Enable the Task system (TaskCreate/TaskGet/TaskList/TaskUpdate) which provides\n * structured task management with dependency tracking (blocks/blockedBy).\n * Replaces TodoWrite — when enabled, CC only has TaskCreate (not TodoWrite).\n */\nprocess.env.CLAUDE_CODE_ENABLE_TASKS = '1';\n\nexport function isDevMode(): boolean {\n return process.env.SHIPYARD_DEV === '1' || process.env.SHIPYARD_DEV === 'true';\n}\n\n/**\n * When enabled, the agent runs with only Shipyard builtins (system prompt,\n * comment + visualize MCP servers) and no user/plugin MCP servers.\n * Useful for testing the harness in isolation without external interference.\n *\n * Set `SHIPYARD_VANILLA_AGENT=1` to activate.\n */\nexport function isVanillaAgentMode(): boolean {\n return (\n process.env.SHIPYARD_VANILLA_AGENT === '1' || process.env.SHIPYARD_VANILLA_AGENT === 'true'\n );\n}\n\nfunction shipyardDirName(): string {\n return isDevMode() ? '.shipyard-dev' : '.shipyard';\n}\n\nexport function getShipyardHome(): string {\n if (process.env.SHIPYARD_HOME) return process.env.SHIPYARD_HOME;\n return join(homedir(), shipyardDirName());\n}\n\nexport const EnvSchema = z.object({\n ANTHROPIC_API_KEY: z.string().min(1).optional(),\n SHIPYARD_DEV: z\n .enum(['1', 'true', '0', 'false', ''])\n .optional()\n .transform((v) => v === '1' || v === 'true'),\n SHIPYARD_DATA_DIR: z\n .string()\n .optional()\n .transform((v) => v ?? `~/${shipyardDirName()}/data`),\n LOG_LEVEL: z.enum(['debug', 'info', 'warn', 'error']).default('warn'),\n SHIPYARD_SIGNALING_URL: z.string().url().optional(),\n SHIPYARD_USER_TOKEN: z.string().optional(),\n SHIPYARD_USER_ID: z.string().optional(),\n SHIPYARD_MACHINE_ID: z.string().optional(),\n SHIPYARD_MACHINE_NAME: z.string().optional(),\n SHIPYARD_USER_DISPLAY_NAME: z.string().optional(),\n SHIPYARD_HOME: z.string().optional(),\n SHIPYARD_WORKTREE_NAME: z.string().optional(),\n SHIPYARD_VANILLA_AGENT: z\n .enum(['1', 'true', '0', 'false', ''])\n .optional()\n .transform((v) => v === '1' || v === 'true'),\n});\n\nexport type Env = z.infer<typeof EnvSchema>;\n\nexport function validateEnv(): Env {\n return EnvSchema.parse(process.env);\n}\n"],"mappings":";;;;;;AAAA,SAAS,eAAe;AACxB,SAAS,YAAY;AAQrB,QAAQ,IAAI,iDAAiD;AAO7D,QAAQ,IAAI,2BAA2B;AAEhC,SAAS,YAAqB;AACnC,SAAO,QAAQ,IAAI,iBAAiB,OAAO,QAAQ,IAAI,iBAAiB;AAC1E;AASO,SAAS,qBAA8B;AAC5C,SACE,QAAQ,IAAI,2BAA2B,OAAO,QAAQ,IAAI,2BAA2B;AAEzF;AAEA,SAAS,kBAA0B;AACjC,SAAO,UAAU,IAAI,kBAAkB;AACzC;AAEO,SAAS,kBAA0B;AACxC,MAAI,QAAQ,IAAI,cAAe,QAAO,QAAQ,IAAI;AAClD,SAAO,KAAK,QAAQ,GAAG,gBAAgB,CAAC;AAC1C;AAEO,IAAM,YAAY,iBAAE,OAAO;AAAA,EAChC,mBAAmB,iBAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EAC9C,cAAc,iBACX,KAAK,CAAC,KAAK,QAAQ,KAAK,SAAS,EAAE,CAAC,EACpC,SAAS,EACT,UAAU,CAAC,MAAM,MAAM,OAAO,MAAM,MAAM;AAAA,EAC7C,mBAAmB,iBAChB,OAAO,EACP,SAAS,EACT,UAAU,CAAC,MAAM,KAAK,KAAK,gBAAgB,CAAC,OAAO;AAAA,EACtD,WAAW,iBAAE,KAAK,CAAC,SAAS,QAAQ,QAAQ,OAAO,CAAC,EAAE,QAAQ,MAAM;AAAA,EACpE,wBAAwB,iBAAE,OAAO,EAAE,IAAI,EAAE,SAAS;AAAA,EAClD,qBAAqB,iBAAE,OAAO,EAAE,SAAS;AAAA,EACzC,kBAAkB,iBAAE,OAAO,EAAE,SAAS;AAAA,EACtC,qBAAqB,iBAAE,OAAO,EAAE,SAAS;AAAA,EACzC,uBAAuB,iBAAE,OAAO,EAAE,SAAS;AAAA,EAC3C,4BAA4B,iBAAE,OAAO,EAAE,SAAS;AAAA,EAChD,eAAe,iBAAE,OAAO,EAAE,SAAS;AAAA,EACnC,wBAAwB,iBAAE,OAAO,EAAE,SAAS;AAAA,EAC5C,wBAAwB,iBACrB,KAAK,CAAC,KAAK,QAAQ,KAAK,SAAS,EAAE,CAAC,EACpC,SAAS,EACT,UAAU,CAAC,MAAM,MAAM,OAAO,MAAM,MAAM;AAC/C,CAAC;AAIM,SAAS,cAAmB;AACjC,SAAO,UAAU,MAAM,QAAQ,GAAG;AACpC;","names":[]}
@@ -1,395 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- logger
4
- } from "./chunk-2UN5AR7V.js";
5
- import {
6
- getShipyardHome
7
- } from "./chunk-PI77CUEP.js";
8
- import {
9
- external_exports
10
- } from "./chunk-VPMN47TL.js";
11
- import {
12
- TIMEOUT_MS,
13
- run,
14
- runWithTimeout
15
- } from "./chunk-66OBOZ3X.js";
16
-
17
- // src/shared/capabilities/git-repo.ts
18
- import { readdir } from "fs/promises";
19
- import { basename, join as join3 } from "path";
20
-
21
- // src/shared/capabilities/environments-cache.ts
22
- import { mkdir, readFile, rename, stat, unlink, writeFile } from "fs/promises";
23
- import { dirname as dirname2, isAbsolute, join as join2, resolve } from "path";
24
-
25
- // src/shared/fs-utils.ts
26
- import { existsSync } from "fs";
27
- import { dirname, join, parse } from "path";
28
- function isEnoent(err) {
29
- return typeof err === "object" && err !== null && "code" in err && err.code === "ENOENT";
30
- }
31
- function isEexist(err) {
32
- return typeof err === "object" && err !== null && "code" in err && err.code === "EEXIST";
33
- }
34
- function isCorruptionError(err) {
35
- if (err instanceof SyntaxError) return true;
36
- if (typeof err === "object" && err !== null && "name" in err && err.name === "ZodError")
37
- return true;
38
- return false;
39
- }
40
- function findProjectRoot(startDir) {
41
- let dir = startDir;
42
- while (true) {
43
- if (existsSync(join(dir, "pnpm-workspace.yaml")) || existsSync(join(dir, ".git"))) {
44
- return { root: dir, foundMarker: true };
45
- }
46
- const parent = dirname(dir);
47
- if (parent === dir || parent === parse(dir).root) {
48
- return { root: startDir, foundMarker: false };
49
- }
50
- dir = parent;
51
- }
52
- }
53
-
54
- // src/shared/capabilities/environments-cache.ts
55
- var CacheEntrySchema = external_exports.object({
56
- path: external_exports.string(),
57
- branch: external_exports.string(),
58
- remote: external_exports.string().nullable(),
59
- headMtimeMs: external_exports.number(),
60
- cachedAt: external_exports.number()
61
- });
62
- var CacheFileSchema = external_exports.object({
63
- entries: external_exports.record(external_exports.string(), CacheEntrySchema)
64
- });
65
- var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
66
- function cacheFilePath() {
67
- return join2(getShipyardHome(), "data", "environments-cache.json");
68
- }
69
- async function loadCache() {
70
- const filePath = cacheFilePath();
71
- try {
72
- const raw = await readFile(filePath, "utf8");
73
- const parsed = JSON.parse(raw);
74
- const result = CacheFileSchema.safeParse(parsed);
75
- if (!result.success) {
76
- logger.debug({ path: filePath }, "environments-cache: malformed, treating as empty");
77
- return /* @__PURE__ */ new Map();
78
- }
79
- return new Map(Object.entries(result.data.entries));
80
- } catch (err) {
81
- if (isEnoent(err)) {
82
- return /* @__PURE__ */ new Map();
83
- }
84
- logger.debug({ err }, "environments-cache: read failed, treating as empty");
85
- return /* @__PURE__ */ new Map();
86
- }
87
- }
88
- async function resolveHeadPath(worktreePath) {
89
- const dotGitPath = join2(worktreePath, ".git");
90
- try {
91
- const dotGitStat = await stat(dotGitPath);
92
- if (dotGitStat.isDirectory()) {
93
- return join2(dotGitPath, "HEAD");
94
- }
95
- if (dotGitStat.isFile()) {
96
- const contents = await readFile(dotGitPath, "utf8");
97
- const match = contents.match(/^gitdir:\s*(.+)\s*$/m);
98
- if (!match?.[1]) return null;
99
- const gitdirRaw = match[1].trim();
100
- const gitdir = isAbsolute(gitdirRaw) ? gitdirRaw : resolve(dirname2(dotGitPath), gitdirRaw);
101
- return join2(gitdir, "HEAD");
102
- }
103
- return null;
104
- } catch {
105
- return null;
106
- }
107
- }
108
- async function getHeadMtime(worktreePath) {
109
- const headPath = await resolveHeadPath(worktreePath);
110
- if (!headPath) return null;
111
- try {
112
- const headStat = await stat(headPath);
113
- return headStat.mtimeMs;
114
- } catch {
115
- return null;
116
- }
117
- }
118
- function isCacheValid(entry, currentMtimeMs) {
119
- if (entry.headMtimeMs !== currentMtimeMs) return false;
120
- if (Date.now() - entry.cachedAt >= CACHE_TTL_MS) return false;
121
- return true;
122
- }
123
- async function saveCache(entries) {
124
- const filePath = cacheFilePath();
125
- const tmpPath = `${filePath}.tmp`;
126
- try {
127
- await mkdir(dirname2(filePath), { recursive: true });
128
- const current = await loadCache();
129
- for (const [path, entry] of entries) {
130
- current.set(path, entry);
131
- }
132
- const payload = { entries: Object.fromEntries(current) };
133
- await writeFile(tmpPath, JSON.stringify(payload), "utf8");
134
- await rename(tmpPath, filePath);
135
- } catch (err) {
136
- logger.debug({ err, filePath }, "environments-cache: save failed");
137
- void unlink(tmpPath).catch(() => {
138
- });
139
- }
140
- }
141
-
142
- // src/shared/capabilities/repo-paths-cache.ts
143
- var cache = null;
144
- function getCachedRepoPaths() {
145
- if (!cache) return null;
146
- return { repos: cache.repos, worktrees: cache.worktrees };
147
- }
148
- function setCachedRepoPaths(repos, worktrees, now = Date.now()) {
149
- cache = { repos, worktrees, populatedAt: now };
150
- }
151
- function addWorktreeToCache(worktreePath, sourceRepoPath) {
152
- if (!cache) return;
153
- const repos = cache.repos.includes(sourceRepoPath) ? cache.repos : [...cache.repos, sourceRepoPath];
154
- const worktrees = cache.worktrees.includes(worktreePath) ? cache.worktrees : [...cache.worktrees, worktreePath];
155
- cache = { repos, worktrees, populatedAt: cache.populatedAt };
156
- }
157
- function removeWorktreeFromCache(worktreePath) {
158
- if (!cache) return;
159
- const next = cache.worktrees.filter((p) => p !== worktreePath);
160
- if (next.length === cache.worktrees.length) return;
161
- cache = { repos: cache.repos, worktrees: next, populatedAt: cache.populatedAt };
162
- }
163
-
164
- // src/shared/capabilities/git-repo.ts
165
- var gitRepoCache = /* @__PURE__ */ new Map();
166
- async function isGitRepo(cwd) {
167
- const cached = gitRepoCache.get(cwd);
168
- if (cached !== void 0) return cached;
169
- try {
170
- await runWithTimeout("git", ["rev-parse", "--git-dir"], cwd, TIMEOUT_MS);
171
- gitRepoCache.set(cwd, true);
172
- return true;
173
- } catch {
174
- gitRepoCache.set(cwd, false);
175
- return false;
176
- }
177
- }
178
- var ghAvailableCache = false;
179
- async function isGhAvailable() {
180
- if (ghAvailableCache) return true;
181
- try {
182
- await run("which", ["gh"]);
183
- ghAvailableCache = true;
184
- return true;
185
- } catch {
186
- return false;
187
- }
188
- }
189
- var topLevelCache = /* @__PURE__ */ new Map();
190
- async function getGitTopLevel(cwd) {
191
- const cached = topLevelCache.get(cwd);
192
- if (cached !== void 0) return cached;
193
- const topLevel = (await runWithTimeout("git", ["rev-parse", "--show-toplevel"], cwd, TIMEOUT_MS)).trim();
194
- topLevelCache.set(cwd, topLevel);
195
- return topLevel;
196
- }
197
- function parseOwnerRepo(remoteUrl) {
198
- const match = remoteUrl.match(/github\.com[:/]([^/]+)\/([^/.]+)/);
199
- if (!match?.[1] || !match[2]) return null;
200
- return { owner: match[1], repo: match[2] };
201
- }
202
- async function getRepoDefaultBranch(cwd) {
203
- try {
204
- const head = (await runWithTimeout(
205
- "git",
206
- ["symbolic-ref", "--short", "refs/remotes/origin/HEAD"],
207
- cwd,
208
- TIMEOUT_MS
209
- )).trim();
210
- if (!head) return null;
211
- return head.replace(/^origin\//, "") || null;
212
- } catch {
213
- return null;
214
- }
215
- }
216
- async function isAncestor(ancestor, descendant, cwd) {
217
- try {
218
- await runWithTimeout("git", ["merge-base", "--is-ancestor", ancestor, descendant], cwd, 5e3);
219
- return true;
220
- } catch {
221
- return false;
222
- }
223
- }
224
- var EXCLUDE_DIRS = /* @__PURE__ */ new Set([
225
- "node_modules",
226
- "Library",
227
- "Applications",
228
- "Pictures",
229
- "Music",
230
- "Movies",
231
- "go",
232
- ".Trash"
233
- ]);
234
- var MAX_DEPTH = 4;
235
- async function findGitRepos(dir, depth = 0) {
236
- if (depth > MAX_DEPTH) return [];
237
- try {
238
- const entries = await readdir(dir, { withFileTypes: true });
239
- for (const entry of entries) {
240
- if (entry.name === ".git") {
241
- return [dir];
242
- }
243
- }
244
- if (depth >= MAX_DEPTH) return [];
245
- const promises = [];
246
- for (const entry of entries) {
247
- if (!entry.isDirectory()) continue;
248
- if (entry.name.startsWith(".")) continue;
249
- if (EXCLUDE_DIRS.has(entry.name)) continue;
250
- promises.push(findGitRepos(join3(dir, entry.name), depth + 1));
251
- }
252
- const results = await Promise.all(promises);
253
- return results.flat();
254
- } catch {
255
- return [];
256
- }
257
- }
258
- async function getRepoMetadata(repoPath, cached) {
259
- try {
260
- if (cached) {
261
- return {
262
- path: repoPath,
263
- name: basename(repoPath),
264
- branch: cached.branch,
265
- ...cached.remote !== null && { remote: cached.remote }
266
- };
267
- }
268
- const [branchResult, remoteResult] = await Promise.allSettled([
269
- run("git", ["branch", "--show-current"], repoPath),
270
- run("git", ["remote", "get-url", "origin"], repoPath)
271
- ]);
272
- const branch = branchResult.status === "fulfilled" ? branchResult.value || "HEAD" : "HEAD";
273
- const remote = remoteResult.status === "fulfilled" ? remoteResult.value || void 0 : void 0;
274
- return {
275
- path: repoPath,
276
- name: basename(repoPath),
277
- branch,
278
- ...remote && { remote }
279
- };
280
- } catch {
281
- return null;
282
- }
283
- }
284
- async function detectEnvironments(lastKnown) {
285
- const { homedir } = await import("os");
286
- const cachedPaths = getCachedRepoPaths();
287
- let repoPaths;
288
- let worktreePaths;
289
- if (cachedPaths) {
290
- repoPaths = cachedPaths.repos;
291
- worktreePaths = cachedPaths.worktrees;
292
- } else {
293
- try {
294
- repoPaths = await findGitRepos(homedir());
295
- } catch (err) {
296
- if (lastKnown && lastKnown.length > 0) {
297
- const { logger: logger2 } = await import("./logger-GQCSLSZH.js");
298
- logger2.warn(
299
- { err, lastKnownCount: lastKnown.length },
300
- "detectEnvironments findGitRepos threw \u2014 preserving lastKnown"
301
- );
302
- return lastKnown;
303
- }
304
- return [];
305
- }
306
- if (repoPaths.length === 0 && lastKnown && lastKnown.length > 0) {
307
- const { logger: logger2 } = await import("./logger-GQCSLSZH.js");
308
- logger2.warn(
309
- { lastKnownCount: lastKnown.length },
310
- "detectEnvironments walk empty but lastKnown non-empty \u2014 preserving lastKnown"
311
- );
312
- return lastKnown;
313
- }
314
- worktreePaths = await discoverWorktrees(repoPaths);
315
- setCachedRepoPaths(repoPaths, worktreePaths);
316
- }
317
- const allPaths = [.../* @__PURE__ */ new Set([...repoPaths, ...worktreePaths])];
318
- const prevCache = await loadCache();
319
- const headMtimes = await Promise.all(allPaths.map(getHeadMtime));
320
- const nextCache = /* @__PURE__ */ new Map();
321
- const resolved = await Promise.all(
322
- allPaths.map(async (path, idx) => {
323
- const currentMtime = headMtimes[idx] ?? null;
324
- const prev = prevCache.get(path);
325
- const cacheHit = prev !== void 0 && currentMtime !== null && isCacheValid(prev, currentMtime);
326
- const info = await getRepoMetadata(path, cacheHit ? prev : void 0);
327
- if (info !== null && currentMtime !== null) {
328
- nextCache.set(path, {
329
- path,
330
- branch: info.branch,
331
- remote: info.remote ?? null,
332
- headMtimeMs: currentMtime,
333
- cachedAt: cacheHit && prev ? prev.cachedAt : Date.now()
334
- });
335
- }
336
- return info;
337
- })
338
- );
339
- await saveCache(nextCache);
340
- return resolved.filter((info) => info !== null);
341
- }
342
- async function discoverWorktrees(repoPaths) {
343
- const results = await Promise.allSettled(repoPaths.map(listWorktreePaths));
344
- return results.flatMap((r) => r.status === "fulfilled" ? r.value : []);
345
- }
346
- async function listWorktreePaths(repoPath) {
347
- try {
348
- const output = await runWithTimeout(
349
- "git",
350
- ["worktree", "list", "--porcelain"],
351
- repoPath,
352
- TIMEOUT_MS
353
- );
354
- return parseWorktreeListOutput(output, repoPath);
355
- } catch {
356
- return [];
357
- }
358
- }
359
- function parseWorktreeListOutput(output, repoPath) {
360
- const paths = [];
361
- for (const line of output.split("\n")) {
362
- if (line.startsWith("worktree ")) {
363
- const wt = line.slice("worktree ".length).trim();
364
- if (wt && wt !== repoPath) paths.push(wt);
365
- }
366
- }
367
- return paths;
368
- }
369
- var _testing = {
370
- parseWorktreeListOutput,
371
- resetGhAvailableCache: () => {
372
- ghAvailableCache = false;
373
- }
374
- };
375
-
376
- export {
377
- isEnoent,
378
- isEexist,
379
- isCorruptionError,
380
- findProjectRoot,
381
- addWorktreeToCache,
382
- removeWorktreeFromCache,
383
- gitRepoCache,
384
- isGitRepo,
385
- isGhAvailable,
386
- getGitTopLevel,
387
- parseOwnerRepo,
388
- getRepoDefaultBranch,
389
- isAncestor,
390
- findGitRepos,
391
- getRepoMetadata,
392
- detectEnvironments,
393
- _testing
394
- };
395
- //# sourceMappingURL=chunk-RXI4637N.js.map