@hasna/mementos 0.14.68 → 0.14.69
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/dist/cli/__fixtures__/clean-fallback-stub-server.d.ts +2 -0
- package/dist/cli/__fixtures__/clean-fallback-stub-server.d.ts.map +1 -0
- package/dist/cli/commands/io-clean.d.ts.map +1 -1
- package/dist/cli/commands/memory-cmd-crud.d.ts.map +1 -1
- package/dist/cli/commands/storage.d.ts.map +1 -1
- package/dist/cli/global-options.d.ts +27 -0
- package/dist/cli/global-options.d.ts.map +1 -0
- package/dist/cli/index.js +502 -241
- package/dist/cli/register-all.d.ts +17 -0
- package/dist/cli/register-all.d.ts.map +1 -0
- package/dist/cli/startup-side-effects.d.ts +9 -0
- package/dist/cli/startup-side-effects.d.ts.map +1 -0
- package/dist/db/__fixtures__/fail-closed-stub-server.d.ts +2 -0
- package/dist/db/__fixtures__/fail-closed-stub-server.d.ts.map +1 -0
- package/dist/db/api-mode.d.ts +70 -2
- package/dist/db/api-mode.d.ts.map +1 -1
- package/dist/db/locks.d.ts.map +1 -1
- package/dist/db/memories.d.ts +24 -6
- package/dist/db/memories.d.ts.map +1 -1
- package/dist/db/session-jobs.d.ts.map +1 -1
- package/dist/db/store-backend.d.ts +33 -0
- package/dist/db/store-backend.d.ts.map +1 -0
- package/dist/index.js +80 -20
- package/dist/lib/enum-validation.d.ts +20 -0
- package/dist/lib/enum-validation.d.ts.map +1 -0
- package/dist/mcp/index.js +147 -30
- package/dist/server/helpers.d.ts +11 -0
- package/dist/server/helpers.d.ts.map +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +185 -30
- package/dist/test-support/preload-local-store.d.ts +2 -0
- package/dist/test-support/preload-local-store.d.ts.map +1 -0
- package/dist/test-support/store-isolation.d.ts +83 -0
- package/dist/test-support/store-isolation.d.ts.map +1 -0
- package/dist/types/index.d.ts +8 -4
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
/**
|
|
3
|
+
* Attach EVERY command group to `program`, in the order the CLI presents them.
|
|
4
|
+
*
|
|
5
|
+
* This is the single registration list. `index.tsx` calls it to build the real
|
|
6
|
+
* CLI, and `global-options.test.ts` calls it to walk the real option tables — so
|
|
7
|
+
* the structural short-flag guard cannot silently check a smaller tree than the
|
|
8
|
+
* one that ships.
|
|
9
|
+
*
|
|
10
|
+
* That drift was real: the guard originally re-listed 11 groups by hand while
|
|
11
|
+
* the CLI wired 15 sources, leaving `init`, `project-panel`, `brains` and the
|
|
12
|
+
* `@hasna/events` groups (`events`, `webhooks`) unwalked. Seven live `-j`
|
|
13
|
+
* collisions sat in that blind spot while the guard reported clean. Adding a
|
|
14
|
+
* command group here is now the only step required for it to be covered.
|
|
15
|
+
*/
|
|
16
|
+
export declare function registerAllCommands(program: Command): Command;
|
|
17
|
+
//# sourceMappingURL=register-all.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register-all.d.ts","sourceRoot":"","sources":["../../src/cli/register-all.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAkBzC;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAiB7D"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
/**
|
|
3
|
+
* Mark a command as having no startup database access, and return it so it can
|
|
4
|
+
* be used inline at the end of a builder chain.
|
|
5
|
+
*/
|
|
6
|
+
export declare function withoutStartupDbAccess<T extends Command>(command: T): T;
|
|
7
|
+
/** Whether the command about to run has opted out of the startup DB access. */
|
|
8
|
+
export declare function skipsStartupDbAccess(command: Command | undefined): boolean;
|
|
9
|
+
//# sourceMappingURL=startup-side-effects.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"startup-side-effects.d.ts","sourceRoot":"","sources":["../../src/cli/startup-side-effects.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIzC;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,SAAS,OAAO,EAAE,OAAO,EAAE,CAAC,GAAG,CAAC,CAGvE;AAED,+EAA+E;AAC/E,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,GAAG,OAAO,CAE1E"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fail-closed-stub-server.d.ts","sourceRoot":"","sources":["../../../src/db/__fixtures__/fail-closed-stub-server.ts"],"names":[],"mappings":"AAcA,QAAA,MAAM,MAAM,uBAiBV,CAAC"}
|
package/dist/db/api-mode.d.ts
CHANGED
|
@@ -2,7 +2,56 @@ export interface ApiConfig {
|
|
|
2
2
|
baseUrl: string;
|
|
3
3
|
apiKey: string;
|
|
4
4
|
}
|
|
5
|
+
export declare const API_URL_ENV_KEYS: readonly ["HASNA_MEMENTOS_API_URL", "MEMENTOS_API_URL"];
|
|
6
|
+
export declare const API_KEY_ENV_KEYS: readonly ["HASNA_MEMENTOS_API_KEY", "MEMENTOS_API_KEY"];
|
|
7
|
+
export declare const DATABASE_URL_ENV_KEYS: readonly ["HASNA_MEMENTOS_DATABASE_URL", "MEMENTOS_DATABASE_URL"];
|
|
8
|
+
/**
|
|
9
|
+
* Which env keys currently select API mode, by name only — never the values.
|
|
10
|
+
* Used by the operator-facing mode report so a human can see *why* the client
|
|
11
|
+
* is pointed at the cloud without reading source or echoing a credential.
|
|
12
|
+
*/
|
|
13
|
+
export declare function getApiModeEnvSources(): {
|
|
14
|
+
urlKey: string | null;
|
|
15
|
+
keyKey: string | null;
|
|
16
|
+
databaseUrlKey: string | null;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Escape hatch for a test that genuinely must reach a non-loopback endpoint.
|
|
20
|
+
* Opt-in by design: the default has to be safe, because the failure it prevents
|
|
21
|
+
* is silent and lands in a shared store.
|
|
22
|
+
*/
|
|
23
|
+
export declare const ALLOW_REMOTE_API_IN_TESTS_ENV = "MEMENTOS_ALLOW_REMOTE_API_IN_TESTS";
|
|
5
24
|
/** Resolve the API client config from env, or `null` when not configured. */
|
|
25
|
+
/** The env keys that explicitly select the local SQLite store. */
|
|
26
|
+
export declare const DB_PATH_ENV_KEYS: readonly ["HASNA_MEMENTOS_DB_PATH", "MEMENTOS_DB_PATH"];
|
|
27
|
+
/** Raised when the environment does not unambiguously select one store. */
|
|
28
|
+
export declare class MementosStoreConfigError extends Error {
|
|
29
|
+
readonly code = "MEMENTOS_STORE_CONFIG";
|
|
30
|
+
constructor(message: string);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Throw unless the environment unambiguously selects one store.
|
|
34
|
+
*
|
|
35
|
+
* AMBIGUOUS CONFIGURATION IS AN ERROR, NOT A DEFAULT. Half an API configuration
|
|
36
|
+
* used to resolve to `null`, which every caller read as "local SQLite" — so an
|
|
37
|
+
* operator whose API key failed to load silently got a different, usually stale,
|
|
38
|
+
* dataset with no error and no flag. From the caller's side that is
|
|
39
|
+
* indistinguishable from a store that is working.
|
|
40
|
+
*
|
|
41
|
+
* Precedence, highest first:
|
|
42
|
+
* 1. An explicit SQLite path (`HASNA_MEMENTOS_DB_PATH` / `MEMENTOS_DB_PATH`) is
|
|
43
|
+
* the narrowest, most specific signal and selects LOCAL, so local dev,
|
|
44
|
+
* tooling and import/export keep working when a stray credential is exported
|
|
45
|
+
* globally.
|
|
46
|
+
* 2. A client DSN present disables API mode. Unchanged here, and tracked
|
|
47
|
+
* separately — a DSN on a client is forbidden and handled in database.ts.
|
|
48
|
+
* 3. Both API URL and API key present -> API mode.
|
|
49
|
+
* 4. Exactly ONE of them present -> ERROR naming the missing variable.
|
|
50
|
+
* 5. Neither present -> LOCAL. The documented single-operator default.
|
|
51
|
+
*
|
|
52
|
+
* Never reads, logs, or embeds a credential value — only variable NAMES.
|
|
53
|
+
*/
|
|
54
|
+
export declare function assertUnambiguousStoreEnv(): void;
|
|
6
55
|
export declare function getApiConfig(): ApiConfig | null;
|
|
7
56
|
/**
|
|
8
57
|
* True when the client should route memory operations to the cloud API.
|
|
@@ -15,8 +64,27 @@ export declare class ApiRequestError extends Error {
|
|
|
15
64
|
readonly body: string;
|
|
16
65
|
constructor(message: string, status: number, body: string);
|
|
17
66
|
}
|
|
18
|
-
|
|
19
|
-
|
|
67
|
+
export interface ApiJsonOptions {
|
|
68
|
+
/**
|
|
69
|
+
* Treat `404` as a normal outcome and return `{status: 404, data: undefined}`
|
|
70
|
+
* instead of throwing. ONLY for callers where "absent" is a real answer —
|
|
71
|
+
* a GET of one record by id, a DELETE that tolerates an already-gone row, or
|
|
72
|
+
* a call that has an explicit fallback for a route an older server image does
|
|
73
|
+
* not serve yet (see `runCleanupViaApi`).
|
|
74
|
+
*
|
|
75
|
+
* It must never be set on a create/upsert path. A 404 there means the route
|
|
76
|
+
* did not exist on the server (client/server version skew, wrong base URL),
|
|
77
|
+
* so nothing was written; returning success-shaped data made the CLI print
|
|
78
|
+
* "Saved:" and exit 0 having persisted nothing. Default is fail-closed.
|
|
79
|
+
*/
|
|
80
|
+
allow404?: boolean;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Authed JSON request. Throws {@link ApiRequestError} on every non-2xx,
|
|
84
|
+
* including 404, unless the caller opts into 404 pass-through via
|
|
85
|
+
* {@link ApiJsonOptions.allow404}.
|
|
86
|
+
*/
|
|
87
|
+
export declare function apiJson<T = unknown>(method: string, path: string, body?: unknown, options?: ApiJsonOptions): {
|
|
20
88
|
status: number;
|
|
21
89
|
data: T;
|
|
22
90
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-mode.d.ts","sourceRoot":"","sources":["../../src/db/api-mode.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"api-mode.d.ts","sourceRoot":"","sources":["../../src/db/api-mode.ts"],"names":[],"mappings":"AA0CA,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AASD,eAAO,MAAM,gBAAgB,yDAA0D,CAAC;AACxF,eAAO,MAAM,gBAAgB,yDAA0D,CAAC;AACxF,eAAO,MAAM,qBAAqB,mEAAoE,CAAC;AAsBvG;;;;GAIG;AACH,wBAAgB,oBAAoB,IAAI;IAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAMtH;AAED;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,uCAAuC,CAAC;AAyDlF,6EAA6E;AAC7E,kEAAkE;AAClE,eAAO,MAAM,gBAAgB,yDAA0D,CAAC;AAExF,2EAA2E;AAC3E,qBAAa,wBAAyB,SAAQ,KAAK;IACjD,QAAQ,CAAC,IAAI,2BAA2B;gBAC5B,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,yBAAyB,IAAI,IAAI,CAuBhD;AAED,wBAAgB,YAAY,IAAI,SAAS,GAAG,IAAI,CAM/C;AAED;;;;GAIG;AACH,wBAAgB,SAAS,IAAI,OAAO,CAGnC;AAED,qBAAa,eAAgB,SAAQ,KAAK;aAGtB,MAAM,EAAE,MAAM;aACd,IAAI,EAAE,MAAM;gBAF5B,OAAO,EAAE,MAAM,EACC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM;CAK/B;AA0GD,MAAM,WAAW,cAAc;IAC7B;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,CAAC,GAAG,OAAO,EACjC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,OAAO,EACd,OAAO,CAAC,EAAE,cAAc,GACvB;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,CAAC,CAAA;CAAE,CAiB7B;AAED,8EAA8E;AAC9E,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAe/D"}
|
package/dist/db/locks.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"locks.d.ts","sourceRoot":"","sources":["../../src/db/locks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,eAAe,CAAC;AAI1D,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,WAAW,GAAG,MAAM,CAAC;AAC5F,MAAM,MAAM,QAAQ,GAAG,UAAU,GAAG,WAAW,CAAC;AAEhD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,YAAY,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,QAAQ,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAcD;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,YAAY,EAC1B,UAAU,EAAE,MAAM,EAClB,QAAQ,GAAE,QAAsB,EAChC,UAAU,SAAM,EAChB,EAAE,CAAC,EAAE,QAAQ,GACZ,YAAY,GAAG,IAAI,CAuErB;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,OAAO,
|
|
1
|
+
{"version":3,"file":"locks.d.ts","sourceRoot":"","sources":["../../src/db/locks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,eAAe,CAAC;AAI1D,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,WAAW,GAAG,MAAM,CAAC;AAC5F,MAAM,MAAM,QAAQ,GAAG,UAAU,GAAG,WAAW,CAAC;AAEhD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,YAAY,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,QAAQ,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAcD;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,YAAY,EAC1B,UAAU,EAAE,MAAM,EAClB,QAAQ,GAAE,QAAsB,EAChC,UAAU,SAAM,EAChB,EAAE,CAAC,EAAE,QAAQ,GACZ,YAAY,GAAG,IAAI,CAuErB;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,OAAO,CAgBnF;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,YAAY,EAC1B,UAAU,EAAE,MAAM,EAClB,EAAE,CAAC,EAAE,QAAQ,GACZ,MAAM,CAOR;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,CAW3E;AAED;;;GAGG;AACH,wBAAgB,SAAS,CACvB,YAAY,EAAE,YAAY,EAC1B,UAAU,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,QAAQ,EACnB,EAAE,CAAC,EAAE,QAAQ,GACZ,YAAY,EAAE,CAqBhB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,YAAY,EAC1B,UAAU,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,QAAQ,EACnB,EAAE,CAAC,EAAE,QAAQ,GACZ,YAAY,GAAG,IAAI,CAcrB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,YAAY,EAAE,CAa7E;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG,eAAe,EAAE,CAS1E;AAED,wBAAgB,iBAAiB,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,CAQvD"}
|
package/dist/db/memories.d.ts
CHANGED
|
@@ -4,7 +4,15 @@ export declare function parseMemoryRow(row: Record<string, unknown>): Memory;
|
|
|
4
4
|
export declare function createMemory(input: CreateMemoryInput, dedupeMode?: DedupeMode, db?: Database): Memory;
|
|
5
5
|
export interface BulkUpsertResult {
|
|
6
6
|
inserted: number;
|
|
7
|
+
/** Rows the store already had (id or unique-key conflict) — a benign no-op. */
|
|
7
8
|
skipped: number;
|
|
9
|
+
/**
|
|
10
|
+
* Rows the store REFUSED (bad enum, failed CHECK/FK, missing key). These did
|
|
11
|
+
* NOT persist. Kept separate from `skipped` so a caller can never read a
|
|
12
|
+
* dropped row as an idempotent no-op; every rejection also has a line in
|
|
13
|
+
* `errors`. `inserted + skipped + rejected === total`.
|
|
14
|
+
*/
|
|
15
|
+
rejected: number;
|
|
8
16
|
errors: string[];
|
|
9
17
|
total: number;
|
|
10
18
|
}
|
|
@@ -18,12 +26,22 @@ export interface BulkUpsertResult {
|
|
|
18
26
|
* - does NOT dedupe by key/scope/agent and does NOT fire hooks / entity
|
|
19
27
|
* extraction / embedding work (those run asynchronously elsewhere).
|
|
20
28
|
*
|
|
21
|
-
* Idempotency is by
|
|
22
|
-
* `ON CONFLICT DO NOTHING
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
29
|
+
* Idempotency is by conflict, not by suppression: the statement carries an
|
|
30
|
+
* explicit bare `ON CONFLICT DO NOTHING`, which SQLite and Postgres both read
|
|
31
|
+
* as "ignore a uniqueness conflict (primary-key `id` or the unique key index)".
|
|
32
|
+
* A row already present is therefore left untouched and re-runs never create
|
|
33
|
+
* duplicates. This is the cross-machine → cloud backfill path for the fleet
|
|
34
|
+
* self-host cutover: a machine's local memories are shipped to the cloud store
|
|
35
|
+
* verbatim without mutating rows that are already present.
|
|
36
|
+
*
|
|
37
|
+
* It deliberately does NOT use `INSERT OR IGNORE`: on SQLite that swallows
|
|
38
|
+
* *every* constraint failure, so a row refused by the `category`/`scope`/
|
|
39
|
+
* `source`/`status` CHECK vanished with `changes === 0` and was counted as an
|
|
40
|
+
* idempotent `skipped` — a cloud write that did not persist reporting success.
|
|
41
|
+
* With a bare `ON CONFLICT DO NOTHING` a CHECK/FK/NOT NULL violation still
|
|
42
|
+
* throws into the catch below and lands in `rejected` + `errors`. Enum values
|
|
43
|
+
* are additionally pre-validated per row so the reason names the field and the
|
|
44
|
+
* accepted set instead of quoting raw constraint SQL.
|
|
27
45
|
*
|
|
28
46
|
* FK-referenced agent/project/session/machine rows are stubbed via
|
|
29
47
|
* {@link ensureMemoryReferences} so cross-machine authorship ids never fail the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memories.d.ts","sourceRoot":"","sources":["../../src/db/memories.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE1D,OAAO,KAAK,EACV,iBAAiB,EACjB,UAAU,EACV,MAAM,EACN,YAAY,EACZ,aAAa,EACb,iBAAiB,EAClB,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"memories.d.ts","sourceRoot":"","sources":["../../src/db/memories.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE1D,OAAO,KAAK,EACV,iBAAiB,EACjB,UAAU,EACV,MAAM,EACN,YAAY,EACZ,aAAa,EACb,iBAAiB,EAClB,MAAM,mBAAmB,CAAC;AAuD3B,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAqCnE;AAMD,wBAAgB,YAAY,CAC1B,KAAK,EAAE,iBAAiB,EACxB,UAAU,GAAE,UAAoB,EAChC,EAAE,CAAC,EAAE,QAAQ,GACZ,MAAM,CA+NR;AAMD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,+EAA+E;IAC/E,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACxC,EAAE,CAAC,EAAE,QAAQ,GACZ,gBAAgB,CA4IlB;AAiED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,IAAI,CAkBlE;AAED,wBAAgB,cAAc,CAC5B,GAAG,EAAE,MAAM,EACX,KAAK,CAAC,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,MAAM,EAChB,SAAS,CAAC,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,EAClB,EAAE,CAAC,EAAE,QAAQ,EACb,KAAK,CAAC,EAAE,MAAM,GACb,MAAM,GAAG,IAAI,CAuCf;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,EACX,KAAK,CAAC,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,MAAM,EAChB,SAAS,CAAC,EAAE,MAAM,EAClB,EAAE,CAAC,EAAE,QAAQ,GACZ,MAAM,EAAE,CA8BV;AAMD,wBAAgB,YAAY,CAAC,MAAM,CAAC,EAAE,YAAY,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,EAAE,CAuK3E;AAED,MAAM,WAAW,oBAAoB;IACnC,GAAG,EAAE,MAAM,EAAE,CAAC;IACd,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE;IACJ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,EACD,EAAE,CAAC,EAAE,QAAQ,GACZ,oBAAoB,CAoDtB;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,EACvF,EAAE,CAAC,EAAE,QAAQ,GACZ,MAAM,EAAE,CAsBV;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,EAC9C,EAAE,CAAC,EAAE,QAAQ,GACZ,MAAM,EAAE,CAkBV;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,aAAa,EAAE,MAAM,EACrB,SAAS,CAAC,EAAE,MAAM,EAClB,EAAE,CAAC,EAAE,QAAQ,GACZ,MAAM,EAAE,CAwBV;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAkBvF;AAMD,wBAAgB,YAAY,CAC1B,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,iBAAiB,EACxB,EAAE,CAAC,EAAE,QAAQ,GACZ,MAAM,CA+HR;AAMD,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,OAAO,CAe/D;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,CAuBvE;AAMD,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,IAAI,CAQ3D;AAUD,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,IAAI,CAuBpE;AAMD,wBAAgB,oBAAoB,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,CAqB1D;AAMD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,aAAa,EAAE,CA4BlF;AAMD,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAcvG;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAClC,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE;IACP,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,OAAO,CAAC;CACpB,EACN,EAAE,CAAC,EAAE,QAAQ,GACZ,OAAO,CAAC,oBAAoB,EAAE,CAAC,CA4DjC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-jobs.d.ts","sourceRoot":"","sources":["../../src/db/session-jobs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,eAAe,CAAC;AAK1D,MAAM,MAAM,gBAAgB,GAAG,aAAa,GAAG,OAAO,GAAG,QAAQ,GAAG,eAAe,CAAC;AACpF,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,YAAY,GAAG,WAAW,GAAG,QAAQ,CAAC;AAEjF,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,MAAM,EAAE,gBAAgB,CAAC;IACzB,MAAM,EAAE,gBAAgB,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AA6BD,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,qBAAqB,EAC5B,EAAE,CAAC,EAAE,QAAQ,GACZ,gBAAgB,CAwBlB;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,gBAAgB,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"session-jobs.d.ts","sourceRoot":"","sources":["../../src/db/session-jobs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,eAAe,CAAC;AAK1D,MAAM,MAAM,gBAAgB,GAAG,aAAa,GAAG,OAAO,GAAG,QAAQ,GAAG,eAAe,CAAC;AACpF,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,YAAY,GAAG,WAAW,GAAG,QAAQ,CAAC;AAEjF,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,MAAM,EAAE,gBAAgB,CAAC;IACzB,MAAM,EAAE,gBAAgB,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AA6BD,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,qBAAqB,EAC5B,EAAE,CAAC,EAAE,QAAQ,GACZ,gBAAgB,CAwBlB;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,gBAAgB,GAAG,IAAI,CAiBhF;AAED,wBAAgB,eAAe,CAC7B,MAAM,CAAC,EAAE,gBAAgB,EACzB,EAAE,CAAC,EAAE,QAAQ,GACZ,gBAAgB,EAAE,CA2CpB;AAED,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,qBAAqB,EAC9B,EAAE,CAAC,EAAE,QAAQ,GACZ,gBAAgB,GAAG,IAAI,CAwCzB;AAED,wBAAgB,iBAAiB,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG,gBAAgB,GAAG,IAAI,CASxE"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The transport that reads and writes will actually use.
|
|
3
|
+
*
|
|
4
|
+
* - `local-sqlite` — the on-disk SQLite file at `db_path` is authoritative.
|
|
5
|
+
* - `cloud-api` — authed HTTPS to the self-hosted server (the shared store).
|
|
6
|
+
* - `cloud-postgres` — a direct Postgres DSN; server-only, never a client.
|
|
7
|
+
*/
|
|
8
|
+
export type StoreBackend = "local-sqlite" | "cloud-api" | "cloud-postgres";
|
|
9
|
+
export interface StoreBackendReport {
|
|
10
|
+
/** Machine-readable contract for scripts and test guards. */
|
|
11
|
+
schema: "mementos.store_backend.v1";
|
|
12
|
+
backend: StoreBackend;
|
|
13
|
+
/** True when the HTTPS client transport is engaged (client reads AND writes). */
|
|
14
|
+
api_mode: boolean;
|
|
15
|
+
/** The `getStorageMode()` value — reported for diagnosis, NOT the answer. */
|
|
16
|
+
storage_mode: string;
|
|
17
|
+
/** The SQLite path that WOULD be used. Meaningful only for `local-sqlite`. */
|
|
18
|
+
db_path: string;
|
|
19
|
+
/** Cloud endpoint origin + prefix. Not a secret; the key is never included. */
|
|
20
|
+
api_endpoint: string | null;
|
|
21
|
+
/** Whether an API key is configured. The value is never read or reported. */
|
|
22
|
+
api_key_present: boolean;
|
|
23
|
+
/** Env key NAMES that produced this backend, or `"default"`. Names only. */
|
|
24
|
+
selected_by: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Resolve the effective store backend from the environment.
|
|
28
|
+
*
|
|
29
|
+
* Pure with respect to storage: touches no database, makes no network call, and
|
|
30
|
+
* never places a credential value in the returned report.
|
|
31
|
+
*/
|
|
32
|
+
export declare function resolveStoreBackend(): StoreBackendReport;
|
|
33
|
+
//# sourceMappingURL=store-backend.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store-backend.d.ts","sourceRoot":"","sources":["../../src/db/store-backend.ts"],"names":[],"mappings":"AA4BA;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GAAG,cAAc,GAAG,WAAW,GAAG,gBAAgB,CAAC;AAE3E,MAAM,WAAW,kBAAkB;IACjC,6DAA6D;IAC7D,MAAM,EAAE,2BAA2B,CAAC;IACpC,OAAO,EAAE,YAAY,CAAC;IACtB,iFAAiF;IACjF,QAAQ,EAAE,OAAO,CAAC;IAClB,6EAA6E;IAC7E,YAAY,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,OAAO,EAAE,MAAM,CAAC;IAChB,+EAA+E;IAC/E,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,6EAA6E;IAC7E,eAAe,EAAE,OAAO,CAAC;IACzB,4EAA4E;IAC5E,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,IAAI,kBAAkB,CAkCxD"}
|
package/dist/index.js
CHANGED
|
@@ -938,7 +938,7 @@ import { tmpdir } from "os";
|
|
|
938
938
|
import { join as join2 } from "path";
|
|
939
939
|
import { writeFileSync as writeFileSync2, unlinkSync } from "fs";
|
|
940
940
|
import { randomUUID } from "crypto";
|
|
941
|
-
function firstEnv(
|
|
941
|
+
function firstEnv(keys) {
|
|
942
942
|
for (const k of keys) {
|
|
943
943
|
const v = process.env[k]?.trim();
|
|
944
944
|
if (v)
|
|
@@ -946,8 +946,38 @@ function firstEnv(...keys) {
|
|
|
946
946
|
}
|
|
947
947
|
return;
|
|
948
948
|
}
|
|
949
|
+
function firstEnvKey(keys) {
|
|
950
|
+
for (const k of keys) {
|
|
951
|
+
if (process.env[k]?.trim())
|
|
952
|
+
return k;
|
|
953
|
+
}
|
|
954
|
+
return null;
|
|
955
|
+
}
|
|
949
956
|
function hasDatabaseUrl() {
|
|
950
|
-
return Boolean(firstEnv(
|
|
957
|
+
return Boolean(firstEnv(DATABASE_URL_ENV_KEYS));
|
|
958
|
+
}
|
|
959
|
+
function isLoopbackHost(rawHost) {
|
|
960
|
+
const host = rawHost.replace(/^\[/, "").replace(/\]$/, "").toLowerCase();
|
|
961
|
+
return host === "localhost" || host === "::1" || /^127\./.test(host);
|
|
962
|
+
}
|
|
963
|
+
function assertRequestAllowedUnderTest(baseUrl) {
|
|
964
|
+
if (process.env["NODE_ENV"] !== "test")
|
|
965
|
+
return;
|
|
966
|
+
if (process.env[ALLOW_REMOTE_API_IN_TESTS_ENV]?.trim())
|
|
967
|
+
return;
|
|
968
|
+
let host;
|
|
969
|
+
try {
|
|
970
|
+
host = new URL(baseUrl).hostname;
|
|
971
|
+
} catch {
|
|
972
|
+
host = "";
|
|
973
|
+
}
|
|
974
|
+
if (host && isLoopbackHost(host))
|
|
975
|
+
return;
|
|
976
|
+
throw new Error("api-mode: REFUSING to make a cloud request from a test process \u2014 this would write to or read " + "from the SHARED PRODUCTION memory store, where test fixtures are indistinguishable from real " + `memories.
|
|
977
|
+
` + ` host : ${host || "(unparseable base URL)"}
|
|
978
|
+
` + ` how this happens: a selector set at module scope (after the bun test preload ran), or \`bun test\` ` + `invoked from a directory with no bunfig.toml so the preload never loaded.
|
|
979
|
+
` + " fix : build the child/process env via src/test-support/store-isolation.ts, or point the " + `suite at a loopback stub.
|
|
980
|
+
` + ` override : set ${ALLOW_REMOTE_API_IN_TESTS_ENV}=1 only for a test that must reach a remote endpoint.`);
|
|
951
981
|
}
|
|
952
982
|
function normalizeBase(raw) {
|
|
953
983
|
let base = raw.trim().replace(/\/+$/, "");
|
|
@@ -955,9 +985,24 @@ function normalizeBase(raw) {
|
|
|
955
985
|
return base;
|
|
956
986
|
return `${base}/v1`;
|
|
957
987
|
}
|
|
988
|
+
function assertUnambiguousStoreEnv() {
|
|
989
|
+
if (firstEnvKey(DB_PATH_ENV_KEYS))
|
|
990
|
+
return;
|
|
991
|
+
if (hasDatabaseUrl())
|
|
992
|
+
return;
|
|
993
|
+
const urlKey = firstEnvKey(API_URL_ENV_KEYS);
|
|
994
|
+
const keyKey = firstEnvKey(API_KEY_ENV_KEYS);
|
|
995
|
+
if (urlKey && !keyKey) {
|
|
996
|
+
throw new MementosStoreConfigError(`${urlKey} points at the cloud memory store but ${API_KEY_ENV_KEYS[0]} is not set. ` + `Refusing to serve the on-box SQLite store in its place, because it holds a different ` + `dataset. Set ${API_KEY_ENV_KEYS[0]} to reach the cloud store. If you meant to use the ` + `on-box SQLite store, unset ${urlKey} or set ${DB_PATH_ENV_KEYS[0]} explicitly.`);
|
|
997
|
+
}
|
|
998
|
+
if (keyKey && !urlKey) {
|
|
999
|
+
throw new MementosStoreConfigError(`${keyKey} is set but ${API_URL_ENV_KEYS[0]} is not, so the cloud memory store cannot be ` + `reached. Refusing to serve the on-box SQLite store in its place, because it holds a ` + `different dataset. Set ${API_URL_ENV_KEYS[0]} to reach the cloud store. If you meant to ` + `use the on-box SQLite store, unset ${keyKey} or set ${DB_PATH_ENV_KEYS[0]} explicitly.`);
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
958
1002
|
function getApiConfig() {
|
|
959
|
-
|
|
960
|
-
const
|
|
1003
|
+
assertUnambiguousStoreEnv();
|
|
1004
|
+
const rawBase = firstEnv(API_URL_ENV_KEYS);
|
|
1005
|
+
const apiKey = firstEnv(API_KEY_ENV_KEYS);
|
|
961
1006
|
if (!rawBase || !apiKey)
|
|
962
1007
|
return null;
|
|
963
1008
|
return { baseUrl: normalizeBase(rawBase), apiKey };
|
|
@@ -971,6 +1016,7 @@ function apiRequestRaw(method, path, body) {
|
|
|
971
1016
|
const cfg = getApiConfig();
|
|
972
1017
|
if (!cfg)
|
|
973
1018
|
throw new Error("api-mode: not configured (HASNA_MEMENTOS_API_URL / HASNA_MEMENTOS_API_KEY)");
|
|
1019
|
+
assertRequestAllowedUnderTest(cfg.baseUrl);
|
|
974
1020
|
const url = `${cfg.baseUrl}${path.startsWith("/") ? path : `/${path}`}`;
|
|
975
1021
|
const hasBody = body !== undefined && body !== null;
|
|
976
1022
|
const timeout = process.env["HASNA_MEMENTOS_API_TIMEOUT"] || DEFAULT_TIMEOUT_S;
|
|
@@ -1037,13 +1083,13 @@ x-api-key: ${cfg.apiKey}
|
|
|
1037
1083
|
}
|
|
1038
1084
|
return { status, body: respBody };
|
|
1039
1085
|
}
|
|
1040
|
-
function apiJson(method, path, body) {
|
|
1086
|
+
function apiJson(method, path, body, options) {
|
|
1041
1087
|
const raw = apiRequestRaw(method, path, body);
|
|
1042
1088
|
if (raw.status >= 200 && raw.status < 300) {
|
|
1043
1089
|
const data = raw.body.trim() ? JSON.parse(raw.body) : undefined;
|
|
1044
1090
|
return { status: raw.status, data };
|
|
1045
1091
|
}
|
|
1046
|
-
if (raw.status === 404) {
|
|
1092
|
+
if (raw.status === 404 && options?.allow404) {
|
|
1047
1093
|
return { status: 404, data: undefined };
|
|
1048
1094
|
}
|
|
1049
1095
|
let msg = `mementos cloud ${method} ${path} \u2192 ${raw.status}`;
|
|
@@ -1075,8 +1121,19 @@ function toQuery(params) {
|
|
|
1075
1121
|
const s = sp.toString();
|
|
1076
1122
|
return s ? `?${s}` : "";
|
|
1077
1123
|
}
|
|
1078
|
-
var ApiRequestError, DEFAULT_TIMEOUT_S = "45";
|
|
1124
|
+
var API_URL_ENV_KEYS, API_KEY_ENV_KEYS, DATABASE_URL_ENV_KEYS, ALLOW_REMOTE_API_IN_TESTS_ENV = "MEMENTOS_ALLOW_REMOTE_API_IN_TESTS", DB_PATH_ENV_KEYS, MementosStoreConfigError, ApiRequestError, DEFAULT_TIMEOUT_S = "45";
|
|
1079
1125
|
var init_api_mode = __esm(() => {
|
|
1126
|
+
API_URL_ENV_KEYS = ["HASNA_MEMENTOS_API_URL", "MEMENTOS_API_URL"];
|
|
1127
|
+
API_KEY_ENV_KEYS = ["HASNA_MEMENTOS_API_KEY", "MEMENTOS_API_KEY"];
|
|
1128
|
+
DATABASE_URL_ENV_KEYS = ["HASNA_MEMENTOS_DATABASE_URL", "MEMENTOS_DATABASE_URL"];
|
|
1129
|
+
DB_PATH_ENV_KEYS = ["HASNA_MEMENTOS_DB_PATH", "MEMENTOS_DB_PATH"];
|
|
1130
|
+
MementosStoreConfigError = class MementosStoreConfigError extends Error {
|
|
1131
|
+
code = "MEMENTOS_STORE_CONFIG";
|
|
1132
|
+
constructor(message) {
|
|
1133
|
+
super(message);
|
|
1134
|
+
this.name = "MementosStoreConfigError";
|
|
1135
|
+
}
|
|
1136
|
+
};
|
|
1080
1137
|
ApiRequestError = class ApiRequestError extends Error {
|
|
1081
1138
|
status;
|
|
1082
1139
|
body;
|
|
@@ -49293,7 +49350,10 @@ function parseMemoryRow(row) {
|
|
|
49293
49350
|
}
|
|
49294
49351
|
function createMemory(input, dedupeMode = "merge", db) {
|
|
49295
49352
|
if (!db && isApiMode()) {
|
|
49296
|
-
const { data } = apiJson("POST", "/memories", { ...input, dedupe: dedupeMode });
|
|
49353
|
+
const { status, data } = apiJson("POST", "/memories", { ...input, dedupe: dedupeMode });
|
|
49354
|
+
if (!data || !data.id) {
|
|
49355
|
+
throw new ApiRequestError(`mementos cloud POST /memories \u2192 ${status} but no memory was returned; the write did not persist (key: ${input.key})`, status, "");
|
|
49356
|
+
}
|
|
49297
49357
|
return data;
|
|
49298
49358
|
}
|
|
49299
49359
|
const d = db || getDatabase();
|
|
@@ -49450,7 +49510,7 @@ function listMemoriesByKey(key, db) {
|
|
|
49450
49510
|
}
|
|
49451
49511
|
function getMemory(id, db) {
|
|
49452
49512
|
if (!db && isApiMode()) {
|
|
49453
|
-
const { status, data } = apiJson("GET", `/memories/${encodeURIComponent(id)}
|
|
49513
|
+
const { status, data } = apiJson("GET", `/memories/${encodeURIComponent(id)}`, undefined, { allow404: true });
|
|
49454
49514
|
return status === 404 ? null : data ?? null;
|
|
49455
49515
|
}
|
|
49456
49516
|
const d = db || getDatabase();
|
|
@@ -49674,7 +49734,7 @@ function listMemories(filter, db) {
|
|
|
49674
49734
|
}
|
|
49675
49735
|
function updateMemory(id, input, db) {
|
|
49676
49736
|
if (!db && isApiMode()) {
|
|
49677
|
-
const { status, data } = apiJson("PATCH", `/memories/${encodeURIComponent(id)}`, input);
|
|
49737
|
+
const { status, data } = apiJson("PATCH", `/memories/${encodeURIComponent(id)}`, input, { allow404: true });
|
|
49678
49738
|
if (status === 404)
|
|
49679
49739
|
throw new MemoryNotFoundError(id);
|
|
49680
49740
|
return data;
|
|
@@ -49782,7 +49842,7 @@ function updateMemory(id, input, db) {
|
|
|
49782
49842
|
}
|
|
49783
49843
|
function deleteMemory(id, db) {
|
|
49784
49844
|
if (!db && isApiMode()) {
|
|
49785
|
-
const { status } = apiJson("DELETE", `/memories/${encodeURIComponent(id)}
|
|
49845
|
+
const { status } = apiJson("DELETE", `/memories/${encodeURIComponent(id)}`, undefined, { allow404: true });
|
|
49786
49846
|
return status !== 404;
|
|
49787
49847
|
}
|
|
49788
49848
|
const d = db || getDatabase();
|
|
@@ -49952,7 +50012,7 @@ function registerAgent(name, sessionId, description, role, projectId, db) {
|
|
|
49952
50012
|
}
|
|
49953
50013
|
function getAgent(idOrName, db) {
|
|
49954
50014
|
if (!db && isApiMode()) {
|
|
49955
|
-
const { status, data } = apiJson("GET", `/agents/${encodeURIComponent(idOrName)}
|
|
50015
|
+
const { status, data } = apiJson("GET", `/agents/${encodeURIComponent(idOrName)}`, undefined, { allow404: true });
|
|
49956
50016
|
if (status === 404 || !data)
|
|
49957
50017
|
return null;
|
|
49958
50018
|
return data;
|
|
@@ -50005,7 +50065,7 @@ function listAgentsByProject(projectId, db) {
|
|
|
50005
50065
|
}
|
|
50006
50066
|
function updateAgent(id, updates, db) {
|
|
50007
50067
|
if (!db && isApiMode()) {
|
|
50008
|
-
const { status, data } = apiJson("PATCH", `/agents/${encodeURIComponent(id)}`, updates);
|
|
50068
|
+
const { status, data } = apiJson("PATCH", `/agents/${encodeURIComponent(id)}`, updates, { allow404: true });
|
|
50009
50069
|
if (status === 404 || !data)
|
|
50010
50070
|
return null;
|
|
50011
50071
|
return data;
|
|
@@ -50112,7 +50172,7 @@ function acquireLock(agentId, resourceType, resourceId, lockType = "exclusive",
|
|
|
50112
50172
|
}
|
|
50113
50173
|
function releaseLock(lockId, agentId, db) {
|
|
50114
50174
|
if (!db && isApiMode()) {
|
|
50115
|
-
const { status } = apiJson("DELETE", `/locks/${encodeURIComponent(lockId)}`, { agent_id: agentId });
|
|
50175
|
+
const { status } = apiJson("DELETE", `/locks/${encodeURIComponent(lockId)}`, { agent_id: agentId }, { allow404: true });
|
|
50116
50176
|
return status !== 404;
|
|
50117
50177
|
}
|
|
50118
50178
|
const d = db || getDatabase();
|
|
@@ -50321,7 +50381,7 @@ function registerProject(name, path, description, memoryPrefix, db) {
|
|
|
50321
50381
|
}
|
|
50322
50382
|
function getProject(idOrPath, db) {
|
|
50323
50383
|
if (!db && isApiMode()) {
|
|
50324
|
-
const { status, data } = apiJson("GET", `/projects/${encodeURIComponent(idOrPath)}
|
|
50384
|
+
const { status, data } = apiJson("GET", `/projects/${encodeURIComponent(idOrPath)}`, undefined, { allow404: true });
|
|
50325
50385
|
if (status === 404 || !data)
|
|
50326
50386
|
return null;
|
|
50327
50387
|
return data;
|
|
@@ -50514,7 +50574,7 @@ function createEntity(input, db) {
|
|
|
50514
50574
|
}
|
|
50515
50575
|
function getEntity(id, db) {
|
|
50516
50576
|
if (!db && isApiMode()) {
|
|
50517
|
-
const { status, data } = apiJson("GET", `/entities/${encodeURIComponent(id)}
|
|
50577
|
+
const { status, data } = apiJson("GET", `/entities/${encodeURIComponent(id)}`, undefined, { allow404: true });
|
|
50518
50578
|
if (status === 404 || !data)
|
|
50519
50579
|
throw new EntityNotFoundError(id);
|
|
50520
50580
|
return data;
|
|
@@ -50597,7 +50657,7 @@ function listEntities(filter = {}, db) {
|
|
|
50597
50657
|
}
|
|
50598
50658
|
function updateEntity(id, input, db) {
|
|
50599
50659
|
if (!db && isApiMode()) {
|
|
50600
|
-
const { status, data } = apiJson("PATCH", `/entities/${encodeURIComponent(id)}`, input);
|
|
50660
|
+
const { status, data } = apiJson("PATCH", `/entities/${encodeURIComponent(id)}`, input, { allow404: true });
|
|
50601
50661
|
if (status === 404 || !data)
|
|
50602
50662
|
throw new EntityNotFoundError(id);
|
|
50603
50663
|
return data;
|
|
@@ -50630,7 +50690,7 @@ function updateEntity(id, input, db) {
|
|
|
50630
50690
|
}
|
|
50631
50691
|
function deleteEntity(id, db) {
|
|
50632
50692
|
if (!db && isApiMode()) {
|
|
50633
|
-
const { status } = apiJson("DELETE", `/entities/${encodeURIComponent(id)}
|
|
50693
|
+
const { status } = apiJson("DELETE", `/entities/${encodeURIComponent(id)}`, undefined, { allow404: true });
|
|
50634
50694
|
if (status === 404)
|
|
50635
50695
|
throw new EntityNotFoundError(id);
|
|
50636
50696
|
return;
|
|
@@ -52691,7 +52751,7 @@ function createRelation(input, db) {
|
|
|
52691
52751
|
}
|
|
52692
52752
|
function getRelation(id, db) {
|
|
52693
52753
|
if (!db && isApiMode()) {
|
|
52694
|
-
const { status, data } = apiJson("GET", `/relations/${encodeURIComponent(id)}
|
|
52754
|
+
const { status, data } = apiJson("GET", `/relations/${encodeURIComponent(id)}`, undefined, { allow404: true });
|
|
52695
52755
|
if (status === 404 || !data)
|
|
52696
52756
|
throw new Error(`Relation not found: ${id}`);
|
|
52697
52757
|
return data;
|
|
@@ -52736,7 +52796,7 @@ function listRelations(filter, db) {
|
|
|
52736
52796
|
}
|
|
52737
52797
|
function deleteRelation(id, db) {
|
|
52738
52798
|
if (!db && isApiMode()) {
|
|
52739
|
-
const { status } = apiJson("DELETE", `/relations/${encodeURIComponent(id)}
|
|
52799
|
+
const { status } = apiJson("DELETE", `/relations/${encodeURIComponent(id)}`, undefined, { allow404: true });
|
|
52740
52800
|
if (status === 404)
|
|
52741
52801
|
throw new Error(`Relation not found: ${id}`);
|
|
52742
52802
|
return;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface EnumViolation {
|
|
2
|
+
field: string;
|
|
3
|
+
value: string;
|
|
4
|
+
allowed: readonly string[];
|
|
5
|
+
}
|
|
6
|
+
/** Human-readable, actionable rejection message naming the field and the accepted set. */
|
|
7
|
+
export declare function formatEnumViolation(v: EnumViolation): string;
|
|
8
|
+
/**
|
|
9
|
+
* Validate one enum-constrained field. Returns the violation, or `null` when
|
|
10
|
+
* the value is acceptable. `undefined`/`null` are acceptable — they mean "leave
|
|
11
|
+
* the column at its default", which is a separate concern from a bad value.
|
|
12
|
+
*/
|
|
13
|
+
export declare function validateEnumField(field: string, value: unknown): EnumViolation | null;
|
|
14
|
+
/**
|
|
15
|
+
* Validate every enum-constrained field present on a create/update payload.
|
|
16
|
+
* Returns the first violation so the caller can fail fast with one clear
|
|
17
|
+
* message, or `null` when the payload is clean.
|
|
18
|
+
*/
|
|
19
|
+
export declare function validateMemoryEnums(input: Record<string, unknown>): EnumViolation | null;
|
|
20
|
+
//# sourceMappingURL=enum-validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enum-validation.d.ts","sourceRoot":"","sources":["../../src/lib/enum-validation.ts"],"names":[],"mappings":"AA6BA,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CAC5B;AAED,0FAA0F;AAC1F,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,aAAa,GAAG,MAAM,CAE5D;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,aAAa,GAAG,IAAI,CAMrF;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,aAAa,GAAG,IAAI,CAOxF"}
|