om-memory-system 3.5.0-next.32 → 3.5.0-next.34

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.
@@ -15,6 +15,8 @@ export async function runWebCommand(args, options = {}, online) {
15
15
  console.error("OMMS web server is disabled (webServerEnabled is false)");
16
16
  return 1;
17
17
  }
18
+ const { webServerUrl } = await import("../services/web-api-auth.js");
19
+ const url = webServerUrl(config.CONFIG.webServerHost, config.CONFIG.webServerPort);
18
20
  if (action === "install" || action === "uninstall") {
19
21
  const { readGlobalConfigRevision, writeGlobalConfigKeys } = await import("../services/global-config-writer.js");
20
22
  await writeGlobalConfigKeys({ webServerAutoStart: action === "install" }, readGlobalConfigRevision());
@@ -23,6 +25,8 @@ export async function runWebCommand(args, options = {}, online) {
23
25
  ? installWebAutostart({ ...options, start: true })
24
26
  : removeWebAutostart({ ...options, start: true });
25
27
  console.log(`OMMS login item: ${result.state}`);
28
+ if (action === "install" && result.state === "installed")
29
+ console.log(`OMMS web app: ${url}`);
26
30
  return action === "install" && result.state !== "installed" ? 1 : 0;
27
31
  }
28
32
  if (action === "status") {
@@ -37,7 +41,7 @@ export async function runWebCommand(args, options = {}, online) {
37
41
  apiToken: config.CONFIG.webServerApiToken,
38
42
  }).checkServerAvailable();
39
43
  });
40
- console.log(JSON.stringify({ setting: config.CONFIG.webServerAutoStart, item: status, online: await check() }, null, 2));
44
+ console.log(JSON.stringify({ setting: config.CONFIG.webServerAutoStart, item: status, url, online: await check() }, null, 2));
41
45
  return 0;
42
46
  }
43
47
  if (!config.CONFIG.webServerEnabled) {
@@ -24,7 +24,8 @@ export async function importReadiness(deps = {}) {
24
24
  const [state, opencode, piReader] = await Promise.all([
25
25
  externalApiState(deps.supportedProviders),
26
26
  (deps.listOpencodeModels ??
27
- (async () => (await import("./settings-models.js")).listOpencodeSettingsModels()))(),
27
+ // Imports run OpenCode models, so they need a session, not the standalone list.
28
+ (async () => (await import("./settings-models.js")).listOpencodeSettingsModels(undefined, null)))(),
28
29
  (deps.loadPiSdk ?? (() => import("@earendil-works/pi-coding-agent")))().then(() => ({ available: true }), () => ({
29
30
  available: false,
30
31
  reason: "The Pi SDK (@earendil-works/pi-coding-agent) is not installed for OpenCode",
@@ -0,0 +1,55 @@
1
+ import { type SpawnOptions } from "node:child_process";
2
+ import type { SettingsModel } from "./backfill-controls.js";
3
+ /**
4
+ * Lists OpenCode's signed-in models when no OpenCode session serves the web app.
5
+ * It starts a private `opencode serve` on loopback with its own password, reads
6
+ * the list over HTTP, and stops the server. See docs/tdr/014 for the v2 API.
7
+ *
8
+ * OpenCode's `/api/model` reply holds provider API keys. Records are cut down to
9
+ * provider, model, and name as soon as they are parsed. Nothing here logs,
10
+ * keeps, or returns the reply body, the child's output, or the password.
11
+ */
12
+ export type StandaloneResult = {
13
+ outcome: "listed";
14
+ models: SettingsModel[];
15
+ } | {
16
+ outcome: "not_found" | "start_timeout" | "empty" | "unreadable";
17
+ };
18
+ interface OutputStream {
19
+ on(event: "data", listener: (chunk: Buffer | string) => void): unknown;
20
+ }
21
+ export interface ChildLike {
22
+ pid?: number;
23
+ stdout: OutputStream | null;
24
+ stderr: OutputStream | null;
25
+ exitCode: number | null;
26
+ signalCode: NodeJS.Signals | null;
27
+ once(event: "exit" | "error", listener: () => void): unknown;
28
+ kill(signal?: NodeJS.Signals): boolean;
29
+ }
30
+ export interface StandaloneDeps {
31
+ spawn(command: string, args: string[], options: SpawnOptions): ChildLike;
32
+ fetch(url: string, init: {
33
+ headers: Record<string, string>;
34
+ signal?: AbortSignal;
35
+ }): Promise<Response>;
36
+ now(): number;
37
+ sleep(ms: number): Promise<void>;
38
+ isFile(path: string): boolean;
39
+ kill(child: ChildLike, signal: "SIGTERM" | "SIGKILL"): void;
40
+ env: Record<string, string | undefined>;
41
+ platform: NodeJS.Platform;
42
+ home: string;
43
+ password(): string;
44
+ log(message: string, data: Record<string, unknown>): void;
45
+ }
46
+ /**
47
+ * A reader that shares one read between callers at the same time, and keeps a
48
+ * listed result for 5 minutes and a failed one for 30 seconds.
49
+ */
50
+ export declare function createStandaloneOpencodeReader(deps: StandaloneDeps): () => Promise<StandaloneResult>;
51
+ /** Reads OpenCode's signed-in models through a private `opencode serve`. */
52
+ export declare function readStandaloneOpencodeModels(): Promise<StandaloneResult>;
53
+ /** Stops every child that a read started, and waits until each one has exited or been killed. */
54
+ export declare function stopStandaloneOpencodeReads(): Promise<void>;
55
+ export {};
@@ -0,0 +1,318 @@
1
+ import { spawn as nodeSpawn } from "node:child_process";
2
+ import { randomBytes } from "node:crypto";
3
+ import { accessSync, constants, statSync } from "node:fs";
4
+ import { homedir } from "node:os";
5
+ import { posix, win32 } from "node:path";
6
+ import { log } from "../services/logger.js";
7
+ import { connectedProviderModels } from "./settings-models.js";
8
+ const START_LIMIT_MS = 10_000;
9
+ const LIST_LIMIT_MS = 5_000;
10
+ const POLL_MS = 250;
11
+ // OpenCode v2.0.18 takes about 3.6 s to exit on SIGTERM once its model list is loaded.
12
+ const KILL_WAIT_MS = 5_000;
13
+ const OUTPUT_CAP = 16 * 1024;
14
+ const LISTED_KEEP_MS = 5 * 60_000;
15
+ const FAILED_KEEP_MS = 30_000;
16
+ const SERVE_ARGS = ["serve", "--hostname=127.0.0.1", "--port=0"];
17
+ /** Stop functions for children that are still running, for the web server's shutdown. */
18
+ const liveChildren = new Set();
19
+ function envValue(deps, name) {
20
+ if (deps.platform !== "win32")
21
+ return deps.env[name];
22
+ const key = Object.keys(deps.env).find((entry) => entry.toUpperCase() === name);
23
+ return key === undefined ? undefined : deps.env[key];
24
+ }
25
+ /** Checks `~/.opencode/bin`, then `PATH`, then the usual install folders. */
26
+ function findOpencode(deps) {
27
+ const windows = deps.platform === "win32";
28
+ const path = windows ? win32 : posix;
29
+ const pathFolders = (envValue(deps, "PATH") ?? "")
30
+ .split(windows ? ";" : ":")
31
+ .map((folder) => folder.trim().replace(/^"(.*)"$/, "$1"))
32
+ .filter(Boolean);
33
+ const folders = [
34
+ path.join(deps.home, ".opencode", "bin"),
35
+ ...pathFolders,
36
+ ...(windows ? [] : ["/opt/homebrew/bin", "/usr/local/bin"]),
37
+ path.join(deps.home, ".bun", "bin"),
38
+ ];
39
+ const extensions = windows
40
+ ? (envValue(deps, "PATHEXT") ?? ".com;.exe;.bat;.cmd").split(";").filter(Boolean)
41
+ : [""];
42
+ for (const folder of folders) {
43
+ for (const extension of extensions) {
44
+ const candidate = path.join(folder, `opencode${extension}`);
45
+ if (deps.isFile(candidate))
46
+ return candidate;
47
+ }
48
+ }
49
+ return null;
50
+ }
51
+ function startServer(deps, program, password) {
52
+ const options = {
53
+ cwd: deps.home,
54
+ env: { ...deps.env, OPENCODE_SERVER_PASSWORD: password },
55
+ stdio: ["ignore", "pipe", "pipe"],
56
+ windowsHide: true,
57
+ };
58
+ if (deps.platform === "win32" && /\.(cmd|bat)$/i.test(program)) {
59
+ // cmd.exe with /s strips the outer quotes, so the program path keeps its own.
60
+ const command = `""${program}" ${SERVE_ARGS.join(" ")}"`;
61
+ return deps.spawn(envValue(deps, "COMSPEC") ?? "cmd.exe", ["/d", "/s", "/c", command], {
62
+ ...options,
63
+ windowsVerbatimArguments: true,
64
+ });
65
+ }
66
+ // A new process group on POSIX, so a wrapper script and its server stop together.
67
+ return deps.spawn(program, SERVE_ARGS, { ...options, detached: deps.platform !== "win32" });
68
+ }
69
+ /** Resolves with the server URL, or null when the child exits or misses the limit. */
70
+ function waitForUrl(deps, child, password) {
71
+ return new Promise((resolve) => {
72
+ let output = "";
73
+ const read = (chunk) => {
74
+ // Keep reading after the match, so a full pipe never blocks the child.
75
+ output = (output + chunk.toString()).slice(-OUTPUT_CAP).split(password).join("[redacted]");
76
+ const url = output.match(/listening on (https?:\/\/\S+)/)?.[1];
77
+ if (url)
78
+ resolve(url.replace(/\/+$/, ""));
79
+ };
80
+ child.stdout?.on("data", read);
81
+ child.stderr?.on("data", read);
82
+ child.once("exit", () => resolve(null));
83
+ child.once("error", () => resolve(null));
84
+ void deps.sleep(START_LIMIT_MS).then(() => resolve(null));
85
+ });
86
+ }
87
+ function mapV2(body) {
88
+ const data = body?.data;
89
+ if (!Array.isArray(data))
90
+ return null;
91
+ const models = [];
92
+ for (const entry of data) {
93
+ const { providerID, id, name } = entry ?? {};
94
+ if (typeof providerID !== "string" || typeof id !== "string")
95
+ return null;
96
+ models.push({
97
+ provider: providerID,
98
+ model: id,
99
+ name: typeof name === "string" && name ? name : id,
100
+ });
101
+ }
102
+ return models;
103
+ }
104
+ function mapV1(body) {
105
+ const list = body;
106
+ if (!Array.isArray(list?.all) || !Array.isArray(list?.connected))
107
+ return null;
108
+ try {
109
+ return connectedProviderModels(list);
110
+ }
111
+ catch {
112
+ return null;
113
+ }
114
+ }
115
+ async function readList(deps, url, auth, route, deadline) {
116
+ let response;
117
+ let text;
118
+ try {
119
+ const signal = AbortSignal.timeout(Math.max(POLL_MS, deadline - deps.now()));
120
+ response = await deps.fetch(`${url}${route === "v2" ? "/api/model" : "/provider"}`, {
121
+ headers: { authorization: auth },
122
+ signal,
123
+ });
124
+ text = await response.text();
125
+ }
126
+ catch {
127
+ return { kind: "no_reply" };
128
+ }
129
+ if (response.status === 404)
130
+ return route === "v2" ? { kind: "not_v2" } : { kind: "unreadable" };
131
+ if (!response.ok)
132
+ return { kind: "no_reply" };
133
+ let body;
134
+ try {
135
+ body = JSON.parse(text);
136
+ }
137
+ catch {
138
+ return route === "v2" ? { kind: "not_v2" } : { kind: "unreadable" };
139
+ }
140
+ const models = route === "v2" ? mapV2(body) : mapV1(body);
141
+ return models ? { kind: "models", models } : { kind: "unreadable" };
142
+ }
143
+ /** Polls until two replies in a row have the same non-empty count, or the limit. */
144
+ async function pollModels(deps, url, password) {
145
+ const auth = `Basic ${Buffer.from(`opencode:${password}`).toString("base64")}`;
146
+ const deadline = deps.now() + LIST_LIMIT_MS;
147
+ let route = "v2";
148
+ let previous = null;
149
+ let latest = [];
150
+ for (;;) {
151
+ let reply = await readList(deps, url, auth, route, deadline);
152
+ if (reply.kind === "not_v2") {
153
+ route = "v1";
154
+ reply = await readList(deps, url, auth, route, deadline);
155
+ }
156
+ if (reply.kind === "unreadable" || reply.kind === "not_v2")
157
+ return { outcome: "unreadable" };
158
+ if (reply.kind === "models") {
159
+ const count = reply.models.length;
160
+ if (count > 0 && previous?.length === count)
161
+ return { outcome: "listed", models: reply.models };
162
+ previous = reply.models;
163
+ if (count > 0)
164
+ latest = reply.models;
165
+ }
166
+ if (deps.now() >= deadline)
167
+ break;
168
+ await deps.sleep(POLL_MS);
169
+ }
170
+ if (latest.length > 0)
171
+ return { outcome: "listed", models: latest };
172
+ return previous ? { outcome: "empty" } : { outcome: "start_timeout" };
173
+ }
174
+ /** Sends SIGTERM, then SIGKILL if the child is still running 5 seconds later. */
175
+ async function stopChild(deps, child) {
176
+ const running = () => child.exitCode === null && child.signalCode === null;
177
+ if (!running())
178
+ return;
179
+ const exited = new Promise((resolve) => child.once("exit", () => resolve(true)));
180
+ try {
181
+ deps.kill(child, "SIGTERM");
182
+ }
183
+ catch {
184
+ // The child may exit between the check and the signal.
185
+ }
186
+ const stopped = await Promise.race([exited, deps.sleep(KILL_WAIT_MS).then(() => false)]);
187
+ if (!stopped && running()) {
188
+ try {
189
+ deps.kill(child, "SIGKILL");
190
+ }
191
+ catch {
192
+ // Already gone.
193
+ }
194
+ }
195
+ }
196
+ async function readOnce(deps) {
197
+ const began = deps.now();
198
+ const program = findOpencode(deps);
199
+ if (!program) {
200
+ deps.log("OpenCode standalone model list", { outcome: "not_found", count: 0 });
201
+ return { outcome: "not_found" };
202
+ }
203
+ const password = deps.password();
204
+ let child = null;
205
+ let stop = null;
206
+ let startMs = null;
207
+ let listMs = 0;
208
+ let result;
209
+ try {
210
+ const started = startServer(deps, program, password);
211
+ child = started;
212
+ let stopping = null;
213
+ stop = () => (stopping ??= stopChild(deps, started));
214
+ liveChildren.add(stop);
215
+ const url = await waitForUrl(deps, started, password);
216
+ if (!url) {
217
+ result = { outcome: "start_timeout" };
218
+ }
219
+ else {
220
+ startMs = deps.now() - began;
221
+ result = await pollModels(deps, url, password);
222
+ listMs = deps.now() - began - startMs;
223
+ }
224
+ }
225
+ catch {
226
+ result = { outcome: child ? "unreadable" : "start_timeout" };
227
+ }
228
+ finally {
229
+ // Stop in the background, so the page gets its list without waiting for the exit.
230
+ if (stop) {
231
+ const done = stop;
232
+ void done().finally(() => liveChildren.delete(done));
233
+ }
234
+ }
235
+ deps.log("OpenCode standalone model list", {
236
+ outcome: result.outcome,
237
+ count: result.outcome === "listed" ? result.models.length : 0,
238
+ ...(startMs === null ? {} : { startMs, listMs }),
239
+ });
240
+ return result;
241
+ }
242
+ /**
243
+ * A reader that shares one read between callers at the same time, and keeps a
244
+ * listed result for 5 minutes and a failed one for 30 seconds.
245
+ */
246
+ export function createStandaloneOpencodeReader(deps) {
247
+ let inflight = null;
248
+ let kept = null;
249
+ return () => {
250
+ if (kept && deps.now() < kept.until)
251
+ return Promise.resolve(kept.result);
252
+ inflight ??= readOnce(deps)
253
+ .then((result) => {
254
+ const keepMs = result.outcome === "listed" ? LISTED_KEEP_MS : FAILED_KEEP_MS;
255
+ kept = { result, until: deps.now() + keepMs };
256
+ return result;
257
+ })
258
+ .finally(() => {
259
+ inflight = null;
260
+ });
261
+ return inflight;
262
+ };
263
+ }
264
+ function isProgramFile(path) {
265
+ try {
266
+ if (!statSync(path).isFile())
267
+ return false;
268
+ if (process.platform !== "win32")
269
+ accessSync(path, constants.X_OK);
270
+ return true;
271
+ }
272
+ catch {
273
+ return false;
274
+ }
275
+ }
276
+ function killTree(child, signal) {
277
+ if (process.platform === "win32") {
278
+ // Windows has no signals; end the whole tree, which includes a .cmd wrapper's server.
279
+ if (child.pid)
280
+ nodeSpawn("taskkill", ["/pid", String(child.pid), "/T", "/F"], {
281
+ stdio: "ignore",
282
+ windowsHide: true,
283
+ }).on("error", () => child.kill(signal));
284
+ else
285
+ child.kill(signal);
286
+ return;
287
+ }
288
+ try {
289
+ if (!child.pid)
290
+ throw new Error("no pid");
291
+ process.kill(-child.pid, signal);
292
+ }
293
+ catch {
294
+ child.kill(signal);
295
+ }
296
+ }
297
+ let defaultReader = null;
298
+ /** Reads OpenCode's signed-in models through a private `opencode serve`. */
299
+ export function readStandaloneOpencodeModels() {
300
+ defaultReader ??= createStandaloneOpencodeReader({
301
+ spawn: (command, args, options) => nodeSpawn(command, args, options),
302
+ fetch: (url, init) => fetch(url, init),
303
+ now: Date.now,
304
+ sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms).unref?.()),
305
+ isFile: isProgramFile,
306
+ kill: killTree,
307
+ env: process.env,
308
+ platform: process.platform,
309
+ home: homedir(),
310
+ password: () => randomBytes(24).toString("base64url"),
311
+ log,
312
+ });
313
+ return defaultReader();
314
+ }
315
+ /** Stops every child that a read started, and waits until each one has exited or been killed. */
316
+ export async function stopStandaloneOpencodeReads() {
317
+ await Promise.all([...liveChildren].map((stop) => stop()));
318
+ }
@@ -1,15 +1,36 @@
1
1
  import type { OpencodeClient } from "@opencode-ai/sdk/v2/client";
2
2
  import type { SettingsModel } from "./backfill-controls.js";
3
+ import type { StandaloneResult } from "./opencode-standalone-models.js";
4
+ export declare const OPENCODE_LIST_UNAVAILABLE = "OpenCode model list unavailable";
5
+ export declare const OPENCODE_NOT_FOUND = "OMMS could not find OpenCode on this computer, so it cannot list OpenCode's models here. Type the model as provider/model, for example zai-coding-plan/glm-5.3. If OpenCode is installed, open an OpenCode session and reload this page.";
6
+ export declare const OPENCODE_TOO_SLOW = "OpenCode took too long to send its model list. Reload this page to try again, or type the model as provider/model.";
7
+ export declare const OPENCODE_NO_MODELS = "OpenCode has no signed-in models. Run `opencode auth login`, then reload this page.";
8
+ export declare const OPENCODE_UNREADABLE = "This OpenCode version sent a model list OMMS cannot read. Type the model as provider/model. Update OMMS if this continues.";
9
+ export declare const PI_LIST_UNAVAILABLE = "OMMS could not read Pi's model list. Type the model as provider/model.";
10
+ export declare const PI_NO_MODELS = "Pi has no signed-in models. Sign in to a provider in Pi, then reload this page.";
11
+ export declare const SETTINGS_MODEL_REASONS: readonly ["OpenCode model list unavailable", "OMMS could not find OpenCode on this computer, so it cannot list OpenCode's models here. Type the model as provider/model, for example zai-coding-plan/glm-5.3. If OpenCode is installed, open an OpenCode session and reload this page.", "OpenCode took too long to send its model list. Reload this page to try again, or type the model as provider/model.", "OpenCode has no signed-in models. Run `opencode auth login`, then reload this page.", "This OpenCode version sent a model list OMMS cannot read. Type the model as provider/model. Update OMMS if this continues.", "OMMS could not read Pi's model list. Type the model as provider/model.", "Pi has no signed-in models. Sign in to a provider in Pi, then reload this page."];
12
+ type ProviderList = {
13
+ connected: string[];
14
+ all: Array<{
15
+ id: string;
16
+ models: Record<string, {
17
+ name: string;
18
+ }>;
19
+ }>;
20
+ };
21
+ /** The models of each connected provider in an OpenCode v1 provider list. */
22
+ export declare function connectedProviderModels(list: ProviderList): SettingsModel[];
3
23
  /** Connected models from an OpenCode client; null when the client returns no list. */
4
24
  export declare function listOpencodeClientModels(client: Pick<OpencodeClient, "provider">): Promise<SettingsModel[] | null>;
5
- export declare function listOpencodeSettingsModels(client?: Pick<OpencodeClient, "provider">): Promise<{
6
- available: false;
7
- reason: string;
8
- models?: undefined;
9
- } | {
10
- available: true;
11
- models: SettingsModel[];
12
- reason?: undefined;
25
+ /**
26
+ * The OpenCode models for the Settings page. Without a client or a registered
27
+ * OpenCode host, this starts a private OpenCode server through `readStandalone`.
28
+ * Pass `null` to skip that, for callers that need a live session.
29
+ */
30
+ export declare function listOpencodeSettingsModels(client?: Pick<OpencodeClient, "provider">, readStandalone?: (() => Promise<StandaloneResult>) | null): Promise<{
31
+ available: boolean;
32
+ models?: SettingsModel[];
33
+ reason?: string;
13
34
  }>;
14
35
  export declare function listPiSettingsModels(loadSdk?: () => Promise<typeof import("@earendil-works/pi-coding-agent")>): Promise<{
15
36
  available: true;
@@ -32,3 +53,4 @@ export declare function listPiSettingsModels(loadSdk?: () => Promise<typeof impo
32
53
  reason: string;
33
54
  models?: undefined;
34
55
  }>;
56
+ export {};
@@ -1,10 +1,31 @@
1
- /** Connected models from an OpenCode client; null when the client returns no list. */
2
- export async function listOpencodeClientModels(client) {
3
- const result = await client.provider.list();
4
- if (!result.data)
5
- return null;
6
- const connected = new Set(result.data.connected);
7
- return result.data.all
1
+ // Reasons the Settings page shows for a missing model list. The page translates
2
+ // each one, so a change here needs matching text in web/src/lib/i18n/settings.ts.
3
+ export const OPENCODE_LIST_UNAVAILABLE = "OpenCode model list unavailable";
4
+ export const OPENCODE_NOT_FOUND = "OMMS could not find OpenCode on this computer, so it cannot list OpenCode's models here. Type the model as provider/model, for example zai-coding-plan/glm-5.3. If OpenCode is installed, open an OpenCode session and reload this page.";
5
+ export const OPENCODE_TOO_SLOW = "OpenCode took too long to send its model list. Reload this page to try again, or type the model as provider/model.";
6
+ export const OPENCODE_NO_MODELS = "OpenCode has no signed-in models. Run `opencode auth login`, then reload this page.";
7
+ export const OPENCODE_UNREADABLE = "This OpenCode version sent a model list OMMS cannot read. Type the model as provider/model. Update OMMS if this continues.";
8
+ export const PI_LIST_UNAVAILABLE = "OMMS could not read Pi's model list. Type the model as provider/model.";
9
+ export const PI_NO_MODELS = "Pi has no signed-in models. Sign in to a provider in Pi, then reload this page.";
10
+ export const SETTINGS_MODEL_REASONS = [
11
+ OPENCODE_LIST_UNAVAILABLE,
12
+ OPENCODE_NOT_FOUND,
13
+ OPENCODE_TOO_SLOW,
14
+ OPENCODE_NO_MODELS,
15
+ OPENCODE_UNREADABLE,
16
+ PI_LIST_UNAVAILABLE,
17
+ PI_NO_MODELS,
18
+ ];
19
+ const STANDALONE_REASONS = {
20
+ not_found: OPENCODE_NOT_FOUND,
21
+ start_timeout: OPENCODE_TOO_SLOW,
22
+ empty: OPENCODE_NO_MODELS,
23
+ unreadable: OPENCODE_UNREADABLE,
24
+ };
25
+ /** The models of each connected provider in an OpenCode v1 provider list. */
26
+ export function connectedProviderModels(list) {
27
+ const connected = new Set(list.connected);
28
+ return list.all
8
29
  .filter((provider) => connected.has(provider.id))
9
30
  .flatMap((provider) => Object.entries(provider.models).map(([model, info]) => ({
10
31
  provider: provider.id,
@@ -12,7 +33,24 @@ export async function listOpencodeClientModels(client) {
12
33
  name: info.name,
13
34
  })));
14
35
  }
15
- export async function listOpencodeSettingsModels(client) {
36
+ /** Connected models from an OpenCode client; null when the client returns no list. */
37
+ export async function listOpencodeClientModels(client) {
38
+ const result = await client.provider.list();
39
+ if (!result.data)
40
+ return null;
41
+ return connectedProviderModels(result.data);
42
+ }
43
+ async function readStandaloneModels() {
44
+ const { readStandaloneOpencodeModels } = await import("./opencode-standalone-models.js");
45
+ return readStandaloneOpencodeModels();
46
+ }
47
+ /**
48
+ * The OpenCode models for the Settings page. Without a client or a registered
49
+ * OpenCode host, this starts a private OpenCode server through `readStandalone`.
50
+ * Pass `null` to skip that, for callers that need a live session.
51
+ */
52
+ export async function listOpencodeSettingsModels(client, readStandalone = readStandaloneModels) {
53
+ const unavailable = (reason) => ({ available: false, reason });
16
54
  try {
17
55
  let models;
18
56
  if (client) {
@@ -21,14 +59,20 @@ export async function listOpencodeSettingsModels(client) {
21
59
  else {
22
60
  const { getOpencodeHostModels } = await import("./backfill-controls.js");
23
61
  const opencode = getOpencodeHostModels();
62
+ if (!opencode && readStandalone) {
63
+ const result = await readStandalone();
64
+ if (result.outcome !== "listed")
65
+ return unavailable(STANDALONE_REASONS[result.outcome]);
66
+ return { available: true, models: result.models };
67
+ }
24
68
  models = opencode ? await opencode.listSettingsModels() : null;
25
69
  }
26
70
  if (!models)
27
- return { available: false, reason: "OpenCode model list unavailable" };
71
+ return unavailable(OPENCODE_LIST_UNAVAILABLE);
28
72
  return { available: true, models };
29
73
  }
30
74
  catch {
31
- return { available: false, reason: "OpenCode model list unavailable" };
75
+ return unavailable(OPENCODE_LIST_UNAVAILABLE);
32
76
  }
33
77
  }
34
78
  export async function listPiSettingsModels(loadSdk = () => import("@earendil-works/pi-coding-agent")) {
@@ -42,9 +86,9 @@ export async function listPiSettingsModels(loadSdk = () => import("@earendil-wor
42
86
  .map((model) => ({ provider: model.provider, model: model.id, name: model.name }));
43
87
  return models.length
44
88
  ? { available: true, models }
45
- : { available: false, models, reason: "No signed-in Pi models found" };
89
+ : { available: false, models, reason: PI_NO_MODELS };
46
90
  }
47
91
  catch {
48
- return { available: false, reason: "Pi model list unavailable; enter provider/model" };
92
+ return { available: false, reason: PI_LIST_UNAVAILABLE };
49
93
  }
50
94
  }
@@ -6,6 +6,7 @@ export { importReadiness } from "./import-readiness.js";
6
6
  export { browseImportSources, validateImportSource } from "./import-sources.js";
7
7
  export { listImportSessions, validateSessionListRequest } from "./import-sessions.js";
8
8
  export { opencodeSnapshots, sweepOrphanSnapshots } from "./opencode-snapshot.js";
9
+ export { stopStandaloneOpencodeReads } from "./opencode-standalone-models.js";
9
10
  export { BackfillControls } from "./backfill-controls.js";
10
11
  export { directoryMapsView } from "./map-suggestions.js";
11
12
  export { testExternalApi } from "./external-api-test.js";
@@ -6,6 +6,7 @@ export { importReadiness } from "./import-readiness.js";
6
6
  export { browseImportSources, validateImportSource } from "./import-sources.js";
7
7
  export { listImportSessions, validateSessionListRequest } from "./import-sessions.js";
8
8
  export { opencodeSnapshots, sweepOrphanSnapshots } from "./opencode-snapshot.js";
9
+ export { stopStandaloneOpencodeReads } from "./opencode-standalone-models.js";
9
10
  export { BackfillControls } from "./backfill-controls.js";
10
11
  export { directoryMapsView } from "./map-suggestions.js";
11
12
  export { testExternalApi } from "./external-api-test.js";
@@ -1,3 +1,5 @@
1
1
  export declare function isLoopbackHost(host: string): boolean;
2
+ /** A URL needs brackets around an IPv6 literal such as ::1. */
3
+ export declare function webServerUrl(host: string, port: number): string;
2
4
  export declare function assertWebServerNetworkAuth(host: string, apiToken?: string, basicAuthEnabled?: boolean): void;
3
5
  export declare function authorizeApiRequest(req: Request, apiToken?: string): Response | null;
@@ -3,6 +3,11 @@ const LOOPBACK_HOSTS = new Set(["127.0.0.1", "localhost", "::1", "[::1]", "0:0:0
3
3
  export function isLoopbackHost(host) {
4
4
  return LOOPBACK_HOSTS.has(host.trim().toLowerCase());
5
5
  }
6
+ /** A URL needs brackets around an IPv6 literal such as ::1. */
7
+ export function webServerUrl(host, port) {
8
+ const urlHost = host.includes(":") && !host.startsWith("[") ? `[${host}]` : host;
9
+ return `http://${urlHost}:${port}`;
10
+ }
6
11
  export function assertWebServerNetworkAuth(host, apiToken, basicAuthEnabled = false) {
7
12
  if (!isLoopbackHost(host) && !apiToken && !basicAuthEnabled) {
8
13
  throw new Error(`webServerHost "${host}" exposes the API on the network. Set webServerApiToken in omms.jsonc, or bind to 127.0.0.1.`);
@@ -1,6 +1,6 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { execFileSync } from "node:child_process";
3
- import { accessSync, existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync, constants, } from "node:fs";
3
+ import { accessSync, existsSync, mkdirSync, readFileSync, realpathSync, renameSync, unlinkSync, writeFileSync, constants, } from "node:fs";
4
4
  import { homedir } from "node:os";
5
5
  import { basename, dirname, join, delimiter, resolve } from "node:path";
6
6
  import { fileURLToPath } from "node:url";
@@ -23,13 +23,34 @@ function executable(name) {
23
23
  }
24
24
  return null;
25
25
  }
26
+ function sameFile(a, b) {
27
+ try {
28
+ return realpathSync(a) === realpathSync(b);
29
+ }
30
+ catch {
31
+ return false;
32
+ }
33
+ }
34
+ /** A Homebrew upgrade deletes the versioned Cellar folder, so use a link that survives it. */
35
+ function stableRuntime(path) {
36
+ const match = /^(.*)[\\/]Cellar[\\/]([^\\/]+)[\\/][^\\/]+[\\/]bin[\\/]([^\\/]+)$/.exec(path);
37
+ const [, prefix, formula, name] = match ?? [];
38
+ if (!prefix || !formula || !name)
39
+ return path;
40
+ const linked = join(prefix, "bin", name);
41
+ if (sameFile(linked, path))
42
+ return linked;
43
+ const opt = join(prefix, "opt", formula, "bin", name);
44
+ return sameFile(opt, path) ? opt : path;
45
+ }
26
46
  /** OpenCode's executable is not a JavaScript runtime. */
27
47
  export function resolveWebRuntime(execPath = process.execPath, platform = process.platform) {
28
48
  const name = basename(execPath).toLowerCase();
29
49
  if (["node", "bun", "node.exe", "bun.exe"].includes(name))
30
- return execPath;
50
+ return stableRuntime(execPath);
31
51
  const suffix = platform === "win32" ? ".exe" : "";
32
- return executable(`node${suffix}`) ?? executable(`bun${suffix}`);
52
+ const found = executable(`node${suffix}`) ?? executable(`bun${suffix}`);
53
+ return found && stableRuntime(found);
33
54
  }
34
55
  function packageRoot() {
35
56
  let dir = dirname(fileURLToPath(import.meta.url));
@@ -5,7 +5,7 @@ import { join, dirname, extname, normalize, sep } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import { log } from "./logger.js";
7
7
  import { corsPreflightResponse, disallowedCorsResponse, isAllowedBrowserOrigin } from "./cors.js";
8
- import { assertWebServerNetworkAuth, authorizeApiRequest, isLoopbackHost } from "./web-api-auth.js";
8
+ import { assertWebServerNetworkAuth, authorizeApiRequest, isLoopbackHost, webServerUrl, } from "./web-api-auth.js";
9
9
  import { getOrCreateAuthToken, isAuthorizedApiRequest } from "./auth-token.js";
10
10
  import { WebAuth } from "./web-auth.js";
11
11
  import { NODE_HTTP_IDLE_TIMEOUT_MS } from "./request-timeouts.js";
@@ -312,8 +312,9 @@ export class WebServer {
312
312
  this.server.stop();
313
313
  this.server = null;
314
314
  this.isOwner = false;
315
- const { opencodeSnapshots } = await import("../importer/web-import-api.js");
315
+ const { opencodeSnapshots, stopStandaloneOpencodeReads } = await import("../importer/web-import-api.js");
316
316
  await opencodeSnapshots.closeAll();
317
+ await stopStandaloneOpencodeReads();
317
318
  }
318
319
  isRunning() {
319
320
  return this.server !== null;
@@ -322,7 +323,7 @@ export class WebServer {
322
323
  return this.isOwner;
323
324
  }
324
325
  getUrl() {
325
- return `http://${this.config.host}:${this.config.port}`;
326
+ return webServerUrl(this.config.host, this.config.port);
326
327
  }
327
328
  async checkServerAvailable() {
328
329
  try {
@@ -119,6 +119,6 @@ ${e}</tr>
119
119
  `}tablecell(e){let t=this.parser.parseInline(e.tokens),n=e.header?`th`:`td`;return(e.align?`<${n} align="${e.align}">`:`<${n}>`)+t+`</${n}>
120
120
  `}strong({tokens:e}){return`<strong>${this.parser.parseInline(e)}</strong>`}em({tokens:e}){return`<em>${this.parser.parseInline(e)}</em>`}codespan({text:e}){return`<code>${Sf(e,!0)}</code>`}br(e){return`<br>`}del({tokens:e}){return`<del>${this.parser.parseInline(e)}</del>`}link({href:e,title:t,tokens:n}){let r=this.parser.parseInline(n),i=Cf(e);if(i===null)return r;e=i;let a=`<a href="`+e+`"`;return t&&(a+=` title="`+Sf(t)+`"`),a+=`>`+r+`</a>`,a}image({href:e,title:t,text:n,tokens:r}){r&&(n=this.parser.parseInline(r,this.parser.textRenderer));let i=Cf(e);if(i===null)return Sf(n);e=i;let a=`<img src="${e}" alt="${Sf(n)}"`;return t&&(a+=` title="${Sf(t)}"`),a+=`>`,a}text(e){return`tokens`in e&&e.tokens?this.parser.parseInline(e.tokens):`escaped`in e&&e.escaped?e.text:Sf(e.text)}},Pf=class{strong({text:e}){return e}em({text:e}){return e}codespan({text:e}){return e}del({text:e}){return e}html({text:e}){return e}text({text:e}){return e}link({text:e}){return``+e}image({text:e}){return``+e}br(){return``}checkbox({raw:e}){return e}},Ff=class e{options;renderer;textRenderer;constructor(e){this.options=e||rd,this.options.renderer=this.options.renderer||new Nf,this.renderer=this.options.renderer,this.renderer.options=this.options,this.renderer.parser=this,this.textRenderer=new Pf}static parse(t,n){return new e(n).parse(t)}static parseInline(t,n){return new e(n).parseInline(t)}parse(e){this.renderer.parser=this;let t=``;for(let n=0;n<e.length;n++){let r=e[n];if(this.options.extensions?.renderers?.[r.type]){let e=r,n=this.options.extensions.renderers[e.type].call({parser:this},e);if(n!==!1||![`space`,`hr`,`heading`,`code`,`table`,`blockquote`,`list`,`checkbox`,`html`,`def`,`paragraph`,`text`].includes(e.type)){t+=n||``;continue}}let i=r;switch(i.type){case`space`:t+=this.renderer.space(i);break;case`hr`:t+=this.renderer.hr(i);break;case`heading`:t+=this.renderer.heading(i);break;case`code`:t+=this.renderer.code(i);break;case`table`:t+=this.renderer.table(i);break;case`blockquote`:t+=this.renderer.blockquote(i);break;case`list`:t+=this.renderer.list(i);break;case`checkbox`:t+=this.renderer.checkbox(i);break;case`html`:t+=this.renderer.html(i);break;case`def`:t+=this.renderer.def(i);break;case`paragraph`:t+=this.renderer.paragraph(i);break;case`text`:t+=this.renderer.text(i);break;default:{let e=`Token with "`+i.type+`" type was not found.`;if(this.options.silent)return console.error(e),``;throw Error(e)}}}return t}parseInline(e,t=this.renderer){this.renderer.parser=this;let n=``;for(let r=0;r<e.length;r++){let i=e[r];if(this.options.extensions?.renderers?.[i.type]){let e=this.options.extensions.renderers[i.type].call({parser:this},i);if(e!==!1||![`escape`,`html`,`link`,`image`,`checkbox`,`strong`,`em`,`codespan`,`br`,`del`,`text`].includes(i.type)){n+=e||``;continue}}let a=i;switch(a.type){case`escape`:n+=t.text(a);break;case`html`:n+=t.html(a);break;case`link`:n+=t.link(a);break;case`image`:n+=t.image(a);break;case`checkbox`:n+=t.checkbox(a);break;case`strong`:n+=t.strong(a);break;case`em`:n+=t.em(a);break;case`codespan`:n+=t.codespan(a);break;case`br`:n+=t.br(a);break;case`del`:n+=t.del(a);break;case`text`:n+=t.text(a);break;default:{let e=`Token with "`+a.type+`" type was not found.`;if(this.options.silent)return console.error(e),``;throw Error(e)}}}return n}},If=class{options;block;constructor(e){this.options=e||rd}static passThroughHooks=new Set([`preprocess`,`postprocess`,`processAllTokens`,`emStrongMask`]);static passThroughHooksRespectAsync=new Set([`preprocess`,`postprocess`,`processAllTokens`]);preprocess(e){return e}postprocess(e){return e}processAllTokens(e){return e}emStrongMask(e){return e}provideLexer(e=this.block){return e?Mf.lex:Mf.lexInline}provideParser(e=this.block){return e?Ff.parse:Ff.parseInline}},Lf=new class{defaults=nd();options=this.setOptions;parse=this.parseMarkdown(!0);parseInline=this.parseMarkdown(!1);Parser=Ff;Renderer=Nf;TextRenderer=Pf;Lexer=Mf;Tokenizer=jf;Hooks=If;constructor(...e){this.use(...e)}walkTokens(e,t){let n=[];for(let r of e)switch(n=n.concat(t.call(this,r)),r.type){case`table`:{let e=r;for(let r of e.header)n=n.concat(this.walkTokens(r.tokens,t));for(let r of e.rows)for(let e of r)n=n.concat(this.walkTokens(e.tokens,t));break}case`list`:{let e=r;n=n.concat(this.walkTokens(e.items,t));break}default:{let e=r;this.defaults.extensions?.childTokens?.[e.type]?this.defaults.extensions.childTokens[e.type].forEach(r=>{let i=e[r].flat(1/0);n=n.concat(this.walkTokens(i,t))}):e.tokens&&(n=n.concat(this.walkTokens(e.tokens,t)))}}return n}use(...e){let t=this.defaults.extensions||{renderers:{},childTokens:{}};return e.forEach(e=>{let n={...e};if(n.async=this.defaults.async||n.async||!1,e.extensions&&(e.extensions.forEach(e=>{if(!e.name)throw Error(`extension name required`);if(`renderer`in e){let n=t.renderers[e.name];n?t.renderers[e.name]=function(...t){let r=e.renderer.apply(this,t);return r===!1&&(r=n.apply(this,t)),r}:t.renderers[e.name]=e.renderer}if(`tokenizer`in e){if(!e.level||e.level!==`block`&&e.level!==`inline`)throw Error(`extension level must be 'block' or 'inline'`);let n=t[e.level];n?n.unshift(e.tokenizer):t[e.level]=[e.tokenizer],e.start&&(e.level===`block`?t.startBlock?t.startBlock.push(e.start):t.startBlock=[e.start]:e.level===`inline`&&(t.startInline?t.startInline.push(e.start):t.startInline=[e.start]))}`childTokens`in e&&e.childTokens&&(t.childTokens[e.name]=e.childTokens)}),n.extensions=t),e.renderer){let t=this.defaults.renderer||new Nf(this.defaults);for(let n in e.renderer){if(!(n in t))throw Error(`renderer '${n}' does not exist`);if([`options`,`parser`].includes(n))continue;let r=n,i=e.renderer[r],a=t[r];t[r]=(...e)=>{let n=i.apply(t,e);return n===!1&&(n=a.apply(t,e)),n||``}}n.renderer=t}if(e.tokenizer){let t=this.defaults.tokenizer||new jf(this.defaults);for(let n in e.tokenizer){if(!(n in t))throw Error(`tokenizer '${n}' does not exist`);if([`options`,`rules`,`lexer`].includes(n))continue;let r=n,i=e.tokenizer[r],a=t[r];t[r]=(...e)=>{let n=i.apply(t,e);return n===!1&&(n=a.apply(t,e)),n}}n.tokenizer=t}if(e.hooks){let t=this.defaults.hooks||new If;for(let n in e.hooks){if(!(n in t))throw Error(`hook '${n}' does not exist`);if([`options`,`block`].includes(n))continue;let r=n,i=e.hooks[r],a=t[r];t[r]=If.passThroughHooks.has(n)?e=>{if(this.defaults.async&&If.passThroughHooksRespectAsync.has(n))return(async()=>{let n=await i.call(t,e);return a.call(t,n)})();let r=i.call(t,e);return a.call(t,r)}:(...e)=>{if(this.defaults.async)return(async()=>{let n=await i.apply(t,e);return n===!1&&(n=await a.apply(t,e)),n})();let n=i.apply(t,e);return n===!1&&(n=a.apply(t,e)),n}}n.hooks=t}if(e.walkTokens){let t=this.defaults.walkTokens,r=e.walkTokens;n.walkTokens=function(e){let n=[];return n.push(r.call(this,e)),t&&(n=n.concat(t.call(this,e))),n}}this.defaults={...this.defaults,...n}}),this}setOptions(e){return this.defaults={...this.defaults,...e},this}lexer(e,t){return Mf.lex(e,t??this.defaults)}parser(e,t){return Ff.parse(e,t??this.defaults)}parseMarkdown(e){return(t,n)=>{let r={...n},i={...this.defaults,...r},a=this.onError(!!i.silent,!!i.async);if(this.defaults.async===!0&&r.async===!1)return a(Error(`marked(): The async option was set to true by an extension. Remove async: false from the parse options object to return a Promise.`));if(typeof t>`u`||t===null)return a(Error(`marked(): input parameter is undefined or null`));if(typeof t!=`string`)return a(Error(`marked(): input parameter is of type `+Object.prototype.toString.call(t)+`, string expected`));if(i.hooks&&(i.hooks.options=i,i.hooks.block=e),i.async)return(async()=>{let n=i.hooks?await i.hooks.preprocess(t):t,r=await(i.hooks?await i.hooks.provideLexer(e):e?Mf.lex:Mf.lexInline)(n,i),a=i.hooks?await i.hooks.processAllTokens(r):r;i.walkTokens&&await Promise.all(this.walkTokens(a,i.walkTokens));let o=await(i.hooks?await i.hooks.provideParser(e):e?Ff.parse:Ff.parseInline)(a,i);return i.hooks?await i.hooks.postprocess(o):o})().catch(a);try{i.hooks&&(t=i.hooks.preprocess(t));let n=(i.hooks?i.hooks.provideLexer(e):e?Mf.lex:Mf.lexInline)(t,i);i.hooks&&(n=i.hooks.processAllTokens(n)),i.walkTokens&&this.walkTokens(n,i.walkTokens);let r=(i.hooks?i.hooks.provideParser(e):e?Ff.parse:Ff.parseInline)(n,i);return i.hooks&&(r=i.hooks.postprocess(r)),r}catch(e){return a(e)}}}onError(e,t){return n=>{if(n.message+=`
121
121
  Please report this to https://github.com/markedjs/marked.`,e){let e=`<p>An error occurred:</p><pre>`+Sf(n.message+``,!0)+`</pre>`;return t?Promise.resolve(e):e}if(t)return Promise.reject(n);throw n}}};function Rf(e,t){return Lf.parse(e,t)}Rf.options=Rf.setOptions=function(e){return Lf.setOptions(e),Rf.defaults=Lf.defaults,id(Rf.defaults),Rf},Rf.getDefaults=nd,Rf.defaults=rd;function zf(...e){return Lf.use(...e),Rf.defaults=Lf.defaults,id(Rf.defaults),Rf}Rf.use=zf,Rf.walkTokens=function(e,t){return Lf.walkTokens(e,t)},Rf.parseInline=Lf.parseInline,Rf.Parser=Ff,Rf.parser=Ff.parse,Rf.Renderer=Nf,Rf.TextRenderer=Pf,Rf.Lexer=Mf,Rf.lexer=Mf.lex,Rf.Tokenizer=jf,Rf.Hooks=If,Rf.parse=Rf,Rf.options,Rf.setOptions,Rf.walkTokens,Rf.parseInline,Ff.parse,Mf.lex,Rf.setOptions({gfm:!0,breaks:!0});function Bf(e){let t=Rf.parse(e,{async:!1});return td.sanitize(t)}function Vf(e){if(e.projectPath){let t=e.projectPath.replace(/\\/g,`/`).split(`/`).filter(e=>e);return t[t.length-1]||e.projectPath}return e.displayName||e.id}function Hf(e){return{createdDate:jl(e.createdAt),updatedDate:e.updatedAt&&e.updatedAt!==e.createdAt?jl(e.updatedAt):null}}function Uf(e,t){return e.similarity===void 0?null:t?`${Math.round(e.similarity*100)}%`:`${e.similarity}%`}function Wf({variant:e,item:t,memory:n,prompt:r,selected:i=!1,onSelect:a,onPin:o,onUnpin:s,onEdit:c,onDeleteMemory:l,onDeletePrompt:u,activeKeyword:d,onKeywordClick:f}){let{t:p}=cr();if(e===`pair`&&n&&r){let e=n.isPinned||!1,t=Hf(n),u=Uf(n,!0);return(0,V.jsxs)(`div`,{className:`rounded-xl border border-border bg-card/60 p-3 space-y-3 ${i?`ring-1 ring-primary/50`:``} ${e?`border-primary/40`:``}`,"data-id":n.id,children:[(0,V.jsxs)(`div`,{className:`space-y-2 rounded-lg bg-muted/40 p-3`,children:[(0,V.jsxs)(`div`,{className:`flex items-center justify-between gap-2`,children:[(0,V.jsx)(Al,{variant:`secondary`,children:p(`badge-prompt`)}),(0,V.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:jl(r.createdAt)})]}),(0,V.jsx)(`p`,{className:`text-sm whitespace-pre-wrap break-words`,children:r.content})]}),(0,V.jsx)(`div`,{className:`flex justify-center text-muted-foreground`,children:(0,V.jsx)(te,{className:`size-4`})}),(0,V.jsxs)(`div`,{className:`space-y-2`,children:[(0,V.jsxs)(`div`,{className:`flex flex-wrap items-start justify-between gap-2`,children:[(0,V.jsxs)(`div`,{className:`flex flex-wrap items-center gap-1.5 min-w-0`,children:[(0,V.jsx)(Hi,{checked:i,onCheckedChange:e=>a?.(n.id,e===!0)}),(0,V.jsx)(Al,{children:p(`badge-memory`)}),n.memoryType?(0,V.jsx)(Al,{variant:`outline`,children:n.memoryType}):null,u?(0,V.jsx)(`span`,{className:`text-xs text-primary`,children:u}):null,e?(0,V.jsx)(Al,{variant:`secondary`,children:p(`badge-pinned`)}):null,(0,V.jsx)(`span`,{className:`text-xs text-muted-foreground truncate`,children:n.displayName||n.id})]}),(0,V.jsxs)(`div`,{className:`flex items-center gap-1 shrink-0`,children:[e?(0,V.jsx)(W,{variant:`ghost`,size:`icon-xs`,title:`Unpin`,onClick:()=>s?.(n.id),children:(0,V.jsx)(ve,{className:`size-3.5 fill-current`})}):(0,V.jsx)(W,{variant:`ghost`,size:`icon-xs`,title:`Pin`,onClick:()=>o?.(n.id),children:(0,V.jsx)(ve,{className:`size-3.5`})}),(0,V.jsx)(W,{variant:`ghost`,size:`icon-xs`,onClick:()=>c?.(n.id),children:(0,V.jsx)(ge,{className:`size-3.5`})}),(0,V.jsx)(Ol,{content:p(`tooltip-delete-pair`),children:(0,V.jsx)(W,{variant:`destructive`,size:`icon-xs`,"aria-label":p(`btn-delete-pair`),onClick:()=>l?.(n.id,!0),children:(0,V.jsx)(De,{className:`size-3.5`})})})]})]}),n.tags?.length?(0,V.jsx)(`div`,{className:`flex flex-wrap gap-1`,children:n.tags.map(e=>(0,V.jsx)(El,{keyword:e,active:d?.toLowerCase()===e.toLowerCase(),title:p(`tooltip-filter-keyword`),onClick:f},e))}):null,(0,V.jsx)(`div`,{className:`markdown-content text-sm prose-invert max-w-none`,dangerouslySetInnerHTML:{__html:Bf(n.content)}}),(0,V.jsxs)(`div`,{className:`flex flex-wrap gap-3 text-xs text-muted-foreground`,children:[(0,V.jsxs)(`span`,{children:[p(`date-created`),` `,t.createdDate]}),t.updatedDate?(0,V.jsxs)(`span`,{children:[p(`date-updated`),` `,t.updatedDate]}):null,(0,V.jsxs)(`span`,{children:[`ID: `,n.id]})]})]})]})}if(e===`prompt`&&t){let e=!!t.linkedMemoryId;return(0,V.jsxs)(`div`,{className:`rounded-xl border border-border bg-card/60 p-3 space-y-2 ${i?`ring-1 ring-primary/50`:``}`,"data-id":t.id,children:[(0,V.jsxs)(`div`,{className:`flex flex-wrap items-start justify-between gap-2`,children:[(0,V.jsxs)(`div`,{className:`flex flex-wrap items-center gap-1.5`,children:[(0,V.jsx)(Hi,{checked:i,onCheckedChange:e=>a?.(t.id,e===!0)}),(0,V.jsx)(fe,{className:`size-3.5 text-muted-foreground`}),(0,V.jsx)(Al,{variant:`secondary`,children:p(`badge-prompt`)}),e?(0,V.jsxs)(Al,{variant:`outline`,children:[(0,V.jsx)(P,{className:`size-3`}),p(`badge-linked`)]}):null,(0,V.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:jl(t.createdAt)})]}),e?(0,V.jsx)(Ol,{content:p(`tooltip-delete-prompt-pair`),children:(0,V.jsx)(W,{variant:`destructive`,size:`icon-xs`,"aria-label":p(e?`btn-delete-pair`:`btn-delete`),onClick:()=>u?.(t.id,e),children:(0,V.jsx)(De,{className:`size-3.5`})})}):(0,V.jsx)(W,{variant:`destructive`,size:`icon-xs`,"aria-label":p(e?`btn-delete-pair`:`btn-delete`),title:p(`btn-delete`),onClick:()=>u?.(t.id,e),children:(0,V.jsx)(De,{className:`size-3.5`})})]}),(0,V.jsx)(`p`,{className:`text-sm whitespace-pre-wrap break-words`,children:t.content}),e?(0,V.jsxs)(`div`,{className:`flex items-center gap-1 text-xs text-muted-foreground`,children:[(0,V.jsx)(te,{className:`size-3`}),p(`text-generated-above`),(0,V.jsx)(re,{className:`size-3`})]}):null]})}if(e===`memory`&&t){let e=t.isPinned||!1,n=!!t.linkedPromptId,r=Hf(t),u=Uf(t,!1);return(0,V.jsxs)(`div`,{className:`rounded-xl border border-border bg-card/60 p-3 space-y-2 ${i?`ring-1 ring-primary/50`:``} ${e?`border-primary/40`:``}`,"data-id":t.id,children:[(0,V.jsxs)(`div`,{className:`flex flex-wrap items-start justify-between gap-2`,children:[(0,V.jsxs)(`div`,{className:`flex flex-wrap items-center gap-1.5 min-w-0`,children:[(0,V.jsx)(Hi,{checked:i,onCheckedChange:e=>a?.(t.id,e===!0)}),t.memoryType?(0,V.jsx)(Al,{variant:`outline`,children:t.memoryType}):null,n?(0,V.jsxs)(Al,{variant:`outline`,children:[(0,V.jsx)(P,{className:`size-3`}),p(`badge-linked`)]}):null,u?(0,V.jsx)(`span`,{className:`text-xs text-primary`,children:u}):null,e?(0,V.jsx)(Al,{variant:`secondary`,children:p(`badge-pinned`)}):null,(0,V.jsx)(`span`,{className:`text-xs text-muted-foreground truncate`,children:Vf(t)}),t.projectPath?(0,V.jsx)(`span`,{className:`text-xs text-muted-foreground/70 truncate w-full`,children:t.projectPath}):null]}),(0,V.jsxs)(`div`,{className:`flex items-center gap-1 shrink-0`,children:[e?(0,V.jsx)(W,{variant:`ghost`,size:`icon-xs`,title:`Unpin`,onClick:()=>s?.(t.id),children:(0,V.jsx)(ve,{className:`size-3.5 fill-current`})}):(0,V.jsx)(W,{variant:`ghost`,size:`icon-xs`,title:`Pin`,onClick:()=>o?.(t.id),children:(0,V.jsx)(ve,{className:`size-3.5`})}),(0,V.jsx)(W,{variant:`ghost`,size:`icon-xs`,onClick:()=>c?.(t.id),children:(0,V.jsx)(ge,{className:`size-3.5`})}),n?(0,V.jsx)(Ol,{content:p(`tooltip-delete-pair`),children:(0,V.jsx)(W,{variant:`destructive`,size:`icon-xs`,"aria-label":p(n?`btn-delete-pair`:`btn-delete`),onClick:()=>l?.(t.id,n),children:(0,V.jsx)(De,{className:`size-3.5`})})}):(0,V.jsx)(W,{variant:`destructive`,size:`icon-xs`,"aria-label":p(n?`btn-delete-pair`:`btn-delete`),title:p(`btn-delete`),onClick:()=>l?.(t.id,n),children:(0,V.jsx)(De,{className:`size-3.5`})})]})]}),t.tags?.length?(0,V.jsx)(`div`,{className:`flex flex-wrap gap-1`,children:t.tags.map(e=>(0,V.jsx)(El,{keyword:e,active:d?.toLowerCase()===e.toLowerCase(),title:p(`tooltip-filter-keyword`),onClick:f},e))}):null,(0,V.jsx)(`div`,{className:`markdown-content text-sm prose-invert max-w-none`,dangerouslySetInnerHTML:{__html:Bf(t.content)}}),n?(0,V.jsxs)(`div`,{className:`flex items-center gap-1 text-xs text-muted-foreground`,children:[(0,V.jsx)(re,{className:`size-3`}),p(`text-from-below`),(0,V.jsx)(te,{className:`size-3`})]}):null,(0,V.jsxs)(`div`,{className:`flex flex-wrap gap-3 text-xs text-muted-foreground`,children:[(0,V.jsxs)(`span`,{children:[p(`date-created`),` `,r.createdDate]}),r.updatedDate?(0,V.jsxs)(`span`,{children:[p(`date-updated`),` `,r.updatedDate]}):null,(0,V.jsxs)(`span`,{children:[`ID: `,t.id]})]})]})}return null}function Gf(e){let t=new Map(e.map(e=>[e.id,e])),n=[],r=new Set;for(let i of e)if(!r.has(i.id)){if(i.type===`memory`&&i.linkedPromptId&&t.has(i.linkedPromptId)){let e=t.get(i.linkedPromptId);n.push({isPair:!0,memory:i,prompt:e}),r.add(i.id),r.add(e.id)}else if(i.type===`prompt`&&i.linkedMemoryId&&t.has(i.linkedMemoryId)){let e=t.get(i.linkedMemoryId);n.push({isPair:!0,memory:e,prompt:i}),r.add(i.id),r.add(e.id)}else n.push({isPair:!1,type:i.type,item:i}),r.add(i.id)}return n.sort((e,t)=>{let n=e.isPair?e.memory.createdAt:e.item.createdAt,r=t.isPair?t.memory.createdAt:t.item.createdAt;return new Date(r).getTime()-new Date(n).getTime()})}function Kf({memories:e,selectedIds:t,currentPage:n,totalPages:r,loading:i=!1,error:a=null,onSelect:o,onPageChange:s,onPin:c,onUnpin:l,onEdit:u,onDeleteMemory:d,onDeletePrompt:f,activeKeyword:p,onKeywordClick:m}){let{t:h}=cr(),g=(0,_.useMemo)(()=>Gf(e),[e]),v=h(`text-page`,{current:n,total:r}),y=(0,V.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,V.jsx)(W,{variant:`outline`,size:`icon-xs`,disabled:!(n>1),onClick:()=>s(-1),children:(0,V.jsx)(ae,{className:`size-3.5`})}),(0,V.jsx)(`span`,{className:`text-xs text-muted-foreground tabular-nums`,children:v}),(0,V.jsx)(W,{variant:`outline`,size:`icon-xs`,disabled:!(n<r),onClick:()=>s(1),children:(0,V.jsx)(A,{className:`size-3.5`})})]});return(0,V.jsxs)(`div`,{className:`space-y-3`,children:[(0,V.jsx)(`div`,{className:`grid min-h-32 items-start gap-3 lg:grid-cols-2`,children:i&&e.length===0?(0,V.jsx)(`div`,{className:`text-sm text-muted-foreground py-8 text-center`,children:h(`loading-init`)}):a?(0,V.jsxs)(`div`,{className:`text-sm text-destructive py-8 text-center`,children:[`Error: `,a]}):g.length===0?(0,V.jsx)(`div`,{className:`text-sm text-muted-foreground py-8 text-center`,children:h(`empty-memories`)}):g.map(e=>e.isPair?(0,V.jsx)(Wf,{variant:`pair`,memory:e.memory,prompt:e.prompt,selected:t.has(e.memory.id),onSelect:o,onPin:c,onUnpin:l,onEdit:u,onDeleteMemory:d,onDeletePrompt:f,activeKeyword:p,onKeywordClick:m},e.memory.id):e.type===`prompt`?(0,V.jsx)(Wf,{variant:`prompt`,item:e.item,selected:t.has(e.item.id),onSelect:o,onPin:c,onUnpin:l,onEdit:u,onDeleteMemory:d,onDeletePrompt:f,activeKeyword:p,onKeywordClick:m},e.item.id):(0,V.jsx)(Wf,{variant:`memory`,item:e.item,selected:t.has(e.item.id),onSelect:o,onPin:c,onUnpin:l,onEdit:u,onDeleteMemory:d,onDeletePrompt:f,activeKeyword:p,onKeywordClick:m},e.item.id))}),(0,V.jsx)(`div`,{className:`flex justify-end`,children:y})]})}var qf=Object.defineProperty,Jf=(e,t)=>qf(e,`name`,{value:t,configurable:!0}),Yf=_.createContext(void 0);function Xf(e){let t=_.useContext(Yf);return e||t||`ltr`}Jf(Xf,`useDirection`);var Zf=Object.defineProperty,Qf=(e,t)=>Zf(e,`name`,{value:t,configurable:!0});function $f(e,[t,n]){return Math.min(n,Math.max(t,e))}Qf($f,`clamp`);var ep=Object.defineProperty,Q=(e,t)=>ep(e,`name`,{value:t,configurable:!0});function tp(e,t){return _.useReducer((e,n)=>t[e][n]??e,e)}Q(tp,`useStateMachine`);var np=`ScrollArea`,[rp,ip]=zr(np),[ap,op]=rp(np),sp=_.forwardRef(Q(function(e,t){let{__scopeScrollArea:n,type:r=`hover`,dir:i,scrollHideDelay:a=600,...o}=e,[s,c]=_.useState(null),[l,u]=_.useState(null),[d,f]=_.useState(null),[p,m]=_.useState(null),[h,g]=_.useState(null),[v,y]=_.useState(0),[b,x]=_.useState(0),[S,C]=_.useState(!1),[w,T]=_.useState(!1),E=hr(t,c),D=Xf(i);return(0,V.jsx)(ap,{scope:n,type:r,dir:D,scrollHideDelay:a,scrollArea:s,viewport:l,onViewportChange:u,content:d,onContentChange:f,scrollbarX:p,onScrollbarXChange:m,scrollbarXEnabled:S,onScrollbarXEnabledChange:C,scrollbarY:h,onScrollbarYChange:g,scrollbarYEnabled:w,onScrollbarYEnabledChange:T,onCornerWidthChange:y,onCornerHeightChange:x,children:(0,V.jsx)(Ci.div,{dir:D,...o,ref:E,style:{position:`relative`,"--radix-scroll-area-corner-width":v+`px`,"--radix-scroll-area-corner-height":b+`px`,...e.style}})})},`ScrollArea`)),cp=`ScrollAreaViewport`,lp=_.forwardRef(Q(function(e,t){let{__scopeScrollArea:n,children:r,nonce:i,...a}=e,o=op(cp,n),s=hr(t,_.useRef(null),o.onViewportChange);return(0,V.jsxs)(V.Fragment,{children:[(0,V.jsx)(up,{nonce:i}),(0,V.jsx)(Ci.div,{"data-radix-scroll-area-viewport":``,...a,ref:s,style:{overflowX:o.scrollbarXEnabled?`scroll`:`hidden`,overflowY:o.scrollbarYEnabled?`scroll`:`hidden`,...e.style},children:(0,V.jsx)(`div`,{ref:o.onContentChange,style:{minWidth:`100%`,display:`table`},children:r})})]})},`ScrollAreaViewport`)),up=_.memo(Q(function({nonce:e}){return(0,V.jsx)(`style`,{dangerouslySetInnerHTML:{__html:`[data-radix-scroll-area-viewport]{scrollbar-width:none;-ms-overflow-style:none;-webkit-overflow-scrolling:touch;}[data-radix-scroll-area-viewport]::-webkit-scrollbar{display:none}`},nonce:e})},`ScrollAreaViewportStyle`),(e,t)=>e.nonce===t.nonce),dp=`ScrollAreaScrollbar`,fp=_.forwardRef(Q(function(e,t){let{forceMount:n,...r}=e,i=op(dp,e.__scopeScrollArea),{onScrollbarXEnabledChange:a,onScrollbarYEnabledChange:o}=i,s=e.orientation===`horizontal`;return _.useEffect(()=>(s?a(!0):o(!0),()=>{s?a(!1):o(!1)}),[s,a,o]),i.type===`hover`?(0,V.jsx)(pp,{...r,ref:t,forceMount:n}):i.type===`scroll`?(0,V.jsx)(mp,{...r,ref:t,forceMount:n}):i.type===`auto`?(0,V.jsx)(hp,{...r,ref:t,forceMount:n}):i.type===`always`?(0,V.jsx)(gp,{...r,ref:t,"data-state":`visible`}):null},`ScrollAreaScrollbar`)),pp=_.forwardRef(Q(function(e,t){let{forceMount:n,...r}=e,i=op(dp,e.__scopeScrollArea),[a,o]=_.useState(!1);return _.useEffect(()=>{let e=i.scrollArea,t=0;if(e){let n=Q(()=>{window.clearTimeout(t),o(!0)},`handlePointerEnter`),r=Q(()=>{t=window.setTimeout(()=>o(!1),i.scrollHideDelay)},`handlePointerLeave`);return e.addEventListener(`pointerenter`,n),e.addEventListener(`pointerleave`,r),()=>{window.clearTimeout(t),e.removeEventListener(`pointerenter`,n),e.removeEventListener(`pointerleave`,r)}}},[i.scrollArea,i.scrollHideDelay]),(0,V.jsx)(hi,{present:n||a,children:(0,V.jsx)(hp,{"data-state":a?`visible`:`hidden`,...r,ref:t})})},`ScrollAreaScrollbarHover`)),mp=_.forwardRef(Q(function(e,t){let{forceMount:n,...r}=e,i=op(dp,e.__scopeScrollArea),a=e.orientation===`horizontal`,o=Ip(()=>c(`SCROLL_END`),100),[s,c]=tp(`hidden`,{hidden:{SCROLL:`scrolling`},scrolling:{SCROLL_END:`idle`,POINTER_ENTER:`interacting`},interacting:{SCROLL:`interacting`,POINTER_LEAVE:`idle`},idle:{HIDE:`hidden`,SCROLL:`scrolling`,POINTER_ENTER:`interacting`}});return _.useEffect(()=>{if(s===`idle`){let e=window.setTimeout(()=>c(`HIDE`),i.scrollHideDelay);return()=>window.clearTimeout(e)}},[s,i.scrollHideDelay,c]),_.useEffect(()=>{let e=i.viewport,t=a?`scrollLeft`:`scrollTop`;if(e){let n=e[t],r=Q(()=>{let r=e[t];n!==r&&(c(`SCROLL`),o()),n=r},`handleScroll`);return e.addEventListener(`scroll`,r),()=>e.removeEventListener(`scroll`,r)}},[i.viewport,a,c,o]),(0,V.jsx)(hi,{present:n||s!==`hidden`,children:(0,V.jsx)(gp,{"data-state":s===`hidden`?`hidden`:`visible`,...r,ref:t,onPointerEnter:Wr(e.onPointerEnter,()=>c(`POINTER_ENTER`)),onPointerLeave:Wr(e.onPointerLeave,()=>c(`POINTER_LEAVE`))})})},`ScrollAreaScrollbarScroll`)),hp=_.forwardRef(Q(function(e,t){let n=op(dp,e.__scopeScrollArea),{forceMount:r,...i}=e,[a,o]=_.useState(!1),s=e.orientation===`horizontal`,c=Ip(()=>{if(n.viewport){let e=n.viewport.offsetWidth<n.viewport.scrollWidth,t=n.viewport.offsetHeight<n.viewport.scrollHeight;o(s?e:t)}},10);return Lp(n.viewport,c),Lp(n.content,c),(0,V.jsx)(hi,{present:r||a,children:(0,V.jsx)(gp,{"data-state":a?`visible`:`hidden`,...i,ref:t})})},`ScrollAreaScrollbarAuto`)),gp=_.forwardRef(Q(function(e,t){let{orientation:n=`vertical`,...r}=e,i=op(dp,e.__scopeScrollArea),a=_.useRef(null),o=_.useRef(0),[s,c]=_.useState({content:0,viewport:0,scrollbar:{size:0,paddingStart:0,paddingEnd:0}}),l=kp(s.viewport,s.content),u={...r,sizes:s,onSizesChange:c,hasThumb:l>0&&l<1,onThumbChange:Q(e=>a.current=e,`onThumbChange`),onThumbPointerUp:Q(()=>o.current=0,`onThumbPointerUp`),onThumbPointerDown:Q(e=>o.current=e,`onThumbPointerDown`)};function d(e,t){return jp(e,o.current,s,t)}return Q(d,`getScrollPosition`),n===`horizontal`?(0,V.jsx)(_p,{...u,ref:t,onThumbPositionChange:()=>{if(i.viewport&&a.current){let e=i.viewport.scrollLeft,t=Mp(e,s,i.dir);a.current.style.transform=`translate3d(${t}px, 0, 0)`}},onWheelScroll:e=>{i.viewport&&(i.viewport.scrollLeft=e)},onDragScroll:e=>{i.viewport&&(i.viewport.scrollLeft=d(e,i.dir))}}):n===`vertical`?(0,V.jsx)(vp,{...u,ref:t,onThumbPositionChange:()=>{if(i.viewport&&a.current){let e=i.viewport.scrollTop,t=Mp(e,s);a.current.style.transform=`translate3d(0, ${t}px, 0)`}},onWheelScroll:e=>{i.viewport&&(i.viewport.scrollTop=e)},onDragScroll:e=>{i.viewport&&(i.viewport.scrollTop=d(e))}}):null},`ScrollAreaScrollbarVisible`)),_p=_.forwardRef(Q(function(e,t){let{sizes:n,onSizesChange:r,...i}=e,a=op(dp,e.__scopeScrollArea),[o,s]=_.useState(),c=_.useRef(null),l=hr(t,c,a.onScrollbarXChange);return _.useEffect(()=>{c.current&&s(getComputedStyle(c.current))},[c]),(0,V.jsx)(xp,{"data-orientation":`horizontal`,...i,ref:l,sizes:n,style:{bottom:0,left:a.dir===`rtl`?`var(--radix-scroll-area-corner-width)`:0,right:a.dir===`ltr`?`var(--radix-scroll-area-corner-width)`:0,"--radix-scroll-area-thumb-width":Ap(n)+`px`,...e.style},onThumbPointerDown:t=>e.onThumbPointerDown(t.x),onDragScroll:t=>e.onDragScroll(t.x),onWheelScroll:(t,n)=>{if(a.viewport){let r=a.viewport.scrollLeft+t.deltaX;e.onWheelScroll(r),Pp(r,n)&&t.preventDefault()}},onResize:()=>{c.current&&a.viewport&&o&&r({content:a.viewport.scrollWidth,viewport:a.viewport.offsetWidth,scrollbar:{size:c.current.clientWidth,paddingStart:Op(o.paddingLeft),paddingEnd:Op(o.paddingRight)}})}})},`ScrollAreaScrollbarX`)),vp=_.forwardRef(Q(function(e,t){let{sizes:n,onSizesChange:r,...i}=e,a=op(dp,e.__scopeScrollArea),[o,s]=_.useState(),c=_.useRef(null),l=hr(t,c,a.onScrollbarYChange);return _.useEffect(()=>{c.current&&s(getComputedStyle(c.current))},[c]),(0,V.jsx)(xp,{"data-orientation":`vertical`,...i,ref:l,sizes:n,style:{top:0,right:a.dir===`ltr`?0:void 0,left:a.dir===`rtl`?0:void 0,bottom:`var(--radix-scroll-area-corner-height)`,"--radix-scroll-area-thumb-height":Ap(n)+`px`,...e.style},onThumbPointerDown:t=>e.onThumbPointerDown(t.y),onDragScroll:t=>e.onDragScroll(t.y),onWheelScroll:(t,n)=>{if(a.viewport){let r=a.viewport.scrollTop+t.deltaY;e.onWheelScroll(r),Pp(r,n)&&t.preventDefault()}},onResize:()=>{c.current&&a.viewport&&o&&r({content:a.viewport.scrollHeight,viewport:a.viewport.offsetHeight,scrollbar:{size:c.current.clientHeight,paddingStart:Op(o.paddingTop),paddingEnd:Op(o.paddingBottom)}})}})},`ScrollAreaScrollbarY`)),[yp,bp]=rp(dp),xp=_.forwardRef(Q(function(e,t){let{__scopeScrollArea:n,sizes:r,hasThumb:i,onThumbChange:a,onThumbPointerUp:o,onThumbPointerDown:s,onThumbPositionChange:c,onDragScroll:l,onWheelScroll:u,onResize:d,...f}=e,p=op(dp,n),[m,h]=_.useState(null),g=hr(t,h),v=_.useRef(null),y=_.useRef(``),b=p.viewport,x=r.content-r.viewport,S=Xi(u),C=Xi(c),w=Ip(d,10);function T(e){if(v.current){let t=e.clientX-v.current.left,n=e.clientY-v.current.top;l({x:t,y:n})}}return Q(T,`handleDragScroll`),_.useEffect(()=>{let e=Q(e=>{let t=e.target;m?.contains(t)&&S(e,x)},`handleWheel`);return document.addEventListener(`wheel`,e,{passive:!1}),()=>document.removeEventListener(`wheel`,e,{passive:!1})},[b,m,x,S]),_.useEffect(C,[r,C]),Lp(m,w),Lp(p.content,w),(0,V.jsx)(yp,{scope:n,scrollbar:m,hasThumb:i,onThumbChange:Xi(a),onThumbPointerUp:Xi(o),onThumbPositionChange:C,onThumbPointerDown:Xi(s),children:(0,V.jsx)(Ci.div,{...f,ref:g,style:{position:`absolute`,...f.style},onPointerDown:Wr(e.onPointerDown,e=>{e.button===0&&(e.target.setPointerCapture(e.pointerId),v.current=m.getBoundingClientRect(),y.current=document.body.style.webkitUserSelect,document.body.style.webkitUserSelect=`none`,p.viewport&&(p.viewport.style.scrollBehavior=`auto`),T(e))}),onPointerMove:Wr(e.onPointerMove,T),onPointerUp:Wr(e.onPointerUp,e=>{let t=e.target;t.hasPointerCapture(e.pointerId)&&t.releasePointerCapture(e.pointerId),document.body.style.webkitUserSelect=y.current,p.viewport&&(p.viewport.style.scrollBehavior=``),v.current=null})})})},`ScrollAreaScrollbarImpl`)),Sp=`ScrollAreaThumb`,Cp=_.forwardRef(Q(function(e,t){let{forceMount:n,...r}=e,i=bp(Sp,e.__scopeScrollArea);return(0,V.jsx)(hi,{present:n||i.hasThumb,children:(0,V.jsx)(wp,{ref:t,...r})})},`ScrollAreaThumb`)),wp=_.forwardRef(Q(function(e,t){let{__scopeScrollArea:n,style:r,...i}=e,a=op(Sp,n),o=bp(Sp,n),{onThumbPositionChange:s}=o,c=hr(t,o.onThumbChange),l=_.useRef(void 0),u=Ip(()=>{l.current&&=(l.current(),void 0)},100);return _.useEffect(()=>{let e=a.viewport;if(e){let t=Q(()=>{if(u(),!l.current){let t=Fp(e,s);l.current=t,s()}},`handleScroll`);return s(),e.addEventListener(`scroll`,t),()=>e.removeEventListener(`scroll`,t)}},[a.viewport,u,s]),(0,V.jsx)(Ci.div,{"data-state":o.hasThumb?`visible`:`hidden`,...i,ref:c,style:{width:`var(--radix-scroll-area-thumb-width)`,height:`var(--radix-scroll-area-thumb-height)`,...r},onPointerDownCapture:Wr(e.onPointerDownCapture,e=>{let t=e.target.getBoundingClientRect(),n=e.clientX-t.left,r=e.clientY-t.top;o.onThumbPointerDown({x:n,y:r})}),onPointerUp:Wr(e.onPointerUp,o.onThumbPointerUp)})},`ScrollAreaThumbImpl`)),Tp=`ScrollAreaCorner`,Ep=_.forwardRef(Q(function(e,t){let n=op(Tp,e.__scopeScrollArea),r=!!(n.scrollbarX&&n.scrollbarY);return n.type!==`scroll`&&r?(0,V.jsx)(Dp,{...e,ref:t}):null},`ScrollAreaCorner`)),Dp=_.forwardRef(Q(function(e,t){let{__scopeScrollArea:n,...r}=e,i=op(Tp,n),[a,o]=_.useState(0),[s,c]=_.useState(0),l=!!(a&&s),{onCornerWidthChange:u,onCornerHeightChange:d}=i;return Lp(i.scrollbarX,()=>{let e=i.scrollbarX?.offsetHeight||0;i.onCornerHeightChange(e),c(e)}),Lp(i.scrollbarY,()=>{let e=i.scrollbarY?.offsetWidth||0;i.onCornerWidthChange(e),o(e)}),_.useEffect(()=>()=>{u(0),d(0)},[u,d]),l?(0,V.jsx)(Ci.div,{...r,ref:t,style:{width:a,height:s,position:`absolute`,right:i.dir===`ltr`?0:void 0,left:i.dir===`rtl`?0:void 0,bottom:0,...e.style}}):null},`ScrollAreaCornerImpl`));function Op(e){return e?parseInt(e,10):0}Q(Op,`toInt`);function kp(e,t){let n=e/t;return isNaN(n)?0:n}Q(kp,`getThumbRatio`);function Ap(e){let t=kp(e.viewport,e.content),n=e.scrollbar.paddingStart+e.scrollbar.paddingEnd,r=(e.scrollbar.size-n)*t;return Math.max(r,18)}Q(Ap,`getThumbSize`);function jp(e,t,n,r=`ltr`){let i=Ap(n),a=i/2,o=t||a,s=i-o,c=n.scrollbar.paddingStart+o,l=n.scrollbar.size-n.scrollbar.paddingEnd-s,u=n.content-n.viewport,d=r===`ltr`?[0,u]:[u*-1,0];return Np([c,l],d)(e)}Q(jp,`getScrollPositionFromPointer`);function Mp(e,t,n=`ltr`){let r=Ap(t),i=t.scrollbar.paddingStart+t.scrollbar.paddingEnd,a=t.scrollbar.size-i,o=t.content-t.viewport,s=a-r,c=$f(e,n===`ltr`?[0,o]:[o*-1,0]);return Np([0,o],[0,s])(c)}Q(Mp,`getThumbOffsetFromScroll`);function Np(e,t){return n=>{if(e[0]===e[1]||t[0]===t[1])return t[0];let r=(t[1]-t[0])/(e[1]-e[0]);return t[0]+r*(n-e[0])}}Q(Np,`linearScale`);function Pp(e,t){return e>0&&e<t}Q(Pp,`isScrollingWithinScrollbarBounds`);var Fp=Q((e,t=()=>{})=>{let n={left:e.scrollLeft,top:e.scrollTop},r=0;return Q((function i(){let a={left:e.scrollLeft,top:e.scrollTop},o=n.left!==a.left,s=n.top!==a.top;(o||s)&&t(),n=a,r=window.requestAnimationFrame(i)}),`loop`)(),()=>window.cancelAnimationFrame(r)},`addUnlinkedScrollListener`);function Ip(e,t){let n=Xi(e),r=_.useRef(0);return _.useEffect(()=>()=>window.clearTimeout(r.current),[]),_.useCallback(()=>{window.clearTimeout(r.current),r.current=window.setTimeout(n,t)},[n,t])}Q(Ip,`useDebounceCallback`);function Lp(e,t){let n=Xi(t);Yr(()=>{let t=0;if(e){let r=new ResizeObserver(()=>{cancelAnimationFrame(t),t=window.requestAnimationFrame(n)});return r.observe(e),()=>{window.cancelAnimationFrame(t),r.unobserve(e)}}},[e,n])}Q(Lp,`useResizeObserver`);var Rp=sp,zp=lp,Bp=Ep;function Vp({className:e,children:t,...n}){return(0,V.jsxs)(Rp,{"data-slot":`scroll-area`,className:B(`relative`,e),...n,children:[(0,V.jsx)(zp,{"data-slot":`scroll-area-viewport`,className:`focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1`,children:t}),(0,V.jsx)(Hp,{}),(0,V.jsx)(Bp,{})]})}function Hp({className:e,orientation:t=`vertical`,...n}){return(0,V.jsx)(fp,{"data-slot":`scroll-area-scrollbar`,orientation:t,className:B(`flex touch-none p-px transition-colors select-none`,t===`vertical`&&`h-full w-2.5 border-l border-l-transparent`,t===`horizontal`&&`h-2.5 flex-col border-t border-t-transparent`,e),...n,children:(0,V.jsx)(Cp,{"data-slot":`scroll-area-thumb`,className:`bg-border relative flex-1 rounded-full`})})}function Up({open:e=!1,profileId:t=``,onOpenChange:n}){let{t:r}=cr(),[i,a]=(0,_.useState)(!1),[o,s]=(0,_.useState)([]);(0,_.useEffect)(()=>{e&&t&&c(t)},[e,t]);async function c(e){a(!0),s([]);let t=await Un(`/api/user-profile/changelog?profileId=${encodeURIComponent(e)}&limit=10`);a(!1),t.success&&t.data?s(t.data):s([])}return(0,V.jsx)(Ds,{open:e,onOpenChange:n,children:(0,V.jsxs)(As,{className:`sm:max-w-lg`,children:[(0,V.jsx)(js,{children:(0,V.jsx)(Ns,{children:r(`modal-changelog-title`)})}),(0,V.jsx)(Vp,{className:`max-h-80`,children:i?(0,V.jsx)(`div`,{className:`text-sm text-muted-foreground py-6 text-center`,children:r(`loading-changelog`)}):o.length===0?(0,V.jsx)(`div`,{className:`text-sm text-muted-foreground py-6 text-center`,children:r(`empty-changelog`)}):(0,V.jsx)(`div`,{className:`space-y-3 pr-3`,children:o.map((e,t)=>(0,V.jsxs)(`div`,{className:`rounded-lg border border-border p-3 space-y-1`,children:[(0,V.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2 text-xs`,children:[(0,V.jsxs)(`span`,{className:`font-medium`,children:[`v`,e.version]}),(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:e.changeType}),(0,V.jsx)(`span`,{className:`text-muted-foreground ml-auto`,children:jl(e.createdAt)})]}),(0,V.jsx)(`p`,{className:`text-sm`,children:e.changeSummary})]},`${e.version}-${t}`))})})]})})}function Wp({className:e,type:t,...n}){return(0,V.jsx)(`input`,{type:t,"data-slot":`input`,className:B(`border-input file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 flex h-8 w-full min-w-0 rounded-lg border bg-transparent px-2.5 py-1 text-sm shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50`,`focus-visible:border-ring focus-visible:ring-ring/30 focus-visible:ring-3`,`aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive`,e),...n})}function Gp({open:e=!1,type:t=null,index:n=0,action:r=`edit`,item:i=null,onOpenChange:a,onSaved:o}){let{t:s}=cr(),[c,l]=(0,_.useState)(``),[u,d]=(0,_.useState)(``),[f,p]=(0,_.useState)([``]),[m,h]=(0,_.useState)(`1`),[g,v]=(0,_.useState)(!1),y=r===`edit`,b=t===`workflows`;(0,_.useEffect)(()=>{e&&i&&(l(i.category||``),d(i.description||``),p(i.steps?.length?[...i.steps]:[``]),h(`1`))},[e,i]);let x=y?s(`btn-edit`)||`Edit Item`:m===`2`?s(`confirm-delete-title`)||`Confirm Delete`:s(`confirm-delete`)||`Delete Item?`;function S(e){a?.(e)}function C(){p(e=>[...e,``])}function w(e){p(t=>{let n=t.filter((t,n)=>n!==e);return n.length===0?[``]:n})}function T(e,t){p(n=>n.map((n,r)=>r===e?t:n))}async function E(e){if(e.preventDefault(),!t)return;if(r===`delete`){if(m===`1`){h(`2`);return}v(!0);let e=await Un(`/api/user-profile/item`,{method:`PATCH`,headers:{"Content-Type":`application/json`},body:JSON.stringify({type:t,index:n,action:`delete`})});v(!1),e.success?(H.success(s(`toast-delete-success`)||`Item deleted`),S(!1),o?.()):H.error(e.error||s(`toast-delete-failed`)||`Delete failed`);return}v(!0);let i={type:t,index:n,action:`edit`,category:b?void 0:c,description:u};t===`workflows`&&(i.steps=f.map(e=>e.trim()).filter(Boolean));let a=await Un(`/api/user-profile/item`,{method:`PATCH`,headers:{"Content-Type":`application/json`},body:JSON.stringify(i)});v(!1),a.success?(H.success(s(`toast-update-success`)||`Item updated`),S(!1),o?.()):H.error(a.error||s(`toast-update-failed`)||`Update failed`)}return(0,V.jsx)(Ds,{open:e,onOpenChange:S,children:(0,V.jsxs)(As,{className:`sm:max-w-lg`,children:[(0,V.jsx)(js,{children:(0,V.jsx)(Ns,{children:x})}),(0,V.jsxs)(`form`,{className:`space-y-4`,onSubmit:E,children:[y||m===`1`?(0,V.jsxs)(V.Fragment,{children:[b?null:(0,V.jsxs)(`div`,{className:`space-y-2`,children:[(0,V.jsx)(Sl,{htmlFor:`profile-item-category`,children:s(`label-category`)}),(0,V.jsx)(Wp,{id:`profile-item-category`,value:c,onChange:e=>l(e.target.value),disabled:!y})]}),(0,V.jsxs)(`div`,{className:`space-y-2`,children:[(0,V.jsx)(Sl,{htmlFor:`profile-item-description`,children:s(`label-description`)}),(0,V.jsx)(Cl,{id:`profile-item-description`,rows:4,value:u,onChange:e=>d(e.target.value),disabled:!y})]}),b&&y?(0,V.jsxs)(`div`,{className:`space-y-2`,children:[(0,V.jsx)(Sl,{children:s(`label-steps`)}),(0,V.jsx)(`div`,{className:`space-y-2`,children:f.map((e,t)=>(0,V.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,V.jsx)(`span`,{className:`text-xs text-muted-foreground w-5 tabular-nums`,children:t+1}),(0,V.jsx)(Wp,{value:e,placeholder:`Step ${t+1}...`,onChange:e=>T(t,e.target.value)}),(0,V.jsx)(W,{type:`button`,variant:`ghost`,size:`icon-xs`,onClick:()=>w(t),children:`×`})]},t))}),(0,V.jsx)(W,{type:`button`,variant:`secondary`,size:`sm`,onClick:C,children:s(`btn-add-step`)})]}):null]}):null,(0,V.jsxs)(Ms,{children:[(0,V.jsx)(W,{type:`button`,variant:`secondary`,onClick:()=>S(!1),children:s(`btn-cancel`)}),(0,V.jsx)(W,{type:`submit`,variant:y?`default`:`destructive`,disabled:g,children:y?s(`btn-save`)||`Save`:s(`btn-delete`)||`Delete`})]})]})]})})}var Kp=20;function qp(e,t){let n=e.length,r=Math.max(1,Math.ceil(n/Kp)),i=Math.min(t,r),a=(i-1)*Kp;return{items:e.slice(a,a+Kp),total:n,totalPages:r,page:i,start:a}}function Jp(e){return Math.round((e.confidence||0)*1e3)/10}function Yp(e){return e.evidence?Array.isArray(e.evidence)?e.evidence.join(`
122
- `):e.evidence:``}function Xp(e){return e.evidence?Array.isArray(e.evidence)?e.evidence.length:1:0}function Zp({profile:e,loading:t=!1,onRefresh:n,onCleanup:r}){let{t:i}=cr(),[a,o]=(0,_.useState)({pref:1,pat:1,wf:1}),[s,c]=(0,_.useState)(!1),[l,u]=(0,_.useState)(!1),[d,f]=(0,_.useState)(null),[p,m]=(0,_.useState)(0),[h,g]=(0,_.useState)(`edit`),[v,y]=(0,_.useState)(null),b=(0,_.useMemo)(()=>{if(!e?.exists||!e.profileData)return null;let t=e.profileData;if(typeof t==`string`)try{t=JSON.parse(t)}catch{return null}return t},[e]),x=(0,_.useMemo)(()=>Dc(b?.preferences),[b]),S=(0,_.useMemo)(()=>Dc(b?.patterns),[b]),C=(0,_.useMemo)(()=>Dc(b?.workflows),[b]),w=(0,_.useMemo)(()=>C.map((e,t)=>({item:e,index:t})).sort((e,t)=>(t.item.frequency||0)-(e.item.frequency||0)),[C]),T=qp(x,a.pref),E=qp(S,a.pat),D=qp(w,a.wf),O=C.length;function te(e,t,n){let r=(e===`preferences`?x:e===`patterns`?S:C)[t];r&&(f(e),m(t),g(n),y(r),u(!0))}function re(e,t){return e.indexOf(t)}function ie({page:e,pageKey:t}){return e.total<=Kp?null:(0,V.jsxs)(`div`,{className:`flex flex-wrap items-center gap-1.5 pt-2`,children:[(0,V.jsxs)(`span`,{className:`text-xs text-muted-foreground mr-2`,children:[e.start+1,`-`,Math.min(e.start+Kp,e.total),` / `,e.total]}),Array.from({length:e.totalPages},(e,t)=>t+1).map(n=>(0,V.jsx)(W,{size:`xs`,variant:n===e.page?`default`:`outline`,onClick:()=>o(e=>({...e,[t]:n})),children:n},n))]})}function k({item:e,type:t,fullList:n}){let r=re(n,e),a=Jp(e);return(0,V.jsxs)(`div`,{className:`rounded-xl border border-border bg-card/50 p-3 space-y-2`,children:[(0,V.jsxs)(`div`,{className:`flex items-start gap-2`,children:[(0,V.jsx)(Al,{variant:`outline`,children:e.category||`General`}),(0,V.jsxs)(`div`,{className:`flex items-center gap-0.5 ml-auto`,children:[(0,V.jsx)(W,{variant:`ghost`,size:`icon-xs`,title:i(`btn-edit`)||`Edit`,onClick:()=>te(t,r,`edit`),children:(0,V.jsx)(_e,{className:`size-3`})}),(0,V.jsx)(W,{variant:`ghost`,size:`icon-xs`,title:i(`btn-delete`)||`Delete`,onClick:()=>te(t,r,`delete`),children:(0,V.jsx)(De,{className:`size-3`})})]}),(0,V.jsxs)(`div`,{className:`size-9 rounded-full border border-border grid place-items-center text-[10px] tabular-nums shrink-0`,title:`${a}%`,children:[a,`%`]})]}),(0,V.jsx)(`p`,{className:`text-sm`,children:e.description||``}),e.evidence||e.frequency?(0,V.jsxs)(`div`,{className:`flex items-center gap-1.5 text-xs text-muted-foreground`,children:[(0,V.jsxs)(`span`,{className:`inline-flex items-center gap-1`,title:i(`label-evidence-tooltip`,{count:e.frequency||1}),children:[(0,V.jsx)(Ee,{className:`size-3`}),e.frequency||1]}),Xp(e)>0?(0,V.jsxs)(V.Fragment,{children:[(0,V.jsx)(`span`,{children:`·`}),(0,V.jsxs)(`span`,{className:`inline-flex items-center gap-1`,title:Yp(e),children:[(0,V.jsx)(N,{className:`size-3`}),Xp(e),` evidence`]})]}):null]}):null]})}return t&&!e?(0,V.jsx)(`div`,{className:`text-sm text-muted-foreground py-8 text-center`,children:i(`loading-profile`)}):e?.exists?b?(0,V.jsxs)(V.Fragment,{children:[(0,V.jsxs)(`div`,{className:`space-y-6`,children:[(0,V.jsxs)(`div`,{className:`space-y-2`,children:[(0,V.jsx)(`h3`,{className:`text-base font-medium`,children:e.displayName||e.userId}),(0,V.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,V.jsxs)(`span`,{className:`rounded-full bg-muted px-2.5 py-1 text-xs`,children:[(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:i(`profile-version`)}),(0,V.jsx)(`span`,{className:`ml-1`,children:e.version})]}),(0,V.jsxs)(`span`,{className:`rounded-full bg-muted px-2.5 py-1 text-xs`,children:[(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:i(`profile-prompts`)}),(0,V.jsx)(`span`,{className:`ml-1`,children:e.totalPromptsAnalyzed})]}),(0,V.jsxs)(`span`,{className:`rounded-full bg-muted px-2.5 py-1 text-xs`,children:[(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:i(`profile-updated`)}),(0,V.jsx)(`span`,{className:`ml-1`,children:e.lastAnalyzedAt?jl(e.lastAnalyzedAt):`—`})]}),(0,V.jsxs)(`div`,{className:`ms-auto flex flex-wrap items-center gap-1.5`,children:[(0,V.jsxs)(W,{variant:`outline`,size:`sm`,onClick:()=>r?.(),children:[(0,V.jsx)(I,{className:`size-3.5`}),i(`btn-ai-cleanup`)]}),(0,V.jsxs)(W,{variant:`secondary`,size:`sm`,onClick:()=>n?.(),children:[(0,V.jsx)(xe,{className:`size-3.5`}),i(`btn-refresh`)]}),(0,V.jsxs)(W,{variant:`secondary`,size:`sm`,onClick:()=>c(!0),children:[(0,V.jsx)(Se,{className:`size-3.5`}),`History`]})]})]})]}),(0,V.jsxs)(`div`,{className:`grid gap-6`,children:[(0,V.jsxs)(`section`,{id:`profile-preferences`,className:`scroll-mt-4 space-y-3`,children:[(0,V.jsxs)(`h4`,{className:`flex items-center gap-2 text-sm font-medium`,children:[(0,V.jsx)(ce,{className:`size-4`}),i(`profile-preferences`),(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:x.length})]}),x.length===0?(0,V.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:i(`empty-preferences`)}):(0,V.jsxs)(V.Fragment,{children:[(0,V.jsx)(`div`,{className:`grid gap-2 sm:grid-cols-2 lg:grid-cols-3`,children:T.items.map(e=>(0,V.jsx)(k,{item:e,type:`preferences`,fullList:x},re(x,e)))}),(0,V.jsx)(ie,{page:T,pageKey:`pref`})]})]}),(0,V.jsxs)(`section`,{id:`profile-patterns`,className:`scroll-mt-4 space-y-3`,children:[(0,V.jsxs)(`h4`,{className:`flex items-center gap-2 text-sm font-medium`,children:[(0,V.jsx)(ee,{className:`size-4`}),i(`profile-patterns`),(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:S.length})]}),S.length===0?(0,V.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:i(`empty-patterns`)}):(0,V.jsxs)(V.Fragment,{children:[(0,V.jsx)(`div`,{className:`grid gap-2 sm:grid-cols-2 lg:grid-cols-3`,children:E.items.map(e=>(0,V.jsx)(k,{item:e,type:`patterns`,fullList:S},re(S,e)))}),(0,V.jsx)(ie,{page:E,pageKey:`pat`})]})]}),(0,V.jsxs)(`section`,{id:`profile-workflows`,className:`scroll-mt-4 space-y-3`,children:[(0,V.jsxs)(`h4`,{className:`flex items-center gap-2 text-sm font-medium`,children:[(0,V.jsx)(Me,{className:`size-4`}),i(`profile-workflows`),(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:O})]}),O===0?(0,V.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:i(`empty-workflows`)}):(0,V.jsxs)(V.Fragment,{children:[(0,V.jsx)(`div`,{className:`space-y-2`,children:D.items.map(e=>{let t=e.item,n=e.index,r=Jp(t);return(0,V.jsxs)(`div`,{className:`rounded-xl border border-border bg-card/50 p-3 space-y-2`,children:[(0,V.jsxs)(`div`,{className:`flex items-start gap-2`,children:[(0,V.jsx)(`div`,{className:`text-sm font-medium flex-1`,children:t.description||``}),(0,V.jsxs)(`div`,{className:`flex items-center gap-0.5`,children:[(0,V.jsx)(W,{variant:`ghost`,size:`icon-xs`,onClick:()=>te(`workflows`,n,`edit`),children:(0,V.jsx)(_e,{className:`size-3`})}),(0,V.jsx)(W,{variant:`ghost`,size:`icon-xs`,onClick:()=>te(`workflows`,n,`delete`),children:(0,V.jsx)(De,{className:`size-3`})})]}),(0,V.jsxs)(`div`,{className:`size-9 rounded-full border border-border grid place-items-center text-[10px] tabular-nums shrink-0`,children:[r,`%`]})]}),t.steps?.length?(0,V.jsx)(`div`,{className:`flex flex-wrap items-center gap-1.5`,children:t.steps.map((e,n)=>(0,V.jsxs)(`div`,{className:`contents`,children:[(0,V.jsxs)(`div`,{className:`inline-flex items-center gap-1.5 rounded-lg bg-muted px-2 py-1 text-xs`,children:[(0,V.jsx)(`span`,{className:`text-muted-foreground tabular-nums`,children:n+1}),(0,V.jsx)(`span`,{children:e})]}),n<(t.steps?.length||0)-1?(0,V.jsx)(ne,{className:`size-3 text-muted-foreground`}):null]},n))}):null,(0,V.jsxs)(`div`,{className:`flex items-center gap-1.5 text-xs text-muted-foreground`,children:[(0,V.jsxs)(`span`,{className:`inline-flex items-center gap-1`,title:i(`label-evidence-tooltip`,{count:t.frequency||1}),children:[(0,V.jsx)(Ee,{className:`size-3`}),t.frequency||1]}),Xp(t)>0?(0,V.jsxs)(V.Fragment,{children:[(0,V.jsx)(`span`,{children:`·`}),(0,V.jsxs)(`span`,{className:`inline-flex items-center gap-1`,title:Yp(t),children:[(0,V.jsx)(N,{className:`size-3`}),Xp(t),` evidence`]})]}):null]})]},e.index)})}),(0,V.jsx)(ie,{page:D,pageKey:`wf`})]})]})]})]}),(0,V.jsx)(Up,{open:s,onOpenChange:c,profileId:e?.id||``}),(0,V.jsx)(Gp,{open:l,onOpenChange:u,type:d,index:p,action:h,item:v,onSaved:n})]}):null:(0,V.jsxs)(`div`,{className:`flex flex-col items-center gap-2 py-10 text-muted-foreground`,children:[(0,V.jsx)(Ae,{className:`size-8`}),(0,V.jsx)(`p`,{className:`text-sm`,children:e?.message||i(`empty-preferences`)})]})}function Qp({open:e=!1,count:t=0,onOpenChange:n,onComplete:r}){let{t:i}=cr(),[a,o]=(0,_.useState)(!1),[s,c]=(0,_.useState)(``),[l,u]=(0,_.useState)(0);(0,_.useEffect)(()=>{e&&!a&&(c(i(`migration-found-tags`,{count:t})),u(0))},[e,a,t,i]);function d(e){a&&!e||n?.(e)}async function f(){o(!0),c(i(`status-migration-init`)),u(0);let e=!0,t=0,n=1e3;for(;e&&t<n;){t++;let n=await Un(`/api/migration/tags/run-batch`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({batchSize:3})});if(!n.success||!n.data){c(i(`toast-migration-failed`)+`: `+(n.error||``)),o(!1);return}let r=n.data.processed;e=n.data.hasMore;let a=n.data.total;u(a>0?Math.round(r/a*100):0),c(i(`status-migration-progress`,{current:r,total:a})),e&&await new Promise(e=>setTimeout(e,100))}if(t>=n){c(i(`migration-stopped`)),o(!1);return}u(100),c(i(`toast-migration-success`)),H.success(i(`toast-migration-success`)),setTimeout(()=>{o(!1),d(!1),r?.()},2e3)}return(0,V.jsx)(Ds,{open:e,onOpenChange:d,children:(0,V.jsxs)(As,{className:`sm:max-w-md`,showCloseButton:!a,children:[(0,V.jsx)(js,{children:(0,V.jsx)(Ns,{children:i(`modal-migration-title`)})}),(0,V.jsxs)(`div`,{className:`space-y-3`,children:[(0,V.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:s}),(0,V.jsx)(`div`,{className:`h-2 rounded-full bg-muted overflow-hidden`,children:(0,V.jsx)(`div`,{className:`h-full bg-primary transition-all duration-200`,style:{width:`${l}%`}})}),(0,V.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:i(`migration-note`)})]}),a?null:(0,V.jsx)(Ms,{children:(0,V.jsxs)(W,{onClick:f,children:[(0,V.jsx)(ye,{className:`size-3.5`}),i(`btn-start-migration`)]})})]})})}async function $(e,t={}){let n=(t.method??`GET`).toUpperCase(),r=n!==`GET`&&n!==`HEAD`,i=await fetch(e,{...t,headers:{"x-omms-token":window.__OMMS_TOKEN__??``,...r||t.body?{"Content-Type":`application/json`}:{},...t.headers}}),a=await i.json();if(!i.ok)throw Error(a.error??`Request failed (${i.status})`);return a}var $p=new Set,em=0;function tm(e){em++;for(let t of $p)t(e)}function nm(){let e=em;return{isCurrent:()=>em===e}}async function rm(){try{let e=await $(`/api/settings`);return tm(e),e}catch{return null}}function im(e){return $p.add(e),()=>{$p.delete(e)}}function am(e,t){return`${e.replace(/\.+$/,``)}. ${t}`}var om={Models:[`模型`,`النماذج`],"External API fallback":[`外部 API 备用模型`,`النموذج الاحتياطي عبر واجهة API الخارجية`],none:[`无`,`لا يوجد`],ready:[`就绪`,`جاهز`],"not ready":[`未就绪`,`غير جاهز`],"Credentials (values hidden)":[`凭据(不显示内容)`,`بيانات الاعتماد (القيم مخفية)`],set:[`已设置`,`مضبوط`],"not set":[`未设置`,`غير مضبوط`],"Effective model":[`当前使用模型`,`النموذج المستخدم`],session:[`会话`,`الجلسة`],"external API":[`外部 API`,`واجهة API الخارجية`],unavailable:[`不可用`,`غير متاح`],"Project override is active. Changes to the global file may not take effect here.":[`项目设置优先。全局文件的更改可能不会在此生效。`,`إعدادات المشروع لها الأولوية. قد لا تسري تغييرات الملف العام هنا.`],"Model choice":[`模型选择`,`اختيار النموذج`],"Session model":[`会话模型`,`نموذج الجلسة`],"Manual model":[`手动选择模型`,`اختيار النموذج يدوياً`],"Manual provider/model":[`手动输入提供商/模型`,`إدخال المزوّد/النموذج يدوياً`],"Model list unavailable. Enter provider/model manually.":[`模型列表不可用。请手动输入提供商/模型。`,`قائمة النماذج غير متاحة. أدخل المزوّد/النموذج يدوياً.`],"Save model":[`保存模型`,`حفظ النموذج`],"Enter a model as provider/model.":[`按 提供商/模型 格式输入。`,`أدخل النموذج بصيغة المزوّد/النموذج.`],"Saved. New capture and profile work uses these settings.":[`已保存。下一次捕获和用户画像将使用这些设置。`,`تم الحفظ. ستستخدم عمليات الالتقاط والملف الشخصي الجديدة هذه الإعدادات.`],"Saved. OMMS now reads ~/.config/omms/omms.jsonc. The legacy config was kept.":[`已保存。OMMS 现在读取 ~/.config/omms/omms.jsonc,旧配置保留。`,`تم الحفظ. يقرأ OMMS الآن الملف ~/.config/omms/omms.jsonc مع الاحتفاظ بالإعدادات القديمة.`],"Current settings were reloaded; check the values and save again.":[`已重新加载当前设置;检查数值后再次保存。`,`أُعيد تحميل الإعدادات الحالية. راجع القيم ثم احفظ مجدداً.`],"Capture diagnostics":[`捕获诊断`,`تشخيص الالتقاط`],"Time range":[`时间范围`,`الفترة الزمنية`],"24 hours":[`24 小时`,`24 ساعة`],"7 days":[`7 天`,`7 أيام`],"30 days":[`30 天`,`30 يوماً`],"90 days":[`90 天`,`90 يوماً`],"Outcomes by model":[`各模型结果`,`النتائج حسب النموذج`],"Host / model":[`主机 / 模型`,`المضيف / النموذج`],Saved:[`已保存`,`محفوظ`],Skipped:[`已跳过`,`متجاوز`],Failed:[`失败`,`فشل`],Total:[`总数`,`المجموع`],"Failure reasons":[`失败原因`,`أسباب الفشل`],"Recent attempts":[`最近尝试`,`المحاولات الأخيرة`],Time:[`时间`,`الوقت`],Host:[`主机`,`المضيف`],Source:[`来源`,`المصدر`],Session:[`会话`,`الجلسة`],Path:[`路径`,`المسار`],Model:[`模型`,`النموذج`],Stop:[`结束原因`,`سبب التوقف`],Blocks:[`区块`,`الكتل`],Prompt:[`提示词长度`,`طول الطلب`],Reply:[`回复长度`,`طول الرد`],Duration:[`耗时`,`المدة`],Outcome:[`结果`,`النتيجة`],Reason:[`原因`,`السبب`],"Save capture traces":[`保存捕获追踪`,`حفظ تتبعات الالتقاط`],"Traces can contain conversation content after redaction. Keep tracing off unless you need it for debugging.":[`追踪文件在脱敏后仍可能包含对话内容。仅在调试时开启。`,`قد تحتوي التتبعات على محتوى المحادثة بعد التنقيح. فعّلها عند التصحيح فقط.`],"This project turns tracing off. The global switch cannot turn it on here.":[`此项目已关闭追踪。全局开关无法在此开启。`,`أوقف هذا المشروع التتبع. لا يمكن للمفتاح العام تفعيله هنا.`],"Trace retention (days)":[`追踪保留天数`,`مدة حفظ التتبعات (بالأيام)`],"Attempt retention (days)":[`尝试记录保留天数`,`مدة حفظ المحاولات (بالأيام)`],"Trace files":[`追踪文件`,`ملفات التتبع`],View:[`查看`,`عرض`],Delete:[`删除`,`حذف`],Health:[`健康检查`,`فحص الحالة`],"Config files":[`配置文件`,`ملفات الإعدادات`],"Memory store":[`记忆存储`,`مخزن الذاكرة`],Embedding:[`嵌入模型`,`نموذج التضمين`],"Web binding":[`网页服务绑定`,`ربط خادم الويب`],"OpenCode model":[`OpenCode 模型`,`نموذج OpenCode`],"Pi model":[`Pi 模型`,`نموذج Pi`],"Capture failures (24 hours)":[`捕获失败(24 小时)`,`فشل الالتقاط (24 ساعة)`],"OpenCode model test":[`OpenCode 模型测试`,`اختبار نموذج OpenCode`],"Pi model test":[`Pi 模型测试`,`اختبار نموذج Pi`],"Run checks":[`运行检查`,`تشغيل الفحوصات`],"Run checks and test models":[`运行检查并测试模型`,`تشغيل الفحوصات واختبار النماذج`],"Checking…":[`正在检查…`,`جارٍ الفحص…`],pass:[`通过`,`نجاح`],warn:[`警告`,`تحذير`],fail:[`失败`,`فشل`],"Import and backfill":[`导入和回填`,`الاستيراد والاستكمال`],"Automatic import":[`自动导入`,`الاستيراد التلقائي`],"Automatic import makes model calls. Changes apply at the next host start. Turning it off stops a running import after the current exchange.":[`自动导入会调用模型。更改在下次启动主机时生效。关闭后,正在运行的导入将在当前轮次结束后停止。`,`يستدعي الاستيراد التلقائي النموذج. تسري التغييرات عند بدء المضيف التالي. يوقف التعطيل الاستيراد بعد التبادل الحالي.`],"Import past chats automatically":[`自动导入过去的对话`,`استيراد المحادثات السابقة تلقائياً`],"Backfill model":[`历史导入模型`,`نموذج استيراد السجل`],"Same as live capture":[`与实时捕获相同`,`مثل الالتقاط المباشر`],"Saved. Changes apply at the next host start.":[`已保存。更改在下次启动主机时生效。`,`تم الحفظ. تسري التغييرات عند بدء المضيف التالي.`],State:[`状态`,`الحالة`],"not started":[`尚未开始`,`لم يبدأ`],"not-started":[`尚未开始`,`لم يبدأ`],Imported:[`已导入`,`مستورَد`],Pending:[`待处理`,`معلّق`],"Unresolved sessions":[`目录未解析的会话`,`جلسات بمجلدات غير محددة`],Cutoff:[`截止时间`,`وقت القطع`],stopped:[`已停止`,`توقف`],"Web app":[`网页应用`,`تطبيق الويب`],"Start web app at login":[`登录时启动网页应用`,`بدء تطبيق الويب عند تسجيل الدخول`],"Login item":[`登录启动项`,`عنصر بدء تسجيل الدخول`],"not installed":[`未安装`,`غير مثبّت`],"not-installed":[`未安装`,`غير مثبّت`],installed:[`已安装`,`مثبّت`],unsupported:[`不受支持`,`غير مدعوم`],"no-runtime":[`缺少运行环境`,`بيئة التشغيل غير متاحة`],"no-package":[`缺少软件包`,`الحزمة غير متاحة`],"Login items are unsupported on this platform.":[`此平台不支持登录启动项。`,`عناصر بدء تسجيل الدخول غير مدعومة على هذا النظام.`],"Install Node or Bun to start the web app at login.":[`安装 Node 或 Bun 后,网页应用才能在登录时启动。`,`ثبّت Node أو Bun لبدء تطبيق الويب عند تسجيل الدخول.`],"Use these commands to apply the change now:":[`使用以下命令立即应用更改:`,`استخدم هذه الأوامر لتطبيق التغيير الآن:`],"Saved. The login item changes at the next Pi or OpenCode start.":[`已保存。登录启动项将在下次启动 Pi 或 OpenCode 时更新。`,`تم الحفظ. يتغير عنصر بدء التشغيل عند بدء Pi أو OpenCode التالي.`],"History host":[`历史记录主机`,`مضيف السجل`],Scope:[`范围`,`النطاق`],"Current project":[`当前项目`,`المشروع الحالي`],"All projects":[`所有项目`,`كل المشاريع`],"Project directory":[`项目目录`,`مجلد المشروع`],"Current directory":[`当前目录`,`المجلد الحالي`],"Profile batch size":[`用户画像批量大小`,`حجم دفعة الملف الشخصي`],"Directory maps (old=new, one per line)":[`目录映射(旧=新,每行一条)`,`تعيين المجلدات (قديم=جديد، واحد في كل سطر)`],"Force reimport":[`强制重新导入`,`فرض إعادة الاستيراد`],"Skip memories":[`跳过记忆`,`تجاوز الذكريات`],"Skip profile":[`跳过用户画像`,`تجاوز الملف الشخصي`],"Import model":[`导入模型`,`نموذج الاستيراد`],"Saved external API":[`已保存的外部 API`,`واجهة API الخارجية المحفوظة`],"Preview (dry run)":[`预览(不写入)`,`معاينة (تشغيل تجريبي)`],"Start import":[`开始导入`,`بدء الاستيراد`],"Cancel after current unit":[`完成当前单元后取消`,`الإلغاء بعد الوحدة الحالية`],"Directory maps must be old=new, one per line":[`目录映射须为 旧=新,每行一条`,`يجب أن يكون تعيين المجلدات بصيغة قديم=جديد، واحد في كل سطر`],running:[`运行中`,`جارٍ التشغيل`],cancelling:[`正在取消`,`جارٍ الإلغاء`],cancelled:[`已取消`,`أُلغي`],done:[`已完成`,`اكتمل`],failed:[`失败`,`فشل`],units:[`单元`,`وحدات`],"List sessions":[`列出会话`,`عرض الجلسات`],"Refresh list":[`刷新列表`,`تحديث القائمة`],"Advanced options":[`高级选项`,`خيارات متقدمة`],"Hide advanced options":[`隐藏高级选项`,`إخفاء الخيارات المتقدمة`],"default location":[`默认位置`,`الموقع الافتراضي`],"Prompt date from":[`提示日期起`,`تاريخ الطلب من`],"Prompt date to":[`提示日期止`,`تاريخ الطلب إلى`],"Prompt dates are inclusive, use your time zone, and filter the turns inside each session, not the list. Empty dates include every turn.":[`提示日期包含首尾两天,按你所在时区计算,只筛选每个会话中的对话轮次,不筛选列表。留空则包含所有轮次。`,`تواريخ الطلب شاملة لليومين، وتُحسب بمنطقتك الزمنية، وتُصفّي الأدوار داخل كل جلسة لا القائمة. تركها فارغة يشمل كل الأدوار.`],"Sessions with directories that no longer exist":[`目录已不存在的会话`,`جلسات لم تعد مجلداتها موجودة`],"Show them and add a directory map":[`显示它们并添加目录映射`,`اعرضها وأضف تعيين مجلد`],Selected:[`已选择`,`المحدد`],"Select all matching":[`选择所有匹配项`,`تحديد كل المطابق`],"Clear selection":[`清除选择`,`مسح التحديد`],Select:[`选择`,`تحديد`],Created:[`创建时间`,`تاريخ الإنشاء`],"Resolved by":[`解析方式`,`طريقة التحديد`],recorded:[`记录的目录`,`المجلد المسجّل`],mapped:[`目录映射`,`تعيين مجلد`],"project root":[`项目根目录`,`جذر المشروع`],missing:[`缺失`,`مفقود`],"No sessions match.":[`没有匹配的会话。`,`لا توجد جلسات مطابقة.`],Previous:[`上一页`,`السابق`],Next:[`下一页`,`التالي`],None:[`无`,`لا شيء`],"Configured, not tested.":[`已配置,未测试。`,`مُعدّ، لم يُختبر.`],"Test models in Health":[`在健康检查中测试模型`,`اختبر النماذج في قسم السلامة`],Import:[`导入`,`استيراد`],sessions:[`个会话`,`جلسات`],"List sessions first.":[`请先列出会话。`,`اعرض الجلسات أولاً.`],"Options changed. Refresh the session list first.":[`选项已更改。请先刷新会话列表。`,`تغيّرت الخيارات. حدّث قائمة الجلسات أولاً.`],"Select at least one session.":[`请至少选择一个会话。`,`حدّد جلسة واحدة على الأقل.`],"Select at most 1000 sessions one by one, or use Select all matching.":[`逐个选择最多 1000 个会话,或使用“选择所有匹配项”。`,`حدّد 1000 جلسة كحد أقصى واحدة تلو الأخرى، أو استخدم «تحديد كل المطابق».`],"Model readiness is unknown. Reload the page.":[`无法确定模型是否就绪。请重新加载页面。`,`حالة جاهزية النموذج غير معروفة. أعد تحميل الصفحة.`],"No import model is ready. Connect a model in OpenCode or complete the external API settings.":[`没有可用的导入模型。请在 OpenCode 中连接模型,或补全外部 API 设置。`,`لا يوجد نموذج استيراد جاهز. اربط نموذجاً في OpenCode أو أكمل إعدادات واجهة API الخارجية.`],"The external API is not ready":[`外部 API 未就绪`,`واجهة API الخارجية غير جاهزة`],"The external API has no memoryModel in the global config":[`全局配置中缺少外部 API 的 memoryModel`,`لا يحتوي الإعداد العام على memoryModel لواجهة API الخارجية`],"The external API has no memoryApiUrl in the global config":[`全局配置中缺少外部 API 的 memoryApiUrl`,`لا يحتوي الإعداد العام على memoryApiUrl لواجهة API الخارجية`],"The external API key is missing in the OpenCode process; check memoryApiKey and its env:// or file:// source":[`OpenCode 进程中缺少外部 API 密钥;请检查 memoryApiKey 及其 env:// 或 file:// 来源`,`مفتاح واجهة API الخارجية غير موجود في عملية OpenCode؛ تحقّق من memoryApiKey ومصدره env:// أو file://`],"The memoryProvider in the global config is not supported for imports":[`全局配置中的 memoryProvider 不支持导入`,`مزوّد memoryProvider في الإعداد العام غير مدعوم للاستيراد`],"Pi sessions folder or one .jsonl session file":[`Pi 会话文件夹或单个 .jsonl 会话文件`,`مجلد جلسات Pi أو ملف جلسة ‎.jsonl واحد`],"OpenCode database file":[`OpenCode 数据库文件`,`ملف قاعدة بيانات OpenCode`],"Absolute path, for example on a mounted volume":[`绝对路径,例如已挂载卷上的路径`,`مسار مطلق، مثلاً على وحدة تخزين مركّبة`],"Use this source":[`使用此来源`,`استخدم هذا المصدر`],Browse:[`浏览`,`استعراض`],"Use the default location":[`使用默认位置`,`استخدم الموقع الافتراضي`],"Chosen source":[`已选来源`,`المصدر المختار`],Choose:[`选择`,`اختيار`],"Only the first 500 entries are shown.":[`仅显示前 500 项。`,`تُعرض أول 500 عنصر فقط.`],"Reload the page before saving again.":[`再次保存前请重新加载页面。`,`أعد تحميل الصفحة قبل الحفظ مرة أخرى.`],"The current settings could not be reloaded. Reload the page.":[`无法重新加载当前设置。请重新加载页面。`,`تعذّر إعادة تحميل الإعدادات الحالية. أعد تحميل الصفحة.`],Log:[`日志`,`السجل`],"Capture attempts only":[`仅显示捕获尝试`,`محاولات الالتقاط فقط`],Refresh:[`刷新`,`تحديث`],"Copy log path":[`复制日志路径`,`نسخ مسار السجل`],"Log lines":[`日志行`,`أسطر السجل`],"No log has been written yet.":[`尚未写入日志。`,`لم يُكتب سجل بعد.`],"Loading log…":[`正在加载日志…`,`جارٍ تحميل السجل…`],"External API":[`外部 API`,`واجهة API الخارجية`],"An OpenAI- or Anthropic-compatible endpoint that either host can use for capture and backfill.":[`兼容 OpenAI 或 Anthropic 的端点,两个宿主都可用于捕获和回填。`,`نقطة نهاية متوافقة مع OpenAI أو Anthropic يمكن لأي مضيف استخدامها للالتقاط والاستيراد.`],Provider:[`提供商`,`المزوّد`],"API URL":[`API 地址`,`عنوان API`],"API key":[`API 密钥`,`مفتاح API`],"Save endpoint":[`保存端点`,`حفظ نقطة النهاية`],"Nothing to save.":[`没有需要保存的内容。`,`لا يوجد ما يُحفظ.`],"Key source":[`密钥来源`,`مصدر المفتاح`],"environment variable":[`环境变量`,`متغير البيئة`],"key file":[`密钥文件`,`ملف المفتاح`],"set in the config file":[`在配置文件中设置`,`مضبوط في ملف الإعدادات`],"resolves in the web app":[`可在网页应用中解析`,`يُقرأ في تطبيق الويب`],"does not resolve in the web app":[`无法在网页应用中解析`,`لا يُقرأ في تطبيق الويب`],"A login web app does not see variables set only in a shell profile; a key file works everywhere.":[`登录时启动的网页应用看不到仅在 shell 配置文件中设置的变量;密钥文件在任何地方都可用。`,`تطبيق الويب الذي يبدأ عند تسجيل الدخول لا يرى المتغيرات المضبوطة في ملف الصدفة فقط؛ ملف المفتاح يعمل في كل مكان.`],"The key does not resolve in this web app. Save it to a key file instead.":[`此网页应用无法解析该密钥。请改为保存到密钥文件。`,`لا يُقرأ المفتاح في تطبيق الويب هذا. احفظه في ملف مفتاح بدلاً من ذلك.`],"Environment variable":[`环境变量`,`متغير البيئة`],"Key file":[`密钥文件`,`ملف المفتاح`],"Save key to a private file":[`将密钥保存到私有文件`,`حفظ المفتاح في ملف خاص`],"Variable name":[`变量名`,`اسم المتغير`],"Key file path":[`密钥文件路径`,`مسار ملف المفتاح`],"Key file name":[`密钥文件名`,`اسم ملف المفتاح`],"The key is written to ~/.config/omms/secrets/ readable only by you. It is never shown again.":[`密钥写入 ~/.config/omms/secrets/,仅你可读,之后不再显示。`,`يُكتب المفتاح في ~/.config/omms/secrets/ ولا يقرؤه غيرك، ولن يُعرض مرة أخرى.`],"Save key source":[`保存密钥来源`,`حفظ مصدر المفتاح`],"Key source saved.":[`密钥来源已保存。`,`تم حفظ مصدر المفتاح.`],"A key file with this name exists. Replace it?":[`已存在同名密钥文件。要替换吗?`,`يوجد ملف مفتاح بهذا الاسم. هل تريد استبداله؟`],"Enter a variable name such as ZAI_API_KEY":[`请输入变量名,例如 ZAI_API_KEY`,`أدخل اسم متغير مثل ZAI_API_KEY`],"Enter the path of a key file":[`请输入密钥文件路径`,`أدخل مسار ملف المفتاح`],"Name the key file with letters, digits, dashes, or underscores":[`密钥文件名只能包含字母、数字、短横线或下划线`,`سمِّ ملف المفتاح بحروف أو أرقام أو شرطات أو شرطات سفلية`],"Paste the key":[`请粘贴密钥`,`الصق المفتاح`],Test:[`测试`,`اختبار`],"Test call succeeded":[`测试调用成功`,`نجح استدعاء الاختبار`],"Test call failed":[`测试调用失败`,`فشل استدعاء الاختبار`],"External API needs":[`外部 API 缺少`,`تحتاج واجهة API الخارجية إلى`],"Automatic import makes model calls. A model change applies at the next run. Turning it off stops a running import after the current exchange.":[`自动导入会调用模型。模型更改在下次运行时生效。关闭后,正在进行的导入会在当前对话完成后停止。`,`يستدعي الاستيراد التلقائي النماذج. يسري تغيير النموذج في التشغيل التالي. إيقافه يوقف الاستيراد الجاري بعد المحادثة الحالية.`],"Saved. Changes apply at the next run.":[`已保存。更改在下次运行时生效。`,`تم الحفظ. تسري التغييرات في التشغيل التالي.`],paused:[`已暂停`,`متوقف مؤقتاً`],"started from auto":[`自动启动`,`بدأ تلقائياً`],"started from web":[`从网页启动`,`بدأ من الويب`],"started from cli":[`从终端启动`,`بدأ من الطرفية`],"started from slash":[`从斜杠命令启动`,`بدأ من أمر الشرطة المائلة`],"Minutes left":[`剩余分钟`,`الدقائق المتبقية`],unknown:[`未知`,`غير معروف`],about:[`约`,`حوالي`],"Run now":[`立即运行`,`تشغيل الآن`],Pause:[`暂停`,`إيقاف مؤقت`],Resume:[`继续`,`استئناف`],"Pausing after the current exchange.":[`将在当前对话完成后暂停。`,`سيتوقف مؤقتاً بعد المحادثة الحالية.`],"Backfill started.":[`回填已开始。`,`بدأ الاستيراد.`],"Directory maps":[`目录映射`,`خرائط المجلدات`],"Map a directory that was moved or deleted to the project it belongs to. A change applies to the next import or backfill run.":[`将已移动或删除的目录映射到其所属项目。更改在下次导入或回填时生效。`,`اربط مجلداً نُقل أو حُذف بالمشروع الذي ينتمي إليه. يسري التغيير في الاستيراد التالي.`],"Saved maps":[`已保存的映射`,`الخرائط المحفوظة`],Keep:[`保留`,`إبقاء`],Remove:[`移除`,`إزالة`],"Unresolved directories":[`无法解析的目录`,`مجلدات غير محلولة`],"No unresolved directories in the latest run.":[`最近一次运行没有无法解析的目录。`,`لا توجد مجلدات غير محلولة في آخر تشغيل.`],"No suggestion found.":[`未找到建议。`,`لم يُعثر على اقتراح.`],"Target directory":[`目标目录`,`المجلد الهدف`],"Use this map":[`使用此映射`,`استخدام هذه الخريطة`],"Save maps":[`保存映射`,`حفظ الخرائط`],"Saved. Maps apply to the next import or backfill run.":[`已保存。映射在下次导入或回填时生效。`,`تم الحفظ. تسري الخرائط في الاستيراد التالي.`],"Running version":[`运行版本`,`الإصدار الجاري`],"Global command":[`全局命令`,`الأمر العام`],"not installed globally":[`未全局安装`,`غير مثبت عمومياً`],"The global command's version differs from the running OMMS. Upgrade it:":[`全局命令的版本与正在运行的 OMMS 不同。请升级:`,`يختلف إصدار الأمر العام عن OMMS الجاري. حدّثه:`],"A global install is optional but recommended: the login item and the terminal commands then run without npx.":[`全局安装是可选的,但建议安装:这样登录项和终端命令无需 npx 即可运行。`,`التثبيت العام اختياري لكنه موصى به: يعمل عنصر تسجيل الدخول وأوامر الطرفية حينها دون npx.`]};function sm(e,t){return t===`en`?e:om[e]?.[t===`zh`?0:1]??e}function cm(){let{language:e}=cr();return t=>sm(t,e)}function lm(e,t){if(e===`env`){let n=(t.name??``).trim();if(!/^[A-Za-z_][A-Za-z0-9_]*$/.test(n))throw Error(`Enter a variable name such as ZAI_API_KEY`);return{source:e,name:n}}if(e===`file`){let n=(t.path??``).trim();if(!n)throw Error(`Enter the path of a key file`);return{source:e,path:n}}let n=(t.name??``).trim()||`memory-api`;if(!/^[A-Za-z0-9_-]{1,64}$/.test(n))throw Error(`Name the key file with letters, digits, dashes, or underscores`);if(!(t.value??``).trim())throw Error(`Paste the key`);return{source:e,name:n,value:t.value??``,replace:t.replace===!0}}function um(e){let t=[],n=e.memoryProvider===`orcarouter`;return!n&&!e.memoryModel&&t.push(`memoryModel`),!n&&!e.memoryApiUrl&&t.push(`memoryApiUrl`),e.keySet||t.push(`memoryApiKey`),t}function dm(e,t){if(t===`inherit`||t===`external`)return{[`${e}Model`]:t};let n=t.indexOf(`/`);if(n<1||n===t.length-1)throw Error(`Enter a model as provider/model.`);return{[`${e}Provider`]:t.slice(0,n),[`${e}Model`]:t.slice(n+1)}}function fm(e){return e.global===null?{kind:`missing`,command:`npm i -g om-memory-system`}:e.global===e.running?{kind:`same`,command:null}:{kind:`different`,command:`npm i -g om-memory-system@latest`}}function pm(e,t,n){let r=new Map;for(let n of e)t.has(n.from)||r.set(n.from,n);for(let e of n){let t=e.target.trim();e.accepted&&t&&r.set(e.directory,{from:e.directory,to:t})}return[...r.values()]}function mm(){let e=cm(),[t,n]=(0,_.useState)(),[r,i]=(0,_.useState)({}),[a,o]=(0,_.useState)(``),[s,c]=(0,_.useState)(!1);(0,_.useEffect)(()=>{let e=!0,t=nm();$(`/api/settings`).then(r=>{e&&t.isCurrent()&&n(r)}).catch(t=>{e&&o(t.message)});for(let t of[`opencode`,`pi`])$(`/api/settings/models?host=${t}`).then(n=>{e&&i(e=>({...e,[t]:n}))}).catch(()=>{e&&i(e=>({...e,[t]:{available:!1}}))});let r=im(t=>{e&&n(t)});return()=>{e=!1,r()}},[]);async function l(n,r){if(!t)return;let i;try{i=dm(n,r)}catch(t){o(e(t.message));return}c(!0);let a,s;try{a=await $(`/api/settings`,{method:`PATCH`,body:JSON.stringify({edits:i,revision:t.revision})})}catch(e){s=e}let l=await rm()!==null;if(c(!1),a){let t=e(a.migratedLegacy?`Saved. OMMS now reads ~/.config/omms/omms.jsonc. The legacy config was kept.`:`Saved. New capture and profile work uses these settings.`);o(l?t:`${t} ${e(`Reload the page before saving again.`)}`)}else o(am(s?.message??``,e(l?`Current settings were reloaded; check the values and save again.`:`The current settings could not be reloaded. Reload the page.`)))}let u=um({memoryProvider:t?.settings.memoryProvider?.globalValue,memoryModel:t?.settings.memoryModel?.globalValue,memoryApiUrl:t?.settings.memoryApiUrl?.globalValue,keySet:!!t?.secrets.memoryApiKey?.set});return(0,V.jsxs)(`section`,{className:`space-y-3 rounded-xl border border-border bg-card p-4`,"aria-label":e(`Models`),children:[(0,V.jsx)(`h2`,{className:`text-lg font-medium`,children:e(`Models`)}),(0,V.jsxs)(`p`,{className:`text-sm text-muted-foreground`,children:[e(`External API fallback`),`: `,t?.fallback.model??e(`none`),` (`,e(t?.fallback.configured?`ready`:`not ready`),`).`]}),[`opencode`,`pi`].map(e=>(0,V.jsx)(hm,{host:e,model:t?.settings[`${e}Model`],provider:t?.settings[`${e}Provider`],effective:e===`pi`?t?.effective.pi.kind:t?.effective.opencode.mode??`unready`,list:r[e],externalMissing:u,busy:s||!t,onSave:t=>l(e,t)},e)),(0,V.jsxs)(`div`,{className:`text-xs text-muted-foreground`,children:[e(`Credentials (values hidden)`),`:`,` `,t&&Object.entries(t.secrets).map(([t,n])=>(0,V.jsxs)(`span`,{className:`me-3`,children:[t,`: `,n.set?`${e(`set`)} (${n.source})`:e(`not set`)]},t))]}),a&&(0,V.jsx)(`p`,{role:`status`,className:`text-sm`,children:a})]})}function hm({host:e,model:t,provider:n,effective:r,list:i,externalMissing:a,busy:o,onSave:s}){let c=cm(),l=t?.globalValue===`inherit`||!t?.globalValue?`inherit`:t.globalValue===`external`?`external`:`${n?.globalValue??``}/${t.globalValue}`,[u,d]=(0,_.useState)(),[f,p]=(0,_.useState)(),[m,h]=(0,_.useState)(),g=u??(l===`inherit`||l===`external`?l:`manual`),v=i?.models??[],y=l===`inherit`||l===`external`?``:l,b=m||y||(v[0]?`${v[0].provider}/${v[0].model}`:`typed`),x=!v.some(e=>`${e.provider}/${e.model}`===b),S=f??y;return(0,V.jsxs)(`div`,{className:`space-y-2 rounded-lg border border-border p-3`,children:[(0,V.jsx)(`h3`,{className:`font-medium`,children:e===`pi`?`Pi`:`OpenCode`}),(0,V.jsxs)(`p`,{className:`text-xs text-muted-foreground`,children:[c(`Effective model`),`:`,` `,t?.value===`external`&&r===`manual`?c(`external API`):t?.value===`inherit`||r===`session`?c(`session`):r===`manual`?c(`external API`):r===`unready`?c(`unavailable`):`${n?.value??``}/${t?.value??``}`]}),(t?.source===`project`||n?.source===`project`)&&(0,V.jsx)(`p`,{className:`text-xs text-amber-600`,children:c(`Project override is active. Changes to the global file may not take effect here.`)}),(0,V.jsx)(`label`,{className:`block text-sm`,htmlFor:`${e}-model`,children:c(`Model choice`)}),(0,V.jsxs)(un,{id:`${e}-model`,className:`w-full rounded-lg border border-border bg-background p-2 text-sm`,value:g,onChange:e=>d(e.target.value),children:[(0,V.jsx)(`option`,{value:`inherit`,children:c(`Session model`)}),(0,V.jsx)(`option`,{value:`manual`,children:c(`Manual model`)}),(0,V.jsx)(`option`,{value:`external`,disabled:a.length>0,children:c(`External API`)})]}),a.length>0&&(0,V.jsxs)(`p`,{className:`text-xs text-muted-foreground`,children:[c(`External API needs`),`: `,a.join(`, `)]}),g===`manual`&&i?.available&&v.length>0&&(0,V.jsxs)(un,{className:`w-full rounded-lg border border-border bg-background p-2 text-sm`,"aria-label":`${e===`pi`?`Pi`:`OpenCode`} ${c(`Model`)}`,value:x?`typed`:b,onChange:e=>h(e.target.value),children:[v.map(e=>(0,V.jsxs)(`option`,{value:`${e.provider}/${e.model}`,children:[e.name,` (`,e.provider,`/`,e.model,`)`]},`${e.provider}/${e.model}`)),(0,V.jsx)(`option`,{value:`typed`,children:c(`Manual provider/model`)})]}),g===`manual`&&x&&(0,V.jsx)(`input`,{className:`w-full rounded-lg border border-border bg-background p-2 text-sm`,"aria-label":`${e} ${c(`Manual provider/model`)}`,placeholder:`provider/model`,value:S,onChange:e=>{p(e.target.value),h(`typed`)}}),g===`manual`&&i&&!i.available&&(0,V.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:c(`Model list unavailable. Enter provider/model manually.`)}),(0,V.jsx)(`button`,{type:`button`,className:`rounded-lg border border-border px-3 py-1.5 text-sm`,disabled:o,onClick:()=>s(g===`manual`?x?S.trim():b:g),children:c(`Save model`)})]})}var gm=[`openai-chat`,`openai-responses`,`anthropic`,`minimax`,`orcarouter`,`google-gemini`];function _m(){let e=cm(),[t,n]=(0,_.useState)(),[r,i]=(0,_.useState)({}),[a,o]=(0,_.useState)(`file`),[s,c]=(0,_.useState)({name:``,path:``,value:``}),[l,u]=(0,_.useState)(``),[d,f]=(0,_.useState)(!1);(0,_.useEffect)(()=>{let e=!0,t=nm();$(`/api/settings`).then(r=>{e&&t.isCurrent()&&n(r)}).catch(t=>{e&&u(t.message)});let r=im(t=>{e&&n(t)});return()=>{e=!1,r()}},[]);let p=e=>String(t?.settings[e]?.globalValue??``),m=e=>r[e]??p(e);async function h(){if(!t)return;let n={};for(let e of[`memoryProvider`,`memoryApiUrl`,`memoryModel`]){let t=m(e).trim();t&&t!==p(e)&&(n[e]=t)}if(!Object.keys(n).length){u(e(`Nothing to save.`));return}f(!0);try{await $(`/api/settings`,{method:`PATCH`,body:JSON.stringify({edits:n,revision:t.revision})}),i({}),u(e(`Saved. New capture and profile work uses these settings.`))}catch(e){u(e.message)}await rm(),f(!1)}async function g(n=!1){if(!t)return;let r;try{r=lm(a,{...s,replace:n})}catch(t){u(e(t.message));return}f(!0);try{await $(`/api/settings/external-api/key`,{method:`POST`,body:JSON.stringify({...r,revision:t.revision})}),c(e=>({...e,value:``})),u(e(`Key source saved.`))}catch(t){let r=t.message;if(a===`paste`&&!n&&r.includes(`Confirm to replace`)){f(!1),window.confirm(e(`A key file with this name exists. Replace it?`))&&await g(!0);return}u(r)}await rm(),f(!1)}async function v(){f(!0);try{let t=await $(`/api/settings/external-api/test`,{method:`POST`,body:`{}`});u(t.ok?`${e(`Test call succeeded`)}: ${t.model??``}`:`${e(`Test call failed`)}: ${t.error??``}`)}catch(e){u(e.message)}f(!1)}let y=t?.externalKey;return(0,V.jsxs)(`section`,{className:`space-y-3 rounded-xl border border-border bg-card p-4`,"aria-label":e(`External API`),children:[(0,V.jsx)(`h2`,{className:`text-lg font-medium`,children:e(`External API`)}),(0,V.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:e(`An OpenAI- or Anthropic-compatible endpoint that either host can use for capture and backfill.`)}),(0,V.jsxs)(`label`,{className:`block text-sm`,children:[e(`Provider`),(0,V.jsx)(un,{"aria-label":e(`Provider`),className:`mt-1 block w-full rounded border border-border bg-background p-2`,value:m(`memoryProvider`)||`openai-chat`,onChange:e=>i({...r,memoryProvider:e.target.value}),children:gm.map(e=>(0,V.jsx)(`option`,{value:e,children:e},e))})]}),(0,V.jsxs)(`label`,{className:`block text-sm`,children:[e(`API URL`),(0,V.jsx)(`input`,{className:`mt-1 block w-full rounded border border-border bg-background p-2`,placeholder:`https://api.example.com/v1`,value:m(`memoryApiUrl`),onChange:e=>i({...r,memoryApiUrl:e.target.value})})]}),(0,V.jsxs)(`label`,{className:`block text-sm`,children:[e(`Model`),(0,V.jsx)(`input`,{className:`mt-1 block w-full rounded border border-border bg-background p-2`,placeholder:`glm-5-turbo`,value:m(`memoryModel`),onChange:e=>i({...r,memoryModel:e.target.value})})]}),(0,V.jsx)(`button`,{type:`button`,className:`rounded border border-border px-3 py-1.5 text-sm`,disabled:d||!t,onClick:()=>void h(),children:e(`Save endpoint`)}),(0,V.jsxs)(`fieldset`,{className:`space-y-2 rounded-lg border border-border p-3 text-sm`,children:[(0,V.jsx)(`legend`,{className:`px-1 font-medium`,children:e(`API key`)}),(0,V.jsxs)(`p`,{className:`text-xs text-muted-foreground`,children:[e(`Key source`),`:`,` `,y?.source===`env`?`${e(`environment variable`)} ${y.reference}`:y?.source===`file`?`${e(`key file`)} ${y.reference}`:y?.source===`literal`?e(`set in the config file`):e(`not set`),y?.source&&` · ${e(y.resolvesInWebApp?`resolves in the web app`:`does not resolve in the web app`)}`]}),y?.source===`env`&&(0,V.jsx)(`p`,{className:`text-xs text-amber-600`,children:e(`A login web app does not see variables set only in a shell profile; a key file works everywhere.`)}),y?.source&&!y.resolvesInWebApp&&(0,V.jsx)(`p`,{className:`text-xs text-amber-600`,children:e(`The key does not resolve in this web app. Save it to a key file instead.`)}),[[`env`,`Environment variable`],[`file`,`Key file`],[`paste`,`Save key to a private file`]].map(([t,n])=>(0,V.jsxs)(`label`,{className:`flex items-center gap-2`,children:[(0,V.jsx)(`input`,{type:`radio`,name:`omms-key-source`,checked:a===t,onChange:()=>o(t)}),e(n)]},t)),a===`env`&&(0,V.jsx)(`input`,{"aria-label":e(`Variable name`),className:`w-full rounded border border-border bg-background p-2`,placeholder:`ZAI_API_KEY`,value:s.name,onChange:e=>c({...s,name:e.target.value})}),a===`file`&&(0,V.jsx)(`input`,{"aria-label":e(`Key file path`),className:`w-full rounded border border-border bg-background p-2`,placeholder:`~/.config/omms/secrets/zai.key`,value:s.path,onChange:e=>c({...s,path:e.target.value})}),a===`paste`&&(0,V.jsxs)(V.Fragment,{children:[(0,V.jsx)(`input`,{"aria-label":e(`Key file name`),className:`w-full rounded border border-border bg-background p-2`,placeholder:`memory-api`,value:s.name,onChange:e=>c({...s,name:e.target.value})}),(0,V.jsx)(`input`,{"aria-label":e(`API key`),type:`password`,autoComplete:`off`,className:`w-full rounded border border-border bg-background p-2`,value:s.value,onChange:e=>c({...s,value:e.target.value})}),(0,V.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:e(`The key is written to ~/.config/omms/secrets/ readable only by you. It is never shown again.`)})]}),(0,V.jsx)(`button`,{type:`button`,className:`rounded border border-border px-3 py-1.5`,disabled:d||!t,onClick:()=>void g(),children:e(`Save key source`)})]}),(0,V.jsx)(`button`,{type:`button`,className:`rounded border border-border px-3 py-1.5 text-sm`,disabled:d,onClick:()=>void v(),children:e(`Test`)}),l&&(0,V.jsx)(`p`,{role:`status`,className:`text-sm`,children:l})]})}function vm(){let e=cm(),[t,n]=(0,_.useState)(),[r,i]=(0,_.useState)(),[a,o]=(0,_.useState)(new Set),[s,c]=(0,_.useState)({}),[l,u]=(0,_.useState)(``),[d,f]=(0,_.useState)(!1),p=()=>$(`/api/settings/import-maps`).then(e=>{i(e),o(new Set),c({})}).catch(e=>u(e.message));(0,_.useEffect)(()=>{let e=!0;$(`/api/settings`).then(t=>{e&&n(t)}).catch(t=>{e&&u(t.message)}),p();let t=im(t=>{e&&n(t)});return()=>{e=!1,t()}},[]);function m(e,t){c(n=>{let r=n[e.directory]??{directory:e.directory,target:e.suggestion??``,accepted:!1};return{...n,[e.directory]:{...r,...t}}})}async function h(){if(!t||!r)return;let n=pm(r.saved,a,Object.values(s));f(!0);try{await $(`/api/settings`,{method:`PATCH`,body:JSON.stringify({edits:{importPathMaps:n},revision:t.revision})}),u(e(`Saved. Maps apply to the next import or backfill run.`))}catch(e){u(e.message)}await rm(),await p(),f(!1)}return(0,V.jsxs)(`section`,{id:`directory-maps`,className:`space-y-3 rounded-xl border border-border bg-card p-4`,"aria-label":e(`Directory maps`),children:[(0,V.jsx)(`h2`,{className:`text-lg font-medium`,children:e(`Directory maps`)}),(0,V.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:e(`Map a directory that was moved or deleted to the project it belongs to. A change applies to the next import or backfill run.`)}),(0,V.jsx)(`h3`,{className:`font-medium`,children:e(`Saved maps`)}),!r?.saved.length&&(0,V.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:e(`none`)}),(0,V.jsx)(`ul`,{className:`space-y-1 text-sm`,children:r?.saved.map(t=>(0,V.jsxs)(`li`,{className:`flex flex-wrap items-center gap-2`,children:[(0,V.jsxs)(`span`,{className:a.has(t.from)?`line-through`:``,children:[(0,V.jsx)(`code`,{children:t.from}),` → `,(0,V.jsx)(`code`,{children:t.to})]}),(0,V.jsx)(`button`,{type:`button`,className:`rounded border border-border px-2 py-0.5 text-xs`,onClick:()=>o(e=>{let n=new Set(e);return n.has(t.from)?n.delete(t.from):n.add(t.from),n}),children:a.has(t.from)?e(`Keep`):e(`Remove`)})]},t.from))}),[`pi`,`opencode`].map(t=>(0,V.jsxs)(`div`,{className:`space-y-2`,children:[(0,V.jsxs)(`h3`,{className:`font-medium`,children:[t===`pi`?`Pi`:`OpenCode`,`: `,e(`Unresolved directories`)]}),!r?.[t].length&&(0,V.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:e(`No unresolved directories in the latest run.`)}),r?.[t].map(t=>{let n=s[t.directory],r=n?.target??t.suggestion??``;return(0,V.jsxs)(`div`,{className:`space-y-1 rounded-lg border border-border p-2 text-sm`,children:[(0,V.jsxs)(`p`,{children:[(0,V.jsx)(`code`,{children:t.directory}),` · `,t.sessions,` `,e(`sessions`)]}),!t.suggestion&&(0,V.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:e(`No suggestion found.`)}),(0,V.jsx)(`input`,{"aria-label":`${t.directory} ${e(`Target directory`)}`,className:`w-full rounded border border-border bg-background p-2`,placeholder:e(`Target directory`),value:r,onChange:e=>m(t,{target:e.target.value})}),(0,V.jsxs)(`label`,{className:`flex items-center gap-2`,children:[(0,V.jsx)(`input`,{type:`checkbox`,checked:n?.accepted??!1,onChange:e=>m(t,{accepted:e.target.checked})}),e(`Use this map`)]})]},t.directory)})]},t)),(0,V.jsx)(`button`,{type:`button`,className:`rounded border border-border px-3 py-1.5 text-sm`,disabled:d||!t||!r,onClick:()=>void h(),children:e(`Save maps`)}),l&&(0,V.jsx)(`p`,{role:`status`,className:`text-sm`,children:l})]})}var ym=`overflow-x-auto rounded-lg border border-border`,bm=`px-3 py-2 text-start font-medium`,xm=`bg-muted/50 text-xs text-muted-foreground`,Sm=`px-3 py-2 text-start font-medium`,Cm=`border-t border-border transition-colors hover:bg-muted/30`,wm=`px-3 py-1.5`;function Tm(){let e=cm(),[t,n]=(0,_.useState)(7),[r,i]=(0,_.useState)(),[a,o]=(0,_.useState)([]),[s,c]=(0,_.useState)(),[l,u]=(0,_.useState)(``),[d,f]=(0,_.useState)(``),p=(0,_.useCallback)(async e=>{let t=nm();try{let[n,r,a]=await Promise.all([$(`/api/settings/diagnostics?days=${e}`),$(`/api/settings/traces`),$(`/api/settings`)]);i(n),o(r.traces),t.isCurrent()&&c(a),f(``)}catch(e){f(e.message)}},[]);(0,_.useEffect)(()=>{p(t)},[t,p]),(0,_.useEffect)(()=>im(e=>c(e)),[]);async function m(n){if(!s)return;let r;try{await $(`/api/settings`,{method:`PATCH`,body:JSON.stringify({revision:s.revision,edits:n})})}catch(e){r=e}let i=await rm()!==null;if(!r){await p(t),i||f(e(`Reload the page before saving again.`));return}f(am(r.message,e(i?`Current settings were reloaded; check the values and save again.`:`The current settings could not be reloaded. Reload the page.`)))}async function h(e){try{let t=await $(`/api/settings/traces/${encodeURIComponent(e)}`);u(t.content)}catch(e){f(e.message)}}async function g(n){if(window.confirm(`${e(`Delete`)} ${n}?`))try{await $(`/api/settings/traces/${encodeURIComponent(n)}`,{method:`DELETE`}),u(``),await p(t)}catch(e){f(e.message)}}let v=s?.settings.captureTrace;return(0,V.jsxs)(`section`,{className:`space-y-4 rounded-xl border border-border bg-card p-4`,"aria-label":e(`Capture diagnostics`),children:[(0,V.jsx)(`h2`,{className:`text-lg font-medium`,children:e(`Capture diagnostics`)}),(0,V.jsxs)(`label`,{className:`flex items-center gap-2 pb-2 text-sm`,children:[e(`Time range`),(0,V.jsxs)(un,{"aria-label":e(`Time range`),className:`rounded-lg border border-border bg-background px-2 py-1`,value:t,onChange:e=>n(Number(e.target.value)),children:[(0,V.jsx)(`option`,{value:1,children:e(`24 hours`)}),(0,V.jsx)(`option`,{value:7,children:e(`7 days`)}),(0,V.jsx)(`option`,{value:30,children:e(`30 days`)}),(0,V.jsx)(`option`,{value:90,children:e(`90 days`)})]})]}),d&&(0,V.jsx)(`p`,{role:`alert`,children:d}),(0,V.jsx)(`div`,{className:ym,children:(0,V.jsxs)(`table`,{className:`w-full text-sm`,children:[(0,V.jsx)(`caption`,{className:bm,children:e(`Outcomes by model`)}),(0,V.jsx)(`thead`,{className:xm,children:(0,V.jsx)(`tr`,{children:[`Host / model`,`Saved`,`Skipped`,`Failed`,`Total`].map((t,n)=>(0,V.jsx)(`th`,{scope:`col`,className:B(Sm,n>0&&`text-end`),children:e(t)},t))})}),(0,V.jsx)(`tbody`,{children:r?.byModel.map((e,t)=>(0,V.jsxs)(`tr`,{className:Cm,children:[(0,V.jsxs)(`td`,{className:wm,children:[(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:e.host}),` `,(0,V.jsx)(`span`,{className:`font-mono text-xs`,children:e.provider||e.model?`${e.provider??``}/${e.model??``}`:`—`})]}),[e.saved,e.skipped,e.failed].map((t,n)=>(0,V.jsxs)(`td`,{className:B(wm,`text-end tabular-nums`),children:[t,` `,(0,V.jsxs)(`span`,{className:`text-xs text-muted-foreground`,children:[Math.round(100*t/e.total),`%`]})]},n)),(0,V.jsx)(`td`,{className:B(wm,`text-end font-medium tabular-nums`),children:e.total})]},`${e.host}-${e.provider}-${e.model}-${t}`))})]})}),(0,V.jsx)(`div`,{className:ym,children:(0,V.jsxs)(`table`,{className:`w-full text-sm`,children:[(0,V.jsx)(`caption`,{className:bm,children:e(`Failure reasons`)}),(0,V.jsx)(`thead`,{className:xm,children:(0,V.jsxs)(`tr`,{children:[(0,V.jsx)(`th`,{scope:`col`,className:Sm,children:e(`Host`)}),(0,V.jsx)(`th`,{scope:`col`,className:Sm,children:e(`Reason`)}),(0,V.jsx)(`th`,{scope:`col`,className:B(Sm,`text-end`),children:e(`Total`)})]})}),(0,V.jsx)(`tbody`,{children:r?.byReason.map((e,t)=>(0,V.jsxs)(`tr`,{className:Cm,children:[(0,V.jsx)(`td`,{className:B(wm,`text-muted-foreground`),children:e.host}),(0,V.jsx)(`td`,{className:B(wm,`font-mono text-xs`),children:e.reason}),(0,V.jsx)(`td`,{className:B(wm,`text-end tabular-nums`),children:e.count})]},`${e.host}-${e.reason}-${t}`))})]})}),(0,V.jsx)(`div`,{className:B(ym,`max-h-72 overflow-y-auto`),children:(0,V.jsxs)(`table`,{className:`w-full min-w-max text-sm`,children:[(0,V.jsx)(`caption`,{className:bm,children:e(`Recent attempts`)}),(0,V.jsx)(`thead`,{className:B(xm,`sticky top-0`),children:(0,V.jsx)(`tr`,{children:[`Time`,`Host`,`Source`,`Session`,`Path`,`Model`,`Stop`,`Blocks`,`Prompt`,`Reply`,`Duration`,`Outcome`,`Reason`].map(t=>(0,V.jsx)(`th`,{scope:`col`,className:Sm,children:e(t)},t))})}),(0,V.jsx)(`tbody`,{children:r?.recent.map((e,t)=>(0,V.jsx)(`tr`,{className:Cm,children:[new Date(e.timestamp).toLocaleString(),e.host,e.sourceType,e.sessionId,e.path,`${e.provider??``}/${e.model??``}`,e.stopReason,e.blockTypes?.join(`, `),e.promptChars,e.replyChars,e.durationMs,e.outcome,e.reason].map((e,t)=>(0,V.jsx)(`td`,{className:B(wm,`whitespace-nowrap`),children:e??``},t))},`${e.timestamp}-${t}`))})]})}),(0,V.jsxs)(`div`,{className:`space-y-2 border-t border-border pt-3 text-sm`,children:[(0,V.jsxs)(`label`,{className:`flex items-center gap-2`,children:[(0,V.jsx)(`input`,{type:`checkbox`,checked:v?.value===!0,disabled:!s,onChange:t=>{t.target.checked&&!window.confirm(e(`Traces can contain conversation content after redaction. Keep tracing off unless you need it for debugging.`))||m({captureTrace:t.target.checked})}}),` `,e(`Save capture traces`)]}),(0,V.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:e(`Traces can contain conversation content after redaction. Keep tracing off unless you need it for debugging.`)}),v?.source===`project`&&(0,V.jsx)(`p`,{className:`text-xs text-amber-600`,children:e(`This project turns tracing off. The global switch cannot turn it on here.`)}),[`captureTraceRetentionDays`,`captureAttemptRetentionDays`].map(t=>(0,V.jsxs)(`label`,{className:`block`,children:[e(t===`captureTraceRetentionDays`?`Trace retention (days)`:`Attempt retention (days)`),` `,(0,V.jsx)(`input`,{type:`number`,min:1,className:`ms-2 w-20 rounded border border-border bg-background p-1`,defaultValue:Number(s?.settings[t]?.value??30),onBlur:e=>{let n=Number(e.target.value);Number.isSafeInteger(n)&&n>0&&n!==s?.settings[t]?.value&&m({[t]:n})}},`${t}-${s?.settings[t]?.value}`)]},t)),(0,V.jsx)(`h3`,{className:`font-medium`,children:e(`Trace files`)}),a.map(t=>(0,V.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,V.jsxs)(`span`,{children:[t.file,` (`,t.size,` bytes)`]}),(0,V.jsx)(`button`,{type:`button`,onClick:()=>void h(t.file),children:e(`View`)}),(0,V.jsx)(`button`,{type:`button`,onClick:()=>void g(t.file),children:e(`Delete`)})]},t.file)),l&&(0,V.jsx)(`pre`,{className:`max-h-60 overflow-auto whitespace-pre-wrap break-words rounded bg-background p-2 text-xs`,children:l})]})]})}function Em(){let e=cm(),[t,n]=(0,_.useState)([]),[r,i]=(0,_.useState)(!1),[a,o]=(0,_.useState)(``);async function s(e){i(!0);try{let t=await $(`/api/settings/health`,{method:`POST`,body:JSON.stringify({testModels:e})});n(t.checks),o(``)}catch(e){o(e.message)}finally{i(!1)}}return(0,V.jsxs)(`section`,{id:`settings-health`,className:`space-y-3 rounded-xl border border-border bg-card p-4`,"aria-label":e(`Health`),children:[(0,V.jsx)(`h2`,{className:`text-lg font-medium`,children:e(`Health`)}),(0,V.jsxs)(`div`,{className:`flex gap-2`,children:[(0,V.jsx)(`button`,{className:`rounded-lg border border-border px-3 py-1.5 text-sm`,disabled:r,onClick:()=>void s(!1),children:e(`Run checks`)}),(0,V.jsx)(`button`,{className:`rounded-lg border border-border px-3 py-1.5 text-sm`,disabled:r,onClick:()=>void s(!0),children:e(`Run checks and test models`)})]}),r&&(0,V.jsx)(`p`,{role:`status`,children:e(`Checking…`)}),a&&(0,V.jsx)(`p`,{role:`alert`,children:a}),(0,V.jsx)(`ul`,{className:`space-y-2 text-sm`,children:t.map(t=>(0,V.jsxs)(`li`,{className:`rounded-lg border border-border p-2`,children:[(0,V.jsx)(`strong`,{className:t.status===`fail`?`text-red-600`:t.status===`warn`?`text-amber-600`:`text-green-600`,children:e(t.status).toUpperCase()}),` `,`· `,e(t.check),`: `,t.reason]},t.check))})]})}function Dm(e){let t=/^(\d{4})-(\d{2})-(\d{2})$/.exec(e);return t?[Number(t[1]),Number(t[2])-1,Number(t[3])]:null}function Om(e){let t=Dm(e);return t?new Date(t[0],t[1],t[2],0,0,0,0).getTime():void 0}function km(e){let t=Dm(e);return t?new Date(t[0],t[1],t[2],23,59,59,999).getTime():void 0}function Am(e,t,n){let r=!n&&e!==null&&e.revision===t.revision;return{page:r?{...t,listedAt:e.listedAt}:t,sameListing:r}}function jm(e){let t=cm(),[n,r]=(0,_.useState)(``),[i,a]=(0,_.useState)(null),[o,s]=(0,_.useState)(null),[c,l]=(0,_.useState)(!1),[u,d]=(0,_.useState)(``),f=`rounded border border-border px-3 py-1.5 text-sm disabled:opacity-50`;async function p(t){try{let n=await $(`/api/settings/imports/sources/validate`,{method:`POST`,body:JSON.stringify({host:e.host,path:t})});a(n),r(n.displayPath),e.onChoose(n),d(``)}catch(e){d(e.message)}}async function m(t){try{let n=await $(`/api/settings/imports/sources/browse`,{method:`POST`,body:JSON.stringify({host:e.host,...t?{path:t}:{}})});s(n),d(``)}catch(e){let t=e.message;/loopback/i.test(t)&&l(!0),d(t)}}function h(){a(null),r(``),s(null),e.onChoose(null)}return(0,V.jsxs)(`div`,{className:`space-y-2 text-sm`,children:[(0,V.jsxs)(`label`,{className:`block`,children:[e.host===`pi`?t(`Pi sessions folder or one .jsonl session file`):t(`OpenCode database file`),(0,V.jsx)(`input`,{className:`mt-1 block w-full rounded border border-border bg-background p-2 font-mono`,value:n,placeholder:t(`Absolute path, for example on a mounted volume`),onChange:e=>r(e.target.value)})]}),(0,V.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[(0,V.jsx)(`button`,{type:`button`,className:f,disabled:!n.trim(),onClick:()=>void p(n.trim()),children:t(`Use this source`)}),!c&&(0,V.jsx)(`button`,{type:`button`,className:f,onClick:()=>void m(o?.path),children:t(`Browse`)}),i&&(0,V.jsx)(`button`,{type:`button`,className:f,onClick:h,children:t(`Use the default location`)})]}),i&&(0,V.jsxs)(`p`,{role:`status`,children:[t(`Chosen source`),`: `,(0,V.jsx)(`span`,{className:`font-mono`,children:i.displayPath})]}),u&&(0,V.jsx)(`p`,{role:`alert`,children:u}),o&&(0,V.jsxs)(`div`,{className:`rounded border border-border p-2`,children:[(0,V.jsx)(`p`,{className:`font-mono text-xs break-all`,children:o.path}),(0,V.jsxs)(`ul`,{className:`max-h-60 overflow-auto`,children:[o.parent&&(0,V.jsx)(`li`,{children:(0,V.jsx)(`button`,{type:`button`,className:`underline`,onClick:()=>void m(o.parent),children:`..`})}),o.entries.map(n=>(0,V.jsxs)(`li`,{className:`flex items-center gap-2`,children:[n.kind===`folder`?(0,V.jsxs)(`button`,{type:`button`,className:`underline`,onClick:()=>void m(n.path),children:[n.name,`/`]}):(0,V.jsx)(`span`,{className:`font-mono text-xs`,children:n.name}),(n.kind!==`folder`||e.host===`pi`)&&(0,V.jsx)(`button`,{type:`button`,className:`text-xs underline`,onClick:()=>void p(n.path),children:t(`Choose`)})]},n.path))]}),o.truncated&&(0,V.jsx)(`p`,{className:`text-xs`,children:t(`Only the first 500 entries are shown.`)})]})]})}var Mm=50,Nm=1e3,Pm={"missing-model":`The external API has no memoryModel in the global config`,"missing-url":`The external API has no memoryApiUrl in the global config`,"missing-key":`The external API key is missing in the OpenCode process; check memoryApiKey and its env:// or file:// source`,"unsupported-provider":`The memoryProvider in the global config is not supported for imports`},Fm=()=>({mode:`ids`,picked:new Map});function Im(e,t){return e.split(`
123
- `).map(e=>e.trim()).filter(Boolean).map(e=>{let n=e.indexOf(`=`);if(n<1||n===e.length-1)throw Error(t);return{from:e.slice(0,n),to:e.slice(n+1)}})}function Lm(){let e=cm(),[t,n]=(0,_.useState)(`pi`),[r,i]=(0,_.useState)(null),[a,o]=(0,_.useState)(`current-project`),[s,c]=(0,_.useState)(``),[l,u]=(0,_.useState)(``),[d,f]=(0,_.useState)(``),[p,m]=(0,_.useState)(``),[h,g]=(0,_.useState)(``),[v,y]=(0,_.useState)(!1),[b,x]=(0,_.useState)(!1),[S,C]=(0,_.useState)(!1),[w,T]=(0,_.useState)(!1),[E,D]=(0,_.useState)(null),[O,ee]=(0,_.useState)(``),[te,ne]=(0,_.useState)(!1),[re,ie]=(0,_.useState)(!1),[k,ae]=(0,_.useState)(Fm),[A,j]=(0,_.useState)(null),[oe,M]=(0,_.useState)(``),[se,ce]=(0,_.useState)(null),[N,P]=(0,_.useState)(``);(0,_.useEffect)(()=>{$(`/api/settings/imports/readiness`).then(e=>{j(e);let t=e.opencode.models[0];M(t?`${t.provider}/${t.model}`:e.external.state===`ready`?`external`:``)}).catch(()=>j(null)),$(`/api/settings/imports/current`).then(e=>ce(e.job)).catch(()=>{})},[]),(0,_.useEffect)(()=>{if(se?.state!==`running`&&se?.state!==`cancelling`)return;let e=setInterval(()=>{$(`/api/settings/imports/current`).then(e=>ce(e.job)).catch(e=>P(e.message))},1e3);return()=>clearInterval(e)},[se?.state]);let le=JSON.stringify([t,r?.sourceToken,a,s,l]),ue=!!E&&(te||le!==O);function de(){let n=Im(l,e(`Directory maps must be old=new, one per line`));return{host:t,...r?{source:r.sourceToken}:{},scope:a,...a===`current-project`&&s?{project:s}:{},pathMaps:n}}async function fe(e,t){ie(!0);try{let n=await $(`/api/settings/imports/sessions`,{method:`POST`,body:JSON.stringify({...de(),offset:e,limit:Mm,refresh:t})}),r=Am(E,n,t);r.sameListing||ae(Fm()),D(r.page),ee(le),ne(!1),P(``)}catch(e){P(e.message)}finally{ie(!1)}}let pe=E?E.total-(a===`all-projects`?E.unresolvedCount:0):0,F=k.mode===`all`?pe-k.excluded.size:k.picked.size,me=e=>k.mode===`all`?!k.excluded.has(e.key):k.picked.has(e.key);function he(e){if(!(!e.selectable||!e.directory)){if(k.mode===`all`){let t=new Set(k.excluded);t.has(e.key)?t.delete(e.key):t.add(e.key),ae({mode:`all`,excluded:t})}else{let t=new Map(k.picked);t.has(e.key)?t.delete(e.key):t.set(e.key,e.directory),ae({mode:`ids`,picked:t})}}}let ge=!b||!S,_e=t===`pi`&&A?.piReader.available===!1,ve=ge?A?oe?oe===`external`&&A.external.state!==`ready`?e(Pm[A.external.state]??`The external API is not ready`):null:e(`No import model is ready. Connect a model in OpenCode or complete the external API settings.`):e(`Model readiness is unknown. Reload the page.`):null,ye=E?ue?e(`Options changed. Refresh the session list first.`):F===0?e(`Select at least one session.`):k.mode===`ids`&&k.picked.size>Nm?e(`Select at most 1000 sessions one by one, or use Select all matching.`):null:e(`List sessions first.`),be=se?.state===`running`||se?.state===`cancelling`;async function xe(n){if(E)try{let r=d?Om(d):void 0,i=p?km(p):void 0,o={host:t,source:E.source.sourceToken,selection:k.mode===`all`?{mode:`all`,excludedKeys:[...k.excluded],revision:E.revision,listedAt:E.listedAt}:{mode:`ids`,sessions:[...k.picked].map(([e,t])=>({key:e,directory:t})),listedAt:E.listedAt},options:{dryRun:n,force:v,skipMemories:b,skipProfile:S,scope:a,...a===`current-project`&&s?{project:s}:{},...r===void 0?{}:{since:r},...i===void 0?{}:{until:i},...h?{profileBatch:Number(h)}:{},pathMaps:Im(l,e(`Directory maps must be old=new, one per line`))},...oe?{modelChoice:oe}:{}},c=await $(`/api/settings/imports`,{method:`POST`,body:JSON.stringify(o)});ce(c),P(``)}catch(e){let t=e.message;/out of date|changed|no longer/i.test(t)&&ne(!0),P(t)}}async function Se(){try{let e=await $(`/api/settings/imports/current/cancel`,{method:`POST`,body:`{}`});ce(e.job)}catch(e){P(e.message)}}function Ce(){o(`all-projects`),T(!0)}let we={recorded:e(`recorded`),mapped:e(`mapped`),worktree:e(`project root`),unresolved:e(`missing`)},I=`mt-1 block w-full rounded border border-border bg-background p-2`,Te=`rounded border border-border px-3 py-1.5 text-sm disabled:opacity-50`;return(0,V.jsxs)(`section`,{className:`space-y-3 rounded-xl border border-border bg-card p-4`,"aria-label":e(`Import and backfill`),children:[(0,V.jsx)(`h2`,{className:`text-lg font-medium`,children:e(`Import and backfill`)}),(0,V.jsxs)(`div`,{className:`flex flex-wrap items-end gap-3`,children:[(0,V.jsxs)(`label`,{className:`text-sm`,children:[e(`History host`),(0,V.jsxs)(un,{"aria-label":e(`History host`),className:I,value:t,onChange:e=>{n(e.target.value),i(null),D(null),ae(Fm())},children:[(0,V.jsx)(`option`,{value:`pi`,children:`Pi`}),(0,V.jsx)(`option`,{value:`opencode`,children:`OpenCode`})]})]}),(0,V.jsx)(`button`,{type:`button`,className:Te,disabled:re,onClick:()=>void fe(0,!0),children:e(E?`Refresh list`:`List sessions`)}),(0,V.jsx)(`button`,{type:`button`,className:Te,onClick:()=>T(!w),children:e(w?`Hide advanced options`:`Advanced options`)})]}),(0,V.jsxs)(`p`,{className:`text-xs text-muted-foreground`,children:[e(`Source`),`: `,r?.displayPath??E?.source.displayPath??e(`default location`),` · `,e(a===`current-project`?`Current project`:`All projects`)]}),_e&&(0,V.jsx)(`p`,{role:`alert`,children:A?.piReader.reason}),w&&(0,V.jsxs)(`div`,{className:`space-y-3 rounded-lg border border-border p-3`,children:[(0,V.jsx)(jm,{host:t,onChoose:i}),(0,V.jsxs)(`div`,{className:`grid gap-3 sm:grid-cols-2`,children:[(0,V.jsxs)(`label`,{className:`text-sm`,children:[e(`Scope`),(0,V.jsxs)(un,{"aria-label":e(`Scope`),className:I,value:a,onChange:e=>o(e.target.value),children:[(0,V.jsx)(`option`,{value:`current-project`,children:e(`Current project`)}),(0,V.jsx)(`option`,{value:`all-projects`,children:e(`All projects`)})]})]}),a===`current-project`&&(0,V.jsxs)(`label`,{className:`text-sm`,children:[e(`Project directory`),(0,V.jsx)(`input`,{className:I,value:s,onChange:e=>c(e.target.value),placeholder:e(`Current directory`)})]}),(0,V.jsxs)(`label`,{className:`text-sm`,children:[e(`Prompt date from`),(0,V.jsx)(`input`,{type:`date`,className:I,value:d,onChange:e=>f(e.target.value)})]}),(0,V.jsxs)(`label`,{className:`text-sm`,children:[e(`Prompt date to`),(0,V.jsx)(`input`,{type:`date`,className:I,value:p,onChange:e=>m(e.target.value)})]}),(0,V.jsxs)(`label`,{className:`text-sm`,children:[e(`Profile batch size`),(0,V.jsx)(`input`,{inputMode:`numeric`,className:I,value:h,onChange:e=>g(e.target.value)})]})]}),(0,V.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:e(`Prompt dates are inclusive, use your time zone, and filter the turns inside each session, not the list. Empty dates include every turn.`)}),(0,V.jsxs)(`label`,{className:`block text-sm`,children:[e(`Directory maps (old=new, one per line)`),(0,V.jsx)(`textarea`,{className:I,rows:2,value:l,onChange:e=>u(e.target.value)})]}),(0,V.jsx)(`div`,{className:`flex flex-wrap gap-3 text-sm`,children:[[`Force reimport`,v,y],[`Skip memories`,b,x],[`Skip profile`,S,C]].map(([t,n,r])=>(0,V.jsxs)(`label`,{children:[(0,V.jsx)(`input`,{type:`checkbox`,checked:n,onChange:e=>r(e.target.checked)}),` `,e(t)]},t))})]}),ue&&E&&(0,V.jsx)(`p`,{role:`status`,className:`text-sm text-amber-600`,children:e(`Options changed. Refresh the session list first.`)}),E&&a===`current-project`&&E.unresolvedCount>0&&(0,V.jsxs)(`p`,{className:`text-sm`,children:[e(`Sessions with directories that no longer exist`),`: `,E.unresolvedCount,`.`,` `,(0,V.jsx)(`button`,{type:`button`,className:`underline`,onClick:Ce,children:e(`Show them and add a directory map`)})]}),E&&(0,V.jsxs)(`div`,{className:`space-y-2`,children:[(0,V.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2 text-sm`,children:[(0,V.jsxs)(`span`,{role:`status`,children:[e(`Selected`),`: `,F,` / `,pe]}),(0,V.jsxs)(`button`,{type:`button`,className:Te,disabled:pe===0,onClick:()=>ae({mode:`all`,excluded:new Set}),children:[e(`Select all matching`),` (`,pe,`)`]}),(0,V.jsx)(`button`,{type:`button`,className:Te,onClick:()=>ae(Fm()),children:e(`Clear selection`)})]}),(0,V.jsxs)(`div`,{className:`overflow-x-auto`,children:[(0,V.jsxs)(`table`,{className:`w-full text-left text-sm`,children:[(0,V.jsx)(`thead`,{children:(0,V.jsxs)(`tr`,{className:`text-muted-foreground`,children:[(0,V.jsx)(`th`,{className:`p-1`,children:(0,V.jsx)(`span`,{className:`sr-only`,children:e(`Select`)})}),(0,V.jsx)(`th`,{className:`p-1`,children:e(`Created`)}),(0,V.jsx)(`th`,{className:`p-1`,children:e(`Session`)}),(0,V.jsx)(`th`,{className:`p-1`,children:e(`Project directory`)}),(0,V.jsx)(`th`,{className:`p-1`,children:e(`Resolved by`)})]})}),(0,V.jsx)(`tbody`,{children:E.rows.map(t=>(0,V.jsxs)(`tr`,{className:`border-t border-border`,children:[(0,V.jsx)(`td`,{className:`p-1`,children:(0,V.jsx)(`input`,{type:`checkbox`,"aria-label":`${e(`Select`)} ${t.sessionId??t.key}`,checked:t.selectable&&me(t),disabled:!t.selectable,onChange:()=>he(t)})}),(0,V.jsx)(`td`,{className:`p-1 whitespace-nowrap`,children:t.createdAt?new Date(t.createdAt).toLocaleString():`—`}),(0,V.jsx)(`td`,{className:`p-1 font-mono text-xs`,children:t.sessionId??t.key}),(0,V.jsx)(`td`,{className:`p-1 font-mono text-xs break-all`,children:t.directory??t.recordedDirectory??`—`}),(0,V.jsx)(`td`,{className:`p-1`,children:we[t.via]})]},t.key))})]}),E.total===0&&(0,V.jsx)(`p`,{className:`text-sm`,children:e(`No sessions match.`)})]}),(0,V.jsxs)(`div`,{className:`flex items-center gap-2 text-sm`,children:[(0,V.jsx)(`button`,{type:`button`,className:Te,disabled:re||E.offset===0,onClick:()=>void fe(Math.max(0,E.offset-Mm),!1),children:e(`Previous`)}),(0,V.jsxs)(`span`,{children:[E.total?E.offset+1:0,`–`,Math.min(E.offset+Mm,E.total),` /`,` `,E.total]}),(0,V.jsx)(`button`,{type:`button`,className:Te,disabled:re||E.offset+Mm>=E.total,onClick:()=>void fe(E.offset+Mm,!1),children:e(`Next`)})]})]}),(0,V.jsxs)(`label`,{className:`block text-sm`,children:[e(`Import model`),(0,V.jsxs)(un,{"aria-label":e(`Import model`),className:I,value:oe,onChange:e=>M(e.target.value),children:[(0,V.jsx)(`option`,{value:``,children:e(`None`)}),A?.opencode.models.map(e=>(0,V.jsxs)(`option`,{value:`${e.provider}/${e.model}`,children:[e.name,` (`,e.provider,`/`,e.model,`)`]},`${e.provider}/${e.model}`)),(0,V.jsxs)(`option`,{value:`external`,children:[e(`Saved external API`),A?.external.model?` (${A.external.model})`:``,A&&A.external.state!==`ready`?` — ${e(`not ready`)}`:``]})]})]}),(0,V.jsxs)(`p`,{className:`text-xs text-muted-foreground`,children:[e(`Configured, not tested.`),` `,(0,V.jsx)(`button`,{type:`button`,className:`underline`,onClick:()=>document.getElementById(`settings-health`)?.scrollIntoView(),children:e(`Test models in Health`)})]}),(0,V.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[(0,V.jsx)(`button`,{type:`button`,className:Te,disabled:be||_e||!!ye,onClick:()=>void xe(!0),children:e(`Preview (dry run)`)}),(0,V.jsx)(`button`,{type:`button`,className:Te,disabled:be||_e||!!ye||!!ve,onClick:()=>void xe(!1),children:e(`Start import`)}),be&&(0,V.jsx)(`button`,{type:`button`,className:Te,disabled:se?.state===`cancelling`,onClick:()=>void Se(),children:e(`Cancel after current unit`)})]}),(ye||ve)&&!be&&(0,V.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:ye??ve}),N&&(0,V.jsx)(`p`,{role:`alert`,children:N}),se&&(0,V.jsxs)(`div`,{role:`status`,className:`text-sm`,children:[se.host,` · `,se.dryRun?e(`Preview (dry run)`):e(`Import`),` · `,e(se.state),` ·`,` `,se.sessions,` `,e(`sessions`),` · `,se.processed,`/`,se.total,` `,e(`units`),se.error&&(0,V.jsx)(`p`,{role:`alert`,children:se.error}),se.report&&(0,V.jsx)(`pre`,{className:`mt-2 overflow-auto whitespace-pre-wrap rounded bg-background p-3 text-xs`,children:se.report})]})]})}function Rm(e){return e.pi?.state===`running`||e.opencode?.state===`running`}function zm(e){return!e||e.total<=0?null:{percent:e.percent,done:`${e.done.toLocaleString(`en-US`)} / ${e.total.toLocaleString(`en-US`)}`,minutesLeft:e.minutesLeft===null?`unknown`:`about ${e.minutesLeft}`}}function Bm(e,t){let n=e?.state===`running`,r=e?.paused===!0;return{runNow:!n&&!r&&!t,pause:n&&!r,resume:r&&!t}}function Vm(e,t){return e??!t}function Hm(e,t){let n=t.indexOf(`/`);if(t!==`inherit`&&t!==`external`&&(n<1||n===t.length-1))throw Error(`Enter a model as provider/model.`);return{[`${e}BackfillModel`]:t}}function Um(){let e=cm(),[t,n]=(0,_.useState)(),[r,i]=(0,_.useState)({pi:null,opencode:null}),[a,o]=(0,_.useState)(),[s,c]=(0,_.useState)({}),[l,u]=(0,_.useState)({}),[d,f]=(0,_.useState)({}),[p,m]=(0,_.useState)(``),[h,g]=(0,_.useState)(!1),v=Rm(r)||a?.pi.run?.state===`running`||a?.opencode.run?.state===`running`,y=()=>$(`/api/settings/backfill/runs`).then(o).catch(e=>m(e.message));(0,_.useEffect)(()=>{let e=!0;$(`/api/settings`).then(t=>{e&&n(t)}).catch(t=>{e&&m(t.message)}),$(`/api/settings/backfill`).then(t=>{e&&i(t)}).catch(t=>{e&&m(t.message)}),y();for(let t of[`pi`,`opencode`])$(`/api/settings/models?host=${t}`).then(n=>{e&&c(e=>({...e,[t]:n}))}).catch(()=>{e&&c(e=>({...e,[t]:{available:!1}}))});let t=im(t=>{e&&n(t)});return()=>{e=!1,t()}},[]),(0,_.useEffect)(()=>{if(!v)return;let e=!0,t=setInterval(()=>{$(`/api/settings/backfill`).then(t=>{e&&i(t)}).catch(t=>{e&&m(t.message)}),e&&y()},3e3);return()=>{e=!1,clearInterval(t)}},[v]);async function b(n){if(t){g(!0);try{await $(`/api/settings`,{method:`PATCH`,body:JSON.stringify({edits:n,revision:t.revision})}),m(e(`Saved. Changes apply at the next run.`))}catch(e){m(e.message)}await rm(),g(!1)}}async function x(t,n){g(!0);try{await $(`/api/settings/backfill/${t}/${n}`,{method:`POST`,body:`{}`}),m(e(n===`pause`?`Pausing after the current exchange.`:`Backfill started.`))}catch(e){m(e.message)}await y(),g(!1)}function S(t,n){try{b(Hm(t,n.trim()))}catch(t){m(e(t.message))}}return(0,V.jsxs)(`section`,{className:`space-y-3 rounded-xl border border-border bg-card p-4`,"aria-label":e(`Automatic import`),children:[(0,V.jsx)(`h2`,{className:`text-lg font-medium`,children:e(`Automatic import`)}),(0,V.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:e(`Automatic import makes model calls. A model change applies at the next run. Turning it off stops a running import after the current exchange.`)}),(0,V.jsxs)(`label`,{className:`flex items-center gap-2 text-sm`,children:[(0,V.jsx)(`input`,{type:`checkbox`,checked:!!t?.settings.autoBackfill?.globalValue,disabled:h||!t,onChange:e=>void b({autoBackfill:e.target.checked})}),e(`Import past chats automatically`)]}),[`pi`,`opencode`].map(n=>{let i=String(t?.settings[`${n}BackfillModel`]?.globalValue??`inherit`),o=l[n]??i,c=s[n]?.models??[],p=o===`inherit`||o===`external`||c.some(e=>`${e.provider}/${e.model}`===o),m=um({memoryProvider:t?.settings.memoryProvider?.globalValue,memoryModel:t?.settings.memoryModel?.globalValue,memoryApiUrl:t?.settings.memoryApiUrl?.globalValue,keySet:!!t?.secrets?.memoryApiKey?.set}),g=a?.[n].run??null,_=a?.[n].runNowUnavailable??null,v=Bm(g,_),y=g?.state===`running`?zm(g):null,b=Vm(d[n],p);return(0,V.jsxs)(`div`,{className:`space-y-2 rounded-lg border border-border p-3 text-sm`,children:[(0,V.jsx)(`h3`,{className:`font-medium`,children:n===`pi`?`Pi`:`OpenCode`}),(0,V.jsxs)(`label`,{className:`block`,children:[e(`Backfill model`),(0,V.jsxs)(un,{"aria-label":`${n===`pi`?`Pi`:`OpenCode`} ${e(`Backfill model`)}`,className:`mt-1 block w-full rounded border border-border bg-background p-2`,value:b?`typed`:o,onChange:e=>{let t=e.target.value;f(e=>({...e,[n]:t===`typed`})),u(e=>({...e,[n]:t}))},children:[(0,V.jsx)(`option`,{value:`inherit`,children:e(`Same as live capture`)}),(0,V.jsx)(`option`,{value:`external`,disabled:m.length>0,children:e(`External API`)}),c.map(e=>(0,V.jsxs)(`option`,{value:`${e.provider}/${e.model}`,children:[e.name,` (`,e.provider,`/`,e.model,`)`]},`${e.provider}/${e.model}`)),(0,V.jsx)(`option`,{value:`typed`,children:e(`Manual provider/model`)})]})]}),b&&(0,V.jsx)(`input`,{"aria-label":`${n} ${e(`Manual provider/model`)}`,className:`w-full rounded border border-border bg-background p-2`,placeholder:`provider/model`,value:o===`typed`?``:o,onChange:e=>u(t=>({...t,[n]:e.target.value}))}),m.length>0&&(0,V.jsxs)(`p`,{className:`text-xs text-muted-foreground`,children:[e(`External API needs`),`: `,m.join(`, `)]}),s[n]?.available===!1&&(0,V.jsx)(`p`,{children:e(`Model list unavailable. Enter provider/model manually.`)}),(0,V.jsx)(`button`,{type:`button`,className:`rounded border border-border px-3 py-1.5`,disabled:h||!t,onClick:()=>S(n,o),children:e(`Save model`)}),(0,V.jsxs)(`p`,{children:[e(`State`),`:`,` `,g?.paused?e(`paused`):e(g?.state??r[n]?.state??`not started`),g?.state===`running`&&g.surface&&` (${e(`started from ${g.surface}`)})`]}),y&&(0,V.jsxs)(`div`,{className:`space-y-1`,children:[(0,V.jsx)(`div`,{role:`progressbar`,"aria-valuemin":0,"aria-valuemax":100,"aria-valuenow":y.percent,className:`h-2 w-full overflow-hidden rounded bg-muted`,children:(0,V.jsx)(`div`,{className:`h-full bg-primary`,style:{width:`${y.percent}%`}})}),(0,V.jsxs)(`p`,{className:`text-muted-foreground`,children:[y.percent,`% · `,y.done,` · `,e(`Minutes left`),`:`,` `,y.minutesLeft===`unknown`?e(`unknown`):y.minutesLeft.replace(`about`,e(`about`))]})]}),(0,V.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[(0,V.jsx)(`button`,{type:`button`,className:`rounded border border-border px-3 py-1.5`,disabled:h||!v.runNow,onClick:()=>void x(n,`run`),children:e(`Run now`)}),(0,V.jsx)(`button`,{type:`button`,className:`rounded border border-border px-3 py-1.5`,disabled:h||!v.pause,onClick:()=>void x(n,`pause`),children:e(`Pause`)}),(0,V.jsx)(`button`,{type:`button`,className:`rounded border border-border px-3 py-1.5`,disabled:h||!v.resume,onClick:()=>void x(n,`resume`),children:e(`Resume`)})]}),_&&(0,V.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:_}),r[n]&&(0,V.jsxs)(`div`,{className:`space-y-1 text-muted-foreground`,children:[(0,V.jsxs)(`p`,{children:[e(`Imported`),`: `,r[n].counts.imported,` · `,e(`Skipped`),`:`,` `,r[n].counts.skipped,` · `,e(`Failed`),`: `,r[n].counts.failed]}),(0,V.jsxs)(`p`,{children:[e(`Pending`),`: `,r[n].counts.pending,` · `,e(`Unresolved sessions`),`:`,` `,r[n].counts.unresolved,r[n].counts.unresolved>0&&(0,V.jsxs)(V.Fragment,{children:[` `,(0,V.jsx)(`a`,{className:`underline`,href:`#directory-maps`,children:e(`Directory maps`)})]})]}),(0,V.jsxs)(`p`,{children:[e(`Model`),`: `,r[n].model??e(`none`)]}),(0,V.jsxs)(`p`,{children:[e(`Cutoff`),`: `,new Date(r[n].cutoff).toLocaleString()]}),r[n].error&&(0,V.jsx)(`p`,{role:`alert`,children:r[n].error})]})]},n)}),p&&(0,V.jsx)(`p`,{role:`status`,className:`text-sm`,children:p})]})}function Wm(){let e=cm(),[t,n]=(0,_.useState)(),[r,i]=(0,_.useState)(),[a,o]=(0,_.useState)(),[s,c]=(0,_.useState)(``),[l,u]=(0,_.useState)(!1);(0,_.useEffect)(()=>{let e=!0;$(`/api/settings`).then(t=>{e&&n(t)}).catch(t=>{e&&c(t.message)}),$(`/api/settings/web-autostart`).then(t=>{e&&i(t)}).catch(t=>{e&&c(t.message)}),$(`/api/settings/version`).then(t=>{e&&o(t)}).catch(()=>{});let t=im(t=>{e&&n(t)});return()=>{e=!1,t()}},[]);async function d(n){if(t){u(!0);try{await $(`/api/settings`,{method:`PATCH`,body:JSON.stringify({edits:{webServerAutoStart:n},revision:t.revision})}),c(e(`Saved. The login item changes at the next Pi or OpenCode start.`))}catch(e){c(e.message)}await rm(),u(!1)}}return(0,V.jsxs)(`section`,{className:`space-y-3 rounded-xl border border-border bg-card p-4`,"aria-label":e(`Web app`),children:[(0,V.jsx)(`h2`,{className:`text-lg font-medium`,children:e(`Web app`)}),(0,V.jsxs)(`label`,{className:`flex items-center gap-2 text-sm`,children:[(0,V.jsx)(`input`,{type:`checkbox`,checked:!!t?.settings.webServerAutoStart?.globalValue,disabled:l||!t,onChange:e=>void d(e.target.checked)}),e(`Start web app at login`)]}),(0,V.jsxs)(`p`,{className:`text-sm`,children:[e(`Login item`),`: `,e(r?.state??`not installed`)]}),r?.state===`unsupported`&&(0,V.jsx)(`p`,{role:`status`,children:e(`Login items are unsupported on this platform.`)}),r?.state===`no-runtime`&&(0,V.jsx)(`p`,{role:`status`,children:e(`Install Node or Bun to start the web app at login.`)}),a&&(0,V.jsx)(Gm,{info:a}),(0,V.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:e(`Use these commands to apply the change now:`)}),(0,V.jsxs)(`div`,{className:`space-y-1 font-mono text-xs`,children:[(0,V.jsx)(`p`,{children:`om-memory-system web`}),(0,V.jsx)(`p`,{children:`om-memory-system web install`}),(0,V.jsx)(`p`,{children:`om-memory-system web uninstall`}),(0,V.jsx)(`p`,{children:`om-memory-system web status`})]}),s&&(0,V.jsx)(`p`,{role:`status`,className:`text-sm`,children:s})]})}function Gm({info:e}){let t=cm(),n=fm(e);return(0,V.jsxs)(`div`,{className:`space-y-1 text-sm`,children:[(0,V.jsxs)(`p`,{children:[t(`Running version`),`: `,e.running,` · `,t(`Global command`),`:`,` `,e.global??t(`not installed globally`)]}),n.kind===`different`&&(0,V.jsx)(`p`,{role:`status`,className:`text-amber-600`,children:t(`The global command's version differs from the running OMMS. Upgrade it:`)}),n.command&&(0,V.jsx)(`p`,{className:`font-mono text-xs`,children:n.command}),(0,V.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:t(`A global install is optional but recommended: the login item and the terminal commands then run without npx.`)})]})}function Km(){let e=cm(),[t,n]=(0,_.useState)(!1),[r,i]=(0,_.useState)(),[a,o]=(0,_.useState)(``);async function s(e){try{i(await $(`/api/settings/log?lines=200${e?`&filter=capture`:``}`)),o(``)}catch(e){o(e.message)}}return(0,_.useEffect)(()=>{s(t)},[t]),(0,V.jsxs)(`section`,{className:`space-y-3 rounded-xl border border-border bg-card p-4`,"aria-label":e(`Log`),children:[(0,V.jsx)(`h2`,{className:`text-lg font-medium`,children:e(`Log`)}),(0,V.jsxs)(`div`,{className:`flex flex-wrap items-center gap-3 text-sm`,children:[(0,V.jsxs)(`label`,{children:[(0,V.jsx)(`input`,{type:`checkbox`,checked:t,onChange:e=>n(e.target.checked)}),` `,e(`Capture attempts only`)]}),(0,V.jsx)(`button`,{type:`button`,className:`rounded-lg border border-border px-3 py-1`,onClick:()=>void s(t),children:e(`Refresh`)}),r&&(0,V.jsx)(`button`,{type:`button`,className:`rounded-lg border border-border px-3 py-1`,onClick:()=>void navigator.clipboard.writeText(r.path),children:e(`Copy log path`)})]}),(0,V.jsx)(`p`,{className:`break-all text-xs text-muted-foreground`,children:r?.path}),a&&(0,V.jsx)(`p`,{role:`alert`,children:a}),(0,V.jsx)(`pre`,{className:`max-h-80 overflow-auto rounded-lg bg-background p-3 text-xs whitespace-pre-wrap break-words`,"aria-label":e(`Log lines`),children:r?r.lines.length?r.lines.join(`
122
+ `):e.evidence:``}function Xp(e){return e.evidence?Array.isArray(e.evidence)?e.evidence.length:1:0}function Zp({profile:e,loading:t=!1,onRefresh:n,onCleanup:r}){let{t:i}=cr(),[a,o]=(0,_.useState)({pref:1,pat:1,wf:1}),[s,c]=(0,_.useState)(!1),[l,u]=(0,_.useState)(!1),[d,f]=(0,_.useState)(null),[p,m]=(0,_.useState)(0),[h,g]=(0,_.useState)(`edit`),[v,y]=(0,_.useState)(null),b=(0,_.useMemo)(()=>{if(!e?.exists||!e.profileData)return null;let t=e.profileData;if(typeof t==`string`)try{t=JSON.parse(t)}catch{return null}return t},[e]),x=(0,_.useMemo)(()=>Dc(b?.preferences),[b]),S=(0,_.useMemo)(()=>Dc(b?.patterns),[b]),C=(0,_.useMemo)(()=>Dc(b?.workflows),[b]),w=(0,_.useMemo)(()=>C.map((e,t)=>({item:e,index:t})).sort((e,t)=>(t.item.frequency||0)-(e.item.frequency||0)),[C]),T=qp(x,a.pref),E=qp(S,a.pat),D=qp(w,a.wf),O=C.length;function te(e,t,n){let r=(e===`preferences`?x:e===`patterns`?S:C)[t];r&&(f(e),m(t),g(n),y(r),u(!0))}function re(e,t){return e.indexOf(t)}function ie({page:e,pageKey:t}){return e.total<=Kp?null:(0,V.jsxs)(`div`,{className:`flex flex-wrap items-center gap-1.5 pt-2`,children:[(0,V.jsxs)(`span`,{className:`text-xs text-muted-foreground mr-2`,children:[e.start+1,`-`,Math.min(e.start+Kp,e.total),` / `,e.total]}),Array.from({length:e.totalPages},(e,t)=>t+1).map(n=>(0,V.jsx)(W,{size:`xs`,variant:n===e.page?`default`:`outline`,onClick:()=>o(e=>({...e,[t]:n})),children:n},n))]})}function k({item:e,type:t,fullList:n}){let r=re(n,e),a=Jp(e);return(0,V.jsxs)(`div`,{className:`rounded-xl border border-border bg-card/50 p-3 space-y-2`,children:[(0,V.jsxs)(`div`,{className:`flex items-start gap-2`,children:[(0,V.jsx)(Al,{variant:`outline`,children:e.category||`General`}),(0,V.jsxs)(`div`,{className:`flex items-center gap-0.5 ml-auto`,children:[(0,V.jsx)(W,{variant:`ghost`,size:`icon-xs`,title:i(`btn-edit`)||`Edit`,onClick:()=>te(t,r,`edit`),children:(0,V.jsx)(_e,{className:`size-3`})}),(0,V.jsx)(W,{variant:`ghost`,size:`icon-xs`,title:i(`btn-delete`)||`Delete`,onClick:()=>te(t,r,`delete`),children:(0,V.jsx)(De,{className:`size-3`})})]}),(0,V.jsxs)(`div`,{className:`size-9 rounded-full border border-border grid place-items-center text-[10px] tabular-nums shrink-0`,title:`${a}%`,children:[a,`%`]})]}),(0,V.jsx)(`p`,{className:`text-sm`,children:e.description||``}),e.evidence||e.frequency?(0,V.jsxs)(`div`,{className:`flex items-center gap-1.5 text-xs text-muted-foreground`,children:[(0,V.jsxs)(`span`,{className:`inline-flex items-center gap-1`,title:i(`label-evidence-tooltip`,{count:e.frequency||1}),children:[(0,V.jsx)(Ee,{className:`size-3`}),e.frequency||1]}),Xp(e)>0?(0,V.jsxs)(V.Fragment,{children:[(0,V.jsx)(`span`,{children:`·`}),(0,V.jsxs)(`span`,{className:`inline-flex items-center gap-1`,title:Yp(e),children:[(0,V.jsx)(N,{className:`size-3`}),Xp(e),` evidence`]})]}):null]}):null]})}return t&&!e?(0,V.jsx)(`div`,{className:`text-sm text-muted-foreground py-8 text-center`,children:i(`loading-profile`)}):e?.exists?b?(0,V.jsxs)(V.Fragment,{children:[(0,V.jsxs)(`div`,{className:`space-y-6`,children:[(0,V.jsxs)(`div`,{className:`space-y-2`,children:[(0,V.jsx)(`h3`,{className:`text-base font-medium`,children:e.displayName||e.userId}),(0,V.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,V.jsxs)(`span`,{className:`rounded-full bg-muted px-2.5 py-1 text-xs`,children:[(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:i(`profile-version`)}),(0,V.jsx)(`span`,{className:`ml-1`,children:e.version})]}),(0,V.jsxs)(`span`,{className:`rounded-full bg-muted px-2.5 py-1 text-xs`,children:[(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:i(`profile-prompts`)}),(0,V.jsx)(`span`,{className:`ml-1`,children:e.totalPromptsAnalyzed})]}),(0,V.jsxs)(`span`,{className:`rounded-full bg-muted px-2.5 py-1 text-xs`,children:[(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:i(`profile-updated`)}),(0,V.jsx)(`span`,{className:`ml-1`,children:e.lastAnalyzedAt?jl(e.lastAnalyzedAt):`—`})]}),(0,V.jsxs)(`div`,{className:`ms-auto flex flex-wrap items-center gap-1.5`,children:[(0,V.jsxs)(W,{variant:`outline`,size:`sm`,onClick:()=>r?.(),children:[(0,V.jsx)(I,{className:`size-3.5`}),i(`btn-ai-cleanup`)]}),(0,V.jsxs)(W,{variant:`secondary`,size:`sm`,onClick:()=>n?.(),children:[(0,V.jsx)(xe,{className:`size-3.5`}),i(`btn-refresh`)]}),(0,V.jsxs)(W,{variant:`secondary`,size:`sm`,onClick:()=>c(!0),children:[(0,V.jsx)(Se,{className:`size-3.5`}),`History`]})]})]})]}),(0,V.jsxs)(`div`,{className:`grid gap-6`,children:[(0,V.jsxs)(`section`,{id:`profile-preferences`,className:`scroll-mt-4 space-y-3`,children:[(0,V.jsxs)(`h4`,{className:`flex items-center gap-2 text-sm font-medium`,children:[(0,V.jsx)(ce,{className:`size-4`}),i(`profile-preferences`),(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:x.length})]}),x.length===0?(0,V.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:i(`empty-preferences`)}):(0,V.jsxs)(V.Fragment,{children:[(0,V.jsx)(`div`,{className:`grid gap-2 sm:grid-cols-2 lg:grid-cols-3`,children:T.items.map(e=>(0,V.jsx)(k,{item:e,type:`preferences`,fullList:x},re(x,e)))}),(0,V.jsx)(ie,{page:T,pageKey:`pref`})]})]}),(0,V.jsxs)(`section`,{id:`profile-patterns`,className:`scroll-mt-4 space-y-3`,children:[(0,V.jsxs)(`h4`,{className:`flex items-center gap-2 text-sm font-medium`,children:[(0,V.jsx)(ee,{className:`size-4`}),i(`profile-patterns`),(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:S.length})]}),S.length===0?(0,V.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:i(`empty-patterns`)}):(0,V.jsxs)(V.Fragment,{children:[(0,V.jsx)(`div`,{className:`grid gap-2 sm:grid-cols-2 lg:grid-cols-3`,children:E.items.map(e=>(0,V.jsx)(k,{item:e,type:`patterns`,fullList:S},re(S,e)))}),(0,V.jsx)(ie,{page:E,pageKey:`pat`})]})]}),(0,V.jsxs)(`section`,{id:`profile-workflows`,className:`scroll-mt-4 space-y-3`,children:[(0,V.jsxs)(`h4`,{className:`flex items-center gap-2 text-sm font-medium`,children:[(0,V.jsx)(Me,{className:`size-4`}),i(`profile-workflows`),(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:O})]}),O===0?(0,V.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:i(`empty-workflows`)}):(0,V.jsxs)(V.Fragment,{children:[(0,V.jsx)(`div`,{className:`space-y-2`,children:D.items.map(e=>{let t=e.item,n=e.index,r=Jp(t);return(0,V.jsxs)(`div`,{className:`rounded-xl border border-border bg-card/50 p-3 space-y-2`,children:[(0,V.jsxs)(`div`,{className:`flex items-start gap-2`,children:[(0,V.jsx)(`div`,{className:`text-sm font-medium flex-1`,children:t.description||``}),(0,V.jsxs)(`div`,{className:`flex items-center gap-0.5`,children:[(0,V.jsx)(W,{variant:`ghost`,size:`icon-xs`,onClick:()=>te(`workflows`,n,`edit`),children:(0,V.jsx)(_e,{className:`size-3`})}),(0,V.jsx)(W,{variant:`ghost`,size:`icon-xs`,onClick:()=>te(`workflows`,n,`delete`),children:(0,V.jsx)(De,{className:`size-3`})})]}),(0,V.jsxs)(`div`,{className:`size-9 rounded-full border border-border grid place-items-center text-[10px] tabular-nums shrink-0`,children:[r,`%`]})]}),t.steps?.length?(0,V.jsx)(`div`,{className:`flex flex-wrap items-center gap-1.5`,children:t.steps.map((e,n)=>(0,V.jsxs)(`div`,{className:`contents`,children:[(0,V.jsxs)(`div`,{className:`inline-flex items-center gap-1.5 rounded-lg bg-muted px-2 py-1 text-xs`,children:[(0,V.jsx)(`span`,{className:`text-muted-foreground tabular-nums`,children:n+1}),(0,V.jsx)(`span`,{children:e})]}),n<(t.steps?.length||0)-1?(0,V.jsx)(ne,{className:`size-3 text-muted-foreground`}):null]},n))}):null,(0,V.jsxs)(`div`,{className:`flex items-center gap-1.5 text-xs text-muted-foreground`,children:[(0,V.jsxs)(`span`,{className:`inline-flex items-center gap-1`,title:i(`label-evidence-tooltip`,{count:t.frequency||1}),children:[(0,V.jsx)(Ee,{className:`size-3`}),t.frequency||1]}),Xp(t)>0?(0,V.jsxs)(V.Fragment,{children:[(0,V.jsx)(`span`,{children:`·`}),(0,V.jsxs)(`span`,{className:`inline-flex items-center gap-1`,title:Yp(t),children:[(0,V.jsx)(N,{className:`size-3`}),Xp(t),` evidence`]})]}):null]})]},e.index)})}),(0,V.jsx)(ie,{page:D,pageKey:`wf`})]})]})]})]}),(0,V.jsx)(Up,{open:s,onOpenChange:c,profileId:e?.id||``}),(0,V.jsx)(Gp,{open:l,onOpenChange:u,type:d,index:p,action:h,item:v,onSaved:n})]}):null:(0,V.jsxs)(`div`,{className:`flex flex-col items-center gap-2 py-10 text-muted-foreground`,children:[(0,V.jsx)(Ae,{className:`size-8`}),(0,V.jsx)(`p`,{className:`text-sm`,children:e?.message||i(`empty-preferences`)})]})}function Qp({open:e=!1,count:t=0,onOpenChange:n,onComplete:r}){let{t:i}=cr(),[a,o]=(0,_.useState)(!1),[s,c]=(0,_.useState)(``),[l,u]=(0,_.useState)(0);(0,_.useEffect)(()=>{e&&!a&&(c(i(`migration-found-tags`,{count:t})),u(0))},[e,a,t,i]);function d(e){a&&!e||n?.(e)}async function f(){o(!0),c(i(`status-migration-init`)),u(0);let e=!0,t=0,n=1e3;for(;e&&t<n;){t++;let n=await Un(`/api/migration/tags/run-batch`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({batchSize:3})});if(!n.success||!n.data){c(i(`toast-migration-failed`)+`: `+(n.error||``)),o(!1);return}let r=n.data.processed;e=n.data.hasMore;let a=n.data.total;u(a>0?Math.round(r/a*100):0),c(i(`status-migration-progress`,{current:r,total:a})),e&&await new Promise(e=>setTimeout(e,100))}if(t>=n){c(i(`migration-stopped`)),o(!1);return}u(100),c(i(`toast-migration-success`)),H.success(i(`toast-migration-success`)),setTimeout(()=>{o(!1),d(!1),r?.()},2e3)}return(0,V.jsx)(Ds,{open:e,onOpenChange:d,children:(0,V.jsxs)(As,{className:`sm:max-w-md`,showCloseButton:!a,children:[(0,V.jsx)(js,{children:(0,V.jsx)(Ns,{children:i(`modal-migration-title`)})}),(0,V.jsxs)(`div`,{className:`space-y-3`,children:[(0,V.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:s}),(0,V.jsx)(`div`,{className:`h-2 rounded-full bg-muted overflow-hidden`,children:(0,V.jsx)(`div`,{className:`h-full bg-primary transition-all duration-200`,style:{width:`${l}%`}})}),(0,V.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:i(`migration-note`)})]}),a?null:(0,V.jsx)(Ms,{children:(0,V.jsxs)(W,{onClick:f,children:[(0,V.jsx)(ye,{className:`size-3.5`}),i(`btn-start-migration`)]})})]})})}async function $(e,t={}){let n=(t.method??`GET`).toUpperCase(),r=n!==`GET`&&n!==`HEAD`,i=await fetch(e,{...t,headers:{"x-omms-token":window.__OMMS_TOKEN__??``,...r||t.body?{"Content-Type":`application/json`}:{},...t.headers}}),a=await i.json();if(!i.ok)throw Error(a.error??`Request failed (${i.status})`);return a}var $p=new Set,em=0;function tm(e){em++;for(let t of $p)t(e)}function nm(){let e=em;return{isCurrent:()=>em===e}}async function rm(){try{let e=await $(`/api/settings`);return tm(e),e}catch{return null}}function im(e){return $p.add(e),()=>{$p.delete(e)}}function am(e,t){return`${e.replace(/\.+$/,``)}. ${t}`}var om={Models:[`模型`,`النماذج`],"External API fallback":[`外部 API 备用模型`,`النموذج الاحتياطي عبر واجهة API الخارجية`],none:[`无`,`لا يوجد`],ready:[`就绪`,`جاهز`],"not ready":[`未就绪`,`غير جاهز`],"Credentials (values hidden)":[`凭据(不显示内容)`,`بيانات الاعتماد (القيم مخفية)`],set:[`已设置`,`مضبوط`],"not set":[`未设置`,`غير مضبوط`],"Effective model":[`当前使用模型`,`النموذج المستخدم`],session:[`会话`,`الجلسة`],"external API":[`外部 API`,`واجهة API الخارجية`],unavailable:[`不可用`,`غير متاح`],"Project override is active. Changes to the global file may not take effect here.":[`项目设置优先。全局文件的更改可能不会在此生效。`,`إعدادات المشروع لها الأولوية. قد لا تسري تغييرات الملف العام هنا.`],"Model choice":[`模型选择`,`اختيار النموذج`],"Session model":[`会话模型`,`نموذج الجلسة`],"Manual model":[`手动选择模型`,`اختيار النموذج يدوياً`],"Manual provider/model":[`手动输入提供商/模型`,`إدخال المزوّد/النموذج يدوياً`],"Model list unavailable. Enter provider/model manually.":[`模型列表不可用。请手动输入提供商/模型。`,`قائمة النماذج غير متاحة. أدخل المزوّد/النموذج يدوياً.`],"OpenCode model list unavailable":[`OpenCode 模型列表不可用`,`قائمة نماذج OpenCode غير متاحة`],"OMMS could not find OpenCode on this computer, so it cannot list OpenCode's models here. Type the model as provider/model, for example zai-coding-plan/glm-5.3. If OpenCode is installed, open an OpenCode session and reload this page.":[`OMMS 在这台电脑上找不到 OpenCode,因此无法在此列出 OpenCode 的模型。请按 提供商/模型 格式输入模型,例如 zai-coding-plan/glm-5.3。如果已安装 OpenCode,请打开一个 OpenCode 会话,然后重新加载此页面。`,`لم يعثر OMMS على OpenCode في هذا الحاسوب، لذا لا يمكنه عرض نماذج OpenCode هنا. أدخل النموذج بصيغة المزوّد/النموذج، مثل zai-coding-plan/glm-5.3. إذا كان OpenCode مثبتاً، فافتح جلسة OpenCode ثم أعد تحميل هذه الصفحة.`],"OpenCode took too long to send its model list. Reload this page to try again, or type the model as provider/model.":[`OpenCode 发送模型列表的时间过长。请重新加载此页面重试,或按 提供商/模型 格式输入模型。`,`استغرق OpenCode وقتاً طويلاً لإرسال قائمة نماذجه. أعد تحميل هذه الصفحة للمحاولة مرة أخرى، أو أدخل النموذج بصيغة المزوّد/النموذج.`],"OpenCode has no signed-in models. Run `opencode auth login`, then reload this page.":["OpenCode 没有已登录的模型。请运行 `opencode auth login`,然后重新加载此页面。","لا توجد في OpenCode نماذج مسجَّل الدخول إليها. شغّل `opencode auth login` ثم أعد تحميل هذه الصفحة."],"This OpenCode version sent a model list OMMS cannot read. Type the model as provider/model. Update OMMS if this continues.":[`此版本的 OpenCode 发送的模型列表 OMMS 无法读取。请按 提供商/模型 格式输入模型。如果问题持续,请更新 OMMS。`,`أرسل هذا الإصدار من OpenCode قائمة نماذج لا يستطيع OMMS قراءتها. أدخل النموذج بصيغة المزوّد/النموذج. حدّث OMMS إذا استمرت المشكلة.`],"OMMS could not read Pi's model list. Type the model as provider/model.":[`OMMS 无法读取 Pi 的模型列表。请按 提供商/模型 格式输入模型。`,`تعذّر على OMMS قراءة قائمة نماذج Pi. أدخل النموذج بصيغة المزوّد/النموذج.`],"Pi has no signed-in models. Sign in to a provider in Pi, then reload this page.":[`Pi 没有已登录的模型。请在 Pi 中登录一个提供商,然后重新加载此页面。`,`لا توجد في Pi نماذج مسجَّل الدخول إليها. سجّل الدخول إلى مزوّد في Pi ثم أعد تحميل هذه الصفحة.`],"Save model":[`保存模型`,`حفظ النموذج`],"Enter a model as provider/model.":[`按 提供商/模型 格式输入。`,`أدخل النموذج بصيغة المزوّد/النموذج.`],"Saved. New capture and profile work uses these settings.":[`已保存。下一次捕获和用户画像将使用这些设置。`,`تم الحفظ. ستستخدم عمليات الالتقاط والملف الشخصي الجديدة هذه الإعدادات.`],"Saved. OMMS now reads ~/.config/omms/omms.jsonc. The legacy config was kept.":[`已保存。OMMS 现在读取 ~/.config/omms/omms.jsonc,旧配置保留。`,`تم الحفظ. يقرأ OMMS الآن الملف ~/.config/omms/omms.jsonc مع الاحتفاظ بالإعدادات القديمة.`],"Current settings were reloaded; check the values and save again.":[`已重新加载当前设置;检查数值后再次保存。`,`أُعيد تحميل الإعدادات الحالية. راجع القيم ثم احفظ مجدداً.`],"Capture diagnostics":[`捕获诊断`,`تشخيص الالتقاط`],"Time range":[`时间范围`,`الفترة الزمنية`],"24 hours":[`24 小时`,`24 ساعة`],"7 days":[`7 天`,`7 أيام`],"30 days":[`30 天`,`30 يوماً`],"90 days":[`90 天`,`90 يوماً`],"Outcomes by model":[`各模型结果`,`النتائج حسب النموذج`],"Host / model":[`主机 / 模型`,`المضيف / النموذج`],Saved:[`已保存`,`محفوظ`],Skipped:[`已跳过`,`متجاوز`],Failed:[`失败`,`فشل`],Total:[`总数`,`المجموع`],"Failure reasons":[`失败原因`,`أسباب الفشل`],"Recent attempts":[`最近尝试`,`المحاولات الأخيرة`],Time:[`时间`,`الوقت`],Host:[`主机`,`المضيف`],Source:[`来源`,`المصدر`],Session:[`会话`,`الجلسة`],Path:[`路径`,`المسار`],Model:[`模型`,`النموذج`],Stop:[`结束原因`,`سبب التوقف`],Blocks:[`区块`,`الكتل`],Prompt:[`提示词长度`,`طول الطلب`],Reply:[`回复长度`,`طول الرد`],Duration:[`耗时`,`المدة`],Outcome:[`结果`,`النتيجة`],Reason:[`原因`,`السبب`],"Save capture traces":[`保存捕获追踪`,`حفظ تتبعات الالتقاط`],"Traces can contain conversation content after redaction. Keep tracing off unless you need it for debugging.":[`追踪文件在脱敏后仍可能包含对话内容。仅在调试时开启。`,`قد تحتوي التتبعات على محتوى المحادثة بعد التنقيح. فعّلها عند التصحيح فقط.`],"This project turns tracing off. The global switch cannot turn it on here.":[`此项目已关闭追踪。全局开关无法在此开启。`,`أوقف هذا المشروع التتبع. لا يمكن للمفتاح العام تفعيله هنا.`],"Trace retention (days)":[`追踪保留天数`,`مدة حفظ التتبعات (بالأيام)`],"Attempt retention (days)":[`尝试记录保留天数`,`مدة حفظ المحاولات (بالأيام)`],"Trace files":[`追踪文件`,`ملفات التتبع`],View:[`查看`,`عرض`],Delete:[`删除`,`حذف`],Health:[`健康检查`,`فحص الحالة`],"Config files":[`配置文件`,`ملفات الإعدادات`],"Memory store":[`记忆存储`,`مخزن الذاكرة`],Embedding:[`嵌入模型`,`نموذج التضمين`],"Web binding":[`网页服务绑定`,`ربط خادم الويب`],"OpenCode model":[`OpenCode 模型`,`نموذج OpenCode`],"Pi model":[`Pi 模型`,`نموذج Pi`],"Capture failures (24 hours)":[`捕获失败(24 小时)`,`فشل الالتقاط (24 ساعة)`],"OpenCode model test":[`OpenCode 模型测试`,`اختبار نموذج OpenCode`],"Pi model test":[`Pi 模型测试`,`اختبار نموذج Pi`],"Run checks":[`运行检查`,`تشغيل الفحوصات`],"Run checks and test models":[`运行检查并测试模型`,`تشغيل الفحوصات واختبار النماذج`],"Checking…":[`正在检查…`,`جارٍ الفحص…`],pass:[`通过`,`نجاح`],warn:[`警告`,`تحذير`],fail:[`失败`,`فشل`],"Import and backfill":[`导入和回填`,`الاستيراد والاستكمال`],"Automatic import":[`自动导入`,`الاستيراد التلقائي`],"Automatic import makes model calls. Changes apply at the next host start. Turning it off stops a running import after the current exchange.":[`自动导入会调用模型。更改在下次启动主机时生效。关闭后,正在运行的导入将在当前轮次结束后停止。`,`يستدعي الاستيراد التلقائي النموذج. تسري التغييرات عند بدء المضيف التالي. يوقف التعطيل الاستيراد بعد التبادل الحالي.`],"Import past chats automatically":[`自动导入过去的对话`,`استيراد المحادثات السابقة تلقائياً`],"Backfill model":[`历史导入模型`,`نموذج استيراد السجل`],"Same as live capture":[`与实时捕获相同`,`مثل الالتقاط المباشر`],"Saved. Changes apply at the next host start.":[`已保存。更改在下次启动主机时生效。`,`تم الحفظ. تسري التغييرات عند بدء المضيف التالي.`],State:[`状态`,`الحالة`],"not started":[`尚未开始`,`لم يبدأ`],"not-started":[`尚未开始`,`لم يبدأ`],Imported:[`已导入`,`مستورَد`],Pending:[`待处理`,`معلّق`],"Unresolved sessions":[`目录未解析的会话`,`جلسات بمجلدات غير محددة`],Cutoff:[`截止时间`,`وقت القطع`],stopped:[`已停止`,`توقف`],"Web app":[`网页应用`,`تطبيق الويب`],"Start web app at login":[`登录时启动网页应用`,`بدء تطبيق الويب عند تسجيل الدخول`],"Login item":[`登录启动项`,`عنصر بدء تسجيل الدخول`],"not installed":[`未安装`,`غير مثبّت`],"not-installed":[`未安装`,`غير مثبّت`],installed:[`已安装`,`مثبّت`],unsupported:[`不受支持`,`غير مدعوم`],"no-runtime":[`缺少运行环境`,`بيئة التشغيل غير متاحة`],"no-package":[`缺少软件包`,`الحزمة غير متاحة`],"Login items are unsupported on this platform.":[`此平台不支持登录启动项。`,`عناصر بدء تسجيل الدخول غير مدعومة على هذا النظام.`],"Install Node or Bun to start the web app at login.":[`安装 Node 或 Bun 后,网页应用才能在登录时启动。`,`ثبّت Node أو Bun لبدء تطبيق الويب عند تسجيل الدخول.`],"Use these commands to apply the change now:":[`使用以下命令立即应用更改:`,`استخدم هذه الأوامر لتطبيق التغيير الآن:`],"Saved. The login item changes at the next Pi or OpenCode start.":[`已保存。登录启动项将在下次启动 Pi 或 OpenCode 时更新。`,`تم الحفظ. يتغير عنصر بدء التشغيل عند بدء Pi أو OpenCode التالي.`],"History host":[`历史记录主机`,`مضيف السجل`],Scope:[`范围`,`النطاق`],"Current project":[`当前项目`,`المشروع الحالي`],"All projects":[`所有项目`,`كل المشاريع`],"Project directory":[`项目目录`,`مجلد المشروع`],"Current directory":[`当前目录`,`المجلد الحالي`],"Profile batch size":[`用户画像批量大小`,`حجم دفعة الملف الشخصي`],"Directory maps (old=new, one per line)":[`目录映射(旧=新,每行一条)`,`تعيين المجلدات (قديم=جديد، واحد في كل سطر)`],"Force reimport":[`强制重新导入`,`فرض إعادة الاستيراد`],"Skip memories":[`跳过记忆`,`تجاوز الذكريات`],"Skip profile":[`跳过用户画像`,`تجاوز الملف الشخصي`],"Import model":[`导入模型`,`نموذج الاستيراد`],"Saved external API":[`已保存的外部 API`,`واجهة API الخارجية المحفوظة`],"Preview (dry run)":[`预览(不写入)`,`معاينة (تشغيل تجريبي)`],"Start import":[`开始导入`,`بدء الاستيراد`],"Cancel after current unit":[`完成当前单元后取消`,`الإلغاء بعد الوحدة الحالية`],"Directory maps must be old=new, one per line":[`目录映射须为 旧=新,每行一条`,`يجب أن يكون تعيين المجلدات بصيغة قديم=جديد، واحد في كل سطر`],running:[`运行中`,`جارٍ التشغيل`],cancelling:[`正在取消`,`جارٍ الإلغاء`],cancelled:[`已取消`,`أُلغي`],done:[`已完成`,`اكتمل`],failed:[`失败`,`فشل`],units:[`单元`,`وحدات`],"List sessions":[`列出会话`,`عرض الجلسات`],"Refresh list":[`刷新列表`,`تحديث القائمة`],"Advanced options":[`高级选项`,`خيارات متقدمة`],"Hide advanced options":[`隐藏高级选项`,`إخفاء الخيارات المتقدمة`],"default location":[`默认位置`,`الموقع الافتراضي`],"Prompt date from":[`提示日期起`,`تاريخ الطلب من`],"Prompt date to":[`提示日期止`,`تاريخ الطلب إلى`],"Prompt dates are inclusive, use your time zone, and filter the turns inside each session, not the list. Empty dates include every turn.":[`提示日期包含首尾两天,按你所在时区计算,只筛选每个会话中的对话轮次,不筛选列表。留空则包含所有轮次。`,`تواريخ الطلب شاملة لليومين، وتُحسب بمنطقتك الزمنية، وتُصفّي الأدوار داخل كل جلسة لا القائمة. تركها فارغة يشمل كل الأدوار.`],"Sessions with directories that no longer exist":[`目录已不存在的会话`,`جلسات لم تعد مجلداتها موجودة`],"Show them and add a directory map":[`显示它们并添加目录映射`,`اعرضها وأضف تعيين مجلد`],Selected:[`已选择`,`المحدد`],"Select all matching":[`选择所有匹配项`,`تحديد كل المطابق`],"Clear selection":[`清除选择`,`مسح التحديد`],Select:[`选择`,`تحديد`],Created:[`创建时间`,`تاريخ الإنشاء`],"Resolved by":[`解析方式`,`طريقة التحديد`],recorded:[`记录的目录`,`المجلد المسجّل`],mapped:[`目录映射`,`تعيين مجلد`],"project root":[`项目根目录`,`جذر المشروع`],missing:[`缺失`,`مفقود`],"No sessions match.":[`没有匹配的会话。`,`لا توجد جلسات مطابقة.`],Previous:[`上一页`,`السابق`],Next:[`下一页`,`التالي`],None:[`无`,`لا شيء`],"Configured, not tested.":[`已配置,未测试。`,`مُعدّ، لم يُختبر.`],"Test models in Health":[`在健康检查中测试模型`,`اختبر النماذج في قسم السلامة`],Import:[`导入`,`استيراد`],sessions:[`个会话`,`جلسات`],"List sessions first.":[`请先列出会话。`,`اعرض الجلسات أولاً.`],"Options changed. Refresh the session list first.":[`选项已更改。请先刷新会话列表。`,`تغيّرت الخيارات. حدّث قائمة الجلسات أولاً.`],"Select at least one session.":[`请至少选择一个会话。`,`حدّد جلسة واحدة على الأقل.`],"Select at most 1000 sessions one by one, or use Select all matching.":[`逐个选择最多 1000 个会话,或使用“选择所有匹配项”。`,`حدّد 1000 جلسة كحد أقصى واحدة تلو الأخرى، أو استخدم «تحديد كل المطابق».`],"Model readiness is unknown. Reload the page.":[`无法确定模型是否就绪。请重新加载页面。`,`حالة جاهزية النموذج غير معروفة. أعد تحميل الصفحة.`],"No import model is ready. Connect a model in OpenCode or complete the external API settings.":[`没有可用的导入模型。请在 OpenCode 中连接模型,或补全外部 API 设置。`,`لا يوجد نموذج استيراد جاهز. اربط نموذجاً في OpenCode أو أكمل إعدادات واجهة API الخارجية.`],"The external API is not ready":[`外部 API 未就绪`,`واجهة API الخارجية غير جاهزة`],"The external API has no memoryModel in the global config":[`全局配置中缺少外部 API 的 memoryModel`,`لا يحتوي الإعداد العام على memoryModel لواجهة API الخارجية`],"The external API has no memoryApiUrl in the global config":[`全局配置中缺少外部 API 的 memoryApiUrl`,`لا يحتوي الإعداد العام على memoryApiUrl لواجهة API الخارجية`],"The external API key is missing in the OpenCode process; check memoryApiKey and its env:// or file:// source":[`OpenCode 进程中缺少外部 API 密钥;请检查 memoryApiKey 及其 env:// 或 file:// 来源`,`مفتاح واجهة API الخارجية غير موجود في عملية OpenCode؛ تحقّق من memoryApiKey ومصدره env:// أو file://`],"The memoryProvider in the global config is not supported for imports":[`全局配置中的 memoryProvider 不支持导入`,`مزوّد memoryProvider في الإعداد العام غير مدعوم للاستيراد`],"Pi sessions folder or one .jsonl session file":[`Pi 会话文件夹或单个 .jsonl 会话文件`,`مجلد جلسات Pi أو ملف جلسة ‎.jsonl واحد`],"OpenCode database file":[`OpenCode 数据库文件`,`ملف قاعدة بيانات OpenCode`],"Absolute path, for example on a mounted volume":[`绝对路径,例如已挂载卷上的路径`,`مسار مطلق، مثلاً على وحدة تخزين مركّبة`],"Use this source":[`使用此来源`,`استخدم هذا المصدر`],Browse:[`浏览`,`استعراض`],"Use the default location":[`使用默认位置`,`استخدم الموقع الافتراضي`],"Chosen source":[`已选来源`,`المصدر المختار`],Choose:[`选择`,`اختيار`],"Only the first 500 entries are shown.":[`仅显示前 500 项。`,`تُعرض أول 500 عنصر فقط.`],"Reload the page before saving again.":[`再次保存前请重新加载页面。`,`أعد تحميل الصفحة قبل الحفظ مرة أخرى.`],"The current settings could not be reloaded. Reload the page.":[`无法重新加载当前设置。请重新加载页面。`,`تعذّر إعادة تحميل الإعدادات الحالية. أعد تحميل الصفحة.`],Log:[`日志`,`السجل`],"Capture attempts only":[`仅显示捕获尝试`,`محاولات الالتقاط فقط`],Refresh:[`刷新`,`تحديث`],"Copy log path":[`复制日志路径`,`نسخ مسار السجل`],"Log lines":[`日志行`,`أسطر السجل`],"No log has been written yet.":[`尚未写入日志。`,`لم يُكتب سجل بعد.`],"Loading log…":[`正在加载日志…`,`جارٍ تحميل السجل…`],"External API":[`外部 API`,`واجهة API الخارجية`],"An OpenAI- or Anthropic-compatible endpoint that either host can use for capture and backfill.":[`兼容 OpenAI 或 Anthropic 的端点,两个宿主都可用于捕获和回填。`,`نقطة نهاية متوافقة مع OpenAI أو Anthropic يمكن لأي مضيف استخدامها للالتقاط والاستيراد.`],Provider:[`提供商`,`المزوّد`],"API URL":[`API 地址`,`عنوان API`],"API key":[`API 密钥`,`مفتاح API`],"Save endpoint":[`保存端点`,`حفظ نقطة النهاية`],"Nothing to save.":[`没有需要保存的内容。`,`لا يوجد ما يُحفظ.`],"Key source":[`密钥来源`,`مصدر المفتاح`],"environment variable":[`环境变量`,`متغير البيئة`],"key file":[`密钥文件`,`ملف المفتاح`],"set in the config file":[`在配置文件中设置`,`مضبوط في ملف الإعدادات`],"resolves in the web app":[`可在网页应用中解析`,`يُقرأ في تطبيق الويب`],"does not resolve in the web app":[`无法在网页应用中解析`,`لا يُقرأ في تطبيق الويب`],"A login web app does not see variables set only in a shell profile; a key file works everywhere.":[`登录时启动的网页应用看不到仅在 shell 配置文件中设置的变量;密钥文件在任何地方都可用。`,`تطبيق الويب الذي يبدأ عند تسجيل الدخول لا يرى المتغيرات المضبوطة في ملف الصدفة فقط؛ ملف المفتاح يعمل في كل مكان.`],"The key does not resolve in this web app. Save it to a key file instead.":[`此网页应用无法解析该密钥。请改为保存到密钥文件。`,`لا يُقرأ المفتاح في تطبيق الويب هذا. احفظه في ملف مفتاح بدلاً من ذلك.`],"Environment variable":[`环境变量`,`متغير البيئة`],"Key file":[`密钥文件`,`ملف المفتاح`],"Save key to a private file":[`将密钥保存到私有文件`,`حفظ المفتاح في ملف خاص`],"Variable name":[`变量名`,`اسم المتغير`],"Key file path":[`密钥文件路径`,`مسار ملف المفتاح`],"Key file name":[`密钥文件名`,`اسم ملف المفتاح`],"The key is written to ~/.config/omms/secrets/ readable only by you. It is never shown again.":[`密钥写入 ~/.config/omms/secrets/,仅你可读,之后不再显示。`,`يُكتب المفتاح في ~/.config/omms/secrets/ ولا يقرؤه غيرك، ولن يُعرض مرة أخرى.`],"Save key source":[`保存密钥来源`,`حفظ مصدر المفتاح`],"Key source saved.":[`密钥来源已保存。`,`تم حفظ مصدر المفتاح.`],"A key file with this name exists. Replace it?":[`已存在同名密钥文件。要替换吗?`,`يوجد ملف مفتاح بهذا الاسم. هل تريد استبداله؟`],"Enter a variable name such as ZAI_API_KEY":[`请输入变量名,例如 ZAI_API_KEY`,`أدخل اسم متغير مثل ZAI_API_KEY`],"Enter the path of a key file":[`请输入密钥文件路径`,`أدخل مسار ملف المفتاح`],"Name the key file with letters, digits, dashes, or underscores":[`密钥文件名只能包含字母、数字、短横线或下划线`,`سمِّ ملف المفتاح بحروف أو أرقام أو شرطات أو شرطات سفلية`],"Paste the key":[`请粘贴密钥`,`الصق المفتاح`],Test:[`测试`,`اختبار`],"Test call succeeded":[`测试调用成功`,`نجح استدعاء الاختبار`],"Test call failed":[`测试调用失败`,`فشل استدعاء الاختبار`],"External API needs":[`外部 API 缺少`,`تحتاج واجهة API الخارجية إلى`],"Automatic import makes model calls. A model change applies at the next run. Turning it off stops a running import after the current exchange.":[`自动导入会调用模型。模型更改在下次运行时生效。关闭后,正在进行的导入会在当前对话完成后停止。`,`يستدعي الاستيراد التلقائي النماذج. يسري تغيير النموذج في التشغيل التالي. إيقافه يوقف الاستيراد الجاري بعد المحادثة الحالية.`],"Saved. Changes apply at the next run.":[`已保存。更改在下次运行时生效。`,`تم الحفظ. تسري التغييرات في التشغيل التالي.`],paused:[`已暂停`,`متوقف مؤقتاً`],"started from auto":[`自动启动`,`بدأ تلقائياً`],"started from web":[`从网页启动`,`بدأ من الويب`],"started from cli":[`从终端启动`,`بدأ من الطرفية`],"started from slash":[`从斜杠命令启动`,`بدأ من أمر الشرطة المائلة`],"Minutes left":[`剩余分钟`,`الدقائق المتبقية`],unknown:[`未知`,`غير معروف`],about:[`约`,`حوالي`],"Run now":[`立即运行`,`تشغيل الآن`],Pause:[`暂停`,`إيقاف مؤقت`],Resume:[`继续`,`استئناف`],"Pausing after the current exchange.":[`将在当前对话完成后暂停。`,`سيتوقف مؤقتاً بعد المحادثة الحالية.`],"Backfill started.":[`回填已开始。`,`بدأ الاستيراد.`],"Directory maps":[`目录映射`,`خرائط المجلدات`],"Map a directory that was moved or deleted to the project it belongs to. A change applies to the next import or backfill run.":[`将已移动或删除的目录映射到其所属项目。更改在下次导入或回填时生效。`,`اربط مجلداً نُقل أو حُذف بالمشروع الذي ينتمي إليه. يسري التغيير في الاستيراد التالي.`],"Saved maps":[`已保存的映射`,`الخرائط المحفوظة`],Keep:[`保留`,`إبقاء`],Remove:[`移除`,`إزالة`],"Unresolved directories":[`无法解析的目录`,`مجلدات غير محلولة`],"No unresolved directories in the latest run.":[`最近一次运行没有无法解析的目录。`,`لا توجد مجلدات غير محلولة في آخر تشغيل.`],"No suggestion found.":[`未找到建议。`,`لم يُعثر على اقتراح.`],"Target directory":[`目标目录`,`المجلد الهدف`],"Use this map":[`使用此映射`,`استخدام هذه الخريطة`],"Save maps":[`保存映射`,`حفظ الخرائط`],"Saved. Maps apply to the next import or backfill run.":[`已保存。映射在下次导入或回填时生效。`,`تم الحفظ. تسري الخرائط في الاستيراد التالي.`],"Running version":[`运行版本`,`الإصدار الجاري`],"Global command":[`全局命令`,`الأمر العام`],"not installed globally":[`未全局安装`,`غير مثبت عمومياً`],"The global command's version differs from the running OMMS. Upgrade it:":[`全局命令的版本与正在运行的 OMMS 不同。请升级:`,`يختلف إصدار الأمر العام عن OMMS الجاري. حدّثه:`],"A global install is optional but recommended: the login item and the terminal commands then run without npx.":[`全局安装是可选的,但建议安装:这样登录项和终端命令无需 npx 即可运行。`,`التثبيت العام اختياري لكنه موصى به: يعمل عنصر تسجيل الدخول وأوامر الطرفية حينها دون npx.`]};function sm(e,t){return t===`en`?e:om[e]?.[t===`zh`?0:1]??e}function cm(){let{language:e}=cr();return t=>sm(t,e)}function lm(e,t){if(e===`env`){let n=(t.name??``).trim();if(!/^[A-Za-z_][A-Za-z0-9_]*$/.test(n))throw Error(`Enter a variable name such as ZAI_API_KEY`);return{source:e,name:n}}if(e===`file`){let n=(t.path??``).trim();if(!n)throw Error(`Enter the path of a key file`);return{source:e,path:n}}let n=(t.name??``).trim()||`memory-api`;if(!/^[A-Za-z0-9_-]{1,64}$/.test(n))throw Error(`Name the key file with letters, digits, dashes, or underscores`);if(!(t.value??``).trim())throw Error(`Paste the key`);return{source:e,name:n,value:t.value??``,replace:t.replace===!0}}function um(e){let t=[],n=e.memoryProvider===`orcarouter`;return!n&&!e.memoryModel&&t.push(`memoryModel`),!n&&!e.memoryApiUrl&&t.push(`memoryApiUrl`),e.keySet||t.push(`memoryApiKey`),t}function dm(e,t){if(t===`inherit`||t===`external`)return{[`${e}Model`]:t};let n=t.indexOf(`/`);if(n<1||n===t.length-1)throw Error(`Enter a model as provider/model.`);return{[`${e}Provider`]:t.slice(0,n),[`${e}Model`]:t.slice(n+1)}}function fm(e){return e.global===null?{kind:`missing`,command:`npm i -g om-memory-system`}:e.global===e.running?{kind:`same`,command:null}:{kind:`different`,command:`npm i -g om-memory-system@latest`}}function pm(e,t,n){let r=new Map;for(let n of e)t.has(n.from)||r.set(n.from,n);for(let e of n){let t=e.target.trim();e.accepted&&t&&r.set(e.directory,{from:e.directory,to:t})}return[...r.values()]}function mm(){let e=cm(),[t,n]=(0,_.useState)(),[r,i]=(0,_.useState)({}),[a,o]=(0,_.useState)(``),[s,c]=(0,_.useState)(!1);(0,_.useEffect)(()=>{let e=!0,t=nm();$(`/api/settings`).then(r=>{e&&t.isCurrent()&&n(r)}).catch(t=>{e&&o(t.message)});for(let t of[`opencode`,`pi`])$(`/api/settings/models?host=${t}`).then(n=>{e&&i(e=>({...e,[t]:n}))}).catch(()=>{e&&i(e=>({...e,[t]:{available:!1}}))});let r=im(t=>{e&&n(t)});return()=>{e=!1,r()}},[]);async function l(n,r){if(!t)return;let i;try{i=dm(n,r)}catch(t){o(e(t.message));return}c(!0);let a,s;try{a=await $(`/api/settings`,{method:`PATCH`,body:JSON.stringify({edits:i,revision:t.revision})})}catch(e){s=e}let l=await rm()!==null;if(c(!1),a){let t=e(a.migratedLegacy?`Saved. OMMS now reads ~/.config/omms/omms.jsonc. The legacy config was kept.`:`Saved. New capture and profile work uses these settings.`);o(l?t:`${t} ${e(`Reload the page before saving again.`)}`)}else o(am(s?.message??``,e(l?`Current settings were reloaded; check the values and save again.`:`The current settings could not be reloaded. Reload the page.`)))}let u=um({memoryProvider:t?.settings.memoryProvider?.globalValue,memoryModel:t?.settings.memoryModel?.globalValue,memoryApiUrl:t?.settings.memoryApiUrl?.globalValue,keySet:!!t?.secrets.memoryApiKey?.set});return(0,V.jsxs)(`section`,{className:`space-y-3 rounded-xl border border-border bg-card p-4`,"aria-label":e(`Models`),children:[(0,V.jsx)(`h2`,{className:`text-lg font-medium`,children:e(`Models`)}),(0,V.jsxs)(`p`,{className:`text-sm text-muted-foreground`,children:[e(`External API fallback`),`: `,t?.fallback.model??e(`none`),` (`,e(t?.fallback.configured?`ready`:`not ready`),`).`]}),[`opencode`,`pi`].map(e=>(0,V.jsx)(hm,{host:e,model:t?.settings[`${e}Model`],provider:t?.settings[`${e}Provider`],effective:e===`pi`?t?.effective.pi.kind:t?.effective.opencode.mode??`unready`,list:r[e],externalMissing:u,busy:s||!t,onSave:t=>l(e,t)},e)),(0,V.jsxs)(`div`,{className:`text-xs text-muted-foreground`,children:[e(`Credentials (values hidden)`),`:`,` `,t&&Object.entries(t.secrets).map(([t,n])=>(0,V.jsxs)(`span`,{className:`me-3`,children:[t,`: `,n.set?`${e(`set`)} (${n.source})`:e(`not set`)]},t))]}),a&&(0,V.jsx)(`p`,{role:`status`,className:`text-sm`,children:a})]})}function hm({host:e,model:t,provider:n,effective:r,list:i,externalMissing:a,busy:o,onSave:s}){let c=cm(),l=t?.globalValue===`inherit`||!t?.globalValue?`inherit`:t.globalValue===`external`?`external`:`${n?.globalValue??``}/${t.globalValue}`,[u,d]=(0,_.useState)(),[f,p]=(0,_.useState)(),[m,h]=(0,_.useState)(),g=u??(l===`inherit`||l===`external`?l:`manual`),v=i?.models??[],y=l===`inherit`||l===`external`?``:l,b=m||y||(v[0]?`${v[0].provider}/${v[0].model}`:`typed`),x=!v.some(e=>`${e.provider}/${e.model}`===b),S=f??y;return(0,V.jsxs)(`div`,{className:`space-y-2 rounded-lg border border-border p-3`,children:[(0,V.jsx)(`h3`,{className:`font-medium`,children:e===`pi`?`Pi`:`OpenCode`}),(0,V.jsxs)(`p`,{className:`text-xs text-muted-foreground`,children:[c(`Effective model`),`:`,` `,t?.value===`external`&&r===`manual`?c(`external API`):t?.value===`inherit`||r===`session`?c(`session`):r===`manual`?c(`external API`):r===`unready`?c(`unavailable`):`${n?.value??``}/${t?.value??``}`]}),(t?.source===`project`||n?.source===`project`)&&(0,V.jsx)(`p`,{className:`text-xs text-amber-600`,children:c(`Project override is active. Changes to the global file may not take effect here.`)}),(0,V.jsx)(`label`,{className:`block text-sm`,htmlFor:`${e}-model`,children:c(`Model choice`)}),(0,V.jsxs)(un,{id:`${e}-model`,className:`w-full rounded-lg border border-border bg-background p-2 text-sm`,value:g,onChange:e=>d(e.target.value),children:[(0,V.jsx)(`option`,{value:`inherit`,children:c(`Session model`)}),(0,V.jsx)(`option`,{value:`manual`,children:c(`Manual model`)}),(0,V.jsx)(`option`,{value:`external`,disabled:a.length>0,children:c(`External API`)})]}),a.length>0&&(0,V.jsxs)(`p`,{className:`text-xs text-muted-foreground`,children:[c(`External API needs`),`: `,a.join(`, `)]}),g===`manual`&&i?.available&&v.length>0&&(0,V.jsxs)(un,{className:`w-full rounded-lg border border-border bg-background p-2 text-sm`,"aria-label":`${e===`pi`?`Pi`:`OpenCode`} ${c(`Model`)}`,value:x?`typed`:b,onChange:e=>h(e.target.value),children:[v.map(e=>(0,V.jsxs)(`option`,{value:`${e.provider}/${e.model}`,children:[e.name,` (`,e.provider,`/`,e.model,`)`]},`${e.provider}/${e.model}`)),(0,V.jsx)(`option`,{value:`typed`,children:c(`Manual provider/model`)})]}),g===`manual`&&x&&(0,V.jsx)(`input`,{className:`w-full rounded-lg border border-border bg-background p-2 text-sm`,"aria-label":`${e} ${c(`Manual provider/model`)}`,placeholder:`provider/model`,value:S,onChange:e=>{p(e.target.value),h(`typed`)}}),g===`manual`&&i&&!i.available&&(0,V.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:i.reason?c(i.reason):c(`Model list unavailable. Enter provider/model manually.`)}),(0,V.jsx)(`button`,{type:`button`,className:`rounded-lg border border-border px-3 py-1.5 text-sm`,disabled:o,onClick:()=>s(g===`manual`?x?S.trim():b:g),children:c(`Save model`)})]})}var gm=[`openai-chat`,`openai-responses`,`anthropic`,`minimax`,`orcarouter`,`google-gemini`];function _m(){let e=cm(),[t,n]=(0,_.useState)(),[r,i]=(0,_.useState)({}),[a,o]=(0,_.useState)(`file`),[s,c]=(0,_.useState)({name:``,path:``,value:``}),[l,u]=(0,_.useState)(``),[d,f]=(0,_.useState)(!1);(0,_.useEffect)(()=>{let e=!0,t=nm();$(`/api/settings`).then(r=>{e&&t.isCurrent()&&n(r)}).catch(t=>{e&&u(t.message)});let r=im(t=>{e&&n(t)});return()=>{e=!1,r()}},[]);let p=e=>String(t?.settings[e]?.globalValue??``),m=e=>r[e]??p(e);async function h(){if(!t)return;let n={};for(let e of[`memoryProvider`,`memoryApiUrl`,`memoryModel`]){let t=m(e).trim();t&&t!==p(e)&&(n[e]=t)}if(!Object.keys(n).length){u(e(`Nothing to save.`));return}f(!0);try{await $(`/api/settings`,{method:`PATCH`,body:JSON.stringify({edits:n,revision:t.revision})}),i({}),u(e(`Saved. New capture and profile work uses these settings.`))}catch(e){u(e.message)}await rm(),f(!1)}async function g(n=!1){if(!t)return;let r;try{r=lm(a,{...s,replace:n})}catch(t){u(e(t.message));return}f(!0);try{await $(`/api/settings/external-api/key`,{method:`POST`,body:JSON.stringify({...r,revision:t.revision})}),c(e=>({...e,value:``})),u(e(`Key source saved.`))}catch(t){let r=t.message;if(a===`paste`&&!n&&r.includes(`Confirm to replace`)){f(!1),window.confirm(e(`A key file with this name exists. Replace it?`))&&await g(!0);return}u(r)}await rm(),f(!1)}async function v(){f(!0);try{let t=await $(`/api/settings/external-api/test`,{method:`POST`,body:`{}`});u(t.ok?`${e(`Test call succeeded`)}: ${t.model??``}`:`${e(`Test call failed`)}: ${t.error??``}`)}catch(e){u(e.message)}f(!1)}let y=t?.externalKey;return(0,V.jsxs)(`section`,{className:`space-y-3 rounded-xl border border-border bg-card p-4`,"aria-label":e(`External API`),children:[(0,V.jsx)(`h2`,{className:`text-lg font-medium`,children:e(`External API`)}),(0,V.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:e(`An OpenAI- or Anthropic-compatible endpoint that either host can use for capture and backfill.`)}),(0,V.jsxs)(`label`,{className:`block text-sm`,children:[e(`Provider`),(0,V.jsx)(un,{"aria-label":e(`Provider`),className:`mt-1 block w-full rounded border border-border bg-background p-2`,value:m(`memoryProvider`)||`openai-chat`,onChange:e=>i({...r,memoryProvider:e.target.value}),children:gm.map(e=>(0,V.jsx)(`option`,{value:e,children:e},e))})]}),(0,V.jsxs)(`label`,{className:`block text-sm`,children:[e(`API URL`),(0,V.jsx)(`input`,{className:`mt-1 block w-full rounded border border-border bg-background p-2`,placeholder:`https://api.example.com/v1`,value:m(`memoryApiUrl`),onChange:e=>i({...r,memoryApiUrl:e.target.value})})]}),(0,V.jsxs)(`label`,{className:`block text-sm`,children:[e(`Model`),(0,V.jsx)(`input`,{className:`mt-1 block w-full rounded border border-border bg-background p-2`,placeholder:`glm-5-turbo`,value:m(`memoryModel`),onChange:e=>i({...r,memoryModel:e.target.value})})]}),(0,V.jsx)(`button`,{type:`button`,className:`rounded border border-border px-3 py-1.5 text-sm`,disabled:d||!t,onClick:()=>void h(),children:e(`Save endpoint`)}),(0,V.jsxs)(`fieldset`,{className:`space-y-2 rounded-lg border border-border p-3 text-sm`,children:[(0,V.jsx)(`legend`,{className:`px-1 font-medium`,children:e(`API key`)}),(0,V.jsxs)(`p`,{className:`text-xs text-muted-foreground`,children:[e(`Key source`),`:`,` `,y?.source===`env`?`${e(`environment variable`)} ${y.reference}`:y?.source===`file`?`${e(`key file`)} ${y.reference}`:y?.source===`literal`?e(`set in the config file`):e(`not set`),y?.source&&` · ${e(y.resolvesInWebApp?`resolves in the web app`:`does not resolve in the web app`)}`]}),y?.source===`env`&&(0,V.jsx)(`p`,{className:`text-xs text-amber-600`,children:e(`A login web app does not see variables set only in a shell profile; a key file works everywhere.`)}),y?.source&&!y.resolvesInWebApp&&(0,V.jsx)(`p`,{className:`text-xs text-amber-600`,children:e(`The key does not resolve in this web app. Save it to a key file instead.`)}),[[`env`,`Environment variable`],[`file`,`Key file`],[`paste`,`Save key to a private file`]].map(([t,n])=>(0,V.jsxs)(`label`,{className:`flex items-center gap-2`,children:[(0,V.jsx)(`input`,{type:`radio`,name:`omms-key-source`,checked:a===t,onChange:()=>o(t)}),e(n)]},t)),a===`env`&&(0,V.jsx)(`input`,{"aria-label":e(`Variable name`),className:`w-full rounded border border-border bg-background p-2`,placeholder:`ZAI_API_KEY`,value:s.name,onChange:e=>c({...s,name:e.target.value})}),a===`file`&&(0,V.jsx)(`input`,{"aria-label":e(`Key file path`),className:`w-full rounded border border-border bg-background p-2`,placeholder:`~/.config/omms/secrets/zai.key`,value:s.path,onChange:e=>c({...s,path:e.target.value})}),a===`paste`&&(0,V.jsxs)(V.Fragment,{children:[(0,V.jsx)(`input`,{"aria-label":e(`Key file name`),className:`w-full rounded border border-border bg-background p-2`,placeholder:`memory-api`,value:s.name,onChange:e=>c({...s,name:e.target.value})}),(0,V.jsx)(`input`,{"aria-label":e(`API key`),type:`password`,autoComplete:`off`,className:`w-full rounded border border-border bg-background p-2`,value:s.value,onChange:e=>c({...s,value:e.target.value})}),(0,V.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:e(`The key is written to ~/.config/omms/secrets/ readable only by you. It is never shown again.`)})]}),(0,V.jsx)(`button`,{type:`button`,className:`rounded border border-border px-3 py-1.5`,disabled:d||!t,onClick:()=>void g(),children:e(`Save key source`)})]}),(0,V.jsx)(`button`,{type:`button`,className:`rounded border border-border px-3 py-1.5 text-sm`,disabled:d,onClick:()=>void v(),children:e(`Test`)}),l&&(0,V.jsx)(`p`,{role:`status`,className:`text-sm`,children:l})]})}function vm(){let e=cm(),[t,n]=(0,_.useState)(),[r,i]=(0,_.useState)(),[a,o]=(0,_.useState)(new Set),[s,c]=(0,_.useState)({}),[l,u]=(0,_.useState)(``),[d,f]=(0,_.useState)(!1),p=()=>$(`/api/settings/import-maps`).then(e=>{i(e),o(new Set),c({})}).catch(e=>u(e.message));(0,_.useEffect)(()=>{let e=!0;$(`/api/settings`).then(t=>{e&&n(t)}).catch(t=>{e&&u(t.message)}),p();let t=im(t=>{e&&n(t)});return()=>{e=!1,t()}},[]);function m(e,t){c(n=>{let r=n[e.directory]??{directory:e.directory,target:e.suggestion??``,accepted:!1};return{...n,[e.directory]:{...r,...t}}})}async function h(){if(!t||!r)return;let n=pm(r.saved,a,Object.values(s));f(!0);try{await $(`/api/settings`,{method:`PATCH`,body:JSON.stringify({edits:{importPathMaps:n},revision:t.revision})}),u(e(`Saved. Maps apply to the next import or backfill run.`))}catch(e){u(e.message)}await rm(),await p(),f(!1)}return(0,V.jsxs)(`section`,{id:`directory-maps`,className:`space-y-3 rounded-xl border border-border bg-card p-4`,"aria-label":e(`Directory maps`),children:[(0,V.jsx)(`h2`,{className:`text-lg font-medium`,children:e(`Directory maps`)}),(0,V.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:e(`Map a directory that was moved or deleted to the project it belongs to. A change applies to the next import or backfill run.`)}),(0,V.jsx)(`h3`,{className:`font-medium`,children:e(`Saved maps`)}),!r?.saved.length&&(0,V.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:e(`none`)}),(0,V.jsx)(`ul`,{className:`space-y-1 text-sm`,children:r?.saved.map(t=>(0,V.jsxs)(`li`,{className:`flex flex-wrap items-center gap-2`,children:[(0,V.jsxs)(`span`,{className:a.has(t.from)?`line-through`:``,children:[(0,V.jsx)(`code`,{children:t.from}),` → `,(0,V.jsx)(`code`,{children:t.to})]}),(0,V.jsx)(`button`,{type:`button`,className:`rounded border border-border px-2 py-0.5 text-xs`,onClick:()=>o(e=>{let n=new Set(e);return n.has(t.from)?n.delete(t.from):n.add(t.from),n}),children:a.has(t.from)?e(`Keep`):e(`Remove`)})]},t.from))}),[`pi`,`opencode`].map(t=>(0,V.jsxs)(`div`,{className:`space-y-2`,children:[(0,V.jsxs)(`h3`,{className:`font-medium`,children:[t===`pi`?`Pi`:`OpenCode`,`: `,e(`Unresolved directories`)]}),!r?.[t].length&&(0,V.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:e(`No unresolved directories in the latest run.`)}),r?.[t].map(t=>{let n=s[t.directory],r=n?.target??t.suggestion??``;return(0,V.jsxs)(`div`,{className:`space-y-1 rounded-lg border border-border p-2 text-sm`,children:[(0,V.jsxs)(`p`,{children:[(0,V.jsx)(`code`,{children:t.directory}),` · `,t.sessions,` `,e(`sessions`)]}),!t.suggestion&&(0,V.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:e(`No suggestion found.`)}),(0,V.jsx)(`input`,{"aria-label":`${t.directory} ${e(`Target directory`)}`,className:`w-full rounded border border-border bg-background p-2`,placeholder:e(`Target directory`),value:r,onChange:e=>m(t,{target:e.target.value})}),(0,V.jsxs)(`label`,{className:`flex items-center gap-2`,children:[(0,V.jsx)(`input`,{type:`checkbox`,checked:n?.accepted??!1,onChange:e=>m(t,{accepted:e.target.checked})}),e(`Use this map`)]})]},t.directory)})]},t)),(0,V.jsx)(`button`,{type:`button`,className:`rounded border border-border px-3 py-1.5 text-sm`,disabled:d||!t||!r,onClick:()=>void h(),children:e(`Save maps`)}),l&&(0,V.jsx)(`p`,{role:`status`,className:`text-sm`,children:l})]})}var ym=`overflow-x-auto rounded-lg border border-border`,bm=`px-3 py-2 text-start font-medium`,xm=`bg-muted/50 text-xs text-muted-foreground`,Sm=`px-3 py-2 text-start font-medium`,Cm=`border-t border-border transition-colors hover:bg-muted/30`,wm=`px-3 py-1.5`;function Tm(){let e=cm(),[t,n]=(0,_.useState)(7),[r,i]=(0,_.useState)(),[a,o]=(0,_.useState)([]),[s,c]=(0,_.useState)(),[l,u]=(0,_.useState)(``),[d,f]=(0,_.useState)(``),p=(0,_.useCallback)(async e=>{let t=nm();try{let[n,r,a]=await Promise.all([$(`/api/settings/diagnostics?days=${e}`),$(`/api/settings/traces`),$(`/api/settings`)]);i(n),o(r.traces),t.isCurrent()&&c(a),f(``)}catch(e){f(e.message)}},[]);(0,_.useEffect)(()=>{p(t)},[t,p]),(0,_.useEffect)(()=>im(e=>c(e)),[]);async function m(n){if(!s)return;let r;try{await $(`/api/settings`,{method:`PATCH`,body:JSON.stringify({revision:s.revision,edits:n})})}catch(e){r=e}let i=await rm()!==null;if(!r){await p(t),i||f(e(`Reload the page before saving again.`));return}f(am(r.message,e(i?`Current settings were reloaded; check the values and save again.`:`The current settings could not be reloaded. Reload the page.`)))}async function h(e){try{let t=await $(`/api/settings/traces/${encodeURIComponent(e)}`);u(t.content)}catch(e){f(e.message)}}async function g(n){if(window.confirm(`${e(`Delete`)} ${n}?`))try{await $(`/api/settings/traces/${encodeURIComponent(n)}`,{method:`DELETE`}),u(``),await p(t)}catch(e){f(e.message)}}let v=s?.settings.captureTrace;return(0,V.jsxs)(`section`,{className:`space-y-4 rounded-xl border border-border bg-card p-4`,"aria-label":e(`Capture diagnostics`),children:[(0,V.jsx)(`h2`,{className:`text-lg font-medium`,children:e(`Capture diagnostics`)}),(0,V.jsxs)(`label`,{className:`flex items-center gap-2 pb-2 text-sm`,children:[e(`Time range`),(0,V.jsxs)(un,{"aria-label":e(`Time range`),className:`rounded-lg border border-border bg-background px-2 py-1`,value:t,onChange:e=>n(Number(e.target.value)),children:[(0,V.jsx)(`option`,{value:1,children:e(`24 hours`)}),(0,V.jsx)(`option`,{value:7,children:e(`7 days`)}),(0,V.jsx)(`option`,{value:30,children:e(`30 days`)}),(0,V.jsx)(`option`,{value:90,children:e(`90 days`)})]})]}),d&&(0,V.jsx)(`p`,{role:`alert`,children:d}),(0,V.jsx)(`div`,{className:ym,children:(0,V.jsxs)(`table`,{className:`w-full text-sm`,children:[(0,V.jsx)(`caption`,{className:bm,children:e(`Outcomes by model`)}),(0,V.jsx)(`thead`,{className:xm,children:(0,V.jsx)(`tr`,{children:[`Host / model`,`Saved`,`Skipped`,`Failed`,`Total`].map((t,n)=>(0,V.jsx)(`th`,{scope:`col`,className:B(Sm,n>0&&`text-end`),children:e(t)},t))})}),(0,V.jsx)(`tbody`,{children:r?.byModel.map((e,t)=>(0,V.jsxs)(`tr`,{className:Cm,children:[(0,V.jsxs)(`td`,{className:wm,children:[(0,V.jsx)(`span`,{className:`text-muted-foreground`,children:e.host}),` `,(0,V.jsx)(`span`,{className:`font-mono text-xs`,children:e.provider||e.model?`${e.provider??``}/${e.model??``}`:`—`})]}),[e.saved,e.skipped,e.failed].map((t,n)=>(0,V.jsxs)(`td`,{className:B(wm,`text-end tabular-nums`),children:[t,` `,(0,V.jsxs)(`span`,{className:`text-xs text-muted-foreground`,children:[Math.round(100*t/e.total),`%`]})]},n)),(0,V.jsx)(`td`,{className:B(wm,`text-end font-medium tabular-nums`),children:e.total})]},`${e.host}-${e.provider}-${e.model}-${t}`))})]})}),(0,V.jsx)(`div`,{className:ym,children:(0,V.jsxs)(`table`,{className:`w-full text-sm`,children:[(0,V.jsx)(`caption`,{className:bm,children:e(`Failure reasons`)}),(0,V.jsx)(`thead`,{className:xm,children:(0,V.jsxs)(`tr`,{children:[(0,V.jsx)(`th`,{scope:`col`,className:Sm,children:e(`Host`)}),(0,V.jsx)(`th`,{scope:`col`,className:Sm,children:e(`Reason`)}),(0,V.jsx)(`th`,{scope:`col`,className:B(Sm,`text-end`),children:e(`Total`)})]})}),(0,V.jsx)(`tbody`,{children:r?.byReason.map((e,t)=>(0,V.jsxs)(`tr`,{className:Cm,children:[(0,V.jsx)(`td`,{className:B(wm,`text-muted-foreground`),children:e.host}),(0,V.jsx)(`td`,{className:B(wm,`font-mono text-xs`),children:e.reason}),(0,V.jsx)(`td`,{className:B(wm,`text-end tabular-nums`),children:e.count})]},`${e.host}-${e.reason}-${t}`))})]})}),(0,V.jsx)(`div`,{className:B(ym,`max-h-72 overflow-y-auto`),children:(0,V.jsxs)(`table`,{className:`w-full min-w-max text-sm`,children:[(0,V.jsx)(`caption`,{className:bm,children:e(`Recent attempts`)}),(0,V.jsx)(`thead`,{className:B(xm,`sticky top-0`),children:(0,V.jsx)(`tr`,{children:[`Time`,`Host`,`Source`,`Session`,`Path`,`Model`,`Stop`,`Blocks`,`Prompt`,`Reply`,`Duration`,`Outcome`,`Reason`].map(t=>(0,V.jsx)(`th`,{scope:`col`,className:Sm,children:e(t)},t))})}),(0,V.jsx)(`tbody`,{children:r?.recent.map((e,t)=>(0,V.jsx)(`tr`,{className:Cm,children:[new Date(e.timestamp).toLocaleString(),e.host,e.sourceType,e.sessionId,e.path,`${e.provider??``}/${e.model??``}`,e.stopReason,e.blockTypes?.join(`, `),e.promptChars,e.replyChars,e.durationMs,e.outcome,e.reason].map((e,t)=>(0,V.jsx)(`td`,{className:B(wm,`whitespace-nowrap`),children:e??``},t))},`${e.timestamp}-${t}`))})]})}),(0,V.jsxs)(`div`,{className:`space-y-2 border-t border-border pt-3 text-sm`,children:[(0,V.jsxs)(`label`,{className:`flex items-center gap-2`,children:[(0,V.jsx)(`input`,{type:`checkbox`,checked:v?.value===!0,disabled:!s,onChange:t=>{t.target.checked&&!window.confirm(e(`Traces can contain conversation content after redaction. Keep tracing off unless you need it for debugging.`))||m({captureTrace:t.target.checked})}}),` `,e(`Save capture traces`)]}),(0,V.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:e(`Traces can contain conversation content after redaction. Keep tracing off unless you need it for debugging.`)}),v?.source===`project`&&(0,V.jsx)(`p`,{className:`text-xs text-amber-600`,children:e(`This project turns tracing off. The global switch cannot turn it on here.`)}),[`captureTraceRetentionDays`,`captureAttemptRetentionDays`].map(t=>(0,V.jsxs)(`label`,{className:`block`,children:[e(t===`captureTraceRetentionDays`?`Trace retention (days)`:`Attempt retention (days)`),` `,(0,V.jsx)(`input`,{type:`number`,min:1,className:`ms-2 w-20 rounded border border-border bg-background p-1`,defaultValue:Number(s?.settings[t]?.value??30),onBlur:e=>{let n=Number(e.target.value);Number.isSafeInteger(n)&&n>0&&n!==s?.settings[t]?.value&&m({[t]:n})}},`${t}-${s?.settings[t]?.value}`)]},t)),(0,V.jsx)(`h3`,{className:`font-medium`,children:e(`Trace files`)}),a.map(t=>(0,V.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,V.jsxs)(`span`,{children:[t.file,` (`,t.size,` bytes)`]}),(0,V.jsx)(`button`,{type:`button`,onClick:()=>void h(t.file),children:e(`View`)}),(0,V.jsx)(`button`,{type:`button`,onClick:()=>void g(t.file),children:e(`Delete`)})]},t.file)),l&&(0,V.jsx)(`pre`,{className:`max-h-60 overflow-auto whitespace-pre-wrap break-words rounded bg-background p-2 text-xs`,children:l})]})]})}function Em(){let e=cm(),[t,n]=(0,_.useState)([]),[r,i]=(0,_.useState)(!1),[a,o]=(0,_.useState)(``);async function s(e){i(!0);try{let t=await $(`/api/settings/health`,{method:`POST`,body:JSON.stringify({testModels:e})});n(t.checks),o(``)}catch(e){o(e.message)}finally{i(!1)}}return(0,V.jsxs)(`section`,{id:`settings-health`,className:`space-y-3 rounded-xl border border-border bg-card p-4`,"aria-label":e(`Health`),children:[(0,V.jsx)(`h2`,{className:`text-lg font-medium`,children:e(`Health`)}),(0,V.jsxs)(`div`,{className:`flex gap-2`,children:[(0,V.jsx)(`button`,{className:`rounded-lg border border-border px-3 py-1.5 text-sm`,disabled:r,onClick:()=>void s(!1),children:e(`Run checks`)}),(0,V.jsx)(`button`,{className:`rounded-lg border border-border px-3 py-1.5 text-sm`,disabled:r,onClick:()=>void s(!0),children:e(`Run checks and test models`)})]}),r&&(0,V.jsx)(`p`,{role:`status`,children:e(`Checking…`)}),a&&(0,V.jsx)(`p`,{role:`alert`,children:a}),(0,V.jsx)(`ul`,{className:`space-y-2 text-sm`,children:t.map(t=>(0,V.jsxs)(`li`,{className:`rounded-lg border border-border p-2`,children:[(0,V.jsx)(`strong`,{className:t.status===`fail`?`text-red-600`:t.status===`warn`?`text-amber-600`:`text-green-600`,children:e(t.status).toUpperCase()}),` `,`· `,e(t.check),`: `,t.reason]},t.check))})]})}function Dm(e){let t=/^(\d{4})-(\d{2})-(\d{2})$/.exec(e);return t?[Number(t[1]),Number(t[2])-1,Number(t[3])]:null}function Om(e){let t=Dm(e);return t?new Date(t[0],t[1],t[2],0,0,0,0).getTime():void 0}function km(e){let t=Dm(e);return t?new Date(t[0],t[1],t[2],23,59,59,999).getTime():void 0}function Am(e,t,n){let r=!n&&e!==null&&e.revision===t.revision;return{page:r?{...t,listedAt:e.listedAt}:t,sameListing:r}}function jm(e){let t=cm(),[n,r]=(0,_.useState)(``),[i,a]=(0,_.useState)(null),[o,s]=(0,_.useState)(null),[c,l]=(0,_.useState)(!1),[u,d]=(0,_.useState)(``),f=`rounded border border-border px-3 py-1.5 text-sm disabled:opacity-50`;async function p(t){try{let n=await $(`/api/settings/imports/sources/validate`,{method:`POST`,body:JSON.stringify({host:e.host,path:t})});a(n),r(n.displayPath),e.onChoose(n),d(``)}catch(e){d(e.message)}}async function m(t){try{let n=await $(`/api/settings/imports/sources/browse`,{method:`POST`,body:JSON.stringify({host:e.host,...t?{path:t}:{}})});s(n),d(``)}catch(e){let t=e.message;/loopback/i.test(t)&&l(!0),d(t)}}function h(){a(null),r(``),s(null),e.onChoose(null)}return(0,V.jsxs)(`div`,{className:`space-y-2 text-sm`,children:[(0,V.jsxs)(`label`,{className:`block`,children:[e.host===`pi`?t(`Pi sessions folder or one .jsonl session file`):t(`OpenCode database file`),(0,V.jsx)(`input`,{className:`mt-1 block w-full rounded border border-border bg-background p-2 font-mono`,value:n,placeholder:t(`Absolute path, for example on a mounted volume`),onChange:e=>r(e.target.value)})]}),(0,V.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[(0,V.jsx)(`button`,{type:`button`,className:f,disabled:!n.trim(),onClick:()=>void p(n.trim()),children:t(`Use this source`)}),!c&&(0,V.jsx)(`button`,{type:`button`,className:f,onClick:()=>void m(o?.path),children:t(`Browse`)}),i&&(0,V.jsx)(`button`,{type:`button`,className:f,onClick:h,children:t(`Use the default location`)})]}),i&&(0,V.jsxs)(`p`,{role:`status`,children:[t(`Chosen source`),`: `,(0,V.jsx)(`span`,{className:`font-mono`,children:i.displayPath})]}),u&&(0,V.jsx)(`p`,{role:`alert`,children:u}),o&&(0,V.jsxs)(`div`,{className:`rounded border border-border p-2`,children:[(0,V.jsx)(`p`,{className:`font-mono text-xs break-all`,children:o.path}),(0,V.jsxs)(`ul`,{className:`max-h-60 overflow-auto`,children:[o.parent&&(0,V.jsx)(`li`,{children:(0,V.jsx)(`button`,{type:`button`,className:`underline`,onClick:()=>void m(o.parent),children:`..`})}),o.entries.map(n=>(0,V.jsxs)(`li`,{className:`flex items-center gap-2`,children:[n.kind===`folder`?(0,V.jsxs)(`button`,{type:`button`,className:`underline`,onClick:()=>void m(n.path),children:[n.name,`/`]}):(0,V.jsx)(`span`,{className:`font-mono text-xs`,children:n.name}),(n.kind!==`folder`||e.host===`pi`)&&(0,V.jsx)(`button`,{type:`button`,className:`text-xs underline`,onClick:()=>void p(n.path),children:t(`Choose`)})]},n.path))]}),o.truncated&&(0,V.jsx)(`p`,{className:`text-xs`,children:t(`Only the first 500 entries are shown.`)})]})]})}var Mm=50,Nm=1e3,Pm={"missing-model":`The external API has no memoryModel in the global config`,"missing-url":`The external API has no memoryApiUrl in the global config`,"missing-key":`The external API key is missing in the OpenCode process; check memoryApiKey and its env:// or file:// source`,"unsupported-provider":`The memoryProvider in the global config is not supported for imports`},Fm=()=>({mode:`ids`,picked:new Map});function Im(e,t){return e.split(`
123
+ `).map(e=>e.trim()).filter(Boolean).map(e=>{let n=e.indexOf(`=`);if(n<1||n===e.length-1)throw Error(t);return{from:e.slice(0,n),to:e.slice(n+1)}})}function Lm(){let e=cm(),[t,n]=(0,_.useState)(`pi`),[r,i]=(0,_.useState)(null),[a,o]=(0,_.useState)(`current-project`),[s,c]=(0,_.useState)(``),[l,u]=(0,_.useState)(``),[d,f]=(0,_.useState)(``),[p,m]=(0,_.useState)(``),[h,g]=(0,_.useState)(``),[v,y]=(0,_.useState)(!1),[b,x]=(0,_.useState)(!1),[S,C]=(0,_.useState)(!1),[w,T]=(0,_.useState)(!1),[E,D]=(0,_.useState)(null),[O,ee]=(0,_.useState)(``),[te,ne]=(0,_.useState)(!1),[re,ie]=(0,_.useState)(!1),[k,ae]=(0,_.useState)(Fm),[A,j]=(0,_.useState)(null),[oe,M]=(0,_.useState)(``),[se,ce]=(0,_.useState)(null),[N,P]=(0,_.useState)(``);(0,_.useEffect)(()=>{$(`/api/settings/imports/readiness`).then(e=>{j(e);let t=e.opencode.models[0];M(t?`${t.provider}/${t.model}`:e.external.state===`ready`?`external`:``)}).catch(()=>j(null)),$(`/api/settings/imports/current`).then(e=>ce(e.job)).catch(()=>{})},[]),(0,_.useEffect)(()=>{if(se?.state!==`running`&&se?.state!==`cancelling`)return;let e=setInterval(()=>{$(`/api/settings/imports/current`).then(e=>ce(e.job)).catch(e=>P(e.message))},1e3);return()=>clearInterval(e)},[se?.state]);let le=JSON.stringify([t,r?.sourceToken,a,s,l]),ue=!!E&&(te||le!==O);function de(){let n=Im(l,e(`Directory maps must be old=new, one per line`));return{host:t,...r?{source:r.sourceToken}:{},scope:a,...a===`current-project`&&s?{project:s}:{},pathMaps:n}}async function fe(e,t){ie(!0);try{let n=await $(`/api/settings/imports/sessions`,{method:`POST`,body:JSON.stringify({...de(),offset:e,limit:Mm,refresh:t})}),r=Am(E,n,t);r.sameListing||ae(Fm()),D(r.page),ee(le),ne(!1),P(``)}catch(e){P(e.message)}finally{ie(!1)}}let pe=E?E.total-(a===`all-projects`?E.unresolvedCount:0):0,F=k.mode===`all`?pe-k.excluded.size:k.picked.size,me=e=>k.mode===`all`?!k.excluded.has(e.key):k.picked.has(e.key);function he(e){if(!(!e.selectable||!e.directory)){if(k.mode===`all`){let t=new Set(k.excluded);t.has(e.key)?t.delete(e.key):t.add(e.key),ae({mode:`all`,excluded:t})}else{let t=new Map(k.picked);t.has(e.key)?t.delete(e.key):t.set(e.key,e.directory),ae({mode:`ids`,picked:t})}}}let ge=!b||!S,_e=t===`pi`&&A?.piReader.available===!1,ve=ge?A?oe?oe===`external`&&A.external.state!==`ready`?e(Pm[A.external.state]??`The external API is not ready`):null:e(`No import model is ready. Connect a model in OpenCode or complete the external API settings.`):e(`Model readiness is unknown. Reload the page.`):null,ye=E?ue?e(`Options changed. Refresh the session list first.`):F===0?e(`Select at least one session.`):k.mode===`ids`&&k.picked.size>Nm?e(`Select at most 1000 sessions one by one, or use Select all matching.`):null:e(`List sessions first.`),be=se?.state===`running`||se?.state===`cancelling`;async function xe(n){if(E)try{let r=d?Om(d):void 0,i=p?km(p):void 0,o={host:t,source:E.source.sourceToken,selection:k.mode===`all`?{mode:`all`,excludedKeys:[...k.excluded],revision:E.revision,listedAt:E.listedAt}:{mode:`ids`,sessions:[...k.picked].map(([e,t])=>({key:e,directory:t})),listedAt:E.listedAt},options:{dryRun:n,force:v,skipMemories:b,skipProfile:S,scope:a,...a===`current-project`&&s?{project:s}:{},...r===void 0?{}:{since:r},...i===void 0?{}:{until:i},...h?{profileBatch:Number(h)}:{},pathMaps:Im(l,e(`Directory maps must be old=new, one per line`))},...oe?{modelChoice:oe}:{}},c=await $(`/api/settings/imports`,{method:`POST`,body:JSON.stringify(o)});ce(c),P(``)}catch(e){let t=e.message;/out of date|changed|no longer/i.test(t)&&ne(!0),P(t)}}async function Se(){try{let e=await $(`/api/settings/imports/current/cancel`,{method:`POST`,body:`{}`});ce(e.job)}catch(e){P(e.message)}}function Ce(){o(`all-projects`),T(!0)}let we={recorded:e(`recorded`),mapped:e(`mapped`),worktree:e(`project root`),unresolved:e(`missing`)},I=`mt-1 block w-full rounded border border-border bg-background p-2`,Te=`rounded border border-border px-3 py-1.5 text-sm disabled:opacity-50`;return(0,V.jsxs)(`section`,{className:`space-y-3 rounded-xl border border-border bg-card p-4`,"aria-label":e(`Import and backfill`),children:[(0,V.jsx)(`h2`,{className:`text-lg font-medium`,children:e(`Import and backfill`)}),(0,V.jsxs)(`div`,{className:`flex flex-wrap items-end gap-3`,children:[(0,V.jsxs)(`label`,{className:`text-sm`,children:[e(`History host`),(0,V.jsxs)(un,{"aria-label":e(`History host`),className:I,value:t,onChange:e=>{n(e.target.value),i(null),D(null),ae(Fm())},children:[(0,V.jsx)(`option`,{value:`pi`,children:`Pi`}),(0,V.jsx)(`option`,{value:`opencode`,children:`OpenCode`})]})]}),(0,V.jsx)(`button`,{type:`button`,className:Te,disabled:re,onClick:()=>void fe(0,!0),children:e(E?`Refresh list`:`List sessions`)}),(0,V.jsx)(`button`,{type:`button`,className:Te,onClick:()=>T(!w),children:e(w?`Hide advanced options`:`Advanced options`)})]}),(0,V.jsxs)(`p`,{className:`text-xs text-muted-foreground`,children:[e(`Source`),`: `,r?.displayPath??E?.source.displayPath??e(`default location`),` · `,e(a===`current-project`?`Current project`:`All projects`)]}),_e&&(0,V.jsx)(`p`,{role:`alert`,children:A?.piReader.reason}),w&&(0,V.jsxs)(`div`,{className:`space-y-3 rounded-lg border border-border p-3`,children:[(0,V.jsx)(jm,{host:t,onChoose:i}),(0,V.jsxs)(`div`,{className:`grid gap-3 sm:grid-cols-2`,children:[(0,V.jsxs)(`label`,{className:`text-sm`,children:[e(`Scope`),(0,V.jsxs)(un,{"aria-label":e(`Scope`),className:I,value:a,onChange:e=>o(e.target.value),children:[(0,V.jsx)(`option`,{value:`current-project`,children:e(`Current project`)}),(0,V.jsx)(`option`,{value:`all-projects`,children:e(`All projects`)})]})]}),a===`current-project`&&(0,V.jsxs)(`label`,{className:`text-sm`,children:[e(`Project directory`),(0,V.jsx)(`input`,{className:I,value:s,onChange:e=>c(e.target.value),placeholder:e(`Current directory`)})]}),(0,V.jsxs)(`label`,{className:`text-sm`,children:[e(`Prompt date from`),(0,V.jsx)(`input`,{type:`date`,className:I,value:d,onChange:e=>f(e.target.value)})]}),(0,V.jsxs)(`label`,{className:`text-sm`,children:[e(`Prompt date to`),(0,V.jsx)(`input`,{type:`date`,className:I,value:p,onChange:e=>m(e.target.value)})]}),(0,V.jsxs)(`label`,{className:`text-sm`,children:[e(`Profile batch size`),(0,V.jsx)(`input`,{inputMode:`numeric`,className:I,value:h,onChange:e=>g(e.target.value)})]})]}),(0,V.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:e(`Prompt dates are inclusive, use your time zone, and filter the turns inside each session, not the list. Empty dates include every turn.`)}),(0,V.jsxs)(`label`,{className:`block text-sm`,children:[e(`Directory maps (old=new, one per line)`),(0,V.jsx)(`textarea`,{className:I,rows:2,value:l,onChange:e=>u(e.target.value)})]}),(0,V.jsx)(`div`,{className:`flex flex-wrap gap-3 text-sm`,children:[[`Force reimport`,v,y],[`Skip memories`,b,x],[`Skip profile`,S,C]].map(([t,n,r])=>(0,V.jsxs)(`label`,{children:[(0,V.jsx)(`input`,{type:`checkbox`,checked:n,onChange:e=>r(e.target.checked)}),` `,e(t)]},t))})]}),ue&&E&&(0,V.jsx)(`p`,{role:`status`,className:`text-sm text-amber-600`,children:e(`Options changed. Refresh the session list first.`)}),E&&a===`current-project`&&E.unresolvedCount>0&&(0,V.jsxs)(`p`,{className:`text-sm`,children:[e(`Sessions with directories that no longer exist`),`: `,E.unresolvedCount,`.`,` `,(0,V.jsx)(`button`,{type:`button`,className:`underline`,onClick:Ce,children:e(`Show them and add a directory map`)})]}),E&&(0,V.jsxs)(`div`,{className:`space-y-2`,children:[(0,V.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2 text-sm`,children:[(0,V.jsxs)(`span`,{role:`status`,children:[e(`Selected`),`: `,F,` / `,pe]}),(0,V.jsxs)(`button`,{type:`button`,className:Te,disabled:pe===0,onClick:()=>ae({mode:`all`,excluded:new Set}),children:[e(`Select all matching`),` (`,pe,`)`]}),(0,V.jsx)(`button`,{type:`button`,className:Te,onClick:()=>ae(Fm()),children:e(`Clear selection`)})]}),(0,V.jsxs)(`div`,{className:`overflow-x-auto`,children:[(0,V.jsxs)(`table`,{className:`w-full text-left text-sm`,children:[(0,V.jsx)(`thead`,{children:(0,V.jsxs)(`tr`,{className:`text-muted-foreground`,children:[(0,V.jsx)(`th`,{className:`p-1`,children:(0,V.jsx)(`span`,{className:`sr-only`,children:e(`Select`)})}),(0,V.jsx)(`th`,{className:`p-1`,children:e(`Created`)}),(0,V.jsx)(`th`,{className:`p-1`,children:e(`Session`)}),(0,V.jsx)(`th`,{className:`p-1`,children:e(`Project directory`)}),(0,V.jsx)(`th`,{className:`p-1`,children:e(`Resolved by`)})]})}),(0,V.jsx)(`tbody`,{children:E.rows.map(t=>(0,V.jsxs)(`tr`,{className:`border-t border-border`,children:[(0,V.jsx)(`td`,{className:`p-1`,children:(0,V.jsx)(`input`,{type:`checkbox`,"aria-label":`${e(`Select`)} ${t.sessionId??t.key}`,checked:t.selectable&&me(t),disabled:!t.selectable,onChange:()=>he(t)})}),(0,V.jsx)(`td`,{className:`p-1 whitespace-nowrap`,children:t.createdAt?new Date(t.createdAt).toLocaleString():`—`}),(0,V.jsx)(`td`,{className:`p-1 font-mono text-xs`,children:t.sessionId??t.key}),(0,V.jsx)(`td`,{className:`p-1 font-mono text-xs break-all`,children:t.directory??t.recordedDirectory??`—`}),(0,V.jsx)(`td`,{className:`p-1`,children:we[t.via]})]},t.key))})]}),E.total===0&&(0,V.jsx)(`p`,{className:`text-sm`,children:e(`No sessions match.`)})]}),(0,V.jsxs)(`div`,{className:`flex items-center gap-2 text-sm`,children:[(0,V.jsx)(`button`,{type:`button`,className:Te,disabled:re||E.offset===0,onClick:()=>void fe(Math.max(0,E.offset-Mm),!1),children:e(`Previous`)}),(0,V.jsxs)(`span`,{children:[E.total?E.offset+1:0,`–`,Math.min(E.offset+Mm,E.total),` /`,` `,E.total]}),(0,V.jsx)(`button`,{type:`button`,className:Te,disabled:re||E.offset+Mm>=E.total,onClick:()=>void fe(E.offset+Mm,!1),children:e(`Next`)})]})]}),(0,V.jsxs)(`label`,{className:`block text-sm`,children:[e(`Import model`),(0,V.jsxs)(un,{"aria-label":e(`Import model`),className:I,value:oe,onChange:e=>M(e.target.value),children:[(0,V.jsx)(`option`,{value:``,children:e(`None`)}),A?.opencode.models.map(e=>(0,V.jsxs)(`option`,{value:`${e.provider}/${e.model}`,children:[e.name,` (`,e.provider,`/`,e.model,`)`]},`${e.provider}/${e.model}`)),(0,V.jsxs)(`option`,{value:`external`,children:[e(`Saved external API`),A?.external.model?` (${A.external.model})`:``,A&&A.external.state!==`ready`?` — ${e(`not ready`)}`:``]})]})]}),(0,V.jsxs)(`p`,{className:`text-xs text-muted-foreground`,children:[e(`Configured, not tested.`),` `,(0,V.jsx)(`button`,{type:`button`,className:`underline`,onClick:()=>document.getElementById(`settings-health`)?.scrollIntoView(),children:e(`Test models in Health`)})]}),(0,V.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[(0,V.jsx)(`button`,{type:`button`,className:Te,disabled:be||_e||!!ye,onClick:()=>void xe(!0),children:e(`Preview (dry run)`)}),(0,V.jsx)(`button`,{type:`button`,className:Te,disabled:be||_e||!!ye||!!ve,onClick:()=>void xe(!1),children:e(`Start import`)}),be&&(0,V.jsx)(`button`,{type:`button`,className:Te,disabled:se?.state===`cancelling`,onClick:()=>void Se(),children:e(`Cancel after current unit`)})]}),(ye||ve)&&!be&&(0,V.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:ye??ve}),N&&(0,V.jsx)(`p`,{role:`alert`,children:N}),se&&(0,V.jsxs)(`div`,{role:`status`,className:`text-sm`,children:[se.host,` · `,se.dryRun?e(`Preview (dry run)`):e(`Import`),` · `,e(se.state),` ·`,` `,se.sessions,` `,e(`sessions`),` · `,se.processed,`/`,se.total,` `,e(`units`),se.error&&(0,V.jsx)(`p`,{role:`alert`,children:se.error}),se.report&&(0,V.jsx)(`pre`,{className:`mt-2 overflow-auto whitespace-pre-wrap rounded bg-background p-3 text-xs`,children:se.report})]})]})}function Rm(e){return e.pi?.state===`running`||e.opencode?.state===`running`}function zm(e){return!e||e.total<=0?null:{percent:e.percent,done:`${e.done.toLocaleString(`en-US`)} / ${e.total.toLocaleString(`en-US`)}`,minutesLeft:e.minutesLeft===null?`unknown`:`about ${e.minutesLeft}`}}function Bm(e,t){let n=e?.state===`running`,r=e?.paused===!0;return{runNow:!n&&!r&&!t,pause:n&&!r,resume:r&&!t}}function Vm(e,t){return e??!t}function Hm(e,t){let n=t.indexOf(`/`);if(t!==`inherit`&&t!==`external`&&(n<1||n===t.length-1))throw Error(`Enter a model as provider/model.`);return{[`${e}BackfillModel`]:t}}function Um(){let e=cm(),[t,n]=(0,_.useState)(),[r,i]=(0,_.useState)({pi:null,opencode:null}),[a,o]=(0,_.useState)(),[s,c]=(0,_.useState)({}),[l,u]=(0,_.useState)({}),[d,f]=(0,_.useState)({}),[p,m]=(0,_.useState)(``),[h,g]=(0,_.useState)(!1),v=Rm(r)||a?.pi.run?.state===`running`||a?.opencode.run?.state===`running`,y=()=>$(`/api/settings/backfill/runs`).then(o).catch(e=>m(e.message));(0,_.useEffect)(()=>{let e=!0;$(`/api/settings`).then(t=>{e&&n(t)}).catch(t=>{e&&m(t.message)}),$(`/api/settings/backfill`).then(t=>{e&&i(t)}).catch(t=>{e&&m(t.message)}),y();for(let t of[`pi`,`opencode`])$(`/api/settings/models?host=${t}`).then(n=>{e&&c(e=>({...e,[t]:n}))}).catch(()=>{e&&c(e=>({...e,[t]:{available:!1}}))});let t=im(t=>{e&&n(t)});return()=>{e=!1,t()}},[]),(0,_.useEffect)(()=>{if(!v)return;let e=!0,t=setInterval(()=>{$(`/api/settings/backfill`).then(t=>{e&&i(t)}).catch(t=>{e&&m(t.message)}),e&&y()},3e3);return()=>{e=!1,clearInterval(t)}},[v]);async function b(n){if(t){g(!0);try{await $(`/api/settings`,{method:`PATCH`,body:JSON.stringify({edits:n,revision:t.revision})}),m(e(`Saved. Changes apply at the next run.`))}catch(e){m(e.message)}await rm(),g(!1)}}async function x(t,n){g(!0);try{await $(`/api/settings/backfill/${t}/${n}`,{method:`POST`,body:`{}`}),m(e(n===`pause`?`Pausing after the current exchange.`:`Backfill started.`))}catch(e){m(e.message)}await y(),g(!1)}function S(t,n){try{b(Hm(t,n.trim()))}catch(t){m(e(t.message))}}return(0,V.jsxs)(`section`,{className:`space-y-3 rounded-xl border border-border bg-card p-4`,"aria-label":e(`Automatic import`),children:[(0,V.jsx)(`h2`,{className:`text-lg font-medium`,children:e(`Automatic import`)}),(0,V.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:e(`Automatic import makes model calls. A model change applies at the next run. Turning it off stops a running import after the current exchange.`)}),(0,V.jsxs)(`label`,{className:`flex items-center gap-2 text-sm`,children:[(0,V.jsx)(`input`,{type:`checkbox`,checked:!!t?.settings.autoBackfill?.globalValue,disabled:h||!t,onChange:e=>void b({autoBackfill:e.target.checked})}),e(`Import past chats automatically`)]}),[`pi`,`opencode`].map(n=>{let i=String(t?.settings[`${n}BackfillModel`]?.globalValue??`inherit`),o=l[n]??i,c=s[n]?.models??[],p=o===`inherit`||o===`external`||c.some(e=>`${e.provider}/${e.model}`===o),m=um({memoryProvider:t?.settings.memoryProvider?.globalValue,memoryModel:t?.settings.memoryModel?.globalValue,memoryApiUrl:t?.settings.memoryApiUrl?.globalValue,keySet:!!t?.secrets?.memoryApiKey?.set}),g=a?.[n].run??null,_=a?.[n].runNowUnavailable??null,v=Bm(g,_),y=g?.state===`running`?zm(g):null,b=Vm(d[n],p);return(0,V.jsxs)(`div`,{className:`space-y-2 rounded-lg border border-border p-3 text-sm`,children:[(0,V.jsx)(`h3`,{className:`font-medium`,children:n===`pi`?`Pi`:`OpenCode`}),(0,V.jsxs)(`label`,{className:`block`,children:[e(`Backfill model`),(0,V.jsxs)(un,{"aria-label":`${n===`pi`?`Pi`:`OpenCode`} ${e(`Backfill model`)}`,className:`mt-1 block w-full rounded border border-border bg-background p-2`,value:b?`typed`:o,onChange:e=>{let t=e.target.value;f(e=>({...e,[n]:t===`typed`})),u(e=>({...e,[n]:t}))},children:[(0,V.jsx)(`option`,{value:`inherit`,children:e(`Same as live capture`)}),(0,V.jsx)(`option`,{value:`external`,disabled:m.length>0,children:e(`External API`)}),c.map(e=>(0,V.jsxs)(`option`,{value:`${e.provider}/${e.model}`,children:[e.name,` (`,e.provider,`/`,e.model,`)`]},`${e.provider}/${e.model}`)),(0,V.jsx)(`option`,{value:`typed`,children:e(`Manual provider/model`)})]})]}),b&&(0,V.jsx)(`input`,{"aria-label":`${n} ${e(`Manual provider/model`)}`,className:`w-full rounded border border-border bg-background p-2`,placeholder:`provider/model`,value:o===`typed`?``:o,onChange:e=>u(t=>({...t,[n]:e.target.value}))}),m.length>0&&(0,V.jsxs)(`p`,{className:`text-xs text-muted-foreground`,children:[e(`External API needs`),`: `,m.join(`, `)]}),s[n]?.available===!1&&(0,V.jsx)(`p`,{children:s[n].reason?e(s[n].reason):e(`Model list unavailable. Enter provider/model manually.`)}),(0,V.jsx)(`button`,{type:`button`,className:`rounded border border-border px-3 py-1.5`,disabled:h||!t,onClick:()=>S(n,o),children:e(`Save model`)}),(0,V.jsxs)(`p`,{children:[e(`State`),`:`,` `,g?.paused?e(`paused`):e(g?.state??r[n]?.state??`not started`),g?.state===`running`&&g.surface&&` (${e(`started from ${g.surface}`)})`]}),y&&(0,V.jsxs)(`div`,{className:`space-y-1`,children:[(0,V.jsx)(`div`,{role:`progressbar`,"aria-valuemin":0,"aria-valuemax":100,"aria-valuenow":y.percent,className:`h-2 w-full overflow-hidden rounded bg-muted`,children:(0,V.jsx)(`div`,{className:`h-full bg-primary`,style:{width:`${y.percent}%`}})}),(0,V.jsxs)(`p`,{className:`text-muted-foreground`,children:[y.percent,`% · `,y.done,` · `,e(`Minutes left`),`:`,` `,y.minutesLeft===`unknown`?e(`unknown`):y.minutesLeft.replace(`about`,e(`about`))]})]}),(0,V.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[(0,V.jsx)(`button`,{type:`button`,className:`rounded border border-border px-3 py-1.5`,disabled:h||!v.runNow,onClick:()=>void x(n,`run`),children:e(`Run now`)}),(0,V.jsx)(`button`,{type:`button`,className:`rounded border border-border px-3 py-1.5`,disabled:h||!v.pause,onClick:()=>void x(n,`pause`),children:e(`Pause`)}),(0,V.jsx)(`button`,{type:`button`,className:`rounded border border-border px-3 py-1.5`,disabled:h||!v.resume,onClick:()=>void x(n,`resume`),children:e(`Resume`)})]}),_&&(0,V.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:_}),r[n]&&(0,V.jsxs)(`div`,{className:`space-y-1 text-muted-foreground`,children:[(0,V.jsxs)(`p`,{children:[e(`Imported`),`: `,r[n].counts.imported,` · `,e(`Skipped`),`:`,` `,r[n].counts.skipped,` · `,e(`Failed`),`: `,r[n].counts.failed]}),(0,V.jsxs)(`p`,{children:[e(`Pending`),`: `,r[n].counts.pending,` · `,e(`Unresolved sessions`),`:`,` `,r[n].counts.unresolved,r[n].counts.unresolved>0&&(0,V.jsxs)(V.Fragment,{children:[` `,(0,V.jsx)(`a`,{className:`underline`,href:`#directory-maps`,children:e(`Directory maps`)})]})]}),(0,V.jsxs)(`p`,{children:[e(`Model`),`: `,r[n].model??e(`none`)]}),(0,V.jsxs)(`p`,{children:[e(`Cutoff`),`: `,new Date(r[n].cutoff).toLocaleString()]}),r[n].error&&(0,V.jsx)(`p`,{role:`alert`,children:r[n].error})]})]},n)}),p&&(0,V.jsx)(`p`,{role:`status`,className:`text-sm`,children:p})]})}function Wm(){let e=cm(),[t,n]=(0,_.useState)(),[r,i]=(0,_.useState)(),[a,o]=(0,_.useState)(),[s,c]=(0,_.useState)(``),[l,u]=(0,_.useState)(!1);(0,_.useEffect)(()=>{let e=!0;$(`/api/settings`).then(t=>{e&&n(t)}).catch(t=>{e&&c(t.message)}),$(`/api/settings/web-autostart`).then(t=>{e&&i(t)}).catch(t=>{e&&c(t.message)}),$(`/api/settings/version`).then(t=>{e&&o(t)}).catch(()=>{});let t=im(t=>{e&&n(t)});return()=>{e=!1,t()}},[]);async function d(n){if(t){u(!0);try{await $(`/api/settings`,{method:`PATCH`,body:JSON.stringify({edits:{webServerAutoStart:n},revision:t.revision})}),c(e(`Saved. The login item changes at the next Pi or OpenCode start.`))}catch(e){c(e.message)}await rm(),u(!1)}}return(0,V.jsxs)(`section`,{className:`space-y-3 rounded-xl border border-border bg-card p-4`,"aria-label":e(`Web app`),children:[(0,V.jsx)(`h2`,{className:`text-lg font-medium`,children:e(`Web app`)}),(0,V.jsxs)(`label`,{className:`flex items-center gap-2 text-sm`,children:[(0,V.jsx)(`input`,{type:`checkbox`,checked:!!t?.settings.webServerAutoStart?.globalValue,disabled:l||!t,onChange:e=>void d(e.target.checked)}),e(`Start web app at login`)]}),(0,V.jsxs)(`p`,{className:`text-sm`,children:[e(`Login item`),`: `,e(r?.state??`not installed`)]}),r?.state===`unsupported`&&(0,V.jsx)(`p`,{role:`status`,children:e(`Login items are unsupported on this platform.`)}),r?.state===`no-runtime`&&(0,V.jsx)(`p`,{role:`status`,children:e(`Install Node or Bun to start the web app at login.`)}),a&&(0,V.jsx)(Gm,{info:a}),(0,V.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:e(`Use these commands to apply the change now:`)}),(0,V.jsxs)(`div`,{className:`space-y-1 font-mono text-xs`,children:[(0,V.jsx)(`p`,{children:`om-memory-system web`}),(0,V.jsx)(`p`,{children:`om-memory-system web install`}),(0,V.jsx)(`p`,{children:`om-memory-system web uninstall`}),(0,V.jsx)(`p`,{children:`om-memory-system web status`})]}),s&&(0,V.jsx)(`p`,{role:`status`,className:`text-sm`,children:s})]})}function Gm({info:e}){let t=cm(),n=fm(e);return(0,V.jsxs)(`div`,{className:`space-y-1 text-sm`,children:[(0,V.jsxs)(`p`,{children:[t(`Running version`),`: `,e.running,` · `,t(`Global command`),`:`,` `,e.global??t(`not installed globally`)]}),n.kind===`different`&&(0,V.jsx)(`p`,{role:`status`,className:`text-amber-600`,children:t(`The global command's version differs from the running OMMS. Upgrade it:`)}),n.command&&(0,V.jsx)(`p`,{className:`font-mono text-xs`,children:n.command}),(0,V.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:t(`A global install is optional but recommended: the login item and the terminal commands then run without npx.`)})]})}function Km(){let e=cm(),[t,n]=(0,_.useState)(!1),[r,i]=(0,_.useState)(),[a,o]=(0,_.useState)(``);async function s(e){try{i(await $(`/api/settings/log?lines=200${e?`&filter=capture`:``}`)),o(``)}catch(e){o(e.message)}}return(0,_.useEffect)(()=>{s(t)},[t]),(0,V.jsxs)(`section`,{className:`space-y-3 rounded-xl border border-border bg-card p-4`,"aria-label":e(`Log`),children:[(0,V.jsx)(`h2`,{className:`text-lg font-medium`,children:e(`Log`)}),(0,V.jsxs)(`div`,{className:`flex flex-wrap items-center gap-3 text-sm`,children:[(0,V.jsxs)(`label`,{children:[(0,V.jsx)(`input`,{type:`checkbox`,checked:t,onChange:e=>n(e.target.checked)}),` `,e(`Capture attempts only`)]}),(0,V.jsx)(`button`,{type:`button`,className:`rounded-lg border border-border px-3 py-1`,onClick:()=>void s(t),children:e(`Refresh`)}),r&&(0,V.jsx)(`button`,{type:`button`,className:`rounded-lg border border-border px-3 py-1`,onClick:()=>void navigator.clipboard.writeText(r.path),children:e(`Copy log path`)})]}),(0,V.jsx)(`p`,{className:`break-all text-xs text-muted-foreground`,children:r?.path}),a&&(0,V.jsx)(`p`,{role:`alert`,children:a}),(0,V.jsx)(`pre`,{className:`max-h-80 overflow-auto rounded-lg bg-background p-3 text-xs whitespace-pre-wrap break-words`,"aria-label":e(`Log lines`),children:r?r.lines.length?r.lines.join(`
124
124
  `):e(`No log has been written yet.`):e(`Loading log…`)})]})}function qm(){return(0,V.jsxs)(`div`,{className:`settings-view space-y-6`,children:[(0,V.jsx)(_m,{}),(0,V.jsx)(mm,{}),(0,V.jsx)(Tm,{}),(0,V.jsx)(Em,{}),(0,V.jsx)(Lm,{}),(0,V.jsx)(Um,{}),(0,V.jsx)(vm,{}),(0,V.jsx)(Wm,{}),(0,V.jsx)(Km,{})]})}var Jm=Pr(`grid gap-0.5 rounded-2xl border px-4 py-3 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2.5 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4 group/alert relative w-full`,{variants:{variant:{default:`bg-card text-card-foreground`,destructive:`text-destructive bg-card *:data-[slot=alert-description]:text-destructive/90 *:[svg]:text-current`}},defaultVariants:{variant:`default`}});function Ym({className:e,variant:t,...n}){return(0,V.jsx)(`div`,{"data-slot":`alert`,role:`alert`,className:B(Jm({variant:t}),e),...n})}function Xm({className:e,...t}){return(0,V.jsx)(`div`,{"data-slot":`alert-description`,className:B(`text-muted-foreground text-sm [&_p]:leading-relaxed col-start-2`,e),...t})}function Zm({...e}){let t=hl();return(0,V.jsx)(Hn,{theme:t,className:`toaster group`,style:{"--normal-bg":`var(--color-popover)`,"--normal-text":`var(--color-popover-foreground)`,"--normal-border":`var(--color-border)`},icons:{loading:(0,V.jsx)(le,{className:`size-4 animate-spin`}),success:(0,V.jsx)(oe,{className:`size-4`}),error:(0,V.jsx)(F,{className:`size-4`}),info:(0,V.jsx)(N,{className:`size-4`}),warning:(0,V.jsx)(ke,{className:`size-4`})},...e})}function Qm(e,t,n){return e?t:n}var $m=[``,`feature`,`bug-fix`,`refactor`,`architecture`,`rule`,`documentation`,`discussion`,`analysis`,`configuration`];function eh(){let{t:e,language:t}=cr(),n=tl(),[r,i]=(0,_.useState)(!1),a=lr(),o=ur();(0,_.useEffect)(()=>{let e=$c();(async()=>{await a.loadTags(),await a.loadMemories(),await a.loadStats(),await a.checkMigrationStatus(),await a.checkAuthWarning()})();let t=setInterval(()=>{a.loadStats(),!a.isSearching&&n===`project`&&a.loadMemories()},3e4);return()=>{e(),clearInterval(t)}},[]);let s=o.loadUserProfile;(0,_.useEffect)(()=>{n===`profile`&&s()},[n,s]);function c(e){e!==t&&(ir(e),a.loadMemories(),a.loadStats(),n===`profile`&&o.loadUserProfile())}function l(e){e.defaultPrevented||e.button!==0||e.metaKey||e.ctrlKey||e.shiftKey||e.altKey||(e.preventDefault(),Qc(Vc.home))}return(0,V.jsxs)(V.Fragment,{children:[(0,V.jsx)(Zm,{richColors:!0,position:`bottom-right`}),(0,V.jsxs)(`div`,{className:`flex min-h-svh bg-background text-foreground`,children:[(0,V.jsx)(yl,{open:r,onOpenChange:i,currentView:n,brand:e(`brand`),projectLabel:e(`tab-project`),profileLabel:e(`tab-profile`),profileSections:[{id:`profile-preferences`,label:e(`profile-preferences`)},{id:`profile-patterns`,label:e(`profile-patterns`)},{id:`profile-workflows`,label:e(`profile-workflows`)}],langLabel:t.toUpperCase(),languageLabel:e(`nav-language`),themeLabel:e(`nav-theme`),settingsLabel:e(`nav-settings`),closeLabel:e(`nav-close`),collapseLabel:e(`nav-collapse`),expandLabel:e(`nav-expand`),onLanguageSelect:c}),(0,V.jsxs)(`div`,{className:`flex min-w-0 flex-1 flex-col`,children:[(0,V.jsxs)(`div`,{className:`sticky top-0 z-30 flex items-center gap-2 border-b border-border bg-background/95 px-3 py-2 backdrop-blur md:hidden`,children:[(0,V.jsx)(W,{variant:`ghost`,size:`icon-sm`,onClick:()=>i(!0),"aria-label":e(`nav-menu`),children:(0,V.jsx)(de,{className:`size-4`})}),(0,V.jsx)(`a`,{href:Vc.home,className:`truncate text-sm tracking-wide text-primary`,onClick:l,children:e(`brand`)})]}),(0,V.jsxs)(`div`,{className:`mx-auto w-full max-w-6xl flex-1 space-y-4 p-4 md:p-6`,children:[a.showAuthWarning?(0,V.jsxs)(Ym,{variant:`destructive`,children:[(0,V.jsx)(ke,{}),(0,V.jsx)(Xm,{children:e(`auth-warning-text`)})]}):null,(0,V.jsxs)(`div`,{className:`flex flex-wrap items-center justify-between gap-2`,children:[(0,V.jsx)(`h1`,{className:`text-base tracking-wide text-primary`,children:e(n===`project`?`tab-project`:n===`profile`?`tab-profile`:`nav-settings`)}),n===`project`?(0,V.jsxs)(`div`,{className:`flex items-center gap-2 text-xs text-muted-foreground`,children:[(0,V.jsx)(`span`,{children:e(`text-total`,{count:Qm(a.isSearching,a.totalItems,a.statsTotal)})}),a.refreshing?(0,V.jsx)(ue,{className:`size-3.5 animate-spin`}):null]}):null]}),n===`project`?(0,V.jsxs)(V.Fragment,{children:[(0,V.jsxs)(`div`,{className:`flex flex-wrap items-end gap-3 rounded-xl border border-border bg-card p-3`,children:[(0,V.jsxs)(`div`,{className:`min-w-0 max-w-full space-y-1`,children:[(0,V.jsx)(Sl,{htmlFor:`tag-filter`,children:e(`label-tag`)}),(0,V.jsxs)(un,{id:`tag-filter`,className:`flex h-8 w-64 max-w-full rounded-lg border border-border bg-background px-2 text-sm`,value:a.selectedTag,onChange:e=>a.onTagFilterChange(e.currentTarget.value),children:[(0,V.jsx)(`option`,{value:``,children:e(`opt-all-tags`)}),a.tags.map(e=>(0,V.jsx)(`option`,{value:e.tag,children:e.displayName||e.tag},e.tag))]})]}),a.selectedKeyword?(0,V.jsxs)(`div`,{className:`space-y-1`,children:[(0,V.jsx)(`span`,{className:`block text-sm font-medium`,children:e(`label-keyword`)}),(0,V.jsxs)(`div`,{className:`flex h-8 items-center gap-1`,children:[(0,V.jsx)(El,{keyword:a.selectedKeyword,active:!0}),(0,V.jsx)(W,{variant:`ghost`,size:`icon-xs`,onClick:()=>a.onKeywordFilterChange(``),"aria-label":e(`btn-clear-keyword`),title:e(`btn-clear-keyword`),children:(0,V.jsx)(Ne,{className:`size-3.5`})})]})]}):null,(0,V.jsxs)(`div`,{className:`flex flex-1 items-end gap-1.5 min-w-56`,children:[(0,V.jsxs)(`div`,{className:`flex-1 space-y-1`,children:[(0,V.jsx)(Sl,{htmlFor:`search-input`,className:`sr-only`,children:e(`placeholder-search`)}),(0,V.jsx)(Wp,{id:`search-input`,placeholder:e(`placeholder-search`),value:a.searchInput,onChange:e=>a.setSearchInput(e.target.value),onKeyDown:e=>e.key===`Enter`&&a.performSearch()})]}),(0,V.jsx)(W,{variant:`outline`,size:`icon`,onClick:a.performSearch,children:(0,V.jsx)(Ce,{className:`size-4`})}),a.isSearching?(0,V.jsx)(W,{variant:`outline`,size:`icon`,onClick:a.clearSearch,children:(0,V.jsx)(Ne,{className:`size-4`})}):null]}),(0,V.jsxs)(`div`,{className:`flex flex-wrap gap-1.5`,children:[(0,V.jsx)(Ol,{content:e(`tooltip-cleanup`),children:(0,V.jsxs)(W,{variant:`outline`,size:`sm`,onClick:a.runCleanup,children:[(0,V.jsx)(Oe,{className:`size-3.5`}),e(`btn-cleanup`)]})}),(0,V.jsx)(Ol,{content:e(`tooltip-deduplicate`),children:(0,V.jsxs)(W,{variant:`outline`,size:`sm`,onClick:a.runDeduplication,children:[(0,V.jsx)(xe,{className:`size-3.5`}),e(`btn-deduplicate`)]})})]}),a.selectedIds.size>0?(0,V.jsxs)(`div`,{className:`flex w-full flex-wrap items-center gap-2 border-t border-border pt-3`,children:[(0,V.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:e(`text-selected`,{count:a.selectedIds.size})}),(0,V.jsx)(W,{variant:`secondary`,size:`xs`,onClick:a.selectAllCurrentPage,children:e(`btn-select-all`)}),(0,V.jsx)(W,{variant:`destructive`,size:`xs`,onClick:a.bulkDelete,children:e(`btn-delete-selected`)}),(0,V.jsx)(W,{variant:`ghost`,size:`xs`,onClick:a.deselectAll,children:e(`btn-deselect-all`)})]}):null]}),a.migrationNeeded?(0,V.jsxs)(Ym,{variant:`destructive`,className:`space-y-3`,children:[(0,V.jsx)(ke,{}),(0,V.jsxs)(Xm,{className:`space-y-3`,children:[(0,V.jsx)(`p`,{children:a.migrationMessage||e(`migration-mismatch`)}),(0,V.jsxs)(`label`,{className:`flex items-start gap-2 text-sm`,children:[(0,V.jsx)(Hi,{checked:a.migrationConfirmed,onCheckedChange:e=>a.setMigrationConfirmed(e===!0),className:`mt-0.5`}),(0,V.jsx)(`span`,{children:e(`migration-understand`)})]}),(0,V.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[(0,V.jsx)(W,{variant:`destructive`,size:`sm`,disabled:!a.migrationConfirmed,onClick:()=>a.runMigration(`fresh-start`),children:e(`btn-fresh-start`)}),(0,V.jsx)(W,{variant:`secondary`,size:`sm`,disabled:!a.migrationConfirmed,onClick:()=>a.runMigration(`re-embed`),children:e(`btn-reembed`)})]})]})]}):null,(0,V.jsx)(Kf,{memories:a.memories,selectedIds:a.selectedIds,currentPage:a.currentPage,totalPages:a.totalPages,totalItems:a.totalItems,isSearching:a.isSearching,loading:a.loadingMemories,error:a.memoriesError,onSelect:a.onSelect,onPageChange:e=>{let t=a.currentPage+e;a.setCurrentPage(t),a.loadMemories({page:t})},onPin:a.pinMemory,onUnpin:a.unpinMemory,onEdit:a.openEdit,onDeleteMemory:a.deleteMemory,onDeletePrompt:a.deletePrompt,activeKeyword:a.selectedKeyword,onKeywordClick:e=>a.onKeywordFilterChange(a.selectedKeyword.toLowerCase()===e.toLowerCase()?``:e)}),(0,V.jsxs)(`section`,{className:`rounded-xl border border-border bg-card p-4 space-y-3`,children:[(0,V.jsx)(`h2`,{className:`text-sm text-muted-foreground`,children:e(`section-add`)}),(0,V.jsxs)(`form`,{className:`space-y-3`,onSubmit:a.addMemory,children:[(0,V.jsxs)(`div`,{className:`grid gap-3 md:grid-cols-3`,children:[(0,V.jsxs)(`div`,{className:`space-y-1`,children:[(0,V.jsx)(Sl,{htmlFor:`add-tag`,children:e(`label-tag`)}),(0,V.jsxs)(un,{id:`add-tag`,required:!0,className:`flex h-8 w-full rounded-lg border border-border bg-background px-2 text-sm`,value:a.addTag,onChange:e=>a.setAddTag(e.target.value),children:[(0,V.jsx)(`option`,{value:``,children:e(`opt-select-tag`)}),a.tags.map(e=>(0,V.jsx)(`option`,{value:e.tag,children:e.displayName||e.tag},e.tag))]})]}),(0,V.jsxs)(`div`,{className:`space-y-1`,children:[(0,V.jsx)(Sl,{htmlFor:`add-type`,children:e(`label-type`)}),(0,V.jsx)(un,{id:`add-type`,className:`flex h-8 w-full rounded-lg border border-border bg-background px-2 text-sm`,value:a.addType,onChange:e=>a.setAddType(e.target.value),children:$m.map(t=>(0,V.jsx)(`option`,{value:t,children:e(t?`opt-${t}`:`opt-other`)},t||`other`))})]}),(0,V.jsxs)(`div`,{className:`space-y-1`,children:[(0,V.jsx)(Sl,{htmlFor:`add-tags`,children:e(`label-tags`)}),(0,V.jsx)(Wp,{id:`add-tags`,placeholder:e(`placeholder-tags`),value:a.addTags,onChange:e=>a.setAddTags(e.target.value)})]})]}),(0,V.jsxs)(`div`,{className:`space-y-1`,children:[(0,V.jsx)(Sl,{htmlFor:`add-content`,children:e(`label-content`)}),(0,V.jsx)(Cl,{id:`add-content`,rows:4,required:!0,placeholder:e(`placeholder-content`),value:a.addContent,onChange:e=>a.setAddContent(e.target.value)})]}),(0,V.jsxs)(W,{type:`submit`,children:[(0,V.jsx)(be,{className:`size-3.5`}),e(`btn-add-memory`)]})]})]})]}):n===`settings`?(0,V.jsx)(qm,{}):(0,V.jsx)(Zp,{profile:o.userProfile,loading:o.loadingProfile,onRefresh:o.refreshProfile,onCleanup:()=>o.setAiCleanupOpen(!0)})]})]})]}),(0,V.jsx)(wl,{open:a.editOpen,onOpenChange:a.setEditOpen,content:a.editContent,onSave:a.saveEdit}),(0,V.jsx)(Qp,{open:a.tagMigrationOpen,onOpenChange:a.setTagMigrationOpen,count:a.tagMigrationCount,onComplete:()=>{a.loadMemories(),a.loadStats()}}),(0,V.jsx)(jc,{open:o.aiCleanupOpen,onOpenChange:o.setAiCleanupOpen,profile:o.userProfile,onApplied:o.loadUserProfile})]})}ml();var th=document.getElementById(`app`);if(!th)throw Error(`Root element #app not found`);(0,v.createRoot)(th).render((0,V.jsx)(_.StrictMode,{children:(0,V.jsx)(sr,{children:(0,V.jsx)(eh,{})})}));
@@ -5,7 +5,7 @@
5
5
  <link rel="icon" type="image/x-icon" href="/favicon.ico" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>omms Memory Explorer</title>
8
- <script type="module" crossorigin src="/assets/index-jqmX5i_i.js"></script>
8
+ <script type="module" crossorigin src="/assets/index-D5A9vMgC.js"></script>
9
9
  <link rel="stylesheet" crossorigin href="/assets/index-DvZhF1BJ.css">
10
10
  </head>
11
11
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "om-memory-system",
3
- "version": "3.5.0-next.32",
3
+ "version": "3.5.0-next.34",
4
4
  "description": "Opinionated modular memory system for coding agents (OpenCode + Pi)",
5
5
  "type": "module",
6
6
  "main": "dist/plugin.js",