@xopcai/xopc 0.0.8 → 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/extensions/telegram/src/plugin.js +1 -1
- package/dist/extensions/telegram/src/routing-integration.js +2 -2
- package/dist/extensions/weixin/src/plugin.js +1 -1
- package/dist/gateway/static/root/assets/{agents-BSNzJWbQ.js → agents-BdC4Y-HX.js} +2 -2
- package/dist/gateway/static/root/assets/agents-BdC4Y-HX.js.map +1 -0
- package/dist/gateway/static/root/assets/{apps-page-BKk9SB4D.js → apps-page-C-oaSHkm.js} +2 -2
- package/dist/gateway/static/root/assets/{apps-page-BKk9SB4D.js.map → apps-page-C-oaSHkm.js.map} +1 -1
- package/dist/gateway/static/root/assets/attachment-load-BDDlItdE.js +1 -0
- package/dist/gateway/static/root/assets/{channels-settings-_J6cQN6G.js → channels-settings-BqEUppPO.js} +2 -2
- package/dist/gateway/static/root/assets/{channels-settings-_J6cQN6G.js.map → channels-settings-BqEUppPO.js.map} +1 -1
- package/dist/gateway/static/root/assets/{chat-agents-api-DPb_0O8M.js → chat-agents-api-BhqjQ7iL.js} +2 -2
- package/dist/gateway/static/root/assets/{chat-agents-api-DPb_0O8M.js.map → chat-agents-api-BhqjQ7iL.js.map} +1 -1
- package/dist/gateway/static/root/assets/{cron-page-BUJOuuKX.js → cron-page-Cli49RKR.js} +2 -2
- package/dist/gateway/static/root/assets/{cron-page-BUJOuuKX.js.map → cron-page-Cli49RKR.js.map} +1 -1
- package/dist/gateway/static/root/assets/{cron-utils-Cn0YVg8x.js → cron-utils-Dkj-Ldpf.js} +2 -2
- package/dist/gateway/static/root/assets/{cron-utils-Cn0YVg8x.js.map → cron-utils-Dkj-Ldpf.js.map} +1 -1
- package/dist/gateway/static/root/assets/{electron-env-D9bm1FIu.js → electron-env-BDtJw9AY.js} +2 -2
- package/dist/gateway/static/root/assets/{electron-env-D9bm1FIu.js.map → electron-env-BDtJw9AY.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-DTz4O5Ua.js → extension-debug-page-BMcZlaxF.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-debug-page-DTz4O5Ua.js.map → extension-debug-page-BMcZlaxF.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-iframe-host-Cs1Kde9o.js → extension-iframe-host-D5HEF0KR.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-iframe-host-Cs1Kde9o.js.map → extension-iframe-host-D5HEF0KR.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-G52iX0Bo.js → extension-page-CXdCSSPl.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-page-G52iX0Bo.js.map → extension-page-CXdCSSPl.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-provider-CO2jxBA9.js → extension-provider-DZCZgQE2.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-provider-CO2jxBA9.js.map → extension-provider-DZCZgQE2.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-D9Ul8uSt.js → extension-settings-page-CX6STpx3.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-settings-page-D9Ul8uSt.js.map → extension-settings-page-CX6STpx3.js.map} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-swr-Bc8SVD15.js → gateway-config-swr-Cph02QZn.js} +2 -2
- package/dist/gateway/static/root/assets/{gateway-config-swr-Bc8SVD15.js.map → gateway-config-swr-Cph02QZn.js.map} +1 -1
- package/dist/gateway/static/root/assets/index-Bty3m0mS.css +2 -0
- package/dist/gateway/static/root/assets/{index-BXUJbteW.js → index-iTUyfzNr.js} +10 -10
- package/dist/gateway/static/root/assets/index-iTUyfzNr.js.map +1 -0
- package/dist/gateway/static/root/assets/{logs-page-5V25JkQY.js → logs-page-B9O5l3I8.js} +2 -2
- package/dist/gateway/static/root/assets/{logs-page-5V25JkQY.js.map → logs-page-B9O5l3I8.js.map} +1 -1
- package/dist/gateway/static/root/assets/{model-selector-he3aQfme.js → model-selector-BLiY_O25.js} +2 -2
- package/dist/gateway/static/root/assets/{model-selector-he3aQfme.js.map → model-selector-BLiY_O25.js.map} +1 -1
- package/dist/gateway/static/root/assets/page-header-store-BFpnFTed.js +2 -0
- package/dist/gateway/static/root/assets/{page-header-store-DJHD9Ean.js.map → page-header-store-BFpnFTed.js.map} +1 -1
- package/dist/gateway/static/root/assets/{session-api-n-4O5d9U.js → session-api-DEhQXWJg.js} +2 -2
- package/dist/gateway/static/root/assets/{session-api-n-4O5d9U.js.map → session-api-DEhQXWJg.js.map} +1 -1
- package/dist/gateway/static/root/assets/{session-working-directory-control-B6dHLvbr.js → session-working-directory-control-DKOtWs3-.js} +2 -2
- package/dist/gateway/static/root/assets/{session-working-directory-control-B6dHLvbr.js.map → session-working-directory-control-DKOtWs3-.js.map} +1 -1
- package/dist/gateway/static/root/assets/{sessions-page-rBUfTdm3.js → sessions-page-BYlWP1ep.js} +2 -2
- package/dist/gateway/static/root/assets/{sessions-page-rBUfTdm3.js.map → sessions-page-BYlWP1ep.js.map} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-B3QrJm-E.js → settings-page-oCnIavdg.js} +2 -2
- package/dist/gateway/static/root/assets/settings-page-oCnIavdg.js.map +1 -0
- package/dist/gateway/static/root/assets/{skill-api-vxtE8kI6.js → skill-api-DWrn8Az0.js} +2 -2
- package/dist/gateway/static/root/assets/{skill-api-vxtE8kI6.js.map → skill-api-DWrn8Az0.js.map} +1 -1
- package/dist/gateway/static/root/assets/{skills-page-D36_O2Ub.js → skills-page-C59WQpM1.js} +2 -2
- package/dist/gateway/static/root/assets/{skills-page-D36_O2Ub.js.map → skills-page-C59WQpM1.js.map} +1 -1
- package/dist/gateway/static/root/assets/{theme-store-CmiSsYBd.js → theme-store-CywXkKml.js} +2 -2
- package/dist/gateway/static/root/assets/{theme-store-CmiSsYBd.js.map → theme-store-CywXkKml.js.map} +1 -1
- package/dist/gateway/static/root/assets/url-D7yWllI8.js +2 -0
- package/dist/gateway/static/root/assets/url-D7yWllI8.js.map +1 -0
- package/dist/gateway/static/root/assets/{useTranslation-DYORQ7x6.js → useTranslation-CACj0DBJ.js} +2 -2
- package/dist/gateway/static/root/assets/{useTranslation-DYORQ7x6.js.map → useTranslation-CACj0DBJ.js.map} +1 -1
- package/dist/gateway/static/root/index.html +15 -15
- package/dist/package.js +1 -1
- package/dist/src/agent/agent-manager.d.ts +1 -0
- package/dist/src/agent/agent-manager.js +20 -12
- package/dist/src/agent/agent-manager.js.map +1 -1
- package/dist/src/agent/background-review/run-background-review.js +2 -0
- package/dist/src/agent/background-review/run-background-review.js.map +1 -1
- package/dist/src/agent/child-agent-factory.js +2 -0
- package/dist/src/agent/child-agent-factory.js.map +1 -1
- package/dist/src/agent/context/expand-at-file-mentions.d.ts +4 -0
- package/dist/src/agent/context/expand-at-file-mentions.js +69 -0
- package/dist/src/agent/context/expand-at-file-mentions.js.map +1 -0
- package/dist/src/agent/context/workspace-seed.js +1 -1
- package/dist/src/agent/image/understanding/pi-ai-provider.js.map +1 -1
- package/dist/src/agent/ipc/bus.js +1 -1
- package/dist/src/agent/ipc/inbox.js +1 -1
- package/dist/src/agent/ipc/socket.js +1 -1
- package/dist/src/agent/memory/compaction.d.ts +1 -1
- package/dist/src/agent/memory/compaction.js +38 -11
- package/dist/src/agent/memory/compaction.js.map +1 -1
- package/dist/src/agent/messaging/command-handler.d.ts +13 -0
- package/dist/src/agent/messaging/command-handler.js +14 -2
- package/dist/src/agent/messaging/command-handler.js.map +1 -1
- package/dist/src/agent/models/manager.js +1 -1
- package/dist/src/agent/orchestration/agent-orchestrator.js +6 -1
- package/dist/src/agent/orchestration/agent-orchestrator.js.map +1 -1
- package/dist/src/agent/prompt/service-prompt-builder.js +1 -1
- package/dist/src/agent/service.d.ts +16 -1
- package/dist/src/agent/service.js +178 -20
- package/dist/src/agent/service.js.map +1 -1
- package/dist/src/agent/skills/format-skills-prompt.js.map +1 -1
- package/dist/src/agent/skills/index.js +1 -1
- package/dist/src/agent/skills/scanner.js +1 -1
- package/dist/src/agent/skills/skill-manage-ops.js +1 -1
- package/dist/src/agent/skills/skill-manage-ops.js.map +1 -1
- package/dist/src/agent/skills/skill-manager.js +1 -1
- package/dist/src/agent/tools/browser/tools.js.map +1 -1
- package/dist/src/agent/tools/factory.js +1 -1
- package/dist/src/agent/tools/image-tool.js.map +1 -1
- package/dist/src/agent/tools/send-media.js +1 -1
- package/dist/src/agent/tools/skill-manage-tool.js +1 -1
- package/dist/src/agent/tools/write.js +1 -1
- package/dist/src/auth/credentials.js +2 -2
- package/dist/src/auth/sync-provider-auth.js +1 -1
- package/dist/src/channels/attachments/inbound-persist.js +1 -1
- package/dist/src/channels/attachments/outbound-tts-persist.js +1 -1
- package/dist/src/channels/index.d.ts +1 -1
- package/dist/src/channels/index.js +2 -2
- package/dist/src/channels/pipeline.d.ts +8 -1
- package/dist/src/channels/pipeline.js +49 -4
- package/dist/src/channels/pipeline.js.map +1 -1
- package/dist/src/chat-commands/builtins/config.d.ts +4 -0
- package/dist/src/chat-commands/builtins/config.js +197 -0
- package/dist/src/chat-commands/builtins/config.js.map +1 -0
- package/dist/src/chat-commands/builtins/context.d.ts +4 -0
- package/dist/src/chat-commands/builtins/context.js +44 -0
- package/dist/src/chat-commands/builtins/context.js.map +1 -0
- package/dist/src/chat-commands/builtins/session.js +111 -0
- package/dist/src/chat-commands/builtins/session.js.map +1 -1
- package/dist/src/chat-commands/builtins/thinking.js +49 -21
- package/dist/src/chat-commands/builtins/thinking.js.map +1 -1
- package/dist/src/chat-commands/config-paths.d.ts +10 -0
- package/dist/src/chat-commands/config-paths.js +45 -0
- package/dist/src/chat-commands/config-paths.js.map +1 -0
- package/dist/src/chat-commands/config-value.d.ts +12 -0
- package/dist/src/chat-commands/config-value.js +53 -0
- package/dist/src/chat-commands/config-value.js.map +1 -0
- package/dist/src/chat-commands/context.d.ts +24 -1
- package/dist/src/chat-commands/context.js +41 -0
- package/dist/src/chat-commands/context.js.map +1 -1
- package/dist/src/chat-commands/index.d.ts +2 -0
- package/dist/src/chat-commands/index.js +5 -1
- package/dist/src/chat-commands/index.js.map +1 -1
- package/dist/src/chat-commands/types.d.ts +33 -1
- package/dist/src/cli/commands/agent/interactive.js +1 -1
- package/dist/src/cli/commands/agent/interactive.js.map +1 -1
- package/dist/src/cli/commands/agent.js +22 -10
- package/dist/src/cli/commands/agent.js.map +1 -1
- package/dist/src/cli/commands/auth.js.map +1 -1
- package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -1
- package/dist/src/cli/commands/extension.js +10 -0
- package/dist/src/cli/commands/extension.js.map +1 -1
- package/dist/src/cli/commands/init.js +3 -4
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/commands/session/utils.js.map +1 -1
- package/dist/src/cli/commands/update.d.ts +1 -0
- package/dist/src/cli/commands/update.js +171 -0
- package/dist/src/cli/commands/update.js.map +1 -0
- package/dist/src/cli/index.d.ts +1 -1
- package/dist/src/cli/index.js +3 -1
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/config/index.d.ts +1 -0
- package/dist/src/config/index.js +5 -4
- package/dist/src/config/index.js.map +1 -1
- package/dist/src/config/loader.js +1 -1
- package/dist/src/config/models-json.js +1 -1
- package/dist/src/config/paths.js.map +1 -1
- package/dist/src/config/profile.js +2 -2
- package/dist/src/config/runtime-overrides.d.ts +8 -0
- package/dist/src/config/runtime-overrides.js +40 -0
- package/dist/src/config/runtime-overrides.js.map +1 -0
- package/dist/src/config/schema.d.ts +35 -0
- package/dist/src/config/schema.js +19 -3
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/cron/executor.js +2 -2
- package/dist/src/cron/persistence.js +1 -1
- package/dist/src/cron/run-log-store.js +1 -1
- package/dist/src/extensions/health.js +1 -1
- package/dist/src/extensions/loader.d.ts +1 -1
- package/dist/src/extensions/loader.js +6 -9
- package/dist/src/extensions/loader.js.map +1 -1
- package/dist/src/extensions/lockfile.js +1 -1
- package/dist/src/extensions/sdk/index.js +6 -1
- package/dist/src/extensions/sdk/index.js.map +1 -0
- package/dist/src/gateway/agents-admin.js +2 -2
- package/dist/src/gateway/agents-admin.js.map +1 -1
- package/dist/src/gateway/hono/oauth.js +1 -1
- package/dist/src/gateway/hono/routes/config.js +1 -1
- package/dist/src/gateway/hono/routes/index.js +2 -0
- package/dist/src/gateway/hono/routes/index.js.map +1 -1
- package/dist/src/gateway/hono/routes/models.js +64 -11
- package/dist/src/gateway/hono/routes/models.js.map +1 -1
- package/dist/src/gateway/hono/routes/public-gateway.js +10 -0
- package/dist/src/gateway/hono/routes/public-gateway.js.map +1 -1
- package/dist/src/gateway/hono/routes/update.d.ts +3 -0
- package/dist/src/gateway/hono/routes/update.js +141 -0
- package/dist/src/gateway/hono/routes/update.js.map +1 -0
- package/dist/src/gateway/hono/routes/workspace.js +84 -4
- package/dist/src/gateway/hono/routes/workspace.js.map +1 -1
- package/dist/src/gateway/hono/sse.js +2 -2
- package/dist/src/gateway/service.d.ts +1 -0
- package/dist/src/gateway/service.js +16 -4
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/gateway/workspace-fs-file-list.d.ts +5 -0
- package/dist/src/gateway/workspace-fs-file-list.js +56 -0
- package/dist/src/gateway/workspace-fs-file-list.js.map +1 -0
- package/dist/src/gateway/workspace-heartbeat-path.js +1 -1
- package/dist/src/gateway/workspace-ripgrep.d.ts +5 -0
- package/dist/src/gateway/workspace-ripgrep.js +88 -4
- package/dist/src/gateway/workspace-ripgrep.js.map +1 -1
- package/dist/src/infra/update-channels.d.ts +14 -0
- package/dist/src/infra/update-channels.js +30 -0
- package/dist/src/infra/update-channels.js.map +1 -0
- package/dist/src/infra/update-check.d.ts +53 -0
- package/dist/src/infra/update-check.js +155 -0
- package/dist/src/infra/update-check.js.map +1 -0
- package/dist/src/infra/update-runner.d.ts +18 -0
- package/dist/src/infra/update-runner.js +112 -0
- package/dist/src/infra/update-runner.js.map +1 -0
- package/dist/src/infra/update-startup.d.ts +20 -0
- package/dist/src/infra/update-startup.js +246 -0
- package/dist/src/infra/update-startup.js.map +1 -0
- package/dist/src/providers/extension-stream-bridge.d.ts +3 -0
- package/dist/src/providers/extension-stream-bridge.js +239 -0
- package/dist/src/providers/extension-stream-bridge.js.map +1 -0
- package/dist/src/providers/index.d.ts +7 -2
- package/dist/src/providers/index.js +77 -14
- package/dist/src/providers/index.js.map +1 -1
- package/dist/src/providers/model-registry.js +1 -1
- package/dist/src/providers/plugin-registry.js +92 -87
- package/dist/src/providers/plugin-registry.js.map +1 -1
- package/dist/src/session/chat-export.d.ts +5 -0
- package/dist/src/session/chat-export.js +35 -0
- package/dist/src/session/chat-export.js.map +1 -0
- package/dist/src/session/config-store.js +1 -1
- package/dist/src/session/manager.d.ts +1 -1
- package/dist/src/session/manager.js +2 -2
- package/dist/src/session/manager.js.map +1 -1
- package/dist/src/session/session-title.js +1 -1
- package/dist/src/session/store.d.ts +1 -1
- package/dist/src/session/store.js +5 -5
- package/dist/src/session/store.js.map +1 -1
- package/dist/src/utils/logger/audit.js +1 -1
- package/dist/src/utils/logger/log-store.js +1 -1
- package/dist/src/utils/logger/rotation.js +1 -1
- package/dist/src/voice/tts/audio.js +1 -1
- package/package.json +2 -1
- package/dist/gateway/static/root/assets/agents-BSNzJWbQ.js.map +0 -1
- package/dist/gateway/static/root/assets/attachment-load-DXcJLSWT.js +0 -1
- package/dist/gateway/static/root/assets/index-BXUJbteW.js.map +0 -1
- package/dist/gateway/static/root/assets/index-CQLMxWSA.css +0 -2
- package/dist/gateway/static/root/assets/page-header-store-DJHD9Ean.js +0 -2
- package/dist/gateway/static/root/assets/settings-page-B3QrJm-E.js.map +0 -1
- package/dist/gateway/static/root/assets/url-CtSqjF9J.js +0 -2
- package/dist/gateway/static/root/assets/url-CtSqjF9J.js.map +0 -1
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Config } from '../config/schema.js';
|
|
2
|
+
import { type UpdateAvailable } from './update-check.js';
|
|
3
|
+
/** Get the cached update-available state (populated after startup check). */
|
|
4
|
+
export declare function getUpdateAvailable(): UpdateAvailable | null;
|
|
5
|
+
/**
|
|
6
|
+
* Main startup update check. Called once when the gateway starts (or on demand).
|
|
7
|
+
*/
|
|
8
|
+
export declare function runGatewayUpdateCheck(params: {
|
|
9
|
+
config: Config;
|
|
10
|
+
onUpdateAvailableChange?: (update: UpdateAvailable | null) => void;
|
|
11
|
+
/** When true, bypass checkOnStart/auto-disabled early exit and throttle (for POST /api/update/check). */
|
|
12
|
+
force?: boolean;
|
|
13
|
+
}): Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* Schedule periodic update checks. Returns a cleanup function to stop the timer.
|
|
16
|
+
*/
|
|
17
|
+
export declare function scheduleGatewayUpdateCheck(params: {
|
|
18
|
+
config: Config;
|
|
19
|
+
onUpdateAvailableChange?: (update: UpdateAvailable | null) => void;
|
|
20
|
+
}): () => void;
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { PACKAGE_VERSION, init_package_version } from "../package-version.js";
|
|
2
|
+
import { createLogger } from "../utils/logger/index.js";
|
|
3
|
+
import { init_logger } from "../utils/logger.js";
|
|
4
|
+
import { resolveStateDir } from "../config/paths-state.js";
|
|
5
|
+
import { init_paths } from "../config/paths.js";
|
|
6
|
+
import { normalizeUpdateChannel } from "./update-channels.js";
|
|
7
|
+
import { compareSemver, detectInstallKind, resolveNpmChannelTag, resolvePackageRoot } from "./update-check.js";
|
|
8
|
+
import { dirname, join } from "node:path";
|
|
9
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
10
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
11
|
+
//#region src/infra/update-startup.ts
|
|
12
|
+
init_paths();
|
|
13
|
+
init_package_version();
|
|
14
|
+
init_logger();
|
|
15
|
+
const log = createLogger("UpdateCheck");
|
|
16
|
+
const UPDATE_CHECK_FILENAME = "update-check.json";
|
|
17
|
+
const CHECK_INTERVAL_MS = 1440 * 60 * 1e3;
|
|
18
|
+
const ONE_HOUR_MS = 3600 * 1e3;
|
|
19
|
+
let updateAvailableCache = null;
|
|
20
|
+
/** Get the cached update-available state (populated after startup check). */
|
|
21
|
+
function getUpdateAvailable() {
|
|
22
|
+
return updateAvailableCache;
|
|
23
|
+
}
|
|
24
|
+
async function readState(statePath) {
|
|
25
|
+
try {
|
|
26
|
+
const raw = await readFile(statePath, "utf-8");
|
|
27
|
+
const parsed = JSON.parse(raw);
|
|
28
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
29
|
+
} catch {
|
|
30
|
+
return {};
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async function writeState(statePath, state) {
|
|
34
|
+
await mkdir(dirname(statePath), { recursive: true });
|
|
35
|
+
await writeFile(statePath, JSON.stringify(state, null, 2), "utf-8");
|
|
36
|
+
}
|
|
37
|
+
function resolveCheckIntervalMs(config) {
|
|
38
|
+
const auto = config.update?.auto;
|
|
39
|
+
if (!auto?.enabled) return CHECK_INTERVAL_MS;
|
|
40
|
+
if ((normalizeUpdateChannel(config.update?.channel) ?? "stable") === "beta") {
|
|
41
|
+
const hours = auto.betaCheckIntervalHours ?? 1;
|
|
42
|
+
return Math.max(ONE_HOUR_MS / 4, Math.floor(hours * ONE_HOUR_MS));
|
|
43
|
+
}
|
|
44
|
+
return ONE_HOUR_MS;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Compute a deterministic delay for stable auto-update rollout,
|
|
48
|
+
* based on a per-installation hash to spread updates over time.
|
|
49
|
+
*/
|
|
50
|
+
function resolveStableJitterMs(installId, version, tag, jitterWindowMs) {
|
|
51
|
+
if (jitterWindowMs <= 0) return 0;
|
|
52
|
+
return createHash("sha256").update(`${installId}:${version}:${tag}`).digest().readUInt32BE(0) % (Math.floor(jitterWindowMs) + 1);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Main startup update check. Called once when the gateway starts (or on demand).
|
|
56
|
+
*/
|
|
57
|
+
async function runGatewayUpdateCheck(params) {
|
|
58
|
+
const { config, force } = params;
|
|
59
|
+
const autoEnabled = config.update?.auto?.enabled ?? false;
|
|
60
|
+
const shouldCheckHints = config.update?.checkOnStart !== false;
|
|
61
|
+
if (!force && !shouldCheckHints && !autoEnabled) return;
|
|
62
|
+
const statePath = join(resolveStateDir(), UPDATE_CHECK_FILENAME);
|
|
63
|
+
const state = await readState(statePath);
|
|
64
|
+
const now = Date.now();
|
|
65
|
+
const lastCheckedAt = state.lastCheckedAt ? Date.parse(state.lastCheckedAt) : null;
|
|
66
|
+
if (state.lastAvailableVersion && (shouldCheckHints || force)) {
|
|
67
|
+
const comparison = compareSemver(PACKAGE_VERSION, state.lastAvailableVersion);
|
|
68
|
+
if (comparison !== null && comparison < 0) {
|
|
69
|
+
const cached = {
|
|
70
|
+
currentVersion: PACKAGE_VERSION,
|
|
71
|
+
latestVersion: state.lastAvailableVersion,
|
|
72
|
+
channel: state.lastAvailableTag ?? "latest"
|
|
73
|
+
};
|
|
74
|
+
updateAvailableCache = cached;
|
|
75
|
+
params.onUpdateAvailableChange?.(cached);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const checkIntervalMs = resolveCheckIntervalMs(config);
|
|
79
|
+
const shouldBypassThrottleForVersion = state.lastCheckPackageVersion === void 0 || state.lastCheckPackageVersion !== PACKAGE_VERSION;
|
|
80
|
+
if (!force && !shouldBypassThrottleForVersion && lastCheckedAt && Number.isFinite(lastCheckedAt) && now - lastCheckedAt < checkIntervalMs) return;
|
|
81
|
+
const root = await resolvePackageRoot();
|
|
82
|
+
let installKind = "unknown";
|
|
83
|
+
if (root) {
|
|
84
|
+
installKind = await detectInstallKind(root);
|
|
85
|
+
if (installKind === "git") log.info("Update check: git checkout (hint-only; use git pull to update, no auto npm install)");
|
|
86
|
+
}
|
|
87
|
+
const channel = normalizeUpdateChannel(config.update?.channel) ?? "stable";
|
|
88
|
+
const resolved = await resolveNpmChannelTag({
|
|
89
|
+
channel,
|
|
90
|
+
timeoutMs: 2500
|
|
91
|
+
});
|
|
92
|
+
const nextState = {
|
|
93
|
+
...state,
|
|
94
|
+
lastCheckedAt: new Date(now).toISOString()
|
|
95
|
+
};
|
|
96
|
+
if (!resolved.version) {
|
|
97
|
+
nextState.lastCheckPackageVersion = PACKAGE_VERSION;
|
|
98
|
+
await writeState(statePath, nextState);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const comparison = compareSemver(PACKAGE_VERSION, resolved.version);
|
|
102
|
+
if (comparison !== null && comparison < 0) {
|
|
103
|
+
const updateInfo = {
|
|
104
|
+
currentVersion: PACKAGE_VERSION,
|
|
105
|
+
latestVersion: resolved.version,
|
|
106
|
+
channel: resolved.tag
|
|
107
|
+
};
|
|
108
|
+
if (shouldCheckHints || force) {
|
|
109
|
+
updateAvailableCache = updateInfo;
|
|
110
|
+
params.onUpdateAvailableChange?.(updateInfo);
|
|
111
|
+
}
|
|
112
|
+
nextState.lastAvailableVersion = resolved.version;
|
|
113
|
+
nextState.lastAvailableTag = resolved.tag;
|
|
114
|
+
const shouldNotify = state.lastNotifiedVersion !== resolved.version || state.lastNotifiedTag !== resolved.tag;
|
|
115
|
+
if ((shouldCheckHints || force) && shouldNotify) {
|
|
116
|
+
log.info({
|
|
117
|
+
currentVersion: PACKAGE_VERSION,
|
|
118
|
+
latestVersion: resolved.version,
|
|
119
|
+
tag: resolved.tag
|
|
120
|
+
}, `Update available (${resolved.tag}): v${resolved.version} (current v${PACKAGE_VERSION}). Run: xopc update`);
|
|
121
|
+
nextState.lastNotifiedVersion = resolved.version;
|
|
122
|
+
nextState.lastNotifiedTag = resolved.tag;
|
|
123
|
+
}
|
|
124
|
+
if (autoEnabled && (channel === "stable" || channel === "beta") && installKind !== "git") await handleAutoUpdate({
|
|
125
|
+
channel,
|
|
126
|
+
version: resolved.version,
|
|
127
|
+
tag: resolved.tag,
|
|
128
|
+
state,
|
|
129
|
+
nextState,
|
|
130
|
+
now,
|
|
131
|
+
root,
|
|
132
|
+
config
|
|
133
|
+
});
|
|
134
|
+
} else {
|
|
135
|
+
delete nextState.lastAvailableVersion;
|
|
136
|
+
delete nextState.lastAvailableTag;
|
|
137
|
+
updateAvailableCache = null;
|
|
138
|
+
params.onUpdateAvailableChange?.(null);
|
|
139
|
+
}
|
|
140
|
+
nextState.lastCheckPackageVersion = PACKAGE_VERSION;
|
|
141
|
+
await writeState(statePath, nextState);
|
|
142
|
+
}
|
|
143
|
+
async function handleAutoUpdate(params) {
|
|
144
|
+
const { channel, version, tag, state, nextState, now, root, config } = params;
|
|
145
|
+
const auto = config.update?.auto;
|
|
146
|
+
if (!auto) return;
|
|
147
|
+
const stableDelayHours = auto.stableDelayHours ?? 6;
|
|
148
|
+
const stableJitterHours = auto.stableJitterHours ?? 12;
|
|
149
|
+
const betaCheckIntervalHours = auto.betaCheckIntervalHours ?? 1;
|
|
150
|
+
const attemptIntervalMs = channel === "beta" ? Math.max(ONE_HOUR_MS / 4, Math.floor(betaCheckIntervalHours * ONE_HOUR_MS)) : ONE_HOUR_MS;
|
|
151
|
+
const lastAttemptAt = state.autoLastAttemptAt ? Date.parse(state.autoLastAttemptAt) : null;
|
|
152
|
+
if (state.autoLastAttemptVersion === version && lastAttemptAt !== null && Number.isFinite(lastAttemptAt) && now - lastAttemptAt < attemptIntervalMs) {
|
|
153
|
+
log.info({
|
|
154
|
+
version,
|
|
155
|
+
tag
|
|
156
|
+
}, "Auto-update deferred: recent attempt exists");
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
if (channel === "stable") {
|
|
160
|
+
if (!nextState.autoInstallId) nextState.autoInstallId = state.autoInstallId?.trim() || randomUUID();
|
|
161
|
+
if (state.autoFirstSeenVersion !== version || state.autoFirstSeenTag !== tag) {
|
|
162
|
+
nextState.autoFirstSeenVersion = version;
|
|
163
|
+
nextState.autoFirstSeenTag = tag;
|
|
164
|
+
nextState.autoFirstSeenAt = new Date(now).toISOString();
|
|
165
|
+
} else nextState.autoFirstSeenAt = state.autoFirstSeenAt;
|
|
166
|
+
const firstSeenMs = nextState.autoFirstSeenAt ? Date.parse(nextState.autoFirstSeenAt) : now;
|
|
167
|
+
const baseDelayMs = Math.max(0, stableDelayHours) * ONE_HOUR_MS;
|
|
168
|
+
const jitterWindowMs = Math.max(0, stableJitterHours) * ONE_HOUR_MS;
|
|
169
|
+
const jitterMs = resolveStableJitterMs(nextState.autoInstallId, version, tag, jitterWindowMs);
|
|
170
|
+
const applyAfterMs = firstSeenMs + baseDelayMs + jitterMs;
|
|
171
|
+
if (now < applyAfterMs) {
|
|
172
|
+
log.info({
|
|
173
|
+
version,
|
|
174
|
+
tag,
|
|
175
|
+
applyAfter: new Date(applyAfterMs).toISOString()
|
|
176
|
+
}, "Auto-update deferred: stable rollout window not yet due");
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
nextState.autoLastAttemptVersion = version;
|
|
181
|
+
nextState.autoLastAttemptAt = new Date(now).toISOString();
|
|
182
|
+
log.info({
|
|
183
|
+
channel,
|
|
184
|
+
version,
|
|
185
|
+
tag
|
|
186
|
+
}, "Starting auto-update");
|
|
187
|
+
try {
|
|
188
|
+
const { runAutoUpdateCommand } = await import("./update-runner.js");
|
|
189
|
+
const result = await runAutoUpdateCommand({
|
|
190
|
+
channel,
|
|
191
|
+
root
|
|
192
|
+
});
|
|
193
|
+
if (result.ok) {
|
|
194
|
+
nextState.autoLastSuccessVersion = version;
|
|
195
|
+
nextState.autoLastSuccessAt = new Date(now).toISOString();
|
|
196
|
+
log.info({
|
|
197
|
+
channel,
|
|
198
|
+
version,
|
|
199
|
+
tag
|
|
200
|
+
}, "Auto-update applied successfully");
|
|
201
|
+
} else log.warn({
|
|
202
|
+
channel,
|
|
203
|
+
version,
|
|
204
|
+
tag,
|
|
205
|
+
exitCode: result.exitCode,
|
|
206
|
+
reason: result.reason
|
|
207
|
+
}, `Auto-update attempt failed: ${result.reason ?? `exit ${result.exitCode}`}`);
|
|
208
|
+
} catch (err) {
|
|
209
|
+
log.error({
|
|
210
|
+
err,
|
|
211
|
+
channel,
|
|
212
|
+
version
|
|
213
|
+
}, "Auto-update command threw");
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Schedule periodic update checks. Returns a cleanup function to stop the timer.
|
|
218
|
+
*/
|
|
219
|
+
function scheduleGatewayUpdateCheck(params) {
|
|
220
|
+
let stopped = false;
|
|
221
|
+
let timer = null;
|
|
222
|
+
const tick = async () => {
|
|
223
|
+
if (stopped) return;
|
|
224
|
+
try {
|
|
225
|
+
await runGatewayUpdateCheck(params);
|
|
226
|
+
} catch (err) {
|
|
227
|
+
log.warn({ err }, "Periodic update check failed");
|
|
228
|
+
}
|
|
229
|
+
if (!stopped) {
|
|
230
|
+
const intervalMs = resolveCheckIntervalMs(params.config);
|
|
231
|
+
timer = setTimeout(() => void tick(), intervalMs);
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
timer = setTimeout(() => void tick(), 5e3);
|
|
235
|
+
return () => {
|
|
236
|
+
stopped = true;
|
|
237
|
+
if (timer) {
|
|
238
|
+
clearTimeout(timer);
|
|
239
|
+
timer = null;
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
//#endregion
|
|
244
|
+
export { getUpdateAvailable, runGatewayUpdateCheck, scheduleGatewayUpdateCheck };
|
|
245
|
+
|
|
246
|
+
//# sourceMappingURL=update-startup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-startup.js","names":[],"sources":["../../../src/infra/update-startup.ts"],"sourcesContent":["// src/infra/update-startup.ts\n\nimport { createHash, randomUUID } from 'node:crypto';\nimport { readFile, writeFile, mkdir } from 'node:fs/promises';\nimport { join, dirname } from 'node:path';\n\nimport type { Config } from '../config/schema.js';\nimport { resolveStateDir } from '../config/paths.js';\nimport { PACKAGE_VERSION } from '../package-version.js';\nimport { createLogger } from '../utils/logger.js';\n\nimport { normalizeUpdateChannel, DEFAULT_PACKAGE_CHANNEL } from './update-channels.js';\nimport {\n compareSemver,\n resolveNpmChannelTag,\n detectInstallKind,\n resolvePackageRoot,\n type InstallKind,\n type UpdateAvailable,\n} from './update-check.js';\n\nconst log = createLogger('UpdateCheck');\n\n// --- State persistence ---\n\nconst UPDATE_CHECK_FILENAME = 'update-check.json';\nconst CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 hours\nconst ONE_HOUR_MS = 60 * 60 * 1000;\n\ntype UpdateCheckState = {\n /** `package.json` version at the last successful registry check (used to bypass 24h throttle when you bump the local version). */\n lastCheckPackageVersion?: string;\n lastCheckedAt?: string;\n lastAvailableVersion?: string;\n lastAvailableTag?: string;\n lastNotifiedVersion?: string;\n lastNotifiedTag?: string;\n autoInstallId?: string;\n autoFirstSeenVersion?: string;\n autoFirstSeenTag?: string;\n autoFirstSeenAt?: string;\n autoLastAttemptVersion?: string;\n autoLastAttemptAt?: string;\n autoLastSuccessVersion?: string;\n autoLastSuccessAt?: string;\n};\n\n// --- In-memory cache ---\n\nlet updateAvailableCache: UpdateAvailable | null = null;\n\n/** Get the cached update-available state (populated after startup check). */\nexport function getUpdateAvailable(): UpdateAvailable | null {\n return updateAvailableCache;\n}\n\n// --- Core logic ---\n\nasync function readState(statePath: string): Promise<UpdateCheckState> {\n try {\n const raw = await readFile(statePath, 'utf-8');\n const parsed = JSON.parse(raw) as UpdateCheckState;\n return parsed && typeof parsed === 'object' ? parsed : {};\n } catch {\n return {};\n }\n}\n\nasync function writeState(statePath: string, state: UpdateCheckState): Promise<void> {\n await mkdir(dirname(statePath), { recursive: true });\n await writeFile(statePath, JSON.stringify(state, null, 2), 'utf-8');\n}\n\nfunction resolveCheckIntervalMs(config: Config): number {\n const auto = config.update?.auto;\n if (!auto?.enabled) return CHECK_INTERVAL_MS;\n\n const channel = normalizeUpdateChannel(config.update?.channel) ?? DEFAULT_PACKAGE_CHANNEL;\n if (channel === 'beta') {\n const hours = auto.betaCheckIntervalHours ?? 1;\n return Math.max(ONE_HOUR_MS / 4, Math.floor(hours * ONE_HOUR_MS));\n }\n return ONE_HOUR_MS;\n}\n\n/**\n * Compute a deterministic delay for stable auto-update rollout,\n * based on a per-installation hash to spread updates over time.\n */\nfunction resolveStableJitterMs(\n installId: string,\n version: string,\n tag: string,\n jitterWindowMs: number,\n): number {\n if (jitterWindowMs <= 0) return 0;\n const hash = createHash('sha256').update(`${installId}:${version}:${tag}`).digest();\n const bucket = hash.readUInt32BE(0);\n return bucket % (Math.floor(jitterWindowMs) + 1);\n}\n\n/**\n * Main startup update check. Called once when the gateway starts (or on demand).\n */\nexport async function runGatewayUpdateCheck(params: {\n config: Config;\n onUpdateAvailableChange?: (update: UpdateAvailable | null) => void;\n /** When true, bypass checkOnStart/auto-disabled early exit and throttle (for POST /api/update/check). */\n force?: boolean;\n}): Promise<void> {\n const { config, force } = params;\n\n const autoEnabled = config.update?.auto?.enabled ?? false;\n const shouldCheckHints = config.update?.checkOnStart !== false;\n if (!force && !shouldCheckHints && !autoEnabled) return;\n\n const stateDir = resolveStateDir();\n const statePath = join(stateDir, UPDATE_CHECK_FILENAME);\n const state = await readState(statePath);\n const now = Date.now();\n\n // Hydrate from persisted state if within throttle window\n const lastCheckedAt = state.lastCheckedAt ? Date.parse(state.lastCheckedAt) : null;\n if (state.lastAvailableVersion && (shouldCheckHints || force)) {\n const comparison = compareSemver(PACKAGE_VERSION, state.lastAvailableVersion);\n if (comparison !== null && comparison < 0) {\n const cached: UpdateAvailable = {\n currentVersion: PACKAGE_VERSION,\n latestVersion: state.lastAvailableVersion,\n channel: state.lastAvailableTag ?? 'latest',\n };\n updateAvailableCache = cached;\n params.onUpdateAvailableChange?.(cached);\n }\n }\n\n const checkIntervalMs = resolveCheckIntervalMs(config);\n // Re-check npm when the local package version changed (e.g. after editing package.json) even within 24h.\n const shouldBypassThrottleForVersion =\n state.lastCheckPackageVersion === undefined || state.lastCheckPackageVersion !== PACKAGE_VERSION;\n if (\n !force &&\n !shouldBypassThrottleForVersion &&\n lastCheckedAt &&\n Number.isFinite(lastCheckedAt) &&\n now - lastCheckedAt < checkIntervalMs\n ) {\n return; // Within throttle window\n }\n\n // Install kind: auto-install only for npm global installs, but we still query npm in git\n // so the Web UI / CLI can show \"newer on registry\" and the top reminder bar.\n const root = await resolvePackageRoot();\n let installKind: InstallKind = 'unknown';\n if (root) {\n installKind = await detectInstallKind(root);\n if (installKind === 'git') {\n log.info('Update check: git checkout (hint-only; use git pull to update, no auto npm install)');\n }\n }\n\n // Query npm registry\n const channel = normalizeUpdateChannel(config.update?.channel) ?? DEFAULT_PACKAGE_CHANNEL;\n const resolved = await resolveNpmChannelTag({ channel, timeoutMs: 2500 });\n\n const nextState: UpdateCheckState = {\n ...state,\n lastCheckedAt: new Date(now).toISOString(),\n };\n\n if (!resolved.version) {\n nextState.lastCheckPackageVersion = PACKAGE_VERSION;\n await writeState(statePath, nextState);\n return;\n }\n\n const comparison = compareSemver(PACKAGE_VERSION, resolved.version);\n if (comparison !== null && comparison < 0) {\n // Update available\n const updateInfo: UpdateAvailable = {\n currentVersion: PACKAGE_VERSION,\n latestVersion: resolved.version,\n channel: resolved.tag,\n };\n\n if (shouldCheckHints || force) {\n updateAvailableCache = updateInfo;\n params.onUpdateAvailableChange?.(updateInfo);\n }\n\n nextState.lastAvailableVersion = resolved.version;\n nextState.lastAvailableTag = resolved.tag;\n\n // Log notification (once per version)\n const shouldNotify =\n state.lastNotifiedVersion !== resolved.version || state.lastNotifiedTag !== resolved.tag;\n if ((shouldCheckHints || force) && shouldNotify) {\n log.info(\n { currentVersion: PACKAGE_VERSION, latestVersion: resolved.version, tag: resolved.tag },\n `Update available (${resolved.tag}): v${resolved.version} (current v${PACKAGE_VERSION}). Run: xopc update`,\n );\n nextState.lastNotifiedVersion = resolved.version;\n nextState.lastNotifiedTag = resolved.tag;\n }\n\n // Auto-update logic (never from a git worktree)\n if (\n autoEnabled &&\n (channel === 'stable' || channel === 'beta') &&\n installKind !== 'git'\n ) {\n await handleAutoUpdate({\n channel,\n version: resolved.version,\n tag: resolved.tag,\n state,\n nextState,\n now,\n root,\n config,\n });\n }\n } else {\n // Current version is up to date or newer\n delete nextState.lastAvailableVersion;\n delete nextState.lastAvailableTag;\n updateAvailableCache = null;\n params.onUpdateAvailableChange?.(null);\n }\n\n nextState.lastCheckPackageVersion = PACKAGE_VERSION;\n await writeState(statePath, nextState);\n}\n\nasync function handleAutoUpdate(params: {\n channel: 'stable' | 'beta';\n version: string;\n tag: string;\n state: UpdateCheckState;\n nextState: UpdateCheckState;\n now: number;\n root: string | null;\n config: Config;\n}): Promise<void> {\n const { channel, version, tag, state, nextState, now, root, config } = params;\n const auto = config.update?.auto;\n if (!auto) return;\n\n const stableDelayHours = auto.stableDelayHours ?? 6;\n const stableJitterHours = auto.stableJitterHours ?? 12;\n const betaCheckIntervalHours = auto.betaCheckIntervalHours ?? 1;\n\n // Rate limit: don't re-attempt same version within interval\n const attemptIntervalMs =\n channel === 'beta'\n ? Math.max(ONE_HOUR_MS / 4, Math.floor(betaCheckIntervalHours * ONE_HOUR_MS))\n : ONE_HOUR_MS;\n const lastAttemptAt = state.autoLastAttemptAt ? Date.parse(state.autoLastAttemptAt) : null;\n const recentAttempt =\n state.autoLastAttemptVersion === version &&\n lastAttemptAt !== null &&\n Number.isFinite(lastAttemptAt) &&\n now - lastAttemptAt < attemptIntervalMs;\n\n if (recentAttempt) {\n log.info({ version, tag }, 'Auto-update deferred: recent attempt exists');\n return;\n }\n\n // Stable rollout delay + jitter\n if (channel === 'stable') {\n if (!nextState.autoInstallId) {\n nextState.autoInstallId = state.autoInstallId?.trim() || randomUUID();\n }\n // Track first-seen time for this version\n if (state.autoFirstSeenVersion !== version || state.autoFirstSeenTag !== tag) {\n nextState.autoFirstSeenVersion = version;\n nextState.autoFirstSeenTag = tag;\n nextState.autoFirstSeenAt = new Date(now).toISOString();\n } else {\n nextState.autoFirstSeenAt = state.autoFirstSeenAt;\n }\n\n const firstSeenMs = nextState.autoFirstSeenAt ? Date.parse(nextState.autoFirstSeenAt) : now;\n const baseDelayMs = Math.max(0, stableDelayHours) * ONE_HOUR_MS;\n const jitterWindowMs = Math.max(0, stableJitterHours) * ONE_HOUR_MS;\n const jitterMs = resolveStableJitterMs(nextState.autoInstallId, version, tag, jitterWindowMs);\n const applyAfterMs = firstSeenMs + baseDelayMs + jitterMs;\n\n if (now < applyAfterMs) {\n log.info(\n { version, tag, applyAfter: new Date(applyAfterMs).toISOString() },\n 'Auto-update deferred: stable rollout window not yet due',\n );\n return;\n }\n }\n\n // Execute auto-update\n nextState.autoLastAttemptVersion = version;\n nextState.autoLastAttemptAt = new Date(now).toISOString();\n\n log.info({ channel, version, tag }, 'Starting auto-update');\n\n try {\n const { runAutoUpdateCommand } = await import('./update-runner.js');\n const result = await runAutoUpdateCommand({ channel, root });\n if (result.ok) {\n nextState.autoLastSuccessVersion = version;\n nextState.autoLastSuccessAt = new Date(now).toISOString();\n log.info({ channel, version, tag }, 'Auto-update applied successfully');\n } else {\n log.warn(\n { channel, version, tag, exitCode: result.exitCode, reason: result.reason },\n `Auto-update attempt failed: ${result.reason ?? `exit ${result.exitCode}`}`,\n );\n }\n } catch (err) {\n log.error({ err, channel, version }, 'Auto-update command threw');\n }\n}\n\n/**\n * Schedule periodic update checks. Returns a cleanup function to stop the timer.\n */\nexport function scheduleGatewayUpdateCheck(params: {\n config: Config;\n onUpdateAvailableChange?: (update: UpdateAvailable | null) => void;\n}): () => void {\n let stopped = false;\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n const tick = async () => {\n if (stopped) return;\n try {\n await runGatewayUpdateCheck(params);\n } catch (err) {\n log.warn({ err }, 'Periodic update check failed');\n }\n if (!stopped) {\n const intervalMs = resolveCheckIntervalMs(params.config);\n timer = setTimeout(() => void tick(), intervalMs);\n }\n };\n\n // Initial check after a short delay (don't block startup)\n timer = setTimeout(() => void tick(), 5000);\n\n return () => {\n stopped = true;\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { createLogger } from "../utils/logger/index.js";
|
|
2
|
+
import { init_logger } from "../utils/logger.js";
|
|
3
|
+
import { getProviderRegistry, init_plugin_registry } from "./plugin-registry.js";
|
|
4
|
+
import { init_providers } from "./index.js";
|
|
5
|
+
import { createAssistantMessageEventStream, streamSimple } from "@mariozechner/pi-ai";
|
|
6
|
+
//#region src/providers/extension-stream-bridge.ts
|
|
7
|
+
init_plugin_registry();
|
|
8
|
+
init_providers();
|
|
9
|
+
init_logger();
|
|
10
|
+
const log = createLogger("ExtensionStreamBridge");
|
|
11
|
+
function createPartialMessage(model) {
|
|
12
|
+
return {
|
|
13
|
+
role: "assistant",
|
|
14
|
+
content: [],
|
|
15
|
+
api: model.api,
|
|
16
|
+
provider: model.provider,
|
|
17
|
+
model: model.id,
|
|
18
|
+
usage: {
|
|
19
|
+
input: 0,
|
|
20
|
+
output: 0,
|
|
21
|
+
cacheRead: 0,
|
|
22
|
+
cacheWrite: 0,
|
|
23
|
+
totalTokens: 0,
|
|
24
|
+
cost: {
|
|
25
|
+
input: 0,
|
|
26
|
+
output: 0,
|
|
27
|
+
cacheRead: 0,
|
|
28
|
+
cacheWrite: 0,
|
|
29
|
+
total: 0
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
stopReason: "stop",
|
|
33
|
+
timestamp: Date.now()
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function parseToolArguments(raw) {
|
|
37
|
+
if (!raw?.trim()) return {};
|
|
38
|
+
try {
|
|
39
|
+
return JSON.parse(raw);
|
|
40
|
+
} catch {
|
|
41
|
+
return {};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/** Wraps {@link streamSimple} so extension-registered models use the plugin `createStream()` path. */
|
|
45
|
+
function createExtensionAwareStreamFn() {
|
|
46
|
+
return ((model, context, options) => {
|
|
47
|
+
if (model.baseUrl !== "extension://provider-plugin") return streamSimple(model, context, options);
|
|
48
|
+
const plugin = getProviderRegistry().get(model.provider);
|
|
49
|
+
if (!plugin) return streamSimple(model, context, options);
|
|
50
|
+
log.info({
|
|
51
|
+
prefix: "ExtensionStreamBridge",
|
|
52
|
+
msg: "Streaming via extension provider"
|
|
53
|
+
});
|
|
54
|
+
const stream = createAssistantMessageEventStream();
|
|
55
|
+
const params = {
|
|
56
|
+
model: model.id,
|
|
57
|
+
messages: context.messages,
|
|
58
|
+
temperature: options?.temperature,
|
|
59
|
+
maxTokens: options?.maxTokens,
|
|
60
|
+
signal: options?.signal
|
|
61
|
+
};
|
|
62
|
+
(async () => {
|
|
63
|
+
const partial = createPartialMessage(model);
|
|
64
|
+
let textContentIndex = -1;
|
|
65
|
+
let hasStarted = false;
|
|
66
|
+
try {
|
|
67
|
+
for await (const chunk of plugin.createStream(params)) {
|
|
68
|
+
if (options?.signal?.aborted) {
|
|
69
|
+
partial.stopReason = "aborted";
|
|
70
|
+
partial.errorMessage = "Request aborted";
|
|
71
|
+
stream.push({
|
|
72
|
+
type: "error",
|
|
73
|
+
reason: "aborted",
|
|
74
|
+
error: partial
|
|
75
|
+
});
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
switch (chunk.type) {
|
|
79
|
+
case "text": {
|
|
80
|
+
if (!hasStarted) {
|
|
81
|
+
hasStarted = true;
|
|
82
|
+
stream.push({
|
|
83
|
+
type: "start",
|
|
84
|
+
partial
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
if (textContentIndex === -1) {
|
|
88
|
+
textContentIndex = partial.content.length;
|
|
89
|
+
partial.content.push({
|
|
90
|
+
type: "text",
|
|
91
|
+
text: ""
|
|
92
|
+
});
|
|
93
|
+
stream.push({
|
|
94
|
+
type: "text_start",
|
|
95
|
+
contentIndex: textContentIndex,
|
|
96
|
+
partial
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
const textContent = partial.content[textContentIndex];
|
|
100
|
+
if (textContent?.type === "text" && chunk.text) {
|
|
101
|
+
textContent.text += chunk.text;
|
|
102
|
+
stream.push({
|
|
103
|
+
type: "text_delta",
|
|
104
|
+
contentIndex: textContentIndex,
|
|
105
|
+
delta: chunk.text,
|
|
106
|
+
partial
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
case "tool_call":
|
|
112
|
+
if (!hasStarted) {
|
|
113
|
+
hasStarted = true;
|
|
114
|
+
stream.push({
|
|
115
|
+
type: "start",
|
|
116
|
+
partial
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
if (textContentIndex !== -1) {
|
|
120
|
+
const tc = partial.content[textContentIndex];
|
|
121
|
+
if (tc?.type === "text") stream.push({
|
|
122
|
+
type: "text_end",
|
|
123
|
+
contentIndex: textContentIndex,
|
|
124
|
+
content: tc.text,
|
|
125
|
+
partial
|
|
126
|
+
});
|
|
127
|
+
textContentIndex = -1;
|
|
128
|
+
}
|
|
129
|
+
if (chunk.toolCall) {
|
|
130
|
+
const idx = partial.content.length;
|
|
131
|
+
const toolCall = {
|
|
132
|
+
type: "toolCall",
|
|
133
|
+
id: chunk.toolCall.id,
|
|
134
|
+
name: chunk.toolCall.name,
|
|
135
|
+
arguments: parseToolArguments(chunk.toolCall.arguments)
|
|
136
|
+
};
|
|
137
|
+
partial.content.push(toolCall);
|
|
138
|
+
stream.push({
|
|
139
|
+
type: "toolcall_start",
|
|
140
|
+
contentIndex: idx,
|
|
141
|
+
partial
|
|
142
|
+
});
|
|
143
|
+
stream.push({
|
|
144
|
+
type: "toolcall_delta",
|
|
145
|
+
contentIndex: idx,
|
|
146
|
+
delta: chunk.toolCall.arguments || "{}",
|
|
147
|
+
partial
|
|
148
|
+
});
|
|
149
|
+
stream.push({
|
|
150
|
+
type: "toolcall_end",
|
|
151
|
+
contentIndex: idx,
|
|
152
|
+
toolCall,
|
|
153
|
+
partial
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
break;
|
|
157
|
+
case "usage":
|
|
158
|
+
if (chunk.usage) {
|
|
159
|
+
const inputCost = chunk.usage.input / 1e6 * (model.cost?.input ?? 0);
|
|
160
|
+
const outputCost = chunk.usage.output / 1e6 * (model.cost?.output ?? 0);
|
|
161
|
+
partial.usage = {
|
|
162
|
+
input: chunk.usage.input,
|
|
163
|
+
output: chunk.usage.output,
|
|
164
|
+
cacheRead: 0,
|
|
165
|
+
cacheWrite: 0,
|
|
166
|
+
totalTokens: chunk.usage.total ?? chunk.usage.input + chunk.usage.output,
|
|
167
|
+
cost: {
|
|
168
|
+
input: inputCost,
|
|
169
|
+
output: outputCost,
|
|
170
|
+
cacheRead: 0,
|
|
171
|
+
cacheWrite: 0,
|
|
172
|
+
total: inputCost + outputCost
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
break;
|
|
177
|
+
case "error":
|
|
178
|
+
if (!hasStarted) {
|
|
179
|
+
hasStarted = true;
|
|
180
|
+
stream.push({
|
|
181
|
+
type: "start",
|
|
182
|
+
partial
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
partial.stopReason = "error";
|
|
186
|
+
partial.errorMessage = chunk.error ?? "Unknown extension provider error";
|
|
187
|
+
stream.push({
|
|
188
|
+
type: "error",
|
|
189
|
+
reason: "error",
|
|
190
|
+
error: partial
|
|
191
|
+
});
|
|
192
|
+
return;
|
|
193
|
+
case "done": break;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (textContentIndex !== -1) {
|
|
197
|
+
const tc = partial.content[textContentIndex];
|
|
198
|
+
if (tc?.type === "text") stream.push({
|
|
199
|
+
type: "text_end",
|
|
200
|
+
contentIndex: textContentIndex,
|
|
201
|
+
content: tc.text,
|
|
202
|
+
partial
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
if (!hasStarted) {
|
|
206
|
+
hasStarted = true;
|
|
207
|
+
stream.push({
|
|
208
|
+
type: "start",
|
|
209
|
+
partial
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
const stopReason = partial.content.some((c) => c.type === "toolCall") ? "toolUse" : "stop";
|
|
213
|
+
partial.stopReason = stopReason;
|
|
214
|
+
stream.push({
|
|
215
|
+
type: "done",
|
|
216
|
+
reason: stopReason,
|
|
217
|
+
message: partial
|
|
218
|
+
});
|
|
219
|
+
} catch (err) {
|
|
220
|
+
if (!hasStarted) stream.push({
|
|
221
|
+
type: "start",
|
|
222
|
+
partial
|
|
223
|
+
});
|
|
224
|
+
partial.stopReason = "error";
|
|
225
|
+
partial.errorMessage = err instanceof Error ? err.message : String(err);
|
|
226
|
+
stream.push({
|
|
227
|
+
type: "error",
|
|
228
|
+
reason: "error",
|
|
229
|
+
error: partial
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
})();
|
|
233
|
+
return stream;
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
//#endregion
|
|
237
|
+
export { createExtensionAwareStreamFn };
|
|
238
|
+
|
|
239
|
+
//# sourceMappingURL=extension-stream-bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension-stream-bridge.js","names":[],"sources":["../../../src/providers/extension-stream-bridge.ts"],"sourcesContent":["import {\n\tstreamSimple,\n\tcreateAssistantMessageEventStream,\n\ttype AssistantMessage,\n\ttype Model,\n\ttype Api,\n\ttype Context,\n\ttype SimpleStreamOptions,\n} from '@mariozechner/pi-ai';\nimport type { StreamFn } from '@mariozechner/pi-agent-core';\nimport type { ProviderStreamParams } from '../extensions/types/providers.js';\nimport { getProviderRegistry } from './plugin-registry.js';\nimport { EXTENSION_PROVIDER_BASE_URL } from './index.js';\nimport { createLogger } from '../utils/logger.js';\n\nconst log = createLogger('ExtensionStreamBridge');\n\nfunction createPartialMessage(model: Model<Api>): AssistantMessage {\n\treturn {\n\t\trole: 'assistant',\n\t\tcontent: [],\n\t\tapi: model.api,\n\t\tprovider: model.provider,\n\t\tmodel: model.id,\n\t\tusage: {\n\t\t\tinput: 0,\n\t\t\toutput: 0,\n\t\t\tcacheRead: 0,\n\t\t\tcacheWrite: 0,\n\t\t\ttotalTokens: 0,\n\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t},\n\t\tstopReason: 'stop',\n\t\ttimestamp: Date.now(),\n\t};\n}\n\nfunction parseToolArguments(raw: string | undefined): Record<string, unknown> {\n\tif (!raw?.trim()) return {};\n\ttry {\n\t\treturn JSON.parse(raw) as Record<string, unknown>;\n\t} catch {\n\t\treturn {};\n\t}\n}\n\n/** Wraps {@link streamSimple} so extension-registered models use the plugin `createStream()` path. */\nexport function createExtensionAwareStreamFn(): StreamFn {\n\treturn ((model: Model<Api>, context: Context, options?: SimpleStreamOptions) => {\n\t\tif (model.baseUrl !== EXTENSION_PROVIDER_BASE_URL) {\n\t\t\treturn streamSimple(model, context, options);\n\t\t}\n\n\t\tconst plugin = getProviderRegistry().get(model.provider);\n\t\tif (!plugin) {\n\t\t\treturn streamSimple(model, context, options);\n\t\t}\n\n\t\tlog.info({ prefix: 'ExtensionStreamBridge', msg: 'Streaming via extension provider' });\n\n\t\tconst stream = createAssistantMessageEventStream();\n\n\t\tconst params: ProviderStreamParams = {\n\t\t\tmodel: model.id,\n\t\t\tmessages: context.messages as unknown as ProviderStreamParams['messages'],\n\t\t\ttemperature: options?.temperature,\n\t\t\tmaxTokens: options?.maxTokens,\n\t\t\tsignal: options?.signal,\n\t\t};\n\n\t\tvoid (async () => {\n\t\t\tconst partial = createPartialMessage(model);\n\t\t\tlet textContentIndex = -1;\n\t\t\tlet hasStarted = false;\n\n\t\t\ttry {\n\t\t\t\tfor await (const chunk of plugin.createStream(params)) {\n\t\t\t\t\tif (options?.signal?.aborted) {\n\t\t\t\t\t\tpartial.stopReason = 'aborted';\n\t\t\t\t\t\tpartial.errorMessage = 'Request aborted';\n\t\t\t\t\t\tstream.push({ type: 'error', reason: 'aborted', error: partial });\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tswitch (chunk.type) {\n\t\t\t\t\t\tcase 'text': {\n\t\t\t\t\t\t\tif (!hasStarted) {\n\t\t\t\t\t\t\t\thasStarted = true;\n\t\t\t\t\t\t\t\tstream.push({ type: 'start', partial });\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (textContentIndex === -1) {\n\t\t\t\t\t\t\t\ttextContentIndex = partial.content.length;\n\t\t\t\t\t\t\t\tpartial.content.push({ type: 'text', text: '' });\n\t\t\t\t\t\t\t\tstream.push({ type: 'text_start', contentIndex: textContentIndex, partial });\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst textContent = partial.content[textContentIndex];\n\t\t\t\t\t\t\tif (textContent?.type === 'text' && chunk.text) {\n\t\t\t\t\t\t\t\ttextContent.text += chunk.text;\n\t\t\t\t\t\t\t\tstream.push({\n\t\t\t\t\t\t\t\t\ttype: 'text_delta',\n\t\t\t\t\t\t\t\t\tcontentIndex: textContentIndex,\n\t\t\t\t\t\t\t\t\tdelta: chunk.text,\n\t\t\t\t\t\t\t\t\tpartial,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcase 'tool_call': {\n\t\t\t\t\t\t\tif (!hasStarted) {\n\t\t\t\t\t\t\t\thasStarted = true;\n\t\t\t\t\t\t\t\tstream.push({ type: 'start', partial });\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (textContentIndex !== -1) {\n\t\t\t\t\t\t\t\tconst tc = partial.content[textContentIndex];\n\t\t\t\t\t\t\t\tif (tc?.type === 'text') {\n\t\t\t\t\t\t\t\t\tstream.push({\n\t\t\t\t\t\t\t\t\t\ttype: 'text_end',\n\t\t\t\t\t\t\t\t\t\tcontentIndex: textContentIndex,\n\t\t\t\t\t\t\t\t\t\tcontent: tc.text,\n\t\t\t\t\t\t\t\t\t\tpartial,\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\ttextContentIndex = -1;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (chunk.toolCall) {\n\t\t\t\t\t\t\t\tconst idx = partial.content.length;\n\t\t\t\t\t\t\t\tconst toolCall = {\n\t\t\t\t\t\t\t\t\ttype: 'toolCall' as const,\n\t\t\t\t\t\t\t\t\tid: chunk.toolCall.id,\n\t\t\t\t\t\t\t\t\tname: chunk.toolCall.name,\n\t\t\t\t\t\t\t\t\targuments: parseToolArguments(chunk.toolCall.arguments),\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\tpartial.content.push(toolCall);\n\t\t\t\t\t\t\t\tstream.push({ type: 'toolcall_start', contentIndex: idx, partial });\n\t\t\t\t\t\t\t\tstream.push({\n\t\t\t\t\t\t\t\t\ttype: 'toolcall_delta',\n\t\t\t\t\t\t\t\t\tcontentIndex: idx,\n\t\t\t\t\t\t\t\t\tdelta: chunk.toolCall.arguments || '{}',\n\t\t\t\t\t\t\t\t\tpartial,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\tstream.push({ type: 'toolcall_end', contentIndex: idx, toolCall, partial });\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcase 'usage': {\n\t\t\t\t\t\t\tif (chunk.usage) {\n\t\t\t\t\t\t\t\tconst inputCost = (chunk.usage.input / 1_000_000) * (model.cost?.input ?? 0);\n\t\t\t\t\t\t\t\tconst outputCost = (chunk.usage.output / 1_000_000) * (model.cost?.output ?? 0);\n\t\t\t\t\t\t\t\tpartial.usage = {\n\t\t\t\t\t\t\t\t\tinput: chunk.usage.input,\n\t\t\t\t\t\t\t\t\toutput: chunk.usage.output,\n\t\t\t\t\t\t\t\t\tcacheRead: 0,\n\t\t\t\t\t\t\t\t\tcacheWrite: 0,\n\t\t\t\t\t\t\t\t\ttotalTokens: chunk.usage.total ?? chunk.usage.input + chunk.usage.output,\n\t\t\t\t\t\t\t\t\tcost: {\n\t\t\t\t\t\t\t\t\t\tinput: inputCost,\n\t\t\t\t\t\t\t\t\t\toutput: outputCost,\n\t\t\t\t\t\t\t\t\t\tcacheRead: 0,\n\t\t\t\t\t\t\t\t\t\tcacheWrite: 0,\n\t\t\t\t\t\t\t\t\t\ttotal: inputCost + outputCost,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcase 'error': {\n\t\t\t\t\t\t\tif (!hasStarted) {\n\t\t\t\t\t\t\t\thasStarted = true;\n\t\t\t\t\t\t\t\tstream.push({ type: 'start', partial });\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tpartial.stopReason = 'error';\n\t\t\t\t\t\t\tpartial.errorMessage = chunk.error ?? 'Unknown extension provider error';\n\t\t\t\t\t\t\tstream.push({ type: 'error', reason: 'error', error: partial });\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcase 'done':\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (textContentIndex !== -1) {\n\t\t\t\t\tconst tc = partial.content[textContentIndex];\n\t\t\t\t\tif (tc?.type === 'text') {\n\t\t\t\t\t\tstream.push({\n\t\t\t\t\t\t\ttype: 'text_end',\n\t\t\t\t\t\t\tcontentIndex: textContentIndex,\n\t\t\t\t\t\t\tcontent: tc.text,\n\t\t\t\t\t\t\tpartial,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!hasStarted) {\n\t\t\t\t\thasStarted = true;\n\t\t\t\t\tstream.push({ type: 'start', partial });\n\t\t\t\t}\n\n\t\t\t\tconst hasToolCalls = partial.content.some(c => c.type === 'toolCall');\n\t\t\t\tconst stopReason = hasToolCalls ? 'toolUse' : 'stop';\n\t\t\t\tpartial.stopReason = stopReason;\n\t\t\t\tstream.push({ type: 'done', reason: stopReason, message: partial });\n\t\t\t} catch (err) {\n\t\t\t\tif (!hasStarted) stream.push({ type: 'start', partial });\n\t\t\t\tpartial.stopReason = 'error';\n\t\t\t\tpartial.errorMessage = err instanceof Error ? err.message : String(err);\n\t\t\t\tstream.push({ type: 'error', reason: 'error', error: partial });\n\t\t\t}\n\t\t})();\n\n\t\treturn stream;\n\t}) as StreamFn;\n}\n"],"mappings":";;;;;;sBAW2D;gBACF;aACP;AAElD,MAAM,MAAM,aAAa,wBAAwB;AAEjD,SAAS,qBAAqB,OAAqC;AAClE,QAAO;EACN,MAAM;EACN,SAAS,EAAE;EACX,KAAK,MAAM;EACX,UAAU,MAAM;EAChB,OAAO,MAAM;EACb,OAAO;GACN,OAAO;GACP,QAAQ;GACR,WAAW;GACX,YAAY;GACZ,aAAa;GACb,MAAM;IAAE,OAAO;IAAG,QAAQ;IAAG,WAAW;IAAG,YAAY;IAAG,OAAO;IAAG;GACpE;EACD,YAAY;EACZ,WAAW,KAAK,KAAK;EACrB;;AAGF,SAAS,mBAAmB,KAAkD;AAC7E,KAAI,CAAC,KAAK,MAAM,CAAE,QAAO,EAAE;AAC3B,KAAI;AACH,SAAO,KAAK,MAAM,IAAI;SACf;AACP,SAAO,EAAE;;;;AAKX,SAAgB,+BAAyC;AACxD,UAAS,OAAmB,SAAkB,YAAkC;AAC/E,MAAI,MAAM,YAAA,8BACT,QAAO,aAAa,OAAO,SAAS,QAAQ;EAG7C,MAAM,SAAS,qBAAqB,CAAC,IAAI,MAAM,SAAS;AACxD,MAAI,CAAC,OACJ,QAAO,aAAa,OAAO,SAAS,QAAQ;AAG7C,MAAI,KAAK;GAAE,QAAQ;GAAyB,KAAK;GAAoC,CAAC;EAEtF,MAAM,SAAS,mCAAmC;EAElD,MAAM,SAA+B;GACpC,OAAO,MAAM;GACb,UAAU,QAAQ;GAClB,aAAa,SAAS;GACtB,WAAW,SAAS;GACpB,QAAQ,SAAS;GACjB;AAED,GAAM,YAAY;GACjB,MAAM,UAAU,qBAAqB,MAAM;GAC3C,IAAI,mBAAmB;GACvB,IAAI,aAAa;AAEjB,OAAI;AACH,eAAW,MAAM,SAAS,OAAO,aAAa,OAAO,EAAE;AACtD,SAAI,SAAS,QAAQ,SAAS;AAC7B,cAAQ,aAAa;AACrB,cAAQ,eAAe;AACvB,aAAO,KAAK;OAAE,MAAM;OAAS,QAAQ;OAAW,OAAO;OAAS,CAAC;AACjE;;AAGD,aAAQ,MAAM,MAAd;MACC,KAAK,QAAQ;AACZ,WAAI,CAAC,YAAY;AAChB,qBAAa;AACb,eAAO,KAAK;SAAE,MAAM;SAAS;SAAS,CAAC;;AAExC,WAAI,qBAAqB,IAAI;AAC5B,2BAAmB,QAAQ,QAAQ;AACnC,gBAAQ,QAAQ,KAAK;SAAE,MAAM;SAAQ,MAAM;SAAI,CAAC;AAChD,eAAO,KAAK;SAAE,MAAM;SAAc,cAAc;SAAkB;SAAS,CAAC;;OAE7E,MAAM,cAAc,QAAQ,QAAQ;AACpC,WAAI,aAAa,SAAS,UAAU,MAAM,MAAM;AAC/C,oBAAY,QAAQ,MAAM;AAC1B,eAAO,KAAK;SACX,MAAM;SACN,cAAc;SACd,OAAO,MAAM;SACb;SACA,CAAC;;AAEH;;MAED,KAAK;AACJ,WAAI,CAAC,YAAY;AAChB,qBAAa;AACb,eAAO,KAAK;SAAE,MAAM;SAAS;SAAS,CAAC;;AAExC,WAAI,qBAAqB,IAAI;QAC5B,MAAM,KAAK,QAAQ,QAAQ;AAC3B,YAAI,IAAI,SAAS,OAChB,QAAO,KAAK;SACX,MAAM;SACN,cAAc;SACd,SAAS,GAAG;SACZ;SACA,CAAC;AAEH,2BAAmB;;AAEpB,WAAI,MAAM,UAAU;QACnB,MAAM,MAAM,QAAQ,QAAQ;QAC5B,MAAM,WAAW;SAChB,MAAM;SACN,IAAI,MAAM,SAAS;SACnB,MAAM,MAAM,SAAS;SACrB,WAAW,mBAAmB,MAAM,SAAS,UAAU;SACvD;AACD,gBAAQ,QAAQ,KAAK,SAAS;AAC9B,eAAO,KAAK;SAAE,MAAM;SAAkB,cAAc;SAAK;SAAS,CAAC;AACnE,eAAO,KAAK;SACX,MAAM;SACN,cAAc;SACd,OAAO,MAAM,SAAS,aAAa;SACnC;SACA,CAAC;AACF,eAAO,KAAK;SAAE,MAAM;SAAgB,cAAc;SAAK;SAAU;SAAS,CAAC;;AAE5E;MAED,KAAK;AACJ,WAAI,MAAM,OAAO;QAChB,MAAM,YAAa,MAAM,MAAM,QAAQ,OAAc,MAAM,MAAM,SAAS;QAC1E,MAAM,aAAc,MAAM,MAAM,SAAS,OAAc,MAAM,MAAM,UAAU;AAC7E,gBAAQ,QAAQ;SACf,OAAO,MAAM,MAAM;SACnB,QAAQ,MAAM,MAAM;SACpB,WAAW;SACX,YAAY;SACZ,aAAa,MAAM,MAAM,SAAS,MAAM,MAAM,QAAQ,MAAM,MAAM;SAClE,MAAM;UACL,OAAO;UACP,QAAQ;UACR,WAAW;UACX,YAAY;UACZ,OAAO,YAAY;UACnB;SACD;;AAEF;MAED,KAAK;AACJ,WAAI,CAAC,YAAY;AAChB,qBAAa;AACb,eAAO,KAAK;SAAE,MAAM;SAAS;SAAS,CAAC;;AAExC,eAAQ,aAAa;AACrB,eAAQ,eAAe,MAAM,SAAS;AACtC,cAAO,KAAK;QAAE,MAAM;QAAS,QAAQ;QAAS,OAAO;QAAS,CAAC;AAC/D;MAED,KAAK,OACJ;;;AAIH,QAAI,qBAAqB,IAAI;KAC5B,MAAM,KAAK,QAAQ,QAAQ;AAC3B,SAAI,IAAI,SAAS,OAChB,QAAO,KAAK;MACX,MAAM;MACN,cAAc;MACd,SAAS,GAAG;MACZ;MACA,CAAC;;AAGJ,QAAI,CAAC,YAAY;AAChB,kBAAa;AACb,YAAO,KAAK;MAAE,MAAM;MAAS;MAAS,CAAC;;IAIxC,MAAM,aADe,QAAQ,QAAQ,MAAK,MAAK,EAAE,SAAS,WAAW,GACnC,YAAY;AAC9C,YAAQ,aAAa;AACrB,WAAO,KAAK;KAAE,MAAM;KAAQ,QAAQ;KAAY,SAAS;KAAS,CAAC;YAC3D,KAAK;AACb,QAAI,CAAC,WAAY,QAAO,KAAK;KAAE,MAAM;KAAS;KAAS,CAAC;AACxD,YAAQ,aAAa;AACrB,YAAQ,eAAe,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AACvE,WAAO,KAAK;KAAE,MAAM;KAAS,QAAQ;KAAS,OAAO;KAAS,CAAC;;MAE7D;AAEJ,SAAO"}
|
|
@@ -3,7 +3,12 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { type Model, type Api } from '@mariozechner/pi-ai';
|
|
5
5
|
import type { Config } from '../config/schema.js';
|
|
6
|
+
import type { ProviderModelDefinition } from '../extensions/types/providers.js';
|
|
6
7
|
export { getApiKeyFromEnv, PROVIDER_ENV_MAP } from './env-keys.js';
|
|
8
|
+
/** Sentinel base URL: model is served by an extension {@link ProviderPluginRegistry} provider. */
|
|
9
|
+
export declare const EXTENSION_PROVIDER_BASE_URL = "extension://provider-plugin";
|
|
10
|
+
/** Map a plugin registry model to the pi-ai {@link Model} shape. */
|
|
11
|
+
export declare function pluginModelToModel(providerId: string, definition: ProviderModelDefinition): Model<Api>;
|
|
7
12
|
/**
|
|
8
13
|
* Get API key synchronously: checks registry (models.json) first, then environment variables.
|
|
9
14
|
* Use this for Agent's getApiKey callback which must be synchronous.
|
|
@@ -26,13 +31,13 @@ export declare function getApiKey(provider: string): Promise<string | undefined>
|
|
|
26
31
|
export declare function isProviderConfiguredSync(provider: string): boolean;
|
|
27
32
|
export declare function isProviderConfigured(provider: string): Promise<boolean>;
|
|
28
33
|
/** Where runtime {@link getApiKey} resolves the key from (no secret values). */
|
|
29
|
-
export type ProviderActiveKeySource = 'none' | 'agent' | 'gateway' | 'oauth' | 'env' | 'models_json';
|
|
34
|
+
export type ProviderActiveKeySource = 'none' | 'agent' | 'gateway' | 'oauth' | 'env' | 'models_json' | 'extension';
|
|
30
35
|
export declare function getProviderActiveKeySource(provider: string): Promise<ProviderActiveKeySource>;
|
|
31
36
|
export declare function getConfiguredProviders(): Promise<string[]>;
|
|
32
37
|
export declare function getAllModels(): readonly Model<Api>[];
|
|
33
38
|
export declare function getAvailableModels(): Promise<readonly Model<Api>[]>;
|
|
34
39
|
export type { Model, Api } from '@mariozechner/pi-ai';
|
|
35
|
-
export type ProviderCategory = 'common' | 'specialty' | 'oauth' | 'enterprise';
|
|
40
|
+
export type ProviderCategory = 'common' | 'specialty' | 'oauth' | 'enterprise' | 'extension';
|
|
36
41
|
export interface ProviderMeta {
|
|
37
42
|
name: string;
|
|
38
43
|
category: ProviderCategory;
|