@jmfederico/pi-web 1.202609.0 → 1.202610.0
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 +3 -3
- package/dist/.plugins-ready +1 -0
- package/dist/cli.js +5 -2
- package/dist/cli.js.map +1 -1
- package/dist/client/assets/index-_1GwJfSs.js +4176 -0
- package/dist/client/assets/vendor-editor-core-YWog6mAR.js +10 -0
- package/dist/client/assets/vendor-editor-languages-DOCw3vc9.js +28 -0
- package/dist/client/index.html +4 -4
- package/dist/docker/piWebDockerCommandPlan.js +9 -1
- package/dist/docker/piWebDockerCommandPlan.js.map +1 -1
- package/dist/nativeServices/installedServiceDefinitions.js +24 -17
- package/dist/nativeServices/installedServiceDefinitions.js.map +1 -1
- package/dist/nativeServices/serviceAction.js +38 -26
- package/dist/nativeServices/serviceAction.js.map +1 -1
- package/dist/nativeServices/servicePlan.js +3 -3
- package/dist/nativeServices/servicePlan.js.map +1 -1
- package/dist/pi-packages/captains-log/README.md +11 -0
- package/dist/pi-packages/captains-log/dist/browser/channelProtocol.js +29 -0
- package/dist/pi-packages/captains-log/dist/browser/index.js +145 -0
- package/dist/pi-packages/captains-log/dist/browser/markdown.js +67 -0
- package/dist/pi-packages/captains-log/dist/browser/panel.js +107 -0
- package/dist/pi-packages/captains-log/dist/browser/protocol.js +26 -0
- package/dist/pi-packages/captains-log/dist/companion.js +126 -0
- package/dist/pi-packages/captains-log/dist/roundtrip.js +62 -0
- package/dist/pi-packages/captains-log/dist/server.js +210 -0
- package/dist/pi-packages/captains-log/dist/store.js +75 -0
- package/dist/pi-packages/captains-log/docs/usage.md +32 -0
- package/dist/pi-packages/captains-log/package.json +35 -0
- package/dist/pi-packages/captains-log/tsconfig.json +21 -0
- package/dist/pi-packages/captains-log/vite.config.mjs +28 -0
- package/dist/pi-packages/relays/pi-web-plugin.js +1 -1
- package/dist/pi-packages/relays/prompts/relay.md +1 -1
- package/dist/pi-packages/relays/skills/relay-runner/SKILL.md +21 -13
- package/dist/pi-web-plugins/files/browser/assets/files-icon-DZObYhxb.svg +3 -0
- package/dist/pi-web-plugins/files/browser/pi-web-plugin.js +439 -0
- package/dist/pi-web-plugins/files/package.json +15 -0
- package/dist/pi-web-plugins/git/browser/git-panel.js +54 -24
- package/dist/pi-web-plugins/git/browser/pi-web-plugin.js +1 -1
- package/dist/pi-web-plugins/git/git-backend.js +5 -5
- package/dist/pi-web-plugins/git/server-plugin.js +5 -3
- package/dist/pi-web-plugins/info/pi-web-plugin.js +1 -1
- package/dist/pi-web-plugins/mermaid/browser/mermaid-engine.js +3590 -0
- package/dist/pi-web-plugins/mermaid/browser/pi-web-plugin.js +6 -0
- package/dist/pi-web-plugins/mermaid/package.json +13 -0
- package/dist/pi-web-plugins/terminal/browser/pi-web-plugin.js +210 -0
- package/dist/pi-web-plugins/terminal/package.json +16 -0
- package/dist/pi-web-plugins/terminal/server-plugin.js +365 -0
- package/dist/{server/terminals → pi-web-plugins/terminal}/terminalService.js +159 -79
- package/dist/pi-web-plugins/updates/pi-web-plugin.js +1 -1
- package/dist/pi-web-plugins/workspace-tasks/pi-web-plugin.js +1 -1
- package/dist/piWebVersionReport.js +12 -4
- package/dist/piWebVersionReport.js.map +1 -1
- package/dist/plugin-api.d.ts +235 -23
- package/dist/plugin-api.js +1 -0
- package/dist/pluginRecoveryCli.js +3 -2
- package/dist/pluginRecoveryCli.js.map +1 -1
- package/dist/server/activity/workspaceActivityService.js.map +1 -1
- package/dist/server/app.js +32 -28
- package/dist/server/app.js.map +1 -1
- package/dist/server/browserMessageProjection.js +39 -20
- package/dist/server/browserMessageProjection.js.map +1 -1
- package/dist/server/knownPiPackages.js +12 -0
- package/dist/server/knownPiPackages.js.map +1 -0
- package/dist/server/machines/machineClient.js +2 -2
- package/dist/server/machines/machineClient.js.map +1 -1
- package/dist/server/machines/machinePluginProxyRoutes.js +53 -11
- package/dist/server/machines/machinePluginProxyRoutes.js.map +1 -1
- package/dist/server/machines/machineProxyRoutes.js +101 -7
- package/dist/server/machines/machineProxyRoutes.js.map +1 -1
- package/dist/server/notices/serverNoticeStore.js +45 -4
- package/dist/server/notices/serverNoticeStore.js.map +1 -1
- package/dist/server/piPackageService.js +5 -5
- package/dist/server/piPackageService.js.map +1 -1
- package/dist/server/piWebPluginCatalog.js +46 -12
- package/dist/server/piWebPluginCatalog.js.map +1 -1
- package/dist/server/piWebPluginLifecycle.js +26 -3
- package/dist/server/piWebPluginLifecycle.js.map +1 -1
- package/dist/server/piWebPluginService.js +37 -2
- package/dist/server/piWebPluginService.js.map +1 -1
- package/dist/server/pluginCallbackDrain.js +35 -0
- package/dist/server/pluginCallbackDrain.js.map +1 -0
- package/dist/server/plugins/pluginBackendChannelProxyAdmission.js +84 -0
- package/dist/server/plugins/pluginBackendChannelProxyAdmission.js.map +1 -0
- package/dist/server/plugins/pluginBackendChannelProxyCoordinator.js +234 -0
- package/dist/server/plugins/pluginBackendChannelProxyCoordinator.js.map +1 -0
- package/dist/server/plugins/pluginBackendChannelProxyRoutes.js +52 -0
- package/dist/server/plugins/pluginBackendChannelProxyRoutes.js.map +1 -0
- package/dist/server/plugins/pluginBackendProxyRoutes.js +38 -31
- package/dist/server/plugins/pluginBackendProxyRoutes.js.map +1 -1
- package/dist/server/plugins/pluginBackendRegistry.js +740 -0
- package/dist/server/plugins/pluginBackendRegistry.js.map +1 -0
- package/dist/server/plugins/serverPluginPiSessionEventsCapability.js +34 -0
- package/dist/server/plugins/serverPluginPiSessionEventsCapability.js.map +1 -0
- package/dist/server/plugins/serverPluginPiSessionsCapability.js +185 -0
- package/dist/server/plugins/serverPluginPiSessionsCapability.js.map +1 -0
- package/dist/server/plugins/serverPluginRuntime.js +1028 -107
- package/dist/server/plugins/serverPluginRuntime.js.map +1 -1
- package/dist/server/plugins/serverPluginWorkspacesCapability.js +114 -0
- package/dist/server/plugins/serverPluginWorkspacesCapability.js.map +1 -0
- package/dist/server/projects/projectLifecycleService.js +91 -0
- package/dist/server/projects/projectLifecycleService.js.map +1 -0
- package/dist/server/realtime/sessionEventHub.js +22 -9
- package/dist/server/realtime/sessionEventHub.js.map +1 -1
- package/dist/server/sessiond/pluginBackendChannelRoutes.js +393 -0
- package/dist/server/sessiond/pluginBackendChannelRoutes.js.map +1 -0
- package/dist/server/sessiond/pluginBackendRoutes.js +10 -7
- package/dist/server/sessiond/pluginBackendRoutes.js.map +1 -1
- package/dist/server/sessiond/projectMutationRoutes.js +38 -0
- package/dist/server/sessiond/projectMutationRoutes.js.map +1 -0
- package/dist/server/sessiond/sessionDaemonShutdown.js +9 -2
- package/dist/server/sessiond/sessionDaemonShutdown.js.map +1 -1
- package/dist/server/sessiond/sessionProxyRoutes.js +24 -0
- package/dist/server/sessiond/sessionProxyRoutes.js.map +1 -1
- package/dist/server/sessiond.js +99 -24
- package/dist/server/sessiond.js.map +1 -1
- package/dist/server/sessions/builtinCommands.js +2 -2
- package/dist/server/sessions/builtinCommands.js.map +1 -1
- package/dist/server/sessions/clientSessionPreview.js +14 -0
- package/dist/server/sessions/clientSessionPreview.js.map +1 -0
- package/dist/server/sessions/piSessionEventConnections.js +65 -0
- package/dist/server/sessions/piSessionEventConnections.js.map +1 -0
- package/dist/server/sessions/piSessionManagerGateway.js +8 -2
- package/dist/server/sessions/piSessionManagerGateway.js.map +1 -1
- package/dist/server/sessions/piSessionService.js +411 -102
- package/dist/server/sessions/piSessionService.js.map +1 -1
- package/dist/server/sessions/sessionActivityMarker.js +105 -0
- package/dist/server/sessions/sessionActivityMarker.js.map +1 -0
- package/dist/server/sessions/sessionEnvironmentFacts.js +5 -4
- package/dist/server/sessions/sessionEnvironmentFacts.js.map +1 -1
- package/dist/server/sessions/sessionMediaIndex.js +184 -0
- package/dist/server/sessions/sessionMediaIndex.js.map +1 -0
- package/dist/server/sessions/sessionNameGenerator.js +3 -2
- package/dist/server/sessions/sessionNameGenerator.js.map +1 -1
- package/dist/server/sessions/sessionRoutes.js +64 -4
- package/dist/server/sessions/sessionRoutes.js.map +1 -1
- package/dist/server/sessions/sessionUnreadStore.js +26 -1
- package/dist/server/sessions/sessionUnreadStore.js.map +1 -1
- package/dist/server/sessions/spawnSessionTool.js +7 -2
- package/dist/server/sessions/spawnSessionTool.js.map +1 -1
- package/dist/server/sessions/spawnSubsessionTool.js +7 -2
- package/dist/server/sessions/spawnSubsessionTool.js.map +1 -1
- package/dist/server/sessions/transcriptMessages.js +54 -0
- package/dist/server/sessions/transcriptMessages.js.map +1 -0
- package/dist/server/terminals/requiredTerminalService.js +103 -0
- package/dist/server/terminals/requiredTerminalService.js.map +1 -0
- package/dist/server/webSocketBridge.js +339 -0
- package/dist/server/webSocketBridge.js.map +1 -1
- package/dist/server/workspaceExplorerRoutes.js +1 -1
- package/dist/server/workspaceExplorerRoutes.js.map +1 -1
- package/dist/server/workspaces/filePreviewService.js +27 -3
- package/dist/server/workspaces/filePreviewService.js.map +1 -1
- package/dist/server/workspaces/sessionDaemonWorkspaceCatalog.js +23 -3
- package/dist/server/workspaces/sessionDaemonWorkspaceCatalog.js.map +1 -1
- package/dist/server/workspaces/workspaceCatalog.js +3 -1
- package/dist/server/workspaces/workspaceCatalog.js.map +1 -1
- package/dist/server/workspaces/workspaceProviderRegistry.js +64 -149
- package/dist/server/workspaces/workspaceProviderRegistry.js.map +1 -1
- package/dist/server/workspaces/workspaceRemovalService.js +23 -1
- package/dist/server/workspaces/workspaceRemovalService.js.map +1 -1
- package/dist/server-plugin-api.d.ts +216 -22
- package/dist/server-plugin-api.js +319 -2
- package/dist/server-plugin-api.js.map +1 -1
- package/dist/serverPluginRecovery.js +4 -0
- package/dist/serverPluginRecovery.js.map +1 -1
- package/dist/sessiond/sessionDaemonClient.js +27 -3
- package/dist/sessiond/sessionDaemonClient.js.map +1 -1
- package/dist/shared/apiTypes.js +1 -1
- package/dist/shared/apiTypes.js.map +1 -1
- package/dist/shared/federatedRoutes.js +47 -17
- package/dist/shared/federatedRoutes.js.map +1 -1
- package/dist/shared/machinePluginIds.js +28 -7
- package/dist/shared/machinePluginIds.js.map +1 -1
- package/dist/shared/pluginApiTypes.d.ts +21 -1
- package/dist/shared/pluginApiTypes.js +0 -1
- package/dist/shared/pluginBackendProtocol.js +163 -2
- package/dist/shared/pluginBackendProtocol.js.map +1 -1
- package/dist/shared/pluginIds.js +8 -1
- package/dist/shared/pluginIds.js.map +1 -1
- package/dist/shared/requiredTerminalPlugin.js +6 -0
- package/dist/shared/requiredTerminalPlugin.js.map +1 -0
- package/dist/shared/serverNoticeContract.js +88 -0
- package/dist/shared/serverNoticeContract.js.map +1 -0
- package/dist/shared/sessionDefaults.js +41 -0
- package/dist/shared/sessionDefaults.js.map +1 -0
- package/dist/shared/sessionMedia.js +6 -0
- package/dist/shared/sessionMedia.js.map +1 -0
- package/docs/config.md +50 -36
- package/docs/plugins.md +157 -1212
- package/examples/session-bridge-plugin/README.md +20 -0
- package/examples/session-bridge-plugin/docs/usage.md +54 -0
- package/examples/session-bridge-plugin/package.json +26 -0
- package/examples/session-bridge-plugin/src/browser/index.ts +84 -0
- package/examples/session-bridge-plugin/src/browser/protocol.ts +24 -0
- package/examples/session-bridge-plugin/src/companion.ts +65 -0
- package/examples/session-bridge-plugin/src/reviewRun.ts +39 -0
- package/examples/session-bridge-plugin/src/server.ts +85 -0
- package/examples/session-bridge-plugin/src/store.ts +57 -0
- package/examples/session-bridge-plugin/tsconfig.json +21 -0
- package/examples/workspace-provider-plugin/README.md +4 -4
- package/examples/workspace-provider-plugin/package.json +1 -1
- package/examples/workspace-provider-plugin/src/browser/index.ts +9 -9
- package/examples/workspace-provider-plugin/src/server.ts +15 -11
- package/package.json +19 -12
- package/dist/client/assets/CodeViewer-CGAlg9S8.js +0 -4
- package/dist/client/assets/TerminalPanel-xhJRhOas.js +0 -187
- package/dist/client/assets/index-DXQKhn1P.js +0 -4326
- package/dist/client/assets/vendor-editor-core-CXO8gGab.js +0 -12
- package/dist/client/assets/vendor-editor-languages-CpW4sJsX.js +0 -46
- package/dist/client/assets/vendor-editor-legacy-CYBnW6ZU.js +0 -1
- package/dist/client/assets/vendor-terminal-BrP-ENHg.css +0 -1
- package/dist/client/assets/vendor-terminal-D8k4UKM2.js +0 -35
- package/dist/server/terminalProxyRoutes.js +0 -141
- package/dist/server/terminalProxyRoutes.js.map +0 -1
- package/dist/server/terminals/terminalRoutes.js +0 -155
- package/dist/server/terminals/terminalRoutes.js.map +0 -1
- package/dist/server/terminals/terminalService.js.map +0 -1
- package/dist/server/terminals/terminalSize.js +0 -17
- package/dist/server/terminals/terminalSize.js.map +0 -1
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { readFile, stat, unlink, writeFile } from "node:fs/promises";
|
|
3
|
+
const markerPath = (sessionFile) => `${sessionFile}.pi-web-activity.json`;
|
|
4
|
+
const isMissing = (error) => typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
5
|
+
/** Best-effort advisory presence, not a lock or an atomic ownership claim. */
|
|
6
|
+
export class SessionActivityMarker {
|
|
7
|
+
constructor(options = {}) {
|
|
8
|
+
this.entries = new Map();
|
|
9
|
+
this.owner = options.owner ?? randomUUID();
|
|
10
|
+
this.now = options.now ?? Date.now;
|
|
11
|
+
this.onError = options.onError;
|
|
12
|
+
}
|
|
13
|
+
async refresh(sessionFile, busy) {
|
|
14
|
+
if (sessionFile === undefined || sessionFile === "" || this.disposal)
|
|
15
|
+
return;
|
|
16
|
+
let entry = this.entries.get(sessionFile);
|
|
17
|
+
if (!entry) {
|
|
18
|
+
entry = { pending: Promise.resolve(), activeElsewhere: false };
|
|
19
|
+
this.entries.set(sessionFile, entry);
|
|
20
|
+
}
|
|
21
|
+
const state = entry;
|
|
22
|
+
await this.enqueue(state, async () => {
|
|
23
|
+
const now = this.now();
|
|
24
|
+
if (state.busy === busy && state.refreshedAt !== undefined && now - state.refreshedAt < 2_000)
|
|
25
|
+
return;
|
|
26
|
+
state.busy = busy;
|
|
27
|
+
state.refreshedAt = now;
|
|
28
|
+
// Keep the last known activity visible until the filesystem read completes.
|
|
29
|
+
// Never create directories or sidecars for transcripts that do not exist.
|
|
30
|
+
await stat(sessionFile);
|
|
31
|
+
const marker = await this.readMarker(sessionFile);
|
|
32
|
+
state.activeElsewhere = marker !== undefined && marker.owner !== this.owner && now - marker.updatedAt <= 15_000;
|
|
33
|
+
if (state.activeElsewhere)
|
|
34
|
+
return;
|
|
35
|
+
if (busy) {
|
|
36
|
+
await writeFile(markerPath(sessionFile), JSON.stringify({ owner: this.owner, updatedAt: now }));
|
|
37
|
+
}
|
|
38
|
+
else if (marker?.owner === this.owner) {
|
|
39
|
+
await unlink(markerPath(sessionFile));
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
isActiveElsewhere(sessionFile) {
|
|
44
|
+
return sessionFile === undefined ? false : this.entries.get(sessionFile)?.activeElsewhere ?? false;
|
|
45
|
+
}
|
|
46
|
+
async release(sessionFile) {
|
|
47
|
+
if (sessionFile === undefined || sessionFile === "")
|
|
48
|
+
return;
|
|
49
|
+
const entry = this.entries.get(sessionFile);
|
|
50
|
+
if (!entry)
|
|
51
|
+
return;
|
|
52
|
+
const pending = this.enqueue(entry, async () => {
|
|
53
|
+
entry.activeElsewhere = false;
|
|
54
|
+
delete entry.refreshedAt;
|
|
55
|
+
delete entry.busy;
|
|
56
|
+
if ((await this.readMarker(sessionFile))?.owner === this.owner) {
|
|
57
|
+
await unlink(markerPath(sessionFile));
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
await pending;
|
|
61
|
+
if (entry.pending === pending)
|
|
62
|
+
this.entries.delete(sessionFile);
|
|
63
|
+
}
|
|
64
|
+
async dispose() {
|
|
65
|
+
this.disposal ??= Promise.all([...this.entries.keys()].map((file) => this.release(file))).then(() => undefined);
|
|
66
|
+
await this.disposal;
|
|
67
|
+
}
|
|
68
|
+
enqueue(entry, operation) {
|
|
69
|
+
entry.pending = entry.pending.then(operation).catch((error) => {
|
|
70
|
+
entry.activeElsewhere = false;
|
|
71
|
+
if (!isMissing(error)) {
|
|
72
|
+
// An error reporter must not turn advisory IO into a session failure.
|
|
73
|
+
try {
|
|
74
|
+
this.onError?.(error);
|
|
75
|
+
}
|
|
76
|
+
catch { /* Best effort only. */ }
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
return entry.pending;
|
|
80
|
+
}
|
|
81
|
+
async readMarker(sessionFile) {
|
|
82
|
+
let text;
|
|
83
|
+
try {
|
|
84
|
+
text = await readFile(markerPath(sessionFile), "utf8");
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
if (isMissing(error))
|
|
88
|
+
return undefined;
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
let value;
|
|
92
|
+
try {
|
|
93
|
+
value = JSON.parse(text);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
if (typeof value !== "object" || value === null || !("owner" in value) || !("updatedAt" in value))
|
|
99
|
+
return undefined;
|
|
100
|
+
if (typeof value.owner !== "string" || typeof value.updatedAt !== "number" || !Number.isFinite(value.updatedAt))
|
|
101
|
+
return undefined;
|
|
102
|
+
return { owner: value.owner, updatedAt: value.updatedAt };
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=sessionActivityMarker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessionActivityMarker.js","sourceRoot":"","sources":["../../../src/server/sessions/sessionActivityMarker.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
*
|
|
12
12
|
* The block tells the agent the nesting exists and which precautions follow,
|
|
13
13
|
* so it learns the rules before discovering them by breaking its own session:
|
|
14
|
-
* use a distinct data dir, socket, and ports for another instance;
|
|
15
|
-
*
|
|
16
|
-
* process before the session daemon
|
|
14
|
+
* use a distinct data dir, socket, and ports for another instance; require an
|
|
15
|
+
* explicit user override before stopping or restarting the hosting daemon;
|
|
16
|
+
* and restart the web/API process before the session daemon.
|
|
17
17
|
*/
|
|
18
18
|
import { piWebDataDir } from "../../config.js";
|
|
19
19
|
import { sessiondEndpointDescription } from "../../sessiond/config.js";
|
|
@@ -25,7 +25,8 @@ export function sessionEnvironmentFacts({ env }) {
|
|
|
25
25
|
`This session runs inside a PI WEB session daemon. Every process spawned from it — the bash tool, terminals, subsessions — inherits \`${PI_WEB_SESSION_ENV}=1\`, marking it as nested inside this PI WEB instance.`,
|
|
26
26
|
`The hosting instance owns the data directory \`${piWebDataDir(env)}\` and its session daemon listens on \`${sessiondEndpointDescription(env)}\`. Spawned processes inherit the daemon's \`PI_WEB_*\` environment, which points at that same live instance.`,
|
|
27
27
|
"Starting another PI WEB instance with those inherited values fails loudly at startup because the live instance owns the state. To run a second instance, give it a distinct `PI_WEB_DATA_DIR`, `PI_WEB_SESSIOND_SOCKET` (or `PI_WEB_SESSIOND_PORT` / `PI_WEB_SESSIOND_HOST`), and `PI_WEB_PORT`.",
|
|
28
|
-
"
|
|
28
|
+
"Do not restart or stop the session daemon hosting this session unless the user explicitly requests or authorizes that daemon restart or stop. An explicit user request overrides this default restriction, including a request to schedule the operation. The daemon owns the terminals and session runtime, so warn the user that the operation interrupts active sessions and this session's own work. A general request to fix the app is not authorization to restart the daemon.",
|
|
29
|
+
"For an authorized scheduled daemon restart or stop, use a detached service-manager timer (such as systemd-run --user --on-active), not a sleep process owned by this session. Report the scheduled operation and delay before it runs; do not claim completion without checking. When both PI WEB services must be restarted, restart the web/API process before the session daemon.",
|
|
29
30
|
];
|
|
30
31
|
return [
|
|
31
32
|
"<pi_web_session_environment>",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sessionEnvironmentFacts.js","sourceRoot":"","sources":["../../../src/server/sessions/sessionEnvironmentFacts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAEvE,gGAAgG;AAChG,MAAM,CAAC,MAAM,kBAAkB,GAAG,gBAAgB,CAAC;AAMnD,iDAAiD;AACjD,MAAM,UAAU,uBAAuB,CAAC,EAAE,GAAG,EAAgC;IAC3E,MAAM,KAAK,GAAG;QACZ,wIAAwI,kBAAkB,yDAAyD;QACnN,kDAAkD,YAAY,CAAC,GAAG,CAAC,0CAA0C,2BAA2B,CAAC,GAAG,CAAC,+GAA+G;QAC5P,kSAAkS;QAClS,
|
|
1
|
+
{"version":3,"file":"sessionEnvironmentFacts.js","sourceRoot":"","sources":["../../../src/server/sessions/sessionEnvironmentFacts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAEvE,gGAAgG;AAChG,MAAM,CAAC,MAAM,kBAAkB,GAAG,gBAAgB,CAAC;AAMnD,iDAAiD;AACjD,MAAM,UAAU,uBAAuB,CAAC,EAAE,GAAG,EAAgC;IAC3E,MAAM,KAAK,GAAG;QACZ,wIAAwI,kBAAkB,yDAAyD;QACnN,kDAAkD,YAAY,CAAC,GAAG,CAAC,0CAA0C,2BAA2B,CAAC,GAAG,CAAC,+GAA+G;QAC5P,kSAAkS;QAClS,udAAud;QACvd,sXAAsX;KACvX,CAAC;IACF,OAAO;QACL,8BAA8B;QAC9B,oDAAoD;QACpD,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC;QACnC,+BAA+B;KAChC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAQD;;;;;GAKG;AACH,MAAM,UAAU,gCAAgC,CAAC,EAAE,GAAG,EAAE,OAAO,EAAmC;IAChG,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,CAAC;IACxB,OAAO,CAAC,uBAAuB,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;AAC5C,CAAC"}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { canonicalizeStoredCwd } from "../workingDirectory.js";
|
|
3
|
+
/**
|
|
4
|
+
* Disposable daemon-owned source bindings and hash memos, never extracted files
|
|
5
|
+
* or decoded-byte caches. Bindings are scoped to a session AND workspace. Both
|
|
6
|
+
* strong indexes are bounded; weak block memos survive eviction only as long as
|
|
7
|
+
* their runtime/transcript owner retains the original block.
|
|
8
|
+
*/
|
|
9
|
+
export class SessionMediaIndex {
|
|
10
|
+
constructor(options = {}) {
|
|
11
|
+
this.media = new Map();
|
|
12
|
+
this.memo = new Map();
|
|
13
|
+
this.blocks = new WeakMap();
|
|
14
|
+
this.mediaBytes = 0;
|
|
15
|
+
this.memoBytes = 0;
|
|
16
|
+
this.maxMediaBytes = options.maxMediaBytes ?? 64 * 1024 * 1024;
|
|
17
|
+
this.maxMediaEntries = options.maxMediaEntries ?? 1024;
|
|
18
|
+
this.maxMemoBytes = options.maxMemoBytes ?? 32 * 1024 * 1024;
|
|
19
|
+
this.maxMemoEntries = options.maxMemoEntries ?? 256;
|
|
20
|
+
this.hash = options.hash ?? ((source) => createHash("sha256").update(source).digest("hex"));
|
|
21
|
+
this.decode = options.decode ?? ((source) => Buffer.from(source, "base64"));
|
|
22
|
+
}
|
|
23
|
+
/** An unscoped projection may produce a reference, but cannot create a binding. */
|
|
24
|
+
reference(scope, block) {
|
|
25
|
+
const image = this.image(block);
|
|
26
|
+
if (image === undefined)
|
|
27
|
+
return undefined;
|
|
28
|
+
if (scope !== undefined)
|
|
29
|
+
this.remember(scope, image);
|
|
30
|
+
return image.reference;
|
|
31
|
+
}
|
|
32
|
+
get(scope, mediaId) {
|
|
33
|
+
const key = bindingKey(scope, mediaId);
|
|
34
|
+
const image = this.media.get(key);
|
|
35
|
+
if (image === undefined)
|
|
36
|
+
return undefined;
|
|
37
|
+
this.media.delete(key);
|
|
38
|
+
this.media.set(key, image);
|
|
39
|
+
return this.decodeImage(image);
|
|
40
|
+
}
|
|
41
|
+
/** Stop at the match; known nonmatching blocks need neither hashing nor decoding. */
|
|
42
|
+
find(scope, mediaId, values) {
|
|
43
|
+
for (const value of values) {
|
|
44
|
+
for (const block of imageBlocks(value)) {
|
|
45
|
+
const image = this.image(block);
|
|
46
|
+
if (image?.reference.mediaId !== mediaId)
|
|
47
|
+
continue;
|
|
48
|
+
this.remember(scope, image);
|
|
49
|
+
return this.decodeImage(image);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
forgetSession(scope) {
|
|
55
|
+
const prefix = `${scopeKey(scope)}:`;
|
|
56
|
+
for (const [key, image] of this.media) {
|
|
57
|
+
if (!key.startsWith(prefix))
|
|
58
|
+
continue;
|
|
59
|
+
this.media.delete(key);
|
|
60
|
+
this.mediaBytes -= bindingWeight(image);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
clear() {
|
|
64
|
+
this.media.clear();
|
|
65
|
+
this.memo.clear();
|
|
66
|
+
this.blocks = new WeakMap();
|
|
67
|
+
this.mediaBytes = 0;
|
|
68
|
+
this.memoBytes = 0;
|
|
69
|
+
}
|
|
70
|
+
image(block) {
|
|
71
|
+
if (block["type"] !== "image" || typeof block["data"] !== "string" || !isSessionImageMimeType(block["mimeType"]))
|
|
72
|
+
return undefined;
|
|
73
|
+
const source = block["data"];
|
|
74
|
+
const mimeType = block["mimeType"];
|
|
75
|
+
const previous = this.blocks.get(block);
|
|
76
|
+
if (previous?.source === source && previous.mimeType === mimeType)
|
|
77
|
+
return previous;
|
|
78
|
+
const key = `${mimeType}\0${source}`;
|
|
79
|
+
const cached = this.memo.get(key);
|
|
80
|
+
if (cached !== undefined) {
|
|
81
|
+
this.memo.delete(key);
|
|
82
|
+
this.memo.set(key, cached);
|
|
83
|
+
this.blocks.set(block, cached);
|
|
84
|
+
return cached;
|
|
85
|
+
}
|
|
86
|
+
const byteSize = imageByteSize(source);
|
|
87
|
+
if (byteSize === undefined)
|
|
88
|
+
return undefined;
|
|
89
|
+
const image = { source, mimeType, reference: { type: "image", mediaId: this.hash(key), mimeType, byteSize } };
|
|
90
|
+
this.blocks.set(block, image);
|
|
91
|
+
const weight = memoWeight(image);
|
|
92
|
+
if (weight <= this.maxMemoBytes && this.maxMemoEntries > 0) {
|
|
93
|
+
this.memo.set(key, image);
|
|
94
|
+
this.memoBytes += weight;
|
|
95
|
+
while (this.memoBytes > this.maxMemoBytes || this.memo.size > this.maxMemoEntries) {
|
|
96
|
+
const oldest = this.memo.entries().next().value;
|
|
97
|
+
if (oldest === undefined)
|
|
98
|
+
break;
|
|
99
|
+
this.memo.delete(oldest[0]);
|
|
100
|
+
this.memoBytes -= memoWeight(oldest[1]);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return image;
|
|
104
|
+
}
|
|
105
|
+
decodeImage(image) {
|
|
106
|
+
return { data: this.decode(image.source), mimeType: image.mimeType };
|
|
107
|
+
}
|
|
108
|
+
remember(scope, image) {
|
|
109
|
+
const weight = bindingWeight(image);
|
|
110
|
+
if (weight > this.maxMediaBytes || this.maxMediaEntries <= 0)
|
|
111
|
+
return;
|
|
112
|
+
const key = bindingKey(scope, image.reference.mediaId);
|
|
113
|
+
const previous = this.media.get(key);
|
|
114
|
+
if (previous !== undefined)
|
|
115
|
+
this.mediaBytes -= bindingWeight(previous);
|
|
116
|
+
this.media.delete(key);
|
|
117
|
+
this.media.set(key, image);
|
|
118
|
+
this.mediaBytes += weight;
|
|
119
|
+
while (this.mediaBytes > this.maxMediaBytes || this.media.size > this.maxMediaEntries) {
|
|
120
|
+
const oldest = this.media.entries().next().value;
|
|
121
|
+
if (oldest === undefined)
|
|
122
|
+
break;
|
|
123
|
+
this.media.delete(oldest[0]);
|
|
124
|
+
this.mediaBytes -= bindingWeight(oldest[1]);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
export function isSessionImageMimeType(value) {
|
|
129
|
+
return value === "image/png" || value === "image/jpeg" || value === "image/gif" || value === "image/webp";
|
|
130
|
+
}
|
|
131
|
+
function scopeKey(scope) {
|
|
132
|
+
return JSON.stringify([scope.id, canonicalizeStoredCwd(scope.cwd)]);
|
|
133
|
+
}
|
|
134
|
+
function bindingKey(scope, mediaId) {
|
|
135
|
+
return `${scopeKey(scope)}:${mediaId}`;
|
|
136
|
+
}
|
|
137
|
+
function bindingWeight(image) {
|
|
138
|
+
// Conservatively count UTF-16 source storage, not the deferred binary size.
|
|
139
|
+
return image.source.length * 2;
|
|
140
|
+
}
|
|
141
|
+
function memoWeight(image) {
|
|
142
|
+
// The content key also retains a copy of the source and MIME.
|
|
143
|
+
return bindingWeight(image) + (image.mimeType.length + 1 + image.source.length) * 2;
|
|
144
|
+
}
|
|
145
|
+
function imageByteSize(source) {
|
|
146
|
+
// Buffer.from(base64) silently accepts junk. Validate standard base64 (with
|
|
147
|
+
// optional omitted padding) arithmetically, including unused final sextet bits,
|
|
148
|
+
// without allocating, decoding, or re-encoding every transcript image.
|
|
149
|
+
if (source === "" || !/^[A-Za-z0-9+/]+={0,2}$/u.test(source))
|
|
150
|
+
return undefined;
|
|
151
|
+
const padding = source.endsWith("==") ? 2 : source.endsWith("=") ? 1 : 0;
|
|
152
|
+
const length = source.length - padding;
|
|
153
|
+
const remainder = length % 4;
|
|
154
|
+
if (remainder === 1 || (padding > 0 && (source.length % 4 !== 0 || padding !== 4 - remainder)))
|
|
155
|
+
return undefined;
|
|
156
|
+
const last = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".indexOf(source.charAt(length - 1));
|
|
157
|
+
if ((remainder === 2 && (last & 15) !== 0) || (remainder === 3 && (last & 3) !== 0))
|
|
158
|
+
return undefined;
|
|
159
|
+
return Math.floor(length * 3 / 4);
|
|
160
|
+
}
|
|
161
|
+
/** Only Pi content containers, not arbitrary tool arguments or provider metadata. */
|
|
162
|
+
function* imageBlocks(value) {
|
|
163
|
+
if (isUnknownArray(value)) {
|
|
164
|
+
for (const part of value)
|
|
165
|
+
yield* imageBlocks(part);
|
|
166
|
+
}
|
|
167
|
+
else if (isRecord(value)) {
|
|
168
|
+
if (value["type"] === "image")
|
|
169
|
+
yield value;
|
|
170
|
+
else {
|
|
171
|
+
if (value["content"] !== undefined)
|
|
172
|
+
yield* imageBlocks(value["content"]);
|
|
173
|
+
if (value["message"] !== undefined)
|
|
174
|
+
yield* imageBlocks(value["message"]);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function isUnknownArray(value) {
|
|
179
|
+
return Array.isArray(value);
|
|
180
|
+
}
|
|
181
|
+
function isRecord(value) {
|
|
182
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=sessionMediaIndex.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessionMediaIndex.js","sourceRoot":"","sources":["../../../src/server/sessions/sessionMediaIndex.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { normalizeContext } from "@earendil-works/pi-ai";
|
|
1
2
|
const SESSION_NAME_TIMEOUT_MS = 10_000;
|
|
2
3
|
const SESSION_NAME_MAX_INPUT_CHARS = 4_000;
|
|
3
4
|
const SESSION_NAME_MAX_LENGTH = 60;
|
|
@@ -9,14 +10,14 @@ export function deterministicSessionName(firstMessage) {
|
|
|
9
10
|
return relayHandoffSessionName(firstMessage.trimStart());
|
|
10
11
|
}
|
|
11
12
|
export async function generateShortSessionName(streamFn, model, firstMessage) {
|
|
12
|
-
const stream = await streamFn(model, {
|
|
13
|
+
const stream = await streamFn(model, normalizeContext({
|
|
13
14
|
systemPrompt: "Generate a concise title for a coding-agent chat session. Return only the title, with no quotes or punctuation wrapper.",
|
|
14
15
|
messages: [{
|
|
15
16
|
role: "user",
|
|
16
17
|
content: `Create a 2-6 word title for this request:\n\n${truncateInput(firstMessage)}`,
|
|
17
18
|
timestamp: Date.now(),
|
|
18
19
|
}],
|
|
19
|
-
}, {
|
|
20
|
+
}), {
|
|
20
21
|
maxTokens: 24,
|
|
21
22
|
reasoning: "minimal",
|
|
22
23
|
signal: AbortSignal.timeout(SESSION_NAME_TIMEOUT_MS),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sessionNameGenerator.js","sourceRoot":"","sources":["../../../src/server/sessions/sessionNameGenerator.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sessionNameGenerator.js","sourceRoot":"","sources":["../../../src/server/sessions/sessionNameGenerator.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { ASK_USER_ID_MAX_LENGTH, ASK_USER_OPTION_LIMIT, ASK_USER_OTHER_TEXT_MAX_LENGTH, ASK_USER_QUESTION_LIMIT, EXTENSION_DIALOG_ID_MAX_LENGTH, EXTENSION_DIALOG_INPUT_MAX_LENGTH, SESSION_TREE_CUSTOM_INSTRUCTIONS_MAX_LENGTH, SESSION_UNREAD_CATALOG_ID_MAX_LENGTH, SESSION_UNREAD_CWD_MAX_LENGTH, SESSION_UNREAD_SESSION_ID_MAX_LENGTH } from "../../shared/apiTypes.js";
|
|
2
|
-
import {
|
|
2
|
+
import { parseSessionDefaultsUpdate } from "../../shared/sessionDefaults.js";
|
|
3
|
+
import { projectBrowserMessageResponse, projectBrowserStreamSnapshot, projectBrowserTranscriptSnapshot } from "../browserMessageProjection.js";
|
|
4
|
+
import { isSessionMediaId, SESSION_MEDIA_MODE } from "../../shared/sessionMedia.js";
|
|
5
|
+
import { isSessionImageMimeType } from "./sessionMediaIndex.js";
|
|
3
6
|
import { normalizeRequestCwd } from "../workingDirectory.js";
|
|
4
7
|
import { normalizeSessionCleanupRequest } from "./sessionCleanup.js";
|
|
5
8
|
const MAX_NOTIFICATION_SESSION_ID_LENGTH = 512;
|
|
@@ -142,12 +145,37 @@ export function registerSessionRoutes(app, sessions, eventHub, prefix = "") {
|
|
|
142
145
|
try {
|
|
143
146
|
const page = { ...optionalField("before", optionalNumber(request.query.before)), ...optionalField("limit", optionalNumber(request.query.limit)) };
|
|
144
147
|
const messages = await sessions.messages(ref, page);
|
|
145
|
-
return projectBrowserMessageResponse(messages);
|
|
148
|
+
return projectBrowserMessageResponse(messages, imageProjector(request.query, eventHub, ref));
|
|
146
149
|
}
|
|
147
150
|
catch (error) {
|
|
148
151
|
return reply.code(404).send({ error: errorMessage(error) });
|
|
149
152
|
}
|
|
150
153
|
});
|
|
154
|
+
app.get(`${prefix}/sessions/:sessionId/media/:mediaId`, async (request, reply) => {
|
|
155
|
+
const ref = sessionRefFromQueryOr400(request.params.sessionId, request.query, reply);
|
|
156
|
+
if (ref === undefined)
|
|
157
|
+
return reply;
|
|
158
|
+
if (!isSessionMediaId(request.params.mediaId))
|
|
159
|
+
return reply.code(400).send({ error: "Invalid media id" });
|
|
160
|
+
try {
|
|
161
|
+
const media = await sessions.media(ref, request.params.mediaId);
|
|
162
|
+
if (media === undefined)
|
|
163
|
+
return await reply.code(404).send({ error: "Session media not found" });
|
|
164
|
+
if (!isSessionImageMimeType(media.mimeType))
|
|
165
|
+
throw new Error("Unsupported session image MIME type");
|
|
166
|
+
return await reply
|
|
167
|
+
.type(media.mimeType)
|
|
168
|
+
.header("Cache-Control", "private, max-age=31536000, immutable")
|
|
169
|
+
.header("Content-Length", media.data.byteLength)
|
|
170
|
+
.header("X-Content-Type-Options", "nosniff")
|
|
171
|
+
.send(media.data);
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
if (isSessionNotFoundError(error))
|
|
175
|
+
return reply.code(404).send({ error: errorMessage(error) });
|
|
176
|
+
throw error; // Fastify logs unexpected failures and returns 500, not a false miss.
|
|
177
|
+
}
|
|
178
|
+
});
|
|
151
179
|
app.get(`${prefix}/sessions/:sessionId/status`, async (request, reply) => {
|
|
152
180
|
const ref = sessionRefFromQueryOr400(request.params.sessionId, request.query, reply);
|
|
153
181
|
if (ref === undefined)
|
|
@@ -159,12 +187,24 @@ export function registerSessionRoutes(app, sessions, eventHub, prefix = "") {
|
|
|
159
187
|
return reply.code(404).send({ error: errorMessage(error) });
|
|
160
188
|
}
|
|
161
189
|
});
|
|
190
|
+
app.get(`${prefix}/sessions/:sessionId/transcript-snapshot`, async (request, reply) => {
|
|
191
|
+
const ref = sessionRefFromQueryOr400(request.params.sessionId, request.query, reply);
|
|
192
|
+
if (ref === undefined)
|
|
193
|
+
return reply;
|
|
194
|
+
try {
|
|
195
|
+
const snapshot = await sessions.transcriptSnapshot(ref, { ...optionalField("limit", optionalNumber(request.query.limit)) });
|
|
196
|
+
return projectBrowserTranscriptSnapshot(snapshot, imageProjector(request.query, eventHub, ref));
|
|
197
|
+
}
|
|
198
|
+
catch (error) {
|
|
199
|
+
return reply.code(404).send({ error: errorMessage(error) });
|
|
200
|
+
}
|
|
201
|
+
});
|
|
162
202
|
app.get(`${prefix}/sessions/:sessionId/stream-snapshot`, async (request, reply) => {
|
|
163
203
|
const ref = sessionRefFromQueryOr400(request.params.sessionId, request.query, reply);
|
|
164
204
|
if (ref === undefined)
|
|
165
205
|
return reply;
|
|
166
206
|
try {
|
|
167
|
-
return await sessions.streamSnapshot(ref);
|
|
207
|
+
return projectBrowserStreamSnapshot(await sessions.streamSnapshot(ref), imageProjector(request.query, eventHub, ref));
|
|
168
208
|
}
|
|
169
209
|
catch (error) {
|
|
170
210
|
return reply.code(404).send({ error: errorMessage(error) });
|
|
@@ -234,6 +274,23 @@ export function registerSessionRoutes(app, sessions, eventHub, prefix = "") {
|
|
|
234
274
|
return reply.code(mutationErrorStatus(error)).send({ error: errorMessage(error) });
|
|
235
275
|
}
|
|
236
276
|
});
|
|
277
|
+
app.get(`${prefix}/sessions/:sessionId/defaults`, async (request, reply) => {
|
|
278
|
+
try {
|
|
279
|
+
return await sessions.getSessionDefaults(sessionRefFromQuery(request.params.sessionId, request.query));
|
|
280
|
+
}
|
|
281
|
+
catch (error) {
|
|
282
|
+
return reply.code(400).send({ error: errorMessage(error) });
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
app.post(`${prefix}/sessions/:sessionId/defaults`, async (request, reply) => {
|
|
286
|
+
try {
|
|
287
|
+
const body = requireRecord(request.body);
|
|
288
|
+
return await sessions.setSessionDefaults(sessionRefFromBody(request.params.sessionId, body), parseSessionDefaultsUpdate(body));
|
|
289
|
+
}
|
|
290
|
+
catch (error) {
|
|
291
|
+
return reply.code(400).send({ error: errorMessage(error) });
|
|
292
|
+
}
|
|
293
|
+
});
|
|
237
294
|
app.get(`${prefix}/sessions/:sessionId/thinking-levels`, async (request, reply) => {
|
|
238
295
|
const ref = sessionRefFromQueryOr400(request.params.sessionId, request.query, reply);
|
|
239
296
|
if (ref === undefined)
|
|
@@ -466,7 +523,7 @@ export function registerSessionRoutes(app, sessions, eventHub, prefix = "") {
|
|
|
466
523
|
app.get(`${prefix}/sessions/:sessionId/events`, { websocket: true }, (socket, request) => {
|
|
467
524
|
// Only the id matters for event subscription; cwd is intentionally ignored
|
|
468
525
|
// so a malformed value cannot throw inside the websocket handler.
|
|
469
|
-
eventHub.add(request.params.sessionId, socket);
|
|
526
|
+
eventHub.add(request.params.sessionId, socket, request.query.media === SESSION_MEDIA_MODE ? SESSION_MEDIA_MODE : undefined);
|
|
470
527
|
});
|
|
471
528
|
app.get(`${prefix}/sessions/events`, { websocket: true }, (socket) => {
|
|
472
529
|
eventHub.addGlobal(socket);
|
|
@@ -475,6 +532,9 @@ export function registerSessionRoutes(app, sessions, eventHub, prefix = "") {
|
|
|
475
532
|
eventHub.addGlobal(socket);
|
|
476
533
|
});
|
|
477
534
|
}
|
|
535
|
+
function imageProjector(query, hub, ref) {
|
|
536
|
+
return query.media === SESSION_MEDIA_MODE ? (image) => hub.mediaIndex.reference(ref, image) : undefined;
|
|
537
|
+
}
|
|
478
538
|
function bulkMutationRefsFromBody(body) {
|
|
479
539
|
const record = requireRecord(body);
|
|
480
540
|
const sessions = record["sessions"];
|