clay-server 2.20.0-beta.1 → 2.20.0-beta.2
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/lib/builtin-mates.js +360 -0
- package/lib/mates.js +108 -0
- package/lib/project.js +53 -44
- package/lib/public/app.js +91 -55
- package/lib/public/css/icon-strip.css +55 -2
- package/lib/public/css/input.css +50 -30
- package/lib/public/index.html +1 -0
- package/lib/public/mates/ally.png +0 -0
- package/lib/public/mates/sage.jpg +0 -0
- package/lib/public/mates/scout.png +0 -0
- package/lib/public/modules/input.js +147 -13
- package/lib/public/modules/sidebar.js +223 -15
- package/lib/sdk-bridge.js +8 -26
- package/lib/server.js +125 -7
- package/lib/users.js +63 -0
- package/package.json +1 -1
package/lib/server.js
CHANGED
|
@@ -326,9 +326,11 @@ function serveStatic(urlPath, res) {
|
|
|
326
326
|
var content = fs.readFileSync(filePath);
|
|
327
327
|
var ext = path.extname(filePath);
|
|
328
328
|
var mime = MIME_TYPES[ext] || "application/octet-stream";
|
|
329
|
+
var isImage = ext === ".png" || ext === ".jpg" || ext === ".jpeg" || ext === ".gif" || ext === ".svg" || ext === ".webp" || ext === ".ico";
|
|
330
|
+
var cacheControl = isImage ? "public, max-age=86400, immutable" : "no-cache";
|
|
329
331
|
res.writeHead(200, {
|
|
330
|
-
"Content-Type": mime + "; charset=utf-8",
|
|
331
|
-
"Cache-Control":
|
|
332
|
+
"Content-Type": mime + (isImage ? "" : "; charset=utf-8"),
|
|
333
|
+
"Cache-Control": cacheControl,
|
|
332
334
|
});
|
|
333
335
|
res.end(content);
|
|
334
336
|
return true;
|
|
@@ -2465,8 +2467,8 @@ function createServer(opts) {
|
|
|
2465
2467
|
return;
|
|
2466
2468
|
}
|
|
2467
2469
|
|
|
2468
|
-
// Static files
|
|
2469
|
-
if (fullUrl.
|
|
2470
|
+
// Static files (favicon, manifest, icons, sw.js, mate avatars, etc.)
|
|
2471
|
+
if (!fullUrl.includes("..") && !fullUrl.startsWith("/p/") && !fullUrl.startsWith("/api/")) {
|
|
2470
2472
|
if (serveStatic(fullUrl, res)) return;
|
|
2471
2473
|
}
|
|
2472
2474
|
|
|
@@ -2973,6 +2975,7 @@ function createServer(opts) {
|
|
|
2973
2975
|
avatarStyle: mp.avatarStyle || "bottts",
|
|
2974
2976
|
avatarSeed: mp.avatarSeed || mate.id,
|
|
2975
2977
|
avatarColor: mp.avatarColor || "#6c5ce7",
|
|
2978
|
+
avatarCustom: mp.avatarCustom || "",
|
|
2976
2979
|
isMate: true,
|
|
2977
2980
|
mateStatus: mate.status,
|
|
2978
2981
|
seedData: mate.seedData || {},
|
|
@@ -3101,6 +3104,8 @@ function createServer(opts) {
|
|
|
3101
3104
|
var mateSlug = "mate-" + mate.id;
|
|
3102
3105
|
var mateName = (mate.profile && mate.profile.displayName) || mate.name || "New Mate";
|
|
3103
3106
|
addProject(mateDir, mateSlug, mateName, null, mate.createdBy, null, { isMate: true, mateDisplayName: mateName });
|
|
3107
|
+
// Auto-add to favorites so it shows in sidebar
|
|
3108
|
+
users.addDmFavorite(userId, mate.id);
|
|
3104
3109
|
ws.send(JSON.stringify({ type: "mate_created", mate: mate, projectSlug: mateSlug }));
|
|
3105
3110
|
} catch (e) {
|
|
3106
3111
|
ws.send(JSON.stringify({ type: "mate_error", error: "Failed to create mate: " + e.message }));
|
|
@@ -3110,7 +3115,30 @@ function createServer(opts) {
|
|
|
3110
3115
|
|
|
3111
3116
|
if (msg.type === "mate_list") {
|
|
3112
3117
|
var mateCtx5 = mates.buildMateCtx(userId);
|
|
3118
|
+
// Backfill built-in mates for existing users
|
|
3119
|
+
try {
|
|
3120
|
+
var deletedKeys = users.getDeletedBuiltinKeys(userId);
|
|
3121
|
+
var newBuiltins = mates.ensureBuiltinMates(mateCtx5, deletedKeys);
|
|
3122
|
+
for (var bi = 0; bi < newBuiltins.length; bi++) {
|
|
3123
|
+
var nb = newBuiltins[bi];
|
|
3124
|
+
var nbSlug = "mate-" + nb.id;
|
|
3125
|
+
var nbDir = mates.getMateDir(mateCtx5, nb.id);
|
|
3126
|
+
var nbName = (nb.profile && nb.profile.displayName) || nb.name || "New Mate";
|
|
3127
|
+
addProject(nbDir, nbSlug, nbName, null, nb.createdBy || userId, null, { isMate: true, mateDisplayName: nbName });
|
|
3128
|
+
users.addDmFavorite(userId, nb.id);
|
|
3129
|
+
}
|
|
3130
|
+
} catch (e) {
|
|
3131
|
+
console.error("[server] Failed to ensure built-in mates:", e.message);
|
|
3132
|
+
}
|
|
3133
|
+
// Ensure built-in mates are in favorites (unless user explicitly removed them)
|
|
3113
3134
|
var mateList = mates.getAllMates(mateCtx5);
|
|
3135
|
+
var currentFavs = users.getDmFavorites(userId);
|
|
3136
|
+
var hiddenIds = users.getDmHidden(userId);
|
|
3137
|
+
for (var bfi = 0; bfi < mateList.length; bfi++) {
|
|
3138
|
+
if (mateList[bfi].builtinKey && currentFavs.indexOf(mateList[bfi].id) === -1 && hiddenIds.indexOf(mateList[bfi].id) === -1) {
|
|
3139
|
+
users.addDmFavorite(userId, mateList[bfi].id);
|
|
3140
|
+
}
|
|
3141
|
+
}
|
|
3114
3142
|
// Ensure all mate projects are registered (survives server restarts)
|
|
3115
3143
|
for (var mi = 0; mi < mateList.length; mi++) {
|
|
3116
3144
|
var m = mateList[mi];
|
|
@@ -3122,31 +3150,121 @@ function createServer(opts) {
|
|
|
3122
3150
|
addProject(mDir, mSlug, mName, null, m.createdBy || userId, null, { isMate: true, mateDisplayName: mName });
|
|
3123
3151
|
}
|
|
3124
3152
|
}
|
|
3125
|
-
|
|
3153
|
+
// Include deleted built-in mates for re-add UI
|
|
3154
|
+
var builtinDefs2 = require("./builtin-mates");
|
|
3155
|
+
var missingKeys2 = mates.getMissingBuiltinKeys(mateCtx5);
|
|
3156
|
+
var availableBuiltins2 = [];
|
|
3157
|
+
for (var abk2 = 0; abk2 < missingKeys2.length; abk2++) {
|
|
3158
|
+
var bDef2 = builtinDefs2.getBuiltinByKey(missingKeys2[abk2]);
|
|
3159
|
+
if (bDef2) {
|
|
3160
|
+
availableBuiltins2.push({
|
|
3161
|
+
key: bDef2.key,
|
|
3162
|
+
displayName: bDef2.displayName,
|
|
3163
|
+
bio: bDef2.bio,
|
|
3164
|
+
avatarCustom: bDef2.avatarCustom || "",
|
|
3165
|
+
});
|
|
3166
|
+
}
|
|
3167
|
+
}
|
|
3168
|
+
ws.send(JSON.stringify({ type: "mate_list", mates: mateList, availableBuiltins: availableBuiltins2 }));
|
|
3126
3169
|
return;
|
|
3127
3170
|
}
|
|
3128
3171
|
|
|
3129
3172
|
if (msg.type === "mate_delete") {
|
|
3130
3173
|
if (!msg.mateId) return;
|
|
3131
3174
|
var mateCtx6 = mates.buildMateCtx(userId);
|
|
3175
|
+
// Track deleted built-in mate key so it doesn't auto-recreate
|
|
3176
|
+
var mateToDelete = mates.getMate(mateCtx6, msg.mateId);
|
|
3177
|
+
if (mateToDelete && mateToDelete.builtinKey) {
|
|
3178
|
+
users.addDeletedBuiltinKey(userId, mateToDelete.builtinKey);
|
|
3179
|
+
}
|
|
3132
3180
|
var result = mates.deleteMate(mateCtx6, msg.mateId);
|
|
3133
3181
|
if (result.error) {
|
|
3134
3182
|
ws.send(JSON.stringify({ type: "mate_error", error: result.error }));
|
|
3135
3183
|
} else {
|
|
3136
3184
|
removeProject("mate-" + msg.mateId);
|
|
3137
|
-
|
|
3185
|
+
// Build updated available builtins list
|
|
3186
|
+
var builtinDefs3 = require("./builtin-mates");
|
|
3187
|
+
var missingKeys3 = mates.getMissingBuiltinKeys(mateCtx6);
|
|
3188
|
+
var availableBuiltins3 = [];
|
|
3189
|
+
for (var abk3 = 0; abk3 < missingKeys3.length; abk3++) {
|
|
3190
|
+
var bDef3 = builtinDefs3.getBuiltinByKey(missingKeys3[abk3]);
|
|
3191
|
+
if (bDef3) {
|
|
3192
|
+
availableBuiltins3.push({
|
|
3193
|
+
key: bDef3.key,
|
|
3194
|
+
displayName: bDef3.displayName,
|
|
3195
|
+
bio: bDef3.bio,
|
|
3196
|
+
avatarCustom: bDef3.avatarCustom || "",
|
|
3197
|
+
});
|
|
3198
|
+
}
|
|
3199
|
+
}
|
|
3200
|
+
ws.send(JSON.stringify({ type: "mate_deleted", mateId: msg.mateId, availableBuiltins: availableBuiltins3 }));
|
|
3138
3201
|
// Broadcast to all clients so strips update
|
|
3139
3202
|
projects.forEach(function (ctx) {
|
|
3140
3203
|
ctx.forEachClient(function (otherWs) {
|
|
3141
3204
|
if (otherWs === ws) return;
|
|
3142
3205
|
if (otherWs.readyState !== 1) return;
|
|
3143
|
-
otherWs.send(JSON.stringify({ type: "mate_deleted", mateId: msg.mateId }));
|
|
3206
|
+
otherWs.send(JSON.stringify({ type: "mate_deleted", mateId: msg.mateId, availableBuiltins: availableBuiltins3 }));
|
|
3144
3207
|
});
|
|
3145
3208
|
});
|
|
3146
3209
|
}
|
|
3147
3210
|
return;
|
|
3148
3211
|
}
|
|
3149
3212
|
|
|
3213
|
+
if (msg.type === "mate_readd_builtin") {
|
|
3214
|
+
if (!msg.builtinKey) return;
|
|
3215
|
+
try {
|
|
3216
|
+
var mateCtxR = mates.buildMateCtx(userId);
|
|
3217
|
+
var missingKeys = mates.getMissingBuiltinKeys(mateCtxR);
|
|
3218
|
+
if (missingKeys.indexOf(msg.builtinKey) === -1) {
|
|
3219
|
+
ws.send(JSON.stringify({ type: "mate_error", error: "This built-in mate already exists" }));
|
|
3220
|
+
return;
|
|
3221
|
+
}
|
|
3222
|
+
var newMate = mates.createBuiltinMate(mateCtxR, msg.builtinKey);
|
|
3223
|
+
users.removeDeletedBuiltinKey(userId, msg.builtinKey);
|
|
3224
|
+
var updatedFavsR = users.addDmFavorite(userId, newMate.id);
|
|
3225
|
+
var readdSlug = "mate-" + newMate.id;
|
|
3226
|
+
var readdDir = mates.getMateDir(mateCtxR, newMate.id);
|
|
3227
|
+
var readdName = (newMate.profile && newMate.profile.displayName) || newMate.name || "New Mate";
|
|
3228
|
+
addProject(readdDir, readdSlug, readdName, null, newMate.createdBy || userId, null, { isMate: true, mateDisplayName: readdName });
|
|
3229
|
+
// Build updated available builtins
|
|
3230
|
+
var builtinDefsR = require("./builtin-mates");
|
|
3231
|
+
var missingKeysR = mates.getMissingBuiltinKeys(mateCtxR);
|
|
3232
|
+
var availableBuiltinsR = [];
|
|
3233
|
+
for (var abkR = 0; abkR < missingKeysR.length; abkR++) {
|
|
3234
|
+
var bDefR = builtinDefsR.getBuiltinByKey(missingKeysR[abkR]);
|
|
3235
|
+
if (bDefR) {
|
|
3236
|
+
availableBuiltinsR.push({ key: bDefR.key, displayName: bDefR.displayName, bio: bDefR.bio, avatarCustom: bDefR.avatarCustom || "" });
|
|
3237
|
+
}
|
|
3238
|
+
}
|
|
3239
|
+
ws.send(JSON.stringify({ type: "mate_created", mate: newMate, projectSlug: readdSlug, availableBuiltins: availableBuiltinsR, dmFavorites: updatedFavsR }));
|
|
3240
|
+
} catch (e) {
|
|
3241
|
+
ws.send(JSON.stringify({ type: "mate_error", error: "Failed to re-add built-in mate: " + e.message }));
|
|
3242
|
+
}
|
|
3243
|
+
return;
|
|
3244
|
+
}
|
|
3245
|
+
|
|
3246
|
+
if (msg.type === "mate_list_available_builtins") {
|
|
3247
|
+
var mateCtxAB = mates.buildMateCtx(userId);
|
|
3248
|
+
var missingBuiltinKeys = mates.getMissingBuiltinKeys(mateCtxAB);
|
|
3249
|
+
var builtinDefs = require("./builtin-mates");
|
|
3250
|
+
var availableBuiltins = [];
|
|
3251
|
+
for (var abk = 0; abk < missingBuiltinKeys.length; abk++) {
|
|
3252
|
+
var bDef = builtinDefs.getBuiltinByKey(missingBuiltinKeys[abk]);
|
|
3253
|
+
if (bDef) {
|
|
3254
|
+
availableBuiltins.push({
|
|
3255
|
+
key: bDef.key,
|
|
3256
|
+
displayName: bDef.displayName,
|
|
3257
|
+
bio: bDef.bio,
|
|
3258
|
+
avatarColor: bDef.avatarColor,
|
|
3259
|
+
avatarStyle: bDef.avatarStyle,
|
|
3260
|
+
avatarCustom: bDef.avatarCustom || "",
|
|
3261
|
+
});
|
|
3262
|
+
}
|
|
3263
|
+
}
|
|
3264
|
+
ws.send(JSON.stringify({ type: "mate_available_builtins", builtins: availableBuiltins }));
|
|
3265
|
+
return;
|
|
3266
|
+
}
|
|
3267
|
+
|
|
3150
3268
|
if (msg.type === "mate_update") {
|
|
3151
3269
|
if (!msg.mateId || !msg.updates) return;
|
|
3152
3270
|
var mateCtx7 = mates.buildMateCtx(userId);
|
package/lib/users.js
CHANGED
|
@@ -176,6 +176,16 @@ function createUser(opts) {
|
|
|
176
176
|
};
|
|
177
177
|
data.users.push(user);
|
|
178
178
|
saveUsers(data);
|
|
179
|
+
|
|
180
|
+
// Seed built-in mates for the new user
|
|
181
|
+
try {
|
|
182
|
+
var mates = require("./mates");
|
|
183
|
+
var mateCtx = mates.buildMateCtx(user.id);
|
|
184
|
+
mates.ensureBuiltinMates(mateCtx);
|
|
185
|
+
} catch (e) {
|
|
186
|
+
console.error("[users] Failed to seed built-in mates for user " + user.id + ":", e.message);
|
|
187
|
+
}
|
|
188
|
+
|
|
179
189
|
return { ok: true, user: user };
|
|
180
190
|
}
|
|
181
191
|
|
|
@@ -422,6 +432,16 @@ function createUserWithoutPin(opts) {
|
|
|
422
432
|
};
|
|
423
433
|
data.users.push(user);
|
|
424
434
|
saveUsers(data);
|
|
435
|
+
|
|
436
|
+
// Seed built-in mates for the new user
|
|
437
|
+
try {
|
|
438
|
+
var mates = require("./mates");
|
|
439
|
+
var mateCtx = mates.buildMateCtx(user.id);
|
|
440
|
+
mates.ensureBuiltinMates(mateCtx);
|
|
441
|
+
} catch (e) {
|
|
442
|
+
console.error("[users] Failed to seed built-in mates for user " + user.id + ":", e.message);
|
|
443
|
+
}
|
|
444
|
+
|
|
425
445
|
return { ok: true, user: user };
|
|
426
446
|
}
|
|
427
447
|
|
|
@@ -563,6 +583,46 @@ function removeDmHidden(userId, targetUserId) {
|
|
|
563
583
|
return [];
|
|
564
584
|
}
|
|
565
585
|
|
|
586
|
+
// --- Deleted built-in mate keys tracking ---
|
|
587
|
+
|
|
588
|
+
function getDeletedBuiltinKeys(userId) {
|
|
589
|
+
var data = loadUsers();
|
|
590
|
+
for (var i = 0; i < data.users.length; i++) {
|
|
591
|
+
if (data.users[i].id === userId) {
|
|
592
|
+
return data.users[i].deletedBuiltinKeys || [];
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
return [];
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
function addDeletedBuiltinKey(userId, key) {
|
|
599
|
+
var data = loadUsers();
|
|
600
|
+
for (var i = 0; i < data.users.length; i++) {
|
|
601
|
+
if (data.users[i].id === userId) {
|
|
602
|
+
if (!data.users[i].deletedBuiltinKeys) data.users[i].deletedBuiltinKeys = [];
|
|
603
|
+
if (data.users[i].deletedBuiltinKeys.indexOf(key) === -1) {
|
|
604
|
+
data.users[i].deletedBuiltinKeys.push(key);
|
|
605
|
+
saveUsers(data);
|
|
606
|
+
}
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
function removeDeletedBuiltinKey(userId, key) {
|
|
613
|
+
var data = loadUsers();
|
|
614
|
+
for (var i = 0; i < data.users.length; i++) {
|
|
615
|
+
if (data.users[i].id === userId) {
|
|
616
|
+
if (!data.users[i].deletedBuiltinKeys) return;
|
|
617
|
+
data.users[i].deletedBuiltinKeys = data.users[i].deletedBuiltinKeys.filter(function (k) {
|
|
618
|
+
return k !== key;
|
|
619
|
+
});
|
|
620
|
+
saveUsers(data);
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
|
|
566
626
|
// --- RBAC permissions ---
|
|
567
627
|
|
|
568
628
|
function getEffectivePermissions(user, osUsersMode) {
|
|
@@ -717,4 +777,7 @@ module.exports = {
|
|
|
717
777
|
removeDmHidden: removeDmHidden,
|
|
718
778
|
getAutoContinue: getAutoContinue,
|
|
719
779
|
setAutoContinue: setAutoContinue,
|
|
780
|
+
getDeletedBuiltinKeys: getDeletedBuiltinKeys,
|
|
781
|
+
addDeletedBuiltinKey: addDeletedBuiltinKey,
|
|
782
|
+
removeDeletedBuiltinKey: removeDeletedBuiltinKey,
|
|
720
783
|
};
|