@agimon-ai/po-core 0.0.0-alpha.82 → 0.0.0-alpha.84

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.
@@ -365,6 +365,31 @@ interface PoWorkflowExecutionPolicyResult {
365
365
  /** Trusted non-secret context only. PO_* and WORKFLOW_* engine identity keys are forbidden. */
366
366
  readonly environment?: Readonly<Record<string, string>>;
367
367
  }
368
+ /**
369
+ * How a bound generation ended. `abandoned` is a person giving up a failed run: it is never recovered.
370
+ * A coordinator releases what it holds for success, skipped and abandoned, and keeps it for failed and
371
+ * interrupted so a recovery of the run reuses it.
372
+ */
373
+ type PoWorkflowSettlementOutcome = PoWorkflowOutcome | 'abandoned';
374
+ interface PoWorkflowSettlementRequest {
375
+ readonly binding: PoWorkflowPolicyBinding;
376
+ readonly run: PoWorkflowRunRef;
377
+ readonly outcome: PoWorkflowSettlementOutcome;
378
+ readonly failedJob?: string;
379
+ readonly ownerSessionId: string;
380
+ readonly launcherSessionId: string;
381
+ /** Ephemeral host lifetime, never persisted. */
382
+ readonly signal: AbortSignal;
383
+ }
384
+ interface PoWorkflowPolicyOptions {
385
+ readonly environmentKeys: readonly string[];
386
+ /**
387
+ * Called once a bound generation is terminal, and again with `abandoned` if a person abandons it.
388
+ * Delivery is durable: the engine retries until this resolves, including after a restart or a
389
+ * remount, so it must be idempotent. Throwing leaves the settlement pending.
390
+ */
391
+ readonly settle?: (request: PoWorkflowSettlementRequest) => Promise<void>;
392
+ }
368
393
  interface PoWorkflowAdmissionOverrides extends PoWorkflowLaunchOverrides {
369
394
  readonly policy?: PoWorkflowPolicyAssociation;
370
395
  }
@@ -425,9 +450,7 @@ interface PoWorkflowAdmissionsService {
425
450
  * Disposal is generation-fenced. Remounting only reattaches; other sessions never supply this callback.
426
451
  * The engine persists the source launcher identity and never falls back to another session's policy.
427
452
  */
428
- registerPolicy(coordinator: string, beforeExecution: (request: PoWorkflowExecutionPolicyRequest) => Promise<PoWorkflowExecutionPolicyResult | void>, options: {
429
- readonly environmentKeys: readonly string[];
430
- }): {
453
+ registerPolicy(coordinator: string, beforeExecution: (request: PoWorkflowExecutionPolicyRequest) => Promise<PoWorkflowExecutionPolicyResult | void>, options: PoWorkflowPolicyOptions): {
431
454
  dispose(): void;
432
455
  };
433
456
  }
@@ -1 +1 @@
1
- {"version":3,"file":"apiContracts.d.mts","names":["_0","_0","_0","IoContext","_0","_$__hubChannel0.PoHubSessionUsage","_0","_$__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","_0","_$__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":"workflow.cjs","names":["Type"],"sources":["../../../src/schemas/workflow.ts"],"sourcesContent":["import type { Context } from '@deepseek-ai/cordis';\nimport { type Static, Type } from 'typebox';\n\nexport const PO_WORKFLOW_LAUNCH_SERVICE = 'po/workflow-launch';\nexport const PO_WORKFLOW_DEFINITION_SOURCES_SERVICE = 'po/workflow-definition-sources';\nexport const PO_WORKFLOW_RUNS_SERVICE = 'po/workflow-runs';\nexport const PO_WORKFLOW_CONTEXT_SERVICE = 'po/workflow-context';\nexport const PO_WORKFLOW_ADMISSIONS_SERVICE = 'po/workflow-admissions';\n\n/** A source-relative definition, never an arbitrary host path. */\nexport const PoWorkflowDefinitionRefSchema = Type.Object(\n {\n source: Type.String({ minLength: 1 }),\n key: Type.String({\n minLength: 1,\n pattern: '^(?!/)(?!.*\\\\\\\\)(?!.*:)(?!.*[\\\\u0000-\\\\u001f\\\\u007f])(?!.*(?:^|/)\\\\.{1,2}(?:/|$))[^/]+(?:/[^/]+)*$',\n }),\n },\n { additionalProperties: false },\n);\nexport type PoWorkflowDefinitionRef = Static<typeof PoWorkflowDefinitionRefSchema>;\n\n/** Registration receipt. runId fences a runKey that can be reused later. */\nexport const PoWorkflowRunRefSchema = Type.Object(\n {\n runId: Type.String({ minLength: 1 }),\n ownerSessionId: Type.String({ minLength: 1 }),\n runKey: Type.String({ minLength: 1 }),\n registryWorkspace: Type.String({ minLength: 1 }),\n },\n { additionalProperties: false },\n);\nexport type PoWorkflowRunRef = Static<typeof PoWorkflowRunRefSchema>;\n\nexport interface PoWorkflowLaunchOverrides {\n readonly runner?: string;\n readonly command?: string;\n readonly choice?: string;\n readonly job?: string;\n readonly inputs?: Readonly<Record<string, string>>;\n readonly env?: Readonly<Record<string, string>>;\n readonly prompt?: string;\n readonly name?: string;\n}\n\n/** Session-owned. Rejection means no observable registration was accepted. */\nexport interface PoWorkflowLaunchService {\n launch(definition: PoWorkflowDefinitionRef, overrides?: PoWorkflowLaunchOverrides): Promise<PoWorkflowRunRef>;\n}\n\n/** Non-secret host association. Environment variables never establish this binding. */\nexport interface PoWorkflowPolicyAssociation {\n readonly coordinator: string;\n readonly key: string;\n}\n\n/** Independent of runId and executionGeneration, including continue-next resumes. */\nexport interface PoWorkflowOperationEpoch {\n readonly admissionId: string;\n readonly operationId: string;\n}\n\nexport interface PoWorkflowPolicyBinding {\n readonly association: PoWorkflowPolicyAssociation;\n readonly epoch: PoWorkflowOperationEpoch;\n}\n\nexport interface PoWorkflowExecutionPolicyRequest {\n readonly binding: PoWorkflowPolicyBinding;\n readonly reason: 'launch' | 'recover' | 'startup' | 'resume';\n readonly ownerSessionId: string;\n readonly launcherSessionId: string;\n /** The engine supplies the actual persisted run after createRun, including startup and recovery. */\n readonly run?: PoWorkflowRunRef;\n readonly previousRun?: PoWorkflowRunRef;\n /** Ephemeral host lifetime, never persisted. */\n readonly signal: AbortSignal;\n}\n\nexport interface PoWorkflowExecutionPolicyResult {\n /** Trusted non-secret context only. PO_* and WORKFLOW_* engine identity keys are forbidden. */\n readonly environment?: Readonly<Record<string, string>>;\n}\n\nexport interface PoWorkflowAdmissionOverrides extends PoWorkflowLaunchOverrides {\n readonly policy?: PoWorkflowPolicyAssociation;\n}\n\nexport interface PoWorkflowRecoveryOptions {\n readonly runner?: string;\n}\n\nexport type PoWorkflowAdmissionRequest =\n | {\n readonly kind: 'launch';\n readonly definition: PoWorkflowDefinitionRef;\n readonly overrides?: PoWorkflowAdmissionOverrides;\n }\n | { readonly kind: 'recover'; readonly previousRun: PoWorkflowRunRef; readonly options?: PoWorkflowRecoveryOptions };\n\nexport interface PoWorkflowValidationReport {\n readonly errors: readonly { readonly code: string; readonly message: string; readonly location?: string }[];\n readonly warnings: readonly { readonly code: string; readonly message: string; readonly location?: string }[];\n readonly deferred: readonly { readonly location: string; readonly reference: string; readonly reason: string }[];\n}\n\nexport type PoWorkflowAdmission =\n | {\n readonly attemptId: string;\n readonly admissionId: string;\n readonly state: 'pending';\n readonly binding?: PoWorkflowPolicyBinding;\n }\n | {\n readonly attemptId: string;\n readonly admissionId: string;\n readonly state: 'rejected';\n readonly errors: readonly string[];\n }\n | {\n readonly attemptId: string;\n readonly admissionId: string;\n readonly state: 'registered';\n readonly run: PoWorkflowRunRef;\n readonly binding?: PoWorkflowPolicyBinding;\n };\n\n/** Trusted, session-bound opt-in capability, never exposed as a tool or code-mode namespace. */\nexport interface PoWorkflowAdmissionsService {\n validate(request: PoWorkflowAdmissionRequest): Promise<PoWorkflowValidationReport>;\n launch(\n attemptId: string,\n definition: PoWorkflowDefinitionRef,\n overrides?: PoWorkflowAdmissionOverrides,\n ): Promise<PoWorkflowAdmission>;\n recover(\n attemptId: string,\n previousRun: PoWorkflowRunRef,\n options?: PoWorkflowRecoveryOptions,\n ): Promise<PoWorkflowAdmission>;\n /** Undefined is unknown or unauthorized, never proof that an executor did not start. */\n read(attemptId: string): Promise<PoWorkflowAdmission | undefined>;\n /** Session-owned registration. Duplicate (source session, coordinator) is rejected.\n * Disposal is generation-fenced. Remounting only reattaches; other sessions never supply this callback.\n * The engine persists the source launcher identity and never falls back to another session's policy.\n */\n registerPolicy(\n coordinator: string,\n beforeExecution: (request: PoWorkflowExecutionPolicyRequest) => Promise<PoWorkflowExecutionPolicyResult | void>,\n options: { readonly environmentKeys: readonly string[] },\n ): { dispose(): void };\n}\nexport interface PoWorkflowDefinitionSource {\n readonly source: string;\n /** An admitted, materialized absolute directory. Registration does not fetch it. */\n readonly directory: string;\n}\n\nexport interface PoWorkflowDefinitionSourceHandle {\n readonly source: string;\n readonly generation: string;\n dispose(): void;\n}\n\n/** Workspace-owned registry. Disposal withdraws only the handle's registration. */\nexport interface PoWorkflowDefinitionSourcesService {\n register(contribution: PoWorkflowDefinitionSource): PoWorkflowDefinitionSourceHandle;\n list(): readonly PoWorkflowDefinitionSource[];\n subscribe(listener: (sources: readonly PoWorkflowDefinitionSource[]) => void): () => void;\n}\n\nexport type PoWorkflowStage = 'running' | 'completed' | 'error';\nexport type PoWorkflowOutcome = 'success' | 'skipped' | 'failed' | 'interrupted';\nexport type PoWorkflowProgressState =\n | 'pending'\n | 'running'\n | 'completed'\n | 'skipped'\n | 'failed'\n | 'pause_requested'\n | 'paused'\n | 'resumed';\n\nexport interface PoWorkflowStepState {\n readonly name: string;\n readonly status: PoWorkflowProgressState;\n readonly ref?: { readonly kind: string; readonly id: string; readonly label?: string };\n readonly reason?: string;\n}\n\nexport interface PoWorkflowJobState {\n readonly name: string;\n readonly phase: 'pre' | 'job' | 'post';\n readonly status: PoWorkflowProgressState;\n readonly steps: readonly PoWorkflowStepState[];\n}\n\nexport interface PoWorkflowRunState {\n readonly run: PoWorkflowRunRef;\n readonly stage: PoWorkflowStage;\n readonly outcome?: PoWorkflowOutcome;\n /** From the same persisted snapshot as stage/outcome, never a newer cached operation. */\n readonly binding?: PoWorkflowPolicyBinding;\n readonly jobs: readonly PoWorkflowJobState[];\n readonly children?: readonly PoWorkflowChildrenStatus[];\n}\n\n/** The parsed definition actually used, not a re-read of mutable YAML on disk. */\nexport interface PoWorkflowConfig {\n readonly definition: PoWorkflowDefinitionRef;\n readonly revision: string;\n /** Engine-owned YAML vocabulary. Core does not import or implement the parser. */\n readonly workflow: Readonly<Record<string, unknown>>;\n}\n\n/** Owner-authoritative views, or an authorized projection for a launcher. */\nexport interface PoWorkflowRunsService {\n readConfig(runId: string): Promise<PoWorkflowConfig | undefined>;\n readState(runId: string): Promise<PoWorkflowRunState | undefined>;\n /** Replays the latest state, including terminal state, before subsequent updates. */\n subscribe(runId: string, listener: (state: PoWorkflowRunState) => void): () => void;\n}\n\nexport interface PoWorkflowChildInvocation {\n readonly invocationId: string;\n readonly job: string;\n readonly state: 'running' | 'completed' | 'failed' | 'interrupted';\n readonly sessionIds: readonly string[];\n readonly startedAt: string;\n readonly endedAt?: string;\n readonly error?: string;\n readonly usage?: import('./hubChannel').PoHubSessionUsage;\n}\n\nexport interface PoWorkflowCriterionState {\n readonly key: string;\n readonly description: string;\n readonly verifiedBy: string;\n readonly status: 'unchecked' | 'checked' | 'not-met';\n readonly feedback?: string;\n readonly invocationId?: string;\n}\n\nexport interface PoWorkflowChildrenStatus {\n readonly lead: string;\n readonly continuations: number;\n readonly maxContinuations: number;\n readonly criteria: readonly PoWorkflowCriterionState[];\n readonly invocations: readonly PoWorkflowChildInvocation[];\n}\n\n/** In-process, caller-bound capability. Never accepted from a tool or serialized. */\nexport interface PoWorkflowChildrenSession {\n readonly role: 'lead' | 'child' | 'consumer';\n spawnChild?(input: {\n job: string;\n prompt: string;\n inputs?: Record<string, string | number | boolean>;\n }): Promise<PoWorkflowChildInvocation>;\n childStatus?(input: { invocationId: string }): Promise<PoWorkflowChildInvocation>;\n children?(): Promise<PoWorkflowChildrenStatus>;\n reportCriteria?(input: {\n results: Record<string, { status: 'checked' | 'not-met'; feedback?: string }>;\n }): Promise<void>;\n /** Immutable pre-review snapshot selected by the runtime, not mutable paths. */\n reviewArtifacts?(): Promise<{ revision: string; artifacts: Readonly<Record<string, string>> }>;\n /** Returns exactly the accepted bytes, after a fresh authoritative ledger/revision check. */\n acceptedArtifacts?(input: {\n job: string;\n }): Promise<{ revision: string; artifacts: Readonly<Record<string, string>> }>;\n}\n\n/** Bound only in the trusted step-session bootstrap, before packages mount. */\nexport interface PoWorkflowContextService {\n readonly run: PoWorkflowRunRef;\n readonly job: string;\n readonly step: string;\n readonly attempt: number;\n readonly config: PoWorkflowConfig;\n readonly jobEnvironment: Readonly<Record<string, string>>;\n readonly stepEnvironment: Readonly<Record<string, string>>;\n readonly traceparent?: string;\n /** Absolute path of the workflow's instructions file, read in place of the discovered AGENTS.md files. */\n readonly instructions?: string;\n readonly state: PoWorkflowRunsService;\n readonly children?: PoWorkflowChildrenSession;\n}\n\n// These are in-process capabilities, not remote session-create fields or durable records.\ndeclare module '@deepseek-ai/cordis' {\n interface Context {\n 'po/workflow-launch': PoWorkflowLaunchService;\n 'po/workflow-definition-sources': PoWorkflowDefinitionSourcesService;\n 'po/workflow-runs': PoWorkflowRunsService;\n 'po/workflow-context': PoWorkflowContextService;\n 'po/workflow-admissions': PoWorkflowAdmissionsService;\n }\n}\n\nexport function readPoWorkflowLaunchService(context: Context): PoWorkflowLaunchService | undefined {\n return context.get(PO_WORKFLOW_LAUNCH_SERVICE) as PoWorkflowLaunchService | undefined;\n}\n\nexport function readPoWorkflowDefinitionSourcesService(\n context: Context,\n): PoWorkflowDefinitionSourcesService | undefined {\n return context.get(PO_WORKFLOW_DEFINITION_SOURCES_SERVICE) as PoWorkflowDefinitionSourcesService | undefined;\n}\n\nexport function readPoWorkflowRunsService(context: Context): PoWorkflowRunsService | undefined {\n return context.get(PO_WORKFLOW_RUNS_SERVICE) as PoWorkflowRunsService | undefined;\n}\n\nexport function readPoWorkflowContextService(context: Context): PoWorkflowContextService | undefined {\n return context.get(PO_WORKFLOW_CONTEXT_SERVICE) as PoWorkflowContextService | undefined;\n}\n\nexport function readPoWorkflowAdmissionsService(context: Context): PoWorkflowAdmissionsService | undefined {\n return context.get(PO_WORKFLOW_ADMISSIONS_SERVICE) as PoWorkflowAdmissionsService | undefined;\n}\n"],"mappings":";;AAGA,MAAa,6BAA6B;AAC1C,MAAa,yCAAyC;AACtD,MAAa,2BAA2B;AACxC,MAAa,8BAA8B;AAC3C,MAAa,iCAAiC;;AAG9C,MAAa,gCAAgCA,QAAAA,KAAK,OAChD;CACE,QAAQA,QAAAA,KAAK,OAAO,EAAE,WAAW,EAAE,CAAC;CACpC,KAAKA,QAAAA,KAAK,OAAO;EACf,WAAW;EACX,SAAS;CACX,CAAC;AACH,GACA,EAAE,sBAAsB,MAAM,CAChC;;AAIA,MAAa,yBAAyBA,QAAAA,KAAK,OACzC;CACE,OAAOA,QAAAA,KAAK,OAAO,EAAE,WAAW,EAAE,CAAC;CACnC,gBAAgBA,QAAAA,KAAK,OAAO,EAAE,WAAW,EAAE,CAAC;CAC5C,QAAQA,QAAAA,KAAK,OAAO,EAAE,WAAW,EAAE,CAAC;CACpC,mBAAmBA,QAAAA,KAAK,OAAO,EAAE,WAAW,EAAE,CAAC;AACjD,GACA,EAAE,sBAAsB,MAAM,CAChC;AA4QA,SAAgB,4BAA4B,SAAuD;CACjG,OAAO,QAAQ,IAAI,0BAA0B;AAC/C;AAEA,SAAgB,uCACd,SACgD;CAChD,OAAO,QAAQ,IAAI,sCAAsC;AAC3D;AAEA,SAAgB,0BAA0B,SAAqD;CAC7F,OAAO,QAAQ,IAAI,wBAAwB;AAC7C;AAEA,SAAgB,6BAA6B,SAAwD;CACnG,OAAO,QAAQ,IAAI,2BAA2B;AAChD;AAEA,SAAgB,gCAAgC,SAA2D;CACzG,OAAO,QAAQ,IAAI,8BAA8B;AACnD"}
1
+ {"version":3,"file":"workflow.cjs","names":["Type"],"sources":["../../../src/schemas/workflow.ts"],"sourcesContent":["import type { Context } from '@deepseek-ai/cordis';\nimport { type Static, Type } from 'typebox';\n\nexport const PO_WORKFLOW_LAUNCH_SERVICE = 'po/workflow-launch';\nexport const PO_WORKFLOW_DEFINITION_SOURCES_SERVICE = 'po/workflow-definition-sources';\nexport const PO_WORKFLOW_RUNS_SERVICE = 'po/workflow-runs';\nexport const PO_WORKFLOW_CONTEXT_SERVICE = 'po/workflow-context';\nexport const PO_WORKFLOW_ADMISSIONS_SERVICE = 'po/workflow-admissions';\n\n/** A source-relative definition, never an arbitrary host path. */\nexport const PoWorkflowDefinitionRefSchema = Type.Object(\n {\n source: Type.String({ minLength: 1 }),\n key: Type.String({\n minLength: 1,\n pattern: '^(?!/)(?!.*\\\\\\\\)(?!.*:)(?!.*[\\\\u0000-\\\\u001f\\\\u007f])(?!.*(?:^|/)\\\\.{1,2}(?:/|$))[^/]+(?:/[^/]+)*$',\n }),\n },\n { additionalProperties: false },\n);\nexport type PoWorkflowDefinitionRef = Static<typeof PoWorkflowDefinitionRefSchema>;\n\n/** Registration receipt. runId fences a runKey that can be reused later. */\nexport const PoWorkflowRunRefSchema = Type.Object(\n {\n runId: Type.String({ minLength: 1 }),\n ownerSessionId: Type.String({ minLength: 1 }),\n runKey: Type.String({ minLength: 1 }),\n registryWorkspace: Type.String({ minLength: 1 }),\n },\n { additionalProperties: false },\n);\nexport type PoWorkflowRunRef = Static<typeof PoWorkflowRunRefSchema>;\n\nexport interface PoWorkflowLaunchOverrides {\n readonly runner?: string;\n readonly command?: string;\n readonly choice?: string;\n readonly job?: string;\n readonly inputs?: Readonly<Record<string, string>>;\n readonly env?: Readonly<Record<string, string>>;\n readonly prompt?: string;\n readonly name?: string;\n}\n\n/** Session-owned. Rejection means no observable registration was accepted. */\nexport interface PoWorkflowLaunchService {\n launch(definition: PoWorkflowDefinitionRef, overrides?: PoWorkflowLaunchOverrides): Promise<PoWorkflowRunRef>;\n}\n\n/** Non-secret host association. Environment variables never establish this binding. */\nexport interface PoWorkflowPolicyAssociation {\n readonly coordinator: string;\n readonly key: string;\n}\n\n/** Independent of runId and executionGeneration, including continue-next resumes. */\nexport interface PoWorkflowOperationEpoch {\n readonly admissionId: string;\n readonly operationId: string;\n}\n\nexport interface PoWorkflowPolicyBinding {\n readonly association: PoWorkflowPolicyAssociation;\n readonly epoch: PoWorkflowOperationEpoch;\n}\n\nexport interface PoWorkflowExecutionPolicyRequest {\n readonly binding: PoWorkflowPolicyBinding;\n readonly reason: 'launch' | 'recover' | 'startup' | 'resume';\n readonly ownerSessionId: string;\n readonly launcherSessionId: string;\n /** The engine supplies the actual persisted run after createRun, including startup and recovery. */\n readonly run?: PoWorkflowRunRef;\n readonly previousRun?: PoWorkflowRunRef;\n /** Ephemeral host lifetime, never persisted. */\n readonly signal: AbortSignal;\n}\n\nexport interface PoWorkflowExecutionPolicyResult {\n /** Trusted non-secret context only. PO_* and WORKFLOW_* engine identity keys are forbidden. */\n readonly environment?: Readonly<Record<string, string>>;\n}\n\n/**\n * How a bound generation ended. `abandoned` is a person giving up a failed run: it is never recovered.\n * A coordinator releases what it holds for success, skipped and abandoned, and keeps it for failed and\n * interrupted so a recovery of the run reuses it.\n */\nexport type PoWorkflowSettlementOutcome = PoWorkflowOutcome | 'abandoned';\n\nexport interface PoWorkflowSettlementRequest {\n readonly binding: PoWorkflowPolicyBinding;\n readonly run: PoWorkflowRunRef;\n readonly outcome: PoWorkflowSettlementOutcome;\n readonly failedJob?: string;\n readonly ownerSessionId: string;\n readonly launcherSessionId: string;\n /** Ephemeral host lifetime, never persisted. */\n readonly signal: AbortSignal;\n}\n\nexport interface PoWorkflowPolicyOptions {\n readonly environmentKeys: readonly string[];\n /**\n * Called once a bound generation is terminal, and again with `abandoned` if a person abandons it.\n * Delivery is durable: the engine retries until this resolves, including after a restart or a\n * remount, so it must be idempotent. Throwing leaves the settlement pending.\n */\n readonly settle?: (request: PoWorkflowSettlementRequest) => Promise<void>;\n}\n\nexport interface PoWorkflowAdmissionOverrides extends PoWorkflowLaunchOverrides {\n readonly policy?: PoWorkflowPolicyAssociation;\n}\n\nexport interface PoWorkflowRecoveryOptions {\n readonly runner?: string;\n}\n\nexport type PoWorkflowAdmissionRequest =\n | {\n readonly kind: 'launch';\n readonly definition: PoWorkflowDefinitionRef;\n readonly overrides?: PoWorkflowAdmissionOverrides;\n }\n | { readonly kind: 'recover'; readonly previousRun: PoWorkflowRunRef; readonly options?: PoWorkflowRecoveryOptions };\n\nexport interface PoWorkflowValidationReport {\n readonly errors: readonly { readonly code: string; readonly message: string; readonly location?: string }[];\n readonly warnings: readonly { readonly code: string; readonly message: string; readonly location?: string }[];\n readonly deferred: readonly { readonly location: string; readonly reference: string; readonly reason: string }[];\n}\n\nexport type PoWorkflowAdmission =\n | {\n readonly attemptId: string;\n readonly admissionId: string;\n readonly state: 'pending';\n readonly binding?: PoWorkflowPolicyBinding;\n }\n | {\n readonly attemptId: string;\n readonly admissionId: string;\n readonly state: 'rejected';\n readonly errors: readonly string[];\n }\n | {\n readonly attemptId: string;\n readonly admissionId: string;\n readonly state: 'registered';\n readonly run: PoWorkflowRunRef;\n readonly binding?: PoWorkflowPolicyBinding;\n };\n\n/** Trusted, session-bound opt-in capability, never exposed as a tool or code-mode namespace. */\nexport interface PoWorkflowAdmissionsService {\n validate(request: PoWorkflowAdmissionRequest): Promise<PoWorkflowValidationReport>;\n launch(\n attemptId: string,\n definition: PoWorkflowDefinitionRef,\n overrides?: PoWorkflowAdmissionOverrides,\n ): Promise<PoWorkflowAdmission>;\n recover(\n attemptId: string,\n previousRun: PoWorkflowRunRef,\n options?: PoWorkflowRecoveryOptions,\n ): Promise<PoWorkflowAdmission>;\n /** Undefined is unknown or unauthorized, never proof that an executor did not start. */\n read(attemptId: string): Promise<PoWorkflowAdmission | undefined>;\n /** Session-owned registration. Duplicate (source session, coordinator) is rejected.\n * Disposal is generation-fenced. Remounting only reattaches; other sessions never supply this callback.\n * The engine persists the source launcher identity and never falls back to another session's policy.\n */\n registerPolicy(\n coordinator: string,\n beforeExecution: (request: PoWorkflowExecutionPolicyRequest) => Promise<PoWorkflowExecutionPolicyResult | void>,\n options: PoWorkflowPolicyOptions,\n ): { dispose(): void };\n}\nexport interface PoWorkflowDefinitionSource {\n readonly source: string;\n /** An admitted, materialized absolute directory. Registration does not fetch it. */\n readonly directory: string;\n}\n\nexport interface PoWorkflowDefinitionSourceHandle {\n readonly source: string;\n readonly generation: string;\n dispose(): void;\n}\n\n/** Workspace-owned registry. Disposal withdraws only the handle's registration. */\nexport interface PoWorkflowDefinitionSourcesService {\n register(contribution: PoWorkflowDefinitionSource): PoWorkflowDefinitionSourceHandle;\n list(): readonly PoWorkflowDefinitionSource[];\n subscribe(listener: (sources: readonly PoWorkflowDefinitionSource[]) => void): () => void;\n}\n\nexport type PoWorkflowStage = 'running' | 'completed' | 'error';\nexport type PoWorkflowOutcome = 'success' | 'skipped' | 'failed' | 'interrupted';\nexport type PoWorkflowProgressState =\n | 'pending'\n | 'running'\n | 'completed'\n | 'skipped'\n | 'failed'\n | 'pause_requested'\n | 'paused'\n | 'resumed';\n\nexport interface PoWorkflowStepState {\n readonly name: string;\n readonly status: PoWorkflowProgressState;\n readonly ref?: { readonly kind: string; readonly id: string; readonly label?: string };\n readonly reason?: string;\n}\n\nexport interface PoWorkflowJobState {\n readonly name: string;\n readonly phase: 'pre' | 'job' | 'post';\n readonly status: PoWorkflowProgressState;\n readonly steps: readonly PoWorkflowStepState[];\n}\n\nexport interface PoWorkflowRunState {\n readonly run: PoWorkflowRunRef;\n readonly stage: PoWorkflowStage;\n readonly outcome?: PoWorkflowOutcome;\n /** From the same persisted snapshot as stage/outcome, never a newer cached operation. */\n readonly binding?: PoWorkflowPolicyBinding;\n readonly jobs: readonly PoWorkflowJobState[];\n readonly children?: readonly PoWorkflowChildrenStatus[];\n}\n\n/** The parsed definition actually used, not a re-read of mutable YAML on disk. */\nexport interface PoWorkflowConfig {\n readonly definition: PoWorkflowDefinitionRef;\n readonly revision: string;\n /** Engine-owned YAML vocabulary. Core does not import or implement the parser. */\n readonly workflow: Readonly<Record<string, unknown>>;\n}\n\n/** Owner-authoritative views, or an authorized projection for a launcher. */\nexport interface PoWorkflowRunsService {\n readConfig(runId: string): Promise<PoWorkflowConfig | undefined>;\n readState(runId: string): Promise<PoWorkflowRunState | undefined>;\n /** Replays the latest state, including terminal state, before subsequent updates. */\n subscribe(runId: string, listener: (state: PoWorkflowRunState) => void): () => void;\n}\n\nexport interface PoWorkflowChildInvocation {\n readonly invocationId: string;\n readonly job: string;\n readonly state: 'running' | 'completed' | 'failed' | 'interrupted';\n readonly sessionIds: readonly string[];\n readonly startedAt: string;\n readonly endedAt?: string;\n readonly error?: string;\n readonly usage?: import('./hubChannel').PoHubSessionUsage;\n}\n\nexport interface PoWorkflowCriterionState {\n readonly key: string;\n readonly description: string;\n readonly verifiedBy: string;\n readonly status: 'unchecked' | 'checked' | 'not-met';\n readonly feedback?: string;\n readonly invocationId?: string;\n}\n\nexport interface PoWorkflowChildrenStatus {\n readonly lead: string;\n readonly continuations: number;\n readonly maxContinuations: number;\n readonly criteria: readonly PoWorkflowCriterionState[];\n readonly invocations: readonly PoWorkflowChildInvocation[];\n}\n\n/** In-process, caller-bound capability. Never accepted from a tool or serialized. */\nexport interface PoWorkflowChildrenSession {\n readonly role: 'lead' | 'child' | 'consumer';\n spawnChild?(input: {\n job: string;\n prompt: string;\n inputs?: Record<string, string | number | boolean>;\n }): Promise<PoWorkflowChildInvocation>;\n childStatus?(input: { invocationId: string }): Promise<PoWorkflowChildInvocation>;\n children?(): Promise<PoWorkflowChildrenStatus>;\n reportCriteria?(input: {\n results: Record<string, { status: 'checked' | 'not-met'; feedback?: string }>;\n }): Promise<void>;\n /** Immutable pre-review snapshot selected by the runtime, not mutable paths. */\n reviewArtifacts?(): Promise<{ revision: string; artifacts: Readonly<Record<string, string>> }>;\n /** Returns exactly the accepted bytes, after a fresh authoritative ledger/revision check. */\n acceptedArtifacts?(input: {\n job: string;\n }): Promise<{ revision: string; artifacts: Readonly<Record<string, string>> }>;\n}\n\n/** Bound only in the trusted step-session bootstrap, before packages mount. */\nexport interface PoWorkflowContextService {\n readonly run: PoWorkflowRunRef;\n readonly job: string;\n readonly step: string;\n readonly attempt: number;\n readonly config: PoWorkflowConfig;\n readonly jobEnvironment: Readonly<Record<string, string>>;\n readonly stepEnvironment: Readonly<Record<string, string>>;\n readonly traceparent?: string;\n /** Absolute path of the workflow's instructions file, read in place of the discovered AGENTS.md files. */\n readonly instructions?: string;\n readonly state: PoWorkflowRunsService;\n readonly children?: PoWorkflowChildrenSession;\n}\n\n// These are in-process capabilities, not remote session-create fields or durable records.\ndeclare module '@deepseek-ai/cordis' {\n interface Context {\n 'po/workflow-launch': PoWorkflowLaunchService;\n 'po/workflow-definition-sources': PoWorkflowDefinitionSourcesService;\n 'po/workflow-runs': PoWorkflowRunsService;\n 'po/workflow-context': PoWorkflowContextService;\n 'po/workflow-admissions': PoWorkflowAdmissionsService;\n }\n}\n\nexport function readPoWorkflowLaunchService(context: Context): PoWorkflowLaunchService | undefined {\n return context.get(PO_WORKFLOW_LAUNCH_SERVICE) as PoWorkflowLaunchService | undefined;\n}\n\nexport function readPoWorkflowDefinitionSourcesService(\n context: Context,\n): PoWorkflowDefinitionSourcesService | undefined {\n return context.get(PO_WORKFLOW_DEFINITION_SOURCES_SERVICE) as PoWorkflowDefinitionSourcesService | undefined;\n}\n\nexport function readPoWorkflowRunsService(context: Context): PoWorkflowRunsService | undefined {\n return context.get(PO_WORKFLOW_RUNS_SERVICE) as PoWorkflowRunsService | undefined;\n}\n\nexport function readPoWorkflowContextService(context: Context): PoWorkflowContextService | undefined {\n return context.get(PO_WORKFLOW_CONTEXT_SERVICE) as PoWorkflowContextService | undefined;\n}\n\nexport function readPoWorkflowAdmissionsService(context: Context): PoWorkflowAdmissionsService | undefined {\n return context.get(PO_WORKFLOW_ADMISSIONS_SERVICE) as PoWorkflowAdmissionsService | undefined;\n}\n"],"mappings":";;AAGA,MAAa,6BAA6B;AAC1C,MAAa,yCAAyC;AACtD,MAAa,2BAA2B;AACxC,MAAa,8BAA8B;AAC3C,MAAa,iCAAiC;;AAG9C,MAAa,gCAAgCA,QAAAA,KAAK,OAChD;CACE,QAAQA,QAAAA,KAAK,OAAO,EAAE,WAAW,EAAE,CAAC;CACpC,KAAKA,QAAAA,KAAK,OAAO;EACf,WAAW;EACX,SAAS;CACX,CAAC;AACH,GACA,EAAE,sBAAsB,MAAM,CAChC;;AAIA,MAAa,yBAAyBA,QAAAA,KAAK,OACzC;CACE,OAAOA,QAAAA,KAAK,OAAO,EAAE,WAAW,EAAE,CAAC;CACnC,gBAAgBA,QAAAA,KAAK,OAAO,EAAE,WAAW,EAAE,CAAC;CAC5C,QAAQA,QAAAA,KAAK,OAAO,EAAE,WAAW,EAAE,CAAC;CACpC,mBAAmBA,QAAAA,KAAK,OAAO,EAAE,WAAW,EAAE,CAAC;AACjD,GACA,EAAE,sBAAsB,MAAM,CAChC;AAwSA,SAAgB,4BAA4B,SAAuD;CACjG,OAAO,QAAQ,IAAI,0BAA0B;AAC/C;AAEA,SAAgB,uCACd,SACgD;CAChD,OAAO,QAAQ,IAAI,sCAAsC;AAC3D;AAEA,SAAgB,0BAA0B,SAAqD;CAC7F,OAAO,QAAQ,IAAI,wBAAwB;AAC7C;AAEA,SAAgB,6BAA6B,SAAwD;CACnG,OAAO,QAAQ,IAAI,2BAA2B;AAChD;AAEA,SAAgB,gCAAgC,SAA2D;CACzG,OAAO,QAAQ,IAAI,8BAA8B;AACnD"}
@@ -64,6 +64,31 @@ export interface PoWorkflowExecutionPolicyResult {
64
64
  /** Trusted non-secret context only. PO_* and WORKFLOW_* engine identity keys are forbidden. */
65
65
  readonly environment?: Readonly<Record<string, string>>;
66
66
  }
67
+ /**
68
+ * How a bound generation ended. `abandoned` is a person giving up a failed run: it is never recovered.
69
+ * A coordinator releases what it holds for success, skipped and abandoned, and keeps it for failed and
70
+ * interrupted so a recovery of the run reuses it.
71
+ */
72
+ export type PoWorkflowSettlementOutcome = PoWorkflowOutcome | 'abandoned';
73
+ export interface PoWorkflowSettlementRequest {
74
+ readonly binding: PoWorkflowPolicyBinding;
75
+ readonly run: PoWorkflowRunRef;
76
+ readonly outcome: PoWorkflowSettlementOutcome;
77
+ readonly failedJob?: string;
78
+ readonly ownerSessionId: string;
79
+ readonly launcherSessionId: string;
80
+ /** Ephemeral host lifetime, never persisted. */
81
+ readonly signal: AbortSignal;
82
+ }
83
+ export interface PoWorkflowPolicyOptions {
84
+ readonly environmentKeys: readonly string[];
85
+ /**
86
+ * Called once a bound generation is terminal, and again with `abandoned` if a person abandons it.
87
+ * Delivery is durable: the engine retries until this resolves, including after a restart or a
88
+ * remount, so it must be idempotent. Throwing leaves the settlement pending.
89
+ */
90
+ readonly settle?: (request: PoWorkflowSettlementRequest) => Promise<void>;
91
+ }
67
92
  export interface PoWorkflowAdmissionOverrides extends PoWorkflowLaunchOverrides {
68
93
  readonly policy?: PoWorkflowPolicyAssociation;
69
94
  }
@@ -124,9 +149,7 @@ export interface PoWorkflowAdmissionsService {
124
149
  * Disposal is generation-fenced. Remounting only reattaches; other sessions never supply this callback.
125
150
  * The engine persists the source launcher identity and never falls back to another session's policy.
126
151
  */
127
- registerPolicy(coordinator: string, beforeExecution: (request: PoWorkflowExecutionPolicyRequest) => Promise<PoWorkflowExecutionPolicyResult | void>, options: {
128
- readonly environmentKeys: readonly string[];
129
- }): {
152
+ registerPolicy(coordinator: string, beforeExecution: (request: PoWorkflowExecutionPolicyRequest) => Promise<PoWorkflowExecutionPolicyResult | void>, options: PoWorkflowPolicyOptions): {
130
153
  dispose(): void;
131
154
  };
132
155
  }
@@ -1 +1 @@
1
- {"version":3,"file":"workflow.d.mts","names":["_$__hubChannel0.PoHubSessionUsage"],"sources":["../../../src/schemas/workflow.ts"],"mappings":";;;;qBAGa;qBACA;qBACA;qBACA;qBACA;;qBAGA,+BAA6B,KAAA;;;;YAU9B,0BAA0B,cAAc;;qBAGvC,wBAAsB,KAAA;;;;;;YASvB,mBAAmB,cAAc;iBAE5B;WACN;WACA;WACA;WACA;WACA,SAAS,SAAS;WAClB,MAAM,SAAS;WACf;WACA;;;iBAIM;EACf,OAAO,YAAY,yBAAyB,YAAY,4BAA4B,QAAQ;;;iBAI7E;WACN;WACA;;;iBAIM;WACN;WACA;;iBAGM;WACN,aAAa;WACb,OAAO;;iBAGD;WACN,SAAS;WACT;WACA;WACA;;WAEA,MAAM;WACN,cAAc;;WAEd,QAAQ;;iBAGF;;WAEN,cAAc,SAAS;;iBAGjB,qCAAqC;WAC3C,SAAS;;iBAGH;WACN;;YAGC;WAEG;WACA,YAAY;WACZ,YAAY;;WAEZ;WAA0B,aAAa;WAA2B,UAAU;;iBAE1E;WACN;aAA4B;aAAuB;aAA0B;;WAC7E;aAA8B;aAAuB;aAA0B;;WAC/E;aAA8B;aAA2B;aAA4B;;;YAGpF;WAEG;WACA;WACA;WACA,UAAU;;WAGV;WACA;WACA;WACA;;WAGA;WACA;WACA;WACA,KAAK;WACL,UAAU;;;iBAIR;EACf,SAAS,SAAS,6BAA6B,QAAQ;EACvD,OACE,mBACA,YAAY,yBACZ,YAAY,+BACX,QAAQ;EACX,QACE,mBACA,aAAa,kBACb,UAAU,4BACT,QAAQ;;EAEX,KAAK,oBAAoB,QAAQ;;;;;EAKjC,eACE,qBACA,kBAAkB,SAAS,qCAAqC,QAAQ,yCACxE;aAAoB;;IACjB;;;iBAEU;WACN;;WAEA;;iBAGM;WACN;WACA;EACT;;;iBAIe;EACf,SAAS,cAAc,6BAA6B;EACpD,iBAAiB;EACjB,UAAU,WAAW,kBAAkB;;YAG7B;YACA;YACA;iBAUK;WACN;WACA,QAAQ;WACR;aAAiB;aAAuB;aAAqB;;WAC7D;;iBAGM;WACN;WACA;WACA,QAAQ;WACR,gBAAgB;;iBAGV;WACN,KAAK;WACL,OAAO;WACP,UAAU;;WAEV,UAAU;WACV,eAAe;WACf,oBAAoB;;;iBAId;WACN,YAAY;WACZ;;WAEA,UAAU,SAAS;;;iBAIb;EACf,WAAW,gBAAgB,QAAQ;EACnC,UAAU,gBAAgB,QAAQ;;EAElC,UAAU,eAAe,WAAW,OAAO;;iBAG5B;WACN;WACA;WACA;WACA;WACA;WACA;WACA;WACA,QARMA;;iBAWA;WACN;WACA;WACA;WACA;WACA;WACA;;iBAGM;WACN;WACA;WACA;WACA,mBAAmB;WACnB,sBAAsB;;;iBAIhB;WACN;EACT,YAAY;IACV;IACA;IACA,SAAS;MACP,QAAQ;EACZ,aAAa;IAAS;MAAyB,QAAQ;EACvD,aAAa,QAAQ;EACrB,gBAAgB;IACd,SAAS;MAAiB;MAA+B;;MACvD;;EAEJ,oBAAoB;IAAU;IAAkB,WAAW,SAAS;;;EAEpE,mBAAmB;IACjB;MACE;IAAU;IAAkB,WAAW,SAAS;;;;iBAIrC;WACN,KAAK;WACL;WACA;WACA;WACA,QAAQ;WACR,gBAAgB,SAAS;WACzB,iBAAiB,SAAS;WAC1B;;WAEA;WACA,OAAO;WACP,WAAW;;;YAKV;IACR,sBAAsB;IACtB,kCAAkC;IAClC,oBAAoB;IACpB,uBAAuB;IACvB,0BAA0B;;;wBAId,4BAA4B,SAAS,UAAU;wBAI/C,uCACd,SAAS,UACR;wBAIa,0BAA0B,SAAS,UAAU;wBAI7C,6BAA6B,SAAS,UAAU;wBAIhD,gCAAgC,SAAS,UAAU"}
1
+ {"version":3,"file":"workflow.d.mts","names":["_$__hubChannel0.PoHubSessionUsage"],"sources":["../../../src/schemas/workflow.ts"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"workflow.mjs","names":[],"sources":["../../../src/schemas/workflow.ts"],"sourcesContent":["import type { Context } from '@deepseek-ai/cordis';\nimport { type Static, Type } from 'typebox';\n\nexport const PO_WORKFLOW_LAUNCH_SERVICE = 'po/workflow-launch';\nexport const PO_WORKFLOW_DEFINITION_SOURCES_SERVICE = 'po/workflow-definition-sources';\nexport const PO_WORKFLOW_RUNS_SERVICE = 'po/workflow-runs';\nexport const PO_WORKFLOW_CONTEXT_SERVICE = 'po/workflow-context';\nexport const PO_WORKFLOW_ADMISSIONS_SERVICE = 'po/workflow-admissions';\n\n/** A source-relative definition, never an arbitrary host path. */\nexport const PoWorkflowDefinitionRefSchema = Type.Object(\n {\n source: Type.String({ minLength: 1 }),\n key: Type.String({\n minLength: 1,\n pattern: '^(?!/)(?!.*\\\\\\\\)(?!.*:)(?!.*[\\\\u0000-\\\\u001f\\\\u007f])(?!.*(?:^|/)\\\\.{1,2}(?:/|$))[^/]+(?:/[^/]+)*$',\n }),\n },\n { additionalProperties: false },\n);\nexport type PoWorkflowDefinitionRef = Static<typeof PoWorkflowDefinitionRefSchema>;\n\n/** Registration receipt. runId fences a runKey that can be reused later. */\nexport const PoWorkflowRunRefSchema = Type.Object(\n {\n runId: Type.String({ minLength: 1 }),\n ownerSessionId: Type.String({ minLength: 1 }),\n runKey: Type.String({ minLength: 1 }),\n registryWorkspace: Type.String({ minLength: 1 }),\n },\n { additionalProperties: false },\n);\nexport type PoWorkflowRunRef = Static<typeof PoWorkflowRunRefSchema>;\n\nexport interface PoWorkflowLaunchOverrides {\n readonly runner?: string;\n readonly command?: string;\n readonly choice?: string;\n readonly job?: string;\n readonly inputs?: Readonly<Record<string, string>>;\n readonly env?: Readonly<Record<string, string>>;\n readonly prompt?: string;\n readonly name?: string;\n}\n\n/** Session-owned. Rejection means no observable registration was accepted. */\nexport interface PoWorkflowLaunchService {\n launch(definition: PoWorkflowDefinitionRef, overrides?: PoWorkflowLaunchOverrides): Promise<PoWorkflowRunRef>;\n}\n\n/** Non-secret host association. Environment variables never establish this binding. */\nexport interface PoWorkflowPolicyAssociation {\n readonly coordinator: string;\n readonly key: string;\n}\n\n/** Independent of runId and executionGeneration, including continue-next resumes. */\nexport interface PoWorkflowOperationEpoch {\n readonly admissionId: string;\n readonly operationId: string;\n}\n\nexport interface PoWorkflowPolicyBinding {\n readonly association: PoWorkflowPolicyAssociation;\n readonly epoch: PoWorkflowOperationEpoch;\n}\n\nexport interface PoWorkflowExecutionPolicyRequest {\n readonly binding: PoWorkflowPolicyBinding;\n readonly reason: 'launch' | 'recover' | 'startup' | 'resume';\n readonly ownerSessionId: string;\n readonly launcherSessionId: string;\n /** The engine supplies the actual persisted run after createRun, including startup and recovery. */\n readonly run?: PoWorkflowRunRef;\n readonly previousRun?: PoWorkflowRunRef;\n /** Ephemeral host lifetime, never persisted. */\n readonly signal: AbortSignal;\n}\n\nexport interface PoWorkflowExecutionPolicyResult {\n /** Trusted non-secret context only. PO_* and WORKFLOW_* engine identity keys are forbidden. */\n readonly environment?: Readonly<Record<string, string>>;\n}\n\nexport interface PoWorkflowAdmissionOverrides extends PoWorkflowLaunchOverrides {\n readonly policy?: PoWorkflowPolicyAssociation;\n}\n\nexport interface PoWorkflowRecoveryOptions {\n readonly runner?: string;\n}\n\nexport type PoWorkflowAdmissionRequest =\n | {\n readonly kind: 'launch';\n readonly definition: PoWorkflowDefinitionRef;\n readonly overrides?: PoWorkflowAdmissionOverrides;\n }\n | { readonly kind: 'recover'; readonly previousRun: PoWorkflowRunRef; readonly options?: PoWorkflowRecoveryOptions };\n\nexport interface PoWorkflowValidationReport {\n readonly errors: readonly { readonly code: string; readonly message: string; readonly location?: string }[];\n readonly warnings: readonly { readonly code: string; readonly message: string; readonly location?: string }[];\n readonly deferred: readonly { readonly location: string; readonly reference: string; readonly reason: string }[];\n}\n\nexport type PoWorkflowAdmission =\n | {\n readonly attemptId: string;\n readonly admissionId: string;\n readonly state: 'pending';\n readonly binding?: PoWorkflowPolicyBinding;\n }\n | {\n readonly attemptId: string;\n readonly admissionId: string;\n readonly state: 'rejected';\n readonly errors: readonly string[];\n }\n | {\n readonly attemptId: string;\n readonly admissionId: string;\n readonly state: 'registered';\n readonly run: PoWorkflowRunRef;\n readonly binding?: PoWorkflowPolicyBinding;\n };\n\n/** Trusted, session-bound opt-in capability, never exposed as a tool or code-mode namespace. */\nexport interface PoWorkflowAdmissionsService {\n validate(request: PoWorkflowAdmissionRequest): Promise<PoWorkflowValidationReport>;\n launch(\n attemptId: string,\n definition: PoWorkflowDefinitionRef,\n overrides?: PoWorkflowAdmissionOverrides,\n ): Promise<PoWorkflowAdmission>;\n recover(\n attemptId: string,\n previousRun: PoWorkflowRunRef,\n options?: PoWorkflowRecoveryOptions,\n ): Promise<PoWorkflowAdmission>;\n /** Undefined is unknown or unauthorized, never proof that an executor did not start. */\n read(attemptId: string): Promise<PoWorkflowAdmission | undefined>;\n /** Session-owned registration. Duplicate (source session, coordinator) is rejected.\n * Disposal is generation-fenced. Remounting only reattaches; other sessions never supply this callback.\n * The engine persists the source launcher identity and never falls back to another session's policy.\n */\n registerPolicy(\n coordinator: string,\n beforeExecution: (request: PoWorkflowExecutionPolicyRequest) => Promise<PoWorkflowExecutionPolicyResult | void>,\n options: { readonly environmentKeys: readonly string[] },\n ): { dispose(): void };\n}\nexport interface PoWorkflowDefinitionSource {\n readonly source: string;\n /** An admitted, materialized absolute directory. Registration does not fetch it. */\n readonly directory: string;\n}\n\nexport interface PoWorkflowDefinitionSourceHandle {\n readonly source: string;\n readonly generation: string;\n dispose(): void;\n}\n\n/** Workspace-owned registry. Disposal withdraws only the handle's registration. */\nexport interface PoWorkflowDefinitionSourcesService {\n register(contribution: PoWorkflowDefinitionSource): PoWorkflowDefinitionSourceHandle;\n list(): readonly PoWorkflowDefinitionSource[];\n subscribe(listener: (sources: readonly PoWorkflowDefinitionSource[]) => void): () => void;\n}\n\nexport type PoWorkflowStage = 'running' | 'completed' | 'error';\nexport type PoWorkflowOutcome = 'success' | 'skipped' | 'failed' | 'interrupted';\nexport type PoWorkflowProgressState =\n | 'pending'\n | 'running'\n | 'completed'\n | 'skipped'\n | 'failed'\n | 'pause_requested'\n | 'paused'\n | 'resumed';\n\nexport interface PoWorkflowStepState {\n readonly name: string;\n readonly status: PoWorkflowProgressState;\n readonly ref?: { readonly kind: string; readonly id: string; readonly label?: string };\n readonly reason?: string;\n}\n\nexport interface PoWorkflowJobState {\n readonly name: string;\n readonly phase: 'pre' | 'job' | 'post';\n readonly status: PoWorkflowProgressState;\n readonly steps: readonly PoWorkflowStepState[];\n}\n\nexport interface PoWorkflowRunState {\n readonly run: PoWorkflowRunRef;\n readonly stage: PoWorkflowStage;\n readonly outcome?: PoWorkflowOutcome;\n /** From the same persisted snapshot as stage/outcome, never a newer cached operation. */\n readonly binding?: PoWorkflowPolicyBinding;\n readonly jobs: readonly PoWorkflowJobState[];\n readonly children?: readonly PoWorkflowChildrenStatus[];\n}\n\n/** The parsed definition actually used, not a re-read of mutable YAML on disk. */\nexport interface PoWorkflowConfig {\n readonly definition: PoWorkflowDefinitionRef;\n readonly revision: string;\n /** Engine-owned YAML vocabulary. Core does not import or implement the parser. */\n readonly workflow: Readonly<Record<string, unknown>>;\n}\n\n/** Owner-authoritative views, or an authorized projection for a launcher. */\nexport interface PoWorkflowRunsService {\n readConfig(runId: string): Promise<PoWorkflowConfig | undefined>;\n readState(runId: string): Promise<PoWorkflowRunState | undefined>;\n /** Replays the latest state, including terminal state, before subsequent updates. */\n subscribe(runId: string, listener: (state: PoWorkflowRunState) => void): () => void;\n}\n\nexport interface PoWorkflowChildInvocation {\n readonly invocationId: string;\n readonly job: string;\n readonly state: 'running' | 'completed' | 'failed' | 'interrupted';\n readonly sessionIds: readonly string[];\n readonly startedAt: string;\n readonly endedAt?: string;\n readonly error?: string;\n readonly usage?: import('./hubChannel').PoHubSessionUsage;\n}\n\nexport interface PoWorkflowCriterionState {\n readonly key: string;\n readonly description: string;\n readonly verifiedBy: string;\n readonly status: 'unchecked' | 'checked' | 'not-met';\n readonly feedback?: string;\n readonly invocationId?: string;\n}\n\nexport interface PoWorkflowChildrenStatus {\n readonly lead: string;\n readonly continuations: number;\n readonly maxContinuations: number;\n readonly criteria: readonly PoWorkflowCriterionState[];\n readonly invocations: readonly PoWorkflowChildInvocation[];\n}\n\n/** In-process, caller-bound capability. Never accepted from a tool or serialized. */\nexport interface PoWorkflowChildrenSession {\n readonly role: 'lead' | 'child' | 'consumer';\n spawnChild?(input: {\n job: string;\n prompt: string;\n inputs?: Record<string, string | number | boolean>;\n }): Promise<PoWorkflowChildInvocation>;\n childStatus?(input: { invocationId: string }): Promise<PoWorkflowChildInvocation>;\n children?(): Promise<PoWorkflowChildrenStatus>;\n reportCriteria?(input: {\n results: Record<string, { status: 'checked' | 'not-met'; feedback?: string }>;\n }): Promise<void>;\n /** Immutable pre-review snapshot selected by the runtime, not mutable paths. */\n reviewArtifacts?(): Promise<{ revision: string; artifacts: Readonly<Record<string, string>> }>;\n /** Returns exactly the accepted bytes, after a fresh authoritative ledger/revision check. */\n acceptedArtifacts?(input: {\n job: string;\n }): Promise<{ revision: string; artifacts: Readonly<Record<string, string>> }>;\n}\n\n/** Bound only in the trusted step-session bootstrap, before packages mount. */\nexport interface PoWorkflowContextService {\n readonly run: PoWorkflowRunRef;\n readonly job: string;\n readonly step: string;\n readonly attempt: number;\n readonly config: PoWorkflowConfig;\n readonly jobEnvironment: Readonly<Record<string, string>>;\n readonly stepEnvironment: Readonly<Record<string, string>>;\n readonly traceparent?: string;\n /** Absolute path of the workflow's instructions file, read in place of the discovered AGENTS.md files. */\n readonly instructions?: string;\n readonly state: PoWorkflowRunsService;\n readonly children?: PoWorkflowChildrenSession;\n}\n\n// These are in-process capabilities, not remote session-create fields or durable records.\ndeclare module '@deepseek-ai/cordis' {\n interface Context {\n 'po/workflow-launch': PoWorkflowLaunchService;\n 'po/workflow-definition-sources': PoWorkflowDefinitionSourcesService;\n 'po/workflow-runs': PoWorkflowRunsService;\n 'po/workflow-context': PoWorkflowContextService;\n 'po/workflow-admissions': PoWorkflowAdmissionsService;\n }\n}\n\nexport function readPoWorkflowLaunchService(context: Context): PoWorkflowLaunchService | undefined {\n return context.get(PO_WORKFLOW_LAUNCH_SERVICE) as PoWorkflowLaunchService | undefined;\n}\n\nexport function readPoWorkflowDefinitionSourcesService(\n context: Context,\n): PoWorkflowDefinitionSourcesService | undefined {\n return context.get(PO_WORKFLOW_DEFINITION_SOURCES_SERVICE) as PoWorkflowDefinitionSourcesService | undefined;\n}\n\nexport function readPoWorkflowRunsService(context: Context): PoWorkflowRunsService | undefined {\n return context.get(PO_WORKFLOW_RUNS_SERVICE) as PoWorkflowRunsService | undefined;\n}\n\nexport function readPoWorkflowContextService(context: Context): PoWorkflowContextService | undefined {\n return context.get(PO_WORKFLOW_CONTEXT_SERVICE) as PoWorkflowContextService | undefined;\n}\n\nexport function readPoWorkflowAdmissionsService(context: Context): PoWorkflowAdmissionsService | undefined {\n return context.get(PO_WORKFLOW_ADMISSIONS_SERVICE) as PoWorkflowAdmissionsService | undefined;\n}\n"],"mappings":";;AAGA,MAAa,6BAA6B;AAC1C,MAAa,yCAAyC;AACtD,MAAa,2BAA2B;AACxC,MAAa,8BAA8B;AAC3C,MAAa,iCAAiC;;AAG9C,MAAa,gCAAgC,KAAK,OAChD;CACE,QAAQ,KAAK,OAAO,EAAE,WAAW,EAAE,CAAC;CACpC,KAAK,KAAK,OAAO;EACf,WAAW;EACX,SAAS;CACX,CAAC;AACH,GACA,EAAE,sBAAsB,MAAM,CAChC;;AAIA,MAAa,yBAAyB,KAAK,OACzC;CACE,OAAO,KAAK,OAAO,EAAE,WAAW,EAAE,CAAC;CACnC,gBAAgB,KAAK,OAAO,EAAE,WAAW,EAAE,CAAC;CAC5C,QAAQ,KAAK,OAAO,EAAE,WAAW,EAAE,CAAC;CACpC,mBAAmB,KAAK,OAAO,EAAE,WAAW,EAAE,CAAC;AACjD,GACA,EAAE,sBAAsB,MAAM,CAChC;AA4QA,SAAgB,4BAA4B,SAAuD;CACjG,OAAO,QAAQ,IAAI,0BAA0B;AAC/C;AAEA,SAAgB,uCACd,SACgD;CAChD,OAAO,QAAQ,IAAI,sCAAsC;AAC3D;AAEA,SAAgB,0BAA0B,SAAqD;CAC7F,OAAO,QAAQ,IAAI,wBAAwB;AAC7C;AAEA,SAAgB,6BAA6B,SAAwD;CACnG,OAAO,QAAQ,IAAI,2BAA2B;AAChD;AAEA,SAAgB,gCAAgC,SAA2D;CACzG,OAAO,QAAQ,IAAI,8BAA8B;AACnD"}
1
+ {"version":3,"file":"workflow.mjs","names":[],"sources":["../../../src/schemas/workflow.ts"],"sourcesContent":["import type { Context } from '@deepseek-ai/cordis';\nimport { type Static, Type } from 'typebox';\n\nexport const PO_WORKFLOW_LAUNCH_SERVICE = 'po/workflow-launch';\nexport const PO_WORKFLOW_DEFINITION_SOURCES_SERVICE = 'po/workflow-definition-sources';\nexport const PO_WORKFLOW_RUNS_SERVICE = 'po/workflow-runs';\nexport const PO_WORKFLOW_CONTEXT_SERVICE = 'po/workflow-context';\nexport const PO_WORKFLOW_ADMISSIONS_SERVICE = 'po/workflow-admissions';\n\n/** A source-relative definition, never an arbitrary host path. */\nexport const PoWorkflowDefinitionRefSchema = Type.Object(\n {\n source: Type.String({ minLength: 1 }),\n key: Type.String({\n minLength: 1,\n pattern: '^(?!/)(?!.*\\\\\\\\)(?!.*:)(?!.*[\\\\u0000-\\\\u001f\\\\u007f])(?!.*(?:^|/)\\\\.{1,2}(?:/|$))[^/]+(?:/[^/]+)*$',\n }),\n },\n { additionalProperties: false },\n);\nexport type PoWorkflowDefinitionRef = Static<typeof PoWorkflowDefinitionRefSchema>;\n\n/** Registration receipt. runId fences a runKey that can be reused later. */\nexport const PoWorkflowRunRefSchema = Type.Object(\n {\n runId: Type.String({ minLength: 1 }),\n ownerSessionId: Type.String({ minLength: 1 }),\n runKey: Type.String({ minLength: 1 }),\n registryWorkspace: Type.String({ minLength: 1 }),\n },\n { additionalProperties: false },\n);\nexport type PoWorkflowRunRef = Static<typeof PoWorkflowRunRefSchema>;\n\nexport interface PoWorkflowLaunchOverrides {\n readonly runner?: string;\n readonly command?: string;\n readonly choice?: string;\n readonly job?: string;\n readonly inputs?: Readonly<Record<string, string>>;\n readonly env?: Readonly<Record<string, string>>;\n readonly prompt?: string;\n readonly name?: string;\n}\n\n/** Session-owned. Rejection means no observable registration was accepted. */\nexport interface PoWorkflowLaunchService {\n launch(definition: PoWorkflowDefinitionRef, overrides?: PoWorkflowLaunchOverrides): Promise<PoWorkflowRunRef>;\n}\n\n/** Non-secret host association. Environment variables never establish this binding. */\nexport interface PoWorkflowPolicyAssociation {\n readonly coordinator: string;\n readonly key: string;\n}\n\n/** Independent of runId and executionGeneration, including continue-next resumes. */\nexport interface PoWorkflowOperationEpoch {\n readonly admissionId: string;\n readonly operationId: string;\n}\n\nexport interface PoWorkflowPolicyBinding {\n readonly association: PoWorkflowPolicyAssociation;\n readonly epoch: PoWorkflowOperationEpoch;\n}\n\nexport interface PoWorkflowExecutionPolicyRequest {\n readonly binding: PoWorkflowPolicyBinding;\n readonly reason: 'launch' | 'recover' | 'startup' | 'resume';\n readonly ownerSessionId: string;\n readonly launcherSessionId: string;\n /** The engine supplies the actual persisted run after createRun, including startup and recovery. */\n readonly run?: PoWorkflowRunRef;\n readonly previousRun?: PoWorkflowRunRef;\n /** Ephemeral host lifetime, never persisted. */\n readonly signal: AbortSignal;\n}\n\nexport interface PoWorkflowExecutionPolicyResult {\n /** Trusted non-secret context only. PO_* and WORKFLOW_* engine identity keys are forbidden. */\n readonly environment?: Readonly<Record<string, string>>;\n}\n\n/**\n * How a bound generation ended. `abandoned` is a person giving up a failed run: it is never recovered.\n * A coordinator releases what it holds for success, skipped and abandoned, and keeps it for failed and\n * interrupted so a recovery of the run reuses it.\n */\nexport type PoWorkflowSettlementOutcome = PoWorkflowOutcome | 'abandoned';\n\nexport interface PoWorkflowSettlementRequest {\n readonly binding: PoWorkflowPolicyBinding;\n readonly run: PoWorkflowRunRef;\n readonly outcome: PoWorkflowSettlementOutcome;\n readonly failedJob?: string;\n readonly ownerSessionId: string;\n readonly launcherSessionId: string;\n /** Ephemeral host lifetime, never persisted. */\n readonly signal: AbortSignal;\n}\n\nexport interface PoWorkflowPolicyOptions {\n readonly environmentKeys: readonly string[];\n /**\n * Called once a bound generation is terminal, and again with `abandoned` if a person abandons it.\n * Delivery is durable: the engine retries until this resolves, including after a restart or a\n * remount, so it must be idempotent. Throwing leaves the settlement pending.\n */\n readonly settle?: (request: PoWorkflowSettlementRequest) => Promise<void>;\n}\n\nexport interface PoWorkflowAdmissionOverrides extends PoWorkflowLaunchOverrides {\n readonly policy?: PoWorkflowPolicyAssociation;\n}\n\nexport interface PoWorkflowRecoveryOptions {\n readonly runner?: string;\n}\n\nexport type PoWorkflowAdmissionRequest =\n | {\n readonly kind: 'launch';\n readonly definition: PoWorkflowDefinitionRef;\n readonly overrides?: PoWorkflowAdmissionOverrides;\n }\n | { readonly kind: 'recover'; readonly previousRun: PoWorkflowRunRef; readonly options?: PoWorkflowRecoveryOptions };\n\nexport interface PoWorkflowValidationReport {\n readonly errors: readonly { readonly code: string; readonly message: string; readonly location?: string }[];\n readonly warnings: readonly { readonly code: string; readonly message: string; readonly location?: string }[];\n readonly deferred: readonly { readonly location: string; readonly reference: string; readonly reason: string }[];\n}\n\nexport type PoWorkflowAdmission =\n | {\n readonly attemptId: string;\n readonly admissionId: string;\n readonly state: 'pending';\n readonly binding?: PoWorkflowPolicyBinding;\n }\n | {\n readonly attemptId: string;\n readonly admissionId: string;\n readonly state: 'rejected';\n readonly errors: readonly string[];\n }\n | {\n readonly attemptId: string;\n readonly admissionId: string;\n readonly state: 'registered';\n readonly run: PoWorkflowRunRef;\n readonly binding?: PoWorkflowPolicyBinding;\n };\n\n/** Trusted, session-bound opt-in capability, never exposed as a tool or code-mode namespace. */\nexport interface PoWorkflowAdmissionsService {\n validate(request: PoWorkflowAdmissionRequest): Promise<PoWorkflowValidationReport>;\n launch(\n attemptId: string,\n definition: PoWorkflowDefinitionRef,\n overrides?: PoWorkflowAdmissionOverrides,\n ): Promise<PoWorkflowAdmission>;\n recover(\n attemptId: string,\n previousRun: PoWorkflowRunRef,\n options?: PoWorkflowRecoveryOptions,\n ): Promise<PoWorkflowAdmission>;\n /** Undefined is unknown or unauthorized, never proof that an executor did not start. */\n read(attemptId: string): Promise<PoWorkflowAdmission | undefined>;\n /** Session-owned registration. Duplicate (source session, coordinator) is rejected.\n * Disposal is generation-fenced. Remounting only reattaches; other sessions never supply this callback.\n * The engine persists the source launcher identity and never falls back to another session's policy.\n */\n registerPolicy(\n coordinator: string,\n beforeExecution: (request: PoWorkflowExecutionPolicyRequest) => Promise<PoWorkflowExecutionPolicyResult | void>,\n options: PoWorkflowPolicyOptions,\n ): { dispose(): void };\n}\nexport interface PoWorkflowDefinitionSource {\n readonly source: string;\n /** An admitted, materialized absolute directory. Registration does not fetch it. */\n readonly directory: string;\n}\n\nexport interface PoWorkflowDefinitionSourceHandle {\n readonly source: string;\n readonly generation: string;\n dispose(): void;\n}\n\n/** Workspace-owned registry. Disposal withdraws only the handle's registration. */\nexport interface PoWorkflowDefinitionSourcesService {\n register(contribution: PoWorkflowDefinitionSource): PoWorkflowDefinitionSourceHandle;\n list(): readonly PoWorkflowDefinitionSource[];\n subscribe(listener: (sources: readonly PoWorkflowDefinitionSource[]) => void): () => void;\n}\n\nexport type PoWorkflowStage = 'running' | 'completed' | 'error';\nexport type PoWorkflowOutcome = 'success' | 'skipped' | 'failed' | 'interrupted';\nexport type PoWorkflowProgressState =\n | 'pending'\n | 'running'\n | 'completed'\n | 'skipped'\n | 'failed'\n | 'pause_requested'\n | 'paused'\n | 'resumed';\n\nexport interface PoWorkflowStepState {\n readonly name: string;\n readonly status: PoWorkflowProgressState;\n readonly ref?: { readonly kind: string; readonly id: string; readonly label?: string };\n readonly reason?: string;\n}\n\nexport interface PoWorkflowJobState {\n readonly name: string;\n readonly phase: 'pre' | 'job' | 'post';\n readonly status: PoWorkflowProgressState;\n readonly steps: readonly PoWorkflowStepState[];\n}\n\nexport interface PoWorkflowRunState {\n readonly run: PoWorkflowRunRef;\n readonly stage: PoWorkflowStage;\n readonly outcome?: PoWorkflowOutcome;\n /** From the same persisted snapshot as stage/outcome, never a newer cached operation. */\n readonly binding?: PoWorkflowPolicyBinding;\n readonly jobs: readonly PoWorkflowJobState[];\n readonly children?: readonly PoWorkflowChildrenStatus[];\n}\n\n/** The parsed definition actually used, not a re-read of mutable YAML on disk. */\nexport interface PoWorkflowConfig {\n readonly definition: PoWorkflowDefinitionRef;\n readonly revision: string;\n /** Engine-owned YAML vocabulary. Core does not import or implement the parser. */\n readonly workflow: Readonly<Record<string, unknown>>;\n}\n\n/** Owner-authoritative views, or an authorized projection for a launcher. */\nexport interface PoWorkflowRunsService {\n readConfig(runId: string): Promise<PoWorkflowConfig | undefined>;\n readState(runId: string): Promise<PoWorkflowRunState | undefined>;\n /** Replays the latest state, including terminal state, before subsequent updates. */\n subscribe(runId: string, listener: (state: PoWorkflowRunState) => void): () => void;\n}\n\nexport interface PoWorkflowChildInvocation {\n readonly invocationId: string;\n readonly job: string;\n readonly state: 'running' | 'completed' | 'failed' | 'interrupted';\n readonly sessionIds: readonly string[];\n readonly startedAt: string;\n readonly endedAt?: string;\n readonly error?: string;\n readonly usage?: import('./hubChannel').PoHubSessionUsage;\n}\n\nexport interface PoWorkflowCriterionState {\n readonly key: string;\n readonly description: string;\n readonly verifiedBy: string;\n readonly status: 'unchecked' | 'checked' | 'not-met';\n readonly feedback?: string;\n readonly invocationId?: string;\n}\n\nexport interface PoWorkflowChildrenStatus {\n readonly lead: string;\n readonly continuations: number;\n readonly maxContinuations: number;\n readonly criteria: readonly PoWorkflowCriterionState[];\n readonly invocations: readonly PoWorkflowChildInvocation[];\n}\n\n/** In-process, caller-bound capability. Never accepted from a tool or serialized. */\nexport interface PoWorkflowChildrenSession {\n readonly role: 'lead' | 'child' | 'consumer';\n spawnChild?(input: {\n job: string;\n prompt: string;\n inputs?: Record<string, string | number | boolean>;\n }): Promise<PoWorkflowChildInvocation>;\n childStatus?(input: { invocationId: string }): Promise<PoWorkflowChildInvocation>;\n children?(): Promise<PoWorkflowChildrenStatus>;\n reportCriteria?(input: {\n results: Record<string, { status: 'checked' | 'not-met'; feedback?: string }>;\n }): Promise<void>;\n /** Immutable pre-review snapshot selected by the runtime, not mutable paths. */\n reviewArtifacts?(): Promise<{ revision: string; artifacts: Readonly<Record<string, string>> }>;\n /** Returns exactly the accepted bytes, after a fresh authoritative ledger/revision check. */\n acceptedArtifacts?(input: {\n job: string;\n }): Promise<{ revision: string; artifacts: Readonly<Record<string, string>> }>;\n}\n\n/** Bound only in the trusted step-session bootstrap, before packages mount. */\nexport interface PoWorkflowContextService {\n readonly run: PoWorkflowRunRef;\n readonly job: string;\n readonly step: string;\n readonly attempt: number;\n readonly config: PoWorkflowConfig;\n readonly jobEnvironment: Readonly<Record<string, string>>;\n readonly stepEnvironment: Readonly<Record<string, string>>;\n readonly traceparent?: string;\n /** Absolute path of the workflow's instructions file, read in place of the discovered AGENTS.md files. */\n readonly instructions?: string;\n readonly state: PoWorkflowRunsService;\n readonly children?: PoWorkflowChildrenSession;\n}\n\n// These are in-process capabilities, not remote session-create fields or durable records.\ndeclare module '@deepseek-ai/cordis' {\n interface Context {\n 'po/workflow-launch': PoWorkflowLaunchService;\n 'po/workflow-definition-sources': PoWorkflowDefinitionSourcesService;\n 'po/workflow-runs': PoWorkflowRunsService;\n 'po/workflow-context': PoWorkflowContextService;\n 'po/workflow-admissions': PoWorkflowAdmissionsService;\n }\n}\n\nexport function readPoWorkflowLaunchService(context: Context): PoWorkflowLaunchService | undefined {\n return context.get(PO_WORKFLOW_LAUNCH_SERVICE) as PoWorkflowLaunchService | undefined;\n}\n\nexport function readPoWorkflowDefinitionSourcesService(\n context: Context,\n): PoWorkflowDefinitionSourcesService | undefined {\n return context.get(PO_WORKFLOW_DEFINITION_SOURCES_SERVICE) as PoWorkflowDefinitionSourcesService | undefined;\n}\n\nexport function readPoWorkflowRunsService(context: Context): PoWorkflowRunsService | undefined {\n return context.get(PO_WORKFLOW_RUNS_SERVICE) as PoWorkflowRunsService | undefined;\n}\n\nexport function readPoWorkflowContextService(context: Context): PoWorkflowContextService | undefined {\n return context.get(PO_WORKFLOW_CONTEXT_SERVICE) as PoWorkflowContextService | undefined;\n}\n\nexport function readPoWorkflowAdmissionsService(context: Context): PoWorkflowAdmissionsService | undefined {\n return context.get(PO_WORKFLOW_ADMISSIONS_SERVICE) as PoWorkflowAdmissionsService | undefined;\n}\n"],"mappings":";;AAGA,MAAa,6BAA6B;AAC1C,MAAa,yCAAyC;AACtD,MAAa,2BAA2B;AACxC,MAAa,8BAA8B;AAC3C,MAAa,iCAAiC;;AAG9C,MAAa,gCAAgC,KAAK,OAChD;CACE,QAAQ,KAAK,OAAO,EAAE,WAAW,EAAE,CAAC;CACpC,KAAK,KAAK,OAAO;EACf,WAAW;EACX,SAAS;CACX,CAAC;AACH,GACA,EAAE,sBAAsB,MAAM,CAChC;;AAIA,MAAa,yBAAyB,KAAK,OACzC;CACE,OAAO,KAAK,OAAO,EAAE,WAAW,EAAE,CAAC;CACnC,gBAAgB,KAAK,OAAO,EAAE,WAAW,EAAE,CAAC;CAC5C,QAAQ,KAAK,OAAO,EAAE,WAAW,EAAE,CAAC;CACpC,mBAAmB,KAAK,OAAO,EAAE,WAAW,EAAE,CAAC;AACjD,GACA,EAAE,sBAAsB,MAAM,CAChC;AAwSA,SAAgB,4BAA4B,SAAuD;CACjG,OAAO,QAAQ,IAAI,0BAA0B;AAC/C;AAEA,SAAgB,uCACd,SACgD;CAChD,OAAO,QAAQ,IAAI,sCAAsC;AAC3D;AAEA,SAAgB,0BAA0B,SAAqD;CAC7F,OAAO,QAAQ,IAAI,wBAAwB;AAC7C;AAEA,SAAgB,6BAA6B,SAAwD;CACnG,OAAO,QAAQ,IAAI,2BAA2B;AAChD;AAEA,SAAgB,gCAAgC,SAA2D;CACzG,OAAO,QAAQ,IAAI,8BAA8B;AACnD"}
@@ -198,7 +198,9 @@ function createTunnelLauncher(options) {
198
198
  if (stopped) return;
199
199
  const outcome = await single(supervisedInput);
200
200
  if (stopped) {
201
- if (outcome.ok) await outcome.stop().catch(() => void 0);
201
+ if (outcome.ok) await outcome.stop().catch((error) => {
202
+ notice(`Could not stop recovered tunnel: ${String(error)}`);
203
+ });
202
204
  return;
203
205
  }
204
206
  if (!outcome.ok) {
@@ -239,8 +241,12 @@ function createTunnelLauncher(options) {
239
241
  recoveryAbort.abort();
240
242
  const recovering = recoveryPromise;
241
243
  stopPromise = (async () => {
242
- await activeStop?.().catch(() => void 0);
243
- await recovering?.catch(() => void 0);
244
+ await activeStop?.().catch((error) => {
245
+ notice(`Could not stop tunnel: ${String(error)}`);
246
+ });
247
+ await recovering?.catch((error) => {
248
+ notice(`Tunnel recovery cleanup failed: ${String(error)}`);
249
+ });
244
250
  await node_fs_promises.default.rm(node_path.default.join(options.stateDir, PID_FILE), { force: true });
245
251
  await node_fs_promises.default.rm(node_path.default.join(options.stateDir, TOKEN_FILE), { force: true });
246
252
  })();
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["path","fs","readFileSync","spawn","describeTunnelFailure","tunnelArgs","PAIRING_PAGE_ROUTE","extractTunnelUrl","mentionsRegisteredConnection"],"sources":["../../../../src/services/tunnelProcess/index.ts"],"sourcesContent":["import { type ChildProcess, spawn } from 'node:child_process';\nimport { readFileSync, rmSync } from 'node:fs';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\n\nimport { PAIRING_PAGE_ROUTE } from '../../constants/remote';\nimport {\n type TunnelConfig,\n type TunnelLauncher,\n type TunnelStartInput,\n type TunnelStartResult,\n} from '../../types/remote';\nimport { PAIRING_PAGE_MARKER } from '../pairingPage';\nimport { describeTunnelFailure, extractTunnelUrl, mentionsRegisteredConnection, tunnelArgs } from '../tunnelOutput';\n\n/** Explicit binary path, ahead of a PATH scan. */\nexport const CLOUDFLARED_ENV = 'PO_CLOUDFLARED';\n// Give new quick-host DNS and negative caches time to settle, within a bounded startup.\nconst START_TIMEOUT_MS = 120_000;\nconst TERMINATE_GRACE_MS = 2000;\nconst SELF_TEST_ATTEMPTS = 240;\nconst SELF_TEST_RETRY_MS = 500;\nconst NAMED_RESTART_ATTEMPTS = 3;\nconst NAMED_RESTART_BASE_DELAY_MS = 500;\nconst NAMED_RESTART_MAX_DELAY_MS = 4000;\nconst NAMED_RESTART_STABLE_MS = 60_000;\n/** Lines of process output kept for a failure report. */\nconst LOG_TAIL = 20;\nconst UNAUTHORIZED = 401;\n/** Cloudflare has the DNS route but no connector has registered yet. */\nconst EDGE_TUNNEL_UNAVAILABLE = 530;\nconst OK = 200;\nconst PID_FILE = 'tunnel.pid';\nconst TOKEN_FILE = 'tunnel.token';\nconst FILE_MODE = 0o600;\n/** A pid file older than this is stale beyond usefulness and the pid has likely been recycled. */\nconst PID_FILE_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;\n\nexport interface ProbeResult {\n status: number;\n body: string;\n}\n\nexport interface TunnelProcessOptions {\n /** Explicit binary, ahead of the env var and the PATH scan. */\n cloudflaredPath?: string;\n stateDir: string;\n onNotice?: (message: string) => void;\n /** Test seam: stands in for node:child_process.spawn. */\n spawnProcess?: typeof spawn;\n /** Test seam: reaches the tunnel from outside, so the self-test needs no network. */\n probe?: (url: string, signal: AbortSignal) => Promise<ProbeResult>;\n /** Test seam: production waits briefly for a new public hostname to propagate. */\n selfTestRetryMs?: number;\n /** Test seam: production bounds the complete startup, including its self-test. */\n startTimeoutMs?: number;\n /** Test seam: production backs named-tunnel restarts off from 500 ms. */\n restartBaseDelayMs?: number;\n /** Test seam: production resets the rapid-crash budget after one stable minute. */\n restartStableMs?: number;\n /** Called after a quick tunnel exits, or after named-tunnel recovery is exhausted. */\n onExit?: (message: string) => void;\n}\n\n/**\n * Finds cloudflared without a shell.\n *\n * A manual scan rather than `which`: spawning a shell to locate a binary is a\n * quoting hazard for no benefit, and this also works the same on every platform.\n */\nexport async function findCloudflared(env: NodeJS.ProcessEnv = process.env): Promise<string | undefined> {\n const names = process.platform === 'win32' ? ['cloudflared.exe', 'cloudflared'] : ['cloudflared'];\n for (const directory of (env.PATH ?? '').split(path.delimiter)) {\n if (directory === '') continue;\n for (const name of names) {\n const candidate = path.join(directory, name);\n try {\n await fs.access(candidate, fs.constants.X_OK);\n return candidate;\n } catch {\n // Not here, or not executable; keep looking.\n }\n }\n }\n return undefined;\n}\n\nasync function defaultProbe(url: string, signal: AbortSignal): Promise<ProbeResult> {\n const response = await fetch(url, { redirect: 'manual', signal });\n return { status: response.status, body: await response.text() };\n}\n\n/**\n * Kills a tunnel left behind by a hub that could not clean up after itself.\n *\n * A SIGKILLed hub never runs its exit handler, and the child is not detached so\n * it is reparented rather than killed. It then holds the tunnel open against a\n * dead port. This is the sweep on the next start.\n *\n * The residual risk is pid reuse after a reboot. The age window narrows it and\n * on Linux the command line is checked; macOS has no equally cheap check, so\n * there the window stands.\n */\ninterface TunnelPidRecord {\n pid?: unknown;\n ownerPid?: unknown;\n startedAt?: unknown;\n}\n\nfunction validPid(value: unknown): value is number {\n return typeof value === 'number' && Number.isSafeInteger(value) && value > 0;\n}\n\n/** Only ESRCH proves the owner is gone; permission and platform errors fail safe. */\nfunction ownerMayBeAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (error) {\n return (error as NodeJS.ErrnoException).code !== 'ESRCH';\n }\n}\n\n// sync: exported createRemoteRuntime must reap stale public tunnels before its synchronous startup returns.\nexport function reapStaleTunnel(stateDir: string, onNotice: (message: string) => void): void {\n const pidPath = path.join(stateDir, PID_FILE);\n const tokenPath = path.join(stateDir, TOKEN_FILE);\n let raw: string;\n try {\n raw = readFileSync(pidPath, 'utf8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') rmSync(tokenPath, { force: true });\n return; // No pid file is the normal case; other read failures are unsafe to guess through.\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return; // A malformed record cannot prove that its owner is gone.\n }\n if (typeof parsed !== 'object' || parsed === null) return;\n const record = parsed as TunnelPidRecord;\n if (Object.hasOwn(record, 'ownerPid')) {\n if (!validPid(record.ownerPid) || ownerMayBeAlive(record.ownerPid)) return;\n }\n\n rmSync(tokenPath, { force: true });\n rmSync(pidPath, { force: true });\n const pid = validPid(record.pid) ? record.pid : undefined;\n const startedAt = typeof record.startedAt === 'number' ? record.startedAt : 0;\n if (pid === undefined || Date.now() - startedAt > PID_FILE_MAX_AGE_MS) return;\n if (process.platform === 'linux') {\n try {\n if (!readFileSync(`/proc/${String(pid)}/cmdline`, 'utf8').includes('cloudflared')) return;\n } catch {\n return; // The process is gone.\n }\n }\n try {\n process.kill(pid, 'SIGTERM');\n onNotice(`stopped a tunnel left running by a previous hub (pid ${String(pid)})`);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ESRCH') onNotice(`Could not stop stale tunnel: ${String(error)}`);\n }\n}\n\nfunction tailOf(lines: readonly string[]): string {\n return lines.slice(-LOG_TAIL).join('').trim();\n}\n\n/** Copies a named tunnel token to a private runtime file understood by cloudflared. */\nasync function writeTokenFile(config: TunnelConfig, stateDir: string): Promise<string | undefined> {\n if (config.kind !== 'named' || config.tokenFile === undefined) return undefined;\n let token: string;\n let sourcePath: string;\n try {\n sourcePath = await fs.realpath(config.tokenFile);\n token = (await fs.readFile(sourcePath, 'utf8')).trim();\n } catch {\n return undefined;\n }\n if (token === '') return undefined;\n await fs.mkdir(stateDir, { recursive: true, mode: 0o700 });\n const tokenPath = path.join(stateDir, TOKEN_FILE);\n let existingTarget: string | undefined;\n try {\n existingTarget = await fs.realpath(tokenPath);\n } catch (error) {\n // Missing is the normal state before launch; other failures must not hide an alias.\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;\n }\n if (sourcePath === path.resolve(tokenPath) || sourcePath === existingTarget) {\n throw new Error('The configured tunnel token file cannot also be Po runtime token storage.');\n }\n await fs.rm(tokenPath, { force: true });\n await fs.writeFile(tokenPath, token, { mode: FILE_MODE });\n await fs.chmod(tokenPath, FILE_MODE);\n return tokenPath;\n}\n\nexport function createTunnelLauncher(options: TunnelProcessOptions): TunnelLauncher {\n const notice = options.onNotice ?? ((): void => {});\n const restartBaseDelayMs = options.restartBaseDelayMs ?? NAMED_RESTART_BASE_DELAY_MS;\n const restartStableMs = options.restartStableMs ?? NAMED_RESTART_STABLE_MS;\n\n return async function launch(input: TunnelStartInput): Promise<TunnelStartResult> {\n let stopped = false;\n let activeStop: (() => Promise<void>) | undefined;\n let stopPromise: Promise<void> | undefined;\n let recoveryPromise: Promise<void> | undefined;\n let pendingExit: string | undefined;\n let restartCount = 0;\n let lastReadyAt = 0;\n const recoveryAbort = new AbortController();\n const signal =\n input.signal === undefined ? recoveryAbort.signal : AbortSignal.any([input.signal, recoveryAbort.signal]);\n const supervisedInput: TunnelStartInput = { ...input, signal };\n\n function queueUnexpectedExit(message: string): void {\n if (stopped) return;\n if (input.config.kind === 'quick') {\n options.onExit?.(message);\n return;\n }\n if (recoveryPromise !== undefined) {\n pendingExit = message;\n return;\n }\n beginRecovery(message);\n }\n\n const single = createSingleTunnelLauncher({ ...options, onExit: queueUnexpectedExit });\n\n async function waitForRestart(attempt: number): Promise<void> {\n if (recoveryAbort.signal.aborted) return;\n const delayMs = Math.min(restartBaseDelayMs * 2 ** (attempt - 1), NAMED_RESTART_MAX_DELAY_MS);\n await new Promise<void>((resolve) => {\n let timer: ReturnType<typeof setTimeout>;\n const done = (): void => {\n clearTimeout(timer);\n recoveryAbort.signal.removeEventListener('abort', done);\n resolve();\n };\n timer = setTimeout(done, delayMs);\n recoveryAbort.signal.addEventListener('abort', done, { once: true });\n if (recoveryAbort.signal.aborted) done();\n });\n }\n\n async function recover(initialMessage: string): Promise<void> {\n if (Date.now() - lastReadyAt >= restartStableMs) restartCount = 0;\n let lastFailure = initialMessage;\n while (!stopped && restartCount < NAMED_RESTART_ATTEMPTS) {\n restartCount += 1;\n notice(\n `cloudflared stopped; restarting named tunnel (attempt ${String(restartCount)} of ${String(NAMED_RESTART_ATTEMPTS)})`,\n );\n await waitForRestart(restartCount);\n if (stopped) return;\n\n const outcome = await single(supervisedInput);\n if (stopped) {\n if (outcome.ok) await outcome.stop().catch(() => undefined);\n return;\n }\n if (!outcome.ok) {\n lastFailure = outcome.message;\n continue;\n }\n activeStop = outcome.stop;\n lastReadyAt = Date.now();\n notice(`named tunnel recovered at ${outcome.publicOrigin}`);\n return;\n }\n\n if (!stopped) {\n options.onExit?.(\n `${initialMessage}\\nNamed tunnel recovery failed after ${String(NAMED_RESTART_ATTEMPTS)} attempts. Last failure: ${lastFailure}`,\n );\n }\n }\n\n function beginRecovery(message: string): void {\n recoveryPromise = recover(message)\n .catch((error: unknown) => {\n if (!stopped) {\n const cause = error instanceof Error ? error.message : String(error);\n options.onExit?.(`${message}\\nNamed tunnel recovery failed: ${cause}`);\n }\n })\n .finally(() => {\n recoveryPromise = undefined;\n const queued = pendingExit;\n pendingExit = undefined;\n if (queued !== undefined && !stopped) beginRecovery(queued);\n });\n }\n\n const initial = await single(supervisedInput);\n if (!initial.ok) {\n stopped = true;\n recoveryAbort.abort();\n return initial;\n }\n activeStop = initial.stop;\n lastReadyAt = Date.now();\n\n const stop = async (): Promise<void> => {\n if (stopPromise !== undefined) return await stopPromise;\n stopped = true;\n recoveryAbort.abort();\n const recovering = recoveryPromise;\n stopPromise = (async () => {\n await activeStop?.().catch(() => undefined);\n await recovering?.catch(() => undefined);\n await fs.rm(path.join(options.stateDir, PID_FILE), { force: true });\n await fs.rm(path.join(options.stateDir, TOKEN_FILE), { force: true });\n })();\n return await stopPromise;\n };\n\n return { ok: true, publicOrigin: initial.publicOrigin, stop };\n };\n}\n\n/** Starts one cloudflared child; the exported launcher supervises successive named-tunnel children. */\nfunction createSingleTunnelLauncher(options: TunnelProcessOptions): TunnelLauncher {\n const notice = options.onNotice ?? ((): void => {});\n const spawnProcess = options.spawnProcess ?? spawn;\n const probe = options.probe ?? defaultProbe;\n const selfTestRetryMs = options.selfTestRetryMs ?? SELF_TEST_RETRY_MS;\n const startTimeoutMs = options.startTimeoutMs ?? START_TIMEOUT_MS;\n const pidPath = path.join(options.stateDir, PID_FILE);\n const runtimeTokenPath = path.join(options.stateDir, TOKEN_FILE);\n\n return async function launch(input: TunnelStartInput): Promise<TunnelStartResult> {\n if (input.signal?.aborted === true) {\n return { ok: false, failure: 'exited', message: 'Tunnel startup was cancelled.' };\n }\n const binary = options.cloudflaredPath ?? process.env[CLOUDFLARED_ENV] ?? (await findCloudflared());\n if (binary === undefined || binary === '') {\n return { ok: false, failure: 'not_installed', message: describeTunnelFailure('not_installed') };\n }\n\n let tokenPath: string | undefined;\n try {\n tokenPath = await writeTokenFile(input.config, options.stateDir);\n } catch (error) {\n const cause = error instanceof Error ? error.message : String(error);\n return { ok: false, failure: 'spawn_failed', message: `${describeTunnelFailure('spawn_failed')} ${cause}` };\n }\n const args = tunnelArgs(input.config, input.port, tokenPath);\n let child: ChildProcess;\n try {\n // Never detached and never unref'd. A detached child becomes a process\n // group leader that survives the terminal's SIGINT, which is exactly the\n // orphaned public tunnel this must avoid. Contrast serverSpawner.ts,\n // which detaches on purpose because sessions outlive the hub.\n child = spawnProcess(binary, args, { detached: false, stdio: ['ignore', 'pipe', 'pipe'] });\n } catch (error) {\n await fs.rm(runtimeTokenPath, { force: true });\n const cause = error instanceof Error ? error.message : String(error);\n return { ok: false, failure: 'spawn_failed', message: `${describeTunnelFailure('spawn_failed')} ${cause}` };\n }\n\n const lines: string[] = [];\n let stopped = false;\n let settle: ((result: TunnelStartResult) => void) | undefined;\n let selfTestStarted = false;\n const selfTestAbort = new AbortController();\n\n const killTree = (): void => {\n try {\n child.kill('SIGTERM');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ESRCH') notice(`Could not stop tunnel: ${String(error)}`);\n }\n };\n const onProcessExit = (): void => {\n // sync: Node does not await filesystem cleanup in the process exit handler.\n // This is the net under process.exit(), an uncaught exception, and an\n // unhandled rejection, none of which reach the graceful close path.\n rmSync(runtimeTokenPath, { force: true });\n try {\n if (child.pid !== undefined) process.kill(child.pid, 'SIGKILL');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ESRCH')\n notice(`Could not kill tunnel at exit: ${String(error)}`);\n }\n };\n process.once('exit', onProcessExit);\n\n const stop = async (): Promise<void> => {\n if (stopped) return;\n stopped = true;\n if (child.exitCode !== null || child.signalCode !== null) {\n process.off('exit', onProcessExit);\n await pidWrite;\n await fs.rm(pidPath, { force: true });\n await fs.rm(runtimeTokenPath, { force: true });\n return;\n }\n await new Promise<void>((resolve) => {\n const escalate = setTimeout(() => {\n try {\n child.kill('SIGKILL');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ESRCH') notice(`Could not kill tunnel: ${String(error)}`);\n }\n }, TERMINATE_GRACE_MS);\n child.once('exit', () => {\n clearTimeout(escalate);\n resolve();\n });\n killTree();\n });\n process.off('exit', onProcessExit);\n await pidWrite;\n await fs.rm(pidPath, { force: true });\n await fs.rm(runtimeTokenPath, { force: true });\n };\n\n // Register child listeners below without yielding, so early exits cannot be missed.\n // Cleanup waits for this ordered write before removing the record.\n const pidWrite = fs\n .mkdir(options.stateDir, { recursive: true, mode: 0o700 })\n .then(() =>\n fs.writeFile(\n pidPath,\n JSON.stringify({ pid: child.pid, ownerPid: process.pid, startedAt: Date.now(), command: binary }),\n { mode: FILE_MODE },\n ),\n )\n .catch((error: unknown) => {\n // The reaper is a safety net; report unavailable recovery without blocking startup.\n notice(`Could not write tunnel process record: ${String(error)}`);\n });\n\n const configuredOrigin = input.config.kind === 'named' ? `https://${input.config.hostname}` : undefined;\n let seenUrl = configuredOrigin;\n let seenConnection = false;\n\n const waitToRetry = async (): Promise<void> => {\n if (selfTestRetryMs <= 0) return;\n await new Promise<void>((resolve) => {\n const done = (): void => {\n clearTimeout(timer);\n selfTestAbort.signal.removeEventListener('abort', done);\n resolve();\n };\n const timer = setTimeout(done, selfTestRetryMs);\n selfTestAbort.signal.addEventListener('abort', done, { once: true });\n });\n };\n\n /** Proves end to end that pairing is reachable and that everything else is not. */\n const runSelfTest = async (origin: string): Promise<TunnelStartResult> => {\n let pairResponse: ProbeResult | undefined;\n let healthResponse: ProbeResult | undefined;\n let cause = 'fetch failed';\n for (let attempt = 0; attempt < SELF_TEST_ATTEMPTS && !selfTestAbort.signal.aborted; attempt += 1) {\n try {\n pairResponse = await probe(`${origin}${PAIRING_PAGE_ROUTE}`, selfTestAbort.signal);\n healthResponse = await probe(`${origin}/api/health`, selfTestAbort.signal);\n if (pairResponse.status !== EDGE_TUNNEL_UNAVAILABLE && healthResponse.status !== EDGE_TUNNEL_UNAVAILABLE)\n break;\n cause = `Cloudflare edge answered ${String(healthResponse.status)} before the connector registered`;\n } catch (error) {\n cause = error instanceof Error ? error.message : String(error);\n }\n if (attempt + 1 < SELF_TEST_ATTEMPTS && !selfTestAbort.signal.aborted) await waitToRetry();\n }\n if (pairResponse === undefined || healthResponse === undefined) {\n return { ok: false, failure: 'self_test_failed', message: `The tunnel did not answer: ${cause}` };\n }\n if (healthResponse.status !== UNAUTHORIZED) {\n // The agent would be on the public internet unauthenticated. This is\n // the one failure worth an automatic abort rather than a warning.\n const message = `${describeTunnelFailure('self_test_failed')} /api/health answered ${String(healthResponse.status)} through the tunnel.`;\n notice(message);\n return { ok: false, failure: 'self_test_failed', message };\n }\n if (pairResponse.status !== OK || !pairResponse.body.includes(PAIRING_PAGE_MARKER)) {\n const message = `The pairing page answered ${String(pairResponse.status)} through the tunnel.`;\n notice(message);\n return { ok: false, failure: 'self_test_failed', message };\n }\n notice(`tunnel ready at ${origin}`);\n return { ok: true, publicOrigin: origin, stop };\n };\n\n const consider = (chunk: string): void => {\n lines.push(chunk);\n if (settle === undefined || selfTestStarted) return;\n seenUrl ??= extractTunnelUrl(chunk);\n seenConnection ||= mentionsRegisteredConnection(chunk);\n if (seenUrl === undefined || !seenConnection) return;\n selfTestStarted = true;\n input.acceptOrigin?.(seenUrl);\n void runSelfTest(seenUrl).then((result) => settle?.(result));\n };\n\n child.stdout?.on('data', (data: Buffer) => consider(data.toString()));\n child.stderr?.on('data', (data: Buffer) => consider(data.toString()));\n\n return await new Promise<TunnelStartResult>((resolve) => {\n let deadline: ReturnType<typeof setTimeout>;\n const finish = (result: TunnelStartResult, stopProcess = !result.ok): void => {\n if (settle === undefined) return;\n settle = undefined;\n clearTimeout(deadline);\n input.signal?.removeEventListener('abort', cancel);\n selfTestAbort.abort();\n if (stopProcess) {\n void stop().then(() => resolve(result));\n } else {\n resolve(result);\n }\n };\n const cancel = (): void => {\n finish({ ok: false, failure: 'exited', message: 'Tunnel startup was cancelled.' });\n };\n settle = (result) => finish(result);\n deadline = setTimeout(() => {\n finish({\n ok: false,\n failure: 'timeout',\n message: `${describeTunnelFailure('timeout')}\\n${tailOf(lines)}`,\n });\n }, startTimeoutMs);\n input.signal?.addEventListener('abort', cancel, { once: true });\n\n child.once('error', (error) => {\n finish({\n ok: false,\n failure: 'spawn_failed',\n message: `${describeTunnelFailure('spawn_failed')} ${error.message}`,\n });\n });\n\n child.once('exit', async (code) => {\n process.off('exit', onProcessExit);\n await pidWrite;\n await fs.rm(runtimeTokenPath, { force: true }).catch((error: unknown) => {\n notice(`Tunnel token cleanup failed: ${error instanceof Error ? error.message : String(error)}`);\n });\n const message = `${describeTunnelFailure('exited')} (code ${String(code)})\\n${tailOf(lines)}`;\n if (settle !== undefined) {\n finish({ ok: false, failure: 'exited', message }, false);\n return;\n }\n // It came up and then died: network loss, an edge rejection, or a\n // self-update. The supervisor either replaces a named connector or tells\n // the host to fail closed rather than leave an unreachable cockpit enabled.\n if (!stopped) options.onExit?.(message);\n });\n\n // A named tunnel already knows its hostname, but it still waits for\n // cloudflared to register an edge connection before probing the public URL.\n if (input.signal?.aborted === true) cancel();\n });\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["path","fs","readFileSync","spawn","describeTunnelFailure","tunnelArgs","PAIRING_PAGE_ROUTE","extractTunnelUrl","mentionsRegisteredConnection"],"sources":["../../../../src/services/tunnelProcess/index.ts"],"sourcesContent":["import { type ChildProcess, spawn } from 'node:child_process';\nimport { readFileSync, rmSync } from 'node:fs';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\n\nimport { PAIRING_PAGE_ROUTE } from '../../constants/remote';\nimport {\n type TunnelConfig,\n type TunnelLauncher,\n type TunnelStartInput,\n type TunnelStartResult,\n} from '../../types/remote';\nimport { PAIRING_PAGE_MARKER } from '../pairingPage';\nimport { describeTunnelFailure, extractTunnelUrl, mentionsRegisteredConnection, tunnelArgs } from '../tunnelOutput';\n\n/** Explicit binary path, ahead of a PATH scan. */\nexport const CLOUDFLARED_ENV = 'PO_CLOUDFLARED';\n// Give new quick-host DNS and negative caches time to settle, within a bounded startup.\nconst START_TIMEOUT_MS = 120_000;\nconst TERMINATE_GRACE_MS = 2000;\nconst SELF_TEST_ATTEMPTS = 240;\nconst SELF_TEST_RETRY_MS = 500;\nconst NAMED_RESTART_ATTEMPTS = 3;\nconst NAMED_RESTART_BASE_DELAY_MS = 500;\nconst NAMED_RESTART_MAX_DELAY_MS = 4000;\nconst NAMED_RESTART_STABLE_MS = 60_000;\n/** Lines of process output kept for a failure report. */\nconst LOG_TAIL = 20;\nconst UNAUTHORIZED = 401;\n/** Cloudflare has the DNS route but no connector has registered yet. */\nconst EDGE_TUNNEL_UNAVAILABLE = 530;\nconst OK = 200;\nconst PID_FILE = 'tunnel.pid';\nconst TOKEN_FILE = 'tunnel.token';\nconst FILE_MODE = 0o600;\n/** A pid file older than this is stale beyond usefulness and the pid has likely been recycled. */\nconst PID_FILE_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;\n\nexport interface ProbeResult {\n status: number;\n body: string;\n}\n\nexport interface TunnelProcessOptions {\n /** Explicit binary, ahead of the env var and the PATH scan. */\n cloudflaredPath?: string;\n stateDir: string;\n onNotice?: (message: string) => void;\n /** Test seam: stands in for node:child_process.spawn. */\n spawnProcess?: typeof spawn;\n /** Test seam: reaches the tunnel from outside, so the self-test needs no network. */\n probe?: (url: string, signal: AbortSignal) => Promise<ProbeResult>;\n /** Test seam: production waits briefly for a new public hostname to propagate. */\n selfTestRetryMs?: number;\n /** Test seam: production bounds the complete startup, including its self-test. */\n startTimeoutMs?: number;\n /** Test seam: production backs named-tunnel restarts off from 500 ms. */\n restartBaseDelayMs?: number;\n /** Test seam: production resets the rapid-crash budget after one stable minute. */\n restartStableMs?: number;\n /** Called after a quick tunnel exits, or after named-tunnel recovery is exhausted. */\n onExit?: (message: string) => void;\n}\n\n/**\n * Finds cloudflared without a shell.\n *\n * A manual scan rather than `which`: spawning a shell to locate a binary is a\n * quoting hazard for no benefit, and this also works the same on every platform.\n */\nexport async function findCloudflared(env: NodeJS.ProcessEnv = process.env): Promise<string | undefined> {\n const names = process.platform === 'win32' ? ['cloudflared.exe', 'cloudflared'] : ['cloudflared'];\n for (const directory of (env.PATH ?? '').split(path.delimiter)) {\n if (directory === '') continue;\n for (const name of names) {\n const candidate = path.join(directory, name);\n try {\n await fs.access(candidate, fs.constants.X_OK);\n return candidate;\n } catch {\n // Not here, or not executable; keep looking.\n }\n }\n }\n return undefined;\n}\n\nasync function defaultProbe(url: string, signal: AbortSignal): Promise<ProbeResult> {\n const response = await fetch(url, { redirect: 'manual', signal });\n return { status: response.status, body: await response.text() };\n}\n\n/**\n * Kills a tunnel left behind by a hub that could not clean up after itself.\n *\n * A SIGKILLed hub never runs its exit handler, and the child is not detached so\n * it is reparented rather than killed. It then holds the tunnel open against a\n * dead port. This is the sweep on the next start.\n *\n * The residual risk is pid reuse after a reboot. The age window narrows it and\n * on Linux the command line is checked; macOS has no equally cheap check, so\n * there the window stands.\n */\ninterface TunnelPidRecord {\n pid?: unknown;\n ownerPid?: unknown;\n startedAt?: unknown;\n}\n\nfunction validPid(value: unknown): value is number {\n return typeof value === 'number' && Number.isSafeInteger(value) && value > 0;\n}\n\n/** Only ESRCH proves the owner is gone; permission and platform errors fail safe. */\nfunction ownerMayBeAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (error) {\n return (error as NodeJS.ErrnoException).code !== 'ESRCH';\n }\n}\n\n// sync: exported createRemoteRuntime must reap stale public tunnels before its synchronous startup returns.\nexport function reapStaleTunnel(stateDir: string, onNotice: (message: string) => void): void {\n const pidPath = path.join(stateDir, PID_FILE);\n const tokenPath = path.join(stateDir, TOKEN_FILE);\n let raw: string;\n try {\n raw = readFileSync(pidPath, 'utf8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') rmSync(tokenPath, { force: true });\n return; // No pid file is the normal case; other read failures are unsafe to guess through.\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return; // A malformed record cannot prove that its owner is gone.\n }\n if (typeof parsed !== 'object' || parsed === null) return;\n const record = parsed as TunnelPidRecord;\n if (Object.hasOwn(record, 'ownerPid')) {\n if (!validPid(record.ownerPid) || ownerMayBeAlive(record.ownerPid)) return;\n }\n\n rmSync(tokenPath, { force: true });\n rmSync(pidPath, { force: true });\n const pid = validPid(record.pid) ? record.pid : undefined;\n const startedAt = typeof record.startedAt === 'number' ? record.startedAt : 0;\n if (pid === undefined || Date.now() - startedAt > PID_FILE_MAX_AGE_MS) return;\n if (process.platform === 'linux') {\n try {\n if (!readFileSync(`/proc/${String(pid)}/cmdline`, 'utf8').includes('cloudflared')) return;\n } catch {\n return; // The process is gone.\n }\n }\n try {\n process.kill(pid, 'SIGTERM');\n onNotice(`stopped a tunnel left running by a previous hub (pid ${String(pid)})`);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ESRCH') onNotice(`Could not stop stale tunnel: ${String(error)}`);\n }\n}\n\nfunction tailOf(lines: readonly string[]): string {\n return lines.slice(-LOG_TAIL).join('').trim();\n}\n\n/** Copies a named tunnel token to a private runtime file understood by cloudflared. */\nasync function writeTokenFile(config: TunnelConfig, stateDir: string): Promise<string | undefined> {\n if (config.kind !== 'named' || config.tokenFile === undefined) return undefined;\n let token: string;\n let sourcePath: string;\n try {\n sourcePath = await fs.realpath(config.tokenFile);\n token = (await fs.readFile(sourcePath, 'utf8')).trim();\n } catch {\n return undefined;\n }\n if (token === '') return undefined;\n await fs.mkdir(stateDir, { recursive: true, mode: 0o700 });\n const tokenPath = path.join(stateDir, TOKEN_FILE);\n let existingTarget: string | undefined;\n try {\n existingTarget = await fs.realpath(tokenPath);\n } catch (error) {\n // Missing is the normal state before launch; other failures must not hide an alias.\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;\n }\n if (sourcePath === path.resolve(tokenPath) || sourcePath === existingTarget) {\n throw new Error('The configured tunnel token file cannot also be Po runtime token storage.');\n }\n await fs.rm(tokenPath, { force: true });\n await fs.writeFile(tokenPath, token, { mode: FILE_MODE });\n await fs.chmod(tokenPath, FILE_MODE);\n return tokenPath;\n}\n\nexport function createTunnelLauncher(options: TunnelProcessOptions): TunnelLauncher {\n const notice = options.onNotice ?? ((): void => {});\n const restartBaseDelayMs = options.restartBaseDelayMs ?? NAMED_RESTART_BASE_DELAY_MS;\n const restartStableMs = options.restartStableMs ?? NAMED_RESTART_STABLE_MS;\n\n return async function launch(input: TunnelStartInput): Promise<TunnelStartResult> {\n let stopped = false;\n let activeStop: (() => Promise<void>) | undefined;\n let stopPromise: Promise<void> | undefined;\n let recoveryPromise: Promise<void> | undefined;\n let pendingExit: string | undefined;\n let restartCount = 0;\n let lastReadyAt = 0;\n const recoveryAbort = new AbortController();\n const signal =\n input.signal === undefined ? recoveryAbort.signal : AbortSignal.any([input.signal, recoveryAbort.signal]);\n const supervisedInput: TunnelStartInput = { ...input, signal };\n\n function queueUnexpectedExit(message: string): void {\n if (stopped) return;\n if (input.config.kind === 'quick') {\n options.onExit?.(message);\n return;\n }\n if (recoveryPromise !== undefined) {\n pendingExit = message;\n return;\n }\n beginRecovery(message);\n }\n\n const single = createSingleTunnelLauncher({ ...options, onExit: queueUnexpectedExit });\n\n async function waitForRestart(attempt: number): Promise<void> {\n if (recoveryAbort.signal.aborted) return;\n const delayMs = Math.min(restartBaseDelayMs * 2 ** (attempt - 1), NAMED_RESTART_MAX_DELAY_MS);\n await new Promise<void>((resolve) => {\n let timer: ReturnType<typeof setTimeout>;\n const done = (): void => {\n clearTimeout(timer);\n recoveryAbort.signal.removeEventListener('abort', done);\n resolve();\n };\n timer = setTimeout(done, delayMs);\n recoveryAbort.signal.addEventListener('abort', done, { once: true });\n if (recoveryAbort.signal.aborted) done();\n });\n }\n\n async function recover(initialMessage: string): Promise<void> {\n if (Date.now() - lastReadyAt >= restartStableMs) restartCount = 0;\n let lastFailure = initialMessage;\n while (!stopped && restartCount < NAMED_RESTART_ATTEMPTS) {\n restartCount += 1;\n notice(\n `cloudflared stopped; restarting named tunnel (attempt ${String(restartCount)} of ${String(NAMED_RESTART_ATTEMPTS)})`,\n );\n await waitForRestart(restartCount);\n if (stopped) return;\n\n const outcome = await single(supervisedInput);\n if (stopped) {\n if (outcome.ok) {\n await outcome.stop().catch((error: unknown) => {\n notice(`Could not stop recovered tunnel: ${String(error)}`);\n });\n }\n return;\n }\n if (!outcome.ok) {\n lastFailure = outcome.message;\n continue;\n }\n activeStop = outcome.stop;\n lastReadyAt = Date.now();\n notice(`named tunnel recovered at ${outcome.publicOrigin}`);\n return;\n }\n\n if (!stopped) {\n options.onExit?.(\n `${initialMessage}\\nNamed tunnel recovery failed after ${String(NAMED_RESTART_ATTEMPTS)} attempts. Last failure: ${lastFailure}`,\n );\n }\n }\n\n function beginRecovery(message: string): void {\n recoveryPromise = recover(message)\n .catch((error: unknown) => {\n if (!stopped) {\n const cause = error instanceof Error ? error.message : String(error);\n options.onExit?.(`${message}\\nNamed tunnel recovery failed: ${cause}`);\n }\n })\n .finally(() => {\n recoveryPromise = undefined;\n const queued = pendingExit;\n pendingExit = undefined;\n if (queued !== undefined && !stopped) beginRecovery(queued);\n });\n }\n\n const initial = await single(supervisedInput);\n if (!initial.ok) {\n stopped = true;\n recoveryAbort.abort();\n return initial;\n }\n activeStop = initial.stop;\n lastReadyAt = Date.now();\n\n const stop = async (): Promise<void> => {\n if (stopPromise !== undefined) return await stopPromise;\n stopped = true;\n recoveryAbort.abort();\n const recovering = recoveryPromise;\n stopPromise = (async () => {\n await activeStop?.().catch((error: unknown) => {\n notice(`Could not stop tunnel: ${String(error)}`);\n });\n await recovering?.catch((error: unknown) => {\n notice(`Tunnel recovery cleanup failed: ${String(error)}`);\n });\n await fs.rm(path.join(options.stateDir, PID_FILE), { force: true });\n await fs.rm(path.join(options.stateDir, TOKEN_FILE), { force: true });\n })();\n return await stopPromise;\n };\n\n return { ok: true, publicOrigin: initial.publicOrigin, stop };\n };\n}\n\n/** Starts one cloudflared child; the exported launcher supervises successive named-tunnel children. */\nfunction createSingleTunnelLauncher(options: TunnelProcessOptions): TunnelLauncher {\n const notice = options.onNotice ?? ((): void => {});\n const spawnProcess = options.spawnProcess ?? spawn;\n const probe = options.probe ?? defaultProbe;\n const selfTestRetryMs = options.selfTestRetryMs ?? SELF_TEST_RETRY_MS;\n const startTimeoutMs = options.startTimeoutMs ?? START_TIMEOUT_MS;\n const pidPath = path.join(options.stateDir, PID_FILE);\n const runtimeTokenPath = path.join(options.stateDir, TOKEN_FILE);\n\n return async function launch(input: TunnelStartInput): Promise<TunnelStartResult> {\n if (input.signal?.aborted === true) {\n return { ok: false, failure: 'exited', message: 'Tunnel startup was cancelled.' };\n }\n const binary = options.cloudflaredPath ?? process.env[CLOUDFLARED_ENV] ?? (await findCloudflared());\n if (binary === undefined || binary === '') {\n return { ok: false, failure: 'not_installed', message: describeTunnelFailure('not_installed') };\n }\n\n let tokenPath: string | undefined;\n try {\n tokenPath = await writeTokenFile(input.config, options.stateDir);\n } catch (error) {\n const cause = error instanceof Error ? error.message : String(error);\n return { ok: false, failure: 'spawn_failed', message: `${describeTunnelFailure('spawn_failed')} ${cause}` };\n }\n const args = tunnelArgs(input.config, input.port, tokenPath);\n let child: ChildProcess;\n try {\n // Never detached and never unref'd. A detached child becomes a process\n // group leader that survives the terminal's SIGINT, which is exactly the\n // orphaned public tunnel this must avoid. Contrast serverSpawner.ts,\n // which detaches on purpose because sessions outlive the hub.\n child = spawnProcess(binary, args, { detached: false, stdio: ['ignore', 'pipe', 'pipe'] });\n } catch (error) {\n await fs.rm(runtimeTokenPath, { force: true });\n const cause = error instanceof Error ? error.message : String(error);\n return { ok: false, failure: 'spawn_failed', message: `${describeTunnelFailure('spawn_failed')} ${cause}` };\n }\n\n const lines: string[] = [];\n let stopped = false;\n let settle: ((result: TunnelStartResult) => void) | undefined;\n let selfTestStarted = false;\n const selfTestAbort = new AbortController();\n\n const killTree = (): void => {\n try {\n child.kill('SIGTERM');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ESRCH') notice(`Could not stop tunnel: ${String(error)}`);\n }\n };\n const onProcessExit = (): void => {\n // sync: Node does not await filesystem cleanup in the process exit handler.\n // This is the net under process.exit(), an uncaught exception, and an\n // unhandled rejection, none of which reach the graceful close path.\n rmSync(runtimeTokenPath, { force: true });\n try {\n if (child.pid !== undefined) process.kill(child.pid, 'SIGKILL');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ESRCH')\n notice(`Could not kill tunnel at exit: ${String(error)}`);\n }\n };\n process.once('exit', onProcessExit);\n\n const stop = async (): Promise<void> => {\n if (stopped) return;\n stopped = true;\n if (child.exitCode !== null || child.signalCode !== null) {\n process.off('exit', onProcessExit);\n await pidWrite;\n await fs.rm(pidPath, { force: true });\n await fs.rm(runtimeTokenPath, { force: true });\n return;\n }\n await new Promise<void>((resolve) => {\n const escalate = setTimeout(() => {\n try {\n child.kill('SIGKILL');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ESRCH') notice(`Could not kill tunnel: ${String(error)}`);\n }\n }, TERMINATE_GRACE_MS);\n child.once('exit', () => {\n clearTimeout(escalate);\n resolve();\n });\n killTree();\n });\n process.off('exit', onProcessExit);\n await pidWrite;\n await fs.rm(pidPath, { force: true });\n await fs.rm(runtimeTokenPath, { force: true });\n };\n\n // Register child listeners below without yielding, so early exits cannot be missed.\n // Cleanup waits for this ordered write before removing the record.\n const pidWrite = fs\n .mkdir(options.stateDir, { recursive: true, mode: 0o700 })\n .then(() =>\n fs.writeFile(\n pidPath,\n JSON.stringify({ pid: child.pid, ownerPid: process.pid, startedAt: Date.now(), command: binary }),\n { mode: FILE_MODE },\n ),\n )\n .catch((error: unknown) => {\n // The reaper is a safety net; report unavailable recovery without blocking startup.\n notice(`Could not write tunnel process record: ${String(error)}`);\n });\n\n const configuredOrigin = input.config.kind === 'named' ? `https://${input.config.hostname}` : undefined;\n let seenUrl = configuredOrigin;\n let seenConnection = false;\n\n const waitToRetry = async (): Promise<void> => {\n if (selfTestRetryMs <= 0) return;\n await new Promise<void>((resolve) => {\n const done = (): void => {\n clearTimeout(timer);\n selfTestAbort.signal.removeEventListener('abort', done);\n resolve();\n };\n const timer = setTimeout(done, selfTestRetryMs);\n selfTestAbort.signal.addEventListener('abort', done, { once: true });\n });\n };\n\n /** Proves end to end that pairing is reachable and that everything else is not. */\n const runSelfTest = async (origin: string): Promise<TunnelStartResult> => {\n let pairResponse: ProbeResult | undefined;\n let healthResponse: ProbeResult | undefined;\n let cause = 'fetch failed';\n for (let attempt = 0; attempt < SELF_TEST_ATTEMPTS && !selfTestAbort.signal.aborted; attempt += 1) {\n try {\n pairResponse = await probe(`${origin}${PAIRING_PAGE_ROUTE}`, selfTestAbort.signal);\n healthResponse = await probe(`${origin}/api/health`, selfTestAbort.signal);\n if (pairResponse.status !== EDGE_TUNNEL_UNAVAILABLE && healthResponse.status !== EDGE_TUNNEL_UNAVAILABLE)\n break;\n cause = `Cloudflare edge answered ${String(healthResponse.status)} before the connector registered`;\n } catch (error) {\n cause = error instanceof Error ? error.message : String(error);\n }\n if (attempt + 1 < SELF_TEST_ATTEMPTS && !selfTestAbort.signal.aborted) await waitToRetry();\n }\n if (pairResponse === undefined || healthResponse === undefined) {\n return { ok: false, failure: 'self_test_failed', message: `The tunnel did not answer: ${cause}` };\n }\n if (healthResponse.status !== UNAUTHORIZED) {\n // The agent would be on the public internet unauthenticated. This is\n // the one failure worth an automatic abort rather than a warning.\n const message = `${describeTunnelFailure('self_test_failed')} /api/health answered ${String(healthResponse.status)} through the tunnel.`;\n notice(message);\n return { ok: false, failure: 'self_test_failed', message };\n }\n if (pairResponse.status !== OK || !pairResponse.body.includes(PAIRING_PAGE_MARKER)) {\n const message = `The pairing page answered ${String(pairResponse.status)} through the tunnel.`;\n notice(message);\n return { ok: false, failure: 'self_test_failed', message };\n }\n notice(`tunnel ready at ${origin}`);\n return { ok: true, publicOrigin: origin, stop };\n };\n\n const consider = (chunk: string): void => {\n lines.push(chunk);\n if (settle === undefined || selfTestStarted) return;\n seenUrl ??= extractTunnelUrl(chunk);\n seenConnection ||= mentionsRegisteredConnection(chunk);\n if (seenUrl === undefined || !seenConnection) return;\n selfTestStarted = true;\n input.acceptOrigin?.(seenUrl);\n void runSelfTest(seenUrl).then((result) => settle?.(result));\n };\n\n child.stdout?.on('data', (data: Buffer) => consider(data.toString()));\n child.stderr?.on('data', (data: Buffer) => consider(data.toString()));\n\n return await new Promise<TunnelStartResult>((resolve) => {\n let deadline: ReturnType<typeof setTimeout>;\n const finish = (result: TunnelStartResult, stopProcess = !result.ok): void => {\n if (settle === undefined) return;\n settle = undefined;\n clearTimeout(deadline);\n input.signal?.removeEventListener('abort', cancel);\n selfTestAbort.abort();\n if (stopProcess) {\n void stop().then(() => resolve(result));\n } else {\n resolve(result);\n }\n };\n const cancel = (): void => {\n finish({ ok: false, failure: 'exited', message: 'Tunnel startup was cancelled.' });\n };\n settle = (result) => finish(result);\n deadline = setTimeout(() => {\n finish({\n ok: false,\n failure: 'timeout',\n message: `${describeTunnelFailure('timeout')}\\n${tailOf(lines)}`,\n });\n }, startTimeoutMs);\n input.signal?.addEventListener('abort', cancel, { once: true });\n\n child.once('error', (error) => {\n finish({\n ok: false,\n failure: 'spawn_failed',\n message: `${describeTunnelFailure('spawn_failed')} ${error.message}`,\n });\n });\n\n child.once('exit', async (code) => {\n process.off('exit', onProcessExit);\n await pidWrite;\n await fs.rm(runtimeTokenPath, { force: true }).catch((error: unknown) => {\n notice(`Tunnel token cleanup failed: ${error instanceof Error ? error.message : String(error)}`);\n });\n const message = `${describeTunnelFailure('exited')} (code ${String(code)})\\n${tailOf(lines)}`;\n if (settle !== undefined) {\n finish({ ok: false, failure: 'exited', message }, false);\n return;\n }\n // It came up and then died: network loss, an edge rejection, or a\n // self-update. The supervisor either replaces a named connector or tells\n // the host to fail closed rather than leave an unreachable cockpit enabled.\n if (!stopped) options.onExit?.(message);\n });\n\n // A named tunnel already knows its hostname, but it still waits for\n // cloudflared to register an edge connection before probing the public URL.\n if (input.signal?.aborted === true) cancel();\n });\n };\n}\n"],"mappings":"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"}
@@ -195,7 +195,9 @@ function createTunnelLauncher(options) {
195
195
  if (stopped) return;
196
196
  const outcome = await single(supervisedInput);
197
197
  if (stopped) {
198
- if (outcome.ok) await outcome.stop().catch(() => void 0);
198
+ if (outcome.ok) await outcome.stop().catch((error) => {
199
+ notice(`Could not stop recovered tunnel: ${String(error)}`);
200
+ });
199
201
  return;
200
202
  }
201
203
  if (!outcome.ok) {
@@ -236,8 +238,12 @@ function createTunnelLauncher(options) {
236
238
  recoveryAbort.abort();
237
239
  const recovering = recoveryPromise;
238
240
  stopPromise = (async () => {
239
- await activeStop?.().catch(() => void 0);
240
- await recovering?.catch(() => void 0);
241
+ await activeStop?.().catch((error) => {
242
+ notice(`Could not stop tunnel: ${String(error)}`);
243
+ });
244
+ await recovering?.catch((error) => {
245
+ notice(`Tunnel recovery cleanup failed: ${String(error)}`);
246
+ });
241
247
  await fs.rm(path.join(options.stateDir, PID_FILE), { force: true });
242
248
  await fs.rm(path.join(options.stateDir, TOKEN_FILE), { force: true });
243
249
  })();
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/services/tunnelProcess/index.ts"],"sourcesContent":["import { type ChildProcess, spawn } from 'node:child_process';\nimport { readFileSync, rmSync } from 'node:fs';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\n\nimport { PAIRING_PAGE_ROUTE } from '../../constants/remote';\nimport {\n type TunnelConfig,\n type TunnelLauncher,\n type TunnelStartInput,\n type TunnelStartResult,\n} from '../../types/remote';\nimport { PAIRING_PAGE_MARKER } from '../pairingPage';\nimport { describeTunnelFailure, extractTunnelUrl, mentionsRegisteredConnection, tunnelArgs } from '../tunnelOutput';\n\n/** Explicit binary path, ahead of a PATH scan. */\nexport const CLOUDFLARED_ENV = 'PO_CLOUDFLARED';\n// Give new quick-host DNS and negative caches time to settle, within a bounded startup.\nconst START_TIMEOUT_MS = 120_000;\nconst TERMINATE_GRACE_MS = 2000;\nconst SELF_TEST_ATTEMPTS = 240;\nconst SELF_TEST_RETRY_MS = 500;\nconst NAMED_RESTART_ATTEMPTS = 3;\nconst NAMED_RESTART_BASE_DELAY_MS = 500;\nconst NAMED_RESTART_MAX_DELAY_MS = 4000;\nconst NAMED_RESTART_STABLE_MS = 60_000;\n/** Lines of process output kept for a failure report. */\nconst LOG_TAIL = 20;\nconst UNAUTHORIZED = 401;\n/** Cloudflare has the DNS route but no connector has registered yet. */\nconst EDGE_TUNNEL_UNAVAILABLE = 530;\nconst OK = 200;\nconst PID_FILE = 'tunnel.pid';\nconst TOKEN_FILE = 'tunnel.token';\nconst FILE_MODE = 0o600;\n/** A pid file older than this is stale beyond usefulness and the pid has likely been recycled. */\nconst PID_FILE_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;\n\nexport interface ProbeResult {\n status: number;\n body: string;\n}\n\nexport interface TunnelProcessOptions {\n /** Explicit binary, ahead of the env var and the PATH scan. */\n cloudflaredPath?: string;\n stateDir: string;\n onNotice?: (message: string) => void;\n /** Test seam: stands in for node:child_process.spawn. */\n spawnProcess?: typeof spawn;\n /** Test seam: reaches the tunnel from outside, so the self-test needs no network. */\n probe?: (url: string, signal: AbortSignal) => Promise<ProbeResult>;\n /** Test seam: production waits briefly for a new public hostname to propagate. */\n selfTestRetryMs?: number;\n /** Test seam: production bounds the complete startup, including its self-test. */\n startTimeoutMs?: number;\n /** Test seam: production backs named-tunnel restarts off from 500 ms. */\n restartBaseDelayMs?: number;\n /** Test seam: production resets the rapid-crash budget after one stable minute. */\n restartStableMs?: number;\n /** Called after a quick tunnel exits, or after named-tunnel recovery is exhausted. */\n onExit?: (message: string) => void;\n}\n\n/**\n * Finds cloudflared without a shell.\n *\n * A manual scan rather than `which`: spawning a shell to locate a binary is a\n * quoting hazard for no benefit, and this also works the same on every platform.\n */\nexport async function findCloudflared(env: NodeJS.ProcessEnv = process.env): Promise<string | undefined> {\n const names = process.platform === 'win32' ? ['cloudflared.exe', 'cloudflared'] : ['cloudflared'];\n for (const directory of (env.PATH ?? '').split(path.delimiter)) {\n if (directory === '') continue;\n for (const name of names) {\n const candidate = path.join(directory, name);\n try {\n await fs.access(candidate, fs.constants.X_OK);\n return candidate;\n } catch {\n // Not here, or not executable; keep looking.\n }\n }\n }\n return undefined;\n}\n\nasync function defaultProbe(url: string, signal: AbortSignal): Promise<ProbeResult> {\n const response = await fetch(url, { redirect: 'manual', signal });\n return { status: response.status, body: await response.text() };\n}\n\n/**\n * Kills a tunnel left behind by a hub that could not clean up after itself.\n *\n * A SIGKILLed hub never runs its exit handler, and the child is not detached so\n * it is reparented rather than killed. It then holds the tunnel open against a\n * dead port. This is the sweep on the next start.\n *\n * The residual risk is pid reuse after a reboot. The age window narrows it and\n * on Linux the command line is checked; macOS has no equally cheap check, so\n * there the window stands.\n */\ninterface TunnelPidRecord {\n pid?: unknown;\n ownerPid?: unknown;\n startedAt?: unknown;\n}\n\nfunction validPid(value: unknown): value is number {\n return typeof value === 'number' && Number.isSafeInteger(value) && value > 0;\n}\n\n/** Only ESRCH proves the owner is gone; permission and platform errors fail safe. */\nfunction ownerMayBeAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (error) {\n return (error as NodeJS.ErrnoException).code !== 'ESRCH';\n }\n}\n\n// sync: exported createRemoteRuntime must reap stale public tunnels before its synchronous startup returns.\nexport function reapStaleTunnel(stateDir: string, onNotice: (message: string) => void): void {\n const pidPath = path.join(stateDir, PID_FILE);\n const tokenPath = path.join(stateDir, TOKEN_FILE);\n let raw: string;\n try {\n raw = readFileSync(pidPath, 'utf8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') rmSync(tokenPath, { force: true });\n return; // No pid file is the normal case; other read failures are unsafe to guess through.\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return; // A malformed record cannot prove that its owner is gone.\n }\n if (typeof parsed !== 'object' || parsed === null) return;\n const record = parsed as TunnelPidRecord;\n if (Object.hasOwn(record, 'ownerPid')) {\n if (!validPid(record.ownerPid) || ownerMayBeAlive(record.ownerPid)) return;\n }\n\n rmSync(tokenPath, { force: true });\n rmSync(pidPath, { force: true });\n const pid = validPid(record.pid) ? record.pid : undefined;\n const startedAt = typeof record.startedAt === 'number' ? record.startedAt : 0;\n if (pid === undefined || Date.now() - startedAt > PID_FILE_MAX_AGE_MS) return;\n if (process.platform === 'linux') {\n try {\n if (!readFileSync(`/proc/${String(pid)}/cmdline`, 'utf8').includes('cloudflared')) return;\n } catch {\n return; // The process is gone.\n }\n }\n try {\n process.kill(pid, 'SIGTERM');\n onNotice(`stopped a tunnel left running by a previous hub (pid ${String(pid)})`);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ESRCH') onNotice(`Could not stop stale tunnel: ${String(error)}`);\n }\n}\n\nfunction tailOf(lines: readonly string[]): string {\n return lines.slice(-LOG_TAIL).join('').trim();\n}\n\n/** Copies a named tunnel token to a private runtime file understood by cloudflared. */\nasync function writeTokenFile(config: TunnelConfig, stateDir: string): Promise<string | undefined> {\n if (config.kind !== 'named' || config.tokenFile === undefined) return undefined;\n let token: string;\n let sourcePath: string;\n try {\n sourcePath = await fs.realpath(config.tokenFile);\n token = (await fs.readFile(sourcePath, 'utf8')).trim();\n } catch {\n return undefined;\n }\n if (token === '') return undefined;\n await fs.mkdir(stateDir, { recursive: true, mode: 0o700 });\n const tokenPath = path.join(stateDir, TOKEN_FILE);\n let existingTarget: string | undefined;\n try {\n existingTarget = await fs.realpath(tokenPath);\n } catch (error) {\n // Missing is the normal state before launch; other failures must not hide an alias.\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;\n }\n if (sourcePath === path.resolve(tokenPath) || sourcePath === existingTarget) {\n throw new Error('The configured tunnel token file cannot also be Po runtime token storage.');\n }\n await fs.rm(tokenPath, { force: true });\n await fs.writeFile(tokenPath, token, { mode: FILE_MODE });\n await fs.chmod(tokenPath, FILE_MODE);\n return tokenPath;\n}\n\nexport function createTunnelLauncher(options: TunnelProcessOptions): TunnelLauncher {\n const notice = options.onNotice ?? ((): void => {});\n const restartBaseDelayMs = options.restartBaseDelayMs ?? NAMED_RESTART_BASE_DELAY_MS;\n const restartStableMs = options.restartStableMs ?? NAMED_RESTART_STABLE_MS;\n\n return async function launch(input: TunnelStartInput): Promise<TunnelStartResult> {\n let stopped = false;\n let activeStop: (() => Promise<void>) | undefined;\n let stopPromise: Promise<void> | undefined;\n let recoveryPromise: Promise<void> | undefined;\n let pendingExit: string | undefined;\n let restartCount = 0;\n let lastReadyAt = 0;\n const recoveryAbort = new AbortController();\n const signal =\n input.signal === undefined ? recoveryAbort.signal : AbortSignal.any([input.signal, recoveryAbort.signal]);\n const supervisedInput: TunnelStartInput = { ...input, signal };\n\n function queueUnexpectedExit(message: string): void {\n if (stopped) return;\n if (input.config.kind === 'quick') {\n options.onExit?.(message);\n return;\n }\n if (recoveryPromise !== undefined) {\n pendingExit = message;\n return;\n }\n beginRecovery(message);\n }\n\n const single = createSingleTunnelLauncher({ ...options, onExit: queueUnexpectedExit });\n\n async function waitForRestart(attempt: number): Promise<void> {\n if (recoveryAbort.signal.aborted) return;\n const delayMs = Math.min(restartBaseDelayMs * 2 ** (attempt - 1), NAMED_RESTART_MAX_DELAY_MS);\n await new Promise<void>((resolve) => {\n let timer: ReturnType<typeof setTimeout>;\n const done = (): void => {\n clearTimeout(timer);\n recoveryAbort.signal.removeEventListener('abort', done);\n resolve();\n };\n timer = setTimeout(done, delayMs);\n recoveryAbort.signal.addEventListener('abort', done, { once: true });\n if (recoveryAbort.signal.aborted) done();\n });\n }\n\n async function recover(initialMessage: string): Promise<void> {\n if (Date.now() - lastReadyAt >= restartStableMs) restartCount = 0;\n let lastFailure = initialMessage;\n while (!stopped && restartCount < NAMED_RESTART_ATTEMPTS) {\n restartCount += 1;\n notice(\n `cloudflared stopped; restarting named tunnel (attempt ${String(restartCount)} of ${String(NAMED_RESTART_ATTEMPTS)})`,\n );\n await waitForRestart(restartCount);\n if (stopped) return;\n\n const outcome = await single(supervisedInput);\n if (stopped) {\n if (outcome.ok) await outcome.stop().catch(() => undefined);\n return;\n }\n if (!outcome.ok) {\n lastFailure = outcome.message;\n continue;\n }\n activeStop = outcome.stop;\n lastReadyAt = Date.now();\n notice(`named tunnel recovered at ${outcome.publicOrigin}`);\n return;\n }\n\n if (!stopped) {\n options.onExit?.(\n `${initialMessage}\\nNamed tunnel recovery failed after ${String(NAMED_RESTART_ATTEMPTS)} attempts. Last failure: ${lastFailure}`,\n );\n }\n }\n\n function beginRecovery(message: string): void {\n recoveryPromise = recover(message)\n .catch((error: unknown) => {\n if (!stopped) {\n const cause = error instanceof Error ? error.message : String(error);\n options.onExit?.(`${message}\\nNamed tunnel recovery failed: ${cause}`);\n }\n })\n .finally(() => {\n recoveryPromise = undefined;\n const queued = pendingExit;\n pendingExit = undefined;\n if (queued !== undefined && !stopped) beginRecovery(queued);\n });\n }\n\n const initial = await single(supervisedInput);\n if (!initial.ok) {\n stopped = true;\n recoveryAbort.abort();\n return initial;\n }\n activeStop = initial.stop;\n lastReadyAt = Date.now();\n\n const stop = async (): Promise<void> => {\n if (stopPromise !== undefined) return await stopPromise;\n stopped = true;\n recoveryAbort.abort();\n const recovering = recoveryPromise;\n stopPromise = (async () => {\n await activeStop?.().catch(() => undefined);\n await recovering?.catch(() => undefined);\n await fs.rm(path.join(options.stateDir, PID_FILE), { force: true });\n await fs.rm(path.join(options.stateDir, TOKEN_FILE), { force: true });\n })();\n return await stopPromise;\n };\n\n return { ok: true, publicOrigin: initial.publicOrigin, stop };\n };\n}\n\n/** Starts one cloudflared child; the exported launcher supervises successive named-tunnel children. */\nfunction createSingleTunnelLauncher(options: TunnelProcessOptions): TunnelLauncher {\n const notice = options.onNotice ?? ((): void => {});\n const spawnProcess = options.spawnProcess ?? spawn;\n const probe = options.probe ?? defaultProbe;\n const selfTestRetryMs = options.selfTestRetryMs ?? SELF_TEST_RETRY_MS;\n const startTimeoutMs = options.startTimeoutMs ?? START_TIMEOUT_MS;\n const pidPath = path.join(options.stateDir, PID_FILE);\n const runtimeTokenPath = path.join(options.stateDir, TOKEN_FILE);\n\n return async function launch(input: TunnelStartInput): Promise<TunnelStartResult> {\n if (input.signal?.aborted === true) {\n return { ok: false, failure: 'exited', message: 'Tunnel startup was cancelled.' };\n }\n const binary = options.cloudflaredPath ?? process.env[CLOUDFLARED_ENV] ?? (await findCloudflared());\n if (binary === undefined || binary === '') {\n return { ok: false, failure: 'not_installed', message: describeTunnelFailure('not_installed') };\n }\n\n let tokenPath: string | undefined;\n try {\n tokenPath = await writeTokenFile(input.config, options.stateDir);\n } catch (error) {\n const cause = error instanceof Error ? error.message : String(error);\n return { ok: false, failure: 'spawn_failed', message: `${describeTunnelFailure('spawn_failed')} ${cause}` };\n }\n const args = tunnelArgs(input.config, input.port, tokenPath);\n let child: ChildProcess;\n try {\n // Never detached and never unref'd. A detached child becomes a process\n // group leader that survives the terminal's SIGINT, which is exactly the\n // orphaned public tunnel this must avoid. Contrast serverSpawner.ts,\n // which detaches on purpose because sessions outlive the hub.\n child = spawnProcess(binary, args, { detached: false, stdio: ['ignore', 'pipe', 'pipe'] });\n } catch (error) {\n await fs.rm(runtimeTokenPath, { force: true });\n const cause = error instanceof Error ? error.message : String(error);\n return { ok: false, failure: 'spawn_failed', message: `${describeTunnelFailure('spawn_failed')} ${cause}` };\n }\n\n const lines: string[] = [];\n let stopped = false;\n let settle: ((result: TunnelStartResult) => void) | undefined;\n let selfTestStarted = false;\n const selfTestAbort = new AbortController();\n\n const killTree = (): void => {\n try {\n child.kill('SIGTERM');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ESRCH') notice(`Could not stop tunnel: ${String(error)}`);\n }\n };\n const onProcessExit = (): void => {\n // sync: Node does not await filesystem cleanup in the process exit handler.\n // This is the net under process.exit(), an uncaught exception, and an\n // unhandled rejection, none of which reach the graceful close path.\n rmSync(runtimeTokenPath, { force: true });\n try {\n if (child.pid !== undefined) process.kill(child.pid, 'SIGKILL');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ESRCH')\n notice(`Could not kill tunnel at exit: ${String(error)}`);\n }\n };\n process.once('exit', onProcessExit);\n\n const stop = async (): Promise<void> => {\n if (stopped) return;\n stopped = true;\n if (child.exitCode !== null || child.signalCode !== null) {\n process.off('exit', onProcessExit);\n await pidWrite;\n await fs.rm(pidPath, { force: true });\n await fs.rm(runtimeTokenPath, { force: true });\n return;\n }\n await new Promise<void>((resolve) => {\n const escalate = setTimeout(() => {\n try {\n child.kill('SIGKILL');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ESRCH') notice(`Could not kill tunnel: ${String(error)}`);\n }\n }, TERMINATE_GRACE_MS);\n child.once('exit', () => {\n clearTimeout(escalate);\n resolve();\n });\n killTree();\n });\n process.off('exit', onProcessExit);\n await pidWrite;\n await fs.rm(pidPath, { force: true });\n await fs.rm(runtimeTokenPath, { force: true });\n };\n\n // Register child listeners below without yielding, so early exits cannot be missed.\n // Cleanup waits for this ordered write before removing the record.\n const pidWrite = fs\n .mkdir(options.stateDir, { recursive: true, mode: 0o700 })\n .then(() =>\n fs.writeFile(\n pidPath,\n JSON.stringify({ pid: child.pid, ownerPid: process.pid, startedAt: Date.now(), command: binary }),\n { mode: FILE_MODE },\n ),\n )\n .catch((error: unknown) => {\n // The reaper is a safety net; report unavailable recovery without blocking startup.\n notice(`Could not write tunnel process record: ${String(error)}`);\n });\n\n const configuredOrigin = input.config.kind === 'named' ? `https://${input.config.hostname}` : undefined;\n let seenUrl = configuredOrigin;\n let seenConnection = false;\n\n const waitToRetry = async (): Promise<void> => {\n if (selfTestRetryMs <= 0) return;\n await new Promise<void>((resolve) => {\n const done = (): void => {\n clearTimeout(timer);\n selfTestAbort.signal.removeEventListener('abort', done);\n resolve();\n };\n const timer = setTimeout(done, selfTestRetryMs);\n selfTestAbort.signal.addEventListener('abort', done, { once: true });\n });\n };\n\n /** Proves end to end that pairing is reachable and that everything else is not. */\n const runSelfTest = async (origin: string): Promise<TunnelStartResult> => {\n let pairResponse: ProbeResult | undefined;\n let healthResponse: ProbeResult | undefined;\n let cause = 'fetch failed';\n for (let attempt = 0; attempt < SELF_TEST_ATTEMPTS && !selfTestAbort.signal.aborted; attempt += 1) {\n try {\n pairResponse = await probe(`${origin}${PAIRING_PAGE_ROUTE}`, selfTestAbort.signal);\n healthResponse = await probe(`${origin}/api/health`, selfTestAbort.signal);\n if (pairResponse.status !== EDGE_TUNNEL_UNAVAILABLE && healthResponse.status !== EDGE_TUNNEL_UNAVAILABLE)\n break;\n cause = `Cloudflare edge answered ${String(healthResponse.status)} before the connector registered`;\n } catch (error) {\n cause = error instanceof Error ? error.message : String(error);\n }\n if (attempt + 1 < SELF_TEST_ATTEMPTS && !selfTestAbort.signal.aborted) await waitToRetry();\n }\n if (pairResponse === undefined || healthResponse === undefined) {\n return { ok: false, failure: 'self_test_failed', message: `The tunnel did not answer: ${cause}` };\n }\n if (healthResponse.status !== UNAUTHORIZED) {\n // The agent would be on the public internet unauthenticated. This is\n // the one failure worth an automatic abort rather than a warning.\n const message = `${describeTunnelFailure('self_test_failed')} /api/health answered ${String(healthResponse.status)} through the tunnel.`;\n notice(message);\n return { ok: false, failure: 'self_test_failed', message };\n }\n if (pairResponse.status !== OK || !pairResponse.body.includes(PAIRING_PAGE_MARKER)) {\n const message = `The pairing page answered ${String(pairResponse.status)} through the tunnel.`;\n notice(message);\n return { ok: false, failure: 'self_test_failed', message };\n }\n notice(`tunnel ready at ${origin}`);\n return { ok: true, publicOrigin: origin, stop };\n };\n\n const consider = (chunk: string): void => {\n lines.push(chunk);\n if (settle === undefined || selfTestStarted) return;\n seenUrl ??= extractTunnelUrl(chunk);\n seenConnection ||= mentionsRegisteredConnection(chunk);\n if (seenUrl === undefined || !seenConnection) return;\n selfTestStarted = true;\n input.acceptOrigin?.(seenUrl);\n void runSelfTest(seenUrl).then((result) => settle?.(result));\n };\n\n child.stdout?.on('data', (data: Buffer) => consider(data.toString()));\n child.stderr?.on('data', (data: Buffer) => consider(data.toString()));\n\n return await new Promise<TunnelStartResult>((resolve) => {\n let deadline: ReturnType<typeof setTimeout>;\n const finish = (result: TunnelStartResult, stopProcess = !result.ok): void => {\n if (settle === undefined) return;\n settle = undefined;\n clearTimeout(deadline);\n input.signal?.removeEventListener('abort', cancel);\n selfTestAbort.abort();\n if (stopProcess) {\n void stop().then(() => resolve(result));\n } else {\n resolve(result);\n }\n };\n const cancel = (): void => {\n finish({ ok: false, failure: 'exited', message: 'Tunnel startup was cancelled.' });\n };\n settle = (result) => finish(result);\n deadline = setTimeout(() => {\n finish({\n ok: false,\n failure: 'timeout',\n message: `${describeTunnelFailure('timeout')}\\n${tailOf(lines)}`,\n });\n }, startTimeoutMs);\n input.signal?.addEventListener('abort', cancel, { once: true });\n\n child.once('error', (error) => {\n finish({\n ok: false,\n failure: 'spawn_failed',\n message: `${describeTunnelFailure('spawn_failed')} ${error.message}`,\n });\n });\n\n child.once('exit', async (code) => {\n process.off('exit', onProcessExit);\n await pidWrite;\n await fs.rm(runtimeTokenPath, { force: true }).catch((error: unknown) => {\n notice(`Tunnel token cleanup failed: ${error instanceof Error ? error.message : String(error)}`);\n });\n const message = `${describeTunnelFailure('exited')} (code ${String(code)})\\n${tailOf(lines)}`;\n if (settle !== undefined) {\n finish({ ok: false, failure: 'exited', message }, false);\n return;\n }\n // It came up and then died: network loss, an edge rejection, or a\n // self-update. The supervisor either replaces a named connector or tells\n // the host to fail closed rather than leave an unreachable cockpit enabled.\n if (!stopped) options.onExit?.(message);\n });\n\n // A named tunnel already knows its hostname, but it still waits for\n // cloudflared to register an edge connection before probing the public URL.\n if (input.signal?.aborted === true) cancel();\n });\n };\n}\n"],"mappings":";;;;;;;;;AAgBA,MAAa,kBAAkB;AAE/B,MAAM,mBAAmB;AACzB,MAAM,qBAAqB;AAC3B,MAAM,qBAAqB;AAC3B,MAAM,qBAAqB;AAC3B,MAAM,yBAAyB;AAC/B,MAAM,8BAA8B;AACpC,MAAM,6BAA6B;AACnC,MAAM,0BAA0B;AAGhC,MAAM,eAAe;;AAErB,MAAM,0BAA0B;AAChC,MAAM,KAAK;AACX,MAAM,WAAW;AACjB,MAAM,aAAa;AACnB,MAAM,YAAY;;AAElB,MAAM,sBAAsB;;;;;;;AAkC5B,eAAsB,gBAAgB,MAAyB,QAAQ,KAAkC;CACvG,MAAM,QAAQ,QAAQ,aAAa,UAAU,CAAC,mBAAmB,aAAa,IAAI,CAAC,aAAa;CAChG,KAAK,MAAM,cAAc,IAAI,QAAQ,GAAA,CAAI,MAAM,KAAK,SAAS,GAAG;EAC9D,IAAI,cAAc,IAAI;EACtB,KAAK,MAAM,QAAQ,OAAO;GACxB,MAAM,YAAY,KAAK,KAAK,WAAW,IAAI;GAC3C,IAAI;IACF,MAAM,GAAG,OAAO,WAAW,GAAG,UAAU,IAAI;IAC5C,OAAO;GACT,QAAQ,CAER;EACF;CACF;AAEF;AAEA,eAAe,aAAa,KAAa,QAA2C;CAClF,MAAM,WAAW,MAAM,MAAM,KAAK;EAAE,UAAU;EAAU;CAAO,CAAC;CAChE,OAAO;EAAE,QAAQ,SAAS;EAAQ,MAAM,MAAM,SAAS,KAAK;CAAE;AAChE;AAmBA,SAAS,SAAS,OAAiC;CACjD,OAAO,OAAO,UAAU,YAAY,OAAO,cAAc,KAAK,KAAK,QAAQ;AAC7E;;AAGA,SAAS,gBAAgB,KAAsB;CAC7C,IAAI;EACF,QAAQ,KAAK,KAAK,CAAC;EACnB,OAAO;CACT,SAAS,OAAO;EACd,OAAQ,MAAgC,SAAS;CACnD;AACF;AAGA,SAAgB,gBAAgB,UAAkB,UAA2C;CAC3F,MAAM,UAAU,KAAK,KAAK,UAAU,QAAQ;CAC5C,MAAM,YAAY,KAAK,KAAK,UAAU,UAAU;CAChD,IAAI;CACJ,IAAI;EACF,MAAM,aAAa,SAAS,MAAM;CACpC,SAAS,OAAO;EACd,IAAK,MAAgC,SAAS,UAAU,OAAO,WAAW,EAAE,OAAO,KAAK,CAAC;EACzF;CACF;CACA,IAAI;CACJ,IAAI;EACF,SAAS,KAAK,MAAM,GAAG;CACzB,QAAQ;EACN;CACF;CACA,IAAI,OAAO,WAAW,YAAY,WAAW,MAAM;CACnD,MAAM,SAAS;CACf,IAAI,OAAO,OAAO,QAAQ,UAAU,GAC9B;MAAA,CAAC,SAAS,OAAO,QAAQ,KAAK,gBAAgB,OAAO,QAAQ,GAAG;CAAA;CAGtE,OAAO,WAAW,EAAE,OAAO,KAAK,CAAC;CACjC,OAAO,SAAS,EAAE,OAAO,KAAK,CAAC;CAC/B,MAAM,MAAM,SAAS,OAAO,GAAG,IAAI,OAAO,MAAM,KAAA;CAChD,MAAM,YAAY,OAAO,OAAO,cAAc,WAAW,OAAO,YAAY;CAC5E,IAAI,QAAQ,KAAA,KAAa,KAAK,IAAI,IAAI,YAAY,qBAAqB;CACvE,IAAI,QAAQ,aAAa,SACvB,IAAI;EACF,IAAI,CAAC,aAAa,SAAS,OAAO,GAAG,EAAE,WAAW,MAAM,CAAC,CAAC,SAAS,aAAa,GAAG;CACrF,QAAQ;EACN;CACF;CAEF,IAAI;EACF,QAAQ,KAAK,KAAK,SAAS;EAC3B,SAAS,wDAAwD,OAAO,GAAG,EAAE,EAAE;CACjF,SAAS,OAAO;EACd,IAAK,MAAgC,SAAS,SAAS,SAAS,gCAAgC,OAAO,KAAK,GAAG;CACjH;AACF;AAEA,SAAS,OAAO,OAAkC;CAChD,OAAO,MAAM,MAAM,GAAS,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK;AAC9C;;AAGA,eAAe,eAAe,QAAsB,UAA+C;CACjG,IAAI,OAAO,SAAS,WAAW,OAAO,cAAc,KAAA,GAAW,OAAO,KAAA;CACtE,IAAI;CACJ,IAAI;CACJ,IAAI;EACF,aAAa,MAAM,GAAG,SAAS,OAAO,SAAS;EAC/C,SAAS,MAAM,GAAG,SAAS,YAAY,MAAM,EAAA,CAAG,KAAK;CACvD,QAAQ;EACN;CACF;CACA,IAAI,UAAU,IAAI,OAAO,KAAA;CACzB,MAAM,GAAG,MAAM,UAAU;EAAE,WAAW;EAAM,MAAM;CAAM,CAAC;CACzD,MAAM,YAAY,KAAK,KAAK,UAAU,UAAU;CAChD,IAAI;CACJ,IAAI;EACF,iBAAiB,MAAM,GAAG,SAAS,SAAS;CAC9C,SAAS,OAAO;EAEd,IAAK,MAAgC,SAAS,UAAU,MAAM;CAChE;CACA,IAAI,eAAe,KAAK,QAAQ,SAAS,KAAK,eAAe,gBAC3D,MAAM,IAAI,MAAM,2EAA2E;CAE7F,MAAM,GAAG,GAAG,WAAW,EAAE,OAAO,KAAK,CAAC;CACtC,MAAM,GAAG,UAAU,WAAW,OAAO,EAAE,MAAM,UAAU,CAAC;CACxD,MAAM,GAAG,MAAM,WAAW,SAAS;CACnC,OAAO;AACT;AAEA,SAAgB,qBAAqB,SAA+C;CAClF,MAAM,SAAS,QAAQ,mBAAyB,CAAC;CACjD,MAAM,qBAAqB,QAAQ,sBAAsB;CACzD,MAAM,kBAAkB,QAAQ,mBAAmB;CAEnD,OAAO,eAAe,OAAO,OAAqD;EAChF,IAAI,UAAU;EACd,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI,eAAe;EACnB,IAAI,cAAc;EAClB,MAAM,gBAAgB,IAAI,gBAAgB;EAC1C,MAAM,SACJ,MAAM,WAAW,KAAA,IAAY,cAAc,SAAS,YAAY,IAAI,CAAC,MAAM,QAAQ,cAAc,MAAM,CAAC;EAC1G,MAAM,kBAAoC;GAAE,GAAG;GAAO;EAAO;EAE7D,SAAS,oBAAoB,SAAuB;GAClD,IAAI,SAAS;GACb,IAAI,MAAM,OAAO,SAAS,SAAS;IACjC,QAAQ,SAAS,OAAO;IACxB;GACF;GACA,IAAI,oBAAoB,KAAA,GAAW;IACjC,cAAc;IACd;GACF;GACA,cAAc,OAAO;EACvB;EAEA,MAAM,SAAS,2BAA2B;GAAE,GAAG;GAAS,QAAQ;EAAoB,CAAC;EAErF,eAAe,eAAe,SAAgC;GAC5D,IAAI,cAAc,OAAO,SAAS;GAClC,MAAM,UAAU,KAAK,IAAI,qBAAqB,MAAM,UAAU,IAAI,0BAA0B;GAC5F,MAAM,IAAI,SAAe,YAAY;IACnC,IAAI;IACJ,MAAM,aAAmB;KACvB,aAAa,KAAK;KAClB,cAAc,OAAO,oBAAoB,SAAS,IAAI;KACtD,QAAQ;IACV;IACA,QAAQ,WAAW,MAAM,OAAO;IAChC,cAAc,OAAO,iBAAiB,SAAS,MAAM,EAAE,MAAM,KAAK,CAAC;IACnE,IAAI,cAAc,OAAO,SAAS,KAAK;GACzC,CAAC;EACH;EAEA,eAAe,QAAQ,gBAAuC;GAC5D,IAAI,KAAK,IAAI,IAAI,eAAe,iBAAiB,eAAe;GAChE,IAAI,cAAc;GAClB,OAAO,CAAC,WAAW,eAAe,wBAAwB;IACxD,gBAAgB;IAChB,OACE,yDAAyD,OAAO,YAAY,EAAE,MAAM,OAAO,sBAAsB,EAAE,EACrH;IACA,MAAM,eAAe,YAAY;IACjC,IAAI,SAAS;IAEb,MAAM,UAAU,MAAM,OAAO,eAAe;IAC5C,IAAI,SAAS;KACX,IAAI,QAAQ,IAAI,MAAM,QAAQ,KAAK,CAAC,CAAC,YAAY,KAAA,CAAS;KAC1D;IACF;IACA,IAAI,CAAC,QAAQ,IAAI;KACf,cAAc,QAAQ;KACtB;IACF;IACA,aAAa,QAAQ;IACrB,cAAc,KAAK,IAAI;IACvB,OAAO,6BAA6B,QAAQ,cAAc;IAC1D;GACF;GAEA,IAAI,CAAC,SACH,QAAQ,SACN,GAAG,eAAe,uCAAuC,OAAO,sBAAsB,EAAE,2BAA2B,aACrH;EAEJ;EAEA,SAAS,cAAc,SAAuB;GAC5C,kBAAkB,QAAQ,OAAO,CAAC,CAC/B,OAAO,UAAmB;IACzB,IAAI,CAAC,SAAS;KACZ,MAAM,QAAQ,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;KACnE,QAAQ,SAAS,GAAG,QAAQ,kCAAkC,OAAO;IACvE;GACF,CAAC,CAAC,CACD,cAAc;IACb,kBAAkB,KAAA;IAClB,MAAM,SAAS;IACf,cAAc,KAAA;IACd,IAAI,WAAW,KAAA,KAAa,CAAC,SAAS,cAAc,MAAM;GAC5D,CAAC;EACL;EAEA,MAAM,UAAU,MAAM,OAAO,eAAe;EAC5C,IAAI,CAAC,QAAQ,IAAI;GACf,UAAU;GACV,cAAc,MAAM;GACpB,OAAO;EACT;EACA,aAAa,QAAQ;EACrB,cAAc,KAAK,IAAI;EAEvB,MAAM,OAAO,YAA2B;GACtC,IAAI,gBAAgB,KAAA,GAAW,OAAO,MAAM;GAC5C,UAAU;GACV,cAAc,MAAM;GACpB,MAAM,aAAa;GACnB,eAAe,YAAY;IACzB,MAAM,aAAa,CAAC,CAAC,YAAY,KAAA,CAAS;IAC1C,MAAM,YAAY,YAAY,KAAA,CAAS;IACvC,MAAM,GAAG,GAAG,KAAK,KAAK,QAAQ,UAAU,QAAQ,GAAG,EAAE,OAAO,KAAK,CAAC;IAClE,MAAM,GAAG,GAAG,KAAK,KAAK,QAAQ,UAAU,UAAU,GAAG,EAAE,OAAO,KAAK,CAAC;GACtE,EAAA,CAAG;GACH,OAAO,MAAM;EACf;EAEA,OAAO;GAAE,IAAI;GAAM,cAAc,QAAQ;GAAc;EAAK;CAC9D;AACF;;AAGA,SAAS,2BAA2B,SAA+C;CACjF,MAAM,SAAS,QAAQ,mBAAyB,CAAC;CACjD,MAAM,eAAe,QAAQ,gBAAgB;CAC7C,MAAM,QAAQ,QAAQ,SAAS;CAC/B,MAAM,kBAAkB,QAAQ,mBAAmB;CACnD,MAAM,iBAAiB,QAAQ,kBAAkB;CACjD,MAAM,UAAU,KAAK,KAAK,QAAQ,UAAU,QAAQ;CACpD,MAAM,mBAAmB,KAAK,KAAK,QAAQ,UAAU,UAAU;CAE/D,OAAO,eAAe,OAAO,OAAqD;EAChF,IAAI,MAAM,QAAQ,YAAY,MAC5B,OAAO;GAAE,IAAI;GAAO,SAAS;GAAU,SAAS;EAAgC;EAElF,MAAM,SAAS,QAAQ,mBAAmB,QAAQ,IAAA,qBAAyB,MAAM,gBAAgB;EACjG,IAAI,WAAW,KAAA,KAAa,WAAW,IACrC,OAAO;GAAE,IAAI;GAAO,SAAS;GAAiB,SAAS,sBAAsB,eAAe;EAAE;EAGhG,IAAI;EACJ,IAAI;GACF,YAAY,MAAM,eAAe,MAAM,QAAQ,QAAQ,QAAQ;EACjE,SAAS,OAAO;GACd,MAAM,QAAQ,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;GACnE,OAAO;IAAE,IAAI;IAAO,SAAS;IAAgB,SAAS,GAAG,sBAAsB,cAAc,EAAE,GAAG;GAAQ;EAC5G;EACA,MAAM,OAAO,WAAW,MAAM,QAAQ,MAAM,MAAM,SAAS;EAC3D,IAAI;EACJ,IAAI;GAKF,QAAQ,aAAa,QAAQ,MAAM;IAAE,UAAU;IAAO,OAAO;KAAC;KAAU;KAAQ;IAAM;GAAE,CAAC;EAC3F,SAAS,OAAO;GACd,MAAM,GAAG,GAAG,kBAAkB,EAAE,OAAO,KAAK,CAAC;GAC7C,MAAM,QAAQ,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;GACnE,OAAO;IAAE,IAAI;IAAO,SAAS;IAAgB,SAAS,GAAG,sBAAsB,cAAc,EAAE,GAAG;GAAQ;EAC5G;EAEA,MAAM,QAAkB,CAAC;EACzB,IAAI,UAAU;EACd,IAAI;EACJ,IAAI,kBAAkB;EACtB,MAAM,gBAAgB,IAAI,gBAAgB;EAE1C,MAAM,iBAAuB;GAC3B,IAAI;IACF,MAAM,KAAK,SAAS;GACtB,SAAS,OAAO;IACd,IAAK,MAAgC,SAAS,SAAS,OAAO,0BAA0B,OAAO,KAAK,GAAG;GACzG;EACF;EACA,MAAM,sBAA4B;GAIhC,OAAO,kBAAkB,EAAE,OAAO,KAAK,CAAC;GACxC,IAAI;IACF,IAAI,MAAM,QAAQ,KAAA,GAAW,QAAQ,KAAK,MAAM,KAAK,SAAS;GAChE,SAAS,OAAO;IACd,IAAK,MAAgC,SAAS,SAC5C,OAAO,kCAAkC,OAAO,KAAK,GAAG;GAC5D;EACF;EACA,QAAQ,KAAK,QAAQ,aAAa;EAElC,MAAM,OAAO,YAA2B;GACtC,IAAI,SAAS;GACb,UAAU;GACV,IAAI,MAAM,aAAa,QAAQ,MAAM,eAAe,MAAM;IACxD,QAAQ,IAAI,QAAQ,aAAa;IACjC,MAAM;IACN,MAAM,GAAG,GAAG,SAAS,EAAE,OAAO,KAAK,CAAC;IACpC,MAAM,GAAG,GAAG,kBAAkB,EAAE,OAAO,KAAK,CAAC;IAC7C;GACF;GACA,MAAM,IAAI,SAAe,YAAY;IACnC,MAAM,WAAW,iBAAiB;KAChC,IAAI;MACF,MAAM,KAAK,SAAS;KACtB,SAAS,OAAO;MACd,IAAK,MAAgC,SAAS,SAAS,OAAO,0BAA0B,OAAO,KAAK,GAAG;KACzG;IACF,GAAG,kBAAkB;IACrB,MAAM,KAAK,cAAc;KACvB,aAAa,QAAQ;KACrB,QAAQ;IACV,CAAC;IACD,SAAS;GACX,CAAC;GACD,QAAQ,IAAI,QAAQ,aAAa;GACjC,MAAM;GACN,MAAM,GAAG,GAAG,SAAS,EAAE,OAAO,KAAK,CAAC;GACpC,MAAM,GAAG,GAAG,kBAAkB,EAAE,OAAO,KAAK,CAAC;EAC/C;EAIA,MAAM,WAAW,GACd,MAAM,QAAQ,UAAU;GAAE,WAAW;GAAM,MAAM;EAAM,CAAC,CAAC,CACzD,WACC,GAAG,UACD,SACA,KAAK,UAAU;GAAE,KAAK,MAAM;GAAK,UAAU,QAAQ;GAAK,WAAW,KAAK,IAAI;GAAG,SAAS;EAAO,CAAC,GAChG,EAAE,MAAM,UAAU,CACpB,CACF,CAAC,CACA,OAAO,UAAmB;GAEzB,OAAO,0CAA0C,OAAO,KAAK,GAAG;EAClE,CAAC;EAGH,IAAI,UADqB,MAAM,OAAO,SAAS,UAAU,WAAW,MAAM,OAAO,aAAa,KAAA;EAE9F,IAAI,iBAAiB;EAErB,MAAM,cAAc,YAA2B;GAC7C,IAAI,mBAAmB,GAAG;GAC1B,MAAM,IAAI,SAAe,YAAY;IACnC,MAAM,aAAmB;KACvB,aAAa,KAAK;KAClB,cAAc,OAAO,oBAAoB,SAAS,IAAI;KACtD,QAAQ;IACV;IACA,MAAM,QAAQ,WAAW,MAAM,eAAe;IAC9C,cAAc,OAAO,iBAAiB,SAAS,MAAM,EAAE,MAAM,KAAK,CAAC;GACrE,CAAC;EACH;;EAGA,MAAM,cAAc,OAAO,WAA+C;GACxE,IAAI;GACJ,IAAI;GACJ,IAAI,QAAQ;GACZ,KAAK,IAAI,UAAU,GAAG,UAAU,sBAAsB,CAAC,cAAc,OAAO,SAAS,WAAW,GAAG;IACjG,IAAI;KACF,eAAe,MAAM,MAAM,GAAG,SAAS,sBAAsB,cAAc,MAAM;KACjF,iBAAiB,MAAM,MAAM,GAAG,OAAO,cAAc,cAAc,MAAM;KACzE,IAAI,aAAa,WAAW,2BAA2B,eAAe,WAAW,yBAC/E;KACF,QAAQ,4BAA4B,OAAO,eAAe,MAAM,EAAE;IACpE,SAAS,OAAO;KACd,QAAQ,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;IAC/D;IACA,IAAI,UAAU,IAAI,sBAAsB,CAAC,cAAc,OAAO,SAAS,MAAM,YAAY;GAC3F;GACA,IAAI,iBAAiB,KAAA,KAAa,mBAAmB,KAAA,GACnD,OAAO;IAAE,IAAI;IAAO,SAAS;IAAoB,SAAS,8BAA8B;GAAQ;GAElG,IAAI,eAAe,WAAW,cAAc;IAG1C,MAAM,UAAU,GAAG,sBAAsB,kBAAkB,EAAE,wBAAwB,OAAO,eAAe,MAAM,EAAE;IACnH,OAAO,OAAO;IACd,OAAO;KAAE,IAAI;KAAO,SAAS;KAAoB;IAAQ;GAC3D;GACA,IAAI,aAAa,WAAW,MAAM,CAAC,aAAa,KAAK,SAAA,iBAA4B,GAAG;IAClF,MAAM,UAAU,6BAA6B,OAAO,aAAa,MAAM,EAAE;IACzE,OAAO,OAAO;IACd,OAAO;KAAE,IAAI;KAAO,SAAS;KAAoB;IAAQ;GAC3D;GACA,OAAO,mBAAmB,QAAQ;GAClC,OAAO;IAAE,IAAI;IAAM,cAAc;IAAQ;GAAK;EAChD;EAEA,MAAM,YAAY,UAAwB;GACxC,MAAM,KAAK,KAAK;GAChB,IAAI,WAAW,KAAA,KAAa,iBAAiB;GAC7C,YAAY,iBAAiB,KAAK;GAClC,mBAAmB,6BAA6B,KAAK;GACrD,IAAI,YAAY,KAAA,KAAa,CAAC,gBAAgB;GAC9C,kBAAkB;GAClB,MAAM,eAAe,OAAO;GAC5B,YAAiB,OAAO,CAAC,CAAC,MAAM,WAAW,SAAS,MAAM,CAAC;EAC7D;EAEA,MAAM,QAAQ,GAAG,SAAS,SAAiB,SAAS,KAAK,SAAS,CAAC,CAAC;EACpE,MAAM,QAAQ,GAAG,SAAS,SAAiB,SAAS,KAAK,SAAS,CAAC,CAAC;EAEpE,OAAO,MAAM,IAAI,SAA4B,YAAY;GACvD,IAAI;GACJ,MAAM,UAAU,QAA2B,cAAc,CAAC,OAAO,OAAa;IAC5E,IAAI,WAAW,KAAA,GAAW;IAC1B,SAAS,KAAA;IACT,aAAa,QAAQ;IACrB,MAAM,QAAQ,oBAAoB,SAAS,MAAM;IACjD,cAAc,MAAM;IACpB,IAAI,aACF,KAAU,CAAC,CAAC,WAAW,QAAQ,MAAM,CAAC;SAEtC,QAAQ,MAAM;GAElB;GACA,MAAM,eAAqB;IACzB,OAAO;KAAE,IAAI;KAAO,SAAS;KAAU,SAAS;IAAgC,CAAC;GACnF;GACA,UAAU,WAAW,OAAO,MAAM;GAClC,WAAW,iBAAiB;IAC1B,OAAO;KACL,IAAI;KACJ,SAAS;KACT,SAAS,GAAG,sBAAsB,SAAS,EAAE,IAAI,OAAO,KAAK;IAC/D,CAAC;GACH,GAAG,cAAc;GACjB,MAAM,QAAQ,iBAAiB,SAAS,QAAQ,EAAE,MAAM,KAAK,CAAC;GAE9D,MAAM,KAAK,UAAU,UAAU;IAC7B,OAAO;KACL,IAAI;KACJ,SAAS;KACT,SAAS,GAAG,sBAAsB,cAAc,EAAE,GAAG,MAAM;IAC7D,CAAC;GACH,CAAC;GAED,MAAM,KAAK,QAAQ,OAAO,SAAS;IACjC,QAAQ,IAAI,QAAQ,aAAa;IACjC,MAAM;IACN,MAAM,GAAG,GAAG,kBAAkB,EAAE,OAAO,KAAK,CAAC,CAAC,CAAC,OAAO,UAAmB;KACvE,OAAO,gCAAgC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAAG;IACjG,CAAC;IACD,MAAM,UAAU,GAAG,sBAAsB,QAAQ,EAAE,SAAS,OAAO,IAAI,EAAE,KAAK,OAAO,KAAK;IAC1F,IAAI,WAAW,KAAA,GAAW;KACxB,OAAO;MAAE,IAAI;MAAO,SAAS;MAAU;KAAQ,GAAG,KAAK;KACvD;IACF;IAIA,IAAI,CAAC,SAAS,QAAQ,SAAS,OAAO;GACxC,CAAC;GAID,IAAI,MAAM,QAAQ,YAAY,MAAM,OAAO;EAC7C,CAAC;CACH;AACF"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/services/tunnelProcess/index.ts"],"sourcesContent":["import { type ChildProcess, spawn } from 'node:child_process';\nimport { readFileSync, rmSync } from 'node:fs';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\n\nimport { PAIRING_PAGE_ROUTE } from '../../constants/remote';\nimport {\n type TunnelConfig,\n type TunnelLauncher,\n type TunnelStartInput,\n type TunnelStartResult,\n} from '../../types/remote';\nimport { PAIRING_PAGE_MARKER } from '../pairingPage';\nimport { describeTunnelFailure, extractTunnelUrl, mentionsRegisteredConnection, tunnelArgs } from '../tunnelOutput';\n\n/** Explicit binary path, ahead of a PATH scan. */\nexport const CLOUDFLARED_ENV = 'PO_CLOUDFLARED';\n// Give new quick-host DNS and negative caches time to settle, within a bounded startup.\nconst START_TIMEOUT_MS = 120_000;\nconst TERMINATE_GRACE_MS = 2000;\nconst SELF_TEST_ATTEMPTS = 240;\nconst SELF_TEST_RETRY_MS = 500;\nconst NAMED_RESTART_ATTEMPTS = 3;\nconst NAMED_RESTART_BASE_DELAY_MS = 500;\nconst NAMED_RESTART_MAX_DELAY_MS = 4000;\nconst NAMED_RESTART_STABLE_MS = 60_000;\n/** Lines of process output kept for a failure report. */\nconst LOG_TAIL = 20;\nconst UNAUTHORIZED = 401;\n/** Cloudflare has the DNS route but no connector has registered yet. */\nconst EDGE_TUNNEL_UNAVAILABLE = 530;\nconst OK = 200;\nconst PID_FILE = 'tunnel.pid';\nconst TOKEN_FILE = 'tunnel.token';\nconst FILE_MODE = 0o600;\n/** A pid file older than this is stale beyond usefulness and the pid has likely been recycled. */\nconst PID_FILE_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;\n\nexport interface ProbeResult {\n status: number;\n body: string;\n}\n\nexport interface TunnelProcessOptions {\n /** Explicit binary, ahead of the env var and the PATH scan. */\n cloudflaredPath?: string;\n stateDir: string;\n onNotice?: (message: string) => void;\n /** Test seam: stands in for node:child_process.spawn. */\n spawnProcess?: typeof spawn;\n /** Test seam: reaches the tunnel from outside, so the self-test needs no network. */\n probe?: (url: string, signal: AbortSignal) => Promise<ProbeResult>;\n /** Test seam: production waits briefly for a new public hostname to propagate. */\n selfTestRetryMs?: number;\n /** Test seam: production bounds the complete startup, including its self-test. */\n startTimeoutMs?: number;\n /** Test seam: production backs named-tunnel restarts off from 500 ms. */\n restartBaseDelayMs?: number;\n /** Test seam: production resets the rapid-crash budget after one stable minute. */\n restartStableMs?: number;\n /** Called after a quick tunnel exits, or after named-tunnel recovery is exhausted. */\n onExit?: (message: string) => void;\n}\n\n/**\n * Finds cloudflared without a shell.\n *\n * A manual scan rather than `which`: spawning a shell to locate a binary is a\n * quoting hazard for no benefit, and this also works the same on every platform.\n */\nexport async function findCloudflared(env: NodeJS.ProcessEnv = process.env): Promise<string | undefined> {\n const names = process.platform === 'win32' ? ['cloudflared.exe', 'cloudflared'] : ['cloudflared'];\n for (const directory of (env.PATH ?? '').split(path.delimiter)) {\n if (directory === '') continue;\n for (const name of names) {\n const candidate = path.join(directory, name);\n try {\n await fs.access(candidate, fs.constants.X_OK);\n return candidate;\n } catch {\n // Not here, or not executable; keep looking.\n }\n }\n }\n return undefined;\n}\n\nasync function defaultProbe(url: string, signal: AbortSignal): Promise<ProbeResult> {\n const response = await fetch(url, { redirect: 'manual', signal });\n return { status: response.status, body: await response.text() };\n}\n\n/**\n * Kills a tunnel left behind by a hub that could not clean up after itself.\n *\n * A SIGKILLed hub never runs its exit handler, and the child is not detached so\n * it is reparented rather than killed. It then holds the tunnel open against a\n * dead port. This is the sweep on the next start.\n *\n * The residual risk is pid reuse after a reboot. The age window narrows it and\n * on Linux the command line is checked; macOS has no equally cheap check, so\n * there the window stands.\n */\ninterface TunnelPidRecord {\n pid?: unknown;\n ownerPid?: unknown;\n startedAt?: unknown;\n}\n\nfunction validPid(value: unknown): value is number {\n return typeof value === 'number' && Number.isSafeInteger(value) && value > 0;\n}\n\n/** Only ESRCH proves the owner is gone; permission and platform errors fail safe. */\nfunction ownerMayBeAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (error) {\n return (error as NodeJS.ErrnoException).code !== 'ESRCH';\n }\n}\n\n// sync: exported createRemoteRuntime must reap stale public tunnels before its synchronous startup returns.\nexport function reapStaleTunnel(stateDir: string, onNotice: (message: string) => void): void {\n const pidPath = path.join(stateDir, PID_FILE);\n const tokenPath = path.join(stateDir, TOKEN_FILE);\n let raw: string;\n try {\n raw = readFileSync(pidPath, 'utf8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') rmSync(tokenPath, { force: true });\n return; // No pid file is the normal case; other read failures are unsafe to guess through.\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return; // A malformed record cannot prove that its owner is gone.\n }\n if (typeof parsed !== 'object' || parsed === null) return;\n const record = parsed as TunnelPidRecord;\n if (Object.hasOwn(record, 'ownerPid')) {\n if (!validPid(record.ownerPid) || ownerMayBeAlive(record.ownerPid)) return;\n }\n\n rmSync(tokenPath, { force: true });\n rmSync(pidPath, { force: true });\n const pid = validPid(record.pid) ? record.pid : undefined;\n const startedAt = typeof record.startedAt === 'number' ? record.startedAt : 0;\n if (pid === undefined || Date.now() - startedAt > PID_FILE_MAX_AGE_MS) return;\n if (process.platform === 'linux') {\n try {\n if (!readFileSync(`/proc/${String(pid)}/cmdline`, 'utf8').includes('cloudflared')) return;\n } catch {\n return; // The process is gone.\n }\n }\n try {\n process.kill(pid, 'SIGTERM');\n onNotice(`stopped a tunnel left running by a previous hub (pid ${String(pid)})`);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ESRCH') onNotice(`Could not stop stale tunnel: ${String(error)}`);\n }\n}\n\nfunction tailOf(lines: readonly string[]): string {\n return lines.slice(-LOG_TAIL).join('').trim();\n}\n\n/** Copies a named tunnel token to a private runtime file understood by cloudflared. */\nasync function writeTokenFile(config: TunnelConfig, stateDir: string): Promise<string | undefined> {\n if (config.kind !== 'named' || config.tokenFile === undefined) return undefined;\n let token: string;\n let sourcePath: string;\n try {\n sourcePath = await fs.realpath(config.tokenFile);\n token = (await fs.readFile(sourcePath, 'utf8')).trim();\n } catch {\n return undefined;\n }\n if (token === '') return undefined;\n await fs.mkdir(stateDir, { recursive: true, mode: 0o700 });\n const tokenPath = path.join(stateDir, TOKEN_FILE);\n let existingTarget: string | undefined;\n try {\n existingTarget = await fs.realpath(tokenPath);\n } catch (error) {\n // Missing is the normal state before launch; other failures must not hide an alias.\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;\n }\n if (sourcePath === path.resolve(tokenPath) || sourcePath === existingTarget) {\n throw new Error('The configured tunnel token file cannot also be Po runtime token storage.');\n }\n await fs.rm(tokenPath, { force: true });\n await fs.writeFile(tokenPath, token, { mode: FILE_MODE });\n await fs.chmod(tokenPath, FILE_MODE);\n return tokenPath;\n}\n\nexport function createTunnelLauncher(options: TunnelProcessOptions): TunnelLauncher {\n const notice = options.onNotice ?? ((): void => {});\n const restartBaseDelayMs = options.restartBaseDelayMs ?? NAMED_RESTART_BASE_DELAY_MS;\n const restartStableMs = options.restartStableMs ?? NAMED_RESTART_STABLE_MS;\n\n return async function launch(input: TunnelStartInput): Promise<TunnelStartResult> {\n let stopped = false;\n let activeStop: (() => Promise<void>) | undefined;\n let stopPromise: Promise<void> | undefined;\n let recoveryPromise: Promise<void> | undefined;\n let pendingExit: string | undefined;\n let restartCount = 0;\n let lastReadyAt = 0;\n const recoveryAbort = new AbortController();\n const signal =\n input.signal === undefined ? recoveryAbort.signal : AbortSignal.any([input.signal, recoveryAbort.signal]);\n const supervisedInput: TunnelStartInput = { ...input, signal };\n\n function queueUnexpectedExit(message: string): void {\n if (stopped) return;\n if (input.config.kind === 'quick') {\n options.onExit?.(message);\n return;\n }\n if (recoveryPromise !== undefined) {\n pendingExit = message;\n return;\n }\n beginRecovery(message);\n }\n\n const single = createSingleTunnelLauncher({ ...options, onExit: queueUnexpectedExit });\n\n async function waitForRestart(attempt: number): Promise<void> {\n if (recoveryAbort.signal.aborted) return;\n const delayMs = Math.min(restartBaseDelayMs * 2 ** (attempt - 1), NAMED_RESTART_MAX_DELAY_MS);\n await new Promise<void>((resolve) => {\n let timer: ReturnType<typeof setTimeout>;\n const done = (): void => {\n clearTimeout(timer);\n recoveryAbort.signal.removeEventListener('abort', done);\n resolve();\n };\n timer = setTimeout(done, delayMs);\n recoveryAbort.signal.addEventListener('abort', done, { once: true });\n if (recoveryAbort.signal.aborted) done();\n });\n }\n\n async function recover(initialMessage: string): Promise<void> {\n if (Date.now() - lastReadyAt >= restartStableMs) restartCount = 0;\n let lastFailure = initialMessage;\n while (!stopped && restartCount < NAMED_RESTART_ATTEMPTS) {\n restartCount += 1;\n notice(\n `cloudflared stopped; restarting named tunnel (attempt ${String(restartCount)} of ${String(NAMED_RESTART_ATTEMPTS)})`,\n );\n await waitForRestart(restartCount);\n if (stopped) return;\n\n const outcome = await single(supervisedInput);\n if (stopped) {\n if (outcome.ok) {\n await outcome.stop().catch((error: unknown) => {\n notice(`Could not stop recovered tunnel: ${String(error)}`);\n });\n }\n return;\n }\n if (!outcome.ok) {\n lastFailure = outcome.message;\n continue;\n }\n activeStop = outcome.stop;\n lastReadyAt = Date.now();\n notice(`named tunnel recovered at ${outcome.publicOrigin}`);\n return;\n }\n\n if (!stopped) {\n options.onExit?.(\n `${initialMessage}\\nNamed tunnel recovery failed after ${String(NAMED_RESTART_ATTEMPTS)} attempts. Last failure: ${lastFailure}`,\n );\n }\n }\n\n function beginRecovery(message: string): void {\n recoveryPromise = recover(message)\n .catch((error: unknown) => {\n if (!stopped) {\n const cause = error instanceof Error ? error.message : String(error);\n options.onExit?.(`${message}\\nNamed tunnel recovery failed: ${cause}`);\n }\n })\n .finally(() => {\n recoveryPromise = undefined;\n const queued = pendingExit;\n pendingExit = undefined;\n if (queued !== undefined && !stopped) beginRecovery(queued);\n });\n }\n\n const initial = await single(supervisedInput);\n if (!initial.ok) {\n stopped = true;\n recoveryAbort.abort();\n return initial;\n }\n activeStop = initial.stop;\n lastReadyAt = Date.now();\n\n const stop = async (): Promise<void> => {\n if (stopPromise !== undefined) return await stopPromise;\n stopped = true;\n recoveryAbort.abort();\n const recovering = recoveryPromise;\n stopPromise = (async () => {\n await activeStop?.().catch((error: unknown) => {\n notice(`Could not stop tunnel: ${String(error)}`);\n });\n await recovering?.catch((error: unknown) => {\n notice(`Tunnel recovery cleanup failed: ${String(error)}`);\n });\n await fs.rm(path.join(options.stateDir, PID_FILE), { force: true });\n await fs.rm(path.join(options.stateDir, TOKEN_FILE), { force: true });\n })();\n return await stopPromise;\n };\n\n return { ok: true, publicOrigin: initial.publicOrigin, stop };\n };\n}\n\n/** Starts one cloudflared child; the exported launcher supervises successive named-tunnel children. */\nfunction createSingleTunnelLauncher(options: TunnelProcessOptions): TunnelLauncher {\n const notice = options.onNotice ?? ((): void => {});\n const spawnProcess = options.spawnProcess ?? spawn;\n const probe = options.probe ?? defaultProbe;\n const selfTestRetryMs = options.selfTestRetryMs ?? SELF_TEST_RETRY_MS;\n const startTimeoutMs = options.startTimeoutMs ?? START_TIMEOUT_MS;\n const pidPath = path.join(options.stateDir, PID_FILE);\n const runtimeTokenPath = path.join(options.stateDir, TOKEN_FILE);\n\n return async function launch(input: TunnelStartInput): Promise<TunnelStartResult> {\n if (input.signal?.aborted === true) {\n return { ok: false, failure: 'exited', message: 'Tunnel startup was cancelled.' };\n }\n const binary = options.cloudflaredPath ?? process.env[CLOUDFLARED_ENV] ?? (await findCloudflared());\n if (binary === undefined || binary === '') {\n return { ok: false, failure: 'not_installed', message: describeTunnelFailure('not_installed') };\n }\n\n let tokenPath: string | undefined;\n try {\n tokenPath = await writeTokenFile(input.config, options.stateDir);\n } catch (error) {\n const cause = error instanceof Error ? error.message : String(error);\n return { ok: false, failure: 'spawn_failed', message: `${describeTunnelFailure('spawn_failed')} ${cause}` };\n }\n const args = tunnelArgs(input.config, input.port, tokenPath);\n let child: ChildProcess;\n try {\n // Never detached and never unref'd. A detached child becomes a process\n // group leader that survives the terminal's SIGINT, which is exactly the\n // orphaned public tunnel this must avoid. Contrast serverSpawner.ts,\n // which detaches on purpose because sessions outlive the hub.\n child = spawnProcess(binary, args, { detached: false, stdio: ['ignore', 'pipe', 'pipe'] });\n } catch (error) {\n await fs.rm(runtimeTokenPath, { force: true });\n const cause = error instanceof Error ? error.message : String(error);\n return { ok: false, failure: 'spawn_failed', message: `${describeTunnelFailure('spawn_failed')} ${cause}` };\n }\n\n const lines: string[] = [];\n let stopped = false;\n let settle: ((result: TunnelStartResult) => void) | undefined;\n let selfTestStarted = false;\n const selfTestAbort = new AbortController();\n\n const killTree = (): void => {\n try {\n child.kill('SIGTERM');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ESRCH') notice(`Could not stop tunnel: ${String(error)}`);\n }\n };\n const onProcessExit = (): void => {\n // sync: Node does not await filesystem cleanup in the process exit handler.\n // This is the net under process.exit(), an uncaught exception, and an\n // unhandled rejection, none of which reach the graceful close path.\n rmSync(runtimeTokenPath, { force: true });\n try {\n if (child.pid !== undefined) process.kill(child.pid, 'SIGKILL');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ESRCH')\n notice(`Could not kill tunnel at exit: ${String(error)}`);\n }\n };\n process.once('exit', onProcessExit);\n\n const stop = async (): Promise<void> => {\n if (stopped) return;\n stopped = true;\n if (child.exitCode !== null || child.signalCode !== null) {\n process.off('exit', onProcessExit);\n await pidWrite;\n await fs.rm(pidPath, { force: true });\n await fs.rm(runtimeTokenPath, { force: true });\n return;\n }\n await new Promise<void>((resolve) => {\n const escalate = setTimeout(() => {\n try {\n child.kill('SIGKILL');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ESRCH') notice(`Could not kill tunnel: ${String(error)}`);\n }\n }, TERMINATE_GRACE_MS);\n child.once('exit', () => {\n clearTimeout(escalate);\n resolve();\n });\n killTree();\n });\n process.off('exit', onProcessExit);\n await pidWrite;\n await fs.rm(pidPath, { force: true });\n await fs.rm(runtimeTokenPath, { force: true });\n };\n\n // Register child listeners below without yielding, so early exits cannot be missed.\n // Cleanup waits for this ordered write before removing the record.\n const pidWrite = fs\n .mkdir(options.stateDir, { recursive: true, mode: 0o700 })\n .then(() =>\n fs.writeFile(\n pidPath,\n JSON.stringify({ pid: child.pid, ownerPid: process.pid, startedAt: Date.now(), command: binary }),\n { mode: FILE_MODE },\n ),\n )\n .catch((error: unknown) => {\n // The reaper is a safety net; report unavailable recovery without blocking startup.\n notice(`Could not write tunnel process record: ${String(error)}`);\n });\n\n const configuredOrigin = input.config.kind === 'named' ? `https://${input.config.hostname}` : undefined;\n let seenUrl = configuredOrigin;\n let seenConnection = false;\n\n const waitToRetry = async (): Promise<void> => {\n if (selfTestRetryMs <= 0) return;\n await new Promise<void>((resolve) => {\n const done = (): void => {\n clearTimeout(timer);\n selfTestAbort.signal.removeEventListener('abort', done);\n resolve();\n };\n const timer = setTimeout(done, selfTestRetryMs);\n selfTestAbort.signal.addEventListener('abort', done, { once: true });\n });\n };\n\n /** Proves end to end that pairing is reachable and that everything else is not. */\n const runSelfTest = async (origin: string): Promise<TunnelStartResult> => {\n let pairResponse: ProbeResult | undefined;\n let healthResponse: ProbeResult | undefined;\n let cause = 'fetch failed';\n for (let attempt = 0; attempt < SELF_TEST_ATTEMPTS && !selfTestAbort.signal.aborted; attempt += 1) {\n try {\n pairResponse = await probe(`${origin}${PAIRING_PAGE_ROUTE}`, selfTestAbort.signal);\n healthResponse = await probe(`${origin}/api/health`, selfTestAbort.signal);\n if (pairResponse.status !== EDGE_TUNNEL_UNAVAILABLE && healthResponse.status !== EDGE_TUNNEL_UNAVAILABLE)\n break;\n cause = `Cloudflare edge answered ${String(healthResponse.status)} before the connector registered`;\n } catch (error) {\n cause = error instanceof Error ? error.message : String(error);\n }\n if (attempt + 1 < SELF_TEST_ATTEMPTS && !selfTestAbort.signal.aborted) await waitToRetry();\n }\n if (pairResponse === undefined || healthResponse === undefined) {\n return { ok: false, failure: 'self_test_failed', message: `The tunnel did not answer: ${cause}` };\n }\n if (healthResponse.status !== UNAUTHORIZED) {\n // The agent would be on the public internet unauthenticated. This is\n // the one failure worth an automatic abort rather than a warning.\n const message = `${describeTunnelFailure('self_test_failed')} /api/health answered ${String(healthResponse.status)} through the tunnel.`;\n notice(message);\n return { ok: false, failure: 'self_test_failed', message };\n }\n if (pairResponse.status !== OK || !pairResponse.body.includes(PAIRING_PAGE_MARKER)) {\n const message = `The pairing page answered ${String(pairResponse.status)} through the tunnel.`;\n notice(message);\n return { ok: false, failure: 'self_test_failed', message };\n }\n notice(`tunnel ready at ${origin}`);\n return { ok: true, publicOrigin: origin, stop };\n };\n\n const consider = (chunk: string): void => {\n lines.push(chunk);\n if (settle === undefined || selfTestStarted) return;\n seenUrl ??= extractTunnelUrl(chunk);\n seenConnection ||= mentionsRegisteredConnection(chunk);\n if (seenUrl === undefined || !seenConnection) return;\n selfTestStarted = true;\n input.acceptOrigin?.(seenUrl);\n void runSelfTest(seenUrl).then((result) => settle?.(result));\n };\n\n child.stdout?.on('data', (data: Buffer) => consider(data.toString()));\n child.stderr?.on('data', (data: Buffer) => consider(data.toString()));\n\n return await new Promise<TunnelStartResult>((resolve) => {\n let deadline: ReturnType<typeof setTimeout>;\n const finish = (result: TunnelStartResult, stopProcess = !result.ok): void => {\n if (settle === undefined) return;\n settle = undefined;\n clearTimeout(deadline);\n input.signal?.removeEventListener('abort', cancel);\n selfTestAbort.abort();\n if (stopProcess) {\n void stop().then(() => resolve(result));\n } else {\n resolve(result);\n }\n };\n const cancel = (): void => {\n finish({ ok: false, failure: 'exited', message: 'Tunnel startup was cancelled.' });\n };\n settle = (result) => finish(result);\n deadline = setTimeout(() => {\n finish({\n ok: false,\n failure: 'timeout',\n message: `${describeTunnelFailure('timeout')}\\n${tailOf(lines)}`,\n });\n }, startTimeoutMs);\n input.signal?.addEventListener('abort', cancel, { once: true });\n\n child.once('error', (error) => {\n finish({\n ok: false,\n failure: 'spawn_failed',\n message: `${describeTunnelFailure('spawn_failed')} ${error.message}`,\n });\n });\n\n child.once('exit', async (code) => {\n process.off('exit', onProcessExit);\n await pidWrite;\n await fs.rm(runtimeTokenPath, { force: true }).catch((error: unknown) => {\n notice(`Tunnel token cleanup failed: ${error instanceof Error ? error.message : String(error)}`);\n });\n const message = `${describeTunnelFailure('exited')} (code ${String(code)})\\n${tailOf(lines)}`;\n if (settle !== undefined) {\n finish({ ok: false, failure: 'exited', message }, false);\n return;\n }\n // It came up and then died: network loss, an edge rejection, or a\n // self-update. The supervisor either replaces a named connector or tells\n // the host to fail closed rather than leave an unreachable cockpit enabled.\n if (!stopped) options.onExit?.(message);\n });\n\n // A named tunnel already knows its hostname, but it still waits for\n // cloudflared to register an edge connection before probing the public URL.\n if (input.signal?.aborted === true) cancel();\n });\n };\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import { PO_WORKFLOW_ADMISSIONS_SERVICE, PO_WORKFLOW_CONTEXT_SERVICE, PO_WORKFLOW_DEFINITION_SOURCES_SERVICE, PO_WORKFLOW_LAUNCH_SERVICE, PO_WORKFLOW_RUNS_SERVICE, PoWorkflowAdmission, PoWorkflowAdmissionOverrides, PoWorkflowAdmissionRequest, PoWorkflowAdmissionsService, PoWorkflowChildInvocation, PoWorkflowChildrenSession, PoWorkflowChildrenStatus, PoWorkflowConfig, PoWorkflowContextService, PoWorkflowCriterionState, PoWorkflowDefinitionRef, PoWorkflowDefinitionRefSchema, PoWorkflowDefinitionSource, PoWorkflowDefinitionSourceHandle, PoWorkflowDefinitionSourcesService, PoWorkflowExecutionPolicyRequest, PoWorkflowExecutionPolicyResult, PoWorkflowJobState, PoWorkflowLaunchOverrides, PoWorkflowLaunchService, PoWorkflowOperationEpoch, PoWorkflowOutcome, PoWorkflowPolicyAssociation, PoWorkflowPolicyBinding, PoWorkflowProgressState, PoWorkflowRecoveryOptions, PoWorkflowRunRef, PoWorkflowRunRefSchema, PoWorkflowRunState, PoWorkflowRunsService, PoWorkflowStage, PoWorkflowStepState, PoWorkflowValidationReport, readPoWorkflowAdmissionsService, readPoWorkflowContextService, readPoWorkflowDefinitionSourcesService, readPoWorkflowLaunchService, readPoWorkflowRunsService } from "./src/schemas/workflow.mjs";
2
- export { PO_WORKFLOW_ADMISSIONS_SERVICE, PO_WORKFLOW_CONTEXT_SERVICE, PO_WORKFLOW_DEFINITION_SOURCES_SERVICE, PO_WORKFLOW_LAUNCH_SERVICE, PO_WORKFLOW_RUNS_SERVICE, type PoWorkflowAdmission, type PoWorkflowAdmissionOverrides, type PoWorkflowAdmissionRequest, type PoWorkflowAdmissionsService, type PoWorkflowChildInvocation, type PoWorkflowChildrenSession, type PoWorkflowChildrenStatus, type PoWorkflowConfig, type PoWorkflowContextService, type PoWorkflowCriterionState, type PoWorkflowDefinitionRef, PoWorkflowDefinitionRefSchema, type PoWorkflowDefinitionSource, type PoWorkflowDefinitionSourceHandle, type PoWorkflowDefinitionSourcesService, type PoWorkflowExecutionPolicyRequest, type PoWorkflowExecutionPolicyResult, type PoWorkflowJobState, type PoWorkflowLaunchOverrides, type PoWorkflowLaunchService, type PoWorkflowOperationEpoch, type PoWorkflowOutcome, type PoWorkflowPolicyAssociation, type PoWorkflowPolicyBinding, type PoWorkflowProgressState, type PoWorkflowRecoveryOptions, type PoWorkflowRunRef, PoWorkflowRunRefSchema, type PoWorkflowRunState, type PoWorkflowRunsService, type PoWorkflowStage, type PoWorkflowStepState, type PoWorkflowValidationReport, readPoWorkflowAdmissionsService, readPoWorkflowContextService, readPoWorkflowDefinitionSourcesService, readPoWorkflowLaunchService, readPoWorkflowRunsService };
1
+ import { PO_WORKFLOW_ADMISSIONS_SERVICE, PO_WORKFLOW_CONTEXT_SERVICE, PO_WORKFLOW_DEFINITION_SOURCES_SERVICE, PO_WORKFLOW_LAUNCH_SERVICE, PO_WORKFLOW_RUNS_SERVICE, PoWorkflowAdmission, PoWorkflowAdmissionOverrides, PoWorkflowAdmissionRequest, PoWorkflowAdmissionsService, PoWorkflowChildInvocation, PoWorkflowChildrenSession, PoWorkflowChildrenStatus, PoWorkflowConfig, PoWorkflowContextService, PoWorkflowCriterionState, PoWorkflowDefinitionRef, PoWorkflowDefinitionRefSchema, PoWorkflowDefinitionSource, PoWorkflowDefinitionSourceHandle, PoWorkflowDefinitionSourcesService, PoWorkflowExecutionPolicyRequest, PoWorkflowExecutionPolicyResult, PoWorkflowJobState, PoWorkflowLaunchOverrides, PoWorkflowLaunchService, PoWorkflowOperationEpoch, PoWorkflowOutcome, PoWorkflowPolicyAssociation, PoWorkflowPolicyBinding, PoWorkflowPolicyOptions, PoWorkflowProgressState, PoWorkflowRecoveryOptions, PoWorkflowRunRef, PoWorkflowRunRefSchema, PoWorkflowRunState, PoWorkflowRunsService, PoWorkflowSettlementOutcome, PoWorkflowSettlementRequest, PoWorkflowStage, PoWorkflowStepState, PoWorkflowValidationReport, readPoWorkflowAdmissionsService, readPoWorkflowContextService, readPoWorkflowDefinitionSourcesService, readPoWorkflowLaunchService, readPoWorkflowRunsService } from "./src/schemas/workflow.mjs";
2
+ export { PO_WORKFLOW_ADMISSIONS_SERVICE, PO_WORKFLOW_CONTEXT_SERVICE, PO_WORKFLOW_DEFINITION_SOURCES_SERVICE, PO_WORKFLOW_LAUNCH_SERVICE, PO_WORKFLOW_RUNS_SERVICE, type PoWorkflowAdmission, type PoWorkflowAdmissionOverrides, type PoWorkflowAdmissionRequest, type PoWorkflowAdmissionsService, type PoWorkflowChildInvocation, type PoWorkflowChildrenSession, type PoWorkflowChildrenStatus, type PoWorkflowConfig, type PoWorkflowContextService, type PoWorkflowCriterionState, type PoWorkflowDefinitionRef, PoWorkflowDefinitionRefSchema, type PoWorkflowDefinitionSource, type PoWorkflowDefinitionSourceHandle, type PoWorkflowDefinitionSourcesService, type PoWorkflowExecutionPolicyRequest, type PoWorkflowExecutionPolicyResult, type PoWorkflowJobState, type PoWorkflowLaunchOverrides, type PoWorkflowLaunchService, type PoWorkflowOperationEpoch, type PoWorkflowOutcome, type PoWorkflowPolicyAssociation, type PoWorkflowPolicyBinding, type PoWorkflowPolicyOptions, type PoWorkflowProgressState, type PoWorkflowRecoveryOptions, type PoWorkflowRunRef, PoWorkflowRunRefSchema, type PoWorkflowRunState, type PoWorkflowRunsService, type PoWorkflowSettlementOutcome, type PoWorkflowSettlementRequest, type PoWorkflowStage, type PoWorkflowStepState, type PoWorkflowValidationReport, readPoWorkflowAdmissionsService, readPoWorkflowContextService, readPoWorkflowDefinitionSourcesService, readPoWorkflowLaunchService, readPoWorkflowRunsService };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agimon-ai/po-core",
3
- "version": "0.0.0-alpha.82",
3
+ "version": "0.0.0-alpha.84",
4
4
  "description": "Po runtime systems and reusable core services.",
5
5
  "keywords": [
6
6
  "ai",
@@ -569,8 +569,8 @@
569
569
  "registry": "https://registry.npmjs.org/"
570
570
  },
571
571
  "dependencies": {
572
- "@agimon-ai/po-logsink": "0.0.1-alpha.64",
573
- "@agimon-ai/po-web-security": "0.0.0-alpha.82",
572
+ "@agimon-ai/po-logsink": "0.0.1-alpha.66",
573
+ "@agimon-ai/po-web-security": "0.0.0-alpha.84",
574
574
  "@deepseek-ai/cordis": "4.0.5",
575
575
  "@earendil-works/chord": "1.4.0",
576
576
  "@earendil-works/pi-agent-core": "1.4.0",
@@ -594,7 +594,7 @@
594
594
  "yaml": "2.9.1"
595
595
  },
596
596
  "devDependencies": {
597
- "@agimon-ai/vibe-lint-plugin-po-core": "0.0.0-alpha.82",
597
+ "@agimon-ai/vibe-lint-plugin-po-core": "0.0.0-alpha.84",
598
598
  "@apidevtools/swagger-parser": "^13.1.0",
599
599
  "@asyncapi/parser": "^3.6.4",
600
600
  "@earendil-works/pi-ai": "1.4.0",