@tokenfactory/acc-runner 0.40.24 → 0.40.26
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/chat-poll/index.d.ts +132 -0
- package/dist/chat-poll/index.d.ts.map +1 -0
- package/dist/chat-poll/index.js +200 -0
- package/dist/chat-poll/index.js.map +1 -0
- package/dist/doctor.d.ts +3 -2
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +40 -19
- package/dist/doctor.js.map +1 -1
- package/dist/provider-auth.d.ts +56 -11
- package/dist/provider-auth.d.ts.map +1 -1
- package/dist/provider-auth.js +89 -11
- package/dist/provider-auth.js.map +1 -1
- package/dist/watch.d.ts.map +1 -1
- package/dist/watch.js +20 -12
- package/dist/watch.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CHAT-HYGIENE AC2 — the companion/fleet DB-POLL FALLBACK
|
|
3
|
+
* for chat turn discovery.
|
|
4
|
+
*
|
|
5
|
+
* WHY THIS EXISTS. A companion (chat-companion.ts) / fleet lane
|
|
6
|
+
* (watch-chat/fleet-chat-lane.ts) discovers which conversations to serve from
|
|
7
|
+
* TWO Realtime-backed signals: the browser's `conversation_active` announce on
|
|
8
|
+
* the `identity:<user>` topic (companion.ts createConversationListener, FU-4)
|
|
9
|
+
* and the `chat:<conversation_id>` turn broadcasts. Both ride Supabase Realtime,
|
|
10
|
+
* which is BEST-EFFORT (no ack, no retry). During a Realtime DELIVERY BLACKOUT —
|
|
11
|
+
* a dropped socket, a Realtime outage, a missed announce — a freshly queued turn
|
|
12
|
+
* is never surfaced, so the companion never claims it and the operator's chat
|
|
13
|
+
* hangs with no reply (the 2026-07-21/22 incident shape).
|
|
14
|
+
*
|
|
15
|
+
* The claim PUMP already runs on its own interval, but it only claims across the
|
|
16
|
+
* conversations already in the served set — a conversation the blackout hid from
|
|
17
|
+
* discovery is never pumped. This module closes that gap with a discovery path
|
|
18
|
+
* that does NOT depend on Realtime at all: a heartbeat poller that queries the DB
|
|
19
|
+
* directly for the bound identity's OWNED conversations carrying a `queued` turn,
|
|
20
|
+
* and feeds them to a sink (in production: fold into the owner-affinity source +
|
|
21
|
+
* nudge the pump). It is the DB twin of the Realtime discovery signals — the
|
|
22
|
+
* safety net that guarantees a queued turn is served within one poll interval
|
|
23
|
+
* even with Realtime entirely dead.
|
|
24
|
+
*
|
|
25
|
+
* Pure primitive: every I/O seam (the DB query, the on-due sink, the clock) is
|
|
26
|
+
* injected, so the whole fallback is exercised deterministically
|
|
27
|
+
* (tests/unit/chat-hygiene, tests/e2e/chat-hygiene) with no network and no
|
|
28
|
+
* Supabase Realtime. Wiring it into `acc-runner companion` / `watch` is a
|
|
29
|
+
* follow-up (mirrors the FLEET-SERVE watch-chat primitives-then-wire split); the
|
|
30
|
+
* primitive + chaos proof land here.
|
|
31
|
+
*/
|
|
32
|
+
/** Env var that opts OUT of the DB-poll fallback (default: on). */
|
|
33
|
+
export declare const CHAT_POLL_ENABLED_ENV = "ACC_RUNNER_CHAT_POLL";
|
|
34
|
+
/** Env var carrying the DB-poll heartbeat cadence (ms). */
|
|
35
|
+
export declare const CHAT_POLL_INTERVAL_MS_ENV = "ACC_RUNNER_CHAT_POLL_MS";
|
|
36
|
+
/**
|
|
37
|
+
* Default poll cadence (ms). Sits in the same ~15-30s band as the companion's
|
|
38
|
+
* FU-10 rescan backstop: short enough that a Realtime-lost queued turn is
|
|
39
|
+
* served within a barely-perceptible delay, long enough that the bounded
|
|
40
|
+
* owner-affinity scan stays trivially cheap.
|
|
41
|
+
*/
|
|
42
|
+
export declare const DEFAULT_CHAT_POLL_INTERVAL_MS = 15000;
|
|
43
|
+
/** Clamp bounds so a fat-fingered env can't hammer the DB or effectively disable the net. */
|
|
44
|
+
export declare const MIN_CHAT_POLL_INTERVAL_MS = 2000;
|
|
45
|
+
export declare const MAX_CHAT_POLL_INTERVAL_MS = 300000;
|
|
46
|
+
/** The queued-turn status the fallback polls for (the only one the pump can act on). */
|
|
47
|
+
export declare const CHAT_POLL_QUEUED_STATUS: "queued";
|
|
48
|
+
/** Is the DB-poll fallback enabled? Disabled by `ACC_RUNNER_CHAT_POLL=0`. */
|
|
49
|
+
export declare function chatPollEnabled(env?: NodeJS.ProcessEnv): boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Resolve the poll cadence (ms). Blank/absent/non-numeric ⇒ default; a numeric
|
|
52
|
+
* value is clamped to [MIN, MAX] so a typo can neither DDoS the DB nor make the
|
|
53
|
+
* fallback effectively never fire.
|
|
54
|
+
*/
|
|
55
|
+
export declare function resolveChatPollIntervalMs(raw: string | undefined | null): number;
|
|
56
|
+
/**
|
|
57
|
+
* Minimal structural shape of the Supabase client the poll query needs — the
|
|
58
|
+
* PostgREST query-builder chain used by companion.ts's backfill. A real
|
|
59
|
+
* RunnerSupabaseClient satisfies it; tests inject a recording fake.
|
|
60
|
+
*/
|
|
61
|
+
export interface ChatPollClient {
|
|
62
|
+
from(table: string): any;
|
|
63
|
+
}
|
|
64
|
+
export interface PollQueuedConversationsOpts {
|
|
65
|
+
/** Best-effort log sink. */
|
|
66
|
+
onLog?: (line: string) => void;
|
|
67
|
+
/** Override the queued-turn status filter (defaults to CHAT_POLL_QUEUED_STATUS). */
|
|
68
|
+
status?: string;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Query the DB for the conversations OWNED BY the bound user that currently have
|
|
72
|
+
* a `queued` chat turn — the Realtime-independent discovery read at the heart of
|
|
73
|
+
* the fallback.
|
|
74
|
+
*
|
|
75
|
+
* Owner-affinity: acc.chat_turns' SELECT policy is ORG-scoped (0254), so a
|
|
76
|
+
* candidate conversation_id may belong to a fellow org member. We re-confirm
|
|
77
|
+
* ownership against acc.chat_conversations (filtered to the bound user) exactly
|
|
78
|
+
* as backfillActiveConversations does, so a companion never surfaces — and never
|
|
79
|
+
* claims — another operator's conversation. Best-effort: any query error is
|
|
80
|
+
* logged and yields an empty result (the fallback is a safety net; it must never
|
|
81
|
+
* throw into the poll loop).
|
|
82
|
+
*/
|
|
83
|
+
export declare function pollQueuedConversations(client: ChatPollClient, boundUserId: string, opts?: PollQueuedConversationsOpts): Promise<string[]>;
|
|
84
|
+
export interface ChatTurnPollerDeps {
|
|
85
|
+
/**
|
|
86
|
+
* Discover the conversation ids that currently have queued work for the bound
|
|
87
|
+
* identity — Realtime-independent. Typically
|
|
88
|
+
* `() => pollQueuedConversations(client, boundUserId, { onLog })`.
|
|
89
|
+
*/
|
|
90
|
+
poll: () => Promise<string[]>;
|
|
91
|
+
/**
|
|
92
|
+
* Act on the discovered conversations: fold them into the served set and nudge
|
|
93
|
+
* the claim pump so their queued turns drain this interval. Best-effort — a
|
|
94
|
+
* throw is caught and logged; the next tick retries. Omit ⇒ discovery-only
|
|
95
|
+
* (the poller just reports what it found, for observability/tests).
|
|
96
|
+
*/
|
|
97
|
+
onDue?: (conversationIds: string[]) => Promise<void> | void;
|
|
98
|
+
/** Poll cadence (ms). Default DEFAULT_CHAT_POLL_INTERVAL_MS; clamped to a sane floor. */
|
|
99
|
+
intervalMs?: number;
|
|
100
|
+
/** Best-effort log sink. */
|
|
101
|
+
onLog?: (line: string) => void;
|
|
102
|
+
}
|
|
103
|
+
/** The result of one poll pass, for observability + tests. */
|
|
104
|
+
export interface ChatPollResult {
|
|
105
|
+
/** Conversation ids the DB reported as carrying queued work this pass. */
|
|
106
|
+
due: string[];
|
|
107
|
+
/** True when the onDue sink ran without throwing (or was absent). */
|
|
108
|
+
handled: boolean;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* The DB-poll fallback loop. `pollOnce()` runs exactly one Realtime-independent
|
|
112
|
+
* discovery pass (query → onDue); `start()` schedules it on the heartbeat
|
|
113
|
+
* interval; `stop()` halts the loop. Single-flight: a slow pass can never stack
|
|
114
|
+
* on the next tick — an overlapping tick is coalesced. The whole loop is
|
|
115
|
+
* best-effort: a query or sink throw is caught + logged and the loop rides on,
|
|
116
|
+
* so the fallback can never crash the companion process.
|
|
117
|
+
*/
|
|
118
|
+
export declare class ChatTurnPoller {
|
|
119
|
+
private readonly deps;
|
|
120
|
+
private timer;
|
|
121
|
+
private stopped;
|
|
122
|
+
private polling;
|
|
123
|
+
private readonly intervalMs;
|
|
124
|
+
constructor(deps: ChatTurnPollerDeps);
|
|
125
|
+
/** Run one discovery pass. Best-effort + single-flight; never throws. */
|
|
126
|
+
pollOnce(): Promise<ChatPollResult>;
|
|
127
|
+
/** Start the heartbeat poll loop (does not block; timer is unref'd). */
|
|
128
|
+
start(): void;
|
|
129
|
+
/** Stop the loop. Idempotent. */
|
|
130
|
+
stop(): void;
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/chat-poll/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,mEAAmE;AACnE,eAAO,MAAM,qBAAqB,yBAAyB,CAAC;AAC5D,2DAA2D;AAC3D,eAAO,MAAM,yBAAyB,4BAA4B,CAAC;AACnE;;;;;GAKG;AACH,eAAO,MAAM,6BAA6B,QAAS,CAAC;AACpD,6FAA6F;AAC7F,eAAO,MAAM,yBAAyB,OAAQ,CAAC;AAC/C,eAAO,MAAM,yBAAyB,SAAU,CAAC;AAEjD,wFAAwF;AACxF,eAAO,MAAM,uBAAuB,EAAG,QAAiB,CAAC;AAQzD,6EAA6E;AAC7E,wBAAgB,eAAe,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAE7E;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAIhF;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAE7B,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,CAAC;CAC1B;AAED,MAAM,WAAW,2BAA2B;IAC1C,4BAA4B;IAC5B,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,oFAAoF;IACpF,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,cAAc,EACtB,WAAW,EAAE,MAAM,EACnB,IAAI,GAAE,2BAAgC,GACrC,OAAO,CAAC,MAAM,EAAE,CAAC,CAoDnB;AAED,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,IAAI,EAAE,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC9B;;;;;OAKG;IACH,KAAK,CAAC,EAAE,CAAC,eAAe,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC5D,yFAAyF;IACzF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4BAA4B;IAC5B,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAChC;AAED,8DAA8D;AAC9D,MAAM,WAAW,cAAc;IAC7B,0EAA0E;IAC1E,GAAG,EAAE,MAAM,EAAE,CAAC;IACd,qEAAqE;IACrE,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;GAOG;AACH,qBAAa,cAAc;IAMb,OAAO,CAAC,QAAQ,CAAC,IAAI;IALjC,OAAO,CAAC,KAAK,CAA+C;IAC5D,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;gBAEP,IAAI,EAAE,kBAAkB;IAOrD,yEAAyE;IACnE,QAAQ,IAAI,OAAO,CAAC,cAAc,CAAC;IAmCzC,wEAAwE;IACxE,KAAK,IAAI,IAAI;IAWb,iCAAiC;IACjC,IAAI,IAAI,IAAI;CAOb"}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CHAT-HYGIENE AC2 — the companion/fleet DB-POLL FALLBACK
|
|
3
|
+
* for chat turn discovery.
|
|
4
|
+
*
|
|
5
|
+
* WHY THIS EXISTS. A companion (chat-companion.ts) / fleet lane
|
|
6
|
+
* (watch-chat/fleet-chat-lane.ts) discovers which conversations to serve from
|
|
7
|
+
* TWO Realtime-backed signals: the browser's `conversation_active` announce on
|
|
8
|
+
* the `identity:<user>` topic (companion.ts createConversationListener, FU-4)
|
|
9
|
+
* and the `chat:<conversation_id>` turn broadcasts. Both ride Supabase Realtime,
|
|
10
|
+
* which is BEST-EFFORT (no ack, no retry). During a Realtime DELIVERY BLACKOUT —
|
|
11
|
+
* a dropped socket, a Realtime outage, a missed announce — a freshly queued turn
|
|
12
|
+
* is never surfaced, so the companion never claims it and the operator's chat
|
|
13
|
+
* hangs with no reply (the 2026-07-21/22 incident shape).
|
|
14
|
+
*
|
|
15
|
+
* The claim PUMP already runs on its own interval, but it only claims across the
|
|
16
|
+
* conversations already in the served set — a conversation the blackout hid from
|
|
17
|
+
* discovery is never pumped. This module closes that gap with a discovery path
|
|
18
|
+
* that does NOT depend on Realtime at all: a heartbeat poller that queries the DB
|
|
19
|
+
* directly for the bound identity's OWNED conversations carrying a `queued` turn,
|
|
20
|
+
* and feeds them to a sink (in production: fold into the owner-affinity source +
|
|
21
|
+
* nudge the pump). It is the DB twin of the Realtime discovery signals — the
|
|
22
|
+
* safety net that guarantees a queued turn is served within one poll interval
|
|
23
|
+
* even with Realtime entirely dead.
|
|
24
|
+
*
|
|
25
|
+
* Pure primitive: every I/O seam (the DB query, the on-due sink, the clock) is
|
|
26
|
+
* injected, so the whole fallback is exercised deterministically
|
|
27
|
+
* (tests/unit/chat-hygiene, tests/e2e/chat-hygiene) with no network and no
|
|
28
|
+
* Supabase Realtime. Wiring it into `acc-runner companion` / `watch` is a
|
|
29
|
+
* follow-up (mirrors the FLEET-SERVE watch-chat primitives-then-wire split); the
|
|
30
|
+
* primitive + chaos proof land here.
|
|
31
|
+
*/
|
|
32
|
+
/** Env var that opts OUT of the DB-poll fallback (default: on). */
|
|
33
|
+
export const CHAT_POLL_ENABLED_ENV = "ACC_RUNNER_CHAT_POLL";
|
|
34
|
+
/** Env var carrying the DB-poll heartbeat cadence (ms). */
|
|
35
|
+
export const CHAT_POLL_INTERVAL_MS_ENV = "ACC_RUNNER_CHAT_POLL_MS";
|
|
36
|
+
/**
|
|
37
|
+
* Default poll cadence (ms). Sits in the same ~15-30s band as the companion's
|
|
38
|
+
* FU-10 rescan backstop: short enough that a Realtime-lost queued turn is
|
|
39
|
+
* served within a barely-perceptible delay, long enough that the bounded
|
|
40
|
+
* owner-affinity scan stays trivially cheap.
|
|
41
|
+
*/
|
|
42
|
+
export const DEFAULT_CHAT_POLL_INTERVAL_MS = 15_000;
|
|
43
|
+
/** Clamp bounds so a fat-fingered env can't hammer the DB or effectively disable the net. */
|
|
44
|
+
export const MIN_CHAT_POLL_INTERVAL_MS = 2_000;
|
|
45
|
+
export const MAX_CHAT_POLL_INTERVAL_MS = 300_000;
|
|
46
|
+
/** The queued-turn status the fallback polls for (the only one the pump can act on). */
|
|
47
|
+
export const CHAT_POLL_QUEUED_STATUS = "queued";
|
|
48
|
+
/** True when the value is a recognised falsy flag ("0"/"false"/"no"/"off"). */
|
|
49
|
+
function isFalsyFlag(raw) {
|
|
50
|
+
const v = (raw ?? "").trim().toLowerCase();
|
|
51
|
+
return v === "0" || v === "false" || v === "no" || v === "off";
|
|
52
|
+
}
|
|
53
|
+
/** Is the DB-poll fallback enabled? Disabled by `ACC_RUNNER_CHAT_POLL=0`. */
|
|
54
|
+
export function chatPollEnabled(env = process.env) {
|
|
55
|
+
return !isFalsyFlag(env[CHAT_POLL_ENABLED_ENV]);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Resolve the poll cadence (ms). Blank/absent/non-numeric ⇒ default; a numeric
|
|
59
|
+
* value is clamped to [MIN, MAX] so a typo can neither DDoS the DB nor make the
|
|
60
|
+
* fallback effectively never fire.
|
|
61
|
+
*/
|
|
62
|
+
export function resolveChatPollIntervalMs(raw) {
|
|
63
|
+
const n = Number.parseInt((raw ?? "").trim(), 10);
|
|
64
|
+
if (!Number.isFinite(n))
|
|
65
|
+
return DEFAULT_CHAT_POLL_INTERVAL_MS;
|
|
66
|
+
return Math.min(MAX_CHAT_POLL_INTERVAL_MS, Math.max(MIN_CHAT_POLL_INTERVAL_MS, n));
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Query the DB for the conversations OWNED BY the bound user that currently have
|
|
70
|
+
* a `queued` chat turn — the Realtime-independent discovery read at the heart of
|
|
71
|
+
* the fallback.
|
|
72
|
+
*
|
|
73
|
+
* Owner-affinity: acc.chat_turns' SELECT policy is ORG-scoped (0254), so a
|
|
74
|
+
* candidate conversation_id may belong to a fellow org member. We re-confirm
|
|
75
|
+
* ownership against acc.chat_conversations (filtered to the bound user) exactly
|
|
76
|
+
* as backfillActiveConversations does, so a companion never surfaces — and never
|
|
77
|
+
* claims — another operator's conversation. Best-effort: any query error is
|
|
78
|
+
* logged and yields an empty result (the fallback is a safety net; it must never
|
|
79
|
+
* throw into the poll loop).
|
|
80
|
+
*/
|
|
81
|
+
export async function pollQueuedConversations(client, boundUserId, opts = {}) {
|
|
82
|
+
const status = opts.status ?? CHAT_POLL_QUEUED_STATUS;
|
|
83
|
+
try {
|
|
84
|
+
const turnsRes = await client
|
|
85
|
+
.from("chat_turns")
|
|
86
|
+
.select("conversation_id")
|
|
87
|
+
.eq("status", status);
|
|
88
|
+
if (turnsRes?.error) {
|
|
89
|
+
opts.onLog?.(`[acc-runner] chat-poll: chat_turns scan failed: ${turnsRes.error.message}`);
|
|
90
|
+
return [];
|
|
91
|
+
}
|
|
92
|
+
const turnRows = Array.isArray(turnsRes?.data) ? turnsRes.data : [];
|
|
93
|
+
const candidateIds = [
|
|
94
|
+
...new Set(turnRows
|
|
95
|
+
.map((r) => {
|
|
96
|
+
const v = r?.conversation_id;
|
|
97
|
+
return typeof v === "string" ? v.trim() : "";
|
|
98
|
+
})
|
|
99
|
+
.filter((id) => id.length > 0)),
|
|
100
|
+
];
|
|
101
|
+
if (candidateIds.length === 0)
|
|
102
|
+
return [];
|
|
103
|
+
const ownedRes = await client
|
|
104
|
+
.from("chat_conversations")
|
|
105
|
+
.select("id")
|
|
106
|
+
.eq("user_id", boundUserId)
|
|
107
|
+
.in("id", candidateIds);
|
|
108
|
+
if (ownedRes?.error) {
|
|
109
|
+
opts.onLog?.(`[acc-runner] chat-poll: ownership check failed: ${ownedRes.error.message}`);
|
|
110
|
+
return [];
|
|
111
|
+
}
|
|
112
|
+
const ownedRows = Array.isArray(ownedRes?.data) ? ownedRes.data : [];
|
|
113
|
+
return [
|
|
114
|
+
...new Set(ownedRows
|
|
115
|
+
.map((r) => {
|
|
116
|
+
const v = r?.id;
|
|
117
|
+
return typeof v === "string" ? v.trim() : "";
|
|
118
|
+
})
|
|
119
|
+
.filter((id) => id.length > 0)),
|
|
120
|
+
];
|
|
121
|
+
}
|
|
122
|
+
catch (err) {
|
|
123
|
+
opts.onLog?.(`[acc-runner] chat-poll query failed: ${err.message}`);
|
|
124
|
+
return [];
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* The DB-poll fallback loop. `pollOnce()` runs exactly one Realtime-independent
|
|
129
|
+
* discovery pass (query → onDue); `start()` schedules it on the heartbeat
|
|
130
|
+
* interval; `stop()` halts the loop. Single-flight: a slow pass can never stack
|
|
131
|
+
* on the next tick — an overlapping tick is coalesced. The whole loop is
|
|
132
|
+
* best-effort: a query or sink throw is caught + logged and the loop rides on,
|
|
133
|
+
* so the fallback can never crash the companion process.
|
|
134
|
+
*/
|
|
135
|
+
export class ChatTurnPoller {
|
|
136
|
+
deps;
|
|
137
|
+
timer = null;
|
|
138
|
+
stopped = false;
|
|
139
|
+
polling = false;
|
|
140
|
+
intervalMs;
|
|
141
|
+
constructor(deps) {
|
|
142
|
+
this.deps = deps;
|
|
143
|
+
this.intervalMs = Math.max(MIN_CHAT_POLL_INTERVAL_MS, deps.intervalMs ?? DEFAULT_CHAT_POLL_INTERVAL_MS);
|
|
144
|
+
}
|
|
145
|
+
/** Run one discovery pass. Best-effort + single-flight; never throws. */
|
|
146
|
+
async pollOnce() {
|
|
147
|
+
if (this.stopped || this.polling)
|
|
148
|
+
return { due: [], handled: false };
|
|
149
|
+
this.polling = true;
|
|
150
|
+
try {
|
|
151
|
+
let due = [];
|
|
152
|
+
try {
|
|
153
|
+
due = await this.deps.poll();
|
|
154
|
+
}
|
|
155
|
+
catch (err) {
|
|
156
|
+
this.deps.onLog?.(`[acc-runner] chat-poll pass failed: ${err.message}`);
|
|
157
|
+
return { due: [], handled: false };
|
|
158
|
+
}
|
|
159
|
+
if (this.stopped)
|
|
160
|
+
return { due, handled: false };
|
|
161
|
+
if (due.length === 0 || !this.deps.onDue) {
|
|
162
|
+
return { due, handled: true };
|
|
163
|
+
}
|
|
164
|
+
try {
|
|
165
|
+
await this.deps.onDue(due);
|
|
166
|
+
this.deps.onLog?.(`[acc-runner] chat-poll fallback surfaced ${due.length} queued conversation(s) ` +
|
|
167
|
+
`(Realtime-independent)`);
|
|
168
|
+
return { due, handled: true };
|
|
169
|
+
}
|
|
170
|
+
catch (err) {
|
|
171
|
+
this.deps.onLog?.(`[acc-runner] chat-poll onDue sink failed: ${err.message}`);
|
|
172
|
+
return { due, handled: false };
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
finally {
|
|
176
|
+
this.polling = false;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/** Start the heartbeat poll loop (does not block; timer is unref'd). */
|
|
180
|
+
start() {
|
|
181
|
+
if (this.timer || this.stopped)
|
|
182
|
+
return;
|
|
183
|
+
this.timer = setInterval(() => {
|
|
184
|
+
void this.pollOnce();
|
|
185
|
+
}, this.intervalMs);
|
|
186
|
+
this.timer.unref?.();
|
|
187
|
+
// Kick one immediate pass so a blackout in progress at startup self-heals
|
|
188
|
+
// without waiting a full interval.
|
|
189
|
+
void this.pollOnce();
|
|
190
|
+
}
|
|
191
|
+
/** Stop the loop. Idempotent. */
|
|
192
|
+
stop() {
|
|
193
|
+
this.stopped = true;
|
|
194
|
+
if (this.timer) {
|
|
195
|
+
clearInterval(this.timer);
|
|
196
|
+
this.timer = null;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/chat-poll/index.ts"],"names":[],"mappings":"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"}
|
package/dist/doctor.d.ts
CHANGED
|
@@ -125,8 +125,9 @@ export interface DoctorOptions {
|
|
|
125
125
|
/**
|
|
126
126
|
* Treat advisory WARN outcomes as hard failures. Used by
|
|
127
127
|
* `scripts/provision-runner.sh` and CI so a runner only reports healthy when
|
|
128
|
-
* the environment is fully green —
|
|
129
|
-
* binary, no claude/version drift, no unconfirmable
|
|
128
|
+
* the environment is fully green — subscription/BYOK serving auth resolved
|
|
129
|
+
* (REVIEW-SUBS), no stale binary, no claude/version drift, no unconfirmable
|
|
130
|
+
* foreign watch process.
|
|
130
131
|
* Off by default so interactive `doctor` stays lenient about advisories.
|
|
131
132
|
*/
|
|
132
133
|
strict?: boolean;
|
package/dist/doctor.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../src/doctor.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../src/doctor.ts"],"names":[],"mappings":"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"}
|
package/dist/doctor.js
CHANGED
|
@@ -9,7 +9,7 @@ import path from "node:path";
|
|
|
9
9
|
import { execa } from "execa";
|
|
10
10
|
import chalk from "chalk";
|
|
11
11
|
import { buildEnv } from "./bin-resolve.js";
|
|
12
|
-
import {
|
|
12
|
+
import { authPrecedenceHint, servingAuthKind, describeServingAuthKind, anthropicApiKey, forwardsAmbientKey, } from "./provider-auth.js";
|
|
13
13
|
import { openaiApiKey, openaiApiKeyFromKeychain, describeCodexAuthMode, } from "./provider-auth.js";
|
|
14
14
|
import { codexEngineEnabled, resolveCodexSpawnMode } from "./engines/codex.js";
|
|
15
15
|
import { claudeCodeEngine } from "./engines/claude-code.js";
|
|
@@ -148,29 +148,50 @@ async function checkClaude() {
|
|
|
148
148
|
};
|
|
149
149
|
}
|
|
150
150
|
/**
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
151
|
+
* Report how spawned claude sessions will authenticate, under the
|
|
152
|
+
* subscription-only serving policy. NO ambient API-key billing: a host
|
|
153
|
+
* ANTHROPIC_API_KEY is forwarded to a spawn ONLY when the serving identity is
|
|
154
|
+
* BYOK `api_key`; otherwise it is stripped and the session serves on the
|
|
155
|
+
* operator's subscription OAuth login. So a MISSING key is now the healthy
|
|
156
|
+
* default, not a warning — the advice is `claude login`, never "export a key".
|
|
157
157
|
*/
|
|
158
158
|
async function checkAnthropicKey() {
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
159
|
+
const kind = servingAuthKind();
|
|
160
|
+
const keyPresent = !!anthropicApiKey();
|
|
161
|
+
const name = "Anthropic serving auth";
|
|
162
|
+
if (forwardsAmbientKey()) {
|
|
163
|
+
// BYOK api_key identity: the host key IS forwarded. Missing key = misconfig.
|
|
164
|
+
if (keyPresent) {
|
|
165
|
+
const hint = authPrecedenceHint();
|
|
166
|
+
return {
|
|
167
|
+
name,
|
|
168
|
+
ok: true,
|
|
169
|
+
detail: hint
|
|
170
|
+
? `${describeServingAuthKind(kind)} — ${hint}`
|
|
171
|
+
: describeServingAuthKind(kind),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
162
174
|
return {
|
|
163
|
-
name
|
|
164
|
-
ok:
|
|
165
|
-
|
|
175
|
+
name,
|
|
176
|
+
ok: false,
|
|
177
|
+
warn: true,
|
|
178
|
+
detail: `${describeServingAuthKind(kind)} but no ANTHROPIC_API_KEY present`,
|
|
179
|
+
fix: "this runner serves a BYOK api_key identity — ensure the v9 broker delivers ANTHROPIC_API_KEY, or switch the identity to subscription serving.",
|
|
166
180
|
};
|
|
167
181
|
}
|
|
182
|
+
// Subscription-first (the desired default). A missing key is correct; a
|
|
183
|
+
// present-but-stripped key is noted so the operator knows it is NOT billed.
|
|
168
184
|
return {
|
|
169
|
-
name
|
|
170
|
-
ok:
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
185
|
+
name,
|
|
186
|
+
ok: true,
|
|
187
|
+
detail: keyPresent
|
|
188
|
+
? `${describeServingAuthKind(kind)}; a host ANTHROPIC_API_KEY is present but will be STRIPPED from spawns (not billed)`
|
|
189
|
+
: describeServingAuthKind(kind),
|
|
190
|
+
...(keyPresent
|
|
191
|
+
? {
|
|
192
|
+
fix: "a stray ANTHROPIC_API_KEY is set but ignored under subscription-only serving; unset it to avoid confusion. Run `claude login` if the subscription session ever lapses.",
|
|
193
|
+
}
|
|
194
|
+
: {}),
|
|
174
195
|
};
|
|
175
196
|
}
|
|
176
197
|
/**
|
|
@@ -195,7 +216,7 @@ export async function checkChatAuth(probe = () => claudeCodeChatEngine.credentia
|
|
|
195
216
|
ok: false,
|
|
196
217
|
warn: true,
|
|
197
218
|
detail: `chat lane unavailable — ${result.detail}`,
|
|
198
|
-
fix: "
|
|
219
|
+
fix: "run an interactive `claude` login so the chat lane can serve turns on the operator's subscription (REVIEW-SUBS: ambient ANTHROPIC_API_KEY is no longer used).",
|
|
199
220
|
};
|
|
200
221
|
}
|
|
201
222
|
const draftOnly = result.tier === "draft-only";
|