bdy 1.25.3-dev → 1.25.4-dev

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/distTs/package.json +1 -1
  2. package/distTs/src/agent/socket/tunnel.js +2 -0
  3. package/distTs/src/command/agent/proxy/status.js +9 -2
  4. package/distTs/src/command/agent/status.js +22 -6
  5. package/distTs/src/command/agent/tags/get.js +9 -2
  6. package/distTs/src/command/agent/target/status.js +9 -2
  7. package/distTs/src/command/agent/tunnel/list.js +6 -1
  8. package/distTs/src/command/agent/tunnel/status.js +13 -1
  9. package/distTs/src/command/agent/version.js +7 -2
  10. package/distTs/src/command/api/info.js +50 -2
  11. package/distTs/src/command/api/list.js +18 -2
  12. package/distTs/src/command/artifact/create.js +5 -0
  13. package/distTs/src/command/artifact/publish.js +20 -22
  14. package/distTs/src/command/artifact/version/create.js +5 -0
  15. package/distTs/src/command/config/get/tunnel.js +8 -3
  16. package/distTs/src/command/config/get/tunnels.js +8 -3
  17. package/distTs/src/command/distro/create.js +5 -0
  18. package/distTs/src/command/distro/route/create.js +5 -0
  19. package/distTs/src/command/distro/route/update.js +5 -0
  20. package/distTs/src/command/distro/update.js +5 -0
  21. package/distTs/src/command/domain/search.js +31 -10
  22. package/distTs/src/command/environment/create.js +5 -0
  23. package/distTs/src/command/environment/update.js +5 -0
  24. package/distTs/src/command/sandbox/app/add.js +7 -1
  25. package/distTs/src/command/sandbox/app/logs.js +9 -4
  26. package/distTs/src/command/sandbox/create.js +13 -5
  27. package/distTs/src/command/sandbox/endpoint/add.js +5 -0
  28. package/distTs/src/command/sandbox/exec/command.js +15 -30
  29. package/distTs/src/command/sandbox/exec/logs.js +4 -29
  30. package/distTs/src/command/sandbox/finishExec.js +33 -0
  31. package/distTs/src/command/sandbox/logs.js +14 -1
  32. package/distTs/src/command/sandbox/snapshot/create.js +9 -1
  33. package/distTs/src/command/target/create.js +5 -0
  34. package/distTs/src/command/target/exec/logs.js +15 -2
  35. package/distTs/src/command/target/update.js +5 -0
  36. package/distTs/src/command/yaml/validate.js +111 -52
  37. package/distTs/src/openapi.js +8 -1
  38. package/distTs/src/output.js +39 -0
  39. package/distTs/src/texts.js +206 -1
  40. package/package.json +1 -1
@@ -22,9 +22,17 @@ function prettifyKey(key) {
22
22
  .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
23
23
  .join(' ');
24
24
  }
25
- function outputErrors(result, indent) {
25
+ function recordError(context, path, message) {
26
+ context.errors.push({
27
+ pipeline: context.pipeline,
28
+ path: path.replace(/\.(\d+)(?=\.|$)/g, '[$1]'),
29
+ message,
30
+ });
31
+ }
32
+ function outputErrors(result, indent, context, prefix = '') {
26
33
  for (const err of result.errors) {
27
- output_1.default.muted(`${indent}${err.path}: ${err.message}`);
34
+ recordError(context, [prefix, err.path].filter(Boolean).join('.'), err.message);
35
+ context.output.muted(`${indent}${err.path}: ${err.message}`);
28
36
  }
29
37
  }
30
38
  function valueType(value) {
@@ -34,9 +42,10 @@ function valueType(value) {
34
42
  return 'array';
35
43
  return typeof value;
36
44
  }
37
- function reportWrongContainer(header, expected, value) {
38
- output_1.default.error(header);
39
- output_1.default.muted(` ${(0, texts_1.ERR_YAML_MUST_BE)(expected, valueType(value))}`);
45
+ function reportWrongContainer(header, expected, value, context, path) {
46
+ recordError(context, path, (0, texts_1.ERR_YAML_MUST_BE)(expected, valueType(value)));
47
+ context.output.error(header);
48
+ context.output.muted(` ${(0, texts_1.ERR_YAML_MUST_BE)(expected, valueType(value))}`);
40
49
  return 1;
41
50
  }
42
51
  // Array whose items reference a def — directly, or via a single $ref branch in
@@ -117,85 +126,101 @@ function itemLabel(value, index, discProp) {
117
126
  name = `#${index + 1}`;
118
127
  return discProp && value && value[discProp] ? `${name} (${value[discProp]})` : name;
119
128
  }
120
- function validateScalarItem(item, section) {
129
+ function validateScalarItem(item, section, context, path) {
121
130
  // Null errors even where the section takes a string: coerceTypes cannot fix a root scalar.
122
131
  if (item === null || !section.scalar) {
123
- output_1.default.error(` ${String(item)}: ${(0, texts_1.ERR_YAML_MUST_BE)('object', valueType(item))}`);
132
+ recordError(context, path, (0, texts_1.ERR_YAML_MUST_BE)('object', valueType(item)));
133
+ context.output.error(` ${String(item)}: ${(0, texts_1.ERR_YAML_MUST_BE)('object', valueType(item))}`);
124
134
  return 1;
125
135
  }
126
136
  const result = (0, schemaUtils_1.validateAgainstSchema)({ ...section.scalar, $defs: (0, schemaUtils_1.loadRawSchema)().$defs }, item);
127
137
  if (result.valid) {
128
- output_1.default.green(` ${String(item)}`);
138
+ context.output.green(` ${String(item)}`);
129
139
  return 0;
130
140
  }
131
- output_1.default.error(` ${String(item)}`);
132
- outputErrors(result, ' ');
141
+ context.output.error(` ${String(item)}`);
142
+ outputErrors(result, ' ', context, path);
133
143
  return result.errors.length;
134
144
  }
135
- function validateItemSection(unions, items, section) {
145
+ function validateItemSection(unions, items, section, context) {
136
146
  const refName = section.defName;
137
147
  const discProp = unions.get(refName)?.property ?? null;
138
148
  let errors = 0;
139
149
  for (let i = 0; i < items.length; i++) {
140
150
  const item = items[i];
151
+ const path = `${section.key}[${i}]`;
141
152
  if (item === null || typeof item !== 'object' || Array.isArray(item)) {
142
- errors += validateScalarItem(item, section);
153
+ errors += validateScalarItem(item, section, context, path);
143
154
  continue;
144
155
  }
145
156
  const label = itemLabel(item, i, discProp);
146
157
  const resolved = resolveDef(unions, refName, item);
147
158
  if ('missingType' in resolved) {
148
- output_1.default.error(` ${label}: ${(0, texts_1.ERR_YAML_ITEM_MISSING)(resolved.missingType)}`);
159
+ recordError(context, `${path}.${resolved.missingType}`, (0, texts_1.ERR_YAML_ITEM_MISSING)(resolved.missingType));
160
+ context.output.error(` ${label}: ${(0, texts_1.ERR_YAML_ITEM_MISSING)(resolved.missingType)}`);
149
161
  errors++;
150
162
  continue;
151
163
  }
152
164
  if ('unknownType' in resolved) {
153
- output_1.default.error(` ${label}: ${(0, texts_1.ERR_YAML_ITEM_UNKNOWN)(resolved.prop, resolved.unknownType)}`);
165
+ recordError(context, `${path}.${resolved.prop}`, (0, texts_1.ERR_YAML_ITEM_UNKNOWN)(resolved.prop, resolved.unknownType));
166
+ context.output.error(` ${label}: ${(0, texts_1.ERR_YAML_ITEM_UNKNOWN)(resolved.prop, resolved.unknownType)}`);
154
167
  errors++;
155
168
  continue;
156
169
  }
157
170
  const result = (0, schemaUtils_1.validateAgainstDef)(resolved.defName, item);
158
171
  if (result.valid) {
159
- output_1.default.green(` ${label}`);
172
+ context.output.green(` ${label}`);
160
173
  }
161
174
  else {
162
175
  errors += result.errors.length;
163
- output_1.default.error(` ${label}`);
164
- outputErrors(result, ' ');
176
+ context.output.error(` ${label}`);
177
+ outputErrors(result, ' ', context, path);
165
178
  }
166
179
  }
167
180
  return errors;
168
181
  }
169
- function validateObjectSection(unions, label, value, refName) {
182
+ function validateObjectSection(unions, label, value, refName, context, path) {
170
183
  const resolved = resolveDef(unions, refName, value);
171
184
  if ('missingType' in resolved) {
172
- output_1.default.error(label);
173
- output_1.default.muted(` ${(0, texts_1.ERR_YAML_ITEM_MISSING)(resolved.missingType)}`);
185
+ context.output.error(label);
186
+ recordError(context, `${path}.${resolved.missingType}`, (0, texts_1.ERR_YAML_ITEM_MISSING)(resolved.missingType));
187
+ context.output.muted(` ${(0, texts_1.ERR_YAML_ITEM_MISSING)(resolved.missingType)}`);
174
188
  return 1;
175
189
  }
176
190
  if ('unknownType' in resolved) {
177
- output_1.default.error(label);
178
- output_1.default.muted(` ${(0, texts_1.ERR_YAML_ITEM_UNKNOWN)(resolved.prop, resolved.unknownType)}`);
191
+ context.output.error(label);
192
+ recordError(context, `${path}.${resolved.prop}`, (0, texts_1.ERR_YAML_ITEM_UNKNOWN)(resolved.prop, resolved.unknownType));
193
+ context.output.muted(` ${(0, texts_1.ERR_YAML_ITEM_UNKNOWN)(resolved.prop, resolved.unknownType)}`);
179
194
  return 1;
180
195
  }
181
196
  const result = (0, schemaUtils_1.validateAgainstDef)(resolved.defName, value);
182
197
  if (result.valid) {
183
- output_1.default.green(label);
198
+ context.output.green(label);
184
199
  return 0;
185
200
  }
186
- output_1.default.error(label);
187
- outputErrors(result, ' ');
201
+ context.output.error(label);
202
+ outputErrors(result, ' ', context, path);
188
203
  return result.errors.length;
189
204
  }
190
- function validateSinglePipeline(parsed, label, unions) {
205
+ function validateSinglePipeline(parsed, label, unions, context) {
191
206
  let errors = 0;
192
207
  const defs = (0, schemaUtils_1.getAllDefs)();
193
208
  const pipelineDef = defs['PipelineYaml'];
194
209
  const props = pipelineDef?.properties || {};
195
- const allKnownKeys = new Set([...Object.keys(props), ...SCHEMA_OMITTED_TOP_LEVEL_KEYS]);
210
+ const allKnownKeys = new Set([
211
+ ...Object.keys(props),
212
+ ...SCHEMA_OMITTED_TOP_LEVEL_KEYS,
213
+ ]);
196
214
  const unknownKeys = Object.keys(parsed).filter((k) => !allKnownKeys.has(k));
197
215
  if (unknownKeys.length > 0) {
198
- output_1.default.yellow(`${label}${(0, texts_1.TXT_YAML_UNKNOWN_TOP_LEVEL_KEYS)(unknownKeys.join(', '))}`);
216
+ for (const key of unknownKeys) {
217
+ context.warnings.push({
218
+ pipeline: context.pipeline,
219
+ path: key,
220
+ message: (0, texts_1.TXT_YAML_UNKNOWN_TOP_LEVEL_KEYS)(key),
221
+ });
222
+ }
223
+ context.output.yellow(`${label}${(0, texts_1.TXT_YAML_UNKNOWN_TOP_LEVEL_KEYS)(unknownKeys.join(', '))}`);
199
224
  }
200
225
  const { itemSections, objectSections, settingsProps } = classifySections(props);
201
226
  if (Object.keys(settingsProps).length > 0) {
@@ -212,12 +237,12 @@ function validateSinglePipeline(parsed, label, unions) {
212
237
  $defs: rawSchema.$defs,
213
238
  }, settingsData);
214
239
  if (result.valid) {
215
- output_1.default.green(`${label}Pipeline settings`);
240
+ context.output.green(`${label}Pipeline settings`);
216
241
  }
217
242
  else {
218
243
  errors += result.errors.length;
219
- output_1.default.error(`${label}Pipeline settings`);
220
- outputErrors(result, ' ');
244
+ context.output.error(`${label}Pipeline settings`);
245
+ outputErrors(result, ' ', context);
221
246
  }
222
247
  }
223
248
  const itemByKey = new Map(itemSections.map((s) => [s.key, s]));
@@ -232,27 +257,52 @@ function validateSinglePipeline(parsed, label, unions) {
232
257
  const header = `${label}${prettifyKey(key)}`;
233
258
  if (itemByKey.has(key)) {
234
259
  if (!Array.isArray(value)) {
235
- errors += reportWrongContainer(header, 'array', value);
260
+ errors += reportWrongContainer(header, 'array', value, context, key);
236
261
  continue;
237
262
  }
238
- output_1.default.blue(header);
239
- errors += validateItemSection(unions, value, itemByKey.get(key));
263
+ context.output.blue(header);
264
+ errors += validateItemSection(unions, value, itemByKey.get(key), context);
240
265
  }
241
266
  else if (objByKey.has(key)) {
242
267
  if (typeof value !== 'object' || Array.isArray(value)) {
243
- errors += reportWrongContainer(header, 'object', value);
268
+ errors += reportWrongContainer(header, 'object', value, context, key);
244
269
  continue;
245
270
  }
246
- errors += validateObjectSection(unions, header, value, objByKey.get(key));
271
+ errors += validateObjectSection(unions, header, value, objByKey.get(key), context, key);
247
272
  }
248
273
  }
249
274
  return errors;
250
275
  }
251
- function validatePipelineFile(file, unions) {
276
+ function validatePipelineFile(file, unions, json) {
277
+ const context = {
278
+ output: {
279
+ normal: output_1.default.progress(json),
280
+ error: output_1.default.progress(json, output_1.default.error.bind(output_1.default)),
281
+ muted: output_1.default.progress(json, output_1.default.muted.bind(output_1.default)),
282
+ green: output_1.default.progress(json, output_1.default.green.bind(output_1.default)),
283
+ yellow: output_1.default.progress(json, output_1.default.yellow.bind(output_1.default)),
284
+ blue: output_1.default.progress(json, output_1.default.blue.bind(output_1.default)),
285
+ },
286
+ pipeline: 0,
287
+ errors: [],
288
+ warnings: [],
289
+ };
290
+ const invalidDocument = (message) => {
291
+ if (json) {
292
+ recordError(context, '', message);
293
+ output_1.default.json({
294
+ valid: false,
295
+ pipelines: 0,
296
+ errors: context.errors,
297
+ warnings: context.warnings,
298
+ });
299
+ output_1.default.exitCode(1);
300
+ }
301
+ output_1.default.exitError(message);
302
+ };
252
303
  const filePath = (0, path_1.resolve)(file);
253
304
  if (!(0, fs_1.existsSync)(filePath)) {
254
- output_1.default.exitError((0, texts_1.ERR_YAML_VALIDATE_FILE_NOT_FOUND)(file));
255
- return;
305
+ invalidDocument((0, texts_1.ERR_YAML_VALIDATE_FILE_NOT_FOUND)(file));
256
306
  }
257
307
  const YAML = require('yaml');
258
308
  const content = (0, fs_1.readFileSync)(filePath, 'utf8');
@@ -261,33 +311,40 @@ function validatePipelineFile(file, unions) {
261
311
  parsed = YAML.parse(content);
262
312
  }
263
313
  catch (e) {
264
- output_1.default.exitError((0, texts_1.ERR_YAML_PARSE)(e.message));
265
- return;
314
+ invalidDocument((0, texts_1.ERR_YAML_PARSE)(e.message));
266
315
  }
267
316
  if (!parsed || typeof parsed !== 'object') {
268
- output_1.default.exitError(texts_1.ERR_YAML_NOT_ARRAY);
269
- return;
317
+ invalidDocument(texts_1.ERR_YAML_NOT_ARRAY);
270
318
  }
271
319
  if (!Array.isArray(parsed)) {
272
- output_1.default.exitError(texts_1.ERR_YAML_PLAIN_OBJECT);
273
- return;
320
+ invalidDocument(texts_1.ERR_YAML_PLAIN_OBJECT);
274
321
  }
275
322
  if (parsed.length === 0) {
276
- output_1.default.exitError(texts_1.ERR_YAML_EMPTY_ARRAY);
277
- return;
323
+ invalidDocument(texts_1.ERR_YAML_EMPTY_ARRAY);
278
324
  }
279
325
  let errors = 0;
280
326
  for (let i = 0; i < parsed.length; i++) {
281
327
  const pipeline = parsed[i];
328
+ context.pipeline = i + 1;
282
329
  if (!pipeline || typeof pipeline !== 'object' || Array.isArray(pipeline)) {
283
- output_1.default.error(` ${(0, texts_1.ERR_YAML_PIPELINE_NOT_OBJECT)(i + 1)}`);
330
+ recordError(context, '', (0, texts_1.ERR_YAML_PIPELINE_NOT_OBJECT)(i + 1));
331
+ context.output.error(` ${(0, texts_1.ERR_YAML_PIPELINE_NOT_OBJECT)(i + 1)}`);
284
332
  errors++;
285
333
  continue;
286
334
  }
287
335
  const pipelineLabel = parsed.length > 1 ? `[${pipeline.pipeline || i + 1}] ` : '';
288
- errors += validateSinglePipeline(pipeline, pipelineLabel, unions);
336
+ errors += validateSinglePipeline(pipeline, pipelineLabel, unions, context);
337
+ }
338
+ if (json) {
339
+ output_1.default.json({
340
+ valid: errors === 0,
341
+ pipelines: parsed.length,
342
+ errors: context.errors,
343
+ warnings: context.warnings,
344
+ });
345
+ output_1.default.exitCode(errors > 0 ? 1 : 0);
289
346
  }
290
- output_1.default.normal('');
347
+ context.output.normal('');
291
348
  if (errors > 0) {
292
349
  output_1.default.exitError((0, texts_1.ERR_YAML_VALIDATE_FAILED_COUNT)(errors));
293
350
  }
@@ -296,11 +353,13 @@ function validatePipelineFile(file, unions) {
296
353
  }
297
354
  }
298
355
  const commandYamlValidate = (0, utils_1.newCommand)('validate', texts_1.DESC_COMMAND_YAML_VALIDATE, {
356
+ helpText: texts_1.HELP_YAML_VALIDATE_JSON,
299
357
  examples: texts_1.EXAMPLE_YAML_VALIDATE,
300
358
  });
301
359
  commandYamlValidate.argument('<file>', texts_1.OPTION_YAML_VALIDATE_FILE);
302
- commandYamlValidate.action(async (file) => {
360
+ commandYamlValidate.option('--format <text|json>', texts_1.OPTION_FORMAT);
361
+ commandYamlValidate.action(async (file, options) => {
303
362
  await (0, schemaUtils_1.ensureYamlSchema)();
304
- validatePipelineFile(file, await unionMappings((0, schemaUtils_1.getAllDefs)()));
363
+ validatePipelineFile(file, await unionMappings((0, schemaUtils_1.getAllDefs)()), options.format === 'json');
305
364
  });
306
365
  exports.default = commandYamlValidate;
@@ -1,11 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.apiEndpoints = apiEndpoints;
4
+ exports.apiSpecification = apiSpecification;
4
5
  const docs_schema_1 = require("@buddy-works/docs-schema");
5
6
  const texts_1 = require("./texts");
6
7
  const utils_1 = require("./utils");
7
8
  const diskCache_1 = require("./diskCache");
8
9
  let held = null;
10
+ let specification = null;
9
11
  async function apiEndpoints(baseUrl) {
10
12
  if (held)
11
13
  return held;
@@ -18,10 +20,15 @@ async function apiEndpoints(baseUrl) {
18
20
  if (!raw)
19
21
  throw new Error(texts_1.ERR_SCHEMA_FETCH_FAILED);
20
22
  try {
21
- held = docs_schema_1.BuddySchema.from({ api: JSON.parse(raw) }).api;
23
+ specification = JSON.parse(raw);
24
+ held = docs_schema_1.BuddySchema.from({ api: specification }).api;
22
25
  }
23
26
  catch {
24
27
  throw new Error(texts_1.ERR_SCHEMA_FETCH_FAILED);
25
28
  }
26
29
  return held;
27
30
  }
31
+ async function apiSpecification(baseUrl) {
32
+ await apiEndpoints(baseUrl);
33
+ return specification;
34
+ }
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const texts_1 = require("./texts");
7
7
  const utils_1 = require("./utils");
8
8
  const chalk_1 = __importDefault(require("chalk"));
9
+ const node_readline_1 = require("node:readline");
9
10
  let _terminal = null;
10
11
  function getTerminal() {
11
12
  if (!_terminal) {
@@ -134,6 +135,15 @@ class Output {
134
135
  msg += '\n';
135
136
  getTerminal()(msg);
136
137
  }
138
+ static progress(quiet, output = this.normal.bind(this)) {
139
+ return (txt, newLine = true, replacePrevious = false) => {
140
+ if (quiet)
141
+ return;
142
+ if (replacePrevious)
143
+ this.clearPreviousLine();
144
+ output(txt, newLine);
145
+ };
146
+ }
137
147
  // Payload output (command results, logs, JSON): bypasses terminal-kit,
138
148
  // whose printf-style formatting and ^-markup corrupt data containing
139
149
  // % or ^ sequences. newLine=false emits a mid-stream chunk, keeping
@@ -652,6 +662,35 @@ class Output {
652
662
  static json(json) {
653
663
  this.data(JSON.stringify(json));
654
664
  }
665
+ static async streamCommandLogs(body, jsonl) {
666
+ // readline preserves UTF-8 and records split across transport chunks.
667
+ const lines = (0, node_readline_1.createInterface)({ input: body, crlfDelay: Infinity });
668
+ for await (const line of lines) {
669
+ if (!line.trim())
670
+ continue;
671
+ let record;
672
+ try {
673
+ record = JSON.parse(line);
674
+ }
675
+ catch {
676
+ continue;
677
+ }
678
+ if (typeof record?.data !== 'string')
679
+ continue;
680
+ if (jsonl) {
681
+ this.json({
682
+ type: 'log',
683
+ ...(record.stream === 'stdout' || record.stream === 'stderr'
684
+ ? { stream: record.stream }
685
+ : {}),
686
+ data: record.data,
687
+ });
688
+ }
689
+ else if (record.data) {
690
+ this.data(record.data);
691
+ }
692
+ }
693
+ }
655
694
  static exitError(err) {
656
695
  this.clearUndici();
657
696
  this.clearTerminal();