@agent-native/core 0.128.0 → 0.128.1
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 +21 -0
- package/corpus/core/package.json +2 -1
- package/corpus/core/src/server/builder-design-systems.ts +299 -82
- 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/clips/_gitignore +2 -0
- package/corpus/templates/content/_gitignore +2 -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/dist/collab/routes.d.ts +1 -1
- 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/agent-engine-api-key-route.d.ts +1 -1
- 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/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/package.json +2 -1
- package/src/server/builder-design-systems.ts +299 -82
- package/src/server/index.ts +8 -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/corpus/README.md
CHANGED
package/corpus/core/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# @agent-native/core
|
|
2
2
|
|
|
3
|
+
## 0.128.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 9140268: Update Builder design-system indexing to call the current `/design-systems/v1/index`
|
|
8
|
+
endpoint with a structured `sources` array (uploaded files, public repos, connected
|
|
9
|
+
projects) instead of the retired `generate` endpoint and its flat `uploads` payload.
|
|
10
|
+
File selections are now attached per source, and the incomplete-response guard only
|
|
11
|
+
requires a `designSystemId`. "Open in Builder" now links into the actual
|
|
12
|
+
project/branch (`branchUrl`) when the service returns one, falling back to the
|
|
13
|
+
design-system-intelligence docs URL. When `/index` returns only a `jobId`, the
|
|
14
|
+
Fusion branch URL is read from `GET /design-systems/v1/decode-jobs/:jobId`
|
|
15
|
+
(exposed as `fetchBuilderDesignSystemDecodeJobStatus`) so "Open in Builder" lands
|
|
16
|
+
on the branch. Large files (notably `.fig`) now stream to
|
|
17
|
+
storage in 16 MiB resumable chunks with retry and offset recovery instead of a
|
|
18
|
+
single unbounded request body. Indexing is split into `startBuilderDesignSystemUpload`
|
|
19
|
+
(opens signed resumable-upload slots) and `indexBuilderDesignSystem` (finalizes from
|
|
20
|
+
resolved sources) so browsers can stream `.fig` bytes straight to storage and never
|
|
21
|
+
hit the serverless request-body cap; `startBuilderDesignSystemIndex` still handles
|
|
22
|
+
small in-memory server-side payloads. Also exports `builderProjectBranchUrl`.
|
|
23
|
+
|
|
3
24
|
## 0.128.0
|
|
4
25
|
|
|
5
26
|
### Minor Changes
|
package/corpus/core/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.128.
|
|
3
|
+
"version": "0.128.1",
|
|
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": {
|
|
@@ -338,6 +338,7 @@
|
|
|
338
338
|
"@assistant-ui/react-markdown": "^0.12.6",
|
|
339
339
|
"@assistant-ui/store": ">=0.2.9 <0.2.14",
|
|
340
340
|
"@assistant-ui/tap": "^0.5.14",
|
|
341
|
+
"@builder.io/ai-utils": "^0.84.0",
|
|
341
342
|
"@clack/prompts": "^1.4.0",
|
|
342
343
|
"@codemirror/lang-sql": "^6.10.0",
|
|
343
344
|
"@codemirror/theme-one-dark": "^6.1.3",
|
|
@@ -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
|
+
}
|
|
@@ -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,
|