@agimon-ai/po-core 0.0.0-alpha.12 → 0.0.0-alpha.13

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.
@@ -7,7 +7,6 @@ const require_sync = require("./src/services/piSettings/sync.cjs");
7
7
  require("./runtimePiSettings.cjs");
8
8
  const require_index$2 = require("./src/services/chatgptLogin/index.cjs");
9
9
  const require_index$3 = require("./src/services/piExtensionHost/index.cjs");
10
- const require_nodePlatform = require("./nodePlatform.cjs");
11
10
  const require_index$4 = require("./src/services/providerAuth/index.cjs");
12
11
  let node_path = require("node:path");
13
12
  node_path = require_runtime.__toESM(node_path, 1);
@@ -18,83 +17,84 @@ function record(value) {
18
17
  return value !== null && typeof value === "object" && !Array.isArray(value);
19
18
  }
20
19
  /** Machine-owned provider credentials and model catalog, available without a session. */
21
- const machineApi = {
22
- basePath: "po",
23
- start(context) {
24
- if (context.scope !== "global") throw new Error("Provider authentication requires the global mount.");
25
- const auth = require_index$4.createProviderAuth({
26
- onNotice: (message) => context.onNotice(message),
27
- runtime: async () => {
28
- const directory = require_core.piAgentDirectory(context.environment, context.homeDirectory);
29
- const modelDir = require_sync.modelConfigDirectory(directory, context.homeDirectory);
30
- const runtime = await _earendil_works_pi_coding_agent.ModelRuntime.create({
31
- authPath: node_path.default.join(modelDir, "auth.json"),
32
- modelsPath: node_path.default.join(modelDir, "models.json")
33
- });
34
- require_index$2.registerChatgptAuthProvider(runtime);
35
- const onNotice = (message) => context.onNotice(message);
36
- const cwd = context.homeDirectory ?? require_index$1.nodeHost.homeDirectory();
37
- const fs = require_nodePlatform.createNodePlatform({ homeDirectory: cwd }).env(cwd);
38
- const settings = _earendil_works_pi_coding_agent.SettingsManager.create(cwd, directory);
39
- await require_index$3.preloadPiExtensions({
40
- cwd,
41
- agentDir: directory,
42
- models: runtime,
43
- extensionPaths: [...await require_index$3.resolvePiSettingsPackageEntries({
20
+ function createMachineApi({ fs }) {
21
+ return {
22
+ basePath: "po",
23
+ start(context) {
24
+ if (context.scope !== "global") throw new Error("Provider authentication requires the global mount.");
25
+ const auth = require_index$4.createProviderAuth({
26
+ onNotice: (message) => context.onNotice(message),
27
+ runtime: async () => {
28
+ const directory = require_core.piAgentDirectory(context.environment, context.homeDirectory);
29
+ const modelDir = require_sync.modelConfigDirectory(directory, context.homeDirectory);
30
+ const runtime = await _earendil_works_pi_coding_agent.ModelRuntime.create({
31
+ authPath: node_path.default.join(modelDir, "auth.json"),
32
+ modelsPath: node_path.default.join(modelDir, "models.json")
33
+ });
34
+ require_index$2.registerChatgptAuthProvider(runtime);
35
+ const onNotice = (message) => context.onNotice(message);
36
+ const cwd = context.homeDirectory ?? require_index$1.nodeHost.homeDirectory();
37
+ const settings = _earendil_works_pi_coding_agent.SettingsManager.create(cwd, directory);
38
+ await require_index$3.preloadPiExtensions({
44
39
  cwd,
45
40
  agentDir: directory,
46
- settings,
41
+ models: runtime,
42
+ extensionPaths: [...await require_index$3.resolvePiSettingsPackageEntries({
43
+ cwd,
44
+ agentDir: directory,
45
+ settings,
46
+ onNotice
47
+ }), ...modelDir !== directory && await require_index.pathExists(fs, node_path.default.join(modelDir, "extensions"), dist_interop_chordContext_mjs.BACKGROUND_CONTEXT).catch(() => false) ? [node_path.default.join(modelDir, "extensions")] : []],
47
48
  onNotice
48
- }), ...modelDir !== directory && await require_index.pathExists(fs, node_path.default.join(modelDir, "extensions"), dist_interop_chordContext_mjs.BACKGROUND_CONTEXT).catch(() => false) ? [node_path.default.join(modelDir, "extensions")] : []],
49
- onNotice
50
- });
51
- return runtime;
52
- }
53
- });
54
- let closed = false;
55
- return {
56
- async fetch(request) {
57
- if (closed) return Response.json({ error: "Provider mount is closed." }, { status: 503 });
58
- const url = new URL(request.url);
59
- try {
60
- if (url.pathname === "/providers" && request.method === "GET") return Response.json({ providers: await auth.listProviders() });
61
- if (url.pathname === "/models" && request.method === "GET") return Response.json({ models: await auth.listModels() });
62
- const provider = /^\/providers\/([^/]+)$/u.exec(url.pathname);
63
- if (provider && request.method === "DELETE") {
64
- const result = await auth.logout(decodeURIComponent(provider[1]));
65
- return result.ok ? Response.json({ ok: true }) : Response.json({ error: result.error }, { status: result.code === "unknown_provider" ? 404 : 502 });
66
- }
67
- if (url.pathname === "/logins" && request.method === "POST") {
68
- const body = await request.json();
69
- if (!record(body) || typeof body.providerId !== "string" || body.type !== "oauth" && body.type !== "api_key") return Response.json({ error: "A provider and supported login method are required." }, { status: 400 });
70
- const result = await auth.startLogin(body.providerId, body.type);
71
- return result.ok ? Response.json({ flow: result.flow }, { status: 201 }) : Response.json({ error: result.error }, { status: result.code === "unknown_provider" ? 404 : result.code === "busy" ? 409 : 400 });
72
- }
73
- const flow = /^\/logins\/([^/]+)(\/answer)?$/u.exec(url.pathname);
74
- if (flow) {
75
- const id = decodeURIComponent(flow[1]);
76
- if (!auth.getLogin(id)) return Response.json({ error: "Login flow not found." }, { status: 404 });
77
- if (flow[2] && request.method === "POST") {
49
+ });
50
+ return runtime;
51
+ }
52
+ });
53
+ let closed = false;
54
+ return {
55
+ async fetch(request) {
56
+ if (closed) return Response.json({ error: "Provider mount is closed." }, { status: 503 });
57
+ const url = new URL(request.url);
58
+ try {
59
+ if (url.pathname === "/providers" && request.method === "GET") return Response.json({ providers: await auth.listProviders() });
60
+ if (url.pathname === "/models" && request.method === "GET") return Response.json({ models: await auth.listModels() });
61
+ const provider = /^\/providers\/([^/]+)$/u.exec(url.pathname);
62
+ if (provider && request.method === "DELETE") {
63
+ const result = await auth.logout(decodeURIComponent(provider[1]));
64
+ return result.ok ? Response.json({ ok: true }) : Response.json({ error: result.error }, { status: result.code === "unknown_provider" ? 404 : 502 });
65
+ }
66
+ if (url.pathname === "/logins" && request.method === "POST") {
78
67
  const body = await request.json();
79
- if (!record(body) || typeof body.promptId !== "string" || typeof body.value !== "string") return Response.json({ error: "A prompt ID and answer are required." }, { status: 400 });
80
- return auth.answerLogin(id, body.promptId, body.value) === "answered" ? Response.json({ flow: auth.getLogin(id) }) : Response.json({ error: "Login is no longer waiting for this answer." }, { status: 409 });
68
+ if (!record(body) || typeof body.providerId !== "string" || body.type !== "oauth" && body.type !== "api_key") return Response.json({ error: "A provider and supported login method are required." }, { status: 400 });
69
+ const result = await auth.startLogin(body.providerId, body.type);
70
+ return result.ok ? Response.json({ flow: result.flow }, { status: 201 }) : Response.json({ error: result.error }, { status: result.code === "unknown_provider" ? 404 : result.code === "busy" ? 409 : 400 });
71
+ }
72
+ const flow = /^\/logins\/([^/]+)(\/answer)?$/u.exec(url.pathname);
73
+ if (flow) {
74
+ const id = decodeURIComponent(flow[1]);
75
+ if (!auth.getLogin(id)) return Response.json({ error: "Login flow not found." }, { status: 404 });
76
+ if (flow[2] && request.method === "POST") {
77
+ const body = await request.json();
78
+ if (!record(body) || typeof body.promptId !== "string" || typeof body.value !== "string") return Response.json({ error: "A prompt ID and answer are required." }, { status: 400 });
79
+ return auth.answerLogin(id, body.promptId, body.value) === "answered" ? Response.json({ flow: auth.getLogin(id) }) : Response.json({ error: "Login is no longer waiting for this answer." }, { status: 409 });
80
+ }
81
+ if (!flow[2] && request.method === "GET") return Response.json({ flow: auth.getLogin(id) });
82
+ if (!flow[2] && request.method === "DELETE") return Response.json({ flow: auth.cancelLogin(id) });
81
83
  }
82
- if (!flow[2] && request.method === "GET") return Response.json({ flow: auth.getLogin(id) });
83
- if (!flow[2] && request.method === "DELETE") return Response.json({ flow: auth.cancelLogin(id) });
84
+ return Response.json({ error: "Provider route not found." }, { status: 404 });
85
+ } catch (error) {
86
+ return Response.json({ error: error instanceof Error ? error.message : String(error) }, { status: error instanceof SyntaxError ? 400 : 502 });
84
87
  }
85
- return Response.json({ error: "Provider route not found." }, { status: 404 });
86
- } catch (error) {
87
- return Response.json({ error: error instanceof Error ? error.message : String(error) }, { status: error instanceof SyntaxError ? 400 : 502 });
88
+ },
89
+ close() {
90
+ closed = true;
91
+ auth.close();
88
92
  }
89
- },
90
- close() {
91
- closed = true;
92
- auth.close();
93
- }
94
- };
95
- }
96
- };
93
+ };
94
+ }
95
+ };
96
+ }
97
97
  //#endregion
98
- exports.machineApi = machineApi;
98
+ exports.createMachineApi = createMachineApi;
99
99
 
100
100
  //# sourceMappingURL=machineApi.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"machineApi.cjs","names":["createProviderAuth","piAgentDirectory","modelConfigDirectory","ModelRuntime","path","nodeHost","createNodePlatform","SettingsManager","preloadPiExtensions","resolvePiSettingsPackageEntries","pathExists","BACKGROUND_CONTEXT"],"sources":["../src/server/machineApi.ts"],"sourcesContent":["import path from 'node:path';\n\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport { ModelRuntime, SettingsManager } from '@earendil-works/pi-coding-agent';\n\nimport type { PoApi } from '../exports/packageApi';\nimport { registerChatgptAuthProvider } from '../services/chatgptLogin';\nimport { nodeHost } from '../services/nodeHost';\nimport { createNodePlatform } from '../services/nodePlatform';\nimport { preloadPiExtensions, resolvePiSettingsPackageEntries } from '../services/piExtensionHost';\nimport { modelConfigDirectory, piAgentDirectory } from '../services/piSettings';\nimport { pathExists } from '../services/platformFiles';\nimport { createProviderAuth } from '../services/providerAuth';\n\nfunction record(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n/** Machine-owned provider credentials and model catalog, available without a session. */\nexport const machineApi: PoApi = {\n basePath: 'po',\n start(context) {\n if (context.scope !== 'global') throw new Error('Provider authentication requires the global mount.');\n const auth = createProviderAuth({\n onNotice: (message) => context.onNotice(message),\n runtime: async () => {\n const directory = piAgentDirectory(context.environment, context.homeDirectory);\n // Resolved as sessions resolve it (headlessSessionHost): with no model config of its\n // own, Po falls back to plain Pi's ~/.pi/agent, so this catalog matches the session picker.\n const modelDir = modelConfigDirectory(directory, context.homeDirectory);\n const runtime = await ModelRuntime.create({\n authPath: path.join(modelDir, 'auth.json'),\n modelsPath: path.join(modelDir, 'models.json'),\n });\n registerChatgptAuthProvider(runtime);\n // Providers registered by Pi settings packages (a Claude Code bridge, say) are\n // listed and pickable here as they are in sessions, which load the same packages.\n const onNotice = (message: string): void => context.onNotice(message);\n // Machine scope has no project, so the home directory stands in for Pi's cwd.\n const cwd = context.homeDirectory ?? nodeHost.homeDirectory();\n const fs = createNodePlatform({ homeDirectory: cwd }).env(cwd);\n const settings = SettingsManager.create(cwd, directory);\n await preloadPiExtensions({\n cwd,\n agentDir: directory,\n models: runtime,\n extensionPaths: [\n ...(await resolvePiSettingsPackageEntries({\n cwd,\n agentDir: directory,\n settings,\n onNotice,\n })),\n // The fallback brings Pi's own extensions with it, as sessions load them: a custom\n // provider such as a local Vertex bridge lives there, not in auth.json.\n ...(modelDir !== directory &&\n (await pathExists(fs, path.join(modelDir, 'extensions'), BACKGROUND_CONTEXT).catch(() => false))\n ? [path.join(modelDir, 'extensions')]\n : []),\n ],\n onNotice,\n });\n return runtime;\n },\n });\n let closed = false;\n return {\n async fetch(request) {\n if (closed) return Response.json({ error: 'Provider mount is closed.' }, { status: 503 });\n const url = new URL(request.url);\n try {\n if (url.pathname === '/providers' && request.method === 'GET')\n return Response.json({ providers: await auth.listProviders() });\n if (url.pathname === '/models' && request.method === 'GET')\n return Response.json({ models: await auth.listModels() });\n const provider = /^\\/providers\\/([^/]+)$/u.exec(url.pathname);\n if (provider && request.method === 'DELETE') {\n const result = await auth.logout(decodeURIComponent(provider[1]));\n return result.ok\n ? Response.json({ ok: true })\n : Response.json({ error: result.error }, { status: result.code === 'unknown_provider' ? 404 : 502 });\n }\n if (url.pathname === '/logins' && request.method === 'POST') {\n const body: unknown = await request.json();\n if (\n !record(body) ||\n typeof body.providerId !== 'string' ||\n (body.type !== 'oauth' && body.type !== 'api_key')\n )\n return Response.json({ error: 'A provider and supported login method are required.' }, { status: 400 });\n const result = await auth.startLogin(body.providerId, body.type);\n return result.ok\n ? Response.json({ flow: result.flow }, { status: 201 })\n : Response.json(\n { error: result.error },\n { status: result.code === 'unknown_provider' ? 404 : result.code === 'busy' ? 409 : 400 },\n );\n }\n const flow = /^\\/logins\\/([^/]+)(\\/answer)?$/u.exec(url.pathname);\n if (flow) {\n const id = decodeURIComponent(flow[1]);\n if (!auth.getLogin(id)) return Response.json({ error: 'Login flow not found.' }, { status: 404 });\n if (flow[2] && request.method === 'POST') {\n const body: unknown = await request.json();\n if (!record(body) || typeof body.promptId !== 'string' || typeof body.value !== 'string')\n return Response.json({ error: 'A prompt ID and answer are required.' }, { status: 400 });\n const answer = auth.answerLogin(id, body.promptId, body.value);\n return answer === 'answered'\n ? Response.json({ flow: auth.getLogin(id) })\n : Response.json({ error: 'Login is no longer waiting for this answer.' }, { status: 409 });\n }\n if (!flow[2] && request.method === 'GET') return Response.json({ flow: auth.getLogin(id) });\n if (!flow[2] && request.method === 'DELETE') return Response.json({ flow: auth.cancelLogin(id) });\n }\n return Response.json({ error: 'Provider route not found.' }, { status: 404 });\n } catch (error) {\n return Response.json(\n { error: error instanceof Error ? error.message : String(error) },\n { status: error instanceof SyntaxError ? 400 : 502 },\n );\n }\n },\n close() {\n closed = true;\n auth.close();\n },\n };\n },\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"machineApi.cjs","names":["createProviderAuth","piAgentDirectory","modelConfigDirectory","ModelRuntime","path","nodeHost","SettingsManager","preloadPiExtensions","resolvePiSettingsPackageEntries","pathExists","BACKGROUND_CONTEXT"],"sources":["../src/server/machineApi.ts"],"sourcesContent":["import path from 'node:path';\n\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport { ModelRuntime, SettingsManager } from '@earendil-works/pi-coding-agent';\nimport type { FileSystem } from '@earendil-works/pi-durable/env';\n\nimport type { PoApi } from '../exports/packageApi';\nimport { registerChatgptAuthProvider } from '../services/chatgptLogin';\nimport { nodeHost } from '../services/nodeHost';\nimport { preloadPiExtensions, resolvePiSettingsPackageEntries } from '../services/piExtensionHost';\nimport { modelConfigDirectory, piAgentDirectory } from '../services/piSettings';\nimport { pathExists } from '../services/platformFiles';\nimport { createProviderAuth } from '../services/providerAuth';\n\nfunction record(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n/** Machine-owned provider credentials and model catalog, available without a session. */\nexport function createMachineApi({ fs }: { fs: FileSystem }): PoApi {\n return {\n basePath: 'po',\n start(context) {\n if (context.scope !== 'global') throw new Error('Provider authentication requires the global mount.');\n const auth = createProviderAuth({\n onNotice: (message) => context.onNotice(message),\n runtime: async () => {\n const directory = piAgentDirectory(context.environment, context.homeDirectory);\n // Resolved as sessions resolve it (headlessSessionHost): with no model config of its\n // own, Po falls back to plain Pi's ~/.pi/agent, so this catalog matches the session picker.\n const modelDir = modelConfigDirectory(directory, context.homeDirectory);\n const runtime = await ModelRuntime.create({\n authPath: path.join(modelDir, 'auth.json'),\n modelsPath: path.join(modelDir, 'models.json'),\n });\n registerChatgptAuthProvider(runtime);\n // Providers registered by Pi settings packages (a Claude Code bridge, say) are\n // listed and pickable here as they are in sessions, which load the same packages.\n const onNotice = (message: string): void => context.onNotice(message);\n // Machine scope has no project, so the home directory stands in for Pi's cwd.\n const cwd = context.homeDirectory ?? nodeHost.homeDirectory();\n const settings = SettingsManager.create(cwd, directory);\n await preloadPiExtensions({\n cwd,\n agentDir: directory,\n models: runtime,\n extensionPaths: [\n ...(await resolvePiSettingsPackageEntries({\n cwd,\n agentDir: directory,\n settings,\n onNotice,\n })),\n // The fallback brings Pi's own extensions with it, as sessions load them: a custom\n // provider such as a local Vertex bridge lives there, not in auth.json.\n ...(modelDir !== directory &&\n (await pathExists(fs, path.join(modelDir, 'extensions'), BACKGROUND_CONTEXT).catch(() => false))\n ? [path.join(modelDir, 'extensions')]\n : []),\n ],\n onNotice,\n });\n return runtime;\n },\n });\n let closed = false;\n return {\n async fetch(request) {\n if (closed) return Response.json({ error: 'Provider mount is closed.' }, { status: 503 });\n const url = new URL(request.url);\n try {\n if (url.pathname === '/providers' && request.method === 'GET')\n return Response.json({ providers: await auth.listProviders() });\n if (url.pathname === '/models' && request.method === 'GET')\n return Response.json({ models: await auth.listModels() });\n const provider = /^\\/providers\\/([^/]+)$/u.exec(url.pathname);\n if (provider && request.method === 'DELETE') {\n const result = await auth.logout(decodeURIComponent(provider[1]));\n return result.ok\n ? Response.json({ ok: true })\n : Response.json({ error: result.error }, { status: result.code === 'unknown_provider' ? 404 : 502 });\n }\n if (url.pathname === '/logins' && request.method === 'POST') {\n const body: unknown = await request.json();\n if (\n !record(body) ||\n typeof body.providerId !== 'string' ||\n (body.type !== 'oauth' && body.type !== 'api_key')\n )\n return Response.json({ error: 'A provider and supported login method are required.' }, { status: 400 });\n const result = await auth.startLogin(body.providerId, body.type);\n return result.ok\n ? Response.json({ flow: result.flow }, { status: 201 })\n : Response.json(\n { error: result.error },\n { status: result.code === 'unknown_provider' ? 404 : result.code === 'busy' ? 409 : 400 },\n );\n }\n const flow = /^\\/logins\\/([^/]+)(\\/answer)?$/u.exec(url.pathname);\n if (flow) {\n const id = decodeURIComponent(flow[1]);\n if (!auth.getLogin(id)) return Response.json({ error: 'Login flow not found.' }, { status: 404 });\n if (flow[2] && request.method === 'POST') {\n const body: unknown = await request.json();\n if (!record(body) || typeof body.promptId !== 'string' || typeof body.value !== 'string')\n return Response.json({ error: 'A prompt ID and answer are required.' }, { status: 400 });\n const answer = auth.answerLogin(id, body.promptId, body.value);\n return answer === 'answered'\n ? Response.json({ flow: auth.getLogin(id) })\n : Response.json({ error: 'Login is no longer waiting for this answer.' }, { status: 409 });\n }\n if (!flow[2] && request.method === 'GET') return Response.json({ flow: auth.getLogin(id) });\n if (!flow[2] && request.method === 'DELETE') return Response.json({ flow: auth.cancelLogin(id) });\n }\n return Response.json({ error: 'Provider route not found.' }, { status: 404 });\n } catch (error) {\n return Response.json(\n { error: error instanceof Error ? error.message : String(error) },\n { status: error instanceof SyntaxError ? 400 : 502 },\n );\n }\n },\n close() {\n closed = true;\n auth.close();\n },\n };\n },\n };\n}\n"],"mappings":"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"}
@@ -1,7 +1,10 @@
1
1
  import { PoApi } from "./src/schemas/packageApi.mjs";
2
2
  import "./packageApi.mjs";
3
+ import { FileSystem } from "@earendil-works/pi-durable/env";
3
4
  //#region src/server/machineApi.d.ts
4
5
  /** Machine-owned provider credentials and model catalog, available without a session. */
5
- export declare const machineApi: PoApi;
6
+ export declare function createMachineApi({ fs }: {
7
+ fs: FileSystem;
8
+ }): PoApi;
6
9
  //#endregion
7
10
  //# sourceMappingURL=machineApi.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"machineApi.d.mts","names":[],"sources":["../src/server/machineApi.ts"],"mappings":";;;;qBAkBa,YAAY"}
1
+ {"version":3,"file":"machineApi.d.mts","names":[],"sources":["../src/server/machineApi.ts"],"mappings":";;;;;wBAkBgB,mBAAmB;EAAQ,IAAI;IAAe"}
@@ -5,7 +5,6 @@ import { modelConfigDirectory } from "./src/services/piSettings/sync.mjs";
5
5
  import "./runtimePiSettings.mjs";
6
6
  import { registerChatgptAuthProvider } from "./src/services/chatgptLogin/index.mjs";
7
7
  import { preloadPiExtensions, resolvePiSettingsPackageEntries } from "./src/services/piExtensionHost/index.mjs";
8
- import { createNodePlatform } from "./nodePlatform.mjs";
9
8
  import { createProviderAuth } from "./src/services/providerAuth/index.mjs";
10
9
  import path from "node:path";
11
10
  import { ModelRuntime, SettingsManager } from "@earendil-works/pi-coding-agent";
@@ -15,83 +14,84 @@ function record(value) {
15
14
  return value !== null && typeof value === "object" && !Array.isArray(value);
16
15
  }
17
16
  /** Machine-owned provider credentials and model catalog, available without a session. */
18
- const machineApi = {
19
- basePath: "po",
20
- start(context) {
21
- if (context.scope !== "global") throw new Error("Provider authentication requires the global mount.");
22
- const auth = createProviderAuth({
23
- onNotice: (message) => context.onNotice(message),
24
- runtime: async () => {
25
- const directory = piAgentDirectory(context.environment, context.homeDirectory);
26
- const modelDir = modelConfigDirectory(directory, context.homeDirectory);
27
- const runtime = await ModelRuntime.create({
28
- authPath: path.join(modelDir, "auth.json"),
29
- modelsPath: path.join(modelDir, "models.json")
30
- });
31
- registerChatgptAuthProvider(runtime);
32
- const onNotice = (message) => context.onNotice(message);
33
- const cwd = context.homeDirectory ?? nodeHost.homeDirectory();
34
- const fs = createNodePlatform({ homeDirectory: cwd }).env(cwd);
35
- const settings = SettingsManager.create(cwd, directory);
36
- await preloadPiExtensions({
37
- cwd,
38
- agentDir: directory,
39
- models: runtime,
40
- extensionPaths: [...await resolvePiSettingsPackageEntries({
17
+ function createMachineApi({ fs }) {
18
+ return {
19
+ basePath: "po",
20
+ start(context) {
21
+ if (context.scope !== "global") throw new Error("Provider authentication requires the global mount.");
22
+ const auth = createProviderAuth({
23
+ onNotice: (message) => context.onNotice(message),
24
+ runtime: async () => {
25
+ const directory = piAgentDirectory(context.environment, context.homeDirectory);
26
+ const modelDir = modelConfigDirectory(directory, context.homeDirectory);
27
+ const runtime = await ModelRuntime.create({
28
+ authPath: path.join(modelDir, "auth.json"),
29
+ modelsPath: path.join(modelDir, "models.json")
30
+ });
31
+ registerChatgptAuthProvider(runtime);
32
+ const onNotice = (message) => context.onNotice(message);
33
+ const cwd = context.homeDirectory ?? nodeHost.homeDirectory();
34
+ const settings = SettingsManager.create(cwd, directory);
35
+ await preloadPiExtensions({
41
36
  cwd,
42
37
  agentDir: directory,
43
- settings,
38
+ models: runtime,
39
+ extensionPaths: [...await resolvePiSettingsPackageEntries({
40
+ cwd,
41
+ agentDir: directory,
42
+ settings,
43
+ onNotice
44
+ }), ...modelDir !== directory && await pathExists(fs, path.join(modelDir, "extensions"), BACKGROUND_CONTEXT).catch(() => false) ? [path.join(modelDir, "extensions")] : []],
44
45
  onNotice
45
- }), ...modelDir !== directory && await pathExists(fs, path.join(modelDir, "extensions"), BACKGROUND_CONTEXT).catch(() => false) ? [path.join(modelDir, "extensions")] : []],
46
- onNotice
47
- });
48
- return runtime;
49
- }
50
- });
51
- let closed = false;
52
- return {
53
- async fetch(request) {
54
- if (closed) return Response.json({ error: "Provider mount is closed." }, { status: 503 });
55
- const url = new URL(request.url);
56
- try {
57
- if (url.pathname === "/providers" && request.method === "GET") return Response.json({ providers: await auth.listProviders() });
58
- if (url.pathname === "/models" && request.method === "GET") return Response.json({ models: await auth.listModels() });
59
- const provider = /^\/providers\/([^/]+)$/u.exec(url.pathname);
60
- if (provider && request.method === "DELETE") {
61
- const result = await auth.logout(decodeURIComponent(provider[1]));
62
- return result.ok ? Response.json({ ok: true }) : Response.json({ error: result.error }, { status: result.code === "unknown_provider" ? 404 : 502 });
63
- }
64
- if (url.pathname === "/logins" && request.method === "POST") {
65
- const body = await request.json();
66
- if (!record(body) || typeof body.providerId !== "string" || body.type !== "oauth" && body.type !== "api_key") return Response.json({ error: "A provider and supported login method are required." }, { status: 400 });
67
- const result = await auth.startLogin(body.providerId, body.type);
68
- return result.ok ? Response.json({ flow: result.flow }, { status: 201 }) : Response.json({ error: result.error }, { status: result.code === "unknown_provider" ? 404 : result.code === "busy" ? 409 : 400 });
69
- }
70
- const flow = /^\/logins\/([^/]+)(\/answer)?$/u.exec(url.pathname);
71
- if (flow) {
72
- const id = decodeURIComponent(flow[1]);
73
- if (!auth.getLogin(id)) return Response.json({ error: "Login flow not found." }, { status: 404 });
74
- if (flow[2] && request.method === "POST") {
46
+ });
47
+ return runtime;
48
+ }
49
+ });
50
+ let closed = false;
51
+ return {
52
+ async fetch(request) {
53
+ if (closed) return Response.json({ error: "Provider mount is closed." }, { status: 503 });
54
+ const url = new URL(request.url);
55
+ try {
56
+ if (url.pathname === "/providers" && request.method === "GET") return Response.json({ providers: await auth.listProviders() });
57
+ if (url.pathname === "/models" && request.method === "GET") return Response.json({ models: await auth.listModels() });
58
+ const provider = /^\/providers\/([^/]+)$/u.exec(url.pathname);
59
+ if (provider && request.method === "DELETE") {
60
+ const result = await auth.logout(decodeURIComponent(provider[1]));
61
+ return result.ok ? Response.json({ ok: true }) : Response.json({ error: result.error }, { status: result.code === "unknown_provider" ? 404 : 502 });
62
+ }
63
+ if (url.pathname === "/logins" && request.method === "POST") {
75
64
  const body = await request.json();
76
- if (!record(body) || typeof body.promptId !== "string" || typeof body.value !== "string") return Response.json({ error: "A prompt ID and answer are required." }, { status: 400 });
77
- return auth.answerLogin(id, body.promptId, body.value) === "answered" ? Response.json({ flow: auth.getLogin(id) }) : Response.json({ error: "Login is no longer waiting for this answer." }, { status: 409 });
65
+ if (!record(body) || typeof body.providerId !== "string" || body.type !== "oauth" && body.type !== "api_key") return Response.json({ error: "A provider and supported login method are required." }, { status: 400 });
66
+ const result = await auth.startLogin(body.providerId, body.type);
67
+ return result.ok ? Response.json({ flow: result.flow }, { status: 201 }) : Response.json({ error: result.error }, { status: result.code === "unknown_provider" ? 404 : result.code === "busy" ? 409 : 400 });
68
+ }
69
+ const flow = /^\/logins\/([^/]+)(\/answer)?$/u.exec(url.pathname);
70
+ if (flow) {
71
+ const id = decodeURIComponent(flow[1]);
72
+ if (!auth.getLogin(id)) return Response.json({ error: "Login flow not found." }, { status: 404 });
73
+ if (flow[2] && request.method === "POST") {
74
+ const body = await request.json();
75
+ if (!record(body) || typeof body.promptId !== "string" || typeof body.value !== "string") return Response.json({ error: "A prompt ID and answer are required." }, { status: 400 });
76
+ return auth.answerLogin(id, body.promptId, body.value) === "answered" ? Response.json({ flow: auth.getLogin(id) }) : Response.json({ error: "Login is no longer waiting for this answer." }, { status: 409 });
77
+ }
78
+ if (!flow[2] && request.method === "GET") return Response.json({ flow: auth.getLogin(id) });
79
+ if (!flow[2] && request.method === "DELETE") return Response.json({ flow: auth.cancelLogin(id) });
78
80
  }
79
- if (!flow[2] && request.method === "GET") return Response.json({ flow: auth.getLogin(id) });
80
- if (!flow[2] && request.method === "DELETE") return Response.json({ flow: auth.cancelLogin(id) });
81
+ return Response.json({ error: "Provider route not found." }, { status: 404 });
82
+ } catch (error) {
83
+ return Response.json({ error: error instanceof Error ? error.message : String(error) }, { status: error instanceof SyntaxError ? 400 : 502 });
81
84
  }
82
- return Response.json({ error: "Provider route not found." }, { status: 404 });
83
- } catch (error) {
84
- return Response.json({ error: error instanceof Error ? error.message : String(error) }, { status: error instanceof SyntaxError ? 400 : 502 });
85
+ },
86
+ close() {
87
+ closed = true;
88
+ auth.close();
85
89
  }
86
- },
87
- close() {
88
- closed = true;
89
- auth.close();
90
- }
91
- };
92
- }
93
- };
90
+ };
91
+ }
92
+ };
93
+ }
94
94
  //#endregion
95
- export { machineApi };
95
+ export { createMachineApi };
96
96
 
97
97
  //# sourceMappingURL=machineApi.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"machineApi.mjs","names":[],"sources":["../src/server/machineApi.ts"],"sourcesContent":["import path from 'node:path';\n\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport { ModelRuntime, SettingsManager } from '@earendil-works/pi-coding-agent';\n\nimport type { PoApi } from '../exports/packageApi';\nimport { registerChatgptAuthProvider } from '../services/chatgptLogin';\nimport { nodeHost } from '../services/nodeHost';\nimport { createNodePlatform } from '../services/nodePlatform';\nimport { preloadPiExtensions, resolvePiSettingsPackageEntries } from '../services/piExtensionHost';\nimport { modelConfigDirectory, piAgentDirectory } from '../services/piSettings';\nimport { pathExists } from '../services/platformFiles';\nimport { createProviderAuth } from '../services/providerAuth';\n\nfunction record(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n/** Machine-owned provider credentials and model catalog, available without a session. */\nexport const machineApi: PoApi = {\n basePath: 'po',\n start(context) {\n if (context.scope !== 'global') throw new Error('Provider authentication requires the global mount.');\n const auth = createProviderAuth({\n onNotice: (message) => context.onNotice(message),\n runtime: async () => {\n const directory = piAgentDirectory(context.environment, context.homeDirectory);\n // Resolved as sessions resolve it (headlessSessionHost): with no model config of its\n // own, Po falls back to plain Pi's ~/.pi/agent, so this catalog matches the session picker.\n const modelDir = modelConfigDirectory(directory, context.homeDirectory);\n const runtime = await ModelRuntime.create({\n authPath: path.join(modelDir, 'auth.json'),\n modelsPath: path.join(modelDir, 'models.json'),\n });\n registerChatgptAuthProvider(runtime);\n // Providers registered by Pi settings packages (a Claude Code bridge, say) are\n // listed and pickable here as they are in sessions, which load the same packages.\n const onNotice = (message: string): void => context.onNotice(message);\n // Machine scope has no project, so the home directory stands in for Pi's cwd.\n const cwd = context.homeDirectory ?? nodeHost.homeDirectory();\n const fs = createNodePlatform({ homeDirectory: cwd }).env(cwd);\n const settings = SettingsManager.create(cwd, directory);\n await preloadPiExtensions({\n cwd,\n agentDir: directory,\n models: runtime,\n extensionPaths: [\n ...(await resolvePiSettingsPackageEntries({\n cwd,\n agentDir: directory,\n settings,\n onNotice,\n })),\n // The fallback brings Pi's own extensions with it, as sessions load them: a custom\n // provider such as a local Vertex bridge lives there, not in auth.json.\n ...(modelDir !== directory &&\n (await pathExists(fs, path.join(modelDir, 'extensions'), BACKGROUND_CONTEXT).catch(() => false))\n ? [path.join(modelDir, 'extensions')]\n : []),\n ],\n onNotice,\n });\n return runtime;\n },\n });\n let closed = false;\n return {\n async fetch(request) {\n if (closed) return Response.json({ error: 'Provider mount is closed.' }, { status: 503 });\n const url = new URL(request.url);\n try {\n if (url.pathname === '/providers' && request.method === 'GET')\n return Response.json({ providers: await auth.listProviders() });\n if (url.pathname === '/models' && request.method === 'GET')\n return Response.json({ models: await auth.listModels() });\n const provider = /^\\/providers\\/([^/]+)$/u.exec(url.pathname);\n if (provider && request.method === 'DELETE') {\n const result = await auth.logout(decodeURIComponent(provider[1]));\n return result.ok\n ? Response.json({ ok: true })\n : Response.json({ error: result.error }, { status: result.code === 'unknown_provider' ? 404 : 502 });\n }\n if (url.pathname === '/logins' && request.method === 'POST') {\n const body: unknown = await request.json();\n if (\n !record(body) ||\n typeof body.providerId !== 'string' ||\n (body.type !== 'oauth' && body.type !== 'api_key')\n )\n return Response.json({ error: 'A provider and supported login method are required.' }, { status: 400 });\n const result = await auth.startLogin(body.providerId, body.type);\n return result.ok\n ? Response.json({ flow: result.flow }, { status: 201 })\n : Response.json(\n { error: result.error },\n { status: result.code === 'unknown_provider' ? 404 : result.code === 'busy' ? 409 : 400 },\n );\n }\n const flow = /^\\/logins\\/([^/]+)(\\/answer)?$/u.exec(url.pathname);\n if (flow) {\n const id = decodeURIComponent(flow[1]);\n if (!auth.getLogin(id)) return Response.json({ error: 'Login flow not found.' }, { status: 404 });\n if (flow[2] && request.method === 'POST') {\n const body: unknown = await request.json();\n if (!record(body) || typeof body.promptId !== 'string' || typeof body.value !== 'string')\n return Response.json({ error: 'A prompt ID and answer are required.' }, { status: 400 });\n const answer = auth.answerLogin(id, body.promptId, body.value);\n return answer === 'answered'\n ? Response.json({ flow: auth.getLogin(id) })\n : Response.json({ error: 'Login is no longer waiting for this answer.' }, { status: 409 });\n }\n if (!flow[2] && request.method === 'GET') return Response.json({ flow: auth.getLogin(id) });\n if (!flow[2] && request.method === 'DELETE') return Response.json({ flow: auth.cancelLogin(id) });\n }\n return Response.json({ error: 'Provider route not found.' }, { status: 404 });\n } catch (error) {\n return Response.json(\n { error: error instanceof Error ? error.message : String(error) },\n { status: error instanceof SyntaxError ? 400 : 502 },\n );\n }\n },\n close() {\n closed = true;\n auth.close();\n },\n };\n },\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"machineApi.mjs","names":[],"sources":["../src/server/machineApi.ts"],"sourcesContent":["import path from 'node:path';\n\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport { ModelRuntime, SettingsManager } from '@earendil-works/pi-coding-agent';\nimport type { FileSystem } from '@earendil-works/pi-durable/env';\n\nimport type { PoApi } from '../exports/packageApi';\nimport { registerChatgptAuthProvider } from '../services/chatgptLogin';\nimport { nodeHost } from '../services/nodeHost';\nimport { preloadPiExtensions, resolvePiSettingsPackageEntries } from '../services/piExtensionHost';\nimport { modelConfigDirectory, piAgentDirectory } from '../services/piSettings';\nimport { pathExists } from '../services/platformFiles';\nimport { createProviderAuth } from '../services/providerAuth';\n\nfunction record(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n/** Machine-owned provider credentials and model catalog, available without a session. */\nexport function createMachineApi({ fs }: { fs: FileSystem }): PoApi {\n return {\n basePath: 'po',\n start(context) {\n if (context.scope !== 'global') throw new Error('Provider authentication requires the global mount.');\n const auth = createProviderAuth({\n onNotice: (message) => context.onNotice(message),\n runtime: async () => {\n const directory = piAgentDirectory(context.environment, context.homeDirectory);\n // Resolved as sessions resolve it (headlessSessionHost): with no model config of its\n // own, Po falls back to plain Pi's ~/.pi/agent, so this catalog matches the session picker.\n const modelDir = modelConfigDirectory(directory, context.homeDirectory);\n const runtime = await ModelRuntime.create({\n authPath: path.join(modelDir, 'auth.json'),\n modelsPath: path.join(modelDir, 'models.json'),\n });\n registerChatgptAuthProvider(runtime);\n // Providers registered by Pi settings packages (a Claude Code bridge, say) are\n // listed and pickable here as they are in sessions, which load the same packages.\n const onNotice = (message: string): void => context.onNotice(message);\n // Machine scope has no project, so the home directory stands in for Pi's cwd.\n const cwd = context.homeDirectory ?? nodeHost.homeDirectory();\n const settings = SettingsManager.create(cwd, directory);\n await preloadPiExtensions({\n cwd,\n agentDir: directory,\n models: runtime,\n extensionPaths: [\n ...(await resolvePiSettingsPackageEntries({\n cwd,\n agentDir: directory,\n settings,\n onNotice,\n })),\n // The fallback brings Pi's own extensions with it, as sessions load them: a custom\n // provider such as a local Vertex bridge lives there, not in auth.json.\n ...(modelDir !== directory &&\n (await pathExists(fs, path.join(modelDir, 'extensions'), BACKGROUND_CONTEXT).catch(() => false))\n ? [path.join(modelDir, 'extensions')]\n : []),\n ],\n onNotice,\n });\n return runtime;\n },\n });\n let closed = false;\n return {\n async fetch(request) {\n if (closed) return Response.json({ error: 'Provider mount is closed.' }, { status: 503 });\n const url = new URL(request.url);\n try {\n if (url.pathname === '/providers' && request.method === 'GET')\n return Response.json({ providers: await auth.listProviders() });\n if (url.pathname === '/models' && request.method === 'GET')\n return Response.json({ models: await auth.listModels() });\n const provider = /^\\/providers\\/([^/]+)$/u.exec(url.pathname);\n if (provider && request.method === 'DELETE') {\n const result = await auth.logout(decodeURIComponent(provider[1]));\n return result.ok\n ? Response.json({ ok: true })\n : Response.json({ error: result.error }, { status: result.code === 'unknown_provider' ? 404 : 502 });\n }\n if (url.pathname === '/logins' && request.method === 'POST') {\n const body: unknown = await request.json();\n if (\n !record(body) ||\n typeof body.providerId !== 'string' ||\n (body.type !== 'oauth' && body.type !== 'api_key')\n )\n return Response.json({ error: 'A provider and supported login method are required.' }, { status: 400 });\n const result = await auth.startLogin(body.providerId, body.type);\n return result.ok\n ? Response.json({ flow: result.flow }, { status: 201 })\n : Response.json(\n { error: result.error },\n { status: result.code === 'unknown_provider' ? 404 : result.code === 'busy' ? 409 : 400 },\n );\n }\n const flow = /^\\/logins\\/([^/]+)(\\/answer)?$/u.exec(url.pathname);\n if (flow) {\n const id = decodeURIComponent(flow[1]);\n if (!auth.getLogin(id)) return Response.json({ error: 'Login flow not found.' }, { status: 404 });\n if (flow[2] && request.method === 'POST') {\n const body: unknown = await request.json();\n if (!record(body) || typeof body.promptId !== 'string' || typeof body.value !== 'string')\n return Response.json({ error: 'A prompt ID and answer are required.' }, { status: 400 });\n const answer = auth.answerLogin(id, body.promptId, body.value);\n return answer === 'answered'\n ? Response.json({ flow: auth.getLogin(id) })\n : Response.json({ error: 'Login is no longer waiting for this answer.' }, { status: 409 });\n }\n if (!flow[2] && request.method === 'GET') return Response.json({ flow: auth.getLogin(id) });\n if (!flow[2] && request.method === 'DELETE') return Response.json({ flow: auth.cancelLogin(id) });\n }\n return Response.json({ error: 'Provider route not found.' }, { status: 404 });\n } catch (error) {\n return Response.json(\n { error: error instanceof Error ? error.message : String(error) },\n { status: error instanceof SyntaxError ? 400 : 502 },\n );\n }\n },\n close() {\n closed = true;\n auth.close();\n },\n };\n },\n };\n}\n"],"mappings":"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"}
@@ -1,6 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_runtime = require("./_virtual/_rolldown/runtime.cjs");
3
- const require_nodePlatform = require("./nodePlatform.cjs");
4
3
  let node_path = require("node:path");
5
4
  node_path = require_runtime.__toESM(node_path, 1);
6
5
  let _earendil_works_pi_durable_env = require("@earendil-works/pi-durable/env");
@@ -67,34 +66,35 @@ async function listSessionFiles(fs, cwd, query, signal) {
67
66
  }
68
67
  return matches.sort().slice(0, MAX_FILE_RESULTS);
69
68
  }
70
- /** Bounded, read-only file completion rooted in the owning session. */
71
- const sessionFilesApi = {
72
- basePath: "files",
73
- start(context) {
74
- if (context.scope !== "session" || !context.cwd) throw new Error("File completion requires a session.");
75
- const cwd = context.cwd;
76
- const fs = require_nodePlatform.createNodePlatform({ homeDirectory: context.homeDirectory }).env(cwd);
77
- let closed = false;
78
- return {
79
- async fetch(request) {
80
- if (closed) return new Response(null, { status: 503 });
81
- const url = new URL(request.url);
82
- if (request.method !== "GET" || url.pathname !== "/") return new Response(null, { status: 404 });
83
- const query = (url.searchParams.get("q") ?? "").toLowerCase();
84
- if (query.length > 256) return Response.json({ error: "File query is too long." }, { status: 400 });
85
- try {
86
- return Response.json({ files: await listSessionFiles(fs, cwd, query, request.signal) });
87
- } catch {
88
- return Response.json({ error: "File completion could not be read." }, { status: 503 });
69
+ /** Bounded, read-only file completion rooted in the owning session, read through the server's platform fs. */
70
+ function createSessionFilesApi({ fs }) {
71
+ return {
72
+ basePath: "files",
73
+ start(context) {
74
+ if (context.scope !== "session" || !context.cwd) throw new Error("File completion requires a session.");
75
+ const cwd = context.cwd;
76
+ let closed = false;
77
+ return {
78
+ async fetch(request) {
79
+ if (closed) return new Response(null, { status: 503 });
80
+ const url = new URL(request.url);
81
+ if (request.method !== "GET" || url.pathname !== "/") return new Response(null, { status: 404 });
82
+ const query = (url.searchParams.get("q") ?? "").toLowerCase();
83
+ if (query.length > 256) return Response.json({ error: "File query is too long." }, { status: 400 });
84
+ try {
85
+ return Response.json({ files: await listSessionFiles(fs, cwd, query, request.signal) });
86
+ } catch {
87
+ return Response.json({ error: "File completion could not be read." }, { status: 503 });
88
+ }
89
+ },
90
+ close() {
91
+ closed = true;
89
92
  }
90
- },
91
- close() {
92
- closed = true;
93
- }
94
- };
95
- }
96
- };
93
+ };
94
+ }
95
+ };
96
+ }
97
97
  //#endregion
98
- exports.sessionFilesApi = sessionFilesApi;
98
+ exports.createSessionFilesApi = createSessionFilesApi;
99
99
 
100
100
  //# sourceMappingURL=sessionFilesApi.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"sessionFilesApi.cjs","names":["path","withAbortSignal","BACKGROUND_CONTEXT","getOrThrow","createNodePlatform"],"sources":["../src/server/sessionFilesApi.ts"],"sourcesContent":["import path from 'node:path';\n\nimport type { Context } from '@earendil-works/chord';\nimport { BACKGROUND_CONTEXT, withAbortSignal } from '@earendil-works/chord/context';\nimport { getOrThrow, type FileSystem } from '@earendil-works/pi-durable/env';\nimport ignore from 'ignore';\n\nimport type { PoApi } from '../exports/packageApi';\nimport { createNodePlatform } from '../services/nodePlatform';\n\nconst EXCLUDED_DIRECTORIES = new Set(['.git', 'node_modules']);\nconst IGNORE_FILES = ['.gitignore', '.poignore'];\nconst FILE_QUERY_TIMEOUT_MS = 2000;\nconst MAX_FILE_RESULTS = 50;\n\n/**\n * The project's ignore test, or nothing when it declares no rules.\n *\n * Only the session root's files are read. A nested .gitignore would need one\n * matcher per directory, and the cost of skipping it is a suggestion that\n * should not have been offered rather than a file that cannot be opened. An\n * unreadable or malformed file leaves completion listing everything, because\n * hiding the tree is the worse failure.\n */\nasync function readIgnoreRules(\n fs: FileSystem,\n cwd: string,\n context: Context,\n): Promise<((relativePath: string) => boolean) | undefined> {\n const rules = (\n await Promise.all(\n IGNORE_FILES.map(async (name) => {\n try {\n const result = await fs.readTextFile(path.join(cwd, name), context);\n return result.ok ? result.value : '';\n } catch {\n return '';\n }\n }),\n )\n ).join('\\n');\n if (rules.trim().length === 0) return undefined;\n try {\n const matcher = ignore().add(rules);\n return (relativePath: string): boolean => matcher.ignores(relativePath);\n } catch {\n return undefined;\n }\n}\n\nasync function listSessionFiles(fs: FileSystem, cwd: string, query: string, signal: AbortSignal): Promise<string[]> {\n const context = withAbortSignal(signal, BACKGROUND_CONTEXT);\n const isIgnored = await readIgnoreRules(fs, cwd, context);\n const deadline = Date.now() + FILE_QUERY_TIMEOUT_MS;\n const pending = [''];\n let next = 0;\n const matches: string[] = [];\n\n while (next < pending.length && matches.length < MAX_FILE_RESULTS) {\n if (signal.aborted) {\n throw signal.reason instanceof Error ? signal.reason : new Error('File completion was aborted.');\n }\n // ponytail: Return matches found so far on huge trees; a search index is the upgrade path.\n if (Date.now() > deadline && next > 0) break;\n const relativeDirectory = pending[next++];\n if (relativeDirectory === undefined) break;\n if (relativeDirectory !== '' && `${relativeDirectory}/`.toLowerCase().includes(query)) {\n matches.push(`${relativeDirectory}/`);\n }\n if (matches.length === MAX_FILE_RESULTS) break;\n const entries = getOrThrow(await fs.listDir(path.join(cwd, relativeDirectory), context));\n const matchingDirectories: string[] = [];\n for (const entry of entries) {\n // Ignore rules are written in posix form, and so is every result.\n const file = relativeDirectory === '' ? entry.name : `${relativeDirectory}/${entry.name}`;\n if (entry.kind === 'directory') {\n // An ignored directory is pruned rather than filtered at its leaves:\n // walking a build cache is what exhausts the completion budget. The\n // trailing slash is what makes a directory-only rule such as\n // '.nx-cache/' match.\n if (EXCLUDED_DIRECTORIES.has(entry.name) || isIgnored?.(`${file}/`) === true) continue;\n // Explore a matching folder before its siblings so its files follow it.\n if (query && entry.name.toLowerCase().includes(query)) matchingDirectories.push(file);\n else pending.push(file);\n } else if (entry.kind === 'file' && isIgnored?.(file) !== true && file.toLowerCase().includes(query)) {\n matches.push(file);\n }\n if (matches.length === MAX_FILE_RESULTS) break;\n }\n pending.splice(next, 0, ...matchingDirectories);\n }\n\n return matches.sort().slice(0, MAX_FILE_RESULTS);\n}\n\n/** Bounded, read-only file completion rooted in the owning session. */\nexport const sessionFilesApi: PoApi = {\n basePath: 'files',\n start(context) {\n if (context.scope !== 'session' || !context.cwd) throw new Error('File completion requires a session.');\n const cwd = context.cwd;\n // The edge twin of the Node platform throws, so an edge mount fails loudly instead of listing nothing.\n const fs: FileSystem = createNodePlatform({ homeDirectory: context.homeDirectory }).env(cwd);\n let closed = false;\n return {\n async fetch(request) {\n if (closed) return new Response(null, { status: 503 });\n const url = new URL(request.url);\n if (request.method !== 'GET' || url.pathname !== '/') return new Response(null, { status: 404 });\n const query = (url.searchParams.get('q') ?? '').toLowerCase();\n if (query.length > 256) return Response.json({ error: 'File query is too long.' }, { status: 400 });\n try {\n return Response.json({ files: await listSessionFiles(fs, cwd, query, request.signal) });\n } catch {\n return Response.json({ error: 'File completion could not be read.' }, { status: 503 });\n }\n },\n close() {\n closed = true;\n },\n };\n },\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"sessionFilesApi.cjs","names":["path","withAbortSignal","BACKGROUND_CONTEXT","getOrThrow"],"sources":["../src/server/sessionFilesApi.ts"],"sourcesContent":["import path from 'node:path';\n\nimport type { Context } from '@earendil-works/chord';\nimport { BACKGROUND_CONTEXT, withAbortSignal } from '@earendil-works/chord/context';\nimport { getOrThrow, type FileSystem } from '@earendil-works/pi-durable/env';\nimport ignore from 'ignore';\n\nimport type { PoApi } from '../exports/packageApi';\n\nconst EXCLUDED_DIRECTORIES = new Set(['.git', 'node_modules']);\nconst IGNORE_FILES = ['.gitignore', '.poignore'];\nconst FILE_QUERY_TIMEOUT_MS = 2000;\nconst MAX_FILE_RESULTS = 50;\n\n/**\n * The project's ignore test, or nothing when it declares no rules.\n *\n * Only the session root's files are read. A nested .gitignore would need one\n * matcher per directory, and the cost of skipping it is a suggestion that\n * should not have been offered rather than a file that cannot be opened. An\n * unreadable or malformed file leaves completion listing everything, because\n * hiding the tree is the worse failure.\n */\nasync function readIgnoreRules(\n fs: FileSystem,\n cwd: string,\n context: Context,\n): Promise<((relativePath: string) => boolean) | undefined> {\n const rules = (\n await Promise.all(\n IGNORE_FILES.map(async (name) => {\n try {\n const result = await fs.readTextFile(path.join(cwd, name), context);\n return result.ok ? result.value : '';\n } catch {\n return '';\n }\n }),\n )\n ).join('\\n');\n if (rules.trim().length === 0) return undefined;\n try {\n const matcher = ignore().add(rules);\n return (relativePath: string): boolean => matcher.ignores(relativePath);\n } catch {\n return undefined;\n }\n}\n\nasync function listSessionFiles(fs: FileSystem, cwd: string, query: string, signal: AbortSignal): Promise<string[]> {\n const context = withAbortSignal(signal, BACKGROUND_CONTEXT);\n const isIgnored = await readIgnoreRules(fs, cwd, context);\n const deadline = Date.now() + FILE_QUERY_TIMEOUT_MS;\n const pending = [''];\n let next = 0;\n const matches: string[] = [];\n\n while (next < pending.length && matches.length < MAX_FILE_RESULTS) {\n if (signal.aborted) {\n throw signal.reason instanceof Error ? signal.reason : new Error('File completion was aborted.');\n }\n // ponytail: Return matches found so far on huge trees; a search index is the upgrade path.\n if (Date.now() > deadline && next > 0) break;\n const relativeDirectory = pending[next++];\n if (relativeDirectory === undefined) break;\n if (relativeDirectory !== '' && `${relativeDirectory}/`.toLowerCase().includes(query)) {\n matches.push(`${relativeDirectory}/`);\n }\n if (matches.length === MAX_FILE_RESULTS) break;\n const entries = getOrThrow(await fs.listDir(path.join(cwd, relativeDirectory), context));\n const matchingDirectories: string[] = [];\n for (const entry of entries) {\n // Ignore rules are written in posix form, and so is every result.\n const file = relativeDirectory === '' ? entry.name : `${relativeDirectory}/${entry.name}`;\n if (entry.kind === 'directory') {\n // An ignored directory is pruned rather than filtered at its leaves:\n // walking a build cache is what exhausts the completion budget. The\n // trailing slash is what makes a directory-only rule such as\n // '.nx-cache/' match.\n if (EXCLUDED_DIRECTORIES.has(entry.name) || isIgnored?.(`${file}/`) === true) continue;\n // Explore a matching folder before its siblings so its files follow it.\n if (query && entry.name.toLowerCase().includes(query)) matchingDirectories.push(file);\n else pending.push(file);\n } else if (entry.kind === 'file' && isIgnored?.(file) !== true && file.toLowerCase().includes(query)) {\n matches.push(file);\n }\n if (matches.length === MAX_FILE_RESULTS) break;\n }\n pending.splice(next, 0, ...matchingDirectories);\n }\n\n return matches.sort().slice(0, MAX_FILE_RESULTS);\n}\n\n/** Bounded, read-only file completion rooted in the owning session, read through the server's platform fs. */\nexport function createSessionFilesApi({ fs }: { fs: FileSystem }): PoApi {\n return {\n basePath: 'files',\n start(context) {\n if (context.scope !== 'session' || !context.cwd) throw new Error('File completion requires a session.');\n const cwd = context.cwd;\n let closed = false;\n return {\n async fetch(request) {\n if (closed) return new Response(null, { status: 503 });\n const url = new URL(request.url);\n if (request.method !== 'GET' || url.pathname !== '/') return new Response(null, { status: 404 });\n const query = (url.searchParams.get('q') ?? '').toLowerCase();\n if (query.length > 256) return Response.json({ error: 'File query is too long.' }, { status: 400 });\n try {\n return Response.json({ files: await listSessionFiles(fs, cwd, query, request.signal) });\n } catch {\n return Response.json({ error: 'File completion could not be read.' }, { status: 503 });\n }\n },\n close() {\n closed = true;\n },\n };\n },\n };\n}\n"],"mappings":"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"}
@@ -1,7 +1,10 @@
1
1
  import { PoApi } from "./src/schemas/packageApi.mjs";
2
2
  import "./packageApi.mjs";
3
+ import { FileSystem } from "@earendil-works/pi-durable/env";
3
4
  //#region src/server/sessionFilesApi.d.ts
4
- /** Bounded, read-only file completion rooted in the owning session. */
5
- export declare const sessionFilesApi: PoApi;
5
+ /** Bounded, read-only file completion rooted in the owning session, read through the server's platform fs. */
6
+ export declare function createSessionFilesApi({ fs }: {
7
+ fs: FileSystem;
8
+ }): PoApi;
6
9
  //#endregion
7
10
  //# sourceMappingURL=sessionFilesApi.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sessionFilesApi.d.mts","names":[],"sources":["../src/server/sessionFilesApi.ts"],"mappings":";;;;qBAgGa,iBAAiB"}
1
+ {"version":3,"file":"sessionFilesApi.d.mts","names":[],"sources":["../src/server/sessionFilesApi.ts"],"mappings":";;;;;wBA+FgB,wBAAwB;EAAQ,IAAI;IAAe"}