@vendoai/store 0.1.0 → 0.4.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 (112) hide show
  1. package/README.md +81 -0
  2. package/dist/blobs.d.ts +5 -0
  3. package/dist/blobs.d.ts.map +1 -0
  4. package/dist/blobs.js +62 -0
  5. package/dist/blobs.js.map +1 -0
  6. package/dist/crypto.d.ts +10 -0
  7. package/dist/crypto.d.ts.map +1 -0
  8. package/dist/crypto.js +44 -0
  9. package/dist/crypto.js.map +1 -0
  10. package/dist/db.d.ts +26 -26
  11. package/dist/db.d.ts.map +1 -0
  12. package/dist/db.js +335 -86
  13. package/dist/db.js.map +1 -0
  14. package/dist/erase.d.ts +33 -0
  15. package/dist/erase.d.ts.map +1 -0
  16. package/dist/erase.js +107 -0
  17. package/dist/erase.js.map +1 -0
  18. package/dist/helpers/approvals.d.ts +12 -0
  19. package/dist/helpers/approvals.d.ts.map +1 -0
  20. package/dist/helpers/approvals.js +37 -0
  21. package/dist/helpers/approvals.js.map +1 -0
  22. package/dist/helpers/apps.d.ts +15 -0
  23. package/dist/helpers/apps.d.ts.map +1 -0
  24. package/dist/helpers/apps.js +42 -0
  25. package/dist/helpers/apps.js.map +1 -0
  26. package/dist/helpers/audit.d.ts +24 -0
  27. package/dist/helpers/audit.d.ts.map +1 -0
  28. package/dist/helpers/audit.js +79 -0
  29. package/dist/helpers/audit.js.map +1 -0
  30. package/dist/helpers/grants.d.ts +14 -0
  31. package/dist/helpers/grants.d.ts.map +1 -0
  32. package/dist/helpers/grants.js +46 -0
  33. package/dist/helpers/grants.js.map +1 -0
  34. package/dist/helpers/rows.d.ts +30 -0
  35. package/dist/helpers/rows.d.ts.map +1 -0
  36. package/dist/helpers/rows.js +191 -0
  37. package/dist/helpers/rows.js.map +1 -0
  38. package/dist/helpers/runs.d.ts +19 -0
  39. package/dist/helpers/runs.d.ts.map +1 -0
  40. package/dist/helpers/runs.js +47 -0
  41. package/dist/helpers/runs.js.map +1 -0
  42. package/dist/helpers/state.d.ts +9 -0
  43. package/dist/helpers/state.d.ts.map +1 -0
  44. package/dist/helpers/state.js +22 -0
  45. package/dist/helpers/state.js.map +1 -0
  46. package/dist/helpers/subjects.d.ts +32 -0
  47. package/dist/helpers/subjects.d.ts.map +1 -0
  48. package/dist/helpers/subjects.js +78 -0
  49. package/dist/helpers/subjects.js.map +1 -0
  50. package/dist/helpers/threads.d.ts +19 -0
  51. package/dist/helpers/threads.d.ts.map +1 -0
  52. package/dist/helpers/threads.js +38 -0
  53. package/dist/helpers/threads.js.map +1 -0
  54. package/dist/helpers/types.d.ts +60 -0
  55. package/dist/helpers/types.d.ts.map +1 -0
  56. package/dist/helpers/types.js +2 -0
  57. package/dist/helpers/types.js.map +1 -0
  58. package/dist/helpers/utils.d.ts +37 -0
  59. package/dist/helpers/utils.d.ts.map +1 -0
  60. package/dist/helpers/utils.js +94 -0
  61. package/dist/helpers/utils.js.map +1 -0
  62. package/dist/index.d.ts +13 -11
  63. package/dist/index.d.ts.map +1 -0
  64. package/dist/index.js +17 -15
  65. package/dist/index.js.map +1 -0
  66. package/dist/records.d.ts +7 -0
  67. package/dist/records.d.ts.map +1 -0
  68. package/dist/records.js +208 -0
  69. package/dist/records.js.map +1 -0
  70. package/dist/routing.d.ts +7 -0
  71. package/dist/routing.d.ts.map +1 -0
  72. package/dist/routing.js +393 -0
  73. package/dist/routing.js.map +1 -0
  74. package/dist/schema.d.ts +18 -1420
  75. package/dist/schema.d.ts.map +1 -0
  76. package/dist/schema.js +206 -114
  77. package/dist/schema.js.map +1 -0
  78. package/dist/secrets.d.ts +13 -0
  79. package/dist/secrets.d.ts.map +1 -0
  80. package/dist/secrets.js +86 -0
  81. package/dist/secrets.js.map +1 -0
  82. package/dist/sessions.d.ts +53 -0
  83. package/dist/sessions.d.ts.map +1 -0
  84. package/dist/sessions.js +85 -0
  85. package/dist/sessions.js.map +1 -0
  86. package/dist/store.d.ts +24 -0
  87. package/dist/store.d.ts.map +1 -0
  88. package/dist/store.js +49 -0
  89. package/dist/store.js.map +1 -0
  90. package/dist/validate.d.ts +25 -0
  91. package/dist/validate.d.ts.map +1 -0
  92. package/dist/validate.js +144 -0
  93. package/dist/validate.js.map +1 -0
  94. package/package.json +15 -14
  95. package/dist/automation-store.d.ts +0 -62
  96. package/dist/automation-store.js +0 -675
  97. package/dist/connections-store.d.ts +0 -25
  98. package/dist/connections-store.js +0 -102
  99. package/dist/decision-store.d.ts +0 -8
  100. package/dist/decision-store.js +0 -39
  101. package/dist/meta.d.ts +0 -5
  102. package/dist/meta.js +0 -20
  103. package/dist/thread-store.d.ts +0 -5
  104. package/dist/thread-store.js +0 -209
  105. package/dist/vendo-registry.d.ts +0 -5
  106. package/dist/vendo-registry.js +0 -52
  107. package/migrations/.gitkeep +0 -0
  108. package/migrations/0000_living_kate_bishop.sql +0 -110
  109. package/migrations/0001_brainy_tinkerer.sql +0 -13
  110. package/migrations/meta/0000_snapshot.json +0 -757
  111. package/migrations/meta/0001_snapshot.json +0 -859
  112. package/migrations/meta/_journal.json +0 -20
@@ -0,0 +1,37 @@
1
+ import { dbFor } from "../store.js";
2
+ import { approvalFromRow, putApprovalRow } from "./rows.js";
3
+ import { text } from "./utils.js";
4
+ import { parseApprovalRequest } from "../validate.js";
5
+ /** 02-store §3 */
6
+ export function approvalStore(store) {
7
+ const db = dbFor(store);
8
+ return {
9
+ async create(request) {
10
+ const parsedRequest = parseApprovalRequest(request);
11
+ await putApprovalRow(db, {
12
+ id: parsedRequest.id,
13
+ subject: parsedRequest.ctx.principal.subject,
14
+ request: parsedRequest,
15
+ status: "pending",
16
+ createdAt: parsedRequest.createdAt,
17
+ }, false);
18
+ },
19
+ async get(id) {
20
+ const result = await db.query("SELECT * FROM vendo_approvals WHERE id = $1", [id]);
21
+ return result.rows[0] ? approvalFromRow(result.rows[0]) : null;
22
+ },
23
+ async pending(principal) {
24
+ const result = await db.query(`SELECT request FROM vendo_approvals WHERE subject = $1 AND status = 'pending'
25
+ ORDER BY created_at ASC, id ASC`, [principal.subject]);
26
+ return result.rows.map((row) => row["request"]);
27
+ },
28
+ async decide(ids, status, decidedAt) {
29
+ const requested = [...new Set(Array.isArray(ids) ? ids : [ids])];
30
+ const result = await db.query(`UPDATE vendo_approvals SET status = $2, decided_at = $3
31
+ WHERE id = ANY($1::text[]) AND status = 'pending' RETURNING id`, [requested, status, decidedAt]);
32
+ const decided = new Set(result.rows.map((row) => text(row["id"])));
33
+ return requested.filter((id) => decided.has(id));
34
+ },
35
+ };
36
+ }
37
+ //# sourceMappingURL=approvals.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approvals.js","sourceRoot":"","sources":["../../src/helpers/approvals.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAmB,MAAM,aAAa,CAAC;AAErD,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC5D,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAClC,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAEtD,kBAAkB;AAClB,MAAM,UAAU,aAAa,CAAC,KAAiB;IAM7C,MAAM,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;IACxB,OAAO;QACL,KAAK,CAAC,MAAM,CAAC,OAAO;YAClB,MAAM,aAAa,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAC;YACpD,MAAM,cAAc,CAAC,EAAE,EAAE;gBACvB,EAAE,EAAE,aAAa,CAAC,EAAE;gBACpB,OAAO,EAAE,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO;gBAC5C,OAAO,EAAE,aAAa;gBACtB,MAAM,EAAE,SAAS;gBACjB,SAAS,EAAE,aAAa,CAAC,SAAS;aACnC,EAAE,KAAK,CAAC,CAAC;QACZ,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,EAAE;YACV,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,KAAK,CAAC,6CAA6C,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;YACnF,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QACjE,CAAC;QACD,KAAK,CAAC,OAAO,CAAC,SAAS;YACrB,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,KAAK,CAC3B;yCACiC,EACjC,CAAC,SAAS,CAAC,OAAO,CAAC,CACpB,CAAC;YACF,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,SAAS,CAAoB,CAAC,CAAC;QACrE,CAAC;QACD,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS;YACjC,MAAM,SAAS,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACjE,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,KAAK,CAC3B;wEACgE,EAChE,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,CAAC,CAC/B,CAAC;YACF,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;YACnE,OAAO,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QACnD,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { AppDocument, AppId, Principal } from "@vendoai/core";
2
+ import { type VendoStore } from "../store.js";
3
+ import type { AppRow } from "./types.js";
4
+ /** 02-store §3 */
5
+ export declare function appStore(store: VendoStore): {
6
+ put(principal: Principal, doc: AppDocument, opts?: {
7
+ enabled?: boolean;
8
+ }): Promise<AppRow>;
9
+ get(id: AppId): Promise<AppRow | null>;
10
+ list(principal: Principal): Promise<AppRow[]>;
11
+ setEnabled(id: AppId, enabled: boolean): Promise<void>;
12
+ delete(id: AppId): Promise<void>;
13
+ };
14
+ export type { AppRow } from "./types.js";
15
+ //# sourceMappingURL=apps.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apps.d.ts","sourceRoot":"","sources":["../../src/helpers/apps.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AACnE,OAAO,EAAS,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAKzC,kBAAkB;AAClB,wBAAgB,QAAQ,CAAC,KAAK,EAAE,UAAU,GAAG;IAC3C,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,EAAE,IAAI,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3F,GAAG,CAAC,EAAE,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACvC,IAAI,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC9C,UAAU,CAAC,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvD,MAAM,CAAC,EAAE,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAClC,CA2CA;AAED,YAAY,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,42 @@
1
+ import { dbFor } from "../store.js";
2
+ import { VendoError } from "@vendoai/core";
3
+ import { appFromRow, putAppRow } from "./rows.js";
4
+ import { parseAppDocument } from "../validate.js";
5
+ /** 02-store §3 */
6
+ export function appStore(store) {
7
+ const db = dbFor(store);
8
+ return {
9
+ async put(principal, doc, opts = {}) {
10
+ const parsedDoc = parseAppDocument(doc);
11
+ // Apps never cross subjects (02 §2): the guarded upsert refuses a
12
+ // foreign-owned id atomically.
13
+ return putAppRow(db, {
14
+ id: parsedDoc.id,
15
+ subject: principal.subject,
16
+ enabled: opts.enabled ?? true,
17
+ doc: parsedDoc,
18
+ });
19
+ },
20
+ async get(id) {
21
+ const result = await db.query("SELECT id, subject, enabled, doc, created_at, updated_at, revision FROM vendo_apps WHERE id = $1", [id]);
22
+ return result.rows[0] ? appFromRow(result.rows[0]) : null;
23
+ },
24
+ async list(principal) {
25
+ const result = await db.query(`SELECT id, subject, enabled, doc, created_at, updated_at, revision FROM vendo_apps
26
+ WHERE subject = $1 ORDER BY created_at ASC, id ASC`, [principal.subject]);
27
+ return result.rows.map(appFromRow);
28
+ },
29
+ async setEnabled(id, enabled) {
30
+ // Wave 7 — every vendo_apps write door bumps the token: a CAS armed with
31
+ // a pre-flip revision must lose, or it would silently revert this flip.
32
+ const result = await db.query("UPDATE vendo_apps SET enabled = $2, updated_at = $3, revision = revision + 1 WHERE id = $1 RETURNING id", [id, enabled, new Date().toISOString()]);
33
+ if (result.rows.length === 0)
34
+ throw new VendoError("not-found", `App ${id} was not found`);
35
+ },
36
+ async delete(id) {
37
+ await db.query("DELETE FROM vendo_state WHERE app_id = $1", [id]);
38
+ await db.query("DELETE FROM vendo_apps WHERE id = $1", [id]);
39
+ },
40
+ };
41
+ }
42
+ //# sourceMappingURL=apps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apps.js","sourceRoot":"","sources":["../../src/helpers/apps.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAmB,MAAM,aAAa,CAAC;AAErD,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD,kBAAkB;AAClB,MAAM,UAAU,QAAQ,CAAC,KAAiB;IAOxC,MAAM,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;IACxB,OAAO;QACL,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,GAAG,EAAE;YACjC,MAAM,SAAS,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;YACxC,kEAAkE;YAClE,+BAA+B;YAC/B,OAAO,SAAS,CAAC,EAAE,EAAE;gBACnB,EAAE,EAAE,SAAS,CAAC,EAAE;gBAChB,OAAO,EAAE,SAAS,CAAC,OAAO;gBAC1B,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,IAAI;gBAC7B,GAAG,EAAE,SAAS;aACf,CAAC,CAAC;QACL,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,EAAE;YACV,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,KAAK,CAC3B,kGAAkG,EAClG,CAAC,EAAE,CAAC,CACL,CAAC;YACF,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC5D,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,SAAS;YAClB,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,KAAK,CAC3B;4DACoD,EACpD,CAAC,SAAS,CAAC,OAAO,CAAC,CACpB,CAAC;YACF,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACrC,CAAC;QACD,KAAK,CAAC,UAAU,CAAC,EAAE,EAAE,OAAO;YAC1B,yEAAyE;YACzE,wEAAwE;YACxE,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,KAAK,CAC3B,yGAAyG,EACzG,CAAC,EAAE,EAAE,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CACxC,CAAC;YACF,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC;gBAAE,MAAM,IAAI,UAAU,CAAC,WAAW,EAAE,OAAO,EAAE,gBAAgB,CAAC,CAAC;QAC7F,CAAC;QACD,KAAK,CAAC,MAAM,CAAC,EAAE;YACb,MAAM,EAAE,CAAC,KAAK,CAAC,2CAA2C,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;YAClE,MAAM,EAAE,CAAC,KAAK,CAAC,sCAAsC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QAC/D,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,24 @@
1
+ import type { AppId, AuditEvent, IsoDateTime, Principal } from "@vendoai/core";
2
+ import { type VendoStore } from "../store.js";
3
+ export interface AuditQuery {
4
+ principal?: Principal;
5
+ appId?: AppId;
6
+ kind?: AuditEvent["kind"];
7
+ from?: IsoDateTime;
8
+ to?: IsoDateTime;
9
+ cursor?: string;
10
+ limit?: number;
11
+ }
12
+ /** 02-store §3 */
13
+ export declare function auditStore(store: VendoStore): {
14
+ append(event: AuditEvent): Promise<void>;
15
+ query(filter: AuditQuery): Promise<{
16
+ events: AuditEvent[];
17
+ cursor?: string;
18
+ }>;
19
+ export(filter?: {
20
+ from?: IsoDateTime;
21
+ to?: IsoDateTime;
22
+ }): AsyncIterable<string>;
23
+ };
24
+ //# sourceMappingURL=audit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/helpers/audit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC/E,OAAO,EAAS,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAKrD,MAAM,WAAW,UAAU;IACzB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,IAAI,CAAC,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;IAC1B,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,EAAE,CAAC,EAAE,WAAW,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,kBAAkB;AAClB,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG;IAC7C,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,KAAK,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,UAAU,EAAE,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9E,MAAM,CAAC,MAAM,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,WAAW,CAAC;QAAC,EAAE,CAAC,EAAE,WAAW,CAAA;KAAE,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;CAClF,CAuEA"}
@@ -0,0 +1,79 @@
1
+ import { dbFor } from "../store.js";
2
+ import { putAuditRow } from "./rows.js";
3
+ import { cursorMs, decodeCursor, encodeCursor, iso, pageLimit, text } from "./utils.js";
4
+ import { parseAuditEvent } from "../validate.js";
5
+ /** 02-store §3 */
6
+ export function auditStore(store) {
7
+ const db = dbFor(store);
8
+ return {
9
+ async append(event) {
10
+ // putAuditRow refuses to replace an existing row (append-only, 02 §2).
11
+ await putAuditRow(db, parseAuditEvent(event));
12
+ },
13
+ async query(filter) {
14
+ const limit = pageLimit(filter.limit);
15
+ const params = [];
16
+ const clauses = [];
17
+ const add = (sql, value) => {
18
+ params.push(value);
19
+ clauses.push(sql.replace("?", `$${params.length}`));
20
+ };
21
+ if (filter.principal)
22
+ add("subject = ?", filter.principal.subject);
23
+ if (filter.appId !== undefined)
24
+ add("app_id = ?", filter.appId);
25
+ if (filter.kind !== undefined)
26
+ add("kind = ?", filter.kind);
27
+ if (filter.from !== undefined)
28
+ add("at >= ?", filter.from);
29
+ if (filter.to !== undefined)
30
+ add("at <= ?", filter.to);
31
+ if (filter.cursor !== undefined) {
32
+ const cursor = decodeCursor(filter.cursor);
33
+ params.push(cursor.c, cursor.i);
34
+ clauses.push(`(${cursorMs("at")}, id) < (${cursorMs(`$${params.length - 1}::timestamptz`)}, $${params.length})`);
35
+ }
36
+ params.push(limit + 1);
37
+ const result = await db.query(`SELECT id, at, event FROM vendo_audit${clauses.length ? ` WHERE ${clauses.join(" AND ")}` : ""}
38
+ ORDER BY ${cursorMs("at")} DESC, id DESC LIMIT $${params.length}`, params);
39
+ const page = result.rows.slice(0, limit);
40
+ const events = page.map((row) => row["event"]);
41
+ const last = page.at(-1);
42
+ return {
43
+ events,
44
+ ...(result.rows.length > limit && last
45
+ ? { cursor: encodeCursor(iso(last["at"]), text(last["id"])) }
46
+ : {}),
47
+ };
48
+ },
49
+ async *export(filter = {}) {
50
+ let cursor;
51
+ while (true) {
52
+ const params = [];
53
+ const clauses = [];
54
+ if (filter.from !== undefined) {
55
+ params.push(filter.from);
56
+ clauses.push(`at >= $${params.length}`);
57
+ }
58
+ if (filter.to !== undefined) {
59
+ params.push(filter.to);
60
+ clauses.push(`at <= $${params.length}`);
61
+ }
62
+ if (cursor) {
63
+ params.push(cursor.c, cursor.i);
64
+ clauses.push(`(${cursorMs("at")}, id) > (${cursorMs(`$${params.length - 1}::timestamptz`)}, $${params.length})`);
65
+ }
66
+ params.push(500);
67
+ const result = await db.query(`SELECT id, at, event FROM vendo_audit${clauses.length ? ` WHERE ${clauses.join(" AND ")}` : ""}
68
+ ORDER BY ${cursorMs("at")} ASC, id ASC LIMIT $${params.length}`, params);
69
+ for (const row of result.rows)
70
+ yield `${JSON.stringify(row["event"])}\n`;
71
+ const last = result.rows.at(-1);
72
+ if (!last || result.rows.length < 500)
73
+ return;
74
+ cursor = { c: iso(last["at"]), i: text(last["id"]) };
75
+ }
76
+ },
77
+ };
78
+ }
79
+ //# sourceMappingURL=audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/helpers/audit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { type AppId, type GrantId, type IsoDateTime, type PermissionGrant, type Principal } from "@vendoai/core";
2
+ import { type VendoStore } from "../store.js";
3
+ /** 02-store §3 */
4
+ export declare function grantStore(store: VendoStore): {
5
+ create(principal: Principal, grant: PermissionGrant): Promise<void>;
6
+ get(id: GrantId): Promise<PermissionGrant | null>;
7
+ list(principal: Principal, filter?: {
8
+ tool?: string;
9
+ appId?: AppId;
10
+ includeInactive?: boolean;
11
+ }): Promise<PermissionGrant[]>;
12
+ revoke(id: GrantId, revokedAt: IsoDateTime): Promise<void>;
13
+ };
14
+ //# sourceMappingURL=grants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grants.d.ts","sourceRoot":"","sources":["../../src/helpers/grants.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,KAAK,EACV,KAAK,OAAO,EACZ,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,SAAS,EACf,MAAM,eAAe,CAAC;AACvB,OAAO,EAAS,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAIrD,kBAAkB;AAClB,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG;IAC7C,MAAM,CAAC,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE,GAAG,CAAC,EAAE,EAAE,OAAO,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC;IAClD,IAAI,CAAC,SAAS,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,KAAK,CAAC;QAAC,eAAe,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;IAC7H,MAAM,CAAC,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5D,CA4CA"}
@@ -0,0 +1,46 @@
1
+ import { VendoError, } from "@vendoai/core";
2
+ import { dbFor } from "../store.js";
3
+ import { grantFromRow, putGrantRow } from "./rows.js";
4
+ import { parsePermissionGrant } from "../validate.js";
5
+ /** 02-store §3 */
6
+ export function grantStore(store) {
7
+ const db = dbFor(store);
8
+ return {
9
+ async create(principal, grant) {
10
+ const parsedGrant = parsePermissionGrant(grant);
11
+ if (parsedGrant.subject !== principal.subject) {
12
+ throw new VendoError("validation", "Grant subject must match principal subject");
13
+ }
14
+ // Guarded upsert (02 §2): a same-subject re-create updates in place; an
15
+ // id owned by another subject is refused as a conflict.
16
+ await putGrantRow(db, parsedGrant);
17
+ },
18
+ async get(id) {
19
+ const result = await db.query("SELECT * FROM vendo_grants WHERE id = $1", [id]);
20
+ return result.rows[0] ? grantFromRow(result.rows[0]) : null;
21
+ },
22
+ async list(principal, filter = {}) {
23
+ const params = [principal.subject];
24
+ const clauses = ["subject = $1"];
25
+ if (filter.tool !== undefined) {
26
+ params.push(filter.tool);
27
+ clauses.push(`tool = $${params.length}`);
28
+ }
29
+ if (filter.appId !== undefined) {
30
+ params.push(filter.appId);
31
+ clauses.push(`app_id = $${params.length}`);
32
+ }
33
+ if (filter.includeInactive !== true) {
34
+ clauses.push("revoked_at IS NULL AND (expires_at IS NULL OR expires_at > now())");
35
+ }
36
+ const result = await db.query(`SELECT * FROM vendo_grants WHERE ${clauses.join(" AND ")} ORDER BY granted_at ASC, id ASC`, params);
37
+ return result.rows.map(grantFromRow);
38
+ },
39
+ async revoke(id, revokedAt) {
40
+ const result = await db.query("UPDATE vendo_grants SET revoked_at = $2 WHERE id = $1 RETURNING id", [id, revokedAt]);
41
+ if (result.rows.length === 0)
42
+ throw new VendoError("not-found", `Grant ${id} was not found`);
43
+ },
44
+ };
45
+ }
46
+ //# sourceMappingURL=grants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grants.js","sourceRoot":"","sources":["../../src/helpers/grants.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,GAMX,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,KAAK,EAAmB,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAEtD,kBAAkB;AAClB,MAAM,UAAU,UAAU,CAAC,KAAiB;IAM1C,MAAM,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;IACxB,OAAO;QACL,KAAK,CAAC,MAAM,CAAC,SAAS,EAAE,KAAK;YAC3B,MAAM,WAAW,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;YAChD,IAAI,WAAW,CAAC,OAAO,KAAK,SAAS,CAAC,OAAO,EAAE,CAAC;gBAC9C,MAAM,IAAI,UAAU,CAAC,YAAY,EAAE,4CAA4C,CAAC,CAAC;YACnF,CAAC;YACD,wEAAwE;YACxE,wDAAwD;YACxD,MAAM,WAAW,CAAC,EAAE,EAAE,WAAW,CAAC,CAAC;QACrC,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,EAAE;YACV,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,KAAK,CAAC,0CAA0C,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;YAChF,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC9D,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,EAAE;YAC/B,MAAM,MAAM,GAAc,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;YAC9C,MAAM,OAAO,GAAG,CAAC,cAAc,CAAC,CAAC;YACjC,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;gBAC9B,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;gBACzB,OAAO,CAAC,IAAI,CAAC,WAAW,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;YAC3C,CAAC;YACD,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;gBAC/B,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBAC1B,OAAO,CAAC,IAAI,CAAC,aAAa,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;YAC7C,CAAC;YACD,IAAI,MAAM,CAAC,eAAe,KAAK,IAAI,EAAE,CAAC;gBACpC,OAAO,CAAC,IAAI,CAAC,mEAAmE,CAAC,CAAC;YACpF,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,KAAK,CAC3B,oCAAoC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,kCAAkC,EAC3F,MAAM,CACP,CAAC;YACF,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QACvC,CAAC;QACD,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS;YACxB,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,KAAK,CAC3B,oEAAoE,EACpE,CAAC,EAAE,EAAE,SAAS,CAAC,CAChB,CAAC;YACF,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC;gBAAE,MAAM,IAAI,UAAU,CAAC,WAAW,EAAE,SAAS,EAAE,gBAAgB,CAAC,CAAC;QAC/F,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,30 @@
1
+ import { type AuditEvent, type Json, type PermissionGrant } from "@vendoai/core";
2
+ import type { Db } from "../db.js";
3
+ import type { AppRow, ApprovalRow, RunRow, StateRow, ThreadRow } from "./types.js";
4
+ export declare function appFromRow(row: Record<string, unknown>): AppRow;
5
+ export declare function putAppRow(db: Db, input: Pick<AppRow, "id" | "subject" | "enabled" | "doc">, now?: string): Promise<AppRow>;
6
+ export declare function threadFromRow(row: Record<string, unknown>): ThreadRow;
7
+ export declare function putThreadRow(db: Db, input: Pick<ThreadRow, "id" | "subject" | "messages" | "title">, now?: string): Promise<ThreadRow>;
8
+ export declare function stateRowFromRow(row: Record<string, unknown>): StateRow;
9
+ /** The single persistent write path for vendo_state, shared by stateStore.put and
10
+ * the routed records("vendo_state").put so the two doors never drift. Writes
11
+ * created_at once on insert and PRESERVES it on conflict (only data + updated_at
12
+ * change), so the seam's createdAt is stable across puts. Never writes the
13
+ * generated `id` column. */
14
+ export declare function putStateRow(db: Db, input: {
15
+ appId: string;
16
+ subject: string;
17
+ data: Json;
18
+ }, now?: string): Promise<StateRow>;
19
+ export declare function grantFromRow(row: Record<string, unknown>): PermissionGrant;
20
+ export declare function putGrantRow(db: Db, grant: PermissionGrant): Promise<void>;
21
+ export declare function approvalFromRow(row: Record<string, unknown>): ApprovalRow;
22
+ export declare function putApprovalRow(db: Db, row: ApprovalRow, upsert?: boolean): Promise<void>;
23
+ /** 02-store §2: vendo_audit is append-only. The insert refuses to touch an
24
+ * existing row ATOMICALLY — ON CONFLICT DO NOTHING plus an empty RETURNING
25
+ * means the id already exists, and the write is rejected instead of replacing
26
+ * history. Deletion happens only through the store erase API (02 §5). */
27
+ export declare function putAuditRow(db: Db, event: AuditEvent): Promise<void>;
28
+ export declare function runFromRow(row: Record<string, unknown>): RunRow;
29
+ export declare function putRunRow(db: Db, run: RunRow): Promise<void>;
30
+ //# sourceMappingURL=rows.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rows.d.ts","sourceRoot":"","sources":["../../src/helpers/rows.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,UAAU,EACf,KAAK,IAAI,EACT,KAAK,eAAe,EACrB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,UAAU,CAAC;AACnC,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAGnF,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAa/D;AAED,wBAAsB,SAAS,CAC7B,EAAE,EAAE,EAAE,EACN,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,GAAG,SAAS,GAAG,SAAS,GAAG,KAAK,CAAC,EACzD,GAAG,SAA2B,GAC7B,OAAO,CAAC,MAAM,CAAC,CAuBjB;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAcrE;AAED,wBAAsB,YAAY,CAChC,EAAE,EAAE,EAAE,EACN,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,GAAG,SAAS,GAAG,UAAU,GAAG,OAAO,CAAC,EAC/D,GAAG,SAA2B,GAC7B,OAAO,CAAC,SAAS,CAAC,CAsBpB;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,QAAQ,CAQtE;AAED;;;;6BAI6B;AAC7B,wBAAsB,WAAW,CAC/B,EAAE,EAAE,EAAE,EACN,KAAK,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,IAAI,CAAA;CAAE,EACrD,GAAG,SAA2B,GAC7B,OAAO,CAAC,QAAQ,CAAC,CASnB;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,eAAe,CAiB1E;AAED,wBAAsB,WAAW,CAAC,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAuB/E;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,WAAW,CAazE;AAED,wBAAsB,cAAc,CAAC,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,UAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAY3F;AAED;;;0EAG0E;AAC1E,wBAAsB,WAAW,CAAC,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAY1E;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAW/D;AAED,wBAAsB,SAAS,CAAC,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAUlE"}
@@ -0,0 +1,191 @@
1
+ import { VendoError, } from "@vendoai/core";
2
+ import { iso, optionalIso, text } from "./utils.js";
3
+ export function appFromRow(row) {
4
+ const revision = row["revision"];
5
+ return {
6
+ id: text(row["id"]),
7
+ subject: text(row["subject"]),
8
+ enabled: row["enabled"] === true,
9
+ doc: row["doc"],
10
+ createdAt: iso(row["created_at"]),
11
+ updatedAt: iso(row["updated_at"]),
12
+ ...(typeof revision === "string" || typeof revision === "number" || typeof revision === "bigint"
13
+ ? { revision: String(revision) }
14
+ : {}),
15
+ };
16
+ }
17
+ export async function putAppRow(db, input, now = new Date().toISOString()) {
18
+ // Apps never cross subjects (02 §2: the app row IS the user's copy). Same
19
+ // atomic guard as putThreadRow: on conflict the update applies ONLY when the
20
+ // existing row already belongs to EXCLUDED.subject — otherwise the WHERE
21
+ // fails, RETURNING is empty, and the cross-subject flip is refused without a
22
+ // TOCTOU window.
23
+ // Every write bumps the revision counter (Wave 7), so a token read before a
24
+ // plain put can no longer compareAndSwap — same discipline as putThreadRow.
25
+ const result = await db.query(`INSERT INTO vendo_apps (id, subject, enabled, doc, created_at, updated_at, revision)
26
+ VALUES ($1, $2, $3, $4::jsonb, $5, $5, 1)
27
+ ON CONFLICT (id) DO UPDATE SET enabled = EXCLUDED.enabled,
28
+ doc = EXCLUDED.doc, updated_at = EXCLUDED.updated_at,
29
+ revision = vendo_apps.revision + 1
30
+ WHERE vendo_apps.subject = EXCLUDED.subject
31
+ RETURNING id, subject, enabled, doc, created_at, updated_at, revision`, [input.id, input.subject, input.enabled, JSON.stringify(input.doc), now]);
32
+ const row = result.rows[0];
33
+ if (row === undefined) {
34
+ throw new VendoError("conflict", `app ${input.id} belongs to another subject`);
35
+ }
36
+ return appFromRow(row);
37
+ }
38
+ export function threadFromRow(row) {
39
+ const title = row["title"];
40
+ const revision = row["revision"];
41
+ return {
42
+ id: text(row["id"]),
43
+ subject: text(row["subject"]),
44
+ messages: row["messages"],
45
+ ...(typeof title === "string" ? { title } : {}),
46
+ createdAt: iso(row["created_at"]),
47
+ updatedAt: iso(row["updated_at"]),
48
+ ...(typeof revision === "string" || typeof revision === "number" || typeof revision === "bigint"
49
+ ? { revision: String(revision) }
50
+ : {}),
51
+ };
52
+ }
53
+ export async function putThreadRow(db, input, now = new Date().toISOString()) {
54
+ // Threads never cross subjects (03 §5). vendo_threads is keyed by the bare id,
55
+ // so the upsert is guarded ATOMICALLY: on conflict it updates ONLY when the
56
+ // existing row already belongs to EXCLUDED.subject — otherwise the WHERE fails,
57
+ // no row is written, RETURNING is empty, and we refuse the cross-subject flip.
58
+ // This closes the TOCTOU window that a resolve()-time pre-check alone cannot
59
+ // (a foreign row can appear during a long streaming turn, before persist runs).
60
+ const result = await db.query(`INSERT INTO vendo_threads (id, subject, messages, title, created_at, updated_at, revision)
61
+ VALUES ($1, $2, $3::jsonb, $4, $5, $5, 1)
62
+ ON CONFLICT (id) DO UPDATE
63
+ SET messages = EXCLUDED.messages, title = EXCLUDED.title, updated_at = EXCLUDED.updated_at,
64
+ revision = vendo_threads.revision + 1
65
+ WHERE vendo_threads.subject = EXCLUDED.subject
66
+ RETURNING id, subject, messages, title, created_at, updated_at, revision`, [input.id, input.subject, JSON.stringify(input.messages), input.title ?? null, now]);
67
+ const row = result.rows[0];
68
+ if (row === undefined) {
69
+ throw new VendoError("conflict", `thread ${input.id} belongs to another subject`);
70
+ }
71
+ return threadFromRow(row);
72
+ }
73
+ export function stateRowFromRow(row) {
74
+ return {
75
+ appId: text(row["app_id"]),
76
+ subject: text(row["subject"]),
77
+ data: row["data"],
78
+ createdAt: iso(row["created_at"]),
79
+ updatedAt: iso(row["updated_at"]),
80
+ };
81
+ }
82
+ /** The single persistent write path for vendo_state, shared by stateStore.put and
83
+ * the routed records("vendo_state").put so the two doors never drift. Writes
84
+ * created_at once on insert and PRESERVES it on conflict (only data + updated_at
85
+ * change), so the seam's createdAt is stable across puts. Never writes the
86
+ * generated `id` column. */
87
+ export async function putStateRow(db, input, now = new Date().toISOString()) {
88
+ const result = await db.query(`INSERT INTO vendo_state (app_id, subject, data, updated_at, created_at)
89
+ VALUES ($1, $2, $3::jsonb, $4, $4)
90
+ ON CONFLICT (app_id, subject) DO UPDATE SET data = EXCLUDED.data, updated_at = EXCLUDED.updated_at
91
+ RETURNING app_id, subject, data, created_at, updated_at`, [input.appId, input.subject, JSON.stringify(input.data), now]);
92
+ return stateRowFromRow(result.rows[0]);
93
+ }
94
+ export function grantFromRow(row) {
95
+ const expiresAt = optionalIso(row["expires_at"]);
96
+ const revokedAt = optionalIso(row["revoked_at"]);
97
+ return {
98
+ id: text(row["id"]),
99
+ subject: text(row["subject"]),
100
+ tool: text(row["tool"]),
101
+ descriptorHash: text(row["descriptor_hash"]),
102
+ scope: row["scope"],
103
+ duration: text(row["duration"]),
104
+ ...(row["context_key"] == null ? {} : { contextKey: text(row["context_key"]) }),
105
+ ...(row["app_id"] == null ? {} : { appId: text(row["app_id"]) }),
106
+ source: text(row["source"]),
107
+ grantedAt: iso(row["granted_at"]),
108
+ ...(expiresAt === undefined ? {} : { expiresAt }),
109
+ ...(revokedAt === undefined ? {} : { revokedAt }),
110
+ };
111
+ }
112
+ export async function putGrantRow(db, grant) {
113
+ // Grants never cross subjects either (02 §2). The upsert carries the same
114
+ // atomic guard as putThreadRow/putAppRow: on conflict it updates ONLY when
115
+ // the existing row already belongs to EXCLUDED.subject; an empty RETURNING
116
+ // means a foreign row holds the id — refuse the flip.
117
+ const result = await db.query(`INSERT INTO vendo_grants
118
+ (id, subject, tool, descriptor_hash, scope, duration, context_key, app_id, source, granted_at, expires_at, revoked_at)
119
+ VALUES ($1, $2, $3, $4, $5::jsonb, $6, $7, $8, $9, $10, $11, $12)
120
+ ON CONFLICT (id) DO UPDATE SET tool = EXCLUDED.tool,
121
+ descriptor_hash = EXCLUDED.descriptor_hash, scope = EXCLUDED.scope,
122
+ duration = EXCLUDED.duration, context_key = EXCLUDED.context_key,
123
+ app_id = EXCLUDED.app_id, source = EXCLUDED.source, granted_at = EXCLUDED.granted_at,
124
+ expires_at = EXCLUDED.expires_at, revoked_at = EXCLUDED.revoked_at
125
+ WHERE vendo_grants.subject = EXCLUDED.subject
126
+ RETURNING id`, [grant.id, grant.subject, grant.tool, grant.descriptorHash, JSON.stringify(grant.scope), grant.duration,
127
+ grant.contextKey ?? null, grant.appId ?? null, grant.source, grant.grantedAt,
128
+ grant.expiresAt ?? null, grant.revokedAt ?? null]);
129
+ if (result.rows[0] === undefined) {
130
+ throw new VendoError("conflict", `grant ${grant.id} belongs to another subject`);
131
+ }
132
+ }
133
+ export function approvalFromRow(row) {
134
+ const decidedAt = optionalIso(row["decided_at"]);
135
+ const consumedAt = optionalIso(row["consumed_at"]);
136
+ return {
137
+ id: text(row["id"]),
138
+ subject: text(row["subject"]),
139
+ request: row["request"],
140
+ status: text(row["status"]),
141
+ ...(decidedAt === undefined ? {} : { decidedAt }),
142
+ ...(row["session_id"] == null ? {} : { sessionId: text(row["session_id"]) }),
143
+ ...(consumedAt === undefined ? {} : { consumedAt }),
144
+ createdAt: iso(row["created_at"]),
145
+ };
146
+ }
147
+ export async function putApprovalRow(db, row, upsert = true) {
148
+ await db.query(`INSERT INTO vendo_approvals
149
+ (id, subject, request, status, decided_at, session_id, consumed_at, created_at)
150
+ VALUES ($1, $2, $3::jsonb, $4, $5, $6, $7, $8)
151
+ ${upsert ? `ON CONFLICT (id) DO UPDATE SET subject = EXCLUDED.subject, request = EXCLUDED.request,
152
+ status = EXCLUDED.status, decided_at = EXCLUDED.decided_at,
153
+ session_id = EXCLUDED.session_id, consumed_at = EXCLUDED.consumed_at,
154
+ created_at = EXCLUDED.created_at` : ""}`, [row.id, row.subject, JSON.stringify(row.request), row.status, row.decidedAt ?? null,
155
+ row.sessionId ?? null, row.consumedAt ?? null, row.createdAt]);
156
+ }
157
+ /** 02-store §2: vendo_audit is append-only. The insert refuses to touch an
158
+ * existing row ATOMICALLY — ON CONFLICT DO NOTHING plus an empty RETURNING
159
+ * means the id already exists, and the write is rejected instead of replacing
160
+ * history. Deletion happens only through the store erase API (02 §5). */
161
+ export async function putAuditRow(db, event) {
162
+ const result = await db.query(`INSERT INTO vendo_audit (id, at, kind, subject, venue, presence, app_id, tool, event)
163
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::jsonb)
164
+ ON CONFLICT (id) DO NOTHING
165
+ RETURNING id`, [event.id, event.at, event.kind, event.principal.subject, event.venue, event.presence,
166
+ event.appId ?? null, event.tool ?? null, JSON.stringify(event)]);
167
+ if (result.rows[0] === undefined) {
168
+ throw new VendoError("conflict", `audit event ${event.id} already exists (vendo_audit is append-only)`);
169
+ }
170
+ }
171
+ export function runFromRow(row) {
172
+ const finishedAt = optionalIso(row["finished_at"]);
173
+ return {
174
+ id: text(row["id"]),
175
+ appId: text(row["app_id"]),
176
+ trigger: row["trigger"],
177
+ status: text(row["status"]),
178
+ record: row["record"],
179
+ startedAt: iso(row["started_at"]),
180
+ ...(finishedAt === undefined ? {} : { finishedAt }),
181
+ };
182
+ }
183
+ export async function putRunRow(db, run) {
184
+ await db.query(`INSERT INTO vendo_runs (id, app_id, trigger, status, record, started_at, finished_at)
185
+ VALUES ($1, $2, $3::jsonb, $4, $5::jsonb, $6, $7)
186
+ ON CONFLICT (id) DO UPDATE SET app_id = EXCLUDED.app_id, trigger = EXCLUDED.trigger,
187
+ status = EXCLUDED.status, record = EXCLUDED.record, started_at = EXCLUDED.started_at,
188
+ finished_at = EXCLUDED.finished_at`, [run.id, run.appId, JSON.stringify(run.trigger), run.status, JSON.stringify(run.record),
189
+ run.startedAt, run.finishedAt ?? null]);
190
+ }
191
+ //# sourceMappingURL=rows.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rows.js","sourceRoot":"","sources":["../../src/helpers/rows.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { AppId, RunId } from "@vendoai/core";
2
+ import { type VendoStore } from "../store.js";
3
+ import type { RunRow } from "./types.js";
4
+ /** 02-store §3 */
5
+ export declare function runStore(store: VendoStore): {
6
+ put(run: RunRow): Promise<void>;
7
+ get(id: RunId): Promise<RunRow | null>;
8
+ list(filter: {
9
+ appId?: AppId;
10
+ status?: RunRow["status"];
11
+ limit?: number;
12
+ cursor?: string;
13
+ }): Promise<{
14
+ runs: RunRow[];
15
+ cursor?: string;
16
+ }>;
17
+ };
18
+ export type { RunRow } from "./types.js";
19
+ //# sourceMappingURL=runs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runs.d.ts","sourceRoot":"","sources":["../../src/helpers/runs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAS,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAKzC,kBAAkB;AAClB,wBAAgB,QAAQ,CAAC,KAAK,EAAE,UAAU,GAAG;IAC3C,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChC,GAAG,CAAC,EAAE,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACvC,IAAI,CAAC,MAAM,EAAE;QAAE,KAAK,CAAC,EAAE,KAAK,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,EAAE,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC3I,CA2CA;AAED,YAAY,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,47 @@
1
+ import { dbFor } from "../store.js";
2
+ import { putRunRow, runFromRow } from "./rows.js";
3
+ import { cursorMs, decodeCursor, encodeCursor, pageLimit } from "./utils.js";
4
+ import { parseRunData } from "../validate.js";
5
+ /** 02-store §3 */
6
+ export function runStore(store) {
7
+ const db = dbFor(store);
8
+ return {
9
+ async put(run) {
10
+ await putRunRow(db, { id: run.id, ...parseRunData(run, run.id) });
11
+ },
12
+ async get(id) {
13
+ const result = await db.query("SELECT * FROM vendo_runs WHERE id = $1", [id]);
14
+ return result.rows[0] ? runFromRow(result.rows[0]) : null;
15
+ },
16
+ async list(filter) {
17
+ const limit = pageLimit(filter.limit);
18
+ const params = [];
19
+ const clauses = [];
20
+ if (filter.appId !== undefined) {
21
+ params.push(filter.appId);
22
+ clauses.push(`app_id = $${params.length}`);
23
+ }
24
+ if (filter.status !== undefined) {
25
+ params.push(filter.status);
26
+ clauses.push(`status = $${params.length}`);
27
+ }
28
+ if (filter.cursor !== undefined) {
29
+ const cursor = decodeCursor(filter.cursor);
30
+ params.push(cursor.c, cursor.i);
31
+ clauses.push(`(${cursorMs("started_at")}, id) < (${cursorMs(`$${params.length - 1}::timestamptz`)}, $${params.length})`);
32
+ }
33
+ params.push(limit + 1);
34
+ const result = await db.query(`SELECT * FROM vendo_runs${clauses.length ? ` WHERE ${clauses.join(" AND ")}` : ""}
35
+ ORDER BY ${cursorMs("started_at")} DESC, id DESC LIMIT $${params.length}`, params);
36
+ const runs = result.rows.slice(0, limit).map(runFromRow);
37
+ const last = runs.at(-1);
38
+ return {
39
+ runs,
40
+ ...(result.rows.length > limit && last
41
+ ? { cursor: encodeCursor(last.startedAt, last.id) }
42
+ : {}),
43
+ };
44
+ },
45
+ };
46
+ }
47
+ //# sourceMappingURL=runs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runs.js","sourceRoot":"","sources":["../../src/helpers/runs.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAmB,MAAM,aAAa,CAAC;AAErD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC7E,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,kBAAkB;AAClB,MAAM,UAAU,QAAQ,CAAC,KAAiB;IAKxC,MAAM,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;IACxB,OAAO;QACL,KAAK,CAAC,GAAG,CAAC,GAAG;YACX,MAAM,SAAS,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,GAAG,YAAY,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QACpE,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,EAAE;YACV,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,KAAK,CAAC,wCAAwC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;YAC9E,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC5D,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,MAAM;YACf,MAAM,KAAK,GAAG,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACtC,MAAM,MAAM,GAAc,EAAE,CAAC;YAC7B,MAAM,OAAO,GAAa,EAAE,CAAC;YAC7B,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;gBAC/B,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBAC1B,OAAO,CAAC,IAAI,CAAC,aAAa,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;YAC7C,CAAC;YACD,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;gBAChC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;gBAC3B,OAAO,CAAC,IAAI,CAAC,aAAa,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;YAC7C,CAAC;YACD,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;gBAChC,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;gBAC3C,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;gBAChC,OAAO,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC,YAAY,CAAC,YAAY,QAAQ,CAAC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,eAAe,CAAC,MAAM,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;YAC3H,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;YACvB,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,KAAK,CAC3B,2BAA2B,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE;oBACtE,QAAQ,CAAC,YAAY,CAAC,yBAAyB,MAAM,CAAC,MAAM,EAAE,EAC1E,MAAM,CACP,CAAC;YACF,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YACzD,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;YACzB,OAAO;gBACL,IAAI;gBACJ,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,GAAG,KAAK,IAAI,IAAI;oBACpC,CAAC,CAAC,EAAE,MAAM,EAAE,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,EAAE,CAAC,EAAE;oBACnD,CAAC,CAAC,EAAE,CAAC;aACR,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC"}