@tekmidian/pai 0.59.0 → 0.60.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/.claude-plugin/plugin.json +8 -33
- package/PLUGIN-ARCHITECTURE.md +4 -6
- package/dist/{aibroker-client-B8Wtasez.mjs → aibroker-client-BYG-GtAg.mjs} +1 -1
- package/dist/{chain-Cltx0NjY.mjs → chain-CGyAIHo4.mjs} +4 -4
- package/dist/{chain-Cltx0NjY.mjs.map → chain-CGyAIHo4.mjs.map} +1 -1
- package/dist/cli/index.mjs +12 -12
- package/dist/cli/program.mjs +12 -12
- package/dist/{config-D1omG_UU.mjs → config-CQCmH_gV.mjs} +17 -4
- package/dist/{config-D1omG_UU.mjs.map → config-CQCmH_gV.mjs.map} +1 -1
- package/dist/{config-DCYWFaEi.mjs → config-DjXq_W42.mjs} +1 -1
- package/dist/{context-handover-cache-Dt4HW1Ro.mjs → context-handover-cache-BzM7Qudn.mjs} +4 -4
- package/dist/{context-handover-cache-Dt4HW1Ro.mjs.map → context-handover-cache-BzM7Qudn.mjs.map} +1 -1
- package/dist/daemon/index.mjs +12 -12
- package/dist/{daemon-CmqJfeb9.mjs → daemon-B_TodsIS.mjs} +97 -23
- package/dist/daemon-B_TodsIS.mjs.map +1 -0
- package/dist/daemon-jviL3VVJ.mjs +19 -0
- package/dist/daemon-mcp/index.mjs +9 -9
- package/dist/daemon-mcp/index.mjs.map +1 -1
- package/dist/{factory-C9wGUe30.mjs → factory-DW7BCJJT.mjs} +1 -1
- package/dist/{factory-DiuntvwG.mjs → factory-EUUhSSoq.mjs} +7 -8
- package/dist/factory-EUUhSSoq.mjs.map +1 -0
- package/dist/{fallback-B_2ozQHw.mjs → fallback-CjdakQVu.mjs} +80 -9
- package/dist/fallback-CjdakQVu.mjs.map +1 -0
- package/dist/hooks/block-sleep-poll.mjs +7590 -0
- package/dist/hooks/block-sleep-poll.mjs.map +7 -0
- package/dist/hooks/capture-all-events.mjs +8 -4
- package/dist/hooks/capture-all-events.mjs.map +2 -2
- package/dist/hooks/capture-session-summary.mjs.map +1 -1
- package/dist/hooks/capture-tool-output.mjs.map +1 -1
- package/dist/hooks/cleanup-session-files.mjs.map +1 -1
- package/dist/hooks/context-compression-hook.mjs +6 -6
- package/dist/hooks/context-compression-hook.mjs.map +3 -3
- package/dist/hooks/initialize-session.mjs.map +1 -1
- package/dist/hooks/load-core-context.mjs.map +1 -1
- package/dist/hooks/load-project-context.mjs.map +2 -2
- package/dist/hooks/post-compact-inject.mjs +1 -1
- package/dist/hooks/post-compact-inject.mjs.map +2 -2
- package/dist/hooks/route-agents-to-worker.mjs +5 -4
- package/dist/hooks/route-agents-to-worker.mjs.map +3 -3
- package/dist/hooks/security-validator.mjs +5 -4
- package/dist/hooks/security-validator.mjs.map +3 -3
- package/dist/hooks/stop-hook.mjs.map +1 -1
- package/dist/hooks/sync-todo-to-md.mjs.map +1 -1
- package/dist/hooks/whisper-rules.mjs +5 -4
- package/dist/hooks/whisper-rules.mjs.map +3 -3
- package/dist/hooks/worker-proxy.mjs +5 -4
- package/dist/hooks/worker-proxy.mjs.map +3 -3
- package/dist/hooks/worker-status-line.mjs +10 -7
- package/dist/hooks/worker-status-line.mjs.map +3 -3
- package/dist/hooks/worker-supervision.mjs +5 -4
- package/dist/hooks/worker-supervision.mjs.map +3 -3
- package/dist/{main-resolver-DYNxxNwJ.mjs → main-resolver-BFQfU5xo.mjs} +100 -21
- package/dist/main-resolver-BFQfU5xo.mjs.map +1 -0
- package/dist/main-resolver-VMBENifT.mjs +7 -0
- package/dist/{planner-DsZZEZCA.mjs → planner-DN967Lvm.mjs} +83 -30
- package/dist/planner-DN967Lvm.mjs.map +1 -0
- package/dist/{program-B5miXqQa.mjs → program-BnsjB57Q.mjs} +278 -92
- package/dist/program-BnsjB57Q.mjs.map +1 -0
- package/dist/router-C5hPk9YP.mjs +3 -0
- package/dist/{router-BdxdsAvs.mjs → router-DjFZIoDl.mjs} +2 -1
- package/dist/router-DjFZIoDl.mjs.map +1 -0
- package/dist/{run-XaA-UPdW.mjs → run-FeM1_UVa.mjs} +102 -18
- package/dist/run-FeM1_UVa.mjs.map +1 -0
- package/dist/{run-env-DyTWgZC9.mjs → run-env-CEHTeIUM.mjs} +2 -2
- package/dist/{run-env-DyTWgZC9.mjs.map → run-env-CEHTeIUM.mjs.map} +1 -1
- package/dist/{server-CzqHGbKt.mjs → server-CekGOJcp.mjs} +2 -2
- package/dist/{server-CzqHGbKt.mjs.map → server-CekGOJcp.mjs.map} +1 -1
- package/dist/{session-keepalive-DDsEo81Q.mjs → session-keepalive-Bc9aT678.mjs} +5 -5
- package/dist/{session-keepalive-DDsEo81Q.mjs.map → session-keepalive-Bc9aT678.mjs.map} +1 -1
- package/dist/{tools-Diro2s8J.mjs → tools-qXyHFwNj.mjs} +2 -2
- package/dist/{tools-Diro2s8J.mjs.map → tools-qXyHFwNj.mjs.map} +1 -1
- package/dist/{tools-KgL1rkPD.mjs → tools-r-7ZG8B5.mjs} +3 -3
- package/dist/{wakeup-D0uvqLsE.mjs → wakeup-C48hcN6O.mjs} +2 -2
- package/dist/wakeup-C48hcN6O.mjs.map +1 -0
- package/dist/{work-queue-worker-DL9lu8sN.mjs → work-queue-worker-BL4dq5P7.mjs} +2 -2
- package/dist/{work-queue-worker-DL9lu8sN.mjs.map → work-queue-worker-BL4dq5P7.mjs.map} +1 -1
- package/dist/work-queue-worker-DVOVcEnO.mjs +11 -0
- package/docker/init.sql +1 -1
- package/docs/commands/README.md +4 -4
- package/docs/commands/daemon.md +6 -6
- package/docs/commands/session.md +2 -2
- package/docs/commands/worker.md +5 -2
- package/docs/workers-config.md +5 -1
- package/package.json +1 -1
- package/plugins/context-preservation/hooks/hooks.json +38 -31
- package/plugins/context-preservation/plugin.json +1 -4
- package/plugins/core/hooks/hooks.json +51 -46
- package/plugins/core/plugin.json +1 -4
- package/plugins/creative/plugin.json +1 -5
- package/plugins/observability/hooks/hooks.json +87 -72
- package/plugins/observability/plugin.json +1 -5
- package/plugins/productivity/hooks/hooks.json +22 -19
- package/plugins/productivity/plugin.json +2 -5
- package/plugins/semantic-search/plugin.json +1 -16
- package/plugins/ui/hooks/hooks.json +17 -14
- package/plugins/ui/plugin.json +1 -5
- package/plugins/zettelkasten/plugin.json +1 -14
- package/src/hooks/session-autosave.sh +4 -2
- package/src/hooks/session-stop.sh +2 -2
- package/src/hooks/ts/capture-all-events.ts +3 -3
- package/src/hooks/ts/lib/metadata-extraction.ts +3 -2
- package/src/hooks/ts/lib/pai-paths.test.ts +1 -1
- package/src/hooks/ts/lib/sleep-poll-gate.test.ts +62 -0
- package/src/hooks/ts/lib/sleep-poll-gate.ts +77 -0
- package/src/hooks/ts/post-tool-use/capture-tool-output.ts +1 -1
- package/src/hooks/ts/post-tool-use/sync-todo-to-md.ts +1 -1
- package/src/hooks/ts/pre-compact/context-compression-hook.ts +1 -1
- package/src/hooks/ts/pre-tool-use/block-sleep-poll.test.ts +44 -0
- package/src/hooks/ts/pre-tool-use/block-sleep-poll.ts +50 -0
- package/src/hooks/ts/session-end/capture-session-summary.ts +1 -1
- package/src/hooks/ts/session-start/initialize-session.ts +2 -2
- package/src/hooks/ts/session-start/load-core-context.ts +2 -2
- package/src/hooks/ts/session-start/load-project-context.ts +1 -1
- package/src/hooks/ts/stop/stop-hook.ts +1 -1
- package/src/hooks/ts/user-prompt/cleanup-session-files.ts +1 -1
- package/statusline-command.sh +26 -11
- package/dist/daemon-B6hx1Ysz.mjs +0 -19
- package/dist/daemon-CmqJfeb9.mjs.map +0 -1
- package/dist/factory-DiuntvwG.mjs.map +0 -1
- package/dist/fallback-B_2ozQHw.mjs.map +0 -1
- package/dist/main-resolver-DYNxxNwJ.mjs.map +0 -1
- package/dist/main-resolver-Df65bPAi.mjs +0 -7
- package/dist/planner-DsZZEZCA.mjs.map +0 -1
- package/dist/program-B5miXqQa.mjs.map +0 -1
- package/dist/router-BXOf_e9e.mjs +0 -3
- package/dist/router-BdxdsAvs.mjs.map +0 -1
- package/dist/run-XaA-UPdW.mjs.map +0 -1
- package/dist/wakeup-D0uvqLsE.mjs.map +0 -1
- package/dist/work-queue-worker-BY7xn4pw.mjs +0 -11
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sleep-poll-gate.ts — the decision behind the PreToolUse Bash sleep-poll gate.
|
|
3
|
+
*
|
|
4
|
+
* Pure: no stdin, no filesystem, no process.env read of its own — the hook
|
|
5
|
+
* entry (pre-tool-use/block-sleep-poll.ts) does the I/O, this module decides,
|
|
6
|
+
* same split as lib/agent-gate.ts and lib/edit-gate.ts.
|
|
7
|
+
*
|
|
8
|
+
* CLAUDE.md already says "never sleep-poll"; prose does not enforce it, a
|
|
9
|
+
* PreToolUse deny does. `run_in_background: true` is always allowed — a
|
|
10
|
+
* backgrounded sleep blocks nothing, it is the harness's own long-command
|
|
11
|
+
* pattern.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { longestSleepSecs } from "../../../workers/status.js";
|
|
15
|
+
|
|
16
|
+
export interface SleepPollGateInput {
|
|
17
|
+
tool_name?: string;
|
|
18
|
+
tool_input?: { command?: string; run_in_background?: boolean };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type GateDecision = { decision: "allow" } | { decision: "deny"; reason: string };
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* A headless worker has no owner to deliver a background-exit notification
|
|
25
|
+
* to — ending its turn ends the process, so the interactive session's advice
|
|
26
|
+
* ("background it, the harness notifies you") does not apply. Text mirrors
|
|
27
|
+
* the worker contract's own sleep-poll line (workerContractPrompt in
|
|
28
|
+
* src/workers/report.ts) so a denied worker sees the same instruction twice.
|
|
29
|
+
*/
|
|
30
|
+
export function denyReason(secs: number, isWorker: boolean): string {
|
|
31
|
+
const lead = `sleep ${secs}s blocked: do not sleep-poll. `;
|
|
32
|
+
if (isWorker) {
|
|
33
|
+
return (
|
|
34
|
+
lead +
|
|
35
|
+
"In a worker, run the long command in the FOREGROUND with a Bash timeout up to 600000 ms. " +
|
|
36
|
+
"If it can run longer, start it in the background and wait with a foreground loop that ends " +
|
|
37
|
+
"when the job ends: while kill -0 <pid> 2>/dev/null; do sleep 15; done"
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
return (
|
|
41
|
+
lead +
|
|
42
|
+
"Run the long command itself with run_in_background: true (the harness notifies when it exits), " +
|
|
43
|
+
"or wait on a specific condition with a bounded loop under 60s per step."
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function decideSleepPollGate(
|
|
48
|
+
input: SleepPollGateInput,
|
|
49
|
+
env: Record<string, string | undefined>
|
|
50
|
+
): GateDecision {
|
|
51
|
+
if (input.tool_name && input.tool_name !== "Bash") return { decision: "allow" };
|
|
52
|
+
if (input.tool_input?.run_in_background === true) return { decision: "allow" };
|
|
53
|
+
|
|
54
|
+
const command = input.tool_input?.command;
|
|
55
|
+
if (typeof command !== "string") return { decision: "allow" };
|
|
56
|
+
|
|
57
|
+
const secs = longestSleepSecs(command);
|
|
58
|
+
if (secs === null) return { decision: "allow" };
|
|
59
|
+
|
|
60
|
+
return { decision: "deny", reason: denyReason(secs, env.PAI_WORKER === "1") };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Render a decision as the JSON the harness reads on the hook's stdout. */
|
|
64
|
+
export function renderDecision(d: GateDecision): string {
|
|
65
|
+
if (d.decision === "allow") {
|
|
66
|
+
return JSON.stringify({
|
|
67
|
+
hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "allow" },
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
return JSON.stringify({
|
|
71
|
+
hookSpecificOutput: {
|
|
72
|
+
hookEventName: "PreToolUse",
|
|
73
|
+
permissionDecision: "deny",
|
|
74
|
+
permissionDecisionReason: d.reason,
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
}
|
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
updateTodoContinue,
|
|
32
32
|
isProbeSession,
|
|
33
33
|
WorkItem,
|
|
34
|
-
} from '../lib/project-utils';
|
|
34
|
+
} from '../lib/project-utils.js';
|
|
35
35
|
import { getContextFill, formatContextFill } from '../lib/context-fill.js';
|
|
36
36
|
import { contentToText, isNoiseFilePath, preferCwdFiles } from '../lib/transcript-text.js';
|
|
37
37
|
import { readContextHandoverCache } from '../lib/context-handover-cache.js';
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { execFileSync } from "node:child_process";
|
|
3
|
+
|
|
4
|
+
// The entry does its own stdin read and calls main() unconditionally at
|
|
5
|
+
// module scope — run it as a real subprocess with its own piped stdin,
|
|
6
|
+
// exactly like route-edits-to-worker.test.ts.
|
|
7
|
+
describe("block-sleep-poll", () => {
|
|
8
|
+
const ENTRYPOINT = "src/hooks/ts/pre-tool-use/block-sleep-poll.ts";
|
|
9
|
+
|
|
10
|
+
function runHook(command: string, extra: Record<string, unknown> = {}, env: NodeJS.ProcessEnv = {}): string {
|
|
11
|
+
// This test may itself run as a worker (PAI_WORKER=1 in its own env),
|
|
12
|
+
// which must not leak into the child and mask the case under test.
|
|
13
|
+
const { PAI_WORKER: _drop, ...baseEnv } = process.env;
|
|
14
|
+
return execFileSync("bun", [ENTRYPOINT], {
|
|
15
|
+
input: JSON.stringify({ tool_name: "Bash", tool_input: { command, ...extra } }),
|
|
16
|
+
encoding: "utf8",
|
|
17
|
+
timeout: 15_000,
|
|
18
|
+
env: { ...baseEnv, ...env },
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
it("denies a long sleep (interactive session)", () => {
|
|
23
|
+
const out = runHook("sleep 590");
|
|
24
|
+
const parsed = JSON.parse(out).hookSpecificOutput;
|
|
25
|
+
expect(parsed.permissionDecision).toBe("deny");
|
|
26
|
+
expect(parsed.permissionDecisionReason).toContain("sleep 590s blocked");
|
|
27
|
+
expect(parsed.permissionDecisionReason).toContain("run_in_background: true");
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("denies a long sleep with the worker-specific reason when PAI_WORKER=1", () => {
|
|
31
|
+
const out = runHook("sleep 590", {}, { PAI_WORKER: "1" });
|
|
32
|
+
const parsed = JSON.parse(out).hookSpecificOutput;
|
|
33
|
+
expect(parsed.permissionDecision).toBe("deny");
|
|
34
|
+
expect(parsed.permissionDecisionReason).toContain("FOREGROUND with a Bash timeout up to 600000 ms");
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("allows a short sleep (prints nothing)", () => {
|
|
38
|
+
expect(runHook("sleep 30")).toBe("");
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("allows a long sleep in the background (prints nothing)", () => {
|
|
42
|
+
expect(runHook("sleep 590", { run_in_background: true })).toBe("");
|
|
43
|
+
});
|
|
44
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* block-sleep-poll.ts — PreToolUse (Bash)
|
|
5
|
+
*
|
|
6
|
+
* Denies a Bash command that contains a `sleep N` of SLEEP_FLOOR_SECS or more
|
|
7
|
+
* as its own command segment — anywhere in the command, not only leading it —
|
|
8
|
+
* unless the call is backgrounded (`run_in_background: true`), which blocks
|
|
9
|
+
* nothing. CLAUDE.md already says "never sleep-poll"; this hook is the
|
|
10
|
+
* enforcement, prose alone does not stop it (observed 2026-09-29: sessions and
|
|
11
|
+
* workers kept running `sleep 590` waits on a background job anyway).
|
|
12
|
+
*
|
|
13
|
+
* This file is only I/O: read stdin, ask lib/sleep-poll-gate for the
|
|
14
|
+
* decision, print it. The decision itself lives in lib/sleep-poll-gate.ts and
|
|
15
|
+
* is tested there.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { decideSleepPollGate, renderDecision, type SleepPollGateInput } from "../lib/sleep-poll-gate.js";
|
|
19
|
+
|
|
20
|
+
function allow(): void {
|
|
21
|
+
process.exit(0);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function deny(reason: string): void {
|
|
25
|
+
process.stdout.write(renderDecision({ decision: "deny", reason }) + "\n");
|
|
26
|
+
process.exit(0);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function main(): Promise<void> {
|
|
30
|
+
let text = "";
|
|
31
|
+
try {
|
|
32
|
+
for await (const chunk of process.stdin) text += chunk;
|
|
33
|
+
} catch {
|
|
34
|
+
return allow();
|
|
35
|
+
}
|
|
36
|
+
if (!text.trim()) return allow();
|
|
37
|
+
|
|
38
|
+
let input: SleepPollGateInput;
|
|
39
|
+
try {
|
|
40
|
+
input = JSON.parse(text) as SleepPollGateInput;
|
|
41
|
+
} catch {
|
|
42
|
+
return allow();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const decision = decideSleepPollGate(input, process.env);
|
|
46
|
+
if (decision.decision === "deny") return deny(decision.reason);
|
|
47
|
+
return allow();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
main().catch(() => process.exit(0));
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import { writeFileSync, mkdirSync, existsSync, readFileSync, readdirSync } from 'fs';
|
|
11
11
|
import { join } from 'path';
|
|
12
|
-
import { historyDir } from '../lib/pai-paths';
|
|
12
|
+
import { historyDir } from '../lib/pai-paths.js';
|
|
13
13
|
|
|
14
14
|
interface SessionData {
|
|
15
15
|
conversation_id: string;
|
|
@@ -24,8 +24,8 @@ import { isWorkerSession } from "../lib/worker-session.js";
|
|
|
24
24
|
import { existsSync, statSync, readFileSync, writeFileSync } from 'fs';
|
|
25
25
|
import { join } from 'path';
|
|
26
26
|
import { tmpdir } from 'os';
|
|
27
|
-
import { ADAPTER_DIR } from '../lib/pai-paths';
|
|
28
|
-
import { sendNtfyNotification, isWhatsAppEnabled, isProbeSession } from '../lib/project-utils';
|
|
27
|
+
import { ADAPTER_DIR } from '../lib/pai-paths.js';
|
|
28
|
+
import { sendNtfyNotification, isWhatsAppEnabled, isProbeSession } from '../lib/project-utils.js';
|
|
29
29
|
|
|
30
30
|
// Debounce duration in milliseconds (prevents duplicate SessionStart events)
|
|
31
31
|
const DEBOUNCE_MS = 2000;
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
import { isWorkerSession } from "../lib/worker-session.js";
|
|
29
29
|
import { readFileSync, existsSync } from 'fs';
|
|
30
30
|
import { join } from 'path';
|
|
31
|
-
import { SKILLS_DIR } from '../lib/pai-paths';
|
|
32
|
-
import { isProbeSession } from '../lib/project-utils';
|
|
31
|
+
import { SKILLS_DIR } from '../lib/pai-paths.js';
|
|
32
|
+
import { isProbeSession } from '../lib/project-utils.js';
|
|
33
33
|
import { stripFrontmatter } from '../lib/frontmatter.js';
|
|
34
34
|
|
|
35
35
|
async function main() {
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
isProbeSession,
|
|
16
16
|
updateTodoContinue, sessionIdFromTranscript,
|
|
17
17
|
WorkItem
|
|
18
|
-
} from '../lib/project-utils';
|
|
18
|
+
} from '../lib/project-utils.js';
|
|
19
19
|
import { paiHomePath, sessionStateDir } from '../../../config/pai-files.js';
|
|
20
20
|
|
|
21
21
|
// ---------------------------------------------------------------------------
|
package/statusline-command.sh
CHANGED
|
@@ -28,6 +28,21 @@ claude_env="${PAI_DIR:-$HOME/.claude}/.env"
|
|
|
28
28
|
# Read JSON input from stdin
|
|
29
29
|
input=$(cat)
|
|
30
30
|
|
|
31
|
+
# GNU coreutils first, BSD/macOS fallback: `stat -f %m` on GNU is filesystem
|
|
32
|
+
# status (not "file", -f means "filesystem"), so it silently parses the path
|
|
33
|
+
# as a file operand and prints filesystem info instead of failing — a cache
|
|
34
|
+
# never reads as fresh. `stat -c` errors cleanly on BSD, so GNU-first is safe
|
|
35
|
+
# on both.
|
|
36
|
+
file_mtime() {
|
|
37
|
+
stat -c %Y "$1" 2>/dev/null || stat -f %m "$1" 2>/dev/null || printf '%s\n' "${2:-0}"
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
# Same GNU-first/BSD-fallback rule for formatting an epoch: `date -d @epoch`
|
|
41
|
+
# on GNU, `date -r epoch` on BSD/macOS.
|
|
42
|
+
fmt_epoch() {
|
|
43
|
+
date -d "@$1" "+$2" 2>/dev/null || date -r "$1" "+$2" 2>/dev/null || printf ''
|
|
44
|
+
}
|
|
45
|
+
|
|
31
46
|
# Get Digital Assistant configuration from environment
|
|
32
47
|
DA_NAME="${DA:-Assistant}" # Assistant name
|
|
33
48
|
DA_COLOR="${DA_COLOR:-purple}" # Color for the assistant name
|
|
@@ -81,7 +96,7 @@ model_id=$(echo "$input" | jq -r '.model.id // .model.display_name // empty' 2>/
|
|
|
81
96
|
cc_version=$(echo "$input" | jq -r '.version // empty' 2>/dev/null)
|
|
82
97
|
if [ -z "$cc_version" ]; then
|
|
83
98
|
_ccv_cache="${pai_cache_dir}/cc-version"
|
|
84
|
-
_ccv_age=$(( $(date +%s) - $(
|
|
99
|
+
_ccv_age=$(( $(date +%s) - $(file_mtime "$_ccv_cache" 0) ))
|
|
85
100
|
if [ -s "$_ccv_cache" ] && [ "$_ccv_age" -lt 86400 ] 2>/dev/null; then
|
|
86
101
|
read -r cc_version < "$_ccv_cache"
|
|
87
102
|
elif command -v claude >/dev/null 2>&1; then
|
|
@@ -133,8 +148,8 @@ if [ -f "$_workers_yaml" ] && command -v node >/dev/null 2>&1; then
|
|
|
133
148
|
_sl_script="$(readlink -f "$0" 2>/dev/null || echo "$0")"
|
|
134
149
|
_sl_script_dir="$(dirname "$_sl_script")"
|
|
135
150
|
_providers_cache="${pai_cache_dir}/statusline-providers.json"
|
|
136
|
-
_yaml_mtime=$(
|
|
137
|
-
_cache_mtime=$(
|
|
151
|
+
_yaml_mtime=$(file_mtime "$_workers_yaml" 0)
|
|
152
|
+
_cache_mtime=$(file_mtime "$_providers_cache" -1)
|
|
138
153
|
if [ ! -s "$_providers_cache" ] || [ "$_cache_mtime" -lt "$_yaml_mtime" ] 2>/dev/null; then
|
|
139
154
|
mkdir -p "$pai_cache_dir" 2>/dev/null
|
|
140
155
|
# umask 077 so the temp file is born 0600 — it must never exist at a
|
|
@@ -209,7 +224,7 @@ dir_name=$(basename "$current_dir" 2>/dev/null)
|
|
|
209
224
|
|
|
210
225
|
# Read Whazaa session name from iTerm2 user variable
|
|
211
226
|
pai_session_name=""
|
|
212
|
-
if [ -n "$ITERM_SESSION_ID" ]; then
|
|
227
|
+
if [ -n "$ITERM_SESSION_ID" ] && command -v osascript >/dev/null 2>&1; then
|
|
213
228
|
ITERM_UUID="${ITERM_SESSION_ID##*:}"
|
|
214
229
|
pai_session_name=$(osascript << APPLESCRIPT 2>/dev/null
|
|
215
230
|
tell application "iTerm2"
|
|
@@ -634,7 +649,7 @@ fi
|
|
|
634
649
|
# buys nothing when the live numbers arrived on stdin.
|
|
635
650
|
if [ "$session_provider" = "anthropic" ] && [ -z "$usage_source" ]; then
|
|
636
651
|
if [ -f "$usage_cache" ]; then
|
|
637
|
-
cache_age=$(( $(date +%s) - $(
|
|
652
|
+
cache_age=$(( $(date +%s) - $(file_mtime "$usage_cache" 0) ))
|
|
638
653
|
[ "$cache_age" -gt "$usage_cache_ttl" ] && _fetch_usage &
|
|
639
654
|
else
|
|
640
655
|
_fetch_usage &
|
|
@@ -643,7 +658,7 @@ fi
|
|
|
643
658
|
|
|
644
659
|
# Source 2 (fallback): the OAuth endpoint cache, and only while it is fresh.
|
|
645
660
|
if [ "$session_provider" = "anthropic" ] && [ -z "$usage_source" ] && [ -f "$usage_cache" ]; then
|
|
646
|
-
_usage_cache_age=$(( $(date +%s) - $(
|
|
661
|
+
_usage_cache_age=$(( $(date +%s) - $(file_mtime "$usage_cache" 0) ))
|
|
647
662
|
if [ "$_usage_cache_age" -le "$usage_cache_max_age" ] 2>/dev/null; then
|
|
648
663
|
usage_source="oauth"
|
|
649
664
|
five_hour=$(jq -r '.five_hour.utilization // empty' "$usage_cache" 2>/dev/null)
|
|
@@ -686,8 +701,8 @@ if [ "$session_provider" = "anthropic" ]; then
|
|
|
686
701
|
# Format reset times in local time
|
|
687
702
|
five_reset_fmt=""
|
|
688
703
|
seven_reset_fmt=""
|
|
689
|
-
[ "$five_reset_epoch" -gt 0 ] 2>/dev/null && five_reset_fmt=$(
|
|
690
|
-
[ "$seven_reset_epoch" -gt 0 ] 2>/dev/null && seven_reset_fmt=$(
|
|
704
|
+
[ "$five_reset_epoch" -gt 0 ] 2>/dev/null && five_reset_fmt=$(fmt_epoch "$five_reset_epoch" "%H:%M")
|
|
705
|
+
[ "$seven_reset_epoch" -gt 0 ] 2>/dev/null && seven_reset_fmt=$(fmt_epoch "$seven_reset_epoch" "%a %H:%M")
|
|
691
706
|
|
|
692
707
|
five_color=$(_usage_color "$five_hour_int")
|
|
693
708
|
seven_color=$(_usage_color "$seven_day_int")
|
|
@@ -834,7 +849,7 @@ if [ "$session_provider" != "anthropic" ]; then
|
|
|
834
849
|
# Use cache if fresh, otherwise refresh in the background
|
|
835
850
|
if [ -n "$usage_url" ]; then
|
|
836
851
|
if [ -f "$provider_cache" ]; then
|
|
837
|
-
cache_age=$(( $(date +%s) - $(
|
|
852
|
+
cache_age=$(( $(date +%s) - $(file_mtime "$provider_cache" 0) ))
|
|
838
853
|
if [ "$cache_age" -gt "$usage_ttl" ]; then
|
|
839
854
|
_fetch_provider_usage "$usage_url" "$usage_auth" "$provider_keyfile" "$provider_cache" "$usage_probe" &
|
|
840
855
|
fi
|
|
@@ -879,9 +894,9 @@ if [ "$session_provider" != "anthropic" ]; then
|
|
|
879
894
|
# Within the next 24h the weekday carries no information;
|
|
880
895
|
# beyond it, the bare time would be ambiguous.
|
|
881
896
|
if [ $(( w_epoch - $(date +%s) )) -lt 86400 ]; then
|
|
882
|
-
w_reset_fmt=$(
|
|
897
|
+
w_reset_fmt=$(fmt_epoch "$w_epoch" "%H:%M")
|
|
883
898
|
else
|
|
884
|
-
w_reset_fmt=$(
|
|
899
|
+
w_reset_fmt=$(fmt_epoch "$w_epoch" "%a %H:%M")
|
|
885
900
|
fi
|
|
886
901
|
fi
|
|
887
902
|
fi
|
package/dist/daemon-B6hx1Ysz.mjs
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import "./utils-C9HsYDpO.mjs";
|
|
2
|
-
import "./migrate-BD7D8EEh.mjs";
|
|
3
|
-
import "./factory-DiuntvwG.mjs";
|
|
4
|
-
import "./wakeup-D0uvqLsE.mjs";
|
|
5
|
-
import "./context-handover-cache-Dt4HW1Ro.mjs";
|
|
6
|
-
import "./config-D1omG_UU.mjs";
|
|
7
|
-
import "./run-env-DyTWgZC9.mjs";
|
|
8
|
-
import "./run-XaA-UPdW.mjs";
|
|
9
|
-
import "./server-CzqHGbKt.mjs";
|
|
10
|
-
import "./state-n13uPK5B.mjs";
|
|
11
|
-
import "./aibroker-client-BK1h--W_.mjs";
|
|
12
|
-
import "./session-keepalive-DDsEo81Q.mjs";
|
|
13
|
-
import "./embeddings-DfrYNIng.mjs";
|
|
14
|
-
import { t as serve } from "./daemon-CmqJfeb9.mjs";
|
|
15
|
-
import "./router-BdxdsAvs.mjs";
|
|
16
|
-
import "./tools-Diro2s8J.mjs";
|
|
17
|
-
import "./work-queue-worker-DL9lu8sN.mjs";
|
|
18
|
-
|
|
19
|
-
export { serve };
|