rubrkit 0.1.1 → 0.4.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 +417 -404
- package/package.json +28 -28
- package/src/api.js +108 -101
- package/src/args.js +209 -175
- package/src/cli.js +97 -93
- package/src/config.js +186 -169
- package/src/formats.js +239 -222
- package/src/pull.js +682 -676
- package/src/sdk.js +556 -443
- package/src/testingCli.js +504 -431
package/package.json
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "rubrkit",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"type": "module",
|
|
5
|
-
"description": "Rubrkit CLI for pulling artifact bundles into local agent projects.",
|
|
6
|
-
"bin": {
|
|
7
|
-
"rubrkit": "./bin/rubrkit.js"
|
|
8
|
-
},
|
|
9
|
-
"exports": {
|
|
10
|
-
".": {
|
|
11
|
-
"types": "./src/index.d.ts",
|
|
12
|
-
"default": "./src/sdk.js"
|
|
13
|
-
}
|
|
14
|
-
},
|
|
15
|
-
"types": "./src/index.d.ts",
|
|
16
|
-
"files": [
|
|
17
|
-
"bin",
|
|
18
|
-
"src",
|
|
19
|
-
"README.md",
|
|
20
|
-
"package.json"
|
|
21
|
-
],
|
|
22
|
-
"engines": {
|
|
23
|
-
"node": ">=22"
|
|
24
|
-
},
|
|
25
|
-
"scripts": {
|
|
26
|
-
"test": "node --test \"test/**/*.test.js\""
|
|
27
|
-
}
|
|
28
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "rubrkit",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Rubrkit CLI for pulling artifact bundles into local agent projects.",
|
|
6
|
+
"bin": {
|
|
7
|
+
"rubrkit": "./bin/rubrkit.js"
|
|
8
|
+
},
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./src/index.d.ts",
|
|
12
|
+
"default": "./src/sdk.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"types": "./src/index.d.ts",
|
|
16
|
+
"files": [
|
|
17
|
+
"bin",
|
|
18
|
+
"src",
|
|
19
|
+
"README.md",
|
|
20
|
+
"package.json"
|
|
21
|
+
],
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=22"
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"test": "node --test \"test/**/*.test.js\""
|
|
27
|
+
}
|
|
28
|
+
}
|
package/src/api.js
CHANGED
|
@@ -1,101 +1,108 @@
|
|
|
1
|
-
import { exitCodeForApiFailure, RubrkitCliError } from './errors.js';
|
|
2
|
-
|
|
3
|
-
export class RubrkitApiClient {
|
|
4
|
-
/**
|
|
5
|
-
* @param {{ apiUrl: string, apiKey: string, fetchImpl?: typeof fetch }} options
|
|
6
|
-
*/
|
|
7
|
-
constructor({ apiUrl, apiKey, fetchImpl = globalThis.fetch }) {
|
|
8
|
-
this.apiUrl = apiUrl.replace(/\/+$/, '');
|
|
9
|
-
this.apiKey = apiKey;
|
|
10
|
-
this.fetchImpl = fetchImpl;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
* @param {string}
|
|
44
|
-
*/
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
1
|
+
import { exitCodeForApiFailure, RubrkitCliError } from './errors.js';
|
|
2
|
+
|
|
3
|
+
export class RubrkitApiClient {
|
|
4
|
+
/**
|
|
5
|
+
* @param {{ apiUrl: string, apiKey: string, fetchImpl?: typeof fetch }} options
|
|
6
|
+
*/
|
|
7
|
+
constructor({ apiUrl, apiKey, fetchImpl = globalThis.fetch }) {
|
|
8
|
+
this.apiUrl = apiUrl.replace(/\/+$/, '');
|
|
9
|
+
this.apiKey = apiKey;
|
|
10
|
+
this.fetchImpl = fetchImpl;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {{ labels?: string[] }} [options]
|
|
15
|
+
*/
|
|
16
|
+
async listArtifactBundles({ labels = [] } = {}) {
|
|
17
|
+
const params = new URLSearchParams({ status: 'active', limit: '100' });
|
|
18
|
+
for (const label of labels) {
|
|
19
|
+
params.append('label', label);
|
|
20
|
+
}
|
|
21
|
+
const data = await this.request(`/artifact-bundles?${params.toString()}`);
|
|
22
|
+
return Array.isArray(data.artifactBundles) ? data.artifactBundles : [];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @param {string} artifactBundleId
|
|
27
|
+
*/
|
|
28
|
+
async getArtifactBundle(artifactBundleId) {
|
|
29
|
+
const data = await this.request(`/artifact-bundles/${encodeURIComponent(artifactBundleId)}`);
|
|
30
|
+
return data;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @param {string} artifactBundleId
|
|
35
|
+
*/
|
|
36
|
+
async listFiles(artifactBundleId) {
|
|
37
|
+
const data = await this.request(`/artifact-bundles/${encodeURIComponent(artifactBundleId)}/files?limit=200`);
|
|
38
|
+
return Array.isArray(data.files) ? data.files : [];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @param {string} artifactBundleId
|
|
43
|
+
* @param {string} fileId
|
|
44
|
+
*/
|
|
45
|
+
async getFile(artifactBundleId, fileId) {
|
|
46
|
+
return this.request(`/artifact-bundles/${encodeURIComponent(artifactBundleId)}/files/${encodeURIComponent(fileId)}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @param {string} path
|
|
51
|
+
*/
|
|
52
|
+
/**
|
|
53
|
+
* @param {string} path
|
|
54
|
+
* @param {{ method?: string, body?: unknown }} [options]
|
|
55
|
+
*/
|
|
56
|
+
async request(path, { method = 'GET', body: requestBody = undefined } = {}) {
|
|
57
|
+
/** @type {Record<string, string>} */
|
|
58
|
+
const headers = {
|
|
59
|
+
accept: 'application/json',
|
|
60
|
+
authorization: `Bearer ${this.apiKey}`,
|
|
61
|
+
'user-agent': 'rubrkit-cli/0.0.0-local',
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
if (requestBody !== undefined) {
|
|
65
|
+
headers['content-type'] = 'application/json';
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const response = await this.fetchImpl(`${this.apiUrl}${path}`, {
|
|
69
|
+
method,
|
|
70
|
+
headers,
|
|
71
|
+
body: requestBody === undefined ? undefined : JSON.stringify(requestBody),
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const text = await response.text();
|
|
75
|
+
const body = text ? parseJson(text) : {};
|
|
76
|
+
|
|
77
|
+
if (!response.ok) {
|
|
78
|
+
const error = body && typeof body === 'object' && 'error' in body ? body.error : {};
|
|
79
|
+
const message =
|
|
80
|
+
error && typeof error === 'object' && typeof error.message === 'string'
|
|
81
|
+
? error.message
|
|
82
|
+
: `Rubrkit API request failed with HTTP ${response.status}.`;
|
|
83
|
+
const code = error && typeof error === 'object' && typeof error.code === 'string' ? error.code : 'api_request_failed';
|
|
84
|
+
|
|
85
|
+
throw new RubrkitCliError(message, {
|
|
86
|
+
code,
|
|
87
|
+
exitCode: exitCodeForApiFailure(code, response.status),
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return body && typeof body === 'object' && 'data' in body ? body.data : body;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* @param {string} text
|
|
97
|
+
*/
|
|
98
|
+
function parseJson(text) {
|
|
99
|
+
try {
|
|
100
|
+
return JSON.parse(text);
|
|
101
|
+
} catch (error) {
|
|
102
|
+
throw new RubrkitCliError('Rubrkit API returned invalid JSON.', {
|
|
103
|
+
code: 'invalid_api_response',
|
|
104
|
+
exitCode: 1,
|
|
105
|
+
details: error,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
package/src/args.js
CHANGED
|
@@ -1,175 +1,209 @@
|
|
|
1
|
-
import { usageError } from './errors.js';
|
|
2
|
-
|
|
3
|
-
const VALUE_FLAGS = new Set([
|
|
4
|
-
'destination',
|
|
5
|
-
'agent',
|
|
6
|
-
'artifact-bundle',
|
|
7
|
-
'
|
|
8
|
-
'
|
|
9
|
-
'
|
|
10
|
-
'
|
|
11
|
-
'
|
|
12
|
-
'fail-
|
|
13
|
-
'
|
|
14
|
-
'
|
|
15
|
-
'api-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
'
|
|
21
|
-
'
|
|
22
|
-
'
|
|
23
|
-
'
|
|
24
|
-
'
|
|
25
|
-
'
|
|
26
|
-
'
|
|
27
|
-
'
|
|
28
|
-
'
|
|
29
|
-
'
|
|
30
|
-
'
|
|
31
|
-
'
|
|
32
|
-
'
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (command === '--
|
|
51
|
-
return { command: '
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
if (
|
|
156
|
-
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
if (!
|
|
160
|
-
throw usageError(
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
1
|
+
import { usageError } from './errors.js';
|
|
2
|
+
|
|
3
|
+
const VALUE_FLAGS = new Set([
|
|
4
|
+
'destination',
|
|
5
|
+
'agent',
|
|
6
|
+
'artifact-bundle',
|
|
7
|
+
'audit-run-id',
|
|
8
|
+
'artifact',
|
|
9
|
+
'rubric',
|
|
10
|
+
'format',
|
|
11
|
+
'output',
|
|
12
|
+
'fail-under',
|
|
13
|
+
'fail-on',
|
|
14
|
+
'config',
|
|
15
|
+
'api-url',
|
|
16
|
+
'api-key',
|
|
17
|
+
]);
|
|
18
|
+
|
|
19
|
+
const BOOLEAN_FLAGS = new Set([
|
|
20
|
+
'all',
|
|
21
|
+
'yes',
|
|
22
|
+
'dry-run',
|
|
23
|
+
'force',
|
|
24
|
+
'prune',
|
|
25
|
+
'update-only',
|
|
26
|
+
'ci',
|
|
27
|
+
'local',
|
|
28
|
+
'remote',
|
|
29
|
+
'no-ai',
|
|
30
|
+
'no-cache',
|
|
31
|
+
'watch',
|
|
32
|
+
'changed',
|
|
33
|
+
'help',
|
|
34
|
+
'version',
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
const MULTI_VALUE_FLAGS = new Set(['label']);
|
|
38
|
+
|
|
39
|
+
const AGENTS = new Set(['auto', 'codex', 'claude', 'generic']);
|
|
40
|
+
const COMMANDS = new Set(['pull', 'validate', 'test', 'audit', 'eval', 'audit-apply', 'report']);
|
|
41
|
+
const FORMATS = new Set(['text', 'json', 'junit']);
|
|
42
|
+
const FAIL_ON_LEVELS = new Set(['critical', 'high', 'medium', 'low']);
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @param {string[]} argv
|
|
46
|
+
*/
|
|
47
|
+
export function parseArgs(argv) {
|
|
48
|
+
const [command, ...rest] = argv;
|
|
49
|
+
|
|
50
|
+
if (!command || command === '--help' || command === '-h') {
|
|
51
|
+
return { command: command ? 'help' : 'help', selector: null, options: {} };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (command === '--version' || command === '-v') {
|
|
55
|
+
return { command: 'version', selector: null, options: {} };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (!COMMANDS.has(command)) {
|
|
59
|
+
throw usageError(`Unknown command "${command}". Run rubrkit --help for usage.`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** @type {Record<string, string | boolean | string[]>} */
|
|
63
|
+
const options = {};
|
|
64
|
+
/** @type {string[]} */
|
|
65
|
+
const positionals = [];
|
|
66
|
+
|
|
67
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
68
|
+
const arg = rest[index];
|
|
69
|
+
|
|
70
|
+
if (arg === '--') {
|
|
71
|
+
positionals.push(...rest.slice(index + 1));
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (!arg.startsWith('-') || arg === '-') {
|
|
76
|
+
positionals.push(arg);
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const normalized = normalizeFlag(arg);
|
|
81
|
+
|
|
82
|
+
if (normalized.alias) {
|
|
83
|
+
options[normalized.name] = true;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (BOOLEAN_FLAGS.has(normalized.name)) {
|
|
88
|
+
if (normalized.value !== null) {
|
|
89
|
+
throw usageError(`Flag --${normalized.name} does not accept a value.`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
options[normalized.name] = true;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (MULTI_VALUE_FLAGS.has(normalized.name)) {
|
|
97
|
+
const value = normalized.value ?? rest[index + 1];
|
|
98
|
+
|
|
99
|
+
if (!value || value.startsWith('--')) {
|
|
100
|
+
throw usageError(`Flag --${normalized.name} requires a value.`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const parts = value
|
|
104
|
+
.split(',')
|
|
105
|
+
.map((part) => part.trim())
|
|
106
|
+
.filter(Boolean);
|
|
107
|
+
const existing = options[normalized.name];
|
|
108
|
+
const current = Array.isArray(existing) ? existing : [];
|
|
109
|
+
options[normalized.name] = [...current, ...parts];
|
|
110
|
+
|
|
111
|
+
if (normalized.value === null) {
|
|
112
|
+
index += 1;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (VALUE_FLAGS.has(normalized.name)) {
|
|
119
|
+
const value = normalized.value ?? rest[index + 1];
|
|
120
|
+
|
|
121
|
+
if (!value || value.startsWith('--')) {
|
|
122
|
+
throw usageError(`Flag --${normalized.name} requires a value.`);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
options[normalized.name] = value;
|
|
126
|
+
|
|
127
|
+
if (normalized.value === null) {
|
|
128
|
+
index += 1;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
throw usageError(`Unknown flag --${normalized.name}.`);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (positionals.length > 1) {
|
|
138
|
+
throw usageError(`The ${command} command accepts at most one target or selector.`);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const selector = positionals[0] ?? null;
|
|
142
|
+
|
|
143
|
+
if (selector === 'all') {
|
|
144
|
+
options.all = true;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (Array.isArray(options.label)) {
|
|
148
|
+
options.label = [...new Set(options.label)];
|
|
149
|
+
|
|
150
|
+
if (options.label.length > 0 && (selector || options['artifact-bundle'] || options.artifact)) {
|
|
151
|
+
throw usageError('--label cannot be combined with a positional selector, --artifact-bundle, or --artifact.');
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (typeof options.agent === 'string' && !AGENTS.has(options.agent)) {
|
|
156
|
+
throw usageError('--agent must be one of auto, codex, claude, or generic.');
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (typeof options.format === 'string' && !FORMATS.has(options.format)) {
|
|
160
|
+
throw usageError('--format must be one of text, json, or junit.');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (typeof options['fail-on'] === 'string' && !FAIL_ON_LEVELS.has(options['fail-on'])) {
|
|
164
|
+
throw usageError('--fail-on must be one of critical, high, medium, or low.');
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (options['fail-under'] !== undefined) {
|
|
168
|
+
const value = Number(options['fail-under']);
|
|
169
|
+
if (!Number.isFinite(value) || value < 0 || value > 100) {
|
|
170
|
+
throw usageError('--fail-under must be a number from 0 to 100.');
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (options.local && options.remote) {
|
|
175
|
+
throw usageError('Use either --local or --remote, not both.');
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return { command, selector, options };
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* @param {string} arg
|
|
183
|
+
*/
|
|
184
|
+
function normalizeFlag(arg) {
|
|
185
|
+
if (arg === '-y') {
|
|
186
|
+
return { name: 'yes', value: null, alias: true };
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (arg === '-h') {
|
|
190
|
+
return { name: 'help', value: null, alias: true };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (!arg.startsWith('--')) {
|
|
194
|
+
throw usageError(`Unknown short flag "${arg}".`);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const raw = arg.slice(2);
|
|
198
|
+
const equalsIndex = raw.indexOf('=');
|
|
199
|
+
|
|
200
|
+
if (equalsIndex === -1) {
|
|
201
|
+
return { name: raw, value: null, alias: false };
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return {
|
|
205
|
+
name: raw.slice(0, equalsIndex),
|
|
206
|
+
value: raw.slice(equalsIndex + 1),
|
|
207
|
+
alias: false,
|
|
208
|
+
};
|
|
209
|
+
}
|