vercel-vm-factory 0.1.1 → 0.1.3

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.
Files changed (3) hide show
  1. package/README.md +5 -0
  2. package/deploy-vm.mjs +227 -54
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -6,6 +6,7 @@ Create a tiny Vercel Container deployment: copy `wsterm` from `ghcr.io/v1xingyue
6
6
  npx vercel-vm-factory create \
7
7
  --base ubuntu \
8
8
  --project ws-shell-ubuntu \
9
+ --auth-mode basic \
9
10
  --auth-user admin \
10
11
  --auth-password change-me
11
12
  ```
@@ -14,6 +15,7 @@ GitHub OAuth is optional:
14
15
 
15
16
  ```bash
16
17
  npx vercel-vm-factory create \
18
+ --auth-mode github \
17
19
  --client-id YOUR_GITHUB_CLIENT_ID \
18
20
  --client-secret YOUR_GITHUB_CLIENT_SECRET \
19
21
  --github-userid 12345678
@@ -25,6 +27,8 @@ Run without flags for prompts:
25
27
  npx vercel-vm-factory create
26
28
  ```
27
29
 
30
+ The prompt asks for authentication first: `basic`, `github`, `both`, or `none`, then only asks for the fields that mode needs.
31
+
28
32
  Check local setup:
29
33
 
30
34
  ```bash
@@ -72,6 +76,7 @@ https://x-shell.vercel.app/auth/github/callback
72
76
 
73
77
  GitHub OAuth fields:
74
78
 
79
+ - Auth mode -> `--auth-mode basic|github|both|none`
75
80
  - Username -> `--auth-user`
76
81
  - Password -> `--auth-password`
77
82
  - Client ID -> `--client-id`
package/deploy-vm.mjs CHANGED
@@ -6,6 +6,8 @@ import { stdin as input, stdout as output } from "node:process";
6
6
  import { homedir } from "node:os";
7
7
  import path from "node:path";
8
8
 
9
+ const defaultWsShellImage = "ghcr.io/v1xingyue/ws-shell:v1.2.alpine";
10
+
9
11
  const bases = {
10
12
  alpine: "alpine:3.23",
11
13
  ubuntu: "ubuntu:24.04",
@@ -18,7 +20,10 @@ const workspaceRoot = process.cwd();
18
20
  const stateRoot = path.join(homedir(), ".vercel-vm-factory");
19
21
  const defaultsPath = path.join(stateRoot, "defaults.json");
20
22
  const legacyDefaultsPath = path.join(scriptRoot, ".defaults.json");
21
- const defaults = { ...(await readDefaults(legacyDefaultsPath)), ...(await readDefaults(defaultsPath)) };
23
+ const defaults = {
24
+ ...(await readDefaults(legacyDefaultsPath)),
25
+ ...(await readDefaults(defaultsPath)),
26
+ };
22
27
  const colorEnabled = output.isTTY && !process.env.NO_COLOR;
23
28
  const color = {
24
29
  dim: (text) => paint(text, "2"),
@@ -41,7 +46,9 @@ try {
41
46
  } catch (error) {
42
47
  const message = error instanceof Error ? error.message : String(error);
43
48
  if (message.includes("scope does not exist")) {
44
- console.error(`\nScope not found. Leave scope empty, or set the real CLI slug with --scope.`);
49
+ console.error(
50
+ `\nScope not found. Leave scope empty, or set the real CLI slug with --scope.`,
51
+ );
45
52
  console.error(`If it was saved before, edit or delete: ${defaultsPath}`);
46
53
  }
47
54
  console.error(message);
@@ -58,12 +65,25 @@ async function main() {
58
65
 
59
66
  const baseName = await chooseBase(args.base ?? defaults.base ?? "alpine");
60
67
  const baseImage = bases[baseName] ?? baseName;
61
- const scope = await optionalValue("scope", "Vercel team/scope", defaults.scope);
62
- const project = await value("project", "Vercel project name", defaults.project ?? `ws-shell-${baseName}`);
63
- const wsShellImage = args.from ?? process.env.WS_SHELL_IMAGE ?? defaults.from ?? "ghcr.io/v1xingyue/ws-shell:v1.1.alpine";
68
+ const scope = await optionalValue(
69
+ "scope",
70
+ "Vercel team/scope",
71
+ defaults.scope,
72
+ );
73
+ const project = await value(
74
+ "project",
75
+ "Vercel project name",
76
+ defaults.project ?? `ws-shell-${baseName}`,
77
+ );
78
+ const wsShellImage =
79
+ args.from ??
80
+ process.env.WS_SHELL_IMAGE ??
81
+ defaults.from ??
82
+ defaultWsShellImage;
64
83
  const prod = args.prod !== "false";
65
84
  const dryRun = Boolean(args["dry-run"]);
66
85
  const skipLink = Boolean(args["skip-link"]);
86
+ const authMode = await chooseAuthMode(defaultAuthMode());
67
87
 
68
88
  const oauthRedirectUrl =
69
89
  args["redirect-url"] ??
@@ -71,7 +91,12 @@ async function main() {
71
91
  defaults["redirect-url"] ??
72
92
  `https://${project}.vercel.app/auth/github/callback`;
73
93
 
74
- const appDir = path.join(workspaceRoot, ".vercel-vm-factory", ".generated", project);
94
+ const appDir = path.join(
95
+ workspaceRoot,
96
+ ".vercel-vm-factory",
97
+ ".generated",
98
+ project,
99
+ );
75
100
  const dockerfile = makeDockerfile({ baseImage, wsShellImage });
76
101
 
77
102
  await rm(appDir, { recursive: true, force: true });
@@ -83,7 +108,8 @@ async function main() {
83
108
  project,
84
109
  scope: scope || "default",
85
110
  source: wsShellImage,
86
- callback: oauthRedirectUrl,
111
+ auth: authMode,
112
+ ...(usesGitHubAuth(authMode) ? { callback: oauthRedirectUrl } : {}),
87
113
  dockerfile: path.join(appDir, "Dockerfile.vercel"),
88
114
  target: prod ? "production" : "preview",
89
115
  });
@@ -94,17 +120,43 @@ async function main() {
94
120
  }
95
121
 
96
122
  await ensureVercelReady();
97
- printOAuthGuide(oauthRedirectUrl);
98
-
99
- const authUsername = await secret("auth-user", "Basic auth username", process.env.AUTH_USERNAME ?? defaults["auth-user"]);
100
- const authPassword = await secret("auth-password", "Basic auth password", process.env.AUTH_PASSWORD ?? defaults["auth-password"]);
101
- const githubClientId = await secret("client-id", "GitHub client id", process.env.GITHUB_CLIENT_ID ?? defaults["client-id"]);
102
- const githubClientSecret = await secret(
103
- "client-secret",
104
- "GitHub client secret",
105
- process.env.GITHUB_CLIENT_SECRET ?? defaults["client-secret"],
106
- );
107
- const allowedUserIds = await secret("github-userid", "Allowed GitHub numeric user id(s)", process.env.ALLOWED_USER_IDS ?? defaults["github-userid"]);
123
+ if (usesGitHubAuth(authMode)) printOAuthGuide(oauthRedirectUrl);
124
+
125
+ const authUsername = usesBasicAuth(authMode)
126
+ ? await secret(
127
+ "auth-user",
128
+ "Basic auth username",
129
+ process.env.AUTH_USERNAME ?? defaults["auth-user"],
130
+ )
131
+ : "";
132
+ const authPassword = usesBasicAuth(authMode)
133
+ ? await secret(
134
+ "auth-password",
135
+ "Basic auth password",
136
+ process.env.AUTH_PASSWORD ?? defaults["auth-password"],
137
+ )
138
+ : "";
139
+ const githubClientId = usesGitHubAuth(authMode)
140
+ ? await secret(
141
+ "client-id",
142
+ "GitHub client id",
143
+ process.env.GITHUB_CLIENT_ID ?? defaults["client-id"],
144
+ )
145
+ : "";
146
+ const githubClientSecret = usesGitHubAuth(authMode)
147
+ ? await secret(
148
+ "client-secret",
149
+ "GitHub client secret",
150
+ process.env.GITHUB_CLIENT_SECRET ?? defaults["client-secret"],
151
+ )
152
+ : "";
153
+ const allowedUserIds = usesGitHubAuth(authMode)
154
+ ? await secret(
155
+ "github-userid",
156
+ "Allowed GitHub numeric user id(s)",
157
+ process.env.ALLOWED_USER_IDS ?? defaults["github-userid"],
158
+ )
159
+ : "";
108
160
 
109
161
  await writeDefaults(defaultsPath, {
110
162
  ...defaults,
@@ -112,6 +164,7 @@ async function main() {
112
164
  scope: scope || undefined,
113
165
  project,
114
166
  from: wsShellImage,
167
+ "auth-mode": authMode,
115
168
  "auth-user": authUsername,
116
169
  "auth-password": authPassword,
117
170
  "client-id": githubClientId,
@@ -125,7 +178,15 @@ async function main() {
125
178
 
126
179
  if (!skipLink) {
127
180
  step("Linking Vercel project");
128
- await runNoUrl("vercel", ["link", "--yes", "--project", project, "--cwd", appDir, ...commonArgs]);
181
+ await runNoUrl("vercel", [
182
+ "link",
183
+ "--yes",
184
+ "--project",
185
+ project,
186
+ "--cwd",
187
+ appDir,
188
+ ...commonArgs,
189
+ ]);
129
190
  } else {
130
191
  warn("skip-link enabled; using existing .vercel/project.json");
131
192
  }
@@ -133,32 +194,33 @@ async function main() {
133
194
  step("Setting project framework=container");
134
195
  await setContainerFramework(appDir, commonArgs);
135
196
 
136
- const vercelArgs = [
137
- "deploy",
138
- appDir,
139
- "--yes",
140
- "--logs",
141
- "--env",
142
- `OAUTH_REDIRECT_URL=${oauthRedirectUrl}`,
143
- ];
197
+ const vercelArgs = ["deploy", appDir, "--yes", "--logs"];
144
198
 
145
199
  if (authUsername) vercelArgs.push("--env", `AUTH_USERNAME=${authUsername}`);
146
200
  if (authPassword) vercelArgs.push("--env", `AUTH_PASSWORD=${authPassword}`);
147
- if (githubClientId) vercelArgs.push("--env", `GITHUB_CLIENT_ID=${githubClientId}`);
148
- if (githubClientSecret) vercelArgs.push("--env", `GITHUB_CLIENT_SECRET=${githubClientSecret}`);
149
- if (allowedUserIds) vercelArgs.push("--env", `ALLOWED_USER_IDS=${allowedUserIds}`);
201
+ if (usesGitHubAuth(authMode))
202
+ vercelArgs.push("--env", `OAUTH_REDIRECT_URL=${oauthRedirectUrl}`);
203
+ if (githubClientId)
204
+ vercelArgs.push("--env", `GITHUB_CLIENT_ID=${githubClientId}`);
205
+ if (githubClientSecret)
206
+ vercelArgs.push("--env", `GITHUB_CLIENT_SECRET=${githubClientSecret}`);
207
+ if (allowedUserIds)
208
+ vercelArgs.push("--env", `ALLOWED_USER_IDS=${allowedUserIds}`);
150
209
  if (prod) vercelArgs.push("--prod");
151
210
  vercelArgs.push(...commonArgs);
152
211
 
153
212
  step("Deploying");
154
213
  const deploymentUrl = await run("vercel", vercelArgs);
155
- console.log(`\n${color.green("Deployment URL:")} ${color.bold(deploymentUrl)}`);
214
+ console.log(
215
+ `\n${color.green("Deployment URL:")} ${color.bold(deploymentUrl)}`,
216
+ );
156
217
  }
157
218
 
158
219
  async function setContainerFramework(appDir, commonArgs) {
159
220
  const projectFile = path.join(appDir, ".vercel", "project.json");
160
221
  const projectConfig = JSON.parse(await readFile(projectFile, "utf8"));
161
- if (!projectConfig.projectId) throw new Error(`Missing projectId in ${projectFile}`);
222
+ if (!projectConfig.projectId)
223
+ throw new Error(`Missing projectId in ${projectFile}`);
162
224
 
163
225
  await runNoUrl("vercel", [
164
226
  "api",
@@ -177,7 +239,9 @@ async function ensureVercelReady() {
177
239
  const version = await runCapture("vercel", ["--version"]);
178
240
  ok(version.split("\n").filter(Boolean).at(-1) || "vercel installed");
179
241
  } catch {
180
- throw new Error("Vercel CLI is not installed. Install it with: pnpm add -g vercel");
242
+ throw new Error(
243
+ "Vercel CLI is not installed. Install it with: pnpm add -g vercel",
244
+ );
181
245
  }
182
246
 
183
247
  step("Checking Vercel login");
@@ -198,12 +262,31 @@ async function doctor() {
198
262
  printKeyValue("base", defaults.base || "not set");
199
263
  printKeyValue("project", defaults.project || "not set");
200
264
  printKeyValue("scope", defaults.scope || "not set");
201
- printKeyValue("source image", defaults.from || "ghcr.io/v1xingyue/ws-shell:v1.1.alpine");
202
- printKeyValue("auth user", defaults["auth-user"] ? mask(defaults["auth-user"]) : "not set");
203
- printKeyValue("auth password", defaults["auth-password"] ? mask(defaults["auth-password"]) : "not set");
204
- printKeyValue("client id", defaults["client-id"] ? mask(defaults["client-id"]) : "not set");
205
- printKeyValue("client secret", defaults["client-secret"] ? mask(defaults["client-secret"]) : "not set");
206
- printKeyValue("github userid", defaults["github-userid"] ? mask(defaults["github-userid"]) : "not set");
265
+ printKeyValue(
266
+ "source image",
267
+ defaults.from || "ghcr.io/v1xingyue/ws-shell:v1.1.alpine",
268
+ );
269
+ printKeyValue("auth mode", defaults["auth-mode"] || "not set");
270
+ printKeyValue(
271
+ "auth user",
272
+ defaults["auth-user"] ? mask(defaults["auth-user"]) : "not set",
273
+ );
274
+ printKeyValue(
275
+ "auth password",
276
+ defaults["auth-password"] ? mask(defaults["auth-password"]) : "not set",
277
+ );
278
+ printKeyValue(
279
+ "client id",
280
+ defaults["client-id"] ? mask(defaults["client-id"]) : "not set",
281
+ );
282
+ printKeyValue(
283
+ "client secret",
284
+ defaults["client-secret"] ? mask(defaults["client-secret"]) : "not set",
285
+ );
286
+ printKeyValue(
287
+ "github userid",
288
+ defaults["github-userid"] ? mask(defaults["github-userid"]) : "not set",
289
+ );
207
290
  }
208
291
 
209
292
  function makeDockerfile({ baseImage, wsShellImage }) {
@@ -229,7 +312,9 @@ async function value(name, question, fallback) {
229
312
  if (!input.isTTY) return current || "";
230
313
 
231
314
  const rl = createInterface({ input, output });
232
- const answer = (await rl.question(`${question}${fallback ? ` [${fallback}]` : ""}: `)).trim();
315
+ const answer = (
316
+ await rl.question(`${question}${fallback ? ` [${fallback}]` : ""}: `)
317
+ ).trim();
233
318
  rl.close();
234
319
  return answer || current || "";
235
320
  }
@@ -256,13 +341,74 @@ async function optionalValue(name, question, fallback) {
256
341
  return answer;
257
342
  }
258
343
 
344
+ function defaultAuthMode() {
345
+ if (args["auth-mode"]) return args["auth-mode"];
346
+ const hasBasic = Boolean(
347
+ args["auth-user"] ||
348
+ args["auth-password"] ||
349
+ process.env.AUTH_USERNAME ||
350
+ process.env.AUTH_PASSWORD,
351
+ );
352
+ const hasGitHub = Boolean(
353
+ args["client-id"] ||
354
+ args["client-secret"] ||
355
+ args["github-userid"] ||
356
+ process.env.GITHUB_CLIENT_ID ||
357
+ process.env.GITHUB_CLIENT_SECRET,
358
+ );
359
+ if (hasBasic && hasGitHub) return "both";
360
+ if (hasBasic) return "basic";
361
+ if (hasGitHub) return "github";
362
+ return defaults["auth-mode"] || (input.isTTY ? "basic" : "none");
363
+ }
364
+
365
+ async function chooseAuthMode(fallback) {
366
+ const modes = new Set(["basic", "github", "both", "none"]);
367
+ if (args["auth-mode"]) {
368
+ if (!modes.has(args["auth-mode"]))
369
+ throw new Error("--auth-mode must be basic, github, both, or none");
370
+ return args["auth-mode"];
371
+ }
372
+ if (!input.isTTY) return modes.has(fallback) ? fallback : "basic";
373
+
374
+ console.log(color.bold("Choose authentication"));
375
+ console.log(` ${color.cyan("1")}. basic username/password`);
376
+ console.log(` ${color.cyan("2")}. GitHub OAuth`);
377
+ console.log(` ${color.cyan("3")}. both`);
378
+ console.log(` ${color.cyan("4")}. none`);
379
+
380
+ const rl = createInterface({ input, output });
381
+ const answer = (await rl.question(`Authentication [${fallback}]: `)).trim();
382
+ rl.close();
383
+
384
+ if (!answer) return modes.has(fallback) ? fallback : "basic";
385
+ if (modes.has(answer)) return answer;
386
+ if (answer === "1") return "basic";
387
+ if (answer === "2") return "github";
388
+ if (answer === "3") return "both";
389
+ if (answer === "4") return "none";
390
+ throw new Error("Authentication must be basic, github, both, or none");
391
+ }
392
+
393
+ function usesBasicAuth(mode) {
394
+ return mode === "basic" || mode === "both";
395
+ }
396
+
397
+ function usesGitHubAuth(mode) {
398
+ return mode === "github" || mode === "both";
399
+ }
400
+
259
401
  async function chooseBase(fallback) {
260
402
  if (args.base) return args.base;
261
403
  if (!input.isTTY) return fallback;
262
404
 
263
405
  const names = Object.keys(bases);
264
406
  console.log(color.bold("Choose base image"));
265
- names.forEach((name, index) => console.log(` ${color.cyan(String(index + 1))}. ${name} ${color.dim(`(${bases[name]})`)}`));
407
+ names.forEach((name, index) =>
408
+ console.log(
409
+ ` ${color.cyan(String(index + 1))}. ${name} ${color.dim(`(${bases[name]})`)}`,
410
+ ),
411
+ );
266
412
  console.log(` ${color.cyan("4")}. custom image`);
267
413
 
268
414
  const rl = createInterface({ input, output });
@@ -272,7 +418,8 @@ async function chooseBase(fallback) {
272
418
  if (!answer) return fallback;
273
419
  if (bases[answer]) return answer;
274
420
  if (/^[1-3]$/.test(answer)) return names[Number(answer) - 1];
275
- if (answer === "4") return value("custom-base", "Custom base image", defaults["custom-base"]);
421
+ if (answer === "4")
422
+ return value("custom-base", "Custom base image", defaults["custom-base"]);
276
423
  return answer;
277
424
  }
278
425
 
@@ -285,7 +432,9 @@ async function readDefaults(file) {
285
432
  }
286
433
 
287
434
  async function writeDefaults(file, data) {
288
- const clean = Object.fromEntries(Object.entries(data).filter(([, value]) => value));
435
+ const clean = Object.fromEntries(
436
+ Object.entries(data).filter(([, value]) => value),
437
+ );
289
438
  await mkdir(path.dirname(file), { recursive: true });
290
439
  await writeFile(file, `${JSON.stringify(clean, null, 2)}\n`);
291
440
  }
@@ -302,8 +451,10 @@ function parseArgs(argv) {
302
451
  if (!arg.startsWith("--")) continue;
303
452
 
304
453
  const [rawKey, rawValue] = arg.slice(2).split("=", 2);
305
- out[rawKey] = rawValue ?? (argv[i + 1]?.startsWith("--") ? true : argv[i + 1]) ?? true;
306
- if (rawValue === undefined && argv[i + 1] && !argv[i + 1].startsWith("--")) i += 1;
454
+ out[rawKey] =
455
+ rawValue ?? (argv[i + 1]?.startsWith("--") ? true : argv[i + 1]) ?? true;
456
+ if (rawValue === undefined && argv[i + 1] && !argv[i + 1].startsWith("--"))
457
+ i += 1;
307
458
  }
308
459
  return out;
309
460
  }
@@ -312,7 +463,10 @@ function parseCommand(argv) {
312
463
  const known = new Set(["create", "doctor", "help"]);
313
464
  const first = argv[0];
314
465
  if (first && !first.startsWith("--")) {
315
- return { command: known.has(first) ? first : first, args: parseArgs(argv.slice(1)) };
466
+ return {
467
+ command: known.has(first) ? first : first,
468
+ args: parseArgs(argv.slice(1)),
469
+ };
316
470
  }
317
471
  return { command: "create", args: parseArgs(argv) };
318
472
  }
@@ -320,7 +474,9 @@ function parseCommand(argv) {
320
474
  function run(command, commandArgs) {
321
475
  return new Promise((resolve, reject) => {
322
476
  let seenUrl = "";
323
- const child = spawn(command, commandArgs, { stdio: ["inherit", "pipe", "pipe"] });
477
+ const child = spawn(command, commandArgs, {
478
+ stdio: ["inherit", "pipe", "pipe"],
479
+ });
324
480
 
325
481
  child.stdout.on("data", (chunk) => {
326
482
  const text = chunk.toString();
@@ -337,7 +493,8 @@ function run(command, commandArgs) {
337
493
  child.on("error", reject);
338
494
  child.on("close", (code) => {
339
495
  if (code === 0 && seenUrl) resolve(seenUrl);
340
- else if (code === 0) reject(new Error("vercel finished but no deployment url was found"));
496
+ else if (code === 0)
497
+ reject(new Error("vercel finished but no deployment url was found"));
341
498
  else reject(new Error(`vercel exited with code ${code}`));
342
499
  });
343
500
  });
@@ -346,7 +503,9 @@ function run(command, commandArgs) {
346
503
  function runCapture(command, commandArgs) {
347
504
  return new Promise((resolve, reject) => {
348
505
  let text = "";
349
- const child = spawn(command, commandArgs, { stdio: ["ignore", "pipe", "pipe"] });
506
+ const child = spawn(command, commandArgs, {
507
+ stdio: ["ignore", "pipe", "pipe"],
508
+ });
350
509
  child.stdout.on("data", (chunk) => {
351
510
  text += chunk.toString();
352
511
  });
@@ -356,7 +515,8 @@ function runCapture(command, commandArgs) {
356
515
  child.on("error", reject);
357
516
  child.on("close", (code) => {
358
517
  if (code === 0) resolve(text.trim());
359
- else reject(new Error(text.trim() || `${command} exited with code ${code}`));
518
+ else
519
+ reject(new Error(text.trim() || `${command} exited with code ${code}`));
360
520
  });
361
521
  });
362
522
  }
@@ -378,7 +538,9 @@ function findLastUrl(text) {
378
538
 
379
539
  function printHeader() {
380
540
  console.log(color.bold(color.cyan("Vercel VM Factory")));
381
- console.log(color.dim("Build a Container deployment from a tiny Dockerfile.vercel"));
541
+ console.log(
542
+ color.dim("Build a Container deployment from a tiny Dockerfile.vercel"),
543
+ );
382
544
  console.log("");
383
545
  }
384
546
 
@@ -392,10 +554,20 @@ function printSummary(items) {
392
554
 
393
555
  function printOAuthGuide(callbackUrl) {
394
556
  console.log(color.bold("GitHub OAuth"));
395
- console.log(`${color.yellow("!")} Set this callback URL in your GitHub OAuth App before deploying:`);
557
+ console.log(
558
+ `${color.yellow("!")} Set this callback URL in your GitHub OAuth App before deploying:`,
559
+ );
396
560
  console.log(` ${color.bold(callbackUrl)}`);
397
- console.log(color.dim(" GitHub: Settings -> Developer settings -> OAuth Apps -> Authorization callback URL"));
398
- console.log(color.dim(" User ID: open https://api.github.com/users/YOUR_LOGIN and copy the numeric id"));
561
+ console.log(
562
+ color.dim(
563
+ " GitHub: Settings -> Developer settings -> OAuth Apps -> Authorization callback URL",
564
+ ),
565
+ );
566
+ console.log(
567
+ color.dim(
568
+ " User ID: open https://api.github.com/users/YOUR_LOGIN and copy the numeric id",
569
+ ),
570
+ );
399
571
  console.log("");
400
572
  }
401
573
 
@@ -432,6 +604,7 @@ Options:
432
604
  --project NAME Vercel project name
433
605
  --scope SLUG Optional Vercel team/user scope slug
434
606
  --from IMAGE Source image for /app/bin/wsterm
607
+ --auth-mode MODE basic, github, both, or none
435
608
  --auth-user VALUE Username/password auth user
436
609
  --auth-password VAL Username/password auth password
437
610
  --client-id VALUE GitHub OAuth client id
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vercel-vm-factory",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Create Vercel Container deployments for ws-shell from selectable base images.",
5
5
  "license": "MIT",
6
6
  "type": "module",