coderemote 0.2.0 → 0.2.1
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 -0
- package/dist/coderemote.js +185 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -97,6 +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 rotate-token # revoke every paired device
|
|
99
99
|
coderemote login # sign in again (--microsoft, or --device-code without a browser)
|
|
100
|
+
coderemote update # install the latest version (`up` says when there's one)
|
|
100
101
|
coderemote help # all options
|
|
101
102
|
```
|
|
102
103
|
|
package/dist/coderemote.js
CHANGED
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
// bin/coderemote.js
|
|
4
4
|
import { execFile as execFile5, spawn as spawn3 } from "node:child_process";
|
|
5
5
|
import { randomBytes } from "node:crypto";
|
|
6
|
-
import
|
|
6
|
+
import fs8 from "node:fs";
|
|
7
7
|
import os4 from "node:os";
|
|
8
|
-
import
|
|
8
|
+
import path7 from "node:path";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
9
10
|
import { promisify as promisify3 } from "node:util";
|
|
10
11
|
import qrcode from "qrcode-terminal";
|
|
11
12
|
|
|
@@ -1365,7 +1366,7 @@ var PreviewTunnel = class {
|
|
|
1365
1366
|
const created = await this.devtunnel(
|
|
1366
1367
|
"create",
|
|
1367
1368
|
"--expiration",
|
|
1368
|
-
"
|
|
1369
|
+
"30d",
|
|
1369
1370
|
"-d",
|
|
1370
1371
|
this.description,
|
|
1371
1372
|
"--labels",
|
|
@@ -1529,6 +1530,130 @@ function tunnelStatus({ devtunnel: devtunnel2, tunnelId, account, anonymous: ano
|
|
|
1529
1530
|
};
|
|
1530
1531
|
}
|
|
1531
1532
|
|
|
1533
|
+
// src/update.js
|
|
1534
|
+
import fs7 from "node:fs";
|
|
1535
|
+
import path6 from "node:path";
|
|
1536
|
+
var LATEST_URL = "https://registry.npmjs.org/coderemote/latest";
|
|
1537
|
+
var CHECK_INTERVAL_MS = 24 * 36e5;
|
|
1538
|
+
var SEMVER = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/;
|
|
1539
|
+
function isNewer(latest, current) {
|
|
1540
|
+
const a = SEMVER.exec(latest ?? "");
|
|
1541
|
+
const b = SEMVER.exec(current ?? "");
|
|
1542
|
+
if (!a || !b) return false;
|
|
1543
|
+
for (let i = 1; i <= 3; i++) {
|
|
1544
|
+
if (+a[i] !== +b[i]) return +a[i] > +b[i];
|
|
1545
|
+
}
|
|
1546
|
+
return !a[4] && !!b[4];
|
|
1547
|
+
}
|
|
1548
|
+
async function fetchLatest({ fetch: fetch2 = globalThis.fetch, timeoutMs = 3e3 } = {}) {
|
|
1549
|
+
try {
|
|
1550
|
+
const res = await fetch2(LATEST_URL, { signal: AbortSignal.timeout(timeoutMs) });
|
|
1551
|
+
if (!res.ok) return null;
|
|
1552
|
+
const { version } = await res.json();
|
|
1553
|
+
return SEMVER.test(version) ? version : null;
|
|
1554
|
+
} catch {
|
|
1555
|
+
return null;
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
async function availableUpdate({ current, dir, fetch: fetch2, now = Date.now() }) {
|
|
1559
|
+
const file = path6.join(dir, "update.json");
|
|
1560
|
+
let cache = {};
|
|
1561
|
+
try {
|
|
1562
|
+
cache = JSON.parse(fs7.readFileSync(file, "utf8"));
|
|
1563
|
+
} catch {
|
|
1564
|
+
}
|
|
1565
|
+
let latest = cache.latest;
|
|
1566
|
+
if (!latest || !(now - (cache.checkedAt ?? 0) < CHECK_INTERVAL_MS)) {
|
|
1567
|
+
const fetched = await fetchLatest({ fetch: fetch2 });
|
|
1568
|
+
if (fetched) {
|
|
1569
|
+
latest = fetched;
|
|
1570
|
+
try {
|
|
1571
|
+
fs7.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
1572
|
+
fs7.writeFileSync(file, JSON.stringify({ latest, checkedAt: now }));
|
|
1573
|
+
} catch {
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
return isNewer(latest, current) ? latest : null;
|
|
1578
|
+
}
|
|
1579
|
+
function installCommand(installedAt) {
|
|
1580
|
+
const p = installedAt.split(path6.sep).join("/");
|
|
1581
|
+
if (p.includes("/pnpm/") || p.includes("/.pnpm/")) return ["pnpm", ["add", "-g", "coderemote@latest"]];
|
|
1582
|
+
if (p.includes("/.bun/")) return ["bun", ["add", "-g", "coderemote@latest"]];
|
|
1583
|
+
if (p.includes("/.yarn/") || p.includes("/yarn/global/")) return ["yarn", ["global", "add", "coderemote@latest"]];
|
|
1584
|
+
return ["npm", ["install", "-g", "coderemote@latest"]];
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
// package.json
|
|
1588
|
+
var package_default = {
|
|
1589
|
+
name: "coderemote",
|
|
1590
|
+
version: "0.2.1",
|
|
1591
|
+
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
|
+
keywords: [
|
|
1593
|
+
"remote",
|
|
1594
|
+
"remote-terminal",
|
|
1595
|
+
"remote-development",
|
|
1596
|
+
"terminal",
|
|
1597
|
+
"ios",
|
|
1598
|
+
"iphone",
|
|
1599
|
+
"mobile",
|
|
1600
|
+
"dev-tunnels",
|
|
1601
|
+
"devtunnel",
|
|
1602
|
+
"tunnel",
|
|
1603
|
+
"ssh-alternative",
|
|
1604
|
+
"file-explorer",
|
|
1605
|
+
"git",
|
|
1606
|
+
"pty",
|
|
1607
|
+
"cli",
|
|
1608
|
+
"vscode",
|
|
1609
|
+
"code-remote"
|
|
1610
|
+
],
|
|
1611
|
+
homepage: "https://coderemoteapp.github.io/web/",
|
|
1612
|
+
bugs: {
|
|
1613
|
+
url: "https://github.com/CodeRemoteApp/coderemote/issues"
|
|
1614
|
+
},
|
|
1615
|
+
repository: {
|
|
1616
|
+
type: "git",
|
|
1617
|
+
url: "git+https://github.com/CodeRemoteApp/coderemote.git",
|
|
1618
|
+
directory: "apps/agent"
|
|
1619
|
+
},
|
|
1620
|
+
license: "AGPL-3.0-or-later",
|
|
1621
|
+
author: "Thanh Tran",
|
|
1622
|
+
type: "module",
|
|
1623
|
+
bin: {
|
|
1624
|
+
coderemote: "dist/coderemote.js"
|
|
1625
|
+
},
|
|
1626
|
+
files: [
|
|
1627
|
+
"dist",
|
|
1628
|
+
"scripts/fix-node-pty.js"
|
|
1629
|
+
],
|
|
1630
|
+
engines: {
|
|
1631
|
+
node: ">=20"
|
|
1632
|
+
},
|
|
1633
|
+
os: [
|
|
1634
|
+
"darwin",
|
|
1635
|
+
"linux"
|
|
1636
|
+
],
|
|
1637
|
+
scripts: {
|
|
1638
|
+
build: "node scripts/build.js",
|
|
1639
|
+
prepare: "node scripts/build.js",
|
|
1640
|
+
prepack: "node scripts/build.js && cp ../../LICENSE LICENSE",
|
|
1641
|
+
postpack: "rm -f LICENSE",
|
|
1642
|
+
postinstall: "node scripts/fix-node-pty.js",
|
|
1643
|
+
test: "node --test 'test/*.test.js'"
|
|
1644
|
+
},
|
|
1645
|
+
dependencies: {
|
|
1646
|
+
"node-pty": "^1.1.0",
|
|
1647
|
+
"qrcode-terminal": "^0.12.0",
|
|
1648
|
+
ws: "^8.22.0"
|
|
1649
|
+
},
|
|
1650
|
+
devDependencies: {
|
|
1651
|
+
"@vsremote/agent-core": "*",
|
|
1652
|
+
"@vsremote/protocol": "*",
|
|
1653
|
+
esbuild: "^0.25.11"
|
|
1654
|
+
}
|
|
1655
|
+
};
|
|
1656
|
+
|
|
1532
1657
|
// src/spinner.js
|
|
1533
1658
|
var FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
1534
1659
|
var dim = (s) => `\x1B[2m${s}\x1B[22m`;
|
|
@@ -1650,16 +1775,18 @@ ${log2}`));
|
|
|
1650
1775
|
|
|
1651
1776
|
// bin/coderemote.js
|
|
1652
1777
|
var run4 = promisify3(execFile5);
|
|
1653
|
-
var CONFIG_DIR =
|
|
1654
|
-
var LEGACY_CONFIG_DIR =
|
|
1655
|
-
if (!
|
|
1656
|
-
var CONFIG_FILE =
|
|
1778
|
+
var CONFIG_DIR = path7.join(os4.homedir(), ".coderemote");
|
|
1779
|
+
var LEGACY_CONFIG_DIR = path7.join(os4.homedir(), ".vsremote");
|
|
1780
|
+
if (!fs8.existsSync(CONFIG_DIR) && fs8.existsSync(LEGACY_CONFIG_DIR)) fs8.renameSync(LEGACY_CONFIG_DIR, CONFIG_DIR);
|
|
1781
|
+
var CONFIG_FILE = path7.join(CONFIG_DIR, "config.json");
|
|
1657
1782
|
var DEFAULT_PORT = 31580;
|
|
1783
|
+
var VERSION = package_default.version;
|
|
1784
|
+
var PACKAGED = path7.basename(path7.dirname(fileURLToPath(import.meta.url))) === "dist";
|
|
1658
1785
|
var spinner = new Spinner();
|
|
1659
1786
|
function loadConfig() {
|
|
1660
1787
|
let cfg2 = {};
|
|
1661
1788
|
try {
|
|
1662
|
-
cfg2 = JSON.parse(
|
|
1789
|
+
cfg2 = JSON.parse(fs8.readFileSync(CONFIG_FILE, "utf8"));
|
|
1663
1790
|
} catch {
|
|
1664
1791
|
}
|
|
1665
1792
|
if (!cfg2.token) {
|
|
@@ -1669,8 +1796,8 @@ function loadConfig() {
|
|
|
1669
1796
|
return cfg2;
|
|
1670
1797
|
}
|
|
1671
1798
|
function saveConfig(cfg2) {
|
|
1672
|
-
|
|
1673
|
-
|
|
1799
|
+
fs8.mkdirSync(CONFIG_DIR, { recursive: true, mode: 448 });
|
|
1800
|
+
fs8.writeFileSync(CONFIG_FILE, JSON.stringify(cfg2, null, 2), { mode: 384 });
|
|
1674
1801
|
}
|
|
1675
1802
|
function listen(token, port2, root2, tunnelToken, preview, status2) {
|
|
1676
1803
|
const server = createAgentServer({ token, root: root2, tunnelToken, preview, tunnelStatus: status2 });
|
|
@@ -1683,7 +1810,7 @@ var DEVTUNNEL = "devtunnel";
|
|
|
1683
1810
|
async function resolveDevtunnel() {
|
|
1684
1811
|
let installing = false;
|
|
1685
1812
|
DEVTUNNEL = await ensureDevtunnel({
|
|
1686
|
-
dir:
|
|
1813
|
+
dir: path7.join(CONFIG_DIR, "bin"),
|
|
1687
1814
|
progress: (detail2) => {
|
|
1688
1815
|
if (!installing) {
|
|
1689
1816
|
installing = true;
|
|
@@ -1849,6 +1976,8 @@ var USAGE = `Usage: coderemote <command> [options]
|
|
|
1849
1976
|
kill <id>... | --all kill terminal sessions (an id prefix is enough)
|
|
1850
1977
|
stop [--all] stop the agent for this folder (or every agent)
|
|
1851
1978
|
rotate-token revoke every paired device
|
|
1979
|
+
update install the latest coderemote (up says when there's one)
|
|
1980
|
+
version print this coderemote's version (also --version, -v)
|
|
1852
1981
|
login sign in to Microsoft Dev Tunnels with GitHub (up does this when needed);
|
|
1853
1982
|
--microsoft for a Microsoft account, --device-code without a browser
|
|
1854
1983
|
|
|
@@ -1861,16 +1990,16 @@ Options:
|
|
|
1861
1990
|
this folder in VS Code in the browser (needs VS Code's \`code\`)`;
|
|
1862
1991
|
var HELP_FLAGS = ["help", "--help", "-help", "-h"];
|
|
1863
1992
|
var argv = process.argv.slice(2);
|
|
1864
|
-
var [cmd, ...rest] = argv.length === 0 || argv.some((a) => HELP_FLAGS.includes(a)) ? ["help"] : argv;
|
|
1993
|
+
var [cmd, ...rest] = argv.length === 0 || argv.some((a) => HELP_FLAGS.includes(a)) ? ["help"] : argv.some((a) => a === "--version" || a === "-v") ? ["version"] : argv;
|
|
1865
1994
|
var flag = (name) => {
|
|
1866
1995
|
const i = rest.indexOf(name);
|
|
1867
1996
|
return i >= 0 ? rest[i + 1] : void 0;
|
|
1868
1997
|
};
|
|
1869
1998
|
var anonymous = rest.includes("--allow-anonymous") || rest.includes("--no-auth");
|
|
1870
1999
|
var port = flag("--port") ? parseInt(flag("--port"), 10) : DEFAULT_PORT;
|
|
1871
|
-
var root =
|
|
2000
|
+
var root = path7.resolve(flag("--root") ?? process.cwd());
|
|
1872
2001
|
var cfg = loadConfig();
|
|
1873
|
-
var registry = new AgentRegistry(
|
|
2002
|
+
var registry = new AgentRegistry(path7.join(CONFIG_DIR, "agents"));
|
|
1874
2003
|
async function start({ tunnel }) {
|
|
1875
2004
|
const running = registry.list().find((a) => a.port === port);
|
|
1876
2005
|
if (running) {
|
|
@@ -1979,6 +2108,7 @@ Token: ${cfg.token}`);
|
|
|
1979
2108
|
break;
|
|
1980
2109
|
}
|
|
1981
2110
|
case "up": {
|
|
2111
|
+
const update = PACKAGED && !process.env.CODEREMOTE_NO_UPDATE_CHECK ? availableUpdate({ current: VERSION, dir: CONFIG_DIR }).catch(() => null) : null;
|
|
1982
2112
|
let started;
|
|
1983
2113
|
try {
|
|
1984
2114
|
started = await start({ tunnel: true });
|
|
@@ -1991,6 +2121,9 @@ Token: ${cfg.token}`);
|
|
|
1991
2121
|
console.log(`
|
|
1992
2122
|
Sharing: ${root}`);
|
|
1993
2123
|
printPairing(url, cfg.token, access, vscodeUrl);
|
|
2124
|
+
const newer = await update;
|
|
2125
|
+
if (newer) console.log(` Update available: ${VERSION} \u2192 ${newer}. Run \`coderemote update\` to install it.
|
|
2126
|
+
`);
|
|
1994
2127
|
break;
|
|
1995
2128
|
}
|
|
1996
2129
|
case "status":
|
|
@@ -2059,6 +2192,44 @@ Token: ${cfg.token}`);
|
|
|
2059
2192
|
await resolveDevtunnel();
|
|
2060
2193
|
process.exit(await interactiveLogin(loginArgs()));
|
|
2061
2194
|
}
|
|
2195
|
+
case "update": {
|
|
2196
|
+
if (!PACKAGED) {
|
|
2197
|
+
console.log("This coderemote runs from a source checkout: update it with `git pull`.");
|
|
2198
|
+
break;
|
|
2199
|
+
}
|
|
2200
|
+
const latest = await fetchLatest({ timeoutMs: 1e4 });
|
|
2201
|
+
if (!latest) {
|
|
2202
|
+
console.error("Couldn't reach the npm registry. Try again, or run `npm install -g coderemote@latest`.");
|
|
2203
|
+
process.exit(1);
|
|
2204
|
+
}
|
|
2205
|
+
if (!isNewer(latest, VERSION)) {
|
|
2206
|
+
console.log(`coderemote ${VERSION} is the latest version.`);
|
|
2207
|
+
break;
|
|
2208
|
+
}
|
|
2209
|
+
const [command, args] = installCommand(fs8.realpathSync(process.argv[1]));
|
|
2210
|
+
console.log(`Updating coderemote ${VERSION} \u2192 ${latest}: ${command} ${args.join(" ")}
|
|
2211
|
+
`);
|
|
2212
|
+
const code = await new Promise((resolve) => {
|
|
2213
|
+
spawn3(command, args, { stdio: "inherit" }).on("exit", resolve).on("error", () => resolve(127));
|
|
2214
|
+
});
|
|
2215
|
+
if (code !== 0) {
|
|
2216
|
+
console.error(`
|
|
2217
|
+
The update failed (${command} exited with ${code}).`);
|
|
2218
|
+
console.error(`If it was a permission error (EACCES), run it with sudo: sudo ${command} ${args.join(" ")}`);
|
|
2219
|
+
process.exit(code || 1);
|
|
2220
|
+
}
|
|
2221
|
+
const running = registry.list();
|
|
2222
|
+
console.log(`
|
|
2223
|
+
Updated to coderemote ${latest}.`);
|
|
2224
|
+
if (running.length) {
|
|
2225
|
+
console.log(`${running.length === 1 ? "An agent is" : `${running.length} agents are`} still running the previous version.`);
|
|
2226
|
+
console.log("Restart: `coderemote stop --all`, then `coderemote up` in each shared folder.");
|
|
2227
|
+
}
|
|
2228
|
+
break;
|
|
2229
|
+
}
|
|
2230
|
+
case "version":
|
|
2231
|
+
console.log(VERSION);
|
|
2232
|
+
break;
|
|
2062
2233
|
case "help":
|
|
2063
2234
|
console.log(USAGE);
|
|
2064
2235
|
break;
|
package/package.json
CHANGED