@serverkgg/bridge 0.1.4 → 0.1.5

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 (72) hide show
  1. package/package.json +1 -1
  2. package/src/bot/botCauses.test.ts +284 -0
  3. package/src/bot/botCauses.ts +58 -0
  4. package/src/bot/botDeploy.test.ts +260 -1
  5. package/src/bot/botDeploy.ts +125 -6
  6. package/src/bot/botState.ts +6 -0
  7. package/src/bot/detect/botInspectWarning.ts +3 -1
  8. package/src/bot/detect/botPackageManager.ts +3 -0
  9. package/src/bot/detect/corpus/alexmubarakshin-ts-telegraf-boilerplate.json +37 -0
  10. package/src/bot/detect/corpus/alsarmad-discord-quran-bot.json +40 -0
  11. package/src/bot/detect/corpus/anasty17-mirror-leech-telegram-bot.json +50 -0
  12. package/src/bot/detect/corpus/anidiotsguide-guidebot.json +36 -0
  13. package/src/bot/detect/corpus/anonymousx1025-anonxmusic.json +45 -0
  14. package/src/bot/detect/corpus/bot-base-telegram-bot-template.json +35 -0
  15. package/src/bot/detect/corpus/bracketed-templates-discordjs.json +40 -0
  16. package/src/bot/detect/corpus/cog-creators-red-discordbot.json +49 -0
  17. package/src/bot/detect/corpus/cranberriez-discord.ts-bot-template.json +35 -0
  18. package/src/bot/detect/corpus/crazycat256-nextcord-bot-template.json +38 -0
  19. package/src/bot/detect/corpus/discord-tickets-bot.json +45 -0
  20. package/src/bot/detect/corpus/edjopato-telegram-typescript-bot-template.json +37 -0
  21. package/src/bot/detect/corpus/haniali3161-bot-adhkar-telegram.json +33 -0
  22. package/src/bot/detect/corpus/hellosverre-discord-ts-template.json +34 -0
  23. package/src/bot/detect/corpus/kevinnovak-discord-bot-typescript-template.json +43 -0
  24. package/src/bot/detect/corpus/kkrypt0nn-python-discord-bot-template.json +41 -0
  25. package/src/bot/detect/corpus/kolezka-telegram-grammy-template.json +37 -0
  26. package/src/bot/detect/corpus/latand-tgbot_template_v3.json +37 -0
  27. package/src/bot/detect/corpus/mahmoudbm77-zekr-telegram-bot.json +36 -0
  28. package/src/bot/detect/corpus/mericcan41-discordjs-v14-template-ts.json +33 -0
  29. package/src/bot/detect/corpus/modmail-dev-modmail.json +52 -0
  30. package/src/bot/detect/corpus/nezdemkovski-telegram-bot-boilerplate.json +39 -0
  31. package/src/bot/detect/corpus/notchum-discord-bot-template.json +39 -0
  32. package/src/bot/detect/corpus/python-discord-bot.json +44 -0
  33. package/src/bot/detect/corpus/python-telegram-bot-rules-bot.json +39 -0
  34. package/src/bot/detect/corpus/rajdave69-pycord-bot-template.json +31 -0
  35. package/src/bot/detect/corpus/rapptz-robodanny.json +36 -0
  36. package/src/bot/detect/corpus/ry2x-typescript-discord.js-v.14-template.json +35 -0
  37. package/src/bot/detect/corpus/saiteja-madha-discord-js-bot.json +45 -0
  38. package/src/bot/detect/corpus/seegreatness-discord.js-for-heroku.json +37 -0
  39. package/src/bot/detect/corpus/serverk-discord-node.json +36 -0
  40. package/src/bot/detect/corpus/serverk-discord-python.json +35 -0
  41. package/src/bot/detect/corpus/serverk-telegram-node.json +36 -0
  42. package/src/bot/detect/corpus/serverk-telegram-python.json +35 -0
  43. package/src/bot/detect/corpus/shuruhatik-quran-radio-bot-discord.json +33 -0
  44. package/src/bot/detect/corpus/skyra-project-skyra.json +46 -0
  45. package/src/bot/detect/corpus/tgbot-collection-ytdlbot.json +41 -0
  46. package/src/bot/detect/corpus/zeriodev-music-bot.json +38 -0
  47. package/src/bot/detect/detect.corpus.test.ts +77 -0
  48. package/src/bot/detect/detect.test.ts +414 -17
  49. package/src/bot/detect/detect.ts +347 -36
  50. package/src/bot/detect/detectResult.ts +4 -0
  51. package/src/bot/detect/detectTable.test.ts +22 -6
  52. package/src/bot/detect/detectTable.ts +187 -19
  53. package/src/bot/detect/detectedVersion.ts +5 -0
  54. package/src/bot/detect/envExample.test.ts +55 -1
  55. package/src/bot/detect/envExample.ts +14 -0
  56. package/src/bot/detect/index.ts +1 -0
  57. package/src/bot/detect/keys.test.ts +32 -1
  58. package/src/bot/detect/keys.ts +15 -0
  59. package/src/bot/detect/serverkJson.test.ts +2 -0
  60. package/src/bot/detect/serverkJson.ts +4 -1
  61. package/src/bot/index.ts +1 -0
  62. package/src/manifest/manifest.ts +2 -1
  63. package/src/modules/bridge.ts +2 -0
  64. package/src/modules/bridgeModule.ts +5 -0
  65. package/src/protocol/bridgeCrash.ts +49 -0
  66. package/src/protocol/bridgeEvents.ts +9 -0
  67. package/src/runtime/bridgeDriver.test.ts +45 -0
  68. package/src/runtime/bridgeDriver.ts +25 -0
  69. package/src/runtime/bridgeEvents.ts +2 -0
  70. package/src/runtime/bridgeSupervisor.test.ts +163 -3
  71. package/src/runtime/bridgeSupervisor.ts +42 -1
  72. package/src/runtime/serve.ts +26 -5
@@ -68,10 +68,13 @@ describe("detect on the templates", () => {
68
68
  command: "npm ci",
69
69
  source: "package-lock.json",
70
70
  },
71
+ build: null,
71
72
  start: {
72
73
  command: "node index.js",
73
74
  source: "package.json",
74
75
  },
76
+ nodeVersion: null,
77
+ pythonVersion: null,
75
78
  env: [
76
79
  {
77
80
  key: "DISCORD_TOKEN",
@@ -93,13 +96,16 @@ describe("detect on the templates", () => {
93
96
  framework: BotFramework.PythonTelegramBot,
94
97
  platform: BotPlatform.Telegram,
95
98
  install: {
96
- command: "pip install -r requirements.txt",
99
+ command: "uv pip install -r requirements.txt",
97
100
  source: "requirements.txt",
98
101
  },
102
+ build: null,
99
103
  start: {
100
104
  command: "python main.py",
101
105
  source: "main.py",
102
106
  },
107
+ nodeVersion: null,
108
+ pythonVersion: null,
103
109
  env: [
104
110
  {
105
111
  key: "TELEGRAM_TOKEN",
@@ -113,7 +119,7 @@ describe("detect on the templates", () => {
113
119
  } satisfies DetectResult);
114
120
  });
115
121
 
116
- it("reads the lockfile a pnpm or yarn project ships", () => {
122
+ it("reads the lockfile a pnpm, yarn or bun project ships, frozen", () => {
117
123
  expect(
118
124
  project(DISCORD_NODE, [
119
125
  "pnpm-lock.yaml",
@@ -125,8 +131,290 @@ describe("detect on the templates", () => {
125
131
  expect(
126
132
  project(DISCORD_NODE, [
127
133
  "yarn.lock",
134
+ ]).install,
135
+ ).toEqual({
136
+ command: "corepack yarn install --frozen-lockfile",
137
+ source: "yarn.lock",
138
+ });
139
+ expect(
140
+ project(DISCORD_NODE, [
141
+ "yarn.lock",
142
+ ".yarnrc.yml",
143
+ ]).install,
144
+ ).toEqual({
145
+ command: "corepack yarn install --immutable",
146
+ source: "yarn.lock",
147
+ });
148
+ expect(
149
+ project(DISCORD_NODE, [
150
+ "bun.lockb",
151
+ ]).install,
152
+ ).toEqual({
153
+ command: "bun install --frozen-lockfile",
154
+ source: "bun.lockb",
155
+ });
156
+ expect(
157
+ project(DISCORD_NODE, [
158
+ "bun.lock",
159
+ ]).packageManager,
160
+ ).toBe(BotPackageManager.Bun);
161
+ });
162
+
163
+ it("lets the packageManager field and the engines name the manager before any lockfile", () => {
164
+ const withField = (packageManager: string, engines?: Record<string, string>) => {
165
+ return JSON.stringify({
166
+ ...JSON.parse(DISCORD_NODE["package.json"]),
167
+ packageManager,
168
+ engines,
169
+ });
170
+ };
171
+
172
+ expect(
173
+ project(
174
+ {
175
+ ...DISCORD_NODE,
176
+ "package.json": withField("pnpm@9.1.0"),
177
+ },
178
+ [
179
+ "package-lock.json",
180
+ ],
181
+ ).install,
182
+ ).toEqual({
183
+ command: "corepack pnpm install",
184
+ source: "package.json",
185
+ });
186
+ expect(
187
+ project(
188
+ {
189
+ ...DISCORD_NODE,
190
+ "package.json": withField("yarn@4.1.0"),
191
+ },
192
+ [
193
+ "yarn.lock",
194
+ ],
195
+ ).install?.command,
196
+ ).toBe("corepack yarn install --immutable");
197
+ expect(
198
+ project(
199
+ {
200
+ ...DISCORD_NODE,
201
+ "package.json": withField("yarn@1.22.0"),
202
+ },
203
+ [
204
+ "yarn.lock",
205
+ ],
206
+ ).install?.command,
207
+ ).toBe("corepack yarn install --frozen-lockfile");
208
+ expect(
209
+ project({
210
+ ...DISCORD_NODE,
211
+ "package.json": JSON.stringify({
212
+ ...JSON.parse(DISCORD_NODE["package.json"]),
213
+ engines: {
214
+ bun: ">=1.1",
215
+ },
216
+ }),
217
+ }).install,
218
+ ).toEqual({
219
+ command: "bun install",
220
+ source: "package.json",
221
+ });
222
+ expect(
223
+ project(DISCORD_NODE, [
224
+ "package-lock.json",
225
+ "yarn.lock",
128
226
  ]).packageManager,
129
227
  ).toBe(BotPackageManager.Yarn);
228
+ expect(
229
+ project(DISCORD_NODE, [
230
+ "package-lock.json",
231
+ ".yarnrc.yml",
232
+ ]).install,
233
+ ).toEqual({
234
+ command: "corepack yarn install --no-immutable",
235
+ source: ".yarnrc.yml",
236
+ });
237
+ expect(
238
+ project(
239
+ {
240
+ ...DISCORD_NODE,
241
+ "package.json": JSON.stringify({
242
+ ...JSON.parse(DISCORD_NODE["package.json"]),
243
+ engines: {
244
+ pnpm: ">=9",
245
+ },
246
+ }),
247
+ },
248
+ [
249
+ "package-lock.json",
250
+ ],
251
+ ).install,
252
+ ).toEqual({
253
+ command: "corepack pnpm install",
254
+ source: "package.json",
255
+ });
256
+ });
257
+
258
+ it("runs the build script with the detected manager, and serverk.json overrides it", () => {
259
+ const built = {
260
+ ...DISCORD_NODE,
261
+ "package.json": JSON.stringify({
262
+ ...JSON.parse(DISCORD_NODE["package.json"]),
263
+ scripts: {
264
+ build: "tsc",
265
+ start: "node dist/index.js",
266
+ },
267
+ }),
268
+ };
269
+
270
+ expect(
271
+ project(built, [
272
+ "pnpm-lock.yaml",
273
+ ]).build,
274
+ ).toEqual({
275
+ command: "corepack pnpm run build",
276
+ source: "package.json",
277
+ });
278
+ expect(project(DISCORD_NODE).build).toBeNull();
279
+ expect(
280
+ project({
281
+ ...built,
282
+ "serverk.json": JSON.stringify({
283
+ build: "npm run compile",
284
+ }),
285
+ }).build,
286
+ ).toEqual({
287
+ command: "npm run compile",
288
+ source: "serverk.json",
289
+ });
290
+ });
291
+
292
+ it("reads the Node version the code asks for and warns when the image cannot meet it", () => {
293
+ const engines = (node: string) => {
294
+ return {
295
+ ...DISCORD_NODE,
296
+ "package.json": JSON.stringify({
297
+ ...JSON.parse(DISCORD_NODE["package.json"]),
298
+ engines: {
299
+ node,
300
+ },
301
+ }),
302
+ };
303
+ };
304
+
305
+ expect(project(engines(">=18")).nodeVersion).toBe("18");
306
+ expect(project(engines(">=18")).warnings).not.toContain(BotInspectWarning.NodeVersionUnmet);
307
+ expect(project(engines("^20.10.0")).warnings).toContain(BotInspectWarning.NodeVersionUnmet);
308
+ expect(project(engines("18.x || >=22")).warnings).not.toContain(BotInspectWarning.NodeVersionUnmet);
309
+ expect(project(engines(">=18 <21")).nodeVersion).toBe("18");
310
+ expect(project(engines(">=18 <21")).warnings).toContain(BotInspectWarning.NodeVersionUnmet);
311
+ expect(project(engines("18 - 20")).nodeVersion).toBe("18");
312
+ expect(project(engines("18 - 20")).warnings).toContain(BotInspectWarning.NodeVersionUnmet);
313
+ expect(project(engines(">24")).warnings).toContain(BotInspectWarning.NodeVersionUnmet);
314
+ expect(project(engines(">24.0.0")).warnings).not.toContain(BotInspectWarning.NodeVersionUnmet);
315
+ expect(project(engines("<21")).nodeVersion).toBe("20");
316
+ expect(project(engines("<21")).warnings).toContain(BotInspectWarning.NodeVersionUnmet);
317
+ expect(project(engines("<=20")).nodeVersion).toBe("20");
318
+ expect(project(engines("*")).warnings).not.toContain(BotInspectWarning.NodeVersionUnmet);
319
+ expect(
320
+ project({
321
+ ...DISCORD_NODE,
322
+ ".nvmrc": "v20.11.1\n",
323
+ }).nodeVersion,
324
+ ).toBe("20");
325
+ expect(
326
+ project({
327
+ ...DISCORD_NODE,
328
+ ".nvmrc": "lts/*\n",
329
+ }).nodeVersion,
330
+ ).toBeNull();
331
+ expect(
332
+ project({
333
+ ...DISCORD_NODE,
334
+ ".tool-versions": "python 3.12.1\nnodejs 22.3.0\n",
335
+ }).nodeVersion,
336
+ ).toBe("22");
337
+ expect(
338
+ project({
339
+ ...DISCORD_NODE,
340
+ ".node-version": "24\n",
341
+ }).warnings,
342
+ ).not.toContain(BotInspectWarning.NodeVersionUnmet);
343
+ });
344
+
345
+ it("reads the Python version from the version files, in order", () => {
346
+ expect(
347
+ project({
348
+ ...TELEGRAM_PYTHON,
349
+ ".python-version": "3.11.4\n",
350
+ "runtime.txt": "python-3.10.2\n",
351
+ }).pythonVersion,
352
+ ).toBe("3.11");
353
+ expect(
354
+ project({
355
+ ...TELEGRAM_PYTHON,
356
+ "runtime.txt": "python-3.10.2\n",
357
+ }).pythonVersion,
358
+ ).toBe("3.10");
359
+ expect(
360
+ project({
361
+ ...TELEGRAM_PYTHON,
362
+ Pipfile: '[requires]\npython_version = "3.9"\n',
363
+ }).pythonVersion,
364
+ ).toBe("3.9");
365
+ expect(
366
+ project({
367
+ ...TELEGRAM_PYTHON,
368
+ ".tool-versions": "nodejs 22.3.0\npython 3.13.0\n",
369
+ }).pythonVersion,
370
+ ).toBe("3.13");
371
+ expect(project(TELEGRAM_PYTHON).pythonVersion).toBeNull();
372
+ });
373
+
374
+ it("installs from the Python lockfiles, honouring uv and warning about the rest", () => {
375
+ const locked = (lockfile: string) => {
376
+ return project(
377
+ {
378
+ "pyproject.toml": '[project]\nname = "bot"\ndependencies = [\n "discord.py>=2.4",\n]\n',
379
+ "bot.py": 'import discord\nimport os\nos.getenv("DISCORD_TOKEN")\n',
380
+ },
381
+ [
382
+ lockfile,
383
+ ],
384
+ );
385
+ };
386
+
387
+ expect(locked("uv.lock").install).toEqual({
388
+ command: "uv sync --locked --no-dev",
389
+ source: "uv.lock",
390
+ });
391
+ expect(locked("uv.lock").packageManager).toBe(BotPackageManager.Uv);
392
+ expect(locked("uv.lock").warnings).not.toContain(BotInspectWarning.LockfileNotHonoured);
393
+ expect(locked("poetry.lock").install).toEqual({
394
+ command: "uv pip install .",
395
+ source: "poetry.lock",
396
+ });
397
+ expect(locked("poetry.lock").packageManager).toBe(BotPackageManager.Pip);
398
+ expect(locked("poetry.lock").warnings).toContain(BotInspectWarning.LockfileNotHonoured);
399
+ expect(locked("pdm.lock").warnings).toContain(BotInspectWarning.LockfileNotHonoured);
400
+ expect(
401
+ project({
402
+ Pipfile: '[packages]\ndiscord-py = "*"\n\n[requires]\npython_version = "3.11"\n',
403
+ "bot.py": "import discord\n",
404
+ }),
405
+ ).toMatchObject({
406
+ packageManager: BotPackageManager.Pipenv,
407
+ install: {
408
+ command: "pipenv install",
409
+ source: "Pipfile",
410
+ },
411
+ pythonVersion: "3.11",
412
+ });
413
+ expect(
414
+ project(TELEGRAM_PYTHON, [
415
+ "uv.lock",
416
+ ]).install?.source,
417
+ ).toBe("requirements.txt");
130
418
  });
131
419
 
132
420
  it("installs a pyproject project with pip", () => {
@@ -136,7 +424,7 @@ describe("detect on the templates", () => {
136
424
  });
137
425
 
138
426
  expect(result.install).toEqual({
139
- command: "pip install .",
427
+ command: "uv pip install .",
140
428
  source: "pyproject.toml",
141
429
  });
142
430
  expect(result.framework).toBe(BotFramework.DiscordPy);
@@ -148,7 +436,7 @@ describe("detect on the templates", () => {
148
436
  });
149
437
 
150
438
  describe("detect start commands", () => {
151
- it("follows the launcher's order: start script, main, known files, then the Procfile", () => {
439
+ it("follows the launcher's order: the Procfile, the start script, main, then the known files", () => {
152
440
  expect(
153
441
  project({
154
442
  "package.json": JSON.stringify({
@@ -172,16 +460,71 @@ describe("detect start commands", () => {
172
460
  });
173
461
  expect(
174
462
  project({
175
- "package.json": "{}",
463
+ "package.json": JSON.stringify({
464
+ scripts: {
465
+ start: "node index.js",
466
+ },
467
+ }),
468
+ "index.js": "",
176
469
  Procfile: "web: node server.js\nworker: node worker.js\n",
177
470
  }).start,
178
471
  ).toEqual({
179
472
  command: "node worker.js",
180
473
  source: "Procfile",
181
474
  });
475
+ expect(
476
+ project({
477
+ "requirements.txt": "python-telegram-bot==21.11.1\n",
478
+ "main.py": "",
479
+ Procfile: "bot: python -m bot\n",
480
+ }).start,
481
+ ).toEqual({
482
+ command: "python -m bot",
483
+ source: "Procfile",
484
+ });
485
+ });
486
+
487
+ it("starts the one Python package that has a __main__ module when no start file exists", () => {
488
+ expect(
489
+ project(
490
+ {
491
+ "requirements.txt": "python-telegram-bot==21.11.1\n",
492
+ },
493
+ [
494
+ "bot/__init__.py",
495
+ "bot/__main__.py",
496
+ "bot/handlers.py",
497
+ "tests/__main__.py",
498
+ ],
499
+ ).start,
500
+ ).toEqual({
501
+ command: "python -m bot",
502
+ source: "bot/__main__.py",
503
+ });
504
+ expect(
505
+ project(
506
+ {
507
+ "requirements.txt": "python-telegram-bot==21.11.1\n",
508
+ },
509
+ [
510
+ "bot/__main__.py",
511
+ "worker/__main__.py",
512
+ ],
513
+ ).start,
514
+ ).toBeNull();
515
+ expect(
516
+ project(
517
+ {
518
+ "requirements.txt": "python-telegram-bot==21.11.1\n",
519
+ },
520
+ [
521
+ ".venv/lib/__main__.py",
522
+ ],
523
+ ).start,
524
+ ).toBeNull();
182
525
  });
183
526
 
184
- it("warns when nothing starts and when node would run TypeScript directly", () => {
527
+ it("warns when nothing starts, and no longer about a TypeScript entry", () => {
185
528
  expect(
186
529
  project({
187
530
  "package.json": "{}",
@@ -192,16 +535,9 @@ describe("detect start commands", () => {
192
535
  "package.json": "{}",
193
536
  "index.ts": "",
194
537
  }).warnings,
195
- ).toContain(BotInspectWarning.TypescriptEntry);
196
- expect(
197
- project({
198
- "package.json": JSON.stringify({
199
- scripts: {
200
- start: "tsx src/index.ts",
201
- },
202
- }),
203
- }).warnings,
204
- ).not.toContain(BotInspectWarning.TypescriptEntry);
538
+ ).toEqual([
539
+ BotInspectWarning.NoLockfile,
540
+ ]);
205
541
  });
206
542
  });
207
543
 
@@ -240,7 +576,7 @@ describe("detect warnings", () => {
240
576
  ]);
241
577
  });
242
578
 
243
- it("flags committed secrets and dependency folders but not the templates for them", () => {
579
+ it("flags committed secrets, dependency folders and database files but not the templates for them", () => {
244
580
  expect(
245
581
  project(DISCORD_NODE, [
246
582
  "package-lock.json",
@@ -250,11 +586,21 @@ describe("detect warnings", () => {
250
586
  "node_modules/some/.env",
251
587
  ".venv/pyvenv.cfg",
252
588
  ".env.sample",
589
+ "data/bot.sqlite",
253
590
  ]).warnings,
254
591
  ).toEqual([
255
592
  BotInspectWarning.EnvCommitted,
256
593
  BotInspectWarning.NodeModulesCommitted,
257
594
  BotInspectWarning.VenvCommitted,
595
+ BotInspectWarning.DatabaseCommitted,
596
+ ]);
597
+ expect(
598
+ project(DISCORD_NODE, [
599
+ "package-lock.json",
600
+ "node_modules/sqlite3/test/fixture.db",
601
+ ]).warnings,
602
+ ).toEqual([
603
+ BotInspectWarning.NodeModulesCommitted,
258
604
  ]);
259
605
  });
260
606
 
@@ -438,6 +784,57 @@ describe("detect env suggestions", () => {
438
784
  ]);
439
785
  });
440
786
 
787
+ it("suggests the keys of a skipped .env after the example files, never its values", () => {
788
+ const result = detect({
789
+ files: [
790
+ {
791
+ path: "requirements.txt",
792
+ },
793
+ {
794
+ path: "main.py",
795
+ },
796
+ {
797
+ path: ".env.example",
798
+ },
799
+ ],
800
+ contents: {
801
+ "requirements.txt": "discord.py==2.7.1\n",
802
+ "main.py": 'import os\nimport discord\nos.getenv("LOG_CHANNEL")\n',
803
+ ".env.example": "PREFIX=!\n",
804
+ },
805
+ skippedEnv: "PREFIX=?\nMY_BOT_TOKEN=abc.def\nOWNER_ID=1234\nTZ=Asia/Riyadh\nLOG_CHANNEL=42\n",
806
+ });
807
+
808
+ expect(result.env).toEqual([
809
+ {
810
+ key: "PREFIX",
811
+ sensitive: false,
812
+ source: ".env.example",
813
+ example: "!",
814
+ },
815
+ {
816
+ key: "MY_BOT_TOKEN",
817
+ sensitive: true,
818
+ source: ".env",
819
+ example: null,
820
+ },
821
+ {
822
+ key: "OWNER_ID",
823
+ sensitive: false,
824
+ source: ".env",
825
+ example: null,
826
+ },
827
+ {
828
+ key: "LOG_CHANNEL",
829
+ sensitive: false,
830
+ source: ".env",
831
+ example: null,
832
+ },
833
+ ]);
834
+ expect(result.tokenKey).toBe("MY_BOT_TOKEN");
835
+ expect(result.warnings).not.toContain(BotInspectWarning.EnvCommitted);
836
+ });
837
+
441
838
  it("names the token key the code reads for its platform", () => {
442
839
  const result = project({
443
840
  "requirements.txt": "discord.py==2.7.1\n",