@seamapi/cli 0.10.0 → 0.12.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.
- package/README.md +118 -0
- package/bin/cli.js +78 -100
- package/bin/cli.js.map +1 -1
- package/completions/seam.bash +11 -0
- package/completions/seam.fish +11 -0
- package/completions/seam.zsh +13 -0
- package/lib/command-spec.d.ts +53 -0
- package/lib/command-spec.js +290 -0
- package/lib/command-spec.js.map +1 -0
- package/lib/completion/describe.d.ts +10 -0
- package/lib/completion/describe.js +16 -0
- package/lib/completion/describe.js.map +1 -0
- package/lib/completion/index.d.ts +20 -0
- package/lib/completion/index.js +65 -0
- package/lib/completion/index.js.map +1 -0
- package/lib/completion/render-bash.d.ts +2 -0
- package/lib/completion/render-bash.js +98 -0
- package/lib/completion/render-bash.js.map +1 -0
- package/lib/completion/render-fish.d.ts +2 -0
- package/lib/completion/render-fish.js +58 -0
- package/lib/completion/render-fish.js.map +1 -0
- package/lib/completion/render-zsh.d.ts +2 -0
- package/lib/completion/render-zsh.js +121 -0
- package/lib/completion/render-zsh.js.map +1 -0
- package/lib/get-response-key.d.ts +9 -0
- package/lib/get-response-key.js +21 -0
- package/lib/get-response-key.js.map +1 -0
- package/lib/interact-for-action-attempt-poll.js +4 -3
- package/lib/interact-for-action-attempt-poll.js.map +1 -1
- package/lib/interact-for-array.js +9 -7
- package/lib/interact-for-array.js.map +1 -1
- package/lib/interact-for-blueprint-object.js +9 -8
- package/lib/interact-for-blueprint-object.js.map +1 -1
- package/lib/interact-for-command-selection.js +2 -2
- package/lib/interact-for-command-selection.js.map +1 -1
- package/lib/interact-for-custom-metadata.js +10 -8
- package/lib/interact-for-custom-metadata.js.map +1 -1
- package/lib/interact-for-login.js +8 -6
- package/lib/interact-for-login.js.map +1 -1
- package/lib/interact-for-resource.js +2 -2
- package/lib/interact-for-resource.js.map +1 -1
- package/lib/interact-for-server-selection.js +7 -5
- package/lib/interact-for-server-selection.js.map +1 -1
- package/lib/interact-for-timestamp.js +2 -2
- package/lib/interact-for-timestamp.js.map +1 -1
- package/lib/interact-for-use-remote-api-defs.js +4 -3
- package/lib/interact-for-use-remote-api-defs.js.map +1 -1
- package/lib/interact-for-workspace-id.js +2 -2
- package/lib/interact-for-workspace-id.js.map +1 -1
- package/lib/output/create-memory-output.d.ts +13 -0
- package/lib/output/create-memory-output.js +22 -0
- package/lib/output/create-memory-output.js.map +1 -0
- package/lib/output/create-output.d.ts +50 -0
- package/lib/output/create-output.js +34 -0
- package/lib/output/create-output.js.map +1 -0
- package/lib/output/get-output.d.ts +12 -0
- package/lib/output/get-output.js +21 -0
- package/lib/output/get-output.js.map +1 -0
- package/lib/output/resolve-output-format.d.ts +16 -0
- package/lib/output/resolve-output-format.js +23 -0
- package/lib/output/resolve-output-format.js.map +1 -0
- package/lib/output/select-response-payload.d.ts +16 -0
- package/lib/output/select-response-payload.js +33 -0
- package/lib/output/select-response-payload.js.map +1 -0
- package/lib/render-help.d.ts +8 -0
- package/lib/render-help.js +150 -0
- package/lib/render-help.js.map +1 -0
- package/lib/util/cli-args.d.ts +16 -1
- package/lib/util/cli-args.js +18 -2
- package/lib/util/cli-args.js.map +1 -1
- package/lib/util/prompt.d.ts +17 -0
- package/lib/util/prompt.js +25 -0
- package/lib/util/prompt.js.map +1 -0
- package/lib/util/read-stdin-json.d.ts +22 -0
- package/lib/util/read-stdin-json.js +42 -0
- package/lib/util/read-stdin-json.js.map +1 -0
- package/lib/util/request-seam-api.d.ts +5 -2
- package/lib/util/request-seam-api.js +13 -15
- package/lib/util/request-seam-api.js.map +1 -1
- package/lib/util/with-loading.js +6 -1
- package/lib/util/with-loading.js.map +1 -1
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +2 -1
- package/src/bin/cli.ts +100 -106
- package/src/lib/command-spec.ts +400 -0
- package/src/lib/completion/describe.ts +21 -0
- package/src/lib/completion/index.ts +82 -0
- package/src/lib/completion/render-bash.ts +125 -0
- package/src/lib/completion/render-fish.ts +80 -0
- package/src/lib/completion/render-zsh.ts +157 -0
- package/src/lib/get-response-key.ts +25 -0
- package/src/lib/interact-for-action-attempt-poll.ts +4 -3
- package/src/lib/interact-for-array.ts +9 -7
- package/src/lib/interact-for-blueprint-object.ts +9 -8
- package/src/lib/interact-for-command-selection.ts +2 -3
- package/src/lib/interact-for-custom-metadata.ts +10 -8
- package/src/lib/interact-for-login.ts +8 -6
- package/src/lib/interact-for-resource.ts +2 -3
- package/src/lib/interact-for-server-selection.ts +7 -6
- package/src/lib/interact-for-timestamp.ts +2 -2
- package/src/lib/interact-for-use-remote-api-defs.ts +4 -4
- package/src/lib/interact-for-workspace-id.ts +2 -2
- package/src/lib/output/create-memory-output.ts +39 -0
- package/src/lib/output/create-output.ts +104 -0
- package/src/lib/output/get-output.ts +24 -0
- package/src/lib/output/resolve-output-format.ts +33 -0
- package/src/lib/output/select-response-payload.ts +56 -0
- package/src/lib/render-help.ts +197 -0
- package/src/lib/util/cli-args.ts +32 -2
- package/src/lib/util/prompt.ts +38 -0
- package/src/lib/util/read-stdin-json.ts +59 -0
- package/src/lib/util/request-seam-api.ts +24 -20
- package/src/lib/util/with-loading.ts +9 -1
- package/src/lib/version.ts +1 -1
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type CommandFlag,
|
|
3
|
+
type CommandSpec,
|
|
4
|
+
flagTokens,
|
|
5
|
+
} from '../command-spec.js'
|
|
6
|
+
import { describeForShell } from './describe.js'
|
|
7
|
+
|
|
8
|
+
export const renderZshCompletion = (spec: CommandSpec): string => {
|
|
9
|
+
const valuelessTokens = spec.globalFlags
|
|
10
|
+
.filter(({ takesValue }) => !takesValue)
|
|
11
|
+
.flatMap(flagTokens)
|
|
12
|
+
.sort()
|
|
13
|
+
|
|
14
|
+
return `${[
|
|
15
|
+
header,
|
|
16
|
+
renderCase('_seam_subcommands', subcommandBranches(spec)),
|
|
17
|
+
renderCase('_seam_flags', flagBranches(spec)),
|
|
18
|
+
renderCase('_seam_flag_values', flagValueBranches(spec)),
|
|
19
|
+
`_seam_global_flags() {\n _seam_reply+=(${describeFlags(spec.globalFlags)})\n}`,
|
|
20
|
+
completionFunction(valuelessTokens),
|
|
21
|
+
dispatch,
|
|
22
|
+
].join('\n\n')}\n`
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const header = `#compdef seam
|
|
26
|
+
|
|
27
|
+
# zsh completion for the seam command.
|
|
28
|
+
#
|
|
29
|
+
# Generated by @seamapi/cli from the Seam API definitions.
|
|
30
|
+
# Do not edit: regenerate with 'seam completion zsh'.
|
|
31
|
+
#
|
|
32
|
+
# Load it for the current shell with
|
|
33
|
+
#
|
|
34
|
+
# source <(seam completion zsh)
|
|
35
|
+
#
|
|
36
|
+
# or install it for every shell with
|
|
37
|
+
#
|
|
38
|
+
# seam completion zsh > "\${fpath[1]}/_seam"`
|
|
39
|
+
|
|
40
|
+
const completionFunction = (valuelessTokens: string[]): string =>
|
|
41
|
+
`_seam() {
|
|
42
|
+
local -a _seam_reply
|
|
43
|
+
local -a valueless
|
|
44
|
+
local command previous word
|
|
45
|
+
local -i index
|
|
46
|
+
|
|
47
|
+
valueless=(${valuelessTokens.join(' ')})
|
|
48
|
+
|
|
49
|
+
# The command path is the run of words before the first flag.
|
|
50
|
+
command=''
|
|
51
|
+
for (( index = 2; index < CURRENT; index++ )); do
|
|
52
|
+
word="\${words[index]}"
|
|
53
|
+
if [[ "$word" == -* ]]; then
|
|
54
|
+
break
|
|
55
|
+
fi
|
|
56
|
+
command="\${command:+$command }$word"
|
|
57
|
+
done
|
|
58
|
+
|
|
59
|
+
previous=''
|
|
60
|
+
if (( CURRENT > 1 )); then
|
|
61
|
+
previous="\${words[CURRENT - 1]}"
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
# Completing the value of a flag that takes one.
|
|
65
|
+
if [[ "$previous" == -* ]] && (( \${valueless[(Ie)$previous]} == 0 )); then
|
|
66
|
+
_seam_flag_values "$command $previous"
|
|
67
|
+
if (( \${#_seam_reply} )); then
|
|
68
|
+
_describe -t values 'value' _seam_reply
|
|
69
|
+
fi
|
|
70
|
+
return
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
if [[ "\${words[CURRENT]}" == -* ]]; then
|
|
74
|
+
_seam_flags "$command"
|
|
75
|
+
_seam_global_flags
|
|
76
|
+
_describe -t options 'option' _seam_reply
|
|
77
|
+
return
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
_seam_subcommands "$command"
|
|
81
|
+
if (( \${#_seam_reply} )); then
|
|
82
|
+
_describe -t commands 'command' _seam_reply
|
|
83
|
+
return
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
_seam_flags "$command"
|
|
87
|
+
_seam_global_flags
|
|
88
|
+
_describe -t options 'option' _seam_reply
|
|
89
|
+
}`
|
|
90
|
+
|
|
91
|
+
// The script runs in three ways. Autoloaded from fpath as _seam, it must
|
|
92
|
+
// complete the in-flight request: funcstack holds _seam. Evaluated by the
|
|
93
|
+
// loader stub inside the autoloaded _seam, the same applies, but eval pushes
|
|
94
|
+
// '(eval)' onto funcstack, so search the whole stack rather than the top.
|
|
95
|
+
// Sourced into a shell, funcstack holds no _seam: register with compdef.
|
|
96
|
+
const dispatch = `if (( \${funcstack[(I)_seam]} )); then
|
|
97
|
+
_seam "$@"
|
|
98
|
+
else
|
|
99
|
+
compdef _seam seam
|
|
100
|
+
fi`
|
|
101
|
+
|
|
102
|
+
interface Branch {
|
|
103
|
+
pattern: string
|
|
104
|
+
entries: string[]
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const subcommandBranches = (spec: CommandSpec): Branch[] =>
|
|
108
|
+
spec.groups.map((group) => ({
|
|
109
|
+
pattern: group.path.join(' '),
|
|
110
|
+
entries: group.subcommands.map(({ name, description }) =>
|
|
111
|
+
describe(name, description),
|
|
112
|
+
),
|
|
113
|
+
}))
|
|
114
|
+
|
|
115
|
+
const flagBranches = (spec: CommandSpec): Branch[] =>
|
|
116
|
+
spec.commands
|
|
117
|
+
.filter(({ flags }) => flags.length > 0)
|
|
118
|
+
.map((command) => ({
|
|
119
|
+
pattern: command.path.join(' '),
|
|
120
|
+
entries: [describeFlags(command.flags)],
|
|
121
|
+
}))
|
|
122
|
+
|
|
123
|
+
const flagValueBranches = (spec: CommandSpec): Branch[] =>
|
|
124
|
+
spec.commands.flatMap((command) =>
|
|
125
|
+
command.flags
|
|
126
|
+
.filter(({ values }) => values.length > 0)
|
|
127
|
+
.flatMap((flag) =>
|
|
128
|
+
flagTokens(flag).map((token) => ({
|
|
129
|
+
pattern: `${command.path.join(' ')} ${token}`,
|
|
130
|
+
entries: flag.values.map((value) => `'${value}'`),
|
|
131
|
+
})),
|
|
132
|
+
),
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
const describeFlags = (flags: CommandFlag[]): string =>
|
|
136
|
+
flags
|
|
137
|
+
.flatMap((flag) =>
|
|
138
|
+
flagTokens(flag).map((token) => describe(token, flag.description)),
|
|
139
|
+
)
|
|
140
|
+
.join(' ')
|
|
141
|
+
|
|
142
|
+
const describe = (value: string, description: string): string => {
|
|
143
|
+
const summary = describeForShell(description)
|
|
144
|
+
return summary === '' ? `'${value}'` : `'${value}:${summary}'`
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const renderCase = (name: string, branches: Branch[]): string =>
|
|
148
|
+
[
|
|
149
|
+
`${name}() {`,
|
|
150
|
+
` case "$1" in`,
|
|
151
|
+
...branches.map(
|
|
152
|
+
({ pattern, entries }) =>
|
|
153
|
+
` ('${pattern}') _seam_reply+=(${entries.join(' ')}) ;;`,
|
|
154
|
+
),
|
|
155
|
+
` esac`,
|
|
156
|
+
`}`,
|
|
157
|
+
].join('\n')
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { getCommandBlueprintDef } from './get-command-blueprint-def.js'
|
|
2
|
+
import type { ContextHelpers } from './types.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The top level response key documented for a command,
|
|
6
|
+
* e.g., `devices` for `seam devices list`.
|
|
7
|
+
*
|
|
8
|
+
* Returns null when the command is not in the blueprint,
|
|
9
|
+
* or when it does not respond with a resource.
|
|
10
|
+
*/
|
|
11
|
+
export const getResponseKey = (
|
|
12
|
+
command: string[],
|
|
13
|
+
ctx: ContextHelpers,
|
|
14
|
+
): string | null => {
|
|
15
|
+
let endpoint
|
|
16
|
+
try {
|
|
17
|
+
endpoint = getCommandBlueprintDef(command, ctx)
|
|
18
|
+
} catch {
|
|
19
|
+
return null
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (endpoint.response.responseType === 'void') return null
|
|
23
|
+
|
|
24
|
+
return endpoint.response.responseKey
|
|
25
|
+
}
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import type { ActionAttemptsGetResponse } from '@seamapi/http/connect'
|
|
2
|
-
import prompts from 'prompts'
|
|
3
2
|
|
|
4
3
|
import { getSeam } from './get-seam.js'
|
|
4
|
+
import { getOutput } from './output/get-output.js'
|
|
5
|
+
import { prompt } from './util/prompt.js'
|
|
5
6
|
import { withLoading } from './util/with-loading.js'
|
|
6
7
|
|
|
7
8
|
export const interactForActionAttemptPoll = async (
|
|
8
9
|
action_attempt: ActionAttemptsGetResponse['action_attempt'],
|
|
9
10
|
) => {
|
|
10
11
|
if (action_attempt.status === 'pending') {
|
|
11
|
-
const { poll_for_action_attempt } = await
|
|
12
|
+
const { poll_for_action_attempt } = await prompt({
|
|
12
13
|
name: 'poll_for_action_attempt',
|
|
13
14
|
message: "Would you like to poll the action attempt until it's ready?",
|
|
14
15
|
type: 'toggle',
|
|
@@ -29,7 +30,7 @@ export const interactForActionAttemptPoll = async (
|
|
|
29
30
|
{ waitForActionAttempt: { pollingInterval: 240, timeout: 10_000 } },
|
|
30
31
|
),
|
|
31
32
|
)
|
|
32
|
-
|
|
33
|
+
getOutput().data({ action_attempt: updated_action_attempt })
|
|
33
34
|
}
|
|
34
35
|
}
|
|
35
36
|
}
|
|
@@ -1,19 +1,21 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { getOutput } from './output/get-output.js'
|
|
2
|
+
import { prompt } from './util/prompt.js'
|
|
2
3
|
|
|
3
4
|
export const interactForArray = async (
|
|
4
5
|
array: string[],
|
|
5
6
|
message: string,
|
|
6
7
|
): Promise<string[]> => {
|
|
7
8
|
const updatedArray = [...array]
|
|
9
|
+
const output = getOutput()
|
|
8
10
|
|
|
9
11
|
const displayList = () => {
|
|
10
|
-
|
|
12
|
+
output.info(`${message} Current list:`)
|
|
11
13
|
if (updatedArray.length > 0) {
|
|
12
14
|
updatedArray.forEach((item, index) => {
|
|
13
|
-
|
|
15
|
+
output.info(`${index + 1}: ${item}`)
|
|
14
16
|
})
|
|
15
17
|
} else {
|
|
16
|
-
|
|
18
|
+
output.info('The list is currently empty.')
|
|
17
19
|
}
|
|
18
20
|
}
|
|
19
21
|
|
|
@@ -21,7 +23,7 @@ export const interactForArray = async (
|
|
|
21
23
|
do {
|
|
22
24
|
displayList()
|
|
23
25
|
|
|
24
|
-
const response = await
|
|
26
|
+
const response = await prompt({
|
|
25
27
|
type: 'select',
|
|
26
28
|
name: 'action',
|
|
27
29
|
message: 'Choose an action:',
|
|
@@ -35,7 +37,7 @@ export const interactForArray = async (
|
|
|
35
37
|
action = response.action
|
|
36
38
|
|
|
37
39
|
if (action === 'add') {
|
|
38
|
-
const { newItem } = await
|
|
40
|
+
const { newItem } = await prompt({
|
|
39
41
|
type: 'text',
|
|
40
42
|
name: 'newItem',
|
|
41
43
|
message: 'Enter the new item:',
|
|
@@ -44,7 +46,7 @@ export const interactForArray = async (
|
|
|
44
46
|
updatedArray.push(newItem)
|
|
45
47
|
}
|
|
46
48
|
} else if (action === 'remove') {
|
|
47
|
-
const { index } = await
|
|
49
|
+
const { index } = await prompt({
|
|
48
50
|
type: 'number',
|
|
49
51
|
name: 'index',
|
|
50
52
|
message: 'Enter the index of the item to remove:',
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { Parameter } from '@seamapi/blueprint'
|
|
2
|
-
import prompts from 'prompts'
|
|
3
2
|
|
|
4
3
|
import { interactForAccessCode } from './interact-for-access-code.js'
|
|
5
4
|
import { interactForAcsEntrance } from './interact-for-acs-entrance.js'
|
|
@@ -11,9 +10,11 @@ import { interactForCustomMetadata } from './interact-for-custom-metadata.js'
|
|
|
11
10
|
import { interactForDevice } from './interact-for-device.js'
|
|
12
11
|
import { interactForTimestamp } from './interact-for-timestamp.js'
|
|
13
12
|
import { interactForUserIdentity } from './interact-for-user-identity.js'
|
|
13
|
+
import { getOutput } from './output/get-output.js'
|
|
14
14
|
import type { ContextHelpers } from './types.js'
|
|
15
15
|
import { NonInteractiveError, toArgName } from './util/cli-args.js'
|
|
16
16
|
import { ellipsis } from './util/ellipsis.js'
|
|
17
|
+
import { prompt } from './util/prompt.js'
|
|
17
18
|
|
|
18
19
|
const ergonomicPropOrder = [
|
|
19
20
|
'name',
|
|
@@ -82,8 +83,8 @@ export const interactForBlueprintObject = async (
|
|
|
82
83
|
? `Editing "${args.subPropertyPath}"`
|
|
83
84
|
: `[${cmdPath}] Parameters`
|
|
84
85
|
|
|
85
|
-
|
|
86
|
-
const { paramToEdit } = await
|
|
86
|
+
getOutput().info()
|
|
87
|
+
const { paramToEdit } = await prompt({
|
|
87
88
|
name: 'paramToEdit',
|
|
88
89
|
message: parameterSelectionMessage,
|
|
89
90
|
type: 'autocomplete',
|
|
@@ -203,7 +204,7 @@ export const interactForBlueprintObject = async (
|
|
|
203
204
|
value = await interactForTimestamp()
|
|
204
205
|
} else {
|
|
205
206
|
value = (
|
|
206
|
-
await
|
|
207
|
+
await prompt({
|
|
207
208
|
name: 'value',
|
|
208
209
|
message: `${paramToEdit}:`,
|
|
209
210
|
type: 'text',
|
|
@@ -214,7 +215,7 @@ export const interactForBlueprintObject = async (
|
|
|
214
215
|
return interactForBlueprintObject(args, ctx)
|
|
215
216
|
} else if (prop.format === 'enum') {
|
|
216
217
|
const value = (
|
|
217
|
-
await
|
|
218
|
+
await prompt({
|
|
218
219
|
name: 'value',
|
|
219
220
|
message: `${paramToEdit}:`,
|
|
220
221
|
type: 'select',
|
|
@@ -227,7 +228,7 @@ export const interactForBlueprintObject = async (
|
|
|
227
228
|
args.params[paramToEdit] = value
|
|
228
229
|
return interactForBlueprintObject(args, ctx)
|
|
229
230
|
} else if (prop.format === 'boolean') {
|
|
230
|
-
const { value } = await
|
|
231
|
+
const { value } = await prompt({
|
|
231
232
|
name: 'value',
|
|
232
233
|
message: `${paramToEdit}:`,
|
|
233
234
|
type: 'toggle',
|
|
@@ -241,7 +242,7 @@ export const interactForBlueprintObject = async (
|
|
|
241
242
|
return interactForBlueprintObject(args, ctx)
|
|
242
243
|
} else if (prop.format === 'list' && prop.itemFormat === 'enum') {
|
|
243
244
|
const value = (
|
|
244
|
-
await
|
|
245
|
+
await prompt({
|
|
245
246
|
name: 'value',
|
|
246
247
|
message: `${paramToEdit}:`,
|
|
247
248
|
type: 'autocompleteMultiselect',
|
|
@@ -272,7 +273,7 @@ export const interactForBlueprintObject = async (
|
|
|
272
273
|
)
|
|
273
274
|
return interactForBlueprintObject(args, ctx)
|
|
274
275
|
} else if (prop.format === 'number') {
|
|
275
|
-
const { value } = await
|
|
276
|
+
const { value } = await prompt({
|
|
276
277
|
name: 'value',
|
|
277
278
|
message: `${paramToEdit}:`,
|
|
278
279
|
type: 'number',
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { isDeepStrictEqual as isEqual } from 'node:util'
|
|
2
2
|
|
|
3
|
-
import prompts from 'prompts'
|
|
4
|
-
|
|
5
3
|
import type { ContextHelpers } from './types.js'
|
|
6
4
|
import { NonInteractiveError } from './util/cli-args.js'
|
|
5
|
+
import { prompt } from './util/prompt.js'
|
|
7
6
|
|
|
8
7
|
const uniqBy = <T>(items: T[], keyOf: (item: T) => unknown): T[] => {
|
|
9
8
|
const seen = new Set<unknown>()
|
|
@@ -93,7 +92,7 @@ export async function interactForCommandSelection(
|
|
|
93
92
|
|
|
94
93
|
const commandPathStr = commandPath.join('/').replace(/-/g, '_')
|
|
95
94
|
|
|
96
|
-
const res = await
|
|
95
|
+
const res = await prompt({
|
|
97
96
|
name: 'Command',
|
|
98
97
|
type: 'autocomplete',
|
|
99
98
|
choices: [
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { getOutput } from './output/get-output.js'
|
|
2
|
+
import { prompt } from './util/prompt.js'
|
|
2
3
|
|
|
3
4
|
// Structurally the CustomMetadata of @seamapi/types, spelled out here so the
|
|
4
5
|
// published declarations do not depend on a development-only package.
|
|
@@ -12,15 +13,16 @@ export const interactForCustomMetadata = async (
|
|
|
12
13
|
custom_metadata: CustomMetadata,
|
|
13
14
|
) => {
|
|
14
15
|
const updated_custom_metadata: UpdatedCustomMetadata = { ...custom_metadata }
|
|
16
|
+
const output = getOutput()
|
|
15
17
|
|
|
16
18
|
const displayCurrentCustomMetadata = () => {
|
|
17
|
-
|
|
19
|
+
output.info('custom_metadata:')
|
|
18
20
|
if (Object.keys(updated_custom_metadata).length > 0) {
|
|
19
21
|
Object.keys(updated_custom_metadata).forEach((key, index) => {
|
|
20
|
-
|
|
22
|
+
output.info(`${index + 1}: ${key}: ${updated_custom_metadata[key]}`)
|
|
21
23
|
})
|
|
22
24
|
} else {
|
|
23
|
-
|
|
25
|
+
output.info('The custom metadata param is empty.')
|
|
24
26
|
}
|
|
25
27
|
}
|
|
26
28
|
|
|
@@ -29,7 +31,7 @@ export const interactForCustomMetadata = async (
|
|
|
29
31
|
do {
|
|
30
32
|
displayCurrentCustomMetadata()
|
|
31
33
|
|
|
32
|
-
const response = await
|
|
34
|
+
const response = await prompt({
|
|
33
35
|
type: 'select',
|
|
34
36
|
name: 'action',
|
|
35
37
|
message: 'Choose an action:',
|
|
@@ -43,13 +45,13 @@ export const interactForCustomMetadata = async (
|
|
|
43
45
|
action = response.action
|
|
44
46
|
|
|
45
47
|
if (action === 'add') {
|
|
46
|
-
const { newKey } = await
|
|
48
|
+
const { newKey } = await prompt({
|
|
47
49
|
type: 'text',
|
|
48
50
|
name: 'newKey',
|
|
49
51
|
message: 'Enter a key to add or edit:',
|
|
50
52
|
})
|
|
51
53
|
|
|
52
|
-
let { newValue } = await
|
|
54
|
+
let { newValue } = await prompt({
|
|
53
55
|
type: 'text',
|
|
54
56
|
name: 'newValue',
|
|
55
57
|
message: 'Enter the new value to add or edit (or null to delete):',
|
|
@@ -65,7 +67,7 @@ export const interactForCustomMetadata = async (
|
|
|
65
67
|
}
|
|
66
68
|
}
|
|
67
69
|
} else if (action === 'remove') {
|
|
68
|
-
const { custom_key_to_remove } = await
|
|
70
|
+
const { custom_key_to_remove } = await prompt({
|
|
69
71
|
type: 'select',
|
|
70
72
|
name: 'custom_key_to_remove',
|
|
71
73
|
message: 'Choose a key-value pair to remove from params:',
|
|
@@ -1,33 +1,35 @@
|
|
|
1
1
|
import { isApiKey, isPersonalAccessToken } from '@seamapi/http/connect'
|
|
2
2
|
import chalk from 'chalk'
|
|
3
|
-
import prompts from 'prompts'
|
|
4
3
|
|
|
5
4
|
import { getConfigStore } from './config/index.js'
|
|
6
5
|
import { getServer } from './get-server.js'
|
|
7
6
|
import { interactForWorkspaceId } from './interact-for-workspace-id.js'
|
|
7
|
+
import { getOutput } from './output/get-output.js'
|
|
8
|
+
import { prompt } from './util/prompt.js'
|
|
8
9
|
import { withLoading } from './util/with-loading.js'
|
|
9
10
|
import { validateToken } from './validate-token.js'
|
|
10
11
|
|
|
11
12
|
export const interactForLogin = async () => {
|
|
12
13
|
const config = await getConfigStore()
|
|
14
|
+
const output = getOutput()
|
|
13
15
|
|
|
14
16
|
if (getServer().includes('localhost')) {
|
|
15
|
-
|
|
17
|
+
output.info(
|
|
16
18
|
`You're using a local Seam Connect instance, you can enter the API Key to your local user, you can create a new user from:\n\n${getServer()}/admin/create_user_with_api_key`,
|
|
17
19
|
)
|
|
18
20
|
} else {
|
|
19
|
-
|
|
21
|
+
output.info(
|
|
20
22
|
`To login, navigate to the URL below and create a new Personal Access Token (PAT) and paste the PAT in the provided box:\n\nhttps://console.seam.co/settings/access-tokens\n\n`,
|
|
21
23
|
)
|
|
22
24
|
}
|
|
23
25
|
|
|
24
|
-
|
|
26
|
+
output.info(
|
|
25
27
|
chalk.gray(
|
|
26
28
|
'> Note: You can enter an API Key here for single-workspace access',
|
|
27
29
|
),
|
|
28
30
|
)
|
|
29
31
|
|
|
30
|
-
const { pat } = await
|
|
32
|
+
const { pat } = await prompt({
|
|
31
33
|
name: 'pat',
|
|
32
34
|
type: 'text',
|
|
33
35
|
message: 'Personal Access Token:',
|
|
@@ -49,5 +51,5 @@ export const interactForLogin = async () => {
|
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
config.set(`${getServer()}.pat`, token)
|
|
52
|
-
|
|
54
|
+
output.info(`Token saved! You may begin using the CLI!`)
|
|
53
55
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import { prompt } from './util/prompt.js'
|
|
3
2
|
import { withLoading } from './util/with-loading.js'
|
|
4
3
|
|
|
5
4
|
export interface ResourceChoice {
|
|
@@ -23,7 +22,7 @@ export const interactForResource = async <Resource>({
|
|
|
23
22
|
`Fetching ${resourceName.replace(/_/g, ' ')}s...`,
|
|
24
23
|
fetchResources,
|
|
25
24
|
)
|
|
26
|
-
const { resourceId } = await
|
|
25
|
+
const { resourceId } = await prompt({
|
|
27
26
|
name: 'resourceId',
|
|
28
27
|
type: 'autocomplete',
|
|
29
28
|
message,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { randomBytes } from 'node:crypto'
|
|
2
2
|
|
|
3
|
-
import prompts from 'prompts'
|
|
4
|
-
|
|
5
3
|
import { getConfigStore } from './config/index.js'
|
|
6
4
|
import { getServer } from './get-server.js'
|
|
5
|
+
import { getOutput } from './output/get-output.js'
|
|
6
|
+
import { prompt } from './util/prompt.js'
|
|
7
7
|
|
|
8
8
|
export async function interactForServerSelection() {
|
|
9
9
|
const servers = [
|
|
@@ -12,7 +12,7 @@ export async function interactForServerSelection() {
|
|
|
12
12
|
'https://fakeseamconnect.seam.vc',
|
|
13
13
|
]
|
|
14
14
|
|
|
15
|
-
const { server } = await
|
|
15
|
+
const { server } = await prompt([
|
|
16
16
|
{
|
|
17
17
|
type: 'select',
|
|
18
18
|
name: 'server',
|
|
@@ -22,8 +22,9 @@ export async function interactForServerSelection() {
|
|
|
22
22
|
])
|
|
23
23
|
|
|
24
24
|
const config = getConfigStore()
|
|
25
|
+
const output = getOutput()
|
|
25
26
|
if (server === servers[2]) {
|
|
26
|
-
let { userUrlSeed } = await
|
|
27
|
+
let { userUrlSeed } = await prompt([
|
|
27
28
|
{
|
|
28
29
|
type: 'text',
|
|
29
30
|
name: 'userUrlSeed',
|
|
@@ -37,10 +38,10 @@ export async function interactForServerSelection() {
|
|
|
37
38
|
}
|
|
38
39
|
config.set('server', `https://${userUrlSeed}.fakeseamconnect.seam.vc`)
|
|
39
40
|
config.set(`${getServer()}.pat`, `seam_apikey1_token`)
|
|
40
|
-
|
|
41
|
+
output.info(`PAT set to use fakeseamconnect with "seam_apikey1_token"`)
|
|
41
42
|
} else {
|
|
42
43
|
config.set('server', server)
|
|
43
44
|
}
|
|
44
45
|
config.delete('current_workspace_id')
|
|
45
|
-
|
|
46
|
+
output.info(`Server set to ${server}`)
|
|
46
47
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { prompt } from './util/prompt.js'
|
|
2
2
|
export const interactForTimestamp = async () => {
|
|
3
|
-
const { timestamp } = await
|
|
3
|
+
const { timestamp } = await prompt({
|
|
4
4
|
name: 'timestamp',
|
|
5
5
|
type: 'date',
|
|
6
6
|
message: 'Enter a timestamp:',
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import prompts from 'prompts'
|
|
2
|
-
|
|
3
1
|
import { getConfigStore } from './config/index.js'
|
|
2
|
+
import { getOutput } from './output/get-output.js'
|
|
3
|
+
import { prompt } from './util/prompt.js'
|
|
4
4
|
|
|
5
5
|
export async function interactForUseRemoteApiDefs() {
|
|
6
|
-
const { use_remote_api_defs } = await
|
|
6
|
+
const { use_remote_api_defs } = await prompt([
|
|
7
7
|
{
|
|
8
8
|
type: 'select',
|
|
9
9
|
name: 'use_remote_api_defs',
|
|
@@ -23,5 +23,5 @@ export async function interactForUseRemoteApiDefs() {
|
|
|
23
23
|
|
|
24
24
|
const config = getConfigStore()
|
|
25
25
|
config.set('use_remote_api_defs', use_remote_api_defs)
|
|
26
|
-
|
|
26
|
+
getOutput().info(`Use remote API Definitions: ${use_remote_api_defs}`)
|
|
27
27
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { SeamHttpWithoutWorkspace } from '@seamapi/http/connect'
|
|
2
|
-
import prompts from 'prompts'
|
|
3
2
|
|
|
4
3
|
import { getConfigStore } from './config/index.js'
|
|
5
4
|
import { getSeamMultiWorkspace } from './get-seam.js'
|
|
6
5
|
import { getServer } from './get-server.js'
|
|
6
|
+
import { prompt } from './util/prompt.js'
|
|
7
7
|
import { withLoading } from './util/with-loading.js'
|
|
8
8
|
|
|
9
9
|
export const interactForWorkspaceId = async (personalAccessToken?: string) => {
|
|
@@ -17,7 +17,7 @@ export const interactForWorkspaceId = async (personalAccessToken?: string) => {
|
|
|
17
17
|
const workspaces = await withLoading('Fetching workspaces...', () =>
|
|
18
18
|
seam.workspaces.list(),
|
|
19
19
|
)
|
|
20
|
-
const { workspaceId } = await
|
|
20
|
+
const { workspaceId } = await prompt({
|
|
21
21
|
name: 'workspaceId',
|
|
22
22
|
type: 'select',
|
|
23
23
|
message: 'Select a workspace:',
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createOutput,
|
|
3
|
+
type CreateOutputOptions,
|
|
4
|
+
type Output,
|
|
5
|
+
type OutputStream,
|
|
6
|
+
} from './create-output.js'
|
|
7
|
+
|
|
8
|
+
export interface MemoryOutput {
|
|
9
|
+
output: Output
|
|
10
|
+
/** Everything written to stdout so far. */
|
|
11
|
+
stdout: () => string
|
|
12
|
+
/** Everything written to stderr so far. */
|
|
13
|
+
stderr: () => string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const createMemoryStream = (): OutputStream & { read: () => string } => {
|
|
17
|
+
const chunks: string[] = []
|
|
18
|
+
return {
|
|
19
|
+
write: (chunk: string) => chunks.push(chunk),
|
|
20
|
+
read: () => chunks.join(''),
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* An {@link Output} that captures writes in memory instead of
|
|
26
|
+
* touching the process streams, for asserting on CLI output.
|
|
27
|
+
*/
|
|
28
|
+
export const createMemoryOutput = (
|
|
29
|
+
options: Omit<CreateOutputOptions, 'stdout' | 'stderr'> = {},
|
|
30
|
+
): MemoryOutput => {
|
|
31
|
+
const stdout = createMemoryStream()
|
|
32
|
+
const stderr = createMemoryStream()
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
output: createOutput({ ...options, stdout, stderr }),
|
|
36
|
+
stdout: stdout.read,
|
|
37
|
+
stderr: stderr.read,
|
|
38
|
+
}
|
|
39
|
+
}
|