offhands 0.1.2 → 0.1.4
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/daemon.mjs +420 -86
- package/package.json +1 -1
package/dist/daemon.mjs
CHANGED
|
@@ -4777,7 +4777,7 @@ var require_main = __commonJS({
|
|
|
4777
4777
|
|
|
4778
4778
|
// ../daemon/src/index.ts
|
|
4779
4779
|
import { resolve as resolve5 } from "node:path";
|
|
4780
|
-
import { existsSync as
|
|
4780
|
+
import { existsSync as existsSync9 } from "node:fs";
|
|
4781
4781
|
|
|
4782
4782
|
// ../daemon/src/runners/claude-code.ts
|
|
4783
4783
|
import { spawn } from "node:child_process";
|
|
@@ -4897,11 +4897,11 @@ function contextUsage(usage, costUsd) {
|
|
|
4897
4897
|
function summariseToolInput(name, input) {
|
|
4898
4898
|
if (typeof input !== "object" || input === null) return name;
|
|
4899
4899
|
const i2 = input;
|
|
4900
|
-
const
|
|
4900
|
+
const firstString3 = (...keys) => {
|
|
4901
4901
|
for (const k2 of keys) if (typeof i2[k2] === "string" && i2[k2] !== "") return i2[k2];
|
|
4902
4902
|
return void 0;
|
|
4903
4903
|
};
|
|
4904
|
-
const hint =
|
|
4904
|
+
const hint = firstString3("file_path", "path", "command", "pattern", "query", "url", "description") ?? "";
|
|
4905
4905
|
return hint ? `${name}: ${truncate(hint, 120)}` : name;
|
|
4906
4906
|
}
|
|
4907
4907
|
function truncate(s2, max) {
|
|
@@ -5297,11 +5297,343 @@ stderr: ${stderrTail}` : ""}`
|
|
|
5297
5297
|
}
|
|
5298
5298
|
};
|
|
5299
5299
|
|
|
5300
|
+
// ../daemon/src/runners/opencode-cli.ts
|
|
5301
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
5302
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
5303
|
+
import { homedir as homedir3 } from "node:os";
|
|
5304
|
+
import { delimiter as delimiter2, join as join3 } from "node:path";
|
|
5305
|
+
|
|
5306
|
+
// ../daemon/src/runners/opencode-map.ts
|
|
5307
|
+
function mapOpenCodeEvent(value) {
|
|
5308
|
+
if (typeof value !== "object" || value === null) return [];
|
|
5309
|
+
const v2 = value;
|
|
5310
|
+
const type = typeof v2.type === "string" ? v2.type : "";
|
|
5311
|
+
const part = v2.part ?? {};
|
|
5312
|
+
const partType = typeof part.type === "string" ? part.type : "";
|
|
5313
|
+
if (type === "text") {
|
|
5314
|
+
const text = typeof part.text === "string" ? part.text : "";
|
|
5315
|
+
return text ? [{ type: "text", chunk: text }] : [];
|
|
5316
|
+
}
|
|
5317
|
+
if (type === "thinking" || partType === "thinking") {
|
|
5318
|
+
const thinking = typeof part.thinking === "string" ? part.thinking : typeof part.text === "string" ? part.text : "";
|
|
5319
|
+
return thinking ? [{ type: "thinking", chunk: thinking }] : [];
|
|
5320
|
+
}
|
|
5321
|
+
if (type === "tool_call" || partType === "tool_call") {
|
|
5322
|
+
const name = firstString2(part, "toolName", "name", "tool") ?? "tool";
|
|
5323
|
+
const input = part.input;
|
|
5324
|
+
const summary = input ? firstString2(input, "command", "path", "file", "summary", "description", "query") ?? "" : "";
|
|
5325
|
+
return [{ type: "tool", name, summary: summary ? `${name}: ${truncate3(summary, 120)}` : name }];
|
|
5326
|
+
}
|
|
5327
|
+
if (type === "step_start" || partType === "step-start") {
|
|
5328
|
+
return [];
|
|
5329
|
+
}
|
|
5330
|
+
if (type === "step_finish" || partType === "step-finish") {
|
|
5331
|
+
const reason = typeof part.reason === "string" ? part.reason : "stop";
|
|
5332
|
+
const isError = reason === "error" || reason === "failed";
|
|
5333
|
+
const usage = extractUsageFromPart(part);
|
|
5334
|
+
if (isError) {
|
|
5335
|
+
const message = typeof part.error === "string" ? part.error : "run failed";
|
|
5336
|
+
const events2 = [{ type: "error", message }];
|
|
5337
|
+
if (usage) events2.push(usage);
|
|
5338
|
+
return events2;
|
|
5339
|
+
}
|
|
5340
|
+
const events = [{ type: "done", summary: "" }];
|
|
5341
|
+
if (usage) events.push(usage);
|
|
5342
|
+
return events;
|
|
5343
|
+
}
|
|
5344
|
+
if (type === "permission_request" || type === "permission.request") {
|
|
5345
|
+
const partData = part.data;
|
|
5346
|
+
const id = firstString2(partData, "id", "permissionId", "id") ?? crypto.randomUUID();
|
|
5347
|
+
const action = firstString2(partData, "toolName", "action", "command") ?? "action";
|
|
5348
|
+
const detail = firstString2(partData, "detail", "description", "message") ?? "";
|
|
5349
|
+
const risk = firstString2(partData, "risk") === "high" ? "high" : "low";
|
|
5350
|
+
const preview = firstString2(partData, "preview", "diff", "command") ?? void 0;
|
|
5351
|
+
const question = partData?.question ? mapQuestion(partData.question) : void 0;
|
|
5352
|
+
return [{
|
|
5353
|
+
type: "approval",
|
|
5354
|
+
id,
|
|
5355
|
+
action,
|
|
5356
|
+
detail,
|
|
5357
|
+
risk,
|
|
5358
|
+
preview,
|
|
5359
|
+
question
|
|
5360
|
+
}];
|
|
5361
|
+
}
|
|
5362
|
+
if (type === "error") {
|
|
5363
|
+
const message = firstString2(v2, "message", "error", "detail", "result") ?? firstString2(part, "message", "error") ?? "unknown error";
|
|
5364
|
+
return [{ type: "error", message }];
|
|
5365
|
+
}
|
|
5366
|
+
if (type === "session_created" || type === "session.updated" || type === "session.created") {
|
|
5367
|
+
return [];
|
|
5368
|
+
}
|
|
5369
|
+
return [];
|
|
5370
|
+
}
|
|
5371
|
+
function extractOpenCodeSessionId(value) {
|
|
5372
|
+
if (typeof value !== "object" || value === null) return null;
|
|
5373
|
+
const v2 = value;
|
|
5374
|
+
const part = v2.part ?? {};
|
|
5375
|
+
return firstString2(v2, "sessionId", "session_id", "sessionID", "id") ?? firstString2(part, "sessionId", "session_id", "sessionID", "id") ?? null;
|
|
5376
|
+
}
|
|
5377
|
+
function extractUsageFromPart(part) {
|
|
5378
|
+
const tokens = part.tokens;
|
|
5379
|
+
const costUsd = typeof part.cost === "number" ? part.cost : typeof part.costUsd === "number" ? part.costUsd : void 0;
|
|
5380
|
+
if (!tokens && costUsd === void 0) return null;
|
|
5381
|
+
const n2 = (k2) => typeof tokens?.[k2] === "number" ? tokens[k2] : 0;
|
|
5382
|
+
const contextTokens = n2("total") + n2("input") + n2("inputTokens") + n2("input_tokens") + n2("cacheReadInputTokens") + n2("cache_read_input_tokens") + n2("cacheCreationInputTokens") + n2("cache_creation_input_tokens") + n2("output") + n2("outputTokens") + n2("output_tokens") + n2("reasoning") + n2("reasoningTokens") + n2("reasoning_tokens");
|
|
5383
|
+
if (contextTokens <= 0 && costUsd === void 0) return null;
|
|
5384
|
+
return {
|
|
5385
|
+
type: "usage",
|
|
5386
|
+
contextTokens,
|
|
5387
|
+
contextWindow: 2e5,
|
|
5388
|
+
...costUsd !== void 0 ? { costUsd } : {}
|
|
5389
|
+
};
|
|
5390
|
+
}
|
|
5391
|
+
function mapQuestion(q2) {
|
|
5392
|
+
if (typeof q2 !== "object" || q2 === null) return void 0;
|
|
5393
|
+
const qq = q2;
|
|
5394
|
+
const text = typeof qq.text === "string" ? qq.text : "";
|
|
5395
|
+
const options = Array.isArray(qq.options) ? qq.options.filter((o2) => typeof o2 === "object" && o2 !== null).map((o2) => ({
|
|
5396
|
+
label: typeof o2.label === "string" ? o2.label : "",
|
|
5397
|
+
description: typeof o2.description === "string" ? o2.description : void 0
|
|
5398
|
+
})) : [];
|
|
5399
|
+
const multiSelect = typeof qq.multiSelect === "boolean" ? qq.multiSelect : void 0;
|
|
5400
|
+
if (!text && options.length === 0) return void 0;
|
|
5401
|
+
return { text, options, multiSelect };
|
|
5402
|
+
}
|
|
5403
|
+
function firstString2(obj, ...keys) {
|
|
5404
|
+
for (const k2 of keys) {
|
|
5405
|
+
const val = obj[k2];
|
|
5406
|
+
if (typeof val === "string" && val !== "") return val;
|
|
5407
|
+
}
|
|
5408
|
+
return void 0;
|
|
5409
|
+
}
|
|
5410
|
+
function truncate3(s2, max) {
|
|
5411
|
+
return s2.length <= max ? s2 : s2.slice(0, max - 1) + "\u2026";
|
|
5412
|
+
}
|
|
5413
|
+
|
|
5414
|
+
// ../daemon/src/runners/opencode-cli.ts
|
|
5415
|
+
var OpenCodeRunner = class {
|
|
5416
|
+
id = "opencode";
|
|
5417
|
+
name = "OpenCode";
|
|
5418
|
+
supportsApprovals = true;
|
|
5419
|
+
models = [];
|
|
5420
|
+
// populated dynamically via `opencode models`
|
|
5421
|
+
/** [command, ...prefixArgs] resolved once. */
|
|
5422
|
+
resolved = null;
|
|
5423
|
+
modelsCache = [];
|
|
5424
|
+
resolveCommand() {
|
|
5425
|
+
if (this.resolved) return this.resolved;
|
|
5426
|
+
if (process.platform === "win32") {
|
|
5427
|
+
const pathDirs = (process.env.PATH ?? "").split(delimiter2);
|
|
5428
|
+
const npmGlobalPaths = [
|
|
5429
|
+
process.env.APPDATA ? join3(process.env.APPDATA, "npm") : null,
|
|
5430
|
+
process.env.LOCALAPPDATA ? join3(process.env.LOCALAPPDATA, "npm") : null,
|
|
5431
|
+
"C:\\nvm4w\\nodejs",
|
|
5432
|
+
"C:\\Program Files\\nodejs",
|
|
5433
|
+
"C:\\Program Files (x86)\\nodejs"
|
|
5434
|
+
].filter((p2) => p2 !== null && existsSync3(p2));
|
|
5435
|
+
const allDirs = [...pathDirs, ...npmGlobalPaths];
|
|
5436
|
+
for (const pathDir of allDirs) {
|
|
5437
|
+
if (!pathDir) continue;
|
|
5438
|
+
const cmdPath = join3(pathDir, "opencode.cmd");
|
|
5439
|
+
if (existsSync3(cmdPath)) {
|
|
5440
|
+
this.resolved = [cmdPath];
|
|
5441
|
+
return this.resolved;
|
|
5442
|
+
}
|
|
5443
|
+
const nodeModulesBin = join3(pathDir, "node_modules", ".bin", "opencode.cmd");
|
|
5444
|
+
if (existsSync3(nodeModulesBin)) {
|
|
5445
|
+
this.resolved = [nodeModulesBin];
|
|
5446
|
+
return this.resolved;
|
|
5447
|
+
}
|
|
5448
|
+
for (const bin of ["bun", "pnpm", "yarn"]) {
|
|
5449
|
+
const binPath = join3(pathDir, bin + ".cmd");
|
|
5450
|
+
if (existsSync3(binPath)) {
|
|
5451
|
+
this.resolved = [binPath, "exec", "opencode"];
|
|
5452
|
+
return this.resolved;
|
|
5453
|
+
}
|
|
5454
|
+
}
|
|
5455
|
+
}
|
|
5456
|
+
const native = join3(homedir3(), "AppData", "Local", "opencode", "opencode.exe");
|
|
5457
|
+
if (existsSync3(native)) {
|
|
5458
|
+
this.resolved = [native];
|
|
5459
|
+
return this.resolved;
|
|
5460
|
+
}
|
|
5461
|
+
return null;
|
|
5462
|
+
}
|
|
5463
|
+
this.resolved = ["opencode"];
|
|
5464
|
+
return this.resolved;
|
|
5465
|
+
}
|
|
5466
|
+
async detect() {
|
|
5467
|
+
const cmd = this.resolveCommand();
|
|
5468
|
+
if (!cmd) return false;
|
|
5469
|
+
await this.fetchModels();
|
|
5470
|
+
return new Promise((resolve6) => {
|
|
5471
|
+
const p2 = spawn4(cmd[0], [...cmd.slice(1), "--version"], { stdio: "ignore", shell: process.platform === "win32" });
|
|
5472
|
+
p2.on("error", () => resolve6(false));
|
|
5473
|
+
p2.on("exit", (code) => resolve6(code === 0));
|
|
5474
|
+
});
|
|
5475
|
+
}
|
|
5476
|
+
async fetchModels() {
|
|
5477
|
+
if (this.modelsCache.length > 0) return this.modelsCache;
|
|
5478
|
+
const cmd = this.resolveCommand();
|
|
5479
|
+
if (!cmd) return [];
|
|
5480
|
+
return new Promise((resolve6) => {
|
|
5481
|
+
const p2 = spawn4(cmd[0], [...cmd.slice(1), "models"], {
|
|
5482
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
5483
|
+
shell: process.platform === "win32"
|
|
5484
|
+
});
|
|
5485
|
+
let output = "";
|
|
5486
|
+
p2.stdout.setEncoding("utf8");
|
|
5487
|
+
p2.stdout.on("data", (chunk) => {
|
|
5488
|
+
output += chunk;
|
|
5489
|
+
});
|
|
5490
|
+
p2.on("close", (code) => {
|
|
5491
|
+
if (code === 0) {
|
|
5492
|
+
try {
|
|
5493
|
+
const lines = output.trim().split("\n");
|
|
5494
|
+
const models = lines.map((l2) => l2.trim()).filter((l2) => l2 && !l2.startsWith("opencode/"));
|
|
5495
|
+
this.modelsCache = models;
|
|
5496
|
+
Object.defineProperty(this, "models", {
|
|
5497
|
+
value: models,
|
|
5498
|
+
writable: false,
|
|
5499
|
+
configurable: true
|
|
5500
|
+
});
|
|
5501
|
+
resolve6(models);
|
|
5502
|
+
} catch {
|
|
5503
|
+
resolve6([]);
|
|
5504
|
+
}
|
|
5505
|
+
} else {
|
|
5506
|
+
resolve6([]);
|
|
5507
|
+
}
|
|
5508
|
+
});
|
|
5509
|
+
p2.on("error", () => resolve6([]));
|
|
5510
|
+
});
|
|
5511
|
+
}
|
|
5512
|
+
loggedIn() {
|
|
5513
|
+
return existsSync3(join3(homedir3(), ".local", "share", "opencode", "auth.json"));
|
|
5514
|
+
}
|
|
5515
|
+
start(run, callbacks) {
|
|
5516
|
+
const queue = new AsyncEventQueue();
|
|
5517
|
+
const parser = new NdjsonParser();
|
|
5518
|
+
let child;
|
|
5519
|
+
let stderrTail = "";
|
|
5520
|
+
let sawTerminal = false;
|
|
5521
|
+
let reportedConversation = false;
|
|
5522
|
+
const emit = (events) => {
|
|
5523
|
+
for (const e of events) {
|
|
5524
|
+
if (e.type === "done" || e.type === "error") sawTerminal = true;
|
|
5525
|
+
queue.push(e);
|
|
5526
|
+
}
|
|
5527
|
+
};
|
|
5528
|
+
const cmd = this.resolveCommand();
|
|
5529
|
+
if (!cmd) {
|
|
5530
|
+
queue.push({ type: "error", message: "opencode CLI not found" });
|
|
5531
|
+
queue.close();
|
|
5532
|
+
return { events: queue, respond: () => {
|
|
5533
|
+
}, cancel: () => {
|
|
5534
|
+
} };
|
|
5535
|
+
}
|
|
5536
|
+
const args2 = [
|
|
5537
|
+
...cmd.slice(1),
|
|
5538
|
+
"run",
|
|
5539
|
+
"--format",
|
|
5540
|
+
"json",
|
|
5541
|
+
"--no-color"
|
|
5542
|
+
];
|
|
5543
|
+
if (run.model) {
|
|
5544
|
+
args2.push("--model", run.model);
|
|
5545
|
+
}
|
|
5546
|
+
if (run.resumeConversationId) {
|
|
5547
|
+
args2.push("--session", run.resumeConversationId);
|
|
5548
|
+
}
|
|
5549
|
+
const mode = run.permissionMode ?? "guarded";
|
|
5550
|
+
if (mode === "bypass") {
|
|
5551
|
+
args2.push("--auto");
|
|
5552
|
+
} else if (mode === "plan") {
|
|
5553
|
+
args2.push("--agent", "plan");
|
|
5554
|
+
} else {
|
|
5555
|
+
}
|
|
5556
|
+
if (run.effort) {
|
|
5557
|
+
const variantMap = { low: "minimal", medium: "high", high: "max", max: "max" };
|
|
5558
|
+
const variant = variantMap[run.effort];
|
|
5559
|
+
if (variant) {
|
|
5560
|
+
args2.push("--variant", variant);
|
|
5561
|
+
args2.push("--thinking");
|
|
5562
|
+
}
|
|
5563
|
+
}
|
|
5564
|
+
if (run.attachments?.length) {
|
|
5565
|
+
for (const file of run.attachments) {
|
|
5566
|
+
args2.push("--file", file);
|
|
5567
|
+
}
|
|
5568
|
+
}
|
|
5569
|
+
args2.push("--dir", run.workspace);
|
|
5570
|
+
const promptArg = process.platform === "win32" ? `"${run.prompt.replace(/"/g, '\\"')}"` : run.prompt;
|
|
5571
|
+
args2.push(promptArg);
|
|
5572
|
+
try {
|
|
5573
|
+
child = spawn4(cmd[0], args2, {
|
|
5574
|
+
cwd: run.workspace,
|
|
5575
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
5576
|
+
shell: process.platform === "win32"
|
|
5577
|
+
});
|
|
5578
|
+
} catch (e) {
|
|
5579
|
+
queue.push({ type: "error", message: `failed to spawn opencode: ${String(e)}` });
|
|
5580
|
+
queue.close();
|
|
5581
|
+
return { events: queue, respond: () => {
|
|
5582
|
+
}, cancel: () => {
|
|
5583
|
+
} };
|
|
5584
|
+
}
|
|
5585
|
+
child.stdout.setEncoding("utf8");
|
|
5586
|
+
child.stdout.on("data", (chunk) => {
|
|
5587
|
+
for (const result of parser.push(chunk)) {
|
|
5588
|
+
if (result.ok) {
|
|
5589
|
+
if (!reportedConversation) {
|
|
5590
|
+
const id = extractOpenCodeSessionId(result.value);
|
|
5591
|
+
if (id) {
|
|
5592
|
+
reportedConversation = true;
|
|
5593
|
+
callbacks?.onConversationId?.(id);
|
|
5594
|
+
}
|
|
5595
|
+
}
|
|
5596
|
+
emit(mapOpenCodeEvent(result.value));
|
|
5597
|
+
}
|
|
5598
|
+
}
|
|
5599
|
+
});
|
|
5600
|
+
child.stderr.setEncoding("utf8");
|
|
5601
|
+
child.stderr.on("data", (chunk) => {
|
|
5602
|
+
stderrTail = (stderrTail + chunk).slice(-2e3);
|
|
5603
|
+
});
|
|
5604
|
+
child.on("error", (err) => {
|
|
5605
|
+
emit([{ type: "error", message: `opencode process error: ${err.message}` }]);
|
|
5606
|
+
queue.close();
|
|
5607
|
+
});
|
|
5608
|
+
child.on("close", (code) => {
|
|
5609
|
+
for (const result of parser.flush()) {
|
|
5610
|
+
if (result.ok) emit(mapOpenCodeEvent(result.value));
|
|
5611
|
+
}
|
|
5612
|
+
if (!sawTerminal) {
|
|
5613
|
+
emit([
|
|
5614
|
+
{
|
|
5615
|
+
type: "error",
|
|
5616
|
+
message: `opencode exited with code ${code ?? "unknown"} before a result${stderrTail ? `
|
|
5617
|
+
stderr: ${stderrTail}` : ""}`
|
|
5618
|
+
}
|
|
5619
|
+
]);
|
|
5620
|
+
}
|
|
5621
|
+
queue.close();
|
|
5622
|
+
});
|
|
5623
|
+
return {
|
|
5624
|
+
events: queue,
|
|
5625
|
+
respond: (approvalId, ok, answer) => {
|
|
5626
|
+
},
|
|
5627
|
+
cancel: () => child.kill()
|
|
5628
|
+
};
|
|
5629
|
+
}
|
|
5630
|
+
};
|
|
5631
|
+
|
|
5300
5632
|
// ../daemon/src/session-manager.ts
|
|
5301
5633
|
import { randomUUID } from "node:crypto";
|
|
5302
5634
|
import { hostname, platform, tmpdir } from "node:os";
|
|
5303
|
-
import { existsSync as
|
|
5304
|
-
import { dirname as dirname2, join as
|
|
5635
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync2, readdirSync as readdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
5636
|
+
import { dirname as dirname2, join as join6, basename as basename3, parse as parse2, resolve as resolve3 } from "node:path";
|
|
5305
5637
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
5306
5638
|
|
|
5307
5639
|
// ../daemon/src/receipts.ts
|
|
@@ -5366,25 +5698,25 @@ function shouldScreenshot(touchedFiles, devUrl2) {
|
|
|
5366
5698
|
}
|
|
5367
5699
|
|
|
5368
5700
|
// ../daemon/src/claude-history.ts
|
|
5369
|
-
import { existsSync as
|
|
5370
|
-
import { homedir as
|
|
5371
|
-
import { basename, join as
|
|
5372
|
-
var DEFAULT_PROJECTS_DIR =
|
|
5701
|
+
import { existsSync as existsSync4, readdirSync, readFileSync, statSync } from "node:fs";
|
|
5702
|
+
import { homedir as homedir4 } from "node:os";
|
|
5703
|
+
import { basename, join as join4, resolve } from "node:path";
|
|
5704
|
+
var DEFAULT_PROJECTS_DIR = join4(homedir4(), ".claude", "projects");
|
|
5373
5705
|
function listClaudeConversations(workspace, projectsDir = process.env.OFFHAND_CLAUDE_PROJECTS_DIR ?? DEFAULT_PROJECTS_DIR) {
|
|
5374
5706
|
const projectDir = findProjectDir(workspace, projectsDir);
|
|
5375
5707
|
if (!projectDir) return [];
|
|
5376
5708
|
return listJsonlFiles(projectDir).map((path) => summarizeConversation(path, workspace)).filter((c2) => c2 !== null).sort((a2, b2) => b2.lastActiveMs - a2.lastActiveMs).slice(0, 50);
|
|
5377
5709
|
}
|
|
5378
5710
|
function findProjectDir(workspace, projectsDir) {
|
|
5379
|
-
if (!
|
|
5711
|
+
if (!existsSync4(projectsDir)) return null;
|
|
5380
5712
|
const candidates = /* @__PURE__ */ new Set([encodeWorkspace(workspace), encodeWorkspace(resolve(workspace))]);
|
|
5381
5713
|
for (const candidate of candidates) {
|
|
5382
|
-
const exact =
|
|
5383
|
-
if (
|
|
5714
|
+
const exact = join4(projectsDir, candidate);
|
|
5715
|
+
if (existsSync4(exact)) return exact;
|
|
5384
5716
|
}
|
|
5385
5717
|
const lower = new Set([...candidates].map((c2) => c2.toLowerCase()));
|
|
5386
5718
|
for (const entry of safeReadDir(projectsDir)) {
|
|
5387
|
-
if (entry.isDirectory() && lower.has(entry.name.toLowerCase())) return
|
|
5719
|
+
if (entry.isDirectory() && lower.has(entry.name.toLowerCase())) return join4(projectsDir, entry.name);
|
|
5388
5720
|
}
|
|
5389
5721
|
return null;
|
|
5390
5722
|
}
|
|
@@ -5392,12 +5724,12 @@ function encodeWorkspace(workspace) {
|
|
|
5392
5724
|
return workspace.replace(/[^A-Za-z0-9]/g, "-");
|
|
5393
5725
|
}
|
|
5394
5726
|
function listJsonlFiles(projectDir) {
|
|
5395
|
-
const files = safeReadDir(projectDir).filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl")).map((entry) =>
|
|
5396
|
-
const sessionsDir =
|
|
5397
|
-
if (!
|
|
5727
|
+
const files = safeReadDir(projectDir).filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl")).map((entry) => join4(projectDir, entry.name));
|
|
5728
|
+
const sessionsDir = join4(projectDir, "sessions");
|
|
5729
|
+
if (!existsSync4(sessionsDir)) return files;
|
|
5398
5730
|
return [
|
|
5399
5731
|
...files,
|
|
5400
|
-
...safeReadDir(sessionsDir).filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl")).map((entry) =>
|
|
5732
|
+
...safeReadDir(sessionsDir).filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl")).map((entry) => join4(sessionsDir, entry.name))
|
|
5401
5733
|
];
|
|
5402
5734
|
}
|
|
5403
5735
|
function summarizeConversation(path, workspace) {
|
|
@@ -5416,7 +5748,7 @@ function summarizeConversation(path, workspace) {
|
|
|
5416
5748
|
return {
|
|
5417
5749
|
conversationId: basename(path, ".jsonl"),
|
|
5418
5750
|
workspace,
|
|
5419
|
-
firstPrompt:
|
|
5751
|
+
firstPrompt: truncate4(firstPrompt || "(empty prompt)", 120),
|
|
5420
5752
|
lastActiveMs: Math.floor(statSync(path).mtimeMs),
|
|
5421
5753
|
messageCount
|
|
5422
5754
|
};
|
|
@@ -5442,7 +5774,7 @@ function userMessageText(value) {
|
|
|
5442
5774
|
function collapseText(text) {
|
|
5443
5775
|
return text.replace(/\s+/g, " ").trim();
|
|
5444
5776
|
}
|
|
5445
|
-
function
|
|
5777
|
+
function truncate4(text, max) {
|
|
5446
5778
|
return text.length <= max ? text : `${text.slice(0, max - 1)}\u2026`;
|
|
5447
5779
|
}
|
|
5448
5780
|
function safeReadDir(path) {
|
|
@@ -5459,7 +5791,7 @@ function isRecord(value) {
|
|
|
5459
5791
|
// ../daemon/src/drop.ts
|
|
5460
5792
|
import {
|
|
5461
5793
|
copyFileSync,
|
|
5462
|
-
existsSync as
|
|
5794
|
+
existsSync as existsSync5,
|
|
5463
5795
|
mkdirSync,
|
|
5464
5796
|
readdirSync as readdirSync2,
|
|
5465
5797
|
readFileSync as readFileSync2,
|
|
@@ -5468,18 +5800,18 @@ import {
|
|
|
5468
5800
|
watch,
|
|
5469
5801
|
writeFileSync
|
|
5470
5802
|
} from "node:fs";
|
|
5471
|
-
import { homedir as
|
|
5472
|
-
import { basename as basename2, join as
|
|
5473
|
-
import { spawn as
|
|
5803
|
+
import { homedir as homedir5 } from "node:os";
|
|
5804
|
+
import { basename as basename2, join as join5, parse, resolve as resolve2 } from "node:path";
|
|
5805
|
+
import { spawn as spawn5 } from "node:child_process";
|
|
5474
5806
|
var DROP_LOG_SESSION_ID = "drop";
|
|
5475
5807
|
function offhandHome() {
|
|
5476
|
-
return process.env.OFFHAND_HOME ??
|
|
5808
|
+
return process.env.OFFHAND_HOME ?? join5(homedir5(), ".offhand");
|
|
5477
5809
|
}
|
|
5478
5810
|
function dropOutboxDir(home = offhandHome()) {
|
|
5479
|
-
return
|
|
5811
|
+
return join5(home, "drop-outbox");
|
|
5480
5812
|
}
|
|
5481
5813
|
function incomingDropDir() {
|
|
5482
|
-
return
|
|
5814
|
+
return join5(homedir5(), "Downloads", "offhand");
|
|
5483
5815
|
}
|
|
5484
5816
|
function safeDropName(input) {
|
|
5485
5817
|
const base = basename2(input).replace(/[<>:"/\\|?*\x00-\x1F]/g, "_").replace(/[. ]+$/g, "").trim();
|
|
@@ -5503,13 +5835,13 @@ function queueDropFileForPhone(sourcePath, outbox = dropOutboxDir()) {
|
|
|
5503
5835
|
const st = statSync2(source);
|
|
5504
5836
|
if (!st.isFile()) throw new Error(`not a file: ${sourcePath}`);
|
|
5505
5837
|
mkdirSync(outbox, { recursive: true });
|
|
5506
|
-
const dest =
|
|
5838
|
+
const dest = join5(outbox, dedupeDropName(basename2(source), safeNames(outbox)));
|
|
5507
5839
|
copyFileSync(source, dest);
|
|
5508
5840
|
return dest;
|
|
5509
5841
|
}
|
|
5510
5842
|
function saveIncomingDrop(name, bytes, dir = incomingDropDir()) {
|
|
5511
5843
|
mkdirSync(dir, { recursive: true });
|
|
5512
|
-
const path =
|
|
5844
|
+
const path = join5(dir, dedupeDropName(name, safeNames(dir)));
|
|
5513
5845
|
writeFileSync(path, bytes);
|
|
5514
5846
|
return path;
|
|
5515
5847
|
}
|
|
@@ -5552,7 +5884,7 @@ function startDropOutboxWatcher(outbox, sendFile) {
|
|
|
5552
5884
|
console.error(`drop: sent but could not delete ${path}: ${e instanceof Error ? e.message : String(e)}`);
|
|
5553
5885
|
}
|
|
5554
5886
|
} catch (e) {
|
|
5555
|
-
if (
|
|
5887
|
+
if (existsSync5(path)) {
|
|
5556
5888
|
console.error(`drop: failed to send ${path}: ${e instanceof Error ? e.message : String(e)}`);
|
|
5557
5889
|
schedule(15e3);
|
|
5558
5890
|
}
|
|
@@ -5564,7 +5896,7 @@ function startDropOutboxWatcher(outbox, sendFile) {
|
|
|
5564
5896
|
timer = null;
|
|
5565
5897
|
if (closed) return;
|
|
5566
5898
|
for (const entry of safeEntries(outbox)) {
|
|
5567
|
-
if (entry.isFile()) void processFile(
|
|
5899
|
+
if (entry.isFile()) void processFile(join5(outbox, entry.name));
|
|
5568
5900
|
}
|
|
5569
5901
|
};
|
|
5570
5902
|
watcher = watch(outbox, () => schedule());
|
|
@@ -5592,7 +5924,7 @@ function showDropToast(savedPath, textToClipboard) {
|
|
|
5592
5924
|
`$toast = [Windows.UI.Notifications.ToastNotification]::new($xml)`,
|
|
5593
5925
|
`[Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier(${psQuote(appId)}).Show($toast)`
|
|
5594
5926
|
].filter(Boolean);
|
|
5595
|
-
const child =
|
|
5927
|
+
const child = spawn5("powershell", ["-NoProfile", "-Command", lines.join("; ")], {
|
|
5596
5928
|
windowsHide: true,
|
|
5597
5929
|
stdio: ["ignore", "ignore", "pipe"]
|
|
5598
5930
|
});
|
|
@@ -5650,7 +5982,7 @@ function mimeFromName(path) {
|
|
|
5650
5982
|
// ../daemon/src/session-manager.ts
|
|
5651
5983
|
function resolveDaemonVersion() {
|
|
5652
5984
|
try {
|
|
5653
|
-
const pkgPath =
|
|
5985
|
+
const pkgPath = join6(dirname2(fileURLToPath2(import.meta.url)), "..", "package.json");
|
|
5654
5986
|
const pkg = JSON.parse(readFileSync3(pkgPath, "utf8"));
|
|
5655
5987
|
return pkg.version ?? "0.0.0";
|
|
5656
5988
|
} catch {
|
|
@@ -5748,9 +6080,9 @@ var SessionManager = class {
|
|
|
5748
6080
|
prompt += "\n\nAttached files (read them from disk):";
|
|
5749
6081
|
for (const a2 of msg.attachments) {
|
|
5750
6082
|
const bytes = await this.attachmentFetcher(a2.blobId);
|
|
5751
|
-
const dir =
|
|
6083
|
+
const dir = join6(tmpdir(), "offhand-attachments");
|
|
5752
6084
|
mkdirSync2(dir, { recursive: true });
|
|
5753
|
-
const path =
|
|
6085
|
+
const path = join6(dir, `${a2.blobId.slice(0, 8)}-${basename3(a2.name)}`);
|
|
5754
6086
|
writeFileSync2(path, bytes);
|
|
5755
6087
|
prompt += `
|
|
5756
6088
|
- ${path} (${a2.mime})`;
|
|
@@ -5988,17 +6320,17 @@ function listFolders(path) {
|
|
|
5988
6320
|
if (!path) return { path: "", parent: null, dirs: driveRoots() };
|
|
5989
6321
|
const current = resolve3(path);
|
|
5990
6322
|
const dirs = safeReadDir2(current).filter((entry) => entry.isDirectory() && !skipDir(entry.name)).map((entry) => {
|
|
5991
|
-
const full =
|
|
5992
|
-
return { name: entry.name, path: full, isGit:
|
|
6323
|
+
const full = join6(current, entry.name);
|
|
6324
|
+
return { name: entry.name, path: full, isGit: existsSync6(join6(full, ".git")) };
|
|
5993
6325
|
}).sort((a2, b2) => Number(b2.isGit) - Number(a2.isGit) || a2.name.localeCompare(b2.name)).slice(0, 200);
|
|
5994
6326
|
return { path: current, parent: isRoot(current) ? null : dirname2(current), dirs };
|
|
5995
6327
|
}
|
|
5996
6328
|
function driveRoots() {
|
|
5997
|
-
if (process.platform !== "win32") return [{ name: "/", path: "/", isGit:
|
|
6329
|
+
if (process.platform !== "win32") return [{ name: "/", path: "/", isGit: existsSync6("/.git") }];
|
|
5998
6330
|
const roots = [];
|
|
5999
6331
|
for (let code = 67; code <= 90; code++) {
|
|
6000
6332
|
const name = `${String.fromCharCode(code)}:\\`;
|
|
6001
|
-
if (
|
|
6333
|
+
if (existsSync6(name)) roots.push({ name, path: name, isGit: existsSync6(join6(name, ".git")) });
|
|
6002
6334
|
}
|
|
6003
6335
|
return roots.sort((a2, b2) => a2.name.localeCompare(b2.name)).slice(0, 200);
|
|
6004
6336
|
}
|
|
@@ -6031,14 +6363,14 @@ function stripTrailing(path) {
|
|
|
6031
6363
|
// ../daemon/src/store.ts
|
|
6032
6364
|
import { DatabaseSync } from "node:sqlite";
|
|
6033
6365
|
import { mkdirSync as mkdirSync3 } from "node:fs";
|
|
6034
|
-
import { homedir as
|
|
6035
|
-
import { join as
|
|
6366
|
+
import { homedir as homedir6 } from "node:os";
|
|
6367
|
+
import { join as join7, basename as basename4 } from "node:path";
|
|
6036
6368
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
6037
6369
|
var Store = class {
|
|
6038
6370
|
db;
|
|
6039
|
-
constructor(dir = process.env.OFFHAND_HOME ??
|
|
6371
|
+
constructor(dir = process.env.OFFHAND_HOME ?? join7(homedir6(), ".offhand")) {
|
|
6040
6372
|
mkdirSync3(dir, { recursive: true });
|
|
6041
|
-
this.db = new DatabaseSync(
|
|
6373
|
+
this.db = new DatabaseSync(join7(dir, "offhand.db"));
|
|
6042
6374
|
this.db.exec(`
|
|
6043
6375
|
PRAGMA journal_mode = WAL;
|
|
6044
6376
|
CREATE TABLE IF NOT EXISTS sessions (
|
|
@@ -10280,6 +10612,11 @@ var coerce = {
|
|
|
10280
10612
|
var NEVER = INVALID;
|
|
10281
10613
|
|
|
10282
10614
|
// ../shared/src/run-events.ts
|
|
10615
|
+
var ApprovalQuestionSchema = external_exports.object({
|
|
10616
|
+
text: external_exports.string(),
|
|
10617
|
+
options: external_exports.array(external_exports.object({ label: external_exports.string(), description: external_exports.string().optional() })),
|
|
10618
|
+
multiSelect: external_exports.boolean().optional()
|
|
10619
|
+
});
|
|
10283
10620
|
var RunEventSchema = external_exports.discriminatedUnion("type", [
|
|
10284
10621
|
external_exports.object({ type: external_exports.literal("text"), chunk: external_exports.string() }),
|
|
10285
10622
|
/** Extended-thinking content, streamed the same way as text (medium+ effort tiers). */
|
|
@@ -10294,11 +10631,7 @@ var RunEventSchema = external_exports.discriminatedUnion("type", [
|
|
|
10294
10631
|
/** Optional content preview (edit diff / command) shown in the approval sheet. */
|
|
10295
10632
|
preview: external_exports.string().optional(),
|
|
10296
10633
|
/** AskUserQuestion payload: render the actual question with options. */
|
|
10297
|
-
question:
|
|
10298
|
-
text: external_exports.string(),
|
|
10299
|
-
options: external_exports.array(external_exports.object({ label: external_exports.string(), description: external_exports.string().optional() })),
|
|
10300
|
-
multiSelect: external_exports.boolean().optional()
|
|
10301
|
-
}).optional()
|
|
10634
|
+
question: ApprovalQuestionSchema.optional()
|
|
10302
10635
|
}),
|
|
10303
10636
|
/** Encrypted blob reference (M5): phone fetches + decrypts locally. */
|
|
10304
10637
|
external_exports.object({
|
|
@@ -10681,9 +11014,9 @@ if (typeof Module.getRandomValue === "undefined") {
|
|
|
10681
11014
|
Module.getRandomValue = randomValuesStandard;
|
|
10682
11015
|
} catch (e) {
|
|
10683
11016
|
try {
|
|
10684
|
-
|
|
11017
|
+
crypto2 = null;
|
|
10685
11018
|
randomValueNodeJS = function() {
|
|
10686
|
-
var buf =
|
|
11019
|
+
var buf = crypto2["randomBytes"](4);
|
|
10687
11020
|
return (buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]) >>> 0;
|
|
10688
11021
|
};
|
|
10689
11022
|
randomValueNodeJS();
|
|
@@ -10696,7 +11029,7 @@ if (typeof Module.getRandomValue === "undefined") {
|
|
|
10696
11029
|
var window_;
|
|
10697
11030
|
var crypto_;
|
|
10698
11031
|
var randomValuesStandard;
|
|
10699
|
-
var
|
|
11032
|
+
var crypto2;
|
|
10700
11033
|
var randomValueNodeJS;
|
|
10701
11034
|
var _Module = Module;
|
|
10702
11035
|
Module.ready = new Promise(function(resolve6, reject) {
|
|
@@ -37590,7 +37923,7 @@ Module.ready = new Promise(function(resolve6, reject) {
|
|
|
37590
37923
|
try {
|
|
37591
37924
|
var window_ = "object" === typeof window ? window : self;
|
|
37592
37925
|
var crypto_ = typeof window_.crypto !== "undefined" ? window_.crypto : window_.msCrypto;
|
|
37593
|
-
crypto_ = crypto_ === void 0 ?
|
|
37926
|
+
crypto_ = crypto_ === void 0 ? crypto2 : crypto_;
|
|
37594
37927
|
var randomValuesStandard = function() {
|
|
37595
37928
|
var buf = new Uint32Array(1);
|
|
37596
37929
|
crypto_.getRandomValues(buf);
|
|
@@ -37600,9 +37933,9 @@ Module.ready = new Promise(function(resolve6, reject) {
|
|
|
37600
37933
|
Module3.getRandomValue = randomValuesStandard;
|
|
37601
37934
|
} catch (e) {
|
|
37602
37935
|
try {
|
|
37603
|
-
var
|
|
37936
|
+
var crypto2 = null;
|
|
37604
37937
|
var randomValueNodeJS = function() {
|
|
37605
|
-
var buf =
|
|
37938
|
+
var buf = crypto2["randomBytes"](4);
|
|
37606
37939
|
return (buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]) >>> 0;
|
|
37607
37940
|
};
|
|
37608
37941
|
randomValueNodeJS();
|
|
@@ -38248,7 +38581,7 @@ Module.ready = new Promise(function(resolve6, reject) {
|
|
|
38248
38581
|
try {
|
|
38249
38582
|
var window_ = "object" === typeof window ? window : self;
|
|
38250
38583
|
var crypto_ = typeof window_.crypto !== "undefined" ? window_.crypto : window_.msCrypto;
|
|
38251
|
-
crypto_ = crypto_ === void 0 ?
|
|
38584
|
+
crypto_ = crypto_ === void 0 ? crypto2 : crypto_;
|
|
38252
38585
|
var randomValuesStandard = function() {
|
|
38253
38586
|
var buf = new Uint32Array(1);
|
|
38254
38587
|
crypto_.getRandomValues(buf);
|
|
@@ -38258,9 +38591,9 @@ Module.ready = new Promise(function(resolve6, reject) {
|
|
|
38258
38591
|
Module2.getRandomValue = randomValuesStandard;
|
|
38259
38592
|
} catch (e) {
|
|
38260
38593
|
try {
|
|
38261
|
-
var
|
|
38594
|
+
var crypto2 = null;
|
|
38262
38595
|
var randomValueNodeJS = function() {
|
|
38263
|
-
var buf =
|
|
38596
|
+
var buf = crypto2["randomBytes"](4);
|
|
38264
38597
|
return (buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]) >>> 0;
|
|
38265
38598
|
};
|
|
38266
38599
|
randomValueNodeJS();
|
|
@@ -41366,17 +41699,17 @@ var RelayClient = class {
|
|
|
41366
41699
|
};
|
|
41367
41700
|
|
|
41368
41701
|
// ../daemon/src/pairing.ts
|
|
41369
|
-
import { existsSync as
|
|
41370
|
-
import { homedir as
|
|
41371
|
-
import { join as
|
|
41702
|
+
import { existsSync as existsSync7, readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4 } from "node:fs";
|
|
41703
|
+
import { homedir as homedir7 } from "node:os";
|
|
41704
|
+
import { join as join8 } from "node:path";
|
|
41372
41705
|
import { randomInt } from "node:crypto";
|
|
41373
|
-
var PAIRING_DIR = process.env.OFFHAND_HOME ??
|
|
41374
|
-
var PAIRING_PATH =
|
|
41706
|
+
var PAIRING_DIR = process.env.OFFHAND_HOME ?? join8(homedir7(), ".offhand");
|
|
41707
|
+
var PAIRING_PATH = join8(PAIRING_DIR, "pairing.json");
|
|
41375
41708
|
var POLL_MS = 2e3;
|
|
41376
41709
|
var POLL_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
41377
41710
|
async function ensurePairing(relayUrl2, forceNew = false, webUrl2 = "https://offhand-web.onrender.com") {
|
|
41378
41711
|
await ready;
|
|
41379
|
-
if (!forceNew &&
|
|
41712
|
+
if (!forceNew && existsSync7(PAIRING_PATH)) {
|
|
41380
41713
|
const f2 = JSON.parse(readFileSync4(PAIRING_PATH, "utf8"));
|
|
41381
41714
|
const kp2 = { publicKey: fromB64u(f2.daemonPublicKey), secretKey: fromB64u(f2.daemonSecretKey) };
|
|
41382
41715
|
const phonePk = fromB64u(f2.phonePublicKey);
|
|
@@ -41449,7 +41782,7 @@ async function ensurePairing(relayUrl2, forceNew = false, webUrl2 = "https://off
|
|
|
41449
41782
|
// ../daemon/src/approvals.ts
|
|
41450
41783
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
41451
41784
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
41452
|
-
import { join as
|
|
41785
|
+
import { join as join9 } from "node:path";
|
|
41453
41786
|
var ApprovalBroker = class {
|
|
41454
41787
|
constructor(timeoutMs) {
|
|
41455
41788
|
this.timeoutMs = timeoutMs;
|
|
@@ -41473,7 +41806,7 @@ var ApprovalBroker = class {
|
|
|
41473
41806
|
}
|
|
41474
41807
|
const risk = classifyRisk(toolName, input);
|
|
41475
41808
|
const filePath = input?.file_path;
|
|
41476
|
-
if (toolName === "Read" && typeof filePath === "string" && filePath.toLowerCase().startsWith(
|
|
41809
|
+
if (toolName === "Read" && typeof filePath === "string" && filePath.toLowerCase().startsWith(join9(tmpdir2(), "offhand-attachments").toLowerCase())) {
|
|
41477
41810
|
return Promise.resolve({ approve: true });
|
|
41478
41811
|
}
|
|
41479
41812
|
if (this.policyProvider() === "trusting" && risk === "low" && toolName !== "AskUserQuestion") {
|
|
@@ -41540,11 +41873,11 @@ function summariseInput(input) {
|
|
|
41540
41873
|
if (typeof input !== "object" || input === null) return String(input ?? "");
|
|
41541
41874
|
const i2 = input;
|
|
41542
41875
|
const q2 = firstQuestion(i2);
|
|
41543
|
-
if (q2) return
|
|
41876
|
+
if (q2) return truncate5(q2.text, 200);
|
|
41544
41877
|
for (const key of ["command", "file_path", "path", "url", "pattern", "description"]) {
|
|
41545
|
-
if (typeof i2[key] === "string" && i2[key] !== "") return
|
|
41878
|
+
if (typeof i2[key] === "string" && i2[key] !== "") return truncate5(`${key}: ${i2[key]}`, 200);
|
|
41546
41879
|
}
|
|
41547
|
-
return
|
|
41880
|
+
return truncate5(JSON.stringify(input), 200);
|
|
41548
41881
|
}
|
|
41549
41882
|
function firstQuestion(i2) {
|
|
41550
41883
|
if (!Array.isArray(i2.questions) || i2.questions.length === 0) return void 0;
|
|
@@ -41562,7 +41895,7 @@ function buildPreview(toolName, input) {
|
|
|
41562
41895
|
const q2 = firstQuestion(i2);
|
|
41563
41896
|
if (q2) {
|
|
41564
41897
|
const lines = [q2.text, ...q2.options.map((o2, n2) => `${n2 + 1}. ${o2.label}${o2.description ? ` \u2014 ${o2.description}` : ""}`)];
|
|
41565
|
-
return
|
|
41898
|
+
return truncate5(lines.join("\n"), 600);
|
|
41566
41899
|
}
|
|
41567
41900
|
if (/^(Edit|MultiEdit)/.test(toolName) && typeof i2.new_string === "string") {
|
|
41568
41901
|
const oldS = typeof i2.old_string === "string" ? i2.old_string : "";
|
|
@@ -41570,15 +41903,15 @@ function buildPreview(toolName, input) {
|
|
|
41570
41903
|
...oldS.split("\n").map((l2) => `- ${l2}`),
|
|
41571
41904
|
...i2.new_string.split("\n").map((l2) => `+ ${l2}`)
|
|
41572
41905
|
];
|
|
41573
|
-
return
|
|
41906
|
+
return truncate5(lines.join("\n"), 600);
|
|
41574
41907
|
}
|
|
41575
41908
|
if (/^Write/.test(toolName) && typeof i2.content === "string") {
|
|
41576
|
-
return
|
|
41909
|
+
return truncate5(i2.content.split("\n").map((l2) => `+ ${l2}`).join("\n"), 600);
|
|
41577
41910
|
}
|
|
41578
|
-
if (typeof i2.command === "string") return
|
|
41911
|
+
if (typeof i2.command === "string") return truncate5(`$ ${i2.command}`, 600);
|
|
41579
41912
|
return void 0;
|
|
41580
41913
|
}
|
|
41581
|
-
function
|
|
41914
|
+
function truncate5(s2, max) {
|
|
41582
41915
|
return s2.length <= max ? s2 : s2.slice(0, max - 1) + "\u2026";
|
|
41583
41916
|
}
|
|
41584
41917
|
|
|
@@ -41615,28 +41948,28 @@ async function downloadArtifact(relayUrl2, sessionId, blobId, keys) {
|
|
|
41615
41948
|
}
|
|
41616
41949
|
|
|
41617
41950
|
// ../daemon/src/autostart.ts
|
|
41618
|
-
import { homedir as
|
|
41619
|
-
import { join as
|
|
41620
|
-
import { existsSync as
|
|
41951
|
+
import { homedir as homedir8 } from "node:os";
|
|
41952
|
+
import { join as join10, resolve as resolve4, dirname as dirname3 } from "node:path";
|
|
41953
|
+
import { existsSync as existsSync8, readFileSync as readFileSync5, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, rmSync } from "node:fs";
|
|
41621
41954
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
41622
41955
|
import { spawnSync } from "node:child_process";
|
|
41623
41956
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
41624
41957
|
var __dirname2 = dirname3(fileURLToPath3(import.meta.url));
|
|
41625
41958
|
var SHORTCUT_NAME = "OffhandDaemon.lnk";
|
|
41626
|
-
var OFFHAND_HOME = process.env.OFFHAND_HOME ??
|
|
41959
|
+
var OFFHAND_HOME = process.env.OFFHAND_HOME ?? join10(homedir8(), ".offhand");
|
|
41627
41960
|
function getStartupDir() {
|
|
41628
41961
|
if (process.platform !== "win32") {
|
|
41629
|
-
return
|
|
41962
|
+
return join10(homedir8(), ".config", "autostart");
|
|
41630
41963
|
}
|
|
41631
|
-
return
|
|
41964
|
+
return join10(process.env.APPDATA ?? "", "Microsoft", "Windows", "Start Menu", "Programs", "Startup");
|
|
41632
41965
|
}
|
|
41633
41966
|
function getWrapperPath() {
|
|
41634
|
-
const wrapperDir =
|
|
41967
|
+
const wrapperDir = join10(OFFHAND_HOME, "autostart");
|
|
41635
41968
|
mkdirSync5(wrapperDir, { recursive: true });
|
|
41636
|
-
return
|
|
41969
|
+
return join10(wrapperDir, "start-daemon.bat");
|
|
41637
41970
|
}
|
|
41638
41971
|
function getShortcutPath() {
|
|
41639
|
-
return
|
|
41972
|
+
return join10(getStartupDir(), SHORTCUT_NAME);
|
|
41640
41973
|
}
|
|
41641
41974
|
function buildWrapperScript(config) {
|
|
41642
41975
|
const projectRoot = resolve4(__dirname2, "..", "..");
|
|
@@ -41669,7 +42002,7 @@ $Shortcut.Arguments = "/c ${targetPath}"
|
|
|
41669
42002
|
$Shortcut.WorkingDirectory = "${resolve4(__dirname2, "..", "..")}"
|
|
41670
42003
|
$Shortcut.Description = "Offhand Daemon - Auto-start on login"
|
|
41671
42004
|
$Shortcut.Save()`;
|
|
41672
|
-
const scriptPath =
|
|
42005
|
+
const scriptPath = join10(tmpdir3(), "offhand-create-shortcut.ps1");
|
|
41673
42006
|
writeFileSync4(scriptPath, psScript);
|
|
41674
42007
|
const result = spawnSync("powershell", ["-NoProfile", "-ExecutionPolicy", "Bypass", "-File", scriptPath], { encoding: "utf8", shell: true });
|
|
41675
42008
|
try {
|
|
@@ -41679,7 +42012,7 @@ $Shortcut.Save()`;
|
|
|
41679
42012
|
return { success: result.status === 0, output: result.stdout + result.stderr };
|
|
41680
42013
|
}
|
|
41681
42014
|
function checkShortcutExists(shortcutPath) {
|
|
41682
|
-
return
|
|
42015
|
+
return existsSync8(shortcutPath);
|
|
41683
42016
|
}
|
|
41684
42017
|
function installAutostart(config) {
|
|
41685
42018
|
const wrapperPath = getWrapperPath();
|
|
@@ -41699,7 +42032,7 @@ function getAutostartStatus() {
|
|
|
41699
42032
|
const shortcutPath = getShortcutPath();
|
|
41700
42033
|
const installed = checkShortcutExists(shortcutPath);
|
|
41701
42034
|
const wrapperPath = getWrapperPath();
|
|
41702
|
-
const wrapperExists =
|
|
42035
|
+
const wrapperExists = existsSync8(wrapperPath);
|
|
41703
42036
|
let details = `Shortcut: ${shortcutPath} (${installed ? "EXISTS" : "MISSING"})`;
|
|
41704
42037
|
if (wrapperExists) {
|
|
41705
42038
|
details += `
|
|
@@ -41708,7 +42041,7 @@ Wrapper: ${wrapperPath} (EXISTS)`;
|
|
|
41708
42041
|
return { installed, details };
|
|
41709
42042
|
}
|
|
41710
42043
|
function saveConfig(config) {
|
|
41711
|
-
const configPath =
|
|
42044
|
+
const configPath = join10(OFFHAND_HOME, "autostart.json");
|
|
41712
42045
|
writeFileSync4(configPath, JSON.stringify(config, null, 2));
|
|
41713
42046
|
}
|
|
41714
42047
|
function getConfigFromStore() {
|
|
@@ -41757,7 +42090,7 @@ var devUrl = argValue("--dev-url");
|
|
|
41757
42090
|
var store = new Store();
|
|
41758
42091
|
var wsArgs = argValues("--workspace").map((w2) => resolve5(w2));
|
|
41759
42092
|
for (const w2 of wsArgs) {
|
|
41760
|
-
if (!
|
|
42093
|
+
if (!existsSync9(w2)) {
|
|
41761
42094
|
console.error(`workspace does not exist: ${w2}`);
|
|
41762
42095
|
process.exit(1);
|
|
41763
42096
|
}
|
|
@@ -41769,6 +42102,7 @@ var approvalUrl = `http://127.0.0.1:${port}/approval`;
|
|
|
41769
42102
|
var runners = [
|
|
41770
42103
|
new ClaudeCodeRunner(broker, approvalUrl),
|
|
41771
42104
|
new CopilotCliRunner(),
|
|
42105
|
+
new OpenCodeRunner(),
|
|
41772
42106
|
new CodexCliRunner(),
|
|
41773
42107
|
new CursorAgentRunner(),
|
|
41774
42108
|
new GeminiCliRunner()
|