@withone/cli 1.42.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.
package/README.md CHANGED
@@ -303,24 +303,61 @@ Default TTL is 1 hour. Configure via `ONE_CACHE_TTL` environment variable or `ca
303
303
 
304
304
  Note: `actions execute` is never cached — it always hits the API fresh.
305
305
 
306
- ### `one sync`
306
+ ### `one mem` — unified memory store
307
307
 
308
- Sync platform data into local SQLite for instant queries, full-text search, scheduled refresh, and change-driven automation. The sync engine (`better-sqlite3`) is an optional dependency install it once per machine:
308
+ One ships a local memory store (a real Postgres process bootstrapped on demand via the bundled `embedded-postgres` plugin, with a `postgres` plugin for remote/self-hosted) that backs both user-authored notes and synced platform data. **Zero-config** the first `one mem` call on a fresh machine auto-initializes the cluster at `~/.one/pg/cluster/` and writes a daemon PID file so subsequent CLI invocations reuse it.
309
309
 
310
310
  ```bash
311
- one sync install && one sync doctor
311
+ # User memories works immediately on a new install
312
+ one mem add note '{"content":"Design review is Thursday"}' --tags work --weight 7
313
+ one mem search "design review" # hybrid FTS + semantic (if key set)
314
+ one mem list note --limit 20
315
+
316
+ # Listing synced platform rows — type is positional and namespaced as <platform>/<model>;
317
+ # there is NO --platform flag and NO platform column in the schema.
318
+ one mem list "gmail/threads"
319
+ one mem list "attio/attioPeople" --limit 5
320
+
321
+ # Enable semantic search (optional)
322
+ one init # re-run — prompts for OpenAI key
323
+ # OR
324
+ one mem config set embedding.apiKey sk-... # writes top-level openaiApiKey
325
+ # OR
326
+ export OPENAI_API_KEY=sk-... # no persistence
327
+
328
+ # Status + diagnostics
329
+ one mem status # backend, provider, _upgrade hint
330
+ one mem doctor # 7-check health report
312
331
  ```
313
332
 
333
+ Key surfaces: `add`, `get`, `update`, `archive`, `list`, `search` (`--deep` forces semantic), `context`, `link`/`linked`/`unlink`, `sources`, `find-by-source`, `export`, `import`, `migrate`, `vacuum`, `reindex`. Run `one guide memory` for the full reference.
334
+
335
+ ### `one sync` (and `one mem sync` alias)
336
+
337
+ Sync platform data into the unified memory store for instant queries, hybrid FTS + semantic search, scheduled refresh, and change-driven automation. Memory is always written; pass `--no-memory` to skip (rare).
338
+
314
339
  ```bash
315
- # Discover → init (one command: infer + late-bound connection + auto-test) → run
340
+ # Discover → init (seeds from built-in, auto-tests) → declare searchable → preview → run
316
341
  one sync models stripe
317
- one sync init stripe balanceTransactions # connection: { platform } baked in, test auto-run
342
+ one sync init stripe balanceTransactions
343
+
344
+ # Optional: declare clean fields for embedding, then preview
345
+ one sync init stripe balanceTransactions --config '{
346
+ "memory": {
347
+ "embed": true,
348
+ "searchable": ["description","type","amount","currency"]
349
+ }
350
+ }'
351
+ one sync test stripe/balanceTransactions --show-searchable
352
+
353
+ # Run — every row lands in memory (SQLite also written for enrich-phase compat)
318
354
  one sync run stripe --since 90d
355
+ one mem sync run stripe # identical (alias)
319
356
 
320
- # Query, search, SQL
357
+ # Query + search (reads from memory)
321
358
  one sync query stripe/balanceTransactions --where "status=available" --limit 20
322
- one sync search "refund"
323
- one sync sql stripe "SELECT count(*) FROM balanceTransactions"
359
+ one sync query stripe/customers --where 'address.city=SF' # dotted paths supported
360
+ one sync search "refund" # hybrid, per-type
324
361
 
325
362
  # Schedule unattended syncs + change hooks
326
363
  one sync schedule add stripe --every 1h
@@ -334,20 +371,23 @@ one sync run stripe --full-refresh
334
371
 
335
372
  > **Connections are late-bound.** Profiles use `"connection": { "platform": "<name>", "tag"?: "..." }` instead of literal `connectionKey` strings. The key is resolved at sync time, so `one add <platform>` (re-auth) doesn't break the profile. `tag` only needed for multi-account platforms (e.g. two Gmail accounts).
336
373
 
374
+ > **`memory.searchable` paths.** Drives what gets embedded + FTS-indexed. Supports numeric indexes (`values.name[0].full_name`) and `[]` wildcards (`messages[].snippet`, `messages[].payload.parts[].body.data`). Without declared paths the default walker concatenates every string — correct but noisy for hierarchical APIs. Always declare when `embed: true`.
375
+
337
376
  | Subcommand | What it does |
338
377
  |------------|-------------|
339
- | `install` / `doctor` | Install + verify the SQLite engine |
378
+ | `profiles [platform]` | List built-in pre-validated profiles |
379
+ | `doctor` | Verify sync engine health |
340
380
  | `models <platform>` | Discover available data models |
341
- | `init <platform> <model>` | Create profile (auto-infers all fields, auto-resolves key, auto-runs test) |
342
- | `test <platform>/<model>` | Validate + auto-fix profile from real API response (also runs inside init) |
343
- | `run <platform>` | Sync data (`--full-refresh`, `--since`, `--dry-run`) |
344
- | `query <platform>/<model>` | Query with `--where`, `--after/before`, `--refresh` |
345
- | `search <query>` | FTS5 across all synced data |
346
- | `sql <platform> <sql>` | Raw SELECT queries |
381
+ | `init <platform> <model>` | Create/patch profile (seeds from built-in, auto-tests) |
382
+ | `test <platform>/<model>` | Validate profile. `--show-searchable` previews embedded text |
383
+ | `run <platform>` | Sync data (`--full-refresh`, `--since`, `--dry-run`, `--no-memory`) |
384
+ | `query <platform>/<model>` | Query memory with `--where` (dotted paths), `--after/before` |
385
+ | `search <query>` | Hybrid FTS + semantic across all synced data |
386
+ | `list [platform]` | Show profiles, record counts, freshness |
347
387
  | `schedule add/list/status/remove/repair` | Cron-backed scheduled syncs with drift detection |
348
- | `remove <platform>` | Delete local data (`--dry-run` to preview) |
388
+ | `remove <platform>` | Delete synced data (`--dry-run` to preview) |
349
389
 
350
- Change hooks (`onInsert`, `onUpdate`, `onChange`) fire per-page during sync — pipe to a shell command, a flow, or an event log. Root-array responses (e.g. Hacker News `/v0/topstories.json` → `[9129911, 9129199, ...]`) are supported by setting `resultsPath` to `""`, `"$"`, or `"."`; primitive elements are auto-wrapped as `{ [idField]: value }`. Run `one guide sync` for the full reference.
390
+ Change hooks (`onInsert`, `onUpdate`, `onChange`) fire per-page during sync — pipe to a shell command, a flow, or an event log. Root-array responses (e.g. Hacker News `/v0/topstories.json` → `[9129911, 9129199, ...]`) are supported by setting `resultsPath` to `""`, `"$"`, or `"."`; primitive elements are auto-wrapped as `{ [idField]: value }`. Run `one guide sync` or `one guide memory` for the full reference.
351
391
 
352
392
  ### `one relay`
353
393
 
@@ -0,0 +1,38 @@
1
+ // src/lib/dot-path.ts
2
+ function getByDotPath(obj, dotPath) {
3
+ const parts = dotPath.split(".").flatMap((part) => {
4
+ const bracketMatch = part.match(/^([^[]+)\[(\d+)\]$/);
5
+ if (bracketMatch) {
6
+ return [bracketMatch[1], bracketMatch[2]];
7
+ }
8
+ return [part];
9
+ });
10
+ let current = obj;
11
+ for (const part of parts) {
12
+ if (current === null || current === void 0) return void 0;
13
+ if (Array.isArray(current) && /^\d+$/.test(part)) {
14
+ current = current[parseInt(part, 10)];
15
+ } else if (typeof current === "object") {
16
+ current = current[part];
17
+ } else {
18
+ return void 0;
19
+ }
20
+ }
21
+ return current;
22
+ }
23
+ function setByDotPath(obj, dotPath, value) {
24
+ const parts = dotPath.split(".");
25
+ let current = obj;
26
+ for (let i = 0; i < parts.length - 1; i++) {
27
+ if (current[parts[i]] === void 0 || current[parts[i]] === null) {
28
+ current[parts[i]] = {};
29
+ }
30
+ current = current[parts[i]];
31
+ }
32
+ current[parts[parts.length - 1]] = value;
33
+ }
34
+
35
+ export {
36
+ getByDotPath,
37
+ setByDotPath
38
+ };