very-happy-cli 0.2.102 → 0.2.104
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-CpKkdwIL.cjs → AcpBackend-DDGfk3HJ.cjs} +1 -1
- package/dist/{AcpBackend-C1nF06ZR.mjs → AcpBackend-De4iQ_JG.mjs} +1 -1
- package/dist/{AcpSessionManager-DjWIwGTR.cjs → AcpSessionManager-Cdv6Xgwy.cjs} +1 -1
- package/dist/{AcpSessionManager-BDad8lK3.mjs → AcpSessionManager-DRzAgHtT.mjs} +1 -1
- package/dist/{config-DSF29tTS.cjs → config-CqM5UxVd.cjs} +2 -2
- package/dist/{config-BlTfR0KA.mjs → config-E3KdWyaQ.mjs} +2 -2
- package/dist/{index-CF3rNFlZ.mjs → index--TiLl7-A.mjs} +22 -16
- package/dist/{index-DjYlt3fw.mjs → index-Bzj1XdPx.mjs} +2 -2
- package/dist/{index-D_2eCeep.cjs → index-CX_3uUem.cjs} +26 -20
- package/dist/{index-CPC-a5lH.mjs → index-C_dmtMJ6.mjs} +5 -5
- package/dist/{index-C8HuYyZq.cjs → index-De6H3U0N.cjs} +2 -2
- package/dist/{index-yLIv8JPD.cjs → index-fuavV8uc.cjs} +4 -4
- package/dist/index.cjs +2 -2
- package/dist/index.mjs +2 -2
- package/dist/{installTerminalHooks-BVMhXRZO.cjs → installTerminalHooks-BXMBwrvq.cjs} +2 -2
- package/dist/{installTerminalHooks-DAR3YEo3.mjs → installTerminalHooks-DV0e8-V3.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-LCZtdhF0.cjs → mcp-Cq3On_Qo.cjs} +2 -2
- package/dist/{mcp--I-Uz_wH.mjs → mcp-DFS9Zohz.mjs} +2 -2
- package/dist/{runGemini-cLR7rfj6.mjs → runGemini-BFjtlkZI.mjs} +4 -4
- package/dist/{runGemini-Crna_6uM.cjs → runGemini-DFvjh8cO.cjs} +4 -4
- package/dist/{runOpenClaw-CWjOCdWg.cjs → runOpenClaw-B5Og1FED.cjs} +3 -3
- package/dist/{runOpenClaw-C1u9xEH7.mjs → runOpenClaw-BFYj1F4u.mjs} +3 -3
- package/dist/{send-BtQ9qf_z.mjs → send-B666yos0.mjs} +2 -2
- package/dist/{send-CufsYMXq.cjs → send-Bog0YPxo.cjs} +2 -2
- package/dist/{spawn-DPllkoUN.cjs → spawn-BSdMkMPX.cjs} +2 -2
- package/dist/{spawn-rdb0uM9R.mjs → spawn-DrqvVoNF.mjs} +2 -2
- package/dist/{types-7-mfp-j5.cjs → types-14LATr9X.cjs} +349 -105
- package/dist/{types-B3Dnzv_q.mjs → types-D9LyZitb.mjs} +347 -105
- package/package.json +7 -7
|
@@ -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.104";
|
|
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.104",
|
|
146
|
+
"very-happy-tools-x64-darwin": "0.2.104",
|
|
147
|
+
"very-happy-tools-arm64-linux": "0.2.104",
|
|
148
|
+
"very-happy-tools-x64-linux": "0.2.104",
|
|
149
|
+
"very-happy-tools-arm64-win32": "0.2.104",
|
|
150
|
+
"very-happy-tools-x64-win32": "0.2.104"
|
|
151
151
|
};
|
|
152
152
|
var devDependencies = {
|
|
153
153
|
"@slopus/happy-wire": "workspace:*",
|
|
@@ -1105,6 +1105,15 @@ z$1.object({
|
|
|
1105
1105
|
/** B-282: `kill-terminal` honours `alsoAttached`. */
|
|
1106
1106
|
killAttached: z$1.boolean().optional()
|
|
1107
1107
|
}).optional(),
|
|
1108
|
+
/**
|
|
1109
|
+
* B-290 capability flag: this daemon answers `claude-list-history` (import a
|
|
1110
|
+
* Claude Code conversation that was never started through very-happy).
|
|
1111
|
+
* Same trust rule (`detectedAt >= startedAt`).
|
|
1112
|
+
*/
|
|
1113
|
+
claudeHistory: z$1.object({
|
|
1114
|
+
rpcAvailable: z$1.boolean(),
|
|
1115
|
+
detectedAt: z$1.number()
|
|
1116
|
+
}).optional(),
|
|
1108
1117
|
/** Relay-owned CLI compatibility/update policy last checked by this daemon. */
|
|
1109
1118
|
cliUpdate: CliUpdateStateSchema.optional(),
|
|
1110
1119
|
/**
|
|
@@ -2094,102 +2103,66 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory) {
|
|
|
2094
2103
|
}
|
|
2095
2104
|
|
|
2096
2105
|
const PRICING = {
|
|
2097
|
-
// --- Claude
|
|
2098
|
-
"claude-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
},
|
|
2104
|
-
"claude-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
},
|
|
2110
|
-
"claude-4-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
},
|
|
2116
|
-
"claude-4.5-sonnet": {
|
|
2117
|
-
input: 3,
|
|
2118
|
-
output: 15,
|
|
2119
|
-
cache_write: 3.75,
|
|
2120
|
-
cache_read: 0.3
|
|
2121
|
-
},
|
|
2122
|
-
"claude-4-sonnet": {
|
|
2123
|
-
input: 3,
|
|
2124
|
-
output: 15,
|
|
2125
|
-
cache_write: 3.75,
|
|
2126
|
-
cache_read: 0.3
|
|
2127
|
-
},
|
|
2128
|
-
"claude-4.5-haiku": {
|
|
2129
|
-
input: 1,
|
|
2130
|
-
output: 5,
|
|
2131
|
-
cache_write: 1.25,
|
|
2132
|
-
cache_read: 0.1
|
|
2133
|
-
},
|
|
2106
|
+
// --- Claude Fable / Mythos (same tier, same price) ---
|
|
2107
|
+
"claude-fable-5-1": { input: 10, output: 50, cache_write: 12.5, cache_read: 0.25 },
|
|
2108
|
+
"claude-fable-5": { input: 10, output: 50, cache_write: 12.5, cache_read: 1 },
|
|
2109
|
+
"claude-mythos-5-1": { input: 10, output: 50, cache_write: 12.5, cache_read: 0.25 },
|
|
2110
|
+
"claude-mythos-5": { input: 10, output: 50, cache_write: 12.5, cache_read: 1 },
|
|
2111
|
+
// --- Claude 5 ---
|
|
2112
|
+
"claude-opus-5": { input: 5, output: 25, cache_write: 6.25, cache_read: 0.5 },
|
|
2113
|
+
"claude-sonnet-5": { input: 2, output: 10, cache_write: 2.5, cache_read: 0.2 },
|
|
2114
|
+
// --- Claude 4.x ---
|
|
2115
|
+
"claude-opus-4-8": { input: 5, output: 25, cache_write: 6.25, cache_read: 0.5 },
|
|
2116
|
+
"claude-opus-4-7": { input: 5, output: 25, cache_write: 6.25, cache_read: 0.5 },
|
|
2117
|
+
"claude-opus-4-6": { input: 5, output: 25, cache_write: 6.25, cache_read: 0.5 },
|
|
2118
|
+
"claude-opus-4-5": { input: 5, output: 25, cache_write: 6.25, cache_read: 0.5 },
|
|
2119
|
+
"claude-opus-4-1": { input: 15, output: 75, cache_write: 18.75, cache_read: 1.5 },
|
|
2120
|
+
"claude-opus-4": { input: 15, output: 75, cache_write: 18.75, cache_read: 1.5 },
|
|
2121
|
+
"claude-sonnet-4-6": { input: 3, output: 15, cache_write: 3.75, cache_read: 0.3 },
|
|
2122
|
+
"claude-sonnet-4-5": { input: 3, output: 15, cache_write: 3.75, cache_read: 0.3 },
|
|
2123
|
+
"claude-sonnet-4": { input: 3, output: 15, cache_write: 3.75, cache_read: 0.3 },
|
|
2124
|
+
"claude-haiku-4-5": { input: 1, output: 5, cache_write: 1.25, cache_read: 0.1 },
|
|
2134
2125
|
// --- Legacy / Claude 3 ---
|
|
2135
|
-
"claude-3-opus-20240229": {
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
},
|
|
2141
|
-
"claude-3-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
"claude-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
cache_read: 0.3
|
|
2152
|
-
},
|
|
2153
|
-
// New Sonnet 3.5 updated model
|
|
2154
|
-
"claude-3-5-sonnet-20241022": {
|
|
2155
|
-
input: 3,
|
|
2156
|
-
output: 15,
|
|
2157
|
-
cache_write: 3.75,
|
|
2158
|
-
cache_read: 0.3
|
|
2159
|
-
},
|
|
2160
|
-
"claude-3-haiku-20240307": {
|
|
2161
|
-
input: 0.25,
|
|
2162
|
-
output: 1.25,
|
|
2163
|
-
cache_write: 0.3125,
|
|
2164
|
-
cache_read: 0.025
|
|
2165
|
-
},
|
|
2166
|
-
"claude-3-5-haiku-20241022": {
|
|
2167
|
-
input: 0.8,
|
|
2168
|
-
output: 4,
|
|
2169
|
-
cache_write: 1,
|
|
2170
|
-
// Approx based on 1.25x rule usually or custom
|
|
2171
|
-
cache_read: 0.08
|
|
2172
|
-
}
|
|
2126
|
+
"claude-3-opus-20240229": { input: 15, output: 75, cache_write: 18.75, cache_read: 1.5 },
|
|
2127
|
+
"claude-3-sonnet-20240229": { input: 3, output: 15, cache_write: 3.75, cache_read: 0.3 },
|
|
2128
|
+
"claude-3-5-sonnet-20240620": { input: 3, output: 15, cache_write: 3.75, cache_read: 0.3 },
|
|
2129
|
+
"claude-3-5-sonnet-20241022": { input: 3, output: 15, cache_write: 3.75, cache_read: 0.3 },
|
|
2130
|
+
"claude-3-7-sonnet-20250219": { input: 3, output: 15, cache_write: 3.75, cache_read: 0.3 },
|
|
2131
|
+
"claude-3-haiku-20240307": { input: 0.25, output: 1.25, cache_write: 0.3125, cache_read: 0.025 },
|
|
2132
|
+
"claude-3-5-haiku-20241022": { input: 0.8, output: 4, cache_write: 1, cache_read: 0.08 }
|
|
2133
|
+
};
|
|
2134
|
+
const DEFAULT_MODEL = "claude-opus-5";
|
|
2135
|
+
const FAMILY_DEFAULTS = {
|
|
2136
|
+
fable: "claude-fable-5-1",
|
|
2137
|
+
// Project Glasswing counterpart of Fable: same tier, same published rates.
|
|
2138
|
+
mythos: "claude-mythos-5-1",
|
|
2139
|
+
opus: "claude-opus-5",
|
|
2140
|
+
sonnet: "claude-sonnet-5",
|
|
2141
|
+
haiku: "claude-haiku-4-5"
|
|
2173
2142
|
};
|
|
2174
|
-
|
|
2143
|
+
function resolvePricingKey(modelId) {
|
|
2144
|
+
if (!modelId) return DEFAULT_MODEL;
|
|
2145
|
+
const raw = modelId.trim().toLowerCase();
|
|
2146
|
+
if (raw in PRICING) return raw;
|
|
2147
|
+
const id = raw.replace(/\[1m\]$/, "");
|
|
2148
|
+
if (id in PRICING) return id;
|
|
2149
|
+
const family = ["fable", "mythos", "opus", "sonnet", "haiku"].find((f) => id.includes(f));
|
|
2150
|
+
if (!family) return DEFAULT_MODEL;
|
|
2151
|
+
const version = id.match(/(?:^|[^0-9])(\d)(?:[-._](\d))?(?:[^0-9]|$)/);
|
|
2152
|
+
const major = version ? version[1] : null;
|
|
2153
|
+
const minor = version && version[2] ? version[2] : null;
|
|
2154
|
+
const key = major ? `claude-${family}-${major}${minor ? `-${minor}` : ""}` : null;
|
|
2155
|
+
if (key && key in PRICING) return key;
|
|
2156
|
+
if (major === "3" && family !== "fable" && family !== "mythos") {
|
|
2157
|
+
if (family === "opus") return "claude-3-opus-20240229";
|
|
2158
|
+
if (family === "haiku") return minor === "5" ? "claude-3-5-haiku-20241022" : "claude-3-haiku-20240307";
|
|
2159
|
+
if (minor === "7") return "claude-3-7-sonnet-20250219";
|
|
2160
|
+
return minor === "5" ? "claude-3-5-sonnet-20241022" : "claude-3-sonnet-20240229";
|
|
2161
|
+
}
|
|
2162
|
+
return FAMILY_DEFAULTS[family];
|
|
2163
|
+
}
|
|
2175
2164
|
function calculateCost(usage, modelId) {
|
|
2176
|
-
|
|
2177
|
-
if (!pricing) {
|
|
2178
|
-
if (modelId?.includes("opus")) {
|
|
2179
|
-
if (modelId.includes("4.5")) pricing = PRICING["claude-4.5-opus"];
|
|
2180
|
-
else if (modelId.includes("4.1")) pricing = PRICING["claude-4.1-opus"];
|
|
2181
|
-
else if (modelId.includes("4")) pricing = PRICING["claude-4-opus"];
|
|
2182
|
-
else pricing = PRICING["claude-3-opus-20240229"];
|
|
2183
|
-
} else if (modelId?.includes("sonnet")) {
|
|
2184
|
-
if (modelId.includes("4.5")) pricing = PRICING["claude-4.5-sonnet"];
|
|
2185
|
-
else if (modelId.includes("4")) pricing = PRICING["claude-4-sonnet"];
|
|
2186
|
-
else pricing = PRICING["claude-3-5-sonnet-20241022"];
|
|
2187
|
-
} else if (modelId?.includes("haiku")) {
|
|
2188
|
-
if (modelId.includes("4.5")) pricing = PRICING["claude-4.5-haiku"];
|
|
2189
|
-
else if (modelId.includes("3.5")) pricing = PRICING["claude-3-5-haiku-20241022"];
|
|
2190
|
-
else pricing = PRICING["claude-3-haiku-20240307"];
|
|
2191
|
-
} else pricing = PRICING[DEFAULT_MODEL];
|
|
2192
|
-
}
|
|
2165
|
+
const pricing = PRICING[resolvePricingKey(modelId)];
|
|
2193
2166
|
const inputCost = usage.input_tokens / 1e6 * pricing.input;
|
|
2194
2167
|
const outputCost = usage.output_tokens / 1e6 * pricing.output;
|
|
2195
2168
|
const cacheWriteCost = (usage.cache_creation_input_tokens || 0) / 1e6 * pricing.cache_write;
|
|
@@ -4034,6 +4007,7 @@ function summarizeSpawnSessionForLog(value) {
|
|
|
4034
4007
|
environmentVariableNames: Object.keys(environmentVariables).sort(),
|
|
4035
4008
|
resumesClaude: typeof input.resumeClaudeSessionId === "string" && input.resumeClaudeSessionId.length > 0,
|
|
4036
4009
|
resumesCodex: typeof input.resumeCodexThreadId === "string" && input.resumeCodexThreadId.length > 0,
|
|
4010
|
+
importsClaude: typeof input.importedFromClaudeSessionId === "string" && input.importedFromClaudeSessionId.length > 0,
|
|
4037
4011
|
forceNew: input.forceNew === true,
|
|
4038
4012
|
...typeof input.permissionMode === "string" ? { permissionMode: input.permissionMode } : {}
|
|
4039
4013
|
};
|
|
@@ -4571,6 +4545,23 @@ function readPersistedSessions() {
|
|
|
4571
4545
|
return {};
|
|
4572
4546
|
}
|
|
4573
4547
|
}
|
|
4548
|
+
function readTrackedClaudeSessionIds() {
|
|
4549
|
+
try {
|
|
4550
|
+
if (!existsSync(configuration.sessionsFile)) return [];
|
|
4551
|
+
const data = JSON.parse(readFileSync(configuration.sessionsFile, "utf-8"));
|
|
4552
|
+
if (!data?.sessions || typeof data.sessions !== "object") return [];
|
|
4553
|
+
const ids = /* @__PURE__ */ new Set();
|
|
4554
|
+
for (const session of Object.values(data.sessions)) {
|
|
4555
|
+
const metadata = (session ?? {}).metadata;
|
|
4556
|
+
for (const id of [metadata?.claudeSessionId, metadata?.importedFromClaudeSessionId]) {
|
|
4557
|
+
if (typeof id === "string" && id.length > 0) ids.add(id.toLowerCase());
|
|
4558
|
+
}
|
|
4559
|
+
}
|
|
4560
|
+
return [...ids];
|
|
4561
|
+
} catch {
|
|
4562
|
+
return [];
|
|
4563
|
+
}
|
|
4564
|
+
}
|
|
4574
4565
|
function persistSession(sessionId, session) {
|
|
4575
4566
|
try {
|
|
4576
4567
|
const existing = readPersistedSessions();
|
|
@@ -4618,6 +4609,7 @@ var persistence = /*#__PURE__*/Object.freeze({
|
|
|
4618
4609
|
readDaemonState: readDaemonState,
|
|
4619
4610
|
readPersistedSessions: readPersistedSessions,
|
|
4620
4611
|
readSettings: readSettings,
|
|
4612
|
+
readTrackedClaudeSessionIds: readTrackedClaudeSessionIds,
|
|
4621
4613
|
releaseDaemonLock: releaseDaemonLock,
|
|
4622
4614
|
updateSettings: updateSettings,
|
|
4623
4615
|
writeCredentialsDataKey: writeCredentialsDataKey,
|
|
@@ -4983,10 +4975,15 @@ function scrubTmuxClientEnv(env) {
|
|
|
4983
4975
|
return env;
|
|
4984
4976
|
}
|
|
4985
4977
|
|
|
4978
|
+
function getClaudeConfigDir() {
|
|
4979
|
+
return process.env.CLAUDE_CONFIG_DIR || join(homedir(), ".claude");
|
|
4980
|
+
}
|
|
4981
|
+
function getClaudeProjectsRoot() {
|
|
4982
|
+
return join(getClaudeConfigDir(), "projects");
|
|
4983
|
+
}
|
|
4986
4984
|
function getProjectPath(workingDirectory) {
|
|
4987
4985
|
const projectId = resolve$1(workingDirectory).replace(/[^a-zA-Z0-9-]/g, "-");
|
|
4988
|
-
|
|
4989
|
-
return join(claudeConfigDir, "projects", projectId);
|
|
4986
|
+
return join(getClaudeProjectsRoot(), projectId);
|
|
4990
4987
|
}
|
|
4991
4988
|
|
|
4992
4989
|
var xtermHeadless = {};
|
|
@@ -12569,6 +12566,182 @@ function detectResumeSupport(happyHomeDir = configuration.happyHomeDir) {
|
|
|
12569
12566
|
};
|
|
12570
12567
|
}
|
|
12571
12568
|
|
|
12569
|
+
const TITLE_PROMPT_PREFIX = "Summarize the SPECIFIC topic/request of this message into a 3-6 word title";
|
|
12570
|
+
const BOARD_ANALYZER_PROMPT_PREFIX = "You are a status analyzer for a task board of coding-agent sessions.";
|
|
12571
|
+
const VERY_HAPPY_ONE_SHOT_PROMPT_PREFIXES = [
|
|
12572
|
+
TITLE_PROMPT_PREFIX,
|
|
12573
|
+
BOARD_ANALYZER_PROMPT_PREFIX
|
|
12574
|
+
];
|
|
12575
|
+
function isVeryHappyOneShotPrompt(firstPrompt) {
|
|
12576
|
+
const text = firstPrompt.trimStart();
|
|
12577
|
+
return VERY_HAPPY_ONE_SHOT_PROMPT_PREFIXES.some((prefix) => text.startsWith(prefix));
|
|
12578
|
+
}
|
|
12579
|
+
|
|
12580
|
+
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;
|
|
12581
|
+
async function listClaudeProjectDirs(projectsRoot) {
|
|
12582
|
+
let names;
|
|
12583
|
+
try {
|
|
12584
|
+
names = await readdir$1(projectsRoot);
|
|
12585
|
+
} catch (error) {
|
|
12586
|
+
if (error.code === "ENOENT") return [];
|
|
12587
|
+
throw error;
|
|
12588
|
+
}
|
|
12589
|
+
const dirs = [];
|
|
12590
|
+
for (const name of names) {
|
|
12591
|
+
const full = join(projectsRoot, name);
|
|
12592
|
+
try {
|
|
12593
|
+
if ((await stat$1(full)).isDirectory()) dirs.push(full);
|
|
12594
|
+
} catch {
|
|
12595
|
+
}
|
|
12596
|
+
}
|
|
12597
|
+
return dirs;
|
|
12598
|
+
}
|
|
12599
|
+
async function collectCandidates(projectDirs, exclude) {
|
|
12600
|
+
const out = [];
|
|
12601
|
+
for (const dir of projectDirs) {
|
|
12602
|
+
let names;
|
|
12603
|
+
try {
|
|
12604
|
+
names = await readdir$1(dir);
|
|
12605
|
+
} catch {
|
|
12606
|
+
continue;
|
|
12607
|
+
}
|
|
12608
|
+
for (const name of names) {
|
|
12609
|
+
if (!name.endsWith(".jsonl")) continue;
|
|
12610
|
+
const id = name.slice(0, -".jsonl".length);
|
|
12611
|
+
if (!UUID_RE$1.test(id) || exclude.has(id.toLowerCase())) continue;
|
|
12612
|
+
const path = join(dir, name);
|
|
12613
|
+
try {
|
|
12614
|
+
const s = await stat$1(path);
|
|
12615
|
+
if (!s.isFile() || s.size === 0) continue;
|
|
12616
|
+
out.push({ path, id, updatedAt: s.mtimeMs, sizeBytes: s.size });
|
|
12617
|
+
} catch {
|
|
12618
|
+
}
|
|
12619
|
+
}
|
|
12620
|
+
}
|
|
12621
|
+
out.sort((a, b) => b.updatedAt - a.updatedAt);
|
|
12622
|
+
const seen = /* @__PURE__ */ new Set();
|
|
12623
|
+
return out.filter((c) => {
|
|
12624
|
+
const key = c.id.toLowerCase();
|
|
12625
|
+
if (seen.has(key)) return false;
|
|
12626
|
+
seen.add(key);
|
|
12627
|
+
return true;
|
|
12628
|
+
});
|
|
12629
|
+
}
|
|
12630
|
+
async function readHead(path, bytes) {
|
|
12631
|
+
const fh = await open(path, "r");
|
|
12632
|
+
try {
|
|
12633
|
+
const buffer = Buffer.alloc(bytes);
|
|
12634
|
+
const { bytesRead } = await fh.read(buffer, 0, bytes, 0);
|
|
12635
|
+
return buffer.subarray(0, bytesRead).toString("utf-8");
|
|
12636
|
+
} finally {
|
|
12637
|
+
await fh.close();
|
|
12638
|
+
}
|
|
12639
|
+
}
|
|
12640
|
+
function oneLine(text, max) {
|
|
12641
|
+
const collapsed = text.replace(/\s+/g, " ").trim();
|
|
12642
|
+
return collapsed.length > max ? `${collapsed.slice(0, max - 1)}\u2026` : collapsed;
|
|
12643
|
+
}
|
|
12644
|
+
function parseTimestamp(raw) {
|
|
12645
|
+
if (typeof raw === "number" && Number.isFinite(raw)) return raw;
|
|
12646
|
+
if (typeof raw === "string") {
|
|
12647
|
+
const t = Date.parse(raw);
|
|
12648
|
+
if (Number.isFinite(t)) return t;
|
|
12649
|
+
}
|
|
12650
|
+
return null;
|
|
12651
|
+
}
|
|
12652
|
+
function parseClaudeHistoryHead(head, promptChars) {
|
|
12653
|
+
let cwd;
|
|
12654
|
+
let firstPrompt;
|
|
12655
|
+
let summary;
|
|
12656
|
+
let startedAt = null;
|
|
12657
|
+
let entrypoint;
|
|
12658
|
+
let gitBranch;
|
|
12659
|
+
let version;
|
|
12660
|
+
const lines = head.split("\n");
|
|
12661
|
+
for (const line of lines) {
|
|
12662
|
+
if (line.length === 0) continue;
|
|
12663
|
+
let parsed;
|
|
12664
|
+
try {
|
|
12665
|
+
parsed = JSON.parse(line);
|
|
12666
|
+
} catch {
|
|
12667
|
+
continue;
|
|
12668
|
+
}
|
|
12669
|
+
if (!parsed || typeof parsed !== "object") continue;
|
|
12670
|
+
if (parsed.type === "summary" && typeof parsed.summary === "string" && !summary) {
|
|
12671
|
+
const s = oneLine(parsed.summary, promptChars);
|
|
12672
|
+
if (s) summary = s;
|
|
12673
|
+
continue;
|
|
12674
|
+
}
|
|
12675
|
+
if (typeof parsed.cwd === "string" && parsed.cwd && !cwd) cwd = parsed.cwd;
|
|
12676
|
+
if (typeof parsed.entrypoint === "string" && parsed.entrypoint && !entrypoint) entrypoint = parsed.entrypoint;
|
|
12677
|
+
if (typeof parsed.gitBranch === "string" && parsed.gitBranch && !gitBranch) gitBranch = parsed.gitBranch;
|
|
12678
|
+
if (typeof parsed.version === "string" && parsed.version && !version) version = parsed.version;
|
|
12679
|
+
if (startedAt === null) startedAt = parseTimestamp(parsed.timestamp);
|
|
12680
|
+
if (!firstPrompt && parsed.type === "user" && !parsed.isSidechain) {
|
|
12681
|
+
const content = parsed.message?.content;
|
|
12682
|
+
let text = null;
|
|
12683
|
+
if (typeof content === "string") {
|
|
12684
|
+
text = content;
|
|
12685
|
+
} else if (Array.isArray(content)) {
|
|
12686
|
+
const block = content.find((b) => b && b.type === "text" && typeof b.text === "string");
|
|
12687
|
+
if (block) text = block.text;
|
|
12688
|
+
}
|
|
12689
|
+
if (text) {
|
|
12690
|
+
if (isVeryHappyOneShotPrompt(text)) return null;
|
|
12691
|
+
const cleaned = oneLine(stripHarnessTags(text), promptChars);
|
|
12692
|
+
if (cleaned) firstPrompt = cleaned;
|
|
12693
|
+
}
|
|
12694
|
+
}
|
|
12695
|
+
if (cwd && firstPrompt && summary !== void 0 && startedAt !== null) break;
|
|
12696
|
+
}
|
|
12697
|
+
if (!cwd) return null;
|
|
12698
|
+
if (!firstPrompt && !summary) return null;
|
|
12699
|
+
return {
|
|
12700
|
+
cwd,
|
|
12701
|
+
firstPrompt: firstPrompt ?? summary ?? "",
|
|
12702
|
+
...summary ? { summary } : {},
|
|
12703
|
+
startedAt: startedAt ?? 0,
|
|
12704
|
+
...entrypoint ? { entrypoint } : {},
|
|
12705
|
+
...gitBranch ? { gitBranch } : {},
|
|
12706
|
+
...version ? { version } : {}
|
|
12707
|
+
};
|
|
12708
|
+
}
|
|
12709
|
+
function stripHarnessTags(text) {
|
|
12710
|
+
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, " ");
|
|
12711
|
+
}
|
|
12712
|
+
async function listClaudeSessionHistory(options) {
|
|
12713
|
+
const limit = Math.max(1, Math.min(options.limit ?? 60, 500));
|
|
12714
|
+
const headBytes = Math.max(4096, options.headBytes ?? 64 * 1024);
|
|
12715
|
+
const promptChars = Math.max(20, options.promptChars ?? 200);
|
|
12716
|
+
const exclude = new Set(Array.from(options.exclude ?? [], (id) => id.toLowerCase()));
|
|
12717
|
+
const candidates = await collectCandidates(options.projectDirs, exclude);
|
|
12718
|
+
const entries = [];
|
|
12719
|
+
let scanned = 0;
|
|
12720
|
+
let truncated = false;
|
|
12721
|
+
for (const candidate of candidates) {
|
|
12722
|
+
if (entries.length >= limit) {
|
|
12723
|
+
truncated = true;
|
|
12724
|
+
break;
|
|
12725
|
+
}
|
|
12726
|
+
scanned += 1;
|
|
12727
|
+
let head;
|
|
12728
|
+
try {
|
|
12729
|
+
head = await readHead(candidate.path, headBytes);
|
|
12730
|
+
} catch {
|
|
12731
|
+
continue;
|
|
12732
|
+
}
|
|
12733
|
+
const parsed = parseClaudeHistoryHead(head, promptChars);
|
|
12734
|
+
if (!parsed) continue;
|
|
12735
|
+
entries.push({
|
|
12736
|
+
claudeSessionId: candidate.id.toLowerCase(),
|
|
12737
|
+
updatedAt: Math.round(candidate.updatedAt),
|
|
12738
|
+
sizeBytes: candidate.sizeBytes,
|
|
12739
|
+
...parsed
|
|
12740
|
+
});
|
|
12741
|
+
}
|
|
12742
|
+
return { entries, truncated, scanned };
|
|
12743
|
+
}
|
|
12744
|
+
|
|
12572
12745
|
class ForkTruncateUuidNotFoundError extends Error {
|
|
12573
12746
|
constructor(cutAfterUuid, sourcePath) {
|
|
12574
12747
|
super(`Truncation UUID ${cutAfterUuid} not found in ${sourcePath}`);
|
|
@@ -12593,6 +12766,16 @@ function isUserPrompt(parsed) {
|
|
|
12593
12766
|
const content = parsed.message?.content;
|
|
12594
12767
|
return typeof content === "string";
|
|
12595
12768
|
}
|
|
12769
|
+
async function discardForkedSession(projectDir, claudeSessionId) {
|
|
12770
|
+
try {
|
|
12771
|
+
await unlink$1(jsonlPath(projectDir, claudeSessionId));
|
|
12772
|
+
logger.debug(`[CLAUDE FORK] Discarded unused fork ${claudeSessionId}`);
|
|
12773
|
+
} catch (error) {
|
|
12774
|
+
if (error.code !== "ENOENT") {
|
|
12775
|
+
logger.debug(`[CLAUDE FORK] Failed to discard fork ${claudeSessionId}:`, error);
|
|
12776
|
+
}
|
|
12777
|
+
}
|
|
12778
|
+
}
|
|
12596
12779
|
async function forkSession(projectDir, sourceClaudeSessionId) {
|
|
12597
12780
|
const newId = randomUUID$1();
|
|
12598
12781
|
const src = jsonlPath(projectDir, sourceClaudeSessionId);
|
|
@@ -14189,12 +14372,12 @@ class ApiMachineClient {
|
|
|
14189
14372
|
this.stopSessionHandler = stopSession;
|
|
14190
14373
|
this.listTrackedSessionIds = listTrackedSessionIds ?? null;
|
|
14191
14374
|
this.rpcHandlerManager.registerHandler("spawn-happy-session", async (params) => {
|
|
14192
|
-
const { directory, sessionId, machineId, approvedNewDirectoryCreation, agent, environmentVariables, token, resumeClaudeSessionId, resumeCodexThreadId, parentSessionId, forkedFromMessageId, variant, forceNew, permissionMode } = params || {};
|
|
14375
|
+
const { directory, sessionId, machineId, approvedNewDirectoryCreation, agent, environmentVariables, token, resumeClaudeSessionId, resumeCodexThreadId, parentSessionId, forkedFromMessageId, importedFromClaudeSessionId, variant, forceNew, permissionMode } = params || {};
|
|
14193
14376
|
logger.debug("[API MACHINE] Spawning session:", summarizeSpawnSessionForLog(params));
|
|
14194
14377
|
if (!directory && variant !== "assistant") {
|
|
14195
14378
|
throw new Error("Directory is required");
|
|
14196
14379
|
}
|
|
14197
|
-
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 });
|
|
14380
|
+
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 });
|
|
14198
14381
|
switch (result.type) {
|
|
14199
14382
|
case "success":
|
|
14200
14383
|
logger.debug(`[API MACHINE] Spawned session ${result.sessionId}`);
|
|
@@ -14206,6 +14389,46 @@ class ApiMachineClient {
|
|
|
14206
14389
|
throw new Error(result.errorMessage);
|
|
14207
14390
|
}
|
|
14208
14391
|
});
|
|
14392
|
+
this.rpcHandlerManager.registerHandler("claude-import-session", async (params) => {
|
|
14393
|
+
const { directory, claudeSessionId, approvedNewDirectoryCreation, permissionMode } = params || {};
|
|
14394
|
+
if (typeof directory !== "string" || directory.length === 0) {
|
|
14395
|
+
throw new Error("directory is required");
|
|
14396
|
+
}
|
|
14397
|
+
if (typeof claudeSessionId !== "string" || !UUID_RE.test(claudeSessionId)) {
|
|
14398
|
+
throw new Error("claudeSessionId must be a valid UUID");
|
|
14399
|
+
}
|
|
14400
|
+
const projectDir = getProjectPath(directory);
|
|
14401
|
+
let newClaudeSessionId;
|
|
14402
|
+
try {
|
|
14403
|
+
newClaudeSessionId = await forkSession(projectDir, claudeSessionId);
|
|
14404
|
+
} catch (error) {
|
|
14405
|
+
if (error instanceof ForkSourceMissingError) {
|
|
14406
|
+
throw new Error("Claude session file not found on this machine");
|
|
14407
|
+
}
|
|
14408
|
+
throw error;
|
|
14409
|
+
}
|
|
14410
|
+
let result;
|
|
14411
|
+
try {
|
|
14412
|
+
result = await spawnSession({
|
|
14413
|
+
directory,
|
|
14414
|
+
machineId: this.machine.id,
|
|
14415
|
+
approvedNewDirectoryCreation: approvedNewDirectoryCreation === true,
|
|
14416
|
+
agent: "claude",
|
|
14417
|
+
resumeClaudeSessionId: newClaudeSessionId,
|
|
14418
|
+
importedFromClaudeSessionId: claudeSessionId,
|
|
14419
|
+
permissionMode: typeof permissionMode === "string" ? permissionMode : void 0
|
|
14420
|
+
});
|
|
14421
|
+
} catch (error) {
|
|
14422
|
+
await discardForkedSession(projectDir, newClaudeSessionId);
|
|
14423
|
+
throw error;
|
|
14424
|
+
}
|
|
14425
|
+
if (result.type !== "success") {
|
|
14426
|
+
await discardForkedSession(projectDir, newClaudeSessionId);
|
|
14427
|
+
return result;
|
|
14428
|
+
}
|
|
14429
|
+
logger.debug(`[API MACHINE] Imported Claude conversation ${claudeSessionId} as session ${result.sessionId}`);
|
|
14430
|
+
return { type: "success", sessionId: result.sessionId, newClaudeSessionId };
|
|
14431
|
+
});
|
|
14209
14432
|
this.syncResumeSessionRpcRegistration();
|
|
14210
14433
|
this.syncRestartSessionRpcRegistration();
|
|
14211
14434
|
this.rpcHandlerManager.registerHandler("open-terminal", async (params) => {
|
|
@@ -14397,6 +14620,23 @@ class ApiMachineClient {
|
|
|
14397
14620
|
throw error;
|
|
14398
14621
|
}
|
|
14399
14622
|
});
|
|
14623
|
+
this.rpcHandlerManager.registerHandler("claude-list-history", async (params) => {
|
|
14624
|
+
const { directory, limit, exclude } = params || {};
|
|
14625
|
+
if (directory !== void 0 && (typeof directory !== "string" || directory.length === 0)) {
|
|
14626
|
+
throw new Error("directory must be a non-empty string when provided");
|
|
14627
|
+
}
|
|
14628
|
+
const projectDirs = typeof directory === "string" ? [getProjectPath(directory)] : await listClaudeProjectDirs(getClaudeProjectsRoot());
|
|
14629
|
+
const excludeIds = [
|
|
14630
|
+
...Array.isArray(exclude) ? exclude.filter((id) => typeof id === "string" && UUID_RE.test(id)) : [],
|
|
14631
|
+
...readTrackedClaudeSessionIds()
|
|
14632
|
+
];
|
|
14633
|
+
const result = await listClaudeSessionHistory({
|
|
14634
|
+
projectDirs,
|
|
14635
|
+
limit: typeof limit === "number" && Number.isFinite(limit) ? Math.max(1, Math.min(Math.floor(limit), 200)) : 60,
|
|
14636
|
+
exclude: excludeIds
|
|
14637
|
+
});
|
|
14638
|
+
return { type: "success", ...result };
|
|
14639
|
+
});
|
|
14400
14640
|
this.rpcHandlerManager.registerHandler("codex-fork-thread", async (params) => {
|
|
14401
14641
|
const directory = requireNonEmptyString(params?.directory, "directory");
|
|
14402
14642
|
const codexThreadId = requireNonEmptyString(params?.codexThreadId, "codexThreadId");
|
|
@@ -14694,6 +14934,8 @@ class ApiMachineClient {
|
|
|
14694
14934
|
terminalRestore: { rpcAvailable: true, detectedAt: now },
|
|
14695
14935
|
// B-273 capability flag (same restamp discipline).
|
|
14696
14936
|
tmuxSessions: { rpcAvailable: true, detectedAt: now, killAttached: true },
|
|
14937
|
+
// B-290 capability flag: `claude-list-history` (same restamp discipline).
|
|
14938
|
+
claudeHistory: { rpcAvailable: true, detectedAt: now },
|
|
14697
14939
|
// B-084: closed records survive daemon restarts (persisted
|
|
14698
14940
|
// in closed-terminals.json), so the connect snapshot ships
|
|
14699
14941
|
// them too — not just the incremental pushes.
|
|
@@ -16035,4 +16277,4 @@ const RawJSONLinesSchema = z$1.discriminatedUnion("type", [
|
|
|
16035
16277
|
}).passthrough()
|
|
16036
16278
|
]);
|
|
16037
16279
|
|
|
16038
|
-
export { detectResumeSupport as $, ApiClient as A, deriveLocalCliUpdateSummary as B, updateSettings as C, encodeBase64Url as D, decodeBase64 as E, writeCredentialsLegacy as F, writeCredentialsDataKey as G, readCredentialsForConfiguredRelay as H, InvalidateSync as I, encrypt as J, isClaudeSessionId as K, getLocalHappyAgentCredentialPath as L, MAX_CHAT_ATTACHMENT_SOURCE_BYTES as M, readLocalHappyAgentCredentials as N, decryptWithDataKey as O, decryptLegacy as P, extractCompleteLines as Q, RawJSONLinesSchema as R, decideBackfill as S, parseTerminalHookPayload as T, tmuxArgs as U, scrubTmuxClientEnv as V, decideMirrorBinding as W, MIRROR_BACKFILL_LINES_DEFAULT as X, persistSession as Y, mirrorLineKey as Z, mirrorLocalId as _, ApiSessionClient as a, detectCLIAvailability as a0, acquireDaemonLock as a1, summarizeSpawnSessionForLog as a2, deletePersistedSessions as a3, shellescape as a4, decrypt as a5, writeDaemonState as a6, writeSettings as a7, fetchCliUpdateState as a8, resolveCliUpdateCheckInterval as a9, DEFAULT_CLI_UPDATE_CHECK_INTERVAL_MS as aa, releaseDaemonLock as ab, isValidSessionId as ac, listVhTerminals as ad, isValidTerminalId as ae, readVhTerminal as af, sendToVhTerminal as ag,
|
|
16280
|
+
export { detectResumeSupport as $, ApiClient as A, deriveLocalCliUpdateSummary as B, updateSettings as C, encodeBase64Url as D, decodeBase64 as E, writeCredentialsLegacy as F, writeCredentialsDataKey as G, readCredentialsForConfiguredRelay as H, InvalidateSync as I, encrypt as J, isClaudeSessionId as K, getLocalHappyAgentCredentialPath as L, MAX_CHAT_ATTACHMENT_SOURCE_BYTES as M, readLocalHappyAgentCredentials as N, decryptWithDataKey as O, decryptLegacy as P, extractCompleteLines as Q, RawJSONLinesSchema as R, decideBackfill as S, parseTerminalHookPayload as T, tmuxArgs as U, scrubTmuxClientEnv as V, decideMirrorBinding as W, MIRROR_BACKFILL_LINES_DEFAULT as X, persistSession as Y, mirrorLineKey as Z, mirrorLocalId as _, ApiSessionClient as a, detectCLIAvailability as a0, acquireDaemonLock as a1, summarizeSpawnSessionForLog as a2, deletePersistedSessions as a3, shellescape as a4, decrypt as a5, writeDaemonState as a6, writeSettings as a7, fetchCliUpdateState as a8, resolveCliUpdateCheckInterval as a9, DEFAULT_CLI_UPDATE_CHECK_INTERVAL_MS as aa, releaseDaemonLock as ab, isValidSessionId as ac, listVhTerminals as ad, isValidTerminalId as ae, readVhTerminal as af, sendToVhTerminal as ag, TITLE_PROMPT_PREFIX as ah, BOARD_ANALYZER_PROMPT_PREFIX as ai, startOfflineReconnection as aj, clearCredentials as ak, clearMachineId as al, SandboxConfigSchema as am, CodexAppServerClient as an, getLatestDaemonLog as ao, persistence as ap, api as aq, createEnvelope as b, configuration as c, contentLogMetadata as d, errorLogMetadata as e, packageJson as f, delay as g, connectionState as h, readSettings as i, encodeBase64 as j, ensurePrivateDirectorySync as k, logger as l, hardenPrivateFileSync as m, readDaemonState as n, clearDaemonState as o, projectPath as p, getProjectPath as q, readPersistedSessions as r, initializeSandbox as s, wrapCommand as t, AsyncLock as u, ensurePrivateDirectory as v, writePrivateFileSync as w, hardenPrivateFile as x, readCredentials as y, credentialRelayProblem as z };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "very-happy-cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.104",
|
|
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.104",
|
|
117
|
+
"very-happy-tools-x64-darwin": "0.2.104",
|
|
118
|
+
"very-happy-tools-arm64-linux": "0.2.104",
|
|
119
|
+
"very-happy-tools-x64-linux": "0.2.104",
|
|
120
|
+
"very-happy-tools-arm64-win32": "0.2.104",
|
|
121
|
+
"very-happy-tools-x64-win32": "0.2.104"
|
|
122
122
|
},
|
|
123
123
|
"devDependencies": {
|
|
124
124
|
"@slopus/happy-wire": "workspace:*",
|