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.
- package/lib/daemon-projects.js +3 -3
- package/lib/daemon.js +1 -1
- package/lib/project-connection.js +20 -5
- package/lib/project-debate.js +4 -0
- package/lib/project-mate-interaction.js +21 -1
- package/lib/project-memory.js +3 -0
- package/lib/project-shell-command.js +160 -0
- package/lib/project-user-message.js +10 -0
- package/lib/project.js +19 -4
- package/lib/public/antigravity-avatar.png +0 -0
- package/lib/public/app.js +28 -0
- package/lib/public/copilot-avatar.svg +5 -0
- package/lib/public/css/command-palette.css +22 -2
- package/lib/public/css/icon-strip.css +29 -13
- package/lib/public/css/input.css +75 -0
- package/lib/public/css/mates.css +6 -0
- package/lib/public/css/menus.css +48 -14
- package/lib/public/css/messages.css +9 -0
- package/lib/public/css/pane.css +4 -0
- package/lib/public/css/pwa-mobile.css +17 -0
- package/lib/public/css/title-bar.css +19 -0
- package/lib/public/grok-avatar.svg +4 -0
- package/lib/public/index.html +38 -7
- package/lib/public/junie-avatar.svg +11 -0
- package/lib/public/kimi-avatar.svg +4 -0
- package/lib/public/modules/app-header.js +4 -0
- package/lib/public/modules/app-messages.js +28 -5
- package/lib/public/modules/app-panels.js +28 -8
- package/lib/public/modules/app-projects.js +3 -1
- package/lib/public/modules/app-rendering.js +19 -4
- package/lib/public/modules/input.js +58 -28
- package/lib/public/modules/mate-sidebar.js +11 -3
- package/lib/public/modules/notifications.js +7 -1
- package/lib/public/modules/pane-bridge.js +11 -0
- package/lib/public/modules/pane-links.js +23 -0
- package/lib/public/modules/project-switcher.js +7 -6
- package/lib/public/modules/shell-command.js +148 -0
- package/lib/public/modules/sidebar-mates.js +1 -1
- package/lib/public/modules/sidebar-mobile.js +2 -1
- package/lib/public/modules/sidebar-projects.js +34 -43
- package/lib/public/modules/sidebar-sessions.js +6 -6
- package/lib/public/modules/split-pair-ui.js +3 -2
- package/lib/public/modules/tools.js +6 -1
- package/lib/public/modules/vendor-priority.js +14 -0
- package/lib/public/modules/vendor-selection.js +20 -0
- package/lib/public/modules/worktree-location.js +17 -0
- package/lib/public/qwen-avatar.svg +4 -0
- package/lib/public/style.css +3 -2
- package/lib/sdk-bridge.js +49 -25
- package/lib/sdk-message-processor.js +10 -1
- package/lib/session-notes-mcp-server.js +7 -1
- package/lib/worktree.js +9 -4
- package/lib/ws-schema.js +3 -0
- package/lib/yoke/acp-agent-profiles.js +64 -14
- package/lib/yoke/adapters/antigravity.js +417 -0
- package/lib/yoke/adapters/codex.js +98 -3
- package/lib/yoke/adapters/copilot.js +7 -0
- package/lib/yoke/adapters/grok.js +7 -0
- package/lib/yoke/adapters/junie.js +7 -0
- package/lib/yoke/adapters/kimi.js +7 -0
- package/lib/yoke/adapters/qwen.js +7 -0
- package/lib/yoke/codex-app-server.js +25 -8
- package/lib/yoke/index.js +58 -24
- package/lib/yoke/instructions.js +3 -0
- package/lib/yoke/vendor-registry.js +61 -6
- package/package.json +1 -1
- package/lib/public/gemini-avatar.svg +0 -11
- 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
|
-
|
|
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(
|
|
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
|
|
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", "
|
|
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 === "
|
|
77
|
-
adapter =
|
|
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:
|
|
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
|
-
|
|
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,
|
|
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
|
|
294
|
-
|
|
295
|
-
|
|
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,
|
|
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
|
|
357
|
-
for (var
|
|
358
|
-
var
|
|
359
|
-
var
|
|
360
|
-
if (installed[
|
|
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[
|
|
363
|
-
auth[
|
|
364
|
-
console.log("[yoke] Adapter created: " +
|
|
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 " +
|
|
395
|
+
console.error("[yoke] Failed to create adapter for " + subprocessVendor + ":", e.message);
|
|
367
396
|
}
|
|
368
|
-
} else if (installed[
|
|
369
|
-
console.log("[yoke] " +
|
|
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({
|
|
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
|
}
|
package/lib/yoke/instructions.js
CHANGED
|
@@ -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
|
-
|
|
44
|
-
displayName: "
|
|
45
|
-
loginCommand: "
|
|
46
|
-
binaryName: "
|
|
47
|
-
avatar: "/
|
|
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,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>
|