@parlor/agent-host 0.3.0 → 0.3.2
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/dist/agent-options.d.ts +5 -0
- package/dist/agent-options.d.ts.map +1 -1
- package/dist/agent-options.js +32 -0
- package/dist/agent-options.js.map +1 -1
- package/dist/agent-skills.d.ts +15 -0
- package/dist/agent-skills.d.ts.map +1 -0
- package/dist/agent-skills.js +92 -0
- package/dist/agent-skills.js.map +1 -0
- package/dist/agent-survey.d.ts +15 -0
- package/dist/agent-survey.d.ts.map +1 -0
- package/dist/agent-survey.js +51 -0
- package/dist/agent-survey.js.map +1 -0
- package/dist/agents.d.ts +15 -0
- package/dist/agents.d.ts.map +1 -1
- package/dist/agents.js +33 -9
- package/dist/agents.js.map +1 -1
- package/dist/codex-app-server.d.ts +7 -0
- package/dist/codex-app-server.d.ts.map +1 -0
- package/dist/codex-app-server.js +46 -0
- package/dist/codex-app-server.js.map +1 -0
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +192 -26
- package/dist/daemon.js.map +1 -1
- package/dist/learned.d.ts +2 -0
- package/dist/learned.d.ts.map +1 -1
- package/dist/learned.js +1 -0
- package/dist/learned.js.map +1 -1
- package/dist/link.d.ts +5 -1
- package/dist/link.d.ts.map +1 -1
- package/dist/link.js +25 -1
- package/dist/link.js.map +1 -1
- package/dist/mcp-proxy.d.ts.map +1 -1
- package/dist/mcp-proxy.js +109 -17
- package/dist/mcp-proxy.js.map +1 -1
- package/dist/shell-path.d.ts +2 -0
- package/dist/shell-path.d.ts.map +1 -1
- package/dist/shell-path.js +14 -3
- package/dist/shell-path.js.map +1 -1
- package/dist/spec-kit.d.ts +4 -0
- package/dist/spec-kit.d.ts.map +1 -1
- package/dist/spec-kit.js +13 -1
- package/dist/spec-kit.js.map +1 -1
- package/package.json +9 -9
package/dist/agent-options.d.ts
CHANGED
|
@@ -9,6 +9,11 @@ export declare function optionChanges(options: readonly AgentOption[], chosen: R
|
|
|
9
9
|
* CLI names under `unifiedWindows`, or the one window an event is about.
|
|
10
10
|
*/
|
|
11
11
|
export declare function agentUsageOf(meta: Readonly<Record<string, unknown>> | null | undefined, at: number): AgentUsage | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* Codex's rate limit, from `account/rateLimits/read` on its app-server: two windows it calls
|
|
14
|
+
* `primary` and `secondary`, each carrying its own length, so neither is taken to be the short one.
|
|
15
|
+
*/
|
|
16
|
+
export declare function codexUsageOf(result: unknown, at: number): AgentUsage | undefined;
|
|
12
17
|
/**
|
|
13
18
|
* `next` laid over `held` window by window: an event names one window, and the others it
|
|
14
19
|
* leaves out still stand until they roll over.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-options.d.ts","sourceRoot":"","sources":["../src/agent-options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAoB,MAAM,cAAc,CAAC;AAE9E,qFAAqF;AACrF,wBAAgB,aAAa,CAC3B,OAAO,EAAE,SAAS,WAAW,EAAE,EAC/B,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,SAAS,GACnD;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,CAQnD;AAiCD;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,EAC1D,EAAE,EAAE,MAAM,GACT,UAAU,GAAG,SAAS,CAmBxB;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,SAAS,EAAE,IAAI,EAAE,UAAU,GAAG,UAAU,CAQrF"}
|
|
1
|
+
{"version":3,"file":"agent-options.d.ts","sourceRoot":"","sources":["../src/agent-options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAoB,MAAM,cAAc,CAAC;AAE9E,qFAAqF;AACrF,wBAAgB,aAAa,CAC3B,OAAO,EAAE,SAAS,WAAW,EAAE,EAC/B,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,SAAS,GACnD;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,CAQnD;AAiCD;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,EAC1D,EAAE,EAAE,MAAM,GACT,UAAU,GAAG,SAAS,CAmBxB;AAQD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CA2BhF;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,SAAS,EAAE,IAAI,EAAE,UAAU,GAAG,UAAU,CAQrF"}
|
package/dist/agent-options.js
CHANGED
|
@@ -51,6 +51,36 @@ export function agentUsageOf(meta, at) {
|
|
|
51
51
|
});
|
|
52
52
|
return windows.length === 0 ? undefined : { windows: ordered(windows), at };
|
|
53
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Codex's rate limit, from `account/rateLimits/read` on its app-server: two windows it calls
|
|
56
|
+
* `primary` and `secondary`, each carrying its own length, so neither is taken to be the short one.
|
|
57
|
+
*/
|
|
58
|
+
export function codexUsageOf(result, at) {
|
|
59
|
+
const limits = result?.rateLimits;
|
|
60
|
+
if (!limits || typeof limits !== "object")
|
|
61
|
+
return undefined;
|
|
62
|
+
const windows = ["primary", "secondary"].flatMap((id) => {
|
|
63
|
+
const slot = limits[id];
|
|
64
|
+
const used = slot?.usedPercent;
|
|
65
|
+
const minutes = slot?.windowDurationMins;
|
|
66
|
+
if (typeof used !== "number" || !Number.isFinite(used))
|
|
67
|
+
return [];
|
|
68
|
+
if (typeof minutes !== "number" || !(minutes > 0))
|
|
69
|
+
return [];
|
|
70
|
+
const resetsAt = typeof slot?.resetsAt === "number" && Number.isFinite(slot.resetsAt)
|
|
71
|
+
? slot.resetsAt * 1000
|
|
72
|
+
: undefined;
|
|
73
|
+
return [
|
|
74
|
+
{
|
|
75
|
+
id,
|
|
76
|
+
minutes,
|
|
77
|
+
used: Math.min(1, Math.max(0, used / 100)),
|
|
78
|
+
...(resetsAt === undefined ? {} : { resetsAt }),
|
|
79
|
+
},
|
|
80
|
+
];
|
|
81
|
+
});
|
|
82
|
+
return windows.length === 0 ? undefined : { windows: ordered(windows), at };
|
|
83
|
+
}
|
|
54
84
|
/**
|
|
55
85
|
* `next` laid over `held` window by window: an event names one window, and the others it
|
|
56
86
|
* leaves out still stand until they roll over.
|
|
@@ -69,5 +99,7 @@ function ordered(windows) {
|
|
|
69
99
|
const ACCOUNT_WINDOWS = {
|
|
70
100
|
five_hour: true,
|
|
71
101
|
seven_day: true,
|
|
102
|
+
primary: true,
|
|
103
|
+
secondary: true,
|
|
72
104
|
};
|
|
73
105
|
//# sourceMappingURL=agent-options.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-options.js","sourceRoot":"","sources":["../src/agent-options.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentOption, AgentUsage, AgentUsageWindow } from \"@parlor/chat\";\n\n/** The values `chosen` names that `options` offers and does not already run with. */\nexport function optionChanges(\n options: readonly AgentOption[],\n chosen: Readonly<Record<string, string>> | undefined,\n): { readonly id: string; readonly value: string }[] {\n return Object.entries(chosen ?? {}).flatMap(([id, value]) => {\n const option = options.find((candidate) => candidate.id === id);\n if (!option || option.current === value) return [];\n return option.values.some((candidate) => candidate.id === value)\n ? [{ id, value }]\n : [];\n });\n}\n\nconst RATE_LIMIT_META = \"_claude/rateLimit\";\nconst WINDOW_MINUTES: Readonly<Record<string, number>> = {\n five_hour: 5 * 60,\n seven_day: 7 * 24 * 60,\n seven_day_opus: 7 * 24 * 60,\n seven_day_sonnet: 7 * 24 * 60,\n};\n\ninterface RateLimitSlot {\n readonly utilization?: unknown;\n readonly resetsAt?: unknown;\n}\n\nfunction windowOf(id: string, slot: RateLimitSlot): AgentUsageWindow | undefined {\n const minutes = WINDOW_MINUTES[id];\n const used = slot.utilization;\n if (minutes === undefined || typeof used !== \"number\" || !Number.isFinite(used)) {\n return undefined;\n }\n const resetsAt =\n typeof slot.resetsAt === \"number\" && Number.isFinite(slot.resetsAt)\n ? slot.resetsAt * 1000\n : undefined;\n return {\n id,\n minutes,\n used: Math.min(1, Math.max(0, used)),\n ...(resetsAt === undefined ? {} : { resetsAt }),\n };\n}\n\n/**\n * Claude Code's rate limit, which rides on a `usage_update`'s `_meta`: every window a newer\n * CLI names under `unifiedWindows`, or the one window an event is about.\n */\nexport function agentUsageOf(\n meta: Readonly<Record<string, unknown>> | null | undefined,\n at: number,\n): AgentUsage | undefined {\n const info = meta?.[RATE_LIMIT_META] as\n | (RateLimitSlot & {\n readonly rateLimitType?: unknown;\n readonly unifiedWindows?: Readonly<Record<string, RateLimitSlot>>;\n })\n | undefined;\n if (!info || typeof info !== \"object\") return undefined;\n const slots: [string, RateLimitSlot][] =\n info.unifiedWindows && typeof info.unifiedWindows === \"object\"\n ? Object.entries(info.unifiedWindows)\n : typeof info.rateLimitType === \"string\"\n ? [[info.rateLimitType, info]]\n : [];\n const windows = slots.flatMap(([id, slot]) => {\n const window = windowOf(id, slot);\n return window ? [window] : [];\n });\n return windows.length === 0 ? undefined : { windows: ordered(windows), at };\n}\n\n/**\n * `next` laid over `held` window by window: an event names one window, and the others it\n * leaves out still stand until they roll over.\n */\nexport function mergeUsage(held: AgentUsage | undefined, next: AgentUsage): AgentUsage {\n const named = new Set(next.windows.map((window) => window.id));\n const kept = (held?.windows ?? []).filter(\n (window) =>\n !named.has(window.id) &&\n (window.resetsAt === undefined || window.resetsAt > next.at),\n );\n return { windows: ordered([...next.windows, ...kept]), at: next.at };\n}\n\n/** The account's own windows before a model's, each set shortest first. */\nfunction ordered(windows: readonly AgentUsageWindow[]): AgentUsageWindow[] {\n const perModel = (window: AgentUsageWindow) => (window.id in ACCOUNT_WINDOWS ? 0 : 1);\n return [...windows].sort(\n (a, b) =>\n perModel(a) - perModel(b) || a.minutes - b.minutes || a.id.localeCompare(b.id),\n );\n}\n\nconst ACCOUNT_WINDOWS: Readonly<Record<string, true>> = {\n five_hour: true,\n seven_day: true,\n};\n"]}
|
|
1
|
+
{"version":3,"file":"agent-options.js","sourceRoot":"","sources":["../src/agent-options.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentOption, AgentUsage, AgentUsageWindow } from \"@parlor/chat\";\n\n/** The values `chosen` names that `options` offers and does not already run with. */\nexport function optionChanges(\n options: readonly AgentOption[],\n chosen: Readonly<Record<string, string>> | undefined,\n): { readonly id: string; readonly value: string }[] {\n return Object.entries(chosen ?? {}).flatMap(([id, value]) => {\n const option = options.find((candidate) => candidate.id === id);\n if (!option || option.current === value) return [];\n return option.values.some((candidate) => candidate.id === value)\n ? [{ id, value }]\n : [];\n });\n}\n\nconst RATE_LIMIT_META = \"_claude/rateLimit\";\nconst WINDOW_MINUTES: Readonly<Record<string, number>> = {\n five_hour: 5 * 60,\n seven_day: 7 * 24 * 60,\n seven_day_opus: 7 * 24 * 60,\n seven_day_sonnet: 7 * 24 * 60,\n};\n\ninterface RateLimitSlot {\n readonly utilization?: unknown;\n readonly resetsAt?: unknown;\n}\n\nfunction windowOf(id: string, slot: RateLimitSlot): AgentUsageWindow | undefined {\n const minutes = WINDOW_MINUTES[id];\n const used = slot.utilization;\n if (minutes === undefined || typeof used !== \"number\" || !Number.isFinite(used)) {\n return undefined;\n }\n const resetsAt =\n typeof slot.resetsAt === \"number\" && Number.isFinite(slot.resetsAt)\n ? slot.resetsAt * 1000\n : undefined;\n return {\n id,\n minutes,\n used: Math.min(1, Math.max(0, used)),\n ...(resetsAt === undefined ? {} : { resetsAt }),\n };\n}\n\n/**\n * Claude Code's rate limit, which rides on a `usage_update`'s `_meta`: every window a newer\n * CLI names under `unifiedWindows`, or the one window an event is about.\n */\nexport function agentUsageOf(\n meta: Readonly<Record<string, unknown>> | null | undefined,\n at: number,\n): AgentUsage | undefined {\n const info = meta?.[RATE_LIMIT_META] as\n | (RateLimitSlot & {\n readonly rateLimitType?: unknown;\n readonly unifiedWindows?: Readonly<Record<string, RateLimitSlot>>;\n })\n | undefined;\n if (!info || typeof info !== \"object\") return undefined;\n const slots: [string, RateLimitSlot][] =\n info.unifiedWindows && typeof info.unifiedWindows === \"object\"\n ? Object.entries(info.unifiedWindows)\n : typeof info.rateLimitType === \"string\"\n ? [[info.rateLimitType, info]]\n : [];\n const windows = slots.flatMap(([id, slot]) => {\n const window = windowOf(id, slot);\n return window ? [window] : [];\n });\n return windows.length === 0 ? undefined : { windows: ordered(windows), at };\n}\n\ninterface CodexWindow {\n readonly usedPercent?: unknown;\n readonly windowDurationMins?: unknown;\n readonly resetsAt?: unknown;\n}\n\n/**\n * Codex's rate limit, from `account/rateLimits/read` on its app-server: two windows it calls\n * `primary` and `secondary`, each carrying its own length, so neither is taken to be the short one.\n */\nexport function codexUsageOf(result: unknown, at: number): AgentUsage | undefined {\n const limits = (\n result as { readonly rateLimits?: Readonly<Record<string, CodexWindow>> }\n )?.rateLimits;\n if (!limits || typeof limits !== \"object\") return undefined;\n const windows = ([\"primary\", \"secondary\"] as const).flatMap(\n (id): AgentUsageWindow[] => {\n const slot = limits[id];\n const used = slot?.usedPercent;\n const minutes = slot?.windowDurationMins;\n if (typeof used !== \"number\" || !Number.isFinite(used)) return [];\n if (typeof minutes !== \"number\" || !(minutes > 0)) return [];\n const resetsAt =\n typeof slot?.resetsAt === \"number\" && Number.isFinite(slot.resetsAt)\n ? slot.resetsAt * 1000\n : undefined;\n return [\n {\n id,\n minutes,\n used: Math.min(1, Math.max(0, used / 100)),\n ...(resetsAt === undefined ? {} : { resetsAt }),\n },\n ];\n },\n );\n return windows.length === 0 ? undefined : { windows: ordered(windows), at };\n}\n\n/**\n * `next` laid over `held` window by window: an event names one window, and the others it\n * leaves out still stand until they roll over.\n */\nexport function mergeUsage(held: AgentUsage | undefined, next: AgentUsage): AgentUsage {\n const named = new Set(next.windows.map((window) => window.id));\n const kept = (held?.windows ?? []).filter(\n (window) =>\n !named.has(window.id) &&\n (window.resetsAt === undefined || window.resetsAt > next.at),\n );\n return { windows: ordered([...next.windows, ...kept]), at: next.at };\n}\n\n/** The account's own windows before a model's, each set shortest first. */\nfunction ordered(windows: readonly AgentUsageWindow[]): AgentUsageWindow[] {\n const perModel = (window: AgentUsageWindow) => (window.id in ACCOUNT_WINDOWS ? 0 : 1);\n return [...windows].sort(\n (a, b) =>\n perModel(a) - perModel(b) || a.minutes - b.minutes || a.id.localeCompare(b.id),\n );\n}\n\nconst ACCOUNT_WINDOWS: Readonly<Record<string, true>> = {\n five_hour: true,\n seven_day: true,\n primary: true,\n secondary: true,\n};\n"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** What a skill says about the app it points the agent's MCP tools at. */
|
|
2
|
+
export interface SkillApp {
|
|
3
|
+
/** The MCP server name, which is also the skill's name and the word the user asks with. */
|
|
4
|
+
readonly name: string;
|
|
5
|
+
readonly appName: string;
|
|
6
|
+
readonly appOrigin: string;
|
|
7
|
+
/** Whose account the tools act as: an email, or a name. */
|
|
8
|
+
readonly account: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function skillOf(app: SkillApp): string;
|
|
11
|
+
/** Writes or refreshes the app's skill for each agent here; a skill of the same name the user wrote stays. */
|
|
12
|
+
export declare function writeAgentSkills(agents: readonly string[], app: SkillApp, env: Record<string, string>): Promise<void>;
|
|
13
|
+
/** Removes the skills Parlor Host wrote for the app; one the user wrote under the name stays. */
|
|
14
|
+
export declare function removeAgentSkills(agents: readonly string[], name: string, env: Record<string, string>): Promise<void>;
|
|
15
|
+
//# sourceMappingURL=agent-skills.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-skills.d.ts","sourceRoot":"","sources":["../src/agent-skills.ts"],"names":[],"mappings":"AAMA,0EAA0E;AAC1E,MAAM,WAAW,QAAQ;IACvB,2FAA2F;IAC3F,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,2DAA2D;IAC3D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAiBD,wBAAgB,OAAO,CAAC,GAAG,EAAE,QAAQ,GAAG,MAAM,CAiB7C;AA4BD,8GAA8G;AAC9G,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,SAAS,MAAM,EAAE,EACzB,GAAG,EAAE,QAAQ,EACb,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC1B,OAAO,CAAC,IAAI,CAAC,CAqBf;AAED,iGAAiG;AACjG,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,SAAS,MAAM,EAAE,EACzB,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC1B,OAAO,CAAC,IAAI,CAAC,CAUf"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { lstat, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { logEvent } from "./log.js";
|
|
5
|
+
/**
|
|
6
|
+
* Where each agent reads user-wide skills. Coding agents load MCP tools on demand and list
|
|
7
|
+
* skills up front, so a skill named after the app is what the agent sees when the user
|
|
8
|
+
* names it, instead of a browser skill or a guess.
|
|
9
|
+
*/
|
|
10
|
+
const SKILL_ROOTS = {
|
|
11
|
+
claude: (env) => join(env.CLAUDE_CONFIG_DIR || join(homeOf(env), ".claude"), "skills"),
|
|
12
|
+
codex: (env) => join(env.CODEX_HOME || join(homeOf(env), ".codex"), "skills"),
|
|
13
|
+
};
|
|
14
|
+
/** Marks a skill directory as Parlor Host's, so a skill the user wrote is never replaced or removed. */
|
|
15
|
+
const OWNER_FILE = ".parlor-host";
|
|
16
|
+
const homeOf = (env) => env.HOME || homedir();
|
|
17
|
+
export function skillOf(app) {
|
|
18
|
+
const description = `Use whenever the user names ${app.appName} or "${app.name}", to read or change anything in ${app.appName} (${app.appOrigin}). Use the ${app.name} MCP tools for it, not a browser.`;
|
|
19
|
+
return `---
|
|
20
|
+
name: ${app.name}
|
|
21
|
+
description: ${JSON.stringify(description)}
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
# ${app.appName}
|
|
25
|
+
|
|
26
|
+
The \`${app.name}\` MCP server acts on the live ${app.appName} app at ${app.appOrigin} as ${app.account}. Its tools read and change the same data the app shows, directly.
|
|
27
|
+
|
|
28
|
+
- For anything the user asks of ${app.appName}, use these tools instead of opening the app in a browser.
|
|
29
|
+
- The tools may load on demand: if none from \`${app.name}\` are listed, search your tools for "${app.name}" first.
|
|
30
|
+
- Reading is free. Before the first change in a conversation, the user is asked to allow changes.
|
|
31
|
+
|
|
32
|
+
Parlor Host wrote this skill when this computer was connected to ${app.appName}, and removes it when it is disconnected.
|
|
33
|
+
`;
|
|
34
|
+
}
|
|
35
|
+
async function ownedBy(dir) {
|
|
36
|
+
return readFile(join(dir, OWNER_FILE), "utf8").then((text) => text.trim(), () => undefined);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Whether the user keeps the agent's settings elsewhere and links them in, as dotfiles and
|
|
40
|
+
* home-manager do: a skill written there would land in their own repository or store.
|
|
41
|
+
*/
|
|
42
|
+
async function linkedIn(root) {
|
|
43
|
+
for (const path of [root, dirname(root)]) {
|
|
44
|
+
const stat = await lstat(path).catch(() => undefined);
|
|
45
|
+
if (stat?.isSymbolicLink())
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
async function exists(path) {
|
|
51
|
+
return readFile(join(path, "SKILL.md")).then(() => true, () => false);
|
|
52
|
+
}
|
|
53
|
+
/** Writes or refreshes the app's skill for each agent here; a skill of the same name the user wrote stays. */
|
|
54
|
+
export async function writeAgentSkills(agents, app, env) {
|
|
55
|
+
for (const agent of agents) {
|
|
56
|
+
const root = SKILL_ROOTS[agent];
|
|
57
|
+
if (!root)
|
|
58
|
+
continue;
|
|
59
|
+
const dir = join(root(env), app.name);
|
|
60
|
+
try {
|
|
61
|
+
if (await linkedIn(root(env))) {
|
|
62
|
+
logEvent("agent_skill_kept", { agent, name: app.name, reason: "linked" });
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if ((await exists(dir)) && (await ownedBy(dir)) === undefined) {
|
|
66
|
+
logEvent("agent_skill_kept", { agent, name: app.name });
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
await mkdir(dir, { recursive: true });
|
|
70
|
+
await writeFile(join(dir, "SKILL.md"), skillOf(app));
|
|
71
|
+
await writeFile(join(dir, OWNER_FILE), `${app.appOrigin}\n`);
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
logEvent("agent_skill_failed", { agent, name: app.name, error: String(error) });
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/** Removes the skills Parlor Host wrote for the app; one the user wrote under the name stays. */
|
|
79
|
+
export async function removeAgentSkills(agents, name, env) {
|
|
80
|
+
for (const agent of agents) {
|
|
81
|
+
const root = SKILL_ROOTS[agent];
|
|
82
|
+
if (!root)
|
|
83
|
+
continue;
|
|
84
|
+
const dir = join(root(env), name);
|
|
85
|
+
if ((await ownedBy(dir)) === undefined)
|
|
86
|
+
continue;
|
|
87
|
+
await rm(dir, { recursive: true, force: true }).catch((error) => {
|
|
88
|
+
logEvent("agent_skill_failed", { agent, name, error: String(error) });
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=agent-skills.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-skills.js","sourceRoot":"","sources":["../src/agent-skills.ts"],"names":[],"mappings":"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","sourcesContent":["import { lstat, mkdir, readFile, rm, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\n\nimport { logEvent } from \"./log.js\";\n\n/** What a skill says about the app it points the agent's MCP tools at. */\nexport interface SkillApp {\n /** The MCP server name, which is also the skill's name and the word the user asks with. */\n readonly name: string;\n readonly appName: string;\n readonly appOrigin: string;\n /** Whose account the tools act as: an email, or a name. */\n readonly account: string;\n}\n\n/**\n * Where each agent reads user-wide skills. Coding agents load MCP tools on demand and list\n * skills up front, so a skill named after the app is what the agent sees when the user\n * names it, instead of a browser skill or a guess.\n */\nconst SKILL_ROOTS: Readonly<Record<string, (env: Record<string, string>) => string>> = {\n claude: (env) => join(env.CLAUDE_CONFIG_DIR || join(homeOf(env), \".claude\"), \"skills\"),\n codex: (env) => join(env.CODEX_HOME || join(homeOf(env), \".codex\"), \"skills\"),\n};\n\n/** Marks a skill directory as Parlor Host's, so a skill the user wrote is never replaced or removed. */\nconst OWNER_FILE = \".parlor-host\";\n\nconst homeOf = (env: Record<string, string>) => env.HOME || homedir();\n\nexport function skillOf(app: SkillApp): string {\n const description = `Use whenever the user names ${app.appName} or \"${app.name}\", to read or change anything in ${app.appName} (${app.appOrigin}). Use the ${app.name} MCP tools for it, not a browser.`;\n return `---\nname: ${app.name}\ndescription: ${JSON.stringify(description)}\n---\n\n# ${app.appName}\n\nThe \\`${app.name}\\` MCP server acts on the live ${app.appName} app at ${app.appOrigin} as ${app.account}. Its tools read and change the same data the app shows, directly.\n\n- For anything the user asks of ${app.appName}, use these tools instead of opening the app in a browser.\n- The tools may load on demand: if none from \\`${app.name}\\` are listed, search your tools for \"${app.name}\" first.\n- Reading is free. Before the first change in a conversation, the user is asked to allow changes.\n\nParlor Host wrote this skill when this computer was connected to ${app.appName}, and removes it when it is disconnected.\n`;\n}\n\nasync function ownedBy(dir: string): Promise<string | undefined> {\n return readFile(join(dir, OWNER_FILE), \"utf8\").then(\n (text) => text.trim(),\n () => undefined,\n );\n}\n\n/**\n * Whether the user keeps the agent's settings elsewhere and links them in, as dotfiles and\n * home-manager do: a skill written there would land in their own repository or store.\n */\nasync function linkedIn(root: string): Promise<boolean> {\n for (const path of [root, dirname(root)]) {\n const stat = await lstat(path).catch(() => undefined);\n if (stat?.isSymbolicLink()) return true;\n }\n return false;\n}\n\nasync function exists(path: string): Promise<boolean> {\n return readFile(join(path, \"SKILL.md\")).then(\n () => true,\n () => false,\n );\n}\n\n/** Writes or refreshes the app's skill for each agent here; a skill of the same name the user wrote stays. */\nexport async function writeAgentSkills(\n agents: readonly string[],\n app: SkillApp,\n env: Record<string, string>,\n): Promise<void> {\n for (const agent of agents) {\n const root = SKILL_ROOTS[agent];\n if (!root) continue;\n const dir = join(root(env), app.name);\n try {\n if (await linkedIn(root(env))) {\n logEvent(\"agent_skill_kept\", { agent, name: app.name, reason: \"linked\" });\n continue;\n }\n if ((await exists(dir)) && (await ownedBy(dir)) === undefined) {\n logEvent(\"agent_skill_kept\", { agent, name: app.name });\n continue;\n }\n await mkdir(dir, { recursive: true });\n await writeFile(join(dir, \"SKILL.md\"), skillOf(app));\n await writeFile(join(dir, OWNER_FILE), `${app.appOrigin}\\n`);\n } catch (error) {\n logEvent(\"agent_skill_failed\", { agent, name: app.name, error: String(error) });\n }\n }\n}\n\n/** Removes the skills Parlor Host wrote for the app; one the user wrote under the name stays. */\nexport async function removeAgentSkills(\n agents: readonly string[],\n name: string,\n env: Record<string, string>,\n): Promise<void> {\n for (const agent of agents) {\n const root = SKILL_ROOTS[agent];\n if (!root) continue;\n const dir = join(root(env), name);\n if ((await ownedBy(dir)) === undefined) continue;\n await rm(dir, { recursive: true, force: true }).catch((error: unknown) => {\n logEvent(\"agent_skill_failed\", { agent, name, error: String(error) });\n });\n }\n}\n"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { AgentOption, AgentUsage } from "@parlor/chat";
|
|
2
|
+
import type { AgentLauncher } from "./acp.js";
|
|
3
|
+
import type { AgentProfile } from "./agents.js";
|
|
4
|
+
/**
|
|
5
|
+
* The options `profile` offers, from a session opened in a scratch directory only to read
|
|
6
|
+
* them and then closed: no prompt, so no model turn. Lets the sidebar offer a model before
|
|
7
|
+
* the agent's first turn. Undefined when the agent cannot be started or is not logged in.
|
|
8
|
+
*/
|
|
9
|
+
export declare function surveyOptions(profile: AgentProfile, launcher: AgentLauncher, env: Readonly<Record<string, string>>, stop?: AbortSignal): Promise<readonly AgentOption[] | undefined>;
|
|
10
|
+
/**
|
|
11
|
+
* The account's allowance from the agent's own CLI, for an agent whose ACP adapter does not
|
|
12
|
+
* carry it: `codex-acp` drops Codex's rate limits. Claude Code sends its own over ACP.
|
|
13
|
+
*/
|
|
14
|
+
export declare function readUsage(profile: AgentProfile, env: Readonly<Record<string, string>>, now: number): Promise<AgentUsage | undefined>;
|
|
15
|
+
//# sourceMappingURL=agent-survey.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-survey.d.ts","sourceRoot":"","sources":["../src/agent-survey.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAKhD;;;;GAIG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,YAAY,EACrB,QAAQ,EAAE,aAAa,EACvB,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EACrC,IAAI,CAAC,EAAE,WAAW,GACjB,OAAO,CAAC,SAAS,WAAW,EAAE,GAAG,SAAS,CAAC,CAyB7C;AAED;;;GAGG;AACH,wBAAsB,SAAS,CAC7B,OAAO,EAAE,YAAY,EACrB,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EACrC,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAMjC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { codexUsageOf } from "./agent-options.js";
|
|
5
|
+
import { askCodex } from "./codex-app-server.js";
|
|
6
|
+
const SURVEY_TIMEOUT_MS = 60_000;
|
|
7
|
+
/**
|
|
8
|
+
* The options `profile` offers, from a session opened in a scratch directory only to read
|
|
9
|
+
* them and then closed: no prompt, so no model turn. Lets the sidebar offer a model before
|
|
10
|
+
* the agent's first turn. Undefined when the agent cannot be started or is not logged in.
|
|
11
|
+
*/
|
|
12
|
+
export async function surveyOptions(profile, launcher, env, stop) {
|
|
13
|
+
const cwd = await mkdtemp(join(tmpdir(), "parlor-survey-"));
|
|
14
|
+
const timeout = AbortSignal.timeout(SURVEY_TIMEOUT_MS);
|
|
15
|
+
const signal = stop ? AbortSignal.any([stop, timeout]) : timeout;
|
|
16
|
+
try {
|
|
17
|
+
const connection = await launcher.launch(profile, { cwd, env, signal });
|
|
18
|
+
const end = () => void connection.kill();
|
|
19
|
+
signal.addEventListener("abort", end, { once: true });
|
|
20
|
+
try {
|
|
21
|
+
if (!connection.capabilities.ready)
|
|
22
|
+
return undefined;
|
|
23
|
+
const session = await connection.openSession({
|
|
24
|
+
cwd,
|
|
25
|
+
mcpServers: [],
|
|
26
|
+
pinAskMode: true,
|
|
27
|
+
});
|
|
28
|
+
return session.options;
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
signal.removeEventListener("abort", end);
|
|
32
|
+
await connection.kill();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
finally {
|
|
39
|
+
await rm(cwd, { recursive: true, force: true });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The account's allowance from the agent's own CLI, for an agent whose ACP adapter does not
|
|
44
|
+
* carry it: `codex-acp` drops Codex's rate limits. Claude Code sends its own over ACP.
|
|
45
|
+
*/
|
|
46
|
+
export async function readUsage(profile, env, now) {
|
|
47
|
+
if (profile.id !== "codex")
|
|
48
|
+
return undefined;
|
|
49
|
+
return codexUsageOf(await askCodex(profile.binary, "account/rateLimits/read", null, env), now);
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=agent-survey.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-survey.js","sourceRoot":"","sources":["../src/agent-survey.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAKjC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEjD,MAAM,iBAAiB,GAAG,MAAM,CAAC;AAEjC;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,OAAqB,EACrB,QAAuB,EACvB,GAAqC,EACrC,IAAkB;IAElB,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,gBAAgB,CAAC,CAAC,CAAC;IAC5D,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACvD,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;IACjE,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC;QACxE,MAAM,GAAG,GAAG,GAAG,EAAE,CAAC,KAAK,UAAU,CAAC,IAAI,EAAE,CAAC;QACzC,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACtD,IAAI,CAAC;YACH,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,KAAK;gBAAE,OAAO,SAAS,CAAC;YACrD,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,WAAW,CAAC;gBAC3C,GAAG;gBACH,UAAU,EAAE,EAAE;gBACd,UAAU,EAAE,IAAI;aACjB,CAAC,CAAC;YACH,OAAO,OAAO,CAAC,OAAO,CAAC;QACzB,CAAC;gBAAS,CAAC;YACT,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;YACzC,MAAM,UAAU,CAAC,IAAI,EAAE,CAAC;QAC1B,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;YAAS,CAAC;QACT,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAClD,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,OAAqB,EACrB,GAAqC,EACrC,GAAW;IAEX,IAAI,OAAO,CAAC,EAAE,KAAK,OAAO;QAAE,OAAO,SAAS,CAAC;IAC7C,OAAO,YAAY,CACjB,MAAM,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,yBAAyB,EAAE,IAAI,EAAE,GAAG,CAAC,EACpE,GAAG,CACJ,CAAC;AACJ,CAAC","sourcesContent":["import { mkdtemp, rm } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\n\nimport type { AgentOption, AgentUsage } from \"@parlor/chat\";\n\nimport type { AgentLauncher } from \"./acp.js\";\nimport { codexUsageOf } from \"./agent-options.js\";\nimport type { AgentProfile } from \"./agents.js\";\nimport { askCodex } from \"./codex-app-server.js\";\n\nconst SURVEY_TIMEOUT_MS = 60_000;\n\n/**\n * The options `profile` offers, from a session opened in a scratch directory only to read\n * them and then closed: no prompt, so no model turn. Lets the sidebar offer a model before\n * the agent's first turn. Undefined when the agent cannot be started or is not logged in.\n */\nexport async function surveyOptions(\n profile: AgentProfile,\n launcher: AgentLauncher,\n env: Readonly<Record<string, string>>,\n stop?: AbortSignal,\n): Promise<readonly AgentOption[] | undefined> {\n const cwd = await mkdtemp(join(tmpdir(), \"parlor-survey-\"));\n const timeout = AbortSignal.timeout(SURVEY_TIMEOUT_MS);\n const signal = stop ? AbortSignal.any([stop, timeout]) : timeout;\n try {\n const connection = await launcher.launch(profile, { cwd, env, signal });\n const end = () => void connection.kill();\n signal.addEventListener(\"abort\", end, { once: true });\n try {\n if (!connection.capabilities.ready) return undefined;\n const session = await connection.openSession({\n cwd,\n mcpServers: [],\n pinAskMode: true,\n });\n return session.options;\n } finally {\n signal.removeEventListener(\"abort\", end);\n await connection.kill();\n }\n } catch {\n return undefined;\n } finally {\n await rm(cwd, { recursive: true, force: true });\n }\n}\n\n/**\n * The account's allowance from the agent's own CLI, for an agent whose ACP adapter does not\n * carry it: `codex-acp` drops Codex's rate limits. Claude Code sends its own over ACP.\n */\nexport async function readUsage(\n profile: AgentProfile,\n env: Readonly<Record<string, string>>,\n now: number,\n): Promise<AgentUsage | undefined> {\n if (profile.id !== \"codex\") return undefined;\n return codexUsageOf(\n await askCodex(profile.binary, \"account/rateLimits/read\", null, env),\n now,\n );\n}\n"]}
|
package/dist/agents.d.ts
CHANGED
|
@@ -6,6 +6,11 @@ export interface AgentProfile {
|
|
|
6
6
|
readonly launch: AgentLaunch;
|
|
7
7
|
/** The CLI behind it, for the installed-and-logged-in check. */
|
|
8
8
|
readonly binary: string;
|
|
9
|
+
/**
|
|
10
|
+
* Where a desktop app ships the same CLI, for a computer with the app and no CLI on its
|
|
11
|
+
* PATH: the app reads the CLI's own settings, so the agent behaves the same either way.
|
|
12
|
+
*/
|
|
13
|
+
readonly apps?: readonly string[] | undefined;
|
|
9
14
|
}
|
|
10
15
|
/**
|
|
11
16
|
* `adapter`: an npm ACP adapter at an exact version, run from the copy this build embeds
|
|
@@ -27,6 +32,16 @@ export declare const ADAPTER_BINARY_ENV: Readonly<Record<string, string>>;
|
|
|
27
32
|
export declare const DETECT_TIMEOUT_MS = 3000;
|
|
28
33
|
/** Built-ins plus the user's own `agents.json`, which only this computer's user edits. */
|
|
29
34
|
export declare function loadProfiles(file: string): Promise<readonly AgentProfile[]>;
|
|
35
|
+
/** The agents installed here, each with its profile's `binary` set to the CLI that answered. */
|
|
36
|
+
export interface FoundAgents {
|
|
37
|
+
readonly agents: readonly AgentInfo[];
|
|
38
|
+
readonly profiles: readonly AgentProfile[];
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Looks for each agent's CLI on the login-shell PATH, then inside the desktop apps that ship
|
|
42
|
+
* it. An agent found nowhere is left out; the profiles it returns run what was found.
|
|
43
|
+
*/
|
|
44
|
+
export declare function findAgents(profiles: readonly AgentProfile[]): Promise<FoundAgents>;
|
|
30
45
|
/** What `hostReport` sends: the installed agents; one that is not installed is left out. */
|
|
31
46
|
export declare function detectAgents(profiles: readonly AgentProfile[]): Promise<AgentInfo[]>;
|
|
32
47
|
//# sourceMappingURL=agents.d.ts.map
|
package/dist/agents.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../src/agents.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAI9C,mGAAmG;AACnG,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,gEAAgE;IAChE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../src/agents.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAI9C,mGAAmG;AACnG,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,gEAAgE;IAChE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;CAC/C;AAED;;;GAGG;AACH,MAAM,MAAM,WAAW,GACnB;IACE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;CAC/C,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;CAClC,CAAC;AAEN,eAAO,MAAM,cAAc,EAAE,SAAS,YAAY,EA4BjD,CAAC;AAEF,iHAAiH;AACjH,eAAO,MAAM,kBAAkB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAG/D,CAAC;AAEF,eAAO,MAAM,iBAAiB,OAAQ,CAAC;AA6CvC,0FAA0F;AAC1F,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,YAAY,EAAE,CAAC,CAkBjF;AAkBD,gGAAgG;AAChG,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,CAAC;IACtC,QAAQ,CAAC,QAAQ,EAAE,SAAS,YAAY,EAAE,CAAC;CAC5C;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,SAAS,YAAY,EAAE,GAChC,OAAO,CAAC,WAAW,CAAC,CAwCtB;AAED,4FAA4F;AAC5F,wBAAsB,YAAY,CAChC,QAAQ,EAAE,SAAS,YAAY,EAAE,GAChC,OAAO,CAAC,SAAS,EAAE,CAAC,CAEtB"}
|
package/dist/agents.js
CHANGED
|
@@ -21,6 +21,7 @@ export const AGENT_PROFILES = [
|
|
|
21
21
|
version: "2.2.2",
|
|
22
22
|
},
|
|
23
23
|
binary: "codex",
|
|
24
|
+
apps: ["/Applications/ChatGPT.app/Contents/Resources/codex-cli/bin/codex"],
|
|
24
25
|
},
|
|
25
26
|
{
|
|
26
27
|
id: "gemini",
|
|
@@ -62,12 +63,14 @@ function profileOf(value) {
|
|
|
62
63
|
if (!isFields(value))
|
|
63
64
|
return undefined;
|
|
64
65
|
const launch = launchOf(value.launch);
|
|
65
|
-
const { id, name, binary } = value;
|
|
66
|
+
const { id, name, binary, apps } = value;
|
|
66
67
|
if (!launch || typeof id !== "string" || typeof name !== "string")
|
|
67
68
|
return undefined;
|
|
68
69
|
if (typeof binary !== "string" || id === "")
|
|
69
70
|
return undefined;
|
|
70
|
-
|
|
71
|
+
if (apps !== undefined && !isStrings(apps))
|
|
72
|
+
return undefined;
|
|
73
|
+
return { id, name, launch, binary, ...(apps ? { apps } : {}) };
|
|
71
74
|
}
|
|
72
75
|
/** Built-ins plus the user's own `agents.json`, which only this computer's user edits. */
|
|
73
76
|
export async function loadProfiles(file) {
|
|
@@ -104,29 +107,50 @@ function versionOf(binary, path) {
|
|
|
104
107
|
});
|
|
105
108
|
});
|
|
106
109
|
}
|
|
107
|
-
/**
|
|
108
|
-
|
|
110
|
+
/**
|
|
111
|
+
* Looks for each agent's CLI on the login-shell PATH, then inside the desktop apps that ship
|
|
112
|
+
* it. An agent found nowhere is left out; the profiles it returns run what was found.
|
|
113
|
+
*/
|
|
114
|
+
export async function findAgents(profiles) {
|
|
109
115
|
const path = await agentPath();
|
|
110
116
|
const found = await Promise.all(profiles.map(async (profile) => {
|
|
111
|
-
|
|
117
|
+
let binary = profile.binary;
|
|
118
|
+
let output = await versionOf(binary, path);
|
|
119
|
+
for (const app of profile.apps ?? []) {
|
|
120
|
+
if (output !== "missing")
|
|
121
|
+
break;
|
|
122
|
+
binary = app;
|
|
123
|
+
output = await versionOf(app, path);
|
|
124
|
+
}
|
|
112
125
|
if (output === "missing")
|
|
113
126
|
return undefined;
|
|
127
|
+
const resolved = binary === profile.binary ? profile : { ...profile, binary };
|
|
114
128
|
if (output === "broken") {
|
|
115
|
-
|
|
129
|
+
const info = {
|
|
116
130
|
id: profile.id,
|
|
117
131
|
name: profile.name,
|
|
118
132
|
ready: false,
|
|
119
|
-
problem: `\`${
|
|
133
|
+
problem: `\`${binary} --version\` failed: check that ${profile.name} runs in a terminal.`,
|
|
120
134
|
};
|
|
135
|
+
return { info, profile: resolved };
|
|
121
136
|
}
|
|
122
137
|
const version = /\d+\.\d+\.\d+[\w.-]*/.exec(output)?.[0];
|
|
123
|
-
|
|
138
|
+
const info = {
|
|
124
139
|
id: profile.id,
|
|
125
140
|
name: profile.name,
|
|
126
141
|
ready: true,
|
|
127
142
|
...(version ? { version } : {}),
|
|
128
143
|
};
|
|
144
|
+
return { info, profile: resolved };
|
|
129
145
|
}));
|
|
130
|
-
|
|
146
|
+
const present = found.filter((entry) => entry !== undefined);
|
|
147
|
+
return {
|
|
148
|
+
agents: present.map((entry) => entry.info),
|
|
149
|
+
profiles: profiles.map((profile) => present.find((entry) => entry.profile.id === profile.id)?.profile ?? profile),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
/** What `hostReport` sends: the installed agents; one that is not installed is left out. */
|
|
153
|
+
export async function detectAgents(profiles) {
|
|
154
|
+
return [...(await findAgents(profiles)).agents];
|
|
131
155
|
}
|
|
132
156
|
//# sourceMappingURL=agents.js.map
|
package/dist/agents.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agents.js","sourceRoot":"","sources":["../src/agents.ts"],"names":[],"mappings":"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","sourcesContent":["import { execFile } from \"node:child_process\";\nimport { readFile } from \"node:fs/promises\";\n\nimport type { AgentInfo } from \"@parlor/chat\";\n\nimport { agentPath } from \"./shell-path.js\";\n\n/** The app picks an agent by `id` only, never by command, so no remote party chooses what runs. */\nexport interface AgentProfile {\n readonly id: string;\n readonly name: string;\n readonly launch: AgentLaunch;\n /** The CLI behind it, for the installed-and-logged-in check. */\n readonly binary: string;\n}\n\n/**\n * `adapter`: an npm ACP adapter at an exact version, run from the copy this build embeds\n * and else through `npx`. `native`: an agent CLI that speaks ACP itself, found on the login-shell PATH.\n */\nexport type AgentLaunch =\n | {\n readonly kind: \"adapter\";\n readonly package: string;\n readonly version: string;\n readonly args?: readonly string[] | undefined;\n }\n | {\n readonly kind: \"native\";\n readonly command: string;\n readonly args: readonly string[];\n };\n\nexport const AGENT_PROFILES: readonly AgentProfile[] = [\n {\n id: \"claude\",\n name: \"Claude Code\",\n launch: {\n kind: \"adapter\",\n package: \"@agentclientprotocol/claude-agent-acp\",\n version: \"0.93.0\",\n },\n binary: \"claude\",\n },\n {\n id: \"codex\",\n name: \"Codex\",\n launch: {\n kind: \"adapter\",\n package: \"@agentclientprotocol/codex-acp\",\n version: \"2.2.2\",\n },\n binary: \"codex\",\n },\n {\n id: \"gemini\",\n name: \"Gemini CLI\",\n launch: { kind: \"native\", command: \"gemini\", args: [\"--acp\"] },\n binary: \"gemini\",\n },\n];\n\n/** Where each adapter looks for the agent CLI it drives; its own search does not survive a single executable. */\nexport const ADAPTER_BINARY_ENV: Readonly<Record<string, string>> = {\n \"@agentclientprotocol/claude-agent-acp\": \"CLAUDE_CODE_EXECUTABLE\",\n \"@agentclientprotocol/codex-acp\": \"CODEX_PATH\",\n};\n\nexport const DETECT_TIMEOUT_MS = 3_000;\n\ntype Fields = Readonly<Record<string, unknown>>;\n\nconst isFields = (value: unknown): value is Fields =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst isStrings = (value: unknown): value is string[] =>\n Array.isArray(value) && value.every((entry) => typeof entry === \"string\");\n\nfunction launchOf(value: unknown): AgentLaunch | undefined {\n if (!isFields(value)) return undefined;\n if (\n value.kind === \"native\" &&\n typeof value.command === \"string\" &&\n isStrings(value.args)\n ) {\n return { kind: \"native\", command: value.command, args: value.args };\n }\n if (\n value.kind === \"adapter\" &&\n typeof value.package === \"string\" &&\n typeof value.version === \"string\" &&\n (value.args === undefined || isStrings(value.args))\n ) {\n return {\n kind: \"adapter\",\n package: value.package,\n version: value.version,\n args: value.args,\n };\n }\n return undefined;\n}\n\nfunction profileOf(value: unknown): AgentProfile | undefined {\n if (!isFields(value)) return undefined;\n const launch = launchOf(value.launch);\n const { id, name, binary } = value;\n if (!launch || typeof id !== \"string\" || typeof name !== \"string\") return undefined;\n if (typeof binary !== \"string\" || id === \"\") return undefined;\n return { id, name, launch, binary };\n}\n\n/** Built-ins plus the user's own `agents.json`, which only this computer's user edits. */\nexport async function loadProfiles(file: string): Promise<readonly AgentProfile[]> {\n let text: string;\n try {\n text = await readFile(file, \"utf8\");\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return AGENT_PROFILES;\n throw error;\n }\n const listed: unknown = JSON.parse(text);\n if (!Array.isArray(listed))\n throw new Error(`${file} must hold a JSON array of agent profiles.`);\n const own = listed.map((entry, at) => {\n const profile = profileOf(entry);\n if (!profile) throw new Error(`${file}: entry ${at + 1} is not an agent profile.`);\n return profile;\n });\n const ids = new Set(own.map((profile) => profile.id));\n return [...AGENT_PROFILES.filter((profile) => !ids.has(profile.id)), ...own];\n}\n\nfunction versionOf(binary: string, path: string): Promise<string | \"missing\" | \"broken\"> {\n return new Promise((resolve) => {\n execFile(\n binary,\n [\"--version\"],\n { timeout: DETECT_TIMEOUT_MS, env: { ...process.env, PATH: path } },\n (error, stdout) => {\n if ((error as NodeJS.ErrnoException | null)?.code === \"ENOENT\")\n resolve(\"missing\");\n else if (error) resolve(\"broken\");\n else resolve(stdout.trim());\n },\n );\n });\n}\n\n/** What `hostReport` sends: the installed agents; one that is not installed is left out. */\nexport async function detectAgents(\n profiles: readonly AgentProfile[],\n): Promise<AgentInfo[]> {\n const path = await agentPath();\n const found = await Promise.all(\n profiles.map(async (profile): Promise<AgentInfo | undefined> => {\n const output = await versionOf(profile.binary, path);\n if (output === \"missing\") return undefined;\n if (output === \"broken\") {\n return {\n id: profile.id,\n name: profile.name,\n ready: false,\n problem: `\\`${profile.binary} --version\\` failed: check that ${profile.name} runs in a terminal.`,\n };\n }\n const version = /\\d+\\.\\d+\\.\\d+[\\w.-]*/.exec(output)?.[0];\n return {\n id: profile.id,\n name: profile.name,\n ready: true,\n ...(version ? { version } : {}),\n };\n }),\n );\n return found.filter((agent): agent is AgentInfo => agent !== undefined);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"agents.js","sourceRoot":"","sources":["../src/agents.ts"],"names":[],"mappings":"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","sourcesContent":["import { execFile } from \"node:child_process\";\nimport { readFile } from \"node:fs/promises\";\n\nimport type { AgentInfo } from \"@parlor/chat\";\n\nimport { agentPath } from \"./shell-path.js\";\n\n/** The app picks an agent by `id` only, never by command, so no remote party chooses what runs. */\nexport interface AgentProfile {\n readonly id: string;\n readonly name: string;\n readonly launch: AgentLaunch;\n /** The CLI behind it, for the installed-and-logged-in check. */\n readonly binary: string;\n /**\n * Where a desktop app ships the same CLI, for a computer with the app and no CLI on its\n * PATH: the app reads the CLI's own settings, so the agent behaves the same either way.\n */\n readonly apps?: readonly string[] | undefined;\n}\n\n/**\n * `adapter`: an npm ACP adapter at an exact version, run from the copy this build embeds\n * and else through `npx`. `native`: an agent CLI that speaks ACP itself, found on the login-shell PATH.\n */\nexport type AgentLaunch =\n | {\n readonly kind: \"adapter\";\n readonly package: string;\n readonly version: string;\n readonly args?: readonly string[] | undefined;\n }\n | {\n readonly kind: \"native\";\n readonly command: string;\n readonly args: readonly string[];\n };\n\nexport const AGENT_PROFILES: readonly AgentProfile[] = [\n {\n id: \"claude\",\n name: \"Claude Code\",\n launch: {\n kind: \"adapter\",\n package: \"@agentclientprotocol/claude-agent-acp\",\n version: \"0.93.0\",\n },\n binary: \"claude\",\n },\n {\n id: \"codex\",\n name: \"Codex\",\n launch: {\n kind: \"adapter\",\n package: \"@agentclientprotocol/codex-acp\",\n version: \"2.2.2\",\n },\n binary: \"codex\",\n apps: [\"/Applications/ChatGPT.app/Contents/Resources/codex-cli/bin/codex\"],\n },\n {\n id: \"gemini\",\n name: \"Gemini CLI\",\n launch: { kind: \"native\", command: \"gemini\", args: [\"--acp\"] },\n binary: \"gemini\",\n },\n];\n\n/** Where each adapter looks for the agent CLI it drives; its own search does not survive a single executable. */\nexport const ADAPTER_BINARY_ENV: Readonly<Record<string, string>> = {\n \"@agentclientprotocol/claude-agent-acp\": \"CLAUDE_CODE_EXECUTABLE\",\n \"@agentclientprotocol/codex-acp\": \"CODEX_PATH\",\n};\n\nexport const DETECT_TIMEOUT_MS = 3_000;\n\ntype Fields = Readonly<Record<string, unknown>>;\n\nconst isFields = (value: unknown): value is Fields =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst isStrings = (value: unknown): value is string[] =>\n Array.isArray(value) && value.every((entry) => typeof entry === \"string\");\n\nfunction launchOf(value: unknown): AgentLaunch | undefined {\n if (!isFields(value)) return undefined;\n if (\n value.kind === \"native\" &&\n typeof value.command === \"string\" &&\n isStrings(value.args)\n ) {\n return { kind: \"native\", command: value.command, args: value.args };\n }\n if (\n value.kind === \"adapter\" &&\n typeof value.package === \"string\" &&\n typeof value.version === \"string\" &&\n (value.args === undefined || isStrings(value.args))\n ) {\n return {\n kind: \"adapter\",\n package: value.package,\n version: value.version,\n args: value.args,\n };\n }\n return undefined;\n}\n\nfunction profileOf(value: unknown): AgentProfile | undefined {\n if (!isFields(value)) return undefined;\n const launch = launchOf(value.launch);\n const { id, name, binary, apps } = value;\n if (!launch || typeof id !== \"string\" || typeof name !== \"string\") return undefined;\n if (typeof binary !== \"string\" || id === \"\") return undefined;\n if (apps !== undefined && !isStrings(apps)) return undefined;\n return { id, name, launch, binary, ...(apps ? { apps } : {}) };\n}\n\n/** Built-ins plus the user's own `agents.json`, which only this computer's user edits. */\nexport async function loadProfiles(file: string): Promise<readonly AgentProfile[]> {\n let text: string;\n try {\n text = await readFile(file, \"utf8\");\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return AGENT_PROFILES;\n throw error;\n }\n const listed: unknown = JSON.parse(text);\n if (!Array.isArray(listed))\n throw new Error(`${file} must hold a JSON array of agent profiles.`);\n const own = listed.map((entry, at) => {\n const profile = profileOf(entry);\n if (!profile) throw new Error(`${file}: entry ${at + 1} is not an agent profile.`);\n return profile;\n });\n const ids = new Set(own.map((profile) => profile.id));\n return [...AGENT_PROFILES.filter((profile) => !ids.has(profile.id)), ...own];\n}\n\nfunction versionOf(binary: string, path: string): Promise<string | \"missing\" | \"broken\"> {\n return new Promise((resolve) => {\n execFile(\n binary,\n [\"--version\"],\n { timeout: DETECT_TIMEOUT_MS, env: { ...process.env, PATH: path } },\n (error, stdout) => {\n if ((error as NodeJS.ErrnoException | null)?.code === \"ENOENT\")\n resolve(\"missing\");\n else if (error) resolve(\"broken\");\n else resolve(stdout.trim());\n },\n );\n });\n}\n\n/** The agents installed here, each with its profile's `binary` set to the CLI that answered. */\nexport interface FoundAgents {\n readonly agents: readonly AgentInfo[];\n readonly profiles: readonly AgentProfile[];\n}\n\n/**\n * Looks for each agent's CLI on the login-shell PATH, then inside the desktop apps that ship\n * it. An agent found nowhere is left out; the profiles it returns run what was found.\n */\nexport async function findAgents(\n profiles: readonly AgentProfile[],\n): Promise<FoundAgents> {\n const path = await agentPath();\n const found = await Promise.all(\n profiles.map(async (profile) => {\n let binary = profile.binary;\n let output = await versionOf(binary, path);\n for (const app of profile.apps ?? []) {\n if (output !== \"missing\") break;\n binary = app;\n output = await versionOf(app, path);\n }\n if (output === \"missing\") return undefined;\n const resolved = binary === profile.binary ? profile : { ...profile, binary };\n if (output === \"broken\") {\n const info: AgentInfo = {\n id: profile.id,\n name: profile.name,\n ready: false,\n problem: `\\`${binary} --version\\` failed: check that ${profile.name} runs in a terminal.`,\n };\n return { info, profile: resolved };\n }\n const version = /\\d+\\.\\d+\\.\\d+[\\w.-]*/.exec(output)?.[0];\n const info: AgentInfo = {\n id: profile.id,\n name: profile.name,\n ready: true,\n ...(version ? { version } : {}),\n };\n return { info, profile: resolved };\n }),\n );\n const present = found.filter((entry) => entry !== undefined);\n return {\n agents: present.map((entry) => entry.info),\n profiles: profiles.map(\n (profile) =>\n present.find((entry) => entry.profile.id === profile.id)?.profile ?? profile,\n ),\n };\n}\n\n/** What `hostReport` sends: the installed agents; one that is not installed is left out. */\nexport async function detectAgents(\n profiles: readonly AgentProfile[],\n): Promise<AgentInfo[]> {\n return [...(await findAgents(profiles)).agents];\n}\n"]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One JSON-RPC call to a Codex app-server of its own, started for it and stopped after. For
|
|
3
|
+
* what Codex keeps but `codex-acp` drops, such as the account's rate limits; it costs no
|
|
4
|
+
* model turn. Undefined when anything goes wrong: Codex is missing, slow, or answers oddly.
|
|
5
|
+
*/
|
|
6
|
+
export declare function askCodex(binary: string, method: string, params: unknown, env: Readonly<Record<string, string>>): Promise<unknown>;
|
|
7
|
+
//# sourceMappingURL=codex-app-server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex-app-server.d.ts","sourceRoot":"","sources":["../src/codex-app-server.ts"],"names":[],"mappings":"AAUA;;;;GAIG;AACH,wBAAgB,QAAQ,CACtB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,OAAO,EACf,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GACpC,OAAO,CAAC,OAAO,CAAC,CA+BlB"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { onLines } from "./lines.js";
|
|
3
|
+
const INITIALIZE = 1;
|
|
4
|
+
const CALL = 2;
|
|
5
|
+
const TIMEOUT_MS = 30_000;
|
|
6
|
+
/** Codex only echoes it back inside a user agent. */
|
|
7
|
+
const CLIENT_INFO = { name: "parlor-host", version: "1" };
|
|
8
|
+
/**
|
|
9
|
+
* One JSON-RPC call to a Codex app-server of its own, started for it and stopped after. For
|
|
10
|
+
* what Codex keeps but `codex-acp` drops, such as the account's rate limits; it costs no
|
|
11
|
+
* model turn. Undefined when anything goes wrong: Codex is missing, slow, or answers oddly.
|
|
12
|
+
*/
|
|
13
|
+
export function askCodex(binary, method, params, env) {
|
|
14
|
+
return new Promise((resolve) => {
|
|
15
|
+
let answer;
|
|
16
|
+
const child = spawn(binary, ["app-server"], {
|
|
17
|
+
env,
|
|
18
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
19
|
+
});
|
|
20
|
+
const timer = setTimeout(() => child.kill("SIGKILL"), TIMEOUT_MS);
|
|
21
|
+
const send = (message) => child.stdin.write(`${JSON.stringify({ jsonrpc: "2.0", ...message })}\n`);
|
|
22
|
+
onLines(child.stdout, (line) => {
|
|
23
|
+
let message;
|
|
24
|
+
try {
|
|
25
|
+
message = JSON.parse(line);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (message.id === INITIALIZE)
|
|
31
|
+
send({ id: CALL, method, params });
|
|
32
|
+
else if (message.id === CALL) {
|
|
33
|
+
answer = message.result;
|
|
34
|
+
child.kill();
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
child.stdin.on("error", () => undefined);
|
|
38
|
+
child.once("error", () => resolve(undefined));
|
|
39
|
+
child.once("close", () => {
|
|
40
|
+
clearTimeout(timer);
|
|
41
|
+
resolve(answer);
|
|
42
|
+
});
|
|
43
|
+
send({ id: INITIALIZE, method: "initialize", params: { clientInfo: CLIENT_INFO } });
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=codex-app-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex-app-server.js","sourceRoot":"","sources":["../src/codex-app-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE3C,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAErC,MAAM,UAAU,GAAG,CAAC,CAAC;AACrB,MAAM,IAAI,GAAG,CAAC,CAAC;AACf,MAAM,UAAU,GAAG,MAAM,CAAC;AAC1B,qDAAqD;AACrD,MAAM,WAAW,GAAG,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC;AAE1D;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CACtB,MAAc,EACd,MAAc,EACd,MAAe,EACf,GAAqC;IAErC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,IAAI,MAAe,CAAC;QACpB,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,YAAY,CAAC,EAAE;YAC1C,GAAG;YACH,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC;SAClC,CAAC,CAAC;QACH,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,UAAU,CAAC,CAAC;QAClE,MAAM,IAAI,GAAG,CAAC,OAAe,EAAE,EAAE,CAC/B,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC;QAC3E,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;YAC7B,IAAI,OAA6D,CAAC;YAClE,IAAI,CAAC;gBACH,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAmB,CAAC;YAC/C,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO;YACT,CAAC;YACD,IAAI,OAAO,CAAC,EAAE,KAAK,UAAU;gBAAE,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;iBAC7D,IAAI,OAAO,CAAC,EAAE,KAAK,IAAI,EAAE,CAAC;gBAC7B,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;gBACxB,KAAK,CAAC,IAAI,EAAE,CAAC;YACf,CAAC;QACH,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QACzC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC;QAC9C,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE;YACvB,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,OAAO,CAAC,MAAM,CAAC,CAAC;QAClB,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,EAAE,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,WAAW,EAAE,EAAE,CAAC,CAAC;IACtF,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { spawn } from \"node:child_process\";\n\nimport { onLines } from \"./lines.js\";\n\nconst INITIALIZE = 1;\nconst CALL = 2;\nconst TIMEOUT_MS = 30_000;\n/** Codex only echoes it back inside a user agent. */\nconst CLIENT_INFO = { name: \"parlor-host\", version: \"1\" };\n\n/**\n * One JSON-RPC call to a Codex app-server of its own, started for it and stopped after. For\n * what Codex keeps but `codex-acp` drops, such as the account's rate limits; it costs no\n * model turn. Undefined when anything goes wrong: Codex is missing, slow, or answers oddly.\n */\nexport function askCodex(\n binary: string,\n method: string,\n params: unknown,\n env: Readonly<Record<string, string>>,\n): Promise<unknown> {\n return new Promise((resolve) => {\n let answer: unknown;\n const child = spawn(binary, [\"app-server\"], {\n env,\n stdio: [\"pipe\", \"pipe\", \"ignore\"],\n });\n const timer = setTimeout(() => child.kill(\"SIGKILL\"), TIMEOUT_MS);\n const send = (message: object) =>\n child.stdin.write(`${JSON.stringify({ jsonrpc: \"2.0\", ...message })}\\n`);\n onLines(child.stdout, (line) => {\n let message: { readonly id?: unknown; readonly result?: unknown };\n try {\n message = JSON.parse(line) as typeof message;\n } catch {\n return;\n }\n if (message.id === INITIALIZE) send({ id: CALL, method, params });\n else if (message.id === CALL) {\n answer = message.result;\n child.kill();\n }\n });\n child.stdin.on(\"error\", () => undefined);\n child.once(\"error\", () => resolve(undefined));\n child.once(\"close\", () => {\n clearTimeout(timer);\n resolve(answer);\n });\n send({ id: INITIALIZE, method: \"initialize\", params: { clientInfo: CLIENT_INFO } });\n });\n}\n"]}
|
package/dist/daemon.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAY9C,OAAO,EAA8B,KAAK,UAAU,EAAE,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAMzF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAQ/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAyC,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AACvF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,KAAK,aAAa,EAAa,MAAM,eAAe,CAAC;AAI9D,OAAO,EAAE,KAAK,WAAW,EAAoC,MAAM,cAAc,CAAC;AAElF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAG7C,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;IACjC,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,6GAA6G;IAC7G,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACzC;AAED;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,kFAAkF;IAClF,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAAC;IAChE,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAAC;IACnE,kFAAkF;IAClF,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAAC;IAChF,4EAA4E;IAC5E,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAED,sFAAsF;AACtF,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAiBD;;;GAGG;AACH,wBAAsB,yBAAyB,CAC7C,IAAI,EAAE,MAAM,EACZ,KAAK,GAAE,MAAM,GAAG,SAAuB,GACtC,OAAO,CAAC,IAAI,CAAC,CAqBf;AAWD,6FAA6F;AAC7F,MAAM,WAAW,aAAc,SAAQ,UAAU;IAC/C,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;CACjC;AAED,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,SAAkB,IAAI,wBAAwB;IAC9C,cAEC;CACF;AAKD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,UAAU,GAAG,aAAa,CA0zBhE"}
|