@jmfederico/pi-web 1.202607.1 → 1.202607.2
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 +8 -8
- package/dist/cli.js +17 -5
- package/dist/cli.js.map +1 -1
- package/dist/client/assets/{CodeViewer-uASb28Y1.js → CodeViewer-B4M13447.js} +1 -1
- package/dist/client/assets/{TerminalPanel-DfTmykFw.js → TerminalPanel-Bmvj-Ecn.js} +1 -1
- package/dist/client/assets/{UnifiedDiffViewer-laQN1x7P.js → UnifiedDiffViewer-C0hygwRo.js} +1 -1
- package/dist/client/assets/index-TvPsRyS1.js +3545 -0
- package/dist/client/index.html +1 -1
- package/dist/config.js +13 -0
- package/dist/config.js.map +1 -1
- package/dist/server/diagnostics/nodePtyNativeModule.js +36 -0
- package/dist/server/diagnostics/nodePtyNativeModule.js.map +1 -0
- package/dist/server/git/gitService.js +176 -5
- package/dist/server/git/gitService.js.map +1 -1
- package/dist/server/piWebStatus.js +6 -3
- package/dist/server/piWebStatus.js.map +1 -1
- package/dist/server/realtime/sessionEventHub.js +4 -0
- package/dist/server/realtime/sessionEventHub.js.map +1 -1
- package/dist/server/sessiond.js +47 -7
- package/dist/server/sessiond.js.map +1 -1
- package/dist/server/sessions/authService.js +58 -7
- package/dist/server/sessions/authService.js.map +1 -1
- package/dist/server/sessions/globalProviderPolicy.js +98 -0
- package/dist/server/sessions/globalProviderPolicy.js.map +1 -0
- package/dist/server/sessions/modelCatalogRefresher.js +184 -0
- package/dist/server/sessions/modelCatalogRefresher.js.map +1 -0
- package/dist/server/sessions/piSessionService.js +736 -100
- package/dist/server/sessions/piSessionService.js.map +1 -1
- package/dist/server/sessions/plainTextTheme.js +112 -0
- package/dist/server/sessions/plainTextTheme.js.map +1 -0
- package/dist/server/sessions/sessionCommandService.js +68 -9
- package/dist/server/sessions/sessionCommandService.js.map +1 -1
- package/dist/server/sessions/sessionNotificationStore.js +484 -0
- package/dist/server/sessions/sessionNotificationStore.js.map +1 -0
- package/dist/server/sessions/sessionRoutes.js +175 -0
- package/dist/server/sessions/sessionRoutes.js.map +1 -1
- package/dist/server/sessions/sessionTreeProjection.js +250 -0
- package/dist/server/sessions/sessionTreeProjection.js.map +1 -0
- package/dist/server/sessions/sessionUnreadStore.js +520 -0
- package/dist/server/sessions/sessionUnreadStore.js.map +1 -0
- package/dist/server/sessions/spawnSessionTool.js +2 -2
- package/dist/server/sessions/spawnSessionTool.js.map +1 -1
- package/dist/server/sessions/spawnSubsessionTool.js +10 -12
- package/dist/server/sessions/spawnSubsessionTool.js.map +1 -1
- package/dist/shared/apiTypes.d.ts +159 -2
- package/dist/shared/apiTypes.js +10 -0
- package/dist/shared/apiTypes.js.map +1 -1
- package/dist/shared/capabilities.js +6 -0
- package/dist/shared/capabilities.js.map +1 -1
- package/dist/shared/federatedRoutes.js +8 -0
- package/dist/shared/federatedRoutes.js.map +1 -1
- package/docs/config.md +32 -4
- package/docs/plugins.md +10 -6
- package/install.sh +1 -1
- package/package.json +8 -7
- package/dist/client/assets/index-Dhapi0oR.js +0 -3093
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sessionEventHub.js","sourceRoot":"","sources":["../../../src/server/realtime/sessionEventHub.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,0BAA0B,EAAE,MAAM,gCAAgC,CAAC;AAU5E,MAAM,OAAO,eAAe;IAA5B;QACmB,qBAAgB,GAAG,IAAI,GAAG,EAA+B,CAAC;QAC1D,kBAAa,GAAG,IAAI,GAAG,EAAkB,CAAC;QAC1C,iBAAY,GAAG,IAAI,GAAG,EAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"sessionEventHub.js","sourceRoot":"","sources":["../../../src/server/realtime/sessionEventHub.ts"],"names":[],"mappings":"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"}
|
package/dist/server/sessiond.js
CHANGED
|
@@ -7,10 +7,14 @@ import { WorkspaceActivityService } from "./activity/workspaceActivityService.js
|
|
|
7
7
|
import { registerWorkspaceActivityRoutes } from "./activity/workspaceActivityRoutes.js";
|
|
8
8
|
import { SessionEventHub } from "./realtime/sessionEventHub.js";
|
|
9
9
|
import { AuthService } from "./sessions/authService.js";
|
|
10
|
+
import { bootstrapAndFreezeGlobalExtensionProviders } from "./sessions/globalProviderPolicy.js";
|
|
10
11
|
import { registerAuthRoutes } from "./sessions/authRoutes.js";
|
|
12
|
+
import { ModelCatalogRefresher } from "./sessions/modelCatalogRefresher.js";
|
|
11
13
|
import { PiSessionService } from "./sessions/piSessionService.js";
|
|
12
14
|
import { createPiSessionManagerGateway } from "./sessions/piSessionManagerGateway.js";
|
|
13
15
|
import { registerSessionRoutes } from "./sessions/sessionRoutes.js";
|
|
16
|
+
import { SessionNotificationStore } from "./sessions/sessionNotificationStore.js";
|
|
17
|
+
import { FileSessionUnreadPersistence, SessionUnreadStore } from "./sessions/sessionUnreadStore.js";
|
|
14
18
|
import { ProjectScopedSpawnTargetResolver } from "./sessions/spawnTargetResolver.js";
|
|
15
19
|
import { ProjectService } from "./projects/projectService.js";
|
|
16
20
|
import { ProjectStore } from "./storage/projectStore.js";
|
|
@@ -20,7 +24,7 @@ import { TerminalService } from "./terminals/terminalService.js";
|
|
|
20
24
|
import { registerTerminalRoutes } from "./terminals/terminalRoutes.js";
|
|
21
25
|
import { getPiWebRuntimeComponent } from "./piWebStatus.js";
|
|
22
26
|
import { SESSIOND_RUNTIME_CAPABILITIES } from "../shared/capabilities.js";
|
|
23
|
-
import { agentSessionDirEnvKeys, effectivePiWebConfig, maxUploadBytes } from "../config.js";
|
|
27
|
+
import { agentSessionDirEnvKeys, effectivePiWebConfig, maxUploadBytes, offlineModeEnabled } from "../config.js";
|
|
24
28
|
import { createActiveAgentProfileDescriptor } from "../sessiond/activeAgentProfile.js";
|
|
25
29
|
import { runSessionDaemonStartup } from "./sessiond/sessionDaemonStartup.js";
|
|
26
30
|
const daemonEnvironment = Object.freeze({ ...process.env });
|
|
@@ -36,8 +40,31 @@ await runSessionDaemonStartup({
|
|
|
36
40
|
logger: app.log,
|
|
37
41
|
async createRuntime() {
|
|
38
42
|
const eventHub = new SessionEventHub();
|
|
43
|
+
const notificationStore = new SessionNotificationStore();
|
|
44
|
+
const unreadStore = new SessionUnreadStore({
|
|
45
|
+
persistence: new FileSessionUnreadPersistence(),
|
|
46
|
+
onPersistenceError(operation, error) {
|
|
47
|
+
app.log.error({ err: error, operation }, "session unread persistence failed");
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
await unreadStore.load();
|
|
39
51
|
const workspaceActivity = new WorkspaceActivityService(eventHub);
|
|
40
52
|
const auth = await AuthService.create({ agentDir: activeAgentProfile.dir, logger: app.log });
|
|
53
|
+
// Capture providers registered by global extensions while the runtime is
|
|
54
|
+
// still mutable, then freeze every later extension-provider mutation before
|
|
55
|
+
// any real session can load project resources.
|
|
56
|
+
await bootstrapAndFreezeGlobalExtensionProviders(auth.runtime, activeAgentProfile.dir, app.log);
|
|
57
|
+
// The shared model runtime is constructed offline so request paths never
|
|
58
|
+
// wait on provider-catalog fetches; this is the single bounded network
|
|
59
|
+
// refresher, and auth changes (login/logout) ask it for a prompt run. It
|
|
60
|
+
// stays fully inert when the operator asked for offline behavior.
|
|
61
|
+
const catalogRefresher = new ModelCatalogRefresher({
|
|
62
|
+
runtime: auth.runtime,
|
|
63
|
+
logger: app.log,
|
|
64
|
+
offline: offlineModeEnabled(daemonEnvironment),
|
|
65
|
+
});
|
|
66
|
+
catalogRefresher.start();
|
|
67
|
+
auth.subscribe(() => { catalogRefresher.requestRefresh(); });
|
|
41
68
|
const spawnTargets = config.spawnSessions
|
|
42
69
|
? new ProjectScopedSpawnTargetResolver({ projects: new ProjectService(new ProjectStore()), workspaces: new WorkspaceService() })
|
|
43
70
|
: undefined;
|
|
@@ -48,6 +75,8 @@ await runSessionDaemonStartup({
|
|
|
48
75
|
logger: app.log,
|
|
49
76
|
...(spawnTargets === undefined ? {} : { spawnTargets }),
|
|
50
77
|
subsessionsEnabled: spawnTargets !== undefined && config.subsessions,
|
|
78
|
+
notificationStore,
|
|
79
|
+
unreadStore,
|
|
51
80
|
sessionManager: createPiSessionManagerGateway({
|
|
52
81
|
agentDir: activeAgentProfile.dir,
|
|
53
82
|
env: daemonEnvironment,
|
|
@@ -60,7 +89,7 @@ await runSessionDaemonStartup({
|
|
|
60
89
|
...getPiWebRuntimeComponent("sessiond", SESSIOND_RUNTIME_CAPABILITIES),
|
|
61
90
|
activeAgentProfile,
|
|
62
91
|
});
|
|
63
|
-
return { eventHub, workspaceActivity, auth, sessions, terminals, activeAgentProfile, runtimeComponent };
|
|
92
|
+
return { eventHub, workspaceActivity, auth, sessions, terminals, unreadStore, activeAgentProfile, runtimeComponent, catalogRefresher };
|
|
64
93
|
},
|
|
65
94
|
registerRoutes({ eventHub, workspaceActivity, auth, sessions, terminals, runtimeComponent }) {
|
|
66
95
|
registerWorkspaceActivityRoutes(app, workspaceActivity);
|
|
@@ -81,17 +110,28 @@ await runSessionDaemonStartup({
|
|
|
81
110
|
}));
|
|
82
111
|
app.get("/runtime", () => runtimeComponent);
|
|
83
112
|
},
|
|
84
|
-
async listen({ auth, sessions, terminals }) {
|
|
113
|
+
async listen({ auth, sessions, terminals, unreadStore, catalogRefresher }) {
|
|
85
114
|
let shuttingDown = false;
|
|
86
115
|
async function shutdown(signal) {
|
|
87
116
|
if (shuttingDown)
|
|
88
117
|
return;
|
|
89
118
|
shuttingDown = true;
|
|
90
119
|
app.log.info({ signal }, "shutting down session daemon");
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
120
|
+
const attempt = async (operation, run) => {
|
|
121
|
+
try {
|
|
122
|
+
await run();
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
process.exitCode = 1;
|
|
126
|
+
app.log.error({ err: error, operation }, "session daemon shutdown operation failed");
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
await attempt("dispose terminals", () => { terminals.dispose(); });
|
|
130
|
+
await attempt("dispose catalog refresher", () => { catalogRefresher.dispose(); });
|
|
131
|
+
await attempt("dispose auth", () => { auth.dispose(); });
|
|
132
|
+
await attempt("dispose sessions", () => sessions.dispose());
|
|
133
|
+
await attempt("flush session unread state", () => unreadStore.flush());
|
|
134
|
+
await attempt("close server", () => app.close());
|
|
95
135
|
}
|
|
96
136
|
process.once("SIGINT", (signal) => { void shutdown(signal); });
|
|
97
137
|
process.once("SIGTERM", (signal) => { void shutdown(signal); });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sessiond.js","sourceRoot":"","sources":["../../src/server/sessiond.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,gBAAgB,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,wBAAwB,EAAE,MAAM,wCAAwC,CAAC;AAClF,OAAO,EAAE,+BAA+B,EAAE,MAAM,uCAAuC,CAAC;AACxF,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,EAAE,6BAA6B,EAAE,MAAM,uCAAuC,CAAC;AACtF,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,EAAE,gCAAgC,EAAE,MAAM,mCAAmC,CAAC;AACrF,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAAE,6BAA6B,EAAE,MAAM,2BAA2B,CAAC;AAC1E,OAAO,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"sessiond.js","sourceRoot":"","sources":["../../src/server/sessiond.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,gBAAgB,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,wBAAwB,EAAE,MAAM,wCAAwC,CAAC;AAClF,OAAO,EAAE,+BAA+B,EAAE,MAAM,uCAAuC,CAAC;AACxF,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,EAAE,0CAA0C,EAAE,MAAM,oCAAoC,CAAC;AAChG,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;AAC5E,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,EAAE,6BAA6B,EAAE,MAAM,uCAAuC,CAAC;AACtF,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,EAAE,wBAAwB,EAAE,MAAM,wCAAwC,CAAC;AAClF,OAAO,EAAE,4BAA4B,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACpG,OAAO,EAAE,gCAAgC,EAAE,MAAM,mCAAmC,CAAC;AACrF,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAAE,6BAA6B,EAAE,MAAM,2BAA2B,CAAC;AAC1E,OAAO,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAChH,OAAO,EAAE,kCAAkC,EAAE,MAAM,mCAAmC,CAAC;AACvF,OAAO,EAAE,uBAAuB,EAAE,MAAM,oCAAoC,CAAC;AAE7E,MAAM,iBAAiB,GAAsB,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;AAC/E,MAAM,EAAE,MAAM,EAAE,GAAG,oBAAoB,CAAC,EAAE,GAAG,EAAE,iBAAiB,EAAE,CAAC,CAAC;AACpE,MAAM,kBAAkB,GAAG,kCAAkC,CAAC;IAC5D,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO;IAC7B,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,GAAG;IACrB,iBAAiB,EAAE,sBAAsB,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC;CAChE,CAAC,CAAC;AACH,MAAM,GAAG,GAAG,OAAO,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,cAAc,CAAC,iBAAiB,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;AAC5F,MAAM,GAAG,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC;AAErC,MAAM,uBAAuB,CAAC;IAC5B,MAAM,EAAE,GAAG,CAAC,GAAG;IACf,KAAK,CAAC,aAAa;QACjB,MAAM,QAAQ,GAAG,IAAI,eAAe,EAAE,CAAC;QACvC,MAAM,iBAAiB,GAAG,IAAI,wBAAwB,EAAE,CAAC;QACzD,MAAM,WAAW,GAAG,IAAI,kBAAkB,CAAC;YACzC,WAAW,EAAE,IAAI,4BAA4B,EAAE;YAC/C,kBAAkB,CAAC,SAAS,EAAE,KAAK;gBACjC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,mCAAmC,CAAC,CAAC;YAChF,CAAC;SACF,CAAC,CAAC;QACH,MAAM,WAAW,CAAC,IAAI,EAAE,CAAC;QACzB,MAAM,iBAAiB,GAAG,IAAI,wBAAwB,CAAC,QAAQ,CAAC,CAAC;QACjE,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC;QAC7F,yEAAyE;QACzE,4EAA4E;QAC5E,+CAA+C;QAC/C,MAAM,0CAA0C,CAAC,IAAI,CAAC,OAAO,EAAE,kBAAkB,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;QAChG,yEAAyE;QACzE,uEAAuE;QACvE,yEAAyE;QACzE,kEAAkE;QAClE,MAAM,gBAAgB,GAAG,IAAI,qBAAqB,CAAC;YACjD,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,MAAM,EAAE,GAAG,CAAC,GAAG;YACf,OAAO,EAAE,kBAAkB,CAAC,iBAAiB,CAAC;SAC/C,CAAC,CAAC;QACH,gBAAgB,CAAC,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,GAAG,gBAAgB,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7D,MAAM,YAAY,GAAG,MAAM,CAAC,aAAa;YACvC,CAAC,CAAC,IAAI,gCAAgC,CAAC,EAAE,QAAQ,EAAE,IAAI,cAAc,CAAC,IAAI,YAAY,EAAE,CAAC,EAAE,UAAU,EAAE,IAAI,gBAAgB,EAAE,EAAE,CAAC;YAChI,CAAC,CAAC,SAAS,CAAC;QACd,MAAM,QAAQ,GAAG,IAAI,gBAAgB,CAAC,QAAQ,EAAE;YAC9C,YAAY,EAAE,IAAI,CAAC,OAAO;YAC1B,QAAQ,EAAE,kBAAkB,CAAC,GAAG;YAChC,iBAAiB;YACjB,MAAM,EAAE,GAAG,CAAC,GAAG;YACf,GAAG,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC;YACvD,kBAAkB,EAAE,YAAY,KAAK,SAAS,IAAI,MAAM,CAAC,WAAW;YACpE,iBAAiB;YACjB,WAAW;YACX,cAAc,EAAE,6BAA6B,CAAC;gBAC5C,QAAQ,EAAE,kBAAkB,CAAC,GAAG;gBAChC,GAAG,EAAE,iBAAiB;gBACtB,iBAAiB,EAAE,kBAAkB,CAAC,iBAAiB;aACxD,CAAC;SACH,CAAC,CAAC;QACH,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM,EAAE,EAAE,GAAG,QAAQ,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAClE,MAAM,SAAS,GAAG,IAAI,eAAe,CAAC,QAAQ,EAAE,iBAAiB,CAAC,CAAC;QACnE,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,CAAC;YACrC,GAAG,wBAAwB,CAAC,UAAU,EAAE,6BAA6B,CAAC;YACtE,kBAAkB;SACnB,CAAC,CAAC;QACH,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,CAAC;IACzI,CAAC;IACD,cAAc,CAAC,EAAE,QAAQ,EAAE,iBAAiB,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,gBAAgB,EAAE;QACzF,+BAA+B,CAAC,GAAG,EAAE,iBAAiB,CAAC,CAAC;QACxD,kBAAkB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAC9B,qBAAqB,CAAC,GAAG,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAC/C,sBAAsB,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QAEvC,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;YACxB,EAAE,EAAE,IAAI;YACR,cAAc,EAAE,QAAQ,CAAC,WAAW,EAAE;YACtC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,OAAO,EAAE;gBACP,SAAS,EAAE,gBAAgB,CAAC,SAAS;gBACrC,KAAK,EAAE,gBAAgB,CAAC,KAAK;gBAC7B,GAAG,CAAC,gBAAgB,CAAC,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,gBAAgB,CAAC,cAAc,EAAE,CAAC;gBAC7G,KAAK,EAAE,KAAK;gBACZ,SAAS,EAAE,gBAAgB,CAAC,SAAS;aACtC;SACF,CAAC,CAAC,CAAC;QAEJ,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,EAAE,CAAC,gBAAgB,CAAC,CAAC;IAC9C,CAAC;IACD,KAAK,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,gBAAgB,EAAE;QACvE,IAAI,YAAY,GAAG,KAAK,CAAC;QACzB,KAAK,UAAU,QAAQ,CAAC,MAAsB;YAC5C,IAAI,YAAY;gBAAE,OAAO;YACzB,YAAY,GAAG,IAAI,CAAC;YACpB,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,8BAA8B,CAAC,CAAC;YACzD,MAAM,OAAO,GAAG,KAAK,EAAE,SAAiB,EAAE,GAA+B,EAAiB,EAAE;gBAC1F,IAAI,CAAC;oBACH,MAAM,GAAG,EAAE,CAAC;gBACd,CAAC;gBAAC,OAAO,KAAc,EAAE,CAAC;oBACxB,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;oBACrB,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,0CAA0C,CAAC,CAAC;gBACvF,CAAC;YACH,CAAC,CAAC;YACF,MAAM,OAAO,CAAC,mBAAmB,EAAE,GAAG,EAAE,GAAG,SAAS,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;YACnE,MAAM,OAAO,CAAC,2BAA2B,EAAE,GAAG,EAAE,GAAG,gBAAgB,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;YAClF,MAAM,OAAO,CAAC,cAAc,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;YACzD,MAAM,OAAO,CAAC,kBAAkB,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;YAC5D,MAAM,OAAO,CAAC,4BAA4B,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC,CAAC;YACvE,MAAM,OAAO,CAAC,cAAc,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC;QACnD,CAAC;QAED,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE,GAAG,KAAK,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC/D,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,MAAM,EAAE,EAAE,GAAG,KAAK,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAEhE,MAAM,SAAS,GAAG,iBAAiB,CAAC,sBAAsB,CAAC,CAAC;QAC5D,MAAM,IAAI,GAAG,SAAS,KAAK,SAAS,IAAI,SAAS,KAAK,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACzF,MAAM,IAAI,GAAG,iBAAiB,CAAC,sBAAsB,CAAC,IAAI,WAAW,CAAC;QAEtE,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,MAAM,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACnC,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,GAAG,kBAAkB,EAAE,CAAC;YAClC,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAChD,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YAChC,MAAM,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;YAC3B,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -3,11 +3,62 @@ import { ModelRuntime } from "@earendil-works/pi-coding-agent";
|
|
|
3
3
|
import { getLoginProviderOptions, getLogoutProviderOptions } from "./authProviderOptions.js";
|
|
4
4
|
import { OAuthLoginFlowService } from "./oauthLoginFlowService.js";
|
|
5
5
|
const noopLogger = { error() { } };
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Serializes the `PI_OFFLINE` windows below, which is what keeps their
|
|
8
|
+
* save/restore pairs properly nested. Two overlapping calls would otherwise both
|
|
9
|
+
* capture the forced `"1"` and restore it, leaving the whole process offline
|
|
10
|
+
* permanently. Owned by this module only; nothing else may mutate it.
|
|
11
|
+
*/
|
|
12
|
+
let offlineRuntimeCreations = Promise.resolve();
|
|
13
|
+
/**
|
|
14
|
+
* Create the shared model runtime with runtime-owned network refreshes disabled.
|
|
15
|
+
*
|
|
16
|
+
* Upstream `ModelRuntime.refresh()`, `login()`, and `logout()` always refresh
|
|
17
|
+
* with `allowNetwork: modelNetworkEnabled` by default, and `login()`/`logout()`
|
|
18
|
+
* accept no abort signal. With the default (`PI_OFFLINE` unset) a single stalled
|
|
19
|
+
* provider-catalog fetch can block those call paths for minutes — and, because
|
|
20
|
+
* pi-web shares one runtime and pi coalesces per-provider refreshes, session
|
|
21
|
+
* creation joins the same stalled fetch. Forcing `PI_OFFLINE` during construction makes every runtime-driven
|
|
22
|
+
* refresh local-only; pi-web performs its own bounded catalog refreshes in the
|
|
23
|
+
* background instead (see modelCatalogRefresher.ts).
|
|
24
|
+
*
|
|
25
|
+
* `modelNetworkEnabled` is computed once from the environment inside
|
|
26
|
+
* `ModelRuntime.create()`, so the env var is the only lever upstream exposes.
|
|
27
|
+
* Calls are queued so their env windows never overlap.
|
|
28
|
+
*/
|
|
29
|
+
function createOfflineModelRuntime(options) {
|
|
30
|
+
const created = offlineRuntimeCreations.then(() => forceOfflineWhile(() => ModelRuntime.create(options)));
|
|
31
|
+
// A failed creation must not poison the queue; the caller still sees the rejection.
|
|
32
|
+
offlineRuntimeCreations = created.then(() => undefined, () => undefined);
|
|
33
|
+
return created;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Force `PI_OFFLINE` for the duration of `create`, then restore what was there.
|
|
37
|
+
*
|
|
38
|
+
* `process.env` is process-wide, so this window is a real global side effect:
|
|
39
|
+
* anything reading `PI_OFFLINE` while `create` awaits observes offline mode,
|
|
40
|
+
* including upstream's package manager, tools manager, and version check. That
|
|
41
|
+
* is acceptable because pi-web only builds runtimes during daemon startup and in
|
|
42
|
+
* tests, and the window is one `ModelRuntime.create()` call — but it is why
|
|
43
|
+
* callers must stay serialized rather than run concurrently.
|
|
44
|
+
*/
|
|
45
|
+
async function forceOfflineWhile(create) {
|
|
46
|
+
const previous = process.env["PI_OFFLINE"];
|
|
47
|
+
process.env["PI_OFFLINE"] = "1";
|
|
48
|
+
try {
|
|
49
|
+
return await create();
|
|
50
|
+
}
|
|
51
|
+
finally {
|
|
52
|
+
if (previous === undefined)
|
|
53
|
+
delete process.env["PI_OFFLINE"];
|
|
54
|
+
else
|
|
55
|
+
process.env["PI_OFFLINE"] = previous;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export function createModelRuntimeForAgentDir(agentDir) {
|
|
59
|
+
return createOfflineModelRuntime({
|
|
8
60
|
authPath: join(agentDir, "auth.json"),
|
|
9
61
|
modelsPath: join(agentDir, "models.json"),
|
|
10
|
-
...(allowModelNetwork === undefined ? {} : { allowModelNetwork }),
|
|
11
62
|
});
|
|
12
63
|
}
|
|
13
64
|
export class AuthService {
|
|
@@ -18,7 +69,7 @@ export class AuthService {
|
|
|
18
69
|
this.logger = logger;
|
|
19
70
|
}
|
|
20
71
|
static async create(deps = {}) {
|
|
21
|
-
const runtime = deps.runtime ?? (deps.agentDir === undefined ? await
|
|
72
|
+
const runtime = deps.runtime ?? (deps.agentDir === undefined ? await createOfflineModelRuntime({}) : await createModelRuntimeForAgentDir(deps.agentDir));
|
|
22
73
|
const logger = deps.logger ?? noopLogger;
|
|
23
74
|
const authFlows = deps.authFlows ?? new OAuthLoginFlowService({ logger });
|
|
24
75
|
return new AuthService(runtime, authFlows, logger);
|
|
@@ -34,7 +85,7 @@ export class AuthService {
|
|
|
34
85
|
this.listeners.clear();
|
|
35
86
|
}
|
|
36
87
|
async authProviders(mode, authType) {
|
|
37
|
-
await this.runtime.
|
|
88
|
+
await this.runtime.refresh();
|
|
38
89
|
const providers = mode === "logout" ? await getLogoutProviderOptions(this.runtime) : getLoginProviderOptions(this.runtime, authType);
|
|
39
90
|
return { providers };
|
|
40
91
|
}
|
|
@@ -113,7 +164,7 @@ export class AuthService {
|
|
|
113
164
|
}
|
|
114
165
|
}
|
|
115
166
|
async requireApiKeyLoginProvider(providerId) {
|
|
116
|
-
await this.runtime.
|
|
167
|
+
await this.runtime.refresh();
|
|
117
168
|
const provider = getLoginProviderOptions(this.runtime, "api_key").find((option) => option.id === providerId);
|
|
118
169
|
if (provider !== undefined)
|
|
119
170
|
return provider;
|
|
@@ -124,7 +175,7 @@ export class AuthService {
|
|
|
124
175
|
throw new Error(`API key provider not found: ${providerId}`);
|
|
125
176
|
}
|
|
126
177
|
async requireOAuthLoginProvider(providerId) {
|
|
127
|
-
await this.runtime.
|
|
178
|
+
await this.runtime.refresh();
|
|
128
179
|
const provider = getLoginProviderOptions(this.runtime, "oauth").find((option) => option.id === providerId);
|
|
129
180
|
if (provider === undefined)
|
|
130
181
|
throw new Error(`OAuth provider not found: ${providerId}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authService.js","sourceRoot":"","sources":["../../../src/server/sessions/authService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"authService.js","sourceRoot":"","sources":["../../../src/server/sessions/authService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { createAgentSessionServices, } from "@earendil-works/pi-coding-agent";
|
|
5
|
+
const LOG_CONTEXT = "global-provider-bootstrap";
|
|
6
|
+
async function loadGlobalExtensionServices(runtime, agentDir) {
|
|
7
|
+
const scratchCwd = await mkdtemp(join(tmpdir(), "pi-web-global-ext-"));
|
|
8
|
+
try {
|
|
9
|
+
return await createAgentSessionServices({ cwd: scratchCwd, agentDir, modelRuntime: runtime });
|
|
10
|
+
}
|
|
11
|
+
finally {
|
|
12
|
+
await rm(scratchCwd, { recursive: true, force: true });
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function logBootstrapDiagnostic(logger, diagnostic) {
|
|
16
|
+
const details = {
|
|
17
|
+
context: LOG_CONTEXT,
|
|
18
|
+
diagnosticType: diagnostic.type,
|
|
19
|
+
diagnostic: diagnostic.message,
|
|
20
|
+
};
|
|
21
|
+
if (diagnostic.type === "error") {
|
|
22
|
+
logger.error(details, "global extension provider bootstrap diagnostic");
|
|
23
|
+
}
|
|
24
|
+
else if (diagnostic.type === "warning") {
|
|
25
|
+
logger.warn(details, "global extension provider bootstrap diagnostic");
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
logger.info(details, "global extension provider bootstrap diagnostic");
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function freezeProviderMutations(runtime, logger) {
|
|
32
|
+
const originalMethods = {
|
|
33
|
+
registerNativeProvider: runtime.registerNativeProvider.bind(runtime),
|
|
34
|
+
registerProvider: runtime.registerProvider.bind(runtime),
|
|
35
|
+
unregisterProvider: runtime.unregisterProvider.bind(runtime),
|
|
36
|
+
};
|
|
37
|
+
const loggedProviderIds = {
|
|
38
|
+
registerNativeProvider: new Set(),
|
|
39
|
+
registerProvider: new Set(),
|
|
40
|
+
unregisterProvider: new Set(),
|
|
41
|
+
};
|
|
42
|
+
const logIgnoredMutation = (operation, providerId) => {
|
|
43
|
+
const loggedIds = loggedProviderIds[operation];
|
|
44
|
+
if (loggedIds.has(providerId))
|
|
45
|
+
return;
|
|
46
|
+
loggedIds.add(providerId);
|
|
47
|
+
try {
|
|
48
|
+
logger.info({ context: LOG_CONTEXT, operation, providerId }, "ignored provider mutation after global bootstrap");
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// Logging must not turn an ignored mutation into an extension failure.
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const frozenMethods = {
|
|
55
|
+
registerProvider(providerId) {
|
|
56
|
+
logIgnoredMutation("registerProvider", providerId);
|
|
57
|
+
},
|
|
58
|
+
registerNativeProvider(provider) {
|
|
59
|
+
logIgnoredMutation("registerNativeProvider", provider.id);
|
|
60
|
+
},
|
|
61
|
+
unregisterProvider(providerId) {
|
|
62
|
+
logIgnoredMutation("unregisterProvider", providerId);
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
try {
|
|
66
|
+
Object.assign(runtime, frozenMethods);
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
Object.assign(runtime, originalMethods);
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Load global extensions once against the shared model runtime, then make its
|
|
75
|
+
* extension-provider baseline immutable for the rest of the daemon lifetime.
|
|
76
|
+
* All sessions share this runtime, so accepting project-dependent mutations
|
|
77
|
+
* would leak provider configuration across workspaces. This is an accidental
|
|
78
|
+
* contamination guard, not a sandbox for otherwise trusted extensions.
|
|
79
|
+
*
|
|
80
|
+
* The temporary cwd is guaranteed to be empty, so Pi discovers agent-dir
|
|
81
|
+
* extensions without loading project resources. Documented initialization-time
|
|
82
|
+
* config and native registrations therefore reach the runtime through Pi's
|
|
83
|
+
* public service factory. Pi exposes no provider-freeze hook, so the daemon
|
|
84
|
+
* deliberately shadows the three public instance mutation methods afterward;
|
|
85
|
+
* every registration replay or later call is then a logged no-op.
|
|
86
|
+
*/
|
|
87
|
+
export async function bootstrapAndFreezeGlobalExtensionProviders(runtime, agentDir, logger) {
|
|
88
|
+
const services = await loadGlobalExtensionServices(runtime, agentDir);
|
|
89
|
+
const providerIds = Object.freeze([...runtime.getRegisteredProviderIds()].sort());
|
|
90
|
+
freezeProviderMutations(runtime, logger);
|
|
91
|
+
for (const diagnostic of services.diagnostics)
|
|
92
|
+
logBootstrapDiagnostic(logger, diagnostic);
|
|
93
|
+
for (const extensionError of services.resourceLoader.getExtensions().errors) {
|
|
94
|
+
logger.error({ context: LOG_CONTEXT, error: extensionError.error }, "global extension failed during provider bootstrap");
|
|
95
|
+
}
|
|
96
|
+
logger.info({ context: LOG_CONTEXT, providerIds }, "global extension provider baseline bootstrapped and frozen");
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=globalProviderPolicy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"globalProviderPolicy.js","sourceRoot":"","sources":["../../../src/server/sessions/globalProviderPolicy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EACL,0BAA0B,GAI3B,MAAM,iCAAiC,CAAC;AAYzC,MAAM,WAAW,GAAG,2BAA2B,CAAC;AAEhD,KAAK,UAAU,2BAA2B,CAAC,OAAqB,EAAE,QAAgB;IAChF,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,oBAAoB,CAAC,CAAC,CAAC;IACvE,IAAI,CAAC;QACH,OAAO,MAAM,0BAA0B,CAAC,EAAE,GAAG,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC,CAAC;IAChG,CAAC;YAAS,CAAC;QACT,MAAM,EAAE,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACzD,CAAC;AACH,CAAC;AAED,SAAS,sBAAsB,CAC7B,MAAqC,EACrC,UAAyC;IAEzC,MAAM,OAAO,GAAG;QACd,OAAO,EAAE,WAAW;QACpB,cAAc,EAAE,UAAU,CAAC,IAAI;QAC/B,UAAU,EAAE,UAAU,CAAC,OAAO;KAC/B,CAAC;IACF,IAAI,UAAU,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAChC,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,gDAAgD,CAAC,CAAC;IAC1E,CAAC;SAAM,IAAI,UAAU,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QACzC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,gDAAgD,CAAC,CAAC;IACzE,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,gDAAgD,CAAC,CAAC;IACzE,CAAC;AACH,CAAC;AAED,SAAS,uBAAuB,CAAC,OAAqB,EAAE,MAAqC;IAC3F,MAAM,eAAe,GAA4B;QAC/C,sBAAsB,EAAE,OAAO,CAAC,sBAAsB,CAAC,IAAI,CAAC,OAAO,CAAC;QACpE,gBAAgB,EAAE,OAAO,CAAC,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC;QACxD,kBAAkB,EAAE,OAAO,CAAC,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC;KAC7D,CAAC;IACF,MAAM,iBAAiB,GAAmD;QACxE,sBAAsB,EAAE,IAAI,GAAG,EAAE;QACjC,gBAAgB,EAAE,IAAI,GAAG,EAAE;QAC3B,kBAAkB,EAAE,IAAI,GAAG,EAAE;KAC9B,CAAC;IACF,MAAM,kBAAkB,GAAG,CAAC,SAAoC,EAAE,UAAkB,EAAQ,EAAE;QAC5F,MAAM,SAAS,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;QAC/C,IAAI,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC;YAAE,OAAO;QACtC,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAC1B,IAAI,CAAC;YACH,MAAM,CAAC,IAAI,CACT,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,EAC/C,kDAAkD,CACnD,CAAC;QACJ,CAAC;QAAC,MAAM,CAAC;YACP,uEAAuE;QACzE,CAAC;IACH,CAAC,CAAC;IACF,MAAM,aAAa,GAA4B;QAC7C,gBAAgB,CAAC,UAAU;YACzB,kBAAkB,CAAC,kBAAkB,EAAE,UAAU,CAAC,CAAC;QACrD,CAAC;QACD,sBAAsB,CAAC,QAAQ;YAC7B,kBAAkB,CAAC,wBAAwB,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;QAC5D,CAAC;QACD,kBAAkB,CAAC,UAAU;YAC3B,kBAAkB,CAAC,oBAAoB,EAAE,UAAU,CAAC,CAAC;QACvD,CAAC;KACF,CAAC;IAEF,IAAI,CAAC;QACH,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;IACxC,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC;QACxC,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,0CAA0C,CAC9D,OAAqB,EACrB,QAAgB,EAChB,MAAqC;IAErC,MAAM,QAAQ,GAAG,MAAM,2BAA2B,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACtE,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,OAAO,CAAC,wBAAwB,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAElF,uBAAuB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAEzC,KAAK,MAAM,UAAU,IAAI,QAAQ,CAAC,WAAW;QAAE,sBAAsB,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAC1F,KAAK,MAAM,cAAc,IAAI,QAAQ,CAAC,cAAc,CAAC,aAAa,EAAE,CAAC,MAAM,EAAE,CAAC;QAC5E,MAAM,CAAC,KAAK,CACV,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,cAAc,CAAC,KAAK,EAAE,EACrD,mDAAmD,CACpD,CAAC;IACJ,CAAC;IACD,MAAM,CAAC,IAAI,CACT,EAAE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,EACrC,4DAA4D,CAC7D,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tick hourly and let pi decide when a fetch is actually due: pi treats stored
|
|
3
|
+
* catalogs as fresh for REMOTE_CATALOG_REFRESH_INTERVAL_MS (4h) and skips the
|
|
4
|
+
* network entirely on an unforced refresh inside that window. Ticking at
|
|
5
|
+
* exactly 4h would land a few seconds short of the window every time, because
|
|
6
|
+
* pi stamps `checkedAt` only after a fetch completes, so every other tick would
|
|
7
|
+
* be skipped and the real cadence would be ~8h. A shorter interval removes that
|
|
8
|
+
* off-by-one-latency skip, tolerates clock skew, and costs nothing when the
|
|
9
|
+
* cache is fresh, because scheduled runs never set `force`.
|
|
10
|
+
*/
|
|
11
|
+
const DEFAULT_INTERVAL_MS = 60 * 60 * 1000;
|
|
12
|
+
/** Give the daemon a moment to finish startup before the first network refresh. */
|
|
13
|
+
const DEFAULT_INITIAL_DELAY_MS = 15_000;
|
|
14
|
+
/**
|
|
15
|
+
* Bound every catalog refresh so a stalled provider fetch cannot run forever.
|
|
16
|
+
* One run covers every refreshable provider under a single signal, so this is a
|
|
17
|
+
* whole-cycle budget for a background job, not pi's 15s startup budget.
|
|
18
|
+
*/
|
|
19
|
+
const DEFAULT_TIMEOUT_MS = 60_000;
|
|
20
|
+
/**
|
|
21
|
+
* Wait this long before the single follow-up attempt that a timed-out or failed
|
|
22
|
+
* run earns, so a transient network problem does not cost a whole interval.
|
|
23
|
+
*/
|
|
24
|
+
const DEFAULT_RETRY_DELAY_MS = 5 * 60 * 1000;
|
|
25
|
+
/** A queued follow-up must keep the strongest mode requested while a run was in flight. */
|
|
26
|
+
const strongestMode = (left, right) => left === "forced" || right === "forced" ? "forced" : "scheduled";
|
|
27
|
+
const noopLogger = {
|
|
28
|
+
info() { },
|
|
29
|
+
warn() { },
|
|
30
|
+
error() { },
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Refreshes provider model catalogs over the network on a background schedule.
|
|
34
|
+
*
|
|
35
|
+
* The shared ModelRuntime is constructed offline (see authService.ts), so its
|
|
36
|
+
* own refreshes never touch the network and stay fast on request paths. This
|
|
37
|
+
* refresher is the single place that deliberately performs network refreshes —
|
|
38
|
+
* bounded by an abort timeout, serialized through one in-flight run, stopped by
|
|
39
|
+
* `dispose()` even mid-flight, and off any request path. `requestRefresh()`
|
|
40
|
+
* additionally asks for a prompt forced refresh after events that change what
|
|
41
|
+
* should be listed, such as provider logins, where the cached catalog is known
|
|
42
|
+
* to be wrong.
|
|
43
|
+
*
|
|
44
|
+
* When the operator asked for offline behavior (`PI_OFFLINE` / `PI_WEB_OFFLINE`),
|
|
45
|
+
* the refresher performs no network I/O at all and the cached catalogs in
|
|
46
|
+
* models-store.json are used as they are.
|
|
47
|
+
*/
|
|
48
|
+
export class ModelCatalogRefresher {
|
|
49
|
+
constructor(options) {
|
|
50
|
+
this.started = false;
|
|
51
|
+
this.disposed = false;
|
|
52
|
+
/**
|
|
53
|
+
* Aborted by `dispose()` so a refresh already in flight stops with the
|
|
54
|
+
* refresher instead of holding its fetch open for the rest of the timeout
|
|
55
|
+
* budget, which would delay daemon shutdown.
|
|
56
|
+
*/
|
|
57
|
+
this.lifetime = new AbortController();
|
|
58
|
+
this.runtime = options.runtime;
|
|
59
|
+
this.logger = options.logger ?? noopLogger;
|
|
60
|
+
this.offline = options.offline ?? false;
|
|
61
|
+
this.intervalMs = options.intervalMs ?? DEFAULT_INTERVAL_MS;
|
|
62
|
+
this.initialDelayMs = options.initialDelayMs ?? DEFAULT_INITIAL_DELAY_MS;
|
|
63
|
+
this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
64
|
+
this.retryDelayMs = options.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS;
|
|
65
|
+
}
|
|
66
|
+
/** Idempotent: a second call keeps the timers the first call installed. */
|
|
67
|
+
start() {
|
|
68
|
+
if (this.disposed || this.started)
|
|
69
|
+
return;
|
|
70
|
+
this.started = true;
|
|
71
|
+
if (this.offline) {
|
|
72
|
+
this.logger.info({}, "offline mode is enabled; skipping background model catalog refreshes");
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
this.initialTimer = setTimeout(() => { this.queueRefresh("scheduled"); }, this.initialDelayMs);
|
|
76
|
+
this.initialTimer.unref();
|
|
77
|
+
this.intervalTimer = setInterval(() => { this.queueRefresh("scheduled"); }, this.intervalMs);
|
|
78
|
+
this.intervalTimer.unref();
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Ask for an immediate refresh that bypasses pi's freshness gate, for callers
|
|
82
|
+
* that know the cached catalog is stale (auth changes). Coalesces with
|
|
83
|
+
* concurrent and overlapping requests, and is a no-op in offline mode so auth
|
|
84
|
+
* changes never trigger network I/O either.
|
|
85
|
+
*/
|
|
86
|
+
requestRefresh() {
|
|
87
|
+
this.queueRefresh("forced");
|
|
88
|
+
}
|
|
89
|
+
/** Terminal: stops the schedule, drops any queued follow-up, and aborts an in-flight run. */
|
|
90
|
+
dispose() {
|
|
91
|
+
this.disposed = true;
|
|
92
|
+
if (this.initialTimer !== undefined)
|
|
93
|
+
clearTimeout(this.initialTimer);
|
|
94
|
+
if (this.intervalTimer !== undefined)
|
|
95
|
+
clearInterval(this.intervalTimer);
|
|
96
|
+
this.clearRetryTimer();
|
|
97
|
+
this.lifetime.abort();
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Single entry point for every refresh trigger. Only one run is ever in
|
|
101
|
+
* flight; overlapping requests collapse into one follow-up that keeps the
|
|
102
|
+
* strongest mode asked for, so a forced request is never downgraded.
|
|
103
|
+
*/
|
|
104
|
+
queueRefresh(mode, isRetry = false) {
|
|
105
|
+
if (this.disposed || this.offline)
|
|
106
|
+
return;
|
|
107
|
+
// Any fresh trigger supersedes a pending retry, which keeps retries from
|
|
108
|
+
// stacking up behind normal activity.
|
|
109
|
+
if (!isRetry)
|
|
110
|
+
this.clearRetryTimer();
|
|
111
|
+
if (this.inflight !== undefined) {
|
|
112
|
+
this.queuedMode = strongestMode(this.queuedMode, mode);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
// runCycle() reports every failure through the logger and never rejects.
|
|
116
|
+
this.inflight = this.runCycle(mode, isRetry);
|
|
117
|
+
}
|
|
118
|
+
/** One run plus the follow-up it leads to: a queued request, a single retry, or nothing. */
|
|
119
|
+
async runCycle(mode, isRetry) {
|
|
120
|
+
const outcome = await this.run(mode);
|
|
121
|
+
this.inflight = undefined;
|
|
122
|
+
const queued = this.queuedMode;
|
|
123
|
+
this.queuedMode = undefined;
|
|
124
|
+
if (queued !== undefined) {
|
|
125
|
+
// A request that arrived during the run replaces any retry this run would
|
|
126
|
+
// have earned; it runs now instead.
|
|
127
|
+
this.queueRefresh(queued);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
// Only a first attempt earns a retry, so a failing provider can never turn
|
|
131
|
+
// into a retry loop.
|
|
132
|
+
if (outcome === "incomplete" && !isRetry)
|
|
133
|
+
this.scheduleRetry(mode);
|
|
134
|
+
}
|
|
135
|
+
scheduleRetry(mode) {
|
|
136
|
+
if (this.disposed)
|
|
137
|
+
return;
|
|
138
|
+
this.logger.info({ retryDelayMs: this.retryDelayMs, mode }, "scheduling one model catalog refresh retry");
|
|
139
|
+
this.retryTimer = setTimeout(() => {
|
|
140
|
+
this.retryTimer = undefined;
|
|
141
|
+
this.queueRefresh(mode, true);
|
|
142
|
+
}, this.retryDelayMs);
|
|
143
|
+
this.retryTimer.unref();
|
|
144
|
+
}
|
|
145
|
+
clearRetryTimer() {
|
|
146
|
+
if (this.retryTimer === undefined)
|
|
147
|
+
return;
|
|
148
|
+
clearTimeout(this.retryTimer);
|
|
149
|
+
this.retryTimer = undefined;
|
|
150
|
+
}
|
|
151
|
+
async run(mode) {
|
|
152
|
+
try {
|
|
153
|
+
const result = await this.runtime.refresh({
|
|
154
|
+
allowNetwork: true,
|
|
155
|
+
force: mode === "forced",
|
|
156
|
+
signal: AbortSignal.any([this.lifetime.signal, AbortSignal.timeout(this.timeoutMs)]),
|
|
157
|
+
});
|
|
158
|
+
if (result.aborted && this.lifetime.signal.aborted) {
|
|
159
|
+
this.logStoppedByDispose();
|
|
160
|
+
return "incomplete";
|
|
161
|
+
}
|
|
162
|
+
if (result.aborted) {
|
|
163
|
+
this.logger.warn({ timeoutMs: this.timeoutMs }, "model catalog refresh timed out; keeping cached catalogs");
|
|
164
|
+
}
|
|
165
|
+
if (result.errors.size > 0) {
|
|
166
|
+
const providers = [...result.errors.entries()].map(([providerId, error]) => `${providerId}: ${error.message}`);
|
|
167
|
+
this.logger.warn({ providers }, "model catalog refresh failed for some providers; keeping cached catalogs");
|
|
168
|
+
}
|
|
169
|
+
return result.aborted || result.errors.size > 0 ? "incomplete" : "complete";
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
if (this.lifetime.signal.aborted)
|
|
173
|
+
this.logStoppedByDispose();
|
|
174
|
+
else
|
|
175
|
+
this.logger.error({ err: error }, "model catalog refresh failed; keeping cached catalogs");
|
|
176
|
+
return "incomplete";
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/** A shutdown abort is expected, so it must not be reported as a timeout or a fault. */
|
|
180
|
+
logStoppedByDispose() {
|
|
181
|
+
this.logger.info({}, "model catalog refresh aborted by dispose; keeping cached catalogs");
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=modelCatalogRefresher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modelCatalogRefresher.js","sourceRoot":"","sources":["../../../src/server/sessions/modelCatalogRefresher.ts"],"names":[],"mappings":"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"}
|