@vigilbase/cli 0.2.0 → 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 (233) hide show
  1. package/CLOUDFLARE.md +2 -5
  2. package/COMMANDS.md +150 -112
  3. package/LOCAL.md +73 -83
  4. package/ONE.md +2 -4
  5. package/README.md +337 -171
  6. package/SECURITY.md +2 -2
  7. package/dist/auth/device-auth.d.ts +0 -1
  8. package/dist/auth/device-auth.js +6 -5
  9. package/dist/auth/index.d.ts +1 -2
  10. package/dist/auth/index.js +1 -2
  11. package/dist/auth/provider.d.ts +1 -1
  12. package/dist/auth/provider.js +16 -7
  13. package/dist/auth/refresh.d.ts +23 -4
  14. package/dist/auth/refresh.js +78 -22
  15. package/dist/auth/store.d.ts +78 -4
  16. package/dist/auth/store.js +385 -74
  17. package/dist/auth/token-metadata.d.ts +0 -1
  18. package/dist/auth/token-metadata.js +0 -1
  19. package/dist/auth/types.d.ts +0 -1
  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 +7 -8
  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 +69 -21
  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 +0 -1
  34. package/dist/commands/helpers.js +0 -1
  35. package/dist/commands/index.d.ts +0 -1
  36. package/dist/commands/index.js +0 -1
  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 +6 -1
  40. package/dist/commands/local.js +34 -11
  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 +52 -18
  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 +2 -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 +16 -3
  60. package/dist/lib/config.js +41 -14
  61. package/dist/lib/format.d.ts +2 -1
  62. package/dist/lib/format.js +19 -8
  63. package/dist/lib/help.d.ts +1 -2
  64. package/dist/lib/help.js +29 -18
  65. package/dist/lib/network.d.ts +10 -1
  66. package/dist/lib/network.js +31 -4
  67. package/dist/lib/one-client.d.ts +2 -1
  68. package/dist/lib/one-client.js +3 -1
  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 +0 -1
  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 +0 -1
  84. package/dist/lib/tui.js +0 -1
  85. package/dist/local/cloudflare-checkup/cf/allowlist.d.ts +0 -1
  86. package/dist/local/cloudflare-checkup/cf/allowlist.js +0 -1
  87. package/dist/local/cloudflare-checkup/cf/auth-lifecycle.d.ts +0 -1
  88. package/dist/local/cloudflare-checkup/cf/auth-lifecycle.js +0 -1
  89. package/dist/local/cloudflare-checkup/cf/child-env.d.ts +0 -1
  90. package/dist/local/cloudflare-checkup/cf/child-env.js +0 -1
  91. package/dist/local/cloudflare-checkup/cf/install.d.ts +0 -1
  92. package/dist/local/cloudflare-checkup/cf/install.js +0 -1
  93. package/dist/local/cloudflare-checkup/cf/locate.d.ts +0 -1
  94. package/dist/local/cloudflare-checkup/cf/locate.js +0 -1
  95. package/dist/local/cloudflare-checkup/cf/run-cf.d.ts +0 -1
  96. package/dist/local/cloudflare-checkup/cf/run-cf.js +0 -1
  97. package/dist/local/cloudflare-checkup/collector.d.ts +0 -1
  98. package/dist/local/cloudflare-checkup/collector.js +0 -1
  99. package/dist/local/cloudflare-checkup/contract.d.ts +14 -3
  100. package/dist/local/cloudflare-checkup/contract.js +9 -3
  101. package/dist/local/cloudflare-checkup/email-verification.d.ts +0 -1
  102. package/dist/local/cloudflare-checkup/email-verification.js +0 -1
  103. package/dist/local/cloudflare-checkup/errors.d.ts +2 -1
  104. package/dist/local/cloudflare-checkup/errors.js +6 -2
  105. package/dist/local/cloudflare-checkup/output.d.ts +0 -1
  106. package/dist/local/cloudflare-checkup/output.js +0 -1
  107. package/dist/local/cloudflare-checkup/prompts.d.ts +0 -1
  108. package/dist/local/cloudflare-checkup/prompts.js +0 -1
  109. package/dist/local/cloudflare-checkup/rate-budget.d.ts +0 -1
  110. package/dist/local/cloudflare-checkup/rate-budget.js +0 -1
  111. package/dist/local/cloudflare-checkup/snapshot-file.d.ts +0 -1
  112. package/dist/local/cloudflare-checkup/snapshot-file.js +0 -1
  113. package/dist/local/cloudflare-checkup/submit-client.d.ts +19 -5
  114. package/dist/local/cloudflare-checkup/submit-client.js +63 -17
  115. package/dist/local/cloudflare-checkup/terminal-ui.d.ts +0 -1
  116. package/dist/local/cloudflare-checkup/terminal-ui.js +0 -1
  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 +5 -9
  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/provider.d.ts.map +0 -1
  127. package/dist/auth/provider.js.map +0 -1
  128. package/dist/auth/refresh.d.ts.map +0 -1
  129. package/dist/auth/refresh.js.map +0 -1
  130. package/dist/auth/store.d.ts.map +0 -1
  131. package/dist/auth/store.js.map +0 -1
  132. package/dist/auth/token-metadata.d.ts.map +0 -1
  133. package/dist/auth/token-metadata.js.map +0 -1
  134. package/dist/auth/types.d.ts.map +0 -1
  135. package/dist/auth/types.js.map +0 -1
  136. package/dist/bin.d.ts.map +0 -1
  137. package/dist/bin.js.map +0 -1
  138. package/dist/cli.d.ts.map +0 -1
  139. package/dist/cli.js.map +0 -1
  140. package/dist/commands/account.d.ts.map +0 -1
  141. package/dist/commands/account.js.map +0 -1
  142. package/dist/commands/auth.d.ts.map +0 -1
  143. package/dist/commands/auth.js.map +0 -1
  144. package/dist/commands/billing.d.ts.map +0 -1
  145. package/dist/commands/billing.js.map +0 -1
  146. package/dist/commands/cloudflare.d.ts.map +0 -1
  147. package/dist/commands/cloudflare.js.map +0 -1
  148. package/dist/commands/helpers.d.ts.map +0 -1
  149. package/dist/commands/helpers.js.map +0 -1
  150. package/dist/commands/index.d.ts.map +0 -1
  151. package/dist/commands/index.js.map +0 -1
  152. package/dist/commands/legacy-validation.d.ts.map +0 -1
  153. package/dist/commands/legacy-validation.js.map +0 -1
  154. package/dist/commands/local.d.ts.map +0 -1
  155. package/dist/commands/local.js.map +0 -1
  156. package/dist/commands/one.d.ts.map +0 -1
  157. package/dist/commands/one.js.map +0 -1
  158. package/dist/commands/public-tools.d.ts.map +0 -1
  159. package/dist/commands/public-tools.js.map +0 -1
  160. package/dist/commands/runs.d.ts.map +0 -1
  161. package/dist/commands/runs.js.map +0 -1
  162. package/dist/commands/security.d.ts.map +0 -1
  163. package/dist/commands/security.js.map +0 -1
  164. package/dist/commands/watchdog.d.ts.map +0 -1
  165. package/dist/commands/watchdog.js.map +0 -1
  166. package/dist/index.d.ts.map +0 -1
  167. package/dist/index.js.map +0 -1
  168. package/dist/lib/banner.d.ts.map +0 -1
  169. package/dist/lib/banner.js.map +0 -1
  170. package/dist/lib/client.d.ts.map +0 -1
  171. package/dist/lib/client.js.map +0 -1
  172. package/dist/lib/cloudflare-client.d.ts.map +0 -1
  173. package/dist/lib/cloudflare-client.js.map +0 -1
  174. package/dist/lib/config.d.ts.map +0 -1
  175. package/dist/lib/config.js.map +0 -1
  176. package/dist/lib/format.d.ts.map +0 -1
  177. package/dist/lib/format.js.map +0 -1
  178. package/dist/lib/help.d.ts.map +0 -1
  179. package/dist/lib/help.js.map +0 -1
  180. package/dist/lib/network.d.ts.map +0 -1
  181. package/dist/lib/network.js.map +0 -1
  182. package/dist/lib/one-client.d.ts.map +0 -1
  183. package/dist/lib/one-client.js.map +0 -1
  184. package/dist/lib/one-operations.d.ts.map +0 -1
  185. package/dist/lib/one-operations.js.map +0 -1
  186. package/dist/lib/open-url.d.ts.map +0 -1
  187. package/dist/lib/open-url.js.map +0 -1
  188. package/dist/lib/progress.d.ts.map +0 -1
  189. package/dist/lib/progress.js.map +0 -1
  190. package/dist/lib/security-client.d.ts.map +0 -1
  191. package/dist/lib/security-client.js.map +0 -1
  192. package/dist/lib/skill-fetch.d.ts.map +0 -1
  193. package/dist/lib/skill-fetch.js.map +0 -1
  194. package/dist/lib/system-trust.d.ts.map +0 -1
  195. package/dist/lib/system-trust.js.map +0 -1
  196. package/dist/lib/tui.d.ts.map +0 -1
  197. package/dist/lib/tui.js.map +0 -1
  198. package/dist/local/cloudflare-checkup/cf/allowlist.d.ts.map +0 -1
  199. package/dist/local/cloudflare-checkup/cf/allowlist.js.map +0 -1
  200. package/dist/local/cloudflare-checkup/cf/auth-lifecycle.d.ts.map +0 -1
  201. package/dist/local/cloudflare-checkup/cf/auth-lifecycle.js.map +0 -1
  202. package/dist/local/cloudflare-checkup/cf/child-env.d.ts.map +0 -1
  203. package/dist/local/cloudflare-checkup/cf/child-env.js.map +0 -1
  204. package/dist/local/cloudflare-checkup/cf/install.d.ts.map +0 -1
  205. package/dist/local/cloudflare-checkup/cf/install.js.map +0 -1
  206. package/dist/local/cloudflare-checkup/cf/locate.d.ts.map +0 -1
  207. package/dist/local/cloudflare-checkup/cf/locate.js.map +0 -1
  208. package/dist/local/cloudflare-checkup/cf/run-cf.d.ts.map +0 -1
  209. package/dist/local/cloudflare-checkup/cf/run-cf.js.map +0 -1
  210. package/dist/local/cloudflare-checkup/collector.d.ts.map +0 -1
  211. package/dist/local/cloudflare-checkup/collector.js.map +0 -1
  212. package/dist/local/cloudflare-checkup/contract.d.ts.map +0 -1
  213. package/dist/local/cloudflare-checkup/contract.js.map +0 -1
  214. package/dist/local/cloudflare-checkup/email-verification.d.ts.map +0 -1
  215. package/dist/local/cloudflare-checkup/email-verification.js.map +0 -1
  216. package/dist/local/cloudflare-checkup/errors.d.ts.map +0 -1
  217. package/dist/local/cloudflare-checkup/errors.js.map +0 -1
  218. package/dist/local/cloudflare-checkup/output.d.ts.map +0 -1
  219. package/dist/local/cloudflare-checkup/output.js.map +0 -1
  220. package/dist/local/cloudflare-checkup/prompts.d.ts.map +0 -1
  221. package/dist/local/cloudflare-checkup/prompts.js.map +0 -1
  222. package/dist/local/cloudflare-checkup/rate-budget.d.ts.map +0 -1
  223. package/dist/local/cloudflare-checkup/rate-budget.js.map +0 -1
  224. package/dist/local/cloudflare-checkup/snapshot-file.d.ts.map +0 -1
  225. package/dist/local/cloudflare-checkup/snapshot-file.js.map +0 -1
  226. package/dist/local/cloudflare-checkup/submit-client.d.ts.map +0 -1
  227. package/dist/local/cloudflare-checkup/submit-client.js.map +0 -1
  228. package/dist/local/cloudflare-checkup/terminal-ui.d.ts.map +0 -1
  229. package/dist/local/cloudflare-checkup/terminal-ui.js.map +0 -1
  230. package/dist/types.d.ts.map +0 -1
  231. package/dist/types.js.map +0 -1
  232. package/dist/version.d.ts.map +0 -1
  233. package/dist/version.js.map +0 -1
@@ -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,6 +1,7 @@
1
1
  import { readIdentityTokenMetadata } from '../auth/token-metadata.js';
2
2
  import { discoverAuthProvider } from '../auth/provider.js';
3
- import { revokeSavedCredentials, withCredentialsLock } from '../auth/refresh.js';
3
+ import { revokeIdentityCredentials, revokeIdentityGrant, revokeSavedCredentials, withCredentialsLock } from '../auth/refresh.js';
4
+ import { checkCredentialsLocation, clearIdentityCredentials, clearOneCredentials, hasIgnoredIdentityCredentials, readReplacedIdentityGrant, readStoredCredentials } from '../auth/store.js';
4
5
  import { getOneAccessToken, readCredentials, requestDeviceCode, waitForDeviceAuthorization, writeOneCredentials, } from '../auth/index.js';
5
6
  import { VigilbaseOneClient, ONE_AUTH_REQUIRED_TOOLS } from '../lib/one-client.js';
6
7
  import { VigilbaseApiError } from '../lib/client.js';
@@ -22,6 +23,30 @@ export async function readTokenFromStdin(input = process.stdin) {
22
23
  throw new Error('Token input must contain exactly one nonempty token.');
23
24
  return token;
24
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
+ }
25
50
  export const handleLogin = async (ctx) => {
26
51
  if (ctx.rest.length)
27
52
  throw new Error('Usage: vigilbase login [--token-stdin | --token <bearer>] [--no-open]');
@@ -29,6 +54,9 @@ export const handleLogin = async (ctx) => {
29
54
  if (ctx.loginHandler) {
30
55
  return ctx.loginHandler(ctx.config);
31
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);
32
60
  if (ctx.values.isTokenStdin && (ctx.values.token !== undefined || ctx.env.VIGILBASE_ONE_TOKEN !== undefined)) {
33
61
  throw new Error('Use --token-stdin without --token or VIGILBASE_ONE_TOKEN.');
34
62
  }
@@ -50,15 +78,19 @@ export const handleLogin = async (ctx) => {
50
78
  email: session.user.email ?? undefined,
51
79
  name: session.user.name ?? undefined,
52
80
  };
53
- await withCredentialsLock(ctx.config, () => writeOneCredentials(ctx.config, {
54
- accessToken: explicitToken,
55
- tokenType: 'Bearer',
56
- expiresAt: typeof session.session?.expiresAt === 'string'
57
- ? session.session.expiresAt
58
- : undefined,
59
- user,
60
- obtainedAt: new Date().toISOString(),
61
- }), true);
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);
62
94
  if (ctx.mode === 'json') {
63
95
  outputJson(ctx, {
64
96
  success: true,
@@ -76,6 +108,8 @@ export const handleLogin = async (ctx) => {
76
108
  const preferNoOpen = Boolean(ctx.values.isNoOpen) || isHeadlessEnvironment(ctx.env);
77
109
  ctx.writer.stderr(`login ${ctx.config.oneBaseUrl}`);
78
110
  ctx.config.authProvider = await discoverAuthProvider(ctx.config);
111
+ if (ctx.config.authProvider)
112
+ await retirePreviousIdentityGrant(ctx);
79
113
  const device = await withSpinner(ctx, 'Requesting device code', () => requestDeviceCode(ctx.config));
80
114
  const verifyUrl = resolveDeviceVerificationUrl(ctx.config.authProvider?.issuer ?? ctx.config.oneBaseUrl, device);
81
115
  const prettyCode = formatUserCode(device.user_code);
@@ -131,15 +165,20 @@ export const handleLogin = async (ctx) => {
131
165
  if (ctx.config.authProvider && (!token.refresh_token || !expiresAt || !token.expires_in || token.expires_in > 300))
132
166
  throw new Error('Identity returned an invalid token grant.');
133
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;
134
- await withCredentialsLock(ctx.config, () => writeOneCredentials(ctx.config, {
135
- ...(ctx.config.authProvider ? { issuer: ctx.config.authProvider.issuer, clientId: ctx.config.clientId, resource: ctx.config.authProvider.resource, refreshToken: token.refresh_token, orgId: identityMetadata?.organizationId } : {}),
136
- accessToken: token.access_token,
137
- tokenType: token.token_type,
138
- scope: token.scope,
139
- expiresAt,
140
- user,
141
- obtainedAt: new Date().toISOString(),
142
- }), true);
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);
143
182
  if (ctx.mode === 'json') {
144
183
  outputJson(ctx, {
145
184
  success: true,
@@ -160,7 +199,10 @@ export const handleLogout = async (ctx) => {
160
199
  if (ctx.rest.length)
161
200
  throw new Error('Usage: vigilbase logout');
162
201
  assertCommandFlags(ctx, []);
163
- const cleared = await revokeSavedCredentials(ctx.config);
202
+ const cleared = await revokeSavedCredentials(ctx.config, (warning) => {
203
+ if (ctx.mode !== 'json')
204
+ ctx.writer.stderr(`Warning: ${warning}`);
205
+ });
164
206
  if (ctx.mode === 'json') {
165
207
  outputJson(ctx, { success: true, cleared });
166
208
  }
@@ -177,6 +219,7 @@ export const handleWhoami = async (ctx) => {
177
219
  throw new Error('Usage: vigilbase whoami');
178
220
  assertCommandFlags(ctx, []);
179
221
  const credentials = await readCredentials(ctx.config);
222
+ const legacyIdentityFileIgnored = await hasIgnoredIdentityCredentials(ctx.config);
180
223
  const accessToken = await getOneAccessToken(ctx.config);
181
224
  let liveUser = null;
182
225
  if (accessToken) {
@@ -211,7 +254,10 @@ export const handleWhoami = async (ctx) => {
211
254
  scope: signedIn ? credentials.one?.scope ?? null : null,
212
255
  obtainedAt: signedIn ? credentials.one?.obtainedAt ?? null : null,
213
256
  authRequiredTools: signedIn ? [...ONE_AUTH_REQUIRED_TOOLS] : [],
257
+ legacyIdentityFileIgnored,
214
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.';
215
261
  if (ctx.mode === 'json') {
216
262
  outputJson(ctx, payload);
217
263
  }
@@ -219,6 +265,7 @@ export const handleWhoami = async (ctx) => {
219
265
  outputLines(ctx, [
220
266
  'Not signed in. Public tools still work.',
221
267
  'Run `vigilbase login` to unlock auth-required tools (website-security, react2shell, fortibleed-check).',
268
+ ...(legacyIdentityFileIgnored ? [legacyNote] : []),
222
269
  ]);
223
270
  }
224
271
  else {
@@ -229,8 +276,9 @@ export const handleWhoami = async (ctx) => {
229
276
  lines.push(`Expires: ${credentials.one.expiresAt}`);
230
277
  }
231
278
  lines.push(`Auth-required tools: ${ONE_AUTH_REQUIRED_TOOLS.join(', ')}`);
279
+ if (legacyIdentityFileIgnored)
280
+ lines.push(legacyNote);
232
281
  outputLines(ctx, lines);
233
282
  }
234
283
  return ok();
235
284
  };
236
- //# 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
@@ -96,4 +96,3 @@ export declare function requireOneClient(ctx: CommandContext): Promise<Vigilbase
96
96
  export declare function withSpinner<T>(ctx: CommandContext, label: string, work: () => Promise<T>): Promise<T>;
97
97
  export declare function ok(): ExitCode;
98
98
  export { EXIT };
99
- //# sourceMappingURL=helpers.d.ts.map
@@ -45,4 +45,3 @@ export function ok() {
45
45
  return EXIT.OK;
46
46
  }
47
47
  export { EXIT };
48
- //# sourceMappingURL=helpers.js.map
@@ -8,4 +8,3 @@ import type { CommandHandler } from './helpers.js';
8
8
  */
9
9
  export declare const COMMAND_HANDLERS: Record<string, CommandHandler>;
10
10
  export declare function getCommandHandler(command: string): CommandHandler | null;
11
- //# sourceMappingURL=index.d.ts.map
@@ -46,4 +46,3 @@ export const COMMAND_HANDLERS = {
46
46
  export function getCommandHandler(command) {
47
47
  return COMMAND_HANDLERS[command] ?? null;
48
48
  }
49
- //# 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
@@ -27,4 +27,9 @@ export declare function displayPath(path: string, env?: NodeJS.ProcessEnv, platf
27
27
  * `-2`, `-3`, … only when a run in the same second already used the name.
28
28
  */
29
29
  export declare function defaultSnapshotName(accountId: string, at: Date, attempt?: number): string;
30
- //# sourceMappingURL=local.d.ts.map
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;
@@ -33,7 +33,7 @@ import { buildSnapshot, collectAccount, estimateRequests, listAccounts } from '.
33
33
  import { accountName, countEntries, countEntriesByKind, dataCategories, loadSnapshotFile, writeSnapshotFile, zoneCount, } from '../local/cloudflare-checkup/snapshot-file.js';
34
34
  import { confirmDefaultYes, confirmUpload, hasInteractiveInput, promptLine } from '../local/cloudflare-checkup/prompts.js';
35
35
  import { SAME_ADDRESS_WAIT, emailLocale, parseEmailFlag, verifyEmail } from '../local/cloudflare-checkup/email-verification.js';
36
- import { SubmitClient, VerificationClient, assertSessionAccepts, displayRequester, resolveSubmitOrigin, } from '../local/cloudflare-checkup/submit-client.js';
36
+ import { SubmitClient, VerificationClient, assertServerAccepts, displayRequester, isLoopbackOrigin, resolveSubmitOrigin, } from '../local/cloudflare-checkup/submit-client.js';
37
37
  import { displayText, formatBytes, formatClock, formatDuration, maskId, readsSummary, shortSha, startCountsProgress } from '../local/cloudflare-checkup/output.js';
38
38
  import { CheckupTui } from '../local/cloudflare-checkup/terminal-ui.js';
39
39
  import { EXIT, assertCommandFlags, ok, outputJson } from './helpers.js';
@@ -1159,10 +1159,23 @@ function clientOptions(ctx, origin) {
1159
1159
  ...(ctx.values.timeout !== undefined ? { timeoutMs: ctx.config.timeoutMs } : {}),
1160
1160
  };
1161
1161
  }
1162
+ /**
1163
+ * Before the email is asked for (or `--email` is used): vigilbase.com says
1164
+ * whether local checkups are on and which CLI and snapshot schema it accepts,
1165
+ * so a switched-off local mode (LOCAL_MODE_DISABLED) or an old CLI
1166
+ * (CLI_UPGRADE_REQUIRED) never costs an emailed code. Without a usable answer
1167
+ * the run goes on, and the session check after the email decides as before.
1168
+ */
1169
+ async function checkAvailability(ctx, origin, schemaVersion) {
1170
+ const client = new VerificationClient(clientOptions(ctx, origin));
1171
+ const availability = await busy(ctx, 'Checking vigilbase.com', () => client.availability());
1172
+ if (availability)
1173
+ assertServerAccepts(availability, CLI_VERSION, schemaVersion);
1174
+ }
1162
1175
  /**
1163
1176
  * The email verification (prompts on stderr, answers from piped stdin
1164
- * without a terminal), then `✓ Verified j***@acme.com`. Nothing is sent
1165
- * before an address is entered, and cf is not touched before it is verified.
1177
+ * without a terminal), then `✓ Verified j***@acme.com`. No address is sent
1178
+ * before one is entered, and cf is not touched before it is verified.
1166
1179
  */
1167
1180
  async function verifyRequester(ctx, options, origin) {
1168
1181
  const verified = await verifyEmail({
@@ -1230,16 +1243,25 @@ class CheckupSession {
1230
1243
  /** Failures after the snapshot is saved that a later `submit <file>` can fix without collecting again. */
1231
1244
  const UPLOAD_LATER_CODES = new Set(['SESSION_INVALID', 'SESSION_EXPIRED', 'SESSION_CLOSED', 'VERIFICATION_EXPIRED', 'VERIFICATION_INVALID',
1232
1245
  'SUBMIT_FAILED', 'VIGILBASE_RATE_LIMITED', 'VIGILBASE_CHALLENGED', 'LOCAL_MODE_DISABLED']);
1246
+ /**
1247
+ * `submit` for a saved file. A loopback origin is named with `--base-url`, so a
1248
+ * development run never suggests an upload to production; production is the
1249
+ * CLI's default and is not named.
1250
+ */
1251
+ export function submitCommand(path, origin) {
1252
+ const baseUrl = isLoopbackOrigin(origin) ? ` --base-url ${shellArg(origin)}` : '';
1253
+ return `${NPX_COMMAND} cloudflare-checkup submit ${shellArg(path)}${baseUrl}`;
1254
+ }
1233
1255
  /**
1234
1256
  * Right before such an error is reported (stderr, both modes): the `submit`
1235
1257
  * command for the saved file. A line of its own, because hints are capped at
1236
1258
  * 300 characters and a path has no fixed length; in the terminal UI, the
1237
1259
  * command has a line of its own too, never wrapped, so it can be copied.
1238
1260
  */
1239
- function noteSavedSnapshot(ctx, error, path) {
1261
+ function noteSavedSnapshot(ctx, error, path, origin) {
1240
1262
  if (!(error instanceof LocalCheckupError) || !UPLOAD_LATER_CODES.has(error.code))
1241
1263
  return;
1242
- const submit = `${NPX_COMMAND} cloudflare-checkup submit ${shellArg(path)}`;
1264
+ const submit = submitCommand(path, origin);
1243
1265
  if (ctx.tui)
1244
1266
  ctx.tui.uploadLater(submit);
1245
1267
  else
@@ -1247,7 +1269,7 @@ function noteSavedSnapshot(ctx, error, path) {
1247
1269
  }
1248
1270
  /** Session checks that need no upload: CLI version, single use, and the bound account. */
1249
1271
  function assertSessionUsable(session, accountId) {
1250
- assertSessionAccepts(session, CLI_VERSION, SNAPSHOT_SCHEMA_VERSION);
1272
+ assertServerAccepts(session, CLI_VERSION, SNAPSHOT_SCHEMA_VERSION);
1251
1273
  if (session.state === 'completed')
1252
1274
  throw new LocalCheckupError('SESSION_USED', 'This checkup already has results, so it cannot accept a new snapshot.');
1253
1275
  // A checkup the email verification just started is never processing; this guards any other state.
@@ -1288,7 +1310,8 @@ async function runOneCommand(ctx, options) {
1288
1310
  // A bad --out never costs an emailed code (collectSnapshot checks it again).
1289
1311
  if (options.out !== undefined)
1290
1312
  await assertOutWritable(options.out, Boolean(ctx.values.isOverwrite));
1291
- // 1-3. The email is verified, and the checkup it started is checked, before cf is touched.
1313
+ // 1-3. Availability, then the email, then the checkup it started are checked before cf is touched.
1314
+ await checkAvailability(ctx, origin, SNAPSHOT_SCHEMA_VERSION);
1292
1315
  const checkup = new CheckupSession(ctx, origin, await verifyRequester(ctx, options, origin));
1293
1316
  const early = await busy(ctx, 'Checking the checkup', () => checkup.getSession());
1294
1317
  assertSessionUsable(early, options.accountId);
@@ -1312,7 +1335,7 @@ async function runOneCommand(ctx, options) {
1312
1335
  result = await uploadLoaded(ctx, { checkup, session, origin, loaded });
1313
1336
  }
1314
1337
  catch (error) {
1315
- noteSavedSnapshot(ctx, error, outcome.path);
1338
+ noteSavedSnapshot(ctx, error, outcome.path, origin);
1316
1339
  throw error;
1317
1340
  }
1318
1341
  if (ctx.mode === 'json') {
@@ -1410,7 +1433,8 @@ async function submitFile(ctx, options) {
1410
1433
  ctx.writer.stderr(head);
1411
1434
  else
1412
1435
  ctx.writer.stdout(head);
1413
- // The file is checked before the email is verified, so a bad file never sends a code.
1436
+ // The file, then vigilbase.com's availability, are checked before the email is verified, so neither costs a code.
1437
+ await checkAvailability(ctx, origin, loaded.snapshot.schemaVersion);
1414
1438
  const checkup = new CheckupSession(ctx, origin, await verifyRequester(ctx, options, origin));
1415
1439
  const session = await busy(ctx, 'Checking the checkup', () => checkup.getSession());
1416
1440
  const result = await uploadLoaded(ctx, { checkup, session, origin, loaded });
@@ -1455,7 +1479,7 @@ async function uploadLoaded(ctx, input) {
1455
1479
  const { checkup, session, origin, loaded } = input;
1456
1480
  const { snapshot } = loaded;
1457
1481
  assertFresh(snapshot);
1458
- assertSessionAccepts(session, CLI_VERSION, snapshot.schemaVersion);
1482
+ assertServerAccepts(session, CLI_VERSION, snapshot.schemaVersion);
1459
1483
  if (session.accountId !== null && session.accountId !== snapshot.accountId) {
1460
1484
  throw new LocalCheckupError('ACCOUNT_MISMATCH', `This checkup is bound to Cloudflare account ${maskId(session.accountId)}, but the snapshot is for ${maskId(snapshot.accountId)}. Nothing was uploaded.`);
1461
1485
  }
@@ -1517,4 +1541,3 @@ async function uploadLoaded(ctx, input) {
1517
1541
  ...(result.resultsUrl !== undefined ? { resultsUrl: result.resultsUrl } : {}),
1518
1542
  };
1519
1543
  }
1520
- //# sourceMappingURL=local.js.map
@@ -3,4 +3,3 @@ import { type CommandContext, type CommandHandler } from './helpers.js';
3
3
  export declare function buildOneOperation(ctx: CommandContext): OneOperation;
4
4
  export declare const handleOne: CommandHandler;
5
5
  export declare const ONE_HELP: Record<string, string>;
6
- //# sourceMappingURL=one.d.ts.map
@@ -153,7 +153,7 @@ export const handleOne = async (ctx) => {
153
153
  };
154
154
  export const ONE_HELP = {
155
155
  customers: `Usage: vigilbase customers <command> [--json]
156
- list List visible customers (also the default)
156
+ list List visible customers (also the default)
157
157
  show <customer-id> Full customer details
158
158
  summary <customer-id> Lightweight service/integration summary
159
159
  services <customer-id> Service records
@@ -163,7 +163,7 @@ export const ONE_HELP = {
163
163
  watchdog-targets <customer-id> Security/uptime targets
164
164
  watchdog-scans <customer-id> --limit 1..100 --offset 0..1000000 --target-id UUID
165
165
  reports <customer-id> Recon reports; --limit 1..100
166
- report <customer-id> <report-id> Recon report detail
166
+ report <customer-id> <report-id> Recon report detail
167
167
  investigations <customer-id> --limit 1..100 --cursor TEXT or --before ISO; --issue-id TEXT
168
168
  investigation <customer-id> <id> --revision 1..2147483647
169
169
  contracts <customer-id> --status draft|pending|active|expired|terminated
@@ -186,10 +186,10 @@ These commands read organization context; they do not switch the active organiza
186
186
  health Metrics backend and alerting readiness (also the default)
187
187
  probes Registered probe heartbeats (internal operators)
188
188
  probe-health Live probe health, with database fallback
189
- metrics <target-id>... Metrics for 1..50 target UUIDs
190
- targets <customer-id> Customer's monitoring targets
191
- status <customer-id> Current status; --light skips latency/SSL provider reads
192
- history <customer-id> --range 24h|7d|30d --region CODE --target-id UUID
189
+ metrics <target-id>... Metrics for 1..50 target UUIDs
190
+ targets <customer-id> Customer's monitoring targets
191
+ status <customer-id> Current status; --light skips latency/SSL provider reads
192
+ history <customer-id> --range 24h|7d|30d --region CODE --target-id UUID
193
193
  All operations require read:monitoring and the API's tenant access checks.
194
194
  History defaults to 7d. Health metadata preserves unknown, partial, and unavailable states.`,
195
195
  support: `Usage: vigilbase support tickets [--all] [--limit 1..100] [--json]
@@ -197,4 +197,3 @@ Lists current organization's open tickets; --all includes closed tickets.
197
197
  The API defaults to 50 results. Requires support:access and a linked Pylon account.
198
198
  The response retains hasMore; the API does not expose cursor pagination.`,
199
199
  };
200
- //# sourceMappingURL=one.js.map
@@ -6,4 +6,3 @@ export declare const handleDescribe: CommandHandler;
6
6
  export declare const handleSkill: CommandHandler;
7
7
  export declare const handleBadge: CommandHandler;
8
8
  export declare const handleDoctor: CommandHandler;
9
- //# sourceMappingURL=public-tools.d.ts.map
@@ -2,16 +2,38 @@ import { z } from 'zod';
2
2
  import { toolNameSchema, validateArguments } from './legacy-validation.js';
3
3
  import { NetworkError } from '../lib/network.js';
4
4
  import { VigilbaseApiError } from '../lib/client.js';
5
- import { getOneAccessToken, readCredentials } from '../auth/index.js';
5
+ import { getOneAccessToken, getOptionalOneAccessToken, hasUsableSignIn, readCredentials } from '../auth/index.js';
6
+ import { SignInUnsupportedError } from '../auth/store.js';
6
7
  import { formatExecuteResult, formatToolDescription, formatToolsList, } from '../lib/format.js';
7
8
  import { isOneAuthRequiredTool, isOneExecutableTool, ONE_AUTH_REQUIRED_TOOLS, resolveToolDefinition, VigilbaseOneClient, } from '../lib/one-client.js';
8
9
  import { EXIT, ok, outputJson, withSpinner, } from './helpers.js';
10
+ const ONE_SIGN_IN_REQUIRED = 'This tool needs a Vigilbase One sign-in: run `vigilbase login`.';
11
+ /**
12
+ * For --via one and auth-required tools only: any sign-in problem fails. Specific errors keep their
13
+ * message; those that don't already point to `vigilbase login` get it as their hint.
14
+ */
15
+ async function requireOneSignIn(config) {
16
+ let accessToken;
17
+ try {
18
+ accessToken = await getOneAccessToken(config);
19
+ }
20
+ catch (error) {
21
+ // Windows cannot sign in at all, so pointing to login would mislead.
22
+ if (error instanceof Error && !(error instanceof SignInUnsupportedError) && !error.message.includes('`vigilbase login`')) {
23
+ Object.assign(error, { hint: ONE_SIGN_IN_REQUIRED });
24
+ }
25
+ throw error;
26
+ }
27
+ if (!accessToken)
28
+ throw new Error(ONE_SIGN_IN_REQUIRED);
29
+ return accessToken;
30
+ }
9
31
  /** Public tools work without login. Auth-required tools need Vigilbase One. */
10
32
  export const handleList = async (ctx) => {
11
33
  validateArguments(ctx, 0);
12
34
  const catalog = await withSpinner(ctx, 'Loading tool catalog', () => ctx.publicClient.listTools());
13
- const accessToken = await getOneAccessToken(ctx.config);
14
- const authenticated = Boolean(accessToken);
35
+ // Offline: list never refreshes, so it cannot leave a refresh half-done.
36
+ const authenticated = await hasUsableSignIn(ctx.config, ctx.writer.stderr);
15
37
  if (ctx.mode === 'json') {
16
38
  outputJson(ctx, {
17
39
  ...catalog,
@@ -65,15 +87,13 @@ export const handleRun = async (ctx) => {
65
87
  if (via && via !== 'one' && via !== 'public') {
66
88
  throw new Error('--via must be one of: public, one');
67
89
  }
68
- const accessToken = await getOneAccessToken(ctx.config);
69
90
  const forcePublic = Boolean(ctx.values.isPublic) || via === 'public';
70
91
  const forceOne = via === 'one';
71
92
  // Public tools stay free unless forced onto One. Auth-required tools need login.
93
+ // The public route is anonymous and never reads the credential store.
72
94
  const useOne = !forcePublic && (forceOne || isOneAuthRequiredTool(toolName));
73
95
  if (useOne) {
74
- if (!accessToken) {
75
- throw new Error(`Tool "${toolName}" requires Vigilbase One auth. Run \`vigilbase login\` first.`);
76
- }
96
+ const accessToken = await requireOneSignIn(ctx.config);
77
97
  if (!isOneExecutableTool(toolName)) {
78
98
  throw new Error(`Tool "${toolName}" is not executable via Vigilbase One CLI yet.`);
79
99
  }
@@ -163,15 +183,21 @@ export const handleBadge = async (ctx) => {
163
183
  export const handleDoctor = async (ctx) => {
164
184
  validateArguments(ctx, 0);
165
185
  let oneCheckUrl = `${ctx.config.oneBaseUrl}/api/auth/get-session`;
166
- const [publicResult, oneResult] = await withSpinner(ctx, 'Checking public API and Vigilbase One', () => Promise.allSettled([
186
+ // A sign-in problem fails its own check, and One connectivity is then checked anonymously.
187
+ // The saved sign-in may refresh, with the configured timeout like any One command.
188
+ const signIn = getOptionalOneAccessToken(ctx.config);
189
+ const [publicResult, oneResult, signInResult] = await withSpinner(ctx, 'Checking public API and Vigilbase One', () => Promise.allSettled([
167
190
  ctx.publicClient.listTools(),
168
191
  (async () => {
169
- const accessToken = await getOneAccessToken(ctx.config);
170
- const saved = await readCredentials(ctx.config);
171
- if (accessToken && saved.version === 2)
172
- oneCheckUrl = `${saved.one.issuer}/api/auth/oauth2/userinfo`;
192
+ const { accessToken } = await signIn;
193
+ const saved = accessToken ? await readCredentials(ctx.config) : null;
194
+ // An Identity session is checked at the userinfo endpoint its issuer's discovery names.
195
+ if (saved?.version === 2)
196
+ oneCheckUrl = `${saved.one.issuer}/.well-known/openid-configuration`;
197
+ // Signed out, the check stays anonymous and never reads the credential store.
198
+ const client = new VigilbaseOneClient(ctx.config, accessToken ?? '', Boolean(accessToken));
173
199
  try {
174
- const session = await new VigilbaseOneClient(ctx.config, accessToken ?? '', true).getSession();
200
+ const session = await client.getSession();
175
201
  return { authenticated: Boolean(session.user?.id) };
176
202
  }
177
203
  catch (error) {
@@ -181,15 +207,23 @@ export const handleDoctor = async (ctx) => {
181
207
  }
182
208
  throw error;
183
209
  }
210
+ finally {
211
+ if (client.userinfoEndpoint)
212
+ oneCheckUrl = client.userinfoEndpoint;
213
+ }
184
214
  })(),
215
+ signIn.then(({ problem }) => {
216
+ if (problem)
217
+ throw new Error(problem);
218
+ }),
185
219
  ]));
186
220
  const checkResult = (result, url) => {
187
221
  if (result.status === 'fulfilled')
188
- return { ok: true, url };
222
+ return { ok: true, ...(url ? { url } : {}) };
189
223
  const error = result.reason;
190
224
  return {
191
225
  ok: false,
192
- url,
226
+ ...(url ? { url } : {}),
193
227
  error: error instanceof Error ? error.message : 'Connectivity check failed.',
194
228
  ...(error instanceof VigilbaseApiError || error instanceof NetworkError ? { status: error.status } : {}),
195
229
  ...(error instanceof NetworkError ? { code: error.code } : {}),
@@ -198,8 +232,9 @@ export const handleDoctor = async (ctx) => {
198
232
  const checks = {
199
233
  publicApi: checkResult(publicResult, `${ctx.config.publicBaseUrl}/api/agent/tools`),
200
234
  one: checkResult(oneResult, oneCheckUrl),
235
+ signIn: checkResult(signInResult),
201
236
  };
202
- const isOk = checks.publicApi.ok && checks.one.ok;
237
+ const isOk = checks.publicApi.ok && checks.one.ok && checks.signIn.ok;
203
238
  const firstFailure = Object.values(checks).find((check) => !check.ok);
204
239
  const catalog = publicResult.status === 'fulfilled' ? publicResult.value : null;
205
240
  const payload = {
@@ -223,10 +258,9 @@ export const handleDoctor = async (ctx) => {
223
258
  }
224
259
  else {
225
260
  for (const [label, check] of Object.entries(checks)) {
226
- ctx.writer.stdout(`${check.ok ? 'ok' : 'fail'} ${label} ${check.url}${check.error ? ` — ${check.error}` : ''}`);
261
+ ctx.writer.stdout(`${check.ok ? 'ok' : 'fail'} ${label}${check.url ? ` ${check.url}` : ''}${check.error ? ` — ${check.error}` : ''}`);
227
262
  }
228
263
  ctx.writer.stdout(`auth=${payload.authenticated ? 'in' : 'out'} tools=${payload.tools.join(',') || 'unavailable'}`);
229
264
  }
230
265
  return isOk ? ok() : EXIT.ERROR;
231
266
  };
232
- //# sourceMappingURL=public-tools.js.map
@@ -1,3 +1,2 @@
1
1
  import { type CommandHandler } from './helpers.js';
2
2
  export declare const handleRuns: CommandHandler;
3
- //# sourceMappingURL=runs.d.ts.map
@@ -73,4 +73,3 @@ export const handleRuns = async (ctx) => {
73
73
  }
74
74
  throw new Error('Usage: vigilbase runs [list|get <id>] [--tool …] [--limit …] [--json]');
75
75
  };
76
- //# sourceMappingURL=runs.js.map
@@ -2,5 +2,4 @@ import { type SecurityRequest } from '../lib/security-client.js';
2
2
  import { type CommandContext, type CommandHandler } from './helpers.js';
3
3
  export declare function buildSecurityRequest(ctx: CommandContext): SecurityRequest;
4
4
  export declare const handleSecurity: CommandHandler;
5
- export declare const SECURITY_HELP = "vigilbase security \u2014 managed security in Vigilbase One\n\n [overview]\n incidents [list] --status --severity --attack-category\n --order-by --order-direction --limit --offset\n incidents show <id>\n incidents timeline <id> --limit --before\n incidents fabric <id> --dataset --min-weight --limit\n incidents share-links <id>\n incidents update <id> <status> --confirm [--note]\n alerts [list] --limit --asset-id --start\n alerts show <id>\n logs --limit --asset-id --provider --dataset --search\n --start --end --sort --frs-min --cursor\n audit --limit --cursor --action-prefix --outcome\n assets\n assets <allowlist|blocklist|remediation|autonomy|datasets> <asset-id>\n assets graph <asset-id> --dataset --hours --min-weight --limit\n fabric default-asset\n detections --asset-id\n providers\n connections [list]\n connections show <id>\n connections <available-assets|remediation> <id>\n connections <logpush-status|worker-stats> <connection-id> <job-id>\n analytics [overview] --metrics + time-window flags\n analytics <security-analysis|bot-activity|top-attacker-ips|bot-asns|dns-security|page-shield>\n analytics provider-events --provider --dataset --start --end\n [--asset-id --search --limit]\n zero-trust <device-health|app-tests|access|sessions|device-posture>\n zero-trust gateway <dns|http|network>\n cloudflare-account [inventory]\n cloudflare-account <audit|ingestion|posture>\n notifications [list] --limit\n notifications <settings|preferences|slack-channels>\n reports [list] --limit --offset\n reports show <id>\n reports settings\n intel quota\n intel <ip|asn|cidr|ja3|ja4> <query>\n settings <lifecycle-emails|broadcast-emails|applications>\n\nTime-window flags: --hours (1\u2013720, default 24) or --start/--end ISO timestamps;\noptional --asset-id. Apply to analytics (except provider-events), zero-trust,\nand cloudflare-account audit/ingestion/posture. Inventory has no window flags.\nLimits: list incidents/reports/audit 1\u2013100 (default 50); alerts/notifications\n1\u2013100 (default 100); timeline 1\u2013200 (default 100); logs 1\u20131000 (default 100);\nprovider-events 1\u2013500 (default 200); graph 1\u20131000 (default 200).\nOffsets: 0\u20131000000. Graph min-weight: 1\u20131000000 (default 1).\nAll: --json, --timeout <ms>, --customer-id <One customer UUID> (internal View-as).\nAll operations read data except confirmed incident status updates.\nUpdate status: acknowledged, mitigating, resolved, false_positive.\nSee SECURITY.md for every endpoint, supported value, alias, and pagination contract.\n";
6
- //# sourceMappingURL=security.d.ts.map
5
+ export declare const SECURITY_HELP = "vigilbase security \u2014 managed security in Vigilbase One\n\n [overview]\n incidents [list] --status --severity --attack-category\n --order-by --order-direction --limit --offset\n incidents show <id>\n incidents timeline <id> --limit --before\n incidents fabric <id> --dataset --min-weight --limit\n incidents share-links <id>\n incidents update <id> <status> --confirm [--note]\n alerts [list] --limit --asset-id --start\n alerts show <id>\n logs --limit --asset-id --provider --dataset --search\n --start --end --sort --frs-min --cursor\n audit --limit --cursor --action-prefix --outcome\n assets\n assets <allowlist|blocklist|remediation|autonomy|datasets> <asset-id>\n assets graph <asset-id> --dataset --hours --min-weight --limit\n fabric default-asset\n detections --asset-id\n providers\n connections [list]\n connections show <id>\n connections <available-assets|remediation> <id>\n connections <logpush-status|worker-stats> <connection-id> <job-id>\n analytics [overview] --metrics + time-window flags\n analytics <security-analysis|bot-activity|top-attacker-ips|bot-asns|dns-security|page-shield>\n analytics provider-events --provider --dataset --start --end\n [--asset-id --search --limit]\n zero-trust <device-health|app-tests|access|sessions|device-posture>\n zero-trust gateway <dns|http|network>\n cloudflare-account [inventory]\n cloudflare-account <audit|ingestion|posture>\n notifications [list] --limit\n notifications <settings|preferences|slack-channels>\n reports [list] --limit --offset\n reports show <id>\n reports settings\n intel quota\n intel <ip|asn|cidr|ja3|ja4> <query>\n settings <lifecycle-emails|broadcast-emails|applications>\n\nTime-window flags: --hours (1\u2013720, default 24) or --start/--end ISO timestamps;\noptional --asset-id. Apply to analytics (except provider-events), zero-trust,\nand cloudflare-account audit/ingestion/posture. Inventory has no window flags.\nLimits: list incidents/reports/audit 1\u2013100 (default 50); alerts/notifications\n1\u2013100 (default 100); timeline 1\u2013200 (default 100); logs 1\u20131000 (default 100);\nprovider-events 1\u2013500 (default 200); graph 1\u20131000 (default 200).\nOffsets: 0\u20131000000. Graph min-weight: 1\u20131000000 (default 1).\nAll: --json, --timeout <ms>, --customer-id <One customer UUID> (internal View-as).\nAll operations read data except confirmed incident status updates.\nUpdate status: acknowledged, mitigating, resolved, false_positive.\nSee SECURITY.md for every endpoint, supported value, alias, and pagination contract.\n";