@withone/cli 1.44.2 → 1.45.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -268,6 +268,7 @@ one actions execute stripe <actionId> <connectionKey> \
268
268
  | `--mock` | Return example response without making an API call |
269
269
  | `--skip-validation` | Skip input validation against the action schema |
270
270
  | `--output <path>` | Save response to a file (for binary downloads) |
271
+ | `--no-cache` | Bypass the cached action details and re-fetch them; the fresh details still refresh the cache (execution itself is never cached) |
271
272
 
272
273
  The CLI validates required parameters (path variables, query params, body fields) against the action schema before executing. Missing params return a clear error with the flag name and description. Pass `--skip-validation` to bypass.
273
274
 
@@ -289,11 +290,11 @@ Each segment follows the same format: `<platform> <actionId> <connectionKey> [-d
289
290
  | `--parallel` | Enable parallel mode |
290
291
  | `--max-concurrency <n>` | Max concurrent actions per batch (default: 5) |
291
292
 
292
- Agent-mode output includes `parallel: true`, per-action `status`/`durationMs`/`response`, plus `totalDurationMs`, `succeeded`, and `failed` counts.
293
+ Agent-mode output includes `parallel: true`, per-action `status`/`durationMs`/`response`/`_preflight` (`{"cache":"hit"|"miss"}`), plus `totalDurationMs`, `succeeded`, and `failed` counts.
293
294
 
294
295
  ### `one cache`
295
296
 
296
- Manage the local cache for knowledge and search responses. The CLI automatically caches `actions knowledge` and `actions search` results so repeated calls serve instantly from disk.
297
+ Manage the local cache for knowledge and search responses. The CLI automatically caches `actions knowledge` and `actions search` results so repeated calls serve instantly from disk. `actions execute` reuses the cached action details (method, path, validation schema) for its preflight lookup, so a knowledge call followed by execute costs a single API round trip.
297
298
 
298
299
  ```bash
299
300
  one cache list # List all cached entries with age and status
@@ -309,11 +310,12 @@ Knowledge and search commands also support cache flags:
309
310
  one actions knowledge gmail <actionId> --no-cache # Skip cache, fetch fresh
310
311
  one actions knowledge gmail <actionId> --cache-status # Check cache status
311
312
  one actions search gmail "send email" --no-cache # Skip cache for search
313
+ one actions execute gmail <actionId> <key> --no-cache # Fresh action-details lookup
312
314
  ```
313
315
 
314
316
  Default TTL is 1 hour. Configure via `ONE_CACHE_TTL` environment variable or `cacheTtl` in `~/.one/config.json`.
315
317
 
316
- Note: `actions execute` is never cached — it always hits the API fresh.
318
+ Note: execution responses are never cached — the action always runs live. Only action metadata (docs, method, path, schema) is cached, and agent-mode execute output reports it via `"_preflight": {"cache": "hit"|"miss"}`.
317
319
 
318
320
  ### `one mem` — unified memory store
319
321
 
@@ -0,0 +1,211 @@
1
+ import {
2
+ getOpenAiApiKey,
3
+ readConfig,
4
+ setOpenAiApiKey,
5
+ writeConfig
6
+ } from "./chunk-TVIZC7AC.js";
7
+
8
+ // src/lib/memory/config.ts
9
+ var DEFAULT_MEMORY_CONFIG = {
10
+ backend: "embedded-postgres",
11
+ plugins: [],
12
+ embedding: {
13
+ provider: "none",
14
+ model: "text-embedding-3-small",
15
+ dimensions: 1536
16
+ },
17
+ defaults: {
18
+ trackAccessOnSearch: true,
19
+ embedOnAdd: true,
20
+ embedOnSync: false
21
+ }
22
+ };
23
+ function getMemoryConfig() {
24
+ const config = readConfig();
25
+ return config?.memory ?? null;
26
+ }
27
+ function getMemoryConfigOrDefault() {
28
+ return getMemoryConfig() ?? DEFAULT_MEMORY_CONFIG;
29
+ }
30
+ function memoryConfigExists() {
31
+ return getMemoryConfig() !== null;
32
+ }
33
+ function updateMemoryConfig(patch, opts = {}) {
34
+ const config = readConfig();
35
+ if (!config) {
36
+ throw new Error("No One config found. Run `one init` first.");
37
+ }
38
+ const current = config.memory ?? DEFAULT_MEMORY_CONFIG;
39
+ const next = opts.replace ? patch : { ...current, ...patch };
40
+ config.memory = next;
41
+ writeConfig(config);
42
+ return next;
43
+ }
44
+ function getEmbeddingApiKey() {
45
+ const fromCore = getOpenAiApiKey();
46
+ if (fromCore) return fromCore;
47
+ const mem = getMemoryConfig();
48
+ return mem?.embedding.apiKey ?? null;
49
+ }
50
+ function setOpenAiApiKey2(key) {
51
+ setOpenAiApiKey(key);
52
+ if (key === "") return;
53
+ const mem = getMemoryConfig();
54
+ if (!mem) return;
55
+ if (mem.embedding.provider === "openai") return;
56
+ updateMemoryConfig({
57
+ ...mem,
58
+ embedding: { ...mem.embedding, provider: "openai" }
59
+ });
60
+ }
61
+
62
+ // src/lib/memory/embedding.ts
63
+ var FETCH_TIMEOUT_MS = 3e4;
64
+ function fetchWithTimeout(url, init, timeoutMs) {
65
+ const ctrl = new AbortController();
66
+ const t = setTimeout(() => ctrl.abort(), timeoutMs);
67
+ return fetch(url, { ...init, signal: ctrl.signal }).finally(() => clearTimeout(t));
68
+ }
69
+ async function embed(text, opts = {}) {
70
+ const clean = text?.trim();
71
+ if (!clean) return null;
72
+ const cfg = getMemoryConfigOrDefault();
73
+ if (cfg.embedding.provider !== "openai") return null;
74
+ const apiKey = getEmbeddingApiKey();
75
+ if (!apiKey) return null;
76
+ const model = opts.model ?? cfg.embedding.model;
77
+ const dimensions = cfg.embedding.dimensions;
78
+ for (let attempt = 0; attempt < 3; attempt++) {
79
+ try {
80
+ const res = await fetchWithTimeout("https://api.openai.com/v1/embeddings", {
81
+ method: "POST",
82
+ headers: {
83
+ "Content-Type": "application/json",
84
+ Authorization: `Bearer ${apiKey}`
85
+ },
86
+ body: JSON.stringify({
87
+ model,
88
+ input: clean.slice(0, 8e3),
89
+ dimensions
90
+ })
91
+ }, FETCH_TIMEOUT_MS);
92
+ if (!res.ok) {
93
+ if (res.status === 429 || res.status >= 500) {
94
+ await sleep(500 * (attempt + 1));
95
+ continue;
96
+ }
97
+ const body2 = await res.text();
98
+ throw new Error(`OpenAI embeddings ${res.status}: ${body2}`);
99
+ }
100
+ const body = await res.json();
101
+ const vector = body.data[0]?.embedding;
102
+ if (!vector || vector.length !== dimensions) {
103
+ throw new Error(`Unexpected embedding shape (got length ${vector?.length})`);
104
+ }
105
+ return { vector, model: `openai:${model}` };
106
+ } catch (err) {
107
+ if (attempt === 2) {
108
+ process.stderr.write(`[mem] embedding failed: ${err instanceof Error ? err.message : String(err)}
109
+ `);
110
+ return null;
111
+ }
112
+ await sleep(500 * (attempt + 1));
113
+ }
114
+ }
115
+ return null;
116
+ }
117
+ async function embedBatch(texts, opts = {}) {
118
+ if (texts.length === 0) return [];
119
+ const cfg = getMemoryConfigOrDefault();
120
+ if (cfg.embedding.provider !== "openai") return texts.map(() => null);
121
+ const apiKey = getEmbeddingApiKey();
122
+ if (!apiKey) return texts.map(() => null);
123
+ const model = opts.model ?? cfg.embedding.model;
124
+ const dimensions = cfg.embedding.dimensions;
125
+ const active = [];
126
+ texts.forEach((t, i) => {
127
+ const clean = t?.trim();
128
+ if (clean) active.push({ index: i, input: clean.slice(0, 8e3) });
129
+ });
130
+ if (active.length === 0) return texts.map(() => null);
131
+ const result = texts.map(() => null);
132
+ for (let attempt = 0; attempt < 3; attempt++) {
133
+ try {
134
+ const res = await fetchWithTimeout("https://api.openai.com/v1/embeddings", {
135
+ method: "POST",
136
+ headers: {
137
+ "Content-Type": "application/json",
138
+ Authorization: `Bearer ${apiKey}`
139
+ },
140
+ body: JSON.stringify({
141
+ model,
142
+ input: active.map((a) => a.input),
143
+ dimensions
144
+ })
145
+ }, FETCH_TIMEOUT_MS);
146
+ if (!res.ok) {
147
+ if (res.status === 429 || res.status >= 500) {
148
+ await sleep(500 * (attempt + 1));
149
+ continue;
150
+ }
151
+ const body2 = await res.text();
152
+ throw new Error(`OpenAI embeddings ${res.status}: ${body2}`);
153
+ }
154
+ const body = await res.json();
155
+ for (const item of body.data) {
156
+ const slot = active[item.index];
157
+ if (!slot) continue;
158
+ result[slot.index] = { vector: item.embedding, model: `openai:${model}` };
159
+ }
160
+ return result;
161
+ } catch (err) {
162
+ if (attempt === 2) {
163
+ process.stderr.write(`[mem] batch embedding failed: ${err instanceof Error ? err.message : String(err)}
164
+ `);
165
+ return result;
166
+ }
167
+ await sleep(500 * (attempt + 1));
168
+ }
169
+ }
170
+ return result;
171
+ }
172
+ function sleep(ms) {
173
+ return new Promise((resolve) => setTimeout(resolve, ms));
174
+ }
175
+ function defaultSearchableText(data, maxLen = 4e3) {
176
+ const parts = [];
177
+ const walk = (value, depth = 0) => {
178
+ if (value === null || value === void 0) return;
179
+ if (typeof value === "string" && value.trim()) {
180
+ parts.push(value.trim());
181
+ return;
182
+ }
183
+ if (typeof value === "number" || typeof value === "boolean") {
184
+ parts.push(String(value));
185
+ return;
186
+ }
187
+ if (depth > 4) return;
188
+ if (Array.isArray(value)) {
189
+ for (const v of value) walk(v, depth + 1);
190
+ return;
191
+ }
192
+ if (typeof value === "object") {
193
+ for (const v of Object.values(value)) walk(v, depth + 1);
194
+ }
195
+ };
196
+ walk(data);
197
+ const joined = parts.join(" ").replace(/\s+/g, " ").trim();
198
+ return joined.length > maxLen ? joined.slice(0, maxLen) : joined;
199
+ }
200
+
201
+ export {
202
+ DEFAULT_MEMORY_CONFIG,
203
+ getMemoryConfig,
204
+ getMemoryConfigOrDefault,
205
+ memoryConfigExists,
206
+ updateMemoryConfig,
207
+ setOpenAiApiKey2 as setOpenAiApiKey,
208
+ embed,
209
+ embedBatch,
210
+ defaultSearchableText
211
+ };