framer-dalton 0.0.12 → 0.0.14
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
|
@@ -10,7 +10,7 @@ import { z } from 'zod';
|
|
|
10
10
|
import { fileURLToPath } from 'url';
|
|
11
11
|
import { createTRPCClient, httpLink } from '@trpc/client';
|
|
12
12
|
|
|
13
|
-
/* @framer/ai CLI v0.0.
|
|
13
|
+
/* @framer/ai CLI v0.0.14 */
|
|
14
14
|
var __defProp = Object.defineProperty;
|
|
15
15
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
16
16
|
function openUrl(url) {
|
|
@@ -134,11 +134,10 @@ function extractProjectId(input) {
|
|
|
134
134
|
if (/^[a-zA-Z0-9]+$/.test(input)) {
|
|
135
135
|
return input;
|
|
136
136
|
}
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
const slugMatch = input.match(/^(?:.+--)?([a-zA-Z0-9]+)(?:-[a-zA-Z0-9]+)?$/);
|
|
137
|
+
const candidate = input.match(/\/projects\/([^/?#]+)/)?.[1] ?? input;
|
|
138
|
+
const slugMatch = candidate.match(
|
|
139
|
+
/^(?:.+--)?([a-zA-Z0-9]+)(?:-[a-zA-Z0-9]+)?$/
|
|
140
|
+
);
|
|
142
141
|
if (slugMatch) {
|
|
143
142
|
return slugMatch[1];
|
|
144
143
|
}
|
|
@@ -181,6 +180,19 @@ function clearApiKey(projectId) {
|
|
|
181
180
|
}
|
|
182
181
|
__name(clearApiKey, "clearApiKey");
|
|
183
182
|
|
|
183
|
+
// src/debug.ts
|
|
184
|
+
var enabled = false;
|
|
185
|
+
function setDebugEnabled(value) {
|
|
186
|
+
enabled = value;
|
|
187
|
+
}
|
|
188
|
+
__name(setDebugEnabled, "setDebugEnabled");
|
|
189
|
+
function debug(tag, message) {
|
|
190
|
+
if (!enabled) return;
|
|
191
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
192
|
+
console.error(`[debug ${timestamp}] ${tag}: ${message}`);
|
|
193
|
+
}
|
|
194
|
+
__name(debug, "debug");
|
|
195
|
+
|
|
184
196
|
// src/utils.ts
|
|
185
197
|
function formatError(error) {
|
|
186
198
|
if (error instanceof Error) {
|
|
@@ -313,20 +325,32 @@ __name(parseCode, "parseCode");
|
|
|
313
325
|
async function acquireKeyFromBrowser(projectId) {
|
|
314
326
|
const state = crypto.randomBytes(16).toString("hex");
|
|
315
327
|
return new Promise((resolve, reject) => {
|
|
316
|
-
let
|
|
328
|
+
let settled = false;
|
|
329
|
+
let pendingApiKey = null;
|
|
330
|
+
function settle(fn) {
|
|
331
|
+
if (settled) return;
|
|
332
|
+
settled = true;
|
|
333
|
+
cleanup();
|
|
334
|
+
fn();
|
|
335
|
+
}
|
|
336
|
+
__name(settle, "settle");
|
|
317
337
|
const server = http.createServer((req, res) => {
|
|
318
338
|
const url = new URL(req.url ?? "/", `http://127.0.0.1`);
|
|
339
|
+
debug("auth", `request: ${url.pathname}`);
|
|
319
340
|
if (url.pathname === "/done") {
|
|
320
341
|
const theme2 = parseTheme(url.searchParams.get("theme"));
|
|
321
342
|
res.writeHead(200, { "Content-Type": "text/html" });
|
|
322
|
-
res.end(successHtml(theme2))
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
343
|
+
res.end(successHtml(theme2), () => {
|
|
344
|
+
if (pendingApiKey) {
|
|
345
|
+
const apiKey2 = pendingApiKey;
|
|
346
|
+
pendingApiKey = null;
|
|
347
|
+
settle(() => {
|
|
348
|
+
debug("auth", "received API key via /done, resolving");
|
|
349
|
+
print("\u2713 Agent authorized");
|
|
350
|
+
resolve(apiKey2);
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
});
|
|
330
354
|
return;
|
|
331
355
|
}
|
|
332
356
|
if (url.pathname === "/error") {
|
|
@@ -346,9 +370,9 @@ async function acquireKeyFromBrowser(projectId) {
|
|
|
346
370
|
const code = parseCode(url.searchParams.get("code")) ?? legacyCode;
|
|
347
371
|
if (code !== null) {
|
|
348
372
|
res.writeHead(200, { "Content-Type": "text/html" });
|
|
349
|
-
res.end(codeHtml(code, theme))
|
|
350
|
-
|
|
351
|
-
|
|
373
|
+
res.end(codeHtml(code, theme), () => {
|
|
374
|
+
settle(() => reject(new Error(codeMessages[code])));
|
|
375
|
+
});
|
|
352
376
|
return;
|
|
353
377
|
}
|
|
354
378
|
const apiKey = url.searchParams.get("apiKey");
|
|
@@ -358,7 +382,8 @@ async function acquireKeyFromBrowser(projectId) {
|
|
|
358
382
|
res.end();
|
|
359
383
|
return;
|
|
360
384
|
}
|
|
361
|
-
|
|
385
|
+
debug("auth", "received valid callback, redirecting to /done");
|
|
386
|
+
pendingApiKey = apiKey;
|
|
362
387
|
res.writeHead(302, { Location: `/done?theme=${theme}` });
|
|
363
388
|
res.end();
|
|
364
389
|
});
|
|
@@ -366,9 +391,11 @@ async function acquireKeyFromBrowser(projectId) {
|
|
|
366
391
|
const pollTimer = setInterval(() => {
|
|
367
392
|
const apiKey = getApiKey(projectId);
|
|
368
393
|
if (apiKey) {
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
394
|
+
settle(() => {
|
|
395
|
+
debug("auth", "API key detected from config file poll");
|
|
396
|
+
print("\u2713 API key detected from config");
|
|
397
|
+
resolve(apiKey);
|
|
398
|
+
});
|
|
372
399
|
}
|
|
373
400
|
}, POLL_INTERVAL_MS);
|
|
374
401
|
const HINT_MS = 55e3;
|
|
@@ -385,40 +412,52 @@ async function acquireKeyFromBrowser(projectId) {
|
|
|
385
412
|
print("Waiting for authorization in browser...");
|
|
386
413
|
}, HINT_MS);
|
|
387
414
|
const timer = setTimeout(() => {
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
415
|
+
settle(
|
|
416
|
+
() => reject(
|
|
417
|
+
new Error(
|
|
418
|
+
"Browser authorization timed out. Use `framer project auth <projectUrlOrId> <apiKey>` instead."
|
|
419
|
+
)
|
|
392
420
|
)
|
|
393
421
|
);
|
|
394
422
|
}, TIMEOUT_MS);
|
|
423
|
+
let cleaned = false;
|
|
395
424
|
function cleanup() {
|
|
425
|
+
if (cleaned) return;
|
|
426
|
+
cleaned = true;
|
|
427
|
+
debug("auth", "cleanup: clearing timers and closing server");
|
|
396
428
|
clearTimeout(timer);
|
|
397
429
|
clearTimeout(hintTimer);
|
|
398
430
|
clearInterval(pollTimer);
|
|
399
431
|
server.close();
|
|
432
|
+
server.closeAllConnections();
|
|
400
433
|
}
|
|
401
434
|
__name(cleanup, "cleanup");
|
|
402
|
-
server.
|
|
435
|
+
server.keepAliveTimeout = 0;
|
|
436
|
+
server.unref();
|
|
437
|
+
debug("auth", "starting callback server...");
|
|
438
|
+
server.listen(0, "127.0.0.1", () => {
|
|
403
439
|
const addr = server.address();
|
|
404
440
|
if (!addr || typeof addr === "string") {
|
|
405
|
-
|
|
406
|
-
reject(new Error("Failed to start callback server."));
|
|
441
|
+
settle(() => reject(new Error("Failed to start callback server.")));
|
|
407
442
|
return;
|
|
408
443
|
}
|
|
444
|
+
debug("auth", `callback server listening on port ${addr.port}`);
|
|
409
445
|
const callbackUrl = `http://127.0.0.1:${addr.port}/callback`;
|
|
410
446
|
const deeplink = new URL("https://framer.com/projects/server-api/auth");
|
|
411
447
|
deeplink.searchParams.set("callback", callbackUrl);
|
|
412
448
|
deeplink.searchParams.set("state", state);
|
|
413
449
|
deeplink.searchParams.set("projectId", projectId);
|
|
414
450
|
const deeplinkStr = deeplink.toString();
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
451
|
+
debug("auth", "opening browser for deeplink...");
|
|
452
|
+
openUrl(deeplinkStr).then((opened) => {
|
|
453
|
+
if (opened) {
|
|
454
|
+
debug("auth", "browser opened successfully");
|
|
455
|
+
print("Waiting for authorization in browser...");
|
|
456
|
+
} else {
|
|
457
|
+
printError("Could not open browser. Open this URL manually:");
|
|
458
|
+
printError(deeplinkStr);
|
|
459
|
+
}
|
|
460
|
+
});
|
|
422
461
|
});
|
|
423
462
|
});
|
|
424
463
|
}
|
|
@@ -6612,6 +6651,18 @@ var types = {
|
|
|
6612
6651
|
description: "",
|
|
6613
6652
|
optional: false
|
|
6614
6653
|
},
|
|
6654
|
+
{
|
|
6655
|
+
name: "[getCurrentUserMessageType]",
|
|
6656
|
+
type: "() => Promise<User>",
|
|
6657
|
+
description: "",
|
|
6658
|
+
optional: false
|
|
6659
|
+
},
|
|
6660
|
+
{
|
|
6661
|
+
name: "[getProjectInfoMessageType]",
|
|
6662
|
+
type: "() => Promise<ProjectInfo>",
|
|
6663
|
+
description: "",
|
|
6664
|
+
optional: false
|
|
6665
|
+
},
|
|
6615
6666
|
{
|
|
6616
6667
|
name: "[getHTMLForNodeMessageType]",
|
|
6617
6668
|
type: "(nodeId: NodeId) => Promise<string | null>",
|
|
@@ -14927,9 +14978,16 @@ ${typeDef}`);
|
|
|
14927
14978
|
return { lines, errors };
|
|
14928
14979
|
}
|
|
14929
14980
|
__name(renderDocs, "renderDocs");
|
|
14981
|
+
|
|
14982
|
+
// src/version.ts
|
|
14983
|
+
var VERSION = (
|
|
14984
|
+
// typeof is used to ensure this can be used just via tsx or node etc. without build
|
|
14985
|
+
"0.0.14"
|
|
14986
|
+
);
|
|
14987
|
+
|
|
14988
|
+
// src/relay-client.ts
|
|
14930
14989
|
var __filename$1 = fileURLToPath(import.meta.url);
|
|
14931
14990
|
var __dirname$1 = path4.dirname(__filename$1);
|
|
14932
|
-
var VERSION = "0.0.12" ;
|
|
14933
14991
|
var RELAY_PORT = Number(process.env.FRAMER_CLI_PORT) || 19988;
|
|
14934
14992
|
var client = createTRPCClient({
|
|
14935
14993
|
links: [
|
|
@@ -14967,11 +15025,15 @@ function compareVersions(v1, v2) {
|
|
|
14967
15025
|
__name(compareVersions, "compareVersions");
|
|
14968
15026
|
async function ensureRelayServerRunning(options = {}) {
|
|
14969
15027
|
const { logger, restartOnVersionMismatch = true } = options;
|
|
15028
|
+
debug("relay", `checking server version (client=${VERSION})...`);
|
|
14970
15029
|
const serverVersion = await getRelayServerVersion();
|
|
15030
|
+
debug("relay", `server version=${serverVersion ?? "not running"}`);
|
|
14971
15031
|
if (serverVersion === VERSION) {
|
|
15032
|
+
debug("relay", "version matches, no action needed");
|
|
14972
15033
|
return;
|
|
14973
15034
|
}
|
|
14974
15035
|
if (serverVersion !== null && compareVersions(serverVersion, VERSION) > 0) {
|
|
15036
|
+
debug("relay", "server version is newer, keeping it");
|
|
14975
15037
|
return;
|
|
14976
15038
|
}
|
|
14977
15039
|
if (serverVersion !== null) {
|
|
@@ -14979,11 +15041,13 @@ async function ensureRelayServerRunning(options = {}) {
|
|
|
14979
15041
|
logger?.log(
|
|
14980
15042
|
`Relay server version mismatch (server: ${serverVersion}, client: ${VERSION}), restarting...`
|
|
14981
15043
|
);
|
|
15044
|
+
debug("relay", "shutting down old server...");
|
|
14982
15045
|
try {
|
|
14983
15046
|
await client.shutdown.mutate();
|
|
14984
15047
|
await sleep(500);
|
|
14985
15048
|
} catch {
|
|
14986
15049
|
}
|
|
15050
|
+
debug("relay", "old server shut down");
|
|
14987
15051
|
} else {
|
|
14988
15052
|
return;
|
|
14989
15053
|
}
|
|
@@ -14992,6 +15056,7 @@ async function ensureRelayServerRunning(options = {}) {
|
|
|
14992
15056
|
}
|
|
14993
15057
|
const isRunningFromSource = __filename$1.endsWith(".ts");
|
|
14994
15058
|
const scriptPath = isRunningFromSource ? path4.resolve(__dirname$1, "./start-relay-server.ts") : path4.resolve(__dirname$1, "./start-relay-server.js");
|
|
15059
|
+
debug("relay", `spawning server process: ${scriptPath}`);
|
|
14995
15060
|
const serverProcess = spawn(
|
|
14996
15061
|
isRunningFromSource ? "tsx" : process.execPath,
|
|
14997
15062
|
[scriptPath],
|
|
@@ -15002,9 +15067,13 @@ async function ensureRelayServerRunning(options = {}) {
|
|
|
15002
15067
|
}
|
|
15003
15068
|
);
|
|
15004
15069
|
serverProcess.unref();
|
|
15005
|
-
for (let
|
|
15070
|
+
for (let attempt = 0; attempt < 10; attempt++) {
|
|
15006
15071
|
await sleep(500);
|
|
15007
15072
|
const newVersion = await getRelayServerVersion();
|
|
15073
|
+
debug(
|
|
15074
|
+
"relay",
|
|
15075
|
+
`readiness poll ${attempt + 1}/10: version=${newVersion ?? "not ready"}`
|
|
15076
|
+
);
|
|
15008
15077
|
if (newVersion) {
|
|
15009
15078
|
logger?.log("Relay server started successfully");
|
|
15010
15079
|
return;
|
|
@@ -15138,7 +15207,12 @@ __name(installSkills, "installSkills");
|
|
|
15138
15207
|
|
|
15139
15208
|
// src/cli.ts
|
|
15140
15209
|
var program = new Command();
|
|
15141
|
-
program.name("framer").version(VERSION).description("Framer Server API CLI")
|
|
15210
|
+
program.name("framer").version(VERSION).description("Framer Server API CLI").option("--debug", "Enable debug logging").hook("preAction", (thisCommand) => {
|
|
15211
|
+
const opts = thisCommand.opts();
|
|
15212
|
+
if (opts.debug) {
|
|
15213
|
+
setDebugEnabled(true);
|
|
15214
|
+
}
|
|
15215
|
+
});
|
|
15142
15216
|
async function readStdin() {
|
|
15143
15217
|
const chunks = [];
|
|
15144
15218
|
for await (const chunk of process.stdin) {
|
|
@@ -15163,11 +15237,13 @@ function printSetupSummary(results) {
|
|
|
15163
15237
|
}
|
|
15164
15238
|
__name(printSetupSummary, "printSetupSummary");
|
|
15165
15239
|
async function getAgentSystemPrompt(sessionId) {
|
|
15240
|
+
debug("exec", "getAgentSystemPrompt: calling relay...");
|
|
15166
15241
|
const result = await client.exec.mutate({
|
|
15167
15242
|
sessionId,
|
|
15168
15243
|
code: "return await framer.getAgentSystemPrompt();",
|
|
15169
15244
|
cwd: process.cwd()
|
|
15170
15245
|
});
|
|
15246
|
+
debug("exec", "getAgentSystemPrompt: relay responded");
|
|
15171
15247
|
if (result.error) {
|
|
15172
15248
|
throw new Error(result.error);
|
|
15173
15249
|
}
|
|
@@ -15179,11 +15255,13 @@ async function getAgentSystemPrompt(sessionId) {
|
|
|
15179
15255
|
}
|
|
15180
15256
|
__name(getAgentSystemPrompt, "getAgentSystemPrompt");
|
|
15181
15257
|
async function getAgentContext(sessionId) {
|
|
15258
|
+
debug("exec", "getAgentContext: calling relay...");
|
|
15182
15259
|
const result = await client.exec.mutate({
|
|
15183
15260
|
sessionId,
|
|
15184
15261
|
code: "return await framer.getAgentContext({ pagePath: '/' });",
|
|
15185
15262
|
cwd: process.cwd()
|
|
15186
15263
|
});
|
|
15264
|
+
debug("exec", "getAgentContext: relay responded");
|
|
15187
15265
|
if (result.error) {
|
|
15188
15266
|
throw new Error(result.error);
|
|
15189
15267
|
}
|
|
@@ -15196,16 +15274,19 @@ async function getAgentContext(sessionId) {
|
|
|
15196
15274
|
__name(getAgentContext, "getAgentContext");
|
|
15197
15275
|
async function refreshSkillsFromSession(sessionId, projectId) {
|
|
15198
15276
|
try {
|
|
15277
|
+
debug("skills", "fetching agent system prompt + context...");
|
|
15199
15278
|
const [canvasPrompt, agentContext] = await Promise.all([
|
|
15200
15279
|
getAgentSystemPrompt(sessionId),
|
|
15201
15280
|
getAgentContext(sessionId)
|
|
15202
15281
|
]);
|
|
15282
|
+
debug("skills", "installing skills...");
|
|
15203
15283
|
installSkills({
|
|
15204
15284
|
type: "project",
|
|
15205
15285
|
canvasPrompt,
|
|
15206
15286
|
projectId,
|
|
15207
15287
|
agentContext
|
|
15208
15288
|
});
|
|
15289
|
+
debug("skills", "skills installed");
|
|
15209
15290
|
} catch (err) {
|
|
15210
15291
|
printError(
|
|
15211
15292
|
`Failed to refresh skills for session ${sessionId}: ${formatError(err)}`
|
|
@@ -15217,11 +15298,15 @@ __name(refreshSkillsFromSession, "refreshSkillsFromSession");
|
|
|
15217
15298
|
async function resolveSessionCredentials(projectUrlOrId) {
|
|
15218
15299
|
try {
|
|
15219
15300
|
const projectId = extractProjectId(projectUrlOrId);
|
|
15301
|
+
debug("credentials", `projectId=${projectId}`);
|
|
15220
15302
|
const cachedApiKey = getApiKey(projectId);
|
|
15221
15303
|
if (cachedApiKey) {
|
|
15304
|
+
debug("credentials", "using cached API key");
|
|
15222
15305
|
return { projectId, apiKey: cachedApiKey };
|
|
15223
15306
|
}
|
|
15307
|
+
debug("credentials", "no cached key, starting browser auth flow...");
|
|
15224
15308
|
const newApiKey = await acquireKeyFromBrowser(projectId);
|
|
15309
|
+
debug("credentials", "browser auth complete, saving project");
|
|
15225
15310
|
saveProject({ projectId, apiKey: newApiKey });
|
|
15226
15311
|
return { projectId, apiKey: newApiKey };
|
|
15227
15312
|
} catch (err) {
|
|
@@ -15231,11 +15316,13 @@ async function resolveSessionCredentials(projectUrlOrId) {
|
|
|
15231
15316
|
}
|
|
15232
15317
|
__name(resolveSessionCredentials, "resolveSessionCredentials");
|
|
15233
15318
|
async function getProjectName(sessionId) {
|
|
15319
|
+
debug("exec", "getProjectName: calling relay...");
|
|
15234
15320
|
const result = await client.exec.mutate({
|
|
15235
15321
|
sessionId,
|
|
15236
15322
|
code: "return (await framer.getProjectInfo()).name;",
|
|
15237
15323
|
cwd: process.cwd()
|
|
15238
15324
|
});
|
|
15325
|
+
debug("exec", "getProjectName: relay responded");
|
|
15239
15326
|
if (result.error) {
|
|
15240
15327
|
throw new Error(result.error);
|
|
15241
15328
|
}
|
|
@@ -15248,7 +15335,9 @@ async function getProjectName(sessionId) {
|
|
|
15248
15335
|
__name(getProjectName, "getProjectName");
|
|
15249
15336
|
async function ensureRelayForCli() {
|
|
15250
15337
|
try {
|
|
15338
|
+
debug("relay", "ensuring relay server is running...");
|
|
15251
15339
|
await ensureRelayServerRunning({ logger: { log: print } });
|
|
15340
|
+
debug("relay", "relay server ready");
|
|
15252
15341
|
} catch (err) {
|
|
15253
15342
|
printError(`Failed to check relay status: ${formatError(err)}`);
|
|
15254
15343
|
process.exit(1);
|
|
@@ -15305,27 +15394,39 @@ program.option("-s, --session <id>", "Session ID (required for code execution)")
|
|
|
15305
15394
|
});
|
|
15306
15395
|
var session = program.command("session").description("Manage sessions");
|
|
15307
15396
|
session.command("new <projectUrlOrId>").description("Create a new session and print the session ID").action(async (projectUrlOrId) => {
|
|
15397
|
+
debug("session.new", `starting for ${projectUrlOrId}`);
|
|
15398
|
+
debug("session.new", "resolving credentials + ensuring relay...");
|
|
15308
15399
|
const [credentials] = await Promise.all([
|
|
15309
15400
|
resolveSessionCredentials(projectUrlOrId),
|
|
15310
15401
|
ensureRelayForCli()
|
|
15311
15402
|
]);
|
|
15403
|
+
debug(
|
|
15404
|
+
"session.new",
|
|
15405
|
+
`credentials resolved for project=${credentials.projectId}`
|
|
15406
|
+
);
|
|
15312
15407
|
try {
|
|
15408
|
+
debug("session.new", "calling createSession on relay...");
|
|
15313
15409
|
const result = await client.createSession.mutate({
|
|
15314
15410
|
projectId: credentials.projectId,
|
|
15315
15411
|
apiKey: credentials.apiKey
|
|
15316
15412
|
});
|
|
15317
15413
|
const sessionId = result.id;
|
|
15414
|
+
debug("session.new", `session created id=${sessionId}`);
|
|
15415
|
+
debug("session.new", "fetching project name + refreshing skills...");
|
|
15318
15416
|
const [projectName] = await Promise.all([
|
|
15319
15417
|
getProjectName(sessionId),
|
|
15320
15418
|
refreshSkillsFromSession(sessionId, credentials.projectId)
|
|
15321
15419
|
]);
|
|
15420
|
+
debug("session.new", `project name="${projectName}", skills refreshed`);
|
|
15322
15421
|
saveProject({
|
|
15323
15422
|
projectId: credentials.projectId,
|
|
15324
15423
|
apiKey: credentials.apiKey,
|
|
15325
15424
|
name: projectName,
|
|
15326
15425
|
lastUsedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
15327
15426
|
});
|
|
15427
|
+
debug("session.new", "project saved, printing session id");
|
|
15328
15428
|
print(sessionId);
|
|
15429
|
+
debug("session.new", "done \u2014 process should exit now");
|
|
15329
15430
|
} catch (err) {
|
|
15330
15431
|
const message = formatError(err);
|
|
15331
15432
|
printError(`Failed to create session: ${message}`);
|
|
@@ -13,7 +13,7 @@ import { createRequire } from 'module';
|
|
|
13
13
|
import * as vm from 'vm';
|
|
14
14
|
import { connect } from 'framer-api';
|
|
15
15
|
|
|
16
|
-
/* @framer/ai relay server v0.0.
|
|
16
|
+
/* @framer/ai relay server v0.0.14 */
|
|
17
17
|
var __defProp = Object.defineProperty;
|
|
18
18
|
var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name);
|
|
19
19
|
var __typeError = (msg) => {
|
|
@@ -89,9 +89,16 @@ function log(message) {
|
|
|
89
89
|
`);
|
|
90
90
|
}
|
|
91
91
|
__name(log, "log");
|
|
92
|
+
|
|
93
|
+
// src/version.ts
|
|
94
|
+
var VERSION = (
|
|
95
|
+
// typeof is used to ensure this can be used just via tsx or node etc. without build
|
|
96
|
+
"0.0.14"
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
// src/relay-client.ts
|
|
92
100
|
var __filename$1 = fileURLToPath(import.meta.url);
|
|
93
101
|
path.dirname(__filename$1);
|
|
94
|
-
var VERSION = "0.0.12" ;
|
|
95
102
|
var RELAY_PORT = Number(process.env.FRAMER_CLI_PORT) || 19988;
|
|
96
103
|
createTRPCClient({
|
|
97
104
|
links: [
|
|
@@ -527,7 +534,9 @@ var ConnectionPool = class {
|
|
|
527
534
|
}
|
|
528
535
|
return entry.connection;
|
|
529
536
|
}
|
|
530
|
-
const connection = await connect(projectId, apiKey
|
|
537
|
+
const connection = await connect(projectId, apiKey, {
|
|
538
|
+
clientId: `dalton/${VERSION}`
|
|
539
|
+
});
|
|
531
540
|
this.pool.set(projectId, {
|
|
532
541
|
connection,
|
|
533
542
|
sessions: /* @__PURE__ */ new Set([session]),
|
|
@@ -796,6 +805,7 @@ var appRouter = t.router({
|
|
|
796
805
|
error: "Failed to get connection for session"
|
|
797
806
|
};
|
|
798
807
|
}
|
|
808
|
+
const start = performance.now();
|
|
799
809
|
const result = await executeWithReconnect(
|
|
800
810
|
session,
|
|
801
811
|
framer,
|
|
@@ -804,8 +814,11 @@ var appRouter = t.router({
|
|
|
804
814
|
() => sessionManager.reconnect(session),
|
|
805
815
|
execId
|
|
806
816
|
);
|
|
817
|
+
const elapsed = (performance.now() - start).toFixed(0);
|
|
807
818
|
if (result.error) {
|
|
808
|
-
log(`exec.error ${tag} error="${result.error}"`);
|
|
819
|
+
log(`exec.error ${tag} ${elapsed}ms error="${result.error}"`);
|
|
820
|
+
} else {
|
|
821
|
+
log(`exec.done ${tag} ${elapsed}ms`);
|
|
809
822
|
}
|
|
810
823
|
return result;
|
|
811
824
|
} catch (_) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: {{SKILL_NAME}}
|
|
3
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."
|
|
4
|
-
allowed-tools: ["Bash(npx framer-dalton:*)", "Bash(npx framer-dalton@latest:*)", "Write({{FRAMER_TEMPORARY_DIR}}/*)"]
|
|
4
|
+
allowed-tools: ["Bash(npx framer-dalton:*)", "Bash(npx framer-dalton@latest:*)", "Read({{FRAMER_TEMPORARY_DIR}}/*)", "Write({{FRAMER_TEMPORARY_DIR}}/*)"]
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
## Project Scope
|
|
@@ -49,6 +49,12 @@ framer -s <sessionId> -f {{FRAMER_TEMPORARY_DIR}}/<sessionId>-read-page.js
|
|
|
49
49
|
framer -s <sessionId> -f {{FRAMER_TEMPORARY_DIR}}/<sessionId>-apply-changes.js
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
+
## Critical for UX: Streaming
|
|
53
|
+
|
|
54
|
+
When you generate a DSL file to apply, keep it short. Each file must only cover ONE logical section (header, footer, hero, card, etc.). Once you generate ONE section, apply it and continue with the next section.
|
|
55
|
+
|
|
56
|
+
This ensures progress is streamed to the user in small, visible increments.
|
|
57
|
+
|
|
52
58
|
## Live Agent System Prompt
|
|
53
59
|
|
|
54
60
|
This is the static canvas-editing prompt returned by `framer.getAgentSystemPrompt()`. It is the sole documentation for:
|
package/docs/skills/framer.md
CHANGED
|
@@ -3,7 +3,7 @@ name: framer
|
|
|
3
3
|
description: >
|
|
4
4
|
Use when the user wants to design, edit, or publish a website or web page — creating layouts, editing sections, updating text or images, managing CMS collections and content, syncing external data, creating or modifying code components, managing color and text styles, handling localization, or publishing deployments. Trigger when the user mentions Framer, references their website or web pages, asks to edit designs, update site content, or work with any Framer project — even if they don't explicitly say 'Framer'.
|
|
5
5
|
**Mandatory precondition**: run `npx framer-dalton@latest setup` and let it complete **BEFORE** loading this skill.
|
|
6
|
-
allowed-tools: ["Bash(npx framer-dalton:*)", "Bash(npx framer-dalton@latest:*)", "Write({{FRAMER_TEMPORARY_DIR}}/*)"]
|
|
6
|
+
allowed-tools: ["Bash(npx framer-dalton:*)", "Bash(npx framer-dalton@latest:*)", "Read({{FRAMER_TEMPORARY_DIR}}/*)", "Write({{FRAMER_TEMPORARY_DIR}}/*)"]
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
If you didn't run this command before loading the skill, run it now:
|
|
@@ -28,6 +28,10 @@ What you can do with the Framer CLI:
|
|
|
28
28
|
|
|
29
29
|
## CLI Usage
|
|
30
30
|
|
|
31
|
+
### Permissions
|
|
32
|
+
|
|
33
|
+
The Framer CLI requires network access (to download packages and connect to Framer servers) and file-system access outside the project directory (to install skills into `~/.agents/skills/` and `~/.claude/skills/`). In sandboxed environments, you must execute `npx framer-dalton` commands with elevated permissions from the get-go. Do not try to execute them with the default sandboxed permissions; they will hang or fail.
|
|
34
|
+
|
|
31
35
|
### Required Workflow
|
|
32
36
|
|
|
33
37
|
Every task follows these steps:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "framer-dalton",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.14",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"framer-dalton": "./dist/cli.js"
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"@trpc/client": "^11.9.0",
|
|
26
26
|
"@trpc/server": "^11.9.0",
|
|
27
27
|
"commander": "^12.1.0",
|
|
28
|
-
"framer-api": "
|
|
28
|
+
"framer-api": "^0.1.4",
|
|
29
29
|
"zod": "^4.3.6"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|