@pocketenv/cli 0.3.3 → 0.3.4
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 +61 -14
- package/package.json +1 -1
- package/src/cmd/start.ts +0 -1
- package/src/lib/expandRepo.ts +3 -0
- package/src/theme.ts +59 -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.4";
|
|
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,55 @@ 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 raw -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
|
+
fs.closeSync(tty);
|
|
557
|
+
execSync(`stty ${savedState} </dev/tty 2>/dev/null`);
|
|
558
|
+
}
|
|
559
|
+
} catch {
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
return false;
|
|
563
|
+
}
|
|
564
|
+
const isLightTerminal = detectLightTerminal();
|
|
518
565
|
const c = {
|
|
519
566
|
primary: (s) => chalk.rgb(0, 232, 198)(s),
|
|
520
567
|
secondary: (s) => chalk.rgb(0, 198, 232)(s),
|
|
521
568
|
accent: (s) => chalk.rgb(130, 100, 255)(s),
|
|
522
569
|
highlight: (s) => chalk.rgb(100, 232, 130)(s),
|
|
523
|
-
muted: (s) => chalk.rgb(200, 210, 220)(s),
|
|
570
|
+
muted: (s) => isLightTerminal ? chalk.black(s) : chalk.rgb(200, 210, 220)(s),
|
|
524
571
|
link: (s) => chalk.rgb(255, 160, 100)(s),
|
|
525
572
|
sky: (s) => chalk.rgb(0, 210, 255)(s),
|
|
526
573
|
error: (s) => chalk.rgb(255, 100, 100)(s)
|
|
@@ -649,12 +696,12 @@ async function createSandbox(name, {
|
|
|
649
696
|
async function logout() {
|
|
650
697
|
const tokenPath = path$1.join(os$1.homedir(), ".pocketenv", "token.json");
|
|
651
698
|
try {
|
|
652
|
-
await fs$
|
|
699
|
+
await fs$2.access(tokenPath);
|
|
653
700
|
} catch {
|
|
654
701
|
consola.log("Logged out successfully");
|
|
655
702
|
return;
|
|
656
703
|
}
|
|
657
|
-
await fs$
|
|
704
|
+
await fs$2.unlink(tokenPath);
|
|
658
705
|
consola.log("Logged out successfully");
|
|
659
706
|
}
|
|
660
707
|
|
|
@@ -1053,10 +1100,10 @@ async function putKeys(sandbox, options) {
|
|
|
1053
1100
|
publicKey = generated.publicKey;
|
|
1054
1101
|
}
|
|
1055
1102
|
if (options.privateKey && !options.generate) {
|
|
1056
|
-
privateKey = await fs$
|
|
1103
|
+
privateKey = await fs$2.readFile(options.privateKey, "utf8");
|
|
1057
1104
|
}
|
|
1058
1105
|
if (options.publicKey && !options.generate) {
|
|
1059
|
-
publicKey = await fs$
|
|
1106
|
+
publicKey = await fs$2.readFile(options.publicKey, "utf8");
|
|
1060
1107
|
}
|
|
1061
1108
|
const validatePrivateKey = (value) => {
|
|
1062
1109
|
const trimmed = value.trim();
|
|
@@ -1381,12 +1428,12 @@ async function putFile(sandbox, remotePath, localPath) {
|
|
|
1381
1428
|
} else if (localPath) {
|
|
1382
1429
|
const resolvedPath = path.resolve(localPath);
|
|
1383
1430
|
try {
|
|
1384
|
-
await fs.access(resolvedPath);
|
|
1431
|
+
await fs$1.access(resolvedPath);
|
|
1385
1432
|
} catch (err) {
|
|
1386
1433
|
consola.error(`No such file: ${c.error(localPath)}`);
|
|
1387
1434
|
process.exit(1);
|
|
1388
1435
|
}
|
|
1389
|
-
content = await fs.readFile(resolvedPath, "utf-8");
|
|
1436
|
+
content = await fs$1.readFile(resolvedPath, "utf-8");
|
|
1390
1437
|
} else {
|
|
1391
1438
|
content = (await editor({
|
|
1392
1439
|
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,69 @@
|
|
|
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
|
+
execSync("stty raw -echo min 0 time 2 </dev/tty 2>/dev/null");
|
|
31
|
+
fs.writeSync(tty, "\x1b]11;?\x07");
|
|
32
|
+
// Read in a loop until we see the response terminator (BEL or ST),
|
|
33
|
+
// so leftover bytes don't leak into the terminal input buffer.
|
|
34
|
+
let resp = "";
|
|
35
|
+
const buf = Buffer.alloc(64);
|
|
36
|
+
for (let i = 0; i < 16; i++) {
|
|
37
|
+
const n = fs.readSync(tty, buf, 0, 64, null);
|
|
38
|
+
if (n === 0) break;
|
|
39
|
+
resp += buf.slice(0, n).toString();
|
|
40
|
+
if (resp.includes("\x07") || resp.includes("\x1b\\")) break;
|
|
41
|
+
}
|
|
42
|
+
const m = resp.match(/rgb:([0-9a-f]+)\/([0-9a-f]+)\/([0-9a-f]+)/i);
|
|
43
|
+
if (m?.[1] && m[2] && m[3]) {
|
|
44
|
+
// Components can be 2 or 4 hex digits; normalize to 0-255
|
|
45
|
+
const norm = (h: string) => parseInt(h.slice(0, 2), 16);
|
|
46
|
+
const r = norm(m[1]), g = norm(m[2]), b = norm(m[3]);
|
|
47
|
+
return 0.299 * r + 0.587 * g + 0.114 * b > 127;
|
|
48
|
+
}
|
|
49
|
+
} finally {
|
|
50
|
+
fs.closeSync(tty);
|
|
51
|
+
execSync(`stty ${savedState} </dev/tty 2>/dev/null`);
|
|
52
|
+
}
|
|
53
|
+
} catch {}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const isLightTerminal = detectLightTerminal();
|
|
2
60
|
|
|
3
61
|
export const c = {
|
|
4
62
|
primary: (s: string | number) => chalk.rgb(0, 232, 198)(s),
|
|
5
63
|
secondary: (s: string | number) => chalk.rgb(0, 198, 232)(s),
|
|
6
64
|
accent: (s: string | number) => chalk.rgb(130, 100, 255)(s),
|
|
7
65
|
highlight: (s: string | number) => chalk.rgb(100, 232, 130)(s),
|
|
8
|
-
muted: (s: string | number) => chalk.rgb(200, 210, 220)(s),
|
|
66
|
+
muted: (s: string | number) => isLightTerminal ? chalk.black(s) : chalk.rgb(200, 210, 220)(s),
|
|
9
67
|
link: (s: string | number) => chalk.rgb(255, 160, 100)(s),
|
|
10
68
|
sky: (s: string | number) => chalk.rgb(0, 210, 255)(s),
|
|
11
69
|
error: (s: string | number) => chalk.rgb(255, 100, 100)(s),
|