@mininglamp-oss/cc-channel-octo 1.0.1 → 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.
- package/CHANGELOG.md +45 -1
- package/README.md +2 -2
- package/dist/agent-bridge.d.ts +12 -0
- package/dist/agent-bridge.js +24 -19
- package/dist/agent-bridge.js.map +1 -1
- package/dist/bot-manager.d.ts +103 -0
- package/dist/bot-manager.js +184 -0
- package/dist/bot-manager.js.map +1 -0
- package/dist/cli.d.ts +109 -0
- package/dist/cli.js +467 -0
- package/dist/cli.js.map +1 -0
- package/dist/config-watcher.d.ts +48 -0
- package/dist/config-watcher.js +143 -0
- package/dist/config-watcher.js.map +1 -0
- package/dist/config.d.ts +16 -0
- package/dist/config.js +14 -0
- package/dist/config.js.map +1 -1
- package/dist/configure.d.ts +11 -0
- package/dist/configure.js +106 -0
- package/dist/configure.js.map +1 -0
- package/dist/gateway.d.ts +6 -1
- package/dist/gateway.js +5 -0
- package/dist/gateway.js.map +1 -1
- package/dist/group-context.d.ts +17 -0
- package/dist/group-context.js +70 -0
- package/dist/group-context.js.map +1 -1
- package/dist/index.d.ts +16 -1
- package/dist/index.js +241 -191
- package/dist/index.js.map +1 -1
- package/dist/mention-utils.d.ts +10 -1
- package/dist/mention-utils.js +16 -2
- package/dist/mention-utils.js.map +1 -1
- package/dist/session-router.d.ts +25 -0
- package/dist/session-router.js +72 -1
- package/dist/session-router.js.map +1 -1
- package/dist/stream-relay.d.ts +1 -1
- package/dist/stream-relay.js +2 -2
- package/dist/stream-relay.js.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,10 +4,53 @@ All notable changes to this project are documented here.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
-
While the major version is `0`, minor releases may carry breaking changes.
|
|
8
7
|
|
|
9
8
|
## [Unreleased]
|
|
10
9
|
|
|
10
|
+
## [1.0.2] - 2026-06-24
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Process-supervisor CLI** — `cc-channel-octo start|stop|restart|status`
|
|
15
|
+
backgrounds the gateway, tracks a process-wide PID file, and stops it
|
|
16
|
+
gracefully (SIGTERM → SIGKILL on timeout). The `bin` now points at the
|
|
17
|
+
supervisor (`dist/cli.js`). Backward compatible: a bare `cc-channel-octo`
|
|
18
|
+
(or `npx @mininglamp-oss/cc-channel-octo`) still starts the gateway in the
|
|
19
|
+
foreground, same as before.
|
|
20
|
+
- **`version` / `--version` / `-v`** — prints the package version (read at
|
|
21
|
+
runtime from `package.json`), and the `--help` banner now carries the version
|
|
22
|
+
on its first line.
|
|
23
|
+
- **`configure` subcommand** — `cc-channel-octo configure --gateway-url <url>
|
|
24
|
+
[--model <id>] [--api-url <url>]` writes the LLM gateway URL, optional model
|
|
25
|
+
id, and the Octo IM server URL (`apiUrl`) into the global config without
|
|
26
|
+
hand-editing JSON. The API key is read from the `CC_OCTO_CONFIGURE_API_KEY`
|
|
27
|
+
environment variable so it never appears in the process argument list. This is
|
|
28
|
+
what an automated installer invokes after a fresh global install to make the
|
|
29
|
+
gateway bootable.
|
|
30
|
+
- **`upgrade` subcommand** — `cc-channel-octo upgrade <version>` performs an
|
|
31
|
+
in-place global npm reinstall to the target version and restarts the gateway,
|
|
32
|
+
so an installed gateway can self-update without an external script.
|
|
33
|
+
- **Zero-bot idle startup** — the gateway now boots and stays up with no bound
|
|
34
|
+
bot (previously it required at least one bot to start). A bot bound later via
|
|
35
|
+
provisioning is picked up on the next restart, so a freshly installed gateway
|
|
36
|
+
can come online immediately and wait for its first bot.
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
|
|
40
|
+
- **Configurable gateway URL and model** — the LLM gateway endpoint and model id
|
|
41
|
+
are now read from config (settable via `configure`) instead of being fixed, so
|
|
42
|
+
one build can target different gateways/models per deployment.
|
|
43
|
+
|
|
44
|
+
### Fixed
|
|
45
|
+
|
|
46
|
+
- **Per-message dispatch timeout** — a hung session no longer blocks the bot
|
|
47
|
+
indefinitely; each message dispatch is now bounded by a timeout so a stuck turn
|
|
48
|
+
is released instead of wedging the session (#142).
|
|
49
|
+
- **Outbound `@uid` mention validation** — mentions emitted by the agent are now
|
|
50
|
+
validated against the live group's member set, so invalid/hallucinated `@uid`s
|
|
51
|
+
are dropped instead of being sent (#144, reworked in #145 to use the
|
|
52
|
+
authoritative member set).
|
|
53
|
+
|
|
11
54
|
## [1.0.1] - 2026-06-10
|
|
12
55
|
|
|
13
56
|
The first npm-installable release. No runtime behavior change — packaging only.
|
|
@@ -343,6 +386,7 @@ hardening across the SSRF, prompt-injection, and protocol-DoS surfaces.
|
|
|
343
386
|
Initial tagged baseline: text messaging, streaming output, SQLite session
|
|
344
387
|
persistence, rate limiting, and the core security model.
|
|
345
388
|
|
|
389
|
+
[1.0.2]: https://github.com/Mininglamp-OSS/cc-channel-octo/compare/v1.0.1...v1.0.2
|
|
346
390
|
[1.0.1]: https://github.com/Mininglamp-OSS/cc-channel-octo/compare/v1.0.0...v1.0.1
|
|
347
391
|
[1.0.0]: https://github.com/Mininglamp-OSS/cc-channel-octo/compare/v0.2.0...v1.0.0
|
|
348
392
|
[0.2.0]: https://github.com/Mininglamp-OSS/cc-channel-octo/compare/v0.1.0...v0.2.0
|
package/README.md
CHANGED
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
8
|
<a href="https://github.com/Mininglamp-OSS/cc-channel-octo/actions"><img src="https://github.com/Mininglamp-OSS/cc-channel-octo/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
|
9
|
-
<a href="https://www.npmjs.com/package/cc-channel-octo"><img src="https://img.shields.io/npm/v/cc-channel-octo" alt="npm version"></a>
|
|
9
|
+
<a href="https://www.npmjs.com/package/@mininglamp-oss/cc-channel-octo"><img src="https://img.shields.io/npm/v/@mininglamp-oss/cc-channel-octo" alt="npm version"></a>
|
|
10
10
|
<a href="./LICENSE"><img src="https://img.shields.io/badge/license-Apache--2.0-blue" alt="License"></a>
|
|
11
|
-
<img src="https://img.shields.io/node/v/cc-channel-octo" alt="Node.js version">
|
|
11
|
+
<img src="https://img.shields.io/node/v/@mininglamp-oss/cc-channel-octo" alt="Node.js version">
|
|
12
12
|
</p>
|
|
13
13
|
|
|
14
14
|
---
|
package/dist/agent-bridge.d.ts
CHANGED
|
@@ -11,6 +11,18 @@
|
|
|
11
11
|
import type { McpServerConfig } from '@anthropic-ai/claude-agent-sdk';
|
|
12
12
|
import type { Config } from './config.js';
|
|
13
13
|
import type { SessionCtx } from './cwd-resolver.js';
|
|
14
|
+
/**
|
|
15
|
+
* Build the SDK subprocess env overlay. The SDK's `env` option REPLACES the
|
|
16
|
+
* subprocess environment, so we spread the base env first to keep
|
|
17
|
+
* PATH/HOME/etc., then layer operator-declared vars and gateway routing.
|
|
18
|
+
* Returns undefined when there is nothing to add (subprocess just inherits).
|
|
19
|
+
* Pure (base env injected) so the injection matrix is unit-testable.
|
|
20
|
+
*
|
|
21
|
+
* Param is narrowed to the fields it reads (not the whole Config['sdk']) so
|
|
22
|
+
* tests pass plain literals — repo lint is `--max-warnings 0` with
|
|
23
|
+
* no-explicit-any, so an `as any` cast in the test would fail the build.
|
|
24
|
+
*/
|
|
25
|
+
export declare function buildSdkEnv(sdk: Pick<Config['sdk'], 'apiKey' | 'anthropicBaseUrl' | 'env'>, baseEnv: NodeJS.ProcessEnv): NodeJS.ProcessEnv | undefined;
|
|
14
26
|
/**
|
|
15
27
|
* @deprecated Back-compat re-export. Section-marker escaping now lives in the
|
|
16
28
|
* shared `prompt-safety` module as `escapeSectionMarkers`. Kept so existing
|
package/dist/agent-bridge.js
CHANGED
|
@@ -12,6 +12,29 @@ import { query as sdkQuery } from '@anthropic-ai/claude-agent-sdk';
|
|
|
12
12
|
import { resolveSessionCwd } from './cwd-resolver.js';
|
|
13
13
|
import { linkSkillsIntoSandbox } from './skill-linker.js';
|
|
14
14
|
import { trustedText, escapeSectionMarkers, CURRENT_MESSAGE_ANCHOR } from './prompt-safety.js';
|
|
15
|
+
/**
|
|
16
|
+
* Build the SDK subprocess env overlay. The SDK's `env` option REPLACES the
|
|
17
|
+
* subprocess environment, so we spread the base env first to keep
|
|
18
|
+
* PATH/HOME/etc., then layer operator-declared vars and gateway routing.
|
|
19
|
+
* Returns undefined when there is nothing to add (subprocess just inherits).
|
|
20
|
+
* Pure (base env injected) so the injection matrix is unit-testable.
|
|
21
|
+
*
|
|
22
|
+
* Param is narrowed to the fields it reads (not the whole Config['sdk']) so
|
|
23
|
+
* tests pass plain literals — repo lint is `--max-warnings 0` with
|
|
24
|
+
* no-explicit-any, so an `as any` cast in the test would fail the build.
|
|
25
|
+
*/
|
|
26
|
+
export function buildSdkEnv(sdk, baseEnv) {
|
|
27
|
+
const extraEnv = sdk.env;
|
|
28
|
+
const hasExtraEnv = extraEnv !== undefined && Object.keys(extraEnv).length > 0;
|
|
29
|
+
if (!sdk.anthropicBaseUrl && !sdk.apiKey && !hasExtraEnv)
|
|
30
|
+
return undefined;
|
|
31
|
+
return {
|
|
32
|
+
...baseEnv,
|
|
33
|
+
...(hasExtraEnv ? extraEnv : {}),
|
|
34
|
+
...(sdk.anthropicBaseUrl ? { ANTHROPIC_BASE_URL: sdk.anthropicBaseUrl } : {}),
|
|
35
|
+
...(sdk.apiKey ? { ANTHROPIC_API_KEY: sdk.apiKey } : {}),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
15
38
|
const VALID_PERMISSION_MODES = new Set([
|
|
16
39
|
'default', 'acceptEdits', 'bypassPermissions', 'plan', 'dontAsk', 'auto',
|
|
17
40
|
]);
|
|
@@ -192,25 +215,7 @@ export async function* queryAgent(userMessage, config, sessionCtx, onToolUse, op
|
|
|
192
215
|
if (sources.length > 0)
|
|
193
216
|
linkSkillsIntoSandbox(cwd, sources);
|
|
194
217
|
}
|
|
195
|
-
|
|
196
|
-
// of mutating the gateway's global process.env. The SDK's `env` REPLACES the
|
|
197
|
-
// subprocess environment entirely, so spread process.env first to preserve
|
|
198
|
-
// PATH/HOME/ANTHROPIC_API_KEY. Scoping here means overrides never leak across
|
|
199
|
-
// requests. When nothing needs adding, omit `env` so the subprocess simply
|
|
200
|
-
// inherits process.env.
|
|
201
|
-
// - sdk.env (#107): operator-declared extra vars (e.g. OCTO_BOT_ID so a
|
|
202
|
-
// multi-bot deploy's octo-cli picks the right profile). Generic — cc does
|
|
203
|
-
// not interpret them.
|
|
204
|
-
// - ANTHROPIC_BASE_URL: model-gateway routing (set last so it wins).
|
|
205
|
-
const extraEnv = config.sdk.env;
|
|
206
|
-
const hasExtraEnv = extraEnv !== undefined && Object.keys(extraEnv).length > 0;
|
|
207
|
-
const env = config.sdk.anthropicBaseUrl || hasExtraEnv
|
|
208
|
-
? {
|
|
209
|
-
...process.env,
|
|
210
|
-
...(hasExtraEnv ? extraEnv : {}),
|
|
211
|
-
...(config.sdk.anthropicBaseUrl ? { ANTHROPIC_BASE_URL: config.sdk.anthropicBaseUrl } : {}),
|
|
212
|
-
}
|
|
213
|
-
: undefined;
|
|
218
|
+
const env = buildSdkEnv(config.sdk, process.env);
|
|
214
219
|
// Build + iterate the SDK stream for a given resume id and prompt. Extracted so
|
|
215
220
|
// a stale/expired `resume` (the SDK throws "No conversation found with session
|
|
216
221
|
// ID: …", verified by spike) can be recovered: clear the bad id and retry once
|
package/dist/agent-bridge.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-bridge.js","sourceRoot":"","sources":["../src/agent-bridge.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"agent-bridge.js","sourceRoot":"","sources":["../src/agent-bridge.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BotManager — runtime registry of live bots for hot-reload (#157).
|
|
3
|
+
*
|
|
4
|
+
* Owns a long-lived `Map<configId, BotStack>` so the gateway can add/remove a
|
|
5
|
+
* single bot at runtime (driven by a config.json watcher) instead of a full
|
|
6
|
+
* process restart. Holds the two-identity discipline (plan B2): the Map is
|
|
7
|
+
* keyed by configId (config.json `bots[].id`), while the SessionRouter loop
|
|
8
|
+
* guard is keyed by robotUid (Octo register id) — never mixed.
|
|
9
|
+
*
|
|
10
|
+
* Concurrency (plan C/C6): all mutations funnel through a single serial queue
|
|
11
|
+
* (`enqueue`), so a watcher burst can never run two add/remove against the same
|
|
12
|
+
* set concurrently. The diff is computed INSIDE the queued task against the
|
|
13
|
+
* freshly-loaded desired set, never precomputed in the watcher callback.
|
|
14
|
+
*/
|
|
15
|
+
import type { SessionRouter } from './session-router.js';
|
|
16
|
+
/**
|
|
17
|
+
* The subset of a started bot the manager needs to track and tear down. The
|
|
18
|
+
* manager keys bots by the configId passed to addBot (not a field here), and the
|
|
19
|
+
* loop-guard uses robotUid — so configId is intentionally NOT carried on the
|
|
20
|
+
* stack (it would be a write-only field; see plan B2 for the identity split).
|
|
21
|
+
*/
|
|
22
|
+
export interface ManagedBot {
|
|
23
|
+
robotUid: string;
|
|
24
|
+
router: SessionRouter;
|
|
25
|
+
connect: () => Promise<void>;
|
|
26
|
+
shutdown: () => Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Diff a desired config-id set against the currently-running config-id set.
|
|
30
|
+
*
|
|
31
|
+
* Returns the configIds to add (in desired, not running) and to remove (running,
|
|
32
|
+
* not in desired). Order within each list is the input order of `desired` /
|
|
33
|
+
* `running` respectively.
|
|
34
|
+
*/
|
|
35
|
+
export declare function diffBotSets(desired: readonly string[], running: readonly string[]): {
|
|
36
|
+
toAdd: string[];
|
|
37
|
+
toRemove: string[];
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Cross-register the loop-guard known-bot uids for a newly-added bot:
|
|
41
|
+
* - the new bot's router learns every existing bot's robotUid, AND
|
|
42
|
+
* - every existing bot's router learns the new bot's robotUid.
|
|
43
|
+
* Bidirectional — a one-way register would let one side treat the other's
|
|
44
|
+
* messages as user input in a mention-free group (plan B). Pure over the
|
|
45
|
+
* passed collections (no I/O), so it is unit-testable.
|
|
46
|
+
*/
|
|
47
|
+
export declare function crossRegisterOnAdd(newBot: ManagedBot, existing: readonly ManagedBot[]): void;
|
|
48
|
+
/** Symmetric teardown: every remaining bot's router forgets the removed bot. */
|
|
49
|
+
export declare function crossUnregisterOnRemove(removed: ManagedBot, remaining: readonly ManagedBot[]): void;
|
|
50
|
+
/** How a managed bot is brought up from a config entry (injected for tests). */
|
|
51
|
+
export type StartFn = (configId: string) => Promise<ManagedBot>;
|
|
52
|
+
/**
|
|
53
|
+
* Runtime registry + serial mutation queue for live bots.
|
|
54
|
+
*
|
|
55
|
+
* The manager never opens sockets itself — `StartFn` (wired to startBot in
|
|
56
|
+
* index.ts) does the register+handler work and returns a ManagedBot whose
|
|
57
|
+
* `connect()` opens the socket. addBot enforces the plan-B ordering:
|
|
58
|
+
* start (register+handler, no socket) → cross-register known bots → connect,
|
|
59
|
+
* with rollback if connect throws.
|
|
60
|
+
*/
|
|
61
|
+
export declare class BotManager {
|
|
62
|
+
private readonly start;
|
|
63
|
+
private readonly bots;
|
|
64
|
+
private queue;
|
|
65
|
+
private shuttingDown;
|
|
66
|
+
constructor(start: StartFn);
|
|
67
|
+
/** Snapshot of currently-running config keys (Map keys). */
|
|
68
|
+
runningKeys(): string[];
|
|
69
|
+
size(): number;
|
|
70
|
+
has(key: string): boolean;
|
|
71
|
+
/** Run a mutation on the serial queue so add/remove never interleave. */
|
|
72
|
+
private enqueue;
|
|
73
|
+
/**
|
|
74
|
+
* Add one bot by configId: start → cross-register → connect, rolling back all
|
|
75
|
+
* three on any failure so a half-added bot never lingers in the Map or in
|
|
76
|
+
* sibling routers' known-bot sets.
|
|
77
|
+
*/
|
|
78
|
+
addBot(configId: string): Promise<void>;
|
|
79
|
+
/** Remove one bot by configId: shutdown + symmetric unregister + drop. */
|
|
80
|
+
removeBot(configId: string): Promise<void>;
|
|
81
|
+
/**
|
|
82
|
+
* Bring up several bots as ONE two-phase batch: start every bot (register +
|
|
83
|
+
* handler, NO socket) → cross-register the whole set's loop-guard uids
|
|
84
|
+
* pairwise → connect them all. This preserves the "no bot opens its socket
|
|
85
|
+
* before every sibling knows its robotUid" invariant ACROSS the initial set —
|
|
86
|
+
* adding bots one-by-one via addBot would let the first bot connect before
|
|
87
|
+
* later bots exist, so it would never learn them. A bot that fails to start or
|
|
88
|
+
* connect is rolled back and skipped (resilience); the rest still come up.
|
|
89
|
+
* Returns the configIds that failed.
|
|
90
|
+
*/
|
|
91
|
+
addBotsBatch(configIds: readonly string[]): Promise<{
|
|
92
|
+
failed: {
|
|
93
|
+
configId: string;
|
|
94
|
+
error: unknown;
|
|
95
|
+
}[];
|
|
96
|
+
}>;
|
|
97
|
+
/**
|
|
98
|
+
* Shut down every running bot (process exit). Drains them concurrently — order
|
|
99
|
+
* doesn't matter at teardown — and clears the Map. Runs on the serial queue so
|
|
100
|
+
* it can't interleave with an in-flight add/remove.
|
|
101
|
+
*/
|
|
102
|
+
shutdownAll(): Promise<void>;
|
|
103
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Diff a desired config-id set against the currently-running config-id set.
|
|
3
|
+
*
|
|
4
|
+
* Returns the configIds to add (in desired, not running) and to remove (running,
|
|
5
|
+
* not in desired). Order within each list is the input order of `desired` /
|
|
6
|
+
* `running` respectively.
|
|
7
|
+
*/
|
|
8
|
+
export function diffBotSets(desired, running) {
|
|
9
|
+
const runningSet = new Set(running);
|
|
10
|
+
const desiredSet = new Set(desired);
|
|
11
|
+
const toAdd = desired.filter((id) => !runningSet.has(id));
|
|
12
|
+
const toRemove = running.filter((id) => !desiredSet.has(id));
|
|
13
|
+
return { toAdd, toRemove };
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Cross-register the loop-guard known-bot uids for a newly-added bot:
|
|
17
|
+
* - the new bot's router learns every existing bot's robotUid, AND
|
|
18
|
+
* - every existing bot's router learns the new bot's robotUid.
|
|
19
|
+
* Bidirectional — a one-way register would let one side treat the other's
|
|
20
|
+
* messages as user input in a mention-free group (plan B). Pure over the
|
|
21
|
+
* passed collections (no I/O), so it is unit-testable.
|
|
22
|
+
*/
|
|
23
|
+
export function crossRegisterOnAdd(newBot, existing) {
|
|
24
|
+
for (const e of existing) {
|
|
25
|
+
if (e.robotUid === newBot.robotUid)
|
|
26
|
+
continue;
|
|
27
|
+
newBot.router.registerKnownBot(e.robotUid);
|
|
28
|
+
e.router.registerKnownBot(newBot.robotUid);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/** Symmetric teardown: every remaining bot's router forgets the removed bot. */
|
|
32
|
+
export function crossUnregisterOnRemove(removed, remaining) {
|
|
33
|
+
for (const r of remaining) {
|
|
34
|
+
r.router.unregisterKnownBot(removed.robotUid);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Runtime registry + serial mutation queue for live bots.
|
|
39
|
+
*
|
|
40
|
+
* The manager never opens sockets itself — `StartFn` (wired to startBot in
|
|
41
|
+
* index.ts) does the register+handler work and returns a ManagedBot whose
|
|
42
|
+
* `connect()` opens the socket. addBot enforces the plan-B ordering:
|
|
43
|
+
* start (register+handler, no socket) → cross-register known bots → connect,
|
|
44
|
+
* with rollback if connect throws.
|
|
45
|
+
*/
|
|
46
|
+
export class BotManager {
|
|
47
|
+
start;
|
|
48
|
+
bots = new Map();
|
|
49
|
+
queue = Promise.resolve();
|
|
50
|
+
// Set by shutdownAll() so any apply task that was already past the watcher's
|
|
51
|
+
// `closed` guard can't re-add a bot after teardown has begun (plan E / N4
|
|
52
|
+
// shutdown race). Once true, addBot is a permanent no-op.
|
|
53
|
+
shuttingDown = false;
|
|
54
|
+
constructor(start) {
|
|
55
|
+
this.start = start;
|
|
56
|
+
}
|
|
57
|
+
/** Snapshot of currently-running config keys (Map keys). */
|
|
58
|
+
runningKeys() {
|
|
59
|
+
return [...this.bots.keys()];
|
|
60
|
+
}
|
|
61
|
+
size() {
|
|
62
|
+
return this.bots.size;
|
|
63
|
+
}
|
|
64
|
+
has(key) {
|
|
65
|
+
return this.bots.has(key);
|
|
66
|
+
}
|
|
67
|
+
/** Run a mutation on the serial queue so add/remove never interleave. */
|
|
68
|
+
enqueue(task) {
|
|
69
|
+
const run = this.queue.then(task, task);
|
|
70
|
+
// Keep the chain alive even if a task rejects (so later tasks still run),
|
|
71
|
+
// but propagate the result/rejection to this call's awaiter.
|
|
72
|
+
this.queue = run.then(() => undefined, () => undefined);
|
|
73
|
+
return run;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Add one bot by configId: start → cross-register → connect, rolling back all
|
|
77
|
+
* three on any failure so a half-added bot never lingers in the Map or in
|
|
78
|
+
* sibling routers' known-bot sets.
|
|
79
|
+
*/
|
|
80
|
+
addBot(configId) {
|
|
81
|
+
return this.enqueue(async () => {
|
|
82
|
+
if (this.shuttingDown)
|
|
83
|
+
return; // teardown started — don't resurrect bots
|
|
84
|
+
if (this.bots.has(configId))
|
|
85
|
+
return; // idempotent — already running
|
|
86
|
+
const bot = await this.start(configId);
|
|
87
|
+
const existing = [...this.bots.values()];
|
|
88
|
+
crossRegisterOnAdd(bot, existing);
|
|
89
|
+
try {
|
|
90
|
+
await bot.connect();
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
// Roll back: undo the cross-registration, tear the bot down, leave the
|
|
94
|
+
// set exactly as it was before this addBot.
|
|
95
|
+
crossUnregisterOnRemove(bot, existing);
|
|
96
|
+
await bot.shutdown().catch(() => { });
|
|
97
|
+
throw err;
|
|
98
|
+
}
|
|
99
|
+
this.bots.set(configId, bot);
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
/** Remove one bot by configId: shutdown + symmetric unregister + drop. */
|
|
103
|
+
removeBot(configId) {
|
|
104
|
+
return this.enqueue(async () => {
|
|
105
|
+
const bot = this.bots.get(configId);
|
|
106
|
+
if (!bot)
|
|
107
|
+
return; // idempotent — already gone
|
|
108
|
+
this.bots.delete(configId);
|
|
109
|
+
const remaining = [...this.bots.values()];
|
|
110
|
+
// Symmetric inverse of addBot's ordering: close the socket FIRST, then let
|
|
111
|
+
// siblings forget this bot. If we unregistered before shutdown, the bot's
|
|
112
|
+
// in-flight handlers (still draining for up to the gateway drain timeout)
|
|
113
|
+
// could emit replies that siblings — having already dropped its robotUid —
|
|
114
|
+
// would treat as user input, reopening the very loop the guard prevents.
|
|
115
|
+
await bot.shutdown().catch(() => { });
|
|
116
|
+
crossUnregisterOnRemove(bot, remaining);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Bring up several bots as ONE two-phase batch: start every bot (register +
|
|
121
|
+
* handler, NO socket) → cross-register the whole set's loop-guard uids
|
|
122
|
+
* pairwise → connect them all. This preserves the "no bot opens its socket
|
|
123
|
+
* before every sibling knows its robotUid" invariant ACROSS the initial set —
|
|
124
|
+
* adding bots one-by-one via addBot would let the first bot connect before
|
|
125
|
+
* later bots exist, so it would never learn them. A bot that fails to start or
|
|
126
|
+
* connect is rolled back and skipped (resilience); the rest still come up.
|
|
127
|
+
* Returns the configIds that failed.
|
|
128
|
+
*/
|
|
129
|
+
addBotsBatch(configIds) {
|
|
130
|
+
return this.enqueue(async () => {
|
|
131
|
+
const failed = [];
|
|
132
|
+
if (this.shuttingDown) {
|
|
133
|
+
return { failed: configIds.map((configId) => ({ configId, error: new Error('shutting down') })) };
|
|
134
|
+
}
|
|
135
|
+
// Phase 1: start (register + handler, no socket) every not-yet-running bot.
|
|
136
|
+
const started = [];
|
|
137
|
+
for (const configId of configIds) {
|
|
138
|
+
if (this.bots.has(configId))
|
|
139
|
+
continue; // already running — skip
|
|
140
|
+
try {
|
|
141
|
+
started.push({ configId, bot: await this.start(configId) });
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
failed.push({ configId, error });
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// Phase 2: cross-register loop-guard uids across the existing set AND the
|
|
148
|
+
// whole freshly-started batch, BEFORE any socket opens.
|
|
149
|
+
const existing = [...this.bots.values()];
|
|
150
|
+
const batch = started.map((s) => s.bot);
|
|
151
|
+
for (const s of started) {
|
|
152
|
+
crossRegisterOnAdd(s.bot, [...existing, ...batch]);
|
|
153
|
+
}
|
|
154
|
+
// Phase 3: connect every started bot. A connect failure rolls back just
|
|
155
|
+
// that bot (unregister from everyone + shutdown) and is reported.
|
|
156
|
+
for (const s of started) {
|
|
157
|
+
try {
|
|
158
|
+
await s.bot.connect();
|
|
159
|
+
this.bots.set(s.configId, s.bot);
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
crossUnregisterOnRemove(s.bot, [...existing, ...batch.filter((b) => b !== s.bot)]);
|
|
163
|
+
await s.bot.shutdown().catch(() => { });
|
|
164
|
+
failed.push({ configId: s.configId, error });
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return { failed };
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Shut down every running bot (process exit). Drains them concurrently — order
|
|
172
|
+
* doesn't matter at teardown — and clears the Map. Runs on the serial queue so
|
|
173
|
+
* it can't interleave with an in-flight add/remove.
|
|
174
|
+
*/
|
|
175
|
+
shutdownAll() {
|
|
176
|
+
this.shuttingDown = true;
|
|
177
|
+
return this.enqueue(async () => {
|
|
178
|
+
const all = [...this.bots.values()];
|
|
179
|
+
this.bots.clear();
|
|
180
|
+
await Promise.allSettled(all.map((b) => b.shutdown()));
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=bot-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bot-manager.js","sourceRoot":"","sources":["../src/bot-manager.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* cc-channel-octo CLI — a process supervisor for the gateway.
|
|
4
|
+
*
|
|
5
|
+
* The gateway itself (`index.ts`) only runs in the foreground. This thin
|
|
6
|
+
* supervisor backgrounds it, tracks a single process-wide PID file, and stops
|
|
7
|
+
* it gracefully (SIGTERM, then SIGKILL on timeout). It does NOT replace the
|
|
8
|
+
* per-bot `gateway.lock` (which prevents two processes serving the same bot) —
|
|
9
|
+
* the PID file lives at the baseDir root, a sibling of every bot subtree.
|
|
10
|
+
*
|
|
11
|
+
* cc-channel-octo start [--foreground]
|
|
12
|
+
* cc-channel-octo stop [--timeout=<seconds>]
|
|
13
|
+
* cc-channel-octo restart
|
|
14
|
+
* cc-channel-octo status
|
|
15
|
+
*
|
|
16
|
+
* POSIX only (macOS/Linux): stop relies on SIGTERM/SIGKILL. On Windows, run the
|
|
17
|
+
* gateway under a service manager instead — Node has no SIGTERM semantics there.
|
|
18
|
+
*/
|
|
19
|
+
export interface SupervisorPaths {
|
|
20
|
+
baseDir: string;
|
|
21
|
+
pidFile: string;
|
|
22
|
+
logFile: string;
|
|
23
|
+
indexEntry: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Resolve the supervisor's fixed paths. baseDir defaults to the directory of
|
|
27
|
+
* the global config (`~/.cc-channel-octo`); tests inject a temp dir. indexEntry
|
|
28
|
+
* is the compiled gateway entrypoint, a sibling of this file.
|
|
29
|
+
*/
|
|
30
|
+
export declare function resolveSupervisorPaths(baseDir?: string): SupervisorPaths;
|
|
31
|
+
export interface ParsedArgs {
|
|
32
|
+
cmd: string;
|
|
33
|
+
foreground: boolean;
|
|
34
|
+
timeoutMs: number;
|
|
35
|
+
/** Positional version target for `upgrade` (e.g. `upgrade 1.2.3`); undefined → latest. */
|
|
36
|
+
version?: string;
|
|
37
|
+
/** `configure --gateway-url <url>`. */
|
|
38
|
+
gatewayUrl?: string;
|
|
39
|
+
/** `configure --api-key <key>`. */
|
|
40
|
+
apiKey?: string;
|
|
41
|
+
/** `configure --model <model>` — optional global sdk.model. */
|
|
42
|
+
model?: string;
|
|
43
|
+
/** `configure --api-url <url>` — Octo IM server url (cc top-level apiUrl). */
|
|
44
|
+
apiUrl?: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Extract a package version from raw package.json text. Returns 'unknown'
|
|
48
|
+
* rather than throwing if the text is malformed or has no non-empty string
|
|
49
|
+
* `version`. Pure (no I/O) so the fallback paths are unit-testable.
|
|
50
|
+
*/
|
|
51
|
+
export declare function parseVersion(raw: string): string;
|
|
52
|
+
/**
|
|
53
|
+
* The package version, read at runtime from package.json — which lives at the
|
|
54
|
+
* package root, one level up from this module in both src/ (src/cli.ts) and the
|
|
55
|
+
* compiled output (dist/cli.js). Returns 'unknown' if the file can't be read.
|
|
56
|
+
*/
|
|
57
|
+
export declare function readVersion(): string;
|
|
58
|
+
export declare function parseArgs(argv: string[]): ParsedArgs;
|
|
59
|
+
/**
|
|
60
|
+
* Liveness probe via signal 0. EPERM means the process exists but is owned by
|
|
61
|
+
* another user — still "alive" for our purposes; ESRCH means it's gone.
|
|
62
|
+
*/
|
|
63
|
+
export declare function isAlive(pid: number): boolean;
|
|
64
|
+
/** The gateway's PID plus the OS identity recorded when we started it. */
|
|
65
|
+
export interface PidRecord {
|
|
66
|
+
pid: number;
|
|
67
|
+
/** Owner identity (process start time); null for a legacy bare-PID file. */
|
|
68
|
+
id: string | null;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Probe for a process's OS identity, used to detect PID reuse. A PID file by
|
|
72
|
+
* itself is not enough: if the gateway crashes uncleanly and the OS later
|
|
73
|
+
* recycles its PID for an unrelated process, signaling that PID would hit the
|
|
74
|
+
* wrong process. The start time is a cheap, POSIX-portable discriminator — a
|
|
75
|
+
* recycled PID belongs to a process that started later, so its start time
|
|
76
|
+
* differs. Injectable so tests can simulate reuse without real processes.
|
|
77
|
+
*
|
|
78
|
+
* Returns null when the identity can't be read (process gone, `ps` absent);
|
|
79
|
+
* callers treat null as "unverifiable" and fall back to liveness only.
|
|
80
|
+
*/
|
|
81
|
+
export type ProcIdentityFn = (pid: number) => string | null;
|
|
82
|
+
export declare function procStartTime(pid: number): string | null;
|
|
83
|
+
/**
|
|
84
|
+
* Read the PID record. Accepts the current JSON form (`{"pid":N,"id":"…"}`) and
|
|
85
|
+
* the legacy bare-integer form (written by pre-ownership-token releases), which
|
|
86
|
+
* yields a null identity so callers fall back to liveness-only handling.
|
|
87
|
+
*/
|
|
88
|
+
export declare function readPidRecord(pidFile: string): PidRecord | null;
|
|
89
|
+
/** The numeric PID from the file (no ownership check), or null. */
|
|
90
|
+
export declare function readPid(pidFile: string): number | null;
|
|
91
|
+
export declare function writePid(pidFile: string, pid: number, id: string | null): void;
|
|
92
|
+
export declare function removePid(pidFile: string): void;
|
|
93
|
+
/**
|
|
94
|
+
* PID of the running gateway we own, or null. A process counts as ours only if
|
|
95
|
+
* it is alive AND its current OS identity matches the one recorded at start —
|
|
96
|
+
* the guard that stops `stop`/`restart`/`upgrade` from signaling a process that
|
|
97
|
+
* merely inherited the PID after an unclean crash + reuse. Identity mismatch
|
|
98
|
+
* means the file is stale (PID recycled), so it is removed. Legacy files (no
|
|
99
|
+
* recorded identity) and unreadable live identities fall back to liveness only,
|
|
100
|
+
* matching the pre-ownership-token behavior rather than refusing to stop.
|
|
101
|
+
*/
|
|
102
|
+
export declare function resolveOwnedPid(paths: SupervisorPaths, procId: ProcIdentityFn): number | null;
|
|
103
|
+
/**
|
|
104
|
+
* Build the `npm install -g <pkg>@<version>` argument vector. A blank/omitted
|
|
105
|
+
* version installs `@latest`. Pure (no I/O) so the injection guard is unit
|
|
106
|
+
* testable. Throws on an unsafe version string.
|
|
107
|
+
*/
|
|
108
|
+
export declare function buildUpgradeArgs(version?: string): string[];
|
|
109
|
+
export declare function run(argv: string[], baseDir?: string, procId?: ProcIdentityFn): Promise<number>;
|