@agent-native/core 0.128.0 → 0.128.2
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/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +27 -0
- package/corpus/core/package.json +3 -1
- package/corpus/core/src/server/builder-design-systems.ts +299 -82
- package/corpus/core/src/server/email-template.ts +33 -0
- package/corpus/core/src/server/index.ts +8 -0
- package/corpus/templates/analytics/_gitignore +2 -0
- package/corpus/templates/assets/_gitignore +2 -0
- package/corpus/templates/calendar/_gitignore +2 -0
- package/corpus/templates/chat/_gitignore +2 -0
- package/corpus/templates/clips/AGENTS.md +6 -1
- package/corpus/templates/clips/_gitignore +2 -0
- package/corpus/templates/clips/actions/complete-transactional-email-summary.ts +56 -0
- package/corpus/templates/clips/actions/import-loom-recording.ts +258 -76
- package/corpus/templates/clips/actions/lib/direct-video.ts +96 -0
- package/corpus/templates/clips/actions/lib/loom-import-job.ts +53 -2
- package/corpus/templates/clips/actions/lib/loom-video.ts +2 -62
- package/corpus/templates/clips/actions/lib/video-download-limits.ts +62 -0
- package/corpus/templates/clips/actions/list-transactional-email-ai-requests.ts +249 -0
- package/corpus/templates/clips/app/components/library/library-grid.tsx +65 -0
- package/corpus/templates/clips/app/components/library/library-layout.tsx +55 -0
- package/corpus/templates/clips/app/components/recorder/pre-record-panel.tsx +28 -126
- package/corpus/templates/clips/app/global.css +23 -0
- package/corpus/templates/clips/app/hooks/use-transactional-email-bridge.ts +98 -0
- package/corpus/templates/clips/app/i18n/en-US.ts +20 -0
- package/corpus/templates/clips/app/routes/_app.tsx +2 -0
- package/corpus/templates/clips/app/routes/import.tsx +396 -0
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +8 -6
- package/corpus/templates/clips/app/routes/record.tsx +13 -71
- package/corpus/templates/clips/changelog/2026-07-25-two-clip-emails-now-include-a-concise-summary-generated-secu.md +6 -0
- package/corpus/templates/clips/data/clips-transactional-emails/config.json +3 -0
- package/corpus/templates/clips/server/jobs/transactional-emails.ts +1034 -0
- package/corpus/templates/clips/server/lib/transactional-email-store.ts +865 -0
- package/corpus/templates/clips/server/lib/transactional-email-templates.ts +270 -0
- package/corpus/templates/clips/server/plugins/calendar-jobs.ts +2 -0
- package/corpus/templates/clips/server/routes/api/view-event.post.ts +59 -2
- package/corpus/templates/content/.agents/skills/document-editing/references/databases.md +8 -2
- package/corpus/templates/content/AGENTS.md +8 -0
- package/corpus/templates/content/_gitignore +2 -0
- package/corpus/templates/content/actions/_content-database-personal-view.ts +28 -3
- package/corpus/templates/content/actions/_property-utils.ts +32 -2
- package/corpus/templates/content/actions/configure-document-property.ts +12 -2
- package/corpus/templates/content/actions/delete-document-property.ts +13 -3
- package/corpus/templates/content/actions/duplicate-document-property.ts +13 -3
- package/corpus/templates/content/actions/list-content-spaces.ts +5 -2
- package/corpus/templates/content/actions/list-document-properties.ts +12 -3
- package/corpus/templates/content/actions/move-database-item.ts +74 -55
- package/corpus/templates/content/actions/reorder-document-property.ts +20 -4
- package/corpus/templates/content/actions/set-document-property.ts +10 -1
- package/corpus/templates/content/actions/update-content-database-personal-view.ts +7 -0
- package/corpus/templates/content/app/components/editor/DocumentBlockFields.tsx +31 -21
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +30 -3
- package/corpus/templates/content/app/components/editor/DocumentInfoPanel.tsx +8 -1
- package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +72 -20
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +79 -10
- package/corpus/templates/content/app/components/editor/database/TimelineView.tsx +8 -1
- package/corpus/templates/content/app/components/editor/database/settings.tsx +6 -0
- package/corpus/templates/content/app/components/editor/database/sidebar.tsx +246 -26
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +491 -197
- package/corpus/templates/content/app/components/sidebar/DocumentTreeItem.tsx +3 -3
- package/corpus/templates/content/app/components/sidebar/sidebar-reorder.tsx +409 -0
- package/corpus/templates/content/app/hooks/use-content-database.ts +117 -3
- package/corpus/templates/content/app/hooks/use-content-spaces.ts +1 -0
- package/corpus/templates/content/app/hooks/use-document-properties.ts +62 -18
- package/corpus/templates/content/app/hooks/use-documents.ts +13 -5
- package/corpus/templates/content/app/i18n-data.ts +261 -3
- package/corpus/templates/content/app/routes/_app.page.$id.tsx +9 -2
- package/corpus/templates/content/changelog/2026-07-24-database-page-properties-keep-their-context.md +6 -0
- package/corpus/templates/content/changelog/2026-07-26-sidebar-references-can-now-be-personally-reordered.md +6 -0
- package/corpus/templates/content/docs/solutions/2026-07-24-database-property-context-switch-diagnosis.md +249 -0
- package/corpus/templates/content/docs/solutions/content-sidebar-order-shape.md +389 -0
- package/corpus/templates/content/shared/api.ts +28 -0
- package/corpus/templates/design/_gitignore +2 -0
- package/corpus/templates/design/app/i18n-data.ts +10 -0
- package/corpus/templates/design/app/lib/builder-design-system-upload.ts +280 -0
- package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +90 -54
- package/corpus/templates/design/changelog/2026-07-28-figma-fig-uploads-now-show-indexing-progress-and-an-open-in-.md +6 -0
- package/corpus/templates/design/server/handlers/design-system-decode-job-status.ts +49 -0
- package/corpus/templates/design/server/handlers/design-system-upload-start.ts +87 -0
- package/corpus/templates/design/server/handlers/index-design-system-sources.ts +82 -0
- package/corpus/templates/design/server/routes/api/design-system-decode-job-status.get.ts +1 -0
- package/corpus/templates/design/server/routes/api/design-system-upload-start.post.ts +1 -0
- package/corpus/templates/design/server/routes/api/index-design-system-sources.post.ts +1 -0
- package/corpus/templates/dispatch/_gitignore +2 -0
- package/corpus/templates/forms/_gitignore +2 -0
- package/corpus/templates/macros/_gitignore +2 -0
- package/corpus/templates/mail/_gitignore +2 -0
- package/corpus/templates/plan/_gitignore +2 -0
- package/corpus/templates/slides/_gitignore +2 -0
- package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +126 -53
- package/corpus/templates/slides/app/components/design-system/builder-design-system-upload.ts +271 -0
- package/corpus/templates/slides/app/components/design-system/builder-index-response.ts +1 -1
- package/corpus/templates/slides/app/i18n/en-US.ts +1 -0
- package/corpus/templates/slides/server/handlers/design-system-decode-job-status.ts +49 -0
- package/corpus/templates/slides/server/handlers/design-system-upload-start.ts +87 -0
- package/corpus/templates/slides/server/handlers/index-design-system-sources.ts +79 -0
- package/corpus/templates/slides/server/routes/api/design-system-decode-job-status.get.ts +1 -0
- package/corpus/templates/slides/server/routes/api/design-system-upload-start.post.ts +1 -0
- package/corpus/templates/slides/server/routes/api/index-design-system-sources.post.ts +1 -0
- package/corpus/templates/tasks/_gitignore +2 -0
- package/corpus/toolkit/CHANGELOG.md +6 -0
- package/corpus/toolkit/package.json +1 -1
- package/corpus/toolkit/src/editor/DragHandle.ts +63 -9
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +3 -3
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +10 -10
- package/dist/provider-api/actions/provider-api.d.ts +8 -8
- package/dist/server/builder-design-systems.d.ts +49 -0
- package/dist/server/builder-design-systems.d.ts.map +1 -1
- package/dist/server/builder-design-systems.js +221 -69
- package/dist/server/builder-design-systems.js.map +1 -1
- package/dist/server/email-template.d.ts +7 -0
- package/dist/server/email-template.d.ts.map +1 -1
- package/dist/server/email-template.js +24 -0
- package/dist/server/email-template.js.map +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/templates/chat/_gitignore +2 -0
- package/package.json +5 -3
- package/src/server/builder-design-systems.ts +299 -82
- package/src/server/email-template.ts +33 -0
- package/src/server/index.ts +8 -0
- package/src/templates/chat/_gitignore +2 -0
- package/corpus/templates/design/server/handlers/index-design-system-with-builder.ts +0 -151
- package/corpus/templates/design/server/routes/api/index-design-system-with-builder.post.ts +0 -1
- package/corpus/templates/slides/server/handlers/index-design-system-with-builder.ts +0 -119
- package/corpus/templates/slides/server/routes/api/index-design-system-with-builder.post.ts +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.128.
|
|
3
|
+
"version": "0.128.2",
|
|
4
4
|
"description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
|
|
5
5
|
"homepage": "https://github.com/BuilderIO/agent-native#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -164,6 +164,7 @@
|
|
|
164
164
|
"./application-state": "./dist/application-state/index.js",
|
|
165
165
|
"./settings": "./dist/settings/index.js",
|
|
166
166
|
"./user-profile": "./dist/user-profile/index.js",
|
|
167
|
+
"./user-profile/server": "./dist/user-profile/store.js",
|
|
167
168
|
"./feature-flags": "./dist/feature-flags/index.js",
|
|
168
169
|
"./feature-flags/registry": "./dist/feature-flags/registry.js",
|
|
169
170
|
"./feature-flags/actions/get-feature-flags": "./dist/feature-flags/actions/get-feature-flags.js",
|
|
@@ -327,6 +328,7 @@
|
|
|
327
328
|
"@assistant-ui/react-markdown": "^0.12.6",
|
|
328
329
|
"@assistant-ui/store": ">=0.2.9 <0.2.14",
|
|
329
330
|
"@assistant-ui/tap": "^0.5.14",
|
|
331
|
+
"@builder.io/ai-utils": "^0.84.0",
|
|
330
332
|
"@clack/prompts": "^1.4.0",
|
|
331
333
|
"@codemirror/lang-sql": "^6.10.0",
|
|
332
334
|
"@codemirror/theme-one-dark": "^6.1.3",
|
|
@@ -388,8 +390,8 @@
|
|
|
388
390
|
"y-protocols": "^1.0.7",
|
|
389
391
|
"yjs": "^13.6.31",
|
|
390
392
|
"zod": "^4.3.6",
|
|
391
|
-
"@agent-native/
|
|
392
|
-
"@agent-native/
|
|
393
|
+
"@agent-native/toolkit": "^0.10.10",
|
|
394
|
+
"@agent-native/recap-cli": "0.5.1"
|
|
393
395
|
},
|
|
394
396
|
"devDependencies": {
|
|
395
397
|
"@ai-sdk/anthropic": "^3.0.71",
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { DesignSystemSourceInput } from "@builder.io/ai-utils";
|
|
2
|
+
|
|
1
3
|
import { withBuilderUtmTrackingParams } from "../shared/builder-link-tracking.js";
|
|
2
4
|
import { FeatureNotConfiguredError } from "./credential-provider.js";
|
|
3
5
|
import {
|
|
@@ -7,6 +9,12 @@ import {
|
|
|
7
9
|
|
|
8
10
|
const DEFAULT_TIMEOUT_MS = 120_000;
|
|
9
11
|
|
|
12
|
+
// GCS resumable uploads require every chunk except the last to be a multiple
|
|
13
|
+
// of 256 KiB. 16 MiB is the recommended default and keeps very large `.fig`
|
|
14
|
+
// files off a single unbounded request body.
|
|
15
|
+
const GCS_CHUNK_SIZE = 16 * 1024 * 1024;
|
|
16
|
+
const MAX_CHUNK_RETRIES = 5;
|
|
17
|
+
|
|
10
18
|
export interface BuilderDesignSystemIndexFile {
|
|
11
19
|
name: string;
|
|
12
20
|
data: Uint8Array;
|
|
@@ -123,16 +131,65 @@ interface UploadStartResponse {
|
|
|
123
131
|
uploads?: Array<{ idx: number; uploadUrl: string; uploadToken: string }>;
|
|
124
132
|
}
|
|
125
133
|
|
|
126
|
-
interface
|
|
127
|
-
projectId?: string;
|
|
128
|
-
jobId?: string;
|
|
134
|
+
interface IndexResponse {
|
|
129
135
|
designSystemId?: string;
|
|
136
|
+
jobId?: string;
|
|
137
|
+
projectId?: string;
|
|
138
|
+
branchUrl?: string;
|
|
139
|
+
branchName?: string;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export interface BuilderDesignSystemUploadAttachment {
|
|
143
|
+
name: string;
|
|
144
|
+
mimetype: string;
|
|
145
|
+
declaredSize: number;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export interface BuilderDesignSystemUploadSlot {
|
|
149
|
+
idx: number;
|
|
150
|
+
uploadUrl: string;
|
|
151
|
+
uploadToken: string;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export interface BuilderDesignSystemIndexFromSourcesOptions {
|
|
155
|
+
sources: DesignSystemSourceInput[];
|
|
156
|
+
projectName?: string;
|
|
157
|
+
devToolsVersion?: string;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export interface BuilderDesignSystemDecodeJobStatus {
|
|
161
|
+
jobId: string;
|
|
162
|
+
status: "pending" | "processing" | "complete" | "error";
|
|
163
|
+
framesProcessed: number;
|
|
164
|
+
totalFrames: number;
|
|
165
|
+
branchName: string | null;
|
|
166
|
+
branchUrl: string | null;
|
|
167
|
+
error: string | null;
|
|
168
|
+
partialFailure?: boolean;
|
|
169
|
+
createdAt: number;
|
|
170
|
+
updatedAt: number;
|
|
130
171
|
}
|
|
131
172
|
|
|
132
173
|
const DEFAULT_MAX_CODE_FILES = 50;
|
|
133
174
|
const DEFAULT_MAX_TOTAL_CODE_BYTES = 2 * 1024 * 1024;
|
|
134
175
|
const MAX_DOC_CONTENT_CHARS = 4_000;
|
|
135
176
|
|
|
177
|
+
export async function fetchBuilderDesignSystemDecodeJobStatus(
|
|
178
|
+
jobId: string,
|
|
179
|
+
): Promise<BuilderDesignSystemDecodeJobStatus> {
|
|
180
|
+
const credentials = await resolveBuilderDesignSystemCredentials();
|
|
181
|
+
const url = makeBuilderDesignSystemUrl(
|
|
182
|
+
"decode-jobs/" + encodeURIComponent(jobId),
|
|
183
|
+
credentials,
|
|
184
|
+
);
|
|
185
|
+
const response = await fetchWithTimeout(url, {
|
|
186
|
+
method: "GET",
|
|
187
|
+
headers: makeBuilderHeaders(credentials),
|
|
188
|
+
});
|
|
189
|
+
await assertOk(response, "Builder design-system decode-job status failed");
|
|
190
|
+
return (await response.json()) as BuilderDesignSystemDecodeJobStatus;
|
|
191
|
+
}
|
|
192
|
+
|
|
136
193
|
function trimTrailingSlash(value: string): string {
|
|
137
194
|
return value.replace(/\/+$/, "");
|
|
138
195
|
}
|
|
@@ -318,17 +375,45 @@ async function assertOk(response: Response, label: string): Promise<void> {
|
|
|
318
375
|
);
|
|
319
376
|
}
|
|
320
377
|
|
|
378
|
+
// GCS reports the highest committed byte in a `Range: bytes=0-<end>` header.
|
|
379
|
+
function committedOffsetFromRange(response: Response): number | null {
|
|
380
|
+
const match = response.headers.get("Range")?.match(/bytes=0-(\d+)/);
|
|
381
|
+
return match ? parseInt(match[1], 10) + 1 : null;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
async function queryCommittedOffset(
|
|
385
|
+
sessionUrl: string,
|
|
386
|
+
total: number,
|
|
387
|
+
): Promise<number> {
|
|
388
|
+
const response = await fetchWithTimeout(sessionUrl, {
|
|
389
|
+
method: "PUT",
|
|
390
|
+
headers: { "Content-Range": `bytes */${total}` },
|
|
391
|
+
});
|
|
392
|
+
if (response.status === 200 || response.status === 201) return total;
|
|
393
|
+
if (response.status === 308) {
|
|
394
|
+
return committedOffsetFromRange(response) ?? 0;
|
|
395
|
+
}
|
|
396
|
+
throw new Error(
|
|
397
|
+
`Builder design-system upload status query failed (${response.status}).`,
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function delay(ms: number): Promise<void> {
|
|
402
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
403
|
+
}
|
|
404
|
+
|
|
321
405
|
async function uploadToResumableUrl(
|
|
322
406
|
slot: { uploadUrl: string },
|
|
323
407
|
file: BuilderDesignSystemIndexFile,
|
|
324
408
|
): Promise<void> {
|
|
325
409
|
const mimeType = mimeTypeForFile(file);
|
|
326
410
|
const bytes = file.data;
|
|
411
|
+
const total = bytes.byteLength;
|
|
327
412
|
const start = await fetchWithTimeout(slot.uploadUrl, {
|
|
328
413
|
method: "POST",
|
|
329
414
|
headers: {
|
|
330
415
|
"x-goog-resumable": "start",
|
|
331
|
-
"x-goog-content-length-range": `0,${
|
|
416
|
+
"x-goog-content-length-range": `0,${total}`,
|
|
332
417
|
"Content-Type": mimeType,
|
|
333
418
|
},
|
|
334
419
|
});
|
|
@@ -338,15 +423,60 @@ async function uploadToResumableUrl(
|
|
|
338
423
|
throw new Error("Builder design-system upload session returned no URL.");
|
|
339
424
|
}
|
|
340
425
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
"Content-Range":
|
|
345
|
-
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
426
|
+
if (total === 0) {
|
|
427
|
+
const response = await fetchWithTimeout(sessionUrl, {
|
|
428
|
+
method: "PUT",
|
|
429
|
+
headers: { "Content-Range": "bytes */0" },
|
|
430
|
+
body: makeBody(bytes, mimeType),
|
|
431
|
+
});
|
|
432
|
+
await assertOk(response, "Builder design-system file upload failed");
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// A failed PUT may have still landed at GCS, so the local offset can't be
|
|
437
|
+
// trusted after an error — only GCS's committed-offset response is
|
|
438
|
+
// authoritative.
|
|
439
|
+
let offset = 0;
|
|
440
|
+
let retries = 0;
|
|
441
|
+
while (offset < total) {
|
|
442
|
+
const end = Math.min(offset + GCS_CHUNK_SIZE, total);
|
|
443
|
+
const isLast = end === total;
|
|
444
|
+
try {
|
|
445
|
+
const response = await fetchWithTimeout(sessionUrl, {
|
|
446
|
+
method: "PUT",
|
|
447
|
+
headers: {
|
|
448
|
+
"Content-Range": `bytes ${offset}-${end - 1}/${total}`,
|
|
449
|
+
"Content-Type": mimeType,
|
|
450
|
+
},
|
|
451
|
+
body: makeBody(bytes.subarray(offset, end), mimeType),
|
|
452
|
+
});
|
|
453
|
+
if (response.status === 200 || response.status === 201) {
|
|
454
|
+
offset = total;
|
|
455
|
+
} else if (!isLast && response.status === 308) {
|
|
456
|
+
const nextOffset = committedOffsetFromRange(response) ?? offset;
|
|
457
|
+
if (nextOffset <= offset) {
|
|
458
|
+
throw new Error(
|
|
459
|
+
`Builder design-system upload stalled at byte ${offset}.`,
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
offset = nextOffset;
|
|
463
|
+
} else {
|
|
464
|
+
throw new Error(
|
|
465
|
+
`Builder design-system file upload failed (${response.status}).`,
|
|
466
|
+
);
|
|
467
|
+
}
|
|
468
|
+
retries = 0;
|
|
469
|
+
} catch (err) {
|
|
470
|
+
if (++retries > MAX_CHUNK_RETRIES) throw err;
|
|
471
|
+
await delay(500 * retries);
|
|
472
|
+
try {
|
|
473
|
+
offset = await queryCommittedOffset(sessionUrl, total);
|
|
474
|
+
} catch {
|
|
475
|
+
// If the offset query also fails, retry from the last local offset —
|
|
476
|
+
// GCS's resumable PUT safely re-acknowledges bytes it already has.
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
350
480
|
}
|
|
351
481
|
|
|
352
482
|
function nonEmptyFiles(
|
|
@@ -368,6 +498,25 @@ export function builderDesignSystemUrl(designSystemId?: string | null): string {
|
|
|
368
498
|
});
|
|
369
499
|
}
|
|
370
500
|
|
|
501
|
+
export function builderProjectBranchUrl(
|
|
502
|
+
projectId?: string | null,
|
|
503
|
+
branchName?: string | null,
|
|
504
|
+
): string | undefined {
|
|
505
|
+
const project = projectId?.trim();
|
|
506
|
+
const branch = branchName?.trim();
|
|
507
|
+
if (!project || !branch) return undefined;
|
|
508
|
+
const host = trimTrailingSlash(getBuilderAppHost());
|
|
509
|
+
const path =
|
|
510
|
+
"/app/projects/" +
|
|
511
|
+
encodeURIComponent(project) +
|
|
512
|
+
"/" +
|
|
513
|
+
encodeURIComponent(branch);
|
|
514
|
+
return withBuilderUtmTrackingParams(host + path, {
|
|
515
|
+
campaign: "product",
|
|
516
|
+
content: "design_system_intelligence",
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
|
|
371
520
|
export function localBuilderDesignSystemId(
|
|
372
521
|
builderDesignSystemId: string,
|
|
373
522
|
): string {
|
|
@@ -569,65 +718,58 @@ export async function hydrateBuilderDesignSystemReference(
|
|
|
569
718
|
};
|
|
570
719
|
}
|
|
571
720
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
721
|
+
/**
|
|
722
|
+
* Opens signed resumable-upload slots for `.fig`/code/design attachments so
|
|
723
|
+
* the browser can stream each file's bytes straight to GCS. Large `.fig`
|
|
724
|
+
* files must not ride through the app server as one request body -- the
|
|
725
|
+
* serverless host caps request bodies well below Figma export sizes.
|
|
726
|
+
*/
|
|
727
|
+
export async function startBuilderDesignSystemUpload(
|
|
728
|
+
attachments: BuilderDesignSystemUploadAttachment[],
|
|
729
|
+
): Promise<BuilderDesignSystemUploadSlot[]> {
|
|
730
|
+
if (attachments.length === 0) return [];
|
|
731
|
+
const credentials = await resolveBuilderDesignSystemCredentials();
|
|
732
|
+
const uploadStart = await fetchWithTimeout(
|
|
733
|
+
makeBuilderDesignSystemUrl("upload/start", credentials),
|
|
734
|
+
{
|
|
735
|
+
method: "POST",
|
|
736
|
+
headers: {
|
|
737
|
+
...makeBuilderHeaders(credentials),
|
|
738
|
+
"Content-Type": "application/json",
|
|
739
|
+
},
|
|
740
|
+
body: JSON.stringify({ attachments }),
|
|
741
|
+
},
|
|
742
|
+
);
|
|
743
|
+
await assertOk(uploadStart, "Builder design-system upload start failed");
|
|
744
|
+
const uploadJson = (await uploadStart.json()) as UploadStartResponse;
|
|
745
|
+
const slots = [...(uploadJson.uploads ?? [])].sort((a, b) => a.idx - b.idx);
|
|
746
|
+
if (slots.length !== attachments.length) {
|
|
747
|
+
throw new Error("Builder did not return upload slots for all files.");
|
|
583
748
|
}
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
749
|
+
for (let i = 0; i < slots.length; i++) {
|
|
750
|
+
if (slots[i].idx !== i) {
|
|
751
|
+
throw new Error("Builder upload slot mismatch: expected " + i + ".");
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
return slots;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
/**
|
|
758
|
+
* Finalizes indexing from already-resolved sources (uploaded file tokens,
|
|
759
|
+
* public repos, connected projects). Callers that stream uploads from the
|
|
760
|
+
* browser pass the returned `uploadToken`s as `file` sources here.
|
|
761
|
+
*/
|
|
762
|
+
export async function indexBuilderDesignSystem(
|
|
763
|
+
options: BuilderDesignSystemIndexFromSourcesOptions,
|
|
764
|
+
): Promise<BuilderDesignSystemIndexResult> {
|
|
765
|
+
if (options.sources.length === 0) {
|
|
589
766
|
throw new Error(
|
|
590
767
|
"Provide at least one .fig/code/text file or a GitHub repository URL to index with Builder.",
|
|
591
768
|
);
|
|
592
769
|
}
|
|
593
|
-
|
|
594
770
|
const credentials = await resolveBuilderDesignSystemCredentials();
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
const uploadStart = await fetchWithTimeout(
|
|
598
|
-
makeBuilderDesignSystemUrl("upload/start", credentials),
|
|
599
|
-
{
|
|
600
|
-
method: "POST",
|
|
601
|
-
headers: {
|
|
602
|
-
...makeBuilderHeaders(credentials),
|
|
603
|
-
"Content-Type": "application/json",
|
|
604
|
-
},
|
|
605
|
-
body: JSON.stringify({
|
|
606
|
-
attachments: files.map((file) => ({
|
|
607
|
-
name: file.name,
|
|
608
|
-
mimetype: mimeTypeForFile(file),
|
|
609
|
-
declaredSize: file.data.byteLength,
|
|
610
|
-
})),
|
|
611
|
-
}),
|
|
612
|
-
},
|
|
613
|
-
);
|
|
614
|
-
await assertOk(uploadStart, "Builder design-system upload start failed");
|
|
615
|
-
const uploadJson = (await uploadStart.json()) as UploadStartResponse;
|
|
616
|
-
const slots = [...(uploadJson.uploads ?? [])].sort((a, b) => a.idx - b.idx);
|
|
617
|
-
if (slots.length !== files.length) {
|
|
618
|
-
throw new Error("Builder did not return upload slots for all files.");
|
|
619
|
-
}
|
|
620
|
-
for (let i = 0; i < slots.length; i++) {
|
|
621
|
-
if (slots[i].idx !== i) {
|
|
622
|
-
throw new Error(`Builder upload slot mismatch: expected ${i}.`);
|
|
623
|
-
}
|
|
624
|
-
await uploadToResumableUrl(slots[i], files[i]);
|
|
625
|
-
}
|
|
626
|
-
uploadTokens = slots.map((slot) => slot.uploadToken);
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
const generate = await fetchWithTimeout(
|
|
630
|
-
makeBuilderDesignSystemUrl("generate", credentials),
|
|
771
|
+
const index = await fetchWithTimeout(
|
|
772
|
+
makeBuilderDesignSystemUrl("index", credentials),
|
|
631
773
|
{
|
|
632
774
|
method: "POST",
|
|
633
775
|
headers: {
|
|
@@ -635,39 +777,114 @@ export async function startBuilderDesignSystemIndex(
|
|
|
635
777
|
"Content-Type": "application/json",
|
|
636
778
|
},
|
|
637
779
|
body: JSON.stringify({
|
|
638
|
-
|
|
780
|
+
sources: options.sources,
|
|
639
781
|
...(options.projectName?.trim()
|
|
640
|
-
? {
|
|
641
|
-
: {}),
|
|
642
|
-
...(options.githubRepoUrl?.trim()
|
|
643
|
-
? { githubRepoUrl: options.githubRepoUrl.trim() }
|
|
782
|
+
? { designSystemName: options.projectName.trim() }
|
|
644
783
|
: {}),
|
|
645
|
-
...(options.connectedProjectId?.trim()
|
|
646
|
-
? { connectedProjectId: options.connectedProjectId.trim() }
|
|
647
|
-
: {}),
|
|
648
|
-
...(options.selection ? { selection: options.selection } : {}),
|
|
649
784
|
...(options.devToolsVersion?.trim()
|
|
650
785
|
? { devToolsVersion: options.devToolsVersion.trim() }
|
|
651
786
|
: {}),
|
|
652
787
|
}),
|
|
653
788
|
},
|
|
654
789
|
);
|
|
655
|
-
await assertOk(
|
|
656
|
-
const
|
|
657
|
-
if (!
|
|
790
|
+
await assertOk(index, "Builder design-system indexing failed");
|
|
791
|
+
const indexed = (await index.json()) as IndexResponse;
|
|
792
|
+
if (!indexed.designSystemId) {
|
|
658
793
|
throw new Error(
|
|
659
794
|
"Builder design-system indexing returned an incomplete response.",
|
|
660
795
|
);
|
|
661
796
|
}
|
|
662
797
|
|
|
798
|
+
const jobId = indexed.jobId ?? "";
|
|
799
|
+
// The `.fig` decode job creates the Fusion branch asynchronously, so
|
|
800
|
+
// `/index` usually can't return a branchUrl yet — the caller polls the
|
|
801
|
+
// decode-job status endpoint for it once the job completes.
|
|
802
|
+
const branchUrl = indexed.branchUrl?.trim() || null;
|
|
803
|
+
|
|
663
804
|
return {
|
|
664
805
|
ok: true,
|
|
665
806
|
source: "builder",
|
|
666
|
-
projectId:
|
|
667
|
-
jobId
|
|
668
|
-
designSystemId:
|
|
807
|
+
projectId: indexed.projectId ?? "",
|
|
808
|
+
jobId,
|
|
809
|
+
designSystemId: indexed.designSystemId,
|
|
669
810
|
suggestedTitle: options.projectName?.trim() || null,
|
|
670
|
-
builderUrl:
|
|
811
|
+
builderUrl:
|
|
812
|
+
branchUrl ||
|
|
813
|
+
builderProjectBranchUrl(indexed.projectId, indexed.branchName) ||
|
|
814
|
+
builderDesignSystemUrl(indexed.designSystemId),
|
|
671
815
|
status: "in-progress",
|
|
672
816
|
};
|
|
673
817
|
}
|
|
818
|
+
|
|
819
|
+
/**
|
|
820
|
+
* Server-side indexing for in-memory files (the agent action's small inline
|
|
821
|
+
* payloads). Uploads each file server->GCS in resumable chunks, then
|
|
822
|
+
* finalizes. Browser callers should instead stream via
|
|
823
|
+
* `startBuilderDesignSystemUpload` + `indexBuilderDesignSystem`.
|
|
824
|
+
*/
|
|
825
|
+
export async function startBuilderDesignSystemIndex(
|
|
826
|
+
options: BuilderDesignSystemIndexOptions,
|
|
827
|
+
): Promise<BuilderDesignSystemIndexResult> {
|
|
828
|
+
const files = nonEmptyFiles(options.files);
|
|
829
|
+
const description = options.description?.trim();
|
|
830
|
+
if (description) {
|
|
831
|
+
files.unshift({
|
|
832
|
+
name: "additional-context.txt",
|
|
833
|
+
data: new TextEncoder().encode(description),
|
|
834
|
+
mimeType: "text/plain",
|
|
835
|
+
});
|
|
836
|
+
}
|
|
837
|
+
if (
|
|
838
|
+
files.length === 0 &&
|
|
839
|
+
!options.githubRepoUrl &&
|
|
840
|
+
!options.connectedProjectId
|
|
841
|
+
) {
|
|
842
|
+
throw new Error(
|
|
843
|
+
"Provide at least one .fig/code/text file or a GitHub repository URL to index with Builder.",
|
|
844
|
+
);
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
const sources: DesignSystemSourceInput[] = [];
|
|
848
|
+
if (files.length > 0) {
|
|
849
|
+
const slots = await startBuilderDesignSystemUpload(
|
|
850
|
+
files.map((file) => ({
|
|
851
|
+
name: file.name,
|
|
852
|
+
mimetype: mimeTypeForFile(file),
|
|
853
|
+
declaredSize: file.data.byteLength,
|
|
854
|
+
})),
|
|
855
|
+
);
|
|
856
|
+
for (let i = 0; i < slots.length; i++) {
|
|
857
|
+
await uploadToResumableUrl(slots[i], files[i]);
|
|
858
|
+
}
|
|
859
|
+
for (let i = 0; i < slots.length; i++) {
|
|
860
|
+
const name = files[i].name;
|
|
861
|
+
const fileSelection = options.selection?.[name];
|
|
862
|
+
sources.push({
|
|
863
|
+
kind: "file",
|
|
864
|
+
uploadToken: slots[i].uploadToken,
|
|
865
|
+
...(fileSelection && fileSelection.length > 0
|
|
866
|
+
? { selection: { [name]: fileSelection } }
|
|
867
|
+
: {}),
|
|
868
|
+
});
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
if (options.githubRepoUrl?.trim()) {
|
|
873
|
+
sources.push({
|
|
874
|
+
kind: "public-repo",
|
|
875
|
+
repoUrl: options.githubRepoUrl.trim(),
|
|
876
|
+
});
|
|
877
|
+
}
|
|
878
|
+
if (options.connectedProjectId?.trim()) {
|
|
879
|
+
sources.push({
|
|
880
|
+
kind: "connected-repo",
|
|
881
|
+
fusionProjectId: options.connectedProjectId.trim(),
|
|
882
|
+
});
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
return indexBuilderDesignSystem({
|
|
886
|
+
sources,
|
|
887
|
+
projectName: options.projectName,
|
|
888
|
+
devToolsVersion: options.devToolsVersion,
|
|
889
|
+
});
|
|
890
|
+
}
|
|
@@ -27,6 +27,12 @@ export interface EmailCta {
|
|
|
27
27
|
url: string;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
export interface EmailLinkBlock {
|
|
31
|
+
intro: string;
|
|
32
|
+
url: string;
|
|
33
|
+
placement?: "before-cta" | "after-cta";
|
|
34
|
+
}
|
|
35
|
+
|
|
30
36
|
export interface RenderEmailArgs {
|
|
31
37
|
/** Short preview text shown by email clients next to the subject. */
|
|
32
38
|
preheader?: string;
|
|
@@ -36,6 +42,8 @@ export interface RenderEmailArgs {
|
|
|
36
42
|
paragraphs: string[];
|
|
37
43
|
/** Primary call-to-action rendered as a real button. */
|
|
38
44
|
cta?: EmailCta;
|
|
45
|
+
/** A treated, copyable URL shown before or after the CTA. */
|
|
46
|
+
linkBlock?: EmailLinkBlock;
|
|
39
47
|
/**
|
|
40
48
|
* Optional trusted HTML injected above the CTA — e.g. a template-owned video
|
|
41
49
|
* thumbnail with a play badge. Injected verbatim, so only pass markup built
|
|
@@ -127,6 +135,19 @@ export function renderEmail(args: RenderEmailArgs): RenderedEmail {
|
|
|
127
135
|
)
|
|
128
136
|
.join("");
|
|
129
137
|
|
|
138
|
+
const linkBlockHtml = args.linkBlock
|
|
139
|
+
? `
|
|
140
|
+
<p style="margin:24px 0 12px 0; font-size:15px; line-height:1.6; color:#d4d4d8;">${escapeHtml(args.linkBlock.intro)}</p>
|
|
141
|
+
<table role="presentation" cellpadding="0" cellspacing="0" border="0" width="100%" style="margin:0; border:1px solid #3f3f46; border-radius:10px; background:#0a0a0c;">
|
|
142
|
+
<tr>
|
|
143
|
+
<td style="padding:14px 16px;">
|
|
144
|
+
<a href="${escapeAttr(args.linkBlock.url)}" style="display:block; color:#d4d4d8; font-family:'SFMono-Regular', Consolas, 'Liberation Mono', monospace; font-size:13px; line-height:1.5; text-decoration:none; word-break:break-all; overflow-wrap:anywhere;">${escapeHtml(args.linkBlock.url)}</a>
|
|
145
|
+
</td>
|
|
146
|
+
</tr>
|
|
147
|
+
</table>
|
|
148
|
+
`
|
|
149
|
+
: "";
|
|
150
|
+
|
|
130
151
|
const ctaHtml = args.cta
|
|
131
152
|
? `
|
|
132
153
|
<table role="presentation" cellpadding="0" cellspacing="0" border="0" style="margin:24px 0 0 0;">
|
|
@@ -187,7 +208,9 @@ export function renderEmail(args: RenderEmailArgs): RenderedEmail {
|
|
|
187
208
|
</h1>
|
|
188
209
|
${paragraphsHtml}
|
|
189
210
|
${heroHtml}
|
|
211
|
+
${args.linkBlock?.placement !== "after-cta" ? linkBlockHtml : ""}
|
|
190
212
|
${ctaHtml}
|
|
213
|
+
${args.linkBlock?.placement === "after-cta" ? linkBlockHtml : ""}
|
|
191
214
|
${footerHtml}
|
|
192
215
|
</td>
|
|
193
216
|
</tr>
|
|
@@ -205,10 +228,20 @@ export function renderEmail(args: RenderEmailArgs): RenderedEmail {
|
|
|
205
228
|
textLines.push(stripTags(p));
|
|
206
229
|
textLines.push("");
|
|
207
230
|
}
|
|
231
|
+
if (args.linkBlock && args.linkBlock.placement !== "after-cta") {
|
|
232
|
+
textLines.push(args.linkBlock.intro);
|
|
233
|
+
textLines.push(args.linkBlock.url);
|
|
234
|
+
textLines.push("");
|
|
235
|
+
}
|
|
208
236
|
if (args.cta) {
|
|
209
237
|
textLines.push(`${args.cta.label}: ${args.cta.url}`);
|
|
210
238
|
textLines.push("");
|
|
211
239
|
}
|
|
240
|
+
if (args.linkBlock?.placement === "after-cta") {
|
|
241
|
+
textLines.push(args.linkBlock.intro);
|
|
242
|
+
textLines.push(args.linkBlock.url);
|
|
243
|
+
textLines.push("");
|
|
244
|
+
}
|
|
212
245
|
if (args.footer) {
|
|
213
246
|
textLines.push(args.footer);
|
|
214
247
|
}
|
package/src/server/index.ts
CHANGED
|
@@ -486,23 +486,31 @@ export {
|
|
|
486
486
|
} from "./credential-provider.js";
|
|
487
487
|
export {
|
|
488
488
|
builderDesignSystemUrl,
|
|
489
|
+
builderProjectBranchUrl,
|
|
489
490
|
buildBuilderDesignSystemIndexFiles,
|
|
490
491
|
createBuilderDesignSystemProxyFields,
|
|
492
|
+
fetchBuilderDesignSystemDecodeJobStatus,
|
|
491
493
|
fetchBuilderDesignSystemDocs,
|
|
492
494
|
getBuilderDesignSystemsBaseUrl,
|
|
493
495
|
hydrateBuilderDesignSystemReference,
|
|
496
|
+
indexBuilderDesignSystem,
|
|
494
497
|
localBuilderDesignSystemId,
|
|
495
498
|
mimeTypeForBuilderDesignSystemFilename,
|
|
496
499
|
parseBuilderDesignSystemProxyReference,
|
|
497
500
|
startBuilderDesignSystemIndex,
|
|
501
|
+
startBuilderDesignSystemUpload,
|
|
498
502
|
type BuildBuilderDesignSystemIndexFilesOptions,
|
|
499
503
|
type BuilderDesignSystemCodeFileInput,
|
|
504
|
+
type BuilderDesignSystemDecodeJobStatus,
|
|
500
505
|
type BuilderDesignSystemDocsOptions,
|
|
501
506
|
type BuilderDesignSystemDocument,
|
|
502
507
|
type BuilderDesignSystemHydratedReference,
|
|
503
508
|
type BuilderDesignSystemIndexFile,
|
|
509
|
+
type BuilderDesignSystemIndexFromSourcesOptions,
|
|
504
510
|
type BuilderDesignSystemIndexOptions,
|
|
505
511
|
type BuilderDesignSystemIndexResult,
|
|
512
|
+
type BuilderDesignSystemUploadAttachment,
|
|
513
|
+
type BuilderDesignSystemUploadSlot,
|
|
506
514
|
type BuilderDesignSystemProxyFields,
|
|
507
515
|
type BuilderDesignSystemProxyFieldsOptions,
|
|
508
516
|
type BuilderDesignSystemProxyReference,
|