localpreview 0.2.3 → 0.2.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/command.js CHANGED
@@ -1,26 +1,52 @@
1
1
  import { formatCaptureOrigin, parseCaptureHostPort, parseTarget, validateRequestedSubdomain, } from "@localpreview/protocol";
2
2
  import { Console, Effect, Layer, Option } from "effect";
3
+ import { adminTokenRequiredMessage, formatStatus, formatWarning, formatListTunnelsOutput, renderCleanHelp, renderConnectHelp, renderGlobalHelp, renderListHelp, removedAdminTokenFlagMessage, } from "./cli-ui.js";
3
4
  import { CliConfig, CliConfigLive, LOCAL_CONTROL_PLANE_URL, normalizeControlPlaneUrl } from "./config.js";
4
5
  import { ControlPlaneClient, ControlPlaneClientLive, } from "./control-plane.js";
5
6
  import { CliUsageError, errorMessage } from "./errors.js";
6
7
  import { LocalProxyLive } from "./local-proxy.js";
7
8
  import { RelayClient, RelayClientLive } from "./relay-client.js";
9
+ import { CLI_PACKAGE_VERSION } from "./version.js";
8
10
  export const runCli = (argv) => {
9
11
  const normalized = normalizeCliArgs(argv);
10
- if (isHelpInvocation(normalized)) {
11
- return printHelp();
12
+ const helpKind = resolveHelpKind(normalized);
13
+ if (helpKind !== null) {
14
+ return printHelp(helpKind);
12
15
  }
13
16
  if (isVersionInvocation(normalized)) {
14
- return Console.log("0.0.0");
17
+ return Console.log(CLI_PACKAGE_VERSION);
15
18
  }
16
19
  const parsed = parseCommand(normalized);
17
20
  if (!parsed.ok) {
18
21
  return Effect.fail(new CliUsageError({ message: parsed.message }));
19
22
  }
20
- return runSubcommand(parsed.command);
23
+ return runSubcommand(parsed.command, normalized);
21
24
  };
22
25
  export const normalizeCliArgs = (argv) => argv[0] === "--" ? argv.slice(1) : argv;
23
- const isHelpInvocation = (argv) => argv.length === 0 || argv.includes("--help") || argv.includes("-h");
26
+ export const buildConnectOriginalArgv = (argv, legacy) => (legacy ? argv : argv[0] === "connect" ? argv.slice(1) : argv);
27
+ const hasHelpFlag = (argv) => argv.includes("--help") || argv.includes("-h");
28
+ const resolveHelpKind = (argv) => {
29
+ if (argv.length === 0) {
30
+ return "global";
31
+ }
32
+ if (!hasHelpFlag(argv)) {
33
+ return null;
34
+ }
35
+ const first = argv[0];
36
+ if (first === "clean") {
37
+ return "clean";
38
+ }
39
+ if (first === "list") {
40
+ return "list";
41
+ }
42
+ if (first === "connect") {
43
+ return "connect";
44
+ }
45
+ if (first !== undefined && !first.startsWith("-")) {
46
+ return "connect";
47
+ }
48
+ return "global";
49
+ };
24
50
  const isVersionInvocation = (argv) => argv.includes("--version");
25
51
  const isLegacyConnectInvocation = (argv) => {
26
52
  const first = argv[0];
@@ -29,21 +55,16 @@ const isLegacyConnectInvocation = (argv) => {
29
55
  }
30
56
  return first !== "connect" && !first.startsWith("-");
31
57
  };
32
- const printHelp = () => Console.log([
33
- "Usage: localpreview connect <port|target-url> [--name subdomain] [--capture host:port] [-l|--local] [--control-plane url]",
34
- " localpreview clean <subdomain> [--force] [-l|--local] [--control-plane url] [--admin-token token]",
35
- " localpreview clean --all --force [-l|--local] [--control-plane url] [--admin-token token]",
36
- "",
37
- "Examples:",
38
- " localpreview connect 3000",
39
- " localpreview connect 5173 --capture localhost:4000",
40
- " localpreview connect https://localhost:3000 --name proyecto",
41
- " localpreview connect 3000 -l",
42
- " localpreview connect 3000 --control-plane https://staging.localpreview.dev",
43
- " localpreview clean proyecto --force",
44
- " localpreview clean --all --force",
45
- " localpreview clean demo --control-plane https://staging.localpreview.dev --admin-token ...",
46
- ].join("\n"));
58
+ const printHelp = (kind) => {
59
+ const text = kind === "global"
60
+ ? renderGlobalHelp()
61
+ : kind === "connect"
62
+ ? renderConnectHelp()
63
+ : kind === "clean"
64
+ ? renderCleanHelp()
65
+ : renderListHelp();
66
+ return Console.log(text);
67
+ };
47
68
  const parseCommand = (argv) => {
48
69
  if (argv[0] === "clean") {
49
70
  const parsed = parseCleanArgs(argv.slice(1));
@@ -58,6 +79,19 @@ const parseCommand = (argv) => {
58
79
  ok: true,
59
80
  };
60
81
  }
82
+ if (argv[0] === "list") {
83
+ const parsed = parseListArgs(argv.slice(1));
84
+ if (!parsed.ok) {
85
+ return parsed;
86
+ }
87
+ return {
88
+ command: {
89
+ config: parsed.config,
90
+ kind: "list",
91
+ },
92
+ ok: true,
93
+ };
94
+ }
61
95
  const legacy = isLegacyConnectInvocation(argv);
62
96
  const args = legacy ? argv : argv[0] === "connect" ? argv.slice(1) : argv;
63
97
  const parsed = parseConnectArgs(args);
@@ -152,7 +186,6 @@ const parseConnectArgs = (argv) => {
152
186
  };
153
187
  const parseCleanArgs = (argv) => {
154
188
  const rest = [...argv];
155
- let adminToken;
156
189
  let all = false;
157
190
  let controlPlane;
158
191
  let force = false;
@@ -165,13 +198,10 @@ const parseCleanArgs = (argv) => {
165
198
  continue;
166
199
  }
167
200
  if (arg === "--admin-token") {
168
- const value = readRequiredOptionValue(rest, index, "--admin-token");
169
- if (!value.ok) {
170
- return value;
171
- }
172
- adminToken = value.value;
173
- index += 1;
174
- continue;
201
+ return {
202
+ message: removedAdminTokenFlagMessage(),
203
+ ok: false,
204
+ };
175
205
  }
176
206
  if (arg === "--all") {
177
207
  all = true;
@@ -222,14 +252,13 @@ const parseCleanArgs = (argv) => {
222
252
  }
223
253
  if (!all && subdomain === undefined) {
224
254
  return {
225
- message: "Usage: localpreview clean <subdomain> [--force] [-l|--local] [--control-plane url] [--admin-token token]\n localpreview clean --all --force [-l|--local] [--control-plane url] [--admin-token token]",
255
+ message: "Usage: localpreview clean <subdomain> [--force] [-l|--local] [--control-plane url]\n localpreview clean --all --force [-l|--local] [--control-plane url]",
226
256
  ok: false,
227
257
  };
228
258
  }
229
259
  return {
230
260
  config: {
231
261
  all,
232
- adminToken: toOption(adminToken),
233
262
  controlPlane: toOption(controlPlane),
234
263
  force,
235
264
  subdomain: toOption(subdomain),
@@ -237,6 +266,119 @@ const parseCleanArgs = (argv) => {
237
266
  ok: true,
238
267
  };
239
268
  };
269
+ const parseListArgs = (argv) => {
270
+ const rest = [...argv];
271
+ let controlPlane;
272
+ let limit = 100;
273
+ let skip = 0;
274
+ let usedLocalControlPlane = false;
275
+ let usedControlPlaneFlag = false;
276
+ for (let index = 0; index < rest.length; index += 1) {
277
+ const arg = rest[index];
278
+ if (arg === undefined) {
279
+ continue;
280
+ }
281
+ if (arg === "--admin-token") {
282
+ return {
283
+ message: removedAdminTokenFlagMessage(),
284
+ ok: false,
285
+ };
286
+ }
287
+ if (arg === "--limit") {
288
+ const value = readRequiredOptionValue(rest, index, "--limit");
289
+ if (!value.ok) {
290
+ return value;
291
+ }
292
+ const parsedLimit = parsePositiveIntegerOption(value.value, "--limit");
293
+ if (!parsedLimit.ok) {
294
+ return parsedLimit;
295
+ }
296
+ limit = parsedLimit.value;
297
+ index += 1;
298
+ continue;
299
+ }
300
+ if (arg === "--skip") {
301
+ const value = readRequiredOptionValue(rest, index, "--skip");
302
+ if (!value.ok) {
303
+ return value;
304
+ }
305
+ const parsedSkip = parseNonNegativeIntegerOption(value.value, "--skip");
306
+ if (!parsedSkip.ok) {
307
+ return parsedSkip;
308
+ }
309
+ skip = parsedSkip.value;
310
+ index += 1;
311
+ continue;
312
+ }
313
+ const controlPlaneFlag = readControlPlaneFlag(rest, index, arg, {
314
+ usedControlPlaneFlag,
315
+ usedLocalControlPlane,
316
+ });
317
+ if (!controlPlaneFlag.ok) {
318
+ return controlPlaneFlag;
319
+ }
320
+ if (controlPlaneFlag.handled) {
321
+ if (controlPlaneFlag.usedLocalControlPlane) {
322
+ usedLocalControlPlane = true;
323
+ }
324
+ if (controlPlaneFlag.usedControlPlaneFlag) {
325
+ usedControlPlaneFlag = true;
326
+ }
327
+ controlPlane = controlPlaneFlag.controlPlane ?? controlPlane;
328
+ index = controlPlaneFlag.nextIndex;
329
+ continue;
330
+ }
331
+ if (arg !== undefined && !arg.startsWith("-")) {
332
+ return {
333
+ message: `Unexpected argument: ${arg}`,
334
+ ok: false,
335
+ };
336
+ }
337
+ return {
338
+ message: `Unknown option: ${arg}`,
339
+ ok: false,
340
+ };
341
+ }
342
+ return {
343
+ config: {
344
+ controlPlane: toOption(controlPlane),
345
+ limit,
346
+ skip,
347
+ },
348
+ ok: true,
349
+ };
350
+ };
351
+ const parsePositiveIntegerOption = (value, option) => {
352
+ if (!/^\d+$/.test(value)) {
353
+ return {
354
+ message: `${option} must be a positive integer.`,
355
+ ok: false,
356
+ };
357
+ }
358
+ const parsed = Number.parseInt(value, 10);
359
+ if (parsed <= 0) {
360
+ return {
361
+ message: `${option} must be a positive integer.`,
362
+ ok: false,
363
+ };
364
+ }
365
+ return {
366
+ ok: true,
367
+ value: parsed,
368
+ };
369
+ };
370
+ const parseNonNegativeIntegerOption = (value, option) => {
371
+ if (!/^\d+$/.test(value)) {
372
+ return {
373
+ message: `${option} must be a non-negative integer.`,
374
+ ok: false,
375
+ };
376
+ }
377
+ return {
378
+ ok: true,
379
+ value: Number.parseInt(value, 10),
380
+ };
381
+ };
240
382
  const readControlPlaneFlag = (argv, index, arg, state) => {
241
383
  if (arg === "-l" || arg === "--local") {
242
384
  if (state.usedControlPlaneFlag) {
@@ -298,11 +440,13 @@ const readRequiredOptionValue = (argv, index, option) => {
298
440
  };
299
441
  };
300
442
  const toOption = (value) => value === undefined ? Option.none() : Option.some(value);
301
- const runSubcommand = (command) => {
443
+ const runSubcommand = (command, originalArgv) => {
302
444
  const controlPlane = Option.getOrUndefined(command.config.controlPlane);
303
445
  const effect = command.kind === "connect"
304
- ? runConnect(command.config, command.legacy)
305
- : runClean(command.config);
446
+ ? runConnect(command.config, command.legacy, originalArgv)
447
+ : command.kind === "clean"
448
+ ? runClean(command.config)
449
+ : runList(command.config);
306
450
  return effect.pipe(Effect.provide(makeCommandLayer(makeConfigInput(controlPlane))));
307
451
  };
308
452
  const makeConfigInput = (controlPlaneUrl) => controlPlaneUrl === undefined ? {} : { controlPlaneUrl };
@@ -312,9 +456,9 @@ const makeCommandLayer = (input) => {
312
456
  const RelayLive = RelayClientLive.pipe(Layer.provide(Layer.mergeAll(ConfigLive, ProxyLive)));
313
457
  return Layer.mergeAll(ConfigLive, ControlPlaneClientLive, ProxyLive, RelayLive);
314
458
  };
315
- const runConnect = (config, legacy) => Effect.scoped(Effect.gen(function* () {
459
+ const runConnect = (config, legacy, originalArgv) => Effect.scoped(Effect.gen(function* () {
316
460
  if (legacy) {
317
- yield* Console.error("Warning: `localpreview <target>` is deprecated. Use `localpreview connect <target>`.");
461
+ yield* Console.error(formatWarning("`localpreview <target>` is deprecated. Use `localpreview connect <target>`."));
318
462
  }
319
463
  const target = parseTarget(config.target);
320
464
  if (!target.ok) {
@@ -325,13 +469,16 @@ const runConnect = (config, legacy) => Effect.scoped(Effect.gen(function* () {
325
469
  const relay = yield* RelayClient;
326
470
  const cliConfig = yield* CliConfig;
327
471
  const tunnel = yield* Effect.acquireRelease(controlPlane.createTunnel(cliConfig.controlPlaneUrl, requestedSubdomain === undefined ? {} : { requestedSubdomain }), (tunnel) => closeTunnelBestEffort(controlPlane, cliConfig.controlPlaneUrl, tunnel));
328
- yield* Console.log(`Tunnel ready: ${tunnel.publicUrl}`);
329
- yield* Console.log(`Forwarding to ${target.target.protocol}://${target.target.hostname}:${target.target.port}`);
472
+ yield* Console.log(formatStatus(`Tunnel ready: ${tunnel.publicUrl}`));
473
+ yield* Console.log(formatStatus(`Forwarding to ${target.target.protocol}://${target.target.hostname}:${target.target.port}`));
330
474
  if (config.captures.length > 0) {
331
475
  const origins = config.captures.map((capture) => formatCaptureOrigin(capture)).join(", ");
332
- yield* Console.error(`Warning: captured local backends (${origins}) are exposed through this preview URL. Anyone with the link can reach them.`);
476
+ yield* Console.error(formatWarning(`Captured local backends (${origins}) are exposed through this preview URL. Anyone with the link can reach them.`));
333
477
  }
334
- yield* relay.connectAndServe(tunnel, target.target, config.captures);
478
+ yield* relay.connectAndServe(tunnel, target.target, config.captures, {
479
+ legacy,
480
+ originalArgv: buildConnectOriginalArgv(originalArgv, legacy),
481
+ });
335
482
  }));
336
483
  const validateRequestedName = (value) => Option.match(value, {
337
484
  onNone: () => Effect.succeed(undefined),
@@ -354,10 +501,11 @@ const runClean = (config) => Effect.gen(function* () {
354
501
  }
355
502
  const controlPlane = yield* ControlPlaneClient;
356
503
  const cliConfig = yield* CliConfig;
357
- const adminToken = Option.getOrUndefined(config.adminToken) ?? cliConfig.cleanupAdminToken;
504
+ const adminToken = cliConfig.adminToken;
358
505
  if (adminToken === undefined || adminToken.length === 0) {
506
+ yield* Console.log(renderGlobalHelp());
359
507
  return yield* Effect.fail(new CliUsageError({
360
- message: "Subdomain cleanup requires an admin token. Set LOCALPREVIEW_CLEANUP_TOKEN or pass --admin-token.",
508
+ message: adminTokenRequiredMessage(),
361
509
  }));
362
510
  }
363
511
  if (config.all) {
@@ -380,7 +528,7 @@ const runClean = (config) => Effect.gen(function* () {
380
528
  }
381
529
  if (subdomain === undefined) {
382
530
  return yield* Effect.fail(new CliUsageError({
383
- message: "Usage: localpreview clean <subdomain> [--force] [-l|--local] [--control-plane url] [--admin-token token]",
531
+ message: "Usage: localpreview clean <subdomain> [--force] [-l|--local] [--control-plane url]",
384
532
  }));
385
533
  }
386
534
  const validation = validateRequestedSubdomain(subdomain);
@@ -403,6 +551,23 @@ const runClean = (config) => Effect.gen(function* () {
403
551
  : ` Tunnel ${result.tunnelId} was ${result.relayStopped ? "stopped" : "removed from the store"}.`;
404
552
  yield* Console.log(`Subdomain "${result.subdomain}" cleaned.${suffix}`);
405
553
  });
554
+ const runList = (config) => Effect.gen(function* () {
555
+ const controlPlane = yield* ControlPlaneClient;
556
+ const cliConfig = yield* CliConfig;
557
+ const adminToken = cliConfig.adminToken;
558
+ if (adminToken === undefined || adminToken.length === 0) {
559
+ yield* Console.log(renderGlobalHelp());
560
+ return yield* Effect.fail(new CliUsageError({
561
+ message: adminTokenRequiredMessage(),
562
+ }));
563
+ }
564
+ const result = yield* controlPlane.listTunnels(cliConfig.controlPlaneUrl, {
565
+ adminToken,
566
+ limit: config.limit,
567
+ skip: config.skip,
568
+ });
569
+ yield* Console.log(formatListTunnelsOutput(result));
570
+ });
406
571
  const closeTunnelBestEffort = (controlPlane, controlPlaneUrl, tunnel) => controlPlane
407
572
  .closeTunnel(controlPlaneUrl, tunnel)
408
- .pipe(Effect.catch((error) => Console.error(`Warning: ${errorMessage(error)}`)));
573
+ .pipe(Effect.catch((error) => Console.error(formatWarning(errorMessage(error)))));
package/dist/config.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Context, Effect, Layer } from "effect";
2
2
  export type CliConfigShape = {
3
- readonly cleanupAdminToken: string | undefined;
3
+ readonly adminToken: string | undefined;
4
4
  readonly controlPlaneUrl: string;
5
5
  readonly maxInFlightRequests: number;
6
6
  readonly relayConnectTimeoutMs: number;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAEhD,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/C,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC;IACvC,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC;IACvC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,sBAAsB,EAAE,MAAM,CAAC;IACxC,QAAQ,CAAC,sBAAsB,EAAE,MAAM,CAAC;CACzC,CAAC;;AAEF,qBAAa,SAAU,SAAQ,cAAyD;CAAG;AAE3F,eAAO,MAAM,wBAAwB,6BAA6B,CAAC;AACnE,eAAO,MAAM,uBAAuB,0BAA0B,CAAC;AAE/D,MAAM,MAAM,8BAA8B,GACtC;IACE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAClB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB,GACD;IACE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;CACpB,CAAC;AAEN,gFAAgF;AAChF,eAAO,MAAM,wBAAwB,GAAI,OAAO,MAAM,KAAG,8BA4DxD,CAAC;AA2BF,eAAO,MAAM,aAAa,GAAI,OAAO;IACnC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CAClC,KAAG,cAoBH,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,OAAO;IACnC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CAClC,yCAAmD,CAAC;AAWrD,eAAO,MAAM,aAAa,GAAI,OAAO;IACnC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CAClC,KAAG,MAAM,CAAC,MAAM,CAAC,cAAc,CAA4C,CAAC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAEhD,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC;IACvC,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC;IACvC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,sBAAsB,EAAE,MAAM,CAAC;IACxC,QAAQ,CAAC,sBAAsB,EAAE,MAAM,CAAC;CACzC,CAAC;;AAEF,qBAAa,SAAU,SAAQ,cAAyD;CAAG;AAE3F,eAAO,MAAM,wBAAwB,6BAA6B,CAAC;AACnE,eAAO,MAAM,uBAAuB,0BAA0B,CAAC;AAE/D,MAAM,MAAM,8BAA8B,GACtC;IACE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAClB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB,GACD;IACE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;CACpB,CAAC;AAEN,gFAAgF;AAChF,eAAO,MAAM,wBAAwB,GAAI,OAAO,MAAM,KAAG,8BA4DxD,CAAC;AA2BF,eAAO,MAAM,aAAa,GAAI,OAAO;IACnC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CAClC,KAAG,cAkBH,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,OAAO;IACnC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CAClC,yCAAmD,CAAC;AAWrD,eAAO,MAAM,aAAa,GAAI,OAAO;IACnC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CAClC,KAAG,MAAM,CAAC,MAAM,CAAC,cAAc,CAA4C,CAAC"}
package/dist/config.js CHANGED
@@ -78,10 +78,9 @@ const isIpv4LoopbackAddress = (hostname) => {
78
78
  export const makeCliConfig = (input) => {
79
79
  const env = input.env ?? process.env;
80
80
  const controlPlaneUrl = input.controlPlaneUrl ?? PUBLIC_CONTROL_PLANE_URL;
81
- const cleanupAdminToken = env.LOCALPREVIEW_CLEANUP_TOKEN ??
82
- (controlPlaneUrl === LOCAL_CONTROL_PLANE_URL ? "local-dev-cleanup-token" : undefined);
81
+ const adminToken = env.LOCALPREVIEW_ADMIN_TOKEN;
83
82
  return {
84
- cleanupAdminToken,
83
+ adminToken,
85
84
  controlPlaneUrl,
86
85
  maxInFlightRequests: readNumber(env.LOCALPREVIEW_MAX_IN_FLIGHT_REQUESTS, 100),
87
86
  relayConnectTimeoutMs: readNumber(env.LOCALPREVIEW_RELAY_CONNECT_TIMEOUT_MS, 10_000),
@@ -1,4 +1,4 @@
1
- import { type CreateTunnelResponse } from "@localpreview/protocol";
1
+ import { type CreateTunnelResponse, type ListTunnelsResponse } from "@localpreview/protocol";
2
2
  import { Context, Effect, Layer } from "effect";
3
3
  import { ControlPlaneError } from "./errors.js";
4
4
  export type ControlPlaneClientShape = {
@@ -15,6 +15,11 @@ export type ControlPlaneClientShape = {
15
15
  readonly createTunnel: (controlPlaneUrl: string, body: {
16
16
  readonly requestedSubdomain?: string;
17
17
  }) => Effect.Effect<CreateTunnelResponse, ControlPlaneError>;
18
+ readonly listTunnels: (controlPlaneUrl: string, input: {
19
+ readonly adminToken: string;
20
+ readonly limit: number;
21
+ readonly skip: number;
22
+ }) => Effect.Effect<ListTunnelsResponse, ControlPlaneError>;
18
23
  };
19
24
  export type CleanSubdomainResponse = {
20
25
  readonly cleaned: boolean;
@@ -55,5 +60,10 @@ export declare const cleanAllSubdomains: (controlPlaneUrl: string, input: {
55
60
  readonly adminToken: string;
56
61
  readonly force: boolean;
57
62
  }) => Promise<CleanAllSubdomainsResponse>;
63
+ export declare const listTunnels: (controlPlaneUrl: string, input: {
64
+ readonly adminToken: string;
65
+ readonly limit: number;
66
+ readonly skip: number;
67
+ }) => Promise<ListTunnelsResponse>;
58
68
  export {};
59
69
  //# sourceMappingURL=control-plane.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"control-plane.d.ts","sourceRoot":"","sources":["../src/control-plane.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"control-plane.d.ts","sourceRoot":"","sources":["../src/control-plane.ts"],"names":[],"mappings":"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"}
@@ -11,11 +11,13 @@ export const ControlPlaneClientLive = Layer.succeed(ControlPlaneClient)({
11
11
  while: (error) => error.retryable === true,
12
12
  })),
13
13
  createTunnel: (controlPlaneUrl, body) => createTunnelEffect(controlPlaneUrl, body),
14
+ listTunnels: (controlPlaneUrl, input) => listTunnelsEffect(controlPlaneUrl, input),
14
15
  });
15
16
  export const createTunnel = (controlPlaneUrl, body) => Effect.runPromise(createTunnelEffect(controlPlaneUrl, body));
16
17
  export const closeTunnel = (controlPlaneUrl, tunnel) => Effect.runPromise(closeTunnelEffect(controlPlaneUrl, tunnel));
17
18
  export const cleanSubdomain = (controlPlaneUrl, input) => Effect.runPromise(cleanSubdomainEffect(controlPlaneUrl, input));
18
19
  export const cleanAllSubdomains = (controlPlaneUrl, input) => Effect.runPromise(cleanAllSubdomainsEffect(controlPlaneUrl, input));
20
+ export const listTunnels = (controlPlaneUrl, input) => Effect.runPromise(listTunnelsEffect(controlPlaneUrl, input));
19
21
  const createTunnelEffect = (controlPlaneUrl, body) => Effect.promise(async () => {
20
22
  let response;
21
23
  try {
@@ -31,7 +33,7 @@ const createTunnelEffect = (controlPlaneUrl, body) => Effect.promise(async () =>
31
33
  throw new ControlPlaneError({
32
34
  message: [
33
35
  `Could not reach localpreview control-plane at ${controlPlaneUrl}.`,
34
- 'Use "-l" or "--local" to target a local control-plane at http://localhost:3000.',
36
+ 'Use "-l" or "--local" as shorthand for --control-plane http://localhost:3000.',
35
37
  ].join("\n"),
36
38
  retryable: false,
37
39
  });
@@ -136,6 +138,59 @@ const cleanAllSubdomainsEffect = (controlPlaneUrl, input) => Effect.promise(asyn
136
138
  }
137
139
  return json;
138
140
  });
141
+ const listTunnelsEffect = (controlPlaneUrl, input) => Effect.promise(async () => {
142
+ let response;
143
+ const url = new URL("/api/tunnels", controlPlaneUrl);
144
+ url.searchParams.set("limit", String(input.limit));
145
+ url.searchParams.set("skip", String(input.skip));
146
+ try {
147
+ response = await fetch(url, {
148
+ headers: {
149
+ [LOCALPREVIEW_ADMIN_TOKEN_HEADER]: input.adminToken,
150
+ },
151
+ method: "GET",
152
+ });
153
+ }
154
+ catch {
155
+ throw new ControlPlaneError({
156
+ message: `Could not reach localpreview control-plane at ${controlPlaneUrl}.`,
157
+ retryable: false,
158
+ });
159
+ }
160
+ const json = await readJson(response, "Tunnel listing");
161
+ if (!response.ok) {
162
+ if (response.status === 404) {
163
+ throw new ControlPlaneError({
164
+ message: [
165
+ "The control-plane does not support tunnel listing yet.",
166
+ "Deploy the updated control-plane, then rerun `localpreview list`.",
167
+ ].join(" "),
168
+ });
169
+ }
170
+ throw new ControlPlaneError({
171
+ message: json.error?.message ?? `Tunnel listing failed with HTTP ${response.status}.`,
172
+ });
173
+ }
174
+ return validateListTunnelsResponse(json);
175
+ });
176
+ const validateListTunnelsResponse = (json) => {
177
+ if (typeof json.total !== "number" ||
178
+ typeof json.skip !== "number" ||
179
+ typeof json.limit !== "number" ||
180
+ !Array.isArray(json.items) ||
181
+ json.counts === undefined ||
182
+ typeof json.counts.tracked !== "number" ||
183
+ typeof json.counts["redis-orphan"] !== "number" ||
184
+ typeof json.counts["sandbox-orphan"] !== "number") {
185
+ throw new ControlPlaneError({
186
+ message: [
187
+ "Tunnel listing returned an invalid response.",
188
+ "This usually means the control-plane is running an older LocalPreview protocol.",
189
+ ].join(" "),
190
+ });
191
+ }
192
+ return json;
193
+ };
139
194
  const requiredCreateTunnelStringFields = [
140
195
  "tunnelId",
141
196
  "subdomain",
package/dist/errors.d.ts CHANGED
@@ -39,6 +39,10 @@ declare const LocalRequestError_base: new <A extends Record<string, any> = {}>(a
39
39
  readonly _tag: "LocalRequestError";
40
40
  } & Readonly<A>;
41
41
  export declare class LocalRequestError extends LocalRequestError_base<{
42
+ readonly invalidCapture?: {
43
+ readonly hostname: string;
44
+ readonly port: number;
45
+ };
42
46
  readonly message: string;
43
47
  readonly requestId: string;
44
48
  }> {
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":";;;AAEA,qBAAa,aAAc,SAAQ,mBAAkC;IACnE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,CAAC;CAAG;;;;AAEL,qBAAa,cAAe,SAAQ,oBAAmC;IACrE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,CAAC;CAAG;;;;AAEL,qBAAa,iBAAkB,SAAQ,uBAAsC;IAC3E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;CAC9B,CAAC;CAAG;;;;AAEL,qBAAa,oBAAqB,SAAQ,0BAAyC;IACjF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;CAC9B,CAAC;CAAG;;;;AAEL,qBAAa,kBAAmB,SAAQ,wBAAuC;IAC7E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,CAAC;CAAG;;;;AAEL,qBAAa,iBAAkB,SAAQ,uBAAsC;IAC3E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B,CAAC;CAAG;;;;AAEL,qBAAa,iBAAkB,SAAQ,uBAAsC;IAC3E,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B,CAAC;CAAG;AAEL,MAAM,MAAM,eAAe,GACvB,cAAc,GACd,iBAAiB,GACjB,oBAAoB,GACpB,kBAAkB,GAClB,iBAAiB,GACjB,iBAAiB,CAAC;AAEtB,eAAO,MAAM,YAAY,GAAI,OAAO,OAAO,KAAG,MAM7C,CAAC"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":";;;AAEA,qBAAa,aAAc,SAAQ,mBAAkC;IACnE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,CAAC;CAAG;;;;AAEL,qBAAa,cAAe,SAAQ,oBAAmC;IACrE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,CAAC;CAAG;;;;AAEL,qBAAa,iBAAkB,SAAQ,uBAAsC;IAC3E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;CAC9B,CAAC;CAAG;;;;AAEL,qBAAa,oBAAqB,SAAQ,0BAAyC;IACjF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;CAC9B,CAAC;CAAG;;;;AAEL,qBAAa,kBAAmB,SAAQ,wBAAuC;IAC7E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,CAAC;CAAG;;;;AAEL,qBAAa,iBAAkB,SAAQ,uBAAsC;IAC3E,QAAQ,CAAC,cAAc,CAAC,EAAE;QACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;KACvB,CAAC;IACF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B,CAAC;CAAG;;;;AAEL,qBAAa,iBAAkB,SAAQ,uBAAsC;IAC3E,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B,CAAC;CAAG;AAEL,MAAM,MAAM,eAAe,GACvB,cAAc,GACd,iBAAiB,GACjB,oBAAoB,GACpB,kBAAkB,GAClB,iBAAiB,GACjB,iBAAiB,CAAC;AAEtB,eAAO,MAAM,YAAY,GAAI,OAAO,OAAO,KAAG,MAM7C,CAAC"}
package/dist/index.js CHANGED
@@ -2,9 +2,10 @@
2
2
  import * as NodeRuntime from "@effect/platform-node/NodeRuntime";
3
3
  import { Console, Effect } from "effect";
4
4
  import { runCli } from "./command.js";
5
+ import { formatCliError } from "./cli-ui.js";
5
6
  import { errorMessage } from "./errors.js";
6
7
  const argv = process.argv.slice(2);
7
- const program = runCli(argv).pipe(Effect.catch((error) => Console.error(errorMessage(error)).pipe(Effect.andThen(Effect.sync(() => {
8
+ const program = runCli(argv).pipe(Effect.catch((error) => Console.error(formatCliError(errorMessage(error))).pipe(Effect.andThen(Effect.sync(() => {
8
9
  process.exitCode =
9
10
  typeof error === "object" &&
10
11
  error !== null &&
@@ -5,6 +5,9 @@ import { CliConfig } from "./config.js";
5
5
  import { RelayProtocolError } from "./errors.js";
6
6
  export type ProxySession = {
7
7
  readonly captures: ReadonlyArray<CaptureTarget>;
8
+ readonly legacy: boolean;
9
+ readonly originalArgv: ReadonlyArray<string>;
10
+ readonly reportedMissingCaptures: Set<string>;
8
11
  readonly target: TunnelTarget;
9
12
  };
10
13
  export type LocalProxyShape = {
@@ -1 +1 @@
1
- {"version":3,"file":"local-proxy.d.ts","sourceRoot":"","sources":["../src/local-proxy.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,aAAa,EAClB,KAAK,WAAW,EAEhB,KAAK,YAAY,EAClB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAW,OAAO,EAAE,MAAM,EAAS,KAAK,EAAO,MAAM,QAAQ,CAAC;AACrE,OAAO,KAAK,SAAS,MAAM,IAAI,CAAC;AAahC,OAAO,EAAE,SAAS,EAAuB,MAAM,aAAa,CAAC;AAC7D,OAAO,EAEL,kBAAkB,EAGnB,MAAM,aAAa,CAAC;AAWrB,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,aAAa,CAAC,CAAC;IAChD,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,aAAa,EAAE,CACtB,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,YAAY,EACrB,KAAK,EAAE,MAAM,KACV,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,kBAAkB,CAAC,CAAC;CAC9C,CAAC;;AAEF,qBAAa,UAAW,SAAQ,eAA4D;CAAG;AAE/F,eAAO,MAAM,cAAc,2CAU1B,CAAC;AA4UF,eAAO,MAAM,uBAAuB,GAAI,QAAQ,MAAM,EAAE,YAAY,MAAM,KAAG,MAqC5E,CAAC;AA0BF,eAAO,MAAM,0BAA0B,GAAI,SAAS,OAAO,KAAG,WAqB7D,CAAC"}
1
+ {"version":3,"file":"local-proxy.d.ts","sourceRoot":"","sources":["../src/local-proxy.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,aAAa,EAClB,KAAK,WAAW,EAEhB,KAAK,YAAY,EAClB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAW,OAAO,EAAE,MAAM,EAAS,KAAK,EAAO,MAAM,QAAQ,CAAC;AACrE,OAAO,KAAK,SAAS,MAAM,IAAI,CAAC;AA8BhC,OAAO,EAAE,SAAS,EAAuB,MAAM,aAAa,CAAC;AAC7D,OAAO,EAEL,kBAAkB,EAGnB,MAAM,aAAa,CAAC;AAWrB,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,aAAa,CAAC,CAAC;IAChD,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,YAAY,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC7C,QAAQ,CAAC,uBAAuB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC9C,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,aAAa,EAAE,CACtB,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,YAAY,EACrB,KAAK,EAAE,MAAM,KACV,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,kBAAkB,CAAC,CAAC;CAC9C,CAAC;;AAEF,qBAAa,UAAW,SAAQ,eAA4D;CAAG;AAE/F,eAAO,MAAM,cAAc,2CAU1B,CAAC;AA6aF,eAAO,MAAM,uBAAuB,GAAI,QAAQ,MAAM,EAAE,YAAY,MAAM,KAAG,MAqC5E,CAAC;AA0CF,eAAO,MAAM,0BAA0B,GAAI,SAAS,OAAO,KAAG,WAqB7D,CAAC"}