@seamapi/cli 0.21.0 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/README.md +39 -4
  2. package/bin/cli.js +29 -9
  3. package/bin/cli.js.map +1 -1
  4. package/lib/args/parse.d.ts +9 -0
  5. package/lib/args/parse.js +42 -2
  6. package/lib/args/parse.js.map +1 -1
  7. package/lib/args/validate.d.ts +10 -0
  8. package/lib/args/validate.js +23 -0
  9. package/lib/args/validate.js.map +1 -1
  10. package/lib/auth/operations.d.ts +13 -15
  11. package/lib/auth/operations.js +25 -48
  12. package/lib/auth/operations.js.map +1 -1
  13. package/lib/blueprint/source-npm.js +2 -2
  14. package/lib/blueprint/source-npm.js.map +1 -1
  15. package/lib/commands/local/login.js +4 -12
  16. package/lib/commands/local/login.js.map +1 -1
  17. package/lib/commands/local/select-endpoint.js +10 -7
  18. package/lib/commands/local/select-endpoint.js.map +1 -1
  19. package/lib/commands/local/select-workspace.js +12 -4
  20. package/lib/commands/local/select-workspace.js.map +1 -1
  21. package/lib/commands/local/wizard.d.ts +7 -0
  22. package/lib/commands/local/wizard.js +31 -0
  23. package/lib/commands/local/wizard.js.map +1 -1
  24. package/lib/commands/registry.d.ts +11 -0
  25. package/lib/commands/registry.js +11 -0
  26. package/lib/commands/registry.js.map +1 -1
  27. package/lib/commands/spec.d.ts +12 -0
  28. package/lib/commands/spec.js +16 -0
  29. package/lib/commands/spec.js.map +1 -1
  30. package/lib/config/cli-config.d.ts +19 -0
  31. package/lib/config/cli-config.js +51 -0
  32. package/lib/config/cli-config.js.map +1 -0
  33. package/lib/config/config-store.d.ts +8 -5
  34. package/lib/config/config-store.js +15 -13
  35. package/lib/config/config-store.js.map +1 -1
  36. package/lib/config/index.d.ts +3 -2
  37. package/lib/config/index.js +3 -2
  38. package/lib/config/index.js.map +1 -1
  39. package/lib/config/memory-config-store.d.ts +6 -0
  40. package/lib/config/memory-config-store.js +6 -0
  41. package/lib/config/memory-config-store.js.map +1 -1
  42. package/lib/context.d.ts +8 -7
  43. package/lib/context.js +26 -21
  44. package/lib/context.js.map +1 -1
  45. package/lib/interactions/endpoint-selection.js +2 -2
  46. package/lib/interactions/endpoint-selection.js.map +1 -1
  47. package/lib/interactions/login.js +2 -2
  48. package/lib/interactions/login.js.map +1 -1
  49. package/lib/interactions/workspace-id.js +2 -2
  50. package/lib/interactions/workspace-id.js.map +1 -1
  51. package/lib/overrides.d.ts +20 -0
  52. package/lib/overrides.js +23 -0
  53. package/lib/overrides.js.map +1 -0
  54. package/lib/render/help.js +20 -1
  55. package/lib/render/help.js.map +1 -1
  56. package/lib/version.d.ts +1 -1
  57. package/lib/version.js +1 -1
  58. package/package.json +2 -2
  59. package/src/bin/cli.ts +34 -9
  60. package/src/lib/args/parse.ts +47 -2
  61. package/src/lib/args/validate.ts +37 -0
  62. package/src/lib/auth/operations.ts +26 -56
  63. package/src/lib/blueprint/source-npm.ts +2 -3
  64. package/src/lib/commands/local/login.ts +4 -15
  65. package/src/lib/commands/local/select-endpoint.ts +11 -7
  66. package/src/lib/commands/local/select-workspace.ts +13 -4
  67. package/src/lib/commands/local/wizard.ts +48 -0
  68. package/src/lib/commands/registry.ts +20 -0
  69. package/src/lib/commands/spec.ts +31 -0
  70. package/src/lib/config/cli-config.ts +83 -0
  71. package/src/lib/config/config-store.ts +16 -13
  72. package/src/lib/config/index.ts +9 -4
  73. package/src/lib/config/memory-config-store.ts +9 -0
  74. package/src/lib/context.ts +34 -33
  75. package/src/lib/interactions/endpoint-selection.ts +2 -2
  76. package/src/lib/interactions/login.ts +2 -2
  77. package/src/lib/interactions/workspace-id.ts +2 -2
  78. package/src/lib/overrides.ts +32 -0
  79. package/src/lib/render/help.ts +21 -1
  80. package/src/lib/version.ts +1 -1
@@ -1,6 +1,6 @@
1
1
  import type { Interactivity } from './args/parse.js'
2
2
  import type { ApiBlueprint } from './blueprint/index.js'
3
- import { type ConfigStore, getConfigStore } from './config/index.js'
3
+ import { type CliConfig, getConfig } from './config/index.js'
4
4
  import {
5
5
  getEndpointFromEnv,
6
6
  getTokenFromEnv,
@@ -8,18 +8,20 @@ import {
8
8
  } from './env.js'
9
9
  import type { SeamApi } from './http/api.js'
10
10
  import type { Output } from './output/output.js'
11
+ import { getAuthOverrides } from './overrides.js'
11
12
 
12
13
  export const defaultEndpoint = 'https://connect.getseam.com'
13
14
 
14
15
  /** Where a resolved value came from, e.g., to refuse writes the env shadows. */
15
- export type ValueSource = 'env' | 'config' | 'default'
16
+ export type ValueSource = 'flag' | 'env' | 'config' | 'default'
16
17
 
17
18
  /**
18
19
  * The endpoint, token, and workspace requests are made with.
19
20
  *
20
- * Resolved in one place so the precedence rule exists once: an environment
21
- * variable wins over the stored value, and the endpoint falls back to Seam.
22
- * The source tags say where each value came from.
21
+ * Resolved in one place so the precedence rule exists once: a flag given for
22
+ * the one command wins over an environment variable, which wins over the
23
+ * stored value, and the endpoint falls back to Seam. The source tags say
24
+ * where each value came from.
23
25
  */
24
26
  export interface AuthContext {
25
27
  endpoint: string
@@ -30,38 +32,45 @@ export interface AuthContext {
30
32
  workspaceIdSource: Exclude<ValueSource, 'default'> | null
31
33
  }
32
34
 
33
- export const resolveAuth = (
34
- config: ConfigStore = getConfigStore(),
35
- ): AuthContext => {
35
+ export const resolveAuth = (config: CliConfig = getConfig()): AuthContext => {
36
+ const { endpoint: flagEndpoint, workspaceId: flagWorkspaceId } =
37
+ getAuthOverrides()
38
+
36
39
  const envEndpoint = getEndpointFromEnv()
37
- // Configs written before the endpoint was called one still hold it under
38
- // `server`, so fall back to that key rather than silently resetting them.
39
- const storedEndpoint = config.get('endpoint') ?? config.get('server')
40
- const endpoint =
41
- envEndpoint ?? (typeof storedEndpoint === 'string' ? storedEndpoint : null)
40
+ const storedEndpoint = config.getEndpoint()
41
+ const endpoint = flagEndpoint ?? envEndpoint ?? storedEndpoint
42
42
 
43
43
  const envToken = getTokenFromEnv()
44
- const storedToken = readString(
45
- config.get(`${endpoint ?? defaultEndpoint}.pat`),
46
- )
44
+ // The token is stored per endpoint, so an overridden endpoint is read with
45
+ // the token belonging to it rather than the one it replaced.
46
+ const storedToken = config.getToken(endpoint ?? defaultEndpoint)
47
47
 
48
48
  const envWorkspaceId = getWorkspaceIdFromEnv()
49
- const storedWorkspaceId = readString(config.get('current_workspace_id'))
49
+ const storedWorkspaceId = config.getWorkspace()
50
+ const workspaceId = flagWorkspaceId ?? envWorkspaceId ?? storedWorkspaceId
50
51
 
51
52
  return {
52
53
  endpoint: endpoint ?? defaultEndpoint,
53
54
  endpointSource:
54
- envEndpoint != null ? 'env' : endpoint != null ? 'config' : 'default',
55
+ flagEndpoint != null
56
+ ? 'flag'
57
+ : envEndpoint != null
58
+ ? 'env'
59
+ : endpoint != null
60
+ ? 'config'
61
+ : 'default',
55
62
  token: envToken ?? storedToken,
56
63
  tokenSource:
57
64
  envToken != null ? 'env' : storedToken != null ? 'config' : null,
58
- workspaceId: envWorkspaceId ?? storedWorkspaceId,
65
+ workspaceId,
59
66
  workspaceIdSource:
60
- envWorkspaceId != null
61
- ? 'env'
62
- : storedWorkspaceId != null
63
- ? 'config'
64
- : null,
67
+ flagWorkspaceId != null
68
+ ? 'flag'
69
+ : envWorkspaceId != null
70
+ ? 'env'
71
+ : storedWorkspaceId != null
72
+ ? 'config'
73
+ : null,
65
74
  }
66
75
  }
67
76
 
@@ -70,7 +79,7 @@ export const resolveAuth = (
70
79
  * shape it acts on, and how it may interact with the user.
71
80
  */
72
81
  export interface CliContext {
73
- config: ConfigStore
82
+ config: CliConfig
74
83
  auth: AuthContext
75
84
  output: Output
76
85
  blueprint: ApiBlueprint
@@ -78,11 +87,3 @@ export interface CliContext {
78
87
  /** The Seam API, constructed on first use and shared for the run. */
79
88
  api: () => Promise<SeamApi>
80
89
  }
81
-
82
- const readString = (value: unknown): string | null => {
83
- if (typeof value !== 'string') return null
84
-
85
- const trimmedValue = value.trim()
86
-
87
- return trimmedValue === '' ? null : trimmedValue
88
- }
@@ -1,11 +1,11 @@
1
1
  import { assertMutable, selectEndpoint } from 'lib/auth/operations.js'
2
- import { getConfigStore } from 'lib/config/index.js'
2
+ import { getConfig } from 'lib/config/index.js'
3
3
  import { resolveAuth } from 'lib/context.js'
4
4
  import { getOutput } from 'lib/output/get-output.js'
5
5
  import { promptAutocomplete } from 'lib/prompt.js'
6
6
 
7
7
  export async function interactForEndpointSelection() {
8
- const config = getConfigStore()
8
+ const config = getConfig()
9
9
  assertMutable(resolveAuth(config), 'endpoint', 'select an endpoint')
10
10
 
11
11
  const endpoints = ['http://localhost:3020', 'https://connect.getseam.com']
@@ -3,7 +3,7 @@ import chalk from 'chalk'
3
3
 
4
4
  import { assertMutable, storeToken } from 'lib/auth/operations.js'
5
5
  import { validateToken } from 'lib/auth/validate-token.js'
6
- import { getConfigStore } from 'lib/config/index.js'
6
+ import { getConfig } from 'lib/config/index.js'
7
7
  import { resolveAuth } from 'lib/context.js'
8
8
  import { getOutput } from 'lib/output/get-output.js'
9
9
  import { withLoading } from 'lib/output/with-loading.js'
@@ -12,7 +12,7 @@ import { promptText } from 'lib/prompt.js'
12
12
  import { interactForWorkspaceId } from './workspace-id.js'
13
13
 
14
14
  export const interactForLogin = async () => {
15
- const config = getConfigStore()
15
+ const config = getConfig()
16
16
  const output = getOutput()
17
17
  const auth = resolveAuth(config)
18
18
 
@@ -1,14 +1,14 @@
1
1
  import { SeamHttpWithoutWorkspace } from '@seamapi/http/connect'
2
2
 
3
3
  import { assertMutable, selectWorkspace } from 'lib/auth/operations.js'
4
- import { getConfigStore } from 'lib/config/index.js'
4
+ import { getConfig } from 'lib/config/index.js'
5
5
  import { resolveAuth } from 'lib/context.js'
6
6
  import { getSeamMultiWorkspace } from 'lib/http/client.js'
7
7
  import { withLoading } from 'lib/output/with-loading.js'
8
8
  import { promptAutocomplete } from 'lib/prompt.js'
9
9
 
10
10
  export const interactForWorkspaceId = async (personalAccessToken?: string) => {
11
- const config = getConfigStore()
11
+ const config = getConfig()
12
12
 
13
13
  // Refuse before prompting: nothing selected here could be stored.
14
14
  assertMutable(resolveAuth(config), 'workspaceId', 'select a workspace')
@@ -0,0 +1,32 @@
1
+ /**
2
+ * The endpoint and workspace may be overridden for a single command.
3
+ *
4
+ * `--endpoint` and `--workspace-id` change what one invocation resolves to
5
+ * and are never stored: only `seam select endpoint` and `seam select
6
+ * workspace` write those settings. They are held here rather than threaded
7
+ * through every call because auth resolves ambiently, exactly as the
8
+ * environment variables they shadow do (see `env.ts`).
9
+ *
10
+ * Set once from the parsed arguments before anything resolves auth, and
11
+ * reset between tests.
12
+ */
13
+
14
+ export interface AuthOverrides {
15
+ endpoint: string | null
16
+ workspaceId: string | null
17
+ }
18
+
19
+ const noOverrides: AuthOverrides = { endpoint: null, workspaceId: null }
20
+
21
+ let overrides: AuthOverrides = noOverrides
22
+
23
+ export const getAuthOverrides = (): AuthOverrides => overrides
24
+
25
+ export const setAuthOverrides = (next: AuthOverrides): void => {
26
+ overrides = next
27
+ }
28
+
29
+ /** Drop the overrides, e.g., between tests sharing a process. */
30
+ export const resetAuthOverrides = (): void => {
31
+ overrides = noOverrides
32
+ }
@@ -131,6 +131,7 @@ const commandSections = (
131
131
  ): Section[] => {
132
132
  const name = ['seam', ...command.path].join(' ')
133
133
  const hasFlags = command.flags.length > 0
134
+ const { positional } = command
134
135
 
135
136
  return [
136
137
  {
@@ -139,7 +140,26 @@ const commandSections = (
139
140
  (line) => line !== '',
140
141
  ),
141
142
  },
142
- { header: 'Usage', content: `${name} [options]` },
143
+ {
144
+ header: 'Usage',
145
+ content:
146
+ positional == null
147
+ ? `${name} [options]`
148
+ : `${name} <${positional.name}> [options]`,
149
+ },
150
+ ...(positional == null
151
+ ? []
152
+ : [
153
+ {
154
+ header: 'Arguments',
155
+ content: [
156
+ {
157
+ name: `{underline <${positional.name}>}`,
158
+ summary: positional.description,
159
+ },
160
+ ],
161
+ },
162
+ ]),
143
163
  // The command's own parameters are what the request is made of, so keep
144
164
  // them apart from the options every seam command takes.
145
165
  ...(hasFlags ? [optionSection(command.flags, 'Parameters')] : []),
@@ -1,5 +1,5 @@
1
1
  // Versions are replaced with generated values when the package is packed.
2
- const seamapiCliVersion = '0.21.0'
2
+ const seamapiCliVersion = '0.23.0'
3
3
  const seamapiBlueprintVersion = '1.5.0'
4
4
 
5
5
  export { seamapiBlueprintVersion }