@agimon-ai/po-core 0.0.0-alpha.81 → 0.0.0-alpha.83
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/apiContracts.d.mts +26 -3
- package/dist/apiContracts.d.mts.map +1 -1
- package/dist/src/schemas/workflow.cjs.map +1 -1
- package/dist/src/schemas/workflow.d.mts +26 -3
- package/dist/src/schemas/workflow.d.mts.map +1 -1
- package/dist/src/schemas/workflow.mjs.map +1 -1
- package/dist/src/services/tunnelProcess/index.cjs +25 -17
- package/dist/src/services/tunnelProcess/index.cjs.map +1 -1
- package/dist/src/services/tunnelProcess/index.mjs +25 -17
- package/dist/src/services/tunnelProcess/index.mjs.map +1 -1
- package/dist/workflow.d.mts +2 -2
- package/package.json +4 -4
package/dist/apiContracts.d.mts
CHANGED
|
@@ -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;;
|
|
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"}
|
|
@@ -11,9 +11,9 @@ let node_child_process = require("node:child_process");
|
|
|
11
11
|
//#region src/services/tunnelProcess/index.ts
|
|
12
12
|
/** Explicit binary path, ahead of a PATH scan. */
|
|
13
13
|
const CLOUDFLARED_ENV = "PO_CLOUDFLARED";
|
|
14
|
-
const START_TIMEOUT_MS =
|
|
14
|
+
const START_TIMEOUT_MS = 12e4;
|
|
15
15
|
const TERMINATE_GRACE_MS = 2e3;
|
|
16
|
-
const SELF_TEST_ATTEMPTS =
|
|
16
|
+
const SELF_TEST_ATTEMPTS = 240;
|
|
17
17
|
const SELF_TEST_RETRY_MS = 500;
|
|
18
18
|
const NAMED_RESTART_ATTEMPTS = 3;
|
|
19
19
|
const NAMED_RESTART_BASE_DELAY_MS = 500;
|
|
@@ -103,7 +103,9 @@ function reapStaleTunnel(stateDir, onNotice) {
|
|
|
103
103
|
try {
|
|
104
104
|
process.kill(pid, "SIGTERM");
|
|
105
105
|
onNotice(`stopped a tunnel left running by a previous hub (pid ${String(pid)})`);
|
|
106
|
-
} catch {
|
|
106
|
+
} catch (error) {
|
|
107
|
+
if (error.code !== "ESRCH") onNotice(`Could not stop stale tunnel: ${String(error)}`);
|
|
108
|
+
}
|
|
107
109
|
}
|
|
108
110
|
function tailOf(lines) {
|
|
109
111
|
return lines.slice(-20).join("").trim();
|
|
@@ -128,7 +130,9 @@ async function writeTokenFile(config, stateDir) {
|
|
|
128
130
|
let existingTarget;
|
|
129
131
|
try {
|
|
130
132
|
existingTarget = await node_fs_promises.default.realpath(tokenPath);
|
|
131
|
-
} catch {
|
|
133
|
+
} catch (error) {
|
|
134
|
+
if (error.code !== "ENOENT") throw error;
|
|
135
|
+
}
|
|
132
136
|
if (sourcePath === node_path.default.resolve(tokenPath) || sourcePath === existingTarget) throw new Error("The configured tunnel token file cannot also be Po runtime token storage.");
|
|
133
137
|
await node_fs_promises.default.rm(tokenPath, { force: true });
|
|
134
138
|
await node_fs_promises.default.writeFile(tokenPath, token, { mode: FILE_MODE });
|
|
@@ -309,13 +313,17 @@ function createSingleTunnelLauncher(options) {
|
|
|
309
313
|
const killTree = () => {
|
|
310
314
|
try {
|
|
311
315
|
child.kill("SIGTERM");
|
|
312
|
-
} catch {
|
|
316
|
+
} catch (error) {
|
|
317
|
+
if (error.code !== "ESRCH") notice(`Could not stop tunnel: ${String(error)}`);
|
|
318
|
+
}
|
|
313
319
|
};
|
|
314
320
|
const onProcessExit = () => {
|
|
315
321
|
(0, node_fs.rmSync)(runtimeTokenPath, { force: true });
|
|
316
322
|
try {
|
|
317
323
|
if (child.pid !== void 0) process.kill(child.pid, "SIGKILL");
|
|
318
|
-
} catch {
|
|
324
|
+
} catch (error) {
|
|
325
|
+
if (error.code !== "ESRCH") notice(`Could not kill tunnel at exit: ${String(error)}`);
|
|
326
|
+
}
|
|
319
327
|
};
|
|
320
328
|
process.once("exit", onProcessExit);
|
|
321
329
|
const stop = async () => {
|
|
@@ -332,7 +340,9 @@ function createSingleTunnelLauncher(options) {
|
|
|
332
340
|
const escalate = setTimeout(() => {
|
|
333
341
|
try {
|
|
334
342
|
child.kill("SIGKILL");
|
|
335
|
-
} catch {
|
|
343
|
+
} catch (error) {
|
|
344
|
+
if (error.code !== "ESRCH") notice(`Could not kill tunnel: ${String(error)}`);
|
|
345
|
+
}
|
|
336
346
|
}, TERMINATE_GRACE_MS);
|
|
337
347
|
child.once("exit", () => {
|
|
338
348
|
clearTimeout(escalate);
|
|
@@ -348,16 +358,14 @@ function createSingleTunnelLauncher(options) {
|
|
|
348
358
|
const pidWrite = node_fs_promises.default.mkdir(options.stateDir, {
|
|
349
359
|
recursive: true,
|
|
350
360
|
mode: 448
|
|
351
|
-
}).then(
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
} catch {}
|
|
360
|
-
}).catch(() => void 0);
|
|
361
|
+
}).then(() => node_fs_promises.default.writeFile(pidPath, JSON.stringify({
|
|
362
|
+
pid: child.pid,
|
|
363
|
+
ownerPid: process.pid,
|
|
364
|
+
startedAt: Date.now(),
|
|
365
|
+
command: binary
|
|
366
|
+
}), { mode: FILE_MODE })).catch((error) => {
|
|
367
|
+
notice(`Could not write tunnel process record: ${String(error)}`);
|
|
368
|
+
});
|
|
361
369
|
let seenUrl = input.config.kind === "named" ? `https://${input.config.hostname}` : void 0;
|
|
362
370
|
let seenConnection = false;
|
|
363
371
|
const waitToRetry = async () => {
|
|
@@ -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';\nconst START_TIMEOUT_MS = 45_000;\nconst TERMINATE_GRACE_MS = 2000;\nconst SELF_TEST_ATTEMPTS = 10;\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 {\n // Already gone, which is the outcome we wanted.\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 {\n // Missing is the normal state before launch.\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 {\n // Already gone.\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 {\n // Already gone.\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 {\n // Already gone.\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(async () => {\n try {\n await fs.writeFile(\n pidPath,\n JSON.stringify({ pid: child.pid, ownerPid: process.pid, startedAt: Date.now(), command: binary }),\n { mode: FILE_MODE },\n );\n } catch {\n // The reaper is a safety net; a missing record must not stop startup.\n }\n })\n .catch(() => undefined);\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) 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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"}
|
|
@@ -8,9 +8,9 @@ import { spawn } from "node:child_process";
|
|
|
8
8
|
//#region src/services/tunnelProcess/index.ts
|
|
9
9
|
/** Explicit binary path, ahead of a PATH scan. */
|
|
10
10
|
const CLOUDFLARED_ENV = "PO_CLOUDFLARED";
|
|
11
|
-
const START_TIMEOUT_MS =
|
|
11
|
+
const START_TIMEOUT_MS = 12e4;
|
|
12
12
|
const TERMINATE_GRACE_MS = 2e3;
|
|
13
|
-
const SELF_TEST_ATTEMPTS =
|
|
13
|
+
const SELF_TEST_ATTEMPTS = 240;
|
|
14
14
|
const SELF_TEST_RETRY_MS = 500;
|
|
15
15
|
const NAMED_RESTART_ATTEMPTS = 3;
|
|
16
16
|
const NAMED_RESTART_BASE_DELAY_MS = 500;
|
|
@@ -100,7 +100,9 @@ function reapStaleTunnel(stateDir, onNotice) {
|
|
|
100
100
|
try {
|
|
101
101
|
process.kill(pid, "SIGTERM");
|
|
102
102
|
onNotice(`stopped a tunnel left running by a previous hub (pid ${String(pid)})`);
|
|
103
|
-
} catch {
|
|
103
|
+
} catch (error) {
|
|
104
|
+
if (error.code !== "ESRCH") onNotice(`Could not stop stale tunnel: ${String(error)}`);
|
|
105
|
+
}
|
|
104
106
|
}
|
|
105
107
|
function tailOf(lines) {
|
|
106
108
|
return lines.slice(-20).join("").trim();
|
|
@@ -125,7 +127,9 @@ async function writeTokenFile(config, stateDir) {
|
|
|
125
127
|
let existingTarget;
|
|
126
128
|
try {
|
|
127
129
|
existingTarget = await fs.realpath(tokenPath);
|
|
128
|
-
} catch {
|
|
130
|
+
} catch (error) {
|
|
131
|
+
if (error.code !== "ENOENT") throw error;
|
|
132
|
+
}
|
|
129
133
|
if (sourcePath === path.resolve(tokenPath) || sourcePath === existingTarget) throw new Error("The configured tunnel token file cannot also be Po runtime token storage.");
|
|
130
134
|
await fs.rm(tokenPath, { force: true });
|
|
131
135
|
await fs.writeFile(tokenPath, token, { mode: FILE_MODE });
|
|
@@ -306,13 +310,17 @@ function createSingleTunnelLauncher(options) {
|
|
|
306
310
|
const killTree = () => {
|
|
307
311
|
try {
|
|
308
312
|
child.kill("SIGTERM");
|
|
309
|
-
} catch {
|
|
313
|
+
} catch (error) {
|
|
314
|
+
if (error.code !== "ESRCH") notice(`Could not stop tunnel: ${String(error)}`);
|
|
315
|
+
}
|
|
310
316
|
};
|
|
311
317
|
const onProcessExit = () => {
|
|
312
318
|
rmSync(runtimeTokenPath, { force: true });
|
|
313
319
|
try {
|
|
314
320
|
if (child.pid !== void 0) process.kill(child.pid, "SIGKILL");
|
|
315
|
-
} catch {
|
|
321
|
+
} catch (error) {
|
|
322
|
+
if (error.code !== "ESRCH") notice(`Could not kill tunnel at exit: ${String(error)}`);
|
|
323
|
+
}
|
|
316
324
|
};
|
|
317
325
|
process.once("exit", onProcessExit);
|
|
318
326
|
const stop = async () => {
|
|
@@ -329,7 +337,9 @@ function createSingleTunnelLauncher(options) {
|
|
|
329
337
|
const escalate = setTimeout(() => {
|
|
330
338
|
try {
|
|
331
339
|
child.kill("SIGKILL");
|
|
332
|
-
} catch {
|
|
340
|
+
} catch (error) {
|
|
341
|
+
if (error.code !== "ESRCH") notice(`Could not kill tunnel: ${String(error)}`);
|
|
342
|
+
}
|
|
333
343
|
}, TERMINATE_GRACE_MS);
|
|
334
344
|
child.once("exit", () => {
|
|
335
345
|
clearTimeout(escalate);
|
|
@@ -345,16 +355,14 @@ function createSingleTunnelLauncher(options) {
|
|
|
345
355
|
const pidWrite = fs.mkdir(options.stateDir, {
|
|
346
356
|
recursive: true,
|
|
347
357
|
mode: 448
|
|
348
|
-
}).then(
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
} catch {}
|
|
357
|
-
}).catch(() => void 0);
|
|
358
|
+
}).then(() => fs.writeFile(pidPath, JSON.stringify({
|
|
359
|
+
pid: child.pid,
|
|
360
|
+
ownerPid: process.pid,
|
|
361
|
+
startedAt: Date.now(),
|
|
362
|
+
command: binary
|
|
363
|
+
}), { mode: FILE_MODE })).catch((error) => {
|
|
364
|
+
notice(`Could not write tunnel process record: ${String(error)}`);
|
|
365
|
+
});
|
|
358
366
|
let seenUrl = input.config.kind === "named" ? `https://${input.config.hostname}` : void 0;
|
|
359
367
|
let seenConnection = false;
|
|
360
368
|
const waitToRetry = async () => {
|
|
@@ -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';\nconst START_TIMEOUT_MS = 45_000;\nconst TERMINATE_GRACE_MS = 2000;\nconst SELF_TEST_ATTEMPTS = 10;\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 {\n // Already gone, which is the outcome we wanted.\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 {\n // Missing is the normal state before launch.\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 {\n // Already gone.\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 {\n // Already gone.\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 {\n // Already gone.\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(async () => {\n try {\n await fs.writeFile(\n pidPath,\n JSON.stringify({ pid: child.pid, ownerPid: process.pid, startedAt: Date.now(), command: binary }),\n { mode: FILE_MODE },\n );\n } catch {\n // The reaper is a safety net; a missing record must not stop startup.\n }\n })\n .catch(() => undefined);\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.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"}
|
package/dist/workflow.d.mts
CHANGED
|
@@ -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.
|
|
3
|
+
"version": "0.0.0-alpha.83",
|
|
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.
|
|
573
|
-
"@agimon-ai/po-web-security": "0.0.0-alpha.
|
|
572
|
+
"@agimon-ai/po-logsink": "0.0.1-alpha.65",
|
|
573
|
+
"@agimon-ai/po-web-security": "0.0.0-alpha.83",
|
|
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.
|
|
597
|
+
"@agimon-ai/vibe-lint-plugin-po-core": "0.0.0-alpha.83",
|
|
598
598
|
"@apidevtools/swagger-parser": "^13.1.0",
|
|
599
599
|
"@asyncapi/parser": "^3.6.4",
|
|
600
600
|
"@earendil-works/pi-ai": "1.4.0",
|