framer-dalton 0.0.23 → 0.0.25
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 +5 -5
- package/dist/cli.js +267 -57
- package/dist/start-relay-server.js +45 -12
- package/docs/skills/framer-code-components.md +1 -1
- package/docs/skills/framer-project.md +426 -0
- package/docs/skills/framer.md +6 -370
- package/package.json +5 -5
- package/docs/skills/framer-canvas-editing-project.md +0 -49
package/dist/cli.js
CHANGED
|
@@ -7,10 +7,12 @@ 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';
|
|
11
|
+
import net from 'net';
|
|
10
12
|
import { fileURLToPath } from 'url';
|
|
11
13
|
import { createTRPCClient, httpLink } from '@trpc/client';
|
|
12
14
|
|
|
13
|
-
/* @framer/ai CLI v0.0.
|
|
15
|
+
/* @framer/ai CLI v0.0.25 */
|
|
14
16
|
var __defProp = Object.defineProperty;
|
|
15
17
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
16
18
|
|
|
@@ -328,7 +330,7 @@ __name(markTelemetryNoticeShown, "markTelemetryNoticeShown");
|
|
|
328
330
|
// src/version.ts
|
|
329
331
|
var VERSION = (
|
|
330
332
|
// typeof is used to ensure this can be used just via tsx or node etc. without build
|
|
331
|
-
"0.0.
|
|
333
|
+
"0.0.25"
|
|
332
334
|
);
|
|
333
335
|
var trackingEndpoint = "https://events.framer.com/track";
|
|
334
336
|
var inProgressTrackings = /* @__PURE__ */ new Set();
|
|
@@ -418,8 +420,27 @@ function trackError(payload) {
|
|
|
418
420
|
});
|
|
419
421
|
}
|
|
420
422
|
__name(trackError, "trackError");
|
|
421
|
-
|
|
422
|
-
|
|
423
|
+
function rehydrateFramerAPIError(error) {
|
|
424
|
+
if (!(error instanceof Error)) return null;
|
|
425
|
+
const framerApi = error.data?.framerApi;
|
|
426
|
+
if (!framerApi || typeof framerApi.code !== "string") return null;
|
|
427
|
+
const code = ErrorCode[framerApi.code] ?? ErrorCode.INTERNAL;
|
|
428
|
+
const options = framerApi.ref ? { ref: framerApi.ref } : void 0;
|
|
429
|
+
return new FramerAPIError(error.message, code, options);
|
|
430
|
+
}
|
|
431
|
+
__name(rehydrateFramerAPIError, "rehydrateFramerAPIError");
|
|
432
|
+
function errorWithRef(message, ref) {
|
|
433
|
+
const err = new Error(message);
|
|
434
|
+
if (ref) err.ref = ref;
|
|
435
|
+
return err;
|
|
436
|
+
}
|
|
437
|
+
__name(errorWithRef, "errorWithRef");
|
|
438
|
+
function getErrorRef(error) {
|
|
439
|
+
if (!(error instanceof Error)) return void 0;
|
|
440
|
+
const ref = error.ref;
|
|
441
|
+
return typeof ref === "string" && ref.length > 0 ? ref : void 0;
|
|
442
|
+
}
|
|
443
|
+
__name(getErrorRef, "getErrorRef");
|
|
423
444
|
function formatError(error) {
|
|
424
445
|
if (error instanceof Error) {
|
|
425
446
|
return error.message;
|
|
@@ -427,6 +448,14 @@ function formatError(error) {
|
|
|
427
448
|
return String(error);
|
|
428
449
|
}
|
|
429
450
|
__name(formatError, "formatError");
|
|
451
|
+
function formatErrorForUser(error) {
|
|
452
|
+
if (!(error instanceof Error)) return String(error);
|
|
453
|
+
const rehydrated = rehydrateFramerAPIError(error);
|
|
454
|
+
if (rehydrated) return rehydrated.toString();
|
|
455
|
+
const ref = getErrorRef(error);
|
|
456
|
+
return ref ? `${error.message} [ref: ${ref}]` : error.message;
|
|
457
|
+
}
|
|
458
|
+
__name(formatErrorForUser, "formatErrorForUser");
|
|
430
459
|
function printJson(value) {
|
|
431
460
|
console.log(JSON.stringify(value, null, 2));
|
|
432
461
|
}
|
|
@@ -518,7 +547,7 @@ function successHtml(theme, message) {
|
|
|
518
547
|
return htmlPage({
|
|
519
548
|
title: "Framer \u2014 Agent Approved",
|
|
520
549
|
heading: "Agent Approved",
|
|
521
|
-
message: message ?? "Your
|
|
550
|
+
message: message ?? "Your external agent now has edit access to the project. You can close this tab and continue with the external agent.",
|
|
522
551
|
theme
|
|
523
552
|
});
|
|
524
553
|
}
|
|
@@ -767,7 +796,7 @@ async function acquireAuthWithNewProject() {
|
|
|
767
796
|
expectProjectId: true,
|
|
768
797
|
successMessage: "Project created and agent authorized",
|
|
769
798
|
authType: "new_project",
|
|
770
|
-
browserSuccessMessage: "A new project has been created and your
|
|
799
|
+
browserSuccessMessage: "A new project has been created and your external agent is authorized to edit it. You can close this tab."
|
|
771
800
|
});
|
|
772
801
|
}
|
|
773
802
|
__name(acquireAuthWithNewProject, "acquireAuthWithNewProject");
|
|
@@ -784,7 +813,7 @@ async function acquireAuthWithRemixProject(sourceProjectUrlOrId) {
|
|
|
784
813
|
expectProjectId: true,
|
|
785
814
|
successMessage: "Project remixed and agent authorized",
|
|
786
815
|
authType: "remix",
|
|
787
|
-
browserSuccessMessage: "The project has been remixed and your
|
|
816
|
+
browserSuccessMessage: "The project has been remixed and your external agent is authorized to edit it. You can close this tab.",
|
|
788
817
|
projectOrigin
|
|
789
818
|
});
|
|
790
819
|
}
|
|
@@ -797,6 +826,48 @@ function isAuthError(errorMessage) {
|
|
|
797
826
|
}
|
|
798
827
|
__name(isAuthError, "isAuthError");
|
|
799
828
|
|
|
829
|
+
// src/closest-match.ts
|
|
830
|
+
function levenshteinDistance(source, target) {
|
|
831
|
+
const sourceLength = source.length;
|
|
832
|
+
const targetLength = target.length;
|
|
833
|
+
const matrix = Array.from(
|
|
834
|
+
{ length: sourceLength + 1 },
|
|
835
|
+
() => Array.from({ length: targetLength + 1 }).fill(0)
|
|
836
|
+
);
|
|
837
|
+
for (let row = 0; row <= sourceLength; row++) matrix[row][0] = row;
|
|
838
|
+
for (let col = 0; col <= targetLength; col++) matrix[0][col] = col;
|
|
839
|
+
for (let row = 1; row <= sourceLength; row++) {
|
|
840
|
+
for (let col = 1; col <= targetLength; col++) {
|
|
841
|
+
const cost = source[row - 1] === target[col - 1] ? 0 : 1;
|
|
842
|
+
matrix[row][col] = Math.min(
|
|
843
|
+
matrix[row - 1][col] + 1,
|
|
844
|
+
matrix[row][col - 1] + 1,
|
|
845
|
+
matrix[row - 1][col - 1] + cost
|
|
846
|
+
);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
return matrix[sourceLength][targetLength];
|
|
850
|
+
}
|
|
851
|
+
__name(levenshteinDistance, "levenshteinDistance");
|
|
852
|
+
function findClosestMatch(query, candidates) {
|
|
853
|
+
const queryLower = query.toLowerCase();
|
|
854
|
+
let bestMatch;
|
|
855
|
+
let bestDistance = Number.POSITIVE_INFINITY;
|
|
856
|
+
for (const candidate of candidates) {
|
|
857
|
+
const distance = levenshteinDistance(queryLower, candidate.toLowerCase());
|
|
858
|
+
if (distance < bestDistance) {
|
|
859
|
+
bestDistance = distance;
|
|
860
|
+
bestMatch = candidate;
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
const maxDistance = Math.max(2, Math.floor(query.length / 3));
|
|
864
|
+
if (bestDistance <= maxDistance) {
|
|
865
|
+
return bestMatch;
|
|
866
|
+
}
|
|
867
|
+
return void 0;
|
|
868
|
+
}
|
|
869
|
+
__name(findClosestMatch, "findClosestMatch");
|
|
870
|
+
|
|
800
871
|
// src/types-data.ts
|
|
801
872
|
var types = {
|
|
802
873
|
addcomponentinstanceoptions: {
|
|
@@ -975,7 +1046,7 @@ var types = {
|
|
|
975
1046
|
{
|
|
976
1047
|
name: "imageUrls",
|
|
977
1048
|
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
|
|
1049
|
+
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
1050
|
optional: true
|
|
980
1051
|
}
|
|
981
1052
|
]
|
|
@@ -1733,6 +1804,12 @@ var types = {
|
|
|
1733
1804
|
kind: "interface",
|
|
1734
1805
|
references: ["CodeExportCommon"],
|
|
1735
1806
|
members: [
|
|
1807
|
+
{
|
|
1808
|
+
name: "componentId",
|
|
1809
|
+
type: "string",
|
|
1810
|
+
description: "Component ID for this code file export.",
|
|
1811
|
+
optional: false
|
|
1812
|
+
},
|
|
1736
1813
|
{
|
|
1737
1814
|
name: "insertURL",
|
|
1738
1815
|
type: "string",
|
|
@@ -4408,7 +4485,7 @@ var types = {
|
|
|
4408
4485
|
name: "HostnameType",
|
|
4409
4486
|
description: "",
|
|
4410
4487
|
kind: "alias",
|
|
4411
|
-
alias: '"default" | "custom" | "version"',
|
|
4488
|
+
alias: '"default" | "custom" | "version" | "branch"',
|
|
4412
4489
|
references: []
|
|
4413
4490
|
},
|
|
4414
4491
|
imageassetdata: {
|
|
@@ -6889,7 +6966,7 @@ var types = {
|
|
|
6889
6966
|
},
|
|
6890
6967
|
{
|
|
6891
6968
|
name: "unstable_getCodeFile",
|
|
6892
|
-
type: "(
|
|
6969
|
+
type: "(idOrPath: string) => Promise<CodeFileData | null>",
|
|
6893
6970
|
description: "@deprecated",
|
|
6894
6971
|
optional: false
|
|
6895
6972
|
},
|
|
@@ -6949,7 +7026,7 @@ var types = {
|
|
|
6949
7026
|
},
|
|
6950
7027
|
{
|
|
6951
7028
|
name: "getCodeFile",
|
|
6952
|
-
type: "(
|
|
7029
|
+
type: "(idOrPath: string) => Promise<CodeFileData | null>",
|
|
6953
7030
|
description: "",
|
|
6954
7031
|
optional: false
|
|
6955
7032
|
},
|
|
@@ -7690,9 +7767,15 @@ var types = {
|
|
|
7690
7767
|
{
|
|
7691
7768
|
name: "model",
|
|
7692
7769
|
type: "string",
|
|
7693
|
-
description: "",
|
|
7770
|
+
description: "Supervisor model id, optionally with eval-style reasoning effort suffix, e.g. `anthropic/claude-opus-4.6=low`.",
|
|
7694
7771
|
optional: false
|
|
7695
7772
|
},
|
|
7773
|
+
{
|
|
7774
|
+
name: "userAgentModel",
|
|
7775
|
+
type: "string",
|
|
7776
|
+
description: "Inner Framer Agent model override. Defaults to `model` when omitted. Supports the same `model=effort` syntax.",
|
|
7777
|
+
optional: true
|
|
7778
|
+
},
|
|
7696
7779
|
{
|
|
7697
7780
|
name: "pagePath",
|
|
7698
7781
|
type: "string",
|
|
@@ -7704,6 +7787,12 @@ var types = {
|
|
|
7704
7787
|
type: "number",
|
|
7705
7788
|
description: "",
|
|
7706
7789
|
optional: true
|
|
7790
|
+
},
|
|
7791
|
+
{
|
|
7792
|
+
name: "captureTrainingData",
|
|
7793
|
+
type: "boolean",
|
|
7794
|
+
description: "When true, capture per-step (system, tools, messages) \u2192 response training rows for SFT.",
|
|
7795
|
+
optional: true
|
|
7707
7796
|
}
|
|
7708
7797
|
]
|
|
7709
7798
|
},
|
|
@@ -7724,6 +7813,18 @@ var types = {
|
|
|
7724
7813
|
type: "string",
|
|
7725
7814
|
description: "",
|
|
7726
7815
|
optional: false
|
|
7816
|
+
},
|
|
7817
|
+
{
|
|
7818
|
+
name: "trainingDataFilename",
|
|
7819
|
+
type: "string",
|
|
7820
|
+
description: "Training-data filename, present when `captureTrainingData` was set.",
|
|
7821
|
+
optional: true
|
|
7822
|
+
},
|
|
7823
|
+
{
|
|
7824
|
+
name: "trainingDataJsonl",
|
|
7825
|
+
type: "string",
|
|
7826
|
+
description: "JSONL string with one training row per inner-agent step, present when `captureTrainingData` was set.",
|
|
7827
|
+
optional: true
|
|
7727
7828
|
}
|
|
7728
7829
|
]
|
|
7729
7830
|
},
|
|
@@ -13426,8 +13527,8 @@ var methodsByCategory = {
|
|
|
13426
13527
|
{
|
|
13427
13528
|
name: "getCodeFile",
|
|
13428
13529
|
category: "framer",
|
|
13429
|
-
signature: "getCodeFile(
|
|
13430
|
-
description: 'Get a specific code file by its ID.\n\n@param
|
|
13530
|
+
signature: "getCodeFile(idOrPath: string): Promise<CodeFile | null>",
|
|
13531
|
+
description: 'Get a specific code file by its ID or file path.\n\n@param idOrPath - The unique identifier (e.g. `"codeFile/AbcD_23"`) or\nfile path (e.g. `"Button.tsx"` or `"overrides/withAnalytics.tsx"`) of the\ncode file.\n@returns The CodeFile instance or `null` if not found.\n\n@example\n```ts\n// Look up by ID\nconst codeFile = await framer.getCodeFile("codeFile/AbcD_23")\n\n// Look up by file path\nconst codeFile = await framer.getCodeFile("Button.tsx")\n```\n@category code-files',
|
|
13431
13532
|
references: ["CodeFile"]
|
|
13432
13533
|
},
|
|
13433
13534
|
{
|
|
@@ -15896,6 +15997,26 @@ function getType(name2) {
|
|
|
15896
15997
|
__name(getType, "getType");
|
|
15897
15998
|
|
|
15898
15999
|
// src/docs.ts
|
|
16000
|
+
function getAllKnownNames() {
|
|
16001
|
+
const bareNames = [
|
|
16002
|
+
...Object.values(classes).map((classInfo) => classInfo.name),
|
|
16003
|
+
...Object.values(types).map((typeInfo) => typeInfo.name)
|
|
16004
|
+
];
|
|
16005
|
+
const framerMethods = methodsByCategory.framer ?? [];
|
|
16006
|
+
for (const method of framerMethods) {
|
|
16007
|
+
bareNames.push(method.name);
|
|
16008
|
+
}
|
|
16009
|
+
const methodNames = [];
|
|
16010
|
+
for (const [category, methods] of Object.entries(methodsByCategory)) {
|
|
16011
|
+
const categoryInfo = classes[category];
|
|
16012
|
+
const displayCategory = categoryInfo?.name ?? category;
|
|
16013
|
+
for (const method of methods) {
|
|
16014
|
+
methodNames.push(`${displayCategory}.${method.name}`);
|
|
16015
|
+
}
|
|
16016
|
+
}
|
|
16017
|
+
return { bareNames, methodNames };
|
|
16018
|
+
}
|
|
16019
|
+
__name(getAllKnownNames, "getAllKnownNames");
|
|
15899
16020
|
function formatDocComment(text, indent = "") {
|
|
15900
16021
|
const lines = text.split("\n");
|
|
15901
16022
|
if (lines.length === 1) {
|
|
@@ -15985,8 +16106,11 @@ function renderDocs(queries) {
|
|
|
15985
16106
|
if (query.includes(".")) {
|
|
15986
16107
|
const method = getMethod(query);
|
|
15987
16108
|
if (!method) {
|
|
16109
|
+
const { methodNames } = getAllKnownNames();
|
|
16110
|
+
const suggestion = findClosestMatch(query, methodNames);
|
|
16111
|
+
const hint = suggestion ? ` Did you mean '${suggestion}'?` : "";
|
|
15988
16112
|
errors.push(
|
|
15989
|
-
`Method '${query}' not found
|
|
16113
|
+
`Method '${query}' not found.${hint} Use 'framer docs' to list all.`
|
|
15990
16114
|
);
|
|
15991
16115
|
return { lines, errors };
|
|
15992
16116
|
}
|
|
@@ -16031,7 +16155,12 @@ ${typeDef}`);
|
|
|
16031
16155
|
${typeDef}`);
|
|
16032
16156
|
}
|
|
16033
16157
|
} else {
|
|
16034
|
-
|
|
16158
|
+
const { bareNames } = getAllKnownNames();
|
|
16159
|
+
const suggestion = findClosestMatch(query, bareNames);
|
|
16160
|
+
const hint = suggestion ? ` Did you mean '${suggestion}'?` : "";
|
|
16161
|
+
errors.push(
|
|
16162
|
+
`'${query}' not found.${hint} Use 'framer docs' to list all.`
|
|
16163
|
+
);
|
|
16035
16164
|
return { lines, errors };
|
|
16036
16165
|
}
|
|
16037
16166
|
}
|
|
@@ -16109,6 +16238,7 @@ __name(readRelayToken, "readRelayToken");
|
|
|
16109
16238
|
var __filename$1 = fileURLToPath(import.meta.url);
|
|
16110
16239
|
var __dirname$1 = path7.dirname(__filename$1);
|
|
16111
16240
|
var RELAY_PORT = Number(process.env.FRAMER_CLI_PORT) || 19988;
|
|
16241
|
+
var WAIT_TIMEOUT_SECONDS = 5;
|
|
16112
16242
|
var client = createTRPCClient({
|
|
16113
16243
|
links: [
|
|
16114
16244
|
httpLink({
|
|
@@ -16136,6 +16266,43 @@ function sleep(ms) {
|
|
|
16136
16266
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
16137
16267
|
}
|
|
16138
16268
|
__name(sleep, "sleep");
|
|
16269
|
+
var PORT_PROBE_TIMEOUT_MS = 1e3;
|
|
16270
|
+
function probePort(port) {
|
|
16271
|
+
return new Promise((resolve) => {
|
|
16272
|
+
const socket = net.createConnection({ host: "127.0.0.1", port });
|
|
16273
|
+
const done = /* @__PURE__ */ __name((result) => {
|
|
16274
|
+
socket.removeAllListeners();
|
|
16275
|
+
socket.destroy();
|
|
16276
|
+
resolve(result);
|
|
16277
|
+
}, "done");
|
|
16278
|
+
socket.setTimeout(PORT_PROBE_TIMEOUT_MS);
|
|
16279
|
+
socket.once("connect", () => done("in_use"));
|
|
16280
|
+
socket.once("timeout", () => {
|
|
16281
|
+
debug("relay", `port ${port} probe timed out`);
|
|
16282
|
+
done("unknown");
|
|
16283
|
+
});
|
|
16284
|
+
socket.once("error", (err) => {
|
|
16285
|
+
if (err.code === "ECONNREFUSED") {
|
|
16286
|
+
done("free");
|
|
16287
|
+
} else {
|
|
16288
|
+
debug("relay", `port ${port} probe error: ${err.code ?? err.message}`);
|
|
16289
|
+
done("unknown");
|
|
16290
|
+
}
|
|
16291
|
+
});
|
|
16292
|
+
});
|
|
16293
|
+
}
|
|
16294
|
+
__name(probePort, "probePort");
|
|
16295
|
+
async function waitForPortFree(port, timeoutMs) {
|
|
16296
|
+
const deadline = Date.now() + timeoutMs;
|
|
16297
|
+
while (Date.now() < deadline) {
|
|
16298
|
+
if (await probePort(port) === "free") {
|
|
16299
|
+
return true;
|
|
16300
|
+
}
|
|
16301
|
+
await sleep(100);
|
|
16302
|
+
}
|
|
16303
|
+
return false;
|
|
16304
|
+
}
|
|
16305
|
+
__name(waitForPortFree, "waitForPortFree");
|
|
16139
16306
|
function compareVersions(v1, v2) {
|
|
16140
16307
|
const parts1 = v1.split(".").map(Number);
|
|
16141
16308
|
const parts2 = v2.split(".").map(Number);
|
|
@@ -16171,8 +16338,20 @@ async function ensureRelayServerRunning(options = {}) {
|
|
|
16171
16338
|
debug("relay", "shutting down old server...");
|
|
16172
16339
|
try {
|
|
16173
16340
|
await client.shutdown.mutate();
|
|
16174
|
-
|
|
16175
|
-
|
|
16341
|
+
} catch (err) {
|
|
16342
|
+
debug(
|
|
16343
|
+
"relay",
|
|
16344
|
+
`shutdown RPC failed: ${err instanceof Error ? err.message : String(err)}`
|
|
16345
|
+
);
|
|
16346
|
+
}
|
|
16347
|
+
const freed = await waitForPortFree(
|
|
16348
|
+
RELAY_PORT,
|
|
16349
|
+
WAIT_TIMEOUT_SECONDS * 1e3
|
|
16350
|
+
);
|
|
16351
|
+
if (!freed) {
|
|
16352
|
+
throw new Error(
|
|
16353
|
+
`Old relay server (v${serverVersion}) is still listening on 127.0.0.1:${RELAY_PORT} after ${WAIT_TIMEOUT_SECONDS}s; please kill it manually`
|
|
16354
|
+
);
|
|
16176
16355
|
}
|
|
16177
16356
|
debug("relay", "old server shut down");
|
|
16178
16357
|
} else {
|
|
@@ -16201,7 +16380,7 @@ async function ensureRelayServerRunning(options = {}) {
|
|
|
16201
16380
|
"relay",
|
|
16202
16381
|
`readiness poll ${attempt + 1}/10: version=${newVersion ?? "not ready"}`
|
|
16203
16382
|
);
|
|
16204
|
-
if (newVersion) {
|
|
16383
|
+
if (newVersion === VERSION) {
|
|
16205
16384
|
logger?.log("Relay server started successfully");
|
|
16206
16385
|
return;
|
|
16207
16386
|
}
|
|
@@ -16209,6 +16388,16 @@ async function ensureRelayServerRunning(options = {}) {
|
|
|
16209
16388
|
throw new Error("Failed to start relay server after 5 seconds");
|
|
16210
16389
|
}
|
|
16211
16390
|
__name(ensureRelayServerRunning, "ensureRelayServerRunning");
|
|
16391
|
+
|
|
16392
|
+
// src/skill-discovery-wait.ts
|
|
16393
|
+
var CLAUDE_CODE_SKILL_DISCOVERY_WAIT_MS = 4e3;
|
|
16394
|
+
async function waitForClaudeCodeSkillDiscovery() {
|
|
16395
|
+
if (process.env.CLAUDECODE === void 0) return;
|
|
16396
|
+
await new Promise(
|
|
16397
|
+
(resolve) => setTimeout(resolve, CLAUDE_CODE_SKILL_DISCOVERY_WAIT_MS)
|
|
16398
|
+
);
|
|
16399
|
+
}
|
|
16400
|
+
__name(waitForClaudeCodeSkillDiscovery, "waitForClaudeCodeSkillDiscovery");
|
|
16212
16401
|
var FRAMER_TEMPORARY_DIR = path7.join(os.tmpdir(), "framer");
|
|
16213
16402
|
function ensureTemporaryDir() {
|
|
16214
16403
|
fs7.mkdirSync(FRAMER_TEMPORARY_DIR, { recursive: true });
|
|
@@ -16254,26 +16443,27 @@ function renderTemplate(template, values) {
|
|
|
16254
16443
|
return rendered;
|
|
16255
16444
|
}
|
|
16256
16445
|
__name(renderTemplate, "renderTemplate");
|
|
16257
|
-
var
|
|
16446
|
+
var PROJECT_SKILL_PREFIX = "framer-project-";
|
|
16447
|
+
var LEGACY_CANVAS_EDITING_SKILL_PREFIX = "framer-canvas-editing-project-";
|
|
16258
16448
|
function toSafeProjectId(projectId) {
|
|
16259
16449
|
return projectId.replace(/[^a-zA-Z0-9_-]/g, "-");
|
|
16260
16450
|
}
|
|
16261
16451
|
__name(toSafeProjectId, "toSafeProjectId");
|
|
16262
|
-
function
|
|
16263
|
-
const skillName = `${
|
|
16264
|
-
const template = readSkillDoc("framer-
|
|
16452
|
+
function buildProjectSkill(projectId, agentContext, agentPrompt) {
|
|
16453
|
+
const skillName = `${PROJECT_SKILL_PREFIX}${toSafeProjectId(projectId)}`;
|
|
16454
|
+
const template = readSkillDoc("framer-project.md");
|
|
16265
16455
|
const content = `${renderTemplate(template, {
|
|
16266
16456
|
SKILL_NAME: skillName,
|
|
16267
16457
|
PROJECT_ID: projectId,
|
|
16268
16458
|
GENERATED_AT: (/* @__PURE__ */ new Date()).toISOString(),
|
|
16269
|
-
|
|
16459
|
+
AGENT_PROMPT: agentPrompt.trimEnd(),
|
|
16270
16460
|
AGENT_CONTEXT: agentContext.trimEnd(),
|
|
16271
16461
|
FRAMER_TEMPORARY_DIR
|
|
16272
16462
|
})}
|
|
16273
16463
|
`;
|
|
16274
16464
|
return { skillName, content };
|
|
16275
16465
|
}
|
|
16276
|
-
__name(
|
|
16466
|
+
__name(buildProjectSkill, "buildProjectSkill");
|
|
16277
16467
|
function writeSkill(root, skillName, content) {
|
|
16278
16468
|
fs7.mkdirSync(root, { recursive: true });
|
|
16279
16469
|
const rootStat = fs7.statSync(root);
|
|
@@ -16291,6 +16481,17 @@ function writeSkill(root, skillName, content) {
|
|
|
16291
16481
|
return filePath;
|
|
16292
16482
|
}
|
|
16293
16483
|
__name(writeSkill, "writeSkill");
|
|
16484
|
+
function cleanupLegacyCanvasEditingSkills(skillRoots = getDefaultSkillRoots()) {
|
|
16485
|
+
for (const root of skillRoots) {
|
|
16486
|
+
if (!fs7.existsSync(root)) continue;
|
|
16487
|
+
for (const entry of fs7.readdirSync(root)) {
|
|
16488
|
+
if (!entry.startsWith(LEGACY_CANVAS_EDITING_SKILL_PREFIX)) continue;
|
|
16489
|
+
const skillDir = path7.join(root, entry);
|
|
16490
|
+
fs7.rmSync(skillDir, { recursive: true, force: true, maxRetries: 2 });
|
|
16491
|
+
}
|
|
16492
|
+
}
|
|
16493
|
+
}
|
|
16494
|
+
__name(cleanupLegacyCanvasEditingSkills, "cleanupLegacyCanvasEditingSkills");
|
|
16294
16495
|
function getDefaultSkillRoots() {
|
|
16295
16496
|
const home = os.homedir();
|
|
16296
16497
|
return [
|
|
@@ -16299,16 +16500,15 @@ function getDefaultSkillRoots() {
|
|
|
16299
16500
|
];
|
|
16300
16501
|
}
|
|
16301
16502
|
__name(getDefaultSkillRoots, "getDefaultSkillRoots");
|
|
16302
|
-
function cleanupStaleSkills(activeProjectIds, skillRoots) {
|
|
16303
|
-
const roots = skillRoots ?? getDefaultSkillRoots();
|
|
16503
|
+
function cleanupStaleSkills(activeProjectIds, skillRoots = getDefaultSkillRoots()) {
|
|
16304
16504
|
const sanitizedActiveProjectIds = new Set(
|
|
16305
16505
|
[...activeProjectIds].map(toSafeProjectId)
|
|
16306
16506
|
);
|
|
16307
|
-
for (const root of
|
|
16507
|
+
for (const root of skillRoots) {
|
|
16308
16508
|
if (!fs7.existsSync(root)) continue;
|
|
16309
16509
|
for (const entry of fs7.readdirSync(root)) {
|
|
16310
|
-
if (!entry.startsWith(
|
|
16311
|
-
const sanitizedProjectId = entry.slice(
|
|
16510
|
+
if (!entry.startsWith(PROJECT_SKILL_PREFIX)) continue;
|
|
16511
|
+
const sanitizedProjectId = entry.slice(PROJECT_SKILL_PREFIX.length);
|
|
16312
16512
|
const isActive = sanitizedActiveProjectIds.has(sanitizedProjectId);
|
|
16313
16513
|
if (isActive) continue;
|
|
16314
16514
|
const skillDir = path7.join(root, entry);
|
|
@@ -16327,10 +16527,10 @@ function installSkills(options = { type: "base" }) {
|
|
|
16327
16527
|
}
|
|
16328
16528
|
];
|
|
16329
16529
|
if (options.type === "project") {
|
|
16330
|
-
const projectSkill =
|
|
16530
|
+
const projectSkill = buildProjectSkill(
|
|
16331
16531
|
options.projectId,
|
|
16332
16532
|
options.agentContext,
|
|
16333
|
-
options.
|
|
16533
|
+
options.agentPrompt
|
|
16334
16534
|
);
|
|
16335
16535
|
skills.push({
|
|
16336
16536
|
name: projectSkill.skillName,
|
|
@@ -16360,6 +16560,10 @@ program.name(PROGRAM_NAME).version(VERSION).description("Framer Server API CLI")
|
|
|
16360
16560
|
setDebugEnabled(true);
|
|
16361
16561
|
}
|
|
16362
16562
|
});
|
|
16563
|
+
function throwRelayError(result) {
|
|
16564
|
+
throw errorWithRef(result.error ?? "Relay error", result.errorRef);
|
|
16565
|
+
}
|
|
16566
|
+
__name(throwRelayError, "throwRelayError");
|
|
16363
16567
|
async function readStdin() {
|
|
16364
16568
|
const chunks = [];
|
|
16365
16569
|
for await (const chunk of process.stdin) {
|
|
@@ -16402,9 +16606,7 @@ async function getAgentSystemPrompt(sessionId) {
|
|
|
16402
16606
|
cwd: process.cwd()
|
|
16403
16607
|
});
|
|
16404
16608
|
debug("exec", "getAgentSystemPrompt: relay responded");
|
|
16405
|
-
if (result.error)
|
|
16406
|
-
throw new Error(result.error);
|
|
16407
|
-
}
|
|
16609
|
+
if (result.error) throwRelayError(result);
|
|
16408
16610
|
const prompt = result.output.at(-1);
|
|
16409
16611
|
if (typeof prompt !== "string") {
|
|
16410
16612
|
throw new Error("Did not receive agent prompt output.");
|
|
@@ -16420,9 +16622,7 @@ async function getAgentContext(sessionId) {
|
|
|
16420
16622
|
cwd: process.cwd()
|
|
16421
16623
|
});
|
|
16422
16624
|
debug("exec", "getAgentContext: relay responded");
|
|
16423
|
-
if (result.error)
|
|
16424
|
-
throw new Error(result.error);
|
|
16425
|
-
}
|
|
16625
|
+
if (result.error) throwRelayError(result);
|
|
16426
16626
|
const context = result.output.at(-1);
|
|
16427
16627
|
if (typeof context !== "string") {
|
|
16428
16628
|
throw new Error("Did not receive agent context output.");
|
|
@@ -16433,21 +16633,22 @@ __name(getAgentContext, "getAgentContext");
|
|
|
16433
16633
|
async function refreshSkillsFromSession(sessionId, projectId) {
|
|
16434
16634
|
try {
|
|
16435
16635
|
debug("skills", "fetching agent system prompt + context...");
|
|
16436
|
-
const [
|
|
16636
|
+
const [agentPrompt, agentContext] = await Promise.all([
|
|
16437
16637
|
getAgentSystemPrompt(sessionId),
|
|
16438
16638
|
getAgentContext(sessionId)
|
|
16439
16639
|
]);
|
|
16440
16640
|
debug("skills", "installing skills...");
|
|
16441
16641
|
installSkills({
|
|
16442
16642
|
type: "project",
|
|
16443
|
-
|
|
16643
|
+
agentPrompt,
|
|
16444
16644
|
projectId,
|
|
16445
16645
|
agentContext
|
|
16446
16646
|
});
|
|
16447
16647
|
debug("skills", "skills installed");
|
|
16448
16648
|
} catch (err) {
|
|
16449
|
-
throw
|
|
16450
|
-
`Failed to refresh skills for session ${sessionId}: ${formatError(err)}
|
|
16649
|
+
throw errorWithRef(
|
|
16650
|
+
`Failed to refresh skills for session ${sessionId}: ${formatError(err)}`,
|
|
16651
|
+
getErrorRef(err)
|
|
16451
16652
|
);
|
|
16452
16653
|
}
|
|
16453
16654
|
}
|
|
@@ -16473,7 +16674,7 @@ async function resolveSessionCredentials(projectUrlOrId) {
|
|
|
16473
16674
|
projectId,
|
|
16474
16675
|
errorMessage: message
|
|
16475
16676
|
});
|
|
16476
|
-
printError(`Failed to resolve credentials: ${
|
|
16677
|
+
printError(`Failed to resolve credentials: ${formatErrorForUser(err)}`);
|
|
16477
16678
|
await waitForTrackingToFinish();
|
|
16478
16679
|
process.exit(1);
|
|
16479
16680
|
}
|
|
@@ -16487,9 +16688,7 @@ async function getProjectName(sessionId) {
|
|
|
16487
16688
|
cwd: process.cwd()
|
|
16488
16689
|
});
|
|
16489
16690
|
debug("exec", "getProjectName: relay responded");
|
|
16490
|
-
if (result.error)
|
|
16491
|
-
throw new Error(result.error);
|
|
16492
|
-
}
|
|
16691
|
+
if (result.error) throwRelayError(result);
|
|
16493
16692
|
const projectName = result.output.at(-1);
|
|
16494
16693
|
if (typeof projectName !== "string") {
|
|
16495
16694
|
throw new Error("Did not receive project name output.");
|
|
@@ -16511,7 +16710,7 @@ async function ensureRelayForCli(context) {
|
|
|
16511
16710
|
userId: context?.userId,
|
|
16512
16711
|
errorMessage: message
|
|
16513
16712
|
});
|
|
16514
|
-
printError(`Failed to check relay status: ${
|
|
16713
|
+
printError(`Failed to check relay status: ${formatErrorForUser(err)}`);
|
|
16515
16714
|
await waitForTrackingToFinish();
|
|
16516
16715
|
process.exit(1);
|
|
16517
16716
|
}
|
|
@@ -16529,12 +16728,14 @@ async function execAndPrint(sessionId, code) {
|
|
|
16529
16728
|
print(line);
|
|
16530
16729
|
}
|
|
16531
16730
|
if (result.error) {
|
|
16532
|
-
printError(
|
|
16731
|
+
printError(
|
|
16732
|
+
`Error: ${formatErrorForUser(errorWithRef(result.error, result.errorRef))}`
|
|
16733
|
+
);
|
|
16533
16734
|
await waitForTrackingToFinish();
|
|
16534
16735
|
process.exit(1);
|
|
16535
16736
|
}
|
|
16536
16737
|
} catch (err) {
|
|
16537
|
-
printError(`Execution failed: ${
|
|
16738
|
+
printError(`Execution failed: ${formatErrorForUser(err)}`);
|
|
16538
16739
|
await waitForTrackingToFinish();
|
|
16539
16740
|
process.exit(1);
|
|
16540
16741
|
}
|
|
@@ -16551,7 +16752,7 @@ program.command("exec").description("Execute code in a session").requiredOption(
|
|
|
16551
16752
|
try {
|
|
16552
16753
|
code = fs7.readFileSync(filePath, "utf-8");
|
|
16553
16754
|
} catch (err) {
|
|
16554
|
-
printError(`Failed to read file: ${
|
|
16755
|
+
printError(`Failed to read file: ${formatErrorForUser(err)}`);
|
|
16555
16756
|
await waitForTrackingToFinish();
|
|
16556
16757
|
process.exit(1);
|
|
16557
16758
|
}
|
|
@@ -16640,6 +16841,7 @@ session.command("new <projectUrlOrId>").description("Create a new session and pr
|
|
|
16640
16841
|
refreshSkillsFromSession(sessionId, projectId)
|
|
16641
16842
|
]);
|
|
16642
16843
|
debug("session.new", `project name="${projectName}", skills refreshed`);
|
|
16844
|
+
await waitForClaudeCodeSkillDiscovery();
|
|
16643
16845
|
saveProject({
|
|
16644
16846
|
projectId,
|
|
16645
16847
|
apiKey: credentials.apiKey,
|
|
@@ -16656,7 +16858,7 @@ session.command("new <projectUrlOrId>").description("Create a new session and pr
|
|
|
16656
16858
|
cleanupStaleSkills(activeProjectIds);
|
|
16657
16859
|
} catch (error) {
|
|
16658
16860
|
printWarning(
|
|
16659
|
-
`Failed to clean up stale skills: ${
|
|
16861
|
+
`Failed to clean up stale skills: ${formatErrorForUser(error)}`
|
|
16660
16862
|
);
|
|
16661
16863
|
}
|
|
16662
16864
|
const activeSessionIds = activeSessions.map(
|
|
@@ -16666,7 +16868,7 @@ session.command("new <projectUrlOrId>").description("Create a new session and pr
|
|
|
16666
16868
|
cleanupStaleSessionCodeFiles(activeSessionIds);
|
|
16667
16869
|
} catch (error) {
|
|
16668
16870
|
printWarning(
|
|
16669
|
-
`Failed to clean up stale session code files: ${
|
|
16871
|
+
`Failed to clean up stale session code files: ${formatErrorForUser(error)}`
|
|
16670
16872
|
);
|
|
16671
16873
|
}
|
|
16672
16874
|
debug(
|
|
@@ -16685,7 +16887,7 @@ session.command("new <projectUrlOrId>").description("Create a new session and pr
|
|
|
16685
16887
|
userId,
|
|
16686
16888
|
errorMessage: message
|
|
16687
16889
|
});
|
|
16688
|
-
printError(`Failed to create session: ${
|
|
16890
|
+
printError(`Failed to create session: ${formatErrorForUser(err)}`);
|
|
16689
16891
|
if (isAuthError(message)) {
|
|
16690
16892
|
clearApiKey(projectId);
|
|
16691
16893
|
printError("The stored API key was invalid and has been removed.");
|
|
@@ -16719,7 +16921,7 @@ session.command("list").description("List all active sessions").action(async ()
|
|
|
16719
16921
|
})
|
|
16720
16922
|
);
|
|
16721
16923
|
} catch (err) {
|
|
16722
|
-
printError(`Failed to list sessions: ${
|
|
16924
|
+
printError(`Failed to list sessions: ${formatErrorForUser(err)}`);
|
|
16723
16925
|
await waitForTrackingToFinish();
|
|
16724
16926
|
process.exit(1);
|
|
16725
16927
|
}
|
|
@@ -16759,7 +16961,7 @@ async function saveAuthResult(verb, authFn) {
|
|
|
16759
16961
|
saveProject({ projectId, apiKey: result.apiKey, userId: result.userId });
|
|
16760
16962
|
print(`Project ${projectId} saved`);
|
|
16761
16963
|
} catch (error) {
|
|
16762
|
-
printError(`Failed to ${verb} project: ${
|
|
16964
|
+
printError(`Failed to ${verb} project: ${formatErrorForUser(error)}`);
|
|
16763
16965
|
await waitForTrackingToFinish();
|
|
16764
16966
|
process.exit(1);
|
|
16765
16967
|
}
|
|
@@ -16782,7 +16984,7 @@ session.command("destroy <sessionId>").description("Destroy a session").action(a
|
|
|
16782
16984
|
await client.destroySession.mutate({ sessionId });
|
|
16783
16985
|
print(`Session ${sessionId} destroyed`);
|
|
16784
16986
|
} catch (err) {
|
|
16785
|
-
printError(`Failed to destroy session: ${
|
|
16987
|
+
printError(`Failed to destroy session: ${formatErrorForUser(err)}`);
|
|
16786
16988
|
await waitForTrackingToFinish();
|
|
16787
16989
|
process.exit(1);
|
|
16788
16990
|
}
|
|
@@ -16809,11 +17011,19 @@ program.command("setup").description(
|
|
|
16809
17011
|
"Install Framer skills into ~/.agents/skills and ~/.claude/skills"
|
|
16810
17012
|
).action(async () => {
|
|
16811
17013
|
try {
|
|
17014
|
+
try {
|
|
17015
|
+
cleanupLegacyCanvasEditingSkills();
|
|
17016
|
+
} catch (error) {
|
|
17017
|
+
printWarning(
|
|
17018
|
+
`Failed to clean up legacy canvas editing skills: ${formatErrorForUser(error)}`
|
|
17019
|
+
);
|
|
17020
|
+
}
|
|
16812
17021
|
const results = installSkills();
|
|
17022
|
+
await waitForClaudeCodeSkillDiscovery();
|
|
16813
17023
|
printSetupSummary(results);
|
|
16814
17024
|
printTelemetryNotice();
|
|
16815
17025
|
} catch (err) {
|
|
16816
|
-
printError(`Setup failed: ${
|
|
17026
|
+
printError(`Setup failed: ${formatErrorForUser(err)}`);
|
|
16817
17027
|
await waitForTrackingToFinish();
|
|
16818
17028
|
process.exit(1);
|
|
16819
17029
|
}
|