@withone/cli 1.47.11 → 1.49.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -0
- package/dist/{chunk-VW7J2RQW.js → chunk-2TWFL3CS.js} +2 -2
- package/dist/{chunk-QV3Y5N5G.js → chunk-4WWK3GO5.js} +2 -2
- package/dist/{chunk-OADHUAEU.js → chunk-7RV7T5PX.js} +180 -23
- package/dist/{chunk-TXTRXV74.js → chunk-C7DWZ7B5.js} +60 -11
- package/dist/{chunk-HS5AHQ4V.js → chunk-CLJFSFFM.js} +2 -2
- package/dist/{chunk-PLMFRFTT.js → chunk-GXOIR3GF.js} +2 -2
- package/dist/{chunk-K6MWE2ZH.js → chunk-SO323PZP.js} +20 -8
- package/dist/{embedding-NVFDR5PK.js → embedding-2YB6CGBA.js} +2 -2
- package/dist/{flow-runner-KBTX5XYX.js → flow-runner-IBB42ED2.js} +2 -2
- package/dist/index.js +158 -35
- package/dist/{migrate-UDEANXEZ.js → migrate-TRUZVAT5.js} +5 -5
- package/dist/{runtime-B5GCQ34P.js → runtime-2PRVL2NO.js} +5 -3
- package/dist/{schema-DXHEU47V.js → schema-LCRWSFD5.js} +4 -4
- package/dist/sql-HG7R2JML.js +12 -0
- package/package.json +1 -1
- package/skills/one/SKILL.md +9 -0
- package/dist/sql-W3ZUOUNR.js +0 -12
package/README.md
CHANGED
|
@@ -329,9 +329,18 @@ One ships a local memory store (a real Postgres process bootstrapped on demand v
|
|
|
329
329
|
```bash
|
|
330
330
|
# User memories — works immediately on a new install
|
|
331
331
|
one mem add note '{"content":"Design review is Thursday"}' --tags work --weight 7
|
|
332
|
+
one mem update <id> '{"status":"done"}' # merges into data; refreshes searchable_text
|
|
332
333
|
one mem search "design review" # hybrid FTS + semantic (if key set)
|
|
333
334
|
one mem list note --limit 20
|
|
334
335
|
|
|
336
|
+
# Identity keys are a first-class column (unique across ACTIVE records; archiving
|
|
337
|
+
# frees them). Manage them after creation with `mem key` — NOT via `mem update`.
|
|
338
|
+
one mem key <id> --add email:x@y.com # also --remove / --set <csv>
|
|
339
|
+
|
|
340
|
+
# Backfill searchable_text for rows that landed NULL or with UUID-noise-heavy text
|
|
341
|
+
# (drops ids/timestamps, leads with name/title/email). No embedding provider needed.
|
|
342
|
+
one mem reindex --searchable --type attio/attioPeople
|
|
343
|
+
|
|
335
344
|
# Listing synced platform rows — type is positional and namespaced as <platform>/<model>;
|
|
336
345
|
# there is NO --platform flag and NO platform column in the schema.
|
|
337
346
|
one mem list "gmail/threads"
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
} from "./chunk-44CV5IMX.js";
|
|
5
5
|
import {
|
|
6
6
|
getCacheTtl
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-SO323PZP.js";
|
|
8
8
|
|
|
9
9
|
// src/lib/flow-runner.ts
|
|
10
10
|
import fs3 from "fs";
|
|
@@ -2319,7 +2319,7 @@ async function executeSubflowStep(step, context, api, permissions, allowedAction
|
|
|
2319
2319
|
if (flowStack.includes(resolvedKey)) {
|
|
2320
2320
|
throw new Error(`Circular flow detected: ${[...flowStack, resolvedKey].join(" \u2192 ")}`);
|
|
2321
2321
|
}
|
|
2322
|
-
const { loadFlowWithMeta: loadFlowWithMeta2 } = await import("./flow-runner-
|
|
2322
|
+
const { loadFlowWithMeta: loadFlowWithMeta2 } = await import("./flow-runner-IBB42ED2.js");
|
|
2323
2323
|
const { flow: subFlow, rootDir: subRootDir } = loadFlowWithMeta2(resolvedKey);
|
|
2324
2324
|
const subContext = await executeFlow(
|
|
2325
2325
|
subFlow,
|
|
@@ -3,10 +3,10 @@ import {
|
|
|
3
3
|
isAgentMode,
|
|
4
4
|
json,
|
|
5
5
|
requireMemoryInit
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-CLJFSFFM.js";
|
|
7
7
|
import {
|
|
8
8
|
getBackend
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-7RV7T5PX.js";
|
|
10
10
|
|
|
11
11
|
// src/commands/mem/sql.ts
|
|
12
12
|
async function memSqlCommand(sql) {
|
|
@@ -5,13 +5,13 @@ import {
|
|
|
5
5
|
getMemoryConfig,
|
|
6
6
|
getMemoryConfigOrDefault,
|
|
7
7
|
updateMemoryConfig
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-C7DWZ7B5.js";
|
|
9
9
|
import {
|
|
10
10
|
getOpenAiApiKey
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-SO323PZP.js";
|
|
12
12
|
|
|
13
13
|
// src/lib/memory/schema.ts
|
|
14
|
-
var SCHEMA_VERSION = "2.
|
|
14
|
+
var SCHEMA_VERSION = "2.2.0";
|
|
15
15
|
var EXTENSIONS_SQL = ``;
|
|
16
16
|
var VECTOR_EXTENSION_SQL = `
|
|
17
17
|
CREATE EXTENSION IF NOT EXISTS vector;
|
|
@@ -105,19 +105,31 @@ BEGIN
|
|
|
105
105
|
END $$;
|
|
106
106
|
`;
|
|
107
107
|
var FUNCTIONS_SQL = `
|
|
108
|
-
-- Enforce
|
|
108
|
+
-- Enforce uniqueness of the "keys" array across ACTIVE records only.
|
|
109
|
+
--
|
|
110
|
+
-- Scoping to active is deliberate: an archived record must not squat on a
|
|
111
|
+
-- key forever. If it did, re-adding the same identity (e.g. a person who
|
|
112
|
+
-- was archived and re-surfaced) would hard-fail with 23505 and the key
|
|
113
|
+
-- could never be reclaimed. So:
|
|
114
|
+
-- - Only ACTIVE incoming rows are checked (an archived NEW row can hold
|
|
115
|
+
-- any keys, including ones a live record also holds \u2014 provenance).
|
|
116
|
+
-- - Conflicts are only raised against other ACTIVE records.
|
|
117
|
+
-- Array-overlap uniqueness can't be expressed as a partial UNIQUE index
|
|
118
|
+
-- (btree can't index "arrays overlap"), so the invariant lives here in a
|
|
119
|
+
-- trigger. find-by-source, key lookups, and mem_upsert_by_keys all
|
|
120
|
+
-- prefer active rows to match this scoping (see below).
|
|
109
121
|
CREATE OR REPLACE FUNCTION mem_enforce_key_uniqueness()
|
|
110
122
|
RETURNS TRIGGER LANGUAGE plpgsql AS $$
|
|
111
123
|
DECLARE
|
|
112
124
|
conflicting_id UUID;
|
|
113
125
|
BEGIN
|
|
114
|
-
IF NEW.keys IS NULL THEN RETURN NEW; END IF;
|
|
126
|
+
IF NEW.keys IS NULL OR NEW.status <> 'active' THEN RETURN NEW; END IF;
|
|
115
127
|
SELECT id INTO conflicting_id
|
|
116
128
|
FROM mem_records
|
|
117
|
-
WHERE keys && NEW.keys AND id != NEW.id
|
|
129
|
+
WHERE keys && NEW.keys AND id != NEW.id AND status = 'active'
|
|
118
130
|
LIMIT 1;
|
|
119
131
|
IF conflicting_id IS NOT NULL THEN
|
|
120
|
-
RAISE EXCEPTION 'Key conflict: one or more keys in % already exist on record %',
|
|
132
|
+
RAISE EXCEPTION 'Key conflict: one or more keys in % already exist on active record %',
|
|
121
133
|
NEW.keys, conflicting_id USING ERRCODE = 'unique_violation';
|
|
122
134
|
END IF;
|
|
123
135
|
RETURN NEW;
|
|
@@ -224,14 +236,15 @@ DECLARE
|
|
|
224
236
|
BEGIN
|
|
225
237
|
-- Deterministic pick when more than one row's keys[] overlap p_keys
|
|
226
238
|
-- (e.g. the incoming row identity-merges with an old Attio-id row AND
|
|
227
|
-
-- a newer email-keyed row from a separate sync).
|
|
228
|
-
--
|
|
229
|
-
--
|
|
230
|
-
--
|
|
239
|
+
-- a newer email-keyed row from a separate sync). Active rows win over
|
|
240
|
+
-- archived (key uniqueness is scoped to active, so an archived dupe may
|
|
241
|
+
-- coexist \u2014 never merge into it when a live owner exists); then newest-
|
|
242
|
+
-- updated; ties broken by id. Without ORDER BY, PG returns whichever
|
|
243
|
+
-- row the planner surfaces first, breaking the keys-are-unique invariant.
|
|
231
244
|
SELECT r.id INTO existing_id
|
|
232
245
|
FROM mem_records r
|
|
233
246
|
WHERE r.keys && p_keys
|
|
234
|
-
ORDER BY r.updated_at DESC NULLS LAST, r.id ASC
|
|
247
|
+
ORDER BY (r.status = 'active') DESC, r.updated_at DESC NULLS LAST, r.id ASC
|
|
235
248
|
LIMIT 1;
|
|
236
249
|
|
|
237
250
|
IF existing_id IS NOT NULL THEN
|
|
@@ -308,12 +321,12 @@ DECLARE
|
|
|
308
321
|
BEGIN
|
|
309
322
|
v_embedding := CASE WHEN p_embedding IS NOT NULL THEN p_embedding::vector ELSE NULL END;
|
|
310
323
|
|
|
311
|
-
-- See no-vector variant for ORDER BY rationale (
|
|
312
|
-
-- when multiple rows' keys overlap p_keys).
|
|
324
|
+
-- See no-vector variant for ORDER BY rationale (active-preferred,
|
|
325
|
+
-- deterministic pick when multiple rows' keys overlap p_keys).
|
|
313
326
|
SELECT r.id INTO existing_id
|
|
314
327
|
FROM mem_records r
|
|
315
328
|
WHERE r.keys && p_keys
|
|
316
|
-
ORDER BY r.updated_at DESC NULLS LAST, r.id ASC
|
|
329
|
+
ORDER BY (r.status = 'active') DESC, r.updated_at DESC NULLS LAST, r.id ASC
|
|
317
330
|
LIMIT 1;
|
|
318
331
|
|
|
319
332
|
IF existing_id IS NOT NULL THEN
|
|
@@ -478,6 +491,13 @@ function hotColumnIndexName(type, jsonPath) {
|
|
|
478
491
|
|
|
479
492
|
// src/lib/memory/plugins/postgres-core/backend.ts
|
|
480
493
|
var SCHEMA_LOCK_ID = 7193095520723763200n.toString();
|
|
494
|
+
function isUniqueViolation(err) {
|
|
495
|
+
if (!err || typeof err !== "object") return false;
|
|
496
|
+
const e = err;
|
|
497
|
+
if (e.code === "23505") return true;
|
|
498
|
+
const msg = typeof e.message === "string" ? e.message : "";
|
|
499
|
+
return /23505|unique_violation|Key conflict/i.test(msg);
|
|
500
|
+
}
|
|
481
501
|
function toRecord(row) {
|
|
482
502
|
return {
|
|
483
503
|
id: row.id,
|
|
@@ -512,6 +532,10 @@ var CoreBackend = class {
|
|
|
512
532
|
}
|
|
513
533
|
async ensureSchema() {
|
|
514
534
|
const caps = this.caps;
|
|
535
|
+
try {
|
|
536
|
+
if (await this.getSchemaVersion() === SCHEMA_VERSION) return;
|
|
537
|
+
} catch {
|
|
538
|
+
}
|
|
515
539
|
await this.client.transaction(async (tx) => {
|
|
516
540
|
try {
|
|
517
541
|
await tx.query(`SELECT pg_advisory_xact_lock(${SCHEMA_LOCK_ID}::bigint)`);
|
|
@@ -623,6 +647,9 @@ var CoreBackend = class {
|
|
|
623
647
|
const searchable = patch.searchable_text ?? existing.searchable_text ?? null;
|
|
624
648
|
const hash = patch.content_hash ?? existing.content_hash ?? null;
|
|
625
649
|
const weight = patch.weight ?? existing.weight;
|
|
650
|
+
const textChanged = searchable !== (existing.searchable_text ?? null);
|
|
651
|
+
const clearEmbedding = textChanged && this.caps.vectorSearch;
|
|
652
|
+
const embeddingReset = clearEmbedding ? `, embedding = NULL, embedded_at = NULL, embedding_model = NULL` : "";
|
|
626
653
|
const res = await this.client.query(
|
|
627
654
|
`UPDATE mem_records
|
|
628
655
|
SET data = $2::jsonb,
|
|
@@ -631,13 +658,46 @@ var CoreBackend = class {
|
|
|
631
658
|
sources = $5::jsonb,
|
|
632
659
|
searchable_text = $6,
|
|
633
660
|
content_hash = $7,
|
|
634
|
-
weight = $8
|
|
661
|
+
weight = $8${embeddingReset}
|
|
635
662
|
WHERE id = $1
|
|
636
663
|
RETURNING *`,
|
|
637
664
|
[id, JSON.stringify(data), tags, keys, JSON.stringify(sources), searchable, hash, weight]
|
|
638
665
|
);
|
|
639
666
|
return res.rows[0] ? toRecord(res.rows[0]) : null;
|
|
640
667
|
}
|
|
668
|
+
async updateKeys(id, keys) {
|
|
669
|
+
try {
|
|
670
|
+
return await this.client.transaction(async (tx) => {
|
|
671
|
+
const exists = await tx.query(
|
|
672
|
+
`SELECT id FROM mem_records WHERE id = $1`,
|
|
673
|
+
[id]
|
|
674
|
+
);
|
|
675
|
+
if (!exists.rows[0]) return { status: "not_found" };
|
|
676
|
+
const conflict = await tx.query(
|
|
677
|
+
`SELECT r.id, r.type,
|
|
678
|
+
(SELECT k FROM unnest($2::text[]) AS k WHERE k = ANY(r.keys) LIMIT 1) AS key
|
|
679
|
+
FROM mem_records r
|
|
680
|
+
WHERE r.keys && $2::text[] AND r.id != $1 AND r.status = 'active'
|
|
681
|
+
LIMIT 1`,
|
|
682
|
+
[id, keys]
|
|
683
|
+
);
|
|
684
|
+
const c = conflict.rows[0];
|
|
685
|
+
if (c) {
|
|
686
|
+
return { status: "conflict", key: c.key, recordId: c.id, recordType: c.type };
|
|
687
|
+
}
|
|
688
|
+
const res = await tx.query(
|
|
689
|
+
`UPDATE mem_records SET keys = $2::text[] WHERE id = $1 RETURNING *`,
|
|
690
|
+
[id, keys.length > 0 ? keys : null]
|
|
691
|
+
);
|
|
692
|
+
return { status: "ok", record: toRecord(res.rows[0]) };
|
|
693
|
+
});
|
|
694
|
+
} catch (err) {
|
|
695
|
+
if (isUniqueViolation(err)) {
|
|
696
|
+
return { status: "conflict", key: keys[0] ?? "", recordId: "unknown", recordType: "unknown" };
|
|
697
|
+
}
|
|
698
|
+
throw err;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
641
701
|
async remove(id) {
|
|
642
702
|
const res = await this.client.query(`DELETE FROM mem_records WHERE id = $1`, [id]);
|
|
643
703
|
return (res.rowCount ?? 0) > 0;
|
|
@@ -651,12 +711,30 @@ var CoreBackend = class {
|
|
|
651
711
|
return (res.rowCount ?? 0) > 0;
|
|
652
712
|
}
|
|
653
713
|
async unarchive(id) {
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
714
|
+
try {
|
|
715
|
+
const res = await this.client.query(
|
|
716
|
+
`UPDATE mem_records SET status = 'active', archived_reason = NULL
|
|
717
|
+
WHERE id = $1 AND status = 'archived'`,
|
|
718
|
+
[id]
|
|
719
|
+
);
|
|
720
|
+
return (res.rowCount ?? 0) > 0;
|
|
721
|
+
} catch (err) {
|
|
722
|
+
if (!isUniqueViolation(err)) throw err;
|
|
723
|
+
const owner = await this.client.query(
|
|
724
|
+
`SELECT r.id, r.type,
|
|
725
|
+
(SELECT k FROM unnest(a.keys) AS k
|
|
726
|
+
WHERE k = ANY(r.keys) LIMIT 1) AS key
|
|
727
|
+
FROM mem_records a
|
|
728
|
+
JOIN mem_records r
|
|
729
|
+
ON r.keys && a.keys AND r.id != a.id AND r.status = 'active'
|
|
730
|
+
WHERE a.id = $1
|
|
731
|
+
LIMIT 1`,
|
|
732
|
+
[id]
|
|
733
|
+
);
|
|
734
|
+
const o = owner.rows[0];
|
|
735
|
+
const detail = o ? `key "${o.key}" was reclaimed by active record ${o.id} (type ${o.type}) \u2014 re-key it or archive that record first` : `one of its keys was reclaimed by another active record`;
|
|
736
|
+
throw new Error(`Cannot unarchive ${id}: ${detail}`);
|
|
737
|
+
}
|
|
660
738
|
}
|
|
661
739
|
async list(type, opts = {}) {
|
|
662
740
|
const limit = opts.limit ?? 100;
|
|
@@ -733,6 +811,48 @@ var CoreBackend = class {
|
|
|
733
811
|
[id, literal, model]
|
|
734
812
|
);
|
|
735
813
|
}
|
|
814
|
+
async listForSearchableBackfill(opts = {}) {
|
|
815
|
+
const limit = opts.limit ?? 1e3;
|
|
816
|
+
const offset = opts.offset ?? 0;
|
|
817
|
+
const params = [];
|
|
818
|
+
const clauses = [`status = 'active'`];
|
|
819
|
+
if (opts.type) {
|
|
820
|
+
params.push(opts.type);
|
|
821
|
+
clauses.push(`type = $${params.length}`);
|
|
822
|
+
}
|
|
823
|
+
if (opts.onlyNull) {
|
|
824
|
+
clauses.push(`(searchable_text IS NULL OR searchable_text = '')`);
|
|
825
|
+
}
|
|
826
|
+
params.push(limit);
|
|
827
|
+
params.push(offset);
|
|
828
|
+
const res = await this.client.query(
|
|
829
|
+
`SELECT id, type, data, searchable_text
|
|
830
|
+
FROM mem_records
|
|
831
|
+
WHERE ${clauses.join(" AND ")}
|
|
832
|
+
ORDER BY id ASC
|
|
833
|
+
LIMIT $${params.length - 1} OFFSET $${params.length}`,
|
|
834
|
+
params
|
|
835
|
+
);
|
|
836
|
+
return res.rows;
|
|
837
|
+
}
|
|
838
|
+
async updateSearchableText(id, text) {
|
|
839
|
+
if (this.caps.vectorSearch) {
|
|
840
|
+
await this.client.query(
|
|
841
|
+
`UPDATE mem_records
|
|
842
|
+
SET searchable_text = $2,
|
|
843
|
+
embedding = NULL,
|
|
844
|
+
embedded_at = NULL,
|
|
845
|
+
embedding_model = NULL
|
|
846
|
+
WHERE id = $1`,
|
|
847
|
+
[id, text]
|
|
848
|
+
);
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
await this.client.query(
|
|
852
|
+
`UPDATE mem_records SET searchable_text = $2 WHERE id = $1`,
|
|
853
|
+
[id, text]
|
|
854
|
+
);
|
|
855
|
+
}
|
|
736
856
|
async listKeysByType(type) {
|
|
737
857
|
const res = await this.client.query(
|
|
738
858
|
`SELECT id, keys FROM mem_records WHERE type = $1 AND status = 'active'`,
|
|
@@ -871,7 +991,10 @@ var CoreBackend = class {
|
|
|
871
991
|
}
|
|
872
992
|
async findBySource(sourceKey) {
|
|
873
993
|
const res = await this.client.query(
|
|
874
|
-
`SELECT * FROM mem_records
|
|
994
|
+
`SELECT * FROM mem_records
|
|
995
|
+
WHERE $1 = ANY(keys)
|
|
996
|
+
ORDER BY (status = 'active') DESC, updated_at DESC
|
|
997
|
+
LIMIT 1`,
|
|
875
998
|
[sourceKey]
|
|
876
999
|
);
|
|
877
1000
|
return res.rows[0] ? toRecord(res.rows[0]) : null;
|
|
@@ -1105,6 +1228,9 @@ var LazyPostgresBackend = class {
|
|
|
1105
1228
|
async update(...a) {
|
|
1106
1229
|
return (await this.ensure()).update(...a);
|
|
1107
1230
|
}
|
|
1231
|
+
async updateKeys(...a) {
|
|
1232
|
+
return (await this.ensure()).updateKeys(...a);
|
|
1233
|
+
}
|
|
1108
1234
|
async remove(...a) {
|
|
1109
1235
|
return (await this.ensure()).remove(...a);
|
|
1110
1236
|
}
|
|
@@ -1126,9 +1252,15 @@ var LazyPostgresBackend = class {
|
|
|
1126
1252
|
async listKeysByType(...a) {
|
|
1127
1253
|
return (await this.ensure()).listKeysByType(...a);
|
|
1128
1254
|
}
|
|
1255
|
+
async listForSearchableBackfill(...a) {
|
|
1256
|
+
return (await this.ensure()).listForSearchableBackfill(...a);
|
|
1257
|
+
}
|
|
1129
1258
|
async updateEmbedding(...a) {
|
|
1130
1259
|
return (await this.ensure()).updateEmbedding(...a);
|
|
1131
1260
|
}
|
|
1261
|
+
async updateSearchableText(...a) {
|
|
1262
|
+
return (await this.ensure()).updateSearchableText(...a);
|
|
1263
|
+
}
|
|
1132
1264
|
async raw(sql, params) {
|
|
1133
1265
|
const b = await this.ensure();
|
|
1134
1266
|
if (!b.raw) throw new Error("Backend does not support raw SQL");
|
|
@@ -1451,6 +1583,9 @@ var LazyEmbeddedPostgresBackend = class {
|
|
|
1451
1583
|
async update(...a) {
|
|
1452
1584
|
return (await this.ensure()).update(...a);
|
|
1453
1585
|
}
|
|
1586
|
+
async updateKeys(...a) {
|
|
1587
|
+
return (await this.ensure()).updateKeys(...a);
|
|
1588
|
+
}
|
|
1454
1589
|
async remove(...a) {
|
|
1455
1590
|
return (await this.ensure()).remove(...a);
|
|
1456
1591
|
}
|
|
@@ -1472,9 +1607,15 @@ var LazyEmbeddedPostgresBackend = class {
|
|
|
1472
1607
|
async listKeysByType(...a) {
|
|
1473
1608
|
return (await this.ensure()).listKeysByType(...a);
|
|
1474
1609
|
}
|
|
1610
|
+
async listForSearchableBackfill(...a) {
|
|
1611
|
+
return (await this.ensure()).listForSearchableBackfill(...a);
|
|
1612
|
+
}
|
|
1475
1613
|
async updateEmbedding(...a) {
|
|
1476
1614
|
return (await this.ensure()).updateEmbedding(...a);
|
|
1477
1615
|
}
|
|
1616
|
+
async updateSearchableText(...a) {
|
|
1617
|
+
return (await this.ensure()).updateSearchableText(...a);
|
|
1618
|
+
}
|
|
1478
1619
|
async raw(sql, params) {
|
|
1479
1620
|
const b = await this.ensure();
|
|
1480
1621
|
if (!b.raw) throw new Error("Backend does not support raw SQL");
|
|
@@ -1671,6 +1812,21 @@ async function addRecord(input, opts = {}) {
|
|
|
1671
1812
|
};
|
|
1672
1813
|
return backend.insert(prepared);
|
|
1673
1814
|
}
|
|
1815
|
+
async function updateRecord(id, patch) {
|
|
1816
|
+
const backend = await getBackend();
|
|
1817
|
+
const existing = await backend.getById(id);
|
|
1818
|
+
if (!existing) return null;
|
|
1819
|
+
const mergedData = patch.data ? { ...existing.data, ...patch.data } : existing.data;
|
|
1820
|
+
const dataChanged = patch.data !== void 0;
|
|
1821
|
+
const searchable_text = patch.searchable_text ?? (dataChanged ? defaultSearchableText(mergedData) : existing.searchable_text ?? void 0);
|
|
1822
|
+
const content_hash = patch.content_hash ?? (dataChanged ? contentHash(mergedData) : existing.content_hash ?? void 0);
|
|
1823
|
+
return backend.update(id, {
|
|
1824
|
+
...patch,
|
|
1825
|
+
data: mergedData,
|
|
1826
|
+
searchable_text,
|
|
1827
|
+
content_hash
|
|
1828
|
+
});
|
|
1829
|
+
}
|
|
1674
1830
|
async function upsertRecord(input, opts = {}) {
|
|
1675
1831
|
const backend = await getBackend();
|
|
1676
1832
|
const { searchable_text, content_hash, embedding, embedding_model } = await prepareRecord(input, opts, "sync");
|
|
@@ -1706,5 +1862,6 @@ export {
|
|
|
1706
1862
|
resetBackendSingleton,
|
|
1707
1863
|
closeBackendIfCached,
|
|
1708
1864
|
addRecord,
|
|
1865
|
+
updateRecord,
|
|
1709
1866
|
upsertRecord
|
|
1710
1867
|
};
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
readConfig,
|
|
4
4
|
setOpenAiApiKey,
|
|
5
5
|
writeConfig
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-SO323PZP.js";
|
|
7
7
|
|
|
8
8
|
// src/lib/memory/config.ts
|
|
9
9
|
var DEFAULT_MEMORY_CONFIG = {
|
|
@@ -172,29 +172,78 @@ async function embedBatch(texts, opts = {}) {
|
|
|
172
172
|
function sleep(ms) {
|
|
173
173
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
174
174
|
}
|
|
175
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
176
|
+
var TIMESTAMP_RE = /^\d{4}-\d{2}-\d{2}([T ]\d{2}:\d{2}(:\d{2})?(\.\d+)?(Z|[+-]\d{2}:?\d{2})?)?$/;
|
|
177
|
+
var NOISE_KEY_RE = /(^|_)(id|ids|uuid|guid|url|href|link|avatar|photo|icon|hash|token|slug)$/i;
|
|
178
|
+
var NOISE_KEY_EXACT = /* @__PURE__ */ new Set([
|
|
179
|
+
"attribute_type",
|
|
180
|
+
"actor_type",
|
|
181
|
+
"created_by_actor",
|
|
182
|
+
"active_from",
|
|
183
|
+
"active_until",
|
|
184
|
+
"created_at",
|
|
185
|
+
"updated_at",
|
|
186
|
+
"last_synced_at"
|
|
187
|
+
]);
|
|
188
|
+
var CONTAINER_KEYS = /* @__PURE__ */ new Set([
|
|
189
|
+
"values",
|
|
190
|
+
"value",
|
|
191
|
+
"data",
|
|
192
|
+
"attributes",
|
|
193
|
+
"properties",
|
|
194
|
+
"items",
|
|
195
|
+
"records",
|
|
196
|
+
"result",
|
|
197
|
+
"results",
|
|
198
|
+
"fields"
|
|
199
|
+
]);
|
|
200
|
+
var PRIORITY_KEY_RE = /(^|_)(name|full_name|first_name|last_name|display_name|title|job_title|role|position|email|email_address|company|organization|org|description|bio|summary|headline|label|subject|content|body|text_content)$/i;
|
|
175
201
|
function defaultSearchableText(data, maxLen = 4e3) {
|
|
176
|
-
const
|
|
177
|
-
const
|
|
202
|
+
const priority = [];
|
|
203
|
+
const normal = [];
|
|
204
|
+
const push = (key, text) => {
|
|
205
|
+
if (key && PRIORITY_KEY_RE.test(key)) priority.push(text);
|
|
206
|
+
else normal.push(text);
|
|
207
|
+
};
|
|
208
|
+
const walk = (value, key, depth) => {
|
|
178
209
|
if (value === null || value === void 0) return;
|
|
179
|
-
if (typeof value === "string"
|
|
180
|
-
|
|
210
|
+
if (typeof value === "string") {
|
|
211
|
+
const t = value.trim();
|
|
212
|
+
if (!t) return;
|
|
213
|
+
if (UUID_RE.test(t) || TIMESTAMP_RE.test(t)) return;
|
|
214
|
+
if (key && NOISE_KEY_RE.test(key)) return;
|
|
215
|
+
push(key, t);
|
|
181
216
|
return;
|
|
182
217
|
}
|
|
183
218
|
if (typeof value === "number" || typeof value === "boolean") {
|
|
184
|
-
|
|
219
|
+
if (key && NOISE_KEY_RE.test(key)) return;
|
|
220
|
+
push(key, String(value));
|
|
185
221
|
return;
|
|
186
222
|
}
|
|
187
|
-
if (depth >
|
|
223
|
+
if (depth > 6) return;
|
|
188
224
|
if (Array.isArray(value)) {
|
|
189
|
-
for (const v of value) walk(v, depth + 1);
|
|
225
|
+
for (const v of value) walk(v, key, depth + 1);
|
|
190
226
|
return;
|
|
191
227
|
}
|
|
192
228
|
if (typeof value === "object") {
|
|
193
|
-
for (const v of Object.
|
|
229
|
+
for (const [k, v] of Object.entries(value)) {
|
|
230
|
+
const lower = k.toLowerCase();
|
|
231
|
+
if (NOISE_KEY_EXACT.has(lower) || NOISE_KEY_RE.test(k)) continue;
|
|
232
|
+
const nextKey = CONTAINER_KEYS.has(lower) ? key : k;
|
|
233
|
+
walk(v, nextKey, depth + 1);
|
|
234
|
+
}
|
|
194
235
|
}
|
|
195
236
|
};
|
|
196
|
-
walk(data);
|
|
197
|
-
const
|
|
237
|
+
walk(data, void 0, 0);
|
|
238
|
+
const seen = /* @__PURE__ */ new Set();
|
|
239
|
+
const ordered = [];
|
|
240
|
+
for (const t of [...priority, ...normal]) {
|
|
241
|
+
const norm = t.toLowerCase();
|
|
242
|
+
if (seen.has(norm)) continue;
|
|
243
|
+
seen.add(norm);
|
|
244
|
+
ordered.push(t);
|
|
245
|
+
}
|
|
246
|
+
const joined = ordered.join(" ").replace(/\s+/g, " ").trim();
|
|
198
247
|
return joined.length > maxLen ? joined.slice(0, maxLen) : joined;
|
|
199
248
|
}
|
|
200
249
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getMemoryConfigOrDefault
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-C7DWZ7B5.js";
|
|
4
4
|
import {
|
|
5
5
|
getOpenAiApiKey,
|
|
6
6
|
readConfig
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-SO323PZP.js";
|
|
8
8
|
|
|
9
9
|
// src/lib/output.ts
|
|
10
10
|
import * as p from "@clack/prompts";
|
|
@@ -6,11 +6,11 @@ import {
|
|
|
6
6
|
note,
|
|
7
7
|
okJson,
|
|
8
8
|
requireMemoryInit
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-CLJFSFFM.js";
|
|
10
10
|
import {
|
|
11
11
|
getBackend,
|
|
12
12
|
upsertRecord
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-7RV7T5PX.js";
|
|
14
14
|
|
|
15
15
|
// src/commands/mem/migrate.ts
|
|
16
16
|
import fs4 from "fs";
|
|
@@ -318,13 +318,6 @@ function appendUsageLog(line) {
|
|
|
318
318
|
} catch {
|
|
319
319
|
}
|
|
320
320
|
}
|
|
321
|
-
function readUsageLog() {
|
|
322
|
-
try {
|
|
323
|
-
return fs.readFileSync(usageLogFile(), "utf-8").split("\n").filter(Boolean).slice(-USAGE_LOG_MAX);
|
|
324
|
-
} catch {
|
|
325
|
-
return [];
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
321
|
function writeUsageLog(lines) {
|
|
329
322
|
try {
|
|
330
323
|
if (lines.length === 0) {
|
|
@@ -337,6 +330,25 @@ function writeUsageLog(lines) {
|
|
|
337
330
|
} catch {
|
|
338
331
|
}
|
|
339
332
|
}
|
|
333
|
+
function claimUsageLog() {
|
|
334
|
+
const src = usageLogFile();
|
|
335
|
+
const tmp = `${src}.claim.${process.pid}.${randomUUID()}`;
|
|
336
|
+
try {
|
|
337
|
+
fs.renameSync(src, tmp);
|
|
338
|
+
} catch {
|
|
339
|
+
return null;
|
|
340
|
+
}
|
|
341
|
+
try {
|
|
342
|
+
return fs.readFileSync(tmp, "utf-8").split("\n").filter(Boolean).slice(-USAGE_LOG_MAX);
|
|
343
|
+
} catch {
|
|
344
|
+
return [];
|
|
345
|
+
} finally {
|
|
346
|
+
try {
|
|
347
|
+
fs.rmSync(tmp, { force: true });
|
|
348
|
+
} catch {
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
340
352
|
function readUsageState() {
|
|
341
353
|
try {
|
|
342
354
|
return JSON.parse(fs.readFileSync(usageStateFile(), "utf-8"));
|
|
@@ -384,8 +396,8 @@ export {
|
|
|
384
396
|
readAnalyticsQueue,
|
|
385
397
|
writeAnalyticsQueue,
|
|
386
398
|
appendUsageLog,
|
|
387
|
-
readUsageLog,
|
|
388
399
|
writeUsageLog,
|
|
400
|
+
claimUsageLog,
|
|
389
401
|
readUsageState,
|
|
390
402
|
writeUsageState
|
|
391
403
|
};
|
|
@@ -12,9 +12,9 @@ import {
|
|
|
12
12
|
stripStepsAlias,
|
|
13
13
|
summarizeFlowInputs,
|
|
14
14
|
walkSteps
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-2TWFL3CS.js";
|
|
16
16
|
import "./chunk-44CV5IMX.js";
|
|
17
|
-
import "./chunk-
|
|
17
|
+
import "./chunk-SO323PZP.js";
|
|
18
18
|
export {
|
|
19
19
|
FlowRunner,
|
|
20
20
|
collectStepTypes,
|
package/dist/index.js
CHANGED
|
@@ -31,10 +31,10 @@ import {
|
|
|
31
31
|
validateActionInput,
|
|
32
32
|
walkSteps,
|
|
33
33
|
writeCache
|
|
34
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-2TWFL3CS.js";
|
|
35
35
|
import {
|
|
36
36
|
memSqlCommand
|
|
37
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-4WWK3GO5.js";
|
|
38
38
|
import {
|
|
39
39
|
countRecords,
|
|
40
40
|
deleteDatabase,
|
|
@@ -59,7 +59,7 @@ import {
|
|
|
59
59
|
upsertRecords,
|
|
60
60
|
writeDraftProfile,
|
|
61
61
|
writeProfile
|
|
62
|
-
} from "./chunk-
|
|
62
|
+
} from "./chunk-GXOIR3GF.js";
|
|
63
63
|
import {
|
|
64
64
|
getByDotPath
|
|
65
65
|
} from "./chunk-44CV5IMX.js";
|
|
@@ -83,7 +83,7 @@ import {
|
|
|
83
83
|
semanticSearchUpgradeLine,
|
|
84
84
|
setAgentMode,
|
|
85
85
|
silenceWarningsInAgentMode
|
|
86
|
-
} from "./chunk-
|
|
86
|
+
} from "./chunk-CLJFSFFM.js";
|
|
87
87
|
import {
|
|
88
88
|
SCHEMA_VERSION,
|
|
89
89
|
addRecord,
|
|
@@ -92,8 +92,9 @@ import {
|
|
|
92
92
|
getBackendPlugin,
|
|
93
93
|
listBackendPlugins,
|
|
94
94
|
loadBackendFromConfig,
|
|
95
|
+
updateRecord,
|
|
95
96
|
upsertRecord
|
|
96
|
-
} from "./chunk-
|
|
97
|
+
} from "./chunk-7RV7T5PX.js";
|
|
97
98
|
import {
|
|
98
99
|
DEFAULT_MEMORY_CONFIG,
|
|
99
100
|
defaultSearchableText,
|
|
@@ -103,10 +104,11 @@ import {
|
|
|
103
104
|
memoryConfigExists,
|
|
104
105
|
setOpenAiApiKey,
|
|
105
106
|
updateMemoryConfig
|
|
106
|
-
} from "./chunk-
|
|
107
|
+
} from "./chunk-C7DWZ7B5.js";
|
|
107
108
|
import {
|
|
108
109
|
appendAnalyticsQueue,
|
|
109
110
|
appendUsageLog,
|
|
111
|
+
claimUsageLog,
|
|
110
112
|
configExists,
|
|
111
113
|
ensureWhoAmI,
|
|
112
114
|
getAccessControl,
|
|
@@ -127,7 +129,6 @@ import {
|
|
|
127
129
|
readConfig,
|
|
128
130
|
readGlobalConfig,
|
|
129
131
|
readProjectConfig,
|
|
130
|
-
readUsageLog,
|
|
131
132
|
readUsageState,
|
|
132
133
|
resolveConfig,
|
|
133
134
|
telemetryNoticeShown,
|
|
@@ -138,7 +139,7 @@ import {
|
|
|
138
139
|
writeConfig,
|
|
139
140
|
writeUsageLog,
|
|
140
141
|
writeUsageState
|
|
141
|
-
} from "./chunk-
|
|
142
|
+
} from "./chunk-SO323PZP.js";
|
|
142
143
|
|
|
143
144
|
// src/cli.ts
|
|
144
145
|
import { createRequire as createRequire3 } from "module";
|
|
@@ -6062,7 +6063,7 @@ async function syncModel(api, profile, options) {
|
|
|
6062
6063
|
updateModelState(platform, model, { status: "failed", pagesProcessed, lastCursor }),
|
|
6063
6064
|
(async () => {
|
|
6064
6065
|
try {
|
|
6065
|
-
const { getBackend: getBackend2 } = await import("./runtime-
|
|
6066
|
+
const { getBackend: getBackend2 } = await import("./runtime-2PRVL2NO.js");
|
|
6066
6067
|
const backend = await getBackend2();
|
|
6067
6068
|
await Promise.race([
|
|
6068
6069
|
backend.close(),
|
|
@@ -6417,7 +6418,7 @@ async function syncModel(api, profile, options) {
|
|
|
6417
6418
|
db.exec(`DROP TABLE IF EXISTS _seen_ids`);
|
|
6418
6419
|
}
|
|
6419
6420
|
if (options.toMemory !== false) {
|
|
6420
|
-
const backend = await (await import("./runtime-
|
|
6421
|
+
const backend = await (await import("./runtime-2PRVL2NO.js")).getBackend();
|
|
6421
6422
|
const type = `${platform}/${model}`;
|
|
6422
6423
|
const existing = await backend.listKeysByType(type);
|
|
6423
6424
|
const sourcePrefix = `${type}:`;
|
|
@@ -6497,7 +6498,7 @@ async function syncModel(api, profile, options) {
|
|
|
6497
6498
|
let statusCounts;
|
|
6498
6499
|
if (options.toMemory !== false) {
|
|
6499
6500
|
try {
|
|
6500
|
-
const backend = await (await import("./runtime-
|
|
6501
|
+
const backend = await (await import("./runtime-2PRVL2NO.js")).getBackend();
|
|
6501
6502
|
const typeName = `${platform}/${model}`;
|
|
6502
6503
|
const [active, archived] = await Promise.all([
|
|
6503
6504
|
backend.count(typeName, { status: "active" }),
|
|
@@ -8130,7 +8131,7 @@ ${result.total} results`);
|
|
|
8130
8131
|
}
|
|
8131
8132
|
}
|
|
8132
8133
|
async function syncSqlCommand(platformModel, sql) {
|
|
8133
|
-
const { syncSqlCommand: runSyncSql } = await import("./sql-
|
|
8134
|
+
const { syncSqlCommand: runSyncSql } = await import("./sql-HG7R2JML.js");
|
|
8134
8135
|
await runSyncSql(platformModel, sql);
|
|
8135
8136
|
}
|
|
8136
8137
|
async function syncDeleteCommand(platformModel, options) {
|
|
@@ -8208,7 +8209,7 @@ async function syncDeleteCommand(platformModel, options) {
|
|
|
8208
8209
|
async function maybeAutoMigrateLegacy(platform, models) {
|
|
8209
8210
|
const dbSize = getDatabaseSize(platform);
|
|
8210
8211
|
if (!dbSize || dbSize === "0 B") return;
|
|
8211
|
-
const { getBackend: getBackend2 } = await import("./runtime-
|
|
8212
|
+
const { getBackend: getBackend2 } = await import("./runtime-2PRVL2NO.js");
|
|
8212
8213
|
const backend = await getBackend2();
|
|
8213
8214
|
let memoryHasData = false;
|
|
8214
8215
|
for (const model of models) {
|
|
@@ -8224,7 +8225,7 @@ async function maybeAutoMigrateLegacy(platform, models) {
|
|
|
8224
8225
|
` detected legacy .one/sync/data/${platform}.db (${dbSize}) \u2014 auto-migrating into memory before sync.
|
|
8225
8226
|
`
|
|
8226
8227
|
);
|
|
8227
|
-
const { memMigrateCommand: memMigrateCommand3 } = await import("./migrate-
|
|
8228
|
+
const { memMigrateCommand: memMigrateCommand3 } = await import("./migrate-TRUZVAT5.js");
|
|
8228
8229
|
await memMigrateCommand3({ platform, yes: true });
|
|
8229
8230
|
return;
|
|
8230
8231
|
}
|
|
@@ -8233,7 +8234,7 @@ async function maybeAutoMigrateLegacy(platform, models) {
|
|
|
8233
8234
|
initialValue: true
|
|
8234
8235
|
});
|
|
8235
8236
|
if (p7.isCancel(shouldMigrate) || !shouldMigrate) return;
|
|
8236
|
-
const { memMigrateCommand: memMigrateCommand2 } = await import("./migrate-
|
|
8237
|
+
const { memMigrateCommand: memMigrateCommand2 } = await import("./migrate-TRUZVAT5.js");
|
|
8237
8238
|
await memMigrateCommand2({ platform, yes: true });
|
|
8238
8239
|
}
|
|
8239
8240
|
async function syncSuggestSearchableCommand(platformModel, options = {}) {
|
|
@@ -8294,7 +8295,7 @@ async function syncSuggestSearchableCommand(platformModel, options = {}) {
|
|
|
8294
8295
|
async function syncListCommand(platform) {
|
|
8295
8296
|
const profiles = listProfiles(platform);
|
|
8296
8297
|
const state = await readSyncState();
|
|
8297
|
-
const { getBackend: getBackend2 } = await import("./runtime-
|
|
8298
|
+
const { getBackend: getBackend2 } = await import("./runtime-2PRVL2NO.js");
|
|
8298
8299
|
const backend = await getBackend2();
|
|
8299
8300
|
const syncs = await Promise.all(profiles.map(async (p10) => {
|
|
8300
8301
|
const modelState = state[p10.platform]?.[p10.model];
|
|
@@ -8569,7 +8570,7 @@ function registerSyncSubcommands(sync) {
|
|
|
8569
8570
|
await syncSqlCommand(platformModel, sql);
|
|
8570
8571
|
});
|
|
8571
8572
|
sync.command("schema <platform/model>").description("Inspect the JSON structure of synced records (field paths, types, examples) \u2014 useful before writing `sync sql` queries").action(async (platformModel) => {
|
|
8572
|
-
const { syncSchemaCommand } = await import("./schema-
|
|
8573
|
+
const { syncSchemaCommand } = await import("./schema-LCRWSFD5.js");
|
|
8573
8574
|
await syncSchemaCommand(platformModel);
|
|
8574
8575
|
});
|
|
8575
8576
|
sync.command("delete <platform/model>").description('Delete records from local sync data (e.g. one sync delete notion/pages --id "abc-123")').option("--id <value>", "Delete record by ID").option("--where <conditions>", 'Delete records matching conditions (e.g. "status=archived")').option("--where-sql <predicate>", `Delete using a raw SQL WHERE clause (e.g. "json_extract(data, '$.type') = 'promotion'")`).option("--yes", "Skip confirmation prompt").action(async (platformModel, options) => {
|
|
@@ -9015,11 +9016,41 @@ async function memGetCommand(id, flags) {
|
|
|
9015
9016
|
async function memUpdateCommand(id, patchRaw) {
|
|
9016
9017
|
requireMemoryInit();
|
|
9017
9018
|
const patch = parseJsonArg(patchRaw, "patch");
|
|
9018
|
-
|
|
9019
|
-
|
|
9019
|
+
if (patch && typeof patch === "object" && !Array.isArray(patch) && "keys" in patch) {
|
|
9020
|
+
error("`keys` is not a data field. Use `one mem key <id> --set <csv>` (or --add/--remove) to change a record's keys.");
|
|
9021
|
+
}
|
|
9022
|
+
const updated = await updateRecord(id, { data: patch });
|
|
9020
9023
|
if (!updated) error(`Record ${id} not found`);
|
|
9021
9024
|
printRecord(updated);
|
|
9022
9025
|
}
|
|
9026
|
+
async function memKeyCommand(id, flags) {
|
|
9027
|
+
requireMemoryInit();
|
|
9028
|
+
const backend = await getBackend();
|
|
9029
|
+
const setList = parseCsv(flags.set);
|
|
9030
|
+
const addList = parseCsv(flags.add) ?? [];
|
|
9031
|
+
const removeList = parseCsv(flags.remove) ?? [];
|
|
9032
|
+
if (!setList && addList.length === 0 && removeList.length === 0) {
|
|
9033
|
+
error("Pass at least one of --add <csv>, --remove <csv>, or --set <csv>.");
|
|
9034
|
+
}
|
|
9035
|
+
const existing = await backend.getById(id);
|
|
9036
|
+
if (!existing) error(`Record ${id} not found`);
|
|
9037
|
+
let next;
|
|
9038
|
+
if (setList) {
|
|
9039
|
+
next = setList;
|
|
9040
|
+
} else {
|
|
9041
|
+
const removeSet = new Set(removeList);
|
|
9042
|
+
next = [...existing.keys ?? [], ...addList].filter((k) => !removeSet.has(k));
|
|
9043
|
+
}
|
|
9044
|
+
next = [...new Set(next)];
|
|
9045
|
+
const outcome = await backend.updateKeys(id, next);
|
|
9046
|
+
if (outcome.status === "not_found") error(`Record ${id} not found`);
|
|
9047
|
+
if (outcome.status === "conflict") {
|
|
9048
|
+
error(
|
|
9049
|
+
`Key "${outcome.key}" is already owned by active record ${outcome.recordId} (type ${outcome.recordType}). Keys must be unique across active records \u2014 archive or re-key that record first.`
|
|
9050
|
+
);
|
|
9051
|
+
}
|
|
9052
|
+
printRecord(outcome.record);
|
|
9053
|
+
}
|
|
9023
9054
|
async function memArchiveCommand(id, flags) {
|
|
9024
9055
|
requireMemoryInit();
|
|
9025
9056
|
const backend = await getBackend();
|
|
@@ -9216,7 +9247,7 @@ async function memDoctorCommand() {
|
|
|
9216
9247
|
}
|
|
9217
9248
|
if (cfg.embedding.provider === "openai") {
|
|
9218
9249
|
try {
|
|
9219
|
-
const { embed: embed2 } = await import("./embedding-
|
|
9250
|
+
const { embed: embed2 } = await import("./embedding-2YB6CGBA.js");
|
|
9220
9251
|
const result = await embed2("connectivity check");
|
|
9221
9252
|
checks.push({
|
|
9222
9253
|
name: "OpenAI embedding provider reachable",
|
|
@@ -9375,6 +9406,9 @@ async function memVacuumCommand() {
|
|
|
9375
9406
|
}
|
|
9376
9407
|
async function memReindexCommand(flags) {
|
|
9377
9408
|
requireMemoryInit();
|
|
9409
|
+
if (flags.searchable) {
|
|
9410
|
+
return memReindexSearchableCommand(flags);
|
|
9411
|
+
}
|
|
9378
9412
|
const backend = await getBackend();
|
|
9379
9413
|
const cfg = getMemoryConfigOrDefault();
|
|
9380
9414
|
if (cfg.embedding.provider !== "openai") {
|
|
@@ -9437,6 +9471,58 @@ async function memReindexCommand(flags) {
|
|
|
9437
9471
|
force: !!flags.force
|
|
9438
9472
|
});
|
|
9439
9473
|
}
|
|
9474
|
+
async function memReindexSearchableCommand(flags) {
|
|
9475
|
+
const backend = await getBackend();
|
|
9476
|
+
const onlyNull = !flags.all;
|
|
9477
|
+
const totalCap = flags.limit ? parsePositiveInt(flags.limit, 1e6, "limit") : 1e6;
|
|
9478
|
+
const PAGE = 500;
|
|
9479
|
+
let considered = 0;
|
|
9480
|
+
let updated = 0;
|
|
9481
|
+
let unchanged = 0;
|
|
9482
|
+
let emptied = 0;
|
|
9483
|
+
let offset = 0;
|
|
9484
|
+
while (considered < totalCap) {
|
|
9485
|
+
const pageSize = Math.min(PAGE, totalCap - considered);
|
|
9486
|
+
const rows = await backend.listForSearchableBackfill({
|
|
9487
|
+
type: flags.type,
|
|
9488
|
+
onlyNull,
|
|
9489
|
+
limit: pageSize,
|
|
9490
|
+
offset
|
|
9491
|
+
});
|
|
9492
|
+
if (rows.length === 0) break;
|
|
9493
|
+
considered += rows.length;
|
|
9494
|
+
let updatedThisPage = 0;
|
|
9495
|
+
for (const r of rows) {
|
|
9496
|
+
const next = defaultSearchableText(r.data);
|
|
9497
|
+
if (!next) {
|
|
9498
|
+
emptied++;
|
|
9499
|
+
continue;
|
|
9500
|
+
}
|
|
9501
|
+
if (next === (r.searchable_text ?? "")) {
|
|
9502
|
+
unchanged++;
|
|
9503
|
+
continue;
|
|
9504
|
+
}
|
|
9505
|
+
await backend.updateSearchableText(r.id, next);
|
|
9506
|
+
updated++;
|
|
9507
|
+
updatedThisPage++;
|
|
9508
|
+
}
|
|
9509
|
+
offset += onlyNull ? rows.length - updatedThisPage : rows.length;
|
|
9510
|
+
if (!isAgentMode()) {
|
|
9511
|
+
process.stderr.write(` searchable: updated ${updated} / considered ${considered}\r`);
|
|
9512
|
+
}
|
|
9513
|
+
}
|
|
9514
|
+
if (!isAgentMode()) process.stderr.write("\n");
|
|
9515
|
+
okJson({
|
|
9516
|
+
status: "ok",
|
|
9517
|
+
mode: "searchable",
|
|
9518
|
+
type: flags.type ?? null,
|
|
9519
|
+
scope: onlyNull ? "null_only" : "all",
|
|
9520
|
+
considered,
|
|
9521
|
+
updated,
|
|
9522
|
+
unchanged,
|
|
9523
|
+
emptied
|
|
9524
|
+
});
|
|
9525
|
+
}
|
|
9440
9526
|
|
|
9441
9527
|
// src/commands/mem.ts
|
|
9442
9528
|
function memStatusCommand() {
|
|
@@ -9475,11 +9561,12 @@ function registerMemoryCommands(program2) {
|
|
|
9475
9561
|
);
|
|
9476
9562
|
mem.command("doctor").description("Diagnose schema, indexes, plugin resolution, connectivity, embeddings").action(memDoctorCommand);
|
|
9477
9563
|
mem.command("vacuum").description("Run backend maintenance (VACUUM ANALYZE on tables)").action(memVacuumCommand);
|
|
9478
|
-
mem.command("reindex").description("Backfill embeddings
|
|
9564
|
+
mem.command("reindex").description("Backfill embeddings (default) or searchable_text (--searchable)").option("--type <type>", "Restrict to one record type, e.g. attio/attioPeople").option("--model <name>", "Override the embedding model").option("--force", "Re-embed even rows that already have a matching embedding", false).option("--batch <n>", "OpenAI calls per batch (default 50)", "50").option("--limit <n>", "Safety cap on total records to scan (default 100000)").option("--searchable", "Regenerate searchable_text (NULL or stale vs. data) instead of embeddings", false).option("--all", "With --searchable: also rewrite rows whose text is stale, not just NULL", false).action(memReindexCommand);
|
|
9479
9565
|
mem.command("sql <query>").description("Run a read-only SELECT / WITH / EXPLAIN against the memory store").action(memSqlCommand);
|
|
9480
9566
|
mem.command("add <type> <data>").description("Add a new memory record (data is JSON)").option("--tags <csv>", "Comma-separated tags").option("--keys <csv>", "Comma-separated keys (prefixed, e.g. email:x@y.com)").option("--weight <n>", "Importance 1-10 (default 5)").option("--embed", "Force embedding for this record").option("--no-embed", "Skip embedding for this record").action(memAddCommand);
|
|
9481
9567
|
mem.command("get <id>").description("Get a record by id").option("--links", "Include outgoing and incoming links", false).action(memGetCommand);
|
|
9482
|
-
mem.command("update <id> <patch>").description("Update a record (patch is JSON merged into data)").action(memUpdateCommand);
|
|
9568
|
+
mem.command("update <id> <patch>").description("Update a record (patch is JSON merged into data; regenerates searchable_text)").action(memUpdateCommand);
|
|
9569
|
+
mem.command("key <id>").description("Manage a record's identity keys (unique across active records)").option("--add <csv>", "Keys to add (comma-separated, e.g. email:x@y.com)").option("--remove <csv>", "Keys to remove (comma-separated)").option("--set <csv>", "Replace all keys with this comma-separated list").action((id, flags) => memKeyCommand(id, flags));
|
|
9483
9570
|
mem.command("archive <id>").description("Archive a record").option("--reason <text>", "Why it was archived (user_archived | deleted_upstream | superseded | \u2026)").action(memArchiveCommand);
|
|
9484
9571
|
mem.command("weight <id> <n>").description("Set record relevance weight (1-10)").action(memWeightCommand);
|
|
9485
9572
|
mem.command("flush <id>").description("Reset access count for a record").action(memFlushCommand);
|
|
@@ -10122,10 +10209,19 @@ one --agent mem add note '{"content":"..."}' --tags work,urgent --weight 8 --key
|
|
|
10122
10209
|
# Get (optionally with links)
|
|
10123
10210
|
one --agent mem get <id> --links
|
|
10124
10211
|
|
|
10125
|
-
# Update (shallow merge into data)
|
|
10212
|
+
# Update (shallow merge into data; regenerates searchable_text from the merged data)
|
|
10126
10213
|
one --agent mem update <id> '{"status":"done"}'
|
|
10127
10214
|
|
|
10128
|
-
#
|
|
10215
|
+
# Manage identity keys AFTER creation (keys are a first-class column, NOT data).
|
|
10216
|
+
# Unique across ACTIVE records \u2014 a clear error names the record that owns a taken key.
|
|
10217
|
+
one --agent mem key <id> --add email:new@x.com
|
|
10218
|
+
one --agent mem key <id> --remove email:old@x.com
|
|
10219
|
+
one --agent mem key <id> --set 'email:a@x.com,phone:+1555' # replace all keys
|
|
10220
|
+
# NOTE: passing keys to 'mem update' is rejected \u2014 keys are not a data field.
|
|
10221
|
+
|
|
10222
|
+
# Archive / unarchive. Archiving FREES a record's keys: an archived record no
|
|
10223
|
+
# longer blocks a new active record from reusing the same key (uniqueness is
|
|
10224
|
+
# scoped to active records).
|
|
10129
10225
|
one --agent mem archive <id> --reason superseded
|
|
10130
10226
|
|
|
10131
10227
|
# List by type \u2014 type is positional. Synced rows are namespaced as <platform>/<model>.
|
|
@@ -10191,8 +10287,12 @@ one --agent mem status # backend, provider, _upgrade hint
|
|
|
10191
10287
|
one --agent mem doctor # 7-check health report
|
|
10192
10288
|
one --agent mem vacuum # backend maintenance (VACUUM ANALYZE)
|
|
10193
10289
|
one --agent mem reindex # re-embed records under current model
|
|
10290
|
+
one --agent mem reindex --searchable # backfill searchable_text where NULL
|
|
10291
|
+
one --agent mem reindex --searchable --type attio/attioPeople --all # also rewrite stale text
|
|
10194
10292
|
\`\`\`
|
|
10195
10293
|
|
|
10294
|
+
\`mem reindex --searchable\` regenerates \`searchable_text\` from each record's own \`data\` (no embedding provider needed). It fixes rows that landed with NULL searchable_text and, thanks to the cleaned-up text builder, drops UUID/timestamp noise and leads with name/title/email fields so FTS ranks on what humans search. Regenerated rows are re-queued for embedding on the next \`mem reindex\`.
|
|
10295
|
+
|
|
10196
10296
|
## Admin
|
|
10197
10297
|
|
|
10198
10298
|
\`\`\`bash
|
|
@@ -11086,7 +11186,7 @@ async function logoutCommand() {
|
|
|
11086
11186
|
|
|
11087
11187
|
// src/lib/analytics.ts
|
|
11088
11188
|
import { createRequire as createRequire2 } from "module";
|
|
11089
|
-
import { randomUUID } from "crypto";
|
|
11189
|
+
import { randomUUID, createHash } from "crypto";
|
|
11090
11190
|
import pc13 from "picocolors";
|
|
11091
11191
|
var require3 = createRequire2(import.meta.url);
|
|
11092
11192
|
var DEFAULT_POSTHOG_HOST = "https://us.i.posthog.com";
|
|
@@ -11129,6 +11229,9 @@ function isTelemetryDisabled() {
|
|
|
11129
11229
|
function distinctId() {
|
|
11130
11230
|
return getWhoAmI()?.user?.id ?? getDeviceId();
|
|
11131
11231
|
}
|
|
11232
|
+
function isAuthenticated() {
|
|
11233
|
+
return !!getWhoAmI()?.user || !!getApiKey();
|
|
11234
|
+
}
|
|
11132
11235
|
function baseProperties() {
|
|
11133
11236
|
return {
|
|
11134
11237
|
$lib: "one-cli",
|
|
@@ -11137,7 +11240,8 @@ function baseProperties() {
|
|
|
11137
11240
|
env: envName(),
|
|
11138
11241
|
os: process.platform,
|
|
11139
11242
|
arch: process.arch,
|
|
11140
|
-
node_version: process.versions.node
|
|
11243
|
+
node_version: process.versions.node,
|
|
11244
|
+
authenticated: isAuthenticated()
|
|
11141
11245
|
};
|
|
11142
11246
|
}
|
|
11143
11247
|
function personSet() {
|
|
@@ -11182,7 +11286,8 @@ function capture(event, properties = {}, opts = {}) {
|
|
|
11182
11286
|
return;
|
|
11183
11287
|
}
|
|
11184
11288
|
const did = opts.distinctId ?? distinctId();
|
|
11185
|
-
const props = { ...baseProperties(), ...properties
|
|
11289
|
+
const props = { ...baseProperties(), ...properties };
|
|
11290
|
+
if (props.$insert_id === void 0) props.$insert_id = randomUUID();
|
|
11186
11291
|
if (did === distinctId()) {
|
|
11187
11292
|
const set = personSet();
|
|
11188
11293
|
if (set) props.$set = set;
|
|
@@ -11200,13 +11305,30 @@ var ROLLUP_MAX_BATCH = 500;
|
|
|
11200
11305
|
function utcDay(ts) {
|
|
11201
11306
|
return new Date(ts).toISOString().slice(0, 10);
|
|
11202
11307
|
}
|
|
11308
|
+
var PRE_AUTH_COMMANDS = /* @__PURE__ */ new Set([
|
|
11309
|
+
"init",
|
|
11310
|
+
"login",
|
|
11311
|
+
"logout",
|
|
11312
|
+
"guide",
|
|
11313
|
+
"platforms",
|
|
11314
|
+
"onboard",
|
|
11315
|
+
"config",
|
|
11316
|
+
"update",
|
|
11317
|
+
"help"
|
|
11318
|
+
]);
|
|
11319
|
+
function shouldRecord(commandPath2) {
|
|
11320
|
+
if (isAuthenticated()) return true;
|
|
11321
|
+
return PRE_AUTH_COMMANDS.has(commandPath2.split(" ")[0]);
|
|
11322
|
+
}
|
|
11203
11323
|
function recordCommand(command) {
|
|
11204
11324
|
if (isTelemetryDisabled()) {
|
|
11205
11325
|
writeUsageLog([]);
|
|
11206
11326
|
return;
|
|
11207
11327
|
}
|
|
11328
|
+
const cmdPath = commandPath(command);
|
|
11329
|
+
if (!shouldRecord(cmdPath)) return;
|
|
11208
11330
|
const did = distinctId();
|
|
11209
|
-
const entry = { ts: Date.now(), command:
|
|
11331
|
+
const entry = { ts: Date.now(), command: cmdPath, agent: isAgentMode(), did };
|
|
11210
11332
|
appendUsageLog(JSON.stringify(entry));
|
|
11211
11333
|
const today = utcDay(entry.ts);
|
|
11212
11334
|
const state = readUsageState();
|
|
@@ -11219,8 +11341,10 @@ function flushUsageRollups(opts = {}) {
|
|
|
11219
11341
|
writeUsageLog([]);
|
|
11220
11342
|
return;
|
|
11221
11343
|
}
|
|
11344
|
+
const lines = claimUsageLog();
|
|
11345
|
+
if (!lines || lines.length === 0) return;
|
|
11222
11346
|
const entries = [];
|
|
11223
|
-
for (const line of
|
|
11347
|
+
for (const line of lines) {
|
|
11224
11348
|
try {
|
|
11225
11349
|
const e = JSON.parse(line);
|
|
11226
11350
|
if (e && typeof e.ts === "number" && typeof e.command === "string" && typeof e.did === "string") {
|
|
@@ -11229,10 +11353,7 @@ function flushUsageRollups(opts = {}) {
|
|
|
11229
11353
|
} catch {
|
|
11230
11354
|
}
|
|
11231
11355
|
}
|
|
11232
|
-
if (entries.length === 0)
|
|
11233
|
-
writeUsageLog([]);
|
|
11234
|
-
return;
|
|
11235
|
-
}
|
|
11356
|
+
if (entries.length === 0) return;
|
|
11236
11357
|
const currentDid = entries[entries.length - 1].did;
|
|
11237
11358
|
const now = Date.now();
|
|
11238
11359
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -11248,7 +11369,7 @@ function flushUsageRollups(opts = {}) {
|
|
|
11248
11369
|
if (due) emitRollup(did, group);
|
|
11249
11370
|
else kept.push(...group);
|
|
11250
11371
|
}
|
|
11251
|
-
|
|
11372
|
+
for (const e of kept) appendUsageLog(JSON.stringify(e));
|
|
11252
11373
|
}
|
|
11253
11374
|
function emitRollup(did, group) {
|
|
11254
11375
|
const byCommand = {};
|
|
@@ -11257,6 +11378,7 @@ function emitRollup(did, group) {
|
|
|
11257
11378
|
byCommand[e.command] = (byCommand[e.command] ?? 0) + 1;
|
|
11258
11379
|
if (e.agent) agentCount += 1;
|
|
11259
11380
|
}
|
|
11381
|
+
const insertId = createHash("sha1").update(`${did}|${group.map((e) => `${e.ts}:${e.command}:${e.agent ? 1 : 0}`).join("|")}`).digest("hex");
|
|
11260
11382
|
capture(
|
|
11261
11383
|
"CLI Usage Rollup",
|
|
11262
11384
|
{
|
|
@@ -11265,7 +11387,8 @@ function emitRollup(did, group) {
|
|
|
11265
11387
|
agent_count: agentCount,
|
|
11266
11388
|
human_count: group.length - agentCount,
|
|
11267
11389
|
window_start: new Date(group[0].ts).toISOString(),
|
|
11268
|
-
window_end: new Date(group[group.length - 1].ts).toISOString()
|
|
11390
|
+
window_end: new Date(group[group.length - 1].ts).toISOString(),
|
|
11391
|
+
$insert_id: insertId
|
|
11269
11392
|
},
|
|
11270
11393
|
{ distinctId: did, timestamp: new Date(group[group.length - 1].ts).toISOString() }
|
|
11271
11394
|
);
|
|
@@ -3,12 +3,12 @@ import {
|
|
|
3
3
|
dotPathToJsonbExpr,
|
|
4
4
|
memMigrateCommand,
|
|
5
5
|
reviveStringifiedJson
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-GXOIR3GF.js";
|
|
7
7
|
import "./chunk-44CV5IMX.js";
|
|
8
|
-
import "./chunk-
|
|
9
|
-
import "./chunk-
|
|
10
|
-
import "./chunk-
|
|
11
|
-
import "./chunk-
|
|
8
|
+
import "./chunk-CLJFSFFM.js";
|
|
9
|
+
import "./chunk-7RV7T5PX.js";
|
|
10
|
+
import "./chunk-C7DWZ7B5.js";
|
|
11
|
+
import "./chunk-SO323PZP.js";
|
|
12
12
|
export {
|
|
13
13
|
buildIdentityMap,
|
|
14
14
|
dotPathToJsonbExpr,
|
|
@@ -3,14 +3,16 @@ import {
|
|
|
3
3
|
closeBackendIfCached,
|
|
4
4
|
getBackend,
|
|
5
5
|
resetBackendSingleton,
|
|
6
|
+
updateRecord,
|
|
6
7
|
upsertRecord
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
9
|
-
import "./chunk-
|
|
8
|
+
} from "./chunk-7RV7T5PX.js";
|
|
9
|
+
import "./chunk-C7DWZ7B5.js";
|
|
10
|
+
import "./chunk-SO323PZP.js";
|
|
10
11
|
export {
|
|
11
12
|
addRecord,
|
|
12
13
|
closeBackendIfCached,
|
|
13
14
|
getBackend,
|
|
14
15
|
resetBackendSingleton,
|
|
16
|
+
updateRecord,
|
|
15
17
|
upsertRecord
|
|
16
18
|
};
|
|
@@ -3,12 +3,12 @@ import {
|
|
|
3
3
|
note,
|
|
4
4
|
okJson,
|
|
5
5
|
requireMemoryInit
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-CLJFSFFM.js";
|
|
7
7
|
import {
|
|
8
8
|
getBackend
|
|
9
|
-
} from "./chunk-
|
|
10
|
-
import "./chunk-
|
|
11
|
-
import "./chunk-
|
|
9
|
+
} from "./chunk-7RV7T5PX.js";
|
|
10
|
+
import "./chunk-C7DWZ7B5.js";
|
|
11
|
+
import "./chunk-SO323PZP.js";
|
|
12
12
|
|
|
13
13
|
// src/lib/memory/sync/schema.ts
|
|
14
14
|
import pc from "picocolors";
|
package/package.json
CHANGED
package/skills/one/SKILL.md
CHANGED
|
@@ -170,10 +170,19 @@ Underneath, the store has no `platform` column — `type` is the only platform-s
|
|
|
170
170
|
```bash
|
|
171
171
|
# User memories
|
|
172
172
|
one --agent mem add note '{"content":"..."}' --tags work --weight 7
|
|
173
|
+
one --agent mem update <id> '{"status":"done"}' # merges into data; refreshes searchable_text
|
|
173
174
|
one --agent mem search "deadline" # hybrid if key set, else FTS
|
|
174
175
|
one --agent mem list note --limit 20
|
|
175
176
|
one --agent mem link <from-id> <to-id> relates_to --bi
|
|
176
177
|
|
|
178
|
+
# Identity keys (first-class column, NOT data). Unique across ACTIVE records;
|
|
179
|
+
# archiving a record frees its keys. `mem update '{"keys":[...]}'` is rejected.
|
|
180
|
+
one --agent mem key <id> --add email:x@y.com # add/--remove/--set
|
|
181
|
+
one --agent mem find-by-source email:x@y.com # prefers the active owner
|
|
182
|
+
|
|
183
|
+
# Backfill searchable_text (no embedding provider needed) — fixes NULL/noisy text
|
|
184
|
+
one --agent mem reindex --searchable --type attio/attioPeople
|
|
185
|
+
|
|
177
186
|
# Status + diagnostics
|
|
178
187
|
one --agent mem status # backend, provider, _upgrade hint
|
|
179
188
|
one --agent mem doctor # full health report
|
package/dist/sql-W3ZUOUNR.js
DELETED