hippo-memory 0.38.0 → 0.40.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/api.d.ts +29 -6
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +168 -42
- package/dist/api.js.map +1 -1
- package/dist/audit.d.ts +1 -1
- package/dist/audit.d.ts.map +1 -1
- package/dist/audit.js.map +1 -1
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +20 -4
- package/dist/auth.js.map +1 -1
- package/dist/cli.js +101 -6
- package/dist/cli.js.map +1 -1
- package/dist/connectors/slack/deletion.d.ts +10 -0
- package/dist/connectors/slack/deletion.d.ts.map +1 -1
- package/dist/connectors/slack/deletion.js +21 -10
- package/dist/connectors/slack/deletion.js.map +1 -1
- package/dist/connectors/slack/dlq.d.ts +59 -1
- package/dist/connectors/slack/dlq.d.ts.map +1 -1
- package/dist/connectors/slack/dlq.js +204 -6
- package/dist/connectors/slack/dlq.js.map +1 -1
- package/dist/connectors/slack/idempotency.d.ts +12 -0
- package/dist/connectors/slack/idempotency.d.ts.map +1 -1
- package/dist/connectors/slack/idempotency.js +16 -0
- package/dist/connectors/slack/idempotency.js.map +1 -1
- package/dist/connectors/slack/ingest.d.ts +7 -1
- package/dist/connectors/slack/ingest.d.ts.map +1 -1
- package/dist/connectors/slack/ingest.js +40 -6
- package/dist/connectors/slack/ingest.js.map +1 -1
- package/dist/connectors/slack/signature.d.ts +7 -0
- package/dist/connectors/slack/signature.d.ts.map +1 -1
- package/dist/connectors/slack/signature.js +16 -9
- package/dist/connectors/slack/signature.js.map +1 -1
- package/dist/connectors/slack/tenant-routing.d.ts +15 -7
- package/dist/connectors/slack/tenant-routing.d.ts.map +1 -1
- package/dist/connectors/slack/tenant-routing.js +28 -8
- package/dist/connectors/slack/tenant-routing.js.map +1 -1
- package/dist/correction-latency.d.ts +36 -0
- package/dist/correction-latency.d.ts.map +1 -0
- package/dist/correction-latency.js +74 -0
- package/dist/correction-latency.js.map +1 -0
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +88 -1
- package/dist/db.js.map +1 -1
- package/dist/mcp/server.d.ts +7 -0
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +73 -30
- package/dist/mcp/server.js.map +1 -1
- package/dist/provenance-coverage.d.ts +18 -0
- package/dist/provenance-coverage.d.ts.map +1 -0
- package/dist/provenance-coverage.js +23 -0
- package/dist/provenance-coverage.js.map +1 -0
- package/dist/raw-archive-mirror-cleanup.d.ts +20 -0
- package/dist/raw-archive-mirror-cleanup.d.ts.map +1 -0
- package/dist/raw-archive-mirror-cleanup.js +53 -0
- package/dist/raw-archive-mirror-cleanup.js.map +1 -0
- package/dist/raw-archive.d.ts +8 -0
- package/dist/raw-archive.d.ts.map +1 -1
- package/dist/raw-archive.js +21 -7
- package/dist/raw-archive.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +142 -18
- package/dist/server.js.map +1 -1
- package/dist/shared.d.ts +1 -0
- package/dist/shared.d.ts.map +1 -1
- package/dist/shared.js +6 -1
- package/dist/shared.js.map +1 -1
- package/dist/src/api.js +168 -42
- package/dist/src/api.js.map +1 -1
- package/dist/src/audit.js.map +1 -1
- package/dist/src/auth.js +20 -4
- package/dist/src/auth.js.map +1 -1
- package/dist/src/cli.js +101 -6
- package/dist/src/cli.js.map +1 -1
- package/dist/src/connectors/slack/deletion.js +21 -10
- package/dist/src/connectors/slack/deletion.js.map +1 -1
- package/dist/src/connectors/slack/dlq.js +204 -6
- package/dist/src/connectors/slack/dlq.js.map +1 -1
- package/dist/src/connectors/slack/idempotency.js +16 -0
- package/dist/src/connectors/slack/idempotency.js.map +1 -1
- package/dist/src/connectors/slack/ingest.js +40 -6
- package/dist/src/connectors/slack/ingest.js.map +1 -1
- package/dist/src/connectors/slack/signature.js +16 -9
- package/dist/src/connectors/slack/signature.js.map +1 -1
- package/dist/src/connectors/slack/tenant-routing.js +28 -8
- package/dist/src/connectors/slack/tenant-routing.js.map +1 -1
- package/dist/src/correction-latency.js +74 -0
- package/dist/src/correction-latency.js.map +1 -0
- package/dist/src/db.js +88 -1
- package/dist/src/db.js.map +1 -1
- package/dist/src/mcp/server.js +73 -30
- package/dist/src/mcp/server.js.map +1 -1
- package/dist/src/provenance-coverage.js +23 -0
- package/dist/src/provenance-coverage.js.map +1 -0
- package/dist/src/raw-archive-mirror-cleanup.js +53 -0
- package/dist/src/raw-archive-mirror-cleanup.js.map +1 -0
- package/dist/src/raw-archive.js +21 -7
- package/dist/src/raw-archive.js.map +1 -1
- package/dist/src/server.js +142 -18
- package/dist/src/server.js.map +1 -1
- package/dist/src/shared.js +6 -1
- package/dist/src/shared.js.map +1 -1
- package/dist/src/store.js +50 -26
- package/dist/src/store.js.map +1 -1
- package/dist/store.d.ts +24 -0
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +50 -26
- package/dist/store.js.map +1 -1
- package/extensions/openclaw-plugin/openclaw.plugin.json +1 -1
- package/extensions/openclaw-plugin/package.json +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/dist/src/auth.js
CHANGED
|
@@ -17,6 +17,14 @@ function hashKey(plaintext) {
|
|
|
17
17
|
const hash = scryptSync(plaintext, salt, SCRYPT_KEYLEN);
|
|
18
18
|
return `scrypt$${salt.toString('hex')}$${hash.toString('hex')}`;
|
|
19
19
|
}
|
|
20
|
+
// Constant dummy hash precomputed once at module load. Used by validateApiKey
|
|
21
|
+
// to pay the scrypt cost on the miss path (unknown / revoked / malformed key)
|
|
22
|
+
// so the timing signal between hit and miss is reduced. The DB lookup branch
|
|
23
|
+
// itself can still leak via cache effects — v0.40 follow-up: request-level
|
|
24
|
+
// rate limit on /v1/* to bound key-id enumeration. Stored format identical to
|
|
25
|
+
// real hashes: scrypt$saltHex$hashHex.
|
|
26
|
+
const DUMMY_PLAINTEXT = 'hk_dummy_constant_padding_for_timing.dummy_secret_padding_for_timing_x';
|
|
27
|
+
const DUMMY_HASH = hashKey(DUMMY_PLAINTEXT);
|
|
20
28
|
function verifyKey(plaintext, stored) {
|
|
21
29
|
const parts = stored.split('$');
|
|
22
30
|
if (parts.length !== 3 || parts[0] !== 'scrypt')
|
|
@@ -36,15 +44,23 @@ export function createApiKey(db, opts) {
|
|
|
36
44
|
}
|
|
37
45
|
export function validateApiKey(db, plaintext) {
|
|
38
46
|
const dot = plaintext.indexOf('.');
|
|
39
|
-
if (dot < 0)
|
|
47
|
+
if (dot < 0) {
|
|
48
|
+
// Malformed input still pays the scrypt cost so caller cannot distinguish
|
|
49
|
+
// "no dot" from "unknown key_id" by timing.
|
|
50
|
+
verifyKey(DUMMY_PLAINTEXT, DUMMY_HASH);
|
|
40
51
|
return { valid: false };
|
|
52
|
+
}
|
|
41
53
|
const keyId = plaintext.slice(0, dot);
|
|
42
54
|
const row = db
|
|
43
55
|
.prepare(`SELECT key_hash, tenant_id, revoked_at FROM api_keys WHERE key_id = ?`)
|
|
44
56
|
.get(keyId);
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
57
|
+
// Always run verifyKey — on miss/revoked, against DUMMY_HASH so scrypt cost
|
|
58
|
+
// is paid. This reduces timing signal between hit and miss, but the DB
|
|
59
|
+
// lookup itself can still leak via cache effects. v0.40 follow-up: add
|
|
60
|
+
// request-level rate limit on /v1/* to bound enumeration.
|
|
61
|
+
const target = (row && !row.revoked_at) ? row.key_hash : DUMMY_HASH;
|
|
62
|
+
const matches = verifyKey(plaintext, target);
|
|
63
|
+
if (!row || row.revoked_at || !matches)
|
|
48
64
|
return { valid: false };
|
|
49
65
|
return { valid: true, tenantId: row.tenant_id, keyId };
|
|
50
66
|
}
|
package/dist/src/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAGvE,MAAM,UAAU,GAAG,KAAK,CAAC;AACzB,MAAM,MAAM,GAAG,EAAE,CAAC,CAAO,4BAA4B;AACrD,MAAM,UAAU,GAAG,EAAE,CAAC,CAAG,yBAAyB;AAClD,MAAM,aAAa,GAAG,EAAE,CAAC;AAEzB,MAAM,MAAM,GAAG,kCAAkC,CAAC;AAElD,SAAS,UAAU,CAAC,CAAS;IAC3B,MAAM,KAAK,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;IAC7B,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE;QAAE,GAAG,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAE,GAAG,EAAE,CAAC,CAAC;IAC1D,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,OAAO,CAAC,SAAiB;IAChC,qCAAqC;IACrC,MAAM,IAAI,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;IAC7B,MAAM,IAAI,GAAG,UAAU,CAAC,SAAS,EAAE,IAAI,EAAE,aAAa,CAAC,CAAC;IACxD,OAAO,UAAU,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;AAClE,CAAC;AAED,SAAS,SAAS,CAAC,SAAiB,EAAE,MAAc;IAClD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAChC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAE,EAAE,KAAK,CAAC,CAAC;IAC3C,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAE,EAAE,KAAK,CAAC,CAAC;IAC/C,MAAM,MAAM,GAAG,UAAU,CAAC,SAAS,EAAE,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC5D,OAAO,QAAQ,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM,IAAI,eAAe,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;AAChF,CAAC;AAYD,MAAM,UAAU,YAAY,CAAC,EAAoB,EAAE,IAAsB;IACvE,MAAM,KAAK,GAAG,GAAG,UAAU,GAAG,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;IACnD,MAAM,MAAM,GAAG,UAAU,CAAC,UAAU,CAAC,CAAC;IACtC,MAAM,SAAS,GAAG,GAAG,KAAK,IAAI,MAAM,EAAE,CAAC;IACvC,MAAM,IAAI,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,EAAE,CAAC,OAAO,CACR,8FAA8F,CAC/F,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,KAAK,IAAI,IAAI,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;IAChF,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;AAC9B,CAAC;AAQD,MAAM,UAAU,cAAc,CAAC,EAAoB,EAAE,SAAiB;IACpE,MAAM,GAAG,GAAG,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACnC,IAAI,GAAG,GAAG,CAAC;
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/auth.ts"],"names":[],"mappings":"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"}
|
package/dist/src/cli.js
CHANGED
|
@@ -53,6 +53,8 @@ import { importChatGPT, importClaude, importCursor, importGenericFile, importMar
|
|
|
53
53
|
import { cmdCapture } from './capture.js';
|
|
54
54
|
import { auditMemories, appendAuditEvent, } from './audit.js';
|
|
55
55
|
import { listApiKeys, revokeApiKey } from './auth.js';
|
|
56
|
+
import { buildProvenanceCoverage } from './provenance-coverage.js';
|
|
57
|
+
import { buildCorrectionLatency } from './correction-latency.js';
|
|
56
58
|
import * as api from './api.js';
|
|
57
59
|
import * as client from './client.js';
|
|
58
60
|
import { detectServer, removePidfile } from './server-detect.js';
|
|
@@ -64,7 +66,7 @@ import { wmPush, wmRead, wmClear, wmFlush } from './working-memory.js';
|
|
|
64
66
|
import { multihopSearch } from './multihop.js';
|
|
65
67
|
import { computeSalience } from './salience.js';
|
|
66
68
|
import { computeAmbientState, renderAmbientSummary } from './ambient.js';
|
|
67
|
-
import { listDlq } from './connectors/slack/dlq.js';
|
|
69
|
+
import { listDlq, replayDlqEntry } from './connectors/slack/dlq.js';
|
|
68
70
|
import { backfillChannel } from './connectors/slack/backfill.js';
|
|
69
71
|
import { slackHistoryFetcher } from './connectors/slack/web-client.js';
|
|
70
72
|
// ---------------------------------------------------------------------------
|
|
@@ -3992,12 +3994,17 @@ function cmdAuthCreate(hippoRoot, flags) {
|
|
|
3992
3994
|
const tenantFlag = typeof flags['tenant'] === 'string' ? flags['tenant'] : undefined;
|
|
3993
3995
|
const labelFlag = typeof flags['label'] === 'string' ? flags['label'] : undefined;
|
|
3994
3996
|
const asJson = Boolean(flags['json']);
|
|
3997
|
+
// The CLI's --tenant flag is the only legitimate cross-tenant override
|
|
3998
|
+
// (admin minting a key for another tenant from the local machine). It
|
|
3999
|
+
// flows through ctx.tenantId, NOT through opts — authCreate's opts no
|
|
4000
|
+
// longer accepts a tenantId field, so the HTTP layer cannot smuggle a
|
|
4001
|
+
// body.tenantId across.
|
|
3995
4002
|
const ctx = {
|
|
3996
4003
|
hippoRoot: root,
|
|
3997
|
-
tenantId: resolveTenantId({}),
|
|
4004
|
+
tenantId: tenantFlag ?? resolveTenantId({}),
|
|
3998
4005
|
actor: 'cli',
|
|
3999
4006
|
};
|
|
4000
|
-
const result = api.authCreate(ctx, {
|
|
4007
|
+
const result = api.authCreate(ctx, { label: labelFlag });
|
|
4001
4008
|
if (asJson) {
|
|
4002
4009
|
console.log(JSON.stringify({
|
|
4003
4010
|
keyId: result.keyId,
|
|
@@ -4464,6 +4471,28 @@ function cmdSlackDlqList(hippoRoot, _flags) {
|
|
|
4464
4471
|
closeHippoDb(db);
|
|
4465
4472
|
}
|
|
4466
4473
|
}
|
|
4474
|
+
function cmdSlackDlqReplay(hippoRoot, args, flags) {
|
|
4475
|
+
const idArg = args[2];
|
|
4476
|
+
if (!idArg) {
|
|
4477
|
+
console.error('Usage: hippo slack dlq replay <id> [--force]');
|
|
4478
|
+
process.exit(1);
|
|
4479
|
+
}
|
|
4480
|
+
const id = Number(idArg);
|
|
4481
|
+
if (!Number.isFinite(id) || !Number.isInteger(id) || id < 1) {
|
|
4482
|
+
console.error(`replay: invalid id ${idArg}`);
|
|
4483
|
+
process.exit(1);
|
|
4484
|
+
}
|
|
4485
|
+
const force = flags.force === true;
|
|
4486
|
+
const result = replayDlqEntry({ hippoRoot }, id, {
|
|
4487
|
+
force,
|
|
4488
|
+
signingSecret: process.env.SLACK_SIGNING_SECRET,
|
|
4489
|
+
});
|
|
4490
|
+
if (!result.ok) {
|
|
4491
|
+
console.error(`replay failed: status=${result.status} retry_count=${result.retryCount}${result.reason ? ` reason=${result.reason}` : ''}`);
|
|
4492
|
+
process.exit(1);
|
|
4493
|
+
}
|
|
4494
|
+
console.log(`replay ok: status=${result.status} memory_id=${result.memoryId ?? '(none)'} retry_count=${result.retryCount}`);
|
|
4495
|
+
}
|
|
4467
4496
|
function cmdSlack(hippoRoot, args, flags) {
|
|
4468
4497
|
const sub = args[0];
|
|
4469
4498
|
if (sub === 'backfill') {
|
|
@@ -4474,7 +4503,11 @@ function cmdSlack(hippoRoot, args, flags) {
|
|
|
4474
4503
|
cmdSlackDlqList(hippoRoot, flags);
|
|
4475
4504
|
return;
|
|
4476
4505
|
}
|
|
4477
|
-
|
|
4506
|
+
if (sub === 'dlq' && args[1] === 'replay') {
|
|
4507
|
+
cmdSlackDlqReplay(hippoRoot, args, flags);
|
|
4508
|
+
return;
|
|
4509
|
+
}
|
|
4510
|
+
console.error('Usage: hippo slack <backfill|dlq list|dlq replay <id> [--force]> [...]');
|
|
4478
4511
|
process.exit(1);
|
|
4479
4512
|
}
|
|
4480
4513
|
function printUsage() {
|
|
@@ -4590,6 +4623,11 @@ Commands:
|
|
|
4590
4623
|
--threshold <n> Overlap threshold 0-1 (default: 0.7)
|
|
4591
4624
|
status Show memory health stats
|
|
4592
4625
|
audit [--fix] Check memory quality (--fix removes junk)
|
|
4626
|
+
provenance Provenance coverage gate for kind='raw' rows
|
|
4627
|
+
--json Output as JSON
|
|
4628
|
+
--strict Exit non-zero when coverage < 100%
|
|
4629
|
+
correction-latency Wall-clock lag from receipt to supersession (p50/p95/max)
|
|
4630
|
+
--json Output as JSON
|
|
4593
4631
|
outcome Apply feedback to last recall
|
|
4594
4632
|
--good Memories were helpful
|
|
4595
4633
|
--bad Memories were irrelevant
|
|
@@ -4977,6 +5015,62 @@ async function main() {
|
|
|
4977
5015
|
}
|
|
4978
5016
|
break;
|
|
4979
5017
|
}
|
|
5018
|
+
case 'correction-latency': {
|
|
5019
|
+
requireInit(hippoRoot);
|
|
5020
|
+
const entries = loadAllEntries(hippoRoot);
|
|
5021
|
+
const report = buildCorrectionLatency(entries);
|
|
5022
|
+
if (flags['json']) {
|
|
5023
|
+
console.log(JSON.stringify(report, null, 2));
|
|
5024
|
+
}
|
|
5025
|
+
else if (report.count === 0) {
|
|
5026
|
+
console.log('No supersessions found. Correction latency is undefined.');
|
|
5027
|
+
}
|
|
5028
|
+
else {
|
|
5029
|
+
const fmt = (ms) => {
|
|
5030
|
+
if (ms === null)
|
|
5031
|
+
return 'n/a';
|
|
5032
|
+
if (ms < 1000)
|
|
5033
|
+
return `${ms}ms`;
|
|
5034
|
+
if (ms < 60_000)
|
|
5035
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
5036
|
+
if (ms < 3_600_000)
|
|
5037
|
+
return `${(ms / 60_000).toFixed(1)}m`;
|
|
5038
|
+
return `${(ms / 3_600_000).toFixed(1)}h`;
|
|
5039
|
+
};
|
|
5040
|
+
console.log(`Corrections: ${report.count} total (${report.extractionCount} extraction-driven, ${report.manualCount} manual)`);
|
|
5041
|
+
console.log(`Latency p50: ${fmt(report.p50Ms)}, p95: ${fmt(report.p95Ms)}, max: ${fmt(report.maxMs)}`);
|
|
5042
|
+
if (report.extractionCount === 0 && report.manualCount > 0) {
|
|
5043
|
+
console.log(`\nAll ${report.manualCount} corrections were manual supersedes: no measurable observation lag.`);
|
|
5044
|
+
console.log(`To measure latency, route corrections through extraction (set new.extracted_from to the raw receipt).`);
|
|
5045
|
+
}
|
|
5046
|
+
}
|
|
5047
|
+
break;
|
|
5048
|
+
}
|
|
5049
|
+
case 'provenance': {
|
|
5050
|
+
requireInit(hippoRoot);
|
|
5051
|
+
const entries = loadAllEntries(hippoRoot);
|
|
5052
|
+
const coverage = buildProvenanceCoverage(entries);
|
|
5053
|
+
if (flags['json']) {
|
|
5054
|
+
console.log(JSON.stringify(coverage, null, 2));
|
|
5055
|
+
}
|
|
5056
|
+
else if (coverage.rawTotal === 0) {
|
|
5057
|
+
console.log('No kind=raw memories present. Coverage gate trivially satisfied.');
|
|
5058
|
+
}
|
|
5059
|
+
else {
|
|
5060
|
+
const pct = (coverage.coverage * 100).toFixed(1);
|
|
5061
|
+
console.log(`Provenance coverage: ${coverage.rawWithEnvelope}/${coverage.rawTotal} raw rows envelope-complete (${pct}%)`);
|
|
5062
|
+
if (coverage.gaps.length > 0) {
|
|
5063
|
+
console.log(`\nGaps:`);
|
|
5064
|
+
for (const g of coverage.gaps) {
|
|
5065
|
+
console.log(` ${g.id}: missing ${g.missing.join(', ')}`);
|
|
5066
|
+
}
|
|
5067
|
+
}
|
|
5068
|
+
}
|
|
5069
|
+
if (flags['strict'] && coverage.coverage < 1) {
|
|
5070
|
+
process.exit(1);
|
|
5071
|
+
}
|
|
5072
|
+
break;
|
|
5073
|
+
}
|
|
4980
5074
|
case 'status':
|
|
4981
5075
|
cmdStatus(hippoRoot);
|
|
4982
5076
|
break;
|
|
@@ -5106,13 +5200,14 @@ async function main() {
|
|
|
5106
5200
|
else if (shareId) {
|
|
5107
5201
|
requireInit(hippoRoot);
|
|
5108
5202
|
const force = Boolean(flags['force']);
|
|
5109
|
-
const
|
|
5203
|
+
const tenantId = resolveTenantId({});
|
|
5204
|
+
const result = shareMemory(hippoRoot, shareId, { force, tenantId });
|
|
5110
5205
|
if (result) {
|
|
5111
5206
|
console.log(`Shared [${result.id}] to global store.`);
|
|
5112
5207
|
console.log(` Source: ${result.source}`);
|
|
5113
5208
|
}
|
|
5114
5209
|
else {
|
|
5115
|
-
const entry = readEntry(hippoRoot, shareId);
|
|
5210
|
+
const entry = readEntry(hippoRoot, shareId, tenantId);
|
|
5116
5211
|
if (entry) {
|
|
5117
5212
|
const score = transferScore(entry);
|
|
5118
5213
|
console.log(`Transfer score too low (${fmt(score)}). This memory looks project-specific.`);
|