framer-dalton 0.0.21 → 0.0.22

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.
@@ -1,19 +1,19 @@
1
- import fs2 from 'fs';
1
+ import fs4 from 'fs';
2
2
  import os from 'os';
3
3
  import path from 'path';
4
4
  import 'child_process';
5
5
  import { fileURLToPath } from 'url';
6
6
  import { createTRPCClient, httpLink } from '@trpc/client';
7
+ import crypto, { randomUUID, timingSafeEqual } from 'crypto';
7
8
  import http from 'http';
8
9
  import { createHTTPHandler } from '@trpc/server/adapters/standalone';
9
10
  import { initTRPC, TRPCError } from '@trpc/server';
10
11
  import { z } from 'zod';
11
12
  import { connect } from 'framer-api';
12
- import crypto, { randomUUID } from 'crypto';
13
13
  import { createRequire } from 'module';
14
14
  import * as vm from 'vm';
15
15
 
16
- /* @framer/ai relay server v0.0.21 */
16
+ /* @framer/ai relay server v0.0.22 */
17
17
  var __defProp = Object.defineProperty;
18
18
  var __knownSymbol = (name2, symbol) => (symbol = Symbol[name2]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name2);
19
19
  var __typeError = (msg) => {
@@ -90,17 +90,67 @@ var initialized = false;
90
90
  function ensureLogDir() {
91
91
  if (initialized) return;
92
92
  const dir = path.dirname(logPath);
93
- fs2.mkdirSync(dir, { recursive: true });
93
+ fs4.mkdirSync(dir, { recursive: true });
94
94
  initialized = true;
95
95
  }
96
96
  __name(ensureLogDir, "ensureLogDir");
97
97
  function log(message) {
98
98
  ensureLogDir();
99
99
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
100
- fs2.appendFileSync(logPath, `${timestamp} ${message}
100
+ fs4.appendFileSync(logPath, `${timestamp} ${message}
101
101
  `);
102
102
  }
103
103
  __name(log, "log");
104
+ function getConfigDir() {
105
+ if (process.env.XDG_CONFIG_HOME) {
106
+ return path.join(process.env.XDG_CONFIG_HOME, "framer");
107
+ }
108
+ if (process.platform === "win32") {
109
+ return path.join(process.env.APPDATA || os.homedir(), "framer");
110
+ }
111
+ return path.join(os.homedir(), ".config", "framer");
112
+ }
113
+ __name(getConfigDir, "getConfigDir");
114
+ function ensureConfigDir() {
115
+ const configDir = getConfigDir();
116
+ fs4.mkdirSync(configDir, { recursive: true, mode: 448 });
117
+ }
118
+ __name(ensureConfigDir, "ensureConfigDir");
119
+
120
+ // src/config/relay-token.ts
121
+ function getRelayTokenPath() {
122
+ return path.join(getConfigDir(), "relay-token");
123
+ }
124
+ __name(getRelayTokenPath, "getRelayTokenPath");
125
+ function generateRelayToken() {
126
+ return crypto.randomBytes(32).toString("base64url");
127
+ }
128
+ __name(generateRelayToken, "generateRelayToken");
129
+ function readRelayToken() {
130
+ try {
131
+ const token = fs4.readFileSync(getRelayTokenPath(), "utf-8").trim();
132
+ return token.length > 0 ? token : null;
133
+ } catch {
134
+ return null;
135
+ }
136
+ }
137
+ __name(readRelayToken, "readRelayToken");
138
+ function writeRelayToken(token) {
139
+ ensureConfigDir();
140
+ fs4.writeFileSync(getRelayTokenPath(), token, { mode: 384 });
141
+ }
142
+ __name(writeRelayToken, "writeRelayToken");
143
+ function clearRelayToken(expectedToken) {
144
+ const filePath = getRelayTokenPath();
145
+ if (expectedToken) {
146
+ const currentToken = readRelayToken();
147
+ if (currentToken !== expectedToken) {
148
+ return;
149
+ }
150
+ }
151
+ fs4.rmSync(filePath, { force: true });
152
+ }
153
+ __name(clearRelayToken, "clearRelayToken");
104
154
  function debug(tag, message) {
105
155
  return;
106
156
  }
@@ -109,7 +159,7 @@ __name(debug, "debug");
109
159
  // src/version.ts
110
160
  var VERSION = (
111
161
  // typeof is used to ensure this can be used just via tsx or node etc. without build
112
- "0.0.21"
162
+ "0.0.22"
113
163
  );
114
164
 
115
165
  // src/relay-client.ts
@@ -119,7 +169,14 @@ var RELAY_PORT = Number(process.env.FRAMER_CLI_PORT) || 19988;
119
169
  createTRPCClient({
120
170
  links: [
121
171
  httpLink({
122
- url: `http://127.0.0.1:${RELAY_PORT}`
172
+ url: `http://127.0.0.1:${RELAY_PORT}`,
173
+ headers() {
174
+ const token = readRelayToken();
175
+ if (!token) {
176
+ return {};
177
+ }
178
+ return { Authorization: `Bearer ${token}` };
179
+ }
123
180
  })
124
181
  ]
125
182
  });
@@ -186,11 +243,11 @@ var ScopedFS = class {
186
243
  // Sync methods
187
244
  readFileSync = /* @__PURE__ */ __name((filePath, options) => {
188
245
  const resolved = this.resolvePath(filePath.toString());
189
- return fs2.readFileSync(resolved, options);
246
+ return fs4.readFileSync(resolved, options);
190
247
  }, "readFileSync");
191
248
  writeFileSync = /* @__PURE__ */ __name((filePath, data, options) => {
192
249
  const resolved = this.resolvePath(filePath.toString());
193
- fs2.writeFileSync(
250
+ fs4.writeFileSync(
194
251
  resolved,
195
252
  data,
196
253
  options
@@ -198,7 +255,7 @@ var ScopedFS = class {
198
255
  }, "writeFileSync");
199
256
  appendFileSync = /* @__PURE__ */ __name((filePath, data, options) => {
200
257
  const resolved = this.resolvePath(filePath.toString());
201
- fs2.appendFileSync(
258
+ fs4.appendFileSync(
202
259
  resolved,
203
260
  data,
204
261
  options
@@ -206,76 +263,76 @@ var ScopedFS = class {
206
263
  }, "appendFileSync");
207
264
  readdirSync = /* @__PURE__ */ __name((dirPath, options) => {
208
265
  const resolved = this.resolvePath(dirPath.toString());
209
- return fs2.readdirSync(resolved, options);
266
+ return fs4.readdirSync(resolved, options);
210
267
  }, "readdirSync");
211
268
  mkdirSync = /* @__PURE__ */ __name((dirPath, options) => {
212
269
  const resolved = this.resolvePath(dirPath.toString());
213
- return fs2.mkdirSync(resolved, options);
270
+ return fs4.mkdirSync(resolved, options);
214
271
  }, "mkdirSync");
215
272
  rmdirSync = /* @__PURE__ */ __name((dirPath, options) => {
216
273
  const resolved = this.resolvePath(dirPath.toString());
217
- fs2.rmdirSync(resolved, options);
274
+ fs4.rmdirSync(resolved, options);
218
275
  }, "rmdirSync");
219
276
  unlinkSync = /* @__PURE__ */ __name((filePath) => {
220
277
  const resolved = this.resolvePath(filePath.toString());
221
- fs2.unlinkSync(resolved);
278
+ fs4.unlinkSync(resolved);
222
279
  }, "unlinkSync");
223
280
  statSync = /* @__PURE__ */ __name((filePath, options) => {
224
281
  const resolved = this.resolvePath(filePath.toString());
225
- return fs2.statSync(resolved, options);
282
+ return fs4.statSync(resolved, options);
226
283
  }, "statSync");
227
284
  lstatSync = /* @__PURE__ */ __name((filePath, options) => {
228
285
  const resolved = this.resolvePath(filePath.toString());
229
- return fs2.lstatSync(resolved, options);
286
+ return fs4.lstatSync(resolved, options);
230
287
  }, "lstatSync");
231
288
  existsSync = /* @__PURE__ */ __name((filePath) => {
232
289
  try {
233
290
  const resolved = this.resolvePath(filePath.toString());
234
- return fs2.existsSync(resolved);
291
+ return fs4.existsSync(resolved);
235
292
  } catch {
236
293
  return false;
237
294
  }
238
295
  }, "existsSync");
239
296
  accessSync = /* @__PURE__ */ __name((filePath, mode) => {
240
297
  const resolved = this.resolvePath(filePath.toString());
241
- fs2.accessSync(resolved, mode);
298
+ fs4.accessSync(resolved, mode);
242
299
  }, "accessSync");
243
300
  copyFileSync = /* @__PURE__ */ __name((src, dest, mode) => {
244
301
  const resolvedSrc = this.resolvePath(src.toString());
245
302
  const resolvedDest = this.resolvePath(dest.toString());
246
- fs2.copyFileSync(resolvedSrc, resolvedDest, mode);
303
+ fs4.copyFileSync(resolvedSrc, resolvedDest, mode);
247
304
  }, "copyFileSync");
248
305
  renameSync = /* @__PURE__ */ __name((oldPath, newPath) => {
249
306
  const resolvedOld = this.resolvePath(oldPath.toString());
250
307
  const resolvedNew = this.resolvePath(newPath.toString());
251
- fs2.renameSync(resolvedOld, resolvedNew);
308
+ fs4.renameSync(resolvedOld, resolvedNew);
252
309
  }, "renameSync");
253
310
  rmSync = /* @__PURE__ */ __name((filePath, options) => {
254
311
  const resolved = this.resolvePath(filePath.toString());
255
- fs2.rmSync(resolved, options);
312
+ fs4.rmSync(resolved, options);
256
313
  }, "rmSync");
257
314
  // Stream methods
258
315
  createReadStream = /* @__PURE__ */ __name((filePath, options) => {
259
316
  const resolved = this.resolvePath(filePath.toString());
260
- return fs2.createReadStream(resolved, options);
317
+ return fs4.createReadStream(resolved, options);
261
318
  }, "createReadStream");
262
319
  createWriteStream = /* @__PURE__ */ __name((filePath, options) => {
263
320
  const resolved = this.resolvePath(filePath.toString());
264
- return fs2.createWriteStream(resolved, options);
321
+ return fs4.createWriteStream(resolved, options);
265
322
  }, "createWriteStream");
266
323
  // Promise-based API (fs.promises equivalent)
267
324
  get promises() {
268
325
  return {
269
326
  readFile: /* @__PURE__ */ __name(async (filePath, options) => {
270
327
  const resolved = this.resolvePath(filePath.toString());
271
- return fs2.promises.readFile(
328
+ return fs4.promises.readFile(
272
329
  resolved,
273
330
  options
274
331
  );
275
332
  }, "readFile"),
276
333
  writeFile: /* @__PURE__ */ __name(async (filePath, data, options) => {
277
334
  const resolved = this.resolvePath(filePath.toString());
278
- return fs2.promises.writeFile(
335
+ return fs4.promises.writeFile(
279
336
  resolved,
280
337
  data,
281
338
  options
@@ -283,7 +340,7 @@ var ScopedFS = class {
283
340
  }, "writeFile"),
284
341
  appendFile: /* @__PURE__ */ __name(async (filePath, data, options) => {
285
342
  const resolved = this.resolvePath(filePath.toString());
286
- return fs2.promises.appendFile(
343
+ return fs4.promises.appendFile(
287
344
  resolved,
288
345
  data,
289
346
  options
@@ -291,48 +348,48 @@ var ScopedFS = class {
291
348
  }, "appendFile"),
292
349
  readdir: /* @__PURE__ */ __name(async (dirPath, options) => {
293
350
  const resolved = this.resolvePath(dirPath.toString());
294
- return fs2.promises.readdir(
351
+ return fs4.promises.readdir(
295
352
  resolved,
296
353
  options
297
354
  );
298
355
  }, "readdir"),
299
356
  mkdir: /* @__PURE__ */ __name(async (dirPath, options) => {
300
357
  const resolved = this.resolvePath(dirPath.toString());
301
- return fs2.promises.mkdir(resolved, options);
358
+ return fs4.promises.mkdir(resolved, options);
302
359
  }, "mkdir"),
303
360
  rmdir: /* @__PURE__ */ __name(async (dirPath, options) => {
304
361
  const resolved = this.resolvePath(dirPath.toString());
305
- return fs2.promises.rmdir(resolved, options);
362
+ return fs4.promises.rmdir(resolved, options);
306
363
  }, "rmdir"),
307
364
  unlink: /* @__PURE__ */ __name(async (filePath) => {
308
365
  const resolved = this.resolvePath(filePath.toString());
309
- return fs2.promises.unlink(resolved);
366
+ return fs4.promises.unlink(resolved);
310
367
  }, "unlink"),
311
368
  stat: /* @__PURE__ */ __name(async (filePath, options) => {
312
369
  const resolved = this.resolvePath(filePath.toString());
313
- return fs2.promises.stat(resolved, options);
370
+ return fs4.promises.stat(resolved, options);
314
371
  }, "stat"),
315
372
  access: /* @__PURE__ */ __name(async (filePath, mode) => {
316
373
  const resolved = this.resolvePath(filePath.toString());
317
- return fs2.promises.access(resolved, mode);
374
+ return fs4.promises.access(resolved, mode);
318
375
  }, "access"),
319
376
  copyFile: /* @__PURE__ */ __name(async (src, dest, mode) => {
320
377
  const resolvedSrc = this.resolvePath(src.toString());
321
378
  const resolvedDest = this.resolvePath(dest.toString());
322
- return fs2.promises.copyFile(resolvedSrc, resolvedDest, mode);
379
+ return fs4.promises.copyFile(resolvedSrc, resolvedDest, mode);
323
380
  }, "copyFile"),
324
381
  rename: /* @__PURE__ */ __name(async (oldPath, newPath) => {
325
382
  const resolvedOld = this.resolvePath(oldPath.toString());
326
383
  const resolvedNew = this.resolvePath(newPath.toString());
327
- return fs2.promises.rename(resolvedOld, resolvedNew);
384
+ return fs4.promises.rename(resolvedOld, resolvedNew);
328
385
  }, "rename"),
329
386
  rm: /* @__PURE__ */ __name(async (filePath, options) => {
330
387
  const resolved = this.resolvePath(filePath.toString());
331
- return fs2.promises.rm(resolved, options);
388
+ return fs4.promises.rm(resolved, options);
332
389
  }, "rm")
333
390
  };
334
391
  }
335
- constants = fs2.constants;
392
+ constants = fs4.constants;
336
393
  };
337
394
 
338
395
  // src/execute.ts
@@ -438,7 +495,7 @@ async function execute(session, code, options = {}) {
438
495
  URLSearchParams,
439
496
  TextEncoder,
440
497
  TextDecoder,
441
- crypto,
498
+ crypto: crypto,
442
499
  AbortController,
443
500
  AbortSignal,
444
501
  structuredClone
@@ -540,21 +597,6 @@ function formatValue(value) {
540
597
  }
541
598
  }
542
599
  __name(formatValue, "formatValue");
543
- function getConfigDir() {
544
- if (process.env.XDG_CONFIG_HOME) {
545
- return path.join(process.env.XDG_CONFIG_HOME, "framer");
546
- }
547
- if (process.platform === "win32") {
548
- return path.join(process.env.APPDATA || os.homedir(), "framer");
549
- }
550
- return path.join(os.homedir(), ".config", "framer");
551
- }
552
- __name(getConfigDir, "getConfigDir");
553
- function ensureConfigDir() {
554
- const configDir = getConfigDir();
555
- fs2.mkdirSync(configDir, { recursive: true, mode: 448 });
556
- }
557
- __name(ensureConfigDir, "ensureConfigDir");
558
600
  var DEFAULT_FS_POLL_INTERVAL_MS = 1e3;
559
601
  var fsPollIntervalMs = DEFAULT_FS_POLL_INTERVAL_MS;
560
602
  var SettingsWatcher = class {
@@ -566,7 +608,7 @@ var SettingsWatcher = class {
566
608
  __name(this, "SettingsWatcher");
567
609
  }
568
610
  start(callback) {
569
- fs2.watchFile(
611
+ fs4.watchFile(
570
612
  this.settingsPath,
571
613
  { persistent: false, interval: fsPollIntervalMs },
572
614
  (curr, prev) => {
@@ -583,7 +625,7 @@ var SettingsWatcher = class {
583
625
  return this;
584
626
  }
585
627
  stop() {
586
- fs2.unwatchFile(this.settingsPath);
628
+ fs4.unwatchFile(this.settingsPath);
587
629
  return this;
588
630
  }
589
631
  };
@@ -620,7 +662,7 @@ __name(getSettings, "getSettings");
620
662
  function readSettingsFile() {
621
663
  const settingsPath = getSettingsPath();
622
664
  try {
623
- const raw = JSON.parse(fs2.readFileSync(settingsPath, "utf-8"));
665
+ const raw = JSON.parse(fs4.readFileSync(settingsPath, "utf-8"));
624
666
  const result = SettingsFileSchema.parse(raw);
625
667
  return {
626
668
  machineId: result.machineId ?? DEFAULT_SETTINGS.machineId,
@@ -639,7 +681,7 @@ function setSettings(newSettings) {
639
681
  __name(setSettings, "setSettings");
640
682
  function writeSettingsFile(settings2) {
641
683
  ensureConfigDir();
642
- fs2.writeFileSync(getSettingsPath(), JSON.stringify(settings2, null, " "), {
684
+ fs4.writeFileSync(getSettingsPath(), JSON.stringify(settings2, null, " "), {
643
685
  mode: 384
644
686
  });
645
687
  }
@@ -1205,12 +1247,74 @@ var appRouter = t.router({
1205
1247
  var IDLE_TIMEOUT_MS = 24 * 60 * 60 * 1e3;
1206
1248
  var IDLE_CHECK_INTERVAL_MS = 60 * 1e3;
1207
1249
  var trpcHandler = createHTTPHandler({ router: appRouter });
1250
+ function getHostHeader(req) {
1251
+ const host = req.headers.host;
1252
+ if (typeof host === "string") {
1253
+ return host;
1254
+ }
1255
+ return null;
1256
+ }
1257
+ __name(getHostHeader, "getHostHeader");
1258
+ function isAllowedHost(hostHeader, listeningPort) {
1259
+ if (!hostHeader || listeningPort === null) {
1260
+ return false;
1261
+ }
1262
+ const normalizedHost = hostHeader.trim().toLowerCase();
1263
+ return normalizedHost === `127.0.0.1:${listeningPort}`;
1264
+ }
1265
+ __name(isAllowedHost, "isAllowedHost");
1266
+ function getAuthorizationHeader(req) {
1267
+ const header = req.headers.authorization;
1268
+ if (typeof header === "string") {
1269
+ return header;
1270
+ }
1271
+ return null;
1272
+ }
1273
+ __name(getAuthorizationHeader, "getAuthorizationHeader");
1274
+ function isAuthorized(authorizationHeader, expectedToken) {
1275
+ if (!authorizationHeader) {
1276
+ return false;
1277
+ }
1278
+ const match = /^Bearer\s+(.+)$/i.exec(authorizationHeader.trim());
1279
+ if (!match) {
1280
+ return false;
1281
+ }
1282
+ const providedToken = match[1] ?? "";
1283
+ const expectedBuffer = Buffer.from(expectedToken);
1284
+ const providedBuffer = Buffer.from(providedToken);
1285
+ if (expectedBuffer.length !== providedBuffer.length) {
1286
+ return false;
1287
+ }
1288
+ return timingSafeEqual(expectedBuffer, providedBuffer);
1289
+ }
1290
+ __name(isAuthorized, "isAuthorized");
1291
+ function getListeningPort(server) {
1292
+ const address = server.address();
1293
+ if (!address || typeof address === "string") {
1294
+ return null;
1295
+ }
1296
+ return address.port;
1297
+ }
1298
+ __name(getListeningPort, "getListeningPort");
1208
1299
  async function startRelayServer(port = RELAY_PORT) {
1209
1300
  let lastActivityAt = Date.now();
1301
+ const relayToken = generateRelayToken();
1210
1302
  const server = http.createServer((req, res) => {
1303
+ const listeningPort = getListeningPort(server);
1304
+ if (!isAllowedHost(getHostHeader(req), listeningPort)) {
1305
+ res.writeHead(403).end("Forbidden");
1306
+ return;
1307
+ }
1308
+ if (!isAuthorized(getAuthorizationHeader(req), relayToken)) {
1309
+ res.writeHead(401).end("Unauthorized");
1310
+ return;
1311
+ }
1211
1312
  lastActivityAt = Date.now();
1212
1313
  trpcHandler(req, res);
1213
1314
  });
1315
+ server.on("close", () => {
1316
+ clearRelayToken(relayToken);
1317
+ });
1214
1318
  const idleCheck = setInterval(() => {
1215
1319
  const idleMs = Date.now() - lastActivityAt;
1216
1320
  if (idleMs >= IDLE_TIMEOUT_MS) {
@@ -1225,9 +1329,14 @@ async function startRelayServer(port = RELAY_PORT) {
1225
1329
  }, IDLE_CHECK_INTERVAL_MS);
1226
1330
  idleCheck.unref();
1227
1331
  return new Promise((resolve, reject) => {
1228
- server.on("error", reject);
1332
+ server.on("error", (error) => {
1333
+ clearRelayToken(relayToken);
1334
+ reject(error);
1335
+ });
1229
1336
  server.listen(port, "127.0.0.1", () => {
1230
- log(`started v${VERSION} on port ${port}`);
1337
+ writeRelayToken(relayToken);
1338
+ const listeningPort = getListeningPort(server) ?? port;
1339
+ log(`started v${VERSION} on port ${listeningPort}`);
1231
1340
  resolve(server);
1232
1341
  });
1233
1342
  });
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: {{SKILL_NAME}}
3
- description: "Project-scoped Framer canvas editing skill for project {{PROJECT_ID}}. Very important: never load this skill without having already read the `framer` skill and without having already run `session new`, which will dynamically update this skill."
3
+ description: "Project-scoped Framer skill for project {{PROJECT_ID}}. Covers canvas editing, project reads, change review, publishing, image sourcing, and component operations. Very important: never load this skill without having already read the `framer` skill and without having already run `session new`, which will dynamically update this skill."
4
4
  allowed-tools: ["Bash(npx framer-dalton:*)", "Bash(npx framer-dalton@latest:*)", "Read({{FRAMER_TEMPORARY_DIR}}/*)", "Write({{FRAMER_TEMPORARY_DIR}}/*)"]
5
5
  ---
6
6
 
@@ -13,14 +13,10 @@ allowed-tools: ["Bash(npx framer-dalton:*)", "Bash(npx framer-dalton@latest:*)",
13
13
 
14
14
  - For design/layout work, do not use low-level node APIs (`createNode`, `setAttributes`, `setRect`, etc.). Use the canvas editing flow (`read-project` + `apply-changes`) with the embedded prompt and project context in this skill.
15
15
  - During normal task execution, do not call `framer.getAgentSystemPrompt()` or `framer.getAgentContext()`. This skill already includes `getAgentContext({ pagePath: "/" })`.
16
- - `npx framer-dalton read-project -s <sessionId> -q <queries> -p <pagePath>` reads project state. Start with page structure only, then request additional targeted queries only if needed. Query types are documented in the embedded prompt below. Never guess names for examples, icon sets, or fonts; look them up first.
17
- - `npx framer-dalton apply-changes -s <sessionId> -p <pagePath> -e <dsl>` applies canvas DSL changes. After applying changes, re-read project state to inspect results and iterate with `read-project` + `apply-changes` until accurate.
18
- - Request examples only when needed, and only after inspecting page structure first. Keep example queries targeted and minimal.
19
- - Use screenshots only when visual verification is necessary and cannot be confirmed from state reads. Do not use them for initial inspection or between every small change.
20
- - When setting text content, use raw Unicode characters directly (for example `->`, not `\u2192`).
21
- - Regenerate the embedded prompt and context by running `session new` again for the same project.
22
- - In rich text, block-level properties (for example line height or alignment) belong on block or parent targets, not `TextRun` targets.
23
- - `IconNode` `$control__icon` is immutable after creation. To change it, delete and recreate the node with the desired icon value.
16
+ - `read-project` and `apply-changes` are one-liner CLI shortcuts for the high-frequency canvas-editing methods. Use them to read state and apply DSL inline without writing a file first:
17
+ - `npx framer-dalton read-project -s <sessionId> -q <queries> -p <pagePath>` equivalent to `framer.readProjectForAgent(queries, { pagePath })`. Query types are documented in the embedded prompt below.
18
+ - `npx framer-dalton apply-changes -s <sessionId> -p <pagePath> -e <dsl>` equivalent to `framer.applyAgentChanges(dsl, { pagePath })`.
19
+ - The embedded prompt below also references other agent-surface methods (`reviewChangesForAgent`, `publishForAgent`, `queryImagesForAgent`, `flattenComponentInstanceForAgent`, `makeExternalComponentLocalForAgent`, and `getNodeForAgent` / `getNodesForAgent` / `getNodesOfTypesForAgent` / `getScopeNodeForAgent` / `getGroundNodeForAgent` / `getParentNodeForAgent` / `getAncestorsForAgent`). These have no dedicated shortcut; invoke via `npx framer-dalton exec -s <sessionId> -e 'console.log(await framer.<method>(<args>))'`.
24
20
 
25
21
  ## Workflow Loop
26
22
 
@@ -42,11 +38,7 @@ This ensures progress is streamed to the user in small, visible increments.
42
38
 
43
39
  ## Live Agent System Prompt
44
40
 
45
- This is the static canvas-editing prompt returned by `framer.getAgentSystemPrompt()`. It is the sole documentation for:
46
-
47
- - command syntax for `apply-changes`
48
- - query types and parameters for `read-project`
49
- - design rules and examples used by the canvas editing flow
41
+ This is the static prompt returned by `framer.getAgentSystemPrompt()`.
50
42
 
51
43
  {{CANVAS_PROMPT}}
52
44