shadok-ai 0.15.13 → 0.15.15
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 +6 -2
- package/dist/lock.d.ts +39 -1
- package/dist/lock.js +68 -6
- package/dist/lock.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -529,7 +529,11 @@ On a normal (non-Docker) host this is a **no-op** — shadok never touches your
|
|
|
529
529
|
### Configuration
|
|
530
530
|
|
|
531
531
|
Config lives in `~/.shadok-ai/config.json` (mode 600) and is **authoritative
|
|
532
|
-
over the environment once set** from the GUI
|
|
532
|
+
over the environment once set** from the GUI — with two exceptions where the
|
|
533
|
+
environment wins: `SHADOK_GUI_PASSWORD` (a saved `--password` is used only when
|
|
534
|
+
that variable is unset, and nothing in the cockpit can change it — on Docker,
|
|
535
|
+
the password is the one the container was created with) and
|
|
536
|
+
`TELEGRAM_BOT_TOKEN`. The Telegram token, allowed
|
|
533
537
|
chats, and the bridge on/off switch are **per launch directory** — running the
|
|
534
538
|
server from another repo gives you a different cockpit and a different bot. So
|
|
535
539
|
are the channel list and the scheduled prompts.
|
|
@@ -542,7 +546,7 @@ machine, which silently shifts every daily prompt on a server running in UTC.
|
|
|
542
546
|
|---|---|
|
|
543
547
|
| `PORT` | HTTP/WS port |
|
|
544
548
|
| `SHADOK_HOST` | interface to bind (default `127.0.0.1`, this machine only) |
|
|
545
|
-
| `SHADOK_GUI_PASSWORD` | require a password for the GUI |
|
|
549
|
+
| `SHADOK_GUI_PASSWORD` | require a password for the GUI — overrides a password saved by `--password` |
|
|
546
550
|
| `SHADOK_ORIGINS` | extra browser origins allowed, comma-separated (reverse proxy) |
|
|
547
551
|
| `SHADOK_TMUX=0` | force the node-pty transport instead of tmux |
|
|
548
552
|
| `SHADOK_IDLE_MIN` | minutes with no client before a session is reclaimed (60) |
|
package/dist/lock.d.ts
CHANGED
|
@@ -18,6 +18,24 @@ export declare function instanceLockPath(cwd?: string): string;
|
|
|
18
18
|
* the left mis-parses every process whose name has one.
|
|
19
19
|
*/
|
|
20
20
|
export declare function stateFromProcStat(contents: string): string | null;
|
|
21
|
+
/**
|
|
22
|
+
* Pure: the process's start time from a `/proc/<pid>/stat` line — field 22,
|
|
23
|
+
* ticks since the machine booted — or null when the line is unusable.
|
|
24
|
+
*
|
|
25
|
+
* This is what tells a pid APART from the pid number. Parsed from the last `)`
|
|
26
|
+
* for the same reason as `stateFromProcStat`: the parenthesised command name is
|
|
27
|
+
* arbitrary. The fields after it start at `state` (field 3), so field 22 sits at
|
|
28
|
+
* index 19.
|
|
29
|
+
*/
|
|
30
|
+
export declare function startTimeFromProcStat(contents: string): string | null;
|
|
31
|
+
/** What a holder wrote: its pid, and its start time when the version that wrote
|
|
32
|
+
* it recorded one (older locks hold a bare number). Pure. */
|
|
33
|
+
export declare function parseLockFile(contents: string): {
|
|
34
|
+
pid: number;
|
|
35
|
+
start: string | null;
|
|
36
|
+
};
|
|
37
|
+
/** The line we write. Pure. Legacy-readable: it still STARTS with the pid. */
|
|
38
|
+
export declare function formatLockFile(pid: number, start: string | null): string;
|
|
21
39
|
/** How `pidAlive` looks a process up; injected in tests. */
|
|
22
40
|
export type ProcStatReader = (pid: number) => string | null;
|
|
23
41
|
/**
|
|
@@ -31,10 +49,30 @@ export type ProcStatReader = (pid: number) => string | null;
|
|
|
31
49
|
* absent the signal's answer stands.
|
|
32
50
|
*/
|
|
33
51
|
export declare function pidAlive(pid: number, procStat?: ProcStatReader): boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Is the process that TOOK this lock still there?
|
|
54
|
+
*
|
|
55
|
+
* Not the same question as "is some process wearing that pid", and in a
|
|
56
|
+
* container they come apart: pid numbers restart from 1 on every container
|
|
57
|
+
* restart, and the server itself lands around 15 — so the number the previous
|
|
58
|
+
* run wrote is, next boot, almost certainly worn by an unrelated process (one
|
|
59
|
+
* of the supervisor's own children). That false positive made the cockpit
|
|
60
|
+
* refuse to start, naming a pid that had nothing to do with it, and exit 0 —
|
|
61
|
+
* so the container stayed down looking like a clean stop. Observed in
|
|
62
|
+
* production: `another instance is already running from this directory
|
|
63
|
+
* (pid 14) — exiting`, where 14 was a node the supervisor had just spawned.
|
|
64
|
+
*
|
|
65
|
+
* The start time settles it: a reused pid would have to have been born on the
|
|
66
|
+
* same clock tick as the holder. An older lock carries no start time, so there
|
|
67
|
+
* the pid is all we have and the previous behaviour stands — a stale one is
|
|
68
|
+
* cleared by the next successful acquire, which writes both.
|
|
69
|
+
*/
|
|
70
|
+
export declare function holderStillThere(pid: number, start: string | null, procStat?: ProcStatReader): boolean;
|
|
34
71
|
/**
|
|
35
72
|
* Try to become the single instance for this launch dir. Returns { ok: true }
|
|
36
73
|
* when the lock is held, or { ok: false, pid } when a LIVE instance already
|
|
37
|
-
* holds it. A stale lock
|
|
74
|
+
* holds it. A stale lock — a dead holder, or a pid the holder no longer is — is
|
|
75
|
+
* reclaimed.
|
|
38
76
|
*/
|
|
39
77
|
export declare function acquireInstanceLock(lockPath?: string, procStat?: ProcStatReader): {
|
|
40
78
|
ok: true;
|
package/dist/lock.js
CHANGED
|
@@ -30,6 +30,32 @@ export function stateFromProcStat(contents) {
|
|
|
30
30
|
const [state] = contents.slice(close + 1).trim().split(/\s+/);
|
|
31
31
|
return state || null;
|
|
32
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Pure: the process's start time from a `/proc/<pid>/stat` line — field 22,
|
|
35
|
+
* ticks since the machine booted — or null when the line is unusable.
|
|
36
|
+
*
|
|
37
|
+
* This is what tells a pid APART from the pid number. Parsed from the last `)`
|
|
38
|
+
* for the same reason as `stateFromProcStat`: the parenthesised command name is
|
|
39
|
+
* arbitrary. The fields after it start at `state` (field 3), so field 22 sits at
|
|
40
|
+
* index 19.
|
|
41
|
+
*/
|
|
42
|
+
export function startTimeFromProcStat(contents) {
|
|
43
|
+
const close = contents.lastIndexOf(")");
|
|
44
|
+
if (close < 0)
|
|
45
|
+
return null;
|
|
46
|
+
const fields = contents.slice(close + 1).trim().split(/\s+/);
|
|
47
|
+
return fields[19] || null;
|
|
48
|
+
}
|
|
49
|
+
/** What a holder wrote: its pid, and its start time when the version that wrote
|
|
50
|
+
* it recorded one (older locks hold a bare number). Pure. */
|
|
51
|
+
export function parseLockFile(contents) {
|
|
52
|
+
const [pid, start] = contents.trim().split(/\s+/);
|
|
53
|
+
return { pid: parseInt(pid, 10) || 0, start: start || null };
|
|
54
|
+
}
|
|
55
|
+
/** The line we write. Pure. Legacy-readable: it still STARTS with the pid. */
|
|
56
|
+
export function formatLockFile(pid, start) {
|
|
57
|
+
return start ? `${pid} ${start}` : String(pid);
|
|
58
|
+
}
|
|
33
59
|
const readProcStat = (pid) => {
|
|
34
60
|
try {
|
|
35
61
|
return fs.readFileSync(`/proc/${pid}/stat`, "utf8");
|
|
@@ -60,29 +86,65 @@ export function pidAlive(pid, procStat = readProcStat) {
|
|
|
60
86
|
const stat = procStat(pid);
|
|
61
87
|
return stat === null ? true : stateFromProcStat(stat) !== "Z";
|
|
62
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* Is the process that TOOK this lock still there?
|
|
91
|
+
*
|
|
92
|
+
* Not the same question as "is some process wearing that pid", and in a
|
|
93
|
+
* container they come apart: pid numbers restart from 1 on every container
|
|
94
|
+
* restart, and the server itself lands around 15 — so the number the previous
|
|
95
|
+
* run wrote is, next boot, almost certainly worn by an unrelated process (one
|
|
96
|
+
* of the supervisor's own children). That false positive made the cockpit
|
|
97
|
+
* refuse to start, naming a pid that had nothing to do with it, and exit 0 —
|
|
98
|
+
* so the container stayed down looking like a clean stop. Observed in
|
|
99
|
+
* production: `another instance is already running from this directory
|
|
100
|
+
* (pid 14) — exiting`, where 14 was a node the supervisor had just spawned.
|
|
101
|
+
*
|
|
102
|
+
* The start time settles it: a reused pid would have to have been born on the
|
|
103
|
+
* same clock tick as the holder. An older lock carries no start time, so there
|
|
104
|
+
* the pid is all we have and the previous behaviour stands — a stale one is
|
|
105
|
+
* cleared by the next successful acquire, which writes both.
|
|
106
|
+
*/
|
|
107
|
+
export function holderStillThere(pid, start, procStat = readProcStat) {
|
|
108
|
+
if (!pidAlive(pid, procStat))
|
|
109
|
+
return false;
|
|
110
|
+
if (!start)
|
|
111
|
+
return true; // legacy lock: nothing to compare against
|
|
112
|
+
const stat = procStat(pid);
|
|
113
|
+
if (stat === null)
|
|
114
|
+
return true; // no /proc: the pid's answer is all there is
|
|
115
|
+
const live = startTimeFromProcStat(stat);
|
|
116
|
+
return live === null || live === start;
|
|
117
|
+
}
|
|
118
|
+
/** Our own start time, for the lock we are about to write (null off Linux). */
|
|
119
|
+
function ownStartTime(procStat) {
|
|
120
|
+
const stat = procStat(process.pid);
|
|
121
|
+
return stat === null ? null : startTimeFromProcStat(stat);
|
|
122
|
+
}
|
|
63
123
|
/**
|
|
64
124
|
* Try to become the single instance for this launch dir. Returns { ok: true }
|
|
65
125
|
* when the lock is held, or { ok: false, pid } when a LIVE instance already
|
|
66
|
-
* holds it. A stale lock
|
|
126
|
+
* holds it. A stale lock — a dead holder, or a pid the holder no longer is — is
|
|
127
|
+
* reclaimed.
|
|
67
128
|
*/
|
|
68
129
|
export function acquireInstanceLock(lockPath = instanceLockPath(), procStat = readProcStat) {
|
|
69
130
|
fs.mkdirSync(path.dirname(lockPath), { recursive: true });
|
|
70
131
|
for (let i = 0; i < 3; i++) {
|
|
71
132
|
try {
|
|
72
133
|
const fd = fs.openSync(lockPath, "wx"); // atomic create-if-absent
|
|
73
|
-
fs.writeSync(fd,
|
|
134
|
+
fs.writeSync(fd, formatLockFile(process.pid, ownStartTime(procStat)));
|
|
74
135
|
fs.closeSync(fd);
|
|
75
136
|
return { ok: true };
|
|
76
137
|
}
|
|
77
138
|
catch (e) {
|
|
78
139
|
if (e?.code !== "EEXIST")
|
|
79
140
|
throw e;
|
|
80
|
-
let
|
|
141
|
+
let held = { pid: 0, start: null };
|
|
81
142
|
try {
|
|
82
|
-
|
|
143
|
+
held = parseLockFile(fs.readFileSync(lockPath, "utf8"));
|
|
83
144
|
}
|
|
84
145
|
catch { /* unreadable */ }
|
|
85
|
-
|
|
146
|
+
const { pid, start } = held;
|
|
147
|
+
if (pid && pid !== process.pid && holderStillThere(pid, start, procStat))
|
|
86
148
|
return { ok: false, pid };
|
|
87
149
|
try {
|
|
88
150
|
fs.rmSync(lockPath);
|
|
@@ -95,7 +157,7 @@ export function acquireInstanceLock(lockPath = instanceLockPath(), procStat = re
|
|
|
95
157
|
/** Release our lock (best effort) — only if we still hold it. */
|
|
96
158
|
export function releaseInstanceLock(lockPath = instanceLockPath()) {
|
|
97
159
|
try {
|
|
98
|
-
const pid =
|
|
160
|
+
const { pid } = parseLockFile(fs.readFileSync(lockPath, "utf8"));
|
|
99
161
|
if (pid === process.pid)
|
|
100
162
|
fs.rmSync(lockPath);
|
|
101
163
|
}
|
package/dist/lock.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lock.js","sourceRoot":"","sources":["../src/lock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B;;;;;;;;GAQG;AAEH,iFAAiF;AACjF,MAAM,UAAU,gBAAgB,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;IAClD,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,eAAe,EAAE,GAAG,CAAC,CAAC;IAC9C,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,YAAY,EAAE,OAAO,EAAE,GAAG,GAAG,OAAO,CAAC,CAAC;AACvE,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IAChD,MAAM,KAAK,GAAG,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACxC,IAAI,KAAK,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAC3B,MAAM,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC9D,OAAO,KAAK,IAAI,IAAI,CAAC;AACvB,CAAC;AAKD,MAAM,YAAY,GAAmB,CAAC,GAAG,EAAE,EAAE;IAC3C,IAAI,CAAC;QACH,OAAO,EAAE,CAAC,YAAY,CAAC,SAAS,GAAG,OAAO,EAAE,MAAM,CAAC,CAAC;IACtD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC,CAAC,iDAAiD;IAChE,CAAC;AACH,CAAC,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,UAAU,QAAQ,CAAC,GAAW,EAAE,QAAQ,GAAmB,YAAY;IAC3E,IAAI,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACnC,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,2CAA2C;IACnE,CAAC;IAAC,OAAO,CAAM,EAAE,CAAC;QAChB,OAAO,CAAC,EAAE,IAAI,KAAK,OAAO,CAAC,CAAC,iDAAiD;IAC/E,CAAC;IACD,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC3B,OAAO,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,iBAAiB,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC;AAChE,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"lock.js","sourceRoot":"","sources":["../src/lock.ts"],"names":[],"mappings":"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"}
|