@yolo-labs/yolobridge 0.15.0 → 0.17.0
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/dist/cli.js +37 -1
- package/dist/local-agent.js +66 -2
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
* YOLOBRIDGE_AUTH_URL for local dev against a different environment.
|
|
18
18
|
*/
|
|
19
19
|
import { fileURLToPath } from 'node:url';
|
|
20
|
-
import
|
|
20
|
+
import path from 'node:path';
|
|
21
|
+
import { realpathSync, existsSync, readFileSync } from 'node:fs';
|
|
21
22
|
import { hostname } from 'node:os';
|
|
22
23
|
import { runLogin } from './login-cmd.js';
|
|
23
24
|
import { runAttachFromDisk, pickWorkspaceFromDisk } from './attach-cmd.js';
|
|
@@ -99,6 +100,7 @@ function printHelp() {
|
|
|
99
100
|
' that check entirely.',
|
|
100
101
|
' detach Detach the current workspace attachment.',
|
|
101
102
|
' status Print local login/attach state.',
|
|
103
|
+
' version Print the installed yolo-bridge version (also --version, -v).',
|
|
102
104
|
' --help Print this help.',
|
|
103
105
|
'',
|
|
104
106
|
`API base: ${apiUrl()} (override: YOLOBRIDGE_API_URL)`,
|
|
@@ -559,6 +561,35 @@ async function cmdWorkspaces() {
|
|
|
559
561
|
process.stdout.write(`${formatWorkspacesTable(result.workspaces)}\n`);
|
|
560
562
|
return 0;
|
|
561
563
|
}
|
|
564
|
+
/**
|
|
565
|
+
* This package's own version, read from the package.json that ships beside
|
|
566
|
+
* `dist/`.
|
|
567
|
+
*
|
|
568
|
+
* Resolved from `import.meta.url`, NOT `process.argv[1]`: the global install
|
|
569
|
+
* puts a symlink on PATH, and argv[1] is that symlink's unresolved path, so
|
|
570
|
+
* walking up from it lands outside the package. Same reasoning as
|
|
571
|
+
* `isMainModule` below.
|
|
572
|
+
*
|
|
573
|
+
* Returns `'unknown'` rather than throwing — a version probe must never be the
|
|
574
|
+
* thing that stops the CLI from starting.
|
|
575
|
+
*/
|
|
576
|
+
export function readOwnVersion() {
|
|
577
|
+
try {
|
|
578
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
579
|
+
// dist/cli.js -> package root. Walk up a couple of levels so this holds
|
|
580
|
+
// whether it is run from `dist/` or from a ts-node-style layout.
|
|
581
|
+
for (const rel of ['..', '../..']) {
|
|
582
|
+
const candidate = path.join(here, rel, 'package.json');
|
|
583
|
+
if (!existsSync(candidate))
|
|
584
|
+
continue;
|
|
585
|
+
const parsed = JSON.parse(readFileSync(candidate, 'utf-8'));
|
|
586
|
+
if (parsed.name === '@yolo-labs/yolobridge' && typeof parsed.version === 'string')
|
|
587
|
+
return parsed.version;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
catch { /* fall through */ }
|
|
591
|
+
return 'unknown';
|
|
592
|
+
}
|
|
562
593
|
async function main() {
|
|
563
594
|
const [, , cmd, ...rest] = process.argv;
|
|
564
595
|
switch (cmd) {
|
|
@@ -572,6 +603,11 @@ async function main() {
|
|
|
572
603
|
return cmdDetach();
|
|
573
604
|
case 'status':
|
|
574
605
|
return cmdStatus();
|
|
606
|
+
case 'version':
|
|
607
|
+
case '--version':
|
|
608
|
+
case '-v':
|
|
609
|
+
process.stdout.write(`${readOwnVersion()}\n`);
|
|
610
|
+
return 0;
|
|
575
611
|
case '--help':
|
|
576
612
|
case '-h':
|
|
577
613
|
case undefined:
|
package/dist/local-agent.js
CHANGED
|
@@ -412,10 +412,16 @@ export function computeUsedRows(term) {
|
|
|
412
412
|
}
|
|
413
413
|
return used;
|
|
414
414
|
}
|
|
415
|
-
/** The PTY's grid, which the tile must render at EXACTLY
|
|
416
|
-
* resized — the human at the keyboard is watching the same PTY), plus how
|
|
415
|
+
/** The PTY's CURRENT grid, which the tile must render at EXACTLY, plus how
|
|
417
416
|
* much of that grid is in USE.
|
|
418
417
|
*
|
|
418
|
+
* The viewer must not resize this grid — the human at the keyboard is
|
|
419
|
+
* watching the same PTY, so a cloud-side resize would reach over and reflow
|
|
420
|
+
* their real screen. But the human themselves CAN resize it, and when they
|
|
421
|
+
* do the ring restarts under a fresh epoch (see `handleLocalResize`), so a
|
|
422
|
+
* viewer holding the old geometry re-seeds rather than replaying old bytes
|
|
423
|
+
* against a grid that no longer exists.
|
|
424
|
+
*
|
|
419
425
|
* ⚠️ `usedRows` IS NOT A SECOND GEOMETRY. It never changes `cols`/`rows` and
|
|
420
426
|
* the viewer never resizes its terminal to it: a viewer's terminal that is
|
|
421
427
|
* not exactly `cols`×`rows` cannot replay the daemon's bytes (relative cursor
|
|
@@ -679,6 +685,12 @@ export function startLocalAgent(opts = {}) {
|
|
|
679
685
|
// explicitly to disable stdin piping entirely, distinct from omitting the
|
|
680
686
|
// field (which defaults to wiring up the real `process.stdin`).
|
|
681
687
|
const inStream = 'stdin' in opts ? opts.stdin : process.stdin;
|
|
688
|
+
// Same defaulting rule as `resolveCols`/`resolveRows`: only reach for the real
|
|
689
|
+
// `process.stdout` when nothing was injected, so a test with a fake sink does
|
|
690
|
+
// not silently pick up the CI runner's terminal.
|
|
691
|
+
const resizeSource = 'resizeSource' in opts
|
|
692
|
+
? opts.resizeSource
|
|
693
|
+
: (opts.stdout === undefined ? process.stdout : undefined);
|
|
682
694
|
const ptyProcess = spawnImpl(agentBin, agentArgs, {
|
|
683
695
|
name: 'xterm-256color',
|
|
684
696
|
cols,
|
|
@@ -706,8 +718,14 @@ export function startLocalAgent(opts = {}) {
|
|
|
706
718
|
writeChain: Promise.resolve(),
|
|
707
719
|
stdin: inStream,
|
|
708
720
|
rawModeEnabled: false,
|
|
721
|
+
resizeSource,
|
|
709
722
|
};
|
|
710
723
|
current = state;
|
|
724
|
+
if (resizeSource && typeof resizeSource.on === 'function') {
|
|
725
|
+
const resizeListener = () => handleLocalResize(state);
|
|
726
|
+
state.resizeListener = resizeListener;
|
|
727
|
+
resizeSource.on('resize', resizeListener);
|
|
728
|
+
}
|
|
711
729
|
ptyProcess.onData((data) => {
|
|
712
730
|
state.lastOutputAt = Date.now();
|
|
713
731
|
outStream.write(data);
|
|
@@ -756,7 +774,53 @@ export function startLocalAgent(opts = {}) {
|
|
|
756
774
|
* exit code 124. With `stdin.pause()` added below, the same repro exits
|
|
757
775
|
* cleanly on its own well under a second — no timeout/kill needed.
|
|
758
776
|
*/
|
|
777
|
+
/**
|
|
778
|
+
* The human at the keyboard resized their terminal window.
|
|
779
|
+
*
|
|
780
|
+
* WHY THIS EXISTS: the PTY size used to be treated as fixed at spawn, on the
|
|
781
|
+
* reasoning that "nothing resizes this PTY, least of all the cloud". The cloud
|
|
782
|
+
* half of that is right and still enforced — a viewer must never reach over and
|
|
783
|
+
* reflow the operator's real screen. The other half was simply wrong: the human
|
|
784
|
+
* watching this PTY through `process.stdout` can drag their window, Node raises
|
|
785
|
+
* `'resize'` on SIGWINCH, and we ignored it. The child kept rendering to the old
|
|
786
|
+
* grid, so the operator's own terminal garbled and the tile kept reporting a
|
|
787
|
+
* geometry that no longer matched anything.
|
|
788
|
+
*
|
|
789
|
+
* Everything retained in the ring was captured against a grid that no longer
|
|
790
|
+
* exists — relative cursor moves and the scroll region are defined against the
|
|
791
|
+
* REAL grid, so replaying those bytes at the new size garbles rather than
|
|
792
|
+
* degrades. A fresh epoch is the honest answer: the viewer re-seeds from the
|
|
793
|
+
* current buffer instead of splicing two geometries together.
|
|
794
|
+
*/
|
|
795
|
+
function handleLocalResize(state) {
|
|
796
|
+
const src = state.resizeSource;
|
|
797
|
+
if (!src)
|
|
798
|
+
return;
|
|
799
|
+
const cols = src.columns ?? 0;
|
|
800
|
+
const rows = src.rows ?? 0;
|
|
801
|
+
// A stdout that has stopped being a TTY (piped, or the window is gone)
|
|
802
|
+
// reports 0/undefined. Resizing a PTY to zero wedges the child.
|
|
803
|
+
if (cols < 1 || rows < 1)
|
|
804
|
+
return;
|
|
805
|
+
// SIGWINCH also fires for changes that are not a size change. Resizing to the
|
|
806
|
+
// size we already have would burn an epoch and force a pointless re-seed.
|
|
807
|
+
if (cols === state.cols && rows === state.rows)
|
|
808
|
+
return;
|
|
809
|
+
state.cols = cols;
|
|
810
|
+
state.rows = rows;
|
|
811
|
+
state.term.resize(cols, rows);
|
|
812
|
+
rawRing = newRawRing(rows);
|
|
813
|
+
try {
|
|
814
|
+
state.ptyProcess.resize(cols, rows);
|
|
815
|
+
}
|
|
816
|
+
catch {
|
|
817
|
+
// The child is already gone; onExit will clear `current`.
|
|
818
|
+
}
|
|
819
|
+
}
|
|
759
820
|
function teardownStdio(state) {
|
|
821
|
+
if (state.resizeSource && state.resizeListener && typeof state.resizeSource.removeListener === 'function') {
|
|
822
|
+
state.resizeSource.removeListener('resize', state.resizeListener);
|
|
823
|
+
}
|
|
760
824
|
const { stdin, stdinListener } = state;
|
|
761
825
|
if (stdin && stdinListener && typeof stdin.removeListener === 'function') {
|
|
762
826
|
stdin.removeListener('data', stdinListener);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yolo-labs/yolobridge",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "YoloBridge — local coding-agent daemon that attaches a user's own Claude Code/Codex session to a YOLO Studio workspace as a first-class tile (docs/YOLOBRIDGE_PLAN.md, build-order Phase 5).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|