jaitus 0.3.23 → 0.3.24

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.
@@ -0,0 +1,54 @@
1
+ // ─── "Update Jaitus to continue", even mid-task ─────────────────────────────────
2
+ //
3
+ // The admin can require a newer CLI (backend services/cliUpdate.js). Every request is
4
+ // checked there, but a CLI sitting idle — or deep in a long tool run — would not hear about
5
+ // it until its next request. So the CLI asks once at start and every two minutes while it
6
+ // is open. The question goes through the same gate: a CLI that is too old is refused with
7
+ // jaitus_update_required, and that refusal is the answer. When it comes, the running task
8
+ // is stopped and the reason is shown once, short.
9
+ //
10
+ // Any failure (offline, timeout, an older server) means "nothing to say": this never
11
+ // blocks or slows the CLI on its own.
12
+
13
+ import { endpoints, version } from './config.js';
14
+
15
+ const EVERY_MS = 2 * 60 * 1000;
16
+ const TIMEOUT_MS = 6000;
17
+
18
+ /** null when this CLI may keep working; otherwise { message } to show. */
19
+ export async function checkPolicy({ fetchImpl = fetch, baseUrl = endpoints().baseUrl } = {}) {
20
+ const ctrl = new AbortController();
21
+ const timer = setTimeout(() => ctrl.abort(), TIMEOUT_MS);
22
+ try {
23
+ const res = await fetchImpl(`${baseUrl}/api/public/cli-policy`, {
24
+ headers: { accept: 'application/json', 'user-agent': `jaitus-cli/${version()}`, 'x-jaitus-features': 'usage-window plan-expired update-required' },
25
+ signal: ctrl.signal,
26
+ });
27
+ if (res.status !== 426) return null;
28
+ const body = await res.json().catch(() => null);
29
+ if (body?.error?.code !== 'jaitus_update_required') return null;
30
+ return { message: String(body.error.message || '').replace(/[\x00-\x1f\x7f]/g, ' ').slice(0, 240) };
31
+ } catch {
32
+ return null;
33
+ } finally {
34
+ clearTimeout(timer);
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Check now and every two minutes; call onBlocked({ message }) once, the first time this
40
+ * CLI is refused. Returns a stop function. The timer never keeps the process alive.
41
+ */
42
+ export function watchPolicy(onBlocked, { everyMs = EVERY_MS, check = checkPolicy } = {}) {
43
+ let done = false;
44
+ let timer = null;
45
+ const run = async () => {
46
+ if (done) return;
47
+ const r = await check();
48
+ if (r && !done) { done = true; clearInterval(timer); onBlocked(r); }
49
+ };
50
+ void run();
51
+ timer = setInterval(() => { void run(); }, everyMs);
52
+ timer.unref?.();
53
+ return () => { done = true; clearInterval(timer); };
54
+ }