framer-dalton 0.0.23 → 0.0.24

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/dist/cli.js CHANGED
@@ -7,10 +7,11 @@ import http from 'http';
7
7
  import { spawn, execFile } from 'child_process';
8
8
  import { z } from 'zod';
9
9
  import os from 'os';
10
+ import { ErrorCode, FramerAPIError } from 'framer-api';
10
11
  import { fileURLToPath } from 'url';
11
12
  import { createTRPCClient, httpLink } from '@trpc/client';
12
13
 
13
- /* @framer/ai CLI v0.0.23 */
14
+ /* @framer/ai CLI v0.0.24 */
14
15
  var __defProp = Object.defineProperty;
15
16
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
16
17
 
@@ -328,7 +329,7 @@ __name(markTelemetryNoticeShown, "markTelemetryNoticeShown");
328
329
  // src/version.ts
329
330
  var VERSION = (
330
331
  // typeof is used to ensure this can be used just via tsx or node etc. without build
331
- "0.0.23"
332
+ "0.0.24"
332
333
  );
333
334
  var trackingEndpoint = "https://events.framer.com/track";
334
335
  var inProgressTrackings = /* @__PURE__ */ new Set();
@@ -418,8 +419,27 @@ function trackError(payload) {
418
419
  });
419
420
  }
420
421
  __name(trackError, "trackError");
421
-
422
- // src/utils.ts
422
+ function rehydrateFramerAPIError(error) {
423
+ if (!(error instanceof Error)) return null;
424
+ const framerApi = error.data?.framerApi;
425
+ if (!framerApi || typeof framerApi.code !== "string") return null;
426
+ const code = ErrorCode[framerApi.code] ?? ErrorCode.INTERNAL;
427
+ const options = framerApi.ref ? { ref: framerApi.ref } : void 0;
428
+ return new FramerAPIError(error.message, code, options);
429
+ }
430
+ __name(rehydrateFramerAPIError, "rehydrateFramerAPIError");
431
+ function errorWithRef(message, ref) {
432
+ const err = new Error(message);
433
+ if (ref) err.ref = ref;
434
+ return err;
435
+ }
436
+ __name(errorWithRef, "errorWithRef");
437
+ function getErrorRef(error) {
438
+ if (!(error instanceof Error)) return void 0;
439
+ const ref = error.ref;
440
+ return typeof ref === "string" && ref.length > 0 ? ref : void 0;
441
+ }
442
+ __name(getErrorRef, "getErrorRef");
423
443
  function formatError(error) {
424
444
  if (error instanceof Error) {
425
445
  return error.message;
@@ -427,6 +447,14 @@ function formatError(error) {
427
447
  return String(error);
428
448
  }
429
449
  __name(formatError, "formatError");
450
+ function formatErrorForUser(error) {
451
+ if (!(error instanceof Error)) return String(error);
452
+ const rehydrated = rehydrateFramerAPIError(error);
453
+ if (rehydrated) return rehydrated.toString();
454
+ const ref = getErrorRef(error);
455
+ return ref ? `${error.message} [ref: ${ref}]` : error.message;
456
+ }
457
+ __name(formatErrorForUser, "formatErrorForUser");
430
458
  function printJson(value) {
431
459
  console.log(JSON.stringify(value, null, 2));
432
460
  }
@@ -518,7 +546,7 @@ function successHtml(theme, message) {
518
546
  return htmlPage({
519
547
  title: "Framer \u2014 Agent Approved",
520
548
  heading: "Agent Approved",
521
- message: message ?? "Your local agent now has edit access to the project. You can close this tab and continue with the local agent.",
549
+ message: message ?? "Your external agent now has edit access to the project. You can close this tab and continue with the external agent.",
522
550
  theme
523
551
  });
524
552
  }
@@ -767,7 +795,7 @@ async function acquireAuthWithNewProject() {
767
795
  expectProjectId: true,
768
796
  successMessage: "Project created and agent authorized",
769
797
  authType: "new_project",
770
- browserSuccessMessage: "A new project has been created and your local agent is authorized to edit it. You can close this tab."
798
+ browserSuccessMessage: "A new project has been created and your external agent is authorized to edit it. You can close this tab."
771
799
  });
772
800
  }
773
801
  __name(acquireAuthWithNewProject, "acquireAuthWithNewProject");
@@ -784,7 +812,7 @@ async function acquireAuthWithRemixProject(sourceProjectUrlOrId) {
784
812
  expectProjectId: true,
785
813
  successMessage: "Project remixed and agent authorized",
786
814
  authType: "remix",
787
- browserSuccessMessage: "The project has been remixed and your local agent is authorized to edit it. You can close this tab.",
815
+ browserSuccessMessage: "The project has been remixed and your external agent is authorized to edit it. You can close this tab.",
788
816
  projectOrigin
789
817
  });
790
818
  }
@@ -797,6 +825,48 @@ function isAuthError(errorMessage) {
797
825
  }
798
826
  __name(isAuthError, "isAuthError");
799
827
 
828
+ // src/closest-match.ts
829
+ function levenshteinDistance(source, target) {
830
+ const sourceLength = source.length;
831
+ const targetLength = target.length;
832
+ const matrix = Array.from(
833
+ { length: sourceLength + 1 },
834
+ () => Array.from({ length: targetLength + 1 }).fill(0)
835
+ );
836
+ for (let row = 0; row <= sourceLength; row++) matrix[row][0] = row;
837
+ for (let col = 0; col <= targetLength; col++) matrix[0][col] = col;
838
+ for (let row = 1; row <= sourceLength; row++) {
839
+ for (let col = 1; col <= targetLength; col++) {
840
+ const cost = source[row - 1] === target[col - 1] ? 0 : 1;
841
+ matrix[row][col] = Math.min(
842
+ matrix[row - 1][col] + 1,
843
+ matrix[row][col - 1] + 1,
844
+ matrix[row - 1][col - 1] + cost
845
+ );
846
+ }
847
+ }
848
+ return matrix[sourceLength][targetLength];
849
+ }
850
+ __name(levenshteinDistance, "levenshteinDistance");
851
+ function findClosestMatch(query, candidates) {
852
+ const queryLower = query.toLowerCase();
853
+ let bestMatch;
854
+ let bestDistance = Number.POSITIVE_INFINITY;
855
+ for (const candidate of candidates) {
856
+ const distance = levenshteinDistance(queryLower, candidate.toLowerCase());
857
+ if (distance < bestDistance) {
858
+ bestDistance = distance;
859
+ bestMatch = candidate;
860
+ }
861
+ }
862
+ const maxDistance = Math.max(2, Math.floor(query.length / 3));
863
+ if (bestDistance <= maxDistance) {
864
+ return bestMatch;
865
+ }
866
+ return void 0;
867
+ }
868
+ __name(findClosestMatch, "findClosestMatch");
869
+
800
870
  // src/types-data.ts
801
871
  var types = {
802
872
  addcomponentinstanceoptions: {
@@ -975,7 +1045,7 @@ var types = {
975
1045
  {
976
1046
  name: "imageUrls",
977
1047
  type: "readonly string[]",
978
- description: "Attach images to this agent turn by URL.\n\nURLs can be external or existing Framer asset URLs. They will be ingested into the\nproject's assets if needed, then sent to the model as image parts.\n\nTip: if you have a local file (or bytes), upload it first (e.g. `framer.uploadImage(...)`) and\npass the returned asset URL here.",
1048
+ description: "Attach images to this agent turn by URL.\n\nURLs can be external or existing Framer asset URLs. They will be ingested into the\nproject's assets if needed, then made available to the agent as trusted attachment URLs.\n\nTip: if you have a local file (or bytes), upload it first (e.g. `framer.uploadImage(...)`) and\npass the returned asset URL here.",
979
1049
  optional: true
980
1050
  }
981
1051
  ]
@@ -7704,6 +7774,12 @@ var types = {
7704
7774
  type: "number",
7705
7775
  description: "",
7706
7776
  optional: true
7777
+ },
7778
+ {
7779
+ name: "captureTrainingData",
7780
+ type: "boolean",
7781
+ description: "When true, capture per-step (system, tools, messages) \u2192 response training rows for SFT.",
7782
+ optional: true
7707
7783
  }
7708
7784
  ]
7709
7785
  },
@@ -7724,6 +7800,18 @@ var types = {
7724
7800
  type: "string",
7725
7801
  description: "",
7726
7802
  optional: false
7803
+ },
7804
+ {
7805
+ name: "trainingDataFilename",
7806
+ type: "string",
7807
+ description: "Training-data filename, present when `captureTrainingData` was set.",
7808
+ optional: true
7809
+ },
7810
+ {
7811
+ name: "trainingDataJsonl",
7812
+ type: "string",
7813
+ description: "JSONL string with one training row per inner-agent step, present when `captureTrainingData` was set.",
7814
+ optional: true
7727
7815
  }
7728
7816
  ]
7729
7817
  },
@@ -15896,6 +15984,26 @@ function getType(name2) {
15896
15984
  __name(getType, "getType");
15897
15985
 
15898
15986
  // src/docs.ts
15987
+ function getAllKnownNames() {
15988
+ const bareNames = [
15989
+ ...Object.values(classes).map((classInfo) => classInfo.name),
15990
+ ...Object.values(types).map((typeInfo) => typeInfo.name)
15991
+ ];
15992
+ const framerMethods = methodsByCategory.framer ?? [];
15993
+ for (const method of framerMethods) {
15994
+ bareNames.push(method.name);
15995
+ }
15996
+ const methodNames = [];
15997
+ for (const [category, methods] of Object.entries(methodsByCategory)) {
15998
+ const categoryInfo = classes[category];
15999
+ const displayCategory = categoryInfo?.name ?? category;
16000
+ for (const method of methods) {
16001
+ methodNames.push(`${displayCategory}.${method.name}`);
16002
+ }
16003
+ }
16004
+ return { bareNames, methodNames };
16005
+ }
16006
+ __name(getAllKnownNames, "getAllKnownNames");
15899
16007
  function formatDocComment(text, indent = "") {
15900
16008
  const lines = text.split("\n");
15901
16009
  if (lines.length === 1) {
@@ -15985,8 +16093,11 @@ function renderDocs(queries) {
15985
16093
  if (query.includes(".")) {
15986
16094
  const method = getMethod(query);
15987
16095
  if (!method) {
16096
+ const { methodNames } = getAllKnownNames();
16097
+ const suggestion = findClosestMatch(query, methodNames);
16098
+ const hint = suggestion ? ` Did you mean '${suggestion}'?` : "";
15988
16099
  errors.push(
15989
- `Method '${query}' not found. Use 'framer docs' to list all.`
16100
+ `Method '${query}' not found.${hint} Use 'framer docs' to list all.`
15990
16101
  );
15991
16102
  return { lines, errors };
15992
16103
  }
@@ -16031,7 +16142,12 @@ ${typeDef}`);
16031
16142
  ${typeDef}`);
16032
16143
  }
16033
16144
  } else {
16034
- errors.push(`'${query}' not found. Use 'framer docs' to list all.`);
16145
+ const { bareNames } = getAllKnownNames();
16146
+ const suggestion = findClosestMatch(query, bareNames);
16147
+ const hint = suggestion ? ` Did you mean '${suggestion}'?` : "";
16148
+ errors.push(
16149
+ `'${query}' not found.${hint} Use 'framer docs' to list all.`
16150
+ );
16035
16151
  return { lines, errors };
16036
16152
  }
16037
16153
  }
@@ -16209,6 +16325,16 @@ async function ensureRelayServerRunning(options = {}) {
16209
16325
  throw new Error("Failed to start relay server after 5 seconds");
16210
16326
  }
16211
16327
  __name(ensureRelayServerRunning, "ensureRelayServerRunning");
16328
+
16329
+ // src/skill-discovery-wait.ts
16330
+ var CLAUDE_CODE_SKILL_DISCOVERY_WAIT_MS = 4e3;
16331
+ async function waitForClaudeCodeSkillDiscovery() {
16332
+ if (process.env.CLAUDECODE === void 0) return;
16333
+ await new Promise(
16334
+ (resolve) => setTimeout(resolve, CLAUDE_CODE_SKILL_DISCOVERY_WAIT_MS)
16335
+ );
16336
+ }
16337
+ __name(waitForClaudeCodeSkillDiscovery, "waitForClaudeCodeSkillDiscovery");
16212
16338
  var FRAMER_TEMPORARY_DIR = path7.join(os.tmpdir(), "framer");
16213
16339
  function ensureTemporaryDir() {
16214
16340
  fs7.mkdirSync(FRAMER_TEMPORARY_DIR, { recursive: true });
@@ -16360,6 +16486,10 @@ program.name(PROGRAM_NAME).version(VERSION).description("Framer Server API CLI")
16360
16486
  setDebugEnabled(true);
16361
16487
  }
16362
16488
  });
16489
+ function throwRelayError(result) {
16490
+ throw errorWithRef(result.error ?? "Relay error", result.errorRef);
16491
+ }
16492
+ __name(throwRelayError, "throwRelayError");
16363
16493
  async function readStdin() {
16364
16494
  const chunks = [];
16365
16495
  for await (const chunk of process.stdin) {
@@ -16402,9 +16532,7 @@ async function getAgentSystemPrompt(sessionId) {
16402
16532
  cwd: process.cwd()
16403
16533
  });
16404
16534
  debug("exec", "getAgentSystemPrompt: relay responded");
16405
- if (result.error) {
16406
- throw new Error(result.error);
16407
- }
16535
+ if (result.error) throwRelayError(result);
16408
16536
  const prompt = result.output.at(-1);
16409
16537
  if (typeof prompt !== "string") {
16410
16538
  throw new Error("Did not receive agent prompt output.");
@@ -16420,9 +16548,7 @@ async function getAgentContext(sessionId) {
16420
16548
  cwd: process.cwd()
16421
16549
  });
16422
16550
  debug("exec", "getAgentContext: relay responded");
16423
- if (result.error) {
16424
- throw new Error(result.error);
16425
- }
16551
+ if (result.error) throwRelayError(result);
16426
16552
  const context = result.output.at(-1);
16427
16553
  if (typeof context !== "string") {
16428
16554
  throw new Error("Did not receive agent context output.");
@@ -16446,8 +16572,9 @@ async function refreshSkillsFromSession(sessionId, projectId) {
16446
16572
  });
16447
16573
  debug("skills", "skills installed");
16448
16574
  } catch (err) {
16449
- throw new Error(
16450
- `Failed to refresh skills for session ${sessionId}: ${formatError(err)}`
16575
+ throw errorWithRef(
16576
+ `Failed to refresh skills for session ${sessionId}: ${formatError(err)}`,
16577
+ getErrorRef(err)
16451
16578
  );
16452
16579
  }
16453
16580
  }
@@ -16473,7 +16600,7 @@ async function resolveSessionCredentials(projectUrlOrId) {
16473
16600
  projectId,
16474
16601
  errorMessage: message
16475
16602
  });
16476
- printError(`Failed to resolve credentials: ${message}`);
16603
+ printError(`Failed to resolve credentials: ${formatErrorForUser(err)}`);
16477
16604
  await waitForTrackingToFinish();
16478
16605
  process.exit(1);
16479
16606
  }
@@ -16487,9 +16614,7 @@ async function getProjectName(sessionId) {
16487
16614
  cwd: process.cwd()
16488
16615
  });
16489
16616
  debug("exec", "getProjectName: relay responded");
16490
- if (result.error) {
16491
- throw new Error(result.error);
16492
- }
16617
+ if (result.error) throwRelayError(result);
16493
16618
  const projectName = result.output.at(-1);
16494
16619
  if (typeof projectName !== "string") {
16495
16620
  throw new Error("Did not receive project name output.");
@@ -16511,7 +16636,7 @@ async function ensureRelayForCli(context) {
16511
16636
  userId: context?.userId,
16512
16637
  errorMessage: message
16513
16638
  });
16514
- printError(`Failed to check relay status: ${message}`);
16639
+ printError(`Failed to check relay status: ${formatErrorForUser(err)}`);
16515
16640
  await waitForTrackingToFinish();
16516
16641
  process.exit(1);
16517
16642
  }
@@ -16529,12 +16654,14 @@ async function execAndPrint(sessionId, code) {
16529
16654
  print(line);
16530
16655
  }
16531
16656
  if (result.error) {
16532
- printError(`Error: ${result.error}`);
16657
+ printError(
16658
+ `Error: ${formatErrorForUser(errorWithRef(result.error, result.errorRef))}`
16659
+ );
16533
16660
  await waitForTrackingToFinish();
16534
16661
  process.exit(1);
16535
16662
  }
16536
16663
  } catch (err) {
16537
- printError(`Execution failed: ${formatError(err)}`);
16664
+ printError(`Execution failed: ${formatErrorForUser(err)}`);
16538
16665
  await waitForTrackingToFinish();
16539
16666
  process.exit(1);
16540
16667
  }
@@ -16551,7 +16678,7 @@ program.command("exec").description("Execute code in a session").requiredOption(
16551
16678
  try {
16552
16679
  code = fs7.readFileSync(filePath, "utf-8");
16553
16680
  } catch (err) {
16554
- printError(`Failed to read file: ${formatError(err)}`);
16681
+ printError(`Failed to read file: ${formatErrorForUser(err)}`);
16555
16682
  await waitForTrackingToFinish();
16556
16683
  process.exit(1);
16557
16684
  }
@@ -16640,6 +16767,7 @@ session.command("new <projectUrlOrId>").description("Create a new session and pr
16640
16767
  refreshSkillsFromSession(sessionId, projectId)
16641
16768
  ]);
16642
16769
  debug("session.new", `project name="${projectName}", skills refreshed`);
16770
+ await waitForClaudeCodeSkillDiscovery();
16643
16771
  saveProject({
16644
16772
  projectId,
16645
16773
  apiKey: credentials.apiKey,
@@ -16656,7 +16784,7 @@ session.command("new <projectUrlOrId>").description("Create a new session and pr
16656
16784
  cleanupStaleSkills(activeProjectIds);
16657
16785
  } catch (error) {
16658
16786
  printWarning(
16659
- `Failed to clean up stale skills: ${formatError(error)}`
16787
+ `Failed to clean up stale skills: ${formatErrorForUser(error)}`
16660
16788
  );
16661
16789
  }
16662
16790
  const activeSessionIds = activeSessions.map(
@@ -16666,7 +16794,7 @@ session.command("new <projectUrlOrId>").description("Create a new session and pr
16666
16794
  cleanupStaleSessionCodeFiles(activeSessionIds);
16667
16795
  } catch (error) {
16668
16796
  printWarning(
16669
- `Failed to clean up stale session code files: ${formatError(error)}`
16797
+ `Failed to clean up stale session code files: ${formatErrorForUser(error)}`
16670
16798
  );
16671
16799
  }
16672
16800
  debug(
@@ -16685,7 +16813,7 @@ session.command("new <projectUrlOrId>").description("Create a new session and pr
16685
16813
  userId,
16686
16814
  errorMessage: message
16687
16815
  });
16688
- printError(`Failed to create session: ${message}`);
16816
+ printError(`Failed to create session: ${formatErrorForUser(err)}`);
16689
16817
  if (isAuthError(message)) {
16690
16818
  clearApiKey(projectId);
16691
16819
  printError("The stored API key was invalid and has been removed.");
@@ -16719,7 +16847,7 @@ session.command("list").description("List all active sessions").action(async ()
16719
16847
  })
16720
16848
  );
16721
16849
  } catch (err) {
16722
- printError(`Failed to list sessions: ${formatError(err)}`);
16850
+ printError(`Failed to list sessions: ${formatErrorForUser(err)}`);
16723
16851
  await waitForTrackingToFinish();
16724
16852
  process.exit(1);
16725
16853
  }
@@ -16759,7 +16887,7 @@ async function saveAuthResult(verb, authFn) {
16759
16887
  saveProject({ projectId, apiKey: result.apiKey, userId: result.userId });
16760
16888
  print(`Project ${projectId} saved`);
16761
16889
  } catch (error) {
16762
- printError(`Failed to ${verb} project: ${formatError(error)}`);
16890
+ printError(`Failed to ${verb} project: ${formatErrorForUser(error)}`);
16763
16891
  await waitForTrackingToFinish();
16764
16892
  process.exit(1);
16765
16893
  }
@@ -16782,7 +16910,7 @@ session.command("destroy <sessionId>").description("Destroy a session").action(a
16782
16910
  await client.destroySession.mutate({ sessionId });
16783
16911
  print(`Session ${sessionId} destroyed`);
16784
16912
  } catch (err) {
16785
- printError(`Failed to destroy session: ${formatError(err)}`);
16913
+ printError(`Failed to destroy session: ${formatErrorForUser(err)}`);
16786
16914
  await waitForTrackingToFinish();
16787
16915
  process.exit(1);
16788
16916
  }
@@ -16810,10 +16938,11 @@ program.command("setup").description(
16810
16938
  ).action(async () => {
16811
16939
  try {
16812
16940
  const results = installSkills();
16941
+ await waitForClaudeCodeSkillDiscovery();
16813
16942
  printSetupSummary(results);
16814
16943
  printTelemetryNotice();
16815
16944
  } catch (err) {
16816
- printError(`Setup failed: ${formatError(err)}`);
16945
+ printError(`Setup failed: ${formatErrorForUser(err)}`);
16817
16946
  await waitForTrackingToFinish();
16818
16947
  process.exit(1);
16819
16948
  }
@@ -8,12 +8,12 @@ import crypto, { randomUUID, timingSafeEqual } from 'crypto';
8
8
  import http from 'http';
9
9
  import { createHTTPHandler } from '@trpc/server/adapters/standalone';
10
10
  import { initTRPC, TRPCError } from '@trpc/server';
11
+ import { connect, FramerAPIError } from 'framer-api';
11
12
  import { z } from 'zod';
12
- import { connect } from 'framer-api';
13
13
  import { createRequire } from 'module';
14
14
  import * as vm from 'vm';
15
15
 
16
- /* @framer/ai relay server v0.0.23 */
16
+ /* @framer/ai relay server v0.0.24 */
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) => {
@@ -159,7 +159,7 @@ __name(debug, "debug");
159
159
  // src/version.ts
160
160
  var VERSION = (
161
161
  // typeof is used to ensure this can be used just via tsx or node etc. without build
162
- "0.0.23"
162
+ "0.0.24"
163
163
  );
164
164
 
165
165
  // src/relay-client.ts
@@ -391,6 +391,19 @@ var ScopedFS = class {
391
391
  }
392
392
  constants = fs4.constants;
393
393
  };
394
+ function getErrorRef(error) {
395
+ if (!(error instanceof Error)) return void 0;
396
+ const ref = error.ref;
397
+ return typeof ref === "string" && ref.length > 0 ? ref : void 0;
398
+ }
399
+ __name(getErrorRef, "getErrorRef");
400
+ function formatError(error) {
401
+ if (error instanceof Error) {
402
+ return error.message;
403
+ }
404
+ return String(error);
405
+ }
406
+ __name(formatError, "formatError");
394
407
 
395
408
  // src/execute.ts
396
409
  var EXECUTION_TIMEOUT = 10 * 60 * 1e3;
@@ -530,9 +543,11 @@ async function execute(session, code, options = {}) {
530
543
  if (isConnectionError(errorMessage)) {
531
544
  session.connection.markDisconnected();
532
545
  }
546
+ const errorRef = getErrorRef(err);
533
547
  return {
534
548
  output,
535
- error: errorMessage
549
+ error: errorMessage,
550
+ ...errorRef ? { errorRef } : {}
536
551
  };
537
552
  } finally {
538
553
  if (timeoutId) clearTimeout(timeoutId);
@@ -546,10 +561,13 @@ async function executeWithReconnect(session, code, options, execId) {
546
561
  );
547
562
  try {
548
563
  await session.connection.reconnect();
549
- } catch {
564
+ } catch (err) {
565
+ const inner = err instanceof Error ? err.message : String(err);
566
+ const errorRef = getErrorRef(err);
550
567
  return {
551
568
  output: [],
552
- error: "Failed to get connection for session"
569
+ error: `Failed to get connection for session: ${inner}`,
570
+ ...errorRef ? { errorRef } : {}
553
571
  };
554
572
  }
555
573
  }
@@ -562,13 +580,16 @@ async function executeWithReconnect(session, code, options, execId) {
562
580
  );
563
581
  try {
564
582
  await session.connection.reconnect();
565
- } catch {
583
+ } catch (err) {
584
+ const inner = err instanceof Error ? err.message : String(err);
585
+ const errorRef = getErrorRef(err);
566
586
  log(
567
- `reconnect.failed exec=${execId} session=${session.id} req=${session.connection.framer.requestId} error="reconnect failed"`
587
+ `reconnect.failed exec=${execId} session=${session.id} req=${session.connection.framer.requestId} error="${inner}"`
568
588
  );
569
589
  return {
570
590
  output: [],
571
- error: "Connection lost and failed to reconnect"
591
+ error: `Connection lost and failed to reconnect: ${inner}`,
592
+ ...errorRef ? { errorRef } : {}
572
593
  };
573
594
  }
574
595
  log(
@@ -1156,7 +1177,17 @@ var SessionManager = class {
1156
1177
  var sessionManager = new SessionManager();
1157
1178
 
1158
1179
  // src/router.ts
1159
- var t = initTRPC.create();
1180
+ var t = initTRPC.create({
1181
+ errorFormatter({ shape, error }) {
1182
+ const cause = error.cause;
1183
+ if (cause instanceof FramerAPIError) {
1184
+ const framerApi = { code: cause.code };
1185
+ if (cause.ref !== void 0) framerApi.ref = cause.ref;
1186
+ return { ...shape, data: { ...shape.data, framerApi } };
1187
+ }
1188
+ return shape;
1189
+ }
1190
+ });
1160
1191
  var nextExecId = 0;
1161
1192
  var appRouter = t.router({
1162
1193
  version: t.procedure.query(() => {
@@ -1192,7 +1223,7 @@ var appRouter = t.router({
1192
1223
  });
1193
1224
  return { id: session.id };
1194
1225
  } catch (err) {
1195
- const message = err instanceof Error ? err.message : String(err);
1226
+ const message = formatError(err);
1196
1227
  trackError({
1197
1228
  errorType: "session_create_error",
1198
1229
  projectId: input.projectId,
@@ -1201,7 +1232,8 @@ var appRouter = t.router({
1201
1232
  });
1202
1233
  throw new TRPCError({
1203
1234
  code: isAuthError(message) ? "UNAUTHORIZED" : "INTERNAL_SERVER_ERROR",
1204
- message
1235
+ message,
1236
+ cause: err
1205
1237
  });
1206
1238
  }
1207
1239
  }),
@@ -198,12 +198,25 @@ Prompting may take a while to complete, so set the command timeout to 10 minutes
198
198
 
199
199
  ## Execute Code
200
200
 
201
- Use your write tool to write your code to a unique file under `{{FRAMER_TEMPORARY_DIR}}/` and execute with `-f`:
201
+ Prefer writing code to a unique file under `{{FRAMER_TEMPORARY_DIR}}/` and executing it with `-f`:
202
202
 
203
203
  ```bash
204
204
  npx framer-dalton exec -s <sessionId> -f {{FRAMER_TEMPORARY_DIR}}/<sessionId>-<short-summary>.js
205
205
  ```
206
206
 
207
+ For short snippets, `exec` also accepts `-e <code>` or code piped on stdin.
208
+
209
+ ## Shell Quoting
210
+
211
+ In Windows PowerShell, if an argument contains nested quotes, use a single-quoted here-string and pass the variable. Do not backslash-escape quotes.
212
+
213
+ ```powershell
214
+ $value = @'
215
+ [{"key":"value","filter":["text","$rect"]}]
216
+ '@
217
+ npx framer-dalton <command> --option $value
218
+ ```
219
+
207
220
  ## API Documentation
208
221
 
209
222
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "framer-dalton",
3
- "version": "0.0.23",
3
+ "version": "0.0.24",
4
4
  "type": "module",
5
5
  "bin": "./dist/cli.js",
6
6
  "main": "./dist/cli.js",
@@ -20,14 +20,14 @@
20
20
  "generate-types": "tsx scripts/generate-types.ts"
21
21
  },
22
22
  "dependencies": {
23
- "@trpc/client": "^11.9.0",
24
- "@trpc/server": "^11.9.0",
23
+ "@trpc/client": "^11.17.0",
24
+ "@trpc/server": "^11.17.0",
25
25
  "commander": "^12.1.0",
26
- "framer-api": "^0.1.9",
26
+ "framer-api": "0.1.10",
27
27
  "zod": "^4.3.6"
28
28
  },
29
29
  "devDependencies": {
30
- "@biomejs/biome": "^2.3.13",
30
+ "@biomejs/biome": "^2.4.13",
31
31
  "@framerjs/framer-events": "0.0.175",
32
32
  "@types/node": "24.10.9",
33
33
  "tsup": "^8.0.2",