very-happy-cli 0.2.101 → 0.2.103
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-mbMlyZnj.mjs → AcpBackend-C8CgzRI_.mjs} +1 -1
- package/dist/{AcpBackend-BHst4DAF.cjs → AcpBackend-Zt10QM8S.cjs} +1 -1
- package/dist/{AcpSessionManager-CwdvDD_z.mjs → AcpSessionManager-BAinHcHh.mjs} +1 -1
- package/dist/{AcpSessionManager-Nyxm1kCR.cjs → AcpSessionManager-D_iGrEbD.cjs} +1 -1
- package/dist/{config-C1Z39_GK.mjs → config-11py0X-q.mjs} +2 -2
- package/dist/{config-DjtLOWRc.cjs → config-CG4aqDiu.cjs} +2 -2
- package/dist/{index-BnVk_E3V.mjs → index-B9SnF1mG.mjs} +2 -2
- package/dist/{index-tQZwyC3N.cjs → index-B_soRPsA.cjs} +4 -4
- package/dist/{index-BL9LWtAR.mjs → index-BjDfS9DT.mjs} +5 -5
- package/dist/{index-DemE8OgZ.cjs → index-CimI2p1M.cjs} +26 -20
- package/dist/{index-3ssSAknz.mjs → index-Dnma0J5k.mjs} +22 -16
- package/dist/{index-DqA3-Xxl.cjs → index-VvPyRTet.cjs} +2 -2
- package/dist/index.cjs +2 -2
- package/dist/index.mjs +2 -2
- package/dist/{installTerminalHooks-BC9thC5_.cjs → installTerminalHooks-BP9uu1Xn.cjs} +2 -2
- package/dist/{installTerminalHooks-BvYqvibn.mjs → installTerminalHooks-UL5Zfif6.mjs} +2 -2
- package/dist/lib.cjs +1 -1
- package/dist/lib.d.cts +17 -0
- package/dist/lib.d.mts +17 -0
- package/dist/lib.mjs +1 -1
- package/dist/{mcp-D6qSHTLO.mjs → mcp-CxepBrGm.mjs} +2 -2
- package/dist/{mcp-ChjpeHtZ.cjs → mcp-mX6g7PpH.cjs} +2 -2
- package/dist/{runGemini-Dy7X7eDh.cjs → runGemini-BPAUC7TY.cjs} +4 -4
- package/dist/{runGemini-D1SFLnxb.mjs → runGemini-Cxa0Tj_7.mjs} +4 -4
- package/dist/{runOpenClaw-AsjRz-Q3.mjs → runOpenClaw-DMAn3-Hq.mjs} +3 -3
- package/dist/{runOpenClaw-BpyEPSp5.cjs → runOpenClaw-QJ8YzPvo.cjs} +3 -3
- package/dist/{send-CvArl8UY.cjs → send-CwRV7cbV.cjs} +2 -2
- package/dist/{send-D1arw2mU.mjs → send-D6NLQaYt.mjs} +2 -2
- package/dist/{spawn-D3k-ywKX.mjs → spawn-DIW1I72G.mjs} +2 -2
- package/dist/{spawn-BYby4e76.cjs → spawn-Db22DkVT.cjs} +2 -2
- package/dist/{types-CkxTp9iI.mjs → types-D9ORBxOD.mjs} +335 -110
- package/dist/{types-BEwKkfnX.cjs → types-DKO60j29.cjs} +337 -110
- package/package.json +7 -7
|
@@ -51,7 +51,7 @@ var z__namespace = /*#__PURE__*/_interopNamespaceDefault(z);
|
|
|
51
51
|
var pty__namespace = /*#__PURE__*/_interopNamespaceDefault(pty);
|
|
52
52
|
|
|
53
53
|
var name = "very-happy-cli";
|
|
54
|
-
var version = "0.2.
|
|
54
|
+
var version = "0.2.103";
|
|
55
55
|
var description = "Remote multi-agent workspace CLI with deep Claude integration, native terminal streaming, and self-hosting.";
|
|
56
56
|
var author = "Kirill Dubovitskiy";
|
|
57
57
|
var contributors = [
|
|
@@ -164,12 +164,12 @@ var dependencies = {
|
|
|
164
164
|
zod: "^4.0.0"
|
|
165
165
|
};
|
|
166
166
|
var optionalDependencies = {
|
|
167
|
-
"very-happy-tools-arm64-darwin": "0.2.
|
|
168
|
-
"very-happy-tools-x64-darwin": "0.2.
|
|
169
|
-
"very-happy-tools-arm64-linux": "0.2.
|
|
170
|
-
"very-happy-tools-x64-linux": "0.2.
|
|
171
|
-
"very-happy-tools-arm64-win32": "0.2.
|
|
172
|
-
"very-happy-tools-x64-win32": "0.2.
|
|
167
|
+
"very-happy-tools-arm64-darwin": "0.2.103",
|
|
168
|
+
"very-happy-tools-x64-darwin": "0.2.103",
|
|
169
|
+
"very-happy-tools-arm64-linux": "0.2.103",
|
|
170
|
+
"very-happy-tools-x64-linux": "0.2.103",
|
|
171
|
+
"very-happy-tools-arm64-win32": "0.2.103",
|
|
172
|
+
"very-happy-tools-x64-win32": "0.2.103"
|
|
173
173
|
};
|
|
174
174
|
var devDependencies = {
|
|
175
175
|
"@slopus/happy-wire": "workspace:*",
|
|
@@ -1127,6 +1127,15 @@ z.z.object({
|
|
|
1127
1127
|
/** B-282: `kill-terminal` honours `alsoAttached`. */
|
|
1128
1128
|
killAttached: z.z.boolean().optional()
|
|
1129
1129
|
}).optional(),
|
|
1130
|
+
/**
|
|
1131
|
+
* B-290 capability flag: this daemon answers `claude-list-history` (import a
|
|
1132
|
+
* Claude Code conversation that was never started through very-happy).
|
|
1133
|
+
* Same trust rule (`detectedAt >= startedAt`).
|
|
1134
|
+
*/
|
|
1135
|
+
claudeHistory: z.z.object({
|
|
1136
|
+
rpcAvailable: z.z.boolean(),
|
|
1137
|
+
detectedAt: z.z.number()
|
|
1138
|
+
}).optional(),
|
|
1130
1139
|
/** Relay-owned CLI compatibility/update policy last checked by this daemon. */
|
|
1131
1140
|
cliUpdate: CliUpdateStateSchema.optional(),
|
|
1132
1141
|
/**
|
|
@@ -1585,7 +1594,7 @@ class RpcHandlerManager {
|
|
|
1585
1594
|
}
|
|
1586
1595
|
}
|
|
1587
1596
|
|
|
1588
|
-
const __dirname$1 = path$1.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('types-
|
|
1597
|
+
const __dirname$1 = path$1.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('types-DKO60j29.cjs', document.baseURI).href))));
|
|
1589
1598
|
function projectPath() {
|
|
1590
1599
|
const path = path$1.resolve(__dirname$1, "..");
|
|
1591
1600
|
return path;
|
|
@@ -2116,102 +2125,66 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory) {
|
|
|
2116
2125
|
}
|
|
2117
2126
|
|
|
2118
2127
|
const PRICING = {
|
|
2119
|
-
// --- Claude
|
|
2120
|
-
"claude-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
},
|
|
2126
|
-
"claude-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
},
|
|
2132
|
-
"claude-4-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
},
|
|
2138
|
-
"claude-4.5-sonnet": {
|
|
2139
|
-
input: 3,
|
|
2140
|
-
output: 15,
|
|
2141
|
-
cache_write: 3.75,
|
|
2142
|
-
cache_read: 0.3
|
|
2143
|
-
},
|
|
2144
|
-
"claude-4-sonnet": {
|
|
2145
|
-
input: 3,
|
|
2146
|
-
output: 15,
|
|
2147
|
-
cache_write: 3.75,
|
|
2148
|
-
cache_read: 0.3
|
|
2149
|
-
},
|
|
2150
|
-
"claude-4.5-haiku": {
|
|
2151
|
-
input: 1,
|
|
2152
|
-
output: 5,
|
|
2153
|
-
cache_write: 1.25,
|
|
2154
|
-
cache_read: 0.1
|
|
2155
|
-
},
|
|
2128
|
+
// --- Claude Fable / Mythos (same tier, same price) ---
|
|
2129
|
+
"claude-fable-5-1": { input: 10, output: 50, cache_write: 12.5, cache_read: 0.25 },
|
|
2130
|
+
"claude-fable-5": { input: 10, output: 50, cache_write: 12.5, cache_read: 1 },
|
|
2131
|
+
"claude-mythos-5-1": { input: 10, output: 50, cache_write: 12.5, cache_read: 0.25 },
|
|
2132
|
+
"claude-mythos-5": { input: 10, output: 50, cache_write: 12.5, cache_read: 1 },
|
|
2133
|
+
// --- Claude 5 ---
|
|
2134
|
+
"claude-opus-5": { input: 5, output: 25, cache_write: 6.25, cache_read: 0.5 },
|
|
2135
|
+
"claude-sonnet-5": { input: 2, output: 10, cache_write: 2.5, cache_read: 0.2 },
|
|
2136
|
+
// --- Claude 4.x ---
|
|
2137
|
+
"claude-opus-4-8": { input: 5, output: 25, cache_write: 6.25, cache_read: 0.5 },
|
|
2138
|
+
"claude-opus-4-7": { input: 5, output: 25, cache_write: 6.25, cache_read: 0.5 },
|
|
2139
|
+
"claude-opus-4-6": { input: 5, output: 25, cache_write: 6.25, cache_read: 0.5 },
|
|
2140
|
+
"claude-opus-4-5": { input: 5, output: 25, cache_write: 6.25, cache_read: 0.5 },
|
|
2141
|
+
"claude-opus-4-1": { input: 15, output: 75, cache_write: 18.75, cache_read: 1.5 },
|
|
2142
|
+
"claude-opus-4": { input: 15, output: 75, cache_write: 18.75, cache_read: 1.5 },
|
|
2143
|
+
"claude-sonnet-4-6": { input: 3, output: 15, cache_write: 3.75, cache_read: 0.3 },
|
|
2144
|
+
"claude-sonnet-4-5": { input: 3, output: 15, cache_write: 3.75, cache_read: 0.3 },
|
|
2145
|
+
"claude-sonnet-4": { input: 3, output: 15, cache_write: 3.75, cache_read: 0.3 },
|
|
2146
|
+
"claude-haiku-4-5": { input: 1, output: 5, cache_write: 1.25, cache_read: 0.1 },
|
|
2156
2147
|
// --- Legacy / Claude 3 ---
|
|
2157
|
-
"claude-3-opus-20240229": {
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
},
|
|
2163
|
-
"claude-3-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
"claude-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
cache_read: 0.3
|
|
2174
|
-
},
|
|
2175
|
-
// New Sonnet 3.5 updated model
|
|
2176
|
-
"claude-3-5-sonnet-20241022": {
|
|
2177
|
-
input: 3,
|
|
2178
|
-
output: 15,
|
|
2179
|
-
cache_write: 3.75,
|
|
2180
|
-
cache_read: 0.3
|
|
2181
|
-
},
|
|
2182
|
-
"claude-3-haiku-20240307": {
|
|
2183
|
-
input: 0.25,
|
|
2184
|
-
output: 1.25,
|
|
2185
|
-
cache_write: 0.3125,
|
|
2186
|
-
cache_read: 0.025
|
|
2187
|
-
},
|
|
2188
|
-
"claude-3-5-haiku-20241022": {
|
|
2189
|
-
input: 0.8,
|
|
2190
|
-
output: 4,
|
|
2191
|
-
cache_write: 1,
|
|
2192
|
-
// Approx based on 1.25x rule usually or custom
|
|
2193
|
-
cache_read: 0.08
|
|
2194
|
-
}
|
|
2148
|
+
"claude-3-opus-20240229": { input: 15, output: 75, cache_write: 18.75, cache_read: 1.5 },
|
|
2149
|
+
"claude-3-sonnet-20240229": { input: 3, output: 15, cache_write: 3.75, cache_read: 0.3 },
|
|
2150
|
+
"claude-3-5-sonnet-20240620": { input: 3, output: 15, cache_write: 3.75, cache_read: 0.3 },
|
|
2151
|
+
"claude-3-5-sonnet-20241022": { input: 3, output: 15, cache_write: 3.75, cache_read: 0.3 },
|
|
2152
|
+
"claude-3-7-sonnet-20250219": { input: 3, output: 15, cache_write: 3.75, cache_read: 0.3 },
|
|
2153
|
+
"claude-3-haiku-20240307": { input: 0.25, output: 1.25, cache_write: 0.3125, cache_read: 0.025 },
|
|
2154
|
+
"claude-3-5-haiku-20241022": { input: 0.8, output: 4, cache_write: 1, cache_read: 0.08 }
|
|
2155
|
+
};
|
|
2156
|
+
const DEFAULT_MODEL = "claude-opus-5";
|
|
2157
|
+
const FAMILY_DEFAULTS = {
|
|
2158
|
+
fable: "claude-fable-5-1",
|
|
2159
|
+
// Project Glasswing counterpart of Fable: same tier, same published rates.
|
|
2160
|
+
mythos: "claude-mythos-5-1",
|
|
2161
|
+
opus: "claude-opus-5",
|
|
2162
|
+
sonnet: "claude-sonnet-5",
|
|
2163
|
+
haiku: "claude-haiku-4-5"
|
|
2195
2164
|
};
|
|
2196
|
-
|
|
2165
|
+
function resolvePricingKey(modelId) {
|
|
2166
|
+
if (!modelId) return DEFAULT_MODEL;
|
|
2167
|
+
const raw = modelId.trim().toLowerCase();
|
|
2168
|
+
if (raw in PRICING) return raw;
|
|
2169
|
+
const id = raw.replace(/\[1m\]$/, "");
|
|
2170
|
+
if (id in PRICING) return id;
|
|
2171
|
+
const family = ["fable", "mythos", "opus", "sonnet", "haiku"].find((f) => id.includes(f));
|
|
2172
|
+
if (!family) return DEFAULT_MODEL;
|
|
2173
|
+
const version = id.match(/(?:^|[^0-9])(\d)(?:[-._](\d))?(?:[^0-9]|$)/);
|
|
2174
|
+
const major = version ? version[1] : null;
|
|
2175
|
+
const minor = version && version[2] ? version[2] : null;
|
|
2176
|
+
const key = major ? `claude-${family}-${major}${minor ? `-${minor}` : ""}` : null;
|
|
2177
|
+
if (key && key in PRICING) return key;
|
|
2178
|
+
if (major === "3" && family !== "fable" && family !== "mythos") {
|
|
2179
|
+
if (family === "opus") return "claude-3-opus-20240229";
|
|
2180
|
+
if (family === "haiku") return minor === "5" ? "claude-3-5-haiku-20241022" : "claude-3-haiku-20240307";
|
|
2181
|
+
if (minor === "7") return "claude-3-7-sonnet-20250219";
|
|
2182
|
+
return minor === "5" ? "claude-3-5-sonnet-20241022" : "claude-3-sonnet-20240229";
|
|
2183
|
+
}
|
|
2184
|
+
return FAMILY_DEFAULTS[family];
|
|
2185
|
+
}
|
|
2197
2186
|
function calculateCost(usage, modelId) {
|
|
2198
|
-
|
|
2199
|
-
if (!pricing) {
|
|
2200
|
-
if (modelId?.includes("opus")) {
|
|
2201
|
-
if (modelId.includes("4.5")) pricing = PRICING["claude-4.5-opus"];
|
|
2202
|
-
else if (modelId.includes("4.1")) pricing = PRICING["claude-4.1-opus"];
|
|
2203
|
-
else if (modelId.includes("4")) pricing = PRICING["claude-4-opus"];
|
|
2204
|
-
else pricing = PRICING["claude-3-opus-20240229"];
|
|
2205
|
-
} else if (modelId?.includes("sonnet")) {
|
|
2206
|
-
if (modelId.includes("4.5")) pricing = PRICING["claude-4.5-sonnet"];
|
|
2207
|
-
else if (modelId.includes("4")) pricing = PRICING["claude-4-sonnet"];
|
|
2208
|
-
else pricing = PRICING["claude-3-5-sonnet-20241022"];
|
|
2209
|
-
} else if (modelId?.includes("haiku")) {
|
|
2210
|
-
if (modelId.includes("4.5")) pricing = PRICING["claude-4.5-haiku"];
|
|
2211
|
-
else if (modelId.includes("3.5")) pricing = PRICING["claude-3-5-haiku-20241022"];
|
|
2212
|
-
else pricing = PRICING["claude-3-haiku-20240307"];
|
|
2213
|
-
} else pricing = PRICING[DEFAULT_MODEL];
|
|
2214
|
-
}
|
|
2187
|
+
const pricing = PRICING[resolvePricingKey(modelId)];
|
|
2215
2188
|
const inputCost = usage.input_tokens / 1e6 * pricing.input;
|
|
2216
2189
|
const outputCost = usage.output_tokens / 1e6 * pricing.output;
|
|
2217
2190
|
const cacheWriteCost = (usage.cache_creation_input_tokens || 0) / 1e6 * pricing.cache_write;
|
|
@@ -4056,6 +4029,7 @@ function summarizeSpawnSessionForLog(value) {
|
|
|
4056
4029
|
environmentVariableNames: Object.keys(environmentVariables).sort(),
|
|
4057
4030
|
resumesClaude: typeof input.resumeClaudeSessionId === "string" && input.resumeClaudeSessionId.length > 0,
|
|
4058
4031
|
resumesCodex: typeof input.resumeCodexThreadId === "string" && input.resumeCodexThreadId.length > 0,
|
|
4032
|
+
importsClaude: typeof input.importedFromClaudeSessionId === "string" && input.importedFromClaudeSessionId.length > 0,
|
|
4059
4033
|
forceNew: input.forceNew === true,
|
|
4060
4034
|
...typeof input.permissionMode === "string" ? { permissionMode: input.permissionMode } : {}
|
|
4061
4035
|
};
|
|
@@ -5005,10 +4979,15 @@ function scrubTmuxClientEnv(env) {
|
|
|
5005
4979
|
return env;
|
|
5006
4980
|
}
|
|
5007
4981
|
|
|
4982
|
+
function getClaudeConfigDir() {
|
|
4983
|
+
return process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), ".claude");
|
|
4984
|
+
}
|
|
4985
|
+
function getClaudeProjectsRoot() {
|
|
4986
|
+
return path.join(getClaudeConfigDir(), "projects");
|
|
4987
|
+
}
|
|
5008
4988
|
function getProjectPath(workingDirectory) {
|
|
5009
4989
|
const projectId = path.resolve(workingDirectory).replace(/[^a-zA-Z0-9-]/g, "-");
|
|
5010
|
-
|
|
5011
|
-
return path.join(claudeConfigDir, "projects", projectId);
|
|
4990
|
+
return path.join(getClaudeProjectsRoot(), projectId);
|
|
5012
4991
|
}
|
|
5013
4992
|
|
|
5014
4993
|
var xtermHeadless = {};
|
|
@@ -10565,13 +10544,17 @@ function ptyEnv() {
|
|
|
10565
10544
|
env.PATH = `${local}:${env.PATH || ""}`;
|
|
10566
10545
|
}
|
|
10567
10546
|
env.TERM = "xterm-256color";
|
|
10568
|
-
|
|
10569
|
-
if (!isUtf8(env.LC_ALL) && !isUtf8(env.LANG) && !isUtf8(env.LC_CTYPE)) {
|
|
10570
|
-
env.LANG = "en_US.UTF-8";
|
|
10571
|
-
env.LC_CTYPE = "en_US.UTF-8";
|
|
10572
|
-
}
|
|
10547
|
+
Object.assign(env, utf8LocaleEnv(env, process.platform));
|
|
10573
10548
|
return env;
|
|
10574
10549
|
}
|
|
10550
|
+
function utf8LocaleEnv(cur, platform) {
|
|
10551
|
+
const isUtf8 = (v) => !!v && /utf-?8/i.test(v);
|
|
10552
|
+
if (isUtf8(cur.LC_ALL) || isUtf8(cur.LANG) || isUtf8(cur.LC_CTYPE)) return {};
|
|
10553
|
+
const locale = platform === "darwin" ? "en_US.UTF-8" : "C.UTF-8";
|
|
10554
|
+
const patch = { LANG: locale, LC_CTYPE: locale };
|
|
10555
|
+
if (cur.LC_ALL) patch.LC_ALL = locale;
|
|
10556
|
+
return patch;
|
|
10557
|
+
}
|
|
10575
10558
|
class TerminalSession {
|
|
10576
10559
|
id;
|
|
10577
10560
|
tmuxSession;
|
|
@@ -12587,6 +12570,182 @@ function detectResumeSupport(happyHomeDir = configuration.happyHomeDir) {
|
|
|
12587
12570
|
};
|
|
12588
12571
|
}
|
|
12589
12572
|
|
|
12573
|
+
const TITLE_PROMPT_PREFIX = "Summarize the SPECIFIC topic/request of this message into a 3-6 word title";
|
|
12574
|
+
const BOARD_ANALYZER_PROMPT_PREFIX = "You are a status analyzer for a task board of coding-agent sessions.";
|
|
12575
|
+
const VERY_HAPPY_ONE_SHOT_PROMPT_PREFIXES = [
|
|
12576
|
+
TITLE_PROMPT_PREFIX,
|
|
12577
|
+
BOARD_ANALYZER_PROMPT_PREFIX
|
|
12578
|
+
];
|
|
12579
|
+
function isVeryHappyOneShotPrompt(firstPrompt) {
|
|
12580
|
+
const text = firstPrompt.trimStart();
|
|
12581
|
+
return VERY_HAPPY_ONE_SHOT_PROMPT_PREFIXES.some((prefix) => text.startsWith(prefix));
|
|
12582
|
+
}
|
|
12583
|
+
|
|
12584
|
+
const UUID_RE$1 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
12585
|
+
async function listClaudeProjectDirs(projectsRoot) {
|
|
12586
|
+
let names;
|
|
12587
|
+
try {
|
|
12588
|
+
names = await promises.readdir(projectsRoot);
|
|
12589
|
+
} catch (error) {
|
|
12590
|
+
if (error.code === "ENOENT") return [];
|
|
12591
|
+
throw error;
|
|
12592
|
+
}
|
|
12593
|
+
const dirs = [];
|
|
12594
|
+
for (const name of names) {
|
|
12595
|
+
const full = path.join(projectsRoot, name);
|
|
12596
|
+
try {
|
|
12597
|
+
if ((await promises.stat(full)).isDirectory()) dirs.push(full);
|
|
12598
|
+
} catch {
|
|
12599
|
+
}
|
|
12600
|
+
}
|
|
12601
|
+
return dirs;
|
|
12602
|
+
}
|
|
12603
|
+
async function collectCandidates(projectDirs, exclude) {
|
|
12604
|
+
const out = [];
|
|
12605
|
+
for (const dir of projectDirs) {
|
|
12606
|
+
let names;
|
|
12607
|
+
try {
|
|
12608
|
+
names = await promises.readdir(dir);
|
|
12609
|
+
} catch {
|
|
12610
|
+
continue;
|
|
12611
|
+
}
|
|
12612
|
+
for (const name of names) {
|
|
12613
|
+
if (!name.endsWith(".jsonl")) continue;
|
|
12614
|
+
const id = name.slice(0, -".jsonl".length);
|
|
12615
|
+
if (!UUID_RE$1.test(id) || exclude.has(id.toLowerCase())) continue;
|
|
12616
|
+
const path$1 = path.join(dir, name);
|
|
12617
|
+
try {
|
|
12618
|
+
const s = await promises.stat(path$1);
|
|
12619
|
+
if (!s.isFile() || s.size === 0) continue;
|
|
12620
|
+
out.push({ path: path$1, id, updatedAt: s.mtimeMs, sizeBytes: s.size });
|
|
12621
|
+
} catch {
|
|
12622
|
+
}
|
|
12623
|
+
}
|
|
12624
|
+
}
|
|
12625
|
+
out.sort((a, b) => b.updatedAt - a.updatedAt);
|
|
12626
|
+
const seen = /* @__PURE__ */ new Set();
|
|
12627
|
+
return out.filter((c) => {
|
|
12628
|
+
const key = c.id.toLowerCase();
|
|
12629
|
+
if (seen.has(key)) return false;
|
|
12630
|
+
seen.add(key);
|
|
12631
|
+
return true;
|
|
12632
|
+
});
|
|
12633
|
+
}
|
|
12634
|
+
async function readHead(path, bytes) {
|
|
12635
|
+
const fh = await promises.open(path, "r");
|
|
12636
|
+
try {
|
|
12637
|
+
const buffer = Buffer.alloc(bytes);
|
|
12638
|
+
const { bytesRead } = await fh.read(buffer, 0, bytes, 0);
|
|
12639
|
+
return buffer.subarray(0, bytesRead).toString("utf-8");
|
|
12640
|
+
} finally {
|
|
12641
|
+
await fh.close();
|
|
12642
|
+
}
|
|
12643
|
+
}
|
|
12644
|
+
function oneLine(text, max) {
|
|
12645
|
+
const collapsed = text.replace(/\s+/g, " ").trim();
|
|
12646
|
+
return collapsed.length > max ? `${collapsed.slice(0, max - 1)}\u2026` : collapsed;
|
|
12647
|
+
}
|
|
12648
|
+
function parseTimestamp(raw) {
|
|
12649
|
+
if (typeof raw === "number" && Number.isFinite(raw)) return raw;
|
|
12650
|
+
if (typeof raw === "string") {
|
|
12651
|
+
const t = Date.parse(raw);
|
|
12652
|
+
if (Number.isFinite(t)) return t;
|
|
12653
|
+
}
|
|
12654
|
+
return null;
|
|
12655
|
+
}
|
|
12656
|
+
function parseClaudeHistoryHead(head, promptChars) {
|
|
12657
|
+
let cwd;
|
|
12658
|
+
let firstPrompt;
|
|
12659
|
+
let summary;
|
|
12660
|
+
let startedAt = null;
|
|
12661
|
+
let entrypoint;
|
|
12662
|
+
let gitBranch;
|
|
12663
|
+
let version;
|
|
12664
|
+
const lines = head.split("\n");
|
|
12665
|
+
for (const line of lines) {
|
|
12666
|
+
if (line.length === 0) continue;
|
|
12667
|
+
let parsed;
|
|
12668
|
+
try {
|
|
12669
|
+
parsed = JSON.parse(line);
|
|
12670
|
+
} catch {
|
|
12671
|
+
continue;
|
|
12672
|
+
}
|
|
12673
|
+
if (!parsed || typeof parsed !== "object") continue;
|
|
12674
|
+
if (parsed.type === "summary" && typeof parsed.summary === "string" && !summary) {
|
|
12675
|
+
const s = oneLine(parsed.summary, promptChars);
|
|
12676
|
+
if (s) summary = s;
|
|
12677
|
+
continue;
|
|
12678
|
+
}
|
|
12679
|
+
if (typeof parsed.cwd === "string" && parsed.cwd && !cwd) cwd = parsed.cwd;
|
|
12680
|
+
if (typeof parsed.entrypoint === "string" && parsed.entrypoint && !entrypoint) entrypoint = parsed.entrypoint;
|
|
12681
|
+
if (typeof parsed.gitBranch === "string" && parsed.gitBranch && !gitBranch) gitBranch = parsed.gitBranch;
|
|
12682
|
+
if (typeof parsed.version === "string" && parsed.version && !version) version = parsed.version;
|
|
12683
|
+
if (startedAt === null) startedAt = parseTimestamp(parsed.timestamp);
|
|
12684
|
+
if (!firstPrompt && parsed.type === "user" && !parsed.isSidechain) {
|
|
12685
|
+
const content = parsed.message?.content;
|
|
12686
|
+
let text = null;
|
|
12687
|
+
if (typeof content === "string") {
|
|
12688
|
+
text = content;
|
|
12689
|
+
} else if (Array.isArray(content)) {
|
|
12690
|
+
const block = content.find((b) => b && b.type === "text" && typeof b.text === "string");
|
|
12691
|
+
if (block) text = block.text;
|
|
12692
|
+
}
|
|
12693
|
+
if (text) {
|
|
12694
|
+
if (isVeryHappyOneShotPrompt(text)) return null;
|
|
12695
|
+
const cleaned = oneLine(stripHarnessTags(text), promptChars);
|
|
12696
|
+
if (cleaned) firstPrompt = cleaned;
|
|
12697
|
+
}
|
|
12698
|
+
}
|
|
12699
|
+
if (cwd && firstPrompt && summary !== void 0 && startedAt !== null) break;
|
|
12700
|
+
}
|
|
12701
|
+
if (!cwd) return null;
|
|
12702
|
+
if (!firstPrompt && !summary) return null;
|
|
12703
|
+
return {
|
|
12704
|
+
cwd,
|
|
12705
|
+
firstPrompt: firstPrompt ?? summary ?? "",
|
|
12706
|
+
...summary ? { summary } : {},
|
|
12707
|
+
startedAt: startedAt ?? 0,
|
|
12708
|
+
...entrypoint ? { entrypoint } : {},
|
|
12709
|
+
...gitBranch ? { gitBranch } : {},
|
|
12710
|
+
...version ? { version } : {}
|
|
12711
|
+
};
|
|
12712
|
+
}
|
|
12713
|
+
function stripHarnessTags(text) {
|
|
12714
|
+
return text.replace(/<(system-reminder|task-notification|command-message|command-name|command-args|local-command-stdout|local-command-caveat)>[\s\S]*?<\/\1>/g, " ").replace(/<\/?(system-reminder|task-notification)>/g, " ");
|
|
12715
|
+
}
|
|
12716
|
+
async function listClaudeSessionHistory(options) {
|
|
12717
|
+
const limit = Math.max(1, Math.min(options.limit ?? 60, 500));
|
|
12718
|
+
const headBytes = Math.max(4096, options.headBytes ?? 64 * 1024);
|
|
12719
|
+
const promptChars = Math.max(20, options.promptChars ?? 200);
|
|
12720
|
+
const exclude = new Set(Array.from(options.exclude ?? [], (id) => id.toLowerCase()));
|
|
12721
|
+
const candidates = await collectCandidates(options.projectDirs, exclude);
|
|
12722
|
+
const entries = [];
|
|
12723
|
+
let scanned = 0;
|
|
12724
|
+
let truncated = false;
|
|
12725
|
+
for (const candidate of candidates) {
|
|
12726
|
+
if (entries.length >= limit) {
|
|
12727
|
+
truncated = true;
|
|
12728
|
+
break;
|
|
12729
|
+
}
|
|
12730
|
+
scanned += 1;
|
|
12731
|
+
let head;
|
|
12732
|
+
try {
|
|
12733
|
+
head = await readHead(candidate.path, headBytes);
|
|
12734
|
+
} catch {
|
|
12735
|
+
continue;
|
|
12736
|
+
}
|
|
12737
|
+
const parsed = parseClaudeHistoryHead(head, promptChars);
|
|
12738
|
+
if (!parsed) continue;
|
|
12739
|
+
entries.push({
|
|
12740
|
+
claudeSessionId: candidate.id.toLowerCase(),
|
|
12741
|
+
updatedAt: Math.round(candidate.updatedAt),
|
|
12742
|
+
sizeBytes: candidate.sizeBytes,
|
|
12743
|
+
...parsed
|
|
12744
|
+
});
|
|
12745
|
+
}
|
|
12746
|
+
return { entries, truncated, scanned };
|
|
12747
|
+
}
|
|
12748
|
+
|
|
12590
12749
|
class ForkTruncateUuidNotFoundError extends Error {
|
|
12591
12750
|
constructor(cutAfterUuid, sourcePath) {
|
|
12592
12751
|
super(`Truncation UUID ${cutAfterUuid} not found in ${sourcePath}`);
|
|
@@ -12611,6 +12770,16 @@ function isUserPrompt(parsed) {
|
|
|
12611
12770
|
const content = parsed.message?.content;
|
|
12612
12771
|
return typeof content === "string";
|
|
12613
12772
|
}
|
|
12773
|
+
async function discardForkedSession(projectDir, claudeSessionId) {
|
|
12774
|
+
try {
|
|
12775
|
+
await promises.unlink(jsonlPath(projectDir, claudeSessionId));
|
|
12776
|
+
logger.debug(`[CLAUDE FORK] Discarded unused fork ${claudeSessionId}`);
|
|
12777
|
+
} catch (error) {
|
|
12778
|
+
if (error.code !== "ENOENT") {
|
|
12779
|
+
logger.debug(`[CLAUDE FORK] Failed to discard fork ${claudeSessionId}:`, error);
|
|
12780
|
+
}
|
|
12781
|
+
}
|
|
12782
|
+
}
|
|
12614
12783
|
async function forkSession(projectDir, sourceClaudeSessionId) {
|
|
12615
12784
|
const newId = node_crypto.randomUUID();
|
|
12616
12785
|
const src = jsonlPath(projectDir, sourceClaudeSessionId);
|
|
@@ -14207,12 +14376,12 @@ class ApiMachineClient {
|
|
|
14207
14376
|
this.stopSessionHandler = stopSession;
|
|
14208
14377
|
this.listTrackedSessionIds = listTrackedSessionIds ?? null;
|
|
14209
14378
|
this.rpcHandlerManager.registerHandler("spawn-happy-session", async (params) => {
|
|
14210
|
-
const { directory, sessionId, machineId, approvedNewDirectoryCreation, agent, environmentVariables, token, resumeClaudeSessionId, resumeCodexThreadId, parentSessionId, forkedFromMessageId, variant, forceNew, permissionMode } = params || {};
|
|
14379
|
+
const { directory, sessionId, machineId, approvedNewDirectoryCreation, agent, environmentVariables, token, resumeClaudeSessionId, resumeCodexThreadId, parentSessionId, forkedFromMessageId, importedFromClaudeSessionId, variant, forceNew, permissionMode } = params || {};
|
|
14211
14380
|
logger.debug("[API MACHINE] Spawning session:", summarizeSpawnSessionForLog(params));
|
|
14212
14381
|
if (!directory && variant !== "assistant") {
|
|
14213
14382
|
throw new Error("Directory is required");
|
|
14214
14383
|
}
|
|
14215
|
-
const result = await spawnSession({ directory: directory || "", sessionId, machineId, approvedNewDirectoryCreation, agent, environmentVariables, token, resumeClaudeSessionId, resumeCodexThreadId, parentSessionId, forkedFromMessageId, variant: variant === "assistant" ? "assistant" : void 0, forceNew: forceNew === true, permissionMode: typeof permissionMode === "string" ? permissionMode : void 0 });
|
|
14384
|
+
const result = await spawnSession({ directory: directory || "", sessionId, machineId, approvedNewDirectoryCreation, agent, environmentVariables, token, resumeClaudeSessionId, resumeCodexThreadId, parentSessionId, forkedFromMessageId, importedFromClaudeSessionId: typeof importedFromClaudeSessionId === "string" ? importedFromClaudeSessionId : void 0, variant: variant === "assistant" ? "assistant" : void 0, forceNew: forceNew === true, permissionMode: typeof permissionMode === "string" ? permissionMode : void 0 });
|
|
14216
14385
|
switch (result.type) {
|
|
14217
14386
|
case "success":
|
|
14218
14387
|
logger.debug(`[API MACHINE] Spawned session ${result.sessionId}`);
|
|
@@ -14224,6 +14393,46 @@ class ApiMachineClient {
|
|
|
14224
14393
|
throw new Error(result.errorMessage);
|
|
14225
14394
|
}
|
|
14226
14395
|
});
|
|
14396
|
+
this.rpcHandlerManager.registerHandler("claude-import-session", async (params) => {
|
|
14397
|
+
const { directory, claudeSessionId, approvedNewDirectoryCreation, permissionMode } = params || {};
|
|
14398
|
+
if (typeof directory !== "string" || directory.length === 0) {
|
|
14399
|
+
throw new Error("directory is required");
|
|
14400
|
+
}
|
|
14401
|
+
if (typeof claudeSessionId !== "string" || !UUID_RE.test(claudeSessionId)) {
|
|
14402
|
+
throw new Error("claudeSessionId must be a valid UUID");
|
|
14403
|
+
}
|
|
14404
|
+
const projectDir = getProjectPath(directory);
|
|
14405
|
+
let newClaudeSessionId;
|
|
14406
|
+
try {
|
|
14407
|
+
newClaudeSessionId = await forkSession(projectDir, claudeSessionId);
|
|
14408
|
+
} catch (error) {
|
|
14409
|
+
if (error instanceof ForkSourceMissingError) {
|
|
14410
|
+
throw new Error("Claude session file not found on this machine");
|
|
14411
|
+
}
|
|
14412
|
+
throw error;
|
|
14413
|
+
}
|
|
14414
|
+
let result;
|
|
14415
|
+
try {
|
|
14416
|
+
result = await spawnSession({
|
|
14417
|
+
directory,
|
|
14418
|
+
machineId: this.machine.id,
|
|
14419
|
+
approvedNewDirectoryCreation: approvedNewDirectoryCreation === true,
|
|
14420
|
+
agent: "claude",
|
|
14421
|
+
resumeClaudeSessionId: newClaudeSessionId,
|
|
14422
|
+
importedFromClaudeSessionId: claudeSessionId,
|
|
14423
|
+
permissionMode: typeof permissionMode === "string" ? permissionMode : void 0
|
|
14424
|
+
});
|
|
14425
|
+
} catch (error) {
|
|
14426
|
+
await discardForkedSession(projectDir, newClaudeSessionId);
|
|
14427
|
+
throw error;
|
|
14428
|
+
}
|
|
14429
|
+
if (result.type !== "success") {
|
|
14430
|
+
await discardForkedSession(projectDir, newClaudeSessionId);
|
|
14431
|
+
return result;
|
|
14432
|
+
}
|
|
14433
|
+
logger.debug(`[API MACHINE] Imported Claude conversation ${claudeSessionId} as session ${result.sessionId}`);
|
|
14434
|
+
return { type: "success", sessionId: result.sessionId, newClaudeSessionId };
|
|
14435
|
+
});
|
|
14227
14436
|
this.syncResumeSessionRpcRegistration();
|
|
14228
14437
|
this.syncRestartSessionRpcRegistration();
|
|
14229
14438
|
this.rpcHandlerManager.registerHandler("open-terminal", async (params) => {
|
|
@@ -14415,6 +14624,20 @@ class ApiMachineClient {
|
|
|
14415
14624
|
throw error;
|
|
14416
14625
|
}
|
|
14417
14626
|
});
|
|
14627
|
+
this.rpcHandlerManager.registerHandler("claude-list-history", async (params) => {
|
|
14628
|
+
const { directory, limit, exclude } = params || {};
|
|
14629
|
+
if (directory !== void 0 && (typeof directory !== "string" || directory.length === 0)) {
|
|
14630
|
+
throw new Error("directory must be a non-empty string when provided");
|
|
14631
|
+
}
|
|
14632
|
+
const projectDirs = typeof directory === "string" ? [getProjectPath(directory)] : await listClaudeProjectDirs(getClaudeProjectsRoot());
|
|
14633
|
+
const excludeIds = Array.isArray(exclude) ? exclude.filter((id) => typeof id === "string" && UUID_RE.test(id)) : [];
|
|
14634
|
+
const result = await listClaudeSessionHistory({
|
|
14635
|
+
projectDirs,
|
|
14636
|
+
limit: typeof limit === "number" && Number.isFinite(limit) ? Math.max(1, Math.min(Math.floor(limit), 200)) : 60,
|
|
14637
|
+
exclude: excludeIds
|
|
14638
|
+
});
|
|
14639
|
+
return { type: "success", ...result };
|
|
14640
|
+
});
|
|
14418
14641
|
this.rpcHandlerManager.registerHandler("codex-fork-thread", async (params) => {
|
|
14419
14642
|
const directory = requireNonEmptyString(params?.directory, "directory");
|
|
14420
14643
|
const codexThreadId = requireNonEmptyString(params?.codexThreadId, "codexThreadId");
|
|
@@ -14712,6 +14935,8 @@ class ApiMachineClient {
|
|
|
14712
14935
|
terminalRestore: { rpcAvailable: true, detectedAt: now },
|
|
14713
14936
|
// B-273 capability flag (same restamp discipline).
|
|
14714
14937
|
tmuxSessions: { rpcAvailable: true, detectedAt: now, killAttached: true },
|
|
14938
|
+
// B-290 capability flag: `claude-list-history` (same restamp discipline).
|
|
14939
|
+
claudeHistory: { rpcAvailable: true, detectedAt: now },
|
|
14715
14940
|
// B-084: closed records survive daemon restarts (persisted
|
|
14716
14941
|
// in closed-terminals.json), so the connect snapshot ships
|
|
14717
14942
|
// them too — not just the incremental pushes.
|
|
@@ -16056,6 +16281,7 @@ const RawJSONLinesSchema = z.z.discriminatedUnion("type", [
|
|
|
16056
16281
|
exports.ApiClient = ApiClient;
|
|
16057
16282
|
exports.ApiSessionClient = ApiSessionClient;
|
|
16058
16283
|
exports.AsyncLock = AsyncLock;
|
|
16284
|
+
exports.BOARD_ANALYZER_PROMPT_PREFIX = BOARD_ANALYZER_PROMPT_PREFIX;
|
|
16059
16285
|
exports.CodexAppServerClient = CodexAppServerClient;
|
|
16060
16286
|
exports.DEFAULT_CLI_UPDATE_CHECK_INTERVAL_MS = DEFAULT_CLI_UPDATE_CHECK_INTERVAL_MS;
|
|
16061
16287
|
exports.InvalidateSync = InvalidateSync;
|
|
@@ -16063,6 +16289,7 @@ exports.MAX_CHAT_ATTACHMENT_SOURCE_BYTES = MAX_CHAT_ATTACHMENT_SOURCE_BYTES;
|
|
|
16063
16289
|
exports.MIRROR_BACKFILL_LINES_DEFAULT = MIRROR_BACKFILL_LINES_DEFAULT;
|
|
16064
16290
|
exports.RawJSONLinesSchema = RawJSONLinesSchema;
|
|
16065
16291
|
exports.SandboxConfigSchema = SandboxConfigSchema;
|
|
16292
|
+
exports.TITLE_PROMPT_PREFIX = TITLE_PROMPT_PREFIX;
|
|
16066
16293
|
exports.acquireDaemonLock = acquireDaemonLock;
|
|
16067
16294
|
exports.api = api;
|
|
16068
16295
|
exports.clearCredentials = clearCredentials;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "very-happy-cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.103",
|
|
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.103",
|
|
117
|
+
"very-happy-tools-x64-darwin": "0.2.103",
|
|
118
|
+
"very-happy-tools-arm64-linux": "0.2.103",
|
|
119
|
+
"very-happy-tools-x64-linux": "0.2.103",
|
|
120
|
+
"very-happy-tools-arm64-win32": "0.2.103",
|
|
121
|
+
"very-happy-tools-x64-win32": "0.2.103"
|
|
122
122
|
},
|
|
123
123
|
"devDependencies": {
|
|
124
124
|
"@slopus/happy-wire": "workspace:*",
|