pairpod-bot 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 (84) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +6 -0
  3. package/dist/access.d.ts +2 -0
  4. package/dist/access.js +14 -0
  5. package/dist/access.js.map +1 -0
  6. package/dist/agents.d.ts +7 -0
  7. package/dist/agents.js +10 -0
  8. package/dist/agents.js.map +1 -0
  9. package/dist/attach.d.ts +2 -0
  10. package/dist/attach.js +65 -0
  11. package/dist/attach.js.map +1 -0
  12. package/dist/bot.d.ts +1 -0
  13. package/dist/bot.js +357 -0
  14. package/dist/bot.js.map +1 -0
  15. package/dist/config.d.ts +18 -0
  16. package/dist/config.js +39 -0
  17. package/dist/config.js.map +1 -0
  18. package/dist/db.d.ts +2 -0
  19. package/dist/db.js +87 -0
  20. package/dist/db.js.map +1 -0
  21. package/dist/docker.d.ts +15 -0
  22. package/dist/docker.js +113 -0
  23. package/dist/docker.js.map +1 -0
  24. package/dist/env.d.ts +2 -0
  25. package/dist/env.js +36 -0
  26. package/dist/env.js.map +1 -0
  27. package/dist/errors.d.ts +7 -0
  28. package/dist/errors.js +25 -0
  29. package/dist/errors.js.map +1 -0
  30. package/dist/local/sessions.d.ts +5 -0
  31. package/dist/local/sessions.js +83 -0
  32. package/dist/local/sessions.js.map +1 -0
  33. package/dist/main.d.ts +1 -0
  34. package/dist/main.js +8 -0
  35. package/dist/main.js.map +1 -0
  36. package/dist/naming.d.ts +3 -0
  37. package/dist/naming.js +19 -0
  38. package/dist/naming.js.map +1 -0
  39. package/dist/network.d.ts +1 -0
  40. package/dist/network.js +11 -0
  41. package/dist/network.js.map +1 -0
  42. package/dist/notifier.d.ts +4 -0
  43. package/dist/notifier.js +47 -0
  44. package/dist/notifier.js.map +1 -0
  45. package/dist/notify.d.ts +2 -0
  46. package/dist/notify.js +19 -0
  47. package/dist/notify.js.map +1 -0
  48. package/dist/paths.d.ts +9 -0
  49. package/dist/paths.js +18 -0
  50. package/dist/paths.js.map +1 -0
  51. package/dist/routes/attach.d.ts +13 -0
  52. package/dist/routes/attach.js +49 -0
  53. package/dist/routes/attach.js.map +1 -0
  54. package/dist/server.d.ts +1 -0
  55. package/dist/server.js +51 -0
  56. package/dist/server.js.map +1 -0
  57. package/dist/ssh.d.ts +2 -0
  58. package/dist/ssh.js +84 -0
  59. package/dist/ssh.js.map +1 -0
  60. package/dist/store.d.ts +65 -0
  61. package/dist/store.js +337 -0
  62. package/dist/store.js.map +1 -0
  63. package/dist/targets/docker.d.ts +7 -0
  64. package/dist/targets/docker.js +14 -0
  65. package/dist/targets/docker.js.map +1 -0
  66. package/dist/targets/index.d.ts +4 -0
  67. package/dist/targets/index.js +33 -0
  68. package/dist/targets/index.js.map +1 -0
  69. package/dist/targets/ssh.d.ts +25 -0
  70. package/dist/targets/ssh.js +121 -0
  71. package/dist/targets/ssh.js.map +1 -0
  72. package/dist/targets/types.d.ts +15 -0
  73. package/dist/targets/types.js +2 -0
  74. package/dist/targets/types.js.map +1 -0
  75. package/dist/telegram-auth.d.ts +7 -0
  76. package/dist/telegram-auth.js +41 -0
  77. package/dist/telegram-auth.js.map +1 -0
  78. package/dist/vault.d.ts +4 -0
  79. package/dist/vault.js +57 -0
  80. package/dist/vault.js.map +1 -0
  81. package/miniapp/index.html +597 -0
  82. package/miniapp/ssh.html +251 -0
  83. package/package.json +44 -0
  84. package/scripts/fix-pty.cjs +15 -0
@@ -0,0 +1,14 @@
1
+ import { execInContainer, attachExec } from "../docker.js";
2
+ export class DockerTarget {
3
+ ref;
4
+ constructor(ref) {
5
+ this.ref = ref;
6
+ }
7
+ exec(cmd) {
8
+ return execInContainer(this.ref, cmd);
9
+ }
10
+ openPty(cmd, cols, rows) {
11
+ return attachExec(this.ref, cmd, cols, rows);
12
+ }
13
+ }
14
+ //# sourceMappingURL=docker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docker.js","sourceRoot":"","sources":["../../src/targets/docker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG3D,MAAM,OAAO,YAAY;IACM;IAA7B,YAA6B,GAAW;QAAX,QAAG,GAAH,GAAG,CAAQ;IAAG,CAAC;IAE5C,IAAI,CAAC,GAAa;QAChB,OAAO,eAAe,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IACxC,CAAC;IAED,OAAO,CAAC,GAAa,EAAE,IAAY,EAAE,IAAY;QAC/C,OAAO,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC/C,CAAC;CACF"}
@@ -0,0 +1,4 @@
1
+ import type { PodTarget } from "./types.js";
2
+ import type { PodRow } from "../store.js";
3
+ export declare function targetForPod(p: PodRow): PodTarget;
4
+ export declare function disposeTarget(podId: string): Promise<void>;
@@ -0,0 +1,33 @@
1
+ import { getDb } from "../db.js";
2
+ import { DockerTarget } from "./docker.js";
3
+ import { SshTarget } from "./ssh.js";
4
+ const sshPool = new Map();
5
+ export function targetForPod(p) {
6
+ if (p.kind !== "ssh")
7
+ return new DockerTarget(`pairpod-${p.id}`);
8
+ let target = sshPool.get(p.id);
9
+ if (!target) {
10
+ target = new SshTarget({
11
+ host: p.ssh_host,
12
+ port: p.ssh_port ?? 22,
13
+ username: p.ssh_user,
14
+ auth: p.ssh_auth,
15
+ keyPath: p.ssh_key_path ?? undefined,
16
+ vaultRef: p.ssh_vault_ref ?? undefined,
17
+ hostFingerprint: p.host_fingerprint ?? undefined,
18
+ onFingerprint: (fp) => {
19
+ getDb().prepare("UPDATE pods SET host_fingerprint = ? WHERE id = ?").run(fp, p.id);
20
+ },
21
+ });
22
+ sshPool.set(p.id, target);
23
+ }
24
+ return target;
25
+ }
26
+ export async function disposeTarget(podId) {
27
+ const target = sshPool.get(podId);
28
+ if (target) {
29
+ await target.dispose();
30
+ sshPool.delete(podId);
31
+ }
32
+ }
33
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/targets/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAAE,SAAS,EAAgB,MAAM,UAAU,CAAC;AAGnD,MAAM,OAAO,GAAG,IAAI,GAAG,EAAqB,CAAC;AAE7C,MAAM,UAAU,YAAY,CAAC,CAAS;IACpC,IAAI,CAAC,CAAC,IAAI,KAAK,KAAK;QAAE,OAAO,IAAI,YAAY,CAAC,WAAW,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACjE,IAAI,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC/B,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,GAAG,IAAI,SAAS,CAAC;YACrB,IAAI,EAAE,CAAC,CAAC,QAAkB;YAC1B,IAAI,EAAE,CAAC,CAAC,QAAQ,IAAI,EAAE;YACtB,QAAQ,EAAE,CAAC,CAAC,QAAkB;YAC9B,IAAI,EAAE,CAAC,CAAC,QAAmB;YAC3B,OAAO,EAAE,CAAC,CAAC,YAAY,IAAI,SAAS;YACpC,QAAQ,EAAE,CAAC,CAAC,aAAa,IAAI,SAAS;YACtC,eAAe,EAAE,CAAC,CAAC,gBAAgB,IAAI,SAAS;YAChD,aAAa,EAAE,CAAC,EAAE,EAAE,EAAE;gBACpB,KAAK,EAAE,CAAC,OAAO,CAAC,mDAAmD,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;YACrF,CAAC;SACF,CAAC,CAAC;QACH,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;IAC5B,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAAa;IAC/C,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAClC,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;QACvB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACxB,CAAC;AACH,CAAC"}
@@ -0,0 +1,25 @@
1
+ import type { PodTarget, ExecResult, PtySession } from "./types.js";
2
+ export type SshAuth = "agent" | "key_path" | "vault";
3
+ export interface SshConnInfo {
4
+ host: string;
5
+ port: number;
6
+ username: string;
7
+ auth: SshAuth;
8
+ keyPath?: string;
9
+ vaultRef?: string;
10
+ hostFingerprint?: string;
11
+ onFingerprint?: (fingerprint: string) => void;
12
+ }
13
+ export declare function shellQuote(arg: string): string;
14
+ export declare function shellJoin(cmd: string[]): string;
15
+ export declare class SshTarget implements PodTarget {
16
+ private readonly info;
17
+ private client;
18
+ private connecting;
19
+ constructor(info: SshConnInfo);
20
+ private buildConfig;
21
+ private connect;
22
+ exec(cmd: string[]): Promise<ExecResult>;
23
+ openPty(cmd: string[], cols: number, rows: number): Promise<PtySession>;
24
+ dispose(): Promise<void>;
25
+ }
@@ -0,0 +1,121 @@
1
+ import fs from "node:fs";
2
+ import crypto from "node:crypto";
3
+ import { Client } from "ssh2";
4
+ import { vaultGet } from "../vault.js";
5
+ export function shellQuote(arg) {
6
+ return `'${arg.replace(/'/g, `'\\''`)}'`;
7
+ }
8
+ export function shellJoin(cmd) {
9
+ return cmd.map(shellQuote).join(" ");
10
+ }
11
+ function fingerprintOf(hostKey) {
12
+ return "SHA256:" + crypto.createHash("sha256").update(hostKey).digest("base64").replace(/=+$/, "");
13
+ }
14
+ export class SshTarget {
15
+ info;
16
+ client = null;
17
+ connecting = null;
18
+ constructor(info) {
19
+ this.info = info;
20
+ }
21
+ buildConfig() {
22
+ const cfg = {
23
+ host: this.info.host,
24
+ port: this.info.port,
25
+ username: this.info.username,
26
+ keepaliveInterval: 15000,
27
+ hostVerifier: (hostKey) => {
28
+ const fp = fingerprintOf(hostKey);
29
+ if (this.info.hostFingerprint)
30
+ return fp === this.info.hostFingerprint;
31
+ this.info.onFingerprint?.(fp);
32
+ return true;
33
+ },
34
+ };
35
+ if (this.info.auth === "agent") {
36
+ cfg.agent = process.env.SSH_AUTH_SOCK;
37
+ }
38
+ else if (this.info.auth === "key_path") {
39
+ cfg.privateKey = fs.readFileSync(this.info.keyPath);
40
+ if (this.info.vaultRef) {
41
+ const secret = JSON.parse(vaultGet(this.info.vaultRef));
42
+ if (secret.passphrase)
43
+ cfg.passphrase = secret.passphrase;
44
+ }
45
+ }
46
+ else {
47
+ const secret = JSON.parse(vaultGet(this.info.vaultRef));
48
+ cfg.privateKey = secret.privateKey;
49
+ if (secret.passphrase)
50
+ cfg.passphrase = secret.passphrase;
51
+ }
52
+ return cfg;
53
+ }
54
+ connect() {
55
+ if (this.client)
56
+ return Promise.resolve(this.client);
57
+ if (this.connecting)
58
+ return this.connecting;
59
+ this.connecting = new Promise((resolve, reject) => {
60
+ const conn = new Client();
61
+ conn.on("ready", () => {
62
+ this.client = conn;
63
+ resolve(conn);
64
+ });
65
+ conn.on("error", (e) => {
66
+ this.connecting = null;
67
+ reject(e);
68
+ });
69
+ conn.on("close", () => {
70
+ this.client = null;
71
+ this.connecting = null;
72
+ });
73
+ conn.connect(this.buildConfig());
74
+ });
75
+ return this.connecting;
76
+ }
77
+ async exec(cmd) {
78
+ const conn = await this.connect();
79
+ return new Promise((resolve, reject) => {
80
+ conn.exec(shellJoin(cmd), (err, stream) => {
81
+ if (err)
82
+ return reject(err);
83
+ let stdout = "";
84
+ let stderr = "";
85
+ stream.on("data", (d) => {
86
+ stdout += d.toString();
87
+ });
88
+ stream.stderr.on("data", (d) => {
89
+ stderr += d.toString();
90
+ });
91
+ stream.on("close", (code) => {
92
+ resolve({ stdout, stderr, exitCode: code ?? 0 });
93
+ });
94
+ stream.on("error", reject);
95
+ });
96
+ });
97
+ }
98
+ async openPty(cmd, cols, rows) {
99
+ const conn = await this.connect();
100
+ return new Promise((resolve, reject) => {
101
+ conn.exec(shellJoin(cmd), { pty: { cols, rows, term: "xterm-256color" } }, (err, stream) => {
102
+ if (err)
103
+ return reject(err);
104
+ resolve({
105
+ stream: stream,
106
+ resize: (c, r) => {
107
+ stream.setWindow(r, c, 0, 0);
108
+ },
109
+ });
110
+ });
111
+ });
112
+ }
113
+ async dispose() {
114
+ if (this.client) {
115
+ this.client.end();
116
+ this.client = null;
117
+ }
118
+ this.connecting = null;
119
+ }
120
+ }
121
+ //# sourceMappingURL=ssh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssh.js","sourceRoot":"","sources":["../../src/targets/ssh.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { Duplex } from "node:stream";
2
+ export interface ExecResult {
3
+ stdout: string;
4
+ stderr: string;
5
+ exitCode: number;
6
+ }
7
+ export interface PtySession {
8
+ stream: Duplex;
9
+ resize(cols: number, rows: number): Promise<void> | void;
10
+ }
11
+ export interface PodTarget {
12
+ exec(cmd: string[]): Promise<ExecResult>;
13
+ openPty(cmd: string[], cols: number, rows: number): Promise<PtySession>;
14
+ dispose?(): Promise<void>;
15
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/targets/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,7 @@
1
+ export interface InitDataResult {
2
+ ok: boolean;
3
+ userId?: number;
4
+ username?: string;
5
+ reason?: string;
6
+ }
7
+ export declare function validateInitData(initData: string, botToken: string, maxAgeSec: number): InitDataResult;
@@ -0,0 +1,41 @@
1
+ import crypto from "node:crypto";
2
+ export function validateInitData(initData, botToken, maxAgeSec) {
3
+ if (!initData)
4
+ return { ok: false, reason: "empty" };
5
+ if (!botToken)
6
+ return { ok: false, reason: "no_token" };
7
+ const params = new URLSearchParams(initData);
8
+ const hash = params.get("hash");
9
+ if (!hash)
10
+ return { ok: false, reason: "no_hash" };
11
+ params.delete("hash");
12
+ const dataCheckString = [...params.entries()]
13
+ .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
14
+ .map(([k, v]) => `${k}=${v}`)
15
+ .join("\n");
16
+ const secretKey = crypto.createHmac("sha256", "WebAppData").update(botToken).digest();
17
+ const computed = crypto.createHmac("sha256", secretKey).update(dataCheckString).digest("hex");
18
+ const a = Buffer.from(computed, "hex");
19
+ const b = Buffer.from(hash, "hex");
20
+ if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
21
+ return { ok: false, reason: "bad_hash" };
22
+ }
23
+ const authDate = Number(params.get("auth_date"));
24
+ if (maxAgeSec > 0 && authDate > 0) {
25
+ const ageSec = Date.now() / 1000 - authDate;
26
+ if (ageSec > maxAgeSec)
27
+ return { ok: false, reason: "expired" };
28
+ }
29
+ let userId;
30
+ let username;
31
+ try {
32
+ const user = JSON.parse(params.get("user") ?? "{}");
33
+ if (typeof user.id === "number")
34
+ userId = user.id;
35
+ if (typeof user.username === "string")
36
+ username = user.username;
37
+ }
38
+ catch { }
39
+ return { ok: true, userId, username };
40
+ }
41
+ //# sourceMappingURL=telegram-auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telegram-auth.js","sourceRoot":"","sources":["../src/telegram-auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export declare function vaultEnabled(): boolean;
2
+ export declare function vaultPut(plaintext: string): string;
3
+ export declare function vaultGet(ref: string): string;
4
+ export declare function vaultRemove(ref: string): void;
package/dist/vault.js ADDED
@@ -0,0 +1,57 @@
1
+ import crypto from "node:crypto";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { paths } from "./paths.js";
5
+ const VAULT_DIR = paths.vault;
6
+ function masterKey() {
7
+ const b64 = process.env.PAIRPOD_VAULT_KEY;
8
+ if (!b64)
9
+ return null;
10
+ const key = Buffer.from(b64, "base64");
11
+ if (key.length !== 32) {
12
+ throw new Error("PAIRPOD_VAULT_KEY must decode to 32 bytes (base64)");
13
+ }
14
+ return key;
15
+ }
16
+ export function vaultEnabled() {
17
+ return masterKey() !== null;
18
+ }
19
+ function refPath(ref) {
20
+ return path.join(VAULT_DIR, `${ref}.json`);
21
+ }
22
+ export function vaultPut(plaintext) {
23
+ const key = masterKey();
24
+ if (!key)
25
+ throw new Error("vault disabled (PAIRPOD_VAULT_KEY unset)");
26
+ const ref = crypto.randomUUID();
27
+ const iv = crypto.randomBytes(12);
28
+ const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
29
+ const data = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
30
+ const entry = {
31
+ iv: iv.toString("base64"),
32
+ tag: cipher.getAuthTag().toString("base64"),
33
+ data: data.toString("base64"),
34
+ };
35
+ fs.mkdirSync(VAULT_DIR, { recursive: true, mode: 0o700 });
36
+ fs.writeFileSync(refPath(ref), JSON.stringify(entry), { mode: 0o600 });
37
+ return ref;
38
+ }
39
+ export function vaultGet(ref) {
40
+ const key = masterKey();
41
+ if (!key)
42
+ throw new Error("vault disabled (PAIRPOD_VAULT_KEY unset)");
43
+ const entry = JSON.parse(fs.readFileSync(refPath(ref), "utf8"));
44
+ const decipher = crypto.createDecipheriv("aes-256-gcm", key, Buffer.from(entry.iv, "base64"));
45
+ decipher.setAuthTag(Buffer.from(entry.tag, "base64"));
46
+ return Buffer.concat([
47
+ decipher.update(Buffer.from(entry.data, "base64")),
48
+ decipher.final(),
49
+ ]).toString("utf8");
50
+ }
51
+ export function vaultRemove(ref) {
52
+ try {
53
+ fs.rmSync(refPath(ref));
54
+ }
55
+ catch { }
56
+ }
57
+ //# sourceMappingURL=vault.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vault.js","sourceRoot":"","sources":["../src/vault.ts"],"names":[],"mappings":"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"}