@songsid/agend 2.1.5 → 2.1.6-beta.10

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.
Files changed (184) hide show
  1. package/dist/agent-endpoint.d.ts +18 -0
  2. package/dist/agent-endpoint.js +53 -2
  3. package/dist/agent-endpoint.js.map +1 -1
  4. package/dist/apply-job.d.ts +114 -0
  5. package/dist/apply-job.js +214 -0
  6. package/dist/apply-job.js.map +1 -0
  7. package/dist/backend/claude-code.d.ts +25 -0
  8. package/dist/backend/claude-code.js +127 -0
  9. package/dist/backend/claude-code.js.map +1 -1
  10. package/dist/backend/codex.d.ts +57 -0
  11. package/dist/backend/codex.js +183 -0
  12. package/dist/backend/codex.js.map +1 -1
  13. package/dist/backend/credential-profile.d.ts +159 -0
  14. package/dist/backend/credential-profile.js +340 -0
  15. package/dist/backend/credential-profile.js.map +1 -0
  16. package/dist/backend/factory.js +4 -1
  17. package/dist/backend/factory.js.map +1 -1
  18. package/dist/backend/kiro-auth-store.d.ts +25 -0
  19. package/dist/backend/kiro-auth-store.js +61 -0
  20. package/dist/backend/kiro-auth-store.js.map +1 -0
  21. package/dist/backend/kiro.d.ts +8 -0
  22. package/dist/backend/kiro.js +31 -2
  23. package/dist/backend/kiro.js.map +1 -1
  24. package/dist/backend/muse.d.ts +90 -0
  25. package/dist/backend/muse.js +437 -0
  26. package/dist/backend/muse.js.map +1 -0
  27. package/dist/backend/types.d.ts +54 -0
  28. package/dist/backend/types.js.map +1 -1
  29. package/dist/channel/access-manager.d.ts +20 -0
  30. package/dist/channel/access-manager.js +25 -0
  31. package/dist/channel/access-manager.js.map +1 -1
  32. package/dist/channel/adapters/discord.d.ts +17 -0
  33. package/dist/channel/adapters/discord.js +72 -11
  34. package/dist/channel/adapters/discord.js.map +1 -1
  35. package/dist/channel/adapters/telegram.d.ts +6 -0
  36. package/dist/channel/adapters/telegram.js +36 -0
  37. package/dist/channel/adapters/telegram.js.map +1 -1
  38. package/dist/channel/markdown-chunk.d.ts +8 -3
  39. package/dist/channel/markdown-chunk.js +12 -3
  40. package/dist/channel/markdown-chunk.js.map +1 -1
  41. package/dist/channel/mcp-server.js +9 -12
  42. package/dist/channel/mcp-server.js.map +1 -1
  43. package/dist/channel/mcp-tools.d.ts +6 -2
  44. package/dist/channel/mcp-tools.js +6 -36
  45. package/dist/channel/mcp-tools.js.map +1 -1
  46. package/dist/channel/tool-router.js +32 -3
  47. package/dist/channel/tool-router.js.map +1 -1
  48. package/dist/channel/types.d.ts +18 -0
  49. package/dist/cli.js +113 -0
  50. package/dist/cli.js.map +1 -1
  51. package/dist/config-validator.js +45 -5
  52. package/dist/config-validator.js.map +1 -1
  53. package/dist/connection-secrets.d.ts +96 -0
  54. package/dist/connection-secrets.js +25 -0
  55. package/dist/connection-secrets.js.map +1 -0
  56. package/dist/daemon.d.ts +76 -0
  57. package/dist/daemon.js +489 -103
  58. package/dist/daemon.js.map +1 -1
  59. package/dist/event-log.d.ts +9 -0
  60. package/dist/event-log.js +21 -0
  61. package/dist/event-log.js.map +1 -1
  62. package/dist/fleet-level-config.d.ts +42 -0
  63. package/dist/fleet-level-config.js +78 -0
  64. package/dist/fleet-level-config.js.map +1 -0
  65. package/dist/fleet-lock.d.ts +21 -0
  66. package/dist/fleet-lock.js +42 -8
  67. package/dist/fleet-lock.js.map +1 -1
  68. package/dist/fleet-manager.d.ts +425 -2
  69. package/dist/fleet-manager.js +2055 -182
  70. package/dist/fleet-manager.js.map +1 -1
  71. package/dist/general-knowledge/skills/credential-profiles/SKILL.md +145 -0
  72. package/dist/general-knowledge/skills/worker-collaboration/SKILL.md +11 -1
  73. package/dist/instance-config-impact.d.ts +55 -0
  74. package/dist/instance-config-impact.js +152 -0
  75. package/dist/instance-config-impact.js.map +1 -0
  76. package/dist/instance-lifecycle.js +8 -4
  77. package/dist/instance-lifecycle.js.map +1 -1
  78. package/dist/locale.js +24 -0
  79. package/dist/locale.js.map +1 -1
  80. package/dist/logger.js +42 -25
  81. package/dist/logger.js.map +1 -1
  82. package/dist/muse-usage-relay.d.ts +72 -0
  83. package/dist/muse-usage-relay.js +419 -0
  84. package/dist/muse-usage-relay.js.map +1 -0
  85. package/dist/outbound-handlers.d.ts +5 -1
  86. package/dist/outbound-handlers.js +199 -1
  87. package/dist/outbound-handlers.js.map +1 -1
  88. package/dist/outbound-schemas.d.ts +7 -5
  89. package/dist/outbound-schemas.js +3 -2
  90. package/dist/outbound-schemas.js.map +1 -1
  91. package/dist/provider-probe.d.ts +48 -0
  92. package/dist/provider-probe.js +121 -0
  93. package/dist/provider-probe.js.map +1 -0
  94. package/dist/provider-secret-registry.d.ts +98 -0
  95. package/dist/provider-secret-registry.js +334 -0
  96. package/dist/provider-secret-registry.js.map +1 -0
  97. package/dist/quickstart-api.d.ts +164 -0
  98. package/dist/quickstart-api.js +351 -0
  99. package/dist/quickstart-api.js.map +1 -0
  100. package/dist/quickstart.js +23 -50
  101. package/dist/quickstart.js.map +1 -1
  102. package/dist/scheduler/db.js +17 -5
  103. package/dist/scheduler/db.js.map +1 -1
  104. package/dist/scheduler/db.test.js +34 -1
  105. package/dist/scheduler/db.test.js.map +1 -1
  106. package/dist/scheduler/types.d.ts +4 -0
  107. package/dist/scheduler/types.js.map +1 -1
  108. package/dist/secret-store.d.ts +25 -0
  109. package/dist/secret-store.js +165 -0
  110. package/dist/secret-store.js.map +1 -0
  111. package/dist/self-restart-limit.d.ts +21 -0
  112. package/dist/self-restart-limit.js +129 -0
  113. package/dist/self-restart-limit.js.map +1 -0
  114. package/dist/settings-api.d.ts +116 -0
  115. package/dist/settings-api.js +530 -5
  116. package/dist/settings-api.js.map +1 -1
  117. package/dist/setup-auth.d.ts +113 -0
  118. package/dist/setup-auth.js +181 -0
  119. package/dist/setup-auth.js.map +1 -0
  120. package/dist/setup-form.d.ts +18 -0
  121. package/dist/setup-form.js +315 -0
  122. package/dist/setup-form.js.map +1 -0
  123. package/dist/setup-host.d.ts +162 -0
  124. package/dist/setup-host.js +496 -0
  125. package/dist/setup-host.js.map +1 -0
  126. package/dist/setup-marker.d.ts +8 -0
  127. package/dist/setup-marker.js +39 -0
  128. package/dist/setup-marker.js.map +1 -0
  129. package/dist/setup-tunnel-consent.d.ts +53 -0
  130. package/dist/setup-tunnel-consent.js +82 -0
  131. package/dist/setup-tunnel-consent.js.map +1 -0
  132. package/dist/setup-wizard.js +4 -0
  133. package/dist/setup-wizard.js.map +1 -1
  134. package/dist/steer-capability.js +4 -1
  135. package/dist/steer-capability.js.map +1 -1
  136. package/dist/tips.js +1 -1
  137. package/dist/tips.js.map +1 -1
  138. package/dist/tmux-manager.js +29 -4
  139. package/dist/tmux-manager.js.map +1 -1
  140. package/dist/tool-permissions-notice.d.ts +38 -0
  141. package/dist/tool-permissions-notice.js +92 -0
  142. package/dist/tool-permissions-notice.js.map +1 -0
  143. package/dist/tool-permissions.d.ts +120 -0
  144. package/dist/tool-permissions.js +277 -0
  145. package/dist/tool-permissions.js.map +1 -0
  146. package/dist/topic-commands.js +1 -0
  147. package/dist/topic-commands.js.map +1 -1
  148. package/dist/transcript-sources.d.ts +12 -1
  149. package/dist/transcript-sources.js +19 -3
  150. package/dist/transcript-sources.js.map +1 -1
  151. package/dist/tunnel/cloudflared.d.ts +71 -0
  152. package/dist/tunnel/cloudflared.js +447 -0
  153. package/dist/tunnel/cloudflared.js.map +1 -0
  154. package/dist/tunnel/lease.d.ts +75 -0
  155. package/dist/tunnel/lease.js +209 -0
  156. package/dist/tunnel/lease.js.map +1 -0
  157. package/dist/tunnel/manager.d.ts +56 -0
  158. package/dist/tunnel/manager.js +167 -0
  159. package/dist/tunnel/manager.js.map +1 -0
  160. package/dist/tunnel/types.d.ts +119 -0
  161. package/dist/tunnel/types.js +33 -0
  162. package/dist/tunnel/types.js.map +1 -0
  163. package/dist/types.d.ts +2 -0
  164. package/dist/ui/dashboard.html +4 -3
  165. package/dist/ui/settings.html +808 -156
  166. package/dist/ui/view.html +2 -2
  167. package/dist/usage/i18n-keys.d.ts +1 -1
  168. package/dist/usage/i18n-keys.js +1 -1
  169. package/dist/usage/i18n-keys.js.map +1 -1
  170. package/dist/usage/providers.d.ts +23 -14
  171. package/dist/usage/providers.js +220 -58
  172. package/dist/usage/providers.js.map +1 -1
  173. package/dist/usage/usage-api.d.ts +13 -0
  174. package/dist/usage/usage-api.js +69 -1
  175. package/dist/usage/usage-api.js.map +1 -1
  176. package/dist/web-api.js +8 -5
  177. package/dist/web-api.js.map +1 -1
  178. package/dist/web-auth.d.ts +75 -0
  179. package/dist/web-auth.js +208 -2
  180. package/dist/web-auth.js.map +1 -1
  181. package/dist/web-terminal.d.ts +30 -1
  182. package/dist/web-terminal.js +86 -3
  183. package/dist/web-terminal.js.map +1 -1
  184. package/package.json +2 -1
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Small, deliberately boring secret store for the running Settings API.
3
+ *
4
+ * Secrets live in the existing .env file for compatibility with the CLI, but
5
+ * all writes go through this module. The write is same-directory atomic,
6
+ * owner-only, and returns a snapshot that can be restored when an adapter
7
+ * rebuild fails. No exception from this module includes the secret value.
8
+ */
9
+ import { closeSync, fsyncSync, lstatSync, mkdirSync, openSync, readFileSync, renameSync, unlinkSync, writeSync, chmodSync, } from "node:fs";
10
+ import { randomBytes } from "node:crypto";
11
+ import { dirname, join } from "node:path";
12
+ export const SECRET_ENV_KEY = /^[A-Za-z_][A-Za-z0-9_]{0,127}$/;
13
+ const RESERVED_ENV_KEYS = new Set([
14
+ "PATH", "HOME", "PWD", "SHELL", "ENV", "BASH_ENV", "NODE_OPTIONS", "LD_PRELOAD",
15
+ ]);
16
+ function fsyncDirectory(path, fsync) {
17
+ const fd = openSync(path, "r");
18
+ try {
19
+ fsync(fd);
20
+ }
21
+ finally {
22
+ closeSync(fd);
23
+ }
24
+ }
25
+ /** Replace one KEY=value while retaining comments and unrelated variables. */
26
+ export function upsertSecretEnvLine(existing, key, value) {
27
+ const lines = existing.split(/\r?\n/);
28
+ let replaced = false;
29
+ const next = lines.map(line => {
30
+ const match = line.match(/^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=/);
31
+ if (!match || match[1] !== key)
32
+ return line;
33
+ replaced = true;
34
+ return `${key}=${value}`;
35
+ });
36
+ if (!replaced) {
37
+ while (next.length && next[next.length - 1] === "")
38
+ next.pop();
39
+ next.push(`${key}=${value}`);
40
+ }
41
+ return next.join("\n").replace(/\n*$/, "\n");
42
+ }
43
+ export class SecretStore {
44
+ path;
45
+ allowedKeys;
46
+ fsync;
47
+ constructor(path, allowedKeys = new Set(), opts = {}) {
48
+ this.path = path;
49
+ this.allowedKeys = allowedKeys;
50
+ this.fsync = opts.fsync ?? fsyncSync;
51
+ mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
52
+ this.rejectSymlink(path);
53
+ }
54
+ rejectSymlink(path) {
55
+ try {
56
+ if (lstatSync(path).isSymbolicLink())
57
+ throw new Error("secret file must not be a symlink");
58
+ }
59
+ catch (err) {
60
+ if (err.code !== "ENOENT")
61
+ throw err;
62
+ }
63
+ }
64
+ validateKey(key) {
65
+ if (!SECRET_ENV_KEY.test(key) || RESERVED_ENV_KEYS.has(key))
66
+ throw new Error("secret key is not allowed");
67
+ if (this.allowedKeys.size > 0 && !this.allowedKeys.has(key))
68
+ throw new Error("secret key is not configured for a connection");
69
+ }
70
+ snapshot() {
71
+ this.rejectSymlink(this.path);
72
+ try {
73
+ return { exists: true, content: readFileSync(this.path, "utf8") };
74
+ }
75
+ catch (err) {
76
+ if (err.code === "ENOENT")
77
+ return { exists: false, content: "" };
78
+ throw new Error("secret file could not be read");
79
+ }
80
+ }
81
+ /** Atomic replacement; callers should retain snapshot() for rollback. */
82
+ replace(content) {
83
+ this.rejectSymlink(this.path);
84
+ const dir = dirname(this.path);
85
+ const temp = join(dir, `.${this.path.split(/[\\/]/).pop() ?? "env"}.${process.pid}.${randomBytes(8).toString("hex")}.tmp`);
86
+ let fd;
87
+ try {
88
+ fd = openSync(temp, "wx", 0o600);
89
+ writeSync(fd, content, undefined, "utf8");
90
+ this.fsync(fd);
91
+ closeSync(fd);
92
+ fd = undefined;
93
+ // chmod is deliberately a hard error: a successful rename with loose
94
+ // permissions is never reported as an applied secret.
95
+ chmodSync(temp, 0o600);
96
+ this.rejectSymlink(temp);
97
+ renameSync(temp, this.path);
98
+ this.fsyncDirectory(dir);
99
+ }
100
+ catch (err) {
101
+ if (fd !== undefined) {
102
+ try {
103
+ closeSync(fd);
104
+ }
105
+ catch { /* best effort */ }
106
+ }
107
+ try {
108
+ unlinkSync(temp);
109
+ }
110
+ catch { /* best effort */ }
111
+ if (err instanceof Error && /secret file must not be a symlink/.test(err.message))
112
+ throw err;
113
+ throw new Error("secret file could not be written atomically");
114
+ }
115
+ }
116
+ fsyncDirectory(path) {
117
+ try {
118
+ fsyncDirectory(path, this.fsync);
119
+ }
120
+ catch {
121
+ throw new Error("secret directory could not be synced");
122
+ }
123
+ }
124
+ restore(snapshot) {
125
+ if (snapshot.exists)
126
+ this.replace(snapshot.content);
127
+ else {
128
+ this.rejectSymlink(this.path);
129
+ try {
130
+ unlinkSync(this.path);
131
+ }
132
+ catch (err) {
133
+ if (err.code !== "ENOENT")
134
+ throw new Error("secret rollback could not remove file");
135
+ }
136
+ try {
137
+ this.fsyncDirectory(dirname(this.path));
138
+ }
139
+ catch {
140
+ throw new Error("secret rollback could not sync directory");
141
+ }
142
+ }
143
+ }
144
+ write(key, value) {
145
+ this.validateKey(key);
146
+ if (!value || /[\r\n\0]/.test(value))
147
+ throw new Error("secret value is invalid");
148
+ const before = this.snapshot();
149
+ try {
150
+ this.replace(upsertSecretEnvLine(before.content, key, value));
151
+ }
152
+ catch (err) {
153
+ // A directory fsync can fail after rename has already replaced the
154
+ // target. Restore the snapshot here so callers never see a failed write
155
+ // with a new secret left on disk.
156
+ try {
157
+ this.restore(before);
158
+ }
159
+ catch { /* caller reports rollback failure */ }
160
+ throw err;
161
+ }
162
+ return before;
163
+ }
164
+ }
165
+ //# sourceMappingURL=secret-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secret-store.js","sourceRoot":"","sources":["../src/secret-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /** At most one panel-triggered self-restart per 10 minutes… */
2
+ export declare const SELF_RESTART_MIN_INTERVAL_MS: number;
3
+ /** …and at most three in an hour, so a slow drip cannot add up to a outage. */
4
+ export declare const SELF_RESTART_WINDOW_MS: number;
5
+ export declare const SELF_RESTART_MAX_PER_WINDOW = 3;
6
+ export interface SelfRestartAllowance {
7
+ allowed: boolean;
8
+ /** Seconds until the next attempt would be accepted; 0 when allowed. */
9
+ retryAfterSeconds: number;
10
+ reason?: "too-soon" | "hourly-cap" | "unreadable";
11
+ }
12
+ export declare function checkSelfRestartAllowance(dataDir: string, now?: number): SelfRestartAllowance;
13
+ /**
14
+ * Record an attempt durably, and say whether it reached the disk.
15
+ *
16
+ * The caller must refuse to restart when this returns false: an unrecorded
17
+ * attempt is an unlimited one.
18
+ */
19
+ export declare function recordSelfRestartAttempt(dataDir: string, now?: number): boolean;
20
+ /** Test seam: the attempts currently on disk, empty when unreadable. */
21
+ export declare function readSelfRestartAttempts(dataDir: string): number[];
@@ -0,0 +1,129 @@
1
+ /**
2
+ * How often the Settings panel may restart AgEnD itself.
3
+ *
4
+ * This is one of the three things that actually hold the DoS line (with the
5
+ * cookie/Origin gate and the out-of-band notice in the channel). The
6
+ * "there must be a pending fleet-level change" precondition is *not* one of
7
+ * them: whoever holds the token can create a pending change at will, so that
8
+ * check only stops misclicks.
9
+ *
10
+ * Three properties the file has to have, all learned from how this fails:
11
+ *
12
+ * - **Written and fsynced before the restart is launched.** The process is
13
+ * about to be replaced. An attempt recorded after the spawn, or left in the
14
+ * OS page cache, is an attempt that never happened — and a rate limit whose
15
+ * counter resets on every restart does not limit restarts.
16
+ * - **Attempts, not successes.** A restart that reliably fails would otherwise
17
+ * be retryable without limit, which is the cheapest possible attack.
18
+ * - **No route may clear it.** A rate limit the attacker can reset is not a
19
+ * rate limit. Nothing in the web layer writes this file except `record()`.
20
+ * - **Unreadable means spent, not fresh.** Treating a corrupt file as an empty
21
+ * one would let the control disarm itself exactly when its state is in doubt.
22
+ * Recovery is deleting the file on the host — which needs the same access as
23
+ * running `agend restart` directly.
24
+ */
25
+ import { closeSync, fsyncSync, openSync, readFileSync, writeSync } from "node:fs";
26
+ import { join } from "node:path";
27
+ const FILE = "self-restart.json";
28
+ /** At most one panel-triggered self-restart per 10 minutes… */
29
+ export const SELF_RESTART_MIN_INTERVAL_MS = 10 * 60_000;
30
+ /** …and at most three in an hour, so a slow drip cannot add up to a outage. */
31
+ export const SELF_RESTART_WINDOW_MS = 60 * 60_000;
32
+ export const SELF_RESTART_MAX_PER_WINDOW = 3;
33
+ function path(dataDir) {
34
+ return join(dataDir, FILE);
35
+ }
36
+ function read(dataDir) {
37
+ let raw;
38
+ try {
39
+ raw = readFileSync(path(dataDir), "utf-8");
40
+ }
41
+ catch (err) {
42
+ // Never written yet — the normal first run.
43
+ if (err.code === "ENOENT")
44
+ return { state: "empty" };
45
+ return { state: "unreadable" };
46
+ }
47
+ try {
48
+ const parsed = JSON.parse(raw);
49
+ if (!Array.isArray(parsed.attempts))
50
+ return { state: "unreadable" };
51
+ const attempts = parsed.attempts.filter(item => typeof item === "number" && Number.isFinite(item));
52
+ if (attempts.length !== parsed.attempts.length)
53
+ return { state: "unreadable" };
54
+ return { state: "ok", attempts };
55
+ }
56
+ catch {
57
+ // Half-written or corrupt. The one thing this must not do is read as "no
58
+ // attempts yet" — a rate limit that resets itself when its own state is
59
+ // damaged is not a rate limit.
60
+ return { state: "unreadable" };
61
+ }
62
+ }
63
+ function recordedAttempts(dataDir) {
64
+ const state = read(dataDir);
65
+ return state.state === "ok" ? state.attempts : [];
66
+ }
67
+ export function checkSelfRestartAllowance(dataDir, now = Date.now()) {
68
+ const state = read(dataDir);
69
+ if (state.state === "unreadable") {
70
+ return { allowed: false, reason: "unreadable", retryAfterSeconds: 0 };
71
+ }
72
+ const attempts = (state.state === "ok" ? state.attempts : [])
73
+ .filter(at => now - at < SELF_RESTART_WINDOW_MS)
74
+ .sort((a, b) => a - b);
75
+ const last = attempts[attempts.length - 1];
76
+ if (last !== undefined && now - last < SELF_RESTART_MIN_INTERVAL_MS) {
77
+ return {
78
+ allowed: false,
79
+ reason: "too-soon",
80
+ retryAfterSeconds: Math.max(1, Math.ceil((SELF_RESTART_MIN_INTERVAL_MS - (now - last)) / 1000)),
81
+ };
82
+ }
83
+ if (attempts.length >= SELF_RESTART_MAX_PER_WINDOW) {
84
+ const oldest = attempts[attempts.length - SELF_RESTART_MAX_PER_WINDOW];
85
+ return {
86
+ allowed: false,
87
+ reason: "hourly-cap",
88
+ retryAfterSeconds: Math.max(1, Math.ceil((SELF_RESTART_WINDOW_MS - (now - oldest)) / 1000)),
89
+ };
90
+ }
91
+ return { allowed: true, retryAfterSeconds: 0 };
92
+ }
93
+ /**
94
+ * Record an attempt durably, and say whether it reached the disk.
95
+ *
96
+ * The caller must refuse to restart when this returns false: an unrecorded
97
+ * attempt is an unlimited one.
98
+ */
99
+ export function recordSelfRestartAttempt(dataDir, now = Date.now()) {
100
+ const attempts = [...recordedAttempts(dataDir).filter(at => now - at < SELF_RESTART_WINDOW_MS), now];
101
+ const target = path(dataDir);
102
+ let fd = null;
103
+ try {
104
+ // Written in place with an fsync rather than the usual write-temp-and-rename:
105
+ // what matters here is that the bytes are on the device before the process
106
+ // is replaced, and a rename would need its own directory fsync to promise
107
+ // the same thing.
108
+ fd = openSync(target, "w", 0o600);
109
+ writeSync(fd, JSON.stringify({ attempts }));
110
+ fsyncSync(fd);
111
+ return true;
112
+ }
113
+ catch {
114
+ return false;
115
+ }
116
+ finally {
117
+ if (fd !== null) {
118
+ try {
119
+ closeSync(fd);
120
+ }
121
+ catch { /* already gone */ }
122
+ }
123
+ }
124
+ }
125
+ /** Test seam: the attempts currently on disk, empty when unreadable. */
126
+ export function readSelfRestartAttempts(dataDir) {
127
+ return recordedAttempts(dataDir);
128
+ }
129
+ //# sourceMappingURL=self-restart-limit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"self-restart-limit.js","sourceRoot":"","sources":["../src/self-restart-limit.ts"],"names":[],"mappings":"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"}
@@ -25,6 +25,9 @@
25
25
  import type { IncomingMessage, ServerResponse } from "node:http";
26
26
  import type { Logger } from "./logger.js";
27
27
  import type { FleetConfig, RawFleetConfig } from "./types.js";
28
+ import { type ApplyJob, type ApplyJobStore, type SelfRestartResult } from "./apply-job.js";
29
+ import { type ConnectionMetadata, type ConnectionBinding, type BindingProbe, type SecretApplyJob, type ProviderSecretApplyJob } from "./connection-secrets.js";
30
+ import type { ProviderSecretStatus } from "./provider-secret-registry.js";
28
31
  export interface SettingsApiContext {
29
32
  fleetConfig: FleetConfig | null;
30
33
  configPath: string | null;
@@ -39,6 +42,119 @@ export interface SettingsApiContext {
39
42
  };
40
43
  isClassicInstance?(name: string): boolean;
41
44
  restartClassicInstanceFromSettings?(instanceName: string, changedFields?: string[]): Promise<void>;
45
+ /** Present on a real fleet; absent in unit contexts that only exercise CRUD. */
46
+ applyJobs?: ApplyJobStore;
47
+ startSettingsApply?(key: string): {
48
+ job: ApplyJob;
49
+ reused: boolean;
50
+ } | {
51
+ busy: ApplyJob | null;
52
+ };
53
+ requestSettingsSelfRestart?(jobId: string, key: string): Promise<SelfRestartResult>;
54
+ /** Non-null when the running config and fleet.yaml disagree on a
55
+ * startup-only key, in which case a restart cannot clear the fleet row. */
56
+ fleetSignatureMismatchKeys?(): string[] | null;
57
+ /** True when a running adapter is already long-polling this bot token. */
58
+ isBotTokenInUse?(token: string): boolean;
59
+ /** Secure Connections & Bots operations. The outer fleet HTTP server has
60
+ * already authenticated the Settings web session before this handler runs. */
61
+ listSecureConnections?(): ConnectionMetadata[];
62
+ verifyConnectionSecret?(input: {
63
+ connectionId: string;
64
+ secret: string;
65
+ sessionBinding: string;
66
+ idempotencyKey: string;
67
+ }): Promise<{
68
+ ok: true;
69
+ verification_id: string;
70
+ expires_at: number;
71
+ identity?: {
72
+ id: string | null;
73
+ username: string | null;
74
+ };
75
+ } | {
76
+ ok: false;
77
+ error: string;
78
+ }>;
79
+ startConnectionSecretApply?(input: {
80
+ connectionId: string;
81
+ verificationId: string;
82
+ sessionBinding: string;
83
+ idempotencyKey: string;
84
+ }): {
85
+ job: SecretApplyJob;
86
+ reused: boolean;
87
+ } | {
88
+ busy: SecretApplyJob | null;
89
+ } | {
90
+ error: string;
91
+ };
92
+ getConnectionSecretApply?(jobId: string, sessionBinding: string): SecretApplyJob | null;
93
+ /** Generic provider API-key verifier registry (#861). */
94
+ providerSecretsEnabled?(): boolean;
95
+ listProviderSecrets?(): ProviderSecretStatus[];
96
+ verifyProviderSecret?(input: {
97
+ specId: string;
98
+ secret: string;
99
+ sessionBinding: string;
100
+ idempotencyKey: string;
101
+ }): Promise<{
102
+ ok: true;
103
+ verification_id: string;
104
+ expires_at: number;
105
+ spec_id: string;
106
+ activation: "next_use" | "reload_hook";
107
+ } | {
108
+ ok: false;
109
+ status: string;
110
+ error: string;
111
+ }>;
112
+ startProviderSecretApply?(input: {
113
+ specId: string;
114
+ verificationId: string;
115
+ sessionBinding: string;
116
+ idempotencyKey: string;
117
+ }): {
118
+ job: ProviderSecretApplyJob;
119
+ reused: boolean;
120
+ } | {
121
+ busy: ProviderSecretApplyJob | null;
122
+ } | {
123
+ error: string;
124
+ };
125
+ getProviderSecretApply?(jobId: string, sessionBinding: string): ProviderSecretApplyJob | null;
126
+ verifyConnectionBinding?(input: {
127
+ connectionId: string;
128
+ binding: {
129
+ group_id?: unknown;
130
+ general_channel_id?: unknown;
131
+ };
132
+ sessionBinding: string;
133
+ idempotencyKey: string;
134
+ }): Promise<{
135
+ ok: true;
136
+ verification_id: string;
137
+ expires_at: number;
138
+ binding: ConnectionBinding;
139
+ probe: BindingProbe;
140
+ } | {
141
+ ok: false;
142
+ error: string;
143
+ }>;
144
+ startConnectionBindingApply?(input: {
145
+ connectionId: string;
146
+ verificationId: string;
147
+ sessionBinding: string;
148
+ idempotencyKey: string;
149
+ }): {
150
+ job: SecretApplyJob;
151
+ reused: boolean;
152
+ } | {
153
+ busy: SecretApplyJob | null;
154
+ } | {
155
+ error: string;
156
+ };
157
+ getConnectionBindingApply?(jobId: string, sessionBinding: string): SecretApplyJob | null;
42
158
  }
43
159
  /** An explicit user-authored YAML mutation that must be persisted even when
44
160
  * its value equals the defaults-expanded runtime snapshot. */