@agimon-ai/po-core 0.0.0-alpha.76 → 0.0.0-alpha.77

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.
@@ -1 +1 @@
1
- {"version":3,"file":"apiContracts.d.mts","names":["_0","_0","_0","IoContext","_0","_$__hubChannel0.PoHubSessionUsage","_$__protocolContracts0.ChordMethods"],"sources":["../src/schemas/serverScope.ts","../src/schemas/approvalPrompt.ts","../src/schemas/approval.ts","../src/schemas/backgroundWork.ts","../src/schemas/platform.ts","../src/schemas/workflow.ts","../src/schemas/hubChannel.ts","../src/schemas/packageApi.ts","../src/schemas/serverBundle.ts","../src/schemas/apiContracts.ts","../src/services/apiContracts/index.ts","../src/schemas/builtinApiContracts.ts","../src/schemas/httpApiContracts.ts","../src/schemas/protocolContracts.ts","../src/schemas/sessionProtocol.ts","../src/schemas/sessionApiContracts.ts","../src/schemas/transportApiContracts.ts"],"mappings":"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"}
1
+ {"version":3,"file":"apiContracts.d.mts","names":["_0","_0","_0","IoContext","_0","_$__hubChannel0.PoHubSessionUsage","_$__protocolContracts0.ChordMethods"],"sources":["../src/schemas/serverScope.ts","../src/schemas/approvalPrompt.ts","../src/schemas/approval.ts","../src/schemas/backgroundWork.ts","../src/schemas/platform.ts","../src/schemas/workflow.ts","../src/schemas/hubChannel.ts","../src/schemas/packageApi.ts","../src/schemas/serverBundle.ts","../src/schemas/apiContracts.ts","../src/services/apiContracts/index.ts","../src/schemas/builtinApiContracts.ts","../src/schemas/httpApiContracts.ts","../src/schemas/protocolContracts.ts","../src/schemas/sessionProtocol.ts","../src/schemas/sessionApiContracts.ts","../src/schemas/transportApiContracts.ts"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"approval.cjs","names":["defineServiceContract"],"sources":["../../../src/schemas/approval.ts"],"sourcesContent":["import type { Context } from '@deepseek-ai/cordis';\n\nimport type { PoApprovalDescription, PoApprovalOperation } from './approvalPrompt';\nimport { defineServiceContract } from './serverScope';\n\nexport {\n PO_APPROVAL_TEXT_LIMITS,\n PO_APPROVAL_VOICE_EVENT,\n parsePoApprovalPrompt,\n PoApprovalPromptSchema,\n type PoApprovalDescription,\n type PoApprovalOperation,\n type PoApprovalPrompt,\n} from './approvalPrompt';\n\nexport const PO_APPROVAL_SERVICE = 'po/approval';\n\n/**\n * Declared on a direct tool, a portable tool, or a code-mode namespace member.\n * - `ask`: ask when a person can answer; run without asking for subagents, background scripts and scheduled runs.\n * - `human`: never run without a person's approval; refused where nobody can answer.\n */\nexport interface PoApprovalPolicy {\n readonly requirement: 'ask' | 'human';\n /** Receives the call's input (a tool's parameters, a namespace member's first argument). */\n describe(input: unknown): PoApprovalDescription | Promise<PoApprovalDescription>;\n}\n\nexport interface PoApprovalRequest {\n readonly sessionId: string;\n /** The direct tool call, or the `exec` call a namespace member runs inside. */\n readonly toolCallId: string;\n readonly operation: PoApprovalOperation;\n readonly requirement: 'ask' | 'human';\n readonly description: PoApprovalDescription;\n /** The call's saved arguments, replayed by the operation kind's executor once approved. Never sent to clients. */\n readonly input: unknown;\n /** Code mode stores the full positional argument list; absent on legacy single-input receipts. */\n readonly argumentFormat?: 'positional';\n}\n\nexport type PoApprovalDecision = 'approved' | 'denied' | 'cancelled';\n\nexport interface PoApprovedCall {\n readonly approvalId: string;\n readonly operation: PoApprovalOperation;\n /** The saved `PoApprovalRequest.input`. */\n readonly input: unknown;\n readonly argumentFormat?: 'positional';\n}\n\n/** Runs an approved call; the resolved value or thrown error is reported to the agent. */\nexport type PoApprovalExecutor = (call: PoApprovedCall, signal: AbortSignal) => Promise<unknown>;\n\n/**\n * Session-scoped. `submit` records a pending request durably and returns at once; the person decides later,\n * and an approved request runs through the executor registered for its operation kind.\n */\nexport interface PoApprovalService {\n submit(request: PoApprovalRequest): Promise<{ readonly id: string }>;\n registerExecutor(kind: PoApprovalOperation['kind'], run: PoApprovalExecutor): () => void;\n}\n\nexport const PO_APPROVAL_CONTRACT = defineServiceContract<PoApprovalService>(PO_APPROVAL_SERVICE, {\n scope: 'session',\n});\n\ndeclare module '@deepseek-ai/cordis' {\n interface Context {\n 'po/approval': PoApprovalService;\n }\n}\n\nexport function readPoApproval(context: Context): PoApprovalService | undefined {\n return context.get(PO_APPROVAL_SERVICE) as PoApprovalService | undefined;\n}\n"],"mappings":";;;AAeA,MAAa,sBAAsB;AAgDnC,MAAa,uBAAuBA,oBAAAA,sBAAyC,qBAAqB,EAChG,OAAO,UACT,CAAC;AAQD,SAAgB,eAAe,SAAiD;CAC9E,OAAO,QAAQ,IAAI,mBAAmB;AACxC"}
1
+ {"version":3,"file":"approval.cjs","names":["defineServiceContract"],"sources":["../../../src/schemas/approval.ts"],"sourcesContent":["import type { Context } from '@deepseek-ai/cordis';\n\nimport type { PoApprovalDescription, PoApprovalOperation } from './approvalPrompt';\nimport { defineServiceContract } from './serverScope';\n\nexport {\n PO_APPROVAL_TEXT_LIMITS,\n PO_APPROVAL_VOICE_EVENT,\n parsePoApprovalPrompt,\n PoApprovalPromptSchema,\n type PoApprovalDescription,\n type PoApprovalOperation,\n type PoApprovalPrompt,\n} from './approvalPrompt';\n\nexport const PO_APPROVAL_SERVICE = 'po/approval';\n\n/**\n * Declared on a direct tool, a portable tool, or a code-mode namespace member.\n * - `ask`: ask when interactive; run without asking for unattended sessions, subagents and background scripts.\n * - `human`: foreground main calls queue through the approval service regardless of session presence.\n * Never run without a person's approval; unavailable to child/background calls or without the service.\n */\nexport interface PoApprovalPolicy {\n readonly requirement: 'ask' | 'human';\n /** Receives the call's input (a tool's parameters, a namespace member's first argument). */\n describe(input: unknown): PoApprovalDescription | Promise<PoApprovalDescription>;\n}\n\nexport interface PoApprovalRequest {\n readonly sessionId: string;\n /** The direct tool call, or the `exec` call a namespace member runs inside. */\n readonly toolCallId: string;\n readonly operation: PoApprovalOperation;\n readonly requirement: 'ask' | 'human';\n readonly description: PoApprovalDescription;\n /** The call's saved arguments, replayed by the operation kind's executor once approved. Never sent to clients. */\n readonly input: unknown;\n /** Code mode stores the full positional argument list; absent on legacy single-input receipts. */\n readonly argumentFormat?: 'positional';\n}\n\nexport type PoApprovalDecision = 'approved' | 'denied' | 'cancelled';\n\nexport interface PoApprovedCall {\n readonly approvalId: string;\n readonly operation: PoApprovalOperation;\n /** The saved `PoApprovalRequest.input`. */\n readonly input: unknown;\n readonly argumentFormat?: 'positional';\n}\n\n/** Runs an approved call; the resolved value or thrown error is reported to the agent. */\nexport type PoApprovalExecutor = (call: PoApprovedCall, signal: AbortSignal) => Promise<unknown>;\n\n/**\n * Session-scoped. `submit` records a pending request durably and returns at once; the person decides later,\n * and an approved request runs through the executor registered for its operation kind.\n */\nexport interface PoApprovalService {\n submit(request: PoApprovalRequest): Promise<{ readonly id: string }>;\n registerExecutor(kind: PoApprovalOperation['kind'], run: PoApprovalExecutor): () => void;\n}\n\nexport const PO_APPROVAL_CONTRACT = defineServiceContract<PoApprovalService>(PO_APPROVAL_SERVICE, {\n scope: 'session',\n});\n\ndeclare module '@deepseek-ai/cordis' {\n interface Context {\n 'po/approval': PoApprovalService;\n }\n}\n\nexport function readPoApproval(context: Context): PoApprovalService | undefined {\n return context.get(PO_APPROVAL_SERVICE) as PoApprovalService | undefined;\n}\n"],"mappings":";;;AAeA,MAAa,sBAAsB;AAiDnC,MAAa,uBAAuBA,oBAAAA,sBAAyC,qBAAqB,EAChG,OAAO,UACT,CAAC;AAQD,SAAgB,eAAe,SAAiD;CAC9E,OAAO,QAAQ,IAAI,mBAAmB;AACxC"}
@@ -5,8 +5,9 @@ import { Context } from "@deepseek-ai/cordis";
5
5
  export declare const PO_APPROVAL_SERVICE = "po/approval";
6
6
  /**
7
7
  * Declared on a direct tool, a portable tool, or a code-mode namespace member.
8
- * - `ask`: ask when a person can answer; run without asking for subagents, background scripts and scheduled runs.
9
- * - `human`: never run without a person's approval; refused where nobody can answer.
8
+ * - `ask`: ask when interactive; run without asking for unattended sessions, subagents and background scripts.
9
+ * - `human`: foreground main calls queue through the approval service regardless of session presence.
10
+ * Never run without a person's approval; unavailable to child/background calls or without the service.
10
11
  */
11
12
  export interface PoApprovalPolicy {
12
13
  readonly requirement: 'ask' | 'human';
@@ -1 +1 @@
1
- {"version":3,"file":"approval.d.mts","names":["_$__serverScope0.PoServiceContract"],"sources":["../../../src/schemas/approval.ts"],"mappings":";;;;qBAea;;;;;;iBAOI;WACN;;EAET,SAAS,iBAAiB,wBAAwB,QAAQ;;iBAG3C;WACN;;WAEA;WACA,WAAW;WACX;WACA,aAAa;;WAEb;;WAEA;;YAGC;iBAEK;WACN;WACA,WAAW;;WAEX;WACA;;;YAIC,sBAAsB,MAAM,gBAAgB,QAAQ,gBAAgB;;;;;iBAM/D;EACf,OAAO,SAAS,oBAAoB;aAAmB;;EACvD,iBAAiB,MAAM,6BAA6B,KAAK;;qBAG9C,sBAAoBA,kBAAA;;YAKrB;IACR,eAAe;;;wBAIH,eAAe,SAAS,UAAU"}
1
+ {"version":3,"file":"approval.d.mts","names":["_$__serverScope0.PoServiceContract"],"sources":["../../../src/schemas/approval.ts"],"mappings":";;;;qBAea;;;;;;;iBAQI;WACN;;EAET,SAAS,iBAAiB,wBAAwB,QAAQ;;iBAG3C;WACN;;WAEA;WACA,WAAW;WACX;WACA,aAAa;;WAEb;;WAEA;;YAGC;iBAEK;WACN;WACA,WAAW;;WAEX;WACA;;;YAIC,sBAAsB,MAAM,gBAAgB,QAAQ,gBAAgB;;;;;iBAM/D;EACf,OAAO,SAAS,oBAAoB;aAAmB;;EACvD,iBAAiB,MAAM,6BAA6B,KAAK;;qBAG9C,sBAAoBA,kBAAA;;YAKrB;IACR,eAAe;;;wBAIH,eAAe,SAAS,UAAU"}
@@ -1 +1 @@
1
- {"version":3,"file":"approval.mjs","names":[],"sources":["../../../src/schemas/approval.ts"],"sourcesContent":["import type { Context } from '@deepseek-ai/cordis';\n\nimport type { PoApprovalDescription, PoApprovalOperation } from './approvalPrompt';\nimport { defineServiceContract } from './serverScope';\n\nexport {\n PO_APPROVAL_TEXT_LIMITS,\n PO_APPROVAL_VOICE_EVENT,\n parsePoApprovalPrompt,\n PoApprovalPromptSchema,\n type PoApprovalDescription,\n type PoApprovalOperation,\n type PoApprovalPrompt,\n} from './approvalPrompt';\n\nexport const PO_APPROVAL_SERVICE = 'po/approval';\n\n/**\n * Declared on a direct tool, a portable tool, or a code-mode namespace member.\n * - `ask`: ask when a person can answer; run without asking for subagents, background scripts and scheduled runs.\n * - `human`: never run without a person's approval; refused where nobody can answer.\n */\nexport interface PoApprovalPolicy {\n readonly requirement: 'ask' | 'human';\n /** Receives the call's input (a tool's parameters, a namespace member's first argument). */\n describe(input: unknown): PoApprovalDescription | Promise<PoApprovalDescription>;\n}\n\nexport interface PoApprovalRequest {\n readonly sessionId: string;\n /** The direct tool call, or the `exec` call a namespace member runs inside. */\n readonly toolCallId: string;\n readonly operation: PoApprovalOperation;\n readonly requirement: 'ask' | 'human';\n readonly description: PoApprovalDescription;\n /** The call's saved arguments, replayed by the operation kind's executor once approved. Never sent to clients. */\n readonly input: unknown;\n /** Code mode stores the full positional argument list; absent on legacy single-input receipts. */\n readonly argumentFormat?: 'positional';\n}\n\nexport type PoApprovalDecision = 'approved' | 'denied' | 'cancelled';\n\nexport interface PoApprovedCall {\n readonly approvalId: string;\n readonly operation: PoApprovalOperation;\n /** The saved `PoApprovalRequest.input`. */\n readonly input: unknown;\n readonly argumentFormat?: 'positional';\n}\n\n/** Runs an approved call; the resolved value or thrown error is reported to the agent. */\nexport type PoApprovalExecutor = (call: PoApprovedCall, signal: AbortSignal) => Promise<unknown>;\n\n/**\n * Session-scoped. `submit` records a pending request durably and returns at once; the person decides later,\n * and an approved request runs through the executor registered for its operation kind.\n */\nexport interface PoApprovalService {\n submit(request: PoApprovalRequest): Promise<{ readonly id: string }>;\n registerExecutor(kind: PoApprovalOperation['kind'], run: PoApprovalExecutor): () => void;\n}\n\nexport const PO_APPROVAL_CONTRACT = defineServiceContract<PoApprovalService>(PO_APPROVAL_SERVICE, {\n scope: 'session',\n});\n\ndeclare module '@deepseek-ai/cordis' {\n interface Context {\n 'po/approval': PoApprovalService;\n }\n}\n\nexport function readPoApproval(context: Context): PoApprovalService | undefined {\n return context.get(PO_APPROVAL_SERVICE) as PoApprovalService | undefined;\n}\n"],"mappings":";;;AAeA,MAAa,sBAAsB;AAgDnC,MAAa,uBAAuB,sBAAyC,qBAAqB,EAChG,OAAO,UACT,CAAC;AAQD,SAAgB,eAAe,SAAiD;CAC9E,OAAO,QAAQ,IAAI,mBAAmB;AACxC"}
1
+ {"version":3,"file":"approval.mjs","names":[],"sources":["../../../src/schemas/approval.ts"],"sourcesContent":["import type { Context } from '@deepseek-ai/cordis';\n\nimport type { PoApprovalDescription, PoApprovalOperation } from './approvalPrompt';\nimport { defineServiceContract } from './serverScope';\n\nexport {\n PO_APPROVAL_TEXT_LIMITS,\n PO_APPROVAL_VOICE_EVENT,\n parsePoApprovalPrompt,\n PoApprovalPromptSchema,\n type PoApprovalDescription,\n type PoApprovalOperation,\n type PoApprovalPrompt,\n} from './approvalPrompt';\n\nexport const PO_APPROVAL_SERVICE = 'po/approval';\n\n/**\n * Declared on a direct tool, a portable tool, or a code-mode namespace member.\n * - `ask`: ask when interactive; run without asking for unattended sessions, subagents and background scripts.\n * - `human`: foreground main calls queue through the approval service regardless of session presence.\n * Never run without a person's approval; unavailable to child/background calls or without the service.\n */\nexport interface PoApprovalPolicy {\n readonly requirement: 'ask' | 'human';\n /** Receives the call's input (a tool's parameters, a namespace member's first argument). */\n describe(input: unknown): PoApprovalDescription | Promise<PoApprovalDescription>;\n}\n\nexport interface PoApprovalRequest {\n readonly sessionId: string;\n /** The direct tool call, or the `exec` call a namespace member runs inside. */\n readonly toolCallId: string;\n readonly operation: PoApprovalOperation;\n readonly requirement: 'ask' | 'human';\n readonly description: PoApprovalDescription;\n /** The call's saved arguments, replayed by the operation kind's executor once approved. Never sent to clients. */\n readonly input: unknown;\n /** Code mode stores the full positional argument list; absent on legacy single-input receipts. */\n readonly argumentFormat?: 'positional';\n}\n\nexport type PoApprovalDecision = 'approved' | 'denied' | 'cancelled';\n\nexport interface PoApprovedCall {\n readonly approvalId: string;\n readonly operation: PoApprovalOperation;\n /** The saved `PoApprovalRequest.input`. */\n readonly input: unknown;\n readonly argumentFormat?: 'positional';\n}\n\n/** Runs an approved call; the resolved value or thrown error is reported to the agent. */\nexport type PoApprovalExecutor = (call: PoApprovedCall, signal: AbortSignal) => Promise<unknown>;\n\n/**\n * Session-scoped. `submit` records a pending request durably and returns at once; the person decides later,\n * and an approved request runs through the executor registered for its operation kind.\n */\nexport interface PoApprovalService {\n submit(request: PoApprovalRequest): Promise<{ readonly id: string }>;\n registerExecutor(kind: PoApprovalOperation['kind'], run: PoApprovalExecutor): () => void;\n}\n\nexport const PO_APPROVAL_CONTRACT = defineServiceContract<PoApprovalService>(PO_APPROVAL_SERVICE, {\n scope: 'session',\n});\n\ndeclare module '@deepseek-ai/cordis' {\n interface Context {\n 'po/approval': PoApprovalService;\n }\n}\n\nexport function readPoApproval(context: Context): PoApprovalService | undefined {\n return context.get(PO_APPROVAL_SERVICE) as PoApprovalService | undefined;\n}\n"],"mappings":";;;AAeA,MAAa,sBAAsB;AAiDnC,MAAa,uBAAuB,sBAAyC,qBAAqB,EAChG,OAAO,UACT,CAAC;AAQD,SAAgB,eAAe,SAAiD;CAC9E,OAAO,QAAQ,IAAI,mBAAmB;AACxC"}
@@ -33,9 +33,9 @@ function boundApprovalDescription(description, fallback) {
33
33
  function isUnattended(input) {
34
34
  return input.caller === "child" || input.background === true || input.interaction === "unattended";
35
35
  }
36
- /** True when an operation with this policy may not even be offered here. */
36
+ /** Foreground sessions may queue approval regardless of presence; child and background calls may not. */
37
37
  function isApprovalUnavailable(policy, input) {
38
- return policy?.requirement === "human" && isUnattended(input);
38
+ return policy?.requirement === "human" && (input.caller === "child" || input.background === true);
39
39
  }
40
40
  function label(operation) {
41
41
  return `'${operation.name}'`;
@@ -54,9 +54,10 @@ async function gateApproval(input) {
54
54
  };
55
55
  const stopped = () => input.signal?.aborted === true;
56
56
  if (stopped()) return cancelled;
57
- if (isUnattended(input)) return policy.requirement === "ask" ? { ok: true } : {
57
+ if (policy.requirement === "ask" && isUnattended(input)) return { ok: true };
58
+ if (isApprovalUnavailable(policy, input)) return {
58
59
  ok: false,
59
- reason: `${label(operation)} needs a person's approval, and nobody can answer here (subagent, background script, workflow or scheduled run). Nothing ran. Ask the person to do it from their own session.`
60
+ reason: `${label(operation)} needs a person's approval and cannot request it from a subagent or background script. Nothing ran. Ask the main agent to request approval in its own session.`
60
61
  };
61
62
  const service = input.service;
62
63
  if (service === void 0) return policy.requirement === "ask" ? { ok: true } : {
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["PO_APPROVAL_TEXT_LIMITS"],"sources":["../../../../src/services/approvalGate/index.ts"],"sourcesContent":["import {\n PO_APPROVAL_TEXT_LIMITS,\n type PoApprovalDescription,\n type PoApprovalOperation,\n type PoApprovalPolicy,\n type PoApprovalService,\n} from '../../schemas/approval';\nimport type { ApprovalGateInput, ApprovalGateResult } from './type';\n\nexport type { ApprovalGateInput, ApprovalGateResult } from './type';\n\n/** Saved input above this size is refused rather than stored. */\nexport const APPROVAL_INPUT_LIMIT = 256 * 1024;\n\n/** Thrown by a gated code-mode member while it waits for approval. Match on `name`: `instanceof` does not cross script sandboxes. */\nexport class ApprovalPendingError extends Error {\n override readonly name = 'ApprovalPendingError';\n constructor(\n readonly approvalId: string,\n message: string,\n ) {\n super(message);\n }\n}\n\nconst {\n title: TITLE_LIMIT,\n summary: SUMMARY_LIMIT,\n details: DETAIL_LIMIT,\n label: LABEL_LIMIT,\n value: VALUE_LIMIT,\n} = PO_APPROVAL_TEXT_LIMITS;\n\nfunction clip(value: unknown, limit: number): string {\n const text = typeof value === 'string' ? value.replace(/\\s+/gu, ' ').trim() : '';\n return text.length > limit ? `${text.slice(0, limit - 1)}…` : text;\n}\n\n/** Bounded plain text, whatever the package's `describe` returned. */\nexport function boundApprovalDescription(description: PoApprovalDescription, fallback: string): PoApprovalDescription {\n const details = Array.isArray(description?.details)\n ? description.details\n .slice(0, DETAIL_LIMIT)\n .map((detail) => ({ label: clip(detail?.label, LABEL_LIMIT), value: clip(detail?.value, VALUE_LIMIT) }))\n .filter((detail) => detail.label !== '' && detail.value !== '')\n : [];\n return {\n title: clip(description?.title, TITLE_LIMIT) || clip(fallback, TITLE_LIMIT),\n summary: clip(description?.summary, SUMMARY_LIMIT),\n ...(details.length === 0 ? {} : { details }),\n };\n}\n\n/** A child or background run cannot elevate the session's host-set presence. */\nexport function isUnattended(input: Pick<ApprovalGateInput, 'caller' | 'background' | 'interaction'>): boolean {\n return input.caller === 'child' || input.background === true || input.interaction === 'unattended';\n}\n\n/** True when an operation with this policy may not even be offered here. */\nexport function isApprovalUnavailable(\n policy: PoApprovalPolicy | undefined,\n input: Pick<ApprovalGateInput, 'caller' | 'background' | 'interaction'>,\n): boolean {\n return policy?.requirement === 'human' && isUnattended(input);\n}\n\nfunction label(operation: PoApprovalOperation): string {\n return `'${operation.name}'`;\n}\n\n/**\n * The one admission rule for direct tools and code-mode members. Resolves `{ ok: true }` only when the operation\n * may run now. A person-gated call is recorded and resolves `pending` at once; it runs later, once approved.\n * A refusal carries the message the agent sees. Never throws for a refusal.\n */\nexport async function gateApproval(input: ApprovalGateInput): Promise<ApprovalGateResult> {\n const { policy, operation } = input;\n if (policy === undefined) return { ok: true };\n const cancelled: ApprovalGateResult = { ok: false, reason: `${label(operation)} was cancelled. Nothing ran.` };\n const stopped = (): boolean => input.signal?.aborted === true;\n if (stopped()) return cancelled;\n if (isUnattended(input)) {\n return policy.requirement === 'ask'\n ? { ok: true }\n : {\n ok: false,\n reason: `${label(operation)} needs a person's approval, and nobody can answer here (subagent, background script, workflow or scheduled run). Nothing ran. Ask the person to do it from their own session.`,\n };\n }\n const service: PoApprovalService | undefined = input.service;\n if (service === undefined) {\n return policy.requirement === 'ask'\n ? { ok: true }\n : {\n ok: false,\n reason: `${label(operation)} needs a person's approval, but approvals are not available in this session. Nothing ran.`,\n };\n }\n let saved: unknown;\n try {\n const json = JSON.stringify(input.arguments ?? null);\n if (new TextEncoder().encode(json).byteLength > APPROVAL_INPUT_LIMIT)\n throw new Error(`its input is larger than ${APPROVAL_INPUT_LIMIT} bytes`);\n saved = JSON.parse(json);\n if (input.argumentFormat === 'positional' && (operation.kind !== 'codemode' || !Array.isArray(saved)))\n throw new Error('positional arguments must be a code-mode argument list');\n } catch (error) {\n return {\n ok: false,\n reason: `${label(operation)} cannot wait for approval: ${error instanceof Error ? error.message : String(error)}. Nothing ran.`,\n };\n }\n let description: PoApprovalDescription;\n try {\n description = boundApprovalDescription(await policy.describe(input.input), operation.name);\n } catch (error) {\n return {\n ok: false,\n reason: `${label(operation)} could not be described for approval: ${error instanceof Error ? error.message : String(error)}. Nothing ran.`,\n };\n }\n if (stopped()) return cancelled;\n try {\n const { id } = await service.submit({\n sessionId: input.sessionId,\n toolCallId: input.toolCallId,\n operation,\n requirement: policy.requirement,\n description,\n input: saved,\n ...(input.argumentFormat === undefined ? {} : { argumentFormat: input.argumentFormat }),\n });\n return {\n ok: false,\n pending: true,\n approvalId: id,\n reason: `${label(operation)} is waiting for the person's approval (id ${id}). Nothing ran yet. Keep working; the result arrives as a message once they decide. Do not call it again.`,\n };\n } catch (error) {\n return {\n ok: false,\n reason: `${label(operation)} could not request approval: ${error instanceof Error ? error.message : String(error)}. Nothing ran.`,\n };\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["PO_APPROVAL_TEXT_LIMITS"],"sources":["../../../../src/services/approvalGate/index.ts"],"sourcesContent":["import {\n PO_APPROVAL_TEXT_LIMITS,\n type PoApprovalDescription,\n type PoApprovalOperation,\n type PoApprovalPolicy,\n type PoApprovalService,\n} from '../../schemas/approval';\nimport type { ApprovalGateInput, ApprovalGateResult } from './type';\n\nexport type { ApprovalGateInput, ApprovalGateResult } from './type';\n\n/** Saved input above this size is refused rather than stored. */\nexport const APPROVAL_INPUT_LIMIT = 256 * 1024;\n\n/** Thrown by a gated code-mode member while it waits for approval. Match on `name`: `instanceof` does not cross script sandboxes. */\nexport class ApprovalPendingError extends Error {\n override readonly name = 'ApprovalPendingError';\n constructor(\n readonly approvalId: string,\n message: string,\n ) {\n super(message);\n }\n}\n\nconst {\n title: TITLE_LIMIT,\n summary: SUMMARY_LIMIT,\n details: DETAIL_LIMIT,\n label: LABEL_LIMIT,\n value: VALUE_LIMIT,\n} = PO_APPROVAL_TEXT_LIMITS;\n\nfunction clip(value: unknown, limit: number): string {\n const text = typeof value === 'string' ? value.replace(/\\s+/gu, ' ').trim() : '';\n return text.length > limit ? `${text.slice(0, limit - 1)}…` : text;\n}\n\n/** Bounded plain text, whatever the package's `describe` returned. */\nexport function boundApprovalDescription(description: PoApprovalDescription, fallback: string): PoApprovalDescription {\n const details = Array.isArray(description?.details)\n ? description.details\n .slice(0, DETAIL_LIMIT)\n .map((detail) => ({ label: clip(detail?.label, LABEL_LIMIT), value: clip(detail?.value, VALUE_LIMIT) }))\n .filter((detail) => detail.label !== '' && detail.value !== '')\n : [];\n return {\n title: clip(description?.title, TITLE_LIMIT) || clip(fallback, TITLE_LIMIT),\n summary: clip(description?.summary, SUMMARY_LIMIT),\n ...(details.length === 0 ? {} : { details }),\n };\n}\n\n/** A child or background run cannot elevate the session's host-set presence. */\nexport function isUnattended(input: Pick<ApprovalGateInput, 'caller' | 'background' | 'interaction'>): boolean {\n return input.caller === 'child' || input.background === true || input.interaction === 'unattended';\n}\n\n/** Foreground sessions may queue approval regardless of presence; child and background calls may not. */\nexport function isApprovalUnavailable(\n policy: PoApprovalPolicy | undefined,\n input: Pick<ApprovalGateInput, 'caller' | 'background' | 'interaction'>,\n): boolean {\n return policy?.requirement === 'human' && (input.caller === 'child' || input.background === true);\n}\n\nfunction label(operation: PoApprovalOperation): string {\n return `'${operation.name}'`;\n}\n\n/**\n * The one admission rule for direct tools and code-mode members. Resolves `{ ok: true }` only when the operation\n * may run now. A person-gated call is recorded and resolves `pending` at once; it runs later, once approved.\n * A refusal carries the message the agent sees. Never throws for a refusal.\n */\nexport async function gateApproval(input: ApprovalGateInput): Promise<ApprovalGateResult> {\n const { policy, operation } = input;\n if (policy === undefined) return { ok: true };\n const cancelled: ApprovalGateResult = { ok: false, reason: `${label(operation)} was cancelled. Nothing ran.` };\n const stopped = (): boolean => input.signal?.aborted === true;\n if (stopped()) return cancelled;\n if (policy.requirement === 'ask' && isUnattended(input)) return { ok: true };\n if (isApprovalUnavailable(policy, input)) {\n return {\n ok: false,\n reason: `${label(operation)} needs a person's approval and cannot request it from a subagent or background script. Nothing ran. Ask the main agent to request approval in its own session.`,\n };\n }\n const service: PoApprovalService | undefined = input.service;\n if (service === undefined) {\n return policy.requirement === 'ask'\n ? { ok: true }\n : {\n ok: false,\n reason: `${label(operation)} needs a person's approval, but approvals are not available in this session. Nothing ran.`,\n };\n }\n let saved: unknown;\n try {\n const json = JSON.stringify(input.arguments ?? null);\n if (new TextEncoder().encode(json).byteLength > APPROVAL_INPUT_LIMIT)\n throw new Error(`its input is larger than ${APPROVAL_INPUT_LIMIT} bytes`);\n saved = JSON.parse(json);\n if (input.argumentFormat === 'positional' && (operation.kind !== 'codemode' || !Array.isArray(saved)))\n throw new Error('positional arguments must be a code-mode argument list');\n } catch (error) {\n return {\n ok: false,\n reason: `${label(operation)} cannot wait for approval: ${error instanceof Error ? error.message : String(error)}. Nothing ran.`,\n };\n }\n let description: PoApprovalDescription;\n try {\n description = boundApprovalDescription(await policy.describe(input.input), operation.name);\n } catch (error) {\n return {\n ok: false,\n reason: `${label(operation)} could not be described for approval: ${error instanceof Error ? error.message : String(error)}. Nothing ran.`,\n };\n }\n if (stopped()) return cancelled;\n try {\n const { id } = await service.submit({\n sessionId: input.sessionId,\n toolCallId: input.toolCallId,\n operation,\n requirement: policy.requirement,\n description,\n input: saved,\n ...(input.argumentFormat === undefined ? {} : { argumentFormat: input.argumentFormat }),\n });\n return {\n ok: false,\n pending: true,\n approvalId: id,\n reason: `${label(operation)} is waiting for the person's approval (id ${id}). Nothing ran yet. Keep working; the result arrives as a message once they decide. Do not call it again.`,\n };\n } catch (error) {\n return {\n ok: false,\n reason: `${label(operation)} could not request approval: ${error instanceof Error ? error.message : String(error)}. Nothing ran.`,\n };\n }\n}\n"],"mappings":"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"}
@@ -14,7 +14,7 @@ export declare class ApprovalPendingError extends Error {
14
14
  export declare function boundApprovalDescription(description: PoApprovalDescription, fallback: string): PoApprovalDescription;
15
15
  /** A child or background run cannot elevate the session's host-set presence. */
16
16
  export declare function isUnattended(input: Pick<ApprovalGateInput, 'caller' | 'background' | 'interaction'>): boolean;
17
- /** True when an operation with this policy may not even be offered here. */
17
+ /** Foreground sessions may queue approval regardless of presence; child and background calls may not. */
18
18
  export declare function isApprovalUnavailable(policy: PoApprovalPolicy | undefined, input: Pick<ApprovalGateInput, 'caller' | 'background' | 'interaction'>): boolean;
19
19
  /**
20
20
  * The one admission rule for direct tools and code-mode members. Resolves `{ ok: true }` only when the operation
@@ -33,9 +33,9 @@ function boundApprovalDescription(description, fallback) {
33
33
  function isUnattended(input) {
34
34
  return input.caller === "child" || input.background === true || input.interaction === "unattended";
35
35
  }
36
- /** True when an operation with this policy may not even be offered here. */
36
+ /** Foreground sessions may queue approval regardless of presence; child and background calls may not. */
37
37
  function isApprovalUnavailable(policy, input) {
38
- return policy?.requirement === "human" && isUnattended(input);
38
+ return policy?.requirement === "human" && (input.caller === "child" || input.background === true);
39
39
  }
40
40
  function label(operation) {
41
41
  return `'${operation.name}'`;
@@ -54,9 +54,10 @@ async function gateApproval(input) {
54
54
  };
55
55
  const stopped = () => input.signal?.aborted === true;
56
56
  if (stopped()) return cancelled;
57
- if (isUnattended(input)) return policy.requirement === "ask" ? { ok: true } : {
57
+ if (policy.requirement === "ask" && isUnattended(input)) return { ok: true };
58
+ if (isApprovalUnavailable(policy, input)) return {
58
59
  ok: false,
59
- reason: `${label(operation)} needs a person's approval, and nobody can answer here (subagent, background script, workflow or scheduled run). Nothing ran. Ask the person to do it from their own session.`
60
+ reason: `${label(operation)} needs a person's approval and cannot request it from a subagent or background script. Nothing ran. Ask the main agent to request approval in its own session.`
60
61
  };
61
62
  const service = input.service;
62
63
  if (service === void 0) return policy.requirement === "ask" ? { ok: true } : {
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/services/approvalGate/index.ts"],"sourcesContent":["import {\n PO_APPROVAL_TEXT_LIMITS,\n type PoApprovalDescription,\n type PoApprovalOperation,\n type PoApprovalPolicy,\n type PoApprovalService,\n} from '../../schemas/approval';\nimport type { ApprovalGateInput, ApprovalGateResult } from './type';\n\nexport type { ApprovalGateInput, ApprovalGateResult } from './type';\n\n/** Saved input above this size is refused rather than stored. */\nexport const APPROVAL_INPUT_LIMIT = 256 * 1024;\n\n/** Thrown by a gated code-mode member while it waits for approval. Match on `name`: `instanceof` does not cross script sandboxes. */\nexport class ApprovalPendingError extends Error {\n override readonly name = 'ApprovalPendingError';\n constructor(\n readonly approvalId: string,\n message: string,\n ) {\n super(message);\n }\n}\n\nconst {\n title: TITLE_LIMIT,\n summary: SUMMARY_LIMIT,\n details: DETAIL_LIMIT,\n label: LABEL_LIMIT,\n value: VALUE_LIMIT,\n} = PO_APPROVAL_TEXT_LIMITS;\n\nfunction clip(value: unknown, limit: number): string {\n const text = typeof value === 'string' ? value.replace(/\\s+/gu, ' ').trim() : '';\n return text.length > limit ? `${text.slice(0, limit - 1)}…` : text;\n}\n\n/** Bounded plain text, whatever the package's `describe` returned. */\nexport function boundApprovalDescription(description: PoApprovalDescription, fallback: string): PoApprovalDescription {\n const details = Array.isArray(description?.details)\n ? description.details\n .slice(0, DETAIL_LIMIT)\n .map((detail) => ({ label: clip(detail?.label, LABEL_LIMIT), value: clip(detail?.value, VALUE_LIMIT) }))\n .filter((detail) => detail.label !== '' && detail.value !== '')\n : [];\n return {\n title: clip(description?.title, TITLE_LIMIT) || clip(fallback, TITLE_LIMIT),\n summary: clip(description?.summary, SUMMARY_LIMIT),\n ...(details.length === 0 ? {} : { details }),\n };\n}\n\n/** A child or background run cannot elevate the session's host-set presence. */\nexport function isUnattended(input: Pick<ApprovalGateInput, 'caller' | 'background' | 'interaction'>): boolean {\n return input.caller === 'child' || input.background === true || input.interaction === 'unattended';\n}\n\n/** True when an operation with this policy may not even be offered here. */\nexport function isApprovalUnavailable(\n policy: PoApprovalPolicy | undefined,\n input: Pick<ApprovalGateInput, 'caller' | 'background' | 'interaction'>,\n): boolean {\n return policy?.requirement === 'human' && isUnattended(input);\n}\n\nfunction label(operation: PoApprovalOperation): string {\n return `'${operation.name}'`;\n}\n\n/**\n * The one admission rule for direct tools and code-mode members. Resolves `{ ok: true }` only when the operation\n * may run now. A person-gated call is recorded and resolves `pending` at once; it runs later, once approved.\n * A refusal carries the message the agent sees. Never throws for a refusal.\n */\nexport async function gateApproval(input: ApprovalGateInput): Promise<ApprovalGateResult> {\n const { policy, operation } = input;\n if (policy === undefined) return { ok: true };\n const cancelled: ApprovalGateResult = { ok: false, reason: `${label(operation)} was cancelled. Nothing ran.` };\n const stopped = (): boolean => input.signal?.aborted === true;\n if (stopped()) return cancelled;\n if (isUnattended(input)) {\n return policy.requirement === 'ask'\n ? { ok: true }\n : {\n ok: false,\n reason: `${label(operation)} needs a person's approval, and nobody can answer here (subagent, background script, workflow or scheduled run). Nothing ran. Ask the person to do it from their own session.`,\n };\n }\n const service: PoApprovalService | undefined = input.service;\n if (service === undefined) {\n return policy.requirement === 'ask'\n ? { ok: true }\n : {\n ok: false,\n reason: `${label(operation)} needs a person's approval, but approvals are not available in this session. Nothing ran.`,\n };\n }\n let saved: unknown;\n try {\n const json = JSON.stringify(input.arguments ?? null);\n if (new TextEncoder().encode(json).byteLength > APPROVAL_INPUT_LIMIT)\n throw new Error(`its input is larger than ${APPROVAL_INPUT_LIMIT} bytes`);\n saved = JSON.parse(json);\n if (input.argumentFormat === 'positional' && (operation.kind !== 'codemode' || !Array.isArray(saved)))\n throw new Error('positional arguments must be a code-mode argument list');\n } catch (error) {\n return {\n ok: false,\n reason: `${label(operation)} cannot wait for approval: ${error instanceof Error ? error.message : String(error)}. Nothing ran.`,\n };\n }\n let description: PoApprovalDescription;\n try {\n description = boundApprovalDescription(await policy.describe(input.input), operation.name);\n } catch (error) {\n return {\n ok: false,\n reason: `${label(operation)} could not be described for approval: ${error instanceof Error ? error.message : String(error)}. Nothing ran.`,\n };\n }\n if (stopped()) return cancelled;\n try {\n const { id } = await service.submit({\n sessionId: input.sessionId,\n toolCallId: input.toolCallId,\n operation,\n requirement: policy.requirement,\n description,\n input: saved,\n ...(input.argumentFormat === undefined ? {} : { argumentFormat: input.argumentFormat }),\n });\n return {\n ok: false,\n pending: true,\n approvalId: id,\n reason: `${label(operation)} is waiting for the person's approval (id ${id}). Nothing ran yet. Keep working; the result arrives as a message once they decide. Do not call it again.`,\n };\n } catch (error) {\n return {\n ok: false,\n reason: `${label(operation)} could not request approval: ${error instanceof Error ? error.message : String(error)}. Nothing ran.`,\n };\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/services/approvalGate/index.ts"],"sourcesContent":["import {\n PO_APPROVAL_TEXT_LIMITS,\n type PoApprovalDescription,\n type PoApprovalOperation,\n type PoApprovalPolicy,\n type PoApprovalService,\n} from '../../schemas/approval';\nimport type { ApprovalGateInput, ApprovalGateResult } from './type';\n\nexport type { ApprovalGateInput, ApprovalGateResult } from './type';\n\n/** Saved input above this size is refused rather than stored. */\nexport const APPROVAL_INPUT_LIMIT = 256 * 1024;\n\n/** Thrown by a gated code-mode member while it waits for approval. Match on `name`: `instanceof` does not cross script sandboxes. */\nexport class ApprovalPendingError extends Error {\n override readonly name = 'ApprovalPendingError';\n constructor(\n readonly approvalId: string,\n message: string,\n ) {\n super(message);\n }\n}\n\nconst {\n title: TITLE_LIMIT,\n summary: SUMMARY_LIMIT,\n details: DETAIL_LIMIT,\n label: LABEL_LIMIT,\n value: VALUE_LIMIT,\n} = PO_APPROVAL_TEXT_LIMITS;\n\nfunction clip(value: unknown, limit: number): string {\n const text = typeof value === 'string' ? value.replace(/\\s+/gu, ' ').trim() : '';\n return text.length > limit ? `${text.slice(0, limit - 1)}…` : text;\n}\n\n/** Bounded plain text, whatever the package's `describe` returned. */\nexport function boundApprovalDescription(description: PoApprovalDescription, fallback: string): PoApprovalDescription {\n const details = Array.isArray(description?.details)\n ? description.details\n .slice(0, DETAIL_LIMIT)\n .map((detail) => ({ label: clip(detail?.label, LABEL_LIMIT), value: clip(detail?.value, VALUE_LIMIT) }))\n .filter((detail) => detail.label !== '' && detail.value !== '')\n : [];\n return {\n title: clip(description?.title, TITLE_LIMIT) || clip(fallback, TITLE_LIMIT),\n summary: clip(description?.summary, SUMMARY_LIMIT),\n ...(details.length === 0 ? {} : { details }),\n };\n}\n\n/** A child or background run cannot elevate the session's host-set presence. */\nexport function isUnattended(input: Pick<ApprovalGateInput, 'caller' | 'background' | 'interaction'>): boolean {\n return input.caller === 'child' || input.background === true || input.interaction === 'unattended';\n}\n\n/** Foreground sessions may queue approval regardless of presence; child and background calls may not. */\nexport function isApprovalUnavailable(\n policy: PoApprovalPolicy | undefined,\n input: Pick<ApprovalGateInput, 'caller' | 'background' | 'interaction'>,\n): boolean {\n return policy?.requirement === 'human' && (input.caller === 'child' || input.background === true);\n}\n\nfunction label(operation: PoApprovalOperation): string {\n return `'${operation.name}'`;\n}\n\n/**\n * The one admission rule for direct tools and code-mode members. Resolves `{ ok: true }` only when the operation\n * may run now. A person-gated call is recorded and resolves `pending` at once; it runs later, once approved.\n * A refusal carries the message the agent sees. Never throws for a refusal.\n */\nexport async function gateApproval(input: ApprovalGateInput): Promise<ApprovalGateResult> {\n const { policy, operation } = input;\n if (policy === undefined) return { ok: true };\n const cancelled: ApprovalGateResult = { ok: false, reason: `${label(operation)} was cancelled. Nothing ran.` };\n const stopped = (): boolean => input.signal?.aborted === true;\n if (stopped()) return cancelled;\n if (policy.requirement === 'ask' && isUnattended(input)) return { ok: true };\n if (isApprovalUnavailable(policy, input)) {\n return {\n ok: false,\n reason: `${label(operation)} needs a person's approval and cannot request it from a subagent or background script. Nothing ran. Ask the main agent to request approval in its own session.`,\n };\n }\n const service: PoApprovalService | undefined = input.service;\n if (service === undefined) {\n return policy.requirement === 'ask'\n ? { ok: true }\n : {\n ok: false,\n reason: `${label(operation)} needs a person's approval, but approvals are not available in this session. Nothing ran.`,\n };\n }\n let saved: unknown;\n try {\n const json = JSON.stringify(input.arguments ?? null);\n if (new TextEncoder().encode(json).byteLength > APPROVAL_INPUT_LIMIT)\n throw new Error(`its input is larger than ${APPROVAL_INPUT_LIMIT} bytes`);\n saved = JSON.parse(json);\n if (input.argumentFormat === 'positional' && (operation.kind !== 'codemode' || !Array.isArray(saved)))\n throw new Error('positional arguments must be a code-mode argument list');\n } catch (error) {\n return {\n ok: false,\n reason: `${label(operation)} cannot wait for approval: ${error instanceof Error ? error.message : String(error)}. Nothing ran.`,\n };\n }\n let description: PoApprovalDescription;\n try {\n description = boundApprovalDescription(await policy.describe(input.input), operation.name);\n } catch (error) {\n return {\n ok: false,\n reason: `${label(operation)} could not be described for approval: ${error instanceof Error ? error.message : String(error)}. Nothing ran.`,\n };\n }\n if (stopped()) return cancelled;\n try {\n const { id } = await service.submit({\n sessionId: input.sessionId,\n toolCallId: input.toolCallId,\n operation,\n requirement: policy.requirement,\n description,\n input: saved,\n ...(input.argumentFormat === undefined ? {} : { argumentFormat: input.argumentFormat }),\n });\n return {\n ok: false,\n pending: true,\n approvalId: id,\n reason: `${label(operation)} is waiting for the person's approval (id ${id}). Nothing ran yet. Keep working; the result arrives as a message once they decide. Do not call it again.`,\n };\n } catch (error) {\n return {\n ok: false,\n reason: `${label(operation)} could not request approval: ${error instanceof Error ? error.message : String(error)}. Nothing ran.`,\n };\n }\n}\n"],"mappings":"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"}
@@ -90,6 +90,21 @@ const GATED_ROUTES = [
90
90
  pattern: /^\/api(?:\/workspaces\/[^/]+)?\/settings\/(?:value|repository\/selection|images)$/u,
91
91
  action: "settings.write"
92
92
  },
93
+ {
94
+ method: "POST",
95
+ pattern: /^\/api(?:\/workspaces\/[^/]+)?\/plugins\/mcp\/servers$/u,
96
+ action: "settings.write"
97
+ },
98
+ {
99
+ method: "PATCH",
100
+ pattern: /^\/api(?:\/workspaces\/[^/]+)?\/plugins\/mcp\/servers$/u,
101
+ action: "settings.write"
102
+ },
103
+ {
104
+ method: "DELETE",
105
+ pattern: /^\/api(?:\/workspaces\/[^/]+)?\/plugins\/mcp\/servers$/u,
106
+ action: "settings.write"
107
+ },
93
108
  {
94
109
  method: "POST",
95
110
  pattern: /^\/api\/workspaces\/[^/]+\/plugins\/mcp\/repository\/discover$/u,
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":[],"sources":["../../../../src/services/webauthnPolicy/index.ts"],"sourcesContent":["/**\n * What a passkey is allowed to prove, and for how long.\n *\n * A session cookie is a bearer token: whoever holds the bytes is you. A passkey\n * is a private key in the device's secure enclave that never leaves it, so the\n * cookie stops being the only thing between a stolen phone and this machine.\n * The cookie does not go away, because a WebSocket cannot run a ceremony per\n * frame; what changes is that the cookie is short-lived, re-mintable with a\n * gesture rather than a QR, and insufficient on its own for the actions below.\n *\n * The crypto lives in the adapter. This file is the policy: which actions need\n * a fresh signature, how long a challenge is good for, and when a counter says\n * the credential has been cloned.\n */\n\n/**\n * Actions that need a fresh gesture even from a live session.\n *\n * These are escalation paths rather than ordinary prompting or tool approval:\n * credentials redirect machine traffic, sessions run code in a selected directory,\n * repository writes change future sessions, and live MCP operations may start local\n * processes or contact remote servers.\n */\nexport const STEP_UP_ACTIONS = [\n 'provider.login',\n 'provider.logout',\n 'session.create',\n 'settings.write',\n 'mcp.discover',\n 'mcp.authorize',\n 'computer-use.activate',\n // Revoking a trusted native device ends every session it holds; from a remote caller it needs a\n // fresh gesture so a stolen cookie cannot lock the owner's phone out.\n 'device.revoke',\n 'peer.link',\n] as const;\n\nexport type StepUpAction = (typeof STEP_UP_ACTIONS)[number];\n\n/** Short enough that a captured challenge is useless, long enough for a slow Face ID. */\nexport const STEP_UP_CHALLENGE_TTL_MS = 60_000;\n/** A registration or sign-in ceremony the user has to complete. */\nexport const CEREMONY_TTL_MS = 120_000;\n\n/**\n * Which action, if any, a request is attempting.\n *\n * Matched here rather than at each route so the list of gated operations is one\n * readable table instead of three call sites that can drift apart. Anything not\n * named is not gated, which is the safe default only because the gate is an\n * addition to the session check rather than a replacement for it.\n */\nconst GATED_ROUTES: readonly { method: string; pattern: RegExp; action: StepUpAction }[] = [\n { method: 'POST', pattern: /^\\/api\\/plugins\\/session-peer\\/link\\/(?:prepare|accept|unlink)$/u, action: 'peer.link' },\n { method: 'POST', pattern: /^\\/api\\/workspaces$/u, action: 'session.create' },\n {\n method: 'POST',\n // Reviving a recorded session starts an agent in a directory, so it is gated\n // exactly as creating one is.\n pattern: /^\\/api\\/workspaces\\/[^/]+\\/(?:resume|sessions(?:\\/[^/]+\\/(?:resume|revive))?)$/u,\n action: 'session.create',\n },\n // The git new-session dialog starts a worktree session, which runs code in a directory.\n { method: 'POST', pattern: /^\\/api\\/workspaces\\/[^/]+\\/plugins\\/git\\/sessions$/u, action: 'session.create' },\n // Choosing a workspace's sandbox changes where its sessions run code.\n { method: 'PATCH', pattern: /^\\/api\\/workspaces\\/[^/]+$/u, action: 'settings.write' },\n // A workspace's git remote credentials redirect what the cockpit pushes and where its token goes.\n { method: 'PUT', pattern: /^\\/api\\/workspaces\\/[^/]+\\/plugins\\/git\\/auth$/u, action: 'settings.write' },\n { method: 'POST', pattern: /^\\/api\\/plugins\\/po\\/logins(?:\\/[^/]+\\/answer)?$/u, action: 'provider.login' },\n { method: 'DELETE', pattern: /^\\/api\\/plugins\\/po\\/providers\\/[^/]+$/u, action: 'provider.logout' },\n {\n method: 'PUT',\n pattern: /^\\/api(?:\\/workspaces\\/[^/]+)?\\/settings\\/(?:value|repository\\/selection|images)$/u,\n action: 'settings.write',\n },\n {\n method: 'POST',\n pattern: /^\\/api\\/workspaces\\/[^/]+\\/plugins\\/mcp\\/repository\\/discover$/u,\n action: 'mcp.discover',\n },\n {\n method: 'POST',\n pattern: /^\\/api\\/workspaces\\/[^/]+\\/plugins\\/mcp\\/repository\\/authorize$/u,\n action: 'mcp.authorize',\n },\n {\n method: 'DELETE',\n pattern: /^\\/api\\/workspaces\\/[^/]+\\/plugins\\/mcp\\/repository\\/authorize\\/[^/]+$/u,\n action: 'mcp.authorize',\n },\n // Creating a session MCP client hands an outside app a credential into a live session, and\n // revoking one cuts an agent off mid-conversation. Both are gated like the repository revoke.\n {\n method: 'POST',\n pattern: /^\\/api\\/workspaces\\/[^/]+\\/sessions\\/[^/]+\\/mcp\\/clients$/u,\n action: 'mcp.authorize',\n },\n {\n method: 'DELETE',\n pattern: /^\\/api\\/workspaces\\/[^/]+\\/sessions\\/[^/]+\\/mcp\\/clients\\/[^/]+$/u,\n action: 'mcp.authorize',\n },\n {\n method: 'POST',\n pattern: /^\\/api\\/workspaces\\/[^/]+\\/sessions\\/[^/]+\\/plugins\\/computer-use\\/activate$/u,\n action: 'computer-use.activate',\n },\n { method: 'DELETE', pattern: /^\\/api\\/remote\\/trusted-devices\\/[^/]+$/u, action: 'device.revoke' },\n];\nexport function stepUpActionFor(method: string, path: string): StepUpAction | undefined {\n const wanted = method.toUpperCase();\n return GATED_ROUTES.find((route) => route.method === wanted && route.pattern.test(path))?.action;\n}\n\nexport function isStepUpAction(value: unknown): value is StepUpAction {\n return typeof value === 'string' && (STEP_UP_ACTIONS as readonly string[]).includes(value);\n}\n\nexport interface StoredCredential {\n /** Opaque handle for display and revocation. */\n id: string;\n /** The authenticator's credential id, base64url, as WebAuthn reports it. */\n credentialId: string;\n /** Base64url public key; the private half never leaves the device. */\n publicKey: string;\n /** Signature counter, for clone detection. */\n counter: number;\n transports: string[];\n label: string;\n createdAt: number;\n lastUsedAt: number;\n}\n\nexport type CounterVerdict = 'ok' | 'clone-suspected';\n\n/**\n * Whether a signature counter moved the way a real authenticator moves it.\n *\n * A counter that stays at zero means the authenticator does not implement one,\n * which is common and not a signal. A counter that goes backwards or repeats\n * while claiming to count means two things are signing with the same key, which\n * is the definition of a cloned credential.\n */\nexport function counterVerdict(stored: number, presented: number): CounterVerdict {\n if (stored === 0 && presented === 0) return 'ok';\n return presented > stored ? 'ok' : 'clone-suspected';\n}\n\n/** Whether a ceremony or step-up challenge is still live. */\nexport function challengeIsFresh(issuedAt: number, now: number, ttlMs: number): boolean {\n return now - issuedAt < ttlMs;\n}\n\n/** A quick tunnel's domain; its hostname rotates on every start. */\nconst ROTATING_SUFFIX = '.trycloudflare.com';\nexport function isQuickTunnelHost(origin: string | undefined): boolean {\n if (origin === undefined) return false;\n try {\n return new URL(origin).hostname.toLowerCase().endsWith(ROTATING_SUFFIX);\n } catch {\n return false;\n }\n}\n/** Dotted-quad, and the bracketed form a URL uses for v6. */\nconst IPV4 = /^\\d{1,3}(?:\\.\\d{1,3}){3}$/u;\n\n/**\n * The relying party id a passkey is bound to.\n *\n * Derived from the configured tunnel hostname and nothing else, never from a\n * request header: `rpID` is the entire scope of a credential, so a caller who\n * could influence it could mint credentials against somebody else's origin.\n *\n * Three things are refused rather than accepted and regretted later:\n *\n * - A quick tunnel, whose hostname rotates on every start, so a passkey\n * registered now would silently stop working on the next one.\n * - An IP address, which WebAuthn does not accept as a relying party id at all;\n * registering against one fails in the browser rather than here, which is a\n * much worse place to find out.\n * - Anything without a dot, which cannot be a public tunnel hostname.\n */\nexport function relyingPartyId(publicOrigin: string | undefined): string | undefined {\n if (publicOrigin === undefined) return undefined;\n let host: string;\n try {\n host = new URL(publicOrigin).hostname.toLowerCase();\n } catch {\n return undefined;\n }\n if (isQuickTunnelHost(publicOrigin)) return undefined;\n if (IPV4.test(host) || host.startsWith('[')) return undefined;\n if (!host.includes('.')) return undefined;\n return host;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAuBA,MAAa,kBAAkB;CAC7B;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;AACF;;AAKA,MAAa,2BAA2B;;AAExC,MAAa,kBAAkB;;;;;;;;;AAU/B,MAAM,eAAqF;CACzF;EAAE,QAAQ;EAAQ,SAAS;EAAoE,QAAQ;CAAY;CACnH;EAAE,QAAQ;EAAQ,SAAS;EAAwB,QAAQ;CAAiB;CAC5E;EACE,QAAQ;EAGR,SAAS;EACT,QAAQ;CACV;CAEA;EAAE,QAAQ;EAAQ,SAAS;EAAuD,QAAQ;CAAiB;CAE3G;EAAE,QAAQ;EAAS,SAAS;EAA+B,QAAQ;CAAiB;CAEpF;EAAE,QAAQ;EAAO,SAAS;EAAmD,QAAQ;CAAiB;CACtG;EAAE,QAAQ;EAAQ,SAAS;EAAqD,QAAQ;CAAiB;CACzG;EAAE,QAAQ;EAAU,SAAS;EAA2C,QAAQ;CAAkB;CAClG;EACE,QAAQ;EACR,SAAS;EACT,QAAQ;CACV;CACA;EACE,QAAQ;EACR,SAAS;EACT,QAAQ;CACV;CACA;EACE,QAAQ;EACR,SAAS;EACT,QAAQ;CACV;CACA;EACE,QAAQ;EACR,SAAS;EACT,QAAQ;CACV;CAGA;EACE,QAAQ;EACR,SAAS;EACT,QAAQ;CACV;CACA;EACE,QAAQ;EACR,SAAS;EACT,QAAQ;CACV;CACA;EACE,QAAQ;EACR,SAAS;EACT,QAAQ;CACV;CACA;EAAE,QAAQ;EAAU,SAAS;EAA4C,QAAQ;CAAgB;AACnG;AACA,SAAgB,gBAAgB,QAAgB,MAAwC;CACtF,MAAM,SAAS,OAAO,YAAY;CAClC,OAAO,aAAa,MAAM,UAAU,MAAM,WAAW,UAAU,MAAM,QAAQ,KAAK,IAAI,CAAC,CAAC,EAAE;AAC5F;AAEA,SAAgB,eAAe,OAAuC;CACpE,OAAO,OAAO,UAAU,YAAa,gBAAsC,SAAS,KAAK;AAC3F;;;;;;;;;AA2BA,SAAgB,eAAe,QAAgB,WAAmC;CAChF,IAAI,WAAW,KAAK,cAAc,GAAG,OAAO;CAC5C,OAAO,YAAY,SAAS,OAAO;AACrC;;AAGA,SAAgB,iBAAiB,UAAkB,KAAa,OAAwB;CACtF,OAAO,MAAM,WAAW;AAC1B;;AAGA,MAAM,kBAAkB;AACxB,SAAgB,kBAAkB,QAAqC;CACrE,IAAI,WAAW,KAAA,GAAW,OAAO;CACjC,IAAI;EACF,OAAO,IAAI,IAAI,MAAM,CAAC,CAAC,SAAS,YAAY,CAAC,CAAC,SAAS,eAAe;CACxE,QAAQ;EACN,OAAO;CACT;AACF;;AAEA,MAAM,OAAO;;;;;;;;;;;;;;;;;AAkBb,SAAgB,eAAe,cAAsD;CACnF,IAAI,iBAAiB,KAAA,GAAW,OAAO,KAAA;CACvC,IAAI;CACJ,IAAI;EACF,OAAO,IAAI,IAAI,YAAY,CAAC,CAAC,SAAS,YAAY;CACpD,QAAQ;EACN;CACF;CACA,IAAI,kBAAkB,YAAY,GAAG,OAAO,KAAA;CAC5C,IAAI,KAAK,KAAK,IAAI,KAAK,KAAK,WAAW,GAAG,GAAG,OAAO,KAAA;CACpD,IAAI,CAAC,KAAK,SAAS,GAAG,GAAG,OAAO,KAAA;CAChC,OAAO;AACT"}
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../../../../src/services/webauthnPolicy/index.ts"],"sourcesContent":["/**\n * What a passkey is allowed to prove, and for how long.\n *\n * A session cookie is a bearer token: whoever holds the bytes is you. A passkey\n * is a private key in the device's secure enclave that never leaves it, so the\n * cookie stops being the only thing between a stolen phone and this machine.\n * The cookie does not go away, because a WebSocket cannot run a ceremony per\n * frame; what changes is that the cookie is short-lived, re-mintable with a\n * gesture rather than a QR, and insufficient on its own for the actions below.\n *\n * The crypto lives in the adapter. This file is the policy: which actions need\n * a fresh signature, how long a challenge is good for, and when a counter says\n * the credential has been cloned.\n */\n\n/**\n * Actions that need a fresh gesture even from a live session.\n *\n * These are escalation paths rather than ordinary prompting or tool approval:\n * credentials redirect machine traffic, sessions run code in a selected directory,\n * repository writes change future sessions, and live MCP operations may start local\n * processes or contact remote servers.\n */\nexport const STEP_UP_ACTIONS = [\n 'provider.login',\n 'provider.logout',\n 'session.create',\n 'settings.write',\n 'mcp.discover',\n 'mcp.authorize',\n 'computer-use.activate',\n // Revoking a trusted native device ends every session it holds; from a remote caller it needs a\n // fresh gesture so a stolen cookie cannot lock the owner's phone out.\n 'device.revoke',\n 'peer.link',\n] as const;\n\nexport type StepUpAction = (typeof STEP_UP_ACTIONS)[number];\n\n/** Short enough that a captured challenge is useless, long enough for a slow Face ID. */\nexport const STEP_UP_CHALLENGE_TTL_MS = 60_000;\n/** A registration or sign-in ceremony the user has to complete. */\nexport const CEREMONY_TTL_MS = 120_000;\n\n/**\n * Which action, if any, a request is attempting.\n *\n * Matched here rather than at each route so the list of gated operations is one\n * readable table instead of three call sites that can drift apart. Anything not\n * named is not gated, which is the safe default only because the gate is an\n * addition to the session check rather than a replacement for it.\n */\nconst GATED_ROUTES: readonly { method: string; pattern: RegExp; action: StepUpAction }[] = [\n { method: 'POST', pattern: /^\\/api\\/plugins\\/session-peer\\/link\\/(?:prepare|accept|unlink)$/u, action: 'peer.link' },\n { method: 'POST', pattern: /^\\/api\\/workspaces$/u, action: 'session.create' },\n {\n method: 'POST',\n // Reviving a recorded session starts an agent in a directory, so it is gated\n // exactly as creating one is.\n pattern: /^\\/api\\/workspaces\\/[^/]+\\/(?:resume|sessions(?:\\/[^/]+\\/(?:resume|revive))?)$/u,\n action: 'session.create',\n },\n // The git new-session dialog starts a worktree session, which runs code in a directory.\n { method: 'POST', pattern: /^\\/api\\/workspaces\\/[^/]+\\/plugins\\/git\\/sessions$/u, action: 'session.create' },\n // Choosing a workspace's sandbox changes where its sessions run code.\n { method: 'PATCH', pattern: /^\\/api\\/workspaces\\/[^/]+$/u, action: 'settings.write' },\n // A workspace's git remote credentials redirect what the cockpit pushes and where its token goes.\n { method: 'PUT', pattern: /^\\/api\\/workspaces\\/[^/]+\\/plugins\\/git\\/auth$/u, action: 'settings.write' },\n { method: 'POST', pattern: /^\\/api\\/plugins\\/po\\/logins(?:\\/[^/]+\\/answer)?$/u, action: 'provider.login' },\n { method: 'DELETE', pattern: /^\\/api\\/plugins\\/po\\/providers\\/[^/]+$/u, action: 'provider.logout' },\n {\n method: 'PUT',\n pattern: /^\\/api(?:\\/workspaces\\/[^/]+)?\\/settings\\/(?:value|repository\\/selection|images)$/u,\n action: 'settings.write',\n },\n // Saved MCP configuration changes affect future sessions, like other settings writes.\n { method: 'POST', pattern: /^\\/api(?:\\/workspaces\\/[^/]+)?\\/plugins\\/mcp\\/servers$/u, action: 'settings.write' },\n { method: 'PATCH', pattern: /^\\/api(?:\\/workspaces\\/[^/]+)?\\/plugins\\/mcp\\/servers$/u, action: 'settings.write' },\n { method: 'DELETE', pattern: /^\\/api(?:\\/workspaces\\/[^/]+)?\\/plugins\\/mcp\\/servers$/u, action: 'settings.write' },\n {\n method: 'POST',\n pattern: /^\\/api\\/workspaces\\/[^/]+\\/plugins\\/mcp\\/repository\\/discover$/u,\n action: 'mcp.discover',\n },\n {\n method: 'POST',\n pattern: /^\\/api\\/workspaces\\/[^/]+\\/plugins\\/mcp\\/repository\\/authorize$/u,\n action: 'mcp.authorize',\n },\n {\n method: 'DELETE',\n pattern: /^\\/api\\/workspaces\\/[^/]+\\/plugins\\/mcp\\/repository\\/authorize\\/[^/]+$/u,\n action: 'mcp.authorize',\n },\n // Creating a session MCP client hands an outside app a credential into a live session, and\n // revoking one cuts an agent off mid-conversation. Both are gated like the repository revoke.\n {\n method: 'POST',\n pattern: /^\\/api\\/workspaces\\/[^/]+\\/sessions\\/[^/]+\\/mcp\\/clients$/u,\n action: 'mcp.authorize',\n },\n {\n method: 'DELETE',\n pattern: /^\\/api\\/workspaces\\/[^/]+\\/sessions\\/[^/]+\\/mcp\\/clients\\/[^/]+$/u,\n action: 'mcp.authorize',\n },\n {\n method: 'POST',\n pattern: /^\\/api\\/workspaces\\/[^/]+\\/sessions\\/[^/]+\\/plugins\\/computer-use\\/activate$/u,\n action: 'computer-use.activate',\n },\n { method: 'DELETE', pattern: /^\\/api\\/remote\\/trusted-devices\\/[^/]+$/u, action: 'device.revoke' },\n];\nexport function stepUpActionFor(method: string, path: string): StepUpAction | undefined {\n const wanted = method.toUpperCase();\n return GATED_ROUTES.find((route) => route.method === wanted && route.pattern.test(path))?.action;\n}\n\nexport function isStepUpAction(value: unknown): value is StepUpAction {\n return typeof value === 'string' && (STEP_UP_ACTIONS as readonly string[]).includes(value);\n}\n\nexport interface StoredCredential {\n /** Opaque handle for display and revocation. */\n id: string;\n /** The authenticator's credential id, base64url, as WebAuthn reports it. */\n credentialId: string;\n /** Base64url public key; the private half never leaves the device. */\n publicKey: string;\n /** Signature counter, for clone detection. */\n counter: number;\n transports: string[];\n label: string;\n createdAt: number;\n lastUsedAt: number;\n}\n\nexport type CounterVerdict = 'ok' | 'clone-suspected';\n\n/**\n * Whether a signature counter moved the way a real authenticator moves it.\n *\n * A counter that stays at zero means the authenticator does not implement one,\n * which is common and not a signal. A counter that goes backwards or repeats\n * while claiming to count means two things are signing with the same key, which\n * is the definition of a cloned credential.\n */\nexport function counterVerdict(stored: number, presented: number): CounterVerdict {\n if (stored === 0 && presented === 0) return 'ok';\n return presented > stored ? 'ok' : 'clone-suspected';\n}\n\n/** Whether a ceremony or step-up challenge is still live. */\nexport function challengeIsFresh(issuedAt: number, now: number, ttlMs: number): boolean {\n return now - issuedAt < ttlMs;\n}\n\n/** A quick tunnel's domain; its hostname rotates on every start. */\nconst ROTATING_SUFFIX = '.trycloudflare.com';\nexport function isQuickTunnelHost(origin: string | undefined): boolean {\n if (origin === undefined) return false;\n try {\n return new URL(origin).hostname.toLowerCase().endsWith(ROTATING_SUFFIX);\n } catch {\n return false;\n }\n}\n/** Dotted-quad, and the bracketed form a URL uses for v6. */\nconst IPV4 = /^\\d{1,3}(?:\\.\\d{1,3}){3}$/u;\n\n/**\n * The relying party id a passkey is bound to.\n *\n * Derived from the configured tunnel hostname and nothing else, never from a\n * request header: `rpID` is the entire scope of a credential, so a caller who\n * could influence it could mint credentials against somebody else's origin.\n *\n * Three things are refused rather than accepted and regretted later:\n *\n * - A quick tunnel, whose hostname rotates on every start, so a passkey\n * registered now would silently stop working on the next one.\n * - An IP address, which WebAuthn does not accept as a relying party id at all;\n * registering against one fails in the browser rather than here, which is a\n * much worse place to find out.\n * - Anything without a dot, which cannot be a public tunnel hostname.\n */\nexport function relyingPartyId(publicOrigin: string | undefined): string | undefined {\n if (publicOrigin === undefined) return undefined;\n let host: string;\n try {\n host = new URL(publicOrigin).hostname.toLowerCase();\n } catch {\n return undefined;\n }\n if (isQuickTunnelHost(publicOrigin)) return undefined;\n if (IPV4.test(host) || host.startsWith('[')) return undefined;\n if (!host.includes('.')) return undefined;\n return host;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAuBA,MAAa,kBAAkB;CAC7B;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;AACF;;AAKA,MAAa,2BAA2B;;AAExC,MAAa,kBAAkB;;;;;;;;;AAU/B,MAAM,eAAqF;CACzF;EAAE,QAAQ;EAAQ,SAAS;EAAoE,QAAQ;CAAY;CACnH;EAAE,QAAQ;EAAQ,SAAS;EAAwB,QAAQ;CAAiB;CAC5E;EACE,QAAQ;EAGR,SAAS;EACT,QAAQ;CACV;CAEA;EAAE,QAAQ;EAAQ,SAAS;EAAuD,QAAQ;CAAiB;CAE3G;EAAE,QAAQ;EAAS,SAAS;EAA+B,QAAQ;CAAiB;CAEpF;EAAE,QAAQ;EAAO,SAAS;EAAmD,QAAQ;CAAiB;CACtG;EAAE,QAAQ;EAAQ,SAAS;EAAqD,QAAQ;CAAiB;CACzG;EAAE,QAAQ;EAAU,SAAS;EAA2C,QAAQ;CAAkB;CAClG;EACE,QAAQ;EACR,SAAS;EACT,QAAQ;CACV;CAEA;EAAE,QAAQ;EAAQ,SAAS;EAA2D,QAAQ;CAAiB;CAC/G;EAAE,QAAQ;EAAS,SAAS;EAA2D,QAAQ;CAAiB;CAChH;EAAE,QAAQ;EAAU,SAAS;EAA2D,QAAQ;CAAiB;CACjH;EACE,QAAQ;EACR,SAAS;EACT,QAAQ;CACV;CACA;EACE,QAAQ;EACR,SAAS;EACT,QAAQ;CACV;CACA;EACE,QAAQ;EACR,SAAS;EACT,QAAQ;CACV;CAGA;EACE,QAAQ;EACR,SAAS;EACT,QAAQ;CACV;CACA;EACE,QAAQ;EACR,SAAS;EACT,QAAQ;CACV;CACA;EACE,QAAQ;EACR,SAAS;EACT,QAAQ;CACV;CACA;EAAE,QAAQ;EAAU,SAAS;EAA4C,QAAQ;CAAgB;AACnG;AACA,SAAgB,gBAAgB,QAAgB,MAAwC;CACtF,MAAM,SAAS,OAAO,YAAY;CAClC,OAAO,aAAa,MAAM,UAAU,MAAM,WAAW,UAAU,MAAM,QAAQ,KAAK,IAAI,CAAC,CAAC,EAAE;AAC5F;AAEA,SAAgB,eAAe,OAAuC;CACpE,OAAO,OAAO,UAAU,YAAa,gBAAsC,SAAS,KAAK;AAC3F;;;;;;;;;AA2BA,SAAgB,eAAe,QAAgB,WAAmC;CAChF,IAAI,WAAW,KAAK,cAAc,GAAG,OAAO;CAC5C,OAAO,YAAY,SAAS,OAAO;AACrC;;AAGA,SAAgB,iBAAiB,UAAkB,KAAa,OAAwB;CACtF,OAAO,MAAM,WAAW;AAC1B;;AAGA,MAAM,kBAAkB;AACxB,SAAgB,kBAAkB,QAAqC;CACrE,IAAI,WAAW,KAAA,GAAW,OAAO;CACjC,IAAI;EACF,OAAO,IAAI,IAAI,MAAM,CAAC,CAAC,SAAS,YAAY,CAAC,CAAC,SAAS,eAAe;CACxE,QAAQ;EACN,OAAO;CACT;AACF;;AAEA,MAAM,OAAO;;;;;;;;;;;;;;;;;AAkBb,SAAgB,eAAe,cAAsD;CACnF,IAAI,iBAAiB,KAAA,GAAW,OAAO,KAAA;CACvC,IAAI;CACJ,IAAI;EACF,OAAO,IAAI,IAAI,YAAY,CAAC,CAAC,SAAS,YAAY;CACpD,QAAQ;EACN;CACF;CACA,IAAI,kBAAkB,YAAY,GAAG,OAAO,KAAA;CAC5C,IAAI,KAAK,KAAK,IAAI,KAAK,KAAK,WAAW,GAAG,GAAG,OAAO,KAAA;CACpD,IAAI,CAAC,KAAK,SAAS,GAAG,GAAG,OAAO,KAAA;CAChC,OAAO;AACT"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../../../src/services/webauthnPolicy/index.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;qBAuBa;YAcD,uBAAuB;iBAiFlB;;EAEf;;EAEA;;EAEA;;EAEA;EACA;EACA;EACA;EACA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../../../src/services/webauthnPolicy/index.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;qBAuBa;YAcD,uBAAuB;iBAqFlB;;EAEf;;EAEA;;EAEA;;EAEA;EACA;EACA;EACA;EACA"}
@@ -90,6 +90,21 @@ const GATED_ROUTES = [
90
90
  pattern: /^\/api(?:\/workspaces\/[^/]+)?\/settings\/(?:value|repository\/selection|images)$/u,
91
91
  action: "settings.write"
92
92
  },
93
+ {
94
+ method: "POST",
95
+ pattern: /^\/api(?:\/workspaces\/[^/]+)?\/plugins\/mcp\/servers$/u,
96
+ action: "settings.write"
97
+ },
98
+ {
99
+ method: "PATCH",
100
+ pattern: /^\/api(?:\/workspaces\/[^/]+)?\/plugins\/mcp\/servers$/u,
101
+ action: "settings.write"
102
+ },
103
+ {
104
+ method: "DELETE",
105
+ pattern: /^\/api(?:\/workspaces\/[^/]+)?\/plugins\/mcp\/servers$/u,
106
+ action: "settings.write"
107
+ },
93
108
  {
94
109
  method: "POST",
95
110
  pattern: /^\/api\/workspaces\/[^/]+\/plugins\/mcp\/repository\/discover$/u,
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/services/webauthnPolicy/index.ts"],"sourcesContent":["/**\n * What a passkey is allowed to prove, and for how long.\n *\n * A session cookie is a bearer token: whoever holds the bytes is you. A passkey\n * is a private key in the device's secure enclave that never leaves it, so the\n * cookie stops being the only thing between a stolen phone and this machine.\n * The cookie does not go away, because a WebSocket cannot run a ceremony per\n * frame; what changes is that the cookie is short-lived, re-mintable with a\n * gesture rather than a QR, and insufficient on its own for the actions below.\n *\n * The crypto lives in the adapter. This file is the policy: which actions need\n * a fresh signature, how long a challenge is good for, and when a counter says\n * the credential has been cloned.\n */\n\n/**\n * Actions that need a fresh gesture even from a live session.\n *\n * These are escalation paths rather than ordinary prompting or tool approval:\n * credentials redirect machine traffic, sessions run code in a selected directory,\n * repository writes change future sessions, and live MCP operations may start local\n * processes or contact remote servers.\n */\nexport const STEP_UP_ACTIONS = [\n 'provider.login',\n 'provider.logout',\n 'session.create',\n 'settings.write',\n 'mcp.discover',\n 'mcp.authorize',\n 'computer-use.activate',\n // Revoking a trusted native device ends every session it holds; from a remote caller it needs a\n // fresh gesture so a stolen cookie cannot lock the owner's phone out.\n 'device.revoke',\n 'peer.link',\n] as const;\n\nexport type StepUpAction = (typeof STEP_UP_ACTIONS)[number];\n\n/** Short enough that a captured challenge is useless, long enough for a slow Face ID. */\nexport const STEP_UP_CHALLENGE_TTL_MS = 60_000;\n/** A registration or sign-in ceremony the user has to complete. */\nexport const CEREMONY_TTL_MS = 120_000;\n\n/**\n * Which action, if any, a request is attempting.\n *\n * Matched here rather than at each route so the list of gated operations is one\n * readable table instead of three call sites that can drift apart. Anything not\n * named is not gated, which is the safe default only because the gate is an\n * addition to the session check rather than a replacement for it.\n */\nconst GATED_ROUTES: readonly { method: string; pattern: RegExp; action: StepUpAction }[] = [\n { method: 'POST', pattern: /^\\/api\\/plugins\\/session-peer\\/link\\/(?:prepare|accept|unlink)$/u, action: 'peer.link' },\n { method: 'POST', pattern: /^\\/api\\/workspaces$/u, action: 'session.create' },\n {\n method: 'POST',\n // Reviving a recorded session starts an agent in a directory, so it is gated\n // exactly as creating one is.\n pattern: /^\\/api\\/workspaces\\/[^/]+\\/(?:resume|sessions(?:\\/[^/]+\\/(?:resume|revive))?)$/u,\n action: 'session.create',\n },\n // The git new-session dialog starts a worktree session, which runs code in a directory.\n { method: 'POST', pattern: /^\\/api\\/workspaces\\/[^/]+\\/plugins\\/git\\/sessions$/u, action: 'session.create' },\n // Choosing a workspace's sandbox changes where its sessions run code.\n { method: 'PATCH', pattern: /^\\/api\\/workspaces\\/[^/]+$/u, action: 'settings.write' },\n // A workspace's git remote credentials redirect what the cockpit pushes and where its token goes.\n { method: 'PUT', pattern: /^\\/api\\/workspaces\\/[^/]+\\/plugins\\/git\\/auth$/u, action: 'settings.write' },\n { method: 'POST', pattern: /^\\/api\\/plugins\\/po\\/logins(?:\\/[^/]+\\/answer)?$/u, action: 'provider.login' },\n { method: 'DELETE', pattern: /^\\/api\\/plugins\\/po\\/providers\\/[^/]+$/u, action: 'provider.logout' },\n {\n method: 'PUT',\n pattern: /^\\/api(?:\\/workspaces\\/[^/]+)?\\/settings\\/(?:value|repository\\/selection|images)$/u,\n action: 'settings.write',\n },\n {\n method: 'POST',\n pattern: /^\\/api\\/workspaces\\/[^/]+\\/plugins\\/mcp\\/repository\\/discover$/u,\n action: 'mcp.discover',\n },\n {\n method: 'POST',\n pattern: /^\\/api\\/workspaces\\/[^/]+\\/plugins\\/mcp\\/repository\\/authorize$/u,\n action: 'mcp.authorize',\n },\n {\n method: 'DELETE',\n pattern: /^\\/api\\/workspaces\\/[^/]+\\/plugins\\/mcp\\/repository\\/authorize\\/[^/]+$/u,\n action: 'mcp.authorize',\n },\n // Creating a session MCP client hands an outside app a credential into a live session, and\n // revoking one cuts an agent off mid-conversation. Both are gated like the repository revoke.\n {\n method: 'POST',\n pattern: /^\\/api\\/workspaces\\/[^/]+\\/sessions\\/[^/]+\\/mcp\\/clients$/u,\n action: 'mcp.authorize',\n },\n {\n method: 'DELETE',\n pattern: /^\\/api\\/workspaces\\/[^/]+\\/sessions\\/[^/]+\\/mcp\\/clients\\/[^/]+$/u,\n action: 'mcp.authorize',\n },\n {\n method: 'POST',\n pattern: /^\\/api\\/workspaces\\/[^/]+\\/sessions\\/[^/]+\\/plugins\\/computer-use\\/activate$/u,\n action: 'computer-use.activate',\n },\n { method: 'DELETE', pattern: /^\\/api\\/remote\\/trusted-devices\\/[^/]+$/u, action: 'device.revoke' },\n];\nexport function stepUpActionFor(method: string, path: string): StepUpAction | undefined {\n const wanted = method.toUpperCase();\n return GATED_ROUTES.find((route) => route.method === wanted && route.pattern.test(path))?.action;\n}\n\nexport function isStepUpAction(value: unknown): value is StepUpAction {\n return typeof value === 'string' && (STEP_UP_ACTIONS as readonly string[]).includes(value);\n}\n\nexport interface StoredCredential {\n /** Opaque handle for display and revocation. */\n id: string;\n /** The authenticator's credential id, base64url, as WebAuthn reports it. */\n credentialId: string;\n /** Base64url public key; the private half never leaves the device. */\n publicKey: string;\n /** Signature counter, for clone detection. */\n counter: number;\n transports: string[];\n label: string;\n createdAt: number;\n lastUsedAt: number;\n}\n\nexport type CounterVerdict = 'ok' | 'clone-suspected';\n\n/**\n * Whether a signature counter moved the way a real authenticator moves it.\n *\n * A counter that stays at zero means the authenticator does not implement one,\n * which is common and not a signal. A counter that goes backwards or repeats\n * while claiming to count means two things are signing with the same key, which\n * is the definition of a cloned credential.\n */\nexport function counterVerdict(stored: number, presented: number): CounterVerdict {\n if (stored === 0 && presented === 0) return 'ok';\n return presented > stored ? 'ok' : 'clone-suspected';\n}\n\n/** Whether a ceremony or step-up challenge is still live. */\nexport function challengeIsFresh(issuedAt: number, now: number, ttlMs: number): boolean {\n return now - issuedAt < ttlMs;\n}\n\n/** A quick tunnel's domain; its hostname rotates on every start. */\nconst ROTATING_SUFFIX = '.trycloudflare.com';\nexport function isQuickTunnelHost(origin: string | undefined): boolean {\n if (origin === undefined) return false;\n try {\n return new URL(origin).hostname.toLowerCase().endsWith(ROTATING_SUFFIX);\n } catch {\n return false;\n }\n}\n/** Dotted-quad, and the bracketed form a URL uses for v6. */\nconst IPV4 = /^\\d{1,3}(?:\\.\\d{1,3}){3}$/u;\n\n/**\n * The relying party id a passkey is bound to.\n *\n * Derived from the configured tunnel hostname and nothing else, never from a\n * request header: `rpID` is the entire scope of a credential, so a caller who\n * could influence it could mint credentials against somebody else's origin.\n *\n * Three things are refused rather than accepted and regretted later:\n *\n * - A quick tunnel, whose hostname rotates on every start, so a passkey\n * registered now would silently stop working on the next one.\n * - An IP address, which WebAuthn does not accept as a relying party id at all;\n * registering against one fails in the browser rather than here, which is a\n * much worse place to find out.\n * - Anything without a dot, which cannot be a public tunnel hostname.\n */\nexport function relyingPartyId(publicOrigin: string | undefined): string | undefined {\n if (publicOrigin === undefined) return undefined;\n let host: string;\n try {\n host = new URL(publicOrigin).hostname.toLowerCase();\n } catch {\n return undefined;\n }\n if (isQuickTunnelHost(publicOrigin)) return undefined;\n if (IPV4.test(host) || host.startsWith('[')) return undefined;\n if (!host.includes('.')) return undefined;\n return host;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAuBA,MAAa,kBAAkB;CAC7B;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;AACF;;AAKA,MAAa,2BAA2B;;AAExC,MAAa,kBAAkB;;;;;;;;;AAU/B,MAAM,eAAqF;CACzF;EAAE,QAAQ;EAAQ,SAAS;EAAoE,QAAQ;CAAY;CACnH;EAAE,QAAQ;EAAQ,SAAS;EAAwB,QAAQ;CAAiB;CAC5E;EACE,QAAQ;EAGR,SAAS;EACT,QAAQ;CACV;CAEA;EAAE,QAAQ;EAAQ,SAAS;EAAuD,QAAQ;CAAiB;CAE3G;EAAE,QAAQ;EAAS,SAAS;EAA+B,QAAQ;CAAiB;CAEpF;EAAE,QAAQ;EAAO,SAAS;EAAmD,QAAQ;CAAiB;CACtG;EAAE,QAAQ;EAAQ,SAAS;EAAqD,QAAQ;CAAiB;CACzG;EAAE,QAAQ;EAAU,SAAS;EAA2C,QAAQ;CAAkB;CAClG;EACE,QAAQ;EACR,SAAS;EACT,QAAQ;CACV;CACA;EACE,QAAQ;EACR,SAAS;EACT,QAAQ;CACV;CACA;EACE,QAAQ;EACR,SAAS;EACT,QAAQ;CACV;CACA;EACE,QAAQ;EACR,SAAS;EACT,QAAQ;CACV;CAGA;EACE,QAAQ;EACR,SAAS;EACT,QAAQ;CACV;CACA;EACE,QAAQ;EACR,SAAS;EACT,QAAQ;CACV;CACA;EACE,QAAQ;EACR,SAAS;EACT,QAAQ;CACV;CACA;EAAE,QAAQ;EAAU,SAAS;EAA4C,QAAQ;CAAgB;AACnG;AACA,SAAgB,gBAAgB,QAAgB,MAAwC;CACtF,MAAM,SAAS,OAAO,YAAY;CAClC,OAAO,aAAa,MAAM,UAAU,MAAM,WAAW,UAAU,MAAM,QAAQ,KAAK,IAAI,CAAC,CAAC,EAAE;AAC5F;AAEA,SAAgB,eAAe,OAAuC;CACpE,OAAO,OAAO,UAAU,YAAa,gBAAsC,SAAS,KAAK;AAC3F;;;;;;;;;AA2BA,SAAgB,eAAe,QAAgB,WAAmC;CAChF,IAAI,WAAW,KAAK,cAAc,GAAG,OAAO;CAC5C,OAAO,YAAY,SAAS,OAAO;AACrC;;AAGA,SAAgB,iBAAiB,UAAkB,KAAa,OAAwB;CACtF,OAAO,MAAM,WAAW;AAC1B;;AAGA,MAAM,kBAAkB;AACxB,SAAgB,kBAAkB,QAAqC;CACrE,IAAI,WAAW,KAAA,GAAW,OAAO;CACjC,IAAI;EACF,OAAO,IAAI,IAAI,MAAM,CAAC,CAAC,SAAS,YAAY,CAAC,CAAC,SAAS,eAAe;CACxE,QAAQ;EACN,OAAO;CACT;AACF;;AAEA,MAAM,OAAO;;;;;;;;;;;;;;;;;AAkBb,SAAgB,eAAe,cAAsD;CACnF,IAAI,iBAAiB,KAAA,GAAW,OAAO,KAAA;CACvC,IAAI;CACJ,IAAI;EACF,OAAO,IAAI,IAAI,YAAY,CAAC,CAAC,SAAS,YAAY;CACpD,QAAQ;EACN;CACF;CACA,IAAI,kBAAkB,YAAY,GAAG,OAAO,KAAA;CAC5C,IAAI,KAAK,KAAK,IAAI,KAAK,KAAK,WAAW,GAAG,GAAG,OAAO,KAAA;CACpD,IAAI,CAAC,KAAK,SAAS,GAAG,GAAG,OAAO,KAAA;CAChC,OAAO;AACT"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/services/webauthnPolicy/index.ts"],"sourcesContent":["/**\n * What a passkey is allowed to prove, and for how long.\n *\n * A session cookie is a bearer token: whoever holds the bytes is you. A passkey\n * is a private key in the device's secure enclave that never leaves it, so the\n * cookie stops being the only thing between a stolen phone and this machine.\n * The cookie does not go away, because a WebSocket cannot run a ceremony per\n * frame; what changes is that the cookie is short-lived, re-mintable with a\n * gesture rather than a QR, and insufficient on its own for the actions below.\n *\n * The crypto lives in the adapter. This file is the policy: which actions need\n * a fresh signature, how long a challenge is good for, and when a counter says\n * the credential has been cloned.\n */\n\n/**\n * Actions that need a fresh gesture even from a live session.\n *\n * These are escalation paths rather than ordinary prompting or tool approval:\n * credentials redirect machine traffic, sessions run code in a selected directory,\n * repository writes change future sessions, and live MCP operations may start local\n * processes or contact remote servers.\n */\nexport const STEP_UP_ACTIONS = [\n 'provider.login',\n 'provider.logout',\n 'session.create',\n 'settings.write',\n 'mcp.discover',\n 'mcp.authorize',\n 'computer-use.activate',\n // Revoking a trusted native device ends every session it holds; from a remote caller it needs a\n // fresh gesture so a stolen cookie cannot lock the owner's phone out.\n 'device.revoke',\n 'peer.link',\n] as const;\n\nexport type StepUpAction = (typeof STEP_UP_ACTIONS)[number];\n\n/** Short enough that a captured challenge is useless, long enough for a slow Face ID. */\nexport const STEP_UP_CHALLENGE_TTL_MS = 60_000;\n/** A registration or sign-in ceremony the user has to complete. */\nexport const CEREMONY_TTL_MS = 120_000;\n\n/**\n * Which action, if any, a request is attempting.\n *\n * Matched here rather than at each route so the list of gated operations is one\n * readable table instead of three call sites that can drift apart. Anything not\n * named is not gated, which is the safe default only because the gate is an\n * addition to the session check rather than a replacement for it.\n */\nconst GATED_ROUTES: readonly { method: string; pattern: RegExp; action: StepUpAction }[] = [\n { method: 'POST', pattern: /^\\/api\\/plugins\\/session-peer\\/link\\/(?:prepare|accept|unlink)$/u, action: 'peer.link' },\n { method: 'POST', pattern: /^\\/api\\/workspaces$/u, action: 'session.create' },\n {\n method: 'POST',\n // Reviving a recorded session starts an agent in a directory, so it is gated\n // exactly as creating one is.\n pattern: /^\\/api\\/workspaces\\/[^/]+\\/(?:resume|sessions(?:\\/[^/]+\\/(?:resume|revive))?)$/u,\n action: 'session.create',\n },\n // The git new-session dialog starts a worktree session, which runs code in a directory.\n { method: 'POST', pattern: /^\\/api\\/workspaces\\/[^/]+\\/plugins\\/git\\/sessions$/u, action: 'session.create' },\n // Choosing a workspace's sandbox changes where its sessions run code.\n { method: 'PATCH', pattern: /^\\/api\\/workspaces\\/[^/]+$/u, action: 'settings.write' },\n // A workspace's git remote credentials redirect what the cockpit pushes and where its token goes.\n { method: 'PUT', pattern: /^\\/api\\/workspaces\\/[^/]+\\/plugins\\/git\\/auth$/u, action: 'settings.write' },\n { method: 'POST', pattern: /^\\/api\\/plugins\\/po\\/logins(?:\\/[^/]+\\/answer)?$/u, action: 'provider.login' },\n { method: 'DELETE', pattern: /^\\/api\\/plugins\\/po\\/providers\\/[^/]+$/u, action: 'provider.logout' },\n {\n method: 'PUT',\n pattern: /^\\/api(?:\\/workspaces\\/[^/]+)?\\/settings\\/(?:value|repository\\/selection|images)$/u,\n action: 'settings.write',\n },\n // Saved MCP configuration changes affect future sessions, like other settings writes.\n { method: 'POST', pattern: /^\\/api(?:\\/workspaces\\/[^/]+)?\\/plugins\\/mcp\\/servers$/u, action: 'settings.write' },\n { method: 'PATCH', pattern: /^\\/api(?:\\/workspaces\\/[^/]+)?\\/plugins\\/mcp\\/servers$/u, action: 'settings.write' },\n { method: 'DELETE', pattern: /^\\/api(?:\\/workspaces\\/[^/]+)?\\/plugins\\/mcp\\/servers$/u, action: 'settings.write' },\n {\n method: 'POST',\n pattern: /^\\/api\\/workspaces\\/[^/]+\\/plugins\\/mcp\\/repository\\/discover$/u,\n action: 'mcp.discover',\n },\n {\n method: 'POST',\n pattern: /^\\/api\\/workspaces\\/[^/]+\\/plugins\\/mcp\\/repository\\/authorize$/u,\n action: 'mcp.authorize',\n },\n {\n method: 'DELETE',\n pattern: /^\\/api\\/workspaces\\/[^/]+\\/plugins\\/mcp\\/repository\\/authorize\\/[^/]+$/u,\n action: 'mcp.authorize',\n },\n // Creating a session MCP client hands an outside app a credential into a live session, and\n // revoking one cuts an agent off mid-conversation. Both are gated like the repository revoke.\n {\n method: 'POST',\n pattern: /^\\/api\\/workspaces\\/[^/]+\\/sessions\\/[^/]+\\/mcp\\/clients$/u,\n action: 'mcp.authorize',\n },\n {\n method: 'DELETE',\n pattern: /^\\/api\\/workspaces\\/[^/]+\\/sessions\\/[^/]+\\/mcp\\/clients\\/[^/]+$/u,\n action: 'mcp.authorize',\n },\n {\n method: 'POST',\n pattern: /^\\/api\\/workspaces\\/[^/]+\\/sessions\\/[^/]+\\/plugins\\/computer-use\\/activate$/u,\n action: 'computer-use.activate',\n },\n { method: 'DELETE', pattern: /^\\/api\\/remote\\/trusted-devices\\/[^/]+$/u, action: 'device.revoke' },\n];\nexport function stepUpActionFor(method: string, path: string): StepUpAction | undefined {\n const wanted = method.toUpperCase();\n return GATED_ROUTES.find((route) => route.method === wanted && route.pattern.test(path))?.action;\n}\n\nexport function isStepUpAction(value: unknown): value is StepUpAction {\n return typeof value === 'string' && (STEP_UP_ACTIONS as readonly string[]).includes(value);\n}\n\nexport interface StoredCredential {\n /** Opaque handle for display and revocation. */\n id: string;\n /** The authenticator's credential id, base64url, as WebAuthn reports it. */\n credentialId: string;\n /** Base64url public key; the private half never leaves the device. */\n publicKey: string;\n /** Signature counter, for clone detection. */\n counter: number;\n transports: string[];\n label: string;\n createdAt: number;\n lastUsedAt: number;\n}\n\nexport type CounterVerdict = 'ok' | 'clone-suspected';\n\n/**\n * Whether a signature counter moved the way a real authenticator moves it.\n *\n * A counter that stays at zero means the authenticator does not implement one,\n * which is common and not a signal. A counter that goes backwards or repeats\n * while claiming to count means two things are signing with the same key, which\n * is the definition of a cloned credential.\n */\nexport function counterVerdict(stored: number, presented: number): CounterVerdict {\n if (stored === 0 && presented === 0) return 'ok';\n return presented > stored ? 'ok' : 'clone-suspected';\n}\n\n/** Whether a ceremony or step-up challenge is still live. */\nexport function challengeIsFresh(issuedAt: number, now: number, ttlMs: number): boolean {\n return now - issuedAt < ttlMs;\n}\n\n/** A quick tunnel's domain; its hostname rotates on every start. */\nconst ROTATING_SUFFIX = '.trycloudflare.com';\nexport function isQuickTunnelHost(origin: string | undefined): boolean {\n if (origin === undefined) return false;\n try {\n return new URL(origin).hostname.toLowerCase().endsWith(ROTATING_SUFFIX);\n } catch {\n return false;\n }\n}\n/** Dotted-quad, and the bracketed form a URL uses for v6. */\nconst IPV4 = /^\\d{1,3}(?:\\.\\d{1,3}){3}$/u;\n\n/**\n * The relying party id a passkey is bound to.\n *\n * Derived from the configured tunnel hostname and nothing else, never from a\n * request header: `rpID` is the entire scope of a credential, so a caller who\n * could influence it could mint credentials against somebody else's origin.\n *\n * Three things are refused rather than accepted and regretted later:\n *\n * - A quick tunnel, whose hostname rotates on every start, so a passkey\n * registered now would silently stop working on the next one.\n * - An IP address, which WebAuthn does not accept as a relying party id at all;\n * registering against one fails in the browser rather than here, which is a\n * much worse place to find out.\n * - Anything without a dot, which cannot be a public tunnel hostname.\n */\nexport function relyingPartyId(publicOrigin: string | undefined): string | undefined {\n if (publicOrigin === undefined) return undefined;\n let host: string;\n try {\n host = new URL(publicOrigin).hostname.toLowerCase();\n } catch {\n return undefined;\n }\n if (isQuickTunnelHost(publicOrigin)) return undefined;\n if (IPV4.test(host) || host.startsWith('[')) return undefined;\n if (!host.includes('.')) return undefined;\n return host;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAuBA,MAAa,kBAAkB;CAC7B;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;AACF;;AAKA,MAAa,2BAA2B;;AAExC,MAAa,kBAAkB;;;;;;;;;AAU/B,MAAM,eAAqF;CACzF;EAAE,QAAQ;EAAQ,SAAS;EAAoE,QAAQ;CAAY;CACnH;EAAE,QAAQ;EAAQ,SAAS;EAAwB,QAAQ;CAAiB;CAC5E;EACE,QAAQ;EAGR,SAAS;EACT,QAAQ;CACV;CAEA;EAAE,QAAQ;EAAQ,SAAS;EAAuD,QAAQ;CAAiB;CAE3G;EAAE,QAAQ;EAAS,SAAS;EAA+B,QAAQ;CAAiB;CAEpF;EAAE,QAAQ;EAAO,SAAS;EAAmD,QAAQ;CAAiB;CACtG;EAAE,QAAQ;EAAQ,SAAS;EAAqD,QAAQ;CAAiB;CACzG;EAAE,QAAQ;EAAU,SAAS;EAA2C,QAAQ;CAAkB;CAClG;EACE,QAAQ;EACR,SAAS;EACT,QAAQ;CACV;CAEA;EAAE,QAAQ;EAAQ,SAAS;EAA2D,QAAQ;CAAiB;CAC/G;EAAE,QAAQ;EAAS,SAAS;EAA2D,QAAQ;CAAiB;CAChH;EAAE,QAAQ;EAAU,SAAS;EAA2D,QAAQ;CAAiB;CACjH;EACE,QAAQ;EACR,SAAS;EACT,QAAQ;CACV;CACA;EACE,QAAQ;EACR,SAAS;EACT,QAAQ;CACV;CACA;EACE,QAAQ;EACR,SAAS;EACT,QAAQ;CACV;CAGA;EACE,QAAQ;EACR,SAAS;EACT,QAAQ;CACV;CACA;EACE,QAAQ;EACR,SAAS;EACT,QAAQ;CACV;CACA;EACE,QAAQ;EACR,SAAS;EACT,QAAQ;CACV;CACA;EAAE,QAAQ;EAAU,SAAS;EAA4C,QAAQ;CAAgB;AACnG;AACA,SAAgB,gBAAgB,QAAgB,MAAwC;CACtF,MAAM,SAAS,OAAO,YAAY;CAClC,OAAO,aAAa,MAAM,UAAU,MAAM,WAAW,UAAU,MAAM,QAAQ,KAAK,IAAI,CAAC,CAAC,EAAE;AAC5F;AAEA,SAAgB,eAAe,OAAuC;CACpE,OAAO,OAAO,UAAU,YAAa,gBAAsC,SAAS,KAAK;AAC3F;;;;;;;;;AA2BA,SAAgB,eAAe,QAAgB,WAAmC;CAChF,IAAI,WAAW,KAAK,cAAc,GAAG,OAAO;CAC5C,OAAO,YAAY,SAAS,OAAO;AACrC;;AAGA,SAAgB,iBAAiB,UAAkB,KAAa,OAAwB;CACtF,OAAO,MAAM,WAAW;AAC1B;;AAGA,MAAM,kBAAkB;AACxB,SAAgB,kBAAkB,QAAqC;CACrE,IAAI,WAAW,KAAA,GAAW,OAAO;CACjC,IAAI;EACF,OAAO,IAAI,IAAI,MAAM,CAAC,CAAC,SAAS,YAAY,CAAC,CAAC,SAAS,eAAe;CACxE,QAAQ;EACN,OAAO;CACT;AACF;;AAEA,MAAM,OAAO;;;;;;;;;;;;;;;;;AAkBb,SAAgB,eAAe,cAAsD;CACnF,IAAI,iBAAiB,KAAA,GAAW,OAAO,KAAA;CACvC,IAAI;CACJ,IAAI;EACF,OAAO,IAAI,IAAI,YAAY,CAAC,CAAC,SAAS,YAAY;CACpD,QAAQ;EACN;CACF;CACA,IAAI,kBAAkB,YAAY,GAAG,OAAO,KAAA;CAC5C,IAAI,KAAK,KAAK,IAAI,KAAK,KAAK,WAAW,GAAG,GAAG,OAAO,KAAA;CACpD,IAAI,CAAC,KAAK,SAAS,GAAG,GAAG,OAAO,KAAA;CAChC,OAAO;AACT"}