@project-ajax/sdk 0.0.55 → 0.0.57

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 (48) hide show
  1. package/dist/cli/api/client.d.ts +2 -2
  2. package/dist/cli/api/client.d.ts.map +1 -1
  3. package/dist/cli/commands/auth.impl.d.ts.map +1 -1
  4. package/dist/cli/commands/auth.impl.js +13 -9
  5. package/dist/cli/commands/bundle.impl.js +2 -2
  6. package/dist/cli/commands/capabilities.impl.js +4 -4
  7. package/dist/cli/commands/connect.impl.js +13 -13
  8. package/dist/cli/commands/deploy.impl.d.ts.map +1 -1
  9. package/dist/cli/commands/deploy.impl.js +10 -16
  10. package/dist/cli/commands/env.impl.d.ts.map +1 -1
  11. package/dist/cli/commands/env.impl.js +7 -10
  12. package/dist/cli/commands/exec.impl.d.ts.map +1 -1
  13. package/dist/cli/commands/exec.impl.js +14 -16
  14. package/dist/cli/commands/runs.impl.js +7 -7
  15. package/dist/cli/commands/secrets.impl.js +12 -12
  16. package/dist/cli/commands/utils/testing.d.ts +2 -11
  17. package/dist/cli/commands/utils/testing.d.ts.map +1 -1
  18. package/dist/cli/commands/utils/testing.js +4 -4
  19. package/dist/cli/config.d.ts.map +1 -1
  20. package/dist/cli/config.js +3 -6
  21. package/dist/cli/context.d.ts +2 -2
  22. package/dist/cli/context.d.ts.map +1 -1
  23. package/dist/cli/context.js +3 -3
  24. package/dist/cli/deploy.js +11 -11
  25. package/dist/cli/handler.js +2 -2
  26. package/dist/cli/{writer.d.ts → io.d.ts} +10 -3
  27. package/dist/cli/io.d.ts.map +1 -0
  28. package/dist/cli/{writer.js → io.js} +24 -2
  29. package/package.json +3 -3
  30. package/src/cli/api/client.ts +3 -3
  31. package/src/cli/commands/auth.impl.test.ts +4 -2
  32. package/src/cli/commands/auth.impl.ts +17 -9
  33. package/src/cli/commands/bundle.impl.ts +2 -2
  34. package/src/cli/commands/capabilities.impl.ts +4 -4
  35. package/src/cli/commands/connect.impl.ts +13 -13
  36. package/src/cli/commands/deploy.impl.test.ts +9 -14
  37. package/src/cli/commands/deploy.impl.ts +11 -16
  38. package/src/cli/commands/env.impl.ts +7 -11
  39. package/src/cli/commands/exec.impl.ts +14 -16
  40. package/src/cli/commands/runs.impl.ts +7 -7
  41. package/src/cli/commands/secrets.impl.ts +12 -12
  42. package/src/cli/commands/utils/testing.ts +5 -4
  43. package/src/cli/config.ts +2 -5
  44. package/src/cli/context.ts +3 -3
  45. package/src/cli/deploy.ts +11 -11
  46. package/src/cli/handler.ts +2 -2
  47. package/src/cli/{writer.ts → io.ts} +34 -2
  48. package/dist/cli/writer.d.ts.map +0 -1
@@ -6,13 +6,13 @@ import { Result } from "./api/result.js";
6
6
  async function deployWorker(context, options) {
7
7
  const { workerPath } = options;
8
8
  const absPath = path.resolve(process.cwd(), workerPath);
9
- context.writer.debug(`Deploying worker from: ${absPath}`);
9
+ context.io.debug(`Deploying worker from: ${absPath}`);
10
10
  const client = context.apiClient;
11
11
  let uploadUrl;
12
12
  let uploadFields;
13
13
  let workerId;
14
14
  if ("workerId" in options) {
15
- context.writer.writeErr(`Updating worker...`);
15
+ context.io.writeErr(`Updating worker...`);
16
16
  workerId = options.workerId;
17
17
  const updateResult = await client.updateWorkerBundle(workerId);
18
18
  if (Result.isSuccess(updateResult)) {
@@ -20,13 +20,13 @@ async function deployWorker(context, options) {
20
20
  uploadUrl = res.url;
21
21
  uploadFields = res.fields;
22
22
  } else {
23
- context.writer.writeErr(
23
+ context.io.writeErr(
24
24
  "Failed to generate pre-signed upload URL for worker bundle:"
25
25
  );
26
26
  throw new Error(updateResult.error.message);
27
27
  }
28
28
  } else {
29
- context.writer.writeErr(`Creating worker...`);
29
+ context.io.writeErr(`Creating worker...`);
30
30
  const createResult = await client.createWorker(options.name);
31
31
  if (Result.isSuccess(createResult)) {
32
32
  const res = Result.unwrap(createResult);
@@ -38,14 +38,14 @@ async function deployWorker(context, options) {
38
38
  throw new Error(createResult.error.message);
39
39
  }
40
40
  }
41
- context.writer.debug(`Generated upload URL: ${uploadUrl}`);
42
- context.writer.writeErr("Building worker bundle...");
41
+ context.io.debug(`Generated upload URL: ${uploadUrl}`);
42
+ context.io.writeErr("Building worker bundle...");
43
43
  const archivePath = await buildWorkerBundle(context, absPath);
44
- context.writer.debug(`Created bundle at: ${archivePath}`);
45
- context.writer.writeErr("Uploading bundle...");
44
+ context.io.debug(`Created bundle at: ${archivePath}`);
45
+ context.io.writeErr("Uploading bundle...");
46
46
  await uploadBundle(uploadUrl, uploadFields, archivePath);
47
- context.writer.debug("Upload complete");
48
- context.writer.writeErr("Fetching and saving worker capabilities...");
47
+ context.io.debug("Upload complete");
48
+ context.io.writeErr("Fetching and saving worker capabilities...");
49
49
  const capabilitiesResult = await client.fetchAndSaveCapabilities(workerId);
50
50
  if (Result.isSuccess(capabilitiesResult)) {
51
51
  return Result.success({ workerId });
@@ -66,7 +66,7 @@ async function buildWorkerBundle(context, workerPath) {
66
66
  outdir,
67
67
  platform: "node"
68
68
  });
69
- context.writer.debug(`Built bundle to: ${outdir}`);
69
+ context.io.debug(`Built bundle to: ${outdir}`);
70
70
  const archiveDir = fs.mkdtempSync("/tmp/workers-archive-");
71
71
  const archivePath = path.join(archiveDir, "archive.tar.gz");
72
72
  childProcess.execSync(`tar -czf ${archivePath} -C ${outdir} .`);
@@ -8,7 +8,7 @@ function buildHandler(handler) {
8
8
  processEnv: process.env,
9
9
  flags
10
10
  });
11
- this.writer.debugEnabled = flags.debug;
11
+ this.io.debugEnabled = flags.debug;
12
12
  await handler.call({ ...this, config }, flags, ...args);
13
13
  };
14
14
  }
@@ -21,7 +21,7 @@ function buildAuthedHandler(handler) {
21
21
  environment,
22
22
  baseUrl: this.config.baseUrl,
23
23
  cellId,
24
- writer: this.writer
24
+ writer: this.io
25
25
  });
26
26
  return handler.call({ ...this, apiClient: client }, flags, ...args);
27
27
  });
@@ -1,3 +1,4 @@
1
+ import * as prompts from "@inquirer/prompts";
1
2
  import { type TableCell } from "@visulima/tabular";
2
3
  export interface WriterOptions {
3
4
  debugEnabled: boolean;
@@ -7,10 +8,13 @@ export interface TableOptions {
7
8
  rows: TableCell[][];
8
9
  plain: boolean;
9
10
  }
11
+ type WithSafety<T> = T & {
12
+ noTTY: string;
13
+ };
10
14
  /**
11
- * A writer writes messages to standard out and standard error.
15
+ * IO manages safe, consistent, input and output patterns for the CLI.
12
16
  */
13
- export declare class Writer {
17
+ export declare class IO {
14
18
  #private;
15
19
  debugEnabled: boolean;
16
20
  constructor(options: WriterOptions);
@@ -44,5 +48,8 @@ export declare class Writer {
44
48
  * @param tableConfig The table configuration.
45
49
  */
46
50
  writeTableErr(tableConfig: TableOptions): void;
51
+ confirm(config: WithSafety<Parameters<typeof prompts.confirm>[0]>): Promise<boolean | void>;
52
+ input(config: WithSafety<Parameters<typeof prompts.input>[0]>): Promise<string | void>;
47
53
  }
48
- //# sourceMappingURL=writer.d.ts.map
54
+ export {};
55
+ //# sourceMappingURL=io.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"io.d.ts","sourceRoot":"","sources":["../../src/cli/io.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAe,KAAK,SAAS,EAAkB,MAAM,mBAAmB,CAAC;AAEhF,MAAM,WAAW,aAAa;IAC7B,YAAY,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC5B,OAAO,EAAE,SAAS,EAAE,GAAG,SAAS,EAAE,EAAE,CAAC;IACrC,IAAI,EAAE,SAAS,EAAE,EAAE,CAAC;IACpB,KAAK,EAAE,OAAO,CAAC;CACf;AAED,KAAK,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG;IACxB,KAAK,EAAE,MAAM,CAAC;CACd,CAAC;AAEF;;GAEG;AACH,qBAAa,EAAE;;IACd,YAAY,EAAE,OAAO,CAAC;gBAEV,OAAO,EAAE,aAAa;IAIlC;;;;OAIG;IACH,KAAK,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,OAAO,OAAO,CAAC,GAAG,CAAC;IAW7C;;;;OAIG;IACH,QAAQ,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,OAAO,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC;IAIzD;;;;OAIG;IACH,QAAQ,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,OAAO,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC;IAIzD;;;;OAIG;IACH,aAAa,CAAC,WAAW,EAAE,YAAY;IAKvC;;;;OAIG;IACH,aAAa,CAAC,WAAW,EAAE,YAAY;IAKvC,OAAO,CAAC,MAAM,EAAE,UAAU,CAAC,UAAU,CAAC,OAAO,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IASjE,KAAK,CAAC,MAAM,EAAE,UAAU,CAAC,UAAU,CAAC,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;CAkC7D"}
@@ -1,5 +1,6 @@
1
+ import * as prompts from "@inquirer/prompts";
1
2
  import { createTable } from "@visulima/tabular";
2
- class Writer {
3
+ class IO {
3
4
  debugEnabled;
4
5
  constructor(options) {
5
6
  this.debugEnabled = options.debugEnabled;
@@ -52,6 +53,27 @@ class Writer {
52
53
  const content = this.#buildTable(tableConfig);
53
54
  this.writeErr(content);
54
55
  }
56
+ confirm(config) {
57
+ if (!process.stdin.isTTY) {
58
+ this.writeErr(config.noTTY);
59
+ process.exit(1);
60
+ }
61
+ return prompts.confirm(config).catch(this.#handlePromptExit.bind(this));
62
+ }
63
+ input(config) {
64
+ if (!process.stdin.isTTY) {
65
+ this.writeErr(config.noTTY);
66
+ process.exit(1);
67
+ }
68
+ return prompts.input(config).catch(this.#handlePromptExit.bind(this));
69
+ }
70
+ #handlePromptExit(err) {
71
+ if (err instanceof Error && err.name === "ExitPromptError") {
72
+ this.writeErr("\u{1F44B} Prompt cancelled. Goodbye!");
73
+ process.exit(1);
74
+ }
75
+ throw err;
76
+ }
55
77
  #buildTable(tableConfig) {
56
78
  if (tableConfig.plain) {
57
79
  return tableConfig.rows.map((row) => {
@@ -69,5 +91,5 @@ function isTableItem(cell) {
69
91
  return typeof cell === "object";
70
92
  }
71
93
  export {
72
- Writer
94
+ IO
73
95
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@project-ajax/sdk",
3
- "version": "0.0.55",
3
+ "version": "0.0.57",
4
4
  "description": "An SDK for building workers for the Project Ajax platform",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -70,11 +70,11 @@
70
70
  "vitest": "^4.0.8"
71
71
  },
72
72
  "dependencies": {
73
+ "@inquirer/prompts": "^8.0.1",
73
74
  "@stricli/auto-complete": "^1.2.4",
74
75
  "@stricli/core": "^1.2.4",
75
76
  "@visulima/tabular": "^3.1.1",
76
77
  "ajv": "^8.17.1",
77
- "esbuild": "^0.25.12",
78
- "prompts": "^2.4.2"
78
+ "esbuild": "^0.25.12"
79
79
  }
80
80
  }
@@ -2,7 +2,7 @@
2
2
  * API client for making authenticated requests to the Workers API
3
3
  */
4
4
 
5
- import type { Writer } from "../writer.js";
5
+ import type { IO } from "../io.js";
6
6
  import { Result } from "./result.js";
7
7
 
8
8
  export type Environment = "local" | "staging" | "dev" | "prod";
@@ -12,7 +12,7 @@ interface ApiClientConfig {
12
12
  environment: Environment;
13
13
  baseUrl?: string | undefined;
14
14
  cellId: string;
15
- writer: Writer;
15
+ writer: IO;
16
16
  }
17
17
 
18
18
  type Endpoint = `/${string}`;
@@ -36,7 +36,7 @@ export class ApiClient {
36
36
  readonly #token: string;
37
37
  readonly #baseUrl: string | undefined;
38
38
  readonly #cellId: string;
39
- readonly #writer: Writer;
39
+ readonly #writer: IO;
40
40
 
41
41
  constructor(config: ApiClientConfig) {
42
42
  this.#token = config.token;
@@ -125,9 +125,11 @@ describe("login", () => {
125
125
  configFile: {
126
126
  token: null,
127
127
  workerId: null,
128
- environment: "prod",
129
128
  baseUrl: "https://www.notion.so",
130
129
  },
130
+ flags: {
131
+ env: "local",
132
+ },
131
133
  });
132
134
 
133
135
  vi.spyOn(Config, "load").mockResolvedValue(mockConfig);
@@ -142,9 +144,9 @@ describe("login", () => {
142
144
  await login.call(context, baseFlags, testToken);
143
145
 
144
146
  expect(updateSpy).toHaveBeenCalledWith({
145
- environment: "prod",
146
147
  token: testToken,
147
148
  workerId: null,
149
+ environment: "local",
148
150
  });
149
151
  });
150
152
  });
@@ -1,3 +1,4 @@
1
+ import type { ConfigMap } from "../config.js";
1
2
  import type { GlobalFlags } from "../flags.js";
2
3
  import { buildHandler, type HandlerContext } from "../handler.js";
3
4
  import { openNotionUrl } from "../utils/openUrl.js";
@@ -13,14 +14,14 @@ export const login = buildHandler(async function (
13
14
  if (!token) {
14
15
  const url = `${this.config.baseUrl}/__workers__?createToken=true`;
15
16
 
16
- this.writer.writeErr(`Opening browser to create a token...\n${url}\n`);
17
- this.writer.writeErr("After creating a token, run:");
18
- this.writer.writeErr("\tnpx workers auth login <token>");
17
+ this.io.writeErr(`Opening browser to create a token...\n${url}\n`);
18
+ this.io.writeErr("After creating a token, run:");
19
+ this.io.writeErr("\tnpx workers auth login <token>");
19
20
 
20
21
  try {
21
22
  await openNotionUrl(environment, url);
22
23
  } catch (_error) {
23
- this.writer.writeErr(
24
+ this.io.writeErr(
24
25
  `Failed to open browser automatically. Please visit:\n ${url}`,
25
26
  );
26
27
  }
@@ -28,17 +29,24 @@ export const login = buildHandler(async function (
28
29
  return;
29
30
  }
30
31
 
31
- await this.config.update({
32
- environment,
32
+ const update: Partial<ConfigMap> = {
33
33
  token,
34
34
  workerId: null,
35
- });
35
+ };
36
+
37
+ console.log("environment", environment);
38
+
39
+ if (environment !== "prod") {
40
+ update.environment = environment;
41
+ }
42
+
43
+ await this.config.update(update);
36
44
 
37
- this.writer.writeErr("Successfully logged in!");
45
+ this.io.writeErr("Successfully logged in!");
38
46
  });
39
47
 
40
48
  export const show = buildHandler(function (this: HandlerContext) {
41
- this.writer.writeOut(`${this.config.token ?? ""}`);
49
+ this.io.writeOut(`${this.config.token ?? ""}`);
42
50
  });
43
51
 
44
52
  export const logout = buildHandler(async function (this: HandlerContext) {
@@ -12,8 +12,8 @@ export const downloadBundle = buildAuthedHandler(async function () {
12
12
 
13
13
  const result = await this.apiClient.downloadWorkerBundle(workerId);
14
14
  if (Result.isFail(result)) {
15
- this.writer.writeErr(`✗ Failed to download bundle`);
16
- this.writer.writeErr(`✗ ${result.error.message}`);
15
+ this.io.writeErr(`✗ Failed to download bundle`);
16
+ this.io.writeErr(`✗ ${result.error.message}`);
17
17
  return;
18
18
  }
19
19
 
@@ -21,9 +21,9 @@ export const listCapabilities = buildAuthedHandler(async function (
21
21
 
22
22
  const data = Result.unwrap(result);
23
23
  if (data.capabilities.length === 0) {
24
- this.writer.writeErr("No capabilities found for this worker.");
24
+ this.io.writeErr("No capabilities found for this worker.");
25
25
  } else {
26
- this.writer.writeTableOut({
26
+ this.io.writeTableOut({
27
27
  headers: ["Key", "Tag"],
28
28
  rows: data.capabilities.map((capability) => [
29
29
  capability.key,
@@ -37,10 +37,10 @@ export const listCapabilities = buildAuthedHandler(async function (
37
37
 
38
38
  // If it's a validation error, show the clean debug message
39
39
  if (result.error.validationError) {
40
- this.writer.writeErr(`✗ ${result.error.validationError.debugMessage}`);
40
+ this.io.writeErr(`✗ ${result.error.validationError.debugMessage}`);
41
41
  throw new Error(result.error.validationError.debugMessage);
42
42
  } else {
43
- this.writer.writeErr(`✗ ${result.error.message}`);
43
+ this.io.writeErr(`✗ ${result.error.message}`);
44
44
  throw new Error(result.error.message);
45
45
  }
46
46
  }
@@ -17,11 +17,11 @@ export const listProviders = buildAuthedHandler(async function (
17
17
  this.process.stderr.write("OK\n\n");
18
18
  const providers = providersResult.value.providers;
19
19
  if (providers.length === 0) {
20
- this.writer.writeErr("No OAuth providers are currently available.");
20
+ this.io.writeErr("No OAuth providers are currently available.");
21
21
  return;
22
22
  }
23
23
 
24
- this.writer.writeTableOut({
24
+ this.io.writeTableOut({
25
25
  headers: ["Provider", "Description"],
26
26
  rows: providers.map((provider) => [provider.key, provider.displayName]),
27
27
  plain: flags.plain,
@@ -50,22 +50,22 @@ export const addConnection = buildAuthedHandler(async function (
50
50
 
51
51
  const { authorizationUrl } = startResult.value;
52
52
 
53
- this.writer.writeErr("Opening your browser to continue the OAuth flow...");
53
+ this.io.writeErr("Opening your browser to continue the OAuth flow...");
54
54
  try {
55
55
  await openBrowserUrl(authorizationUrl);
56
56
  } catch (error) {
57
- this.writer.writeErr(
57
+ this.io.writeErr(
58
58
  `Unable to open the browser automatically (${String(
59
59
  error,
60
60
  )}). Please open the link below manually.`,
61
61
  );
62
62
  }
63
63
 
64
- this.writer.writeErr("");
65
- this.writer.writeErr("If the browser did not open, visit:");
66
- this.writer.writeErr(` ${authorizationUrl}`);
67
- this.writer.writeErr("");
68
- this.writer.writeErr(
64
+ this.io.writeErr("");
65
+ this.io.writeErr("If the browser did not open, visit:");
66
+ this.io.writeErr(` ${authorizationUrl}`);
67
+ this.io.writeErr("");
68
+ this.io.writeErr(
69
69
  "After completing the flow in your browser, return to the CLI.",
70
70
  );
71
71
  });
@@ -91,11 +91,11 @@ export const listConnections = buildAuthedHandler(async function (
91
91
  const secrets = secretsResult.value.secrets;
92
92
 
93
93
  if (secrets.length === 0) {
94
- this.writer.writeErr("No OAuth connections found for this worker.");
94
+ this.io.writeErr("No OAuth connections found for this worker.");
95
95
  return;
96
96
  }
97
97
 
98
- this.writer.writeTableOut({
98
+ this.io.writeTableOut({
99
99
  headers: ["Env Var", "Created At"],
100
100
  rows: secrets.map((secret) => [
101
101
  `process.env.${secret.key}`,
@@ -143,7 +143,7 @@ function reportApiError(
143
143
  error: ApiError,
144
144
  action: string,
145
145
  ): never {
146
- context.writer.writeErr(`✗ Failed to ${action}`);
147
- context.writer.writeErr(`✗ ${error.message}`);
146
+ context.io.writeErr(`✗ Failed to ${action}`);
147
+ context.io.writeErr(`✗ ${error.message}`);
148
148
  throw new Error(error.message);
149
149
  }
@@ -21,11 +21,6 @@ vi.mock("../deploy.js", () => ({
21
21
  deployWorker: vi.fn(),
22
22
  }));
23
23
 
24
- vi.mock("prompts", () => ({
25
- default: vi.fn(),
26
- }));
27
-
28
- import prompts from "prompts";
29
24
  import { deployWorker } from "../deploy.js";
30
25
  import { deploy } from "./deploy.impl.js";
31
26
 
@@ -96,6 +91,7 @@ describe("deploy", () => {
96
91
 
97
92
  const updateSpy = vi.spyOn(mockConfig, "update");
98
93
  const context = createBaseContext();
94
+ vi.spyOn(context.io, "input").mockResolvedValue("my-worker");
99
95
 
100
96
  await deploy.call(context, { ...baseFlags, name: "my-worker" });
101
97
 
@@ -110,7 +106,7 @@ describe("deploy", () => {
110
106
  expect(updateSpy).toHaveBeenCalledWith({
111
107
  workerId: "worker-456",
112
108
  });
113
- expect(prompts).not.toHaveBeenCalled();
109
+ expect(context.io.input).not.toHaveBeenCalled();
114
110
 
115
111
  const allCalls = stderrSpy.mock.calls.map((call) => call[0]).join("");
116
112
  expect(allCalls).toContain("✓ Successfully deployed worker");
@@ -128,21 +124,20 @@ describe("deploy", () => {
128
124
  });
129
125
 
130
126
  vi.spyOn(Config, "load").mockResolvedValue(mockConfig);
131
- vi.mocked(prompts).mockResolvedValue({ name: "prompted-worker" });
132
127
  vi.mocked(deployWorker).mockResolvedValue(
133
128
  Result.success({ workerId: "worker-789" }),
134
129
  );
135
130
 
136
131
  const updateSpy = vi.spyOn(mockConfig, "update");
137
132
  const context = createBaseContext();
133
+ vi.spyOn(context.io, "input").mockResolvedValue("prompted-worker");
138
134
 
139
135
  await deploy.call(context, baseFlags);
140
136
 
141
- expect(prompts).toHaveBeenCalledWith({
142
- type: "text",
143
- name: "name",
137
+ expect(context.io.input).toHaveBeenCalledWith({
144
138
  message: "Enter a name for the worker",
145
- validate: expect.any(Function),
139
+ required: true,
140
+ noTTY: "Provide a name for the worker with --name",
146
141
  });
147
142
 
148
143
  expect(deployWorker).toHaveBeenCalledWith(expect.anything(), {
@@ -158,7 +153,7 @@ describe("deploy", () => {
158
153
  });
159
154
  });
160
155
 
161
- it("throws error when prompt is cancelled", async () => {
156
+ it("throws error when name is not provided", async () => {
162
157
  const [mockConfig] = await createAndLoadConfig({
163
158
  configFile: {
164
159
  token:
@@ -170,9 +165,9 @@ describe("deploy", () => {
170
165
  });
171
166
 
172
167
  vi.spyOn(Config, "load").mockResolvedValue(mockConfig);
173
- vi.mocked(prompts).mockResolvedValue({ name: undefined });
174
168
 
175
169
  const context = createBaseContext();
170
+ vi.spyOn(context.io, "input").mockResolvedValue("");
176
171
 
177
172
  await expect(deploy.call(context, baseFlags)).rejects.toThrow(
178
173
  "Name is required",
@@ -240,12 +235,12 @@ describe("deploy", () => {
240
235
  });
241
236
 
242
237
  vi.spyOn(Config, "load").mockResolvedValue(mockConfig);
243
- vi.mocked(prompts).mockResolvedValue({ name: " worker-name " });
244
238
  vi.mocked(deployWorker).mockResolvedValue(
245
239
  Result.success({ workerId: "worker-999" }),
246
240
  );
247
241
 
248
242
  const context = createBaseContext();
243
+ vi.spyOn(context.io, "input").mockResolvedValue(" worker-name ");
249
244
 
250
245
  await deploy.call(context, baseFlags);
251
246
 
@@ -1,4 +1,3 @@
1
- import prompts from "prompts";
2
1
  import type { ApiError } from "../api/client.js";
3
2
  import { Result } from "../api/result.js";
4
3
  import { deployWorker } from "../deploy.js";
@@ -19,7 +18,7 @@ export const deploy = buildAuthedHandler(async function (flags: DeployFlags) {
19
18
  }
20
19
  const workerPath = this.process.cwd();
21
20
 
22
- this.writer.writeErr("Deploying worker...");
21
+ this.io.writeErr("Deploying worker...");
23
22
 
24
23
  const name = flags.name;
25
24
  if (workerId && name) {
@@ -39,23 +38,19 @@ export const deploy = buildAuthedHandler(async function (flags: DeployFlags) {
39
38
  if (name) {
40
39
  validatedName = name;
41
40
  } else {
42
- const { name: nameInput } = await prompts({
43
- type: "text",
44
- name: "name",
41
+ const nameInput = await this.io.input({
45
42
  message: "Enter a name for the worker",
46
- validate: (value) => {
47
- if (value.length < 1) {
48
- return "Name is required";
49
- }
50
- return true;
51
- },
43
+ required: true,
44
+ noTTY: "Provide a name for the worker with --name",
52
45
  });
53
46
 
54
- if (!nameInput?.trim()) {
47
+ const trimmedName = nameInput?.trim();
48
+
49
+ if (!trimmedName) {
55
50
  throw new Error("Name is required");
56
51
  }
57
52
 
58
- validatedName = nameInput.trim();
53
+ validatedName = trimmedName;
59
54
  }
60
55
 
61
56
  result = await deployWorker(this, {
@@ -69,10 +64,10 @@ export const deploy = buildAuthedHandler(async function (flags: DeployFlags) {
69
64
  if (Result.isSuccess(result)) {
70
65
  const { workerId } = Result.unwrap(result);
71
66
  await this.config.update({ workerId });
72
- this.writer.writeErr("✓ Successfully deployed worker");
67
+ this.io.writeErr("✓ Successfully deployed worker");
73
68
  } else {
74
- this.writer.writeErr("✗ Failed to deploy worker");
75
- this.writer.writeErr(result.error.message);
69
+ this.io.writeErr("✗ Failed to deploy worker");
70
+ this.io.writeErr(result.error.message);
76
71
  return;
77
72
  }
78
73
  });
@@ -1,5 +1,3 @@
1
- import prompts from "prompts";
2
-
3
1
  import { Result } from "../api/result.js";
4
2
  import { buildAuthedHandler } from "../handler.js";
5
3
  import { pluralize } from "../utils/string.js";
@@ -28,25 +26,23 @@ export const pullEnv = buildAuthedHandler(async function (
28
26
  );
29
27
 
30
28
  if (this.fs.existsSync(absoluteDestination) && !flags.yes) {
31
- const { overwrite } = await prompts({
32
- type: "confirm",
33
- name: "overwrite",
29
+ const overwrite = await this.io.confirm({
34
30
  message: `File "${destination}" already exists. Overwrite it?`,
35
- initial: false,
31
+ noTTY: `File "${destination}" already exists.`,
36
32
  });
37
33
 
38
34
  if (overwrite !== true) {
39
- this.writer.writeErr("Aborted.");
35
+ this.io.writeErr("Aborted.");
40
36
  return;
41
37
  }
42
38
  }
43
39
 
44
- this.writer.writeErr("Fetching environment variables...");
40
+ this.io.writeErr("Fetching environment variables...");
45
41
  const envResult = await this.apiClient.pullEnv(workerId);
46
42
 
47
43
  if (Result.isFail(envResult)) {
48
- this.writer.writeErr("✗ Failed to fetch env vars");
49
- this.writer.writeErr(`✗ ${envResult.error.message}`);
44
+ this.io.writeErr("✗ Failed to fetch env vars");
45
+ this.io.writeErr(`✗ ${envResult.error.message}`);
50
46
  throw new Error(envResult.error.message);
51
47
  }
52
48
 
@@ -58,7 +54,7 @@ export const pullEnv = buildAuthedHandler(async function (
58
54
  });
59
55
  await this.fs.promises.writeFile(absoluteDestination, fileContents, "utf8");
60
56
 
61
- this.writer.writeErr(
57
+ this.io.writeErr(
62
58
  `Wrote ${pluralize(envEntries.length, "env var")} to "${destination}"`,
63
59
  );
64
60
  });
@@ -44,7 +44,7 @@ export const exec = buildAuthedHandler(async function (
44
44
  });
45
45
 
46
46
  // Execute the capability
47
- this.writer.writeErr(`Executing capability "${capabilityName}"...`);
47
+ this.io.writeErr(`Executing capability "${capabilityName}"...`);
48
48
 
49
49
  if (flags.stream) {
50
50
  const result = await this.apiClient.runCapability(
@@ -94,21 +94,21 @@ export const exec = buildAuthedHandler(async function (
94
94
  .trim()
95
95
  .split("\n")
96
96
  .forEach((dataLine) => {
97
- this.writer.writeErr(
97
+ this.io.writeErr(
98
98
  `[worker:${parsedLine.event.stream}] ${dataLine.trim()}`,
99
99
  );
100
100
  });
101
101
 
102
102
  break;
103
103
  case "result":
104
- this.writer.writeOut(JSON.stringify(parsedLine.result, null, 2));
104
+ this.io.writeOut(JSON.stringify(parsedLine.result, null, 2));
105
105
  break;
106
106
  case "error":
107
- this.writer.writeErr(`Error: ${parsedLine.error}`);
107
+ this.io.writeErr(`Error: ${parsedLine.error}`);
108
108
  break;
109
109
  }
110
110
  } catch (error) {
111
- this.writer.writeErr(`Error parsing log line: ${jsonLine}: ${error}`);
111
+ this.io.writeErr(`Error parsing log line: ${jsonLine}: ${error}`);
112
112
  }
113
113
  };
114
114
 
@@ -132,19 +132,17 @@ export const exec = buildAuthedHandler(async function (
132
132
  }
133
133
  return;
134
134
  } else {
135
- this.writer.writeErr(`\n✗ Failed to execute capability`);
135
+ this.io.writeErr(`\n✗ Failed to execute capability`);
136
136
 
137
137
  // If it's a validation error, show the clean debug message
138
138
  if (result.error.validationError) {
139
- this.writer.writeErr(`\t✗ HTTP Error: ${result.error.status}`);
140
- this.writer.writeErr(
141
- `\t✗ ${result.error.validationError.debugMessage}`,
142
- );
143
- this.writer.writeErr(`\n${usageHint}`);
139
+ this.io.writeErr(`\t✗ HTTP Error: ${result.error.status}`);
140
+ this.io.writeErr(`\t✗ ${result.error.validationError.debugMessage}`);
141
+ this.io.writeErr(`\n${usageHint}`);
144
142
  throw new Error(result.error.validationError.debugMessage);
145
143
  } else {
146
- this.writer.writeErr(`\t✗ HTTP Error: ${result.error.status}`);
147
- this.writer.writeErr(`\t✗ ${result.error.message}`);
144
+ this.io.writeErr(`\t✗ HTTP Error: ${result.error.status}`);
145
+ this.io.writeErr(`\t✗ ${result.error.message}`);
148
146
  throw new Error(result.error.message);
149
147
  }
150
148
  }
@@ -159,10 +157,10 @@ export const exec = buildAuthedHandler(async function (
159
157
 
160
158
  if (Result.isSuccess(result)) {
161
159
  const data = Result.unwrap(result);
162
- this.writer.writeOut(JSON.stringify(data.result, null, 2));
160
+ this.io.writeOut(JSON.stringify(data.result, null, 2));
163
161
  } else {
164
- this.writer.writeErr(`\n✗ Failed to execute capability`);
165
- this.writer.writeErr(`\t✗ ${result.error.message}`);
162
+ this.io.writeErr(`\n✗ Failed to execute capability`);
163
+ this.io.writeErr(`\t✗ ${result.error.message}`);
166
164
  throw new Error(result.error.message);
167
165
  }
168
166
  }