@jmfederico/pi-web 1.202607.0 → 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.
Files changed (119) hide show
  1. package/README.md +12 -9
  2. package/dist/cli.js +305 -322
  3. package/dist/cli.js.map +1 -1
  4. package/dist/client/assets/{CodeViewer-tXF3UTsY.js → CodeViewer-B4M13447.js} +1 -1
  5. package/dist/client/assets/{TerminalPanel-D_ihiWAt.js → TerminalPanel-Bmvj-Ecn.js} +1 -1
  6. package/dist/client/assets/{UnifiedDiffViewer-BziirXBJ.js → UnifiedDiffViewer-C0hygwRo.js} +1 -1
  7. package/dist/client/assets/index-TvPsRyS1.js +3545 -0
  8. package/dist/client/index.html +6 -6
  9. package/dist/client/manifest.webmanifest +4 -4
  10. package/dist/config.js +157 -2
  11. package/dist/config.js.map +1 -1
  12. package/dist/nativeServices/serviceDoctor.js +480 -0
  13. package/dist/nativeServices/serviceDoctor.js.map +1 -0
  14. package/dist/nativeServices/serviceInstall.js +35 -0
  15. package/dist/nativeServices/serviceInstall.js.map +1 -0
  16. package/dist/nativeServices/servicePlan.js +358 -0
  17. package/dist/nativeServices/servicePlan.js.map +1 -0
  18. package/dist/nativeServices/serviceProbe.js +447 -0
  19. package/dist/nativeServices/serviceProbe.js.map +1 -0
  20. package/dist/nativeServices/serviceRendering.js +122 -0
  21. package/dist/nativeServices/serviceRendering.js.map +1 -0
  22. package/dist/pi-web-plugins/updates/pi-web-plugin.js +12 -0
  23. package/dist/plugin-api.d.ts +2 -0
  24. package/dist/server/activeAgentProfileProvider.js +25 -0
  25. package/dist/server/activeAgentProfileProvider.js.map +1 -0
  26. package/dist/server/app.js +57 -16
  27. package/dist/server/app.js.map +1 -1
  28. package/dist/server/configRoutes.js +43 -9
  29. package/dist/server/configRoutes.js.map +1 -1
  30. package/dist/server/diagnostics/nodePtyNativeModule.js +36 -0
  31. package/dist/server/diagnostics/nodePtyNativeModule.js.map +1 -0
  32. package/dist/server/git/gitService.js +176 -5
  33. package/dist/server/git/gitService.js.map +1 -1
  34. package/dist/server/machines/machinePluginProxyRoutes.js +7 -4
  35. package/dist/server/machines/machinePluginProxyRoutes.js.map +1 -1
  36. package/dist/server/machines/machineProxyRoutes.js +16 -4
  37. package/dist/server/machines/machineProxyRoutes.js.map +1 -1
  38. package/dist/server/machines/machineRoutes.js +1 -1
  39. package/dist/server/machines/machineRoutes.js.map +1 -1
  40. package/dist/server/machines/machineService.js +2 -2
  41. package/dist/server/machines/machineService.js.map +1 -1
  42. package/dist/server/piPackageRoutes.js +7 -3
  43. package/dist/server/piPackageRoutes.js.map +1 -1
  44. package/dist/server/piPackageService.js +34 -2
  45. package/dist/server/piPackageService.js.map +1 -1
  46. package/dist/server/piWebPluginService.js +48 -12
  47. package/dist/server/piWebPluginService.js.map +1 -1
  48. package/dist/server/piWebReleaseLookupCache.js +37 -0
  49. package/dist/server/piWebReleaseLookupCache.js.map +1 -0
  50. package/dist/server/piWebStatus.js +38 -39
  51. package/dist/server/piWebStatus.js.map +1 -1
  52. package/dist/server/piWebStatusCache.js +19 -6
  53. package/dist/server/piWebStatusCache.js.map +1 -1
  54. package/dist/server/realtime/sessionEventHub.js +37 -7
  55. package/dist/server/realtime/sessionEventHub.js.map +1 -1
  56. package/dist/server/sessiond/sessionDaemonStartup.js +49 -0
  57. package/dist/server/sessiond/sessionDaemonStartup.js.map +1 -0
  58. package/dist/server/sessiond.js +128 -62
  59. package/dist/server/sessiond.js.map +1 -1
  60. package/dist/server/sessions/authProviderOptions.js +30 -32
  61. package/dist/server/sessions/authProviderOptions.js.map +1 -1
  62. package/dist/server/sessions/authRoutes.js +14 -4
  63. package/dist/server/sessions/authRoutes.js.map +1 -1
  64. package/dist/server/sessions/authService.js +140 -29
  65. package/dist/server/sessions/authService.js.map +1 -1
  66. package/dist/server/sessions/globalProviderPolicy.js +98 -0
  67. package/dist/server/sessions/globalProviderPolicy.js.map +1 -0
  68. package/dist/server/sessions/modelCatalogRefresher.js +184 -0
  69. package/dist/server/sessions/modelCatalogRefresher.js.map +1 -0
  70. package/dist/server/sessions/oauthLoginFlowService.js +176 -53
  71. package/dist/server/sessions/oauthLoginFlowService.js.map +1 -1
  72. package/dist/server/sessions/piSessionManagerGateway.js +22 -21
  73. package/dist/server/sessions/piSessionManagerGateway.js.map +1 -1
  74. package/dist/server/sessions/piSessionService.js +936 -111
  75. package/dist/server/sessions/piSessionService.js.map +1 -1
  76. package/dist/server/sessions/plainTextTheme.js +112 -0
  77. package/dist/server/sessions/plainTextTheme.js.map +1 -0
  78. package/dist/server/sessions/sessionArchiveMigration.js +590 -0
  79. package/dist/server/sessions/sessionArchiveMigration.js.map +1 -0
  80. package/dist/server/sessions/sessionArchiveStore.js +7 -4
  81. package/dist/server/sessions/sessionArchiveStore.js.map +1 -1
  82. package/dist/server/sessions/sessionCommandService.js +68 -9
  83. package/dist/server/sessions/sessionCommandService.js.map +1 -1
  84. package/dist/server/sessions/sessionNotificationStore.js +484 -0
  85. package/dist/server/sessions/sessionNotificationStore.js.map +1 -0
  86. package/dist/server/sessions/sessionRoutes.js +202 -2
  87. package/dist/server/sessions/sessionRoutes.js.map +1 -1
  88. package/dist/server/sessions/sessionService.js +2 -0
  89. package/dist/server/sessions/sessionService.js.map +1 -0
  90. package/dist/server/sessions/sessionTreeProjection.js +250 -0
  91. package/dist/server/sessions/sessionTreeProjection.js.map +1 -0
  92. package/dist/server/sessions/sessionUnreadStore.js +520 -0
  93. package/dist/server/sessions/sessionUnreadStore.js.map +1 -0
  94. package/dist/server/sessions/spawnSessionTool.js +2 -2
  95. package/dist/server/sessions/spawnSessionTool.js.map +1 -1
  96. package/dist/server/sessions/spawnSubsessionTool.js +69 -31
  97. package/dist/server/sessions/spawnSubsessionTool.js.map +1 -1
  98. package/dist/server/terminals/terminalService.js +10 -4
  99. package/dist/server/terminals/terminalService.js.map +1 -1
  100. package/dist/sessiond/activeAgentProfile.js +31 -0
  101. package/dist/sessiond/activeAgentProfile.js.map +1 -0
  102. package/dist/sessiond/sessionDaemonClient.js +38 -0
  103. package/dist/sessiond/sessionDaemonClient.js.map +1 -1
  104. package/dist/shared/activeAgentProfile.js +76 -0
  105. package/dist/shared/activeAgentProfile.js.map +1 -0
  106. package/dist/shared/apiTypes.d.ts +256 -4
  107. package/dist/shared/apiTypes.js +12 -0
  108. package/dist/shared/apiTypes.js.map +1 -1
  109. package/dist/shared/capabilities.js +11 -0
  110. package/dist/shared/capabilities.js.map +1 -1
  111. package/dist/shared/federatedRoutes.js +12 -0
  112. package/dist/shared/federatedRoutes.js.map +1 -1
  113. package/dist/shared/piWebStatusParsing.js +6 -0
  114. package/dist/shared/piWebStatusParsing.js.map +1 -1
  115. package/docs/config.md +85 -12
  116. package/docs/plugins.md +29 -11
  117. package/install.sh +1 -1
  118. package/package.json +9 -8
  119. package/dist/client/assets/index-Brfk1lwl.js +0 -2950
@@ -1,11 +1,78 @@
1
- import { AuthStorage, ModelRegistry } from "@earendil-works/pi-coding-agent";
1
+ import { join } from "node:path";
2
+ import { ModelRuntime } from "@earendil-works/pi-coding-agent";
2
3
  import { getLoginProviderOptions, getLogoutProviderOptions } from "./authProviderOptions.js";
3
4
  import { OAuthLoginFlowService } from "./oauthLoginFlowService.js";
5
+ const noopLogger = { error() { } };
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({
60
+ authPath: join(agentDir, "auth.json"),
61
+ modelsPath: join(agentDir, "models.json"),
62
+ });
63
+ }
4
64
  export class AuthService {
5
- constructor(deps = {}) {
65
+ constructor(runtime, authFlows, logger) {
6
66
  this.listeners = new Set();
7
- this.modelRegistry = deps.modelRegistry ?? ModelRegistry.create(AuthStorage.create());
8
- this.authFlows = deps.authFlows ?? new OAuthLoginFlowService();
67
+ this.runtime = runtime;
68
+ this.authFlows = authFlows;
69
+ this.logger = logger;
70
+ }
71
+ static async create(deps = {}) {
72
+ const runtime = deps.runtime ?? (deps.agentDir === undefined ? await createOfflineModelRuntime({}) : await createModelRuntimeForAgentDir(deps.agentDir));
73
+ const logger = deps.logger ?? noopLogger;
74
+ const authFlows = deps.authFlows ?? new OAuthLoginFlowService({ logger });
75
+ return new AuthService(runtime, authFlows, logger);
9
76
  }
10
77
  subscribe(listener) {
11
78
  this.listeners.add(listener);
@@ -17,32 +84,58 @@ export class AuthService {
17
84
  this.authFlows.dispose();
18
85
  this.listeners.clear();
19
86
  }
20
- authProviders(mode, authType) {
21
- this.modelRegistry.refresh();
22
- const providers = mode === "logout" ? getLogoutProviderOptions(this.modelRegistry) : getLoginProviderOptions(this.modelRegistry, authType);
87
+ async authProviders(mode, authType) {
88
+ await this.runtime.refresh();
89
+ const providers = mode === "logout" ? await getLogoutProviderOptions(this.runtime) : getLoginProviderOptions(this.runtime, authType);
23
90
  return { providers };
24
91
  }
25
- saveApiKey(providerId, key) {
92
+ async saveApiKey(providerId, key) {
26
93
  if (key.trim() === "")
27
94
  throw new Error("API key is required");
28
- this.modelRegistry.authStorage.set(providerId, { type: "api_key", key });
29
- this.refreshAuthState();
95
+ const provider = await this.requireApiKeyLoginProvider(providerId);
96
+ let promptAttempted = false;
97
+ const interaction = {
98
+ prompt: (prompt) => {
99
+ if (promptAttempted) {
100
+ throw new Error(`${provider.name} requires interactive setup; use Pi's generic /login flow`);
101
+ }
102
+ promptAttempted = true;
103
+ if (prompt.signal?.aborted === true)
104
+ throw new Error("Login cancelled");
105
+ if (prompt.type !== "secret") {
106
+ throw new Error(`${provider.name} requires interactive setup; use Pi's generic /login flow`);
107
+ }
108
+ return Promise.resolve(key);
109
+ },
110
+ notify: () => undefined,
111
+ };
112
+ await this.runtime.login(providerId, "api_key", interaction);
113
+ await this.emit({}, { operation: "login", providerId, authType: "api_key" });
30
114
  return { accepted: true };
31
115
  }
32
- logoutProvider(providerId) {
33
- this.modelRegistry.authStorage.logout(providerId);
34
- this.refreshAuthState({ removedProviderId: providerId });
116
+ async logoutProvider(providerId) {
117
+ await this.runtime.logout(providerId);
118
+ await this.emit({ removedProviderId: providerId }, { operation: "logout", providerId });
35
119
  return { accepted: true };
36
120
  }
37
- startOAuthLogin(providerId) {
38
- const provider = this.requireOAuthLoginProvider(providerId);
121
+ async startApiKeyLogin(providerId) {
122
+ const provider = await this.requireApiKeyLoginProvider(providerId);
39
123
  return this.authFlows.start({
40
124
  providerId,
41
125
  providerName: provider.name,
42
- authStorage: this.modelRegistry.authStorage,
43
- onComplete: () => {
44
- this.refreshAuthState();
45
- },
126
+ runtime: this.runtime,
127
+ authType: "api_key",
128
+ onComplete: () => this.emit({}, { operation: "login", providerId, authType: "api_key" }),
129
+ });
130
+ }
131
+ async startOAuthLogin(providerId) {
132
+ const provider = await this.requireOAuthLoginProvider(providerId);
133
+ return this.authFlows.start({
134
+ providerId,
135
+ providerName: provider.name,
136
+ runtime: this.runtime,
137
+ authType: "oauth",
138
+ onComplete: () => this.emit({}, { operation: "login", providerId, authType: "oauth" }),
46
139
  });
47
140
  }
48
141
  oauthFlow(flowId) {
@@ -54,18 +147,36 @@ export class AuthService {
54
147
  cancelOAuthFlow(flowId) {
55
148
  return this.authFlows.cancel(flowId);
56
149
  }
57
- refreshAuthState(change = {}) {
58
- this.modelRegistry.authStorage.reload();
59
- this.modelRegistry.refresh();
60
- this.emit(change);
150
+ async emit(change, context) {
151
+ const results = await Promise.allSettled([...this.listeners].map(async (listener) => listener(change)));
152
+ for (const result of results) {
153
+ if (result.status === "rejected") {
154
+ this.logErrorNoThrow({ err: result.reason, ...context }, "auth-change listener failed");
155
+ }
156
+ }
157
+ }
158
+ logErrorNoThrow(details, message) {
159
+ try {
160
+ this.logger.error(details, message);
161
+ }
162
+ catch {
163
+ // A diagnostic failure cannot turn an already-committed auth mutation into an API failure.
164
+ }
61
165
  }
62
- emit(change) {
63
- for (const listener of this.listeners)
64
- listener(change);
166
+ async requireApiKeyLoginProvider(providerId) {
167
+ await this.runtime.refresh();
168
+ const provider = getLoginProviderOptions(this.runtime, "api_key").find((option) => option.id === providerId);
169
+ if (provider !== undefined)
170
+ return provider;
171
+ const knownProvider = this.runtime.getProviders().find((option) => option.id === providerId);
172
+ if (knownProvider !== undefined) {
173
+ throw new Error(`${knownProvider.name} does not support interactive API-key setup`);
174
+ }
175
+ throw new Error(`API key provider not found: ${providerId}`);
65
176
  }
66
- requireOAuthLoginProvider(providerId) {
67
- this.modelRegistry.refresh();
68
- const provider = getLoginProviderOptions(this.modelRegistry, "oauth").find((option) => option.id === providerId);
177
+ async requireOAuthLoginProvider(providerId) {
178
+ await this.runtime.refresh();
179
+ const provider = getLoginProviderOptions(this.runtime, "oauth").find((option) => option.id === providerId);
69
180
  if (provider === undefined)
70
181
  throw new Error(`OAuth provider not found: ${providerId}`);
71
182
  return provider;
@@ -1 +1 @@
1
- {"version":3,"file":"authService.js","sourceRoot":"","sources":["../../../src/server/sessions/authService.ts"],"names":[],"mappings":"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"}
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":"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"}
@@ -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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"}