@nightowlsdev/storage-supabase 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,165 @@
1
+ import { AgentVersion, ContainerFloor, StorageAdapter } from '@nightowlsdev/core';
2
+ import { Pool } from 'pg';
3
+ import { SupabaseClient } from '@supabase/supabase-js';
4
+ import { PostgresStore, PgVector } from '@mastra/pg';
5
+
6
+ interface SupabaseStorageOpts {
7
+ url: string;
8
+ secretKey: string;
9
+ /** Direct/Session Postgres URL (port 5432). REQUIRED — pg/Mastra need the non-pooler port. */
10
+ dbUrl: string;
11
+ max?: number;
12
+ /** Postgres SSL. Default for a remote dbUrl is `true` (verify the server cert). Explicit override honored.
13
+ * Localhost stays unencrypted (undefined). Pass `{ rejectUnauthorized: false }` only to opt out of verification. */
14
+ ssl?: boolean | {
15
+ rejectUnauthorized: boolean;
16
+ };
17
+ }
18
+ interface Ctx$1 {
19
+ pool: Pool;
20
+ sb: SupabaseClient;
21
+ }
22
+
23
+ declare function createMastraPgStore(opts: {
24
+ dbUrl: string;
25
+ disableInit?: boolean;
26
+ }): PostgresStore;
27
+ /** Mastra PgVector over the `nightowls` schema — backs semantic recall. The pgvector EXTENSION is enabled by
28
+ * the packaged 0004 migration; PgVector auto-creates its index table on first recall (spike-verified). */
29
+ declare function createMastraVectorStore(opts: {
30
+ dbUrl: string;
31
+ }): PgVector;
32
+
33
+ /** The shape callers pass — `version` is derived, not supplied. */
34
+ type PublishAgentDef = Omit<AgentVersion, "version"> & {
35
+ tenantId: string;
36
+ actor?: string;
37
+ version?: number;
38
+ };
39
+ /** Insert a new agent version, flip the head pointer, audit. Used by seeding/CLI (not in the core interface).
40
+ * Takes the typed `Ctx` directly (no `__ctx` any-cast); call via `publishAgentVersion(storage.ctx, def)`. */
41
+ declare function publishAgentVersion(ctx: Ctx$1, def: PublishAgentDef): Promise<void>;
42
+ /** One row of an agent's version history (for rollback UX). */
43
+ interface AgentVersionInfo {
44
+ version: number;
45
+ role: string;
46
+ modelId: string;
47
+ status: string;
48
+ isCurrent: boolean;
49
+ }
50
+ /** List an agent's versions (oldest→newest), flagging the current head. Tenant-scoped. Read-only — use it to
51
+ * pick a `toVersion` for `rollbackAgentVersion`. Returns [] for an unknown agent. */
52
+ declare function listAgentVersions(ctx: Ctx$1, tenantId: string, slug: string): Promise<AgentVersionInfo[]>;
53
+ /**
54
+ * Roll an agent back to a prior version's content (R13). APPEND-ONLY: this republishes the target version's
55
+ * content as a NEW head (max+1) rather than moving the pointer backward — so history always moves forward (like
56
+ * `git revert`) and the version counter never collides. Audited as `action='rollback'` with `restoredFrom`.
57
+ * Throws if `toVersion` doesn't exist for this tenant/slug. NOTIFYs the R12 cache invalidation. Returns the new
58
+ * version number + the source it was restored from.
59
+ */
60
+ declare function rollbackAgentVersion(ctx: Ctx$1, args: {
61
+ tenantId: string;
62
+ slug: string;
63
+ toVersion: number;
64
+ actor?: string;
65
+ }): Promise<{
66
+ version: number;
67
+ restoredFrom: number;
68
+ }>;
69
+
70
+ /** A single Night Owls migration: a stable `version`, a human `name`, and fully-qualified `nightowls.*` SQL.
71
+ * Night Owls contributes these to the host's `supabase/migrations/` (via `owl install`/`db eject`);
72
+ * the host applies them with its own tooling — Night Owls never runs DDL itself. */
73
+ interface Migration {
74
+ version: string;
75
+ name: string;
76
+ sql: string;
77
+ }
78
+ declare const MIGRATIONS: Migration[];
79
+
80
+ /**
81
+ * The Night Owls adapter plugin manifest. `@nightowlsdev/cli` discovers this from the host's installed
82
+ * `@nightowlsdev/*` deps, dynamic-imports it, and acts on it declaratively:
83
+ * - `migrations` → ejected into the host's `supabase/migrations/` at install (the user applies them
84
+ * with their own tooling, e.g. `supabase db push` — Night Owls never runs DDL itself),
85
+ * - `env` → merged into `.env.example` (idempotent, only if the KEY is absent),
86
+ * - `config` → the `import` + wiring `snippet` inserted at the `// nightowls:storage` marker,
87
+ * - `init` → a print-only hook run after the wiring (it NEVER applies DDL),
88
+ * - `commands` → `owl storage-supabase <cmd>` subcommands (e.g. `info` — pure, no DB/network).
89
+ *
90
+ * This object STRUCTURALLY matches the CLI's `NightOwlsPlugin` type but does NOT import it (no dependency
91
+ * cycle): all codegen lives in `@nightowlsdev/cli`; this adapter stays data-only. The `init`/command handler
92
+ * contexts are typed STRUCTURALLY (local `Ctx`/inline shapes) — never importing `@nightowlsdev/cli`.
93
+ */
94
+ /** Structural context for this adapter's pure command handlers — matches the CLI's PluginCommandContext. */
95
+ type Ctx = {
96
+ cwd: string;
97
+ log: (m: string) => void;
98
+ args: string[];
99
+ options: Record<string, unknown>;
100
+ };
101
+ declare const nightOwlsPlugin: {
102
+ readonly name: "storage-supabase";
103
+ readonly version: "0.0.0";
104
+ readonly kind: "storage";
105
+ readonly pkg: "@nightowlsdev/storage-supabase";
106
+ readonly description: "Supabase Postgres storage + the nightowls schema (migrations, Realtime, RLS).";
107
+ readonly migrations: Migration[];
108
+ readonly env: readonly [{
109
+ readonly key: "SUPABASE_URL";
110
+ readonly example: "http://127.0.0.1:54321";
111
+ readonly comment: "Supabase project URL";
112
+ }, {
113
+ readonly key: "SUPABASE_SECRET_KEY";
114
+ readonly example: "";
115
+ readonly comment: "service_role/secret — server storage adapter ONLY (never in the browser)";
116
+ }, {
117
+ readonly key: "DATABASE_URL";
118
+ readonly example: "postgresql://postgres:postgres@127.0.0.1:5432/postgres";
119
+ readonly comment: "Session/Direct port 5432 — NOT 6543 (nightowls migrations + the pg pool)";
120
+ }, {
121
+ readonly key: "SUPABASE_ANON_KEY";
122
+ readonly example: "";
123
+ readonly comment: "publishable/anon — browser auth + Realtime ONLY";
124
+ }];
125
+ readonly config: {
126
+ readonly import: "import { createSupabaseStorage } from '@nightowlsdev/storage-supabase';";
127
+ readonly snippet: "storage = createSupabaseStorage({ url: env.SUPABASE_URL, secretKey: env.SUPABASE_SECRET_KEY, dbUrl: env.DATABASE_URL });";
128
+ readonly marker: "storage";
129
+ };
130
+ readonly init: (ctx: {
131
+ cwd: string;
132
+ log: (m: string) => void;
133
+ }) => void;
134
+ readonly commands: readonly [{
135
+ readonly name: "info";
136
+ readonly description: "Show the nightowls migrations and env this adapter contributes.";
137
+ readonly run: (ctx: Ctx) => void;
138
+ }];
139
+ };
140
+
141
+ interface PostgresFloorOpts {
142
+ /** Stale-lock reclaim window — a held lock older than this is stealable (a crashed holder). Default 180_000. */
143
+ maxHoldMs?: number;
144
+ /** Poll interval while queued for the floor. Default 250. */
145
+ pollMs?: number;
146
+ }
147
+ /** A cross-instance {@link ContainerFloor} backed by Postgres (E3). Mutual exclusion is guaranteed by the
148
+ * `floor_lock` primary key; FIFO order by `floor_waiter.enqueued_at` (millisecond-truncated so the JS-Date
149
+ * precision a node-pg param carries matches the stored value, with `run_id` breaking ms ties → a total order);
150
+ * waiter liveness by a `last_seen` HEARTBEAT touched on every poll (so a legitimately deep waiter — which can
151
+ * wait many×maxHold behind others — is never mistaken for a crashed orphan, while a dead waiter still clears
152
+ * within seconds). Wire it for serverless:
153
+ * `new SwarmEngine({ …, floor: createPostgresFloor(storage.ctx.pool) })`. */
154
+ declare function createPostgresFloor(pool: Pool, opts?: PostgresFloorOpts): ContainerFloor;
155
+
156
+ /** The Supabase StorageAdapter plus a typed internal handle (`ctx`) so management helpers
157
+ * like `publishAgentVersion` can reuse the same pg pool without any `as any` coupling. */
158
+ interface SupabaseStorage extends StorageAdapter {
159
+ /** Internal pg pool + supabase-js client. Pass to `publishAgentVersion(storage.ctx, def)`. */
160
+ readonly ctx: Ctx$1;
161
+ close(): Promise<void>;
162
+ }
163
+ declare function createSupabaseStorage(opts: SupabaseStorageOpts): SupabaseStorage;
164
+
165
+ export { type AgentVersionInfo, type Ctx$1 as Ctx, MIGRATIONS, type Migration, type PostgresFloorOpts, type SupabaseStorage, type SupabaseStorageOpts, createMastraPgStore, createMastraVectorStore, createPostgresFloor, createSupabaseStorage, listAgentVersions, nightOwlsPlugin, publishAgentVersion, rollbackAgentVersion };
@@ -0,0 +1,165 @@
1
+ import { AgentVersion, ContainerFloor, StorageAdapter } from '@nightowlsdev/core';
2
+ import { Pool } from 'pg';
3
+ import { SupabaseClient } from '@supabase/supabase-js';
4
+ import { PostgresStore, PgVector } from '@mastra/pg';
5
+
6
+ interface SupabaseStorageOpts {
7
+ url: string;
8
+ secretKey: string;
9
+ /** Direct/Session Postgres URL (port 5432). REQUIRED — pg/Mastra need the non-pooler port. */
10
+ dbUrl: string;
11
+ max?: number;
12
+ /** Postgres SSL. Default for a remote dbUrl is `true` (verify the server cert). Explicit override honored.
13
+ * Localhost stays unencrypted (undefined). Pass `{ rejectUnauthorized: false }` only to opt out of verification. */
14
+ ssl?: boolean | {
15
+ rejectUnauthorized: boolean;
16
+ };
17
+ }
18
+ interface Ctx$1 {
19
+ pool: Pool;
20
+ sb: SupabaseClient;
21
+ }
22
+
23
+ declare function createMastraPgStore(opts: {
24
+ dbUrl: string;
25
+ disableInit?: boolean;
26
+ }): PostgresStore;
27
+ /** Mastra PgVector over the `nightowls` schema — backs semantic recall. The pgvector EXTENSION is enabled by
28
+ * the packaged 0004 migration; PgVector auto-creates its index table on first recall (spike-verified). */
29
+ declare function createMastraVectorStore(opts: {
30
+ dbUrl: string;
31
+ }): PgVector;
32
+
33
+ /** The shape callers pass — `version` is derived, not supplied. */
34
+ type PublishAgentDef = Omit<AgentVersion, "version"> & {
35
+ tenantId: string;
36
+ actor?: string;
37
+ version?: number;
38
+ };
39
+ /** Insert a new agent version, flip the head pointer, audit. Used by seeding/CLI (not in the core interface).
40
+ * Takes the typed `Ctx` directly (no `__ctx` any-cast); call via `publishAgentVersion(storage.ctx, def)`. */
41
+ declare function publishAgentVersion(ctx: Ctx$1, def: PublishAgentDef): Promise<void>;
42
+ /** One row of an agent's version history (for rollback UX). */
43
+ interface AgentVersionInfo {
44
+ version: number;
45
+ role: string;
46
+ modelId: string;
47
+ status: string;
48
+ isCurrent: boolean;
49
+ }
50
+ /** List an agent's versions (oldest→newest), flagging the current head. Tenant-scoped. Read-only — use it to
51
+ * pick a `toVersion` for `rollbackAgentVersion`. Returns [] for an unknown agent. */
52
+ declare function listAgentVersions(ctx: Ctx$1, tenantId: string, slug: string): Promise<AgentVersionInfo[]>;
53
+ /**
54
+ * Roll an agent back to a prior version's content (R13). APPEND-ONLY: this republishes the target version's
55
+ * content as a NEW head (max+1) rather than moving the pointer backward — so history always moves forward (like
56
+ * `git revert`) and the version counter never collides. Audited as `action='rollback'` with `restoredFrom`.
57
+ * Throws if `toVersion` doesn't exist for this tenant/slug. NOTIFYs the R12 cache invalidation. Returns the new
58
+ * version number + the source it was restored from.
59
+ */
60
+ declare function rollbackAgentVersion(ctx: Ctx$1, args: {
61
+ tenantId: string;
62
+ slug: string;
63
+ toVersion: number;
64
+ actor?: string;
65
+ }): Promise<{
66
+ version: number;
67
+ restoredFrom: number;
68
+ }>;
69
+
70
+ /** A single Night Owls migration: a stable `version`, a human `name`, and fully-qualified `nightowls.*` SQL.
71
+ * Night Owls contributes these to the host's `supabase/migrations/` (via `owl install`/`db eject`);
72
+ * the host applies them with its own tooling — Night Owls never runs DDL itself. */
73
+ interface Migration {
74
+ version: string;
75
+ name: string;
76
+ sql: string;
77
+ }
78
+ declare const MIGRATIONS: Migration[];
79
+
80
+ /**
81
+ * The Night Owls adapter plugin manifest. `@nightowlsdev/cli` discovers this from the host's installed
82
+ * `@nightowlsdev/*` deps, dynamic-imports it, and acts on it declaratively:
83
+ * - `migrations` → ejected into the host's `supabase/migrations/` at install (the user applies them
84
+ * with their own tooling, e.g. `supabase db push` — Night Owls never runs DDL itself),
85
+ * - `env` → merged into `.env.example` (idempotent, only if the KEY is absent),
86
+ * - `config` → the `import` + wiring `snippet` inserted at the `// nightowls:storage` marker,
87
+ * - `init` → a print-only hook run after the wiring (it NEVER applies DDL),
88
+ * - `commands` → `owl storage-supabase <cmd>` subcommands (e.g. `info` — pure, no DB/network).
89
+ *
90
+ * This object STRUCTURALLY matches the CLI's `NightOwlsPlugin` type but does NOT import it (no dependency
91
+ * cycle): all codegen lives in `@nightowlsdev/cli`; this adapter stays data-only. The `init`/command handler
92
+ * contexts are typed STRUCTURALLY (local `Ctx`/inline shapes) — never importing `@nightowlsdev/cli`.
93
+ */
94
+ /** Structural context for this adapter's pure command handlers — matches the CLI's PluginCommandContext. */
95
+ type Ctx = {
96
+ cwd: string;
97
+ log: (m: string) => void;
98
+ args: string[];
99
+ options: Record<string, unknown>;
100
+ };
101
+ declare const nightOwlsPlugin: {
102
+ readonly name: "storage-supabase";
103
+ readonly version: "0.0.0";
104
+ readonly kind: "storage";
105
+ readonly pkg: "@nightowlsdev/storage-supabase";
106
+ readonly description: "Supabase Postgres storage + the nightowls schema (migrations, Realtime, RLS).";
107
+ readonly migrations: Migration[];
108
+ readonly env: readonly [{
109
+ readonly key: "SUPABASE_URL";
110
+ readonly example: "http://127.0.0.1:54321";
111
+ readonly comment: "Supabase project URL";
112
+ }, {
113
+ readonly key: "SUPABASE_SECRET_KEY";
114
+ readonly example: "";
115
+ readonly comment: "service_role/secret — server storage adapter ONLY (never in the browser)";
116
+ }, {
117
+ readonly key: "DATABASE_URL";
118
+ readonly example: "postgresql://postgres:postgres@127.0.0.1:5432/postgres";
119
+ readonly comment: "Session/Direct port 5432 — NOT 6543 (nightowls migrations + the pg pool)";
120
+ }, {
121
+ readonly key: "SUPABASE_ANON_KEY";
122
+ readonly example: "";
123
+ readonly comment: "publishable/anon — browser auth + Realtime ONLY";
124
+ }];
125
+ readonly config: {
126
+ readonly import: "import { createSupabaseStorage } from '@nightowlsdev/storage-supabase';";
127
+ readonly snippet: "storage = createSupabaseStorage({ url: env.SUPABASE_URL, secretKey: env.SUPABASE_SECRET_KEY, dbUrl: env.DATABASE_URL });";
128
+ readonly marker: "storage";
129
+ };
130
+ readonly init: (ctx: {
131
+ cwd: string;
132
+ log: (m: string) => void;
133
+ }) => void;
134
+ readonly commands: readonly [{
135
+ readonly name: "info";
136
+ readonly description: "Show the nightowls migrations and env this adapter contributes.";
137
+ readonly run: (ctx: Ctx) => void;
138
+ }];
139
+ };
140
+
141
+ interface PostgresFloorOpts {
142
+ /** Stale-lock reclaim window — a held lock older than this is stealable (a crashed holder). Default 180_000. */
143
+ maxHoldMs?: number;
144
+ /** Poll interval while queued for the floor. Default 250. */
145
+ pollMs?: number;
146
+ }
147
+ /** A cross-instance {@link ContainerFloor} backed by Postgres (E3). Mutual exclusion is guaranteed by the
148
+ * `floor_lock` primary key; FIFO order by `floor_waiter.enqueued_at` (millisecond-truncated so the JS-Date
149
+ * precision a node-pg param carries matches the stored value, with `run_id` breaking ms ties → a total order);
150
+ * waiter liveness by a `last_seen` HEARTBEAT touched on every poll (so a legitimately deep waiter — which can
151
+ * wait many×maxHold behind others — is never mistaken for a crashed orphan, while a dead waiter still clears
152
+ * within seconds). Wire it for serverless:
153
+ * `new SwarmEngine({ …, floor: createPostgresFloor(storage.ctx.pool) })`. */
154
+ declare function createPostgresFloor(pool: Pool, opts?: PostgresFloorOpts): ContainerFloor;
155
+
156
+ /** The Supabase StorageAdapter plus a typed internal handle (`ctx`) so management helpers
157
+ * like `publishAgentVersion` can reuse the same pg pool without any `as any` coupling. */
158
+ interface SupabaseStorage extends StorageAdapter {
159
+ /** Internal pg pool + supabase-js client. Pass to `publishAgentVersion(storage.ctx, def)`. */
160
+ readonly ctx: Ctx$1;
161
+ close(): Promise<void>;
162
+ }
163
+ declare function createSupabaseStorage(opts: SupabaseStorageOpts): SupabaseStorage;
164
+
165
+ export { type AgentVersionInfo, type Ctx$1 as Ctx, MIGRATIONS, type Migration, type PostgresFloorOpts, type SupabaseStorage, type SupabaseStorageOpts, createMastraPgStore, createMastraVectorStore, createPostgresFloor, createSupabaseStorage, listAgentVersions, nightOwlsPlugin, publishAgentVersion, rollbackAgentVersion };