@senpiper/sp-agent 0.1.9 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/INSTALL.md +1 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/distribution.d.ts +2 -2
- package/dist/distribution.d.ts.map +1 -1
- package/dist/distribution.js +1 -2
- package/dist/distribution.js.map +1 -1
- package/dist/extension-identity.generated.d.ts +3 -3
- package/dist/extension-identity.generated.d.ts.map +1 -1
- package/dist/extension-identity.generated.js +3 -3
- package/dist/extension-identity.generated.js.map +1 -1
- package/dist/runtime-cli.d.ts.map +1 -1
- package/dist/runtime-cli.js +1 -1
- package/dist/runtime-cli.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/node_modules/@senpiper/pi-coding-agent/dist/bundle/chunks/{chunk-IK73Y232.js → chunk-LUSUF4ZH.js} +1 -1
- package/node_modules/@senpiper/pi-coding-agent/dist/bundle/cli.js +1 -1
- package/node_modules/@senpiper/pi-coding-agent/dist/bundle/index.js +1 -1
- package/node_modules/@senpiper/pi-coding-agent/dist/bundle/rpc-entry.js +1 -1
- package/node_modules/@senpiper/pi-coding-agent/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
- package/node_modules/@senpiper/pi-coding-agent/node_modules/@earendil-works/pi-ai/dist/providers/data/amazon-bedrock.json +1 -1
- package/node_modules/@senpiper/pi-coding-agent/node_modules/@earendil-works/pi-ai/dist/providers/data/nvidia.json +1 -1
- package/node_modules/@senpiper/pi-coding-agent/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode-go.json +1 -1
- package/node_modules/@senpiper/pi-coding-agent/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
- package/node_modules/@senpiper/pi-coding-agent/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
- package/node_modules/@senpiper/sp-agent-extensions/TERMINAL_WEB_BRIDGE.md +1 -1
- package/node_modules/@senpiper/sp-agent-extensions/dist/cli-commands.d.ts +0 -6
- package/node_modules/@senpiper/sp-agent-extensions/dist/cli-commands.d.ts.map +1 -1
- package/node_modules/@senpiper/sp-agent-extensions/dist/cli-commands.js +2 -81
- package/node_modules/@senpiper/sp-agent-extensions/dist/cli-commands.js.map +1 -1
- package/node_modules/@senpiper/sp-agent-extensions/dist/commands.d.ts +2 -2
- package/node_modules/@senpiper/sp-agent-extensions/dist/commands.d.ts.map +1 -1
- package/node_modules/@senpiper/sp-agent-extensions/dist/commands.js +2 -2
- package/node_modules/@senpiper/sp-agent-extensions/dist/commands.js.map +1 -1
- package/node_modules/@senpiper/sp-agent-extensions/dist/gateway.d.ts.map +1 -1
- package/node_modules/@senpiper/sp-agent-extensions/dist/gateway.js +14 -3
- package/node_modules/@senpiper/sp-agent-extensions/dist/gateway.js.map +1 -1
- package/node_modules/@senpiper/sp-agent-extensions/dist/index.d.ts +0 -2
- package/node_modules/@senpiper/sp-agent-extensions/dist/index.d.ts.map +1 -1
- package/node_modules/@senpiper/sp-agent-extensions/dist/index.js +0 -2
- package/node_modules/@senpiper/sp-agent-extensions/dist/index.js.map +1 -1
- package/node_modules/@senpiper/sp-agent-extensions/dist/interactions.d.ts +0 -2
- package/node_modules/@senpiper/sp-agent-extensions/dist/interactions.d.ts.map +1 -1
- package/node_modules/@senpiper/sp-agent-extensions/dist/interactions.js +0 -3
- package/node_modules/@senpiper/sp-agent-extensions/dist/interactions.js.map +1 -1
- package/node_modules/@senpiper/sp-agent-extensions/dist/login-command.d.ts.map +1 -1
- package/node_modules/@senpiper/sp-agent-extensions/dist/login-command.js +1 -11
- package/node_modules/@senpiper/sp-agent-extensions/dist/login-command.js.map +1 -1
- package/node_modules/@senpiper/sp-agent-extensions/dist/startup-header.d.ts.map +1 -1
- package/node_modules/@senpiper/sp-agent-extensions/dist/startup-header.js +1 -1
- package/node_modules/@senpiper/sp-agent-extensions/dist/startup-header.js.map +1 -1
- package/node_modules/@senpiper/sp-agent-extensions/package.json +1 -4
- package/package.json +3 -3
- package/node_modules/@senpiper/sp-agent-extensions/dist/onboarding-command.d.ts +0 -4
- package/node_modules/@senpiper/sp-agent-extensions/dist/onboarding-command.d.ts.map +0 -1
- package/node_modules/@senpiper/sp-agent-extensions/dist/onboarding-command.js +0 -67
- package/node_modules/@senpiper/sp-agent-extensions/dist/onboarding-command.js.map +0 -1
- package/node_modules/@senpiper/sp-agent-extensions/dist/onboarding-installation.d.ts +0 -14
- package/node_modules/@senpiper/sp-agent-extensions/dist/onboarding-installation.d.ts.map +0 -1
- package/node_modules/@senpiper/sp-agent-extensions/dist/onboarding-installation.js +0 -32
- package/node_modules/@senpiper/sp-agent-extensions/dist/onboarding-installation.js.map +0 -1
- package/node_modules/@senpiper/sp-agent-extensions/dist/onboarding.d.ts +0 -39
- package/node_modules/@senpiper/sp-agent-extensions/dist/onboarding.d.ts.map +0 -1
- package/node_modules/@senpiper/sp-agent-extensions/dist/onboarding.js +0 -311
- package/node_modules/@senpiper/sp-agent-extensions/dist/onboarding.js.map +0 -1
|
@@ -162,7 +162,7 @@ Successful responses return `{ "accepted": true }`. The next `question_pending`
|
|
|
162
162
|
|
|
163
163
|
Questions stay pending across web disconnects. Read a snapshot on reconnect and subscribe after its sequence to close the snapshot/subscription race. Process events in sequence order; only clear a displayed question when a resolved event matches its interaction ID. Abort, session shutdown, reload, and generation replacement cancel the questionnaire rather than restoring it. No question is reconstructed from JSONL, and no second session is created.
|
|
164
164
|
|
|
165
|
-
This protocol covers `ask_user_question`, not arbitrary extension UI prompts such as login
|
|
165
|
+
This protocol covers `ask_user_question`, not arbitrary extension UI prompts such as login. Oversized question events follow the existing frame limit; terminal dialogs remain available, but the web snapshot can return `frame_too_large`. The bridge does not split questions into multiple frames.
|
|
166
166
|
|
|
167
167
|
## SP Web sidecar
|
|
168
168
|
|
|
@@ -4,12 +4,6 @@ interface LoginOptions {
|
|
|
4
4
|
help?: boolean;
|
|
5
5
|
}
|
|
6
6
|
export declare function parseLoginOptions(args: string[]): LoginOptions;
|
|
7
|
-
export declare function runCliOnboarding(): Promise<{
|
|
8
|
-
completed: boolean;
|
|
9
|
-
installedSkillIds: string[];
|
|
10
|
-
failedSkillIds: string[];
|
|
11
|
-
results: import("./onboarding.ts").SkillInstallResult[];
|
|
12
|
-
}>;
|
|
13
7
|
export declare function handleSpCommand(args: string[]): Promise<boolean>;
|
|
14
8
|
export {};
|
|
15
9
|
//# sourceMappingURL=cli-commands.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli-commands.d.ts","sourceRoot":"","sources":["../src/cli-commands.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cli-commands.d.ts","sourceRoot":"","sources":["../src/cli-commands.ts"],"names":[],"mappings":"AAUA,UAAU,YAAY;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;CACf;AAWD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,gBAqB/C;AAkBD,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,oBAkCnD","sourcesContent":["import { stdin as input, stdout as output } from \"node:process\";\nimport { createInterface } from \"node:readline/promises\";\nimport {\n\tfetchSpGatewayModels,\n\tnormalizeSpGatewayBaseUrl,\n\tremoveSpGatewayLogin,\n\twriteSpGatewayLogin,\n\twriteSpGatewayState,\n} from \"./gateway.ts\";\n\ninterface LoginOptions {\n\tgatewayUrl?: string;\n\tapiKey?: string;\n\thelp?: boolean;\n}\n\nfunction printHelp() {\n\tconsole.log(`sp-agent gateway setup\n\nUsage:\n sp login [--gateway-url <url>] [--api-key <key>]\n sp logout\n`);\n}\n\nexport function parseLoginOptions(args: string[]) {\n\tconst options: LoginOptions = {};\n\tfor (let index = 0; index < args.length; index++) {\n\t\tconst argument = args[index];\n\t\tif (argument === \"-h\" || argument === \"--help\") {\n\t\t\toptions.help = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (argument === \"--gateway-url\") {\n\t\t\toptions.gatewayUrl = args[++index];\n\t\t\tif (!options.gatewayUrl) throw new Error(\"--gateway-url requires a value\");\n\t\t\tcontinue;\n\t\t}\n\t\tif (argument === \"--api-key\") {\n\t\t\toptions.apiKey = args[++index];\n\t\t\tif (!options.apiKey) throw new Error(\"--api-key requires a value\");\n\t\t\tcontinue;\n\t\t}\n\t\tthrow new Error(`Unknown login option: ${argument}`);\n\t}\n\treturn options;\n}\n\nasync function promptForLogin(options: LoginOptions) {\n\tif (options.gatewayUrl && options.apiKey) return { gatewayUrl: options.gatewayUrl, apiKey: options.apiKey };\n\tif (!input.isTTY || !output.isTTY) {\n\t\tthrow new Error(\"Missing gateway credentials. Pass --gateway-url and --api-key in non-interactive mode.\");\n\t}\n\tconst prompt = createInterface({ input, output });\n\ttry {\n\t\treturn {\n\t\t\tgatewayUrl: options.gatewayUrl ?? (await prompt.question(\"sp-gateway URL: \")),\n\t\t\tapiKey: options.apiKey ?? (await prompt.question(\"sp-gateway API key: \")),\n\t\t};\n\t} finally {\n\t\tprompt.close();\n\t}\n}\n\nexport async function handleSpCommand(args: string[]) {\n\tconst [command, ...rest] = args;\n\tif (command !== \"login\" && command !== \"logout\") return false;\n\ttry {\n\t\tif (command === \"logout\") {\n\t\t\tif (rest.some((argument) => argument === \"-h\" || argument === \"--help\")) {\n\t\t\t\tprintHelp();\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (rest.length > 0) throw new Error(`Unknown logout option: ${rest[0]}`);\n\t\t\tremoveSpGatewayLogin();\n\t\t\tconsole.log(\"Removed stored sp-gateway API key.\");\n\t\t\treturn true;\n\t\t}\n\n\t\tconst options = parseLoginOptions(rest);\n\t\tif (options.help) {\n\t\t\tprintHelp();\n\t\t\treturn true;\n\t\t}\n\t\tconst { gatewayUrl, apiKey } = await promptForLogin(options);\n\t\tconst baseUrl = normalizeSpGatewayBaseUrl(gatewayUrl);\n\t\tconst models = await fetchSpGatewayModels(baseUrl, apiKey);\n\t\twriteSpGatewayState(baseUrl, models);\n\t\twriteSpGatewayLogin(apiKey, models[0]!.id);\n\t\tconsole.log(`Connected to sp-gateway at ${baseUrl}`);\n\t\tconsole.log(`Synced ${models.length} model${models.length === 1 ? \"\" : \"s\"}.`);\n\t\tconsole.log(`Selected model: ${models[0]!.id}`);\n\t\treturn true;\n\t} catch (error) {\n\t\tconsole.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n\t\tprocess.exitCode = 1;\n\t\treturn true;\n\t}\n}\n"]}
|
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
import { stdin as input, stdout as output } from "node:process";
|
|
2
2
|
import { createInterface } from "node:readline/promises";
|
|
3
|
-
import { fetchSpGatewayModels,
|
|
4
|
-
import { fetchSkillCatalog, hasCompletedOnboarding, selectCatalogSkills } from "./onboarding.js";
|
|
5
|
-
import { installOnboardingSelection } from "./onboarding-installation.js";
|
|
3
|
+
import { fetchSpGatewayModels, normalizeSpGatewayBaseUrl, removeSpGatewayLogin, writeSpGatewayLogin, writeSpGatewayState, } from "./gateway.js";
|
|
6
4
|
function printHelp() {
|
|
7
5
|
console.log(`sp-agent gateway setup
|
|
8
6
|
|
|
9
7
|
Usage:
|
|
10
8
|
sp login [--gateway-url <url>] [--api-key <key>]
|
|
11
9
|
sp logout
|
|
12
|
-
sp onboarding
|
|
13
10
|
`);
|
|
14
11
|
}
|
|
15
12
|
export function parseLoginOptions(args) {
|
|
@@ -53,75 +50,11 @@ async function promptForLogin(options) {
|
|
|
53
50
|
prompt.close();
|
|
54
51
|
}
|
|
55
52
|
}
|
|
56
|
-
async function promptYesNo(prompt, question) {
|
|
57
|
-
while (true) {
|
|
58
|
-
const answer = (await prompt.question(`${question} [Y/n] `)).trim().toLowerCase();
|
|
59
|
-
if (!answer || answer === "y" || answer === "yes")
|
|
60
|
-
return true;
|
|
61
|
-
if (answer === "n" || answer === "no")
|
|
62
|
-
return false;
|
|
63
|
-
console.log("Enter y or n.");
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
async function promptRoles(prompt, catalog) {
|
|
67
|
-
console.log("\nDeveloper roles");
|
|
68
|
-
for (const [index, role] of catalog.roles.entries())
|
|
69
|
-
console.log(` ${index + 1}. ${role.title}`);
|
|
70
|
-
while (true) {
|
|
71
|
-
const answer = (await prompt.question("Select role numbers separated by commas, or Enter for none: ")).trim();
|
|
72
|
-
if (!answer)
|
|
73
|
-
return [];
|
|
74
|
-
const indexes = answer.split(",").map((value) => Number(value.trim()) - 1);
|
|
75
|
-
if (indexes.every((index) => Number.isInteger(index) && index >= 0 && index < catalog.roles.length)) {
|
|
76
|
-
return [...new Set(indexes.map((index) => catalog.roles[index].id))];
|
|
77
|
-
}
|
|
78
|
-
console.log("Enter only the listed role numbers.");
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
export async function runCliOnboarding() {
|
|
82
|
-
if (process.env.PI_OFFLINE || process.env.SP_OFFLINE)
|
|
83
|
-
throw new Error("Onboarding requires network access.");
|
|
84
|
-
const state = readSpGatewayState();
|
|
85
|
-
const apiKey = readSpGatewayApiKey();
|
|
86
|
-
if (!state || !apiKey)
|
|
87
|
-
throw new Error("sp-gateway is not configured. Run `sp login` first.");
|
|
88
|
-
const catalog = await fetchSkillCatalog(state.baseUrl, apiKey);
|
|
89
|
-
const prompt = createInterface({ input, output });
|
|
90
|
-
let roles;
|
|
91
|
-
let includeDefaults;
|
|
92
|
-
let includeRoleSkills;
|
|
93
|
-
try {
|
|
94
|
-
roles = await promptRoles(prompt, catalog);
|
|
95
|
-
includeDefaults = await promptYesNo(prompt, "Install the default workflow skills?");
|
|
96
|
-
includeRoleSkills = roles.length > 0 ? await promptYesNo(prompt, "Install role-recommended skills?") : false;
|
|
97
|
-
}
|
|
98
|
-
finally {
|
|
99
|
-
prompt.close();
|
|
100
|
-
}
|
|
101
|
-
const skills = selectCatalogSkills(catalog, roles, includeDefaults, includeRoleSkills);
|
|
102
|
-
console.log(`Installing ${skills.length} selected skill${skills.length === 1 ? "" : "s"}…`);
|
|
103
|
-
const installation = await installOnboardingSelection(getSpAgentDir(), roles, skills);
|
|
104
|
-
console.log(`Installed: ${installation.installedSkillIds.join(", ") || "none"}.`);
|
|
105
|
-
console.log(`Failed: ${installation.failedSkillIds.join(", ") || "none"}.`);
|
|
106
|
-
return installation;
|
|
107
|
-
}
|
|
108
53
|
export async function handleSpCommand(args) {
|
|
109
54
|
const [command, ...rest] = args;
|
|
110
|
-
if (command !== "login" && command !== "logout"
|
|
55
|
+
if (command !== "login" && command !== "logout")
|
|
111
56
|
return false;
|
|
112
57
|
try {
|
|
113
|
-
if (command === "onboarding") {
|
|
114
|
-
if (rest.some((argument) => argument === "-h" || argument === "--help")) {
|
|
115
|
-
printHelp();
|
|
116
|
-
return true;
|
|
117
|
-
}
|
|
118
|
-
if (rest.length > 0)
|
|
119
|
-
throw new Error(`Unknown onboarding option: ${rest[0]}`);
|
|
120
|
-
if (!input.isTTY || !output.isTTY)
|
|
121
|
-
throw new Error("Onboarding requires an interactive terminal.");
|
|
122
|
-
await runCliOnboarding();
|
|
123
|
-
return true;
|
|
124
|
-
}
|
|
125
58
|
if (command === "logout") {
|
|
126
59
|
if (rest.some((argument) => argument === "-h" || argument === "--help")) {
|
|
127
60
|
printHelp();
|
|
@@ -146,18 +79,6 @@ export async function handleSpCommand(args) {
|
|
|
146
79
|
console.log(`Connected to sp-gateway at ${baseUrl}`);
|
|
147
80
|
console.log(`Synced ${models.length} model${models.length === 1 ? "" : "s"}.`);
|
|
148
81
|
console.log(`Selected model: ${models[0].id}`);
|
|
149
|
-
if (!hasCompletedOnboarding(getSpAgentDir()) && input.isTTY && output.isTTY) {
|
|
150
|
-
try {
|
|
151
|
-
await runCliOnboarding();
|
|
152
|
-
}
|
|
153
|
-
catch (error) {
|
|
154
|
-
console.error(`Onboarding deferred: ${error instanceof Error ? error.message : String(error)}`);
|
|
155
|
-
console.log("Run `sp onboarding` later.");
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
else if (!hasCompletedOnboarding(getSpAgentDir())) {
|
|
159
|
-
console.log("Run `sp onboarding` from an interactive terminal to install curated skills.");
|
|
160
|
-
}
|
|
161
82
|
return true;
|
|
162
83
|
}
|
|
163
84
|
catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli-commands.js","sourceRoot":"","sources":["../src/cli-commands.ts"],"names":[],"mappings":"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","sourcesContent":["import { stdin as input, stdout as output } from \"node:process\";\nimport { createInterface, type Interface } from \"node:readline/promises\";\nimport {\n\tfetchSpGatewayModels,\n\tgetSpAgentDir,\n\tnormalizeSpGatewayBaseUrl,\n\treadSpGatewayApiKey,\n\treadSpGatewayState,\n\tremoveSpGatewayLogin,\n\twriteSpGatewayLogin,\n\twriteSpGatewayState,\n} from \"./gateway.ts\";\nimport { fetchSkillCatalog, hasCompletedOnboarding, type SkillCatalog, selectCatalogSkills } from \"./onboarding.ts\";\nimport { installOnboardingSelection } from \"./onboarding-installation.ts\";\n\ninterface LoginOptions {\n\tgatewayUrl?: string;\n\tapiKey?: string;\n\thelp?: boolean;\n}\n\nfunction printHelp() {\n\tconsole.log(`sp-agent gateway setup\n\nUsage:\n sp login [--gateway-url <url>] [--api-key <key>]\n sp logout\n sp onboarding\n`);\n}\n\nexport function parseLoginOptions(args: string[]) {\n\tconst options: LoginOptions = {};\n\tfor (let index = 0; index < args.length; index++) {\n\t\tconst argument = args[index];\n\t\tif (argument === \"-h\" || argument === \"--help\") {\n\t\t\toptions.help = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (argument === \"--gateway-url\") {\n\t\t\toptions.gatewayUrl = args[++index];\n\t\t\tif (!options.gatewayUrl) throw new Error(\"--gateway-url requires a value\");\n\t\t\tcontinue;\n\t\t}\n\t\tif (argument === \"--api-key\") {\n\t\t\toptions.apiKey = args[++index];\n\t\t\tif (!options.apiKey) throw new Error(\"--api-key requires a value\");\n\t\t\tcontinue;\n\t\t}\n\t\tthrow new Error(`Unknown login option: ${argument}`);\n\t}\n\treturn options;\n}\n\nasync function promptForLogin(options: LoginOptions) {\n\tif (options.gatewayUrl && options.apiKey) return { gatewayUrl: options.gatewayUrl, apiKey: options.apiKey };\n\tif (!input.isTTY || !output.isTTY) {\n\t\tthrow new Error(\"Missing gateway credentials. Pass --gateway-url and --api-key in non-interactive mode.\");\n\t}\n\tconst prompt = createInterface({ input, output });\n\ttry {\n\t\treturn {\n\t\t\tgatewayUrl: options.gatewayUrl ?? (await prompt.question(\"sp-gateway URL: \")),\n\t\t\tapiKey: options.apiKey ?? (await prompt.question(\"sp-gateway API key: \")),\n\t\t};\n\t} finally {\n\t\tprompt.close();\n\t}\n}\n\nasync function promptYesNo(prompt: Interface, question: string) {\n\twhile (true) {\n\t\tconst answer = (await prompt.question(`${question} [Y/n] `)).trim().toLowerCase();\n\t\tif (!answer || answer === \"y\" || answer === \"yes\") return true;\n\t\tif (answer === \"n\" || answer === \"no\") return false;\n\t\tconsole.log(\"Enter y or n.\");\n\t}\n}\n\nasync function promptRoles(prompt: Interface, catalog: SkillCatalog) {\n\tconsole.log(\"\\nDeveloper roles\");\n\tfor (const [index, role] of catalog.roles.entries()) console.log(` ${index + 1}. ${role.title}`);\n\twhile (true) {\n\t\tconst answer = (await prompt.question(\"Select role numbers separated by commas, or Enter for none: \")).trim();\n\t\tif (!answer) return [];\n\t\tconst indexes = answer.split(\",\").map((value) => Number(value.trim()) - 1);\n\t\tif (indexes.every((index) => Number.isInteger(index) && index >= 0 && index < catalog.roles.length)) {\n\t\t\treturn [...new Set(indexes.map((index) => catalog.roles[index]!.id))];\n\t\t}\n\t\tconsole.log(\"Enter only the listed role numbers.\");\n\t}\n}\n\nexport async function runCliOnboarding() {\n\tif (process.env.PI_OFFLINE || process.env.SP_OFFLINE) throw new Error(\"Onboarding requires network access.\");\n\tconst state = readSpGatewayState();\n\tconst apiKey = readSpGatewayApiKey();\n\tif (!state || !apiKey) throw new Error(\"sp-gateway is not configured. Run `sp login` first.\");\n\tconst catalog = await fetchSkillCatalog(state.baseUrl, apiKey);\n\tconst prompt = createInterface({ input, output });\n\tlet roles: string[];\n\tlet includeDefaults: boolean;\n\tlet includeRoleSkills: boolean;\n\ttry {\n\t\troles = await promptRoles(prompt, catalog);\n\t\tincludeDefaults = await promptYesNo(prompt, \"Install the default workflow skills?\");\n\t\tincludeRoleSkills = roles.length > 0 ? await promptYesNo(prompt, \"Install role-recommended skills?\") : false;\n\t} finally {\n\t\tprompt.close();\n\t}\n\tconst skills = selectCatalogSkills(catalog, roles, includeDefaults, includeRoleSkills);\n\tconsole.log(`Installing ${skills.length} selected skill${skills.length === 1 ? \"\" : \"s\"}…`);\n\tconst installation = await installOnboardingSelection(getSpAgentDir(), roles, skills);\n\tconsole.log(`Installed: ${installation.installedSkillIds.join(\", \") || \"none\"}.`);\n\tconsole.log(`Failed: ${installation.failedSkillIds.join(\", \") || \"none\"}.`);\n\treturn installation;\n}\n\nexport async function handleSpCommand(args: string[]) {\n\tconst [command, ...rest] = args;\n\tif (command !== \"login\" && command !== \"logout\" && command !== \"onboarding\") return false;\n\ttry {\n\t\tif (command === \"onboarding\") {\n\t\t\tif (rest.some((argument) => argument === \"-h\" || argument === \"--help\")) {\n\t\t\t\tprintHelp();\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (rest.length > 0) throw new Error(`Unknown onboarding option: ${rest[0]}`);\n\t\t\tif (!input.isTTY || !output.isTTY) throw new Error(\"Onboarding requires an interactive terminal.\");\n\t\t\tawait runCliOnboarding();\n\t\t\treturn true;\n\t\t}\n\t\tif (command === \"logout\") {\n\t\t\tif (rest.some((argument) => argument === \"-h\" || argument === \"--help\")) {\n\t\t\t\tprintHelp();\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (rest.length > 0) throw new Error(`Unknown logout option: ${rest[0]}`);\n\t\t\tremoveSpGatewayLogin();\n\t\t\tconsole.log(\"Removed stored sp-gateway API key.\");\n\t\t\treturn true;\n\t\t}\n\n\t\tconst options = parseLoginOptions(rest);\n\t\tif (options.help) {\n\t\t\tprintHelp();\n\t\t\treturn true;\n\t\t}\n\t\tconst { gatewayUrl, apiKey } = await promptForLogin(options);\n\t\tconst baseUrl = normalizeSpGatewayBaseUrl(gatewayUrl);\n\t\tconst models = await fetchSpGatewayModels(baseUrl, apiKey);\n\t\twriteSpGatewayState(baseUrl, models);\n\t\twriteSpGatewayLogin(apiKey, models[0]!.id);\n\t\tconsole.log(`Connected to sp-gateway at ${baseUrl}`);\n\t\tconsole.log(`Synced ${models.length} model${models.length === 1 ? \"\" : \"s\"}.`);\n\t\tconsole.log(`Selected model: ${models[0]!.id}`);\n\t\tif (!hasCompletedOnboarding(getSpAgentDir()) && input.isTTY && output.isTTY) {\n\t\t\ttry {\n\t\t\t\tawait runCliOnboarding();\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(`Onboarding deferred: ${error instanceof Error ? error.message : String(error)}`);\n\t\t\t\tconsole.log(\"Run `sp onboarding` later.\");\n\t\t\t}\n\t\t} else if (!hasCompletedOnboarding(getSpAgentDir())) {\n\t\t\tconsole.log(\"Run `sp onboarding` from an interactive terminal to install curated skills.\");\n\t\t}\n\t\treturn true;\n\t} catch (error) {\n\t\tconsole.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n\t\tprocess.exitCode = 1;\n\t\treturn true;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"cli-commands.js","sourceRoot":"","sources":["../src/cli-commands.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,KAAK,EAAE,MAAM,IAAI,MAAM,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EACN,oBAAoB,EACpB,yBAAyB,EACzB,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,GACnB,MAAM,cAAc,CAAC;AAQtB,SAAS,SAAS,GAAG;IACpB,OAAO,CAAC,GAAG,CAAC;;;;;CAKZ,CAAC,CAAC;AAAA,CACF;AAED,MAAM,UAAU,iBAAiB,CAAC,IAAc,EAAE;IACjD,MAAM,OAAO,GAAiB,EAAE,CAAC;IACjC,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;QAClD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;QAC7B,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAChD,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;YACpB,SAAS;QACV,CAAC;QACD,IAAI,QAAQ,KAAK,eAAe,EAAE,CAAC;YAClC,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC;YACnC,IAAI,CAAC,OAAO,CAAC,UAAU;gBAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;YAC3E,SAAS;QACV,CAAC;QACD,IAAI,QAAQ,KAAK,WAAW,EAAE,CAAC;YAC9B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC;YAC/B,IAAI,CAAC,OAAO,CAAC,MAAM;gBAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;YACnE,SAAS;QACV,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,yBAAyB,QAAQ,EAAE,CAAC,CAAC;IACtD,CAAC;IACD,OAAO,OAAO,CAAC;AAAA,CACf;AAED,KAAK,UAAU,cAAc,CAAC,OAAqB,EAAE;IACpD,IAAI,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC,MAAM;QAAE,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;IAC5G,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CAAC,wFAAwF,CAAC,CAAC;IAC3G,CAAC;IACD,MAAM,MAAM,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;IAClD,IAAI,CAAC;QACJ,OAAO;YACN,UAAU,EAAE,OAAO,CAAC,UAAU,IAAI,CAAC,MAAM,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC;YAC7E,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,CAAC,MAAM,MAAM,CAAC,QAAQ,CAAC,sBAAsB,CAAC,CAAC;SACzE,CAAC;IACH,CAAC;YAAS,CAAC;QACV,MAAM,CAAC,KAAK,EAAE,CAAC;IAChB,CAAC;AAAA,CACD;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAc,EAAE;IACrD,MAAM,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IAChC,IAAI,OAAO,KAAK,OAAO,IAAI,OAAO,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC9D,IAAI,CAAC;QACJ,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;YAC1B,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,KAAK,IAAI,IAAI,QAAQ,KAAK,QAAQ,CAAC,EAAE,CAAC;gBACzE,SAAS,EAAE,CAAC;gBACZ,OAAO,IAAI,CAAC;YACb,CAAC;YACD,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAC1E,oBAAoB,EAAE,CAAC;YACvB,OAAO,CAAC,GAAG,CAAC,oCAAoC,CAAC,CAAC;YAClD,OAAO,IAAI,CAAC;QACb,CAAC;QAED,MAAM,OAAO,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;YAClB,SAAS,EAAE,CAAC;YACZ,OAAO,IAAI,CAAC;QACb,CAAC;QACD,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC,CAAC;QAC7D,MAAM,OAAO,GAAG,yBAAyB,CAAC,UAAU,CAAC,CAAC;QACtD,MAAM,MAAM,GAAG,MAAM,oBAAoB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC3D,mBAAmB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACrC,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAE,CAAC,EAAE,CAAC,CAAC;QAC3C,OAAO,CAAC,GAAG,CAAC,8BAA8B,OAAO,EAAE,CAAC,CAAC;QACrD,OAAO,CAAC,GAAG,CAAC,UAAU,MAAM,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC;QAC/E,OAAO,CAAC,GAAG,CAAC,mBAAmB,MAAM,CAAC,CAAC,CAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QAChD,OAAO,IAAI,CAAC;IACb,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,OAAO,CAAC,KAAK,CAAC,UAAU,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAClF,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC;IACb,CAAC;AAAA,CACD","sourcesContent":["import { stdin as input, stdout as output } from \"node:process\";\nimport { createInterface } from \"node:readline/promises\";\nimport {\n\tfetchSpGatewayModels,\n\tnormalizeSpGatewayBaseUrl,\n\tremoveSpGatewayLogin,\n\twriteSpGatewayLogin,\n\twriteSpGatewayState,\n} from \"./gateway.ts\";\n\ninterface LoginOptions {\n\tgatewayUrl?: string;\n\tapiKey?: string;\n\thelp?: boolean;\n}\n\nfunction printHelp() {\n\tconsole.log(`sp-agent gateway setup\n\nUsage:\n sp login [--gateway-url <url>] [--api-key <key>]\n sp logout\n`);\n}\n\nexport function parseLoginOptions(args: string[]) {\n\tconst options: LoginOptions = {};\n\tfor (let index = 0; index < args.length; index++) {\n\t\tconst argument = args[index];\n\t\tif (argument === \"-h\" || argument === \"--help\") {\n\t\t\toptions.help = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (argument === \"--gateway-url\") {\n\t\t\toptions.gatewayUrl = args[++index];\n\t\t\tif (!options.gatewayUrl) throw new Error(\"--gateway-url requires a value\");\n\t\t\tcontinue;\n\t\t}\n\t\tif (argument === \"--api-key\") {\n\t\t\toptions.apiKey = args[++index];\n\t\t\tif (!options.apiKey) throw new Error(\"--api-key requires a value\");\n\t\t\tcontinue;\n\t\t}\n\t\tthrow new Error(`Unknown login option: ${argument}`);\n\t}\n\treturn options;\n}\n\nasync function promptForLogin(options: LoginOptions) {\n\tif (options.gatewayUrl && options.apiKey) return { gatewayUrl: options.gatewayUrl, apiKey: options.apiKey };\n\tif (!input.isTTY || !output.isTTY) {\n\t\tthrow new Error(\"Missing gateway credentials. Pass --gateway-url and --api-key in non-interactive mode.\");\n\t}\n\tconst prompt = createInterface({ input, output });\n\ttry {\n\t\treturn {\n\t\t\tgatewayUrl: options.gatewayUrl ?? (await prompt.question(\"sp-gateway URL: \")),\n\t\t\tapiKey: options.apiKey ?? (await prompt.question(\"sp-gateway API key: \")),\n\t\t};\n\t} finally {\n\t\tprompt.close();\n\t}\n}\n\nexport async function handleSpCommand(args: string[]) {\n\tconst [command, ...rest] = args;\n\tif (command !== \"login\" && command !== \"logout\") return false;\n\ttry {\n\t\tif (command === \"logout\") {\n\t\t\tif (rest.some((argument) => argument === \"-h\" || argument === \"--help\")) {\n\t\t\t\tprintHelp();\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (rest.length > 0) throw new Error(`Unknown logout option: ${rest[0]}`);\n\t\t\tremoveSpGatewayLogin();\n\t\t\tconsole.log(\"Removed stored sp-gateway API key.\");\n\t\t\treturn true;\n\t\t}\n\n\t\tconst options = parseLoginOptions(rest);\n\t\tif (options.help) {\n\t\t\tprintHelp();\n\t\t\treturn true;\n\t\t}\n\t\tconst { gatewayUrl, apiKey } = await promptForLogin(options);\n\t\tconst baseUrl = normalizeSpGatewayBaseUrl(gatewayUrl);\n\t\tconst models = await fetchSpGatewayModels(baseUrl, apiKey);\n\t\twriteSpGatewayState(baseUrl, models);\n\t\twriteSpGatewayLogin(apiKey, models[0]!.id);\n\t\tconsole.log(`Connected to sp-gateway at ${baseUrl}`);\n\t\tconsole.log(`Synced ${models.length} model${models.length === 1 ? \"\" : \"s\"}.`);\n\t\tconsole.log(`Selected model: ${models[0]!.id}`);\n\t\treturn true;\n\t} catch (error) {\n\t\tconsole.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n\t\tprocess.exitCode = 1;\n\t\treturn true;\n\t}\n}\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@senpiper/pi-coding-agent";
|
|
2
|
-
import { handleSpCommand
|
|
2
|
+
import { handleSpCommand } from "./cli-commands.ts";
|
|
3
3
|
export type SpCommandExtensionApi = ExtensionAPI;
|
|
4
|
-
export { handleSpCommand
|
|
4
|
+
export { handleSpCommand };
|
|
5
5
|
export declare function registerSpCommands(pi: ExtensionAPI): void;
|
|
6
6
|
export declare function getSpGatewayStatus(): import("./gateway.ts").SpGatewayState | undefined;
|
|
7
7
|
//# sourceMappingURL=commands.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,
|
|
1
|
+
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAIpD,MAAM,MAAM,qBAAqB,GAAG,YAAY,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,CAAC;AAE3B,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,YAAY,QAElD;AAED,wBAAgB,kBAAkB,sDAEjC","sourcesContent":["import type { ExtensionAPI } from \"@senpiper/pi-coding-agent\";\nimport { handleSpCommand } from \"./cli-commands.ts\";\nimport { readSpGatewayState } from \"./gateway.ts\";\nimport { registerLoginCommand } from \"./login-command.ts\";\n\nexport type SpCommandExtensionApi = ExtensionAPI;\nexport { handleSpCommand };\n\nexport function registerSpCommands(pi: ExtensionAPI) {\n\tregisterLoginCommand(pi);\n}\n\nexport function getSpGatewayStatus() {\n\treturn readSpGatewayState();\n}\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { handleSpCommand
|
|
1
|
+
import { handleSpCommand } from "./cli-commands.js";
|
|
2
2
|
import { readSpGatewayState } from "./gateway.js";
|
|
3
3
|
import { registerLoginCommand } from "./login-command.js";
|
|
4
|
-
export { handleSpCommand
|
|
4
|
+
export { handleSpCommand };
|
|
5
5
|
export function registerSpCommands(pi) {
|
|
6
6
|
registerLoginCommand(pi);
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commands.js","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,
|
|
1
|
+
{"version":3,"file":"commands.js","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAG1D,OAAO,EAAE,eAAe,EAAE,CAAC;AAE3B,MAAM,UAAU,kBAAkB,CAAC,EAAgB,EAAE;IACpD,oBAAoB,CAAC,EAAE,CAAC,CAAC;AAAA,CACzB;AAED,MAAM,UAAU,kBAAkB,GAAG;IACpC,OAAO,kBAAkB,EAAE,CAAC;AAAA,CAC5B","sourcesContent":["import type { ExtensionAPI } from \"@senpiper/pi-coding-agent\";\nimport { handleSpCommand } from \"./cli-commands.ts\";\nimport { readSpGatewayState } from \"./gateway.ts\";\nimport { registerLoginCommand } from \"./login-command.ts\";\n\nexport type SpCommandExtensionApi = ExtensionAPI;\nexport { handleSpCommand };\n\nexport function registerSpCommands(pi: ExtensionAPI) {\n\tregisterLoginCommand(pi);\n}\n\nexport function getSpGatewayStatus() {\n\treturn readSpGatewayState();\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gateway.d.ts","sourceRoot":"","sources":["../src/gateway.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAGrH,MAAM,MAAM,cAAc,GAAG,YAAY,CAAC;AAC1C,MAAM,MAAM,qBAAqB,GAAG,mBAAmB,CAAC;AAExD,eAAO,MAAM,sBAAsB,eAAe,CAAC;AACnD,eAAO,MAAM,6BAA6B,eAAe,CAAC;AAC1D,eAAO,MAAM,wBAAwB,eAAe,CAAC;AACrD,eAAO,MAAM,aAAa,OAAO,CAAC;AAClC,eAAO,MAAM,cAAc,aAAa,CAAC;AAEzC,MAAM,WAAW,cAAc;IAC9B,aAAa,EAAE,CAAC,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,qBAAqB,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE,gBAAgB,GAAG,uBAAuB,CAAC;IACjD,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;CACX;AAgCD,wBAAgB,aAAa,WAE5B;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,SAAkB,UAE/D;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,SAAkB,UAE7D;AAED,wBAAgB,aAAa,CAAC,QAAQ,SAAkB,UAEvD;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,SAAkB,UAE3D;AAoBD,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,UAkBtD;AA4GD,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,kCAoB/F;AAiBD,wBAAgB,kBAAkB,CAAC,QAAQ,SAAkB,GAAG,cAAc,GAAG,SAAS,CAsBzF;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,qBAAqB,EAAE,EAAE,QAAQ,SAAkB,QAO/G;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,SAAkB,sBAa7D;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,QAAQ,SAAkB,QAYnG;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,SAAkB,QAM9D;AAeD,wBAAgB,mBAAmB,iCAElC;AAED,wBAAgB,qBAAqB,SAEpC;AAED,wBAAgB,6BAA6B,CAC5C,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,qBAAqB,EAAE,EAC/B,QAAQ,SAAkB,GACxB,cAAc,CAsChB;AAED,wBAAgB,wBAAwB,IAAI,gBAAgB,CAyB3D","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\tsystemPrompt: `${event.systemPrompt}\\n\\n<sp_agent_runtime>\\nThis is Senpiper's sp-agent distribution. Use 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}));\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.d.ts","sourceRoot":"","sources":["../src/gateway.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAGrH,MAAM,MAAM,cAAc,GAAG,YAAY,CAAC;AAC1C,MAAM,MAAM,qBAAqB,GAAG,mBAAmB,CAAC;AAExD,eAAO,MAAM,sBAAsB,eAAe,CAAC;AACnD,eAAO,MAAM,6BAA6B,eAAe,CAAC;AAC1D,eAAO,MAAM,wBAAwB,eAAe,CAAC;AACrD,eAAO,MAAM,aAAa,OAAO,CAAC;AAClC,eAAO,MAAM,cAAc,aAAa,CAAC;AAEzC,MAAM,WAAW,cAAc;IAC9B,aAAa,EAAE,CAAC,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,qBAAqB,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE,gBAAgB,GAAG,uBAAuB,CAAC;IACjD,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;CACX;AAgCD,wBAAgB,aAAa,WAE5B;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,SAAkB,UAE/D;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,SAAkB,UAE7D;AAED,wBAAgB,aAAa,CAAC,QAAQ,SAAkB,UAEvD;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,SAAkB,UAE3D;AAoBD,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,UAkBtD;AA4GD,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,kCAoB/F;AAiBD,wBAAgB,kBAAkB,CAAC,QAAQ,SAAkB,GAAG,cAAc,GAAG,SAAS,CAsBzF;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,qBAAqB,EAAE,EAAE,QAAQ,SAAkB,QAO/G;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,SAAkB,sBAa7D;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,QAAQ,SAAkB,QAYnG;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,SAAkB,QAM9D;AAeD,wBAAgB,mBAAmB,iCAElC;AAED,wBAAgB,qBAAqB,SAEpC;AAED,wBAAgB,6BAA6B,CAC5C,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,qBAAqB,EAAE,EAC/B,QAAQ,SAAkB,GACxB,cAAc,CAsChB;AAED,wBAAgB,wBAAwB,IAAI,gBAAgB,CAuC3D","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"]}
|
|
@@ -358,9 +358,20 @@ export function createSpGatewayExtension() {
|
|
|
358
358
|
if (state) {
|
|
359
359
|
pi.registerProvider(SP_GATEWAY_PROVIDER_ID, createSpGatewayProviderConfig(state.baseUrl, state.models));
|
|
360
360
|
}
|
|
361
|
-
pi.on("before_agent_start", (event) =>
|
|
362
|
-
|
|
363
|
-
|
|
361
|
+
pi.on("before_agent_start", (event) => {
|
|
362
|
+
// Replace only the upstream introduction; preserve project instructions
|
|
363
|
+
// and documentation references, including legitimate Pi development tasks.
|
|
364
|
+
const systemPrompt = event.systemPrompt.replace(/^You are an expert coding assistant operating inside pi, a coding agent harness\./u, "You are SP, Senpiper's AI coding assistant.");
|
|
365
|
+
return {
|
|
366
|
+
systemPrompt: `${systemPrompt}\n\n<sp_agent_runtime>
|
|
367
|
+
You are SP, Senpiper's AI coding assistant. Use SP as your public name in both terminal and web conversations.
|
|
368
|
+
For 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."
|
|
369
|
+
Do 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.
|
|
370
|
+
If explicitly asked about technical origins or dependencies, answer accurately and briefly. This product identity does not imply that Senpiper trained the underlying model.
|
|
371
|
+
Use 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.
|
|
372
|
+
</sp_agent_runtime>`,
|
|
373
|
+
};
|
|
374
|
+
});
|
|
364
375
|
pi.on("session_start", (_event, ctx) => {
|
|
365
376
|
ctx.ui.setTitle("sp-agent");
|
|
366
377
|
const warning = getSpGatewayWarning();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gateway.js","sourceRoot":"","sources":["../src/gateway.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACpG,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAO1C,MAAM,CAAC,MAAM,sBAAsB,GAAG,YAAY,CAAC;AACnD,MAAM,CAAC,MAAM,6BAA6B,GAAG,YAAY,CAAC;AAC1D,MAAM,CAAC,MAAM,wBAAwB,GAAG,YAAY,CAAC;AACrD,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,CAAC;AAClC,MAAM,CAAC,MAAM,cAAc,GAAG,UAAU,CAAC;AAgBzC,IAAI,cAA4C,CAAC;AAEjD,SAAS,QAAQ,CAAC,KAAc,EAAuB;IACtD,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAAA,CAC5E;AAED,SAAS,WAAW,CAAC,KAAc,EAAE;IACpC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AAAA,CAC5E;AAED,SAAS,YAAY,CAAC,KAAc,EAAE;IACrC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AAAA,CAC/E;AAED,SAAS,eAAe,CAAC,KAAc,EAAE;IACxC,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IACnC,OAAO,MAAM,KAAK,SAAS,IAAI,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAAA,CAC3E;AAED,SAAS,iBAAiB,CAAC,KAAc,EAAE;IAC1C,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IACnC,OAAO,MAAM,KAAK,SAAS,IAAI,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;AAAA,CAChE;AAED,SAAS,YAAY,CAAC,KAAc,EAAE;IACrC,OAAO,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AAAA,CACtD;AAED,MAAM,UAAU,aAAa,GAAG;IAC/B,OAAO,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;AAAA,CAC1E;AAED,MAAM,UAAU,qBAAqB,CAAC,QAAQ,GAAG,aAAa,EAAE,EAAE;IACjE,OAAO,IAAI,CAAC,QAAQ,EAAE,iBAAiB,CAAC,CAAC;AAAA,CACzC;AAED,MAAM,UAAU,mBAAmB,CAAC,QAAQ,GAAG,aAAa,EAAE,EAAE;IAC/D,OAAO,IAAI,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;AAAA,CACrC;AAED,MAAM,UAAU,aAAa,CAAC,QAAQ,GAAG,aAAa,EAAE,EAAE;IACzD,OAAO,IAAI,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;AAAA,CACnC;AAED,MAAM,UAAU,iBAAiB,CAAC,QAAQ,GAAG,aAAa,EAAE,EAAE;IAC7D,OAAO,IAAI,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC;AAAA,CACvC;AAED,SAAS,YAAY,CAAC,IAAY,EAAc;IAC/C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IACjC,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;IAClD,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,CAAC;IACxB,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAY,CAAC;IAC9C,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,IAAI,EAAE,CAAC,CAAC;IACxE,OAAO,MAAM,CAAC;AAAA,CACd;AAED,SAAS,gBAAgB,CAAC,IAAY,EAAE,KAAc,EAAE;IACvD,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,MAAM,aAAa,GAAG,GAAG,IAAI,IAAI,OAAO,CAAC,GAAG,MAAM,CAAC;IACnD,aAAa,CAAC,aAAa,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC1G,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO;QAAE,SAAS,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;IAClE,UAAU,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;IAChC,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO;QAAE,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AAAA,CACzD;AAED,MAAM,UAAU,yBAAyB,CAAC,KAAa,EAAE;IACxD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IAC9D,MAAM,UAAU,GAAG,2BAA2B,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,OAAO,EAAE,CAAC;IAC9F,IAAI,GAAQ,CAAC;IACb,IAAI,CAAC;QACJ,GAAG,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACR,MAAM,IAAI,KAAK,CAAC,wBAAwB,KAAK,EAAE,CAAC,CAAC;IAClD,CAAC;IACD,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,IAAI,GAAG,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QAC3D,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;IACxD,CAAC;IACD,GAAG,CAAC,IAAI,GAAG,EAAE,CAAC;IACd,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC;IAChB,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IACjD,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,GAAG,CAAC,QAAQ,GAAG,GAAG,GAAG,CAAC,QAAQ,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IAC7F,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAAA,CAC1C;AAED,SAAS,UAAU,CAAC,KAAc,EAAoC;IACrE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAC5C,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,KAAK,EAA6B,EAAE,CAAC,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,OAAO,CAAC,CAAC;IACxG,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAAA,CAC1D;AAED,SAAS,SAAS,CAAC,KAAc,EAA6C;IAC7E,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IACvC,MAAM,KAAK,GAAG,iBAAiB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC7C,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/C,MAAM,SAAS,GAAG,iBAAiB,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IACrD,MAAM,UAAU,GAAG,iBAAiB,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IACvD,IAAI,KAAK,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS,IAAI,SAAS,KAAK,SAAS,IAAI,UAAU,KAAK,SAAS;QACrG,OAAO,SAAS,CAAC;IAClB,OAAO,EAAE,KAAK,EAAE,KAAK,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC,EAAE,SAAS,EAAE,SAAS,IAAI,CAAC,EAAE,UAAU,EAAE,UAAU,IAAI,CAAC,EAAE,CAAC;AAAA,CAC1G;AAED,SAAS,qBAAqB,CAAC,KAAc,EAA6C;IACzF,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IACvC,MAAM,MAAM,GAA2D,EAAE,CAAC;IAC1E,KAAK,MAAM,GAAG,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAU,EAAE,CAAC;QACxF,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;QACzB,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;YAAE,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IACtE,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;AAAA,CAC3D;AAED,SAAS,cAAc,CAAC,KAAc,EAA6C;IAClF,QAAQ,KAAK,EAAE,CAAC;QACf,KAAK,QAAQ,CAAC;QACd,KAAK,YAAY,CAAC;QAClB,KAAK,UAAU,CAAC;QAChB,KAAK,UAAU,CAAC;QAChB,KAAK,KAAK,CAAC;QACX,KAAK,MAAM,CAAC;QACZ,KAAK,eAAe,CAAC;QACrB,KAAK,oBAAoB,CAAC;QAC1B,KAAK,iBAAiB,CAAC;QACvB,KAAK,UAAU;YACd,OAAO,KAAK,CAAC;QACd;YACC,OAAO,SAAS,CAAC;IACnB,CAAC;AAAA,CACD;AAED,SAAS,qBAAqB,CAAC,KAAc,EAAoD;IAChG,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,kBAAkB,IAAI,KAAK,KAAK,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AAAA,CACxG;AAED,SAAS,WAAW,CAAC,KAAc,EAA2B;IAC7D,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5C,MAAM,cAAc,GACnB,MAAM,CAAC,cAAc,KAAK,uBAAuB,IAAI,MAAM,CAAC,cAAc,KAAK,YAAY;QAC1F,CAAC,CAAC,MAAM,CAAC,cAAc;QACvB,CAAC,CAAC,SAAS,CAAC;IACd,MAAM,MAAM,GAAG;QACd,aAAa,EAAE,YAAY,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,KAAK;QAC1D,qBAAqB,EAAE,YAAY,CAAC,MAAM,CAAC,qBAAqB,CAAC;QACjE,uBAAuB,EAAE,YAAY,CAAC,MAAM,CAAC,uBAAuB,CAAC;QACrE,wBAAwB,EAAE,YAAY,CAAC,MAAM,CAAC,wBAAwB,CAAC;QACvE,cAAc;QACd,sBAAsB,EAAE,YAAY,CAAC,MAAM,CAAC,sBAAsB,CAAC;QACnE,gCAAgC,EAAE,YAAY,CAAC,MAAM,CAAC,gCAAgC,CAAC;QACvF,sBAAsB,EAAE,YAAY,CAAC,MAAM,CAAC,sBAAsB,CAAC;QACnE,2CAA2C,EAAE,YAAY,CAAC,MAAM,CAAC,2CAA2C,CAAC;QAC7G,cAAc,EAAE,cAAc,CAAC,MAAM,CAAC,cAAc,CAAC;QACrD,kBAAkB,EAAE,YAAY,CAAC,MAAM,CAAC,kBAAkB,CAAC;QAC3D,0BAA0B,EAAE,YAAY,CAAC,MAAM,CAAC,0BAA0B,CAAC;QAC3E,qBAAqB,EAAE,qBAAqB,CAAC,MAAM,CAAC,qBAAqB,CAAC;QAC1E,0BAA0B,EAAE,YAAY,CAAC,MAAM,CAAC,0BAA0B,CAAC;KACzC,CAAC;IACpC,OAAO,MAAM,CAAC;AAAA,CACd;AAED,SAAS,iBAAiB,CAAC,KAAc,EAAqC;IAC7E,MAAM,MAAM,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;IAC/F,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IAC9B,MAAM,EAAE,GAAG,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAClC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,MAAM,GAAG,GAAG,IAAI,wBAAwB,CAAC,IAAI,CAAC,EAAE,CAAC;QAAE,OAAO,SAAS,CAAC;IAClF,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;IAClE,MAAM,KAAK,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC;IAC/D,MAAM,SAAS,GAAG,KAAK,CAAC,WAAW,CAAC,CAAC;IACrC,OAAO;QACN,EAAE;QACF,IAAI,EAAE,WAAW,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE;QACtC,SAAS,EAAE,OAAO,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK;QAC7D,gBAAgB,EAAE,qBAAqB,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;QAClE,KAAK,EAAE,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC;QAC7C,IAAI,EAAE,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE;QACtF,aAAa,EAAE,eAAe,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,IAAI,OAAO;QACjE,SAAS,EAAE,eAAe,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,IAAI,MAAM;QACxD,MAAM,EAAE,WAAW,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;KACpC,CAAC;AAAA,CACF;AAED,SAAS,iBAAiB,CAAC,MAA0B,EAAE,MAAc,EAAE;IACtE,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAkC,EAAE,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC;IACpH,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,MAAM,4BAA4B,CAAC,CAAC;IAChF,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;IAC9B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC5B,IAAI,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,GAAG,MAAM,gCAAgC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC;QAC7F,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACnB,CAAC;IACD,OAAO,MAAM,CAAC;AAAA,CACd;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,OAAe,EAAE,MAAc,EAAE,MAAoB,EAAE;IACjG,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,SAAS,EAAE;QACrE,MAAM;QACN,OAAO,EAAE;YACR,aAAa,EAAE,UAAU,MAAM,EAAE;YACjC,MAAM,EAAE,kBAAkB;YAC1B,YAAY,EAAE,aAAa;YAC3B,mBAAmB,EAAE,cAAc;SACnC;KACD,CAAC,CAAC;IACH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QAClB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QACnD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACnE,MAAM,IAAI,KAAK,CAAC,mCAAmC,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,IAAI,MAAM,EAAE,CAAC,CAAC;IACxG,CAAC;IACD,MAAM,OAAO,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAY,CAAC;IACnD,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACxD,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;IACxE,CAAC;IACD,OAAO,iBAAiB,CAAC,OAAO,CAAC,IAAI,EAAE,gCAAgC,CAAC,CAAC;AAAA,CACzE;AAED,SAAS,wBAAwB,CAAC,QAAgB,EAA8B;IAC/E,MAAM,IAAI,GAAG,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IAC3C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IACxC,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAChC,MAAM,SAAS,GAAG,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;IACxE,MAAM,aAAa,GAAG,SAAS,EAAE,CAAC,sBAAsB,CAAC,IAAI,SAAS,EAAE,CAAC,6BAA6B,CAAC,CAAC;IACxG,MAAM,QAAQ,GAAG,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC;IACrE,IAAI,CAAC,QAAQ,IAAI,OAAO,QAAQ,CAAC,OAAO,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,SAAS,CAAC;IAC3G,OAAO;QACN,aAAa,EAAE,CAAC;QAChB,OAAO,EAAE,yBAAyB,CAAC,QAAQ,CAAC,OAAO,CAAC;QACpD,MAAM,EAAE,iBAAiB,CAAC,QAAQ,CAAC,MAAM,EAAE,2BAA2B,IAAI,EAAE,CAAC;KAC7E,CAAC;AAAA,CACF;AAED,MAAM,UAAU,kBAAkB,CAAC,QAAQ,GAAG,aAAa,EAAE,EAA8B;IAC1F,MAAM,IAAI,GAAG,qBAAqB,CAAC,QAAQ,CAAC,CAAC;IAC7C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACvB,MAAM,MAAM,GAAG,wBAAwB,CAAC,QAAQ,CAAC,CAAC;QAClD,IAAI,CAAC,MAAM;YAAE,OAAO,SAAS,CAAC;QAC9B,mBAAmB,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAC7D,cAAc,GAAG;YAChB,IAAI,EAAE,uBAAuB;YAC7B,OAAO,EAAE,mDAAmD,mBAAmB,CAAC,QAAQ,CAAC,GAAG;YAC5F,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SAC5B,CAAC;QACF,OAAO,MAAM,CAAC;IACf,CAAC;IACD,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IACjC,IAAI,KAAK,CAAC,aAAa,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;QACpG,MAAM,IAAI,KAAK,CAAC,qCAAqC,IAAI,EAAE,CAAC,CAAC;IAC9D,CAAC;IACD,OAAO;QACN,aAAa,EAAE,CAAC;QAChB,OAAO,EAAE,yBAAyB,CAAC,KAAK,CAAC,OAAO,CAAC;QACjD,MAAM,EAAE,iBAAiB,CAAC,KAAK,CAAC,MAAM,EAAE,4BAA4B,IAAI,EAAE,CAAC;KAC3E,CAAC;AAAA,CACF;AAED,MAAM,UAAU,mBAAmB,CAAC,OAAe,EAAE,MAA+B,EAAE,QAAQ,GAAG,aAAa,EAAE,EAAE;IACjH,MAAM,eAAe,GAAG,iBAAiB,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IACtE,gBAAgB,CAAC,qBAAqB,CAAC,QAAQ,CAAC,EAAE;QACjD,aAAa,EAAE,CAAC;QAChB,OAAO,EAAE,yBAAyB,CAAC,OAAO,CAAC;QAC3C,MAAM,EAAE,eAAe;KACE,CAAC,CAAC;AAAA,CAC5B;AAED,MAAM,UAAU,mBAAmB,CAAC,QAAQ,GAAG,aAAa,EAAE,EAAE;IAC/D,MAAM,IAAI,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;IACrC,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,UAAU,GAAG,IAAI,CAAC,sBAAsB,CAAC,CAAC;IAC9C,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,6BAA6B,CAAC,EAAE,CAAC;QACxD,UAAU,GAAG,IAAI,CAAC,6BAA6B,CAAC,CAAC;QACjD,IAAI,CAAC,sBAAsB,CAAC,GAAG,UAAU,CAAC;QAC1C,OAAO,IAAI,CAAC,6BAA6B,CAAC,CAAC;QAC3C,gBAAgB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC9B,CAAC;IACD,OAAO,QAAQ,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC,IAAI,KAAK,SAAS,IAAI,OAAO,UAAU,CAAC,GAAG,KAAK,QAAQ;QACjG,CAAC,CAAC,UAAU,CAAC,GAAG;QAChB,CAAC,CAAC,SAAS,CAAC;AAAA,CACb;AAED,MAAM,UAAU,mBAAmB,CAAC,MAAc,EAAE,YAAoB,EAAE,QAAQ,GAAG,aAAa,EAAE,EAAE;IACrG,MAAM,QAAQ,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;IACzC,MAAM,IAAI,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;IACpC,IAAI,CAAC,sBAAsB,CAAC,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC;IAChE,OAAO,IAAI,CAAC,6BAA6B,CAAC,CAAC;IAC3C,gBAAgB,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAEjC,MAAM,YAAY,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IACjD,MAAM,QAAQ,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC;IAC5C,QAAQ,CAAC,eAAe,GAAG,sBAAsB,CAAC;IAClD,QAAQ,CAAC,YAAY,GAAG,YAAY,CAAC;IACrC,gBAAgB,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;AAAA,CACzC;AAED,MAAM,UAAU,oBAAoB,CAAC,QAAQ,GAAG,aAAa,EAAE,EAAE;IAChE,MAAM,IAAI,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;IACrC,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAChC,OAAO,IAAI,CAAC,sBAAsB,CAAC,CAAC;IACpC,OAAO,IAAI,CAAC,6BAA6B,CAAC,CAAC;IAC3C,gBAAgB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AAAA,CAC7B;AAED,SAAS,aAAa,CAAC,OAA6B,EAAE;IACrD,OAAO,OAAO,CAAC,UAAU,EAAE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;AAAA,CACnF;AAED,SAAS,YAAY,CAAC,MAAwC,EAAE,OAAe,EAAgB;IAC9F,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAC7B,GAAG,KAAK;QACR,QAAQ,EAAE,sBAAsB;QAChC,OAAO;QACP,GAAG,EAAE,oBAAoB;KACzB,CAAC,CAAC,CAAC;AAAA,CACJ;AAED,MAAM,UAAU,mBAAmB,GAAG;IACrC,OAAO,cAAc,CAAC;AAAA,CACtB;AAED,MAAM,UAAU,qBAAqB,GAAG;IACvC,cAAc,GAAG,SAAS,CAAC;AAAA,CAC3B;AAED,MAAM,UAAU,6BAA6B,CAC5C,OAAe,EACf,MAA+B,EAC/B,QAAQ,GAAG,aAAa,EAAE,EACT;IACjB,IAAI,aAAa,GAAG,iBAAiB,CAAC,MAAM,EAAE,mCAAmC,CAAC,CAAC;IACnF,OAAO;QACN,IAAI,EAAE,wBAAwB;QAC9B,OAAO;QACP,GAAG,EAAE,oBAAoB;QACzB,UAAU,EAAE,IAAI;QAChB,OAAO,EAAE;YACR,YAAY,EAAE,aAAa;YAC3B,mBAAmB,EAAE,cAAc;SACnC;QACD,MAAM,EAAE,aAAa;QACrB,KAAK,CAAC,aAAa,CAAC,OAAO,EAAE;YAC5B,IAAI,CAAC,OAAO,CAAC,YAAY;gBAAE,OAAO,aAAa,CAAC;YAChD,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;YACtC,IAAI,CAAC,MAAM;gBAAE,OAAO,aAAa,CAAC;YAClC,IAAI,CAAC;gBACJ,MAAM,SAAS,GAAG,MAAM,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;gBAC9E,IAAI,OAAO,CAAC,MAAM,CAAC,OAAO;oBAAE,OAAO,aAAa,CAAC;gBACjD,MAAM,OAAO,CAAC,OAAO,CAAC;oBACrB,OAAO,EAAE,EAAE,MAAM,EAAE,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE;oBAC5E,MAAM,EAAE,GAAG,EAAE,CAAC;wBACb,mBAAmB,CAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;wBAClD,aAAa,GAAG,SAAS,CAAC;wBAC1B,cAAc,GAAG,SAAS,CAAC;oBAAA,CAC3B;iBACD,CAAC,CAAC;gBACH,OAAO,aAAa,CAAC;YACtB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,cAAc,GAAG;oBAChB,IAAI,EAAE,gBAAgB;oBACtB,OAAO,EAAE,0CAA0C,aAAa,CAAC,MAAM,gBAAgB,aAAa,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;oBACzL,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;iBAC5B,CAAC;gBACF,MAAM,KAAK,CAAC;YACb,CAAC;QAAA,CACD;KACD,CAAC;AAAA,CACF;AAED,MAAM,UAAU,wBAAwB,GAAqB;IAC5D,OAAO,CAAC,EAAE,EAAE,EAAE,CAAC;QACd,MAAM,KAAK,GAAG,kBAAkB,EAAE,CAAC;QACnC,IAAI,KAAK,EAAE,CAAC;YACX,EAAE,CAAC,gBAAgB,CAAC,sBAAsB,EAAE,6BAA6B,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;QACzG,CAAC;QAED,EAAE,CAAC,EAAE,CAAC,oBAAoB,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YACvC,YAAY,EAAE,GAAG,KAAK,CAAC,YAAY,+RAA+R;SAClU,CAAC,CAAC,CAAC;QAEJ,EAAE,CAAC,EAAE,CAAC,eAAe,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC;YACvC,GAAG,CAAC,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;YAC5B,MAAM,OAAO,GAAG,mBAAmB,EAAE,CAAC;YACtC,IAAI,OAAO;gBAAE,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;YACvD,IAAI,GAAG,CAAC,IAAI,KAAK,KAAK;gBAAE,OAAO;YAC/B,GAAG,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;gBAClC,MAAM,EAAE,GAAG,EAAE,CAAC;oBACb,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;oBAC1C,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,iBAAgB,KAAK,CAAC,MAAM,CAAC,MAAM,SAAS,CAAC,CAAC,CAAC,gBAAgB,CAAC;iBAC1F;gBACD,UAAU,GAAG,EAAC,CAAC;aACf,CAAC,CAAC,CAAC;QAAA,CACJ,CAAC,CAAC;IAAA,CACH,CAAC;AAAA,CACF","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\tsystemPrompt: `${event.systemPrompt}\\n\\n<sp_agent_runtime>\\nThis is Senpiper's sp-agent distribution. Use 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}));\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 = \"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"]}
|
|
@@ -3,8 +3,6 @@ export { handleSpCommand } from "./commands.ts";
|
|
|
3
3
|
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
|
-
export { clearOnboardingState, fetchSkillCatalog, getOnboardingStatePath, hasCompletedOnboarding, installCatalogSkills, parseSkillCatalog, selectCatalogSkills, writeOnboardingState, } from "./onboarding.ts";
|
|
7
|
-
export { installOnboardingSelection, type OnboardingInstallationResult } from "./onboarding-installation.ts";
|
|
8
6
|
export type { PendingQuestion, QuestionResponse } from "./question-interaction.ts";
|
|
9
7
|
export { ensureSpWebSidecar, getSpWebSidecarStatePath, resolveSpWebLaunch, type SpWebLauncherOptions, type SpWebLaunchResult, type SpWebSidecarState, } from "./sp-web-launcher.ts";
|
|
10
8
|
export { filterSpStartupMessage, registerSpStartupHeader, renderSpStartupHeader } from "./startup-header.ts";
|
|
@@ -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,
|
|
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"]}
|
|
@@ -3,8 +3,6 @@ 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 { clearOnboardingState, fetchSkillCatalog, getOnboardingStatePath, hasCompletedOnboarding, installCatalogSkills, parseSkillCatalog, selectCatalogSkills, writeOnboardingState, } from "./onboarding.js";
|
|
7
|
-
export { installOnboardingSelection } from "./onboarding-installation.js";
|
|
8
6
|
export { ensureSpWebSidecar, getSpWebSidecarStatePath, resolveSpWebLaunch, } from "./sp-web-launcher.js";
|
|
9
7
|
export { filterSpStartupMessage, registerSpStartupHeader, renderSpStartupHeader } from "./startup-header.js";
|
|
10
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";
|