@hasna/mementos 0.14.83 → 0.14.85
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/Dockerfile.package +0 -1
- package/README.md +13 -10
- package/dashboard/bun.lock +519 -0
- package/dist/cli/commands/info-history.d.ts.map +1 -1
- package/dist/cli/commands/info-stale.d.ts.map +1 -1
- package/dist/cli/commands/io-export.d.ts.map +1 -1
- package/dist/cli/commands/memory-cmd-list.d.ts.map +1 -1
- package/dist/cli/commands/storage.d.ts.map +1 -1
- package/dist/cli/helpers.d.ts +31 -0
- package/dist/cli/helpers.d.ts.map +1 -1
- package/dist/cli/index.js +3781 -1512
- package/dist/db/analytics.d.ts +13 -0
- package/dist/db/analytics.d.ts.map +1 -1
- package/dist/db/api-mode.d.ts +5 -1
- package/dist/db/api-mode.d.ts.map +1 -1
- package/dist/db/database.d.ts.map +1 -1
- package/dist/db/memories.d.ts +59 -3
- package/dist/db/memories.d.ts.map +1 -1
- package/dist/db/store-backend.d.ts +3 -3
- package/dist/db/store-backend.d.ts.map +1 -1
- package/dist/diagnostics/historical-project-registration-receipt.d.ts +39 -0
- package/dist/diagnostics/historical-project-registration-receipt.d.ts.map +1 -0
- package/dist/diagnostics/historical-project-registration-receipt.js +1656 -0
- package/dist/generated/storage-kit/backend.d.ts +20 -0
- package/dist/generated/storage-kit/backend.d.ts.map +1 -0
- package/dist/generated/storage-kit/index.d.ts +2 -2
- package/dist/generated/storage-kit/index.d.ts.map +1 -1
- package/dist/generated/storage-kit/migrations.d.ts.map +1 -1
- package/dist/generated/storage-kit/own.d.ts +11 -0
- package/dist/generated/storage-kit/own.d.ts.map +1 -0
- package/dist/generated/storage-kit/pool.d.ts +7 -6
- package/dist/generated/storage-kit/pool.d.ts.map +1 -1
- package/dist/generated/storage-kit/tls.d.ts +30 -3
- package/dist/generated/storage-kit/tls.d.ts.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3628 -1453
- package/dist/lib/gatherer.d.ts.map +1 -1
- package/dist/lib/retired-storage-mode.d.ts +11 -0
- package/dist/lib/retired-storage-mode.d.ts.map +1 -0
- package/dist/lib/storage-sync.d.ts +2 -2
- package/dist/lib/storage-sync.d.ts.map +1 -1
- package/dist/mcp/index.js +3684 -1492
- package/dist/mcp/tools/memory-io.d.ts.map +1 -1
- package/dist/mcp/tools/project-tools.d.ts.map +1 -1
- package/dist/project-registration/authority.d.ts.map +1 -1
- package/dist/project-registration/historical-receipt.d.ts +38 -0
- package/dist/project-registration/historical-receipt.d.ts.map +1 -0
- package/dist/project-registration/identity.d.ts.map +1 -1
- package/dist/project-registration/index.d.ts +1 -1
- package/dist/project-registration/index.d.ts.map +1 -1
- package/dist/project-registration/types.d.ts +26 -0
- package/dist/project-registration/types.d.ts.map +1 -1
- package/dist/project-registration.js +361 -162
- package/dist/sdk/index.d.ts +7 -0
- package/dist/sdk/index.d.ts.map +1 -1
- package/dist/sdk/index.js +122 -30
- package/dist/server/index.js +3698 -1431
- package/dist/storage.d.ts +18 -50
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +151 -122
- package/dist/test-support/memories-page-stub-server.d.ts +2 -0
- package/dist/test-support/memories-page-stub-server.d.ts.map +1 -0
- package/dist/test-support/memories-page-stub.d.ts +28 -0
- package/dist/test-support/memories-page-stub.d.ts.map +1 -0
- package/dist/test-support/store-isolation.d.ts +5 -0
- package/dist/test-support/store-isolation.d.ts.map +1 -1
- package/docker-entrypoint.sh +4 -5
- package/hasna.contract.json +2 -2
- package/package.json +7 -6
- package/dist/generated/storage-kit/mode.d.ts +0 -48
- package/dist/generated/storage-kit/mode.d.ts.map +0 -1
|
@@ -46,24 +46,109 @@ var __export = (target, all) => {
|
|
|
46
46
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
47
47
|
var __require = import.meta.require;
|
|
48
48
|
|
|
49
|
-
// src/generated/storage-kit/
|
|
50
|
-
function
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if (
|
|
55
|
-
return
|
|
56
|
-
if (
|
|
57
|
-
return
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
49
|
+
// src/generated/storage-kit/own.ts
|
|
50
|
+
function ownProp(source, key) {
|
|
51
|
+
if (source === null || source === undefined)
|
|
52
|
+
return;
|
|
53
|
+
const kind = typeof source;
|
|
54
|
+
if (kind !== "object" && kind !== "function")
|
|
55
|
+
return;
|
|
56
|
+
if (!Object.hasOwn(source, key))
|
|
57
|
+
return;
|
|
58
|
+
return source[key];
|
|
59
|
+
}
|
|
60
|
+
function ownString(source, key) {
|
|
61
|
+
const value = ownProp(source, key);
|
|
62
|
+
return typeof value === "string" ? value : undefined;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// src/generated/storage-kit/backend.ts
|
|
66
|
+
function envToken(name) {
|
|
67
|
+
return name.toUpperCase().replace(/-/g, "_");
|
|
68
|
+
}
|
|
69
|
+
function serverDataBackendEnvKeys(name) {
|
|
70
|
+
const token = envToken(name);
|
|
71
|
+
return {
|
|
72
|
+
databaseUrlKeys: [`HASNA_${token}_DATABASE_URL`, `${token}_DATABASE_URL`]
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function legacyModeKeys(name) {
|
|
76
|
+
const token = envToken(name);
|
|
77
|
+
return [
|
|
78
|
+
`HASNA_${token}_STORAGE_MODE`,
|
|
79
|
+
`HASNA_${token}_MODE`,
|
|
80
|
+
`${token}_STORAGE_MODE`,
|
|
81
|
+
`${token}_MODE`
|
|
82
|
+
];
|
|
83
|
+
}
|
|
84
|
+
function firstEnv(env, keys) {
|
|
85
|
+
for (const key of keys) {
|
|
86
|
+
const value = ownString(env, key)?.trim();
|
|
87
|
+
if (value)
|
|
88
|
+
return { key, value };
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
function firstDefinedEnvKey(env, keys) {
|
|
93
|
+
for (const key of keys) {
|
|
94
|
+
if (Object.hasOwn(env, key) && env[key] !== undefined)
|
|
95
|
+
return key;
|
|
96
|
+
}
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
function assertNoLegacyStorageMode(name, env = process.env) {
|
|
100
|
+
const legacyKey = firstDefinedEnvKey(env, legacyModeKeys(name));
|
|
101
|
+
if (!legacyKey)
|
|
102
|
+
return;
|
|
103
|
+
const canonicalDatabaseUrl = serverDataBackendEnvKeys(name).databaseUrlKeys[0];
|
|
104
|
+
throw new Error(`${legacyKey} was removed. Delete the storage-mode variable; ` + `set ${canonicalDatabaseUrl} to select the postgresql server backend, ` + `or leave it unset for sqlite.`);
|
|
105
|
+
}
|
|
106
|
+
function resolveServerDataBackend(name, env = process.env) {
|
|
107
|
+
assertNoLegacyStorageMode(name, env);
|
|
108
|
+
const databaseUrl = firstEnv(env, serverDataBackendEnvKeys(name).databaseUrlKeys);
|
|
109
|
+
if (!databaseUrl) {
|
|
110
|
+
return {
|
|
111
|
+
backend: "sqlite",
|
|
112
|
+
source: "default",
|
|
113
|
+
databaseUrlPresent: false,
|
|
114
|
+
databaseUrlSource: null
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
backend: "postgresql",
|
|
119
|
+
source: databaseUrl.key,
|
|
120
|
+
databaseUrlPresent: true,
|
|
121
|
+
databaseUrlSource: databaseUrl.key
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function resolveDatabaseUrl(name, env = process.env) {
|
|
125
|
+
assertNoLegacyStorageMode(name, env);
|
|
126
|
+
const hit = firstEnv(env, serverDataBackendEnvKeys(name).databaseUrlKeys);
|
|
127
|
+
return hit?.value ?? null;
|
|
128
|
+
}
|
|
129
|
+
var init_backend = () => {};
|
|
130
|
+
|
|
131
|
+
// src/lib/retired-storage-mode.ts
|
|
132
|
+
function firstDefinedEnvKey2(env, keys) {
|
|
133
|
+
for (const key of keys) {
|
|
134
|
+
if (Object.hasOwn(env, key) && env[key] !== undefined)
|
|
135
|
+
return key;
|
|
136
|
+
}
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
function assertNoLegacyStorageMode2(env = process.env) {
|
|
140
|
+
const legacyKey = firstDefinedEnvKey2(env, LEGACY_STORAGE_MODE_KEYS);
|
|
141
|
+
if (!legacyKey)
|
|
142
|
+
return;
|
|
143
|
+
throw new Error(`${legacyKey} was removed. Deployment modes no longer exist: delete the storage-mode variable. ` + `The client uses the local SQLite store, or the HTTP API selected by ` + `HASNA_MEMENTOS_API_URL + HASNA_MEMENTOS_API_KEY. ` + `On the server, set HASNA_MEMENTOS_DATABASE_URL to select the postgresql backend, ` + `or leave it unset for sqlite.`);
|
|
144
|
+
}
|
|
145
|
+
var LEGACY_STORAGE_MODE_KEYS;
|
|
146
|
+
var init_retired_storage_mode = __esm(() => {
|
|
147
|
+
LEGACY_STORAGE_MODE_KEYS = [
|
|
148
|
+
"HASNA_MEMENTOS_STORAGE_MODE",
|
|
149
|
+
"HASNA_MEMENTOS_MODE",
|
|
150
|
+
"MEMENTOS_STORAGE_MODE",
|
|
151
|
+
"MEMENTOS_MODE"
|
|
67
152
|
];
|
|
68
153
|
});
|
|
69
154
|
|
|
@@ -328,27 +413,6 @@ function readEnv(name) {
|
|
|
328
413
|
const value = process.env[name]?.trim();
|
|
329
414
|
return value ? value : null;
|
|
330
415
|
}
|
|
331
|
-
function warnDeprecatedStorageMode(alias) {
|
|
332
|
-
if (warnedDeprecatedModes.has(alias))
|
|
333
|
-
return;
|
|
334
|
-
warnedDeprecatedModes.add(alias);
|
|
335
|
-
process.emitWarning(`${MEMENTOS_STORAGE_ENV.mode}="${alias}" is deprecated; use "cloud". ` + `"${alias}" now maps to pure-remote cloud storage. The local<->remote ` + `sync path is deprecated and is not the fleet cutover path.`, { type: "DeprecationWarning", code: "MEMENTOS_STORAGE_MODE_ALIAS" });
|
|
336
|
-
}
|
|
337
|
-
function normalizeStorageMode2(value, source) {
|
|
338
|
-
if (!value || !value.trim())
|
|
339
|
-
return null;
|
|
340
|
-
let normalized;
|
|
341
|
-
try {
|
|
342
|
-
normalized = normalizeStorageMode(value);
|
|
343
|
-
} catch (error) {
|
|
344
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
345
|
-
throw new Error(`mementos: ${source}=${value} is not a valid mode. ${detail}`);
|
|
346
|
-
}
|
|
347
|
-
if (normalized.deprecatedAlias) {
|
|
348
|
-
warnDeprecatedStorageMode(normalized.deprecatedAlias);
|
|
349
|
-
}
|
|
350
|
-
return normalized.mode;
|
|
351
|
-
}
|
|
352
416
|
function readConfigFile() {
|
|
353
417
|
if (!existsSync(STORAGE_CONFIG_PATH)) {
|
|
354
418
|
return {};
|
|
@@ -384,19 +448,15 @@ function getStorageDatabaseUrl() {
|
|
|
384
448
|
function getStorageDatabaseEnvName() {
|
|
385
449
|
return getStorageEnvName("databaseUrl");
|
|
386
450
|
}
|
|
387
|
-
function
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
}
|
|
393
|
-
return null;
|
|
451
|
+
function getStorageBackend(env = process.env) {
|
|
452
|
+
return resolveServerDataBackend("mementos", env).backend;
|
|
453
|
+
}
|
|
454
|
+
function getStorageBackendDatabaseUrl(env = process.env) {
|
|
455
|
+
return resolveDatabaseUrl("mementos", env);
|
|
394
456
|
}
|
|
395
457
|
function getStorageConfig() {
|
|
458
|
+
assertNoLegacyStorageMode2();
|
|
396
459
|
const fileConfig = readConfigFile();
|
|
397
|
-
const modeOverride = getStorageModeOverride();
|
|
398
|
-
const envConnectionString = getConfiguredConnectionString();
|
|
399
|
-
const fileMode = normalizeStorageMode2(fileConfig.mode, `${STORAGE_CONFIG_PATH} "mode"`);
|
|
400
460
|
const merged = {
|
|
401
461
|
...DEFAULT_STORAGE_CONFIG,
|
|
402
462
|
...fileConfig,
|
|
@@ -407,19 +467,10 @@ function getStorageConfig() {
|
|
|
407
467
|
sync: {
|
|
408
468
|
...DEFAULT_STORAGE_CONFIG.sync,
|
|
409
469
|
...fileConfig.sync ?? {}
|
|
410
|
-
}
|
|
411
|
-
mode: fileMode ?? DEFAULT_STORAGE_CONFIG.mode
|
|
470
|
+
}
|
|
412
471
|
};
|
|
413
|
-
if (modeOverride) {
|
|
414
|
-
merged.mode = modeOverride;
|
|
415
|
-
} else if (envConnectionString && merged.mode === "local") {
|
|
416
|
-
merged.mode = "cloud";
|
|
417
|
-
}
|
|
418
472
|
return merged;
|
|
419
473
|
}
|
|
420
|
-
function getStorageMode() {
|
|
421
|
-
return getStorageConfig().mode;
|
|
422
|
-
}
|
|
423
474
|
function isSecretQueryParam(key) {
|
|
424
475
|
const normalized = key.trim().toLowerCase();
|
|
425
476
|
return SECRET_QUERY_PARAMS.has(normalized) || /(?:secret|token|password|passphrase|credential|api[_-]?key|apikey|private[_-]?key|auth|session)/i.test(normalized);
|
|
@@ -490,7 +541,7 @@ function storageEnvStatus(key) {
|
|
|
490
541
|
configured: readEnv(activeName) !== null
|
|
491
542
|
};
|
|
492
543
|
}
|
|
493
|
-
function
|
|
544
|
+
function postgresBackendStatus(config) {
|
|
494
545
|
const env = getStorageDatabaseEnv();
|
|
495
546
|
const envUrl = env ? readEnv(env.name) : null;
|
|
496
547
|
if (env && envUrl) {
|
|
@@ -528,12 +579,8 @@ function remoteDatabaseConfigStatus(config) {
|
|
|
528
579
|
rds_compatible: configured
|
|
529
580
|
};
|
|
530
581
|
}
|
|
531
|
-
function runtimeKindFor(mode) {
|
|
532
|
-
return mode === "cloud" ? "cloud-postgres" : "local-sqlite";
|
|
533
|
-
}
|
|
534
582
|
function getSafeStorageConfigSummary(config = getStorageConfig()) {
|
|
535
583
|
return {
|
|
536
|
-
mode: config.mode,
|
|
537
584
|
auto_sync_interval_minutes: config.auto_sync_interval_minutes,
|
|
538
585
|
feedback_endpoint_configured: config.feedback_endpoint.trim() !== "",
|
|
539
586
|
sync: { ...config.sync },
|
|
@@ -548,29 +595,30 @@ function getSafeStorageConfigSummary(config = getStorageConfig()) {
|
|
|
548
595
|
};
|
|
549
596
|
}
|
|
550
597
|
function getStorageStatus() {
|
|
598
|
+
assertNoLegacyStorageMode2();
|
|
551
599
|
const config = getStorageConfig();
|
|
552
|
-
const
|
|
553
|
-
const
|
|
554
|
-
const
|
|
600
|
+
const backend = getStorageBackend();
|
|
601
|
+
const postgresRequested = backend === "postgresql";
|
|
602
|
+
const postgres = postgresBackendStatus(config);
|
|
555
603
|
const issues = [];
|
|
556
604
|
const warnings = [];
|
|
557
|
-
if (
|
|
558
|
-
issues.push(`
|
|
605
|
+
if (postgresRequested && !postgres.configured) {
|
|
606
|
+
issues.push(`PostgreSQL is selected (HASNA_MEMENTOS_DATABASE_URL) but not configured. ${postgres.issues.join(" ")}`);
|
|
559
607
|
}
|
|
560
|
-
if (
|
|
561
|
-
warnings.push(`
|
|
608
|
+
if (!postgresRequested && postgres.issues.length > 0 && postgres.source !== "none") {
|
|
609
|
+
warnings.push(`PostgreSQL configuration is present but invalid; the postgresql backend stays disabled until fixed. ${postgres.issues.join(" ")}`);
|
|
562
610
|
}
|
|
563
|
-
if (
|
|
564
|
-
warnings.push("
|
|
611
|
+
if (!postgresRequested && postgres.configured) {
|
|
612
|
+
warnings.push("PostgreSQL configuration is present, but the selected backend is sqlite; the postgresql backend stays disabled until HASNA_MEMENTOS_DATABASE_URL is set.");
|
|
565
613
|
}
|
|
566
|
-
const failClosed =
|
|
614
|
+
const failClosed = postgresRequested && !postgres.configured;
|
|
567
615
|
const runtime = {
|
|
568
|
-
contract: "mementos-
|
|
569
|
-
kind:
|
|
616
|
+
contract: "mementos-storage-runtime-v1",
|
|
617
|
+
kind: backend,
|
|
570
618
|
fail_closed: failClosed,
|
|
571
619
|
local: {
|
|
572
620
|
adapter: "sqlite",
|
|
573
|
-
primary_runtime:
|
|
621
|
+
primary_runtime: backend === "sqlite",
|
|
574
622
|
data_dir: LOCAL_DATA_DIR,
|
|
575
623
|
config_path: STORAGE_CONFIG_PATH,
|
|
576
624
|
local_file_sync: {
|
|
@@ -578,34 +626,22 @@ function getStorageStatus() {
|
|
|
578
626
|
reason: "Mementos stores local state in SQLite; it does not sync raw local data files."
|
|
579
627
|
}
|
|
580
628
|
},
|
|
581
|
-
|
|
629
|
+
backend: {
|
|
582
630
|
adapter: "postgres",
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
rds_compatible: remote.rds_compatible,
|
|
631
|
+
requested: postgresRequested,
|
|
632
|
+
configured: postgres.configured,
|
|
633
|
+
source: postgres.source,
|
|
634
|
+
env_name: postgres.env_name,
|
|
635
|
+
redacted_url: postgres.redacted_url,
|
|
636
|
+
rds_compatible: postgres.rds_compatible,
|
|
590
637
|
fail_closed: failClosed,
|
|
591
|
-
missing:
|
|
592
|
-
},
|
|
593
|
-
object_storage: {
|
|
594
|
-
s3: {
|
|
595
|
-
supported: false,
|
|
596
|
-
mutation_allowed: false,
|
|
597
|
-
reason: "No S3 object-storage adapter is part of this runtime."
|
|
598
|
-
},
|
|
599
|
-
aws: {
|
|
600
|
-
mutation_allowed: false,
|
|
601
|
-
reason: "Diagnostics do not store sensitive values, change AWS resources, deploy, or mutate production data."
|
|
602
|
-
}
|
|
638
|
+
missing: postgres.missing
|
|
603
639
|
},
|
|
604
640
|
migrations: {
|
|
605
641
|
target: "postgres-rds-compatible",
|
|
606
642
|
command: "mementos storage migrate",
|
|
607
643
|
dry_run_command: "mementos storage migrate --dry-run",
|
|
608
|
-
configured:
|
|
644
|
+
configured: postgres.configured,
|
|
609
645
|
mutates_remote_on_apply: true,
|
|
610
646
|
requires_approval_for_live_run: true
|
|
611
647
|
}
|
|
@@ -613,21 +649,19 @@ function getStorageStatus() {
|
|
|
613
649
|
return {
|
|
614
650
|
ok: issues.length === 0,
|
|
615
651
|
service: "mementos",
|
|
616
|
-
|
|
617
|
-
local_default:
|
|
618
|
-
remote_enabled: remoteRequested,
|
|
652
|
+
backend,
|
|
653
|
+
local_default: backend === "sqlite",
|
|
619
654
|
runtime,
|
|
620
655
|
database: {
|
|
621
|
-
configured:
|
|
622
|
-
redacted_url:
|
|
623
|
-
source:
|
|
624
|
-
env_name:
|
|
625
|
-
rds_compatible:
|
|
656
|
+
configured: postgres.configured,
|
|
657
|
+
redacted_url: postgres.redacted_url,
|
|
658
|
+
source: postgres.source,
|
|
659
|
+
env_name: postgres.env_name,
|
|
660
|
+
rds_compatible: postgres.rds_compatible
|
|
626
661
|
},
|
|
627
662
|
tables: MEMENTOS_STORAGE_TABLES,
|
|
628
663
|
env: {
|
|
629
|
-
databaseUrl: storageEnvStatus("databaseUrl")
|
|
630
|
-
mode: storageEnvStatus("mode")
|
|
664
|
+
databaseUrl: storageEnvStatus("databaseUrl")
|
|
631
665
|
},
|
|
632
666
|
issues,
|
|
633
667
|
warnings,
|
|
@@ -643,8 +677,9 @@ function getConfiguredConnectionString() {
|
|
|
643
677
|
return getStorageDatabaseUrl() ?? undefined;
|
|
644
678
|
}
|
|
645
679
|
function getStorageConnectionString(dbName = "mementos") {
|
|
680
|
+
assertNoLegacyStorageMode2();
|
|
646
681
|
if (!isServerContext()) {
|
|
647
|
-
throw new Error("Refusing to construct an RDS Postgres DSN outside the mementos-serve server. " + "The raw database DSN is NEVER distributed to client machines. " + "Clients must use the
|
|
682
|
+
throw new Error("Refusing to construct an RDS Postgres DSN outside the mementos-serve server. " + "The raw database DSN is NEVER distributed to client machines. " + "Clients must use the HTTP API: set HASNA_MEMENTOS_API_URL and " + "HASNA_MEMENTOS_API_KEY (and unset HASNA_MEMENTOS_DATABASE_URL).");
|
|
648
683
|
}
|
|
649
684
|
const envConnectionString = getConfiguredConnectionString();
|
|
650
685
|
if (envConnectionString) {
|
|
@@ -815,7 +850,7 @@ function resetAllSyncMeta(db) {
|
|
|
815
850
|
ensureSyncMetaTable(db);
|
|
816
851
|
db.run("DELETE FROM _sync_meta");
|
|
817
852
|
}
|
|
818
|
-
var _serverContext = false, PgSyncPool, MEMENTOS_STORAGE_TABLES, STORAGE_TABLES, MEMENTOS_STORAGE_ENV, MEMENTOS_STORAGE_FALLBACK_ENV, LOCAL_DATA_DIR, DEFAULT_STORAGE_CONFIG, STORAGE_CONFIG_DIR, STORAGE_CONFIG_PATH, DATABASE_ENV_NAMES,
|
|
853
|
+
var _serverContext = false, PgSyncPool, MEMENTOS_STORAGE_TABLES, STORAGE_TABLES, MEMENTOS_STORAGE_ENV, MEMENTOS_STORAGE_FALLBACK_ENV, LOCAL_DATA_DIR, DEFAULT_STORAGE_CONFIG, STORAGE_CONFIG_DIR, STORAGE_CONFIG_PATH, DATABASE_ENV_NAMES, SECRET_QUERY_PARAMS, getMementosStorageStatus, SYNC_EXCLUDED_TABLE_PATTERNS, SYNC_META_TABLE_SQL = `
|
|
819
854
|
CREATE TABLE IF NOT EXISTS _sync_meta (
|
|
820
855
|
table_name TEXT PRIMARY KEY,
|
|
821
856
|
last_synced_at TEXT,
|
|
@@ -823,7 +858,8 @@ CREATE TABLE IF NOT EXISTS _sync_meta (
|
|
|
823
858
|
direction TEXT DEFAULT 'push'
|
|
824
859
|
)`;
|
|
825
860
|
var init_storage = __esm(() => {
|
|
826
|
-
|
|
861
|
+
init_backend();
|
|
862
|
+
init_retired_storage_mode();
|
|
827
863
|
PgSyncPool = class PgSyncPool {
|
|
828
864
|
worker;
|
|
829
865
|
status;
|
|
@@ -910,12 +946,10 @@ var init_storage = __esm(() => {
|
|
|
910
946
|
];
|
|
911
947
|
STORAGE_TABLES = MEMENTOS_STORAGE_TABLES;
|
|
912
948
|
MEMENTOS_STORAGE_ENV = {
|
|
913
|
-
databaseUrl: "HASNA_MEMENTOS_DATABASE_URL"
|
|
914
|
-
mode: "HASNA_MEMENTOS_STORAGE_MODE"
|
|
949
|
+
databaseUrl: "HASNA_MEMENTOS_DATABASE_URL"
|
|
915
950
|
};
|
|
916
951
|
MEMENTOS_STORAGE_FALLBACK_ENV = {
|
|
917
|
-
databaseUrl: "MEMENTOS_DATABASE_URL"
|
|
918
|
-
mode: "MEMENTOS_STORAGE_MODE"
|
|
952
|
+
databaseUrl: "MEMENTOS_DATABASE_URL"
|
|
919
953
|
};
|
|
920
954
|
LOCAL_DATA_DIR = join(homedir(), ".hasna", "mementos");
|
|
921
955
|
DEFAULT_STORAGE_CONFIG = {
|
|
@@ -926,7 +960,6 @@ var init_storage = __esm(() => {
|
|
|
926
960
|
password_env: "MEMENTOS_DATABASE_PASSWORD",
|
|
927
961
|
ssl: true
|
|
928
962
|
},
|
|
929
|
-
mode: "local",
|
|
930
963
|
auto_sync_interval_minutes: 0,
|
|
931
964
|
feedback_endpoint: "",
|
|
932
965
|
sync: {
|
|
@@ -939,11 +972,6 @@ var init_storage = __esm(() => {
|
|
|
939
972
|
{ name: MEMENTOS_STORAGE_ENV.databaseUrl, deprecated: false },
|
|
940
973
|
{ name: MEMENTOS_STORAGE_FALLBACK_ENV.databaseUrl, deprecated: false }
|
|
941
974
|
];
|
|
942
|
-
MODE_ENV_NAMES = [
|
|
943
|
-
{ name: MEMENTOS_STORAGE_ENV.mode, deprecated: false },
|
|
944
|
-
{ name: MEMENTOS_STORAGE_FALLBACK_ENV.mode, deprecated: false }
|
|
945
|
-
];
|
|
946
|
-
warnedDeprecatedModes = new Set;
|
|
947
975
|
SECRET_QUERY_PARAMS = new Set([
|
|
948
976
|
"password",
|
|
949
977
|
"pass",
|
|
@@ -968,7 +996,7 @@ import { tmpdir } from "os";
|
|
|
968
996
|
import { join as join2 } from "path";
|
|
969
997
|
import { writeFileSync as writeFileSync2, unlinkSync } from "fs";
|
|
970
998
|
import { randomUUID } from "crypto";
|
|
971
|
-
function
|
|
999
|
+
function firstEnv2(keys) {
|
|
972
1000
|
for (const k of keys) {
|
|
973
1001
|
const v = process.env[k]?.trim();
|
|
974
1002
|
if (v)
|
|
@@ -984,7 +1012,7 @@ function firstEnvKey(keys) {
|
|
|
984
1012
|
return null;
|
|
985
1013
|
}
|
|
986
1014
|
function hasDatabaseUrl() {
|
|
987
|
-
return Boolean(
|
|
1015
|
+
return Boolean(firstEnv2(DATABASE_URL_ENV_KEYS));
|
|
988
1016
|
}
|
|
989
1017
|
function isLoopbackHost(rawHost) {
|
|
990
1018
|
const host = rawHost.replace(/^\[/, "").replace(/\]$/, "").toLowerCase();
|
|
@@ -1015,7 +1043,8 @@ function normalizeBase(raw) {
|
|
|
1015
1043
|
return base;
|
|
1016
1044
|
return `${base}/v1`;
|
|
1017
1045
|
}
|
|
1018
|
-
function assertUnambiguousStoreEnv() {
|
|
1046
|
+
function assertUnambiguousStoreEnv(env = process.env) {
|
|
1047
|
+
assertNoLegacyStorageMode2(env);
|
|
1019
1048
|
if (firstEnvKey(DB_PATH_ENV_KEYS))
|
|
1020
1049
|
return;
|
|
1021
1050
|
if (hasDatabaseUrl())
|
|
@@ -1033,8 +1062,8 @@ function getApiConfig() {
|
|
|
1033
1062
|
assertUnambiguousStoreEnv();
|
|
1034
1063
|
if (firstEnvKey(DB_PATH_ENV_KEYS))
|
|
1035
1064
|
return null;
|
|
1036
|
-
const rawBase =
|
|
1037
|
-
const apiKey =
|
|
1065
|
+
const rawBase = firstEnv2(API_URL_ENV_KEYS);
|
|
1066
|
+
const apiKey = firstEnv2(API_KEY_ENV_KEYS);
|
|
1038
1067
|
if (!rawBase || !apiKey)
|
|
1039
1068
|
return null;
|
|
1040
1069
|
return { baseUrl: normalizeBase(rawBase), apiKey };
|
|
@@ -1118,7 +1147,16 @@ x-api-key: ${cfg.apiKey}
|
|
|
1118
1147
|
function apiJson(method, path, body, options) {
|
|
1119
1148
|
const raw = apiRequestRaw(method, path, body);
|
|
1120
1149
|
if (raw.status >= 200 && raw.status < 300) {
|
|
1121
|
-
|
|
1150
|
+
let data;
|
|
1151
|
+
if (raw.body.trim()) {
|
|
1152
|
+
try {
|
|
1153
|
+
data = JSON.parse(raw.body);
|
|
1154
|
+
} catch (e) {
|
|
1155
|
+
throw new ApiRequestError(`mementos cloud ${method} ${path} returned status ${raw.status} with a body that is not valid JSON (${e instanceof Error ? e.message : String(e)}) \u2014 the response is truncated or the server is unhealthy`, raw.status, raw.body.slice(0, 500));
|
|
1156
|
+
}
|
|
1157
|
+
} else {
|
|
1158
|
+
data = undefined;
|
|
1159
|
+
}
|
|
1122
1160
|
return { status: raw.status, data };
|
|
1123
1161
|
}
|
|
1124
1162
|
if (raw.status === 404 && options?.allow404) {
|
|
@@ -1155,6 +1193,7 @@ function toQuery(params) {
|
|
|
1155
1193
|
}
|
|
1156
1194
|
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";
|
|
1157
1195
|
var init_api_mode = __esm(() => {
|
|
1196
|
+
init_retired_storage_mode();
|
|
1158
1197
|
API_URL_ENV_KEYS = ["HASNA_MEMENTOS_API_URL", "MEMENTOS_API_URL"];
|
|
1159
1198
|
API_KEY_ENV_KEYS = ["HASNA_MEMENTOS_API_KEY", "MEMENTOS_API_KEY"];
|
|
1160
1199
|
DATABASE_URL_ENV_KEYS = ["HASNA_MEMENTOS_DATABASE_URL", "MEMENTOS_DATABASE_URL"];
|
|
@@ -2657,7 +2696,7 @@ function getCloudDatabase() {
|
|
|
2657
2696
|
if (_pg)
|
|
2658
2697
|
return _pg;
|
|
2659
2698
|
if (!isServerContext()) {
|
|
2660
|
-
throw new Error("Direct
|
|
2699
|
+
throw new Error("Direct PostgreSQL storage is server-only. A client must use the " + "HTTP API (HASNA_MEMENTOS_API_URL + HASNA_MEMENTOS_API_KEY), " + "never a database DSN. Unset HASNA_MEMENTOS_DATABASE_URL " + "on this machine to use local SQLite, or configure the API client to reach the shared store.");
|
|
2661
2700
|
}
|
|
2662
2701
|
const connectionString = getStorageConnectionString();
|
|
2663
2702
|
_pg = new PgAdapter(connectionString);
|
|
@@ -2670,7 +2709,7 @@ function getDatabase(dbPath) {
|
|
|
2670
2709
|
if (isApiMode()) {
|
|
2671
2710
|
throw new Error("mementos is in API mode (HASNA_MEMENTOS_API_URL + HASNA_MEMENTOS_API_KEY set) " + "but this operation tried to open a local SQLite database. That would create a " + "split-brain local island instead of using the shared cloud store. This op must " + "route through the API client (src/db/api-mode.ts); if it needs a server endpoint " + "that does not exist yet, add it to src/server and redeploy. To use local storage " + "instead, unset HASNA_MEMENTOS_API_URL / HASNA_MEMENTOS_API_KEY.");
|
|
2672
2711
|
}
|
|
2673
|
-
if (
|
|
2712
|
+
if (getStorageBackend() === "postgresql") {
|
|
2674
2713
|
return getCloudDatabase();
|
|
2675
2714
|
}
|
|
2676
2715
|
if (process.env["NODE_ENV"] === "test") {
|
|
@@ -2856,6 +2895,90 @@ class MementosProjectRegistrationError extends Error {
|
|
|
2856
2895
|
}
|
|
2857
2896
|
}
|
|
2858
2897
|
|
|
2898
|
+
// src/project-registration/historical-receipt.ts
|
|
2899
|
+
var ROOT_TENANT_ID = "adfd95c7-ee8b-52cb-ae47-4ae65dae3313";
|
|
2900
|
+
var FLEET_RESOURCES_HISTORICAL_PROJECT_REGISTRATION = {
|
|
2901
|
+
authority: "mementos",
|
|
2902
|
+
authority_route: "mementos.project-registration.v1",
|
|
2903
|
+
package_version: "1.0.0-rc.3",
|
|
2904
|
+
authority_id: "mementos",
|
|
2905
|
+
tenant_id: "default",
|
|
2906
|
+
corpus_id: "default",
|
|
2907
|
+
operation_id: "op-cli-register-full",
|
|
2908
|
+
step_id: "mementos_project",
|
|
2909
|
+
resource_kind: "project",
|
|
2910
|
+
direction: "forward",
|
|
2911
|
+
target_selector: "wks_005285827590a93b70e5",
|
|
2912
|
+
target_id: "mm_project_f75606ef14e51fb577a15882ba0ab8ed333b2c29",
|
|
2913
|
+
idempotency_key: "prk_786cdc6babddeae1be8d5078d0f75322d1ea1010ac5d8a50",
|
|
2914
|
+
receipt_id: "mmpr_a647f9908a33bb64bf137f584202590f91b72a33",
|
|
2915
|
+
request_digest: "8eb0e3fee26fdb7edb8a444aeddaaedb79a84a5a36e742ae03616f62b19f5c22",
|
|
2916
|
+
precondition_digest: "0b6c22fcf75d835f9f27308b1a9d40e8902e48a97da370fd56df5acacfcd968e",
|
|
2917
|
+
result_revision: "2026-08-09T13:37:43.068Z",
|
|
2918
|
+
result_digest: "26fb0dd5c5e20909d3653ab0ddd8fa45e5142728e4e66d595e2419f7ed40b036",
|
|
2919
|
+
created_at: "2026-08-09T13:37:43.068Z",
|
|
2920
|
+
identity_digest: "1108dd972c90e4989036fb6ecec7dde10f54641a697bd53cfcd64017f589ace7",
|
|
2921
|
+
target_digest: "72789fcd60668eeb66ee068936d3afa96deadcb007228830c64b6de783b6dcef",
|
|
2922
|
+
operation_digest: "d7e37bfb2c0d28f8c4086c53224cd855d14694f32807b74c102b61a99963f3c0",
|
|
2923
|
+
idempotency_digest: "b56d8ec37efb818759cdd2ee3b58bc1401ac13384c8d64c1813bb5c198b1e376",
|
|
2924
|
+
receipt_digest: "52ce90c5de6817989f107d9d27e06156962a2b9c365e34e16a57aa37886501a2",
|
|
2925
|
+
response_digest: "2fa3ed508f5f192c18753b1205ced996340fdf02f735420d32db897fc239805c"
|
|
2926
|
+
};
|
|
2927
|
+
var FLEET_RESOURCES_HISTORICAL_RECEIPT = {
|
|
2928
|
+
receipt_id: FLEET_RESOURCES_HISTORICAL_PROJECT_REGISTRATION.receipt_id,
|
|
2929
|
+
authority: FLEET_RESOURCES_HISTORICAL_PROJECT_REGISTRATION.authority,
|
|
2930
|
+
route: FLEET_RESOURCES_HISTORICAL_PROJECT_REGISTRATION.authority_route,
|
|
2931
|
+
package_version: FLEET_RESOURCES_HISTORICAL_PROJECT_REGISTRATION.package_version,
|
|
2932
|
+
authority_id: FLEET_RESOURCES_HISTORICAL_PROJECT_REGISTRATION.authority_id,
|
|
2933
|
+
tenant_id: FLEET_RESOURCES_HISTORICAL_PROJECT_REGISTRATION.tenant_id,
|
|
2934
|
+
corpus_id: FLEET_RESOURCES_HISTORICAL_PROJECT_REGISTRATION.corpus_id,
|
|
2935
|
+
operation_id: FLEET_RESOURCES_HISTORICAL_PROJECT_REGISTRATION.operation_id,
|
|
2936
|
+
step_id: FLEET_RESOURCES_HISTORICAL_PROJECT_REGISTRATION.step_id,
|
|
2937
|
+
resource_kind: FLEET_RESOURCES_HISTORICAL_PROJECT_REGISTRATION.resource_kind,
|
|
2938
|
+
direction: FLEET_RESOURCES_HISTORICAL_PROJECT_REGISTRATION.direction,
|
|
2939
|
+
idempotency_key: FLEET_RESOURCES_HISTORICAL_PROJECT_REGISTRATION.idempotency_key,
|
|
2940
|
+
request_digest: FLEET_RESOURCES_HISTORICAL_PROJECT_REGISTRATION.request_digest,
|
|
2941
|
+
precondition_digest: FLEET_RESOURCES_HISTORICAL_PROJECT_REGISTRATION.precondition_digest,
|
|
2942
|
+
outcome: "accepted",
|
|
2943
|
+
reason: null,
|
|
2944
|
+
target_id: FLEET_RESOURCES_HISTORICAL_PROJECT_REGISTRATION.target_id,
|
|
2945
|
+
result_revision: FLEET_RESOURCES_HISTORICAL_PROJECT_REGISTRATION.result_revision,
|
|
2946
|
+
result_digest: FLEET_RESOURCES_HISTORICAL_PROJECT_REGISTRATION.result_digest,
|
|
2947
|
+
duplicate_of_receipt_id: null,
|
|
2948
|
+
accepted_receipt_id: null,
|
|
2949
|
+
created_by_operation: true,
|
|
2950
|
+
created_at: FLEET_RESOURCES_HISTORICAL_PROJECT_REGISTRATION.created_at
|
|
2951
|
+
};
|
|
2952
|
+
var FLEET_RESOURCES_CURRENT_PROJECT_REGISTRATION_IDENTITY = {
|
|
2953
|
+
authority_id: "mementos",
|
|
2954
|
+
tenant_id: ROOT_TENANT_ID,
|
|
2955
|
+
corpus_id: "mementos:postgresql"
|
|
2956
|
+
};
|
|
2957
|
+
var FLEET_RESOURCES_HISTORICAL_LOOKUP_IDENTITY = {
|
|
2958
|
+
source: {
|
|
2959
|
+
authority: FLEET_RESOURCES_HISTORICAL_PROJECT_REGISTRATION.authority,
|
|
2960
|
+
authority_route: FLEET_RESOURCES_HISTORICAL_PROJECT_REGISTRATION.authority_route,
|
|
2961
|
+
package_version: FLEET_RESOURCES_HISTORICAL_PROJECT_REGISTRATION.package_version,
|
|
2962
|
+
authority_id: FLEET_RESOURCES_HISTORICAL_PROJECT_REGISTRATION.authority_id,
|
|
2963
|
+
tenant_id: FLEET_RESOURCES_HISTORICAL_PROJECT_REGISTRATION.tenant_id,
|
|
2964
|
+
corpus_id: FLEET_RESOURCES_HISTORICAL_PROJECT_REGISTRATION.corpus_id,
|
|
2965
|
+
operation_id: FLEET_RESOURCES_HISTORICAL_PROJECT_REGISTRATION.operation_id,
|
|
2966
|
+
step_id: FLEET_RESOURCES_HISTORICAL_PROJECT_REGISTRATION.step_id,
|
|
2967
|
+
resource_kind: FLEET_RESOURCES_HISTORICAL_PROJECT_REGISTRATION.resource_kind,
|
|
2968
|
+
direction: FLEET_RESOURCES_HISTORICAL_PROJECT_REGISTRATION.direction,
|
|
2969
|
+
target_selector: FLEET_RESOURCES_HISTORICAL_PROJECT_REGISTRATION.target_selector,
|
|
2970
|
+
target_id: FLEET_RESOURCES_HISTORICAL_PROJECT_REGISTRATION.target_id,
|
|
2971
|
+
idempotency_key: FLEET_RESOURCES_HISTORICAL_PROJECT_REGISTRATION.idempotency_key,
|
|
2972
|
+
receipt_id: FLEET_RESOURCES_HISTORICAL_PROJECT_REGISTRATION.receipt_id
|
|
2973
|
+
},
|
|
2974
|
+
destination: FLEET_RESOURCES_CURRENT_PROJECT_REGISTRATION_IDENTITY,
|
|
2975
|
+
lookup_only: true,
|
|
2976
|
+
immutable_receipt: true
|
|
2977
|
+
};
|
|
2978
|
+
function supportsFleetResourcesHistoricalReceiptLookup(capability) {
|
|
2979
|
+
return capability.authority_id === FLEET_RESOURCES_CURRENT_PROJECT_REGISTRATION_IDENTITY.authority_id && capability.tenant_id === FLEET_RESOURCES_CURRENT_PROJECT_REGISTRATION_IDENTITY.tenant_id && capability.corpus_id === FLEET_RESOURCES_CURRENT_PROJECT_REGISTRATION_IDENTITY.corpus_id;
|
|
2980
|
+
}
|
|
2981
|
+
|
|
2859
2982
|
// src/project-registration/identity.ts
|
|
2860
2983
|
var MEMENTOS_PROJECT_AUTHORITY_ENV = {
|
|
2861
2984
|
authorityId: "MEMENTOS_PROJECT_AUTHORITY_ID",
|
|
@@ -2897,12 +3020,13 @@ function resolveMementosProjectAuthorityIdentity(options = {}) {
|
|
|
2897
3020
|
}
|
|
2898
3021
|
function buildMementosProjectRegistrationCapability(options = {}) {
|
|
2899
3022
|
const identity = resolveMementosProjectAuthorityIdentity(options);
|
|
2900
|
-
|
|
3023
|
+
const capability = {
|
|
2901
3024
|
authority: "mementos",
|
|
2902
3025
|
route: MEMENTOS_PROJECT_REGISTRATION_ROUTE,
|
|
2903
3026
|
package_version: options.packageVersion ?? getMementosPackageVersion(),
|
|
2904
3027
|
...identity,
|
|
2905
3028
|
supported_resources: ["project"],
|
|
3029
|
+
supported_historical_lookup_identities: [],
|
|
2906
3030
|
conditional_create: true,
|
|
2907
3031
|
immutable_receipts: true,
|
|
2908
3032
|
exact_terminal_lookup: true,
|
|
@@ -2921,6 +3045,12 @@ function buildMementosProjectRegistrationCapability(options = {}) {
|
|
|
2921
3045
|
stable_keyset_pagination: true,
|
|
2922
3046
|
explicit_membership_only: true
|
|
2923
3047
|
};
|
|
3048
|
+
if (supportsFleetResourcesHistoricalReceiptLookup(capability)) {
|
|
3049
|
+
capability.supported_historical_lookup_identities = [
|
|
3050
|
+
FLEET_RESOURCES_HISTORICAL_LOOKUP_IDENTITY
|
|
3051
|
+
];
|
|
3052
|
+
}
|
|
3053
|
+
return capability;
|
|
2924
3054
|
}
|
|
2925
3055
|
|
|
2926
3056
|
// src/db/projects.ts
|
|
@@ -3998,12 +4128,21 @@ function assertInverseRequest(request, capability) {
|
|
|
3998
4128
|
}
|
|
3999
4129
|
return accepted;
|
|
4000
4130
|
}
|
|
4131
|
+
function matchesCurrentLookupIdentity(request, capability) {
|
|
4132
|
+
return request.authority_route === capability.route && request.package_version === capability.package_version && request.authority_id === capability.authority_id && request.tenant_id === capability.tenant_id && request.corpus_id === capability.corpus_id;
|
|
4133
|
+
}
|
|
4134
|
+
function matchesFleetResourcesHistoricalLookup(request, capability) {
|
|
4135
|
+
if (!supportsFleetResourcesHistoricalReceiptLookup(capability))
|
|
4136
|
+
return false;
|
|
4137
|
+
const source = FLEET_RESOURCES_HISTORICAL_LOOKUP_IDENTITY.source;
|
|
4138
|
+
return request.authority === source.authority && request.authority_route === source.authority_route && request.package_version === source.package_version && request.authority_id === source.authority_id && request.tenant_id === source.tenant_id && request.corpus_id === source.corpus_id && request.operation_id === source.operation_id && request.step_id === source.step_id && request.resource_kind === source.resource_kind && request.direction === source.direction && request.target_selector === source.target_selector && request.target_id === source.target_id && request.idempotency_key === source.idempotency_key;
|
|
4139
|
+
}
|
|
4001
4140
|
function assertLookup(request, capability) {
|
|
4002
4141
|
assertBounds(request);
|
|
4003
4142
|
if (request.max_items !== 1) {
|
|
4004
4143
|
throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_BOUNDS", "terminal receipt lookup requires max_items exactly 1");
|
|
4005
4144
|
}
|
|
4006
|
-
if (request.authority !== "mementos" || request.resource_kind !== "project" || request
|
|
4145
|
+
if (request.authority !== "mementos" || request.resource_kind !== "project" || !matchesCurrentLookupIdentity(request, capability) && !matchesFleetResourcesHistoricalLookup(request, capability)) {
|
|
4007
4146
|
throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_CAPABILITY_MISMATCH", "receipt lookup does not match this authority capability identity");
|
|
4008
4147
|
}
|
|
4009
4148
|
requireString(request.operation_id, "operation_id", { pattern: OPERATION_PATTERN });
|
|
@@ -4021,6 +4160,7 @@ function assertLookup(request, capability) {
|
|
|
4021
4160
|
pattern: PROJECT_ID_PATTERN
|
|
4022
4161
|
});
|
|
4023
4162
|
}
|
|
4163
|
+
return matchesCurrentLookupIdentity(request, capability) ? "current" : "fleet_resources_historical";
|
|
4024
4164
|
}
|
|
4025
4165
|
|
|
4026
4166
|
class PackageOwnedMementosProjectRegistrationAuthority {
|
|
@@ -4285,11 +4425,14 @@ class PackageOwnedMementosProjectRegistrationAuthority {
|
|
|
4285
4425
|
}
|
|
4286
4426
|
async lookupReceipt(request) {
|
|
4287
4427
|
const startedAt = Date.now();
|
|
4288
|
-
assertLookup(request, this.capabilityValue);
|
|
4428
|
+
const lookupIdentity = assertLookup(request, this.capabilityValue);
|
|
4289
4429
|
const receipt = getReceiptForLookup(this.db, request);
|
|
4290
4430
|
if (!receipt || request.target_id !== undefined && receipt.target_id !== request.target_id) {
|
|
4291
4431
|
throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_RECEIPT_NOT_FOUND", "exact immutable terminal receipt was not found");
|
|
4292
4432
|
}
|
|
4433
|
+
if (lookupIdentity === "fleet_resources_historical" && (receipt.receipt_id !== FLEET_RESOURCES_HISTORICAL_LOOKUP_IDENTITY.source.receipt_id || canonicalMementosProjectRegistrationJson(publicReceipt(receipt)) !== canonicalMementosProjectRegistrationJson(FLEET_RESOURCES_HISTORICAL_RECEIPT))) {
|
|
4434
|
+
throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_RECEIPT_NOT_FOUND", "exact immutable historical terminal receipt was not found");
|
|
4435
|
+
}
|
|
4293
4436
|
return withResponseControl(publicReceipt(receipt), request, startedAt);
|
|
4294
4437
|
}
|
|
4295
4438
|
async compensate(request) {
|
|
@@ -4889,12 +5032,12 @@ function deserializeEmbedding(raw) {
|
|
|
4889
5032
|
var REDACTED = "[REDACTED]";
|
|
4890
5033
|
var SECRET_PATTERNS = [
|
|
4891
5034
|
{ name: "openai_key", pattern: /sk-[a-zA-Z0-9_-]{20,}/g },
|
|
4892
|
-
{ name: "anthropic_key", pattern: /sk-ant-[a-zA-Z0-9_-]{20,}/g },
|
|
5035
|
+
{ name: "anthropic_key", pattern: /sk[-]ant[-][a-zA-Z0-9_-]{20,}/g },
|
|
4893
5036
|
{ name: "generic_key", pattern: /(?:pk|tok|key|token|api[_-]?key)[_-][a-zA-Z0-9_-]{16,}/gi },
|
|
4894
5037
|
{ name: "aws_key", pattern: /AKIA[A-Z0-9]{16}/g },
|
|
4895
5038
|
{ name: "aws_secret", pattern: /(?<=AWS_SECRET_ACCESS_KEY\s*=\s*)[A-Za-z0-9/+=]{40}/g },
|
|
4896
5039
|
{ name: "github_token", pattern: /gh[ps]_[a-zA-Z0-9]{36,}/g },
|
|
4897
|
-
{ name: "github_oauth", pattern: /
|
|
5040
|
+
{ name: "github_oauth", pattern: /gho[_][a-zA-Z0-9]{36,}/g },
|
|
4898
5041
|
{ name: "npm_token", pattern: /npm_[a-zA-Z0-9]{36,}/g },
|
|
4899
5042
|
{ name: "bearer", pattern: /Bearer\s+[a-zA-Z0-9_\-.]{20,}/g },
|
|
4900
5043
|
{ name: "conn_string", pattern: /(?:postgres|postgresql|mysql|mongodb|redis|amqp|mqtt):\/\/[^\s"'`]+@[^\s"'`]+/gi },
|
|
@@ -5446,29 +5589,7 @@ function getMemoriesByKey(key, scope, agentId, projectId, db) {
|
|
|
5446
5589
|
const rows = d.query(sql).all(...params);
|
|
5447
5590
|
return rows.map(parseMemoryRow);
|
|
5448
5591
|
}
|
|
5449
|
-
function
|
|
5450
|
-
if (!db && isApiMode()) {
|
|
5451
|
-
const f = filter || {};
|
|
5452
|
-
const q = toQuery({
|
|
5453
|
-
key: f.key,
|
|
5454
|
-
scope: f.scope,
|
|
5455
|
-
category: f.category,
|
|
5456
|
-
status: f.status,
|
|
5457
|
-
tags: f.tags,
|
|
5458
|
-
min_importance: f.min_importance,
|
|
5459
|
-
pinned: f.pinned,
|
|
5460
|
-
agent_id: f.agent_id,
|
|
5461
|
-
project_id: f.project_id,
|
|
5462
|
-
session_id: f.session_id,
|
|
5463
|
-
namespace: f.namespace,
|
|
5464
|
-
as_of: f.as_of,
|
|
5465
|
-
limit: f.limit,
|
|
5466
|
-
offset: f.offset
|
|
5467
|
-
});
|
|
5468
|
-
const { data } = apiJson("GET", `/memories${q}`);
|
|
5469
|
-
return data?.memories ?? [];
|
|
5470
|
-
}
|
|
5471
|
-
const d = db || getDatabase();
|
|
5592
|
+
function buildMemoryListConditions(filter) {
|
|
5472
5593
|
const conditions = [];
|
|
5473
5594
|
const params = [];
|
|
5474
5595
|
if (filter) {
|
|
@@ -5580,21 +5701,99 @@ function listMemories(filter, db) {
|
|
|
5580
5701
|
} else {
|
|
5581
5702
|
conditions.push("status = 'active'");
|
|
5582
5703
|
}
|
|
5704
|
+
return { conditions, params };
|
|
5705
|
+
}
|
|
5706
|
+
function listMemoriesPage(filter, db) {
|
|
5707
|
+
const f = filter || {};
|
|
5708
|
+
if (!db && isApiMode()) {
|
|
5709
|
+
const q = toQuery({
|
|
5710
|
+
key: f.key,
|
|
5711
|
+
scope: f.scope,
|
|
5712
|
+
category: f.category,
|
|
5713
|
+
status: f.status,
|
|
5714
|
+
tags: f.tags,
|
|
5715
|
+
min_importance: f.min_importance,
|
|
5716
|
+
pinned: f.pinned,
|
|
5717
|
+
agent_id: f.agent_id,
|
|
5718
|
+
project_id: f.project_id,
|
|
5719
|
+
session_id: f.session_id,
|
|
5720
|
+
namespace: f.namespace,
|
|
5721
|
+
as_of: f.as_of,
|
|
5722
|
+
limit: f.limit,
|
|
5723
|
+
offset: f.offset
|
|
5724
|
+
});
|
|
5725
|
+
const { data } = apiJson("GET", `/memories${q}`);
|
|
5726
|
+
return {
|
|
5727
|
+
rows: data?.memories ?? [],
|
|
5728
|
+
has_more: data?.has_more,
|
|
5729
|
+
next_cursor: data?.next_cursor ?? null
|
|
5730
|
+
};
|
|
5731
|
+
}
|
|
5732
|
+
const d = db || getDatabase();
|
|
5733
|
+
const { conditions, params } = buildMemoryListConditions(f);
|
|
5583
5734
|
let sql = "SELECT * FROM memories";
|
|
5584
5735
|
if (conditions.length > 0) {
|
|
5585
5736
|
sql += ` WHERE ${conditions.join(" AND ")}`;
|
|
5586
5737
|
}
|
|
5587
5738
|
sql += " ORDER BY importance DESC, created_at DESC";
|
|
5588
|
-
if (
|
|
5739
|
+
if (f.limit) {
|
|
5589
5740
|
sql += " LIMIT ?";
|
|
5590
|
-
params.push(
|
|
5741
|
+
params.push(f.limit);
|
|
5591
5742
|
}
|
|
5592
|
-
if (
|
|
5743
|
+
if (f.offset) {
|
|
5593
5744
|
sql += " OFFSET ?";
|
|
5594
|
-
params.push(
|
|
5745
|
+
params.push(f.offset);
|
|
5595
5746
|
}
|
|
5596
5747
|
const rows = d.query(sql).all(...params);
|
|
5597
|
-
|
|
5748
|
+
const parsed = rows.map(parseMemoryRow);
|
|
5749
|
+
const hasMore = f.limit !== undefined && parsed.length === f.limit;
|
|
5750
|
+
return {
|
|
5751
|
+
rows: parsed,
|
|
5752
|
+
has_more: hasMore,
|
|
5753
|
+
next_cursor: hasMore ? (f.offset ?? 0) + parsed.length : null
|
|
5754
|
+
};
|
|
5755
|
+
}
|
|
5756
|
+
function listMemories(filter, db) {
|
|
5757
|
+
return listMemoriesPage(filter, db).rows;
|
|
5758
|
+
}
|
|
5759
|
+
function listMemoriesBounded(filter = {}, target, db) {
|
|
5760
|
+
const pageSize = 1000;
|
|
5761
|
+
const maxPages = 1000;
|
|
5762
|
+
const want = target === undefined ? undefined : target + 1;
|
|
5763
|
+
const rows = [];
|
|
5764
|
+
const seenCursors = new Set;
|
|
5765
|
+
let cursor = filter.offset ?? 0;
|
|
5766
|
+
let pages = 0;
|
|
5767
|
+
for (;; ) {
|
|
5768
|
+
if (want !== undefined && rows.length >= want)
|
|
5769
|
+
break;
|
|
5770
|
+
if (++pages > maxPages) {
|
|
5771
|
+
throw new Error(`memories list traversal exceeded ${maxPages} pages while collecting rows \u2014 aborting (cursor cycle?)`);
|
|
5772
|
+
}
|
|
5773
|
+
const limit = Math.min(want === undefined ? pageSize : want - rows.length, pageSize);
|
|
5774
|
+
const page = listMemoriesPage({ ...filter, limit, offset: cursor }, db);
|
|
5775
|
+
rows.push(...page.rows);
|
|
5776
|
+
if (page.has_more === false)
|
|
5777
|
+
break;
|
|
5778
|
+
if (page.has_more === undefined && page.rows.length < limit)
|
|
5779
|
+
break;
|
|
5780
|
+
if (page.has_more === true && (page.next_cursor === null || page.next_cursor === undefined)) {
|
|
5781
|
+
throw new Error("memories list page claimed more results without a next cursor \u2014 aborting");
|
|
5782
|
+
}
|
|
5783
|
+
const next = page.next_cursor ?? cursor + page.rows.length;
|
|
5784
|
+
if (seenCursors.has(next)) {
|
|
5785
|
+
throw new Error("memories list traversal repeated a cursor \u2014 aborting");
|
|
5786
|
+
}
|
|
5787
|
+
seenCursors.add(next);
|
|
5788
|
+
cursor = next;
|
|
5789
|
+
}
|
|
5790
|
+
const hasMore = target !== undefined && rows.length > target;
|
|
5791
|
+
const trimmed = hasMore ? rows.slice(0, target) : rows;
|
|
5792
|
+
return {
|
|
5793
|
+
rows: trimmed,
|
|
5794
|
+
has_more: hasMore,
|
|
5795
|
+
next_cursor: hasMore ? (filter.offset ?? 0) + trimmed.length : null
|
|
5796
|
+
};
|
|
5598
5797
|
}
|
|
5599
5798
|
function updateMemory(id, input, db) {
|
|
5600
5799
|
if (!db && isApiMode()) {
|