@tickernelz/paperclip-pro-adapter-pi-local 2026.925.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/LICENSE +22 -0
  2. package/dist/cli/format-event.d.ts +2 -0
  3. package/dist/cli/format-event.d.ts.map +1 -0
  4. package/dist/cli/format-event.js +99 -0
  5. package/dist/cli/format-event.js.map +1 -0
  6. package/dist/cli/index.d.ts +2 -0
  7. package/dist/cli/index.d.ts.map +1 -0
  8. package/dist/cli/index.js +2 -0
  9. package/dist/cli/index.js.map +1 -0
  10. package/dist/index.d.ts +9 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +40 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/server/execute.d.ts +3 -0
  15. package/dist/server/execute.d.ts.map +1 -0
  16. package/dist/server/execute.js +725 -0
  17. package/dist/server/execute.js.map +1 -0
  18. package/dist/server/execute.remote.test.d.ts +2 -0
  19. package/dist/server/execute.remote.test.d.ts.map +1 -0
  20. package/dist/server/execute.remote.test.js +533 -0
  21. package/dist/server/execute.remote.test.js.map +1 -0
  22. package/dist/server/index.d.ts +8 -0
  23. package/dist/server/index.d.ts.map +1 -0
  24. package/dist/server/index.js +51 -0
  25. package/dist/server/index.js.map +1 -0
  26. package/dist/server/models.d.ts +20 -0
  27. package/dist/server/models.d.ts.map +1 -0
  28. package/dist/server/models.js +163 -0
  29. package/dist/server/models.js.map +1 -0
  30. package/dist/server/models.test.d.ts +2 -0
  31. package/dist/server/models.test.d.ts.map +1 -0
  32. package/dist/server/models.test.js +22 -0
  33. package/dist/server/models.test.js.map +1 -0
  34. package/dist/server/parse.d.ts +23 -0
  35. package/dist/server/parse.d.ts.map +1 -0
  36. package/dist/server/parse.js +210 -0
  37. package/dist/server/parse.js.map +1 -0
  38. package/dist/server/parse.test.d.ts +2 -0
  39. package/dist/server/parse.test.d.ts.map +1 -0
  40. package/dist/server/parse.test.js +264 -0
  41. package/dist/server/parse.test.js.map +1 -0
  42. package/dist/server/runtime-config.d.ts +12 -0
  43. package/dist/server/runtime-config.d.ts.map +1 -0
  44. package/dist/server/runtime-config.js +126 -0
  45. package/dist/server/runtime-config.js.map +1 -0
  46. package/dist/server/runtime-config.test.d.ts +2 -0
  47. package/dist/server/runtime-config.test.d.ts.map +1 -0
  48. package/dist/server/runtime-config.test.js +197 -0
  49. package/dist/server/runtime-config.test.js.map +1 -0
  50. package/dist/server/skills.d.ts +7 -0
  51. package/dist/server/skills.d.ts.map +1 -0
  52. package/dist/server/skills.js +69 -0
  53. package/dist/server/skills.js.map +1 -0
  54. package/dist/server/test.d.ts +3 -0
  55. package/dist/server/test.d.ts.map +1 -0
  56. package/dist/server/test.js +309 -0
  57. package/dist/server/test.js.map +1 -0
  58. package/dist/ui/build-config.d.ts +3 -0
  59. package/dist/ui/build-config.d.ts.map +1 -0
  60. package/dist/ui/build-config.js +26 -0
  61. package/dist/ui/build-config.js.map +1 -0
  62. package/dist/ui/index.d.ts +3 -0
  63. package/dist/ui/index.d.ts.map +1 -0
  64. package/dist/ui/index.js +3 -0
  65. package/dist/ui/index.js.map +1 -0
  66. package/dist/ui/parse-stdout.d.ts +4 -0
  67. package/dist/ui/parse-stdout.d.ts.map +1 -0
  68. package/dist/ui/parse-stdout.js +271 -0
  69. package/dist/ui/parse-stdout.js.map +1 -0
  70. package/package.json +57 -0
@@ -0,0 +1,264 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { parsePiJsonl, isPiUnknownSessionError } from "./parse.js";
3
+ describe("parsePiJsonl", () => {
4
+ it("parses agent lifecycle and messages", () => {
5
+ const stdout = [
6
+ JSON.stringify({ type: "agent_start" }),
7
+ JSON.stringify({
8
+ type: "turn_end",
9
+ message: {
10
+ role: "assistant",
11
+ content: [{ type: "text", text: "Hello from Pi" }],
12
+ },
13
+ }),
14
+ JSON.stringify({ type: "agent_end", messages: [] }),
15
+ ].join("\n");
16
+ const parsed = parsePiJsonl(stdout);
17
+ expect(parsed.messages).toContain("Hello from Pi");
18
+ expect(parsed.finalMessage).toBe("Hello from Pi");
19
+ });
20
+ it("parses streaming text deltas", () => {
21
+ const stdout = [
22
+ JSON.stringify({
23
+ type: "message_update",
24
+ assistantMessageEvent: { type: "text_delta", delta: "Hello " },
25
+ }),
26
+ JSON.stringify({
27
+ type: "message_update",
28
+ assistantMessageEvent: { type: "text_delta", delta: "World" },
29
+ }),
30
+ JSON.stringify({
31
+ type: "turn_end",
32
+ message: {
33
+ role: "assistant",
34
+ content: "Hello World",
35
+ },
36
+ }),
37
+ ].join("\n");
38
+ const parsed = parsePiJsonl(stdout);
39
+ expect(parsed.messages).toContain("Hello World");
40
+ });
41
+ it("parses tool execution", () => {
42
+ const stdout = [
43
+ JSON.stringify({
44
+ type: "tool_execution_start",
45
+ toolCallId: "tool_1",
46
+ toolName: "read",
47
+ args: { path: "/tmp/test.txt" },
48
+ }),
49
+ JSON.stringify({
50
+ type: "tool_execution_end",
51
+ toolCallId: "tool_1",
52
+ toolName: "read",
53
+ result: "file contents",
54
+ isError: false,
55
+ }),
56
+ JSON.stringify({
57
+ type: "turn_end",
58
+ message: { role: "assistant", content: "Done" },
59
+ toolResults: [
60
+ {
61
+ toolCallId: "tool_1",
62
+ content: "file contents",
63
+ isError: false,
64
+ },
65
+ ],
66
+ }),
67
+ ].join("\n");
68
+ const parsed = parsePiJsonl(stdout);
69
+ expect(parsed.toolCalls).toHaveLength(1);
70
+ expect(parsed.toolCalls[0].toolName).toBe("read");
71
+ expect(parsed.toolCalls[0].result).toBe("file contents");
72
+ expect(parsed.toolCalls[0].isError).toBe(false);
73
+ });
74
+ it("handles errors in tool execution", () => {
75
+ const stdout = [
76
+ JSON.stringify({
77
+ type: "tool_execution_start",
78
+ toolCallId: "tool_1",
79
+ toolName: "read",
80
+ args: { path: "/missing.txt" },
81
+ }),
82
+ JSON.stringify({
83
+ type: "tool_execution_end",
84
+ toolCallId: "tool_1",
85
+ toolName: "read",
86
+ result: "File not found",
87
+ isError: true,
88
+ }),
89
+ ].join("\n");
90
+ const parsed = parsePiJsonl(stdout);
91
+ expect(parsed.toolCalls).toHaveLength(1);
92
+ expect(parsed.toolCalls[0].isError).toBe(true);
93
+ expect(parsed.toolCalls[0].result).toBe("File not found");
94
+ });
95
+ it("extracts usage and cost from turn_end events", () => {
96
+ const stdout = [
97
+ JSON.stringify({
98
+ type: "turn_end",
99
+ message: {
100
+ role: "assistant",
101
+ content: "Response with usage",
102
+ usage: {
103
+ input: 100,
104
+ output: 50,
105
+ cacheRead: 20,
106
+ totalTokens: 170,
107
+ cost: {
108
+ input: 0.001,
109
+ output: 0.0015,
110
+ cacheRead: 0.0001,
111
+ cacheWrite: 0,
112
+ total: 0.0026,
113
+ },
114
+ },
115
+ },
116
+ toolResults: [],
117
+ }),
118
+ ].join("\n");
119
+ const parsed = parsePiJsonl(stdout);
120
+ expect(parsed.usage.inputTokens).toBe(100);
121
+ expect(parsed.usage.outputTokens).toBe(50);
122
+ expect(parsed.usage.cachedInputTokens).toBe(20);
123
+ expect(parsed.usage.costUsd).toBeCloseTo(0.0026, 4);
124
+ });
125
+ it("accumulates usage from multiple turns", () => {
126
+ const stdout = [
127
+ JSON.stringify({
128
+ type: "turn_end",
129
+ message: {
130
+ role: "assistant",
131
+ content: "First response",
132
+ usage: {
133
+ input: 50,
134
+ output: 25,
135
+ cacheRead: 0,
136
+ cost: { total: 0.001 },
137
+ },
138
+ },
139
+ }),
140
+ JSON.stringify({
141
+ type: "turn_end",
142
+ message: {
143
+ role: "assistant",
144
+ content: "Second response",
145
+ usage: {
146
+ input: 30,
147
+ output: 20,
148
+ cacheRead: 10,
149
+ cost: { total: 0.0015 },
150
+ },
151
+ },
152
+ }),
153
+ ].join("\n");
154
+ const parsed = parsePiJsonl(stdout);
155
+ expect(parsed.usage.inputTokens).toBe(80);
156
+ expect(parsed.usage.outputTokens).toBe(45);
157
+ expect(parsed.usage.cachedInputTokens).toBe(10);
158
+ expect(parsed.usage.costUsd).toBeCloseTo(0.0025, 4);
159
+ });
160
+ it("handles standalone usage events with Pi format", () => {
161
+ const stdout = [
162
+ JSON.stringify({
163
+ type: "usage",
164
+ usage: {
165
+ input: 200,
166
+ output: 100,
167
+ cacheRead: 50,
168
+ cost: { total: 0.005 },
169
+ },
170
+ }),
171
+ ].join("\n");
172
+ const parsed = parsePiJsonl(stdout);
173
+ expect(parsed.usage.inputTokens).toBe(200);
174
+ expect(parsed.usage.outputTokens).toBe(100);
175
+ expect(parsed.usage.cachedInputTokens).toBe(50);
176
+ expect(parsed.usage.costUsd).toBe(0.005);
177
+ });
178
+ it("handles standalone usage events with generic format", () => {
179
+ const stdout = [
180
+ JSON.stringify({
181
+ type: "usage",
182
+ usage: {
183
+ inputTokens: 150,
184
+ outputTokens: 75,
185
+ cachedInputTokens: 25,
186
+ costUsd: 0.003,
187
+ },
188
+ }),
189
+ ].join("\n");
190
+ const parsed = parsePiJsonl(stdout);
191
+ expect(parsed.usage.inputTokens).toBe(150);
192
+ expect(parsed.usage.outputTokens).toBe(75);
193
+ expect(parsed.usage.cachedInputTokens).toBe(25);
194
+ expect(parsed.usage.costUsd).toBe(0.003);
195
+ });
196
+ it("surfaces failed auto-retry exhaustion as an error", () => {
197
+ const stdout = [
198
+ JSON.stringify({
199
+ type: "auto_retry_end",
200
+ success: false,
201
+ attempt: 3,
202
+ finalError: "Cloud Code Assist API error (429): RESOURCE_EXHAUSTED",
203
+ }),
204
+ ].join("\n");
205
+ const parsed = parsePiJsonl(stdout);
206
+ expect(parsed.errors).toEqual(["Cloud Code Assist API error (429): RESOURCE_EXHAUSTED"]);
207
+ });
208
+ it("does not treat successful auto-retry as an error", () => {
209
+ const stdout = [
210
+ JSON.stringify({
211
+ type: "auto_retry_end",
212
+ success: true,
213
+ attempt: 2,
214
+ }),
215
+ ].join("\n");
216
+ const parsed = parsePiJsonl(stdout);
217
+ expect(parsed.errors).toEqual([]);
218
+ });
219
+ it("surfaces standalone error events", () => {
220
+ const stdout = [
221
+ JSON.stringify({
222
+ type: "error",
223
+ message: "Connection to model provider lost",
224
+ }),
225
+ ].join("\n");
226
+ const parsed = parsePiJsonl(stdout);
227
+ expect(parsed.errors).toEqual(["Connection to model provider lost"]);
228
+ });
229
+ it("ignores error events with empty messages", () => {
230
+ const stdout = [
231
+ JSON.stringify({
232
+ type: "error",
233
+ message: "",
234
+ }),
235
+ ].join("\n");
236
+ const parsed = parsePiJsonl(stdout);
237
+ expect(parsed.errors).toEqual([]);
238
+ });
239
+ });
240
+ describe("isPiUnknownSessionError", () => {
241
+ it("detects unknown session errors", () => {
242
+ expect(isPiUnknownSessionError("session not found: s_123", "")).toBe(true);
243
+ expect(isPiUnknownSessionError("", "unknown session id")).toBe(true);
244
+ expect(isPiUnknownSessionError("", "no session available")).toBe(true);
245
+ expect(isPiUnknownSessionError("all good", "")).toBe(false);
246
+ expect(isPiUnknownSessionError("working fine", "no errors")).toBe(false);
247
+ });
248
+ });
249
+ describe("terminal provider failures", () => {
250
+ it("surfaces and deduplicates errors from Pi assistant messages", () => {
251
+ const message = { role: "assistant", content: [], stopReason: "error", errorMessage: "400 Context limit exceeded" };
252
+ const parsed = parsePiJsonl([
253
+ { type: "message_end", message },
254
+ { type: "turn_end", message },
255
+ { type: "agent_end", messages: [message] },
256
+ ].map(event => JSON.stringify(event)).join("\n"));
257
+ expect(parsed.errors).toEqual(["400 Context limit exceeded"]);
258
+ });
259
+ it("reports an error even when the provider omitted its message", () => {
260
+ expect(parsePiJsonl(JSON.stringify({ type: "turn_end", message: { role: "assistant", stopReason: "error" } })).errors)
261
+ .toEqual(["Pi provider request failed."]);
262
+ });
263
+ });
264
+ //# sourceMappingURL=parse.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.test.js","sourceRoot":"","sources":["../../src/server/parse.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ type PreparedPiRuntimeConfig = {
2
+ env: Record<string, string>;
3
+ notes: string[];
4
+ /** The managed agent-config dir, or null when no provider config was written. */
5
+ agentConfigDir: string | null;
6
+ cleanup: () => Promise<void>;
7
+ };
8
+ export declare function preparePiRuntimeConfig(input: {
9
+ env: Record<string, string>;
10
+ }): Promise<PreparedPiRuntimeConfig>;
11
+ export {};
12
+ //# sourceMappingURL=runtime-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-config.d.ts","sourceRoot":"","sources":["../../src/server/runtime-config.ts"],"names":[],"mappings":"AAIA,KAAK,uBAAuB,GAAG;IAC7B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,iFAAiF;IACjF,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B,CAAC;AAgGF,wBAAsB,sBAAsB,CAAC,KAAK,EAAE;IAClD,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC7B,GAAG,OAAO,CAAC,uBAAuB,CAAC,CA2CnC"}
@@ -0,0 +1,126 @@
1
+ import fs from "node:fs/promises";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ function isPlainObject(value) {
5
+ return typeof value === "object" && value !== null && !Array.isArray(value);
6
+ }
7
+ // Recursively replace {env:VAR} placeholders with the resolved value. Used to bake
8
+ // gateway provider secrets (e.g. an LLM-gateway virtual key) into models.json
9
+ // SERVER-SIDE, where the value is reliably present. Pi resolves a provider apiKey
10
+ // by trying it as an env var name first, then as a literal -- but the (possibly
11
+ // sandboxed) run process env plumbing is not guaranteed to carry the key, so we
12
+ // resolve it here. Unresolvable placeholders are left intact; an env var set to
13
+ // an empty string counts as unresolvable (the placeholder stays for Pi to try).
14
+ function expandEnvPlaceholders(value, resolve) {
15
+ if (typeof value === "string") {
16
+ return value.replace(/\{env:([A-Za-z_][A-Za-z0-9_]*)\}/g, (match, name) => {
17
+ const resolved = resolve(name);
18
+ return resolved !== undefined && resolved.length > 0 ? resolved : match;
19
+ });
20
+ }
21
+ if (Array.isArray(value)) {
22
+ return value.map((entry) => expandEnvPlaceholders(entry, resolve));
23
+ }
24
+ if (isPlainObject(value)) {
25
+ const out = {};
26
+ for (const [key, entry] of Object.entries(value)) {
27
+ out[key] = expandEnvPlaceholders(entry, resolve);
28
+ }
29
+ return out;
30
+ }
31
+ return value;
32
+ }
33
+ function parseProviderConfig(raw, resolveEnv) {
34
+ // Unset/empty (or an empty JSON object) is the normal "feature off" case: no warning.
35
+ if (typeof raw !== "string" || raw.trim().length === 0) {
36
+ return { providers: null, warning: null };
37
+ }
38
+ // A SET but unusable value is a misconfiguration; surface it so the run does
39
+ // not proceed silently unconfigured and fail later with an opaque
40
+ // model-not-found error pointing nowhere near the env var.
41
+ try {
42
+ const parsed = JSON.parse(raw);
43
+ if (!isPlainObject(parsed)) {
44
+ return {
45
+ providers: null,
46
+ warning: "PAPERCLIP_PI_PROVIDERS is set but is not a JSON object; custom providers ignored.",
47
+ };
48
+ }
49
+ // Only keep provider entries that are themselves objects; surface the ones
50
+ // we drop so a malformed entry is just as diagnosable as malformed JSON.
51
+ const providers = {};
52
+ const skipped = [];
53
+ for (const [key, value] of Object.entries(parsed)) {
54
+ if (isPlainObject(value))
55
+ providers[key] = expandEnvPlaceholders(value, resolveEnv);
56
+ else
57
+ skipped.push(key);
58
+ }
59
+ return {
60
+ providers: Object.keys(providers).length > 0 ? providers : null,
61
+ warning: skipped.length > 0
62
+ ? `PAPERCLIP_PI_PROVIDERS: skipped provider(s) with non-object values: ${skipped.join(", ")}.`
63
+ : null,
64
+ };
65
+ }
66
+ catch {
67
+ return {
68
+ providers: null,
69
+ warning: "PAPERCLIP_PI_PROVIDERS contains invalid JSON; custom providers ignored.",
70
+ };
71
+ }
72
+ }
73
+ // Materialize custom Pi providers supplied via PAPERCLIP_PI_PROVIDERS (a JSON
74
+ // object in Pi's models.json "providers" shape) into a managed agent-config dir.
75
+ //
76
+ // Pi has no base-url CLI flag or env var: the only mechanism for pointing it at a
77
+ // custom/OpenAI- or Anthropic-compatible endpoint is a models.json file in its
78
+ // agent config dir, which Pi resolves from $PI_CODING_AGENT_DIR (falling back to
79
+ // $HOME/.pi/agent). Pi resolves `--provider P --model M` by an exact (provider,
80
+ // model id) match against that registry, so routing a gateway model requires the
81
+ // provider entry to enumerate its models explicitly. We accept the providers as
82
+ // config (not hard-coded) so the gateway URL, key, and model list stay declarative.
83
+ //
84
+ // When PAPERCLIP_PI_PROVIDERS is set we write {"providers": ...} to a fresh temp
85
+ // dir and point PI_CODING_AGENT_DIR at it; the managed dir intentionally replaces
86
+ // the host agent dir (credentials travel inside the providers config itself, via
87
+ // a literal apiKey or a server-side-expanded {env:VAR} placeholder). For remote
88
+ // execution targets, execute.ts ships the dir to the sandbox as a runtime asset
89
+ // and repoints PI_CODING_AGENT_DIR at the in-sandbox copy.
90
+ export async function preparePiRuntimeConfig(input) {
91
+ const resolveEnv = (name) => input.env[name] ?? process.env[name];
92
+ const { providers, warning } = parseProviderConfig(input.env.PAPERCLIP_PI_PROVIDERS ?? process.env.PAPERCLIP_PI_PROVIDERS, resolveEnv);
93
+ if (!providers) {
94
+ return {
95
+ env: input.env,
96
+ notes: warning ? [warning] : [],
97
+ agentConfigDir: null,
98
+ cleanup: async () => { },
99
+ };
100
+ }
101
+ const agentConfigDir = await fs.mkdtemp(path.join(os.tmpdir(), "paperclip-pi-agent-config-"));
102
+ try {
103
+ await fs.writeFile(path.join(agentConfigDir, "models.json"), `${JSON.stringify({ providers }, null, 2)}\n`, "utf8");
104
+ }
105
+ catch (err) {
106
+ // Never leak the temp dir when the write fails (e.g. disk-full): the
107
+ // caller only receives the cleanup handle on success.
108
+ await fs.rm(agentConfigDir, { recursive: true, force: true }).catch(() => undefined);
109
+ throw err;
110
+ }
111
+ return {
112
+ env: {
113
+ ...input.env,
114
+ PI_CODING_AGENT_DIR: agentConfigDir,
115
+ },
116
+ notes: [
117
+ ...(warning ? [warning] : []),
118
+ `Injected ${Object.keys(providers).length} custom Pi provider(s) from PAPERCLIP_PI_PROVIDERS into a managed models.json: ${Object.keys(providers).join(", ")}.`,
119
+ ],
120
+ agentConfigDir,
121
+ cleanup: async () => {
122
+ await fs.rm(agentConfigDir, { recursive: true, force: true });
123
+ },
124
+ };
125
+ }
126
+ //# sourceMappingURL=runtime-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-config.js","sourceRoot":"","sources":["../../src/server/runtime-config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=runtime-config.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-config.test.d.ts","sourceRoot":"","sources":["../../src/server/runtime-config.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,197 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { afterEach, describe, expect, it } from "vitest";
4
+ import { preparePiRuntimeConfig } from "./runtime-config.js";
5
+ const cleanupPaths = new Set();
6
+ afterEach(async () => {
7
+ await Promise.all([...cleanupPaths].map(async (filepath) => {
8
+ await fs.rm(filepath, { recursive: true, force: true });
9
+ cleanupPaths.delete(filepath);
10
+ }));
11
+ });
12
+ async function readModelsJson(agentConfigDir) {
13
+ return JSON.parse(await fs.readFile(path.join(agentConfigDir, "models.json"), "utf8"));
14
+ }
15
+ describe("preparePiRuntimeConfig", () => {
16
+ it("is a no-op when PAPERCLIP_PI_PROVIDERS is unset", async () => {
17
+ const prepared = await preparePiRuntimeConfig({ env: { FOO: "bar" } });
18
+ expect(prepared.env).toEqual({ FOO: "bar" });
19
+ expect(prepared.env.PI_CODING_AGENT_DIR).toBeUndefined();
20
+ expect(prepared.notes).toEqual([]);
21
+ await prepared.cleanup();
22
+ });
23
+ it("writes the providers JSON verbatim to a managed models.json and points PI_CODING_AGENT_DIR at it", async () => {
24
+ const providers = {
25
+ tensorix: {
26
+ baseUrl: "http://gateway.example.svc.cluster.local:8080/anthropic",
27
+ apiKey: "sk-literal",
28
+ api: "anthropic-messages",
29
+ models: [
30
+ {
31
+ id: "deepseek/deepseek-chat-v3.1",
32
+ name: "DeepSeek v3.1",
33
+ reasoning: false,
34
+ input: ["text"],
35
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
36
+ contextWindow: 128000,
37
+ maxTokens: 32000,
38
+ },
39
+ ],
40
+ },
41
+ };
42
+ const prepared = await preparePiRuntimeConfig({
43
+ env: { PAPERCLIP_PI_PROVIDERS: JSON.stringify(providers) },
44
+ });
45
+ const agentConfigDir = prepared.env.PI_CODING_AGENT_DIR;
46
+ expect(agentConfigDir).toBeTruthy();
47
+ cleanupPaths.add(agentConfigDir);
48
+ expect(await readModelsJson(agentConfigDir)).toEqual({ providers });
49
+ expect(prepared.notes.some((n) => n.includes("tensorix"))).toBe(true);
50
+ await prepared.cleanup();
51
+ cleanupPaths.delete(agentConfigDir);
52
+ await expect(fs.access(agentConfigDir)).rejects.toThrow();
53
+ });
54
+ it("reads PAPERCLIP_PI_PROVIDERS from process.env when absent from the run env", async () => {
55
+ const providers = { tensorix: { baseUrl: "http://gw/anthropic", api: "anthropic-messages", models: [] } };
56
+ process.env.PAPERCLIP_PI_PROVIDERS = JSON.stringify(providers);
57
+ try {
58
+ const prepared = await preparePiRuntimeConfig({ env: {} });
59
+ const agentConfigDir = prepared.env.PI_CODING_AGENT_DIR;
60
+ expect(agentConfigDir).toBeTruthy();
61
+ cleanupPaths.add(agentConfigDir);
62
+ expect(await readModelsJson(agentConfigDir)).toEqual({ providers });
63
+ await prepared.cleanup();
64
+ }
65
+ finally {
66
+ delete process.env.PAPERCLIP_PI_PROVIDERS;
67
+ }
68
+ });
69
+ it("expands {env:VAR} placeholders from the run env (bakes the literal key)", async () => {
70
+ const providers = {
71
+ tensorix: { baseUrl: "http://gw/anthropic", apiKey: "{env:ANTHROPIC_API_KEY}", api: "anthropic-messages", models: [] },
72
+ };
73
+ const prepared = await preparePiRuntimeConfig({
74
+ env: {
75
+ PAPERCLIP_PI_PROVIDERS: JSON.stringify(providers),
76
+ ANTHROPIC_API_KEY: "sk-bf-REALVK",
77
+ },
78
+ });
79
+ const agentConfigDir = prepared.env.PI_CODING_AGENT_DIR;
80
+ cleanupPaths.add(agentConfigDir);
81
+ const modelsJson = (await readModelsJson(agentConfigDir));
82
+ expect(modelsJson.providers.tensorix.apiKey).toBe("sk-bf-REALVK");
83
+ await prepared.cleanup();
84
+ });
85
+ it("expands {env:VAR} placeholders from process.env when absent from the run env", async () => {
86
+ const providers = {
87
+ tensorix: { baseUrl: "http://gw/anthropic", apiKey: "{env:PAPERCLIP_PI_TEST_KEY}", api: "anthropic-messages", models: [] },
88
+ };
89
+ process.env.PAPERCLIP_PI_TEST_KEY = "sk-from-process-env";
90
+ try {
91
+ const prepared = await preparePiRuntimeConfig({
92
+ env: { PAPERCLIP_PI_PROVIDERS: JSON.stringify(providers) },
93
+ });
94
+ const agentConfigDir = prepared.env.PI_CODING_AGENT_DIR;
95
+ cleanupPaths.add(agentConfigDir);
96
+ const modelsJson = (await readModelsJson(agentConfigDir));
97
+ expect(modelsJson.providers.tensorix.apiKey).toBe("sk-from-process-env");
98
+ await prepared.cleanup();
99
+ }
100
+ finally {
101
+ delete process.env.PAPERCLIP_PI_TEST_KEY;
102
+ }
103
+ });
104
+ it("leaves an unresolvable {env:VAR} placeholder intact", async () => {
105
+ const providers = {
106
+ tensorix: { baseUrl: "http://gw/anthropic", apiKey: "{env:DEFINITELY_UNSET_VAR_XYZ}", api: "anthropic-messages", models: [] },
107
+ };
108
+ const prepared = await preparePiRuntimeConfig({
109
+ env: { PAPERCLIP_PI_PROVIDERS: JSON.stringify(providers) },
110
+ });
111
+ const agentConfigDir = prepared.env.PI_CODING_AGENT_DIR;
112
+ cleanupPaths.add(agentConfigDir);
113
+ const modelsJson = (await readModelsJson(agentConfigDir));
114
+ expect(modelsJson.providers.tensorix.apiKey).toBe("{env:DEFINITELY_UNSET_VAR_XYZ}");
115
+ await prepared.cleanup();
116
+ });
117
+ it("ignores malformed PAPERCLIP_PI_PROVIDERS without writing a config", async () => {
118
+ const prepared = await preparePiRuntimeConfig({
119
+ env: { PAPERCLIP_PI_PROVIDERS: "not json" },
120
+ });
121
+ expect(prepared.env.PI_CODING_AGENT_DIR).toBeUndefined();
122
+ expect(prepared.notes).toEqual([
123
+ "PAPERCLIP_PI_PROVIDERS contains invalid JSON; custom providers ignored.",
124
+ ]);
125
+ await prepared.cleanup();
126
+ });
127
+ it("ignores provider entries that are not objects and names them in the note", async () => {
128
+ const prepared = await preparePiRuntimeConfig({
129
+ env: { PAPERCLIP_PI_PROVIDERS: JSON.stringify({ tensorix: "nope" }) },
130
+ });
131
+ expect(prepared.env.PI_CODING_AGENT_DIR).toBeUndefined();
132
+ expect(prepared.agentConfigDir).toBeNull();
133
+ expect(prepared.notes).toEqual([
134
+ "PAPERCLIP_PI_PROVIDERS: skipped provider(s) with non-object values: tensorix.",
135
+ ]);
136
+ await prepared.cleanup();
137
+ });
138
+ it("surfaces skipped non-object entries while keeping the usable ones", async () => {
139
+ const prepared = await preparePiRuntimeConfig({
140
+ env: {
141
+ PAPERCLIP_PI_PROVIDERS: JSON.stringify({
142
+ bad: "http://gw/v1",
143
+ tensorix: { baseUrl: "http://gw/anthropic", apiKey: "k", api: "anthropic-messages", models: [] },
144
+ }),
145
+ },
146
+ });
147
+ const agentConfigDir = prepared.env.PI_CODING_AGENT_DIR;
148
+ cleanupPaths.add(agentConfigDir);
149
+ expect(prepared.agentConfigDir).toBe(agentConfigDir);
150
+ const modelsJson = (await readModelsJson(agentConfigDir));
151
+ expect(modelsJson.providers.tensorix).toBeDefined();
152
+ expect(modelsJson.providers.bad).toBeUndefined();
153
+ expect(prepared.notes).toEqual([
154
+ "PAPERCLIP_PI_PROVIDERS: skipped provider(s) with non-object values: bad.",
155
+ "Injected 1 custom Pi provider(s) from PAPERCLIP_PI_PROVIDERS into a managed models.json: tensorix.",
156
+ ]);
157
+ await prepared.cleanup();
158
+ });
159
+ it("surfaces a note when PAPERCLIP_PI_PROVIDERS contains invalid JSON", async () => {
160
+ const prepared = await preparePiRuntimeConfig({
161
+ env: { PAPERCLIP_PI_PROVIDERS: "{not json" },
162
+ });
163
+ expect(prepared.env.PI_CODING_AGENT_DIR).toBeUndefined();
164
+ expect(prepared.notes).toEqual([
165
+ "PAPERCLIP_PI_PROVIDERS contains invalid JSON; custom providers ignored.",
166
+ ]);
167
+ await prepared.cleanup();
168
+ });
169
+ it("surfaces a note when PAPERCLIP_PI_PROVIDERS is not a JSON object", async () => {
170
+ const prepared = await preparePiRuntimeConfig({
171
+ env: { PAPERCLIP_PI_PROVIDERS: "[1,2]" },
172
+ });
173
+ expect(prepared.notes).toEqual([
174
+ "PAPERCLIP_PI_PROVIDERS is set but is not a JSON object; custom providers ignored.",
175
+ ]);
176
+ await prepared.cleanup();
177
+ });
178
+ it("surfaces the skipped entries when no provider objects remain", async () => {
179
+ const prepared = await preparePiRuntimeConfig({
180
+ env: { PAPERCLIP_PI_PROVIDERS: '{"a": 1}' },
181
+ });
182
+ expect(prepared.env.PI_CODING_AGENT_DIR).toBeUndefined();
183
+ expect(prepared.notes).toEqual([
184
+ "PAPERCLIP_PI_PROVIDERS: skipped provider(s) with non-object values: a.",
185
+ ]);
186
+ await prepared.cleanup();
187
+ });
188
+ it("stays silent when PAPERCLIP_PI_PROVIDERS is an empty object", async () => {
189
+ const prepared = await preparePiRuntimeConfig({
190
+ env: { PAPERCLIP_PI_PROVIDERS: "{}" },
191
+ });
192
+ expect(prepared.env.PI_CODING_AGENT_DIR).toBeUndefined();
193
+ expect(prepared.notes).toEqual([]);
194
+ await prepared.cleanup();
195
+ });
196
+ });
197
+ //# sourceMappingURL=runtime-config.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-config.test.js","sourceRoot":"","sources":["../../src/server/runtime-config.test.ts"],"names":[],"mappings":"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"}