discord-agent-bridge 0.1.0

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 (201) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +195 -0
  3. package/dist/app.js +210 -0
  4. package/dist/app.js.map +1 -0
  5. package/dist/app.test.js +117 -0
  6. package/dist/app.test.js.map +1 -0
  7. package/dist/cli.js +63 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/cli.test.js +91 -0
  10. package/dist/cli.test.js.map +1 -0
  11. package/dist/core/auditLog.js +65 -0
  12. package/dist/core/auditLog.js.map +1 -0
  13. package/dist/core/auditLog.test.js +124 -0
  14. package/dist/core/auditLog.test.js.map +1 -0
  15. package/dist/core/auth.js +106 -0
  16. package/dist/core/auth.js.map +1 -0
  17. package/dist/core/auth.test.js +216 -0
  18. package/dist/core/auth.test.js.map +1 -0
  19. package/dist/core/channelRegistry.js +119 -0
  20. package/dist/core/channelRegistry.js.map +1 -0
  21. package/dist/core/channelRegistry.test.js +114 -0
  22. package/dist/core/channelRegistry.test.js.map +1 -0
  23. package/dist/core/commandPolicy.js +6 -0
  24. package/dist/core/commandPolicy.js.map +1 -0
  25. package/dist/core/commandRouter.js +9 -0
  26. package/dist/core/commandRouter.js.map +1 -0
  27. package/dist/core/config.js +141 -0
  28. package/dist/core/config.js.map +1 -0
  29. package/dist/core/config.test.js +149 -0
  30. package/dist/core/config.test.js.map +1 -0
  31. package/dist/core/configResolver.js +90 -0
  32. package/dist/core/configResolver.js.map +1 -0
  33. package/dist/core/configResolver.test.js +138 -0
  34. package/dist/core/configResolver.test.js.map +1 -0
  35. package/dist/core/configSchema.js +164 -0
  36. package/dist/core/configSchema.js.map +1 -0
  37. package/dist/core/contracts.js +5 -0
  38. package/dist/core/contracts.js.map +1 -0
  39. package/dist/core/eventBus.js +42 -0
  40. package/dist/core/eventBus.js.map +1 -0
  41. package/dist/core/eventBus.test.js +72 -0
  42. package/dist/core/eventBus.test.js.map +1 -0
  43. package/dist/core/hookBridge.js +11 -0
  44. package/dist/core/hookBridge.js.map +1 -0
  45. package/dist/core/logger.js +116 -0
  46. package/dist/core/logger.js.map +1 -0
  47. package/dist/core/logger.test.js +88 -0
  48. package/dist/core/logger.test.js.map +1 -0
  49. package/dist/core/modeRegistry.js +33 -0
  50. package/dist/core/modeRegistry.js.map +1 -0
  51. package/dist/core/permissionResolver.js +43 -0
  52. package/dist/core/permissionResolver.js.map +1 -0
  53. package/dist/core/permissionResolver.test.js +136 -0
  54. package/dist/core/permissionResolver.test.js.map +1 -0
  55. package/dist/core/providerCatalog.js +240 -0
  56. package/dist/core/providerCatalog.js.map +1 -0
  57. package/dist/core/providerCatalog.test.js +230 -0
  58. package/dist/core/providerCatalog.test.js.map +1 -0
  59. package/dist/core/sessionOrchestrator.js +463 -0
  60. package/dist/core/sessionOrchestrator.js.map +1 -0
  61. package/dist/core/sessionOrchestrator.test.js +344 -0
  62. package/dist/core/sessionOrchestrator.test.js.map +1 -0
  63. package/dist/core/state/schema.js +49 -0
  64. package/dist/core/state/schema.js.map +1 -0
  65. package/dist/core/state/store.js +86 -0
  66. package/dist/core/state/store.js.map +1 -0
  67. package/dist/core/state/store.test.js +99 -0
  68. package/dist/core/state/store.test.js.map +1 -0
  69. package/dist/core/usageService.js +310 -0
  70. package/dist/core/usageService.js.map +1 -0
  71. package/dist/core/usageService.test.js +234 -0
  72. package/dist/core/usageService.test.js.map +1 -0
  73. package/dist/discord/alwaysAllow.test.js +181 -0
  74. package/dist/discord/alwaysAllow.test.js.map +1 -0
  75. package/dist/discord/client.js +666 -0
  76. package/dist/discord/client.js.map +1 -0
  77. package/dist/discord/client.test.js +256 -0
  78. package/dist/discord/client.test.js.map +1 -0
  79. package/dist/discord/configPanel.js +270 -0
  80. package/dist/discord/configPanel.js.map +1 -0
  81. package/dist/discord/configPanel.test.js +332 -0
  82. package/dist/discord/configPanel.test.js.map +1 -0
  83. package/dist/discord/directoryBrowser.js +151 -0
  84. package/dist/discord/directoryBrowser.js.map +1 -0
  85. package/dist/discord/directoryBrowser.test.js +163 -0
  86. package/dist/discord/directoryBrowser.test.js.map +1 -0
  87. package/dist/discord/favorites.js +10 -0
  88. package/dist/discord/favorites.js.map +1 -0
  89. package/dist/discord/fileDownload.js +94 -0
  90. package/dist/discord/fileDownload.js.map +1 -0
  91. package/dist/discord/fileDownload.test.js +59 -0
  92. package/dist/discord/fileDownload.test.js.map +1 -0
  93. package/dist/discord/format.js +104 -0
  94. package/dist/discord/format.js.map +1 -0
  95. package/dist/discord/format.test.js +40 -0
  96. package/dist/discord/format.test.js.map +1 -0
  97. package/dist/discord/guildChannels.js +97 -0
  98. package/dist/discord/guildChannels.js.map +1 -0
  99. package/dist/discord/guildChannels.test.js +231 -0
  100. package/dist/discord/guildChannels.test.js.map +1 -0
  101. package/dist/discord/i18n.js +207 -0
  102. package/dist/discord/i18n.js.map +1 -0
  103. package/dist/discord/i18n.test.js +31 -0
  104. package/dist/discord/i18n.test.js.map +1 -0
  105. package/dist/discord/interactionRouter.js +954 -0
  106. package/dist/discord/interactionRouter.js.map +1 -0
  107. package/dist/discord/interactionRouter.test.js +1245 -0
  108. package/dist/discord/interactionRouter.test.js.map +1 -0
  109. package/dist/discord/messageRouter.js +221 -0
  110. package/dist/discord/messageRouter.js.map +1 -0
  111. package/dist/discord/messageRouter.test.js +360 -0
  112. package/dist/discord/messageRouter.test.js.map +1 -0
  113. package/dist/discord/ports.js +12 -0
  114. package/dist/discord/ports.js.map +1 -0
  115. package/dist/discord/renderers/defaultSet.test.js +97 -0
  116. package/dist/discord/renderers/defaultSet.test.js.map +1 -0
  117. package/dist/discord/renderers/diffView.js +73 -0
  118. package/dist/discord/renderers/diffView.js.map +1 -0
  119. package/dist/discord/renderers/diffView.test.js +54 -0
  120. package/dist/discord/renderers/diffView.test.js.map +1 -0
  121. package/dist/discord/renderers/index.js +143 -0
  122. package/dist/discord/renderers/index.js.map +1 -0
  123. package/dist/discord/renderers/index.test.js +124 -0
  124. package/dist/discord/renderers/index.test.js.map +1 -0
  125. package/dist/discord/renderers/mentionOnComplete.js +19 -0
  126. package/dist/discord/renderers/mentionOnComplete.js.map +1 -0
  127. package/dist/discord/renderers/mentionOnComplete.test.js +35 -0
  128. package/dist/discord/renderers/mentionOnComplete.test.js.map +1 -0
  129. package/dist/discord/renderers/permissionButtons.js +150 -0
  130. package/dist/discord/renderers/permissionButtons.js.map +1 -0
  131. package/dist/discord/renderers/permissionButtons.test.js +108 -0
  132. package/dist/discord/renderers/permissionButtons.test.js.map +1 -0
  133. package/dist/discord/renderers/resultLine.js +25 -0
  134. package/dist/discord/renderers/resultLine.js.map +1 -0
  135. package/dist/discord/renderers/statusEmbed.js +20 -0
  136. package/dist/discord/renderers/statusEmbed.js.map +1 -0
  137. package/dist/discord/renderers/statusEmbed.test.js +32 -0
  138. package/dist/discord/renderers/statusEmbed.test.js.map +1 -0
  139. package/dist/discord/renderers/streamEmbed.js +134 -0
  140. package/dist/discord/renderers/streamEmbed.js.map +1 -0
  141. package/dist/discord/renderers/streamEmbed.test.js +97 -0
  142. package/dist/discord/renderers/streamEmbed.test.js.map +1 -0
  143. package/dist/discord/renderers/toolThread.js +65 -0
  144. package/dist/discord/renderers/toolThread.js.map +1 -0
  145. package/dist/discord/renderers/toolThread.test.js +54 -0
  146. package/dist/discord/renderers/toolThread.test.js.map +1 -0
  147. package/dist/discord/renderers/transcriptFeed.js +33 -0
  148. package/dist/discord/renderers/transcriptFeed.js.map +1 -0
  149. package/dist/discord/renderers/usageEmbed.js +78 -0
  150. package/dist/discord/renderers/usageEmbed.js.map +1 -0
  151. package/dist/discord/renderers/usageEmbed.test.js +42 -0
  152. package/dist/discord/renderers/usageEmbed.test.js.map +1 -0
  153. package/dist/discord/wiring.js +214 -0
  154. package/dist/discord/wiring.js.map +1 -0
  155. package/dist/discord/wiring.test.js +252 -0
  156. package/dist/discord/wiring.test.js.map +1 -0
  157. package/dist/discord/wizard/channelWizard.js +305 -0
  158. package/dist/discord/wizard/channelWizard.js.map +1 -0
  159. package/dist/discord/wizard/channelWizard.test.js +298 -0
  160. package/dist/discord/wizard/channelWizard.test.js.map +1 -0
  161. package/dist/discord/wizard/resumeWizard.js +123 -0
  162. package/dist/discord/wizard/resumeWizard.js.map +1 -0
  163. package/dist/modes/claude/index.js +80 -0
  164. package/dist/modes/claude/index.js.map +1 -0
  165. package/dist/modes/claude/listResumable.test.js +72 -0
  166. package/dist/modes/claude/listResumable.test.js.map +1 -0
  167. package/dist/modes/claude/mcpFileTool.js +82 -0
  168. package/dist/modes/claude/mcpFileTool.js.map +1 -0
  169. package/dist/modes/claude/permissions.js +35 -0
  170. package/dist/modes/claude/permissions.js.map +1 -0
  171. package/dist/modes/claude/plugins.js +56 -0
  172. package/dist/modes/claude/plugins.js.map +1 -0
  173. package/dist/modes/claude/session.js +324 -0
  174. package/dist/modes/claude/session.js.map +1 -0
  175. package/dist/modes/claude/session.test.js +415 -0
  176. package/dist/modes/claude/session.test.js.map +1 -0
  177. package/dist/modes/codex/discovery.js +221 -0
  178. package/dist/modes/codex/discovery.js.map +1 -0
  179. package/dist/modes/codex/discovery.test.js +213 -0
  180. package/dist/modes/codex/discovery.test.js.map +1 -0
  181. package/dist/modes/codex/eventMapper.js +262 -0
  182. package/dist/modes/codex/eventMapper.js.map +1 -0
  183. package/dist/modes/codex/index.js +132 -0
  184. package/dist/modes/codex/index.js.map +1 -0
  185. package/dist/modes/codex/index.test.js +170 -0
  186. package/dist/modes/codex/index.test.js.map +1 -0
  187. package/dist/modes/codex/runner.js +207 -0
  188. package/dist/modes/codex/runner.js.map +1 -0
  189. package/dist/modes/codex/runner.test.js +587 -0
  190. package/dist/modes/codex/runner.test.js.map +1 -0
  191. package/dist/modes/codex/sqliteReader.js +171 -0
  192. package/dist/modes/codex/sqliteReader.js.map +1 -0
  193. package/dist/modes/codex/sqliteReader.test.js +137 -0
  194. package/dist/modes/codex/sqliteReader.test.js.map +1 -0
  195. package/dist/setup/wizard.js +115 -0
  196. package/dist/setup/wizard.js.map +1 -0
  197. package/dist/setup/wizard.test.js +216 -0
  198. package/dist/setup/wizard.test.js.map +1 -0
  199. package/dist/startBot.test.js +78 -0
  200. package/dist/startBot.test.js.map +1 -0
  201. package/package.json +58 -0
@@ -0,0 +1,231 @@
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
+ import * as fs from 'node:fs';
3
+ import * as os from 'node:os';
4
+ import * as path from 'node:path';
5
+ import { ensureGuildChannels, autoProvisionGuild, createSessionChannel, sessionChannelName, } from './guildChannels.js';
6
+ import { ConfigStore } from '../core/config.js';
7
+ import { CONFIG_VERSION } from '../core/configSchema.js';
8
+ // A recording logger for the auto-provision warning/skip assertions.
9
+ function fakeLogger() {
10
+ const info = vi.fn();
11
+ const warn = vi.fn();
12
+ const logger = { debug: vi.fn(), info, warn, error: vi.fn() };
13
+ return { logger, info, warn };
14
+ }
15
+ // A fake GuildChannelProvisioner backed by an in-memory channel map, mirroring
16
+ // discord.js guild.channels.create/delete WITHOUT a gateway. Every create records the
17
+ // call so a test can assert idempotency (a reused channel makes no create call).
18
+ class FakeProvisioner {
19
+ guildId = 'g1';
20
+ // id → { name, type }. Seeded with pre-existing channels to exercise reuse.
21
+ channels = new Map();
22
+ createdNames = [];
23
+ deleted = [];
24
+ // Toggle the bot's Manage Channels permission to exercise the guarded skip path.
25
+ manageChannels = true;
26
+ seq = 0;
27
+ seed(id, name, type) {
28
+ this.channels.set(id, { name, type });
29
+ }
30
+ canManageChannels() {
31
+ return this.manageChannels;
32
+ }
33
+ channelExists(id) {
34
+ return this.channels.has(id);
35
+ }
36
+ nextId() {
37
+ this.seq += 1;
38
+ return `chan-${this.seq}`;
39
+ }
40
+ async ensureCategory(name, existingId) {
41
+ if (existingId && this.channels.has(existingId)) {
42
+ return { id: existingId, name: this.channels.get(existingId).name };
43
+ }
44
+ const id = this.nextId();
45
+ this.channels.set(id, { name, type: 'category' });
46
+ this.createdNames.push(name);
47
+ return { id, name };
48
+ }
49
+ async ensureTextChannel(name, parentId, existingId) {
50
+ if (existingId && this.channels.has(existingId)) {
51
+ return { id: existingId, name: this.channels.get(existingId).name };
52
+ }
53
+ const id = this.nextId();
54
+ this.channels.set(id, { name, type: 'text', parent: parentId });
55
+ this.createdNames.push(name);
56
+ return { id, name };
57
+ }
58
+ async createTextChannel(name, parentId) {
59
+ const id = this.nextId();
60
+ this.channels.set(id, { name, type: 'text', ...(parentId ? { parent: parentId } : {}) });
61
+ this.createdNames.push(name);
62
+ return { id, name };
63
+ }
64
+ async deleteChannel(id) {
65
+ this.channels.delete(id);
66
+ this.deleted.push(id);
67
+ }
68
+ }
69
+ function writeConfig(dir) {
70
+ const config = {
71
+ version: CONFIG_VERSION,
72
+ discord: { token: 'x', clientId: 'cid' },
73
+ auth: { adminRoleIds: [], executeRoleIds: [], readOnlyRoleIds: [], dmPolicy: 'deny' },
74
+ defaults: {
75
+ mode: 'claude',
76
+ claudeModel: 'opus',
77
+ codexModel: '',
78
+ permissionMode: 'default',
79
+ permissionProfile: null,
80
+ codexHome: '~/.codex',
81
+ codexCliCommand: 'codex',
82
+ codexCliVersion: null,
83
+ },
84
+ limits: { maxSessionsPerUser: 0, permissionTimeoutSec: 60, codexTimeoutMs: 1_800_000 },
85
+ policy: { unknownCommand: 'confirm', allowExtraCommands: [] },
86
+ autoAllowClaudeTools: ['Read'],
87
+ profiles: {},
88
+ usage: { userAgent: 'claude-code', cacheSec: 180 },
89
+ audit: { channelId: null },
90
+ locale: 'ko',
91
+ logLevel: 'info',
92
+ favorites: [],
93
+ };
94
+ fs.mkdirSync(dir, { recursive: true });
95
+ fs.writeFileSync(path.join(dir, 'config.json'), JSON.stringify(config));
96
+ }
97
+ let home;
98
+ let store;
99
+ beforeEach(() => {
100
+ home = fs.mkdtempSync(path.join(os.tmpdir(), 'dab-gc-'));
101
+ writeConfig(home);
102
+ store = new ConfigStore(home);
103
+ });
104
+ afterEach(() => {
105
+ fs.rmSync(home, { recursive: true, force: true });
106
+ });
107
+ describe('ensureGuildChannels', () => {
108
+ it('creates the category + control channel + sessions category and persists the ids', async () => {
109
+ const prov = new FakeProvisioner();
110
+ const channels = await ensureGuildChannels(prov, store);
111
+ // Three channels were created (control category, control channel, sessions category).
112
+ expect(prov.createdNames).toHaveLength(3);
113
+ expect(channels.categoryId).toBeTruthy();
114
+ expect(channels.controlChannelId).toBeTruthy();
115
+ expect(channels.sessionsCategoryId).toBeTruthy();
116
+ expect(channels.statusChannelId).toBeNull();
117
+ // The control channel is parented to the control category.
118
+ expect(prov.channels.get(channels.controlChannelId)?.parent).toBe(channels.categoryId);
119
+ // Persisted to servers/g1.json.
120
+ const saved = store.loadServerConfig('g1');
121
+ expect(saved?.channels).toEqual(channels);
122
+ });
123
+ it('is idempotent: a second run reuses the stored channels and creates nothing new', async () => {
124
+ const prov = new FakeProvisioner();
125
+ const first = await ensureGuildChannels(prov, store);
126
+ expect(prov.createdNames).toHaveLength(3);
127
+ // Re-run against the SAME provisioner (channels still exist) → no new creates,
128
+ // same ids returned.
129
+ const second = await ensureGuildChannels(prov, store);
130
+ expect(prov.createdNames).toHaveLength(3); // unchanged
131
+ expect(second).toEqual(first);
132
+ });
133
+ it('re-creates only a channel that was deleted out from under it', async () => {
134
+ const prov = new FakeProvisioner();
135
+ const first = await ensureGuildChannels(prov, store);
136
+ // The control channel gets deleted; a re-run must re-create ONLY it.
137
+ prov.channels.delete(first.controlChannelId);
138
+ const second = await ensureGuildChannels(prov, store);
139
+ expect(second.categoryId).toBe(first.categoryId); // reused
140
+ expect(second.sessionsCategoryId).toBe(first.sessionsCategoryId); // reused
141
+ expect(second.controlChannelId).not.toBe(first.controlChannelId); // re-created
142
+ // Exactly one additional create (the control channel).
143
+ expect(prov.createdNames).toHaveLength(4);
144
+ // The re-created id is persisted.
145
+ expect(store.loadServerConfig('g1')?.channels?.controlChannelId).toBe(second.controlChannelId);
146
+ });
147
+ it('preserves existing server auth/defaults when persisting channels (first /init)', async () => {
148
+ // A /config-created server file already has auth; /init must not clobber it.
149
+ store.saveServerConfig({ version: 1, guildId: 'g1', auth: { executeRoleIds: ['role-exec'] } });
150
+ const prov = new FakeProvisioner();
151
+ await ensureGuildChannels(prov, store);
152
+ const saved = store.loadServerConfig('g1');
153
+ expect(saved?.auth?.executeRoleIds).toEqual(['role-exec']);
154
+ expect(saved?.channels).toBeDefined();
155
+ });
156
+ });
157
+ describe('autoProvisionGuild (ready / guild-join, /init optional)', () => {
158
+ it('provisions the channel structure when a guild lacks it (invokes create)', async () => {
159
+ const prov = new FakeProvisioner();
160
+ const { logger, info } = fakeLogger();
161
+ const channels = await autoProvisionGuild(prov, store, logger);
162
+ // The 🤖 Agent category + #agent-start + Agent - Sessions were created.
163
+ expect(prov.createdNames).toHaveLength(3);
164
+ expect(channels?.controlChannelId).toBeTruthy();
165
+ // The persisted ids match what auto-provision returned.
166
+ expect(store.loadServerConfig('g1')?.channels).toEqual(channels);
167
+ expect(info).toHaveBeenCalled();
168
+ });
169
+ it('is idempotent when already provisioned (no new channels created)', async () => {
170
+ const prov = new FakeProvisioner();
171
+ const { logger } = fakeLogger();
172
+ const first = await autoProvisionGuild(prov, store, logger);
173
+ expect(prov.createdNames).toHaveLength(3);
174
+ // A second pass (e.g. a later ClientReady after a restart) reuses the stored ids.
175
+ const second = await autoProvisionGuild(prov, store, logger);
176
+ expect(prov.createdNames).toHaveLength(3);
177
+ expect(second).toEqual(first);
178
+ });
179
+ it('missing Manage Channels: logs a warning, creates nothing, does NOT throw', async () => {
180
+ const prov = new FakeProvisioner();
181
+ prov.manageChannels = false;
182
+ const { logger, warn } = fakeLogger();
183
+ const result = await autoProvisionGuild(prov, store, logger);
184
+ expect(result).toBeNull();
185
+ expect(prov.createdNames).toHaveLength(0);
186
+ expect(store.loadServerConfig('g1')?.channels).toBeUndefined();
187
+ // A clear warning names the missing permission.
188
+ expect(warn).toHaveBeenCalled();
189
+ expect(warn.mock.calls[0][0]).toContain('Manage Channels');
190
+ });
191
+ it('swallows a create failure (logs a warning, returns null, no throw)', async () => {
192
+ const prov = new FakeProvisioner();
193
+ // A create rejects mid-provision (mirrors a permission error the guard cannot see).
194
+ prov.ensureCategory = vi.fn(async () => {
195
+ throw new Error('Missing Permissions');
196
+ });
197
+ const { logger, warn } = fakeLogger();
198
+ const result = await autoProvisionGuild(prov, store, logger);
199
+ expect(result).toBeNull();
200
+ expect(warn).toHaveBeenCalled();
201
+ });
202
+ });
203
+ describe('createSessionChannel', () => {
204
+ it('creates a proj-<folder> channel under the sessions category', async () => {
205
+ const prov = new FakeProvisioner();
206
+ const created = await createSessionChannel(prov, '/abs/path/My Project', 'sessions-cat');
207
+ expect(created.name).toBe('proj-my-project');
208
+ expect(prov.channels.get(created.id)?.parent).toBe('sessions-cat');
209
+ });
210
+ it('creates the channel without a parent when no sessions category is given', async () => {
211
+ const prov = new FakeProvisioner();
212
+ const created = await createSessionChannel(prov, '/abs/path/thing');
213
+ expect(prov.channels.get(created.id)?.parent).toBeUndefined();
214
+ });
215
+ });
216
+ describe('sessionChannelName', () => {
217
+ it('slugifies the folder basename with a proj- prefix', () => {
218
+ expect(sessionChannelName('/home/me/My_App')).toBe('proj-my-app');
219
+ expect(sessionChannelName('/home/me/foo.bar')).toBe('proj-foo-bar');
220
+ expect(sessionChannelName('/home/me/repo/')).toBe('proj-repo');
221
+ });
222
+ it('falls back to proj-session for an empty/unusable basename', () => {
223
+ expect(sessionChannelName('/')).toBe('proj-session');
224
+ expect(sessionChannelName('///')).toBe('proj-session');
225
+ });
226
+ it('caps the name at Discord’s 100-char limit', () => {
227
+ const long = '/x/' + 'a'.repeat(200);
228
+ expect(sessionChannelName(long).length).toBeLessThanOrEqual(100);
229
+ });
230
+ });
231
+ //# sourceMappingURL=guildChannels.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guildChannels.test.js","sourceRoot":"","sources":["../../src/discord/guildChannels.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,207 @@
1
+ // Korean-default localizable bot messages (§4, §11 item 14). Every user-facing
2
+ // bot string flows through t(key, vars?): a flat catalog keyed by dotted names,
3
+ // default locale 'ko'. A missing key falls back to the key itself (so a typo is
4
+ // visible, never a blank string), and {placeholders} are interpolated from vars.
5
+ //
6
+ // The catalog is intentionally flat and local to the discord layer — core stays
7
+ // transport-agnostic. 7b/8 sets the active locale from config.locale (§8.1).
8
+ // The default active locale; overridden at boot from config.locale.
9
+ let activeLocale = 'ko';
10
+ export function setLocale(locale) {
11
+ activeLocale = locale;
12
+ }
13
+ export function getLocale() {
14
+ return activeLocale;
15
+ }
16
+ const ko = {
17
+ // Wizard (steps: 폴더 → 백엔드 → 모델 → 추론수준 → 권한 → 시작)
18
+ 'wizard.title': '세션 시작',
19
+ 'wizard.step.folder': '1/5단계 · 폴더',
20
+ 'wizard.step.backend': '2/5단계 · 백엔드를 선택하고 "다음"을 누르세요.',
21
+ 'wizard.step.model': '3/5단계 · 모델을 선택하고 "다음"을 누르세요.',
22
+ 'wizard.step.effort': '4/5단계 · 추론 수준을 선택하고 "다음"을 누르세요.',
23
+ 'wizard.step.perm': '5/5단계 · 권한을 선택하고 "✅ 시작"을 누르세요.',
24
+ 'wizard.confirm': '`{cwd}` 에서 {backend} 세션을 시작할까요? (권한: {perm})',
25
+ 'wizard.started': '세션을 시작했어요. 백엔드 {backend} · 폴더 `{cwd}`',
26
+ 'wizard.cancelled': '세션 시작을 취소했어요.',
27
+ 'wizard.cancel': '취소',
28
+ 'wizard.next': '다음',
29
+ 'wizard.start': '✅ 시작',
30
+ 'wizard.profile.advanced': '고급: 권한 모드 직접 선택',
31
+ // /config panel (role tiers + defaults, clicked in Discord)
32
+ 'config.title': '역할·기본값 설정',
33
+ 'config.intro': '① 역할: 봇을 쓸 사람의 Discord 역할을 고르고 **저장**하세요. 본인(관리자)이 가진 역할을 **admin**에 넣으면 다 됩니다.\n② 아래 기본값(백엔드·모델·권한·언어)은 **고르면 바로 저장**됩니다.\nClaude·Codex는 각자 홈(`~/.claude`, `~/.codex`)을 자동으로 사용하며, **작업할 프로젝트 폴더는 `/agent start` 할 때 고릅니다.**',
34
+ 'config.role.admin.placeholder': 'admin 역할 (설정·stop-all)',
35
+ 'config.role.execute.placeholder': 'execute 역할 (세션 시작·명령 실행)',
36
+ 'config.role.readOnly.placeholder': 'read-only 역할 (읽기 전용)',
37
+ 'config.default.backend.placeholder': '기본 백엔드',
38
+ 'config.default.model.placeholder': '기본 모델',
39
+ 'config.default.permMode.placeholder': '권한 모드 (기본)',
40
+ 'config.default.locale.placeholder': '봇 언어',
41
+ 'config.locale.ko': '한국어 (ko)',
42
+ 'config.locale.en': 'English (en)',
43
+ 'config.save': '저장',
44
+ 'config.saved': '이 서버 설정을 저장했어요.\n• admin: {admin}\n• execute: {execute}\n• read-only: {readOnly}\n• 기본 백엔드: {backend} · 모델: {model} · 권한: {perm}',
45
+ // Auto-save notices: each /config select persists ONE field immediately.
46
+ 'config.autosaved.locale': '언어를 저장했어요: {locale}',
47
+ 'config.autosaved.backend': '기본 백엔드를 저장했어요: {backend}',
48
+ 'config.autosaved.model': '기본 모델을 저장했어요: {model}',
49
+ 'config.autosaved.permMode': '권한 모드를 저장했어요: {perm}',
50
+ // Backend / permission mode labels
51
+ 'backend.claude': 'Claude Code',
52
+ 'backend.codex': 'Codex',
53
+ // Confirmation/notice labels (NOT the dropdown OPTION labels — those are English,
54
+ // sourced from providerCatalog). Used in save/switch notices and the wizard confirm.
55
+ 'perm.default': '기본 (매번 확인)',
56
+ 'perm.acceptEdits': '편집 자동 승인',
57
+ 'perm.bypassPermissions': '전체 자동 승인 (⚠️ 위험)',
58
+ 'perm.plan': '플랜 (읽기 전용)',
59
+ 'perm.dontAsk': '사전 승인만 허용 (미승인 거부)',
60
+ 'perm.auto': '자동 판단 (모델이 승인/거부)',
61
+ // Codex-native sandbox modes (shown in the status embed for a Codex session).
62
+ 'perm.read-only': '읽기 전용 (실행 시 확인)',
63
+ 'perm.workspace-write': '작업 폴더 쓰기 허용',
64
+ 'perm.danger-full-access': '전체 접근 (⚠️ 샌드박스 없음)',
65
+ // Directory browser
66
+ 'dir.up': '⬆ 상위 폴더',
67
+ 'dir.select': '하위 폴더로 이동…',
68
+ 'dir.here': '✅ 이 폴더로 시작',
69
+ 'dir.resume': '세션 재개',
70
+ 'dir.create': '📁 폴더 만들기',
71
+ 'dir.empty': '(하위 폴더 없음)',
72
+ 'dir.escape': '허용된 범위를 벗어난 경로입니다.',
73
+ // Folder-create modal (📁 Create): a single text input for the new folder name,
74
+ // created as a direct subfolder of the current browsed directory.
75
+ 'dir.create.title': '새 폴더 만들기',
76
+ 'dir.create.label': '폴더 이름',
77
+ 'dir.create.placeholder': '예: my-project',
78
+ 'dir.create.invalid': '폴더 이름이 올바르지 않아요. `/`, `..`, 절대 경로는 쓸 수 없어요.',
79
+ 'dir.create.failed': '폴더를 만들지 못했어요: {error}',
80
+ 'dir.create.done': '폴더를 만들었어요: {name}',
81
+ // Folder-picker guidance (step 1 of /agent start). Shows the current path + how to
82
+ // navigate; the actual PROJECT folder is chosen HERE (not in /config).
83
+ 'dir.guide': '작업할 **프로젝트 폴더**를 고르세요. 목록에서 하위 폴더로 들어가거나 `⬆ 상위 폴더`로 올라간 뒤, `✅ 이 폴더로 시작`을 누르세요.',
84
+ 'dir.current': '현재 위치',
85
+ // Permission buttons
86
+ 'perm.request.title': '권한 요청',
87
+ 'perm.request.body': '**도구:** {tool}\n\n{input}',
88
+ 'perm.button.allow': '허용',
89
+ 'perm.button.always': '항상 허용',
90
+ 'perm.button.deny': '거부',
91
+ 'perm.decided.allow': '허용됨',
92
+ 'perm.decided.always': '항상 허용됨',
93
+ 'perm.decided.deny': '거부됨',
94
+ // Status embed
95
+ 'status.title': '세션 상태',
96
+ 'status.mode': '모드',
97
+ 'status.cwd': '작업 폴더',
98
+ 'status.session': '세션 ID',
99
+ 'status.permMode': '권한 모드',
100
+ 'status.usage.codex': '사용량/한도 정보 없음 (Codex CLI 제한)',
101
+ // Result line
102
+ 'result.done': '완료',
103
+ 'result.cost': '비용',
104
+ 'result.tokens': '토큰',
105
+ 'result.duration': '소요',
106
+ // Stream embed
107
+ 'stream.responding': '응답 중…',
108
+ 'stream.thinking': '생각 중…',
109
+ 'stream.thought': '{sec}초 동안 생각함',
110
+ // Tool thread
111
+ 'tool.result': '결과',
112
+ 'tool.error': '오류',
113
+ // Usage panel
114
+ 'usage.title': 'Claude 사용량',
115
+ 'usage.fiveHour': '5시간',
116
+ 'usage.weekly': '주간',
117
+ 'usage.weeklyOpus': '주간 (Opus)',
118
+ 'usage.weeklySonnet': '주간 (Sonnet)',
119
+ 'usage.context': '컨텍스트',
120
+ 'usage.resets': '초기화 {reset}',
121
+ // File download
122
+ 'file.escape': '워크스페이스를 벗어난 경로는 다운로드할 수 없습니다.',
123
+ 'file.notFound': '파일을 찾을 수 없습니다.',
124
+ 'file.notFile': '파일이 아닙니다.',
125
+ // Transcript feed (Codex)
126
+ 'transcript.working': '작업 중…',
127
+ // Router notices (7b)
128
+ 'auth.denied': '권한이 없습니다: {reason}',
129
+ 'router.noSession': '이 채널에는 활성 세션이 없어요. 먼저 `/agent start` 를 실행하세요.',
130
+ 'router.turn.queued': '대기열에 추가했어요 (#{depth}).',
131
+ 'cmd.start.launched': '세션 시작 마법사를 열었어요.',
132
+ 'cmd.start.channelCreated': '세션 채널 생성됨: {channel}',
133
+ 'cmd.start.intro': '이 채널에서 에이전트와 대화하세요. 메시지를 보내면 작업이 시작됩니다. `/agent close` 로 세션을 종료하고 채널을 정리할 수 있어요.',
134
+ 'cmd.init.done': '채널 구성을 완료했어요. {control} 에서 `/agent start` 로 세션을 시작하세요.',
135
+ 'cmd.init.unavailable': '채널을 만들 수 없어요. 봇에 "채널 관리(Manage Channels)" 권한이 있는지 확인하세요.',
136
+ 'cmd.config.opened': '역할·기본값 설정 패널을 열었어요. ① 역할을 고르고 저장, ② 아래 기본값은 고르면 바로 저장돼요.',
137
+ 'cmd.config.denied': '`/config` 는 서버 관리자(Administrator) 또는 admin 티어만 사용할 수 있어요.',
138
+ 'cmd.resume.none': '재개할 수 있는 세션이 없어요. 새로 시작하려면 `/agent start` 를 사용하세요.',
139
+ 'cmd.resume.rebound': '이 채널을 다시 연결했어요.',
140
+ // Resume-from-folder flow (Resume Session button on the folder step).
141
+ 'resume.step.backend': '재개할 백엔드를 선택하고 "다음"을 누르세요.',
142
+ 'resume.step.pick': '재개할 세션을 선택하세요.',
143
+ 'resume.select.placeholder': '세션 선택…',
144
+ 'resume.none': '재개할 세션이 없습니다.',
145
+ 'resume.done': '세션 재개됨: {channel}',
146
+ 'resume.status.title': '세션 재개됨',
147
+ 'resume.time.now': '방금',
148
+ 'resume.time.min': '{n}분 전',
149
+ 'resume.time.hour': '{n}시간 전',
150
+ 'resume.time.day': '{n}일 전',
151
+ 'cmd.close.done': '세션을 종료하고 보관했어요.',
152
+ 'cmd.stop.done': '세션을 중지했어요.',
153
+ 'cmd.stopAll.done': '모든 세션을 중지했어요 ({count}개).',
154
+ 'cmd.mode.switched': '백엔드를 {backend} 로 바꿨어요.',
155
+ 'cmd.mode.freshContext': '⚠️ {backend} 로 바꾸면 이 채널은 새 대화로 시작돼요. 이전 맥락은 안 넘어갑니다.',
156
+ 'cmd.mode.unavailable': '`{backend}` 백엔드는 사용할 수 없어요. 현재 세션은 그대로 유지했어요.',
157
+ 'cmd.perm.switched': '권한 설정을 바꿨어요: {perm}',
158
+ 'cmd.error': '명령을 처리하지 못했어요: {error}',
159
+ // Generic ack for an interaction that failed before/while routing (no detail leaked).
160
+ 'cmd.error.generic': '명령을 처리하지 못했어요. 잠시 후 다시 시도해 주세요.',
161
+ // Startup guidance (boot path): config missing / token missing → point at --setup.
162
+ 'boot.noConfig': '설정이 없습니다. 먼저 셋업을 실행하세요: node dist/cli.js --setup',
163
+ 'boot.noToken': '토큰이 설정되지 않았습니다 — --setup을 다시 실행하세요.',
164
+ // Terminal setup guidance: roles move from the terminal to Discord `/config`.
165
+ 'setup.rolesInDiscord': '역할은 봇을 서버에 초대한 뒤 Discord에서 `/config` 명령으로 클릭 설정하세요.',
166
+ // Terminal setup guidance: model/language/permission defaults move to `/config`.
167
+ 'setup.defaultsInDiscord': '모델·언어·권한 등 기본값은 봇 초대 후 Discord `/config`에서 설정하세요.',
168
+ };
169
+ const en = {
170
+ 'wizard.title': 'Start session',
171
+ 'wizard.confirm': 'Start a {backend} session in `{cwd}`? (permission: {perm})',
172
+ 'perm.button.allow': 'Allow',
173
+ 'perm.button.always': 'Always allow',
174
+ 'perm.button.deny': 'Deny',
175
+ 'status.usage.codex': 'usage/limits unavailable (Codex CLI limitation)',
176
+ 'transcript.working': 'working…',
177
+ 'boot.noConfig': 'No configuration found. Run setup first: node dist/cli.js --setup',
178
+ 'boot.noToken': 'Discord token is not set — run --setup again.',
179
+ 'config.default.locale.placeholder': 'Bot language',
180
+ 'config.default.permMode.placeholder': 'Permission mode (default)',
181
+ 'config.autosaved.locale': 'Saved language: {locale}',
182
+ 'config.autosaved.backend': 'Saved default backend: {backend}',
183
+ 'config.autosaved.model': 'Saved default model: {model}',
184
+ 'config.autosaved.permMode': 'Saved permission mode: {perm}',
185
+ 'dir.up': '⬆ Parent folder',
186
+ 'dir.select': 'Go into a subfolder…',
187
+ 'dir.here': '✅ Start in this folder',
188
+ 'dir.current': 'Current location',
189
+ };
190
+ const catalogs = { ko, en };
191
+ // Interpolate {name} placeholders from vars. An unknown placeholder is left as-is
192
+ // (visible), never silently blanked.
193
+ function interpolate(template, vars) {
194
+ if (!vars)
195
+ return template;
196
+ return template.replace(/\{(\w+)\}/g, (whole, name) => {
197
+ const v = vars[name];
198
+ return v === undefined ? whole : String(v);
199
+ });
200
+ }
201
+ // Resolve a key in the active locale, falling back to 'ko', then to the key
202
+ // itself, then interpolate. `locale` overrides the active locale for one call.
203
+ export function t(key, vars, locale = activeLocale) {
204
+ const template = catalogs[locale]?.[key] ?? ko[key] ?? key;
205
+ return interpolate(template, vars);
206
+ }
207
+ //# sourceMappingURL=i18n.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"i18n.js","sourceRoot":"","sources":["../../src/discord/i18n.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import { describe, it, expect, afterEach } from 'vitest';
2
+ import { t, setLocale, getLocale } from './i18n.js';
3
+ describe('i18n', () => {
4
+ afterEach(() => setLocale('ko'));
5
+ it('defaults to Korean and returns the Korean string for a known key', () => {
6
+ expect(getLocale()).toBe('ko');
7
+ expect(t('perm.button.allow')).toBe('허용');
8
+ expect(t('status.title')).toBe('세션 상태');
9
+ });
10
+ it('falls back to the key itself for a missing key', () => {
11
+ expect(t('this.key.does.not.exist')).toBe('this.key.does.not.exist');
12
+ });
13
+ it('interpolates {placeholder} vars', () => {
14
+ expect(t('wizard.started', { backend: 'claude', cwd: '/ws' })).toBe('세션을 시작했어요. 백엔드 claude · 폴더 `/ws`');
15
+ });
16
+ it('leaves an unknown placeholder untouched (never blanked)', () => {
17
+ // 'stream.thought' expects {sec}; omit it and the token stays visible.
18
+ expect(t('stream.thought')).toBe('{sec}초 동안 생각함');
19
+ });
20
+ it('resolves in the active locale, falling back to ko for keys not in en', () => {
21
+ setLocale('en');
22
+ expect(t('perm.button.allow')).toBe('Allow');
23
+ // 'status.title' has no English entry → falls back to Korean.
24
+ expect(t('status.title')).toBe('세션 상태');
25
+ });
26
+ it('honors a per-call locale override', () => {
27
+ expect(t('perm.button.deny', undefined, 'en')).toBe('Deny');
28
+ expect(getLocale()).toBe('ko'); // active locale unchanged
29
+ });
30
+ });
31
+ //# sourceMappingURL=i18n.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"i18n.test.js","sourceRoot":"","sources":["../../src/discord/i18n.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACzD,OAAO,EAAE,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEpD,QAAQ,CAAC,MAAM,EAAE,GAAG,EAAE;IACpB,SAAS,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;IAEjC,EAAE,CAAC,kEAAkE,EAAE,GAAG,EAAE;QAC1E,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/B,MAAM,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1C,MAAM,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACxD,MAAM,CAAC,CAAC,CAAC,yBAAyB,CAAC,CAAC,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;IACvE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QACzC,MAAM,CAAC,CAAC,CAAC,gBAAgB,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CACjE,kCAAkC,CACnC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;QACjE,uEAAuE;QACvE,MAAM,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sEAAsE,EAAE,GAAG,EAAE;QAC9E,SAAS,CAAC,IAAI,CAAC,CAAC;QAChB,MAAM,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC7C,8DAA8D;QAC9D,MAAM,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,MAAM,CAAC,CAAC,CAAC,kBAAkB,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC5D,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,0BAA0B;IAC5D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}