@senpiper/sp-agent 0.1.10 → 0.1.11

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 (86) hide show
  1. package/INSTALL.md +36 -24
  2. package/README.md +13 -13
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +4 -4
  5. package/dist/cli.js.map +1 -1
  6. package/dist/distribution.d.ts +2 -2
  7. package/dist/distribution.d.ts.map +1 -1
  8. package/dist/distribution.js +1 -1
  9. package/dist/distribution.js.map +1 -1
  10. package/dist/extension-identity.generated.d.ts +3 -3
  11. package/dist/extension-identity.generated.d.ts.map +1 -1
  12. package/dist/extension-identity.generated.js +3 -3
  13. package/dist/extension-identity.generated.js.map +1 -1
  14. package/dist/install-sp.sh +9 -9
  15. package/dist/release-manager.d.ts.map +1 -1
  16. package/dist/release-manager.js +2 -2
  17. package/dist/release-manager.js.map +1 -1
  18. package/dist/runtime-cli.d.ts.map +1 -1
  19. package/dist/runtime-cli.js +4 -4
  20. package/dist/runtime-cli.js.map +1 -1
  21. package/dist/sdk.d.ts +1 -1
  22. package/dist/sdk.d.ts.map +1 -1
  23. package/dist/sdk.js +5 -5
  24. package/dist/sdk.js.map +1 -1
  25. package/dist/session-manager.d.ts +1 -1
  26. package/dist/session-manager.d.ts.map +1 -1
  27. package/dist/session-manager.js +15 -15
  28. package/dist/session-manager.js.map +1 -1
  29. package/dist/sp-web.d.ts.map +1 -1
  30. package/dist/sp-web.js +2 -2
  31. package/dist/sp-web.js.map +1 -1
  32. package/dist/version.d.ts +1 -1
  33. package/dist/version.d.ts.map +1 -1
  34. package/dist/version.js +1 -1
  35. package/dist/version.js.map +1 -1
  36. package/node_modules/@senpiper/pi-coding-agent/dist/bundle/chunks/{chunk-LUSUF4ZH.js → chunk-VVKE4QZJ.js} +1 -1
  37. package/node_modules/@senpiper/pi-coding-agent/dist/bundle/cli.js +1 -1
  38. package/node_modules/@senpiper/pi-coding-agent/dist/bundle/index.js +1 -1
  39. package/node_modules/@senpiper/pi-coding-agent/dist/bundle/rpc-entry.js +1 -1
  40. package/node_modules/@senpiper/pi-coding-agent/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  41. package/node_modules/@senpiper/pi-coding-agent/node_modules/@earendil-works/pi-ai/dist/providers/data/amazon-bedrock.json +1 -1
  42. package/node_modules/@senpiper/pi-coding-agent/node_modules/@earendil-works/pi-ai/dist/providers/data/baseten.json +1 -1
  43. package/node_modules/@senpiper/pi-coding-agent/node_modules/@earendil-works/pi-ai/dist/providers/data/fireworks.json +1 -1
  44. package/node_modules/@senpiper/pi-coding-agent/node_modules/@earendil-works/pi-ai/dist/providers/data/huggingface.json +1 -1
  45. package/node_modules/@senpiper/pi-coding-agent/node_modules/@earendil-works/pi-ai/dist/providers/data/kimi-coding.json +1 -1
  46. package/node_modules/@senpiper/pi-coding-agent/node_modules/@earendil-works/pi-ai/dist/providers/data/nvidia.json +1 -1
  47. package/node_modules/@senpiper/pi-coding-agent/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode-go.json +1 -1
  48. package/node_modules/@senpiper/pi-coding-agent/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  49. package/node_modules/@senpiper/pi-coding-agent/node_modules/@earendil-works/pi-ai/dist/providers/data/qwen-token-plan-cn.json +1 -1
  50. package/node_modules/@senpiper/pi-coding-agent/node_modules/@earendil-works/pi-ai/dist/providers/data/qwen-token-plan.json +1 -1
  51. package/node_modules/@senpiper/pi-coding-agent/node_modules/@earendil-works/pi-ai/dist/providers/data/together.json +1 -1
  52. package/node_modules/@senpiper/pi-coding-agent/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
  53. package/node_modules/@senpiper/pi-coding-agent/package.json +1 -1
  54. package/node_modules/@senpiper/sp-agent-extensions/CHANGELOG.md +17 -0
  55. package/node_modules/@senpiper/sp-agent-extensions/CONCURRENT_SESSIONS.md +9 -9
  56. package/node_modules/@senpiper/sp-agent-extensions/TERMINAL_WEB_BRIDGE.md +28 -9
  57. package/node_modules/@senpiper/sp-agent-extensions/dist/cli-commands.d.ts.map +1 -1
  58. package/node_modules/@senpiper/sp-agent-extensions/dist/cli-commands.js +5 -5
  59. package/node_modules/@senpiper/sp-agent-extensions/dist/cli-commands.js.map +1 -1
  60. package/node_modules/@senpiper/sp-agent-extensions/dist/gateway.d.ts +1 -1
  61. package/node_modules/@senpiper/sp-agent-extensions/dist/gateway.d.ts.map +1 -1
  62. package/node_modules/@senpiper/sp-agent-extensions/dist/gateway.js +18 -18
  63. package/node_modules/@senpiper/sp-agent-extensions/dist/gateway.js.map +1 -1
  64. package/node_modules/@senpiper/sp-agent-extensions/dist/index.d.ts +1 -1
  65. package/node_modules/@senpiper/sp-agent-extensions/dist/index.d.ts.map +1 -1
  66. package/node_modules/@senpiper/sp-agent-extensions/dist/index.js +1 -1
  67. package/node_modules/@senpiper/sp-agent-extensions/dist/index.js.map +1 -1
  68. package/node_modules/@senpiper/sp-agent-extensions/dist/login-command.d.ts.map +1 -1
  69. package/node_modules/@senpiper/sp-agent-extensions/dist/login-command.js +9 -9
  70. package/node_modules/@senpiper/sp-agent-extensions/dist/login-command.js.map +1 -1
  71. package/node_modules/@senpiper/sp-agent-extensions/dist/sp-web-launcher.d.ts +27 -0
  72. package/node_modules/@senpiper/sp-agent-extensions/dist/sp-web-launcher.d.ts.map +1 -1
  73. package/node_modules/@senpiper/sp-agent-extensions/dist/sp-web-launcher.js +270 -28
  74. package/node_modules/@senpiper/sp-agent-extensions/dist/sp-web-launcher.js.map +1 -1
  75. package/node_modules/@senpiper/sp-agent-extensions/dist/startup-header.d.ts.map +1 -1
  76. package/node_modules/@senpiper/sp-agent-extensions/dist/startup-header.js +12 -19
  77. package/node_modules/@senpiper/sp-agent-extensions/dist/startup-header.js.map +1 -1
  78. package/node_modules/@senpiper/sp-agent-extensions/dist/terminal-web-bridge.d.ts +1 -1
  79. package/node_modules/@senpiper/sp-agent-extensions/dist/terminal-web-bridge.d.ts.map +1 -1
  80. package/node_modules/@senpiper/sp-agent-extensions/dist/terminal-web-bridge.js +11 -9
  81. package/node_modules/@senpiper/sp-agent-extensions/dist/terminal-web-bridge.js.map +1 -1
  82. package/node_modules/@senpiper/sp-agent-extensions/dist/web-command.d.ts.map +1 -1
  83. package/node_modules/@senpiper/sp-agent-extensions/dist/web-command.js +36 -10
  84. package/node_modules/@senpiper/sp-agent-extensions/dist/web-command.js.map +1 -1
  85. package/node_modules/@senpiper/sp-agent-extensions/package.json +3 -2
  86. package/package.json +7 -6
@@ -1 +1 @@
1
- {"version":3,"file":"gateway.js","sourceRoot":"","sources":["../src/gateway.ts"],"names":[],"mappings":"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","sourcesContent":["import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport type { ExtensionAPI, ExtensionFactory, ProviderConfig, ProviderModelConfig } from \"@senpiper/pi-coding-agent\";\nimport type { Api, Model, OpenAICompletionsCompat, RefreshModelsContext } from \"@senpiper/pi-coding-agent/runtime-deps\";\n\nexport type SpExtensionApi = ExtensionAPI;\nexport type SpProviderModelConfig = ProviderModelConfig;\n\nexport const SP_GATEWAY_PROVIDER_ID = \"sp-gateway\";\nexport const LEGACY_SP_GATEWAY_PROVIDER_ID = \"sengateway\";\nexport const SP_GATEWAY_PROVIDER_NAME = \"sp-gateway\";\nexport const SP_USER_AGENT = \"sp\";\nexport const SP_CLIENT_NAME = \"sp-agent\";\n\nexport interface SpGatewayState {\n\tschemaVersion: 1;\n\tbaseUrl: string;\n\tmodels: SpProviderModelConfig[];\n}\n\nexport interface SpGatewayWarning {\n\tkind: \"refresh_failed\" | \"legacy_cache_imported\";\n\tmessage: string;\n\tat: string;\n}\n\ntype JsonRecord = Record<string, unknown>;\n\nlet gatewayWarning: SpGatewayWarning | undefined;\n\nfunction isRecord(value: unknown): value is JsonRecord {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction stringValue(value: unknown) {\n\treturn typeof value === \"string\" && value.trim() ? value.trim() : undefined;\n}\n\nfunction finiteNumber(value: unknown) {\n\treturn typeof value === \"number\" && Number.isFinite(value) ? value : undefined;\n}\n\nfunction positiveInteger(value: unknown) {\n\tconst number = finiteNumber(value);\n\treturn number !== undefined && number > 0 ? Math.floor(number) : undefined;\n}\n\nfunction nonNegativeNumber(value: unknown) {\n\tconst number = finiteNumber(value);\n\treturn number !== undefined && number >= 0 ? number : undefined;\n}\n\nfunction booleanValue(value: unknown) {\n\treturn typeof value === \"boolean\" ? value : undefined;\n}\n\nexport function getSpAgentDir() {\n\treturn process.env.SP_CODING_AGENT_DIR || join(homedir(), \".sp\", \"agent\");\n}\n\nexport function getSpGatewayStatePath(agentDir = getSpAgentDir()) {\n\treturn join(agentDir, \"sp-gateway.json\");\n}\n\nexport function getLegacyModelsPath(agentDir = getSpAgentDir()) {\n\treturn join(agentDir, \"models.json\");\n}\n\nexport function getSpAuthPath(agentDir = getSpAgentDir()) {\n\treturn join(agentDir, \"auth.json\");\n}\n\nexport function getSpSettingsPath(agentDir = getSpAgentDir()) {\n\treturn join(agentDir, \"settings.json\");\n}\n\nfunction readJsonFile(path: string): JsonRecord {\n\tif (!existsSync(path)) return {};\n\tconst content = readFileSync(path, \"utf8\").trim();\n\tif (!content) return {};\n\tconst parsed = JSON.parse(content) as unknown;\n\tif (!isRecord(parsed)) throw new Error(`Expected an object in ${path}`);\n\treturn parsed;\n}\n\nfunction writePrivateJson(path: string, value: unknown) {\n\tmkdirSync(dirname(path), { recursive: true });\n\tconst temporaryPath = `${path}.${process.pid}.tmp`;\n\twriteFileSync(temporaryPath, `${JSON.stringify(value, null, \"\\t\")}\\n`, { encoding: \"utf8\", mode: 0o600 });\n\tif (process.platform !== \"win32\") chmodSync(temporaryPath, 0o600);\n\trenameSync(temporaryPath, path);\n\tif (process.platform !== \"win32\") chmodSync(path, 0o600);\n}\n\nexport function normalizeSpGatewayBaseUrl(input: string) {\n\tconst trimmed = input.trim();\n\tif (!trimmed) throw new Error(\"Gateway URL cannot be empty.\");\n\tconst withScheme = /^[a-z][a-z0-9+.-]*:\\/\\//iu.test(trimmed) ? trimmed : `https://${trimmed}`;\n\tlet url: URL;\n\ttry {\n\t\turl = new URL(withScheme);\n\t} catch {\n\t\tthrow new Error(`Invalid gateway URL: ${input}`);\n\t}\n\tif (url.protocol !== \"https:\" && url.protocol !== \"http:\") {\n\t\tthrow new Error(\"Gateway URL must use HTTPS or HTTP.\");\n\t}\n\turl.hash = \"\";\n\turl.search = \"\";\n\turl.pathname = url.pathname.replace(/\\/+$/u, \"\");\n\tif (!url.pathname.endsWith(\"/v1\")) url.pathname = `${url.pathname}/v1`.replace(/\\/+/gu, \"/\");\n\treturn url.toString().replace(/\\/$/u, \"\");\n}\n\nfunction parseInput(value: unknown): (\"text\" | \"image\")[] | undefined {\n\tif (!Array.isArray(value)) return undefined;\n\tconst input = value.filter((entry): entry is \"text\" | \"image\" => entry === \"text\" || entry === \"image\");\n\treturn input.length > 0 ? [...new Set(input)] : undefined;\n}\n\nfunction parseCost(value: unknown): SpProviderModelConfig[\"cost\"] | undefined {\n\tif (!isRecord(value)) return undefined;\n\tconst input = nonNegativeNumber(value.input);\n\tconst output = nonNegativeNumber(value.output);\n\tconst cacheRead = nonNegativeNumber(value.cacheRead);\n\tconst cacheWrite = nonNegativeNumber(value.cacheWrite);\n\tif (input === undefined && output === undefined && cacheRead === undefined && cacheWrite === undefined)\n\t\treturn undefined;\n\treturn { input: input ?? 0, output: output ?? 0, cacheRead: cacheRead ?? 0, cacheWrite: cacheWrite ?? 0 };\n}\n\nfunction parseThinkingLevelMap(value: unknown): SpProviderModelConfig[\"thinkingLevelMap\"] {\n\tif (!isRecord(value)) return undefined;\n\tconst result: NonNullable<SpProviderModelConfig[\"thinkingLevelMap\"]> = {};\n\tfor (const key of [\"off\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\"] as const) {\n\t\tconst entry = value[key];\n\t\tif (typeof entry === \"string\" || entry === null) result[key] = entry;\n\t}\n\treturn Object.keys(result).length > 0 ? result : undefined;\n}\n\nfunction thinkingFormat(value: unknown): OpenAICompletionsCompat[\"thinkingFormat\"] {\n\tswitch (value) {\n\t\tcase \"openai\":\n\t\tcase \"openrouter\":\n\t\tcase \"deepseek\":\n\t\tcase \"together\":\n\t\tcase \"zai\":\n\t\tcase \"qwen\":\n\t\tcase \"chat-template\":\n\t\tcase \"qwen-chat-template\":\n\t\tcase \"string-thinking\":\n\t\tcase \"ant-ling\":\n\t\t\treturn value;\n\t\tdefault:\n\t\t\treturn undefined;\n\t}\n}\n\nfunction sessionAffinityFormat(value: unknown): OpenAICompletionsCompat[\"sessionAffinityFormat\"] {\n\treturn value === \"openai\" || value === \"openai-nosession\" || value === \"openrouter\" ? value : undefined;\n}\n\nfunction parseCompat(value: unknown): OpenAICompletionsCompat {\n\tconst record = isRecord(value) ? value : {};\n\tconst maxTokensField =\n\t\trecord.maxTokensField === \"max_completion_tokens\" || record.maxTokensField === \"max_tokens\"\n\t\t\t? record.maxTokensField\n\t\t\t: undefined;\n\tconst compat = {\n\t\tsupportsStore: booleanValue(record.supportsStore) ?? false,\n\t\tsupportsDeveloperRole: booleanValue(record.supportsDeveloperRole),\n\t\tsupportsReasoningEffort: booleanValue(record.supportsReasoningEffort),\n\t\tsupportsUsageInStreaming: booleanValue(record.supportsUsageInStreaming),\n\t\tmaxTokensField,\n\t\trequiresToolResultName: booleanValue(record.requiresToolResultName),\n\t\trequiresAssistantAfterToolResult: booleanValue(record.requiresAssistantAfterToolResult),\n\t\trequiresThinkingAsText: booleanValue(record.requiresThinkingAsText),\n\t\trequiresReasoningContentOnAssistantMessages: booleanValue(record.requiresReasoningContentOnAssistantMessages),\n\t\tthinkingFormat: thinkingFormat(record.thinkingFormat),\n\t\tsupportsStrictMode: booleanValue(record.supportsStrictMode),\n\t\tsendSessionAffinityHeaders: booleanValue(record.sendSessionAffinityHeaders),\n\t\tsessionAffinityFormat: sessionAffinityFormat(record.sessionAffinityFormat),\n\t\tsupportsLongCacheRetention: booleanValue(record.supportsLongCacheRetention),\n\t} satisfies OpenAICompletionsCompat;\n\treturn compat;\n}\n\nfunction parseGatewayModel(value: unknown): SpProviderModelConfig | undefined {\n\tconst record = typeof value === \"string\" ? { id: value } : isRecord(value) ? value : undefined;\n\tif (!record) return undefined;\n\tconst id = stringValue(record.id);\n\tif (!id || id.length > 256 || /[\\u0000-\\u001f\\u007f]/u.test(id)) return undefined;\n\tconst metadata = isRecord(record.metadata) ? record.metadata : {};\n\tconst field = (name: string) => metadata[name] ?? record[name];\n\tconst reasoning = field(\"reasoning\");\n\treturn {\n\t\tid,\n\t\tname: stringValue(field(\"name\")) ?? id,\n\t\treasoning: typeof reasoning === \"boolean\" ? reasoning : false,\n\t\tthinkingLevelMap: parseThinkingLevelMap(field(\"thinkingLevelMap\")),\n\t\tinput: parseInput(field(\"input\")) ?? [\"text\"],\n\t\tcost: parseCost(field(\"cost\")) ?? { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n\t\tcontextWindow: positiveInteger(field(\"contextWindow\")) ?? 128_000,\n\t\tmaxTokens: positiveInteger(field(\"maxTokens\")) ?? 32_000,\n\t\tcompat: parseCompat(field(\"compat\")),\n\t};\n}\n\nfunction parseModelCatalog(values: readonly unknown[], source: string) {\n\tconst models = values.map(parseGatewayModel).filter((model): model is SpProviderModelConfig => model !== undefined);\n\tif (models.length === 0) throw new Error(`${source} contains no valid models.`);\n\tconst ids = new Set<string>();\n\tfor (const model of models) {\n\t\tif (ids.has(model.id)) throw new Error(`${source} contains duplicate model id ${model.id}.`);\n\t\tids.add(model.id);\n\t}\n\treturn models;\n}\n\nexport async function fetchSpGatewayModels(baseUrl: string, apiKey: string, signal?: AbortSignal) {\n\tconst response = await fetch(`${baseUrl.replace(/\\/$/u, \"\")}/models`, {\n\t\tsignal,\n\t\theaders: {\n\t\t\tAuthorization: `Bearer ${apiKey}`,\n\t\t\tAccept: \"application/json\",\n\t\t\t\"User-Agent\": SP_USER_AGENT,\n\t\t\t\"X-Senpiper-Client\": SP_CLIENT_NAME,\n\t\t},\n\t});\n\tif (!response.ok) {\n\t\tconst body = await response.text().catch(() => \"\");\n\t\tconst suffix = body.trim() ? `: ${body.trim().slice(0, 300)}` : \"\";\n\t\tthrow new Error(`sp-gateway rejected /v1/models (${response.status} ${response.statusText})${suffix}`);\n\t}\n\tconst payload = (await response.json()) as unknown;\n\tif (!isRecord(payload) || !Array.isArray(payload.data)) {\n\t\tthrow new Error(\"sp-gateway returned an invalid /v1/models envelope.\");\n\t}\n\treturn parseModelCatalog(payload.data, \"sp-gateway /v1/models response\");\n}\n\nfunction readLegacySpGatewayState(agentDir: string): SpGatewayState | undefined {\n\tconst path = getLegacyModelsPath(agentDir);\n\tif (!existsSync(path)) return undefined;\n\tconst root = readJsonFile(path);\n\tconst providers = isRecord(root.providers) ? root.providers : undefined;\n\tconst providerValue = providers?.[SP_GATEWAY_PROVIDER_ID] ?? providers?.[LEGACY_SP_GATEWAY_PROVIDER_ID];\n\tconst provider = isRecord(providerValue) ? providerValue : undefined;\n\tif (!provider || typeof provider.baseUrl !== \"string\" || !Array.isArray(provider.models)) return undefined;\n\treturn {\n\t\tschemaVersion: 1,\n\t\tbaseUrl: normalizeSpGatewayBaseUrl(provider.baseUrl),\n\t\tmodels: parseModelCatalog(provider.models, `legacy sp-gateway cache ${path}`),\n\t};\n}\n\nexport function readSpGatewayState(agentDir = getSpAgentDir()): SpGatewayState | undefined {\n\tconst path = getSpGatewayStatePath(agentDir);\n\tif (!existsSync(path)) {\n\t\tconst legacy = readLegacySpGatewayState(agentDir);\n\t\tif (!legacy) return undefined;\n\t\twriteSpGatewayState(legacy.baseUrl, legacy.models, agentDir);\n\t\tgatewayWarning = {\n\t\t\tkind: \"legacy_cache_imported\",\n\t\t\tmessage: `Imported the legacy sp-gateway model cache from ${getLegacyModelsPath(agentDir)}.`,\n\t\t\tat: new Date().toISOString(),\n\t\t};\n\t\treturn legacy;\n\t}\n\tconst value = readJsonFile(path);\n\tif (value.schemaVersion !== 1 || typeof value.baseUrl !== \"string\" || !Array.isArray(value.models)) {\n\t\tthrow new Error(`Invalid sp-gateway configuration: ${path}`);\n\t}\n\treturn {\n\t\tschemaVersion: 1,\n\t\tbaseUrl: normalizeSpGatewayBaseUrl(value.baseUrl),\n\t\tmodels: parseModelCatalog(value.models, `sp-gateway configuration ${path}`),\n\t};\n}\n\nexport function writeSpGatewayState(baseUrl: string, models: SpProviderModelConfig[], agentDir = getSpAgentDir()) {\n\tconst validatedModels = parseModelCatalog(models, \"sp-gateway state\");\n\twritePrivateJson(getSpGatewayStatePath(agentDir), {\n\t\tschemaVersion: 1,\n\t\tbaseUrl: normalizeSpGatewayBaseUrl(baseUrl),\n\t\tmodels: validatedModels,\n\t} satisfies SpGatewayState);\n}\n\nexport function readSpGatewayApiKey(agentDir = getSpAgentDir()) {\n\tconst path = getSpAuthPath(agentDir);\n\tconst auth = readJsonFile(path);\n\tlet credential = auth[SP_GATEWAY_PROVIDER_ID];\n\tif (!credential && auth[LEGACY_SP_GATEWAY_PROVIDER_ID]) {\n\t\tcredential = auth[LEGACY_SP_GATEWAY_PROVIDER_ID];\n\t\tauth[SP_GATEWAY_PROVIDER_ID] = credential;\n\t\tdelete auth[LEGACY_SP_GATEWAY_PROVIDER_ID];\n\t\twritePrivateJson(path, auth);\n\t}\n\treturn isRecord(credential) && credential.type === \"api_key\" && typeof credential.key === \"string\"\n\t\t? credential.key\n\t\t: undefined;\n}\n\nexport function writeSpGatewayLogin(apiKey: string, firstModelId: string, agentDir = getSpAgentDir()) {\n\tconst authPath = getSpAuthPath(agentDir);\n\tconst auth = readJsonFile(authPath);\n\tauth[SP_GATEWAY_PROVIDER_ID] = { type: \"api_key\", key: apiKey };\n\tdelete auth[LEGACY_SP_GATEWAY_PROVIDER_ID];\n\twritePrivateJson(authPath, auth);\n\n\tconst settingsPath = getSpSettingsPath(agentDir);\n\tconst settings = readJsonFile(settingsPath);\n\tsettings.defaultProvider = SP_GATEWAY_PROVIDER_ID;\n\tsettings.defaultModel = firstModelId;\n\twritePrivateJson(settingsPath, settings);\n}\n\nexport function removeSpGatewayLogin(agentDir = getSpAgentDir()) {\n\tconst path = getSpAuthPath(agentDir);\n\tconst auth = readJsonFile(path);\n\tdelete auth[SP_GATEWAY_PROVIDER_ID];\n\tdelete auth[LEGACY_SP_GATEWAY_PROVIDER_ID];\n\twritePrivateJson(path, auth);\n}\n\nfunction credentialKey(context: RefreshModelsContext) {\n\treturn context.credential?.type === \"api_key\" ? context.credential.key : undefined;\n}\n\nfunction storedModels(models: readonly SpProviderModelConfig[], baseUrl: string): Model<Api>[] {\n\treturn models.map((model) => ({\n\t\t...model,\n\t\tprovider: SP_GATEWAY_PROVIDER_ID,\n\t\tbaseUrl,\n\t\tapi: \"openai-completions\",\n\t}));\n}\n\nexport function getSpGatewayWarning() {\n\treturn gatewayWarning;\n}\n\nexport function clearSpGatewayWarning() {\n\tgatewayWarning = undefined;\n}\n\nexport function createSpGatewayProviderConfig(\n\tbaseUrl: string,\n\tmodels: SpProviderModelConfig[],\n\tagentDir = getSpAgentDir(),\n): ProviderConfig {\n\tlet currentModels = parseModelCatalog(models, \"sp-gateway provider configuration\");\n\treturn {\n\t\tname: SP_GATEWAY_PROVIDER_NAME,\n\t\tbaseUrl,\n\t\tapi: \"openai-completions\",\n\t\tauthHeader: true,\n\t\theaders: {\n\t\t\t\"User-Agent\": SP_USER_AGENT,\n\t\t\t\"X-Senpiper-Client\": SP_CLIENT_NAME,\n\t\t},\n\t\tmodels: currentModels,\n\t\tasync refreshModels(context) {\n\t\t\tif (!context.allowNetwork) return currentModels;\n\t\t\tconst apiKey = credentialKey(context);\n\t\t\tif (!apiKey) return currentModels;\n\t\t\ttry {\n\t\t\t\tconst refreshed = await fetchSpGatewayModels(baseUrl, apiKey, context.signal);\n\t\t\t\tif (context.signal.aborted) return currentModels;\n\t\t\t\tawait context.publish({\n\t\t\t\t\tpersist: { models: storedModels(refreshed, baseUrl), checkedAt: Date.now() },\n\t\t\t\t\tupdate: () => {\n\t\t\t\t\t\twriteSpGatewayState(baseUrl, refreshed, agentDir);\n\t\t\t\t\t\tcurrentModels = refreshed;\n\t\t\t\t\t\tgatewayWarning = undefined;\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\treturn currentModels;\n\t\t\t} catch (error) {\n\t\t\t\tgatewayWarning = {\n\t\t\t\t\tkind: \"refresh_failed\",\n\t\t\t\t\tmessage: `sp-gateway model refresh failed; using ${currentModels.length} cached model${currentModels.length === 1 ? \"\" : \"s\"}: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\t\tat: new Date().toISOString(),\n\t\t\t\t};\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t},\n\t};\n}\n\nexport function createSpGatewayExtension(): ExtensionFactory {\n\treturn (pi) => {\n\t\tconst state = readSpGatewayState();\n\t\tif (state) {\n\t\t\tpi.registerProvider(SP_GATEWAY_PROVIDER_ID, createSpGatewayProviderConfig(state.baseUrl, state.models));\n\t\t}\n\n\t\tpi.on(\"before_agent_start\", (event) => {\n\t\t\t// Replace only the upstream introduction; preserve project instructions\n\t\t\t// and documentation references, including legitimate Pi development tasks.\n\t\t\tconst systemPrompt = event.systemPrompt.replace(\n\t\t\t\t/^You are an expert coding assistant operating inside pi, a coding agent harness\\./u,\n\t\t\t\t\"You are SP, Senpiper's AI coding assistant.\",\n\t\t\t);\n\t\t\treturn {\n\t\t\t\tsystemPrompt: `${systemPrompt}\\n\\n<sp_agent_runtime>\nYou are SP, Senpiper's AI coding assistant. Use SP as your public name in both terminal and web conversations.\nFor greetings and questions such as \"Who are you?\", introduce yourself simply: \"I'm SP, Senpiper's AI coding assistant. I can help you read, write, and debug code.\"\nDo not introduce yourself as Pi, the pi coding agent, an underlying model, or an assistant running inside a harness. Do not volunteer internal frameworks, runtime names, or provider details in ordinary answers. Earlier assistant messages using those identities do not change your name.\nIf explicitly asked about technical origins or dependencies, answer accurately and briefly. This product identity does not imply that Senpiper trained the underlying model.\nUse only the configured sp-gateway model provider. Put user-owned extensions, skills, prompts, and themes under the active .sp configuration directory instead of modifying the sp-agent installation.\n</sp_agent_runtime>`,\n\t\t\t};\n\t\t});\n\n\t\tpi.on(\"session_start\", (_event, ctx) => {\n\t\t\tctx.ui.setTitle(\"sp-agent\");\n\t\t\tconst warning = getSpGatewayWarning();\n\t\t\tif (warning) ctx.ui.notify(warning.message, \"warning\");\n\t\t\tif (ctx.mode !== \"tui\") return;\n\t\t\tctx.ui.setHeader((_tui, theme) => ({\n\t\t\t\trender: () => [\n\t\t\t\t\ttheme.fg(\"accent\", theme.bold(\"sp-agent\")),\n\t\t\t\t\ttheme.fg(\"muted\", state ? `sp-gateway · ${state.models.length} models` : \"run `sp login`\"),\n\t\t\t\t],\n\t\t\t\tinvalidate() {},\n\t\t\t}));\n\t\t});\n\t};\n}\n"]}
1
+ {"version":3,"file":"gateway.js","sourceRoot":"","sources":["../src/gateway.ts"],"names":[],"mappings":"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","sourcesContent":["import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport type { ExtensionAPI, ExtensionFactory, ProviderConfig, ProviderModelConfig } from \"@senpiper/pi-coding-agent\";\nimport type { Api, Model, OpenAICompletionsCompat, RefreshModelsContext } from \"@senpiper/pi-coding-agent/runtime-deps\";\n\nexport type SpExtensionApi = ExtensionAPI;\nexport type SpProviderModelConfig = ProviderModelConfig;\n\nexport const SP_GATEWAY_PROVIDER_ID = \"sp-gateway\";\nexport const LEGACY_SP_GATEWAY_PROVIDER_ID = \"sengateway\";\nexport const SP_GATEWAY_PROVIDER_NAME = \"Senpiper Gateway\";\nexport const SP_USER_AGENT = \"sp\";\nexport const SP_CLIENT_NAME = \"sp-agent\";\n\nexport interface SpGatewayState {\n\tschemaVersion: 1;\n\tbaseUrl: string;\n\tmodels: SpProviderModelConfig[];\n}\n\nexport interface SpGatewayWarning {\n\tkind: \"refresh_failed\" | \"legacy_cache_imported\";\n\tmessage: string;\n\tat: string;\n}\n\ntype JsonRecord = Record<string, unknown>;\n\nlet gatewayWarning: SpGatewayWarning | undefined;\n\nfunction isRecord(value: unknown): value is JsonRecord {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction stringValue(value: unknown) {\n\treturn typeof value === \"string\" && value.trim() ? value.trim() : undefined;\n}\n\nfunction finiteNumber(value: unknown) {\n\treturn typeof value === \"number\" && Number.isFinite(value) ? value : undefined;\n}\n\nfunction positiveInteger(value: unknown) {\n\tconst number = finiteNumber(value);\n\treturn number !== undefined && number > 0 ? Math.floor(number) : undefined;\n}\n\nfunction nonNegativeNumber(value: unknown) {\n\tconst number = finiteNumber(value);\n\treturn number !== undefined && number >= 0 ? number : undefined;\n}\n\nfunction booleanValue(value: unknown) {\n\treturn typeof value === \"boolean\" ? value : undefined;\n}\n\nexport function getSpAgentDir() {\n\treturn process.env.SP_CODING_AGENT_DIR || join(homedir(), \".sp\", \"agent\");\n}\n\nexport function getSpGatewayStatePath(agentDir = getSpAgentDir()) {\n\treturn join(agentDir, \"sp-gateway.json\");\n}\n\nexport function getLegacyModelsPath(agentDir = getSpAgentDir()) {\n\treturn join(agentDir, \"models.json\");\n}\n\nexport function getSpAuthPath(agentDir = getSpAgentDir()) {\n\treturn join(agentDir, \"auth.json\");\n}\n\nexport function getSpSettingsPath(agentDir = getSpAgentDir()) {\n\treturn join(agentDir, \"settings.json\");\n}\n\nfunction readJsonFile(path: string): JsonRecord {\n\tif (!existsSync(path)) return {};\n\tconst content = readFileSync(path, \"utf8\").trim();\n\tif (!content) return {};\n\tconst parsed = JSON.parse(content) as unknown;\n\tif (!isRecord(parsed)) throw new Error(`Expected an object in ${path}`);\n\treturn parsed;\n}\n\nfunction writePrivateJson(path: string, value: unknown) {\n\tmkdirSync(dirname(path), { recursive: true });\n\tconst temporaryPath = `${path}.${process.pid}.tmp`;\n\twriteFileSync(temporaryPath, `${JSON.stringify(value, null, \"\\t\")}\\n`, { encoding: \"utf8\", mode: 0o600 });\n\tif (process.platform !== \"win32\") chmodSync(temporaryPath, 0o600);\n\trenameSync(temporaryPath, path);\n\tif (process.platform !== \"win32\") chmodSync(path, 0o600);\n}\n\nexport function normalizeSpGatewayBaseUrl(input: string) {\n\tconst trimmed = input.trim();\n\tif (!trimmed) throw new Error(\"Gateway URL cannot be empty.\");\n\tconst withScheme = /^[a-z][a-z0-9+.-]*:\\/\\//iu.test(trimmed) ? trimmed : `https://${trimmed}`;\n\tlet url: URL;\n\ttry {\n\t\turl = new URL(withScheme);\n\t} catch {\n\t\tthrow new Error(`Invalid gateway URL: ${input}`);\n\t}\n\tif (url.protocol !== \"https:\" && url.protocol !== \"http:\") {\n\t\tthrow new Error(\"Gateway URL must use HTTPS or HTTP.\");\n\t}\n\turl.hash = \"\";\n\turl.search = \"\";\n\turl.pathname = url.pathname.replace(/\\/+$/u, \"\");\n\tif (!url.pathname.endsWith(\"/v1\")) url.pathname = `${url.pathname}/v1`.replace(/\\/+/gu, \"/\");\n\treturn url.toString().replace(/\\/$/u, \"\");\n}\n\nfunction parseInput(value: unknown): (\"text\" | \"image\")[] | undefined {\n\tif (!Array.isArray(value)) return undefined;\n\tconst input = value.filter((entry): entry is \"text\" | \"image\" => entry === \"text\" || entry === \"image\");\n\treturn input.length > 0 ? [...new Set(input)] : undefined;\n}\n\nfunction parseCost(value: unknown): SpProviderModelConfig[\"cost\"] | undefined {\n\tif (!isRecord(value)) return undefined;\n\tconst input = nonNegativeNumber(value.input);\n\tconst output = nonNegativeNumber(value.output);\n\tconst cacheRead = nonNegativeNumber(value.cacheRead);\n\tconst cacheWrite = nonNegativeNumber(value.cacheWrite);\n\tif (input === undefined && output === undefined && cacheRead === undefined && cacheWrite === undefined)\n\t\treturn undefined;\n\treturn { input: input ?? 0, output: output ?? 0, cacheRead: cacheRead ?? 0, cacheWrite: cacheWrite ?? 0 };\n}\n\nfunction parseThinkingLevelMap(value: unknown): SpProviderModelConfig[\"thinkingLevelMap\"] {\n\tif (!isRecord(value)) return undefined;\n\tconst result: NonNullable<SpProviderModelConfig[\"thinkingLevelMap\"]> = {};\n\tfor (const key of [\"off\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\"] as const) {\n\t\tconst entry = value[key];\n\t\tif (typeof entry === \"string\" || entry === null) result[key] = entry;\n\t}\n\treturn Object.keys(result).length > 0 ? result : undefined;\n}\n\nfunction thinkingFormat(value: unknown): OpenAICompletionsCompat[\"thinkingFormat\"] {\n\tswitch (value) {\n\t\tcase \"openai\":\n\t\tcase \"openrouter\":\n\t\tcase \"deepseek\":\n\t\tcase \"together\":\n\t\tcase \"zai\":\n\t\tcase \"qwen\":\n\t\tcase \"chat-template\":\n\t\tcase \"qwen-chat-template\":\n\t\tcase \"string-thinking\":\n\t\tcase \"ant-ling\":\n\t\t\treturn value;\n\t\tdefault:\n\t\t\treturn undefined;\n\t}\n}\n\nfunction sessionAffinityFormat(value: unknown): OpenAICompletionsCompat[\"sessionAffinityFormat\"] {\n\treturn value === \"openai\" || value === \"openai-nosession\" || value === \"openrouter\" ? value : undefined;\n}\n\nfunction parseCompat(value: unknown): OpenAICompletionsCompat {\n\tconst record = isRecord(value) ? value : {};\n\tconst maxTokensField =\n\t\trecord.maxTokensField === \"max_completion_tokens\" || record.maxTokensField === \"max_tokens\"\n\t\t\t? record.maxTokensField\n\t\t\t: undefined;\n\tconst compat = {\n\t\tsupportsStore: booleanValue(record.supportsStore) ?? false,\n\t\tsupportsDeveloperRole: booleanValue(record.supportsDeveloperRole),\n\t\tsupportsReasoningEffort: booleanValue(record.supportsReasoningEffort),\n\t\tsupportsUsageInStreaming: booleanValue(record.supportsUsageInStreaming),\n\t\tmaxTokensField,\n\t\trequiresToolResultName: booleanValue(record.requiresToolResultName),\n\t\trequiresAssistantAfterToolResult: booleanValue(record.requiresAssistantAfterToolResult),\n\t\trequiresThinkingAsText: booleanValue(record.requiresThinkingAsText),\n\t\trequiresReasoningContentOnAssistantMessages: booleanValue(record.requiresReasoningContentOnAssistantMessages),\n\t\tthinkingFormat: thinkingFormat(record.thinkingFormat),\n\t\tsupportsStrictMode: booleanValue(record.supportsStrictMode),\n\t\tsendSessionAffinityHeaders: booleanValue(record.sendSessionAffinityHeaders),\n\t\tsessionAffinityFormat: sessionAffinityFormat(record.sessionAffinityFormat),\n\t\tsupportsLongCacheRetention: booleanValue(record.supportsLongCacheRetention),\n\t} satisfies OpenAICompletionsCompat;\n\treturn compat;\n}\n\nfunction parseGatewayModel(value: unknown): SpProviderModelConfig | undefined {\n\tconst record = typeof value === \"string\" ? { id: value } : isRecord(value) ? value : undefined;\n\tif (!record) return undefined;\n\tconst id = stringValue(record.id);\n\tif (!id || id.length > 256 || /[\\u0000-\\u001f\\u007f]/u.test(id)) return undefined;\n\tconst metadata = isRecord(record.metadata) ? record.metadata : {};\n\tconst field = (name: string) => metadata[name] ?? record[name];\n\tconst reasoning = field(\"reasoning\");\n\treturn {\n\t\tid,\n\t\tname: stringValue(field(\"name\")) ?? id,\n\t\treasoning: typeof reasoning === \"boolean\" ? reasoning : false,\n\t\tthinkingLevelMap: parseThinkingLevelMap(field(\"thinkingLevelMap\")),\n\t\tinput: parseInput(field(\"input\")) ?? [\"text\"],\n\t\tcost: parseCost(field(\"cost\")) ?? { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n\t\tcontextWindow: positiveInteger(field(\"contextWindow\")) ?? 128_000,\n\t\tmaxTokens: positiveInteger(field(\"maxTokens\")) ?? 32_000,\n\t\tcompat: parseCompat(field(\"compat\")),\n\t};\n}\n\nfunction parseModelCatalog(values: readonly unknown[], source: string) {\n\tconst models = values.map(parseGatewayModel).filter((model): model is SpProviderModelConfig => model !== undefined);\n\tif (models.length === 0) throw new Error(`${source} contains no valid models.`);\n\tconst ids = new Set<string>();\n\tfor (const model of models) {\n\t\tif (ids.has(model.id)) throw new Error(`${source} contains duplicate model id ${model.id}.`);\n\t\tids.add(model.id);\n\t}\n\treturn models;\n}\n\nexport async function fetchSpGatewayModels(baseUrl: string, apiKey: string, signal?: AbortSignal) {\n\tconst response = await fetch(`${baseUrl.replace(/\\/$/u, \"\")}/models`, {\n\t\tsignal,\n\t\theaders: {\n\t\t\tAuthorization: `Bearer ${apiKey}`,\n\t\t\tAccept: \"application/json\",\n\t\t\t\"User-Agent\": SP_USER_AGENT,\n\t\t\t\"X-Senpiper-Client\": SP_CLIENT_NAME,\n\t\t},\n\t});\n\tif (!response.ok) {\n\t\tconst body = await response.text().catch(() => \"\");\n\t\tconst suffix = body.trim() ? `: ${body.trim().slice(0, 300)}` : \"\";\n\t\tthrow new Error(`Senpiper Gateway rejected /v1/models (${response.status} ${response.statusText})${suffix}`);\n\t}\n\tconst payload = (await response.json()) as unknown;\n\tif (!isRecord(payload) || !Array.isArray(payload.data)) {\n\t\tthrow new Error(\"Senpiper Gateway returned an invalid /v1/models envelope.\");\n\t}\n\treturn parseModelCatalog(payload.data, \"Senpiper Gateway /v1/models response\");\n}\n\nfunction readLegacySpGatewayState(agentDir: string): SpGatewayState | undefined {\n\tconst path = getLegacyModelsPath(agentDir);\n\tif (!existsSync(path)) return undefined;\n\tconst root = readJsonFile(path);\n\tconst providers = isRecord(root.providers) ? root.providers : undefined;\n\tconst providerValue = providers?.[SP_GATEWAY_PROVIDER_ID] ?? providers?.[LEGACY_SP_GATEWAY_PROVIDER_ID];\n\tconst provider = isRecord(providerValue) ? providerValue : undefined;\n\tif (!provider || typeof provider.baseUrl !== \"string\" || !Array.isArray(provider.models)) return undefined;\n\treturn {\n\t\tschemaVersion: 1,\n\t\tbaseUrl: normalizeSpGatewayBaseUrl(provider.baseUrl),\n\t\tmodels: parseModelCatalog(provider.models, `legacy Senpiper Gateway cache ${path}`),\n\t};\n}\n\nexport function readSpGatewayState(agentDir = getSpAgentDir()): SpGatewayState | undefined {\n\tconst path = getSpGatewayStatePath(agentDir);\n\tif (!existsSync(path)) {\n\t\tconst legacy = readLegacySpGatewayState(agentDir);\n\t\tif (!legacy) return undefined;\n\t\twriteSpGatewayState(legacy.baseUrl, legacy.models, agentDir);\n\t\tgatewayWarning = {\n\t\t\tkind: \"legacy_cache_imported\",\n\t\t\tmessage: `Imported the legacy Senpiper Gateway model cache from ${getLegacyModelsPath(agentDir)}.`,\n\t\t\tat: new Date().toISOString(),\n\t\t};\n\t\treturn legacy;\n\t}\n\tconst value = readJsonFile(path);\n\tif (value.schemaVersion !== 1 || typeof value.baseUrl !== \"string\" || !Array.isArray(value.models)) {\n\t\tthrow new Error(`Invalid Senpiper Gateway configuration: ${path}`);\n\t}\n\treturn {\n\t\tschemaVersion: 1,\n\t\tbaseUrl: normalizeSpGatewayBaseUrl(value.baseUrl),\n\t\tmodels: parseModelCatalog(value.models, `Senpiper Gateway configuration ${path}`),\n\t};\n}\n\nexport function writeSpGatewayState(baseUrl: string, models: SpProviderModelConfig[], agentDir = getSpAgentDir()) {\n\tconst validatedModels = parseModelCatalog(models, \"Senpiper Gateway state\");\n\twritePrivateJson(getSpGatewayStatePath(agentDir), {\n\t\tschemaVersion: 1,\n\t\tbaseUrl: normalizeSpGatewayBaseUrl(baseUrl),\n\t\tmodels: validatedModels,\n\t} satisfies SpGatewayState);\n}\n\nexport function readSpGatewayApiKey(agentDir = getSpAgentDir()) {\n\tconst path = getSpAuthPath(agentDir);\n\tconst auth = readJsonFile(path);\n\tlet credential = auth[SP_GATEWAY_PROVIDER_ID];\n\tif (!credential && auth[LEGACY_SP_GATEWAY_PROVIDER_ID]) {\n\t\tcredential = auth[LEGACY_SP_GATEWAY_PROVIDER_ID];\n\t\tauth[SP_GATEWAY_PROVIDER_ID] = credential;\n\t\tdelete auth[LEGACY_SP_GATEWAY_PROVIDER_ID];\n\t\twritePrivateJson(path, auth);\n\t}\n\treturn isRecord(credential) && credential.type === \"api_key\" && typeof credential.key === \"string\"\n\t\t? credential.key\n\t\t: undefined;\n}\n\nexport function writeSpGatewayLogin(apiKey: string, firstModelId: string, agentDir = getSpAgentDir()) {\n\tconst authPath = getSpAuthPath(agentDir);\n\tconst auth = readJsonFile(authPath);\n\tauth[SP_GATEWAY_PROVIDER_ID] = { type: \"api_key\", key: apiKey };\n\tdelete auth[LEGACY_SP_GATEWAY_PROVIDER_ID];\n\twritePrivateJson(authPath, auth);\n\n\tconst settingsPath = getSpSettingsPath(agentDir);\n\tconst settings = readJsonFile(settingsPath);\n\tsettings.defaultProvider = SP_GATEWAY_PROVIDER_ID;\n\tsettings.defaultModel = firstModelId;\n\twritePrivateJson(settingsPath, settings);\n}\n\nexport function removeSpGatewayLogin(agentDir = getSpAgentDir()) {\n\tconst path = getSpAuthPath(agentDir);\n\tconst auth = readJsonFile(path);\n\tdelete auth[SP_GATEWAY_PROVIDER_ID];\n\tdelete auth[LEGACY_SP_GATEWAY_PROVIDER_ID];\n\twritePrivateJson(path, auth);\n}\n\nfunction credentialKey(context: RefreshModelsContext) {\n\treturn context.credential?.type === \"api_key\" ? context.credential.key : undefined;\n}\n\nfunction storedModels(models: readonly SpProviderModelConfig[], baseUrl: string): Model<Api>[] {\n\treturn models.map((model) => ({\n\t\t...model,\n\t\tprovider: SP_GATEWAY_PROVIDER_ID,\n\t\tbaseUrl,\n\t\tapi: \"openai-completions\",\n\t}));\n}\n\nexport function getSpGatewayWarning() {\n\treturn gatewayWarning;\n}\n\nexport function clearSpGatewayWarning() {\n\tgatewayWarning = undefined;\n}\n\nexport function createSpGatewayProviderConfig(\n\tbaseUrl: string,\n\tmodels: SpProviderModelConfig[],\n\tagentDir = getSpAgentDir(),\n): ProviderConfig {\n\tlet currentModels = parseModelCatalog(models, \"Senpiper Gateway provider configuration\");\n\treturn {\n\t\tname: SP_GATEWAY_PROVIDER_NAME,\n\t\tbaseUrl,\n\t\tapi: \"openai-completions\",\n\t\tauthHeader: true,\n\t\theaders: {\n\t\t\t\"User-Agent\": SP_USER_AGENT,\n\t\t\t\"X-Senpiper-Client\": SP_CLIENT_NAME,\n\t\t},\n\t\tmodels: currentModels,\n\t\tasync refreshModels(context) {\n\t\t\tif (!context.allowNetwork) return currentModels;\n\t\t\tconst apiKey = credentialKey(context);\n\t\t\tif (!apiKey) return currentModels;\n\t\t\ttry {\n\t\t\t\tconst refreshed = await fetchSpGatewayModels(baseUrl, apiKey, context.signal);\n\t\t\t\tif (context.signal.aborted) return currentModels;\n\t\t\t\tawait context.publish({\n\t\t\t\t\tpersist: { models: storedModels(refreshed, baseUrl), checkedAt: Date.now() },\n\t\t\t\t\tupdate: () => {\n\t\t\t\t\t\twriteSpGatewayState(baseUrl, refreshed, agentDir);\n\t\t\t\t\t\tcurrentModels = refreshed;\n\t\t\t\t\t\tgatewayWarning = undefined;\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\treturn currentModels;\n\t\t\t} catch (error) {\n\t\t\t\tgatewayWarning = {\n\t\t\t\t\tkind: \"refresh_failed\",\n\t\t\t\t\tmessage: `Senpiper Gateway model refresh failed; using ${currentModels.length} cached model${currentModels.length === 1 ? \"\" : \"s\"}: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\t\tat: new Date().toISOString(),\n\t\t\t\t};\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t},\n\t};\n}\n\nexport function createSpGatewayExtension(): ExtensionFactory {\n\treturn (pi) => {\n\t\tconst state = readSpGatewayState();\n\t\tif (state) {\n\t\t\tpi.registerProvider(SP_GATEWAY_PROVIDER_ID, createSpGatewayProviderConfig(state.baseUrl, state.models));\n\t\t}\n\n\t\tpi.on(\"before_agent_start\", (event) => {\n\t\t\t// Replace only the upstream introduction; preserve project instructions\n\t\t\t// and documentation references, including legitimate Pi development tasks.\n\t\t\tconst systemPrompt = event.systemPrompt.replace(\n\t\t\t\t/^You are an expert coding assistant operating inside pi, a coding agent harness\\./u,\n\t\t\t\t\"You are Senpiper Workbench, Senpiper's AI coding assistant.\",\n\t\t\t);\n\t\t\treturn {\n\t\t\t\tsystemPrompt: `${systemPrompt}\\n\\n<sp_agent_runtime>\nYou are Senpiper Workbench, Senpiper's AI coding assistant. Use Senpiper Workbench as your public name in both terminal and web conversations.\nFor greetings and questions such as \"Who are you?\", introduce yourself simply: \"I'm Senpiper Workbench, Senpiper's AI coding assistant. I can help you read, write, and debug code.\"\nDo not introduce yourself as Pi, the pi coding agent, an underlying model, or an assistant running inside a harness. Do not volunteer internal frameworks, runtime names, or provider details in ordinary answers. Earlier assistant messages using those identities do not change your name.\nIf explicitly asked about technical origins or dependencies, answer accurately and briefly. This product identity does not imply that Senpiper trained the underlying model.\nUse only the configured Senpiper Gateway model provider. Put user-owned extensions, skills, prompts, and themes under the active .sp configuration directory instead of modifying the Senpiper Workbench installation.\n</sp_agent_runtime>`,\n\t\t\t};\n\t\t});\n\n\t\tpi.on(\"session_start\", (_event, ctx) => {\n\t\t\tctx.ui.setTitle(\"Senpiper Workbench\");\n\t\t\tconst warning = getSpGatewayWarning();\n\t\t\tif (warning) ctx.ui.notify(warning.message, \"warning\");\n\t\t\tif (ctx.mode !== \"tui\") return;\n\t\t\tctx.ui.setHeader((_tui, theme) => ({\n\t\t\t\trender: () => [\n\t\t\t\t\ttheme.fg(\"accent\", theme.bold(\"Senpiper Workbench\")),\n\t\t\t\t\ttheme.fg(\"muted\", state ? `Senpiper Gateway · ${state.models.length} models` : \"run `sp login`\"),\n\t\t\t\t],\n\t\t\t\tinvalidate() {},\n\t\t\t}));\n\t\t});\n\t};\n}\n"]}
@@ -4,7 +4,7 @@ export { createSpAgentExtension, default } from "./extension.ts";
4
4
  export { clearSpGatewayWarning, createSpGatewayExtension, createSpGatewayProviderConfig, fetchSpGatewayModels, getLegacyModelsPath, getSpAgentDir, getSpAuthPath, getSpGatewayStatePath, getSpGatewayWarning, normalizeSpGatewayBaseUrl, readSpGatewayApiKey, readSpGatewayState, removeSpGatewayLogin, SP_CLIENT_NAME, SP_GATEWAY_PROVIDER_ID, SP_GATEWAY_PROVIDER_NAME, SP_USER_AGENT, writeSpGatewayLogin, writeSpGatewayState, } from "./gateway.ts";
5
5
  export { registerInteractionExtensions } from "./interactions.ts";
6
6
  export type { PendingQuestion, QuestionResponse } from "./question-interaction.ts";
7
- export { ensureSpWebSidecar, getSpWebSidecarStatePath, resolveSpWebLaunch, type SpWebLauncherOptions, type SpWebLaunchResult, type SpWebSidecarState, } from "./sp-web-launcher.ts";
7
+ export { ensureSpWebSidecar, getSpWebSidecarStatePath, resolveSpWebLaunch, type SpWebLauncherOptions, type SpWebLaunchResult, type SpWebSidecarState, type SpWebStopResult, stopSpWebSidecar, } from "./sp-web-launcher.ts";
8
8
  export { filterSpStartupMessage, registerSpStartupHeader, renderSpStartupHeader } from "./startup-header.ts";
9
9
  export { DEFAULT_TERMINAL_BRIDGE_MAX_BUFFERED_BYTES, DEFAULT_TERMINAL_BRIDGE_MAX_CLIENTS, DEFAULT_TERMINAL_BRIDGE_MAX_FRAME_BYTES, readTerminalBridgeDescriptor, registerTerminalWebBridge, TERMINAL_BRIDGE_PROTOCOL, TERMINAL_BRIDGE_VERSION, type TerminalBridgeDescriptor, TerminalWebBridge, type TerminalWebBridgeOptions, terminalBridgeDescriptorExists, } from "./terminal-web-bridge.ts";
10
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,aAAa,EACb,4BAA4B,EAC5B,mCAAmC,EACnC,uBAAuB,EACvB,+BAA+B,GAC/B,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,sBAAsB,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACjE,OAAO,EACN,qBAAqB,EACrB,wBAAwB,EACxB,6BAA6B,EAC7B,oBAAoB,EACpB,mBAAmB,EACnB,aAAa,EACb,aAAa,EACb,qBAAqB,EACrB,mBAAmB,EACnB,yBAAyB,EACzB,mBAAmB,EACnB,kBAAkB,EAClB,oBAAoB,EACpB,cAAc,EACd,sBAAsB,EACtB,wBAAwB,EACxB,aAAa,EACb,mBAAmB,EACnB,mBAAmB,GACnB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,6BAA6B,EAAE,MAAM,mBAAmB,CAAC;AAClE,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AACnF,OAAO,EACN,kBAAkB,EAClB,wBAAwB,EACxB,kBAAkB,EAClB,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,GACtB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC7G,OAAO,EACN,0CAA0C,EAC1C,mCAAmC,EACnC,uCAAuC,EACvC,4BAA4B,EAC5B,yBAAyB,EACzB,wBAAwB,EACxB,uBAAuB,EACvB,KAAK,wBAAwB,EAC7B,iBAAiB,EACjB,KAAK,wBAAwB,EAC7B,8BAA8B,GAC9B,MAAM,0BAA0B,CAAC","sourcesContent":["export {\n\tAskUserSchema,\n\tbuildAskUserQuestionResponse,\n\tcreateAskUserQuestionToolDefinition,\n\tregisterAskUserQuestion,\n\tvalidateAskUserQuestionEnvelope,\n} from \"./ask-user.ts\";\nexport { handleSpCommand } from \"./commands.ts\";\nexport { createSpAgentExtension, default } from \"./extension.ts\";\nexport {\n\tclearSpGatewayWarning,\n\tcreateSpGatewayExtension,\n\tcreateSpGatewayProviderConfig,\n\tfetchSpGatewayModels,\n\tgetLegacyModelsPath,\n\tgetSpAgentDir,\n\tgetSpAuthPath,\n\tgetSpGatewayStatePath,\n\tgetSpGatewayWarning,\n\tnormalizeSpGatewayBaseUrl,\n\treadSpGatewayApiKey,\n\treadSpGatewayState,\n\tremoveSpGatewayLogin,\n\tSP_CLIENT_NAME,\n\tSP_GATEWAY_PROVIDER_ID,\n\tSP_GATEWAY_PROVIDER_NAME,\n\tSP_USER_AGENT,\n\twriteSpGatewayLogin,\n\twriteSpGatewayState,\n} from \"./gateway.ts\";\nexport { registerInteractionExtensions } from \"./interactions.ts\";\nexport type { PendingQuestion, QuestionResponse } from \"./question-interaction.ts\";\nexport {\n\tensureSpWebSidecar,\n\tgetSpWebSidecarStatePath,\n\tresolveSpWebLaunch,\n\ttype SpWebLauncherOptions,\n\ttype SpWebLaunchResult,\n\ttype SpWebSidecarState,\n} from \"./sp-web-launcher.ts\";\nexport { filterSpStartupMessage, registerSpStartupHeader, renderSpStartupHeader } from \"./startup-header.ts\";\nexport {\n\tDEFAULT_TERMINAL_BRIDGE_MAX_BUFFERED_BYTES,\n\tDEFAULT_TERMINAL_BRIDGE_MAX_CLIENTS,\n\tDEFAULT_TERMINAL_BRIDGE_MAX_FRAME_BYTES,\n\treadTerminalBridgeDescriptor,\n\tregisterTerminalWebBridge,\n\tTERMINAL_BRIDGE_PROTOCOL,\n\tTERMINAL_BRIDGE_VERSION,\n\ttype TerminalBridgeDescriptor,\n\tTerminalWebBridge,\n\ttype TerminalWebBridgeOptions,\n\tterminalBridgeDescriptorExists,\n} from \"./terminal-web-bridge.ts\";\n"]}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,aAAa,EACb,4BAA4B,EAC5B,mCAAmC,EACnC,uBAAuB,EACvB,+BAA+B,GAC/B,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,sBAAsB,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACjE,OAAO,EACN,qBAAqB,EACrB,wBAAwB,EACxB,6BAA6B,EAC7B,oBAAoB,EACpB,mBAAmB,EACnB,aAAa,EACb,aAAa,EACb,qBAAqB,EACrB,mBAAmB,EACnB,yBAAyB,EACzB,mBAAmB,EACnB,kBAAkB,EAClB,oBAAoB,EACpB,cAAc,EACd,sBAAsB,EACtB,wBAAwB,EACxB,aAAa,EACb,mBAAmB,EACnB,mBAAmB,GACnB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,6BAA6B,EAAE,MAAM,mBAAmB,CAAC;AAClE,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AACnF,OAAO,EACN,kBAAkB,EAClB,wBAAwB,EACxB,kBAAkB,EAClB,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,gBAAgB,GAChB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC7G,OAAO,EACN,0CAA0C,EAC1C,mCAAmC,EACnC,uCAAuC,EACvC,4BAA4B,EAC5B,yBAAyB,EACzB,wBAAwB,EACxB,uBAAuB,EACvB,KAAK,wBAAwB,EAC7B,iBAAiB,EACjB,KAAK,wBAAwB,EAC7B,8BAA8B,GAC9B,MAAM,0BAA0B,CAAC","sourcesContent":["export {\n\tAskUserSchema,\n\tbuildAskUserQuestionResponse,\n\tcreateAskUserQuestionToolDefinition,\n\tregisterAskUserQuestion,\n\tvalidateAskUserQuestionEnvelope,\n} from \"./ask-user.ts\";\nexport { handleSpCommand } from \"./commands.ts\";\nexport { createSpAgentExtension, default } from \"./extension.ts\";\nexport {\n\tclearSpGatewayWarning,\n\tcreateSpGatewayExtension,\n\tcreateSpGatewayProviderConfig,\n\tfetchSpGatewayModels,\n\tgetLegacyModelsPath,\n\tgetSpAgentDir,\n\tgetSpAuthPath,\n\tgetSpGatewayStatePath,\n\tgetSpGatewayWarning,\n\tnormalizeSpGatewayBaseUrl,\n\treadSpGatewayApiKey,\n\treadSpGatewayState,\n\tremoveSpGatewayLogin,\n\tSP_CLIENT_NAME,\n\tSP_GATEWAY_PROVIDER_ID,\n\tSP_GATEWAY_PROVIDER_NAME,\n\tSP_USER_AGENT,\n\twriteSpGatewayLogin,\n\twriteSpGatewayState,\n} from \"./gateway.ts\";\nexport { registerInteractionExtensions } from \"./interactions.ts\";\nexport type { PendingQuestion, QuestionResponse } from \"./question-interaction.ts\";\nexport {\n\tensureSpWebSidecar,\n\tgetSpWebSidecarStatePath,\n\tresolveSpWebLaunch,\n\ttype SpWebLauncherOptions,\n\ttype SpWebLaunchResult,\n\ttype SpWebSidecarState,\n\ttype SpWebStopResult,\n\tstopSpWebSidecar,\n} from \"./sp-web-launcher.ts\";\nexport { filterSpStartupMessage, registerSpStartupHeader, renderSpStartupHeader } from \"./startup-header.ts\";\nexport {\n\tDEFAULT_TERMINAL_BRIDGE_MAX_BUFFERED_BYTES,\n\tDEFAULT_TERMINAL_BRIDGE_MAX_CLIENTS,\n\tDEFAULT_TERMINAL_BRIDGE_MAX_FRAME_BYTES,\n\treadTerminalBridgeDescriptor,\n\tregisterTerminalWebBridge,\n\tTERMINAL_BRIDGE_PROTOCOL,\n\tTERMINAL_BRIDGE_VERSION,\n\ttype TerminalBridgeDescriptor,\n\tTerminalWebBridge,\n\ttype TerminalWebBridgeOptions,\n\tterminalBridgeDescriptorExists,\n} from \"./terminal-web-bridge.ts\";\n"]}
@@ -3,7 +3,7 @@ export { handleSpCommand } from "./commands.js";
3
3
  export { createSpAgentExtension, default } from "./extension.js";
4
4
  export { clearSpGatewayWarning, createSpGatewayExtension, createSpGatewayProviderConfig, fetchSpGatewayModels, getLegacyModelsPath, getSpAgentDir, getSpAuthPath, getSpGatewayStatePath, getSpGatewayWarning, normalizeSpGatewayBaseUrl, readSpGatewayApiKey, readSpGatewayState, removeSpGatewayLogin, SP_CLIENT_NAME, SP_GATEWAY_PROVIDER_ID, SP_GATEWAY_PROVIDER_NAME, SP_USER_AGENT, writeSpGatewayLogin, writeSpGatewayState, } from "./gateway.js";
5
5
  export { registerInteractionExtensions } from "./interactions.js";
6
- export { ensureSpWebSidecar, getSpWebSidecarStatePath, resolveSpWebLaunch, } from "./sp-web-launcher.js";
6
+ export { ensureSpWebSidecar, getSpWebSidecarStatePath, resolveSpWebLaunch, stopSpWebSidecar, } from "./sp-web-launcher.js";
7
7
  export { filterSpStartupMessage, registerSpStartupHeader, renderSpStartupHeader } from "./startup-header.js";
8
8
  export { DEFAULT_TERMINAL_BRIDGE_MAX_BUFFERED_BYTES, DEFAULT_TERMINAL_BRIDGE_MAX_CLIENTS, DEFAULT_TERMINAL_BRIDGE_MAX_FRAME_BYTES, readTerminalBridgeDescriptor, registerTerminalWebBridge, TERMINAL_BRIDGE_PROTOCOL, TERMINAL_BRIDGE_VERSION, TerminalWebBridge, terminalBridgeDescriptorExists, } from "./terminal-web-bridge.js";
9
9
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,aAAa,EACb,4BAA4B,EAC5B,mCAAmC,EACnC,uBAAuB,EACvB,+BAA+B,GAC/B,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,sBAAsB,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACjE,OAAO,EACN,qBAAqB,EACrB,wBAAwB,EACxB,6BAA6B,EAC7B,oBAAoB,EACpB,mBAAmB,EACnB,aAAa,EACb,aAAa,EACb,qBAAqB,EACrB,mBAAmB,EACnB,yBAAyB,EACzB,mBAAmB,EACnB,kBAAkB,EAClB,oBAAoB,EACpB,cAAc,EACd,sBAAsB,EACtB,wBAAwB,EACxB,aAAa,EACb,mBAAmB,EACnB,mBAAmB,GACnB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,6BAA6B,EAAE,MAAM,mBAAmB,CAAC;AAElE,OAAO,EACN,kBAAkB,EAClB,wBAAwB,EACxB,kBAAkB,GAIlB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC7G,OAAO,EACN,0CAA0C,EAC1C,mCAAmC,EACnC,uCAAuC,EACvC,4BAA4B,EAC5B,yBAAyB,EACzB,wBAAwB,EACxB,uBAAuB,EAEvB,iBAAiB,EAEjB,8BAA8B,GAC9B,MAAM,0BAA0B,CAAC","sourcesContent":["export {\n\tAskUserSchema,\n\tbuildAskUserQuestionResponse,\n\tcreateAskUserQuestionToolDefinition,\n\tregisterAskUserQuestion,\n\tvalidateAskUserQuestionEnvelope,\n} from \"./ask-user.ts\";\nexport { handleSpCommand } from \"./commands.ts\";\nexport { createSpAgentExtension, default } from \"./extension.ts\";\nexport {\n\tclearSpGatewayWarning,\n\tcreateSpGatewayExtension,\n\tcreateSpGatewayProviderConfig,\n\tfetchSpGatewayModels,\n\tgetLegacyModelsPath,\n\tgetSpAgentDir,\n\tgetSpAuthPath,\n\tgetSpGatewayStatePath,\n\tgetSpGatewayWarning,\n\tnormalizeSpGatewayBaseUrl,\n\treadSpGatewayApiKey,\n\treadSpGatewayState,\n\tremoveSpGatewayLogin,\n\tSP_CLIENT_NAME,\n\tSP_GATEWAY_PROVIDER_ID,\n\tSP_GATEWAY_PROVIDER_NAME,\n\tSP_USER_AGENT,\n\twriteSpGatewayLogin,\n\twriteSpGatewayState,\n} from \"./gateway.ts\";\nexport { registerInteractionExtensions } from \"./interactions.ts\";\nexport type { PendingQuestion, QuestionResponse } from \"./question-interaction.ts\";\nexport {\n\tensureSpWebSidecar,\n\tgetSpWebSidecarStatePath,\n\tresolveSpWebLaunch,\n\ttype SpWebLauncherOptions,\n\ttype SpWebLaunchResult,\n\ttype SpWebSidecarState,\n} from \"./sp-web-launcher.ts\";\nexport { filterSpStartupMessage, registerSpStartupHeader, renderSpStartupHeader } from \"./startup-header.ts\";\nexport {\n\tDEFAULT_TERMINAL_BRIDGE_MAX_BUFFERED_BYTES,\n\tDEFAULT_TERMINAL_BRIDGE_MAX_CLIENTS,\n\tDEFAULT_TERMINAL_BRIDGE_MAX_FRAME_BYTES,\n\treadTerminalBridgeDescriptor,\n\tregisterTerminalWebBridge,\n\tTERMINAL_BRIDGE_PROTOCOL,\n\tTERMINAL_BRIDGE_VERSION,\n\ttype TerminalBridgeDescriptor,\n\tTerminalWebBridge,\n\ttype TerminalWebBridgeOptions,\n\tterminalBridgeDescriptorExists,\n} from \"./terminal-web-bridge.ts\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,aAAa,EACb,4BAA4B,EAC5B,mCAAmC,EACnC,uBAAuB,EACvB,+BAA+B,GAC/B,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,sBAAsB,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACjE,OAAO,EACN,qBAAqB,EACrB,wBAAwB,EACxB,6BAA6B,EAC7B,oBAAoB,EACpB,mBAAmB,EACnB,aAAa,EACb,aAAa,EACb,qBAAqB,EACrB,mBAAmB,EACnB,yBAAyB,EACzB,mBAAmB,EACnB,kBAAkB,EAClB,oBAAoB,EACpB,cAAc,EACd,sBAAsB,EACtB,wBAAwB,EACxB,aAAa,EACb,mBAAmB,EACnB,mBAAmB,GACnB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,6BAA6B,EAAE,MAAM,mBAAmB,CAAC;AAElE,OAAO,EACN,kBAAkB,EAClB,wBAAwB,EACxB,kBAAkB,EAKlB,gBAAgB,GAChB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC7G,OAAO,EACN,0CAA0C,EAC1C,mCAAmC,EACnC,uCAAuC,EACvC,4BAA4B,EAC5B,yBAAyB,EACzB,wBAAwB,EACxB,uBAAuB,EAEvB,iBAAiB,EAEjB,8BAA8B,GAC9B,MAAM,0BAA0B,CAAC","sourcesContent":["export {\n\tAskUserSchema,\n\tbuildAskUserQuestionResponse,\n\tcreateAskUserQuestionToolDefinition,\n\tregisterAskUserQuestion,\n\tvalidateAskUserQuestionEnvelope,\n} from \"./ask-user.ts\";\nexport { handleSpCommand } from \"./commands.ts\";\nexport { createSpAgentExtension, default } from \"./extension.ts\";\nexport {\n\tclearSpGatewayWarning,\n\tcreateSpGatewayExtension,\n\tcreateSpGatewayProviderConfig,\n\tfetchSpGatewayModels,\n\tgetLegacyModelsPath,\n\tgetSpAgentDir,\n\tgetSpAuthPath,\n\tgetSpGatewayStatePath,\n\tgetSpGatewayWarning,\n\tnormalizeSpGatewayBaseUrl,\n\treadSpGatewayApiKey,\n\treadSpGatewayState,\n\tremoveSpGatewayLogin,\n\tSP_CLIENT_NAME,\n\tSP_GATEWAY_PROVIDER_ID,\n\tSP_GATEWAY_PROVIDER_NAME,\n\tSP_USER_AGENT,\n\twriteSpGatewayLogin,\n\twriteSpGatewayState,\n} from \"./gateway.ts\";\nexport { registerInteractionExtensions } from \"./interactions.ts\";\nexport type { PendingQuestion, QuestionResponse } from \"./question-interaction.ts\";\nexport {\n\tensureSpWebSidecar,\n\tgetSpWebSidecarStatePath,\n\tresolveSpWebLaunch,\n\ttype SpWebLauncherOptions,\n\ttype SpWebLaunchResult,\n\ttype SpWebSidecarState,\n\ttype SpWebStopResult,\n\tstopSpWebSidecar,\n} from \"./sp-web-launcher.ts\";\nexport { filterSpStartupMessage, registerSpStartupHeader, renderSpStartupHeader } from \"./startup-header.ts\";\nexport {\n\tDEFAULT_TERMINAL_BRIDGE_MAX_BUFFERED_BYTES,\n\tDEFAULT_TERMINAL_BRIDGE_MAX_CLIENTS,\n\tDEFAULT_TERMINAL_BRIDGE_MAX_FRAME_BYTES,\n\treadTerminalBridgeDescriptor,\n\tregisterTerminalWebBridge,\n\tTERMINAL_BRIDGE_PROTOCOL,\n\tTERMINAL_BRIDGE_VERSION,\n\ttype TerminalBridgeDescriptor,\n\tTerminalWebBridge,\n\ttype TerminalWebBridgeOptions,\n\tterminalBridgeDescriptorExists,\n} from \"./terminal-web-bridge.ts\";\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"login-command.d.ts","sourceRoot":"","sources":["../src/login-command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAWvF,wBAAsB,aAAa,CAAC,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,uBAAuB,oBA+CnG;AAED,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,YAAY,QAYpD","sourcesContent":["import type { ExtensionAPI, ExtensionCommandContext } from \"@senpiper/pi-coding-agent\";\nimport {\n\tcreateSpGatewayProviderConfig,\n\tfetchSpGatewayModels,\n\tnormalizeSpGatewayBaseUrl,\n\treadSpGatewayState,\n\tSP_GATEWAY_PROVIDER_ID,\n\twriteSpGatewayLogin,\n\twriteSpGatewayState,\n} from \"./gateway.ts\";\n\nexport async function runSlashLogin(pi: ExtensionAPI, args: string, context: ExtensionCommandContext) {\n\tconst existingState = readSpGatewayState();\n\tconst argumentUrl = args.trim();\n\tconst enteredUrl = argumentUrl\n\t\t? argumentUrl\n\t\t: await context.ui.input(\"sp-gateway URL\", existingState?.baseUrl ?? \"https://gateway.example.com/v1\");\n\tif (enteredUrl === undefined) {\n\t\tcontext.ui.notify(\"sp-gateway login cancelled.\", \"warning\");\n\t\treturn false;\n\t}\n\tconst gatewayUrl = enteredUrl.trim() || existingState?.baseUrl;\n\tif (!gatewayUrl) {\n\t\tcontext.ui.notify(\"A sp-gateway URL is required.\", \"error\");\n\t\treturn false;\n\t}\n\tconst apiKey = await context.ui.input(\"sp-gateway API key\", \"Paste your API key\", { secret: true });\n\tif (!apiKey?.trim()) {\n\t\tcontext.ui.notify(apiKey === undefined ? \"sp-gateway login cancelled.\" : \"An API key is required.\", \"warning\");\n\t\treturn false;\n\t}\n\n\tcontext.ui.setProgress(\"Connecting to sp-gateway and synchronizing models…\");\n\ttry {\n\t\tconst baseUrl = normalizeSpGatewayBaseUrl(gatewayUrl);\n\t\tconst models = await fetchSpGatewayModels(baseUrl, apiKey.trim());\n\t\twriteSpGatewayState(baseUrl, models);\n\t\twriteSpGatewayLogin(apiKey.trim(), models[0]!.id);\n\t\tpi.registerProvider(SP_GATEWAY_PROVIDER_ID, createSpGatewayProviderConfig(baseUrl, models));\n\t\tawait context.modelRegistry.setRuntimeApiKey(SP_GATEWAY_PROVIDER_ID, apiKey.trim());\n\t\tconst firstModel = models[0]!;\n\t\tconst selected = await pi.setModel({\n\t\t\t...firstModel,\n\t\t\tprovider: SP_GATEWAY_PROVIDER_ID,\n\t\t\tbaseUrl,\n\t\t\tapi: \"openai-completions\",\n\t\t});\n\t\tcontext.ui.notify(\n\t\t\tselected\n\t\t\t\t? `Connected to sp-gateway · ${models.length} models · selected ${firstModel.id}.`\n\t\t\t\t: `Connected to sp-gateway · ${models.length} models available. Use /model to select one.`,\n\t\t\t\"info\",\n\t\t);\n\t} finally {\n\t\tcontext.ui.setProgress(undefined);\n\t}\n\n\treturn true;\n}\n\nexport function registerLoginCommand(pi: ExtensionAPI) {\n\tpi.registerCommand(\"login\", {\n\t\tdescription: \"Log in to SP Gateway\",\n\t\thandler: async (args, context) => {\n\t\t\ttry {\n\t\t\t\tawait runSlashLogin(pi, args, context);\n\t\t\t} catch (error) {\n\t\t\t\tcontext.ui.setProgress(undefined);\n\t\t\t\tcontext.ui.notify(error instanceof Error ? error.message : String(error), \"error\");\n\t\t\t}\n\t\t},\n\t});\n}\n"]}
1
+ {"version":3,"file":"login-command.d.ts","sourceRoot":"","sources":["../src/login-command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAWvF,wBAAsB,aAAa,CAAC,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,uBAAuB,oBAkDnG;AAED,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,YAAY,QAYpD","sourcesContent":["import type { ExtensionAPI, ExtensionCommandContext } from \"@senpiper/pi-coding-agent\";\nimport {\n\tcreateSpGatewayProviderConfig,\n\tfetchSpGatewayModels,\n\tnormalizeSpGatewayBaseUrl,\n\treadSpGatewayState,\n\tSP_GATEWAY_PROVIDER_ID,\n\twriteSpGatewayLogin,\n\twriteSpGatewayState,\n} from \"./gateway.ts\";\n\nexport async function runSlashLogin(pi: ExtensionAPI, args: string, context: ExtensionCommandContext) {\n\tconst existingState = readSpGatewayState();\n\tconst argumentUrl = args.trim();\n\tconst enteredUrl = argumentUrl\n\t\t? argumentUrl\n\t\t: await context.ui.input(\"Senpiper Gateway URL\", existingState?.baseUrl ?? \"https://gateway.example.com/v1\");\n\tif (enteredUrl === undefined) {\n\t\tcontext.ui.notify(\"Senpiper Gateway login cancelled.\", \"warning\");\n\t\treturn false;\n\t}\n\tconst gatewayUrl = enteredUrl.trim() || existingState?.baseUrl;\n\tif (!gatewayUrl) {\n\t\tcontext.ui.notify(\"A Senpiper Gateway URL is required.\", \"error\");\n\t\treturn false;\n\t}\n\tconst apiKey = await context.ui.input(\"Senpiper Gateway API key\", \"Paste your API key\", { secret: true });\n\tif (!apiKey?.trim()) {\n\t\tcontext.ui.notify(\n\t\t\tapiKey === undefined ? \"Senpiper Gateway login cancelled.\" : \"An API key is required.\",\n\t\t\t\"warning\",\n\t\t);\n\t\treturn false;\n\t}\n\n\tcontext.ui.setProgress(\"Connecting to Senpiper Gateway and synchronizing models…\");\n\ttry {\n\t\tconst baseUrl = normalizeSpGatewayBaseUrl(gatewayUrl);\n\t\tconst models = await fetchSpGatewayModels(baseUrl, apiKey.trim());\n\t\twriteSpGatewayState(baseUrl, models);\n\t\twriteSpGatewayLogin(apiKey.trim(), models[0]!.id);\n\t\tpi.registerProvider(SP_GATEWAY_PROVIDER_ID, createSpGatewayProviderConfig(baseUrl, models));\n\t\tawait context.modelRegistry.setRuntimeApiKey(SP_GATEWAY_PROVIDER_ID, apiKey.trim());\n\t\tconst firstModel = models[0]!;\n\t\tconst selected = await pi.setModel({\n\t\t\t...firstModel,\n\t\t\tprovider: SP_GATEWAY_PROVIDER_ID,\n\t\t\tbaseUrl,\n\t\t\tapi: \"openai-completions\",\n\t\t});\n\t\tcontext.ui.notify(\n\t\t\tselected\n\t\t\t\t? `Connected to Senpiper Gateway · ${models.length} models · selected ${firstModel.id}.`\n\t\t\t\t: `Connected to Senpiper Gateway · ${models.length} models available. Use /model to select one.`,\n\t\t\t\"info\",\n\t\t);\n\t} finally {\n\t\tcontext.ui.setProgress(undefined);\n\t}\n\n\treturn true;\n}\n\nexport function registerLoginCommand(pi: ExtensionAPI) {\n\tpi.registerCommand(\"login\", {\n\t\tdescription: \"Log in to Senpiper Gateway\",\n\t\thandler: async (args, context) => {\n\t\t\ttry {\n\t\t\t\tawait runSlashLogin(pi, args, context);\n\t\t\t} catch (error) {\n\t\t\t\tcontext.ui.setProgress(undefined);\n\t\t\t\tcontext.ui.notify(error instanceof Error ? error.message : String(error), \"error\");\n\t\t\t}\n\t\t},\n\t});\n}\n"]}
@@ -4,22 +4,22 @@ export async function runSlashLogin(pi, args, context) {
4
4
  const argumentUrl = args.trim();
5
5
  const enteredUrl = argumentUrl
6
6
  ? argumentUrl
7
- : await context.ui.input("sp-gateway URL", existingState?.baseUrl ?? "https://gateway.example.com/v1");
7
+ : await context.ui.input("Senpiper Gateway URL", existingState?.baseUrl ?? "https://gateway.example.com/v1");
8
8
  if (enteredUrl === undefined) {
9
- context.ui.notify("sp-gateway login cancelled.", "warning");
9
+ context.ui.notify("Senpiper Gateway login cancelled.", "warning");
10
10
  return false;
11
11
  }
12
12
  const gatewayUrl = enteredUrl.trim() || existingState?.baseUrl;
13
13
  if (!gatewayUrl) {
14
- context.ui.notify("A sp-gateway URL is required.", "error");
14
+ context.ui.notify("A Senpiper Gateway URL is required.", "error");
15
15
  return false;
16
16
  }
17
- const apiKey = await context.ui.input("sp-gateway API key", "Paste your API key", { secret: true });
17
+ const apiKey = await context.ui.input("Senpiper Gateway API key", "Paste your API key", { secret: true });
18
18
  if (!apiKey?.trim()) {
19
- context.ui.notify(apiKey === undefined ? "sp-gateway login cancelled." : "An API key is required.", "warning");
19
+ context.ui.notify(apiKey === undefined ? "Senpiper Gateway login cancelled." : "An API key is required.", "warning");
20
20
  return false;
21
21
  }
22
- context.ui.setProgress("Connecting to sp-gateway and synchronizing models…");
22
+ context.ui.setProgress("Connecting to Senpiper Gateway and synchronizing models…");
23
23
  try {
24
24
  const baseUrl = normalizeSpGatewayBaseUrl(gatewayUrl);
25
25
  const models = await fetchSpGatewayModels(baseUrl, apiKey.trim());
@@ -35,8 +35,8 @@ export async function runSlashLogin(pi, args, context) {
35
35
  api: "openai-completions",
36
36
  });
37
37
  context.ui.notify(selected
38
- ? `Connected to sp-gateway · ${models.length} models · selected ${firstModel.id}.`
39
- : `Connected to sp-gateway · ${models.length} models available. Use /model to select one.`, "info");
38
+ ? `Connected to Senpiper Gateway · ${models.length} models · selected ${firstModel.id}.`
39
+ : `Connected to Senpiper Gateway · ${models.length} models available. Use /model to select one.`, "info");
40
40
  }
41
41
  finally {
42
42
  context.ui.setProgress(undefined);
@@ -45,7 +45,7 @@ export async function runSlashLogin(pi, args, context) {
45
45
  }
46
46
  export function registerLoginCommand(pi) {
47
47
  pi.registerCommand("login", {
48
- description: "Log in to SP Gateway",
48
+ description: "Log in to Senpiper Gateway",
49
49
  handler: async (args, context) => {
50
50
  try {
51
51
  await runSlashLogin(pi, args, context);
@@ -1 +1 @@
1
- {"version":3,"file":"login-command.js","sourceRoot":"","sources":["../src/login-command.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ExtensionAPI, ExtensionCommandContext } from \"@senpiper/pi-coding-agent\";\nimport {\n\tcreateSpGatewayProviderConfig,\n\tfetchSpGatewayModels,\n\tnormalizeSpGatewayBaseUrl,\n\treadSpGatewayState,\n\tSP_GATEWAY_PROVIDER_ID,\n\twriteSpGatewayLogin,\n\twriteSpGatewayState,\n} from \"./gateway.ts\";\n\nexport async function runSlashLogin(pi: ExtensionAPI, args: string, context: ExtensionCommandContext) {\n\tconst existingState = readSpGatewayState();\n\tconst argumentUrl = args.trim();\n\tconst enteredUrl = argumentUrl\n\t\t? argumentUrl\n\t\t: await context.ui.input(\"sp-gateway URL\", existingState?.baseUrl ?? \"https://gateway.example.com/v1\");\n\tif (enteredUrl === undefined) {\n\t\tcontext.ui.notify(\"sp-gateway login cancelled.\", \"warning\");\n\t\treturn false;\n\t}\n\tconst gatewayUrl = enteredUrl.trim() || existingState?.baseUrl;\n\tif (!gatewayUrl) {\n\t\tcontext.ui.notify(\"A sp-gateway URL is required.\", \"error\");\n\t\treturn false;\n\t}\n\tconst apiKey = await context.ui.input(\"sp-gateway API key\", \"Paste your API key\", { secret: true });\n\tif (!apiKey?.trim()) {\n\t\tcontext.ui.notify(apiKey === undefined ? \"sp-gateway login cancelled.\" : \"An API key is required.\", \"warning\");\n\t\treturn false;\n\t}\n\n\tcontext.ui.setProgress(\"Connecting to sp-gateway and synchronizing models…\");\n\ttry {\n\t\tconst baseUrl = normalizeSpGatewayBaseUrl(gatewayUrl);\n\t\tconst models = await fetchSpGatewayModels(baseUrl, apiKey.trim());\n\t\twriteSpGatewayState(baseUrl, models);\n\t\twriteSpGatewayLogin(apiKey.trim(), models[0]!.id);\n\t\tpi.registerProvider(SP_GATEWAY_PROVIDER_ID, createSpGatewayProviderConfig(baseUrl, models));\n\t\tawait context.modelRegistry.setRuntimeApiKey(SP_GATEWAY_PROVIDER_ID, apiKey.trim());\n\t\tconst firstModel = models[0]!;\n\t\tconst selected = await pi.setModel({\n\t\t\t...firstModel,\n\t\t\tprovider: SP_GATEWAY_PROVIDER_ID,\n\t\t\tbaseUrl,\n\t\t\tapi: \"openai-completions\",\n\t\t});\n\t\tcontext.ui.notify(\n\t\t\tselected\n\t\t\t\t? `Connected to sp-gateway · ${models.length} models · selected ${firstModel.id}.`\n\t\t\t\t: `Connected to sp-gateway · ${models.length} models available. Use /model to select one.`,\n\t\t\t\"info\",\n\t\t);\n\t} finally {\n\t\tcontext.ui.setProgress(undefined);\n\t}\n\n\treturn true;\n}\n\nexport function registerLoginCommand(pi: ExtensionAPI) {\n\tpi.registerCommand(\"login\", {\n\t\tdescription: \"Log in to SP Gateway\",\n\t\thandler: async (args, context) => {\n\t\t\ttry {\n\t\t\t\tawait runSlashLogin(pi, args, context);\n\t\t\t} catch (error) {\n\t\t\t\tcontext.ui.setProgress(undefined);\n\t\t\t\tcontext.ui.notify(error instanceof Error ? error.message : String(error), \"error\");\n\t\t\t}\n\t\t},\n\t});\n}\n"]}
1
+ {"version":3,"file":"login-command.js","sourceRoot":"","sources":["../src/login-command.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ExtensionAPI, ExtensionCommandContext } from \"@senpiper/pi-coding-agent\";\nimport {\n\tcreateSpGatewayProviderConfig,\n\tfetchSpGatewayModels,\n\tnormalizeSpGatewayBaseUrl,\n\treadSpGatewayState,\n\tSP_GATEWAY_PROVIDER_ID,\n\twriteSpGatewayLogin,\n\twriteSpGatewayState,\n} from \"./gateway.ts\";\n\nexport async function runSlashLogin(pi: ExtensionAPI, args: string, context: ExtensionCommandContext) {\n\tconst existingState = readSpGatewayState();\n\tconst argumentUrl = args.trim();\n\tconst enteredUrl = argumentUrl\n\t\t? argumentUrl\n\t\t: await context.ui.input(\"Senpiper Gateway URL\", existingState?.baseUrl ?? \"https://gateway.example.com/v1\");\n\tif (enteredUrl === undefined) {\n\t\tcontext.ui.notify(\"Senpiper Gateway login cancelled.\", \"warning\");\n\t\treturn false;\n\t}\n\tconst gatewayUrl = enteredUrl.trim() || existingState?.baseUrl;\n\tif (!gatewayUrl) {\n\t\tcontext.ui.notify(\"A Senpiper Gateway URL is required.\", \"error\");\n\t\treturn false;\n\t}\n\tconst apiKey = await context.ui.input(\"Senpiper Gateway API key\", \"Paste your API key\", { secret: true });\n\tif (!apiKey?.trim()) {\n\t\tcontext.ui.notify(\n\t\t\tapiKey === undefined ? \"Senpiper Gateway login cancelled.\" : \"An API key is required.\",\n\t\t\t\"warning\",\n\t\t);\n\t\treturn false;\n\t}\n\n\tcontext.ui.setProgress(\"Connecting to Senpiper Gateway and synchronizing models…\");\n\ttry {\n\t\tconst baseUrl = normalizeSpGatewayBaseUrl(gatewayUrl);\n\t\tconst models = await fetchSpGatewayModels(baseUrl, apiKey.trim());\n\t\twriteSpGatewayState(baseUrl, models);\n\t\twriteSpGatewayLogin(apiKey.trim(), models[0]!.id);\n\t\tpi.registerProvider(SP_GATEWAY_PROVIDER_ID, createSpGatewayProviderConfig(baseUrl, models));\n\t\tawait context.modelRegistry.setRuntimeApiKey(SP_GATEWAY_PROVIDER_ID, apiKey.trim());\n\t\tconst firstModel = models[0]!;\n\t\tconst selected = await pi.setModel({\n\t\t\t...firstModel,\n\t\t\tprovider: SP_GATEWAY_PROVIDER_ID,\n\t\t\tbaseUrl,\n\t\t\tapi: \"openai-completions\",\n\t\t});\n\t\tcontext.ui.notify(\n\t\t\tselected\n\t\t\t\t? `Connected to Senpiper Gateway · ${models.length} models · selected ${firstModel.id}.`\n\t\t\t\t: `Connected to Senpiper Gateway · ${models.length} models available. Use /model to select one.`,\n\t\t\t\"info\",\n\t\t);\n\t} finally {\n\t\tcontext.ui.setProgress(undefined);\n\t}\n\n\treturn true;\n}\n\nexport function registerLoginCommand(pi: ExtensionAPI) {\n\tpi.registerCommand(\"login\", {\n\t\tdescription: \"Log in to Senpiper Gateway\",\n\t\thandler: async (args, context) => {\n\t\t\ttry {\n\t\t\t\tawait runSlashLogin(pi, args, context);\n\t\t\t} catch (error) {\n\t\t\t\tcontext.ui.setProgress(undefined);\n\t\t\t\tcontext.ui.notify(error instanceof Error ? error.message : String(error), \"error\");\n\t\t\t}\n\t\t},\n\t});\n}\n"]}
@@ -5,16 +5,42 @@ export interface SpWebSidecarState {
5
5
  executable: string;
6
6
  args: string[];
7
7
  startedAt: string;
8
+ /** Random identity inherited only by this launched process tree. */
9
+ launchToken?: string;
8
10
  }
9
11
  export interface SpWebLaunchResult {
10
12
  status: "disabled" | "reused" | "launched" | "pending" | "failed";
11
13
  pid?: number;
12
14
  error?: string;
13
15
  }
16
+ export interface SpWebStopResult {
17
+ status: "stopped" | "not-running" | "pending" | "failed";
18
+ pid?: number;
19
+ error?: string;
20
+ }
21
+ export interface SpWebProcessIdentity {
22
+ launchToken?: string;
23
+ /** Full argv of the live process, when the platform exposes argument boundaries. */
24
+ argv?: string[];
25
+ /** Joined command line, used when argv boundaries are unavailable. */
26
+ command?: string;
27
+ /** Live process environment, when readable. */
28
+ env?: NodeJS.ProcessEnv;
29
+ /** Wall-clock start time of the live process, when readable. */
30
+ startedAtMs?: number;
31
+ }
14
32
  export interface SpWebLauncherOptions {
15
33
  env?: NodeJS.ProcessEnv;
16
34
  spawnProcess?: (executable: string, args: readonly string[], options: SpawnOptions) => ChildProcess;
17
35
  isProcessRunning?: (pid: number) => boolean;
36
+ /** Verifies that a live PID is still the process recorded at launch. */
37
+ readProcessIdentity?: (pid: number) => SpWebProcessIdentity | undefined;
38
+ /** Sends the graceful stop signal. Defaults to process.kill. */
39
+ signalProcess?: (pid: number, signal: NodeJS.Signals) => void;
40
+ /** Waits for a signaled PID to exit. Defaults to polling isProcessRunning. */
41
+ waitForProcessExit?: (pid: number, timeoutMs: number) => Promise<boolean>;
42
+ /** Grace period for /web off before reporting a timeout. Defaults to 8000 ms. */
43
+ stopTimeoutMs?: number;
18
44
  }
19
45
  export declare function resolveSpWebLaunch(env: NodeJS.ProcessEnv, runtime?: {
20
46
  moduleUrl: string;
@@ -25,5 +51,6 @@ export declare function resolveSpWebLaunch(env: NodeJS.ProcessEnv, runtime?: {
25
51
  args: string[];
26
52
  };
27
53
  export declare function ensureSpWebSidecar(discoveryDir: string, options?: SpWebLauncherOptions): SpWebLaunchResult;
54
+ export declare function stopSpWebSidecar(discoveryDir: string, options?: SpWebLauncherOptions): Promise<SpWebStopResult>;
28
55
  export declare function getSpWebSidecarStatePath(discoveryDir: string): string;
29
56
  //# sourceMappingURL=sp-web-launcher.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sp-web-launcher.d.ts","sourceRoot":"","sources":["../src/sp-web-launcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,YAAY,EAAS,MAAM,oBAAoB,CAAC;AAqBjF,MAAM,WAAW,iBAAiB;IACjC,OAAO,EAAE,CAAC,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IACjC,MAAM,EAAE,UAAU,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,CAAC;IAClE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,oBAAoB;IACpC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,YAAY,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,EAAE,YAAY,KAAK,YAAY,CAAC;IACpG,gBAAgB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;CAC5C;AAyED,wBAAgB,kBAAkB,CACjC,GAAG,EAAE,MAAM,CAAC,UAAU,EACtB,OAAO;;;;CAA0F,GAC/F;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CAkBxC;AAUD,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,GAAE,oBAAyB,GAAG,iBAAiB,CAyE9G;AAED,wBAAgB,wBAAwB,CAAC,YAAY,EAAE,MAAM,UAE5D","sourcesContent":["import { type ChildProcess, type SpawnOptions, spawn } from \"node:child_process\";\nimport {\n\tchmodSync,\n\tcloseSync,\n\tconstants,\n\topenSync,\n\treadFileSync,\n\ttype Stats,\n\tstatSync,\n\tunlinkSync,\n\twriteFileSync,\n} from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { join } from \"node:path\";\n\nconst SIDECAR_VERSION = 1;\nconst SIDECAR_STATE_FILE = \"sp-web-sidecar.json\";\nconst LEGACY_STATE_FILE = \"pi-web-sidecar.json\";\n// Keep one lock namespace while older SP launchers are still running.\nconst SIDECAR_LOCK_FILE = \"pi-web-sidecar.lock\";\n\nexport interface SpWebSidecarState {\n\tversion: 1;\n\tpid: number;\n\texecutable: string;\n\targs: string[];\n\tstartedAt: string;\n}\n\nexport interface SpWebLaunchResult {\n\tstatus: \"disabled\" | \"reused\" | \"launched\" | \"pending\" | \"failed\";\n\tpid?: number;\n\terror?: string;\n}\n\nexport interface SpWebLauncherOptions {\n\tenv?: NodeJS.ProcessEnv;\n\tspawnProcess?: (executable: string, args: readonly string[], options: SpawnOptions) => ChildProcess;\n\tisProcessRunning?: (pid: number) => boolean;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction readState(path: string): SpWebSidecarState | undefined {\n\ttry {\n\t\tconst value = JSON.parse(readFileSync(path, \"utf8\")) as unknown;\n\t\tif (\n\t\t\t!isRecord(value) ||\n\t\t\tvalue.version !== SIDECAR_VERSION ||\n\t\t\ttypeof value.pid !== \"number\" ||\n\t\t\t!Number.isSafeInteger(value.pid) ||\n\t\t\tvalue.pid <= 0 ||\n\t\t\ttypeof value.executable !== \"string\" ||\n\t\t\t!Array.isArray(value.args) ||\n\t\t\t!value.args.every((entry) => typeof entry === \"string\") ||\n\t\t\ttypeof value.startedAt !== \"string\"\n\t\t) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn {\n\t\t\tversion: SIDECAR_VERSION,\n\t\t\tpid: value.pid,\n\t\t\texecutable: value.executable,\n\t\t\targs: value.args,\n\t\t\tstartedAt: value.startedAt,\n\t\t};\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction processIsRunning(pid: number) {\n\ttry {\n\t\tprocess.kill(pid, 0);\n\t\treturn true;\n\t} catch (error) {\n\t\treturn isRecord(error) && error.code === \"EPERM\";\n\t}\n}\n\nfunction removeFile(path: string) {\n\ttry {\n\t\tunlinkSync(path);\n\t} catch (error) {\n\t\tif (!isRecord(error) || error.code !== \"ENOENT\") throw error;\n\t}\n}\n\nfunction writePrivateState(path: string, state: SpWebSidecarState) {\n\twriteFileSync(path, `${JSON.stringify(state, null, \"\\t\")}\\n`, {\n\t\tencoding: \"utf8\",\n\t\tmode: 0o600,\n\t});\n\tif (process.platform !== \"win32\") chmodSync(path, 0o600);\n}\n\nfunction parseArgs(env: NodeJS.ProcessEnv): string[] {\n\tconst raw = env.SP_WEB_ARGS_JSON ?? env.SP_PI_WEB_ARGS_JSON;\n\tif (!raw) return [];\n\tconst value = JSON.parse(raw) as unknown;\n\tif (!Array.isArray(value) || !value.every((entry) => typeof entry === \"string\")) {\n\t\tthrow new Error(\"SP_WEB_ARGS_JSON must be a JSON array of strings.\");\n\t}\n\treturn value;\n}\n\nfunction disabled(env: NodeJS.ProcessEnv) {\n\treturn /^(?:1|true|yes)$/iu.test(env.SP_WEB_DISABLE ?? env.SP_PI_WEB_DISABLE ?? \"\");\n}\n\nexport function resolveSpWebLaunch(\n\tenv: NodeJS.ProcessEnv,\n\truntime = { moduleUrl: import.meta.url, execPath: process.execPath, bun: !!process.versions.bun },\n): { executable: string; args: string[] } {\n\tconst args = parseArgs(env);\n\tif (!args.includes(\"--no-open\")) args.push(\"--no-open\");\n\tconst explicit = (env.SP_WEB_EXECUTABLE ?? env.SP_PI_WEB_EXECUTABLE)?.trim();\n\tif (explicit) return { executable: explicit, args };\n\n\t// The module anchor handles Node installs. The executable anchor also handles\n\t// compiled Bun binaries inside the npm package, whose module URL is virtual.\n\tfor (const anchor of [runtime.moduleUrl, runtime.execPath]) {\n\t\ttry {\n\t\t\tconst entry = createRequire(anchor).resolve(\"@senpiper/sp-web/bin/sp-web.js\");\n\t\t\treturn { executable: runtime.bun ? \"node\" : runtime.execPath, args: [entry, ...args] };\n\t\t} catch (error) {\n\t\t\tif (!isRecord(error) || error.code !== \"MODULE_NOT_FOUND\") throw error;\n\t\t}\n\t}\n\t// Standalone archives do not necessarily contain an npm dependency tree.\n\treturn { executable: \"sp-web\", args };\n}\n\nfunction lockIsStale(path: string, stat: (path: string) => Stats = statSync) {\n\ttry {\n\t\treturn Date.now() - stat(path).mtimeMs > 30_000;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nexport function ensureSpWebSidecar(discoveryDir: string, options: SpWebLauncherOptions = {}): SpWebLaunchResult {\n\tconst env = options.env ?? process.env;\n\tif (disabled(env)) return { status: \"disabled\" };\n\n\tconst statePath = join(discoveryDir, SIDECAR_STATE_FILE);\n\tconst legacyPath = join(discoveryDir, LEGACY_STATE_FILE);\n\tconst lockPath = join(discoveryDir, SIDECAR_LOCK_FILE);\n\tconst isRunning = options.isProcessRunning ?? processIsRunning;\n\tconst liveState = () => [readState(statePath), readState(legacyPath)].find((state) => state && isRunning(state.pid));\n\tconst current = liveState();\n\tif (current) return { status: \"reused\", pid: current.pid };\n\n\tlet lockFd: number | undefined;\n\tlet ownsLock = false;\n\ttry {\n\t\ttry {\n\t\t\tlockFd = openSync(lockPath, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY, 0o600);\n\t\t\townsLock = true;\n\t\t\twriteFileSync(lockFd, `${process.pid}\\n`, \"utf8\");\n\t\t} catch (error) {\n\t\t\tif (!isRecord(error) || error.code !== \"EEXIST\") throw error;\n\t\t\tconst racedState = liveState();\n\t\t\tif (racedState) return { status: \"reused\", pid: racedState.pid };\n\t\t\tif (!lockIsStale(lockPath)) return { status: \"pending\" };\n\t\t\tremoveFile(lockPath);\n\t\t\tlockFd = openSync(lockPath, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY, 0o600);\n\t\t\townsLock = true;\n\t\t\twriteFileSync(lockFd, `${process.pid}\\n`, \"utf8\");\n\t\t}\n\n\t\tconst racedState = liveState();\n\t\tif (racedState) return { status: \"reused\", pid: racedState.pid };\n\t\tconst { executable, args } = resolveSpWebLaunch(env);\n\t\tconst childEnv: NodeJS.ProcessEnv = {\n\t\t\t...env,\n\t\t\tSP_WEB_NO_OPEN: \"1\",\n\t\t\tSP_TERMINAL_BRIDGE_ONLY: \"1\",\n\t\t\tSP_TERMINAL_BRIDGE_DISCOVERY_DIR: discoveryDir,\n\t\t\tSP_TERMINAL_BRIDGE_PROTOCOL: \"sp-terminal-bridge-v1\",\n\t\t};\n\t\tconst spawnProcess =\n\t\t\toptions.spawnProcess ?? ((command, commandArgs, spawnOptions) => spawn(command, commandArgs, spawnOptions));\n\t\tconst child = spawnProcess(executable, args, {\n\t\t\tdetached: true,\n\t\t\tstdio: \"ignore\",\n\t\t\tshell: false,\n\t\t\tenv: childEnv,\n\t\t});\n\t\tchild.once(\"error\", () => {});\n\t\tif (child.pid === undefined) {\n\t\t\treturn {\n\t\t\t\tstatus: \"failed\",\n\t\t\t\terror: `Could not start SP Web with ${executable}. Install Node and the bundled @senpiper/sp-web package, or set SP_WEB_EXECUTABLE to an installed sp-web executable.`,\n\t\t\t};\n\t\t}\n\t\tconst state: SpWebSidecarState = {\n\t\t\tversion: SIDECAR_VERSION,\n\t\t\tpid: child.pid,\n\t\t\texecutable,\n\t\t\targs,\n\t\t\tstartedAt: new Date().toISOString(),\n\t\t};\n\t\twritePrivateState(statePath, state);\n\t\t// Older cooperating launchers must see the new process too.\n\t\twritePrivateState(legacyPath, state);\n\t\tchild.unref();\n\t\treturn { status: \"launched\", pid: child.pid };\n\t} catch (error) {\n\t\treturn { status: \"failed\", error: error instanceof Error ? error.message : String(error) };\n\t} finally {\n\t\tif (lockFd !== undefined) closeSync(lockFd);\n\t\tif (ownsLock) removeFile(lockPath);\n\t}\n}\n\nexport function getSpWebSidecarStatePath(discoveryDir: string) {\n\treturn join(discoveryDir, SIDECAR_STATE_FILE);\n}\n"]}
1
+ {"version":3,"file":"sp-web-launcher.d.ts","sourceRoot":"","sources":["../src/sp-web-launcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAgB,KAAK,YAAY,EAAS,MAAM,oBAAoB,CAAC;AA8B/F,MAAM,WAAW,iBAAiB;IACjC,OAAO,EAAE,CAAC,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IACjC,MAAM,EAAE,UAAU,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,CAAC;IAClE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC/B,MAAM,EAAE,SAAS,GAAG,aAAa,GAAG,SAAS,GAAG,QAAQ,CAAC;IACzD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,oBAAoB;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oFAAoF;IACpF,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+CAA+C;IAC/C,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,gEAAgE;IAChE,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACpC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,YAAY,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,EAAE,YAAY,KAAK,YAAY,CAAC;IACpG,gBAAgB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IAC5C,wEAAwE;IACxE,mBAAmB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,oBAAoB,GAAG,SAAS,CAAC;IACxE,gEAAgE;IAChE,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,KAAK,IAAI,CAAC;IAC9D,8EAA8E;IAC9E,kBAAkB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1E,iFAAiF;IACjF,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB;AA4ED,wBAAgB,kBAAkB,CACjC,GAAG,EAAE,MAAM,CAAC,UAAU,EACtB,OAAO;;;;CAA0F,GAC/F;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CAkBxC;AAmJD,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,GAAE,oBAAyB,GAAG,iBAAiB,CA6D9G;AAED,wBAAsB,gBAAgB,CACrC,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE,oBAAyB,GAChC,OAAO,CAAC,eAAe,CAAC,CA0G1B;AAED,wBAAgB,wBAAwB,CAAC,YAAY,EAAE,MAAM,UAE5D","sourcesContent":["import { type ChildProcess, execFileSync, type SpawnOptions, spawn } from \"node:child_process\";\nimport { randomBytes } from \"node:crypto\";\nimport {\n\tchmodSync,\n\tcloseSync,\n\tconstants,\n\texistsSync,\n\topenSync,\n\treadFileSync,\n\ttype Stats,\n\tstatSync,\n\tunlinkSync,\n\twriteFileSync,\n} from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { join } from \"node:path\";\nimport { setTimeout as sleep } from \"node:timers/promises\";\n\nconst SIDECAR_VERSION = 1;\nconst SIDECAR_STATE_FILE = \"sp-web-sidecar.json\";\nconst LEGACY_STATE_FILE = \"pi-web-sidecar.json\";\n// Keep one lock namespace while older Senpiper Workbench launchers are still running.\nconst SIDECAR_LOCK_FILE = \"pi-web-sidecar.lock\";\n// The sp-web wrapper forwards SIGTERM to Next and force-kills Next after 5s.\nconst SIDECAR_STOP_TIMEOUT_MS = 8_000;\nconst SIDECAR_STOP_POLL_MS = 100;\n// Older records lack a launch token. Their timestamp must match the process\n// table's second-resolution start time as well as the exact command line.\nconst SIDECAR_IDENTITY_TOLERANCE_MS = 5_000;\n\nexport interface SpWebSidecarState {\n\tversion: 1;\n\tpid: number;\n\texecutable: string;\n\targs: string[];\n\tstartedAt: string;\n\t/** Random identity inherited only by this launched process tree. */\n\tlaunchToken?: string;\n}\n\nexport interface SpWebLaunchResult {\n\tstatus: \"disabled\" | \"reused\" | \"launched\" | \"pending\" | \"failed\";\n\tpid?: number;\n\terror?: string;\n}\n\nexport interface SpWebStopResult {\n\tstatus: \"stopped\" | \"not-running\" | \"pending\" | \"failed\";\n\tpid?: number;\n\terror?: string;\n}\n\nexport interface SpWebProcessIdentity {\n\tlaunchToken?: string;\n\t/** Full argv of the live process, when the platform exposes argument boundaries. */\n\targv?: string[];\n\t/** Joined command line, used when argv boundaries are unavailable. */\n\tcommand?: string;\n\t/** Live process environment, when readable. */\n\tenv?: NodeJS.ProcessEnv;\n\t/** Wall-clock start time of the live process, when readable. */\n\tstartedAtMs?: number;\n}\n\nexport interface SpWebLauncherOptions {\n\tenv?: NodeJS.ProcessEnv;\n\tspawnProcess?: (executable: string, args: readonly string[], options: SpawnOptions) => ChildProcess;\n\tisProcessRunning?: (pid: number) => boolean;\n\t/** Verifies that a live PID is still the process recorded at launch. */\n\treadProcessIdentity?: (pid: number) => SpWebProcessIdentity | undefined;\n\t/** Sends the graceful stop signal. Defaults to process.kill. */\n\tsignalProcess?: (pid: number, signal: NodeJS.Signals) => void;\n\t/** Waits for a signaled PID to exit. Defaults to polling isProcessRunning. */\n\twaitForProcessExit?: (pid: number, timeoutMs: number) => Promise<boolean>;\n\t/** Grace period for /web off before reporting a timeout. Defaults to 8000 ms. */\n\tstopTimeoutMs?: number;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction readState(path: string): SpWebSidecarState | undefined {\n\ttry {\n\t\tconst value = JSON.parse(readFileSync(path, \"utf8\")) as unknown;\n\t\tif (\n\t\t\t!isRecord(value) ||\n\t\t\tvalue.version !== SIDECAR_VERSION ||\n\t\t\ttypeof value.pid !== \"number\" ||\n\t\t\t!Number.isSafeInteger(value.pid) ||\n\t\t\tvalue.pid <= 0 ||\n\t\t\ttypeof value.executable !== \"string\" ||\n\t\t\t!Array.isArray(value.args) ||\n\t\t\t!value.args.every((entry) => typeof entry === \"string\") ||\n\t\t\ttypeof value.startedAt !== \"string\" ||\n\t\t\t(value.launchToken !== undefined &&\n\t\t\t\t(typeof value.launchToken !== \"string\" || !/^[a-f0-9]{64}$/u.test(value.launchToken)))\n\t\t) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn {\n\t\t\tversion: SIDECAR_VERSION,\n\t\t\tpid: value.pid,\n\t\t\texecutable: value.executable,\n\t\t\targs: value.args,\n\t\t\tstartedAt: value.startedAt,\n\t\t\t...(typeof value.launchToken === \"string\" ? { launchToken: value.launchToken } : {}),\n\t\t};\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction processIsRunning(pid: number) {\n\ttry {\n\t\tprocess.kill(pid, 0);\n\t\treturn true;\n\t} catch (error) {\n\t\treturn isRecord(error) && error.code === \"EPERM\";\n\t}\n}\n\nfunction removeFile(path: string) {\n\ttry {\n\t\tunlinkSync(path);\n\t} catch (error) {\n\t\tif (!isRecord(error) || error.code !== \"ENOENT\") throw error;\n\t}\n}\n\nfunction writePrivateState(path: string, state: SpWebSidecarState) {\n\twriteFileSync(path, `${JSON.stringify(state, null, \"\\t\")}\\n`, {\n\t\tencoding: \"utf8\",\n\t\tmode: 0o600,\n\t});\n\tif (process.platform !== \"win32\") chmodSync(path, 0o600);\n}\n\nfunction parseArgs(env: NodeJS.ProcessEnv): string[] {\n\tconst raw = env.SP_WEB_ARGS_JSON ?? env.SP_PI_WEB_ARGS_JSON;\n\tif (!raw) return [];\n\tconst value = JSON.parse(raw) as unknown;\n\tif (!Array.isArray(value) || !value.every((entry) => typeof entry === \"string\")) {\n\t\tthrow new Error(\"SP_WEB_ARGS_JSON must be a JSON array of strings.\");\n\t}\n\treturn value;\n}\n\nfunction disabled(env: NodeJS.ProcessEnv) {\n\treturn /^(?:1|true|yes)$/iu.test(env.SP_WEB_DISABLE ?? env.SP_PI_WEB_DISABLE ?? \"\");\n}\n\nexport function resolveSpWebLaunch(\n\tenv: NodeJS.ProcessEnv,\n\truntime = { moduleUrl: import.meta.url, execPath: process.execPath, bun: !!process.versions.bun },\n): { executable: string; args: string[] } {\n\tconst args = parseArgs(env);\n\tif (!args.includes(\"--no-open\")) args.push(\"--no-open\");\n\tconst explicit = (env.SP_WEB_EXECUTABLE ?? env.SP_PI_WEB_EXECUTABLE)?.trim();\n\tif (explicit) return { executable: explicit, args };\n\n\t// The module anchor handles Node installs. The executable anchor also handles\n\t// compiled Bun binaries inside the npm package, whose module URL is virtual.\n\tfor (const anchor of [runtime.moduleUrl, runtime.execPath]) {\n\t\ttry {\n\t\t\tconst entry = createRequire(anchor).resolve(\"@senpiper/sp-web/bin/sp-web.js\");\n\t\t\treturn { executable: runtime.bun ? \"node\" : runtime.execPath, args: [entry, ...args] };\n\t\t} catch (error) {\n\t\t\tif (!isRecord(error) || error.code !== \"MODULE_NOT_FOUND\") throw error;\n\t\t}\n\t}\n\t// Standalone archives do not necessarily contain an npm dependency tree.\n\treturn { executable: \"sp-web\", args };\n}\n\nfunction lockIsStale(path: string, stat: (path: string) => Stats = statSync) {\n\ttry {\n\t\treturn Date.now() - stat(path).mtimeMs > 30_000;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/** Cooperative lock shared by stop and start. One operation owns it at a time. */\nfunction acquireSidecarLock(lockPath: string): number | \"pending\" {\n\tfor (let attempt = 0; attempt < 2; attempt++) {\n\t\tlet fd: number;\n\t\ttry {\n\t\t\tfd = openSync(lockPath, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY, 0o600);\n\t\t} catch (error) {\n\t\t\tif (!isRecord(error) || error.code !== \"EEXIST\") throw error;\n\t\t\tif (!lockIsStale(lockPath)) return \"pending\";\n\t\t\tremoveFile(lockPath);\n\t\t\tcontinue;\n\t\t}\n\t\ttry {\n\t\t\twriteFileSync(fd, `${process.pid}\\n`, \"utf8\");\n\t\t\treturn fd;\n\t\t} catch (error) {\n\t\t\tcloseSync(fd);\n\t\t\tremoveFile(lockPath);\n\t\t\tthrow error;\n\t\t}\n\t}\n\treturn \"pending\";\n}\n\nfunction readLinuxProcessArgv(pid: number): string[] | undefined {\n\ttry {\n\t\tconst argv = readFileSync(`/proc/${pid}/cmdline`, \"utf8\").split(\"\\0\");\n\t\twhile (argv.length > 0 && argv[argv.length - 1] === \"\") argv.pop();\n\t\treturn argv.length > 0 ? argv : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction readLinuxProcessEnvironment(pid: number): NodeJS.ProcessEnv | undefined {\n\ttry {\n\t\tconst env: NodeJS.ProcessEnv = {};\n\t\tfor (const entry of readFileSync(`/proc/${pid}/environ`, \"utf8\").split(\"\\0\")) {\n\t\t\tconst separator = entry.indexOf(\"=\");\n\t\t\tif (separator <= 0) continue;\n\t\t\tenv[entry.slice(0, separator)] = entry.slice(separator + 1);\n\t\t}\n\t\treturn env;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction readProcessTableField(pid: number, field: \"command\" | \"lstart\", withEnvironment = false): string | undefined {\n\tif (process.platform === \"win32\") return undefined;\n\ttry {\n\t\treturn execFileSync(\"ps\", [withEnvironment ? \"eww\" : \"-ww\", \"-o\", `${field}=`, \"-p\", String(pid)], {\n\t\t\tencoding: \"utf8\",\n\t\t\tstdio: [\"ignore\", \"pipe\", \"ignore\"],\n\t\t\ttimeout: 2_000,\n\t\t\tenv: { ...process.env, LC_ALL: \"C\" },\n\t\t}).trim();\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/**\n * Reads enough of the live process to prove it is still the recorded launch.\n * Linux exposes full argv plus the launch environment; other Unix systems fall\n * back to the process table command and start time.\n */\nfunction readLiveProcessIdentity(pid: number): SpWebProcessIdentity | undefined {\n\tconst startedAt = readProcessTableField(pid, \"lstart\");\n\tconst startedAtMs = startedAt === undefined ? Number.NaN : Date.parse(startedAt);\n\tif (process.platform === \"linux\") {\n\t\tconst argv = readLinuxProcessArgv(pid);\n\t\tif (argv) return { argv, env: readLinuxProcessEnvironment(pid), startedAtMs };\n\t}\n\tconst command = readProcessTableField(pid, \"command\");\n\tif (command === undefined || command === \"\") return undefined;\n\tconst environment = readProcessTableField(pid, \"command\", true);\n\tconst launchToken = environment?.match(/(?:^|\\s)SP_WEB_SIDECAR_TOKEN=([a-f0-9]{64})(?:\\s|$)/u)?.[1];\n\treturn { command, startedAtMs, launchToken };\n}\n\nfunction verifySidecarIdentity(\n\trecord: SpWebSidecarState,\n\tdiscoveryDir: string,\n\tidentity: SpWebProcessIdentity | undefined,\n): { ok: true } | { ok: false; reason: string } {\n\tif (!identity) return { ok: false, reason: \"its command line could not be read\" };\n\tconst owner = identity.env?.SP_TERMINAL_BRIDGE_DISCOVERY_DIR ?? identity.env?.PI_TERMINAL_BRIDGE_DISCOVERY_DIR;\n\tif (owner !== undefined && owner !== discoveryDir) {\n\t\treturn { ok: false, reason: \"it belongs to a different Senpiper Workbench profile\" };\n\t}\n\tif (record.launchToken !== undefined) {\n\t\t// Executable overrides can be shebang scripts or PATH aliases, so their\n\t\t// live argv need not match spawn's argv. A per-launch nonce survives that\n\t\t// transformation without trusting a PID or command name alone.\n\t\treturn (identity.launchToken ?? identity.env?.SP_WEB_SIDECAR_TOKEN) === record.launchToken\n\t\t\t? { ok: true }\n\t\t\t: { ok: false, reason: \"its launch identity does not match the recorded server\" };\n\t}\n\tconst expected = [record.executable, ...record.args];\n\tif (identity.argv) {\n\t\tif (identity.argv.length !== expected.length || identity.argv.some((value, index) => value !== expected[index])) {\n\t\t\treturn { ok: false, reason: \"its command line does not match the recorded launch\" };\n\t\t}\n\t} else if (identity.command !== undefined) {\n\t\tif (identity.command !== expected.join(\" \")) {\n\t\t\treturn { ok: false, reason: \"its command line does not match the recorded launch\" };\n\t\t}\n\t} else {\n\t\treturn { ok: false, reason: \"its command line could not be read\" };\n\t}\n\tconst recordedAt = Date.parse(record.startedAt);\n\tif (\n\t\tidentity.startedAtMs === undefined ||\n\t\t!Number.isFinite(identity.startedAtMs) ||\n\t\t!Number.isFinite(recordedAt) ||\n\t\tMath.abs(identity.startedAtMs - recordedAt) > SIDECAR_IDENTITY_TOLERANCE_MS\n\t) {\n\t\treturn { ok: false, reason: \"its start time does not match the recorded launch\" };\n\t}\n\treturn { ok: true };\n}\n\nasync function waitForProcessExit(\n\tpid: number,\n\ttimeoutMs: number,\n\tisRunning: (pid: number) => boolean,\n): Promise<boolean> {\n\tconst deadline = Date.now() + timeoutMs;\n\tfor (;;) {\n\t\tif (!isRunning(pid)) return true;\n\t\tconst remaining = deadline - Date.now();\n\t\tif (remaining <= 0) return false;\n\t\tawait sleep(Math.min(SIDECAR_STOP_POLL_MS, remaining));\n\t}\n}\n\nexport function ensureSpWebSidecar(discoveryDir: string, options: SpWebLauncherOptions = {}): SpWebLaunchResult {\n\tconst env = options.env ?? process.env;\n\tif (disabled(env)) return { status: \"disabled\" };\n\n\tconst statePath = join(discoveryDir, SIDECAR_STATE_FILE);\n\tconst legacyPath = join(discoveryDir, LEGACY_STATE_FILE);\n\tconst lockPath = join(discoveryDir, SIDECAR_LOCK_FILE);\n\tconst isRunning = options.isProcessRunning ?? processIsRunning;\n\tconst liveState = () => [readState(statePath), readState(legacyPath)].find((state) => state && isRunning(state.pid));\n\tlet lockFd: number | undefined;\n\ttry {\n\t\tconst acquired = acquireSidecarLock(lockPath);\n\t\tif (acquired === \"pending\") return { status: \"pending\" };\n\t\tlockFd = acquired;\n\n\t\tconst racedState = liveState();\n\t\tif (racedState) return { status: \"reused\", pid: racedState.pid };\n\t\tconst { executable, args } = resolveSpWebLaunch(env);\n\t\tconst launchToken = randomBytes(32).toString(\"hex\");\n\t\tconst childEnv: NodeJS.ProcessEnv = {\n\t\t\t...env,\n\t\t\tSP_WEB_NO_OPEN: \"1\",\n\t\t\tSP_TERMINAL_BRIDGE_ONLY: \"1\",\n\t\t\tSP_TERMINAL_BRIDGE_DISCOVERY_DIR: discoveryDir,\n\t\t\tSP_TERMINAL_BRIDGE_PROTOCOL: \"sp-terminal-bridge-v1\",\n\t\t\tSP_WEB_SIDECAR_TOKEN: launchToken,\n\t\t};\n\t\tconst spawnProcess =\n\t\t\toptions.spawnProcess ?? ((command, commandArgs, spawnOptions) => spawn(command, commandArgs, spawnOptions));\n\t\tconst child = spawnProcess(executable, args, {\n\t\t\tdetached: true,\n\t\t\tstdio: \"ignore\",\n\t\t\tshell: false,\n\t\t\tenv: childEnv,\n\t\t});\n\t\tchild.once(\"error\", () => {});\n\t\tif (child.pid === undefined) {\n\t\t\treturn {\n\t\t\t\tstatus: \"failed\",\n\t\t\t\terror: `Could not start Senpiper Workbench with ${executable}. Install Node and the bundled @senpiper/sp-web package, or set SP_WEB_EXECUTABLE to an installed sp-web executable.`,\n\t\t\t};\n\t\t}\n\t\tconst state: SpWebSidecarState = {\n\t\t\tversion: SIDECAR_VERSION,\n\t\t\tpid: child.pid,\n\t\t\texecutable,\n\t\t\targs,\n\t\t\tstartedAt: new Date().toISOString(),\n\t\t\tlaunchToken,\n\t\t};\n\t\twritePrivateState(statePath, state);\n\t\t// Older cooperating launchers must see the new process too.\n\t\twritePrivateState(legacyPath, state);\n\t\tchild.unref();\n\t\treturn { status: \"launched\", pid: child.pid };\n\t} catch (error) {\n\t\treturn { status: \"failed\", error: error instanceof Error ? error.message : String(error) };\n\t} finally {\n\t\tif (lockFd !== undefined) closeSync(lockFd);\n\t\tif (lockFd !== undefined) removeFile(lockPath);\n\t}\n}\n\nexport async function stopSpWebSidecar(\n\tdiscoveryDir: string,\n\toptions: SpWebLauncherOptions = {},\n): Promise<SpWebStopResult> {\n\tif (!existsSync(discoveryDir)) return { status: \"not-running\" };\n\n\tconst statePath = join(discoveryDir, SIDECAR_STATE_FILE);\n\tconst legacyPath = join(discoveryDir, LEGACY_STATE_FILE);\n\tconst lockPath = join(discoveryDir, SIDECAR_LOCK_FILE);\n\tconst isRunning = options.isProcessRunning ?? processIsRunning;\n\tconst signalProcess =\n\t\toptions.signalProcess ??\n\t\t((pid: number, signal: NodeJS.Signals) => {\n\t\t\tprocess.kill(pid, signal);\n\t\t});\n\tconst readIdentity = options.readProcessIdentity ?? readLiveProcessIdentity;\n\tconst timeoutMs = options.stopTimeoutMs ?? SIDECAR_STOP_TIMEOUT_MS;\n\tif (!Number.isFinite(timeoutMs) || timeoutMs < 0 || timeoutMs > 20_000) {\n\t\treturn { status: \"failed\", error: \"The stop timeout must be between 0 and 20000 ms.\" };\n\t}\n\tconst waitForExit =\n\t\toptions.waitForProcessExit ?? ((pid: number, bound: number) => waitForProcessExit(pid, bound, isRunning));\n\n\tlet lockFd: number | undefined;\n\ttry {\n\t\tconst acquired = acquireSidecarLock(lockPath);\n\t\tif (acquired === \"pending\") return { status: \"pending\" };\n\t\tlockFd = acquired;\n\t} catch (error) {\n\t\treturn { status: \"failed\", error: error instanceof Error ? error.message : String(error) };\n\t}\n\n\tlet result: SpWebStopResult = { status: \"failed\", error: \"The stop did not complete.\" };\n\ttry {\n\t\tconst records = [readState(statePath), readState(legacyPath)].filter(\n\t\t\t(record): record is SpWebSidecarState => record !== undefined && isRunning(record.pid),\n\t\t);\n\t\tconst distinctPids = new Set(records.map((record) => record.pid));\n\t\tif (distinctPids.size > 1) {\n\t\t\tresult = {\n\t\t\t\tstatus: \"failed\",\n\t\t\t\terror: `Conflicting live Senpiper Workbench records point to PIDs ${[...distinctPids].join(\" and \")}. Remove the stale record after checking both processes.`,\n\t\t\t};\n\t\t} else if (records.length === 0) {\n\t\t\tremoveFile(statePath);\n\t\t\tremoveFile(legacyPath);\n\t\t\tresult = { status: \"not-running\" };\n\t\t} else {\n\t\t\tconst record = records[0]!;\n\t\t\tif (record.pid === process.pid || record.pid === 1) {\n\t\t\t\tthrow new Error(`Refusing to stop PID ${record.pid}: it is not a managed web server.`);\n\t\t\t}\n\t\t\tconst verification = verifySidecarIdentity(record, discoveryDir, readIdentity(record.pid));\n\t\t\tif (!verification.ok) {\n\t\t\t\tresult = {\n\t\t\t\t\tstatus: \"failed\",\n\t\t\t\t\tpid: record.pid,\n\t\t\t\t\terror: `Refusing to stop PID ${record.pid}: ${verification.reason}.`,\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\tlet signaled = false;\n\t\t\t\ttry {\n\t\t\t\t\tsignalProcess(record.pid, \"SIGTERM\");\n\t\t\t\t\tsignaled = true;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tresult = {\n\t\t\t\t\t\tstatus: \"failed\",\n\t\t\t\t\t\tpid: record.pid,\n\t\t\t\t\t\terror: `Could not stop PID ${record.pid}: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\tif (signaled) {\n\t\t\t\t\tconst exited = (await waitForExit(record.pid, timeoutMs)) && !isRunning(record.pid);\n\t\t\t\t\tif (!exited) {\n\t\t\t\t\t\tresult = {\n\t\t\t\t\t\t\tstatus: \"failed\",\n\t\t\t\t\t\t\tpid: record.pid,\n\t\t\t\t\t\t\terror: `PID ${record.pid} did not exit within ${timeoutMs} ms.`,\n\t\t\t\t\t\t};\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Cleanup only after the process exit was confirmed.\n\t\t\t\t\t\tremoveFile(statePath);\n\t\t\t\t\t\tremoveFile(legacyPath);\n\t\t\t\t\t\tresult = { status: \"stopped\", pid: record.pid };\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} catch (error) {\n\t\tresult = { status: \"failed\", error: error instanceof Error ? error.message : String(error) };\n\t}\n\n\ttry {\n\t\tif (lockFd !== undefined) {\n\t\t\tcloseSync(lockFd);\n\t\t\tremoveFile(lockPath);\n\t\t}\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tresult =\n\t\t\tresult.status === \"failed\"\n\t\t\t\t? { ...result, error: `${result.error ?? \"The stop failed.\"} Could not release ${lockPath}: ${message}` }\n\t\t\t\t: {\n\t\t\t\t\t\tstatus: \"failed\",\n\t\t\t\t\t\tpid: result.pid,\n\t\t\t\t\t\terror: `Stopped the managed web server but could not release ${lockPath}: ${message}`,\n\t\t\t\t\t};\n\t}\n\treturn result;\n}\n\nexport function getSpWebSidecarStatePath(discoveryDir: string) {\n\treturn join(discoveryDir, SIDECAR_STATE_FILE);\n}\n"]}