@jmfederico/pi-web 1.202607.3 → 1.202608.1
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/README.md +4 -4
- package/dist/cli.js +69 -7
- package/dist/cli.js.map +1 -1
- package/dist/client/assets/{CodeViewer-CDVbMiN9.js → CodeViewer-mEiBZFA5.js} +2 -2
- package/dist/client/assets/{TerminalPanel-CTS1CgqF.js → TerminalPanel-O9381Eme.js} +6 -6
- package/dist/client/assets/index-uI2AGFUy.js +4091 -0
- package/dist/client/index.html +1 -1
- package/dist/config.js +109 -73
- package/dist/config.js.map +1 -1
- package/dist/docker/piWebDockerCommandPlan.js +4 -3
- package/dist/docker/piWebDockerCommandPlan.js.map +1 -1
- package/dist/nativeServices/servicePlan.js +1 -1
- package/dist/nativeServices/servicePlan.js.map +1 -1
- package/dist/pi-web-plugins/git/browser/git-contract.js +108 -0
- package/dist/pi-web-plugins/git/browser/git-panel.js +609 -0
- package/dist/pi-web-plugins/git/browser/gitFileList.js +43 -0
- package/dist/pi-web-plugins/git/browser/gitFileShared.js +16 -0
- package/dist/pi-web-plugins/git/browser/gitFileTree.js +81 -0
- package/dist/pi-web-plugins/git/browser/gitFileViewPreference.js +35 -0
- package/dist/pi-web-plugins/git/browser/gitRoute.js +42 -0
- package/dist/pi-web-plugins/git/browser/pi-web-plugin.js +10 -0
- package/dist/pi-web-plugins/git/browser/unifiedDiff.js +301 -0
- package/dist/{server/git/gitService.js → pi-web-plugins/git/git-backend.js} +209 -60
- package/dist/pi-web-plugins/git/package.json +16 -0
- package/dist/pi-web-plugins/git/server-plugin.js +176 -0
- package/dist/pi-web-plugins/info/infoInternals.js +1 -2
- package/dist/pi-web-plugins/info/package.json +1 -1
- package/dist/pi-web-plugins/info/pi-web-plugin.js +2 -2
- package/dist/pi-web-plugins/relays/package.json +1 -1
- package/dist/pi-web-plugins/relays/pi-web-plugin.js +3 -3
- package/dist/pi-web-plugins/relays/relayDiscovery.js +159 -27
- package/dist/pi-web-plugins/relays/relaysPanelElement.js +173 -16
- package/dist/pi-web-plugins/updates/package.json +1 -1
- package/dist/pi-web-plugins/updates/pi-web-plugin.js +1 -1
- package/dist/pi-web-plugins/workspace-tasks/package.json +1 -1
- package/dist/pi-web-plugins/workspace-tasks/pi-web-plugin.js +3 -3
- package/dist/plugin-api.d.ts +33 -16
- package/dist/pluginRecoveryCli.js +172 -0
- package/dist/pluginRecoveryCli.js.map +1 -0
- package/dist/server/activity/workspaceActivityService.js +27 -19
- package/dist/server/activity/workspaceActivityService.js.map +1 -1
- package/dist/server/app.js +35 -37
- package/dist/server/app.js.map +1 -1
- package/dist/server/browserMessageProjection.js +0 -2
- package/dist/server/browserMessageProjection.js.map +1 -1
- package/dist/server/configRoutes.js +4 -31
- package/dist/server/configRoutes.js.map +1 -1
- package/dist/server/machines/machineClient.js +23 -4
- package/dist/server/machines/machineClient.js.map +1 -1
- package/dist/server/machines/machinePluginProxyRoutes.js +74 -17
- package/dist/server/machines/machinePluginProxyRoutes.js.map +1 -1
- package/dist/server/machines/machineProxyRoutes.js +167 -21
- package/dist/server/machines/machineProxyRoutes.js.map +1 -1
- package/dist/server/machines/machineService.js +22 -0
- package/dist/server/machines/machineService.js.map +1 -1
- package/dist/server/piWebPluginCatalog.js +584 -0
- package/dist/server/piWebPluginCatalog.js.map +1 -0
- package/dist/server/piWebPluginLifecycle.js +162 -0
- package/dist/server/piWebPluginLifecycle.js.map +1 -0
- package/dist/server/piWebPluginService.js +159 -261
- package/dist/server/piWebPluginService.js.map +1 -1
- package/dist/server/piWebStatus.js +40 -45
- package/dist/server/piWebStatus.js.map +1 -1
- package/dist/server/plugins/pluginBackendProxyRoutes.js +69 -0
- package/dist/server/plugins/pluginBackendProxyRoutes.js.map +1 -0
- package/dist/server/plugins/serverPluginExec.js +184 -0
- package/dist/server/plugins/serverPluginExec.js.map +1 -0
- package/dist/server/plugins/serverPluginRuntime.js +480 -0
- package/dist/server/plugins/serverPluginRuntime.js.map +1 -0
- package/dist/server/realtime/sessionEventHub.js +26 -11
- package/dist/server/realtime/sessionEventHub.js.map +1 -1
- package/dist/server/requestCancellation.js +32 -0
- package/dist/server/requestCancellation.js.map +1 -0
- package/dist/server/sessiond/agentHttpDispatcher.js +108 -0
- package/dist/server/sessiond/agentHttpDispatcher.js.map +1 -0
- package/dist/server/sessiond/agentProcessEnvironment.js +61 -0
- package/dist/server/sessiond/agentProcessEnvironment.js.map +1 -0
- package/dist/server/sessiond/pluginBackendRoutes.js +64 -0
- package/dist/server/sessiond/pluginBackendRoutes.js.map +1 -0
- package/dist/server/sessiond/sessionDaemonShutdown.js +24 -0
- package/dist/server/sessiond/sessionDaemonShutdown.js.map +1 -0
- package/dist/server/sessiond/sessionProxyRoutes.js +2 -2
- package/dist/server/sessiond/sessionProxyRoutes.js.map +1 -1
- package/dist/server/sessiond/sessionServiceDependencies.js +3 -1
- package/dist/server/sessiond/sessionServiceDependencies.js.map +1 -1
- package/dist/server/sessiond/sessiondStateOwnership.js +235 -0
- package/dist/server/sessiond/sessiondStateOwnership.js.map +1 -0
- package/dist/server/sessiond/workspaceCatalogRoutes.js +31 -0
- package/dist/server/sessiond/workspaceCatalogRoutes.js.map +1 -0
- package/dist/server/sessiond/workspaceRemovalRoutes.js +41 -0
- package/dist/server/sessiond/workspaceRemovalRoutes.js.map +1 -0
- package/dist/server/sessiond.js +283 -96
- package/dist/server/sessiond.js.map +1 -1
- package/dist/server/sessions/askUserTool.js +0 -3
- package/dist/server/sessions/askUserTool.js.map +1 -1
- package/dist/server/sessions/authRoutes.js +0 -10
- package/dist/server/sessions/authRoutes.js.map +1 -1
- package/dist/server/sessions/authService.js +15 -78
- package/dist/server/sessions/authService.js.map +1 -1
- package/dist/server/sessions/dockerEnvironmentFacts.js +254 -0
- package/dist/server/sessions/dockerEnvironmentFacts.js.map +1 -0
- package/dist/server/sessions/messagePaging.js +2 -2
- package/dist/server/sessions/messagePaging.js.map +1 -1
- package/dist/server/sessions/modelCatalogRefresher.js +4 -3
- package/dist/server/sessions/modelCatalogRefresher.js.map +1 -1
- package/dist/server/sessions/oauthLoginFlowService.js +2 -5
- package/dist/server/sessions/oauthLoginFlowService.js.map +1 -1
- package/dist/server/sessions/pendingAskStore.js +0 -3
- package/dist/server/sessions/pendingAskStore.js.map +1 -1
- package/dist/server/sessions/piSessionManagerGateway.js +134 -3
- package/dist/server/sessions/piSessionManagerGateway.js.map +1 -1
- package/dist/server/sessions/piSessionService.js +318 -253
- package/dist/server/sessions/piSessionService.js.map +1 -1
- package/dist/server/sessions/sessionCommandService.js +24 -1
- package/dist/server/sessions/sessionCommandService.js.map +1 -1
- package/dist/server/sessions/sessionEnvironmentFacts.js +48 -0
- package/dist/server/sessions/sessionEnvironmentFacts.js.map +1 -0
- package/dist/server/sessions/sessionFileFormat.js +27 -0
- package/dist/server/sessions/sessionFileFormat.js.map +1 -0
- package/dist/server/sessions/sessionFileHeader.js +82 -23
- package/dist/server/sessions/sessionFileHeader.js.map +1 -1
- package/dist/server/sessions/sessionRoutes.js +88 -56
- package/dist/server/sessions/sessionRoutes.js.map +1 -1
- package/dist/server/sessions/sessionSummaryScanner.js +491 -0
- package/dist/server/sessions/sessionSummaryScanner.js.map +1 -0
- package/dist/server/sessions/spawnSessionTool.js +8 -1
- package/dist/server/sessions/spawnSessionTool.js.map +1 -1
- package/dist/server/sessions/spawnSubsessionTool.js +11 -9
- package/dist/server/sessions/spawnSubsessionTool.js.map +1 -1
- package/dist/server/sessions/spawnTargetResolver.js +1 -1
- package/dist/server/status/machineStatusRoutes.js +8 -0
- package/dist/server/status/machineStatusRoutes.js.map +1 -0
- package/dist/server/status/machineStatusService.js +181 -0
- package/dist/server/status/machineStatusService.js.map +1 -0
- package/dist/server/status/workspaceAttribution.js +87 -0
- package/dist/server/status/workspaceAttribution.js.map +1 -0
- package/dist/server/terminalProxyRoutes.js +2 -1
- package/dist/server/terminalProxyRoutes.js.map +1 -1
- package/dist/server/workspaceExplorerRoutes.js +20 -13
- package/dist/server/workspaceExplorerRoutes.js.map +1 -1
- package/dist/server/workspaces/effectivePathAccess.js +0 -19
- package/dist/server/workspaces/effectivePathAccess.js.map +1 -1
- package/dist/server/workspaces/fileContentService.js +18 -9
- package/dist/server/workspaces/fileContentService.js.map +1 -1
- package/dist/server/workspaces/filePreviewResponseHeaders.js +18 -0
- package/dist/server/workspaces/filePreviewResponseHeaders.js.map +1 -0
- package/dist/server/workspaces/filePreviewResponsePolicy.js +55 -0
- package/dist/server/workspaces/filePreviewResponsePolicy.js.map +1 -0
- package/dist/server/workspaces/filePreviewService.js +81 -0
- package/dist/server/workspaces/filePreviewService.js.map +1 -0
- package/dist/server/workspaces/projectWorkspaceCwds.js +0 -5
- package/dist/server/workspaces/projectWorkspaceCwds.js.map +1 -1
- package/dist/server/workspaces/sessionDaemonWorkspaceCatalog.js +402 -0
- package/dist/server/workspaces/sessionDaemonWorkspaceCatalog.js.map +1 -0
- package/dist/server/workspaces/workspaceCatalog.js +47 -0
- package/dist/server/workspaces/workspaceCatalog.js.map +1 -0
- package/dist/server/workspaces/workspaceContext.js +1 -3
- package/dist/server/workspaces/workspaceContext.js.map +1 -1
- package/dist/server/workspaces/workspaceDeletionRoutes.js +39 -103
- package/dist/server/workspaces/workspaceDeletionRoutes.js.map +1 -1
- package/dist/server/workspaces/workspaceProviderRegistry.js +651 -0
- package/dist/server/workspaces/workspaceProviderRegistry.js.map +1 -0
- package/dist/server/workspaces/workspaceRemovalService.js +256 -0
- package/dist/server/workspaces/workspaceRemovalService.js.map +1 -0
- package/dist/server/workspaces/workspaceRouteErrors.js +7 -0
- package/dist/server/workspaces/workspaceRouteErrors.js.map +1 -0
- package/dist/server/workspaces/worktreePreRemoveHook.js +39 -0
- package/dist/server/workspaces/worktreePreRemoveHook.js.map +1 -0
- package/dist/server-plugin-api.d.ts +140 -0
- package/dist/server-plugin-api.js +2 -0
- package/dist/server-plugin-api.js.map +1 -0
- package/dist/serverPluginRecovery.js +138 -0
- package/dist/serverPluginRecovery.js.map +1 -0
- package/dist/sessiond/activeAgentProfile.js +3 -22
- package/dist/sessiond/activeAgentProfile.js.map +1 -1
- package/dist/sessiond/config.js +13 -2
- package/dist/sessiond/config.js.map +1 -1
- package/dist/sessiond/sessionDaemonClient.js +11 -9
- package/dist/sessiond/sessionDaemonClient.js.map +1 -1
- package/dist/shared/activeAgentProfile.js +2 -41
- package/dist/shared/activeAgentProfile.js.map +1 -1
- package/dist/shared/activity.js +0 -3
- package/dist/shared/activity.js.map +1 -1
- package/dist/shared/apiTypes.js +7 -14
- package/dist/shared/apiTypes.js.map +1 -1
- package/dist/shared/capabilities.js +8 -40
- package/dist/shared/capabilities.js.map +1 -1
- package/dist/shared/federatedRoutes.js +28 -8
- package/dist/shared/federatedRoutes.js.map +1 -1
- package/dist/shared/machinePluginIds.js +4 -2
- package/dist/shared/machinePluginIds.js.map +1 -1
- package/dist/shared/machineStatus.js +94 -0
- package/dist/shared/machineStatus.js.map +1 -0
- package/dist/shared/piWebStatusParsing.js +29 -0
- package/dist/shared/piWebStatusParsing.js.map +1 -1
- package/dist/shared/pluginApiTypes.d.ts +148 -0
- package/dist/shared/pluginApiTypes.js +3 -0
- package/dist/shared/pluginApiTypes.js.map +1 -0
- package/dist/shared/pluginBackendProtocol.js +110 -0
- package/dist/shared/pluginBackendProtocol.js.map +1 -0
- package/dist/shared/pluginIds.js +5 -0
- package/dist/shared/pluginIds.js.map +1 -1
- package/dist/shared/pluginRecoveryCommands.js +14 -0
- package/dist/shared/pluginRecoveryCommands.js.map +1 -0
- package/dist/shared/workspaceFiles.js +41 -2
- package/dist/shared/workspaceFiles.js.map +1 -1
- package/dist/shared/workspaceRemovalProtocol.js +24 -0
- package/dist/shared/workspaceRemovalProtocol.js.map +1 -0
- package/docs/config.md +140 -47
- package/docs/plugins.md +386 -137
- package/examples/workspace-provider-plugin/README.md +52 -0
- package/examples/workspace-provider-plugin/package.json +25 -0
- package/examples/workspace-provider-plugin/src/browser/index.ts +69 -0
- package/examples/workspace-provider-plugin/src/server.ts +64 -0
- package/examples/workspace-provider-plugin/tsconfig.json +21 -0
- package/package.json +32 -12
- package/server-plugin-api.d.ts +1 -0
- package/dist/client/assets/UnifiedDiffViewer-Cqke12ZX.js +0 -39
- package/dist/client/assets/index-LME0LfPb.js +0 -4068
- package/dist/plugin-api/unstable.d.ts +0 -22
- package/dist/server/activity/workspaceActivityRoutes.js +0 -4
- package/dist/server/activity/workspaceActivityRoutes.js.map +0 -1
- package/dist/server/git/gitService.js.map +0 -1
- package/dist/server/gitRoutes.js +0 -23
- package/dist/server/gitRoutes.js.map +0 -1
- package/dist/server/sessions/parentSessionLocator.js +0 -75
- package/dist/server/sessions/parentSessionLocator.js.map +0 -1
- package/dist/server/workspaces/gitWorktreeDiscovery.js +0 -43
- package/dist/server/workspaces/gitWorktreeDiscovery.js.map +0 -1
- package/dist/server/workspaces/imagePreviewService.js +0 -40
- package/dist/server/workspaces/imagePreviewService.js.map +0 -1
- package/dist/server/workspaces/workspaceService.js +0 -52
- package/dist/server/workspaces/workspaceService.js.map +0 -1
- package/dist/shared/apiTypes.d.ts +0 -1234
- package/dist/shared/thinkingLevels.d.ts +0 -27
- package/plugin-api/unstable.d.ts +0 -1
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { EventEmitter } from "node:events";
|
|
2
|
+
import { SettingsManager } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
import { Client, EnvHttpProxyAgent, Pool, setGlobalDispatcher } from "undici";
|
|
4
|
+
/**
|
|
5
|
+
* HTTP idle timeout for the session daemon's embedded Pi runtime.
|
|
6
|
+
*
|
|
7
|
+
* undici's global dispatcher caps `bodyTimeout` and `headersTimeout` at
|
|
8
|
+
* 300_000 ms by default, so any model response that stays idle for 5 minutes
|
|
9
|
+
* (slow local backends such as vLLM producing a single chunk) dies with
|
|
10
|
+
* `TypeError: terminated` — surfaced as "(SYSTEM) Model response failed:
|
|
11
|
+
* terminated" (issue #113).
|
|
12
|
+
*
|
|
13
|
+
* The pi CLI avoids this by calling
|
|
14
|
+
* `configureHttpDispatcher(settingsManager.getHttpIdleTimeoutMs())` at
|
|
15
|
+
* bootstrap; the Pi SDK setting `httpIdleTimeoutMs` (agent profile
|
|
16
|
+
* `settings.json`, default 300_000, `0` disables the idle timers) was simply
|
|
17
|
+
* never applied by PI WEB's embedded session runtime. This module is that
|
|
18
|
+
* missing step for the session daemon.
|
|
19
|
+
*
|
|
20
|
+
* The SDK's own `configureHttpDispatcher` lives in
|
|
21
|
+
* `@earendil-works/pi-coding-agent/dist/core/http-dispatcher.js`, but the
|
|
22
|
+
* package `exports` map blocks deep imports (`ERR_PACKAGE_PATH_NOT_EXPORTED`)
|
|
23
|
+
* and nothing equivalent is re-exported, so the semantics are mirrored here.
|
|
24
|
+
* The one deliberate deviation is skipping the SDK's `undici.install()` step,
|
|
25
|
+
* which replaces `globalThis.fetch` with npm undici's fetch to work around
|
|
26
|
+
* Node 26.0's bundled fetch failing to decompress responses read through an
|
|
27
|
+
* npm dispatcher. A long-lived shared daemon should change the dispatcher,
|
|
28
|
+
* not the fetch implementation every in-process consumer sees; built-in
|
|
29
|
+
* fetch on Node 22/24 honors the global dispatcher without it. If a future
|
|
30
|
+
* Node reintroduces the mismatch (symptom: `response.json()` failing on
|
|
31
|
+
* compressed bodies), revisit. If the SDK ever exports
|
|
32
|
+
* `configureHttpDispatcher`, prefer delegating to it.
|
|
33
|
+
*
|
|
34
|
+
* The value is applied once, at daemon start; changing `httpIdleTimeoutMs`
|
|
35
|
+
* requires restarting the session daemon.
|
|
36
|
+
*/
|
|
37
|
+
const DEFAULT_HTTP_IDLE_TIMEOUT_MS = 300_000;
|
|
38
|
+
/**
|
|
39
|
+
* Read the active agent profile's `httpIdleTimeoutMs` through the SDK
|
|
40
|
+
* `SettingsManager`, reusing its parsing, validation, and defaults
|
|
41
|
+
* (`"disabled"` → 0, absent → 300_000, invalid → throws).
|
|
42
|
+
*/
|
|
43
|
+
export function resolveAgentHttpIdleTimeoutMs({ agentDir, cwd }) {
|
|
44
|
+
return SettingsManager.create(cwd, agentDir).getHttpIdleTimeoutMs();
|
|
45
|
+
}
|
|
46
|
+
// Undici can emit an internal Client "error" while terminating a mid-stream
|
|
47
|
+
// fetch body. The body stream still rejects through the response reader; this
|
|
48
|
+
// listener only prevents EventEmitter's unhandled "error" special case from
|
|
49
|
+
// crashing the daemon. `Dispatcher`'s typed event overloads hide the generic
|
|
50
|
+
// `EventEmitter.on`, so the listener is attached through the prototype.
|
|
51
|
+
const ignoreUndiciDispatcherError = () => { };
|
|
52
|
+
function withUndiciErrorListener(dispatcher) {
|
|
53
|
+
EventEmitter.prototype.on.call(dispatcher, "error", ignoreUndiciDispatcherError);
|
|
54
|
+
return dispatcher;
|
|
55
|
+
}
|
|
56
|
+
function createUndiciClient(origin, options) {
|
|
57
|
+
return withUndiciErrorListener(new Client(origin, options));
|
|
58
|
+
}
|
|
59
|
+
function createUndiciOriginDispatcher(origin, options) {
|
|
60
|
+
if (options.connections === 1) {
|
|
61
|
+
return createUndiciClient(origin, options);
|
|
62
|
+
}
|
|
63
|
+
return withUndiciErrorListener(new Pool(origin, {
|
|
64
|
+
...options,
|
|
65
|
+
factory: createUndiciClient,
|
|
66
|
+
}));
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Install a global undici dispatcher that applies `timeoutMs` to both
|
|
70
|
+
* `bodyTimeout` and `headersTimeout`, mirroring the SDK's
|
|
71
|
+
* `configureHttpDispatcher`. `0` disables the idle timers (undici semantics).
|
|
72
|
+
*/
|
|
73
|
+
export function configureAgentHttpDispatcher(timeoutMs) {
|
|
74
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs < 0) {
|
|
75
|
+
throw new Error(`Invalid HTTP idle timeout: ${String(timeoutMs)}`);
|
|
76
|
+
}
|
|
77
|
+
const normalizedTimeoutMs = Math.floor(timeoutMs);
|
|
78
|
+
const dispatcher = withUndiciErrorListener(new EnvHttpProxyAgent({
|
|
79
|
+
allowH2: false,
|
|
80
|
+
bodyTimeout: normalizedTimeoutMs,
|
|
81
|
+
headersTimeout: normalizedTimeoutMs,
|
|
82
|
+
clientFactory: createUndiciClient,
|
|
83
|
+
factory: createUndiciOriginDispatcher,
|
|
84
|
+
}));
|
|
85
|
+
setGlobalDispatcher(dispatcher);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Resolve the agent profile's `httpIdleTimeoutMs` and install it on the
|
|
89
|
+
* global dispatcher. Never throws: an unreadable or invalid setting falls
|
|
90
|
+
* back to the 300_000 ms default and reports a warning for logging, so a bad
|
|
91
|
+
* settings file cannot keep the daemon from starting.
|
|
92
|
+
*/
|
|
93
|
+
export function applyAgentHttpIdleTimeout({ agentDir, cwd }) {
|
|
94
|
+
let timeoutMs = DEFAULT_HTTP_IDLE_TIMEOUT_MS;
|
|
95
|
+
let warning;
|
|
96
|
+
try {
|
|
97
|
+
timeoutMs = resolveAgentHttpIdleTimeoutMs({ agentDir, cwd });
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
warning = `Falling back to the ${String(DEFAULT_HTTP_IDLE_TIMEOUT_MS)} ms HTTP idle default because the agent profile httpIdleTimeoutMs could not be applied: ${describeError(error)}`;
|
|
101
|
+
}
|
|
102
|
+
configureAgentHttpDispatcher(timeoutMs);
|
|
103
|
+
return warning === undefined ? { timeoutMs } : { timeoutMs, warning };
|
|
104
|
+
}
|
|
105
|
+
function describeError(error) {
|
|
106
|
+
return error instanceof Error ? error.message : String(error);
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=agentHttpDispatcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agentHttpDispatcher.js","sourceRoot":"","sources":["../../../src/server/sessiond/agentHttpDispatcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment hygiene for agent-executed processes.
|
|
3
|
+
*
|
|
4
|
+
* Sessions run inside the session daemon process, so everything agents can
|
|
5
|
+
* spawn — the bash tool, terminals, subsessions — inherits the daemon's
|
|
6
|
+
* environment by default. That inheritance is deliberate: the daemon's
|
|
7
|
+
* `PI_WEB_*` wiring stays visible so agent-facing tooling keeps working, and
|
|
8
|
+
* a second PI WEB instance started from inside a session resolves the same
|
|
9
|
+
* state and fails loudly at startup (see `sessiondStateOwnership`) instead of
|
|
10
|
+
* silently corrupting the live daemon's data. `PI_WEB_SESSION=1` and the
|
|
11
|
+
* session environment facts tell agents they are nested and which precautions
|
|
12
|
+
* apply.
|
|
13
|
+
*
|
|
14
|
+
* The daemon captures its environment once at startup for its own use and
|
|
15
|
+
* then removes the handful of keys that silently distort the tools agents
|
|
16
|
+
* run, so all later spawns inherit a clean environment without per-call-site
|
|
17
|
+
* filtering. Visibility rules:
|
|
18
|
+
*
|
|
19
|
+
* - Ordinary variables (`PATH`, `HOME`, `XDG_CONFIG_HOME`, proxy keys, …)
|
|
20
|
+
* stay.
|
|
21
|
+
* - `PI_WEB_*` keys stay — including `PI_WEB_DOCKER_*` deployment descriptors
|
|
22
|
+
* the agent-facing `pi-web-docker` CLI reads, and the deprecated
|
|
23
|
+
* `PI_WEB_AGENT_*` aliases: the canonical `PI_CODING_AGENT_*` vars are
|
|
24
|
+
* exported with the fully resolved values, so both names resolve
|
|
25
|
+
* identically for anything started from here.
|
|
26
|
+
* - `HOSTEXEC_*` stays: agents use `hostexec`.
|
|
27
|
+
* - `PI_CODING_AGENT_DIR` stays so agent-spawned `pi` CLIs find the profile's
|
|
28
|
+
* auth and models; `PI_CODING_AGENT_SESSION_DIR` stays for the same reason:
|
|
29
|
+
* it is documented `pi` configuration, not daemon wiring, so a deployment
|
|
30
|
+
* that sets it wants every `pi` process — whether daemon-spawned or started
|
|
31
|
+
* by an agent — using the same session storage.
|
|
32
|
+
* - `NODE_ENV` and `PORT` are app-runtime configuration and the only removals:
|
|
33
|
+
* `NODE_ENV=production` makes npm skip devDependencies and flips Node
|
|
34
|
+
* package-export conditions, and `PORT` leaks into the listener
|
|
35
|
+
* configuration of tools agents run.
|
|
36
|
+
*/
|
|
37
|
+
/**
|
|
38
|
+
* Keys that configure the daemon or app runtime in ways that silently break
|
|
39
|
+
* the tools agents run. Daemon configuration belongs in `PI_WEB_`-prefixed
|
|
40
|
+
* keys, which stay visible; a key only belongs here when it cannot carry the
|
|
41
|
+
* prefix (deployment conventions like `NODE_ENV`).
|
|
42
|
+
*/
|
|
43
|
+
const DAEMON_ONLY_ENV_KEYS = new Set(["NODE_ENV", "PORT"]);
|
|
44
|
+
export function isAgentVisibleEnvKey(key) {
|
|
45
|
+
return !DAEMON_ONLY_ENV_KEYS.has(key);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Delete every non-agent-visible key from `env` — the daemon's `process.env`
|
|
49
|
+
* at startup — and return the removed key names, sorted, for logging.
|
|
50
|
+
*/
|
|
51
|
+
export function scrubNonAgentVisibleEnvKeys(env) {
|
|
52
|
+
const scrubbed = Object.keys(env).filter((key) => !isAgentVisibleEnvKey(key));
|
|
53
|
+
for (const key of scrubbed) {
|
|
54
|
+
// process.env is a dynamic key/value bag; deleting the daemon-only keys is
|
|
55
|
+
// the whole point of the scrub, and the keys cannot be known statically.
|
|
56
|
+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
57
|
+
delete env[key];
|
|
58
|
+
}
|
|
59
|
+
return scrubbed.sort();
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=agentProcessEnvironment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agentProcessEnvironment.js","sourceRoot":"","sources":["../../../src/server/sessiond/agentProcessEnvironment.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH;;;;;GAKG;AACH,MAAM,oBAAoB,GAAG,IAAI,GAAG,CAAC,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;AAE3D,MAAM,UAAU,oBAAoB,CAAC,GAAW;IAC9C,OAAO,CAAC,oBAAoB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AACxC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,2BAA2B,CAAC,GAAsB;IAChE,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9E,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,2EAA2E;QAC3E,yEAAyE;QACzE,gEAAgE;QAChE,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;AACzB,CAAC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { isPiWebPluginId } from "../../shared/pluginIds.js";
|
|
2
|
+
import { parsePluginBackendRequestEnvelope, PLUGIN_BACKEND_REQUEST_BODY_MAX_BYTES, PLUGIN_BACKEND_RESPONSE_JSON_MAX_BYTES, requirePluginBackendOperation, serializeBoundedPluginBackendJson, } from "../../shared/pluginBackendProtocol.js";
|
|
3
|
+
import { WorkspaceProviderRequestError, } from "../workspaces/workspaceProviderRegistry.js";
|
|
4
|
+
/** JSON-only sessiond boundary for the active owner of one current workspace. */
|
|
5
|
+
export function registerPluginBackendRoutes(app, dependencies, prefix = "/plugin-backends") {
|
|
6
|
+
app.post(`${prefix}/:pluginId/projects/:projectId/workspaces/:workspaceId/:operation`, { bodyLimit: PLUGIN_BACKEND_REQUEST_BODY_MAX_BYTES }, async (request, reply) => {
|
|
7
|
+
const { pluginId, projectId, workspaceId } = request.params;
|
|
8
|
+
let operation = request.params.operation;
|
|
9
|
+
let envelope;
|
|
10
|
+
try {
|
|
11
|
+
if (!isPiWebPluginId(pluginId))
|
|
12
|
+
throw new Error(`Invalid PI WEB plugin id: ${pluginId}`);
|
|
13
|
+
operation = requirePluginBackendOperation(operation);
|
|
14
|
+
if (projectId === "")
|
|
15
|
+
throw new Error("Project id is required");
|
|
16
|
+
if (workspaceId === "")
|
|
17
|
+
throw new Error("Workspace id is required");
|
|
18
|
+
envelope = parsePluginBackendRequestEnvelope(request.body);
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
return attributedError(reply, 400, boundedErrorMessage(error), "invalid-request", pluginId, operation);
|
|
22
|
+
}
|
|
23
|
+
let project;
|
|
24
|
+
try {
|
|
25
|
+
project = await dependencies.projects.requireProject(projectId);
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
const message = boundedErrorMessage(error);
|
|
29
|
+
return attributedError(reply, message === "Project not found" ? 404 : 500, message, message === "Project not found" ? "project-not-found" : "project-resolution-failed", pluginId, operation);
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
const result = await dependencies.backends.request({
|
|
33
|
+
pluginId,
|
|
34
|
+
moduleRevision: envelope.revision,
|
|
35
|
+
project,
|
|
36
|
+
workspaceId,
|
|
37
|
+
operation,
|
|
38
|
+
input: envelope.input,
|
|
39
|
+
});
|
|
40
|
+
const serialized = serializeBoundedPluginBackendJson(result, `Server plugin ${pluginId} operation ${operation} result`, PLUGIN_BACKEND_RESPONSE_JSON_MAX_BYTES);
|
|
41
|
+
return await reply.type("application/json; charset=utf-8").send(serialized);
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
return await pluginBackendRequestFailed(reply, error, pluginId, operation);
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
dependencies.onWorkspacesMutated();
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
function pluginBackendRequestFailed(reply, error, pluginId, operation) {
|
|
52
|
+
if (error instanceof WorkspaceProviderRequestError) {
|
|
53
|
+
return attributedError(reply, error.statusCode, error.message, error.code, pluginId, operation);
|
|
54
|
+
}
|
|
55
|
+
return attributedError(reply, 502, `Plugin backend request failed: ${boundedErrorMessage(error)}`, "request-failed", pluginId, operation);
|
|
56
|
+
}
|
|
57
|
+
function attributedError(reply, statusCode, message, code, pluginId, operation) {
|
|
58
|
+
return reply.code(statusCode).send({ error: message, code, pluginId, operation });
|
|
59
|
+
}
|
|
60
|
+
function boundedErrorMessage(error) {
|
|
61
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
62
|
+
return message.length <= 2_048 ? message : `${message.slice(0, 2_045)}...`;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=pluginBackendRoutes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pluginBackendRoutes.js","sourceRoot":"","sources":["../../../src/server/sessiond/pluginBackendRoutes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** Quiesces ingress, disposes consumers, then tears down plugin providers and dependencies. */
|
|
2
|
+
export async function runSessionDaemonShutdown(options) {
|
|
3
|
+
const { dependencies } = options;
|
|
4
|
+
const operations = [
|
|
5
|
+
["quiesce server", () => dependencies.quiesceServer()],
|
|
6
|
+
["dispose terminals", () => dependencies.terminals.dispose()],
|
|
7
|
+
["dispose catalog refresher", () => dependencies.catalogRefresher.dispose()],
|
|
8
|
+
["dispose sessions", () => dependencies.sessions.dispose()],
|
|
9
|
+
["close server", () => dependencies.closeServer()],
|
|
10
|
+
["stop server plugins", () => dependencies.serverPlugins.stop()],
|
|
11
|
+
["dispose auth", () => dependencies.auth.dispose()],
|
|
12
|
+
["flush session unread state", () => dependencies.unreadStore.flush()],
|
|
13
|
+
];
|
|
14
|
+
for (const [operation, run] of operations) {
|
|
15
|
+
try {
|
|
16
|
+
await run();
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
options.onFailure?.();
|
|
20
|
+
options.logger.error({ err: error, operation }, "session daemon shutdown operation failed");
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=sessionDaemonShutdown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessionDaemonShutdown.js","sourceRoot":"","sources":["../../../src/server/sessiond/sessionDaemonShutdown.ts"],"names":[],"mappings":"AAqBA,+FAA+F;AAC/F,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,OAAqC;IAClF,MAAM,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC;IACjC,MAAM,UAAU,GAA+D;QAC7E,CAAC,gBAAgB,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,aAAa,EAAE,CAAC;QACtD,CAAC,mBAAmB,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC;QAC7D,CAAC,2BAA2B,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC;QAC5E,CAAC,kBAAkB,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;QAC3D,CAAC,cAAc,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,CAAC;QAClD,CAAC,qBAAqB,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC;QAChE,CAAC,cAAc,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;QACnD,CAAC,4BAA4B,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC;KACvE,CAAC;IAEF,KAAK,MAAM,CAAC,SAAS,EAAE,GAAG,CAAC,IAAI,UAAU,EAAE,CAAC;QAC1C,IAAI,CAAC;YACH,MAAM,GAAG,EAAE,CAAC;QACd,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC;YACtB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,0CAA0C,CAAC,CAAC;QAC9F,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { WebSocket } from "ws";
|
|
2
|
-
import { SessionDaemonClient } from "../../sessiond/sessionDaemonClient.js";
|
|
2
|
+
import { SessionDaemonClient, } from "../../sessiond/sessionDaemonClient.js";
|
|
3
3
|
export function registerSessionProxyRoutes(app, daemon = new SessionDaemonClient(), prefix = "/api") {
|
|
4
4
|
const proxy = async (request, reply) => {
|
|
5
5
|
try {
|
|
@@ -26,7 +26,7 @@ export function registerSessionProxyRoutes(app, daemon = new SessionDaemonClient
|
|
|
26
26
|
app.get(`${prefix}/events`, { websocket: true }, (socket) => {
|
|
27
27
|
bridgeSockets(socket, daemon.connectWebSocket("/events"));
|
|
28
28
|
});
|
|
29
|
-
app.all(`${prefix}/
|
|
29
|
+
app.all(`${prefix}/status`, (request, reply) => proxy(request, reply));
|
|
30
30
|
app.all(`${prefix}/auth`, (request, reply) => proxy(request, reply));
|
|
31
31
|
app.all(`${prefix}/auth/*`, (request, reply) => proxy(request, reply));
|
|
32
32
|
app.all(`${prefix}/sessions`, (request, reply) => proxy(request, reply));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sessionProxyRoutes.js","sourceRoot":"","sources":["../../../src/server/sessiond/sessionProxyRoutes.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAgB,MAAM,IAAI,CAAC;AAC7C,OAAO,
|
|
1
|
+
{"version":3,"file":"sessionProxyRoutes.js","sourceRoot":"","sources":["../../../src/server/sessiond/sessionProxyRoutes.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,17 +12,19 @@ export function sessionServiceDependencies(input) {
|
|
|
12
12
|
return {
|
|
13
13
|
modelRuntime: input.modelRuntime,
|
|
14
14
|
agentDir: input.agentDir,
|
|
15
|
+
archiveStore: input.archiveStore,
|
|
15
16
|
workspaceActivity: input.workspaceActivity,
|
|
16
17
|
logger: input.logger,
|
|
17
18
|
...(input.spawnTargets === undefined ? {} : { spawnTargets: input.spawnTargets }),
|
|
18
|
-
...(input.projectWorkspaces === undefined ? {} : { projectWorkspaces: input.projectWorkspaces }),
|
|
19
19
|
// Tracked subsessions share the spawn capability's project-scope resolver,
|
|
20
20
|
// so they stay off unless spawning is configured too.
|
|
21
21
|
subsessionsEnabled: input.spawnTargets !== undefined && input.subsessionsEnabled,
|
|
22
22
|
askUserEnabled: input.askUserEnabled,
|
|
23
|
+
appendSystemPromptSections: input.appendSystemPromptSections,
|
|
23
24
|
extensionDialogsTimeoutMs: input.extensionDialogsTimeoutMs,
|
|
24
25
|
notificationStore: input.notificationStore,
|
|
25
26
|
unreadStore: input.unreadStore,
|
|
27
|
+
onUnreadChanged: input.onUnreadChanged,
|
|
26
28
|
// Read-only, so session startup can tell a waiting user that provider
|
|
27
29
|
// model lists are refreshing at the same time.
|
|
28
30
|
catalogRefreshStatus: input.catalogRefreshStatus,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sessionServiceDependencies.js","sourceRoot":"","sources":["../../../src/server/sessiond/sessionServiceDependencies.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sessionServiceDependencies.js","sourceRoot":"","sources":["../../../src/server/sessiond/sessionServiceDependencies.ts"],"names":[],"mappings":"AAmCA;;;;;;;;;GASG;AACH,MAAM,UAAU,0BAA0B,CAAC,KAAoC;IAC7E,OAAO;QACL,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,iBAAiB,EAAE,KAAK,CAAC,iBAAiB;QAC1C,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,GAAG,CAAC,KAAK,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,KAAK,CAAC,YAAY,EAAE,CAAC;QACjF,2EAA2E;QAC3E,sDAAsD;QACtD,kBAAkB,EAAE,KAAK,CAAC,YAAY,KAAK,SAAS,IAAI,KAAK,CAAC,kBAAkB;QAChF,cAAc,EAAE,KAAK,CAAC,cAAc;QACpC,0BAA0B,EAAE,KAAK,CAAC,0BAA0B;QAC5D,yBAAyB,EAAE,KAAK,CAAC,yBAAyB;QAC1D,iBAAiB,EAAE,KAAK,CAAC,iBAAiB;QAC1C,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,eAAe,EAAE,KAAK,CAAC,eAAe;QACtC,sEAAsE;QACtE,+CAA+C;QAC/C,oBAAoB,EAAE,KAAK,CAAC,oBAAoB;QAChD,cAAc,EAAE,KAAK,CAAC,cAAc;KACrC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session daemon state ownership.
|
|
3
|
+
*
|
|
4
|
+
* The session daemon is the authority over a pi-web instance's durable state:
|
|
5
|
+
* projects, machines, session archives, and unread state all live in the data
|
|
6
|
+
* directory, and the daemon is the only process allowed to own it while
|
|
7
|
+
* running (the web/API process of the same instance shares the directory but
|
|
8
|
+
* never claims it). A second daemon resolving the same data directory would
|
|
9
|
+
* corrupt that state silently, so the daemon records its ownership in
|
|
10
|
+
* `<data dir>/sessiond-owner.json` at startup and refuses to start while a
|
|
11
|
+
* live owner holds the marker.
|
|
12
|
+
*
|
|
13
|
+
* Liveness uses the owning process's Linux `/proc` start-time ticks when both
|
|
14
|
+
* the marker and the probe have them: the recorded value distinguishes the
|
|
15
|
+
* original owner from a reused pid — including the same numeric pid in a
|
|
16
|
+
* recreated container's fresh PID namespace — so stale markers never block
|
|
17
|
+
* startup and live owners always do. Otherwise it falls back to a
|
|
18
|
+
* `kill(pid, 0)` probe. Detection is scoped to one PID namespace; two daemons
|
|
19
|
+
* in different containers sharing one data volume cannot see each other's
|
|
20
|
+
* processes, an operator error no pid-based mechanism can catch.
|
|
21
|
+
*
|
|
22
|
+
* Service and `tsx watch` restarts briefly overlap the outgoing daemon, so a
|
|
23
|
+
* claimant that finds a live owner waits a short grace for the owner to
|
|
24
|
+
* release (orderly shutdown removes the marker) before failing. When the
|
|
25
|
+
* owner persists past the grace window, startup fails with a
|
|
26
|
+
* {@link SessiondStateOwnershipConflictError} naming the owner and the
|
|
27
|
+
* configuration a second instance needs: a distinct `PI_WEB_DATA_DIR`,
|
|
28
|
+
* `PI_WEB_SESSIOND_SOCKET` (or `PI_WEB_SESSIOND_PORT` / `PI_WEB_SESSIOND_HOST`),
|
|
29
|
+
* and web port.
|
|
30
|
+
*/
|
|
31
|
+
import { readFileSync } from "node:fs";
|
|
32
|
+
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
33
|
+
import { join } from "node:path";
|
|
34
|
+
import { piWebDataDir } from "../../config.js";
|
|
35
|
+
import { sessiondEndpointDescription } from "../../sessiond/config.js";
|
|
36
|
+
/** Marker file recording the owning daemon, inside the instance data directory. */
|
|
37
|
+
export const SESSIOND_OWNER_MARKER_FILENAME = "sessiond-owner.json";
|
|
38
|
+
/**
|
|
39
|
+
* The daemon could not start because a live session daemon owns the state this
|
|
40
|
+
* one resolved. The message is the user- and agent-facing remediation, so it
|
|
41
|
+
* names the owner and the distinct configuration a second instance needs.
|
|
42
|
+
*/
|
|
43
|
+
export class SessiondStateOwnershipConflictError extends Error {
|
|
44
|
+
constructor(options) {
|
|
45
|
+
const { dataDir, markerPath, owner } = options;
|
|
46
|
+
super(`another pi-web session daemon already owns this state\n\n` +
|
|
47
|
+
`The data directory "${dataDir}" is owned by a live session daemon ` +
|
|
48
|
+
`(pid ${String(owner.pid)}, started ${owner.startedAt}, listening on ${owner.endpoint}; ` +
|
|
49
|
+
`ownership marker ${markerPath}). Two daemons sharing one data directory corrupt each other's state.\n\n` +
|
|
50
|
+
`To run a second pi-web instance, give it its own state and endpoints:\n` +
|
|
51
|
+
`- PI_WEB_DATA_DIR: a different data directory\n` +
|
|
52
|
+
`- PI_WEB_SESSIOND_SOCKET (or PI_WEB_SESSIOND_PORT / PI_WEB_SESSIOND_HOST): a different session daemon endpoint\n` +
|
|
53
|
+
`- PI_WEB_PORT: a different web/API port\n\n` +
|
|
54
|
+
`If pid ${String(owner.pid)} is not a running pi-web session daemon, remove the stale marker file and start again.`);
|
|
55
|
+
this.name = "SessiondStateOwnershipConflictError";
|
|
56
|
+
this.dataDir = dataDir;
|
|
57
|
+
this.owner = owner;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const DEFAULT_GRACE_MS = 15_000;
|
|
61
|
+
const DEFAULT_POLL_INTERVAL_MS = 250;
|
|
62
|
+
/**
|
|
63
|
+
* Claim ownership of the instance data directory for this daemon.
|
|
64
|
+
*
|
|
65
|
+
* Resolves once the marker records this daemon; throws
|
|
66
|
+
* {@link SessiondStateOwnershipConflictError} when a live owner persists past
|
|
67
|
+
* the grace window.
|
|
68
|
+
*/
|
|
69
|
+
export async function claimSessiondStateOwnership(options) {
|
|
70
|
+
const ownPid = options.ownPid ?? process.pid;
|
|
71
|
+
const isAlive = options.isAlive ?? isOwnershipRecordAlive;
|
|
72
|
+
const graceMs = options.graceMs ?? DEFAULT_GRACE_MS;
|
|
73
|
+
const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
74
|
+
const sleep = options.sleep ?? defaultSleep;
|
|
75
|
+
const logger = options.logger;
|
|
76
|
+
const dataDir = options.dataDir ?? piWebDataDir(options.env);
|
|
77
|
+
const markerPath = join(dataDir, SESSIOND_OWNER_MARKER_FILENAME);
|
|
78
|
+
const processStartTime = readProcessStartTime(ownPid);
|
|
79
|
+
const record = {
|
|
80
|
+
pid: ownPid,
|
|
81
|
+
startedAt: new Date().toISOString(),
|
|
82
|
+
...(processStartTime === undefined ? {} : { processStartTime }),
|
|
83
|
+
endpoint: sessiondEndpointDescription(options.env),
|
|
84
|
+
};
|
|
85
|
+
await mkdir(dataDir, { recursive: true });
|
|
86
|
+
const deadline = Date.now() + graceMs;
|
|
87
|
+
let waitedForOwner = false;
|
|
88
|
+
for (;;) {
|
|
89
|
+
const existing = await readOwnershipRecord(markerPath);
|
|
90
|
+
if (existing !== undefined && existing !== "invalid") {
|
|
91
|
+
if (existing.pid !== ownPid && isAlive(existing)) {
|
|
92
|
+
// A live different owner: wait out the grace window for an orderly
|
|
93
|
+
// restart to release, then fail loudly.
|
|
94
|
+
if (Date.now() >= deadline) {
|
|
95
|
+
throw new SessiondStateOwnershipConflictError({ dataDir, markerPath, owner: existing });
|
|
96
|
+
}
|
|
97
|
+
if (!waitedForOwner) {
|
|
98
|
+
logger?.warn({ markerPath, owner: existing }, "another live session daemon owns this state; waiting briefly for it to release ownership");
|
|
99
|
+
waitedForOwner = true;
|
|
100
|
+
}
|
|
101
|
+
await sleep(pollIntervalMs);
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (existing.pid !== ownPid) {
|
|
105
|
+
logger?.info({ markerPath, previousOwner: existing }, "taking over session daemon state ownership from a stale marker");
|
|
106
|
+
}
|
|
107
|
+
await rm(markerPath, { force: true });
|
|
108
|
+
}
|
|
109
|
+
else if (existing === "invalid") {
|
|
110
|
+
// Possibly a torn write by a claimant mid-create; give it the grace
|
|
111
|
+
// window to finish before discarding the marker.
|
|
112
|
+
if (Date.now() < deadline) {
|
|
113
|
+
await sleep(pollIntervalMs);
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
logger?.warn({ markerPath }, "discarding an unreadable session daemon ownership marker");
|
|
117
|
+
await rm(markerPath, { force: true });
|
|
118
|
+
}
|
|
119
|
+
// No live owner holds the marker: claim it exclusively. The loser of a
|
|
120
|
+
// create race re-reads and re-evaluates the winner's marker above.
|
|
121
|
+
try {
|
|
122
|
+
await writeFile(markerPath, `${JSON.stringify(record, null, 2)}\n`, { flag: "wx" });
|
|
123
|
+
return {
|
|
124
|
+
markerPath,
|
|
125
|
+
record,
|
|
126
|
+
release: () => releaseOwnershipMarker(markerPath, record, logger),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
if (!isErrorCode(error, "EEXIST"))
|
|
131
|
+
throw error;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/** Parse marker content; `"invalid"` when it is not a readable ownership record. */
|
|
136
|
+
export function parseOwnershipRecord(content) {
|
|
137
|
+
try {
|
|
138
|
+
const value = JSON.parse(content);
|
|
139
|
+
if (!isRecord(value))
|
|
140
|
+
return "invalid";
|
|
141
|
+
if (typeof value["pid"] !== "number" || typeof value["startedAt"] !== "string" || typeof value["endpoint"] !== "string") {
|
|
142
|
+
return "invalid";
|
|
143
|
+
}
|
|
144
|
+
if (value["processStartTime"] !== undefined && typeof value["processStartTime"] !== "string")
|
|
145
|
+
return "invalid";
|
|
146
|
+
return {
|
|
147
|
+
pid: value["pid"],
|
|
148
|
+
startedAt: value["startedAt"],
|
|
149
|
+
...(value["processStartTime"] === undefined ? {} : { processStartTime: value["processStartTime"] }),
|
|
150
|
+
endpoint: value["endpoint"],
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
return "invalid";
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Whether the process a marker records is still that same running process.
|
|
159
|
+
*
|
|
160
|
+
* With both the recorded and the current `/proc` start time available, a
|
|
161
|
+
* mismatch pins down pid reuse; otherwise the pid probe is all there is.
|
|
162
|
+
*/
|
|
163
|
+
export function isOwnershipRecordAlive(record) {
|
|
164
|
+
const currentStartTime = readProcessStartTime(record.pid);
|
|
165
|
+
if (currentStartTime !== undefined && record.processStartTime !== undefined) {
|
|
166
|
+
return currentStartTime === record.processStartTime;
|
|
167
|
+
}
|
|
168
|
+
return isProcessAlive(record.pid);
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Linux-only precise process identity: `/proc/<pid>/stat` field 22 (start time
|
|
172
|
+
* in clock ticks since boot). `undefined` off Linux or when the process is
|
|
173
|
+
* gone. The `comm` field may contain spaces and parentheses, so parsing starts
|
|
174
|
+
* after its closing parenthesis.
|
|
175
|
+
*/
|
|
176
|
+
export function readProcessStartTime(pid) {
|
|
177
|
+
try {
|
|
178
|
+
const stat = readFileSync(`/proc/${String(pid)}/stat`, "utf8");
|
|
179
|
+
const closeParen = stat.lastIndexOf(")");
|
|
180
|
+
if (closeParen === -1)
|
|
181
|
+
return undefined;
|
|
182
|
+
const afterComm = stat.slice(closeParen + 2).split(" ");
|
|
183
|
+
return afterComm[19];
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
return undefined;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
async function readOwnershipRecord(markerPath) {
|
|
190
|
+
let content;
|
|
191
|
+
try {
|
|
192
|
+
content = await readFile(markerPath, "utf8");
|
|
193
|
+
}
|
|
194
|
+
catch (error) {
|
|
195
|
+
if (isErrorCode(error, "ENOENT"))
|
|
196
|
+
return undefined;
|
|
197
|
+
throw error;
|
|
198
|
+
}
|
|
199
|
+
return parseOwnershipRecord(content);
|
|
200
|
+
}
|
|
201
|
+
async function releaseOwnershipMarker(markerPath, record, logger) {
|
|
202
|
+
try {
|
|
203
|
+
const current = await readOwnershipRecord(markerPath);
|
|
204
|
+
if (current === undefined || current === "invalid")
|
|
205
|
+
return;
|
|
206
|
+
// Remove only our own marker: pid plus the wall-clock start makes a newer
|
|
207
|
+
// owner that reused the pid after a takeover race safe from deletion.
|
|
208
|
+
if (current.pid === record.pid && current.startedAt === record.startedAt) {
|
|
209
|
+
await rm(markerPath, { force: true });
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
logger?.warn({ err: error, markerPath }, "could not release the session daemon ownership marker; the next start discards it as stale");
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
function isProcessAlive(pid) {
|
|
217
|
+
try {
|
|
218
|
+
process.kill(pid, 0);
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
catch (error) {
|
|
222
|
+
// EPERM means the process exists but belongs to someone else.
|
|
223
|
+
return isErrorCode(error, "EPERM");
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
function defaultSleep(ms) {
|
|
227
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
228
|
+
}
|
|
229
|
+
function isRecord(value) {
|
|
230
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
231
|
+
}
|
|
232
|
+
function isErrorCode(error, code) {
|
|
233
|
+
return isRecord(error) && error["code"] === code;
|
|
234
|
+
}
|
|
235
|
+
//# sourceMappingURL=sessiondStateOwnership.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessiondStateOwnership.js","sourceRoot":"","sources":["../../../src/server/sessiond/sessiondStateOwnership.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** Internal sessiond protocol; browser-facing routes consume it through a typed client. */
|
|
2
|
+
export function registerWorkspaceCatalogRoutes(app, dependencies, prefix = "/workspace-catalog") {
|
|
3
|
+
app.get(`${prefix}/provider-runtime`, () => dependencies.providerRuntime);
|
|
4
|
+
app.get(`${prefix}/projects/:projectId/workspaces`, async (request, reply) => {
|
|
5
|
+
try {
|
|
6
|
+
const project = await dependencies.projects.requireProject(request.params.projectId);
|
|
7
|
+
return await dependencies.workspaces.resolve(project);
|
|
8
|
+
}
|
|
9
|
+
catch (error) {
|
|
10
|
+
return catalogRequestFailed(reply, error);
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
app.get(`${prefix}/projects/:projectId/workspaces/:workspaceId`, async (request, reply) => {
|
|
14
|
+
try {
|
|
15
|
+
const project = await dependencies.projects.requireProject(request.params.projectId);
|
|
16
|
+
const resolution = await dependencies.workspaces.resolve(project);
|
|
17
|
+
const workspace = resolution.workspaces.find((candidate) => candidate.id === request.params.workspaceId);
|
|
18
|
+
if (workspace === undefined)
|
|
19
|
+
return await reply.code(404).send({ error: "Workspace not found" });
|
|
20
|
+
return workspace;
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
return catalogRequestFailed(reply, error);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
function catalogRequestFailed(reply, error) {
|
|
28
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
29
|
+
return reply.code(message === "Project not found" ? 404 : 500).send({ error: message });
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=workspaceCatalogRoutes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspaceCatalogRoutes.js","sourceRoot":"","sources":["../../../src/server/sessiond/workspaceCatalogRoutes.ts"],"names":[],"mappings":"AAmBA,2FAA2F;AAC3F,MAAM,UAAU,8BAA8B,CAC5C,GAAoB,EACpB,YAA+C,EAC/C,MAAM,GAAG,oBAAoB;IAE7B,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,mBAAmB,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,eAAe,CAAC,CAAC;IAE1E,GAAG,CAAC,GAAG,CAAoC,GAAG,MAAM,iCAAiC,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QAC9G,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,QAAQ,CAAC,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YACrF,OAAO,MAAM,YAAY,CAAC,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACxD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,oBAAoB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,GAAG,CAAyD,GAAG,MAAM,8CAA8C,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QAChJ,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,QAAQ,CAAC,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YACrF,MAAM,UAAU,GAAG,MAAM,YAAY,CAAC,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YAClE,MAAM,SAAS,GAAG,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,EAAE,KAAK,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;YACzG,IAAI,SAAS,KAAK,SAAS;gBAAE,OAAO,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,qBAAqB,EAAE,CAAC,CAAC;YACjG,OAAO,SAAS,CAAC;QACnB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,oBAAoB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,oBAAoB,CAAC,KAAmB,EAAE,KAAc;IAC/D,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACvE,OAAO,KAAK,CAAC,IAAI,CAAC,OAAO,KAAK,mBAAmB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;AAC1F,CAAC"}
|