coderemote 0.3.0 → 0.3.2
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/README.md +1 -1
- package/dist/coderemote.js +65 -20
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -97,7 +97,7 @@ coderemote stop # stop the agent for this folder (--all: every
|
|
|
97
97
|
coderemote serve # share on 127.0.0.1 only, no tunnel (local testing)
|
|
98
98
|
coderemote requests [PORT] # requests that reached a previewed port (--grep, --failed, --json)
|
|
99
99
|
coderemote request <ID> # headers and bodies of one request (--show-secrets, --json)
|
|
100
|
-
coderemote rotate-token # revoke every paired device
|
|
100
|
+
coderemote rotate-token # revoke every paired device, stop running agents
|
|
101
101
|
coderemote login # sign in again (--microsoft, or --device-code without a browser)
|
|
102
102
|
coderemote update # install the latest version (`up` says when there's one)
|
|
103
103
|
coderemote help # all options
|
package/dist/coderemote.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import { execFile as execFile5, spawn as spawn3 } from "node:child_process";
|
|
5
5
|
import { randomBytes } from "node:crypto";
|
|
6
6
|
import fs8 from "node:fs";
|
|
7
|
+
import net2 from "node:net";
|
|
7
8
|
import os4 from "node:os";
|
|
8
9
|
import path7 from "node:path";
|
|
9
10
|
import { fileURLToPath } from "node:url";
|
|
@@ -434,11 +435,11 @@ async function listeningSockets() {
|
|
|
434
435
|
return sockets;
|
|
435
436
|
}
|
|
436
437
|
async function processTable() {
|
|
437
|
-
const out = await run("ps", ["-A", "-ww", "-o", "pid=,ppid=,command="]);
|
|
438
|
+
const out = await run("ps", ["-A", "-ww", "-o", "pid=,ppid=,tty=,command="]);
|
|
438
439
|
const table = /* @__PURE__ */ new Map();
|
|
439
440
|
for (const line of out.split("\n")) {
|
|
440
|
-
const m = line.match(/^\s*(\d+)\s+(\d+)\s+(.*)$/);
|
|
441
|
-
if (m) table.set(parseInt(m[1], 10), { ppid: parseInt(m[2], 10),
|
|
441
|
+
const m = line.match(/^\s*(\d+)\s+(\d+)\s+(\S+)\s+(.*)$/);
|
|
442
|
+
if (m) table.set(parseInt(m[1], 10), { ppid: parseInt(m[2], 10), tty: /^\?+$/.test(m[3]) ? null : m[3], command: m[4] });
|
|
442
443
|
}
|
|
443
444
|
return table;
|
|
444
445
|
}
|
|
@@ -476,6 +477,8 @@ async function listPorts(shells = /* @__PURE__ */ new Map()) {
|
|
|
476
477
|
command: table.get(s.pid)?.command ?? s.name,
|
|
477
478
|
cwd: null,
|
|
478
479
|
session: sessionOf(s.pid, table, shells),
|
|
480
|
+
// Started from a terminal (a dev server) rather than by launchd, an app or a daemon.
|
|
481
|
+
terminal: table.get(s.pid)?.tty != null,
|
|
479
482
|
self: s.pid === process.pid,
|
|
480
483
|
agent: s.pid === process.pid || AGENT_COMMAND.test(table.get(s.pid)?.command ?? "")
|
|
481
484
|
});
|
|
@@ -1478,7 +1481,7 @@ function parse(out) {
|
|
|
1478
1481
|
return JSON.parse(out.slice(out.indexOf("{")));
|
|
1479
1482
|
}
|
|
1480
1483
|
function machineName(description) {
|
|
1481
|
-
return (description || "").replace(/^(coderemote|vsremote)\s+/, "").replace(/\.local
|
|
1484
|
+
return (description || "").replace(/^(coderemote|vsremote)\s+/, "").replace(/\.local\b/, "") || null;
|
|
1482
1485
|
}
|
|
1483
1486
|
function machine(tunnel, current) {
|
|
1484
1487
|
return {
|
|
@@ -1587,7 +1590,7 @@ function installCommand(installedAt) {
|
|
|
1587
1590
|
// package.json
|
|
1588
1591
|
var package_default = {
|
|
1589
1592
|
name: "coderemote",
|
|
1590
|
-
version: "0.3.
|
|
1593
|
+
version: "0.3.2",
|
|
1591
1594
|
description: "Use your computer from your iPhone: files, git and a real terminal over a secure Microsoft Dev Tunnel. No SSH, no port forwarding, no VPN.",
|
|
1592
1595
|
keywords: [
|
|
1593
1596
|
"remote",
|
|
@@ -1935,12 +1938,23 @@ function loadConfig() {
|
|
|
1935
1938
|
cfg2.token = randomBytes(32).toString("base64url");
|
|
1936
1939
|
saveConfig(cfg2);
|
|
1937
1940
|
}
|
|
1941
|
+
if (cfg2.tunnelId && !cfg2.tunnels) {
|
|
1942
|
+
cfg2.tunnels = { [DEFAULT_PORT]: cfg2.tunnelId };
|
|
1943
|
+
saveConfig(cfg2);
|
|
1944
|
+
}
|
|
1938
1945
|
return cfg2;
|
|
1939
1946
|
}
|
|
1940
1947
|
function saveConfig(cfg2) {
|
|
1941
1948
|
fs8.mkdirSync(CONFIG_DIR, { recursive: true, mode: 448 });
|
|
1942
1949
|
fs8.writeFileSync(CONFIG_FILE, JSON.stringify(cfg2, null, 2), { mode: 384 });
|
|
1943
1950
|
}
|
|
1951
|
+
function portFree(port2) {
|
|
1952
|
+
return new Promise((resolve) => {
|
|
1953
|
+
const probe = net2.createServer();
|
|
1954
|
+
probe.once("error", () => resolve(false));
|
|
1955
|
+
probe.listen(port2, "127.0.0.1", () => probe.close(() => resolve(true)));
|
|
1956
|
+
});
|
|
1957
|
+
}
|
|
1944
1958
|
function listen(token, port2, root2, tunnelToken, preview, status2) {
|
|
1945
1959
|
const server = createAgentServer({ token, root: root2, tunnelToken, preview, tunnelStatus: status2 });
|
|
1946
1960
|
return new Promise((resolve, reject) => {
|
|
@@ -2009,20 +2023,22 @@ function applyAccess(tunnelId, anonymous2) {
|
|
|
2009
2023
|
return anonymous2 ? devtunnel("access", "create", tunnelId, "--anonymous") : devtunnel("access", "reset", tunnelId);
|
|
2010
2024
|
}
|
|
2011
2025
|
async function ensureTunnel(cfg2, port2, anonymous2) {
|
|
2012
|
-
|
|
2026
|
+
const known = cfg2.tunnels?.[port2];
|
|
2027
|
+
if (known) {
|
|
2013
2028
|
const [updated, access] = await Promise.all([
|
|
2014
|
-
devtunnel("update",
|
|
2015
|
-
applyAccess(
|
|
2029
|
+
devtunnel("update", known, "--expiration", "30d", "--add-labels", MACHINE_LABEL),
|
|
2030
|
+
applyAccess(known, anonymous2)
|
|
2016
2031
|
]);
|
|
2017
2032
|
if (updated.ok) {
|
|
2018
2033
|
if (!access.ok) throw new Error(`devtunnel access failed:
|
|
2019
2034
|
${access.out}`);
|
|
2020
|
-
return
|
|
2035
|
+
return known;
|
|
2021
2036
|
}
|
|
2022
2037
|
if (!/not found/i.test(updated.out)) throw new Error(`devtunnel update failed:
|
|
2023
2038
|
${updated.out}`);
|
|
2024
2039
|
}
|
|
2025
|
-
const
|
|
2040
|
+
const name = port2 === DEFAULT_PORT ? os4.hostname() : `${os4.hostname()} :${port2}`;
|
|
2041
|
+
const createArgs = ["create", "--expiration", "30d", "-d", `coderemote ${name}`, "--labels", MACHINE_LABEL, "--json"];
|
|
2026
2042
|
if (anonymous2) createArgs.push("--allow-anonymous");
|
|
2027
2043
|
const created = await devtunnel(...createArgs);
|
|
2028
2044
|
if (!created.ok) throw new Error(`devtunnel create failed:
|
|
@@ -2033,7 +2049,8 @@ ${created.out}`);
|
|
|
2033
2049
|
const portRes = await devtunnel("port", "create", tunnelId, "-p", String(port2), "--protocol", "http");
|
|
2034
2050
|
if (!portRes.ok) throw new Error(`devtunnel port create failed:
|
|
2035
2051
|
${portRes.out}`);
|
|
2036
|
-
cfg2.
|
|
2052
|
+
cfg2.tunnels = { ...cfg2.tunnels, [port2]: tunnelId };
|
|
2053
|
+
if (port2 === DEFAULT_PORT) cfg2.tunnelId = tunnelId;
|
|
2037
2054
|
saveConfig(cfg2);
|
|
2038
2055
|
return tunnelId;
|
|
2039
2056
|
}
|
|
@@ -2117,7 +2134,7 @@ var USAGE = `Usage: coderemote <command> [options]
|
|
|
2117
2134
|
status list running agents and their terminal sessions
|
|
2118
2135
|
kill <id>... | --all kill terminal sessions (an id prefix is enough)
|
|
2119
2136
|
stop [--all] stop the agent for this folder (or every agent)
|
|
2120
|
-
rotate-token revoke every paired device
|
|
2137
|
+
rotate-token revoke every paired device and stop running agents
|
|
2121
2138
|
update install the latest coderemote (up says when there's one)
|
|
2122
2139
|
requests [PORT] list the requests that reached a previewed port, oldest first;
|
|
2123
2140
|
--grep TEXT, --failed (errors, 4xx and 5xx), --limit N (default 50)
|
|
@@ -2131,7 +2148,8 @@ var USAGE = `Usage: coderemote <command> [options]
|
|
|
2131
2148
|
|
|
2132
2149
|
Options:
|
|
2133
2150
|
--root DIR folder to share (default: current folder)
|
|
2134
|
-
--port N local port (default: ${DEFAULT_PORT}
|
|
2151
|
+
--port N local port (default: the first free one from ${DEFAULT_PORT}; each port
|
|
2152
|
+
keeps its own tunnel URL, so a folder pairs once per port)
|
|
2135
2153
|
--allow-anonymous up: skip the sign-in, the pairing token alone grants access
|
|
2136
2154
|
(alias --no-auth)
|
|
2137
2155
|
--web up: also run \`code tunnel\` and print a vscode.dev link that opens
|
|
@@ -2149,11 +2167,32 @@ var root = path7.resolve(flag("--root") ?? process.cwd());
|
|
|
2149
2167
|
var cfg = loadConfig();
|
|
2150
2168
|
var registry = new AgentRegistry(path7.join(CONFIG_DIR, "agents"));
|
|
2151
2169
|
async function start({ tunnel }) {
|
|
2152
|
-
const
|
|
2153
|
-
if (
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2170
|
+
const agents = registry.list();
|
|
2171
|
+
if (flag("--port")) {
|
|
2172
|
+
const running = agents.find((a) => a.port === port);
|
|
2173
|
+
if (running) {
|
|
2174
|
+
console.error(`An agent already runs on port ${port} for ${running.root} (pid ${running.pid}).`);
|
|
2175
|
+
console.error("Stop it with `coderemote stop --all`, or pick another port with --port.");
|
|
2176
|
+
process.exit(1);
|
|
2177
|
+
}
|
|
2178
|
+
} else {
|
|
2179
|
+
const sharing = agents.find((a) => a.root === root);
|
|
2180
|
+
if (sharing) {
|
|
2181
|
+
console.error(`${root} is already shared on port ${sharing.port} (pid ${sharing.pid}).`);
|
|
2182
|
+
console.error("Stop it with `coderemote stop`, or start another one with --port.");
|
|
2183
|
+
process.exit(1);
|
|
2184
|
+
}
|
|
2185
|
+
const taken = async (p) => agents.some((a) => a.port === p) || !await portFree(p);
|
|
2186
|
+
const remembered = cfg.folderPorts?.[root];
|
|
2187
|
+
if (remembered && !await taken(remembered)) port = remembered;
|
|
2188
|
+
else {
|
|
2189
|
+
const claimed = new Set(Object.values(cfg.folderPorts ?? {}));
|
|
2190
|
+
while (claimed.has(port) || await taken(port)) port++;
|
|
2191
|
+
}
|
|
2192
|
+
if (port !== remembered) {
|
|
2193
|
+
cfg.folderPorts = { ...cfg.folderPorts, [root]: port };
|
|
2194
|
+
saveConfig(cfg);
|
|
2195
|
+
}
|
|
2157
2196
|
}
|
|
2158
2197
|
let server;
|
|
2159
2198
|
let tunnelId;
|
|
@@ -2193,7 +2232,7 @@ async function start({ tunnel }) {
|
|
|
2193
2232
|
account = user;
|
|
2194
2233
|
spinner.succeed();
|
|
2195
2234
|
spinner.start("Connecting to the relay");
|
|
2196
|
-
const known = cfg.
|
|
2235
|
+
const known = cfg.tunnels?.[port];
|
|
2197
2236
|
let hosting = known ? hostTunnel(known, port) : null;
|
|
2198
2237
|
hosting?.catch(() => {
|
|
2199
2238
|
});
|
|
@@ -2356,7 +2395,13 @@ Token: ${cfg.token}`);
|
|
|
2356
2395
|
case "rotate-token": {
|
|
2357
2396
|
cfg.token = randomBytes(32).toString("base64url");
|
|
2358
2397
|
saveConfig(cfg);
|
|
2359
|
-
|
|
2398
|
+
const agents = registry.list();
|
|
2399
|
+
for (const agent of agents) {
|
|
2400
|
+
process.kill(agent.pid, "SIGTERM");
|
|
2401
|
+
console.log(`stopped agent for ${agent.root} (pid ${agent.pid})`);
|
|
2402
|
+
}
|
|
2403
|
+
console.log("Token rotated. Re-pair your devices.");
|
|
2404
|
+
if (agents.length) console.log("Run `coderemote up` again in each folder above; each keeps its tunnel URL.");
|
|
2360
2405
|
break;
|
|
2361
2406
|
}
|
|
2362
2407
|
case "login": {
|
package/package.json
CHANGED