@parlor/agent-host 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/acp.d.ts +21 -0
- package/dist/acp.d.ts.map +1 -1
- package/dist/acp.js +138 -12
- package/dist/acp.js.map +1 -1
- package/dist/agent-options.d.ts +17 -0
- package/dist/agent-options.d.ts.map +1 -0
- package/dist/agent-options.js +73 -0
- package/dist/agent-options.js.map +1 -0
- package/dist/agent-tools.d.ts +30 -0
- package/dist/agent-tools.d.ts.map +1 -0
- package/dist/agent-tools.js +71 -0
- package/dist/agent-tools.js.map +1 -0
- package/dist/control.d.ts +15 -0
- package/dist/control.d.ts.map +1 -1
- package/dist/control.js.map +1 -1
- package/dist/daemon.d.ts +10 -2
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +149 -14
- package/dist/daemon.js.map +1 -1
- package/dist/fold.d.ts.map +1 -1
- package/dist/fold.js +2 -0
- package/dist/fold.js.map +1 -1
- package/dist/grants.d.ts +9 -0
- package/dist/grants.d.ts.map +1 -1
- package/dist/grants.js.map +1 -1
- package/dist/learned.d.ts +23 -0
- package/dist/learned.d.ts.map +1 -0
- package/dist/learned.js +36 -0
- package/dist/learned.js.map +1 -0
- package/dist/main.js +105 -9
- package/dist/main.js.map +1 -1
- package/dist/mcp-proxy.d.ts +26 -3
- package/dist/mcp-proxy.d.ts.map +1 -1
- package/dist/mcp-proxy.js +180 -9
- package/dist/mcp-proxy.js.map +1 -1
- package/dist/pairing.d.ts +2 -0
- package/dist/pairing.d.ts.map +1 -1
- package/dist/pairing.js +25 -3
- package/dist/pairing.js.map +1 -1
- package/dist/paths.d.ts +2 -0
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +1 -0
- package/dist/paths.js.map +1 -1
- package/dist/policy.d.ts +12 -2
- package/dist/policy.d.ts.map +1 -1
- package/dist/policy.js +13 -3
- package/dist/policy.js.map +1 -1
- package/dist/prompter.d.ts +5 -0
- package/dist/prompter.d.ts.map +1 -1
- package/dist/prompter.js +15 -0
- package/dist/prompter.js.map +1 -1
- package/dist/spec-kit.d.ts +2 -1
- package/dist/spec-kit.d.ts.map +1 -1
- package/dist/spec-kit.js +6 -1
- package/dist/spec-kit.js.map +1 -1
- package/dist/testing/fake-agent.d.ts +12 -0
- package/dist/testing/fake-agent.d.ts.map +1 -1
- package/dist/testing/fake-agent.js +11 -1
- package/dist/testing/fake-agent.js.map +1 -1
- package/dist/turn.d.ts +7 -1
- package/dist/turn.d.ts.map +1 -1
- package/dist/turn.js +16 -3
- package/dist/turn.js.map +1 -1
- package/package.json +9 -9
package/dist/fold.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fold.js","sourceRoot":"","sources":["../src/fold.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ChatItemBody, HostFrame, HostItemDraft } from \"@parlor/chat\";\nimport { asId, type ItemId, type TurnId } from \"@parlor/core\";\n\nimport type { AgentUpdate } from \"./acp.js\";\n\ninterface OpenText {\n readonly id: string;\n readonly kind: \"reply\" | \"reasoning\";\n readonly text: string;\n}\n\ninterface OpenTool {\n readonly id: string;\n readonly title: string;\n readonly app: boolean;\n readonly name: string;\n}\n\nexport interface TurnFold {\n readonly turnId: TurnId;\n readonly open: OpenText | undefined;\n readonly tools: ReadonlyMap<string, OpenTool>;\n readonly plan: Extract<AgentUpdate, { kind: \"plan\" }>[\"entries\"] | undefined;\n readonly nextId: number;\n}\n\nexport interface FoldStep {\n readonly fold: TurnFold;\n /** Ephemeral, streamed now. */\n readonly frames: readonly HostFrame[];\n /** Finished, appended durably. */\n readonly closed: readonly HostItemDraft[];\n}\n\nconst MAX_DETAIL_CHARS = 4_000;\n\ntype ToolFrameStatus = \"pending\" | \"running\";\n\nconst itemId = (id: string): ItemId => asId(\"item\", id);\n\nconst entriesOf = (\n entries: Extract<AgentUpdate, { kind: \"plan\" }>[\"entries\"],\n): ChatItemBody<\"plan\">[\"entries\"] => entries.map((entry) => ({ ...entry }));\n\nconst unchanged = (fold: TurnFold): FoldStep => ({ fold, frames: [], closed: [] });\n\nconst planItemOf = (fold: TurnFold) => itemId(`${fold.turnId}.0`);\n\nfunction allocate(fold: TurnFold): { readonly id: string; readonly fold: TurnFold } {\n return {\n id: `${fold.turnId}.${fold.nextId}`,\n fold: { ...fold, nextId: fold.nextId + 1 },\n };\n}\n\nfunction closeText(fold: TurnFold, interrupted = false): FoldStep {\n const open = fold.open;\n if (!open) return unchanged(fold);\n const closed: HostItemDraft =\n open.kind === \"reply\"\n ? {\n id: itemId(open.id),\n kind: \"reply\",\n body: {\n text: open.text,\n ...(interrupted ? { interrupted: true as const } : {}),\n },\n }\n : { id: itemId(open.id), kind: \"reasoning\", body: { text: open.text } };\n return { fold: { ...fold, open: undefined }, frames: [], closed: [closed] };\n}\n\nfunction toolFrame(tool: OpenTool, status: ToolFrameStatus): HostFrame {\n return {\n type: \"item\",\n item: { id: itemId(tool.id), kind: \"tool\", title: tool.title, status },\n };\n}\n\nfunction toolDraft(\n tool: OpenTool,\n status: ChatItemBody<\"tool\">[\"status\"],\n detail: string | undefined,\n): HostItemDraft {\n return {\n id: itemId(tool.id),\n kind: \"tool\",\n body: {\n title: tool.title,\n tool: { kind: tool.app ? \"app\" : \"local\", name: tool.name },\n status,\n ...(detail ? { detail: detail.slice(0, MAX_DETAIL_CHARS) } : {}),\n },\n };\n}\n\nfunction withTool(fold: TurnFold, toolCallId: string, tool: OpenTool | undefined) {\n const tools = new Map(fold.tools);\n if (tool) tools.set(toolCallId, tool);\n else tools.delete(toolCallId);\n return { ...fold, tools };\n}\n\nfunction appendText(fold: TurnFold, kind: OpenText[\"kind\"], text: string): FoldStep {\n if (text === \"\") return unchanged(fold);\n const switched =\n fold.open && fold.open.kind !== kind ? closeText(fold) : unchanged(fold);\n let current = switched.fold;\n let open = current.open;\n if (!open) {\n const next = allocate(current);\n current = next.fold;\n open = { id: next.id, kind, text: \"\" };\n }\n return {\n fold: { ...current, open: { ...open, text: open.text + text } },\n frames: [\n { type: \"delta\", itemId: itemId(open.id), kind, offset: open.text.length, text },\n ],\n closed: switched.closed,\n };\n}\n\nfunction startTool(\n fold: TurnFold,\n update: Extract<AgentUpdate, { kind: \"tool_start\" }>,\n appServer: string,\n): FoldStep {\n const known = fold.tools.get(update.toolCallId);\n if (known) {\n const tool = { ...known, title: update.title || known.title };\n return {\n fold: withTool(fold, update.toolCallId, tool),\n frames: [toolFrame(tool, \"pending\")],\n closed: [],\n };\n }\n const text = closeText(fold);\n const next = allocate(text.fold);\n const tool: OpenTool = {\n id: next.id,\n title: update.title,\n app: update.mcp !== undefined && update.mcp.server === appServer,\n name: update.mcp?.tool ?? update.toolKind,\n };\n return {\n fold: withTool(next.fold, update.toolCallId, tool),\n frames: [toolFrame(tool, \"pending\")],\n closed: text.closed,\n };\n}\n\nfunction updateTool(\n fold: TurnFold,\n update: Extract<AgentUpdate, { kind: \"tool_update\" }>,\n): FoldStep {\n const known = fold.tools.get(update.toolCallId);\n if (!known) return unchanged(fold);\n const tool = { ...known, title: update.title || known.title };\n if (update.status === \"completed\" || update.status === \"failed\") {\n return {\n fold: withTool(fold, update.toolCallId, undefined),\n frames: [],\n closed: [\n toolDraft(tool, update.status === \"completed\" ? \"done\" : \"failed\", update.detail),\n ],\n };\n }\n return {\n fold: withTool(fold, update.toolCallId, tool),\n frames: [toolFrame(tool, update.status === \"in_progress\" ? \"running\" : \"pending\")],\n closed: [],\n };\n}\n\n/** Pure: knows no ACP, HTTP or time. Item ids are `<turnId>.<n>`. */\nexport function startFold(turnId: TurnId): TurnFold {\n return { turnId, open: undefined, tools: new Map(), plan: undefined, nextId: 1 };\n}\n\n/** `appServer` is the MCP server name the host gave the agent for this app. */\nexport function foldUpdate(\n fold: TurnFold,\n update: AgentUpdate,\n appServer: string,\n): FoldStep {\n switch (update.kind) {\n case \"text\":\n return appendText(fold, \"reply\", update.text);\n case \"thought\":\n return appendText(fold, \"reasoning\", update.text);\n case \"tool_start\":\n return startTool(fold, update, appServer);\n case \"tool_update\":\n return updateTool(fold, update);\n case \"plan\":\n return {\n fold: { ...fold, plan: update.entries },\n frames: [\n {\n type: \"item\",\n item: {\n id: planItemOf(fold),\n kind: \"plan\",\n entries: entriesOf(update.entries),\n },\n },\n ],\n closed: [],\n };\n case \"mode\":\n return unchanged(fold);\n }\n}\n\n/** Closes what is open; `interrupted` marks the last reply when the user stopped the turn. */\nexport function finishFold(fold: TurnFold, interrupted: boolean): FoldStep {\n const text = closeText(fold, interrupted);\n const tools = [...fold.tools.values()].map((tool) =>\n toolDraft(tool, interrupted ? \"cancelled\" : \"done\", undefined),\n );\n const plan: HostItemDraft[] = fold.plan\n ? [{ id: planItemOf(fold), kind: \"plan\", body: { entries: entriesOf(fold.plan) } }]\n : [];\n return {\n fold: { ...text.fold, tools: new Map(), plan: undefined },\n frames: [],\n closed: [...text.closed, ...tools, ...plan],\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"fold.js","sourceRoot":"","sources":["../src/fold.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ChatItemBody, HostFrame, HostItemDraft } from \"@parlor/chat\";\nimport { asId, type ItemId, type TurnId } from \"@parlor/core\";\n\nimport type { AgentUpdate } from \"./acp.js\";\n\ninterface OpenText {\n readonly id: string;\n readonly kind: \"reply\" | \"reasoning\";\n readonly text: string;\n}\n\ninterface OpenTool {\n readonly id: string;\n readonly title: string;\n readonly app: boolean;\n readonly name: string;\n}\n\nexport interface TurnFold {\n readonly turnId: TurnId;\n readonly open: OpenText | undefined;\n readonly tools: ReadonlyMap<string, OpenTool>;\n readonly plan: Extract<AgentUpdate, { kind: \"plan\" }>[\"entries\"] | undefined;\n readonly nextId: number;\n}\n\nexport interface FoldStep {\n readonly fold: TurnFold;\n /** Ephemeral, streamed now. */\n readonly frames: readonly HostFrame[];\n /** Finished, appended durably. */\n readonly closed: readonly HostItemDraft[];\n}\n\nconst MAX_DETAIL_CHARS = 4_000;\n\ntype ToolFrameStatus = \"pending\" | \"running\";\n\nconst itemId = (id: string): ItemId => asId(\"item\", id);\n\nconst entriesOf = (\n entries: Extract<AgentUpdate, { kind: \"plan\" }>[\"entries\"],\n): ChatItemBody<\"plan\">[\"entries\"] => entries.map((entry) => ({ ...entry }));\n\nconst unchanged = (fold: TurnFold): FoldStep => ({ fold, frames: [], closed: [] });\n\nconst planItemOf = (fold: TurnFold) => itemId(`${fold.turnId}.0`);\n\nfunction allocate(fold: TurnFold): { readonly id: string; readonly fold: TurnFold } {\n return {\n id: `${fold.turnId}.${fold.nextId}`,\n fold: { ...fold, nextId: fold.nextId + 1 },\n };\n}\n\nfunction closeText(fold: TurnFold, interrupted = false): FoldStep {\n const open = fold.open;\n if (!open) return unchanged(fold);\n const closed: HostItemDraft =\n open.kind === \"reply\"\n ? {\n id: itemId(open.id),\n kind: \"reply\",\n body: {\n text: open.text,\n ...(interrupted ? { interrupted: true as const } : {}),\n },\n }\n : { id: itemId(open.id), kind: \"reasoning\", body: { text: open.text } };\n return { fold: { ...fold, open: undefined }, frames: [], closed: [closed] };\n}\n\nfunction toolFrame(tool: OpenTool, status: ToolFrameStatus): HostFrame {\n return {\n type: \"item\",\n item: { id: itemId(tool.id), kind: \"tool\", title: tool.title, status },\n };\n}\n\nfunction toolDraft(\n tool: OpenTool,\n status: ChatItemBody<\"tool\">[\"status\"],\n detail: string | undefined,\n): HostItemDraft {\n return {\n id: itemId(tool.id),\n kind: \"tool\",\n body: {\n title: tool.title,\n tool: { kind: tool.app ? \"app\" : \"local\", name: tool.name },\n status,\n ...(detail ? { detail: detail.slice(0, MAX_DETAIL_CHARS) } : {}),\n },\n };\n}\n\nfunction withTool(fold: TurnFold, toolCallId: string, tool: OpenTool | undefined) {\n const tools = new Map(fold.tools);\n if (tool) tools.set(toolCallId, tool);\n else tools.delete(toolCallId);\n return { ...fold, tools };\n}\n\nfunction appendText(fold: TurnFold, kind: OpenText[\"kind\"], text: string): FoldStep {\n if (text === \"\") return unchanged(fold);\n const switched =\n fold.open && fold.open.kind !== kind ? closeText(fold) : unchanged(fold);\n let current = switched.fold;\n let open = current.open;\n if (!open) {\n const next = allocate(current);\n current = next.fold;\n open = { id: next.id, kind, text: \"\" };\n }\n return {\n fold: { ...current, open: { ...open, text: open.text + text } },\n frames: [\n { type: \"delta\", itemId: itemId(open.id), kind, offset: open.text.length, text },\n ],\n closed: switched.closed,\n };\n}\n\nfunction startTool(\n fold: TurnFold,\n update: Extract<AgentUpdate, { kind: \"tool_start\" }>,\n appServer: string,\n): FoldStep {\n const known = fold.tools.get(update.toolCallId);\n if (known) {\n const tool = { ...known, title: update.title || known.title };\n return {\n fold: withTool(fold, update.toolCallId, tool),\n frames: [toolFrame(tool, \"pending\")],\n closed: [],\n };\n }\n const text = closeText(fold);\n const next = allocate(text.fold);\n const tool: OpenTool = {\n id: next.id,\n title: update.title,\n app: update.mcp !== undefined && update.mcp.server === appServer,\n name: update.mcp?.tool ?? update.toolKind,\n };\n return {\n fold: withTool(next.fold, update.toolCallId, tool),\n frames: [toolFrame(tool, \"pending\")],\n closed: text.closed,\n };\n}\n\nfunction updateTool(\n fold: TurnFold,\n update: Extract<AgentUpdate, { kind: \"tool_update\" }>,\n): FoldStep {\n const known = fold.tools.get(update.toolCallId);\n if (!known) return unchanged(fold);\n const tool = { ...known, title: update.title || known.title };\n if (update.status === \"completed\" || update.status === \"failed\") {\n return {\n fold: withTool(fold, update.toolCallId, undefined),\n frames: [],\n closed: [\n toolDraft(tool, update.status === \"completed\" ? \"done\" : \"failed\", update.detail),\n ],\n };\n }\n return {\n fold: withTool(fold, update.toolCallId, tool),\n frames: [toolFrame(tool, update.status === \"in_progress\" ? \"running\" : \"pending\")],\n closed: [],\n };\n}\n\n/** Pure: knows no ACP, HTTP or time. Item ids are `<turnId>.<n>`. */\nexport function startFold(turnId: TurnId): TurnFold {\n return { turnId, open: undefined, tools: new Map(), plan: undefined, nextId: 1 };\n}\n\n/** `appServer` is the MCP server name the host gave the agent for this app. */\nexport function foldUpdate(\n fold: TurnFold,\n update: AgentUpdate,\n appServer: string,\n): FoldStep {\n switch (update.kind) {\n case \"text\":\n return appendText(fold, \"reply\", update.text);\n case \"thought\":\n return appendText(fold, \"reasoning\", update.text);\n case \"tool_start\":\n return startTool(fold, update, appServer);\n case \"tool_update\":\n return updateTool(fold, update);\n case \"plan\":\n return {\n fold: { ...fold, plan: update.entries },\n frames: [\n {\n type: \"item\",\n item: {\n id: planItemOf(fold),\n kind: \"plan\",\n entries: entriesOf(update.entries),\n },\n },\n ],\n closed: [],\n };\n case \"mode\":\n case \"options\":\n case \"usage\":\n return unchanged(fold);\n }\n}\n\n/** Closes what is open; `interrupted` marks the last reply when the user stopped the turn. */\nexport function finishFold(fold: TurnFold, interrupted: boolean): FoldStep {\n const text = closeText(fold, interrupted);\n const tools = [...fold.tools.values()].map((tool) =>\n toolDraft(tool, interrupted ? \"cancelled\" : \"done\", undefined),\n );\n const plan: HostItemDraft[] = fold.plan\n ? [{ id: planItemOf(fold), kind: \"plan\", body: { entries: entriesOf(fold.plan) } }]\n : [];\n return {\n fold: { ...text.fold, tools: new Map(), plan: undefined },\n frames: [],\n closed: [...text.closed, ...tools, ...plan],\n };\n}\n"]}
|
package/dist/grants.d.ts
CHANGED
|
@@ -19,6 +19,15 @@ export interface AppGrant {
|
|
|
19
19
|
* browser proves it on its first prompt and is trusted without asking, then this is dropped.
|
|
20
20
|
*/
|
|
21
21
|
readonly pairingSecret?: string | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* Whether the coding agents on this computer may use the app outside its sidebar, as the
|
|
24
|
+
* user chose when connecting, and the name and agents it is registered under once it is.
|
|
25
|
+
*/
|
|
26
|
+
readonly agentTools?: {
|
|
27
|
+
readonly on: boolean;
|
|
28
|
+
readonly name?: string | undefined;
|
|
29
|
+
readonly agents?: readonly string[] | undefined;
|
|
30
|
+
} | undefined;
|
|
22
31
|
}
|
|
23
32
|
/** One key per app grant in the OS keychain; the server holds only its public half. */
|
|
24
33
|
export interface HostKeys {
|
package/dist/grants.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grants.d.ts","sourceRoot":"","sources":["../src/grants.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAKpD,sFAAsF;AACtF,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;IACjC,kFAAkF;IAClF,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,4CAA4C;IAC5C,QAAQ,CAAC,GAAG,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;IAChC,gGAAgG;IAChG,QAAQ,CAAC,QAAQ,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;IACrC;;;OAGG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"grants.d.ts","sourceRoot":"","sources":["../src/grants.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAKpD,sFAAsF;AACtF,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;IACjC,kFAAkF;IAClF,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,4CAA4C;IAC5C,QAAQ,CAAC,GAAG,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;IAChC,gGAAgG;IAChG,QAAQ,CAAC,QAAQ,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;IACrC;;;OAGG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C;;;OAGG;IACH,QAAQ,CAAC,UAAU,CAAC,EAChB;QACE,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;QACrB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACnC,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;KACjD,GACD,SAAS,CAAC;CACf;AAED,uFAAuF;AACvF,MAAM,WAAW,QAAQ;IACvB,sEAAsE;IACtE,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAChD,sDAAsD;IACtD,MAAM,CACJ,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,KAAK,GAAG,MAAM,CAAC;QAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;KAAE,GACjE,OAAO,CAAC,MAAM,CAAC,CAAC;IACnB,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1C;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAC;IAC/B,uCAAuC;IACvC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,IAAI,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3C,qEAAqE;IACrE,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,IAAI,OAAO,CAAC,SAAS,QAAQ,EAAE,CAAC,CAAC;IACrC,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAAC;IACtD,IAAI,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1C;AAED,8DAA8D;AAC9D,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAcvD;AA0JD,kGAAkG;AAClG,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,CAE1D;AAED,oFAAoF;AACpF,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,QAAQ,CAMvD"}
|
package/dist/grants.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grants.js","sourceRoot":"","sources":["../src/grants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAKtC,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAmD1C,8DAA8D;AAC9D,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,MAAM,MAAM,GAAG,QAAQ,CAA2B,IAAI,CAAC,CAAC;IACxD,OAAO;QACL,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;QACpD,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,SAAS,CAAC;QAC1D,IAAI,EAAE,CAAC,KAAK,EAAE,EAAE,CACd,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE;YACpB,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,KAAK,CAAC;QAC/B,CAAC,CAAC;QACJ,MAAM,EAAE,CAAC,SAAS,EAAE,EAAE,CACpB,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE;YACpB,OAAO,GAAG,CAAC,SAAS,CAAC,CAAC;QACxB,CAAC,CAAC;KACL,CAAC;AACJ,CAAC;AAED,MAAM,OAAO,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAW,CAAC;AAChE,MAAM,KAAK,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAW,CAAC;AAC1D,MAAM,MAAM,GAAG,kCAAkC,CAAC;AAoBlD,SAAS,YAAY,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,EAAc;IAClD,OAAO,UAAU,CAAC,QAAQ,CAAC;SACxB,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;SAC1C,MAAM,CAAC,WAAW,CAAC,CAAC;AACzB,CAAC;AAED,MAAM,MAAM,GAAG,CAAC,GAAc,EAAE,EAAE,CAAC,KAAK,EAAE,OAAmB,EAAE,EAAE,CAC/D,MAAM,CAAC,IAAI,CAAC,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CACjF,WAAW,CACZ,CAAC;AAEJ,SAAS,MAAM,CAAC,OAAoB;IAClC,MAAM,MAAM,GAAG,IAAI,GAAG,EAA0C,CAAC;IACjE,MAAM,IAAI,GAAG,CAAC,SAAiB,EAAE,EAAE;QACjC,IAAI,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAClC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,KAAK,GAAG,CAAC,KAAK,IAAI,EAAE;gBAClB,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;gBAC7C,IAAI,MAAM,KAAK,SAAS;oBAAE,OAAO,SAAS,CAAC;gBAC3C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CACvB,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CACrC,CAAC;gBACf,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE;oBAC3E,MAAM;iBACP,CAAC,CAAC;gBACH,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;YAC3D,CAAC,CAAC,EAAE,CAAC;YACL,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC;YAC5C,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;QAC/B,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;IACF,OAAO;QACL,KAAK,CAAC,MAAM,CAAC,SAAS;YACpB,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;YAChF,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;YACtE,MAAM,SAAS,GAAe;gBAC5B,GAAG,EAAE,QAAQ,CAAC,GAAI;gBAClB,GAAG,EAAE,QAAQ,CAAC,GAAI;gBAClB,CAAC,EAAE,QAAQ,CAAC,CAAE;gBACd,CAAC,EAAE,QAAQ,CAAC,CAAE;aACf,CAAC;YACF,MAAM,UAAU,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC;YAC3C,OAAO;gBACL,SAAS;gBACT,UAAU;gBACV,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC;gBAC7B,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;oBACtB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;oBAClE,MAAM,MAAM,GAAc,EAAE,GAAG,EAAE,GAAG,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC;oBAC5D,MAAM,OAAO,CAAC,KAAK,CACjB,SAAS,EACT,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAC1D,CAAC;oBACF,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;gBAC3B,CAAC;aACF,CAAC;QACJ,CAAC;QACD,KAAK,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO;YAC7B,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,CAAC;YAClC,IAAI,CAAC,GAAG;gBAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,SAAS,GAAG,CAAC,CAAC;YAC1E,OAAO,aAAa,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;QACtD,CAAC;QACD,KAAK,CAAC,MAAM,CAAC,SAAS;YACpB,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YACzB,MAAM,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAClC,CAAC;KACF,CAAC;AACJ,CAAC;AAED,MAAM,GAAG,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAChC,MAAM,SAAS,GAAG,EAAE,CAAC;AAErB,SAAS,WAAW,CAAC,OAAe;IAClC,MAAM,OAAO,GAAG,CAAC,OAAe,EAAE,EAAE;QAClC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,GAAG,OAAO,wBAAwB,CAAC,CAAC;QAC/E,OAAO,OAAO,CAAC;IACjB,CAAC,CAAC;IACF,OAAO;QACL,KAAK,CAAC,IAAI,CAAC,OAAO;YAChB,IAAI,CAAC;gBACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,CAAC,UAAU,EAAE;oBACvC,uBAAuB;oBACvB,IAAI;oBACJ,OAAO;oBACP,IAAI;oBACJ,OAAO,CAAC,OAAO,CAAC;oBAChB,IAAI;iBACL,CAAC,CAAC;gBACH,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC;YACvB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAK,KAA4B,CAAC,IAAI,KAAK,SAAS;oBAAE,OAAO,SAAS,CAAC;gBACvE,MAAM,KAAK,CAAC;YACd,CAAC;QACH,CAAC;QACD,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM;YACzB,oFAAoF;YACpF,MAAM,OAAO,GAAG,8BAA8B,OAAO,OAAO,OAAO,CAAC,OAAO,CAAC,OAAO,MAAM,IAAI,CAAC;YAC9F,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBAC1C,MAAM,KAAK,GAAG,KAAK,CAAC,UAAU,EAAE,CAAC,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;gBAC/E,IAAI,MAAM,GAAG,EAAE,CAAC;gBAChB,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,CAAC,MAAM,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;gBACzE,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;gBAC5B,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAC1B,IAAI,KAAK,CAAC,IAAI,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE;oBAChC,CAAC,CAAC,OAAO,EAAE;oBACX,CAAC,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,iCAAiC,MAAM,CAAC,IAAI,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC,CAChF,CAAC;gBACF,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAC3B,CAAC,CAAC,CAAC;YACH,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC;gBAC1C,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;YACxD,CAAC;QACH,CAAC;QACD,KAAK,CAAC,MAAM,CAAC,OAAO;YAClB,IAAI,CAAC;gBACH,MAAM,GAAG,CAAC,UAAU,EAAE;oBACpB,yBAAyB;oBACzB,IAAI;oBACJ,OAAO;oBACP,IAAI;oBACJ,OAAO,CAAC,OAAO,CAAC;iBACjB,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAK,KAA4B,CAAC,IAAI,KAAK,SAAS;oBAAE,MAAM,KAAK,CAAC;YACpE,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC;AAED,kGAAkG;AAClG,MAAM,UAAU,gBAAgB,CAAC,OAAe;IAC9C,OAAO,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC;AACtC,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,WAAW,CAAC,SAAiB;IAC3C,OAAO;QACL,MAAM,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;QAChF,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC,SAAS;QAC7B,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC,SAAS;KAC9B,CAAC;AACJ,CAAC","sourcesContent":["import { execFile, spawn } from \"node:child_process\";\nimport { createHash } from \"node:crypto\";\nimport { promisify } from \"node:util\";\n\nimport type { AccountSummary } from \"@parlor/auth\";\nimport type { GrantId, HostId } from \"@parlor/core\";\n\nimport { hostAssertion } from \"./assertion.js\";\nimport { jsonFile } from \"./json-file.js\";\n\n/** One app this computer is connected to. Its key lives in `HostKeys`, never here. */\nexport interface AppGrant {\n readonly appOrigin: string;\n readonly appName: string;\n readonly grantId: GrantId;\n readonly hostId: HostId;\n readonly account: AccountSummary;\n /** The agents' working directory for this app; only the user changes it, here. */\n readonly workspace: string;\n readonly createdAt: number;\n /** A `parlor dev` grant: in memory only. */\n readonly dev?: true | undefined;\n /** Refused here but not yet revoked by the app: kept only to retry the revoke, never served. */\n readonly declined?: true | undefined;\n /**\n * Handed only to the browser that approved `connect`, through the loopback redirect; that\n * browser proves it on its first prompt and is trusted without asking, then this is dropped.\n */\n readonly pairingSecret?: string | undefined;\n}\n\n/** One key per app grant in the OS keychain; the server holds only its public half. */\nexport interface HostKeys {\n /** A pairing's new key pair, kept only once the pairing completes. */\n create(appOrigin: string): Promise<HostKeyPair>;\n /** A fresh `ParlorHost` assertion for one request. */\n assert(\n appOrigin: string,\n request: { readonly method: \"GET\" | \"POST\"; readonly url: string },\n ): Promise<string>;\n remove(appOrigin: string): Promise<void>;\n}\n\nexport interface HostKeyPair {\n readonly publicKey: JsonWebKey;\n /** RFC 7638, the assertion's `kid`. */\n readonly thumbprint: string;\n sign(payload: Uint8Array): Promise<string>;\n /** Binds the key to the grant the pairing produced and stores it. */\n keep(grantId: GrantId): Promise<void>;\n}\n\nexport interface GrantStore {\n list(): Promise<readonly AppGrant[]>;\n get(appOrigin: string): Promise<AppGrant | undefined>;\n save(grant: AppGrant): Promise<void>;\n remove(appOrigin: string): Promise<void>;\n}\n\n/** `~/.parlor/host/grants.json`, 0600, written atomically. */\nexport function fileGrantStore(file: string): GrantStore {\n const grants = jsonFile<Record<string, AppGrant>>(file);\n return {\n list: async () => Object.values(await grants.read()),\n get: async (appOrigin) => (await grants.read())[appOrigin],\n save: (grant) =>\n grants.change((all) => {\n all[grant.appOrigin] = grant;\n }),\n remove: (appOrigin) =>\n grants.change((all) => {\n delete all[appOrigin];\n }),\n };\n}\n\nconst EC_P256 = { name: \"ECDSA\", namedCurve: \"P-256\" } as const;\nconst ES256 = { name: \"ECDSA\", hash: \"SHA-256\" } as const;\nconst ORIGIN = /^https?:\\/\\/[A-Za-z0-9.\\-[\\]:]+$/;\n\ninterface StoredKey {\n readonly jwk: JsonWebKey;\n readonly kid: string;\n readonly grantId: string;\n}\n\ninterface LoadedKey {\n readonly key: CryptoKey;\n readonly kid: string;\n readonly grantId: string;\n}\n\ninterface SecretStore {\n read(account: string): Promise<string | undefined>;\n write(account: string, secret: string): Promise<void>;\n remove(account: string): Promise<void>;\n}\n\nfunction thumbprintOf({ crv, kty, x, y }: JsonWebKey): string {\n return createHash(\"sha256\")\n .update(JSON.stringify({ crv, kty, x, y }))\n .digest(\"base64url\");\n}\n\nconst signer = (key: CryptoKey) => async (payload: Uint8Array) =>\n Buffer.from(await crypto.subtle.sign(ES256, key, new Uint8Array(payload))).toString(\n \"base64url\",\n );\n\nfunction keysIn(secrets: SecretStore): HostKeys {\n const loaded = new Map<string, Promise<LoadedKey | undefined>>();\n const load = (appOrigin: string) => {\n let entry = loaded.get(appOrigin);\n if (!entry) {\n entry = (async () => {\n const secret = await secrets.read(appOrigin);\n if (secret === undefined) return undefined;\n const stored = JSON.parse(\n Buffer.from(secret, \"base64url\").toString(\"utf8\"),\n ) as StoredKey;\n const key = await crypto.subtle.importKey(\"jwk\", stored.jwk, EC_P256, false, [\n \"sign\",\n ]);\n return { key, kid: stored.kid, grantId: stored.grantId };\n })();\n entry.catch(() => loaded.delete(appOrigin));\n loaded.set(appOrigin, entry);\n }\n return entry;\n };\n return {\n async create(appOrigin) {\n const pair = await crypto.subtle.generateKey(EC_P256, true, [\"sign\", \"verify\"]);\n const exported = await crypto.subtle.exportKey(\"jwk\", pair.publicKey);\n const publicKey: JsonWebKey = {\n kty: exported.kty!,\n crv: exported.crv!,\n x: exported.x!,\n y: exported.y!,\n };\n const thumbprint = thumbprintOf(publicKey);\n return {\n publicKey,\n thumbprint,\n sign: signer(pair.privateKey),\n keep: async (grantId) => {\n const jwk = await crypto.subtle.exportKey(\"jwk\", pair.privateKey);\n const stored: StoredKey = { jwk, kid: thumbprint, grantId };\n await secrets.write(\n appOrigin,\n Buffer.from(JSON.stringify(stored)).toString(\"base64url\"),\n );\n loaded.delete(appOrigin);\n },\n };\n },\n async assert(appOrigin, request) {\n const key = await load(appOrigin);\n if (!key) throw new Error(`This computer holds no key for ${appOrigin}.`);\n return hostAssertion(signer(key.key), key, request);\n },\n async remove(appOrigin) {\n loaded.delete(appOrigin);\n await secrets.remove(appOrigin);\n },\n };\n}\n\nconst run = promisify(execFile);\nconst NOT_FOUND = 44;\n\nfunction macKeychain(service: string): SecretStore {\n const checked = (account: string) => {\n if (!ORIGIN.test(account)) throw new Error(`${account} is not an app origin.`);\n return account;\n };\n return {\n async read(account) {\n try {\n const { stdout } = await run(\"security\", [\n \"find-generic-password\",\n \"-s\",\n service,\n \"-a\",\n checked(account),\n \"-w\",\n ]);\n return stdout.trim();\n } catch (error) {\n if ((error as { code?: unknown }).code === NOT_FOUND) return undefined;\n throw error;\n }\n },\n async write(account, secret) {\n // Through `security -i` on stdin, so the private key never shows in a process list.\n const command = `add-generic-password -U -s ${service} -a ${checked(account)} -w ${secret}\\n`;\n await new Promise<void>((resolve, reject) => {\n const child = spawn(\"security\", [\"-i\"], { stdio: [\"pipe\", \"ignore\", \"pipe\"] });\n let stderr = \"\";\n child.stderr.on(\"data\", (chunk: Buffer) => (stderr += chunk.toString()));\n child.once(\"error\", reject);\n child.once(\"exit\", (code) =>\n code === 0 && stderr.trim() === \"\"\n ? resolve()\n : reject(new Error(`The Keychain refused the key: ${stderr.trim() || code}`)),\n );\n child.stdin.end(command);\n });\n if ((await this.read(account)) !== secret) {\n throw new Error(\"The Keychain did not keep the key.\");\n }\n },\n async remove(account) {\n try {\n await run(\"security\", [\n \"delete-generic-password\",\n \"-s\",\n service,\n \"-a\",\n checked(account),\n ]);\n } catch (error) {\n if ((error as { code?: unknown }).code !== NOT_FOUND) throw error;\n }\n },\n };\n}\n\n/** The macOS Keychain; other platforms are refused before pairing (`unsupportedHostPlatform`). */\nexport function keychainHostKeys(service: string): HostKeys {\n return keysIn(macKeychain(service));\n}\n\n/** For `parlor-host dev`: one dev host token for every request, nothing on disk. */\nexport function devHostKeys(hostToken: string): HostKeys {\n return {\n create: () => Promise.reject(new Error(\"A parlor dev server needs no pairing.\")),\n assert: async () => hostToken,\n remove: async () => undefined,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"grants.js","sourceRoot":"","sources":["../src/grants.ts"],"names":[],"mappings":"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","sourcesContent":["import { execFile, spawn } from \"node:child_process\";\nimport { createHash } from \"node:crypto\";\nimport { promisify } from \"node:util\";\n\nimport type { AccountSummary } from \"@parlor/auth\";\nimport type { GrantId, HostId } from \"@parlor/core\";\n\nimport { hostAssertion } from \"./assertion.js\";\nimport { jsonFile } from \"./json-file.js\";\n\n/** One app this computer is connected to. Its key lives in `HostKeys`, never here. */\nexport interface AppGrant {\n readonly appOrigin: string;\n readonly appName: string;\n readonly grantId: GrantId;\n readonly hostId: HostId;\n readonly account: AccountSummary;\n /** The agents' working directory for this app; only the user changes it, here. */\n readonly workspace: string;\n readonly createdAt: number;\n /** A `parlor dev` grant: in memory only. */\n readonly dev?: true | undefined;\n /** Refused here but not yet revoked by the app: kept only to retry the revoke, never served. */\n readonly declined?: true | undefined;\n /**\n * Handed only to the browser that approved `connect`, through the loopback redirect; that\n * browser proves it on its first prompt and is trusted without asking, then this is dropped.\n */\n readonly pairingSecret?: string | undefined;\n /**\n * Whether the coding agents on this computer may use the app outside its sidebar, as the\n * user chose when connecting, and the name and agents it is registered under once it is.\n */\n readonly agentTools?:\n | {\n readonly on: boolean;\n readonly name?: string | undefined;\n readonly agents?: readonly string[] | undefined;\n }\n | undefined;\n}\n\n/** One key per app grant in the OS keychain; the server holds only its public half. */\nexport interface HostKeys {\n /** A pairing's new key pair, kept only once the pairing completes. */\n create(appOrigin: string): Promise<HostKeyPair>;\n /** A fresh `ParlorHost` assertion for one request. */\n assert(\n appOrigin: string,\n request: { readonly method: \"GET\" | \"POST\"; readonly url: string },\n ): Promise<string>;\n remove(appOrigin: string): Promise<void>;\n}\n\nexport interface HostKeyPair {\n readonly publicKey: JsonWebKey;\n /** RFC 7638, the assertion's `kid`. */\n readonly thumbprint: string;\n sign(payload: Uint8Array): Promise<string>;\n /** Binds the key to the grant the pairing produced and stores it. */\n keep(grantId: GrantId): Promise<void>;\n}\n\nexport interface GrantStore {\n list(): Promise<readonly AppGrant[]>;\n get(appOrigin: string): Promise<AppGrant | undefined>;\n save(grant: AppGrant): Promise<void>;\n remove(appOrigin: string): Promise<void>;\n}\n\n/** `~/.parlor/host/grants.json`, 0600, written atomically. */\nexport function fileGrantStore(file: string): GrantStore {\n const grants = jsonFile<Record<string, AppGrant>>(file);\n return {\n list: async () => Object.values(await grants.read()),\n get: async (appOrigin) => (await grants.read())[appOrigin],\n save: (grant) =>\n grants.change((all) => {\n all[grant.appOrigin] = grant;\n }),\n remove: (appOrigin) =>\n grants.change((all) => {\n delete all[appOrigin];\n }),\n };\n}\n\nconst EC_P256 = { name: \"ECDSA\", namedCurve: \"P-256\" } as const;\nconst ES256 = { name: \"ECDSA\", hash: \"SHA-256\" } as const;\nconst ORIGIN = /^https?:\\/\\/[A-Za-z0-9.\\-[\\]:]+$/;\n\ninterface StoredKey {\n readonly jwk: JsonWebKey;\n readonly kid: string;\n readonly grantId: string;\n}\n\ninterface LoadedKey {\n readonly key: CryptoKey;\n readonly kid: string;\n readonly grantId: string;\n}\n\ninterface SecretStore {\n read(account: string): Promise<string | undefined>;\n write(account: string, secret: string): Promise<void>;\n remove(account: string): Promise<void>;\n}\n\nfunction thumbprintOf({ crv, kty, x, y }: JsonWebKey): string {\n return createHash(\"sha256\")\n .update(JSON.stringify({ crv, kty, x, y }))\n .digest(\"base64url\");\n}\n\nconst signer = (key: CryptoKey) => async (payload: Uint8Array) =>\n Buffer.from(await crypto.subtle.sign(ES256, key, new Uint8Array(payload))).toString(\n \"base64url\",\n );\n\nfunction keysIn(secrets: SecretStore): HostKeys {\n const loaded = new Map<string, Promise<LoadedKey | undefined>>();\n const load = (appOrigin: string) => {\n let entry = loaded.get(appOrigin);\n if (!entry) {\n entry = (async () => {\n const secret = await secrets.read(appOrigin);\n if (secret === undefined) return undefined;\n const stored = JSON.parse(\n Buffer.from(secret, \"base64url\").toString(\"utf8\"),\n ) as StoredKey;\n const key = await crypto.subtle.importKey(\"jwk\", stored.jwk, EC_P256, false, [\n \"sign\",\n ]);\n return { key, kid: stored.kid, grantId: stored.grantId };\n })();\n entry.catch(() => loaded.delete(appOrigin));\n loaded.set(appOrigin, entry);\n }\n return entry;\n };\n return {\n async create(appOrigin) {\n const pair = await crypto.subtle.generateKey(EC_P256, true, [\"sign\", \"verify\"]);\n const exported = await crypto.subtle.exportKey(\"jwk\", pair.publicKey);\n const publicKey: JsonWebKey = {\n kty: exported.kty!,\n crv: exported.crv!,\n x: exported.x!,\n y: exported.y!,\n };\n const thumbprint = thumbprintOf(publicKey);\n return {\n publicKey,\n thumbprint,\n sign: signer(pair.privateKey),\n keep: async (grantId) => {\n const jwk = await crypto.subtle.exportKey(\"jwk\", pair.privateKey);\n const stored: StoredKey = { jwk, kid: thumbprint, grantId };\n await secrets.write(\n appOrigin,\n Buffer.from(JSON.stringify(stored)).toString(\"base64url\"),\n );\n loaded.delete(appOrigin);\n },\n };\n },\n async assert(appOrigin, request) {\n const key = await load(appOrigin);\n if (!key) throw new Error(`This computer holds no key for ${appOrigin}.`);\n return hostAssertion(signer(key.key), key, request);\n },\n async remove(appOrigin) {\n loaded.delete(appOrigin);\n await secrets.remove(appOrigin);\n },\n };\n}\n\nconst run = promisify(execFile);\nconst NOT_FOUND = 44;\n\nfunction macKeychain(service: string): SecretStore {\n const checked = (account: string) => {\n if (!ORIGIN.test(account)) throw new Error(`${account} is not an app origin.`);\n return account;\n };\n return {\n async read(account) {\n try {\n const { stdout } = await run(\"security\", [\n \"find-generic-password\",\n \"-s\",\n service,\n \"-a\",\n checked(account),\n \"-w\",\n ]);\n return stdout.trim();\n } catch (error) {\n if ((error as { code?: unknown }).code === NOT_FOUND) return undefined;\n throw error;\n }\n },\n async write(account, secret) {\n // Through `security -i` on stdin, so the private key never shows in a process list.\n const command = `add-generic-password -U -s ${service} -a ${checked(account)} -w ${secret}\\n`;\n await new Promise<void>((resolve, reject) => {\n const child = spawn(\"security\", [\"-i\"], { stdio: [\"pipe\", \"ignore\", \"pipe\"] });\n let stderr = \"\";\n child.stderr.on(\"data\", (chunk: Buffer) => (stderr += chunk.toString()));\n child.once(\"error\", reject);\n child.once(\"exit\", (code) =>\n code === 0 && stderr.trim() === \"\"\n ? resolve()\n : reject(new Error(`The Keychain refused the key: ${stderr.trim() || code}`)),\n );\n child.stdin.end(command);\n });\n if ((await this.read(account)) !== secret) {\n throw new Error(\"The Keychain did not keep the key.\");\n }\n },\n async remove(account) {\n try {\n await run(\"security\", [\n \"delete-generic-password\",\n \"-s\",\n service,\n \"-a\",\n checked(account),\n ]);\n } catch (error) {\n if ((error as { code?: unknown }).code !== NOT_FOUND) throw error;\n }\n },\n };\n}\n\n/** The macOS Keychain; other platforms are refused before pairing (`unsupportedHostPlatform`). */\nexport function keychainHostKeys(service: string): HostKeys {\n return keysIn(macKeychain(service));\n}\n\n/** For `parlor-host dev`: one dev host token for every request, nothing on disk. */\nexport function devHostKeys(hostToken: string): HostKeys {\n return {\n create: () => Promise.reject(new Error(\"A parlor dev server needs no pairing.\")),\n assert: async () => hostToken,\n remove: async () => undefined,\n };\n}\n"]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { AgentInfo, AgentOption, AgentUsage } from "@parlor/chat";
|
|
2
|
+
export interface Learned {
|
|
3
|
+
readonly options?: readonly AgentOption[] | undefined;
|
|
4
|
+
readonly usage?: AgentUsage | undefined;
|
|
5
|
+
}
|
|
6
|
+
export type LearnedNews = {
|
|
7
|
+
readonly options: readonly AgentOption[];
|
|
8
|
+
} | {
|
|
9
|
+
readonly usage: AgentUsage;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* What each agent last said about itself, kept across restarts: the allowance only arrives
|
|
13
|
+
* on a turn, and a computer that forgot it would show nothing until the next one.
|
|
14
|
+
*/
|
|
15
|
+
export interface LearnedStore {
|
|
16
|
+
load(): Promise<void>;
|
|
17
|
+
/** Whether it changed what `describe` returns. */
|
|
18
|
+
learn(agentId: string, news: LearnedNews): Promise<boolean>;
|
|
19
|
+
/** `agents` with what each said about itself, minus usage windows that rolled over. */
|
|
20
|
+
describe(agents: readonly AgentInfo[], now: number): AgentInfo[];
|
|
21
|
+
}
|
|
22
|
+
export declare function learnedStore(file: string): LearnedStore;
|
|
23
|
+
//# sourceMappingURL=learned.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"learned.d.ts","sourceRoot":"","sources":["../src/learned.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAKvE,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,WAAW,EAAE,GAAG,SAAS,CAAC;IACtD,QAAQ,CAAC,KAAK,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;CACzC;AAED,MAAM,MAAM,WAAW,GACnB;IAAE,QAAQ,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,CAAA;CAAE,GAC5C;IAAE,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAA;CAAE,CAAC;AAEnC;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,kDAAkD;IAClD,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5D,uFAAuF;IACvF,QAAQ,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,SAAS,EAAE,CAAC;CAClE;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAmCvD"}
|
package/dist/learned.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { mergeUsage } from "./agent-options.js";
|
|
2
|
+
import { jsonFile } from "./json-file.js";
|
|
3
|
+
export function learnedStore(file) {
|
|
4
|
+
const stored = jsonFile(file);
|
|
5
|
+
let known = {};
|
|
6
|
+
return {
|
|
7
|
+
load: async () => {
|
|
8
|
+
known = await stored.read().catch(() => ({}));
|
|
9
|
+
},
|
|
10
|
+
learn: async (agentId, news) => {
|
|
11
|
+
const held = known[agentId] ?? {};
|
|
12
|
+
const next = "options" in news
|
|
13
|
+
? { ...held, options: news.options }
|
|
14
|
+
: { ...held, usage: mergeUsage(held.usage, news.usage) };
|
|
15
|
+
if (JSON.stringify(next) === JSON.stringify(held))
|
|
16
|
+
return false;
|
|
17
|
+
known = { ...known, [agentId]: next };
|
|
18
|
+
await stored.change((all) => {
|
|
19
|
+
all[agentId] = next;
|
|
20
|
+
});
|
|
21
|
+
return true;
|
|
22
|
+
},
|
|
23
|
+
describe: (agents, now) => agents.map((agent) => {
|
|
24
|
+
const { options, usage } = known[agent.id] ?? {};
|
|
25
|
+
const windows = usage?.windows.filter((window) => window.resetsAt === undefined || window.resetsAt > now);
|
|
26
|
+
return {
|
|
27
|
+
...agent,
|
|
28
|
+
...(options && options.length > 0 ? { options } : {}),
|
|
29
|
+
...(usage && windows && windows.length > 0
|
|
30
|
+
? { usage: { ...usage, windows } }
|
|
31
|
+
: {}),
|
|
32
|
+
};
|
|
33
|
+
}),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=learned.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"learned.js","sourceRoot":"","sources":["../src/learned.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAuB1C,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,MAAM,MAAM,GAAG,QAAQ,CAA0B,IAAI,CAAC,CAAC;IACvD,IAAI,KAAK,GAA4B,EAAE,CAAC;IACxC,OAAO;QACL,IAAI,EAAE,KAAK,IAAI,EAAE;YACf,KAAK,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAChD,CAAC;QACD,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;YAC7B,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;YAClC,MAAM,IAAI,GACR,SAAS,IAAI,IAAI;gBACf,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE;gBACpC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,UAAU,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YAC7D,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;gBAAE,OAAO,KAAK,CAAC;YAChE,KAAK,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC;YACtC,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE;gBAC1B,GAAG,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;YACtB,CAAC,CAAC,CAAC;YACH,OAAO,IAAI,CAAC;QACd,CAAC;QACD,QAAQ,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE,CACxB,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;YACnB,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC;YACjD,MAAM,OAAO,GAAG,KAAK,EAAE,OAAO,CAAC,MAAM,CACnC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,KAAK,SAAS,IAAI,MAAM,CAAC,QAAQ,GAAG,GAAG,CACnE,CAAC;YACF,OAAO;gBACL,GAAG,KAAK;gBACR,GAAG,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACrD,GAAG,CAAC,KAAK,IAAI,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;oBACxC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,EAAE;oBAClC,CAAC,CAAC,EAAE,CAAC;aACR,CAAC;QACJ,CAAC,CAAC;KACL,CAAC;AACJ,CAAC","sourcesContent":["import type { AgentInfo, AgentOption, AgentUsage } from \"@parlor/chat\";\n\nimport { mergeUsage } from \"./agent-options.js\";\nimport { jsonFile } from \"./json-file.js\";\n\nexport interface Learned {\n readonly options?: readonly AgentOption[] | undefined;\n readonly usage?: AgentUsage | undefined;\n}\n\nexport type LearnedNews =\n | { readonly options: readonly AgentOption[] }\n | { readonly usage: AgentUsage };\n\n/**\n * What each agent last said about itself, kept across restarts: the allowance only arrives\n * on a turn, and a computer that forgot it would show nothing until the next one.\n */\nexport interface LearnedStore {\n load(): Promise<void>;\n /** Whether it changed what `describe` returns. */\n learn(agentId: string, news: LearnedNews): Promise<boolean>;\n /** `agents` with what each said about itself, minus usage windows that rolled over. */\n describe(agents: readonly AgentInfo[], now: number): AgentInfo[];\n}\n\nexport function learnedStore(file: string): LearnedStore {\n const stored = jsonFile<Record<string, Learned>>(file);\n let known: Record<string, Learned> = {};\n return {\n load: async () => {\n known = await stored.read().catch(() => ({}));\n },\n learn: async (agentId, news) => {\n const held = known[agentId] ?? {};\n const next: Learned =\n \"options\" in news\n ? { ...held, options: news.options }\n : { ...held, usage: mergeUsage(held.usage, news.usage) };\n if (JSON.stringify(next) === JSON.stringify(held)) return false;\n known = { ...known, [agentId]: next };\n await stored.change((all) => {\n all[agentId] = next;\n });\n return true;\n },\n describe: (agents, now) =>\n agents.map((agent) => {\n const { options, usage } = known[agent.id] ?? {};\n const windows = usage?.windows.filter(\n (window) => window.resetsAt === undefined || window.resetsAt > now,\n );\n return {\n ...agent,\n ...(options && options.length > 0 ? { options } : {}),\n ...(usage && windows && windows.length > 0\n ? { usage: { ...usage, windows } }\n : {}),\n };\n }),\n };\n}\n"]}
|
package/dist/main.js
CHANGED
|
@@ -30,7 +30,7 @@ function fail(message) {
|
|
|
30
30
|
process.exitCode = 1;
|
|
31
31
|
}
|
|
32
32
|
async function host() {
|
|
33
|
-
const [{ acpLauncher }, { HOST_PROTOCOL }, { CONTROL_PROTOCOL, connectControl }, { createHostDaemon, HostAlreadyRunning }, { fileGrantStore, keychainHostKeys }, { LOCK_TOKEN_ENV }, { createMcpProxy }, { hostPaths }, { changePolicy, DEFAULT_POLICY, loadPolicy }, { createSessionPool }, { macDialogPrompter, terminalPrompter }, { revokeGrant }, { fileTrustStore }, { ensureService },] = await Promise.all([
|
|
33
|
+
const [{ acpLauncher }, { HOST_PROTOCOL }, { CONTROL_PROTOCOL, connectControl }, { createHostDaemon, HostAlreadyRunning }, { fileGrantStore, keychainHostKeys }, { LOCK_TOKEN_ENV }, { createMcpProxy }, { hostPaths }, { changePolicy, DEFAULT_POLICY, loadPolicy }, { createSessionPool }, { macDialogPrompter, terminalPrompter }, { revokeGrant }, { fileTrustStore }, { ensureService }, { unregisterAgentTools, agentNames }, { detectAgents, loadProfiles }, { agentEnvironment },] = await Promise.all([
|
|
34
34
|
import("./acp.js"),
|
|
35
35
|
import("@parlor/chat"),
|
|
36
36
|
import("./control.js"),
|
|
@@ -45,6 +45,9 @@ async function host() {
|
|
|
45
45
|
import("./revoke.js"),
|
|
46
46
|
import("./trust.js"),
|
|
47
47
|
import("./service.js"),
|
|
48
|
+
import("./agent-tools.js"),
|
|
49
|
+
import("./agents.js"),
|
|
50
|
+
import("./shell-path.js"),
|
|
48
51
|
]);
|
|
49
52
|
const release = {
|
|
50
53
|
version,
|
|
@@ -72,12 +75,25 @@ async function host() {
|
|
|
72
75
|
client.close();
|
|
73
76
|
if (!reply.ok)
|
|
74
77
|
throw new Error(reply.error);
|
|
75
|
-
return;
|
|
78
|
+
return reply.agentTools;
|
|
76
79
|
}
|
|
77
80
|
const grants = fileGrantStore(paths.grants);
|
|
78
81
|
const grant = await grants.get(origin);
|
|
79
82
|
if (!grant)
|
|
80
83
|
throw new Error(`This computer is not connected to ${origin}.`);
|
|
84
|
+
const tools = grant.agentTools;
|
|
85
|
+
let removed;
|
|
86
|
+
if (tools?.name && tools.agents?.length) {
|
|
87
|
+
const profiles = await loadProfiles(paths.agents);
|
|
88
|
+
const installed = await detectAgents(profiles);
|
|
89
|
+
const listed = { name: tools.name, agents: tools.agents };
|
|
90
|
+
await unregisterAgentTools({
|
|
91
|
+
profiles: profiles.filter((profile) => installed.some((a) => a.id === profile.id)),
|
|
92
|
+
hostCommand: join(paths.root, "bin", "parlor-host"),
|
|
93
|
+
env: agentEnvironment,
|
|
94
|
+
}, listed);
|
|
95
|
+
removed = { name: tools.name, agents: agentNames(profiles, listed) };
|
|
96
|
+
}
|
|
81
97
|
const keys = keychainHostKeys(KEYCHAIN_SERVICE);
|
|
82
98
|
await revokeGrant({
|
|
83
99
|
appOrigin: origin,
|
|
@@ -87,6 +103,7 @@ async function host() {
|
|
|
87
103
|
await grants.remove(origin);
|
|
88
104
|
await keys.remove(origin);
|
|
89
105
|
await fileTrustStore(paths.trust).forget(origin);
|
|
106
|
+
return removed;
|
|
90
107
|
};
|
|
91
108
|
return {
|
|
92
109
|
changePolicy,
|
|
@@ -242,6 +259,7 @@ const connect = defineCommand({
|
|
|
242
259
|
platform: platformName(),
|
|
243
260
|
keys: kit.keychainHostKeys(KEYCHAIN_SERVICE),
|
|
244
261
|
workspaceRoot: paths.workspaces,
|
|
262
|
+
offerAgentTools: true,
|
|
245
263
|
channel: {
|
|
246
264
|
kind: "browser",
|
|
247
265
|
open: async (url) => {
|
|
@@ -291,20 +309,74 @@ const connect = defineCommand({
|
|
|
291
309
|
fail(reply.error);
|
|
292
310
|
return;
|
|
293
311
|
}
|
|
294
|
-
|
|
312
|
+
const app = printable(grant.appName);
|
|
313
|
+
process.stdout.write(`Connected to ${app} as ${printable(grant.account.email ?? grant.account.name)}.\n${usage(app, reply.agentTools)}Not you? Run \`npx @parlor/agent-host disconnect ${origin}\`.\n`);
|
|
295
314
|
if (alreadyConnected) {
|
|
296
315
|
process.stdout.write("This computer was already connected. To use your agents from another browser or device, open the Agent sidebar there and link it.\n");
|
|
316
|
+
if (grant.agentTools === undefined) {
|
|
317
|
+
process.stdout.write(`To use ${app} from Claude Code and Codex here too, run \`npx @parlor/agent-host tools ${origin}\`.\n`);
|
|
318
|
+
}
|
|
297
319
|
}
|
|
298
320
|
},
|
|
299
321
|
});
|
|
322
|
+
/** The two ways to use the app from this computer, for the lines after "Connected". */
|
|
323
|
+
function usage(app, tools) {
|
|
324
|
+
const sidebar = ` - In ${app}'s agent sidebar.\n`;
|
|
325
|
+
if (!tools)
|
|
326
|
+
return `Use your agents in ${app}'s agent sidebar.\n`;
|
|
327
|
+
return `Use it two ways:\n${sidebar} - In ${tools.agents} on this computer: ask them to "use ${tools.name} to ...". They ask you before their first change in each conversation.\n`;
|
|
328
|
+
}
|
|
329
|
+
const tools = defineCommand({
|
|
330
|
+
meta: {
|
|
331
|
+
name: "tools",
|
|
332
|
+
description: "Let the coding agents on this computer use an app outside its sidebar, or stop them",
|
|
333
|
+
},
|
|
334
|
+
args: {
|
|
335
|
+
url: { type: "positional", required: true, description: "The app's URL" },
|
|
336
|
+
off: { type: "boolean", description: "Stop them; the sidebar keeps working" },
|
|
337
|
+
},
|
|
338
|
+
async run({ args }) {
|
|
339
|
+
const kit = await host();
|
|
340
|
+
const origin = new URL(args.url).origin;
|
|
341
|
+
const client = await kit.connectControl(kit.hostPaths().control);
|
|
342
|
+
if (!client) {
|
|
343
|
+
fail(`Parlor Host is not running. Run \`npx @parlor/agent-host connect ${origin}\` first.`);
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
const reply = await client.send({
|
|
347
|
+
op: "agent_tools",
|
|
348
|
+
appOrigin: origin,
|
|
349
|
+
on: !args.off,
|
|
350
|
+
});
|
|
351
|
+
client.close();
|
|
352
|
+
if (!reply.ok) {
|
|
353
|
+
fail(reply.error);
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
process.stdout.write(args.off
|
|
357
|
+
? `The coding agents on this computer no longer use ${origin}. Its sidebar still works.\n`
|
|
358
|
+
: reply.agentTools
|
|
359
|
+
? `${reply.agentTools.agents} can use ${origin}: ask them to "use ${reply.agentTools.name} to ...".\n`
|
|
360
|
+
: "No coding agent here can take it: install Claude Code or Codex, then run this again.\n");
|
|
361
|
+
},
|
|
362
|
+
});
|
|
300
363
|
const disconnect = defineCommand({
|
|
301
364
|
meta: { name: "disconnect", description: "Disconnect this computer from a Parlor app" },
|
|
302
365
|
args: { url: { type: "positional", required: true, description: "The app's URL" } },
|
|
303
366
|
async run({ args }) {
|
|
304
367
|
const kit = await host();
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
368
|
+
const origin = new URL(args.url).origin;
|
|
369
|
+
let removed;
|
|
370
|
+
try {
|
|
371
|
+
removed = await kit.disconnectFrom(origin);
|
|
372
|
+
}
|
|
373
|
+
catch (error) {
|
|
374
|
+
fail(errorText(error));
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
process.stdout.write(`Disconnected this computer from ${origin}: its sidebar can no longer reach your agents here.\n${removed
|
|
378
|
+
? `Removed ${removed.name} from ${removed.agents}, so they cannot change it either.\n`
|
|
379
|
+
: ""}`);
|
|
308
380
|
},
|
|
309
381
|
});
|
|
310
382
|
const status = defineCommand({
|
|
@@ -400,6 +472,10 @@ const config = defineCommand({
|
|
|
400
472
|
type: "string",
|
|
401
473
|
description: "Hours a linked device sends before it confirms with Face ID or Touch ID again, or off",
|
|
402
474
|
},
|
|
475
|
+
"agent-mode": {
|
|
476
|
+
type: "string",
|
|
477
|
+
description: "auto: the agent's own safety review decides and asks only for what it flags; ask: it asks before every action",
|
|
478
|
+
},
|
|
403
479
|
},
|
|
404
480
|
async run({ args }) {
|
|
405
481
|
const kit = await host();
|
|
@@ -422,6 +498,14 @@ const config = defineCommand({
|
|
|
422
498
|
}
|
|
423
499
|
change = { ...change, promptPresence: hours === "off" ? "off" : { maxAgeHours } };
|
|
424
500
|
}
|
|
501
|
+
const agentMode = args["agent-mode"];
|
|
502
|
+
if (agentMode !== undefined) {
|
|
503
|
+
if (agentMode !== "auto" && agentMode !== "ask") {
|
|
504
|
+
fail("--agent-mode takes auto or ask.");
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
change = { ...change, agentMode };
|
|
508
|
+
}
|
|
425
509
|
const current = Object.keys(change).length === 0
|
|
426
510
|
? await kit.loadPolicy(policy, kit.DEFAULT_POLICY)
|
|
427
511
|
: await kit.changePolicy(policy, kit.DEFAULT_POLICY, change);
|
|
@@ -494,9 +578,20 @@ function memoryGrants() {
|
|
|
494
578
|
}
|
|
495
579
|
const mcpProxy = defineCommand({
|
|
496
580
|
meta: { name: "mcp-proxy", description: "Internal: an agent's stdio MCP server" },
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
581
|
+
args: {
|
|
582
|
+
app: {
|
|
583
|
+
type: "string",
|
|
584
|
+
description: "Serve this app to a coding agent outside the sidebar",
|
|
585
|
+
},
|
|
586
|
+
},
|
|
587
|
+
async run({ args }) {
|
|
588
|
+
const [{ runMcpProxyProcess }, { hostPaths }] = await Promise.all([
|
|
589
|
+
import("./mcp-proxy.js"),
|
|
590
|
+
import("./paths.js"),
|
|
591
|
+
]);
|
|
592
|
+
await runMcpProxyProcess(process.env, args.app
|
|
593
|
+
? { appOrigin: new URL(args.app).origin, socketPath: hostPaths().proxy }
|
|
594
|
+
: undefined);
|
|
500
595
|
},
|
|
501
596
|
});
|
|
502
597
|
void runMain(defineCommand({
|
|
@@ -515,6 +610,7 @@ void runMain(defineCommand({
|
|
|
515
610
|
config,
|
|
516
611
|
dev,
|
|
517
612
|
"mcp-proxy": mcpProxy,
|
|
613
|
+
tools,
|
|
518
614
|
},
|
|
519
615
|
}));
|
|
520
616
|
//# sourceMappingURL=main.js.map
|