@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.
Files changed (54) hide show
  1. package/LICENSE +661 -0
  2. package/dist/billing.d.ts +40 -0
  3. package/dist/billing.d.ts.map +1 -0
  4. package/dist/billing.js +142 -0
  5. package/dist/billing.js.map +1 -0
  6. package/dist/connector.d.ts +22 -0
  7. package/dist/connector.d.ts.map +1 -0
  8. package/dist/connector.js +65 -0
  9. package/dist/connector.js.map +1 -0
  10. package/dist/cost.d.ts +15 -0
  11. package/dist/cost.d.ts.map +1 -0
  12. package/dist/cost.js +38 -0
  13. package/dist/cost.js.map +1 -0
  14. package/dist/engine.d.ts +47 -0
  15. package/dist/engine.d.ts.map +1 -0
  16. package/dist/engine.js +62 -0
  17. package/dist/engine.js.map +1 -0
  18. package/dist/entitlements.d.ts +8 -0
  19. package/dist/entitlements.d.ts.map +1 -0
  20. package/dist/entitlements.js +33 -0
  21. package/dist/entitlements.js.map +1 -0
  22. package/dist/envelope.d.ts +9 -0
  23. package/dist/envelope.d.ts.map +1 -0
  24. package/dist/envelope.js +72 -0
  25. package/dist/envelope.js.map +1 -0
  26. package/dist/erasure.d.ts +27 -0
  27. package/dist/erasure.d.ts.map +1 -0
  28. package/dist/erasure.js +54 -0
  29. package/dist/erasure.js.map +1 -0
  30. package/dist/index.d.ts +13 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +13 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/neighbour.d.ts +13 -0
  35. package/dist/neighbour.d.ts.map +1 -0
  36. package/dist/neighbour.js +76 -0
  37. package/dist/neighbour.js.map +1 -0
  38. package/dist/oauth.d.ts +5 -0
  39. package/dist/oauth.d.ts.map +1 -0
  40. package/dist/oauth.js +35 -0
  41. package/dist/oauth.js.map +1 -0
  42. package/dist/plans.d.ts +29 -0
  43. package/dist/plans.d.ts.map +1 -0
  44. package/dist/plans.js +110 -0
  45. package/dist/plans.js.map +1 -0
  46. package/dist/tenants.d.ts +48 -0
  47. package/dist/tenants.d.ts.map +1 -0
  48. package/dist/tenants.js +121 -0
  49. package/dist/tenants.js.map +1 -0
  50. package/dist/tracing.d.ts +11 -0
  51. package/dist/tracing.d.ts.map +1 -0
  52. package/dist/tracing.js +9 -0
  53. package/dist/tracing.js.map +1 -0
  54. package/package.json +52 -0
@@ -0,0 +1,27 @@
1
+ import { type SqliteDatabase } from "@agentsean/db";
2
+ /** GDPR erasure runbook. Deletes tenant-owned rows; sites cascade via FK. */
3
+ export declare function eraseTenant(db: SqliteDatabase, tenantId: string, now?: Date): {
4
+ sitesRemoved: number;
5
+ };
6
+ export declare const SUBPROCESSORS: readonly [{
7
+ readonly name: "Stripe";
8
+ readonly purpose: "Billing and metered article usage";
9
+ readonly region: "US";
10
+ }, {
11
+ readonly name: "Hetzner";
12
+ readonly purpose: "App and Postgres compute";
13
+ readonly region: "EU";
14
+ }, {
15
+ readonly name: "Cloudflare R2";
16
+ readonly purpose: "Object storage (crawl artifacts, reports)";
17
+ readonly region: "global";
18
+ }, {
19
+ readonly name: "Google";
20
+ readonly purpose: "Search Console / Analytics OAuth (refresh tokens)";
21
+ readonly region: "US";
22
+ }, {
23
+ readonly name: "DataForSEO";
24
+ readonly purpose: "Licensed rank snapshots when a customer key is present";
25
+ readonly region: "US";
26
+ }];
27
+ //# sourceMappingURL=erasure.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"erasure.d.ts","sourceRoot":"","sources":["../src/erasure.ts"],"names":[],"mappings":"AAEA,OAAO,EAWL,KAAK,cAAc,EACpB,MAAM,eAAe,CAAC;AAEvB,6EAA6E;AAC7E,wBAAgB,WAAW,CACzB,EAAE,EAAE,cAAc,EAClB,QAAQ,EAAE,MAAM,EAChB,GAAG,OAAa,GACf;IAAE,YAAY,EAAE,MAAM,CAAA;CAAE,CA6B1B;AAED,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;EAkBhB,CAAC"}
@@ -0,0 +1,54 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { eq } from "drizzle-orm";
3
+ import { connectorPairings, envelopeKeys, erasureRequests, meteredUsage, quotaWindows, sites, subscriptions, tenantSeats, tenantSites, tenants, } from "@agentsean/db";
4
+ /** GDPR erasure runbook. Deletes tenant-owned rows; sites cascade via FK. */
5
+ export function eraseTenant(db, tenantId, now = new Date()) {
6
+ const links = db
7
+ .select()
8
+ .from(tenantSites)
9
+ .where(eq(tenantSites.tenantId, tenantId))
10
+ .all();
11
+ const siteIds = links.map((l) => l.siteId);
12
+ for (const siteId of siteIds) {
13
+ db.delete(sites).where(eq(sites.id, siteId)).run();
14
+ }
15
+ db.delete(connectorPairings).where(eq(connectorPairings.tenantId, tenantId)).run();
16
+ db.delete(quotaWindows).where(eq(quotaWindows.tenantId, tenantId)).run();
17
+ db.delete(meteredUsage).where(eq(meteredUsage.tenantId, tenantId)).run();
18
+ db.delete(envelopeKeys).where(eq(envelopeKeys.tenantId, tenantId)).run();
19
+ db.delete(subscriptions).where(eq(subscriptions.tenantId, tenantId)).run();
20
+ db.delete(tenantSeats).where(eq(tenantSeats.tenantId, tenantId)).run();
21
+ db.delete(tenantSites).where(eq(tenantSites.tenantId, tenantId)).run();
22
+ db.delete(tenants).where(eq(tenants.id, tenantId)).run();
23
+ db.insert(erasureRequests)
24
+ .values({
25
+ id: randomUUID(),
26
+ tenantId,
27
+ status: "completed",
28
+ requestedAt: now.toISOString(),
29
+ completedAt: now.toISOString(),
30
+ notes: `Removed ${siteIds.length} site(s), seats, keys, usage, and subscription.`,
31
+ })
32
+ .run();
33
+ return { sitesRemoved: siteIds.length };
34
+ }
35
+ export const SUBPROCESSORS = [
36
+ { name: "Stripe", purpose: "Billing and metered article usage", region: "US" },
37
+ { name: "Hetzner", purpose: "App and Postgres compute", region: "EU" },
38
+ {
39
+ name: "Cloudflare R2",
40
+ purpose: "Object storage (crawl artifacts, reports)",
41
+ region: "global",
42
+ },
43
+ {
44
+ name: "Google",
45
+ purpose: "Search Console / Analytics OAuth (refresh tokens)",
46
+ region: "US",
47
+ },
48
+ {
49
+ name: "DataForSEO",
50
+ purpose: "Licensed rank snapshots when a customer key is present",
51
+ region: "US",
52
+ },
53
+ ];
54
+ //# sourceMappingURL=erasure.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"erasure.js","sourceRoot":"","sources":["../src/erasure.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ export { PLAN_IDS, PLANS, NON_LLM_COGS_USD, BYOK_REQUIRED, isPlanId, planOf, grossMargin, isHostedMode, type PlanId, type Plan, type RankCadence, } from "./plans.js";
2
+ export { EnvelopeError, masterKeyFromEnv, encryptSecret, decryptSecret, loadOrCreateDek, } from "./envelope.js";
3
+ export { JOBS_PER_MIN, CONCURRENT_JOBS, CRAWL_PAGES_PER_DAY, NeighbourLimitError, allowTenantJob, allowTenantCrawlPages, acquireConcurrency, releaseConcurrency, } from "./neighbour.js";
4
+ export { CMS_WRITE_KINDS, HostedCredentialError, isCmsWriteKind, refuseHostedCmsCredential, createConnectorPairing, activateConnector, listConnectors, } from "./connector.js";
5
+ export { SiteQuotaError, ByokRequiredError, createTenant, getTenant, tenantSiteCount, addTenantSite, listTenantSites, tenantIdForSite, assertByok, rankCadenceForTenant, } from "./tenants.js";
6
+ export { fakeStripe, applyStripeEvent, activateSubscription, reportArticleUsage, articlesThisMonth, type StripeEvent, type StripeLike, } from "./billing.js";
7
+ export { tenantCostVisibility, type TenantCost } from "./cost.js";
8
+ export { eraseTenant, SUBPROCESSORS } from "./erasure.js";
9
+ export { hostedPublicOrigin, hostedOauthRedirectUri, assertOauthRedirect, } from "./oauth.js";
10
+ export { traceLlm, type LlmTrace } from "./tracing.js";
11
+ export { EntitlementError, hasFeature, assertEntitlement, type EntitlementFeature, } from "./entitlements.js";
12
+ export { signupTenant, completeCheckout, hostedStatus } from "./engine.js";
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,KAAK,EACL,gBAAgB,EAChB,aAAa,EACb,QAAQ,EACR,MAAM,EACN,WAAW,EACX,YAAY,EACZ,KAAK,MAAM,EACX,KAAK,IAAI,EACT,KAAK,WAAW,GACjB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,aAAa,EACb,aAAa,EACb,eAAe,GAChB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,YAAY,EACZ,eAAe,EACf,mBAAmB,EACnB,mBAAmB,EACnB,cAAc,EACd,qBAAqB,EACrB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,cAAc,EACd,yBAAyB,EACzB,sBAAsB,EACtB,iBAAiB,EACjB,cAAc,GACf,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,SAAS,EACT,eAAe,EACf,aAAa,EACb,eAAe,EACf,eAAe,EACf,UAAU,EACV,oBAAoB,GACrB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,EACjB,KAAK,WAAW,EAChB,KAAK,UAAU,GAChB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,oBAAoB,EAAE,KAAK,UAAU,EAAE,MAAM,WAAW,CAAC;AAClE,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EACL,kBAAkB,EAClB,sBAAsB,EACtB,mBAAmB,GACpB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,QAAQ,EAAE,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,EACL,gBAAgB,EAChB,UAAU,EACV,iBAAiB,EACjB,KAAK,kBAAkB,GACxB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,13 @@
1
+ export { PLAN_IDS, PLANS, NON_LLM_COGS_USD, BYOK_REQUIRED, isPlanId, planOf, grossMargin, isHostedMode, } from "./plans.js";
2
+ export { EnvelopeError, masterKeyFromEnv, encryptSecret, decryptSecret, loadOrCreateDek, } from "./envelope.js";
3
+ export { JOBS_PER_MIN, CONCURRENT_JOBS, CRAWL_PAGES_PER_DAY, NeighbourLimitError, allowTenantJob, allowTenantCrawlPages, acquireConcurrency, releaseConcurrency, } from "./neighbour.js";
4
+ export { CMS_WRITE_KINDS, HostedCredentialError, isCmsWriteKind, refuseHostedCmsCredential, createConnectorPairing, activateConnector, listConnectors, } from "./connector.js";
5
+ export { SiteQuotaError, ByokRequiredError, createTenant, getTenant, tenantSiteCount, addTenantSite, listTenantSites, tenantIdForSite, assertByok, rankCadenceForTenant, } from "./tenants.js";
6
+ export { fakeStripe, applyStripeEvent, activateSubscription, reportArticleUsage, articlesThisMonth, } from "./billing.js";
7
+ export { tenantCostVisibility } from "./cost.js";
8
+ export { eraseTenant, SUBPROCESSORS } from "./erasure.js";
9
+ export { hostedPublicOrigin, hostedOauthRedirectUri, assertOauthRedirect, } from "./oauth.js";
10
+ export { traceLlm } from "./tracing.js";
11
+ export { EntitlementError, hasFeature, assertEntitlement, } from "./entitlements.js";
12
+ export { signupTenant, completeCheckout, hostedStatus } from "./engine.js";
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,KAAK,EACL,gBAAgB,EAChB,aAAa,EACb,QAAQ,EACR,MAAM,EACN,WAAW,EACX,YAAY,GAIb,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,aAAa,EACb,aAAa,EACb,eAAe,GAChB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,YAAY,EACZ,eAAe,EACf,mBAAmB,EACnB,mBAAmB,EACnB,cAAc,EACd,qBAAqB,EACrB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,cAAc,EACd,yBAAyB,EACzB,sBAAsB,EACtB,iBAAiB,EACjB,cAAc,GACf,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,SAAS,EACT,eAAe,EACf,aAAa,EACb,eAAe,EACf,eAAe,EACf,UAAU,EACV,oBAAoB,GACrB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,GAGlB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,oBAAoB,EAAmB,MAAM,WAAW,CAAC;AAClE,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EACL,kBAAkB,EAClB,sBAAsB,EACtB,mBAAmB,GACpB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,QAAQ,EAAiB,MAAM,cAAc,CAAC;AACvD,OAAO,EACL,gBAAgB,EAChB,UAAU,EACV,iBAAiB,GAElB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,13 @@
1
+ import { type SqliteDatabase } from "@agentsean/db";
2
+ /** Shared-cluster noisy-neighbour caps. Per-tenant isolated machines are not the $9 path. */
3
+ export declare const JOBS_PER_MIN = 30;
4
+ export declare const CONCURRENT_JOBS = 2;
5
+ export declare const CRAWL_PAGES_PER_DAY = 5000;
6
+ export declare class NeighbourLimitError extends Error {
7
+ readonly name = "NeighbourLimitError";
8
+ }
9
+ export declare function allowTenantJob(db: SqliteDatabase, tenantId: string, now?: Date): boolean;
10
+ export declare function allowTenantCrawlPages(db: SqliteDatabase, tenantId: string, pages: number, now?: Date): boolean;
11
+ export declare function acquireConcurrency(db: SqliteDatabase, tenantId: string, now?: Date): void;
12
+ export declare function releaseConcurrency(db: SqliteDatabase, tenantId: string, now?: Date): void;
13
+ //# sourceMappingURL=neighbour.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"neighbour.d.ts","sourceRoot":"","sources":["../src/neighbour.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAElE,6FAA6F;AAC7F,eAAO,MAAM,YAAY,KAAK,CAAC;AAC/B,eAAO,MAAM,eAAe,IAAI,CAAC;AACjC,eAAO,MAAM,mBAAmB,OAAQ,CAAC;AAEzC,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,SAAkB,IAAI,yBAAyB;CAChD;AAkDD,wBAAgB,cAAc,CAC5B,EAAE,EAAE,cAAc,EAClB,QAAQ,EAAE,MAAM,EAChB,GAAG,OAAa,GACf,OAAO,CAOT;AAED,wBAAgB,qBAAqB,CACnC,EAAE,EAAE,cAAc,EAClB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,GAAG,OAAa,GACf,OAAO,CAST;AAED,wBAAgB,kBAAkB,CAChC,EAAE,EAAE,cAAc,EAClB,QAAQ,EAAE,MAAM,EAChB,GAAG,OAAa,GACf,IAAI,CAMN;AAED,wBAAgB,kBAAkB,CAChC,EAAE,EAAE,cAAc,EAClB,QAAQ,EAAE,MAAM,EAChB,GAAG,OAAa,GACf,IAAI,CAEN"}
@@ -0,0 +1,76 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { eq } from "drizzle-orm";
3
+ import { quotaWindows } from "@agentsean/db";
4
+ /** Shared-cluster noisy-neighbour caps. Per-tenant isolated machines are not the $9 path. */
5
+ export const JOBS_PER_MIN = 30;
6
+ export const CONCURRENT_JOBS = 2;
7
+ export const CRAWL_PAGES_PER_DAY = 5_000;
8
+ export class NeighbourLimitError extends Error {
9
+ name = "NeighbourLimitError";
10
+ }
11
+ function minuteBucket(now) {
12
+ return now.toISOString().slice(0, 16);
13
+ }
14
+ function dayBucket(now) {
15
+ return now.toISOString().slice(0, 10);
16
+ }
17
+ function bump(db, tenantId, windowStart, field, delta) {
18
+ const row = db
19
+ .select()
20
+ .from(quotaWindows)
21
+ .where(eq(quotaWindows.tenantId, tenantId))
22
+ .all()
23
+ .find((r) => r.windowStart === windowStart);
24
+ if (!row) {
25
+ const initial = {
26
+ jobs: 0,
27
+ crawlPages: 0,
28
+ concurrent: 0,
29
+ [field]: Math.max(0, delta),
30
+ };
31
+ db.insert(quotaWindows)
32
+ .values({
33
+ id: randomUUID(),
34
+ tenantId,
35
+ windowStart,
36
+ jobs: initial.jobs,
37
+ crawlPages: initial.crawlPages,
38
+ concurrent: initial.concurrent,
39
+ })
40
+ .run();
41
+ return initial[field];
42
+ }
43
+ const next = Math.max(0, row[field] + delta);
44
+ db.update(quotaWindows)
45
+ .set({ [field]: next })
46
+ .where(eq(quotaWindows.id, row.id))
47
+ .run();
48
+ return next;
49
+ }
50
+ export function allowTenantJob(db, tenantId, now = new Date()) {
51
+ const n = bump(db, tenantId, minuteBucket(now), "jobs", 1);
52
+ if (n > JOBS_PER_MIN) {
53
+ bump(db, tenantId, minuteBucket(now), "jobs", -1);
54
+ throw new NeighbourLimitError(`Tenant job cap is ${JOBS_PER_MIN}/min.`);
55
+ }
56
+ return true;
57
+ }
58
+ export function allowTenantCrawlPages(db, tenantId, pages, now = new Date()) {
59
+ const n = bump(db, tenantId, dayBucket(now), "crawlPages", pages);
60
+ if (n > CRAWL_PAGES_PER_DAY) {
61
+ bump(db, tenantId, dayBucket(now), "crawlPages", -pages);
62
+ throw new NeighbourLimitError(`Tenant crawl cap is ${CRAWL_PAGES_PER_DAY} pages/day.`);
63
+ }
64
+ return true;
65
+ }
66
+ export function acquireConcurrency(db, tenantId, now = new Date()) {
67
+ const n = bump(db, tenantId, dayBucket(now), "concurrent", 1);
68
+ if (n > CONCURRENT_JOBS) {
69
+ bump(db, tenantId, dayBucket(now), "concurrent", -1);
70
+ throw new NeighbourLimitError(`Tenant concurrent-job cap is ${CONCURRENT_JOBS}.`);
71
+ }
72
+ }
73
+ export function releaseConcurrency(db, tenantId, now = new Date()) {
74
+ bump(db, tenantId, dayBucket(now), "concurrent", -1);
75
+ }
76
+ //# sourceMappingURL=neighbour.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"neighbour.js","sourceRoot":"","sources":["../src/neighbour.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export declare function hostedPublicOrigin(env?: NodeJS.ProcessEnv): string | null;
2
+ export declare function hostedOauthRedirectUri(env?: NodeJS.ProcessEnv, path?: string): string | null;
3
+ /** Self-host broker stays loopback-only. Hosted OAuth is a web client on our origin. */
4
+ export declare function assertOauthRedirect(uri: string, env?: NodeJS.ProcessEnv): void;
5
+ //# sourceMappingURL=oauth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../src/oauth.ts"],"names":[],"mappings":"AAEA,wBAAgB,kBAAkB,CAChC,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,GAAG,IAAI,CAUf;AAED,wBAAgB,sBAAsB,CACpC,GAAG,GAAE,MAAM,CAAC,UAAwB,EACpC,IAAI,SAAoB,GACvB,MAAM,GAAG,IAAI,CAIf;AAED,wFAAwF;AACxF,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,MAAM,EACX,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,IAAI,CAWN"}
package/dist/oauth.js ADDED
@@ -0,0 +1,35 @@
1
+ import { isHostedMode } from "./plans.js";
2
+ export function hostedPublicOrigin(env = process.env) {
3
+ const raw = env["SEAN_PUBLIC_ORIGIN"]?.trim();
4
+ if (!raw)
5
+ return null;
6
+ try {
7
+ const u = new URL(raw);
8
+ if (u.protocol !== "https:" && u.protocol !== "http:")
9
+ return null;
10
+ return u.origin;
11
+ }
12
+ catch {
13
+ return null;
14
+ }
15
+ }
16
+ export function hostedOauthRedirectUri(env = process.env, path = "/oauth/callback") {
17
+ const origin = hostedPublicOrigin(env);
18
+ if (!origin)
19
+ return null;
20
+ return `${origin}${path.startsWith("/") ? path : `/${path}`}`;
21
+ }
22
+ /** Self-host broker stays loopback-only. Hosted OAuth is a web client on our origin. */
23
+ export function assertOauthRedirect(uri, env = process.env) {
24
+ if (isHostedMode(env)) {
25
+ const origin = hostedPublicOrigin(env);
26
+ if (!origin || !uri.startsWith(origin)) {
27
+ throw new Error("Hosted OAuth redirect must match SEAN_PUBLIC_ORIGIN.");
28
+ }
29
+ return;
30
+ }
31
+ if (!uri.startsWith("http://127.0.0.1:")) {
32
+ throw new Error("Broker will only hand tokens to http://127.0.0.1 loopback.");
33
+ }
34
+ }
35
+ //# sourceMappingURL=oauth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.js","sourceRoot":"","sources":["../src/oauth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,MAAM,UAAU,kBAAkB,CAChC,MAAyB,OAAO,CAAC,GAAG;IAEpC,MAAM,GAAG,GAAG,GAAG,CAAC,oBAAoB,CAAC,EAAE,IAAI,EAAE,CAAC;IAC9C,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QACvB,IAAI,CAAC,CAAC,QAAQ,KAAK,QAAQ,IAAI,CAAC,CAAC,QAAQ,KAAK,OAAO;YAAE,OAAO,IAAI,CAAC;QACnE,OAAO,CAAC,CAAC,MAAM,CAAC;IAClB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,MAAyB,OAAO,CAAC,GAAG,EACpC,IAAI,GAAG,iBAAiB;IAExB,MAAM,MAAM,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC;IACvC,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,OAAO,GAAG,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;AAChE,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,mBAAmB,CACjC,GAAW,EACX,MAAyB,OAAO,CAAC,GAAG;IAEpC,IAAI,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC;QACtB,MAAM,MAAM,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC;QACvC,IAAI,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YACvC,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;QAC1E,CAAC;QACD,OAAO;IACT,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,mBAAmB,CAAC,EAAE,CAAC;QACzC,MAAM,IAAI,KAAK,CAAC,4DAA4D,CAAC,CAAC;IAChF,CAAC;AACH,CAAC"}
@@ -0,0 +1,29 @@
1
+ /** Locked packaging from PLAN Phase 10. $8 stays as the per-site agency price. */
2
+ export declare const PLAN_IDS: readonly ["self_host", "cloud_starter", "cloud_pro", "business", "agency"];
3
+ export type PlanId = (typeof PLAN_IDS)[number];
4
+ export type RankCadence = "weekly" | "daily";
5
+ export type Plan = {
6
+ id: PlanId;
7
+ name: string;
8
+ priceUsdMonth: number;
9
+ sites: number;
10
+ ranks: RankCadence;
11
+ aiVisibility: boolean;
12
+ articlesMetered: boolean;
13
+ seats: number;
14
+ whiteLabel: boolean;
15
+ apiAccess: boolean;
16
+ hostedOauth: boolean;
17
+ priorityQueue: boolean;
18
+ clientSeats: boolean;
19
+ bulkOps: boolean;
20
+ };
21
+ export declare const PLANS: Record<PlanId, Plan>;
22
+ /** Non-LLM COGS at BYOK. PLAN: $2.29/tenant/month → 71% gross on Starter. */
23
+ export declare const NON_LLM_COGS_USD = 2.29;
24
+ export declare const BYOK_REQUIRED = true;
25
+ export declare function isPlanId(value: string): value is PlanId;
26
+ export declare function planOf(id: string): Plan;
27
+ export declare function grossMargin(priceUsd: number): number;
28
+ export declare function isHostedMode(env?: NodeJS.ProcessEnv): boolean;
29
+ //# sourceMappingURL=plans.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plans.d.ts","sourceRoot":"","sources":["../src/plans.ts"],"names":[],"mappings":"AAAA,kFAAkF;AAElF,eAAO,MAAM,QAAQ,4EAMX,CAAC;AAEX,MAAM,MAAM,MAAM,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC;AAE/C,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE7C,MAAM,MAAM,IAAI,GAAG;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,WAAW,CAAC;IACnB,YAAY,EAAE,OAAO,CAAC;IACtB,eAAe,EAAE,OAAO,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,OAAO,CAAC;IACrB,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,eAAO,MAAM,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAiFtC,CAAC;AAEF,6EAA6E;AAC7E,eAAO,MAAM,gBAAgB,OAAO,CAAC;AACrC,eAAO,MAAM,aAAa,OAAO,CAAC;AAElC,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,MAAM,CAEvD;AAED,wBAAgB,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAGvC;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGpD;AAED,wBAAgB,YAAY,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAE1E"}
package/dist/plans.js ADDED
@@ -0,0 +1,110 @@
1
+ /** Locked packaging from PLAN Phase 10. $8 stays as the per-site agency price. */
2
+ export const PLAN_IDS = [
3
+ "self_host",
4
+ "cloud_starter",
5
+ "cloud_pro",
6
+ "business",
7
+ "agency",
8
+ ];
9
+ export const PLANS = {
10
+ self_host: {
11
+ id: "self_host",
12
+ name: "Self-host",
13
+ priceUsdMonth: 0,
14
+ sites: Number.POSITIVE_INFINITY,
15
+ ranks: "weekly",
16
+ aiVisibility: true,
17
+ articlesMetered: false,
18
+ seats: Number.POSITIVE_INFINITY,
19
+ whiteLabel: true,
20
+ apiAccess: true,
21
+ hostedOauth: false,
22
+ priorityQueue: false,
23
+ clientSeats: true,
24
+ bulkOps: true,
25
+ },
26
+ cloud_starter: {
27
+ id: "cloud_starter",
28
+ name: "Cloud Starter",
29
+ priceUsdMonth: 9,
30
+ sites: 1,
31
+ ranks: "weekly",
32
+ aiVisibility: false,
33
+ articlesMetered: true,
34
+ seats: 1,
35
+ whiteLabel: false,
36
+ apiAccess: false,
37
+ hostedOauth: true,
38
+ priorityQueue: false,
39
+ clientSeats: false,
40
+ bulkOps: false,
41
+ },
42
+ cloud_pro: {
43
+ id: "cloud_pro",
44
+ name: "Cloud Pro",
45
+ priceUsdMonth: 29,
46
+ sites: 3,
47
+ ranks: "daily",
48
+ aiVisibility: true,
49
+ articlesMetered: true,
50
+ seats: 3,
51
+ whiteLabel: false,
52
+ apiAccess: false,
53
+ hostedOauth: true,
54
+ priorityQueue: false,
55
+ clientSeats: false,
56
+ bulkOps: false,
57
+ },
58
+ business: {
59
+ id: "business",
60
+ name: "Business",
61
+ priceUsdMonth: 79,
62
+ sites: 10,
63
+ ranks: "daily",
64
+ aiVisibility: true,
65
+ articlesMetered: true,
66
+ seats: 10,
67
+ whiteLabel: false,
68
+ apiAccess: true,
69
+ hostedOauth: true,
70
+ priorityQueue: true,
71
+ clientSeats: false,
72
+ bulkOps: false,
73
+ },
74
+ agency: {
75
+ id: "agency",
76
+ name: "Agency",
77
+ priceUsdMonth: 249,
78
+ sites: 50,
79
+ ranks: "daily",
80
+ aiVisibility: true,
81
+ articlesMetered: true,
82
+ seats: 25,
83
+ whiteLabel: true,
84
+ apiAccess: true,
85
+ hostedOauth: true,
86
+ priorityQueue: true,
87
+ clientSeats: true,
88
+ bulkOps: true,
89
+ },
90
+ };
91
+ /** Non-LLM COGS at BYOK. PLAN: $2.29/tenant/month → 71% gross on Starter. */
92
+ export const NON_LLM_COGS_USD = 2.29;
93
+ export const BYOK_REQUIRED = true;
94
+ export function isPlanId(value) {
95
+ return PLAN_IDS.includes(value);
96
+ }
97
+ export function planOf(id) {
98
+ if (!isPlanId(id))
99
+ throw new Error(`unknown plan ${id}`);
100
+ return PLANS[id];
101
+ }
102
+ export function grossMargin(priceUsd) {
103
+ if (priceUsd <= 0)
104
+ return 1;
105
+ return (priceUsd - NON_LLM_COGS_USD) / priceUsd;
106
+ }
107
+ export function isHostedMode(env = process.env) {
108
+ return env["SEAN_HOSTED"] === "1" || Boolean(env["SEAN_PUBLIC_ORIGIN"]?.trim());
109
+ }
110
+ //# sourceMappingURL=plans.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plans.js","sourceRoot":"","sources":["../src/plans.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ import { type SqliteDatabase } from "@agentsean/db";
2
+ import { type PlanId } from "./plans.js";
3
+ export declare class SiteQuotaError extends Error {
4
+ readonly name = "SiteQuotaError";
5
+ }
6
+ export declare class ByokRequiredError extends Error {
7
+ readonly name = "ByokRequiredError";
8
+ }
9
+ export declare function createTenant(db: SqliteDatabase, opts: {
10
+ name: string;
11
+ email: string;
12
+ plan: PlanId;
13
+ now?: Date | undefined;
14
+ }): {
15
+ id: string;
16
+ };
17
+ export declare function getTenant(db: SqliteDatabase, tenantId: string): {
18
+ id: string;
19
+ name: string;
20
+ email: string;
21
+ plan: string;
22
+ status: string;
23
+ stripeCustomerId: string | null;
24
+ stripeSubscriptionId: string | null;
25
+ byok: number;
26
+ createdAt: string;
27
+ updatedAt: string;
28
+ } | null;
29
+ export declare function tenantSiteCount(db: SqliteDatabase, tenantId: string): number;
30
+ export declare function addTenantSite(db: SqliteDatabase, opts: {
31
+ tenantId: string;
32
+ origin: string;
33
+ name?: string | undefined;
34
+ now?: Date | undefined;
35
+ }): {
36
+ siteId: string;
37
+ };
38
+ export declare function listTenantSites(db: SqliteDatabase, tenantId: string): {
39
+ id: string;
40
+ origin: string;
41
+ name: string | null;
42
+ }[];
43
+ export declare function tenantIdForSite(db: SqliteDatabase, siteId: string): string | null;
44
+ export declare function assertByok(tenant: {
45
+ byok: number;
46
+ }): void;
47
+ export declare function rankCadenceForTenant(db: SqliteDatabase, tenantId: string): "weekly" | "daily";
48
+ //# sourceMappingURL=tenants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tenants.d.ts","sourceRoot":"","sources":["../src/tenants.ts"],"names":[],"mappings":"AAEA,OAAO,EAKL,KAAK,cAAc,EACpB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAiB,KAAK,MAAM,EAAE,MAAM,YAAY,CAAC;AAExD,qBAAa,cAAe,SAAQ,KAAK;IACvC,SAAkB,IAAI,oBAAoB;CAC3C;AAED,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,SAAkB,IAAI,uBAAuB;CAC9C;AAED,wBAAgB,YAAY,CAC1B,EAAE,EAAE,cAAc,EAClB,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,IAAI,GAAG,SAAS,CAAA;CAAE,GAC1E;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CA2BhB;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM;;;;;;;;;;;SAE7D;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAG5E;AAED,wBAAgB,aAAa,CAC3B,EAAE,EAAE,cAAc,EAClB,IAAI,EAAE;IACJ,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,GAAG,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;CACxB,GACA;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CA6CpB;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM;QAM3C,MAAM;YAAU,MAAM;UAAQ,MAAM,GAAG,IAAI;IAMnE;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKjF;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAMzD;AAED,wBAAgB,oBAAoB,CAClC,EAAE,EAAE,cAAc,EAClB,QAAQ,EAAE,MAAM,GACf,QAAQ,GAAG,OAAO,CAIpB"}
@@ -0,0 +1,121 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { eq } from "drizzle-orm";
3
+ import { sites, tenantSeats, tenantSites, tenants, } from "@agentsean/db";
4
+ import { PLANS, planOf } from "./plans.js";
5
+ export class SiteQuotaError extends Error {
6
+ name = "SiteQuotaError";
7
+ }
8
+ export class ByokRequiredError extends Error {
9
+ name = "ByokRequiredError";
10
+ }
11
+ export function createTenant(db, opts) {
12
+ const now = (opts.now ?? new Date()).toISOString();
13
+ const id = randomUUID();
14
+ db.insert(tenants)
15
+ .values({
16
+ id,
17
+ name: opts.name,
18
+ email: opts.email.toLowerCase(),
19
+ plan: opts.plan,
20
+ status: "trialing",
21
+ stripeCustomerId: null,
22
+ stripeSubscriptionId: null,
23
+ byok: 1,
24
+ createdAt: now,
25
+ updatedAt: now,
26
+ })
27
+ .run();
28
+ db.insert(tenantSeats)
29
+ .values({
30
+ id: randomUUID(),
31
+ tenantId: id,
32
+ email: opts.email.toLowerCase(),
33
+ role: "owner",
34
+ createdAt: now,
35
+ })
36
+ .run();
37
+ return { id };
38
+ }
39
+ export function getTenant(db, tenantId) {
40
+ return db.select().from(tenants).where(eq(tenants.id, tenantId)).get() ?? null;
41
+ }
42
+ export function tenantSiteCount(db, tenantId) {
43
+ return db.select().from(tenantSites).where(eq(tenantSites.tenantId, tenantId)).all()
44
+ .length;
45
+ }
46
+ export function addTenantSite(db, opts) {
47
+ const tenant = getTenant(db, opts.tenantId);
48
+ if (!tenant)
49
+ throw new Error("unknown_tenant");
50
+ const plan = planOf(tenant.plan);
51
+ const used = tenantSiteCount(db, opts.tenantId);
52
+ if (used >= plan.sites) {
53
+ throw new SiteQuotaError(`${plan.name} includes ${plan.sites} site${plan.sites === 1 ? "" : "s"}. ${used} already attached.`);
54
+ }
55
+ const now = (opts.now ?? new Date()).toISOString();
56
+ let site = db.select().from(sites).where(eq(sites.origin, opts.origin)).get();
57
+ if (!site) {
58
+ const siteId = randomUUID();
59
+ db.insert(sites)
60
+ .values({
61
+ id: siteId,
62
+ origin: opts.origin,
63
+ name: opts.name ?? opts.origin,
64
+ createdAt: now,
65
+ updatedAt: now,
66
+ })
67
+ .run();
68
+ site = db.select().from(sites).where(eq(sites.id, siteId)).get();
69
+ }
70
+ if (!site)
71
+ throw new Error("site_insert_failed");
72
+ const existing = db
73
+ .select()
74
+ .from(tenantSites)
75
+ .where(eq(tenantSites.siteId, site.id))
76
+ .get();
77
+ if (existing && existing.tenantId !== opts.tenantId) {
78
+ throw new SiteQuotaError("That origin is already attached to another tenant.");
79
+ }
80
+ if (!existing) {
81
+ db.insert(tenantSites)
82
+ .values({
83
+ id: randomUUID(),
84
+ tenantId: opts.tenantId,
85
+ siteId: site.id,
86
+ createdAt: now,
87
+ })
88
+ .run();
89
+ }
90
+ return { siteId: site.id };
91
+ }
92
+ export function listTenantSites(db, tenantId) {
93
+ const links = db
94
+ .select()
95
+ .from(tenantSites)
96
+ .where(eq(tenantSites.tenantId, tenantId))
97
+ .all();
98
+ const out = [];
99
+ for (const link of links) {
100
+ const site = db.select().from(sites).where(eq(sites.id, link.siteId)).get();
101
+ if (site)
102
+ out.push({ id: site.id, origin: site.origin, name: site.name });
103
+ }
104
+ return out;
105
+ }
106
+ export function tenantIdForSite(db, siteId) {
107
+ return (db.select().from(tenantSites).where(eq(tenantSites.siteId, siteId)).get()
108
+ ?.tenantId ?? null);
109
+ }
110
+ export function assertByok(tenant) {
111
+ if (!tenant.byok) {
112
+ throw new ByokRequiredError("BYOK is not optional on the hosted tier. Without it, non-LLM COGS still works; LLM spend is $13–16 against $8.");
113
+ }
114
+ }
115
+ export function rankCadenceForTenant(db, tenantId) {
116
+ const tenant = getTenant(db, tenantId);
117
+ if (!tenant)
118
+ return "weekly";
119
+ return PLANS[planOf(tenant.plan).id].ranks;
120
+ }
121
+ //# sourceMappingURL=tenants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tenants.js","sourceRoot":"","sources":["../src/tenants.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ export type LlmTrace = {
2
+ tenantId: string;
3
+ siteId?: string | undefined;
4
+ model: string;
5
+ inputTokens: number;
6
+ outputTokens: number;
7
+ costUsd: number;
8
+ };
9
+ /** Langfuse is hosted-tier only. Self-host is a no-op. */
10
+ export declare function traceLlm(event: LlmTrace, env?: NodeJS.ProcessEnv): Promise<void>;
11
+ //# sourceMappingURL=tracing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tracing.d.ts","sourceRoot":"","sources":["../src/tracing.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,0DAA0D;AAC1D,wBAAsB,QAAQ,CAC5B,KAAK,EAAE,QAAQ,EACf,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,OAAO,CAAC,IAAI,CAAC,CAGf"}