daily-focus 0.2.0-dev.14 → 1.1.0-dev.1
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/README.md +7 -1
- package/dist/{audit-DTXVAT5Z.js → audit-W3O6CLOF.js} +4 -6
- package/dist/chunk-F3EHZWRS.js +456 -0
- package/dist/{chunk-IFAGJFOT.js → chunk-OPASVYLR.js} +79 -6
- package/dist/chunk-Q5OKXJXT.js +404 -0
- package/dist/{chunk-NAD6PRZT.js → chunk-UM2EPNWR.js} +5 -7
- package/dist/cli.js +25 -31
- package/dist/demo-HTFKCJJ5.js +859 -0
- package/dist/{init-BMFPWMXL.js → init-DEVCAQZZ.js} +1 -2
- package/dist/{seed-GCT7Z3IV.js → seed-SMKNFYTC.js} +4 -5
- package/dist/{server-ANIS2MXU.js → server-JJVEH4ME.js} +26 -477
- package/dist/{service-NEQVCWI5.js → service-6ZFOHPWN.js} +1 -2
- package/package.json +1 -1
- package/public/app.js +20 -5
- package/dist/chunk-K2VT4JBR.js +0 -81
- package/dist/chunk-UAR65OCG.js +0 -179
- package/dist/sample-QDEXFFRY.js +0 -7
|
@@ -8,12 +8,11 @@ import {
|
|
|
8
8
|
} from "./chunk-QKT2FAB5.js";
|
|
9
9
|
import {
|
|
10
10
|
loadConfig
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-OPASVYLR.js";
|
|
12
12
|
import {
|
|
13
13
|
PACKAGED,
|
|
14
14
|
command
|
|
15
15
|
} from "./chunk-BC2W7NND.js";
|
|
16
|
-
import "./chunk-K2VT4JBR.js";
|
|
17
16
|
|
|
18
17
|
// scripts/init.ts
|
|
19
18
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
import {
|
|
2
2
|
sampleBrief
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-Q5OKXJXT.js";
|
|
4
4
|
import {
|
|
5
5
|
Store
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-UM2EPNWR.js";
|
|
7
7
|
import {
|
|
8
8
|
loadConfig
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-OPASVYLR.js";
|
|
10
10
|
import {
|
|
11
11
|
command
|
|
12
12
|
} from "./chunk-BC2W7NND.js";
|
|
13
|
-
import "./chunk-K2VT4JBR.js";
|
|
14
13
|
|
|
15
14
|
// scripts/seed.ts
|
|
16
15
|
import { writeFile } from "node:fs/promises";
|
|
17
|
-
var brief = sampleBrief();
|
|
18
16
|
var config = loadConfig();
|
|
17
|
+
var brief = sampleBrief(/* @__PURE__ */ new Date(), config.profile);
|
|
19
18
|
var force = process.argv.slice(2).includes("--force");
|
|
20
19
|
var store = new Store(config);
|
|
21
20
|
await store.ensureDataDir();
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ADAPTERS,
|
|
3
|
+
Assistant
|
|
4
|
+
} from "./chunk-F3EHZWRS.js";
|
|
1
5
|
import {
|
|
2
6
|
FOCUS_TEMPLATE,
|
|
3
7
|
sourcesTemplate
|
|
@@ -16,7 +20,7 @@ import {
|
|
|
16
20
|
stopSession,
|
|
17
21
|
tempPathFor,
|
|
18
22
|
writeJsonAtomic
|
|
19
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-UM2EPNWR.js";
|
|
20
24
|
import {
|
|
21
25
|
CLI_NAMES,
|
|
22
26
|
LAUNCH_SETTINGS,
|
|
@@ -24,23 +28,21 @@ import {
|
|
|
24
28
|
briefWritingTools,
|
|
25
29
|
describeTime,
|
|
26
30
|
envSources,
|
|
31
|
+
laterISO,
|
|
27
32
|
layerEnv,
|
|
28
33
|
loadConfig,
|
|
29
34
|
nextScheduledRun,
|
|
35
|
+
parseISO,
|
|
30
36
|
readDotEnv,
|
|
31
37
|
resolveSchedule,
|
|
32
38
|
scheduledRunDue
|
|
33
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-OPASVYLR.js";
|
|
34
40
|
import {
|
|
35
41
|
PACKAGED,
|
|
36
42
|
command,
|
|
37
43
|
packageVersion,
|
|
38
44
|
ranDirectly
|
|
39
45
|
} from "./chunk-BC2W7NND.js";
|
|
40
|
-
import {
|
|
41
|
-
laterISO,
|
|
42
|
-
parseISO
|
|
43
|
-
} from "./chunk-K2VT4JBR.js";
|
|
44
46
|
|
|
45
47
|
// src/server.ts
|
|
46
48
|
import { createServer } from "node:http";
|
|
@@ -2379,466 +2381,13 @@ async function readTicketsFile(path) {
|
|
|
2379
2381
|
}
|
|
2380
2382
|
|
|
2381
2383
|
// src/agent.ts
|
|
2382
|
-
import { spawn as spawn2 } from "node:child_process";
|
|
2383
|
-
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
2384
|
-
import { appendFile as appendFile2, mkdir as mkdir2, readFile as readFile7 } from "node:fs/promises";
|
|
2385
|
-
import { homedir } from "node:os";
|
|
2386
|
-
import { createInterface as createInterface2 } from "node:readline";
|
|
2387
|
-
|
|
2388
|
-
// src/assistant.ts
|
|
2389
2384
|
import { spawn } from "node:child_process";
|
|
2390
2385
|
import { randomUUID } from "node:crypto";
|
|
2391
2386
|
import { appendFile, mkdir, readFile as readFile6 } from "node:fs/promises";
|
|
2387
|
+
import { homedir } from "node:os";
|
|
2392
2388
|
import { createInterface } from "node:readline";
|
|
2393
|
-
var TURN_TIMEOUT_MS = 10 * 6e4;
|
|
2394
|
-
var TURNS_SHOWN = 20;
|
|
2395
|
-
var QUICK_ACTIONS = [
|
|
2396
|
-
{
|
|
2397
|
-
id: "assess-review",
|
|
2398
|
-
label: "Assess the review",
|
|
2399
|
-
sources: ["github"],
|
|
2400
|
-
request: "Read every review comment and requested change on this pull request against the diff. For each one say whether it is right, partly right or mistaken, with the evidence, and what I should do about it."
|
|
2401
|
-
},
|
|
2402
|
-
{
|
|
2403
|
-
id: "explain-ci",
|
|
2404
|
-
label: "Explain the CI failure",
|
|
2405
|
-
sources: ["github"],
|
|
2406
|
-
request: "Find the failing checks on this pull request and read their logs. Explain what is failing, why, and the smallest change that would fix it. Do not attempt the fix."
|
|
2407
|
-
},
|
|
2408
|
-
{
|
|
2409
|
-
id: "summarise-pr",
|
|
2410
|
-
label: "Summarise the change",
|
|
2411
|
-
sources: ["github"],
|
|
2412
|
-
request: "Summarise what this pull request changes and why, in a form I could paste as its description, and flag anything a reviewer is likely to push back on."
|
|
2413
|
-
},
|
|
2414
|
-
{
|
|
2415
|
-
id: "reply-reviewer",
|
|
2416
|
-
label: "Draft replies to reviewers",
|
|
2417
|
-
sources: ["github"],
|
|
2418
|
-
request: "Draft a reply to each open review comment on this pull request, in my voice, agreeing where the reviewer is right and explaining where they are not. Give me the text to post. Do not post it."
|
|
2419
|
-
},
|
|
2420
|
-
{
|
|
2421
|
-
id: "draft-reply",
|
|
2422
|
-
label: "Draft a reply",
|
|
2423
|
-
sources: ["email"],
|
|
2424
|
-
request: "Read the whole thread and draft a reply from me, in my voice, that answers what was asked. Create it as a Gmail draft on this thread so I can open it there. Never send it. If you cannot create drafts, give me the text instead and say so."
|
|
2425
|
-
},
|
|
2426
|
-
{
|
|
2427
|
-
id: "summarise-thread",
|
|
2428
|
-
label: "Summarise the thread",
|
|
2429
|
-
sources: ["email"],
|
|
2430
|
-
request: "Read the whole thread and summarise it: who wants what, what has been decided, and what is still open."
|
|
2431
|
-
},
|
|
2432
|
-
{
|
|
2433
|
-
id: "what-they-need",
|
|
2434
|
-
label: "What do they need from me?",
|
|
2435
|
-
sources: ["email", "slack", "messages"],
|
|
2436
|
-
request: "Read this and tell me exactly what is being asked of me, by whom, by when, and what the smallest adequate response would be."
|
|
2437
|
-
},
|
|
2438
|
-
{
|
|
2439
|
-
id: "summarise-ticket",
|
|
2440
|
-
label: "Summarise the ticket",
|
|
2441
|
-
sources: ["jira"],
|
|
2442
|
-
request: "Read this ticket, its comments and anything it links to, and summarise where it stands: what is done, what is left, and what is blocking it."
|
|
2443
|
-
},
|
|
2444
|
-
{
|
|
2445
|
-
id: "status-comment",
|
|
2446
|
-
label: "Draft a status comment",
|
|
2447
|
-
sources: ["jira"],
|
|
2448
|
-
request: "Read this ticket and its linked pull requests, and draft a short status comment from me for the ticket. Give me the text to post. Do not post it."
|
|
2449
|
-
},
|
|
2450
|
-
{
|
|
2451
|
-
id: "next-steps",
|
|
2452
|
-
label: "Break it into next steps",
|
|
2453
|
-
sources: null,
|
|
2454
|
-
request: "Read whatever this item points at and turn it into three to five concrete next steps I could start on today, smallest first."
|
|
2455
|
-
}
|
|
2456
|
-
];
|
|
2457
|
-
function foldAssistantLog(text) {
|
|
2458
|
-
const turns = /* @__PURE__ */ new Map();
|
|
2459
|
-
for (const line of text.split("\n")) {
|
|
2460
|
-
if (line.trim() === "") continue;
|
|
2461
|
-
let record;
|
|
2462
|
-
try {
|
|
2463
|
-
record = JSON.parse(line);
|
|
2464
|
-
} catch {
|
|
2465
|
-
continue;
|
|
2466
|
-
}
|
|
2467
|
-
if (typeof record !== "object" || record === null || typeof record.turn !== "string") continue;
|
|
2468
|
-
if (record.event === "started") {
|
|
2469
|
-
if (typeof record.item !== "string" || typeof record.request !== "string") continue;
|
|
2470
|
-
turns.set(record.turn, {
|
|
2471
|
-
id: record.turn,
|
|
2472
|
-
itemId: record.item,
|
|
2473
|
-
agent: record.agent,
|
|
2474
|
-
sessionId: null,
|
|
2475
|
-
request: record.request,
|
|
2476
|
-
action: typeof record.action === "string" ? record.action : null,
|
|
2477
|
-
startedAt: record.at,
|
|
2478
|
-
endedAt: null,
|
|
2479
|
-
status: "running",
|
|
2480
|
-
reply: "",
|
|
2481
|
-
error: null
|
|
2482
|
-
});
|
|
2483
|
-
continue;
|
|
2484
|
-
}
|
|
2485
|
-
const turn = turns.get(record.turn);
|
|
2486
|
-
if (!turn) continue;
|
|
2487
|
-
turn.sessionId = typeof record.sessionId === "string" ? record.sessionId : turn.sessionId;
|
|
2488
|
-
turn.reply = typeof record.reply === "string" ? record.reply : turn.reply;
|
|
2489
|
-
turn.endedAt = record.at;
|
|
2490
|
-
if (record.event === "finished") {
|
|
2491
|
-
turn.status = "done";
|
|
2492
|
-
} else if (record.event === "failed") {
|
|
2493
|
-
turn.status = "failed";
|
|
2494
|
-
turn.error = record.error;
|
|
2495
|
-
} else if (record.event === "aborted") {
|
|
2496
|
-
turn.status = "aborted";
|
|
2497
|
-
turn.error = record.reason;
|
|
2498
|
-
}
|
|
2499
|
-
}
|
|
2500
|
-
return turns;
|
|
2501
|
-
}
|
|
2502
|
-
function parseLine(line) {
|
|
2503
|
-
const trimmed = line.trim();
|
|
2504
|
-
if (!trimmed.startsWith("{")) return null;
|
|
2505
|
-
try {
|
|
2506
|
-
const parsed = JSON.parse(trimmed);
|
|
2507
|
-
return typeof parsed === "object" && parsed !== null ? parsed : null;
|
|
2508
|
-
} catch {
|
|
2509
|
-
return null;
|
|
2510
|
-
}
|
|
2511
|
-
}
|
|
2512
|
-
var claudeAdapter = {
|
|
2513
|
-
args({ prompt, sessionId, model, effort, tools, denyEdits }) {
|
|
2514
|
-
const args = [
|
|
2515
|
-
"-p",
|
|
2516
|
-
"--output-format",
|
|
2517
|
-
"stream-json",
|
|
2518
|
-
"--verbose",
|
|
2519
|
-
"--permission-mode",
|
|
2520
|
-
"dontAsk",
|
|
2521
|
-
"--permission-prompts",
|
|
2522
|
-
"none"
|
|
2523
|
-
];
|
|
2524
|
-
if (denyEdits) args.push("--disallowedTools", "Edit", "Write", "NotebookEdit");
|
|
2525
|
-
if (tools.length > 0) args.push("--allowedTools", ...tools);
|
|
2526
|
-
if (model) args.push("--model", model);
|
|
2527
|
-
if (effort) args.push("--effort", effort);
|
|
2528
|
-
if (sessionId) args.push("--resume", sessionId);
|
|
2529
|
-
args.push(prompt);
|
|
2530
|
-
return args;
|
|
2531
|
-
},
|
|
2532
|
-
parse(line) {
|
|
2533
|
-
const event = parseLine(line);
|
|
2534
|
-
if (!event) return null;
|
|
2535
|
-
const type = event.type;
|
|
2536
|
-
if (type === "system" && typeof event.session_id === "string") {
|
|
2537
|
-
return { sessionId: event.session_id };
|
|
2538
|
-
}
|
|
2539
|
-
if (type === "assistant") {
|
|
2540
|
-
const message = event.message;
|
|
2541
|
-
const content = Array.isArray(message?.content) ? message.content : [];
|
|
2542
|
-
const text = content.filter((block) => block.type === "text" && typeof block.text === "string").map((block) => block.text).join("\n\n");
|
|
2543
|
-
return text ? { text } : null;
|
|
2544
|
-
}
|
|
2545
|
-
if (type === "result") {
|
|
2546
|
-
const ok = event.is_error !== true && event.subtype === "success";
|
|
2547
|
-
const reply = typeof event.result === "string" ? event.result : void 0;
|
|
2548
|
-
const parsed = { done: ok ? { ok, reply } : { ok, error: reply ?? String(event.subtype ?? "failed") } };
|
|
2549
|
-
if (typeof event.session_id === "string") parsed.sessionId = event.session_id;
|
|
2550
|
-
return parsed;
|
|
2551
|
-
}
|
|
2552
|
-
return null;
|
|
2553
|
-
}
|
|
2554
|
-
};
|
|
2555
|
-
var codexAdapter = {
|
|
2556
|
-
args({ prompt, sessionId, model, effort }) {
|
|
2557
|
-
const args = ["exec"];
|
|
2558
|
-
if (sessionId) args.push("resume");
|
|
2559
|
-
args.push("--json", "--skip-git-repo-check");
|
|
2560
|
-
args.push("-c", 'sandbox_mode="workspace-write"', "-c", "sandbox_workspace_write.network_access=true");
|
|
2561
|
-
if (model) args.push("-c", `model=${JSON.stringify(model)}`);
|
|
2562
|
-
if (effort) args.push("-c", `model_reasoning_effort=${JSON.stringify(effort)}`);
|
|
2563
|
-
if (sessionId) args.push(sessionId);
|
|
2564
|
-
args.push(prompt);
|
|
2565
|
-
return args;
|
|
2566
|
-
},
|
|
2567
|
-
parse(line) {
|
|
2568
|
-
const event = parseLine(line);
|
|
2569
|
-
if (!event) return null;
|
|
2570
|
-
const type = event.type;
|
|
2571
|
-
if (type === "thread.started" && typeof event.thread_id === "string") {
|
|
2572
|
-
return { sessionId: event.thread_id };
|
|
2573
|
-
}
|
|
2574
|
-
if (type === "item.completed") {
|
|
2575
|
-
const item = event.item;
|
|
2576
|
-
if (item?.type === "agent_message" && typeof item.text === "string") return { text: item.text };
|
|
2577
|
-
return null;
|
|
2578
|
-
}
|
|
2579
|
-
if (type === "turn.completed") return { done: { ok: true } };
|
|
2580
|
-
if (type === "turn.failed" || type === "error") {
|
|
2581
|
-
const error = event.error;
|
|
2582
|
-
const message = typeof event.message === "string" ? event.message : error?.message;
|
|
2583
|
-
return { done: { ok: false, error: message ?? String(type) } };
|
|
2584
|
-
}
|
|
2585
|
-
return null;
|
|
2586
|
-
}
|
|
2587
|
-
};
|
|
2588
|
-
var ADAPTERS = {
|
|
2589
|
-
claude: claudeAdapter,
|
|
2590
|
-
codex: codexAdapter
|
|
2591
|
-
};
|
|
2592
|
-
function composePrompt(instructions, context, request, profile) {
|
|
2593
|
-
const now = /* @__PURE__ */ new Date();
|
|
2594
|
-
const facts = [`- Profile: ${profile}`, `- Today: ${now.toISOString().slice(0, 10)}`];
|
|
2595
|
-
const parts = [instructions.trim(), "", "## Context", "", ...facts, ""];
|
|
2596
|
-
if (context.item) parts.push("### The item", "", "```json", JSON.stringify(context.item, null, 2), "```", "");
|
|
2597
|
-
if (context.pull) parts.push("### The pull request, as the board last saw it", "", "```json", JSON.stringify(context.pull, null, 2), "```", "");
|
|
2598
|
-
if (context.ticket) parts.push("### The ticket, as the board last saw it", "", "```json", JSON.stringify(context.ticket, null, 2), "```", "");
|
|
2599
|
-
parts.push("## Request", "", request.trim());
|
|
2600
|
-
return parts.join("\n");
|
|
2601
|
-
}
|
|
2602
|
-
var Assistant = class _Assistant {
|
|
2603
|
-
turns = /* @__PURE__ */ new Map();
|
|
2604
|
-
running = /* @__PURE__ */ new Map();
|
|
2605
|
-
loaded = false;
|
|
2606
|
-
config;
|
|
2607
|
-
hooks;
|
|
2608
|
-
spawnFn;
|
|
2609
|
-
constructor(config, hooks, spawnFn = spawn) {
|
|
2610
|
-
this.config = config;
|
|
2611
|
-
this.hooks = hooks;
|
|
2612
|
-
this.spawnFn = spawnFn;
|
|
2613
|
-
}
|
|
2614
|
-
get enabled() {
|
|
2615
|
-
return this.config.assistant.agent !== null;
|
|
2616
|
-
}
|
|
2617
|
-
/** Whether any turn has a process going right now. */
|
|
2618
|
-
get busy() {
|
|
2619
|
-
return this.running.size > 0;
|
|
2620
|
-
}
|
|
2621
|
-
/**
|
|
2622
|
-
* Read the log, and close whatever was left running when the last process
|
|
2623
|
-
* died. Cheap when the assistant is off: the log is read only if it exists.
|
|
2624
|
-
*/
|
|
2625
|
-
async start() {
|
|
2626
|
-
if (this.loaded) return;
|
|
2627
|
-
this.loaded = true;
|
|
2628
|
-
let text = "";
|
|
2629
|
-
try {
|
|
2630
|
-
text = await readFile6(this.config.assistantLogFile, "utf8");
|
|
2631
|
-
} catch {
|
|
2632
|
-
return;
|
|
2633
|
-
}
|
|
2634
|
-
for (const [id, turn] of foldAssistantLog(text)) this.turns.set(id, turn);
|
|
2635
|
-
for (const turn of this.turns.values()) {
|
|
2636
|
-
if (turn.status !== "running") continue;
|
|
2637
|
-
turn.status = "aborted";
|
|
2638
|
-
turn.error = "the dashboard restarted while this was running";
|
|
2639
|
-
turn.endedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2640
|
-
await this.append({
|
|
2641
|
-
event: "aborted",
|
|
2642
|
-
turn: turn.id,
|
|
2643
|
-
sessionId: turn.sessionId,
|
|
2644
|
-
reason: turn.error,
|
|
2645
|
-
reply: turn.reply,
|
|
2646
|
-
at: turn.endedAt
|
|
2647
|
-
});
|
|
2648
|
-
}
|
|
2649
|
-
}
|
|
2650
|
-
view() {
|
|
2651
|
-
const items = {};
|
|
2652
|
-
for (const turn of this.turns.values()) {
|
|
2653
|
-
const entry = items[turn.itemId] ??= { running: false, sessionId: null, turns: [] };
|
|
2654
|
-
entry.turns.push(turn);
|
|
2655
|
-
if (turn.status === "running") entry.running = true;
|
|
2656
|
-
if (turn.sessionId && turn.agent === this.config.assistant.agent) entry.sessionId = turn.sessionId;
|
|
2657
|
-
}
|
|
2658
|
-
for (const entry of Object.values(items)) {
|
|
2659
|
-
entry.turns.sort((a, b) => a.startedAt.localeCompare(b.startedAt));
|
|
2660
|
-
entry.turns = entry.turns.slice(-TURNS_SHOWN);
|
|
2661
|
-
}
|
|
2662
|
-
return {
|
|
2663
|
-
enabled: this.enabled,
|
|
2664
|
-
agent: this.config.assistant.agent,
|
|
2665
|
-
quickActions: [...QUICK_ACTIONS],
|
|
2666
|
-
items
|
|
2667
|
-
};
|
|
2668
|
-
}
|
|
2669
|
-
/** The request text an ask resolves to, or null when there is nothing to send. */
|
|
2670
|
-
static requestFor(ask) {
|
|
2671
|
-
const action = ask.action ? QUICK_ACTIONS.find((candidate) => candidate.id === ask.action) : void 0;
|
|
2672
|
-
if (ask.action && !action) return null;
|
|
2673
|
-
const typed = (ask.text ?? "").trim();
|
|
2674
|
-
if (!action && typed === "") return null;
|
|
2675
|
-
const request = action ? typed ? `${action.request}
|
|
2676
|
-
|
|
2677
|
-
${typed}` : action.request : typed;
|
|
2678
|
-
return { request, action: action?.id ?? null };
|
|
2679
|
-
}
|
|
2680
|
-
/**
|
|
2681
|
-
* Start a turn. Returns once the process is running, not once it has answered;
|
|
2682
|
-
* the answer arrives through `onChange` and the next state.
|
|
2683
|
-
*/
|
|
2684
|
-
async ask(ask, context) {
|
|
2685
|
-
const agent = this.config.assistant.agent;
|
|
2686
|
-
if (!agent) throw new Error("the assistant is off; set DAILY_FOCUS_ASSISTANT to claude or codex");
|
|
2687
|
-
if (this.running.has(ask.itemId)) throw new Error("the assistant is already working on this item");
|
|
2688
|
-
const resolved = _Assistant.requestFor(ask);
|
|
2689
|
-
if (!resolved) throw new Error("nothing to ask: give a quick action or some text");
|
|
2690
|
-
await this.start();
|
|
2691
|
-
const previous = this.view().items[ask.itemId]?.sessionId ?? null;
|
|
2692
|
-
const turn = {
|
|
2693
|
-
id: randomUUID(),
|
|
2694
|
-
itemId: ask.itemId,
|
|
2695
|
-
agent,
|
|
2696
|
-
// Null until the CLI says: a resumed session that fails must not be
|
|
2697
|
-
// recorded as if this CLI had owned it.
|
|
2698
|
-
sessionId: null,
|
|
2699
|
-
request: resolved.request,
|
|
2700
|
-
action: resolved.action,
|
|
2701
|
-
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2702
|
-
endedAt: null,
|
|
2703
|
-
status: "running",
|
|
2704
|
-
reply: "",
|
|
2705
|
-
error: null
|
|
2706
|
-
};
|
|
2707
|
-
this.turns.set(turn.id, turn);
|
|
2708
|
-
await this.append({
|
|
2709
|
-
event: "started",
|
|
2710
|
-
turn: turn.id,
|
|
2711
|
-
item: turn.itemId,
|
|
2712
|
-
agent,
|
|
2713
|
-
request: turn.request,
|
|
2714
|
-
action: turn.action,
|
|
2715
|
-
at: turn.startedAt
|
|
2716
|
-
});
|
|
2717
|
-
const prompt = previous ? resolved.request : composePrompt(await this.instructions(), context, resolved.request, this.config.profile);
|
|
2718
|
-
await mkdir(this.config.assistantDir, { recursive: true });
|
|
2719
|
-
const adapter = ADAPTERS[agent];
|
|
2720
|
-
const args = adapter.args({
|
|
2721
|
-
prompt,
|
|
2722
|
-
sessionId: previous,
|
|
2723
|
-
model: this.config.assistant.model,
|
|
2724
|
-
effort: this.config.assistant.effort,
|
|
2725
|
-
tools: this.config.assistant.tools,
|
|
2726
|
-
denyEdits: true
|
|
2727
|
-
});
|
|
2728
|
-
let child;
|
|
2729
|
-
try {
|
|
2730
|
-
child = this.spawnFn(this.config.assistant.binPath, args, {
|
|
2731
|
-
cwd: this.config.assistantDir,
|
|
2732
|
-
// Stdin closed: both CLIs wait on it otherwise, one of them for three seconds.
|
|
2733
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
2734
|
-
env: process.env
|
|
2735
|
-
});
|
|
2736
|
-
} catch (err) {
|
|
2737
|
-
await this.finish(turn, { ok: false, error: this.spawnError(err) });
|
|
2738
|
-
return turn;
|
|
2739
|
-
}
|
|
2740
|
-
const timer = setTimeout(() => this.stop(turn.itemId, "gave up after ten minutes"), TURN_TIMEOUT_MS);
|
|
2741
|
-
timer.unref();
|
|
2742
|
-
const run6 = { turn, child, timer, abortReason: null };
|
|
2743
|
-
this.running.set(turn.itemId, run6);
|
|
2744
|
-
this.hooks.onChange();
|
|
2745
|
-
let done;
|
|
2746
|
-
const stderr = [];
|
|
2747
|
-
child.stderr?.on("data", (chunk) => {
|
|
2748
|
-
stderr.push(chunk.toString("utf8"));
|
|
2749
|
-
if (stderr.length > 50) stderr.shift();
|
|
2750
|
-
});
|
|
2751
|
-
if (child.stdout) {
|
|
2752
|
-
const lines = createInterface({ input: child.stdout });
|
|
2753
|
-
lines.on("line", (line) => {
|
|
2754
|
-
const event = adapter.parse(line);
|
|
2755
|
-
if (!event) return;
|
|
2756
|
-
if (event.sessionId) turn.sessionId = event.sessionId;
|
|
2757
|
-
if (event.text) {
|
|
2758
|
-
turn.reply = turn.reply ? `${turn.reply}
|
|
2759
|
-
|
|
2760
|
-
${event.text}` : event.text;
|
|
2761
|
-
this.hooks.onChange();
|
|
2762
|
-
}
|
|
2763
|
-
if (event.done) done = event.done;
|
|
2764
|
-
});
|
|
2765
|
-
}
|
|
2766
|
-
child.on("error", (err) => {
|
|
2767
|
-
done = { ok: false, error: this.spawnError(err) };
|
|
2768
|
-
});
|
|
2769
|
-
child.on("close", (code) => {
|
|
2770
|
-
clearTimeout(timer);
|
|
2771
|
-
this.running.delete(turn.itemId);
|
|
2772
|
-
const outcome = run6.abortReason ? { ok: false, error: run6.abortReason } : done ?? {
|
|
2773
|
-
ok: false,
|
|
2774
|
-
error: `${agent} exited with code ${code}${stderr.length ? `: ${stderr.join("").trim().split("\n").slice(-3).join(" ")}` : ""}`
|
|
2775
|
-
};
|
|
2776
|
-
void this.finish(turn, outcome, run6.abortReason !== null);
|
|
2777
|
-
});
|
|
2778
|
-
return turn;
|
|
2779
|
-
}
|
|
2780
|
-
/** Kill the turn running on an item, if any. */
|
|
2781
|
-
async stop(itemId, reason = "stopped") {
|
|
2782
|
-
const run6 = this.running.get(itemId);
|
|
2783
|
-
if (!run6) return false;
|
|
2784
|
-
run6.abortReason = reason;
|
|
2785
|
-
run6.child.kill("SIGTERM");
|
|
2786
|
-
return true;
|
|
2787
|
-
}
|
|
2788
|
-
/** Kill everything, for shutdown. The exit handlers write the aborted lines. */
|
|
2789
|
-
async close() {
|
|
2790
|
-
for (const itemId of [...this.running.keys()]) await this.stop(itemId, "the dashboard shut down");
|
|
2791
|
-
}
|
|
2792
|
-
async finish(turn, outcome, aborted = false) {
|
|
2793
|
-
const at = (/* @__PURE__ */ new Date()).toISOString();
|
|
2794
|
-
const reply = outcome.reply ?? turn.reply;
|
|
2795
|
-
turn.reply = reply;
|
|
2796
|
-
turn.endedAt = at;
|
|
2797
|
-
if (outcome.ok) {
|
|
2798
|
-
turn.status = "done";
|
|
2799
|
-
await this.append({ event: "finished", turn: turn.id, sessionId: turn.sessionId, reply, at });
|
|
2800
|
-
} else if (aborted) {
|
|
2801
|
-
turn.status = "aborted";
|
|
2802
|
-
turn.error = outcome.error ?? "stopped";
|
|
2803
|
-
await this.append({ event: "aborted", turn: turn.id, sessionId: turn.sessionId, reason: turn.error, reply, at });
|
|
2804
|
-
} else {
|
|
2805
|
-
turn.status = "failed";
|
|
2806
|
-
turn.error = outcome.error ?? "failed";
|
|
2807
|
-
await this.append({ event: "failed", turn: turn.id, sessionId: turn.sessionId, error: turn.error, reply, at });
|
|
2808
|
-
}
|
|
2809
|
-
this.hooks.onChange();
|
|
2810
|
-
}
|
|
2811
|
-
spawnError(err) {
|
|
2812
|
-
const code = err.code;
|
|
2813
|
-
const bin = this.config.assistant.binPath;
|
|
2814
|
-
if (code === "ENOENT") return `could not find ${bin}; install it or set DAILY_FOCUS_ASSISTANT_BIN to its path`;
|
|
2815
|
-
return `could not run ${bin}: ${err instanceof Error ? err.message : String(err)}`;
|
|
2816
|
-
}
|
|
2817
|
-
/** The store's copy, or the repo's before `npm run init` has linked it. */
|
|
2818
|
-
async instructions() {
|
|
2819
|
-
for (const path of [this.config.assistantPromptFile, this.config.assistantPromptSource]) {
|
|
2820
|
-
try {
|
|
2821
|
-
return await readFile6(path, "utf8");
|
|
2822
|
-
} catch {
|
|
2823
|
-
}
|
|
2824
|
-
}
|
|
2825
|
-
return "You are helping with one item from a personal dashboard. Read only; never send, post or edit anything.";
|
|
2826
|
-
}
|
|
2827
|
-
/**
|
|
2828
|
-
* The log records what happened; the turn in memory is updated by the caller
|
|
2829
|
-
* first. Only this process writes the file, so the memory never has to be
|
|
2830
|
-
* re-read from it.
|
|
2831
|
-
*/
|
|
2832
|
-
async append(record) {
|
|
2833
|
-
await mkdir(this.config.dataDir, { recursive: true });
|
|
2834
|
-
await appendFile(this.config.assistantLogFile, `${JSON.stringify(record)}
|
|
2835
|
-
`, "utf8");
|
|
2836
|
-
}
|
|
2837
|
-
};
|
|
2838
|
-
|
|
2839
|
-
// src/agent.ts
|
|
2840
2389
|
var RUN_TIMEOUT_MS = 60 * 6e4;
|
|
2841
|
-
var
|
|
2390
|
+
var TURN_TIMEOUT_MS = 10 * 6e4;
|
|
2842
2391
|
var RUNS_SHOWN = 30;
|
|
2843
2392
|
function agentPrompt(promptPath) {
|
|
2844
2393
|
return [
|
|
@@ -2952,7 +2501,7 @@ var AgentRunner = class {
|
|
|
2952
2501
|
config;
|
|
2953
2502
|
hooks;
|
|
2954
2503
|
spawnFn;
|
|
2955
|
-
constructor(config, hooks, spawnFn =
|
|
2504
|
+
constructor(config, hooks, spawnFn = spawn) {
|
|
2956
2505
|
this.config = config;
|
|
2957
2506
|
this.hooks = hooks;
|
|
2958
2507
|
this.spawnFn = spawnFn;
|
|
@@ -2969,7 +2518,7 @@ var AgentRunner = class {
|
|
|
2969
2518
|
this.loaded = true;
|
|
2970
2519
|
let text = "";
|
|
2971
2520
|
try {
|
|
2972
|
-
text = await
|
|
2521
|
+
text = await readFile6(this.config.agentLogFile, "utf8");
|
|
2973
2522
|
} catch {
|
|
2974
2523
|
return;
|
|
2975
2524
|
}
|
|
@@ -3056,7 +2605,7 @@ var AgentRunner = class {
|
|
|
3056
2605
|
}
|
|
3057
2606
|
await this.start();
|
|
3058
2607
|
const run6 = {
|
|
3059
|
-
id:
|
|
2608
|
+
id: randomUUID(),
|
|
3060
2609
|
cli,
|
|
3061
2610
|
trigger,
|
|
3062
2611
|
sessionId: null,
|
|
@@ -3106,7 +2655,7 @@ var AgentRunner = class {
|
|
|
3106
2655
|
const text = question.trim();
|
|
3107
2656
|
if (text === "") throw new Error("nothing to ask");
|
|
3108
2657
|
const turn = {
|
|
3109
|
-
id:
|
|
2658
|
+
id: randomUUID(),
|
|
3110
2659
|
question: text,
|
|
3111
2660
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3112
2661
|
endedAt: null,
|
|
@@ -3126,7 +2675,7 @@ var AgentRunner = class {
|
|
|
3126
2675
|
tools: this.config.agent.tools,
|
|
3127
2676
|
denyEdits: true
|
|
3128
2677
|
});
|
|
3129
|
-
await this.spawnInto(run6, turn, args,
|
|
2678
|
+
await this.spawnInto(run6, turn, args, TURN_TIMEOUT_MS, "gave up after ten minutes");
|
|
3130
2679
|
return turn;
|
|
3131
2680
|
}
|
|
3132
2681
|
/**
|
|
@@ -3137,7 +2686,7 @@ var AgentRunner = class {
|
|
|
3137
2686
|
async spawnInto(run6, turn, args, timeoutMs, timeoutReason) {
|
|
3138
2687
|
const cli = run6.cli;
|
|
3139
2688
|
const adapter = ADAPTERS[cli];
|
|
3140
|
-
await
|
|
2689
|
+
await mkdir(this.config.dataDir, { recursive: true });
|
|
3141
2690
|
let child;
|
|
3142
2691
|
try {
|
|
3143
2692
|
child = this.spawnFn(this.config.agent.binPath, args, {
|
|
@@ -3162,7 +2711,7 @@ var AgentRunner = class {
|
|
|
3162
2711
|
if (stderr.length > 50) stderr.shift();
|
|
3163
2712
|
});
|
|
3164
2713
|
if (child.stdout) {
|
|
3165
|
-
const lines =
|
|
2714
|
+
const lines = createInterface({ input: child.stdout });
|
|
3166
2715
|
lines.on("line", (line) => {
|
|
3167
2716
|
const event = adapter.parse(line);
|
|
3168
2717
|
if (!event) return;
|
|
@@ -3257,8 +2806,8 @@ ${event.text}` : event.text;
|
|
|
3257
2806
|
return `could not run ${bin}: ${err instanceof Error ? err.message : String(err)}`;
|
|
3258
2807
|
}
|
|
3259
2808
|
async append(record) {
|
|
3260
|
-
await
|
|
3261
|
-
await
|
|
2809
|
+
await mkdir(this.config.dataDir, { recursive: true });
|
|
2810
|
+
await appendFile(this.config.agentLogFile, `${JSON.stringify(record)}
|
|
3262
2811
|
`, "utf8");
|
|
3263
2812
|
}
|
|
3264
2813
|
};
|
|
@@ -3790,7 +3339,7 @@ async function saveSettings(path, settings) {
|
|
|
3790
3339
|
|
|
3791
3340
|
// src/setup.ts
|
|
3792
3341
|
import { execFile as execFile5 } from "node:child_process";
|
|
3793
|
-
import { access, constants, readdir as readdir2, readFile as
|
|
3342
|
+
import { access, constants, readdir as readdir2, readFile as readFile7, stat as stat3 } from "node:fs/promises";
|
|
3794
3343
|
import { delimiter, isAbsolute, join as join3 } from "node:path";
|
|
3795
3344
|
import { promisify as promisify5 } from "node:util";
|
|
3796
3345
|
var run5 = promisify5(execFile5);
|
|
@@ -3857,7 +3406,7 @@ async function firstBriefPending(config) {
|
|
|
3857
3406
|
async function setupState(config, opts) {
|
|
3858
3407
|
let sources = null;
|
|
3859
3408
|
try {
|
|
3860
|
-
sources = await
|
|
3409
|
+
sources = await readFile7(config.sourcesFile, "utf8");
|
|
3861
3410
|
} catch {
|
|
3862
3411
|
sources = null;
|
|
3863
3412
|
}
|
|
@@ -3909,13 +3458,13 @@ function fillIdentity(template, who) {
|
|
|
3909
3458
|
}
|
|
3910
3459
|
|
|
3911
3460
|
// src/shellpath.ts
|
|
3912
|
-
import { spawn as
|
|
3461
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
3913
3462
|
import { userInfo } from "node:os";
|
|
3914
3463
|
var START = "__DAILY_FOCUS_PATH_START__";
|
|
3915
3464
|
var END = "__DAILY_FOCUS_PATH_END__";
|
|
3916
3465
|
var SHELL_TIMEOUT_MS = 5e3;
|
|
3917
3466
|
var defaultExec = (file, args, { timeout }) => new Promise((resolve2, reject) => {
|
|
3918
|
-
const child =
|
|
3467
|
+
const child = spawn2(file, args, { detached: true, stdio: ["ignore", "pipe", "ignore"] });
|
|
3919
3468
|
let output = "";
|
|
3920
3469
|
let settled = false;
|
|
3921
3470
|
const finish = (error) => {
|
|
@@ -4151,9 +3700,9 @@ async function startServer(env, options = {}) {
|
|
|
4151
3700
|
});
|
|
4152
3701
|
const subscribers = /* @__PURE__ */ new Set();
|
|
4153
3702
|
let assetVersion = await computeAssetVersion(PUBLIC_DIR);
|
|
4154
|
-
const board = new Board(config, () => void broadcast());
|
|
3703
|
+
const board = new Board(config, () => void broadcast(), options.integrations?.github);
|
|
4155
3704
|
const calendar = new CalendarBoard(config, () => void broadcast());
|
|
4156
|
-
const tickets = new TicketBoard(config, () => void broadcast());
|
|
3705
|
+
const tickets = new TicketBoard(config, () => void broadcast(), options.integrations?.jira);
|
|
4157
3706
|
const assistant = new Assistant(config, {
|
|
4158
3707
|
onChange: () => {
|
|
4159
3708
|
void broadcast();
|
|
@@ -4780,7 +4329,7 @@ async function runServer(options = {}) {
|
|
|
4780
4329
|
let current = null;
|
|
4781
4330
|
let stopping = false;
|
|
4782
4331
|
async function start() {
|
|
4783
|
-
current = await startServer(
|
|
4332
|
+
current = await startServer(options.env, { onRestart: () => void restart(), integrations: options.integrations });
|
|
4784
4333
|
}
|
|
4785
4334
|
async function restart() {
|
|
4786
4335
|
console.log("[daily-focus] restarting to apply new settings");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
loadConfig
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-OPASVYLR.js";
|
|
4
4
|
import {
|
|
5
5
|
IN_NPX_CACHE,
|
|
6
6
|
PACKAGED,
|
|
@@ -8,7 +8,6 @@ import {
|
|
|
8
8
|
command,
|
|
9
9
|
ranDirectly
|
|
10
10
|
} from "./chunk-BC2W7NND.js";
|
|
11
|
-
import "./chunk-K2VT4JBR.js";
|
|
12
11
|
|
|
13
12
|
// scripts/service.ts
|
|
14
13
|
import { execFile } from "node:child_process";
|