@zonease/happy 0.6.37 → 0.6.39
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/{types-DYCJXXdA.mjs → api-CIZKyfSn.mjs} +136 -1725
- package/dist/{types-BrTorNAy.cjs → api-CwOCQeLH.cjs} +317 -1961
- package/dist/auth-0VnlXT9f.mjs +181 -0
- package/dist/auth-BE8dD20T.mjs +270 -0
- package/dist/auth-BIleVCw0.cjs +274 -0
- package/dist/auth-Cas9DR3b.cjs +183 -0
- package/dist/browser-Cc08m7of.cjs +22 -0
- package/dist/browser-Dll5hxOV.mjs +20 -0
- package/dist/claudeLocal-CPT3zcIU.mjs +344 -0
- package/dist/claudeLocal-WkgLbl1a.cjs +352 -0
- package/dist/codex/happyMcpStdioBridge.cjs +4 -1
- package/dist/codex/happyMcpStdioBridge.mjs +4 -1
- package/dist/common-BdNgghUJ.mjs +75 -0
- package/dist/common-CZvSRU4p.cjs +83 -0
- package/dist/{config-D_g6ohaV.cjs → config-Draa-5XC.cjs} +21 -34
- package/dist/{config-DPn-cbpz.mjs → config-rTT4qTU5.mjs} +7 -20
- package/dist/connect-BfC1tYP_.mjs +679 -0
- package/dist/connect-CJZ73syK.cjs +681 -0
- package/dist/{constants-B4x1K7by.cjs → constants-E_UWYTo3.cjs} +2 -2
- package/dist/{constants-BO9OAFOT.mjs → constants-pmXZGibX.mjs} +1 -1
- package/dist/controlClient-CM_QcJnB.mjs +159 -0
- package/dist/controlClient-DZMIsKsT.cjs +168 -0
- package/dist/deviceExec-CU1R9CRb.cjs +84 -0
- package/dist/deviceExec-D93AOVV4.mjs +81 -0
- package/dist/diagnose-B3D1a_v0.cjs +60 -0
- package/dist/diagnose-CuRKXxt1.mjs +58 -0
- package/dist/doctor-C08m5Z9f.cjs +78 -0
- package/dist/doctor-Cya9YSA0.cjs +214 -0
- package/dist/doctor-DZYB6IVc.mjs +211 -0
- package/dist/doctor-ON7pN5G5.mjs +74 -0
- package/dist/downloadImage-DfSFLb-h.mjs +887 -0
- package/dist/downloadImage-MQMrUUxS.cjs +897 -0
- package/dist/enroll-BxlbVftP.cjs +115 -0
- package/dist/enroll-C52x8oly.mjs +113 -0
- package/dist/evidence-Ccd9iYcj.mjs +127 -0
- package/dist/evidence-CjWBG5rX.cjs +129 -0
- package/dist/formatImageMessage-B4lSxlk2.mjs +43 -0
- package/dist/formatImageMessage-DbyG8c2j.cjs +46 -0
- package/dist/github-DKcyF1zp.mjs +57 -0
- package/dist/github-h9GhJJ_C.cjs +59 -0
- package/dist/index-CDTNgfUf.cjs +578 -0
- package/dist/index-CPKdvi7J.mjs +554 -0
- package/dist/index.cjs +812 -40
- package/dist/index.mjs +813 -40
- package/dist/install-BokHRgFW.cjs +98 -0
- package/dist/install-C_cp8b2-.cjs +96 -0
- package/dist/install-DAeCn8ae.mjs +94 -0
- package/dist/install-oRAjVLjP.mjs +95 -0
- package/dist/lib.cjs +18 -12
- package/dist/lib.d.cts +16 -16
- package/dist/lib.d.mts +16 -16
- package/dist/lib.mjs +13 -7
- package/dist/{loadableToolOutput-lQH8Kb_w.mjs → loadableToolOutput-Cuygmd4K.mjs} +4 -2
- package/dist/{loadableToolOutput-CasonFIt.cjs → loadableToolOutput-DDCJjywt.cjs} +63 -61
- package/dist/mcpToolSchemas-CjeTfrAs.mjs +62 -0
- package/dist/mcpToolSchemas-DK42yoZ_.cjs +64 -0
- package/dist/persistence-BFjvtUad.mjs +944 -0
- package/dist/persistence-JLUn_Nou.cjs +996 -0
- package/dist/projectPath-BTBIMB57.mjs +10 -0
- package/dist/projectPath-ChLhdOyt.cjs +13 -0
- package/dist/prompt-BTWDZZO0.cjs +25 -0
- package/dist/prompt-BsAZsPbo.mjs +22 -0
- package/dist/release-BMEzNONE.mjs +168 -0
- package/dist/release-EhOJeAZe.cjs +171 -0
- package/dist/run-BDn2aE3Q.cjs +2152 -0
- package/dist/run-DBZqN6Kn.mjs +2128 -0
- package/dist/runClaude-DNHvERX0.mjs +3763 -0
- package/dist/runClaude-uaKxPZPm.cjs +3766 -0
- package/dist/{runCodex-BTt3fxG4.cjs → runCodex-82UrZLme.cjs} +169 -153
- package/dist/{runCodex-BXCB5trB.mjs → runCodex-C-RTM0wA.mjs} +36 -20
- package/dist/{runGemini-37HHbp9n.mjs → runGemini-Bm6-4Xm2.mjs} +34 -17
- package/dist/{runGemini-C-0nSFBv.cjs → runGemini-CXMr4Jwb.cjs} +212 -195
- package/dist/{runOneShot-CXhbFJ2j.cjs → runOneShot-1B6RMYT7.cjs} +31 -55
- package/dist/{runOneShot-USX8QPfE.mjs → runOneShot-ioARRHrx.mjs} +14 -38
- package/dist/{serve-DZAq-_1F.cjs → serve-BtdAhkIE.cjs} +35 -58
- package/dist/{serve-BRedJPzL.mjs → serve-CwibwU6N.mjs} +12 -35
- package/dist/session-2sU4Bu1R.mjs +108 -0
- package/dist/session-D--F87zL.cjs +110 -0
- package/dist/sessionKeyCache-4Tp8pJ3N.cjs +36 -0
- package/dist/sessionKeyCache-D2BwO91C.mjs +33 -0
- package/dist/sessionList-BCSw3qYt.mjs +51 -0
- package/dist/sessionList-ICJsjNtJ.cjs +54 -0
- package/dist/sessionSummary-Bkz52BPh.cjs +393 -0
- package/dist/sessionSummary-lHGBWQSb.mjs +387 -0
- package/dist/spawnHappyCLI-CoORX0YW.mjs +66 -0
- package/dist/spawnHappyCLI-D_V-AAoP.cjs +70 -0
- package/dist/ssh-BTOlhhxi.cjs +333 -0
- package/dist/ssh-DL_AkRAD.mjs +328 -0
- package/dist/systemdScope-BeOtAkhk.mjs +25 -0
- package/dist/systemdScope-DW3-UoKB.cjs +29 -0
- package/dist/task-CQzrHRZe.mjs +165 -0
- package/dist/task-CSpaC9HX.cjs +167 -0
- package/dist/time-Bj2HRkKH.cjs +33 -0
- package/dist/time-DgsBLJA_.mjs +29 -0
- package/dist/trimIdent-4gvk9Wk6.cjs +22 -0
- package/dist/trimIdent-BAjmv_TG.mjs +20 -0
- package/dist/types-BoJh4Qfv.mjs +57 -0
- package/dist/types-DWeB7aqh.cjs +59 -0
- package/dist/uninstall-BiKyRAb1.cjs +77 -0
- package/dist/uninstall-nFMFGUVM.mjs +75 -0
- package/dist/update-BcqRHXf2.mjs +109 -0
- package/dist/update-Dw8pgsAT.cjs +111 -0
- package/dist/workflowCommon-B0d01Y_a.mjs +56 -0
- package/dist/workflowCommon-Bl568-rt.cjs +62 -0
- package/dist/workflowReports-BEWuQesA.cjs +113 -0
- package/dist/workflowReports-Dd8-4-8f.mjs +108 -0
- package/package.json +1 -1
- package/dist/index-BlkHOkB3.cjs +0 -11312
- package/dist/index-DJ2VpGiR.mjs +0 -11272
- package/dist/mcpToolSchemas-CdPXJQmb.mjs +0 -175
- package/dist/mcpToolSchemas-f-kttjFw.cjs +0 -186
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var path = require('node:path');
|
|
4
|
+
var os = require('node:os');
|
|
5
|
+
|
|
6
|
+
const SERVICE_NAME = "happy-daemon.service";
|
|
7
|
+
function systemdScopeFor(isRoot) {
|
|
8
|
+
if (isRoot) {
|
|
9
|
+
return {
|
|
10
|
+
system: true,
|
|
11
|
+
unitPath: path.join("/etc/systemd/system", SERVICE_NAME),
|
|
12
|
+
systemctlFlag: "",
|
|
13
|
+
wantedBy: "multi-user.target"
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
system: false,
|
|
18
|
+
unitPath: path.join(os.homedir(), ".config", "systemd", "user", SERVICE_NAME),
|
|
19
|
+
systemctlFlag: "--user",
|
|
20
|
+
wantedBy: "default.target"
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function isRootUser() {
|
|
24
|
+
return typeof process.getuid === "function" && process.getuid() === 0;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
exports.SERVICE_NAME = SERVICE_NAME;
|
|
28
|
+
exports.isRootUser = isRootUser;
|
|
29
|
+
exports.systemdScopeFor = systemdScopeFor;
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { r as readCredentials } from './persistence-BFjvtUad.mjs';
|
|
4
|
+
import { r as runSessionList } from './sessionList-BCSw3qYt.mjs';
|
|
5
|
+
import { r as runSessionSummary } from './sessionSummary-lHGBWQSb.mjs';
|
|
6
|
+
import { p as parseWorkflowArgs, s as stringFlag, b as booleanFlag, n as numberFlag, a as printOrWriteOutput } from './workflowCommon-B0d01Y_a.mjs';
|
|
7
|
+
import 'node:fs/promises';
|
|
8
|
+
import 'node:fs';
|
|
9
|
+
import 'node:os';
|
|
10
|
+
import 'zod';
|
|
11
|
+
import 'node:crypto';
|
|
12
|
+
import 'tweetnacl';
|
|
13
|
+
import 'fs';
|
|
14
|
+
import 'axios';
|
|
15
|
+
import './auth-BE8dD20T.mjs';
|
|
16
|
+
import 'qrcode-terminal';
|
|
17
|
+
import './time-DgsBLJA_.mjs';
|
|
18
|
+
import './browser-Dll5hxOV.mjs';
|
|
19
|
+
import 'open';
|
|
20
|
+
import 'react';
|
|
21
|
+
import 'ink';
|
|
22
|
+
import './sessionKeyCache-D2BwO91C.mjs';
|
|
23
|
+
|
|
24
|
+
function printTaskHelp() {
|
|
25
|
+
console.log(`
|
|
26
|
+
${chalk.bold("happy task")} - Agent handoff helpers
|
|
27
|
+
|
|
28
|
+
${chalk.bold("Usage:")}
|
|
29
|
+
happy task brief --session <sessionId> [options]
|
|
30
|
+
happy task brief <sessionId> [options]
|
|
31
|
+
happy task brief --recent [--project <path>] [options]
|
|
32
|
+
|
|
33
|
+
${chalk.bold("Options:")}
|
|
34
|
+
--session <id> Happy session ID to summarize
|
|
35
|
+
--recent Pick the most recent session, optionally filtered by --project
|
|
36
|
+
--project <path> Project path filter for --recent
|
|
37
|
+
--limit <n> Raw messages to compact (default: 200)
|
|
38
|
+
--max-turns <n> Compact turns to include (default: 80)
|
|
39
|
+
--text-limit <n> Max chars per text block (default: 800)
|
|
40
|
+
--out <path> Write the brief to a markdown file
|
|
41
|
+
--json Print machine-readable JSON
|
|
42
|
+
-h, --help Show this help
|
|
43
|
+
`);
|
|
44
|
+
}
|
|
45
|
+
function collectSessionText(summary) {
|
|
46
|
+
const chunks = [];
|
|
47
|
+
if (summary.storedSummary) chunks.push(summary.storedSummary);
|
|
48
|
+
for (const turn of summary.turns) {
|
|
49
|
+
if (turn.user?.text) chunks.push(turn.user.text);
|
|
50
|
+
for (const text of turn.assistantTexts) chunks.push(text.text);
|
|
51
|
+
}
|
|
52
|
+
return chunks.join("\n");
|
|
53
|
+
}
|
|
54
|
+
function extractMatchingLines(text, patterns, limit) {
|
|
55
|
+
const seen = /* @__PURE__ */ new Set();
|
|
56
|
+
const lines = [];
|
|
57
|
+
for (const line of text.split("\n")) {
|
|
58
|
+
const trimmed = line.replace(/^[-*]\s*/, "").trim();
|
|
59
|
+
if (!trimmed || seen.has(trimmed)) continue;
|
|
60
|
+
if (patterns.some((pattern) => pattern.test(trimmed))) {
|
|
61
|
+
seen.add(trimmed);
|
|
62
|
+
lines.push(trimmed);
|
|
63
|
+
}
|
|
64
|
+
if (lines.length >= limit) break;
|
|
65
|
+
}
|
|
66
|
+
return lines;
|
|
67
|
+
}
|
|
68
|
+
function bulletList(lines, fallback) {
|
|
69
|
+
if (lines.length === 0) return `- ${fallback}`;
|
|
70
|
+
return lines.map((line) => `- ${line}`).join("\n");
|
|
71
|
+
}
|
|
72
|
+
function buildBriefMarkdown(summary, source) {
|
|
73
|
+
const text = collectSessionText(summary);
|
|
74
|
+
const done = extractMatchingLines(text, [/完成|已实现|通过|passed|fixed|implemented|verified/i], 6);
|
|
75
|
+
const blockers = extractMatchingLines(text, [/失败|阻塞|blocked|failed|error|E401|unrelated/i], 6);
|
|
76
|
+
const next = extractMatchingLines(text, [/继续|下一步|TODO|todo|next|publish|发布|验证|verify/i], 6);
|
|
77
|
+
return `# Happy Task Brief
|
|
78
|
+
|
|
79
|
+
- Source: ${source}
|
|
80
|
+
- Session: \`${summary.sessionId}\`
|
|
81
|
+
- Active: ${summary.active ? "yes" : "no"}
|
|
82
|
+
- Agent: ${summary.agent.name ?? "(unknown)"}${summary.agent.model ? ` / ${summary.agent.model}` : ""}
|
|
83
|
+
- Machine: ${summary.machine.name ?? summary.machine.id ?? "(unknown)"}
|
|
84
|
+
- Path: ${summary.path ?? "(unknown)"}
|
|
85
|
+
- Updated: ${new Date(summary.updatedAt).toISOString()}
|
|
86
|
+
|
|
87
|
+
## Stored Summary
|
|
88
|
+
|
|
89
|
+
${summary.storedSummary ?? "(none)"}
|
|
90
|
+
|
|
91
|
+
## Recent Conversation
|
|
92
|
+
|
|
93
|
+
${summary.summaryText}
|
|
94
|
+
|
|
95
|
+
## Done / Evidence
|
|
96
|
+
|
|
97
|
+
${bulletList(done, "No explicit completion evidence detected in the compact window.")}
|
|
98
|
+
|
|
99
|
+
## Blockers / Risks
|
|
100
|
+
|
|
101
|
+
${bulletList(blockers, "No explicit blocker detected in the compact window.")}
|
|
102
|
+
|
|
103
|
+
## Suggested Next Actions
|
|
104
|
+
|
|
105
|
+
${bulletList(next, "Continue from the latest user request and verify before reporting completion.")}
|
|
106
|
+
|
|
107
|
+
## Continuation Prompt
|
|
108
|
+
|
|
109
|
+
Continue session \`${summary.sessionId}\` from this brief. Preserve existing user changes, inspect the repository rules before editing, implement only the requested scope, run focused verification, and summarize changed files plus test results in Chinese.
|
|
110
|
+
`;
|
|
111
|
+
}
|
|
112
|
+
function findRecentSession(sessions, projectPath) {
|
|
113
|
+
if (!projectPath) return sessions[0];
|
|
114
|
+
const resolved = resolve(projectPath);
|
|
115
|
+
return sessions.find((session) => {
|
|
116
|
+
if (!session.path) return false;
|
|
117
|
+
const path = resolve(session.path);
|
|
118
|
+
return path === resolved || path.startsWith(`${resolved}/`) || resolved.startsWith(`${path}/`);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
async function handleTaskCommand(args) {
|
|
122
|
+
const action = args[0];
|
|
123
|
+
if (!action || action === "-h" || action === "--help") {
|
|
124
|
+
printTaskHelp();
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (action !== "brief") {
|
|
128
|
+
throw new Error(`Unknown task command: ${action}. Use "happy task brief".`);
|
|
129
|
+
}
|
|
130
|
+
const parsed = parseWorkflowArgs(args.slice(1), /* @__PURE__ */ new Set(["--session", "--project", "--limit", "--max-turns", "--text-limit", "--out"]));
|
|
131
|
+
const credentials = await readCredentials();
|
|
132
|
+
if (!credentials) {
|
|
133
|
+
throw new Error('No happy credentials found. Run "happy auth login" first.');
|
|
134
|
+
}
|
|
135
|
+
let sessionId = stringFlag(parsed, "--session") ?? parsed.positionals[0];
|
|
136
|
+
let source = "session";
|
|
137
|
+
if (!sessionId && booleanFlag(parsed, "--recent")) {
|
|
138
|
+
const result = await runSessionList(credentials, { status: "all", limit: 150 });
|
|
139
|
+
const recent = findRecentSession(result.sessions, stringFlag(parsed, "--project"));
|
|
140
|
+
if (!recent) {
|
|
141
|
+
throw new Error("No matching recent session found.");
|
|
142
|
+
}
|
|
143
|
+
sessionId = recent.sessionId;
|
|
144
|
+
source = "recent";
|
|
145
|
+
}
|
|
146
|
+
if (!sessionId) {
|
|
147
|
+
throw new Error('Session ID required. Use "happy task brief --session <id>" or "--recent".');
|
|
148
|
+
}
|
|
149
|
+
const input = {
|
|
150
|
+
sessionId,
|
|
151
|
+
limit: numberFlag(parsed, "--limit"),
|
|
152
|
+
maxTurns: numberFlag(parsed, "--max-turns"),
|
|
153
|
+
textLimit: numberFlag(parsed, "--text-limit"),
|
|
154
|
+
includeTools: true
|
|
155
|
+
};
|
|
156
|
+
const summary = await runSessionSummary(credentials, input);
|
|
157
|
+
const brief = buildBriefMarkdown(summary, source);
|
|
158
|
+
if (booleanFlag(parsed, "--json")) {
|
|
159
|
+
console.log(JSON.stringify({ source, summary, brief }, null, 2));
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
await printOrWriteOutput(brief, stringFlag(parsed, "--out"));
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export { handleTaskCommand };
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chalk = require('chalk');
|
|
4
|
+
var path = require('node:path');
|
|
5
|
+
var logger = require('./persistence-JLUn_Nou.cjs');
|
|
6
|
+
var sessionList = require('./sessionList-ICJsjNtJ.cjs');
|
|
7
|
+
var sessionSummary = require('./sessionSummary-Bkz52BPh.cjs');
|
|
8
|
+
var workflowCommon = require('./workflowCommon-Bl568-rt.cjs');
|
|
9
|
+
require('node:fs/promises');
|
|
10
|
+
require('node:fs');
|
|
11
|
+
require('node:os');
|
|
12
|
+
require('zod');
|
|
13
|
+
require('node:crypto');
|
|
14
|
+
require('tweetnacl');
|
|
15
|
+
require('fs');
|
|
16
|
+
require('axios');
|
|
17
|
+
require('./auth-BIleVCw0.cjs');
|
|
18
|
+
require('qrcode-terminal');
|
|
19
|
+
require('./time-Bj2HRkKH.cjs');
|
|
20
|
+
require('./browser-Cc08m7of.cjs');
|
|
21
|
+
require('open');
|
|
22
|
+
require('react');
|
|
23
|
+
require('ink');
|
|
24
|
+
require('./sessionKeyCache-4Tp8pJ3N.cjs');
|
|
25
|
+
|
|
26
|
+
function printTaskHelp() {
|
|
27
|
+
console.log(`
|
|
28
|
+
${chalk.bold("happy task")} - Agent handoff helpers
|
|
29
|
+
|
|
30
|
+
${chalk.bold("Usage:")}
|
|
31
|
+
happy task brief --session <sessionId> [options]
|
|
32
|
+
happy task brief <sessionId> [options]
|
|
33
|
+
happy task brief --recent [--project <path>] [options]
|
|
34
|
+
|
|
35
|
+
${chalk.bold("Options:")}
|
|
36
|
+
--session <id> Happy session ID to summarize
|
|
37
|
+
--recent Pick the most recent session, optionally filtered by --project
|
|
38
|
+
--project <path> Project path filter for --recent
|
|
39
|
+
--limit <n> Raw messages to compact (default: 200)
|
|
40
|
+
--max-turns <n> Compact turns to include (default: 80)
|
|
41
|
+
--text-limit <n> Max chars per text block (default: 800)
|
|
42
|
+
--out <path> Write the brief to a markdown file
|
|
43
|
+
--json Print machine-readable JSON
|
|
44
|
+
-h, --help Show this help
|
|
45
|
+
`);
|
|
46
|
+
}
|
|
47
|
+
function collectSessionText(summary) {
|
|
48
|
+
const chunks = [];
|
|
49
|
+
if (summary.storedSummary) chunks.push(summary.storedSummary);
|
|
50
|
+
for (const turn of summary.turns) {
|
|
51
|
+
if (turn.user?.text) chunks.push(turn.user.text);
|
|
52
|
+
for (const text of turn.assistantTexts) chunks.push(text.text);
|
|
53
|
+
}
|
|
54
|
+
return chunks.join("\n");
|
|
55
|
+
}
|
|
56
|
+
function extractMatchingLines(text, patterns, limit) {
|
|
57
|
+
const seen = /* @__PURE__ */ new Set();
|
|
58
|
+
const lines = [];
|
|
59
|
+
for (const line of text.split("\n")) {
|
|
60
|
+
const trimmed = line.replace(/^[-*]\s*/, "").trim();
|
|
61
|
+
if (!trimmed || seen.has(trimmed)) continue;
|
|
62
|
+
if (patterns.some((pattern) => pattern.test(trimmed))) {
|
|
63
|
+
seen.add(trimmed);
|
|
64
|
+
lines.push(trimmed);
|
|
65
|
+
}
|
|
66
|
+
if (lines.length >= limit) break;
|
|
67
|
+
}
|
|
68
|
+
return lines;
|
|
69
|
+
}
|
|
70
|
+
function bulletList(lines, fallback) {
|
|
71
|
+
if (lines.length === 0) return `- ${fallback}`;
|
|
72
|
+
return lines.map((line) => `- ${line}`).join("\n");
|
|
73
|
+
}
|
|
74
|
+
function buildBriefMarkdown(summary, source) {
|
|
75
|
+
const text = collectSessionText(summary);
|
|
76
|
+
const done = extractMatchingLines(text, [/完成|已实现|通过|passed|fixed|implemented|verified/i], 6);
|
|
77
|
+
const blockers = extractMatchingLines(text, [/失败|阻塞|blocked|failed|error|E401|unrelated/i], 6);
|
|
78
|
+
const next = extractMatchingLines(text, [/继续|下一步|TODO|todo|next|publish|发布|验证|verify/i], 6);
|
|
79
|
+
return `# Happy Task Brief
|
|
80
|
+
|
|
81
|
+
- Source: ${source}
|
|
82
|
+
- Session: \`${summary.sessionId}\`
|
|
83
|
+
- Active: ${summary.active ? "yes" : "no"}
|
|
84
|
+
- Agent: ${summary.agent.name ?? "(unknown)"}${summary.agent.model ? ` / ${summary.agent.model}` : ""}
|
|
85
|
+
- Machine: ${summary.machine.name ?? summary.machine.id ?? "(unknown)"}
|
|
86
|
+
- Path: ${summary.path ?? "(unknown)"}
|
|
87
|
+
- Updated: ${new Date(summary.updatedAt).toISOString()}
|
|
88
|
+
|
|
89
|
+
## Stored Summary
|
|
90
|
+
|
|
91
|
+
${summary.storedSummary ?? "(none)"}
|
|
92
|
+
|
|
93
|
+
## Recent Conversation
|
|
94
|
+
|
|
95
|
+
${summary.summaryText}
|
|
96
|
+
|
|
97
|
+
## Done / Evidence
|
|
98
|
+
|
|
99
|
+
${bulletList(done, "No explicit completion evidence detected in the compact window.")}
|
|
100
|
+
|
|
101
|
+
## Blockers / Risks
|
|
102
|
+
|
|
103
|
+
${bulletList(blockers, "No explicit blocker detected in the compact window.")}
|
|
104
|
+
|
|
105
|
+
## Suggested Next Actions
|
|
106
|
+
|
|
107
|
+
${bulletList(next, "Continue from the latest user request and verify before reporting completion.")}
|
|
108
|
+
|
|
109
|
+
## Continuation Prompt
|
|
110
|
+
|
|
111
|
+
Continue session \`${summary.sessionId}\` from this brief. Preserve existing user changes, inspect the repository rules before editing, implement only the requested scope, run focused verification, and summarize changed files plus test results in Chinese.
|
|
112
|
+
`;
|
|
113
|
+
}
|
|
114
|
+
function findRecentSession(sessions, projectPath) {
|
|
115
|
+
if (!projectPath) return sessions[0];
|
|
116
|
+
const resolved = path.resolve(projectPath);
|
|
117
|
+
return sessions.find((session) => {
|
|
118
|
+
if (!session.path) return false;
|
|
119
|
+
const path$1 = path.resolve(session.path);
|
|
120
|
+
return path$1 === resolved || path$1.startsWith(`${resolved}/`) || resolved.startsWith(`${path$1}/`);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
async function handleTaskCommand(args) {
|
|
124
|
+
const action = args[0];
|
|
125
|
+
if (!action || action === "-h" || action === "--help") {
|
|
126
|
+
printTaskHelp();
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
if (action !== "brief") {
|
|
130
|
+
throw new Error(`Unknown task command: ${action}. Use "happy task brief".`);
|
|
131
|
+
}
|
|
132
|
+
const parsed = workflowCommon.parseWorkflowArgs(args.slice(1), /* @__PURE__ */ new Set(["--session", "--project", "--limit", "--max-turns", "--text-limit", "--out"]));
|
|
133
|
+
const credentials = await logger.readCredentials();
|
|
134
|
+
if (!credentials) {
|
|
135
|
+
throw new Error('No happy credentials found. Run "happy auth login" first.');
|
|
136
|
+
}
|
|
137
|
+
let sessionId = workflowCommon.stringFlag(parsed, "--session") ?? parsed.positionals[0];
|
|
138
|
+
let source = "session";
|
|
139
|
+
if (!sessionId && workflowCommon.booleanFlag(parsed, "--recent")) {
|
|
140
|
+
const result = await sessionList.runSessionList(credentials, { status: "all", limit: 150 });
|
|
141
|
+
const recent = findRecentSession(result.sessions, workflowCommon.stringFlag(parsed, "--project"));
|
|
142
|
+
if (!recent) {
|
|
143
|
+
throw new Error("No matching recent session found.");
|
|
144
|
+
}
|
|
145
|
+
sessionId = recent.sessionId;
|
|
146
|
+
source = "recent";
|
|
147
|
+
}
|
|
148
|
+
if (!sessionId) {
|
|
149
|
+
throw new Error('Session ID required. Use "happy task brief --session <id>" or "--recent".');
|
|
150
|
+
}
|
|
151
|
+
const input = {
|
|
152
|
+
sessionId,
|
|
153
|
+
limit: workflowCommon.numberFlag(parsed, "--limit"),
|
|
154
|
+
maxTurns: workflowCommon.numberFlag(parsed, "--max-turns"),
|
|
155
|
+
textLimit: workflowCommon.numberFlag(parsed, "--text-limit"),
|
|
156
|
+
includeTools: true
|
|
157
|
+
};
|
|
158
|
+
const summary = await sessionSummary.runSessionSummary(credentials, input);
|
|
159
|
+
const brief = buildBriefMarkdown(summary, source);
|
|
160
|
+
if (workflowCommon.booleanFlag(parsed, "--json")) {
|
|
161
|
+
console.log(JSON.stringify({ source, summary, brief }, null, 2));
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
await workflowCommon.printOrWriteOutput(brief, workflowCommon.stringFlag(parsed, "--out"));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
exports.handleTaskCommand = handleTaskCommand;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
async function delay(ms) {
|
|
4
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
5
|
+
}
|
|
6
|
+
function exponentialBackoffDelay(currentFailureCount, minDelay, maxDelay, maxFailureCount) {
|
|
7
|
+
let maxDelayRet = minDelay + (maxDelay - minDelay) / maxFailureCount * Math.min(currentFailureCount, maxFailureCount);
|
|
8
|
+
return Math.round(Math.random() * maxDelayRet);
|
|
9
|
+
}
|
|
10
|
+
function createBackoff(opts) {
|
|
11
|
+
return async (callback) => {
|
|
12
|
+
let currentFailureCount = 0;
|
|
13
|
+
const minDelay = 250;
|
|
14
|
+
const maxDelay = 1e3;
|
|
15
|
+
const maxFailureCount = 50;
|
|
16
|
+
while (true) {
|
|
17
|
+
try {
|
|
18
|
+
return await callback();
|
|
19
|
+
} catch (e) {
|
|
20
|
+
if (currentFailureCount < maxFailureCount) {
|
|
21
|
+
currentFailureCount++;
|
|
22
|
+
}
|
|
23
|
+
let waitForRequest = exponentialBackoffDelay(currentFailureCount, minDelay, maxDelay, maxFailureCount);
|
|
24
|
+
await delay(waitForRequest);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
let backoff = createBackoff();
|
|
30
|
+
|
|
31
|
+
exports.backoff = backoff;
|
|
32
|
+
exports.delay = delay;
|
|
33
|
+
exports.exponentialBackoffDelay = exponentialBackoffDelay;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
async function delay(ms) {
|
|
2
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
3
|
+
}
|
|
4
|
+
function exponentialBackoffDelay(currentFailureCount, minDelay, maxDelay, maxFailureCount) {
|
|
5
|
+
let maxDelayRet = minDelay + (maxDelay - minDelay) / maxFailureCount * Math.min(currentFailureCount, maxFailureCount);
|
|
6
|
+
return Math.round(Math.random() * maxDelayRet);
|
|
7
|
+
}
|
|
8
|
+
function createBackoff(opts) {
|
|
9
|
+
return async (callback) => {
|
|
10
|
+
let currentFailureCount = 0;
|
|
11
|
+
const minDelay = 250;
|
|
12
|
+
const maxDelay = 1e3;
|
|
13
|
+
const maxFailureCount = 50;
|
|
14
|
+
while (true) {
|
|
15
|
+
try {
|
|
16
|
+
return await callback();
|
|
17
|
+
} catch (e) {
|
|
18
|
+
if (currentFailureCount < maxFailureCount) {
|
|
19
|
+
currentFailureCount++;
|
|
20
|
+
}
|
|
21
|
+
let waitForRequest = exponentialBackoffDelay(currentFailureCount, minDelay, maxDelay, maxFailureCount);
|
|
22
|
+
await delay(waitForRequest);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
let backoff = createBackoff();
|
|
28
|
+
|
|
29
|
+
export { backoff as b, delay as d, exponentialBackoffDelay as e };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function trimIdent(text) {
|
|
4
|
+
const lines = text.split("\n");
|
|
5
|
+
while (lines.length > 0 && lines[0].trim() === "") {
|
|
6
|
+
lines.shift();
|
|
7
|
+
}
|
|
8
|
+
while (lines.length > 0 && lines[lines.length - 1].trim() === "") {
|
|
9
|
+
lines.pop();
|
|
10
|
+
}
|
|
11
|
+
const minSpaces = lines.reduce((min, line) => {
|
|
12
|
+
if (line.trim() === "") {
|
|
13
|
+
return min;
|
|
14
|
+
}
|
|
15
|
+
const leadingSpaces = line.match(/^\s*/)[0].length;
|
|
16
|
+
return Math.min(min, leadingSpaces);
|
|
17
|
+
}, Infinity);
|
|
18
|
+
const trimmedLines = lines.map((line) => line.slice(minSpaces));
|
|
19
|
+
return trimmedLines.join("\n");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
exports.trimIdent = trimIdent;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
function trimIdent(text) {
|
|
2
|
+
const lines = text.split("\n");
|
|
3
|
+
while (lines.length > 0 && lines[0].trim() === "") {
|
|
4
|
+
lines.shift();
|
|
5
|
+
}
|
|
6
|
+
while (lines.length > 0 && lines[lines.length - 1].trim() === "") {
|
|
7
|
+
lines.pop();
|
|
8
|
+
}
|
|
9
|
+
const minSpaces = lines.reduce((min, line) => {
|
|
10
|
+
if (line.trim() === "") {
|
|
11
|
+
return min;
|
|
12
|
+
}
|
|
13
|
+
const leadingSpaces = line.match(/^\s*/)[0].length;
|
|
14
|
+
return Math.min(min, leadingSpaces);
|
|
15
|
+
}, Infinity);
|
|
16
|
+
const trimmedLines = lines.map((line) => line.slice(minSpaces));
|
|
17
|
+
return trimmedLines.join("\n");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export { trimIdent as t };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
const UsageSchema = z.object({
|
|
4
|
+
input_tokens: z.number().int().nonnegative(),
|
|
5
|
+
cache_creation_input_tokens: z.number().int().nonnegative().optional(),
|
|
6
|
+
cache_read_input_tokens: z.number().int().nonnegative().optional(),
|
|
7
|
+
output_tokens: z.number().int().nonnegative(),
|
|
8
|
+
service_tier: z.string().nullish()
|
|
9
|
+
}).passthrough();
|
|
10
|
+
const RawJSONLinesSchema = z.discriminatedUnion("type", [
|
|
11
|
+
// User message - validates uuid and message.content
|
|
12
|
+
z.object({
|
|
13
|
+
type: z.literal("user"),
|
|
14
|
+
isSidechain: z.boolean().optional(),
|
|
15
|
+
isMeta: z.boolean().optional(),
|
|
16
|
+
uuid: z.string(),
|
|
17
|
+
// Used in getMessageKey()
|
|
18
|
+
message: z.object({
|
|
19
|
+
content: z.union([z.string(), z.any()])
|
|
20
|
+
// Used in sessionScanner.ts
|
|
21
|
+
}).passthrough()
|
|
22
|
+
}).passthrough(),
|
|
23
|
+
// Assistant message - only validates uuid and type
|
|
24
|
+
// message object is optional to handle synthetic error messages (isApiErrorMessage: true)
|
|
25
|
+
// which may have different structure than normal assistant messages
|
|
26
|
+
z.object({
|
|
27
|
+
uuid: z.string(),
|
|
28
|
+
type: z.literal("assistant"),
|
|
29
|
+
message: z.object({
|
|
30
|
+
usage: UsageSchema.optional(),
|
|
31
|
+
// Used in apiSession.ts
|
|
32
|
+
model: z.string().optional()
|
|
33
|
+
// Used for cost calculation
|
|
34
|
+
}).passthrough().optional()
|
|
35
|
+
}).passthrough(),
|
|
36
|
+
// Summary message - validates summary and leafUuid
|
|
37
|
+
z.object({
|
|
38
|
+
type: z.literal("summary"),
|
|
39
|
+
summary: z.string(),
|
|
40
|
+
// Used in apiSession.ts
|
|
41
|
+
leafUuid: z.string()
|
|
42
|
+
// Used in getMessageKey()
|
|
43
|
+
}).passthrough(),
|
|
44
|
+
// System message - validates uuid
|
|
45
|
+
z.object({
|
|
46
|
+
type: z.literal("system"),
|
|
47
|
+
uuid: z.string()
|
|
48
|
+
// Used in getMessageKey()
|
|
49
|
+
}).passthrough(),
|
|
50
|
+
// Progress message - hook progress events, not counted toward message limits
|
|
51
|
+
z.object({
|
|
52
|
+
type: z.literal("progress"),
|
|
53
|
+
uuid: z.string()
|
|
54
|
+
}).passthrough()
|
|
55
|
+
]);
|
|
56
|
+
|
|
57
|
+
export { RawJSONLinesSchema as R };
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var z = require('zod');
|
|
4
|
+
|
|
5
|
+
const UsageSchema = z.z.object({
|
|
6
|
+
input_tokens: z.z.number().int().nonnegative(),
|
|
7
|
+
cache_creation_input_tokens: z.z.number().int().nonnegative().optional(),
|
|
8
|
+
cache_read_input_tokens: z.z.number().int().nonnegative().optional(),
|
|
9
|
+
output_tokens: z.z.number().int().nonnegative(),
|
|
10
|
+
service_tier: z.z.string().nullish()
|
|
11
|
+
}).passthrough();
|
|
12
|
+
const RawJSONLinesSchema = z.z.discriminatedUnion("type", [
|
|
13
|
+
// User message - validates uuid and message.content
|
|
14
|
+
z.z.object({
|
|
15
|
+
type: z.z.literal("user"),
|
|
16
|
+
isSidechain: z.z.boolean().optional(),
|
|
17
|
+
isMeta: z.z.boolean().optional(),
|
|
18
|
+
uuid: z.z.string(),
|
|
19
|
+
// Used in getMessageKey()
|
|
20
|
+
message: z.z.object({
|
|
21
|
+
content: z.z.union([z.z.string(), z.z.any()])
|
|
22
|
+
// Used in sessionScanner.ts
|
|
23
|
+
}).passthrough()
|
|
24
|
+
}).passthrough(),
|
|
25
|
+
// Assistant message - only validates uuid and type
|
|
26
|
+
// message object is optional to handle synthetic error messages (isApiErrorMessage: true)
|
|
27
|
+
// which may have different structure than normal assistant messages
|
|
28
|
+
z.z.object({
|
|
29
|
+
uuid: z.z.string(),
|
|
30
|
+
type: z.z.literal("assistant"),
|
|
31
|
+
message: z.z.object({
|
|
32
|
+
usage: UsageSchema.optional(),
|
|
33
|
+
// Used in apiSession.ts
|
|
34
|
+
model: z.z.string().optional()
|
|
35
|
+
// Used for cost calculation
|
|
36
|
+
}).passthrough().optional()
|
|
37
|
+
}).passthrough(),
|
|
38
|
+
// Summary message - validates summary and leafUuid
|
|
39
|
+
z.z.object({
|
|
40
|
+
type: z.z.literal("summary"),
|
|
41
|
+
summary: z.z.string(),
|
|
42
|
+
// Used in apiSession.ts
|
|
43
|
+
leafUuid: z.z.string()
|
|
44
|
+
// Used in getMessageKey()
|
|
45
|
+
}).passthrough(),
|
|
46
|
+
// System message - validates uuid
|
|
47
|
+
z.z.object({
|
|
48
|
+
type: z.z.literal("system"),
|
|
49
|
+
uuid: z.z.string()
|
|
50
|
+
// Used in getMessageKey()
|
|
51
|
+
}).passthrough(),
|
|
52
|
+
// Progress message - hook progress events, not counted toward message limits
|
|
53
|
+
z.z.object({
|
|
54
|
+
type: z.z.literal("progress"),
|
|
55
|
+
uuid: z.z.string()
|
|
56
|
+
}).passthrough()
|
|
57
|
+
]);
|
|
58
|
+
|
|
59
|
+
exports.RawJSONLinesSchema = RawJSONLinesSchema;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var logger = require('./persistence-JLUn_Nou.cjs');
|
|
4
|
+
var fs = require('fs');
|
|
5
|
+
var child_process = require('child_process');
|
|
6
|
+
var path = require('path');
|
|
7
|
+
var os = require('os');
|
|
8
|
+
var systemdScope = require('./systemdScope-DW3-UoKB.cjs');
|
|
9
|
+
var controlClient = require('./controlClient-DZMIsKsT.cjs');
|
|
10
|
+
require('node:fs/promises');
|
|
11
|
+
require('node:fs');
|
|
12
|
+
require('node:os');
|
|
13
|
+
require('node:path');
|
|
14
|
+
require('zod');
|
|
15
|
+
require('node:crypto');
|
|
16
|
+
require('tweetnacl');
|
|
17
|
+
require('chalk');
|
|
18
|
+
require('./projectPath-ChLhdOyt.cjs');
|
|
19
|
+
require('url');
|
|
20
|
+
|
|
21
|
+
const PLIST_LABEL = "com.happy-cli.daemon";
|
|
22
|
+
const PLIST_PATH = path.join(os.homedir(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
|
|
23
|
+
async function uninstall$2() {
|
|
24
|
+
if (!fs.existsSync(PLIST_PATH)) {
|
|
25
|
+
logger.logger.info("No LaunchAgent found. Auto-start is not enabled.");
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
child_process.execSync(`launchctl unload ${PLIST_PATH}`, { stdio: "ignore" });
|
|
30
|
+
} catch {
|
|
31
|
+
}
|
|
32
|
+
fs.unlinkSync(PLIST_PATH);
|
|
33
|
+
logger.logger.info("Daemon auto-start disabled.");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function uninstall$1() {
|
|
37
|
+
const scopes = [systemdScope.systemdScopeFor(true), systemdScope.systemdScopeFor(false)].filter((scope) => fs.existsSync(scope.unitPath));
|
|
38
|
+
if (scopes.length === 0) {
|
|
39
|
+
logger.logger.info("No systemd service found. Auto-start is not enabled.");
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
for (const scope of scopes) {
|
|
43
|
+
if (scope.system && !systemdScope.isRootUser()) {
|
|
44
|
+
logger.logger.info(`Skipping ${scope.unitPath} \u2014 removing it needs root: sudo happy daemon disable`);
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
child_process.execSync(`systemctl ${scope.systemctlFlag} disable --now ${systemdScope.SERVICE_NAME}`.trim(), { stdio: "ignore" });
|
|
49
|
+
} catch {
|
|
50
|
+
}
|
|
51
|
+
fs.unlinkSync(scope.unitPath);
|
|
52
|
+
try {
|
|
53
|
+
child_process.execSync(`systemctl ${scope.systemctlFlag} daemon-reload`.trim(), { stdio: "ignore" });
|
|
54
|
+
} catch {
|
|
55
|
+
}
|
|
56
|
+
logger.logger.info(`Removed ${scope.unitPath}`);
|
|
57
|
+
}
|
|
58
|
+
logger.logger.info("Daemon auto-start disabled.");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function uninstall() {
|
|
62
|
+
if (process.platform === "darwin") {
|
|
63
|
+
logger.logger.info("Removing daemon auto-start for macOS...");
|
|
64
|
+
await uninstall$2();
|
|
65
|
+
} else if (process.platform === "linux") {
|
|
66
|
+
logger.logger.info("Removing daemon auto-start for Linux...");
|
|
67
|
+
await uninstall$1();
|
|
68
|
+
} else {
|
|
69
|
+
throw new Error(`Unsupported platform: ${process.platform}. Only macOS and Linux are supported.`);
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
await controlClient.stopDaemon();
|
|
73
|
+
} catch {
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
exports.uninstall = uninstall;
|