very-happy-cli 0.2.132 → 0.2.134
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/{AcpBackend-BNMLGtST.mjs → AcpBackend-B8KEjDwF.mjs} +3 -3
- package/dist/{AcpBackend--EnHZRC4.cjs → AcpBackend-CeNp7tgD.cjs} +3 -3
- package/dist/{AcpSessionManager-RoOyx5KI.cjs → AcpSessionManager-Bu1d05bx.cjs} +1 -1
- package/dist/{AcpSessionManager-BF5JZYCC.mjs → AcpSessionManager-KlDZbEmZ.mjs} +1 -1
- package/dist/{agentGuidance-DeOIgOhh.mjs → agentGuidance-CPG5Cr5T.mjs} +1 -1
- package/dist/{agentGuidance-R9MH5Cd5.cjs → agentGuidance-Cg8Lha39.cjs} +1 -1
- package/dist/codex/happyMcpStdioBridge.cjs +2 -2
- package/dist/codex/happyMcpStdioBridge.mjs +2 -2
- package/dist/{config-CkmssSRu.cjs → config-C3e90AIH.cjs} +4 -4
- package/dist/{config-ClCkJ1a_.mjs → config-C4gWfnJX.mjs} +4 -4
- package/dist/{index-Dn7s2iaT.cjs → index-B1plzL9M.cjs} +85 -36
- package/dist/{index-DdbU8FmA.mjs → index-B6UvB88K.mjs} +16 -10
- package/dist/{index-D1gHXtkw.mjs → index-BqUOj1zK.mjs} +81 -32
- package/dist/{index-DK0qz1y7.mjs → index-CEDPokKf.mjs} +4 -4
- package/dist/{index-Bqg6yo5s.cjs → index-CX-zp3FH.cjs} +4 -4
- package/dist/{index-CjopZZg3.cjs → index-jqG8CV0c.cjs} +15 -9
- package/dist/index.cjs +4 -4
- package/dist/index.mjs +4 -4
- package/dist/{installTerminalHooks-CRWWmonO.mjs → installTerminalHooks-BT4Dq28Y.mjs} +4 -4
- package/dist/{installTerminalHooks-CPZ3vVn8.cjs → installTerminalHooks-DPmEaHg2.cjs} +4 -4
- package/dist/lib.cjs +2 -2
- package/dist/lib.d.cts +15 -60
- package/dist/lib.d.mts +15 -60
- package/dist/lib.mjs +2 -2
- package/dist/{limits-C9aXkP7U.mjs → limits-BGeIBBE3.mjs} +7 -7
- package/dist/{limits-DLlsV71c.cjs → limits-D6kiAfXJ.cjs} +7 -7
- package/dist/{mcp-DkoXvc07.mjs → mcp-BGu_dUaJ.mjs} +4 -4
- package/dist/{mcp-DHkYfENQ.cjs → mcp-DGZmUpuZ.cjs} +4 -4
- package/dist/{runGemini-Cx1YcK2o.cjs → runGemini-BPPnulIk.cjs} +6 -6
- package/dist/{runGemini-BjEl7syj.mjs → runGemini-DGTNwiLo.mjs} +6 -6
- package/dist/{runOpenClaw-I4sGAQf1.cjs → runOpenClaw-BB3Ivt1S.cjs} +5 -5
- package/dist/{runOpenClaw-ByHu69K5.mjs → runOpenClaw-CxgTFOSM.mjs} +5 -5
- package/dist/{send-gH1vJ0iv.cjs → send-Bo6iKuXr.cjs} +4 -4
- package/dist/{send-CSOeUlQC.mjs → send-DHShoBeq.mjs} +4 -4
- package/dist/{sessions-CP9FlcJ6.mjs → sessions-CJuwvEr0.mjs} +4 -4
- package/dist/{sessions-BBc8L8qf.cjs → sessions-DyC5Attg.cjs} +4 -4
- package/dist/{spawn-DrQo81MW.mjs → spawn-QtKlr8Ig.mjs} +4 -4
- package/dist/{spawn-Dmfbot5k.cjs → spawn-gha6nGx-.cjs} +4 -4
- package/dist/{teams-Bbvo1G6y.mjs → teams-BF-GxUZc.mjs} +4 -4
- package/dist/{teams-DtXQYmEK.cjs → teams-BbzWNV9e.cjs} +4 -4
- package/dist/{todo-DOi6svw9.cjs → todo-BaNaNZ8c.cjs} +3 -3
- package/dist/{todo-BAwevRyb.mjs → todo-CkJ5UjWs.mjs} +3 -3
- package/dist/{types-CA-c2NH7.mjs → types-Cq-tvv9t.mjs} +89 -27
- package/dist/{types-B15E2OVy.cjs → types-Dabm17Ln.cjs} +90 -28
- package/package.json +7 -7
|
@@ -2,15 +2,15 @@ import { homedir } from 'node:os';
|
|
|
2
2
|
import { unlink, rmdir, mkdir, lstat, readFile, writeFile, rename } from 'node:fs/promises';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import { createHash, randomUUID } from 'node:crypto';
|
|
5
|
-
import { d as teamRequest, e as createTeamsClient } from './agentGuidance-
|
|
6
|
-
import { d as readCredentialsForConfiguredRelay } from './limits-
|
|
5
|
+
import { d as teamRequest, e as createTeamsClient } from './agentGuidance-CPG5Cr5T.mjs';
|
|
6
|
+
import { d as readCredentialsForConfiguredRelay } from './limits-BGeIBBE3.mjs';
|
|
7
7
|
import { join, resolve, dirname } from 'node:path';
|
|
8
|
-
import { T as TEAM_SKILL } from './index-
|
|
8
|
+
import { T as TEAM_SKILL } from './index-BqUOj1zK.mjs';
|
|
9
9
|
import 'node:fs';
|
|
10
10
|
import 'tweetnacl';
|
|
11
11
|
import 'chalk';
|
|
12
12
|
import 'node:util';
|
|
13
|
-
import './types-
|
|
13
|
+
import './types-Cq-tvv9t.mjs';
|
|
14
14
|
import 'axios';
|
|
15
15
|
import 'node:events';
|
|
16
16
|
import 'socket.io-client';
|
|
@@ -4,15 +4,15 @@ var os = require('node:os');
|
|
|
4
4
|
var promises = require('node:fs/promises');
|
|
5
5
|
var z = require('zod');
|
|
6
6
|
var node_crypto = require('node:crypto');
|
|
7
|
-
var agentGuidance = require('./agentGuidance-
|
|
8
|
-
var persistence = require('./limits-
|
|
7
|
+
var agentGuidance = require('./agentGuidance-Cg8Lha39.cjs');
|
|
8
|
+
var persistence = require('./limits-D6kiAfXJ.cjs');
|
|
9
9
|
var path = require('node:path');
|
|
10
|
-
var index = require('./index-
|
|
10
|
+
var index = require('./index-B1plzL9M.cjs');
|
|
11
11
|
require('node:fs');
|
|
12
12
|
require('tweetnacl');
|
|
13
13
|
require('chalk');
|
|
14
14
|
require('node:util');
|
|
15
|
-
require('./types-
|
|
15
|
+
require('./types-Dabm17Ln.cjs');
|
|
16
16
|
require('axios');
|
|
17
17
|
require('node:events');
|
|
18
18
|
require('socket.io-client');
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var persistence = require('./limits-
|
|
4
|
-
var api = require('./types-
|
|
5
|
-
var agentGuidance = require('./agentGuidance-
|
|
3
|
+
var persistence = require('./limits-D6kiAfXJ.cjs');
|
|
4
|
+
var api = require('./types-Dabm17Ln.cjs');
|
|
5
|
+
var agentGuidance = require('./agentGuidance-Cg8Lha39.cjs');
|
|
6
6
|
require('node:fs/promises');
|
|
7
7
|
require('node:fs');
|
|
8
8
|
require('node:os');
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { d as readCredentialsForConfiguredRelay, c as configuration } from './limits-
|
|
2
|
-
import { b as createBuiltinTodoClient, B as BUILTIN_TODO_SKILL } from './types-
|
|
3
|
-
import { c as readTeamScope, t as teamScopePath } from './agentGuidance-
|
|
1
|
+
import { d as readCredentialsForConfiguredRelay, c as configuration } from './limits-BGeIBBE3.mjs';
|
|
2
|
+
import { b as createBuiltinTodoClient, B as BUILTIN_TODO_SKILL } from './types-Cq-tvv9t.mjs';
|
|
3
|
+
import { c as readTeamScope, t as teamScopePath } from './agentGuidance-CPG5Cr5T.mjs';
|
|
4
4
|
import 'node:fs/promises';
|
|
5
5
|
import 'node:fs';
|
|
6
6
|
import 'node:os';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import axios from 'axios';
|
|
2
|
-
import { l as logger, i as encodeBase64, j as encrypt, k as decrypt, m as decodeBase64, c as configuration, o as decryptBlob, q as prepareClipboardText, g as readSettings, r as readPersistedSessions, s as credentialRelayProblem, p as packageJson, t as readTrackedClaudeSessionIds, u as libsodiumEncryptForPublicKey, v as libsodiumPublicKeyFromSecretKey, x as getRandomBytes, y as redactLogValue } from './limits-
|
|
2
|
+
import { l as logger, i as encodeBase64, j as encrypt, k as decrypt, m as decodeBase64, c as configuration, o as decryptBlob, q as prepareClipboardText, g as readSettings, r as readPersistedSessions, s as credentialRelayProblem, p as packageJson, t as readTrackedClaudeSessionIds, u as libsodiumEncryptForPublicKey, v as libsodiumPublicKeyFromSecretKey, x as getRandomBytes, y as redactLogValue } from './limits-BGeIBBE3.mjs';
|
|
3
3
|
import { EventEmitter } from 'node:events';
|
|
4
4
|
import { io } from 'socket.io-client';
|
|
5
5
|
import * as z from 'zod';
|
|
@@ -830,6 +830,7 @@ const CliUpdateStateSchema = z$1.object({
|
|
|
830
830
|
* from `recommendedVersion`; null means no unattended install. */
|
|
831
831
|
autoUpdateVersion: z$1.string().nullable().optional(),
|
|
832
832
|
retrySupported: z$1.boolean().optional(),
|
|
833
|
+
manualUpdateSupported: z$1.boolean().optional(),
|
|
833
834
|
checkedAt: z$1.number(),
|
|
834
835
|
/**
|
|
835
836
|
* B-321: a new bundle is on disk but refused to run, so the daemon is still
|
|
@@ -839,6 +840,7 @@ const CliUpdateStateSchema = z$1.object({
|
|
|
839
840
|
handoverHold: z$1.object({ reason: z$1.string(), at: z$1.number() }).nullable().optional(),
|
|
840
841
|
/** B-327: the last thing the machine's auto-update did, or why it did nothing. */
|
|
841
842
|
autoUpdate: z$1.object({
|
|
843
|
+
source: z$1.string().optional(),
|
|
842
844
|
state: z$1.union([z$1.enum(["installing", "installed", "failed", "skipped"]), z$1.string()]),
|
|
843
845
|
version: z$1.string().nullable(),
|
|
844
846
|
detail: z$1.string().optional(),
|
|
@@ -855,6 +857,8 @@ const WebTerminalListItemSchema = z$1.object({
|
|
|
855
857
|
createdAt: z$1.number().optional(),
|
|
856
858
|
activityAt: z$1.number().optional(),
|
|
857
859
|
agentState: z$1.enum(["working", "needs_input", "idle", "shell"]).optional(),
|
|
860
|
+
agentKind: z$1.string().optional(),
|
|
861
|
+
agentObservedAt: z$1.number().optional(),
|
|
858
862
|
/** B-105: mirror session of the claude running inside the terminal. */
|
|
859
863
|
mirrorSessionId: z$1.string().optional(),
|
|
860
864
|
/** B-150: auto-restored after a restart (ms epoch) — the directory and the
|
|
@@ -4335,6 +4339,58 @@ function registerTodoHandlers(manager) {
|
|
|
4335
4339
|
});
|
|
4336
4340
|
}
|
|
4337
4341
|
|
|
4342
|
+
function codingAgentFromProcessTree(panePid, snapshot) {
|
|
4343
|
+
const rows = snapshot.split("\n").flatMap((line) => {
|
|
4344
|
+
const m = line.trim().match(/^(\d+)\s+(\d+)\s+(.+)$/);
|
|
4345
|
+
return m ? [{ pid: Number(m[1]), parent: Number(m[2]), command: m[3] }] : [];
|
|
4346
|
+
});
|
|
4347
|
+
const visit = (pid, seen = /* @__PURE__ */ new Set()) => {
|
|
4348
|
+
if (seen.has(pid)) return void 0;
|
|
4349
|
+
seen.add(pid);
|
|
4350
|
+
const row = rows.find((r) => r.pid === pid);
|
|
4351
|
+
if (!row) return void 0;
|
|
4352
|
+
for (const child of rows.filter((r) => r.parent === pid)) {
|
|
4353
|
+
const kind = visit(child.pid, seen);
|
|
4354
|
+
if (kind) return kind;
|
|
4355
|
+
}
|
|
4356
|
+
const words = row.command.split(/\s+/);
|
|
4357
|
+
const executable = words[0].split("/").pop();
|
|
4358
|
+
const candidate = executable === "node" ? words[1]?.split("/").pop() : executable;
|
|
4359
|
+
if (candidate === "pi" || candidate === "pi-coding-agent") return "pi";
|
|
4360
|
+
if (candidate === "codex") return "codex";
|
|
4361
|
+
if (candidate === "claude" || /^\d+\.\d+\.\d+$/.test(candidate ?? "")) return "claude";
|
|
4362
|
+
return void 0;
|
|
4363
|
+
};
|
|
4364
|
+
return visit(panePid);
|
|
4365
|
+
}
|
|
4366
|
+
|
|
4367
|
+
const shells = /* @__PURE__ */ new Set(["zsh", "bash", "fish", "sh", "dash", "ksh", "tcsh", "csh"]);
|
|
4368
|
+
function classifyAgentPane(command, tail) {
|
|
4369
|
+
const cmd = command.trim().replace(/^-/, "").split(/[\\/]/).pop().toLowerCase();
|
|
4370
|
+
const lines = tail.replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, "").replace(/\r/g, "").trimEnd().split("\n");
|
|
4371
|
+
const screen = lines.join("\n");
|
|
4372
|
+
const footer = lines.slice(-20).join("\n");
|
|
4373
|
+
if (shells.has(cmd)) return { agentState: "shell" };
|
|
4374
|
+
if (!["node", "claude", "codex", "pi"].includes(cmd) && !/^\d+\.\d+\.\d+$/.test(cmd)) return {};
|
|
4375
|
+
const agentKind = cmd === "pi" ? "pi" : cmd === "codex" ? "codex" : cmd === "claude" || /^\d+\.\d+\.\d+$/.test(cmd) ? "claude" : /OpenAI Codex/.test(screen) ? "codex" : /(?:^|\n)\s*pi v\d|Pi can explain its own features/.test(screen) ? "pi" : /Claude Code|tell Claude what to do|bypass permissions on|⏵⏵|[·✻✽✶✳] [^\n]+… \(esc to interrupt\)/.test(footer) ? "claude" : void 0;
|
|
4376
|
+
if (!agentKind) return {};
|
|
4377
|
+
const result = (agentState) => ({ agentKind, agentState });
|
|
4378
|
+
if (agentKind === "pi") {
|
|
4379
|
+
if (/^\s*→ \S/m.test(screen) && /(?:↑↓ navigate.*(?:enter|return) select.*esc cancel|Enter to select.*Esc to cancel)/i.test(footer)) return result("needs_input");
|
|
4380
|
+
if (/^\s*[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏◐◓◑◒]\s+(?:Working|Thinking|Retrying|Compacting context|Auto-compacting|Summarizing branch).*$/m.test(footer)) return result("working");
|
|
4381
|
+
if (/^\s*[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏◐◓◑◒]\s/m.test(footer)) return result();
|
|
4382
|
+
const editorRules = footer.match(/^\s*─{8,}\s*$/gm) ?? [];
|
|
4383
|
+
if (editorRules.length >= 2 && /(?:\d+(?:\.\d+)?|\?)%\/\d+(?:\.\d+)?[km]?.* • (?:thinking off|minimal|low|medium|high|xhigh)/i.test(footer)) return result("idle");
|
|
4384
|
+
return result();
|
|
4385
|
+
}
|
|
4386
|
+
const choice = /^[\s│]*[❯›>]\s*\d+\.\s/m.test(footer);
|
|
4387
|
+
if (choice && /Do you want|Would you like|approve|Yes,|Yes\b|Enter to select.*navigate.*Esc to cancel/i.test(footer)) return result("needs_input");
|
|
4388
|
+
if (/esc to interrupt/i.test(footer)) return result("working");
|
|
4389
|
+
if (agentKind === "codex" && /^\s*gpt-[\w.-]+ (?:minimal|low|medium|high|xhigh|fast)[^\n]* · (?:~|\/|[A-Z]:[\\/])/m.test(footer) && /^› /m.test(footer)) return result("idle");
|
|
4390
|
+
if (/\? for shortcuts|bypass permissions on|⏵⏵|\d+% context left/.test(footer)) return result("idle");
|
|
4391
|
+
return result();
|
|
4392
|
+
}
|
|
4393
|
+
|
|
4338
4394
|
function sanitizeGeometry(cols, rows) {
|
|
4339
4395
|
const ok = (v) => typeof v === "number" && Number.isInteger(v) && v >= 2 && v <= 1e4;
|
|
4340
4396
|
return ok(cols) && ok(rows) ? { cols, rows } : void 0;
|
|
@@ -9544,10 +9600,12 @@ class TerminalNotifyTracker {
|
|
|
9544
9600
|
* Feed one observation for a terminal. Returns the notification event this
|
|
9545
9601
|
* observation triggers, or null. Call once per terminal per tick.
|
|
9546
9602
|
*/
|
|
9547
|
-
observe(terminalId, state, now) {
|
|
9603
|
+
observe(terminalId, state, now, agentKind) {
|
|
9548
9604
|
let t = this.tracks.get(terminalId);
|
|
9549
|
-
if (!t) {
|
|
9605
|
+
if (!t || t.agentKind !== agentKind || now - t.lastObservedAt > 45e3) {
|
|
9550
9606
|
t = {
|
|
9607
|
+
agentKind,
|
|
9608
|
+
lastObservedAt: now,
|
|
9551
9609
|
armed: state === "working",
|
|
9552
9610
|
confirmed: state,
|
|
9553
9611
|
candidate: state,
|
|
@@ -9558,6 +9616,7 @@ class TerminalNotifyTracker {
|
|
|
9558
9616
|
this.tracks.set(terminalId, t);
|
|
9559
9617
|
return null;
|
|
9560
9618
|
}
|
|
9619
|
+
t.lastObservedAt = now;
|
|
9561
9620
|
if (state === "working") t.armed = true;
|
|
9562
9621
|
if (state === t.confirmed) {
|
|
9563
9622
|
t.candidate = state;
|
|
@@ -9890,6 +9949,8 @@ function terminalListSignature(items) {
|
|
|
9890
9949
|
t.createdAt ?? 0,
|
|
9891
9950
|
Math.floor((t.activityAt ?? 0) / ACTIVITY_SIGNATURE_BUCKET_MS),
|
|
9892
9951
|
t.agentState ?? "",
|
|
9952
|
+
t.agentKind ?? "",
|
|
9953
|
+
Math.floor((t.agentObservedAt ?? 0) / 1e4),
|
|
9893
9954
|
// B-105: a mirror binding appearing/disappearing MUST push the
|
|
9894
9955
|
// list, or the web never learns the toggle became available.
|
|
9895
9956
|
t.mirrorSessionId ?? "",
|
|
@@ -9938,7 +9999,7 @@ const LIST_SESSIONS_FORMAT = [
|
|
|
9938
9999
|
// command only separates shell/idle). MUST stay before pane_title: that
|
|
9939
10000
|
// field is deliberately last so a pathological 0x1f inside a title can only
|
|
9940
10001
|
// garble the title, never shift the fields.
|
|
9941
|
-
"#{pane_current_command}",
|
|
10002
|
+
"#{pane_current_command} #{pane_pid}",
|
|
9942
10003
|
"#{pane_title}"
|
|
9943
10004
|
].join(LIST_FIELD_SEP);
|
|
9944
10005
|
function parseSessionListLine(line) {
|
|
@@ -9956,7 +10017,8 @@ function parseSessionListLine(line) {
|
|
|
9956
10017
|
vhTitle: vhTitle.trim() || void 0,
|
|
9957
10018
|
manual: manual.trim().length > 0,
|
|
9958
10019
|
tags: parseTerminalTags(vhTags),
|
|
9959
|
-
paneCurrentCommand: paneCommand.trim() || void 0,
|
|
10020
|
+
paneCurrentCommand: paneCommand.trim().replace(/ \d+$/, "") || void 0,
|
|
10021
|
+
.../ (\d+)$/.test(paneCommand) ? { panePid: Number(paneCommand.match(/ (\d+)$/)[1]) } : {},
|
|
9960
10022
|
// Verbatim (no trim): tmux allows edge spaces in a session name and the
|
|
9961
10023
|
// restore lookup / attach echo compare it exactly.
|
|
9962
10024
|
attachTmux: isSafeTmuxSessionName(vhAttach) ? vhAttach : void 0,
|
|
@@ -9998,22 +10060,6 @@ function parseTerminalTags(raw) {
|
|
|
9998
10060
|
return [];
|
|
9999
10061
|
}
|
|
10000
10062
|
}
|
|
10001
|
-
function classifyPane(currentCommand, tail) {
|
|
10002
|
-
const cmd = (currentCommand || "").trim().replace(/^-/, "").toLowerCase();
|
|
10003
|
-
const isShell = SHELL_COMMANDS.has(cmd);
|
|
10004
|
-
const lines = tail.replace(/\r/g, "").split("\n").map((l) => l.trimEnd());
|
|
10005
|
-
while (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
|
|
10006
|
-
const text = lines.join("\n");
|
|
10007
|
-
const last15 = lines.slice(-15).join("\n");
|
|
10008
|
-
const hasDialog = last15.includes("Do you want") || last15.includes("Would you like to proceed") || /^[\s│]*[❯>]\s*1\.\s/m.test(last15) || /\(y\/n\)/i.test(last15);
|
|
10009
|
-
if (hasDialog) return "needs_input";
|
|
10010
|
-
if (text.includes("esc to interrupt")) return "working";
|
|
10011
|
-
const looksLikeClaude = looksLikeClaudeCommand(cmd);
|
|
10012
|
-
const hasIdleFooter = text.includes("? for shortcuts") || text.includes("bypass permissions on") || text.includes("\u23F5\u23F5");
|
|
10013
|
-
if (looksLikeClaude || hasIdleFooter) return "idle";
|
|
10014
|
-
if (isShell) return "shell";
|
|
10015
|
-
return void 0;
|
|
10016
|
-
}
|
|
10017
10063
|
function looksLikeClaudeCommand(currentCommand) {
|
|
10018
10064
|
const cmd = (currentCommand || "").trim().replace(/^-/, "").toLowerCase();
|
|
10019
10065
|
return cmd === "claude" || cmd === "node" || /^\d+\.\d+(\.\d+)?$/.test(cmd);
|
|
@@ -11024,7 +11070,7 @@ class WebTerminalManager {
|
|
|
11024
11070
|
const now = Date.now();
|
|
11025
11071
|
this.notifyTracker.prune(list.map((t) => t.id));
|
|
11026
11072
|
for (const item of list) {
|
|
11027
|
-
const event = this.notifyTracker.observe(item.id, item.agentState, now);
|
|
11073
|
+
const event = this.notifyTracker.observe(item.id, item.agentState, now, item.agentKind);
|
|
11028
11074
|
if (!event) continue;
|
|
11029
11075
|
try {
|
|
11030
11076
|
this.onNotify({ terminalId: item.id, title: item.title || "Terminal", event });
|
|
@@ -11756,6 +11802,7 @@ class WebTerminalManager {
|
|
|
11756
11802
|
if (r.status !== 0 || !r.stdout) return [];
|
|
11757
11803
|
const hostname = os$1.hostname();
|
|
11758
11804
|
const out = [];
|
|
11805
|
+
let processSnapshot;
|
|
11759
11806
|
for (const line of r.stdout.split("\n")) {
|
|
11760
11807
|
const s = parseSessionListLine(line);
|
|
11761
11808
|
if (!s || !s.name.startsWith("vh-")) continue;
|
|
@@ -11773,7 +11820,15 @@ class WebTerminalManager {
|
|
|
11773
11820
|
} catch {
|
|
11774
11821
|
}
|
|
11775
11822
|
}
|
|
11776
|
-
|
|
11823
|
+
let identity = s.paneCurrentCommand;
|
|
11824
|
+
if (identity === "node" && s.panePid) {
|
|
11825
|
+
if (processSnapshot === void 0) {
|
|
11826
|
+
const ps = spawnSync("ps", ["-axo", "pid=,ppid=,args="], { encoding: "utf8", timeout: TMUX_PROBE_TIMEOUT_MS });
|
|
11827
|
+
processSnapshot = ps.status === 0 ? ps.stdout : "";
|
|
11828
|
+
}
|
|
11829
|
+
identity = codingAgentFromProcessTree(s.panePid, processSnapshot) ?? identity;
|
|
11830
|
+
}
|
|
11831
|
+
const probe = this.probeAgentState(s.name, identity);
|
|
11777
11832
|
out.push({
|
|
11778
11833
|
id,
|
|
11779
11834
|
title,
|
|
@@ -11785,6 +11840,8 @@ class WebTerminalManager {
|
|
|
11785
11840
|
// simply omit it and web clients fall back to createdAt.
|
|
11786
11841
|
activityAt: s.activity,
|
|
11787
11842
|
agentState: probe.agentState,
|
|
11843
|
+
agentKind: probe.agentKind,
|
|
11844
|
+
agentObservedAt: probe.agentObservedAt,
|
|
11788
11845
|
// Daemon-internal (not pushed to web / not in the signature):
|
|
11789
11846
|
// the mirror reconciler's stricter "claude is really here" gate.
|
|
11790
11847
|
claudeConfident: probe.claudeConfident,
|
|
@@ -11811,16 +11868,17 @@ class WebTerminalManager {
|
|
|
11811
11868
|
if (live) {
|
|
11812
11869
|
try {
|
|
11813
11870
|
const { command, tail } = live.agentProbeInput();
|
|
11814
|
-
|
|
11871
|
+
const observation = classifyAgentPane(command === "node" ? polledCommand || command : command || polledCommand || "", tail);
|
|
11872
|
+
return { ...observation, agentObservedAt: Date.now(), claudeConfident: observation.agentKind === "claude" && isClaudeConfident(tail) };
|
|
11815
11873
|
} catch {
|
|
11816
11874
|
}
|
|
11817
11875
|
}
|
|
11818
|
-
return this.probeAgentStateViaTmux(sessionName);
|
|
11876
|
+
return this.probeAgentStateViaTmux(sessionName, polledCommand);
|
|
11819
11877
|
}
|
|
11820
11878
|
/** Fallback probe for sessions with no live headless: 2 short tmux calls
|
|
11821
11879
|
* (foreground command + pane tail) fed into classifyPane. Any failure or
|
|
11822
11880
|
* timeout → undefined (the field is omitted), never an error. */
|
|
11823
|
-
probeAgentStateViaTmux(sessionName) {
|
|
11881
|
+
probeAgentStateViaTmux(sessionName, polledCommand) {
|
|
11824
11882
|
try {
|
|
11825
11883
|
const cmd = spawnSync(
|
|
11826
11884
|
"tmux",
|
|
@@ -11834,7 +11892,8 @@ class WebTerminalManager {
|
|
|
11834
11892
|
{ encoding: "utf8", timeout: TMUX_PROBE_TIMEOUT_MS, env: ptyEnv() }
|
|
11835
11893
|
);
|
|
11836
11894
|
if (cap.status !== 0 || typeof cap.stdout !== "string") return { agentState: void 0, claudeConfident: false };
|
|
11837
|
-
|
|
11895
|
+
const observation = classifyAgentPane(cmd.stdout.trim() === "node" ? polledCommand || "node" : cmd.stdout.trim(), cap.stdout);
|
|
11896
|
+
return { ...observation, agentObservedAt: Date.now(), claudeConfident: observation.agentKind === "claude" && isClaudeConfident(cap.stdout) };
|
|
11838
11897
|
} catch {
|
|
11839
11898
|
return { agentState: void 0, claudeConfident: false };
|
|
11840
11899
|
}
|
|
@@ -14167,6 +14226,9 @@ class ApiMachineClient {
|
|
|
14167
14226
|
encTerminalData(dataBase64) {
|
|
14168
14227
|
return encodeBase64(encrypt(this.machine.encryptionKey, this.machine.encryptionVariant, dataBase64));
|
|
14169
14228
|
}
|
|
14229
|
+
setCliUpdateRequestHandler(handler) {
|
|
14230
|
+
this.rpcHandlerManager.registerHandler("cli-update-request", (params) => handler(params && typeof params === "object" ? params.version : void 0));
|
|
14231
|
+
}
|
|
14170
14232
|
setCliUpdateRetryHandler(handler) {
|
|
14171
14233
|
this.rpcHandlerManager.registerHandler("cli-update-retry", (params) => handler(params && typeof params === "object" ? params.version : void 0));
|
|
14172
14234
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var axios = require('axios');
|
|
4
|
-
var persistence = require('./limits-
|
|
4
|
+
var persistence = require('./limits-D6kiAfXJ.cjs');
|
|
5
5
|
var node_events = require('node:events');
|
|
6
6
|
var socket_ioClient = require('socket.io-client');
|
|
7
7
|
var z = require('zod');
|
|
@@ -852,6 +852,7 @@ const CliUpdateStateSchema = z.z.object({
|
|
|
852
852
|
* from `recommendedVersion`; null means no unattended install. */
|
|
853
853
|
autoUpdateVersion: z.z.string().nullable().optional(),
|
|
854
854
|
retrySupported: z.z.boolean().optional(),
|
|
855
|
+
manualUpdateSupported: z.z.boolean().optional(),
|
|
855
856
|
checkedAt: z.z.number(),
|
|
856
857
|
/**
|
|
857
858
|
* B-321: a new bundle is on disk but refused to run, so the daemon is still
|
|
@@ -861,6 +862,7 @@ const CliUpdateStateSchema = z.z.object({
|
|
|
861
862
|
handoverHold: z.z.object({ reason: z.z.string(), at: z.z.number() }).nullable().optional(),
|
|
862
863
|
/** B-327: the last thing the machine's auto-update did, or why it did nothing. */
|
|
863
864
|
autoUpdate: z.z.object({
|
|
865
|
+
source: z.z.string().optional(),
|
|
864
866
|
state: z.z.union([z.z.enum(["installing", "installed", "failed", "skipped"]), z.z.string()]),
|
|
865
867
|
version: z.z.string().nullable(),
|
|
866
868
|
detail: z.z.string().optional(),
|
|
@@ -877,6 +879,8 @@ const WebTerminalListItemSchema = z.z.object({
|
|
|
877
879
|
createdAt: z.z.number().optional(),
|
|
878
880
|
activityAt: z.z.number().optional(),
|
|
879
881
|
agentState: z.z.enum(["working", "needs_input", "idle", "shell"]).optional(),
|
|
882
|
+
agentKind: z.z.string().optional(),
|
|
883
|
+
agentObservedAt: z.z.number().optional(),
|
|
880
884
|
/** B-105: mirror session of the claude running inside the terminal. */
|
|
881
885
|
mirrorSessionId: z.z.string().optional(),
|
|
882
886
|
/** B-150: auto-restored after a restart (ms epoch) — the directory and the
|
|
@@ -1343,7 +1347,7 @@ class RpcHandlerManager {
|
|
|
1343
1347
|
}
|
|
1344
1348
|
}
|
|
1345
1349
|
|
|
1346
|
-
const __dirname$1 = path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('types-
|
|
1350
|
+
const __dirname$1 = path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('types-Dabm17Ln.cjs', document.baseURI).href))));
|
|
1347
1351
|
function projectPath() {
|
|
1348
1352
|
const path$1 = path.resolve(__dirname$1, "..");
|
|
1349
1353
|
return path$1;
|
|
@@ -4357,6 +4361,58 @@ function registerTodoHandlers(manager) {
|
|
|
4357
4361
|
});
|
|
4358
4362
|
}
|
|
4359
4363
|
|
|
4364
|
+
function codingAgentFromProcessTree(panePid, snapshot) {
|
|
4365
|
+
const rows = snapshot.split("\n").flatMap((line) => {
|
|
4366
|
+
const m = line.trim().match(/^(\d+)\s+(\d+)\s+(.+)$/);
|
|
4367
|
+
return m ? [{ pid: Number(m[1]), parent: Number(m[2]), command: m[3] }] : [];
|
|
4368
|
+
});
|
|
4369
|
+
const visit = (pid, seen = /* @__PURE__ */ new Set()) => {
|
|
4370
|
+
if (seen.has(pid)) return void 0;
|
|
4371
|
+
seen.add(pid);
|
|
4372
|
+
const row = rows.find((r) => r.pid === pid);
|
|
4373
|
+
if (!row) return void 0;
|
|
4374
|
+
for (const child of rows.filter((r) => r.parent === pid)) {
|
|
4375
|
+
const kind = visit(child.pid, seen);
|
|
4376
|
+
if (kind) return kind;
|
|
4377
|
+
}
|
|
4378
|
+
const words = row.command.split(/\s+/);
|
|
4379
|
+
const executable = words[0].split("/").pop();
|
|
4380
|
+
const candidate = executable === "node" ? words[1]?.split("/").pop() : executable;
|
|
4381
|
+
if (candidate === "pi" || candidate === "pi-coding-agent") return "pi";
|
|
4382
|
+
if (candidate === "codex") return "codex";
|
|
4383
|
+
if (candidate === "claude" || /^\d+\.\d+\.\d+$/.test(candidate ?? "")) return "claude";
|
|
4384
|
+
return void 0;
|
|
4385
|
+
};
|
|
4386
|
+
return visit(panePid);
|
|
4387
|
+
}
|
|
4388
|
+
|
|
4389
|
+
const shells = /* @__PURE__ */ new Set(["zsh", "bash", "fish", "sh", "dash", "ksh", "tcsh", "csh"]);
|
|
4390
|
+
function classifyAgentPane(command, tail) {
|
|
4391
|
+
const cmd = command.trim().replace(/^-/, "").split(/[\\/]/).pop().toLowerCase();
|
|
4392
|
+
const lines = tail.replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, "").replace(/\r/g, "").trimEnd().split("\n");
|
|
4393
|
+
const screen = lines.join("\n");
|
|
4394
|
+
const footer = lines.slice(-20).join("\n");
|
|
4395
|
+
if (shells.has(cmd)) return { agentState: "shell" };
|
|
4396
|
+
if (!["node", "claude", "codex", "pi"].includes(cmd) && !/^\d+\.\d+\.\d+$/.test(cmd)) return {};
|
|
4397
|
+
const agentKind = cmd === "pi" ? "pi" : cmd === "codex" ? "codex" : cmd === "claude" || /^\d+\.\d+\.\d+$/.test(cmd) ? "claude" : /OpenAI Codex/.test(screen) ? "codex" : /(?:^|\n)\s*pi v\d|Pi can explain its own features/.test(screen) ? "pi" : /Claude Code|tell Claude what to do|bypass permissions on|⏵⏵|[·✻✽✶✳] [^\n]+… \(esc to interrupt\)/.test(footer) ? "claude" : void 0;
|
|
4398
|
+
if (!agentKind) return {};
|
|
4399
|
+
const result = (agentState) => ({ agentKind, agentState });
|
|
4400
|
+
if (agentKind === "pi") {
|
|
4401
|
+
if (/^\s*→ \S/m.test(screen) && /(?:↑↓ navigate.*(?:enter|return) select.*esc cancel|Enter to select.*Esc to cancel)/i.test(footer)) return result("needs_input");
|
|
4402
|
+
if (/^\s*[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏◐◓◑◒]\s+(?:Working|Thinking|Retrying|Compacting context|Auto-compacting|Summarizing branch).*$/m.test(footer)) return result("working");
|
|
4403
|
+
if (/^\s*[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏◐◓◑◒]\s/m.test(footer)) return result();
|
|
4404
|
+
const editorRules = footer.match(/^\s*─{8,}\s*$/gm) ?? [];
|
|
4405
|
+
if (editorRules.length >= 2 && /(?:\d+(?:\.\d+)?|\?)%\/\d+(?:\.\d+)?[km]?.* • (?:thinking off|minimal|low|medium|high|xhigh)/i.test(footer)) return result("idle");
|
|
4406
|
+
return result();
|
|
4407
|
+
}
|
|
4408
|
+
const choice = /^[\s│]*[❯›>]\s*\d+\.\s/m.test(footer);
|
|
4409
|
+
if (choice && /Do you want|Would you like|approve|Yes,|Yes\b|Enter to select.*navigate.*Esc to cancel/i.test(footer)) return result("needs_input");
|
|
4410
|
+
if (/esc to interrupt/i.test(footer)) return result("working");
|
|
4411
|
+
if (agentKind === "codex" && /^\s*gpt-[\w.-]+ (?:minimal|low|medium|high|xhigh|fast)[^\n]* · (?:~|\/|[A-Z]:[\\/])/m.test(footer) && /^› /m.test(footer)) return result("idle");
|
|
4412
|
+
if (/\? for shortcuts|bypass permissions on|⏵⏵|\d+% context left/.test(footer)) return result("idle");
|
|
4413
|
+
return result();
|
|
4414
|
+
}
|
|
4415
|
+
|
|
4360
4416
|
function sanitizeGeometry(cols, rows) {
|
|
4361
4417
|
const ok = (v) => typeof v === "number" && Number.isInteger(v) && v >= 2 && v <= 1e4;
|
|
4362
4418
|
return ok(cols) && ok(rows) ? { cols, rows } : void 0;
|
|
@@ -9566,10 +9622,12 @@ class TerminalNotifyTracker {
|
|
|
9566
9622
|
* Feed one observation for a terminal. Returns the notification event this
|
|
9567
9623
|
* observation triggers, or null. Call once per terminal per tick.
|
|
9568
9624
|
*/
|
|
9569
|
-
observe(terminalId, state, now) {
|
|
9625
|
+
observe(terminalId, state, now, agentKind) {
|
|
9570
9626
|
let t = this.tracks.get(terminalId);
|
|
9571
|
-
if (!t) {
|
|
9627
|
+
if (!t || t.agentKind !== agentKind || now - t.lastObservedAt > 45e3) {
|
|
9572
9628
|
t = {
|
|
9629
|
+
agentKind,
|
|
9630
|
+
lastObservedAt: now,
|
|
9573
9631
|
armed: state === "working",
|
|
9574
9632
|
confirmed: state,
|
|
9575
9633
|
candidate: state,
|
|
@@ -9580,6 +9638,7 @@ class TerminalNotifyTracker {
|
|
|
9580
9638
|
this.tracks.set(terminalId, t);
|
|
9581
9639
|
return null;
|
|
9582
9640
|
}
|
|
9641
|
+
t.lastObservedAt = now;
|
|
9583
9642
|
if (state === "working") t.armed = true;
|
|
9584
9643
|
if (state === t.confirmed) {
|
|
9585
9644
|
t.candidate = state;
|
|
@@ -9912,6 +9971,8 @@ function terminalListSignature(items) {
|
|
|
9912
9971
|
t.createdAt ?? 0,
|
|
9913
9972
|
Math.floor((t.activityAt ?? 0) / ACTIVITY_SIGNATURE_BUCKET_MS),
|
|
9914
9973
|
t.agentState ?? "",
|
|
9974
|
+
t.agentKind ?? "",
|
|
9975
|
+
Math.floor((t.agentObservedAt ?? 0) / 1e4),
|
|
9915
9976
|
// B-105: a mirror binding appearing/disappearing MUST push the
|
|
9916
9977
|
// list, or the web never learns the toggle became available.
|
|
9917
9978
|
t.mirrorSessionId ?? "",
|
|
@@ -9960,7 +10021,7 @@ const LIST_SESSIONS_FORMAT = [
|
|
|
9960
10021
|
// command only separates shell/idle). MUST stay before pane_title: that
|
|
9961
10022
|
// field is deliberately last so a pathological 0x1f inside a title can only
|
|
9962
10023
|
// garble the title, never shift the fields.
|
|
9963
|
-
"#{pane_current_command}",
|
|
10024
|
+
"#{pane_current_command} #{pane_pid}",
|
|
9964
10025
|
"#{pane_title}"
|
|
9965
10026
|
].join(LIST_FIELD_SEP);
|
|
9966
10027
|
function parseSessionListLine(line) {
|
|
@@ -9978,7 +10039,8 @@ function parseSessionListLine(line) {
|
|
|
9978
10039
|
vhTitle: vhTitle.trim() || void 0,
|
|
9979
10040
|
manual: manual.trim().length > 0,
|
|
9980
10041
|
tags: parseTerminalTags(vhTags),
|
|
9981
|
-
paneCurrentCommand: paneCommand.trim() || void 0,
|
|
10042
|
+
paneCurrentCommand: paneCommand.trim().replace(/ \d+$/, "") || void 0,
|
|
10043
|
+
.../ (\d+)$/.test(paneCommand) ? { panePid: Number(paneCommand.match(/ (\d+)$/)[1]) } : {},
|
|
9982
10044
|
// Verbatim (no trim): tmux allows edge spaces in a session name and the
|
|
9983
10045
|
// restore lookup / attach echo compare it exactly.
|
|
9984
10046
|
attachTmux: isSafeTmuxSessionName(vhAttach) ? vhAttach : void 0,
|
|
@@ -10020,22 +10082,6 @@ function parseTerminalTags(raw) {
|
|
|
10020
10082
|
return [];
|
|
10021
10083
|
}
|
|
10022
10084
|
}
|
|
10023
|
-
function classifyPane(currentCommand, tail) {
|
|
10024
|
-
const cmd = (currentCommand || "").trim().replace(/^-/, "").toLowerCase();
|
|
10025
|
-
const isShell = SHELL_COMMANDS.has(cmd);
|
|
10026
|
-
const lines = tail.replace(/\r/g, "").split("\n").map((l) => l.trimEnd());
|
|
10027
|
-
while (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
|
|
10028
|
-
const text = lines.join("\n");
|
|
10029
|
-
const last15 = lines.slice(-15).join("\n");
|
|
10030
|
-
const hasDialog = last15.includes("Do you want") || last15.includes("Would you like to proceed") || /^[\s│]*[❯>]\s*1\.\s/m.test(last15) || /\(y\/n\)/i.test(last15);
|
|
10031
|
-
if (hasDialog) return "needs_input";
|
|
10032
|
-
if (text.includes("esc to interrupt")) return "working";
|
|
10033
|
-
const looksLikeClaude = looksLikeClaudeCommand(cmd);
|
|
10034
|
-
const hasIdleFooter = text.includes("? for shortcuts") || text.includes("bypass permissions on") || text.includes("\u23F5\u23F5");
|
|
10035
|
-
if (looksLikeClaude || hasIdleFooter) return "idle";
|
|
10036
|
-
if (isShell) return "shell";
|
|
10037
|
-
return void 0;
|
|
10038
|
-
}
|
|
10039
10085
|
function looksLikeClaudeCommand(currentCommand) {
|
|
10040
10086
|
const cmd = (currentCommand || "").trim().replace(/^-/, "").toLowerCase();
|
|
10041
10087
|
return cmd === "claude" || cmd === "node" || /^\d+\.\d+(\.\d+)?$/.test(cmd);
|
|
@@ -11046,7 +11092,7 @@ class WebTerminalManager {
|
|
|
11046
11092
|
const now = Date.now();
|
|
11047
11093
|
this.notifyTracker.prune(list.map((t) => t.id));
|
|
11048
11094
|
for (const item of list) {
|
|
11049
|
-
const event = this.notifyTracker.observe(item.id, item.agentState, now);
|
|
11095
|
+
const event = this.notifyTracker.observe(item.id, item.agentState, now, item.agentKind);
|
|
11050
11096
|
if (!event) continue;
|
|
11051
11097
|
try {
|
|
11052
11098
|
this.onNotify({ terminalId: item.id, title: item.title || "Terminal", event });
|
|
@@ -11778,6 +11824,7 @@ class WebTerminalManager {
|
|
|
11778
11824
|
if (r.status !== 0 || !r.stdout) return [];
|
|
11779
11825
|
const hostname = os$1.hostname();
|
|
11780
11826
|
const out = [];
|
|
11827
|
+
let processSnapshot;
|
|
11781
11828
|
for (const line of r.stdout.split("\n")) {
|
|
11782
11829
|
const s = parseSessionListLine(line);
|
|
11783
11830
|
if (!s || !s.name.startsWith("vh-")) continue;
|
|
@@ -11795,7 +11842,15 @@ class WebTerminalManager {
|
|
|
11795
11842
|
} catch {
|
|
11796
11843
|
}
|
|
11797
11844
|
}
|
|
11798
|
-
|
|
11845
|
+
let identity = s.paneCurrentCommand;
|
|
11846
|
+
if (identity === "node" && s.panePid) {
|
|
11847
|
+
if (processSnapshot === void 0) {
|
|
11848
|
+
const ps = node_child_process.spawnSync("ps", ["-axo", "pid=,ppid=,args="], { encoding: "utf8", timeout: TMUX_PROBE_TIMEOUT_MS });
|
|
11849
|
+
processSnapshot = ps.status === 0 ? ps.stdout : "";
|
|
11850
|
+
}
|
|
11851
|
+
identity = codingAgentFromProcessTree(s.panePid, processSnapshot) ?? identity;
|
|
11852
|
+
}
|
|
11853
|
+
const probe = this.probeAgentState(s.name, identity);
|
|
11799
11854
|
out.push({
|
|
11800
11855
|
id,
|
|
11801
11856
|
title,
|
|
@@ -11807,6 +11862,8 @@ class WebTerminalManager {
|
|
|
11807
11862
|
// simply omit it and web clients fall back to createdAt.
|
|
11808
11863
|
activityAt: s.activity,
|
|
11809
11864
|
agentState: probe.agentState,
|
|
11865
|
+
agentKind: probe.agentKind,
|
|
11866
|
+
agentObservedAt: probe.agentObservedAt,
|
|
11810
11867
|
// Daemon-internal (not pushed to web / not in the signature):
|
|
11811
11868
|
// the mirror reconciler's stricter "claude is really here" gate.
|
|
11812
11869
|
claudeConfident: probe.claudeConfident,
|
|
@@ -11833,16 +11890,17 @@ class WebTerminalManager {
|
|
|
11833
11890
|
if (live) {
|
|
11834
11891
|
try {
|
|
11835
11892
|
const { command, tail } = live.agentProbeInput();
|
|
11836
|
-
|
|
11893
|
+
const observation = classifyAgentPane(command === "node" ? polledCommand || command : command || polledCommand || "", tail);
|
|
11894
|
+
return { ...observation, agentObservedAt: Date.now(), claudeConfident: observation.agentKind === "claude" && isClaudeConfident(tail) };
|
|
11837
11895
|
} catch {
|
|
11838
11896
|
}
|
|
11839
11897
|
}
|
|
11840
|
-
return this.probeAgentStateViaTmux(sessionName);
|
|
11898
|
+
return this.probeAgentStateViaTmux(sessionName, polledCommand);
|
|
11841
11899
|
}
|
|
11842
11900
|
/** Fallback probe for sessions with no live headless: 2 short tmux calls
|
|
11843
11901
|
* (foreground command + pane tail) fed into classifyPane. Any failure or
|
|
11844
11902
|
* timeout → undefined (the field is omitted), never an error. */
|
|
11845
|
-
probeAgentStateViaTmux(sessionName) {
|
|
11903
|
+
probeAgentStateViaTmux(sessionName, polledCommand) {
|
|
11846
11904
|
try {
|
|
11847
11905
|
const cmd = node_child_process.spawnSync(
|
|
11848
11906
|
"tmux",
|
|
@@ -11856,7 +11914,8 @@ class WebTerminalManager {
|
|
|
11856
11914
|
{ encoding: "utf8", timeout: TMUX_PROBE_TIMEOUT_MS, env: ptyEnv() }
|
|
11857
11915
|
);
|
|
11858
11916
|
if (cap.status !== 0 || typeof cap.stdout !== "string") return { agentState: void 0, claudeConfident: false };
|
|
11859
|
-
|
|
11917
|
+
const observation = classifyAgentPane(cmd.stdout.trim() === "node" ? polledCommand || "node" : cmd.stdout.trim(), cap.stdout);
|
|
11918
|
+
return { ...observation, agentObservedAt: Date.now(), claudeConfident: observation.agentKind === "claude" && isClaudeConfident(cap.stdout) };
|
|
11860
11919
|
} catch {
|
|
11861
11920
|
return { agentState: void 0, claudeConfident: false };
|
|
11862
11921
|
}
|
|
@@ -14189,6 +14248,9 @@ class ApiMachineClient {
|
|
|
14189
14248
|
encTerminalData(dataBase64) {
|
|
14190
14249
|
return persistence.encodeBase64(persistence.encrypt(this.machine.encryptionKey, this.machine.encryptionVariant, dataBase64));
|
|
14191
14250
|
}
|
|
14251
|
+
setCliUpdateRequestHandler(handler) {
|
|
14252
|
+
this.rpcHandlerManager.registerHandler("cli-update-request", (params) => handler(params && typeof params === "object" ? params.version : void 0));
|
|
14253
|
+
}
|
|
14192
14254
|
setCliUpdateRetryHandler(handler) {
|
|
14193
14255
|
this.rpcHandlerManager.registerHandler("cli-update-retry", (params) => handler(params && typeof params === "object" ? params.version : void 0));
|
|
14194
14256
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "very-happy-cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.134",
|
|
4
4
|
"description": "Remote multi-agent workspace CLI with deep Claude integration, native terminal streaming, and self-hosting.",
|
|
5
5
|
"author": "Kirill Dubovitskiy",
|
|
6
6
|
"contributors": [
|
|
@@ -113,12 +113,12 @@
|
|
|
113
113
|
"zod": "^4.0.0"
|
|
114
114
|
},
|
|
115
115
|
"optionalDependencies": {
|
|
116
|
-
"very-happy-tools-arm64-darwin": "0.2.
|
|
117
|
-
"very-happy-tools-x64-darwin": "0.2.
|
|
118
|
-
"very-happy-tools-arm64-linux": "0.2.
|
|
119
|
-
"very-happy-tools-x64-linux": "0.2.
|
|
120
|
-
"very-happy-tools-arm64-win32": "0.2.
|
|
121
|
-
"very-happy-tools-x64-win32": "0.2.
|
|
116
|
+
"very-happy-tools-arm64-darwin": "0.2.134",
|
|
117
|
+
"very-happy-tools-x64-darwin": "0.2.134",
|
|
118
|
+
"very-happy-tools-arm64-linux": "0.2.134",
|
|
119
|
+
"very-happy-tools-x64-linux": "0.2.134",
|
|
120
|
+
"very-happy-tools-arm64-win32": "0.2.134",
|
|
121
|
+
"very-happy-tools-x64-win32": "0.2.134"
|
|
122
122
|
},
|
|
123
123
|
"devDependencies": {
|
|
124
124
|
"@slopus/happy-wire": "workspace:*",
|