@agentsean/hosted 1.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/LICENSE +661 -0
- package/dist/billing.d.ts +40 -0
- package/dist/billing.d.ts.map +1 -0
- package/dist/billing.js +142 -0
- package/dist/billing.js.map +1 -0
- package/dist/connector.d.ts +22 -0
- package/dist/connector.d.ts.map +1 -0
- package/dist/connector.js +65 -0
- package/dist/connector.js.map +1 -0
- package/dist/cost.d.ts +15 -0
- package/dist/cost.d.ts.map +1 -0
- package/dist/cost.js +38 -0
- package/dist/cost.js.map +1 -0
- package/dist/engine.d.ts +47 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +62 -0
- package/dist/engine.js.map +1 -0
- package/dist/entitlements.d.ts +8 -0
- package/dist/entitlements.d.ts.map +1 -0
- package/dist/entitlements.js +33 -0
- package/dist/entitlements.js.map +1 -0
- package/dist/envelope.d.ts +9 -0
- package/dist/envelope.d.ts.map +1 -0
- package/dist/envelope.js +72 -0
- package/dist/envelope.js.map +1 -0
- package/dist/erasure.d.ts +27 -0
- package/dist/erasure.d.ts.map +1 -0
- package/dist/erasure.js +54 -0
- package/dist/erasure.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/neighbour.d.ts +13 -0
- package/dist/neighbour.d.ts.map +1 -0
- package/dist/neighbour.js +76 -0
- package/dist/neighbour.js.map +1 -0
- package/dist/oauth.d.ts +5 -0
- package/dist/oauth.d.ts.map +1 -0
- package/dist/oauth.js +35 -0
- package/dist/oauth.js.map +1 -0
- package/dist/plans.d.ts +29 -0
- package/dist/plans.d.ts.map +1 -0
- package/dist/plans.js +110 -0
- package/dist/plans.js.map +1 -0
- package/dist/tenants.d.ts +48 -0
- package/dist/tenants.d.ts.map +1 -0
- package/dist/tenants.js +121 -0
- package/dist/tenants.js.map +1 -0
- package/dist/tracing.d.ts +11 -0
- package/dist/tracing.d.ts.map +1 -0
- package/dist/tracing.js +9 -0
- package/dist/tracing.js.map +1 -0
- package/package.json +52 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { type SqliteDatabase } from "@agentsean/db";
|
|
2
|
+
import { type PlanId } from "./plans.js";
|
|
3
|
+
export type StripeEvent = {
|
|
4
|
+
id: string;
|
|
5
|
+
type: string;
|
|
6
|
+
data: {
|
|
7
|
+
object: Record<string, unknown>;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
export type StripeLike = {
|
|
11
|
+
createCheckoutSession: (opts: {
|
|
12
|
+
tenantId: string;
|
|
13
|
+
plan: PlanId;
|
|
14
|
+
email: string;
|
|
15
|
+
interval: "month" | "year";
|
|
16
|
+
}) => Promise<{
|
|
17
|
+
id: string;
|
|
18
|
+
url: string;
|
|
19
|
+
}>;
|
|
20
|
+
reportMeteredUsage?: ((opts: {
|
|
21
|
+
tenantId: string;
|
|
22
|
+
quantity: number;
|
|
23
|
+
period: string;
|
|
24
|
+
}) => Promise<void>) | undefined;
|
|
25
|
+
};
|
|
26
|
+
export declare function fakeStripe(baseUrl?: string): StripeLike;
|
|
27
|
+
export declare function applyStripeEvent(db: SqliteDatabase, event: StripeEvent, now?: Date): {
|
|
28
|
+
duplicate: boolean;
|
|
29
|
+
tenantId: string | null;
|
|
30
|
+
};
|
|
31
|
+
export declare function activateSubscription(db: SqliteDatabase, opts: {
|
|
32
|
+
tenantId: string;
|
|
33
|
+
plan: PlanId;
|
|
34
|
+
customerId: string | null;
|
|
35
|
+
subscriptionId: string | null;
|
|
36
|
+
now?: Date | undefined;
|
|
37
|
+
}): void;
|
|
38
|
+
export declare function reportArticleUsage(db: SqliteDatabase, tenantId: string, quantity?: number, now?: Date, stripe?: StripeLike | undefined): void;
|
|
39
|
+
export declare function articlesThisMonth(db: SqliteDatabase, tenantId: string, now?: Date): number;
|
|
40
|
+
//# sourceMappingURL=billing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"billing.d.ts","sourceRoot":"","sources":["../src/billing.ts"],"names":[],"mappings":"AAEA,OAAO,EAKL,KAAK,cAAc,EACpB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAY,KAAK,MAAM,EAAE,MAAM,YAAY,CAAC;AAEnD,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC;CAC3C,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,qBAAqB,EAAE,CAAC,IAAI,EAAE;QAC5B,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,OAAO,GAAG,MAAM,CAAC;KAC5B,KAAK,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC3C,kBAAkB,CAAC,EACf,CAAC,CAAC,IAAI,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,GACjF,SAAS,CAAC;CACf,CAAC;AAEF,wBAAgB,UAAU,CAAC,OAAO,SAAiC,GAAG,UAAU,CAY/E;AAOD,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,cAAc,EAClB,KAAK,EAAE,WAAW,EAClB,GAAG,OAAa,GACf;IAAE,SAAS,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAmEjD;AAWD,wBAAgB,oBAAoB,CAClC,EAAE,EAAE,cAAc,EAClB,IAAI,EAAE;IACJ,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,GAAG,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;CACxB,GACA,IAAI,CA0BN;AAED,wBAAgB,kBAAkB,CAChC,EAAE,EAAE,cAAc,EAClB,QAAQ,EAAE,MAAM,EAChB,QAAQ,SAAI,EACZ,GAAG,OAAa,EAChB,MAAM,CAAC,EAAE,UAAU,GAAG,SAAS,GAC9B,IAAI,CAaN;AAED,wBAAgB,iBAAiB,CAC/B,EAAE,EAAE,cAAc,EAClB,QAAQ,EAAE,MAAM,EAChB,GAAG,OAAa,GACf,MAAM,CASR"}
|
package/dist/billing.js
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { eq } from "drizzle-orm";
|
|
3
|
+
import { meteredUsage, stripeEvents, subscriptions, tenants, } from "@agentsean/db";
|
|
4
|
+
import { isPlanId } from "./plans.js";
|
|
5
|
+
export function fakeStripe(baseUrl = "https://checkout.stripe.test") {
|
|
6
|
+
return {
|
|
7
|
+
async createCheckoutSession(opts) {
|
|
8
|
+
return {
|
|
9
|
+
id: `cs_test_${opts.tenantId}`,
|
|
10
|
+
url: `${baseUrl}/c/pay/${opts.plan}?tenant=${opts.tenantId}`,
|
|
11
|
+
};
|
|
12
|
+
},
|
|
13
|
+
async reportMeteredUsage() {
|
|
14
|
+
return;
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function str(obj, key) {
|
|
19
|
+
const v = obj[key];
|
|
20
|
+
return typeof v === "string" ? v : undefined;
|
|
21
|
+
}
|
|
22
|
+
export function applyStripeEvent(db, event, now = new Date()) {
|
|
23
|
+
const existing = db
|
|
24
|
+
.select()
|
|
25
|
+
.from(stripeEvents)
|
|
26
|
+
.where(eq(stripeEvents.id, event.id))
|
|
27
|
+
.get();
|
|
28
|
+
if (existing)
|
|
29
|
+
return { duplicate: true, tenantId: null };
|
|
30
|
+
db.insert(stripeEvents)
|
|
31
|
+
.values({
|
|
32
|
+
id: event.id,
|
|
33
|
+
type: event.type,
|
|
34
|
+
payload: JSON.stringify(event.data.object),
|
|
35
|
+
processedAt: now.toISOString(),
|
|
36
|
+
})
|
|
37
|
+
.run();
|
|
38
|
+
const obj = event.data.object;
|
|
39
|
+
const tenantId = str(obj, "client_reference_id") ?? str(obj, "tenantId") ?? null;
|
|
40
|
+
const planRaw = str(obj, "plan") ?? metadataPlan(obj);
|
|
41
|
+
const customer = str(obj, "customer");
|
|
42
|
+
const subscription = str(obj, "subscription") ?? str(obj, "id");
|
|
43
|
+
if (event.type === "checkout.session.completed" &&
|
|
44
|
+
tenantId &&
|
|
45
|
+
planRaw &&
|
|
46
|
+
isPlanId(planRaw)) {
|
|
47
|
+
activateSubscription(db, {
|
|
48
|
+
tenantId,
|
|
49
|
+
plan: planRaw,
|
|
50
|
+
customerId: customer ?? null,
|
|
51
|
+
subscriptionId: subscription ?? null,
|
|
52
|
+
now,
|
|
53
|
+
});
|
|
54
|
+
return { duplicate: false, tenantId };
|
|
55
|
+
}
|
|
56
|
+
if (event.type === "customer.subscription.updated" &&
|
|
57
|
+
tenantId &&
|
|
58
|
+
planRaw &&
|
|
59
|
+
isPlanId(planRaw)) {
|
|
60
|
+
activateSubscription(db, {
|
|
61
|
+
tenantId,
|
|
62
|
+
plan: planRaw,
|
|
63
|
+
customerId: customer ?? null,
|
|
64
|
+
subscriptionId: subscription ?? null,
|
|
65
|
+
now,
|
|
66
|
+
});
|
|
67
|
+
return { duplicate: false, tenantId };
|
|
68
|
+
}
|
|
69
|
+
if (event.type === "customer.subscription.deleted" && tenantId) {
|
|
70
|
+
db.update(tenants)
|
|
71
|
+
.set({ status: "canceled", updatedAt: now.toISOString() })
|
|
72
|
+
.where(eq(tenants.id, tenantId))
|
|
73
|
+
.run();
|
|
74
|
+
return { duplicate: false, tenantId };
|
|
75
|
+
}
|
|
76
|
+
if (event.type === "invoice.payment_failed" && tenantId) {
|
|
77
|
+
db.update(tenants)
|
|
78
|
+
.set({ status: "past_due", updatedAt: now.toISOString() })
|
|
79
|
+
.where(eq(tenants.id, tenantId))
|
|
80
|
+
.run();
|
|
81
|
+
return { duplicate: false, tenantId };
|
|
82
|
+
}
|
|
83
|
+
return { duplicate: false, tenantId };
|
|
84
|
+
}
|
|
85
|
+
function metadataPlan(obj) {
|
|
86
|
+
const meta = obj["metadata"];
|
|
87
|
+
if (meta && typeof meta === "object" && !Array.isArray(meta) && "plan" in meta) {
|
|
88
|
+
const p = meta.plan;
|
|
89
|
+
return typeof p === "string" ? p : undefined;
|
|
90
|
+
}
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
export function activateSubscription(db, opts) {
|
|
94
|
+
const now = (opts.now ?? new Date()).toISOString();
|
|
95
|
+
db.update(tenants)
|
|
96
|
+
.set({
|
|
97
|
+
plan: opts.plan,
|
|
98
|
+
status: "active",
|
|
99
|
+
stripeCustomerId: opts.customerId,
|
|
100
|
+
stripeSubscriptionId: opts.subscriptionId,
|
|
101
|
+
updatedAt: now,
|
|
102
|
+
})
|
|
103
|
+
.where(eq(tenants.id, opts.tenantId))
|
|
104
|
+
.run();
|
|
105
|
+
db.insert(subscriptions)
|
|
106
|
+
.values({
|
|
107
|
+
id: randomUUID(),
|
|
108
|
+
tenantId: opts.tenantId,
|
|
109
|
+
plan: opts.plan,
|
|
110
|
+
interval: "month",
|
|
111
|
+
status: "active",
|
|
112
|
+
currentPeriodEnd: new Date((opts.now ?? new Date()).getTime() + 30 * 86400000).toISOString(),
|
|
113
|
+
createdAt: now,
|
|
114
|
+
updatedAt: now,
|
|
115
|
+
})
|
|
116
|
+
.run();
|
|
117
|
+
}
|
|
118
|
+
export function reportArticleUsage(db, tenantId, quantity = 1, now = new Date(), stripe) {
|
|
119
|
+
const period = now.toISOString().slice(0, 7);
|
|
120
|
+
db.insert(meteredUsage)
|
|
121
|
+
.values({
|
|
122
|
+
id: randomUUID(),
|
|
123
|
+
tenantId,
|
|
124
|
+
kind: "article",
|
|
125
|
+
quantity,
|
|
126
|
+
period,
|
|
127
|
+
createdAt: now.toISOString(),
|
|
128
|
+
})
|
|
129
|
+
.run();
|
|
130
|
+
void stripe?.reportMeteredUsage?.({ tenantId, quantity, period });
|
|
131
|
+
}
|
|
132
|
+
export function articlesThisMonth(db, tenantId, now = new Date()) {
|
|
133
|
+
const period = now.toISOString().slice(0, 7);
|
|
134
|
+
return db
|
|
135
|
+
.select()
|
|
136
|
+
.from(meteredUsage)
|
|
137
|
+
.where(eq(meteredUsage.tenantId, tenantId))
|
|
138
|
+
.all()
|
|
139
|
+
.filter((r) => r.period === period && r.kind === "article")
|
|
140
|
+
.reduce((s, r) => s + r.quantity, 0);
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=billing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"billing.js","sourceRoot":"","sources":["../src/billing.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type SqliteDatabase } from "@agentsean/db";
|
|
2
|
+
/** CMS write kinds the hosted control plane must never store. */
|
|
3
|
+
export declare const CMS_WRITE_KINDS: readonly ["wordpress", "shopify", "webflow", "ghost", "wix", "bigcommerce", "contentful", "sanity", "strapi", "payload", "cloudflare", "git"];
|
|
4
|
+
export declare class HostedCredentialError extends Error {
|
|
5
|
+
readonly name = "HostedCredentialError";
|
|
6
|
+
}
|
|
7
|
+
export declare function isCmsWriteKind(kind: string): boolean;
|
|
8
|
+
export declare function refuseHostedCmsCredential(kind: string): never;
|
|
9
|
+
export declare function createConnectorPairing(db: SqliteDatabase, tenantId: string, siteId: string | null, now?: Date): {
|
|
10
|
+
id: string;
|
|
11
|
+
token: string;
|
|
12
|
+
};
|
|
13
|
+
export declare function activateConnector(db: SqliteDatabase, token: string): boolean;
|
|
14
|
+
export declare function listConnectors(db: SqliteDatabase, tenantId: string): {
|
|
15
|
+
id: string;
|
|
16
|
+
tenantId: string;
|
|
17
|
+
siteId: string | null;
|
|
18
|
+
tokenHash: string;
|
|
19
|
+
status: string;
|
|
20
|
+
createdAt: string;
|
|
21
|
+
}[];
|
|
22
|
+
//# sourceMappingURL=connector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connector.d.ts","sourceRoot":"","sources":["../src/connector.ts"],"names":[],"mappings":"AAEA,OAAO,EAAqB,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAEvE,iEAAiE;AACjE,eAAO,MAAM,eAAe,+IAalB,CAAC;AAEX,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,SAAkB,IAAI,2BAA2B;CAClD;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,CAI7D;AAED,wBAAgB,sBAAsB,CACpC,EAAE,EAAE,cAAc,EAClB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GAAG,IAAI,EACrB,GAAG,OAAa,GACf;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAc/B;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAa5E;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM;;;;;;;IAMlE"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
2
|
+
import { eq } from "drizzle-orm";
|
|
3
|
+
import { connectorPairings } from "@agentsean/db";
|
|
4
|
+
/** CMS write kinds the hosted control plane must never store. */
|
|
5
|
+
export const CMS_WRITE_KINDS = [
|
|
6
|
+
"wordpress",
|
|
7
|
+
"shopify",
|
|
8
|
+
"webflow",
|
|
9
|
+
"ghost",
|
|
10
|
+
"wix",
|
|
11
|
+
"bigcommerce",
|
|
12
|
+
"contentful",
|
|
13
|
+
"sanity",
|
|
14
|
+
"strapi",
|
|
15
|
+
"payload",
|
|
16
|
+
"cloudflare",
|
|
17
|
+
"git",
|
|
18
|
+
];
|
|
19
|
+
export class HostedCredentialError extends Error {
|
|
20
|
+
name = "HostedCredentialError";
|
|
21
|
+
}
|
|
22
|
+
export function isCmsWriteKind(kind) {
|
|
23
|
+
return CMS_WRITE_KINDS.includes(kind);
|
|
24
|
+
}
|
|
25
|
+
export function refuseHostedCmsCredential(kind) {
|
|
26
|
+
throw new HostedCredentialError(`Hosted Sean will not store ${kind} write credentials. Pair a customer-side connector; the customer's own daemon holds the key.`);
|
|
27
|
+
}
|
|
28
|
+
export function createConnectorPairing(db, tenantId, siteId, now = new Date()) {
|
|
29
|
+
const token = randomBytes(24).toString("base64url");
|
|
30
|
+
const id = randomUUID();
|
|
31
|
+
db.insert(connectorPairings)
|
|
32
|
+
.values({
|
|
33
|
+
id,
|
|
34
|
+
tenantId,
|
|
35
|
+
siteId,
|
|
36
|
+
tokenHash: createHash("sha256").update(token).digest("hex"),
|
|
37
|
+
status: "pending",
|
|
38
|
+
createdAt: now.toISOString(),
|
|
39
|
+
})
|
|
40
|
+
.run();
|
|
41
|
+
return { id, token };
|
|
42
|
+
}
|
|
43
|
+
export function activateConnector(db, token) {
|
|
44
|
+
const hash = createHash("sha256").update(token).digest("hex");
|
|
45
|
+
const row = db
|
|
46
|
+
.select()
|
|
47
|
+
.from(connectorPairings)
|
|
48
|
+
.all()
|
|
49
|
+
.find((r) => r.tokenHash === hash);
|
|
50
|
+
if (!row)
|
|
51
|
+
return false;
|
|
52
|
+
db.update(connectorPairings)
|
|
53
|
+
.set({ status: "active" })
|
|
54
|
+
.where(eq(connectorPairings.id, row.id))
|
|
55
|
+
.run();
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
export function listConnectors(db, tenantId) {
|
|
59
|
+
return db
|
|
60
|
+
.select()
|
|
61
|
+
.from(connectorPairings)
|
|
62
|
+
.where(eq(connectorPairings.tenantId, tenantId))
|
|
63
|
+
.all();
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=connector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connector.js","sourceRoot":"","sources":["../src/connector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,iBAAiB,EAAuB,MAAM,eAAe,CAAC;AAEvE,iEAAiE;AACjE,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,WAAW;IACX,SAAS;IACT,SAAS;IACT,OAAO;IACP,KAAK;IACL,aAAa;IACb,YAAY;IACZ,QAAQ;IACR,QAAQ;IACR,SAAS;IACT,YAAY;IACZ,KAAK;CACG,CAAC;AAEX,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IAC5B,IAAI,GAAG,uBAAuB,CAAC;CAClD;AAED,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAQ,eAAqC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,IAAY;IACpD,MAAM,IAAI,qBAAqB,CAC7B,8BAA8B,IAAI,8FAA8F,CACjI,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,EAAkB,EAClB,QAAgB,EAChB,MAAqB,EACrB,GAAG,GAAG,IAAI,IAAI,EAAE;IAEhB,MAAM,KAAK,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IACpD,MAAM,EAAE,GAAG,UAAU,EAAE,CAAC;IACxB,EAAE,CAAC,MAAM,CAAC,iBAAiB,CAAC;SACzB,MAAM,CAAC;QACN,EAAE;QACF,QAAQ;QACR,MAAM;QACN,SAAS,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;QAC3D,MAAM,EAAE,SAAS;QACjB,SAAS,EAAE,GAAG,CAAC,WAAW,EAAE;KAC7B,CAAC;SACD,GAAG,EAAE,CAAC;IACT,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,EAAkB,EAAE,KAAa;IACjE,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC9D,MAAM,GAAG,GAAG,EAAE;SACX,MAAM,EAAE;SACR,IAAI,CAAC,iBAAiB,CAAC;SACvB,GAAG,EAAE;SACL,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,IAAI,CAAC,CAAC;IACrC,IAAI,CAAC,GAAG;QAAE,OAAO,KAAK,CAAC;IACvB,EAAE,CAAC,MAAM,CAAC,iBAAiB,CAAC;SACzB,GAAG,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;SACzB,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAAC,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;SACvC,GAAG,EAAE,CAAC;IACT,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,EAAkB,EAAE,QAAgB;IACjE,OAAO,EAAE;SACN,MAAM,EAAE;SACR,IAAI,CAAC,iBAAiB,CAAC;SACvB,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;SAC/C,GAAG,EAAE,CAAC;AACX,CAAC"}
|
package/dist/cost.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type SqliteDatabase } from "@agentsean/db";
|
|
2
|
+
export type TenantCost = {
|
|
3
|
+
tenantId: string;
|
|
4
|
+
plan: string;
|
|
5
|
+
sites: number;
|
|
6
|
+
siteCap: number;
|
|
7
|
+
ledgerUsd: number;
|
|
8
|
+
cogsUsd: number;
|
|
9
|
+
revenueUsd: number;
|
|
10
|
+
margin: number;
|
|
11
|
+
articles: number;
|
|
12
|
+
byok: boolean;
|
|
13
|
+
};
|
|
14
|
+
export declare function tenantCostVisibility(db: SqliteDatabase, tenantId: string, now?: Date): TenantCost;
|
|
15
|
+
//# sourceMappingURL=cost.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cost.d.ts","sourceRoot":"","sources":["../src/cost.ts"],"names":[],"mappings":"AACA,OAAO,EAA2B,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAK7E,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,OAAO,CAAC;CACf,CAAC;AAEF,wBAAgB,oBAAoB,CAClC,EAAE,EAAE,cAAc,EAClB,QAAQ,EAAE,MAAM,EAChB,GAAG,OAAa,GACf,UAAU,CA6BZ"}
|
package/dist/cost.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { eq } from "drizzle-orm";
|
|
2
|
+
import { costLedger, tenantSites } from "@agentsean/db";
|
|
3
|
+
import { NON_LLM_COGS_USD, planOf } from "./plans.js";
|
|
4
|
+
import { getTenant, tenantSiteCount } from "./tenants.js";
|
|
5
|
+
import { articlesThisMonth } from "./billing.js";
|
|
6
|
+
export function tenantCostVisibility(db, tenantId, now = new Date()) {
|
|
7
|
+
const tenant = getTenant(db, tenantId);
|
|
8
|
+
if (!tenant)
|
|
9
|
+
throw new Error("unknown_tenant");
|
|
10
|
+
const plan = planOf(tenant.plan);
|
|
11
|
+
const links = db
|
|
12
|
+
.select()
|
|
13
|
+
.from(tenantSites)
|
|
14
|
+
.where(eq(tenantSites.tenantId, tenantId))
|
|
15
|
+
.all();
|
|
16
|
+
const siteIds = new Set(links.map((l) => l.siteId));
|
|
17
|
+
let ledgerUsd = 0;
|
|
18
|
+
for (const row of db.select().from(costLedger).all()) {
|
|
19
|
+
if (row.siteId && siteIds.has(row.siteId))
|
|
20
|
+
ledgerUsd += row.costUsd;
|
|
21
|
+
}
|
|
22
|
+
const sites = tenantSiteCount(db, tenantId);
|
|
23
|
+
const revenueUsd = plan.priceUsdMonth;
|
|
24
|
+
const cogsUsd = NON_LLM_COGS_USD + ledgerUsd;
|
|
25
|
+
return {
|
|
26
|
+
tenantId,
|
|
27
|
+
plan: plan.id,
|
|
28
|
+
sites,
|
|
29
|
+
siteCap: plan.sites,
|
|
30
|
+
ledgerUsd,
|
|
31
|
+
cogsUsd,
|
|
32
|
+
revenueUsd,
|
|
33
|
+
margin: revenueUsd <= 0 ? 1 : (revenueUsd - cogsUsd) / revenueUsd,
|
|
34
|
+
articles: articlesThisMonth(db, tenantId, now),
|
|
35
|
+
byok: Boolean(tenant.byok),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=cost.js.map
|
package/dist/cost.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cost.js","sourceRoot":"","sources":["../src/cost.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,WAAW,EAAuB,MAAM,eAAe,CAAC;AAC7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAejD,MAAM,UAAU,oBAAoB,CAClC,EAAkB,EAClB,QAAgB,EAChB,GAAG,GAAG,IAAI,IAAI,EAAE;IAEhB,MAAM,MAAM,GAAG,SAAS,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;IACvC,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC;IAC/C,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACjC,MAAM,KAAK,GAAG,EAAE;SACb,MAAM,EAAE;SACR,IAAI,CAAC,WAAW,CAAC;SACjB,KAAK,CAAC,EAAE,CAAC,WAAW,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;SACzC,GAAG,EAAE,CAAC;IACT,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;IACpD,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,KAAK,MAAM,GAAG,IAAI,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC;QACrD,IAAI,GAAG,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC;YAAE,SAAS,IAAI,GAAG,CAAC,OAAO,CAAC;IACtE,CAAC;IACD,MAAM,KAAK,GAAG,eAAe,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;IAC5C,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,CAAC;IACtC,MAAM,OAAO,GAAG,gBAAgB,GAAG,SAAS,CAAC;IAC7C,OAAO;QACL,QAAQ;QACR,IAAI,EAAE,IAAI,CAAC,EAAE;QACb,KAAK;QACL,OAAO,EAAE,IAAI,CAAC,KAAK;QACnB,SAAS;QACT,OAAO;QACP,UAAU;QACV,MAAM,EAAE,UAAU,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,GAAG,OAAO,CAAC,GAAG,UAAU;QACjE,QAAQ,EAAE,iBAAiB,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,CAAC;QAC9C,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC;KAC3B,CAAC;AACJ,CAAC"}
|
package/dist/engine.d.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { type PlanId } from "./plans.js";
|
|
2
|
+
import { addTenantSite } from "./tenants.js";
|
|
3
|
+
import { type StripeLike } from "./billing.js";
|
|
4
|
+
import type { SqliteDatabase } from "@agentsean/db";
|
|
5
|
+
export type SignupResult = {
|
|
6
|
+
tenantId: string;
|
|
7
|
+
plan: PlanId;
|
|
8
|
+
checkoutUrl: string;
|
|
9
|
+
};
|
|
10
|
+
export declare function signupTenant(db: SqliteDatabase, opts: {
|
|
11
|
+
name: string;
|
|
12
|
+
email: string;
|
|
13
|
+
plan: PlanId;
|
|
14
|
+
stripe?: StripeLike | undefined;
|
|
15
|
+
now?: Date | undefined;
|
|
16
|
+
}): Promise<SignupResult>;
|
|
17
|
+
/** Simulate (or apply) the paid webhook so the tenant can add sites. */
|
|
18
|
+
export declare function completeCheckout(db: SqliteDatabase, opts: {
|
|
19
|
+
tenantId: string;
|
|
20
|
+
plan: PlanId;
|
|
21
|
+
eventId?: string | undefined;
|
|
22
|
+
now?: Date | undefined;
|
|
23
|
+
}): void;
|
|
24
|
+
export declare function hostedStatus(db: SqliteDatabase, tenantId: string): {
|
|
25
|
+
tenant: {
|
|
26
|
+
id: string;
|
|
27
|
+
name: string;
|
|
28
|
+
email: string;
|
|
29
|
+
plan: string;
|
|
30
|
+
status: string;
|
|
31
|
+
stripeCustomerId: string | null;
|
|
32
|
+
stripeSubscriptionId: string | null;
|
|
33
|
+
byok: number;
|
|
34
|
+
createdAt: string;
|
|
35
|
+
updatedAt: string;
|
|
36
|
+
};
|
|
37
|
+
plan: import("./plans.js").Plan;
|
|
38
|
+
sites: {
|
|
39
|
+
id: string;
|
|
40
|
+
origin: string;
|
|
41
|
+
name: string | null;
|
|
42
|
+
}[];
|
|
43
|
+
cost: import("./cost.js").TenantCost;
|
|
44
|
+
googleRedirect: string | null;
|
|
45
|
+
} | null;
|
|
46
|
+
export { addTenantSite };
|
|
47
|
+
//# sourceMappingURL=engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,MAAM,EAAE,MAAM,YAAY,CAAC;AAC1D,OAAO,EAAE,aAAa,EAA4C,MAAM,cAAc,CAAC;AACvF,OAAO,EAIL,KAAK,UAAU,EAChB,MAAM,cAAc,CAAC;AAGtB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,wBAAsB,YAAY,CAChC,EAAE,EAAE,cAAc,EAClB,IAAI,EAAE;IACJ,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IAChC,GAAG,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;CACxB,GACA,OAAO,CAAC,YAAY,CAAC,CAwBvB;AAED,wEAAwE;AACxE,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,cAAc,EAClB,IAAI,EAAE;IACJ,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,GAAG,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;CACxB,GACA,IAAI,CAeN;AAED,wBAAgB,YAAY,CAAC,EAAE,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;SAWhE;AAED,OAAO,EAAE,aAAa,EAAE,CAAC"}
|
package/dist/engine.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { isPlanId, PLANS } from "./plans.js";
|
|
2
|
+
import { addTenantSite, createTenant, getTenant, listTenantSites } from "./tenants.js";
|
|
3
|
+
import { applyStripeEvent, fakeStripe, } from "./billing.js";
|
|
4
|
+
import { tenantCostVisibility } from "./cost.js";
|
|
5
|
+
import { hostedOauthRedirectUri } from "./oauth.js";
|
|
6
|
+
export async function signupTenant(db, opts) {
|
|
7
|
+
if (opts.plan === "self_host") {
|
|
8
|
+
const tenant = createTenant(db, {
|
|
9
|
+
name: opts.name,
|
|
10
|
+
email: opts.email,
|
|
11
|
+
plan: "self_host",
|
|
12
|
+
now: opts.now,
|
|
13
|
+
});
|
|
14
|
+
return { tenantId: tenant.id, plan: "self_host", checkoutUrl: "" };
|
|
15
|
+
}
|
|
16
|
+
const tenant = createTenant(db, {
|
|
17
|
+
name: opts.name,
|
|
18
|
+
email: opts.email,
|
|
19
|
+
plan: opts.plan,
|
|
20
|
+
now: opts.now,
|
|
21
|
+
});
|
|
22
|
+
const stripe = opts.stripe ?? fakeStripe();
|
|
23
|
+
const session = await stripe.createCheckoutSession({
|
|
24
|
+
tenantId: tenant.id,
|
|
25
|
+
plan: opts.plan,
|
|
26
|
+
email: opts.email,
|
|
27
|
+
interval: "month",
|
|
28
|
+
});
|
|
29
|
+
return { tenantId: tenant.id, plan: opts.plan, checkoutUrl: session.url };
|
|
30
|
+
}
|
|
31
|
+
/** Simulate (or apply) the paid webhook so the tenant can add sites. */
|
|
32
|
+
export function completeCheckout(db, opts) {
|
|
33
|
+
const event = {
|
|
34
|
+
id: opts.eventId ?? `evt_${opts.tenantId}`,
|
|
35
|
+
type: "checkout.session.completed",
|
|
36
|
+
data: {
|
|
37
|
+
object: {
|
|
38
|
+
client_reference_id: opts.tenantId,
|
|
39
|
+
tenantId: opts.tenantId,
|
|
40
|
+
plan: opts.plan,
|
|
41
|
+
customer: `cus_${opts.tenantId}`,
|
|
42
|
+
subscription: `sub_${opts.tenantId}`,
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
applyStripeEvent(db, event, opts.now ?? new Date());
|
|
47
|
+
}
|
|
48
|
+
export function hostedStatus(db, tenantId) {
|
|
49
|
+
const tenant = getTenant(db, tenantId);
|
|
50
|
+
if (!tenant)
|
|
51
|
+
return null;
|
|
52
|
+
const plan = PLANS[isPlanId(tenant.plan) ? tenant.plan : "cloud_starter"];
|
|
53
|
+
return {
|
|
54
|
+
tenant,
|
|
55
|
+
plan,
|
|
56
|
+
sites: listTenantSites(db, tenantId),
|
|
57
|
+
cost: tenantCostVisibility(db, tenantId),
|
|
58
|
+
googleRedirect: hostedOauthRedirectUri(),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export { addTenantSite };
|
|
62
|
+
//# sourceMappingURL=engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type Plan, type PlanId } from "./plans.js";
|
|
2
|
+
export type EntitlementFeature = "aiVisibility" | "hostedOauth" | "whiteLabel" | "apiAccess" | "priorityQueue" | "clientSeats" | "bulkOps" | "dailyRanks";
|
|
3
|
+
export declare class EntitlementError extends Error {
|
|
4
|
+
readonly name = "EntitlementError";
|
|
5
|
+
}
|
|
6
|
+
export declare function hasFeature(plan: Plan, feature: EntitlementFeature): boolean;
|
|
7
|
+
export declare function assertEntitlement(planId: PlanId | string, feature: EntitlementFeature): void;
|
|
8
|
+
//# sourceMappingURL=entitlements.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entitlements.d.ts","sourceRoot":"","sources":["../src/entitlements.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,KAAK,IAAI,EAAE,KAAK,MAAM,EAAE,MAAM,YAAY,CAAC;AAE1E,MAAM,MAAM,kBAAkB,GAC1B,cAAc,GACd,aAAa,GACb,YAAY,GACZ,WAAW,GACX,eAAe,GACf,aAAa,GACb,SAAS,GACT,YAAY,CAAC;AAEjB,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,SAAkB,IAAI,sBAAsB;CAC7C;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAmB3E;AAED,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,GAAG,MAAM,EACvB,OAAO,EAAE,kBAAkB,GAC1B,IAAI,CAKN"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { isHostedMode, planOf } from "./plans.js";
|
|
2
|
+
export class EntitlementError extends Error {
|
|
3
|
+
name = "EntitlementError";
|
|
4
|
+
}
|
|
5
|
+
export function hasFeature(plan, feature) {
|
|
6
|
+
switch (feature) {
|
|
7
|
+
case "aiVisibility":
|
|
8
|
+
return plan.aiVisibility;
|
|
9
|
+
case "hostedOauth":
|
|
10
|
+
return plan.hostedOauth;
|
|
11
|
+
case "whiteLabel":
|
|
12
|
+
return plan.whiteLabel;
|
|
13
|
+
case "apiAccess":
|
|
14
|
+
return plan.apiAccess;
|
|
15
|
+
case "priorityQueue":
|
|
16
|
+
return plan.priorityQueue;
|
|
17
|
+
case "clientSeats":
|
|
18
|
+
return plan.clientSeats;
|
|
19
|
+
case "bulkOps":
|
|
20
|
+
return plan.bulkOps;
|
|
21
|
+
case "dailyRanks":
|
|
22
|
+
return plan.ranks === "daily";
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export function assertEntitlement(planId, feature) {
|
|
26
|
+
if (!isHostedMode())
|
|
27
|
+
return;
|
|
28
|
+
const plan = planOf(planId);
|
|
29
|
+
if (hasFeature(plan, feature))
|
|
30
|
+
return;
|
|
31
|
+
throw new EntitlementError(`${plan.name} does not include ${feature}.`);
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=entitlements.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entitlements.js","sourceRoot":"","sources":["../src/entitlements.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,EAA0B,MAAM,YAAY,CAAC;AAY1E,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IACvB,IAAI,GAAG,kBAAkB,CAAC;CAC7C;AAED,MAAM,UAAU,UAAU,CAAC,IAAU,EAAE,OAA2B;IAChE,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,cAAc;YACjB,OAAO,IAAI,CAAC,YAAY,CAAC;QAC3B,KAAK,aAAa;YAChB,OAAO,IAAI,CAAC,WAAW,CAAC;QAC1B,KAAK,YAAY;YACf,OAAO,IAAI,CAAC,UAAU,CAAC;QACzB,KAAK,WAAW;YACd,OAAO,IAAI,CAAC,SAAS,CAAC;QACxB,KAAK,eAAe;YAClB,OAAO,IAAI,CAAC,aAAa,CAAC;QAC5B,KAAK,aAAa;YAChB,OAAO,IAAI,CAAC,WAAW,CAAC;QAC1B,KAAK,SAAS;YACZ,OAAO,IAAI,CAAC,OAAO,CAAC;QACtB,KAAK,YAAY;YACf,OAAO,IAAI,CAAC,KAAK,KAAK,OAAO,CAAC;IAClC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,MAAuB,EACvB,OAA2B;IAE3B,IAAI,CAAC,YAAY,EAAE;QAAE,OAAO;IAC5B,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IAC5B,IAAI,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC;QAAE,OAAO;IACtC,MAAM,IAAI,gBAAgB,CAAC,GAAG,IAAI,CAAC,IAAI,qBAAqB,OAAO,GAAG,CAAC,CAAC;AAC1E,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type SqliteDatabase } from "@agentsean/db";
|
|
2
|
+
export declare class EnvelopeError extends Error {
|
|
3
|
+
readonly name = "EnvelopeError";
|
|
4
|
+
}
|
|
5
|
+
export declare function masterKeyFromEnv(env?: NodeJS.ProcessEnv): Buffer;
|
|
6
|
+
export declare function encryptSecret(dek: Buffer, plaintext: string): string;
|
|
7
|
+
export declare function decryptSecret(dek: Buffer, blob: string): string;
|
|
8
|
+
export declare function loadOrCreateDek(db: SqliteDatabase, tenantId: string, master: Buffer, now?: Date): Buffer;
|
|
9
|
+
//# sourceMappingURL=envelope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"envelope.d.ts","sourceRoot":"","sources":["../src/envelope.ts"],"names":[],"mappings":"AAGA,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAIlE,qBAAa,aAAc,SAAQ,KAAK;IACtC,SAAkB,IAAI,mBAAmB;CAC1C;AAED,wBAAgB,gBAAgB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAS7E;AAoBD,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAMpE;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAQ/D;AAED,wBAAgB,eAAe,CAC7B,EAAE,EAAE,cAAc,EAClB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,GAAG,OAAa,GACf,MAAM,CAiBR"}
|
package/dist/envelope.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { createCipheriv, createDecipheriv, createHash, randomBytes } from "node:crypto";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { eq } from "drizzle-orm";
|
|
4
|
+
import { envelopeKeys } from "@agentsean/db";
|
|
5
|
+
const ALGO = "aes-256-gcm";
|
|
6
|
+
export class EnvelopeError extends Error {
|
|
7
|
+
name = "EnvelopeError";
|
|
8
|
+
}
|
|
9
|
+
export function masterKeyFromEnv(env = process.env) {
|
|
10
|
+
const raw = env["SEAN_KMS_KEY"]?.trim();
|
|
11
|
+
if (!raw) {
|
|
12
|
+
throw new EnvelopeError("SEAN_KMS_KEY is required to wrap per-tenant data keys.");
|
|
13
|
+
}
|
|
14
|
+
if (/^[0-9a-f]+$/i.test(raw) && raw.length === 64)
|
|
15
|
+
return Buffer.from(raw, "hex");
|
|
16
|
+
const buf = Buffer.from(raw, "base64");
|
|
17
|
+
if (buf.length === 32)
|
|
18
|
+
return buf;
|
|
19
|
+
return createHash("sha256").update(raw).digest();
|
|
20
|
+
}
|
|
21
|
+
function wrapDek(master, dek) {
|
|
22
|
+
const iv = randomBytes(12);
|
|
23
|
+
const cipher = createCipheriv(ALGO, master, iv);
|
|
24
|
+
const enc = Buffer.concat([cipher.update(dek), cipher.final()]);
|
|
25
|
+
const tag = cipher.getAuthTag();
|
|
26
|
+
return Buffer.concat([iv, tag, enc]).toString("base64url");
|
|
27
|
+
}
|
|
28
|
+
function unwrapDek(master, wrapped) {
|
|
29
|
+
const buf = Buffer.from(wrapped, "base64url");
|
|
30
|
+
const iv = buf.subarray(0, 12);
|
|
31
|
+
const tag = buf.subarray(12, 28);
|
|
32
|
+
const enc = buf.subarray(28);
|
|
33
|
+
const decipher = createDecipheriv(ALGO, master, iv);
|
|
34
|
+
decipher.setAuthTag(tag);
|
|
35
|
+
return Buffer.concat([decipher.update(enc), decipher.final()]);
|
|
36
|
+
}
|
|
37
|
+
export function encryptSecret(dek, plaintext) {
|
|
38
|
+
const iv = randomBytes(12);
|
|
39
|
+
const cipher = createCipheriv(ALGO, dek, iv);
|
|
40
|
+
const enc = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
|
|
41
|
+
const tag = cipher.getAuthTag();
|
|
42
|
+
return Buffer.concat([iv, tag, enc]).toString("base64url");
|
|
43
|
+
}
|
|
44
|
+
export function decryptSecret(dek, blob) {
|
|
45
|
+
const buf = Buffer.from(blob, "base64url");
|
|
46
|
+
const iv = buf.subarray(0, 12);
|
|
47
|
+
const tag = buf.subarray(12, 28);
|
|
48
|
+
const enc = buf.subarray(28);
|
|
49
|
+
const decipher = createDecipheriv(ALGO, dek, iv);
|
|
50
|
+
decipher.setAuthTag(tag);
|
|
51
|
+
return Buffer.concat([decipher.update(enc), decipher.final()]).toString("utf8");
|
|
52
|
+
}
|
|
53
|
+
export function loadOrCreateDek(db, tenantId, master, now = new Date()) {
|
|
54
|
+
const existing = db
|
|
55
|
+
.select()
|
|
56
|
+
.from(envelopeKeys)
|
|
57
|
+
.where(eq(envelopeKeys.tenantId, tenantId))
|
|
58
|
+
.get();
|
|
59
|
+
if (existing)
|
|
60
|
+
return unwrapDek(master, existing.wrappedDek);
|
|
61
|
+
const dek = randomBytes(32);
|
|
62
|
+
db.insert(envelopeKeys)
|
|
63
|
+
.values({
|
|
64
|
+
id: randomUUID(),
|
|
65
|
+
tenantId,
|
|
66
|
+
wrappedDek: wrapDek(master, dek),
|
|
67
|
+
createdAt: now.toISOString(),
|
|
68
|
+
})
|
|
69
|
+
.run();
|
|
70
|
+
return dek;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=envelope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"envelope.js","sourceRoot":"","sources":["../src/envelope.ts"],"names":[],"mappings":"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"}
|