playit-reversed 0.1.0-beta.2 → 0.1.0-beta.3

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.
@@ -0,0 +1,44 @@
1
+ /**
2
+ * AUTO-GENERATED FILE - DO NOT EDIT
3
+ * Generated at: ${new Date().toISOString()}
4
+ *
5
+ * Regenerate with: bun run playit:generate
6
+ */
7
+ /** All available agent IDs */
8
+ export type AgentId = string;
9
+ /** All available agent names */
10
+ export type AgentName = string;
11
+ /** Agent identifier (property name) */
12
+ export type AgentKey = string;
13
+ /**
14
+ * Reference to an agent with methods
15
+ */
16
+ export declare class AgentRef {
17
+ readonly id: AgentId;
18
+ readonly name: AgentName;
19
+ readonly clientIp: string;
20
+ readonly tunnelIp: string;
21
+ readonly version: string;
22
+ readonly os: string;
23
+ readonly status: string;
24
+ constructor(id: string, name: string, clientIp: string, tunnelIp: string, version: string, os: string, status: string);
25
+ /**
26
+ * Get tunnels for this agent
27
+ * TODO: Implement when tunnel parsing is ready
28
+ */
29
+ getTunnels(): Promise<any[]>;
30
+ /**
31
+ * Create a new tunnel for this agent
32
+ * TODO: Implement when tunnel API is ready
33
+ */
34
+ createTunnel(options: {
35
+ name: string;
36
+ localPort: number;
37
+ protocol?: "tcp" | "udp" | "both";
38
+ }): Promise<void>;
39
+ /**
40
+ * Delete this agent
41
+ * TODO: Implement when agent deletion API is ready
42
+ */
43
+ delete(): Promise<void>;
44
+ }
package/dist/cli.js CHANGED
@@ -611,7 +611,77 @@ var betterFetch = async (url, options) => {
611
611
  };
612
612
 
613
613
  // src/parsers/agents.ts
614
+ function extractRemixContext(html) {
615
+ const match = html.match(/<script>window\.__remixContext = ([\s\S]*?);<\/script>/);
616
+ if (!match?.[1])
617
+ return null;
618
+ try {
619
+ return JSON.parse(match[1]);
620
+ } catch {
621
+ return null;
622
+ }
623
+ }
624
+ function parsePlayItHtml(html) {
625
+ const context = extractRemixContext(html);
626
+ if (!context) {
627
+ throw new Error("Could not find Remix context in HTML");
628
+ }
629
+ const accountData = context.state.loaderData["routes/account"];
630
+ const agents = accountData.agents.agents.map((agent) => ({
631
+ id: agent.id,
632
+ name: agent.name,
633
+ clientIp: agent.status.data.client_addr,
634
+ tunnelIp: agent.status.data.tunnel_addr,
635
+ version: agent.agent_version.version,
636
+ os: agent.agent_version.platform,
637
+ status: agent.status.state
638
+ }));
639
+ const tunnels = accountData.tunnels.tunnels.map((tunnel) => ({
640
+ id: tunnel.id,
641
+ name: tunnel.name,
642
+ tunnelType: tunnel.tunnel_type,
643
+ portType: tunnel.port_type,
644
+ portCount: tunnel.port_count,
645
+ alloc: {
646
+ status: tunnel.alloc.status,
647
+ id: tunnel.alloc.data.id,
648
+ ipHostname: tunnel.alloc.data.ip_hostname,
649
+ staticIp4: tunnel.alloc.data.static_ip4,
650
+ staticIp6: tunnel.alloc.data.static_ip6,
651
+ assignedDomain: tunnel.alloc.data.assigned_domain,
652
+ assignedSrv: tunnel.alloc.data.assigned_srv,
653
+ tunnelIp: tunnel.alloc.data.tunnel_ip,
654
+ portStart: tunnel.alloc.data.port_start,
655
+ portEnd: tunnel.alloc.data.port_end,
656
+ ipType: tunnel.alloc.data.ip_type,
657
+ region: tunnel.alloc.data.region
658
+ },
659
+ origin: {
660
+ agentId: tunnel.origin.data.agent_id,
661
+ agentName: tunnel.origin.data.agent_name,
662
+ localIp: tunnel.origin.data.local_ip,
663
+ localPort: tunnel.origin.data.local_port
664
+ },
665
+ domain: tunnel.domain,
666
+ active: tunnel.active,
667
+ region: tunnel.region,
668
+ proxyProtocol: tunnel.proxy_protocol
669
+ }));
670
+ return { agents, tunnels };
671
+ }
614
672
  function parseAgentsHtml(html) {
673
+ const context = extractRemixContext(html);
674
+ if (context) {
675
+ return context.state.loaderData["routes/account"].agents.agents.map((agent) => ({
676
+ id: agent.id,
677
+ name: agent.name,
678
+ clientIp: agent.status.data.client_addr,
679
+ tunnelIp: agent.status.data.tunnel_addr,
680
+ version: agent.agent_version.version,
681
+ os: agent.agent_version.platform,
682
+ status: agent.status.state
683
+ }));
684
+ }
615
685
  const table = html.match(/<table class="row-links with-spacing">[\s\S]*?<\/table>/)?.[0];
616
686
  if (!table) {
617
687
  throw new Error("No table found in HTML");
@@ -622,6 +692,43 @@ function parseAgentsHtml(html) {
622
692
  }
623
693
  return rows.map(parseAgentRow);
624
694
  }
695
+ function parseTunnelsHtml(html) {
696
+ const context = extractRemixContext(html);
697
+ if (!context) {
698
+ throw new Error("Could not find Remix context in HTML");
699
+ }
700
+ return context.state.loaderData["routes/account"].tunnels.tunnels.map((tunnel) => ({
701
+ id: tunnel.id,
702
+ name: tunnel.name,
703
+ tunnelType: tunnel.tunnel_type,
704
+ portType: tunnel.port_type,
705
+ portCount: tunnel.port_count,
706
+ alloc: {
707
+ status: tunnel.alloc.status,
708
+ id: tunnel.alloc.data.id,
709
+ ipHostname: tunnel.alloc.data.ip_hostname,
710
+ staticIp4: tunnel.alloc.data.static_ip4,
711
+ staticIp6: tunnel.alloc.data.static_ip6,
712
+ assignedDomain: tunnel.alloc.data.assigned_domain,
713
+ assignedSrv: tunnel.alloc.data.assigned_srv,
714
+ tunnelIp: tunnel.alloc.data.tunnel_ip,
715
+ portStart: tunnel.alloc.data.port_start,
716
+ portEnd: tunnel.alloc.data.port_end,
717
+ ipType: tunnel.alloc.data.ip_type,
718
+ region: tunnel.alloc.data.region
719
+ },
720
+ origin: {
721
+ agentId: tunnel.origin.data.agent_id,
722
+ agentName: tunnel.origin.data.agent_name,
723
+ localIp: tunnel.origin.data.local_ip,
724
+ localPort: tunnel.origin.data.local_port
725
+ },
726
+ domain: tunnel.domain,
727
+ active: tunnel.active,
728
+ region: tunnel.region,
729
+ proxyProtocol: tunnel.proxy_protocol
730
+ }));
731
+ }
625
732
  function parseAgentRow(row) {
626
733
  const id = row.match(/href="\/account\/agents\/([a-f0-9-]+)"/)?.[1] ?? "";
627
734
  const name = row.match(/<td[^>]*><a[^>]*>([^<]+)<span/)?.[1]?.trim() ?? "";
@@ -14265,12 +14372,12 @@ class PlayIt {
14265
14372
  }
14266
14373
  return new PlayIt(options);
14267
14374
  }
14268
- async fetchAgents() {
14375
+ async fetchAll() {
14269
14376
  const { data, error: error48 } = await this.$fetch("/account/agents");
14270
14377
  if (error48) {
14271
- throw new Error("Failed to get agents: " + error48.message);
14378
+ throw new Error("Failed to fetch PlayIt data: " + error48.message);
14272
14379
  }
14273
- return parseAgentsHtml(data);
14380
+ return parsePlayItHtml(data);
14274
14381
  }
14275
14382
  }
14276
14383
 
@@ -14334,60 +14441,126 @@ async function setup() {
14334
14441
  console.log(`✓ Token found
14335
14442
  `);
14336
14443
  }
14337
- console.log("Fetching agents from PlayIt...");
14444
+ console.log("Fetching data from PlayIt...");
14338
14445
  const client = PlayIt.create({
14339
14446
  authorizationToken: token,
14340
14447
  _skipCodegenCheck: true
14341
14448
  });
14342
14449
  try {
14343
- const agents = await client.fetchAgents();
14344
- console.log(`✓ Found ${agents.length} agent(s)
14345
- `);
14346
- for (const agent of agents) {
14347
- console.log(` • ${agent.name} (${agent.status})`);
14450
+ const data = await client.fetchAll();
14451
+ console.log(`✓ Found ${data.agents.length} agent(s)`);
14452
+ for (const agent of data.agents) {
14453
+ const agentTunnels = data.tunnels.filter((t) => t.origin.agentId === agent.id);
14454
+ console.log(` • ${agent.name} (${agent.status}) - ${agentTunnels.length} tunnel(s)`);
14348
14455
  }
14349
- console.log();
14350
- fs2.writeFileSync(DATA_FILE, JSON.stringify({ agents, updatedAt: new Date().toISOString() }, null, 2));
14351
- await generateTypes(agents);
14456
+ console.log(`
14457
+ Found ${data.tunnels.length} tunnel(s)`);
14458
+ for (const tunnel of data.tunnels) {
14459
+ console.log(` • ${tunnel.name} (${tunnel.portType}) → ${tunnel.origin.localIp}:${tunnel.origin.localPort}`);
14460
+ }
14461
+ fs2.writeFileSync(DATA_FILE, JSON.stringify({
14462
+ ...data,
14463
+ updatedAt: new Date().toISOString()
14464
+ }, null, 2));
14465
+ await generateTypes(data);
14352
14466
  console.log(`
14353
14467
  ✅ Setup complete!
14354
14468
  `);
14355
14469
  console.log("Usage:");
14356
14470
  console.log(` import { playit } from "./generated/playit";
14357
14471
  `);
14472
+ console.log(" // Access agents");
14358
14473
  console.log(" const agent = playit.agents.capaodocorvo_srv;");
14359
- console.log(` console.log(agent.id, agent.status);
14474
+ console.log(` console.log(agent.tunnels);
14475
+ `);
14476
+ console.log(" // Access tunnels directly");
14477
+ console.log(" const tunnel = playit.tunnels.SSH;");
14478
+ console.log(` console.log(tunnel.alloc.assignedDomain);
14360
14479
  `);
14361
14480
  } catch (error48) {
14362
- console.error("❌ Failed to fetch agents:", error48);
14481
+ console.error("❌ Failed to fetch data:", error48);
14363
14482
  process.exit(1);
14364
14483
  }
14365
14484
  }
14366
- async function generateTypes(agents) {
14367
- if (!agents) {
14485
+ async function generateTypes(data) {
14486
+ if (!data) {
14368
14487
  if (!fs2.existsSync(DATA_FILE)) {
14369
14488
  console.error("❌ No data found. Run 'bun run playit:setup' first.");
14370
14489
  process.exit(1);
14371
14490
  }
14372
- const data = JSON.parse(fs2.readFileSync(DATA_FILE, "utf-8"));
14373
- agents = data.agents;
14491
+ data = JSON.parse(fs2.readFileSync(DATA_FILE, "utf-8"));
14374
14492
  }
14493
+ const { agents, tunnels } = data;
14375
14494
  if (!agents || agents.length === 0) {
14376
14495
  console.error("❌ No agents found");
14377
14496
  process.exit(1);
14378
14497
  }
14379
14498
  const toIdentifier = (name) => name.replace(/[^a-zA-Z0-9]/g, "_");
14499
+ const tunnelKeyCounts = new Map;
14500
+ const getTunnelKey = (name) => {
14501
+ const base = toIdentifier(name);
14502
+ const count = tunnelKeyCounts.get(base) || 0;
14503
+ tunnelKeyCounts.set(base, count + 1);
14504
+ return count === 0 ? base : `${base}_${count + 1}`;
14505
+ };
14506
+ const tunnelKeys = tunnels.map((t) => getTunnelKey(t.name));
14380
14507
  const agentIds = agents.map((a) => `"${a.id}"`).join(" | ");
14381
14508
  const agentNames = agents.map((a) => `"${a.name}"`).join(" | ");
14382
- const agentEntries = agents.map((a) => ` "${a.name}": "${a.id}"`).join(`,
14509
+ const agentKeys = agents.map((a) => `"${toIdentifier(a.name)}"`).join(" | ");
14510
+ const tunnelIds = tunnels.length > 0 ? tunnels.map((t) => `"${t.id}"`).join(" | ") : "never";
14511
+ const tunnelNames = tunnels.length > 0 ? [...new Set(tunnels.map((t) => `"${t.name}"`))].join(" | ") : "never";
14512
+ const tunnelKeyTypes = tunnels.length > 0 ? tunnelKeys.map((k) => `"${k}"`).join(" | ") : "never";
14513
+ const tunnelInstances = tunnels.map((t, i) => ` ${tunnelKeys[i]}: {
14514
+ id: "${t.id}" as const,
14515
+ name: "${t.name}" as const,
14516
+ tunnelType: ${t.tunnelType ? `"${t.tunnelType}"` : "null"},
14517
+ portType: "${t.portType}" as const,
14518
+ portCount: ${t.portCount},
14519
+ alloc: {
14520
+ status: "${t.alloc.status}",
14521
+ id: "${t.alloc.id}",
14522
+ ipHostname: "${t.alloc.ipHostname}",
14523
+ staticIp4: "${t.alloc.staticIp4}",
14524
+ staticIp6: "${t.alloc.staticIp6}",
14525
+ assignedDomain: "${t.alloc.assignedDomain}",
14526
+ assignedSrv: ${t.alloc.assignedSrv ? `"${t.alloc.assignedSrv}"` : "null"},
14527
+ tunnelIp: "${t.alloc.tunnelIp}",
14528
+ portStart: ${t.alloc.portStart},
14529
+ portEnd: ${t.alloc.portEnd},
14530
+ ipType: "${t.alloc.ipType}",
14531
+ region: "${t.alloc.region}",
14532
+ },
14533
+ origin: {
14534
+ agentId: "${t.origin.agentId}" as AgentId,
14535
+ agentName: "${t.origin.agentName}" as AgentName,
14536
+ localIp: "${t.origin.localIp}",
14537
+ localPort: ${t.origin.localPort},
14538
+ },
14539
+ domain: ${t.domain ? `"${t.domain}"` : "null"},
14540
+ active: ${t.active},
14541
+ region: "${t.region}",
14542
+ proxyProtocol: ${t.proxyProtocol ? `"${t.proxyProtocol}"` : "null"},
14543
+ }`).join(`,
14383
14544
  `);
14384
- const agentInstances = agents.map((a) => ` ${toIdentifier(a.name)}: new AgentRef("${a.id}", "${a.name}", "${a.clientIp}", "${a.tunnelIp}", "${a.version}", "${a.os}", "${a.status}")`).join(`,
14545
+ const agentInstances = agents.map((a) => {
14546
+ const agentTunnelRefs = tunnels.map((t, i) => ({ tunnel: t, key: tunnelKeys[i] })).filter(({ tunnel }) => tunnel.origin.agentId === a.id).map(({ key }) => `tunnels.${key}`).join(", ");
14547
+ return ` ${toIdentifier(a.name)}: {
14548
+ id: "${a.id}" as const,
14549
+ name: "${a.name}" as const,
14550
+ clientIp: "${a.clientIp}",
14551
+ tunnelIp: "${a.tunnelIp}",
14552
+ version: "${a.version}",
14553
+ os: "${a.os}" as const,
14554
+ status: "${a.status}" as const,
14555
+ tunnels: [${agentTunnelRefs}] as const,
14556
+ }`;
14557
+ }).join(`,
14385
14558
  `);
14386
14559
  const content = `/**
14387
14560
  * AUTO-GENERATED FILE - DO NOT EDIT
14388
14561
  * Generated at: ${new Date().toISOString()}
14389
14562
  *
14390
- * Regenerate with: bun run playit:generate
14563
+ * Regenerate with: bun run playit:setup
14391
14564
  */
14392
14565
 
14393
14566
  import { spawn } from "child_process";
@@ -14401,82 +14574,41 @@ export type AgentId = ${agentIds};
14401
14574
  export type AgentName = ${agentNames};
14402
14575
 
14403
14576
  /** Agent identifier (property name) */
14404
- export type AgentKey = ${agents.map((a) => `"${toIdentifier(a.name)}"`).join(" | ")};
14577
+ export type AgentKey = ${agentKeys};
14405
14578
 
14406
- // ============ Agent Reference ============
14579
+ /** All available tunnel IDs */
14580
+ export type TunnelId = ${tunnelIds};
14407
14581
 
14408
- /**
14409
- * Reference to an agent with methods
14410
- */
14411
- export class AgentRef {
14412
- readonly id: AgentId;
14413
- readonly name: AgentName;
14414
- readonly clientIp: string;
14415
- readonly tunnelIp: string;
14416
- readonly version: string;
14417
- readonly os: string;
14418
- readonly status: string;
14582
+ /** All available tunnel names */
14583
+ export type TunnelName = ${tunnelNames};
14419
14584
 
14420
- constructor(
14421
- id: string,
14422
- name: string,
14423
- clientIp: string,
14424
- tunnelIp: string,
14425
- version: string,
14426
- os: string,
14427
- status: string
14428
- ) {
14429
- this.id = id as AgentId;
14430
- this.name = name as AgentName;
14431
- this.clientIp = clientIp;
14432
- this.tunnelIp = tunnelIp;
14433
- this.version = version;
14434
- this.os = os;
14435
- this.status = status;
14436
- }
14585
+ /** Tunnel identifier (property name) */
14586
+ export type TunnelKey = ${tunnelKeyTypes};
14437
14587
 
14438
- /**
14439
- * Get tunnels for this agent
14440
- * TODO: Implement when tunnel parsing is ready
14441
- */
14442
- async getTunnels(): Promise<any[]> {
14443
- // TODO: Implement tunnel fetching
14444
- throw new Error("Not implemented yet - tunnels coming soon");
14445
- }
14588
+ // ============ Tunnels ============
14446
14589
 
14447
- /**
14448
- * Create a new tunnel for this agent
14449
- * TODO: Implement when tunnel API is ready
14450
- */
14451
- async createTunnel(options: {
14452
- name: string;
14453
- localPort: number;
14454
- protocol?: "tcp" | "udp" | "both";
14455
- }): Promise<void> {
14456
- // TODO: Implement tunnel creation
14457
- throw new Error("Not implemented yet - tunnels coming soon");
14458
- }
14590
+ /** All tunnels (statically typed) */
14591
+ export const tunnels = {
14592
+ ${tunnelInstances}
14593
+ } as const;
14459
14594
 
14460
- /**
14461
- * Delete this agent
14462
- * TODO: Implement when agent deletion API is ready
14463
- */
14464
- async delete(): Promise<void> {
14465
- // TODO: Implement agent deletion
14466
- throw new Error("Not implemented yet");
14467
- }
14468
- }
14595
+ /** Array of all tunnel IDs */
14596
+ export const ALL_TUNNEL_IDS: TunnelId[] = [${tunnels.map((t) => `"${t.id}"`).join(", ")}];
14597
+
14598
+ /** Array of all tunnel names */
14599
+ export const ALL_TUNNEL_NAMES: TunnelName[] = [${tunnels.map((t) => `"${t.name}"`).join(", ")}];
14469
14600
 
14470
14601
  // ============ Agents ============
14471
14602
 
14472
- /** All agents (statically typed with methods) */
14603
+ /** All agents (statically typed with their tunnels) */
14473
14604
  export const agents = {
14474
14605
  ${agentInstances}
14475
14606
  } as const;
14476
14607
 
14477
14608
  /** Map of agent names to IDs */
14478
14609
  export const AGENT_IDS = {
14479
- ${agentEntries}
14610
+ ${agents.map((a) => ` "${a.name}": "${a.id}"`).join(`,
14611
+ `)}
14480
14612
  } as const;
14481
14613
 
14482
14614
  /** Array of all agent IDs */
@@ -14489,7 +14621,6 @@ export const ALL_AGENT_NAMES: AgentName[] = [${agents.map((a) => `"${a.name}"`).
14489
14621
 
14490
14622
  /**
14491
14623
  * Regenerate types by re-fetching from PlayIt API
14492
- * This runs the codegen CLI
14493
14624
  */
14494
14625
  export async function regenerate(): Promise<void> {
14495
14626
  return new Promise((resolve, reject) => {
@@ -14518,29 +14649,40 @@ export async function regenerate(): Promise<void> {
14518
14649
  * \`\`\`ts
14519
14650
  * import { playit } from "./generated/playit";
14520
14651
  *
14521
- * // Access agents directly (fully typed with methods!)
14652
+ * // Access agents (with their tunnels)
14522
14653
  * const agent = playit.agents.${toIdentifier(agents[0]?.name || "my_agent")};
14523
- * console.log(agent.id); // "${agents[0]?.id || "..."}"
14524
- * console.log(agent.status); // "${agents[0]?.status || "..."}"
14654
+ * console.log(agent.id); // "${agents[0]?.id || "..."}"
14655
+ * console.log(agent.status); // "${agents[0]?.status || "..."}"
14656
+ * console.log(agent.tunnels); // All tunnels for this agent
14525
14657
  *
14526
- * // Agent methods (coming soon)
14527
- * // const tunnels = await agent.getTunnels();
14528
- * // await agent.createTunnel({ name: "SSH", localPort: 22 });
14658
+ * // Access tunnels directly
14659
+ * const tunnel = playit.tunnels.${toIdentifier(tunnels[0]?.name || "my_tunnel")};
14660
+ * console.log(tunnel.alloc.assignedDomain);
14661
+ * console.log(tunnel.origin.localPort);
14529
14662
  *
14530
14663
  * // Regenerate types after changes
14531
14664
  * await playit.regenerate();
14532
14665
  * \`\`\`
14533
14666
  */
14534
14667
  export const playit = {
14535
- /** All agents (statically typed with methods) */
14668
+ /** All agents (with their tunnels) */
14536
14669
  agents,
14537
14670
 
14671
+ /** All tunnels */
14672
+ tunnels,
14673
+
14538
14674
  /** All agent IDs */
14539
14675
  agentIds: ALL_AGENT_IDS,
14540
14676
 
14541
14677
  /** All agent names */
14542
14678
  agentNames: ALL_AGENT_NAMES,
14543
14679
 
14680
+ /** All tunnel IDs */
14681
+ tunnelIds: ALL_TUNNEL_IDS,
14682
+
14683
+ /** All tunnel names */
14684
+ tunnelNames: ALL_TUNNEL_NAMES,
14685
+
14544
14686
  /** Regenerate types after changes */
14545
14687
  regenerate,
14546
14688
  };
@@ -14552,7 +14694,8 @@ export default playit;
14552
14694
  }
14553
14695
  const outputPath = path2.join(GENERATED_DIR, "playit.ts");
14554
14696
  fs2.writeFileSync(outputPath, content);
14555
- console.log(`✓ Generated types at ${outputPath}`);
14697
+ console.log(`
14698
+ ✓ Generated types at ${outputPath}`);
14556
14699
  }
14557
14700
  var command = process.argv[2];
14558
14701
  switch (command) {
package/dist/client.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Agent, PlayItOptions } from "./types";
1
+ import type { PlayItOptions, PlayItData } from "./types";
2
2
  /**
3
3
  * PlayIt.gg API client
4
4
  *
@@ -19,7 +19,7 @@ export declare class PlayIt<TAgentId extends string = string, TAgentName extends
19
19
  _skipCodegenCheck?: boolean;
20
20
  }): PlayIt<TAgentId, TAgentName>;
21
21
  /**
22
- * Fetch all agents from the API
22
+ * Fetch all data from PlayIt (agents + tunnels) in a single request
23
23
  */
24
- fetchAgents(): Promise<Agent[]>;
24
+ fetchAll(): Promise<PlayItData>;
25
25
  }
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import "dotenv/config";
2
2
  export { PlayIt } from "./client";
3
3
  export { PlayIt as default } from "./client";
4
- export type { Agent, Tunnel, PlayItOptions } from "./types";
5
- export { parseAgentsHtml } from "./parsers";
4
+ export type { Agent, Tunnel, TunnelAlloc, TunnelOrigin, PlayItOptions, PlayItData } from "./types";
5
+ export { parsePlayItHtml, parseAgentsHtml, parseTunnelsHtml } from "./parsers";
6
6
  export { requireCodegen, isCodegenComplete, CodegenRequiredError } from "./guard";
package/dist/index.js CHANGED
@@ -1052,7 +1052,77 @@ var betterFetch = async (url, options) => {
1052
1052
  };
1053
1053
 
1054
1054
  // src/parsers/agents.ts
1055
+ function extractRemixContext(html) {
1056
+ const match = html.match(/<script>window\.__remixContext = ([\s\S]*?);<\/script>/);
1057
+ if (!match?.[1])
1058
+ return null;
1059
+ try {
1060
+ return JSON.parse(match[1]);
1061
+ } catch {
1062
+ return null;
1063
+ }
1064
+ }
1065
+ function parsePlayItHtml(html) {
1066
+ const context = extractRemixContext(html);
1067
+ if (!context) {
1068
+ throw new Error("Could not find Remix context in HTML");
1069
+ }
1070
+ const accountData = context.state.loaderData["routes/account"];
1071
+ const agents = accountData.agents.agents.map((agent) => ({
1072
+ id: agent.id,
1073
+ name: agent.name,
1074
+ clientIp: agent.status.data.client_addr,
1075
+ tunnelIp: agent.status.data.tunnel_addr,
1076
+ version: agent.agent_version.version,
1077
+ os: agent.agent_version.platform,
1078
+ status: agent.status.state
1079
+ }));
1080
+ const tunnels = accountData.tunnels.tunnels.map((tunnel) => ({
1081
+ id: tunnel.id,
1082
+ name: tunnel.name,
1083
+ tunnelType: tunnel.tunnel_type,
1084
+ portType: tunnel.port_type,
1085
+ portCount: tunnel.port_count,
1086
+ alloc: {
1087
+ status: tunnel.alloc.status,
1088
+ id: tunnel.alloc.data.id,
1089
+ ipHostname: tunnel.alloc.data.ip_hostname,
1090
+ staticIp4: tunnel.alloc.data.static_ip4,
1091
+ staticIp6: tunnel.alloc.data.static_ip6,
1092
+ assignedDomain: tunnel.alloc.data.assigned_domain,
1093
+ assignedSrv: tunnel.alloc.data.assigned_srv,
1094
+ tunnelIp: tunnel.alloc.data.tunnel_ip,
1095
+ portStart: tunnel.alloc.data.port_start,
1096
+ portEnd: tunnel.alloc.data.port_end,
1097
+ ipType: tunnel.alloc.data.ip_type,
1098
+ region: tunnel.alloc.data.region
1099
+ },
1100
+ origin: {
1101
+ agentId: tunnel.origin.data.agent_id,
1102
+ agentName: tunnel.origin.data.agent_name,
1103
+ localIp: tunnel.origin.data.local_ip,
1104
+ localPort: tunnel.origin.data.local_port
1105
+ },
1106
+ domain: tunnel.domain,
1107
+ active: tunnel.active,
1108
+ region: tunnel.region,
1109
+ proxyProtocol: tunnel.proxy_protocol
1110
+ }));
1111
+ return { agents, tunnels };
1112
+ }
1055
1113
  function parseAgentsHtml(html) {
1114
+ const context = extractRemixContext(html);
1115
+ if (context) {
1116
+ return context.state.loaderData["routes/account"].agents.agents.map((agent) => ({
1117
+ id: agent.id,
1118
+ name: agent.name,
1119
+ clientIp: agent.status.data.client_addr,
1120
+ tunnelIp: agent.status.data.tunnel_addr,
1121
+ version: agent.agent_version.version,
1122
+ os: agent.agent_version.platform,
1123
+ status: agent.status.state
1124
+ }));
1125
+ }
1056
1126
  const table = html.match(/<table class="row-links with-spacing">[\s\S]*?<\/table>/)?.[0];
1057
1127
  if (!table) {
1058
1128
  throw new Error("No table found in HTML");
@@ -1063,6 +1133,43 @@ function parseAgentsHtml(html) {
1063
1133
  }
1064
1134
  return rows.map(parseAgentRow);
1065
1135
  }
1136
+ function parseTunnelsHtml(html) {
1137
+ const context = extractRemixContext(html);
1138
+ if (!context) {
1139
+ throw new Error("Could not find Remix context in HTML");
1140
+ }
1141
+ return context.state.loaderData["routes/account"].tunnels.tunnels.map((tunnel) => ({
1142
+ id: tunnel.id,
1143
+ name: tunnel.name,
1144
+ tunnelType: tunnel.tunnel_type,
1145
+ portType: tunnel.port_type,
1146
+ portCount: tunnel.port_count,
1147
+ alloc: {
1148
+ status: tunnel.alloc.status,
1149
+ id: tunnel.alloc.data.id,
1150
+ ipHostname: tunnel.alloc.data.ip_hostname,
1151
+ staticIp4: tunnel.alloc.data.static_ip4,
1152
+ staticIp6: tunnel.alloc.data.static_ip6,
1153
+ assignedDomain: tunnel.alloc.data.assigned_domain,
1154
+ assignedSrv: tunnel.alloc.data.assigned_srv,
1155
+ tunnelIp: tunnel.alloc.data.tunnel_ip,
1156
+ portStart: tunnel.alloc.data.port_start,
1157
+ portEnd: tunnel.alloc.data.port_end,
1158
+ ipType: tunnel.alloc.data.ip_type,
1159
+ region: tunnel.alloc.data.region
1160
+ },
1161
+ origin: {
1162
+ agentId: tunnel.origin.data.agent_id,
1163
+ agentName: tunnel.origin.data.agent_name,
1164
+ localIp: tunnel.origin.data.local_ip,
1165
+ localPort: tunnel.origin.data.local_port
1166
+ },
1167
+ domain: tunnel.domain,
1168
+ active: tunnel.active,
1169
+ region: tunnel.region,
1170
+ proxyProtocol: tunnel.proxy_protocol
1171
+ }));
1172
+ }
1066
1173
  function parseAgentRow(row) {
1067
1174
  const id = row.match(/href="\/account\/agents\/([a-f0-9-]+)"/)?.[1] ?? "";
1068
1175
  const name = row.match(/<td[^>]*><a[^>]*>([^<]+)<span/)?.[1]?.trim() ?? "";
@@ -14706,16 +14813,18 @@ class PlayIt {
14706
14813
  }
14707
14814
  return new PlayIt(options);
14708
14815
  }
14709
- async fetchAgents() {
14816
+ async fetchAll() {
14710
14817
  const { data, error: error48 } = await this.$fetch("/account/agents");
14711
14818
  if (error48) {
14712
- throw new Error("Failed to get agents: " + error48.message);
14819
+ throw new Error("Failed to fetch PlayIt data: " + error48.message);
14713
14820
  }
14714
- return parseAgentsHtml(data);
14821
+ return parsePlayItHtml(data);
14715
14822
  }
14716
14823
  }
14717
14824
  export {
14718
14825
  requireCodegen,
14826
+ parseTunnelsHtml,
14827
+ parsePlayItHtml,
14719
14828
  parseAgentsHtml,
14720
14829
  isCodegenComplete,
14721
14830
  PlayIt as default,
@@ -1,5 +1,15 @@
1
- import type { Agent } from "../types";
1
+ import type { Agent, Tunnel, PlayItData } from "../types";
2
+ /**
3
+ * Parse all data from the PlayIt HTML page (agents + tunnels)
4
+ */
5
+ export declare function parsePlayItHtml(html: string): PlayItData;
2
6
  /**
3
7
  * Parse the agents HTML page and extract agent data
8
+ * @deprecated Use parsePlayItHtml instead
4
9
  */
5
10
  export declare function parseAgentsHtml(html: string): Agent[];
11
+ /**
12
+ * Parse tunnels from HTML
13
+ * @deprecated Use parsePlayItHtml instead
14
+ */
15
+ export declare function parseTunnelsHtml(html: string): Tunnel[];
@@ -1 +1 @@
1
- export { parseAgentsHtml } from "./agents";
1
+ export { parsePlayItHtml, parseAgentsHtml, parseTunnelsHtml } from "./agents";
package/dist/types.d.ts CHANGED
@@ -7,8 +7,38 @@ export interface Agent {
7
7
  os: string;
8
8
  status: string;
9
9
  }
10
+ export interface TunnelAlloc {
11
+ status: string;
12
+ id: string;
13
+ ipHostname: string;
14
+ staticIp4: string;
15
+ staticIp6: string;
16
+ assignedDomain: string;
17
+ assignedSrv: string | null;
18
+ tunnelIp: string;
19
+ portStart: number;
20
+ portEnd: number;
21
+ ipType: string;
22
+ region: string;
23
+ }
24
+ export interface TunnelOrigin {
25
+ agentId: string;
26
+ agentName: string;
27
+ localIp: string;
28
+ localPort: number;
29
+ }
10
30
  export interface Tunnel {
11
31
  id: string;
32
+ name: string;
33
+ tunnelType: string | null;
34
+ portType: string;
35
+ portCount: number;
36
+ alloc: TunnelAlloc;
37
+ origin: TunnelOrigin;
38
+ domain: string | null;
39
+ active: boolean;
40
+ region: string;
41
+ proxyProtocol: string | null;
12
42
  }
13
43
  export interface PlayItOptions {
14
44
  /** Your playit.gg session token */
@@ -16,3 +46,7 @@ export interface PlayItOptions {
16
46
  /** Base URL for the API (default: https://playit.gg) */
17
47
  baseUrl?: string;
18
48
  }
49
+ export interface PlayItData {
50
+ agents: Agent[];
51
+ tunnels: Tunnel[];
52
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "playit-reversed",
3
- "version": "0.1.0-beta.2",
3
+ "version": "0.1.0-beta.3",
4
4
  "author": {
5
5
  "name": "Cete",
6
6
  "email": "tocka@tockanest.ch",
@@ -52,4 +52,4 @@
52
52
  "client"
53
53
  ],
54
54
  "license": "MIT"
55
- }
55
+ }