@agent-native/core 0.15.0 → 0.15.1

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.
Files changed (136) hide show
  1. package/dist/client/dev-mode.d.ts +14 -0
  2. package/dist/client/dev-mode.d.ts.map +1 -0
  3. package/dist/client/dev-mode.js +14 -0
  4. package/dist/client/dev-mode.js.map +1 -0
  5. package/dist/client/extensions/EmbeddedTool.d.ts +20 -0
  6. package/dist/client/extensions/EmbeddedTool.d.ts.map +1 -0
  7. package/dist/client/extensions/EmbeddedTool.js +199 -0
  8. package/dist/client/extensions/EmbeddedTool.js.map +1 -0
  9. package/dist/client/extensions/ToolEditor.d.ts +5 -0
  10. package/dist/client/extensions/ToolEditor.d.ts.map +1 -0
  11. package/dist/client/extensions/ToolEditor.js +129 -0
  12. package/dist/client/extensions/ToolEditor.js.map +1 -0
  13. package/dist/client/extensions/ToolViewer.d.ts +5 -0
  14. package/dist/client/extensions/ToolViewer.d.ts.map +1 -0
  15. package/dist/client/extensions/ToolViewer.js +400 -0
  16. package/dist/client/extensions/ToolViewer.js.map +1 -0
  17. package/dist/client/extensions/ToolViewerPage.d.ts +2 -0
  18. package/dist/client/extensions/ToolViewerPage.d.ts.map +1 -0
  19. package/dist/client/extensions/ToolViewerPage.js +24 -0
  20. package/dist/client/extensions/ToolViewerPage.js.map +1 -0
  21. package/dist/client/extensions/ToolsListPage.d.ts +2 -0
  22. package/dist/client/extensions/ToolsListPage.d.ts.map +1 -0
  23. package/dist/client/extensions/ToolsListPage.js +67 -0
  24. package/dist/client/extensions/ToolsListPage.js.map +1 -0
  25. package/dist/client/extensions/ToolsSidebarSection.d.ts +2 -0
  26. package/dist/client/extensions/ToolsSidebarSection.d.ts.map +1 -0
  27. package/dist/client/extensions/ToolsSidebarSection.js +236 -0
  28. package/dist/client/extensions/ToolsSidebarSection.js.map +1 -0
  29. package/dist/client/extensions/tool-order.d.ts +7 -0
  30. package/dist/client/extensions/tool-order.d.ts.map +1 -0
  31. package/dist/client/extensions/tool-order.js +47 -0
  32. package/dist/client/extensions/tool-order.js.map +1 -0
  33. package/dist/client/theme.js +1 -1
  34. package/dist/client/theme.js.map +1 -1
  35. package/dist/client/tools/EmbeddedTool.d.ts +20 -0
  36. package/dist/client/tools/EmbeddedTool.d.ts.map +1 -0
  37. package/dist/client/tools/EmbeddedTool.js +199 -0
  38. package/dist/client/tools/EmbeddedTool.js.map +1 -0
  39. package/dist/client/tools/ExtensionSlot.d.ts +27 -0
  40. package/dist/client/tools/ExtensionSlot.d.ts.map +1 -0
  41. package/dist/client/tools/ExtensionSlot.js +96 -0
  42. package/dist/client/tools/ExtensionSlot.js.map +1 -0
  43. package/dist/client/tools/ToolEditor.d.ts +5 -0
  44. package/dist/client/tools/ToolEditor.d.ts.map +1 -0
  45. package/dist/client/tools/ToolEditor.js +129 -0
  46. package/dist/client/tools/ToolEditor.js.map +1 -0
  47. package/dist/client/tools/ToolViewer.d.ts +5 -0
  48. package/dist/client/tools/ToolViewer.d.ts.map +1 -0
  49. package/dist/client/tools/ToolViewer.js +400 -0
  50. package/dist/client/tools/ToolViewer.js.map +1 -0
  51. package/dist/client/tools/ToolViewerPage.d.ts +2 -0
  52. package/dist/client/tools/ToolViewerPage.d.ts.map +1 -0
  53. package/dist/client/tools/ToolViewerPage.js +24 -0
  54. package/dist/client/tools/ToolViewerPage.js.map +1 -0
  55. package/dist/client/tools/ToolsListPage.d.ts +2 -0
  56. package/dist/client/tools/ToolsListPage.d.ts.map +1 -0
  57. package/dist/client/tools/ToolsListPage.js +67 -0
  58. package/dist/client/tools/ToolsListPage.js.map +1 -0
  59. package/dist/client/tools/ToolsSidebarSection.d.ts +2 -0
  60. package/dist/client/tools/ToolsSidebarSection.d.ts.map +1 -0
  61. package/dist/client/tools/ToolsSidebarSection.js +236 -0
  62. package/dist/client/tools/ToolsSidebarSection.js.map +1 -0
  63. package/dist/client/tools/iframe-bridge.d.ts +38 -0
  64. package/dist/client/tools/iframe-bridge.d.ts.map +1 -0
  65. package/dist/client/tools/iframe-bridge.js +207 -0
  66. package/dist/client/tools/iframe-bridge.js.map +1 -0
  67. package/dist/client/tools/index.d.ts +8 -0
  68. package/dist/client/tools/index.d.ts.map +1 -0
  69. package/dist/client/tools/index.js +8 -0
  70. package/dist/client/tools/index.js.map +1 -0
  71. package/dist/client/tools/tool-order.d.ts +7 -0
  72. package/dist/client/tools/tool-order.d.ts.map +1 -0
  73. package/dist/client/tools/tool-order.js +47 -0
  74. package/dist/client/tools/tool-order.js.map +1 -0
  75. package/dist/server/auth.d.ts.map +1 -1
  76. package/dist/server/auth.js +44 -6
  77. package/dist/server/auth.js.map +1 -1
  78. package/dist/server/google-auth-plugin.d.ts.map +1 -1
  79. package/dist/server/google-auth-plugin.js +15 -7
  80. package/dist/server/google-auth-plugin.js.map +1 -1
  81. package/dist/server/local-migration.d.ts +41 -0
  82. package/dist/server/local-migration.d.ts.map +1 -0
  83. package/dist/server/local-migration.js +235 -0
  84. package/dist/server/local-migration.js.map +1 -0
  85. package/dist/server/onboarding-html.d.ts.map +1 -1
  86. package/dist/server/onboarding-html.js +15 -7
  87. package/dist/server/onboarding-html.js.map +1 -1
  88. package/dist/tools/actions.d.ts +3 -0
  89. package/dist/tools/actions.d.ts.map +1 -0
  90. package/dist/tools/actions.js +272 -0
  91. package/dist/tools/actions.js.map +1 -0
  92. package/dist/tools/fetch-tool.d.ts +23 -0
  93. package/dist/tools/fetch-tool.d.ts.map +1 -0
  94. package/dist/tools/fetch-tool.js +178 -0
  95. package/dist/tools/fetch-tool.js.map +1 -0
  96. package/dist/tools/html-shell.d.ts +45 -0
  97. package/dist/tools/html-shell.d.ts.map +1 -0
  98. package/dist/tools/html-shell.js +514 -0
  99. package/dist/tools/html-shell.js.map +1 -0
  100. package/dist/tools/proxy-security.d.ts +12 -0
  101. package/dist/tools/proxy-security.d.ts.map +1 -0
  102. package/dist/tools/proxy-security.js +158 -0
  103. package/dist/tools/proxy-security.js.map +1 -0
  104. package/dist/tools/routes.d.ts +2 -0
  105. package/dist/tools/routes.d.ts.map +1 -0
  106. package/dist/tools/routes.js +627 -0
  107. package/dist/tools/routes.js.map +1 -0
  108. package/dist/tools/schema.d.ts +664 -0
  109. package/dist/tools/schema.d.ts.map +1 -0
  110. package/dist/tools/schema.js +146 -0
  111. package/dist/tools/schema.js.map +1 -0
  112. package/dist/tools/slots/routes.d.ts +15 -0
  113. package/dist/tools/slots/routes.d.ts.map +1 -0
  114. package/dist/tools/slots/routes.js +94 -0
  115. package/dist/tools/slots/routes.js.map +1 -0
  116. package/dist/tools/slots/schema.d.ts +303 -0
  117. package/dist/tools/slots/schema.d.ts.map +1 -0
  118. package/dist/tools/slots/schema.js +76 -0
  119. package/dist/tools/slots/schema.js.map +1 -0
  120. package/dist/tools/slots/store.d.ts +66 -0
  121. package/dist/tools/slots/store.d.ts.map +1 -0
  122. package/dist/tools/slots/store.js +227 -0
  123. package/dist/tools/slots/store.js.map +1 -0
  124. package/dist/tools/store.d.ts +40 -0
  125. package/dist/tools/store.d.ts.map +1 -0
  126. package/dist/tools/store.js +193 -0
  127. package/dist/tools/store.js.map +1 -0
  128. package/dist/tools/theme.d.ts +2 -0
  129. package/dist/tools/theme.d.ts.map +1 -0
  130. package/dist/tools/theme.js +67 -0
  131. package/dist/tools/theme.js.map +1 -0
  132. package/dist/tools/url-safety.d.ts +24 -0
  133. package/dist/tools/url-safety.d.ts.map +1 -0
  134. package/dist/tools/url-safety.js +224 -0
  135. package/dist/tools/url-safety.js.map +1 -0
  136. package/package.json +1 -1
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Drizzle schema for the tool extension-points system.
3
+ *
4
+ * Two tables:
5
+ *
6
+ * - `tool_slots` — declarations: "tool X can render in slot Y". Authored
7
+ * once per tool, regardless of installer.
8
+ * - `tool_slot_installs` — per-user installs: "user U wants tool X in slot Y at
9
+ * position N". Always scoped by owner_email.
10
+ *
11
+ * Neither table spreads `ownableColumns()` — they're not first-class shareable
12
+ * resources. Access to the underlying tool flows through the existing `tools`
13
+ * table sharing model; install rows are personal preferences scoped to the
14
+ * installing user.
15
+ */
16
+ import { table, text, integer, now } from "../../db/schema.js";
17
+ export const toolSlots = table("tool_slots", {
18
+ id: text("id").primaryKey(),
19
+ toolId: text("tool_id").notNull(),
20
+ slotId: text("slot_id").notNull(),
21
+ config: text("config"),
22
+ createdAt: text("created_at").notNull().default(now()),
23
+ });
24
+ export const toolSlotInstalls = table("tool_slot_installs", {
25
+ id: text("id").primaryKey(),
26
+ toolId: text("tool_id").notNull(),
27
+ slotId: text("slot_id").notNull(),
28
+ ownerEmail: text("owner_email").notNull(),
29
+ orgId: text("org_id"),
30
+ position: integer("position").notNull().default(0),
31
+ config: text("config"),
32
+ createdAt: text("created_at").notNull().default(now()),
33
+ updatedAt: text("updated_at").notNull().default(now()),
34
+ });
35
+ export const TOOL_SLOTS_CREATE_SQL = `CREATE TABLE IF NOT EXISTS tool_slots (
36
+ id TEXT PRIMARY KEY,
37
+ tool_id TEXT NOT NULL,
38
+ slot_id TEXT NOT NULL,
39
+ config TEXT,
40
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
41
+ )`;
42
+ export const TOOL_SLOTS_CREATE_SQL_PG = `CREATE TABLE IF NOT EXISTS tool_slots (
43
+ id TEXT PRIMARY KEY,
44
+ tool_id TEXT NOT NULL,
45
+ slot_id TEXT NOT NULL,
46
+ config TEXT,
47
+ created_at TEXT NOT NULL DEFAULT now()
48
+ )`;
49
+ export const TOOL_SLOTS_BY_SLOT_INDEX_SQL = `CREATE INDEX IF NOT EXISTS tool_slots_by_slot_idx ON tool_slots (slot_id)`;
50
+ export const TOOL_SLOTS_BY_TOOL_INDEX_SQL = `CREATE INDEX IF NOT EXISTS tool_slots_by_tool_idx ON tool_slots (tool_id)`;
51
+ export const TOOL_SLOTS_UNIQUE_INDEX_SQL = `CREATE UNIQUE INDEX IF NOT EXISTS tool_slots_unique_idx ON tool_slots (tool_id, slot_id)`;
52
+ export const TOOL_SLOT_INSTALLS_CREATE_SQL = `CREATE TABLE IF NOT EXISTS tool_slot_installs (
53
+ id TEXT PRIMARY KEY,
54
+ tool_id TEXT NOT NULL,
55
+ slot_id TEXT NOT NULL,
56
+ owner_email TEXT NOT NULL,
57
+ org_id TEXT,
58
+ position INTEGER NOT NULL DEFAULT 0,
59
+ config TEXT,
60
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
61
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
62
+ )`;
63
+ export const TOOL_SLOT_INSTALLS_CREATE_SQL_PG = `CREATE TABLE IF NOT EXISTS tool_slot_installs (
64
+ id TEXT PRIMARY KEY,
65
+ tool_id TEXT NOT NULL,
66
+ slot_id TEXT NOT NULL,
67
+ owner_email TEXT NOT NULL,
68
+ org_id TEXT,
69
+ position INTEGER NOT NULL DEFAULT 0,
70
+ config TEXT,
71
+ created_at TEXT NOT NULL DEFAULT now(),
72
+ updated_at TEXT NOT NULL DEFAULT now()
73
+ )`;
74
+ export const TOOL_SLOT_INSTALLS_BY_USER_SLOT_INDEX_SQL = `CREATE INDEX IF NOT EXISTS tool_slot_installs_by_user_slot_idx ON tool_slot_installs (owner_email, slot_id)`;
75
+ export const TOOL_SLOT_INSTALLS_UNIQUE_INDEX_SQL = `CREATE UNIQUE INDEX IF NOT EXISTS tool_slot_installs_unique_idx ON tool_slot_installs (owner_email, tool_id, slot_id)`;
76
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/tools/slots/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC;AAE/D,MAAM,CAAC,MAAM,SAAS,GAAG,KAAK,CAAC,YAAY,EAAE;IAC3C,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE;IAC3B,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE;IACjC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE;IACjC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC;IACtB,SAAS,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;CACvD,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,CAAC,oBAAoB,EAAE;IAC1D,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE;IAC3B,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE;IACjC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE;IACjC,UAAU,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC,OAAO,EAAE;IACzC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAClD,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC;IACtB,SAAS,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;IACtD,SAAS,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;CACvD,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,qBAAqB,GAAG;;;;;;EAMnC,CAAC;AAEH,MAAM,CAAC,MAAM,wBAAwB,GAAG;;;;;;EAMtC,CAAC;AAEH,MAAM,CAAC,MAAM,4BAA4B,GAAG,2EAA2E,CAAC;AACxH,MAAM,CAAC,MAAM,4BAA4B,GAAG,2EAA2E,CAAC;AACxH,MAAM,CAAC,MAAM,2BAA2B,GAAG,0FAA0F,CAAC;AAEtI,MAAM,CAAC,MAAM,6BAA6B,GAAG;;;;;;;;;;EAU3C,CAAC;AAEH,MAAM,CAAC,MAAM,gCAAgC,GAAG;;;;;;;;;;EAU9C,CAAC;AAEH,MAAM,CAAC,MAAM,yCAAyC,GAAG,6GAA6G,CAAC;AACvK,MAAM,CAAC,MAAM,mCAAmC,GAAG,uHAAuH,CAAC","sourcesContent":["/**\n * Drizzle schema for the tool extension-points system.\n *\n * Two tables:\n *\n * - `tool_slots` — declarations: \"tool X can render in slot Y\". Authored\n * once per tool, regardless of installer.\n * - `tool_slot_installs` — per-user installs: \"user U wants tool X in slot Y at\n * position N\". Always scoped by owner_email.\n *\n * Neither table spreads `ownableColumns()` — they're not first-class shareable\n * resources. Access to the underlying tool flows through the existing `tools`\n * table sharing model; install rows are personal preferences scoped to the\n * installing user.\n */\n\nimport { table, text, integer, now } from \"../../db/schema.js\";\n\nexport const toolSlots = table(\"tool_slots\", {\n id: text(\"id\").primaryKey(),\n toolId: text(\"tool_id\").notNull(),\n slotId: text(\"slot_id\").notNull(),\n config: text(\"config\"),\n createdAt: text(\"created_at\").notNull().default(now()),\n});\n\nexport const toolSlotInstalls = table(\"tool_slot_installs\", {\n id: text(\"id\").primaryKey(),\n toolId: text(\"tool_id\").notNull(),\n slotId: text(\"slot_id\").notNull(),\n ownerEmail: text(\"owner_email\").notNull(),\n orgId: text(\"org_id\"),\n position: integer(\"position\").notNull().default(0),\n config: text(\"config\"),\n createdAt: text(\"created_at\").notNull().default(now()),\n updatedAt: text(\"updated_at\").notNull().default(now()),\n});\n\nexport const TOOL_SLOTS_CREATE_SQL = `CREATE TABLE IF NOT EXISTS tool_slots (\n id TEXT PRIMARY KEY,\n tool_id TEXT NOT NULL,\n slot_id TEXT NOT NULL,\n config TEXT,\n created_at TEXT NOT NULL DEFAULT (datetime('now'))\n)`;\n\nexport const TOOL_SLOTS_CREATE_SQL_PG = `CREATE TABLE IF NOT EXISTS tool_slots (\n id TEXT PRIMARY KEY,\n tool_id TEXT NOT NULL,\n slot_id TEXT NOT NULL,\n config TEXT,\n created_at TEXT NOT NULL DEFAULT now()\n)`;\n\nexport const TOOL_SLOTS_BY_SLOT_INDEX_SQL = `CREATE INDEX IF NOT EXISTS tool_slots_by_slot_idx ON tool_slots (slot_id)`;\nexport const TOOL_SLOTS_BY_TOOL_INDEX_SQL = `CREATE INDEX IF NOT EXISTS tool_slots_by_tool_idx ON tool_slots (tool_id)`;\nexport const TOOL_SLOTS_UNIQUE_INDEX_SQL = `CREATE UNIQUE INDEX IF NOT EXISTS tool_slots_unique_idx ON tool_slots (tool_id, slot_id)`;\n\nexport const TOOL_SLOT_INSTALLS_CREATE_SQL = `CREATE TABLE IF NOT EXISTS tool_slot_installs (\n id TEXT PRIMARY KEY,\n tool_id TEXT NOT NULL,\n slot_id TEXT NOT NULL,\n owner_email TEXT NOT NULL,\n org_id TEXT,\n position INTEGER NOT NULL DEFAULT 0,\n config TEXT,\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n updated_at TEXT NOT NULL DEFAULT (datetime('now'))\n)`;\n\nexport const TOOL_SLOT_INSTALLS_CREATE_SQL_PG = `CREATE TABLE IF NOT EXISTS tool_slot_installs (\n id TEXT PRIMARY KEY,\n tool_id TEXT NOT NULL,\n slot_id TEXT NOT NULL,\n owner_email TEXT NOT NULL,\n org_id TEXT,\n position INTEGER NOT NULL DEFAULT 0,\n config TEXT,\n created_at TEXT NOT NULL DEFAULT now(),\n updated_at TEXT NOT NULL DEFAULT now()\n)`;\n\nexport const TOOL_SLOT_INSTALLS_BY_USER_SLOT_INDEX_SQL = `CREATE INDEX IF NOT EXISTS tool_slot_installs_by_user_slot_idx ON tool_slot_installs (owner_email, slot_id)`;\nexport const TOOL_SLOT_INSTALLS_UNIQUE_INDEX_SQL = `CREATE UNIQUE INDEX IF NOT EXISTS tool_slot_installs_unique_idx ON tool_slot_installs (owner_email, tool_id, slot_id)`;\n"]}
@@ -0,0 +1,66 @@
1
+ export declare function ensureSlotTables(): Promise<void>;
2
+ export interface ToolSlotRow {
3
+ id: string;
4
+ toolId: string;
5
+ slotId: string;
6
+ config: string | null;
7
+ createdAt: string;
8
+ }
9
+ export interface ToolSlotInstallRow {
10
+ id: string;
11
+ toolId: string;
12
+ slotId: string;
13
+ ownerEmail: string;
14
+ orgId: string | null;
15
+ position: number;
16
+ config: string | null;
17
+ createdAt: string;
18
+ updatedAt: string;
19
+ }
20
+ /**
21
+ * Declare that a tool can render in a slot. Caller must have editor access on
22
+ * the tool (only people who can edit a tool can change its slot targets).
23
+ */
24
+ export declare function addToolSlotTarget(toolId: string, slotId: string, config?: string): Promise<ToolSlotRow>;
25
+ export declare function removeToolSlotTarget(toolId: string, slotId: string): Promise<boolean>;
26
+ export declare function listSlotsForTool(toolId: string): Promise<ToolSlotRow[]>;
27
+ /**
28
+ * List tools that declare a slot — but only tools the current user has access
29
+ * to. Joins through the tools access filter.
30
+ */
31
+ export declare function listToolsForSlot(slotId: string): Promise<Array<{
32
+ toolId: string;
33
+ name: string;
34
+ description: string;
35
+ icon: string | null;
36
+ config: string | null;
37
+ }>>;
38
+ /**
39
+ * Install a tool into a slot for the current user. Verifies the user has at
40
+ * least viewer access to the tool. Idempotent — re-installing returns the
41
+ * existing row.
42
+ */
43
+ export declare function installToolSlot(toolId: string, slotId: string, opts?: {
44
+ position?: number;
45
+ config?: string;
46
+ }): Promise<ToolSlotInstallRow>;
47
+ export declare function uninstallToolSlot(toolId: string, slotId: string): Promise<boolean>;
48
+ /**
49
+ * List the current user's installs for a slot. Joins with `tools` so the
50
+ * caller gets tool name/description/icon/updatedAt without a second query.
51
+ * Tools the user has lost access to are silently skipped (lazy garbage
52
+ * collection).
53
+ */
54
+ export declare function listSlotInstallsForUser(slotId: string): Promise<Array<{
55
+ installId: string;
56
+ toolId: string;
57
+ name: string;
58
+ description: string;
59
+ icon: string | null;
60
+ updatedAt: string;
61
+ position: number;
62
+ config: string | null;
63
+ }>>;
64
+ /** Delete every slot/install row referencing a tool. Called from deleteTool. */
65
+ export declare function cascadeDeleteToolSlots(toolId: string): Promise<void>;
66
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../../src/tools/slots/store.ts"],"names":[],"mappings":"AA4BA,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAmBtD;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,WAAW,CAAC,CA+BtB;AAED,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,OAAO,CAAC,CAQlB;AAED,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAS7E;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAC7D,KAAK,CAAC;IACJ,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,CAAC,CACH,CA8BA;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5C,OAAO,CAAC,kBAAkB,CAAC,CA+C7B;AAED,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,OAAO,CAAC,CAclB;AAED;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CACpE,KAAK,CAAC;IACJ,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,CAAC,CACH,CA4CA;AAED,gFAAgF;AAChF,wBAAsB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAK1E"}
@@ -0,0 +1,227 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { and, eq, inArray, sql } from "drizzle-orm";
3
+ import { getDbExec, isPostgres } from "../../db/client.js";
4
+ import { createGetDb } from "../../db/create-get-db.js";
5
+ import { accessFilter, assertAccess } from "../../sharing/access.js";
6
+ import { getRequestUserEmail, getRequestOrgId, } from "../../server/request-context.js";
7
+ import { tools, toolShares } from "../schema.js";
8
+ import { toolSlots, toolSlotInstalls, TOOL_SLOTS_CREATE_SQL, TOOL_SLOTS_CREATE_SQL_PG, TOOL_SLOTS_BY_SLOT_INDEX_SQL, TOOL_SLOTS_BY_TOOL_INDEX_SQL, TOOL_SLOTS_UNIQUE_INDEX_SQL, TOOL_SLOT_INSTALLS_CREATE_SQL, TOOL_SLOT_INSTALLS_CREATE_SQL_PG, TOOL_SLOT_INSTALLS_BY_USER_SLOT_INDEX_SQL, TOOL_SLOT_INSTALLS_UNIQUE_INDEX_SQL, } from "./schema.js";
9
+ const getDb = createGetDb({ tools, toolShares, toolSlots, toolSlotInstalls });
10
+ let _initPromise;
11
+ export async function ensureSlotTables() {
12
+ if (!_initPromise) {
13
+ _initPromise = (async () => {
14
+ const client = getDbExec();
15
+ const pg = isPostgres();
16
+ await client.execute(pg ? TOOL_SLOTS_CREATE_SQL_PG : TOOL_SLOTS_CREATE_SQL);
17
+ await client.execute(TOOL_SLOTS_BY_SLOT_INDEX_SQL);
18
+ await client.execute(TOOL_SLOTS_BY_TOOL_INDEX_SQL);
19
+ await client.execute(TOOL_SLOTS_UNIQUE_INDEX_SQL);
20
+ await client.execute(pg ? TOOL_SLOT_INSTALLS_CREATE_SQL_PG : TOOL_SLOT_INSTALLS_CREATE_SQL);
21
+ await client.execute(TOOL_SLOT_INSTALLS_BY_USER_SLOT_INDEX_SQL);
22
+ await client.execute(TOOL_SLOT_INSTALLS_UNIQUE_INDEX_SQL);
23
+ })();
24
+ }
25
+ return _initPromise;
26
+ }
27
+ /**
28
+ * Declare that a tool can render in a slot. Caller must have editor access on
29
+ * the tool (only people who can edit a tool can change its slot targets).
30
+ */
31
+ export async function addToolSlotTarget(toolId, slotId, config) {
32
+ await ensureSlotTables();
33
+ await assertAccess("tool", toolId, "editor");
34
+ const db = getDb();
35
+ const id = randomUUID();
36
+ const createdAt = new Date().toISOString();
37
+ const row = {
38
+ id,
39
+ toolId,
40
+ slotId,
41
+ config: config ?? null,
42
+ createdAt,
43
+ };
44
+ try {
45
+ await db.insert(toolSlots).values(row);
46
+ }
47
+ catch (err) {
48
+ // Unique index hit — already declared. Treat as idempotent: return existing.
49
+ if (String(err?.message ?? err)
50
+ .toLowerCase()
51
+ .includes("unique")) {
52
+ const existing = await db
53
+ .select()
54
+ .from(toolSlots)
55
+ .where(and(eq(toolSlots.toolId, toolId), eq(toolSlots.slotId, slotId)));
56
+ if (existing[0])
57
+ return existing[0];
58
+ }
59
+ throw err;
60
+ }
61
+ return row;
62
+ }
63
+ export async function removeToolSlotTarget(toolId, slotId) {
64
+ await ensureSlotTables();
65
+ await assertAccess("tool", toolId, "editor");
66
+ const db = getDb();
67
+ await db
68
+ .delete(toolSlots)
69
+ .where(and(eq(toolSlots.toolId, toolId), eq(toolSlots.slotId, slotId)));
70
+ return true;
71
+ }
72
+ export async function listSlotsForTool(toolId) {
73
+ await ensureSlotTables();
74
+ await assertAccess("tool", toolId, "viewer");
75
+ const db = getDb();
76
+ const rows = await db
77
+ .select()
78
+ .from(toolSlots)
79
+ .where(eq(toolSlots.toolId, toolId));
80
+ return rows;
81
+ }
82
+ /**
83
+ * List tools that declare a slot — but only tools the current user has access
84
+ * to. Joins through the tools access filter.
85
+ */
86
+ export async function listToolsForSlot(slotId) {
87
+ await ensureSlotTables();
88
+ const db = getDb();
89
+ // Pull tools the user can see, then narrow to ones declaring this slot.
90
+ const accessible = await db
91
+ .select({
92
+ id: tools.id,
93
+ name: tools.name,
94
+ description: tools.description,
95
+ icon: tools.icon,
96
+ })
97
+ .from(tools)
98
+ .where(accessFilter(tools, toolShares));
99
+ if (accessible.length === 0)
100
+ return [];
101
+ const ids = accessible.map((t) => t.id);
102
+ const declarations = await db
103
+ .select()
104
+ .from(toolSlots)
105
+ .where(and(eq(toolSlots.slotId, slotId), inArray(toolSlots.toolId, ids)));
106
+ const byId = new Map(accessible.map((t) => [t.id, t]));
107
+ return declarations.map((d) => {
108
+ const t = byId.get(d.toolId);
109
+ return {
110
+ toolId: d.toolId,
111
+ name: t.name,
112
+ description: t.description,
113
+ icon: t.icon,
114
+ config: d.config,
115
+ };
116
+ });
117
+ }
118
+ /**
119
+ * Install a tool into a slot for the current user. Verifies the user has at
120
+ * least viewer access to the tool. Idempotent — re-installing returns the
121
+ * existing row.
122
+ */
123
+ export async function installToolSlot(toolId, slotId, opts) {
124
+ await ensureSlotTables();
125
+ await assertAccess("tool", toolId, "viewer");
126
+ const userEmail = requireUserEmail();
127
+ const orgId = getRequestOrgId();
128
+ const db = getDb();
129
+ const existing = await db
130
+ .select()
131
+ .from(toolSlotInstalls)
132
+ .where(and(eq(toolSlotInstalls.ownerEmail, userEmail), eq(toolSlotInstalls.toolId, toolId), eq(toolSlotInstalls.slotId, slotId)));
133
+ if (existing[0])
134
+ return existing[0];
135
+ const id = randomUUID();
136
+ const now = new Date().toISOString();
137
+ let position = opts?.position;
138
+ if (position === undefined) {
139
+ const rows = await db
140
+ .select({ pos: sql `MAX(${toolSlotInstalls.position})` })
141
+ .from(toolSlotInstalls)
142
+ .where(and(eq(toolSlotInstalls.ownerEmail, userEmail), eq(toolSlotInstalls.slotId, slotId)));
143
+ const maxPos = Number(rows[0]?.pos ?? -1);
144
+ position = Number.isFinite(maxPos) ? maxPos + 1 : 0;
145
+ }
146
+ const row = {
147
+ id,
148
+ toolId,
149
+ slotId,
150
+ ownerEmail: userEmail,
151
+ orgId: orgId ?? null,
152
+ position,
153
+ config: opts?.config ?? null,
154
+ createdAt: now,
155
+ updatedAt: now,
156
+ };
157
+ await db.insert(toolSlotInstalls).values(row);
158
+ return row;
159
+ }
160
+ export async function uninstallToolSlot(toolId, slotId) {
161
+ await ensureSlotTables();
162
+ const userEmail = requireUserEmail();
163
+ const db = getDb();
164
+ await db
165
+ .delete(toolSlotInstalls)
166
+ .where(and(eq(toolSlotInstalls.ownerEmail, userEmail), eq(toolSlotInstalls.toolId, toolId), eq(toolSlotInstalls.slotId, slotId)));
167
+ return true;
168
+ }
169
+ /**
170
+ * List the current user's installs for a slot. Joins with `tools` so the
171
+ * caller gets tool name/description/icon/updatedAt without a second query.
172
+ * Tools the user has lost access to are silently skipped (lazy garbage
173
+ * collection).
174
+ */
175
+ export async function listSlotInstallsForUser(slotId) {
176
+ await ensureSlotTables();
177
+ const userEmail = requireUserEmail();
178
+ const db = getDb();
179
+ const installs = await db
180
+ .select()
181
+ .from(toolSlotInstalls)
182
+ .where(and(eq(toolSlotInstalls.ownerEmail, userEmail), eq(toolSlotInstalls.slotId, slotId)));
183
+ if (installs.length === 0)
184
+ return [];
185
+ const accessible = await db
186
+ .select({
187
+ id: tools.id,
188
+ name: tools.name,
189
+ description: tools.description,
190
+ icon: tools.icon,
191
+ updatedAt: tools.updatedAt,
192
+ })
193
+ .from(tools)
194
+ .where(accessFilter(tools, toolShares));
195
+ const byId = new Map(accessible.map((t) => [t.id, t]));
196
+ return installs
197
+ .filter((i) => byId.has(i.toolId))
198
+ .sort((a, b) => a.position - b.position)
199
+ .map((i) => {
200
+ const t = byId.get(i.toolId);
201
+ return {
202
+ installId: i.id,
203
+ toolId: i.toolId,
204
+ name: t.name,
205
+ description: t.description,
206
+ icon: t.icon,
207
+ updatedAt: t.updatedAt,
208
+ position: i.position,
209
+ config: i.config,
210
+ };
211
+ });
212
+ }
213
+ /** Delete every slot/install row referencing a tool. Called from deleteTool. */
214
+ export async function cascadeDeleteToolSlots(toolId) {
215
+ await ensureSlotTables();
216
+ const db = getDb();
217
+ await db.delete(toolSlots).where(eq(toolSlots.toolId, toolId));
218
+ await db.delete(toolSlotInstalls).where(eq(toolSlotInstalls.toolId, toolId));
219
+ }
220
+ function requireUserEmail() {
221
+ const email = getRequestUserEmail();
222
+ if (!email) {
223
+ throw new Error("Slot operations require an authenticated user.");
224
+ }
225
+ return email;
226
+ }
227
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../../src/tools/slots/store.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { and, eq, inArray, sql } from \"drizzle-orm\";\nimport { getDbExec, isPostgres } from \"../../db/client.js\";\nimport { createGetDb } from \"../../db/create-get-db.js\";\nimport { accessFilter, assertAccess } from \"../../sharing/access.js\";\nimport {\n getRequestUserEmail,\n getRequestOrgId,\n} from \"../../server/request-context.js\";\nimport { tools, toolShares } from \"../schema.js\";\nimport {\n toolSlots,\n toolSlotInstalls,\n TOOL_SLOTS_CREATE_SQL,\n TOOL_SLOTS_CREATE_SQL_PG,\n TOOL_SLOTS_BY_SLOT_INDEX_SQL,\n TOOL_SLOTS_BY_TOOL_INDEX_SQL,\n TOOL_SLOTS_UNIQUE_INDEX_SQL,\n TOOL_SLOT_INSTALLS_CREATE_SQL,\n TOOL_SLOT_INSTALLS_CREATE_SQL_PG,\n TOOL_SLOT_INSTALLS_BY_USER_SLOT_INDEX_SQL,\n TOOL_SLOT_INSTALLS_UNIQUE_INDEX_SQL,\n} from \"./schema.js\";\n\nconst getDb = createGetDb({ tools, toolShares, toolSlots, toolSlotInstalls });\n\nlet _initPromise: Promise<void> | undefined;\n\nexport async function ensureSlotTables(): Promise<void> {\n if (!_initPromise) {\n _initPromise = (async () => {\n const client = getDbExec();\n const pg = isPostgres();\n await client.execute(\n pg ? TOOL_SLOTS_CREATE_SQL_PG : TOOL_SLOTS_CREATE_SQL,\n );\n await client.execute(TOOL_SLOTS_BY_SLOT_INDEX_SQL);\n await client.execute(TOOL_SLOTS_BY_TOOL_INDEX_SQL);\n await client.execute(TOOL_SLOTS_UNIQUE_INDEX_SQL);\n await client.execute(\n pg ? TOOL_SLOT_INSTALLS_CREATE_SQL_PG : TOOL_SLOT_INSTALLS_CREATE_SQL,\n );\n await client.execute(TOOL_SLOT_INSTALLS_BY_USER_SLOT_INDEX_SQL);\n await client.execute(TOOL_SLOT_INSTALLS_UNIQUE_INDEX_SQL);\n })();\n }\n return _initPromise;\n}\n\nexport interface ToolSlotRow {\n id: string;\n toolId: string;\n slotId: string;\n config: string | null;\n createdAt: string;\n}\n\nexport interface ToolSlotInstallRow {\n id: string;\n toolId: string;\n slotId: string;\n ownerEmail: string;\n orgId: string | null;\n position: number;\n config: string | null;\n createdAt: string;\n updatedAt: string;\n}\n\n/**\n * Declare that a tool can render in a slot. Caller must have editor access on\n * the tool (only people who can edit a tool can change its slot targets).\n */\nexport async function addToolSlotTarget(\n toolId: string,\n slotId: string,\n config?: string,\n): Promise<ToolSlotRow> {\n await ensureSlotTables();\n await assertAccess(\"tool\", toolId, \"editor\");\n const db = getDb();\n const id = randomUUID();\n const createdAt = new Date().toISOString();\n const row: ToolSlotRow = {\n id,\n toolId,\n slotId,\n config: config ?? null,\n createdAt,\n };\n try {\n await db.insert(toolSlots).values(row);\n } catch (err: any) {\n // Unique index hit — already declared. Treat as idempotent: return existing.\n if (\n String(err?.message ?? err)\n .toLowerCase()\n .includes(\"unique\")\n ) {\n const existing = await db\n .select()\n .from(toolSlots)\n .where(and(eq(toolSlots.toolId, toolId), eq(toolSlots.slotId, slotId)));\n if (existing[0]) return existing[0] as ToolSlotRow;\n }\n throw err;\n }\n return row;\n}\n\nexport async function removeToolSlotTarget(\n toolId: string,\n slotId: string,\n): Promise<boolean> {\n await ensureSlotTables();\n await assertAccess(\"tool\", toolId, \"editor\");\n const db = getDb();\n await db\n .delete(toolSlots)\n .where(and(eq(toolSlots.toolId, toolId), eq(toolSlots.slotId, slotId)));\n return true;\n}\n\nexport async function listSlotsForTool(toolId: string): Promise<ToolSlotRow[]> {\n await ensureSlotTables();\n await assertAccess(\"tool\", toolId, \"viewer\");\n const db = getDb();\n const rows = await db\n .select()\n .from(toolSlots)\n .where(eq(toolSlots.toolId, toolId));\n return rows as ToolSlotRow[];\n}\n\n/**\n * List tools that declare a slot — but only tools the current user has access\n * to. Joins through the tools access filter.\n */\nexport async function listToolsForSlot(slotId: string): Promise<\n Array<{\n toolId: string;\n name: string;\n description: string;\n icon: string | null;\n config: string | null;\n }>\n> {\n await ensureSlotTables();\n const db = getDb();\n // Pull tools the user can see, then narrow to ones declaring this slot.\n const accessible = await db\n .select({\n id: tools.id,\n name: tools.name,\n description: tools.description,\n icon: tools.icon,\n })\n .from(tools)\n .where(accessFilter(tools, toolShares));\n if (accessible.length === 0) return [];\n const ids = accessible.map((t: any) => t.id);\n const declarations = await db\n .select()\n .from(toolSlots)\n .where(and(eq(toolSlots.slotId, slotId), inArray(toolSlots.toolId, ids)));\n const byId = new Map(accessible.map((t: any) => [t.id, t]));\n return (declarations as ToolSlotRow[]).map((d) => {\n const t = byId.get(d.toolId)!;\n return {\n toolId: d.toolId,\n name: t.name,\n description: t.description,\n icon: t.icon,\n config: d.config,\n };\n });\n}\n\n/**\n * Install a tool into a slot for the current user. Verifies the user has at\n * least viewer access to the tool. Idempotent — re-installing returns the\n * existing row.\n */\nexport async function installToolSlot(\n toolId: string,\n slotId: string,\n opts?: { position?: number; config?: string },\n): Promise<ToolSlotInstallRow> {\n await ensureSlotTables();\n await assertAccess(\"tool\", toolId, \"viewer\");\n const userEmail = requireUserEmail();\n const orgId = getRequestOrgId();\n const db = getDb();\n const existing = await db\n .select()\n .from(toolSlotInstalls)\n .where(\n and(\n eq(toolSlotInstalls.ownerEmail, userEmail),\n eq(toolSlotInstalls.toolId, toolId),\n eq(toolSlotInstalls.slotId, slotId),\n ),\n );\n if (existing[0]) return existing[0] as ToolSlotInstallRow;\n\n const id = randomUUID();\n const now = new Date().toISOString();\n let position = opts?.position;\n if (position === undefined) {\n const rows = await db\n .select({ pos: sql<number>`MAX(${toolSlotInstalls.position})` })\n .from(toolSlotInstalls)\n .where(\n and(\n eq(toolSlotInstalls.ownerEmail, userEmail),\n eq(toolSlotInstalls.slotId, slotId),\n ),\n );\n const maxPos = Number((rows[0] as any)?.pos ?? -1);\n position = Number.isFinite(maxPos) ? maxPos + 1 : 0;\n }\n const row: ToolSlotInstallRow = {\n id,\n toolId,\n slotId,\n ownerEmail: userEmail,\n orgId: orgId ?? null,\n position,\n config: opts?.config ?? null,\n createdAt: now,\n updatedAt: now,\n };\n await db.insert(toolSlotInstalls).values(row);\n return row;\n}\n\nexport async function uninstallToolSlot(\n toolId: string,\n slotId: string,\n): Promise<boolean> {\n await ensureSlotTables();\n const userEmail = requireUserEmail();\n const db = getDb();\n await db\n .delete(toolSlotInstalls)\n .where(\n and(\n eq(toolSlotInstalls.ownerEmail, userEmail),\n eq(toolSlotInstalls.toolId, toolId),\n eq(toolSlotInstalls.slotId, slotId),\n ),\n );\n return true;\n}\n\n/**\n * List the current user's installs for a slot. Joins with `tools` so the\n * caller gets tool name/description/icon/updatedAt without a second query.\n * Tools the user has lost access to are silently skipped (lazy garbage\n * collection).\n */\nexport async function listSlotInstallsForUser(slotId: string): Promise<\n Array<{\n installId: string;\n toolId: string;\n name: string;\n description: string;\n icon: string | null;\n updatedAt: string;\n position: number;\n config: string | null;\n }>\n> {\n await ensureSlotTables();\n const userEmail = requireUserEmail();\n const db = getDb();\n\n const installs = await db\n .select()\n .from(toolSlotInstalls)\n .where(\n and(\n eq(toolSlotInstalls.ownerEmail, userEmail),\n eq(toolSlotInstalls.slotId, slotId),\n ),\n );\n if (installs.length === 0) return [];\n\n const accessible = await db\n .select({\n id: tools.id,\n name: tools.name,\n description: tools.description,\n icon: tools.icon,\n updatedAt: tools.updatedAt,\n })\n .from(tools)\n .where(accessFilter(tools, toolShares));\n const byId = new Map(accessible.map((t: any) => [t.id, t]));\n\n return (installs as ToolSlotInstallRow[])\n .filter((i) => byId.has(i.toolId))\n .sort((a, b) => a.position - b.position)\n .map((i) => {\n const t = byId.get(i.toolId)!;\n return {\n installId: i.id,\n toolId: i.toolId,\n name: t.name,\n description: t.description,\n icon: t.icon,\n updatedAt: t.updatedAt,\n position: i.position,\n config: i.config,\n };\n });\n}\n\n/** Delete every slot/install row referencing a tool. Called from deleteTool. */\nexport async function cascadeDeleteToolSlots(toolId: string): Promise<void> {\n await ensureSlotTables();\n const db = getDb();\n await db.delete(toolSlots).where(eq(toolSlots.toolId, toolId));\n await db.delete(toolSlotInstalls).where(eq(toolSlotInstalls.toolId, toolId));\n}\n\nfunction requireUserEmail(): string {\n const email = getRequestUserEmail();\n if (!email) {\n throw new Error(\"Slot operations require an authenticated user.\");\n }\n return email;\n}\n"]}
@@ -0,0 +1,40 @@
1
+ export declare function ensureToolsTables(): Promise<void>;
2
+ export declare function registerToolsShareable(): void;
3
+ export interface ToolRow {
4
+ id: string;
5
+ name: string;
6
+ description: string;
7
+ content: string;
8
+ icon: string | null;
9
+ createdAt: string;
10
+ updatedAt: string;
11
+ ownerEmail: string;
12
+ orgId: string | null;
13
+ visibility: "private" | "org" | "public";
14
+ }
15
+ export declare function listTools(): Promise<ToolRow[]>;
16
+ export declare function getTool(id: string): Promise<ToolRow | null>;
17
+ export interface CreateToolData {
18
+ name: string;
19
+ description?: string;
20
+ content?: string;
21
+ icon?: string;
22
+ }
23
+ export declare function createTool(data: CreateToolData): Promise<ToolRow>;
24
+ export interface UpdateToolData {
25
+ name?: string;
26
+ description?: string;
27
+ icon?: string;
28
+ visibility?: "private" | "org" | "public";
29
+ }
30
+ export declare function updateTool(id: string, data: UpdateToolData): Promise<ToolRow | null>;
31
+ export interface UpdateToolContentOpts {
32
+ content?: string;
33
+ patches?: Array<{
34
+ find: string;
35
+ replace: string;
36
+ }>;
37
+ }
38
+ export declare function updateToolContent(id: string, opts: UpdateToolContentOpts): Promise<ToolRow | null>;
39
+ export declare function deleteTool(id: string): Promise<boolean>;
40
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/tools/store.ts"],"names":[],"mappings":"AAuCA,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CA6CvD;AA4DD,wBAAgB,sBAAsB,SASrC;AAED,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,EAAE,SAAS,GAAG,KAAK,GAAG,QAAQ,CAAC;CAC1C;AAED,wBAAsB,SAAS,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC,CAOpD;AAED,wBAAsB,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAIjE;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAsB,UAAU,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,CA2BvE;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,SAAS,GAAG,KAAK,GAAG,QAAQ,CAAC;CAC3C;AAED,wBAAsB,UAAU,CAC9B,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,cAAc,GACnB,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAczB;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACpD;AAED,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,qBAAqB,GAC1B,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAyBzB;AAED,wBAAsB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAe7D"}
@@ -0,0 +1,193 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { eq } from "drizzle-orm";
3
+ import { getDbExec, isPostgres, retryOnDdlRace } from "../db/client.js";
4
+ import { createGetDb } from "../db/create-get-db.js";
5
+ import { accessFilter, assertAccess, resolveAccess, } from "../sharing/access.js";
6
+ import { getRequestUserEmail, getRequestOrgId, } from "../server/request-context.js";
7
+ import { registerShareableResource } from "../sharing/registry.js";
8
+ import { tools, toolShares, TOOLS_CREATE_SQL, TOOLS_CREATE_SQL_PG, TOOL_SHARES_CREATE_SQL, TOOL_SHARES_CREATE_SQL_PG, TOOL_DATA_CREATE_SQL, TOOL_DATA_CREATE_SQL_PG, TOOL_DATA_ITEM_INDEX_SQL, TOOL_DATA_ITEM_INDEX_SQL_PG, TOOL_DATA_DROP_OLD_INDEX_SQL, TOOL_DATA_DROP_OLD_INDEX_SQL_PG, TOOLS_OWNER_INDEX_SQL, TOOLS_ORG_INDEX_SQL, TOOL_SHARES_RESOURCE_INDEX_SQL, TOOL_CONSENTS_CREATE_SQL, TOOL_CONSENTS_CREATE_SQL_PG, TOOL_CONSENTS_VIEWER_INDEX_SQL, } from "./schema.js";
9
+ const getDb = createGetDb({ tools, toolShares });
10
+ let _initPromise;
11
+ export async function ensureToolsTables() {
12
+ if (!_initPromise) {
13
+ _initPromise = (async () => {
14
+ const client = getDbExec();
15
+ const pg = isPostgres();
16
+ await retryOnDdlRace(() => client.execute(pg ? TOOLS_CREATE_SQL_PG : TOOLS_CREATE_SQL));
17
+ await retryOnDdlRace(() => client.execute(pg ? TOOL_SHARES_CREATE_SQL_PG : TOOL_SHARES_CREATE_SQL));
18
+ await retryOnDdlRace(() => client.execute(pg ? TOOL_DATA_CREATE_SQL_PG : TOOL_DATA_CREATE_SQL));
19
+ await ensureToolDataItemId(client, pg);
20
+ await ensureToolDataScope(client, pg);
21
+ await client.execute(pg ? TOOL_DATA_DROP_OLD_INDEX_SQL_PG : TOOL_DATA_DROP_OLD_INDEX_SQL);
22
+ await retryOnDdlRace(() => client.execute(pg ? TOOL_DATA_ITEM_INDEX_SQL_PG : TOOL_DATA_ITEM_INDEX_SQL));
23
+ await retryOnDdlRace(() => client.execute(TOOLS_OWNER_INDEX_SQL));
24
+ await retryOnDdlRace(() => client.execute(TOOLS_ORG_INDEX_SQL));
25
+ await retryOnDdlRace(() => client.execute(TOOL_SHARES_RESOURCE_INDEX_SQL));
26
+ // tool_consents was introduced for an audit-C1 per-viewer consent
27
+ // gate that we removed once we settled on intra-org trust as the
28
+ // baseline. The table is kept (additive — never drop) so deploys
29
+ // that already created it stay healthy; the runtime consent code
30
+ // is gone. Idempotent CREATE IF NOT EXISTS for fresh schemas.
31
+ await retryOnDdlRace(() => client.execute(pg ? TOOL_CONSENTS_CREATE_SQL_PG : TOOL_CONSENTS_CREATE_SQL));
32
+ await retryOnDdlRace(() => client.execute(TOOL_CONSENTS_VIEWER_INDEX_SQL));
33
+ })();
34
+ }
35
+ return _initPromise;
36
+ }
37
+ async function ensureToolDataItemId(client, pg) {
38
+ if (pg) {
39
+ await client.execute(`ALTER TABLE tool_data ADD COLUMN IF NOT EXISTS item_id TEXT`);
40
+ return;
41
+ }
42
+ // Keep this additive: legacy rows with item_id=id are still read correctly
43
+ // through COALESCE(item_id, id), so SQLite never needs a table rebuild here.
44
+ try {
45
+ await client.execute(`ALTER TABLE tool_data ADD COLUMN item_id TEXT`);
46
+ }
47
+ catch (err) {
48
+ if (!String(err?.message ?? err)
49
+ .toLowerCase()
50
+ .includes("duplicate")) {
51
+ throw err;
52
+ }
53
+ }
54
+ }
55
+ async function ensureToolDataScope(client, pg) {
56
+ const addCol = (name, def) => {
57
+ if (pg) {
58
+ return client.execute(`ALTER TABLE tool_data ADD COLUMN IF NOT EXISTS ${name} ${def}`);
59
+ }
60
+ return client
61
+ .execute(`ALTER TABLE tool_data ADD COLUMN ${name} ${def}`)
62
+ .catch((err) => {
63
+ if (!String(err?.message ?? err)
64
+ .toLowerCase()
65
+ .includes("duplicate"))
66
+ throw err;
67
+ });
68
+ };
69
+ await addCol("scope", "TEXT NOT NULL DEFAULT 'user'");
70
+ await addCol("org_id", "TEXT");
71
+ await addCol("scope_key", "TEXT NOT NULL DEFAULT 'local@localhost'");
72
+ // One-time backfill migration: replaces the dev-mode DEFAULT scope_key
73
+ // with each row's real owner_email. Not a per-request fallback.
74
+ await client.execute(
75
+ // guard:allow-localhost-fallback — one-time backfill migration replacing dev-mode default scope_key with the row's real owner_email
76
+ `UPDATE tool_data SET scope_key = owner_email WHERE scope_key = 'local@localhost' AND owner_email != 'local@localhost'`);
77
+ }
78
+ export function registerToolsShareable() {
79
+ registerShareableResource({
80
+ type: "tool",
81
+ resourceTable: tools,
82
+ sharesTable: toolShares,
83
+ displayName: "Tool",
84
+ titleColumn: "name",
85
+ getDb: () => getDb(),
86
+ });
87
+ }
88
+ export async function listTools() {
89
+ await ensureToolsTables();
90
+ const db = getDb();
91
+ return db
92
+ .select()
93
+ .from(tools)
94
+ .where(accessFilter(tools, toolShares));
95
+ }
96
+ export async function getTool(id) {
97
+ await ensureToolsTables();
98
+ const access = await resolveAccess("tool", id);
99
+ return access?.resource ?? null;
100
+ }
101
+ export async function createTool(data) {
102
+ await ensureToolsTables();
103
+ const db = getDb();
104
+ const userEmail = getRequestUserEmail();
105
+ if (!userEmail)
106
+ throw new Error("no authenticated user");
107
+ const orgId = getRequestOrgId();
108
+ const id = randomUUID();
109
+ const now = new Date().toISOString();
110
+ const row = {
111
+ id,
112
+ name: data.name,
113
+ description: data.description ?? "",
114
+ content: data.content ?? "",
115
+ icon: data.icon ?? null,
116
+ createdAt: now,
117
+ updatedAt: now,
118
+ ownerEmail: userEmail,
119
+ orgId: orgId ?? null,
120
+ // Default to org-visibility when the user has an active organization so
121
+ // teammates see the tool in their sidebar — matching how analytics
122
+ // dashboards/analyses are scoped (`templates/analytics/server/lib/
123
+ // dashboards-store.ts:356`). Solo users (no org) get the private
124
+ // default. Owners can still flip back to private via update-tool.
125
+ visibility: orgId ? "org" : "private",
126
+ };
127
+ await db.insert(tools).values(row);
128
+ return row;
129
+ }
130
+ export async function updateTool(id, data) {
131
+ await ensureToolsTables();
132
+ await assertAccess("tool", id, "editor");
133
+ const db = getDb();
134
+ const updates = {
135
+ updatedAt: new Date().toISOString(),
136
+ };
137
+ if (data.name !== undefined)
138
+ updates.name = data.name;
139
+ if (data.description !== undefined)
140
+ updates.description = data.description;
141
+ if (data.icon !== undefined)
142
+ updates.icon = data.icon;
143
+ if (data.visibility !== undefined)
144
+ updates.visibility = data.visibility;
145
+ await db.update(tools).set(updates).where(eq(tools.id, id));
146
+ const rows = await db.select().from(tools).where(eq(tools.id, id));
147
+ return rows[0] ?? null;
148
+ }
149
+ export async function updateToolContent(id, opts) {
150
+ await ensureToolsTables();
151
+ await assertAccess("tool", id, "editor");
152
+ const db = getDb();
153
+ let newContent;
154
+ if (opts.content !== undefined) {
155
+ newContent = opts.content;
156
+ }
157
+ else if (opts.patches) {
158
+ const rows = await db.select().from(tools).where(eq(tools.id, id));
159
+ if (!rows[0])
160
+ return null;
161
+ newContent = rows[0].content;
162
+ for (const patch of opts.patches) {
163
+ newContent = newContent.replace(patch.find, patch.replace);
164
+ }
165
+ }
166
+ else {
167
+ return null;
168
+ }
169
+ await db
170
+ .update(tools)
171
+ .set({ content: newContent, updatedAt: new Date().toISOString() })
172
+ .where(eq(tools.id, id));
173
+ const rows = await db.select().from(tools).where(eq(tools.id, id));
174
+ return rows[0] ?? null;
175
+ }
176
+ export async function deleteTool(id) {
177
+ await ensureToolsTables();
178
+ await assertAccess("tool", id, "admin");
179
+ const db = getDb();
180
+ const rows = await db.select().from(tools).where(eq(tools.id, id));
181
+ if (!rows[0])
182
+ return false;
183
+ await db.delete(toolShares).where(eq(toolShares.resourceId, id));
184
+ await getDbExec().execute({
185
+ sql: `DELETE FROM tool_data WHERE tool_id = ?`,
186
+ args: [id],
187
+ });
188
+ const { cascadeDeleteToolSlots } = await import("./slots/store.js");
189
+ await cascadeDeleteToolSlots(id);
190
+ await db.delete(tools).where(eq(tools.id, id));
191
+ return true;
192
+ }
193
+ //# sourceMappingURL=store.js.map