@mininglamp-oss/cc-channel-octo 1.0.1-dev.0ac574a

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 (93) hide show
  1. package/CHANGELOG.md +361 -0
  2. package/LICENSE +191 -0
  3. package/README.md +577 -0
  4. package/config.bot.example.json +15 -0
  5. package/config.example.json +33 -0
  6. package/dist/agent-bridge.d.ts +91 -0
  7. package/dist/agent-bridge.js +397 -0
  8. package/dist/agent-bridge.js.map +1 -0
  9. package/dist/cli.d.ts +109 -0
  10. package/dist/cli.js +467 -0
  11. package/dist/cli.js.map +1 -0
  12. package/dist/commands.d.ts +57 -0
  13. package/dist/commands.js +121 -0
  14. package/dist/commands.js.map +1 -0
  15. package/dist/config.d.ts +294 -0
  16. package/dist/config.js +344 -0
  17. package/dist/config.js.map +1 -0
  18. package/dist/configure.d.ts +11 -0
  19. package/dist/configure.js +106 -0
  20. package/dist/configure.js.map +1 -0
  21. package/dist/cron-evaluator.d.ts +53 -0
  22. package/dist/cron-evaluator.js +191 -0
  23. package/dist/cron-evaluator.js.map +1 -0
  24. package/dist/cron-fire-marker.d.ts +24 -0
  25. package/dist/cron-fire-marker.js +25 -0
  26. package/dist/cron-fire-marker.js.map +1 -0
  27. package/dist/cron-scheduler.d.ts +46 -0
  28. package/dist/cron-scheduler.js +114 -0
  29. package/dist/cron-scheduler.js.map +1 -0
  30. package/dist/cron-store.d.ts +62 -0
  31. package/dist/cron-store.js +63 -0
  32. package/dist/cron-store.js.map +1 -0
  33. package/dist/cron-tool.d.ts +44 -0
  34. package/dist/cron-tool.js +151 -0
  35. package/dist/cron-tool.js.map +1 -0
  36. package/dist/cwd-resolver.d.ts +72 -0
  37. package/dist/cwd-resolver.js +166 -0
  38. package/dist/cwd-resolver.js.map +1 -0
  39. package/dist/db-adapter.d.ts +21 -0
  40. package/dist/db-adapter.js +64 -0
  41. package/dist/db-adapter.js.map +1 -0
  42. package/dist/file-inline-wrap.d.ts +94 -0
  43. package/dist/file-inline-wrap.js +243 -0
  44. package/dist/file-inline-wrap.js.map +1 -0
  45. package/dist/gateway.d.ts +105 -0
  46. package/dist/gateway.js +425 -0
  47. package/dist/gateway.js.map +1 -0
  48. package/dist/group-config.d.ts +41 -0
  49. package/dist/group-config.js +104 -0
  50. package/dist/group-config.js.map +1 -0
  51. package/dist/group-context.d.ts +81 -0
  52. package/dist/group-context.js +466 -0
  53. package/dist/group-context.js.map +1 -0
  54. package/dist/inbound.d.ts +136 -0
  55. package/dist/inbound.js +667 -0
  56. package/dist/inbound.js.map +1 -0
  57. package/dist/index.d.ts +65 -0
  58. package/dist/index.js +1026 -0
  59. package/dist/index.js.map +1 -0
  60. package/dist/media-inbound.d.ts +38 -0
  61. package/dist/media-inbound.js +131 -0
  62. package/dist/media-inbound.js.map +1 -0
  63. package/dist/mention-utils.d.ts +108 -0
  64. package/dist/mention-utils.js +199 -0
  65. package/dist/mention-utils.js.map +1 -0
  66. package/dist/octo/api.d.ts +148 -0
  67. package/dist/octo/api.js +320 -0
  68. package/dist/octo/api.js.map +1 -0
  69. package/dist/octo/socket.d.ts +102 -0
  70. package/dist/octo/socket.js +793 -0
  71. package/dist/octo/socket.js.map +1 -0
  72. package/dist/octo/types.d.ts +126 -0
  73. package/dist/octo/types.js +35 -0
  74. package/dist/octo/types.js.map +1 -0
  75. package/dist/prompt-safety.d.ts +78 -0
  76. package/dist/prompt-safety.js +148 -0
  77. package/dist/prompt-safety.js.map +1 -0
  78. package/dist/session-router.d.ts +144 -0
  79. package/dist/session-router.js +490 -0
  80. package/dist/session-router.js.map +1 -0
  81. package/dist/session-store.d.ts +89 -0
  82. package/dist/session-store.js +297 -0
  83. package/dist/session-store.js.map +1 -0
  84. package/dist/skill-linker.d.ts +31 -0
  85. package/dist/skill-linker.js +160 -0
  86. package/dist/skill-linker.js.map +1 -0
  87. package/dist/stream-relay.d.ts +42 -0
  88. package/dist/stream-relay.js +243 -0
  89. package/dist/stream-relay.js.map +1 -0
  90. package/dist/url-policy.d.ts +103 -0
  91. package/dist/url-policy.js +290 -0
  92. package/dist/url-policy.js.map +1 -0
  93. package/package.json +79 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.js","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,294 @@
1
+ /**
2
+ * Configuration loading.
3
+ *
4
+ * Two-layer, bot-first model:
5
+ * - GLOBAL `~/.cc-channel-octo/config.json` — shared defaults + a `bots` list.
6
+ * Never holds a botToken.
7
+ * - PER-BOT `~/.cc-channel-octo/<id>/config.json` — that bot's botToken + any
8
+ * overrides. Each bot is a self-contained subtree:
9
+ * <baseDir>/<id>/{config.json, SOUL.md, data/, workspace/, memory/}
10
+ * - `baseDir` is the directory containing the global config.json. Per-bot dirs
11
+ * are DERIVED from `<baseDir>/<id>/…` (not separately configurable) so a bot
12
+ * can never point its data outside its own subtree.
13
+ *
14
+ * env overrides still apply to the shared/global layer.
15
+ */
16
+ /**
17
+ * Default global config path: `~/.cc-channel-octo/config.json`. This is the
18
+ * single, fixed production location (no env/CLI override). Tests pass an
19
+ * explicit path, which also sets `baseDir` to that file's directory.
20
+ */
21
+ export declare const DEFAULT_CONFIG_PATH: string;
22
+ /**
23
+ * Q2: Wildcard form of `allowedTools` — `"*"` means "allow every tool the SDK
24
+ * exposes". Otherwise must be an explicit string array (whitelist mode).
25
+ */
26
+ export type AllowedTools = string[] | '*';
27
+ export interface Config {
28
+ botToken: string;
29
+ apiUrl: string;
30
+ /**
31
+ * Base directory containing the global config.json. Every bot's subtree lives
32
+ * at `<baseDir>/<botId>/…`. Defaults to `~/.cc-channel-octo` (the dir of
33
+ * DEFAULT_CONFIG_PATH); when an explicit config path is passed, it is that
34
+ * file's directory.
35
+ */
36
+ baseDir: string;
37
+ /**
38
+ * DERIVED (not user-configurable): per-session cwd sandbox base for THIS bot,
39
+ * `<baseDir>/<botId>/workspace`. Each (DM peer | group channel) gets its own
40
+ * hashed subdir under it via `cwd-resolver.resolveSessionCwd()`. Populated by
41
+ * `resolveBotConfigs()`.
42
+ */
43
+ cwdBase?: string;
44
+ /**
45
+ * @deprecated Alias of `cwdBase`, kept in sync so hand-built Config objects
46
+ * (tests, legacy consumers reading `config.cwd`) still compile.
47
+ */
48
+ cwd: string;
49
+ /**
50
+ * DERIVED (not user-configurable): SQLite/data dir for THIS bot,
51
+ * `<baseDir>/<botId>/data`. Populated by `resolveBotConfigs()`.
52
+ */
53
+ dataDir: string;
54
+ /**
55
+ * DERIVED (not user-configurable): SDK auto-memory base for THIS bot,
56
+ * `<baseDir>/<botId>/memory`. Each session gets a hashed subdir under it (same
57
+ * partitioning as the cwd sandbox: group=shared per channel, DM=private per
58
+ * peer). Separate from the cwd sandbox so the 7-day cwd TTL never reclaims
59
+ * memory. Populated by `resolveBotConfigs()`.
60
+ */
61
+ memoryBase?: string;
62
+ /**
63
+ * DERIVED (not user-configurable): per-bot skills directory,
64
+ * `<baseDir>/<botId>/skills`. Each immediate subdir is a Claude skill
65
+ * (`SKILL.md` + optional `references/`, `scripts/`). Symlinked into each
66
+ * session sandbox's `.claude/skills/` so the SDK discovers it (requires
67
+ * `sdk.settingSources` to include `project`). Per-bot skills override
68
+ * same-named global skills. Populated by `resolveBotConfigs()`.
69
+ */
70
+ skillsDir?: string;
71
+ /**
72
+ * DERIVED (not user-configurable): install-wide skills directory shared by all
73
+ * bots, `<baseDir>/skills`. Loaded for every bot (lower precedence than the
74
+ * per-bot `skillsDir`). Populated by `resolveBotConfigs()`.
75
+ */
76
+ globalSkillsDir?: string;
77
+ /**
78
+ * v1.0: directory of per-group instruction files (`<groupId>.md`). When set,
79
+ * a matching file's contents are injected into the system prompt as trusted
80
+ * custom instructions for that group. Operator-controlled — must NOT be the
81
+ * per-session cwd sandbox (which the agent can write). Unset = feature off.
82
+ */
83
+ groupConfigDir?: string;
84
+ sdk: {
85
+ model?: string;
86
+ /**
87
+ * Q2: `"*"` allows every tool the SDK exposes; otherwise an explicit
88
+ * whitelist. Default is `"*"` because we already control surface area via
89
+ * `permissionMode` and `cwdBase` isolation — the old hard-coded 8-tool
90
+ * list was redundant lockdown and broke operators who needed SDK-internal
91
+ * tools like `TodoWrite`/`Task`.
92
+ */
93
+ allowedTools: AllowedTools;
94
+ permissionMode: string;
95
+ maxTurns?: number;
96
+ systemPrompt?: string;
97
+ /**
98
+ * Which filesystem settings sources the SDK loads (`user`/`project`/`local`).
99
+ * Default is `['project']` so the SDK discovers skills symlinked into the
100
+ * session sandbox's `.claude/skills/` (#100 — generic external tooling).
101
+ *
102
+ * Memory isolation is preserved INDEPENDENTLY of this: the auto-memory
103
+ * directory is pinned via inline `settings.autoMemoryDirectory` (the SDK's
104
+ * `flagSettings` tier), which takes precedence over any `projectSettings`
105
+ * value — and the SDK explicitly ignores `autoMemoryDirectory` coming from a
106
+ * checked-in `projectSettings` for security. So `['project']` lets the bot
107
+ * read the sandbox `.claude/` (skills, and any CLAUDE.md/settings.json the
108
+ * agent itself wrote — acceptable, it's the agent's own workspace) WITHOUT
109
+ * the memory leaking into the host `~/.claude` (verified empirically).
110
+ *
111
+ * `'user'` would additionally load the operator's real `~/.claude` config —
112
+ * opt into that deliberately only. Note: this controls what is LOADED (read),
113
+ * not tool-write scope (governed by `permissionMode`/`allowedTools`).
114
+ */
115
+ settingSources: string[];
116
+ /**
117
+ * v0.3: when true, the bot sends brief "🔧 Running <tool>…" progress
118
+ * messages as the agent invokes tools, so users see activity during long
119
+ * tool-heavy turns. Default false — it adds extra chat messages, so it is
120
+ * opt-in. Env: `CC_OCTO_SDK_TOOL_PROGRESS=true`.
121
+ */
122
+ toolProgress?: boolean;
123
+ /**
124
+ * Q1: Override the upstream Claude API endpoint (e.g. self-hosted gateway).
125
+ * Forwarded to the SDK subprocess via the standard `ANTHROPIC_BASE_URL`
126
+ * environment variable.
127
+ */
128
+ anthropicBaseUrl?: string;
129
+ /**
130
+ * Q1b: Anthropic API key for the upstream gateway. When set, forwarded to
131
+ * the SDK subprocess via the standard `ANTHROPIC_API_KEY` env var (same
132
+ * mechanism as `anthropicBaseUrl`). Written by the `configure` subcommand
133
+ * during daemon-driven one-click install; persisted in the global config.
134
+ */
135
+ apiKey?: string;
136
+ /**
137
+ * #107: extra environment variables injected verbatim into the agent's SDK
138
+ * subprocess (on top of the inherited process.env). Generic and declarative
139
+ * — cc knows nothing about what they mean. Use it to give a bot's skills the
140
+ * env their external CLIs need, e.g. `{ "OCTO_BOT_ID": "<robotId>" }` so a
141
+ * multi-bot deploy's `octo-cli` calls select the right stored profile.
142
+ * Per-bot (set in `<baseDir>/<id>/config.json`).
143
+ */
144
+ env?: Record<string, string>;
145
+ /**
146
+ * #110: which skills this bot enables, selecting a subset of the skills
147
+ * discovered in its session sandbox's `.claude/skills/` (the shared library
148
+ * `<baseDir>/skills` + per-bot `<baseDir>/<id>/skills`, symlinked in by the
149
+ * skill-linker). Names match each SKILL.md `name` / directory name.
150
+ * - omitted: SDK default (no explicit selection).
151
+ * - `'all'`: enable every discovered skill.
152
+ * - `string[]`: enable only the listed skills.
153
+ * This is the per-bot SELECTION layer over the centrally-maintained library:
154
+ * maintain skills once, each bot picks its own subset. Per-bot (set in
155
+ * `<baseDir>/<id>/config.json`).
156
+ */
157
+ skills?: string[] | 'all';
158
+ /**
159
+ * #115: when true, give the agent a `cron` tool set (cron_create / cron_list
160
+ * / cron_delete) to register per-bot scheduled tasks, persisted to
161
+ * `<baseDir>/<botId>/cron.json` and fired by the gateway scheduler through
162
+ * the normal message pipeline (bound to the creating session). Task creation
163
+ * is gated to the bot owner uid (registerBot.owner_uid). Default off. Per-bot.
164
+ */
165
+ cron?: boolean;
166
+ };
167
+ rateLimit: {
168
+ maxPerMinute: number;
169
+ };
170
+ context: {
171
+ maxContextChars: number;
172
+ historyLimit: number;
173
+ };
174
+ /** Maximum response length in chars before truncation (Q32). */
175
+ maxResponseChars: number;
176
+ /**
177
+ * Per-message dispatch timeout in ms (#141). Bounds the full handler
178
+ * pipeline (agent query + stream) under the per-session lock. If a turn
179
+ * hangs past this, the session lock is released (a hung turn would otherwise
180
+ * block every subsequent message on that session forever) and the user gets
181
+ * a one-shot apology. Does NOT cancel the in-flight turn — only unblocks the
182
+ * queue. Default 5 minutes.
183
+ */
184
+ dispatchTimeoutMs: number;
185
+ botBlocklist?: string[];
186
+ /**
187
+ * G14: Bots in this list are allowed to DM the bot even if their uid matches
188
+ * the `_bot` heuristic. Use this to whitelist trusted bots.
189
+ */
190
+ allowedBotUids?: string[];
191
+ /** Group IDs where the bot responds without being @mentioned (G12). */
192
+ mentionFreeGroups?: string[];
193
+ /**
194
+ * v0.3 multi-bot: optional per-bot overrides. When present and non-empty, the
195
+ * process runs ONE independent bot per entry, each with its own gateway,
196
+ * router, store, and (by default) data directory — so bots never share history
197
+ * or working dirs. Each entry inherits every top-level field and overrides the
198
+ * listed ones; `botToken` is required per entry. When absent, the process runs
199
+ * a single bot from the top-level fields exactly as before.
200
+ *
201
+ * Resolved into concrete per-bot Config objects by `resolveBotConfigs()`.
202
+ */
203
+ bots?: BotOverride[];
204
+ /**
205
+ * v0.3 multi-bot: stable identifier for THIS bot, used to namespace its data
206
+ * directory and logs when running multiple bots. Defaults to `default` for the
207
+ * single-bot case. Populated by `resolveBotConfigs()`.
208
+ */
209
+ botId?: string;
210
+ /**
211
+ * #86: media CDN host (no scheme), prefetched at startup from the upload-
212
+ * credentials STS response (`cdnBaseUrl`). Octo serves media from a separate
213
+ * CDN host than `apiUrl`; inbound media URLs on this host are allowed by
214
+ * buildMediaUrl. Runtime-populated (not from the config file); undefined until
215
+ * the prefetch succeeds, in which case only same-apiUrl-host media is allowed.
216
+ */
217
+ mediaCdnHost?: string;
218
+ }
219
+ /**
220
+ * One bot's entry. In the two-layer model the global config's `bots` array
221
+ * lists which bots to run (by `id`); each bot's real settings — including its
222
+ * required `botToken` — live in `<baseDir>/<id>/config.json`, which is merged
223
+ * OVER both the global shared fields and any inline fields here (per-dir wins).
224
+ *
225
+ * Per-bot directories are NOT configurable here: they are always derived as
226
+ * `<baseDir>/<id>/{data,workspace,memory}` so a bot cannot escape its subtree.
227
+ */
228
+ export interface BotOverride {
229
+ /**
230
+ * Stable id — also the bot's subtree name under `baseDir`. Required in the
231
+ * two-layer model (it selects `<baseDir>/<id>/config.json`). Must be a
232
+ * conservative slug: letters, digits, dot, underscore, hyphen — no path
233
+ * separators (it becomes a path segment).
234
+ */
235
+ id?: string;
236
+ /**
237
+ * Optional here — normally provided by the per-bot `<id>/config.json`. If set
238
+ * inline it is used unless the per-bot file overrides it.
239
+ */
240
+ botToken?: string;
241
+ apiUrl?: string;
242
+ model?: string;
243
+ systemPrompt?: string;
244
+ botBlocklist?: string[];
245
+ allowedBotUids?: string[];
246
+ mentionFreeGroups?: string[];
247
+ }
248
+ /**
249
+ * SSRF protection for apiUrl: implemented in url-policy.ts (isAllowedApiUrl).
250
+ * S6 fix: now rejects https://127.0.0.1 too — https doesn't make a private
251
+ * address safe (could be a self-signed mitmproxy).
252
+ */
253
+ export declare function loadConfig(configPath?: string): Config;
254
+ /**
255
+ * v0.3 multi-bot: expand a loaded Config into one concrete Config per bot.
256
+ *
257
+ * - Single-bot (no `bots`): returns `[config]` with `botId` defaulted to
258
+ * `default`, unchanged otherwise — fully backward compatible.
259
+ * - Multi-bot: returns one Config per `bots[]` entry. Each inherits the base
260
+ * config and applies its overrides. To guarantee bots never share history,
261
+ * cwd, or lock files, each bot's `dataDir` and `cwdBase` are namespaced by its
262
+ * id UNLESS the entry sets them explicitly.
263
+ *
264
+ * Throws on missing/duplicate bot tokens or duplicate ids (fail fast at boot).
265
+ */
266
+ /**
267
+ * Expand a loaded GLOBAL config into one concrete Config per bot.
268
+ *
269
+ * Two-layer, bot-first model:
270
+ * - Single-bot (no `bots`): one bot with id `default`. Its token/overrides come
271
+ * from the global config and/or `<baseDir>/default/config.json`.
272
+ * - Multi-bot: one Config per `bots[]` entry (selected by `id`). For each, the
273
+ * effective config is: global shared fields ⊕ inline `bots[]` fields ⊕
274
+ * `<baseDir>/<id>/config.json` (per-dir file wins).
275
+ *
276
+ * Every bot's directories are DERIVED (never configurable):
277
+ * data = <baseDir>/<id>/data
278
+ * workspace = <baseDir>/<id>/workspace (cwdBase)
279
+ * memory = <baseDir>/<id>/memory
280
+ * and its personality from `<baseDir>/<id>/SOUL.md` (overrides systemPrompt).
281
+ *
282
+ * Throws on missing/duplicate tokens, duplicate ids, invalid id slugs, or unsafe
283
+ * apiUrl (fail fast at boot).
284
+ */
285
+ export declare function resolveBotConfigs(config: Config): Config[];
286
+ /**
287
+ * v1.1: openclaw-style per-bot personality. Read `<botRoot>/SOUL.md` if it
288
+ * exists and return its trimmed contents as the bot's "soul" (voice/stance/
289
+ * boundaries), to be composed into the agent system prompt. Mirrors openclaw's
290
+ * SOUL.md: a file you edit, not a config string. When the file is absent or
291
+ * empty, returns undefined so the caller falls back to the `systemPrompt`
292
+ * config string. Best-effort — a read error never blocks startup.
293
+ */
294
+ export declare function loadSoul(botRoot: string): string | undefined;
package/dist/config.js ADDED
@@ -0,0 +1,344 @@
1
+ /**
2
+ * Configuration loading.
3
+ *
4
+ * Two-layer, bot-first model:
5
+ * - GLOBAL `~/.cc-channel-octo/config.json` — shared defaults + a `bots` list.
6
+ * Never holds a botToken.
7
+ * - PER-BOT `~/.cc-channel-octo/<id>/config.json` — that bot's botToken + any
8
+ * overrides. Each bot is a self-contained subtree:
9
+ * <baseDir>/<id>/{config.json, SOUL.md, data/, workspace/, memory/}
10
+ * - `baseDir` is the directory containing the global config.json. Per-bot dirs
11
+ * are DERIVED from `<baseDir>/<id>/…` (not separately configurable) so a bot
12
+ * can never point its data outside its own subtree.
13
+ *
14
+ * env overrides still apply to the shared/global layer.
15
+ */
16
+ import { readFileSync, existsSync, statSync, realpathSync } from 'node:fs';
17
+ import { resolve as resolvePath, sep, dirname, join as pathJoin } from 'node:path';
18
+ import { homedir } from 'node:os';
19
+ import { isAllowedApiUrl } from './url-policy.js';
20
+ /**
21
+ * Default global config path: `~/.cc-channel-octo/config.json`. This is the
22
+ * single, fixed production location (no env/CLI override). Tests pass an
23
+ * explicit path, which also sets `baseDir` to that file's directory.
24
+ */
25
+ export const DEFAULT_CONFIG_PATH = pathJoin(homedir(), '.cc-channel-octo', 'config.json');
26
+ function defaults() {
27
+ return {
28
+ botToken: '',
29
+ apiUrl: '',
30
+ // baseDir is set by loadConfig() from the config path's directory; the
31
+ // per-bot dirs below are DERIVED in resolveBotConfigs() as
32
+ // <baseDir>/<botId>/{workspace,data,memory}. Left empty here.
33
+ baseDir: '',
34
+ cwdBase: '',
35
+ cwd: '',
36
+ dataDir: '',
37
+ memoryBase: '',
38
+ sdk: {
39
+ // Q2: default to wildcard — operators tighten only when they need to.
40
+ allowedTools: '*',
41
+ permissionMode: 'bypassPermissions',
42
+ // #100: load project-scope settings so the SDK discovers skills symlinked
43
+ // into the session sandbox's .claude/skills/. Memory stays isolated via the
44
+ // inline settings.autoMemoryDirectory pin (flagSettings > projectSettings).
45
+ settingSources: ['project'],
46
+ },
47
+ rateLimit: {
48
+ maxPerMinute: 5,
49
+ },
50
+ context: {
51
+ maxContextChars: 6000,
52
+ historyLimit: 40,
53
+ },
54
+ maxResponseChars: 524_288, // 512 KB (Q32)
55
+ dispatchTimeoutMs: 300_000, // 5 min (#141)
56
+ };
57
+ }
58
+ function readConfigFile(configFilePath) {
59
+ if (!existsSync(configFilePath)) {
60
+ return {};
61
+ }
62
+ // Q12: Warn if config file is readable by group/others (contains botToken).
63
+ try {
64
+ const stat = statSync(configFilePath);
65
+ const mode = stat.mode & 0o777;
66
+ if (mode & 0o077) {
67
+ console.warn(`[cc-channel-octo] WARNING: ${configFilePath} has mode ${mode.toString(8)} — ` +
68
+ `secrets may be exposed to other users. Fix with: chmod 600 ${configFilePath}`);
69
+ }
70
+ }
71
+ catch {
72
+ // Best-effort check — don't block startup if stat fails.
73
+ }
74
+ const raw = readFileSync(configFilePath, 'utf-8');
75
+ let parsed;
76
+ try {
77
+ parsed = JSON.parse(raw);
78
+ }
79
+ catch (err) {
80
+ const msg = err instanceof Error ? err.message : String(err);
81
+ throw new Error(`Failed to parse config file ${configFilePath}: ${msg}`);
82
+ }
83
+ // Strip top-level keys starting with "_" (e.g. _comment).
84
+ const cleaned = {};
85
+ for (const [k, v] of Object.entries(parsed)) {
86
+ if (!k.startsWith('_'))
87
+ cleaned[k] = v;
88
+ }
89
+ return cleaned;
90
+ }
91
+ function mergeConfig(base, override) {
92
+ return {
93
+ botToken: override.botToken ?? base.botToken,
94
+ apiUrl: override.apiUrl ?? base.apiUrl,
95
+ // baseDir + derived dirs are filled by loadConfig()/resolveBotConfigs(),
96
+ // not by config-file merge.
97
+ baseDir: base.baseDir,
98
+ cwdBase: base.cwdBase,
99
+ cwd: base.cwd,
100
+ dataDir: base.dataDir,
101
+ memoryBase: base.memoryBase,
102
+ groupConfigDir: override.groupConfigDir ?? base.groupConfigDir,
103
+ sdk: {
104
+ ...base.sdk,
105
+ ...(override.sdk ?? {}),
106
+ },
107
+ rateLimit: {
108
+ ...base.rateLimit,
109
+ ...(override.rateLimit ?? {}),
110
+ },
111
+ context: {
112
+ ...base.context,
113
+ ...(override.context ?? {}),
114
+ },
115
+ maxResponseChars: override.maxResponseChars ?? base.maxResponseChars,
116
+ dispatchTimeoutMs: override.dispatchTimeoutMs ?? base.dispatchTimeoutMs,
117
+ botBlocklist: override.botBlocklist ?? base.botBlocklist,
118
+ allowedBotUids: override.allowedBotUids ?? base.allowedBotUids,
119
+ mentionFreeGroups: override.mentionFreeGroups ?? base.mentionFreeGroups,
120
+ bots: override.bots ?? base.bots,
121
+ };
122
+ }
123
+ /**
124
+ * SSRF protection for apiUrl: implemented in url-policy.ts (isAllowedApiUrl).
125
+ * S6 fix: now rejects https://127.0.0.1 too — https doesn't make a private
126
+ * address safe (could be a self-signed mitmproxy).
127
+ */
128
+ export function loadConfig(configPath) {
129
+ const path = configPath ?? DEFAULT_CONFIG_PATH;
130
+ // Migration aid: if the fixed global config is missing but a legacy
131
+ // ./config.json exists in the cwd, point the operator at the move rather than
132
+ // failing later with a cryptic "Missing required config: apiUrl".
133
+ if (configPath === undefined && !existsSync(path) && existsSync('./config.json')) {
134
+ throw new Error(`No config at ${path}, but ./config.json exists. The config location moved: ` +
135
+ `cc-channel-octo now loads ~/.cc-channel-octo/config.json (shared, no token) plus ` +
136
+ `~/.cc-channel-octo/<botId>/config.json (per-bot token). Move your settings there ` +
137
+ `(see config.example.json / config.bot.example.json).`);
138
+ }
139
+ const fileCfg = readConfigFile(path);
140
+ // Config comes ONLY from config.json (global + per-bot layers) — there is no
141
+ // environment-variable override path (#103). The sole exception that still
142
+ // *reads* the environment lives nowhere here: `sdk.anthropicBaseUrl` is set in
143
+ // config.json and forwarded to the SDK subprocess by agent-bridge.
144
+ const final = mergeConfig(defaults(), fileCfg);
145
+ // baseDir = the directory containing the global config.json. Every bot's
146
+ // subtree lives at <baseDir>/<botId>/…. resolveBotConfigs() derives the
147
+ // per-bot dirs from this.
148
+ final.baseDir = dirname(resolvePath(path));
149
+ // apiUrl is shared and required at the global layer (a per-bot config.json may
150
+ // still override it, re-checked per bot in resolveBotConfigs). botToken is NOT
151
+ // validated here — it lives in each bot's <id>/config.json.
152
+ if (!final.apiUrl) {
153
+ throw new Error('Missing required config: apiUrl (set CC_OCTO_API_URL or config.json)');
154
+ }
155
+ if (!isAllowedApiUrl(final.apiUrl)) {
156
+ throw new Error(`Unsafe apiUrl: ${final.apiUrl} — must be https:// or http://localhost/http://127.0.0.1 (SSRF protection)`);
157
+ }
158
+ // Q1: the gateway endpoint receives the Anthropic API key and all prompt /
159
+ // response content, so it gets the same SSRF policy as apiUrl.
160
+ if (final.sdk.anthropicBaseUrl && !isAllowedApiUrl(final.sdk.anthropicBaseUrl)) {
161
+ throw new Error(`Unsafe sdk.anthropicBaseUrl: ${final.sdk.anthropicBaseUrl} — must be https:// ` +
162
+ `or http://localhost/http://127.0.0.1 (SSRF protection)`);
163
+ }
164
+ return final;
165
+ }
166
+ /**
167
+ * Enforce that `groupConfigDir` (whose files are injected UNSANITIZED into the
168
+ * system prompt) is not the same as, nor nested under, the agent-writable
169
+ * `cwdBase`. Otherwise a user-driven agent could write its own future
170
+ * system-prompt instructions.
171
+ *
172
+ * Uses realpathSync.native for paths that exist (so symlinks can't dodge the
173
+ * boundary) and falls back to lexical resolve() for not-yet-created dirs.
174
+ */
175
+ function assertGroupConfigDirOutsideCwd(cfg) {
176
+ if (!cfg.groupConfigDir)
177
+ return;
178
+ const cwdBase = cfg.cwdBase ?? cfg.cwd;
179
+ const cwdBaseResolved = canonicalize(cwdBase);
180
+ const groupDirResolved = canonicalize(cfg.groupConfigDir);
181
+ if (groupDirResolved === cwdBaseResolved || isPathInside(groupDirResolved, cwdBaseResolved)) {
182
+ throw new Error(`Unsafe groupConfigDir: ${cfg.groupConfigDir} is the same as or nested under ` +
183
+ `cwdBase (${cwdBase}). It must be operator-controlled and outside the ` +
184
+ `agent-writable sandbox, since its files are injected into the system prompt.`);
185
+ }
186
+ }
187
+ /** Resolve to a real path when it exists (defeats symlink dodges), else lexical. */
188
+ function canonicalize(p) {
189
+ try {
190
+ return realpathSync.native(p);
191
+ }
192
+ catch {
193
+ return resolvePath(p);
194
+ }
195
+ }
196
+ /** True when `child` is strictly inside `parent` (both already resolved). */
197
+ function isPathInside(child, parent) {
198
+ const parentWithSep = parent.endsWith(sep) ? parent : parent + sep;
199
+ return child.startsWith(parentWithSep);
200
+ }
201
+ /**
202
+ * v0.3 multi-bot: expand a loaded Config into one concrete Config per bot.
203
+ *
204
+ * - Single-bot (no `bots`): returns `[config]` with `botId` defaulted to
205
+ * `default`, unchanged otherwise — fully backward compatible.
206
+ * - Multi-bot: returns one Config per `bots[]` entry. Each inherits the base
207
+ * config and applies its overrides. To guarantee bots never share history,
208
+ * cwd, or lock files, each bot's `dataDir` and `cwdBase` are namespaced by its
209
+ * id UNLESS the entry sets them explicitly.
210
+ *
211
+ * Throws on missing/duplicate bot tokens or duplicate ids (fail fast at boot).
212
+ */
213
+ /**
214
+ * Expand a loaded GLOBAL config into one concrete Config per bot.
215
+ *
216
+ * Two-layer, bot-first model:
217
+ * - Single-bot (no `bots`): one bot with id `default`. Its token/overrides come
218
+ * from the global config and/or `<baseDir>/default/config.json`.
219
+ * - Multi-bot: one Config per `bots[]` entry (selected by `id`). For each, the
220
+ * effective config is: global shared fields ⊕ inline `bots[]` fields ⊕
221
+ * `<baseDir>/<id>/config.json` (per-dir file wins).
222
+ *
223
+ * Every bot's directories are DERIVED (never configurable):
224
+ * data = <baseDir>/<id>/data
225
+ * workspace = <baseDir>/<id>/workspace (cwdBase)
226
+ * memory = <baseDir>/<id>/memory
227
+ * and its personality from `<baseDir>/<id>/SOUL.md` (overrides systemPrompt).
228
+ *
229
+ * Throws on missing/duplicate tokens, duplicate ids, invalid id slugs, or unsafe
230
+ * apiUrl (fail fast at boot).
231
+ */
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
+ }
245
+ // Single-bot: synthesize one entry with id "default".
246
+ const entries = config.bots && config.bots.length > 0
247
+ ? config.bots
248
+ : [{ id: 'default', botToken: config.botToken || undefined }];
249
+ const seenIds = new Set();
250
+ const seenTokens = new Set();
251
+ const resolvedBots = entries.map((bot, i) => {
252
+ const id = bot.id ?? `bot${i}`;
253
+ // The id becomes a path segment for the bot's subtree, so restrict it to a
254
+ // conservative slug — otherwise ids like "../ops" or "a/b" could escape or
255
+ // alias the intended directory, defeating isolation.
256
+ if (!/^[a-zA-Z0-9._-]+$/.test(id) || id === '.' || id === '..') {
257
+ throw new Error(`Bot "${id}": invalid id — use only letters, digits, dot, underscore, hyphen (no path separators)`);
258
+ }
259
+ if (seenIds.has(id)) {
260
+ throw new Error(`Duplicate bot id "${id}" — ids must be unique`);
261
+ }
262
+ seenIds.add(id);
263
+ // Derive the bot's self-contained subtree under baseDir.
264
+ const botRoot = pathJoin(config.baseDir, id);
265
+ const botDataDir = pathJoin(botRoot, 'data');
266
+ const botCwdBase = pathJoin(botRoot, 'workspace');
267
+ const botMemoryBase = pathJoin(botRoot, 'memory');
268
+ // #100: per-bot skills (<baseDir>/<id>/skills) + install-wide global skills
269
+ // (<baseDir>/skills). Symlinked into each session sandbox by skill-linker.
270
+ const botSkillsDir = pathJoin(botRoot, 'skills');
271
+ const globalSkillsDir = pathJoin(config.baseDir, 'skills');
272
+ // Per-bot config.json (in the bot's own subtree) is the highest-priority
273
+ // layer: global shared ⊕ inline bots[] entry ⊕ <baseDir>/<id>/config.json.
274
+ const perBotFile = readConfigFile(pathJoin(botRoot, 'config.json'));
275
+ const botToken = perBotFile.botToken ?? bot.botToken ?? '';
276
+ if (!botToken) {
277
+ throw new Error(`Bot "${id}": missing botToken — set it in ${pathJoin(botRoot, 'config.json')}`);
278
+ }
279
+ if (seenTokens.has(botToken)) {
280
+ throw new Error(`Duplicate botToken across bots — each bot needs a distinct token`);
281
+ }
282
+ seenTokens.add(botToken);
283
+ // openclaw-style SOUL.md in the bot's subtree overrides systemPrompt (which
284
+ // may come from the per-bot file, the inline entry, or the shared config).
285
+ const botSoul = loadSoul(botRoot);
286
+ const sharedSystemPrompt = config.sdk.systemPrompt;
287
+ const botSystemPrompt = botSoul ?? perBotFile.sdk?.systemPrompt ?? bot.systemPrompt ?? sharedSystemPrompt;
288
+ const apiUrl = perBotFile.apiUrl ?? bot.apiUrl ?? config.apiUrl;
289
+ const model = perBotFile.sdk?.model ?? bot.model ?? config.sdk.model;
290
+ const resolved = {
291
+ ...config,
292
+ bots: undefined, // a per-bot config is single-bot
293
+ botId: id,
294
+ botToken,
295
+ apiUrl,
296
+ baseDir: config.baseDir,
297
+ dataDir: botDataDir,
298
+ cwdBase: botCwdBase,
299
+ cwd: botCwdBase,
300
+ memoryBase: botMemoryBase,
301
+ skillsDir: botSkillsDir,
302
+ globalSkillsDir,
303
+ botBlocklist: perBotFile.botBlocklist ?? bot.botBlocklist ?? config.botBlocklist,
304
+ allowedBotUids: perBotFile.allowedBotUids ?? bot.allowedBotUids ?? config.allowedBotUids,
305
+ mentionFreeGroups: perBotFile.mentionFreeGroups ?? bot.mentionFreeGroups ?? config.mentionFreeGroups,
306
+ groupConfigDir: perBotFile.groupConfigDir ?? config.groupConfigDir,
307
+ sdk: {
308
+ ...config.sdk,
309
+ ...(perBotFile.sdk ?? {}),
310
+ ...(model !== undefined ? { model } : {}),
311
+ ...(botSystemPrompt !== undefined ? { systemPrompt: botSystemPrompt } : {}),
312
+ },
313
+ };
314
+ if (!isAllowedApiUrl(resolved.apiUrl)) {
315
+ throw new Error(`Bot "${id}": unsafe apiUrl ${resolved.apiUrl} (SSRF protection)`);
316
+ }
317
+ // GROUP.md trust boundary: groupConfigDir must not be the bot's writable cwd.
318
+ assertGroupConfigDirOutsideCwd(resolved);
319
+ return resolved;
320
+ });
321
+ return resolvedBots;
322
+ }
323
+ /**
324
+ * v1.1: openclaw-style per-bot personality. Read `<botRoot>/SOUL.md` if it
325
+ * exists and return its trimmed contents as the bot's "soul" (voice/stance/
326
+ * boundaries), to be composed into the agent system prompt. Mirrors openclaw's
327
+ * SOUL.md: a file you edit, not a config string. When the file is absent or
328
+ * empty, returns undefined so the caller falls back to the `systemPrompt`
329
+ * config string. Best-effort — a read error never blocks startup.
330
+ */
331
+ export function loadSoul(botRoot) {
332
+ const path = pathJoin(botRoot, 'SOUL.md');
333
+ if (!existsSync(path))
334
+ return undefined;
335
+ try {
336
+ const content = readFileSync(path, 'utf-8').trim();
337
+ return content.length > 0 ? content : undefined;
338
+ }
339
+ catch (err) {
340
+ console.warn(`[cc-channel-octo] WARNING: failed to read ${path}: ${err instanceof Error ? err.message : String(err)}`);
341
+ return undefined;
342
+ }
343
+ }
344
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}