clay-server 3.4.0-beta.1 → 3.4.0-beta.10

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.
Files changed (68) hide show
  1. package/lib/daemon-projects.js +3 -3
  2. package/lib/daemon.js +1 -1
  3. package/lib/project-connection.js +20 -5
  4. package/lib/project-debate.js +4 -0
  5. package/lib/project-mate-interaction.js +21 -1
  6. package/lib/project-memory.js +3 -0
  7. package/lib/project-shell-command.js +160 -0
  8. package/lib/project-user-message.js +10 -0
  9. package/lib/project.js +19 -4
  10. package/lib/public/antigravity-avatar.png +0 -0
  11. package/lib/public/app.js +28 -0
  12. package/lib/public/copilot-avatar.svg +5 -0
  13. package/lib/public/css/command-palette.css +22 -2
  14. package/lib/public/css/icon-strip.css +29 -13
  15. package/lib/public/css/input.css +75 -0
  16. package/lib/public/css/mates.css +6 -0
  17. package/lib/public/css/menus.css +48 -14
  18. package/lib/public/css/messages.css +9 -0
  19. package/lib/public/css/pane.css +4 -0
  20. package/lib/public/css/pwa-mobile.css +17 -0
  21. package/lib/public/css/title-bar.css +19 -0
  22. package/lib/public/grok-avatar.svg +4 -0
  23. package/lib/public/index.html +38 -7
  24. package/lib/public/junie-avatar.svg +11 -0
  25. package/lib/public/kimi-avatar.svg +4 -0
  26. package/lib/public/modules/app-header.js +4 -0
  27. package/lib/public/modules/app-messages.js +28 -5
  28. package/lib/public/modules/app-panels.js +28 -8
  29. package/lib/public/modules/app-projects.js +3 -1
  30. package/lib/public/modules/app-rendering.js +19 -4
  31. package/lib/public/modules/input.js +58 -28
  32. package/lib/public/modules/mate-sidebar.js +11 -3
  33. package/lib/public/modules/notifications.js +7 -1
  34. package/lib/public/modules/pane-bridge.js +11 -0
  35. package/lib/public/modules/pane-links.js +23 -0
  36. package/lib/public/modules/project-switcher.js +7 -6
  37. package/lib/public/modules/shell-command.js +148 -0
  38. package/lib/public/modules/sidebar-mates.js +1 -1
  39. package/lib/public/modules/sidebar-mobile.js +2 -1
  40. package/lib/public/modules/sidebar-projects.js +34 -43
  41. package/lib/public/modules/sidebar-sessions.js +6 -6
  42. package/lib/public/modules/split-pair-ui.js +3 -2
  43. package/lib/public/modules/tools.js +6 -1
  44. package/lib/public/modules/vendor-priority.js +14 -0
  45. package/lib/public/modules/vendor-selection.js +20 -0
  46. package/lib/public/modules/worktree-location.js +17 -0
  47. package/lib/public/qwen-avatar.svg +4 -0
  48. package/lib/public/style.css +3 -2
  49. package/lib/sdk-bridge.js +49 -25
  50. package/lib/sdk-message-processor.js +10 -1
  51. package/lib/session-notes-mcp-server.js +7 -1
  52. package/lib/worktree.js +9 -4
  53. package/lib/ws-schema.js +3 -0
  54. package/lib/yoke/acp-agent-profiles.js +64 -14
  55. package/lib/yoke/adapters/antigravity.js +417 -0
  56. package/lib/yoke/adapters/codex.js +98 -3
  57. package/lib/yoke/adapters/copilot.js +7 -0
  58. package/lib/yoke/adapters/grok.js +7 -0
  59. package/lib/yoke/adapters/junie.js +7 -0
  60. package/lib/yoke/adapters/kimi.js +7 -0
  61. package/lib/yoke/adapters/qwen.js +7 -0
  62. package/lib/yoke/codex-app-server.js +25 -8
  63. package/lib/yoke/index.js +58 -24
  64. package/lib/yoke/instructions.js +3 -0
  65. package/lib/yoke/vendor-registry.js +61 -6
  66. package/package.json +1 -1
  67. package/lib/public/gemini-avatar.svg +0 -11
  68. package/lib/yoke/adapters/gemini.js +0 -7
@@ -9,6 +9,8 @@ var readline = require("readline");
9
9
  var path = require("path");
10
10
  var fs = require("fs");
11
11
  var { createRequire } = require("module");
12
+ var { buildUserEnv } = require("../build-user-env");
13
+ var { wrapSpawnAsUser } = require("../os-users");
12
14
 
13
15
  // --- Find the codex binary path ---
14
16
  // Mirrors the logic from @openai/codex-sdk findCodexPath()
@@ -120,14 +122,30 @@ function CodexAppServer(executablePath, opts) {
120
122
  this._stderrBuf = "";
121
123
  }
122
124
 
125
+ function buildSpawnSpec(executablePath, args, opts, wrapFn) {
126
+ opts = opts || {};
127
+ var osUserInfo = opts.osUserInfo || null;
128
+ var env = osUserInfo
129
+ ? Object.assign(buildUserEnv(osUserInfo), opts.env || {})
130
+ : Object.assign({}, process.env, opts.env || {});
131
+ var spawnOptions = {
132
+ stdio: ["pipe", "pipe", "pipe"],
133
+ env: env,
134
+ cwd: opts.cwd || process.cwd(),
135
+ };
136
+ if (osUserInfo) {
137
+ spawnOptions.uid = osUserInfo.uid;
138
+ spawnOptions.gid = osUserInfo.gid;
139
+ }
140
+ return (wrapFn || wrapSpawnAsUser)(executablePath, args, spawnOptions);
141
+ }
142
+
123
143
  CodexAppServer.prototype.start = function() {
124
144
  var self = this;
125
145
 
126
146
  return new Promise(function(resolve, reject) {
127
147
  try {
128
148
  var args = ["app-server"];
129
- var env = Object.assign({}, process.env, self.opts.env || {});
130
-
131
149
  // Pass config overrides via --config key=value flags
132
150
  if (self.opts.config) {
133
151
  var configArgs = serializeConfig(self.opts.config, "");
@@ -136,13 +154,11 @@ CodexAppServer.prototype.start = function() {
136
154
  }
137
155
  }
138
156
 
139
- console.log("[codex-app-server] Spawning:", self.executablePath, args.join(" "));
157
+ var spawnSpec = buildSpawnSpec(self.executablePath, args, self.opts);
158
+ var userSuffix = self.opts.osUserInfo ? " as " + self.opts.osUserInfo.user : "";
159
+ console.log("[codex-app-server] Spawning" + userSuffix + ":", spawnSpec.command, spawnSpec.args.join(" "));
140
160
 
141
- self.proc = spawn(self.executablePath, args, {
142
- stdio: ["pipe", "pipe", "pipe"],
143
- env: env,
144
- cwd: self.opts.cwd || process.cwd(),
145
- });
161
+ self.proc = spawn(spawnSpec.command, spawnSpec.args, spawnSpec.options);
146
162
 
147
163
  self.proc.on("error", function(err) {
148
164
  console.error("[codex-app-server] Process error:", err.message);
@@ -383,4 +399,5 @@ CodexAppServer.prototype.stop = function() {
383
399
  module.exports = {
384
400
  CodexAppServer: CodexAppServer,
385
401
  findCodexPath: findCodexPath,
402
+ buildSpawnSpec: buildSpawnSpec,
386
403
  };
package/lib/yoke/index.js CHANGED
@@ -6,13 +6,18 @@ var instructions = require("./instructions");
6
6
  var vendorRegistry = require("./vendor-registry");
7
7
  var createClaudeAdapter = require("./adapters/claude").createClaudeAdapter;
8
8
  var createCodexAdapter = require("./adapters/codex").createCodexAdapter;
9
- var createGeminiAdapter = require("./adapters/gemini").createGeminiAdapter;
9
+ var createAntigravityAdapter = require("./adapters/antigravity").createAntigravityAdapter;
10
10
  var createOpenCodeAdapter = require("./adapters/opencode").createOpenCodeAdapter;
11
+ var createKimiAdapter = require("./adapters/kimi").createKimiAdapter;
12
+ var createGrokAdapter = require("./adapters/grok").createGrokAdapter;
13
+ var createCopilotAdapter = require("./adapters/copilot").createCopilotAdapter;
14
+ var createQwenAdapter = require("./adapters/qwen").createQwenAdapter;
15
+ var createJunieAdapter = require("./adapters/junie").createJunieAdapter;
11
16
  var createKiroAdapter = require("./adapters/kiro").createKiroAdapter;
12
17
 
13
18
  // Keep the first session in a new project predictable. This order is also
14
19
  // used by the UI when a project has no remembered vendor yet.
15
- var DEFAULT_VENDOR_ORDER = ["claude", "codex", "gemini", "opencode", "kiro"];
20
+ var DEFAULT_VENDOR_ORDER = ["claude", "codex", "grok", "kimi", "copilot", "qwen", "junie", "antigravity", "opencode", "kiro"];
16
21
 
17
22
  function resolveDefaultVendor(availableVendors) {
18
23
  availableVendors = availableVendors || {};
@@ -73,10 +78,20 @@ function createAdapter(opts) {
73
78
  adapter = createClaudeAdapter(opts);
74
79
  } else if (vendor === "codex") {
75
80
  adapter = createCodexAdapter(opts);
76
- } else if (vendor === "gemini") {
77
- adapter = createGeminiAdapter(opts);
81
+ } else if (vendor === "antigravity") {
82
+ adapter = createAntigravityAdapter(opts);
78
83
  } else if (vendor === "opencode") {
79
84
  adapter = createOpenCodeAdapter(opts);
85
+ } else if (vendor === "kimi") {
86
+ adapter = createKimiAdapter(opts);
87
+ } else if (vendor === "grok") {
88
+ adapter = createGrokAdapter(opts);
89
+ } else if (vendor === "copilot") {
90
+ adapter = createCopilotAdapter(opts);
91
+ } else if (vendor === "qwen") {
92
+ adapter = createQwenAdapter(opts);
93
+ } else if (vendor === "junie") {
94
+ adapter = createJunieAdapter(opts);
80
95
  } else if (vendor === "kiro") {
81
96
  adapter = createKiroAdapter(opts);
82
97
  } else {
@@ -102,7 +117,7 @@ function logAuthCheck(auth) {
102
117
  if (_lastAuthLogKey === key && now - _lastAuthLogAt < 30000) return;
103
118
  _lastAuthLogKey = key;
104
119
  _lastAuthLogAt = now;
105
- console.log("[yoke] Auth check: claude=" + auth.claude + " codex=" + auth.codex + " gemini=" + auth.gemini + " opencode=" + auth.opencode + " kiro=" + auth.kiro);
120
+ console.log("[yoke] Auth check: " + Object.keys(auth).map(function(vendor) { return vendor + "=" + auth[vendor]; }).join(" "));
106
121
  }
107
122
 
108
123
  function checkAuth() {
@@ -236,8 +251,13 @@ function checkAuth() {
236
251
  _authCache = {
237
252
  claude: checkClaude(),
238
253
  codex: checkCodex(),
239
- gemini: !!lookupBinary("gemini"),
254
+ antigravity: !!lookupBinary("agy"),
240
255
  opencode: !!lookupBinary("opencode"),
256
+ kimi: !!lookupBinary("kimi"),
257
+ grok: !!lookupBinary("grok"),
258
+ copilot: !!lookupBinary("copilot"),
259
+ qwen: !!lookupBinary("qwen"),
260
+ junie: !!lookupBinary("junie"),
241
261
  kiro: checkKiro(),
242
262
  };
243
263
  logAuthCheck(_authCache);
@@ -261,7 +281,7 @@ function checkInstalled() {
261
281
 
262
282
  var fs = require("fs");
263
283
  var execFileSync = require("child_process").execFileSync;
264
- var result = { claude: false, codex: false, gemini: false, opencode: false, kiro: false };
284
+ var result = { claude: false, codex: false, antigravity: false, opencode: false, kimi: false, grok: false, copilot: false, qwen: false, junie: false, kiro: false };
265
285
  try {
266
286
  if (process.platform === "win32") execFileSync("where", ["claude"], { timeout: 3000, stdio: ["pipe", "pipe", "pipe"] });
267
287
  else execFileSync("which", ["claude"], { timeout: 3000, stdio: ["pipe", "pipe", "pipe"] });
@@ -290,10 +310,19 @@ function checkInstalled() {
290
310
  }
291
311
  } catch (e) {}
292
312
  try {
293
- var acpProfiles = require("./acp-agent-profiles");
294
- result.gemini = !!acpProfiles.findAcpAgentPath(acpProfiles.getAcpAgentProfile("gemini"));
295
- result.opencode = !!acpProfiles.findAcpAgentPath(acpProfiles.getAcpAgentProfile("opencode"));
313
+ var antigravityBinary = process.platform === "win32"
314
+ ? execFileSync("where", ["agy"], { timeout: 3000, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] })
315
+ : execFileSync("which", ["agy"], { timeout: 3000, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] });
316
+ result.antigravity = !!antigravityBinary.trim();
296
317
  } catch (e) {}
318
+ var acpVendorKeys = ["opencode", "kimi", "grok", "copilot", "qwen", "junie"];
319
+ for (var acpIndex = 0; acpIndex < acpVendorKeys.length; acpIndex++) {
320
+ try {
321
+ var acpProfiles = require("./acp-agent-profiles");
322
+ var acpVendor = acpVendorKeys[acpIndex];
323
+ result[acpVendor] = !!acpProfiles.findAcpAgentPath(acpProfiles.getAcpAgentProfile(acpVendor));
324
+ } catch (e) {}
325
+ }
297
326
  _installedCache = result;
298
327
  return result;
299
328
  }
@@ -318,7 +347,7 @@ function createAdapters(opts) {
318
347
  // that `claude auth status` does not always detect. Runtime auth failures are
319
348
  // handled downstream via query-level error detection.
320
349
  var installed = checkInstalled();
321
- var auth = { claude: false, codex: false, gemini: false, opencode: false, kiro: false };
350
+ var auth = { claude: false, codex: false, antigravity: false, opencode: false, kimi: false, grok: false, copilot: false, qwen: false, junie: false, kiro: false };
322
351
  var adapters = {};
323
352
 
324
353
  function supportsConfiguredIsolation(vendor) {
@@ -345,7 +374,7 @@ function createAdapters(opts) {
345
374
 
346
375
  if (installed.codex && supportsConfiguredIsolation("codex")) {
347
376
  try {
348
- adapters.codex = createAdapter({ vendor: "codex", cwd: opts.cwd, slug: opts.slug });
377
+ adapters.codex = createAdapter({ vendor: "codex", cwd: opts.cwd, slug: opts.slug, osUsers: !!opts.osUsers });
349
378
  auth.codex = true;
350
379
  console.log("[yoke] Adapter created: codex");
351
380
  } catch (e) {
@@ -353,20 +382,20 @@ function createAdapters(opts) {
353
382
  }
354
383
  }
355
384
 
356
- var acpVendors = ["gemini", "opencode"];
357
- for (var acpIndex = 0; acpIndex < acpVendors.length; acpIndex++) {
358
- var acpVendor = acpVendors[acpIndex];
359
- var acpInfo = vendorRegistry.getVendorInfo(acpVendor);
360
- if (installed[acpVendor] && supportsConfiguredIsolation(acpVendor)) {
385
+ var subprocessVendors = ["antigravity", "opencode", "kimi", "grok", "copilot", "qwen", "junie"];
386
+ for (var subprocessIndex = 0; subprocessIndex < subprocessVendors.length; subprocessIndex++) {
387
+ var subprocessVendor = subprocessVendors[subprocessIndex];
388
+ var subprocessInfo = vendorRegistry.getVendorInfo(subprocessVendor);
389
+ if (installed[subprocessVendor] && supportsConfiguredIsolation(subprocessVendor)) {
361
390
  try {
362
- adapters[acpVendor] = createAdapter({ vendor: acpVendor, cwd: opts.cwd, slug: opts.slug });
363
- auth[acpVendor] = true;
364
- console.log("[yoke] Adapter created: " + acpVendor);
391
+ adapters[subprocessVendor] = createAdapter({ vendor: subprocessVendor, cwd: opts.cwd, slug: opts.slug });
392
+ auth[subprocessVendor] = true;
393
+ console.log("[yoke] Adapter created: " + subprocessVendor);
365
394
  } catch (e) {
366
- console.error("[yoke] Failed to create adapter for " + acpVendor + ":", e.message);
395
+ console.error("[yoke] Failed to create adapter for " + subprocessVendor + ":", e.message);
367
396
  }
368
- } else if (installed[acpVendor] && opts.osUsers) {
369
- console.log("[yoke] " + acpInfo.displayName + " adapter disabled: OS-user isolation requires per-user spawning");
397
+ } else if (installed[subprocessVendor] && opts.osUsers) {
398
+ console.log("[yoke] " + subprocessInfo.displayName + " adapter disabled: OS-user isolation requires per-user spawning");
370
399
  }
371
400
  }
372
401
 
@@ -406,7 +435,12 @@ async function lazyCreateAdapter(adapters, vendor, opts) {
406
435
  if (!installed[vendor]) return null;
407
436
 
408
437
  try {
409
- var ad = createAdapter({ vendor: vendor, cwd: opts.cwd, slug: opts.slug });
438
+ var ad = createAdapter({
439
+ vendor: vendor,
440
+ cwd: opts.cwd,
441
+ slug: opts.slug,
442
+ osUsers: !!(opts.osUsers || opts.linuxUser),
443
+ });
410
444
  if (typeof ad.init === "function") {
411
445
  await ad.init(opts || {});
412
446
  }
@@ -18,12 +18,15 @@ var KNOWN_FILES = [
18
18
  { file: ".cursorrules", label: ".cursorrules" },
19
19
  { file: ".github/copilot-instructions.md", label: ".github/copilot-instructions.md" },
20
20
  { file: "COPILOT.md", label: "COPILOT.md" },
21
+ { file: "QWEN.md", label: "QWEN.md" },
21
22
  ];
22
23
 
23
24
  // Files each vendor reads natively (skip these to avoid duplication).
24
25
  var NATIVE_FILES = {
25
26
  claude: ["CLAUDE.md"],
26
27
  codex: ["AGENTS.md"],
28
+ copilot: [".github/copilot-instructions.md", "COPILOT.md"],
29
+ qwen: ["QWEN.md"],
27
30
  };
28
31
 
29
32
  // Scan projectDir for instruction files and return merged text.
@@ -40,13 +40,13 @@ var VENDOR_REGISTRY = {
40
40
  },
41
41
  rateLimitTracking: true,
42
42
  },
43
- gemini: {
44
- displayName: "Gemini CLI",
45
- loginCommand: "gemini",
46
- binaryName: "gemini",
47
- avatar: "/gemini-avatar.svg",
43
+ antigravity: {
44
+ displayName: "Antigravity CLI",
45
+ loginCommand: "agy",
46
+ binaryName: "agy",
47
+ avatar: "/antigravity-avatar.png",
48
48
  sessionModes: ["gui"],
49
- effortLevels: [],
49
+ effortLevels: ["low", "medium", "high"],
50
50
  osUserIsolation: false,
51
51
  usageDashboard: null,
52
52
  rateLimitTracking: false,
@@ -62,6 +62,61 @@ var VENDOR_REGISTRY = {
62
62
  usageDashboard: null,
63
63
  rateLimitTracking: false,
64
64
  },
65
+ kimi: {
66
+ displayName: "Kimi Code",
67
+ loginCommand: "kimi login",
68
+ binaryName: "kimi",
69
+ avatar: "/kimi-avatar.svg",
70
+ sessionModes: ["gui"],
71
+ effortLevels: [],
72
+ osUserIsolation: false,
73
+ usageDashboard: null,
74
+ rateLimitTracking: false,
75
+ },
76
+ grok: {
77
+ displayName: "Grok Build",
78
+ loginCommand: "grok login --device-auth",
79
+ binaryName: "grok",
80
+ avatar: "/grok-avatar.svg",
81
+ sessionModes: ["gui"],
82
+ effortLevels: [],
83
+ osUserIsolation: false,
84
+ usageDashboard: null,
85
+ rateLimitTracking: false,
86
+ },
87
+ copilot: {
88
+ displayName: "GitHub Copilot CLI",
89
+ loginCommand: "copilot login",
90
+ binaryName: "copilot",
91
+ avatar: "/copilot-avatar.svg",
92
+ sessionModes: ["gui"],
93
+ effortLevels: [],
94
+ osUserIsolation: false,
95
+ usageDashboard: null,
96
+ rateLimitTracking: false,
97
+ },
98
+ qwen: {
99
+ displayName: "Qwen Code",
100
+ loginCommand: "qwen",
101
+ binaryName: "qwen",
102
+ avatar: "/qwen-avatar.svg",
103
+ sessionModes: ["gui"],
104
+ effortLevels: [],
105
+ osUserIsolation: false,
106
+ usageDashboard: null,
107
+ rateLimitTracking: false,
108
+ },
109
+ junie: {
110
+ displayName: "Junie CLI",
111
+ loginCommand: "junie",
112
+ binaryName: "junie",
113
+ avatar: "/junie-avatar.svg",
114
+ sessionModes: ["gui"],
115
+ effortLevels: [],
116
+ osUserIsolation: false,
117
+ usageDashboard: null,
118
+ rateLimitTracking: false,
119
+ },
65
120
  kiro: {
66
121
  displayName: "Kiro CLI",
67
122
  loginCommand: "kiro-cli login",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clay-server",
3
- "version": "3.4.0-beta.1",
3
+ "version": "3.4.0-beta.10",
4
4
  "description": "Self-hosted team workspace for Claude Code and Codex. Multi-user, browser-based, with persistent AI mates.",
5
5
  "bin": {
6
6
  "clay-server": "./bin/cli.js",
@@ -1,11 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="Gemini CLI">
2
- <defs>
3
- <linearGradient id="g" x1="8" y1="56" x2="56" y2="8" gradientUnits="userSpaceOnUse">
4
- <stop stop-color="#4f7cff"/>
5
- <stop offset="0.5" stop-color="#9b5cff"/>
6
- <stop offset="1" stop-color="#ff65b3"/>
7
- </linearGradient>
8
- </defs>
9
- <rect width="64" height="64" rx="16" fill="#10131d"/>
10
- <path d="M32 8c2.3 14.1 9.9 21.7 24 24-14.1 2.3-21.7 9.9-24 24-2.3-14.1-9.9-21.7-24-24 14.1-2.3 21.7-9.9 24-24Z" fill="url(#g)"/>
11
- </svg>
@@ -1,7 +0,0 @@
1
- var createAcpAdapter = require("./acp").createAcpAdapter;
2
-
3
- function createGeminiAdapter(opts) {
4
- return createAcpAdapter("gemini", opts);
5
- }
6
-
7
- module.exports = { createGeminiAdapter: createGeminiAdapter };