wepscli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +293 -0
- package/dist/WEPSCLI-shell/agent-runtime.js +824 -0
- package/dist/WEPSCLI-shell/agent-runtime.js.map +1 -0
- package/dist/WEPSCLI-shell/approval-overlay.js +275 -0
- package/dist/WEPSCLI-shell/approval-overlay.js.map +1 -0
- package/dist/WEPSCLI-shell/chat-components.js +760 -0
- package/dist/WEPSCLI-shell/chat-components.js.map +1 -0
- package/dist/WEPSCLI-shell/components.js +850 -0
- package/dist/WEPSCLI-shell/components.js.map +1 -0
- package/dist/WEPSCLI-shell/config-overlays.js +205 -0
- package/dist/WEPSCLI-shell/config-overlays.js.map +1 -0
- package/dist/WEPSCLI-shell/debug-log.js +16 -0
- package/dist/WEPSCLI-shell/debug-log.js.map +1 -0
- package/dist/WEPSCLI-shell/file-change-preview.js +261 -0
- package/dist/WEPSCLI-shell/file-change-preview.js.map +1 -0
- package/dist/WEPSCLI-shell/helpers.js +112 -0
- package/dist/WEPSCLI-shell/helpers.js.map +1 -0
- package/dist/WEPSCLI-shell/index.js +3 -0
- package/dist/WEPSCLI-shell/index.js.map +1 -0
- package/dist/WEPSCLI-shell/provider-add-flow.js +406 -0
- package/dist/WEPSCLI-shell/provider-add-flow.js.map +1 -0
- package/dist/WEPSCLI-shell/run-wepscli-shell.js +21 -0
- package/dist/WEPSCLI-shell/run-wepscli-shell.js.map +1 -0
- package/dist/WEPSCLI-shell/runtime-recovery.js +37 -0
- package/dist/WEPSCLI-shell/runtime-recovery.js.map +1 -0
- package/dist/WEPSCLI-shell/runtime-status.js +66 -0
- package/dist/WEPSCLI-shell/runtime-status.js.map +1 -0
- package/dist/WEPSCLI-shell/shell-app.js +1047 -0
- package/dist/WEPSCLI-shell/shell-app.js.map +1 -0
- package/dist/WEPSCLI-shell/shell-modes.js +77 -0
- package/dist/WEPSCLI-shell/shell-modes.js.map +1 -0
- package/dist/WEPSCLI-shell/slash-commands.js +135 -0
- package/dist/WEPSCLI-shell/slash-commands.js.map +1 -0
- package/dist/WEPSCLI-shell/theme.js +19 -0
- package/dist/WEPSCLI-shell/theme.js.map +1 -0
- package/dist/WEPSCLI-shell/tool-approval.js +85 -0
- package/dist/WEPSCLI-shell/tool-approval.js.map +1 -0
- package/dist/WEPSCLI-shell/tool-diff.js +76 -0
- package/dist/WEPSCLI-shell/tool-diff.js.map +1 -0
- package/dist/WEPSCLI-shell/tool-file-changes.js +268 -0
- package/dist/WEPSCLI-shell/tool-file-changes.js.map +1 -0
- package/dist/WEPSCLI-shell/tool-message-detail.js +138 -0
- package/dist/WEPSCLI-shell/tool-message-detail.js.map +1 -0
- package/dist/WEPSCLI-shell/tool-messages.js +145 -0
- package/dist/WEPSCLI-shell/tool-messages.js.map +1 -0
- package/dist/WEPSCLI-shell/transcript-panel.js +372 -0
- package/dist/WEPSCLI-shell/transcript-panel.js.map +1 -0
- package/dist/WEPSCLI-shell/transcript-state.js +62 -0
- package/dist/WEPSCLI-shell/transcript-state.js.map +1 -0
- package/dist/WEPSCLI-shell/types.js +1 -0
- package/dist/WEPSCLI-shell/types.js.map +1 -0
- package/dist/cli.js +11 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.js +40 -0
- package/dist/config.js.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/main.js +140 -0
- package/dist/main.js.map +1 -0
- package/dist/onboarding/action-screen.js +90 -0
- package/dist/onboarding/action-screen.js.map +1 -0
- package/dist/onboarding/framed-screen.js +35 -0
- package/dist/onboarding/framed-screen.js.map +1 -0
- package/dist/onboarding/onboarding-app.js +312 -0
- package/dist/onboarding/onboarding-app.js.map +1 -0
- package/dist/onboarding/run-onboarding.js +23 -0
- package/dist/onboarding/run-onboarding.js.map +1 -0
- package/dist/onboarding/select-screen.js +21 -0
- package/dist/onboarding/select-screen.js.map +1 -0
- package/dist/onboarding/summary-screen.js +23 -0
- package/dist/onboarding/summary-screen.js.map +1 -0
- package/dist/onboarding/text-input-screen.js +55 -0
- package/dist/onboarding/text-input-screen.js.map +1 -0
- package/dist/onboarding/theme.js +26 -0
- package/dist/onboarding/theme.js.map +1 -0
- package/dist/provider-profiles/api-key-store.js +51 -0
- package/dist/provider-profiles/api-key-store.js.map +1 -0
- package/dist/provider-profiles/defaults.js +18 -0
- package/dist/provider-profiles/defaults.js.map +1 -0
- package/dist/provider-profiles/fetch-models.js +53 -0
- package/dist/provider-profiles/fetch-models.js.map +1 -0
- package/dist/provider-profiles/index.js +6 -0
- package/dist/provider-profiles/index.js.map +1 -0
- package/dist/provider-profiles/provider-profile-service.js +223 -0
- package/dist/provider-profiles/provider-profile-service.js.map +1 -0
- package/dist/provider-profiles/providers-config-store.js +17 -0
- package/dist/provider-profiles/providers-config-store.js.map +1 -0
- package/dist/provider-profiles/types.js +1 -0
- package/dist/provider-profiles/types.js.map +1 -0
- package/dist/session-history/session-history-service.js +142 -0
- package/dist/session-history/session-history-service.js.map +1 -0
- package/dist/shell/animator.js +30 -0
- package/dist/shell/animator.js.map +1 -0
- package/dist/shell/clickables.js +101 -0
- package/dist/shell/clickables.js.map +1 -0
- package/dist/shell/dashboard-shell.js +292 -0
- package/dist/shell/dashboard-shell.js.map +1 -0
- package/dist/shell/index.js +5 -0
- package/dist/shell/index.js.map +1 -0
- package/dist/shell/keymap.js +14 -0
- package/dist/shell/keymap.js.map +1 -0
- package/dist/shell/mouse.js +39 -0
- package/dist/shell/mouse.js.map +1 -0
- package/dist/shell/render.js +122 -0
- package/dist/shell/render.js.map +1 -0
- package/dist/shell/run-shell.js +36 -0
- package/dist/shell/run-shell.js.map +1 -0
- package/dist/shell/theme.js +56 -0
- package/dist/shell/theme.js.map +1 -0
- package/dist/storage/locked-json-file.js +88 -0
- package/dist/storage/locked-json-file.js.map +1 -0
- package/dist/workbench/animator.js +30 -0
- package/dist/workbench/animator.js.map +1 -0
- package/dist/workbench/index.js +6 -0
- package/dist/workbench/index.js.map +1 -0
- package/dist/workbench/mouse.js +39 -0
- package/dist/workbench/mouse.js.map +1 -0
- package/dist/workbench/render.js +82 -0
- package/dist/workbench/render.js.map +1 -0
- package/dist/workbench/renderer.js +364 -0
- package/dist/workbench/renderer.js.map +1 -0
- package/dist/workbench/run-workbench.js +36 -0
- package/dist/workbench/run-workbench.js.map +1 -0
- package/dist/workbench/theme.js +63 -0
- package/dist/workbench/theme.js.map +1 -0
- package/dist/workbench/types.js +1 -0
- package/dist/workbench/types.js.map +1 -0
- package/dist/workbench/workbench-shell.js +649 -0
- package/dist/workbench/workbench-shell.js.map +1 -0
- package/package.json +65 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const PROVIDERS_CONFIG_VERSION = 1;
|
|
2
|
+
export function createDefaultProvidersConfig() {
|
|
3
|
+
return {
|
|
4
|
+
version: PROVIDERS_CONFIG_VERSION,
|
|
5
|
+
profiles: []
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
export function getDefaultDialect(family) {
|
|
9
|
+
switch (family) {
|
|
10
|
+
case "openai":
|
|
11
|
+
return "openai-responses";
|
|
12
|
+
case "anthropic":
|
|
13
|
+
return "anthropic-messages";
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export function trimTrailingSlash(value) {
|
|
17
|
+
return value.replace(/\/+$/, "");
|
|
18
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["PROVIDERS_CONFIG_VERSION","createDefaultProvidersConfig","version","profiles","getDefaultDialect","family","trimTrailingSlash","value","replace"],"sources":["defaults.ts"],"sourcesContent":["import type { ProviderApiDialect, ProviderFamily, ProvidersConfig } from \"./types.js\";\r\n\r\nexport const PROVIDERS_CONFIG_VERSION = 1;\r\n\r\nexport function createDefaultProvidersConfig(): ProvidersConfig {\r\n\treturn {\r\n\t\tversion: PROVIDERS_CONFIG_VERSION,\r\n\t\tprofiles: [],\r\n\t};\r\n}\r\n\r\nexport function getDefaultDialect(family: ProviderFamily): ProviderApiDialect {\r\n\tswitch (family) {\r\n\t\tcase \"openai\":\r\n\t\t\treturn \"openai-responses\";\r\n\t\tcase \"anthropic\":\r\n\t\t\treturn \"anthropic-messages\";\r\n\t}\r\n}\r\n\r\nexport function trimTrailingSlash(value: string): string {\r\n\treturn value.replace(/\\/+$/, \"\");\r\n}\r\n"],"mappings":"AAEA,OAAO,MAAMA,wBAAwB,GAAG,CAAC;AAEzC,OAAO,SAASC,4BAA4BA,CAAA,EAAoB;EAC/D,OAAO;IACNC,OAAO,EAAEF,wBAAwB;IACjCG,QAAQ,EAAE;EACX,CAAC;AACF;AAEA,OAAO,SAASC,iBAAiBA,CAACC,MAAsB,EAAsB;EAC7E,QAAQA,MAAM;IACb,KAAK,QAAQ;MACZ,OAAO,kBAAkB;IAC1B,KAAK,WAAW;MACf,OAAO,oBAAoB;EAC7B;AACD;AAEA,OAAO,SAASC,iBAAiBA,CAACC,KAAa,EAAU;EACxD,OAAOA,KAAK,CAACC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;AACjC","ignoreList":[]}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { trimTrailingSlash } from "./defaults.js";
|
|
2
|
+
function getModelsUrl(baseUrl) {
|
|
3
|
+
const normalized = trimTrailingSlash(baseUrl.trim());
|
|
4
|
+
if (normalized.endsWith("/models")) {
|
|
5
|
+
return normalized;
|
|
6
|
+
}
|
|
7
|
+
return `${normalized}/models`;
|
|
8
|
+
}
|
|
9
|
+
function normalizeOpenAiModels(payload) {
|
|
10
|
+
const items = payload.data ?? [];
|
|
11
|
+
return items.filter(item => typeof item.id === "string" && item.id.length > 0).map(item => ({
|
|
12
|
+
id: item.id,
|
|
13
|
+
name: item.id,
|
|
14
|
+
family: "openai",
|
|
15
|
+
raw: item
|
|
16
|
+
}));
|
|
17
|
+
}
|
|
18
|
+
function normalizeAnthropicModels(payload) {
|
|
19
|
+
const items = payload.data ?? [];
|
|
20
|
+
return items.filter(item => typeof item.id === "string" && item.id.length > 0).map(item => ({
|
|
21
|
+
id: item.id,
|
|
22
|
+
name: item.display_name || item.id,
|
|
23
|
+
family: "anthropic",
|
|
24
|
+
raw: item
|
|
25
|
+
}));
|
|
26
|
+
}
|
|
27
|
+
function buildHeaders(profile, apiKey) {
|
|
28
|
+
if (profile.family === "anthropic") {
|
|
29
|
+
return {
|
|
30
|
+
"x-api-key": apiKey,
|
|
31
|
+
"anthropic-version": "2023-06-01"
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
Authorization: `Bearer ${apiKey}`
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export async function fetchModelsForProfile(profile, apiKey, fetchImpl = fetch) {
|
|
39
|
+
const response = await fetchImpl(getModelsUrl(profile.baseUrl), {
|
|
40
|
+
method: "GET",
|
|
41
|
+
headers: buildHeaders(profile, apiKey)
|
|
42
|
+
});
|
|
43
|
+
if (!response.ok) {
|
|
44
|
+
const text = await response.text();
|
|
45
|
+
throw new Error(`Model listing failed (${response.status} ${response.statusText}): ${text || "empty response"}`);
|
|
46
|
+
}
|
|
47
|
+
const payload = await response.json();
|
|
48
|
+
const models = profile.family === "anthropic" ? normalizeAnthropicModels(payload) : normalizeOpenAiModels(payload);
|
|
49
|
+
if (models.length === 0) {
|
|
50
|
+
throw new Error("Model listing returned no usable models");
|
|
51
|
+
}
|
|
52
|
+
return models;
|
|
53
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["trimTrailingSlash","getModelsUrl","baseUrl","normalized","trim","endsWith","normalizeOpenAiModels","payload","items","data","filter","item","id","length","map","name","family","raw","normalizeAnthropicModels","display_name","buildHeaders","profile","apiKey","Authorization","fetchModelsForProfile","fetchImpl","fetch","response","method","headers","ok","text","Error","status","statusText","json","models"],"sources":["fetch-models.ts"],"sourcesContent":["import { trimTrailingSlash } from \"./defaults.js\";\r\nimport type { DiscoveredModel, ProviderProfile } from \"./types.js\";\r\n\r\ninterface OpenAiListModelsResponse {\r\n\tdata?: Array<{\r\n\t\tid?: string;\r\n\t\towned_by?: string;\r\n\t}>;\r\n}\r\n\r\ninterface AnthropicListModelsResponse {\r\n\tdata?: Array<{\r\n\t\tid?: string;\r\n\t\tdisplay_name?: string;\r\n\t}>;\r\n}\r\n\r\nfunction getModelsUrl(baseUrl: string): string {\r\n\tconst normalized = trimTrailingSlash(baseUrl.trim());\r\n\tif (normalized.endsWith(\"/models\")) {\r\n\t\treturn normalized;\r\n\t}\r\n\treturn `${normalized}/models`;\r\n}\r\n\r\nfunction normalizeOpenAiModels(payload: OpenAiListModelsResponse): DiscoveredModel[] {\r\n\tconst items = payload.data ?? [];\r\n\treturn items\r\n\t\t.filter((item): item is { id: string; owned_by?: string } => typeof item.id === \"string\" && item.id.length > 0)\r\n\t\t.map((item) => ({\r\n\t\t\tid: item.id,\r\n\t\t\tname: item.id,\r\n\t\t\tfamily: \"openai\",\r\n\t\t\traw: item,\r\n\t\t}));\r\n}\r\n\r\nfunction normalizeAnthropicModels(payload: AnthropicListModelsResponse): DiscoveredModel[] {\r\n\tconst items = payload.data ?? [];\r\n\treturn items\r\n\t\t.filter((item): item is { id: string; display_name?: string } => typeof item.id === \"string\" && item.id.length > 0)\r\n\t\t.map((item) => ({\r\n\t\t\tid: item.id,\r\n\t\t\tname: item.display_name || item.id,\r\n\t\t\tfamily: \"anthropic\",\r\n\t\t\traw: item,\r\n\t\t}));\r\n}\r\n\r\nfunction buildHeaders(profile: ProviderProfile, apiKey: string): Record<string, string> {\r\n\tif (profile.family === \"anthropic\") {\r\n\t\treturn {\r\n\t\t\t\"x-api-key\": apiKey,\r\n\t\t\t\"anthropic-version\": \"2023-06-01\",\r\n\t\t};\r\n\t}\r\n\r\n\treturn {\r\n\t\tAuthorization: `Bearer ${apiKey}`,\r\n\t};\r\n}\r\n\r\nexport async function fetchModelsForProfile(\r\n\tprofile: ProviderProfile,\r\n\tapiKey: string,\r\n\tfetchImpl: typeof fetch = fetch,\r\n): Promise<DiscoveredModel[]> {\r\n\tconst response = await fetchImpl(getModelsUrl(profile.baseUrl), {\r\n\t\tmethod: \"GET\",\r\n\t\theaders: buildHeaders(profile, apiKey),\r\n\t});\r\n\r\n\tif (!response.ok) {\r\n\t\tconst text = await response.text();\r\n\t\tthrow new Error(`Model listing failed (${response.status} ${response.statusText}): ${text || \"empty response\"}`);\r\n\t}\r\n\r\n\tconst payload = (await response.json()) as OpenAiListModelsResponse | AnthropicListModelsResponse;\r\n\tconst models = profile.family === \"anthropic\" ? normalizeAnthropicModels(payload) : normalizeOpenAiModels(payload);\r\n\r\n\tif (models.length === 0) {\r\n\t\tthrow new Error(\"Model listing returned no usable models\");\r\n\t}\r\n\r\n\treturn models;\r\n}\r\n"],"mappings":"AAAA,SAASA,iBAAiB,QAAQ,eAAe;AAiBjD,SAASC,YAAYA,CAACC,OAAe,EAAU;EAC9C,MAAMC,UAAU,GAAGH,iBAAiB,CAACE,OAAO,CAACE,IAAI,CAAC,CAAC,CAAC;EACpD,IAAID,UAAU,CAACE,QAAQ,CAAC,SAAS,CAAC,EAAE;IACnC,OAAOF,UAAU;EAClB;EACA,OAAO,GAAGA,UAAU,SAAS;AAC9B;AAEA,SAASG,qBAAqBA,CAACC,OAAiC,EAAqB;EACpF,MAAMC,KAAK,GAAGD,OAAO,CAACE,IAAI,IAAI,EAAE;EAChC,OAAOD,KAAK,CACVE,MAAM,CAAEC,IAAI,IAAgD,OAAOA,IAAI,CAACC,EAAE,KAAK,QAAQ,IAAID,IAAI,CAACC,EAAE,CAACC,MAAM,GAAG,CAAC,CAAC,CAC9GC,GAAG,CAAEH,IAAI,KAAM;IACfC,EAAE,EAAED,IAAI,CAACC,EAAE;IACXG,IAAI,EAAEJ,IAAI,CAACC,EAAE;IACbI,MAAM,EAAE,QAAQ;IAChBC,GAAG,EAAEN;EACN,CAAC,CAAC,CAAC;AACL;AAEA,SAASO,wBAAwBA,CAACX,OAAoC,EAAqB;EAC1F,MAAMC,KAAK,GAAGD,OAAO,CAACE,IAAI,IAAI,EAAE;EAChC,OAAOD,KAAK,CACVE,MAAM,CAAEC,IAAI,IAAoD,OAAOA,IAAI,CAACC,EAAE,KAAK,QAAQ,IAAID,IAAI,CAACC,EAAE,CAACC,MAAM,GAAG,CAAC,CAAC,CAClHC,GAAG,CAAEH,IAAI,KAAM;IACfC,EAAE,EAAED,IAAI,CAACC,EAAE;IACXG,IAAI,EAAEJ,IAAI,CAACQ,YAAY,IAAIR,IAAI,CAACC,EAAE;IAClCI,MAAM,EAAE,WAAW;IACnBC,GAAG,EAAEN;EACN,CAAC,CAAC,CAAC;AACL;AAEA,SAASS,YAAYA,CAACC,OAAwB,EAAEC,MAAc,EAA0B;EACvF,IAAID,OAAO,CAACL,MAAM,KAAK,WAAW,EAAE;IACnC,OAAO;MACN,WAAW,EAAEM,MAAM;MACnB,mBAAmB,EAAE;IACtB,CAAC;EACF;EAEA,OAAO;IACNC,aAAa,EAAE,UAAUD,MAAM;EAChC,CAAC;AACF;AAEA,OAAO,eAAeE,qBAAqBA,CAC1CH,OAAwB,EACxBC,MAAc,EACdG,SAAuB,GAAGC,KAAK,EACF;EAC7B,MAAMC,QAAQ,GAAG,MAAMF,SAAS,CAACxB,YAAY,CAACoB,OAAO,CAACnB,OAAO,CAAC,EAAE;IAC/D0B,MAAM,EAAE,KAAK;IACbC,OAAO,EAAET,YAAY,CAACC,OAAO,EAAEC,MAAM;EACtC,CAAC,CAAC;EAEF,IAAI,CAACK,QAAQ,CAACG,EAAE,EAAE;IACjB,MAAMC,IAAI,GAAG,MAAMJ,QAAQ,CAACI,IAAI,CAAC,CAAC;IAClC,MAAM,IAAIC,KAAK,CAAC,yBAAyBL,QAAQ,CAACM,MAAM,IAAIN,QAAQ,CAACO,UAAU,MAAMH,IAAI,IAAI,gBAAgB,EAAE,CAAC;EACjH;EAEA,MAAMxB,OAAO,GAAI,MAAMoB,QAAQ,CAACQ,IAAI,CAAC,CAA4D;EACjG,MAAMC,MAAM,GAAGf,OAAO,CAACL,MAAM,KAAK,WAAW,GAAGE,wBAAwB,CAACX,OAAO,CAAC,GAAGD,qBAAqB,CAACC,OAAO,CAAC;EAElH,IAAI6B,MAAM,CAACvB,MAAM,KAAK,CAAC,EAAE;IACxB,MAAM,IAAImB,KAAK,CAAC,yCAAyC,CAAC;EAC3D;EAEA,OAAOI,MAAM;AACd","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":[],"sources":["index.ts"],"sourcesContent":["export * from \"./api-key-store.js\";\r\nexport * from \"./defaults.js\";\r\nexport * from \"./fetch-models.js\";\r\nexport * from \"./provider-profile-service.js\";\r\nexport * from \"./providers-config-store.js\";\r\nexport * from \"./types.js\";\r\n"],"mappings":"AAAA,cAAc,oBAAoB;AAClC,cAAc,eAAe;AAC7B,cAAc,mBAAmB;AACjC,cAAc,+BAA+B;AAC7C,cAAc,6BAA6B;AAC3C,cAAc,YAAY","ignoreList":[]}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { randomUUID } from "crypto";
|
|
2
|
+
import { getDefaultDialect } from "./defaults.js";
|
|
3
|
+
import { fetchModelsForProfile } from "./fetch-models.js";
|
|
4
|
+
import { ApiKeyStore } from "./api-key-store.js";
|
|
5
|
+
import { ProvidersConfigStore } from "./providers-config-store.js";
|
|
6
|
+
function slugify(value) {
|
|
7
|
+
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
8
|
+
}
|
|
9
|
+
function createProfileId(label) {
|
|
10
|
+
const slug = slugify(label) || "provider";
|
|
11
|
+
return `${slug}-${randomUUID().slice(0, 8)}`;
|
|
12
|
+
}
|
|
13
|
+
function nowIso() {
|
|
14
|
+
return new Date().toISOString();
|
|
15
|
+
}
|
|
16
|
+
function cloneProfile(profile) {
|
|
17
|
+
return structuredClone(profile);
|
|
18
|
+
}
|
|
19
|
+
export class ProviderProfileService {
|
|
20
|
+
constructor(configStore = new ProvidersConfigStore(), apiKeyStore = new ApiKeyStore()) {
|
|
21
|
+
this.configStore = configStore;
|
|
22
|
+
this.apiKeyStore = apiKeyStore;
|
|
23
|
+
}
|
|
24
|
+
ensureStorage() {
|
|
25
|
+
this.configStore.save(this.configStore.load());
|
|
26
|
+
this.apiKeyStore.ensureFile();
|
|
27
|
+
}
|
|
28
|
+
listProfiles() {
|
|
29
|
+
return this.configStore.load().profiles.map(cloneProfile);
|
|
30
|
+
}
|
|
31
|
+
getActiveSelection() {
|
|
32
|
+
const config = this.configStore.load();
|
|
33
|
+
return {
|
|
34
|
+
profileId: config.activeProfileId,
|
|
35
|
+
modelId: config.activeModelId
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
getProfile(profileId) {
|
|
39
|
+
const profile = this.configStore.load().profiles.find(item => item.id === profileId);
|
|
40
|
+
return profile ? cloneProfile(profile) : undefined;
|
|
41
|
+
}
|
|
42
|
+
createProfile(input) {
|
|
43
|
+
const timestamp = nowIso();
|
|
44
|
+
const profile = {
|
|
45
|
+
id: createProfileId(input.label),
|
|
46
|
+
label: input.label.trim(),
|
|
47
|
+
family: input.family,
|
|
48
|
+
apiDialect: input.apiDialect ?? getDefaultDialect(input.family),
|
|
49
|
+
baseUrl: input.baseUrl.trim(),
|
|
50
|
+
enabled: input.enabled ?? true,
|
|
51
|
+
models: [],
|
|
52
|
+
createdAt: timestamp,
|
|
53
|
+
updatedAt: timestamp,
|
|
54
|
+
lastValidationStatus: "unknown"
|
|
55
|
+
};
|
|
56
|
+
this.configStore.update(current => ({
|
|
57
|
+
result: undefined,
|
|
58
|
+
next: {
|
|
59
|
+
...current,
|
|
60
|
+
activeProfileId: current.activeProfileId ?? profile.id,
|
|
61
|
+
profiles: [...current.profiles, profile]
|
|
62
|
+
}
|
|
63
|
+
}));
|
|
64
|
+
if (input.apiKey) {
|
|
65
|
+
this.apiKeyStore.setApiKey(profile.id, input.apiKey);
|
|
66
|
+
}
|
|
67
|
+
return cloneProfile(profile);
|
|
68
|
+
}
|
|
69
|
+
updateProfile(profileId, input) {
|
|
70
|
+
const updated = this.configStore.update(current => {
|
|
71
|
+
const index = current.profiles.findIndex(item => item.id === profileId);
|
|
72
|
+
if (index === -1) {
|
|
73
|
+
throw new Error(`Unknown provider profile: ${profileId}`);
|
|
74
|
+
}
|
|
75
|
+
const existing = current.profiles[index];
|
|
76
|
+
const nextProfile = {
|
|
77
|
+
...existing,
|
|
78
|
+
label: input.label?.trim() ?? existing.label,
|
|
79
|
+
baseUrl: input.baseUrl?.trim() ?? existing.baseUrl,
|
|
80
|
+
apiDialect: input.apiDialect ?? existing.apiDialect,
|
|
81
|
+
enabled: input.enabled ?? existing.enabled,
|
|
82
|
+
updatedAt: nowIso()
|
|
83
|
+
};
|
|
84
|
+
const nextProfiles = [...current.profiles];
|
|
85
|
+
nextProfiles[index] = nextProfile;
|
|
86
|
+
return {
|
|
87
|
+
result: nextProfile,
|
|
88
|
+
next: {
|
|
89
|
+
...current,
|
|
90
|
+
profiles: nextProfiles
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
});
|
|
94
|
+
if (input.apiKey) {
|
|
95
|
+
this.apiKeyStore.setApiKey(profileId, input.apiKey);
|
|
96
|
+
}
|
|
97
|
+
return cloneProfile(updated);
|
|
98
|
+
}
|
|
99
|
+
removeProfile(profileId) {
|
|
100
|
+
this.configStore.update(current => {
|
|
101
|
+
const nextProfiles = current.profiles.filter(item => item.id !== profileId);
|
|
102
|
+
const activeProfileId = current.activeProfileId === profileId ? nextProfiles[0]?.id : current.activeProfileId;
|
|
103
|
+
const activeModelId = activeProfileId === current.activeProfileId ? current.activeModelId : undefined;
|
|
104
|
+
return {
|
|
105
|
+
result: undefined,
|
|
106
|
+
next: {
|
|
107
|
+
...current,
|
|
108
|
+
activeProfileId,
|
|
109
|
+
activeModelId,
|
|
110
|
+
profiles: nextProfiles
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
});
|
|
114
|
+
this.apiKeyStore.removeApiKey(profileId);
|
|
115
|
+
}
|
|
116
|
+
setActiveSelection(profileId, modelId) {
|
|
117
|
+
this.configStore.update(current => {
|
|
118
|
+
const profile = current.profiles.find(item => item.id === profileId);
|
|
119
|
+
if (!profile) {
|
|
120
|
+
throw new Error(`Unknown provider profile: ${profileId}`);
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
result: undefined,
|
|
124
|
+
next: {
|
|
125
|
+
...current,
|
|
126
|
+
activeProfileId: profileId,
|
|
127
|
+
activeModelId: modelId
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
getApiKey(profileId) {
|
|
133
|
+
return this.apiKeyStore.getApiKey(profileId);
|
|
134
|
+
}
|
|
135
|
+
replaceModels(profileId, models, validation = {
|
|
136
|
+
status: "unknown"
|
|
137
|
+
}) {
|
|
138
|
+
const updated = this.configStore.update(current => {
|
|
139
|
+
const index = current.profiles.findIndex(item => item.id === profileId);
|
|
140
|
+
if (index === -1) {
|
|
141
|
+
throw new Error(`Unknown provider profile: ${profileId}`);
|
|
142
|
+
}
|
|
143
|
+
const existing = current.profiles[index];
|
|
144
|
+
const nextProfile = {
|
|
145
|
+
...existing,
|
|
146
|
+
models: structuredClone(models),
|
|
147
|
+
lastValidatedAt: nowIso(),
|
|
148
|
+
lastValidationStatus: validation.status,
|
|
149
|
+
lastValidationMessage: validation.message,
|
|
150
|
+
updatedAt: nowIso()
|
|
151
|
+
};
|
|
152
|
+
const nextProfiles = [...current.profiles];
|
|
153
|
+
nextProfiles[index] = nextProfile;
|
|
154
|
+
const nextActiveModelId = current.activeProfileId === profileId ? current.activeModelId ?? models[0]?.id : current.activeModelId;
|
|
155
|
+
return {
|
|
156
|
+
result: nextProfile,
|
|
157
|
+
next: {
|
|
158
|
+
...current,
|
|
159
|
+
activeModelId: nextActiveModelId,
|
|
160
|
+
profiles: nextProfiles
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
});
|
|
164
|
+
return cloneProfile(updated);
|
|
165
|
+
}
|
|
166
|
+
async refreshModels(profileId, fetchImpl = fetch) {
|
|
167
|
+
const profile = this.getProfile(profileId);
|
|
168
|
+
if (!profile) {
|
|
169
|
+
throw new Error(`Unknown provider profile: ${profileId}`);
|
|
170
|
+
}
|
|
171
|
+
const apiKey = this.apiKeyStore.getApiKey(profileId);
|
|
172
|
+
if (!apiKey) {
|
|
173
|
+
throw new Error(`No API key configured for provider profile: ${profileId}`);
|
|
174
|
+
}
|
|
175
|
+
try {
|
|
176
|
+
const models = await fetchModelsForProfile(profile, apiKey, fetchImpl);
|
|
177
|
+
const updatedProfile = this.configStore.update(current => {
|
|
178
|
+
const nextProfiles = current.profiles.map(item => item.id === profileId ? {
|
|
179
|
+
...item,
|
|
180
|
+
models,
|
|
181
|
+
lastValidatedAt: nowIso(),
|
|
182
|
+
lastValidationStatus: "ok",
|
|
183
|
+
lastValidationMessage: undefined,
|
|
184
|
+
updatedAt: nowIso()
|
|
185
|
+
} : item);
|
|
186
|
+
return {
|
|
187
|
+
result: nextProfiles.find(item => item.id === profileId),
|
|
188
|
+
next: {
|
|
189
|
+
...current,
|
|
190
|
+
activeModelId: current.activeProfileId === profileId && !current.activeModelId ? models[0]?.id : current.activeModelId,
|
|
191
|
+
profiles: nextProfiles
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
});
|
|
195
|
+
return {
|
|
196
|
+
profile: cloneProfile(updatedProfile),
|
|
197
|
+
models: structuredClone(models)
|
|
198
|
+
};
|
|
199
|
+
} catch (error) {
|
|
200
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
201
|
+
const updatedProfile = this.configStore.update(current => {
|
|
202
|
+
const nextProfiles = current.profiles.map(item => item.id === profileId ? {
|
|
203
|
+
...item,
|
|
204
|
+
lastValidatedAt: nowIso(),
|
|
205
|
+
lastValidationStatus: "error",
|
|
206
|
+
lastValidationMessage: message,
|
|
207
|
+
updatedAt: nowIso()
|
|
208
|
+
} : item);
|
|
209
|
+
return {
|
|
210
|
+
result: nextProfiles.find(item => item.id === profileId),
|
|
211
|
+
next: {
|
|
212
|
+
...current,
|
|
213
|
+
profiles: nextProfiles
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
});
|
|
217
|
+
return {
|
|
218
|
+
profile: cloneProfile(updatedProfile),
|
|
219
|
+
error: message
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["randomUUID","getDefaultDialect","fetchModelsForProfile","ApiKeyStore","ProvidersConfigStore","slugify","value","trim","toLowerCase","replace","createProfileId","label","slug","slice","nowIso","Date","toISOString","cloneProfile","profile","structuredClone","ProviderProfileService","constructor","configStore","apiKeyStore","ensureStorage","save","load","ensureFile","listProfiles","profiles","map","getActiveSelection","config","profileId","activeProfileId","modelId","activeModelId","getProfile","find","item","id","undefined","createProfile","input","timestamp","family","apiDialect","baseUrl","enabled","models","createdAt","updatedAt","lastValidationStatus","update","current","result","next","apiKey","setApiKey","updateProfile","updated","index","findIndex","Error","existing","nextProfile","nextProfiles","removeProfile","filter","removeApiKey","setActiveSelection","getApiKey","replaceModels","validation","status","lastValidatedAt","lastValidationMessage","message","nextActiveModelId","refreshModels","fetchImpl","fetch","updatedProfile","error","String"],"sources":["provider-profile-service.ts"],"sourcesContent":["import { randomUUID } from \"crypto\";\r\nimport { getDefaultDialect } from \"./defaults.js\";\r\nimport { fetchModelsForProfile } from \"./fetch-models.js\";\r\nimport { ApiKeyStore } from \"./api-key-store.js\";\r\nimport { ProvidersConfigStore } from \"./providers-config-store.js\";\r\nimport type {\r\n\tCreateProviderProfileInput,\r\n\tDiscoveredModel,\r\n\tFailedRefreshModelsResult,\r\n\tProviderProfile,\r\n\tRefreshModelsResult,\r\n\tUpdateProviderProfileInput,\r\n} from \"./types.js\";\r\n\r\nfunction slugify(value: string): string {\r\n\treturn value\r\n\t\t.trim()\r\n\t\t.toLowerCase()\r\n\t\t.replace(/[^a-z0-9]+/g, \"-\")\r\n\t\t.replace(/^-+|-+$/g, \"\");\r\n}\r\n\r\nfunction createProfileId(label: string): string {\r\n\tconst slug = slugify(label) || \"provider\";\r\n\treturn `${slug}-${randomUUID().slice(0, 8)}`;\r\n}\r\n\r\nfunction nowIso(): string {\r\n\treturn new Date().toISOString();\r\n}\r\n\r\nfunction cloneProfile(profile: ProviderProfile): ProviderProfile {\r\n\treturn structuredClone(profile);\r\n}\r\n\r\nexport class ProviderProfileService {\r\n\tconstructor(\r\n\t\tprivate readonly configStore: ProvidersConfigStore = new ProvidersConfigStore(),\r\n\t\tprivate readonly apiKeyStore: ApiKeyStore = new ApiKeyStore(),\r\n\t) {}\r\n\r\n\tensureStorage(): void {\r\n\t\tthis.configStore.save(this.configStore.load());\r\n\t\tthis.apiKeyStore.ensureFile();\r\n\t}\r\n\r\n\tlistProfiles(): ProviderProfile[] {\r\n\t\treturn this.configStore.load().profiles.map(cloneProfile);\r\n\t}\r\n\r\n\tgetActiveSelection(): { profileId?: string; modelId?: string } {\r\n\t\tconst config = this.configStore.load();\r\n\t\treturn {\r\n\t\t\tprofileId: config.activeProfileId,\r\n\t\t\tmodelId: config.activeModelId,\r\n\t\t};\r\n\t}\r\n\r\n\tgetProfile(profileId: string): ProviderProfile | undefined {\r\n\t\tconst profile = this.configStore.load().profiles.find((item) => item.id === profileId);\r\n\t\treturn profile ? cloneProfile(profile) : undefined;\r\n\t}\r\n\r\n\tcreateProfile(input: CreateProviderProfileInput): ProviderProfile {\r\n\t\tconst timestamp = nowIso();\r\n\t\tconst profile: ProviderProfile = {\r\n\t\t\tid: createProfileId(input.label),\r\n\t\t\tlabel: input.label.trim(),\r\n\t\t\tfamily: input.family,\r\n\t\t\tapiDialect: input.apiDialect ?? getDefaultDialect(input.family),\r\n\t\t\tbaseUrl: input.baseUrl.trim(),\r\n\t\t\tenabled: input.enabled ?? true,\r\n\t\t\tmodels: [],\r\n\t\t\tcreatedAt: timestamp,\r\n\t\t\tupdatedAt: timestamp,\r\n\t\t\tlastValidationStatus: \"unknown\",\r\n\t\t};\r\n\r\n\t\tthis.configStore.update((current) => ({\r\n\t\t\tresult: undefined,\r\n\t\t\tnext: {\r\n\t\t\t\t...current,\r\n\t\t\t\tactiveProfileId: current.activeProfileId ?? profile.id,\r\n\t\t\t\tprofiles: [...current.profiles, profile],\r\n\t\t\t},\r\n\t\t}));\r\n\r\n\t\tif (input.apiKey) {\r\n\t\t\tthis.apiKeyStore.setApiKey(profile.id, input.apiKey);\r\n\t\t}\r\n\r\n\t\treturn cloneProfile(profile);\r\n\t}\r\n\r\n\tupdateProfile(profileId: string, input: UpdateProviderProfileInput): ProviderProfile {\r\n\t\tconst updated = this.configStore.update((current) => {\r\n\t\t\tconst index = current.profiles.findIndex((item) => item.id === profileId);\r\n\t\t\tif (index === -1) {\r\n\t\t\t\tthrow new Error(`Unknown provider profile: ${profileId}`);\r\n\t\t\t}\r\n\r\n\t\t\tconst existing = current.profiles[index];\r\n\t\t\tconst nextProfile: ProviderProfile = {\r\n\t\t\t\t...existing,\r\n\t\t\t\tlabel: input.label?.trim() ?? existing.label,\r\n\t\t\t\tbaseUrl: input.baseUrl?.trim() ?? existing.baseUrl,\r\n\t\t\t\tapiDialect: input.apiDialect ?? existing.apiDialect,\r\n\t\t\t\tenabled: input.enabled ?? existing.enabled,\r\n\t\t\t\tupdatedAt: nowIso(),\r\n\t\t\t};\r\n\r\n\t\t\tconst nextProfiles = [...current.profiles];\r\n\t\t\tnextProfiles[index] = nextProfile;\r\n\r\n\t\t\treturn {\r\n\t\t\t\tresult: nextProfile,\r\n\t\t\t\tnext: {\r\n\t\t\t\t\t...current,\r\n\t\t\t\t\tprofiles: nextProfiles,\r\n\t\t\t\t},\r\n\t\t\t};\r\n\t\t});\r\n\r\n\t\tif (input.apiKey) {\r\n\t\t\tthis.apiKeyStore.setApiKey(profileId, input.apiKey);\r\n\t\t}\r\n\r\n\t\treturn cloneProfile(updated);\r\n\t}\r\n\r\n\tremoveProfile(profileId: string): void {\r\n\t\tthis.configStore.update((current) => {\r\n\t\t\tconst nextProfiles = current.profiles.filter((item) => item.id !== profileId);\r\n\t\t\tconst activeProfileId = current.activeProfileId === profileId ? nextProfiles[0]?.id : current.activeProfileId;\r\n\t\t\tconst activeModelId = activeProfileId === current.activeProfileId ? current.activeModelId : undefined;\r\n\r\n\t\t\treturn {\r\n\t\t\t\tresult: undefined,\r\n\t\t\t\tnext: {\r\n\t\t\t\t\t...current,\r\n\t\t\t\t\tactiveProfileId,\r\n\t\t\t\t\tactiveModelId,\r\n\t\t\t\t\tprofiles: nextProfiles,\r\n\t\t\t\t},\r\n\t\t\t};\r\n\t\t});\r\n\r\n\t\tthis.apiKeyStore.removeApiKey(profileId);\r\n\t}\r\n\r\n\tsetActiveSelection(profileId: string, modelId?: string): void {\r\n\t\tthis.configStore.update((current) => {\r\n\t\t\tconst profile = current.profiles.find((item) => item.id === profileId);\r\n\t\t\tif (!profile) {\r\n\t\t\t\tthrow new Error(`Unknown provider profile: ${profileId}`);\r\n\t\t\t}\r\n\r\n\t\t\treturn {\r\n\t\t\t\tresult: undefined,\r\n\t\t\t\tnext: {\r\n\t\t\t\t\t...current,\r\n\t\t\t\t\tactiveProfileId: profileId,\r\n\t\t\t\t\tactiveModelId: modelId,\r\n\t\t\t\t},\r\n\t\t\t};\r\n\t\t});\r\n\t}\r\n\r\n\tgetApiKey(profileId: string): string | undefined {\r\n\t\treturn this.apiKeyStore.getApiKey(profileId);\r\n\t}\r\n\r\n\treplaceModels(\r\n\t\tprofileId: string,\r\n\t\tmodels: DiscoveredModel[],\r\n\t\tvalidation: { status: \"unknown\" | \"ok\" | \"error\"; message?: string } = { status: \"unknown\" },\r\n\t): ProviderProfile {\r\n\t\tconst updated = this.configStore.update((current) => {\r\n\t\t\tconst index = current.profiles.findIndex((item) => item.id === profileId);\r\n\t\t\tif (index === -1) {\r\n\t\t\t\tthrow new Error(`Unknown provider profile: ${profileId}`);\r\n\t\t\t}\r\n\r\n\t\t\tconst existing = current.profiles[index];\r\n\t\t\tconst nextProfile: ProviderProfile = {\r\n\t\t\t\t...existing,\r\n\t\t\t\tmodels: structuredClone(models),\r\n\t\t\t\tlastValidatedAt: nowIso(),\r\n\t\t\t\tlastValidationStatus: validation.status,\r\n\t\t\t\tlastValidationMessage: validation.message,\r\n\t\t\t\tupdatedAt: nowIso(),\r\n\t\t\t};\r\n\r\n\t\t\tconst nextProfiles = [...current.profiles];\r\n\t\t\tnextProfiles[index] = nextProfile;\r\n\r\n\t\t\tconst nextActiveModelId =\r\n\t\t\t\tcurrent.activeProfileId === profileId\r\n\t\t\t\t\t? current.activeModelId ?? models[0]?.id\r\n\t\t\t\t\t: current.activeModelId;\r\n\r\n\t\t\treturn {\r\n\t\t\t\tresult: nextProfile,\r\n\t\t\t\tnext: {\r\n\t\t\t\t\t...current,\r\n\t\t\t\t\tactiveModelId: nextActiveModelId,\r\n\t\t\t\t\tprofiles: nextProfiles,\r\n\t\t\t\t},\r\n\t\t\t};\r\n\t\t});\r\n\r\n\t\treturn cloneProfile(updated);\r\n\t}\r\n\r\n\tasync refreshModels(\r\n\t\tprofileId: string,\r\n\t\tfetchImpl: typeof fetch = fetch,\r\n\t): Promise<RefreshModelsResult | FailedRefreshModelsResult> {\r\n\t\tconst profile = this.getProfile(profileId);\r\n\t\tif (!profile) {\r\n\t\t\tthrow new Error(`Unknown provider profile: ${profileId}`);\r\n\t\t}\r\n\r\n\t\tconst apiKey = this.apiKeyStore.getApiKey(profileId);\r\n\t\tif (!apiKey) {\r\n\t\t\tthrow new Error(`No API key configured for provider profile: ${profileId}`);\r\n\t\t}\r\n\r\n\t\ttry {\r\n\t\t\tconst models = await fetchModelsForProfile(profile, apiKey, fetchImpl);\r\n\t\t\tconst updatedProfile = this.configStore.update((current) => {\r\n\t\t\t\tconst nextProfiles = current.profiles.map((item) =>\r\n\t\t\t\t\titem.id === profileId\r\n\t\t\t\t\t\t? {\r\n\t\t\t\t\t\t\t\t...item,\r\n\t\t\t\t\t\t\t\tmodels,\r\n\t\t\t\t\t\t\t\tlastValidatedAt: nowIso(),\r\n\t\t\t\t\t\t\t\tlastValidationStatus: \"ok\" as const,\r\n\t\t\t\t\t\t\t\tlastValidationMessage: undefined,\r\n\t\t\t\t\t\t\t\tupdatedAt: nowIso(),\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t: item,\r\n\t\t\t\t);\r\n\r\n\t\t\t\treturn {\r\n\t\t\t\t\tresult: nextProfiles.find((item) => item.id === profileId)!,\r\n\t\t\t\t\tnext: {\r\n\t\t\t\t\t\t...current,\r\n\t\t\t\t\t\tactiveModelId:\r\n\t\t\t\t\t\t\tcurrent.activeProfileId === profileId && !current.activeModelId ? models[0]?.id : current.activeModelId,\r\n\t\t\t\t\t\tprofiles: nextProfiles,\r\n\t\t\t\t\t},\r\n\t\t\t\t};\r\n\t\t\t});\r\n\r\n\t\t\treturn {\r\n\t\t\t\tprofile: cloneProfile(updatedProfile),\r\n\t\t\t\tmodels: structuredClone(models),\r\n\t\t\t};\r\n\t\t} catch (error) {\r\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\r\n\t\t\tconst updatedProfile = this.configStore.update((current) => {\r\n\t\t\t\tconst nextProfiles = current.profiles.map((item) =>\r\n\t\t\t\t\titem.id === profileId\r\n\t\t\t\t\t\t? {\r\n\t\t\t\t\t\t\t\t...item,\r\n\t\t\t\t\t\t\t\tlastValidatedAt: nowIso(),\r\n\t\t\t\t\t\t\t\tlastValidationStatus: \"error\" as const,\r\n\t\t\t\t\t\t\t\tlastValidationMessage: message,\r\n\t\t\t\t\t\t\t\tupdatedAt: nowIso(),\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t: item,\r\n\t\t\t\t);\r\n\r\n\t\t\t\treturn {\r\n\t\t\t\t\tresult: nextProfiles.find((item) => item.id === profileId)!,\r\n\t\t\t\t\tnext: {\r\n\t\t\t\t\t\t...current,\r\n\t\t\t\t\t\tprofiles: nextProfiles,\r\n\t\t\t\t\t},\r\n\t\t\t\t};\r\n\t\t\t});\r\n\r\n\t\t\treturn {\r\n\t\t\t\tprofile: cloneProfile(updatedProfile),\r\n\t\t\t\terror: message,\r\n\t\t\t};\r\n\t\t}\r\n\t}\r\n}\r\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { getProvidersPath } from "../config.js";
|
|
2
|
+
import { LockedJsonFile } from "../storage/locked-json-file.js";
|
|
3
|
+
import { createDefaultProvidersConfig } from "./defaults.js";
|
|
4
|
+
export class ProvidersConfigStore {
|
|
5
|
+
constructor(providersPath = getProvidersPath()) {
|
|
6
|
+
this.storage = new LockedJsonFile(providersPath, createDefaultProvidersConfig());
|
|
7
|
+
}
|
|
8
|
+
load() {
|
|
9
|
+
return this.storage.read();
|
|
10
|
+
}
|
|
11
|
+
save(next) {
|
|
12
|
+
this.storage.write(next);
|
|
13
|
+
}
|
|
14
|
+
update(fn) {
|
|
15
|
+
return this.storage.withLock(fn);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["getProvidersPath","LockedJsonFile","createDefaultProvidersConfig","ProvidersConfigStore","constructor","providersPath","storage","load","read","save","next","write","update","fn","withLock"],"sources":["providers-config-store.ts"],"sourcesContent":["import { getProvidersPath } from \"../config.js\";\r\nimport { LockedJsonFile } from \"../storage/locked-json-file.js\";\r\nimport { createDefaultProvidersConfig } from \"./defaults.js\";\r\nimport type { ProvidersConfig } from \"./types.js\";\r\n\r\nexport class ProvidersConfigStore {\r\n\tprivate readonly storage: LockedJsonFile<ProvidersConfig>;\r\n\r\n\tconstructor(providersPath: string = getProvidersPath()) {\r\n\t\tthis.storage = new LockedJsonFile<ProvidersConfig>(providersPath, createDefaultProvidersConfig());\r\n\t}\r\n\r\n\tload(): ProvidersConfig {\r\n\t\treturn this.storage.read();\r\n\t}\r\n\r\n\tsave(next: ProvidersConfig): void {\r\n\t\tthis.storage.write(next);\r\n\t}\r\n\r\n\tupdate<TResult>(fn: (current: ProvidersConfig) => { result: TResult; next?: ProvidersConfig }): TResult {\r\n\t\treturn this.storage.withLock(fn);\r\n\t}\r\n}\r\n"],"mappings":"AAAA,SAASA,gBAAgB,QAAQ,cAAc;AAC/C,SAASC,cAAc,QAAQ,gCAAgC;AAC/D,SAASC,4BAA4B,QAAQ,eAAe;AAG5D,OAAO,MAAMC,oBAAoB,CAAC;EAGjCC,WAAWA,CAACC,aAAqB,GAAGL,gBAAgB,CAAC,CAAC,EAAE;IACvD,IAAI,CAACM,OAAO,GAAG,IAAIL,cAAc,CAAkBI,aAAa,EAAEH,4BAA4B,CAAC,CAAC,CAAC;EAClG;EAEAK,IAAIA,CAAA,EAAoB;IACvB,OAAO,IAAI,CAACD,OAAO,CAACE,IAAI,CAAC,CAAC;EAC3B;EAEAC,IAAIA,CAACC,IAAqB,EAAQ;IACjC,IAAI,CAACJ,OAAO,CAACK,KAAK,CAACD,IAAI,CAAC;EACzB;EAEAE,MAAMA,CAAUC,EAA6E,EAAW;IACvG,OAAO,IAAI,CAACP,OAAO,CAACQ,QAAQ,CAACD,EAAE,CAAC;EACjC;AACD","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":[],"sources":["types.ts"],"sourcesContent":["export type ProviderFamily = \"openai\" | \"anthropic\";\r\n\r\nexport type ProviderApiDialect = \"openai-responses\" | \"openai-completions\" | \"anthropic-messages\";\r\n\r\nexport type ValidationStatus = \"unknown\" | \"ok\" | \"error\";\r\n\r\nexport interface DiscoveredModel {\r\n\tid: string;\r\n\tname: string;\r\n\tfamily: ProviderFamily;\r\n\traw?: unknown;\r\n}\r\n\r\nexport interface ProviderProfile {\r\n\tid: string;\r\n\tlabel: string;\r\n\tfamily: ProviderFamily;\r\n\tapiDialect: ProviderApiDialect;\r\n\tbaseUrl: string;\r\n\tenabled: boolean;\r\n\tmodels: DiscoveredModel[];\r\n\tcreatedAt: string;\r\n\tupdatedAt: string;\r\n\tlastValidatedAt?: string;\r\n\tlastValidationStatus: ValidationStatus;\r\n\tlastValidationMessage?: string;\r\n}\r\n\r\nexport interface ProvidersConfig {\r\n\tversion: 1;\r\n\tactiveProfileId?: string;\r\n\tactiveModelId?: string;\r\n\tprofiles: ProviderProfile[];\r\n}\r\n\r\nexport interface CreateProviderProfileInput {\r\n\tlabel: string;\r\n\tfamily: ProviderFamily;\r\n\tbaseUrl: string;\r\n\tapiDialect?: ProviderApiDialect;\r\n\tenabled?: boolean;\r\n\tapiKey?: string;\r\n}\r\n\r\nexport interface UpdateProviderProfileInput {\r\n\tlabel?: string;\r\n\tbaseUrl?: string;\r\n\tapiDialect?: ProviderApiDialect;\r\n\tenabled?: boolean;\r\n\tapiKey?: string;\r\n}\r\n\r\nexport interface RefreshModelsResult {\r\n\tprofile: ProviderProfile;\r\n\tmodels: DiscoveredModel[];\r\n}\r\n\r\nexport interface FailedRefreshModelsResult {\r\n\tprofile: ProviderProfile;\r\n\terror: string;\r\n}\r\n"],"mappings":"","ignoreList":[]}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { randomUUID } from "crypto";
|
|
2
|
+
import { getSessionsPath } from "../config.js";
|
|
3
|
+
import { LockedJsonFile } from "../storage/locked-json-file.js";
|
|
4
|
+
function nowIso() {
|
|
5
|
+
return new Date().toISOString();
|
|
6
|
+
}
|
|
7
|
+
function createDefaultSessionsConfig() {
|
|
8
|
+
return {
|
|
9
|
+
version: 1,
|
|
10
|
+
sessions: []
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function cloneSession(record) {
|
|
14
|
+
return structuredClone(record);
|
|
15
|
+
}
|
|
16
|
+
export class SessionHistoryService {
|
|
17
|
+
constructor(filePath = getSessionsPath()) {
|
|
18
|
+
this.storage = new LockedJsonFile(filePath, createDefaultSessionsConfig());
|
|
19
|
+
}
|
|
20
|
+
listSessions() {
|
|
21
|
+
const config = this.storage.read();
|
|
22
|
+
return config.sessions.slice().sort((left, right) => right.updatedAt.localeCompare(left.updatedAt)).map(cloneSession);
|
|
23
|
+
}
|
|
24
|
+
ensureSeed(seed) {
|
|
25
|
+
return this.storage.withLock(current => {
|
|
26
|
+
if (current.sessions.length > 0) {
|
|
27
|
+
return {
|
|
28
|
+
result: current.sessions.slice().sort((left, right) => right.updatedAt.localeCompare(left.updatedAt)).map(cloneSession)
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
const timestamp = nowIso();
|
|
32
|
+
const sessions = seed.map(item => ({
|
|
33
|
+
id: randomUUID(),
|
|
34
|
+
title: item.title,
|
|
35
|
+
summary: item.summary,
|
|
36
|
+
state: item.state ?? "ready",
|
|
37
|
+
createdAt: timestamp,
|
|
38
|
+
updatedAt: timestamp,
|
|
39
|
+
providerProfileId: item.providerProfileId,
|
|
40
|
+
providerLabel: item.providerLabel,
|
|
41
|
+
modelId: item.modelId,
|
|
42
|
+
lastPrompt: item.lastPrompt,
|
|
43
|
+
runtimeSessionFile: item.runtimeSessionFile
|
|
44
|
+
}));
|
|
45
|
+
return {
|
|
46
|
+
result: sessions.map(cloneSession),
|
|
47
|
+
next: {
|
|
48
|
+
...current,
|
|
49
|
+
sessions
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
createSession(input) {
|
|
55
|
+
return this.storage.withLock(current => {
|
|
56
|
+
const timestamp = nowIso();
|
|
57
|
+
const session = {
|
|
58
|
+
id: randomUUID(),
|
|
59
|
+
title: input.title,
|
|
60
|
+
summary: input.summary,
|
|
61
|
+
state: input.state ?? "ready",
|
|
62
|
+
createdAt: timestamp,
|
|
63
|
+
updatedAt: timestamp,
|
|
64
|
+
providerProfileId: input.providerProfileId,
|
|
65
|
+
providerLabel: input.providerLabel,
|
|
66
|
+
modelId: input.modelId,
|
|
67
|
+
lastPrompt: input.lastPrompt,
|
|
68
|
+
runtimeSessionFile: input.runtimeSessionFile
|
|
69
|
+
};
|
|
70
|
+
return {
|
|
71
|
+
result: cloneSession(session),
|
|
72
|
+
next: {
|
|
73
|
+
...current,
|
|
74
|
+
sessions: [session, ...current.sessions].slice(0, 50)
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
updateSession(sessionId, input) {
|
|
80
|
+
return this.storage.withLock(current => {
|
|
81
|
+
const index = current.sessions.findIndex(session => session.id === sessionId);
|
|
82
|
+
if (index === -1) {
|
|
83
|
+
return {
|
|
84
|
+
result: undefined
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
const existing = current.sessions[index];
|
|
88
|
+
const nextRecord = {
|
|
89
|
+
...existing,
|
|
90
|
+
title: input.title ?? existing.title,
|
|
91
|
+
summary: input.summary ?? existing.summary,
|
|
92
|
+
state: input.state ?? existing.state,
|
|
93
|
+
providerProfileId: input.providerProfileId ?? existing.providerProfileId,
|
|
94
|
+
providerLabel: input.providerLabel ?? existing.providerLabel,
|
|
95
|
+
modelId: input.modelId ?? existing.modelId,
|
|
96
|
+
lastPrompt: input.lastPrompt ?? existing.lastPrompt,
|
|
97
|
+
runtimeSessionFile: input.runtimeSessionFile ?? existing.runtimeSessionFile,
|
|
98
|
+
updatedAt: nowIso()
|
|
99
|
+
};
|
|
100
|
+
const sessions = current.sessions.slice();
|
|
101
|
+
sessions[index] = nextRecord;
|
|
102
|
+
return {
|
|
103
|
+
result: cloneSession(nextRecord),
|
|
104
|
+
next: {
|
|
105
|
+
...current,
|
|
106
|
+
sessions
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
markActive(sessionId) {
|
|
112
|
+
return this.storage.withLock(current => {
|
|
113
|
+
let activeRecord;
|
|
114
|
+
const timestamp = nowIso();
|
|
115
|
+
const sessions = current.sessions.map(session => {
|
|
116
|
+
if (session.id === sessionId) {
|
|
117
|
+
activeRecord = {
|
|
118
|
+
...session,
|
|
119
|
+
state: "active",
|
|
120
|
+
updatedAt: timestamp
|
|
121
|
+
};
|
|
122
|
+
return activeRecord;
|
|
123
|
+
}
|
|
124
|
+
if (session.state === "active") {
|
|
125
|
+
return {
|
|
126
|
+
...session,
|
|
127
|
+
state: "recent",
|
|
128
|
+
updatedAt: session.updatedAt
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
return session;
|
|
132
|
+
});
|
|
133
|
+
return {
|
|
134
|
+
result: activeRecord ? cloneSession(activeRecord) : undefined,
|
|
135
|
+
next: activeRecord ? {
|
|
136
|
+
...current,
|
|
137
|
+
sessions
|
|
138
|
+
} : undefined
|
|
139
|
+
};
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["randomUUID","getSessionsPath","LockedJsonFile","nowIso","Date","toISOString","createDefaultSessionsConfig","version","sessions","cloneSession","record","structuredClone","SessionHistoryService","constructor","filePath","storage","listSessions","config","read","slice","sort","left","right","updatedAt","localeCompare","map","ensureSeed","seed","withLock","current","length","result","timestamp","item","id","title","summary","state","createdAt","providerProfileId","providerLabel","modelId","lastPrompt","runtimeSessionFile","next","createSession","input","session","updateSession","sessionId","index","findIndex","undefined","existing","nextRecord","markActive","activeRecord"],"sources":["session-history-service.ts"],"sourcesContent":["import { randomUUID } from \"crypto\";\r\nimport { getSessionsPath } from \"../config.js\";\r\nimport { LockedJsonFile } from \"../storage/locked-json-file.js\";\r\n\r\nexport type ShellSessionState = \"active\" | \"ready\" | \"recent\";\r\n\r\nexport interface ShellSessionRecord {\r\n\tid: string;\r\n\ttitle: string;\r\n\tsummary: string;\r\n\tstate: ShellSessionState;\r\n\tcreatedAt: string;\r\n\tupdatedAt: string;\r\n\tproviderProfileId?: string;\r\n\tproviderLabel?: string;\r\n\tmodelId?: string;\r\n\tlastPrompt?: string;\r\n\truntimeSessionFile?: string;\r\n}\r\n\r\ninterface SessionsConfig {\r\n\tversion: 1;\r\n\tsessions: ShellSessionRecord[];\r\n}\r\n\r\ninterface CreateShellSessionInput {\r\n\ttitle: string;\r\n\tsummary: string;\r\n\tstate?: ShellSessionState;\r\n\tproviderProfileId?: string;\r\n\tproviderLabel?: string;\r\n\tmodelId?: string;\r\n\tlastPrompt?: string;\r\n\truntimeSessionFile?: string;\r\n}\r\n\r\ninterface UpdateShellSessionInput {\r\n\ttitle?: string;\r\n\tsummary?: string;\r\n\tstate?: ShellSessionState;\r\n\tproviderProfileId?: string;\r\n\tproviderLabel?: string;\r\n\tmodelId?: string;\r\n\tlastPrompt?: string;\r\n\truntimeSessionFile?: string;\r\n}\r\n\r\nfunction nowIso(): string {\r\n\treturn new Date().toISOString();\r\n}\r\n\r\nfunction createDefaultSessionsConfig(): SessionsConfig {\r\n\treturn {\r\n\t\tversion: 1,\r\n\t\tsessions: [],\r\n\t};\r\n}\r\n\r\nfunction cloneSession(record: ShellSessionRecord): ShellSessionRecord {\r\n\treturn structuredClone(record);\r\n}\r\n\r\nexport class SessionHistoryService {\r\n\tprivate readonly storage: LockedJsonFile<SessionsConfig>;\r\n\r\n\tconstructor(filePath: string = getSessionsPath()) {\r\n\t\tthis.storage = new LockedJsonFile<SessionsConfig>(filePath, createDefaultSessionsConfig());\r\n\t}\r\n\r\n\tlistSessions(): ShellSessionRecord[] {\r\n\t\tconst config = this.storage.read();\r\n\t\treturn config.sessions\r\n\t\t\t.slice()\r\n\t\t\t.sort((left, right) => right.updatedAt.localeCompare(left.updatedAt))\r\n\t\t\t.map(cloneSession);\r\n\t}\r\n\r\n\tensureSeed(seed: CreateShellSessionInput[]): ShellSessionRecord[] {\r\n\t\treturn this.storage.withLock((current) => {\r\n\t\t\tif (current.sessions.length > 0) {\r\n\t\t\t\treturn {\r\n\t\t\t\t\tresult: current.sessions\r\n\t\t\t\t\t\t.slice()\r\n\t\t\t\t\t\t.sort((left, right) => right.updatedAt.localeCompare(left.updatedAt))\r\n\t\t\t\t\t\t.map(cloneSession),\r\n\t\t\t\t};\r\n\t\t\t}\r\n\r\n\t\t\tconst timestamp = nowIso();\r\n\t\t\tconst sessions = seed.map((item) => ({\r\n\t\t\t\tid: randomUUID(),\r\n\t\t\t\ttitle: item.title,\r\n\t\t\t\tsummary: item.summary,\r\n\t\t\t\tstate: item.state ?? \"ready\",\r\n\t\t\t\tcreatedAt: timestamp,\r\n\t\t\t\tupdatedAt: timestamp,\r\n\t\t\t\tproviderProfileId: item.providerProfileId,\r\n\t\t\t\tproviderLabel: item.providerLabel,\r\n\t\t\t\tmodelId: item.modelId,\r\n\t\t\t\tlastPrompt: item.lastPrompt,\r\n\t\t\t\truntimeSessionFile: item.runtimeSessionFile,\r\n\t\t\t}));\r\n\r\n\t\t\treturn {\r\n\t\t\t\tresult: sessions.map(cloneSession),\r\n\t\t\t\tnext: {\r\n\t\t\t\t\t...current,\r\n\t\t\t\t\tsessions,\r\n\t\t\t\t},\r\n\t\t\t};\r\n\t\t});\r\n\t}\r\n\r\n\tcreateSession(input: CreateShellSessionInput): ShellSessionRecord {\r\n\t\treturn this.storage.withLock((current) => {\r\n\t\t\tconst timestamp = nowIso();\r\n\t\t\tconst session: ShellSessionRecord = {\r\n\t\t\t\tid: randomUUID(),\r\n\t\t\t\ttitle: input.title,\r\n\t\t\t\tsummary: input.summary,\r\n\t\t\t\tstate: input.state ?? \"ready\",\r\n\t\t\t\tcreatedAt: timestamp,\r\n\t\t\t\tupdatedAt: timestamp,\r\n\t\t\t\tproviderProfileId: input.providerProfileId,\r\n\t\t\t\tproviderLabel: input.providerLabel,\r\n\t\t\t\tmodelId: input.modelId,\r\n\t\t\t\tlastPrompt: input.lastPrompt,\r\n\t\t\t\truntimeSessionFile: input.runtimeSessionFile,\r\n\t\t\t};\r\n\r\n\t\t\treturn {\r\n\t\t\t\tresult: cloneSession(session),\r\n\t\t\t\tnext: {\r\n\t\t\t\t\t...current,\r\n\t\t\t\t\tsessions: [session, ...current.sessions].slice(0, 50),\r\n\t\t\t\t},\r\n\t\t\t};\r\n\t\t});\r\n\t}\r\n\r\n\tupdateSession(sessionId: string, input: UpdateShellSessionInput): ShellSessionRecord | undefined {\r\n\t\treturn this.storage.withLock((current) => {\r\n\t\t\tconst index = current.sessions.findIndex((session) => session.id === sessionId);\r\n\t\t\tif (index === -1) {\r\n\t\t\t\treturn { result: undefined };\r\n\t\t\t}\r\n\r\n\t\t\tconst existing = current.sessions[index]!;\r\n\t\t\tconst nextRecord: ShellSessionRecord = {\r\n\t\t\t\t...existing,\r\n\t\t\t\ttitle: input.title ?? existing.title,\r\n\t\t\t\tsummary: input.summary ?? existing.summary,\r\n\t\t\t\tstate: input.state ?? existing.state,\r\n\t\t\t\tproviderProfileId: input.providerProfileId ?? existing.providerProfileId,\r\n\t\t\t\tproviderLabel: input.providerLabel ?? existing.providerLabel,\r\n\t\t\t\tmodelId: input.modelId ?? existing.modelId,\r\n\t\t\t\tlastPrompt: input.lastPrompt ?? existing.lastPrompt,\r\n\t\t\t\truntimeSessionFile: input.runtimeSessionFile ?? existing.runtimeSessionFile,\r\n\t\t\t\tupdatedAt: nowIso(),\r\n\t\t\t};\r\n\r\n\t\t\tconst sessions = current.sessions.slice();\r\n\t\t\tsessions[index] = nextRecord;\r\n\r\n\t\t\treturn {\r\n\t\t\t\tresult: cloneSession(nextRecord),\r\n\t\t\t\tnext: {\r\n\t\t\t\t\t...current,\r\n\t\t\t\t\tsessions,\r\n\t\t\t\t},\r\n\t\t\t};\r\n\t\t});\r\n\t}\r\n\r\n\tmarkActive(sessionId: string): ShellSessionRecord | undefined {\r\n\t\treturn this.storage.withLock((current) => {\r\n\t\t\tlet activeRecord: ShellSessionRecord | undefined;\r\n\t\t\tconst timestamp = nowIso();\r\n\t\t\tconst sessions: ShellSessionRecord[] = current.sessions.map((session): ShellSessionRecord => {\r\n\t\t\t\tif (session.id === sessionId) {\r\n\t\t\t\t\tactiveRecord = {\r\n\t\t\t\t\t\t...session,\r\n\t\t\t\t\t\tstate: \"active\",\r\n\t\t\t\t\t\tupdatedAt: timestamp,\r\n\t\t\t\t\t};\r\n\t\t\t\t\treturn activeRecord;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif (session.state === \"active\") {\r\n\t\t\t\t\treturn {\r\n\t\t\t\t\t\t...session,\r\n\t\t\t\t\t\tstate: \"recent\",\r\n\t\t\t\t\t\tupdatedAt: session.updatedAt,\r\n\t\t\t\t\t};\r\n\t\t\t\t}\r\n\r\n\t\t\t\treturn session;\r\n\t\t\t});\r\n\r\n\t\t\treturn {\r\n\t\t\t\tresult: activeRecord ? cloneSession(activeRecord) : undefined,\r\n\t\t\t\tnext: activeRecord\r\n\t\t\t\t\t? {\r\n\t\t\t\t\t\t\t...current,\r\n\t\t\t\t\t\t\tsessions,\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t: undefined,\r\n\t\t\t};\r\n\t\t});\r\n\t}\r\n}\r\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export class ShellAnimator {
|
|
2
|
+
frame = 0;
|
|
3
|
+
startedAt = Date.now();
|
|
4
|
+
focusPulseUntil = 0;
|
|
5
|
+
constructor(ui) {
|
|
6
|
+
this.ui = ui;
|
|
7
|
+
this.interval = setInterval(() => {
|
|
8
|
+
this.frame += 1;
|
|
9
|
+
this.ui.requestRender();
|
|
10
|
+
}, 120);
|
|
11
|
+
}
|
|
12
|
+
dispose() {
|
|
13
|
+
if (!this.interval) return;
|
|
14
|
+
clearInterval(this.interval);
|
|
15
|
+
this.interval = undefined;
|
|
16
|
+
}
|
|
17
|
+
markFocusPulse() {
|
|
18
|
+
this.focusPulseUntil = Date.now() + 500;
|
|
19
|
+
}
|
|
20
|
+
getSnapshot() {
|
|
21
|
+
const elapsed = Date.now() - this.startedAt;
|
|
22
|
+
return {
|
|
23
|
+
frame: this.frame,
|
|
24
|
+
showMain: elapsed >= 180,
|
|
25
|
+
showStatus: elapsed >= 360,
|
|
26
|
+
showDock: elapsed >= 540,
|
|
27
|
+
focusPulseActive: Date.now() < this.focusPulseUntil
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["ShellAnimator","frame","startedAt","Date","now","focusPulseUntil","constructor","ui","interval","setInterval","requestRender","dispose","clearInterval","undefined","markFocusPulse","getSnapshot","elapsed","showMain","showStatus","showDock","focusPulseActive"],"sources":["animator.ts"],"sourcesContent":["import type { TUI } from \"@mariozechner/pi-tui\";\r\n\r\nexport interface ShellAnimationSnapshot {\r\n\tframe: number;\r\n\tshowMain: boolean;\r\n\tshowStatus: boolean;\r\n\tshowDock: boolean;\r\n\tfocusPulseActive: boolean;\r\n}\r\n\r\nexport class ShellAnimator {\r\n\tprivate frame = 0;\r\n\tprivate interval: NodeJS.Timeout | undefined;\r\n\tprivate readonly startedAt = Date.now();\r\n\tprivate focusPulseUntil = 0;\r\n\r\n\tconstructor(private readonly ui: TUI) {\r\n\t\tthis.interval = setInterval(() => {\r\n\t\t\tthis.frame += 1;\r\n\t\t\tthis.ui.requestRender();\r\n\t\t}, 120);\r\n\t}\r\n\r\n\tdispose(): void {\r\n\t\tif (!this.interval) return;\r\n\t\tclearInterval(this.interval);\r\n\t\tthis.interval = undefined;\r\n\t}\r\n\r\n\tmarkFocusPulse(): void {\r\n\t\tthis.focusPulseUntil = Date.now() + 500;\r\n\t}\r\n\r\n\tgetSnapshot(): ShellAnimationSnapshot {\r\n\t\tconst elapsed = Date.now() - this.startedAt;\r\n\t\treturn {\r\n\t\t\tframe: this.frame,\r\n\t\t\tshowMain: elapsed >= 180,\r\n\t\t\tshowStatus: elapsed >= 360,\r\n\t\t\tshowDock: elapsed >= 540,\r\n\t\t\tfocusPulseActive: Date.now() < this.focusPulseUntil,\r\n\t\t};\r\n\t}\r\n}\r\n"],"mappings":"AAUA,OAAO,MAAMA,aAAa,CAAC;EAClBC,KAAK,GAAG,CAAC;EAEAC,SAAS,GAAGC,IAAI,CAACC,GAAG,CAAC,CAAC;EAC/BC,eAAe,GAAG,CAAC;EAE3BC,WAAWA,CAAkBC,EAAO,EAAE;IAAA,KAATA,EAAO,GAAPA,EAAO;IACnC,IAAI,CAACC,QAAQ,GAAGC,WAAW,CAAC,MAAM;MACjC,IAAI,CAACR,KAAK,IAAI,CAAC;MACf,IAAI,CAACM,EAAE,CAACG,aAAa,CAAC,CAAC;IACxB,CAAC,EAAE,GAAG,CAAC;EACR;EAEAC,OAAOA,CAAA,EAAS;IACf,IAAI,CAAC,IAAI,CAACH,QAAQ,EAAE;IACpBI,aAAa,CAAC,IAAI,CAACJ,QAAQ,CAAC;IAC5B,IAAI,CAACA,QAAQ,GAAGK,SAAS;EAC1B;EAEAC,cAAcA,CAAA,EAAS;IACtB,IAAI,CAACT,eAAe,GAAGF,IAAI,CAACC,GAAG,CAAC,CAAC,GAAG,GAAG;EACxC;EAEAW,WAAWA,CAAA,EAA2B;IACrC,MAAMC,OAAO,GAAGb,IAAI,CAACC,GAAG,CAAC,CAAC,GAAG,IAAI,CAACF,SAAS;IAC3C,OAAO;MACND,KAAK,EAAE,IAAI,CAACA,KAAK;MACjBgB,QAAQ,EAAED,OAAO,IAAI,GAAG;MACxBE,UAAU,EAAEF,OAAO,IAAI,GAAG;MAC1BG,QAAQ,EAAEH,OAAO,IAAI,GAAG;MACxBI,gBAAgB,EAAEjB,IAAI,CAACC,GAAG,CAAC,CAAC,GAAG,IAAI,CAACC;IACrC,CAAC;EACF;AACD","ignoreList":[]}
|