OXYGENHUB
# ff39a5e808da spectra-vscode / src / cli.ts
2.7 KiB · last changed Spectra for VS Code 0.1: set up and chat without the command line (#6) 1 day ago source blame history raw
ff39a5e8 Andy McCall ↶ 1
// Running the spectra program: setup commands, and the bridge.
2
import * as cp from "node:child_process";
3
import * as fs from "node:fs";
4
import * as os from "node:os";
5
import * as path from "node:path";
6
import * as vscode from "vscode";
7
8
export interface Ran {
9
  code: number;
10
  stdout: string;
11
  stderr: string;
12
}
13
14
/** The spectra program: the setting, else PATH, else ~/.local/bin. */
15
export function binary(): string | undefined {
16
  const set = vscode.workspace.getConfiguration("spectra").get<string>("path")?.trim();
17
  if (set) return set.replace(/^~(?=\/)/, os.homedir());
18
  for (const dir of (process.env.PATH ?? "").split(path.delimiter)) {
19
    const p = path.join(dir, "spectra");
20
    if (dir && fs.existsSync(p)) return p;
21
  }
22
  const local = path.join(os.homedir(), ".local", "bin", "spectra");
23
  return fs.existsSync(local) ? local : undefined;
24
}
25
26
export function run(args: string[], cwd?: string, input?: string): Promise<Ran> {
27
  const bin = binary();
28
  if (!bin) return Promise.resolve({ code: 127, stdout: "", stderr: "spectra is not installed (Spectra: Check for spectra)" });
29
  return new Promise((resolve) => {
30
    const child = cp.execFile(bin, args, { cwd, env: { ...process.env, NO_COLOR: "1" }, maxBuffer: 16 * 1024 * 1024 }, (err, stdout, stderr) => {
31
      const code = err && typeof (err as cp.ExecFileException).code === "number" ? ((err as cp.ExecFileException).code as number) : err ? 1 : 0;
32
      resolve({ code, stdout: String(stdout), stderr: String(stderr) });
33
    });
34
    if (input !== undefined) {
35
      child.stdin?.end(input);
36
    }
37
  });
38
}
39
40
/** Run, and parse stdout as JSON; throw with spectra's own words on failure. */
41
export async function json<T>(args: string[], cwd?: string): Promise<T> {
42
  const r = await run([...args], cwd);
43
  if (r.code !== 0) throw new Error(why(r));
44
  return JSON.parse(r.stdout) as T;
45
}
46
47
/** Run; throw with spectra's own words on failure. */
48
export async function ok(args: string[], cwd?: string, input?: string): Promise<string> {
49
  const r = await run(args, cwd, input);
50
  if (r.code !== 0) throw new Error(why(r));
51
  return r.stdout;
52
}
53
54
/** The error spectra printed, without its `error:` prefix. */
55
export function why(r: Ran): string {
56
  const text = (r.stderr.trim() || r.stdout.trim()).split("\n").filter((l) => l.trim()).pop() ?? `spectra exited ${r.code}`;
57
  return text.replace(/^error:\s*/, "");
58
}
59
60
export interface Status {
61
  version: string;
62
  home: string;
63
  identity: { name: string; key: string } | null;
64
  server: { url: string; instance: string } | null;
65
  local: { data: string; initialised: boolean; serving: boolean };
66
  reachable: boolean;
67
}
68
69
export function status(): Promise<Status> {
70
  return json<Status>(["status", "--json"]);
71
}