@astralyn/pi 0.85.1 → 0.85.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/CHANGELOG.md +28 -0
- package/dist/cli.js +94 -91
- package/dist/core/agent-session.js +2 -2
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/background/history.d.ts.map +1 -1
- package/dist/core/background/history.js +6 -0
- package/dist/core/background/history.js.map +1 -1
- package/dist/core/background/presentation.d.ts.map +1 -1
- package/dist/core/background/presentation.js +41 -4
- package/dist/core/background/presentation.js.map +1 -1
- package/dist/core/background/service.d.ts +2 -2
- package/dist/core/background/service.d.ts.map +1 -1
- package/dist/core/background/service.js +38 -15
- package/dist/core/background/service.js.map +1 -1
- package/dist/core/background/types.d.ts +9 -0
- package/dist/core/background/types.d.ts.map +1 -1
- package/dist/core/background/types.js +4 -0
- package/dist/core/background/types.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +5 -12
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +11 -19
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/compaction/settings.d.ts +27 -0
- package/dist/core/compaction/settings.d.ts.map +1 -0
- package/dist/core/compaction/settings.js +40 -0
- package/dist/core/compaction/settings.js.map +1 -0
- package/dist/core/keybindings.d.ts +15 -0
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +12 -0
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/model-config.d.ts +32 -10
- package/dist/core/model-config.d.ts.map +1 -1
- package/dist/core/model-config.js +38 -1
- package/dist/core/model-config.js.map +1 -1
- package/dist/core/model-runtime.d.ts +2 -0
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +4 -0
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/settings-manager.d.ts +4 -7
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +7 -6
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/tools/bash.js +1 -1
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/shell-execution.d.ts.map +1 -1
- package/dist/core/tools/shell-execution.js +3 -1
- package/dist/core/tools/shell-execution.js.map +1 -1
- package/dist/extensions/background/manager.d.ts +8 -5
- package/dist/extensions/background/manager.d.ts.map +1 -1
- package/dist/extensions/background/manager.js +115 -39
- package/dist/extensions/background/manager.js.map +1 -1
- package/dist/extensions/background/task-view.d.ts +5 -0
- package/dist/extensions/background/task-view.d.ts.map +1 -1
- package/dist/extensions/background/task-view.js +4 -0
- package/dist/extensions/background/task-view.js.map +1 -1
- package/dist/extensions/btw/controller.d.ts +1 -1
- package/dist/extensions/btw/controller.d.ts.map +1 -1
- package/dist/extensions/btw/controller.js +4 -4
- package/dist/extensions/btw/controller.js.map +1 -1
- package/dist/extensions/btw/index.js +3 -3
- package/dist/extensions/btw/index.js.map +1 -1
- package/dist/extensions/index.js +2 -2
- package/dist/extensions/index.js.map +1 -1
- package/dist/extensions/provider/catalog.d.ts +57 -0
- package/dist/extensions/provider/catalog.d.ts.map +1 -0
- package/dist/extensions/provider/catalog.js +181 -0
- package/dist/extensions/provider/catalog.js.map +1 -0
- package/dist/extensions/provider/compat-fields.d.ts +25 -0
- package/dist/extensions/provider/compat-fields.d.ts.map +1 -0
- package/dist/extensions/provider/compat-fields.js +148 -0
- package/dist/extensions/provider/compat-fields.js.map +1 -0
- package/dist/extensions/provider/configuration.d.ts +8 -0
- package/dist/extensions/provider/configuration.d.ts.map +1 -0
- package/dist/extensions/provider/configuration.js +21 -0
- package/dist/extensions/provider/configuration.js.map +1 -0
- package/dist/extensions/provider/connection.d.ts +14 -0
- package/dist/extensions/provider/connection.d.ts.map +1 -0
- package/dist/extensions/provider/connection.js +86 -0
- package/dist/extensions/provider/connection.js.map +1 -0
- package/dist/extensions/provider/constants.d.ts +38 -0
- package/dist/extensions/provider/constants.d.ts.map +1 -0
- package/dist/extensions/provider/constants.js +75 -0
- package/dist/extensions/provider/constants.js.map +1 -0
- package/dist/extensions/provider/index.d.ts +4 -0
- package/dist/extensions/provider/index.d.ts.map +1 -0
- package/dist/extensions/provider/index.js +65 -0
- package/dist/extensions/provider/index.js.map +1 -0
- package/dist/extensions/provider/probe.d.ts +34 -0
- package/dist/extensions/provider/probe.d.ts.map +1 -0
- package/dist/extensions/provider/probe.js +178 -0
- package/dist/extensions/provider/probe.js.map +1 -0
- package/dist/extensions/provider/refresh.d.ts +30 -0
- package/dist/extensions/provider/refresh.d.ts.map +1 -0
- package/dist/extensions/provider/refresh.js +66 -0
- package/dist/extensions/provider/refresh.js.map +1 -0
- package/dist/extensions/provider/store.d.ts +131 -0
- package/dist/extensions/provider/store.d.ts.map +1 -0
- package/dist/extensions/provider/store.js +503 -0
- package/dist/extensions/provider/store.js.map +1 -0
- package/dist/extensions/provider/ui/app.d.ts +25 -0
- package/dist/extensions/provider/ui/app.d.ts.map +1 -0
- package/dist/extensions/provider/ui/app.js +153 -0
- package/dist/extensions/provider/ui/app.js.map +1 -0
- package/dist/extensions/provider/ui/builtin-data.d.ts +53 -0
- package/dist/extensions/provider/ui/builtin-data.d.ts.map +1 -0
- package/dist/extensions/provider/ui/builtin-data.js +315 -0
- package/dist/extensions/provider/ui/builtin-data.js.map +1 -0
- package/dist/extensions/provider/ui/compat.d.ts +57 -0
- package/dist/extensions/provider/ui/compat.d.ts.map +1 -0
- package/dist/extensions/provider/ui/compat.js +427 -0
- package/dist/extensions/provider/ui/compat.js.map +1 -0
- package/dist/extensions/provider/ui/dialogs.d.ts +30 -0
- package/dist/extensions/provider/ui/dialogs.d.ts.map +1 -0
- package/dist/extensions/provider/ui/dialogs.js +89 -0
- package/dist/extensions/provider/ui/dialogs.js.map +1 -0
- package/dist/extensions/provider/ui/dictionary.d.ts +29 -0
- package/dist/extensions/provider/ui/dictionary.d.ts.map +1 -0
- package/dist/extensions/provider/ui/dictionary.js +385 -0
- package/dist/extensions/provider/ui/dictionary.js.map +1 -0
- package/dist/extensions/provider/ui/editor.d.ts +91 -0
- package/dist/extensions/provider/ui/editor.d.ts.map +1 -0
- package/dist/extensions/provider/ui/editor.js +667 -0
- package/dist/extensions/provider/ui/editor.js.map +1 -0
- package/dist/extensions/provider/ui/fetch-models.d.ts +39 -0
- package/dist/extensions/provider/ui/fetch-models.d.ts.map +1 -0
- package/dist/extensions/provider/ui/fetch-models.js +320 -0
- package/dist/extensions/provider/ui/fetch-models.js.map +1 -0
- package/dist/extensions/provider/ui/model-fields.d.ts +43 -0
- package/dist/extensions/provider/ui/model-fields.d.ts.map +1 -0
- package/dist/extensions/provider/ui/model-fields.js +441 -0
- package/dist/extensions/provider/ui/model-fields.js.map +1 -0
- package/dist/extensions/provider/ui/model-options.d.ts +93 -0
- package/dist/extensions/provider/ui/model-options.d.ts.map +1 -0
- package/dist/extensions/provider/ui/model-options.js +529 -0
- package/dist/extensions/provider/ui/model-options.js.map +1 -0
- package/dist/extensions/provider/ui/pane.d.ts +81 -0
- package/dist/extensions/provider/ui/pane.d.ts.map +1 -0
- package/dist/extensions/provider/ui/pane.js +3 -0
- package/dist/extensions/provider/ui/pane.js.map +1 -0
- package/dist/extensions/provider/ui/provider-fields.d.ts +60 -0
- package/dist/extensions/provider/ui/provider-fields.d.ts.map +1 -0
- package/dist/extensions/provider/ui/provider-fields.js +326 -0
- package/dist/extensions/provider/ui/provider-fields.js.map +1 -0
- package/dist/extensions/provider/ui/provider-list.d.ts +34 -0
- package/dist/extensions/provider/ui/provider-list.d.ts.map +1 -0
- package/dist/extensions/provider/ui/provider-list.js +162 -0
- package/dist/extensions/provider/ui/provider-list.js.map +1 -0
- package/dist/extensions/provider/ui/session.d.ts +20 -0
- package/dist/extensions/provider/ui/session.d.ts.map +1 -0
- package/dist/extensions/provider/ui/session.js +52 -0
- package/dist/extensions/provider/ui/session.js.map +1 -0
- package/dist/extensions/provider/ui/thinking-map.d.ts +20 -0
- package/dist/extensions/provider/ui/thinking-map.d.ts.map +1 -0
- package/dist/extensions/provider/ui/thinking-map.js +166 -0
- package/dist/extensions/provider/ui/thinking-map.js.map +1 -0
- package/dist/extensions/provider/ui/value-row.d.ts +88 -0
- package/dist/extensions/provider/ui/value-row.d.ts.map +1 -0
- package/dist/extensions/provider/ui/value-row.js +158 -0
- package/dist/extensions/provider/ui/value-row.js.map +1 -0
- package/dist/extensions/statusline/index.js +2 -2
- package/dist/extensions/statusline/index.js.map +1 -1
- package/dist/extensions/subagent/index.d.ts.map +1 -1
- package/dist/extensions/subagent/index.js +4 -2
- package/dist/extensions/subagent/index.js.map +1 -1
- package/dist/extensions/subagent/resolve.d.ts +5 -0
- package/dist/extensions/subagent/resolve.d.ts.map +1 -1
- package/dist/extensions/subagent/resolve.js +15 -7
- package/dist/extensions/subagent/resolve.js.map +1 -1
- package/dist/extensions/subagent/schema.d.ts +2 -0
- package/dist/extensions/subagent/schema.d.ts.map +1 -1
- package/dist/extensions/subagent/schema.js +1 -0
- package/dist/extensions/subagent/schema.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +10 -0
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +20 -2
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +6 -1
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/rpc-entry.js +94 -91
- package/docs/bundled/README.md +1 -1
- package/docs/bundled/extensions/background.md +5 -5
- package/docs/bundled/extensions/provider.md +117 -0
- package/docs/bundled/extensions/statusline.md +1 -1
- package/docs/bundled/extensions/subagent.md +7 -3
- package/docs/compaction.md +16 -6
- package/docs/docs.json +2 -2
- package/docs/keybindings.md +3 -0
- package/docs/settings.md +11 -7
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/dist/extensions/router/constants.d.ts +0 -23
- package/dist/extensions/router/constants.d.ts.map +0 -1
- package/dist/extensions/router/constants.js +0 -30
- package/dist/extensions/router/constants.js.map +0 -1
- package/dist/extensions/router/dialog-host.d.ts +0 -60
- package/dist/extensions/router/dialog-host.d.ts.map +0 -1
- package/dist/extensions/router/dialog-host.js +0 -196
- package/dist/extensions/router/dialog-host.js.map +0 -1
- package/dist/extensions/router/dialog.d.ts +0 -45
- package/dist/extensions/router/dialog.d.ts.map +0 -1
- package/dist/extensions/router/dialog.js +0 -378
- package/dist/extensions/router/dialog.js.map +0 -1
- package/dist/extensions/router/identity.d.ts +0 -15
- package/dist/extensions/router/identity.d.ts.map +0 -1
- package/dist/extensions/router/identity.js +0 -173
- package/dist/extensions/router/identity.js.map +0 -1
- package/dist/extensions/router/index.d.ts +0 -15
- package/dist/extensions/router/index.d.ts.map +0 -1
- package/dist/extensions/router/index.js +0 -70
- package/dist/extensions/router/index.js.map +0 -1
- package/dist/extensions/router/presets.d.ts +0 -51
- package/dist/extensions/router/presets.d.ts.map +0 -1
- package/dist/extensions/router/presets.js +0 -108
- package/dist/extensions/router/presets.js.map +0 -1
- package/dist/extensions/router/probe.d.ts +0 -26
- package/dist/extensions/router/probe.d.ts.map +0 -1
- package/dist/extensions/router/probe.js +0 -216
- package/dist/extensions/router/probe.js.map +0 -1
- package/dist/extensions/router/register.d.ts +0 -12
- package/dist/extensions/router/register.d.ts.map +0 -1
- package/dist/extensions/router/register.js +0 -90
- package/dist/extensions/router/register.js.map +0 -1
- package/dist/extensions/router/state.d.ts +0 -20
- package/dist/extensions/router/state.d.ts.map +0 -1
- package/dist/extensions/router/state.js +0 -169
- package/dist/extensions/router/state.js.map +0 -1
- package/dist/extensions/router/store.d.ts +0 -12
- package/dist/extensions/router/store.d.ts.map +0 -1
- package/dist/extensions/router/store.js +0 -266
- package/dist/extensions/router/store.js.map +0 -1
- package/dist/extensions/router/stream.d.ts +0 -12
- package/dist/extensions/router/stream.d.ts.map +0 -1
- package/dist/extensions/router/stream.js +0 -233
- package/dist/extensions/router/stream.js.map +0 -1
- package/dist/extensions/router/transport.d.ts +0 -9
- package/dist/extensions/router/transport.d.ts.map +0 -1
- package/dist/extensions/router/transport.js +0 -81
- package/dist/extensions/router/transport.js.map +0 -1
- package/dist/extensions/router/types.d.ts +0 -39
- package/dist/extensions/router/types.d.ts.map +0 -1
- package/dist/extensions/router/types.js +0 -2
- package/dist/extensions/router/types.js.map +0 -1
- package/dist/extensions/router/ui.d.ts +0 -26
- package/dist/extensions/router/ui.d.ts.map +0 -1
- package/dist/extensions/router/ui.js +0 -897
- package/dist/extensions/router/ui.js.map +0 -1
- package/docs/bundled/extensions/router.md +0 -197
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/** Saved provider configuration, canonical authentication, and catalog import operations. */
|
|
2
|
+
import { raceWithAbortSignal } from "../../utils/abort.js";
|
|
3
|
+
import { effectiveModelSettings } from "./configuration.js";
|
|
4
|
+
import { formatError, PROBE_LIMITS } from "./constants.js";
|
|
5
|
+
import { probeProviderModels } from "./probe.js";
|
|
6
|
+
export async function fetchProviderModels(options, signal) {
|
|
7
|
+
const { store, refresher, runtime, providerId } = options;
|
|
8
|
+
const deadline = AbortSignal.any([signal, AbortSignal.timeout(PROBE_LIMITS.timeoutMs)]);
|
|
9
|
+
try {
|
|
10
|
+
await raceWithAbortSignal(store.flush(), deadline);
|
|
11
|
+
const unsaved = store.getPendingError(providerId);
|
|
12
|
+
if (unsaved)
|
|
13
|
+
return { ok: false, error: `Save the provider changes before fetching: ${unsaved}` };
|
|
14
|
+
const provider = store.getProvider(providerId);
|
|
15
|
+
const settings = effectiveModelSettings(providerId, provider);
|
|
16
|
+
if (!settings.baseUrl)
|
|
17
|
+
return { ok: false, error: "Set a baseUrl under API Auth first." };
|
|
18
|
+
const sync = await refresher.refreshNow(providerId, deadline);
|
|
19
|
+
deadline.throwIfAborted();
|
|
20
|
+
if (!sync.ok)
|
|
21
|
+
return { ok: false, error: `The provider did not reload: ${sync.errors.join("; ")}` };
|
|
22
|
+
const resolved = await raceWithAbortSignal(runtime.getAuth(providerId, { signal: deadline }), deadline);
|
|
23
|
+
const configured = provider?.apiKey !== undefined ||
|
|
24
|
+
provider?.authHeader === true ||
|
|
25
|
+
Object.keys(provider?.headers ?? {}).length > 0 ||
|
|
26
|
+
runtime.getProviderAuthStatus(providerId).configured;
|
|
27
|
+
if (!resolved && configured) {
|
|
28
|
+
return {
|
|
29
|
+
ok: false,
|
|
30
|
+
error: "Configured credentials or headers did not resolve; check the provider authentication.",
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
const result = await probeProviderModels({
|
|
34
|
+
baseUrl: resolved?.auth.baseUrl ?? settings.baseUrl,
|
|
35
|
+
auth: resolved?.auth,
|
|
36
|
+
api: settings.api,
|
|
37
|
+
signal: deadline,
|
|
38
|
+
});
|
|
39
|
+
deadline.throwIfAborted();
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
if (deadline.aborted) {
|
|
44
|
+
return {
|
|
45
|
+
ok: false,
|
|
46
|
+
error: signal.aborted ? "Cancelled." : "Timed out while preparing or fetching the model catalog.",
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
return { ok: false, error: formatError(error) };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export async function importProviderModels(options, models, signal) {
|
|
53
|
+
const { store, refresher, providerId } = options;
|
|
54
|
+
await raceWithAbortSignal(store.flush(), signal);
|
|
55
|
+
const unsaved = store.getPendingError(providerId);
|
|
56
|
+
if (unsaved)
|
|
57
|
+
return unsaved;
|
|
58
|
+
const settings = effectiveModelSettings(providerId, store.getProvider(providerId));
|
|
59
|
+
if (!settings.api || !settings.baseUrl)
|
|
60
|
+
return "Set baseUrl and API Type under API Auth before importing models.";
|
|
61
|
+
const existing = new Set(store.getModels(providerId).map((model) => model.id));
|
|
62
|
+
const fresh = models.filter((model) => {
|
|
63
|
+
if (existing.has(model.id))
|
|
64
|
+
return false;
|
|
65
|
+
existing.add(model.id);
|
|
66
|
+
return true;
|
|
67
|
+
});
|
|
68
|
+
if (fresh.length === 0)
|
|
69
|
+
return undefined;
|
|
70
|
+
signal.throwIfAborted();
|
|
71
|
+
store.batch(() => {
|
|
72
|
+
for (const model of fresh)
|
|
73
|
+
store.addModel(providerId, model.name ? { id: model.id, name: model.name } : { id: model.id });
|
|
74
|
+
});
|
|
75
|
+
refresher.touch(providerId);
|
|
76
|
+
// These edits were confirmed. Finish the write even if the originating pane closes.
|
|
77
|
+
await store.flush();
|
|
78
|
+
const saveError = store.getPendingError(providerId);
|
|
79
|
+
if (saveError)
|
|
80
|
+
return saveError;
|
|
81
|
+
const outcome = await refresher.refreshNow(providerId, signal);
|
|
82
|
+
if (!outcome.ok)
|
|
83
|
+
return `Models were saved, but refresh failed: ${outcome.errors.join("; ")}`;
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=connection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection.js","sourceRoot":"","sources":["../../../src/extensions/provider/connection.ts"],"names":[],"mappings":"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","sourcesContent":["/** Saved provider configuration, canonical authentication, and catalog import operations. */\n\nimport type { ModelRuntime } from \"../../core/model-runtime.ts\";\nimport { raceWithAbortSignal } from \"../../utils/abort.ts\";\nimport { effectiveModelSettings } from \"./configuration.ts\";\nimport { formatError, PROBE_LIMITS } from \"./constants.ts\";\nimport { type ProbeModel, type ProbeResult, probeProviderModels } from \"./probe.ts\";\nimport type { RefreshCoordinator } from \"./refresh.ts\";\nimport type { ModelsJsonStore } from \"./store.ts\";\n\nexport interface ProviderConnectionOptions {\n\tstore: ModelsJsonStore;\n\trefresher: RefreshCoordinator;\n\tproviderId: string;\n\truntime: Pick<ModelRuntime, \"getAuth\" | \"getProviderAuthStatus\">;\n}\n\nexport async function fetchProviderModels(\n\toptions: ProviderConnectionOptions,\n\tsignal: AbortSignal,\n): Promise<ProbeResult> {\n\tconst { store, refresher, runtime, providerId } = options;\n\tconst deadline = AbortSignal.any([signal, AbortSignal.timeout(PROBE_LIMITS.timeoutMs)]);\n\ttry {\n\t\tawait raceWithAbortSignal(store.flush(), deadline);\n\t\tconst unsaved = store.getPendingError(providerId);\n\t\tif (unsaved) return { ok: false, error: `Save the provider changes before fetching: ${unsaved}` };\n\t\tconst provider = store.getProvider(providerId);\n\t\tconst settings = effectiveModelSettings(providerId, provider);\n\t\tif (!settings.baseUrl) return { ok: false, error: \"Set a baseUrl under API Auth first.\" };\n\t\tconst sync = await refresher.refreshNow(providerId, deadline);\n\t\tdeadline.throwIfAborted();\n\t\tif (!sync.ok) return { ok: false, error: `The provider did not reload: ${sync.errors.join(\"; \")}` };\n\t\tconst resolved = await raceWithAbortSignal(runtime.getAuth(providerId, { signal: deadline }), deadline);\n\t\tconst configured =\n\t\t\tprovider?.apiKey !== undefined ||\n\t\t\tprovider?.authHeader === true ||\n\t\t\tObject.keys(provider?.headers ?? {}).length > 0 ||\n\t\t\truntime.getProviderAuthStatus(providerId).configured;\n\t\tif (!resolved && configured) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: \"Configured credentials or headers did not resolve; check the provider authentication.\",\n\t\t\t};\n\t\t}\n\t\tconst result = await probeProviderModels({\n\t\t\tbaseUrl: resolved?.auth.baseUrl ?? settings.baseUrl,\n\t\t\tauth: resolved?.auth,\n\t\t\tapi: settings.api,\n\t\t\tsignal: deadline,\n\t\t});\n\t\tdeadline.throwIfAborted();\n\t\treturn result;\n\t} catch (error) {\n\t\tif (deadline.aborted) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: signal.aborted ? \"Cancelled.\" : \"Timed out while preparing or fetching the model catalog.\",\n\t\t\t};\n\t\t}\n\t\treturn { ok: false, error: formatError(error) };\n\t}\n}\n\nexport async function importProviderModels(\n\toptions: ProviderConnectionOptions,\n\tmodels: readonly ProbeModel[],\n\tsignal: AbortSignal,\n): Promise<string | undefined> {\n\tconst { store, refresher, providerId } = options;\n\tawait raceWithAbortSignal(store.flush(), signal);\n\tconst unsaved = store.getPendingError(providerId);\n\tif (unsaved) return unsaved;\n\tconst settings = effectiveModelSettings(providerId, store.getProvider(providerId));\n\tif (!settings.api || !settings.baseUrl) return \"Set baseUrl and API Type under API Auth before importing models.\";\n\tconst existing = new Set(store.getModels(providerId).map((model) => model.id));\n\tconst fresh = models.filter((model) => {\n\t\tif (existing.has(model.id)) return false;\n\t\texisting.add(model.id);\n\t\treturn true;\n\t});\n\tif (fresh.length === 0) return undefined;\n\tsignal.throwIfAborted();\n\tstore.batch(() => {\n\t\tfor (const model of fresh)\n\t\t\tstore.addModel(providerId, model.name ? { id: model.id, name: model.name } : { id: model.id });\n\t});\n\trefresher.touch(providerId);\n\t// These edits were confirmed. Finish the write even if the originating pane closes.\n\tawait store.flush();\n\tconst saveError = store.getPendingError(providerId);\n\tif (saveError) return saveError;\n\tconst outcome = await refresher.refreshNow(providerId, signal);\n\tif (!outcome.ok) return `Models were saved, but refresh failed: ${outcome.errors.join(\"; \")}`;\n\treturn undefined;\n}\n"]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/** Shared constants for the /provider extension. */
|
|
2
|
+
import type { Api, ModelThinkingLevel } from "@earendil-works/pi-ai";
|
|
3
|
+
export declare const COMMAND_NAME = "provider";
|
|
4
|
+
export declare const COMMAND_DESCRIPTION = "Manage models.json providers: connection, API type, and models";
|
|
5
|
+
export declare const NO_UI_WARNING = "/provider requires interactive TUI mode.";
|
|
6
|
+
export declare const NO_MODELS_FILE_WARNING = "models.json is disabled for this runtime (custom SDK configuration), so /provider is unavailable.";
|
|
7
|
+
/**
|
|
8
|
+
* API types offered in the single-select list. The four common custom-endpoint
|
|
9
|
+
* protocols come first; the rest follow in stable order. Custom api strings
|
|
10
|
+
* already present in models.json are preserved and appended at render time.
|
|
11
|
+
*/
|
|
12
|
+
export declare const API_TYPES: readonly Api[];
|
|
13
|
+
/** Input modality options. Adding a type also requires pi-ai Model.input and the models.json schema to support it. */
|
|
14
|
+
export declare const INPUT_TYPES: readonly ["text", "image"];
|
|
15
|
+
export type InputType = (typeof INPUT_TYPES)[number];
|
|
16
|
+
export declare const THINKING_LEVELS: readonly ModelThinkingLevel[];
|
|
17
|
+
/** GET {baseUrl}/models limits. */
|
|
18
|
+
export declare const PROBE_LIMITS: {
|
|
19
|
+
readonly timeoutMs: 10000;
|
|
20
|
+
readonly maxBodyBytes: number;
|
|
21
|
+
readonly maxErrorBytes: 4096;
|
|
22
|
+
readonly maxErrorChars: 400;
|
|
23
|
+
readonly maxModels: 2000;
|
|
24
|
+
};
|
|
25
|
+
/** Maximum reference candidates offered by Use Built-in Data. */
|
|
26
|
+
export declare const MAX_CANDIDATES = 8;
|
|
27
|
+
/** Runtime refresh deadline when leaving /provider or after a fetch import. */
|
|
28
|
+
export declare const REFRESH_TIMEOUT_MS = 15000;
|
|
29
|
+
export declare function formatError(error: unknown): string;
|
|
30
|
+
/** `1 entry` / `2 entries` — handles the -y → -ies case. */
|
|
31
|
+
export declare function plural(count: number, word: string): string;
|
|
32
|
+
/** Display truncate by characters (rows are width-truncated again at render time). */
|
|
33
|
+
export declare function truncate(value: string, max: number): string;
|
|
34
|
+
/** Mask a literal API key for display. Env/command references stay verbatim; the mask is never saved. */
|
|
35
|
+
export declare function maskApiKey(value: string): string;
|
|
36
|
+
/** True when the raw apiKey value is an env/command reference rather than a literal secret. */
|
|
37
|
+
export declare function isApiKeyReference(value: string): boolean;
|
|
38
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/extensions/provider/constants.ts"],"names":[],"mappings":"AAAA,oDAAoD;AAEpD,OAAO,KAAK,EAAE,GAAG,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAErE,eAAO,MAAM,YAAY,aAAa,CAAC;AACvC,eAAO,MAAM,mBAAmB,mEAAmE,CAAC;AACpG,eAAO,MAAM,aAAa,6CAA6C,CAAC;AACxE,eAAO,MAAM,sBAAsB,sGACiE,CAAC;AAErG;;;;GAIG;AACH,eAAO,MAAM,SAAS,EAAE,SAAS,GAAG,EAWnC,CAAC;AAEF,sHAAsH;AACtH,eAAO,MAAM,WAAW,4BAA6B,CAAC;AACtD,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AAErD,eAAO,MAAM,eAAe,EAAE,SAAS,kBAAkB,EAQxD,CAAC;AAEF,mCAAmC;AACnC,eAAO,MAAM,YAAY;;;;;;CAMf,CAAC;AAEX,iEAAiE;AACjE,eAAO,MAAM,cAAc,IAAI,CAAC;AAEhC,+EAA+E;AAC/E,eAAO,MAAM,kBAAkB,QAAS,CAAC;AAEzC,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAElD;AAED,4DAA4D;AAC5D,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAG1D;AAED,sFAAsF;AACtF,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAG3D;AAED,yGAAyG;AACzG,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKhD;AAED,+FAA+F;AAC/F,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAExD"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/** Shared constants for the /provider extension. */
|
|
2
|
+
export const COMMAND_NAME = "provider";
|
|
3
|
+
export const COMMAND_DESCRIPTION = "Manage models.json providers: connection, API type, and models";
|
|
4
|
+
export const NO_UI_WARNING = "/provider requires interactive TUI mode.";
|
|
5
|
+
export const NO_MODELS_FILE_WARNING = "models.json is disabled for this runtime (custom SDK configuration), so /provider is unavailable.";
|
|
6
|
+
/**
|
|
7
|
+
* API types offered in the single-select list. The four common custom-endpoint
|
|
8
|
+
* protocols come first; the rest follow in stable order. Custom api strings
|
|
9
|
+
* already present in models.json are preserved and appended at render time.
|
|
10
|
+
*/
|
|
11
|
+
export const API_TYPES = [
|
|
12
|
+
"openai-responses",
|
|
13
|
+
"openai-completions",
|
|
14
|
+
"anthropic-messages",
|
|
15
|
+
"google-generative-ai",
|
|
16
|
+
"azure-openai-responses",
|
|
17
|
+
"openai-codex-responses",
|
|
18
|
+
"mistral-conversations",
|
|
19
|
+
"google-vertex",
|
|
20
|
+
"bedrock-converse-stream",
|
|
21
|
+
"pi-messages",
|
|
22
|
+
];
|
|
23
|
+
/** Input modality options. Adding a type also requires pi-ai Model.input and the models.json schema to support it. */
|
|
24
|
+
export const INPUT_TYPES = ["text", "image"];
|
|
25
|
+
export const THINKING_LEVELS = [
|
|
26
|
+
"off",
|
|
27
|
+
"minimal",
|
|
28
|
+
"low",
|
|
29
|
+
"medium",
|
|
30
|
+
"high",
|
|
31
|
+
"xhigh",
|
|
32
|
+
"max",
|
|
33
|
+
];
|
|
34
|
+
/** GET {baseUrl}/models limits. */
|
|
35
|
+
export const PROBE_LIMITS = {
|
|
36
|
+
timeoutMs: 10_000,
|
|
37
|
+
maxBodyBytes: 4 * 1024 * 1024,
|
|
38
|
+
maxErrorBytes: 4_096,
|
|
39
|
+
maxErrorChars: 400,
|
|
40
|
+
maxModels: 2_000,
|
|
41
|
+
};
|
|
42
|
+
/** Maximum reference candidates offered by Use Built-in Data. */
|
|
43
|
+
export const MAX_CANDIDATES = 8;
|
|
44
|
+
/** Runtime refresh deadline when leaving /provider or after a fetch import. */
|
|
45
|
+
export const REFRESH_TIMEOUT_MS = 15_000;
|
|
46
|
+
export function formatError(error) {
|
|
47
|
+
return error instanceof Error ? error.message : String(error);
|
|
48
|
+
}
|
|
49
|
+
/** `1 entry` / `2 entries` — handles the -y → -ies case. */
|
|
50
|
+
export function plural(count, word) {
|
|
51
|
+
if (count === 1)
|
|
52
|
+
return word;
|
|
53
|
+
return word.endsWith("y") ? `${word.slice(0, -1)}ies` : `${word}s`;
|
|
54
|
+
}
|
|
55
|
+
/** Display truncate by characters (rows are width-truncated again at render time). */
|
|
56
|
+
export function truncate(value, max) {
|
|
57
|
+
if (value.length <= max)
|
|
58
|
+
return value;
|
|
59
|
+
return `${value.slice(0, Math.max(0, max - 1))}…`;
|
|
60
|
+
}
|
|
61
|
+
/** Mask a literal API key for display. Env/command references stay verbatim; the mask is never saved. */
|
|
62
|
+
export function maskApiKey(value) {
|
|
63
|
+
if (!value)
|
|
64
|
+
return value;
|
|
65
|
+
if (value.startsWith("$") || value.startsWith("!"))
|
|
66
|
+
return value;
|
|
67
|
+
if (value.length <= 8)
|
|
68
|
+
return "••••••";
|
|
69
|
+
return `••••••${value.slice(-4)}`;
|
|
70
|
+
}
|
|
71
|
+
/** True when the raw apiKey value is an env/command reference rather than a literal secret. */
|
|
72
|
+
export function isApiKeyReference(value) {
|
|
73
|
+
return value.startsWith("$") || value.startsWith("!");
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/extensions/provider/constants.ts"],"names":[],"mappings":"AAAA,oDAAoD;AAIpD,MAAM,CAAC,MAAM,YAAY,GAAG,UAAU,CAAC;AACvC,MAAM,CAAC,MAAM,mBAAmB,GAAG,gEAAgE,CAAC;AACpG,MAAM,CAAC,MAAM,aAAa,GAAG,0CAA0C,CAAC;AACxE,MAAM,CAAC,MAAM,sBAAsB,GAClC,mGAAmG,CAAC;AAErG;;;;GAIG;AACH,MAAM,CAAC,MAAM,SAAS,GAAmB;IACxC,kBAAkB;IAClB,oBAAoB;IACpB,oBAAoB;IACpB,sBAAsB;IACtB,wBAAwB;IACxB,wBAAwB;IACxB,uBAAuB;IACvB,eAAe;IACf,yBAAyB;IACzB,aAAa;CACb,CAAC;AAEF,sHAAsH;AACtH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,MAAM,EAAE,OAAO,CAAU,CAAC;AAGtD,MAAM,CAAC,MAAM,eAAe,GAAkC;IAC7D,KAAK;IACL,SAAS;IACT,KAAK;IACL,QAAQ;IACR,MAAM;IACN,OAAO;IACP,KAAK;CACL,CAAC;AAEF,mCAAmC;AACnC,MAAM,CAAC,MAAM,YAAY,GAAG;IAC3B,SAAS,EAAE,MAAM;IACjB,YAAY,EAAE,CAAC,GAAG,IAAI,GAAG,IAAI;IAC7B,aAAa,EAAE,KAAK;IACpB,aAAa,EAAE,GAAG;IAClB,SAAS,EAAE,KAAK;CACP,CAAC;AAEX,iEAAiE;AACjE,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC;AAEhC,+EAA+E;AAC/E,MAAM,CAAC,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAEzC,MAAM,UAAU,WAAW,CAAC,KAAc;IACzC,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC/D,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,MAAM,CAAC,KAAa,EAAE,IAAY;IACjD,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7B,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC;AACpE,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,QAAQ,CAAC,KAAa,EAAE,GAAW;IAClD,IAAI,KAAK,CAAC,MAAM,IAAI,GAAG;QAAE,OAAO,KAAK,CAAC;IACtC,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;AACnD,CAAC;AAED,yGAAyG;AACzG,MAAM,UAAU,UAAU,CAAC,KAAa;IACvC,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IACzB,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IACjE,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC;QAAE,OAAO,QAAQ,CAAC;IACvC,OAAO,SAAS,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACnC,CAAC;AAED,+FAA+F;AAC/F,MAAM,UAAU,iBAAiB,CAAC,KAAa;IAC9C,OAAO,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;AACvD,CAAC","sourcesContent":["/** Shared constants for the /provider extension. */\n\nimport type { Api, ModelThinkingLevel } from \"@earendil-works/pi-ai\";\n\nexport const COMMAND_NAME = \"provider\";\nexport const COMMAND_DESCRIPTION = \"Manage models.json providers: connection, API type, and models\";\nexport const NO_UI_WARNING = \"/provider requires interactive TUI mode.\";\nexport const NO_MODELS_FILE_WARNING =\n\t\"models.json is disabled for this runtime (custom SDK configuration), so /provider is unavailable.\";\n\n/**\n * API types offered in the single-select list. The four common custom-endpoint\n * protocols come first; the rest follow in stable order. Custom api strings\n * already present in models.json are preserved and appended at render time.\n */\nexport const API_TYPES: readonly Api[] = [\n\t\"openai-responses\",\n\t\"openai-completions\",\n\t\"anthropic-messages\",\n\t\"google-generative-ai\",\n\t\"azure-openai-responses\",\n\t\"openai-codex-responses\",\n\t\"mistral-conversations\",\n\t\"google-vertex\",\n\t\"bedrock-converse-stream\",\n\t\"pi-messages\",\n];\n\n/** Input modality options. Adding a type also requires pi-ai Model.input and the models.json schema to support it. */\nexport const INPUT_TYPES = [\"text\", \"image\"] as const;\nexport type InputType = (typeof INPUT_TYPES)[number];\n\nexport const THINKING_LEVELS: readonly ModelThinkingLevel[] = [\n\t\"off\",\n\t\"minimal\",\n\t\"low\",\n\t\"medium\",\n\t\"high\",\n\t\"xhigh\",\n\t\"max\",\n];\n\n/** GET {baseUrl}/models limits. */\nexport const PROBE_LIMITS = {\n\ttimeoutMs: 10_000,\n\tmaxBodyBytes: 4 * 1024 * 1024,\n\tmaxErrorBytes: 4_096,\n\tmaxErrorChars: 400,\n\tmaxModels: 2_000,\n} as const;\n\n/** Maximum reference candidates offered by Use Built-in Data. */\nexport const MAX_CANDIDATES = 8;\n\n/** Runtime refresh deadline when leaving /provider or after a fetch import. */\nexport const REFRESH_TIMEOUT_MS = 15_000;\n\nexport function formatError(error: unknown): string {\n\treturn error instanceof Error ? error.message : String(error);\n}\n\n/** `1 entry` / `2 entries` — handles the -y → -ies case. */\nexport function plural(count: number, word: string): string {\n\tif (count === 1) return word;\n\treturn word.endsWith(\"y\") ? `${word.slice(0, -1)}ies` : `${word}s`;\n}\n\n/** Display truncate by characters (rows are width-truncated again at render time). */\nexport function truncate(value: string, max: number): string {\n\tif (value.length <= max) return value;\n\treturn `${value.slice(0, Math.max(0, max - 1))}…`;\n}\n\n/** Mask a literal API key for display. Env/command references stay verbatim; the mask is never saved. */\nexport function maskApiKey(value: string): string {\n\tif (!value) return value;\n\tif (value.startsWith(\"$\") || value.startsWith(\"!\")) return value;\n\tif (value.length <= 8) return \"••••••\";\n\treturn `••••••${value.slice(-4)}`;\n}\n\n/** True when the raw apiKey value is an env/command reference rather than a literal secret. */\nexport function isApiKeyReference(value: string): boolean {\n\treturn value.startsWith(\"$\") || value.startsWith(\"!\");\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/extensions/provider/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAE9E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAYnE,MAAM,CAAC,OAAO,UAAU,iBAAiB,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CA4DhE"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/** /provider edits the current runtime's models.json through a native TUI. */
|
|
2
|
+
import { COMMAND_DESCRIPTION, COMMAND_NAME, formatError, NO_MODELS_FILE_WARNING, NO_UI_WARNING, plural, } from "./constants.js";
|
|
3
|
+
import { ModelsJsonStore } from "./store.js";
|
|
4
|
+
import { createProviderApp, createProviderErrorScreen } from "./ui/app.js";
|
|
5
|
+
export default function providerExtension(pi) {
|
|
6
|
+
let modelsPath;
|
|
7
|
+
pi.on("session_start", (_event, ctx) => {
|
|
8
|
+
modelsPath = ctx.modelRuntime.getModelsPath();
|
|
9
|
+
});
|
|
10
|
+
pi.registerCommand(COMMAND_NAME, {
|
|
11
|
+
description: COMMAND_DESCRIPTION,
|
|
12
|
+
getArgumentCompletions: async (prefix) => {
|
|
13
|
+
if (!modelsPath)
|
|
14
|
+
return null;
|
|
15
|
+
const load = await ModelsJsonStore.load(modelsPath);
|
|
16
|
+
if (!load.ok)
|
|
17
|
+
return null;
|
|
18
|
+
const query = prefix.trim().toLowerCase();
|
|
19
|
+
const items = load.store
|
|
20
|
+
.getProviderIds()
|
|
21
|
+
.filter((id) => id.toLowerCase().startsWith(query))
|
|
22
|
+
.map((id) => {
|
|
23
|
+
const count = load.store.getModels(id).length;
|
|
24
|
+
return {
|
|
25
|
+
value: id,
|
|
26
|
+
label: id,
|
|
27
|
+
description: `${String(count)} ${plural(count, "model")}`,
|
|
28
|
+
};
|
|
29
|
+
});
|
|
30
|
+
return items.length ? items : null;
|
|
31
|
+
},
|
|
32
|
+
handler: async (args, ctx) => {
|
|
33
|
+
if (!ctx.hasUI || ctx.mode !== "tui") {
|
|
34
|
+
ctx.ui.notify(NO_UI_WARNING, "warning");
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const path = ctx.modelRuntime.getModelsPath();
|
|
38
|
+
modelsPath = path;
|
|
39
|
+
if (!path) {
|
|
40
|
+
ctx.ui.notify(NO_MODELS_FILE_WARNING, "warning");
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
const load = await ModelsJsonStore.load(path);
|
|
45
|
+
if (!load.ok) {
|
|
46
|
+
await ctx.ui.custom((tui, _theme, _keybindings, done) => createProviderErrorScreen(tui, done, load.error));
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
await ctx.ui.custom((tui, theme, keybindings, done) => createProviderApp(tui, theme, keybindings, done, {
|
|
50
|
+
store: load.store,
|
|
51
|
+
runtime: ctx.modelRuntime,
|
|
52
|
+
registry: ctx.modelRegistry,
|
|
53
|
+
getCurrentModel: () => ctx.model,
|
|
54
|
+
setModel: (model) => pi.setModel(model),
|
|
55
|
+
notify: (message, type) => ctx.ui.notify(message, type),
|
|
56
|
+
initialProviderId: args.trim() || undefined,
|
|
57
|
+
}));
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
ctx.ui.notify(formatError(error), "error");
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/extensions/provider/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAG9E,OAAO,EACN,mBAAmB,EACnB,YAAY,EACZ,WAAW,EACX,sBAAsB,EACtB,aAAa,EACb,MAAM,GACN,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAE3E,MAAM,CAAC,OAAO,UAAU,iBAAiB,CAAC,EAAgB;IACzD,IAAI,UAA8B,CAAC;IACnC,EAAE,CAAC,EAAE,CAAC,eAAe,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE;QACtC,UAAU,GAAG,GAAG,CAAC,YAAY,CAAC,aAAa,EAAE,CAAC;IAC/C,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,eAAe,CAAC,YAAY,EAAE;QAChC,WAAW,EAAE,mBAAmB;QAChC,sBAAsB,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;YACxC,IAAI,CAAC,UAAU;gBAAE,OAAO,IAAI,CAAC;YAC7B,MAAM,IAAI,GAAG,MAAM,eAAe,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YACpD,IAAI,CAAC,IAAI,CAAC,EAAE;gBAAE,OAAO,IAAI,CAAC;YAC1B,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YAC1C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK;iBACtB,cAAc,EAAE;iBAChB,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;iBAClD,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE;gBACX,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC;gBAC9C,OAAO;oBACN,KAAK,EAAE,EAAE;oBACT,KAAK,EAAE,EAAE;oBACT,WAAW,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE;iBACzD,CAAC;YACH,CAAC,CAAC,CAAC;YACJ,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;QACpC,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE;YAC5B,IAAI,CAAC,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;gBACtC,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC;gBACxC,OAAO;YACR,CAAC;YACD,MAAM,IAAI,GAAG,GAAG,CAAC,YAAY,CAAC,aAAa,EAAE,CAAC;YAC9C,UAAU,GAAG,IAAI,CAAC;YAClB,IAAI,CAAC,IAAI,EAAE,CAAC;gBACX,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,sBAAsB,EAAE,SAAS,CAAC,CAAC;gBACjD,OAAO;YACR,CAAC;YACD,IAAI,CAAC;gBACJ,MAAM,IAAI,GAAG,MAAM,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAC9C,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;oBACd,MAAM,GAAG,CAAC,EAAE,CAAC,MAAM,CAAO,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,EAAE,CAC7D,yBAAyB,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAChD,CAAC;oBACF,OAAO;gBACR,CAAC;gBACD,MAAM,GAAG,CAAC,EAAE,CAAC,MAAM,CAAO,CAAC,GAAG,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE,CAC3D,iBAAiB,CAAC,GAAG,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE;oBAChD,KAAK,EAAE,IAAI,CAAC,KAAK;oBACjB,OAAO,EAAE,GAAG,CAAC,YAAY;oBACzB,QAAQ,EAAE,GAAG,CAAC,aAAa;oBAC3B,eAAe,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK;oBAChC,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC;oBACvC,MAAM,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC;oBACvD,iBAAiB,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,SAAS;iBAC3C,CAAC,CACF,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,CAAC;YAC5C,CAAC;QACF,CAAC;KACD,CAAC,CAAC;AACJ,CAAC","sourcesContent":["/** /provider edits the current runtime's models.json through a native TUI. */\n\nimport type { ExtensionAPI } from \"../../core/extensions/types.ts\";\nimport {\n\tCOMMAND_DESCRIPTION,\n\tCOMMAND_NAME,\n\tformatError,\n\tNO_MODELS_FILE_WARNING,\n\tNO_UI_WARNING,\n\tplural,\n} from \"./constants.ts\";\nimport { ModelsJsonStore } from \"./store.ts\";\nimport { createProviderApp, createProviderErrorScreen } from \"./ui/app.ts\";\n\nexport default function providerExtension(pi: ExtensionAPI): void {\n\tlet modelsPath: string | undefined;\n\tpi.on(\"session_start\", (_event, ctx) => {\n\t\tmodelsPath = ctx.modelRuntime.getModelsPath();\n\t});\n\tpi.registerCommand(COMMAND_NAME, {\n\t\tdescription: COMMAND_DESCRIPTION,\n\t\tgetArgumentCompletions: async (prefix) => {\n\t\t\tif (!modelsPath) return null;\n\t\t\tconst load = await ModelsJsonStore.load(modelsPath);\n\t\t\tif (!load.ok) return null;\n\t\t\tconst query = prefix.trim().toLowerCase();\n\t\t\tconst items = load.store\n\t\t\t\t.getProviderIds()\n\t\t\t\t.filter((id) => id.toLowerCase().startsWith(query))\n\t\t\t\t.map((id) => {\n\t\t\t\t\tconst count = load.store.getModels(id).length;\n\t\t\t\t\treturn {\n\t\t\t\t\t\tvalue: id,\n\t\t\t\t\t\tlabel: id,\n\t\t\t\t\t\tdescription: `${String(count)} ${plural(count, \"model\")}`,\n\t\t\t\t\t};\n\t\t\t\t});\n\t\t\treturn items.length ? items : null;\n\t\t},\n\t\thandler: async (args, ctx) => {\n\t\t\tif (!ctx.hasUI || ctx.mode !== \"tui\") {\n\t\t\t\tctx.ui.notify(NO_UI_WARNING, \"warning\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst path = ctx.modelRuntime.getModelsPath();\n\t\t\tmodelsPath = path;\n\t\t\tif (!path) {\n\t\t\t\tctx.ui.notify(NO_MODELS_FILE_WARNING, \"warning\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst load = await ModelsJsonStore.load(path);\n\t\t\t\tif (!load.ok) {\n\t\t\t\t\tawait ctx.ui.custom<void>((tui, _theme, _keybindings, done) =>\n\t\t\t\t\t\tcreateProviderErrorScreen(tui, done, load.error),\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tawait ctx.ui.custom<void>((tui, theme, keybindings, done) =>\n\t\t\t\t\tcreateProviderApp(tui, theme, keybindings, done, {\n\t\t\t\t\t\tstore: load.store,\n\t\t\t\t\t\truntime: ctx.modelRuntime,\n\t\t\t\t\t\tregistry: ctx.modelRegistry,\n\t\t\t\t\t\tgetCurrentModel: () => ctx.model,\n\t\t\t\t\t\tsetModel: (model) => pi.setModel(model),\n\t\t\t\t\t\tnotify: (message, type) => ctx.ui.notify(message, type),\n\t\t\t\t\t\tinitialProviderId: args.trim() || undefined,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t} catch (error) {\n\t\t\t\tctx.ui.notify(formatError(error), \"error\");\n\t\t\t}\n\t\t},\n\t});\n}\n"]}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetch the OpenAI-style model catalog from a configured provider endpoint.
|
|
3
|
+
*
|
|
4
|
+
* Auth is resolved by the caller through ModelRuntime.getAuth() so Pi's
|
|
5
|
+
* credential precedence (auth.json > env > models.json) applies. Raw
|
|
6
|
+
* `$VAR` / `!command` placeholders never reach the wire: a failed resolution
|
|
7
|
+
* aborts before this function is called.
|
|
8
|
+
*/
|
|
9
|
+
import type { FetchFunction, ModelAuth } from "@earendil-works/pi-ai";
|
|
10
|
+
export interface ProbeModel {
|
|
11
|
+
id: string;
|
|
12
|
+
name?: string;
|
|
13
|
+
}
|
|
14
|
+
export type ProbeResult = {
|
|
15
|
+
ok: true;
|
|
16
|
+
models: ProbeModel[];
|
|
17
|
+
truncated: boolean;
|
|
18
|
+
} | {
|
|
19
|
+
ok: false;
|
|
20
|
+
error: string;
|
|
21
|
+
};
|
|
22
|
+
export declare function probeProviderModels(opts: {
|
|
23
|
+
baseUrl: string;
|
|
24
|
+
/** Resolved auth from ModelRuntime.getAuth(). Undefined = anonymous public catalog. */
|
|
25
|
+
auth?: ModelAuth;
|
|
26
|
+
/** Effective provider api; selects the protocol's auth header scheme. */
|
|
27
|
+
api?: string;
|
|
28
|
+
fetch?: FetchFunction;
|
|
29
|
+
signal?: AbortSignal;
|
|
30
|
+
timeoutMs?: number;
|
|
31
|
+
}): Promise<ProbeResult>;
|
|
32
|
+
/** Append a path segment, keeping the query string and avoiding duplicate slashes or a doubled /v1. */
|
|
33
|
+
export declare function modelCatalogUrl(base: URL, api?: string): URL;
|
|
34
|
+
//# sourceMappingURL=probe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"probe.d.ts","sourceRoot":"","sources":["../../../src/extensions/provider/probe.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAmB,MAAM,uBAAuB,CAAC;AAKvF,MAAM,WAAW,UAAU;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,WAAW,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,UAAU,EAAE,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhH,wBAAsB,mBAAmB,CAAC,IAAI,EAAE;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,uFAAuF;IACvF,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,yEAAyE;IACzE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,WAAW,CAAC,CAmEvB;AAED,uGAAuG;AACvG,wBAAgB,eAAe,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,GAAG,CAK5D"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetch the OpenAI-style model catalog from a configured provider endpoint.
|
|
3
|
+
*
|
|
4
|
+
* Auth is resolved by the caller through ModelRuntime.getAuth() so Pi's
|
|
5
|
+
* credential precedence (auth.json > env > models.json) applies. Raw
|
|
6
|
+
* `$VAR` / `!command` placeholders never reach the wire: a failed resolution
|
|
7
|
+
* aborts before this function is called.
|
|
8
|
+
*/
|
|
9
|
+
import { stripTerminalSequences } from "@earendil-works/pi-tui";
|
|
10
|
+
import { readResponseTextBounded } from "../../utils/http-response.js";
|
|
11
|
+
import { formatError, PROBE_LIMITS } from "./constants.js";
|
|
12
|
+
export async function probeProviderModels(opts) {
|
|
13
|
+
let baseUrl;
|
|
14
|
+
try {
|
|
15
|
+
baseUrl = new URL(opts.baseUrl);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return { ok: false, error: "Base URL is not a valid URL." };
|
|
19
|
+
}
|
|
20
|
+
if (baseUrl.protocol !== "http:" && baseUrl.protocol !== "https:") {
|
|
21
|
+
return { ok: false, error: `Unsupported protocol: ${baseUrl.protocol}` };
|
|
22
|
+
}
|
|
23
|
+
if (baseUrl.username || baseUrl.password || baseUrl.hash) {
|
|
24
|
+
return { ok: false, error: "Base URL must not contain credentials or a fragment." };
|
|
25
|
+
}
|
|
26
|
+
if (opts.signal?.aborted)
|
|
27
|
+
return { ok: false, error: "Cancelled." };
|
|
28
|
+
const controller = new AbortController();
|
|
29
|
+
const timeoutMs = opts.timeoutMs ?? PROBE_LIMITS.timeoutMs;
|
|
30
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
31
|
+
const onOuterAbort = () => controller.abort();
|
|
32
|
+
opts.signal?.addEventListener("abort", onOuterAbort, { once: true });
|
|
33
|
+
try {
|
|
34
|
+
const url = modelCatalogUrl(baseUrl, opts.api);
|
|
35
|
+
const headers = buildHeaders(opts.auth, opts.api);
|
|
36
|
+
const doFetch = opts.fetch ?? globalThis.fetch;
|
|
37
|
+
const response = await doFetch(url, { method: "GET", headers, signal: controller.signal });
|
|
38
|
+
if (!response.ok) {
|
|
39
|
+
const body = await readResponseTextBounded(response, {
|
|
40
|
+
maxBytes: PROBE_LIMITS.maxErrorBytes,
|
|
41
|
+
signal: controller.signal,
|
|
42
|
+
});
|
|
43
|
+
return {
|
|
44
|
+
ok: false,
|
|
45
|
+
error: `HTTP ${response.status}${body ? `: ${safeErrorText(body, opts.auth).slice(0, PROBE_LIMITS.maxErrorChars)}` : ""}`,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
const text = await readResponseTextBounded(response, {
|
|
49
|
+
maxBytes: PROBE_LIMITS.maxBodyBytes,
|
|
50
|
+
overflowMessage: `Response exceeds ${PROBE_LIMITS.maxBodyBytes} bytes.`,
|
|
51
|
+
signal: controller.signal,
|
|
52
|
+
});
|
|
53
|
+
let json;
|
|
54
|
+
try {
|
|
55
|
+
json = JSON.parse(text);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return { ok: false, error: "Model catalog response is not JSON." };
|
|
59
|
+
}
|
|
60
|
+
const models = parseCatalogModels(json);
|
|
61
|
+
if (models === null) {
|
|
62
|
+
return {
|
|
63
|
+
ok: false,
|
|
64
|
+
error: "JSON has no supported OpenAI-style data[] or models[] catalog with model ids. Add models manually if this endpoint uses another catalog format.",
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
const sorted = dedupeSort(models);
|
|
68
|
+
const morePages = typeof json === "object" && json !== null && "has_more" in json && json.has_more === true;
|
|
69
|
+
const truncated = sorted.length > PROBE_LIMITS.maxModels || morePages;
|
|
70
|
+
return { ok: true, models: sorted.slice(0, PROBE_LIMITS.maxModels), truncated };
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
if (controller.signal.aborted) {
|
|
74
|
+
return { ok: false, error: opts.signal?.aborted ? "Cancelled." : `Timed out after ${timeoutMs}ms.` };
|
|
75
|
+
}
|
|
76
|
+
return { ok: false, error: safeErrorText(formatError(error), opts.auth) };
|
|
77
|
+
}
|
|
78
|
+
finally {
|
|
79
|
+
clearTimeout(timer);
|
|
80
|
+
opts.signal?.removeEventListener("abort", onOuterAbort);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/** Append a path segment, keeping the query string and avoiding duplicate slashes or a doubled /v1. */
|
|
84
|
+
export function modelCatalogUrl(base, api) {
|
|
85
|
+
const url = new URL(base.href);
|
|
86
|
+
const path = url.pathname.replace(/\/+$/, "");
|
|
87
|
+
url.pathname = api === "anthropic-messages" && !path.endsWith("/v1") ? `${path}/v1/models` : `${path}/models`;
|
|
88
|
+
return url;
|
|
89
|
+
}
|
|
90
|
+
function safeErrorText(text, auth) {
|
|
91
|
+
for (const value of [auth?.apiKey, ...Object.values(auth?.headers ?? {})]) {
|
|
92
|
+
if (typeof value !== "string" || !value)
|
|
93
|
+
continue;
|
|
94
|
+
text = text.replaceAll(value, "[redacted]");
|
|
95
|
+
const token = value.replace(/^Bearer\s+/i, "");
|
|
96
|
+
if (token !== value && token)
|
|
97
|
+
text = text.replaceAll(token, "[redacted]");
|
|
98
|
+
}
|
|
99
|
+
return stripTerminalSequences(text);
|
|
100
|
+
}
|
|
101
|
+
function buildHeaders(auth, api) {
|
|
102
|
+
const headers = new Headers();
|
|
103
|
+
headers.set("accept", "application/json");
|
|
104
|
+
const configured = auth?.headers ?? {};
|
|
105
|
+
if (api === "anthropic-messages")
|
|
106
|
+
headers.set("anthropic-version", "2023-06-01");
|
|
107
|
+
let hasAuthorization = false;
|
|
108
|
+
for (const [key, value] of Object.entries(configured)) {
|
|
109
|
+
// null explicitly removes a header; it also suppresses the default auth header.
|
|
110
|
+
if (key.toLowerCase() === "authorization")
|
|
111
|
+
hasAuthorization = true;
|
|
112
|
+
if (typeof value === "string")
|
|
113
|
+
headers.set(key, value);
|
|
114
|
+
else if (value === null)
|
|
115
|
+
headers.delete(key);
|
|
116
|
+
}
|
|
117
|
+
headers.set("accept", "application/json");
|
|
118
|
+
if (!auth?.apiKey || hasAuthorization)
|
|
119
|
+
return headers;
|
|
120
|
+
const lower = (name) => Object.keys(configured).some((key) => key.toLowerCase() === name);
|
|
121
|
+
if (api === "anthropic-messages") {
|
|
122
|
+
// Mirror pi-ai: OAuth tokens use Bearer; plain keys use x-api-key + version.
|
|
123
|
+
if (auth.apiKey.includes("sk-ant-oat")) {
|
|
124
|
+
headers.set("authorization", `Bearer ${auth.apiKey}`);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
if (!lower("x-api-key"))
|
|
128
|
+
headers.set("x-api-key", auth.apiKey);
|
|
129
|
+
if (!lower("anthropic-version"))
|
|
130
|
+
headers.set("anthropic-version", "2023-06-01");
|
|
131
|
+
}
|
|
132
|
+
return headers;
|
|
133
|
+
}
|
|
134
|
+
if (api === "google-generative-ai" || api === "google-vertex") {
|
|
135
|
+
if (!lower("x-goog-api-key"))
|
|
136
|
+
headers.set("x-goog-api-key", auth.apiKey);
|
|
137
|
+
return headers;
|
|
138
|
+
}
|
|
139
|
+
headers.set("authorization", `Bearer ${auth.apiKey}`);
|
|
140
|
+
return headers;
|
|
141
|
+
}
|
|
142
|
+
function parseCatalogModels(json) {
|
|
143
|
+
if (!json || typeof json !== "object")
|
|
144
|
+
return null;
|
|
145
|
+
const payload = json;
|
|
146
|
+
const data = Array.isArray(json) ? json : Array.isArray(payload.models) ? payload.models : payload.data;
|
|
147
|
+
if (!Array.isArray(data))
|
|
148
|
+
return null;
|
|
149
|
+
const models = [];
|
|
150
|
+
for (const item of data) {
|
|
151
|
+
if (!item || typeof item !== "object")
|
|
152
|
+
continue;
|
|
153
|
+
const record = item;
|
|
154
|
+
const id = (typeof record.id === "string" ? record.id.trim() : "") ||
|
|
155
|
+
(typeof record.slug === "string" ? record.slug.trim() : "");
|
|
156
|
+
if (!id)
|
|
157
|
+
continue;
|
|
158
|
+
// OpenAI uses `name`; Anthropic's /v1/models uses `display_name`.
|
|
159
|
+
const displayName = typeof record.name === "string" && record.name.trim() ? record.name.trim() : undefined;
|
|
160
|
+
const anthropicName = typeof record.display_name === "string" && record.display_name.trim()
|
|
161
|
+
? record.display_name.trim()
|
|
162
|
+
: typeof record.displayName === "string"
|
|
163
|
+
? record.displayName.trim()
|
|
164
|
+
: undefined;
|
|
165
|
+
const name = (displayName ?? anthropicName) !== id ? (displayName ?? anthropicName) : undefined;
|
|
166
|
+
models.push(name ? { id, name } : { id });
|
|
167
|
+
}
|
|
168
|
+
return data.length > 0 && models.length === 0 ? null : models;
|
|
169
|
+
}
|
|
170
|
+
function dedupeSort(models) {
|
|
171
|
+
const map = new Map();
|
|
172
|
+
for (const model of models) {
|
|
173
|
+
if (!map.has(model.id))
|
|
174
|
+
map.set(model.id, model);
|
|
175
|
+
}
|
|
176
|
+
return [...map.values()].sort((a, b) => a.id.localeCompare(b.id));
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=probe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"probe.js","sourceRoot":"","sources":["../../../src/extensions/provider/probe.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Fetch the OpenAI-style model catalog from a configured provider endpoint.\n *\n * Auth is resolved by the caller through ModelRuntime.getAuth() so Pi's\n * credential precedence (auth.json > env > models.json) applies. Raw\n * `$VAR` / `!command` placeholders never reach the wire: a failed resolution\n * aborts before this function is called.\n */\n\nimport type { FetchFunction, ModelAuth, ProviderHeaders } from \"@earendil-works/pi-ai\";\nimport { stripTerminalSequences } from \"@earendil-works/pi-tui\";\nimport { readResponseTextBounded } from \"../../utils/http-response.ts\";\nimport { formatError, PROBE_LIMITS } from \"./constants.ts\";\n\nexport interface ProbeModel {\n\tid: string;\n\tname?: string;\n}\n\nexport type ProbeResult = { ok: true; models: ProbeModel[]; truncated: boolean } | { ok: false; error: string };\n\nexport async function probeProviderModels(opts: {\n\tbaseUrl: string;\n\t/** Resolved auth from ModelRuntime.getAuth(). Undefined = anonymous public catalog. */\n\tauth?: ModelAuth;\n\t/** Effective provider api; selects the protocol's auth header scheme. */\n\tapi?: string;\n\tfetch?: FetchFunction;\n\tsignal?: AbortSignal;\n\ttimeoutMs?: number;\n}): Promise<ProbeResult> {\n\tlet baseUrl: URL;\n\ttry {\n\t\tbaseUrl = new URL(opts.baseUrl);\n\t} catch {\n\t\treturn { ok: false, error: \"Base URL is not a valid URL.\" };\n\t}\n\tif (baseUrl.protocol !== \"http:\" && baseUrl.protocol !== \"https:\") {\n\t\treturn { ok: false, error: `Unsupported protocol: ${baseUrl.protocol}` };\n\t}\n\tif (baseUrl.username || baseUrl.password || baseUrl.hash) {\n\t\treturn { ok: false, error: \"Base URL must not contain credentials or a fragment.\" };\n\t}\n\tif (opts.signal?.aborted) return { ok: false, error: \"Cancelled.\" };\n\n\tconst controller = new AbortController();\n\tconst timeoutMs = opts.timeoutMs ?? PROBE_LIMITS.timeoutMs;\n\tconst timer = setTimeout(() => controller.abort(), timeoutMs);\n\tconst onOuterAbort = () => controller.abort();\n\topts.signal?.addEventListener(\"abort\", onOuterAbort, { once: true });\n\n\ttry {\n\t\tconst url = modelCatalogUrl(baseUrl, opts.api);\n\t\tconst headers = buildHeaders(opts.auth, opts.api);\n\t\tconst doFetch = opts.fetch ?? globalThis.fetch;\n\t\tconst response = await doFetch(url, { method: \"GET\", headers, signal: controller.signal });\n\t\tif (!response.ok) {\n\t\t\tconst body = await readResponseTextBounded(response, {\n\t\t\t\tmaxBytes: PROBE_LIMITS.maxErrorBytes,\n\t\t\t\tsignal: controller.signal,\n\t\t\t});\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: `HTTP ${response.status}${body ? `: ${safeErrorText(body, opts.auth).slice(0, PROBE_LIMITS.maxErrorChars)}` : \"\"}`,\n\t\t\t};\n\t\t}\n\t\tconst text = await readResponseTextBounded(response, {\n\t\t\tmaxBytes: PROBE_LIMITS.maxBodyBytes,\n\t\t\toverflowMessage: `Response exceeds ${PROBE_LIMITS.maxBodyBytes} bytes.`,\n\t\t\tsignal: controller.signal,\n\t\t});\n\t\tlet json: unknown;\n\t\ttry {\n\t\t\tjson = JSON.parse(text);\n\t\t} catch {\n\t\t\treturn { ok: false, error: \"Model catalog response is not JSON.\" };\n\t\t}\n\t\tconst models = parseCatalogModels(json);\n\t\tif (models === null) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: \"JSON has no supported OpenAI-style data[] or models[] catalog with model ids. Add models manually if this endpoint uses another catalog format.\",\n\t\t\t};\n\t\t}\n\t\tconst sorted = dedupeSort(models);\n\t\tconst morePages = typeof json === \"object\" && json !== null && \"has_more\" in json && json.has_more === true;\n\t\tconst truncated = sorted.length > PROBE_LIMITS.maxModels || morePages;\n\t\treturn { ok: true, models: sorted.slice(0, PROBE_LIMITS.maxModels), truncated };\n\t} catch (error) {\n\t\tif (controller.signal.aborted) {\n\t\t\treturn { ok: false, error: opts.signal?.aborted ? \"Cancelled.\" : `Timed out after ${timeoutMs}ms.` };\n\t\t}\n\t\treturn { ok: false, error: safeErrorText(formatError(error), opts.auth) };\n\t} finally {\n\t\tclearTimeout(timer);\n\t\topts.signal?.removeEventListener(\"abort\", onOuterAbort);\n\t}\n}\n\n/** Append a path segment, keeping the query string and avoiding duplicate slashes or a doubled /v1. */\nexport function modelCatalogUrl(base: URL, api?: string): URL {\n\tconst url = new URL(base.href);\n\tconst path = url.pathname.replace(/\\/+$/, \"\");\n\turl.pathname = api === \"anthropic-messages\" && !path.endsWith(\"/v1\") ? `${path}/v1/models` : `${path}/models`;\n\treturn url;\n}\n\nfunction safeErrorText(text: string, auth: ModelAuth | undefined): string {\n\tfor (const value of [auth?.apiKey, ...Object.values(auth?.headers ?? {})]) {\n\t\tif (typeof value !== \"string\" || !value) continue;\n\t\ttext = text.replaceAll(value, \"[redacted]\");\n\t\tconst token = value.replace(/^Bearer\\s+/i, \"\");\n\t\tif (token !== value && token) text = text.replaceAll(token, \"[redacted]\");\n\t}\n\treturn stripTerminalSequences(text);\n}\n\nfunction buildHeaders(auth: ModelAuth | undefined, api: string | undefined): Headers {\n\tconst headers = new Headers();\n\theaders.set(\"accept\", \"application/json\");\n\tconst configured: ProviderHeaders = auth?.headers ?? {};\n\tif (api === \"anthropic-messages\") headers.set(\"anthropic-version\", \"2023-06-01\");\n\tlet hasAuthorization = false;\n\tfor (const [key, value] of Object.entries(configured)) {\n\t\t// null explicitly removes a header; it also suppresses the default auth header.\n\t\tif (key.toLowerCase() === \"authorization\") hasAuthorization = true;\n\t\tif (typeof value === \"string\") headers.set(key, value);\n\t\telse if (value === null) headers.delete(key);\n\t}\n\theaders.set(\"accept\", \"application/json\");\n\tif (!auth?.apiKey || hasAuthorization) return headers;\n\tconst lower = (name: string) => Object.keys(configured).some((key) => key.toLowerCase() === name);\n\tif (api === \"anthropic-messages\") {\n\t\t// Mirror pi-ai: OAuth tokens use Bearer; plain keys use x-api-key + version.\n\t\tif (auth.apiKey.includes(\"sk-ant-oat\")) {\n\t\t\theaders.set(\"authorization\", `Bearer ${auth.apiKey}`);\n\t\t} else {\n\t\t\tif (!lower(\"x-api-key\")) headers.set(\"x-api-key\", auth.apiKey);\n\t\t\tif (!lower(\"anthropic-version\")) headers.set(\"anthropic-version\", \"2023-06-01\");\n\t\t}\n\t\treturn headers;\n\t}\n\tif (api === \"google-generative-ai\" || api === \"google-vertex\") {\n\t\tif (!lower(\"x-goog-api-key\")) headers.set(\"x-goog-api-key\", auth.apiKey);\n\t\treturn headers;\n\t}\n\theaders.set(\"authorization\", `Bearer ${auth.apiKey}`);\n\treturn headers;\n}\n\nfunction parseCatalogModels(json: unknown): ProbeModel[] | null {\n\tif (!json || typeof json !== \"object\") return null;\n\tconst payload = json as { models?: unknown; data?: unknown };\n\tconst data = Array.isArray(json) ? json : Array.isArray(payload.models) ? payload.models : payload.data;\n\tif (!Array.isArray(data)) return null;\n\tconst models: ProbeModel[] = [];\n\tfor (const item of data) {\n\t\tif (!item || typeof item !== \"object\") continue;\n\t\tconst record = item as Record<string, unknown>;\n\t\tconst id =\n\t\t\t(typeof record.id === \"string\" ? record.id.trim() : \"\") ||\n\t\t\t(typeof record.slug === \"string\" ? record.slug.trim() : \"\");\n\t\tif (!id) continue;\n\t\t// OpenAI uses `name`; Anthropic's /v1/models uses `display_name`.\n\t\tconst displayName = typeof record.name === \"string\" && record.name.trim() ? record.name.trim() : undefined;\n\t\tconst anthropicName =\n\t\t\ttypeof record.display_name === \"string\" && record.display_name.trim()\n\t\t\t\t? record.display_name.trim()\n\t\t\t\t: typeof record.displayName === \"string\"\n\t\t\t\t\t? record.displayName.trim()\n\t\t\t\t\t: undefined;\n\t\tconst name = (displayName ?? anthropicName) !== id ? (displayName ?? anthropicName) : undefined;\n\t\tmodels.push(name ? { id, name } : { id });\n\t}\n\treturn data.length > 0 && models.length === 0 ? null : models;\n}\n\nfunction dedupeSort(models: ProbeModel[]): ProbeModel[] {\n\tconst map = new Map<string, ProbeModel>();\n\tfor (const model of models) {\n\t\tif (!map.has(model.id)) map.set(model.id, model);\n\t}\n\treturn [...map.values()].sort((a, b) => a.id.localeCompare(b.id));\n}\n"]}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime refresh coordination for /provider.
|
|
3
|
+
*
|
|
4
|
+
* Saves hit disk immediately; runtime refreshes are deferred: normal field
|
|
5
|
+
* edits mark the provider as touched and flush when the session closes, while
|
|
6
|
+
* Fetch Models refreshes the current provider right before auth resolution
|
|
7
|
+
* and right after importing. Save failures and refresh failures are reported
|
|
8
|
+
* separately so a successful write is never misreported.
|
|
9
|
+
*/
|
|
10
|
+
import type { ModelRuntime } from "../../core/model-runtime.ts";
|
|
11
|
+
export interface RefreshOutcome {
|
|
12
|
+
ok: boolean;
|
|
13
|
+
aborted: boolean;
|
|
14
|
+
errors: string[];
|
|
15
|
+
}
|
|
16
|
+
export declare class RefreshCoordinator {
|
|
17
|
+
private readonly runtime;
|
|
18
|
+
private readonly pending;
|
|
19
|
+
private readonly changed;
|
|
20
|
+
private revision;
|
|
21
|
+
constructor(runtime: Pick<ModelRuntime, "refresh" | "getError">);
|
|
22
|
+
touch(providerId: string): void;
|
|
23
|
+
get touchedProviders(): readonly string[];
|
|
24
|
+
/** Offline-scoped refresh of a single provider (fetch prep / post-import). */
|
|
25
|
+
refreshNow(providerId: string, signal?: AbortSignal): Promise<RefreshOutcome>;
|
|
26
|
+
/** Refresh everything touched, including deleted provider ids. */
|
|
27
|
+
flush(signal?: AbortSignal): Promise<RefreshOutcome>;
|
|
28
|
+
private run;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=refresh.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refresh.d.ts","sourceRoot":"","sources":["../../../src/extensions/provider/refresh.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAGhE,MAAM,WAAW,cAAc;IAC9B,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,qBAAa,kBAAkB;IAC9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6C;IACrE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6B;IACrD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAC7C,OAAO,CAAC,QAAQ,CAAK;gBAET,OAAO,EAAE,IAAI,CAAC,YAAY,EAAE,SAAS,GAAG,UAAU,CAAC;IAI/D,KAAK,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAK/B,IAAI,gBAAgB,IAAI,SAAS,MAAM,EAAE,CAExC;IAED,8EAA8E;IACxE,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,cAAc,CAAC;IAInF,kEAAkE;IAC5D,KAAK,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,cAAc,CAAC;YAM5C,GAAG;CAyBjB"}
|