neonctl 2.27.1 → 2.29.0

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 (136) hide show
  1. package/README.md +35 -3
  2. package/dist/analytics.js +52 -34
  3. package/dist/api.js +643 -13
  4. package/dist/auth.js +50 -44
  5. package/dist/cli.js +8 -1
  6. package/dist/commands/auth.js +64 -51
  7. package/dist/commands/bootstrap.js +115 -157
  8. package/dist/commands/branches.js +160 -150
  9. package/dist/commands/bucket.js +183 -146
  10. package/dist/commands/checkout.js +51 -51
  11. package/dist/commands/config.js +228 -82
  12. package/dist/commands/connection_string.js +62 -62
  13. package/dist/commands/data_api.js +100 -101
  14. package/dist/commands/databases.js +29 -26
  15. package/dist/commands/deploy.js +12 -12
  16. package/dist/commands/dev.js +114 -114
  17. package/dist/commands/env.js +43 -43
  18. package/dist/commands/functions.js +101 -104
  19. package/dist/commands/index.js +27 -25
  20. package/dist/commands/init.js +23 -22
  21. package/dist/commands/ip_allow.js +29 -29
  22. package/dist/commands/link.js +232 -182
  23. package/dist/commands/neon_auth.js +385 -370
  24. package/dist/commands/operations.js +11 -11
  25. package/dist/commands/orgs.js +8 -8
  26. package/dist/commands/projects.js +103 -101
  27. package/dist/commands/psql.js +31 -31
  28. package/dist/commands/roles.js +27 -24
  29. package/dist/commands/schema_diff.js +25 -26
  30. package/dist/commands/set_context.js +17 -17
  31. package/dist/commands/status.js +40 -0
  32. package/dist/commands/user.js +5 -5
  33. package/dist/commands/vpc_endpoints.js +50 -50
  34. package/dist/config.js +7 -7
  35. package/dist/config_format.js +5 -5
  36. package/dist/context.js +37 -14
  37. package/dist/current_branch_fast_path.js +55 -0
  38. package/dist/dev/env.js +33 -33
  39. package/dist/dev/functions.js +4 -4
  40. package/dist/dev/inputs.js +6 -6
  41. package/dist/dev/runtime.js +25 -25
  42. package/dist/env.js +14 -14
  43. package/dist/env_file.js +13 -13
  44. package/dist/errors.js +68 -5
  45. package/dist/functions_api.js +10 -10
  46. package/dist/help.js +15 -15
  47. package/dist/index.js +110 -107
  48. package/dist/log.js +2 -2
  49. package/dist/parameters.gen.js +14 -14
  50. package/dist/pkg.js +5 -5
  51. package/dist/psql/cli.js +4 -2
  52. package/dist/psql/command/cmd_cond.js +61 -61
  53. package/dist/psql/command/cmd_connect.js +159 -154
  54. package/dist/psql/command/cmd_copy.js +107 -97
  55. package/dist/psql/command/cmd_describe.js +368 -363
  56. package/dist/psql/command/cmd_format.js +276 -263
  57. package/dist/psql/command/cmd_io.js +269 -263
  58. package/dist/psql/command/cmd_lo.js +74 -66
  59. package/dist/psql/command/cmd_meta.js +148 -148
  60. package/dist/psql/command/cmd_misc.js +17 -17
  61. package/dist/psql/command/cmd_pipeline.js +142 -135
  62. package/dist/psql/command/cmd_restrict.js +25 -25
  63. package/dist/psql/command/cmd_show.js +183 -168
  64. package/dist/psql/command/dispatch.js +26 -26
  65. package/dist/psql/command/shared.js +14 -14
  66. package/dist/psql/complete/filenames.js +16 -16
  67. package/dist/psql/complete/index.js +4 -4
  68. package/dist/psql/complete/matcher.js +33 -32
  69. package/dist/psql/complete/psqlVars.js +173 -173
  70. package/dist/psql/complete/queries.js +5 -3
  71. package/dist/psql/complete/rules.js +900 -863
  72. package/dist/psql/core/common.js +136 -133
  73. package/dist/psql/core/help.js +343 -343
  74. package/dist/psql/core/mainloop.js +160 -153
  75. package/dist/psql/core/prompt.js +126 -123
  76. package/dist/psql/core/settings.js +111 -111
  77. package/dist/psql/core/sqlHelp.js +150 -150
  78. package/dist/psql/core/startup.js +211 -205
  79. package/dist/psql/core/syncVars.js +14 -14
  80. package/dist/psql/core/variables.js +24 -24
  81. package/dist/psql/describe/formatters.js +302 -289
  82. package/dist/psql/describe/processNamePattern.js +28 -28
  83. package/dist/psql/describe/queries.js +656 -651
  84. package/dist/psql/index.js +436 -411
  85. package/dist/psql/io/history.js +36 -36
  86. package/dist/psql/io/input.js +15 -15
  87. package/dist/psql/io/lineEditor/buffer.js +27 -25
  88. package/dist/psql/io/lineEditor/complete.js +15 -15
  89. package/dist/psql/io/lineEditor/filename.js +22 -22
  90. package/dist/psql/io/lineEditor/index.js +65 -62
  91. package/dist/psql/io/lineEditor/keymap.js +325 -318
  92. package/dist/psql/io/lineEditor/vt100.js +60 -60
  93. package/dist/psql/io/pgpass.js +18 -18
  94. package/dist/psql/io/pgservice.js +14 -14
  95. package/dist/psql/io/psqlrc.js +46 -46
  96. package/dist/psql/print/aligned.js +175 -166
  97. package/dist/psql/print/asciidoc.js +51 -51
  98. package/dist/psql/print/crosstab.js +34 -31
  99. package/dist/psql/print/csv.js +25 -22
  100. package/dist/psql/print/html.js +54 -54
  101. package/dist/psql/print/json.js +12 -12
  102. package/dist/psql/print/latex.js +118 -118
  103. package/dist/psql/print/pager.js +28 -26
  104. package/dist/psql/print/troff.js +48 -48
  105. package/dist/psql/print/unaligned.js +15 -14
  106. package/dist/psql/print/units.js +17 -17
  107. package/dist/psql/scanner/slash.js +48 -46
  108. package/dist/psql/scanner/sql.js +88 -84
  109. package/dist/psql/scanner/stringutils.js +21 -17
  110. package/dist/psql/types/index.js +7 -7
  111. package/dist/psql/types/scanner.js +8 -8
  112. package/dist/psql/wire/connection.js +341 -327
  113. package/dist/psql/wire/copy.js +7 -7
  114. package/dist/psql/wire/pipeline.js +26 -24
  115. package/dist/psql/wire/protocol.js +102 -102
  116. package/dist/psql/wire/sasl.js +62 -62
  117. package/dist/psql/wire/tls.js +79 -73
  118. package/dist/storage_api.js +22 -23
  119. package/dist/test_utils/fixtures.js +74 -41
  120. package/dist/test_utils/oauth_server.js +5 -5
  121. package/dist/utils/api_enums.js +33 -0
  122. package/dist/utils/branch_notice.js +5 -5
  123. package/dist/utils/branch_picker.js +26 -26
  124. package/dist/utils/compute_units.js +4 -4
  125. package/dist/utils/enrichers.js +28 -16
  126. package/dist/utils/esbuild.js +28 -28
  127. package/dist/utils/formats.js +1 -1
  128. package/dist/utils/middlewares.js +3 -3
  129. package/dist/utils/package_manager.js +68 -0
  130. package/dist/utils/point_in_time.js +12 -12
  131. package/dist/utils/psql.js +30 -30
  132. package/dist/utils/string.js +2 -2
  133. package/dist/utils/ui.js +9 -9
  134. package/dist/utils/zip.js +1 -1
  135. package/dist/writer.js +17 -17
  136. package/package.json +10 -12
package/dist/auth.js CHANGED
@@ -1,58 +1,62 @@
1
- import * as client from 'openid-client';
2
- import { createServer } from 'node:http';
3
- import { createReadStream } from 'node:fs';
4
- import { join } from 'node:path';
5
- import open from 'open';
6
- import { log } from './log.js';
7
- import { fileURLToPath } from 'node:url';
8
- import { sendError } from './analytics.js';
9
- import { matchErrorCode } from './errors.js';
10
- import { extendTokenSet } from './utils/auth.js';
1
+ import { createReadStream } from "node:fs";
2
+ import { createServer, } from "node:http";
3
+ import { join } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import open from "open";
6
+ import * as client from "openid-client";
7
+ import { sendError } from "./analytics.js";
8
+ import { matchErrorCode } from "./errors.js";
9
+ import { log } from "./log.js";
10
+ import { extendTokenSet } from "./utils/auth.js";
11
11
  // oauth server timeouts
12
12
  const SERVER_TIMEOUT = 10000;
13
13
  // where to wait for incoming redirect request from oauth server to arrive
14
14
  const REDIRECT_URI = (port) => `http://127.0.0.1:${port}/callback`;
15
15
  // These scopes cannot be cancelled, they are always needed.
16
- const ALWAYS_PRESENT_SCOPES = ['openid', 'offline', 'offline_access'];
16
+ const ALWAYS_PRESENT_SCOPES = ["openid", "offline", "offline_access"];
17
17
  const NEONCTL_SCOPES = [
18
18
  ...ALWAYS_PRESENT_SCOPES,
19
- 'urn:neoncloud:projects:create',
20
- 'urn:neoncloud:projects:read',
21
- 'urn:neoncloud:projects:update',
22
- 'urn:neoncloud:projects:delete',
23
- 'urn:neoncloud:orgs:create',
24
- 'urn:neoncloud:orgs:read',
25
- 'urn:neoncloud:orgs:update',
26
- 'urn:neoncloud:orgs:delete',
27
- 'urn:neoncloud:orgs:permission',
19
+ "urn:neoncloud:projects:create",
20
+ "urn:neoncloud:projects:read",
21
+ "urn:neoncloud:projects:update",
22
+ "urn:neoncloud:projects:delete",
23
+ "urn:neoncloud:orgs:create",
24
+ "urn:neoncloud:orgs:read",
25
+ "urn:neoncloud:orgs:update",
26
+ "urn:neoncloud:orgs:delete",
27
+ "urn:neoncloud:orgs:permission",
28
28
  ];
29
29
  const AUTH_TIMEOUT_SECONDS = 60;
30
- export const defaultClientID = 'neonctl';
30
+ export const defaultClientID = "neonctl";
31
31
  export const refreshToken = async ({ oauthHost, clientId, allowUnsafeTls }, tokenSet) => {
32
- log.debug('Discovering oauth server');
33
- const configuration = await client.discovery(new URL(oauthHost), clientId, { token_endpoint_auth_method: 'none' }, client.None(), {
32
+ log.debug("Discovering oauth server");
33
+ const configuration = await client.discovery(new URL(oauthHost), clientId, { token_endpoint_auth_method: "none" }, client.None(), {
34
34
  timeout: SERVER_TIMEOUT,
35
- // eslint-disable-next-line @typescript-eslint/no-deprecated
36
- execute: allowUnsafeTls ? [client.allowInsecureRequests] : undefined,
35
+ execute: allowUnsafeTls
36
+ ? // eslint-disable-next-line @typescript-eslint/no-deprecated
37
+ [client.allowInsecureRequests]
38
+ : undefined,
37
39
  });
38
40
  return await client.refreshTokenGrant(configuration, tokenSet.refresh_token);
39
41
  };
40
42
  export const auth = async ({ oauthHost, clientId, allowUnsafeTls, }) => {
41
- log.debug('Discovering oauth server');
42
- const configuration = await client.discovery(new URL(oauthHost), clientId, { token_endpoint_auth_method: 'none' }, client.None(), {
43
+ log.debug("Discovering oauth server");
44
+ const configuration = await client.discovery(new URL(oauthHost), clientId, { token_endpoint_auth_method: "none" }, client.None(), {
43
45
  timeout: SERVER_TIMEOUT,
44
- // eslint-disable-next-line @typescript-eslint/no-deprecated
45
- execute: allowUnsafeTls ? [client.allowInsecureRequests] : undefined,
46
+ execute: allowUnsafeTls
47
+ ? // eslint-disable-next-line @typescript-eslint/no-deprecated
48
+ [client.allowInsecureRequests]
49
+ : undefined,
46
50
  });
47
51
  //
48
52
  // Start HTTP server and wait till /callback is hit
49
53
  //
50
- log.debug('Starting HTTP Server for callback');
54
+ log.debug("Starting HTTP Server for callback");
51
55
  const server = createServer();
52
- server.listen(0, '127.0.0.1', function () {
56
+ server.listen(0, "127.0.0.1", function () {
53
57
  log.debug(`Listening on port ${this.address().port}`);
54
58
  });
55
- await new Promise((resolve) => server.once('listening', resolve));
59
+ await new Promise((resolve) => server.once("listening", resolve));
56
60
  const listen_port = server.address().port;
57
61
  // https://datatracker.ietf.org/doc/html/rfc6819#section-4.4.1.8
58
62
  const state = client.randomState();
@@ -67,17 +71,17 @@ export const auth = async ({ oauthHost, clientId, allowUnsafeTls, }) => {
67
71
  //
68
72
  // Wait for callback and follow oauth flow.
69
73
  //
70
- if (!request.url?.startsWith('/callback')) {
74
+ if (!request.url?.startsWith("/callback")) {
71
75
  response.writeHead(404);
72
76
  response.end();
73
77
  return;
74
78
  }
75
79
  // process the CORS preflight OPTIONS request
76
- if (request.method === 'OPTIONS') {
80
+ if (request.method === "OPTIONS") {
77
81
  response.writeHead(200, {
78
- 'Access-Control-Allow-Origin': '*',
79
- 'Access-Control-Allow-Methods': 'GET, POST',
80
- 'Access-Control-Allow-Headers': 'Content-Type',
82
+ "Access-Control-Allow-Origin": "*",
83
+ "Access-Control-Allow-Methods": "GET, POST",
84
+ "Access-Control-Allow-Headers": "Content-Type",
81
85
  });
82
86
  response.end();
83
87
  return;
@@ -87,29 +91,31 @@ export const auth = async ({ oauthHost, clientId, allowUnsafeTls, }) => {
87
91
  pkceCodeVerifier: codeVerifier,
88
92
  expectedState: state,
89
93
  });
90
- response.writeHead(200, { 'Content-Type': 'text/html' });
91
- createReadStream(join(fileURLToPath(new URL('.', import.meta.url)), './callback.html')).pipe(response);
94
+ response.writeHead(200, { "Content-Type": "text/html" });
95
+ createReadStream(join(fileURLToPath(new URL(".", import.meta.url)), "./callback.html")).pipe(response);
92
96
  clearTimeout(timer);
93
97
  const exp = new Date();
94
98
  exp.setSeconds(exp.getSeconds() + (tokenSet.expires_in ?? 0));
95
99
  resolve(extendTokenSet(tokenSet));
96
100
  server.close();
97
101
  };
98
- server.on('request', (req, res) => {
102
+ server.on("request", (req, res) => {
99
103
  void onRequest(req, res);
100
104
  });
101
105
  //
102
106
  // Open browser to let user authenticate
103
107
  //
104
- const scopes = clientId == defaultClientID ? NEONCTL_SCOPES : ALWAYS_PRESENT_SCOPES;
108
+ const scopes = clientId == defaultClientID
109
+ ? NEONCTL_SCOPES
110
+ : ALWAYS_PRESENT_SCOPES;
105
111
  const authUrl = client.buildAuthorizationUrl(configuration, {
106
- scope: scopes.join(' '),
112
+ scope: scopes.join(" "),
107
113
  state,
108
114
  code_challenge: codeChallenge,
109
- code_challenge_method: 'S256',
115
+ code_challenge_method: "S256",
110
116
  redirect_uri: REDIRECT_URI(listen_port),
111
117
  });
112
- log.info('Awaiting authentication in web browser.');
118
+ log.info("Awaiting authentication in web browser.");
113
119
  log.info(`Auth Url: ${authUrl}`);
114
120
  open(authUrl.href).catch((err) => {
115
121
  const msg = `Failed to open web browser. Please copy & paste auth url to authenticate in browser.`;
package/dist/cli.js CHANGED
@@ -1,2 +1,9 @@
1
1
  #!/usr/bin/env node
2
- import './index.js';
2
+ import { tryCurrentBranchFastPath } from "./current_branch_fast_path.js";
3
+ // Fast path for the offline `(config) status --current-branch` probe (used by shell
4
+ // prompts): read the pinned branch from `.neon` without loading the full command tree,
5
+ // api-client, and yargs (~200ms). Falls through to the full CLI for everything else, so
6
+ // the heavy `index.js` is imported lazily and only when actually needed.
7
+ if (!tryCurrentBranchFastPath(process.argv)) {
8
+ void import("./index.js");
9
+ }
@@ -1,25 +1,26 @@
1
- import { existsSync, readFileSync, writeFileSync, rmSync } from 'node:fs';
2
- import { join } from 'node:path';
3
- import { createHash } from 'node:crypto';
4
- import { getApiClient } from '../api.js';
5
- import { auth, refreshToken } from '../auth.js';
6
- import { CREDENTIALS_FILE } from '../config.js';
7
- import { isCi } from '../env.js';
8
- import { log } from '../log.js';
9
- import { extendTokenSet } from '../utils/auth.js';
10
- export const command = 'auth';
11
- export const aliases = ['login'];
12
- export const describe = 'Authenticate';
13
- export const builder = (yargs) => yargs.option('context-file', {
1
+ import { createHash } from "node:crypto";
2
+ import { existsSync, readFileSync, rmSync, writeFileSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { getApiClient } from "../api.js";
5
+ import { auth, refreshToken } from "../auth.js";
6
+ import { CREDENTIALS_FILE } from "../config.js";
7
+ import { isConfigInit, isCurrentBranchProbe } from "../context.js";
8
+ import { isCi } from "../env.js";
9
+ import { log } from "../log.js";
10
+ import { extendTokenSet } from "../utils/auth.js";
11
+ export const command = "auth";
12
+ export const aliases = ["login"];
13
+ export const describe = "Authenticate";
14
+ export const builder = (yargs) => yargs.option("context-file", {
14
15
  hidden: true,
15
16
  });
16
17
  export const handler = async (args) => {
17
18
  await authFlow(args);
18
19
  };
19
- export const authFlow = async ({ configDir, oauthHost, clientId, apiHost, forceAuth, 'force-auth': forceAuthKebab, allowUnsafeTls, }) => {
20
+ export const authFlow = async ({ configDir, oauthHost, clientId, apiHost, forceAuth, "force-auth": forceAuthKebab, allowUnsafeTls, }) => {
20
21
  const allowInteractiveAuth = forceAuth ?? forceAuthKebab;
21
22
  if (!allowInteractiveAuth && isCi()) {
22
- throw new Error('Cannot run interactive auth in CI');
23
+ throw new Error("Cannot run interactive auth in CI");
23
24
  }
24
25
  const tokenSet = await auth({
25
26
  oauthHost: oauthHost,
@@ -29,16 +30,16 @@ export const authFlow = async ({ configDir, oauthHost, clientId, apiHost, forceA
29
30
  const credentialsPath = join(configDir, CREDENTIALS_FILE);
30
31
  try {
31
32
  await preserveCredentials(credentialsPath, tokenSet, getApiClient({
32
- apiKey: tokenSet.access_token || '',
33
+ apiKey: tokenSet.access_token || "",
33
34
  apiHost,
34
35
  }));
35
36
  }
36
37
  catch {
37
- log.error('Failed to save credentials');
38
- return '';
38
+ log.error("Failed to save credentials");
39
+ return "";
39
40
  }
40
- log.info('Auth complete');
41
- return tokenSet.access_token || '';
41
+ log.info("Auth complete");
42
+ return tokenSet.access_token || "";
42
43
  };
43
44
  const preserveCredentials = async (path, credentials, apiClient) => {
44
45
  const { data: { id }, } = await apiClient.getCurrentUserInfo();
@@ -51,13 +52,13 @@ const preserveCredentials = async (path, credentials, apiClient) => {
51
52
  writeFileSync(path, contents, {
52
53
  mode: 0o700,
53
54
  });
54
- log.debug('Saved credentials to %s', path);
55
- log.debug('Credentials MD5 hash: %s', md5hash(contents));
55
+ log.debug("Saved credentials to %s", path);
56
+ log.debug("Credentials MD5 hash: %s", md5hash(contents));
56
57
  };
57
58
  const handleExistingToken = async (tokenSet, props, credentialsPath) => {
58
59
  // Use existing access_token, if present and valid
59
60
  if (tokenSet.access_token && tokenSet.expires_at > Date.now()) {
60
- log.debug('Using existing valid access_token');
61
+ log.debug("Using existing valid access_token");
61
62
  const apiClient = getApiClient({
62
63
  apiKey: tokenSet.access_token,
63
64
  apiHost: props.apiHost,
@@ -66,10 +67,10 @@ const handleExistingToken = async (tokenSet, props, credentialsPath) => {
66
67
  }
67
68
  // Either access_token is missing or its expired. Refresh the token
68
69
  log.debug(tokenSet.expires_at < Date.now()
69
- ? 'Token is expired, attempting refresh'
70
- : 'Token is missing access_token, attempting refresh');
70
+ ? "Token is expired, attempting refresh"
71
+ : "Token is missing access_token, attempting refresh");
71
72
  if (!tokenSet.refresh_token) {
72
- log.debug('TokenSet is missing refresh_token, starting authentication');
73
+ log.debug("TokenSet is missing refresh_token, starting authentication");
73
74
  return null;
74
75
  }
75
76
  try {
@@ -86,13 +87,13 @@ const handleExistingToken = async (tokenSet, props, credentialsPath) => {
86
87
  apiHost: props.apiHost,
87
88
  });
88
89
  await preserveCredentials(credentialsPath, extendedTokenSet, apiClient);
89
- log.debug('Token refresh successful');
90
+ log.debug("Token refresh successful");
90
91
  return { apiKey, apiClient };
91
92
  }
92
93
  catch (err) {
93
- const typedErr = err instanceof Error ? err : new Error('Unknown error');
94
- log.debug('Failed to refresh token: %s', typedErr.message);
95
- throw new Error('AUTH_REFRESH_FAILED');
94
+ const typedErr = err instanceof Error ? err : new Error("Unknown error");
95
+ log.debug("Failed to refresh token: %s", typedErr.message);
96
+ throw new Error("AUTH_REFRESH_FAILED");
96
97
  }
97
98
  };
98
99
  export const ensureAuth = async (props) => {
@@ -100,23 +101,34 @@ export const ensureAuth = async (props) => {
100
101
  if (props._.length === 0 || props.help) {
101
102
  return;
102
103
  }
104
+ // `(config) status --current-branch` is a purely-local read of `.neon`; it must
105
+ // never refresh a token or pop a browser login. Skip auth entirely (the handler
106
+ // doesn't use an API client in this mode).
107
+ if (isCurrentBranchProbe(props)) {
108
+ return;
109
+ }
110
+ // `config init` only scaffolds a neon.ts and installs npm packages locally; it
111
+ // never calls the Neon API, so skip auth entirely — no token refresh, no login.
112
+ if (isConfigInit(props)) {
113
+ return;
114
+ }
103
115
  // `dev` runs a function locally. It injects the selected branch's env vars
104
116
  // when credentials happen to be available, but must never trigger an
105
117
  // interactive login: use an API key or existing stored credentials if
106
118
  // present, otherwise run with no API client (env injection is skipped).
107
- const isLocalDev = props._[0] === 'dev';
119
+ const isLocalDev = props._[0] === "dev";
108
120
  // `bootstrap` only copies a public template repo; it never calls the Neon
109
121
  // API, so it must work without credentials and must never pop a browser
110
122
  // login. It uses an API key / stored credentials when present (harmless),
111
123
  // otherwise it proceeds with no API client.
112
- const isBootstrap = props._[0] === 'bootstrap';
124
+ const isBootstrap = props._[0] === "bootstrap";
113
125
  // `init` manages its own auth flow (asks the user if they have an account,
114
126
  // then triggers OAuth at the right time). Skip the global auth middleware.
115
- const isInit = props._[0] === 'init';
127
+ const isInit = props._[0] === "init";
116
128
  // Use existing API key or handle auth command
117
- if (props.apiKey || props._[0] === 'auth') {
129
+ if (props.apiKey || props._[0] === "auth") {
118
130
  if (props.apiKey) {
119
- log.debug('Using an API key to authorize requests');
131
+ log.debug("Using an API key to authorize requests");
120
132
  }
121
133
  props.apiClient = getApiClient({
122
134
  apiKey: props.apiKey,
@@ -127,10 +139,10 @@ export const ensureAuth = async (props) => {
127
139
  const credentialsPath = join(props.configDir, CREDENTIALS_FILE);
128
140
  // Handle case when credentials file exists
129
141
  if (existsSync(credentialsPath)) {
130
- log.debug('Trying to read credentials from %s', credentialsPath);
142
+ log.debug("Trying to read credentials from %s", credentialsPath);
131
143
  try {
132
- const contents = readFileSync(credentialsPath, 'utf8');
133
- log.debug('Credentials MD5 hash: %s', md5hash(contents));
144
+ const contents = readFileSync(credentialsPath, "utf8");
145
+ log.debug("Credentials MD5 hash: %s", md5hash(contents));
134
146
  const tokenSet = JSON.parse(contents);
135
147
  // Try to use existing token or refresh it
136
148
  const result = await handleExistingToken(tokenSet, props, credentialsPath);
@@ -141,32 +153,33 @@ export const ensureAuth = async (props) => {
141
153
  }
142
154
  }
143
155
  catch (err) {
144
- if (!(err instanceof Error && err.message === 'AUTH_REFRESH_FAILED') &&
145
- err.code !== 'ENOENT' &&
156
+ if (!(err instanceof Error &&
157
+ err.message === "AUTH_REFRESH_FAILED") &&
158
+ err.code !== "ENOENT" &&
146
159
  !(err instanceof SyntaxError)) {
147
160
  // Throw for any errors except auth refresh failure, missing file, or invalid credentials file
148
161
  throw err;
149
162
  }
150
163
  // Fall through to new auth flow for auth failures
151
- log.debug('Ensure auth failed, starting authentication', err);
164
+ log.debug("Ensure auth failed, starting authentication", err);
152
165
  }
153
166
  }
154
167
  else {
155
- log.debug('Credentials file %s does not exist, starting authentication', credentialsPath);
168
+ log.debug("Credentials file %s does not exist, starting authentication", credentialsPath);
156
169
  }
157
170
  // `dev` never launches the interactive browser flow. With no usable
158
171
  // credentials it proceeds without an API client; env injection is skipped
159
172
  // and the function still runs locally.
160
173
  if (isLocalDev) {
161
- log.debug('dev: no usable credentials; running without env injection');
174
+ log.debug("dev: no usable credentials; running without env injection");
162
175
  return;
163
176
  }
164
177
  if (isBootstrap) {
165
- log.debug('bootstrap: no usable credentials; continuing without auth');
178
+ log.debug("bootstrap: no usable credentials; continuing without auth");
166
179
  return;
167
180
  }
168
181
  if (isInit) {
169
- log.debug('init: skipping global auth; init manages its own auth flow');
182
+ log.debug("init: skipping global auth; init manages its own auth flow");
170
183
  return;
171
184
  }
172
185
  // Start new auth flow if no valid token exists or refresh failed
@@ -186,16 +199,16 @@ export const deleteCredentials = (configDir) => {
186
199
  try {
187
200
  if (existsSync(credentialsPath)) {
188
201
  rmSync(credentialsPath);
189
- log.info('Deleted credentials from %s', credentialsPath);
202
+ log.info("Deleted credentials from %s", credentialsPath);
190
203
  }
191
204
  else {
192
- log.debug('Credentials file %s does not exist', credentialsPath);
205
+ log.debug("Credentials file %s does not exist", credentialsPath);
193
206
  }
194
207
  }
195
208
  catch (err) {
196
- const typedErr = err instanceof Error ? err : new Error('Unknown error');
197
- log.error('Failed to delete credentials: %s', typedErr.message);
198
- throw new Error('CREDENTIALS_DELETE_FAILED');
209
+ const typedErr = err instanceof Error ? err : new Error("Unknown error");
210
+ log.error("Failed to delete credentials: %s", typedErr.message);
211
+ throw new Error("CREDENTIALS_DELETE_FAILED");
199
212
  }
200
213
  };
201
- const md5hash = (s) => createHash('md5').update(s).digest('hex');
214
+ const md5hash = (s) => createHash("md5").update(s).digest("hex");