very-happy-cli 0.2.110 → 0.2.112
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-D0_wbTs6.cjs → AcpBackend-B3x-gQWP.cjs} +1 -1
- package/dist/{AcpBackend-DzZR7TEv.mjs → AcpBackend-DDEdYVW2.mjs} +1 -1
- package/dist/{AcpSessionManager-tWuFWeIF.mjs → AcpSessionManager-BORAhQ1r.mjs} +1 -1
- package/dist/{AcpSessionManager-Dcj_iHw8.cjs → AcpSessionManager-DGPLD-go.cjs} +1 -1
- package/dist/{config-CTyyVXHj.mjs → config-B_L5vXXu.mjs} +2 -2
- package/dist/{config-CTCGilz_.cjs → config-KX6e7GEu.cjs} +2 -2
- package/dist/{index-D-adqB7g.cjs → index-B7-t5Kwz.cjs} +4 -4
- package/dist/{index-CvnMiYgh.cjs → index-BnRQP3PL.cjs} +250 -20
- package/dist/{index-DW78HyvK.cjs → index-CTd0skWH.cjs} +2 -2
- package/dist/{index-Brhvj7p0.mjs → index-Cu5FdgTq.mjs} +5 -5
- package/dist/{index-BW9kDx5m.mjs → index-inkhalP5.mjs} +245 -17
- package/dist/{index-jYltd-ZW.mjs → index-lQzCAx9I.mjs} +2 -2
- package/dist/index.cjs +2 -2
- package/dist/index.mjs +2 -2
- package/dist/{installTerminalHooks-DhQ6k7YD.cjs → installTerminalHooks-BVk-r0yD.cjs} +2 -2
- package/dist/{installTerminalHooks-CJyoR9jp.mjs → installTerminalHooks-CQiW7Jn9.mjs} +2 -2
- package/dist/lib.cjs +1 -1
- package/dist/lib.d.cts +44 -0
- package/dist/lib.d.mts +44 -0
- package/dist/lib.mjs +1 -1
- package/dist/{mcp-Dr1PegOw.cjs → mcp-D0DIU8Q2.cjs} +2 -2
- package/dist/{mcp-BO9-sxMr.mjs → mcp-DD-0VkiA.mjs} +2 -2
- package/dist/{runGemini-CuzqF6Ys.mjs → runGemini-DHyBDo5P.mjs} +4 -4
- package/dist/{runGemini-D6Wwfk_0.cjs → runGemini-Dq1d8Ccb.cjs} +4 -4
- package/dist/{runOpenClaw-BYmlS-4Q.cjs → runOpenClaw-BHXnUF_m.cjs} +3 -3
- package/dist/{runOpenClaw-B5pkmSTb.mjs → runOpenClaw-DX-tgQ2V.mjs} +3 -3
- package/dist/{send-Btz1IZ9U.mjs → send-0TYDXiGx.mjs} +2 -2
- package/dist/{send-CuajooZC.cjs → send-By6zEUWo.cjs} +2 -2
- package/dist/{sessions-CVUBS3LG.cjs → sessions-DTBRcBRk.cjs} +2 -2
- package/dist/{sessions-DDwCphTQ.mjs → sessions-TM8VaCMY.mjs} +2 -2
- package/dist/{spawn-CwS8CPbr.cjs → spawn-CweTEYze.cjs} +57 -4
- package/dist/{spawn-DcA80_VN.mjs → spawn-D0oTo5BT.mjs} +56 -5
- package/dist/{types-BdO0jg7G.mjs → types-C5ewhu1Q.mjs} +96 -11
- package/dist/{types--t4zLibT.cjs → types-R11tMGyX.cjs} +97 -11
- package/package.json +7 -7
|
@@ -4,10 +4,10 @@ var ink = require('ink');
|
|
|
4
4
|
var React = require('react');
|
|
5
5
|
var node_crypto = require('node:crypto');
|
|
6
6
|
var path = require('node:path');
|
|
7
|
-
var persistence = require('./types
|
|
8
|
-
var index = require('./index-
|
|
9
|
-
var AcpBackend = require('./AcpBackend-
|
|
10
|
-
var config = require('./config-
|
|
7
|
+
var persistence = require('./types-R11tMGyX.cjs');
|
|
8
|
+
var index = require('./index-BnRQP3PL.cjs');
|
|
9
|
+
var AcpBackend = require('./AcpBackend-B3x-gQWP.cjs');
|
|
10
|
+
var config = require('./config-KX6e7GEu.cjs');
|
|
11
11
|
require('axios');
|
|
12
12
|
require('chalk');
|
|
13
13
|
require('node:util');
|
|
@@ -5,9 +5,9 @@ var node_child_process = require('node:child_process');
|
|
|
5
5
|
var os = require('node:os');
|
|
6
6
|
var node_fs = require('node:fs');
|
|
7
7
|
var path = require('node:path');
|
|
8
|
-
var persistence = require('./types
|
|
9
|
-
var AcpSessionManager = require('./AcpSessionManager-
|
|
10
|
-
var index = require('./index-
|
|
8
|
+
var persistence = require('./types-R11tMGyX.cjs');
|
|
9
|
+
var AcpSessionManager = require('./AcpSessionManager-DGPLD-go.cjs');
|
|
10
|
+
var index = require('./index-BnRQP3PL.cjs');
|
|
11
11
|
var WebSocket = require('ws');
|
|
12
12
|
var ed = require('@noble/ed25519');
|
|
13
13
|
var sha2_js = require('@noble/hashes/sha2.js');
|
|
@@ -3,9 +3,9 @@ import { execFileSync } from 'node:child_process';
|
|
|
3
3
|
import os from 'node:os';
|
|
4
4
|
import { existsSync, readFileSync } from 'node:fs';
|
|
5
5
|
import { join } from 'node:path';
|
|
6
|
-
import { m as ensurePrivateDirectorySync, n as hardenPrivateFileSync, w as writePrivateFileSync, e as errorLogMetadata, d as contentLogMetadata, h as connectionState, A as ApiClient, j as readSettings, k as encodeBase64, l as logger, c as configuration } from './types-
|
|
7
|
-
import { A as AcpSessionManager } from './AcpSessionManager-
|
|
8
|
-
import {
|
|
6
|
+
import { m as ensurePrivateDirectorySync, n as hardenPrivateFileSync, w as writePrivateFileSync, e as errorLogMetadata, d as contentLogMetadata, h as connectionState, A as ApiClient, j as readSettings, k as encodeBase64, l as logger, c as configuration } from './types-C5ewhu1Q.mjs';
|
|
7
|
+
import { A as AcpSessionManager } from './AcpSessionManager-BORAhQ1r.mjs';
|
|
8
|
+
import { h as initialMachineMetadata, j as createSessionMetadata, k as setupOfflineReconnection, n as notifyDaemonSessionStarted, m as MessageQueue2, v as registerKillSessionHandler } from './index-inkhalP5.mjs';
|
|
9
9
|
import WebSocket from 'ws';
|
|
10
10
|
import * as ed from '@noble/ed25519';
|
|
11
11
|
import { sha512 } from '@noble/hashes/sha2.js';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { readFileSync } from 'node:fs';
|
|
3
3
|
import { resolve } from 'node:path';
|
|
4
|
-
import { r as readPersistedSessions, c as configuration } from './types-
|
|
5
|
-
import {
|
|
4
|
+
import { r as readPersistedSessions, c as configuration } from './types-C5ewhu1Q.mjs';
|
|
5
|
+
import { d as sendUserMessage, a as sessionWebUrl } from './index-inkhalP5.mjs';
|
|
6
6
|
import 'axios';
|
|
7
7
|
import 'node:util';
|
|
8
8
|
import 'node:os';
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
var chalk = require('chalk');
|
|
4
4
|
var node_fs = require('node:fs');
|
|
5
5
|
var path = require('node:path');
|
|
6
|
-
var persistence = require('./types
|
|
7
|
-
var index = require('./index-
|
|
6
|
+
var persistence = require('./types-R11tMGyX.cjs');
|
|
7
|
+
var index = require('./index-BnRQP3PL.cjs');
|
|
8
8
|
require('axios');
|
|
9
9
|
require('node:util');
|
|
10
10
|
require('node:os');
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var chalk = require('chalk');
|
|
4
|
-
var index = require('./index-
|
|
5
|
-
var persistence = require('./types
|
|
4
|
+
var index = require('./index-BnRQP3PL.cjs');
|
|
5
|
+
var persistence = require('./types-R11tMGyX.cjs');
|
|
6
6
|
require('node:os');
|
|
7
7
|
require('node:crypto');
|
|
8
8
|
require('cross-spawn');
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
import { D as DEFAULT_RECENT_LIMIT, M as MAX_READ_LIMIT, l as listSessions, r as readSessionTranscript,
|
|
3
|
-
import { i as isValidSessionId } from './types-
|
|
2
|
+
import { D as DEFAULT_RECENT_LIMIT, M as MAX_READ_LIMIT, l as listSessions, r as readSessionTranscript, e as stopSession, f as archiveSession } from './index-inkhalP5.mjs';
|
|
3
|
+
import { i as isValidSessionId } from './types-C5ewhu1Q.mjs';
|
|
4
4
|
import 'node:os';
|
|
5
5
|
import 'node:crypto';
|
|
6
6
|
import 'cross-spawn';
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
var chalk = require('chalk');
|
|
4
4
|
var node_fs = require('node:fs');
|
|
5
5
|
var path = require('node:path');
|
|
6
|
-
var index = require('./index-
|
|
7
|
-
var persistence = require('./types
|
|
6
|
+
var index = require('./index-BnRQP3PL.cjs');
|
|
7
|
+
var persistence = require('./types-R11tMGyX.cjs');
|
|
8
8
|
require('node:os');
|
|
9
9
|
require('node:crypto');
|
|
10
10
|
require('cross-spawn');
|
|
@@ -48,6 +48,14 @@ require('node:util');
|
|
|
48
48
|
require('url');
|
|
49
49
|
require('node-pty');
|
|
50
50
|
|
|
51
|
+
const SPAWN_AGENTS = ["claude", "codex", "gemini", "openclaw"];
|
|
52
|
+
function parseEnvAssignment(raw) {
|
|
53
|
+
const eq = raw.indexOf("=");
|
|
54
|
+
if (eq <= 0) throw new Error(`--env expects KEY=VALUE, got: ${raw}`);
|
|
55
|
+
const key = raw.slice(0, eq);
|
|
56
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) throw new Error(`--env key is not a valid variable name: ${key}`);
|
|
57
|
+
return [key, raw.slice(eq + 1)];
|
|
58
|
+
}
|
|
51
59
|
function parseSpawnArgs(args) {
|
|
52
60
|
const options = { json: false, help: false };
|
|
53
61
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -71,6 +79,25 @@ function parseSpawnArgs(args) {
|
|
|
71
79
|
throw new Error("--spawned-by must be 1-24 chars of [a-z0-9] plus - or _, starting with a letter or digit");
|
|
72
80
|
}
|
|
73
81
|
options.spawnedBy = value;
|
|
82
|
+
} else if (arg === "--permission-mode") {
|
|
83
|
+
const value = args[++i];
|
|
84
|
+
if (value === void 0) throw new Error("--permission-mode requires a value");
|
|
85
|
+
if (index.sanitizeSpawnPermissionMode(value) === null) {
|
|
86
|
+
throw new Error(`--permission-mode must be one of: ${index.ALLOWED_SPAWN_PERMISSION_MODES.join(", ")}`);
|
|
87
|
+
}
|
|
88
|
+
options.permissionMode = value;
|
|
89
|
+
} else if (arg === "--agent") {
|
|
90
|
+
const value = args[++i];
|
|
91
|
+
if (value === void 0) throw new Error("--agent requires a value");
|
|
92
|
+
if (!SPAWN_AGENTS.includes(value)) {
|
|
93
|
+
throw new Error(`--agent must be one of: ${SPAWN_AGENTS.join(", ")}`);
|
|
94
|
+
}
|
|
95
|
+
options.agent = value;
|
|
96
|
+
} else if (arg === "--env") {
|
|
97
|
+
const value = args[++i];
|
|
98
|
+
if (value === void 0) throw new Error("--env requires a value");
|
|
99
|
+
const [key, val] = parseEnvAssignment(value);
|
|
100
|
+
options.env = { ...options.env ?? {}, [key]: val };
|
|
74
101
|
} else if (arg === "--json") {
|
|
75
102
|
options.json = true;
|
|
76
103
|
} else if (arg === "--help" || arg === "-h") {
|
|
@@ -89,7 +116,9 @@ function printHelp() {
|
|
|
89
116
|
${chalk.bold("very-happy spawn")} - Spawn a remote session via the local daemon (for automation)
|
|
90
117
|
|
|
91
118
|
${chalk.bold("Usage:")}
|
|
92
|
-
very-happy spawn --dir <path> [--prompt <text> | --prompt-file <file>]
|
|
119
|
+
very-happy spawn --dir <path> [--prompt <text> | --prompt-file <file>]
|
|
120
|
+
[--spawned-by <name>] [--permission-mode <mode>]
|
|
121
|
+
[--agent <name>] [--env KEY=VALUE]... [--json]
|
|
93
122
|
|
|
94
123
|
${chalk.bold("Options:")}
|
|
95
124
|
--dir, -d <path> Working directory for the new session (required)
|
|
@@ -98,6 +127,17 @@ ${chalk.bold("Options:")}
|
|
|
98
127
|
--spawned-by <name> Tag the new session with its origin (e.g. tanka).
|
|
99
128
|
1-24 chars: [a-z0-9] plus - or _. Shows as a chip in
|
|
100
129
|
the web list and is searchable as #<name>.
|
|
130
|
+
--permission-mode <m> Permission mode for the new session: default,
|
|
131
|
+
acceptEdits, plan, yolo, bypassPermissions.
|
|
132
|
+
WITHOUT this the session runs in 'default' and stops
|
|
133
|
+
at the first un-allowlisted tool waiting for someone
|
|
134
|
+
to approve \u2014 if nothing is watching, it just hangs.
|
|
135
|
+
Unattended dispatchers want bypassPermissions.
|
|
136
|
+
--agent <name> Backend to run: claude (default), codex, gemini,
|
|
137
|
+
openclaw.
|
|
138
|
+
--env KEY=VALUE Extra environment for the session process. Repeatable.
|
|
139
|
+
\${VAR} is expanded against the daemon's environment;
|
|
140
|
+
an unresolved reference fails the spawn.
|
|
101
141
|
--json Machine-readable output: {"sessionId", "url"}
|
|
102
142
|
-h, --help Show this help
|
|
103
143
|
|
|
@@ -163,7 +203,12 @@ async function handleSpawnCommand(args) {
|
|
|
163
203
|
process.exit(1);
|
|
164
204
|
}
|
|
165
205
|
persistence.logger.debug(`[SPAWN CMD] Spawning session in ${directory}`);
|
|
166
|
-
const result = await index.spawnDaemonSession(directory, void 0, {
|
|
206
|
+
const result = await index.spawnDaemonSession(directory, void 0, {
|
|
207
|
+
spawnedBy: options.spawnedBy,
|
|
208
|
+
permissionMode: options.permissionMode,
|
|
209
|
+
agent: options.agent,
|
|
210
|
+
environmentVariables: options.env
|
|
211
|
+
});
|
|
167
212
|
if (result?.error || !result?.success || !result?.sessionId) {
|
|
168
213
|
const message = result?.error || "Daemon returned no session ID";
|
|
169
214
|
console.error(chalk.red("Error:"), `Failed to spawn session: ${message}`);
|
|
@@ -184,6 +229,12 @@ async function handleSpawnCommand(args) {
|
|
|
184
229
|
if (options.spawnedBy !== void 0) {
|
|
185
230
|
payload.spawnedBy = options.spawnedBy;
|
|
186
231
|
}
|
|
232
|
+
if (options.permissionMode !== void 0) {
|
|
233
|
+
payload.permissionMode = options.permissionMode;
|
|
234
|
+
}
|
|
235
|
+
if (options.agent !== void 0) {
|
|
236
|
+
payload.agent = options.agent;
|
|
237
|
+
}
|
|
187
238
|
if (prompt !== void 0) {
|
|
188
239
|
payload.promptDelivered = promptError === null;
|
|
189
240
|
}
|
|
@@ -207,5 +258,7 @@ async function handleSpawnCommand(args) {
|
|
|
207
258
|
}
|
|
208
259
|
|
|
209
260
|
exports.sessionWebUrl = index.sessionWebUrl;
|
|
261
|
+
exports.SPAWN_AGENTS = SPAWN_AGENTS;
|
|
210
262
|
exports.handleSpawnCommand = handleSpawnCommand;
|
|
263
|
+
exports.parseEnvAssignment = parseEnvAssignment;
|
|
211
264
|
exports.parseSpawnArgs = parseSpawnArgs;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { readFileSync, statSync } from 'node:fs';
|
|
3
3
|
import { resolve } from 'node:path';
|
|
4
|
-
import { c as checkIfDaemonRunningAndCleanupStaleState, s as spawnDaemonSession, a as sessionWebUrl, i as isValidSpawnOrigin, w as waitForSessionKey,
|
|
5
|
-
import { l as logger } from './types-
|
|
4
|
+
import { c as checkIfDaemonRunningAndCleanupStaleState, s as spawnDaemonSession, a as sessionWebUrl, i as isValidSpawnOrigin, b as sanitizeSpawnPermissionMode, A as ALLOWED_SPAWN_PERMISSION_MODES, w as waitForSessionKey, d as sendUserMessage } from './index-inkhalP5.mjs';
|
|
5
|
+
import { l as logger } from './types-C5ewhu1Q.mjs';
|
|
6
6
|
import 'node:os';
|
|
7
7
|
import 'node:crypto';
|
|
8
8
|
import 'cross-spawn';
|
|
@@ -46,6 +46,14 @@ import 'node:util';
|
|
|
46
46
|
import 'url';
|
|
47
47
|
import 'node-pty';
|
|
48
48
|
|
|
49
|
+
const SPAWN_AGENTS = ["claude", "codex", "gemini", "openclaw"];
|
|
50
|
+
function parseEnvAssignment(raw) {
|
|
51
|
+
const eq = raw.indexOf("=");
|
|
52
|
+
if (eq <= 0) throw new Error(`--env expects KEY=VALUE, got: ${raw}`);
|
|
53
|
+
const key = raw.slice(0, eq);
|
|
54
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) throw new Error(`--env key is not a valid variable name: ${key}`);
|
|
55
|
+
return [key, raw.slice(eq + 1)];
|
|
56
|
+
}
|
|
49
57
|
function parseSpawnArgs(args) {
|
|
50
58
|
const options = { json: false, help: false };
|
|
51
59
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -69,6 +77,25 @@ function parseSpawnArgs(args) {
|
|
|
69
77
|
throw new Error("--spawned-by must be 1-24 chars of [a-z0-9] plus - or _, starting with a letter or digit");
|
|
70
78
|
}
|
|
71
79
|
options.spawnedBy = value;
|
|
80
|
+
} else if (arg === "--permission-mode") {
|
|
81
|
+
const value = args[++i];
|
|
82
|
+
if (value === void 0) throw new Error("--permission-mode requires a value");
|
|
83
|
+
if (sanitizeSpawnPermissionMode(value) === null) {
|
|
84
|
+
throw new Error(`--permission-mode must be one of: ${ALLOWED_SPAWN_PERMISSION_MODES.join(", ")}`);
|
|
85
|
+
}
|
|
86
|
+
options.permissionMode = value;
|
|
87
|
+
} else if (arg === "--agent") {
|
|
88
|
+
const value = args[++i];
|
|
89
|
+
if (value === void 0) throw new Error("--agent requires a value");
|
|
90
|
+
if (!SPAWN_AGENTS.includes(value)) {
|
|
91
|
+
throw new Error(`--agent must be one of: ${SPAWN_AGENTS.join(", ")}`);
|
|
92
|
+
}
|
|
93
|
+
options.agent = value;
|
|
94
|
+
} else if (arg === "--env") {
|
|
95
|
+
const value = args[++i];
|
|
96
|
+
if (value === void 0) throw new Error("--env requires a value");
|
|
97
|
+
const [key, val] = parseEnvAssignment(value);
|
|
98
|
+
options.env = { ...options.env ?? {}, [key]: val };
|
|
72
99
|
} else if (arg === "--json") {
|
|
73
100
|
options.json = true;
|
|
74
101
|
} else if (arg === "--help" || arg === "-h") {
|
|
@@ -87,7 +114,9 @@ function printHelp() {
|
|
|
87
114
|
${chalk.bold("very-happy spawn")} - Spawn a remote session via the local daemon (for automation)
|
|
88
115
|
|
|
89
116
|
${chalk.bold("Usage:")}
|
|
90
|
-
very-happy spawn --dir <path> [--prompt <text> | --prompt-file <file>]
|
|
117
|
+
very-happy spawn --dir <path> [--prompt <text> | --prompt-file <file>]
|
|
118
|
+
[--spawned-by <name>] [--permission-mode <mode>]
|
|
119
|
+
[--agent <name>] [--env KEY=VALUE]... [--json]
|
|
91
120
|
|
|
92
121
|
${chalk.bold("Options:")}
|
|
93
122
|
--dir, -d <path> Working directory for the new session (required)
|
|
@@ -96,6 +125,17 @@ ${chalk.bold("Options:")}
|
|
|
96
125
|
--spawned-by <name> Tag the new session with its origin (e.g. tanka).
|
|
97
126
|
1-24 chars: [a-z0-9] plus - or _. Shows as a chip in
|
|
98
127
|
the web list and is searchable as #<name>.
|
|
128
|
+
--permission-mode <m> Permission mode for the new session: default,
|
|
129
|
+
acceptEdits, plan, yolo, bypassPermissions.
|
|
130
|
+
WITHOUT this the session runs in 'default' and stops
|
|
131
|
+
at the first un-allowlisted tool waiting for someone
|
|
132
|
+
to approve \u2014 if nothing is watching, it just hangs.
|
|
133
|
+
Unattended dispatchers want bypassPermissions.
|
|
134
|
+
--agent <name> Backend to run: claude (default), codex, gemini,
|
|
135
|
+
openclaw.
|
|
136
|
+
--env KEY=VALUE Extra environment for the session process. Repeatable.
|
|
137
|
+
\${VAR} is expanded against the daemon's environment;
|
|
138
|
+
an unresolved reference fails the spawn.
|
|
99
139
|
--json Machine-readable output: {"sessionId", "url"}
|
|
100
140
|
-h, --help Show this help
|
|
101
141
|
|
|
@@ -161,7 +201,12 @@ async function handleSpawnCommand(args) {
|
|
|
161
201
|
process.exit(1);
|
|
162
202
|
}
|
|
163
203
|
logger.debug(`[SPAWN CMD] Spawning session in ${directory}`);
|
|
164
|
-
const result = await spawnDaemonSession(directory, void 0, {
|
|
204
|
+
const result = await spawnDaemonSession(directory, void 0, {
|
|
205
|
+
spawnedBy: options.spawnedBy,
|
|
206
|
+
permissionMode: options.permissionMode,
|
|
207
|
+
agent: options.agent,
|
|
208
|
+
environmentVariables: options.env
|
|
209
|
+
});
|
|
165
210
|
if (result?.error || !result?.success || !result?.sessionId) {
|
|
166
211
|
const message = result?.error || "Daemon returned no session ID";
|
|
167
212
|
console.error(chalk.red("Error:"), `Failed to spawn session: ${message}`);
|
|
@@ -182,6 +227,12 @@ async function handleSpawnCommand(args) {
|
|
|
182
227
|
if (options.spawnedBy !== void 0) {
|
|
183
228
|
payload.spawnedBy = options.spawnedBy;
|
|
184
229
|
}
|
|
230
|
+
if (options.permissionMode !== void 0) {
|
|
231
|
+
payload.permissionMode = options.permissionMode;
|
|
232
|
+
}
|
|
233
|
+
if (options.agent !== void 0) {
|
|
234
|
+
payload.agent = options.agent;
|
|
235
|
+
}
|
|
185
236
|
if (prompt !== void 0) {
|
|
186
237
|
payload.promptDelivered = promptError === null;
|
|
187
238
|
}
|
|
@@ -204,4 +255,4 @@ async function handleSpawnCommand(args) {
|
|
|
204
255
|
process.exit(0);
|
|
205
256
|
}
|
|
206
257
|
|
|
207
|
-
export { handleSpawnCommand, parseSpawnArgs, sessionWebUrl };
|
|
258
|
+
export { SPAWN_AGENTS, handleSpawnCommand, parseEnvAssignment, parseSpawnArgs, sessionWebUrl };
|
|
@@ -29,7 +29,7 @@ import { SandboxManager } from '@anthropic-ai/sandbox-runtime';
|
|
|
29
29
|
import { Expo } from 'expo-server-sdk';
|
|
30
30
|
|
|
31
31
|
var name = "very-happy-cli";
|
|
32
|
-
var version = "0.2.
|
|
32
|
+
var version = "0.2.112";
|
|
33
33
|
var description = "Remote multi-agent workspace CLI with deep Claude integration, native terminal streaming, and self-hosting.";
|
|
34
34
|
var author = "Kirill Dubovitskiy";
|
|
35
35
|
var contributors = [
|
|
@@ -142,12 +142,12 @@ var dependencies = {
|
|
|
142
142
|
zod: "^4.0.0"
|
|
143
143
|
};
|
|
144
144
|
var optionalDependencies = {
|
|
145
|
-
"very-happy-tools-arm64-darwin": "0.2.
|
|
146
|
-
"very-happy-tools-x64-darwin": "0.2.
|
|
147
|
-
"very-happy-tools-arm64-linux": "0.2.
|
|
148
|
-
"very-happy-tools-x64-linux": "0.2.
|
|
149
|
-
"very-happy-tools-arm64-win32": "0.2.
|
|
150
|
-
"very-happy-tools-x64-win32": "0.2.
|
|
145
|
+
"very-happy-tools-arm64-darwin": "0.2.112",
|
|
146
|
+
"very-happy-tools-x64-darwin": "0.2.112",
|
|
147
|
+
"very-happy-tools-arm64-linux": "0.2.112",
|
|
148
|
+
"very-happy-tools-x64-linux": "0.2.112",
|
|
149
|
+
"very-happy-tools-arm64-win32": "0.2.112",
|
|
150
|
+
"very-happy-tools-x64-win32": "0.2.112"
|
|
151
151
|
};
|
|
152
152
|
var devDependencies = {
|
|
153
153
|
"@slopus/happy-wire": "workspace:*",
|
|
@@ -747,6 +747,14 @@ const sessionEnvelopeSchema = z.object({
|
|
|
747
747
|
// Codex app-server item id for this envelope. Used as the precise
|
|
748
748
|
// rollback point for Codex thread duplicate/fork-from-message.
|
|
749
749
|
codexItemId: z.string().min(1).optional(),
|
|
750
|
+
// B-309: identity of the live draft this envelope supersedes,
|
|
751
|
+
// `"<api message id>:<content block index>"`. Set on text/thinking
|
|
752
|
+
// envelopes mapped from a Claude assistant message so the web can replace
|
|
753
|
+
// the streamed draft with the persisted message in a single frame instead
|
|
754
|
+
// of letting it flicker out and back in. Absent from every other producer
|
|
755
|
+
// (and from CLIs older than the streaming relay) — consumers must treat a
|
|
756
|
+
// missing key as "no draft to claim", never as an error.
|
|
757
|
+
streamKey: z.string().min(1).optional(),
|
|
750
758
|
// Per-API-call token usage of the assistant message this envelope was
|
|
751
759
|
// mapped from (B-108). Envelope-level (not inside `ev`) so every envelope
|
|
752
760
|
// type carries it uniformly — the web feeds it into its context meter.
|
|
@@ -785,6 +793,7 @@ function createEnvelope(role, ev, opts = {}) {
|
|
|
785
793
|
...opts.subagent ? { subagent: opts.subagent } : {},
|
|
786
794
|
...opts.claudeUuid ? { claudeUuid: opts.claudeUuid } : {},
|
|
787
795
|
...opts.codexItemId ? { codexItemId: opts.codexItemId } : {},
|
|
796
|
+
...opts.streamKey ? { streamKey: opts.streamKey } : {},
|
|
788
797
|
...opts.usage ? { usage: opts.usage } : {},
|
|
789
798
|
ev
|
|
790
799
|
});
|
|
@@ -887,6 +896,44 @@ z.object({
|
|
|
887
896
|
body: CoreUpdateBodySchema,
|
|
888
897
|
createdAt: z.number()
|
|
889
898
|
});
|
|
899
|
+
const STREAM_DELTA_MAX_CHARS = 16 * 1024;
|
|
900
|
+
const sessionStreamBlockKindSchema = z.enum(["text", "thinking"]);
|
|
901
|
+
const sessionStreamBlockStartSchema = z.object({
|
|
902
|
+
t: z.literal("block-start"),
|
|
903
|
+
mid: z.string().min(1),
|
|
904
|
+
idx: z.number().int().min(0),
|
|
905
|
+
kind: sessionStreamBlockKindSchema
|
|
906
|
+
});
|
|
907
|
+
const sessionStreamBlockDeltaSchema = z.object({
|
|
908
|
+
t: z.literal("block-delta"),
|
|
909
|
+
mid: z.string().min(1),
|
|
910
|
+
idx: z.number().int().min(0),
|
|
911
|
+
text: z.string().max(STREAM_DELTA_MAX_CHARS)
|
|
912
|
+
});
|
|
913
|
+
const sessionStreamBlockEndSchema = z.object({
|
|
914
|
+
t: z.literal("block-end"),
|
|
915
|
+
mid: z.string().min(1),
|
|
916
|
+
idx: z.number().int().min(0)
|
|
917
|
+
});
|
|
918
|
+
const sessionStreamProgressSchema = z.object({
|
|
919
|
+
t: z.literal("progress"),
|
|
920
|
+
thinkingTokens: z.number().int().min(0).optional(),
|
|
921
|
+
outputTokens: z.number().int().min(0).optional(),
|
|
922
|
+
status: z.enum(["requesting", "compacting"]).optional()
|
|
923
|
+
});
|
|
924
|
+
const sessionStreamTurnEndSchema = z.object({
|
|
925
|
+
t: z.literal("turn-end")
|
|
926
|
+
});
|
|
927
|
+
z.discriminatedUnion("t", [
|
|
928
|
+
sessionStreamBlockStartSchema,
|
|
929
|
+
sessionStreamBlockDeltaSchema,
|
|
930
|
+
sessionStreamBlockEndSchema,
|
|
931
|
+
sessionStreamProgressSchema,
|
|
932
|
+
sessionStreamTurnEndSchema
|
|
933
|
+
]);
|
|
934
|
+
function streamKeyOf(messageId, blockIndex) {
|
|
935
|
+
return `${messageId}:${blockIndex}`;
|
|
936
|
+
}
|
|
890
937
|
const VoiceConversationGrantedSchema = z.object({
|
|
891
938
|
allowed: z.literal(true),
|
|
892
939
|
conversationToken: z.string(),
|
|
@@ -2769,13 +2816,24 @@ function mapClaudeLogMessageToSessionEnvelopesInternal(message, state) {
|
|
|
2769
2816
|
maybeEmitSubagentStart(state, turnId, subagent, envelopes);
|
|
2770
2817
|
const usage = pickAssistantUsage(message);
|
|
2771
2818
|
const blocks = Array.isArray(message.message?.content) ? message.message.content : [];
|
|
2772
|
-
|
|
2819
|
+
const apiMessageId = !isSidechainMessage(message) && typeof message.message?.id === "string" && message.message.id.length > 0 ? message.message.id : void 0;
|
|
2820
|
+
let blockCursor = 0;
|
|
2821
|
+
if (apiMessageId) {
|
|
2822
|
+
if (state.streamBlockMessageId !== apiMessageId) {
|
|
2823
|
+
state.streamBlockMessageId = apiMessageId;
|
|
2824
|
+
state.streamBlockCursor = 0;
|
|
2825
|
+
}
|
|
2826
|
+
blockCursor = state.streamBlockCursor ?? 0;
|
|
2827
|
+
state.streamBlockCursor = blockCursor + blocks.length;
|
|
2828
|
+
}
|
|
2829
|
+
const draftKey = (index) => apiMessageId ? streamKeyOf(apiMessageId, blockCursor + index) : void 0;
|
|
2830
|
+
for (const [blockIndex, block] of blocks.entries()) {
|
|
2773
2831
|
if (block.type === "text" && typeof block.text === "string" && block.text.trim().length > 0) {
|
|
2774
|
-
envelopes.push(createEnvelope("agent", { t: "text", text: block.text }, { turn: turnId, subagent, claudeUuid, usage }));
|
|
2832
|
+
envelopes.push(createEnvelope("agent", { t: "text", text: block.text }, { turn: turnId, subagent, claudeUuid, usage, streamKey: draftKey(blockIndex) }));
|
|
2775
2833
|
continue;
|
|
2776
2834
|
}
|
|
2777
2835
|
if (block.type === "thinking" && typeof block.thinking === "string" && block.thinking.trim().length > 0) {
|
|
2778
|
-
envelopes.push(createEnvelope("agent", { t: "text", text: block.thinking, thinking: true }, { turn: turnId, subagent, claudeUuid, usage }));
|
|
2836
|
+
envelopes.push(createEnvelope("agent", { t: "text", text: block.thinking, thinking: true }, { turn: turnId, subagent, claudeUuid, usage, streamKey: draftKey(blockIndex) }));
|
|
2779
2837
|
continue;
|
|
2780
2838
|
}
|
|
2781
2839
|
if (block.type === "tool_use") {
|
|
@@ -3774,6 +3832,33 @@ class ApiSessionClient extends EventEmitter {
|
|
|
3774
3832
|
mode
|
|
3775
3833
|
});
|
|
3776
3834
|
}
|
|
3835
|
+
/**
|
|
3836
|
+
* B-309: push one live stream frame (thinking/text delta or progress) to
|
|
3837
|
+
* this account's web clients.
|
|
3838
|
+
*
|
|
3839
|
+
* Encrypted with the SESSION key and relayed without being stored —
|
|
3840
|
+
* clipboard-push's shape, for clipboard-push's reasons: the relay must not
|
|
3841
|
+
* be able to read thinking text, and the session id is stamped by the
|
|
3842
|
+
* server from the authenticated connection rather than trusted from here.
|
|
3843
|
+
*
|
|
3844
|
+
* `volatile` on purpose. A draft that misses the wire while disconnected
|
|
3845
|
+
* is worth nothing — the persisted message is still coming, and replaying
|
|
3846
|
+
* stale deltas after a reconnect would paint text that has already landed.
|
|
3847
|
+
*/
|
|
3848
|
+
sendStreamFrame(frame) {
|
|
3849
|
+
if (!this.socket.connected) return;
|
|
3850
|
+
try {
|
|
3851
|
+
this.socket.volatile.emit("session-stream", {
|
|
3852
|
+
// The frame object, NOT a JSON string: `encrypt` serializes
|
|
3853
|
+
// internally, so pre-stringifying would double-encode and
|
|
3854
|
+
// escape every quote in the streamed text.
|
|
3855
|
+
payload: encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, frame)),
|
|
3856
|
+
enc: true
|
|
3857
|
+
});
|
|
3858
|
+
} catch (error) {
|
|
3859
|
+
logger.debug(`[API] Failed to relay stream frame: ${error}`);
|
|
3860
|
+
}
|
|
3861
|
+
}
|
|
3777
3862
|
/**
|
|
3778
3863
|
* Send session death message
|
|
3779
3864
|
*/
|
|
@@ -16302,4 +16387,4 @@ const RawJSONLinesSchema = z$1.discriminatedUnion("type", [
|
|
|
16302
16387
|
}).passthrough()
|
|
16303
16388
|
]);
|
|
16304
16389
|
|
|
16305
|
-
export {
|
|
16390
|
+
export { mirrorLineKey as $, ApiClient as A, credentialRelayProblem as B, deriveLocalCliUpdateSummary as C, updateSettings as D, encodeBase64Url as E, decodeBase64 as F, writeCredentialsLegacy as G, writeCredentialsDataKey as H, InvalidateSync as I, readCredentialsForConfiguredRelay as J, encrypt as K, isClaudeSessionId as L, MAX_CHAT_ATTACHMENT_SOURCE_BYTES as M, getLocalHappyAgentCredentialPath as N, readLocalHappyAgentCredentials as O, decryptWithDataKey as P, decryptLegacy as Q, RawJSONLinesSchema as R, STREAM_DELTA_MAX_CHARS as S, extractCompleteLines as T, decideBackfill as U, parseTerminalHookPayload as V, tmuxArgs as W, scrubTmuxClientEnv as X, decideMirrorBinding as Y, MIRROR_BACKFILL_LINES_DEFAULT as Z, persistSession as _, ApiSessionClient as a, mirrorLocalId as a0, detectResumeSupport as a1, detectCLIAvailability as a2, acquireDaemonLock as a3, summarizeSpawnSessionForLog as a4, deletePersistedSessions as a5, shellescape as a6, decrypt as a7, writeDaemonState as a8, writeSettings as a9, fetchCliUpdateState as aa, resolveCliUpdateCheckInterval as ab, DEFAULT_CLI_UPDATE_CHECK_INTERVAL_MS as ac, releaseDaemonLock as ad, listVhTerminals as ae, isValidTerminalId as af, readVhTerminal as ag, sendToVhTerminal as ah, TITLE_PROMPT_PREFIX as ai, BOARD_ANALYZER_PROMPT_PREFIX as aj, startOfflineReconnection as ak, clearCredentials as al, clearMachineId as am, SandboxConfigSchema as an, CodexAppServerClient as ao, getLatestDaemonLog as ap, persistence as aq, api as ar, createEnvelope as b, configuration as c, contentLogMetadata as d, errorLogMetadata as e, packageJson as f, delay as g, connectionState as h, isValidSessionId as i, readSettings as j, encodeBase64 as k, logger as l, ensurePrivateDirectorySync as m, hardenPrivateFileSync as n, readDaemonState as o, projectPath as p, clearDaemonState as q, readPersistedSessions as r, getProjectPath as s, initializeSandbox as t, wrapCommand as u, AsyncLock as v, writePrivateFileSync as w, ensurePrivateDirectory as x, hardenPrivateFile as y, readCredentials as z };
|