@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 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$1 from 'node:fs/promises';
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.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$1.mkdir(path$1.dirname(tokenPath), { recursive: true });
481
- await fs$1.writeFile(
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$1.access(tokenPath);
705
+ await fs$2.access(tokenPath);
653
706
  } catch {
654
707
  consola.log("Logged out successfully");
655
708
  return;
656
709
  }
657
- await fs$1.unlink(tokenPath);
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$1.readFile(options.privateKey, "utf8");
1109
+ privateKey = await fs$2.readFile(options.privateKey, "utf8");
1057
1110
  }
1058
1111
  if (options.publicKey && !options.generate) {
1059
- publicKey = await fs$1.readFile(options.publicKey, "utf8");
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
@@ -4,7 +4,7 @@
4
4
  "bin": {
5
5
  "pocketenv": "dist/index.js"
6
6
  },
7
- "version": "0.3.3",
7
+ "version": "0.3.5",
8
8
  "type": "module",
9
9
  "keywords": [
10
10
  "sandbox",
package/src/cmd/start.ts CHANGED
@@ -16,7 +16,6 @@ async function start(
16
16
 
17
17
  try {
18
18
  const authToken = env.POCKETENV_TOKEN || token;
19
-
20
19
  await client.post(
21
20
  "/xrpc/io.pocketenv.sandbox.startSandbox",
22
21
  {
@@ -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),