clay-server 2.5.0 → 2.6.0
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/bin/claude-relay.js +6 -0
- package/bin/cli.js +26 -8
- package/lib/cli-sessions.js +2 -7
- package/lib/config.js +78 -5
- package/lib/daemon.js +233 -2
- package/lib/notes.js +3 -2
- package/lib/project.js +465 -28
- package/lib/public/app.js +187 -24
- package/lib/public/css/base.css +61 -61
- package/lib/public/css/diff.css +3 -4
- package/lib/public/css/filebrowser.css +362 -2
- package/lib/public/css/icon-strip.css +317 -1
- package/lib/public/css/input.css +127 -50
- package/lib/public/css/messages.css +1 -1
- package/lib/public/css/mobile-nav.css +8 -4
- package/lib/public/css/overlays.css +9 -6
- package/lib/public/css/server-settings.css +67 -20
- package/lib/public/css/sidebar.css +10 -101
- package/lib/public/css/skills.css +730 -0
- package/lib/public/css/title-bar.css +82 -4
- package/lib/public/index.html +277 -70
- package/lib/public/modules/input.js +119 -56
- package/lib/public/modules/project-settings.js +906 -0
- package/lib/public/modules/server-settings.js +409 -53
- package/lib/public/modules/sidebar.js +720 -1
- package/lib/public/modules/skills.js +710 -0
- package/lib/public/modules/terminal.js +7 -0
- package/lib/public/modules/theme.js +88 -89
- package/lib/public/style.css +1 -0
- package/lib/sdk-bridge.js +18 -7
- package/lib/server.js +305 -1
- package/lib/sessions.js +9 -4
- package/lib/utils.js +18 -0
- package/package.json +3 -2
package/bin/cli.js
CHANGED
|
@@ -7,11 +7,8 @@ var { execSync, execFileSync, spawn } = require("child_process");
|
|
|
7
7
|
var qrcode = require("qrcode-terminal");
|
|
8
8
|
var net = require("net");
|
|
9
9
|
|
|
10
|
-
// Detect dev mode
|
|
10
|
+
// Detect dev mode (no separate storage — dev and prod share ~/.clay)
|
|
11
11
|
var _isDev = (process.argv[1] && path.basename(process.argv[1]) === "clay-dev") || process.argv.includes("--dev");
|
|
12
|
-
if (_isDev) {
|
|
13
|
-
process.env.CLAY_HOME = path.join(os.homedir(), ".clay-dev");
|
|
14
|
-
}
|
|
15
12
|
|
|
16
13
|
var { loadConfig, saveConfig, configPath, socketPath, logPath, ensureConfigDir, isDaemonAlive, isDaemonAliveAsync, generateSlug, clearStaleConfig, loadClayrc, saveClayrc, readCrashInfo } = require("../lib/config");
|
|
17
14
|
var { sendIPCCommand } = require("../lib/ipc");
|
|
@@ -1250,7 +1247,18 @@ async function forkDaemon(pin, keepAwake, extraProjects, addCwd) {
|
|
|
1250
1247
|
// Only include cwd if explicitly requested
|
|
1251
1248
|
if (addCwd) {
|
|
1252
1249
|
var slug = generateSlug(cwd, []);
|
|
1253
|
-
|
|
1250
|
+
var cwdEntry = { path: cwd, slug: slug, addedAt: Date.now() };
|
|
1251
|
+
// Restore title/icon from .clayrc if available
|
|
1252
|
+
var cwdRc = loadClayrc();
|
|
1253
|
+
var cwdRecent = cwdRc.recentProjects || [];
|
|
1254
|
+
for (var cr = 0; cr < cwdRecent.length; cr++) {
|
|
1255
|
+
if (cwdRecent[cr].path === cwd) {
|
|
1256
|
+
if (cwdRecent[cr].title) cwdEntry.title = cwdRecent[cr].title;
|
|
1257
|
+
if (cwdRecent[cr].icon) cwdEntry.icon = cwdRecent[cr].icon;
|
|
1258
|
+
break;
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
allProjects.push(cwdEntry);
|
|
1254
1262
|
usedSlugs.push(slug);
|
|
1255
1263
|
}
|
|
1256
1264
|
|
|
@@ -1262,7 +1270,7 @@ async function forkDaemon(pin, keepAwake, extraProjects, addCwd) {
|
|
|
1262
1270
|
if (!fs.existsSync(rp.path)) continue; // skip missing directories
|
|
1263
1271
|
var rpSlug = generateSlug(rp.path, usedSlugs);
|
|
1264
1272
|
usedSlugs.push(rpSlug);
|
|
1265
|
-
allProjects.push({ path: rp.path, slug: rpSlug, title: rp.title || undefined, addedAt: rp.addedAt || Date.now() });
|
|
1273
|
+
allProjects.push({ path: rp.path, slug: rpSlug, title: rp.title || undefined, icon: rp.icon || undefined, addedAt: rp.addedAt || Date.now() });
|
|
1266
1274
|
}
|
|
1267
1275
|
}
|
|
1268
1276
|
|
|
@@ -1350,19 +1358,29 @@ async function devMode(pin, keepAwake, existingPinHash) {
|
|
|
1350
1358
|
}
|
|
1351
1359
|
|
|
1352
1360
|
var slug = generateSlug(cwd, []);
|
|
1353
|
-
var
|
|
1361
|
+
var cwdDevEntry = { path: cwd, slug: slug, addedAt: Date.now() };
|
|
1354
1362
|
|
|
1355
1363
|
// Restore previous projects
|
|
1356
1364
|
var rc = loadClayrc();
|
|
1357
1365
|
var restorable = (rc.recentProjects || []).filter(function (p) {
|
|
1358
1366
|
return p.path !== cwd && fs.existsSync(p.path);
|
|
1359
1367
|
});
|
|
1368
|
+
// Restore title/icon for cwd from .clayrc
|
|
1369
|
+
var rcAll = rc.recentProjects || [];
|
|
1370
|
+
for (var ci = 0; ci < rcAll.length; ci++) {
|
|
1371
|
+
if (rcAll[ci].path === cwd) {
|
|
1372
|
+
if (rcAll[ci].title) cwdDevEntry.title = rcAll[ci].title;
|
|
1373
|
+
if (rcAll[ci].icon) cwdDevEntry.icon = rcAll[ci].icon;
|
|
1374
|
+
break;
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
var allProjects = [cwdDevEntry];
|
|
1360
1378
|
var usedSlugs = [slug];
|
|
1361
1379
|
for (var ri = 0; ri < restorable.length; ri++) {
|
|
1362
1380
|
var rp = restorable[ri];
|
|
1363
1381
|
var rpSlug = generateSlug(rp.path, usedSlugs);
|
|
1364
1382
|
usedSlugs.push(rpSlug);
|
|
1365
|
-
allProjects.push({ path: rp.path, slug: rpSlug, title: rp.title || undefined, addedAt: rp.addedAt || Date.now() });
|
|
1383
|
+
allProjects.push({ path: rp.path, slug: rpSlug, title: rp.title || undefined, icon: rp.icon || undefined, addedAt: rp.addedAt || Date.now() });
|
|
1366
1384
|
}
|
|
1367
1385
|
|
|
1368
1386
|
var config = {
|
package/lib/cli-sessions.js
CHANGED
|
@@ -2,14 +2,9 @@ var fs = require("fs");
|
|
|
2
2
|
var path = require("path");
|
|
3
3
|
var os = require("os");
|
|
4
4
|
var readline = require("readline");
|
|
5
|
+
var utils = require("./utils");
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
* Compute the encoded project directory name used by the Claude CLI.
|
|
8
|
-
* Replaces all "/" with "-", e.g. "/Users/foo/project" -> "-Users-foo-project"
|
|
9
|
-
*/
|
|
10
|
-
function encodeCwd(cwd) {
|
|
11
|
-
return cwd.replace(/\//g, "-");
|
|
12
|
-
}
|
|
7
|
+
var encodeCwd = utils.encodeCwd;
|
|
13
8
|
|
|
14
9
|
/**
|
|
15
10
|
* Parse the first ~20 lines of a CLI session JSONL file to extract metadata.
|
package/lib/config.js
CHANGED
|
@@ -18,6 +18,54 @@ if (!fs.existsSync(CLAY_HOME) && fs.existsSync(LEGACY_HOME)) {
|
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
// Auto-migrate dev sessions: merge ~/.clay-dev/sessions/ into ~/.clay/sessions/
|
|
22
|
+
var CLAY_DEV_HOME = path.join(os.homedir(), ".clay-dev");
|
|
23
|
+
var devSessionsRoot = path.join(CLAY_DEV_HOME, "sessions");
|
|
24
|
+
if (fs.existsSync(devSessionsRoot)) {
|
|
25
|
+
try {
|
|
26
|
+
var prodSessionsRoot = path.join(CLAY_HOME, "sessions");
|
|
27
|
+
fs.mkdirSync(prodSessionsRoot, { recursive: true });
|
|
28
|
+
var projectDirs = fs.readdirSync(devSessionsRoot);
|
|
29
|
+
var totalMigrated = 0;
|
|
30
|
+
for (var di = 0; di < projectDirs.length; di++) {
|
|
31
|
+
var srcDir = path.join(devSessionsRoot, projectDirs[di]);
|
|
32
|
+
if (!fs.statSync(srcDir).isDirectory()) continue;
|
|
33
|
+
var destDir = path.join(prodSessionsRoot, projectDirs[di]);
|
|
34
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
35
|
+
var files = fs.readdirSync(srcDir);
|
|
36
|
+
for (var fi = 0; fi < files.length; fi++) {
|
|
37
|
+
if (!files[fi].endsWith(".jsonl")) continue;
|
|
38
|
+
var srcFile = path.join(srcDir, files[fi]);
|
|
39
|
+
var destFile = path.join(destDir, files[fi]);
|
|
40
|
+
if (fs.existsSync(destFile)) continue;
|
|
41
|
+
try {
|
|
42
|
+
fs.renameSync(srcFile, destFile);
|
|
43
|
+
totalMigrated++;
|
|
44
|
+
} catch (e2) {
|
|
45
|
+
try {
|
|
46
|
+
fs.copyFileSync(srcFile, destFile);
|
|
47
|
+
fs.unlinkSync(srcFile);
|
|
48
|
+
totalMigrated++;
|
|
49
|
+
} catch (e3) {}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Clean up empty project dir
|
|
53
|
+
try {
|
|
54
|
+
if (fs.readdirSync(srcDir).length === 0) fs.rmdirSync(srcDir);
|
|
55
|
+
} catch (e4) {}
|
|
56
|
+
}
|
|
57
|
+
if (totalMigrated > 0) {
|
|
58
|
+
console.log("[config] Migrated " + totalMigrated + " dev session(s) from " + devSessionsRoot + " → " + prodSessionsRoot);
|
|
59
|
+
}
|
|
60
|
+
// Clean up empty dev sessions root
|
|
61
|
+
try {
|
|
62
|
+
if (fs.readdirSync(devSessionsRoot).length === 0) fs.rmdirSync(devSessionsRoot);
|
|
63
|
+
} catch (e5) {}
|
|
64
|
+
} catch (e) {
|
|
65
|
+
console.error("[config] Dev session migration failed:", e.message);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
21
69
|
var CONFIG_DIR = CLAY_HOME;
|
|
22
70
|
var CLAYRC_PATH = path.join(os.homedir(), ".clayrc");
|
|
23
71
|
var CRASH_INFO_PATH = path.join(CONFIG_DIR, "crash.json");
|
|
@@ -28,7 +76,7 @@ function configPath() {
|
|
|
28
76
|
|
|
29
77
|
function socketPath() {
|
|
30
78
|
if (process.platform === "win32") {
|
|
31
|
-
var pipeName =
|
|
79
|
+
var pipeName = "clay-daemon";
|
|
32
80
|
return "\\\\.\\pipe\\" + pipeName;
|
|
33
81
|
}
|
|
34
82
|
return path.join(CONFIG_DIR, "daemon.sock");
|
|
@@ -191,28 +239,52 @@ function syncClayrc(projects) {
|
|
|
191
239
|
byPath[p.path].slug = p.slug;
|
|
192
240
|
byPath[p.path].lastUsed = Date.now();
|
|
193
241
|
if (p.title) byPath[p.path].title = p.title;
|
|
194
|
-
else delete byPath[p.path].title;
|
|
242
|
+
else if ("title" in p) delete byPath[p.path].title;
|
|
243
|
+
if (p.icon) byPath[p.path].icon = p.icon;
|
|
244
|
+
else if ("icon" in p) delete byPath[p.path].icon;
|
|
195
245
|
} else {
|
|
196
246
|
// New entry
|
|
197
247
|
byPath[p.path] = {
|
|
198
248
|
path: p.path,
|
|
199
249
|
slug: p.slug,
|
|
200
250
|
title: p.title || undefined,
|
|
251
|
+
icon: p.icon || undefined,
|
|
201
252
|
addedAt: p.addedAt || Date.now(),
|
|
202
253
|
lastUsed: Date.now(),
|
|
203
254
|
};
|
|
204
255
|
}
|
|
205
256
|
}
|
|
206
257
|
|
|
207
|
-
//
|
|
208
|
-
|
|
209
|
-
|
|
258
|
+
// Active projects first, preserving config order (user's drag-and-drop order),
|
|
259
|
+
// then inactive recent projects sorted by lastUsed descending
|
|
260
|
+
var activePaths = {};
|
|
261
|
+
var ordered = [];
|
|
262
|
+
for (var k = 0; k < projects.length; k++) {
|
|
263
|
+
activePaths[projects[k].path] = true;
|
|
264
|
+
if (byPath[projects[k].path]) ordered.push(byPath[projects[k].path]);
|
|
265
|
+
}
|
|
266
|
+
var inactive = [];
|
|
267
|
+
var paths = Object.keys(byPath);
|
|
268
|
+
for (var m = 0; m < paths.length; m++) {
|
|
269
|
+
if (!activePaths[paths[m]]) inactive.push(byPath[paths[m]]);
|
|
270
|
+
}
|
|
271
|
+
inactive.sort(function (a, b) { return (b.lastUsed || 0) - (a.lastUsed || 0); });
|
|
272
|
+
var all = ordered.concat(inactive);
|
|
210
273
|
|
|
211
274
|
// Keep at most 20 recent projects
|
|
212
275
|
rc.recentProjects = all.slice(0, 20);
|
|
213
276
|
saveClayrc(rc);
|
|
214
277
|
}
|
|
215
278
|
|
|
279
|
+
function removeFromClayrc(projectPath) {
|
|
280
|
+
var rc = loadClayrc();
|
|
281
|
+
var before = (rc.recentProjects || []).length;
|
|
282
|
+
rc.recentProjects = (rc.recentProjects || []).filter(function (p) {
|
|
283
|
+
return p.path !== projectPath;
|
|
284
|
+
});
|
|
285
|
+
if (rc.recentProjects.length !== before) saveClayrc(rc);
|
|
286
|
+
}
|
|
287
|
+
|
|
216
288
|
module.exports = {
|
|
217
289
|
CONFIG_DIR: CONFIG_DIR,
|
|
218
290
|
configPath: configPath,
|
|
@@ -234,4 +306,5 @@ module.exports = {
|
|
|
234
306
|
loadClayrc: loadClayrc,
|
|
235
307
|
saveClayrc: saveClayrc,
|
|
236
308
|
syncClayrc: syncClayrc,
|
|
309
|
+
removeFromClayrc: removeFromClayrc,
|
|
237
310
|
};
|
package/lib/daemon.js
CHANGED
|
@@ -22,7 +22,7 @@ delete process.env.CLAUDECODE;
|
|
|
22
22
|
|
|
23
23
|
var fs = require("fs");
|
|
24
24
|
var path = require("path");
|
|
25
|
-
var { loadConfig, saveConfig, socketPath, generateSlug, syncClayrc, writeCrashInfo, readCrashInfo, clearCrashInfo } = require("./config");
|
|
25
|
+
var { loadConfig, saveConfig, socketPath, generateSlug, syncClayrc, removeFromClayrc, writeCrashInfo, readCrashInfo, clearCrashInfo } = require("./config");
|
|
26
26
|
var { createIPCServer } = require("./ipc");
|
|
27
27
|
var { createServer, generateAuthToken } = require("./server");
|
|
28
28
|
|
|
@@ -117,9 +117,16 @@ var relay = createServer({
|
|
|
117
117
|
if (config.projects[j].slug === slug) { found = true; break; }
|
|
118
118
|
}
|
|
119
119
|
if (!found) return { ok: false, error: "Project not found" };
|
|
120
|
+
// Find path before removing so we can clean up .clayrc
|
|
121
|
+
var removedPath = null;
|
|
122
|
+
for (var rj = 0; rj < config.projects.length; rj++) {
|
|
123
|
+
if (config.projects[rj].slug === slug) { removedPath = config.projects[rj].path; break; }
|
|
124
|
+
}
|
|
120
125
|
relay.removeProject(slug);
|
|
121
126
|
config.projects = config.projects.filter(function (p) { return p.slug !== slug; });
|
|
122
127
|
saveConfig(config);
|
|
128
|
+
// Remove from .clayrc so it doesn't appear in restore prompt
|
|
129
|
+
if (removedPath) { try { removeFromClayrc(removedPath); } catch (e) {} }
|
|
123
130
|
try { syncClayrc(config.projects); } catch (e) {}
|
|
124
131
|
console.log("[daemon] Removed project (web):", slug);
|
|
125
132
|
relay.broadcastAll({
|
|
@@ -129,6 +136,215 @@ var relay = createServer({
|
|
|
129
136
|
});
|
|
130
137
|
return { ok: true };
|
|
131
138
|
},
|
|
139
|
+
onReorderProjects: function (slugs) {
|
|
140
|
+
// Build a slug->project map from current projects
|
|
141
|
+
var projectMap = {};
|
|
142
|
+
for (var j = 0; j < config.projects.length; j++) {
|
|
143
|
+
projectMap[config.projects[j].slug] = config.projects[j];
|
|
144
|
+
}
|
|
145
|
+
// Reorder based on the slugs array
|
|
146
|
+
var reordered = [];
|
|
147
|
+
for (var k = 0; k < slugs.length; k++) {
|
|
148
|
+
if (projectMap[slugs[k]]) {
|
|
149
|
+
reordered.push(projectMap[slugs[k]]);
|
|
150
|
+
delete projectMap[slugs[k]];
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// Append any remaining projects not in slugs (safety)
|
|
154
|
+
var remaining = Object.keys(projectMap);
|
|
155
|
+
for (var m = 0; m < remaining.length; m++) {
|
|
156
|
+
reordered.push(projectMap[remaining[m]]);
|
|
157
|
+
}
|
|
158
|
+
config.projects = reordered;
|
|
159
|
+
saveConfig(config);
|
|
160
|
+
try { syncClayrc(config.projects); } catch (e) {}
|
|
161
|
+
// Also reorder the in-memory Map so getProjects() returns the new order
|
|
162
|
+
relay.reorderProjects(slugs);
|
|
163
|
+
relay.broadcastAll({
|
|
164
|
+
type: "projects_updated",
|
|
165
|
+
projects: relay.getProjects(),
|
|
166
|
+
projectCount: config.projects.length,
|
|
167
|
+
});
|
|
168
|
+
return { ok: true };
|
|
169
|
+
},
|
|
170
|
+
onSetProjectTitle: function (slug, newTitle) {
|
|
171
|
+
relay.setProjectTitle(slug, newTitle);
|
|
172
|
+
for (var ti = 0; ti < config.projects.length; ti++) {
|
|
173
|
+
if (config.projects[ti].slug === slug) {
|
|
174
|
+
if (newTitle) {
|
|
175
|
+
config.projects[ti].title = newTitle;
|
|
176
|
+
} else {
|
|
177
|
+
delete config.projects[ti].title;
|
|
178
|
+
}
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
saveConfig(config);
|
|
183
|
+
try { syncClayrc(config.projects); } catch (e) {}
|
|
184
|
+
relay.broadcastAll({
|
|
185
|
+
type: "projects_updated",
|
|
186
|
+
projects: relay.getProjects(),
|
|
187
|
+
projectCount: config.projects.length,
|
|
188
|
+
});
|
|
189
|
+
return { ok: true };
|
|
190
|
+
},
|
|
191
|
+
onSetProjectIcon: function (slug, newIcon) {
|
|
192
|
+
relay.setProjectIcon(slug, newIcon);
|
|
193
|
+
for (var ii = 0; ii < config.projects.length; ii++) {
|
|
194
|
+
if (config.projects[ii].slug === slug) {
|
|
195
|
+
if (newIcon) {
|
|
196
|
+
config.projects[ii].icon = newIcon;
|
|
197
|
+
} else {
|
|
198
|
+
delete config.projects[ii].icon;
|
|
199
|
+
}
|
|
200
|
+
break;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
saveConfig(config);
|
|
204
|
+
try { syncClayrc(config.projects); } catch (e) {}
|
|
205
|
+
relay.broadcastAll({
|
|
206
|
+
type: "projects_updated",
|
|
207
|
+
projects: relay.getProjects(),
|
|
208
|
+
projectCount: config.projects.length,
|
|
209
|
+
});
|
|
210
|
+
return { ok: true };
|
|
211
|
+
},
|
|
212
|
+
onGetProjectEnv: function (slug) {
|
|
213
|
+
for (var ei = 0; ei < config.projects.length; ei++) {
|
|
214
|
+
if (config.projects[ei].slug === slug) {
|
|
215
|
+
return { envrc: config.projects[ei].envrc || "" };
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return { envrc: "" };
|
|
219
|
+
},
|
|
220
|
+
onSetProjectEnv: function (slug, envrc) {
|
|
221
|
+
for (var ei = 0; ei < config.projects.length; ei++) {
|
|
222
|
+
if (config.projects[ei].slug === slug) {
|
|
223
|
+
if (envrc) {
|
|
224
|
+
config.projects[ei].envrc = envrc;
|
|
225
|
+
} else {
|
|
226
|
+
delete config.projects[ei].envrc;
|
|
227
|
+
}
|
|
228
|
+
saveConfig(config);
|
|
229
|
+
return { ok: true };
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return { ok: false, error: "Project not found" };
|
|
233
|
+
},
|
|
234
|
+
onGetSharedEnv: function () {
|
|
235
|
+
return { envrc: config.sharedEnv || "" };
|
|
236
|
+
},
|
|
237
|
+
onSetSharedEnv: function (envrc) {
|
|
238
|
+
if (envrc) {
|
|
239
|
+
config.sharedEnv = envrc;
|
|
240
|
+
} else {
|
|
241
|
+
delete config.sharedEnv;
|
|
242
|
+
}
|
|
243
|
+
saveConfig(config);
|
|
244
|
+
return { ok: true };
|
|
245
|
+
},
|
|
246
|
+
onGetServerDefaultEffort: function () {
|
|
247
|
+
return { effort: config.defaultEffort || null };
|
|
248
|
+
},
|
|
249
|
+
onSetServerDefaultEffort: function (effort) {
|
|
250
|
+
if (effort) {
|
|
251
|
+
config.defaultEffort = effort;
|
|
252
|
+
} else {
|
|
253
|
+
delete config.defaultEffort;
|
|
254
|
+
}
|
|
255
|
+
saveConfig(config);
|
|
256
|
+
return { ok: true };
|
|
257
|
+
},
|
|
258
|
+
onGetProjectDefaultEffort: function (slug) {
|
|
259
|
+
for (var i = 0; i < config.projects.length; i++) {
|
|
260
|
+
if (config.projects[i].slug === slug) {
|
|
261
|
+
return { effort: config.projects[i].defaultEffort || null };
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return { effort: null };
|
|
265
|
+
},
|
|
266
|
+
onSetProjectDefaultEffort: function (slug, effort) {
|
|
267
|
+
for (var i = 0; i < config.projects.length; i++) {
|
|
268
|
+
if (config.projects[i].slug === slug) {
|
|
269
|
+
if (effort) {
|
|
270
|
+
config.projects[i].defaultEffort = effort;
|
|
271
|
+
} else {
|
|
272
|
+
delete config.projects[i].defaultEffort;
|
|
273
|
+
}
|
|
274
|
+
saveConfig(config);
|
|
275
|
+
return { ok: true };
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return { ok: false, error: "Project not found" };
|
|
279
|
+
},
|
|
280
|
+
onGetServerDefaultModel: function () {
|
|
281
|
+
return { model: config.defaultModel || null };
|
|
282
|
+
},
|
|
283
|
+
onSetServerDefaultModel: function (model) {
|
|
284
|
+
if (model) {
|
|
285
|
+
config.defaultModel = model;
|
|
286
|
+
} else {
|
|
287
|
+
delete config.defaultModel;
|
|
288
|
+
}
|
|
289
|
+
saveConfig(config);
|
|
290
|
+
return { ok: true };
|
|
291
|
+
},
|
|
292
|
+
onGetProjectDefaultModel: function (slug) {
|
|
293
|
+
for (var i = 0; i < config.projects.length; i++) {
|
|
294
|
+
if (config.projects[i].slug === slug) {
|
|
295
|
+
return { model: config.projects[i].defaultModel || null };
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return { model: null };
|
|
299
|
+
},
|
|
300
|
+
onSetProjectDefaultModel: function (slug, model) {
|
|
301
|
+
for (var i = 0; i < config.projects.length; i++) {
|
|
302
|
+
if (config.projects[i].slug === slug) {
|
|
303
|
+
if (model) {
|
|
304
|
+
config.projects[i].defaultModel = model;
|
|
305
|
+
} else {
|
|
306
|
+
delete config.projects[i].defaultModel;
|
|
307
|
+
}
|
|
308
|
+
saveConfig(config);
|
|
309
|
+
return { ok: true };
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return { ok: false, error: "Project not found" };
|
|
313
|
+
},
|
|
314
|
+
onGetServerDefaultMode: function () {
|
|
315
|
+
return { mode: config.defaultMode || null };
|
|
316
|
+
},
|
|
317
|
+
onSetServerDefaultMode: function (mode) {
|
|
318
|
+
if (mode) {
|
|
319
|
+
config.defaultMode = mode;
|
|
320
|
+
} else {
|
|
321
|
+
delete config.defaultMode;
|
|
322
|
+
}
|
|
323
|
+
saveConfig(config);
|
|
324
|
+
return { ok: true };
|
|
325
|
+
},
|
|
326
|
+
onGetProjectDefaultMode: function (slug) {
|
|
327
|
+
for (var i = 0; i < config.projects.length; i++) {
|
|
328
|
+
if (config.projects[i].slug === slug) {
|
|
329
|
+
return { mode: config.projects[i].defaultMode || null };
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
return { mode: null };
|
|
333
|
+
},
|
|
334
|
+
onSetProjectDefaultMode: function (slug, mode) {
|
|
335
|
+
for (var i = 0; i < config.projects.length; i++) {
|
|
336
|
+
if (config.projects[i].slug === slug) {
|
|
337
|
+
if (mode) {
|
|
338
|
+
config.projects[i].defaultMode = mode;
|
|
339
|
+
} else {
|
|
340
|
+
delete config.projects[i].defaultMode;
|
|
341
|
+
}
|
|
342
|
+
saveConfig(config);
|
|
343
|
+
return { ok: true };
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return { ok: false, error: "Project not found" };
|
|
347
|
+
},
|
|
132
348
|
onGetDaemonConfig: function () {
|
|
133
349
|
return {
|
|
134
350
|
port: config.port,
|
|
@@ -179,7 +395,7 @@ for (var i = 0; i < projects.length; i++) {
|
|
|
179
395
|
var p = projects[i];
|
|
180
396
|
if (fs.existsSync(p.path)) {
|
|
181
397
|
console.log("[daemon] Adding project:", p.slug, "→", p.path);
|
|
182
|
-
relay.addProject(p.path, p.slug, p.title);
|
|
398
|
+
relay.addProject(p.path, p.slug, p.title, p.icon);
|
|
183
399
|
} else {
|
|
184
400
|
console.log("[daemon] Skipping missing project:", p.path);
|
|
185
401
|
}
|
|
@@ -207,6 +423,11 @@ var ipc = createIPCServer(socketPath(), function (msg) {
|
|
|
207
423
|
saveConfig(config);
|
|
208
424
|
try { syncClayrc(config.projects); } catch (e) {}
|
|
209
425
|
console.log("[daemon] Added project:", slug, "→", absPath);
|
|
426
|
+
relay.broadcastAll({
|
|
427
|
+
type: "projects_updated",
|
|
428
|
+
projects: relay.getProjects(),
|
|
429
|
+
projectCount: config.projects.length,
|
|
430
|
+
});
|
|
210
431
|
return { ok: true, slug: slug };
|
|
211
432
|
}
|
|
212
433
|
|
|
@@ -228,6 +449,11 @@ var ipc = createIPCServer(socketPath(), function (msg) {
|
|
|
228
449
|
saveConfig(config);
|
|
229
450
|
try { syncClayrc(config.projects); } catch (e) {}
|
|
230
451
|
console.log("[daemon] Removed project:", target);
|
|
452
|
+
relay.broadcastAll({
|
|
453
|
+
type: "projects_updated",
|
|
454
|
+
projects: relay.getProjects(),
|
|
455
|
+
projectCount: config.projects.length,
|
|
456
|
+
});
|
|
231
457
|
return { ok: true };
|
|
232
458
|
}
|
|
233
459
|
|
|
@@ -266,6 +492,11 @@ var ipc = createIPCServer(socketPath(), function (msg) {
|
|
|
266
492
|
saveConfig(config);
|
|
267
493
|
try { syncClayrc(config.projects); } catch (e) {}
|
|
268
494
|
console.log("[daemon] Project title:", msg.slug, "→", newTitle || "(default)");
|
|
495
|
+
relay.broadcastAll({
|
|
496
|
+
type: "projects_updated",
|
|
497
|
+
projects: relay.getProjects(),
|
|
498
|
+
projectCount: config.projects.length,
|
|
499
|
+
});
|
|
269
500
|
return { ok: true };
|
|
270
501
|
}
|
|
271
502
|
|
package/lib/notes.js
CHANGED
|
@@ -2,12 +2,13 @@ var fs = require("fs");
|
|
|
2
2
|
var path = require("path");
|
|
3
3
|
var crypto = require("crypto");
|
|
4
4
|
var config = require("./config");
|
|
5
|
+
var utils = require("./utils");
|
|
5
6
|
|
|
6
7
|
function createNotesManager(opts) {
|
|
7
8
|
var cwd = opts.cwd;
|
|
8
9
|
|
|
9
|
-
// Storage path: ~/.
|
|
10
|
-
var encodedCwd =
|
|
10
|
+
// Storage path: ~/.clay/notes/{encodedCwd}.json
|
|
11
|
+
var encodedCwd = utils.encodeCwd(cwd);
|
|
11
12
|
var notesDir = path.join(config.CONFIG_DIR, "notes");
|
|
12
13
|
var notesFile = path.join(notesDir, encodedCwd + ".json");
|
|
13
14
|
|