@serverkgg/bridge 0.1.5 → 0.1.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@serverkgg/bridge",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "Serverk game bridge and driver runtime, wire protocol, manifest schema, and maintainer CLI",
5
5
  "homepage": "https://serverk.gg",
6
6
  "license": "SEE LICENSE IN LICENSE.md",
@@ -39,9 +39,9 @@
39
39
  },
40
40
  "scripts": {
41
41
  "tsc": "tsc",
42
- "test": "bun test --parallel"
42
+ "test": "bun test --parallel=8"
43
43
  },
44
44
  "dependencies": {
45
- "zod": "4.6.4"
45
+ "zod": "4.6.5"
46
46
  }
47
47
  }
@@ -37,6 +37,14 @@ const CASES: readonly {
37
37
  line: "aiogram.exceptions.TelegramUnauthorizedError: Telegram server says - Unauthorized",
38
38
  cause: BridgeCrashCause.TokenInvalid,
39
39
  },
40
+ {
41
+ line: "pyrogram.errors.exceptions.bad_request_400.AccessTokenInvalid: Telegram says: [400 ACCESS_TOKEN_INVALID] - The bot access token is invalid",
42
+ cause: BridgeCrashCause.TokenInvalid,
43
+ },
44
+ {
45
+ line: "pyrogram.errors.exceptions.bad_request_400.AccessTokenExpired: Telegram says: [400 ACCESS_TOKEN_EXPIRED] - The bot token has expired",
46
+ cause: BridgeCrashCause.TokenInvalid,
47
+ },
40
48
  {
41
49
  line: "Error: Used disallowed intents",
42
50
  cause: BridgeCrashCause.IntentsDisallowed,
@@ -4,7 +4,7 @@ import { BridgeEventName } from "../protocol";
4
4
  export const BOT_CRASH_EVIDENCE: readonly BridgeEventPattern[] = [
5
5
  {
6
6
  match:
7
- /\[TokenInvalid\]|An invalid token was provided|LoginFailure: Improper token|\b401:? Unauthorized\b|\bInvalidToken\b|TelegramUnauthorizedError/,
7
+ /\[TokenInvalid\]|An invalid token was provided|LoginFailure: Improper token|\b401:? Unauthorized\b|\bInvalidToken\b|TelegramUnauthorizedError|\bACCESS_TOKEN_(?:INVALID|EXPIRED)\b/,
8
8
  emit: BridgeEventName.TokenInvalid,
9
9
  },
10
10
  {
@@ -174,6 +174,7 @@ export const BOT_FRAMEWORKS: readonly BotFrameworkSpec[] = [
174
174
  platform: BotPlatform.Telegram,
175
175
  packages: [
176
176
  "pyrogram",
177
+ "kurigram",
177
178
  ],
178
179
  modules: [
179
180
  "pyrogram",
@@ -3,6 +3,7 @@ export enum BotInspectWarning {
3
3
  NodeVersionUnmet = "node-version-unmet",
4
4
  LockfileNotHonoured = "lockfile-not-honoured",
5
5
  NoStart = "no-start",
6
+ WindowsCommand = "windows-command",
6
7
  NoManifest = "no-manifest",
7
8
  EnvCommitted = "env-committed",
8
9
  NodeModulesCommitted = "node-modules-committed",
@@ -35,8 +35,8 @@
35
35
  "expected": {
36
36
  "runtime": "python",
37
37
  "packageManager": "pip",
38
- "framework": null,
39
- "platform": null,
38
+ "framework": "pyrogram",
39
+ "platform": "telegram",
40
40
  "install": "uv pip install -r requirements.txt",
41
41
  "build": null,
42
42
  "start": "python -m bot",
@@ -32,8 +32,8 @@
32
32
  "expected": {
33
33
  "runtime": "python",
34
34
  "packageManager": "pip",
35
- "framework": null,
36
- "platform": null,
35
+ "framework": "pyrogram",
36
+ "platform": "telegram",
37
37
  "install": "uv pip install -r requirements.txt",
38
38
  "build": null,
39
39
  "start": "bash start",
@@ -28,6 +28,8 @@
28
28
  "nodeVersion": null,
29
29
  "pythonVersion": null,
30
30
  "tokenKey": null,
31
- "warnings": []
31
+ "warnings": [
32
+ "windows-command"
33
+ ]
32
34
  }
33
35
  }
@@ -28,8 +28,8 @@
28
28
  "expected": {
29
29
  "runtime": "python",
30
30
  "packageManager": "pip",
31
- "framework": null,
32
- "platform": null,
31
+ "framework": "pyrogram",
32
+ "platform": "telegram",
33
33
  "install": "uv pip install -r requirements.txt",
34
34
  "build": null,
35
35
  "start": "python src/main.py",
@@ -604,6 +604,24 @@ describe("detect warnings", () => {
604
604
  ]);
605
605
  });
606
606
 
607
+ it("warns when a start or build script runs a Windows-only command", () => {
608
+ const scripts = (start: string, build: string) => {
609
+ return project({
610
+ "package.json": JSON.stringify({
611
+ scripts: {
612
+ start,
613
+ build,
614
+ },
615
+ }),
616
+ "package-lock.json": "{}",
617
+ }).warnings;
618
+ };
619
+
620
+ expect(scripts("cls && node build/index.js", "cls && node build.js")).toContain(BotInspectWarning.WindowsCommand);
621
+ expect(scripts("node build/index.js", "CLS&&node build.js")).toContain(BotInspectWarning.WindowsCommand);
622
+ expect(scripts("node index.js", "tsc")).not.toContain(BotInspectWarning.WindowsCommand);
623
+ });
624
+
607
625
  it("flags two runtimes and picks the one whose framework it found", () => {
608
626
  const result = project({
609
627
  ...DISCORD_NODE,
@@ -630,6 +648,17 @@ describe("detect warnings", () => {
630
648
  expect(result.warnings).toContain(BotInspectWarning.TwoPlatforms);
631
649
  });
632
650
 
651
+ it("reads kurigram, the Pyrogram fork the corpus bots install, as Pyrogram on Telegram", () => {
652
+ const result = project({
653
+ "requirements.txt": "kurigram>=2.2.25\ntgcrypto\n",
654
+ "bot/__main__.py": "",
655
+ });
656
+
657
+ expect(result.framework).toBe(BotFramework.Pyrogram);
658
+ expect(result.platform).toBe(BotPlatform.Telegram);
659
+ expect(result.warnings).not.toContain(BotInspectWarning.TwoPlatforms);
660
+ });
661
+
633
662
  it("drops keys the platform owns and warns, but keeps quiet about keys the runtime gives anyway", () => {
634
663
  const result = project({
635
664
  ...DISCORD_NODE,
@@ -53,6 +53,12 @@ const NOT_A_BOT_PACKAGE: ReadonlySet<string> = new Set([
53
53
  "scripts",
54
54
  ]);
55
55
 
56
+ const WINDOWS_COMMANDS: ReadonlySet<string> = new Set([
57
+ "cls",
58
+ ]);
59
+
60
+ const COMMAND_SEPARATOR = /&&|\|\||;|\|/;
61
+
56
62
  const MANAGER_FIELD = /^(npm|pnpm|yarn|bun)@(\d+)/;
57
63
 
58
64
  const ENGINE_MANAGERS: readonly string[] = [
@@ -394,6 +400,18 @@ const frameworksOf = (
394
400
  return declared.length > 0 ? declared : specs.filter((spec) => spec.modules.some((name) => imports.has(name)));
395
401
  };
396
402
 
403
+ const runsWindowsCommand = (command: string | null) => {
404
+ if (command === null) {
405
+ return false;
406
+ }
407
+
408
+ return command.split(COMMAND_SEPARATOR).some((segment) => {
409
+ const program = segment.trim().split(/\s+/)[0] ?? "";
410
+
411
+ return WINDOWS_COMMANDS.has(program.toLowerCase());
412
+ });
413
+ };
414
+
397
415
  const procfileCommand = (text: string | null): DetectedCommand | null => {
398
416
  if (text === null) {
399
417
  return null;
@@ -1094,6 +1112,18 @@ export const detect = (input: DetectInput): DetectResult => {
1094
1112
  warnings.add(BotInspectWarning.NoStart);
1095
1113
  }
1096
1114
 
1115
+ const buildScript = recordOf(packageJson?.scripts)[DETECT_TABLE.node.buildScript];
1116
+
1117
+ const commands = [
1118
+ start?.command ?? null,
1119
+ build?.command ?? null,
1120
+ build?.source === DETECT_TABLE.node.manifest && typeof buildScript === "string" ? buildScript : null,
1121
+ ];
1122
+
1123
+ if (commands.some(runsWindowsCommand)) {
1124
+ warnings.add(BotInspectWarning.WindowsCommand);
1125
+ }
1126
+
1097
1127
  committedWarnings(paths, warnings);
1098
1128
 
1099
1129
  const env = envCollector(warnings);
@@ -5,6 +5,7 @@ import { parseStatus, probeMinecraftPing } from "./minecraftPing";
5
5
  const HOST = "127.0.0.1";
6
6
 
7
7
  const CLOSED_PORT = 59_441;
8
+ const CLOSED_HOST = "::1";
8
9
 
9
10
  const varInt = (value: number) => {
10
11
  const bytes: number[] = [];
@@ -294,7 +295,7 @@ describe("probeMinecraftPing", () => {
294
295
  it("rejects as unreachable when nothing listens on the port", async () => {
295
296
  const error = await probeMinecraftPing(
296
297
  {
297
- address: HOST,
298
+ address: CLOSED_HOST,
298
299
  port: CLOSED_PORT,
299
300
  },
300
301
  2000,
@@ -19,6 +19,7 @@ import { createRconWriter, type RconWriter } from "./rconWriter";
19
19
  const HOST = "127.0.0.1";
20
20
 
21
21
  const CLOSED_PORT = 59_433;
22
+ const CLOSED_HOST = "::1";
22
23
 
23
24
  const PASSWORD = "kX7-forbidden-in-errors";
24
25
 
@@ -1005,7 +1006,7 @@ describe("RconClient failures", () => {
1005
1006
 
1006
1007
  it("reports a port nothing listens on", async () => {
1007
1008
  const client = new RconClient({
1008
- host: HOST,
1009
+ host: CLOSED_HOST,
1009
1010
  port: CLOSED_PORT,
1010
1011
  password: PASSWORD,
1011
1012
  timeoutMs: 1000,
@@ -1475,7 +1476,7 @@ describe("RconClient secrecy", () => {
1475
1476
  });
1476
1477
 
1477
1478
  const unreachable = new RconClient({
1478
- host: HOST,
1479
+ host: CLOSED_HOST,
1479
1480
  port: CLOSED_PORT,
1480
1481
  password: PASSWORD,
1481
1482
  timeoutMs: 500,