hippo-memory 1.12.4 → 1.12.5

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.
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Slack workspace registration helpers (T2B follow-up, 2026-05-24).
3
+ *
4
+ * The `slack_workspaces` table maps Slack `team_id` → hippo `tenant_id`.
5
+ * Empty table = single-tenant install (HIPPO_TENANT fallback).
6
+ * Non-empty = multi-workspace install (fail-closed routing via
7
+ * `resolveTenantForTeam`).
8
+ *
9
+ * Before T2B, populating this table required direct SQL — fine for a
10
+ * single-machine deployment, awkward for operators with multiple
11
+ * workspaces. These helpers give the CLI (`hippo slack workspaces
12
+ * add|list|remove`) a clean surface.
13
+ *
14
+ * Design choices:
15
+ * - `add` is an upsert (ON CONFLICT UPDATE). Re-registering an
16
+ * existing team_id with a different tenant_id intentionally
17
+ * overwrites — operators move workspaces between tenants and the
18
+ * CLI shouldn't require a delete+add dance.
19
+ * - `list` sorts by team_id for stable output.
20
+ * - `remove` returns a boolean for the CLI to distinguish "removed"
21
+ * from "not found" without an extra SELECT.
22
+ */
23
+ import type { DatabaseSyncLike } from '../../db.js';
24
+ export interface SlackWorkspace {
25
+ teamId: string;
26
+ tenantId: string;
27
+ addedAt: string;
28
+ }
29
+ export interface AddWorkspaceOpts {
30
+ teamId: string;
31
+ tenantId: string;
32
+ }
33
+ /**
34
+ * Register or re-register a Slack team → tenant mapping. Upserts on
35
+ * team_id conflict (operators move workspaces between tenants).
36
+ */
37
+ export declare function addWorkspace(db: DatabaseSyncLike, opts: AddWorkspaceOpts): SlackWorkspace;
38
+ /**
39
+ * List all registered workspaces, sorted by team_id for stable output.
40
+ */
41
+ export declare function listWorkspaces(db: DatabaseSyncLike): SlackWorkspace[];
42
+ /**
43
+ * Remove a workspace registration by team_id. Returns true if a row was
44
+ * deleted, false if no row matched (so the CLI can report not-found
45
+ * without a separate lookup).
46
+ */
47
+ export declare function removeWorkspace(db: DatabaseSyncLike, teamId: string): boolean;
48
+ //# sourceMappingURL=workspaces.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspaces.d.ts","sourceRoot":"","sources":["../../../src/connectors/slack/workspaces.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,EAAE,EAAE,gBAAgB,EACpB,IAAI,EAAE,gBAAgB,GACrB,cAAc,CAUhB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,gBAAgB,GAAG,cAAc,EAAE,CAWrE;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,EAAE,EAAE,gBAAgB,EACpB,MAAM,EAAE,MAAM,GACb,OAAO,CAKT"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Slack workspace registration helpers (T2B follow-up, 2026-05-24).
3
+ *
4
+ * The `slack_workspaces` table maps Slack `team_id` → hippo `tenant_id`.
5
+ * Empty table = single-tenant install (HIPPO_TENANT fallback).
6
+ * Non-empty = multi-workspace install (fail-closed routing via
7
+ * `resolveTenantForTeam`).
8
+ *
9
+ * Before T2B, populating this table required direct SQL — fine for a
10
+ * single-machine deployment, awkward for operators with multiple
11
+ * workspaces. These helpers give the CLI (`hippo slack workspaces
12
+ * add|list|remove`) a clean surface.
13
+ *
14
+ * Design choices:
15
+ * - `add` is an upsert (ON CONFLICT UPDATE). Re-registering an
16
+ * existing team_id with a different tenant_id intentionally
17
+ * overwrites — operators move workspaces between tenants and the
18
+ * CLI shouldn't require a delete+add dance.
19
+ * - `list` sorts by team_id for stable output.
20
+ * - `remove` returns a boolean for the CLI to distinguish "removed"
21
+ * from "not found" without an extra SELECT.
22
+ */
23
+ /**
24
+ * Register or re-register a Slack team → tenant mapping. Upserts on
25
+ * team_id conflict (operators move workspaces between tenants).
26
+ */
27
+ export function addWorkspace(db, opts) {
28
+ const addedAt = new Date().toISOString();
29
+ db.prepare(`INSERT INTO slack_workspaces (team_id, tenant_id, added_at)
30
+ VALUES (?, ?, ?)
31
+ ON CONFLICT(team_id) DO UPDATE SET
32
+ tenant_id = excluded.tenant_id,
33
+ added_at = excluded.added_at`).run(opts.teamId, opts.tenantId, addedAt);
34
+ return { teamId: opts.teamId, tenantId: opts.tenantId, addedAt };
35
+ }
36
+ /**
37
+ * List all registered workspaces, sorted by team_id for stable output.
38
+ */
39
+ export function listWorkspaces(db) {
40
+ const rows = db
41
+ .prepare(`SELECT team_id, tenant_id, added_at FROM slack_workspaces ORDER BY team_id`)
42
+ .all();
43
+ return rows.map((r) => ({
44
+ teamId: r.team_id,
45
+ tenantId: r.tenant_id,
46
+ addedAt: r.added_at,
47
+ }));
48
+ }
49
+ /**
50
+ * Remove a workspace registration by team_id. Returns true if a row was
51
+ * deleted, false if no row matched (so the CLI can report not-found
52
+ * without a separate lookup).
53
+ */
54
+ export function removeWorkspace(db, teamId) {
55
+ const result = db
56
+ .prepare(`DELETE FROM slack_workspaces WHERE team_id = ?`)
57
+ .run(teamId);
58
+ return Number(result.changes) > 0;
59
+ }
60
+ //# sourceMappingURL=workspaces.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspaces.js","sourceRoot":"","sources":["../../../src/connectors/slack/workspaces.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAeH;;;GAGG;AACH,MAAM,UAAU,YAAY,CAC1B,EAAoB,EACpB,IAAsB;IAEtB,MAAM,OAAO,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACzC,EAAE,CAAC,OAAO,CACR;;;;oCAIgC,CACjC,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC3C,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAC;AACnE,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,EAAoB;IACjD,MAAM,IAAI,GAAG,EAAE;SACZ,OAAO,CACN,4EAA4E,CAC7E;SACA,GAAG,EAAqE,CAAC;IAC5E,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACtB,MAAM,EAAE,CAAC,CAAC,OAAO;QACjB,QAAQ,EAAE,CAAC,CAAC,SAAS;QACrB,OAAO,EAAE,CAAC,CAAC,QAAQ;KACpB,CAAC,CAAC,CAAC;AACN,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAC7B,EAAoB,EACpB,MAAc;IAEd,MAAM,MAAM,GAAG,EAAE;SACd,OAAO,CAAC,gDAAgD,CAAC;SACzD,GAAG,CAAC,MAAM,CAAC,CAAC;IACf,OAAO,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;AACpC,CAAC"}
package/dist/src/cli.js CHANGED
@@ -70,6 +70,7 @@ import { computeAmbientState, renderAmbientSummary } from './ambient.js';
70
70
  import { listDlq, replayDlqEntry } from './connectors/slack/dlq.js';
71
71
  import { backfillChannel } from './connectors/slack/backfill.js';
72
72
  import { slackHistoryFetcher } from './connectors/slack/web-client.js';
73
+ import { addWorkspace as addSlackWorkspace, listWorkspaces as listSlackWorkspaces, removeWorkspace as removeSlackWorkspace, } from './connectors/slack/workspaces.js';
73
74
  import { cmdGithub } from './connectors/github/cli-impl.js';
74
75
  // ---------------------------------------------------------------------------
75
76
  // Helpers
@@ -4567,6 +4568,71 @@ function cmdSlackDlqReplay(hippoRoot, args, flags) {
4567
4568
  }
4568
4569
  console.log(`replay ok: status=${result.status} memory_id=${result.memoryId ?? '(none)'} retry_count=${result.retryCount}`);
4569
4570
  }
4571
+ function printSlackWorkspacesUsage() {
4572
+ console.log('hippo slack workspaces <add|list|remove> [options]');
4573
+ console.log(' add --team <T> --tenant <t> Register a workspace (upserts on existing team-id)');
4574
+ console.log(' list List all registered workspaces');
4575
+ console.log(' remove --team <T> Remove a workspace registration');
4576
+ }
4577
+ function cmdSlackWorkspacesAdd(hippoRoot, flags) {
4578
+ if (flags['help']) {
4579
+ printSlackWorkspacesUsage();
4580
+ return;
4581
+ }
4582
+ const teamId = typeof flags['team'] === 'string' ? flags['team'].trim() : '';
4583
+ const tenantId = typeof flags['tenant'] === 'string' ? flags['tenant'].trim() : '';
4584
+ if (!teamId || !tenantId) {
4585
+ console.error('Usage: hippo slack workspaces add --team <T> --tenant <t>');
4586
+ process.exit(1);
4587
+ }
4588
+ const db = openHippoDb(hippoRoot);
4589
+ try {
4590
+ const ws = addSlackWorkspace(db, { teamId, tenantId });
4591
+ console.log(`added: ${ws.teamId} -> ${ws.tenantId} (${ws.addedAt})`);
4592
+ }
4593
+ finally {
4594
+ closeHippoDb(db);
4595
+ }
4596
+ }
4597
+ function cmdSlackWorkspacesList(hippoRoot) {
4598
+ const db = openHippoDb(hippoRoot);
4599
+ try {
4600
+ const items = listSlackWorkspaces(db);
4601
+ if (items.length === 0) {
4602
+ console.log('(no registered workspaces; routing via HIPPO_TENANT fallback)');
4603
+ return;
4604
+ }
4605
+ for (const ws of items) {
4606
+ console.log(`${ws.teamId}\t${ws.tenantId}\t${ws.addedAt}`);
4607
+ }
4608
+ }
4609
+ finally {
4610
+ closeHippoDb(db);
4611
+ }
4612
+ }
4613
+ function cmdSlackWorkspacesRemove(hippoRoot, flags) {
4614
+ if (flags['help']) {
4615
+ printSlackWorkspacesUsage();
4616
+ return;
4617
+ }
4618
+ const teamId = typeof flags['team'] === 'string' ? flags['team'].trim() : '';
4619
+ if (!teamId) {
4620
+ console.error('Usage: hippo slack workspaces remove --team <T>');
4621
+ process.exit(1);
4622
+ }
4623
+ const db = openHippoDb(hippoRoot);
4624
+ try {
4625
+ const removed = removeSlackWorkspace(db, teamId);
4626
+ if (!removed) {
4627
+ console.error(`no workspace registered for team ${teamId}`);
4628
+ process.exit(1);
4629
+ }
4630
+ console.log(`removed: ${teamId}`);
4631
+ }
4632
+ finally {
4633
+ closeHippoDb(db);
4634
+ }
4635
+ }
4570
4636
  function cmdSlack(hippoRoot, args, flags) {
4571
4637
  const sub = args[0];
4572
4638
  if (sub === 'backfill') {
@@ -4581,7 +4647,24 @@ function cmdSlack(hippoRoot, args, flags) {
4581
4647
  cmdSlackDlqReplay(hippoRoot, args, flags);
4582
4648
  return;
4583
4649
  }
4584
- console.error('Usage: hippo slack <backfill|dlq list|dlq replay <id> [--force]> [...]');
4650
+ if (sub === 'workspaces') {
4651
+ const action = args[1];
4652
+ if (action === 'add') {
4653
+ cmdSlackWorkspacesAdd(hippoRoot, flags);
4654
+ return;
4655
+ }
4656
+ if (action === 'list') {
4657
+ cmdSlackWorkspacesList(hippoRoot);
4658
+ return;
4659
+ }
4660
+ if (action === 'remove') {
4661
+ cmdSlackWorkspacesRemove(hippoRoot, flags);
4662
+ return;
4663
+ }
4664
+ printSlackWorkspacesUsage();
4665
+ process.exit(1);
4666
+ }
4667
+ console.error('Usage: hippo slack <backfill|dlq list|dlq replay <id> [--force]|workspaces add|workspaces list|workspaces remove> [...]');
4585
4668
  process.exit(1);
4586
4669
  }
4587
4670
  function printUsage() {