bdy 1.23.11-dev → 1.23.13-dev-target-commands
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/distTs/detect-rules.json +351 -0
- package/distTs/package.json +3 -1
- package/distTs/src/api/client.js +222 -12
- package/distTs/src/cliIndex.js +4 -0
- package/distTs/src/command/artifact/list.js +2 -6
- package/distTs/src/command/distro/list.js +1 -0
- package/distTs/src/command/distro/route/update.js +1 -1
- package/distTs/src/command/distro/update.js +9 -9
- package/distTs/src/command/environment/create.js +70 -0
- package/distTs/src/command/environment/delete.js +29 -0
- package/distTs/src/command/environment/get.js +48 -0
- package/distTs/src/command/environment/list.js +45 -0
- package/distTs/src/command/environment/resolve.js +22 -0
- package/distTs/src/command/environment/update.js +59 -0
- package/distTs/src/command/environment.js +20 -0
- package/distTs/src/command/pipeline/run/apply.js +62 -0
- package/distTs/src/command/target/create.js +33 -0
- package/distTs/src/command/target/delete.js +30 -0
- package/distTs/src/command/target/exec/command.js +103 -0
- package/distTs/src/command/target/exec/kill.js +31 -0
- package/distTs/src/command/target/exec/list.js +53 -0
- package/distTs/src/command/target/exec/logs.js +27 -0
- package/distTs/src/command/target/exec/status.js +44 -0
- package/distTs/src/command/target/exec.js +24 -0
- package/distTs/src/command/target/get.js +63 -0
- package/distTs/src/command/target/list.js +49 -0
- package/distTs/src/command/target/scope.js +108 -0
- package/distTs/src/command/target/update.js +31 -0
- package/distTs/src/command/target.js +22 -0
- package/distTs/src/command/yaml/actions/detect.js +268 -0
- package/distTs/src/command/yaml/actions/info.js +56 -0
- package/distTs/src/command/yaml/actions/list.js +70 -0
- package/distTs/src/command/yaml/actions/schema.js +104 -0
- package/distTs/src/command/yaml/actions.js +13 -0
- package/distTs/src/command/yaml/agents.js +88 -0
- package/distTs/src/command/yaml/cache.js +98 -0
- package/distTs/src/command/yaml/init.js +110 -0
- package/distTs/src/command/yaml/pipeline.js +42 -0
- package/distTs/src/command/yaml/render.js +83 -0
- package/distTs/src/command/yaml/schemaUtils.js +139 -0
- package/distTs/src/command/yaml/validate.js +259 -0
- package/distTs/src/command/yaml.js +18 -0
- package/distTs/src/diskCache.js +82 -0
- package/distTs/src/input.js +65 -0
- package/distTs/src/output.js +21 -2
- package/distTs/src/texts.js +252 -27
- package/distTs/src/utils.js +18 -2
- package/package.json +3 -1
- package/distTs/README.md +0 -181
- package/distTs/src/command/project/get.js +0 -18
- package/distTs/src/command/project/set.js +0 -31
- package/distTs/src/command/sandbox/get/yaml.js +0 -30
- package/distTs/src/command/vt/scrape.js +0 -193
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const fs_1 = require("fs");
|
|
7
|
+
const path_1 = require("path");
|
|
8
|
+
const utils_1 = require("../../utils");
|
|
9
|
+
const output_1 = __importDefault(require("../../output"));
|
|
10
|
+
const texts_1 = require("../../texts");
|
|
11
|
+
const schemaUtils_1 = require("./schemaUtils");
|
|
12
|
+
// Valid top-level keys the schema's PipelineYaml.properties omits (everything
|
|
13
|
+
// else is derived from the schema, so this only covers genuine gaps).
|
|
14
|
+
const SCHEMA_OMITTED_TOP_LEVEL_KEYS = ['git_config_ref'];
|
|
15
|
+
const LABEL_KEYS = ['action', 'target', 'name', 'key', 'title', 'trigger_condition'];
|
|
16
|
+
function stripRef(ref) {
|
|
17
|
+
return ref.replace('#/$defs/', '');
|
|
18
|
+
}
|
|
19
|
+
function prettifyKey(key) {
|
|
20
|
+
return key
|
|
21
|
+
.split('_')
|
|
22
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
23
|
+
.join(' ');
|
|
24
|
+
}
|
|
25
|
+
function outputErrors(result, indent) {
|
|
26
|
+
for (const err of result.errors) {
|
|
27
|
+
output_1.default.muted(`${indent}${err.path}: ${err.message}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// Array whose items reference a def — directly, or via a single $ref branch in
|
|
31
|
+
// items.oneOf (how `targets` mixes a string ref with an object form).
|
|
32
|
+
function itemRef(prop) {
|
|
33
|
+
if (!prop || prop.type !== 'array' || !prop.items)
|
|
34
|
+
return null;
|
|
35
|
+
if (prop.items.$ref)
|
|
36
|
+
return stripRef(prop.items.$ref);
|
|
37
|
+
if (Array.isArray(prop.items.oneOf)) {
|
|
38
|
+
const refs = prop.items.oneOf.filter((b) => b && b.$ref);
|
|
39
|
+
if (refs.length === 1)
|
|
40
|
+
return stripRef(refs[0].$ref);
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
function classifySections(props) {
|
|
45
|
+
const itemSections = [];
|
|
46
|
+
const objectSections = [];
|
|
47
|
+
const settingsProps = {};
|
|
48
|
+
for (const [key, prop] of Object.entries(props)) {
|
|
49
|
+
const ref = itemRef(prop);
|
|
50
|
+
if (ref) {
|
|
51
|
+
itemSections.push({ key, defName: ref });
|
|
52
|
+
}
|
|
53
|
+
else if (prop && prop.$ref) {
|
|
54
|
+
objectSections.push({ key, defName: stripRef(prop.$ref) });
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
settingsProps[key] = prop;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return { itemSections, objectSections, settingsProps };
|
|
61
|
+
}
|
|
62
|
+
function discriminatorProp(def) {
|
|
63
|
+
return def && def.oneOf && def.discriminator ? def.discriminator.propertyName : null;
|
|
64
|
+
}
|
|
65
|
+
// Resolve a discriminated def to its concrete branch by the item's `type`, so
|
|
66
|
+
// AJV always validates against a single def (never a oneOf). Plain defs pass through.
|
|
67
|
+
function resolveDef(defs, refName, value) {
|
|
68
|
+
const def = defs[refName];
|
|
69
|
+
const prop = discriminatorProp(def);
|
|
70
|
+
if (!prop)
|
|
71
|
+
return { defName: refName };
|
|
72
|
+
const dv = value?.[prop];
|
|
73
|
+
if (dv === undefined || dv === null)
|
|
74
|
+
return { missingType: prop };
|
|
75
|
+
for (const branch of def.oneOf) {
|
|
76
|
+
const branchName = branch && branch.$ref ? stripRef(branch.$ref) : null;
|
|
77
|
+
if (!branchName)
|
|
78
|
+
continue;
|
|
79
|
+
if (defs[branchName]?.properties?.[prop]?.const === dv)
|
|
80
|
+
return { defName: branchName };
|
|
81
|
+
}
|
|
82
|
+
return { unknownType: String(dv), prop };
|
|
83
|
+
}
|
|
84
|
+
function itemLabel(value, index, discProp) {
|
|
85
|
+
let name;
|
|
86
|
+
for (const k of LABEL_KEYS) {
|
|
87
|
+
if (value && value[k]) {
|
|
88
|
+
name = String(value[k]);
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (!name && !discProp && value && value.type)
|
|
93
|
+
name = String(value.type);
|
|
94
|
+
if (!name)
|
|
95
|
+
name = `#${index + 1}`;
|
|
96
|
+
return discProp && value && value[discProp] ? `${name} (${value[discProp]})` : name;
|
|
97
|
+
}
|
|
98
|
+
function validateItemSection(defs, items, refName) {
|
|
99
|
+
const discProp = discriminatorProp(defs[refName]);
|
|
100
|
+
let errors = 0;
|
|
101
|
+
for (let i = 0; i < items.length; i++) {
|
|
102
|
+
const item = items[i];
|
|
103
|
+
if (item === null || typeof item !== 'object' || Array.isArray(item)) {
|
|
104
|
+
output_1.default.green(` ${String(item)}`);
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
const label = itemLabel(item, i, discProp);
|
|
108
|
+
const resolved = resolveDef(defs, refName, item);
|
|
109
|
+
if ('missingType' in resolved) {
|
|
110
|
+
output_1.default.error(` ${label}: missing "${resolved.missingType}"`);
|
|
111
|
+
errors++;
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
if ('unknownType' in resolved) {
|
|
115
|
+
output_1.default.error(` ${label}: unknown ${resolved.prop} "${resolved.unknownType}"`);
|
|
116
|
+
errors++;
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
const result = (0, schemaUtils_1.validateAgainstDef)(resolved.defName, item);
|
|
120
|
+
if (result.valid) {
|
|
121
|
+
output_1.default.green(` ${label}`);
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
errors += result.errors.length;
|
|
125
|
+
output_1.default.error(` ${label}`);
|
|
126
|
+
outputErrors(result, ' ');
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return errors;
|
|
130
|
+
}
|
|
131
|
+
function validateObjectSection(defs, label, value, refName) {
|
|
132
|
+
const resolved = resolveDef(defs, refName, value);
|
|
133
|
+
if ('missingType' in resolved) {
|
|
134
|
+
output_1.default.error(label);
|
|
135
|
+
output_1.default.muted(` missing "${resolved.missingType}"`);
|
|
136
|
+
return 1;
|
|
137
|
+
}
|
|
138
|
+
if ('unknownType' in resolved) {
|
|
139
|
+
output_1.default.error(label);
|
|
140
|
+
output_1.default.muted(` unknown ${resolved.prop} "${resolved.unknownType}"`);
|
|
141
|
+
return 1;
|
|
142
|
+
}
|
|
143
|
+
const result = (0, schemaUtils_1.validateAgainstDef)(resolved.defName, value);
|
|
144
|
+
if (result.valid) {
|
|
145
|
+
output_1.default.green(label);
|
|
146
|
+
return 0;
|
|
147
|
+
}
|
|
148
|
+
output_1.default.error(label);
|
|
149
|
+
outputErrors(result, ' ');
|
|
150
|
+
return result.errors.length;
|
|
151
|
+
}
|
|
152
|
+
function validateSinglePipeline(parsed, label) {
|
|
153
|
+
let errors = 0;
|
|
154
|
+
const defs = (0, schemaUtils_1.getAllDefs)();
|
|
155
|
+
const pipelineDef = defs['PipelineYaml'];
|
|
156
|
+
const props = pipelineDef?.properties || {};
|
|
157
|
+
const allKnownKeys = new Set([...Object.keys(props), ...SCHEMA_OMITTED_TOP_LEVEL_KEYS]);
|
|
158
|
+
const unknownKeys = Object.keys(parsed).filter((k) => !allKnownKeys.has(k));
|
|
159
|
+
if (unknownKeys.length > 0) {
|
|
160
|
+
output_1.default.yellow(`${label}Unknown top-level keys: ${unknownKeys.join(', ')}`);
|
|
161
|
+
}
|
|
162
|
+
const { itemSections, objectSections, settingsProps } = classifySections(props);
|
|
163
|
+
if (Object.keys(settingsProps).length > 0) {
|
|
164
|
+
const settingsData = {};
|
|
165
|
+
for (const key of Object.keys(settingsProps)) {
|
|
166
|
+
if (key in parsed)
|
|
167
|
+
settingsData[key] = parsed[key];
|
|
168
|
+
}
|
|
169
|
+
const rawSchema = (0, schemaUtils_1.loadRawSchema)();
|
|
170
|
+
const result = (0, schemaUtils_1.validateAgainstSchema)({
|
|
171
|
+
type: 'object',
|
|
172
|
+
properties: settingsProps,
|
|
173
|
+
required: (pipelineDef?.required || []).filter((r) => r in settingsProps),
|
|
174
|
+
$defs: rawSchema.$defs,
|
|
175
|
+
}, settingsData);
|
|
176
|
+
if (result.valid) {
|
|
177
|
+
output_1.default.green(`${label}Pipeline settings`);
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
errors += result.errors.length;
|
|
181
|
+
output_1.default.error(`${label}Pipeline settings`);
|
|
182
|
+
outputErrors(result, ' ');
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
const itemByKey = new Map(itemSections.map((s) => [s.key, s.defName]));
|
|
186
|
+
const objByKey = new Map(objectSections.map((s) => [s.key, s.defName]));
|
|
187
|
+
for (const key of Object.keys(props)) {
|
|
188
|
+
if (!(key in parsed))
|
|
189
|
+
continue;
|
|
190
|
+
const value = parsed[key];
|
|
191
|
+
const header = `${label}${prettifyKey(key)}`;
|
|
192
|
+
if (itemByKey.has(key)) {
|
|
193
|
+
if (!Array.isArray(value))
|
|
194
|
+
continue;
|
|
195
|
+
output_1.default.blue(header);
|
|
196
|
+
errors += validateItemSection(defs, value, itemByKey.get(key));
|
|
197
|
+
}
|
|
198
|
+
else if (objByKey.has(key)) {
|
|
199
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value))
|
|
200
|
+
continue;
|
|
201
|
+
errors += validateObjectSection(defs, header, value, objByKey.get(key));
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return errors;
|
|
205
|
+
}
|
|
206
|
+
function validatePipelineFile(file) {
|
|
207
|
+
const filePath = (0, path_1.resolve)(file);
|
|
208
|
+
if (!(0, fs_1.existsSync)(filePath)) {
|
|
209
|
+
output_1.default.exitError((0, texts_1.ERR_YAML_VALIDATE_FILE_NOT_FOUND)(file));
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
const YAML = require('yaml');
|
|
213
|
+
const content = (0, fs_1.readFileSync)(filePath, 'utf8');
|
|
214
|
+
let parsed;
|
|
215
|
+
try {
|
|
216
|
+
parsed = YAML.parse(content);
|
|
217
|
+
}
|
|
218
|
+
catch (e) {
|
|
219
|
+
output_1.default.exitError(`YAML parse error: ${e.message}`);
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
223
|
+
output_1.default.exitError('Invalid pipeline YAML: expected an array of pipeline objects');
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
if (!Array.isArray(parsed)) {
|
|
227
|
+
output_1.default.exitError('Invalid pipeline YAML: expected an array (e.g. "- pipeline: my-pipeline"), got a plain object');
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
if (parsed.length === 0) {
|
|
231
|
+
output_1.default.exitError('Invalid pipeline YAML: empty array');
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
let errors = 0;
|
|
235
|
+
for (let i = 0; i < parsed.length; i++) {
|
|
236
|
+
const pipeline = parsed[i];
|
|
237
|
+
if (!pipeline || typeof pipeline !== 'object' || Array.isArray(pipeline)) {
|
|
238
|
+
output_1.default.error(` Pipeline #${i + 1}: expected an object`);
|
|
239
|
+
errors++;
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
const pipelineLabel = parsed.length > 1 ? `[${pipeline.pipeline || i + 1}] ` : '';
|
|
243
|
+
errors += validateSinglePipeline(pipeline, pipelineLabel);
|
|
244
|
+
}
|
|
245
|
+
output_1.default.normal('');
|
|
246
|
+
if (errors > 0) {
|
|
247
|
+
output_1.default.exitError(`${texts_1.ERR_YAML_VALIDATE_FAILED} (${errors} ${errors === 1 ? 'error' : 'errors'})`);
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
output_1.default.exitSuccess(texts_1.TXT_YAML_VALIDATE_ALL_PASS);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
const commandYamlValidate = (0, utils_1.newCommand)('validate', texts_1.DESC_COMMAND_YAML_VALIDATE);
|
|
254
|
+
commandYamlValidate.argument('<file>', texts_1.OPTION_YAML_VALIDATE_FILE);
|
|
255
|
+
commandYamlValidate.action(async (file) => {
|
|
256
|
+
await (0, schemaUtils_1.ensureYamlSchema)();
|
|
257
|
+
validatePipelineFile(file);
|
|
258
|
+
});
|
|
259
|
+
exports.default = commandYamlValidate;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const utils_1 = require("../utils");
|
|
7
|
+
const texts_1 = require("../texts");
|
|
8
|
+
const pipeline_1 = __importDefault(require("./yaml/pipeline"));
|
|
9
|
+
const actions_1 = __importDefault(require("./yaml/actions"));
|
|
10
|
+
const validate_1 = __importDefault(require("./yaml/validate"));
|
|
11
|
+
const init_1 = __importDefault(require("./yaml/init"));
|
|
12
|
+
const commandYaml = (0, utils_1.newCommand)('yaml', texts_1.DESC_COMMAND_YAML);
|
|
13
|
+
commandYaml.addCommand(pipeline_1.default);
|
|
14
|
+
commandYaml.addCommand(actions_1.default);
|
|
15
|
+
commandYaml.addCommand(validate_1.default);
|
|
16
|
+
commandYaml.addCommand(init_1.default);
|
|
17
|
+
commandYaml.addHelpText('after', texts_1.EXAMPLE_YAML);
|
|
18
|
+
exports.default = commandYaml;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.CACHE_TTL_MS = void 0;
|
|
7
|
+
exports.cacheRoot = cacheRoot;
|
|
8
|
+
exports.hashKey = hashKey;
|
|
9
|
+
exports.cachePaths = cachePaths;
|
|
10
|
+
exports.readMeta = readMeta;
|
|
11
|
+
exports.isHardFresh = isHardFresh;
|
|
12
|
+
exports.writeAtomic = writeAtomic;
|
|
13
|
+
exports.refresh = refresh;
|
|
14
|
+
const node_crypto_1 = require("node:crypto");
|
|
15
|
+
const node_fs_1 = require("node:fs");
|
|
16
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
17
|
+
const undici_1 = require("undici");
|
|
18
|
+
const utils_1 = require("./utils");
|
|
19
|
+
exports.CACHE_TTL_MS = 60 * 60 * 1000; // 1h
|
|
20
|
+
function cacheRoot() {
|
|
21
|
+
const dir = node_path_1.default.join((0, utils_1.getHomeDirectory)(), 'cache');
|
|
22
|
+
if (!(0, node_fs_1.existsSync)(dir))
|
|
23
|
+
(0, node_fs_1.mkdirSync)(dir, { recursive: true });
|
|
24
|
+
return dir;
|
|
25
|
+
}
|
|
26
|
+
function hashKey(input) {
|
|
27
|
+
return (0, node_crypto_1.createHash)('sha256').update(input).digest('hex').slice(0, 16);
|
|
28
|
+
}
|
|
29
|
+
function cachePaths(subdir, url) {
|
|
30
|
+
const dir = node_path_1.default.join(cacheRoot(), subdir);
|
|
31
|
+
if (!(0, node_fs_1.existsSync)(dir))
|
|
32
|
+
(0, node_fs_1.mkdirSync)(dir, { recursive: true });
|
|
33
|
+
const key = hashKey(url);
|
|
34
|
+
return {
|
|
35
|
+
raw: node_path_1.default.join(dir, `${key}.json`),
|
|
36
|
+
schema: node_path_1.default.join(dir, `${key}.v8`),
|
|
37
|
+
meta: node_path_1.default.join(dir, `${key}.meta.json`),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function readMeta(metaPath) {
|
|
41
|
+
try {
|
|
42
|
+
return JSON.parse((0, node_fs_1.readFileSync)(metaPath, 'utf8'));
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return {};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function isHardFresh(meta, ttlMs = exports.CACHE_TTL_MS) {
|
|
49
|
+
return !!meta.fetchedAt && Date.now() - meta.fetchedAt < ttlMs;
|
|
50
|
+
}
|
|
51
|
+
function writeAtomic(filePath, data) {
|
|
52
|
+
const tmp = `${filePath}.${process.pid}.tmp`;
|
|
53
|
+
(0, node_fs_1.writeFileSync)(tmp, data);
|
|
54
|
+
(0, node_fs_1.renameSync)(tmp, filePath);
|
|
55
|
+
}
|
|
56
|
+
async function refresh(url, paths) {
|
|
57
|
+
const hasRaw = (0, node_fs_1.existsSync)(paths.raw);
|
|
58
|
+
const meta = hasRaw ? readMeta(paths.meta) : {};
|
|
59
|
+
// The server serves a strong content-based ETag, so revalidate with If-None-Match.
|
|
60
|
+
const headers = {};
|
|
61
|
+
if (hasRaw && meta.etag)
|
|
62
|
+
headers['if-none-match'] = meta.etag;
|
|
63
|
+
try {
|
|
64
|
+
const res = await (0, undici_1.request)(url, { headers });
|
|
65
|
+
const etag = res.headers['etag'];
|
|
66
|
+
if (res.statusCode === 304 && hasRaw) {
|
|
67
|
+
await res.body.dump();
|
|
68
|
+
writeAtomic(paths.meta, JSON.stringify({ etag: meta.etag, fetchedAt: Date.now() }));
|
|
69
|
+
return 'unchanged';
|
|
70
|
+
}
|
|
71
|
+
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
72
|
+
writeAtomic(paths.raw, await res.body.text());
|
|
73
|
+
writeAtomic(paths.meta, JSON.stringify({ etag: typeof etag === 'string' ? etag : undefined, fetchedAt: Date.now() }));
|
|
74
|
+
return 'updated';
|
|
75
|
+
}
|
|
76
|
+
await res.body.dump();
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// network error — fall back to the cached body if present
|
|
80
|
+
}
|
|
81
|
+
return hasRaw ? 'unchanged' : 'none';
|
|
82
|
+
}
|
package/distTs/src/input.js
CHANGED
|
@@ -368,8 +368,19 @@ class Input {
|
|
|
368
368
|
const ApiClient = require('./api/client').default;
|
|
369
369
|
const { token: t, refreshToken, clientId, clientToken, clientSecret, } = this.restApiToken(allowNoToken, token);
|
|
370
370
|
const baseUrl = this.restApiBaseUrl(api, region, t);
|
|
371
|
+
ApiClient.onRateLimitLow = (remaining, reset) => output_1.default.noteWarning((0, texts_1.WARN_REST_API_RATE_LIMIT_LOW)(remaining, reset));
|
|
371
372
|
return new ApiClient(baseUrl, t, refreshToken, clientId, clientSecret, clientToken);
|
|
372
373
|
}
|
|
374
|
+
// Stderr note for list commands whose PROJECT scope was inferred (linked
|
|
375
|
+
// project / BUDDY_PROJECT) rather than requested via -p or --scope, so the
|
|
376
|
+
// narrowing is never silent.
|
|
377
|
+
static projectScopeNote(options, project, scope, resource) {
|
|
378
|
+
if (options.format === 'json' || options.scope || options.project)
|
|
379
|
+
return;
|
|
380
|
+
if (scope !== 'PROJECT' || !project)
|
|
381
|
+
return;
|
|
382
|
+
output_1.default.note((0, texts_1.TXT_LIST_PROJECT_SCOPE_NOTE)(resource, project));
|
|
383
|
+
}
|
|
373
384
|
static routeType(type) {
|
|
374
385
|
if (!type)
|
|
375
386
|
return distro_1.ROUTE_TYPE.DIRECT_PROXY;
|
|
@@ -1087,6 +1098,54 @@ class Input {
|
|
|
1087
1098
|
return distro_1.SCOPE.PROJECT;
|
|
1088
1099
|
return distro_1.SCOPE.WORKSPACE;
|
|
1089
1100
|
}
|
|
1101
|
+
static targetScope(project, pipeline, environment, scope) {
|
|
1102
|
+
Input.targetLine(project, pipeline, environment);
|
|
1103
|
+
if (scope) {
|
|
1104
|
+
scope = scope.toUpperCase();
|
|
1105
|
+
if (scope === utils_1.TARGET_SCOPE.WORKSPACE) {
|
|
1106
|
+
return scope;
|
|
1107
|
+
}
|
|
1108
|
+
if (scope === utils_1.TARGET_SCOPE.PROJECT) {
|
|
1109
|
+
if (!project)
|
|
1110
|
+
output_1.default.exitError(texts_1.ERR_COMMAND_SCOPE_NO_PROJECT);
|
|
1111
|
+
return scope;
|
|
1112
|
+
}
|
|
1113
|
+
if (scope === utils_1.TARGET_SCOPE.PIPELINE) {
|
|
1114
|
+
if (!pipeline)
|
|
1115
|
+
output_1.default.exitError(texts_1.ERR_TARGET_SCOPE_NO_PIPELINE);
|
|
1116
|
+
return scope;
|
|
1117
|
+
}
|
|
1118
|
+
if (scope === utils_1.TARGET_SCOPE.ENVIRONMENT) {
|
|
1119
|
+
if (!environment)
|
|
1120
|
+
output_1.default.exitError(texts_1.ERR_TARGET_SCOPE_NO_ENVIRONMENT);
|
|
1121
|
+
return scope;
|
|
1122
|
+
}
|
|
1123
|
+
return output_1.default.exitError(texts_1.ERR_TARGET_SCOPE);
|
|
1124
|
+
}
|
|
1125
|
+
if (pipeline)
|
|
1126
|
+
return utils_1.TARGET_SCOPE.PIPELINE;
|
|
1127
|
+
if (environment)
|
|
1128
|
+
return utils_1.TARGET_SCOPE.ENVIRONMENT;
|
|
1129
|
+
if (project)
|
|
1130
|
+
return utils_1.TARGET_SCOPE.PROJECT;
|
|
1131
|
+
return utils_1.TARGET_SCOPE.WORKSPACE;
|
|
1132
|
+
}
|
|
1133
|
+
// Line context for resolving a target identifier via /identifiers:
|
|
1134
|
+
// params describe one line from workspace down (ws -> project -> pipeline
|
|
1135
|
+
// or environment), searched from the deepest scope up.
|
|
1136
|
+
static targetLine(project, pipeline, environment) {
|
|
1137
|
+
if (pipeline && !project) {
|
|
1138
|
+
output_1.default.exitError(texts_1.ERR_TARGET_PIPELINE_REQUIRES_PROJECT);
|
|
1139
|
+
}
|
|
1140
|
+
if (pipeline && environment) {
|
|
1141
|
+
output_1.default.exitError(texts_1.ERR_TARGET_PIPELINE_ENV_CONFLICT);
|
|
1142
|
+
}
|
|
1143
|
+
return {
|
|
1144
|
+
project: project || undefined,
|
|
1145
|
+
pipeline,
|
|
1146
|
+
environment,
|
|
1147
|
+
};
|
|
1148
|
+
}
|
|
1090
1149
|
static restApiProject(project, allowNull) {
|
|
1091
1150
|
const ProjectCfg = require('./project/cfg').default;
|
|
1092
1151
|
let p = process.env.BUDDY_PROJECT;
|
|
@@ -1107,6 +1166,12 @@ class Input {
|
|
|
1107
1166
|
output_1.default.exitError(texts_1.ERR_SANDBOX_BOOT_SCRIPT_NOT_EXISTS);
|
|
1108
1167
|
return node_fs_1.default.readFileSync(p, 'utf8');
|
|
1109
1168
|
}
|
|
1169
|
+
static tags(tags) {
|
|
1170
|
+
return tags
|
|
1171
|
+
.split(',')
|
|
1172
|
+
.map((t) => t.trim())
|
|
1173
|
+
.filter(Boolean);
|
|
1174
|
+
}
|
|
1110
1175
|
static restApiYaml(yaml) {
|
|
1111
1176
|
if (!yaml)
|
|
1112
1177
|
output_1.default.exitError(texts_1.ERR_REST_API_YAML);
|
package/distTs/src/output.js
CHANGED
|
@@ -134,6 +134,23 @@ class Output {
|
|
|
134
134
|
msg += '\n';
|
|
135
135
|
getTerminal()(msg);
|
|
136
136
|
}
|
|
137
|
+
// Payload output (command results, logs, JSON): bypasses terminal-kit,
|
|
138
|
+
// whose printf-style formatting and ^-markup corrupt data containing
|
|
139
|
+
// % or ^ sequences.
|
|
140
|
+
static data(txt) {
|
|
141
|
+
process.stdout.write(`${txt}\n`);
|
|
142
|
+
}
|
|
143
|
+
static writeStderr(txt) {
|
|
144
|
+
process.stderr.write(`${txt}\n`);
|
|
145
|
+
}
|
|
146
|
+
// Narration accompanying payload output goes to stderr so stdout stays
|
|
147
|
+
// parseable by scripts.
|
|
148
|
+
static note(txt) {
|
|
149
|
+
this.writeStderr(this.getMutedColor(txt));
|
|
150
|
+
}
|
|
151
|
+
static noteWarning(txt) {
|
|
152
|
+
this.writeStderr(this.getYellowColor(txt));
|
|
153
|
+
}
|
|
137
154
|
static gray(txt, newLine = true) {
|
|
138
155
|
this.normal(this.getMutedColor(txt), newLine);
|
|
139
156
|
}
|
|
@@ -624,7 +641,7 @@ class Output {
|
|
|
624
641
|
_terminal.grabInput(false);
|
|
625
642
|
}
|
|
626
643
|
static json(json) {
|
|
627
|
-
this.
|
|
644
|
+
this.data(JSON.stringify(json));
|
|
628
645
|
}
|
|
629
646
|
static exitError(err) {
|
|
630
647
|
this.clearUndici();
|
|
@@ -642,7 +659,9 @@ class Output {
|
|
|
642
659
|
if (isDebug)
|
|
643
660
|
msg += `\n${e.stack}`;
|
|
644
661
|
}
|
|
645
|
-
|
|
662
|
+
// Raw stderr: error messages carry user/API data (SQL, URLs) that
|
|
663
|
+
// terminal-kit's %-formatting would corrupt.
|
|
664
|
+
this.writeStderr(this.getRedColor(String(msg)));
|
|
646
665
|
process.exit(1);
|
|
647
666
|
}
|
|
648
667
|
}
|