@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,546 @@
1
+ const DEFAULT_MIME_TYPE = 'application/octet-stream';
2
+
3
+ const TEXT_ATTACHMENT_MIME_TYPES = {
4
+ csv: 'text/csv',
5
+ css: 'text/css',
6
+ json: 'application/json',
7
+ txt: 'text/plain',
8
+ yaml: 'text/yaml'
9
+ } as const;
10
+
11
+ export const TEXT_LIKE_FILE_EXTENSIONS = [
12
+ '.txt',
13
+ '.text',
14
+ '.log',
15
+ '.md',
16
+ '.markdown',
17
+ '.csv',
18
+ '.tsv',
19
+ '.json',
20
+ '.jsonl',
21
+ '.ndjson',
22
+ '.xml',
23
+ '.yaml',
24
+ '.yml',
25
+ '.toml',
26
+ '.ini',
27
+ '.cfg',
28
+ '.conf',
29
+ '.env',
30
+ '.sh',
31
+ '.bash',
32
+ '.zsh',
33
+ '.js',
34
+ '.jsx',
35
+ '.ts',
36
+ '.tsx',
37
+ '.mts',
38
+ '.cts',
39
+ '.py',
40
+ '.go',
41
+ '.java',
42
+ '.rb',
43
+ '.php',
44
+ '.sql',
45
+ '.css',
46
+ '.scss',
47
+ '.html',
48
+ '.htm'
49
+ ] as const;
50
+
51
+ const MIME_TYPE_FILE_EXTENSIONS: Record<string, string> = {
52
+ 'application/json': '.json',
53
+ 'application/pdf': '.pdf',
54
+ 'application/x-yaml': '.yaml',
55
+ 'application/yaml': '.yaml',
56
+ 'image/gif': '.gif',
57
+ 'image/jpeg': '.jpg',
58
+ 'image/jpg': '.jpg',
59
+ 'image/png': '.png',
60
+ 'image/webp': '.webp',
61
+ 'text/css': '.css',
62
+ 'text/csv': '.csv',
63
+ 'text/plain': '.txt',
64
+ 'text/yaml': '.yaml'
65
+ };
66
+
67
+ type UploadAttachmentDataLike = {
68
+ data: string;
69
+ mimeType?: string | null;
70
+ name?: string;
71
+ fileName?: string;
72
+ };
73
+
74
+ type UploadImageAttachmentLike = {
75
+ type: 'image';
76
+ image: string;
77
+ fileName: string;
78
+ };
79
+
80
+ type UploadPdfAttachmentLike = {
81
+ type: 'pdf';
82
+ data: string;
83
+ fileName: string;
84
+ };
85
+
86
+ type UploadTextAttachmentLike = {
87
+ type: 'csv' | 'css' | 'json' | 'txt' | 'yaml';
88
+ content: string;
89
+ fileName: string;
90
+ mimeType?: string | null;
91
+ };
92
+
93
+ type UploadUnsupportedAttachmentLike = {
94
+ type: 'uploaded';
95
+ };
96
+
97
+ export type AttachmentUploadSourceLike =
98
+ | Blob
99
+ | File
100
+ | UploadAttachmentDataLike
101
+ | UploadImageAttachmentLike
102
+ | UploadPdfAttachmentLike
103
+ | UploadTextAttachmentLike
104
+ | UploadUnsupportedAttachmentLike;
105
+
106
+ export interface AttachmentUploadDescriptor {
107
+ blob: Blob;
108
+ fileName: string;
109
+ mimeType: string;
110
+ }
111
+
112
+ export type AttachmentUploadScopeType = 'app' | 'org';
113
+ export type AttachmentUploadPurpose = 'attachment' | 'context';
114
+
115
+ export interface AppContextUploadDto {
116
+ applicationId: string;
117
+ branchName?: string;
118
+ commitId?: string;
119
+ purpose: 'context';
120
+ scopeType: 'app';
121
+ uploaded: true;
122
+ }
123
+
124
+ export interface AttachmentUploadDto {
125
+ applicationId?: string;
126
+ attachmentId: string;
127
+ contentUrl: string;
128
+ fileName: string;
129
+ mimeType: string | null;
130
+ purpose: AttachmentUploadPurpose;
131
+ scopeType: AttachmentUploadScopeType;
132
+ signedUrl: string;
133
+ signedUrlExpiresAt: string;
134
+ storageKey: string;
135
+ }
136
+
137
+ export type UploadBodyDto = AppContextUploadDto | AttachmentUploadDto;
138
+
139
+ // Backwards-compatible alias for existing attachment-only client consumers.
140
+ export type AttachmentUploadServerDto = AttachmentUploadDto;
141
+
142
+ export interface UploadedAttachmentLike {
143
+ type: 'uploaded';
144
+ url: string;
145
+ signedUrl: string;
146
+ mediaType: string;
147
+ fileName: string;
148
+ label?: string;
149
+ insight?: string;
150
+ storageKey?: string;
151
+ signedUrlExpiresAt: string;
152
+ scopeType?: AttachmentUploadScopeType;
153
+ applicationId?: string;
154
+ }
155
+
156
+ export interface AttachmentUploadMetadata {
157
+ label?: string;
158
+ insight?: string;
159
+ }
160
+
161
+ export function buildAttachmentUploadMetadata(params: AttachmentUploadMetadata): AttachmentUploadMetadata | undefined {
162
+ const label = normalizeNonEmptyString(params.label);
163
+ const insight = normalizeNonEmptyString(params.insight);
164
+
165
+ if (!label && !insight) {
166
+ return undefined;
167
+ }
168
+
169
+ return {
170
+ ...(label ? { label } : {}),
171
+ ...(insight ? { insight } : {})
172
+ };
173
+ }
174
+
175
+ function normalizeNonEmptyString(value: unknown): string | undefined {
176
+ if (typeof value !== 'string') {
177
+ return undefined;
178
+ }
179
+
180
+ const trimmed = value.trim();
181
+ return trimmed.length > 0 ? trimmed : undefined;
182
+ }
183
+
184
+ export function isTextLikeMimeType(mimeType: string): boolean {
185
+ const normalizedMimeType = mimeType.toLowerCase();
186
+ return (
187
+ normalizedMimeType.startsWith('text/') ||
188
+ normalizedMimeType.includes('json') ||
189
+ normalizedMimeType.includes('xml') ||
190
+ normalizedMimeType.includes('yaml') ||
191
+ normalizedMimeType.includes('toml') ||
192
+ normalizedMimeType.includes('javascript') ||
193
+ normalizedMimeType.includes('typescript') ||
194
+ normalizedMimeType.includes('ecmascript') ||
195
+ normalizedMimeType.includes('markdown') ||
196
+ normalizedMimeType.includes('shellscript') ||
197
+ normalizedMimeType.includes('python') ||
198
+ normalizedMimeType.includes('ruby') ||
199
+ normalizedMimeType.includes('php') ||
200
+ normalizedMimeType.includes('sql')
201
+ );
202
+ }
203
+
204
+ export function getTextAttachmentType(params: {
205
+ fileName?: string;
206
+ mimeType?: string | null;
207
+ }): 'csv' | 'css' | 'json' | 'txt' | 'yaml' | null {
208
+ const normalizedFileName = normalizeNonEmptyString(params.fileName)?.toLowerCase();
209
+ const normalizedMimeType = normalizeNonEmptyString(params.mimeType)?.toLowerCase();
210
+
211
+ if (normalizedMimeType === 'text/csv' || normalizedFileName?.endsWith('.csv')) {
212
+ return 'csv';
213
+ }
214
+ if (normalizedMimeType === 'application/json' || normalizedFileName?.endsWith('.json')) {
215
+ return 'json';
216
+ }
217
+ if (
218
+ normalizedMimeType === 'text/yaml' ||
219
+ normalizedMimeType === 'application/x-yaml' ||
220
+ normalizedMimeType === 'application/yaml' ||
221
+ normalizedFileName?.endsWith('.yaml') ||
222
+ normalizedFileName?.endsWith('.yml')
223
+ ) {
224
+ return 'yaml';
225
+ }
226
+ if (normalizedMimeType === 'text/css' || normalizedFileName?.endsWith('.css')) {
227
+ return 'css';
228
+ }
229
+ if (
230
+ normalizedMimeType === 'text/plain' ||
231
+ (normalizedMimeType ? isTextLikeMimeType(normalizedMimeType) : false) ||
232
+ (normalizedFileName ? TEXT_LIKE_FILE_EXTENSIONS.some((extension) => normalizedFileName.endsWith(extension)) : false)
233
+ ) {
234
+ return 'txt';
235
+ }
236
+
237
+ return null;
238
+ }
239
+
240
+ function getTextLikeMimeTypeFromFileName(fileName: string | undefined): string | undefined {
241
+ const textAttachmentType = getTextAttachmentType({ fileName });
242
+ return textAttachmentType ? TEXT_ATTACHMENT_MIME_TYPES[textAttachmentType] : undefined;
243
+ }
244
+
245
+ export function normalizeAttachmentMimeType(params: {
246
+ fileName?: string;
247
+ mimeType?: string | null;
248
+ fallbackMimeType?: string | null;
249
+ }): string {
250
+ const normalizedMimeType =
251
+ normalizeNonEmptyString(params.mimeType)?.toLowerCase() ?? normalizeNonEmptyString(params.fallbackMimeType)?.toLowerCase();
252
+ const textLikeMimeType = getTextLikeMimeTypeFromFileName(params.fileName);
253
+
254
+ if (!textLikeMimeType) {
255
+ return normalizedMimeType ?? DEFAULT_MIME_TYPE;
256
+ }
257
+
258
+ if (!normalizedMimeType) {
259
+ return textLikeMimeType;
260
+ }
261
+
262
+ return isTextLikeMimeType(normalizedMimeType) ? normalizedMimeType : textLikeMimeType;
263
+ }
264
+
265
+ function getDefaultFileName(mimeType: string): string {
266
+ const normalizedMimeType = mimeType.toLowerCase();
267
+ const knownExtension = MIME_TYPE_FILE_EXTENSIONS[normalizedMimeType];
268
+ if (knownExtension) {
269
+ return `attachment${knownExtension}`;
270
+ }
271
+
272
+ if (normalizedMimeType.startsWith('image/')) {
273
+ const subtype = normalizedMimeType.slice('image/'.length).split(/[+;]/)[0]?.trim();
274
+ if (subtype) {
275
+ return `attachment.${subtype}`;
276
+ }
277
+ }
278
+
279
+ return 'attachment';
280
+ }
281
+
282
+ function resolveFileName(params: { fileName: unknown; fallbackFileName?: string; mimeType: string }): string {
283
+ return (
284
+ normalizeNonEmptyString(params.fileName) ?? normalizeNonEmptyString(params.fallbackFileName) ?? getDefaultFileName(params.mimeType)
285
+ );
286
+ }
287
+
288
+ function decodeBase64(base64Data: string): Uint8Array {
289
+ return Uint8Array.from(atob(base64Data), (char) => char.charCodeAt(0));
290
+ }
291
+
292
+ function parseDataUrl(data: string): { base64Data: string; mimeType?: string } | null {
293
+ const match = data.match(/^data:([^;,]+)?(?:;base64)?,(.*)$/s);
294
+ if (!match) {
295
+ return null;
296
+ }
297
+
298
+ return {
299
+ mimeType: normalizeNonEmptyString(match[1]),
300
+ base64Data: match[2] ?? ''
301
+ };
302
+ }
303
+
304
+ function isAttachmentDataLike(source: AttachmentUploadSourceLike): source is UploadAttachmentDataLike {
305
+ return 'data' in source && typeof source.data === 'string' && !('type' in source);
306
+ }
307
+
308
+ export function toAttachmentUploadDescriptor(
309
+ source: AttachmentUploadSourceLike,
310
+ options?: {
311
+ fallbackFileName?: string;
312
+ fallbackMimeType?: string;
313
+ }
314
+ ): AttachmentUploadDescriptor | null {
315
+ if (source instanceof File) {
316
+ const mimeType = normalizeAttachmentMimeType({
317
+ fileName: source.name,
318
+ mimeType: source.type,
319
+ fallbackMimeType: options?.fallbackMimeType
320
+ });
321
+
322
+ return {
323
+ blob: source,
324
+ fileName: resolveFileName({
325
+ fileName: source.name,
326
+ fallbackFileName: options?.fallbackFileName,
327
+ mimeType
328
+ }),
329
+ mimeType
330
+ };
331
+ }
332
+
333
+ if (source instanceof Blob) {
334
+ const sourceWithOptionalName = source as Blob & { name?: unknown };
335
+ const mimeType = normalizeAttachmentMimeType({
336
+ fileName: typeof sourceWithOptionalName.name === 'string' ? sourceWithOptionalName.name : options?.fallbackFileName,
337
+ mimeType: source.type,
338
+ fallbackMimeType: options?.fallbackMimeType
339
+ });
340
+
341
+ return {
342
+ blob: source,
343
+ fileName: resolveFileName({
344
+ fileName: sourceWithOptionalName.name,
345
+ fallbackFileName: options?.fallbackFileName,
346
+ mimeType
347
+ }),
348
+ mimeType
349
+ };
350
+ }
351
+
352
+ if (isAttachmentDataLike(source)) {
353
+ const parsedDataUrl = parseDataUrl(source.data);
354
+ const mimeType = normalizeAttachmentMimeType({
355
+ fileName: typeof source.name === 'string' ? source.name : source.fileName,
356
+ mimeType: parsedDataUrl?.mimeType ?? source.mimeType,
357
+ fallbackMimeType: options?.fallbackMimeType
358
+ });
359
+ const base64Data = parsedDataUrl?.base64Data ?? source.data;
360
+
361
+ return {
362
+ blob: new Blob([decodeBase64(base64Data)], { type: mimeType }),
363
+ fileName: resolveFileName({
364
+ fileName: source.name ?? source.fileName,
365
+ fallbackFileName: options?.fallbackFileName,
366
+ mimeType
367
+ }),
368
+ mimeType
369
+ };
370
+ }
371
+
372
+ if (source.type === 'image') {
373
+ const parsedDataUrl = parseDataUrl(source.image);
374
+ if (!parsedDataUrl?.mimeType) {
375
+ return null;
376
+ }
377
+
378
+ return {
379
+ blob: new Blob([decodeBase64(parsedDataUrl.base64Data)], {
380
+ type: parsedDataUrl.mimeType
381
+ }),
382
+ fileName: resolveFileName({
383
+ fileName: source.fileName,
384
+ fallbackFileName: options?.fallbackFileName,
385
+ mimeType: parsedDataUrl.mimeType
386
+ }),
387
+ mimeType: parsedDataUrl.mimeType
388
+ };
389
+ }
390
+
391
+ if (source.type === 'pdf') {
392
+ const parsedDataUrl = parseDataUrl(source.data);
393
+ const mimeType = parsedDataUrl?.mimeType ?? normalizeNonEmptyString(options?.fallbackMimeType) ?? 'application/pdf';
394
+
395
+ return {
396
+ blob: new Blob([decodeBase64(parsedDataUrl?.base64Data ?? source.data)], {
397
+ type: mimeType
398
+ }),
399
+ fileName: resolveFileName({
400
+ fileName: source.fileName,
401
+ fallbackFileName: options?.fallbackFileName,
402
+ mimeType
403
+ }),
404
+ mimeType
405
+ };
406
+ }
407
+
408
+ if (source.type === 'csv' || source.type === 'css' || source.type === 'json' || source.type === 'txt' || source.type === 'yaml') {
409
+ const mimeType = normalizeAttachmentMimeType({
410
+ fileName: source.fileName,
411
+ mimeType: source.mimeType,
412
+ fallbackMimeType: options?.fallbackMimeType ?? TEXT_ATTACHMENT_MIME_TYPES[source.type]
413
+ });
414
+
415
+ return {
416
+ blob: new Blob([source.content], { type: mimeType }),
417
+ fileName: resolveFileName({
418
+ fileName: source.fileName,
419
+ fallbackFileName: options?.fallbackFileName,
420
+ mimeType
421
+ }),
422
+ mimeType
423
+ };
424
+ }
425
+
426
+ return null;
427
+ }
428
+
429
+ export function buildAttachmentUploadFormData(params: {
430
+ descriptor: AttachmentUploadDescriptor;
431
+ prefix: string;
432
+ fieldName?: string;
433
+ branchId?: string;
434
+ commitId?: string;
435
+ metadata?: AttachmentUploadMetadata;
436
+ }): FormData {
437
+ const formData = new FormData();
438
+ formData.append(params.fieldName ?? 'file', params.descriptor.blob, params.descriptor.fileName);
439
+ formData.append('prefix', params.prefix);
440
+
441
+ if (params.branchId) {
442
+ formData.append('branchId', params.branchId);
443
+ }
444
+
445
+ if (params.commitId) {
446
+ formData.append('commitId', params.commitId);
447
+ }
448
+
449
+ const metadata = buildAttachmentUploadMetadata(params.metadata ?? {});
450
+ if (metadata) {
451
+ formData.append('metadata', JSON.stringify(metadata));
452
+ }
453
+
454
+ return formData;
455
+ }
456
+
457
+ export function createUploadedAttachmentFromServer(params: {
458
+ response: AttachmentUploadServerDto;
459
+ fallbackFileName: string;
460
+ fallbackMimeType: string;
461
+ label?: string;
462
+ insight?: string;
463
+ }): UploadedAttachmentLike {
464
+ const mediaType = normalizeNonEmptyString(params.response.mimeType) ?? params.fallbackMimeType;
465
+
466
+ return {
467
+ type: 'uploaded',
468
+ url: normalizeNonEmptyString(params.response.contentUrl) ?? params.response.signedUrl,
469
+ signedUrl: params.response.signedUrl,
470
+ mediaType,
471
+ fileName: normalizeNonEmptyString(params.response.fileName) ?? params.fallbackFileName,
472
+ label: params.label,
473
+ insight: params.insight,
474
+ storageKey: normalizeNonEmptyString(params.response.storageKey),
475
+ signedUrlExpiresAt: params.response.signedUrlExpiresAt,
476
+ scopeType: params.response.scopeType,
477
+ applicationId: normalizeNonEmptyString(params.response.applicationId)
478
+ };
479
+ }
480
+
481
+ /**
482
+ * Shared transport for POST /api/v1/files/upload. Builds form-data, URL with query params,
483
+ * fetches, and returns the raw server DTO. Callers map to their own types (e.g. Attachment,
484
+ * AttachmentUploadResult).
485
+ */
486
+ export async function uploadAttachmentToServerApi(params: {
487
+ baseUrl: string;
488
+ authorization: string;
489
+ descriptor: AttachmentUploadDescriptor;
490
+ scopeType: AttachmentUploadScopeType;
491
+ purpose: AttachmentUploadPurpose;
492
+ applicationId?: string;
493
+ branchName?: string;
494
+ commitId?: string;
495
+ prefix?: string;
496
+ metadata?: AttachmentUploadMetadata;
497
+ }): Promise<AttachmentUploadServerDto> {
498
+ const {
499
+ baseUrl,
500
+ authorization,
501
+ descriptor,
502
+ scopeType,
503
+ purpose,
504
+ applicationId,
505
+ branchName,
506
+ commitId,
507
+ prefix = 'message-attachment',
508
+ metadata
509
+ } = params;
510
+
511
+ const formData = buildAttachmentUploadFormData({
512
+ descriptor,
513
+ prefix,
514
+ metadata,
515
+ ...(branchName != null && { branchId: branchName }),
516
+ ...(commitId != null && { commitId })
517
+ });
518
+
519
+ const uploadUrl = new URL('/api/v1/files/upload', baseUrl);
520
+ uploadUrl.searchParams.set('scopeType', scopeType);
521
+ uploadUrl.searchParams.set('purpose', purpose);
522
+ if (applicationId != null && applicationId.trim() !== '') {
523
+ uploadUrl.searchParams.set('applicationId', applicationId);
524
+ }
525
+ if (branchName != null && branchName.trim() !== '') {
526
+ uploadUrl.searchParams.set('branchName', branchName);
527
+ }
528
+ if (commitId != null && commitId.trim() !== '') {
529
+ uploadUrl.searchParams.set('commitId', commitId);
530
+ }
531
+
532
+ const response = await fetch(uploadUrl.toString(), {
533
+ method: 'POST',
534
+ body: formData,
535
+ headers: {
536
+ Authorization: authorization
537
+ }
538
+ });
539
+
540
+ if (!response.ok) {
541
+ const errorText = await response.text().catch(() => '');
542
+ throw new Error(`Attachment upload failed: status=${response.status}${errorText ? `, body=${errorText}` : ''}`);
543
+ }
544
+
545
+ return (await response.json()) as AttachmentUploadServerDto;
546
+ }
@@ -0,0 +1,68 @@
1
+ export const DEFAULT_SUPERBLOCKS_DOMAIN = 'app.superblocks.com';
2
+
3
+ export function isGitHubRemoteUrl(remoteUrl: string): boolean {
4
+ return /(^git@github\.com:)|(^https?:\/\/github\.com\/)|(^ssh:\/\/git@github\.com\/)/.test(remoteUrl.trim());
5
+ }
6
+
7
+ export function buildGithubSuperblocksSyncWorkflow(domain: string = DEFAULT_SUPERBLOCKS_DOMAIN): string {
8
+ return `name: Sync changes to Superblocks
9
+
10
+ on:
11
+ push:
12
+ branches:
13
+ - main
14
+ workflow_dispatch:
15
+
16
+ jobs:
17
+ superblocks-sync:
18
+ runs-on: ubuntu-latest
19
+ name: Sync to Superblocks
20
+ permissions:
21
+ contents: read
22
+ packages: read
23
+ steps:
24
+ - name: Checkout
25
+ uses: actions/checkout@v4
26
+ with:
27
+ fetch-depth: 0
28
+ persist-credentials: false
29
+
30
+ # Optional: needed only if the CLI version is private in GitHub Packages.
31
+ - name: Setup npm credentials
32
+ env:
33
+ NPM_AUTH_TOKEN: \${{ secrets.NPM_AUTH_TOKEN }}
34
+ run: |
35
+ if [ -n "$NPM_AUTH_TOKEN" ]; then
36
+ echo "@superblocksteam:registry=https://npm.pkg.github.com" >> .npmrc
37
+ echo "always-auth=true" >> .npmrc
38
+ echo "//npm.pkg.github.com/:_authToken=$NPM_AUTH_TOKEN" >> .npmrc
39
+ fi
40
+
41
+ - name: Sync
42
+ uses: superblocksteam/sync-action@v1
43
+ env:
44
+ NPM_CONFIG_USERCONFIG: \${{ github.workspace }}/.npmrc
45
+ with:
46
+ token: \${{ secrets.SUPERBLOCKS_TOKEN }}
47
+ domain: ${domain}
48
+ path: .
49
+ sha: \${{ github.sha }}
50
+ `;
51
+ }
52
+
53
+ export function getSuperblocksDomainFromBaseUrl(superblocksBaseUrl: string): string {
54
+ const trimmed = superblocksBaseUrl.trim();
55
+ if (!trimmed) {
56
+ return DEFAULT_SUPERBLOCKS_DOMAIN;
57
+ }
58
+
59
+ try {
60
+ return new URL(trimmed).host || DEFAULT_SUPERBLOCKS_DOMAIN;
61
+ } catch {
62
+ return DEFAULT_SUPERBLOCKS_DOMAIN;
63
+ }
64
+ }
65
+
66
+ export function buildGithubSuperblocksSyncWorkflowFromBaseUrl(superblocksBaseUrl: string): string {
67
+ return buildGithubSuperblocksSyncWorkflow(getSuperblocksDomainFromBaseUrl(superblocksBaseUrl));
68
+ }
@@ -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,47 +0,0 @@
1
- export type AiCreditTokenRates = {
2
- inputTokenCost: number;
3
- outputTokenCost: number;
4
- cacheReadTokenCost: number;
5
- cacheWriteTokenCost: number;
6
- };
7
- export declare const AI_CREDIT_COST_MULTIPLIER = 2;
8
- export declare const AI_MODEL_CREDIT_RATES: {
9
- readonly 'claude-haiku-4-5': {
10
- readonly inputTokenCost: 0.000001;
11
- readonly outputTokenCost: 0.000005;
12
- readonly cacheReadTokenCost: 1e-7;
13
- readonly cacheWriteTokenCost: 0.00000125;
14
- };
15
- readonly 'claude-opus-4-5': {
16
- readonly inputTokenCost: 0.000005;
17
- readonly outputTokenCost: 0.000025;
18
- readonly cacheReadTokenCost: 5e-7;
19
- readonly cacheWriteTokenCost: 0.00000625;
20
- };
21
- readonly 'claude-opus-4-6': {
22
- readonly inputTokenCost: 0.000005;
23
- readonly outputTokenCost: 0.000025;
24
- readonly cacheReadTokenCost: 5e-7;
25
- readonly cacheWriteTokenCost: 0.00000625;
26
- };
27
- readonly 'claude-sonnet-4-5': {
28
- readonly inputTokenCost: 0.000003;
29
- readonly outputTokenCost: 0.000015;
30
- readonly cacheReadTokenCost: 3e-7;
31
- readonly cacheWriteTokenCost: 0.00000375;
32
- };
33
- };
34
- export declare const AI_FALLBACK_CREDIT_RATES: AiCreditTokenRates;
35
- export declare const resolveAiCreditRates: (model: string) => {
36
- modelKey: string;
37
- rates: AiCreditTokenRates;
38
- usedFallback: boolean;
39
- };
40
- export declare const computeAiCreditUsage: (params: {
41
- inputTokens: number;
42
- outputTokens: number;
43
- cachedReadTokens: number;
44
- cachedWriteTokens: number;
45
- rates: AiCreditTokenRates;
46
- }) => number;
47
- //# sourceMappingURL=credit-pricing.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"credit-pricing.d.ts","sourceRoot":"","sources":["../../../src/types/ai/credit-pricing.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,kBAAkB,GAAG;IAC/B,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,mBAAmB,EAAE,MAAM,CAAC;CAC7B,CAAC;AAEF,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAE3C,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;CAyBqB,CAAC;AAExD,eAAO,MAAM,wBAAwB,EAAE,kBAA+D,CAAC;AAcvG,eAAO,MAAM,oBAAoB,GAC/B,OAAO,MAAM,KACZ;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,kBAAkB,CAAC;IAC1B,YAAY,EAAE,OAAO,CAAC;CAgBvB,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAI,QAAQ;IAC3C,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,KAAK,EAAE,kBAAkB,CAAC;CAC3B,KAAG,MAOH,CAAC"}