@pocketenv/cli 0.3.3 → 0.3.5
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/index.js +67 -14
- package/package.json +1 -1
- package/src/cmd/start.ts +0 -1
- package/src/lib/expandRepo.ts +3 -0
- package/src/theme.ts +61 -1
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import { Command } from 'commander';
|
|
4
4
|
import consola from 'consola';
|
|
5
|
-
import fs from 'fs/promises';
|
|
5
|
+
import fs$1 from 'fs/promises';
|
|
6
6
|
import path from 'path';
|
|
7
7
|
import os from 'os';
|
|
8
8
|
import { env as env$2 } from 'process';
|
|
@@ -13,21 +13,23 @@ import { EventSource } from 'eventsource';
|
|
|
13
13
|
import open from 'open';
|
|
14
14
|
import express from 'express';
|
|
15
15
|
import cors from 'cors';
|
|
16
|
-
import fs$
|
|
16
|
+
import fs$2 from 'node:fs/promises';
|
|
17
17
|
import os$1 from 'node:os';
|
|
18
18
|
import path$1 from 'node:path';
|
|
19
19
|
import Table from 'cli-table3';
|
|
20
20
|
import dayjs from 'dayjs';
|
|
21
21
|
import relativeTime from 'dayjs/plugin/relativeTime.js';
|
|
22
|
+
import { execSync } from 'child_process';
|
|
23
|
+
import * as fs from 'fs';
|
|
22
24
|
import { password, editor, input } from '@inquirer/prompts';
|
|
23
25
|
import sodium from 'libsodium-wrappers';
|
|
24
26
|
|
|
25
|
-
var version = "0.3.
|
|
27
|
+
var version = "0.3.5";
|
|
26
28
|
|
|
27
29
|
async function getAccessToken() {
|
|
28
30
|
const tokenPath = path.join(os.homedir(), ".pocketenv", "token.json");
|
|
29
31
|
try {
|
|
30
|
-
await fs.access(tokenPath);
|
|
32
|
+
await fs$1.access(tokenPath);
|
|
31
33
|
} catch (err) {
|
|
32
34
|
if (!env$2.POCKETENV_TOKEN) {
|
|
33
35
|
consola.error(
|
|
@@ -38,7 +40,7 @@ async function getAccessToken() {
|
|
|
38
40
|
process.exit(1);
|
|
39
41
|
}
|
|
40
42
|
}
|
|
41
|
-
const tokenData = await fs.readFile(tokenPath, "utf-8");
|
|
43
|
+
const tokenData = await fs$1.readFile(tokenPath, "utf-8");
|
|
42
44
|
const { token } = JSON.parse(tokenData);
|
|
43
45
|
if (!token) {
|
|
44
46
|
consola.error(
|
|
@@ -415,6 +417,8 @@ function expandRepo(repo) {
|
|
|
415
417
|
if (githubMatch) return `https://github.com/${githubMatch[1]}`;
|
|
416
418
|
const tangledMatch = repo.match(/^tangled:([^/]+\/[^/]+)$/);
|
|
417
419
|
if (tangledMatch) return `https://tangled.org/${tangledMatch[1]}`;
|
|
420
|
+
const gitlabMatch = repo.match(/^gitlab:([^/]+\/[^/]+)$/);
|
|
421
|
+
if (gitlabMatch) return `https://gitlab.com/${gitlabMatch[1]}`;
|
|
418
422
|
return repo;
|
|
419
423
|
}
|
|
420
424
|
|
|
@@ -477,8 +481,8 @@ async function login(handle) {
|
|
|
477
481
|
app.post("/token", async (req, res) => {
|
|
478
482
|
console.log(chalk.bold(chalk.greenBright("Login successful!\n")));
|
|
479
483
|
const tokenPath = path$1.join(os$1.homedir(), ".pocketenv", "token.json");
|
|
480
|
-
await fs$
|
|
481
|
-
await fs$
|
|
484
|
+
await fs$2.mkdir(path$1.dirname(tokenPath), { recursive: true });
|
|
485
|
+
await fs$2.writeFile(
|
|
482
486
|
tokenPath,
|
|
483
487
|
JSON.stringify({ token: req.body.token }, null, 2)
|
|
484
488
|
);
|
|
@@ -515,12 +519,61 @@ async function whoami() {
|
|
|
515
519
|
);
|
|
516
520
|
}
|
|
517
521
|
|
|
522
|
+
function detectLightTerminal() {
|
|
523
|
+
const vscodeTheme = process.env.VSCODE_THEME_KIND;
|
|
524
|
+
if (vscodeTheme) {
|
|
525
|
+
return vscodeTheme === "vscode-light" || vscodeTheme === "vscode-high-contrast-light";
|
|
526
|
+
}
|
|
527
|
+
const colorfgbg = process.env.COLORFGBG;
|
|
528
|
+
if (colorfgbg) {
|
|
529
|
+
const parts = colorfgbg.split(";");
|
|
530
|
+
const bg = parseInt(parts[parts.length - 1] ?? "", 10);
|
|
531
|
+
if (!isNaN(bg)) return bg >= 8;
|
|
532
|
+
}
|
|
533
|
+
if (process.stdout.isTTY) {
|
|
534
|
+
try {
|
|
535
|
+
const savedState = execSync("stty -g </dev/tty 2>/dev/null", { encoding: "utf8" }).trim();
|
|
536
|
+
if (!savedState) return false;
|
|
537
|
+
const tty = fs.openSync("/dev/tty", "r+");
|
|
538
|
+
try {
|
|
539
|
+
execSync("stty -icanon -echo min 0 time 2 </dev/tty 2>/dev/null");
|
|
540
|
+
fs.writeSync(tty, "\x1B]11;?\x07");
|
|
541
|
+
let resp = "";
|
|
542
|
+
const buf = Buffer.alloc(64);
|
|
543
|
+
for (let i = 0; i < 16; i++) {
|
|
544
|
+
const n = fs.readSync(tty, buf, 0, 64, null);
|
|
545
|
+
if (n === 0) break;
|
|
546
|
+
resp += buf.slice(0, n).toString();
|
|
547
|
+
if (resp.includes("\x07") || resp.includes("\x1B\\")) break;
|
|
548
|
+
}
|
|
549
|
+
const m = resp.match(/rgb:([0-9a-f]+)\/([0-9a-f]+)\/([0-9a-f]+)/i);
|
|
550
|
+
if (m?.[1] && m[2] && m[3]) {
|
|
551
|
+
const norm = (h) => parseInt(h.slice(0, 2), 16);
|
|
552
|
+
const r = norm(m[1]), g = norm(m[2]), b = norm(m[3]);
|
|
553
|
+
return 0.299 * r + 0.587 * g + 0.114 * b > 127;
|
|
554
|
+
}
|
|
555
|
+
} finally {
|
|
556
|
+
try {
|
|
557
|
+
fs.closeSync(tty);
|
|
558
|
+
} catch {
|
|
559
|
+
}
|
|
560
|
+
try {
|
|
561
|
+
execSync(`stty ${savedState} </dev/tty 2>/dev/null`);
|
|
562
|
+
} catch {
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
} catch {
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
return false;
|
|
569
|
+
}
|
|
570
|
+
const isLightTerminal = detectLightTerminal();
|
|
518
571
|
const c = {
|
|
519
572
|
primary: (s) => chalk.rgb(0, 232, 198)(s),
|
|
520
573
|
secondary: (s) => chalk.rgb(0, 198, 232)(s),
|
|
521
574
|
accent: (s) => chalk.rgb(130, 100, 255)(s),
|
|
522
575
|
highlight: (s) => chalk.rgb(100, 232, 130)(s),
|
|
523
|
-
muted: (s) => chalk.rgb(200, 210, 220)(s),
|
|
576
|
+
muted: (s) => isLightTerminal ? chalk.black(s) : chalk.rgb(200, 210, 220)(s),
|
|
524
577
|
link: (s) => chalk.rgb(255, 160, 100)(s),
|
|
525
578
|
sky: (s) => chalk.rgb(0, 210, 255)(s),
|
|
526
579
|
error: (s) => chalk.rgb(255, 100, 100)(s)
|
|
@@ -649,12 +702,12 @@ async function createSandbox(name, {
|
|
|
649
702
|
async function logout() {
|
|
650
703
|
const tokenPath = path$1.join(os$1.homedir(), ".pocketenv", "token.json");
|
|
651
704
|
try {
|
|
652
|
-
await fs$
|
|
705
|
+
await fs$2.access(tokenPath);
|
|
653
706
|
} catch {
|
|
654
707
|
consola.log("Logged out successfully");
|
|
655
708
|
return;
|
|
656
709
|
}
|
|
657
|
-
await fs$
|
|
710
|
+
await fs$2.unlink(tokenPath);
|
|
658
711
|
consola.log("Logged out successfully");
|
|
659
712
|
}
|
|
660
713
|
|
|
@@ -1053,10 +1106,10 @@ async function putKeys(sandbox, options) {
|
|
|
1053
1106
|
publicKey = generated.publicKey;
|
|
1054
1107
|
}
|
|
1055
1108
|
if (options.privateKey && !options.generate) {
|
|
1056
|
-
privateKey = await fs$
|
|
1109
|
+
privateKey = await fs$2.readFile(options.privateKey, "utf8");
|
|
1057
1110
|
}
|
|
1058
1111
|
if (options.publicKey && !options.generate) {
|
|
1059
|
-
publicKey = await fs$
|
|
1112
|
+
publicKey = await fs$2.readFile(options.publicKey, "utf8");
|
|
1060
1113
|
}
|
|
1061
1114
|
const validatePrivateKey = (value) => {
|
|
1062
1115
|
const trimmed = value.trim();
|
|
@@ -1381,12 +1434,12 @@ async function putFile(sandbox, remotePath, localPath) {
|
|
|
1381
1434
|
} else if (localPath) {
|
|
1382
1435
|
const resolvedPath = path.resolve(localPath);
|
|
1383
1436
|
try {
|
|
1384
|
-
await fs.access(resolvedPath);
|
|
1437
|
+
await fs$1.access(resolvedPath);
|
|
1385
1438
|
} catch (err) {
|
|
1386
1439
|
consola.error(`No such file: ${c.error(localPath)}`);
|
|
1387
1440
|
process.exit(1);
|
|
1388
1441
|
}
|
|
1389
|
-
content = await fs.readFile(resolvedPath, "utf-8");
|
|
1442
|
+
content = await fs$1.readFile(resolvedPath, "utf-8");
|
|
1390
1443
|
} else {
|
|
1391
1444
|
content = (await editor({
|
|
1392
1445
|
message: "File content (opens in $EDITOR):",
|
package/package.json
CHANGED
package/src/cmd/start.ts
CHANGED
package/src/lib/expandRepo.ts
CHANGED
|
@@ -5,5 +5,8 @@ export function expandRepo(repo: string): string {
|
|
|
5
5
|
const tangledMatch = repo.match(/^tangled:([^/]+\/[^/]+)$/);
|
|
6
6
|
if (tangledMatch) return `https://tangled.org/${tangledMatch[1]}`;
|
|
7
7
|
|
|
8
|
+
const gitlabMatch = repo.match(/^gitlab:([^/]+\/[^/]+)$/);
|
|
9
|
+
if (gitlabMatch) return `https://gitlab.com/${gitlabMatch[1]}`;
|
|
10
|
+
|
|
8
11
|
return repo;
|
|
9
12
|
}
|
package/src/theme.ts
CHANGED
|
@@ -1,11 +1,71 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
|
+
import { execSync } from "child_process";
|
|
3
|
+
import * as fs from "fs";
|
|
4
|
+
|
|
5
|
+
function detectLightTerminal(): boolean {
|
|
6
|
+
// VS Code terminal
|
|
7
|
+
const vscodeTheme = process.env.VSCODE_THEME_KIND;
|
|
8
|
+
if (vscodeTheme) {
|
|
9
|
+
return vscodeTheme === "vscode-light" || vscodeTheme === "vscode-high-contrast-light";
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// COLORFGBG — set by xterm, iTerm2, etc. ("fg;bg", bg >= 8 = light)
|
|
13
|
+
const colorfgbg = process.env.COLORFGBG;
|
|
14
|
+
if (colorfgbg) {
|
|
15
|
+
const parts = colorfgbg.split(";");
|
|
16
|
+
const bg = parseInt(parts[parts.length - 1] ?? "", 10);
|
|
17
|
+
if (!isNaN(bg)) return bg >= 8;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// OSC 11 background color query — works with Apple Terminal, iTerm2, etc.
|
|
21
|
+
// stty is redirected from /dev/tty explicitly because execSync pipes stdio,
|
|
22
|
+
// which means stty would otherwise fail to find the terminal.
|
|
23
|
+
if (process.stdout.isTTY) {
|
|
24
|
+
try {
|
|
25
|
+
const savedState = execSync("stty -g </dev/tty 2>/dev/null", { encoding: "utf8" }).trim();
|
|
26
|
+
// If we couldn't save the state, skip to avoid leaving the terminal in raw mode.
|
|
27
|
+
if (!savedState) return false;
|
|
28
|
+
const tty = fs.openSync("/dev/tty", "r+");
|
|
29
|
+
try {
|
|
30
|
+
// Use -icanon -echo instead of raw: avoids disabling ISIG (Ctrl+C) so
|
|
31
|
+
// signal handling stays intact even if the restore below fails.
|
|
32
|
+
execSync("stty -icanon -echo min 0 time 2 </dev/tty 2>/dev/null");
|
|
33
|
+
fs.writeSync(tty, "\x1b]11;?\x07");
|
|
34
|
+
// Read in a loop until we see the response terminator (BEL or ST),
|
|
35
|
+
// so leftover bytes don't leak into the terminal input buffer.
|
|
36
|
+
let resp = "";
|
|
37
|
+
const buf = Buffer.alloc(64);
|
|
38
|
+
for (let i = 0; i < 16; i++) {
|
|
39
|
+
const n = fs.readSync(tty, buf, 0, 64, null);
|
|
40
|
+
if (n === 0) break;
|
|
41
|
+
resp += buf.slice(0, n).toString();
|
|
42
|
+
if (resp.includes("\x07") || resp.includes("\x1b\\")) break;
|
|
43
|
+
}
|
|
44
|
+
const m = resp.match(/rgb:([0-9a-f]+)\/([0-9a-f]+)\/([0-9a-f]+)/i);
|
|
45
|
+
if (m?.[1] && m[2] && m[3]) {
|
|
46
|
+
// Components can be 2 or 4 hex digits; normalize to 0-255
|
|
47
|
+
const norm = (h: string) => parseInt(h.slice(0, 2), 16);
|
|
48
|
+
const r = norm(m[1]), g = norm(m[2]), b = norm(m[3]);
|
|
49
|
+
return 0.299 * r + 0.587 * g + 0.114 * b > 127;
|
|
50
|
+
}
|
|
51
|
+
} finally {
|
|
52
|
+
try { fs.closeSync(tty); } catch {}
|
|
53
|
+
try { execSync(`stty ${savedState} </dev/tty 2>/dev/null`); } catch {}
|
|
54
|
+
}
|
|
55
|
+
} catch {}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const isLightTerminal = detectLightTerminal();
|
|
2
62
|
|
|
3
63
|
export const c = {
|
|
4
64
|
primary: (s: string | number) => chalk.rgb(0, 232, 198)(s),
|
|
5
65
|
secondary: (s: string | number) => chalk.rgb(0, 198, 232)(s),
|
|
6
66
|
accent: (s: string | number) => chalk.rgb(130, 100, 255)(s),
|
|
7
67
|
highlight: (s: string | number) => chalk.rgb(100, 232, 130)(s),
|
|
8
|
-
muted: (s: string | number) => chalk.rgb(200, 210, 220)(s),
|
|
68
|
+
muted: (s: string | number) => isLightTerminal ? chalk.black(s) : chalk.rgb(200, 210, 220)(s),
|
|
9
69
|
link: (s: string | number) => chalk.rgb(255, 160, 100)(s),
|
|
10
70
|
sky: (s: string | number) => chalk.rgb(0, 210, 255)(s),
|
|
11
71
|
error: (s: string | number) => chalk.rgb(255, 100, 100)(s),
|