@mininglamp-oss/cc-channel-octo 1.0.1-dev.8d32215 → 1.0.2-dev.0af5b14

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,143 @@
1
+ /**
2
+ * Config watcher — drives BotManager from config.json changes (#157).
3
+ *
4
+ * Watches the global config's directory (not the file directly: an atomic
5
+ * temp+rename swaps the inode, and a file-targeted fs.watch would stop firing
6
+ * after the first rename). On any change it schedules a debounced, serialized
7
+ * `applyLatestConfig`, which RE-READS the latest config inside the task (never
8
+ * a precomputed diff — plan C6) and reconciles the running set toward it.
9
+ *
10
+ * Robustness (plan D): the desired set is produced by a single `loadDesired`
11
+ * call; if it throws for ANY reason (half-written file, JSON error, missing
12
+ * token, duplicate id, unsafe apiUrl, broken per-bot config), the current
13
+ * running set is left untouched — a bad edit never tears down healthy bots.
14
+ */
15
+ import { watch } from 'node:fs';
16
+ import { dirname, basename } from 'node:path';
17
+ import { diffBotSets } from './bot-manager.js';
18
+ /**
19
+ * Reconcile the running set toward `desiredConfigIds` once. Removes first, then
20
+ * adds, so a config that swaps a bot for another with an overlapping resource
21
+ * (lock/dir) frees it before the replacement claims it. Add/remove failures are
22
+ * logged and swallowed so one bad bot doesn't abort the whole reconcile; the
23
+ * BotManager queue keeps the set consistent. Pure-ish (no fs) for unit testing.
24
+ *
25
+ * `isStale` is checked before each add/remove: a newer config event makes the
26
+ * in-flight reconcile abandon its remaining (now outdated) actions, so a bot
27
+ * removed by a later edit is never started by an earlier, slower reconcile
28
+ * (plan C6 generation guard).
29
+ */
30
+ export async function reconcile(manager, desiredConfigIds, log = () => { }, isStale = () => false) {
31
+ const { toAdd, toRemove } = diffBotSets(desiredConfigIds, manager.runningKeys());
32
+ for (const id of toRemove) {
33
+ if (isStale()) {
34
+ log(`[hot-reload] reconcile superseded by a newer config, stopping`);
35
+ return;
36
+ }
37
+ try {
38
+ await manager.removeBot(id);
39
+ log(`[hot-reload] removed bot ${id}`);
40
+ }
41
+ catch (err) {
42
+ log(`[hot-reload] removeBot ${id} failed: ${errMsg(err)}`);
43
+ }
44
+ }
45
+ for (const id of toAdd) {
46
+ if (isStale()) {
47
+ log(`[hot-reload] reconcile superseded by a newer config, stopping`);
48
+ return;
49
+ }
50
+ try {
51
+ await manager.addBot(id);
52
+ log(`[hot-reload] added bot ${id}`);
53
+ }
54
+ catch (err) {
55
+ log(`[hot-reload] addBot ${id} failed: ${errMsg(err)}`);
56
+ }
57
+ }
58
+ }
59
+ function errMsg(err) {
60
+ return err instanceof Error ? err.message : String(err);
61
+ }
62
+ /**
63
+ * Start watching `configPath`'s directory and reconcile on change.
64
+ *
65
+ * Serialization & staleness: a single `chain` promise serializes apply runs,
66
+ * and each run re-reads the latest config at execution time, so a burst of
67
+ * events collapses to "apply the newest state" rather than replaying each
68
+ * intermediate edit. The BotManager's own queue further serializes the
69
+ * resulting add/remove calls.
70
+ */
71
+ export function watchConfig(opts) {
72
+ const { configPath, manager, loadDesired } = opts;
73
+ const debounceMs = opts.debounceMs ?? 200;
74
+ const log = opts.log ?? (() => { });
75
+ const dir = dirname(configPath);
76
+ const file = basename(configPath);
77
+ let chain = Promise.resolve();
78
+ let timer;
79
+ let closed = false;
80
+ // Generation guard (plan C6). `latestGen` is bumped the moment a file event is
81
+ // observed (in schedule), NOT when the debounced apply finally runs — so an
82
+ // in-flight reconcile is invalidated immediately on a new event, even during
83
+ // the debounce window, instead of continuing to add/connect a bot a later
84
+ // edit already removed. Each apply captures the gen current when it starts and
85
+ // bails (isStale) as soon as a newer event has bumped past it.
86
+ let latestGen = 0;
87
+ const apply = () => {
88
+ const myGen = latestGen;
89
+ // Re-read latest desired set INSIDE the serialized task (plan C6). Any
90
+ // failure (half-write / invalid config) leaves the running set untouched.
91
+ chain = chain.then(async () => {
92
+ if (closed)
93
+ return;
94
+ let desired;
95
+ try {
96
+ desired = loadDesired();
97
+ }
98
+ catch (err) {
99
+ log(`[hot-reload] config invalid, keeping current bots: ${errMsg(err)}`);
100
+ return;
101
+ }
102
+ await reconcile(manager, desired, log, () => closed || myGen !== latestGen);
103
+ });
104
+ return chain;
105
+ };
106
+ const schedule = () => {
107
+ if (closed)
108
+ return;
109
+ // Invalidate any in-flight reconcile right now (not at debounce expiry): a
110
+ // slow reconcile must see this newer event before it acts on stale desired.
111
+ latestGen++;
112
+ if (timer)
113
+ clearTimeout(timer);
114
+ timer = setTimeout(() => {
115
+ timer = undefined;
116
+ void apply();
117
+ }, debounceMs);
118
+ timer.unref?.();
119
+ };
120
+ let watcher;
121
+ try {
122
+ watcher = watch(dir, (_event, changed) => {
123
+ // Only react to our config file (the dir may hold per-bot subdirs etc.).
124
+ // changed is null on some platforms — be permissive and reconcile then.
125
+ if (changed === null || changed === file)
126
+ schedule();
127
+ });
128
+ watcher.on('error', (err) => log(`[hot-reload] watcher error: ${errMsg(err)}`));
129
+ }
130
+ catch (err) {
131
+ log(`[hot-reload] failed to start watcher on ${dir}: ${errMsg(err)}`);
132
+ }
133
+ return {
134
+ applyNow: apply,
135
+ close: () => {
136
+ closed = true;
137
+ if (timer)
138
+ clearTimeout(timer);
139
+ watcher?.close();
140
+ },
141
+ };
142
+ }
143
+ //# sourceMappingURL=config-watcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-watcher.js","sourceRoot":"","sources":["../src/config-watcher.ts"],"names":[],"mappings":"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"}
package/dist/config.js CHANGED
@@ -230,6 +230,18 @@ function isPathInside(child, parent) {
230
230
  * apiUrl (fail fast at boot).
231
231
  */
232
232
  export function resolveBotConfigs(config) {
233
+ // Zero-bot idle: no bots[] list and no global botToken — and no token in the
234
+ // default per-bot file either. Return [] so the gateway can run idle (online,
235
+ // no bots) until the first bot is provisioned, instead of throwing. A legacy
236
+ // single bot may keep its token only in <baseDir>/default/config.json (read by
237
+ // the synthesized "default" entry below), so check that before idling.
238
+ const hasInlineBots = !!(config.bots && config.bots.length > 0);
239
+ if (!hasInlineBots && !config.botToken) {
240
+ const defaultPerBot = readConfigFile(pathJoin(config.baseDir, 'default', 'config.json'));
241
+ if (!defaultPerBot.botToken) {
242
+ return [];
243
+ }
244
+ }
233
245
  // Single-bot: synthesize one entry with id "default".
234
246
  const entries = config.bots && config.bots.length > 0
235
247
  ? config.bots
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -1 +1,11 @@
1
- export declare function configure(gatewayUrl: string, apiKey: string, configPath?: string): void;
1
+ /**
2
+ * The Anthropic SDK appends `/v1/messages` to ANTHROPIC_BASE_URL. A gateway
3
+ * pasted with a trailing `/v1` would otherwise yield `/v1/v1/messages` (404,
4
+ * misreported as a model error). Strip a trailing `/v1` (optionally with a
5
+ * slash) so the stored base is the host root. Pure for unit testing.
6
+ */
7
+ export declare function normalizeGatewayUrl(raw: string): string;
8
+ export declare function configure(gatewayUrl: string, apiKey: string, configPath?: string, opts?: {
9
+ model?: string;
10
+ apiUrl?: string;
11
+ }): void;
package/dist/configure.js CHANGED
@@ -12,7 +12,16 @@ import { readFileSync, writeFileSync, existsSync, mkdirSync, chmodSync, renameSy
12
12
  import { dirname } from 'node:path';
13
13
  import { DEFAULT_CONFIG_PATH } from './config.js';
14
14
  import { isAllowedApiUrl } from './url-policy.js';
15
- export function configure(gatewayUrl, apiKey, configPath) {
15
+ /**
16
+ * The Anthropic SDK appends `/v1/messages` to ANTHROPIC_BASE_URL. A gateway
17
+ * pasted with a trailing `/v1` would otherwise yield `/v1/v1/messages` (404,
18
+ * misreported as a model error). Strip a trailing `/v1` (optionally with a
19
+ * slash) so the stored base is the host root. Pure for unit testing.
20
+ */
21
+ export function normalizeGatewayUrl(raw) {
22
+ return raw.trim().replace(/\/v1\/?$/i, '');
23
+ }
24
+ export function configure(gatewayUrl, apiKey, configPath, opts) {
16
25
  if (!gatewayUrl)
17
26
  throw new Error('configure: --gateway-url is required');
18
27
  if (!apiKey)
@@ -22,6 +31,13 @@ export function configure(gatewayUrl, apiKey, configPath) {
22
31
  if (!isAllowedApiUrl(gatewayUrl)) {
23
32
  throw new Error(`configure: unsafe --gateway-url ${gatewayUrl} (must be https:// or http://localhost)`);
24
33
  }
34
+ // apiUrl is the Octo IM server (cc's top-level config.apiUrl). The daemon
35
+ // passes its server url at install time so the zero-bot idle gateway can boot
36
+ // (loadConfig requires apiUrl). Same SSRF policy as the gateway url.
37
+ if (opts?.apiUrl && !isAllowedApiUrl(opts.apiUrl)) {
38
+ throw new Error(`configure: unsafe --api-url ${opts.apiUrl} (must be https:// or http://localhost)`);
39
+ }
40
+ const normalizedUrl = normalizeGatewayUrl(gatewayUrl);
25
41
  const path = configPath ?? DEFAULT_CONFIG_PATH;
26
42
  let existing = {};
27
43
  if (existsSync(path)) {
@@ -48,8 +64,19 @@ export function configure(gatewayUrl, apiKey, configPath) {
48
64
  : {};
49
65
  const merged = {
50
66
  ...existing,
51
- sdk: { ...existingSdk, anthropicBaseUrl: gatewayUrl, apiKey },
67
+ sdk: { ...existingSdk, anthropicBaseUrl: normalizedUrl, apiKey },
52
68
  };
69
+ // Write model only when provided; omitting it PRESERVES any existing sdk.model
70
+ // (the existingSdk spread above) so a re-configure that just rotates the key
71
+ // never wipes the model. Resetting model→default is intentionally not a
72
+ // configure feature (add an explicit --clear-model later if ever needed).
73
+ if (opts?.model) {
74
+ merged.sdk.model = opts.model;
75
+ }
76
+ // The Octo IM server url lives at the top level (not under sdk).
77
+ if (opts?.apiUrl) {
78
+ merged.apiUrl = opts.apiUrl;
79
+ }
53
80
  mkdirSync(dirname(path), { recursive: true });
54
81
  // Atomic write: temp file in same directory with 0600 mode, then rename.
55
82
  // `wx` (exclusive create) refuses to write through a pre-existing file or a
@@ -1 +1 @@
1
- {"version":3,"file":"configure.js","sourceRoot":"","sources":["../src/configure.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"configure.js","sourceRoot":"","sources":["../src/configure.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -6,37 +6,28 @@
6
6
  * Orchestrates: loadConfig → createAdapter → SessionStore.init →
7
7
  * OctoGateway.start → setMessageHandler wiring the full pipeline.
8
8
  */
9
- import { loadConfig } from './config.js';
9
+ import { loadConfig, resolveBotConfigs } from './config.js';
10
+ import { type ManagedBot } from './bot-manager.js';
10
11
  import { SessionStore } from './session-store.js';
11
12
  import { SessionRouter } from './session-router.js';
12
13
  import { GroupContext } from './group-context.js';
13
14
  import { StreamRelay } from './stream-relay.js';
14
15
  import type { BotMessage } from './octo/types.js';
15
16
  import { CronStore } from './cron-store.js';
16
- export interface BotStack {
17
- botId: string;
18
- router: SessionRouter;
19
- connect: () => Promise<void>;
20
- shutdown: () => Promise<void>;
21
- }
22
- export interface StartFailure {
23
- /** The failed bot's id (or a positional `#<index>` fallback). */
24
- id: string;
25
- reason: unknown;
26
- }
27
17
  /**
28
- * Split the settled results of starting every bot into the stacks that came up
29
- * and the ones that failed. Pure (no I/O) so the multi-bot resilience policy
30
- * skip failed bots, keep the rest, only fatal when none startis unit
31
- * testable. A failed bot's own partial resources are released inside startBot's
32
- * failure path (see its try/catch); this function never touches a stack.
18
+ * Resolve a single bot's concrete Config by its configId (config.json
19
+ * `bots[].id`). Re-reads via resolveBotConfigs so a bot added after boot picks
20
+ * up the latest global+per-bot merge. Throws if the id is absentthe manager
21
+ * treats that as a failed add (rolled back), never starts a half-bot.
33
22
  */
34
- export declare function partitionStartResults(results: PromiseSettledResult<BotStack>[], configs: {
35
- botId?: string;
36
- }[]): {
37
- stacks: BotStack[];
38
- failures: StartFailure[];
39
- };
23
+ export declare function resolveBotConfigById(config: ReturnType<typeof loadConfig>, configId: string): ReturnType<typeof resolveBotConfigs>[number];
24
+ /**
25
+ * What startBot returns. The hot-reload manager (BotManager) consumes exactly
26
+ * ManagedBot — robotUid (loop-guard key) + router + connect + shutdown — so
27
+ * BotStack is just ManagedBot. The configId is the addBot argument, not a field
28
+ * on the stack (see plan B2 for why the two identities are kept separate).
29
+ */
30
+ export type BotStack = ManagedBot;
40
31
  /**
41
32
  * Process a single inbound message through the full pipeline: route → context →
42
33
  * agent query → stream → persist. Exported so tests can drive the real pipeline
package/dist/index.js CHANGED
@@ -6,7 +6,9 @@
6
6
  * Orchestrates: loadConfig → createAdapter → SessionStore.init →
7
7
  * OctoGateway.start → setMessageHandler wiring the full pipeline.
8
8
  */
9
- import { loadConfig, resolveBotConfigs } from './config.js';
9
+ import { loadConfig, resolveBotConfigs, DEFAULT_CONFIG_PATH } from './config.js';
10
+ import { BotManager } from './bot-manager.js';
11
+ import { watchConfig } from './config-watcher.js';
10
12
  import { createAdapter } from './db-adapter.js';
11
13
  import { SessionStore } from './session-store.js';
12
14
  import { OctoGateway } from './gateway.js';
@@ -29,6 +31,23 @@ import { buildInlinedFileBody, truncateUtf8ByBytes, assembleUserMessage, MAX_USE
29
31
  import { join } from 'node:path';
30
32
  import { mkdirSync, realpathSync } from 'node:fs';
31
33
  import { pathToFileURL, fileURLToPath } from 'node:url';
34
+ /**
35
+ * Resolve a single bot's concrete Config by its configId (config.json
36
+ * `bots[].id`). Re-reads via resolveBotConfigs so a bot added after boot picks
37
+ * up the latest global+per-bot merge. Throws if the id is absent — the manager
38
+ * treats that as a failed add (rolled back), never starts a half-bot.
39
+ */
40
+ export function resolveBotConfigById(config, configId) {
41
+ const match = resolveBotConfigs(config).find((c) => c.botId === configId);
42
+ if (!match) {
43
+ throw new Error(`bot config not found for id "${configId}"`);
44
+ }
45
+ return match;
46
+ }
47
+ /** Absolute path to the global config.json the watcher should observe. */
48
+ function configPathForWatch() {
49
+ return DEFAULT_CONFIG_PATH;
50
+ }
32
51
  async function main() {
33
52
  // --- Q8: Global unhandled rejection handler ---
34
53
  process.on('unhandledRejection', (reason) => {
@@ -36,114 +55,69 @@ async function main() {
36
55
  });
37
56
  // --- Config ---
38
57
  const config = loadConfig();
39
- // v0.3 multi-bot: expand into one concrete Config per bot. Single-bot configs
40
- // resolve to a 1-element array, so the loop below is the same code path.
41
- const botConfigs = resolveBotConfigs(config);
42
- const multi = botConfigs.length > 1;
43
- if (multi) {
44
- console.log(`[cc-channel-octo] Multi-bot mode: starting ${botConfigs.length} bots`);
45
- }
46
- // Each bot runs a fully independent stack (gateway + router + store + cwd
47
- // cleanup), isolated by its own dataDir/cwdBase. They share nothing stateful,
48
- // so per-user history and sandboxes never cross between bots.
49
- //
50
- // Two-phase startup so no WebSocket ACKs a message before its handler is
51
- // ready: startBot() registers over REST (gets botId) and installs the message
52
- // handler, but does NOT open the socket. We then cross-register sibling bot
53
- // ids, and only AFTER that connect every socket.
54
- //
55
- // startBot() acquires the gateway.lock, opens the SQLite store, and arms the
56
- // cwd-cleanup interval; on its own failure it releases those (see startBot's
57
- // try/catch), so a rejected bot leaves nothing behind. Settle all, then apply
58
- // the multi-bot resilience policy: skip the bots that failed, keep the ones
59
- // that came up, and only treat startup as fatal when NONE start. (Single-bot
60
- // mode has one entry, so "none started" == that bot failed → fatal, same as
61
- // the old fail-fast behavior.) This stops one bot's bad token / held lock from
62
- // taking the whole gateway and every sibling bot offline.
63
- const startResults = await Promise.allSettled(botConfigs.map((c) => startBot(c, multi)));
64
- const { stacks, failures } = partitionStartResults(startResults, botConfigs);
65
- for (const f of failures) {
66
- const reason = f.reason instanceof Error ? f.reason.message : String(f.reason);
67
- console.error(`[cc-channel-octo] bot "${f.id}" failed to start, skipping: ${reason}`);
68
- }
69
- if (stacks.length === 0) {
70
- // Nothing came up — surface the first failure and exit (process supervisor
71
- // / operator restarts once the underlying cause is fixed).
72
- throw failures[0]?.reason ?? new Error('no bots started');
58
+ // #157 hot-reload: a single long-lived BotManager owns the live bot set for
59
+ // the whole process lifetime. Bots are added/removed at runtime by a
60
+ // config.json watcher — provisioning a new bot no longer restarts the gateway.
61
+ // The manager always runs under multi-bot signal ownership (main() owns the
62
+ // shutdown signals; per-gateway handlers are disabled) so a bot added later is
63
+ // governed the same way as one present at boot.
64
+ const manager = new BotManager(async (configId) => {
65
+ // Re-read config from disk: a bot added after boot is not in the boot-time
66
+ // config object. loadConfig() here reflects the latest config.json the
67
+ // watcher reacted to. startBot does register + handler install but does NOT
68
+ // open the socket — BotManager.addBot opens it (via stack.connect) AFTER
69
+ // cross-registering the loop-guard, preserving the two-phase ordering.
70
+ const botConfig = resolveBotConfigById(loadConfig(), configId);
71
+ return startBot(botConfig, /* multi (main owns signals) */ true);
72
+ });
73
+ // Initial sync: bring up the configured bots as ONE two-phase batch (start all
74
+ // cross-register all → connect all), so no bot opens its socket before every
75
+ // sibling knows its robotUid (loop guard). resolveBotConfigs throws on an
76
+ // invalid config; let that fail boot.
77
+ const initialIds = resolveBotConfigs(config).map((c) => c.botId).filter((id) => !!id);
78
+ const { failed } = await manager.addBotsBatch(initialIds);
79
+ for (const f of failed) {
80
+ // Multi-bot resilience: one bot failing to start must not abort the others.
81
+ console.error(`[cc-channel-octo] bot "${f.configId}" failed to start, skipping: ${f.error instanceof Error ? f.error.message : String(f.error)}`);
73
82
  }
74
- // Multi-bot loop guard: make every router aware of ALL bot ids in this
75
- // process, so a mention-free group can't let one bot reply to another's
76
- // messages (knownBotUids looksLikeBot dropped). botIds are known after
77
- // register() (REST), before any socket is open.
78
- if (multi) {
79
- const allBotIds = stacks.map((s) => s.botId);
80
- for (const s of stacks) {
81
- for (const id of allBotIds) {
82
- if (id !== s.botId)
83
- s.router.registerKnownBot(id);
84
- }
85
- }
83
+ // Preserve pre-#157 semantics: an EMPTY config is a healthy idle state, but a
84
+ // NON-empty config where every bot failed is a boot failure (don't silently
85
+ // run idle while the operator believes bots are configured). The watcher still
86
+ // self-heals the empty/idle case on the next provision.
87
+ if (initialIds.length > 0 && manager.size() === 0) {
88
+ throw failed[0]?.error ?? new Error('no bots started');
86
89
  }
87
- // Handlers are wired and siblings registered — now open every socket. From
88
- // this point inbound messages are dispatched, never ACK'd-and-dropped.
89
- //
90
- // connect() is synchronous: it opens the WebSocket (fire-and-forget) and
91
- // starts services. The try/catch here isolates a *synchronous* connect
92
- // failure (e.g. an insecure ws:// URL rejected by isAllowedWsUrl, or a
93
- // createSocket error) to that one bot — the others keep running. A *later*
94
- // async WS drop is NOT surfaced here; it's handled by the gateway's own
95
- // reconnect loop and never kills the process. The real auth gate is the
96
- // register phase above (bad bot tokens fail there over REST and are isolated);
97
- // by here every bot holds a freshly minted im_token, so connect failures are
98
- // transport/transient, not per-bot auth.
99
- const connected = [];
100
- for (const s of stacks) {
101
- try {
102
- await s.connect();
103
- connected.push(s);
104
- }
105
- catch (err) {
106
- const reason = err instanceof Error ? err.message : String(err);
107
- console.error(`[cc-channel-octo] bot "${s.botId}" failed to connect, skipping: ${reason}`);
108
- await s.shutdown().catch(() => { });
109
- }
90
+ if (manager.size() === 0) {
91
+ console.log('[cc-channel-octo] idle (no bots configured) awaiting provision');
110
92
  }
111
- if (connected.length === 0) {
112
- throw new Error('no bots connected');
93
+ else {
94
+ console.log(`[cc-channel-octo] Ready — ${manager.size()} bot(s) running`);
113
95
  }
114
- // Wire a single process-wide shutdown that drains every LIVE bot, so N
115
- // gateways don't each call process.exit. The per-gateway signal handlers are
116
- // disabled in multi-bot mode (handleSignals=false); we own the signals here.
117
- if (multi) {
118
- const shutdownAll = async (signal) => {
119
- console.log(`[cc-channel-octo] Received ${signal}, shutting down ${connected.length} bots...`);
120
- await Promise.allSettled(connected.map((s) => s.shutdown()));
121
- process.exit(0);
96
+ // #157: watch config.json and reconcile the running set on change. loadDesired
97
+ // re-reads + re-validates inside the watcher; an invalid/half-written config
98
+ // keeps the current bots (see config-watcher). The directory is watched (not
99
+ // the file) so an atomic temp+rename swap doesn't drop the subscription.
100
+ const watcher = watchConfig({
101
+ configPath: configPathForWatch(),
102
+ manager,
103
+ loadDesired: () => resolveBotConfigs(loadConfig()).map((c) => c.botId).filter((id) => !!id),
104
+ log: (m) => console.log(`[cc-channel-octo] ${m}`),
105
+ });
106
+ // Single process-wide shutdown: stop the watcher, drain every live bot, exit.
107
+ // keepalive holds the event loop even when zero bots are running (idle state
108
+ // is now a steady runtime state, not just a boot branch).
109
+ await new Promise((resolve) => {
110
+ const keepalive = setInterval(() => { }, 60_000); // ref'd → keeps loop alive while idle
111
+ const bye = async (signal) => {
112
+ console.log(`[cc-channel-octo] Received ${signal}, shutting down ${manager.size()} bot(s)...`);
113
+ clearInterval(keepalive);
114
+ watcher.close();
115
+ await manager.shutdownAll();
116
+ resolve();
122
117
  };
123
- process.once('SIGINT', () => void shutdownAll('SIGINT'));
124
- process.once('SIGTERM', () => void shutdownAll('SIGTERM'));
125
- }
126
- console.log(`[cc-channel-octo] Ready — listening for messages (${connected.length} bot(s))`);
127
- }
128
- /**
129
- * Split the settled results of starting every bot into the stacks that came up
130
- * and the ones that failed. Pure (no I/O) so the multi-bot resilience policy —
131
- * skip failed bots, keep the rest, only fatal when none start — is unit
132
- * testable. A failed bot's own partial resources are released inside startBot's
133
- * failure path (see its try/catch); this function never touches a stack.
134
- */
135
- export function partitionStartResults(results, configs) {
136
- const stacks = [];
137
- const failures = [];
138
- results.forEach((r, i) => {
139
- if (r.status === 'fulfilled') {
140
- stacks.push(r.value);
141
- }
142
- else {
143
- failures.push({ id: configs[i]?.botId ?? `#${i}`, reason: r.reason });
144
- }
118
+ process.once('SIGINT', () => void bye('SIGINT'));
119
+ process.once('SIGTERM', () => void bye('SIGTERM'));
145
120
  });
146
- return { stacks, failures };
147
121
  }
148
122
  /**
149
123
  * Start one bot's full pipeline. `ownSignals` is true for the single-bot case
@@ -309,7 +283,7 @@ async function startBot(config, multi) {
309
283
  };
310
284
  // Single-bot: the gateway's own SIGINT/SIGTERM handler invokes this.
311
285
  gateway.setShutdownCallback(shutdown);
312
- return { botId: gateway.botId, router, connect, shutdown };
286
+ return { robotUid: gateway.botId, router, connect, shutdown };
313
287
  }
314
288
  catch (err) {
315
289
  // Release the durable resources acquired above so a failed bot doesn't leak