framer-dalton 0.0.25 → 0.0.27
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/README.md +1 -1
- package/dist/cli.js +346 -311
- package/dist/start-relay-server.js +111 -27
- package/docs/skills/framer-project.md +36 -19
- package/docs/skills/framer.md +1 -1
- package/package.json +8 -7
|
@@ -14,7 +14,7 @@ import { z } from 'zod';
|
|
|
14
14
|
import { createRequire } from 'module';
|
|
15
15
|
import * as vm from 'vm';
|
|
16
16
|
|
|
17
|
-
/* @framer/ai relay server v0.0.
|
|
17
|
+
/* @framer/ai relay server v0.0.27 */
|
|
18
18
|
var __defProp = Object.defineProperty;
|
|
19
19
|
var __knownSymbol = (name2, symbol) => (symbol = Symbol[name2]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name2);
|
|
20
20
|
var __typeError = (msg) => {
|
|
@@ -160,7 +160,7 @@ __name(debug, "debug");
|
|
|
160
160
|
// src/version.ts
|
|
161
161
|
var VERSION = (
|
|
162
162
|
// typeof is used to ensure this can be used just via tsx or node etc. without build
|
|
163
|
-
"0.0.
|
|
163
|
+
"0.0.27"
|
|
164
164
|
);
|
|
165
165
|
|
|
166
166
|
// src/relay-client.ts
|
|
@@ -182,6 +182,35 @@ createTRPCClient({
|
|
|
182
182
|
]
|
|
183
183
|
});
|
|
184
184
|
|
|
185
|
+
// src/agent-thread-context.ts
|
|
186
|
+
var MAX_AGENT_THREAD_ID_LENGTH = 256;
|
|
187
|
+
var MAX_AGENT_PROVIDER_LENGTH = 64;
|
|
188
|
+
var SAFE_VALUE_PATTERN = /^[A-Za-z0-9._:-]+$/;
|
|
189
|
+
function normalizeValue(value, maxLength) {
|
|
190
|
+
const trimmed = value?.trim();
|
|
191
|
+
if (!trimmed) return void 0;
|
|
192
|
+
if (trimmed.length > maxLength) return void 0;
|
|
193
|
+
if (!SAFE_VALUE_PATTERN.test(trimmed)) return void 0;
|
|
194
|
+
return trimmed;
|
|
195
|
+
}
|
|
196
|
+
__name(normalizeValue, "normalizeValue");
|
|
197
|
+
function normalizeAgentThreadContext(input) {
|
|
198
|
+
const agentThreadId = normalizeValue(
|
|
199
|
+
input.agentThreadId,
|
|
200
|
+
MAX_AGENT_THREAD_ID_LENGTH
|
|
201
|
+
);
|
|
202
|
+
if (!agentThreadId) return {};
|
|
203
|
+
const agentProvider = normalizeValue(
|
|
204
|
+
input.agentProvider,
|
|
205
|
+
MAX_AGENT_PROVIDER_LENGTH
|
|
206
|
+
);
|
|
207
|
+
return {
|
|
208
|
+
agentThreadId,
|
|
209
|
+
...agentProvider ? { agentProvider } : {}
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
__name(normalizeAgentThreadContext, "normalizeAgentThreadContext");
|
|
213
|
+
|
|
185
214
|
// src/connection-errors.ts
|
|
186
215
|
var CONNECTION_ERROR_PATTERNS = [
|
|
187
216
|
"Connection closed",
|
|
@@ -198,8 +227,15 @@ var CONNECTION_ERROR_PATTERNS = [
|
|
|
198
227
|
// Our own check in executor.ts
|
|
199
228
|
"Execution timed out after",
|
|
200
229
|
// Likely dead connection
|
|
201
|
-
"Session expired"
|
|
230
|
+
"Session expired",
|
|
202
231
|
// Server-side session expiry
|
|
232
|
+
// Typed page-death (PROJECT_CLOSED) for the string fallback; isRetryableError is the primary path.
|
|
233
|
+
"Project session crashed",
|
|
234
|
+
// PageCrashedError (renderer crash / OOM)
|
|
235
|
+
"Project was closed",
|
|
236
|
+
// StaleSessionError (page closed/reaped)
|
|
237
|
+
"Session was recycled"
|
|
238
|
+
// SessionRecycledError (destroy->respawn reconnect race)
|
|
203
239
|
];
|
|
204
240
|
function isConnectionError(errorMessage) {
|
|
205
241
|
return CONNECTION_ERROR_PATTERNS.some(
|
|
@@ -207,6 +243,18 @@ function isConnectionError(errorMessage) {
|
|
|
207
243
|
);
|
|
208
244
|
}
|
|
209
245
|
__name(isConnectionError, "isConnectionError");
|
|
246
|
+
function isRetryableError(err) {
|
|
247
|
+
if (err && typeof err === "object") {
|
|
248
|
+
const e = err;
|
|
249
|
+
if (e.retryable === true) return true;
|
|
250
|
+
if (e.code === "PROJECT_CLOSED") return true;
|
|
251
|
+
if (typeof e.message === "string" && isConnectionError(e.message))
|
|
252
|
+
return true;
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
return typeof err === "string" && isConnectionError(err);
|
|
256
|
+
}
|
|
257
|
+
__name(isRetryableError, "isRetryableError");
|
|
210
258
|
var AUTH_ERROR_PATTERNS = ["does not have access", "UNAUTHORIZED"];
|
|
211
259
|
function isAuthError(errorMessage) {
|
|
212
260
|
return AUTH_ERROR_PATTERNS.some((p) => errorMessage.includes(p));
|
|
@@ -541,20 +589,37 @@ async function execute(session, code, options = {}) {
|
|
|
541
589
|
return { output };
|
|
542
590
|
} catch (err) {
|
|
543
591
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
544
|
-
|
|
592
|
+
const retryable = isRetryableError(err);
|
|
593
|
+
if (retryable || isConnectionError(errorMessage)) {
|
|
545
594
|
session.connection.markDisconnected();
|
|
546
595
|
}
|
|
547
596
|
const errorRef = getErrorRef(err);
|
|
548
597
|
return {
|
|
549
598
|
output,
|
|
550
599
|
error: errorMessage,
|
|
551
|
-
...errorRef ? { errorRef } : {}
|
|
600
|
+
...errorRef ? { errorRef } : {},
|
|
601
|
+
...retryable ? { retryable: true } : {}
|
|
552
602
|
};
|
|
553
603
|
} finally {
|
|
554
604
|
if (timeoutId) clearTimeout(timeoutId);
|
|
555
605
|
}
|
|
556
606
|
}
|
|
557
607
|
__name(execute, "execute");
|
|
608
|
+
function isReconnectable(err) {
|
|
609
|
+
return isRetryableError(err) || isConnectionError(err instanceof Error ? err.message : String(err));
|
|
610
|
+
}
|
|
611
|
+
__name(isReconnectable, "isReconnectable");
|
|
612
|
+
function connectionFailureResult(prefix, err) {
|
|
613
|
+
const inner = err instanceof Error ? err.message : String(err);
|
|
614
|
+
const errorRef = getErrorRef(err);
|
|
615
|
+
return {
|
|
616
|
+
output: [],
|
|
617
|
+
error: `${prefix}: ${inner}`,
|
|
618
|
+
...errorRef ? { errorRef } : {},
|
|
619
|
+
...isRetryableError(err) ? { retryable: true } : {}
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
__name(connectionFailureResult, "connectionFailureResult");
|
|
558
623
|
async function executeWithReconnect(session, code, options, execId) {
|
|
559
624
|
if (!session.connection.isConnected()) {
|
|
560
625
|
log(
|
|
@@ -563,17 +628,27 @@ async function executeWithReconnect(session, code, options, execId) {
|
|
|
563
628
|
try {
|
|
564
629
|
await session.connection.reconnect();
|
|
565
630
|
} catch (err) {
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
631
|
+
if (!isReconnectable(err)) {
|
|
632
|
+
return connectionFailureResult(
|
|
633
|
+
"Failed to get connection for session",
|
|
634
|
+
err
|
|
635
|
+
);
|
|
636
|
+
}
|
|
637
|
+
log(
|
|
638
|
+
`exec.reconnect.retry exec=${execId} session=${session.id} reason="${err instanceof Error ? err.message : String(err)}"`
|
|
639
|
+
);
|
|
640
|
+
try {
|
|
641
|
+
await session.connection.reconnect();
|
|
642
|
+
} catch (err2) {
|
|
643
|
+
return connectionFailureResult(
|
|
644
|
+
"Failed to get connection for session",
|
|
645
|
+
err2
|
|
646
|
+
);
|
|
647
|
+
}
|
|
573
648
|
}
|
|
574
649
|
}
|
|
575
650
|
const result = await execute(session, code, options);
|
|
576
|
-
if (!result.error || !isConnectionError(result.error)) {
|
|
651
|
+
if (!result.error || !(result.retryable || isConnectionError(result.error))) {
|
|
577
652
|
return result;
|
|
578
653
|
}
|
|
579
654
|
log(
|
|
@@ -582,16 +657,13 @@ async function executeWithReconnect(session, code, options, execId) {
|
|
|
582
657
|
try {
|
|
583
658
|
await session.connection.reconnect();
|
|
584
659
|
} catch (err) {
|
|
585
|
-
const inner = err instanceof Error ? err.message : String(err);
|
|
586
|
-
const errorRef = getErrorRef(err);
|
|
587
660
|
log(
|
|
588
|
-
`reconnect.failed exec=${execId} session=${session.id} req=${session.connection.framer.requestId} error="${
|
|
661
|
+
`reconnect.failed exec=${execId} session=${session.id} req=${session.connection.framer.requestId} error="${err instanceof Error ? err.message : String(err)}"`
|
|
662
|
+
);
|
|
663
|
+
return connectionFailureResult(
|
|
664
|
+
"Connection lost and failed to reconnect",
|
|
665
|
+
err
|
|
589
666
|
);
|
|
590
|
-
return {
|
|
591
|
-
output: [],
|
|
592
|
-
error: `Connection lost and failed to reconnect: ${inner}`,
|
|
593
|
-
...errorRef ? { errorRef } : {}
|
|
594
|
-
};
|
|
595
667
|
}
|
|
596
668
|
log(
|
|
597
669
|
`reconnect.success exec=${execId} session=${session.id} req=${session.connection.framer.requestId}`
|
|
@@ -897,10 +969,11 @@ var Connection = class _Connection {
|
|
|
897
969
|
headlessServerUrl: this.headlessServerUrl
|
|
898
970
|
}));
|
|
899
971
|
}
|
|
900
|
-
createSession(id) {
|
|
972
|
+
createSession(id, agentThreadContext) {
|
|
901
973
|
const session = {
|
|
902
974
|
id,
|
|
903
975
|
state: {},
|
|
976
|
+
agentThreadContext,
|
|
904
977
|
lastActivityAt: Date.now(),
|
|
905
978
|
inflight: 0,
|
|
906
979
|
connection: this
|
|
@@ -971,14 +1044,17 @@ var SessionManager = class {
|
|
|
971
1044
|
}
|
|
972
1045
|
connections = [];
|
|
973
1046
|
idleCheck = null;
|
|
974
|
-
async create(projectId, userId, apiKey, headlessServerUrl) {
|
|
1047
|
+
async create(projectId, userId, apiKey, headlessServerUrl, agentThreadContext = {}) {
|
|
975
1048
|
const connection = await this.findOrCreateConnection(
|
|
976
1049
|
projectId,
|
|
977
1050
|
userId,
|
|
978
1051
|
apiKey,
|
|
979
1052
|
headlessServerUrl
|
|
980
1053
|
);
|
|
981
|
-
const session = connection.createSession(
|
|
1054
|
+
const session = connection.createSession(
|
|
1055
|
+
this.getNextSessionId(),
|
|
1056
|
+
agentThreadContext
|
|
1057
|
+
);
|
|
982
1058
|
this.startIdleCheck();
|
|
983
1059
|
return session;
|
|
984
1060
|
}
|
|
@@ -1008,6 +1084,7 @@ var SessionManager = class {
|
|
|
1008
1084
|
projectId: session.connection.projectId,
|
|
1009
1085
|
userId: session.connection.userId,
|
|
1010
1086
|
sessionId: session.connection.getServerSessionId(),
|
|
1087
|
+
...session.agentThreadContext,
|
|
1011
1088
|
durationMs,
|
|
1012
1089
|
hasError: result.error !== void 0,
|
|
1013
1090
|
...result.error ? { errorMessage: result.error } : {}
|
|
@@ -1048,7 +1125,8 @@ var SessionManager = class {
|
|
|
1048
1125
|
trackSessionDestroy({
|
|
1049
1126
|
projectId: session.connection.projectId,
|
|
1050
1127
|
userId: session.connection.userId,
|
|
1051
|
-
sessionId: session.connection.getServerSessionId()
|
|
1128
|
+
sessionId: session.connection.getServerSessionId(),
|
|
1129
|
+
...session.agentThreadContext
|
|
1052
1130
|
});
|
|
1053
1131
|
session.connection.removeSession(id);
|
|
1054
1132
|
if (!session.connection.hasSessions()) {
|
|
@@ -1202,16 +1280,20 @@ var appRouter = t.router({
|
|
|
1202
1280
|
projectId: z.string(),
|
|
1203
1281
|
apiKey: z.string(),
|
|
1204
1282
|
userId: z.string().optional(),
|
|
1205
|
-
headlessServerUrl: z.string()
|
|
1283
|
+
headlessServerUrl: z.string(),
|
|
1284
|
+
agentThreadId: z.string().optional(),
|
|
1285
|
+
agentProvider: z.string().optional()
|
|
1206
1286
|
})
|
|
1207
1287
|
).mutation(async ({ input }) => {
|
|
1208
1288
|
const start = performance.now();
|
|
1289
|
+
const agentThreadContext = normalizeAgentThreadContext(input);
|
|
1209
1290
|
try {
|
|
1210
1291
|
const session = await sessionManager.create(
|
|
1211
1292
|
input.projectId,
|
|
1212
1293
|
input.userId,
|
|
1213
1294
|
input.apiKey,
|
|
1214
|
-
input.headlessServerUrl
|
|
1295
|
+
input.headlessServerUrl,
|
|
1296
|
+
agentThreadContext
|
|
1215
1297
|
);
|
|
1216
1298
|
log(
|
|
1217
1299
|
`session.new id=${session.id} project=${input.projectId} headlessServerUrl=${input.headlessServerUrl}`
|
|
@@ -1220,6 +1302,7 @@ var appRouter = t.router({
|
|
|
1220
1302
|
projectId: input.projectId,
|
|
1221
1303
|
userId: session.connection.userId,
|
|
1222
1304
|
sessionId: session.connection.getServerSessionId(),
|
|
1305
|
+
...session.agentThreadContext,
|
|
1223
1306
|
durationMs: Math.round(performance.now() - start)
|
|
1224
1307
|
});
|
|
1225
1308
|
return { id: session.id };
|
|
@@ -1229,6 +1312,7 @@ var appRouter = t.router({
|
|
|
1229
1312
|
errorType: "session_create_error",
|
|
1230
1313
|
projectId: input.projectId,
|
|
1231
1314
|
userId: input.userId,
|
|
1315
|
+
...agentThreadContext,
|
|
1232
1316
|
errorMessage: message
|
|
1233
1317
|
});
|
|
1234
1318
|
throw new TRPCError({
|
|
@@ -36,15 +36,20 @@ Always save results you'll need again. Don't repeat API calls.
|
|
|
36
36
|
|
|
37
37
|
## Method Selection
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
Choose the highest-priority interface available for the task:
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
1. Subcommands, such as `framer-dalton read-project` and `framer-dalton apply-changes`
|
|
42
|
+
2. Agent-specific methods, such as `framer.agent.readProject`, `framer.agent.applyChanges`, and `framer.agent.publish`
|
|
43
|
+
3. Generic plugin API methods, such as top-level `framer.*` methods
|
|
44
44
|
|
|
45
|
-
Use
|
|
45
|
+
- Use `framer.agent.readProject`, `framer.agent.getNode`, `framer.agent.getNodes`, `framer.agent.getNodesOfTypes`, `framer.agent.getScopeNode`, `framer.agent.getGroundNode`, `framer.agent.getParentNode`, `framer.agent.getAncestors`, `framer.agent.serialize`, `framer.agent.serializeNodes`, and `framer.agent.paginate` for project tree reads.
|
|
46
|
+
- Use `framer.agent.readComponentControls`, `framer.agent.readIconSetControls`, `framer.agent.readIcons`, `framer.agent.readLayoutTemplateControls`, and `framer.agent.readShaderControls` for reading the controls of components, icon sets, icons, layout templates, and shaders.
|
|
47
|
+
- Use `framer.agent.applyChanges` for page and layout edits. Do not use low-level node APIs like `createNode`, `setAttributes`, or `setRect` for design/layout work.
|
|
48
|
+
- Use `framer.agent.publish` for publishing. Do not use `publish`, `getDeployments`, or `deploy` for normal agent publishing flows.
|
|
46
49
|
|
|
47
|
-
|
|
50
|
+
Use generic plugin API methods only for capabilities that do not have a subcommand or agent-specific counterpart, such as code file management, localization, and redirects.
|
|
51
|
+
|
|
52
|
+
During normal task execution, do not call `framer.agent.getSystemPrompt()` or `framer.agent.getContext()`. This skill already includes their output.
|
|
48
53
|
|
|
49
54
|
## Core Usage Principles
|
|
50
55
|
|
|
@@ -99,15 +104,15 @@ Store anything you'll reference again.
|
|
|
99
104
|
|
|
100
105
|
Use this approach only if the user asks for it. Do not mention this approach to the user otherwise.
|
|
101
106
|
|
|
102
|
-
This approach is called “Prompt the Framer agent”. With this approach, use `framer.
|
|
107
|
+
This approach is called “Prompt the Framer agent”. With this approach, use `framer.agent.startConversation()` to start a stateful design subagent. Keep the `conversationId` it returns in `state`, and call `framer.agent.continueConversation()` with it to continue the same design task.
|
|
103
108
|
|
|
104
|
-
Do not call `framer.
|
|
109
|
+
Do not call `framer.agent.getSystemPrompt()` or `framer.agent.getContext()` or `framer.agent.applyChanges()` with this approach.
|
|
105
110
|
|
|
106
111
|
Example:
|
|
107
112
|
|
|
108
113
|
```js
|
|
109
114
|
state.agent ??= {};
|
|
110
|
-
const first = await framer.
|
|
115
|
+
const first = await framer.agent.startConversation(
|
|
111
116
|
"Build me a landing page based on the attached screenshot",
|
|
112
117
|
{
|
|
113
118
|
pagePath: "/",
|
|
@@ -118,7 +123,7 @@ const first = await framer.startAgentConversation(
|
|
|
118
123
|
state.agent.conversationId = first.conversationId;
|
|
119
124
|
console.log(first.responseMessages);
|
|
120
125
|
|
|
121
|
-
const second = await framer.
|
|
126
|
+
const second = await framer.agent.continueConversation("Now make it pink", {
|
|
122
127
|
conversationId: state.agent.conversationId,
|
|
123
128
|
selectionNodeIds: ["someNodeId"],
|
|
124
129
|
// imageUrls: [...],
|
|
@@ -170,7 +175,7 @@ npx framer-dalton docs ScreenshotOptions # Show type + recursively expa
|
|
|
170
175
|
|
|
171
176
|
Collections are Framer's CMS. Each collection has fields (columns) and items (rows). Use the plugin Collection API (the methods below) for collection schema and item CRUD.
|
|
172
177
|
|
|
173
|
-
**Exception — CMS Collection Lists.** The in-canvas construct that *renders* a collection as a repeating list of cards (the "Collection List" / repeater) is an agent/page concern. Build and edit those through `
|
|
178
|
+
**Exception — CMS Collection Lists.** The in-canvas construct that *renders* a collection as a repeating list of cards (the "Collection List" / repeater) is an agent/page concern. Build and edit those through `framer.agent.readProject` and `framer.agent.applyChanges`, not the plugin API. Use the plugin Collection API to read available collections and field schema first, then use `framer.agent.applyChanges` to add the list.
|
|
174
179
|
|
|
175
180
|
#### Reading Collections
|
|
176
181
|
|
|
@@ -182,10 +187,11 @@ console.log(collections.map((c) => ({ name: c.name, id: c.id })));
|
|
|
182
187
|
// Get a specific collection by ID
|
|
183
188
|
const collection = await framer.getCollection("collection-id");
|
|
184
189
|
|
|
185
|
-
// Get fields (columns)
|
|
190
|
+
// Get fields (columns)
|
|
186
191
|
const fields = await collection.getFields();
|
|
187
|
-
console.log
|
|
188
|
-
|
|
192
|
+
// Note that a plain console.log will miss getter-backed properties
|
|
193
|
+
console.log(fields.map((f) => ({ id: f.id, name: f.name, type: f.type })));
|
|
194
|
+
// [{ id: "BnNuS2i3o", name: "Title", type: "string" }, ...]
|
|
189
195
|
|
|
190
196
|
// Get items (rows)
|
|
191
197
|
const items = await collection.getItems();
|
|
@@ -244,6 +250,17 @@ const ids = items.map((i) => i.id).reverse();
|
|
|
244
250
|
await collection.setItemOrder(ids);
|
|
245
251
|
```
|
|
246
252
|
|
|
253
|
+
#### Writing enum fields
|
|
254
|
+
|
|
255
|
+
Enum fields are **write-by-case-id, read-by-name**: a write must pass the case's `id`, but `getItems()` reads the value back as the case name. Writing `{ type: "enum", value: "New" }` (the name) is rejected — look up the id from the field definition first:
|
|
256
|
+
|
|
257
|
+
```js
|
|
258
|
+
const caseId = field.cases.find((c) => c.name === "New").id;
|
|
259
|
+
await collection.addItems([
|
|
260
|
+
{ slug: "hello-world", fieldData: { [field.id]: { type: "enum", value: caseId } } },
|
|
261
|
+
]);
|
|
262
|
+
```
|
|
263
|
+
|
|
247
264
|
#### Working with CollectionItem
|
|
248
265
|
|
|
249
266
|
```js
|
|
@@ -262,14 +279,14 @@ await item.remove();
|
|
|
262
279
|
|
|
263
280
|
### Working with Images
|
|
264
281
|
|
|
265
|
-
Canvas editing accepts image URLs directly when setting an image fill, so the typical task is to obtain a URL and
|
|
282
|
+
Canvas editing accepts image URLs directly when setting an image fill, so the typical task is to obtain a URL and use it directly with `framer.agent.applyChanges`.
|
|
266
283
|
|
|
267
284
|
There are three sources you'll typically pull URLs from:
|
|
268
285
|
|
|
269
|
-
**Stock photography.** Use `framer.
|
|
286
|
+
**Stock photography.** Use `framer.agent.queryImages` to source candidates and stash the URL you want:
|
|
270
287
|
|
|
271
288
|
```js
|
|
272
|
-
const { results } = await framer.
|
|
289
|
+
const { results } = await framer.agent.queryImages({
|
|
273
290
|
source: "unsplash",
|
|
274
291
|
query: "snow-capped mountains",
|
|
275
292
|
count: 4,
|
|
@@ -290,7 +307,7 @@ state.heroUrl = (await framer.uploadImage({
|
|
|
290
307
|
**An image already on the canvas.** Read the node and reuse its existing image URL:
|
|
291
308
|
|
|
292
309
|
```js
|
|
293
|
-
const node = await framer.
|
|
310
|
+
const node = await framer.agent.getNode({ id: "<image-node-id>" });
|
|
294
311
|
state.heroUrl = node.attributes.fill;
|
|
295
312
|
```
|
|
296
313
|
|
|
@@ -330,7 +347,7 @@ console.log(state.diagnostics);
|
|
|
330
347
|
state.component = state.codeFile.exports.find(
|
|
331
348
|
(exportItem) => exportItem.type === "component" && exportItem.isDefaultExport,
|
|
332
349
|
);
|
|
333
|
-
await framer.
|
|
350
|
+
await framer.agent.applyChanges(
|
|
334
351
|
`+ComponentInstanceNode badge parent="wrapper" component="${state.component.componentId}"; SET badge width=120 height=48;`,
|
|
335
352
|
{ pagePath: "/" },
|
|
336
353
|
);
|
package/docs/skills/framer.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: framer
|
|
3
3
|
description: >
|
|
4
|
-
Use when the user wants to design, edit, or publish a website or web page — creating layouts, editing sections, updating text or images, managing CMS collections and content, syncing external data, creating or modifying code components, managing color and text styles, handling localization, or publishing deployments. Trigger when the user mentions Framer, references their website or web pages, asks to edit designs, update site content, or work with any Framer project — even if they don't explicitly say 'Framer'.
|
|
4
|
+
Use when the user wants to design, edit, or publish a website or web page — creating layouts, editing sections, updating text or images, managing CMS collections and content, syncing external data, creating or modifying code components, managing color and text styles, handling localization, or publishing deployments. Trigger when the user mentions Framer, references their website or web pages, asks to edit designs, update site content, or work with any Framer project — even if they don't explicitly say 'Framer'.
|
|
5
5
|
**Mandatory precondition**: run `npx framer-dalton@latest setup` and let it complete **BEFORE** loading this skill.
|
|
6
6
|
allowed-tools: ["Bash(npx framer-dalton:*)", "Bash(npx framer-dalton@latest:*)", "Read({{FRAMER_TEMPORARY_DIR}}/*)", "Write({{FRAMER_TEMPORARY_DIR}}/*)"]
|
|
7
7
|
---
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "framer-dalton",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.27",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": "./dist/cli.js",
|
|
6
6
|
"main": "./dist/cli.js",
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"typecheck": "tsc --noEmit",
|
|
17
17
|
"clean": "rm -rf dist",
|
|
18
18
|
"check": "biome check .",
|
|
19
|
+
"check:fix": "biome check --write .",
|
|
19
20
|
"format": "biome format --write .",
|
|
20
21
|
"generate-types": "tsx scripts/generate-types.ts"
|
|
21
22
|
},
|
|
@@ -23,17 +24,17 @@
|
|
|
23
24
|
"@trpc/client": "^11.17.0",
|
|
24
25
|
"@trpc/server": "^11.17.0",
|
|
25
26
|
"commander": "^12.1.0",
|
|
26
|
-
"framer-api": "0.1.
|
|
27
|
-
"zod": "^4.3
|
|
27
|
+
"framer-api": "0.1.14",
|
|
28
|
+
"zod": "^4.4.3"
|
|
28
29
|
},
|
|
29
30
|
"devDependencies": {
|
|
30
31
|
"@biomejs/biome": "^2.4.13",
|
|
31
|
-
"@framerjs/framer-events": "0.0.
|
|
32
|
-
"@types/node": "24.
|
|
32
|
+
"@framerjs/framer-events": "0.0.183",
|
|
33
|
+
"@types/node": "24.12.4",
|
|
33
34
|
"tsup": "^8.0.2",
|
|
34
|
-
"tsx": "^4.
|
|
35
|
+
"tsx": "^4.22.3",
|
|
35
36
|
"typescript": "^5.9.2",
|
|
36
|
-
"vitest": "^4.
|
|
37
|
+
"vitest": "^4.1.7"
|
|
37
38
|
},
|
|
38
39
|
"packageManager": "yarn@4.13.0",
|
|
39
40
|
"engines": {
|