@seamapi/cli 0.24.1 → 0.26.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 (99) hide show
  1. package/README.md +8 -4
  2. package/bin/cli.js +8 -2
  3. package/bin/cli.js.map +1 -1
  4. package/lib/args/parse.js +1 -0
  5. package/lib/args/parse.js.map +1 -1
  6. package/lib/auth/validate-token.js +1 -1
  7. package/lib/auth/validate-token.js.map +1 -1
  8. package/lib/blueprint/source-remote.js +1 -1
  9. package/lib/blueprint/source-remote.js.map +1 -1
  10. package/lib/commands/local/wizard.d.ts +8 -2
  11. package/lib/commands/local/wizard.js +28 -9
  12. package/lib/commands/local/wizard.js.map +1 -1
  13. package/lib/commands/spec.js +8 -0
  14. package/lib/commands/spec.js.map +1 -1
  15. package/lib/http/api.d.ts +1 -1
  16. package/lib/http/api.js +1 -1
  17. package/lib/http/api.js.map +1 -1
  18. package/lib/http/client.d.ts +1 -1
  19. package/lib/http/client.js +1 -1
  20. package/lib/http/client.js.map +1 -1
  21. package/lib/http/memory-seam-api.js +1 -1
  22. package/lib/http/memory-seam-api.js.map +1 -1
  23. package/lib/http/request.js +1 -1
  24. package/lib/http/request.js.map +1 -1
  25. package/lib/interactions/access-code.d.ts +1 -1
  26. package/lib/interactions/access-code.js +2 -1
  27. package/lib/interactions/access-code.js.map +1 -1
  28. package/lib/interactions/acs-entrance.d.ts +1 -1
  29. package/lib/interactions/acs-entrance.js +2 -1
  30. package/lib/interactions/acs-entrance.js.map +1 -1
  31. package/lib/interactions/acs-system.d.ts +4 -1
  32. package/lib/interactions/acs-system.js +2 -1
  33. package/lib/interactions/acs-system.js.map +1 -1
  34. package/lib/interactions/acs-user.d.ts +1 -1
  35. package/lib/interactions/acs-user.js +5 -2
  36. package/lib/interactions/acs-user.js.map +1 -1
  37. package/lib/interactions/action-attempt-poll.d.ts +1 -1
  38. package/lib/interactions/blueprint-object.js +41 -13
  39. package/lib/interactions/blueprint-object.js.map +1 -1
  40. package/lib/interactions/connected-account.d.ts +1 -1
  41. package/lib/interactions/connected-account.js +2 -1
  42. package/lib/interactions/connected-account.js.map +1 -1
  43. package/lib/interactions/custom-metadata.js +4 -0
  44. package/lib/interactions/custom-metadata.js.map +1 -1
  45. package/lib/interactions/device.d.ts +1 -1
  46. package/lib/interactions/device.js +2 -1
  47. package/lib/interactions/device.js.map +1 -1
  48. package/lib/interactions/login.js +1 -1
  49. package/lib/interactions/login.js.map +1 -1
  50. package/lib/interactions/resource.d.ts +3 -1
  51. package/lib/interactions/resource.js +2 -1
  52. package/lib/interactions/resource.js.map +1 -1
  53. package/lib/interactions/timestamp.d.ts +1 -1
  54. package/lib/interactions/timestamp.js +4 -1
  55. package/lib/interactions/timestamp.js.map +1 -1
  56. package/lib/interactions/user-identity.d.ts +1 -1
  57. package/lib/interactions/user-identity.js +2 -1
  58. package/lib/interactions/user-identity.js.map +1 -1
  59. package/lib/interactions/workspace-id.js +1 -1
  60. package/lib/interactions/workspace-id.js.map +1 -1
  61. package/lib/memory-prompt.d.ts +10 -7
  62. package/lib/memory-prompt.js +12 -6
  63. package/lib/memory-prompt.js.map +1 -1
  64. package/lib/prompt.d.ts +27 -3
  65. package/lib/prompt.js +26 -1
  66. package/lib/prompt.js.map +1 -1
  67. package/lib/render/help.js +1 -1
  68. package/lib/render/help.js.map +1 -1
  69. package/lib/version.d.ts +1 -1
  70. package/lib/version.js +1 -1
  71. package/package.json +11 -13
  72. package/src/bin/cli.ts +9 -2
  73. package/src/lib/args/parse.ts +1 -0
  74. package/src/lib/auth/validate-token.ts +1 -1
  75. package/src/lib/blueprint/source-remote.ts +1 -1
  76. package/src/lib/commands/local/wizard.ts +46 -4
  77. package/src/lib/commands/spec.ts +8 -0
  78. package/src/lib/http/api.ts +1 -1
  79. package/src/lib/http/client.ts +1 -1
  80. package/src/lib/http/memory-seam-api.ts +1 -4
  81. package/src/lib/http/request.ts +1 -4
  82. package/src/lib/interactions/access-code.ts +11 -7
  83. package/src/lib/interactions/acs-entrance.ts +2 -1
  84. package/src/lib/interactions/acs-system.ts +8 -1
  85. package/src/lib/interactions/acs-user.ts +5 -4
  86. package/src/lib/interactions/action-attempt-poll.ts +1 -1
  87. package/src/lib/interactions/blueprint-object.ts +62 -13
  88. package/src/lib/interactions/connected-account.ts +2 -1
  89. package/src/lib/interactions/custom-metadata.ts +6 -0
  90. package/src/lib/interactions/device.ts +2 -1
  91. package/src/lib/interactions/login.ts +1 -1
  92. package/src/lib/interactions/resource.ts +4 -0
  93. package/src/lib/interactions/timestamp.ts +4 -1
  94. package/src/lib/interactions/user-identity.ts +2 -1
  95. package/src/lib/interactions/workspace-id.ts +1 -1
  96. package/src/lib/memory-prompt.ts +31 -9
  97. package/src/lib/prompt.ts +65 -4
  98. package/src/lib/render/help.ts +1 -1
  99. package/src/lib/version.ts +1 -1
@@ -207,32 +207,47 @@ export const interactForBlueprintObject = async (
207
207
  }
208
208
  }
209
209
 
210
+ const current = args.params[paramToEdit]
211
+
210
212
  if (paramToEdit === 'device_id') {
211
- args.params[paramToEdit] = await interactForDevice()
213
+ args.params[paramToEdit] = await interactForDevice(toText(current))
212
214
  return interactForBlueprintObject(args, ctx)
213
215
  } else if (paramToEdit === 'access_code_id') {
214
- args.params[paramToEdit] = await interactForAccessCode(args.params as any)
216
+ args.params[paramToEdit] = await interactForAccessCode(
217
+ args.params as any,
218
+ toText(current),
219
+ )
215
220
  return interactForBlueprintObject(args, ctx)
216
221
  } else if (paramToEdit === 'connected_account_id') {
217
- const connectedAccountId = await interactForConnectedAccount()
222
+ const connectedAccountId = await interactForConnectedAccount(
223
+ toText(current),
224
+ )
218
225
  args.params[paramToEdit] = connectedAccountId
219
226
  return interactForBlueprintObject(args, ctx)
220
227
  } else if (
221
228
  paramToEdit === 'user_identity_id' ||
222
229
  paramToEdit === 'user_identity_ids'
223
230
  ) {
224
- const userIdentityId = await interactForUserIdentity()
231
+ const userIdentityId = await interactForUserIdentity(
232
+ paramToEdit === 'user_identity_ids'
233
+ ? toTextList(current)[0]
234
+ : toText(current),
235
+ )
225
236
  args.params[paramToEdit] =
226
237
  paramToEdit === 'user_identity_ids' ? [userIdentityId] : userIdentityId
227
238
  return interactForBlueprintObject(args, ctx)
228
239
  } else if (paramToEdit.endsWith('acs_system_id')) {
229
- args.params[paramToEdit] = await interactForAcsSystem()
240
+ args.params[paramToEdit] = await interactForAcsSystem({
241
+ initialValue: toText(current),
242
+ })
230
243
  return interactForBlueprintObject(args, ctx)
231
244
  } else if (paramToEdit.endsWith('acs_user_id')) {
232
- args.params[paramToEdit] = await interactForAcsUser()
245
+ args.params[paramToEdit] = await interactForAcsUser(toText(current))
233
246
  return interactForBlueprintObject(args, ctx)
234
247
  } else if (paramToEdit.endsWith('acs_entrance_id')) {
235
- args.params['acs_entrance_id'] = await interactForAcsEntrance()
248
+ args.params['acs_entrance_id'] = await interactForAcsEntrance(
249
+ toText(args.params['acs_entrance_id']),
250
+ )
236
251
  return interactForBlueprintObject(args, ctx)
237
252
  } else if (
238
253
  paramToEdit.endsWith('_at') ||
@@ -240,14 +255,14 @@ export const interactForBlueprintObject = async (
240
255
  paramToEdit.endsWith('_before') ||
241
256
  paramToEdit.endsWith('_after')
242
257
  ) {
243
- args.params[paramToEdit] = await interactForTimestamp()
258
+ args.params[paramToEdit] = await interactForTimestamp(toText(current))
244
259
  return interactForBlueprintObject(args, ctx)
245
260
  } else if (
246
261
  paramToEdit === 'custom_metadata' ||
247
262
  paramToEdit === 'custom_metadata_has'
248
263
  ) {
249
264
  args.params[paramToEdit] = await interactForCustomMetadata(
250
- args.params[paramToEdit] || {},
265
+ toRecord(current),
251
266
  )
252
267
  return interactForBlueprintObject(args, ctx)
253
268
  }
@@ -256,10 +271,11 @@ export const interactForBlueprintObject = async (
256
271
  if (['string', 'id', 'datetime'].includes(prop.format)) {
257
272
  let value
258
273
  if (prop.format === 'datetime') {
259
- value = await interactForTimestamp()
274
+ value = await interactForTimestamp(toText(current))
260
275
  } else {
261
276
  value = await promptText({
262
277
  message: withBackHint(`${paramToEdit}:`),
278
+ initialValue: toText(current),
263
279
  })
264
280
  }
265
281
  args.params[paramToEdit] = value
@@ -271,13 +287,14 @@ export const interactForBlueprintObject = async (
271
287
  label: v.name,
272
288
  value: v.name,
273
289
  })),
290
+ initialValue: toText(current),
274
291
  })
275
292
  args.params[paramToEdit] = value
276
293
  return interactForBlueprintObject(args, ctx)
277
294
  } else if (prop.format === 'boolean') {
278
295
  const value = await promptConfirm({
279
296
  message: withBackHint(`${paramToEdit}:`),
280
- initialValue: true,
297
+ initialValue: toBoolean(current) ?? true,
281
298
  active: 'true',
282
299
  inactive: 'false',
283
300
  })
@@ -292,12 +309,13 @@ export const interactForBlueprintObject = async (
292
309
  label: v.name,
293
310
  value: v.name,
294
311
  })),
312
+ initialValues: toTextList(current),
295
313
  })
296
314
  args.params[paramToEdit] = value
297
315
  return interactForBlueprintObject(args, ctx)
298
316
  } else if (prop.format === 'list') {
299
317
  args.params[paramToEdit] = await interactForArray(
300
- args.params[paramToEdit] || [],
318
+ toTextList(current),
301
319
  `Edit the list for ${paramToEdit}`,
302
320
  )
303
321
  return interactForBlueprintObject(args, ctx)
@@ -305,7 +323,7 @@ export const interactForBlueprintObject = async (
305
323
  args.params[paramToEdit] = await interactForBlueprintObject(
306
324
  {
307
325
  command: args.command,
308
- params: {},
326
+ params: toRecord(current),
309
327
  parameters: prop.parameters,
310
328
  isSubProperty: true,
311
329
  subPropertyPath: paramToEdit,
@@ -316,6 +334,7 @@ export const interactForBlueprintObject = async (
316
334
  } else if (prop.format === 'number') {
317
335
  const value = await promptNumber({
318
336
  message: withBackHint(`${paramToEdit}:`),
337
+ initialValue: toNumber(current),
319
338
  })
320
339
 
321
340
  args.params[paramToEdit] = value
@@ -332,3 +351,33 @@ export const interactForBlueprintObject = async (
332
351
  `Didn't know how to handle Blueprint parameter for property: "${paramToEdit}"`,
333
352
  )
334
353
  }
354
+
355
+ /*
356
+ * A parameter's current value in the shape its editor starts from, so that
357
+ * editing a parameter that already has a value continues from that value
358
+ * instead of from nothing — whether it came from an argument, from the JSON
359
+ * piped in, or from an earlier trip through this menu.
360
+ *
361
+ * Params are passed through as given, so a value need not match the format
362
+ * its parameter documents: the JSON piped in is arbitrary, and a schema can
363
+ * change under a script. A value the editor cannot start from is left out of
364
+ * it, which opens the editor as an unset parameter does, rather than
365
+ * rendering a value of the wrong type as text to edit.
366
+ */
367
+
368
+ const toText = (value: unknown): string | undefined =>
369
+ typeof value === 'string' ? value : undefined
370
+
371
+ const toNumber = (value: unknown): number | undefined =>
372
+ typeof value === 'number' ? value : undefined
373
+
374
+ const toBoolean = (value: unknown): boolean | undefined =>
375
+ typeof value === 'boolean' ? value : undefined
376
+
377
+ const toTextList = (value: unknown): string[] =>
378
+ Array.isArray(value) ? value.filter((item) => typeof item === 'string') : []
379
+
380
+ const toRecord = (value: unknown): Record<string, any> =>
381
+ typeof value === 'object' && value !== null && !Array.isArray(value)
382
+ ? (value as Record<string, any>)
383
+ : {}
@@ -1,12 +1,13 @@
1
1
  import { getSeam } from 'lib/http/client.js'
2
2
 
3
3
  import { interactForResource } from './resource.js'
4
- export const interactForConnectedAccount = async () => {
4
+ export const interactForConnectedAccount = async (initialValue?: string) => {
5
5
  const seam = await getSeam()
6
6
 
7
7
  return interactForResource({
8
8
  resourceName: 'connected_account',
9
9
  fetchResources: () => seam.connectedAccounts.list(),
10
+ initialValue,
10
11
  toChoice: (connectedAccount) => {
11
12
  const identifiers = Object.values(
12
13
  connectedAccount.user_identifier ?? {},
@@ -53,10 +53,16 @@ export const interactForCustomMetadata = async (
53
53
  message: withBackHint('Enter a key to add or edit:'),
54
54
  })
55
55
 
56
+ // Editing an existing key continues from the value it has, so a value
57
+ // is corrected rather than retyped.
58
+ const currentValue = updatedCustomMetadata[newKey]
59
+
56
60
  let newValue: string | boolean = await promptText({
57
61
  message: withBackHint(
58
62
  'Enter the new value to add or edit (or null to delete):',
59
63
  ),
64
+ initialValue:
65
+ currentValue == null ? undefined : currentValue.toString(),
60
66
  })
61
67
  if (newKey) {
62
68
  if (newValue === 'false' || newValue === 'true') {
@@ -1,12 +1,13 @@
1
1
  import { getSeam } from 'lib/http/client.js'
2
2
 
3
3
  import { interactForResource } from './resource.js'
4
- export const interactForDevice = async () => {
4
+ export const interactForDevice = async (initialValue?: string) => {
5
5
  const seam = await getSeam()
6
6
 
7
7
  return interactForResource({
8
8
  resourceName: 'device',
9
9
  fetchResources: () => seam.devices.list(),
10
+ initialValue,
10
11
  toChoice: (device) => ({
11
12
  title: device.properties.name ?? '<No Name>',
12
13
  value: device.device_id,
@@ -1,4 +1,4 @@
1
- import { isApiKey, isPersonalAccessToken } from '@seamapi/http/connect'
1
+ import { isApiKey, isPersonalAccessToken } from '@seamapi/http'
2
2
  import chalk from 'chalk'
3
3
 
4
4
  import { assertMutable, storeToken } from 'lib/auth/operations.js'
@@ -12,11 +12,14 @@ export const interactForResource = async <Resource>({
12
12
  fetchResources,
13
13
  toChoice,
14
14
  message = `Select a ${resourceName}:`,
15
+ initialValue,
15
16
  }: {
16
17
  resourceName: string
17
18
  fetchResources: () => Promise<Resource[]>
18
19
  toChoice: (resource: Resource) => ResourceChoice
19
20
  message?: string | undefined
21
+ /** The resource already chosen, for the list to open on. */
22
+ initialValue?: string | undefined
20
23
  }) => {
21
24
  const resources = await withLoading(
22
25
  `Fetching ${resourceName.replace(/_/g, ' ')}s...`,
@@ -30,5 +33,6 @@ export const interactForResource = async <Resource>({
30
33
  const { title, value, description } = toChoice(resource)
31
34
  return { label: title, value, hint: description }
32
35
  }),
36
+ initialValue,
33
37
  })
34
38
  }
@@ -1,11 +1,14 @@
1
1
  import { promptText, withBackHint } from 'lib/prompt.js'
2
2
 
3
- export const interactForTimestamp = async () => {
3
+ export const interactForTimestamp = async (currentValue?: string) => {
4
4
  const now = new Date().toISOString()
5
5
  const timestamp = await promptText({
6
6
  message: withBackHint('Enter a timestamp:'),
7
7
  placeholder: now,
8
8
  defaultValue: now,
9
+ // A timestamp already given is offered for editing, rather than making
10
+ // the user type it out again to shift it by an hour.
11
+ initialValue: currentValue,
9
12
  validate: (value) => {
10
13
  if (value == null || value === '') return undefined
11
14
  if (Number.isNaN(new Date(value).getTime())) {
@@ -2,12 +2,13 @@ import { getSeam } from 'lib/http/client.js'
2
2
 
3
3
  import { interactForResource } from './resource.js'
4
4
 
5
- export const interactForUserIdentity = async () => {
5
+ export const interactForUserIdentity = async (initialValue?: string) => {
6
6
  const seam = await getSeam()
7
7
 
8
8
  return interactForResource({
9
9
  resourceName: 'user_identity',
10
10
  fetchResources: () => seam.userIdentities.list(),
11
+ initialValue,
11
12
  toChoice: (userIdentity) => ({
12
13
  title: `${userIdentity.email_address} "${userIdentity.full_name}: ${userIdentity.user_identity_key}`,
13
14
  value: userIdentity.user_identity_id,
@@ -1,4 +1,4 @@
1
- import { SeamHttpWithoutWorkspace } from '@seamapi/http/connect'
1
+ import { SeamHttpWithoutWorkspace } from '@seamapi/http'
2
2
 
3
3
  import { assertMutable, selectWorkspace } from 'lib/auth/operations.js'
4
4
  import { getConfig } from 'lib/config/index.js'
@@ -3,6 +3,7 @@ import type {
3
3
  PromptChoice,
4
4
  PromptClient,
5
5
  PromptConfirmOptions,
6
+ PromptMultiselectOptions,
6
7
  PromptNumberOptions,
7
8
  PromptSelectOptions,
8
9
  PromptTextOptions,
@@ -19,6 +20,9 @@ export interface PromptQuestion {
19
20
  | 'autocompleteMultiselect'
20
21
  message: string
21
22
  choices?: Array<PromptChoice<unknown>>
23
+ /** What the question was seeded with — what the user sees to edit. */
24
+ initialValue?: unknown
25
+ initialValues?: unknown[] | undefined
22
26
  }
23
27
 
24
28
  /** Scripted in place of an answer to dismiss that prompt. */
@@ -44,35 +48,53 @@ export class MemoryPromptClient implements PromptClient {
44
48
 
45
49
  canPrompt = (): boolean => true
46
50
 
47
- text = async ({ message }: PromptTextOptions): Promise<string> =>
48
- this.answer({ kind: 'text', message }) as string
51
+ text = async ({
52
+ message,
53
+ initialValue,
54
+ }: PromptTextOptions): Promise<string> =>
55
+ this.answer({ kind: 'text', message, initialValue }) as string
49
56
 
50
- number = async ({ message }: PromptNumberOptions): Promise<number> =>
51
- this.answer({ kind: 'number', message }) as number
57
+ number = async ({
58
+ message,
59
+ initialValue,
60
+ }: PromptNumberOptions): Promise<number> =>
61
+ this.answer({ kind: 'number', message, initialValue }) as number
52
62
 
53
- confirm = async ({ message }: PromptConfirmOptions): Promise<boolean> =>
54
- this.answer({ kind: 'confirm', message }) as boolean
63
+ confirm = async ({
64
+ message,
65
+ initialValue,
66
+ }: PromptConfirmOptions): Promise<boolean> =>
67
+ this.answer({ kind: 'confirm', message, initialValue }) as boolean
55
68
 
56
69
  select = async <Value>({
57
70
  message,
58
71
  choices,
72
+ initialValue,
59
73
  }: PromptSelectOptions<Value>): Promise<Value> =>
60
- this.answer({ kind: 'select', message, choices }) as Value
74
+ this.answer({ kind: 'select', message, choices, initialValue }) as Value
61
75
 
62
76
  autocomplete = async <Value>({
63
77
  message,
64
78
  choices,
79
+ initialValue,
65
80
  }: PromptSelectOptions<Value>): Promise<Value> =>
66
- this.answer({ kind: 'autocomplete', message, choices }) as Value
81
+ this.answer({
82
+ kind: 'autocomplete',
83
+ message,
84
+ choices,
85
+ initialValue,
86
+ }) as Value
67
87
 
68
88
  autocompleteMultiselect = async <Value>({
69
89
  message,
70
90
  choices,
71
- }: PromptSelectOptions<Value>): Promise<Value[]> =>
91
+ initialValues,
92
+ }: PromptMultiselectOptions<Value>): Promise<Value[]> =>
72
93
  this.answer({
73
94
  kind: 'autocompleteMultiselect',
74
95
  message,
75
96
  choices,
97
+ initialValues,
76
98
  }) as Value[]
77
99
 
78
100
  private answer(question: PromptQuestion): unknown {
package/src/lib/prompt.ts CHANGED
@@ -24,11 +24,15 @@ export interface PromptTextOptions {
24
24
  message: string
25
25
  placeholder?: string
26
26
  defaultValue?: string
27
+ /** Editable text the prompt opens with, for editing a value in place. */
28
+ initialValue?: string | undefined
27
29
  validate?: (value: string | undefined) => string | undefined
28
30
  }
29
31
 
30
32
  export interface PromptNumberOptions {
31
33
  message: string
34
+ /** Editable text the prompt opens with, for editing a value in place. */
35
+ initialValue?: number | undefined
32
36
  validate?: (value: number) => string | undefined
33
37
  }
34
38
 
@@ -42,6 +46,15 @@ export interface PromptConfirmOptions {
42
46
  export interface PromptSelectOptions<Value> {
43
47
  message: string
44
48
  choices: Array<PromptChoice<Value>>
49
+ /** The choice to open on, for editing a value in place. */
50
+ initialValue?: Value | undefined
51
+ }
52
+
53
+ export interface PromptMultiselectOptions<Value> {
54
+ message: string
55
+ choices: Array<PromptChoice<Value>>
56
+ /** The choices to open selected, for editing a value in place. */
57
+ initialValues?: Value[] | undefined
45
58
  }
46
59
 
47
60
  /**
@@ -60,7 +73,7 @@ export interface PromptClient {
60
73
  select: <Value>(options: PromptSelectOptions<Value>) => Promise<Value>
61
74
  autocomplete: <Value>(options: PromptSelectOptions<Value>) => Promise<Value>
62
75
  autocompleteMultiselect: <Value>(
63
- options: PromptSelectOptions<Value>,
76
+ options: PromptMultiselectOptions<Value>,
64
77
  ) => Promise<Value[]>
65
78
  }
66
79
 
@@ -133,6 +146,37 @@ const toOptions = <Value>(
133
146
  : { label, value, hint }) as Option<Value>,
134
147
  )
135
148
 
149
+ /**
150
+ * A value as the choice a list prompt opens on: the value when the list
151
+ * offers it, and otherwise nothing.
152
+ *
153
+ * A value the list does not offer, such as an id passed as an argument that
154
+ * is not among the resources fetched, would leave clack holding a selection
155
+ * with no choice behind it — so the prompt opens on its first choice, the
156
+ * same as one with no value to start from.
157
+ */
158
+ export const offeredValue = <Value>(
159
+ choices: Array<PromptChoice<Value>>,
160
+ value: Value | undefined,
161
+ ): Value | undefined =>
162
+ choices.some((choice) => choice.value === value) ? value : undefined
163
+
164
+ /** Every one of the values a list prompt offers, in the order given. */
165
+ export const offeredValues = <Value>(
166
+ choices: Array<PromptChoice<Value>>,
167
+ values: Value[] | undefined,
168
+ ): Value[] =>
169
+ (values ?? []).filter((value) => offeredValue(choices, value) !== undefined)
170
+
171
+ // Options a clack prompt is given cannot carry an explicit undefined: it
172
+ // declares each optional field without it, and the CLI type-checks with
173
+ // exactOptionalPropertyTypes.
174
+ const optional = <Key extends string, Value>(
175
+ key: Key,
176
+ value: Value | undefined,
177
+ ): Partial<Record<Key, Value>> =>
178
+ value === undefined ? {} : ({ [key]: value } as Record<Key, Value>)
179
+
136
180
  export class TerminalPromptClient implements PromptClient {
137
181
  /**
138
182
  * Prompts read raw keypresses and render an interface, so they need a
@@ -145,7 +189,14 @@ export class TerminalPromptClient implements PromptClient {
145
189
 
146
190
  text = async (options: PromptTextOptions): Promise<string> => {
147
191
  installArrowKeyAliases()
148
- return unwrap(await text({ ...options, output }))
192
+ const { initialValue, ...rest } = options
193
+ return unwrap(
194
+ await text({
195
+ ...rest,
196
+ ...optional('initialValue', initialValue),
197
+ output,
198
+ }),
199
+ )
149
200
  }
150
201
 
151
202
  number = async (options: PromptNumberOptions): Promise<number> => {
@@ -153,6 +204,7 @@ export class TerminalPromptClient implements PromptClient {
153
204
  const value = unwrap(
154
205
  await text({
155
206
  message: options.message,
207
+ ...optional('initialValue', options.initialValue?.toString()),
156
208
  validate: (value) => {
157
209
  if (value == null || value.trim() === '') return 'Enter a number'
158
210
  const parsed = Number(value)
@@ -178,6 +230,10 @@ export class TerminalPromptClient implements PromptClient {
178
230
  await select<Value>({
179
231
  message: options.message,
180
232
  options: toOptions(options.choices),
233
+ ...optional(
234
+ 'initialValue',
235
+ offeredValue(options.choices, options.initialValue),
236
+ ),
181
237
  output,
182
238
  }),
183
239
  )
@@ -191,6 +247,10 @@ export class TerminalPromptClient implements PromptClient {
191
247
  await autocomplete<Value>({
192
248
  message: options.message,
193
249
  options: toOptions(options.choices),
250
+ ...optional(
251
+ 'initialValue',
252
+ offeredValue(options.choices, options.initialValue),
253
+ ),
194
254
  // Search a list by any part of a name or hint, rather than only by
195
255
  // the label, which is all clack matches for itself.
196
256
  filter: searchChoices,
@@ -200,13 +260,14 @@ export class TerminalPromptClient implements PromptClient {
200
260
  }
201
261
 
202
262
  autocompleteMultiselect = async <Value>(
203
- options: PromptSelectOptions<Value>,
263
+ options: PromptMultiselectOptions<Value>,
204
264
  ): Promise<Value[]> => {
205
265
  installArrowKeyAliases()
206
266
  return unwrap(
207
267
  await autocompleteMultiselect<Value>({
208
268
  message: options.message,
209
269
  options: toOptions(options.choices),
270
+ initialValues: offeredValues(options.choices, options.initialValues),
210
271
  filter: searchChoices,
211
272
  output,
212
273
  }),
@@ -273,7 +334,7 @@ export const promptAutocomplete = async <Value>(
273
334
  }
274
335
 
275
336
  export const promptAutocompleteMultiselect = async <Value>(
276
- options: PromptSelectOptions<Value>,
337
+ options: PromptMultiselectOptions<Value>,
277
338
  ): Promise<Value[]> => {
278
339
  ensureInteractive()
279
340
  return await client.autocompleteMultiselect(options)
@@ -36,7 +36,7 @@ const outputSection = {
36
36
  content: [
37
37
  'Only the response is written to stdout, so it is safe to pipe. Prompts, progress, and other information are written to stderr.',
38
38
  'The response is trimmed to the response key and pagination.',
39
- 'Request params may be piped or redirected in as a JSON object. Params given as arguments win over params read from stdin.',
39
+ 'Request params may be piped or redirected in as a JSON object, or passed inline with --raw. Params given as arguments win over raw or stdin params.',
40
40
  ],
41
41
  }
42
42
 
@@ -1,5 +1,5 @@
1
1
  // Versions are replaced with generated values when the package is packed.
2
- const seamapiCliVersion = '0.24.1'
2
+ const seamapiCliVersion = '0.26.0'
3
3
  const seamapiBlueprintVersion = '1.5.0'
4
4
 
5
5
  export { seamapiBlueprintVersion }