@vigilbase/cli 0.1.4 → 0.2.1

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 (191) hide show
  1. package/CLOUDFLARE.md +3 -4
  2. package/COMMANDS.md +158 -83
  3. package/LOCAL.md +721 -0
  4. package/ONE.md +2 -4
  5. package/README.md +341 -123
  6. package/SECURITY.md +2 -2
  7. package/dist/auth/device-auth.d.ts +0 -1
  8. package/dist/auth/device-auth.js +13 -8
  9. package/dist/auth/index.d.ts +4 -2
  10. package/dist/auth/index.js +3 -2
  11. package/dist/auth/provider.d.ts +18 -0
  12. package/dist/auth/provider.js +71 -0
  13. package/dist/auth/refresh.d.ts +29 -0
  14. package/dist/auth/refresh.js +199 -0
  15. package/dist/auth/store.d.ts +103 -4
  16. package/dist/auth/store.js +400 -74
  17. package/dist/auth/token-metadata.d.ts +9 -0
  18. package/dist/auth/token-metadata.js +21 -0
  19. package/dist/auth/types.d.ts +8 -2
  20. package/dist/auth/types.js +0 -1
  21. package/dist/bin.d.ts +0 -1
  22. package/dist/bin.js +0 -1
  23. package/dist/cli.d.ts +4 -2
  24. package/dist/cli.js +45 -15
  25. package/dist/commands/account.d.ts +0 -1
  26. package/dist/commands/account.js +0 -1
  27. package/dist/commands/auth.d.ts +0 -1
  28. package/dist/commands/auth.js +82 -22
  29. package/dist/commands/billing.d.ts +0 -1
  30. package/dist/commands/billing.js +0 -1
  31. package/dist/commands/cloudflare.d.ts +1 -2
  32. package/dist/commands/cloudflare.js +6 -6
  33. package/dist/commands/helpers.d.ts +15 -1
  34. package/dist/commands/helpers.js +2 -3
  35. package/dist/commands/index.d.ts +3 -2
  36. package/dist/commands/index.js +6 -2
  37. package/dist/commands/legacy-validation.d.ts +0 -1
  38. package/dist/commands/legacy-validation.js +0 -1
  39. package/dist/commands/local.d.ts +35 -0
  40. package/dist/commands/local.js +1543 -0
  41. package/dist/commands/one.d.ts +0 -1
  42. package/dist/commands/one.js +6 -7
  43. package/dist/commands/public-tools.d.ts +0 -1
  44. package/dist/commands/public-tools.js +55 -17
  45. package/dist/commands/runs.d.ts +0 -1
  46. package/dist/commands/runs.js +0 -1
  47. package/dist/commands/security.d.ts +1 -2
  48. package/dist/commands/security.js +1 -2
  49. package/dist/commands/watchdog.d.ts +0 -1
  50. package/dist/commands/watchdog.js +0 -1
  51. package/dist/index.d.ts +3 -3
  52. package/dist/index.js +1 -2
  53. package/dist/lib/banner.d.ts +0 -1
  54. package/dist/lib/banner.js +0 -1
  55. package/dist/lib/client.d.ts +4 -2
  56. package/dist/lib/client.js +42 -18
  57. package/dist/lib/cloudflare-client.d.ts +0 -1
  58. package/dist/lib/cloudflare-client.js +0 -1
  59. package/dist/lib/config.d.ts +31 -3
  60. package/dist/lib/config.js +72 -13
  61. package/dist/lib/format.d.ts +2 -1
  62. package/dist/lib/format.js +27 -7
  63. package/dist/lib/help.d.ts +1 -2
  64. package/dist/lib/help.js +37 -17
  65. package/dist/lib/network.d.ts +10 -1
  66. package/dist/lib/network.js +33 -5
  67. package/dist/lib/one-client.d.ts +5 -3
  68. package/dist/lib/one-client.js +49 -3
  69. package/dist/lib/one-operations.d.ts +0 -1
  70. package/dist/lib/one-operations.js +0 -1
  71. package/dist/lib/open-url.d.ts +0 -1
  72. package/dist/lib/open-url.js +0 -1
  73. package/dist/lib/platform.d.ts +2 -0
  74. package/dist/lib/platform.js +4 -0
  75. package/dist/lib/progress.d.ts +0 -1
  76. package/dist/lib/progress.js +0 -1
  77. package/dist/lib/security-client.d.ts +1 -2
  78. package/dist/lib/security-client.js +0 -1
  79. package/dist/lib/skill-fetch.d.ts +0 -1
  80. package/dist/lib/skill-fetch.js +0 -1
  81. package/dist/lib/system-trust.d.ts +0 -1
  82. package/dist/lib/system-trust.js +0 -1
  83. package/dist/lib/tui.d.ts +308 -0
  84. package/dist/lib/tui.js +846 -0
  85. package/dist/local/cloudflare-checkup/cf/allowlist.d.ts +94 -0
  86. package/dist/local/cloudflare-checkup/cf/allowlist.js +183 -0
  87. package/dist/local/cloudflare-checkup/cf/auth-lifecycle.d.ts +112 -0
  88. package/dist/local/cloudflare-checkup/cf/auth-lifecycle.js +312 -0
  89. package/dist/local/cloudflare-checkup/cf/child-env.d.ts +31 -0
  90. package/dist/local/cloudflare-checkup/cf/child-env.js +80 -0
  91. package/dist/local/cloudflare-checkup/cf/install.d.ts +105 -0
  92. package/dist/local/cloudflare-checkup/cf/install.js +308 -0
  93. package/dist/local/cloudflare-checkup/cf/locate.d.ts +43 -0
  94. package/dist/local/cloudflare-checkup/cf/locate.js +227 -0
  95. package/dist/local/cloudflare-checkup/cf/run-cf.d.ts +88 -0
  96. package/dist/local/cloudflare-checkup/cf/run-cf.js +274 -0
  97. package/dist/local/cloudflare-checkup/collector.d.ts +47 -0
  98. package/dist/local/cloudflare-checkup/collector.js +295 -0
  99. package/dist/local/cloudflare-checkup/contract.d.ts +2804 -0
  100. package/dist/local/cloudflare-checkup/contract.js +1620 -0
  101. package/dist/local/cloudflare-checkup/email-verification.d.ts +124 -0
  102. package/dist/local/cloudflare-checkup/email-verification.js +211 -0
  103. package/dist/local/cloudflare-checkup/errors.d.ts +29 -0
  104. package/dist/local/cloudflare-checkup/errors.js +103 -0
  105. package/dist/local/cloudflare-checkup/output.d.ts +23 -0
  106. package/dist/local/cloudflare-checkup/output.js +88 -0
  107. package/dist/local/cloudflare-checkup/prompts.d.ts +40 -0
  108. package/dist/local/cloudflare-checkup/prompts.js +139 -0
  109. package/dist/local/cloudflare-checkup/rate-budget.d.ts +41 -0
  110. package/dist/local/cloudflare-checkup/rate-budget.js +79 -0
  111. package/dist/local/cloudflare-checkup/snapshot-file.d.ts +52 -0
  112. package/dist/local/cloudflare-checkup/snapshot-file.js +185 -0
  113. package/dist/local/cloudflare-checkup/submit-client.d.ts +112 -0
  114. package/dist/local/cloudflare-checkup/submit-client.js +395 -0
  115. package/dist/local/cloudflare-checkup/terminal-ui.d.ts +170 -0
  116. package/dist/local/cloudflare-checkup/terminal-ui.js +403 -0
  117. package/dist/types.d.ts +25 -3
  118. package/dist/types.js +16 -1
  119. package/dist/version.d.ts +0 -1
  120. package/dist/version.js +0 -1
  121. package/package.json +12 -12
  122. package/dist/auth/device-auth.d.ts.map +0 -1
  123. package/dist/auth/device-auth.js.map +0 -1
  124. package/dist/auth/index.d.ts.map +0 -1
  125. package/dist/auth/index.js.map +0 -1
  126. package/dist/auth/store.d.ts.map +0 -1
  127. package/dist/auth/store.js.map +0 -1
  128. package/dist/auth/types.d.ts.map +0 -1
  129. package/dist/auth/types.js.map +0 -1
  130. package/dist/bin.d.ts.map +0 -1
  131. package/dist/bin.js.map +0 -1
  132. package/dist/cli.d.ts.map +0 -1
  133. package/dist/cli.js.map +0 -1
  134. package/dist/commands/account.d.ts.map +0 -1
  135. package/dist/commands/account.js.map +0 -1
  136. package/dist/commands/auth.d.ts.map +0 -1
  137. package/dist/commands/auth.js.map +0 -1
  138. package/dist/commands/billing.d.ts.map +0 -1
  139. package/dist/commands/billing.js.map +0 -1
  140. package/dist/commands/cloudflare.d.ts.map +0 -1
  141. package/dist/commands/cloudflare.js.map +0 -1
  142. package/dist/commands/helpers.d.ts.map +0 -1
  143. package/dist/commands/helpers.js.map +0 -1
  144. package/dist/commands/index.d.ts.map +0 -1
  145. package/dist/commands/index.js.map +0 -1
  146. package/dist/commands/legacy-validation.d.ts.map +0 -1
  147. package/dist/commands/legacy-validation.js.map +0 -1
  148. package/dist/commands/one.d.ts.map +0 -1
  149. package/dist/commands/one.js.map +0 -1
  150. package/dist/commands/public-tools.d.ts.map +0 -1
  151. package/dist/commands/public-tools.js.map +0 -1
  152. package/dist/commands/runs.d.ts.map +0 -1
  153. package/dist/commands/runs.js.map +0 -1
  154. package/dist/commands/security.d.ts.map +0 -1
  155. package/dist/commands/security.js.map +0 -1
  156. package/dist/commands/watchdog.d.ts.map +0 -1
  157. package/dist/commands/watchdog.js.map +0 -1
  158. package/dist/index.d.ts.map +0 -1
  159. package/dist/index.js.map +0 -1
  160. package/dist/lib/banner.d.ts.map +0 -1
  161. package/dist/lib/banner.js.map +0 -1
  162. package/dist/lib/client.d.ts.map +0 -1
  163. package/dist/lib/client.js.map +0 -1
  164. package/dist/lib/cloudflare-client.d.ts.map +0 -1
  165. package/dist/lib/cloudflare-client.js.map +0 -1
  166. package/dist/lib/config.d.ts.map +0 -1
  167. package/dist/lib/config.js.map +0 -1
  168. package/dist/lib/format.d.ts.map +0 -1
  169. package/dist/lib/format.js.map +0 -1
  170. package/dist/lib/help.d.ts.map +0 -1
  171. package/dist/lib/help.js.map +0 -1
  172. package/dist/lib/network.d.ts.map +0 -1
  173. package/dist/lib/network.js.map +0 -1
  174. package/dist/lib/one-client.d.ts.map +0 -1
  175. package/dist/lib/one-client.js.map +0 -1
  176. package/dist/lib/one-operations.d.ts.map +0 -1
  177. package/dist/lib/one-operations.js.map +0 -1
  178. package/dist/lib/open-url.d.ts.map +0 -1
  179. package/dist/lib/open-url.js.map +0 -1
  180. package/dist/lib/progress.d.ts.map +0 -1
  181. package/dist/lib/progress.js.map +0 -1
  182. package/dist/lib/security-client.d.ts.map +0 -1
  183. package/dist/lib/security-client.js.map +0 -1
  184. package/dist/lib/skill-fetch.d.ts.map +0 -1
  185. package/dist/lib/skill-fetch.js.map +0 -1
  186. package/dist/lib/system-trust.d.ts.map +0 -1
  187. package/dist/lib/system-trust.js.map +0 -1
  188. package/dist/types.d.ts.map +0 -1
  189. package/dist/types.js.map +0 -1
  190. package/dist/version.d.ts.map +0 -1
  191. package/dist/version.js.map +0 -1
package/dist/cli.js CHANGED
@@ -1,11 +1,13 @@
1
1
  import { parseArgs } from 'node:util';
2
2
  import { DeviceAuthError } from './auth/index.js';
3
3
  import { VigilbaseApiError, VigilbaseClient } from './lib/client.js';
4
- import { createConfig } from './lib/config.js';
4
+ import { createConfig, createLocalCommandConfig } from './lib/config.js';
5
5
  import { renderBanner, shouldShowBanner } from './lib/banner.js';
6
6
  import { createWriter, formatError } from './lib/format.js';
7
7
  import { HELP_TAGLINE, HELP_TEXT, helpForCommand } from './lib/help.js';
8
+ import { wasRendered } from './lib/tui.js';
8
9
  import { getCommandHandler } from './commands/index.js';
10
+ import { LocalCheckupError } from './local/cloudflare-checkup/errors.js';
9
11
  import { EXIT } from './types.js';
10
12
  import { CLI_VERSION } from './version.js';
11
13
  export { CLI_VERSION };
@@ -20,6 +22,7 @@ function parseCli(argv) {
20
22
  version: { type: 'boolean', short: 'V', default: false },
21
23
  'base-url': { type: 'string' },
22
24
  'one-url': { type: 'string' },
25
+ 'auth-url': { type: 'string' },
23
26
  'client-id': { type: 'string' },
24
27
  timeout: { type: 'string' },
25
28
  style: { type: 'string' },
@@ -67,6 +70,14 @@ function parseCli(argv) {
67
70
  'revision': { type: 'string' },
68
71
  all: { type: 'boolean', default: false },
69
72
  light: { type: 'boolean', default: false },
73
+ profile: { type: 'string' },
74
+ 'cf-auth': { type: 'string' },
75
+ 'cf-bin': { type: 'string' },
76
+ out: { type: 'string' },
77
+ overwrite: { type: 'boolean', default: false },
78
+ 'dry-run': { type: 'boolean', default: false },
79
+ 'max-requests-per-5min': { type: 'string' },
80
+ yes: { type: 'boolean', default: false },
70
81
  },
71
82
  });
72
83
  return {
@@ -108,6 +119,7 @@ function parseCli(argv) {
108
119
  isVersion: Boolean(values.version),
109
120
  'base-url': typeof values['base-url'] === 'string' ? values['base-url'] : undefined,
110
121
  'one-url': typeof values['one-url'] === 'string' ? values['one-url'] : undefined,
122
+ 'auth-url': typeof values['auth-url'] === 'string' ? values['auth-url'] : undefined,
111
123
  'client-id': typeof values['client-id'] === 'string' ? values['client-id'] : undefined,
112
124
  timeout: typeof values.timeout === 'string' ? values.timeout : undefined,
113
125
  style: typeof values.style === 'string' ? values.style : undefined,
@@ -126,6 +138,14 @@ function parseCli(argv) {
126
138
  tool: typeof values.tool === 'string' ? values.tool : undefined,
127
139
  limit: typeof values.limit === 'string' ? values.limit : undefined,
128
140
  'target-id': typeof values['target-id'] === 'string' ? values['target-id'] : undefined,
141
+ profile: typeof values.profile === 'string' ? values.profile : undefined,
142
+ 'cf-auth': typeof values['cf-auth'] === 'string' ? values['cf-auth'] : undefined,
143
+ 'cf-bin': typeof values['cf-bin'] === 'string' ? values['cf-bin'] : undefined,
144
+ out: typeof values.out === 'string' ? values.out : undefined,
145
+ isOverwrite: Boolean(values.overwrite),
146
+ isDryRun: Boolean(values['dry-run']),
147
+ 'max-requests-per-5min': typeof values['max-requests-per-5min'] === 'string' ? values['max-requests-per-5min'] : undefined,
148
+ isYes: Boolean(values.yes),
129
149
  },
130
150
  };
131
151
  }
@@ -143,7 +163,8 @@ function resolveMode(jsonFlag) {
143
163
  }
144
164
  export async function runCli(options = {}) {
145
165
  const argv = options.argv ?? process.argv.slice(2);
146
- const writer = createWriter(options.stdout, options.stderr);
166
+ const stdoutStream = options.stdout ?? process.stdout;
167
+ const writer = createWriter(stdoutStream, options.stderr);
147
168
  const previousEnv = process.env;
148
169
  if (options.env) {
149
170
  process.env = options.env;
@@ -152,7 +173,7 @@ export async function runCli(options = {}) {
152
173
  const parsed = parseCli(argv);
153
174
  const mode = resolveMode(parsed.values.isJson);
154
175
  if (parsed.values.isVersion || parsed.values.isHelp || !parsed.positionals.length || parsed.positionals[0] === 'help') {
155
- const informational = new Set(['isJson', 'isHelp', 'isVersion', 'base-url', 'one-url', 'client-id', 'timeout']);
176
+ const informational = new Set(['isJson', 'isHelp', 'isVersion', 'base-url', 'one-url', 'auth-url', 'client-id', 'timeout']);
156
177
  for (const [key, value] of Object.entries(parsed.values)) {
157
178
  if (value === undefined || value === false || informational.has(key))
158
179
  continue;
@@ -168,6 +189,10 @@ export async function runCli(options = {}) {
168
189
  if (mode === 'json') {
169
190
  writer.stdout(JSON.stringify({ name: '@vigilbase/cli', version: CLI_VERSION }, null, 2));
170
191
  }
192
+ else if (!stdoutStream.isTTY) {
193
+ // Pipes and files get only the version, for scripts.
194
+ writer.stdout(CLI_VERSION);
195
+ }
171
196
  else if (shouldShowBanner(mode)) {
172
197
  writer.stdout(renderBanner({ version: CLI_VERSION, tagline: HELP_TAGLINE }));
173
198
  }
@@ -205,12 +230,16 @@ export async function runCli(options = {}) {
205
230
  }
206
231
  return EXIT.OK;
207
232
  }
208
- const config = createConfig({
209
- baseUrl: parsed.values['base-url'],
210
- oneBaseUrl: parsed.values['one-url'],
211
- clientId: parsed.values['client-id'],
212
- timeoutMs: parseTimeout(parsed.values.timeout),
213
- });
233
+ // Local checkups validate --base-url themselves (resolveSubmitOrigin) and ignore VIGILBASE_* URLs.
234
+ const config = command === 'local' || command === 'cloudflare-checkup'
235
+ ? createLocalCommandConfig(parseTimeout(parsed.values.timeout))
236
+ : createConfig({
237
+ baseUrl: parsed.values['base-url'],
238
+ oneBaseUrl: parsed.values['one-url'],
239
+ identityBaseUrl: parsed.values['auth-url'],
240
+ clientId: parsed.values['client-id'],
241
+ timeoutMs: parseTimeout(parsed.values.timeout),
242
+ });
214
243
  const publicClient = new VigilbaseClient(config);
215
244
  const handler = getCommandHandler(command);
216
245
  if (!handler) {
@@ -226,19 +255,21 @@ export async function runCli(options = {}) {
226
255
  publicClient,
227
256
  env: process.env,
228
257
  stdin: options.stdin,
258
+ stdoutStream,
259
+ stderrStream: options.stderr ?? process.stderr,
229
260
  loginHandler: options.loginHandler,
230
261
  });
231
262
  }
232
263
  catch (error) {
233
264
  const mode = argv.includes('--json') || argv.includes('-j') ? 'json' : 'text';
234
- formatError(error, mode, writer);
265
+ // The terminal UI already showed it (■ and the hint) in its timeline.
266
+ if (mode === 'json' || !wasRendered(error))
267
+ formatError(error, mode, writer);
235
268
  if ((error instanceof VigilbaseApiError || error instanceof DeviceAuthError) && error.status === 429) {
236
269
  return EXIT.RATE_LIMITED;
237
270
  }
238
- if (error instanceof DeviceAuthError && error.code === 'not_enabled') {
239
- if (mode !== 'json') {
240
- writer.stderr('Public tools remain available without login: `vigilbase list`, `vigilbase run …`');
241
- }
271
+ if (error instanceof LocalCheckupError && error.isRateLimited) {
272
+ return EXIT.RATE_LIMITED;
242
273
  }
243
274
  return EXIT.ERROR;
244
275
  }
@@ -248,4 +279,3 @@ export async function runCli(options = {}) {
248
279
  }
249
280
  }
250
281
  }
251
- //# sourceMappingURL=cli.js.map
@@ -1,3 +1,2 @@
1
1
  import { type CommandHandler } from './helpers.js';
2
2
  export declare const handleAccount: CommandHandler;
3
- //# sourceMappingURL=account.d.ts.map
@@ -184,4 +184,3 @@ export const handleAccount = async (ctx) => {
184
184
  }
185
185
  throw new Error('Usage: vigilbase account [services|org|endpoints|sessions|invites|billing] [--json]');
186
186
  };
187
- //# sourceMappingURL=account.js.map
@@ -3,4 +3,3 @@ export declare function readTokenFromStdin(input?: NodeJS.ReadableStream): Promi
3
3
  export declare const handleLogin: CommandHandler;
4
4
  export declare const handleLogout: CommandHandler;
5
5
  export declare const handleWhoami: CommandHandler;
6
- //# sourceMappingURL=auth.d.ts.map
@@ -1,4 +1,8 @@
1
- import { clearOneCredentials, getOneAccessToken, readCredentials, requestDeviceCode, waitForDeviceAuthorization, writeOneCredentials, } from '../auth/index.js';
1
+ import { readIdentityTokenMetadata } from '../auth/token-metadata.js';
2
+ import { discoverAuthProvider } from '../auth/provider.js';
3
+ import { revokeIdentityCredentials, revokeIdentityGrant, revokeSavedCredentials, withCredentialsLock } from '../auth/refresh.js';
4
+ import { checkCredentialsLocation, clearIdentityCredentials, clearOneCredentials, hasIgnoredIdentityCredentials, readReplacedIdentityGrant, readStoredCredentials } from '../auth/store.js';
5
+ import { getOneAccessToken, readCredentials, requestDeviceCode, waitForDeviceAuthorization, writeOneCredentials, } from '../auth/index.js';
2
6
  import { VigilbaseOneClient, ONE_AUTH_REQUIRED_TOOLS } from '../lib/one-client.js';
3
7
  import { VigilbaseApiError } from '../lib/client.js';
4
8
  import { formatUserCode, isHeadlessEnvironment, openUrlInBrowser, resolveDeviceVerificationUrl, } from '../lib/open-url.js';
@@ -19,6 +23,30 @@ export async function readTokenFromStdin(input = process.stdin) {
19
23
  throw new Error('Token input must contain exactly one nonempty token.');
20
24
  return token;
21
25
  }
26
+ /**
27
+ * Revoke the Identity grant a new login replaces, before the new grant exists. Identity treats a revoked refresh
28
+ * token presented again as reuse and ends every CLI grant of the user, so revoking after the new grant was issued
29
+ * could end it too. The lock is held from the read to the removal of the old file, so no concurrent command can
30
+ * refresh with the token in between, and no later command presents it again.
31
+ */
32
+ async function retirePreviousIdentityGrant(ctx) {
33
+ await withCredentialsLock(ctx.config, async () => {
34
+ const previous = await readReplacedIdentityGrant(ctx.config);
35
+ // After a refresh with an uncertain result the token may be rotated already; presenting it trips the same check.
36
+ if (!previous || previous.refreshPending)
37
+ return;
38
+ try {
39
+ await revokeIdentityGrant(ctx.config, previous);
40
+ }
41
+ catch {
42
+ ctx.writer.stderr("Warning: Couldn't revoke your previous sign-in; it will expire on its own.");
43
+ return;
44
+ }
45
+ // The grant came from identity-credentials.json, or from a CLI 0.2.0 credentials.json when that file is absent.
46
+ const { identity } = await readStoredCredentials(ctx.config, () => { });
47
+ await (identity ? clearIdentityCredentials(ctx.config) : clearOneCredentials(ctx.config));
48
+ }, true);
49
+ }
22
50
  export const handleLogin = async (ctx) => {
23
51
  if (ctx.rest.length)
24
52
  throw new Error('Usage: vigilbase login [--token-stdin | --token <bearer>] [--no-open]');
@@ -26,6 +54,9 @@ export const handleLogin = async (ctx) => {
26
54
  if (ctx.loginHandler) {
27
55
  return ctx.loginHandler(ctx.config);
28
56
  }
57
+ // Fail on Windows or an unsafe credentials directory or file before validating a token or starting device
58
+ // authorization: once a device grant is approved, a failed save would leave it unsaved and unrevoked.
59
+ await checkCredentialsLocation(ctx.config);
29
60
  if (ctx.values.isTokenStdin && (ctx.values.token !== undefined || ctx.env.VIGILBASE_ONE_TOKEN !== undefined)) {
30
61
  throw new Error('Use --token-stdin without --token or VIGILBASE_ONE_TOKEN.');
31
62
  }
@@ -47,15 +78,19 @@ export const handleLogin = async (ctx) => {
47
78
  email: session.user.email ?? undefined,
48
79
  name: session.user.name ?? undefined,
49
80
  };
50
- await writeOneCredentials(ctx.config, {
51
- accessToken: explicitToken,
52
- tokenType: 'Bearer',
53
- expiresAt: typeof session.session?.expiresAt === 'string'
54
- ? session.session.expiresAt
55
- : undefined,
56
- user,
57
- obtainedAt: new Date().toISOString(),
58
- });
81
+ await withCredentialsLock(ctx.config, async () => {
82
+ // Identity credentials win when tokens are read, so revoke them before saving this session.
83
+ await revokeIdentityCredentials(ctx.config);
84
+ await writeOneCredentials(ctx.config, {
85
+ accessToken: explicitToken,
86
+ tokenType: 'Bearer',
87
+ expiresAt: typeof session.session?.expiresAt === 'string'
88
+ ? session.session.expiresAt
89
+ : undefined,
90
+ user,
91
+ obtainedAt: new Date().toISOString(),
92
+ });
93
+ }, true);
59
94
  if (ctx.mode === 'json') {
60
95
  outputJson(ctx, {
61
96
  success: true,
@@ -72,8 +107,11 @@ export const handleLogin = async (ctx) => {
72
107
  }
73
108
  const preferNoOpen = Boolean(ctx.values.isNoOpen) || isHeadlessEnvironment(ctx.env);
74
109
  ctx.writer.stderr(`login ${ctx.config.oneBaseUrl}`);
110
+ ctx.config.authProvider = await discoverAuthProvider(ctx.config);
111
+ if (ctx.config.authProvider)
112
+ await retirePreviousIdentityGrant(ctx);
75
113
  const device = await withSpinner(ctx, 'Requesting device code', () => requestDeviceCode(ctx.config));
76
- const verifyUrl = resolveDeviceVerificationUrl(ctx.config.oneBaseUrl, device);
114
+ const verifyUrl = resolveDeviceVerificationUrl(ctx.config.authProvider?.issuer ?? ctx.config.oneBaseUrl, device);
77
115
  const prettyCode = formatUserCode(device.user_code);
78
116
  ctx.writer.stderr(`code ${prettyCode}`);
79
117
  ctx.writer.stderr(`url ${verifyUrl}`);
@@ -124,14 +162,23 @@ export const handleLogin = async (ctx) => {
124
162
  catch {
125
163
  // Session enrichment is best-effort.
126
164
  }
127
- await writeOneCredentials(ctx.config, {
128
- accessToken: token.access_token,
129
- tokenType: token.token_type,
130
- scope: token.scope,
131
- expiresAt,
132
- user,
133
- obtainedAt: new Date().toISOString(),
134
- });
165
+ if (ctx.config.authProvider && (!token.refresh_token || !expiresAt || !token.expires_in || token.expires_in > 300))
166
+ throw new Error('Identity returned an invalid token grant.');
167
+ const identityMetadata = ctx.config.authProvider ? readIdentityTokenMetadata(token.access_token, { issuer: ctx.config.authProvider.issuer, clientId: ctx.config.clientId, resource: ctx.config.authProvider.resource }) : undefined;
168
+ await withCredentialsLock(ctx.config, async () => {
169
+ // A legacy session is saved in credentials.json, which saved Identity credentials would override.
170
+ if (!ctx.config.authProvider)
171
+ await revokeIdentityCredentials(ctx.config);
172
+ await writeOneCredentials(ctx.config, {
173
+ ...(ctx.config.authProvider ? { issuer: ctx.config.authProvider.issuer, clientId: ctx.config.clientId, resource: ctx.config.authProvider.resource, refreshToken: token.refresh_token, orgId: identityMetadata?.organizationId } : {}),
174
+ accessToken: token.access_token,
175
+ tokenType: token.token_type,
176
+ scope: token.scope,
177
+ expiresAt,
178
+ user,
179
+ obtainedAt: new Date().toISOString(),
180
+ });
181
+ }, true);
135
182
  if (ctx.mode === 'json') {
136
183
  outputJson(ctx, {
137
184
  success: true,
@@ -152,7 +199,10 @@ export const handleLogout = async (ctx) => {
152
199
  if (ctx.rest.length)
153
200
  throw new Error('Usage: vigilbase logout');
154
201
  assertCommandFlags(ctx, []);
155
- const cleared = await clearOneCredentials(ctx.config);
202
+ const cleared = await revokeSavedCredentials(ctx.config, (warning) => {
203
+ if (ctx.mode !== 'json')
204
+ ctx.writer.stderr(`Warning: ${warning}`);
205
+ });
156
206
  if (ctx.mode === 'json') {
157
207
  outputJson(ctx, { success: true, cleared });
158
208
  }
@@ -169,11 +219,12 @@ export const handleWhoami = async (ctx) => {
169
219
  throw new Error('Usage: vigilbase whoami');
170
220
  assertCommandFlags(ctx, []);
171
221
  const credentials = await readCredentials(ctx.config);
222
+ const legacyIdentityFileIgnored = await hasIgnoredIdentityCredentials(ctx.config);
172
223
  const accessToken = await getOneAccessToken(ctx.config);
173
224
  let liveUser = null;
174
225
  if (accessToken) {
175
226
  try {
176
- const session = await withSpinner(ctx, 'Loading session', () => new VigilbaseOneClient(ctx.config, accessToken).getSession());
227
+ const session = await withSpinner(ctx, 'Loading session', () => new VigilbaseOneClient(ctx.config, accessToken, true).getSession());
177
228
  if (session.user) {
178
229
  liveUser = {
179
230
  id: session.user.id,
@@ -194,12 +245,19 @@ export const handleWhoami = async (ctx) => {
194
245
  const payload = {
195
246
  authenticated: signedIn,
196
247
  oneBaseUrl: ctx.config.oneBaseUrl,
248
+ issuer: credentials.one?.issuer ?? null,
249
+ clientId: credentials.one?.clientId ?? null,
250
+ resource: credentials.one?.resource ?? null,
251
+ organizationId: credentials.one?.orgId ?? null,
197
252
  user: liveUser,
198
253
  expiresAt: signedIn ? credentials.one?.expiresAt ?? null : null,
199
254
  scope: signedIn ? credentials.one?.scope ?? null : null,
200
255
  obtainedAt: signedIn ? credentials.one?.obtainedAt ?? null : null,
201
256
  authRequiredTools: signedIn ? [...ONE_AUTH_REQUIRED_TOOLS] : [],
257
+ legacyIdentityFileIgnored,
202
258
  };
259
+ // CLI 0.2.0 can save a second Identity grant in credentials.json; logout revokes it.
260
+ const legacyNote = 'Note: found a credentials file from CLI 0.2.0. `vigilbase logout` will revoke and remove it.';
203
261
  if (ctx.mode === 'json') {
204
262
  outputJson(ctx, payload);
205
263
  }
@@ -207,6 +265,7 @@ export const handleWhoami = async (ctx) => {
207
265
  outputLines(ctx, [
208
266
  'Not signed in. Public tools still work.',
209
267
  'Run `vigilbase login` to unlock auth-required tools (website-security, react2shell, fortibleed-check).',
268
+ ...(legacyIdentityFileIgnored ? [legacyNote] : []),
210
269
  ]);
211
270
  }
212
271
  else {
@@ -217,8 +276,9 @@ export const handleWhoami = async (ctx) => {
217
276
  lines.push(`Expires: ${credentials.one.expiresAt}`);
218
277
  }
219
278
  lines.push(`Auth-required tools: ${ONE_AUTH_REQUIRED_TOOLS.join(', ')}`);
279
+ if (legacyIdentityFileIgnored)
280
+ lines.push(legacyNote);
220
281
  outputLines(ctx, lines);
221
282
  }
222
283
  return ok();
223
284
  };
224
- //# sourceMappingURL=auth.js.map
@@ -1,3 +1,2 @@
1
1
  import { type CommandHandler } from './helpers.js';
2
2
  export declare const handleBilling: CommandHandler;
3
- //# sourceMappingURL=billing.d.ts.map
@@ -48,4 +48,3 @@ export const handleBilling = async (ctx) => {
48
48
  }
49
49
  throw new Error('Usage: vigilbase billing [summary|portal] [--json] [--no-open]');
50
50
  };
51
- //# sourceMappingURL=billing.js.map
@@ -1,4 +1,3 @@
1
1
  import { type CommandHandler } from './helpers.js';
2
- export declare const CLOUDFLARE_HELP = "Usage: vigilbase cf <command> [options]\nAlias: vigilbase cloudflare\n\nInventory and workspace:\n zones List zones (default); --customer-id, --account-id, --zone-id\n zone <zone-id> Zone detail; --customer-id selects live details, optional --account-id\n accounts List accessible accounts; --customer-id, --account-id\n account <account-id> Stored account detail; --customer-id\n workspace [section] Workspace bootstrap or section; --customer-id, --account-id, --zone-id\n Sections: overview, reports, zones, rule-analyzer, accounts, checkups, settings\n\nZone reads (optional --customer-id; IDs can use --zone-id):\n dns <zone-id> DNS records, TTL and proxy state\n waf <zone-id> WAF rules, expressions, analytics and suggestions\n speed-settings <zone-id> Performance settings (internal users only)\n\nAccount reads (optional --customer-id; IDs can use --account-id):\n audit-logs <account-id> Latest provider audit events (up to 100; service-read permission)\n zero-trust <account-id> Users, Access applications, tunnels and authentication analytics\n developer-platform <account-id> Workers, Workers analytics and Pages\n magic-transit <account-id> Prefixes, routes and traffic analytics\n Zero Trust, Developer Platform and Magic Transit require deployment-read permission.\n\nAnalytics and checkups:\n analytics --customer-id <uuid> [--account-id <id> | --zone-id <id>] [--days 1..30]\n Default 7 days; live zone analytics uses the most recent 23 hours.\n checkup [status] Latest completed checkup; --customer-id, --account-id\n checkup start Start and persist a scan; --customer-id, --account-id\n checkup progress <run-uuid> Read saved progress once\n checkup result <run-uuid> Read the stored Cloudflare checkup result\n checkups aliases checkup; list/show alias status; run aliases start.\n\nCommon options: --json, --one-url <origin>, --timeout <milliseconds>, --help\nCustomer IDs are UUIDs; account and zone IDs are 32 hexadecimal characters.\nOne login, tenant bindings and permissions apply to every command.\nCheckup start creates a scan run. These commands do not edit provider configuration.\nSee CLOUDFLARE.md for full grammar, API mapping, data limits and examples.";
2
+ export declare const CLOUDFLARE_HELP = "Usage: vigilbase cf <command> [options]\nAlias: vigilbase cloudflare\n\nInventory and workspace:\n zones List zones (default); --customer-id, --account-id, --zone-id\n zone <zone-id> Zone detail; --customer-id selects live details, optional --account-id\n accounts List accessible accounts; --customer-id, --account-id\n account <account-id> Stored account detail; --customer-id\n workspace [section] Workspace bootstrap or section; --customer-id, --account-id, --zone-id\n Sections: overview, reports, zones, rule-analyzer, accounts, checkups, settings\n\nZone reads (optional --customer-id; IDs can use --zone-id):\n dns <zone-id> DNS records, TTL and proxy state\n waf <zone-id> WAF rules, expressions, analytics and suggestions\n speed-settings <zone-id> Performance settings (internal users only)\n\nAccount reads (optional --customer-id; IDs can use --account-id):\n audit-logs <account-id> Latest provider audit events (up to 100; service-read permission)\n zero-trust <account-id> Users, Access applications, tunnels and authentication analytics\n developer-platform <account-id>\n Workers, Workers analytics and Pages\n magic-transit <account-id> Prefixes, routes and traffic analytics\n Zero Trust, Developer Platform and Magic Transit require deployment-read permission.\n\nAnalytics and checkups:\n analytics --customer-id <uuid> [--account-id <id> | --zone-id <id>] [--days 1..30]\n Default 7 days; live zone analytics uses the most recent 23 hours.\n checkup [status] Latest completed checkup; --customer-id, --account-id\n checkup start Start and persist a scan; --customer-id, --account-id\n checkup progress <run-uuid> Read saved progress once\n checkup result <run-uuid> Read the stored Cloudflare checkup result\n checkups aliases checkup; list/show alias status; run aliases start.\n\nCommon options: --json, --one-url <origin>, --timeout <milliseconds>, --help\nCustomer IDs are UUIDs; account and zone IDs are 32 hexadecimal characters.\nOne login, tenant bindings and permissions apply to every command.\nCheckup start creates a scan run. These commands do not edit provider configuration.\nSee CLOUDFLARE.md for full grammar, API mapping, data limits and examples.";
3
3
  export declare const handleCloudflare: CommandHandler;
4
- //# sourceMappingURL=cloudflare.d.ts.map
@@ -9,9 +9,9 @@ Alias: vigilbase cloudflare
9
9
  Inventory and workspace:
10
10
  zones List zones (default); --customer-id, --account-id, --zone-id
11
11
  zone <zone-id> Zone detail; --customer-id selects live details, optional --account-id
12
- accounts List accessible accounts; --customer-id, --account-id
12
+ accounts List accessible accounts; --customer-id, --account-id
13
13
  account <account-id> Stored account detail; --customer-id
14
- workspace [section] Workspace bootstrap or section; --customer-id, --account-id, --zone-id
14
+ workspace [section] Workspace bootstrap or section; --customer-id, --account-id, --zone-id
15
15
  Sections: overview, reports, zones, rule-analyzer, accounts, checkups, settings
16
16
 
17
17
  Zone reads (optional --customer-id; IDs can use --zone-id):
@@ -22,15 +22,16 @@ Zone reads (optional --customer-id; IDs can use --zone-id):
22
22
  Account reads (optional --customer-id; IDs can use --account-id):
23
23
  audit-logs <account-id> Latest provider audit events (up to 100; service-read permission)
24
24
  zero-trust <account-id> Users, Access applications, tunnels and authentication analytics
25
- developer-platform <account-id> Workers, Workers analytics and Pages
25
+ developer-platform <account-id>
26
+ Workers, Workers analytics and Pages
26
27
  magic-transit <account-id> Prefixes, routes and traffic analytics
27
28
  Zero Trust, Developer Platform and Magic Transit require deployment-read permission.
28
29
 
29
30
  Analytics and checkups:
30
31
  analytics --customer-id <uuid> [--account-id <id> | --zone-id <id>] [--days 1..30]
31
32
  Default 7 days; live zone analytics uses the most recent 23 hours.
32
- checkup [status] Latest completed checkup; --customer-id, --account-id
33
- checkup start Start and persist a scan; --customer-id, --account-id
33
+ checkup [status] Latest completed checkup; --customer-id, --account-id
34
+ checkup start Start and persist a scan; --customer-id, --account-id
34
35
  checkup progress <run-uuid> Read saved progress once
35
36
  checkup result <run-uuid> Read the stored Cloudflare checkup result
36
37
  checkups aliases checkup; list/show alias status; run aliases start.
@@ -338,4 +339,3 @@ export const handleCloudflare = async (ctx) => {
338
339
  fields(ctx, report);
339
340
  });
340
341
  };
341
- //# sourceMappingURL=cloudflare.js.map
@@ -10,6 +10,7 @@ export type ParsedCliValues = {
10
10
  isVersion?: boolean;
11
11
  'base-url'?: string;
12
12
  'one-url'?: string;
13
+ 'auth-url'?: string;
13
14
  'client-id'?: string;
14
15
  timeout?: string;
15
16
  style?: string;
@@ -57,6 +58,14 @@ export type ParsedCliValues = {
57
58
  'revision'?: string;
58
59
  isAll?: boolean;
59
60
  isLight?: boolean;
61
+ profile?: string;
62
+ 'cf-auth'?: string;
63
+ 'cf-bin'?: string;
64
+ out?: string;
65
+ isOverwrite?: boolean;
66
+ isDryRun?: boolean;
67
+ 'max-requests-per-5min'?: string;
68
+ isYes?: boolean;
60
69
  };
61
70
  export type CommandContext = {
62
71
  command: string;
@@ -68,6 +77,12 @@ export type CommandContext = {
68
77
  publicClient: VigilbaseClient;
69
78
  env: NodeJS.ProcessEnv;
70
79
  stdin?: NodeJS.ReadableStream;
80
+ /** Raw stdout, only to tell whether it is a terminal; text output still goes through `writer`. */
81
+ stdoutStream?: NodeJS.WritableStream & {
82
+ isTTY?: boolean;
83
+ };
84
+ /** Raw stderr for prompts that must not end in a newline; text output still goes through `writer`. */
85
+ stderrStream?: NodeJS.WritableStream;
71
86
  loginHandler?: (config: CliConfigLegacy) => Promise<ExitCode>;
72
87
  };
73
88
  export type CommandHandler = (ctx: CommandContext) => Promise<ExitCode>;
@@ -81,4 +96,3 @@ export declare function requireOneClient(ctx: CommandContext): Promise<Vigilbase
81
96
  export declare function withSpinner<T>(ctx: CommandContext, label: string, work: () => Promise<T>): Promise<T>;
82
97
  export declare function ok(): ExitCode;
83
98
  export { EXIT };
84
- //# sourceMappingURL=helpers.d.ts.map
@@ -5,7 +5,7 @@ import { getOneAccessToken } from '../auth/index.js';
5
5
  import { VigilbaseOneClient as OneClient } from '../lib/one-client.js';
6
6
  /** Reject known flags from other command groups instead of silently ignoring filters. */
7
7
  export function assertCommandFlags(ctx, allowed) {
8
- const common = new Set(['isJson', 'isHelp', 'isVersion', 'base-url', 'one-url', 'client-id', 'timeout']);
8
+ const common = new Set(['isJson', 'isHelp', 'isVersion', 'base-url', 'one-url', 'auth-url', 'client-id', 'timeout']);
9
9
  for (const [key, value] of Object.entries(ctx.values)) {
10
10
  if (value === undefined || value === false || common.has(key) || allowed.includes(key))
11
11
  continue;
@@ -36,7 +36,7 @@ export async function requireOneClient(ctx) {
36
36
  if (!accessToken) {
37
37
  throw new Error('This command requires Vigilbase One auth. Run `vigilbase login` first.');
38
38
  }
39
- return new OneClient(ctx.config, accessToken);
39
+ return new OneClient(ctx.config, accessToken, true);
40
40
  }
41
41
  export async function withSpinner(ctx, label, work) {
42
42
  return withProgress(label, ctx.mode, work);
@@ -45,4 +45,3 @@ export function ok() {
45
45
  return EXIT.OK;
46
46
  }
47
47
  export { EXIT };
48
- //# sourceMappingURL=helpers.js.map
@@ -2,8 +2,9 @@ import type { CommandHandler } from './helpers.js';
2
2
  /**
3
3
  * Modular command registry.
4
4
  * Public tool commands never require auth. Auth-gated commands live under
5
- * account / runs / cf / watchdog / billing / login.
5
+ * account / runs / cf / watchdog / billing / login. `local` runs on this
6
+ * machine with the user's own tools (Cloudflare's cf) and needs no login;
7
+ * `cloudflare-checkup` is the top-level alias of `local cloudflare-checkup`.
6
8
  */
7
9
  export declare const COMMAND_HANDLERS: Record<string, CommandHandler>;
8
10
  export declare function getCommandHandler(command: string): CommandHandler | null;
9
- //# sourceMappingURL=index.d.ts.map
@@ -2,6 +2,7 @@ import { handleLogin, handleLogout, handleWhoami } from './auth.js';
2
2
  import { handleAccount } from './account.js';
3
3
  import { handleBilling } from './billing.js';
4
4
  import { handleCloudflare } from './cloudflare.js';
5
+ import { handleCloudflareCheckup, handleLocal } from './local.js';
5
6
  import { handleBadge, handleDescribe, handleDoctor, handleList, handleRun, handleSkill, } from './public-tools.js';
6
7
  import { handleRuns } from './runs.js';
7
8
  import { handleOne } from './one.js';
@@ -10,7 +11,9 @@ import { handleWatchdog } from './watchdog.js';
10
11
  /**
11
12
  * Modular command registry.
12
13
  * Public tool commands never require auth. Auth-gated commands live under
13
- * account / runs / cf / watchdog / billing / login.
14
+ * account / runs / cf / watchdog / billing / login. `local` runs on this
15
+ * machine with the user's own tools (Cloudflare's cf) and needs no login;
16
+ * `cloudflare-checkup` is the top-level alias of `local cloudflare-checkup`.
14
17
  */
15
18
  export const COMMAND_HANDLERS = {
16
19
  list: handleList,
@@ -37,8 +40,9 @@ export const COMMAND_HANDLERS = {
37
40
  monitoring: handleOne,
38
41
  support: handleOne,
39
42
  billing: handleBilling,
43
+ local: handleLocal,
44
+ 'cloudflare-checkup': handleCloudflareCheckup,
40
45
  };
41
46
  export function getCommandHandler(command) {
42
47
  return COMMAND_HANDLERS[command] ?? null;
43
48
  }
44
- //# sourceMappingURL=index.js.map
@@ -18,4 +18,3 @@ export declare const invitationSchema: z.ZodObject<{
18
18
  }>>;
19
19
  }, z.core.$strip>;
20
20
  export declare const toolNameSchema: z.ZodString;
21
- //# sourceMappingURL=legacy-validation.d.ts.map
@@ -17,4 +17,3 @@ export const invitationSchema = z.object({
17
17
  role: z.enum(['admin', 'member', 'readonly', 'partner_owner', 'partner_sales', 'partner_operations', 'partner_finance', 'partner_readonly', 'billing_only', 'sales_only']).default('member'),
18
18
  });
19
19
  export const toolNameSchema = z.string().trim().min(1).max(128).regex(/^[a-z0-9-]+$/, 'Tool names must contain lowercase letters, digits, or hyphens.');
20
- //# sourceMappingURL=legacy-validation.js.map
@@ -0,0 +1,35 @@
1
+ import { type CommandHandler } from './helpers.js';
2
+ /**
3
+ * How the page, the docs and the hints spell the command: the bare package,
4
+ * no version. vigilbase.com refuses CLIs older than it accepts, and that
5
+ * error's hint (CLI_UPGRADE_REQUIRED) names `@latest` to get past a cached copy.
6
+ */
7
+ export declare const NPX_COMMAND = "npx @vigilbase/cli";
8
+ export declare const LOCAL_HELP: string;
9
+ /** `vigilbase local cloudflare-checkup [<command>] …` */
10
+ export declare const handleLocal: CommandHandler;
11
+ /** `vigilbase cloudflare-checkup …`: the top-level alias. */
12
+ export declare const handleCloudflareCheckup: CommandHandler;
13
+ /**
14
+ * A path as one shell word, for hints the user may copy. POSIX shells get
15
+ * single quotes (nothing inside is expanded), with a leading `~/` left outside
16
+ * them so the shell still expands it.
17
+ */
18
+ export declare function shellArg(value: string, platform?: NodeJS.Platform): string;
19
+ /**
20
+ * `~/…` for paths under the home directory. Not on Windows: cmd and Windows
21
+ * PowerShell do not expand `~` for native commands, and the printed path is
22
+ * what users pass to `submit`.
23
+ */
24
+ export declare function displayPath(path: string, env?: NodeJS.ProcessEnv, platform?: NodeJS.Platform): string;
25
+ /**
26
+ * `cloudflare-checkup-<first 8 of the account ID>-<UTC yyyymmddThhmmssZ>.json`;
27
+ * `-2`, `-3`, … only when a run in the same second already used the name.
28
+ */
29
+ export declare function defaultSnapshotName(accountId: string, at: Date, attempt?: number): string;
30
+ /**
31
+ * `submit` for a saved file. A loopback origin is named with `--base-url`, so a
32
+ * development run never suggests an upload to production; production is the
33
+ * CLI's default and is not named.
34
+ */
35
+ export declare function submitCommand(path: string, origin: string): string;