@seamapi/cli 0.11.0 → 0.13.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 +71 -0
- package/bin/cli.js +38 -98
- package/bin/cli.js.map +1 -1
- package/completions/seam.bash +16 -0
- package/completions/seam.fish +15 -0
- package/completions/seam.zsh +19 -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 +28 -0
- package/lib/completion/index.js +92 -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/render-help.d.ts +8 -0
- package/lib/render-help.js +150 -0
- package/lib/render-help.js.map +1 -0
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +2 -1
- package/src/bin/cli.ts +54 -104
- package/src/lib/command-spec.ts +400 -0
- package/src/lib/completion/describe.ts +21 -0
- package/src/lib/completion/index.ts +110 -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/render-help.ts +197 -0
- package/src/lib/version.ts +1 -1
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { flagTokens, } from '../command-spec.js';
|
|
2
|
+
import { describeForShell } from './describe.js';
|
|
3
|
+
export const renderZshCompletion = (spec) => {
|
|
4
|
+
const valuelessTokens = spec.globalFlags
|
|
5
|
+
.filter(({ takesValue }) => !takesValue)
|
|
6
|
+
.flatMap(flagTokens)
|
|
7
|
+
.sort();
|
|
8
|
+
return `${[
|
|
9
|
+
header,
|
|
10
|
+
renderCase('_seam_subcommands', subcommandBranches(spec)),
|
|
11
|
+
renderCase('_seam_flags', flagBranches(spec)),
|
|
12
|
+
renderCase('_seam_flag_values', flagValueBranches(spec)),
|
|
13
|
+
`_seam_global_flags() {\n _seam_reply+=(${describeFlags(spec.globalFlags)})\n}`,
|
|
14
|
+
completionFunction(valuelessTokens),
|
|
15
|
+
dispatch,
|
|
16
|
+
].join('\n\n')}\n`;
|
|
17
|
+
};
|
|
18
|
+
const header = `#compdef seam
|
|
19
|
+
|
|
20
|
+
# zsh completion for the seam command.
|
|
21
|
+
#
|
|
22
|
+
# Generated by @seamapi/cli from the Seam API definitions.
|
|
23
|
+
# Do not edit: regenerate with 'seam completion zsh'.
|
|
24
|
+
#
|
|
25
|
+
# Load it for the current shell with
|
|
26
|
+
#
|
|
27
|
+
# source <(seam completion zsh)
|
|
28
|
+
#
|
|
29
|
+
# or install it for every shell with
|
|
30
|
+
#
|
|
31
|
+
# seam completion zsh > "\${fpath[1]}/_seam"`;
|
|
32
|
+
const completionFunction = (valuelessTokens) => `_seam() {
|
|
33
|
+
local -a _seam_reply
|
|
34
|
+
local -a valueless
|
|
35
|
+
local command previous word
|
|
36
|
+
local -i index
|
|
37
|
+
|
|
38
|
+
valueless=(${valuelessTokens.join(' ')})
|
|
39
|
+
|
|
40
|
+
# The command path is the run of words before the first flag.
|
|
41
|
+
command=''
|
|
42
|
+
for (( index = 2; index < CURRENT; index++ )); do
|
|
43
|
+
word="\${words[index]}"
|
|
44
|
+
if [[ "$word" == -* ]]; then
|
|
45
|
+
break
|
|
46
|
+
fi
|
|
47
|
+
command="\${command:+$command }$word"
|
|
48
|
+
done
|
|
49
|
+
|
|
50
|
+
previous=''
|
|
51
|
+
if (( CURRENT > 1 )); then
|
|
52
|
+
previous="\${words[CURRENT - 1]}"
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
# Completing the value of a flag that takes one.
|
|
56
|
+
if [[ "$previous" == -* ]] && (( \${valueless[(Ie)$previous]} == 0 )); then
|
|
57
|
+
_seam_flag_values "$command $previous"
|
|
58
|
+
if (( \${#_seam_reply} )); then
|
|
59
|
+
_describe -t values 'value' _seam_reply
|
|
60
|
+
fi
|
|
61
|
+
return
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
if [[ "\${words[CURRENT]}" == -* ]]; then
|
|
65
|
+
_seam_flags "$command"
|
|
66
|
+
_seam_global_flags
|
|
67
|
+
_describe -t options 'option' _seam_reply
|
|
68
|
+
return
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
_seam_subcommands "$command"
|
|
72
|
+
if (( \${#_seam_reply} )); then
|
|
73
|
+
_describe -t commands 'command' _seam_reply
|
|
74
|
+
return
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
_seam_flags "$command"
|
|
78
|
+
_seam_global_flags
|
|
79
|
+
_describe -t options 'option' _seam_reply
|
|
80
|
+
}`;
|
|
81
|
+
// The script runs in three ways. Autoloaded from fpath as _seam, it must
|
|
82
|
+
// complete the in-flight request: funcstack holds _seam. Evaluated by the
|
|
83
|
+
// loader stub inside the autoloaded _seam, the same applies, but eval pushes
|
|
84
|
+
// '(eval)' onto funcstack, so search the whole stack rather than the top.
|
|
85
|
+
// Sourced into a shell, funcstack holds no _seam: register with compdef.
|
|
86
|
+
const dispatch = `if (( \${funcstack[(I)_seam]} )); then
|
|
87
|
+
_seam "$@"
|
|
88
|
+
else
|
|
89
|
+
compdef _seam seam
|
|
90
|
+
fi`;
|
|
91
|
+
const subcommandBranches = (spec) => spec.groups.map((group) => ({
|
|
92
|
+
pattern: group.path.join(' '),
|
|
93
|
+
entries: group.subcommands.map(({ name, description }) => describe(name, description)),
|
|
94
|
+
}));
|
|
95
|
+
const flagBranches = (spec) => spec.commands
|
|
96
|
+
.filter(({ flags }) => flags.length > 0)
|
|
97
|
+
.map((command) => ({
|
|
98
|
+
pattern: command.path.join(' '),
|
|
99
|
+
entries: [describeFlags(command.flags)],
|
|
100
|
+
}));
|
|
101
|
+
const flagValueBranches = (spec) => spec.commands.flatMap((command) => command.flags
|
|
102
|
+
.filter(({ values }) => values.length > 0)
|
|
103
|
+
.flatMap((flag) => flagTokens(flag).map((token) => ({
|
|
104
|
+
pattern: `${command.path.join(' ')} ${token}`,
|
|
105
|
+
entries: flag.values.map((value) => `'${value}'`),
|
|
106
|
+
}))));
|
|
107
|
+
const describeFlags = (flags) => flags
|
|
108
|
+
.flatMap((flag) => flagTokens(flag).map((token) => describe(token, flag.description)))
|
|
109
|
+
.join(' ');
|
|
110
|
+
const describe = (value, description) => {
|
|
111
|
+
const summary = describeForShell(description);
|
|
112
|
+
return summary === '' ? `'${value}'` : `'${value}:${summary}'`;
|
|
113
|
+
};
|
|
114
|
+
const renderCase = (name, branches) => [
|
|
115
|
+
`${name}() {`,
|
|
116
|
+
` case "$1" in`,
|
|
117
|
+
...branches.map(({ pattern, entries }) => ` ('${pattern}') _seam_reply+=(${entries.join(' ')}) ;;`),
|
|
118
|
+
` esac`,
|
|
119
|
+
`}`,
|
|
120
|
+
].join('\n');
|
|
121
|
+
//# sourceMappingURL=render-zsh.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render-zsh.js","sourceRoot":"","sources":["../../src/lib/completion/render-zsh.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type CommandSpec } from './command-spec.js';
|
|
2
|
+
/**
|
|
3
|
+
* Render the help guide for a command path, or `null` when no command or
|
|
4
|
+
* group goes by that path.
|
|
5
|
+
*
|
|
6
|
+
* An empty path is the guide for `seam` itself.
|
|
7
|
+
*/
|
|
8
|
+
export declare const renderHelp: (path: string[], spec: CommandSpec) => string | null;
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import commandLineUsage, {} from 'command-line-usage';
|
|
2
|
+
import { findCommand, findGroup, } from './command-spec.js';
|
|
3
|
+
/**
|
|
4
|
+
* Render the help guide for a command path, or `null` when no command or
|
|
5
|
+
* group goes by that path.
|
|
6
|
+
*
|
|
7
|
+
* An empty path is the guide for `seam` itself.
|
|
8
|
+
*/
|
|
9
|
+
export const renderHelp = (path, spec) => {
|
|
10
|
+
const group = findGroup(spec, path);
|
|
11
|
+
if (group != null)
|
|
12
|
+
return commandLineUsage(groupSections(group, spec));
|
|
13
|
+
const command = findCommand(spec, path);
|
|
14
|
+
if (command != null)
|
|
15
|
+
return commandLineUsage(commandSections(command, spec));
|
|
16
|
+
return null;
|
|
17
|
+
};
|
|
18
|
+
const overview = 'Every seam command runs as soon as every required property is given, and otherwise prompts you for what is missing with helpful suggestions. Pass -i to always review properties first, or -y to never be prompted.';
|
|
19
|
+
const outputSection = {
|
|
20
|
+
header: 'Output',
|
|
21
|
+
content: [
|
|
22
|
+
'Only the response is written to stdout, so it is safe to pipe. Prompts, progress, and other information are written to stderr.',
|
|
23
|
+
'The response is trimmed to the response key and pagination.',
|
|
24
|
+
'Request params may be piped or redirected in as a JSON object. Params given as arguments win over params read from stdin.',
|
|
25
|
+
],
|
|
26
|
+
};
|
|
27
|
+
const examples = [
|
|
28
|
+
{ name: 'seam', summary: 'Interactively select commands to execute.' },
|
|
29
|
+
{ name: 'seam login', summary: 'Login to Seam.' },
|
|
30
|
+
{ name: 'seam wizard', summary: 'Set up Seam in the current project.' },
|
|
31
|
+
{ name: 'seam select workspace', summary: 'Select your workspace.' },
|
|
32
|
+
{
|
|
33
|
+
name: 'seam connect-webviews create',
|
|
34
|
+
summary: 'Create a connect webview to connect devices.',
|
|
35
|
+
},
|
|
36
|
+
{ name: 'seam devices list', summary: 'List devices in your workspace.' },
|
|
37
|
+
{
|
|
38
|
+
name: 'seam devices list {bold --interactive}',
|
|
39
|
+
summary: 'Review and edit filters before listing devices.',
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: 'seam devices list {bold --non-interactive}',
|
|
43
|
+
summary: 'List devices, failing instead of prompting.',
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: 'seam locks unlock-door {bold --device-id} $MY_DOOR',
|
|
47
|
+
summary: 'Unlock a lock.',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: "seam access-codes create {bold --code} '1234' {bold --name} 'My Code'",
|
|
51
|
+
summary: 'Create an access code.',
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: 'seam devices list > devices.json',
|
|
55
|
+
summary: 'Write the response to a file as JSON.',
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: 'cat params.json | seam locks unlock-door',
|
|
59
|
+
summary: 'Pipe request params in as JSON.',
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: 'seam completion bash',
|
|
63
|
+
summary: 'Print a shell completion script for bash, fish, or zsh.',
|
|
64
|
+
},
|
|
65
|
+
];
|
|
66
|
+
const groupSections = (group, spec) => {
|
|
67
|
+
const isRoot = group.path.length === 0;
|
|
68
|
+
const name = ['seam', ...group.path].join(' ');
|
|
69
|
+
return [
|
|
70
|
+
isRoot
|
|
71
|
+
? { header: 'Seam CLI', content: overview }
|
|
72
|
+
: { header: name, content: `Commands under ${name}.` },
|
|
73
|
+
{ header: 'Usage', content: `${name} <command> [options]` },
|
|
74
|
+
...commandSectionsForGroup(group, isRoot),
|
|
75
|
+
optionSection(spec.globalFlags),
|
|
76
|
+
...(isRoot
|
|
77
|
+
? [outputSection, { header: 'Command List Examples', content: examples }]
|
|
78
|
+
: []),
|
|
79
|
+
{ content: `Run '${name} <command> --help' to see a command in detail.` },
|
|
80
|
+
];
|
|
81
|
+
};
|
|
82
|
+
const commandSectionsForGroup = (group, isRoot) => {
|
|
83
|
+
const content = (subcommands) => subcommands.map(({ name, description }) => ({ name, summary: description }));
|
|
84
|
+
// The root guide separates the commands of the CLI itself from the commands
|
|
85
|
+
// that call the Seam API. Anywhere deeper the split adds nothing: a group
|
|
86
|
+
// holds commands of one kind.
|
|
87
|
+
if (!isRoot) {
|
|
88
|
+
return [{ header: 'Commands', content: content(group.subcommands) }];
|
|
89
|
+
}
|
|
90
|
+
const cli = group.subcommands.filter(({ kind }) => kind === 'cli');
|
|
91
|
+
const api = group.subcommands.filter(({ kind }) => kind === 'api');
|
|
92
|
+
return [
|
|
93
|
+
{ header: 'Commands', content: content(cli) },
|
|
94
|
+
{ header: 'API Commands', content: content(api) },
|
|
95
|
+
].filter((section) => section.content.length > 0);
|
|
96
|
+
};
|
|
97
|
+
const commandSections = (command, spec) => {
|
|
98
|
+
const name = ['seam', ...command.path].join(' ');
|
|
99
|
+
const hasFlags = command.flags.length > 0;
|
|
100
|
+
return [
|
|
101
|
+
{
|
|
102
|
+
header: name,
|
|
103
|
+
content: [command.title, command.description].filter((line) => line !== ''),
|
|
104
|
+
},
|
|
105
|
+
{ header: 'Usage', content: `${name} [options]` },
|
|
106
|
+
// The command's own parameters are what the request is made of, so keep
|
|
107
|
+
// them apart from the options every seam command takes.
|
|
108
|
+
...(hasFlags ? [optionSection(command.flags, 'Parameters')] : []),
|
|
109
|
+
optionSection(spec.globalFlags),
|
|
110
|
+
...(hasFlags
|
|
111
|
+
? [
|
|
112
|
+
{
|
|
113
|
+
content: 'Any required parameter left out is prompted for interactively.',
|
|
114
|
+
},
|
|
115
|
+
]
|
|
116
|
+
: []),
|
|
117
|
+
];
|
|
118
|
+
};
|
|
119
|
+
const optionSection = (flags, header = 'Options') => ({
|
|
120
|
+
header,
|
|
121
|
+
optionList: flags.map(toOptionDefinition),
|
|
122
|
+
});
|
|
123
|
+
const maxDocumentedValues = 8;
|
|
124
|
+
const toOptionDefinition = (flag) => {
|
|
125
|
+
const description = [
|
|
126
|
+
flag.isRequired ? '{bold [required]}' : '',
|
|
127
|
+
flag.description,
|
|
128
|
+
describeValues(flag),
|
|
129
|
+
]
|
|
130
|
+
.filter((part) => part !== '')
|
|
131
|
+
.join(' ');
|
|
132
|
+
return {
|
|
133
|
+
// command-line-usage renders a nameless option as the short form alone.
|
|
134
|
+
name: flag.long ?? '',
|
|
135
|
+
...(flag.short == null ? {} : { alias: flag.short }),
|
|
136
|
+
// A flag with no value must be typed as a boolean, or the guide labels it
|
|
137
|
+
// as taking a string.
|
|
138
|
+
type: flag.takesValue ? String : Boolean,
|
|
139
|
+
...(flag.takesValue ? { typeLabel: '{underline value}' } : {}),
|
|
140
|
+
description,
|
|
141
|
+
};
|
|
142
|
+
};
|
|
143
|
+
const describeValues = (flag) => {
|
|
144
|
+
if (flag.values.length === 0)
|
|
145
|
+
return '';
|
|
146
|
+
const shown = flag.values.slice(0, maxDocumentedValues).join(', ');
|
|
147
|
+
const rest = flag.values.length - maxDocumentedValues;
|
|
148
|
+
return rest > 0 ? `One of: ${shown}, and ${rest} more.` : `One of: ${shown}.`;
|
|
149
|
+
};
|
|
150
|
+
//# sourceMappingURL=render-help.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render-help.js","sourceRoot":"","sources":["../src/lib/render-help.ts"],"names":[],"mappings":"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"}
|
package/lib/version.d.ts
CHANGED
package/lib/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seamapi/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "A command line interface (CLI) for interacting with the Seam API.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"index.js.map",
|
|
46
46
|
"index.d.ts",
|
|
47
47
|
"bin",
|
|
48
|
+
"completions",
|
|
48
49
|
"lib",
|
|
49
50
|
"src",
|
|
50
51
|
"!test",
|
package/src/bin/cli.ts
CHANGED
|
@@ -3,9 +3,14 @@ import { randomBytes } from 'node:crypto'
|
|
|
3
3
|
import { isDeepStrictEqual as isEqual } from 'node:util'
|
|
4
4
|
|
|
5
5
|
import chalk from 'chalk'
|
|
6
|
-
import commandLineUsage from 'command-line-usage'
|
|
7
6
|
import type { ParsedArgs } from 'minimist'
|
|
8
7
|
|
|
8
|
+
import { getCommandSpec } from 'lib/command-spec.js'
|
|
9
|
+
import {
|
|
10
|
+
completionShells,
|
|
11
|
+
isCompletionShell,
|
|
12
|
+
renderCompletion,
|
|
13
|
+
} from 'lib/completion/index.js'
|
|
9
14
|
import { getConfigStore } from 'lib/config/index.js'
|
|
10
15
|
import { getApiBlueprint } from 'lib/get-api-blueprint.js'
|
|
11
16
|
import { getResponseKey } from 'lib/get-response-key.js'
|
|
@@ -20,6 +25,7 @@ import { interactForWorkspaceId } from 'lib/interact-for-workspace-id.js'
|
|
|
20
25
|
import { createOutput } from 'lib/output/create-output.js'
|
|
21
26
|
import { getOutput, setOutput } from 'lib/output/get-output.js'
|
|
22
27
|
import { resolveOutputFormat } from 'lib/output/resolve-output-format.js'
|
|
28
|
+
import { renderHelp } from 'lib/render-help.js'
|
|
23
29
|
import type { ContextHelpers } from 'lib/types.js'
|
|
24
30
|
import {
|
|
25
31
|
cliFlags,
|
|
@@ -34,109 +40,35 @@ import { RequestSeamApi } from 'lib/util/request-seam-api.js'
|
|
|
34
40
|
import { validateToken } from 'lib/validate-token.js'
|
|
35
41
|
import seamapiCliVersion from 'lib/version.js'
|
|
36
42
|
|
|
37
|
-
const sections = [
|
|
38
|
-
{
|
|
39
|
-
header: 'Seam CLI',
|
|
40
|
-
content:
|
|
41
|
-
'Every seam command runs as soon as every required property is given, and otherwise prompts you for what is missing with helpful suggestions. Pass -i to always review properties first, or -y to never be prompted. ',
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
header: 'Options',
|
|
45
|
-
optionList: [
|
|
46
|
-
{
|
|
47
|
-
name: 'help',
|
|
48
|
-
description: 'Display this help guide.',
|
|
49
|
-
alias: 'h',
|
|
50
|
-
type: Boolean,
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
name: 'interactive',
|
|
54
|
-
description:
|
|
55
|
-
'Always prompt to review and edit properties, prefilled with the given arguments.',
|
|
56
|
-
alias: 'i',
|
|
57
|
-
type: Boolean,
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
name: 'non-interactive',
|
|
61
|
-
description:
|
|
62
|
-
'Never prompt: exit with an error if the command or any required property is missing.',
|
|
63
|
-
alias: 'y',
|
|
64
|
-
type: Boolean,
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
name: 'json',
|
|
68
|
-
description:
|
|
69
|
-
'Write the response to stdout as JSON. Enabled automatically when stdout is not a terminal, disable with {bold --no-json}.',
|
|
70
|
-
type: Boolean,
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
name: 'update',
|
|
74
|
-
description: 'Force an update of the cached Seam API definitions.',
|
|
75
|
-
type: Boolean,
|
|
76
|
-
},
|
|
77
|
-
],
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
header: 'Output',
|
|
81
|
-
content: [
|
|
82
|
-
'Only the response is written to stdout, so it is safe to pipe. Prompts, progress, and other information are written to stderr.',
|
|
83
|
-
'The response is trimmed to the response key and pagination.',
|
|
84
|
-
'Request params may be piped or redirected in as a JSON object. Params given as arguments win over params read from stdin.',
|
|
85
|
-
],
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
header: 'Command List Examples',
|
|
89
|
-
content: [
|
|
90
|
-
{ name: 'seam', summary: 'Interactively select commands to execute.' },
|
|
91
|
-
{ name: 'seam login', summary: 'Login to Seam.' },
|
|
92
|
-
{
|
|
93
|
-
name: 'seam wizard',
|
|
94
|
-
summary: 'Set up Seam in the current project.',
|
|
95
|
-
},
|
|
96
|
-
{ name: 'seam select workspace', summary: 'Select your workspace.' },
|
|
97
|
-
{
|
|
98
|
-
name: 'seam connect-webviews create',
|
|
99
|
-
summary: 'Create a connect webview to connect devices.',
|
|
100
|
-
},
|
|
101
|
-
{ name: 'seam devices list', summary: 'List devices in your workspace.' },
|
|
102
|
-
{
|
|
103
|
-
name: 'seam devices list {bold --interactive}',
|
|
104
|
-
summary: 'Review and edit filters before listing devices.',
|
|
105
|
-
},
|
|
106
|
-
{
|
|
107
|
-
name: 'seam devices list {bold --non-interactive}',
|
|
108
|
-
summary: 'List devices, failing instead of prompting.',
|
|
109
|
-
},
|
|
110
|
-
{
|
|
111
|
-
name: 'seam locks unlock-door {bold --device-id} $MY_DOOR',
|
|
112
|
-
summary: 'Unlock a lock.',
|
|
113
|
-
},
|
|
114
|
-
{
|
|
115
|
-
name: "seam access-codes create {bold --code} '1234' {bold --name} 'My Code'",
|
|
116
|
-
summary: 'Create an access code.',
|
|
117
|
-
},
|
|
118
|
-
{
|
|
119
|
-
name: 'seam access-codes list {bold --device-id} $MY_DOOR',
|
|
120
|
-
summary: 'List you access codes.',
|
|
121
|
-
},
|
|
122
|
-
{
|
|
123
|
-
name: 'seam devices list > devices.json',
|
|
124
|
-
summary: 'Write the response to a file as JSON.',
|
|
125
|
-
},
|
|
126
|
-
{
|
|
127
|
-
name: 'cat params.json | seam locks unlock-door',
|
|
128
|
-
summary: 'Pipe request params in as JSON.',
|
|
129
|
-
},
|
|
130
|
-
],
|
|
131
|
-
},
|
|
132
|
-
]
|
|
133
|
-
|
|
134
43
|
async function cli(args: ParsedArgs) {
|
|
135
44
|
const config = getConfigStore()
|
|
136
45
|
const output = getOutput()
|
|
137
46
|
|
|
138
|
-
|
|
139
|
-
|
|
47
|
+
const update = args['update'] === true
|
|
48
|
+
|
|
49
|
+
const helpFlag = args['help'] ?? args['h']
|
|
50
|
+
if (helpFlag != null) {
|
|
51
|
+
// Help comes from the cached API definitions so that it works without
|
|
52
|
+
// logging in, and offline once the cache is warm.
|
|
53
|
+
const spec = getCommandSpec(await getApiBlueprint(false, { update }))
|
|
54
|
+
|
|
55
|
+
// minimist reads the word after --help as its value, so 'seam --help
|
|
56
|
+
// devices' asks about devices just as 'seam devices --help' does.
|
|
57
|
+
const commandPath = [
|
|
58
|
+
...args._,
|
|
59
|
+
...(typeof helpFlag === 'string' ? [helpFlag] : []),
|
|
60
|
+
].map(toCommandWord)
|
|
61
|
+
|
|
62
|
+
const help = renderHelp(commandPath, spec)
|
|
63
|
+
|
|
64
|
+
if (help == null) {
|
|
65
|
+
output.error(chalk.red(`Unknown command: seam ${commandPath.join(' ')}`))
|
|
66
|
+
output.error(`Run 'seam --help' to see the available commands.`)
|
|
67
|
+
process.exitCode = 1
|
|
68
|
+
return
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
output.text(help)
|
|
140
72
|
return
|
|
141
73
|
}
|
|
142
74
|
|
|
@@ -145,6 +77,24 @@ async function cli(args: ParsedArgs) {
|
|
|
145
77
|
return
|
|
146
78
|
}
|
|
147
79
|
|
|
80
|
+
if (args._[0] === 'completion') {
|
|
81
|
+
const shell = args._[1]
|
|
82
|
+
|
|
83
|
+
if (!isCompletionShell(shell)) {
|
|
84
|
+
output.error(`Usage: seam completion <${completionShells.join('|')}>`)
|
|
85
|
+
process.exitCode = 1
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Completions always come from the cached API definitions so that they
|
|
90
|
+
// can be generated without logging in. They may lag the definitions
|
|
91
|
+
// served by Seam when config use-remote-api-defs is enabled.
|
|
92
|
+
output.text(
|
|
93
|
+
renderCompletion(shell, await getApiBlueprint(false, { update })),
|
|
94
|
+
)
|
|
95
|
+
return
|
|
96
|
+
}
|
|
97
|
+
|
|
148
98
|
if (
|
|
149
99
|
args._[0] === 'config' &&
|
|
150
100
|
args._[1] === 'set' &&
|
|
@@ -171,7 +121,7 @@ async function cli(args: ParsedArgs) {
|
|
|
171
121
|
return
|
|
172
122
|
}
|
|
173
123
|
|
|
174
|
-
args._ = args._.map(
|
|
124
|
+
args._ = args._.map(toCommandWord)
|
|
175
125
|
for (const k in args) {
|
|
176
126
|
args[k.toLowerCase().replace(/-/g, '_')] = args[k]
|
|
177
127
|
}
|
|
@@ -179,9 +129,6 @@ async function cli(args: ParsedArgs) {
|
|
|
179
129
|
const use_remote_api_defs =
|
|
180
130
|
args['remote_api_defs'] ?? config.get('use_remote_api_defs')
|
|
181
131
|
|
|
182
|
-
const update = args['update'] === true
|
|
183
|
-
delete args['update']
|
|
184
|
-
|
|
185
132
|
const blueprint = await getApiBlueprint(use_remote_api_defs ?? false, {
|
|
186
133
|
update,
|
|
187
134
|
})
|
|
@@ -336,6 +283,9 @@ async function cli(args: ParsedArgs) {
|
|
|
336
283
|
}
|
|
337
284
|
}
|
|
338
285
|
|
|
286
|
+
const toCommandWord = (arg: string): string =>
|
|
287
|
+
arg.toLowerCase().replace(/_/g, '-')
|
|
288
|
+
|
|
339
289
|
const handleConnectWebviewResponse = async (
|
|
340
290
|
connect_webview: any,
|
|
341
291
|
interactivity: Interactivity,
|