@withone/cli 1.41.0 → 1.43.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,477 @@
1
+ // src/lib/config.ts
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import os from "os";
5
+ function configDir() {
6
+ return path.join(os.homedir(), ".one");
7
+ }
8
+ function configFile() {
9
+ return path.join(configDir(), "config.json");
10
+ }
11
+ function projectsDir() {
12
+ return path.join(configDir(), "projects");
13
+ }
14
+ function getProjectRoot(cwd = process.cwd()) {
15
+ let dir = path.resolve(cwd);
16
+ const root = path.parse(dir).root;
17
+ while (dir !== root) {
18
+ if (fs.existsSync(path.join(dir, ".git")) || fs.existsSync(path.join(dir, "package.json"))) {
19
+ return dir;
20
+ }
21
+ dir = path.dirname(dir);
22
+ }
23
+ return path.resolve(cwd);
24
+ }
25
+ function getProjectSlug(projectRoot = getProjectRoot()) {
26
+ return projectRoot.replace(/[\\/]/g, "-");
27
+ }
28
+ function getProjectConfigDir(projectRoot = getProjectRoot()) {
29
+ return path.join(projectsDir(), getProjectSlug(projectRoot));
30
+ }
31
+ function getProjectConfigPath(projectRoot = getProjectRoot()) {
32
+ return path.join(getProjectConfigDir(projectRoot), "config.json");
33
+ }
34
+ function getGlobalConfigPath() {
35
+ return configFile();
36
+ }
37
+ function resolveConfig() {
38
+ const projectRoot = getProjectRoot();
39
+ const projectSlug = getProjectSlug(projectRoot);
40
+ const projectPath = getProjectConfigPath(projectRoot);
41
+ if (fs.existsSync(projectPath)) {
42
+ const config = readConfigFile(projectPath);
43
+ if (config) {
44
+ return { config, scope: "project", path: projectPath, projectRoot, projectSlug };
45
+ }
46
+ }
47
+ const root = path.parse(process.cwd()).root;
48
+ let dir = path.resolve(process.cwd());
49
+ while (dir !== root) {
50
+ dir = path.dirname(dir);
51
+ const slug = getProjectSlug(dir);
52
+ const configPath = path.join(projectsDir(), slug, "config.json");
53
+ if (fs.existsSync(configPath)) {
54
+ const config = readConfigFile(configPath);
55
+ if (config) {
56
+ return { config, scope: "project", path: configPath, projectRoot: dir, projectSlug: slug };
57
+ }
58
+ }
59
+ }
60
+ if (fs.existsSync(configFile())) {
61
+ const config = readConfigFile(configFile());
62
+ if (config) {
63
+ return { config, scope: "global", path: configFile(), projectRoot, projectSlug };
64
+ }
65
+ }
66
+ return { config: null, scope: null, path: configFile(), projectRoot, projectSlug };
67
+ }
68
+ function readConfigFile(filePath) {
69
+ try {
70
+ const content = fs.readFileSync(filePath, "utf-8");
71
+ return JSON.parse(content);
72
+ } catch {
73
+ return null;
74
+ }
75
+ }
76
+ function configExists() {
77
+ return resolveConfig().config !== null;
78
+ }
79
+ function globalConfigExists() {
80
+ return fs.existsSync(configFile());
81
+ }
82
+ function projectConfigExists(projectRoot = getProjectRoot()) {
83
+ return fs.existsSync(getProjectConfigPath(projectRoot));
84
+ }
85
+ function readConfig() {
86
+ return resolveConfig().config;
87
+ }
88
+ function readGlobalConfig() {
89
+ if (!fs.existsSync(configFile())) return null;
90
+ return readConfigFile(configFile());
91
+ }
92
+ function readProjectConfig() {
93
+ const projectPath = getProjectConfigPath();
94
+ if (!fs.existsSync(projectPath)) return null;
95
+ return readConfigFile(projectPath);
96
+ }
97
+ function writeConfig(config, scope) {
98
+ const targetScope = scope ?? resolveConfig().scope ?? "global";
99
+ if (targetScope === "project") {
100
+ const dir = getProjectConfigDir();
101
+ if (!fs.existsSync(dir)) {
102
+ fs.mkdirSync(dir, { recursive: true, mode: 448 });
103
+ }
104
+ const filePath = getProjectConfigPath();
105
+ fs.writeFileSync(filePath, JSON.stringify(config, null, 2), { mode: 384 });
106
+ return;
107
+ }
108
+ if (!fs.existsSync(configDir())) {
109
+ fs.mkdirSync(configDir(), { mode: 448 });
110
+ }
111
+ fs.writeFileSync(configFile(), JSON.stringify(config, null, 2), { mode: 384 });
112
+ }
113
+ function readOneRc() {
114
+ const rcPath = path.join(process.cwd(), ".onerc");
115
+ if (!fs.existsSync(rcPath)) return {};
116
+ try {
117
+ const content = fs.readFileSync(rcPath, "utf-8");
118
+ const result = {};
119
+ for (const line of content.split("\n")) {
120
+ const trimmed = line.trim();
121
+ if (!trimmed || trimmed.startsWith("#")) continue;
122
+ const eqIndex = trimmed.indexOf("=");
123
+ if (eqIndex === -1) continue;
124
+ const key = trimmed.slice(0, eqIndex).trim();
125
+ const value = trimmed.slice(eqIndex + 1).trim();
126
+ result[key] = value;
127
+ }
128
+ return result;
129
+ } catch {
130
+ return {};
131
+ }
132
+ }
133
+ function getApiKey() {
134
+ if (process.env.ONE_SECRET) return process.env.ONE_SECRET;
135
+ const rc = readOneRc();
136
+ if (rc.ONE_SECRET) return rc.ONE_SECRET;
137
+ return readConfig()?.apiKey ?? null;
138
+ }
139
+ function getOpenAiApiKey() {
140
+ if (process.env.OPENAI_API_KEY) return process.env.OPENAI_API_KEY;
141
+ const rc = readOneRc();
142
+ if (rc.OPENAI_API_KEY) return rc.OPENAI_API_KEY;
143
+ return readConfig()?.openaiApiKey ?? null;
144
+ }
145
+ function setOpenAiApiKey(key) {
146
+ const resolved = resolveConfig();
147
+ if (!resolved.config) {
148
+ throw new Error("No One config found. Run `one init` first.");
149
+ }
150
+ if (key === "") {
151
+ delete resolved.config.openaiApiKey;
152
+ } else {
153
+ resolved.config.openaiApiKey = key;
154
+ }
155
+ writeConfig(resolved.config, resolved.scope ?? "global");
156
+ }
157
+ function getAccessControlFromAllSources() {
158
+ const rc = readOneRc();
159
+ const fileAc = getAccessControl();
160
+ const merged = { ...fileAc };
161
+ if (rc.ONE_PERMISSIONS) {
162
+ merged.permissions = rc.ONE_PERMISSIONS;
163
+ }
164
+ if (rc.ONE_CONNECTION_KEYS) {
165
+ merged.connectionKeys = rc.ONE_CONNECTION_KEYS.split(",").map((s) => s.trim()).filter(Boolean);
166
+ }
167
+ if (rc.ONE_ACTION_IDS) {
168
+ merged.actionIds = rc.ONE_ACTION_IDS.split(",").map((s) => s.trim()).filter(Boolean);
169
+ }
170
+ if (rc.ONE_KNOWLEDGE_AGENT) {
171
+ merged.knowledgeAgent = rc.ONE_KNOWLEDGE_AGENT === "true";
172
+ }
173
+ return merged;
174
+ }
175
+ function getAccessControl() {
176
+ return readConfig()?.accessControl ?? {};
177
+ }
178
+ var DEFAULT_API_BASE = "https://api.withone.ai/v1";
179
+ function getApiBase() {
180
+ const config = readConfig();
181
+ if (config?.apiBase) return `${config.apiBase}/v1`;
182
+ return DEFAULT_API_BASE;
183
+ }
184
+ function updateApiBase(url) {
185
+ const config = readConfig();
186
+ if (!config) return;
187
+ if (url) {
188
+ config.apiBase = url;
189
+ } else {
190
+ delete config.apiBase;
191
+ }
192
+ delete config.whoami;
193
+ writeConfig(config);
194
+ }
195
+ function getCacheTtl() {
196
+ if (process.env.ONE_CACHE_TTL) {
197
+ const val = parseInt(process.env.ONE_CACHE_TTL, 10);
198
+ if (!isNaN(val) && val > 0) return val;
199
+ }
200
+ const config = readConfig();
201
+ if (config?.cacheTtl && config.cacheTtl > 0) return config.cacheTtl;
202
+ return 3600;
203
+ }
204
+ function updateAccessControl(settings) {
205
+ const config = readConfig();
206
+ if (!config) return;
207
+ const cleaned = {};
208
+ if (settings.permissions && settings.permissions !== "admin") {
209
+ cleaned.permissions = settings.permissions;
210
+ }
211
+ if (settings.connectionKeys && !(settings.connectionKeys.length === 1 && settings.connectionKeys[0] === "*")) {
212
+ cleaned.connectionKeys = settings.connectionKeys;
213
+ }
214
+ if (settings.actionIds && !(settings.actionIds.length === 1 && settings.actionIds[0] === "*")) {
215
+ cleaned.actionIds = settings.actionIds;
216
+ }
217
+ if (settings.knowledgeAgent) {
218
+ cleaned.knowledgeAgent = true;
219
+ }
220
+ if (Object.keys(cleaned).length === 0) {
221
+ delete config.accessControl;
222
+ } else {
223
+ config.accessControl = cleaned;
224
+ }
225
+ writeConfig(config);
226
+ }
227
+ function getWhoAmI() {
228
+ return readConfig()?.whoami ?? null;
229
+ }
230
+ function updateWhoAmI(whoami) {
231
+ const config = readConfig();
232
+ if (!config) return;
233
+ config.whoami = whoami;
234
+ writeConfig(config);
235
+ }
236
+ async function ensureWhoAmI(api) {
237
+ const cached = getWhoAmI();
238
+ if (cached) return cached;
239
+ try {
240
+ const whoami = await api.whoami();
241
+ updateWhoAmI(whoami);
242
+ return whoami;
243
+ } catch {
244
+ return null;
245
+ }
246
+ }
247
+ function getEnvFromApiKey(apiKey) {
248
+ return apiKey.startsWith("sk_test_") ? "test" : "live";
249
+ }
250
+
251
+ // src/lib/memory/config.ts
252
+ var DEFAULT_MEMORY_CONFIG = {
253
+ backend: "embedded-postgres",
254
+ plugins: [],
255
+ embedding: {
256
+ provider: "none",
257
+ model: "text-embedding-3-small",
258
+ dimensions: 1536
259
+ },
260
+ defaults: {
261
+ trackAccessOnSearch: true,
262
+ embedOnAdd: true,
263
+ embedOnSync: false
264
+ }
265
+ };
266
+ function getMemoryConfig() {
267
+ const config = readConfig();
268
+ return config?.memory ?? null;
269
+ }
270
+ function getMemoryConfigOrDefault() {
271
+ return getMemoryConfig() ?? DEFAULT_MEMORY_CONFIG;
272
+ }
273
+ function memoryConfigExists() {
274
+ return getMemoryConfig() !== null;
275
+ }
276
+ function updateMemoryConfig(patch, opts = {}) {
277
+ const config = readConfig();
278
+ if (!config) {
279
+ throw new Error("No One config found. Run `one init` first.");
280
+ }
281
+ const current = config.memory ?? DEFAULT_MEMORY_CONFIG;
282
+ const next = opts.replace ? patch : { ...current, ...patch };
283
+ config.memory = next;
284
+ writeConfig(config);
285
+ return next;
286
+ }
287
+ function getEmbeddingApiKey() {
288
+ const fromCore = getOpenAiApiKey();
289
+ if (fromCore) return fromCore;
290
+ const mem = getMemoryConfig();
291
+ return mem?.embedding.apiKey ?? null;
292
+ }
293
+ function setOpenAiApiKey2(key) {
294
+ setOpenAiApiKey(key);
295
+ if (key === "") return;
296
+ const mem = getMemoryConfig();
297
+ if (!mem) return;
298
+ if (mem.embedding.provider === "openai") return;
299
+ updateMemoryConfig({
300
+ ...mem,
301
+ embedding: { ...mem.embedding, provider: "openai" }
302
+ });
303
+ }
304
+
305
+ // src/lib/memory/embedding.ts
306
+ var FETCH_TIMEOUT_MS = 3e4;
307
+ function fetchWithTimeout(url, init, timeoutMs) {
308
+ const ctrl = new AbortController();
309
+ const t = setTimeout(() => ctrl.abort(), timeoutMs);
310
+ return fetch(url, { ...init, signal: ctrl.signal }).finally(() => clearTimeout(t));
311
+ }
312
+ async function embed(text, opts = {}) {
313
+ const clean = text?.trim();
314
+ if (!clean) return null;
315
+ const cfg = getMemoryConfigOrDefault();
316
+ if (cfg.embedding.provider !== "openai") return null;
317
+ const apiKey = getEmbeddingApiKey();
318
+ if (!apiKey) return null;
319
+ const model = opts.model ?? cfg.embedding.model;
320
+ const dimensions = cfg.embedding.dimensions;
321
+ for (let attempt = 0; attempt < 3; attempt++) {
322
+ try {
323
+ const res = await fetchWithTimeout("https://api.openai.com/v1/embeddings", {
324
+ method: "POST",
325
+ headers: {
326
+ "Content-Type": "application/json",
327
+ Authorization: `Bearer ${apiKey}`
328
+ },
329
+ body: JSON.stringify({
330
+ model,
331
+ input: clean.slice(0, 8e3),
332
+ dimensions
333
+ })
334
+ }, FETCH_TIMEOUT_MS);
335
+ if (!res.ok) {
336
+ if (res.status === 429 || res.status >= 500) {
337
+ await sleep(500 * (attempt + 1));
338
+ continue;
339
+ }
340
+ const body2 = await res.text();
341
+ throw new Error(`OpenAI embeddings ${res.status}: ${body2}`);
342
+ }
343
+ const body = await res.json();
344
+ const vector = body.data[0]?.embedding;
345
+ if (!vector || vector.length !== dimensions) {
346
+ throw new Error(`Unexpected embedding shape (got length ${vector?.length})`);
347
+ }
348
+ return { vector, model: `openai:${model}` };
349
+ } catch (err) {
350
+ if (attempt === 2) {
351
+ process.stderr.write(`[mem] embedding failed: ${err instanceof Error ? err.message : String(err)}
352
+ `);
353
+ return null;
354
+ }
355
+ await sleep(500 * (attempt + 1));
356
+ }
357
+ }
358
+ return null;
359
+ }
360
+ async function embedBatch(texts, opts = {}) {
361
+ if (texts.length === 0) return [];
362
+ const cfg = getMemoryConfigOrDefault();
363
+ if (cfg.embedding.provider !== "openai") return texts.map(() => null);
364
+ const apiKey = getEmbeddingApiKey();
365
+ if (!apiKey) return texts.map(() => null);
366
+ const model = opts.model ?? cfg.embedding.model;
367
+ const dimensions = cfg.embedding.dimensions;
368
+ const active = [];
369
+ texts.forEach((t, i) => {
370
+ const clean = t?.trim();
371
+ if (clean) active.push({ index: i, input: clean.slice(0, 8e3) });
372
+ });
373
+ if (active.length === 0) return texts.map(() => null);
374
+ const result = texts.map(() => null);
375
+ for (let attempt = 0; attempt < 3; attempt++) {
376
+ try {
377
+ const res = await fetchWithTimeout("https://api.openai.com/v1/embeddings", {
378
+ method: "POST",
379
+ headers: {
380
+ "Content-Type": "application/json",
381
+ Authorization: `Bearer ${apiKey}`
382
+ },
383
+ body: JSON.stringify({
384
+ model,
385
+ input: active.map((a) => a.input),
386
+ dimensions
387
+ })
388
+ }, FETCH_TIMEOUT_MS);
389
+ if (!res.ok) {
390
+ if (res.status === 429 || res.status >= 500) {
391
+ await sleep(500 * (attempt + 1));
392
+ continue;
393
+ }
394
+ const body2 = await res.text();
395
+ throw new Error(`OpenAI embeddings ${res.status}: ${body2}`);
396
+ }
397
+ const body = await res.json();
398
+ for (const item of body.data) {
399
+ const slot = active[item.index];
400
+ if (!slot) continue;
401
+ result[slot.index] = { vector: item.embedding, model: `openai:${model}` };
402
+ }
403
+ return result;
404
+ } catch (err) {
405
+ if (attempt === 2) {
406
+ process.stderr.write(`[mem] batch embedding failed: ${err instanceof Error ? err.message : String(err)}
407
+ `);
408
+ return result;
409
+ }
410
+ await sleep(500 * (attempt + 1));
411
+ }
412
+ }
413
+ return result;
414
+ }
415
+ function sleep(ms) {
416
+ return new Promise((resolve) => setTimeout(resolve, ms));
417
+ }
418
+ function defaultSearchableText(data, maxLen = 4e3) {
419
+ const parts = [];
420
+ const walk = (value, depth = 0) => {
421
+ if (value === null || value === void 0) return;
422
+ if (typeof value === "string" && value.trim()) {
423
+ parts.push(value.trim());
424
+ return;
425
+ }
426
+ if (typeof value === "number" || typeof value === "boolean") {
427
+ parts.push(String(value));
428
+ return;
429
+ }
430
+ if (depth > 4) return;
431
+ if (Array.isArray(value)) {
432
+ for (const v of value) walk(v, depth + 1);
433
+ return;
434
+ }
435
+ if (typeof value === "object") {
436
+ for (const v of Object.values(value)) walk(v, depth + 1);
437
+ }
438
+ };
439
+ walk(data);
440
+ const joined = parts.join(" ").replace(/\s+/g, " ").trim();
441
+ return joined.length > maxLen ? joined.slice(0, maxLen) : joined;
442
+ }
443
+
444
+ export {
445
+ getProjectRoot,
446
+ getProjectConfigPath,
447
+ getGlobalConfigPath,
448
+ resolveConfig,
449
+ configExists,
450
+ globalConfigExists,
451
+ projectConfigExists,
452
+ readConfig,
453
+ readGlobalConfig,
454
+ readProjectConfig,
455
+ writeConfig,
456
+ getApiKey,
457
+ getOpenAiApiKey,
458
+ getAccessControlFromAllSources,
459
+ getAccessControl,
460
+ getApiBase,
461
+ updateApiBase,
462
+ getCacheTtl,
463
+ updateAccessControl,
464
+ getWhoAmI,
465
+ updateWhoAmI,
466
+ ensureWhoAmI,
467
+ getEnvFromApiKey,
468
+ DEFAULT_MEMORY_CONFIG,
469
+ getMemoryConfig,
470
+ getMemoryConfigOrDefault,
471
+ memoryConfigExists,
472
+ updateMemoryConfig,
473
+ setOpenAiApiKey2 as setOpenAiApiKey,
474
+ embed,
475
+ embedBatch,
476
+ defaultSearchableText
477
+ };
@@ -1,3 +1,8 @@
1
+ import {
2
+ getByDotPath,
3
+ setByDotPath
4
+ } from "./chunk-44CV5IMX.js";
5
+
1
6
  // src/lib/flow-runner.ts
2
7
  import fs2 from "fs";
3
8
  import path2 from "path";
@@ -442,6 +447,9 @@ var OneApi = class {
442
447
  async listRelayEventTypes(platform) {
443
448
  return this.requestFull({ path: "/webhooks/relay/event-types", queryParams: { platform } });
444
449
  }
450
+ async listRelayPlatforms() {
451
+ return this.requestFull({ path: "/webhooks/relay/platforms" });
452
+ }
445
453
  async waitForConnection(platform, timeoutMs = 5 * 60 * 1e3, pollIntervalMs = 5e3, onPoll) {
446
454
  const startTime = Date.now();
447
455
  const existingConnections = await this.listConnections();
@@ -568,40 +576,6 @@ function validateActionInput(action, args) {
568
576
  return { valid: false, missing };
569
577
  }
570
578
 
571
- // src/lib/dot-path.ts
572
- function getByDotPath(obj, dotPath) {
573
- const parts = dotPath.split(".").flatMap((part) => {
574
- const bracketMatch = part.match(/^([^[]+)\[(\d+)\]$/);
575
- if (bracketMatch) {
576
- return [bracketMatch[1], bracketMatch[2]];
577
- }
578
- return [part];
579
- });
580
- let current = obj;
581
- for (const part of parts) {
582
- if (current === null || current === void 0) return void 0;
583
- if (Array.isArray(current) && /^\d+$/.test(part)) {
584
- current = current[parseInt(part, 10)];
585
- } else if (typeof current === "object") {
586
- current = current[part];
587
- } else {
588
- return void 0;
589
- }
590
- }
591
- return current;
592
- }
593
- function setByDotPath(obj, dotPath, value) {
594
- const parts = dotPath.split(".");
595
- let current = obj;
596
- for (let i = 0; i < parts.length - 1; i++) {
597
- if (current[parts[i]] === void 0 || current[parts[i]] === null) {
598
- current[parts[i]] = {};
599
- }
600
- current = current[parts[i]];
601
- }
602
- current[parts[parts.length - 1]] = value;
603
- }
604
-
605
579
  // src/lib/flow-engine.ts
606
580
  var execAsync = promisify(exec);
607
581
  function sleep2(ms) {
@@ -1137,7 +1111,7 @@ async function executeSubflowStep(step, context, api, permissions, allowedAction
1137
1111
  if (flowStack.includes(resolvedKey)) {
1138
1112
  throw new Error(`Circular flow detected: ${[...flowStack, resolvedKey].join(" \u2192 ")}`);
1139
1113
  }
1140
- const { loadFlowWithMeta: loadFlowWithMeta2 } = await import("./flow-runner-26LNPU4F.js");
1114
+ const { loadFlowWithMeta: loadFlowWithMeta2 } = await import("./flow-runner-XSJ4US5S.js");
1141
1115
  const { flow: subFlow, rootDir: subRootDir } = loadFlowWithMeta2(resolvedKey);
1142
1116
  const subContext = await executeFlow(
1143
1117
  subFlow,
@@ -2791,7 +2765,6 @@ export {
2791
2765
  getStepTypeDescriptor,
2792
2766
  getNestedStepsKeys,
2793
2767
  generateFlowGuide,
2794
- getByDotPath,
2795
2768
  FlowRunner,
2796
2769
  resolveFlowPath,
2797
2770
  getFlowRootDir,
@@ -0,0 +1,158 @@
1
+ import {
2
+ getMemoryConfigOrDefault,
3
+ getOpenAiApiKey,
4
+ readConfig
5
+ } from "./chunk-AU2ZEEMS.js";
6
+
7
+ // src/lib/output.ts
8
+ import * as p from "@clack/prompts";
9
+ var _agentMode = false;
10
+ function setAgentMode(value) {
11
+ _agentMode = value;
12
+ }
13
+ function isAgentMode() {
14
+ return _agentMode || process.env.ONE_AGENT === "1";
15
+ }
16
+ function createSpinner() {
17
+ if (isAgentMode()) {
18
+ return { start() {
19
+ }, stop() {
20
+ } };
21
+ }
22
+ return p.spinner();
23
+ }
24
+ function intro2(msg) {
25
+ if (!isAgentMode()) p.intro(msg);
26
+ }
27
+ function outro2(msg) {
28
+ if (!isAgentMode()) p.outro(msg);
29
+ }
30
+ function note2(msg, title) {
31
+ if (!isAgentMode()) p.note(msg, title);
32
+ }
33
+ function cancel2(msg) {
34
+ if (!isAgentMode()) p.cancel(msg);
35
+ }
36
+ function json(data) {
37
+ process.stdout.write(JSON.stringify(data) + "\n");
38
+ }
39
+ function error(message, exitCode = 1) {
40
+ if (isAgentMode()) {
41
+ json({ error: message });
42
+ } else {
43
+ p.cancel(message);
44
+ }
45
+ process.exit(exitCode);
46
+ }
47
+
48
+ // src/commands/mem/util.ts
49
+ function requireMemoryInit() {
50
+ if (!readConfig()) {
51
+ error("No One config found. Run `one init` first.");
52
+ }
53
+ }
54
+ function parseJsonArg(arg, field = "data") {
55
+ try {
56
+ const parsed = JSON.parse(arg);
57
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
58
+ error(`${field} must be a JSON object, not ${Array.isArray(parsed) ? "an array" : typeof parsed}.`);
59
+ }
60
+ return parsed;
61
+ } catch (err) {
62
+ error(`Invalid JSON for ${field}: ${err instanceof Error ? err.message : String(err)}`);
63
+ }
64
+ }
65
+ function parseCsv(value) {
66
+ if (!value) return void 0;
67
+ return value.split(",").map((s) => s.trim()).filter(Boolean);
68
+ }
69
+ function parsePositiveInt(value, fallback, label = "value") {
70
+ if (value === void 0) return fallback;
71
+ const n = Number.parseInt(value, 10);
72
+ if (!Number.isFinite(n) || n <= 0) {
73
+ error(`${label} must be a positive integer`);
74
+ }
75
+ return n;
76
+ }
77
+ function printRecord(record) {
78
+ if (isAgentMode()) {
79
+ json(record);
80
+ return;
81
+ }
82
+ console.log(JSON.stringify(record, null, 2));
83
+ }
84
+ function printList(items) {
85
+ if (isAgentMode()) {
86
+ json({ items, total: items.length });
87
+ return;
88
+ }
89
+ if (items.length === 0) {
90
+ console.log("(no results)");
91
+ return;
92
+ }
93
+ console.log(JSON.stringify(items, null, 2));
94
+ }
95
+ function okJson(payload) {
96
+ if (isAgentMode()) {
97
+ json(payload);
98
+ return;
99
+ }
100
+ console.log(JSON.stringify(payload, null, 2));
101
+ }
102
+ function semanticSearchUpgradeHint(opts = {}) {
103
+ const cfg = getMemoryConfigOrDefault();
104
+ const keyPresent = !!getOpenAiApiKey();
105
+ const providerOn = cfg.embedding.provider === "openai";
106
+ if (opts.vectorSearchAvailable === false) {
107
+ return {
108
+ capability: "semantic_search",
109
+ available: true,
110
+ currentMode: "fts_only",
111
+ how: "Install pgvector for the bundled Postgres: `brew install pgvector` (or point at a remote Postgres with pgvector via `one mem config set backend postgres`)",
112
+ benefit: "Ranks memories by meaning, not just keyword overlap \u2014 finds relevant records even when the query and the data use different words."
113
+ };
114
+ }
115
+ if (providerOn && keyPresent) return null;
116
+ if (!keyPresent) {
117
+ return {
118
+ capability: "semantic_search",
119
+ available: true,
120
+ currentMode: "fts_only",
121
+ how: 'Add an OpenAI key: `one init` (then "Add OpenAI key"), or `one mem config set embedding.apiKey sk-...`',
122
+ benefit: "Ranks memories by meaning, not just keyword overlap \u2014 finds relevant records even when the query and the data use different words."
123
+ };
124
+ }
125
+ return {
126
+ capability: "semantic_search",
127
+ available: true,
128
+ currentMode: "fts_only",
129
+ how: "Flip the provider on: `one mem config set embedding.provider openai`",
130
+ benefit: "Ranks memories by meaning, not just keyword overlap."
131
+ };
132
+ }
133
+ function semanticSearchUpgradeLine(opts = {}) {
134
+ const hint = semanticSearchUpgradeHint(opts);
135
+ if (!hint) return "";
136
+ return `tip: semantic search available \u2014 ${hint.how}`;
137
+ }
138
+
139
+ export {
140
+ setAgentMode,
141
+ isAgentMode,
142
+ createSpinner,
143
+ intro2 as intro,
144
+ outro2 as outro,
145
+ note2 as note,
146
+ cancel2 as cancel,
147
+ json,
148
+ error,
149
+ requireMemoryInit,
150
+ parseJsonArg,
151
+ parseCsv,
152
+ parsePositiveInt,
153
+ printRecord,
154
+ printList,
155
+ okJson,
156
+ semanticSearchUpgradeHint,
157
+ semanticSearchUpgradeLine
158
+ };