@duckmind/dm-darwin-x64 0.36.0 → 0.36.4

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/dm CHANGED
Binary file
@@ -4,14 +4,21 @@ Browser-first Computer Use extension for DM.
4
4
 
5
5
  What it does:
6
6
 
7
- - launches or reuses a dedicated Chrome profile via the bundled `greedysearch-dm` CDP helpers
7
+ - launches or reuses a dedicated browser profile via the bundled `greedysearch-dm` CDP helpers
8
8
  - bootstraps a blank tab on fresh empty profiles so browser automation does not fail on the first run
9
9
  - exposes one tool, `browser_cua`, for list / navigate / snapshot / screenshot / click / type / evaluate / stop
10
10
  - stores browser screenshots under `~/.dm/agent/cua/screenshots/` by default
11
11
 
12
+ Browser selection:
13
+
14
+ - default: Chrome/Chromium auto-detected by `greedysearch-dm`
15
+ - opt-in CloakBrowser trial: set `DM_CUA_BROWSER=cloak` and one of `DM_CUA_BROWSER_PATH`, `DM_CUA_CLOAK_PATH`, `CLOAKBROWSER_BINARY_PATH`, or `CHROME_PATH` to the CloakBrowser Chromium executable
16
+ - DM does not bundle or auto-download the compiled CloakBrowser binary; install/pre-download it outside DM first because upstream permits use but not redistribution
17
+ - Current `browser_cua` actions still use CDP commands through `greedysearch-dm`; full CloakBrowser stealth parity would require a future Playwright-backed CUA lane.
18
+
12
19
  What it does **not** do yet:
13
20
 
14
21
  - it does not replace the macOS `computer-use` Codex plugin lane
15
22
  - it does not replace Android `adbridge`
16
23
 
17
- Those broader lanes are still tracked separately. This extension closes the most actionable CUA gap from the current DM snapshot: a first-class browser lane that works from a fresh dedicated Chrome profile instead of overloading `coding_task`.
24
+ Those broader lanes are still tracked separately. This extension closes the most actionable CUA gap from the current DM snapshot: a first-class browser lane that works from a fresh dedicated browser profile instead of overloading `coding_task`.
@@ -16,10 +16,12 @@ Usage:
16
16
  node browser-cua.mjs evaluate --expression <js> [--tab <prefix>] [--json]
17
17
  node browser-cua.mjs stop [--json]
18
18
 
19
- Notes:
20
- - This helper reuses DM's bundled greedysearch-dm CDP sidecar.
21
- - Fresh dedicated Chrome profiles auto-bootstrap a blank tab.
22
- - screenshot defaults to ~/.dm/agent/cua/screenshots when --output is omitted.`);
19
+ Notes:
20
+ - This helper reuses DM's bundled greedysearch-dm CDP sidecar.
21
+ - Fresh dedicated browser profiles auto-bootstrap a blank tab.
22
+ - Default browser is Chrome/Chromium. To try CloakBrowser, set DM_CUA_BROWSER=cloak
23
+ and point DM_CUA_BROWSER_PATH or CLOAKBROWSER_BINARY_PATH at its Chromium binary.
24
+ - screenshot defaults to ~/.dm/agent/cua/screenshots when --output is omitted.`);
23
25
  }
24
26
 
25
27
  function parseArgs(argv) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dm-cua",
3
3
  "version": "0.1.0",
4
- "description": "First-class browser CUA extension for DM using a dedicated Chrome CDP profile",
4
+ "description": "First-class browser CUA extension for DM using a dedicated Chrome/CDP-compatible profile",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "index.js",
@@ -24,6 +24,7 @@
24
24
  "browser",
25
25
  "cua",
26
26
  "chrome",
27
+ "cloakbrowser",
27
28
  "automation"
28
29
  ],
29
30
  "peerDependencies": {
@@ -15,4 +15,5 @@ Rules:
15
15
  - Start with an inspect action before click/type on unfamiliar pages.
16
16
  - Prefer `navigate` for deterministic URLs, including search URLs.
17
17
  - Use `screenshot` before complex click loops or when spatial layout matters.
18
- - Fresh Chrome profiles auto-bootstrap a blank tab, so the first browser action should not fail just because the page list is empty.
18
+ - Fresh dedicated browser profiles auto-bootstrap a blank tab, so the first browser action should not fail just because the page list is empty.
19
+ - Default browser is the bundled Chrome/Chromium-compatible CDP lane. To evaluate CloakBrowser, install/pre-download CloakBrowser outside DM, then run with `DM_CUA_BROWSER=cloak` and `DM_CUA_BROWSER_PATH` or `CLOAKBROWSER_BINARY_PATH` pointing at its Chromium executable. DM must not bundle the compiled CloakBrowser binary because upstream permits use but not redistribution.
@@ -12,6 +12,10 @@ const DEFAULT_CUA_PORT_BASE = 9322;
12
12
  const DEFAULT_CUA_PORT_RANGE = 400;
13
13
  const DEFAULT_TIMEOUT_MS = 60000;
14
14
  const URLISH_PATTERN = /^(about:|https?:\/\/|file:|chrome-error:\/\/)/i;
15
+ const BROWSER_DISPLAY_NAMES = {
16
+ chrome: "Chrome",
17
+ cloak: "CloakBrowser",
18
+ };
15
19
 
16
20
  function parsePort(value) {
17
21
  const port = Number.parseInt(String(value ?? ""), 10);
@@ -22,18 +26,40 @@ function hashScope(value) {
22
26
  return createHash("sha256").update(String(value || "default")).digest("hex");
23
27
  }
24
28
 
29
+ function normalizeBrowserKind(value) {
30
+ const raw = String(value || "chrome").trim().toLowerCase();
31
+ if (raw === "cloakbrowser" || raw === "cloak-browser") return "cloak";
32
+ if (raw === "chromium" || raw === "google-chrome" || raw === "chrome") return "chrome";
33
+ return raw.replace(/[^a-z0-9_.-]/g, "-") || "chrome";
34
+ }
35
+
36
+ function resolveBrowserExecutablePath(env, browserKind) {
37
+ const explicit =
38
+ env.DM_CUA_BROWSER_PATH
39
+ || (browserKind === "cloak" ? env.DM_CUA_CLOAK_PATH || env.CLOAKBROWSER_BINARY_PATH : undefined)
40
+ || env.CHROME_PATH;
41
+ return explicit ? resolve(String(explicit)) : undefined;
42
+ }
43
+
25
44
  export function resolveBrowserRuntimeConfig(env = process.env, scope = process.cwd()) {
26
45
  const rawScope = env.DM_CUA_SCOPE || env.DM_CODING_AGENT_DIR || env.PI_CODING_AGENT_DIR || scope;
27
46
  const scopeHash = hashScope(resolve(String(rawScope || ".")));
28
47
  const instanceId = String(env.DM_CUA_INSTANCE_ID || scopeHash.slice(0, 12)).replace(/[^a-zA-Z0-9_.-]/g, "-");
48
+ const browserKind = normalizeBrowserKind(env.DM_CUA_BROWSER || env.DM_BROWSER_CUA_BROWSER || env.DM_CUA_BROWSER_KIND);
49
+ const browserDisplayName = env.DM_CUA_BROWSER_DISPLAY_NAME || BROWSER_DISPLAY_NAMES[browserKind] || browserKind;
50
+ const browserExecutablePath = resolveBrowserExecutablePath(env, browserKind);
29
51
  const port =
30
52
  parsePort(env.DM_CUA_PORT)
31
53
  ?? parsePort(env.GREEDY_SEARCH_PORT)
32
54
  ?? DEFAULT_CUA_PORT_BASE + (Number.parseInt(scopeHash.slice(0, 8), 16) % DEFAULT_CUA_PORT_RANGE);
33
- const profileDir = resolve(env.DM_CUA_PROFILE_DIR || join(tmpdir(), `dm-cua-chrome-profile-${instanceId}`));
55
+ const profileSuffix = browserKind === "chrome" ? "chrome" : browserKind;
56
+ const profileDir = resolve(env.DM_CUA_PROFILE_DIR || join(tmpdir(), `dm-cua-${profileSuffix}-profile-${instanceId}`));
34
57
  const socketBaseDir = platform() === "win32" ? tmpdir() : "/tmp";
35
58
  return {
36
59
  instanceId,
60
+ browserKind,
61
+ browserDisplayName,
62
+ browserExecutablePath,
37
63
  port,
38
64
  profileDir,
39
65
  pidFile: join(profileDir, "chrome.pid"),
@@ -48,6 +74,31 @@ const CHROME_PORT = BROWSER_CONFIG.port;
48
74
 
49
75
  export const BROWSER_PROFILE_DIR = BROWSER_CONFIG.profileDir;
50
76
 
77
+ function browserSummary() {
78
+ return {
79
+ kind: BROWSER_CONFIG.browserKind,
80
+ displayName: BROWSER_CONFIG.browserDisplayName,
81
+ executablePath: BROWSER_CONFIG.browserExecutablePath,
82
+ profileDir: BROWSER_CONFIG.profileDir,
83
+ port: BROWSER_CONFIG.port,
84
+ };
85
+ }
86
+
87
+ function ensureBrowserSelectionUsable() {
88
+ if (BROWSER_CONFIG.browserKind === "cloak" && !BROWSER_CONFIG.browserExecutablePath) {
89
+ throw new Error(
90
+ [
91
+ "CloakBrowser selected for browser CUA, but no executable path was provided.",
92
+ "Install/pre-download CloakBrowser yourself, then set DM_CUA_BROWSER_PATH, DM_CUA_CLOAK_PATH, CLOAKBROWSER_BINARY_PATH, or CHROME_PATH.",
93
+ "DM does not bundle the compiled CloakBrowser binary because its upstream license allows use but not redistribution.",
94
+ ].join(" "),
95
+ );
96
+ }
97
+ if (BROWSER_CONFIG.browserExecutablePath && !existsSync(BROWSER_CONFIG.browserExecutablePath)) {
98
+ throw new Error(`${BROWSER_CONFIG.browserDisplayName} executable not found: ${BROWSER_CONFIG.browserExecutablePath}`);
99
+ }
100
+ }
101
+
51
102
  export function resolveBrowserHelperPaths() {
52
103
  return {
53
104
  launchScript: fileURLToPath(new URL("../../greedysearch-dm/bin/launch.mjs", import.meta.url)),
@@ -63,9 +114,11 @@ function browserEnv() {
63
114
  "1";
64
115
  return {
65
116
  ...process.env,
117
+ ...(BROWSER_CONFIG.browserExecutablePath ? { CHROME_PATH: BROWSER_CONFIG.browserExecutablePath } : {}),
66
118
  CDP_PROFILE_DIR: BROWSER_CONFIG.profileDir.replace(/\\/g, "/"),
67
119
  CDP_PAGES_CACHE: BROWSER_CONFIG.pagesCache.replace(/\\/g, "/"),
68
120
  CDP_SOCKET_DIR: BROWSER_CONFIG.socketDir.replace(/\\/g, "/"),
121
+ GREEDY_SEARCH_BROWSER_LABEL: BROWSER_CONFIG.browserDisplayName,
69
122
  GREEDY_SEARCH_PORT: String(BROWSER_CONFIG.port),
70
123
  GREEDY_SEARCH_PROFILE_DIR: BROWSER_CONFIG.profileDir.replace(/\\/g, "/"),
71
124
  GREEDY_SEARCH_PID_FILE: BROWSER_CONFIG.pidFile.replace(/\\/g, "/"),
@@ -171,7 +224,7 @@ function requestJson(method, requestPath) {
171
224
  });
172
225
  response.on("end", () => {
173
226
  if ((response.statusCode ?? 500) >= 400) {
174
- rejectPromise(new Error(`Chrome CDP HTTP ${response.statusCode}: ${body.trim()}`));
227
+ rejectPromise(new Error(`Browser CDP HTTP ${response.statusCode}: ${body.trim()}`));
175
228
  return;
176
229
  }
177
230
  try {
@@ -216,6 +269,7 @@ async function ensureBrowserHelpersExist() {
216
269
  }
217
270
 
218
271
  export async function ensureBrowserReady() {
272
+ ensureBrowserSelectionUsable();
219
273
  const { launchScript } = await ensureBrowserHelpersExist();
220
274
  await runNodeScript(launchScript, [], { env: browserEnv(), timeoutMs: DEFAULT_TIMEOUT_MS });
221
275
  let pages = await listPages();
@@ -252,7 +306,7 @@ export async function runBrowserAction({
252
306
  switch (action) {
253
307
  case "list": {
254
308
  const pages = await ensureBrowserReady();
255
- return { status: "ok", action, pages };
309
+ return { status: "ok", action, browser: browserSummary(), pages };
256
310
  }
257
311
  case "navigate": {
258
312
  if (!url) throw new Error("url is required for action=navigate");
@@ -263,6 +317,7 @@ export async function runBrowserAction({
263
317
  return {
264
318
  status: "ok",
265
319
  action,
320
+ browser: browserSummary(),
266
321
  tab: context.tab,
267
322
  url: page?.url || url,
268
323
  title: page?.title || "",
@@ -276,6 +331,7 @@ export async function runBrowserAction({
276
331
  return {
277
332
  status: "ok",
278
333
  action,
334
+ browser: browserSummary(),
279
335
  tab: context.tab,
280
336
  title: context.page?.title || "",
281
337
  url: context.page?.url || "",
@@ -291,6 +347,7 @@ export async function runBrowserAction({
291
347
  return {
292
348
  status: "ok",
293
349
  action,
350
+ browser: browserSummary(),
294
351
  tab: context.tab,
295
352
  title: context.page?.title || "",
296
353
  url: context.page?.url || "",
@@ -307,6 +364,7 @@ export async function runBrowserAction({
307
364
  return {
308
365
  status: "ok",
309
366
  action,
367
+ browser: browserSummary(),
310
368
  tab: context.tab,
311
369
  selector,
312
370
  message: result.stdout.trim(),
@@ -321,6 +379,7 @@ export async function runBrowserAction({
321
379
  return {
322
380
  status: "ok",
323
381
  action,
382
+ browser: browserSummary(),
324
383
  tab: context.tab,
325
384
  x,
326
385
  y,
@@ -334,6 +393,7 @@ export async function runBrowserAction({
334
393
  return {
335
394
  status: "ok",
336
395
  action,
396
+ browser: browserSummary(),
337
397
  tab: context.tab,
338
398
  textLength: text.length,
339
399
  message: result.stdout.trim(),
@@ -346,6 +406,7 @@ export async function runBrowserAction({
346
406
  return {
347
407
  status: "ok",
348
408
  action,
409
+ browser: browserSummary(),
349
410
  tab: context.tab,
350
411
  value: result.stdout.trim(),
351
412
  };
@@ -361,6 +422,7 @@ export async function runBrowserAction({
361
422
  return {
362
423
  status: "ok",
363
424
  action,
425
+ browser: browserSummary(),
364
426
  message: (result.stdout || "Stopped browser CUA lane.").trim(),
365
427
  };
366
428
  }
@@ -63,4 +63,20 @@ test("resolveBrowserRuntimeConfig accepts explicit safe overrides", () => {
63
63
  assert.equal(config.port, 9456);
64
64
  assert.equal(config.instanceId, "session-one");
65
65
  assert.equal(config.profileDir, "/tmp/custom-dm-cua-profile");
66
+ assert.equal(config.browserKind, "chrome");
67
+ assert.equal(config.browserDisplayName, "Chrome");
68
+ });
69
+
70
+ test("resolveBrowserRuntimeConfig supports opt-in CloakBrowser executable path", () => {
71
+ const config = resolveBrowserRuntimeConfig(
72
+ {
73
+ DM_CUA_BROWSER: "cloakbrowser",
74
+ CLOAKBROWSER_BINARY_PATH: "/opt/cloak/Chromium.app/Contents/MacOS/Chromium",
75
+ },
76
+ "/tmp/dm-cua-project-c",
77
+ );
78
+ assert.equal(config.browserKind, "cloak");
79
+ assert.equal(config.browserDisplayName, "CloakBrowser");
80
+ assert.equal(config.browserExecutablePath, "/opt/cloak/Chromium.app/Contents/MacOS/Chromium");
81
+ assert.match(config.profileDir, /dm-cua-cloak-profile-[a-f0-9]{12}$/);
66
82
  });
@@ -7448,9 +7448,9 @@
7448
7448
  }
7449
7449
  },
7450
7450
  "node_modules/semver": {
7451
- "version": "7.8.2",
7452
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.2.tgz",
7453
- "integrity": "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==",
7451
+ "version": "7.8.3",
7452
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.3.tgz",
7453
+ "integrity": "sha512-wnilbGyMxzbY7dNOl7jpKbLSjcfeweJWU5j4+u5qW+6/wuGD9KzIGOyZnQVSBM9E7DtWaaH3CyHkppYrKYoxwg==",
7454
7454
  "dev": true,
7455
7455
  "license": "ISC",
7456
7456
  "bin": {
@@ -42,6 +42,7 @@ const ACTIVE_PORT = join(PROFILE_DIR, "DevToolsActivePort");
42
42
  const PID_FILE = process.env.GREEDY_SEARCH_PID_FILE || join(tmpdir(), "greedysearch-chrome.pid");
43
43
  const MODE_FILE = process.env.GREEDY_SEARCH_MODE_FILE || join(tmpdir(), "greedysearch-chrome-mode");
44
44
  const ALLOW_PORT_CLEANUP = /^(1|true|yes|on)$/i.test(process.env.GREEDY_SEARCH_ALLOW_PORT_CLEANUP ?? "1");
45
+ const BROWSER_LABEL = process.env.GREEDY_SEARCH_BROWSER_LABEL || "Chrome";
45
46
 
46
47
  function findChrome() {
47
48
  const os = platform();
@@ -283,11 +284,11 @@ function cleanupGhostChrome() {
283
284
 
284
285
  if (!ALLOW_PORT_CLEANUP) {
285
286
  console.error(
286
- `Chrome/CDP port ${PORT} is already owned by untracked pid ${portPid}; refusing to kill it. Set GREEDY_SEARCH_PORT or DM_CUA_PORT to a free port.`,
287
+ `${BROWSER_LABEL}/CDP port ${PORT} is already owned by untracked pid ${portPid}; refusing to kill it. Set GREEDY_SEARCH_PORT or DM_CUA_PORT to a free port.`,
287
288
  );
288
289
  process.exit(1);
289
290
  }
290
- console.log(`Ghost Chrome on port ${PORT} (pid ${portPid}) — cleaning up...`);
291
+ console.log(`Ghost ${BROWSER_LABEL} on port ${PORT} (pid ${portPid}) — cleaning up...`);
291
292
  killProcess(portPid);
292
293
  try {
293
294
  unlinkSync(PID_FILE);
@@ -351,10 +352,10 @@ async function main() {
351
352
  if (pid) {
352
353
  const ok = killProcess(pid);
353
354
  console.log(
354
- ok ? `Stopped Chrome (pid ${pid}).` : `Failed to stop pid ${pid}.`,
355
+ ok ? `Stopped ${BROWSER_LABEL} (pid ${pid}).` : `Failed to stop pid ${pid}.`,
355
356
  );
356
357
  } else {
357
- console.log("GreedySearch Chrome is not running.");
358
+ console.log(`GreedySearch ${BROWSER_LABEL} is not running.`);
358
359
  }
359
360
  try {
360
361
  unlinkSync(PID_FILE);
@@ -386,7 +387,7 @@ async function main() {
386
387
  !process.argv.includes("--headless");
387
388
  if (isWantingVisible && isModeFileHeadless()) {
388
389
  console.log(
389
- `Headless Chrome running (pid ${existing}) but visible requested — killing...`,
390
+ `Headless ${BROWSER_LABEL} running (pid ${existing}) but visible requested — killing...`,
390
391
  );
391
392
  killProcess(existing);
392
393
  try {
@@ -399,7 +400,7 @@ async function main() {
399
400
  } else {
400
401
  const ready = await writePortFile(5000);
401
402
  if (ready) {
402
- console.log(`GreedySearch Chrome already running (pid ${existing}).`);
403
+ console.log(`GreedySearch ${BROWSER_LABEL} already running (pid ${existing}).`);
403
404
  return;
404
405
  }
405
406
  console.log(`Stale PID ${existing} — launching fresh.`);
@@ -411,13 +412,13 @@ async function main() {
411
412
 
412
413
  const CHROME_EXE = process.env.CHROME_PATH || findChrome();
413
414
  if (!CHROME_EXE) {
414
- console.error("Chrome not found. Set CHROME_PATH env var.");
415
+ console.error(`${BROWSER_LABEL} not found. Set CHROME_PATH env var.`);
415
416
  process.exit(1);
416
417
  }
417
418
 
418
419
  mkdirSync(PROFILE_DIR, { recursive: true });
419
420
 
420
- console.log(`Launching GreedySearch Chrome on port ${PORT}...`);
421
+ console.log(`Launching GreedySearch ${BROWSER_LABEL} on port ${PORT}...`);
421
422
  if (isHeadless()) {
422
423
  console.log("Headless mode — no window will be shown");
423
424
  } else if (!isVisible()) {
@@ -435,7 +436,7 @@ async function main() {
435
436
 
436
437
  const portFileReady = await writePortFile();
437
438
  if (!portFileReady) {
438
- console.error("Chrome did not become ready within 15s.");
439
+ console.error(`${BROWSER_LABEL} did not become ready within 15s.`);
439
440
  process.exit(1);
440
441
  }
441
442
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@duckmind/dm-darwin-x64",
3
- "version": "0.36.0",
3
+ "version": "0.36.4",
4
4
  "description": "DuckMind (dm) binary payload for darwin x64",
5
5
  "license": "MIT",
6
6
  "os": [
@@ -1,84 +0,0 @@
1
- {
2
- "$schema": "https://duckmind.ai/theme-schema.json",
3
- "name": "duckmind-harpy",
4
- "vars": {
5
- "background": "#121212",
6
- "surface": "#171B22",
7
- "surfaceAlt": "#1F2430",
8
- "surfaceSuccess": "#18211B",
9
- "surfaceError": "#21181D",
10
- "selection": "#2D3748",
11
- "foreground": "#F8F8F2",
12
- "foregroundSoft": "#B8BCC6",
13
- "comment": "#7F869E",
14
- "dimText": "#5C6378",
15
- "harpyPink": "#F04282",
16
- "harpyBlue": "#5FA0DE",
17
- "harpyTeal": "#4ED4E8",
18
- "green": "#A6E22E",
19
- "orange": "#FD971F",
20
- "amber": "#D19A66",
21
- "harpyViolet": "#9B8BF4",
22
- "red": "#E06C75",
23
- "alarm": "#FF0055",
24
- "criticalRed": "#FF0040"
25
- },
26
- "colors": {
27
- "accent": "harpyViolet",
28
- "border": "comment",
29
- "borderAccent": "harpyTeal",
30
- "borderMuted": "surfaceAlt",
31
- "success": "green",
32
- "error": "red",
33
- "warning": "orange",
34
- "muted": "comment",
35
- "dim": "dimText",
36
- "text": "#F8F8F2",
37
- "thinkingText": "comment",
38
- "selectedBg": "selection",
39
- "userMessageBg": "surfaceAlt",
40
- "userMessageText": "foreground",
41
- "customMessageBg": "surface",
42
- "customMessageText": "foreground",
43
- "customMessageLabel": "harpyPink",
44
- "toolPendingBg": "surface",
45
- "toolSuccessBg": "surfaceSuccess",
46
- "toolErrorBg": "surfaceError",
47
- "toolTitle": "harpyTeal",
48
- "toolOutput": "foregroundSoft",
49
- "mdHeading": "harpyViolet",
50
- "mdLink": "harpyBlue",
51
- "mdLinkUrl": "comment",
52
- "mdCode": "amber",
53
- "mdCodeBlock": "foregroundSoft",
54
- "mdCodeBlockBorder": "comment",
55
- "mdQuote": "foregroundSoft",
56
- "mdQuoteBorder": "comment",
57
- "mdHr": "dimText",
58
- "mdListBullet": "harpyTeal",
59
- "toolDiffAdded": "green",
60
- "toolDiffRemoved": "red",
61
- "toolDiffContext": "comment",
62
- "syntaxComment": "comment",
63
- "syntaxKeyword": "harpyPink",
64
- "syntaxFunction": "#5C9CE6",
65
- "syntaxVariable": "amber",
66
- "syntaxString": "green",
67
- "syntaxNumber": "orange",
68
- "syntaxType": "harpyTeal",
69
- "syntaxOperator": "harpyPink",
70
- "syntaxPunctuation": "foregroundSoft",
71
- "thinkingOff": "surface",
72
- "thinkingMinimal": "comment",
73
- "thinkingLow": "amber",
74
- "thinkingMedium": "orange",
75
- "thinkingHigh": "alarm",
76
- "thinkingXhigh": "criticalRed",
77
- "bashMode": "orange"
78
- },
79
- "export": {
80
- "pageBg": "#121212",
81
- "cardBg": "#171B22",
82
- "infoBg": "#4B3F63"
83
- }
84
- }