@tenyokjs/devdevian-cli 0.0.0-stage → 0.2.0-alpha.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/lib/window.mjs ADDED
@@ -0,0 +1,119 @@
1
+ import { spawn, spawnSync } from "node:child_process";
2
+ import { mkdtemp, writeFile, rm } from "node:fs/promises";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ export const shellQuote = (value) =>
6
+ "'" + String(value).replaceAll("'", "'\\''") + "'";
7
+ export function macScript({ node, entry, args = [], cwd, env = {} }) {
8
+ const selected = ["DEVDEVIAN_CONFIG_DIR", "NO_COLOR"]
9
+ .filter((key) => env[key])
10
+ .map((key) => `${key}=${shellQuote(env[key])} `)
11
+ .join("");
12
+ return `#!/bin/sh\ncd -- ${shellQuote(cwd)} || exit 1\n${selected}${[node, entry, "tui", ...args].map(shellQuote).join(" ")}\nresult=$?\nrm -f -- "$0"\nrmdir -- "$(dirname -- "$0")" 2>/dev/null\nexit "$result"\n`;
13
+ }
14
+ const available = (command) =>
15
+ spawnSync(process.platform === "win32" ? "where" : "which", [command], {
16
+ stdio: "ignore",
17
+ shell: false,
18
+ }).status === 0;
19
+ export async function openWindow({
20
+ entry,
21
+ args = [],
22
+ cwd = process.cwd(),
23
+ env = process.env,
24
+ platform = process.platform,
25
+ node = process.execPath,
26
+ } = {}) {
27
+ let command, argv, temporary;
28
+ if (platform === "darwin") {
29
+ temporary = await mkdtemp(join(tmpdir(), "devdevian-window-"));
30
+ const script = join(temporary, "Devdevian.command");
31
+ await writeFile(script, macScript({ node, entry, args, cwd, env }), {
32
+ mode: 0o700,
33
+ });
34
+ command = "open";
35
+ argv = ["-na", "Terminal", script];
36
+ } else if (platform === "win32") {
37
+ if (!available("wt.exe"))
38
+ throw Error(
39
+ "Windows Terminal (wt.exe) is required for a separate window. Run devdevian tui in the current terminal instead.",
40
+ );
41
+ command = "wt.exe";
42
+ argv = [
43
+ "-w",
44
+ "new",
45
+ "new-tab",
46
+ "--startingDirectory",
47
+ cwd,
48
+ node,
49
+ entry,
50
+ "tui",
51
+ ...args,
52
+ ];
53
+ } else {
54
+ if (!env.DISPLAY && !env.WAYLAND_DISPLAY)
55
+ throw Error(
56
+ "A graphical session is needed for a new window. Over SSH, run devdevian tui in your current terminal.",
57
+ );
58
+ if (available("gnome-terminal")) {
59
+ command = "gnome-terminal";
60
+ argv = ["--working-directory", cwd, "--", node, entry, "tui", ...args];
61
+ } else if (available("konsole")) {
62
+ command = "konsole";
63
+ argv = [
64
+ "--separate",
65
+ "--workdir",
66
+ cwd,
67
+ "-e",
68
+ node,
69
+ entry,
70
+ "tui",
71
+ ...args,
72
+ ];
73
+ } else if (available("xterm")) {
74
+ command = "xterm";
75
+ argv = ["-T", "Devdevian", "-e", node, entry, "tui", ...args];
76
+ } else
77
+ throw Error(
78
+ "Install GNOME Terminal, Konsole or xterm, or run devdevian tui in your current terminal.",
79
+ );
80
+ }
81
+ try {
82
+ await new Promise((resolve, reject) => {
83
+ const child = spawn(command, argv, {
84
+ cwd,
85
+ env,
86
+ stdio: "ignore",
87
+ detached: true,
88
+ shell: false,
89
+ });
90
+ // Launchers may spawn successfully and still reject the requested application.
91
+ const launcherExits = platform === "darwin" || platform === "win32";
92
+ const timer = setTimeout(
93
+ () => {
94
+ if (launcherExits) {
95
+ child.kill();
96
+ reject(Error("Terminal launcher timed out."));
97
+ } else {
98
+ child.unref();
99
+ resolve();
100
+ }
101
+ },
102
+ launcherExits ? 10000 : 1500,
103
+ );
104
+ child.once("error", (error) => {
105
+ clearTimeout(timer);
106
+ reject(error);
107
+ });
108
+ child.once("exit", (code) => {
109
+ clearTimeout(timer);
110
+ code === 0 ? resolve() : reject(Error("Terminal launcher failed."));
111
+ });
112
+ });
113
+ } catch {
114
+ if (temporary) await rm(temporary, { recursive: true, force: true });
115
+ throw Error(
116
+ "Could not open a terminal window. Run devdevian tui in your current terminal.",
117
+ );
118
+ }
119
+ }
package/package.json CHANGED
@@ -1,6 +1,51 @@
1
1
  {
2
2
  "name": "@tenyokjs/devdevian-cli",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.2.0-alpha.1",
4
+ "description": "Devdevian in your terminal: discussions, questions and human code reviews.",
5
+ "type": "module",
6
+ "bin": {
7
+ "devdevian": "bin/devdevian.mjs"
8
+ },
9
+ "files": [
10
+ "bin/",
11
+ "lib/",
12
+ "README.md",
13
+ "LICENSE"
14
+ ],
15
+ "engines": {
16
+ "node": ">=24"
17
+ },
18
+ "license": "GPL-3.0-only",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/Tenyokj/devdevian.git",
22
+ "directory": "packages/cli"
23
+ },
24
+ "homepage": "https://devdevian.vercel.app",
25
+ "bugs": {
26
+ "url": "https://github.com/Tenyokj/devdevian/issues"
27
+ },
28
+ "publishConfig": {
29
+ "access": "public",
30
+ "registry": "https://registry.npmjs.org/"
31
+ },
32
+ "keywords": [
33
+ "devdevian",
34
+ "cli",
35
+ "forum",
36
+ "code-review",
37
+ "developers"
38
+ ],
39
+ "scripts": {
40
+ "test": "node --test test/*.test.mjs"
41
+ },
42
+ "dependencies": {
43
+ "ink": "8.0.0",
44
+ "react": "19.3.1",
45
+ "string-width": "8.1.1",
46
+ "wrap-ansi": "10.0.0"
47
+ },
48
+ "devDependencies": {
49
+ "@xterm/headless": "5.5.0"
50
+ }
51
+ }