@framer/agent 0.0.33 → 0.0.34
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 +14 -0
- package/dist/cli.js +194 -66
- package/dist/start-relay-server.js +71 -82
- package/docs/skills/framer-project.md +17 -17
- package/docs/skills/framer.md +11 -11
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -121,3 +121,17 @@ Usage will only consume your own agent's tokens.
|
|
|
121
121
|
### Why does this use so many tokens?
|
|
122
122
|
|
|
123
123
|
Token consumption for Framer Agent can be relatively high. For other work like web development tasks, your agent already has a lot of knowledge baked into its model and so is able to get to work straight away. For Framer Agent, we need to first teach your AI how to interact with Framer projects and provide a lot of context. This requires a certain amount of token usage before being able to undertake the task at hand.
|
|
124
|
+
|
|
125
|
+
### How do I get the latest releases if I have `min-release-age` set in my npm config?
|
|
126
|
+
|
|
127
|
+
If your npm config delays new packages with `min-release-age`, you can make generated Framer Agent skills override that delay. This updates your installed skills immediately:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
npx --min-release-age=0 @framer/agent@latest override-min-release-age enable
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
To restore your default npm behavior in generated skills:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
npx @framer/agent@latest override-min-release-age disable
|
|
137
|
+
```
|
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
2
|
+
import fs8 from 'fs';
|
|
3
3
|
import path7 from 'path';
|
|
4
4
|
import { Command } from 'commander';
|
|
5
5
|
import crypto, { randomUUID } from 'crypto';
|
|
@@ -7,12 +7,13 @@ import http from 'http';
|
|
|
7
7
|
import { spawn, execFile, execFileSync } from 'child_process';
|
|
8
8
|
import { z } from 'zod';
|
|
9
9
|
import os2 from 'os';
|
|
10
|
+
import isWsl from 'is-wsl';
|
|
10
11
|
import { ErrorCode, FramerAPIError } from 'framer-api';
|
|
11
12
|
import net from 'net';
|
|
12
13
|
import { fileURLToPath } from 'url';
|
|
13
14
|
import { createTRPCClient, httpLink } from '@trpc/client';
|
|
14
15
|
|
|
15
|
-
/* @framer/ai CLI v0.0.
|
|
16
|
+
/* @framer/ai CLI v0.0.34 */
|
|
16
17
|
var __defProp = Object.defineProperty;
|
|
17
18
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
18
19
|
|
|
@@ -34,11 +35,12 @@ Please upgrade: https://nodejs.org/`
|
|
|
34
35
|
var MAX_AGENT_THREAD_ID_LENGTH = 256;
|
|
35
36
|
var MAX_AGENT_PROVIDER_LENGTH = 64;
|
|
36
37
|
var SAFE_VALUE_PATTERN = /^[A-Za-z0-9._:-]+$/;
|
|
37
|
-
var
|
|
38
|
+
var threadIdEnvVars = [
|
|
38
39
|
{ provider: "codex", envVar: "CODEX_THREAD_ID" },
|
|
39
40
|
{ provider: "claude", envVar: "CLAUDE_CODE_SESSION_ID" },
|
|
40
41
|
{ provider: "claude", envVar: "CLAUDE_CODE_REMOTE_SESSION_ID" }
|
|
41
42
|
];
|
|
43
|
+
var providerPresenceEnvVars = [{ provider: "cursor", envVar: "CURSOR_AGENT" }];
|
|
42
44
|
function normalizeValue(value, maxLength) {
|
|
43
45
|
const trimmed = value?.trim();
|
|
44
46
|
if (!trimmed) return void 0;
|
|
@@ -48,29 +50,38 @@ function normalizeValue(value, maxLength) {
|
|
|
48
50
|
}
|
|
49
51
|
__name(normalizeValue, "normalizeValue");
|
|
50
52
|
function normalizeAgentThreadContext(input) {
|
|
53
|
+
const trimmedAgentThreadId = input.agentThreadId?.trim();
|
|
51
54
|
const agentThreadId = normalizeValue(
|
|
52
55
|
input.agentThreadId,
|
|
53
56
|
MAX_AGENT_THREAD_ID_LENGTH
|
|
54
57
|
);
|
|
55
|
-
if (!agentThreadId) return {};
|
|
58
|
+
if (trimmedAgentThreadId && !agentThreadId) return {};
|
|
56
59
|
const agentProvider = normalizeValue(
|
|
57
60
|
input.agentProvider,
|
|
58
61
|
MAX_AGENT_PROVIDER_LENGTH
|
|
59
62
|
);
|
|
63
|
+
if (!agentThreadId && !agentProvider) return {};
|
|
60
64
|
return {
|
|
61
|
-
agentThreadId,
|
|
65
|
+
...agentThreadId ? { agentThreadId } : {},
|
|
62
66
|
...agentProvider ? { agentProvider } : {}
|
|
63
67
|
};
|
|
64
68
|
}
|
|
65
69
|
__name(normalizeAgentThreadContext, "normalizeAgentThreadContext");
|
|
66
70
|
function resolveAgentThreadContext(env = process.env) {
|
|
67
|
-
for (const { provider, envVar } of
|
|
71
|
+
for (const { provider, envVar } of threadIdEnvVars) {
|
|
68
72
|
const agentThreadContext = normalizeAgentThreadContext({
|
|
69
73
|
agentThreadId: env[envVar],
|
|
70
74
|
agentProvider: provider
|
|
71
75
|
});
|
|
72
76
|
if (agentThreadContext.agentThreadId) return agentThreadContext;
|
|
73
77
|
}
|
|
78
|
+
for (const { provider, envVar } of providerPresenceEnvVars) {
|
|
79
|
+
if (!env[envVar]?.trim()) continue;
|
|
80
|
+
const agentThreadContext = normalizeAgentThreadContext({
|
|
81
|
+
agentProvider: provider
|
|
82
|
+
});
|
|
83
|
+
if (agentThreadContext.agentProvider) return agentThreadContext;
|
|
84
|
+
}
|
|
74
85
|
return {};
|
|
75
86
|
}
|
|
76
87
|
__name(resolveAgentThreadContext, "resolveAgentThreadContext");
|
|
@@ -107,7 +118,7 @@ function getConfigDir() {
|
|
|
107
118
|
__name(getConfigDir, "getConfigDir");
|
|
108
119
|
function ensureConfigDir() {
|
|
109
120
|
const configDir = getConfigDir();
|
|
110
|
-
|
|
121
|
+
fs8.mkdirSync(configDir, { recursive: true, mode: 448 });
|
|
111
122
|
}
|
|
112
123
|
__name(ensureConfigDir, "ensureConfigDir");
|
|
113
124
|
|
|
@@ -135,7 +146,7 @@ var ProjectsConfigSchema = z.object({
|
|
|
135
146
|
var LegacyCredentialsSchema = z.record(z.string(), z.string());
|
|
136
147
|
function readJsonFile(filePath) {
|
|
137
148
|
try {
|
|
138
|
-
return JSON.parse(
|
|
149
|
+
return JSON.parse(fs8.readFileSync(filePath, "utf-8"));
|
|
139
150
|
} catch (_error) {
|
|
140
151
|
return null;
|
|
141
152
|
}
|
|
@@ -143,7 +154,7 @@ function readJsonFile(filePath) {
|
|
|
143
154
|
__name(readJsonFile, "readJsonFile");
|
|
144
155
|
function writeProjectsConfig(config) {
|
|
145
156
|
ensureConfigDir();
|
|
146
|
-
|
|
157
|
+
fs8.writeFileSync(
|
|
147
158
|
getProjectsConfigPath(),
|
|
148
159
|
JSON.stringify(config, null, " "),
|
|
149
160
|
{
|
|
@@ -170,7 +181,7 @@ function migrateLegacyCredentials() {
|
|
|
170
181
|
}
|
|
171
182
|
const config = { version: 2, projects };
|
|
172
183
|
writeProjectsConfig(config);
|
|
173
|
-
|
|
184
|
+
fs8.rmSync(getLegacyCredentialsPath(), { force: true });
|
|
174
185
|
return config;
|
|
175
186
|
}
|
|
176
187
|
__name(migrateLegacyCredentials, "migrateLegacyCredentials");
|
|
@@ -183,7 +194,7 @@ function readProjectsConfig() {
|
|
|
183
194
|
return result.data;
|
|
184
195
|
}
|
|
185
196
|
}
|
|
186
|
-
if (
|
|
197
|
+
if (fs8.existsSync(getLegacyCredentialsPath())) {
|
|
187
198
|
return migrateLegacyCredentials();
|
|
188
199
|
}
|
|
189
200
|
return { version: 2, projects: {} };
|
|
@@ -266,7 +277,7 @@ var SettingsWatcher = class {
|
|
|
266
277
|
__name(this, "SettingsWatcher");
|
|
267
278
|
}
|
|
268
279
|
start(callback) {
|
|
269
|
-
|
|
280
|
+
fs8.watchFile(
|
|
270
281
|
this.settingsPath,
|
|
271
282
|
{ persistent: false, interval: fsPollIntervalMs },
|
|
272
283
|
(curr, prev) => {
|
|
@@ -283,7 +294,7 @@ var SettingsWatcher = class {
|
|
|
283
294
|
return this;
|
|
284
295
|
}
|
|
285
296
|
stop() {
|
|
286
|
-
|
|
297
|
+
fs8.unwatchFile(this.settingsPath);
|
|
287
298
|
return this;
|
|
288
299
|
}
|
|
289
300
|
};
|
|
@@ -292,11 +303,13 @@ var SettingsWatcher = class {
|
|
|
292
303
|
var DEFAULT_MACHINE_ID = "unknown-machine-id-this-should-never-be-persisted";
|
|
293
304
|
var DEFAULT_SETTINGS = {
|
|
294
305
|
machineId: DEFAULT_MACHINE_ID,
|
|
306
|
+
npxMinReleaseAgeOverrideEnabled: false,
|
|
295
307
|
telemetryEnabled: true,
|
|
296
308
|
telemetryNoticeShown: false
|
|
297
309
|
};
|
|
298
310
|
var SettingsFileSchema = z.object({
|
|
299
311
|
machineId: z.string().optional(),
|
|
312
|
+
npxMinReleaseAgeOverrideEnabled: z.boolean().optional(),
|
|
300
313
|
telemetryEnabled: z.boolean().optional(),
|
|
301
314
|
telemetryNoticeShown: z.boolean().optional()
|
|
302
315
|
});
|
|
@@ -320,10 +333,11 @@ __name(getSettings, "getSettings");
|
|
|
320
333
|
function readSettingsFile() {
|
|
321
334
|
const settingsPath = getSettingsPath();
|
|
322
335
|
try {
|
|
323
|
-
const raw = JSON.parse(
|
|
336
|
+
const raw = JSON.parse(fs8.readFileSync(settingsPath, "utf-8"));
|
|
324
337
|
const result = SettingsFileSchema.parse(raw);
|
|
325
338
|
return {
|
|
326
339
|
machineId: result.machineId ?? DEFAULT_SETTINGS.machineId,
|
|
340
|
+
npxMinReleaseAgeOverrideEnabled: result.npxMinReleaseAgeOverrideEnabled ?? DEFAULT_SETTINGS.npxMinReleaseAgeOverrideEnabled,
|
|
327
341
|
telemetryEnabled: result.telemetryEnabled ?? DEFAULT_SETTINGS.telemetryEnabled,
|
|
328
342
|
telemetryNoticeShown: result.telemetryNoticeShown ?? DEFAULT_SETTINGS.telemetryNoticeShown
|
|
329
343
|
};
|
|
@@ -339,7 +353,7 @@ function setSettings(newSettings) {
|
|
|
339
353
|
__name(setSettings, "setSettings");
|
|
340
354
|
function writeSettingsFile(settings2) {
|
|
341
355
|
ensureConfigDir();
|
|
342
|
-
|
|
356
|
+
fs8.writeFileSync(getSettingsPath(), JSON.stringify(settings2, null, " "), {
|
|
343
357
|
mode: 384
|
|
344
358
|
});
|
|
345
359
|
}
|
|
@@ -353,6 +367,15 @@ function getMachineId() {
|
|
|
353
367
|
return getSettings().machineId;
|
|
354
368
|
}
|
|
355
369
|
__name(getMachineId, "getMachineId");
|
|
370
|
+
function isNpxMinReleaseAgeOverrideEnabled() {
|
|
371
|
+
return getSettings().npxMinReleaseAgeOverrideEnabled;
|
|
372
|
+
}
|
|
373
|
+
__name(isNpxMinReleaseAgeOverrideEnabled, "isNpxMinReleaseAgeOverrideEnabled");
|
|
374
|
+
function setNpxMinReleaseAgeOverrideEnabled(enabled2) {
|
|
375
|
+
const settings2 = getSettings();
|
|
376
|
+
setSettings({ ...settings2, npxMinReleaseAgeOverrideEnabled: enabled2 });
|
|
377
|
+
}
|
|
378
|
+
__name(setNpxMinReleaseAgeOverrideEnabled, "setNpxMinReleaseAgeOverrideEnabled");
|
|
356
379
|
function isTelemetryEnabled() {
|
|
357
380
|
return getSettings().telemetryEnabled;
|
|
358
381
|
}
|
|
@@ -375,7 +398,7 @@ __name(markTelemetryNoticeShown, "markTelemetryNoticeShown");
|
|
|
375
398
|
// src/version.ts
|
|
376
399
|
var VERSION = (
|
|
377
400
|
// typeof is used to ensure this can be used just via tsx or node etc. without build
|
|
378
|
-
"0.0.
|
|
401
|
+
"0.0.34"
|
|
379
402
|
);
|
|
380
403
|
var trackingEndpoint = "https://events.framer.com/track";
|
|
381
404
|
var inProgressTrackings = /* @__PURE__ */ new Set();
|
|
@@ -405,8 +428,27 @@ function getMacOsVersion() {
|
|
|
405
428
|
}
|
|
406
429
|
}
|
|
407
430
|
__name(getMacOsVersion, "getMacOsVersion");
|
|
431
|
+
function parseOsReleaseValue(content, key) {
|
|
432
|
+
const line = content.split("\n").find((candidate) => candidate.startsWith(`${key}=`));
|
|
433
|
+
const value = line?.slice(key.length + 1).trim();
|
|
434
|
+
if (!value) return void 0;
|
|
435
|
+
return value.replace(/^"|"$/g, "") || void 0;
|
|
436
|
+
}
|
|
437
|
+
__name(parseOsReleaseValue, "parseOsReleaseValue");
|
|
438
|
+
function getLinuxOsVersion() {
|
|
439
|
+
let version;
|
|
440
|
+
try {
|
|
441
|
+
const osRelease = fs8.readFileSync("/etc/os-release", "utf8");
|
|
442
|
+
version = parseOsReleaseValue(osRelease, "PRETTY_NAME") ?? "unknown";
|
|
443
|
+
} catch {
|
|
444
|
+
version = os2.release().trim() || "unknown";
|
|
445
|
+
}
|
|
446
|
+
return isWsl ? `${version} wsl` : version;
|
|
447
|
+
}
|
|
448
|
+
__name(getLinuxOsVersion, "getLinuxOsVersion");
|
|
408
449
|
function getOsVersion(platform) {
|
|
409
450
|
if (platform === "darwin") return getMacOsVersion();
|
|
451
|
+
if (platform === "linux") return getLinuxOsVersion();
|
|
410
452
|
const version = os2.release().trim();
|
|
411
453
|
return version || "unknown";
|
|
412
454
|
}
|
|
@@ -654,6 +696,7 @@ var themes = {
|
|
|
654
696
|
};
|
|
655
697
|
function htmlPage(opts) {
|
|
656
698
|
const t = themes[opts.theme];
|
|
699
|
+
const footer = opts.action ? `<div class="footer"><a href="${opts.action.href}">${opts.action.label}</a></div>` : "";
|
|
657
700
|
return `<!DOCTYPE html>
|
|
658
701
|
<html lang="en">
|
|
659
702
|
<head>
|
|
@@ -670,26 +713,28 @@ body{font-family:"Inter",system-ui,-apple-system,sans-serif;font-feature-setting
|
|
|
670
713
|
.header{display:flex;align-items:center;height:50px;border-bottom:1px solid ${t.separatorColor};color:${t.titleColor};font-size:12px;font-weight:600;line-height:1}
|
|
671
714
|
.content{padding:10px 0}
|
|
672
715
|
.text{color:${t.textColor};font-size:12px;font-weight:500;line-height:1.5;text-wrap:balance}
|
|
673
|
-
.footer{padding:
|
|
674
|
-
.footer
|
|
675
|
-
.footer
|
|
716
|
+
.footer{padding:0 0 10px}
|
|
717
|
+
.footer a{display:block;width:100%;height:30px;border:none;border-radius:8px;background:${t.buttonBackground};color:${t.buttonText};font-size:12px;font-weight:600;line-height:30px;text-align:center;cursor:pointer;font-family:inherit;text-decoration:none}
|
|
718
|
+
.footer a:hover{background:${t.buttonBackgroundHover}}
|
|
676
719
|
</style>
|
|
677
720
|
</head>
|
|
678
721
|
<body>
|
|
679
722
|
<div class="modal">
|
|
680
723
|
<div class="header">${opts.heading}</div>
|
|
681
724
|
<div class="content"><span class="text">${opts.message}</span></div>
|
|
725
|
+
${footer}
|
|
682
726
|
</div>
|
|
683
727
|
</body>
|
|
684
728
|
</html>`;
|
|
685
729
|
}
|
|
686
730
|
__name(htmlPage, "htmlPage");
|
|
687
|
-
function successHtml(theme, message) {
|
|
731
|
+
function successHtml(theme, message, action) {
|
|
688
732
|
return htmlPage({
|
|
689
733
|
title: "Framer \u2014 Agent Approved",
|
|
690
734
|
heading: "Agent Approved",
|
|
691
735
|
message: message ?? "Your external agent now has edit access to the project. You can close this tab and continue with the external agent.",
|
|
692
|
-
theme
|
|
736
|
+
theme,
|
|
737
|
+
action
|
|
693
738
|
});
|
|
694
739
|
}
|
|
695
740
|
__name(successHtml, "successHtml");
|
|
@@ -729,12 +774,18 @@ __name(isFramerHostname, "isFramerHostname");
|
|
|
729
774
|
function resolveProjectOrigin(projectUrlOrId) {
|
|
730
775
|
try {
|
|
731
776
|
const url = new URL(projectUrlOrId);
|
|
732
|
-
if (isFramerHostname(url.hostname.toLowerCase()))
|
|
777
|
+
if (url.protocol === "https:" && isFramerHostname(url.hostname.toLowerCase())) {
|
|
778
|
+
return url.origin;
|
|
779
|
+
}
|
|
733
780
|
} catch {
|
|
734
781
|
}
|
|
735
782
|
return "https://framer.com";
|
|
736
783
|
}
|
|
737
784
|
__name(resolveProjectOrigin, "resolveProjectOrigin");
|
|
785
|
+
function projectUrl(projectOrigin, projectId) {
|
|
786
|
+
return new URL(`/projects/${encodeURIComponent(projectId)}`, projectOrigin).href;
|
|
787
|
+
}
|
|
788
|
+
__name(projectUrl, "projectUrl");
|
|
738
789
|
function runBrowserAuthFlow(options) {
|
|
739
790
|
const {
|
|
740
791
|
deeplinkParams,
|
|
@@ -762,8 +813,13 @@ function runBrowserAuthFlow(options) {
|
|
|
762
813
|
debug("auth", `request: ${url.pathname}`);
|
|
763
814
|
if (url.pathname === "/done") {
|
|
764
815
|
const theme2 = parseTheme(url.searchParams.get("theme"));
|
|
816
|
+
const projectId = pendingAuth?.projectId ?? pollProjectId;
|
|
817
|
+
const action = projectId ? {
|
|
818
|
+
label: "Open Project",
|
|
819
|
+
href: projectUrl(projectOrigin, projectId)
|
|
820
|
+
} : void 0;
|
|
765
821
|
res.writeHead(200, { "Content-Type": "text/html" });
|
|
766
|
-
res.end(successHtml(theme2, browserSuccessMessage), () => {
|
|
822
|
+
res.end(successHtml(theme2, browserSuccessMessage, action), () => {
|
|
767
823
|
if (pendingAuth) {
|
|
768
824
|
const auth = pendingAuth;
|
|
769
825
|
pendingAuth = null;
|
|
@@ -1112,11 +1168,14 @@ var types = {
|
|
|
1112
1168
|
name: "AgentBranchChange",
|
|
1113
1169
|
description: "",
|
|
1114
1170
|
kind: "interface",
|
|
1115
|
-
references: [
|
|
1171
|
+
references: [
|
|
1172
|
+
"AgentBranchCollectionItemChange",
|
|
1173
|
+
"AgentBranchChangeSettingsTab"
|
|
1174
|
+
],
|
|
1116
1175
|
members: [
|
|
1117
1176
|
{
|
|
1118
1177
|
name: "type",
|
|
1119
|
-
type: '"page" | "collection" | "designPage" | "siteSettings" | "component" | "layoutTemplate"',
|
|
1178
|
+
type: '"page" | "collection" | "designPage" | "siteSettings" | "component" | "layoutTemplate" | "vectorSet" | "codeFile"',
|
|
1120
1179
|
description: "",
|
|
1121
1180
|
optional: false
|
|
1122
1181
|
},
|
|
@@ -1146,12 +1205,19 @@ var types = {
|
|
|
1146
1205
|
},
|
|
1147
1206
|
{
|
|
1148
1207
|
name: "settingsTab",
|
|
1149
|
-
type:
|
|
1208
|
+
type: "AgentBranchChangeSettingsTab",
|
|
1150
1209
|
description: "",
|
|
1151
1210
|
optional: true
|
|
1152
1211
|
}
|
|
1153
1212
|
]
|
|
1154
1213
|
},
|
|
1214
|
+
agentbranchchangesettingstab: {
|
|
1215
|
+
name: "AgentBranchChangeSettingsTab",
|
|
1216
|
+
description: "",
|
|
1217
|
+
kind: "alias",
|
|
1218
|
+
alias: '"general" | "redirects" | "customCode"',
|
|
1219
|
+
references: []
|
|
1220
|
+
},
|
|
1155
1221
|
agentbranchcollectionitemchange: {
|
|
1156
1222
|
name: "AgentBranchCollectionItemChange",
|
|
1157
1223
|
description: "",
|
|
@@ -7558,7 +7624,7 @@ var types = {
|
|
|
7558
7624
|
},
|
|
7559
7625
|
{
|
|
7560
7626
|
name: "applyAgentChanges",
|
|
7561
|
-
type: "(dsl: string, options?: {\n pagePath?: string;\n }) => Promise<
|
|
7627
|
+
type: "(dsl: string, options?: {\n pagePath?: string;\n }) => Promise<unknown>",
|
|
7562
7628
|
description: "@alpha",
|
|
7563
7629
|
optional: false
|
|
7564
7630
|
},
|
|
@@ -14123,8 +14189,8 @@ var methodsByCategory = {
|
|
|
14123
14189
|
{
|
|
14124
14190
|
name: "applyChanges",
|
|
14125
14191
|
category: "FramerAgentAPI",
|
|
14126
|
-
signature: "applyChanges(dsl: string, options?: { pagePath?: string; }): Promise<
|
|
14127
|
-
description: 'Applies commands to the canvas to create, update, remove, move, or duplicate nodes.\n\nThe command syntax is documented in the string returned by {@link getSystemPrompt}.\nEach call is scoped to a single page.\n\n@param dsl - A string of commands separated by `;`. See {@link getSystemPrompt} for syntax.\n@param options.pagePath - Target page path (e.g. `"/about"`). Defaults to the active page.',
|
|
14192
|
+
signature: "applyChanges(dsl: string, options?: { pagePath?: string; }): Promise<unknown>",
|
|
14193
|
+
description: 'Applies commands to the canvas to create, update, remove, move, or duplicate nodes.\n\nThe command syntax is documented in the string returned by {@link getSystemPrompt}.\nEach call is scoped to a single page.\n\nCommands that fail (including syntax errors) are reported in the result\'s `errors`\nwithout blocking the remaining commands. Errors returned here are not repeated by\n{@link reviewChanges}, which reports the remaining project diagnostics.\n\nThe result schema is documented in the string returned by {@link getSystemPrompt}.\n\n@param dsl - A string of commands separated by `;`. See {@link getSystemPrompt} for syntax.\n@param options.pagePath - Target page path (e.g. `"/about"`). Defaults to the active page.\n@returns The per-command status and errors, and the canonical system ids of created nodes.',
|
|
14128
14194
|
references: []
|
|
14129
14195
|
},
|
|
14130
14196
|
{
|
|
@@ -14334,7 +14400,7 @@ var methodsByCategory = {
|
|
|
14334
14400
|
name: "reviewChanges",
|
|
14335
14401
|
category: "FramerAgentAPI",
|
|
14336
14402
|
signature: "reviewChanges(): Promise<unknown>",
|
|
14337
|
-
description: "Reviews changes made by prior {@link applyChanges} calls in this session.\n\nConsumes accumulated diagnostics from the session's agent context.\n\n@returns The session's accumulated changes, errors, warnings, and deferred trait reports.",
|
|
14403
|
+
description: "Reviews changes made by prior {@link applyChanges} calls in this session.\n\nConsumes accumulated diagnostics from the session's agent context. Command errors\nalready returned by {@link applyChanges} are not repeated here.\n\n@returns The session's accumulated changes, errors, warnings, and deferred trait reports.",
|
|
14338
14404
|
references: []
|
|
14339
14405
|
},
|
|
14340
14406
|
{
|
|
@@ -16576,8 +16642,8 @@ function createProjectBaseUrl(origin) {
|
|
|
16576
16642
|
__name(createProjectBaseUrl, "createProjectBaseUrl");
|
|
16577
16643
|
function inferHeadlessServerUrl(projectUrlOrId) {
|
|
16578
16644
|
try {
|
|
16579
|
-
const
|
|
16580
|
-
const hostname =
|
|
16645
|
+
const projectUrl2 = new URL(projectUrlOrId);
|
|
16646
|
+
const hostname = projectUrl2.hostname.toLowerCase();
|
|
16581
16647
|
if (hostname === "development.framer.com") {
|
|
16582
16648
|
return DEV_HEADLESS_SERVER_URL;
|
|
16583
16649
|
}
|
|
@@ -16585,7 +16651,7 @@ function inferHeadlessServerUrl(projectUrlOrId) {
|
|
|
16585
16651
|
const headlessServerUrl = new URL(PROD_HEADLESS_SERVER_URL);
|
|
16586
16652
|
headlessServerUrl.searchParams.set(
|
|
16587
16653
|
"baseUrl",
|
|
16588
|
-
createProjectBaseUrl(
|
|
16654
|
+
createProjectBaseUrl(projectUrl2.origin)
|
|
16589
16655
|
);
|
|
16590
16656
|
return headlessServerUrl.toString();
|
|
16591
16657
|
}
|
|
@@ -16593,7 +16659,7 @@ function inferHeadlessServerUrl(projectUrlOrId) {
|
|
|
16593
16659
|
const headlessServerUrl = new URL(DEV_HEADLESS_SERVER_URL);
|
|
16594
16660
|
headlessServerUrl.searchParams.set(
|
|
16595
16661
|
"baseUrl",
|
|
16596
|
-
createProjectBaseUrl(
|
|
16662
|
+
createProjectBaseUrl(projectUrl2.origin)
|
|
16597
16663
|
);
|
|
16598
16664
|
return headlessServerUrl.toString();
|
|
16599
16665
|
}
|
|
@@ -16609,7 +16675,7 @@ function getRelayTokenPath() {
|
|
|
16609
16675
|
__name(getRelayTokenPath, "getRelayTokenPath");
|
|
16610
16676
|
function readRelayToken() {
|
|
16611
16677
|
try {
|
|
16612
|
-
const token =
|
|
16678
|
+
const token = fs8.readFileSync(getRelayTokenPath(), "utf-8").trim();
|
|
16613
16679
|
return token.length > 0 ? token : null;
|
|
16614
16680
|
} catch {
|
|
16615
16681
|
return null;
|
|
@@ -16783,17 +16849,17 @@ async function waitForClaudeCodeSkillDiscovery() {
|
|
|
16783
16849
|
__name(waitForClaudeCodeSkillDiscovery, "waitForClaudeCodeSkillDiscovery");
|
|
16784
16850
|
var FRAMER_TEMPORARY_DIR = path7.join(os2.tmpdir(), "framer");
|
|
16785
16851
|
function ensureTemporaryDir() {
|
|
16786
|
-
|
|
16852
|
+
fs8.mkdirSync(FRAMER_TEMPORARY_DIR, { recursive: true });
|
|
16787
16853
|
}
|
|
16788
16854
|
__name(ensureTemporaryDir, "ensureTemporaryDir");
|
|
16789
16855
|
function cleanupStaleSessionCodeFiles(activeSessionIds) {
|
|
16790
|
-
if (!
|
|
16856
|
+
if (!fs8.existsSync(FRAMER_TEMPORARY_DIR)) return;
|
|
16791
16857
|
const activeSessionIdsSet = new Set(activeSessionIds);
|
|
16792
|
-
for (const entry of
|
|
16858
|
+
for (const entry of fs8.readdirSync(FRAMER_TEMPORARY_DIR)) {
|
|
16793
16859
|
const [sessionId] = entry.split("-");
|
|
16794
16860
|
if (activeSessionIdsSet.has(sessionId)) continue;
|
|
16795
16861
|
const filePath = path7.join(FRAMER_TEMPORARY_DIR, entry);
|
|
16796
|
-
|
|
16862
|
+
fs8.rmSync(filePath, { recursive: true, force: true, maxRetries: 2 });
|
|
16797
16863
|
}
|
|
16798
16864
|
}
|
|
16799
16865
|
__name(cleanupStaleSessionCodeFiles, "cleanupStaleSessionCodeFiles");
|
|
@@ -16804,12 +16870,16 @@ var CODE_COMPONENTS_SKILL_NAME = "framer-code-components";
|
|
|
16804
16870
|
var __dirname2 = path7.dirname(fileURLToPath(import.meta.url));
|
|
16805
16871
|
var skillsDocsDir = path7.join(__dirname2, "..", "docs", "skills");
|
|
16806
16872
|
function readSkillDoc(name2) {
|
|
16807
|
-
return
|
|
16873
|
+
return fs8.readFileSync(path7.join(skillsDocsDir, name2), "utf-8").trimEnd();
|
|
16808
16874
|
}
|
|
16809
16875
|
__name(readSkillDoc, "readSkillDoc");
|
|
16810
16876
|
function buildMetaSkill() {
|
|
16877
|
+
const command = getFramerAgentCommand();
|
|
16811
16878
|
const template = readSkillDoc("framer.md");
|
|
16812
|
-
return `${renderTemplate(template, {
|
|
16879
|
+
return `${renderTemplate(template, {
|
|
16880
|
+
FRAMER_TEMPORARY_DIR,
|
|
16881
|
+
FRAMER_AGENT_COMMAND: command
|
|
16882
|
+
})}
|
|
16813
16883
|
`;
|
|
16814
16884
|
}
|
|
16815
16885
|
__name(buildMetaSkill, "buildMetaSkill");
|
|
@@ -16828,49 +16898,58 @@ function renderTemplate(template, values) {
|
|
|
16828
16898
|
__name(renderTemplate, "renderTemplate");
|
|
16829
16899
|
var PROJECT_SKILL_PREFIX = "framer-project-";
|
|
16830
16900
|
var LEGACY_CANVAS_EDITING_SKILL_PREFIX = "framer-canvas-editing-project-";
|
|
16901
|
+
var PACKAGE_NAME = "@framer/agent";
|
|
16902
|
+
var DEFAULT_FRAMER_AGENT_COMMAND = `npx ${PACKAGE_NAME}`;
|
|
16903
|
+
var RELEASE_AGE_OVERRIDE_FRAMER_AGENT_COMMAND = `npx --min-release-age=0 ${PACKAGE_NAME}`;
|
|
16904
|
+
function getFramerAgentCommand() {
|
|
16905
|
+
return isNpxMinReleaseAgeOverrideEnabled() ? RELEASE_AGE_OVERRIDE_FRAMER_AGENT_COMMAND : DEFAULT_FRAMER_AGENT_COMMAND;
|
|
16906
|
+
}
|
|
16907
|
+
__name(getFramerAgentCommand, "getFramerAgentCommand");
|
|
16831
16908
|
function toSafeProjectId(projectId) {
|
|
16832
16909
|
return projectId.replace(/[^a-zA-Z0-9_-]/g, "-");
|
|
16833
16910
|
}
|
|
16834
16911
|
__name(toSafeProjectId, "toSafeProjectId");
|
|
16835
16912
|
function buildProjectSkill(projectId, agentContext, agentPrompt) {
|
|
16913
|
+
const command = getFramerAgentCommand();
|
|
16836
16914
|
const skillName = `${PROJECT_SKILL_PREFIX}${toSafeProjectId(projectId)}`;
|
|
16837
16915
|
const template = readSkillDoc("framer-project.md");
|
|
16838
16916
|
const content = `${renderTemplate(template, {
|
|
16839
16917
|
SKILL_NAME: skillName,
|
|
16840
16918
|
PROJECT_ID: projectId,
|
|
16841
16919
|
GENERATED_AT: (/* @__PURE__ */ new Date()).toISOString(),
|
|
16842
|
-
AGENT_PROMPT: agentPrompt.trimEnd(),
|
|
16843
|
-
AGENT_CONTEXT: agentContext.trimEnd(),
|
|
16844
|
-
FRAMER_TEMPORARY_DIR
|
|
16920
|
+
AGENT_PROMPT: agentPrompt.trimEnd().replaceAll(DEFAULT_FRAMER_AGENT_COMMAND, command),
|
|
16921
|
+
AGENT_CONTEXT: agentContext.trimEnd().replaceAll(DEFAULT_FRAMER_AGENT_COMMAND, command),
|
|
16922
|
+
FRAMER_TEMPORARY_DIR,
|
|
16923
|
+
FRAMER_AGENT_COMMAND: command
|
|
16845
16924
|
})}
|
|
16846
16925
|
`;
|
|
16847
16926
|
return { skillName, content };
|
|
16848
16927
|
}
|
|
16849
16928
|
__name(buildProjectSkill, "buildProjectSkill");
|
|
16850
16929
|
function writeSkill(root, skillName, content) {
|
|
16851
|
-
|
|
16852
|
-
const rootStat =
|
|
16930
|
+
fs8.mkdirSync(root, { recursive: true });
|
|
16931
|
+
const rootStat = fs8.statSync(root);
|
|
16853
16932
|
if (!rootStat.isDirectory()) {
|
|
16854
16933
|
throw new Error(`Skill root is not a directory: ${root}`);
|
|
16855
16934
|
}
|
|
16856
16935
|
const skillDir = path7.join(root, skillName);
|
|
16857
16936
|
const filePath = path7.join(skillDir, "SKILL.md");
|
|
16858
|
-
const current =
|
|
16937
|
+
const current = fs8.lstatSync(skillDir, { throwIfNoEntry: false });
|
|
16859
16938
|
if (current && (current.isSymbolicLink() || !current.isDirectory())) {
|
|
16860
|
-
|
|
16939
|
+
fs8.rmSync(skillDir, { recursive: true, force: true });
|
|
16861
16940
|
}
|
|
16862
|
-
|
|
16863
|
-
|
|
16941
|
+
fs8.mkdirSync(skillDir, { recursive: true });
|
|
16942
|
+
fs8.writeFileSync(filePath, content, "utf-8");
|
|
16864
16943
|
return filePath;
|
|
16865
16944
|
}
|
|
16866
16945
|
__name(writeSkill, "writeSkill");
|
|
16867
16946
|
function cleanupLegacyCanvasEditingSkills(skillRoots = getDefaultSkillRoots()) {
|
|
16868
16947
|
for (const root of skillRoots) {
|
|
16869
|
-
if (!
|
|
16870
|
-
for (const entry of
|
|
16948
|
+
if (!fs8.existsSync(root)) continue;
|
|
16949
|
+
for (const entry of fs8.readdirSync(root)) {
|
|
16871
16950
|
if (!entry.startsWith(LEGACY_CANVAS_EDITING_SKILL_PREFIX)) continue;
|
|
16872
16951
|
const skillDir = path7.join(root, entry);
|
|
16873
|
-
|
|
16952
|
+
fs8.rmSync(skillDir, { recursive: true, force: true, maxRetries: 2 });
|
|
16874
16953
|
}
|
|
16875
16954
|
}
|
|
16876
16955
|
}
|
|
@@ -16888,14 +16967,14 @@ function cleanupStaleSkills(activeProjectIds, skillRoots = getDefaultSkillRoots(
|
|
|
16888
16967
|
[...activeProjectIds].map(toSafeProjectId)
|
|
16889
16968
|
);
|
|
16890
16969
|
for (const root of skillRoots) {
|
|
16891
|
-
if (!
|
|
16892
|
-
for (const entry of
|
|
16970
|
+
if (!fs8.existsSync(root)) continue;
|
|
16971
|
+
for (const entry of fs8.readdirSync(root)) {
|
|
16893
16972
|
if (!entry.startsWith(PROJECT_SKILL_PREFIX)) continue;
|
|
16894
16973
|
const sanitizedProjectId = entry.slice(PROJECT_SKILL_PREFIX.length);
|
|
16895
16974
|
const isActive = sanitizedActiveProjectIds.has(sanitizedProjectId);
|
|
16896
16975
|
if (isActive) continue;
|
|
16897
16976
|
const skillDir = path7.join(root, entry);
|
|
16898
|
-
|
|
16977
|
+
fs8.rmSync(skillDir, { recursive: true, force: true, maxRetries: 2 });
|
|
16899
16978
|
}
|
|
16900
16979
|
}
|
|
16901
16980
|
}
|
|
@@ -16981,6 +17060,19 @@ function printTelemetryNotice() {
|
|
|
16981
17060
|
markTelemetryNoticeShown();
|
|
16982
17061
|
}
|
|
16983
17062
|
__name(printTelemetryNotice, "printTelemetryNotice");
|
|
17063
|
+
async function installBaseSkills() {
|
|
17064
|
+
try {
|
|
17065
|
+
cleanupLegacyCanvasEditingSkills();
|
|
17066
|
+
} catch (error) {
|
|
17067
|
+
printWarning(
|
|
17068
|
+
`Failed to clean up legacy canvas editing skills: ${formatErrorForUser(error)}`
|
|
17069
|
+
);
|
|
17070
|
+
}
|
|
17071
|
+
const results = installSkills();
|
|
17072
|
+
await waitForClaudeCodeSkillDiscovery();
|
|
17073
|
+
return results;
|
|
17074
|
+
}
|
|
17075
|
+
__name(installBaseSkills, "installBaseSkills");
|
|
16984
17076
|
async function getAgentSystemPrompt(sessionId) {
|
|
16985
17077
|
debug("exec", "getAgentSystemPrompt: calling relay...");
|
|
16986
17078
|
const result = await client.exec.mutate({
|
|
@@ -17137,7 +17229,7 @@ program.command("exec").description("Execute code in a session").requiredOption(
|
|
|
17137
17229
|
let code = evalCode;
|
|
17138
17230
|
if (!code && filePath) {
|
|
17139
17231
|
try {
|
|
17140
|
-
code =
|
|
17232
|
+
code = fs8.readFileSync(filePath, "utf-8");
|
|
17141
17233
|
} catch (err) {
|
|
17142
17234
|
printError(`Failed to read file: ${formatErrorForUser(err)}`);
|
|
17143
17235
|
await waitForTrackingToFinish();
|
|
@@ -17401,15 +17493,7 @@ program.command("setup").description(
|
|
|
17401
17493
|
"Install Framer skills into ~/.agents/skills and ~/.claude/skills"
|
|
17402
17494
|
).action(async () => {
|
|
17403
17495
|
try {
|
|
17404
|
-
|
|
17405
|
-
cleanupLegacyCanvasEditingSkills();
|
|
17406
|
-
} catch (error) {
|
|
17407
|
-
printWarning(
|
|
17408
|
-
`Failed to clean up legacy canvas editing skills: ${formatErrorForUser(error)}`
|
|
17409
|
-
);
|
|
17410
|
-
}
|
|
17411
|
-
const results = installSkills();
|
|
17412
|
-
await waitForClaudeCodeSkillDiscovery();
|
|
17496
|
+
const results = await installBaseSkills();
|
|
17413
17497
|
printSetupSummary(results);
|
|
17414
17498
|
printTelemetryNotice();
|
|
17415
17499
|
} catch (err) {
|
|
@@ -17441,4 +17525,48 @@ telemetry.command("disable").description("Disable telemetry").action(() => {
|
|
|
17441
17525
|
telemetry.command("status").description("Show current telemetry status").action(() => {
|
|
17442
17526
|
print(`Telemetry is ${isTelemetryEnabled() ? "enabled" : "disabled"}`);
|
|
17443
17527
|
});
|
|
17528
|
+
var overrideMinReleaseAge = program.command("override-min-release-age").description("Configure generated skills to override npm min-release-age");
|
|
17529
|
+
overrideMinReleaseAge.command("enable").description("Override npm min-release-age for generated Framer npx commands").action(async () => {
|
|
17530
|
+
try {
|
|
17531
|
+
setNpxMinReleaseAgeOverrideEnabled(true);
|
|
17532
|
+
print(
|
|
17533
|
+
"Generated Framer skills will use `npx --min-release-age=0 @framer/agent`."
|
|
17534
|
+
);
|
|
17535
|
+
const results = await installBaseSkills();
|
|
17536
|
+
printSetupSummary(results);
|
|
17537
|
+
} catch (err) {
|
|
17538
|
+
printError(
|
|
17539
|
+
`Failed to enable min-release-age override: ${formatErrorForUser(err)}`
|
|
17540
|
+
);
|
|
17541
|
+
await waitForTrackingToFinish();
|
|
17542
|
+
process.exit(1);
|
|
17543
|
+
}
|
|
17544
|
+
});
|
|
17545
|
+
overrideMinReleaseAge.command("disable").description("Use the default npm min-release-age for generated npx commands").action(async () => {
|
|
17546
|
+
try {
|
|
17547
|
+
setNpxMinReleaseAgeOverrideEnabled(false);
|
|
17548
|
+
print(
|
|
17549
|
+
"Generated Framer skills will use the default npm min-release-age."
|
|
17550
|
+
);
|
|
17551
|
+
const results = await installBaseSkills();
|
|
17552
|
+
printSetupSummary(results);
|
|
17553
|
+
} catch (err) {
|
|
17554
|
+
printError(
|
|
17555
|
+
`Failed to disable min-release-age override: ${formatErrorForUser(err)}`
|
|
17556
|
+
);
|
|
17557
|
+
await waitForTrackingToFinish();
|
|
17558
|
+
process.exit(1);
|
|
17559
|
+
}
|
|
17560
|
+
});
|
|
17561
|
+
overrideMinReleaseAge.command("status").description("Show current npm min-release-age override status").action(() => {
|
|
17562
|
+
if (isNpxMinReleaseAgeOverrideEnabled()) {
|
|
17563
|
+
print(
|
|
17564
|
+
"npm min-release-age override is enabled for generated Framer skills."
|
|
17565
|
+
);
|
|
17566
|
+
return;
|
|
17567
|
+
}
|
|
17568
|
+
print(
|
|
17569
|
+
"npm min-release-age override is disabled for generated Framer skills."
|
|
17570
|
+
);
|
|
17571
|
+
});
|
|
17444
17572
|
program.parse();
|
|
@@ -13,8 +13,9 @@ import { connect, FramerAPIError } from 'framer-api';
|
|
|
13
13
|
import { z } from 'zod';
|
|
14
14
|
import { createRequire } from 'module';
|
|
15
15
|
import * as vm from 'vm';
|
|
16
|
+
import isWsl from 'is-wsl';
|
|
16
17
|
|
|
17
|
-
/* @framer/ai relay server v0.0.
|
|
18
|
+
/* @framer/ai relay server v0.0.34 */
|
|
18
19
|
var __defProp = Object.defineProperty;
|
|
19
20
|
var __knownSymbol = (name2, symbol) => (symbol = Symbol[name2]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name2);
|
|
20
21
|
var __typeError = (msg) => {
|
|
@@ -199,7 +200,7 @@ __name(debug, "debug");
|
|
|
199
200
|
// src/version.ts
|
|
200
201
|
var VERSION = (
|
|
201
202
|
// typeof is used to ensure this can be used just via tsx or node etc. without build
|
|
202
|
-
"0.0.
|
|
203
|
+
"0.0.34"
|
|
203
204
|
);
|
|
204
205
|
|
|
205
206
|
// src/relay-client.ts
|
|
@@ -234,17 +235,19 @@ function normalizeValue(value, maxLength) {
|
|
|
234
235
|
}
|
|
235
236
|
__name(normalizeValue, "normalizeValue");
|
|
236
237
|
function normalizeAgentThreadContext(input) {
|
|
238
|
+
const trimmedAgentThreadId = input.agentThreadId?.trim();
|
|
237
239
|
const agentThreadId = normalizeValue(
|
|
238
240
|
input.agentThreadId,
|
|
239
241
|
MAX_AGENT_THREAD_ID_LENGTH
|
|
240
242
|
);
|
|
241
|
-
if (!agentThreadId) return {};
|
|
243
|
+
if (trimmedAgentThreadId && !agentThreadId) return {};
|
|
242
244
|
const agentProvider = normalizeValue(
|
|
243
245
|
input.agentProvider,
|
|
244
246
|
MAX_AGENT_PROVIDER_LENGTH
|
|
245
247
|
);
|
|
248
|
+
if (!agentThreadId && !agentProvider) return {};
|
|
246
249
|
return {
|
|
247
|
-
agentThreadId,
|
|
250
|
+
...agentThreadId ? { agentThreadId } : {},
|
|
248
251
|
...agentProvider ? { agentProvider } : {}
|
|
249
252
|
};
|
|
250
253
|
}
|
|
@@ -842,11 +845,13 @@ var SettingsWatcher = class {
|
|
|
842
845
|
var DEFAULT_MACHINE_ID = "unknown-machine-id-this-should-never-be-persisted";
|
|
843
846
|
var DEFAULT_SETTINGS = {
|
|
844
847
|
machineId: DEFAULT_MACHINE_ID,
|
|
848
|
+
npxMinReleaseAgeOverrideEnabled: false,
|
|
845
849
|
telemetryEnabled: true,
|
|
846
850
|
telemetryNoticeShown: false
|
|
847
851
|
};
|
|
848
852
|
var SettingsFileSchema = z.object({
|
|
849
853
|
machineId: z.string().optional(),
|
|
854
|
+
npxMinReleaseAgeOverrideEnabled: z.boolean().optional(),
|
|
850
855
|
telemetryEnabled: z.boolean().optional(),
|
|
851
856
|
telemetryNoticeShown: z.boolean().optional()
|
|
852
857
|
});
|
|
@@ -874,6 +879,7 @@ function readSettingsFile() {
|
|
|
874
879
|
const result = SettingsFileSchema.parse(raw);
|
|
875
880
|
return {
|
|
876
881
|
machineId: result.machineId ?? DEFAULT_SETTINGS.machineId,
|
|
882
|
+
npxMinReleaseAgeOverrideEnabled: result.npxMinReleaseAgeOverrideEnabled ?? DEFAULT_SETTINGS.npxMinReleaseAgeOverrideEnabled,
|
|
877
883
|
telemetryEnabled: result.telemetryEnabled ?? DEFAULT_SETTINGS.telemetryEnabled,
|
|
878
884
|
telemetryNoticeShown: result.telemetryNoticeShown ?? DEFAULT_SETTINGS.telemetryNoticeShown
|
|
879
885
|
};
|
|
@@ -935,8 +941,27 @@ function getMacOsVersion() {
|
|
|
935
941
|
}
|
|
936
942
|
}
|
|
937
943
|
__name(getMacOsVersion, "getMacOsVersion");
|
|
944
|
+
function parseOsReleaseValue(content, key) {
|
|
945
|
+
const line = content.split("\n").find((candidate) => candidate.startsWith(`${key}=`));
|
|
946
|
+
const value = line?.slice(key.length + 1).trim();
|
|
947
|
+
if (!value) return void 0;
|
|
948
|
+
return value.replace(/^"|"$/g, "") || void 0;
|
|
949
|
+
}
|
|
950
|
+
__name(parseOsReleaseValue, "parseOsReleaseValue");
|
|
951
|
+
function getLinuxOsVersion() {
|
|
952
|
+
let version;
|
|
953
|
+
try {
|
|
954
|
+
const osRelease = fs4.readFileSync("/etc/os-release", "utf8");
|
|
955
|
+
version = parseOsReleaseValue(osRelease, "PRETTY_NAME") ?? "unknown";
|
|
956
|
+
} catch {
|
|
957
|
+
version = os5.release().trim() || "unknown";
|
|
958
|
+
}
|
|
959
|
+
return isWsl ? `${version} wsl` : version;
|
|
960
|
+
}
|
|
961
|
+
__name(getLinuxOsVersion, "getLinuxOsVersion");
|
|
938
962
|
function getOsVersion(platform) {
|
|
939
963
|
if (platform === "darwin") return getMacOsVersion();
|
|
964
|
+
if (platform === "linux") return getLinuxOsVersion();
|
|
940
965
|
const version = os5.release().trim();
|
|
941
966
|
return version || "unknown";
|
|
942
967
|
}
|
|
@@ -1106,22 +1131,20 @@ var SESSION_IDLE_TIMEOUT_MS = 60 * 1e3;
|
|
|
1106
1131
|
var SESSION_IDLE_CHECK_INTERVAL_MS = 30 * 1e3;
|
|
1107
1132
|
var MISSING_SERVER_SESSION_ID = "missing-session-id-should-not-happen";
|
|
1108
1133
|
var Connection = class _Connection {
|
|
1109
|
-
constructor(projectId, userId,
|
|
1134
|
+
constructor(projectId, userId, headlessServerUrl, framer) {
|
|
1110
1135
|
this.projectId = projectId;
|
|
1111
1136
|
this.userId = userId;
|
|
1112
|
-
this.apiKey = apiKey;
|
|
1113
1137
|
this.headlessServerUrl = headlessServerUrl;
|
|
1114
1138
|
this.framer = framer;
|
|
1115
1139
|
}
|
|
1116
1140
|
projectId;
|
|
1117
1141
|
userId;
|
|
1118
|
-
apiKey;
|
|
1119
1142
|
headlessServerUrl;
|
|
1120
1143
|
framer;
|
|
1121
1144
|
static {
|
|
1122
1145
|
__name(this, "Connection");
|
|
1123
1146
|
}
|
|
1124
|
-
|
|
1147
|
+
session;
|
|
1125
1148
|
status = "connected";
|
|
1126
1149
|
pendingReconnect;
|
|
1127
1150
|
activeBranchId;
|
|
@@ -1130,7 +1153,7 @@ var Connection = class _Connection {
|
|
|
1130
1153
|
clientId: `dalton/${VERSION}`,
|
|
1131
1154
|
serverUrl: headlessServerUrl
|
|
1132
1155
|
});
|
|
1133
|
-
return new _Connection(projectId, userId,
|
|
1156
|
+
return new _Connection(projectId, userId, headlessServerUrl, framer);
|
|
1134
1157
|
}
|
|
1135
1158
|
isConnected() {
|
|
1136
1159
|
return this.status === "connected";
|
|
@@ -1144,15 +1167,19 @@ var Connection = class _Connection {
|
|
|
1144
1167
|
getServerSessionId() {
|
|
1145
1168
|
return this.framer.sessionId ?? MISSING_SERVER_SESSION_ID;
|
|
1146
1169
|
}
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1170
|
+
getSessionInfo() {
|
|
1171
|
+
if (!this.session) return void 0;
|
|
1172
|
+
return {
|
|
1173
|
+
id: this.session.id,
|
|
1150
1174
|
projectId: this.projectId,
|
|
1151
|
-
stateKeys: Object.keys(session.state),
|
|
1175
|
+
stateKeys: Object.keys(this.session.state),
|
|
1152
1176
|
headlessServerUrl: this.headlessServerUrl
|
|
1153
|
-
}
|
|
1177
|
+
};
|
|
1154
1178
|
}
|
|
1155
1179
|
createSession(id, agentThreadContext) {
|
|
1180
|
+
if (this.session) {
|
|
1181
|
+
throw new Error("Connection already has a session");
|
|
1182
|
+
}
|
|
1156
1183
|
const session = {
|
|
1157
1184
|
id,
|
|
1158
1185
|
state: {},
|
|
@@ -1161,28 +1188,17 @@ var Connection = class _Connection {
|
|
|
1161
1188
|
inflight: 0,
|
|
1162
1189
|
connection: this
|
|
1163
1190
|
};
|
|
1164
|
-
this.
|
|
1191
|
+
this.session = session;
|
|
1165
1192
|
return session;
|
|
1166
1193
|
}
|
|
1167
1194
|
findSession(id) {
|
|
1168
|
-
return this.
|
|
1169
|
-
}
|
|
1170
|
-
removeSession(id) {
|
|
1171
|
-
const index = this.sessions.findIndex((session) => session.id === id);
|
|
1172
|
-
if (index >= 0) {
|
|
1173
|
-
this.sessions.splice(index, 1);
|
|
1174
|
-
}
|
|
1195
|
+
return this.session?.id === id ? this.session : void 0;
|
|
1175
1196
|
}
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
}
|
|
1179
|
-
clearSessions() {
|
|
1180
|
-
this.sessions.length = 0;
|
|
1197
|
+
clearSession() {
|
|
1198
|
+
this.session = void 0;
|
|
1181
1199
|
}
|
|
1182
1200
|
isIdle(now, timeoutMs) {
|
|
1183
|
-
return this.
|
|
1184
|
-
(session) => session.inflight === 0 && now - session.lastActivityAt >= timeoutMs
|
|
1185
|
-
);
|
|
1201
|
+
return this.session !== void 0 && this.session.inflight === 0 && now - this.session.lastActivityAt >= timeoutMs;
|
|
1186
1202
|
}
|
|
1187
1203
|
async reconnect(expectedInitialBranchId = this.activeBranchId) {
|
|
1188
1204
|
if (this.status === "connected") {
|
|
@@ -1228,7 +1244,7 @@ var SessionManager = class {
|
|
|
1228
1244
|
connections = [];
|
|
1229
1245
|
idleCheck = null;
|
|
1230
1246
|
async create(projectId, userId, apiKey, headlessServerUrl, agentThreadContext = {}) {
|
|
1231
|
-
const connection = await
|
|
1247
|
+
const connection = await Connection.open(
|
|
1232
1248
|
projectId,
|
|
1233
1249
|
userId,
|
|
1234
1250
|
apiKey,
|
|
@@ -1238,13 +1254,23 @@ var SessionManager = class {
|
|
|
1238
1254
|
this.getNextSessionId(),
|
|
1239
1255
|
agentThreadContext
|
|
1240
1256
|
);
|
|
1257
|
+
trackConnectionAcquire({
|
|
1258
|
+
projectId,
|
|
1259
|
+
userId: connection.userId,
|
|
1260
|
+
sessionId: connection.getServerSessionId(),
|
|
1261
|
+
isReuse: false
|
|
1262
|
+
});
|
|
1263
|
+
this.connections.push(connection);
|
|
1241
1264
|
this.startIdleCheck();
|
|
1242
1265
|
return session;
|
|
1243
1266
|
}
|
|
1244
1267
|
list() {
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1268
|
+
const sessions = [];
|
|
1269
|
+
for (const connection of this.connections) {
|
|
1270
|
+
const sessionInfo = connection.getSessionInfo();
|
|
1271
|
+
if (sessionInfo) sessions.push(sessionInfo);
|
|
1272
|
+
}
|
|
1273
|
+
return sessions;
|
|
1248
1274
|
}
|
|
1249
1275
|
get(id) {
|
|
1250
1276
|
return this.findSession(id);
|
|
@@ -1305,30 +1331,29 @@ var SessionManager = class {
|
|
|
1305
1331
|
if (!session) {
|
|
1306
1332
|
return;
|
|
1307
1333
|
}
|
|
1334
|
+
const connection = session.connection;
|
|
1335
|
+
await connection.disconnect();
|
|
1336
|
+
connection.clearSession();
|
|
1337
|
+
const index = this.connections.indexOf(connection);
|
|
1338
|
+
if (index >= 0) {
|
|
1339
|
+
this.connections.splice(index, 1);
|
|
1340
|
+
}
|
|
1341
|
+
if (this.connections.length === 0) {
|
|
1342
|
+
this.stopIdleCheck();
|
|
1343
|
+
}
|
|
1308
1344
|
trackSessionDestroy({
|
|
1309
1345
|
projectId: session.connection.projectId,
|
|
1310
1346
|
userId: session.connection.userId,
|
|
1311
1347
|
sessionId: session.connection.getServerSessionId(),
|
|
1312
1348
|
...session.agentThreadContext
|
|
1313
1349
|
});
|
|
1314
|
-
session.connection.removeSession(id);
|
|
1315
|
-
if (!session.connection.hasSessions()) {
|
|
1316
|
-
await session.connection.disconnect();
|
|
1317
|
-
const index = this.connections.indexOf(session.connection);
|
|
1318
|
-
if (index >= 0) {
|
|
1319
|
-
this.connections.splice(index, 1);
|
|
1320
|
-
}
|
|
1321
|
-
}
|
|
1322
|
-
if (this.connections.length === 0) {
|
|
1323
|
-
this.stopIdleCheck();
|
|
1324
|
-
}
|
|
1325
1350
|
}
|
|
1326
1351
|
async destroyAll() {
|
|
1327
1352
|
const connections = [...this.connections];
|
|
1328
1353
|
this.connections = [];
|
|
1329
1354
|
this.stopIdleCheck();
|
|
1330
1355
|
for (const connection of connections) {
|
|
1331
|
-
connection.
|
|
1356
|
+
connection.clearSession();
|
|
1332
1357
|
await connection.disconnect();
|
|
1333
1358
|
}
|
|
1334
1359
|
}
|
|
@@ -1385,42 +1410,6 @@ var SessionManager = class {
|
|
|
1385
1410
|
}
|
|
1386
1411
|
}
|
|
1387
1412
|
}
|
|
1388
|
-
findConnection(projectId, apiKey, headlessServerUrl) {
|
|
1389
|
-
return this.connections.find(
|
|
1390
|
-
(connection) => connection.projectId === projectId && connection.apiKey === apiKey && connection.headlessServerUrl === headlessServerUrl
|
|
1391
|
-
);
|
|
1392
|
-
}
|
|
1393
|
-
async findOrCreateConnection(projectId, userId, apiKey, headlessServerUrl) {
|
|
1394
|
-
const existingConnection = this.findConnection(
|
|
1395
|
-
projectId,
|
|
1396
|
-
apiKey,
|
|
1397
|
-
headlessServerUrl
|
|
1398
|
-
);
|
|
1399
|
-
if (existingConnection) {
|
|
1400
|
-
await existingConnection.reconnect();
|
|
1401
|
-
trackConnectionAcquire({
|
|
1402
|
-
projectId,
|
|
1403
|
-
userId: existingConnection.userId,
|
|
1404
|
-
sessionId: existingConnection.getServerSessionId(),
|
|
1405
|
-
isReuse: true
|
|
1406
|
-
});
|
|
1407
|
-
return existingConnection;
|
|
1408
|
-
}
|
|
1409
|
-
const connection = await Connection.open(
|
|
1410
|
-
projectId,
|
|
1411
|
-
userId,
|
|
1412
|
-
apiKey,
|
|
1413
|
-
headlessServerUrl
|
|
1414
|
-
);
|
|
1415
|
-
trackConnectionAcquire({
|
|
1416
|
-
projectId,
|
|
1417
|
-
userId: connection.userId,
|
|
1418
|
-
sessionId: connection.getServerSessionId(),
|
|
1419
|
-
isReuse: false
|
|
1420
|
-
});
|
|
1421
|
-
this.connections.push(connection);
|
|
1422
|
-
return connection;
|
|
1423
|
-
}
|
|
1424
1413
|
findSession(id) {
|
|
1425
1414
|
for (const connection of this.connections) {
|
|
1426
1415
|
const session = connection.findSession(id);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: {{SKILL_NAME}}
|
|
3
3
|
description: "Project-scoped Framer skill for project {{PROJECT_ID}}. Covers project reads, edits, change review, publishing, image sourcing, component operations, and canvas editing. 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(
|
|
4
|
+
allowed-tools: ["Bash({{FRAMER_AGENT_COMMAND}}:*)", "Bash({{FRAMER_AGENT_COMMAND}}@latest:*)", "Read({{FRAMER_TEMPORARY_DIR}}/*)", "Write({{FRAMER_TEMPORARY_DIR}}/*)"]
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
## Project Scope
|
|
@@ -15,11 +15,11 @@ Every connected-project task follows these steps:
|
|
|
15
15
|
|
|
16
16
|
### 1. Look up the API (before EVERY new use of an API method)
|
|
17
17
|
|
|
18
|
-
**You MUST run `
|
|
18
|
+
**You MUST run `{{FRAMER_AGENT_COMMAND}}@latest docs` before writing any code.** Do not guess method names or signatures.
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
{{FRAMER_AGENT_COMMAND}}@latest docs Collection # What methods exist?
|
|
22
|
+
{{FRAMER_AGENT_COMMAND}}@latest docs Collection.getItems # What are the parameters and return type?
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
### 2. Execute code
|
|
@@ -27,7 +27,7 @@ npx @framer/agent@latest docs Collection.getItems # What are the parameters and
|
|
|
27
27
|
Only after checking docs, use your write tool to write your code to a unique file under `{{FRAMER_TEMPORARY_DIR}}/`, then execute it with `-f`. Do not create code files with shell heredocs or `cat`. Name each file `<sessionId>-<short-summary>.js` where `<short-summary>` is a brief kebab-case description (e.g., `1-read-collections.js`, `1-add-team-member.js`).
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
|
-
|
|
30
|
+
{{FRAMER_AGENT_COMMAND}}@latest exec -s 1 -f {{FRAMER_TEMPORARY_DIR}}/1-read-collections.js
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
### 3. Store results in `state`
|
|
@@ -38,10 +38,10 @@ Always save results you'll need again. Don't repeat API calls.
|
|
|
38
38
|
|
|
39
39
|
Prefer the agent-specific methods `framer.agent.*` over regular plugin API methods (`framer.*`).
|
|
40
40
|
|
|
41
|
-
For `framer.agent.readProject` and `framer.agent.applyChanges` calls, use the `
|
|
41
|
+
For `framer.agent.readProject` and `framer.agent.applyChanges` calls, use the `{{FRAMER_AGENT_COMMAND}}@latest read-project` and `{{FRAMER_AGENT_COMMAND}}@latest apply-changes` subcommands when possible. It's still ok to call those methods in exec scripts if you require something more complex than a plain method call.
|
|
42
42
|
|
|
43
43
|
- Use `framer.agent.getNode`, `framer.agent.getNodes`, `framer.agent.getNodesOfTypes`, `framer.agent.getScopeNode`, `framer.agent.getGroundNode`, `framer.agent.getParentNode`, `framer.agent.getAncestors`, `framer.agent.serialize`, `framer.agent.serializeNodes`, and `framer.agent.paginate` for project tree reads.
|
|
44
|
-
- Do not use `
|
|
44
|
+
- Do not use `{{FRAMER_AGENT_COMMAND}}@latest read-project` or `framer.agent.readProject` for node tree reads unless you have just checked current docs and confirmed the exact query type. Query shapes such as `{ type: "node-by-id" }` are not valid for the current local API.
|
|
45
45
|
- Use `framer.agent.readComponentControls`, `framer.agent.readIconSetControls`, `framer.agent.readIcons`, `framer.agent.readLayoutTemplateControls`, and `framer.agent.readShaderControls` for reading the controls of components, icon sets, icons, layout templates, and shaders.
|
|
46
46
|
- Use `framer.agent.applyChanges` for page and layout edits. Do not use low-level node APIs like `createNode`, `setAttributes`, or `setRect` for design/layout work.
|
|
47
47
|
- Use `framer.agent.publish` for publishing. Do not use `publish`, `getDeployments`, or `deploy` for normal agent publishing flows.
|
|
@@ -87,7 +87,7 @@ state.collections = await framer.getCollections();
|
|
|
87
87
|
```
|
|
88
88
|
|
|
89
89
|
```bash
|
|
90
|
-
|
|
90
|
+
{{FRAMER_AGENT_COMMAND}}@latest exec -s 1 -f {{FRAMER_TEMPORARY_DIR}}/1-get-collections.js
|
|
91
91
|
```
|
|
92
92
|
|
|
93
93
|
```js
|
|
@@ -97,7 +97,7 @@ console.log(state.teamItems.length);
|
|
|
97
97
|
```
|
|
98
98
|
|
|
99
99
|
```bash
|
|
100
|
-
|
|
100
|
+
{{FRAMER_AGENT_COMMAND}}@latest exec -s 1 -f {{FRAMER_TEMPORARY_DIR}}/1-get-team-items.js
|
|
101
101
|
```
|
|
102
102
|
|
|
103
103
|
Store anything you'll reference again.
|
|
@@ -141,7 +141,7 @@ Prompting may take a while to complete, so set the command timeout to 10 minutes
|
|
|
141
141
|
Prefer using your write tool to write code to a unique file under `{{FRAMER_TEMPORARY_DIR}}/` and executing it with `-f`. Do not create code files with shell heredocs or `cat`:
|
|
142
142
|
|
|
143
143
|
```bash
|
|
144
|
-
|
|
144
|
+
{{FRAMER_AGENT_COMMAND}}@latest exec -s <sessionId> -f {{FRAMER_TEMPORARY_DIR}}/<sessionId>-<short-summary>.js
|
|
145
145
|
```
|
|
146
146
|
|
|
147
147
|
For short snippets, `exec` also accepts `-e <code>` or code piped on stdin.
|
|
@@ -154,24 +154,24 @@ In Windows PowerShell, if an argument contains nested quotes, use a single-quote
|
|
|
154
154
|
$value = @'
|
|
155
155
|
[{"key":"value","filter":["text","$rect"]}]
|
|
156
156
|
'@
|
|
157
|
-
|
|
157
|
+
{{FRAMER_AGENT_COMMAND}}@latest <command> --option $value
|
|
158
158
|
```
|
|
159
159
|
|
|
160
160
|
## API Documentation
|
|
161
161
|
|
|
162
162
|
```bash
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
163
|
+
{{FRAMER_AGENT_COMMAND}}@latest docs # List all available methods
|
|
164
|
+
{{FRAMER_AGENT_COMMAND}}@latest docs framer.getCollections # Show top level method
|
|
165
|
+
{{FRAMER_AGENT_COMMAND}}@latest docs Collection # Show class with all method signatures
|
|
166
|
+
{{FRAMER_AGENT_COMMAND}}@latest docs Collection.addItems # Show method + recursively expand all referenced types
|
|
167
|
+
{{FRAMER_AGENT_COMMAND}}@latest docs ScreenshotOptions # Show type + recursively expand all referenced types
|
|
168
168
|
```
|
|
169
169
|
|
|
170
170
|
`docs` with no arguments lists available methods. Looking up a class shows its full definition without expanding referenced types. Looking up a specific method or type automatically expands all referenced types recursively.
|
|
171
171
|
|
|
172
172
|
## API Examples
|
|
173
173
|
|
|
174
|
-
**STOP: These are patterns only. Before using any method below, run `
|
|
174
|
+
**STOP: These are patterns only. Before using any method below, run `{{FRAMER_AGENT_COMMAND}}@latest docs <ClassName>` to verify the current signature.**
|
|
175
175
|
|
|
176
176
|
### Working with Collections (CMS)
|
|
177
177
|
|
package/docs/skills/framer.md
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
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
|
-
**Mandatory precondition**: run `
|
|
6
|
-
allowed-tools: ["Bash(
|
|
5
|
+
**Mandatory precondition**: run `{{FRAMER_AGENT_COMMAND}}@latest setup` and let it complete **BEFORE** loading this skill.
|
|
6
|
+
allowed-tools: ["Bash({{FRAMER_AGENT_COMMAND}}:*)", "Bash({{FRAMER_AGENT_COMMAND}}@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:
|
|
10
10
|
```bash
|
|
11
|
-
|
|
11
|
+
{{FRAMER_AGENT_COMMAND}}@latest setup
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
## Capabilities
|
|
@@ -29,14 +29,14 @@ What you can do with the Framer CLI:
|
|
|
29
29
|
|
|
30
30
|
### Permissions
|
|
31
31
|
|
|
32
|
-
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 `
|
|
32
|
+
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 `{{FRAMER_AGENT_COMMAND}}@latest` commands with elevated permissions from the get-go. Do not try to execute them with the default sandboxed permissions; they will hang or fail.
|
|
33
33
|
|
|
34
34
|
### Session Management
|
|
35
35
|
|
|
36
36
|
If the user refers to a project by name or description rather than giving a URL, first inspect the recent projects:
|
|
37
37
|
|
|
38
38
|
```bash
|
|
39
|
-
|
|
39
|
+
{{FRAMER_AGENT_COMMAND}}@latest project list
|
|
40
40
|
```
|
|
41
41
|
|
|
42
42
|
Use that list to infer the likely project from the names and recency. If the right project is already known, use its project ID with `session new`. If there are multiple possible matches, clarify with the user. Only ask the user for a Project URL if there is no clear match. Avoid talking about the technical contents of returned data like IDs.
|
|
@@ -46,7 +46,7 @@ Each session maintains a persistent connection to a Framer project. Use sessions
|
|
|
46
46
|
Create a session against an existing project:
|
|
47
47
|
|
|
48
48
|
```bash
|
|
49
|
-
|
|
49
|
+
{{FRAMER_AGENT_COMMAND}}@latest session new "<url or id>"
|
|
50
50
|
```
|
|
51
51
|
|
|
52
52
|
This prints the session ID. You must always use that session ID with `-s <id>` for all subsequent commands. Using the same session preserves your `state` between calls.
|
|
@@ -54,21 +54,21 @@ This prints the session ID. You must always use that session ID with `-s <id>` f
|
|
|
54
54
|
To create a brand new empty project and connect to it:
|
|
55
55
|
|
|
56
56
|
```bash
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
{{FRAMER_AGENT_COMMAND}}@latest project new
|
|
58
|
+
{{FRAMER_AGENT_COMMAND}}@latest session new "<returned project id>"
|
|
59
59
|
```
|
|
60
60
|
|
|
61
61
|
To remix (duplicate) an existing project and connect to the copy:
|
|
62
62
|
|
|
63
63
|
```bash
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
{{FRAMER_AGENT_COMMAND}}@latest project remix "<url, project id, or remix link>"
|
|
65
|
+
{{FRAMER_AGENT_COMMAND}}@latest session new "<returned project id>"
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
List active sessions:
|
|
69
69
|
|
|
70
70
|
```bash
|
|
71
|
-
|
|
71
|
+
{{FRAMER_AGENT_COMMAND}}@latest session list
|
|
72
72
|
```
|
|
73
73
|
|
|
74
74
|
## Project-scoped skill
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@framer/agent",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.34",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": "./dist/cli.js",
|
|
6
6
|
"main": "./dist/cli.js",
|
|
@@ -24,15 +24,16 @@
|
|
|
24
24
|
"@trpc/client": "^11.17.0",
|
|
25
25
|
"@trpc/server": "^11.17.0",
|
|
26
26
|
"commander": "^12.1.0",
|
|
27
|
-
"framer-api": "^0.1.
|
|
27
|
+
"framer-api": "^0.1.17",
|
|
28
|
+
"is-wsl": "^3.1.1",
|
|
28
29
|
"zod": "^4.4.3"
|
|
29
30
|
},
|
|
30
31
|
"devDependencies": {
|
|
31
32
|
"@biomejs/biome": "^2.4.13",
|
|
32
|
-
"@framerjs/framer-events": "0.0.
|
|
33
|
+
"@framerjs/framer-events": "0.0.187",
|
|
33
34
|
"@types/node": "24.13.0",
|
|
34
35
|
"tsup": "^8.0.2",
|
|
35
|
-
"tsx": "^4.22.
|
|
36
|
+
"tsx": "^4.22.4",
|
|
36
37
|
"typescript": "^5.9.2",
|
|
37
38
|
"vitest": "^4.1.7"
|
|
38
39
|
},
|