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.
- package/dist/cli/index.d.ts +44 -0
- package/dist/cli.js +234 -91
- package/dist/client.d.ts +3 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.js +112 -3
- package/dist/parsers/agents.d.ts +11 -1
- package/dist/parsers/index.d.ts +1 -1
- package/dist/types.d.ts +34 -0
- package/package.json +2 -2
|
@@ -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
|
|
14375
|
+
async fetchAll() {
|
|
14269
14376
|
const { data, error: error48 } = await this.$fetch("/account/agents");
|
|
14270
14377
|
if (error48) {
|
|
14271
|
-
throw new Error("Failed to
|
|
14378
|
+
throw new Error("Failed to fetch PlayIt data: " + error48.message);
|
|
14272
14379
|
}
|
|
14273
|
-
return
|
|
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
|
|
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
|
|
14344
|
-
console.log(`✓ Found ${agents.length} agent(s)
|
|
14345
|
-
|
|
14346
|
-
|
|
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
|
-
|
|
14351
|
-
|
|
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.
|
|
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
|
|
14481
|
+
console.error("❌ Failed to fetch data:", error48);
|
|
14363
14482
|
process.exit(1);
|
|
14364
14483
|
}
|
|
14365
14484
|
}
|
|
14366
|
-
async function generateTypes(
|
|
14367
|
-
if (!
|
|
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
|
-
|
|
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
|
|
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) =>
|
|
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:
|
|
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 = ${
|
|
14577
|
+
export type AgentKey = ${agentKeys};
|
|
14405
14578
|
|
|
14406
|
-
|
|
14579
|
+
/** All available tunnel IDs */
|
|
14580
|
+
export type TunnelId = ${tunnelIds};
|
|
14407
14581
|
|
|
14408
|
-
/**
|
|
14409
|
-
|
|
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
|
-
|
|
14421
|
-
|
|
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
|
-
|
|
14449
|
-
|
|
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
|
-
|
|
14462
|
-
|
|
14463
|
-
|
|
14464
|
-
|
|
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
|
|
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
|
-
${
|
|
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
|
|
14652
|
+
* // Access agents (with their tunnels)
|
|
14522
14653
|
* const agent = playit.agents.${toIdentifier(agents[0]?.name || "my_agent")};
|
|
14523
|
-
* console.log(agent.id);
|
|
14524
|
-
* console.log(agent.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
|
-
* //
|
|
14527
|
-
*
|
|
14528
|
-
*
|
|
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 (
|
|
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(
|
|
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 {
|
|
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
|
|
22
|
+
* Fetch all data from PlayIt (agents + tunnels) in a single request
|
|
23
23
|
*/
|
|
24
|
-
|
|
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
|
|
14816
|
+
async fetchAll() {
|
|
14710
14817
|
const { data, error: error48 } = await this.$fetch("/account/agents");
|
|
14711
14818
|
if (error48) {
|
|
14712
|
-
throw new Error("Failed to
|
|
14819
|
+
throw new Error("Failed to fetch PlayIt data: " + error48.message);
|
|
14713
14820
|
}
|
|
14714
|
-
return
|
|
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,
|
package/dist/parsers/agents.d.ts
CHANGED
|
@@ -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[];
|
package/dist/parsers/index.d.ts
CHANGED
|
@@ -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
|
+
}
|