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 +162 -33
- package/dist/start-relay-server.js +44 -12
- package/docs/skills/framer.md +14 -1
- package/package.json +5 -5
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.
|
|
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.
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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: ${
|
|
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: ${
|
|
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(
|
|
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: ${
|
|
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: ${
|
|
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: ${
|
|
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: ${
|
|
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: ${
|
|
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: ${
|
|
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: ${
|
|
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: ${
|
|
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: ${
|
|
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.
|
|
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.
|
|
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:
|
|
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="
|
|
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:
|
|
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 =
|
|
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
|
}),
|
package/docs/skills/framer.md
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
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.
|
|
24
|
-
"@trpc/server": "^11.
|
|
23
|
+
"@trpc/client": "^11.17.0",
|
|
24
|
+
"@trpc/server": "^11.17.0",
|
|
25
25
|
"commander": "^12.1.0",
|
|
26
|
-
"framer-api": "
|
|
26
|
+
"framer-api": "0.1.10",
|
|
27
27
|
"zod": "^4.3.6"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
|
-
"@biomejs/biome": "^2.
|
|
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",
|