hatchee 0.1.4 → 0.1.6
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/cli.mjs +119 -50
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -3942,16 +3942,24 @@ class SpawnedSession {
|
|
|
3942
3942
|
daemon;
|
|
3943
3943
|
sessionId;
|
|
3944
3944
|
cwd;
|
|
3945
|
+
model;
|
|
3945
3946
|
claudeBin;
|
|
3946
3947
|
proc = null;
|
|
3947
|
-
|
|
3948
|
-
|
|
3948
|
+
mode = "edits";
|
|
3949
|
+
claudeSessionId = null;
|
|
3950
|
+
stopped = false;
|
|
3951
|
+
constructor(daemon, sessionId, cwd, model = null, claudeBin = process.env.DROVER_CLAUDE_BIN || "claude") {
|
|
3949
3952
|
this.daemon = daemon;
|
|
3950
3953
|
this.sessionId = sessionId;
|
|
3951
3954
|
this.cwd = cwd;
|
|
3955
|
+
this.model = model;
|
|
3952
3956
|
this.claudeBin = claudeBin;
|
|
3953
3957
|
}
|
|
3954
3958
|
start(firstTask, mode = "edits") {
|
|
3959
|
+
this.mode = mode;
|
|
3960
|
+
this.spawnProc(firstTask);
|
|
3961
|
+
}
|
|
3962
|
+
spawnProc(firstMessage) {
|
|
3955
3963
|
const args = [
|
|
3956
3964
|
"--print",
|
|
3957
3965
|
"--output-format",
|
|
@@ -3960,7 +3968,9 @@ class SpawnedSession {
|
|
|
3960
3968
|
"stream-json",
|
|
3961
3969
|
"--verbose",
|
|
3962
3970
|
"--permission-mode",
|
|
3963
|
-
PERMISSION_MODE[mode] ?? "acceptEdits"
|
|
3971
|
+
PERMISSION_MODE[this.mode] ?? "acceptEdits",
|
|
3972
|
+
...this.model ? ["--model", this.model] : [],
|
|
3973
|
+
...this.claudeSessionId ? ["--resume", this.claudeSessionId] : []
|
|
3964
3974
|
];
|
|
3965
3975
|
let proc;
|
|
3966
3976
|
try {
|
|
@@ -3976,22 +3986,28 @@ class SpawnedSession {
|
|
|
3976
3986
|
if (s)
|
|
3977
3987
|
this.daemon.spawnLine(this.sessionId, s, "muted");
|
|
3978
3988
|
});
|
|
3979
|
-
proc.on("exit", (code) =>
|
|
3980
|
-
this.daemon.spawnExit(this.sessionId, code ?? 0);
|
|
3981
|
-
this.proc = null;
|
|
3982
|
-
});
|
|
3989
|
+
proc.on("exit", (code) => this.onProcExit(code ?? 0));
|
|
3983
3990
|
proc.on("error", (e) => this.daemon.spawnFailed(this.sessionId, String(e)));
|
|
3984
|
-
if (
|
|
3985
|
-
this.send(
|
|
3991
|
+
if (firstMessage.trim())
|
|
3992
|
+
this.send(firstMessage);
|
|
3986
3993
|
}
|
|
3987
3994
|
send(text) {
|
|
3988
|
-
if (
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
this.proc.stdin.write(JSON.stringify(msg) + `
|
|
3995
|
+
if (this.proc?.stdin.writable) {
|
|
3996
|
+
const msg = { type: "user", message: { role: "user", content: text } };
|
|
3997
|
+
this.proc.stdin.write(JSON.stringify(msg) + `
|
|
3992
3998
|
`);
|
|
3999
|
+
} else {
|
|
4000
|
+
this.spawnProc(text);
|
|
4001
|
+
}
|
|
4002
|
+
}
|
|
4003
|
+
onProcExit(_code) {
|
|
4004
|
+
this.proc = null;
|
|
4005
|
+
if (this.stopped)
|
|
4006
|
+
return;
|
|
4007
|
+
this.daemon.spawnState(this.sessionId, "waiting", "ready — send your next message");
|
|
3993
4008
|
}
|
|
3994
4009
|
stop() {
|
|
4010
|
+
this.stopped = true;
|
|
3995
4011
|
try {
|
|
3996
4012
|
this.proc?.stdin.end();
|
|
3997
4013
|
} catch {}
|
|
@@ -4009,8 +4025,11 @@ class SpawnedSession {
|
|
|
4009
4025
|
}
|
|
4010
4026
|
switch (m.type) {
|
|
4011
4027
|
case "system":
|
|
4012
|
-
if (m.subtype === "init")
|
|
4028
|
+
if (m.subtype === "init") {
|
|
4029
|
+
if (typeof m.session_id === "string")
|
|
4030
|
+
this.claudeSessionId = m.session_id;
|
|
4013
4031
|
this.daemon.spawnState(this.sessionId, "working", "session ready");
|
|
4032
|
+
}
|
|
4014
4033
|
break;
|
|
4015
4034
|
case "assistant": {
|
|
4016
4035
|
for (const block of m.message?.content ?? []) {
|
|
@@ -7263,6 +7282,9 @@ function lanIPv4() {
|
|
|
7263
7282
|
|
|
7264
7283
|
// src/server.ts
|
|
7265
7284
|
import { createServer } from "node:http";
|
|
7285
|
+
import { readdirSync, statSync, existsSync as existsSync2 } from "node:fs";
|
|
7286
|
+
import { homedir as homedir2 } from "node:os";
|
|
7287
|
+
import { join as join2, basename } from "node:path";
|
|
7266
7288
|
|
|
7267
7289
|
// ../../node_modules/ws/wrapper.mjs
|
|
7268
7290
|
var import_stream = __toESM(require_stream(), 1);
|
|
@@ -11343,6 +11365,11 @@ var RulesUpdate = exports_external.object({
|
|
|
11343
11365
|
t: exports_external.literal("rules"),
|
|
11344
11366
|
rules: exports_external.array(Rule)
|
|
11345
11367
|
});
|
|
11368
|
+
var DirEntry = exports_external.object({ path: exports_external.string(), name: exports_external.string(), git: exports_external.boolean().default(false) });
|
|
11369
|
+
var DirsUpdate = exports_external.object({
|
|
11370
|
+
t: exports_external.literal("dirs"),
|
|
11371
|
+
dirs: exports_external.array(DirEntry)
|
|
11372
|
+
});
|
|
11346
11373
|
var DaemonToPhone = exports_external.discriminatedUnion("t", [
|
|
11347
11374
|
ServerHello,
|
|
11348
11375
|
SessionUpsert,
|
|
@@ -11353,6 +11380,7 @@ var DaemonToPhone = exports_external.discriminatedUnion("t", [
|
|
|
11353
11380
|
ErrorMsg,
|
|
11354
11381
|
Pong,
|
|
11355
11382
|
RulesUpdate,
|
|
11383
|
+
DirsUpdate,
|
|
11356
11384
|
Encrypted
|
|
11357
11385
|
]);
|
|
11358
11386
|
var DaemonInner = exports_external.discriminatedUnion("t", [
|
|
@@ -11363,7 +11391,8 @@ var DaemonInner = exports_external.discriminatedUnion("t", [
|
|
|
11363
11391
|
PermissionResolved,
|
|
11364
11392
|
ErrorMsg,
|
|
11365
11393
|
Pong,
|
|
11366
|
-
RulesUpdate
|
|
11394
|
+
RulesUpdate,
|
|
11395
|
+
DirsUpdate
|
|
11367
11396
|
]);
|
|
11368
11397
|
var ClientHello = exports_external.object({
|
|
11369
11398
|
t: exports_external.literal("hello"),
|
|
@@ -11392,12 +11421,14 @@ var Spawn = exports_external.object({
|
|
|
11392
11421
|
t: exports_external.literal("spawn"),
|
|
11393
11422
|
task: exports_external.string().default(""),
|
|
11394
11423
|
cwd: exports_external.string().nullable().default(null),
|
|
11395
|
-
mode: SpawnMode.default("edits")
|
|
11424
|
+
mode: SpawnMode.default("edits"),
|
|
11425
|
+
model: exports_external.string().nullable().default(null)
|
|
11396
11426
|
});
|
|
11397
11427
|
var Stop = exports_external.object({
|
|
11398
11428
|
t: exports_external.literal("stop"),
|
|
11399
11429
|
sessionId: exports_external.string()
|
|
11400
11430
|
});
|
|
11431
|
+
var ListDirs = exports_external.object({ t: exports_external.literal("list_dirs") });
|
|
11401
11432
|
var Ping = exports_external.object({ t: exports_external.literal("ping") });
|
|
11402
11433
|
var PushRegister = exports_external.object({
|
|
11403
11434
|
t: exports_external.literal("push_register"),
|
|
@@ -11412,6 +11443,7 @@ var PhoneToDaemon = exports_external.discriminatedUnion("t", [
|
|
|
11412
11443
|
UserMessage,
|
|
11413
11444
|
Spawn,
|
|
11414
11445
|
Stop,
|
|
11446
|
+
ListDirs,
|
|
11415
11447
|
Ping,
|
|
11416
11448
|
PushRegister,
|
|
11417
11449
|
Encrypted
|
|
@@ -11422,6 +11454,7 @@ var PhoneInner = exports_external.discriminatedUnion("t", [
|
|
|
11422
11454
|
UserMessage,
|
|
11423
11455
|
Spawn,
|
|
11424
11456
|
Stop,
|
|
11457
|
+
ListDirs,
|
|
11425
11458
|
Ping,
|
|
11426
11459
|
PushRegister
|
|
11427
11460
|
]);
|
|
@@ -11579,7 +11612,7 @@ class Daemon {
|
|
|
11579
11612
|
relayLeave(conn) {
|
|
11580
11613
|
this.phones.delete(conn);
|
|
11581
11614
|
}
|
|
11582
|
-
async spawnSession(task, cwd, mode = "edits") {
|
|
11615
|
+
async spawnSession(task, cwd, mode = "edits", model = null) {
|
|
11583
11616
|
const { SpawnedSession: SpawnedSession2 } = await Promise.resolve().then(() => (init_spawn(), exports_spawn));
|
|
11584
11617
|
const id = `spawn-${crypto.randomUUID()}`;
|
|
11585
11618
|
const dir = cwd || this.defaultCwd;
|
|
@@ -11597,10 +11630,43 @@ class Daemon {
|
|
|
11597
11630
|
this.registry.appendLine(id, { text: `❯ ${task}`, color: "me" });
|
|
11598
11631
|
this.broadcast({ t: "line", sessionId: id, line: { text: `❯ ${task}`, color: "me" } });
|
|
11599
11632
|
}
|
|
11600
|
-
const session = new SpawnedSession2(this, id, dir);
|
|
11633
|
+
const session = new SpawnedSession2(this, id, dir, model);
|
|
11601
11634
|
this.spawned.set(id, session);
|
|
11602
11635
|
session.start(task, mode);
|
|
11603
11636
|
}
|
|
11637
|
+
listProjectDirs() {
|
|
11638
|
+
const home = homedir2();
|
|
11639
|
+
const roots = [home, ...["Projects", "code", "dev", "src", "repos", "Developer", "work"].map((d) => join2(home, d))];
|
|
11640
|
+
const out = new Map;
|
|
11641
|
+
const add2 = (p) => {
|
|
11642
|
+
if (out.has(p))
|
|
11643
|
+
return;
|
|
11644
|
+
out.set(p, { path: p, name: basename(p) || p, git: existsSync2(join2(p, ".git")) });
|
|
11645
|
+
};
|
|
11646
|
+
add2(this.defaultCwd);
|
|
11647
|
+
for (const root of roots) {
|
|
11648
|
+
let entries = [];
|
|
11649
|
+
try {
|
|
11650
|
+
entries = readdirSync(root);
|
|
11651
|
+
} catch {
|
|
11652
|
+
continue;
|
|
11653
|
+
}
|
|
11654
|
+
for (const e of entries) {
|
|
11655
|
+
if (e.startsWith(".") || out.size > 80)
|
|
11656
|
+
continue;
|
|
11657
|
+
const p = join2(root, e);
|
|
11658
|
+
try {
|
|
11659
|
+
if (!statSync(p).isDirectory())
|
|
11660
|
+
continue;
|
|
11661
|
+
} catch {
|
|
11662
|
+
continue;
|
|
11663
|
+
}
|
|
11664
|
+
if (root !== home || existsSync2(join2(p, ".git")))
|
|
11665
|
+
add2(p);
|
|
11666
|
+
}
|
|
11667
|
+
}
|
|
11668
|
+
return [...out.values()].sort((a, b) => Number(b.git) - Number(a.git) || a.name.localeCompare(b.name)).slice(0, 50);
|
|
11669
|
+
}
|
|
11604
11670
|
spawnLine(sessionId, text, color) {
|
|
11605
11671
|
this.registry.appendLine(sessionId, { text, color });
|
|
11606
11672
|
this.broadcast({ t: "line", sessionId, line: { text, color } });
|
|
@@ -11777,9 +11843,12 @@ class Daemon {
|
|
|
11777
11843
|
break;
|
|
11778
11844
|
}
|
|
11779
11845
|
case "spawn": {
|
|
11780
|
-
this.spawnSession(msg.task ?? "", msg.cwd ?? null, msg.mode ?? "edits");
|
|
11846
|
+
this.spawnSession(msg.task ?? "", msg.cwd ?? null, msg.mode ?? "edits", msg.model ?? null);
|
|
11781
11847
|
break;
|
|
11782
11848
|
}
|
|
11849
|
+
case "list_dirs":
|
|
11850
|
+
this.sendTo(p, { t: "dirs", dirs: this.listProjectDirs() });
|
|
11851
|
+
break;
|
|
11783
11852
|
case "stop": {
|
|
11784
11853
|
const owned = this.spawned.get(msg.sessionId);
|
|
11785
11854
|
if (owned) {
|
|
@@ -11942,7 +12011,8 @@ class Daemon {
|
|
|
11942
12011
|
});
|
|
11943
12012
|
const hooks = createServer((req, res) => {
|
|
11944
12013
|
const url = req.url ?? "";
|
|
11945
|
-
const
|
|
12014
|
+
const ctEssence = String(req.headers["content-type"] ?? "").split(";")[0].trim().toLowerCase();
|
|
12015
|
+
const jsonPost = req.method === "POST" && ctEssence === "application/json";
|
|
11946
12016
|
if (url === "/pair") {
|
|
11947
12017
|
if (!jsonPost) {
|
|
11948
12018
|
res.writeHead(415).end("expected application/json");
|
|
@@ -12107,16 +12177,16 @@ function b64url2(bytes) {
|
|
|
12107
12177
|
}
|
|
12108
12178
|
|
|
12109
12179
|
// src/hooks.ts
|
|
12110
|
-
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as
|
|
12111
|
-
import { homedir as
|
|
12112
|
-
import { join as
|
|
12113
|
-
var claudeDir = () => process.env.CLAUDE_CONFIG_DIR ||
|
|
12114
|
-
var settingsPath = () =>
|
|
12180
|
+
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync3, mkdirSync as mkdirSync2 } from "node:fs";
|
|
12181
|
+
import { homedir as homedir3 } from "node:os";
|
|
12182
|
+
import { join as join3 } from "node:path";
|
|
12183
|
+
var claudeDir = () => process.env.CLAUDE_CONFIG_DIR || join3(homedir3(), ".claude");
|
|
12184
|
+
var settingsPath = () => join3(claudeDir(), "settings.json");
|
|
12115
12185
|
var EVENTS = ["SessionStart", "UserPromptSubmit", "PreToolUse", "PostToolUse", "Notification", "Stop"];
|
|
12116
12186
|
function installHooks(droverBin) {
|
|
12117
12187
|
mkdirSync2(claudeDir(), { recursive: true });
|
|
12118
12188
|
let settings = {};
|
|
12119
|
-
if (
|
|
12189
|
+
if (existsSync3(settingsPath())) {
|
|
12120
12190
|
try {
|
|
12121
12191
|
settings = JSON.parse(readFileSync2(settingsPath(), "utf8"));
|
|
12122
12192
|
} catch {
|
|
@@ -12153,7 +12223,7 @@ function isOurs(x) {
|
|
|
12153
12223
|
return cmd.includes("hatchee") || cmd.includes("drover") || cmd.includes("daemon/src/cli.ts");
|
|
12154
12224
|
}
|
|
12155
12225
|
function uninstallHooks() {
|
|
12156
|
-
if (!
|
|
12226
|
+
if (!existsSync3(settingsPath()))
|
|
12157
12227
|
return;
|
|
12158
12228
|
try {
|
|
12159
12229
|
const settings = JSON.parse(readFileSync2(settingsPath(), "utf8"));
|
|
@@ -12218,31 +12288,30 @@ async function runHook(hookPort) {
|
|
|
12218
12288
|
}
|
|
12219
12289
|
|
|
12220
12290
|
// src/service.ts
|
|
12221
|
-
import { homedir as
|
|
12222
|
-
import { join as
|
|
12223
|
-
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync3, copyFileSync, chmodSync, existsSync as
|
|
12291
|
+
import { homedir as homedir4 } from "node:os";
|
|
12292
|
+
import { join as join4, dirname } from "node:path";
|
|
12293
|
+
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync3, copyFileSync, chmodSync, existsSync as existsSync4, rmSync } from "node:fs";
|
|
12224
12294
|
import { execFileSync } from "node:child_process";
|
|
12225
|
-
var HOME =
|
|
12226
|
-
var HATCHEE_DIR =
|
|
12227
|
-
var BIN_DIR =
|
|
12228
|
-
var STABLE_BIN =
|
|
12229
|
-
var LOG =
|
|
12295
|
+
var HOME = homedir4();
|
|
12296
|
+
var HATCHEE_DIR = join4(HOME, ".hatchee");
|
|
12297
|
+
var BIN_DIR = join4(HATCHEE_DIR, "bin");
|
|
12298
|
+
var STABLE_BIN = join4(BIN_DIR, "hatchee.mjs");
|
|
12299
|
+
var LOG = join4(HATCHEE_DIR, "daemon.log");
|
|
12230
12300
|
var LABEL = "cloud.hatchee.daemon";
|
|
12231
|
-
var PLIST =
|
|
12232
|
-
var UNIT =
|
|
12301
|
+
var PLIST = join4(HOME, "Library", "LaunchAgents", `${LABEL}.plist`);
|
|
12302
|
+
var UNIT = join4(HOME, ".config", "systemd", "user", "hatchee.service");
|
|
12233
12303
|
function servicePath(node) {
|
|
12234
|
-
|
|
12304
|
+
const system = ["/usr/bin", "/bin", "/usr/sbin", "/sbin"];
|
|
12305
|
+
const userTool = [
|
|
12235
12306
|
dirname(node),
|
|
12236
|
-
"/usr/bin",
|
|
12237
|
-
"/bin",
|
|
12238
|
-
"/usr/sbin",
|
|
12239
|
-
"/sbin",
|
|
12240
12307
|
"/opt/homebrew/bin",
|
|
12241
12308
|
"/usr/local/bin",
|
|
12242
|
-
|
|
12243
|
-
|
|
12244
|
-
|
|
12245
|
-
]
|
|
12309
|
+
join4(HOME, ".bun/bin"),
|
|
12310
|
+
join4(HOME, ".npm-global/bin"),
|
|
12311
|
+
join4(HOME, ".local/bin")
|
|
12312
|
+
];
|
|
12313
|
+
const seen = new Set;
|
|
12314
|
+
return [...system, ...userTool].filter((p) => p && !seen.has(p) && seen.add(p)).join(":");
|
|
12246
12315
|
}
|
|
12247
12316
|
function xmlEsc(s) {
|
|
12248
12317
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
@@ -12390,12 +12459,12 @@ function uninstallService() {
|
|
|
12390
12459
|
if (process.platform === "darwin") {
|
|
12391
12460
|
const uid = String(process.getuid?.() ?? "");
|
|
12392
12461
|
run("launchctl", ["bootout", `gui/${uid}/${LABEL}`]);
|
|
12393
|
-
if (
|
|
12462
|
+
if (existsSync4(PLIST))
|
|
12394
12463
|
rmSync(PLIST);
|
|
12395
12464
|
console.log(` ✓ background service removed (launchd: ${LABEL})`);
|
|
12396
12465
|
} else if (process.platform === "linux") {
|
|
12397
12466
|
run("systemctl", ["--user", "disable", "--now", "hatchee.service"]);
|
|
12398
|
-
if (
|
|
12467
|
+
if (existsSync4(UNIT)) {
|
|
12399
12468
|
rmSync(UNIT);
|
|
12400
12469
|
run("systemctl", ["--user", "daemon-reload"]);
|
|
12401
12470
|
}
|
|
@@ -12403,12 +12472,12 @@ function uninstallService() {
|
|
|
12403
12472
|
} else {
|
|
12404
12473
|
console.log(` no background service on ${process.platform}.`);
|
|
12405
12474
|
}
|
|
12406
|
-
if (
|
|
12475
|
+
if (existsSync4(STABLE_BIN))
|
|
12407
12476
|
rmSync(STABLE_BIN);
|
|
12408
12477
|
}
|
|
12409
12478
|
|
|
12410
12479
|
// src/cli.ts
|
|
12411
|
-
var VERSION2 = "0.1.
|
|
12480
|
+
var VERSION2 = "0.1.6";
|
|
12412
12481
|
var cmd = process.argv[2] ?? "help";
|
|
12413
12482
|
switch (cmd) {
|
|
12414
12483
|
case "up": {
|
package/package.json
CHANGED