@agimon-ai/po-core 0.0.0-alpha.30 → 0.0.0-alpha.31
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 +13 -0
- package/dist/apiContracts.d.mts.map +1 -1
- package/dist/headless.d.mts +2 -2
- package/dist/headlessServer.cjs +1 -1
- package/dist/headlessServer.mjs +1 -1
- package/dist/machineApi.cjs +1 -1
- package/dist/machineApi.mjs +1 -1
- package/dist/platform.cjs +9 -1
- package/dist/platform.d.mts +4 -4
- package/dist/platform.mjs +4 -4
- package/dist/remoteRuntime.cjs +35 -1
- package/dist/remoteRuntime.cjs.map +1 -1
- package/dist/remoteRuntime.d.mts +14 -0
- package/dist/remoteRuntime.d.mts.map +1 -1
- package/dist/remoteRuntime.mjs +35 -1
- package/dist/remoteRuntime.mjs.map +1 -1
- package/dist/sessionFilesApi.cjs +1 -1
- package/dist/sessionFilesApi.mjs +1 -1
- package/dist/sqliteSessionStorage.cjs +1 -1
- package/dist/sqliteSessionStorage.mjs +1 -1
- package/dist/src/pi/piSessionRuntime.cjs +1 -1
- package/dist/src/pi/piSessionRuntime.mjs +1 -1
- package/dist/src/schemas/headless.cjs.map +1 -1
- package/dist/src/schemas/headless.d.mts +22 -0
- package/dist/src/schemas/headless.d.mts.map +1 -1
- package/dist/src/schemas/headless.mjs.map +1 -1
- package/dist/src/schemas/platform.cjs +6 -0
- package/dist/src/schemas/platform.cjs.map +1 -1
- package/dist/src/schemas/platform.d.mts +17 -0
- package/dist/src/schemas/platform.d.mts.map +1 -1
- package/dist/src/schemas/platform.mjs +5 -1
- package/dist/src/schemas/platform.mjs.map +1 -1
- package/dist/src/schemas/pluginProtocol.d.mts +1 -1
- package/dist/src/server/directHarnessRuntime.cjs +17 -1
- package/dist/src/server/directHarnessRuntime.cjs.map +1 -1
- package/dist/src/server/directHarnessRuntime.d.mts.map +1 -1
- package/dist/src/server/directHarnessRuntime.mjs +17 -1
- package/dist/src/server/directHarnessRuntime.mjs.map +1 -1
- package/dist/src/server/serverScope.cjs +2 -1
- package/dist/src/server/serverScope.cjs.map +1 -1
- package/dist/src/server/serverScope.d.mts.map +1 -1
- package/dist/src/server/serverScope.mjs +4 -3
- package/dist/src/server/serverScope.mjs.map +1 -1
- package/dist/src/services/bundlePublication/index.cjs +46 -2
- package/dist/src/services/bundlePublication/index.cjs.map +1 -1
- package/dist/src/services/bundlePublication/index.d.mts +11 -0
- package/dist/src/services/bundlePublication/index.d.mts.map +1 -0
- package/dist/src/services/bundlePublication/index.mjs +46 -3
- package/dist/src/services/bundlePublication/index.mjs.map +1 -1
- package/dist/src/services/nativeChildTools/index.cjs +299 -1
- package/dist/src/services/nativeChildTools/index.cjs.map +1 -1
- package/dist/src/services/nativeChildTools/index.d.mts +11 -0
- package/dist/src/services/nativeChildTools/index.d.mts.map +1 -0
- package/dist/src/services/nativeChildTools/index.mjs +298 -3
- package/dist/src/services/nativeChildTools/index.mjs.map +1 -1
- package/dist/src/services/packageApiHarness/index.cjs +1 -0
- package/dist/src/services/packageApiHarness/index.cjs.map +1 -1
- package/dist/src/services/packageApiHarness/index.d.mts +2 -1
- package/dist/src/services/packageApiHarness/index.d.mts.map +1 -1
- package/dist/src/services/packageApiHarness/index.mjs +1 -0
- package/dist/src/services/packageApiHarness/index.mjs.map +1 -1
- package/dist/src/services/piExtensionHost/index.cjs +1 -1
- package/dist/src/services/piExtensionHost/index.mjs +1 -1
- package/dist/src/services/platformFiles/index.cjs +81 -0
- package/dist/src/services/platformFiles/index.cjs.map +1 -1
- package/dist/src/services/platformFiles/index.d.mts +12 -1
- package/dist/src/services/platformFiles/index.d.mts.map +1 -1
- package/dist/src/services/platformFiles/index.mjs +76 -1
- package/dist/src/services/platformFiles/index.mjs.map +1 -1
- package/dist/src/services/remoteAccess/index.cjs +51 -9
- package/dist/src/services/remoteAccess/index.cjs.map +1 -1
- package/dist/src/services/remoteAccess/index.d.mts +2 -1
- package/dist/src/services/remoteAccess/index.d.mts.map +1 -1
- package/dist/src/services/remoteAccess/index.mjs +51 -9
- package/dist/src/services/remoteAccess/index.mjs.map +1 -1
- package/dist/src/services/remoteAccessStore/remoteAccessRecords.cjs.map +1 -1
- package/dist/src/services/remoteAccessStore/remoteAccessRecords.mjs.map +1 -1
- package/dist/src/services/remoteAccessStore/sync.cjs +9 -0
- package/dist/src/services/remoteAccessStore/sync.cjs.map +1 -1
- package/dist/src/services/remoteAccessStore/sync.mjs +10 -1
- package/dist/src/services/remoteAccessStore/sync.mjs.map +1 -1
- package/dist/src/services/sessionFiles/index.cjs +1 -1
- package/dist/src/services/sessionFiles/index.mjs +1 -1
- package/dist/src/services/toolContinuation/index.cjs +80 -0
- package/dist/src/services/toolContinuation/index.cjs.map +1 -0
- package/dist/src/services/toolContinuation/index.mjs +79 -0
- package/dist/src/services/toolContinuation/index.mjs.map +1 -0
- package/dist/src/services/transcriptPages/index.cjs +39 -1
- package/dist/src/services/transcriptPages/index.cjs.map +1 -1
- package/dist/src/services/transcriptPages/index.mjs +39 -1
- package/dist/src/services/transcriptPages/index.mjs.map +1 -1
- package/dist/src/systems/child/adapters/headlessChildSessionService.cjs +7 -3
- package/dist/src/systems/child/adapters/headlessChildSessionService.cjs.map +1 -1
- package/dist/src/systems/child/adapters/headlessChildSessionService.d.mts +2 -0
- package/dist/src/systems/child/adapters/headlessChildSessionService.d.mts.map +1 -1
- package/dist/src/systems/child/adapters/headlessChildSessionService.mjs +8 -4
- package/dist/src/systems/child/adapters/headlessChildSessionService.mjs.map +1 -1
- package/dist/src/systems/main/adapters/headlessHost.cjs +5 -2
- package/dist/src/systems/main/adapters/headlessHost.cjs.map +1 -1
- package/dist/src/systems/main/adapters/headlessHost.d.mts.map +1 -1
- package/dist/src/systems/main/adapters/headlessHost.mjs +5 -2
- package/dist/src/systems/main/adapters/headlessHost.mjs.map +1 -1
- package/dist/src/systems/main/adapters/headlessSessionHost.cjs +52 -26
- package/dist/src/systems/main/adapters/headlessSessionHost.cjs.map +1 -1
- package/dist/src/systems/main/adapters/headlessSessionHost.d.mts.map +1 -1
- package/dist/src/systems/main/adapters/headlessSessionHost.mjs +33 -7
- package/dist/src/systems/main/adapters/headlessSessionHost.mjs.map +1 -1
- package/dist/webCompositions.cjs +3 -2
- package/dist/webCompositions.cjs.map +1 -1
- package/dist/webCompositions.d.mts +3 -1
- package/dist/webCompositions.d.mts.map +1 -1
- package/dist/webCompositions.mjs +4 -4
- package/dist/webCompositions.mjs.map +1 -1
- package/package.json +4 -4
package/dist/apiContracts.d.mts
CHANGED
|
@@ -66,6 +66,13 @@ declare module '@deepseek-ai/cordis' {
|
|
|
66
66
|
}
|
|
67
67
|
//#endregion
|
|
68
68
|
//#region src/schemas/platform.d.ts
|
|
69
|
+
/** Host-owned npm installation and native module loading. */
|
|
70
|
+
interface PoNpm {
|
|
71
|
+
import(specifier: string, options: {
|
|
72
|
+
sessionId: string;
|
|
73
|
+
signal?: AbortSignal;
|
|
74
|
+
}): Promise<Record<string, unknown>>;
|
|
75
|
+
}
|
|
69
76
|
interface PoSqliteService {
|
|
70
77
|
readonly abiVersion: 1;
|
|
71
78
|
/** Node: the database file at this path. Edge: the owning Durable Object's single database. */
|
|
@@ -122,8 +129,13 @@ interface PoIsolateRequest {
|
|
|
122
129
|
* Without it, or on hosts that cannot import, `import()` rejects.
|
|
123
130
|
*/
|
|
124
131
|
readonly resolveImport?: (specifier: string) => Promise<{
|
|
132
|
+
readonly kind?: 'script';
|
|
125
133
|
readonly id: string;
|
|
126
134
|
readonly source: string;
|
|
135
|
+
} | {
|
|
136
|
+
readonly kind: 'module';
|
|
137
|
+
readonly id: string;
|
|
138
|
+
readonly exports: Record<string, unknown>;
|
|
127
139
|
}>;
|
|
128
140
|
}
|
|
129
141
|
type PoIsolateErrorKind = 'timeout' | 'aborted' | 'compile' | 'error';
|
|
@@ -158,6 +170,7 @@ declare module '@deepseek-ai/cordis' {
|
|
|
158
170
|
'po/sqlite': PoSqliteService;
|
|
159
171
|
'po/session-storage': PoSessionStorageService;
|
|
160
172
|
'po/isolate': PoIsolateService;
|
|
173
|
+
'po/npm': PoNpm;
|
|
161
174
|
'po/session-files': {
|
|
162
175
|
readonly temporaryDirectory: string;
|
|
163
176
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apiContracts.d.mts","names":["_0","IoContext","_0","_$__protocolContracts0.ChordMethods"],"sources":["../src/schemas/backgroundWork.ts","../src/schemas/platform.ts","../src/schemas/workflow.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":";;;;;;;;cAWa,kCAAgC,KAAA;;;;;;KASjC,6BAA6B,cAAc;cAE1C,0BAAwB,KAAA;;;;;;;KAIzB,qBAAqB,cAAc;UAW9B;WACN;;;;;;EAMT,2BAA2B;;UAGZ;WACN;WACA;;EAET;EACA;;UAGe;WACN,gBAAgB;WAChB;aAA4B;aAA2B;;;UAGjD;WACN;WACA;WACA;;UAGM;;WAEN;EACT,SAAS,UAAU,yBAAyB;;EAE5C,SAAS,qBAAqB;;;YAIpB;IACR,sBAAsB;;YAGd;IACR,6BAA6B,OAAO
|
|
1
|
+
{"version":3,"file":"apiContracts.d.mts","names":["_0","IoContext","_0","_$__protocolContracts0.ChordMethods"],"sources":["../src/schemas/backgroundWork.ts","../src/schemas/platform.ts","../src/schemas/workflow.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":";;;;;;;;cAWa,kCAAgC,KAAA;;;;;;KASjC,6BAA6B,cAAc;cAE1C,0BAAwB,KAAA;;;;;;;KAIzB,qBAAqB,cAAc;UAW9B;WACN;;;;;;EAMT,2BAA2B;;UAGZ;WACN;WACA;;EAET;EACA;;UAGe;WACN,gBAAgB;WAChB;aAA4B;aAA2B;;;UAGjD;WACN;WACA;WACA;;UAGM;;WAEN;EACT,SAAS,UAAU,yBAAyB;;EAE5C,SAAS,qBAAqB;;;YAIpB;IACR,sBAAsB;;YAGd;IACR,6BAA6B,OAAO;;;;;;UClEvB;EACf,OAAO,mBAAmB;IAAW;IAAmB,SAAS;MAAgB,QAAQ;;UAG1E;WACN;;EAET,KAAK,cAAc,SAASC,UAAY,QAAQ;;;UAIjC;WACN,SAAS;;EAElB,yBAAyB;;EAEzB,QAAQ,SAASA,UAAY;;UAGd;;WAEN;;WAEA;;WAEA;;UAGM;WACN;;WAEA;;;;;;UAOM;WACN;EACT,KAAK,SAAS,yBAAyB,SAASA,UAAY,QAAQ;;EAEpE,aAAa,mBAAmB,SAASA,UAAY,QAAQ;EAC7D,KAAK,SAASA,UAAY,iBAAiB;;;UAI5B;;WAEN;;WAEA,SAAS,SAAS;WAClB;WACA,QAAQ;;WAER;;WAEA;;WAEA,gBAAgB;;;;;WAKhB,iBACP,sBACG;aACU;aAA0B;aAAqB;;aAC/C;aAAyB;aAAqB,SAAS;;;KAI5D;KAEA;WACG;WAAmB;;WAEnB;WACA;aACE,MAAM;aACN;aACA;;aAEA;aACA;;;;;;;UAQA;WACN;;WAEA;EACT,IAAI,SAAS,mBAAmB,QAAQ;;;YAoB9B;IACR,SAAS;IACT,YAAY;IACZ,aAAa;IACb,sBAAsB;IACtB,cAAc;IACd,UAAU;IACV;eAA+B;;;;;;;cChItB,+BAA6B,KAAA;;;;KAU9B,0BAA0B,cAAc;;cAGvC,wBAAsB,KAAA;;;;;;KASvB,mBAAmB,cAAc;UAE5B;WACN;WACA;WACA;WACA;WACA,SAAS,SAAS;WAClB,MAAM,SAAS;WACf;WACA;;;UAIM;EACf,OAAO,YAAY,yBAAyB,YAAY,4BAA4B,QAAQ;;UAG7E;WACN;;WAEA;;UAGM;WACN;WACA;EACT;;;UAIe;EACf,SAAS,cAAc,6BAA6B;EACpD,iBAAiB;EACjB,UAAU,WAAW,kBAAkB;;KAG7B;KACA;KACA;UAUK;WACN;WACA,QAAQ;WACR;aAAiB;aAAuB;aAAqB;;WAC7D;;UAGM;WACN;WACA;WACA,QAAQ;WACR,gBAAgB;;UAGV;WACN,KAAK;WACL,OAAO;WACP,UAAU;WACV,eAAe;;;UAIT;WACN,YAAY;WACZ;;WAEA,UAAU,SAAS;;;UAIb;EACf,WAAW,gBAAgB,QAAQ;EACnC,UAAU,gBAAgB,QAAQ;;EAElC,UAAU,eAAe,WAAW,OAAO;;;UAI5B;WACN,KAAK;WACL;WACA;WACA;WACA,QAAQ;WACR,gBAAgB,SAAS;WACzB,iBAAiB,SAAS;WAC1B;WACA,OAAO;;;YAKN;IACR,sBAAsB;IACtB,kCAAkC;IAClC,oBAAoB;IACpB,uBAAuB;;;;;;KC1Gf;;;;UC3BK;WACN;WACA;;;;qBCCE;qBACA;;qBAYA,qBAAmB,KAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;YAkEpB,gBAAgB,cAAc;YAC9B,iBAAiB;YACjB,mBAAmB;wBAEf,kBAAkB,UAAU,gBAAgB;wBAI5C,iBAAiB,iBAAiB;iBAsBjC;EACf;EACA;EACA,iBAAiB;EACjB,iBAAiB;EACjB,WAAW;EACX;;EAEA;;iBAGe;EACf,gBAAgB;EAChB;EACA;EACA,UAAU;;wBAGI,uBAAuB,iBAAiB;;;UClIvC;EACf,OAAO;EACP;EACA;EACA,UAAU;;UAGK;EACf,SAAS;EACT,UAAU;EACV;IACE;IACA;IACA,WAAW;IACX;MACE,OAAO;MACP;MACA;MACA;MACA;;IAEF;MAAY;MAAc;MAAiB,OAAO;;IAClD;MAAQ;MAAqB,OAAO;MAAY;;IAChD;;;;wBAKY,sBAAsB;;wBAwBtB,mBAAmB,qBAAqB,yBAAyB;;;qBC3CpE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBCFA,gBAAc,KAAA;;;;qBACd,aAAW,KAAA;MAAX,KAAA;;;wBAIG,iBAAiB,0BAA0B,kBAAe;;;;UCRzD;WACN,OAAO;WACP,SAAS;;;;;;KAOR,YAAY,UAAU,qBAAqB,UAAU,uBAAuB,KACtF,kBAAkB,OAAO,SAAS;WAErB,MAAM;WAAY,QAAQ;;KAE7B,aAAa,UAAU,eAAe,uBAC/C,WAAW,IAAI,YACd,EAAE;WAAsB,YAAY,UAAU;IAAY,eAC1D,EAAE;WAAsB,cAAc,UAAU;IAAY;;;KCrBpD,+CAA+C;GAAiB,cAAc;;;;qBC2E7E,2BAAyB,KAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBA8LzB,gBAHME;;IAlJf,MAAI,KAAA;;;;;;IAMJ,QAAM,KAAA;;;;;;;;;;;;IAYN,MAAI,KAAA,QAAA,KAAA,SAAA,KAAA;;;;;;;;;;;IAQG,MAAI,KAAA,QAAA,KAAA,SAAA,KAAA;;;;;;;;;;;IAEK,MAAI,KAAA;;;;;IACV,MAAI,KAAA;;;;;;IACD,MAAI,KAAA,QAAA,KAAA,iBAAA,KAAA,qBAAA,KAAA,iBAAA,KAAA,oBAAA,KAAA,kBAAA,KAAA,iBAAA,KAAA;;;IACJ,MAAI,KAAA;;;IACH,MAAI,KAAA;;;IACR,MAAI,KAAA;;;;;;;;;;IACI,MAAI,KAAA;;;;;;;;IAAW,QAAM,KAAA;;;;;IAEvC,MAAI,KAAA;;;IACJ,QAAM,KAAA,QAAA,KAAA,qBAAA,KAAA,uBAAA,KAAA,8BAAA,KAAA;;;IAQN,MAAI,KAAA;;;;IACJ,QAAM,KAAA,QAAA,KAAA,sBAAA,KAAA,uBAAA,KAAA,4BAAA,KAAA;;;;IAQM,QAAM,KAAA;;;;;;IAElB,MAAI,KAAA;;;;;;;IAOJ,QAAM,KAAA;;;;;;;;;;;;;;;;;;;;;;;IAmBN,MAAI,KAAA,QAAA,KAAA;;;;;;;;;;;;IAOJ,QAAM,KAAA;;;;;;;;;;;;;;;;;;;;;;;;IAqBN,QAAM,KAAA;;;;;;;;;;;;;;;;;;;IAeN,QAAM,KAAA,OAAA,KAAA;;;;;;;;;;;;;;IAiBc,QAAM,KAAA,OAAA,KAAA;;;;;;IACE,QAAM,KAAA,OAAA,KAAA,QAAA,KAAA,iBAAA,KAAA,qBAAA,KAAA,iBAAA,KAAA,oBAAA,KAAA,kBAAA,KAAA,iBAAA,KAAA;;;IACzB,MAAI,KAAA;;;IAAuD,QAAM,KAAA,QAAA;;;IACjE,MAAI,KAAA;;;IACF,MAAI,KAAA,OAAA,KAAA;;;;;;qBCnQN;qBA6BA,mBAAiB,KAAA,QAAA,KAAA,QAAA,KAAA,eAAA,KAAA,YAAA,KAAA,QAAA,KAAA,eAAA,KAAA,QAAA,KAAA,OAAA,KAAA,QAAA,KAAA,SAAA,KAAA,aAAA,KAAA,YAAA,KAAA,YAAA,KAAA,QAAA,KAAA,eAAA,KAAA,YAAA,KAAA,QAAA,KAAA,eAAA,KAAA,QAAA,KAAA,OAAA,KAAA,QAAA,KAAA,SAAA,KAAA,aAAA,KAAA,cAAA,KAAA,QAAA,KAAA,iBAAA,KAAA,QAAA,KAAA,eAAA,KAAA,QAAA,KAAA,OAAA,KAAA,QAAA,KAAA,SAAA,KAAA,aAAA,KAAA,YAAA,KAAA,WAAA,KAAA,QAAA,KAAA,eAAA,KAAA,WAAA,KAAA,QAAA,KAAA,eAAA,KAAA,QAAA,KAAA,OAAA,KAAA,QAAA,KAAA,SAAA,KAAA,aAAA,KAAA,YAAA,KAAA,YAAA,KAAA,QAAA,KAAA,eAAA,KAAA,YAAA,KAAA,QAAA,KAAA,eAAA,KAAA,QAAA,KAAA,OAAA,KAAA,QAAA,KAAA,SAAA,KAAA,aAAA,KAAA,YAAA,KAAA,UAAA,KAAA,UAAA,KAAA,OAAA,KAAA,aAAA,KAAA,QAAA,KAAA,eAAA,KAAA,UAAA,KAAA,UAAA,KAAA,OAAA,KAAA,aAAA,KAAA,QAAA,KAAA,eAAA,KAAA,QAAA,KAAA,OAAA,KAAA,QAAA,KAAA,SAAA,KAAA,aAAA,KAAA,YAAA,KAAA,OAAA,KAAA,aAAA,KAAA,QAAA,KAAA,eAAA,KAAA,OAAA,KAAA,aAAA,KAAA,QAAA,KAAA,eAAA,KAAA,UAAA,KAAA,OAAA,KAAA,QAAA,KAAA,SAAA,KAAA;;qBAqCjB,wBAAsB,KAAA,QAAA,KAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAwBtB,wBAAsB,KAAA,QAAA,KAAA"}
|
package/dist/headless.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { PO_HEADLESS_HOST_SERVICE, PO_HEADLESS_OWNER, PO_LOAD_SKILL_TOOL, PO_SEARCH_SKILLS_TOOL, PoHeadlessActivity, PoHeadlessCapability, PoHeadlessCapabilitySnapshot, PoHeadlessClient, PoHeadlessClientRequest, PoHeadlessCommand, PoHeadlessCompactionResult, PoHeadlessCondition, PoHeadlessContent, PoHeadlessEventName, PoHeadlessExecutionContext, PoHeadlessHook, PoHeadlessHookEvent, PoHeadlessHookResult, PoHeadlessHostService, PoHeadlessModelMetadata, PoHeadlessModelSettings, PoHeadlessPromptAdmissionError, PoHeadlessRegistration, PoHeadlessResource, PoHeadlessSelection, PoHeadlessSelectionChange, PoHeadlessSession, PoHeadlessTool, PoHeadlessToolBusyError, PoHeadlessToolCompletionRequest, PoHeadlessToolCompletionResult, PoHeadlessToolRestriction, PoHeadlessToolResult, PoMcpSkillAccess, PoSessionSetupError, dropInvalidImages, invalidImageReason, isPoHeadlessPromptAdmissionError, isPoHeadlessToolBusyError, isPoHeadlessToolModelVisible, isPoSessionSetupError, readPoHeadlessHost, readPoHeadlessOwner, requirePoHeadlessHost, toPoHeadlessToolResult } from "./src/schemas/headless.mjs";
|
|
2
|
-
export { PO_HEADLESS_HOST_SERVICE, PO_HEADLESS_OWNER, PO_LOAD_SKILL_TOOL, PO_SEARCH_SKILLS_TOOL, PoHeadlessActivity, PoHeadlessCapability, PoHeadlessCapabilitySnapshot, PoHeadlessClient, PoHeadlessClientRequest, PoHeadlessCommand, PoHeadlessCompactionResult, PoHeadlessCondition, PoHeadlessContent, PoHeadlessEventName, PoHeadlessExecutionContext, PoHeadlessHook, PoHeadlessHookEvent, PoHeadlessHookResult, PoHeadlessHostService, PoHeadlessModelMetadata, PoHeadlessModelSettings, PoHeadlessPromptAdmissionError, PoHeadlessRegistration, PoHeadlessResource, PoHeadlessSelection, PoHeadlessSelectionChange, PoHeadlessSession, PoHeadlessTool, PoHeadlessToolBusyError, PoHeadlessToolCompletionRequest, PoHeadlessToolCompletionResult, PoHeadlessToolRestriction, PoHeadlessToolResult, PoMcpSkillAccess, PoSessionSetupError, dropInvalidImages, invalidImageReason, isPoHeadlessPromptAdmissionError, isPoHeadlessToolBusyError, isPoHeadlessToolModelVisible, isPoSessionSetupError, readPoHeadlessHost, readPoHeadlessOwner, requirePoHeadlessHost, toPoHeadlessToolResult };
|
|
1
|
+
import { PO_HEADLESS_HOST_SERVICE, PO_HEADLESS_OWNER, PO_LOAD_SKILL_TOOL, PO_SEARCH_SKILLS_TOOL, PoHeadlessActivity, PoHeadlessCapability, PoHeadlessCapabilitySnapshot, PoHeadlessClient, PoHeadlessClientRequest, PoHeadlessCommand, PoHeadlessCompactionResult, PoHeadlessCondition, PoHeadlessContent, PoHeadlessEventName, PoHeadlessExecutionContext, PoHeadlessHook, PoHeadlessHookEvent, PoHeadlessHookResult, PoHeadlessHostService, PoHeadlessModelMetadata, PoHeadlessModelSettings, PoHeadlessPromptAdmissionError, PoHeadlessRegistration, PoHeadlessResource, PoHeadlessSelection, PoHeadlessSelectionChange, PoHeadlessSession, PoHeadlessTool, PoHeadlessToolBusyError, PoHeadlessToolCompletionRequest, PoHeadlessToolCompletionResult, PoHeadlessToolContinuation, PoHeadlessToolRestriction, PoHeadlessToolResult, PoMcpSkillAccess, PoSessionSetupError, dropInvalidImages, invalidImageReason, isPoHeadlessPromptAdmissionError, isPoHeadlessToolBusyError, isPoHeadlessToolModelVisible, isPoSessionSetupError, readPoHeadlessHost, readPoHeadlessOwner, requirePoHeadlessHost, toPoHeadlessToolResult } from "./src/schemas/headless.mjs";
|
|
2
|
+
export { PO_HEADLESS_HOST_SERVICE, PO_HEADLESS_OWNER, PO_LOAD_SKILL_TOOL, PO_SEARCH_SKILLS_TOOL, PoHeadlessActivity, PoHeadlessCapability, PoHeadlessCapabilitySnapshot, PoHeadlessClient, PoHeadlessClientRequest, PoHeadlessCommand, PoHeadlessCompactionResult, PoHeadlessCondition, PoHeadlessContent, PoHeadlessEventName, PoHeadlessExecutionContext, PoHeadlessHook, PoHeadlessHookEvent, PoHeadlessHookResult, PoHeadlessHostService, PoHeadlessModelMetadata, PoHeadlessModelSettings, PoHeadlessPromptAdmissionError, PoHeadlessRegistration, PoHeadlessResource, PoHeadlessSelection, PoHeadlessSelectionChange, PoHeadlessSession, PoHeadlessTool, PoHeadlessToolBusyError, PoHeadlessToolCompletionRequest, PoHeadlessToolCompletionResult, PoHeadlessToolContinuation, PoHeadlessToolRestriction, PoHeadlessToolResult, PoMcpSkillAccess, PoSessionSetupError, dropInvalidImages, invalidImageReason, isPoHeadlessPromptAdmissionError, isPoHeadlessToolBusyError, isPoHeadlessToolModelVisible, isPoSessionSetupError, readPoHeadlessHost, readPoHeadlessOwner, requirePoHeadlessHost, toPoHeadlessToolResult };
|
package/dist/headlessServer.cjs
CHANGED
|
@@ -23,8 +23,8 @@ let node_os = require("node:os");
|
|
|
23
23
|
node_os = require_runtime.__toESM(node_os, 1);
|
|
24
24
|
let node_path = require("node:path");
|
|
25
25
|
node_path = require_runtime.__toESM(node_path, 1);
|
|
26
|
-
let node_http = require("node:http");
|
|
27
26
|
let dist_interop_chordContext_mjs = require("../dist/interop/chordContext.mjs");
|
|
27
|
+
let node_http = require("node:http");
|
|
28
28
|
let dist_interop_chord_mjs = require("../dist/interop/chord.mjs");
|
|
29
29
|
let node_stream = require("node:stream");
|
|
30
30
|
let node_stream_promises = require("node:stream/promises");
|
package/dist/headlessServer.mjs
CHANGED
|
@@ -18,8 +18,8 @@ import { createHash, timingSafeEqual } from "node:crypto";
|
|
|
18
18
|
import fs from "node:fs";
|
|
19
19
|
import os from "node:os";
|
|
20
20
|
import path from "node:path";
|
|
21
|
-
import { createServer } from "node:http";
|
|
22
21
|
import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
|
|
22
|
+
import { createServer } from "node:http";
|
|
23
23
|
import { webSocketLink } from "@earendil-works/chord";
|
|
24
24
|
import { Readable } from "node:stream";
|
|
25
25
|
import { pipeline } from "node:stream/promises";
|
package/dist/machineApi.cjs
CHANGED
|
@@ -10,8 +10,8 @@ const require_index$3 = require("./src/services/piExtensionHost/index.cjs");
|
|
|
10
10
|
const require_index$4 = require("./src/services/providerAuth/index.cjs");
|
|
11
11
|
let node_path = require("node:path");
|
|
12
12
|
node_path = require_runtime.__toESM(node_path, 1);
|
|
13
|
-
let _earendil_works_pi_coding_agent = require("@earendil-works/pi-coding-agent");
|
|
14
13
|
let dist_interop_chordContext_mjs = require("../dist/interop/chordContext.mjs");
|
|
14
|
+
let _earendil_works_pi_coding_agent = require("@earendil-works/pi-coding-agent");
|
|
15
15
|
//#region src/server/machineApi.ts
|
|
16
16
|
function record(value) {
|
|
17
17
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
package/dist/machineApi.mjs
CHANGED
|
@@ -7,8 +7,8 @@ import { registerChatgptAuthProvider } from "./src/services/chatgptLogin/index.m
|
|
|
7
7
|
import { preloadPiExtensions, resolvePiSettingsPackageEntries } from "./src/services/piExtensionHost/index.mjs";
|
|
8
8
|
import { createProviderAuth } from "./src/services/providerAuth/index.mjs";
|
|
9
9
|
import path from "node:path";
|
|
10
|
-
import { ModelRuntime, SettingsManager } from "@earendil-works/pi-coding-agent";
|
|
11
10
|
import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
|
|
11
|
+
import { ModelRuntime, SettingsManager } from "@earendil-works/pi-coding-agent";
|
|
12
12
|
//#region src/server/machineApi.ts
|
|
13
13
|
function record(value) {
|
|
14
14
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
package/dist/platform.cjs
CHANGED
|
@@ -7,8 +7,8 @@ const require_index$3 = require("./src/services/durableSession/index.cjs");
|
|
|
7
7
|
const require_index$4 = require("./src/services/sessionFiles/index.cjs");
|
|
8
8
|
const require_platform = require("./src/schemas/platform.cjs");
|
|
9
9
|
const require_index$5 = require("./src/services/fileAccess/index.cjs");
|
|
10
|
-
let _earendil_works_pi_durable_env = require("@earendil-works/pi-durable/env");
|
|
11
10
|
let dist_interop_chordContext_mjs = require("../dist/interop/chordContext.mjs");
|
|
11
|
+
let _earendil_works_pi_durable_env = require("@earendil-works/pi-durable/env");
|
|
12
12
|
Object.defineProperty(exports, "BACKGROUND_CONTEXT", {
|
|
13
13
|
enumerable: true,
|
|
14
14
|
get: function() {
|
|
@@ -27,9 +27,11 @@ Object.defineProperty(exports, "FileError", {
|
|
|
27
27
|
return _earendil_works_pi_durable_env.FileError;
|
|
28
28
|
}
|
|
29
29
|
});
|
|
30
|
+
exports.FileTooLargeError = require_index.FileTooLargeError;
|
|
30
31
|
exports.PO_FILE_ACCESS_SERVICE = require_index$5.PO_FILE_ACCESS_SERVICE;
|
|
31
32
|
exports.PO_FS_SERVICE = require_platform.PO_FS_SERVICE;
|
|
32
33
|
exports.PO_ISOLATE_SERVICE = require_platform.PO_ISOLATE_SERVICE;
|
|
34
|
+
exports.PO_NPM_SERVICE = require_platform.PO_NPM_SERVICE;
|
|
33
35
|
exports.PO_SESSION_FILES_SERVICE = require_index$4.PO_SESSION_FILES_SERVICE;
|
|
34
36
|
exports.PO_SESSION_STORAGE_SERVICE = require_platform.PO_SESSION_STORAGE_SERVICE;
|
|
35
37
|
exports.PO_SESSION_TEMP_DIR = require_index$4.PO_SESSION_TEMP_DIR;
|
|
@@ -45,13 +47,19 @@ Object.defineProperty(exports, "getOrThrow", {
|
|
|
45
47
|
}
|
|
46
48
|
});
|
|
47
49
|
exports.initializeSessionIdentity = require_index$3.initializeSessionIdentity;
|
|
50
|
+
exports.memoryDiagnosticAttributes = require_index.memoryDiagnosticAttributes;
|
|
48
51
|
exports.nodeHost = require_index$1.nodeHost;
|
|
49
52
|
exports.pathExists = require_index.pathExists;
|
|
53
|
+
exports.readBoundedFile = require_index.readBoundedFile;
|
|
50
54
|
exports.readPoFileAccess = require_index$5.readPoFileAccess;
|
|
51
55
|
exports.readPoFs = require_platform.readPoFs;
|
|
52
56
|
exports.readPoIsolate = require_platform.readPoIsolate;
|
|
57
|
+
exports.readPoNpm = require_platform.readPoNpm;
|
|
53
58
|
exports.readSessionFileDirectories = require_index$4.readSessionFileDirectories;
|
|
54
59
|
exports.readTextIfExists = require_index.readTextIfExists;
|
|
60
|
+
exports.recordBoundedRead = require_index.recordBoundedRead;
|
|
61
|
+
exports.recordRipgrepOutput = require_index.recordRipgrepOutput;
|
|
62
|
+
exports.recordTranscriptProjection = require_index.recordTranscriptProjection;
|
|
55
63
|
exports.removePath = require_index.removePath;
|
|
56
64
|
exports.removeSessionFiles = require_index$4.removeSessionFiles;
|
|
57
65
|
exports.requirePoFs = require_platform.requirePoFs;
|
package/dist/platform.d.mts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { PO_FS_SERVICE, PO_ISOLATE_SERVICE, PO_SESSION_STORAGE_SERVICE, PO_SHELL_SERVICE, PO_SQLITE_SERVICE, PoIsolateErrorKind, PoIsolateRequest, PoIsolateResult, PoIsolateService, PoPlatform, PoSessionStorage, PoSessionStorageRequest, PoSessionStorageService, PoSqliteService, PoStoredSession, readPoFs, readPoIsolate, requirePoFs, requirePoIsolate, requirePoShell, requirePoSqlite } from "./src/schemas/platform.mjs";
|
|
1
|
+
import { PO_FS_SERVICE, PO_ISOLATE_SERVICE, PO_NPM_SERVICE, PO_SESSION_STORAGE_SERVICE, PO_SHELL_SERVICE, PO_SQLITE_SERVICE, PoIsolateErrorKind, PoIsolateRequest, PoIsolateResult, PoIsolateService, PoNpm, PoPlatform, PoSessionStorage, PoSessionStorageRequest, PoSessionStorageService, PoSqliteService, PoStoredSession, readPoFs, readPoIsolate, readPoNpm, requirePoFs, requirePoIsolate, requirePoShell, requirePoSqlite } from "./src/schemas/platform.mjs";
|
|
2
2
|
import { PO_SESSION_FILES_SERVICE, PO_SESSION_TEMP_DIR, PoSessionFileDirectories, PoSessionFiles, SessionTempDirectoryOptions, readSessionFileDirectories, removeSessionFiles, sessionTempDirectory } from "./src/services/sessionFiles/index.mjs";
|
|
3
3
|
import { PO_FILE_ACCESS_SERVICE, PoFileAccess, readPoFileAccess } from "./src/services/fileAccess/index.mjs";
|
|
4
4
|
import { SerialRunner, SerialTask, createSerialRunner } from "./src/services/serialRunner/index.mjs";
|
|
5
5
|
import { initializeSessionIdentity } from "./src/services/durableSession/index.mjs";
|
|
6
6
|
import { SessionIdentityDoc, SessionMetadataDoc } from "./src/services/durableSession/docs.mjs";
|
|
7
7
|
import { nodeHost } from "./src/services/nodeHost/index.mjs";
|
|
8
|
-
import { pathExists, readTextIfExists, removePath, writeFileAtomic } from "./src/services/platformFiles/index.mjs";
|
|
9
|
-
import { ExecutionEnv, ExecutionError, FileError, FileSystem, Result, Shell, getOrThrow } from "@earendil-works/pi-durable/env";
|
|
8
|
+
import { FileTooLargeError, memoryDiagnosticAttributes, pathExists, readBoundedFile, readTextIfExists, recordBoundedRead, recordRipgrepOutput, recordTranscriptProjection, removePath, writeFileAtomic } from "./src/services/platformFiles/index.mjs";
|
|
10
9
|
import { BACKGROUND_CONTEXT, withAbortSignal } from "@earendil-works/chord/context";
|
|
11
|
-
|
|
10
|
+
import { ExecutionEnv, ExecutionError, FileError, FileSystem, Result, Shell, getOrThrow } from "@earendil-works/pi-durable/env";
|
|
11
|
+
export { BACKGROUND_CONTEXT, type ExecutionEnv, ExecutionError, FileError, type FileSystem, FileTooLargeError, PO_FILE_ACCESS_SERVICE, PO_FS_SERVICE, PO_ISOLATE_SERVICE, PO_NPM_SERVICE, PO_SESSION_FILES_SERVICE, PO_SESSION_STORAGE_SERVICE, PO_SESSION_TEMP_DIR, PO_SHELL_SERVICE, PO_SQLITE_SERVICE, type PoFileAccess, type PoIsolateErrorKind, type PoIsolateRequest, type PoIsolateResult, type PoIsolateService, type PoNpm, type PoPlatform, type PoSessionFileDirectories, type PoSessionFiles, type PoSessionStorage, type PoSessionStorageRequest, type PoSessionStorageService, type PoSqliteService, type PoStoredSession, type Result, type SerialRunner, type SerialTask, SessionIdentityDoc, SessionMetadataDoc, type SessionTempDirectoryOptions, type Shell, createSerialRunner, getOrThrow, initializeSessionIdentity, memoryDiagnosticAttributes, nodeHost, pathExists, readBoundedFile, readPoFileAccess, readPoFs, readPoIsolate, readPoNpm, readSessionFileDirectories, readTextIfExists, recordBoundedRead, recordRipgrepOutput, recordTranscriptProjection, removePath, removeSessionFiles, requirePoFs, requirePoIsolate, requirePoShell, requirePoSqlite, sessionTempDirectory, withAbortSignal, writeFileAtomic };
|
package/dist/platform.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { pathExists, readTextIfExists, removePath, writeFileAtomic } from "./src/services/platformFiles/index.mjs";
|
|
1
|
+
import { FileTooLargeError, memoryDiagnosticAttributes, pathExists, readBoundedFile, readTextIfExists, recordBoundedRead, recordRipgrepOutput, recordTranscriptProjection, removePath, writeFileAtomic } from "./src/services/platformFiles/index.mjs";
|
|
2
2
|
import { nodeHost } from "./src/services/nodeHost/index.mjs";
|
|
3
3
|
import { createSerialRunner } from "./src/services/serialRunner/index.mjs";
|
|
4
4
|
import { SessionIdentityDoc, SessionMetadataDoc } from "./src/services/durableSession/docs.mjs";
|
|
5
5
|
import { initializeSessionIdentity } from "./src/services/durableSession/index.mjs";
|
|
6
6
|
import { PO_SESSION_FILES_SERVICE, PO_SESSION_TEMP_DIR, readSessionFileDirectories, removeSessionFiles, sessionTempDirectory } from "./src/services/sessionFiles/index.mjs";
|
|
7
|
-
import { PO_FS_SERVICE, PO_ISOLATE_SERVICE, PO_SESSION_STORAGE_SERVICE, PO_SHELL_SERVICE, PO_SQLITE_SERVICE, readPoFs, readPoIsolate, requirePoFs, requirePoIsolate, requirePoShell, requirePoSqlite } from "./src/schemas/platform.mjs";
|
|
7
|
+
import { PO_FS_SERVICE, PO_ISOLATE_SERVICE, PO_NPM_SERVICE, PO_SESSION_STORAGE_SERVICE, PO_SHELL_SERVICE, PO_SQLITE_SERVICE, readPoFs, readPoIsolate, readPoNpm, requirePoFs, requirePoIsolate, requirePoShell, requirePoSqlite } from "./src/schemas/platform.mjs";
|
|
8
8
|
import { PO_FILE_ACCESS_SERVICE, readPoFileAccess } from "./src/services/fileAccess/index.mjs";
|
|
9
|
-
import { ExecutionError, FileError, getOrThrow } from "@earendil-works/pi-durable/env";
|
|
10
9
|
import { BACKGROUND_CONTEXT, withAbortSignal } from "@earendil-works/chord/context";
|
|
11
|
-
|
|
10
|
+
import { ExecutionError, FileError, getOrThrow } from "@earendil-works/pi-durable/env";
|
|
11
|
+
export { BACKGROUND_CONTEXT, ExecutionError, FileError, FileTooLargeError, PO_FILE_ACCESS_SERVICE, PO_FS_SERVICE, PO_ISOLATE_SERVICE, PO_NPM_SERVICE, PO_SESSION_FILES_SERVICE, PO_SESSION_STORAGE_SERVICE, PO_SESSION_TEMP_DIR, PO_SHELL_SERVICE, PO_SQLITE_SERVICE, SessionIdentityDoc, SessionMetadataDoc, createSerialRunner, getOrThrow, initializeSessionIdentity, memoryDiagnosticAttributes, nodeHost, pathExists, readBoundedFile, readPoFileAccess, readPoFs, readPoIsolate, readPoNpm, readSessionFileDirectories, readTextIfExists, recordBoundedRead, recordRipgrepOutput, recordTranscriptProjection, removePath, removeSessionFiles, requirePoFs, requirePoIsolate, requirePoShell, requirePoSqlite, sessionTempDirectory, withAbortSignal, writeFileAtomic };
|
package/dist/remoteRuntime.cjs
CHANGED
|
@@ -110,7 +110,26 @@ function createRemoteRuntime(options) {
|
|
|
110
110
|
});
|
|
111
111
|
const app = new hono.Hono();
|
|
112
112
|
let remote;
|
|
113
|
+
let closing = false;
|
|
114
|
+
let lastIntent;
|
|
113
115
|
let frontendOrigin;
|
|
116
|
+
const tunnelIntent = () => {
|
|
117
|
+
const state = remote.state();
|
|
118
|
+
const tunnel = remote.settings().tunnel;
|
|
119
|
+
if (state.status !== "on" || tunnel.kind !== "named" || state.publicUrl === void 0) return void 0;
|
|
120
|
+
if (new URL(state.publicUrl).hostname !== tunnel.hostname) return void 0;
|
|
121
|
+
return {
|
|
122
|
+
hostname: tunnel.hostname,
|
|
123
|
+
...state.closesAt === void 0 ? {} : { closesAt: Date.parse(state.closesAt) }
|
|
124
|
+
};
|
|
125
|
+
};
|
|
126
|
+
const publishIntent = () => {
|
|
127
|
+
if (closing) return;
|
|
128
|
+
const intent = tunnelIntent();
|
|
129
|
+
if (intent?.hostname === lastIntent?.hostname && intent?.closesAt === lastIntent?.closesAt) return;
|
|
130
|
+
lastIntent = intent;
|
|
131
|
+
options.onTunnelIntentChanged?.(intent);
|
|
132
|
+
};
|
|
114
133
|
const fileShares = require_index$2.createPublicFileShares({
|
|
115
134
|
publicOrigin: () => remote.publicOrigin(),
|
|
116
135
|
publicOriginRevision: () => remote.publicOriginRevision(),
|
|
@@ -149,6 +168,7 @@ function createRemoteRuntime(options) {
|
|
|
149
168
|
});
|
|
150
169
|
remote = require_index$5.createRemoteAccess({
|
|
151
170
|
store,
|
|
171
|
+
broadcastAll: publishIntent,
|
|
152
172
|
onDeviceDropped: options.onDeviceDropped,
|
|
153
173
|
launchTunnel: options.launchTunnel ?? require_index$7.createTunnelLauncher({
|
|
154
174
|
stateDir: store.directory,
|
|
@@ -465,7 +485,21 @@ function createRemoteRuntime(options) {
|
|
|
465
485
|
remote,
|
|
466
486
|
fileShares,
|
|
467
487
|
fetchLocal: async (request) => app.fetch(request, { listener: "local" }),
|
|
468
|
-
|
|
488
|
+
tunnelIntent,
|
|
489
|
+
restoreTunnel: async (intent) => {
|
|
490
|
+
const parsed = store.settingsForRestore();
|
|
491
|
+
const tunnel = parsed?.settings.tunnel;
|
|
492
|
+
if (closing || typeof intent?.hostname !== "string" || tunnel?.kind !== "named" || tunnel.hostname !== intent.hostname || parsed?.warnings.length !== 0 || remote.settings().tunnel.kind !== "named" || intent.closesAt !== void 0 && (!Number.isFinite(intent.closesAt) || intent.closesAt <= Date.now())) return {
|
|
493
|
+
ok: false,
|
|
494
|
+
error: "The named tunnel intent is no longer valid for automatic restore."
|
|
495
|
+
};
|
|
496
|
+
remote.updateSettings(parsed.settings);
|
|
497
|
+
return remote.enable(intent.closesAt);
|
|
498
|
+
},
|
|
499
|
+
close: async () => {
|
|
500
|
+
closing = true;
|
|
501
|
+
await remote.close();
|
|
502
|
+
}
|
|
469
503
|
};
|
|
470
504
|
}
|
|
471
505
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remoteRuntime.cjs","names":["PUBLIC_FILE_SHARE_PREFIX","PO_API_CALLER_LOCALITY_HEADER","PO_API_CALLER_DEVICE_ID_HEADER","PO_API_CALLER_STEP_UP_HEADER","REMOTE_HTTP_ROUTE","REMOTE_CHANNEL_ROUTE","pipeline","Readable","createRemoteAccessStore","defaultRemoteStateDir","createDevProxyStore","Hono","createPublicFileShares","isPublicSessionMcpRoute","originVerdict","localOriginPolicy","isPublicPairingRoute","getCookie","DEVICE_COOKIE","isDevProxyPath","createRemoteAccess","createTunnelLauncher","WebSocketServer","createServer","parseProxyPath","parsePoSocketPath","createTextLink","stepUpActionFor","STEP_UP_HEADER","isSessionMcpHostRoute","isSessionMcpPhoneRoute"],"sources":["../src/server/remoteRuntime.ts"],"sourcesContent":["import { createServer, type IncomingMessage, type ServerResponse } from 'node:http';\nimport { Readable } from 'node:stream';\nimport { pipeline } from 'node:stream/promises';\n\nimport type { Link } from '@earendil-works/chord';\nimport { Hono } from 'hono';\nimport { getCookie } from 'hono/cookie';\nimport { WebSocketServer, type RawData } from 'ws';\n\nimport { DEVICE_COOKIE, REMOTE_CHANNEL_ROUTE, REMOTE_HTTP_ROUTE, STEP_UP_HEADER } from '../constants/remote';\nimport {\n PO_API_CALLER_DEVICE_ID_HEADER,\n PO_API_CALLER_LOCALITY_HEADER,\n PO_API_CALLER_STEP_UP_HEADER,\n parsePoSocketPath,\n} from '../schemas/packageApi';\nimport { createTextLink } from '../schemas/protocolContracts';\nimport { isDevProxyPath, parseProxyPath } from '../services/devProxyPolicy';\nimport { createDevProxyStore } from '../services/devProxyStore';\nimport { createPublicFileShares, PUBLIC_FILE_SHARE_PREFIX, type PublicFileShares } from '../services/publicFileShares';\nimport { createRemoteAccess, type RemoteAccess } from '../services/remoteAccess';\nimport { createRemoteAccessStore, defaultRemoteStateDir } from '../services/remoteAccessStore';\nimport {\n isPublicPairingRoute,\n originVerdict,\n type GuardListener,\n localOriginPolicy,\n} from '../services/remoteGuardPolicy';\nimport { createTunnelLauncher, reapStaleTunnel } from '../services/tunnelProcess';\nimport { stepUpActionFor } from '../services/webauthnPolicy';\nimport { type TunnelLauncher } from '../types/remote';\nimport { registerDevProxyRoutes } from './devProxyRoutes';\nimport { relayDevProxySocket } from './devProxySocket';\nimport { registerRemoteRoutes } from './remoteRoutes';\nimport { isPublicSessionMcpRoute, isSessionMcpHostRoute, isSessionMcpPhoneRoute } from './sessionMcpRoutes';\n\nexport { relayDevProxySocket, type DevProxySocketInput } from './devProxySocket';\n\nconst MAX_REQUEST_BYTES = 8 * 1024 * 1024;\nconst TUNNEL_HEADERS_TIMEOUT_MS = 5_000;\nconst TUNNEL_REQUEST_TIMEOUT_MS = 10_000;\nconst TUNNEL_CONNECTION_LIMIT = 64;\nconst TUNNEL_REQUESTS_PER_SOCKET = 100;\nconst SEALED_HTTP_VERSION = 1;\nconst SEALED_HTTP_METHODS = new Set(['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE']);\n/** HMAC-authenticated peer routes are the only non-device tunnel APIs. */\nconst SESSION_PEER_INBOX_ROUTE = '/api/plugins/session-peer/inbox';\nconst VOICE_PEER_ROUTES = new Set(['/api/plugins/voice/peer', '/api/plugins/voice/peer-ownership']);\n/** The one public file share shape: a 256-bit base64url token and nothing after it. */\nconst PUBLIC_FILE_SHARE_ROUTE = /^\\/mcp-file\\/([A-Za-z0-9_-]{43})$/u;\n/** Everything under this name is reserved for shares, so a near miss never reaches another route. */\nconst PUBLIC_FILE_SHARE_NAMESPACE = PUBLIC_FILE_SHARE_PREFIX.slice(0, -1);\nconst FORBIDDEN_HEADERS = new Set([\n 'authorization',\n 'connection',\n 'content-length',\n 'cookie',\n 'forwarded',\n 'host',\n 'origin',\n 'transfer-encoding',\n 'upgrade',\n 'x-po-token',\n 'x-po-desktop',\n PO_API_CALLER_LOCALITY_HEADER,\n PO_API_CALLER_DEVICE_ID_HEADER,\n PO_API_CALLER_STEP_UP_HEADER,\n 'x-forwarded-for',\n 'x-forwarded-host',\n 'x-forwarded-proto',\n]);\n\ninterface SealedHttpRequest {\n v: number;\n method: string;\n target: string;\n headers: Array<[string, string]>;\n body?: string;\n}\n\nfunction sealedRequest(value: unknown): SealedHttpRequest | undefined {\n if (typeof value !== 'object' || value === null) return undefined;\n const request = value as Partial<SealedHttpRequest>;\n if (\n request.v !== SEALED_HTTP_VERSION ||\n typeof request.method !== 'string' ||\n !SEALED_HTTP_METHODS.has(request.method) ||\n typeof request.target !== 'string' ||\n !request.target.startsWith('/') ||\n request.target.startsWith('//') ||\n request.target.includes('#') ||\n !Array.isArray(request.headers) ||\n !request.headers.every(\n (header) => Array.isArray(header) && header.length === 2 && header.every((part) => typeof part === 'string'),\n ) ||\n (request.body !== undefined && typeof request.body !== 'string')\n )\n return undefined;\n const parsed = new URL(request.target, 'http://po.local');\n if (parsed.pathname === REMOTE_HTTP_ROUTE || parsed.pathname === REMOTE_CHANNEL_ROUTE) return undefined;\n return request as SealedHttpRequest;\n}\n\nfunction decodedBody(value: string | undefined): Buffer | undefined {\n if (value === undefined) return Buffer.alloc(0);\n if (!/^(?:[A-Za-z\\d+/]{4})*(?:[A-Za-z\\d+/]{2}==|[A-Za-z\\d+/]{3}=)?$/u.test(value)) return undefined;\n const body = Buffer.from(value, 'base64');\n return body.toString('base64') === value ? body : undefined;\n}\n\ninterface ListenerBindings {\n listener: GuardListener;\n incoming?: IncomingMessage;\n sealedDeviceId?: string;\n /** Whether the sealed gateway verified a step-up for this re-dispatched request. */\n sealedStepUp?: 'verified' | 'not-required';\n}\n\nexport interface RemoteRuntimeOptions {\n homeDirectory: string;\n registrationToken: string;\n bundleTrust(): { publicKey: string; revision: number } | undefined;\n /** Signs `po/host-channel-key/v1` fields with the shell signer, for native device reconnects. */\n attestHost?: (fields: readonly string[]) => string | undefined;\n onNotice(message: string): void;\n /** The listener to which a sealed, authenticated request is forwarded. */\n forward(request: Request): Promise<Response>;\n /** Hands a paired device's relayed Chord link to the protocol host, as an upgrade at `pathname` would. */\n acceptProtocol(pathname: string, link: Link, deviceId: string): boolean;\n onDeviceDropped?: (deviceId: string) => void;\n launchTunnel?: TunnelLauncher;\n /** Ports the host itself listens on, which a dev proxy target may never name. */\n reservedPorts?: () => readonly (number | undefined)[];\n}\n\nexport interface RemoteRuntime {\n readonly remote: RemoteAccess;\n /** Short-lived public links served by the tunnel listener itself, never forwarded to the hub. */\n readonly fileShares: PublicFileShares;\n fetchLocal(request: Request): Promise<Response>;\n close(): Promise<void>;\n}\n\nasync function readBody(request: IncomingMessage): Promise<Buffer> {\n const chunks: Buffer[] = [];\n let size = 0;\n for await (const chunk of request) {\n const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\n size += bytes.byteLength;\n if (size > MAX_REQUEST_BYTES) throw new Error('The tunnel request body is too large.');\n chunks.push(bytes);\n }\n return Buffer.concat(chunks);\n}\n\nasync function writeResponse(response: ServerResponse, result: Response): Promise<void> {\n response.writeHead(result.status, Object.fromEntries(result.headers.entries()));\n if (result.body === null) {\n response.end();\n return;\n }\n await pipeline(Readable.fromWeb(result.body as import('node:stream/web').ReadableStream), response);\n}\n\n/** One global control plane and a second socket dedicated to public tunnel traffic. */\nexport function createRemoteRuntime(options: RemoteRuntimeOptions): RemoteRuntime {\n const store = createRemoteAccessStore({\n stateDir: defaultRemoteStateDir(options.homeDirectory),\n onNotice: (message) => options.onNotice(message),\n });\n reapStaleTunnel(store.directory, (message) => options.onNotice(message));\n const devProxy = createDevProxyStore({ stateDir: store.directory, onNotice: (message) => options.onNotice(message) });\n const app = new Hono<{ Bindings: ListenerBindings }>();\n let remote: RemoteAccess;\n let frontendOrigin: string | undefined;\n const fileShares = createPublicFileShares({\n publicOrigin: () => remote.publicOrigin(),\n publicOriginRevision: () => remote.publicOriginRevision(),\n onNotice: (message) => options.onNotice(message),\n });\n /** Exact share route on the decoded path, and the raw path must not have needed decoding. */\n const publicFileShareToken = (request: Request, path: string): string | undefined =>\n new URL(request.url).pathname === path ? PUBLIC_FILE_SHARE_ROUTE.exec(path)?.[1] : undefined;\n const inFileShareNamespace = (request: Request, path: string): boolean =>\n path.startsWith(PUBLIC_FILE_SHARE_NAMESPACE) ||\n new URL(request.url).pathname.startsWith(PUBLIC_FILE_SHARE_NAMESPACE);\n app.use('*', async (context, next) => {\n if (context.env.listener === 'local') return next();\n const path = context.req.path;\n const publicSessionMcp = isPublicSessionMcpRoute(context.req.method, path);\n const publicSessionPeer = context.req.method === 'POST' && path === SESSION_PEER_INBOX_ROUTE;\n const publicVoicePeer = context.req.method === 'POST' && VOICE_PEER_ROUTES.has(path);\n const publicFileShare =\n (context.req.method === 'GET' || context.req.method === 'HEAD') &&\n publicFileShareToken(context.req.raw, path) !== undefined;\n const verdict = originVerdict({\n listener: 'tunnel',\n // Exact OAuth, MCP, and HMAC-authenticated peer routes authenticate at the\n // application layer. Treat an absent Origin like a server-to-server read.\n method: publicSessionMcp || publicSessionPeer || publicVoicePeer ? 'GET' : context.req.method,\n isUpgrade: false,\n origin: context.req.header('origin'),\n host: context.req.header('host'),\n local: localOriginPolicy(0),\n tunnel: remote.tunnelPolicy(),\n });\n if (verdict !== 'allow') return context.json({ error: `Tunnel request refused: ${verdict}.` }, 403);\n // The token is the credential, so a share needs no device. Near misses never fall through.\n if (publicFileShare) return next();\n if (inFileShareNamespace(context.req.raw, path)) return context.json({ error: 'Not found.' }, 404);\n if (isPublicPairingRoute(context.req.method, path) || publicSessionMcp || publicSessionPeer || publicVoicePeer)\n return next();\n const device = remote.authorize(getCookie(context, DEVICE_COOKIE, 'host'));\n if (device === undefined) return context.json({ error: 'This device is not paired.' }, 401);\n if (context.env.sealedDeviceId === device) return next();\n // A proxied dev site issues its own subresource requests and form posts, none of which can go\n // through a gateway that seals whole JSON responses. The same predicate the proxy route uses\n // refuses dot segments, so a path granted here cannot resolve into /api/.\n if (isDevProxyPath(path)) return next();\n if (context.req.method === 'POST' && (path === REMOTE_CHANNEL_ROUTE || path === REMOTE_HTTP_ROUTE)) return next();\n if ((context.req.method === 'GET' || context.req.method === 'HEAD') && !path.startsWith('/api/')) return next();\n return context.json({ error: 'Remote HTTP requests must use the sealed gateway.' }, 401);\n });\n\n remote = createRemoteAccess({\n store,\n onDeviceDropped: options.onDeviceDropped,\n launchTunnel:\n options.launchTunnel ??\n createTunnelLauncher({\n stateDir: store.directory,\n onNotice: (message) => options.onNotice(message),\n onExit: (message) => {\n options.onNotice(`remote access: ${message}`);\n void remote.disable();\n },\n }),\n bindListener: async () => {\n const webSockets = new WebSocketServer({ noServer: true, maxPayload: MAX_REQUEST_BYTES });\n const server = createServer((incoming, outgoing) => {\n void (async () => {\n const abort = new AbortController();\n const disconnected = (): void => abort.abort();\n incoming.once('aborted', disconnected);\n outgoing.once('close', disconnected);\n try {\n const url = new URL(incoming.url ?? '/', `http://${incoming.headers.host ?? 'localhost'}`);\n const headers = new Headers();\n for (const [name, value] of Object.entries(incoming.headers))\n if (value !== undefined) headers.set(name, Array.isArray(value) ? value.join(', ') : value);\n const body = incoming.method === 'GET' || incoming.method === 'HEAD' ? undefined : await readBody(incoming);\n const request = new Request(url, {\n method: incoming.method,\n headers,\n signal: abort.signal,\n ...(body === undefined ? {} : { body }),\n });\n await writeResponse(outgoing, await app.fetch(request, { listener: 'tunnel', incoming }));\n } finally {\n incoming.off('aborted', disconnected);\n outgoing.off('close', disconnected);\n }\n })().catch((error: unknown) => {\n options.onNotice(`tunnel request failed: ${String(error)}`);\n if (!outgoing.headersSent) {\n outgoing.writeHead(500, { 'content-type': 'application/json' });\n outgoing.end(JSON.stringify({ error: 'The tunnel request failed.' }));\n } else outgoing.destroy();\n });\n });\n server.on('upgrade', (incoming, socket, head) => {\n const url = new URL(incoming.url ?? '/', `http://${incoming.headers.host ?? 'localhost'}`);\n const verdict = originVerdict({\n listener: 'tunnel',\n method: incoming.method ?? 'GET',\n isUpgrade: true,\n origin: incoming.headers.origin,\n host: incoming.headers.host,\n local: localOriginPolicy(0),\n tunnel: remote.tunnelPolicy(),\n });\n const cookies = new Map(\n (incoming.headers.cookie ?? '').split(';').map((part) => {\n const at = part.indexOf('=');\n return [part.slice(0, at).trim(), part.slice(at + 1).trim()];\n }),\n );\n const device = remote.authorize(cookies.get(`__Host-${DEVICE_COOKIE}`));\n // Hot module reload: a paired device's dev-site socket goes to the registered target.\n if (isDevProxyPath(url.pathname) && verdict === 'allow' && device !== undefined) {\n const parsed = parseProxyPath(url.pathname);\n const target = parsed.kind === 'match' ? devProxy.find(parsed.name) : undefined;\n if (parsed.kind !== 'match' || target === undefined) {\n socket.write('HTTP/1.1 404 Not Found\\r\\n\\r\\n');\n socket.destroy();\n return;\n }\n webSockets.handleUpgrade(incoming, socket, head, (client) => {\n const untrack = remote.trackSocket(device, (code, reason) => client.close(code, reason));\n client.once('close', untrack);\n relayDevProxySocket(client, {\n port: target.port,\n upstreamPath: `${parsed.upstreamPath}${url.search}`,\n protocols: incoming.headers['sec-websocket-protocol'],\n onNotice: (message) => options.onNotice(message),\n });\n });\n return;\n }\n const channel = device === undefined ? undefined : remote.channelFor(device, 'protocol');\n if (!parsePoSocketPath(url.pathname) || verdict !== 'allow' || !device || !channel) {\n socket.write('HTTP/1.1 403 Forbidden\\r\\n\\r\\n');\n socket.destroy();\n return;\n }\n webSockets.handleUpgrade(incoming, socket, head, (client) => {\n const untrack = remote.trackSocket(device, (code, reason) => client.close(code, reason));\n const relay = createTextLink({\n send: (text) => {\n const sealed = channel.seal(Buffer.from(text));\n if (!sealed.ok) {\n relay.ended('seal failed');\n return;\n }\n client.send(Buffer.from(JSON.stringify(sealed.envelope)));\n },\n close: () => {\n untrack();\n client.close();\n },\n });\n client.on('message', (raw: RawData) => {\n if (!remote.isDeviceAuthorized(device)) {\n relay.ended('device revoked');\n return;\n }\n const bytes = Array.isArray(raw) ? Buffer.concat(raw) : Buffer.from(raw as Buffer);\n let envelope: unknown;\n try {\n envelope = JSON.parse(bytes.toString('utf8')) as unknown;\n } catch {\n relay.ended('invalid envelope');\n return;\n }\n const opened = channel.open(envelope);\n if (!opened.ok) {\n relay.ended('invalid envelope');\n return;\n }\n relay.deliver(Buffer.from(opened.plaintext).toString('utf8'));\n });\n client.on('close', () => relay.ended('client closed'));\n client.on('error', () => relay.ended('client error'));\n try {\n options.acceptProtocol(url.pathname, relay.link, device);\n } catch (error) {\n options.onNotice(`remote protocol could not connect: ${String(error)}`);\n relay.ended('protocol unavailable');\n }\n });\n });\n server.headersTimeout = TUNNEL_HEADERS_TIMEOUT_MS;\n server.requestTimeout = TUNNEL_REQUEST_TIMEOUT_MS;\n server.maxConnections = TUNNEL_CONNECTION_LIMIT;\n server.maxRequestsPerSocket = TUNNEL_REQUESTS_PER_SOCKET;\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject);\n server.listen(0, '127.0.0.1', resolve);\n });\n const address = server.address();\n if (!address || typeof address === 'string') throw new Error('The tunnel listener has no TCP address.');\n return {\n port: address.port,\n close: async () => {\n for (const client of webSockets.clients) client.terminate();\n webSockets.close();\n server.closeAllConnections();\n await new Promise<void>((resolve, reject) => server.close((error) => (error ? reject(error) : resolve())));\n },\n };\n },\n bundleTrust: () => options.bundleTrust(),\n attestHost: (fields) => options.attestHost?.(fields),\n onNotice: (message) => options.onNotice(message),\n });\n registerRemoteRoutes(app as unknown as Hono, {\n remote,\n listenerOf: (context) => (context.env as ListenerBindings).listener,\n });\n registerDevProxyRoutes(app as unknown as Hono, {\n store: devProxy,\n listenerOf: (context) => (context.env as ListenerBindings).listener,\n // Read per call: the tunnel port exists only while remote access is on, and a target checked\n // against a stale list could end up aimed at the cockpit itself.\n reservedPorts: () => [\n remote.tunnelPort(),\n frontendOrigin === undefined ? undefined : Number(new URL(frontendOrigin).port),\n ...(options.reservedPorts?.() ?? []),\n ],\n onNotice: (message) => options.onNotice(message),\n });\n app.post('/api/remote/frontend', async (context) => {\n if (context.env.listener !== 'local')\n return context.json({ error: 'This action is only available on the host.' }, 403);\n if (context.req.header('x-po-web-registration') !== options.registrationToken)\n return context.json({ error: 'The web presentation server was not authenticated.' }, 403);\n let body: unknown;\n try {\n body = await context.req.json();\n } catch {\n return context.json({ error: 'A loopback web origin is required.' }, 400);\n }\n const origin = typeof body === 'object' && body !== null && 'origin' in body ? body.origin : undefined;\n if (typeof origin !== 'string') return context.json({ error: 'A loopback web origin is required.' }, 400);\n let parsed: URL;\n try {\n parsed = new URL(origin);\n } catch {\n return context.json({ error: 'The web origin is invalid.' }, 400);\n }\n if (\n parsed.protocol !== 'http:' ||\n !['127.0.0.1', 'localhost', '[::1]'].includes(parsed.hostname) ||\n parsed.pathname !== '/' ||\n parsed.search !== '' ||\n parsed.hash !== '' ||\n !parsed.port\n )\n return context.json({ error: 'The web origin must be a loopback HTTP listener.' }, 400);\n frontendOrigin = parsed.origin;\n return context.json({ ok: true });\n });\n app.post(REMOTE_HTTP_ROUTE, async (context) => {\n const device = remote.authorize(getCookie(context, DEVICE_COOKIE, 'host'));\n const channel = device === undefined ? undefined : remote.channelFor(device, 'http');\n if (!device || !channel) return context.json({ error: 'A sealed paired device is required.' }, 401);\n let envelope: unknown;\n try {\n envelope = await context.req.json();\n } catch {\n return context.json({ error: 'The sealed request envelope was malformed.' }, 400);\n }\n const opened = channel.open(envelope);\n if (!opened.ok) return context.json({ error: `The sealed request was refused: ${opened.failure}.` }, 400);\n let decoded: unknown;\n try {\n decoded = JSON.parse(new TextDecoder().decode(opened.plaintext)) as unknown;\n } catch {\n return context.json({ error: 'The sealed HTTP request was malformed.' }, 400);\n }\n const inner = sealedRequest(decoded);\n const body = decodedBody(inner?.body);\n if (!inner || !body) return context.json({ error: 'The sealed HTTP request was invalid.' }, 400);\n const target = new URL(inner.target, 'http://po.local');\n const headers = new Headers();\n for (const [name, value] of inner.headers) {\n const normalized = name.toLowerCase();\n if (FORBIDDEN_HEADERS.has(normalized) || normalized.startsWith('sec-')) continue;\n headers.append(name, value);\n }\n const action = stepUpActionFor(inner.method, target.pathname);\n const stepUpKind = remote.trustKind(device) === 'device-key' ? 'device-key' : 'passkey';\n const stepUpNeeded = action !== undefined && remote.stepUpRequired(action, device);\n let stepUpDenied = false;\n if (action && stepUpNeeded) {\n const assertion = headers.get(STEP_UP_HEADER);\n let credential: unknown;\n try {\n credential = assertion ? JSON.parse(Buffer.from(assertion, 'base64url').toString('utf8')) : undefined;\n } catch {\n credential = undefined;\n }\n const ceremony = credential as { ceremonyId?: unknown; response?: unknown; signature?: unknown } | undefined;\n if (stepUpKind === 'device-key') {\n // A device-key session proves the gesture with its biometric key, never a passkey.\n stepUpDenied = !remote.finishDeviceStepUp(device, action, ceremony?.ceremonyId, ceremony?.signature);\n } else if (\n typeof ceremony?.ceremonyId !== 'string' ||\n !(await remote.passkeys().finishStepUp(ceremony.ceremonyId, `device:${device}`, action, ceremony.response))\n )\n stepUpDenied = true;\n }\n headers.delete(STEP_UP_HEADER);\n const sealedStepUp = stepUpNeeded && !stepUpDenied ? 'verified' : 'not-required';\n // Caller stamps are assigned after the sealed request is opened. They never\n // originate from browser-controlled sealed headers.\n headers.set(PO_API_CALLER_LOCALITY_HEADER, 'remote');\n headers.set(PO_API_CALLER_DEVICE_ID_HEADER, device);\n headers.set(PO_API_CALLER_STEP_UP_HEADER, sealedStepUp);\n let response: Response;\n if (stepUpDenied) {\n const gesture = stepUpKind === 'device-key' ? 'biometric confirmation' : 'passkey gesture';\n response = context.json({ error: `This action needs a fresh ${gesture}.`, action, kind: stepUpKind }, 401);\n } else if (isSessionMcpHostRoute(target.pathname) && !isSessionMcpPhoneRoute(inner.method, target.pathname)) {\n response = context.json({ error: 'Session MCP management is only available on the host.' }, 403);\n } else {\n const internal = new Request(target, {\n method: inner.method,\n headers,\n ...(inner.method === 'GET' || inner.method === 'HEAD' ? {} : { body }),\n });\n if (target.pathname === '/api/remote' || target.pathname.startsWith('/api/remote/')) {\n headers.set('cookie', context.req.header('cookie') ?? '');\n headers.set('host', context.req.header('host') ?? '');\n headers.set('origin', context.req.header('origin') ?? '');\n response = await app.fetch(new Request(internal, { headers }), {\n listener: 'tunnel',\n sealedDeviceId: device,\n sealedStepUp,\n });\n } else response = await options.forward(internal);\n }\n const responseBody = Buffer.from(await response.arrayBuffer());\n const sealed = channel.seal(\n new TextEncoder().encode(\n JSON.stringify({\n v: SEALED_HTTP_VERSION,\n status: response.status,\n headers: [...response.headers.entries()],\n body: responseBody.toString('base64'),\n }),\n ),\n );\n return sealed.ok\n ? context.json(sealed.envelope)\n : context.json({ error: `The sealed response failed: ${sealed.failure}.` }, 503);\n });\n app.on(['POST', 'DELETE'], '*', async (context) => {\n if (\n !isPublicSessionMcpRoute(context.req.method, context.req.path) &&\n !(context.req.method === 'POST' && context.req.path === SESSION_PEER_INBOX_ROUTE) &&\n !(context.req.method === 'POST' && VOICE_PEER_ROUTES.has(context.req.path))\n )\n return context.notFound();\n return options.forward(context.req.raw);\n });\n app.on(['GET', 'HEAD'], '*', async (context) => {\n // Served here and never through forward(), which would attach the host token.\n if (inFileShareNamespace(context.req.raw, context.req.path)) {\n const token = publicFileShareToken(context.req.raw, context.req.path);\n return context.env.listener === 'tunnel' && token !== undefined\n ? fileShares.serve(token, context.req.method)\n : context.json({ error: 'Not found.' }, 404);\n }\n if (isPublicSessionMcpRoute(context.req.method, context.req.path)) return options.forward(context.req.raw);\n if (context.req.path.startsWith('/api/')) return context.json({ error: 'Not found.' }, 404);\n if (context.req.path === '/' && remote.authorize(getCookie(context, DEVICE_COOKIE, 'host')) === undefined)\n return context.redirect('/pair');\n if (context.req.path === '/bundle-manifest.json' || context.req.path.startsWith('/bundle-assets/')) {\n const from = new URL(context.req.url);\n return await options.forward(\n new Request(new URL(`${from.pathname}${from.search}`, 'http://po.local'), {\n method: context.req.method,\n }),\n );\n }\n if (!frontendOrigin) return context.json({ error: 'The web presentation server is unavailable.' }, 503);\n const from = new URL(context.req.url);\n const target = new URL(`${from.pathname}${from.search}`, frontendOrigin);\n const result = await fetch(target, { method: context.req.method, redirect: 'manual' });\n const headers = new Headers(result.headers);\n headers.delete('set-cookie');\n return new Response(result.body, { status: result.status, headers });\n });\n\n return {\n remote,\n fileShares,\n fetchLocal: async (request) => app.fetch(request, { listener: 'local' }),\n close: async () => remote.close(),\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"remoteRuntime.cjs","names":["PUBLIC_FILE_SHARE_PREFIX","PO_API_CALLER_LOCALITY_HEADER","PO_API_CALLER_DEVICE_ID_HEADER","PO_API_CALLER_STEP_UP_HEADER","REMOTE_HTTP_ROUTE","REMOTE_CHANNEL_ROUTE","pipeline","Readable","createRemoteAccessStore","defaultRemoteStateDir","createDevProxyStore","Hono","createPublicFileShares","isPublicSessionMcpRoute","originVerdict","localOriginPolicy","isPublicPairingRoute","getCookie","DEVICE_COOKIE","isDevProxyPath","createRemoteAccess","createTunnelLauncher","WebSocketServer","createServer","parseProxyPath","parsePoSocketPath","createTextLink","stepUpActionFor","STEP_UP_HEADER","isSessionMcpHostRoute","isSessionMcpPhoneRoute"],"sources":["../src/server/remoteRuntime.ts"],"sourcesContent":["import { createServer, type IncomingMessage, type ServerResponse } from 'node:http';\nimport { Readable } from 'node:stream';\nimport { pipeline } from 'node:stream/promises';\n\nimport type { Link } from '@earendil-works/chord';\nimport { Hono } from 'hono';\nimport { getCookie } from 'hono/cookie';\nimport { WebSocketServer, type RawData } from 'ws';\n\nimport { DEVICE_COOKIE, REMOTE_CHANNEL_ROUTE, REMOTE_HTTP_ROUTE, STEP_UP_HEADER } from '../constants/remote';\nimport {\n PO_API_CALLER_DEVICE_ID_HEADER,\n PO_API_CALLER_LOCALITY_HEADER,\n PO_API_CALLER_STEP_UP_HEADER,\n parsePoSocketPath,\n} from '../schemas/packageApi';\nimport { createTextLink } from '../schemas/protocolContracts';\nimport { isDevProxyPath, parseProxyPath } from '../services/devProxyPolicy';\nimport { createDevProxyStore } from '../services/devProxyStore';\nimport { createPublicFileShares, PUBLIC_FILE_SHARE_PREFIX, type PublicFileShares } from '../services/publicFileShares';\nimport { createRemoteAccess, type RemoteAccess } from '../services/remoteAccess';\nimport { createRemoteAccessStore, defaultRemoteStateDir } from '../services/remoteAccessStore';\nimport {\n isPublicPairingRoute,\n originVerdict,\n type GuardListener,\n localOriginPolicy,\n} from '../services/remoteGuardPolicy';\nimport { createTunnelLauncher, reapStaleTunnel } from '../services/tunnelProcess';\nimport { stepUpActionFor } from '../services/webauthnPolicy';\nimport { type TunnelLauncher } from '../types/remote';\nimport { registerDevProxyRoutes } from './devProxyRoutes';\nimport { relayDevProxySocket } from './devProxySocket';\nimport { registerRemoteRoutes } from './remoteRoutes';\nimport { isPublicSessionMcpRoute, isSessionMcpHostRoute, isSessionMcpPhoneRoute } from './sessionMcpRoutes';\n\nexport { relayDevProxySocket, type DevProxySocketInput } from './devProxySocket';\n\nconst MAX_REQUEST_BYTES = 8 * 1024 * 1024;\nconst TUNNEL_HEADERS_TIMEOUT_MS = 5_000;\nconst TUNNEL_REQUEST_TIMEOUT_MS = 10_000;\nconst TUNNEL_CONNECTION_LIMIT = 64;\nconst TUNNEL_REQUESTS_PER_SOCKET = 100;\nconst SEALED_HTTP_VERSION = 1;\nconst SEALED_HTTP_METHODS = new Set(['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE']);\n/** HMAC-authenticated peer routes are the only non-device tunnel APIs. */\nconst SESSION_PEER_INBOX_ROUTE = '/api/plugins/session-peer/inbox';\nconst VOICE_PEER_ROUTES = new Set(['/api/plugins/voice/peer', '/api/plugins/voice/peer-ownership']);\n/** The one public file share shape: a 256-bit base64url token and nothing after it. */\nconst PUBLIC_FILE_SHARE_ROUTE = /^\\/mcp-file\\/([A-Za-z0-9_-]{43})$/u;\n/** Everything under this name is reserved for shares, so a near miss never reaches another route. */\nconst PUBLIC_FILE_SHARE_NAMESPACE = PUBLIC_FILE_SHARE_PREFIX.slice(0, -1);\nconst FORBIDDEN_HEADERS = new Set([\n 'authorization',\n 'connection',\n 'content-length',\n 'cookie',\n 'forwarded',\n 'host',\n 'origin',\n 'transfer-encoding',\n 'upgrade',\n 'x-po-token',\n 'x-po-desktop',\n PO_API_CALLER_LOCALITY_HEADER,\n PO_API_CALLER_DEVICE_ID_HEADER,\n PO_API_CALLER_STEP_UP_HEADER,\n 'x-forwarded-for',\n 'x-forwarded-host',\n 'x-forwarded-proto',\n]);\n\ninterface SealedHttpRequest {\n v: number;\n method: string;\n target: string;\n headers: Array<[string, string]>;\n body?: string;\n}\n\nfunction sealedRequest(value: unknown): SealedHttpRequest | undefined {\n if (typeof value !== 'object' || value === null) return undefined;\n const request = value as Partial<SealedHttpRequest>;\n if (\n request.v !== SEALED_HTTP_VERSION ||\n typeof request.method !== 'string' ||\n !SEALED_HTTP_METHODS.has(request.method) ||\n typeof request.target !== 'string' ||\n !request.target.startsWith('/') ||\n request.target.startsWith('//') ||\n request.target.includes('#') ||\n !Array.isArray(request.headers) ||\n !request.headers.every(\n (header) => Array.isArray(header) && header.length === 2 && header.every((part) => typeof part === 'string'),\n ) ||\n (request.body !== undefined && typeof request.body !== 'string')\n )\n return undefined;\n const parsed = new URL(request.target, 'http://po.local');\n if (parsed.pathname === REMOTE_HTTP_ROUTE || parsed.pathname === REMOTE_CHANNEL_ROUTE) return undefined;\n return request as SealedHttpRequest;\n}\n\nfunction decodedBody(value: string | undefined): Buffer | undefined {\n if (value === undefined) return Buffer.alloc(0);\n if (!/^(?:[A-Za-z\\d+/]{4})*(?:[A-Za-z\\d+/]{2}==|[A-Za-z\\d+/]{3}=)?$/u.test(value)) return undefined;\n const body = Buffer.from(value, 'base64');\n return body.toString('base64') === value ? body : undefined;\n}\n\ninterface ListenerBindings {\n listener: GuardListener;\n incoming?: IncomingMessage;\n sealedDeviceId?: string;\n /** Whether the sealed gateway verified a step-up for this re-dispatched request. */\n sealedStepUp?: 'verified' | 'not-required';\n}\n\n/** Only public exposure intent is durable. Credentials and pairing state never belong here. */\nexport interface TunnelIntent {\n hostname: string;\n closesAt?: number;\n}\n\nexport interface RemoteRuntimeOptions {\n homeDirectory: string;\n registrationToken: string;\n bundleTrust(): { publicKey: string; revision: number } | undefined;\n /** Signs `po/host-channel-key/v1` fields with the shell signer, for native device reconnects. */\n attestHost?: (fields: readonly string[]) => string | undefined;\n onNotice(message: string): void;\n /** The listener to which a sealed, authenticated request is forwarded. */\n forward(request: Request): Promise<Response>;\n /** Hands a paired device's relayed Chord link to the protocol host, as an upgrade at `pathname` would. */\n acceptProtocol(pathname: string, link: Link, deviceId: string): boolean;\n onDeviceDropped?: (deviceId: string) => void;\n onTunnelIntentChanged?: (intent: TunnelIntent | undefined) => void;\n launchTunnel?: TunnelLauncher;\n /** Ports the host itself listens on, which a dev proxy target may never name. */\n reservedPorts?: () => readonly (number | undefined)[];\n}\n\nexport interface RemoteRuntime {\n readonly remote: RemoteAccess;\n /** Short-lived public links served by the tunnel listener itself, never forwarded to the hub. */\n readonly fileShares: PublicFileShares;\n fetchLocal(request: Request): Promise<Response>;\n /** Automatically restores only a valid, unchanged named hostname and unexpired deadline. */\n restoreTunnel(intent: TunnelIntent): Promise<{ ok: true } | { ok: false; error: string }>;\n tunnelIntent(): TunnelIntent | undefined;\n close(): Promise<void>;\n}\n\nasync function readBody(request: IncomingMessage): Promise<Buffer> {\n const chunks: Buffer[] = [];\n let size = 0;\n for await (const chunk of request) {\n const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\n size += bytes.byteLength;\n if (size > MAX_REQUEST_BYTES) throw new Error('The tunnel request body is too large.');\n chunks.push(bytes);\n }\n return Buffer.concat(chunks);\n}\n\nasync function writeResponse(response: ServerResponse, result: Response): Promise<void> {\n response.writeHead(result.status, Object.fromEntries(result.headers.entries()));\n if (result.body === null) {\n response.end();\n return;\n }\n await pipeline(Readable.fromWeb(result.body as import('node:stream/web').ReadableStream), response);\n}\n\n/** One global control plane and a second socket dedicated to public tunnel traffic. */\nexport function createRemoteRuntime(options: RemoteRuntimeOptions): RemoteRuntime {\n const store = createRemoteAccessStore({\n stateDir: defaultRemoteStateDir(options.homeDirectory),\n onNotice: (message) => options.onNotice(message),\n });\n reapStaleTunnel(store.directory, (message) => options.onNotice(message));\n const devProxy = createDevProxyStore({ stateDir: store.directory, onNotice: (message) => options.onNotice(message) });\n const app = new Hono<{ Bindings: ListenerBindings }>();\n let remote: RemoteAccess;\n let closing = false;\n let lastIntent: TunnelIntent | undefined;\n let frontendOrigin: string | undefined;\n const tunnelIntent = (): TunnelIntent | undefined => {\n const state = remote.state();\n const tunnel = remote.settings().tunnel;\n if (state.status !== 'on' || tunnel.kind !== 'named' || state.publicUrl === undefined) return undefined;\n if (new URL(state.publicUrl).hostname !== tunnel.hostname) return undefined;\n return {\n hostname: tunnel.hostname,\n ...(state.closesAt === undefined ? {} : { closesAt: Date.parse(state.closesAt) }),\n };\n };\n const publishIntent = (): void => {\n if (closing) return;\n const intent = tunnelIntent();\n if (intent?.hostname === lastIntent?.hostname && intent?.closesAt === lastIntent?.closesAt) return;\n lastIntent = intent;\n options.onTunnelIntentChanged?.(intent);\n };\n const fileShares = createPublicFileShares({\n publicOrigin: () => remote.publicOrigin(),\n publicOriginRevision: () => remote.publicOriginRevision(),\n onNotice: (message) => options.onNotice(message),\n });\n /** Exact share route on the decoded path, and the raw path must not have needed decoding. */\n const publicFileShareToken = (request: Request, path: string): string | undefined =>\n new URL(request.url).pathname === path ? PUBLIC_FILE_SHARE_ROUTE.exec(path)?.[1] : undefined;\n const inFileShareNamespace = (request: Request, path: string): boolean =>\n path.startsWith(PUBLIC_FILE_SHARE_NAMESPACE) ||\n new URL(request.url).pathname.startsWith(PUBLIC_FILE_SHARE_NAMESPACE);\n app.use('*', async (context, next) => {\n if (context.env.listener === 'local') return next();\n const path = context.req.path;\n const publicSessionMcp = isPublicSessionMcpRoute(context.req.method, path);\n const publicSessionPeer = context.req.method === 'POST' && path === SESSION_PEER_INBOX_ROUTE;\n const publicVoicePeer = context.req.method === 'POST' && VOICE_PEER_ROUTES.has(path);\n const publicFileShare =\n (context.req.method === 'GET' || context.req.method === 'HEAD') &&\n publicFileShareToken(context.req.raw, path) !== undefined;\n const verdict = originVerdict({\n listener: 'tunnel',\n // Exact OAuth, MCP, and HMAC-authenticated peer routes authenticate at the\n // application layer. Treat an absent Origin like a server-to-server read.\n method: publicSessionMcp || publicSessionPeer || publicVoicePeer ? 'GET' : context.req.method,\n isUpgrade: false,\n origin: context.req.header('origin'),\n host: context.req.header('host'),\n local: localOriginPolicy(0),\n tunnel: remote.tunnelPolicy(),\n });\n if (verdict !== 'allow') return context.json({ error: `Tunnel request refused: ${verdict}.` }, 403);\n // The token is the credential, so a share needs no device. Near misses never fall through.\n if (publicFileShare) return next();\n if (inFileShareNamespace(context.req.raw, path)) return context.json({ error: 'Not found.' }, 404);\n if (isPublicPairingRoute(context.req.method, path) || publicSessionMcp || publicSessionPeer || publicVoicePeer)\n return next();\n const device = remote.authorize(getCookie(context, DEVICE_COOKIE, 'host'));\n if (device === undefined) return context.json({ error: 'This device is not paired.' }, 401);\n if (context.env.sealedDeviceId === device) return next();\n // A proxied dev site issues its own subresource requests and form posts, none of which can go\n // through a gateway that seals whole JSON responses. The same predicate the proxy route uses\n // refuses dot segments, so a path granted here cannot resolve into /api/.\n if (isDevProxyPath(path)) return next();\n if (context.req.method === 'POST' && (path === REMOTE_CHANNEL_ROUTE || path === REMOTE_HTTP_ROUTE)) return next();\n if ((context.req.method === 'GET' || context.req.method === 'HEAD') && !path.startsWith('/api/')) return next();\n return context.json({ error: 'Remote HTTP requests must use the sealed gateway.' }, 401);\n });\n\n remote = createRemoteAccess({\n store,\n broadcastAll: publishIntent,\n onDeviceDropped: options.onDeviceDropped,\n launchTunnel:\n options.launchTunnel ??\n createTunnelLauncher({\n stateDir: store.directory,\n onNotice: (message) => options.onNotice(message),\n onExit: (message) => {\n options.onNotice(`remote access: ${message}`);\n void remote.disable();\n },\n }),\n bindListener: async () => {\n const webSockets = new WebSocketServer({ noServer: true, maxPayload: MAX_REQUEST_BYTES });\n const server = createServer((incoming, outgoing) => {\n void (async () => {\n const abort = new AbortController();\n const disconnected = (): void => abort.abort();\n incoming.once('aborted', disconnected);\n outgoing.once('close', disconnected);\n try {\n const url = new URL(incoming.url ?? '/', `http://${incoming.headers.host ?? 'localhost'}`);\n const headers = new Headers();\n for (const [name, value] of Object.entries(incoming.headers))\n if (value !== undefined) headers.set(name, Array.isArray(value) ? value.join(', ') : value);\n const body = incoming.method === 'GET' || incoming.method === 'HEAD' ? undefined : await readBody(incoming);\n const request = new Request(url, {\n method: incoming.method,\n headers,\n signal: abort.signal,\n ...(body === undefined ? {} : { body }),\n });\n await writeResponse(outgoing, await app.fetch(request, { listener: 'tunnel', incoming }));\n } finally {\n incoming.off('aborted', disconnected);\n outgoing.off('close', disconnected);\n }\n })().catch((error: unknown) => {\n options.onNotice(`tunnel request failed: ${String(error)}`);\n if (!outgoing.headersSent) {\n outgoing.writeHead(500, { 'content-type': 'application/json' });\n outgoing.end(JSON.stringify({ error: 'The tunnel request failed.' }));\n } else outgoing.destroy();\n });\n });\n server.on('upgrade', (incoming, socket, head) => {\n const url = new URL(incoming.url ?? '/', `http://${incoming.headers.host ?? 'localhost'}`);\n const verdict = originVerdict({\n listener: 'tunnel',\n method: incoming.method ?? 'GET',\n isUpgrade: true,\n origin: incoming.headers.origin,\n host: incoming.headers.host,\n local: localOriginPolicy(0),\n tunnel: remote.tunnelPolicy(),\n });\n const cookies = new Map(\n (incoming.headers.cookie ?? '').split(';').map((part) => {\n const at = part.indexOf('=');\n return [part.slice(0, at).trim(), part.slice(at + 1).trim()];\n }),\n );\n const device = remote.authorize(cookies.get(`__Host-${DEVICE_COOKIE}`));\n // Hot module reload: a paired device's dev-site socket goes to the registered target.\n if (isDevProxyPath(url.pathname) && verdict === 'allow' && device !== undefined) {\n const parsed = parseProxyPath(url.pathname);\n const target = parsed.kind === 'match' ? devProxy.find(parsed.name) : undefined;\n if (parsed.kind !== 'match' || target === undefined) {\n socket.write('HTTP/1.1 404 Not Found\\r\\n\\r\\n');\n socket.destroy();\n return;\n }\n webSockets.handleUpgrade(incoming, socket, head, (client) => {\n const untrack = remote.trackSocket(device, (code, reason) => client.close(code, reason));\n client.once('close', untrack);\n relayDevProxySocket(client, {\n port: target.port,\n upstreamPath: `${parsed.upstreamPath}${url.search}`,\n protocols: incoming.headers['sec-websocket-protocol'],\n onNotice: (message) => options.onNotice(message),\n });\n });\n return;\n }\n const channel = device === undefined ? undefined : remote.channelFor(device, 'protocol');\n if (!parsePoSocketPath(url.pathname) || verdict !== 'allow' || !device || !channel) {\n socket.write('HTTP/1.1 403 Forbidden\\r\\n\\r\\n');\n socket.destroy();\n return;\n }\n webSockets.handleUpgrade(incoming, socket, head, (client) => {\n const untrack = remote.trackSocket(device, (code, reason) => client.close(code, reason));\n const relay = createTextLink({\n send: (text) => {\n const sealed = channel.seal(Buffer.from(text));\n if (!sealed.ok) {\n relay.ended('seal failed');\n return;\n }\n client.send(Buffer.from(JSON.stringify(sealed.envelope)));\n },\n close: () => {\n untrack();\n client.close();\n },\n });\n client.on('message', (raw: RawData) => {\n if (!remote.isDeviceAuthorized(device)) {\n relay.ended('device revoked');\n return;\n }\n const bytes = Array.isArray(raw) ? Buffer.concat(raw) : Buffer.from(raw as Buffer);\n let envelope: unknown;\n try {\n envelope = JSON.parse(bytes.toString('utf8')) as unknown;\n } catch {\n relay.ended('invalid envelope');\n return;\n }\n const opened = channel.open(envelope);\n if (!opened.ok) {\n relay.ended('invalid envelope');\n return;\n }\n relay.deliver(Buffer.from(opened.plaintext).toString('utf8'));\n });\n client.on('close', () => relay.ended('client closed'));\n client.on('error', () => relay.ended('client error'));\n try {\n options.acceptProtocol(url.pathname, relay.link, device);\n } catch (error) {\n options.onNotice(`remote protocol could not connect: ${String(error)}`);\n relay.ended('protocol unavailable');\n }\n });\n });\n server.headersTimeout = TUNNEL_HEADERS_TIMEOUT_MS;\n server.requestTimeout = TUNNEL_REQUEST_TIMEOUT_MS;\n server.maxConnections = TUNNEL_CONNECTION_LIMIT;\n server.maxRequestsPerSocket = TUNNEL_REQUESTS_PER_SOCKET;\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject);\n server.listen(0, '127.0.0.1', resolve);\n });\n const address = server.address();\n if (!address || typeof address === 'string') throw new Error('The tunnel listener has no TCP address.');\n return {\n port: address.port,\n close: async () => {\n for (const client of webSockets.clients) client.terminate();\n webSockets.close();\n server.closeAllConnections();\n await new Promise<void>((resolve, reject) => server.close((error) => (error ? reject(error) : resolve())));\n },\n };\n },\n bundleTrust: () => options.bundleTrust(),\n attestHost: (fields) => options.attestHost?.(fields),\n onNotice: (message) => options.onNotice(message),\n });\n registerRemoteRoutes(app as unknown as Hono, {\n remote,\n listenerOf: (context) => (context.env as ListenerBindings).listener,\n });\n registerDevProxyRoutes(app as unknown as Hono, {\n store: devProxy,\n listenerOf: (context) => (context.env as ListenerBindings).listener,\n // Read per call: the tunnel port exists only while remote access is on, and a target checked\n // against a stale list could end up aimed at the cockpit itself.\n reservedPorts: () => [\n remote.tunnelPort(),\n frontendOrigin === undefined ? undefined : Number(new URL(frontendOrigin).port),\n ...(options.reservedPorts?.() ?? []),\n ],\n onNotice: (message) => options.onNotice(message),\n });\n app.post('/api/remote/frontend', async (context) => {\n if (context.env.listener !== 'local')\n return context.json({ error: 'This action is only available on the host.' }, 403);\n if (context.req.header('x-po-web-registration') !== options.registrationToken)\n return context.json({ error: 'The web presentation server was not authenticated.' }, 403);\n let body: unknown;\n try {\n body = await context.req.json();\n } catch {\n return context.json({ error: 'A loopback web origin is required.' }, 400);\n }\n const origin = typeof body === 'object' && body !== null && 'origin' in body ? body.origin : undefined;\n if (typeof origin !== 'string') return context.json({ error: 'A loopback web origin is required.' }, 400);\n let parsed: URL;\n try {\n parsed = new URL(origin);\n } catch {\n return context.json({ error: 'The web origin is invalid.' }, 400);\n }\n if (\n parsed.protocol !== 'http:' ||\n !['127.0.0.1', 'localhost', '[::1]'].includes(parsed.hostname) ||\n parsed.pathname !== '/' ||\n parsed.search !== '' ||\n parsed.hash !== '' ||\n !parsed.port\n )\n return context.json({ error: 'The web origin must be a loopback HTTP listener.' }, 400);\n frontendOrigin = parsed.origin;\n return context.json({ ok: true });\n });\n app.post(REMOTE_HTTP_ROUTE, async (context) => {\n const device = remote.authorize(getCookie(context, DEVICE_COOKIE, 'host'));\n const channel = device === undefined ? undefined : remote.channelFor(device, 'http');\n if (!device || !channel) return context.json({ error: 'A sealed paired device is required.' }, 401);\n let envelope: unknown;\n try {\n envelope = await context.req.json();\n } catch {\n return context.json({ error: 'The sealed request envelope was malformed.' }, 400);\n }\n const opened = channel.open(envelope);\n if (!opened.ok) return context.json({ error: `The sealed request was refused: ${opened.failure}.` }, 400);\n let decoded: unknown;\n try {\n decoded = JSON.parse(new TextDecoder().decode(opened.plaintext)) as unknown;\n } catch {\n return context.json({ error: 'The sealed HTTP request was malformed.' }, 400);\n }\n const inner = sealedRequest(decoded);\n const body = decodedBody(inner?.body);\n if (!inner || !body) return context.json({ error: 'The sealed HTTP request was invalid.' }, 400);\n const target = new URL(inner.target, 'http://po.local');\n const headers = new Headers();\n for (const [name, value] of inner.headers) {\n const normalized = name.toLowerCase();\n if (FORBIDDEN_HEADERS.has(normalized) || normalized.startsWith('sec-')) continue;\n headers.append(name, value);\n }\n const action = stepUpActionFor(inner.method, target.pathname);\n const stepUpKind = remote.trustKind(device) === 'device-key' ? 'device-key' : 'passkey';\n const stepUpNeeded = action !== undefined && remote.stepUpRequired(action, device);\n let stepUpDenied = false;\n if (action && stepUpNeeded) {\n const assertion = headers.get(STEP_UP_HEADER);\n let credential: unknown;\n try {\n credential = assertion ? JSON.parse(Buffer.from(assertion, 'base64url').toString('utf8')) : undefined;\n } catch {\n credential = undefined;\n }\n const ceremony = credential as { ceremonyId?: unknown; response?: unknown; signature?: unknown } | undefined;\n if (stepUpKind === 'device-key') {\n // A device-key session proves the gesture with its biometric key, never a passkey.\n stepUpDenied = !remote.finishDeviceStepUp(device, action, ceremony?.ceremonyId, ceremony?.signature);\n } else if (\n typeof ceremony?.ceremonyId !== 'string' ||\n !(await remote.passkeys().finishStepUp(ceremony.ceremonyId, `device:${device}`, action, ceremony.response))\n )\n stepUpDenied = true;\n }\n headers.delete(STEP_UP_HEADER);\n const sealedStepUp = stepUpNeeded && !stepUpDenied ? 'verified' : 'not-required';\n // Caller stamps are assigned after the sealed request is opened. They never\n // originate from browser-controlled sealed headers.\n headers.set(PO_API_CALLER_LOCALITY_HEADER, 'remote');\n headers.set(PO_API_CALLER_DEVICE_ID_HEADER, device);\n headers.set(PO_API_CALLER_STEP_UP_HEADER, sealedStepUp);\n let response: Response;\n if (stepUpDenied) {\n const gesture = stepUpKind === 'device-key' ? 'biometric confirmation' : 'passkey gesture';\n response = context.json({ error: `This action needs a fresh ${gesture}.`, action, kind: stepUpKind }, 401);\n } else if (isSessionMcpHostRoute(target.pathname) && !isSessionMcpPhoneRoute(inner.method, target.pathname)) {\n response = context.json({ error: 'Session MCP management is only available on the host.' }, 403);\n } else {\n const internal = new Request(target, {\n method: inner.method,\n headers,\n ...(inner.method === 'GET' || inner.method === 'HEAD' ? {} : { body }),\n });\n if (target.pathname === '/api/remote' || target.pathname.startsWith('/api/remote/')) {\n headers.set('cookie', context.req.header('cookie') ?? '');\n headers.set('host', context.req.header('host') ?? '');\n headers.set('origin', context.req.header('origin') ?? '');\n response = await app.fetch(new Request(internal, { headers }), {\n listener: 'tunnel',\n sealedDeviceId: device,\n sealedStepUp,\n });\n } else response = await options.forward(internal);\n }\n const responseBody = Buffer.from(await response.arrayBuffer());\n const sealed = channel.seal(\n new TextEncoder().encode(\n JSON.stringify({\n v: SEALED_HTTP_VERSION,\n status: response.status,\n headers: [...response.headers.entries()],\n body: responseBody.toString('base64'),\n }),\n ),\n );\n return sealed.ok\n ? context.json(sealed.envelope)\n : context.json({ error: `The sealed response failed: ${sealed.failure}.` }, 503);\n });\n app.on(['POST', 'DELETE'], '*', async (context) => {\n if (\n !isPublicSessionMcpRoute(context.req.method, context.req.path) &&\n !(context.req.method === 'POST' && context.req.path === SESSION_PEER_INBOX_ROUTE) &&\n !(context.req.method === 'POST' && VOICE_PEER_ROUTES.has(context.req.path))\n )\n return context.notFound();\n return options.forward(context.req.raw);\n });\n app.on(['GET', 'HEAD'], '*', async (context) => {\n // Served here and never through forward(), which would attach the host token.\n if (inFileShareNamespace(context.req.raw, context.req.path)) {\n const token = publicFileShareToken(context.req.raw, context.req.path);\n return context.env.listener === 'tunnel' && token !== undefined\n ? fileShares.serve(token, context.req.method)\n : context.json({ error: 'Not found.' }, 404);\n }\n if (isPublicSessionMcpRoute(context.req.method, context.req.path)) return options.forward(context.req.raw);\n if (context.req.path.startsWith('/api/')) return context.json({ error: 'Not found.' }, 404);\n if (context.req.path === '/' && remote.authorize(getCookie(context, DEVICE_COOKIE, 'host')) === undefined)\n return context.redirect('/pair');\n if (context.req.path === '/bundle-manifest.json' || context.req.path.startsWith('/bundle-assets/')) {\n const from = new URL(context.req.url);\n return await options.forward(\n new Request(new URL(`${from.pathname}${from.search}`, 'http://po.local'), {\n method: context.req.method,\n }),\n );\n }\n if (!frontendOrigin) return context.json({ error: 'The web presentation server is unavailable.' }, 503);\n const from = new URL(context.req.url);\n const target = new URL(`${from.pathname}${from.search}`, frontendOrigin);\n const result = await fetch(target, { method: context.req.method, redirect: 'manual' });\n const headers = new Headers(result.headers);\n headers.delete('set-cookie');\n return new Response(result.body, { status: result.status, headers });\n });\n\n return {\n remote,\n fileShares,\n fetchLocal: async (request) => app.fetch(request, { listener: 'local' }),\n tunnelIntent,\n restoreTunnel: async (intent) => {\n const parsed = store.settingsForRestore();\n const tunnel = parsed?.settings.tunnel;\n if (\n closing ||\n typeof intent?.hostname !== 'string' ||\n tunnel?.kind !== 'named' ||\n tunnel.hostname !== intent.hostname ||\n parsed?.warnings.length !== 0 ||\n remote.settings().tunnel.kind !== 'named' ||\n (intent.closesAt !== undefined && (!Number.isFinite(intent.closesAt) || intent.closesAt <= Date.now()))\n )\n return { ok: false, error: 'The named tunnel intent is no longer valid for automatic restore.' };\n // Use the validated current file, not the store's potentially stale normalized snapshot.\n remote.updateSettings(parsed.settings);\n return remote.enable(intent.closesAt);\n },\n close: async () => {\n // A runtime shutdown is not a user disable. The owner decides whether to retain intent.\n closing = true;\n await remote.close();\n },\n };\n}\n"],"mappings":"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"}
|
package/dist/remoteRuntime.d.mts
CHANGED
|
@@ -4,6 +4,11 @@ import { RemoteAccess } from "./src/services/remoteAccess/index.mjs";
|
|
|
4
4
|
import { DevProxySocketInput, relayDevProxySocket } from "./src/server/devProxySocket.mjs";
|
|
5
5
|
import { Link } from "@earendil-works/chord";
|
|
6
6
|
//#region src/server/remoteRuntime.d.ts
|
|
7
|
+
/** Only public exposure intent is durable. Credentials and pairing state never belong here. */
|
|
8
|
+
export interface TunnelIntent {
|
|
9
|
+
hostname: string;
|
|
10
|
+
closesAt?: number;
|
|
11
|
+
}
|
|
7
12
|
export interface RemoteRuntimeOptions {
|
|
8
13
|
homeDirectory: string;
|
|
9
14
|
registrationToken: string;
|
|
@@ -19,6 +24,7 @@ export interface RemoteRuntimeOptions {
|
|
|
19
24
|
/** Hands a paired device's relayed Chord link to the protocol host, as an upgrade at `pathname` would. */
|
|
20
25
|
acceptProtocol(pathname: string, link: Link, deviceId: string): boolean;
|
|
21
26
|
onDeviceDropped?: (deviceId: string) => void;
|
|
27
|
+
onTunnelIntentChanged?: (intent: TunnelIntent | undefined) => void;
|
|
22
28
|
launchTunnel?: TunnelLauncher;
|
|
23
29
|
/** Ports the host itself listens on, which a dev proxy target may never name. */
|
|
24
30
|
reservedPorts?: () => readonly (number | undefined)[];
|
|
@@ -28,6 +34,14 @@ export interface RemoteRuntime {
|
|
|
28
34
|
/** Short-lived public links served by the tunnel listener itself, never forwarded to the hub. */
|
|
29
35
|
readonly fileShares: PublicFileShares;
|
|
30
36
|
fetchLocal(request: Request): Promise<Response>;
|
|
37
|
+
/** Automatically restores only a valid, unchanged named hostname and unexpired deadline. */
|
|
38
|
+
restoreTunnel(intent: TunnelIntent): Promise<{
|
|
39
|
+
ok: true;
|
|
40
|
+
} | {
|
|
41
|
+
ok: false;
|
|
42
|
+
error: string;
|
|
43
|
+
}>;
|
|
44
|
+
tunnelIntent(): TunnelIntent | undefined;
|
|
31
45
|
close(): Promise<void>;
|
|
32
46
|
}
|
|
33
47
|
/** One global control plane and a second socket dedicated to public tunnel traffic. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remoteRuntime.d.mts","names":[],"sources":["../src/server/remoteRuntime.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"remoteRuntime.d.mts","names":[],"sources":["../src/server/remoteRuntime.ts"],"mappings":";;;;;;;iBAuHiB;EACf;EACA;;iBAGe;EACf;EACA;EACA;IAAiB;IAAmB;;;EAEpC,cAAc;EACd,SAAS;;EAET,QAAQ,SAAS,UAAU,QAAQ;;EAEnC,eAAe,kBAAkB,MAAM,MAAM;EAC7C,mBAAmB;EACnB,yBAAyB,QAAQ;EACjC,eAAe;;EAEf;;iBAGe;WACN,QAAQ;;WAER,YAAY;EACrB,WAAW,SAAS,UAAU,QAAQ;;EAEtC,cAAc,QAAQ,eAAe;IAAU;;IAAe;IAAW;;EACzE,gBAAgB;EAChB,SAAS;;;wBAyBK,oBAAoB,SAAS,uBAAuB"}
|
package/dist/remoteRuntime.mjs
CHANGED
|
@@ -109,7 +109,26 @@ function createRemoteRuntime(options) {
|
|
|
109
109
|
});
|
|
110
110
|
const app = new Hono();
|
|
111
111
|
let remote;
|
|
112
|
+
let closing = false;
|
|
113
|
+
let lastIntent;
|
|
112
114
|
let frontendOrigin;
|
|
115
|
+
const tunnelIntent = () => {
|
|
116
|
+
const state = remote.state();
|
|
117
|
+
const tunnel = remote.settings().tunnel;
|
|
118
|
+
if (state.status !== "on" || tunnel.kind !== "named" || state.publicUrl === void 0) return void 0;
|
|
119
|
+
if (new URL(state.publicUrl).hostname !== tunnel.hostname) return void 0;
|
|
120
|
+
return {
|
|
121
|
+
hostname: tunnel.hostname,
|
|
122
|
+
...state.closesAt === void 0 ? {} : { closesAt: Date.parse(state.closesAt) }
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
const publishIntent = () => {
|
|
126
|
+
if (closing) return;
|
|
127
|
+
const intent = tunnelIntent();
|
|
128
|
+
if (intent?.hostname === lastIntent?.hostname && intent?.closesAt === lastIntent?.closesAt) return;
|
|
129
|
+
lastIntent = intent;
|
|
130
|
+
options.onTunnelIntentChanged?.(intent);
|
|
131
|
+
};
|
|
113
132
|
const fileShares = createPublicFileShares({
|
|
114
133
|
publicOrigin: () => remote.publicOrigin(),
|
|
115
134
|
publicOriginRevision: () => remote.publicOriginRevision(),
|
|
@@ -148,6 +167,7 @@ function createRemoteRuntime(options) {
|
|
|
148
167
|
});
|
|
149
168
|
remote = createRemoteAccess({
|
|
150
169
|
store,
|
|
170
|
+
broadcastAll: publishIntent,
|
|
151
171
|
onDeviceDropped: options.onDeviceDropped,
|
|
152
172
|
launchTunnel: options.launchTunnel ?? createTunnelLauncher({
|
|
153
173
|
stateDir: store.directory,
|
|
@@ -464,7 +484,21 @@ function createRemoteRuntime(options) {
|
|
|
464
484
|
remote,
|
|
465
485
|
fileShares,
|
|
466
486
|
fetchLocal: async (request) => app.fetch(request, { listener: "local" }),
|
|
467
|
-
|
|
487
|
+
tunnelIntent,
|
|
488
|
+
restoreTunnel: async (intent) => {
|
|
489
|
+
const parsed = store.settingsForRestore();
|
|
490
|
+
const tunnel = parsed?.settings.tunnel;
|
|
491
|
+
if (closing || typeof intent?.hostname !== "string" || tunnel?.kind !== "named" || tunnel.hostname !== intent.hostname || parsed?.warnings.length !== 0 || remote.settings().tunnel.kind !== "named" || intent.closesAt !== void 0 && (!Number.isFinite(intent.closesAt) || intent.closesAt <= Date.now())) return {
|
|
492
|
+
ok: false,
|
|
493
|
+
error: "The named tunnel intent is no longer valid for automatic restore."
|
|
494
|
+
};
|
|
495
|
+
remote.updateSettings(parsed.settings);
|
|
496
|
+
return remote.enable(intent.closesAt);
|
|
497
|
+
},
|
|
498
|
+
close: async () => {
|
|
499
|
+
closing = true;
|
|
500
|
+
await remote.close();
|
|
501
|
+
}
|
|
468
502
|
};
|
|
469
503
|
}
|
|
470
504
|
//#endregion
|