vercel-vm-factory 0.12.8 → 0.16.8

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/README.md CHANGED
@@ -29,7 +29,7 @@ Run without flags for prompts:
29
29
  npx vercel-vm-factory create
30
30
  ```
31
31
 
32
- The prompt walks through VM image, project, shell, optional preinstalled tools, and authentication. For list prompts, enter either names or numbers; tool choices can be comma-separated, for example `1,3` or `nodejs,claude-code`.
32
+ The prompt walks through VM image, Vercel team, project, shell, optional preinstalled tools, and authentication. Teams are loaded from `vercel teams list --format json`; choose `Default` to use the current Vercel CLI scope. For list prompts, enter either names or numbers; tool choices can be comma-separated, for example `1,3` or `nodejs,claude-code`.
33
33
 
34
34
  Check local setup:
35
35
 
@@ -47,7 +47,7 @@ Common flags:
47
47
  - `--shell /bin/bash|/bin/zsh|/bin/sh`
48
48
  - `--tools nodejs,codex,claude-code`
49
49
  - `--project NAME`
50
- - `--scope TEAM_SLUG`
50
+ - `--scope TEAM_SLUG` to skip team selection
51
51
  - `--auth-mode basic|github|both|none`
52
52
  - `--dry-run`
53
53
 
@@ -57,7 +57,7 @@ The generated project contains only `Dockerfile.vercel`.
57
57
 
58
58
  CLI mapping:
59
59
 
60
- - Vercel Team -> `--scope TEAM_SLUG` when needed; omit it to use the CLI default scope
60
+ - Vercel Team -> loaded from `vercel teams list --format json`; `--scope TEAM_SLUG` skips the selector
61
61
  - Project Name -> `--project x-shell`
62
62
  - Application Preset -> patched through Vercel API as `framework=container`
63
63
  - Root Directory -> generated project directory
package/config.mjs ADDED
@@ -0,0 +1,21 @@
1
+ export const defaultWsShellImage = "ghcr.io/v1xingyue/ws-shell:v1.8.alpine";
2
+
3
+ export const vmImages = {
4
+ alpine: "alpine:3.23",
5
+ ubuntu: "ubuntu:24.04",
6
+ debian: "debian:13-slim",
7
+ };
8
+
9
+ export const shells = ["/bin/bash", "/bin/zsh", "/bin/sh"];
10
+
11
+ export const toolChoices = {
12
+ nodejs: "Node.js + npm",
13
+ codex: "OpenAI Codex CLI",
14
+ "claude-code": "Claude Code",
15
+ };
16
+
17
+ export const codeDefaults = {
18
+ "vm-image": "alpine",
19
+ from: defaultWsShellImage,
20
+ shell: "/bin/sh",
21
+ };
package/deploy-vm.mjs CHANGED
@@ -4,33 +4,23 @@ import { spawn } from "node:child_process";
4
4
  import { stdin as input, stdout as output } from "node:process";
5
5
  import { homedir } from "node:os";
6
6
  import path from "node:path";
7
+ import { fileURLToPath } from "node:url";
7
8
  import * as p from "@clack/prompts";
8
-
9
- const defaultWsShellImage = "ghcr.io/v1xingyue/ws-shell:v1.8.alpine";
10
-
11
- const vmImages = {
12
- alpine: "alpine:3.23",
13
- ubuntu: "ubuntu:24.04",
14
- debian: "debian:13-slim",
15
- };
16
- const shells = ["/bin/bash", "/bin/zsh", "/bin/sh"];
17
- const toolChoices = {
18
- nodejs: "Node.js + npm",
19
- codex: "OpenAI Codex CLI",
20
- "claude-code": "Claude Code",
21
- };
9
+ import {
10
+ codeDefaults,
11
+ defaultWsShellImage,
12
+ shells,
13
+ toolChoices,
14
+ vmImages,
15
+ } from "./config.mjs";
22
16
 
23
17
  const { command, args } = parseCommand(process.argv.slice(2));
24
- const scriptRoot = path.resolve(import.meta.dirname);
18
+ const scriptFile = fileURLToPath(import.meta.url);
19
+ const scriptRoot = path.dirname(scriptFile);
25
20
  const workspaceRoot = process.cwd();
26
21
  const stateRoot = path.join(homedir(), ".vercel-vm-factory");
27
22
  const defaultsPath = path.join(stateRoot, "defaults.json");
28
23
  const legacyDefaultsPath = path.join(scriptRoot, ".defaults.json");
29
- const codeDefaults = {
30
- "vm-image": "alpine",
31
- from: defaultWsShellImage,
32
- shell: "/bin/sh",
33
- };
34
24
  const packagedDefaults = {
35
25
  ...(await readDefaults(legacyDefaultsPath)),
36
26
  ...codeDefaults,
@@ -85,11 +75,7 @@ async function main() {
85
75
  args["vm-image"] ?? args.base ?? defaults["vm-image"] ?? "alpine",
86
76
  );
87
77
  const vmImage = vmImages[vmImageName] ?? vmImageName;
88
- const scope = await optionalValue(
89
- "scope",
90
- "Vercel team/scope",
91
- defaults.scope,
92
- );
78
+ const scope = await chooseVercelScope(defaults.scope);
93
79
  const project = await value(
94
80
  "project",
95
81
  "Vercel project name",
@@ -493,17 +479,6 @@ async function secret(name, question, fallback) {
493
479
  return answer || fallback || "";
494
480
  }
495
481
 
496
- async function optionalValue(name, question, fallback) {
497
- if (args[name] !== undefined) return args[name];
498
- if (!input.isTTY) return "";
499
-
500
- const answer = await askText(
501
- question,
502
- fallback ? `${fallback}; Enter to skip` : "skip",
503
- );
504
- return answer;
505
- }
506
-
507
482
  function defaultAuthMode() {
508
483
  if (args["auth-mode"]) return args["auth-mode"];
509
484
  const hasBasic = Boolean(
@@ -621,6 +596,62 @@ async function chooseTools(fallback) {
621
596
  return selected.join(",");
622
597
  }
623
598
 
599
+ async function chooseVercelScope(fallback) {
600
+ if (args.scope !== undefined) return args.scope;
601
+ if (!input.isTTY) return fallback || "";
602
+
603
+ const teams = await getVercelTeams();
604
+ const options = [
605
+ { value: "", label: "Default", hint: "current Vercel CLI scope" },
606
+ ...teams.map((team) => ({
607
+ value: team.slug,
608
+ label: team.name || team.slug,
609
+ hint: team.slug,
610
+ })),
611
+ ];
612
+
613
+ return promptResult(
614
+ p.select({
615
+ message: "Vercel team",
616
+ initialValue: teams.some((team) => team.slug === fallback)
617
+ ? fallback
618
+ : "",
619
+ options,
620
+ }),
621
+ );
622
+ }
623
+
624
+ async function getVercelTeams() {
625
+ try {
626
+ const commandArgs = ["teams", "list", "--format", "json"];
627
+ if (args.token) commandArgs.push("--token", args.token);
628
+ const text = await runCapture("vercel", commandArgs);
629
+ return extractVercelTeams(text);
630
+ } catch {
631
+ warn("could not load Vercel teams; using default scope");
632
+ return [];
633
+ }
634
+ }
635
+
636
+ function extractVercelTeams(text) {
637
+ const start = text.indexOf("[");
638
+ const end = text.lastIndexOf("]");
639
+ if (start === -1 || end === -1 || end < start) return [];
640
+
641
+ try {
642
+ const data = JSON.parse(text.slice(start, end + 1));
643
+ const teams = Array.isArray(data) ? data : data.teams || [];
644
+ return teams
645
+ .map((team) => ({
646
+ name: team.name || team.slug || "",
647
+ slug: team.slug || "",
648
+ }))
649
+ .filter((team) => team.slug);
650
+ } catch {
651
+ return [];
652
+ }
653
+ }
654
+
624
655
  function parseTools(value) {
625
656
  const names = Object.keys(toolChoices);
626
657
  const selected = String(value || "")
@@ -647,7 +678,7 @@ async function readDefaults(file) {
647
678
  async function syncDefaults() {
648
679
  const current = await readDefaults(defaultsPath);
649
680
  const codeMtime = Math.max(
650
- await readMtime(import.meta.filename),
681
+ await readMtime(scriptFile),
651
682
  await readMtime(legacyDefaultsPath),
652
683
  );
653
684
  const homeMtime = await readMtime(defaultsPath);
@@ -934,7 +965,7 @@ Options:
934
965
  --vm-image NAME alpine, ubuntu, debian, or a custom VM image
935
966
  --base NAME Alias for --vm-image
936
967
  --project NAME Vercel project name
937
- --scope SLUG Optional Vercel team/user scope slug
968
+ --scope SLUG Vercel team/user scope slug; skips team selector
938
969
  --from IMAGE Source image for /app/bin/wsterm
939
970
  --shell PATH /bin/bash, /bin/zsh, or /bin/sh
940
971
  --tools LIST nodejs,codex,claude-code
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vercel-vm-factory",
3
- "version": "0.12.8",
3
+ "version": "0.16.8",
4
4
  "description": "Create Vercel Container deployments for ws-shell from selectable VM images.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -9,6 +9,7 @@
9
9
  },
10
10
  "files": [
11
11
  "bin/",
12
+ "config.mjs",
12
13
  "deploy-vm.mjs",
13
14
  "README.md"
14
15
  ],