@nightowlsdev/storage-supabase 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +138 -2
- package/dist/index.d.cts +93 -9
- package/dist/index.d.ts +93 -9
- package/dist/index.js +131 -1
- package/package.json +5 -5
package/dist/index.cjs
CHANGED
|
@@ -25,13 +25,19 @@ __export(index_exports, {
|
|
|
25
25
|
createMastraVectorStore: () => createMastraVectorStore,
|
|
26
26
|
createPostgresFloor: () => createPostgresFloor,
|
|
27
27
|
createSupabaseStorage: () => createSupabaseStorage,
|
|
28
|
+
ensureAgentVersion: () => ensureAgentVersion,
|
|
29
|
+
hostOwnedOrgMembershipSql: () => hostOwnedOrgMembershipSql,
|
|
30
|
+
hostOwnedOrgMigration: () => hostOwnedOrgMigration,
|
|
28
31
|
listAgentVersions: () => listAgentVersions,
|
|
32
|
+
listTenants: () => listTenants,
|
|
29
33
|
makeBundleRepo: () => makeBundleRepo,
|
|
30
34
|
makeBundleWritableRepo: () => makeBundleWritableRepo,
|
|
35
|
+
makeThreadStore: () => makeThreadStore,
|
|
31
36
|
makeVersionedRepo: () => makeVersionedRepo,
|
|
32
37
|
nightOwlsPlugin: () => nightOwlsPlugin,
|
|
33
38
|
publishAgentVersion: () => publishAgentVersion,
|
|
34
|
-
rollbackAgentVersion: () => rollbackAgentVersion
|
|
39
|
+
rollbackAgentVersion: () => rollbackAgentVersion,
|
|
40
|
+
supabaseUsageSink: () => supabaseUsageSink
|
|
35
41
|
});
|
|
36
42
|
module.exports = __toCommonJS(index_exports);
|
|
37
43
|
|
|
@@ -308,6 +314,18 @@ function makeScratchpadStore(ctx) {
|
|
|
308
314
|
};
|
|
309
315
|
}
|
|
310
316
|
|
|
317
|
+
// src/threads.ts
|
|
318
|
+
function makeThreadStore(ctx) {
|
|
319
|
+
return {
|
|
320
|
+
async ensure({ id, orgId, userId, projectId }) {
|
|
321
|
+
await ctx.pool.query(
|
|
322
|
+
"insert into threads(id, org_id, user_id, project_id) values($1,$2,$3,$4) on conflict (id) do nothing",
|
|
323
|
+
[id, orgId, userId, projectId ?? null]
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
|
|
311
329
|
// src/agents.ts
|
|
312
330
|
var import_core3 = require("@nightowlsdev/core");
|
|
313
331
|
|
|
@@ -468,6 +486,41 @@ async function publishAgentVersion(ctx, def) {
|
|
|
468
486
|
client.release();
|
|
469
487
|
}
|
|
470
488
|
}
|
|
489
|
+
async function ensureAgentVersion(ctx, def) {
|
|
490
|
+
const actor = def.actor ?? SEED_ACTOR;
|
|
491
|
+
(0, import_core3.assertActorMayMutateDefinition)(actor);
|
|
492
|
+
const client = await ctx.pool.connect();
|
|
493
|
+
try {
|
|
494
|
+
await client.query("begin");
|
|
495
|
+
await client.query("select pg_advisory_xact_lock(hashtext($1), hashtext($2))", ["ensure_agent", `${def.tenantId}:${def.slug}`]);
|
|
496
|
+
const head = (await client.query(
|
|
497
|
+
`select v.version from agents a join agent_versions v on v.id = a.current_version_id where a.org_id=$1 and a.slug=$2 and a.project_id is null`,
|
|
498
|
+
[def.tenantId, def.slug]
|
|
499
|
+
)).rows[0];
|
|
500
|
+
if (head) {
|
|
501
|
+
await client.query("commit");
|
|
502
|
+
return { version: head.version, created: false };
|
|
503
|
+
}
|
|
504
|
+
const agent = (await client.query(
|
|
505
|
+
`insert into agents(org_id, slug, is_orchestrator) values($1,$2,$3)
|
|
506
|
+
on conflict (org_id, project_id, slug) do update set slug=excluded.slug returning id`,
|
|
507
|
+
[def.tenantId, def.slug, def.role === "orchestrator"]
|
|
508
|
+
)).rows[0];
|
|
509
|
+
const version = await commitVersion(client, agent.id, def.tenantId, def, "publish", auditActor(actor), { slug: def.slug });
|
|
510
|
+
await client.query("commit");
|
|
511
|
+
await notifyInvalidate(client, def.tenantId, def.slug);
|
|
512
|
+
return { version, created: true };
|
|
513
|
+
} catch (e) {
|
|
514
|
+
await client.query("rollback");
|
|
515
|
+
throw e;
|
|
516
|
+
} finally {
|
|
517
|
+
client.release();
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
async function listTenants(ctx) {
|
|
521
|
+
const rows = await many(ctx.pool, "select id::text as id from orgs order by created_at asc");
|
|
522
|
+
return rows.map((r) => r.id);
|
|
523
|
+
}
|
|
471
524
|
async function listAgentVersions(ctx, tenantId, slug) {
|
|
472
525
|
const rows = await many(
|
|
473
526
|
ctx.pool,
|
|
@@ -633,6 +686,78 @@ function createMastraVectorStore(opts) {
|
|
|
633
686
|
return new import_pg2.PgVector({ id: "nightowls-vector", connectionString: opts.dbUrl, schemaName: "nightowls" });
|
|
634
687
|
}
|
|
635
688
|
|
|
689
|
+
// src/usage-sink.ts
|
|
690
|
+
var IDENT = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
691
|
+
var TABLE = /^[a-zA-Z_][a-zA-Z0-9_]*(\.[a-zA-Z_][a-zA-Z0-9_]*)?$/;
|
|
692
|
+
function defaultMap(ev, ctx) {
|
|
693
|
+
const b = ev.data.breakdown;
|
|
694
|
+
return {
|
|
695
|
+
generation_id: ev.data.generationId,
|
|
696
|
+
run_id: ctx.runId,
|
|
697
|
+
org_id: ctx.tenantId,
|
|
698
|
+
agent_slug: ev.data.slug,
|
|
699
|
+
model_id: ev.data.modelId,
|
|
700
|
+
input_tokens: b.inputTokens ?? 0,
|
|
701
|
+
output_tokens: b.outputTokens ?? 0,
|
|
702
|
+
cost_usd: ev.data.cost.usd
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
function supabaseUsageSink(opts) {
|
|
706
|
+
const conflict = opts.conflictColumn ?? "generation_id";
|
|
707
|
+
if (!IDENT.test(conflict)) throw new Error(`supabaseUsageSink: unsafe conflictColumn ${JSON.stringify(conflict)}`);
|
|
708
|
+
if (!TABLE.test(opts.table)) throw new Error(`supabaseUsageSink: unsafe table ${JSON.stringify(opts.table)}`);
|
|
709
|
+
const map = opts.map ?? defaultMap;
|
|
710
|
+
return async (ev, ctx) => {
|
|
711
|
+
if (ev.type !== "swarm.usage") return;
|
|
712
|
+
const row = map(ev, ctx);
|
|
713
|
+
if (!row) return;
|
|
714
|
+
const cols = Object.keys(row);
|
|
715
|
+
for (const c of cols) if (!IDENT.test(c)) throw new Error(`supabaseUsageSink: unsafe column ${JSON.stringify(c)}`);
|
|
716
|
+
const placeholders = cols.map((_c, i) => `$${i + 1}`).join(",");
|
|
717
|
+
const sql = `insert into ${opts.table}(${cols.join(",")}) values(${placeholders}) on conflict (${conflict}) do nothing`;
|
|
718
|
+
await opts.pool.query(sql, cols.map((c) => row[c]));
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// src/host-org-source.ts
|
|
723
|
+
var QUALIFIED = /^[a-zA-Z_][a-zA-Z0-9_]*(\.[a-zA-Z_][a-zA-Z0-9_]*)?$/;
|
|
724
|
+
var PLAIN = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
725
|
+
function assertIdent(v, kind, re) {
|
|
726
|
+
if (!re.test(v)) throw new Error(`hostOwnedOrgMembership: unsafe ${kind} ${JSON.stringify(v)}`);
|
|
727
|
+
}
|
|
728
|
+
function hostOwnedOrgMembershipSql(opts) {
|
|
729
|
+
const schema = opts.schema ?? "nightowls";
|
|
730
|
+
const orgIdColumn = opts.orgIdColumn ?? "organization_id";
|
|
731
|
+
const userIdColumn = opts.userIdColumn ?? "user_id";
|
|
732
|
+
const userIdExpr = opts.userIdExpr ?? "(select auth.uid())";
|
|
733
|
+
assertIdent(schema, "schema", PLAIN);
|
|
734
|
+
assertIdent(opts.membershipTable, "membershipTable", QUALIFIED);
|
|
735
|
+
assertIdent(orgIdColumn, "orgIdColumn", PLAIN);
|
|
736
|
+
assertIdent(userIdColumn, "userIdColumn", PLAIN);
|
|
737
|
+
if (/;|--|\/\*|\$\$/.test(userIdExpr)) throw new Error(`hostOwnedOrgMembership: unsafe userIdExpr ${JSON.stringify(userIdExpr)}`);
|
|
738
|
+
return `-- FR-015 \u2014 override ${schema}.is_org_member to read the host membership table ${opts.membershipTable}.
|
|
739
|
+
create or replace function ${schema}.is_org_member(p_org uuid)
|
|
740
|
+
returns boolean language sql stable set search_path = '' as $$
|
|
741
|
+
select exists (
|
|
742
|
+
select 1 from ${opts.membershipTable} m
|
|
743
|
+
where m.${orgIdColumn} = p_org and m.${userIdColumn} = ${userIdExpr}
|
|
744
|
+
);
|
|
745
|
+
$$;
|
|
746
|
+
-- The Realtime gate calls is_org_member as the AUTHENTICATED role during a private subscribe, so it must be able
|
|
747
|
+
-- to read the membership table. (No-op if you already granted it.)
|
|
748
|
+
grant select on ${opts.membershipTable} to authenticated;
|
|
749
|
+
`;
|
|
750
|
+
}
|
|
751
|
+
function hostOwnedOrgMigration(opts) {
|
|
752
|
+
const version = opts.version ?? "host_org_source";
|
|
753
|
+
assertIdent(version, "version", PLAIN);
|
|
754
|
+
return {
|
|
755
|
+
version,
|
|
756
|
+
name: `host-owned org/membership source (is_org_member \u2192 ${opts.membershipTable})`,
|
|
757
|
+
sql: hostOwnedOrgMembershipSql(opts)
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
|
|
636
761
|
// src/migrations/0001_core.ts
|
|
637
762
|
var M0001_CORE = {
|
|
638
763
|
version: "0001_core",
|
|
@@ -2085,7 +2210,12 @@ function createSupabaseStorage(opts) {
|
|
|
2085
2210
|
runs: makeRunStore(ctx),
|
|
2086
2211
|
events: makeEventStore(ctx),
|
|
2087
2212
|
messages: makeMessageStore(ctx),
|
|
2213
|
+
// FR-009: the engine ensures this thread row at run start, so a host no longer hand-writes raw SQL against
|
|
2214
|
+
// `nightowls.threads`, and `messages.append` cannot throw `unknown thread` through the supported path.
|
|
2215
|
+
threads: makeThreadStore(ctx),
|
|
2088
2216
|
scratchpad: makeScratchpadStore(ctx),
|
|
2217
|
+
// FR-016: enumerate the engine's tenants for idempotent per-tenant crew backfill (pairs with ensureAgentVersion).
|
|
2218
|
+
listTenants: () => listTenants(ctx),
|
|
2089
2219
|
// Record the suspended run in the tenant-scoped followup index so `runs.findSuspended` (the resume
|
|
2090
2220
|
// authz gate) can resolve it. RE-OPEN on conflict (reset `answered_at`): Mastra REUSES the same
|
|
2091
2221
|
// `toolCallId` when an agent asks AGAIN after a resume, so the engine's `followupId = runId:toolCallId`
|
|
@@ -2155,11 +2285,17 @@ function createSupabaseStorage(opts) {
|
|
|
2155
2285
|
createMastraVectorStore,
|
|
2156
2286
|
createPostgresFloor,
|
|
2157
2287
|
createSupabaseStorage,
|
|
2288
|
+
ensureAgentVersion,
|
|
2289
|
+
hostOwnedOrgMembershipSql,
|
|
2290
|
+
hostOwnedOrgMigration,
|
|
2158
2291
|
listAgentVersions,
|
|
2292
|
+
listTenants,
|
|
2159
2293
|
makeBundleRepo,
|
|
2160
2294
|
makeBundleWritableRepo,
|
|
2295
|
+
makeThreadStore,
|
|
2161
2296
|
makeVersionedRepo,
|
|
2162
2297
|
nightOwlsPlugin,
|
|
2163
2298
|
publishAgentVersion,
|
|
2164
|
-
rollbackAgentVersion
|
|
2299
|
+
rollbackAgentVersion,
|
|
2300
|
+
supabaseUsageSink
|
|
2165
2301
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AgentVersionContent, SwarmActor, AgentVersionInfo, VersionedRepo, BundleRepo, BundleWritableRepo, ContainerFloor, StorageAdapter } from '@nightowlsdev/core';
|
|
1
|
+
import { AgentVersionContent, SwarmActor, AgentVersionInfo, VersionedRepo, ThreadStore, SwarmEvent, SwarmContext, BundleRepo, BundleWritableRepo, ContainerFloor, StorageAdapter } from '@nightowlsdev/core';
|
|
2
2
|
export { AgentVersionInfo } from '@nightowlsdev/core';
|
|
3
3
|
import { Pool } from 'pg';
|
|
4
4
|
import { SupabaseClient } from '@supabase/supabase-js';
|
|
@@ -56,6 +56,23 @@ type PublishAgentDef = AgentVersionContent & {
|
|
|
56
56
|
declare function publishAgentVersion(ctx: Ctx$1, def: PublishAgentDef): Promise<{
|
|
57
57
|
version: number;
|
|
58
58
|
}>;
|
|
59
|
+
/**
|
|
60
|
+
* FR-016 — idempotently publish an agent version ONLY if the tenant has no head for that slug yet. The "seed a
|
|
61
|
+
* crew for tenant X if absent" primitive a host loops over `listTenants()` for backfill, so it never re-publishes
|
|
62
|
+
* (no version churn) on a tenant that already has the agent. Returns the head version + whether THIS call created
|
|
63
|
+
* it. (Use `publishAgentVersion` directly when you intentionally want a new version every time.)
|
|
64
|
+
*
|
|
65
|
+
* ATOMIC: the existence check + the publish run in ONE transaction under a per-(tenant,slug) advisory lock, so two
|
|
66
|
+
* concurrent backfill workers for the same slug can't both observe "absent" and each append a version — the second
|
|
67
|
+
* waiter sees v1 already present and returns `created:false`.
|
|
68
|
+
*/
|
|
69
|
+
declare function ensureAgentVersion(ctx: Ctx$1, def: PublishAgentDef): Promise<{
|
|
70
|
+
version: number;
|
|
71
|
+
created: boolean;
|
|
72
|
+
}>;
|
|
73
|
+
/** FR-016 — enumerate the engine's tenants (the `orgs` the engine knows), so a host can backfill each tenant's
|
|
74
|
+
* crew without raw SQL against whatever host table happens to hold tenants. Ordered oldest→newest. */
|
|
75
|
+
declare function listTenants(ctx: Ctx$1): Promise<string[]>;
|
|
59
76
|
/** List an agent's versions (oldest→newest), flagging the current head. Tenant-scoped. Read-only — use it to
|
|
60
77
|
* pick a `toVersion` for `rollbackAgentVersion`. Returns [] for an unknown agent. */
|
|
61
78
|
declare function listAgentVersions(ctx: Ctx$1, tenantId: string, slug: string): Promise<AgentVersionInfo[]>;
|
|
@@ -76,15 +93,51 @@ declare function rollbackAgentVersion(ctx: Ctx$1, args: {
|
|
|
76
93
|
restoredFrom: number;
|
|
77
94
|
}>;
|
|
78
95
|
|
|
79
|
-
/** The READ-ONLY bundle repo (head/getVersion/listSlugs). The writable surface lives in `makeBundleWritableRepo`. */
|
|
80
|
-
declare function makeBundleRepo(ctx: Ctx$1): BundleRepo;
|
|
81
96
|
/**
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
97
|
+
* FR-009 — the supported, schema-private way to create the `threads` row a run FK-references. `runs.thread_id` is
|
|
98
|
+
* a NOT NULL FK to `threads(id)`, and `messages.append` throws `unknown thread` without it, so a host previously
|
|
99
|
+
* had to reach into the raw pool and hardcode the engine's private column names. `ensure` does an insert-or-ignore
|
|
100
|
+
* on the primary key, so it is safe to call before every run (the engine calls it at run start). `org_id` /
|
|
101
|
+
* `user_id` are NOT NULL in the schema; `project_id` is the optional host-owned sub-scope.
|
|
86
102
|
*/
|
|
87
|
-
declare function
|
|
103
|
+
declare function makeThreadStore(ctx: Ctx$1): ThreadStore;
|
|
104
|
+
|
|
105
|
+
/** A flat row to upsert: column name → value. Column names are HOST code (trusted), validated to a safe identifier. */
|
|
106
|
+
type UsageRow = Record<string, string | number | boolean | null>;
|
|
107
|
+
interface SupabaseUsageSinkOpts {
|
|
108
|
+
/** The pg pool to write through (e.g. `storage.ctx.pool`). */
|
|
109
|
+
pool: Pool;
|
|
110
|
+
/** Destination table (schema-qualified if needed, e.g. `"public.llm_usage_logs"`). */
|
|
111
|
+
table: string;
|
|
112
|
+
/**
|
|
113
|
+
* The column that carries the engine's per-generation `generationId`. The sink upserts `on conflict (<this>) do
|
|
114
|
+
* nothing`, so a re-delivered usage event (realtime replay, retry) inserts at most once. Must be UNIQUE/PK in
|
|
115
|
+
* the table. Default `"generation_id"`.
|
|
116
|
+
*/
|
|
117
|
+
conflictColumn?: string;
|
|
118
|
+
/**
|
|
119
|
+
* Map ONE `swarm.usage` event → the row to insert. Defaults to a sensible shape
|
|
120
|
+
* (`generation_id`, `run_id`, `org_id`, `agent_slug`, `model_id`, `input_tokens`, `output_tokens`, `cost_usd`).
|
|
121
|
+
* Override to match your table. Return `null` to skip an event.
|
|
122
|
+
*/
|
|
123
|
+
map?: (ev: Extract<SwarmEvent, {
|
|
124
|
+
type: "swarm.usage";
|
|
125
|
+
}>, ctx: SwarmContext) => UsageRow | null;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* FR-004 — an `onEvent` handler that records each per-generation `swarm.usage` into a host Supabase table, keyed
|
|
129
|
+
* idempotently by the engine's stable `generationId` (no double-count under delegation, no OTel pipeline). Wire it
|
|
130
|
+
* onto `defineSwarm({ onEvent })` (compose with your own observer if you have one). Failures are the caller's to
|
|
131
|
+
* handle — the engine swallows a throwing `onEvent` so a sink hiccup never breaks a run, but you should log your own.
|
|
132
|
+
*
|
|
133
|
+
* NOTE: this records the engine's METERED (token-priced) cost — the framework never knows a provider's post-hoc
|
|
134
|
+
* BILLED amount. Use `generationId` to reconcile "actual" cost later if your provider exposes it (see FR-004).
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* const sink = supabaseUsageSink({ pool: storage.ctx.pool, table: "llm_usage_logs" });
|
|
138
|
+
* defineSwarm({ ..., onEvent: sink });
|
|
139
|
+
*/
|
|
140
|
+
declare function supabaseUsageSink(opts: SupabaseUsageSinkOpts): (ev: SwarmEvent, ctx: SwarmContext) => Promise<void>;
|
|
88
141
|
|
|
89
142
|
/** A single Night Owls migration: a stable `version`, a human `name`, and fully-qualified `nightowls.*` SQL.
|
|
90
143
|
* Night Owls contributes these to the host's `supabase/migrations/` (via `owl install`/`db eject`);
|
|
@@ -96,6 +149,37 @@ interface Migration {
|
|
|
96
149
|
}
|
|
97
150
|
declare const MIGRATIONS: Migration[];
|
|
98
151
|
|
|
152
|
+
interface HostOrgSourceOpts {
|
|
153
|
+
/** The engine schema the override targets (the deployed name). Default `"nightowls"`. */
|
|
154
|
+
schema?: string;
|
|
155
|
+
/** The host membership table, schema-qualified, e.g. `"public.organization_members"`. */
|
|
156
|
+
membershipTable: string;
|
|
157
|
+
/** The org-id column on the membership table. Default `"organization_id"`. */
|
|
158
|
+
orgIdColumn?: string;
|
|
159
|
+
/** The user-id column on the membership table (compared to `userIdExpr`). Default `"user_id"`. */
|
|
160
|
+
userIdColumn?: string;
|
|
161
|
+
/** The SQL expression for the current user id (must match `userIdColumn`'s type). Default `(select auth.uid())`. */
|
|
162
|
+
userIdExpr?: string;
|
|
163
|
+
/** Migration version label. Default `"host_org_source"`. */
|
|
164
|
+
version?: string;
|
|
165
|
+
}
|
|
166
|
+
/** Generate the SQL that overrides `<schema>.is_org_member` to read the host's membership table (+ the GRANT the
|
|
167
|
+
* Realtime gate needs to read it as the `authenticated` role during a private subscribe). */
|
|
168
|
+
declare function hostOwnedOrgMembershipSql(opts: HostOrgSourceOpts): string;
|
|
169
|
+
/** The same override packaged as a `Migration` so a host can append it to its migration set AFTER `0001_core`
|
|
170
|
+
* (it `create or replace`s the function the core migration defined). */
|
|
171
|
+
declare function hostOwnedOrgMigration(opts: HostOrgSourceOpts): Migration;
|
|
172
|
+
|
|
173
|
+
/** The READ-ONLY bundle repo (head/getVersion/listSlugs). The writable surface lives in `makeBundleWritableRepo`. */
|
|
174
|
+
declare function makeBundleRepo(ctx: Ctx$1): BundleRepo;
|
|
175
|
+
/**
|
|
176
|
+
* The WRITABLE bundle definition repo (BN2) — the core `BundleWritableRepo` contract backed by Postgres. Extends
|
|
177
|
+
* the read repo with the append-only `publish`/`rollback`/`listVersions` surface, on the SAME shared
|
|
178
|
+
* `appendVersion` primitive as agents (so a bundle gets the same race-safe append-and-flip + audit + the
|
|
179
|
+
* published-row immutability trigger). Every mutation enforces the non-bypassable agent-bar first.
|
|
180
|
+
*/
|
|
181
|
+
declare function makeBundleWritableRepo(ctx: Ctx$1): BundleWritableRepo;
|
|
182
|
+
|
|
99
183
|
/**
|
|
100
184
|
* The Night Owls adapter plugin manifest. `@nightowlsdev/cli` discovers this from the host's installed
|
|
101
185
|
* `@nightowlsdev/*` deps, dynamic-imports it, and acts on it declaratively:
|
|
@@ -181,4 +265,4 @@ interface SupabaseStorage extends StorageAdapter {
|
|
|
181
265
|
}
|
|
182
266
|
declare function createSupabaseStorage(opts: SupabaseStorageOpts): SupabaseStorage;
|
|
183
267
|
|
|
184
|
-
export { type Ctx$1 as Ctx, MIGRATIONS, type Migration, type PostgresFloorOpts, type PublishAgentDef, type SupabaseStorage, type SupabaseStorageOpts, createMastraPgStore, createMastraVectorStore, createPostgresFloor, createSupabaseStorage, listAgentVersions, makeBundleRepo, makeBundleWritableRepo, makeVersionedRepo, nightOwlsPlugin, publishAgentVersion, rollbackAgentVersion };
|
|
268
|
+
export { type Ctx$1 as Ctx, type HostOrgSourceOpts, MIGRATIONS, type Migration, type PostgresFloorOpts, type PublishAgentDef, type SupabaseStorage, type SupabaseStorageOpts, type SupabaseUsageSinkOpts, type UsageRow, createMastraPgStore, createMastraVectorStore, createPostgresFloor, createSupabaseStorage, ensureAgentVersion, hostOwnedOrgMembershipSql, hostOwnedOrgMigration, listAgentVersions, listTenants, makeBundleRepo, makeBundleWritableRepo, makeThreadStore, makeVersionedRepo, nightOwlsPlugin, publishAgentVersion, rollbackAgentVersion, supabaseUsageSink };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AgentVersionContent, SwarmActor, AgentVersionInfo, VersionedRepo, BundleRepo, BundleWritableRepo, ContainerFloor, StorageAdapter } from '@nightowlsdev/core';
|
|
1
|
+
import { AgentVersionContent, SwarmActor, AgentVersionInfo, VersionedRepo, ThreadStore, SwarmEvent, SwarmContext, BundleRepo, BundleWritableRepo, ContainerFloor, StorageAdapter } from '@nightowlsdev/core';
|
|
2
2
|
export { AgentVersionInfo } from '@nightowlsdev/core';
|
|
3
3
|
import { Pool } from 'pg';
|
|
4
4
|
import { SupabaseClient } from '@supabase/supabase-js';
|
|
@@ -56,6 +56,23 @@ type PublishAgentDef = AgentVersionContent & {
|
|
|
56
56
|
declare function publishAgentVersion(ctx: Ctx$1, def: PublishAgentDef): Promise<{
|
|
57
57
|
version: number;
|
|
58
58
|
}>;
|
|
59
|
+
/**
|
|
60
|
+
* FR-016 — idempotently publish an agent version ONLY if the tenant has no head for that slug yet. The "seed a
|
|
61
|
+
* crew for tenant X if absent" primitive a host loops over `listTenants()` for backfill, so it never re-publishes
|
|
62
|
+
* (no version churn) on a tenant that already has the agent. Returns the head version + whether THIS call created
|
|
63
|
+
* it. (Use `publishAgentVersion` directly when you intentionally want a new version every time.)
|
|
64
|
+
*
|
|
65
|
+
* ATOMIC: the existence check + the publish run in ONE transaction under a per-(tenant,slug) advisory lock, so two
|
|
66
|
+
* concurrent backfill workers for the same slug can't both observe "absent" and each append a version — the second
|
|
67
|
+
* waiter sees v1 already present and returns `created:false`.
|
|
68
|
+
*/
|
|
69
|
+
declare function ensureAgentVersion(ctx: Ctx$1, def: PublishAgentDef): Promise<{
|
|
70
|
+
version: number;
|
|
71
|
+
created: boolean;
|
|
72
|
+
}>;
|
|
73
|
+
/** FR-016 — enumerate the engine's tenants (the `orgs` the engine knows), so a host can backfill each tenant's
|
|
74
|
+
* crew without raw SQL against whatever host table happens to hold tenants. Ordered oldest→newest. */
|
|
75
|
+
declare function listTenants(ctx: Ctx$1): Promise<string[]>;
|
|
59
76
|
/** List an agent's versions (oldest→newest), flagging the current head. Tenant-scoped. Read-only — use it to
|
|
60
77
|
* pick a `toVersion` for `rollbackAgentVersion`. Returns [] for an unknown agent. */
|
|
61
78
|
declare function listAgentVersions(ctx: Ctx$1, tenantId: string, slug: string): Promise<AgentVersionInfo[]>;
|
|
@@ -76,15 +93,51 @@ declare function rollbackAgentVersion(ctx: Ctx$1, args: {
|
|
|
76
93
|
restoredFrom: number;
|
|
77
94
|
}>;
|
|
78
95
|
|
|
79
|
-
/** The READ-ONLY bundle repo (head/getVersion/listSlugs). The writable surface lives in `makeBundleWritableRepo`. */
|
|
80
|
-
declare function makeBundleRepo(ctx: Ctx$1): BundleRepo;
|
|
81
96
|
/**
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
97
|
+
* FR-009 — the supported, schema-private way to create the `threads` row a run FK-references. `runs.thread_id` is
|
|
98
|
+
* a NOT NULL FK to `threads(id)`, and `messages.append` throws `unknown thread` without it, so a host previously
|
|
99
|
+
* had to reach into the raw pool and hardcode the engine's private column names. `ensure` does an insert-or-ignore
|
|
100
|
+
* on the primary key, so it is safe to call before every run (the engine calls it at run start). `org_id` /
|
|
101
|
+
* `user_id` are NOT NULL in the schema; `project_id` is the optional host-owned sub-scope.
|
|
86
102
|
*/
|
|
87
|
-
declare function
|
|
103
|
+
declare function makeThreadStore(ctx: Ctx$1): ThreadStore;
|
|
104
|
+
|
|
105
|
+
/** A flat row to upsert: column name → value. Column names are HOST code (trusted), validated to a safe identifier. */
|
|
106
|
+
type UsageRow = Record<string, string | number | boolean | null>;
|
|
107
|
+
interface SupabaseUsageSinkOpts {
|
|
108
|
+
/** The pg pool to write through (e.g. `storage.ctx.pool`). */
|
|
109
|
+
pool: Pool;
|
|
110
|
+
/** Destination table (schema-qualified if needed, e.g. `"public.llm_usage_logs"`). */
|
|
111
|
+
table: string;
|
|
112
|
+
/**
|
|
113
|
+
* The column that carries the engine's per-generation `generationId`. The sink upserts `on conflict (<this>) do
|
|
114
|
+
* nothing`, so a re-delivered usage event (realtime replay, retry) inserts at most once. Must be UNIQUE/PK in
|
|
115
|
+
* the table. Default `"generation_id"`.
|
|
116
|
+
*/
|
|
117
|
+
conflictColumn?: string;
|
|
118
|
+
/**
|
|
119
|
+
* Map ONE `swarm.usage` event → the row to insert. Defaults to a sensible shape
|
|
120
|
+
* (`generation_id`, `run_id`, `org_id`, `agent_slug`, `model_id`, `input_tokens`, `output_tokens`, `cost_usd`).
|
|
121
|
+
* Override to match your table. Return `null` to skip an event.
|
|
122
|
+
*/
|
|
123
|
+
map?: (ev: Extract<SwarmEvent, {
|
|
124
|
+
type: "swarm.usage";
|
|
125
|
+
}>, ctx: SwarmContext) => UsageRow | null;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* FR-004 — an `onEvent` handler that records each per-generation `swarm.usage` into a host Supabase table, keyed
|
|
129
|
+
* idempotently by the engine's stable `generationId` (no double-count under delegation, no OTel pipeline). Wire it
|
|
130
|
+
* onto `defineSwarm({ onEvent })` (compose with your own observer if you have one). Failures are the caller's to
|
|
131
|
+
* handle — the engine swallows a throwing `onEvent` so a sink hiccup never breaks a run, but you should log your own.
|
|
132
|
+
*
|
|
133
|
+
* NOTE: this records the engine's METERED (token-priced) cost — the framework never knows a provider's post-hoc
|
|
134
|
+
* BILLED amount. Use `generationId` to reconcile "actual" cost later if your provider exposes it (see FR-004).
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* const sink = supabaseUsageSink({ pool: storage.ctx.pool, table: "llm_usage_logs" });
|
|
138
|
+
* defineSwarm({ ..., onEvent: sink });
|
|
139
|
+
*/
|
|
140
|
+
declare function supabaseUsageSink(opts: SupabaseUsageSinkOpts): (ev: SwarmEvent, ctx: SwarmContext) => Promise<void>;
|
|
88
141
|
|
|
89
142
|
/** A single Night Owls migration: a stable `version`, a human `name`, and fully-qualified `nightowls.*` SQL.
|
|
90
143
|
* Night Owls contributes these to the host's `supabase/migrations/` (via `owl install`/`db eject`);
|
|
@@ -96,6 +149,37 @@ interface Migration {
|
|
|
96
149
|
}
|
|
97
150
|
declare const MIGRATIONS: Migration[];
|
|
98
151
|
|
|
152
|
+
interface HostOrgSourceOpts {
|
|
153
|
+
/** The engine schema the override targets (the deployed name). Default `"nightowls"`. */
|
|
154
|
+
schema?: string;
|
|
155
|
+
/** The host membership table, schema-qualified, e.g. `"public.organization_members"`. */
|
|
156
|
+
membershipTable: string;
|
|
157
|
+
/** The org-id column on the membership table. Default `"organization_id"`. */
|
|
158
|
+
orgIdColumn?: string;
|
|
159
|
+
/** The user-id column on the membership table (compared to `userIdExpr`). Default `"user_id"`. */
|
|
160
|
+
userIdColumn?: string;
|
|
161
|
+
/** The SQL expression for the current user id (must match `userIdColumn`'s type). Default `(select auth.uid())`. */
|
|
162
|
+
userIdExpr?: string;
|
|
163
|
+
/** Migration version label. Default `"host_org_source"`. */
|
|
164
|
+
version?: string;
|
|
165
|
+
}
|
|
166
|
+
/** Generate the SQL that overrides `<schema>.is_org_member` to read the host's membership table (+ the GRANT the
|
|
167
|
+
* Realtime gate needs to read it as the `authenticated` role during a private subscribe). */
|
|
168
|
+
declare function hostOwnedOrgMembershipSql(opts: HostOrgSourceOpts): string;
|
|
169
|
+
/** The same override packaged as a `Migration` so a host can append it to its migration set AFTER `0001_core`
|
|
170
|
+
* (it `create or replace`s the function the core migration defined). */
|
|
171
|
+
declare function hostOwnedOrgMigration(opts: HostOrgSourceOpts): Migration;
|
|
172
|
+
|
|
173
|
+
/** The READ-ONLY bundle repo (head/getVersion/listSlugs). The writable surface lives in `makeBundleWritableRepo`. */
|
|
174
|
+
declare function makeBundleRepo(ctx: Ctx$1): BundleRepo;
|
|
175
|
+
/**
|
|
176
|
+
* The WRITABLE bundle definition repo (BN2) — the core `BundleWritableRepo` contract backed by Postgres. Extends
|
|
177
|
+
* the read repo with the append-only `publish`/`rollback`/`listVersions` surface, on the SAME shared
|
|
178
|
+
* `appendVersion` primitive as agents (so a bundle gets the same race-safe append-and-flip + audit + the
|
|
179
|
+
* published-row immutability trigger). Every mutation enforces the non-bypassable agent-bar first.
|
|
180
|
+
*/
|
|
181
|
+
declare function makeBundleWritableRepo(ctx: Ctx$1): BundleWritableRepo;
|
|
182
|
+
|
|
99
183
|
/**
|
|
100
184
|
* The Night Owls adapter plugin manifest. `@nightowlsdev/cli` discovers this from the host's installed
|
|
101
185
|
* `@nightowlsdev/*` deps, dynamic-imports it, and acts on it declaratively:
|
|
@@ -181,4 +265,4 @@ interface SupabaseStorage extends StorageAdapter {
|
|
|
181
265
|
}
|
|
182
266
|
declare function createSupabaseStorage(opts: SupabaseStorageOpts): SupabaseStorage;
|
|
183
267
|
|
|
184
|
-
export { type Ctx$1 as Ctx, MIGRATIONS, type Migration, type PostgresFloorOpts, type PublishAgentDef, type SupabaseStorage, type SupabaseStorageOpts, createMastraPgStore, createMastraVectorStore, createPostgresFloor, createSupabaseStorage, listAgentVersions, makeBundleRepo, makeBundleWritableRepo, makeVersionedRepo, nightOwlsPlugin, publishAgentVersion, rollbackAgentVersion };
|
|
268
|
+
export { type Ctx$1 as Ctx, type HostOrgSourceOpts, MIGRATIONS, type Migration, type PostgresFloorOpts, type PublishAgentDef, type SupabaseStorage, type SupabaseStorageOpts, type SupabaseUsageSinkOpts, type UsageRow, createMastraPgStore, createMastraVectorStore, createPostgresFloor, createSupabaseStorage, ensureAgentVersion, hostOwnedOrgMembershipSql, hostOwnedOrgMigration, listAgentVersions, listTenants, makeBundleRepo, makeBundleWritableRepo, makeThreadStore, makeVersionedRepo, nightOwlsPlugin, publishAgentVersion, rollbackAgentVersion, supabaseUsageSink };
|
package/dist/index.js
CHANGED
|
@@ -271,6 +271,18 @@ function makeScratchpadStore(ctx) {
|
|
|
271
271
|
};
|
|
272
272
|
}
|
|
273
273
|
|
|
274
|
+
// src/threads.ts
|
|
275
|
+
function makeThreadStore(ctx) {
|
|
276
|
+
return {
|
|
277
|
+
async ensure({ id, orgId, userId, projectId }) {
|
|
278
|
+
await ctx.pool.query(
|
|
279
|
+
"insert into threads(id, org_id, user_id, project_id) values($1,$2,$3,$4) on conflict (id) do nothing",
|
|
280
|
+
[id, orgId, userId, projectId ?? null]
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
274
286
|
// src/agents.ts
|
|
275
287
|
import { assertActorMayMutateDefinition } from "@nightowlsdev/core";
|
|
276
288
|
|
|
@@ -431,6 +443,41 @@ async function publishAgentVersion(ctx, def) {
|
|
|
431
443
|
client.release();
|
|
432
444
|
}
|
|
433
445
|
}
|
|
446
|
+
async function ensureAgentVersion(ctx, def) {
|
|
447
|
+
const actor = def.actor ?? SEED_ACTOR;
|
|
448
|
+
assertActorMayMutateDefinition(actor);
|
|
449
|
+
const client = await ctx.pool.connect();
|
|
450
|
+
try {
|
|
451
|
+
await client.query("begin");
|
|
452
|
+
await client.query("select pg_advisory_xact_lock(hashtext($1), hashtext($2))", ["ensure_agent", `${def.tenantId}:${def.slug}`]);
|
|
453
|
+
const head = (await client.query(
|
|
454
|
+
`select v.version from agents a join agent_versions v on v.id = a.current_version_id where a.org_id=$1 and a.slug=$2 and a.project_id is null`,
|
|
455
|
+
[def.tenantId, def.slug]
|
|
456
|
+
)).rows[0];
|
|
457
|
+
if (head) {
|
|
458
|
+
await client.query("commit");
|
|
459
|
+
return { version: head.version, created: false };
|
|
460
|
+
}
|
|
461
|
+
const agent = (await client.query(
|
|
462
|
+
`insert into agents(org_id, slug, is_orchestrator) values($1,$2,$3)
|
|
463
|
+
on conflict (org_id, project_id, slug) do update set slug=excluded.slug returning id`,
|
|
464
|
+
[def.tenantId, def.slug, def.role === "orchestrator"]
|
|
465
|
+
)).rows[0];
|
|
466
|
+
const version = await commitVersion(client, agent.id, def.tenantId, def, "publish", auditActor(actor), { slug: def.slug });
|
|
467
|
+
await client.query("commit");
|
|
468
|
+
await notifyInvalidate(client, def.tenantId, def.slug);
|
|
469
|
+
return { version, created: true };
|
|
470
|
+
} catch (e) {
|
|
471
|
+
await client.query("rollback");
|
|
472
|
+
throw e;
|
|
473
|
+
} finally {
|
|
474
|
+
client.release();
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
async function listTenants(ctx) {
|
|
478
|
+
const rows = await many(ctx.pool, "select id::text as id from orgs order by created_at asc");
|
|
479
|
+
return rows.map((r) => r.id);
|
|
480
|
+
}
|
|
434
481
|
async function listAgentVersions(ctx, tenantId, slug) {
|
|
435
482
|
const rows = await many(
|
|
436
483
|
ctx.pool,
|
|
@@ -596,6 +643,78 @@ function createMastraVectorStore(opts) {
|
|
|
596
643
|
return new PgVector({ id: "nightowls-vector", connectionString: opts.dbUrl, schemaName: "nightowls" });
|
|
597
644
|
}
|
|
598
645
|
|
|
646
|
+
// src/usage-sink.ts
|
|
647
|
+
var IDENT = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
648
|
+
var TABLE = /^[a-zA-Z_][a-zA-Z0-9_]*(\.[a-zA-Z_][a-zA-Z0-9_]*)?$/;
|
|
649
|
+
function defaultMap(ev, ctx) {
|
|
650
|
+
const b = ev.data.breakdown;
|
|
651
|
+
return {
|
|
652
|
+
generation_id: ev.data.generationId,
|
|
653
|
+
run_id: ctx.runId,
|
|
654
|
+
org_id: ctx.tenantId,
|
|
655
|
+
agent_slug: ev.data.slug,
|
|
656
|
+
model_id: ev.data.modelId,
|
|
657
|
+
input_tokens: b.inputTokens ?? 0,
|
|
658
|
+
output_tokens: b.outputTokens ?? 0,
|
|
659
|
+
cost_usd: ev.data.cost.usd
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
function supabaseUsageSink(opts) {
|
|
663
|
+
const conflict = opts.conflictColumn ?? "generation_id";
|
|
664
|
+
if (!IDENT.test(conflict)) throw new Error(`supabaseUsageSink: unsafe conflictColumn ${JSON.stringify(conflict)}`);
|
|
665
|
+
if (!TABLE.test(opts.table)) throw new Error(`supabaseUsageSink: unsafe table ${JSON.stringify(opts.table)}`);
|
|
666
|
+
const map = opts.map ?? defaultMap;
|
|
667
|
+
return async (ev, ctx) => {
|
|
668
|
+
if (ev.type !== "swarm.usage") return;
|
|
669
|
+
const row = map(ev, ctx);
|
|
670
|
+
if (!row) return;
|
|
671
|
+
const cols = Object.keys(row);
|
|
672
|
+
for (const c of cols) if (!IDENT.test(c)) throw new Error(`supabaseUsageSink: unsafe column ${JSON.stringify(c)}`);
|
|
673
|
+
const placeholders = cols.map((_c, i) => `$${i + 1}`).join(",");
|
|
674
|
+
const sql = `insert into ${opts.table}(${cols.join(",")}) values(${placeholders}) on conflict (${conflict}) do nothing`;
|
|
675
|
+
await opts.pool.query(sql, cols.map((c) => row[c]));
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// src/host-org-source.ts
|
|
680
|
+
var QUALIFIED = /^[a-zA-Z_][a-zA-Z0-9_]*(\.[a-zA-Z_][a-zA-Z0-9_]*)?$/;
|
|
681
|
+
var PLAIN = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
682
|
+
function assertIdent(v, kind, re) {
|
|
683
|
+
if (!re.test(v)) throw new Error(`hostOwnedOrgMembership: unsafe ${kind} ${JSON.stringify(v)}`);
|
|
684
|
+
}
|
|
685
|
+
function hostOwnedOrgMembershipSql(opts) {
|
|
686
|
+
const schema = opts.schema ?? "nightowls";
|
|
687
|
+
const orgIdColumn = opts.orgIdColumn ?? "organization_id";
|
|
688
|
+
const userIdColumn = opts.userIdColumn ?? "user_id";
|
|
689
|
+
const userIdExpr = opts.userIdExpr ?? "(select auth.uid())";
|
|
690
|
+
assertIdent(schema, "schema", PLAIN);
|
|
691
|
+
assertIdent(opts.membershipTable, "membershipTable", QUALIFIED);
|
|
692
|
+
assertIdent(orgIdColumn, "orgIdColumn", PLAIN);
|
|
693
|
+
assertIdent(userIdColumn, "userIdColumn", PLAIN);
|
|
694
|
+
if (/;|--|\/\*|\$\$/.test(userIdExpr)) throw new Error(`hostOwnedOrgMembership: unsafe userIdExpr ${JSON.stringify(userIdExpr)}`);
|
|
695
|
+
return `-- FR-015 \u2014 override ${schema}.is_org_member to read the host membership table ${opts.membershipTable}.
|
|
696
|
+
create or replace function ${schema}.is_org_member(p_org uuid)
|
|
697
|
+
returns boolean language sql stable set search_path = '' as $$
|
|
698
|
+
select exists (
|
|
699
|
+
select 1 from ${opts.membershipTable} m
|
|
700
|
+
where m.${orgIdColumn} = p_org and m.${userIdColumn} = ${userIdExpr}
|
|
701
|
+
);
|
|
702
|
+
$$;
|
|
703
|
+
-- The Realtime gate calls is_org_member as the AUTHENTICATED role during a private subscribe, so it must be able
|
|
704
|
+
-- to read the membership table. (No-op if you already granted it.)
|
|
705
|
+
grant select on ${opts.membershipTable} to authenticated;
|
|
706
|
+
`;
|
|
707
|
+
}
|
|
708
|
+
function hostOwnedOrgMigration(opts) {
|
|
709
|
+
const version = opts.version ?? "host_org_source";
|
|
710
|
+
assertIdent(version, "version", PLAIN);
|
|
711
|
+
return {
|
|
712
|
+
version,
|
|
713
|
+
name: `host-owned org/membership source (is_org_member \u2192 ${opts.membershipTable})`,
|
|
714
|
+
sql: hostOwnedOrgMembershipSql(opts)
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
|
|
599
718
|
// src/migrations/0001_core.ts
|
|
600
719
|
var M0001_CORE = {
|
|
601
720
|
version: "0001_core",
|
|
@@ -2048,7 +2167,12 @@ function createSupabaseStorage(opts) {
|
|
|
2048
2167
|
runs: makeRunStore(ctx),
|
|
2049
2168
|
events: makeEventStore(ctx),
|
|
2050
2169
|
messages: makeMessageStore(ctx),
|
|
2170
|
+
// FR-009: the engine ensures this thread row at run start, so a host no longer hand-writes raw SQL against
|
|
2171
|
+
// `nightowls.threads`, and `messages.append` cannot throw `unknown thread` through the supported path.
|
|
2172
|
+
threads: makeThreadStore(ctx),
|
|
2051
2173
|
scratchpad: makeScratchpadStore(ctx),
|
|
2174
|
+
// FR-016: enumerate the engine's tenants for idempotent per-tenant crew backfill (pairs with ensureAgentVersion).
|
|
2175
|
+
listTenants: () => listTenants(ctx),
|
|
2052
2176
|
// Record the suspended run in the tenant-scoped followup index so `runs.findSuspended` (the resume
|
|
2053
2177
|
// authz gate) can resolve it. RE-OPEN on conflict (reset `answered_at`): Mastra REUSES the same
|
|
2054
2178
|
// `toolCallId` when an agent asks AGAIN after a resume, so the engine's `followupId = runId:toolCallId`
|
|
@@ -2117,11 +2241,17 @@ export {
|
|
|
2117
2241
|
createMastraVectorStore,
|
|
2118
2242
|
createPostgresFloor,
|
|
2119
2243
|
createSupabaseStorage,
|
|
2244
|
+
ensureAgentVersion,
|
|
2245
|
+
hostOwnedOrgMembershipSql,
|
|
2246
|
+
hostOwnedOrgMigration,
|
|
2120
2247
|
listAgentVersions,
|
|
2248
|
+
listTenants,
|
|
2121
2249
|
makeBundleRepo,
|
|
2122
2250
|
makeBundleWritableRepo,
|
|
2251
|
+
makeThreadStore,
|
|
2123
2252
|
makeVersionedRepo,
|
|
2124
2253
|
nightOwlsPlugin,
|
|
2125
2254
|
publishAgentVersion,
|
|
2126
|
-
rollbackAgentVersion
|
|
2255
|
+
rollbackAgentVersion,
|
|
2256
|
+
supabaseUsageSink
|
|
2127
2257
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nightowlsdev/storage-supabase",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"peerDependencies": {
|
|
34
34
|
"@mastra/core": "^1.38.0",
|
|
35
35
|
"@mastra/pg": "^1.12.0",
|
|
36
|
-
"@nightowlsdev/core": "0.
|
|
36
|
+
"@nightowlsdev/core": "0.5.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@mastra/core": "^1.38.0",
|
|
@@ -46,9 +46,9 @@
|
|
|
46
46
|
"typescript": "6.0.3",
|
|
47
47
|
"vitest": "^3.2.0",
|
|
48
48
|
"zod": "^4.0.0",
|
|
49
|
-
"@nightowlsdev/
|
|
50
|
-
"@nightowlsdev/
|
|
51
|
-
"@nightowlsdev/
|
|
49
|
+
"@nightowlsdev/core": "0.5.0",
|
|
50
|
+
"@nightowlsdev/eslint-config": "0.0.0",
|
|
51
|
+
"@nightowlsdev/tsconfig": "0.0.0"
|
|
52
52
|
},
|
|
53
53
|
"scripts": {
|
|
54
54
|
"build": "tsup",
|