@ashx-j/lunr 0.1.4 → 0.1.5
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/builtin-extensions/lunr-local-providers/index.d.ts.map +1 -1
- package/dist/builtin-extensions/lunr-local-providers/index.js +25 -3
- package/dist/builtin-extensions/lunr-local-providers/index.js.map +1 -1
- package/dist/builtin-extensions/lunr-local-providers/local-servers.d.ts.map +1 -1
- package/dist/builtin-extensions/lunr-local-providers/local-servers.js +4 -2
- package/dist/builtin-extensions/lunr-local-providers/local-servers.js.map +1 -1
- package/dist/builtin-extensions/pi-ollama-cloud/index.d.ts +2 -3
- package/dist/builtin-extensions/pi-ollama-cloud/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-ollama-cloud/index.js +4 -25
- package/dist/builtin-extensions/pi-ollama-cloud/index.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-execution.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-execution.js +3 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-execution.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.js +4 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js +4 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js +20 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/dynamic-fanout.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/dynamic-fanout.js +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/dynamic-fanout.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.d.ts +2 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/pi-args.d.ts +2 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/pi-args.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/pi-args.js +2 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/pi-args.js.map +1 -1
- package/dist/catalog/models.json +1 -1
- package/dist/catalog/providers/amazon-bedrock.json +1 -1
- package/dist/catalog/providers/huggingface.json +1 -1
- package/dist/catalog/providers/opencode-go.json +1 -1
- package/dist/catalog/providers/openrouter.json +1 -1
- package/dist/catalog/providers/together.json +1 -1
- package/dist/catalog/providers/vercel-ai-gateway.json +1 -1
- package/dist/catalog/publication.json +3 -3
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +19 -4
- package/dist/cli.js.map +1 -1
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +4 -0
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +39 -7
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/catalog-merge.d.ts.map +1 -1
- package/dist/core/catalog-merge.js +4 -1
- package/dist/core/catalog-merge.js.map +1 -1
- package/dist/core/grok-cli-auth.d.ts +13 -3
- package/dist/core/grok-cli-auth.d.ts.map +1 -1
- package/dist/core/grok-cli-auth.js +130 -21
- package/dist/core/grok-cli-auth.js.map +1 -1
- package/dist/core/model-runtime.d.ts +6 -0
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +38 -10
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/official-catalog.d.ts +8 -1
- package/dist/core/official-catalog.d.ts.map +1 -1
- package/dist/core/official-catalog.js +38 -11
- package/dist/core/official-catalog.js.map +1 -1
- package/dist/core/plan-mode.d.ts +2 -0
- package/dist/core/plan-mode.d.ts.map +1 -1
- package/dist/core/plan-mode.js +7 -0
- package/dist/core/plan-mode.js.map +1 -1
- package/dist/core/subagent-permission-inherit.d.ts +38 -0
- package/dist/core/subagent-permission-inherit.d.ts.map +1 -0
- package/dist/core/subagent-permission-inherit.js +110 -0
- package/dist/core/subagent-permission-inherit.js.map +1 -0
- package/dist/core/usage-service.d.ts.map +1 -1
- package/dist/core/usage-service.js +5 -5
- package/dist/core/usage-service.js.map +1 -1
- package/dist/gateway/command.d.ts.map +1 -1
- package/dist/gateway/command.js +2 -1
- package/dist/gateway/command.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +4 -0
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +18 -8
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/builtin-extensions/lunr-local-providers/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAyC,MAAM,iCAAiC,CAAC;AAuB3G,MAAM,CAAC,OAAO,UAAU,kBAAkB,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/builtin-extensions/lunr-local-providers/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAyC,MAAM,iCAAiC,CAAC;AAuB3G,MAAM,CAAC,OAAO,UAAU,kBAAkB,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAsGjE","sourcesContent":["/**\n * lunR local providers — one-click Ollama and LM Studio.\n *\n * Each local server is registered as an OAuth-style provider where the OAuth\n * credential is a dummy token standing in for the keyless local server. This\n * turns /login into a one-click \"detect and connect\" flow: selecting the\n * provider probes localhost (3s timeout), stores the dummy credential through\n * the standard login persistence path (Models.login → CredentialStore.modify),\n * refreshes the model list, and auto-selects the first model when no real\n * model is active (mirroring completeProviderAuthentication).\n *\n * Behavior when a server is offline: the model list refreshes to empty and\n * the provider becomes unavailable; nothing throws.\n *\n * Known quirk (documented, not fixed): Ollama tool-calling over the\n * OpenAI-compatible /v1 endpoint can break streaming for some models.\n */\n\nimport type { ExtensionAPI, ExtensionContext, ProviderModelConfig } from \"@ashx-j/lunr\";\nimport { fetchLocalModelIds, LOCAL_SERVERS, type LocalServerSpec, toModelConfig } from \"./local-servers.ts\";\n\nconst LOGIN_PROBE_TIMEOUT_MS = 3000;\nconst REFRESH_TIMEOUT_MS = 3000;\nconst AUTO_SELECT_POLL_MS = 100;\nconst AUTO_SELECT_MAX_WAIT_MS = 5000;\n/** Dummy local credentials never expire; far-future timestamp (2100-01-01). */\nconst NEVER_EXPIRES = 4102444800000;\n\ninterface ProviderState {\n\tspec: LocalServerSpec;\n\t/** Last model list seen from the server; empty when offline. */\n\tmodels: ProviderModelConfig[];\n\t/** Set by the login flow; consumed by the next successful refresh to trigger auto-select. */\n\tjustLoggedIn: boolean;\n}\n\n/** Same placeholder check interactive-mode uses before auto-selecting a model after login. */\nfunction isUnknownModel(model: { provider: string; id: string; api: string } | undefined): boolean {\n\treturn !!model && model.provider === \"unknown\" && model.id === \"unknown\" && model.api === \"unknown\";\n}\n\nexport default function lunrLocalProviders(pi: ExtensionAPI): void {\n\t// Latest event context, kept fresh so the post-login auto-select can see\n\t// the current model and the availability snapshot.\n\tlet lastCtx: ExtensionContext | undefined;\n\tpi.on(\"session_start\", (_event, ctx) => {\n\t\tlastCtx = ctx;\n\t});\n\tpi.on(\"model_select\", (_event, ctx) => {\n\t\tlastCtx = ctx;\n\t});\n\n\tfor (const spec of LOCAL_SERVERS) {\n\t\tconst state: ProviderState = { spec, models: [], justLoggedIn: false };\n\n\t\t/**\n\t\t * After a successful login the runtime re-refresh may still be in\n\t\t * flight, so poll the availability snapshot briefly, then select the\n\t\t * first local model — but only when the session has no real model,\n\t\t * mirroring completeProviderAuthentication.\n\t\t */\n\t\tconst scheduleAutoSelect = (): void => {\n\t\t\tconst started = Date.now();\n\t\t\tconst timer = setInterval(() => {\n\t\t\t\tconst ctx = lastCtx;\n\t\t\t\tif (!ctx) {\n\t\t\t\t\tclearInterval(timer);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst first = ctx.modelRegistry.getAvailable().find((model) => model.provider === spec.providerId);\n\t\t\t\tif (!first) {\n\t\t\t\t\tif (Date.now() - started > AUTO_SELECT_MAX_WAIT_MS) clearInterval(timer);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tclearInterval(timer);\n\t\t\t\tconst current = ctx.model;\n\t\t\t\tif (current && !isUnknownModel(current)) return;\n\t\t\t\tvoid pi.setModel(first);\n\t\t\t}, AUTO_SELECT_POLL_MS);\n\t\t\ttimer.unref();\n\t\t};\n\n\t\tpi.registerProvider(spec.providerId, {\n\t\t\tname: spec.displayName,\n\t\t\tbaseUrl: spec.baseUrl,\n\t\t\tapi: \"openai-completions\",\n\t\t\tmodels: state.models,\n\t\t\trefreshModels: async (context) => {\n\t\t\t\t// Cache-only refresh must not probe localhost. A hung IPv6\n\t\t\t\t// localhost on Windows can sit until REFRESH_TIMEOUT_MS.\n\t\t\t\tif (!context.allowNetwork) {\n\t\t\t\t\tconst stored = await context.store.read();\n\t\t\t\t\tif (stored?.models?.length) {\n\t\t\t\t\t\tstate.models = stored.models.map((model) => toModelConfig(model.id));\n\t\t\t\t\t}\n\t\t\t\t\treturn state.models;\n\t\t\t\t}\n\t\t\t\tconst ids = await fetchLocalModelIds(spec, REFRESH_TIMEOUT_MS, context.signal);\n\t\t\t\tstate.models = (ids ?? []).map(toModelConfig);\n\t\t\t\tif (state.models.length > 0) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait context.store.write({\n\t\t\t\t\t\t\tmodels: state.models.map((model) => ({\n\t\t\t\t\t\t\t\t...model,\n\t\t\t\t\t\t\t\tapi: \"openai-completions\" as const,\n\t\t\t\t\t\t\t\tprovider: spec.providerId,\n\t\t\t\t\t\t\t\tbaseUrl: spec.baseUrl,\n\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t\tcheckedAt: Date.now(),\n\t\t\t\t\t\t});\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Store write is best-effort.\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (state.justLoggedIn && state.models.length > 0) {\n\t\t\t\t\tstate.justLoggedIn = false;\n\t\t\t\t\tscheduleAutoSelect();\n\t\t\t\t}\n\t\t\t\treturn state.models;\n\t\t\t},\n\t\t\toauth: {\n\t\t\t\tname: spec.displayName,\n\t\t\t\tlogin: async (callbacks) => {\n\t\t\t\t\tcallbacks.onProgress?.(`Looking for ${spec.productName} on localhost:${spec.port}...`);\n\t\t\t\t\tconst ids = await fetchLocalModelIds(spec, LOGIN_PROBE_TIMEOUT_MS, callbacks.signal);\n\t\t\t\t\tif (ids === null) {\n\t\t\t\t\t\tthrow new Error(`${spec.productName} not detected on localhost:${spec.port} — start it and retry.`);\n\t\t\t\t\t}\n\t\t\t\t\tif (ids.length === 0) {\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t`${spec.productName} is running on localhost:${spec.port} but has no models. Load a model and retry.`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tstate.models = ids.map(toModelConfig);\n\t\t\t\t\tstate.justLoggedIn = true;\n\t\t\t\t\t// Persisted by the standard login flow (Models.login → CredentialStore.modify).\n\t\t\t\t\treturn { refresh: spec.dummyKey, access: spec.dummyKey, expires: NEVER_EXPIRES };\n\t\t\t\t},\n\t\t\t\trefreshToken: async (credentials) => credentials,\n\t\t\t\tgetApiKey: () => spec.dummyKey,\n\t\t\t},\n\t\t});\n\t}\n}\n"]}
|
|
@@ -72,11 +72,33 @@ export default function lunrLocalProviders(pi) {
|
|
|
72
72
|
api: "openai-completions",
|
|
73
73
|
models: state.models,
|
|
74
74
|
refreshModels: async (context) => {
|
|
75
|
-
//
|
|
76
|
-
//
|
|
77
|
-
|
|
75
|
+
// Cache-only refresh must not probe localhost. A hung IPv6
|
|
76
|
+
// localhost on Windows can sit until REFRESH_TIMEOUT_MS.
|
|
77
|
+
if (!context.allowNetwork) {
|
|
78
|
+
const stored = await context.store.read();
|
|
79
|
+
if (stored?.models?.length) {
|
|
80
|
+
state.models = stored.models.map((model) => toModelConfig(model.id));
|
|
81
|
+
}
|
|
82
|
+
return state.models;
|
|
83
|
+
}
|
|
78
84
|
const ids = await fetchLocalModelIds(spec, REFRESH_TIMEOUT_MS, context.signal);
|
|
79
85
|
state.models = (ids ?? []).map(toModelConfig);
|
|
86
|
+
if (state.models.length > 0) {
|
|
87
|
+
try {
|
|
88
|
+
await context.store.write({
|
|
89
|
+
models: state.models.map((model) => ({
|
|
90
|
+
...model,
|
|
91
|
+
api: "openai-completions",
|
|
92
|
+
provider: spec.providerId,
|
|
93
|
+
baseUrl: spec.baseUrl,
|
|
94
|
+
})),
|
|
95
|
+
checkedAt: Date.now(),
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
// Store write is best-effort.
|
|
100
|
+
}
|
|
101
|
+
}
|
|
80
102
|
if (state.justLoggedIn && state.models.length > 0) {
|
|
81
103
|
state.justLoggedIn = false;
|
|
82
104
|
scheduleAutoSelect();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/builtin-extensions/lunr-local-providers/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAwB,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAE5G,MAAM,sBAAsB,GAAG,IAAI,CAAC;AACpC,MAAM,kBAAkB,GAAG,IAAI,CAAC;AAChC,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAChC,MAAM,uBAAuB,GAAG,IAAI,CAAC;AACrC,+EAA+E;AAC/E,MAAM,aAAa,GAAG,aAAa,CAAC;AAUpC,8FAA8F;AAC9F,SAAS,cAAc,CAAC,KAAgE,EAAW;IAClG,OAAO,CAAC,CAAC,KAAK,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS,IAAI,KAAK,CAAC,EAAE,KAAK,SAAS,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS,CAAC;AAAA,CACpG;AAED,MAAM,CAAC,OAAO,UAAU,kBAAkB,CAAC,EAAgB,EAAQ;IAClE,yEAAyE;IACzE,mDAAmD;IACnD,IAAI,OAAqC,CAAC;IAC1C,EAAE,CAAC,EAAE,CAAC,eAAe,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC;QACvC,OAAO,GAAG,GAAG,CAAC;IAAA,CACd,CAAC,CAAC;IACH,EAAE,CAAC,EAAE,CAAC,cAAc,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC;QACtC,OAAO,GAAG,GAAG,CAAC;IAAA,CACd,CAAC,CAAC;IAEH,KAAK,MAAM,IAAI,IAAI,aAAa,EAAE,CAAC;QAClC,MAAM,KAAK,GAAkB,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;QAEvE;;;;;WAKG;QACH,MAAM,kBAAkB,GAAG,GAAS,EAAE,CAAC;YACtC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAC3B,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;gBAC/B,MAAM,GAAG,GAAG,OAAO,CAAC;gBACpB,IAAI,CAAC,GAAG,EAAE,CAAC;oBACV,aAAa,CAAC,KAAK,CAAC,CAAC;oBACrB,OAAO;gBACR,CAAC;gBACD,MAAM,KAAK,GAAG,GAAG,CAAC,aAAa,CAAC,YAAY,EAAE,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,KAAK,IAAI,CAAC,UAAU,CAAC,CAAC;gBACnG,IAAI,CAAC,KAAK,EAAE,CAAC;oBACZ,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,GAAG,uBAAuB;wBAAE,aAAa,CAAC,KAAK,CAAC,CAAC;oBACzE,OAAO;gBACR,CAAC;gBACD,aAAa,CAAC,KAAK,CAAC,CAAC;gBACrB,MAAM,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC;gBAC1B,IAAI,OAAO,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC;oBAAE,OAAO;gBAChD,KAAK,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;YAAA,CACxB,EAAE,mBAAmB,CAAC,CAAC;YACxB,KAAK,CAAC,KAAK,EAAE,CAAC;QAAA,CACd,CAAC;QAEF,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,UAAU,EAAE;YACpC,IAAI,EAAE,IAAI,CAAC,WAAW;YACtB,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,GAAG,EAAE,oBAAoB;YACzB,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,aAAa,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,CAAC;gBACjC,8DAA8D;gBAC9D,gEAAgE;gBAChE,4DAA4D;gBAC5D,MAAM,GAAG,GAAG,MAAM,kBAAkB,CAAC,IAAI,EAAE,kBAAkB,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;gBAC/E,KAAK,CAAC,MAAM,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;gBAC9C,IAAI,KAAK,CAAC,YAAY,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACnD,KAAK,CAAC,YAAY,GAAG,KAAK,CAAC;oBAC3B,kBAAkB,EAAE,CAAC;gBACtB,CAAC;gBACD,OAAO,KAAK,CAAC,MAAM,CAAC;YAAA,CACpB;YACD,KAAK,EAAE;gBACN,IAAI,EAAE,IAAI,CAAC,WAAW;gBACtB,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,CAAC;oBAC3B,SAAS,CAAC,UAAU,EAAE,CAAC,eAAe,IAAI,CAAC,WAAW,iBAAiB,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC;oBACvF,MAAM,GAAG,GAAG,MAAM,kBAAkB,CAAC,IAAI,EAAE,sBAAsB,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC;oBACrF,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;wBAClB,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,CAAC,WAAW,8BAA8B,IAAI,CAAC,IAAI,0BAAwB,CAAC,CAAC;oBACrG,CAAC;oBACD,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;wBACtB,MAAM,IAAI,KAAK,CACd,GAAG,IAAI,CAAC,WAAW,4BAA4B,IAAI,CAAC,IAAI,6CAA6C,CACrG,CAAC;oBACH,CAAC;oBACD,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;oBACtC,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC;oBAC1B,kFAAgF;oBAChF,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC;gBAAA,CACjF;gBACD,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,CAAC,WAAW;gBAChD,SAAS,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ;aAC9B;SACD,CAAC,CAAC;IACJ,CAAC;AAAA,CACD","sourcesContent":["/**\n * lunR local providers — one-click Ollama and LM Studio.\n *\n * Each local server is registered as an OAuth-style provider where the OAuth\n * credential is a dummy token standing in for the keyless local server. This\n * turns /login into a one-click \"detect and connect\" flow: selecting the\n * provider probes localhost (3s timeout), stores the dummy credential through\n * the standard login persistence path (Models.login → CredentialStore.modify),\n * refreshes the model list, and auto-selects the first model when no real\n * model is active (mirroring completeProviderAuthentication).\n *\n * Behavior when a server is offline: the model list refreshes to empty and\n * the provider becomes unavailable; nothing throws.\n *\n * Known quirk (documented, not fixed): Ollama tool-calling over the\n * OpenAI-compatible /v1 endpoint can break streaming for some models.\n */\n\nimport type { ExtensionAPI, ExtensionContext, ProviderModelConfig } from \"@ashx-j/lunr\";\nimport { fetchLocalModelIds, LOCAL_SERVERS, type LocalServerSpec, toModelConfig } from \"./local-servers.ts\";\n\nconst LOGIN_PROBE_TIMEOUT_MS = 3000;\nconst REFRESH_TIMEOUT_MS = 3000;\nconst AUTO_SELECT_POLL_MS = 100;\nconst AUTO_SELECT_MAX_WAIT_MS = 5000;\n/** Dummy local credentials never expire; far-future timestamp (2100-01-01). */\nconst NEVER_EXPIRES = 4102444800000;\n\ninterface ProviderState {\n\tspec: LocalServerSpec;\n\t/** Last model list seen from the server; empty when offline. */\n\tmodels: ProviderModelConfig[];\n\t/** Set by the login flow; consumed by the next successful refresh to trigger auto-select. */\n\tjustLoggedIn: boolean;\n}\n\n/** Same placeholder check interactive-mode uses before auto-selecting a model after login. */\nfunction isUnknownModel(model: { provider: string; id: string; api: string } | undefined): boolean {\n\treturn !!model && model.provider === \"unknown\" && model.id === \"unknown\" && model.api === \"unknown\";\n}\n\nexport default function lunrLocalProviders(pi: ExtensionAPI): void {\n\t// Latest event context, kept fresh so the post-login auto-select can see\n\t// the current model and the availability snapshot.\n\tlet lastCtx: ExtensionContext | undefined;\n\tpi.on(\"session_start\", (_event, ctx) => {\n\t\tlastCtx = ctx;\n\t});\n\tpi.on(\"model_select\", (_event, ctx) => {\n\t\tlastCtx = ctx;\n\t});\n\n\tfor (const spec of LOCAL_SERVERS) {\n\t\tconst state: ProviderState = { spec, models: [], justLoggedIn: false };\n\n\t\t/**\n\t\t * After a successful login the runtime re-refresh may still be in\n\t\t * flight, so poll the availability snapshot briefly, then select the\n\t\t * first local model — but only when the session has no real model,\n\t\t * mirroring completeProviderAuthentication.\n\t\t */\n\t\tconst scheduleAutoSelect = (): void => {\n\t\t\tconst started = Date.now();\n\t\t\tconst timer = setInterval(() => {\n\t\t\t\tconst ctx = lastCtx;\n\t\t\t\tif (!ctx) {\n\t\t\t\t\tclearInterval(timer);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst first = ctx.modelRegistry.getAvailable().find((model) => model.provider === spec.providerId);\n\t\t\t\tif (!first) {\n\t\t\t\t\tif (Date.now() - started > AUTO_SELECT_MAX_WAIT_MS) clearInterval(timer);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tclearInterval(timer);\n\t\t\t\tconst current = ctx.model;\n\t\t\t\tif (current && !isUnknownModel(current)) return;\n\t\t\t\tvoid pi.setModel(first);\n\t\t\t}, AUTO_SELECT_POLL_MS);\n\t\t\ttimer.unref();\n\t\t};\n\n\t\tpi.registerProvider(spec.providerId, {\n\t\t\tname: spec.displayName,\n\t\t\tbaseUrl: spec.baseUrl,\n\t\t\tapi: \"openai-completions\",\n\t\t\tmodels: state.models,\n\t\t\trefreshModels: async (context) => {\n\t\t\t\t// Probe even when allowNetwork is false: localhost fails fast\n\t\t\t\t// (connection refused in ~ms), so the registration-time offline\n\t\t\t\t// refresh still populates models whenever the server is up.\n\t\t\t\tconst ids = await fetchLocalModelIds(spec, REFRESH_TIMEOUT_MS, context.signal);\n\t\t\t\tstate.models = (ids ?? []).map(toModelConfig);\n\t\t\t\tif (state.justLoggedIn && state.models.length > 0) {\n\t\t\t\t\tstate.justLoggedIn = false;\n\t\t\t\t\tscheduleAutoSelect();\n\t\t\t\t}\n\t\t\t\treturn state.models;\n\t\t\t},\n\t\t\toauth: {\n\t\t\t\tname: spec.displayName,\n\t\t\t\tlogin: async (callbacks) => {\n\t\t\t\t\tcallbacks.onProgress?.(`Looking for ${spec.productName} on localhost:${spec.port}...`);\n\t\t\t\t\tconst ids = await fetchLocalModelIds(spec, LOGIN_PROBE_TIMEOUT_MS, callbacks.signal);\n\t\t\t\t\tif (ids === null) {\n\t\t\t\t\t\tthrow new Error(`${spec.productName} not detected on localhost:${spec.port} — start it and retry.`);\n\t\t\t\t\t}\n\t\t\t\t\tif (ids.length === 0) {\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t`${spec.productName} is running on localhost:${spec.port} but has no models. Load a model and retry.`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tstate.models = ids.map(toModelConfig);\n\t\t\t\t\tstate.justLoggedIn = true;\n\t\t\t\t\t// Persisted by the standard login flow (Models.login → CredentialStore.modify).\n\t\t\t\t\treturn { refresh: spec.dummyKey, access: spec.dummyKey, expires: NEVER_EXPIRES };\n\t\t\t\t},\n\t\t\t\trefreshToken: async (credentials) => credentials,\n\t\t\t\tgetApiKey: () => spec.dummyKey,\n\t\t\t},\n\t\t});\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/builtin-extensions/lunr-local-providers/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * lunR local providers — one-click Ollama and LM Studio.\n *\n * Each local server is registered as an OAuth-style provider where the OAuth\n * credential is a dummy token standing in for the keyless local server. This\n * turns /login into a one-click \"detect and connect\" flow: selecting the\n * provider probes localhost (3s timeout), stores the dummy credential through\n * the standard login persistence path (Models.login → CredentialStore.modify),\n * refreshes the model list, and auto-selects the first model when no real\n * model is active (mirroring completeProviderAuthentication).\n *\n * Behavior when a server is offline: the model list refreshes to empty and\n * the provider becomes unavailable; nothing throws.\n *\n * Known quirk (documented, not fixed): Ollama tool-calling over the\n * OpenAI-compatible /v1 endpoint can break streaming for some models.\n */\n\nimport type { ExtensionAPI, ExtensionContext, ProviderModelConfig } from \"@ashx-j/lunr\";\nimport { fetchLocalModelIds, LOCAL_SERVERS, type LocalServerSpec, toModelConfig } from \"./local-servers.ts\";\n\nconst LOGIN_PROBE_TIMEOUT_MS = 3000;\nconst REFRESH_TIMEOUT_MS = 3000;\nconst AUTO_SELECT_POLL_MS = 100;\nconst AUTO_SELECT_MAX_WAIT_MS = 5000;\n/** Dummy local credentials never expire; far-future timestamp (2100-01-01). */\nconst NEVER_EXPIRES = 4102444800000;\n\ninterface ProviderState {\n\tspec: LocalServerSpec;\n\t/** Last model list seen from the server; empty when offline. */\n\tmodels: ProviderModelConfig[];\n\t/** Set by the login flow; consumed by the next successful refresh to trigger auto-select. */\n\tjustLoggedIn: boolean;\n}\n\n/** Same placeholder check interactive-mode uses before auto-selecting a model after login. */\nfunction isUnknownModel(model: { provider: string; id: string; api: string } | undefined): boolean {\n\treturn !!model && model.provider === \"unknown\" && model.id === \"unknown\" && model.api === \"unknown\";\n}\n\nexport default function lunrLocalProviders(pi: ExtensionAPI): void {\n\t// Latest event context, kept fresh so the post-login auto-select can see\n\t// the current model and the availability snapshot.\n\tlet lastCtx: ExtensionContext | undefined;\n\tpi.on(\"session_start\", (_event, ctx) => {\n\t\tlastCtx = ctx;\n\t});\n\tpi.on(\"model_select\", (_event, ctx) => {\n\t\tlastCtx = ctx;\n\t});\n\n\tfor (const spec of LOCAL_SERVERS) {\n\t\tconst state: ProviderState = { spec, models: [], justLoggedIn: false };\n\n\t\t/**\n\t\t * After a successful login the runtime re-refresh may still be in\n\t\t * flight, so poll the availability snapshot briefly, then select the\n\t\t * first local model — but only when the session has no real model,\n\t\t * mirroring completeProviderAuthentication.\n\t\t */\n\t\tconst scheduleAutoSelect = (): void => {\n\t\t\tconst started = Date.now();\n\t\t\tconst timer = setInterval(() => {\n\t\t\t\tconst ctx = lastCtx;\n\t\t\t\tif (!ctx) {\n\t\t\t\t\tclearInterval(timer);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst first = ctx.modelRegistry.getAvailable().find((model) => model.provider === spec.providerId);\n\t\t\t\tif (!first) {\n\t\t\t\t\tif (Date.now() - started > AUTO_SELECT_MAX_WAIT_MS) clearInterval(timer);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tclearInterval(timer);\n\t\t\t\tconst current = ctx.model;\n\t\t\t\tif (current && !isUnknownModel(current)) return;\n\t\t\t\tvoid pi.setModel(first);\n\t\t\t}, AUTO_SELECT_POLL_MS);\n\t\t\ttimer.unref();\n\t\t};\n\n\t\tpi.registerProvider(spec.providerId, {\n\t\t\tname: spec.displayName,\n\t\t\tbaseUrl: spec.baseUrl,\n\t\t\tapi: \"openai-completions\",\n\t\t\tmodels: state.models,\n\t\t\trefreshModels: async (context) => {\n\t\t\t\t// Cache-only refresh must not probe localhost. A hung IPv6\n\t\t\t\t// localhost on Windows can sit until REFRESH_TIMEOUT_MS.\n\t\t\t\tif (!context.allowNetwork) {\n\t\t\t\t\tconst stored = await context.store.read();\n\t\t\t\t\tif (stored?.models?.length) {\n\t\t\t\t\t\tstate.models = stored.models.map((model) => toModelConfig(model.id));\n\t\t\t\t\t}\n\t\t\t\t\treturn state.models;\n\t\t\t\t}\n\t\t\t\tconst ids = await fetchLocalModelIds(spec, REFRESH_TIMEOUT_MS, context.signal);\n\t\t\t\tstate.models = (ids ?? []).map(toModelConfig);\n\t\t\t\tif (state.models.length > 0) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait context.store.write({\n\t\t\t\t\t\t\tmodels: state.models.map((model) => ({\n\t\t\t\t\t\t\t\t...model,\n\t\t\t\t\t\t\t\tapi: \"openai-completions\" as const,\n\t\t\t\t\t\t\t\tprovider: spec.providerId,\n\t\t\t\t\t\t\t\tbaseUrl: spec.baseUrl,\n\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t\tcheckedAt: Date.now(),\n\t\t\t\t\t\t});\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Store write is best-effort.\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (state.justLoggedIn && state.models.length > 0) {\n\t\t\t\t\tstate.justLoggedIn = false;\n\t\t\t\t\tscheduleAutoSelect();\n\t\t\t\t}\n\t\t\t\treturn state.models;\n\t\t\t},\n\t\t\toauth: {\n\t\t\t\tname: spec.displayName,\n\t\t\t\tlogin: async (callbacks) => {\n\t\t\t\t\tcallbacks.onProgress?.(`Looking for ${spec.productName} on localhost:${spec.port}...`);\n\t\t\t\t\tconst ids = await fetchLocalModelIds(spec, LOGIN_PROBE_TIMEOUT_MS, callbacks.signal);\n\t\t\t\t\tif (ids === null) {\n\t\t\t\t\t\tthrow new Error(`${spec.productName} not detected on localhost:${spec.port} — start it and retry.`);\n\t\t\t\t\t}\n\t\t\t\t\tif (ids.length === 0) {\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t`${spec.productName} is running on localhost:${spec.port} but has no models. Load a model and retry.`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tstate.models = ids.map(toModelConfig);\n\t\t\t\t\tstate.justLoggedIn = true;\n\t\t\t\t\t// Persisted by the standard login flow (Models.login → CredentialStore.modify).\n\t\t\t\t\treturn { refresh: spec.dummyKey, access: spec.dummyKey, expires: NEVER_EXPIRES };\n\t\t\t\t},\n\t\t\t\trefreshToken: async (credentials) => credentials,\n\t\t\t\tgetApiKey: () => spec.dummyKey,\n\t\t\t},\n\t\t});\n\t}\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-servers.d.ts","sourceRoot":"","sources":["../../../src/builtin-extensions/lunr-local-providers/local-servers.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAE3E,MAAM,WAAW,eAAe;IAC/B,4DAA4D;IAC5D,UAAU,EAAE,MAAM,CAAC;IACnB,+CAA+C;IAC/C,WAAW,EAAE,MAAM,CAAC;IACpB,sDAAsD;IACtD,WAAW,EAAE,MAAM,CAAC;IACpB,4CAA4C;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,+DAA+D;IAC/D,OAAO,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,SAAS,EAAE,MAAM,CAAC;IAClB,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,QAAQ,EAAE,MAAM,CAAC;CACjB;
|
|
1
|
+
{"version":3,"file":"local-servers.d.ts","sourceRoot":"","sources":["../../../src/builtin-extensions/lunr-local-providers/local-servers.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAE3E,MAAM,WAAW,eAAe;IAC/B,4DAA4D;IAC5D,UAAU,EAAE,MAAM,CAAC;IACnB,+CAA+C;IAC/C,WAAW,EAAE,MAAM,CAAC;IACpB,sDAAsD;IACtD,WAAW,EAAE,MAAM,CAAC;IACpB,4CAA4C;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,+DAA+D;IAC/D,OAAO,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,SAAS,EAAE,MAAM,CAAC;IAClB,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,QAAQ,EAAE,MAAM,CAAC;CACjB;AAOD,eAAO,MAAM,YAAY,EAAE,eAS1B,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,eAQvB,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,SAAS,eAAe,EAA8B,CAAC;AAEnF;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,EAAE,CA+B1D;AAuBD;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACvC,IAAI,EAAE,eAAe,EACrB,SAAS,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,WAAW,GAClB,OAAO,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAQ1B;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,mBAAmB,CAU7D","sourcesContent":["/**\n * Local model server discovery for lunR (Ollama, LM Studio).\n *\n * Pure functions with no runtime imports so the probing and response\n * normalization can be harness-tested directly against a mock HTTP server.\n * The extension factory lives in index.ts.\n */\n\nimport type { ProviderModelConfig } from \"@ashx-j/lunr\";\n\nexport interface LocalServerSpec {\n\t/** Provider id used for registration and auth.json keys. */\n\tproviderId: string;\n\t/** Display name shown in /login and /model. */\n\tdisplayName: string;\n\t/** Bare product name used in probe/login messages. */\n\tproductName: string;\n\t/** localhost port, for failure messages. */\n\tport: number;\n\t/** OpenAI-compatible base URL registered with the provider. */\n\tbaseUrl: string;\n\t/** OpenAI-style model listing endpoint ({ data: [{ id }] }). */\n\tmodelsUrl: string;\n\t/** Ollama-only fallback listing endpoint ({ models: [{ name }] }). */\n\ttagsUrl?: string;\n\t/** Dummy credential value; local servers need no real key. */\n\tdummyKey: string;\n}\n\n// Use IPv4 loopback. `localhost` can prefer ::1 and hang on Windows when the\n// server only listens on 127.0.0.1.\nconst OLLAMA_ORIGIN = \"http://127.0.0.1:11434\";\nconst LM_STUDIO_ORIGIN = \"http://127.0.0.1:1234\";\n\nexport const OLLAMA_LOCAL: LocalServerSpec = {\n\tproviderId: \"ollama-local\",\n\tdisplayName: \"Ollama (local)\",\n\tproductName: \"Ollama\",\n\tport: 11434,\n\tbaseUrl: `${OLLAMA_ORIGIN}/v1`,\n\tmodelsUrl: `${OLLAMA_ORIGIN}/v1/models`,\n\ttagsUrl: `${OLLAMA_ORIGIN}/api/tags`,\n\tdummyKey: \"ollama\",\n};\n\nexport const LM_STUDIO: LocalServerSpec = {\n\tproviderId: \"lm-studio\",\n\tdisplayName: \"LM Studio (local)\",\n\tproductName: \"LM Studio\",\n\tport: 1234,\n\tbaseUrl: `${LM_STUDIO_ORIGIN}/v1`,\n\tmodelsUrl: `${LM_STUDIO_ORIGIN}/v1/models`,\n\tdummyKey: \"local\",\n};\n\nexport const LOCAL_SERVERS: readonly LocalServerSpec[] = [OLLAMA_LOCAL, LM_STUDIO];\n\n/**\n * Normalize the two known listing shapes into a deduped id list:\n * - OpenAI style `{ data: [{ id, ... }] }` (Ollama /v1/models, LM Studio)\n * - Ollama /api/tags `{ models: [{ name, ... }] }`\n * Bare string arrays and `id`/`name`/`model` entry fields are tolerated.\n */\nexport function extractModelIds(payload: unknown): string[] {\n\tconst ids: string[] = [];\n\tconst push = (value: unknown): void => {\n\t\tif (typeof value === \"string\" && value.length > 0 && !ids.includes(value)) {\n\t\t\tids.push(value);\n\t\t}\n\t};\n\tconst fromEntry = (entry: unknown): void => {\n\t\tif (typeof entry === \"string\") {\n\t\t\tpush(entry);\n\t\t\treturn;\n\t\t}\n\t\tif (entry !== null && typeof entry === \"object\") {\n\t\t\tconst record = entry as Record<string, unknown>;\n\t\t\tpush(record.id ?? record.name ?? record.model);\n\t\t}\n\t};\n\tif (Array.isArray(payload)) {\n\t\tfor (const entry of payload) fromEntry(entry);\n\t\treturn ids;\n\t}\n\tif (payload !== null && typeof payload === \"object\") {\n\t\tconst record = payload as Record<string, unknown>;\n\t\tif (Array.isArray(record.data)) {\n\t\t\tfor (const entry of record.data) fromEntry(entry);\n\t\t}\n\t\tif (Array.isArray(record.models)) {\n\t\t\tfor (const entry of record.models) fromEntry(entry);\n\t\t}\n\t}\n\treturn ids;\n}\n\n/** Fetch JSON with a hard timeout; null on any network/HTTP/parse failure. Never throws. */\nasync function fetchJson(url: string, timeoutMs: number, signal?: AbortSignal): Promise<unknown | null> {\n\tconst controller = new AbortController();\n\tconst timer = setTimeout(() => controller.abort(), timeoutMs);\n\tconst onAbort = (): void => controller.abort();\n\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\ttry {\n\t\tconst response = await fetch(url, {\n\t\t\theaders: { accept: \"application/json\" },\n\t\t\tsignal: controller.signal,\n\t\t});\n\t\tif (!response.ok) return null;\n\t\treturn (await response.json()) as unknown;\n\t} catch {\n\t\treturn null;\n\t} finally {\n\t\tclearTimeout(timer);\n\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t}\n}\n\n/**\n * Probe a local server for its model list.\n * Returns a (possibly empty) id list when the server responded, or null when\n * nothing usable answered — unreachable port, non-JSON, or error status on\n * every known endpoint. Never throws; worst-case delay is ~timeoutMs per\n * endpoint.\n */\nexport async function fetchLocalModelIds(\n\tspec: LocalServerSpec,\n\ttimeoutMs: number,\n\tsignal?: AbortSignal,\n): Promise<string[] | null> {\n\tconst payload = await fetchJson(spec.modelsUrl, timeoutMs, signal);\n\tif (payload !== null) return extractModelIds(payload);\n\tif (spec.tagsUrl) {\n\t\tconst fallback = await fetchJson(spec.tagsUrl, timeoutMs, signal);\n\t\tif (fallback !== null) return extractModelIds(fallback);\n\t}\n\treturn null;\n}\n\n/**\n * ProviderModelConfig defaults for local models: 32k context, zero cost,\n * text-only. Local servers do not advertise per-model limits, so these are\n * conservative fixed values.\n */\nexport function toModelConfig(id: string): ProviderModelConfig {\n\treturn {\n\t\tid,\n\t\tname: id,\n\t\treasoning: false,\n\t\tinput: [\"text\"],\n\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n\t\tcontextWindow: 32768,\n\t\tmaxTokens: 8192,\n\t};\n}\n"]}
|
|
@@ -5,8 +5,10 @@
|
|
|
5
5
|
* normalization can be harness-tested directly against a mock HTTP server.
|
|
6
6
|
* The extension factory lives in index.ts.
|
|
7
7
|
*/
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
// Use IPv4 loopback. `localhost` can prefer ::1 and hang on Windows when the
|
|
9
|
+
// server only listens on 127.0.0.1.
|
|
10
|
+
const OLLAMA_ORIGIN = "http://127.0.0.1:11434";
|
|
11
|
+
const LM_STUDIO_ORIGIN = "http://127.0.0.1:1234";
|
|
10
12
|
export const OLLAMA_LOCAL = {
|
|
11
13
|
providerId: "ollama-local",
|
|
12
14
|
displayName: "Ollama (local)",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-servers.js","sourceRoot":"","sources":["../../../src/builtin-extensions/lunr-local-providers/local-servers.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Local model server discovery for lunR (Ollama, LM Studio).\n *\n * Pure functions with no runtime imports so the probing and response\n * normalization can be harness-tested directly against a mock HTTP server.\n * The extension factory lives in index.ts.\n */\n\nimport type { ProviderModelConfig } from \"@ashx-j/lunr\";\n\nexport interface LocalServerSpec {\n\t/** Provider id used for registration and auth.json keys. */\n\tproviderId: string;\n\t/** Display name shown in /login and /model. */\n\tdisplayName: string;\n\t/** Bare product name used in probe/login messages. */\n\tproductName: string;\n\t/** localhost port, for failure messages. */\n\tport: number;\n\t/** OpenAI-compatible base URL registered with the provider. */\n\tbaseUrl: string;\n\t/** OpenAI-style model listing endpoint ({ data: [{ id }] }). */\n\tmodelsUrl: string;\n\t/** Ollama-only fallback listing endpoint ({ models: [{ name }] }). */\n\ttagsUrl?: string;\n\t/** Dummy credential value; local servers need no real key. */\n\tdummyKey: string;\n}\n\nconst OLLAMA_ORIGIN = \"http://
|
|
1
|
+
{"version":3,"file":"local-servers.js","sourceRoot":"","sources":["../../../src/builtin-extensions/lunr-local-providers/local-servers.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Local model server discovery for lunR (Ollama, LM Studio).\n *\n * Pure functions with no runtime imports so the probing and response\n * normalization can be harness-tested directly against a mock HTTP server.\n * The extension factory lives in index.ts.\n */\n\nimport type { ProviderModelConfig } from \"@ashx-j/lunr\";\n\nexport interface LocalServerSpec {\n\t/** Provider id used for registration and auth.json keys. */\n\tproviderId: string;\n\t/** Display name shown in /login and /model. */\n\tdisplayName: string;\n\t/** Bare product name used in probe/login messages. */\n\tproductName: string;\n\t/** localhost port, for failure messages. */\n\tport: number;\n\t/** OpenAI-compatible base URL registered with the provider. */\n\tbaseUrl: string;\n\t/** OpenAI-style model listing endpoint ({ data: [{ id }] }). */\n\tmodelsUrl: string;\n\t/** Ollama-only fallback listing endpoint ({ models: [{ name }] }). */\n\ttagsUrl?: string;\n\t/** Dummy credential value; local servers need no real key. */\n\tdummyKey: string;\n}\n\n// Use IPv4 loopback. `localhost` can prefer ::1 and hang on Windows when the\n// server only listens on 127.0.0.1.\nconst OLLAMA_ORIGIN = \"http://127.0.0.1:11434\";\nconst LM_STUDIO_ORIGIN = \"http://127.0.0.1:1234\";\n\nexport const OLLAMA_LOCAL: LocalServerSpec = {\n\tproviderId: \"ollama-local\",\n\tdisplayName: \"Ollama (local)\",\n\tproductName: \"Ollama\",\n\tport: 11434,\n\tbaseUrl: `${OLLAMA_ORIGIN}/v1`,\n\tmodelsUrl: `${OLLAMA_ORIGIN}/v1/models`,\n\ttagsUrl: `${OLLAMA_ORIGIN}/api/tags`,\n\tdummyKey: \"ollama\",\n};\n\nexport const LM_STUDIO: LocalServerSpec = {\n\tproviderId: \"lm-studio\",\n\tdisplayName: \"LM Studio (local)\",\n\tproductName: \"LM Studio\",\n\tport: 1234,\n\tbaseUrl: `${LM_STUDIO_ORIGIN}/v1`,\n\tmodelsUrl: `${LM_STUDIO_ORIGIN}/v1/models`,\n\tdummyKey: \"local\",\n};\n\nexport const LOCAL_SERVERS: readonly LocalServerSpec[] = [OLLAMA_LOCAL, LM_STUDIO];\n\n/**\n * Normalize the two known listing shapes into a deduped id list:\n * - OpenAI style `{ data: [{ id, ... }] }` (Ollama /v1/models, LM Studio)\n * - Ollama /api/tags `{ models: [{ name, ... }] }`\n * Bare string arrays and `id`/`name`/`model` entry fields are tolerated.\n */\nexport function extractModelIds(payload: unknown): string[] {\n\tconst ids: string[] = [];\n\tconst push = (value: unknown): void => {\n\t\tif (typeof value === \"string\" && value.length > 0 && !ids.includes(value)) {\n\t\t\tids.push(value);\n\t\t}\n\t};\n\tconst fromEntry = (entry: unknown): void => {\n\t\tif (typeof entry === \"string\") {\n\t\t\tpush(entry);\n\t\t\treturn;\n\t\t}\n\t\tif (entry !== null && typeof entry === \"object\") {\n\t\t\tconst record = entry as Record<string, unknown>;\n\t\t\tpush(record.id ?? record.name ?? record.model);\n\t\t}\n\t};\n\tif (Array.isArray(payload)) {\n\t\tfor (const entry of payload) fromEntry(entry);\n\t\treturn ids;\n\t}\n\tif (payload !== null && typeof payload === \"object\") {\n\t\tconst record = payload as Record<string, unknown>;\n\t\tif (Array.isArray(record.data)) {\n\t\t\tfor (const entry of record.data) fromEntry(entry);\n\t\t}\n\t\tif (Array.isArray(record.models)) {\n\t\t\tfor (const entry of record.models) fromEntry(entry);\n\t\t}\n\t}\n\treturn ids;\n}\n\n/** Fetch JSON with a hard timeout; null on any network/HTTP/parse failure. Never throws. */\nasync function fetchJson(url: string, timeoutMs: number, signal?: AbortSignal): Promise<unknown | null> {\n\tconst controller = new AbortController();\n\tconst timer = setTimeout(() => controller.abort(), timeoutMs);\n\tconst onAbort = (): void => controller.abort();\n\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\ttry {\n\t\tconst response = await fetch(url, {\n\t\t\theaders: { accept: \"application/json\" },\n\t\t\tsignal: controller.signal,\n\t\t});\n\t\tif (!response.ok) return null;\n\t\treturn (await response.json()) as unknown;\n\t} catch {\n\t\treturn null;\n\t} finally {\n\t\tclearTimeout(timer);\n\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t}\n}\n\n/**\n * Probe a local server for its model list.\n * Returns a (possibly empty) id list when the server responded, or null when\n * nothing usable answered — unreachable port, non-JSON, or error status on\n * every known endpoint. Never throws; worst-case delay is ~timeoutMs per\n * endpoint.\n */\nexport async function fetchLocalModelIds(\n\tspec: LocalServerSpec,\n\ttimeoutMs: number,\n\tsignal?: AbortSignal,\n): Promise<string[] | null> {\n\tconst payload = await fetchJson(spec.modelsUrl, timeoutMs, signal);\n\tif (payload !== null) return extractModelIds(payload);\n\tif (spec.tagsUrl) {\n\t\tconst fallback = await fetchJson(spec.tagsUrl, timeoutMs, signal);\n\t\tif (fallback !== null) return extractModelIds(fallback);\n\t}\n\treturn null;\n}\n\n/**\n * ProviderModelConfig defaults for local models: 32k context, zero cost,\n * text-only. Local servers do not advertise per-model limits, so these are\n * conservative fixed values.\n */\nexport function toModelConfig(id: string): ProviderModelConfig {\n\treturn {\n\t\tid,\n\t\tname: id,\n\t\treasoning: false,\n\t\tinput: [\"text\"],\n\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n\t\tcontextWindow: 32768,\n\t\tmaxTokens: 8192,\n\t};\n}\n"]}
|
|
@@ -20,9 +20,8 @@
|
|
|
20
20
|
* Startup behavior:
|
|
21
21
|
* - Missing cache: uses baked-in GENERATED_MODELS (manually generated via
|
|
22
22
|
* `npm run generate-models` and committed to the repo).
|
|
23
|
-
* - Stale
|
|
24
|
-
*
|
|
25
|
-
* - Fresh cache: uses cached data directly, no refresh triggered.
|
|
23
|
+
* - Stale or fresh cache: uses the cached data immediately. Cloud fetch only
|
|
24
|
+
* from /refresh when network is allowed (session_start stays cache-only).
|
|
26
25
|
*
|
|
27
26
|
* Only models with "tools" capability are registered.
|
|
28
27
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/builtin-extensions/pi-ollama-cloud/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/builtin-extensions/pi-ollama-cloud/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAgD,MAAM,iCAAiC,CAAC;AAuElH,yBAA+B,EAAE,EAAE,YAAY,iBAiB9C","sourcesContent":["/**\n * Ollama Cloud Provider Extension\n *\n * Registers Ollama Cloud as a model provider with dynamically fetched models.\n *\n * Setup:\n * 1. Get an API key from https://ollama.com\n * 2. Add to auth.json in the agent config dir (~/.pi/agent/auth.json, or set PI_CODING_AGENT_DIR):\n * { \"ollama-cloud\": { \"type\": \"api_key\", \"key\": \"your-key\" } }\n * 3. Run /refresh to fetch model metadata\n * 4. Use /model or ctrl+l to select an Ollama Cloud model\n *\n * Two endpoints are used to build the model list:\n * - GET https://ollama.com/v1/models -> list of model IDs\n * - POST https://ollama.com/api/show -> per-model details (capabilities, context length)\n *\n * Raw /api/show responses are cached at <agentDir>/cache/ollama-cloud-models.json\n * so the provider assembly can be debugged and re-derived without re-fetching.\n *\n * Startup behavior:\n * - Missing cache: uses baked-in GENERATED_MODELS (manually generated via\n * `npm run generate-models` and committed to the repo).\n * - Stale or fresh cache: uses the cached data immediately. Cloud fetch only\n * from /refresh when network is allowed (session_start stays cache-only).\n *\n * Only models with \"tools\" capability are registered.\n */\n\nimport type { ExtensionAPI, ExtensionCommandContext, ProviderModelConfig } from \"@ashx-j/lunr\";\nimport { GENERATED_MODELS } from \"./models.generated.ts\";\nimport {\n assembleModels,\n fetchModels,\n OLLAMA_BASE,\n type RefreshProgress,\n readCacheState,\n writeCache,\n} from \"./models.ts\";\n// Web tools (ollama_web_search, ollama_web_fetch) excluded — API type mismatches with current pi-coding-agent version. TODO: re-include after updating web-tools.ts to match current AgentToolResult/AuthStorage APIs.\n// lunr 2026-08-02: the no-op /settings \"Ollama web tools\" row, the @lunr/ollama-webtools bridge, and config.ts (webTools key, PI_OLLAMA_WEB_TOOLS) were removed — they toggled state nothing consumed. A leftover \"webTools\" key in ollama-cloud.json is harmless.\n\n// --- Registrations ---\n\nfunction registerProvider(pi: ExtensionAPI, models: ProviderModelConfig[]) {\n pi.registerProvider(\"ollama-cloud\", {\n name: \"Ollama Cloud\",\n baseUrl: `${OLLAMA_BASE}/v1`,\n apiKey: \"$OLLAMA_API_KEY\",\n api: \"openai-completions\",\n models,\n });\n}\n\nfunction renderProgressBar(current: number, total: number, width = 15): string {\n if (total <= 0) return `[${\"░\".repeat(width)}]`;\n const ratio = Math.max(0, Math.min(1, current / total));\n const filled = Math.round(ratio * width);\n return `[${\"█\".repeat(filled)}${\"░\".repeat(width - filled)}]`;\n}\n\nfunction createRefreshProgressUi(ctx: Pick<ExtensionCommandContext, \"ui\">) {\n const key = \"ollama-cloud-refresh\";\n return {\n update(progress: RefreshProgress) {\n const current = progress.current ?? 0;\n const total = progress.total ?? 0;\n ctx.ui.setWidget(key, [renderProgressBar(current, total)], { placement: \"belowEditor\" });\n },\n clear() {\n ctx.ui.setWidget(key, undefined);\n ctx.ui.setStatus(key, undefined);\n ctx.ui.setWorkingMessage();\n },\n };\n}\n\nasync function runRefresh(pi: ExtensionAPI, ctx: Pick<ExtensionCommandContext, \"ui\">) {\n const progressUi = createRefreshProgressUi(ctx);\n try {\n progressUi.update({ stage: \"list\", message: \"Starting refresh...\" });\n\n const raw = await fetchModels(ctx, (progress) => progressUi.update(progress));\n if (!raw) return false;\n if (Object.keys(raw).length === 0) return false;\n\n writeCache(raw);\n const newModels = assembleModels(raw);\n\n registerProvider(pi, newModels);\n\n ctx.ui.notify(`Registered ${newModels.length} Ollama Cloud models`, \"info\");\n return true;\n } finally {\n progressUi.clear();\n }\n}\n\n// --- Main ---\n\nexport default async function (pi: ExtensionAPI) {\n const cacheState = readCacheState();\n // When cache is missing, GENERATED_MODELS serves as the cache —\n // it is manually generated via `npm run generate-models` and committed to the repo.\n // Stale cache is used immediately; Cloud fetch only from /refresh (when network is allowed).\n // GENERATED_MODELS ships with the package (36 tool-capable models from\n // the build script). Used when no local cache exists. A fresh user cache\n // from /refresh takes precedence over the generated list.\n const models = cacheState.status === \"missing\" ? GENERATED_MODELS : assembleModels(cacheState.models);\n\n registerProvider(pi, models);\n // lunr: no user-visible command here — /refresh is the single refresh entry\n // point. Core triggers this extension's refresh through the bridge because\n // it bypasses the refreshModels hook and re-registers the provider wholesale.\n (globalThis as Record<symbol, unknown>)[Symbol.for(\"@lunr/ollama-cloud-refresh\")] = (\n ctx: Pick<ExtensionCommandContext, \"ui\">,\n ) => runRefresh(pi, ctx);\n}\n"]}
|
|
@@ -20,9 +20,8 @@
|
|
|
20
20
|
* Startup behavior:
|
|
21
21
|
* - Missing cache: uses baked-in GENERATED_MODELS (manually generated via
|
|
22
22
|
* `npm run generate-models` and committed to the repo).
|
|
23
|
-
* - Stale
|
|
24
|
-
*
|
|
25
|
-
* - Fresh cache: uses cached data directly, no refresh triggered.
|
|
23
|
+
* - Stale or fresh cache: uses the cached data immediately. Cloud fetch only
|
|
24
|
+
* from /refresh when network is allowed (session_start stays cache-only).
|
|
26
25
|
*
|
|
27
26
|
* Only models with "tools" capability are registered.
|
|
28
27
|
*/
|
|
@@ -53,17 +52,7 @@ function createRefreshProgressUi(ctx) {
|
|
|
53
52
|
update(progress) {
|
|
54
53
|
const current = progress.current ?? 0;
|
|
55
54
|
const total = progress.total ?? 0;
|
|
56
|
-
|
|
57
|
-
const failed = progress.failed ? `, ${progress.failed} failed` : "";
|
|
58
|
-
const stage = progress.stage === "list"
|
|
59
|
-
? "Discovering models"
|
|
60
|
-
: progress.stage === "details"
|
|
61
|
-
? "Fetching model details"
|
|
62
|
-
: "Done";
|
|
63
|
-
const summary = total > 0 ? `${current}/${total} (${percent}%${failed})` : progress.message;
|
|
64
|
-
const line = `☁ Ollama Cloud - ${stage} — ${summary} ${renderProgressBar(current, total)}`;
|
|
65
|
-
ctx.ui.setWorkingMessage(`Refreshing Ollama Cloud models - ${stage.toLowerCase()}`);
|
|
66
|
-
ctx.ui.setWidget(key, [line], { placement: "belowEditor" });
|
|
55
|
+
ctx.ui.setWidget(key, [renderProgressBar(current, total)], { placement: "belowEditor" });
|
|
67
56
|
},
|
|
68
57
|
clear() {
|
|
69
58
|
ctx.ui.setWidget(key, undefined);
|
|
@@ -94,10 +83,9 @@ async function runRefresh(pi, ctx) {
|
|
|
94
83
|
// --- Main ---
|
|
95
84
|
export default async function (pi) {
|
|
96
85
|
const cacheState = readCacheState();
|
|
97
|
-
// Auto-refresh only when the disk cache is stale (>30 days).
|
|
98
86
|
// When cache is missing, GENERATED_MODELS serves as the cache —
|
|
99
87
|
// it is manually generated via `npm run generate-models` and committed to the repo.
|
|
100
|
-
|
|
88
|
+
// Stale cache is used immediately; Cloud fetch only from /refresh (when network is allowed).
|
|
101
89
|
// GENERATED_MODELS ships with the package (36 tool-capable models from
|
|
102
90
|
// the build script). Used when no local cache exists. A fresh user cache
|
|
103
91
|
// from /refresh takes precedence over the generated list.
|
|
@@ -107,14 +95,5 @@ export default async function (pi) {
|
|
|
107
95
|
// point. Core triggers this extension's refresh through the bridge because
|
|
108
96
|
// it bypasses the refreshModels hook and re-registers the provider wholesale.
|
|
109
97
|
globalThis[Symbol.for("@lunr/ollama-cloud-refresh")] = (ctx) => runRefresh(pi, ctx);
|
|
110
|
-
if (needsStartupRefresh) {
|
|
111
|
-
let started = false;
|
|
112
|
-
pi.on("session_start", async (_event, ctx) => {
|
|
113
|
-
if (started)
|
|
114
|
-
return;
|
|
115
|
-
started = true;
|
|
116
|
-
await runRefresh(pi, ctx);
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
98
|
}
|
|
120
99
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/builtin-extensions/pi-ollama-cloud/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/builtin-extensions/pi-ollama-cloud/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Ollama Cloud Provider Extension\n *\n * Registers Ollama Cloud as a model provider with dynamically fetched models.\n *\n * Setup:\n * 1. Get an API key from https://ollama.com\n * 2. Add to auth.json in the agent config dir (~/.pi/agent/auth.json, or set PI_CODING_AGENT_DIR):\n * { \"ollama-cloud\": { \"type\": \"api_key\", \"key\": \"your-key\" } }\n * 3. Run /refresh to fetch model metadata\n * 4. Use /model or ctrl+l to select an Ollama Cloud model\n *\n * Two endpoints are used to build the model list:\n * - GET https://ollama.com/v1/models -> list of model IDs\n * - POST https://ollama.com/api/show -> per-model details (capabilities, context length)\n *\n * Raw /api/show responses are cached at <agentDir>/cache/ollama-cloud-models.json\n * so the provider assembly can be debugged and re-derived without re-fetching.\n *\n * Startup behavior:\n * - Missing cache: uses baked-in GENERATED_MODELS (manually generated via\n * `npm run generate-models` and committed to the repo).\n * - Stale or fresh cache: uses the cached data immediately. Cloud fetch only\n * from /refresh when network is allowed (session_start stays cache-only).\n *\n * Only models with \"tools\" capability are registered.\n */\n\nimport type { ExtensionAPI, ExtensionCommandContext, ProviderModelConfig } from \"@ashx-j/lunr\";\nimport { GENERATED_MODELS } from \"./models.generated.ts\";\nimport {\n assembleModels,\n fetchModels,\n OLLAMA_BASE,\n type RefreshProgress,\n readCacheState,\n writeCache,\n} from \"./models.ts\";\n// Web tools (ollama_web_search, ollama_web_fetch) excluded — API type mismatches with current pi-coding-agent version. TODO: re-include after updating web-tools.ts to match current AgentToolResult/AuthStorage APIs.\n// lunr 2026-08-02: the no-op /settings \"Ollama web tools\" row, the @lunr/ollama-webtools bridge, and config.ts (webTools key, PI_OLLAMA_WEB_TOOLS) were removed — they toggled state nothing consumed. A leftover \"webTools\" key in ollama-cloud.json is harmless.\n\n// --- Registrations ---\n\nfunction registerProvider(pi: ExtensionAPI, models: ProviderModelConfig[]) {\n pi.registerProvider(\"ollama-cloud\", {\n name: \"Ollama Cloud\",\n baseUrl: `${OLLAMA_BASE}/v1`,\n apiKey: \"$OLLAMA_API_KEY\",\n api: \"openai-completions\",\n models,\n });\n}\n\nfunction renderProgressBar(current: number, total: number, width = 15): string {\n if (total <= 0) return `[${\"░\".repeat(width)}]`;\n const ratio = Math.max(0, Math.min(1, current / total));\n const filled = Math.round(ratio * width);\n return `[${\"█\".repeat(filled)}${\"░\".repeat(width - filled)}]`;\n}\n\nfunction createRefreshProgressUi(ctx: Pick<ExtensionCommandContext, \"ui\">) {\n const key = \"ollama-cloud-refresh\";\n return {\n update(progress: RefreshProgress) {\n const current = progress.current ?? 0;\n const total = progress.total ?? 0;\n ctx.ui.setWidget(key, [renderProgressBar(current, total)], { placement: \"belowEditor\" });\n },\n clear() {\n ctx.ui.setWidget(key, undefined);\n ctx.ui.setStatus(key, undefined);\n ctx.ui.setWorkingMessage();\n },\n };\n}\n\nasync function runRefresh(pi: ExtensionAPI, ctx: Pick<ExtensionCommandContext, \"ui\">) {\n const progressUi = createRefreshProgressUi(ctx);\n try {\n progressUi.update({ stage: \"list\", message: \"Starting refresh...\" });\n\n const raw = await fetchModels(ctx, (progress) => progressUi.update(progress));\n if (!raw) return false;\n if (Object.keys(raw).length === 0) return false;\n\n writeCache(raw);\n const newModels = assembleModels(raw);\n\n registerProvider(pi, newModels);\n\n ctx.ui.notify(`Registered ${newModels.length} Ollama Cloud models`, \"info\");\n return true;\n } finally {\n progressUi.clear();\n }\n}\n\n// --- Main ---\n\nexport default async function (pi: ExtensionAPI) {\n const cacheState = readCacheState();\n // When cache is missing, GENERATED_MODELS serves as the cache —\n // it is manually generated via `npm run generate-models` and committed to the repo.\n // Stale cache is used immediately; Cloud fetch only from /refresh (when network is allowed).\n // GENERATED_MODELS ships with the package (36 tool-capable models from\n // the build script). Used when no local cache exists. A fresh user cache\n // from /refresh takes precedence over the generated list.\n const models = cacheState.status === \"missing\" ? GENERATED_MODELS : assembleModels(cacheState.models);\n\n registerProvider(pi, models);\n // lunr: no user-visible command here — /refresh is the single refresh entry\n // point. Core triggers this extension's refresh through the bridge because\n // it bypasses the refreshModels hook and re-registers the provider wholesale.\n (globalThis as Record<symbol, unknown>)[Symbol.for(\"@lunr/ollama-cloud-refresh\")] = (\n ctx: Pick<ExtensionCommandContext, \"ui\">,\n ) => runRefresh(pi, ctx);\n}\n"]}
|