@superblocksteam/shared 0.9567.3 → 0.9568.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 (103) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +1 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/socket/protocol.d.ts +13 -1
  6. package/dist/socket/protocol.d.ts.map +1 -1
  7. package/dist/socket/protocol.js.map +1 -1
  8. package/dist/types/ai/index.d.ts +43 -1
  9. package/dist/types/ai/index.d.ts.map +1 -1
  10. package/dist/types/ai/index.js +1 -1
  11. package/dist/types/ai/index.js.map +1 -1
  12. package/dist/types/ai/quota-paywall.d.ts +8 -1
  13. package/dist/types/ai/quota-paywall.d.ts.map +1 -1
  14. package/dist/types/ai/quota-paywall.js +46 -15
  15. package/dist/types/ai/quota-paywall.js.map +1 -1
  16. package/dist/types/billing/billing.d.ts +5 -0
  17. package/dist/types/billing/billing.d.ts.map +1 -1
  18. package/dist/types/billing/billing.js +3 -0
  19. package/dist/types/billing/billing.js.map +1 -1
  20. package/dist/types/fact/fact.d.ts +17 -0
  21. package/dist/types/fact/fact.d.ts.map +1 -1
  22. package/dist/types/rbac/index.d.ts +5 -0
  23. package/dist/types/rbac/index.d.ts.map +1 -1
  24. package/dist/types/rbac/index.js +5 -0
  25. package/dist/types/rbac/index.js.map +1 -1
  26. package/dist/utils/attachment-upload.d.ts +122 -0
  27. package/dist/utils/attachment-upload.d.ts.map +1 -0
  28. package/dist/utils/attachment-upload.js +356 -0
  29. package/dist/utils/attachment-upload.js.map +1 -0
  30. package/dist/utils/attachment-upload.test.d.ts +2 -0
  31. package/dist/utils/attachment-upload.test.d.ts.map +1 -0
  32. package/dist/utils/attachment-upload.test.js +126 -0
  33. package/dist/utils/attachment-upload.test.js.map +1 -0
  34. package/dist/utils/github-workflow.d.ts +6 -0
  35. package/dist/utils/github-workflow.d.ts.map +1 -0
  36. package/dist/utils/github-workflow.js +72 -0
  37. package/dist/utils/github-workflow.js.map +1 -0
  38. package/dist/utils/index.d.ts +1 -0
  39. package/dist/utils/index.d.ts.map +1 -1
  40. package/dist/utils/index.js +1 -0
  41. package/dist/utils/index.js.map +1 -1
  42. package/dist-esm/index.d.ts +1 -0
  43. package/dist-esm/index.d.ts.map +1 -1
  44. package/dist-esm/index.js +1 -0
  45. package/dist-esm/index.js.map +1 -1
  46. package/dist-esm/socket/protocol.d.ts +13 -1
  47. package/dist-esm/socket/protocol.d.ts.map +1 -1
  48. package/dist-esm/socket/protocol.js.map +1 -1
  49. package/dist-esm/types/ai/index.d.ts +43 -1
  50. package/dist-esm/types/ai/index.d.ts.map +1 -1
  51. package/dist-esm/types/ai/index.js +1 -1
  52. package/dist-esm/types/ai/index.js.map +1 -1
  53. package/dist-esm/types/ai/quota-paywall.d.ts +8 -1
  54. package/dist-esm/types/ai/quota-paywall.d.ts.map +1 -1
  55. package/dist-esm/types/ai/quota-paywall.js +45 -14
  56. package/dist-esm/types/ai/quota-paywall.js.map +1 -1
  57. package/dist-esm/types/billing/billing.d.ts +5 -0
  58. package/dist-esm/types/billing/billing.d.ts.map +1 -1
  59. package/dist-esm/types/billing/billing.js +3 -0
  60. package/dist-esm/types/billing/billing.js.map +1 -1
  61. package/dist-esm/types/fact/fact.d.ts +17 -0
  62. package/dist-esm/types/fact/fact.d.ts.map +1 -1
  63. package/dist-esm/types/rbac/index.d.ts +5 -0
  64. package/dist-esm/types/rbac/index.d.ts.map +1 -1
  65. package/dist-esm/types/rbac/index.js +5 -0
  66. package/dist-esm/types/rbac/index.js.map +1 -1
  67. package/dist-esm/utils/attachment-upload.d.ts +122 -0
  68. package/dist-esm/utils/attachment-upload.d.ts.map +1 -0
  69. package/dist-esm/utils/attachment-upload.js +345 -0
  70. package/dist-esm/utils/attachment-upload.js.map +1 -0
  71. package/dist-esm/utils/attachment-upload.test.d.ts +2 -0
  72. package/dist-esm/utils/attachment-upload.test.d.ts.map +1 -0
  73. package/dist-esm/utils/attachment-upload.test.js +124 -0
  74. package/dist-esm/utils/attachment-upload.test.js.map +1 -0
  75. package/dist-esm/utils/github-workflow.d.ts +6 -0
  76. package/dist-esm/utils/github-workflow.d.ts.map +1 -0
  77. package/dist-esm/utils/github-workflow.js +65 -0
  78. package/dist-esm/utils/github-workflow.js.map +1 -0
  79. package/dist-esm/utils/index.d.ts +1 -0
  80. package/dist-esm/utils/index.d.ts.map +1 -1
  81. package/dist-esm/utils/index.js +1 -0
  82. package/dist-esm/utils/index.js.map +1 -1
  83. package/package.json +1 -1
  84. package/src/index.ts +1 -0
  85. package/src/socket/protocol.ts +21 -1
  86. package/src/types/ai/index.ts +47 -1
  87. package/src/types/ai/quota-paywall.ts +53 -15
  88. package/src/types/billing/billing.ts +5 -0
  89. package/src/types/fact/fact.ts +18 -0
  90. package/src/types/rbac/index.ts +5 -0
  91. package/src/utils/attachment-upload.test.ts +153 -0
  92. package/src/utils/attachment-upload.ts +546 -0
  93. package/src/utils/github-workflow.ts +68 -0
  94. package/src/utils/index.ts +1 -0
  95. package/dist/types/ai/credit-pricing.d.ts +0 -47
  96. package/dist/types/ai/credit-pricing.d.ts.map +0 -1
  97. package/dist/types/ai/credit-pricing.js +0 -66
  98. package/dist/types/ai/credit-pricing.js.map +0 -1
  99. package/dist-esm/types/ai/credit-pricing.d.ts +0 -47
  100. package/dist-esm/types/ai/credit-pricing.d.ts.map +0 -1
  101. package/dist-esm/types/ai/credit-pricing.js +0 -61
  102. package/dist-esm/types/ai/credit-pricing.js.map +0 -1
  103. package/src/types/ai/credit-pricing.ts +0 -87
@@ -0,0 +1,6 @@
1
+ export declare const DEFAULT_SUPERBLOCKS_DOMAIN = "app.superblocks.com";
2
+ export declare function isGitHubRemoteUrl(remoteUrl: string): boolean;
3
+ export declare function buildGithubSuperblocksSyncWorkflow(domain?: string): string;
4
+ export declare function getSuperblocksDomainFromBaseUrl(superblocksBaseUrl: string): string;
5
+ export declare function buildGithubSuperblocksSyncWorkflowFromBaseUrl(superblocksBaseUrl: string): string;
6
+ //# sourceMappingURL=github-workflow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-workflow.d.ts","sourceRoot":"","sources":["../../src/utils/github-workflow.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,0BAA0B,wBAAwB,CAAC;AAEhE,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAE5D;AAED,wBAAgB,kCAAkC,CAAC,MAAM,GAAE,MAAmC,GAAG,MAAM,CA4CtG;AAED,wBAAgB,+BAA+B,CAAC,kBAAkB,EAAE,MAAM,GAAG,MAAM,CAWlF;AAED,wBAAgB,6CAA6C,CAAC,kBAAkB,EAAE,MAAM,GAAG,MAAM,CAEhG"}
@@ -0,0 +1,65 @@
1
+ export const DEFAULT_SUPERBLOCKS_DOMAIN = 'app.superblocks.com';
2
+ export function isGitHubRemoteUrl(remoteUrl) {
3
+ return /(^git@github\.com:)|(^https?:\/\/github\.com\/)|(^ssh:\/\/git@github\.com\/)/.test(remoteUrl.trim());
4
+ }
5
+ export function buildGithubSuperblocksSyncWorkflow(domain = DEFAULT_SUPERBLOCKS_DOMAIN) {
6
+ return `name: Sync changes to Superblocks
7
+
8
+ on:
9
+ push:
10
+ branches:
11
+ - main
12
+ workflow_dispatch:
13
+
14
+ jobs:
15
+ superblocks-sync:
16
+ runs-on: ubuntu-latest
17
+ name: Sync to Superblocks
18
+ permissions:
19
+ contents: read
20
+ packages: read
21
+ steps:
22
+ - name: Checkout
23
+ uses: actions/checkout@v4
24
+ with:
25
+ fetch-depth: 0
26
+ persist-credentials: false
27
+
28
+ # Optional: needed only if the CLI version is private in GitHub Packages.
29
+ - name: Setup npm credentials
30
+ env:
31
+ NPM_AUTH_TOKEN: \${{ secrets.NPM_AUTH_TOKEN }}
32
+ run: |
33
+ if [ -n "$NPM_AUTH_TOKEN" ]; then
34
+ echo "@superblocksteam:registry=https://npm.pkg.github.com" >> .npmrc
35
+ echo "always-auth=true" >> .npmrc
36
+ echo "//npm.pkg.github.com/:_authToken=$NPM_AUTH_TOKEN" >> .npmrc
37
+ fi
38
+
39
+ - name: Sync
40
+ uses: superblocksteam/sync-action@v1
41
+ env:
42
+ NPM_CONFIG_USERCONFIG: \${{ github.workspace }}/.npmrc
43
+ with:
44
+ token: \${{ secrets.SUPERBLOCKS_TOKEN }}
45
+ domain: ${domain}
46
+ path: .
47
+ sha: \${{ github.sha }}
48
+ `;
49
+ }
50
+ export function getSuperblocksDomainFromBaseUrl(superblocksBaseUrl) {
51
+ const trimmed = superblocksBaseUrl.trim();
52
+ if (!trimmed) {
53
+ return DEFAULT_SUPERBLOCKS_DOMAIN;
54
+ }
55
+ try {
56
+ return new URL(trimmed).host || DEFAULT_SUPERBLOCKS_DOMAIN;
57
+ }
58
+ catch {
59
+ return DEFAULT_SUPERBLOCKS_DOMAIN;
60
+ }
61
+ }
62
+ export function buildGithubSuperblocksSyncWorkflowFromBaseUrl(superblocksBaseUrl) {
63
+ return buildGithubSuperblocksSyncWorkflow(getSuperblocksDomainFromBaseUrl(superblocksBaseUrl));
64
+ }
65
+ //# sourceMappingURL=github-workflow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-workflow.js","sourceRoot":"","sources":["../../src/utils/github-workflow.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,0BAA0B,GAAG,qBAAqB,CAAC;AAEhE,MAAM,UAAU,iBAAiB,CAAC,SAAiB;IACjD,OAAO,8EAA8E,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC;AAC/G,CAAC;AAED,MAAM,UAAU,kCAAkC,CAAC,SAAiB,0BAA0B;IAC5F,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oBAuCW,MAAM;;;CAGzB,CAAC;AACF,CAAC;AAED,MAAM,UAAU,+BAA+B,CAAC,kBAA0B;IACxE,MAAM,OAAO,GAAG,kBAAkB,CAAC,IAAI,EAAE,CAAC;IAC1C,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,0BAA0B,CAAC;IACpC,CAAC;IAED,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,IAAI,IAAI,0BAA0B,CAAC;IAC7D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,0BAA0B,CAAC;IACpC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,6CAA6C,CAAC,kBAA0B;IACtF,OAAO,kCAAkC,CAAC,+BAA+B,CAAC,kBAAkB,CAAC,CAAC,CAAC;AACjG,CAAC"}
@@ -1,4 +1,5 @@
1
1
  export * from './configuration.js';
2
+ export * from './attachment-upload.js';
2
3
  export * from './env.js';
3
4
  export * from './environment.js';
4
5
  export * from './string.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,UAAU,CAAC;AACzB,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,wBAAwB,CAAC;AAEvC,OAAO,MAAM,MAAM,2BAA2B,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC;AACvC,cAAc,UAAU,CAAC;AACzB,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,wBAAwB,CAAC;AAEvC,OAAO,MAAM,MAAM,2BAA2B,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,CAAC"}
@@ -1,4 +1,5 @@
1
1
  export * from './configuration.js';
2
+ export * from './attachment-upload.js';
2
3
  export * from './env.js';
3
4
  export * from './environment.js';
4
5
  export * from './string.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,UAAU,CAAC;AACzB,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,wBAAwB,CAAC;AAEvC,OAAO,MAAM,MAAM,2BAA2B,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC;AACvC,cAAc,UAAU,CAAC;AACzB,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,wBAAwB,CAAC;AAEvC,OAAO,MAAM,MAAM,2BAA2B,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@superblocksteam/shared",
3
- "version": "0.9567.3",
3
+ "version": "0.9568.0",
4
4
  "description": "Superblocks Shared Resources",
5
5
  "license": "Superblocks Community Software License",
6
6
  "repository": "https://github.com/superblocksteam/shared.git",
package/src/index.ts CHANGED
@@ -8,6 +8,7 @@ export * from './observability/index.js';
8
8
  export * from './constants.js';
9
9
  export * from './pageMigrations/index.js';
10
10
  export * from './utils/appDSL.js';
11
+ export * from './utils/github-workflow.js';
11
12
  export * from './pages/index.js';
12
13
  export * from './utils/page.js';
13
14
  export * from './utils/response-dto.js';
@@ -2,7 +2,17 @@
2
2
 
3
3
  import { DirectoryEntry, PutManyRequestPayload, PutManyResponsePayload } from '../dbfs/types.js';
4
4
  import { ApiToSign, ApiToVerify, AppToSign, AppToVerify, ApplicationSignatureTreeSigned, Signature } from '../signing/constants.js';
5
- import { AiChatMessageDto, AiChatMessagePayload, AiWorkflowEvent, FactListQuery, ListFactsResponse } from '../types/index.js';
5
+ import {
6
+ AiChatMessageDto,
7
+ AiChatMessagePayload,
8
+ AiWorkflowEvent,
9
+ AiResolveAttachmentLeasesRequest,
10
+ AiResolveAttachmentLeasesResponse,
11
+ FactCreate,
12
+ FactDto,
13
+ FactListQuery,
14
+ ListFactsResponse
15
+ } from '../types/index.js';
6
16
  import {
7
17
  ApplicationConfiguration,
8
18
  ApplicationPageClonePayload,
@@ -134,9 +144,15 @@ export interface ServerMethods {
134
144
  submit: ServerMethodSchema<AiWorkflowEvent, void>;
135
145
  };
136
146
  };
147
+ /**
148
+ * Resolve attachment leases and return fresh signed URLs.
149
+ * Called by ai-service to refresh attachment leases before sending to LLM.
150
+ */
151
+ resolveAttachmentLeases: ServerMethodSchema<AiResolveAttachmentLeasesRequest, AiResolveAttachmentLeasesResponse>;
137
152
  };
138
153
  fact: {
139
154
  list: ServerMethodSchema<FactListQuery, ListFactsResponse>;
155
+ create: ServerMethodSchema<FactCreate, FactDto>;
140
156
  };
141
157
  };
142
158
  v2: {
@@ -225,6 +241,10 @@ export interface ServerMethods {
225
241
  lock: {
226
242
  acquire: ServerMethodSchema<{ applicationId: string; branchName: string; sessionType: string }, GetAppBranchLockResponseBody>;
227
243
  release: ServerMethodSchema<{ applicationId: string; branchName: string; lockId: string }, void>;
244
+ transfer: ServerMethodSchema<
245
+ { applicationId: string; fromBranchName: string; toBranchName: string; lockId: string },
246
+ GetAppBranchLockResponseBody
247
+ >;
228
248
  grab: ServerMethodSchema<{ applicationId: string; branchName: string; lockId: string }, GetAppBranchLockResponseBody>;
229
249
  inspect: ServerMethodSchema<{ applicationId: string; branchName: string }, LockInfo>;
230
250
  forceTake: ServerMethodSchema<{ applicationId: string; branchName: string; sessionType: string }, GetAppBranchLockResponseBody>;
@@ -8,12 +8,59 @@ export type AiChatMessageDto = {
8
8
  authorName?: string | null;
9
9
  };
10
10
 
11
+ /**
12
+ * Attachment reference for lease resolution.
13
+ */
14
+ export interface AiAttachmentLeaseReference {
15
+ /** Stable URL of the attachment */
16
+ url: string;
17
+ /** Media/MIME type */
18
+ mediaType: string;
19
+ /** Original filename */
20
+ fileName: string;
21
+ /** Optional storage key in object storage */
22
+ storageKey?: string;
23
+ /** Scope of the attachment: org-wide or app-specific */
24
+ scopeType?: 'org' | 'app';
25
+ /** Application ID if scopeType is "app" */
26
+ applicationId?: string;
27
+ }
28
+
29
+ /**
30
+ * Resolved attachment with fresh signed URL.
31
+ */
32
+ export interface AiResolvedAttachment {
33
+ url: string;
34
+ signedUrl: string;
35
+ expiresAt: string;
36
+ mimeType: string;
37
+ }
38
+
39
+ /**
40
+ * Request to resolve attachment leases via RPC.
41
+ */
42
+ export interface AiResolveAttachmentLeasesRequest {
43
+ attachments: AiAttachmentLeaseReference[];
44
+ /** Scoped token for Bucketeer access (issued client-side). */
45
+ scopedToken: string;
46
+ /** Base URL of the Superblocks server, used to derive Bucketeer URL. */
47
+ superblocksBaseUrl: string;
48
+ }
49
+
50
+ /**
51
+ * Response with resolved attachment leases.
52
+ */
53
+ export interface AiResolveAttachmentLeasesResponse {
54
+ attachments: AiResolvedAttachment[];
55
+ }
56
+
11
57
  /**
12
58
  * System event trigger types for AI operations without prompt_id
13
59
  */
14
60
  export enum AiSystemTriggerType {
15
61
  APP_NAME = 'app_name',
16
62
  COMMIT_MESSAGE = 'commit_message',
63
+ DASHBOARD_CHAT = 'dashboard_chat',
17
64
  FACT_METADATA = 'fact_metadata'
18
65
  }
19
66
 
@@ -46,5 +93,4 @@ export interface AiWorkflowEvent {
46
93
  attributes: AiWorkflowAttributes;
47
94
  }
48
95
 
49
- export * from './credit-pricing.js';
50
96
  export * from './quota-paywall.js';
@@ -1,8 +1,19 @@
1
- export type AiQuotaPaywallReason = 'token_limit_exceeded' | 'trial_expired';
1
+ export type AiQuotaPaywallReason = 'credit_limit_exceeded' | 'no_seat_assigned' | 'token_limit_exceeded' | 'trial_expired';
2
2
 
3
3
  export const getAiQuotaPaywallReasonFromMessage = (message: string): AiQuotaPaywallReason | undefined => {
4
4
  const normalizedMessage = message.toLowerCase();
5
5
 
6
+ if (
7
+ normalizedMessage.includes('no_seat_assigned') ||
8
+ normalizedMessage.includes("doesn't have an ai builder license assigned") ||
9
+ normalizedMessage.includes("doesn't have an ai builder seat assigned") ||
10
+ normalizedMessage.includes('does not have an ai builder license assigned') ||
11
+ normalizedMessage.includes('does not have an ai builder seat assigned') ||
12
+ normalizedMessage.includes('no builder seat assigned')
13
+ ) {
14
+ return 'no_seat_assigned';
15
+ }
16
+
6
17
  if (
7
18
  normalizedMessage.includes('trial_expired') ||
8
19
  normalizedMessage.includes('ai trial period has ended') ||
@@ -11,7 +22,15 @@ export const getAiQuotaPaywallReasonFromMessage = (message: string): AiQuotaPayw
11
22
  return 'trial_expired';
12
23
  }
13
24
 
14
- if (normalizedMessage.includes('token_limit_exceeded') || normalizedMessage.includes('ai usage limit')) {
25
+ if (normalizedMessage.includes('token_limit_exceeded')) {
26
+ return 'token_limit_exceeded';
27
+ }
28
+
29
+ if (normalizedMessage.includes('credit_limit_exceeded') || normalizedMessage.includes('organization has reached its ai usage limit')) {
30
+ return 'credit_limit_exceeded';
31
+ }
32
+
33
+ if (normalizedMessage.includes('ai usage limit')) {
15
34
  return 'token_limit_exceeded';
16
35
  }
17
36
 
@@ -22,11 +41,22 @@ export const AI_QUOTA_PAYWALL_STRIPE_CHECKOUT_URL = 'https://buy.stripe.com/7sYe
22
41
 
23
42
  export const AI_QUOTA_PAYWALL_CONTACT_SALES_EMAIL = 'sales@superblockshq.com';
24
43
 
44
+ // TODO: Revisit this mailto template once backend enforcement is in place.
45
+ // Surface the specific reason (buy a seat vs. increase org credits) so the
46
+ // email to the admin is actionable rather than generic.
25
47
  export const AI_QUOTA_PAYWALL_CONTACT_SALES_MAILTO = 'mailto:sales@superblockshq.com?subject=Enterprise%20inquiry';
26
48
 
27
49
  export const AI_QUOTA_PAYWALL_REASON_CONFIG: Record<AiQuotaPaywallReason, { title: string; subtitle: string }> = {
50
+ credit_limit_exceeded: {
51
+ title: 'Your organization has reached its AI usage limit',
52
+ subtitle: 'Ask your admin to purchase additional AI credits to continue using Clark AI.'
53
+ },
54
+ no_seat_assigned: {
55
+ title: "You don't have an AI Builder license",
56
+ subtitle: 'Ask your admin to assign a license so you can continue building with Clark AI.'
57
+ },
28
58
  token_limit_exceeded: {
29
- title: 'Your free trial has ended',
59
+ title: 'Your organization has reached its AI usage limit',
30
60
  subtitle: 'Upgrade your plan to continue using Clark AI and other AI-powered features.'
31
61
  },
32
62
  trial_expired: {
@@ -35,28 +65,36 @@ export const AI_QUOTA_PAYWALL_REASON_CONFIG: Record<AiQuotaPaywallReason, { titl
35
65
  }
36
66
  };
37
67
 
68
+ export const AI_QUOTA_PAYWALL_PRICE_PER_SEAT_ANNUAL = 80;
69
+ export const AI_QUOTA_PAYWALL_PRICE_PER_SEAT_MONTHLY = 100;
70
+ export const AI_QUOTA_PAYWALL_CREDITS_PER_SEAT = 80;
71
+ export const AI_QUOTA_PAYWALL_APPS_PER_SEAT = 1;
72
+ export const AI_QUOTA_PAYWALL_MIN_SEATS = 1;
73
+ export const AI_QUOTA_PAYWALL_MAX_SEATS = 100;
74
+
38
75
  export const AI_QUOTA_PAYWALL_TEAMS_FEATURES = [
39
- '200 AI Credits per AI Builder / month',
76
+ '80 AI Credits per AI Builder / month',
77
+ 'Org-level credit top-ups, shared across team',
40
78
  'Clark AI agent for app building',
41
79
  'Clark User Memory',
42
80
  'AI Credit Packs add-on available'
43
81
  ];
44
82
 
45
83
  export const AI_QUOTA_PAYWALL_ENTERPRISE_FEATURES = [
46
- '200 AI Credits per AI Builder / month',
47
- 'Org-level credit top-ups, shared across team',
48
- 'Clark AI agent for app building',
49
- 'Clark Integration and Organization Memory'
84
+ 'Clark Integration and Organization memory',
85
+ 'Source Control via GitHub, GitLab, BitBucket, Azure DevOps',
86
+ 'Secrets Management',
87
+ 'Embedded Applications',
88
+ 'Custom app hosting'
50
89
  ];
51
90
 
52
- export const AI_QUOTA_PAYWALL_TEAMS_PLATFORM_FEATURES = ['Staging and production environments', '50+ integrations'];
91
+ export const AI_QUOTA_PAYWALL_TEAMS_PLATFORM_FEATURES = ['Staging and production environments', '1 hosted app included'];
53
92
 
54
93
  export const AI_QUOTA_PAYWALL_ENTERPRISE_PLATFORM_FEATURES = [
55
- 'Source Control via GitHub, GitLab, BitBucket, Azure DevOps',
56
- 'Custom app hosting with volume pricing',
94
+ 'Deploy in your VPC (Hybrid or Cloud Prem)',
57
95
  'SSO / SAML / OIDC',
58
- 'VPC Deployment',
59
- 'Audit Logs',
60
- 'Dedicated support',
61
- '99.9% SLA'
96
+ 'Role-based access control (RBAC)',
97
+ 'Audit Logs & Observability'
62
98
  ];
99
+
100
+ export const AI_QUOTA_PAYWALL_ENTERPRISE_SUPPORT_FEATURES = ['Dedicated account team', 'Enterprise Uptime and SLAs'];
@@ -1,7 +1,10 @@
1
1
  export enum BillingPlan {
2
2
  TRIAL = 'TRIAL',
3
+ /** @deprecated Legacy v1 plan — no longer issued to new organizations. */
3
4
  FREE = 'FREE',
5
+ /** @deprecated Legacy v1 plan — no longer issued to new organizations. */
4
6
  STARTER = 'STARTER',
7
+ /** @deprecated Legacy v1 plan — no longer issued to new organizations. */
5
8
  PRO = 'PRO',
6
9
  ENTERPRISE = 'ENTERPRISE',
7
10
  TEAMS = 'TEAMS',
@@ -13,4 +16,6 @@ export type Billing = {
13
16
  isExpired: boolean;
14
17
  expiryDate: Date;
15
18
  daysLeft: number;
19
+ billingInterval?: 'annual' | 'monthly' | null;
20
+ interval?: 'annual' | 'monthly' | null;
16
21
  };
@@ -54,3 +54,21 @@ export type FactListQuery = {
54
54
  export interface ListFactsResponse {
55
55
  facts: FactDto[];
56
56
  }
57
+
58
+ /**
59
+ * FactCreate for Socket RPC protocol.
60
+ *
61
+ * NOTE: This type is intentionally duplicated from @superblocksteam/schemas to avoid
62
+ * a circular dependency. The schemas package cannot depend on shared, and the socket
63
+ * protocol in shared needs this type. Keep in sync with packages/schemas/src/schemas/FactCreate.ts.
64
+ */
65
+ export type FactCreate = {
66
+ name: string;
67
+ description: string;
68
+ content: string;
69
+ metadata?: Record<string, unknown>;
70
+ entityScope: FactEntityScope;
71
+ entityId: string;
72
+ accessType?: FactAccessType;
73
+ createdSource?: FactCreatedSource;
74
+ };
@@ -39,6 +39,7 @@ export enum PermissionedEntities {
39
39
  APPLICATION_V2 = 'application_v2',
40
40
  API_V3 = 'api_v3',
41
41
  API = 'api',
42
+ ORGANIZATION = 'organization',
42
43
  /**
43
44
  * @deprecated The Datasource entity is being deprecated in favor of the Integration entity.
44
45
  */
@@ -135,6 +136,7 @@ export enum ResourceTypeEnum {
135
136
  APPLICATIONS = 'apps',
136
137
  APPLICATIONS_APIS = 'apps.apis',
137
138
  APPLICATIONS_PAGES = 'apps.pages',
139
+ BILLING = 'billing',
138
140
  FOLDERS = 'folders',
139
141
  GROUPS = 'groups',
140
142
  GROUPS_MEMBERS = 'groups.members',
@@ -187,6 +189,9 @@ export const ActionTypeByResourceType = {
187
189
  READ: ActionTypeEnum.READ,
188
190
  MANAGE: ActionTypeEnum.MANAGE
189
191
  },
192
+ BILLING: {
193
+ MANAGE: ActionTypeEnum.MANAGE
194
+ },
190
195
  WORKFLOWS: {
191
196
  DEPLOY: ActionTypeEnum.DEPLOY,
192
197
  DELETE: ActionTypeEnum.DELETE,
@@ -0,0 +1,153 @@
1
+ import { describe, expect, test } from 'vitest';
2
+ import {
3
+ buildAttachmentUploadMetadata,
4
+ buildAttachmentUploadFormData,
5
+ createUploadedAttachmentFromServer,
6
+ normalizeAttachmentMimeType,
7
+ toAttachmentUploadDescriptor
8
+ } from './attachment-upload.js';
9
+
10
+ describe('toAttachmentUploadDescriptor', () => {
11
+ test('preserves explicit names for non-File uploads', () => {
12
+ const descriptor = toAttachmentUploadDescriptor({
13
+ data: 'data:image/webp;base64,Zm9v',
14
+ mimeType: 'image/webp',
15
+ name: 'capture.webp'
16
+ });
17
+
18
+ expect(descriptor).toMatchObject({
19
+ fileName: 'capture.webp',
20
+ mimeType: 'image/webp'
21
+ });
22
+ });
23
+
24
+ test('falls back to mime-derived filenames for unnamed non-File uploads', () => {
25
+ const descriptor = toAttachmentUploadDescriptor({
26
+ data: 'data:application/pdf;base64,Zm9v',
27
+ mimeType: 'application/pdf'
28
+ });
29
+
30
+ expect(descriptor).toMatchObject({
31
+ fileName: 'attachment.pdf',
32
+ mimeType: 'application/pdf'
33
+ });
34
+ });
35
+
36
+ test('normalizes browser ts mime types from filename when they are not text-like', () => {
37
+ const descriptor = toAttachmentUploadDescriptor(new File(['const answer = 42;'], 'main.ts', { type: 'video/mp2t' }));
38
+
39
+ expect(descriptor).toMatchObject({
40
+ fileName: 'main.ts',
41
+ mimeType: 'text/plain'
42
+ });
43
+ });
44
+ });
45
+
46
+ describe('buildAttachmentUploadFormData', () => {
47
+ test('appends the normalized filename to form data', () => {
48
+ const descriptor = toAttachmentUploadDescriptor({
49
+ data: 'data:image/webp;base64,Zm9v',
50
+ mimeType: 'image/webp',
51
+ name: 'capture.webp'
52
+ });
53
+
54
+ expect(descriptor).not.toBeNull();
55
+
56
+ const formData = buildAttachmentUploadFormData({
57
+ descriptor: descriptor!,
58
+ prefix: 'captured-screenshot'
59
+ });
60
+
61
+ const uploadedFile = formData.get('file');
62
+ expect(uploadedFile).toBeInstanceOf(File);
63
+ expect((uploadedFile as File).name).toBe('capture.webp');
64
+ expect(formData.get('prefix')).toBe('captured-screenshot');
65
+ });
66
+
67
+ test('appends serialized metadata when provided', () => {
68
+ const descriptor = toAttachmentUploadDescriptor({
69
+ data: 'data:text/plain;base64,Zm9v',
70
+ mimeType: 'text/plain',
71
+ name: 'notes.txt'
72
+ });
73
+
74
+ expect(descriptor).not.toBeNull();
75
+
76
+ const formData = buildAttachmentUploadFormData({
77
+ descriptor: descriptor!,
78
+ prefix: 'message-attachment',
79
+ metadata: buildAttachmentUploadMetadata({
80
+ label: 'Error screenshot',
81
+ insight: 'Shows a missing submit button'
82
+ })
83
+ });
84
+
85
+ expect(formData.get('metadata')).toBe(
86
+ JSON.stringify({
87
+ label: 'Error screenshot',
88
+ insight: 'Shows a missing submit button'
89
+ })
90
+ );
91
+ });
92
+ });
93
+
94
+ describe('createUploadedAttachmentFromServer', () => {
95
+ test('falls back to the original mime type when the server omits one', () => {
96
+ const uploadedAttachment = createUploadedAttachmentFromServer({
97
+ response: {
98
+ attachmentId: '123',
99
+ fileName: '',
100
+ contentUrl: '/api/v1/files/content/123',
101
+ mimeType: null,
102
+ purpose: 'attachment',
103
+ scopeType: 'app',
104
+ signedUrl: 'https://signed.example/123',
105
+ signedUrlExpiresAt: '2026-03-06T00:00:00.000Z',
106
+ storageKey: 'message-attachment/123'
107
+ },
108
+ fallbackFileName: 'spec.pdf',
109
+ fallbackMimeType: 'application/pdf',
110
+ label: 'Spec',
111
+ insight: 'Reference doc'
112
+ });
113
+
114
+ expect(uploadedAttachment).toEqual({
115
+ type: 'uploaded',
116
+ url: '/api/v1/files/content/123',
117
+ signedUrl: 'https://signed.example/123',
118
+ mediaType: 'application/pdf',
119
+ fileName: 'spec.pdf',
120
+ label: 'Spec',
121
+ insight: 'Reference doc',
122
+ storageKey: 'message-attachment/123',
123
+ signedUrlExpiresAt: '2026-03-06T00:00:00.000Z',
124
+ scopeType: 'app',
125
+ applicationId: undefined
126
+ });
127
+ });
128
+ });
129
+
130
+ describe('buildAttachmentUploadMetadata', () => {
131
+ test('normalizes non-empty label and insight fields', () => {
132
+ expect(
133
+ buildAttachmentUploadMetadata({
134
+ label: 'Error screenshot',
135
+ insight: 'Shows a missing submit button'
136
+ })
137
+ ).toEqual({
138
+ label: 'Error screenshot',
139
+ insight: 'Shows a missing submit button'
140
+ });
141
+ });
142
+ });
143
+
144
+ describe('normalizeAttachmentMimeType', () => {
145
+ test('prefers text-safe mime types for text-like filenames', () => {
146
+ expect(
147
+ normalizeAttachmentMimeType({
148
+ fileName: 'component.tsx',
149
+ mimeType: 'video/mp2t'
150
+ })
151
+ ).toBe('text/plain');
152
+ });
153
+ });