@duckmind/dm-darwin-x64 0.66.7 → 0.66.9

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/README.md CHANGED
@@ -4,7 +4,11 @@
4
4
  No UI overhead. Just plan, run, and get things done.
5
5
  </p>
6
6
 
7
- ## What's new in 0.66.7
7
+ Requires Node.js 22.19.0 or newer for bundled web access (including `undici`).
8
+ If your Node.js is older, upgrade it before installing or updating DM.
9
+ Install `@duckmind/dm`, not a platform package for a different OS.
8
10
 
9
- What's new: Web control, monitoring, and multi-agent DM. Use `/tty start` or `dm --host 0.0.0.0`.
11
+ ## What's new in 0.66.9
12
+
13
+ What’s new: Bug fixes and a new Web TTY (start with /tty start or dm --host 0.0.0.0).
10
14
 
package/dm CHANGED
@@ -1,4 +1,4 @@
1
1
  [diffend] Oversized file quarantined before diffing.
2
2
  name: package/dm
3
3
  size: 79153728 bytes
4
- sha256: 93c63daf645b2d53b6d8d3841b5c68a1ddb67e7c004e47dda9aa72e75b1df13d
4
+ sha256: 361a13b5badcfa6a4d0b58500cd8c85605f54057b2caa6fac3caedeee7644deb
@@ -840,8 +840,8 @@
840
840
  "zai.js"
841
841
  ],
842
842
  "file_count": 93,
843
- "before_bytes": 3520597,
844
- "after_bytes": 3344463,
843
+ "before_bytes": 3519916,
844
+ "after_bytes": 3343949,
845
845
  "runtime_specifier_rewrites": 72,
846
846
  "runtime_filename_rewrites": 90,
847
847
  "declaration_files_removed": 1
@@ -2307,8 +2307,8 @@
2307
2307
  "web/client.js"
2308
2308
  ],
2309
2309
  "file_count": 7,
2310
- "before_bytes": 86145,
2311
- "after_bytes": 83320,
2310
+ "before_bytes": 92857,
2311
+ "after_bytes": 90217,
2312
2312
  "runtime_specifier_rewrites": 3,
2313
2313
  "runtime_filename_rewrites": 6,
2314
2314
  "declaration_files_removed": 0
@@ -27,6 +27,11 @@ const historyText = document.querySelector("#history-text");
27
27
  const historyLines = document.querySelector("#history-lines");
28
28
  const historyCopy = document.querySelector("#history-copy");
29
29
  const copyStatus = document.querySelector("#copy-status");
30
+ const outputMenu = document.querySelector("#output-menu");
31
+ const outputPaste = document.querySelector("#output-paste");
32
+ const outputSelect = document.querySelector("#output-select");
33
+ const outputSelectAll = document.querySelector("#output-select-all");
34
+ const outputCopy = document.querySelector("#output-copy");
30
35
  const accessory = document.querySelector("#accessory");
31
36
  const ctrlButton = document.querySelector("#ctrl");
32
37
  const altButton = document.querySelector("#alt");
@@ -101,6 +106,9 @@ let streamHistory = "";
101
106
  let historyEscape = "text";
102
107
  let historyFrozen = false;
103
108
  let selectedForCopy = null;
109
+ let outputMenuTimer;
110
+ let outputMenuPointer;
111
+ let outputMenuOpen = false;
104
112
  let displayedHistoryLines = MAX_HISTORY_LINES;
105
113
  const TOKEN_STORAGE_KEY = "dm-tty-access-token";
106
114
  try {
@@ -160,7 +168,73 @@ function clearOutputSelection() {
160
168
  if (outputSelection())
161
169
  document.getSelection().removeAllRanges();
162
170
  }
171
+ function closeOutputMenu() {
172
+ clearTimeout(outputMenuTimer);
173
+ outputMenuTimer = undefined;
174
+ outputMenuPointer = undefined;
175
+ outputMenuOpen = false;
176
+ outputMenu.hidden = true;
177
+ }
178
+ function updateOutputMenu() {
179
+ const selected = Boolean(outputSelection());
180
+ outputCopy.hidden = !selected;
181
+ outputCopy.disabled = !selected;
182
+ }
183
+ function showOutputMenu(clientX, clientY) {
184
+ if (!openHistory())
185
+ return false;
186
+ const bounds = terminalContainer.getBoundingClientRect();
187
+ const width = outputMenu.offsetWidth || 148;
188
+ const height = outputMenu.offsetHeight || 170;
189
+ const left = Math.max(4, Math.min(clientX - bounds.left, bounds.width - width - 4));
190
+ const top = Math.max(4, Math.min(clientY - bounds.top, bounds.height - height - 4));
191
+ outputMenu.style.left = `${left}px`;
192
+ outputMenu.style.top = `${top}px`;
193
+ outputMenu.hidden = false;
194
+ outputMenuOpen = true;
195
+ updateOutputMenu();
196
+ return true;
197
+ }
198
+ function pointRange(clientX, clientY) {
199
+ const range = document.caretRangeFromPoint?.(clientX, clientY);
200
+ if (range?.startContainer && historyText.contains(range.startContainer))
201
+ return range;
202
+ const position = document.caretPositionFromPoint?.(clientX, clientY);
203
+ if (!position?.offsetNode || !historyText.contains(position.offsetNode))
204
+ return null;
205
+ const fallback = document.createRange();
206
+ fallback.setStart(position.offsetNode, position.offset);
207
+ fallback.setEnd(position.offsetNode, position.offset);
208
+ return fallback;
209
+ }
210
+ function selectOutputAtPoint(clientX, clientY) {
211
+ if (!openHistory() || !historyText.textContent)
212
+ return false;
213
+ const point = pointRange(clientX, clientY);
214
+ if (!point || point.startContainer.nodeType !== 3)
215
+ return selectAllOutput();
216
+ const text = point.startContainer.textContent ?? "";
217
+ let start = Math.max(0, Math.min(point.startOffset, text.length));
218
+ let end = start;
219
+ while (start > 0 && !/\s/u.test(text[start - 1]))
220
+ start--;
221
+ while (end < text.length && !/\s/u.test(text[end]))
222
+ end++;
223
+ if (start === end)
224
+ end = Math.min(text.length, start + 1);
225
+ const range = document.createRange();
226
+ range.setStart(point.startContainer, start);
227
+ range.setEnd(point.startContainer, end);
228
+ const selection = document.getSelection?.();
229
+ if (!selection)
230
+ return false;
231
+ selection.removeAllRanges();
232
+ selection.addRange(range);
233
+ historyFrozen = true;
234
+ return Boolean(outputSelection());
235
+ }
163
236
  function closeHistory() {
237
+ closeOutputMenu();
164
238
  clearOutputSelection();
165
239
  historyPanel.hidden = true;
166
240
  historyFrozen = false;
@@ -259,6 +333,7 @@ function selectAllOutput() {
259
333
  const selection = document.getSelection();
260
334
  selection.removeAllRanges();
261
335
  selection.addRange(range);
336
+ updateOutputMenu();
262
337
  return true;
263
338
  }
264
339
  async function copyOutputText(selectedText = null) {
@@ -289,12 +364,108 @@ async function copyOutputText(selectedText = null) {
289
364
  copyStatus.textContent = copied ? "Copied output" : "Select text and use the browser Copy menu";
290
365
  }
291
366
  }
292
- historyText.addEventListener("pointerdown", () => {
367
+ async function pasteOutputText() {
368
+ closeOutputMenu();
369
+ const generation = connectionGeneration;
370
+ if (!navigator.clipboard?.readText) {
371
+ copyStatus.textContent = "Clipboard unavailable; use the keyboard Paste action";
372
+ return;
373
+ }
374
+ let text;
375
+ try {
376
+ text = await navigator.clipboard.readText();
377
+ } catch {
378
+ if (generation === connectionGeneration)
379
+ copyStatus.textContent = "Clipboard permission denied";
380
+ return;
381
+ }
382
+ if (generation !== connectionGeneration || pendingSessionId)
383
+ return;
384
+ if (!text) {
385
+ copyStatus.textContent = "Clipboard is empty";
386
+ return;
387
+ }
388
+ if (!authenticated || socket?.readyState !== WebSocket.OPEN) {
389
+ copyStatus.textContent = "Connect before pasting";
390
+ return;
391
+ }
392
+ closeHistory();
393
+ const start = command.selectionStart ?? command.value.length;
394
+ const end = command.selectionEnd ?? start;
395
+ if (terminal.buffer.active.type !== "alternate" && !remoteEditing) {
396
+ remoteEditing = false;
397
+ speechDraft = true;
398
+ command.setRangeText(text, start, end, "end");
399
+ lastNativeValue = command.value;
400
+ focusCommand();
401
+ positionCommand();
402
+ copyStatus.textContent = "Pasted into draft; press Enter to send";
403
+ return;
404
+ }
405
+ const data = terminal.modes.bracketedPasteMode ? `\x1B[200~${text.replace(/\r?\n/g, "\r")}\x1B[201~` : text;
406
+ if (send({ type: "input", data }))
407
+ copyStatus.textContent = "Pasted";
408
+ }
409
+ function beginOutputLongPress(event) {
410
+ if (event.pointerType && event.pointerType !== "touch" && event.pointerType !== "pen")
411
+ return;
293
412
  historyFrozen = true;
413
+ clearTimeout(outputMenuTimer);
414
+ outputMenuPointer = { x: event.clientX ?? 0, y: event.clientY ?? 0 };
415
+ outputMenuTimer = setTimeout(() => {
416
+ if (!outputMenuPointer)
417
+ return;
418
+ const { x, y } = outputMenuPointer;
419
+ outputMenuPointer = undefined;
420
+ selectOutputAtPoint(x, y);
421
+ showOutputMenu(x, y);
422
+ }, 500);
423
+ }
424
+ function cancelOutputLongPress(event) {
425
+ if (!outputMenuPointer)
426
+ return;
427
+ if (event?.type === "pointermove") {
428
+ if (Math.hypot(event.clientX - outputMenuPointer.x, event.clientY - outputMenuPointer.y) > 10)
429
+ closeOutputMenu();
430
+ return;
431
+ }
432
+ if (event && Math.hypot(event.clientX - outputMenuPointer.x, event.clientY - outputMenuPointer.y) > 10)
433
+ closeOutputMenu();
434
+ else if (!outputMenuOpen) {
435
+ clearTimeout(outputMenuTimer);
436
+ outputMenuTimer = undefined;
437
+ outputMenuPointer = undefined;
438
+ }
439
+ }
440
+ historyText.addEventListener("pointerdown", beginOutputLongPress);
441
+ historyText.addEventListener("pointermove", cancelOutputLongPress);
442
+ historyText.addEventListener("pointerup", cancelOutputLongPress);
443
+ historyText.addEventListener("pointercancel", closeOutputMenu);
444
+ terminalContainer.addEventListener("pointerdown", (event) => {
445
+ if (historyPanel.hidden)
446
+ beginOutputLongPress(event);
447
+ });
448
+ terminalContainer.addEventListener("pointermove", cancelOutputLongPress);
449
+ terminalContainer.addEventListener("pointerup", cancelOutputLongPress);
450
+ terminalContainer.addEventListener("pointercancel", closeOutputMenu);
451
+ terminalContainer.addEventListener("contextmenu", (event) => {
452
+ if (!historyPanel.hidden)
453
+ return;
454
+ event.preventDefault();
455
+ selectOutputAtPoint(event.clientX, event.clientY);
456
+ showOutputMenu(event.clientX, event.clientY);
457
+ });
458
+ historyText.addEventListener("contextmenu", (event) => {
459
+ event.preventDefault();
460
+ selectOutputAtPoint(event.clientX, event.clientY);
461
+ showOutputMenu(event.clientX, event.clientY);
294
462
  });
295
463
  document.addEventListener("selectionchange", () => {
296
- if (!historyPanel.hidden && outputSelection())
464
+ if (outputMenuOpen)
465
+ updateOutputMenu();
466
+ if (!historyPanel.hidden && outputSelection()) {
297
467
  historyFrozen = true;
468
+ }
298
469
  });
299
470
  historyPanel.addEventListener("keydown", (event) => {
300
471
  if (event.key.toLowerCase() === "a" && (event.metaKey || event.ctrlKey)) {
@@ -318,6 +489,29 @@ historyCopy.addEventListener("click", () => {
318
489
  selectedForCopy = null;
319
490
  return copyOutputText(selected);
320
491
  });
492
+ outputSelect.addEventListener("click", () => {
493
+ closeOutputMenu();
494
+ });
495
+ outputSelectAll.addEventListener("click", () => {
496
+ selectAllOutput();
497
+ });
498
+ outputMenu.addEventListener("pointerdown", (event) => {
499
+ event.preventDefault();
500
+ });
501
+ outputCopy.addEventListener("click", () => {
502
+ const selected = outputSelection();
503
+ closeOutputMenu();
504
+ return copyOutputText(selected);
505
+ });
506
+ outputPaste.addEventListener("click", pasteOutputText);
507
+ document.addEventListener("pointerdown", (event) => {
508
+ if (outputMenuOpen && !outputMenu.contains(event.target))
509
+ closeOutputMenu();
510
+ });
511
+ document.addEventListener("keydown", (event) => {
512
+ if (event.key === "Escape")
513
+ closeOutputMenu();
514
+ });
321
515
  function updateModifiers() {
322
516
  ctrlButton.textContent = ctrl === 2 ? "CTRL \uD83D\uDD12" : ctrl ? "CTRL" : "Ctrl";
323
517
  ctrlButton.setAttribute("aria-pressed", String(ctrl > 0));
@@ -60,6 +60,12 @@
60
60
  <span id="copy-status" role="status" aria-live="polite"></span>
61
61
  <pre id="history-text"></pre>
62
62
  </section>
63
+ <div id="output-menu" class="output-menu" role="menu" aria-label="Terminal text actions" hidden>
64
+ <button id="output-paste" type="button" role="menuitem">Paste</button>
65
+ <button id="output-select" type="button" role="menuitem">Select</button>
66
+ <button id="output-select-all" type="button" role="menuitem">Select All</button>
67
+ <button id="output-copy" type="button" role="menuitem" hidden>Copy</button>
68
+ </div>
63
69
  <form id="command-form" autocomplete="off" hidden>
64
70
  <label class="sr-only" for="command">Shell command (Vietnamese IME)</label>
65
71
  <textarea id="command" rows="1" wrap="off" autocomplete="off" autocapitalize="off" autocorrect="off" spellcheck="false" aria-label="Shell command (Vietnamese IME)"></textarea>
@@ -20,6 +20,10 @@ input:focus, textarea:focus, select:focus, button:focus-visible, summary:focus-v
20
20
  #history-lines { width: 72px; padding: 3px 5px; font-size: 16px; }
21
21
  #history-text { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; user-select: text; -webkit-user-select: text; font: 14px/1.4 "courier-new", courier, monospace; }
22
22
  ::selection { color: #fff; background: #5b21b6; }
23
+ .output-menu { position: absolute; z-index: 6; display: grid; min-width: 148px; gap: 2px; padding: 4px; border: 1px solid #64748b; border-radius: 7px; background: #1e293b; box-shadow: 0 8px 24px #0009; }
24
+ .output-menu[hidden] { display: none; }
25
+ .output-menu button { min-height: 38px; padding: 7px 10px; border: 0; border-radius: 4px; background: transparent; text-align: left; }
26
+ .output-menu button:hover, .output-menu button:focus-visible { background: #334155; }
23
27
  summary { cursor: pointer; }
24
28
  #session-menu { flex: none; }
25
29
  #session-menu > summary { list-style: none; width: 40px; height: 34px; text-align: center; font-size: 24px; line-height: 30px; }
@@ -24434,13 +24434,7 @@ var CAPABILITY_LABELS = {
24434
24434
  "stored-content": "stored-result retrieval"
24435
24435
  };
24436
24436
  function supportsDynamicTools(pi) {
24437
- if (typeof pi.getAllTools !== "function" || typeof pi.getActiveTools !== "function" || typeof pi.setActiveTools !== "function")
24438
- return false;
24439
- const unsubscribe = pi.on("session_start", () => {});
24440
- if (typeof unsubscribe !== "function")
24441
- return false;
24442
- unsubscribe();
24443
- return true;
24437
+ return typeof pi.on === "function" && typeof pi.getAllTools === "function" && typeof pi.getActiveTools === "function" && typeof pi.setActiveTools === "function";
24444
24438
  }
24445
24439
  function addsToolsWithoutCheckpoint(model) {
24446
24440
  const compat = model?.compat;
@@ -24476,7 +24470,6 @@ function registerWebToolActivation(pi, tools, mode) {
24476
24470
  if (tools.length === 0)
24477
24471
  return;
24478
24472
  if (!supportsDynamicTools(pi)) {
24479
- console.warn("[dm-web-access] Dynamic tool activation requires DM 0.86.0 or newer; web tools remain eagerly available.");
24480
24473
  return;
24481
24474
  }
24482
24475
  const names = tools.map((tool) => tool.name);
@@ -8,13 +8,7 @@ const CAPABILITY_LABELS = {
8
8
  "stored-content": "stored-result retrieval"
9
9
  };
10
10
  function supportsDynamicTools(pi) {
11
- if (typeof pi.getAllTools !== "function" || typeof pi.getActiveTools !== "function" || typeof pi.setActiveTools !== "function")
12
- return false;
13
- const unsubscribe = pi.on("session_start", () => {});
14
- if (typeof unsubscribe !== "function")
15
- return false;
16
- unsubscribe();
17
- return true;
11
+ return typeof pi.on === "function" && typeof pi.getAllTools === "function" && typeof pi.getActiveTools === "function" && typeof pi.setActiveTools === "function";
18
12
  }
19
13
  function addsToolsWithoutCheckpoint(model) {
20
14
  const compat = model?.compat;
@@ -50,7 +44,6 @@ export function registerWebToolActivation(pi, tools, mode) {
50
44
  if (tools.length === 0)
51
45
  return;
52
46
  if (!supportsDynamicTools(pi)) {
53
- console.warn("[dm-web-access] Dynamic tool activation requires DM 0.86.0 or newer; web tools remain eagerly available.");
54
47
  return;
55
48
  }
56
49
  const names = tools.map((tool) => tool.name);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@duckmind/dm-darwin-x64",
3
- "version": "0.66.7",
3
+ "version": "0.66.9",
4
4
  "description": "DuckMind (dm) binary payload for darwin x64",
5
5
  "license": "MIT",
6
6
  "os": [
@@ -12,7 +12,7 @@
12
12
  "bin": {
13
13
  "dm": "dm"
14
14
  },
15
- "piConfig": {
15
+ "dmConfig": {
16
16
  "name": "dm",
17
17
  "configDir": ".dm"
18
18
  },
@@ -24,7 +24,7 @@
24
24
  "terminal"
25
25
  ],
26
26
  "engines": {
27
- "node": ">=20.0.0"
27
+ "node": ">=22.19.0"
28
28
  },
29
29
  "dependencies": {
30
30
  "@js-temporal/polyfill": "0.5.1",