@agent-native/core 0.80.9 → 0.80.11

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 (148) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +13 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/agent/production-agent.ts +108 -15
  5. package/corpus/core/src/cli/design-connect.ts +28 -2
  6. package/corpus/core/src/cli/skills.ts +45 -24
  7. package/corpus/core/src/client/AssistantChat.tsx +123 -26
  8. package/corpus/core/src/client/sse-event-processor.ts +9 -0
  9. package/corpus/templates/assets/.agents/skills/image-generation/SKILL.md +9 -2
  10. package/corpus/templates/assets/AGENTS.md +4 -0
  11. package/corpus/templates/assets/actions/_tool-activity.ts +46 -0
  12. package/corpus/templates/assets/actions/generate-image-batch.ts +26 -5
  13. package/corpus/templates/assets/actions/generate-image.ts +118 -67
  14. package/corpus/templates/assets/actions/list-draft-assets.ts +50 -0
  15. package/corpus/templates/assets/actions/rerun-generation-run.ts +52 -31
  16. package/corpus/templates/assets/app/components/create/RecentDraftsSection.tsx +100 -0
  17. package/corpus/templates/assets/app/i18n/zh-TW.ts +7 -0
  18. package/corpus/templates/assets/app/i18n-data.ts +61 -0
  19. package/corpus/templates/assets/app/routes/_index.tsx +4 -0
  20. package/corpus/templates/assets/app/routes/library.tsx +145 -38
  21. package/corpus/templates/assets/changelog/2026-06-29-image-generation-can-now-render-requested-text-and-respect-b.md +6 -0
  22. package/corpus/templates/assets/changelog/2026-06-30-image-generation-no-longer-looks-stuck-while-the-provider-is-still-working.md +6 -0
  23. package/corpus/templates/assets/changelog/2026-06-30-tagged-presets-now-control-the-image-aspect-ratio.md +6 -0
  24. package/corpus/templates/assets/server/lib/generation.ts +135 -11
  25. package/corpus/templates/assets/shared/api.ts +4 -0
  26. package/corpus/templates/clips/actions/list-ai-requests.ts +60 -0
  27. package/corpus/templates/clips/app/hooks/use-auto-title.ts +24 -25
  28. package/corpus/templates/design/DESIGN-STUDIO-PLAN.md +717 -0
  29. package/corpus/templates/design/actions/add-breakpoint.ts +143 -0
  30. package/corpus/templates/design/actions/add-localhost-screens.ts +5 -0
  31. package/corpus/templates/design/actions/apply-a11y-fix.ts +317 -0
  32. package/corpus/templates/design/actions/apply-component-prop-edit.ts +441 -0
  33. package/corpus/templates/design/actions/apply-design-state.ts +213 -0
  34. package/corpus/templates/design/actions/apply-design-token-edit.ts +202 -0
  35. package/corpus/templates/design/actions/apply-motion-edit.ts +413 -0
  36. package/corpus/templates/design/actions/apply-shader-fill.ts +404 -0
  37. package/corpus/templates/design/actions/apply-visual-edit.ts +191 -5
  38. package/corpus/templates/design/actions/capture-design-state.ts +224 -0
  39. package/corpus/templates/design/actions/connect-builder-app.ts +162 -0
  40. package/corpus/templates/design/actions/create-component.ts +447 -0
  41. package/corpus/templates/design/actions/create-design-branch.ts +263 -0
  42. package/corpus/templates/design/actions/create-design-state.ts +162 -0
  43. package/corpus/templates/design/actions/delete-design-state.ts +55 -0
  44. package/corpus/templates/design/actions/delete-design.ts +20 -0
  45. package/corpus/templates/design/actions/deploy-design-preview.ts +275 -0
  46. package/corpus/templates/design/actions/get-component-details.ts +251 -0
  47. package/corpus/templates/design/actions/get-design-branch-diff.ts +362 -0
  48. package/corpus/templates/design/actions/get-design-review.ts +314 -0
  49. package/corpus/templates/design/actions/get-design-surface-index.ts +582 -0
  50. package/corpus/templates/design/actions/get-motion-timeline.ts +99 -0
  51. package/corpus/templates/design/actions/index-components.ts +258 -0
  52. package/corpus/templates/design/actions/index-design-tokens.ts +277 -0
  53. package/corpus/templates/design/actions/list-design-extensions.ts +309 -0
  54. package/corpus/templates/design/actions/list-design-source-capabilities.ts +153 -0
  55. package/corpus/templates/design/actions/list-design-states.ts +90 -0
  56. package/corpus/templates/design/actions/migrate-inline-design-to-app.ts +298 -0
  57. package/corpus/templates/design/actions/open-component-source.ts +242 -0
  58. package/corpus/templates/design/actions/preview-component-prop-edit.ts +260 -0
  59. package/corpus/templates/design/actions/preview-design-token-edit.ts +113 -0
  60. package/corpus/templates/design/actions/preview-shader-fill.ts +187 -0
  61. package/corpus/templates/design/actions/remove-breakpoint.ts +103 -0
  62. package/corpus/templates/design/actions/remove-motion-timeline.ts +196 -0
  63. package/corpus/templates/design/actions/run-design-audit.ts +436 -0
  64. package/corpus/templates/design/actions/run-design-extension-action.ts +284 -0
  65. package/corpus/templates/design/actions/set-active-breakpoint.ts +39 -0
  66. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +1550 -82
  67. package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +649 -43
  68. package/corpus/templates/design/app/components/design/DrawOverlay.tsx +1 -1
  69. package/corpus/templates/design/app/components/design/EditPanel.tsx +1788 -52
  70. package/corpus/templates/design/app/components/design/LayersPanel.tsx +114 -33
  71. package/corpus/templates/design/app/components/design/LocalSourceEditBanner.tsx +157 -0
  72. package/corpus/templates/design/app/components/design/MotionDock.tsx +1071 -0
  73. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +1053 -99
  74. package/corpus/templates/design/app/components/design/ReviewPanel.tsx +766 -0
  75. package/corpus/templates/design/app/components/design/StatesPanel.tsx +581 -0
  76. package/corpus/templates/design/app/components/design/TokensPanel.tsx +578 -0
  77. package/corpus/templates/design/app/components/design/canvas-primitive-style.ts +241 -0
  78. package/corpus/templates/design/app/components/design/index.ts +16 -0
  79. package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +15 -6
  80. package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +66 -3
  81. package/corpus/templates/design/app/components/design/inspector/InspectorAiActions.tsx +145 -0
  82. package/corpus/templates/design/app/components/design/inspector/SHADER_INTEGRATION.md +42 -12
  83. package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +1 -1
  84. package/corpus/templates/design/app/hooks/useAgentEditRequest.ts +131 -0
  85. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +3 -1
  86. package/corpus/templates/design/app/i18n/zh-TW.ts +32 -0
  87. package/corpus/templates/design/app/i18n-data.ts +280 -0
  88. package/corpus/templates/design/app/pages/DesignEditor.tsx +3087 -490
  89. package/corpus/templates/design/changelog/2026-06-29-added-a-review-panel-in-the-design-editor-s-inspector-with-a.md +6 -0
  90. package/corpus/templates/design/changelog/2026-06-29-copying-and-pasting-layers-no-longer-shows-success-notificat.md +6 -0
  91. package/corpus/templates/design/changelog/2026-06-29-device-presets-in-all-screens-view-resize-the-selected-previ.md +6 -0
  92. package/corpus/templates/design/changelog/2026-06-29-layer-and-canvas-drags-keep-the-layer-list-stable-while-chan.md +6 -0
  93. package/corpus/templates/design/changelog/2026-06-29-layer-moves-can-be-undone-and-redone-without-flashing-the-ca.md +6 -0
  94. package/corpus/templates/design/changelog/2026-06-29-screen-previews-use-a-single-blue-hover-border-in-all-screen.md +6 -0
  95. package/corpus/templates/design/changelog/2026-06-29-selected-containers-show-draggable-padding-and-gap-guides-on.md +6 -0
  96. package/corpus/templates/design/changelog/2026-06-29-the-design-editor-adds-a-studio-layer-with-tokens-respon.md +6 -0
  97. package/corpus/templates/design/changelog/2026-06-30-accessibility-findings-now-offer-a-one-click-fix.md +6 -0
  98. package/corpus/templates/design/changelog/2026-06-30-component-instances-now-have-editable-props-in-the-inspe.md +6 -0
  99. package/corpus/templates/design/changelog/2026-06-30-design-token-edits-now-stay-visible-in-previews-and-token-li.md +6 -0
  100. package/corpus/templates/design/changelog/2026-06-30-inspect-code-now-shows-the-elements-opening-tag-at-a-gla.md +6 -0
  101. package/corpus/templates/design/changelog/2026-06-30-shader-fill-presets-can-now-be-applied-to-an-element.md +6 -0
  102. package/corpus/templates/design/changelog/2026-06-30-the-motion-timeline-can-now-add-a-track-to-any-element-a.md +6 -0
  103. package/corpus/templates/design/changelog/2026-06-30-visual-editor-selection-layer-paint-and-drawing-controls-are-more-reliable.md +6 -0
  104. package/corpus/templates/design/e2e/global-setup.ts +94 -2
  105. package/corpus/templates/design/e2e/helpers.ts +10 -4
  106. package/corpus/templates/design/server/db/schema.ts +123 -0
  107. package/corpus/templates/design/server/plugins/db.ts +90 -0
  108. package/corpus/templates/design/shared/builder-app.ts +297 -0
  109. package/corpus/templates/design/shared/capability-resolver.ts +123 -0
  110. package/corpus/templates/design/shared/capture-sanitize.ts +70 -0
  111. package/corpus/templates/design/shared/code-layer.ts +1016 -71
  112. package/corpus/templates/design/shared/component-model.ts +274 -0
  113. package/corpus/templates/design/shared/design-review.ts +275 -0
  114. package/corpus/templates/design/shared/design-source-capabilities.ts +286 -0
  115. package/corpus/templates/design/shared/design-state.ts +112 -0
  116. package/corpus/templates/design/shared/design-surface-index.ts +258 -0
  117. package/corpus/templates/design/shared/motion-compiler.ts +349 -0
  118. package/corpus/templates/design/shared/motion-timeline.ts +193 -0
  119. package/corpus/templates/design/shared/resolve-tweaks.ts +32 -9
  120. package/corpus/templates/design/shared/responsive-classes.ts +452 -0
  121. package/corpus/templates/design/shared/shader-fill.ts +323 -0
  122. package/corpus/templates/design/shared/source-mode.ts +245 -0
  123. package/corpus/templates/plan/app/components/plan/CanvasArea.tsx +6 -2
  124. package/corpus/templates/plan/changelog/2026-06-29-plan-canvases-open-without-an-initial-pan-and-zoom-flicker.md +6 -0
  125. package/dist/agent/production-agent.d.ts.map +1 -1
  126. package/dist/agent/production-agent.js +94 -10
  127. package/dist/agent/production-agent.js.map +1 -1
  128. package/dist/cli/design-connect.d.ts.map +1 -1
  129. package/dist/cli/design-connect.js +28 -2
  130. package/dist/cli/design-connect.js.map +1 -1
  131. package/dist/cli/skills.d.ts.map +1 -1
  132. package/dist/cli/skills.js +37 -21
  133. package/dist/cli/skills.js.map +1 -1
  134. package/dist/client/AssistantChat.d.ts +14 -0
  135. package/dist/client/AssistantChat.d.ts.map +1 -1
  136. package/dist/client/AssistantChat.js +115 -22
  137. package/dist/client/AssistantChat.js.map +1 -1
  138. package/dist/client/sse-event-processor.d.ts.map +1 -1
  139. package/dist/client/sse-event-processor.js +10 -0
  140. package/dist/client/sse-event-processor.js.map +1 -1
  141. package/dist/collab/routes.d.ts +1 -1
  142. package/dist/file-upload/actions/upload-image.d.ts +2 -2
  143. package/dist/notifications/routes.d.ts +2 -2
  144. package/dist/observability/routes.d.ts +8 -8
  145. package/dist/resources/handlers.d.ts +2 -2
  146. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  147. package/dist/server/transcribe-voice.d.ts +1 -1
  148. package/package.json +1 -1
@@ -0,0 +1,224 @@
1
+ import { defineAction } from "@agent-native/core";
2
+ import {
3
+ getRequestOrgId,
4
+ getRequestUserEmail,
5
+ } from "@agent-native/core/server/request-context";
6
+ import { assertAccess } from "@agent-native/core/sharing";
7
+ import { eq } from "drizzle-orm";
8
+ import { nanoid } from "nanoid";
9
+ import { z } from "zod";
10
+
11
+ import { getDb, schema } from "../server/db/index.js";
12
+ import "../server/db/index.js"; // ensure registerShareableResource runs
13
+ import { hasCapability } from "../shared/design-source-capabilities.js";
14
+ import type { DesignSourceCapabilities } from "../shared/design-source-capabilities.js";
15
+
16
+ /**
17
+ * Resolve the capabilities for the design's source type.
18
+ * Falls back to inline capabilities when not present in stored data.
19
+ */
20
+ function resolveCapabilities(
21
+ designData: string | null,
22
+ ): DesignSourceCapabilities | null {
23
+ if (!designData) return null;
24
+ try {
25
+ const parsed = JSON.parse(designData) as Record<string, unknown>;
26
+ if (parsed.capabilities && typeof parsed.capabilities === "object") {
27
+ return parsed.capabilities as DesignSourceCapabilities;
28
+ }
29
+ } catch {
30
+ // ignore parse errors
31
+ }
32
+ return null;
33
+ }
34
+
35
+ /**
36
+ * Maximum serialised size of a captured/replayed `captureData` payload.
37
+ * Captured DOM snapshots are arbitrary caller markup; cap them so a single
38
+ * capture can't bloat the design row (and the shareable content it feeds).
39
+ */
40
+ const CAPTURE_DATA_MAX_BYTES = 256 * 1024; // 256KB
41
+
42
+ /**
43
+ * Strip stored-XSS vectors out of an HTML/markup string before it is persisted
44
+ * and later replayed into shareable design content. Mirrors the framework's
45
+ * text-edit HTML sanitiser: removes script/style/iframe/object/embed/link/meta/
46
+ * base tags, inline `on*` handlers, and `javascript:` / `vbscript:` / `data:`
47
+ * URLs in `href` / `src` / `xlink:href`.
48
+ */
49
+ function sanitizeMarkup(html: string): string {
50
+ return html
51
+ .replace(
52
+ /<\s*(script|style|iframe|object|embed|link|meta|base)\b[\s\S]*?<\s*\/\s*\1\s*>/gi,
53
+ "",
54
+ )
55
+ .replace(
56
+ /<\s*(script|style|iframe|object|embed|link|meta|base)\b[^>]*\/?\s*>/gi,
57
+ "",
58
+ )
59
+ .replace(/\s+on[A-Za-z]+\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/g, "")
60
+ .replace(
61
+ /\s+(href|src|xlink:href)\s*=\s*(?:(["'])\s*(?:javascript|vbscript|data):[\s\S]*?\2|(?:javascript|vbscript|data):[^\s>]*)/gi,
62
+ "",
63
+ );
64
+ }
65
+
66
+ /**
67
+ * A string "looks like markup" — and is therefore worth sanitising — when it
68
+ * contains an angle-bracket tag opener or an Alpine `x-`/`@`/`:` binding that
69
+ * could carry script. Plain data strings (route names, ids) are left untouched.
70
+ */
71
+ function looksLikeMarkup(value: string): boolean {
72
+ return /<[a-zA-Z!/]/.test(value) || value.includes("</");
73
+ }
74
+
75
+ /**
76
+ * Recursively sanitise every string value inside a captured/replayed
77
+ * `captureData` object (e.g. `domHtml`, `domSnapshot`, `x-data` markup) so no
78
+ * untrusted DOM is persisted raw. Non-string leaves pass through unchanged.
79
+ */
80
+ function sanitizeCaptureData(value: unknown): unknown {
81
+ if (typeof value === "string") {
82
+ return looksLikeMarkup(value) ? sanitizeMarkup(value) : value;
83
+ }
84
+ if (Array.isArray(value)) {
85
+ return value.map((entry) => sanitizeCaptureData(entry));
86
+ }
87
+ if (value && typeof value === "object") {
88
+ const out: Record<string, unknown> = {};
89
+ for (const [k, v] of Object.entries(value as Record<string, unknown>)) {
90
+ out[k] = sanitizeCaptureData(v);
91
+ }
92
+ return out;
93
+ }
94
+ return value;
95
+ }
96
+
97
+ export default defineAction({
98
+ description:
99
+ "Capture the running app's current route, props, and API data into a " +
100
+ "design_state row of kind 'capture'. Requires the design's source to " +
101
+ "advertise the 'captureState' capability (localhost and fusion tiers). " +
102
+ "The bridge's captureState operation is called and the result is persisted " +
103
+ "as a new design state that can be loaded in the States panel. " +
104
+ "For inline designs without a live bridge, use create-design-state instead.",
105
+ schema: z.object({
106
+ designId: z.string().describe("Design project ID"),
107
+ name: z
108
+ .string()
109
+ .min(1)
110
+ .describe(
111
+ "Human-readable label for the captured state (e.g. 'Checkout – empty cart', 'Profile – logged in').",
112
+ ),
113
+ breakpoint: z
114
+ .enum(["auto", "desktop", "tablet", "mobile"])
115
+ .default("auto")
116
+ .describe("Breakpoint context active at the time of capture."),
117
+ sourceRef: z
118
+ .string()
119
+ .optional()
120
+ .describe(
121
+ "Source connection ref (routeId for localhost/fusion). Omit if the design has only one source.",
122
+ ),
123
+ route: z
124
+ .string()
125
+ .optional()
126
+ .describe("App route path at capture time (e.g. '/checkout')."),
127
+ captureData: z
128
+ .preprocess(
129
+ (v) => (typeof v === "string" ? JSON.parse(v) : v),
130
+ z.record(z.string(), z.unknown()),
131
+ )
132
+ .describe(
133
+ "The payload returned by the bridge captureState op — typically " +
134
+ "{ route, props, queryParams, apiResponses, domSnapshot }. " +
135
+ "The caller (UI or agent) must have already invoked the bridge and pass the result here.",
136
+ ),
137
+ previewRef: z
138
+ .string()
139
+ .optional()
140
+ .describe(
141
+ "Snapshot image URL or design_version id produced immediately before " +
142
+ "or after the capture (optional).",
143
+ ),
144
+ }),
145
+ run: async ({
146
+ designId,
147
+ name,
148
+ breakpoint,
149
+ sourceRef,
150
+ route,
151
+ captureData,
152
+ previewRef,
153
+ }) => {
154
+ await assertAccess("design", designId, "editor");
155
+
156
+ const db = getDb();
157
+
158
+ // Capability gate — captureState must be available for this design source.
159
+ const [design] = await db
160
+ .select({ data: schema.designs.data })
161
+ .from(schema.designs)
162
+ .where(eq(schema.designs.id, designId))
163
+ .limit(1);
164
+
165
+ if (!design) {
166
+ throw new Error(`Design '${designId}' not found.`);
167
+ }
168
+
169
+ const caps = resolveCapabilities(design.data);
170
+ if (caps !== null && !hasCapability(caps, "captureState")) {
171
+ throw new Error(
172
+ "The design's source does not support captureState. " +
173
+ "Connect Builder or a localhost bridge to enable live captures.",
174
+ );
175
+ }
176
+
177
+ const id = nanoid();
178
+ const now = new Date().toISOString();
179
+ const ownerEmail = getRequestUserEmail();
180
+ if (!ownerEmail) throw new Error("no authenticated user");
181
+ const orgId = getRequestOrgId();
182
+
183
+ // Sanitise captured DOM/markup (stored-XSS guard) and enforce a size cap so
184
+ // a single capture can't bloat the row / the shareable content it feeds.
185
+ const sanitizedCaptureData = sanitizeCaptureData(captureData);
186
+ const captureDataJson = JSON.stringify(sanitizedCaptureData);
187
+ if (Buffer.byteLength(captureDataJson, "utf8") > CAPTURE_DATA_MAX_BYTES) {
188
+ throw new Error(
189
+ `captureData exceeds the ${Math.round(
190
+ CAPTURE_DATA_MAX_BYTES / 1024,
191
+ )}KB limit. Capture a smaller DOM snapshot or trim the payload.`,
192
+ );
193
+ }
194
+
195
+ await db.insert(schema.designState).values({
196
+ id,
197
+ designId,
198
+ sourceRef: sourceRef ?? null,
199
+ name,
200
+ kind: "capture",
201
+ breakpoint,
202
+ route: route ?? null,
203
+ fixtureData: null,
204
+ captureData: captureDataJson,
205
+ previewRef: previewRef ?? null,
206
+ createdAt: now,
207
+ updatedAt: now,
208
+ ownerEmail,
209
+ orgId,
210
+ });
211
+
212
+ return {
213
+ id,
214
+ designId,
215
+ name,
216
+ kind: "capture",
217
+ breakpoint,
218
+ route: route ?? null,
219
+ sourceRef: sourceRef ?? null,
220
+ previewRef: previewRef ?? null,
221
+ createdAt: now,
222
+ };
223
+ },
224
+ });
@@ -0,0 +1,162 @@
1
+ /**
2
+ * connect-builder-app — return the Builder connection state / CTA payload for
3
+ * a design so the UI can render the appropriate inline card.
4
+ *
5
+ * This action intentionally does NOT start the OAuth / cli-auth flow; the
6
+ * existing `connect-builder` agent-chat tool owns that flow and renders the
7
+ * interactive card in chat. What this action does instead:
8
+ *
9
+ * 1. Check whether Builder is currently configured (credentials + project ID)
10
+ * via the shared `resolveBuilderStatus` helper (no credential values leak).
11
+ * 2. Return a structured payload the UI can use to decide whether to render
12
+ * an "already connected" summary, a "connect to unlock" CTA, or a
13
+ * "Builder enabled — ready to migrate" state.
14
+ *
15
+ * The `connectUrl` field is the pre-built URL that opens the Builder cli-auth
16
+ * popup from the current app origin (same shape the agent-chat plugin returns
17
+ * in the `kind: "connect-builder-card"` tool result). The UI should open this
18
+ * in a popup and poll `/builder/status` for completion, matching the existing
19
+ * connect flow.
20
+ *
21
+ * Gate: any design the caller can view is sufficient — the action is read-only
22
+ * and returns only connection-level metadata, not design content.
23
+ */
24
+
25
+ import { defineAction } from "@agent-native/core";
26
+ import { getBuilderBranchProjectId } from "@agent-native/core/server";
27
+ import { getRequestContext } from "@agent-native/core/server/request-context";
28
+ import { resolveAccess } from "@agent-native/core/sharing";
29
+ import { z } from "zod";
30
+
31
+ import "../server/db/index.js"; // ensure registerShareableResource runs
32
+ import { resolveBuilderStatus } from "../shared/builder-app.js";
33
+
34
+ // ---------------------------------------------------------------------------
35
+ // Helpers
36
+ // ---------------------------------------------------------------------------
37
+
38
+ /** The default Builder app host — mirrors the constant in builder-browser.ts. */
39
+ const DEFAULT_BUILDER_APP_HOST = "https://builder.io";
40
+
41
+ /** Resolve the Builder app host from env, matching core builder-browser.ts. */
42
+ function resolveBuilderAppHost(): string {
43
+ return (
44
+ process.env.BUILDER_APP_HOST ||
45
+ process.env.BUILDER_PUBLIC_APP_HOST ||
46
+ DEFAULT_BUILDER_APP_HOST
47
+ );
48
+ }
49
+
50
+ /**
51
+ * Build the connect URL for the current deployment's origin.
52
+ * Mirrors the shape returned by `getBuilderBrowserConnectUrl` in core, but
53
+ * without requiring the H3 event — uses the request-context origin instead.
54
+ */
55
+ function buildConnectUrl(origin: string): string {
56
+ const base = origin.replace(/\/+$/, "");
57
+ return `${base}/_agent-native/builder/connect`;
58
+ }
59
+
60
+ // ---------------------------------------------------------------------------
61
+ // Action
62
+ // ---------------------------------------------------------------------------
63
+
64
+ export default defineAction({
65
+ description:
66
+ "Return the Builder connection state and CTA payload for a design. " +
67
+ "Use this to check whether Builder is configured before offering the " +
68
+ "'Make it real' upgrade flow. Returns { connected, builderEnabled, " +
69
+ "connectUrl, appHost, branchProjectId } so the UI can render the correct " +
70
+ "inline card without making a separate status fetch. " +
71
+ "When connected is false, direct the user to the connectUrl to start the " +
72
+ "Builder OAuth flow. When builderEnabled is true, the Builder cloud agent " +
73
+ "can accept a migration job via migrate-inline-design-to-app.",
74
+ schema: z.object({
75
+ designId: z
76
+ .string()
77
+ .describe("Design project ID to check Builder connection for"),
78
+ }),
79
+ readOnly: true,
80
+ http: { method: "GET" },
81
+ run: async ({ designId }) => {
82
+ // Require at least viewer access so unauthenticated callers cannot
83
+ // probe Builder connection state.
84
+ const access = await resolveAccess("design", designId);
85
+ if (!access) {
86
+ throw new Error("Design not found");
87
+ }
88
+
89
+ const status = await resolveBuilderStatus();
90
+
91
+ // Resolve the connect URL from the current request origin so it points
92
+ // to this exact deployment (same origin as the signed connect token the
93
+ // server mints for cli-auth flows).
94
+ const origin = getRequestContext()?.requestOrigin ?? "";
95
+ const connectUrl = buildConnectUrl(origin);
96
+
97
+ const appHost = resolveBuilderAppHost();
98
+
99
+ // Surface the env-level branch project id for informational use only.
100
+ // (Credential values are never included.)
101
+ const branchProjectId =
102
+ status.branchProjectId || getBuilderBranchProjectId() || undefined;
103
+
104
+ if (!status.connected) {
105
+ return {
106
+ connected: false,
107
+ builderEnabled: false,
108
+ connectUrl,
109
+ appHost,
110
+ branchProjectId,
111
+ cta: {
112
+ kind: "connect-builder" as const,
113
+ label: "Make this a real app",
114
+ description:
115
+ "Connect Builder.io to unlock React components, live props, " +
116
+ "data states, branches, and one-click deploys.",
117
+ primaryAction: "Connect Builder.io",
118
+ connectUrl,
119
+ },
120
+ message:
121
+ "Builder is not connected. Open connectUrl to start the OAuth flow.",
122
+ };
123
+ }
124
+
125
+ if (!status.builderEnabled) {
126
+ return {
127
+ connected: true,
128
+ builderEnabled: false,
129
+ connectUrl,
130
+ appHost,
131
+ branchProjectId,
132
+ cta: {
133
+ kind: "configure-project" as const,
134
+ label: "Configure Builder project",
135
+ description:
136
+ "Builder credentials are present but no branch project is " +
137
+ "configured. Set DISPATCH_BUILDER_PROJECT_ID, " +
138
+ "BUILDER_BRANCH_PROJECT_ID, or BUILDER_PROJECT_ID to enable " +
139
+ "the cloud agent.",
140
+ primaryAction: "Open Builder settings",
141
+ connectUrl: `${appHost}/account-settings`,
142
+ },
143
+ message:
144
+ "Builder credentials are configured but no branch project ID is set. " +
145
+ "Set DISPATCH_BUILDER_PROJECT_ID to enable cloud agent migration.",
146
+ };
147
+ }
148
+
149
+ // Fully configured — ready for migration.
150
+ return {
151
+ connected: true,
152
+ builderEnabled: true,
153
+ connectUrl,
154
+ appHost,
155
+ branchProjectId,
156
+ cta: null,
157
+ message:
158
+ "Builder is connected and cloud agents are available. " +
159
+ "Call migrate-inline-design-to-app to generate a real React app branch.",
160
+ };
161
+ },
162
+ });