@songsid/agend 2.1.6-beta.1 → 2.1.6-beta.2
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/apply-job.d.ts +114 -0
- package/dist/apply-job.js +214 -0
- package/dist/apply-job.js.map +1 -0
- package/dist/backend/credential-profile.d.ts +114 -0
- package/dist/backend/credential-profile.js +242 -0
- package/dist/backend/credential-profile.js.map +1 -0
- package/dist/backend/kiro-auth-store.d.ts +25 -0
- package/dist/backend/kiro-auth-store.js +61 -0
- package/dist/backend/kiro-auth-store.js.map +1 -0
- package/dist/backend/kiro.d.ts +8 -0
- package/dist/backend/kiro.js +31 -2
- package/dist/backend/kiro.js.map +1 -1
- package/dist/cli.js +113 -0
- package/dist/cli.js.map +1 -1
- package/dist/config-validator.js +21 -0
- package/dist/config-validator.js.map +1 -1
- package/dist/daemon.d.ts +10 -0
- package/dist/daemon.js +27 -1
- package/dist/daemon.js.map +1 -1
- package/dist/fleet-level-config.d.ts +42 -0
- package/dist/fleet-level-config.js +78 -0
- package/dist/fleet-level-config.js.map +1 -0
- package/dist/fleet-lock.d.ts +21 -0
- package/dist/fleet-lock.js +42 -8
- package/dist/fleet-lock.js.map +1 -1
- package/dist/fleet-manager.d.ts +122 -1
- package/dist/fleet-manager.js +537 -83
- package/dist/fleet-manager.js.map +1 -1
- package/dist/general-knowledge/skills/credential-profiles/SKILL.md +121 -0
- package/dist/instance-config-impact.d.ts +55 -0
- package/dist/instance-config-impact.js +152 -0
- package/dist/instance-config-impact.js.map +1 -0
- package/dist/locale.js +2 -0
- package/dist/locale.js.map +1 -1
- package/dist/outbound-handlers.d.ts +5 -1
- package/dist/outbound-handlers.js +179 -1
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +6 -5
- package/dist/outbound-schemas.js +1 -0
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/provider-probe.d.ts +45 -0
- package/dist/provider-probe.js +111 -0
- package/dist/provider-probe.js.map +1 -0
- package/dist/quickstart-api.d.ts +161 -0
- package/dist/quickstart-api.js +340 -0
- package/dist/quickstart-api.js.map +1 -0
- package/dist/quickstart.js +23 -50
- package/dist/quickstart.js.map +1 -1
- package/dist/self-restart-limit.d.ts +21 -0
- package/dist/self-restart-limit.js +129 -0
- package/dist/self-restart-limit.js.map +1 -0
- package/dist/settings-api.d.ts +15 -0
- package/dist/settings-api.js +126 -1
- package/dist/settings-api.js.map +1 -1
- package/dist/setup-auth.d.ts +113 -0
- package/dist/setup-auth.js +181 -0
- package/dist/setup-auth.js.map +1 -0
- package/dist/setup-form.d.ts +18 -0
- package/dist/setup-form.js +315 -0
- package/dist/setup-form.js.map +1 -0
- package/dist/setup-host.d.ts +162 -0
- package/dist/setup-host.js +496 -0
- package/dist/setup-host.js.map +1 -0
- package/dist/setup-marker.d.ts +8 -0
- package/dist/setup-marker.js +39 -0
- package/dist/setup-marker.js.map +1 -0
- package/dist/setup-tunnel-consent.d.ts +53 -0
- package/dist/setup-tunnel-consent.js +82 -0
- package/dist/setup-tunnel-consent.js.map +1 -0
- package/dist/tips.js +1 -1
- package/dist/tips.js.map +1 -1
- package/dist/transcript-sources.d.ts +12 -1
- package/dist/transcript-sources.js +19 -3
- package/dist/transcript-sources.js.map +1 -1
- package/dist/tunnel/cloudflared.d.ts +71 -0
- package/dist/tunnel/cloudflared.js +447 -0
- package/dist/tunnel/cloudflared.js.map +1 -0
- package/dist/tunnel/lease.d.ts +75 -0
- package/dist/tunnel/lease.js +209 -0
- package/dist/tunnel/lease.js.map +1 -0
- package/dist/tunnel/manager.d.ts +56 -0
- package/dist/tunnel/manager.js +167 -0
- package/dist/tunnel/manager.js.map +1 -0
- package/dist/tunnel/types.d.ts +119 -0
- package/dist/tunnel/types.js +33 -0
- package/dist/tunnel/types.js.map +1 -0
- package/dist/ui/dashboard.html +4 -3
- package/dist/ui/settings.html +640 -153
- package/dist/usage/providers.d.ts +16 -13
- package/dist/usage/providers.js +79 -53
- package/dist/usage/providers.js.map +1 -1
- package/dist/web-api.js +6 -4
- package/dist/web-api.js.map +1 -1
- package/dist/web-auth.d.ts +75 -0
- package/dist/web-auth.js +208 -2
- package/dist/web-auth.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which fleet settings only a fresh process can adopt.
|
|
3
|
+
*
|
|
4
|
+
* The test is mechanical, not intuitive: **read once at construction and never
|
|
5
|
+
* re-read by `reconcileInstances`**. "Feels fleet-level" is not the test —
|
|
6
|
+
* `defaults.backend` feels fleet-level and is absorbed entirely by restarting
|
|
7
|
+
* the agents.
|
|
8
|
+
*
|
|
9
|
+
* Getting this set wrong in the *narrow* direction is worse than not narrowing
|
|
10
|
+
* at all. A startup-only key left out is saved to disk, produces no instance
|
|
11
|
+
* restart and no fleet row, and therefore never takes effect and never tells
|
|
12
|
+
* anyone — where an over-broad set only asks for a restart that was not needed.
|
|
13
|
+
* `tests/fleet-level-config.test.ts` asserts both sides of the line so a new
|
|
14
|
+
* key has to be classified deliberately.
|
|
15
|
+
*/
|
|
16
|
+
import type { FleetConfig } from "./types.js";
|
|
17
|
+
/**
|
|
18
|
+
* Startup-only, with the construction site that consumes each one.
|
|
19
|
+
*
|
|
20
|
+
* `timezone` is deliberately absent: `detectLocale()` reads only
|
|
21
|
+
* `defaults.locale` plus the host clock, and the configurable timezone lives
|
|
22
|
+
* inside `defaults.cost_guard`, which is covered whole.
|
|
23
|
+
*/
|
|
24
|
+
export declare const STARTUP_ONLY_FLEET_KEYS: readonly ["channel", "channels", "health_port", "defaults.locale", "defaults.cost_guard", "defaults.webhooks", "defaults.daily_summary", "defaults.scheduler.max_schedules", "defaults.scheduler.default_timezone"];
|
|
25
|
+
/**
|
|
26
|
+
* Verified to be read at the point of use, so they must NOT be in the set above.
|
|
27
|
+
*
|
|
28
|
+
* Listed rather than implied: the enumeration test asserts their absence, which
|
|
29
|
+
* is what stops the set from quietly growing back into "every cold default".
|
|
30
|
+
*/
|
|
31
|
+
export declare const RUNTIME_READ_FLEET_KEYS: readonly ["defaults.backend", "defaults.model", "defaults.tool_set", "defaults.agent_mode", "defaults.hang_detector", "defaults.startup.concurrency", "defaults.startup.stagger_delay_ms", "defaults.max_cross_instance_message_bytes", "defaults.tips", "defaults.progress_min_elapsed", "defaults.scheduler.retry_count", "defaults.scheduler.retry_interval_ms", "web", "hostname", "login", "web_terminal"];
|
|
32
|
+
/**
|
|
33
|
+
* The fleet-level configuration as one comparable string.
|
|
34
|
+
*
|
|
35
|
+
* Built by walking a fixed key list, so the output is stable regardless of the
|
|
36
|
+
* order keys appear in the YAML — two configs that differ only in key order
|
|
37
|
+
* must not look like a pending restart.
|
|
38
|
+
*/
|
|
39
|
+
export declare function fleetLevelSignature(config: FleetConfig | null): string;
|
|
40
|
+
/** The keys whose values differ between two configs — for the log line that
|
|
41
|
+
* explains why a restart is being asked for, or why two signatures disagree. */
|
|
42
|
+
export declare function fleetLevelDifferences(a: FleetConfig | null, b: FleetConfig | null): string[];
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Startup-only, with the construction site that consumes each one.
|
|
3
|
+
*
|
|
4
|
+
* `timezone` is deliberately absent: `detectLocale()` reads only
|
|
5
|
+
* `defaults.locale` plus the host clock, and the configurable timezone lives
|
|
6
|
+
* inside `defaults.cost_guard`, which is covered whole.
|
|
7
|
+
*/
|
|
8
|
+
export const STARTUP_ONLY_FLEET_KEYS = [
|
|
9
|
+
"channel", // createAdapter() at startup
|
|
10
|
+
"channels", // createAdapter() at startup
|
|
11
|
+
"health_port", // startHealthServer(), once
|
|
12
|
+
"defaults.locale", // setLocale(detectLocale(fleet)), once
|
|
13
|
+
"defaults.cost_guard", // new CostGuard(...)
|
|
14
|
+
"defaults.webhooks", // new WebhookEmitter(...)
|
|
15
|
+
"defaults.daily_summary", // new DailySummary(...)
|
|
16
|
+
"defaults.scheduler.max_schedules", // Scheduler ctor config
|
|
17
|
+
"defaults.scheduler.default_timezone", // Scheduler ctor config
|
|
18
|
+
];
|
|
19
|
+
/**
|
|
20
|
+
* Verified to be read at the point of use, so they must NOT be in the set above.
|
|
21
|
+
*
|
|
22
|
+
* Listed rather than implied: the enumeration test asserts their absence, which
|
|
23
|
+
* is what stops the set from quietly growing back into "every cold default".
|
|
24
|
+
*/
|
|
25
|
+
export const RUNTIME_READ_FLEET_KEYS = [
|
|
26
|
+
// Absorbed by restarting the agent.
|
|
27
|
+
"defaults.backend",
|
|
28
|
+
"defaults.model",
|
|
29
|
+
"defaults.tool_set",
|
|
30
|
+
"defaults.agent_mode",
|
|
31
|
+
"defaults.hang_detector",
|
|
32
|
+
// Read per spawn by the SpawnGate closures.
|
|
33
|
+
"defaults.startup.concurrency",
|
|
34
|
+
"defaults.startup.stagger_delay_ms",
|
|
35
|
+
// Read per message / per post / per tick.
|
|
36
|
+
"defaults.max_cross_instance_message_bytes",
|
|
37
|
+
"defaults.tips",
|
|
38
|
+
"defaults.progress_min_elapsed",
|
|
39
|
+
// Read on every schedule trigger, unlike the two Scheduler ctor keys.
|
|
40
|
+
"defaults.scheduler.retry_count",
|
|
41
|
+
"defaults.scheduler.retry_interval_ms",
|
|
42
|
+
// Read when the value is used; no construction captures them.
|
|
43
|
+
"web",
|
|
44
|
+
"hostname",
|
|
45
|
+
"login",
|
|
46
|
+
"web_terminal",
|
|
47
|
+
];
|
|
48
|
+
function pick(source, path) {
|
|
49
|
+
let value = source;
|
|
50
|
+
for (const segment of path.split(".")) {
|
|
51
|
+
if (value === null || typeof value !== "object")
|
|
52
|
+
return undefined;
|
|
53
|
+
value = value[segment];
|
|
54
|
+
}
|
|
55
|
+
return value;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The fleet-level configuration as one comparable string.
|
|
59
|
+
*
|
|
60
|
+
* Built by walking a fixed key list, so the output is stable regardless of the
|
|
61
|
+
* order keys appear in the YAML — two configs that differ only in key order
|
|
62
|
+
* must not look like a pending restart.
|
|
63
|
+
*/
|
|
64
|
+
export function fleetLevelSignature(config) {
|
|
65
|
+
const snapshot = {};
|
|
66
|
+
for (const path of STARTUP_ONLY_FLEET_KEYS) {
|
|
67
|
+
const value = pick(config, path);
|
|
68
|
+
if (value !== undefined)
|
|
69
|
+
snapshot[path] = value;
|
|
70
|
+
}
|
|
71
|
+
return JSON.stringify(snapshot);
|
|
72
|
+
}
|
|
73
|
+
/** The keys whose values differ between two configs — for the log line that
|
|
74
|
+
* explains why a restart is being asked for, or why two signatures disagree. */
|
|
75
|
+
export function fleetLevelDifferences(a, b) {
|
|
76
|
+
return STARTUP_ONLY_FLEET_KEYS.filter(path => JSON.stringify(pick(a, path) ?? null) !== JSON.stringify(pick(b, path) ?? null));
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=fleet-level-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fleet-level-config.js","sourceRoot":"","sources":["../src/fleet-level-config.ts"],"names":[],"mappings":"AAiBA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC,SAAS,EAA+B,6BAA6B;IACrE,UAAU,EAA8B,6BAA6B;IACrE,aAAa,EAA2B,4BAA4B;IACpE,iBAAiB,EAAuB,uCAAuC;IAC/E,qBAAqB,EAAmB,qBAAqB;IAC7D,mBAAmB,EAAqB,0BAA0B;IAClE,wBAAwB,EAAgB,wBAAwB;IAChE,kCAAkC,EAAM,wBAAwB;IAChE,qCAAqC,EAAG,wBAAwB;CACxD,CAAC;AAEX;;;;;GAKG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC,oCAAoC;IACpC,kBAAkB;IAClB,gBAAgB;IAChB,mBAAmB;IACnB,qBAAqB;IACrB,wBAAwB;IACxB,4CAA4C;IAC5C,8BAA8B;IAC9B,mCAAmC;IACnC,0CAA0C;IAC1C,2CAA2C;IAC3C,eAAe;IACf,+BAA+B;IAC/B,sEAAsE;IACtE,gCAAgC;IAChC,sCAAsC;IACtC,8DAA8D;IAC9D,KAAK;IACL,UAAU;IACV,OAAO;IACP,cAAc;CACN,CAAC;AAEX,SAAS,IAAI,CAAC,MAAe,EAAE,IAAY;IACzC,IAAI,KAAK,GAAY,MAAM,CAAC;IAC5B,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACtC,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAC;QAClE,KAAK,GAAI,KAAiC,CAAC,OAAO,CAAC,CAAC;IACtD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAA0B;IAC5D,MAAM,QAAQ,GAA4B,EAAE,CAAC;IAC7C,KAAK,MAAM,IAAI,IAAI,uBAAuB,EAAE,CAAC;QAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACjC,IAAI,KAAK,KAAK,SAAS;YAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC;IAClD,CAAC;IACD,OAAO,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;AAClC,CAAC;AAED;gFACgF;AAChF,MAAM,UAAU,qBAAqB,CAAC,CAAqB,EAAE,CAAqB;IAChF,OAAO,uBAAuB,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAC3C,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC;AACrF,CAAC"}
|
package/dist/fleet-lock.d.ts
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What kind of process holds the lock.
|
|
3
|
+
*
|
|
4
|
+
* A record written before this field existed has no role; it can only have been
|
|
5
|
+
* a fleet, so that is what a missing value means. Never widen that default —
|
|
6
|
+
* "unknown" must not become "safe to take from".
|
|
7
|
+
*/
|
|
8
|
+
export type FleetLockRole = "fleet" | "setup-host";
|
|
1
9
|
interface FleetLockRecord {
|
|
2
10
|
pid: number;
|
|
3
11
|
nonce: string;
|
|
4
12
|
createdAt: string;
|
|
13
|
+
role?: FleetLockRole;
|
|
5
14
|
}
|
|
6
15
|
export interface FleetLockHandle {
|
|
7
16
|
path: string;
|
|
@@ -11,11 +20,23 @@ export interface FleetLockHandle {
|
|
|
11
20
|
export interface FleetLockProbe {
|
|
12
21
|
pid?: number;
|
|
13
22
|
nonce?: string;
|
|
23
|
+
/** Defaults to "fleet"; the pre-fleet setup host claims "setup-host". */
|
|
24
|
+
role?: FleetLockRole;
|
|
14
25
|
isProcessAlive?: (pid: number) => boolean;
|
|
15
26
|
readCommandLine?: (pid: number) => string;
|
|
16
27
|
}
|
|
28
|
+
/** Whether a process command line identifies the AgEnD setup page. */
|
|
29
|
+
export declare function isSetupHostCommandLine(commandLine: string): boolean;
|
|
17
30
|
/** Whether a process command line identifies an AgEnD fleet process. */
|
|
18
31
|
export declare function isFleetStartCommandLine(commandLine: string): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* A process's command line, or "" when it cannot be read.
|
|
34
|
+
*
|
|
35
|
+
* Exported because the health-port takeover has to answer the same question
|
|
36
|
+
* before it sends a signal: an empty answer means "cannot confirm", and nothing
|
|
37
|
+
* destructive may be done on that.
|
|
38
|
+
*/
|
|
39
|
+
export declare function readProcessCommandLine(pid: number): string;
|
|
19
40
|
/**
|
|
20
41
|
* Atomically claim one fleet process per AGEND_HOME. O_EXCL is portable and,
|
|
21
42
|
* unlike fleet.pid, is acquired before FleetManager can mutate tmux or sockets.
|
package/dist/fleet-lock.js
CHANGED
|
@@ -2,6 +2,11 @@ import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "
|
|
|
2
2
|
import { execFileSync } from "node:child_process";
|
|
3
3
|
import { randomBytes } from "node:crypto";
|
|
4
4
|
import { join } from "node:path";
|
|
5
|
+
/** Whether a process command line identifies the AgEnD setup page. */
|
|
6
|
+
export function isSetupHostCommandLine(commandLine) {
|
|
7
|
+
const normalized = commandLine.replace(/\0/g, " ").replace(/\s+/g, " ").trim();
|
|
8
|
+
return /\b(?:agend|(?:cli|daemon-entry)\.(?:js|ts))\b.*\bsetup\b/i.test(normalized);
|
|
9
|
+
}
|
|
5
10
|
/** Whether a process command line identifies an AgEnD fleet process. */
|
|
6
11
|
export function isFleetStartCommandLine(commandLine) {
|
|
7
12
|
const normalized = commandLine.replace(/\0/g, " ").replace(/\s+/g, " ").trim();
|
|
@@ -16,7 +21,14 @@ function processAlive(pid) {
|
|
|
16
21
|
return false;
|
|
17
22
|
}
|
|
18
23
|
}
|
|
19
|
-
|
|
24
|
+
/**
|
|
25
|
+
* A process's command line, or "" when it cannot be read.
|
|
26
|
+
*
|
|
27
|
+
* Exported because the health-port takeover has to answer the same question
|
|
28
|
+
* before it sends a signal: an empty answer means "cannot confirm", and nothing
|
|
29
|
+
* destructive may be done on that.
|
|
30
|
+
*/
|
|
31
|
+
export function readProcessCommandLine(pid) {
|
|
20
32
|
try {
|
|
21
33
|
return readFileSync(`/proc/${pid}/cmdline`, "utf8").replace(/\0/g, " ").trim();
|
|
22
34
|
}
|
|
@@ -57,10 +69,11 @@ export function acquireFleetLock(dataDir, probe = {}) {
|
|
|
57
69
|
pid,
|
|
58
70
|
nonce: probe.nonce ?? randomBytes(16).toString("hex"),
|
|
59
71
|
createdAt: new Date().toISOString(),
|
|
72
|
+
role: probe.role ?? "fleet",
|
|
60
73
|
};
|
|
61
74
|
const serialized = JSON.stringify(record) + "\n";
|
|
62
75
|
const isAlive = probe.isProcessAlive ?? processAlive;
|
|
63
|
-
const readCommandLine = probe.readCommandLine ??
|
|
76
|
+
const readCommandLine = probe.readCommandLine ?? readProcessCommandLine;
|
|
64
77
|
for (let attempt = 0; attempt < 4; attempt++) {
|
|
65
78
|
try {
|
|
66
79
|
writeFileSync(lockPath, serialized, { encoding: "utf8", flag: "wx", mode: 0o600 });
|
|
@@ -81,14 +94,35 @@ export function acquireFleetLock(dataDir, probe = {}) {
|
|
|
81
94
|
const owner = parseRecord(observed);
|
|
82
95
|
if (owner && isAlive(owner.pid)) {
|
|
83
96
|
const commandLine = readCommandLine(owner.pid);
|
|
84
|
-
|
|
85
|
-
|
|
97
|
+
// A live setup host is an owner in its own right. Before the role existed
|
|
98
|
+
// its command line did not match the fleet pattern, so a starting fleet
|
|
99
|
+
// read the lock as stale and took it — and then collided with the host on
|
|
100
|
+
// the health port, where the takeover kills whatever fleet.pid names.
|
|
101
|
+
//
|
|
102
|
+
// Symmetric with the fleet case on purpose: the setup page is short-lived
|
|
103
|
+
// and often killed outright, so its pid is a prime candidate for reuse
|
|
104
|
+
// after a reboot. Positive evidence that the pid now belongs to something
|
|
105
|
+
// else makes the record stale; no evidence still refuses, because an
|
|
106
|
+
// unreadable owner must never be assumed absent.
|
|
107
|
+
if (owner.role === "setup-host") {
|
|
108
|
+
if (!commandLine) {
|
|
109
|
+
throw new Error(`Fleet lock is owned by live PID ${owner.pid}; refusing to replace it`);
|
|
110
|
+
}
|
|
111
|
+
if (isSetupHostCommandLine(commandLine)) {
|
|
112
|
+
throw new Error(`Setup is already running (PID ${owner.pid}, lock: ${lockPath})`);
|
|
113
|
+
}
|
|
114
|
+
// The pid was reused by an unrelated process: the lock is stale.
|
|
86
115
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
116
|
+
else {
|
|
117
|
+
if (isFleetStartCommandLine(commandLine)) {
|
|
118
|
+
throw new Error(`Fleet is already running (PID ${owner.pid}, lock: ${lockPath})`);
|
|
119
|
+
}
|
|
120
|
+
// An alive PID with an unreadable command line is not safe to steal from.
|
|
121
|
+
if (!commandLine) {
|
|
122
|
+
throw new Error(`Fleet lock is owned by live PID ${owner.pid}; refusing to replace it`);
|
|
123
|
+
}
|
|
124
|
+
// The PID was reused by an unrelated process: the lock is stale.
|
|
90
125
|
}
|
|
91
|
-
// The PID was reused by an unrelated process: the lock is stale.
|
|
92
126
|
}
|
|
93
127
|
// Only unlink the exact stale record we inspected. A competing starter may
|
|
94
128
|
// already have replaced it; in that case leave its lock untouched and retry.
|
package/dist/fleet-lock.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fleet-lock.js","sourceRoot":"","sources":["../src/fleet-lock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACzF,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"fleet-lock.js","sourceRoot":"","sources":["../src/fleet-lock.ts"],"names":[],"mappings":"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"}
|
package/dist/fleet-manager.d.ts
CHANGED
|
@@ -21,6 +21,7 @@ import { type AgentEndpointContext } from "./agent-endpoint.js";
|
|
|
21
21
|
import { ClassicChannelManager } from "./classic-channel-manager.js";
|
|
22
22
|
import { type ExplicitInstanceRemoval } from "./instance-removal.js";
|
|
23
23
|
import type { InstanceState } from "./backend/types.js";
|
|
24
|
+
import { ApplyJobStore, type ApplyJob, type ApplyTargetKind, type SelfRestartResult } from "./apply-job.js";
|
|
24
25
|
import { StormWindow } from "./storm-window.js";
|
|
25
26
|
import { SpawnGate } from "./spawn-gate.js";
|
|
26
27
|
import { BackendOutageTracker } from "./backend-outage.js";
|
|
@@ -262,7 +263,29 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
262
263
|
private mirrorBuffer;
|
|
263
264
|
private mirrorTimer;
|
|
264
265
|
private sseClients;
|
|
265
|
-
|
|
266
|
+
/**
|
|
267
|
+
* Read from disk on every access rather than cached at startup: `agend
|
|
268
|
+
* web-token rotate` runs in a separate process, and a cached copy would keep
|
|
269
|
+
* authorizing revoked links and cookies until the fleet restarted.
|
|
270
|
+
*/
|
|
271
|
+
private get webToken();
|
|
272
|
+
/**
|
|
273
|
+
* Set while a Settings apply job is driving the reconcile. The reconcile
|
|
274
|
+
* stays the single doer; it just says out loud what it is doing to whom, so
|
|
275
|
+
* the job's rows are the work rather than a prediction of it.
|
|
276
|
+
*/
|
|
277
|
+
private applyJobStoreCache;
|
|
278
|
+
/** The apply that currently owns the reconcile slot, reserved synchronously
|
|
279
|
+
* so a second request cannot slip in before the first one starts working. */
|
|
280
|
+
private activeApplyJobId;
|
|
281
|
+
/** The fleet-level signature this process actually came up on. */
|
|
282
|
+
private appliedFleetLevel;
|
|
283
|
+
/** The config behind that signature, kept so a "needs restart" log can name
|
|
284
|
+
* which keys moved rather than just asserting that something did. */
|
|
285
|
+
private startupFleetConfig;
|
|
286
|
+
/** Set when the file on disk and the in-memory config disagree on a
|
|
287
|
+
* startup-only key at startup. See checkStartupSignatureConsistency(). */
|
|
288
|
+
private fleetSignatureMismatch;
|
|
266
289
|
private viewToken;
|
|
267
290
|
private healthServerListening;
|
|
268
291
|
constructor(dataDir: string);
|
|
@@ -273,6 +296,18 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
273
296
|
stormSuppressed(kind: string): boolean;
|
|
274
297
|
private handleSighup;
|
|
275
298
|
private scheduleReconcile;
|
|
299
|
+
/**
|
|
300
|
+
* Take the reconcile slot, or refuse.
|
|
301
|
+
*
|
|
302
|
+
* Only one reconcile may touch lifecycle and config at a time — two of them
|
|
303
|
+
* stop and start the same instance in parallel. SIGHUP and a Settings apply
|
|
304
|
+
* are the same operation from two entrances, so they share the one slot: the
|
|
305
|
+
* signal coalesces into a pending replay, the apply is told the fleet is busy.
|
|
306
|
+
*
|
|
307
|
+
* The returned promise is the caller's to handle; the stored one is already
|
|
308
|
+
* handled, so a rejection never escapes as an unhandled rejection.
|
|
309
|
+
*/
|
|
310
|
+
private startExclusiveReconcile;
|
|
276
311
|
/**
|
|
277
312
|
* Is the fleet going down (or coming back up) on purpose?
|
|
278
313
|
*
|
|
@@ -398,6 +433,9 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
398
433
|
* Classic channels use their effective backend merge chain, not the raw row.
|
|
399
434
|
*/
|
|
400
435
|
getActiveUsageProviderIds(): ReadonlySet<string>;
|
|
436
|
+
/** `[instance, effective backend, credential profile]` for everything that is
|
|
437
|
+
* running or paused — the one place both usage views agree on who is live. */
|
|
438
|
+
private activeBackendBindings;
|
|
401
439
|
/** Effective backends with a running or persisted-paused fleet/Classic instance. */
|
|
402
440
|
getActiveBackendIds(): ReadonlySet<string>;
|
|
403
441
|
isClassicInstance(name: string): boolean;
|
|
@@ -1584,6 +1622,89 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
1584
1622
|
* fields, plus cold fleet-level settings, retain restart semantics.
|
|
1585
1623
|
*/
|
|
1586
1624
|
private reconcileInstances;
|
|
1625
|
+
/**
|
|
1626
|
+
* `{channel, cold defaults}` as one comparable string — the part of the config
|
|
1627
|
+
* a running fleet process cannot adopt without restarting.
|
|
1628
|
+
*/
|
|
1629
|
+
private fleetLevelSignature;
|
|
1630
|
+
/**
|
|
1631
|
+
* The config a reconcile is about to load, not the one held in memory.
|
|
1632
|
+
*
|
|
1633
|
+
* Settings mutates the in-memory object and writes the file; only the file
|
|
1634
|
+
* goes back through defaults expansion. Forecasting from memory therefore
|
|
1635
|
+
* misses every instance that a changed fleet default will restart — the user
|
|
1636
|
+
* is told "restart AgEnD" and not told that five agents are about to go down.
|
|
1637
|
+
*/
|
|
1638
|
+
private nextFleetConfig;
|
|
1639
|
+
/**
|
|
1640
|
+
* Does the config this process is running match the one a reconcile would
|
|
1641
|
+
* load off disk?
|
|
1642
|
+
*
|
|
1643
|
+
* If not, every apply reports a fleet-level change that a restart cannot
|
|
1644
|
+
* clear — restart, recompute, disagree again — a self-sustaining loop that
|
|
1645
|
+
* the rate limit can only slow to three naggings an hour. Startup rewrites
|
|
1646
|
+
* the file in three places before this point (slimFleetConfigAtStartup, the
|
|
1647
|
+
* general auto-create, the general fixup), so the two can genuinely diverge.
|
|
1648
|
+
*
|
|
1649
|
+
* An offer to restart that cannot possibly succeed is worse than no offer, so
|
|
1650
|
+
* the panel shows the mismatch instead of a button.
|
|
1651
|
+
*/
|
|
1652
|
+
private checkStartupSignatureConsistency;
|
|
1653
|
+
/**
|
|
1654
|
+
* Is a live adapter already long-polling this bot token?
|
|
1655
|
+
*
|
|
1656
|
+
* Telegram's `getUpdates` has exactly one consumer: a second poller takes
|
|
1657
|
+
* turns with the first and both miss messages. The setup wizard's "post in
|
|
1658
|
+
* the group and I'll detect it" step is a second poller, so it has to know
|
|
1659
|
+
* when the answer is "not against this token, not while I'm running".
|
|
1660
|
+
*/
|
|
1661
|
+
isBotTokenInUse(token: string): boolean;
|
|
1662
|
+
/** Non-null when startup found the running config and fleet.yaml disagreeing. */
|
|
1663
|
+
fleetSignatureMismatchKeys(): string[] | null;
|
|
1664
|
+
/**
|
|
1665
|
+
* Restart AgEnD itself on behalf of a Settings apply.
|
|
1666
|
+
*
|
|
1667
|
+
* Deliberately a separate action from Apply: the panel is reachable from
|
|
1668
|
+
* outside the LAN, and "restart the whole fleet" must never be something a
|
|
1669
|
+
* single Apply click can carry along with it.
|
|
1670
|
+
*
|
|
1671
|
+
* The order below is the safety envelope, and the order matters:
|
|
1672
|
+
* concurrency and consistency first (cheap, and a restart during a reconcile
|
|
1673
|
+
* is the dangerous one), then the state checks, then the rate limit, then the
|
|
1674
|
+
* audit notice — and only once all of that holds is the attempt written to
|
|
1675
|
+
* disk and fsynced, before anything spawns.
|
|
1676
|
+
*/
|
|
1677
|
+
requestSettingsSelfRestart(jobId: string, key: string): Promise<SelfRestartResult>;
|
|
1678
|
+
/** The audit notice, and the message the restart progress will edit. */
|
|
1679
|
+
private postSelfRestartNotice;
|
|
1680
|
+
/** Jobs outlive this process on purpose; see apply-job.ts. */
|
|
1681
|
+
get applyJobs(): ApplyJobStore;
|
|
1682
|
+
/**
|
|
1683
|
+
* What a reconcile is about to do, per target.
|
|
1684
|
+
*
|
|
1685
|
+
* A forecast, not the record: it is built from the same `classifyInstanceChange`
|
|
1686
|
+
* the reconcile decides with, but the rows that end up in the job are the ones
|
|
1687
|
+
* the reconcile reports as it works. A target the forecast missed (a Classic
|
|
1688
|
+
* instance inheriting a changed default) is added when it is first touched.
|
|
1689
|
+
*/
|
|
1690
|
+
planConfigApply(): Array<{
|
|
1691
|
+
target: string;
|
|
1692
|
+
kind: ApplyTargetKind;
|
|
1693
|
+
}>;
|
|
1694
|
+
/**
|
|
1695
|
+
* Start (or re-join) a Settings apply.
|
|
1696
|
+
*
|
|
1697
|
+
* The key is the client's. Handing back the existing job for a repeated key is
|
|
1698
|
+
* the whole point: the retry after a lost response must not apply everything a
|
|
1699
|
+
* second time.
|
|
1700
|
+
*/
|
|
1701
|
+
startSettingsApply(key: string): {
|
|
1702
|
+
job: ApplyJob;
|
|
1703
|
+
reused: boolean;
|
|
1704
|
+
} | {
|
|
1705
|
+
busy: ApplyJob | null;
|
|
1706
|
+
};
|
|
1707
|
+
private runSettingsApply;
|
|
1587
1708
|
restartInstances(): Promise<void>;
|
|
1588
1709
|
private checkForUpdates;
|
|
1589
1710
|
/**
|