@chenguangyao/devflow-kit 0.1.43 → 0.1.44

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.
@@ -31,10 +31,12 @@
31
31
  "definitions": {
32
32
  "actionMap": {
33
33
  "type": "object",
34
- "required": ["picker", "applySelection", "check", "confirm"],
34
+ "required": ["picker", "applySelection", "applySelectionFile", "card", "check", "confirm"],
35
35
  "properties": {
36
36
  "picker": { "type": "string" },
37
37
  "applySelection": { "type": "string" },
38
+ "applySelectionFile": { "type": "string" },
39
+ "card": { "type": "string" },
38
40
  "check": { "type": "string" },
39
41
  "confirm": { "type": "string" }
40
42
  },
@@ -56,6 +56,8 @@
56
56
  "locked",
57
57
  "source",
58
58
  "status",
59
+ "after",
60
+ "before",
59
61
  "installed",
60
62
  "command"
61
63
  ],
@@ -73,6 +75,8 @@
73
75
  "locked": { "type": "boolean" },
74
76
  "source": { "type": ["string", "null"] },
75
77
  "status": { "type": ["string", "null"] },
78
+ "after": { "type": ["string", "null"] },
79
+ "before": { "type": ["string", "null"] },
76
80
  "installed": { "type": "boolean" },
77
81
  "command": { "type": ["string", "null"] }
78
82
  },
@@ -80,11 +84,12 @@
80
84
  },
81
85
  "actionMap": {
82
86
  "type": "object",
83
- "required": ["card", "check", "applySelection", "confirm", "suggest"],
87
+ "required": ["card", "check", "applySelection", "applySelectionFile", "confirm", "suggest"],
84
88
  "properties": {
85
89
  "card": { "type": "string" },
86
90
  "check": { "type": "string" },
87
91
  "applySelection": { "type": "string" },
92
+ "applySelectionFile": { "type": "string" },
88
93
  "confirm": { "type": "string" },
89
94
  "suggest": { "type": "string" }
90
95
  },
@@ -0,0 +1,69 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://devflow.dev/schemas/workflow-selection-result.schema.json",
4
+ "title": "devflow workflow selection result surface",
5
+ "description": "Machine-readable successful response returned by devflow flow apply-selection --json.",
6
+ "type": "object",
7
+ "required": [
8
+ "type",
9
+ "slug",
10
+ "ok",
11
+ "added",
12
+ "disabled",
13
+ "skipped",
14
+ "diff",
15
+ "actions",
16
+ "nextAction"
17
+ ],
18
+ "properties": {
19
+ "type": { "const": "workflow_selection_result_surface" },
20
+ "slug": { "type": "string" },
21
+ "ok": { "const": true },
22
+ "added": {
23
+ "type": "array",
24
+ "items": { "type": "string" }
25
+ },
26
+ "disabled": {
27
+ "type": "array",
28
+ "items": { "type": "string" }
29
+ },
30
+ "skipped": {
31
+ "type": "array",
32
+ "items": {
33
+ "type": "object",
34
+ "required": ["step", "reason"],
35
+ "properties": {
36
+ "step": { "type": ["string", "null"] },
37
+ "reason": { "type": "string" }
38
+ },
39
+ "additionalProperties": true
40
+ }
41
+ },
42
+ "diff": {
43
+ "type": "object",
44
+ "required": ["added", "disabled", "moved", "verifyReports"],
45
+ "properties": {
46
+ "added": { "type": "array", "items": { "type": "string" } },
47
+ "disabled": { "type": "array", "items": { "type": "string" } },
48
+ "moved": { "type": "array", "items": { "type": "string" } },
49
+ "verifyReports": { "type": "array", "items": { "type": "string" } }
50
+ },
51
+ "additionalProperties": true
52
+ },
53
+ "actions": {
54
+ "type": "object",
55
+ "required": ["picker", "applySelection", "applySelectionFile", "card", "check", "confirm"],
56
+ "properties": {
57
+ "picker": { "type": "string" },
58
+ "applySelection": { "type": "string" },
59
+ "applySelectionFile": { "type": "string" },
60
+ "card": { "type": "string" },
61
+ "check": { "type": "string" },
62
+ "confirm": { "type": "string" }
63
+ },
64
+ "additionalProperties": true
65
+ },
66
+ "nextAction": { "type": "string" }
67
+ },
68
+ "additionalProperties": true
69
+ }
@@ -0,0 +1,84 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://devflow.dev/schemas/workflow-selection.schema.json",
4
+ "title": "devflow workflow selection input",
5
+ "description": "Input accepted by devflow flow apply-selection --selection=<json>. UI may send a minimal items list or the edited workflow picker surface.",
6
+ "oneOf": [
7
+ { "$ref": "#/definitions/selection", "title": "Minimal workflow selection" },
8
+ { "$ref": "#/definitions/pickerSurface", "title": "Edited workflow picker surface" },
9
+ { "$ref": "#/definitions/chatSelection", "title": "Edited workflow chat selection" }
10
+ ],
11
+ "definitions": {
12
+ "selection": {
13
+ "type": "object",
14
+ "required": ["items"],
15
+ "properties": {
16
+ "items": {
17
+ "type": "array",
18
+ "items": { "$ref": "#/definitions/item" }
19
+ }
20
+ },
21
+ "additionalProperties": true
22
+ },
23
+ "pickerSurface": {
24
+ "type": "object",
25
+ "required": ["type", "groups"],
26
+ "properties": {
27
+ "type": { "const": "workflow_picker_surface" },
28
+ "groups": {
29
+ "type": "array",
30
+ "items": {
31
+ "type": "object",
32
+ "required": ["items"],
33
+ "properties": {
34
+ "items": {
35
+ "type": "array",
36
+ "items": { "$ref": "#/definitions/item" }
37
+ }
38
+ },
39
+ "additionalProperties": true
40
+ }
41
+ }
42
+ },
43
+ "additionalProperties": true
44
+ },
45
+ "chatSelection": {
46
+ "type": "object",
47
+ "required": ["type", "groups"],
48
+ "properties": {
49
+ "type": { "const": "workflow_chat_selection" },
50
+ "layout": { "const": "linear-checkboxes" },
51
+ "groups": {
52
+ "type": "array",
53
+ "items": {
54
+ "type": "object",
55
+ "required": ["items"],
56
+ "properties": {
57
+ "id": { "type": "string" },
58
+ "label": { "type": "string" },
59
+ "items": {
60
+ "type": "array",
61
+ "items": { "$ref": "#/definitions/item" }
62
+ }
63
+ },
64
+ "additionalProperties": true
65
+ }
66
+ }
67
+ },
68
+ "additionalProperties": true
69
+ },
70
+ "item": {
71
+ "type": "object",
72
+ "required": ["step", "selected"],
73
+ "properties": {
74
+ "step": { "type": "string" },
75
+ "skill": { "type": "string" },
76
+ "selected": { "type": "boolean" },
77
+ "reason": { "type": ["string", "null"] },
78
+ "after": { "type": ["string", "null"] },
79
+ "before": { "type": ["string", "null"] }
80
+ },
81
+ "additionalProperties": true
82
+ }
83
+ }
84
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://devflow.dev/schemas/workflow-ui-command-result.schema.json",
4
+ "title": "devflow workflow UI command result surface",
5
+ "description": "Stable command result surface returned by devflow local workflow UI host mutation endpoints.",
6
+ "type": "object",
7
+ "required": ["type", "schema", "ok", "slug", "output", "error"],
8
+ "properties": {
9
+ "type": {
10
+ "const": "workflow_ui_command_result"
11
+ },
12
+ "schema": {
13
+ "const": "https://devflow.dev/schemas/workflow-ui-command-result.schema.json"
14
+ },
15
+ "ok": {
16
+ "type": "boolean"
17
+ },
18
+ "slug": {
19
+ "type": "string"
20
+ },
21
+ "output": {
22
+ "type": "string"
23
+ },
24
+ "error": {
25
+ "type": ["string", "null"]
26
+ }
27
+ },
28
+ "additionalProperties": true
29
+ }
@@ -0,0 +1,49 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://devflow.dev/schemas/workflow-ui-error.schema.json",
4
+ "title": "devflow workflow UI error surface",
5
+ "description": "Stable error surface returned by devflow local workflow UI host endpoints.",
6
+ "type": "object",
7
+ "required": ["type", "schema", "ok", "code", "message"],
8
+ "properties": {
9
+ "type": {
10
+ "const": "workflow_ui_error"
11
+ },
12
+ "schema": {
13
+ "const": "https://devflow.dev/schemas/workflow-ui-error.schema.json"
14
+ },
15
+ "ok": {
16
+ "const": false
17
+ },
18
+ "code": {
19
+ "type": "string",
20
+ "enum": [
21
+ "invalid-workflow-selection",
22
+ "invalid-json-body",
23
+ "invalid-checkpoint-resolution",
24
+ "request-body-too-large",
25
+ "route-not-found",
26
+ "devflow-command-failed",
27
+ "devflow-json-missing",
28
+ "workflow-ui-error"
29
+ ]
30
+ },
31
+ "message": {
32
+ "type": "string"
33
+ },
34
+ "details": {},
35
+ "command": {
36
+ "type": "string"
37
+ },
38
+ "exitCode": {
39
+ "type": "number"
40
+ },
41
+ "stdout": {
42
+ "type": "string"
43
+ },
44
+ "stderr": {
45
+ "type": "string"
46
+ }
47
+ },
48
+ "additionalProperties": true
49
+ }
@@ -10,7 +10,12 @@ const childProcess = require('child_process');
10
10
  function renderWorkflowUiPrototype(options = {}) {
11
11
  const root = options.root || path.resolve(__dirname, '..');
12
12
  const fixtures = options.pickers && options.card
13
- ? { pickers: options.pickers, card: options.card }
13
+ ? {
14
+ pickers: options.pickers,
15
+ card: options.card,
16
+ selectionResults: options.selectionResults || [],
17
+ policyCards: options.policyCards || [],
18
+ }
14
19
  : loadFixtureSurfaces({ root });
15
20
  const sourceLabel = options.sourceLabel || fixtures.card.slug || fixtures.card.baseRecipe?.id || 'workflow';
16
21
 
@@ -35,7 +40,9 @@ function renderWorkflowUiPrototype(options = {}) {
35
40
  ` <code>${escapeHtml(sourceLabel)} · ${escapeHtml(fixtures.card.actions.picker || fixtures.card.nextAction || '-')}</code>`,
36
41
  ' </section>',
37
42
  fixtures.pickers.map((entry) => renderPickerSurface(entry.surface, entry.title)).join('\n'),
43
+ (fixtures.selectionResults || []).map(renderSelectionResultSurface).join('\n'),
38
44
  renderConfirmationSurface(fixtures.card),
45
+ (fixtures.policyCards || []).map(renderPolicyConfirmationSurface).join('\n'),
39
46
  ' </main>',
40
47
  '</body>',
41
48
  '</html>',
@@ -50,6 +57,12 @@ function loadFixtureSurfaces({ root = path.resolve(__dirname, '..') } = {}) {
50
57
  { title: '风险推荐', surface: readJson(root, 'test/fixtures/workflow-surfaces/picker-risk-recommended.json') },
51
58
  ],
52
59
  card: readJson(root, 'test/fixtures/workflow-surfaces/confirmation-card.json'),
60
+ selectionResults: [
61
+ readJson(root, 'test/fixtures/workflow-surfaces/selection-result.json'),
62
+ ],
63
+ policyCards: [
64
+ readJson(root, 'test/fixtures/workflow-surfaces/policy-confirmation-card.json'),
65
+ ],
53
66
  };
54
67
  }
55
68
 
@@ -57,15 +70,36 @@ function loadWorkflowSurfacesFromCli({
57
70
  root = path.resolve(__dirname, '..'),
58
71
  slug,
59
72
  cwd = process.cwd(),
73
+ selection = null,
74
+ selectionFile = null,
60
75
  execFileSync = childProcess.execFileSync,
61
76
  } = {}) {
62
77
  if (!slug) throw new Error('loadWorkflowSurfacesFromCli requires slug');
63
78
  const picker = runDevflowJson(root, cwd, ['flow', 'picker', `--slug=${slug}`, '--json'], execFileSync);
79
+ let selectionArgs = null;
80
+ if (selection) {
81
+ selectionArgs = ['flow', 'apply-selection', `--slug=${slug}`, `--selection=${selection}`, '--json'];
82
+ } else if (selectionFile) {
83
+ selectionArgs = picker.actions?.applySelectionFile
84
+ ? actionToDevflowArgs(picker.actions.applySelectionFile, { '<file>': selectionFile })
85
+ : ['flow', 'apply-selection', `--slug=${slug}`, `--selection-file=${selectionFile}`, '--json'];
86
+ }
87
+ const selectionSurface = selectionArgs
88
+ ? runDevflowJson(root, cwd, selectionArgs, execFileSync)
89
+ : null;
64
90
  const card = runDevflowJson(root, cwd, ['flow', 'card', `--slug=${slug}`, '--json'], execFileSync);
65
- return {
91
+ const surfaces = {
66
92
  pickers: [{ title: '当前 workflow', surface: picker }],
67
93
  card,
94
+ selectionResults: [],
95
+ policyCards: [],
68
96
  };
97
+ if (selectionSurface?.type === 'workflow_policy_confirmation_surface') {
98
+ surfaces.policyCards.push(selectionSurface);
99
+ } else if (selectionSurface) {
100
+ surfaces.selectionResults.push(selectionSurface);
101
+ }
102
+ return surfaces;
69
103
  }
70
104
 
71
105
  function runDevflowJson(root, cwd, args, execFileSync) {
@@ -77,6 +111,45 @@ function runDevflowJson(root, cwd, args, execFileSync) {
77
111
  return JSON.parse(String(out));
78
112
  }
79
113
 
114
+ function actionToDevflowArgs(action, replacements = {}) {
115
+ const tokens = splitActionTokens(action).map((token) => replacePlaceholders(token, replacements));
116
+ if (tokens[0] !== 'devflow') {
117
+ throw new Error(`unsupported workflow action: ${action}`);
118
+ }
119
+ return tokens.slice(1);
120
+ }
121
+
122
+ function splitActionTokens(action) {
123
+ const tokens = [];
124
+ let token = '';
125
+ let quote = null;
126
+ for (const ch of String(action)) {
127
+ if (quote) {
128
+ if (ch === quote) quote = null;
129
+ else token += ch;
130
+ } else if (ch === '\'' || ch === '"') {
131
+ quote = ch;
132
+ } else if (/\s/.test(ch)) {
133
+ if (token) {
134
+ tokens.push(token);
135
+ token = '';
136
+ }
137
+ } else {
138
+ token += ch;
139
+ }
140
+ }
141
+ if (token) tokens.push(token);
142
+ return tokens;
143
+ }
144
+
145
+ function replacePlaceholders(value, replacements) {
146
+ let out = value;
147
+ for (const [placeholder, replacement] of Object.entries(replacements)) {
148
+ out = out.split(placeholder).join(replacement);
149
+ }
150
+ return out;
151
+ }
152
+
80
153
  function readJson(root, rel) {
81
154
  return JSON.parse(fs.readFileSync(path.join(root, rel), 'utf8'));
82
155
  }
@@ -147,6 +220,55 @@ function renderConfirmationSurface(surface) {
147
220
  ].join('\n');
148
221
  }
149
222
 
223
+ function renderSelectionResultSurface(surface) {
224
+ return [
225
+ ' <section class="surface result" data-surface="workflow-selection-result" aria-label="Workflow selection result">',
226
+ ` <div class="surface-head"><h2>选择已应用</h2><span>${escapeHtml(surface.type)} / ${surface.ok ? 'ok' : 'blocked'}</span></div>`,
227
+ ' <div class="result-grid">',
228
+ renderResultMetric('新增', surface.added),
229
+ renderResultMetric('禁用', surface.disabled),
230
+ renderResultMetric('移动', surface.diff?.moved || []),
231
+ renderResultMetric('验证报告', surface.diff?.verifyReports || []),
232
+ ' </div>',
233
+ ' <div class="actions">',
234
+ ` <button type="button">continue</button>`,
235
+ ` <code>${escapeHtml(surface.nextAction)}</code>`,
236
+ ` <code>${escapeHtml(surface.actions.picker)}</code>`,
237
+ ' </div>',
238
+ ' </section>',
239
+ ].join('\n');
240
+ }
241
+
242
+ function renderResultMetric(label, values = []) {
243
+ const text = values.length ? values.join(', ') : '-';
244
+ return [
245
+ ' <div>',
246
+ ` <small>${escapeHtml(label)}</small>`,
247
+ ` <strong>${escapeHtml(text)}</strong>`,
248
+ ' </div>',
249
+ ].join('\n');
250
+ }
251
+
252
+ function renderPolicyConfirmationSurface(surface) {
253
+ const card = surface.confirmationCard;
254
+ const secondary = (card.secondaryActions || [])[0];
255
+ return [
256
+ ' <section class="surface policy" data-surface="workflow-policy-card" aria-label="Workflow policy confirmation card">',
257
+ ` <div class="surface-head"><h2>${escapeHtml(card.title || '确认 workflow 风险')}</h2><span>${escapeHtml(card.type)} / ${escapeHtml(surface.checkpoint.status || '-')}</span></div>`,
258
+ ` <p>${escapeHtml(card.question || surface.checkpoint.summary || '')}</p>`,
259
+ ' <div class="risk-box">',
260
+ ` <strong>${escapeHtml(surface.checkpoint.summary || '-')}</strong>`,
261
+ ` <small>${escapeHtml(surface.checkpoint.id)} · ${escapeHtml(surface.checkpoint.phase || '-')}</small>`,
262
+ ' </div>',
263
+ ' <div class="actions">',
264
+ ` <button type="button">${escapeHtml(card.primaryAction.id)}</button>`,
265
+ ` <code>${escapeHtml(card.primaryAction.command)}</code>`,
266
+ secondary ? ` <code>${escapeHtml(secondary.command)}</code>` : '',
267
+ ' </div>',
268
+ ' </section>',
269
+ ].filter(Boolean).join('\n');
270
+ }
271
+
150
272
  function renderTimelineStep(step, num) {
151
273
  const tags = [
152
274
  step.required ? 'required' : null,
@@ -188,11 +310,17 @@ function renderCss() {
188
310
  ' small { color: var(--muted); font-size: 12px; line-height: 1.35; }',
189
311
  ' li p { color: var(--warn); font-size: 12px; margin: 0; }',
190
312
  ' .command-row, .actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; align-items: center; }',
313
+ ' .result { border-color: #95c9bf; background: #f5fbf9; }',
314
+ ' .result-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; }',
315
+ ' .result-grid div { display: grid; gap: 5px; border: 1px solid var(--line); border-radius: 8px; background: white; padding: 12px; min-height: 72px; }',
316
+ ' .result-grid strong { overflow-wrap: anywhere; }',
317
+ ' .policy { border-color: #e3b16f; background: #fffaf3; }',
318
+ ' .risk-box { display: grid; gap: 4px; margin-top: 12px; border: 1px solid #e3b16f; border-radius: 8px; background: white; padding: 12px; }',
191
319
  ' .timeline { display: grid; grid-template-columns: repeat(8, minmax(96px, 1fr)); gap: 8px; overflow-x: auto; padding-bottom: 4px; }',
192
320
  ' .timeline li { min-width: 96px; border: 1px solid var(--line); border-radius: 8px; padding: 10px; background: var(--panel); display: grid; gap: 5px; }',
193
321
  ' .timeline span { display: grid; place-items: center; width: 22px; height: 22px; border-radius: 999px; background: var(--accent); color: white; font-size: 12px; font-weight: 700; }',
194
322
  ' button { border: 0; border-radius: 6px; padding: 8px 12px; background: var(--accent); color: white; font-weight: 700; }',
195
- ' @media (max-width: 860px) { .toolbar { align-items: start; flex-direction: column; } .groups { grid-template-columns: 1fr; } .timeline { grid-template-columns: repeat(4, minmax(96px, 1fr)); } }',
323
+ ' @media (max-width: 860px) { .toolbar { align-items: start; flex-direction: column; } .groups, .result-grid { grid-template-columns: 1fr; } .timeline { grid-template-columns: repeat(4, minmax(96px, 1fr)); } }',
196
324
  ].join('\n');
197
325
  }
198
326
 
@@ -234,7 +362,13 @@ if (require.main === module) {
234
362
  const args = parseArgs(process.argv.slice(2));
235
363
  const root = args.root || path.resolve(__dirname, '..');
236
364
  const surfaces = args.slug
237
- ? loadWorkflowSurfacesFromCli({ root, slug: args.slug, cwd: args.cwd || process.cwd() })
365
+ ? loadWorkflowSurfacesFromCli({
366
+ root,
367
+ slug: args.slug,
368
+ cwd: args.cwd || process.cwd(),
369
+ selection: args.selection || null,
370
+ selectionFile: args.selectionFile || null,
371
+ })
238
372
  : loadFixtureSurfaces({ root });
239
373
  const options = {
240
374
  root,
@@ -257,6 +391,8 @@ function parseArgs(argv) {
257
391
  else if (arg.startsWith('--cwd=')) out.cwd = arg.slice('--cwd='.length);
258
392
  else if (arg.startsWith('--root=')) out.root = arg.slice('--root='.length);
259
393
  else if (arg.startsWith('--out=')) out.out = arg.slice('--out='.length);
394
+ else if (arg.startsWith('--selection=')) out.selection = arg.slice('--selection='.length);
395
+ else if (arg.startsWith('--selection-file=')) out.selectionFile = arg.slice('--selection-file='.length);
260
396
  }
261
397
  return out;
262
398
  }
@@ -265,6 +401,7 @@ module.exports = {
265
401
  renderWorkflowUiPrototype,
266
402
  loadFixtureSurfaces,
267
403
  loadWorkflowSurfacesFromCli,
404
+ actionToDevflowArgs,
268
405
  outputPathFor,
269
406
  writePrototype,
270
407
  parseArgs,
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+
7
+ const client = require('../src/client/workflow-adapter-client.js');
8
+
9
+ function parseArgs(argv) {
10
+ const flags = {};
11
+ for (const arg of argv) {
12
+ if (!arg.startsWith('--')) continue;
13
+ const eq = arg.indexOf('=');
14
+ if (eq === -1) flags[arg.slice(2)] = true;
15
+ else flags[arg.slice(2, eq)] = arg.slice(eq + 1);
16
+ }
17
+ return flags;
18
+ }
19
+
20
+ function parseSelectedSteps(flags) {
21
+ const raw = flags.steps || flags.selected || '';
22
+ if (!raw || raw === true) return null;
23
+ return String(raw).split(',').map((item) => item.trim()).filter(Boolean);
24
+ }
25
+
26
+ function readSurface(flags) {
27
+ if (flags['surface-json']) return JSON.parse(String(flags['surface-json']));
28
+ const file = flags['surface-file'];
29
+ if (!file || file === true) throw new Error('usage: node scripts/workflow-adapter-client-example.js --surface-file=<adapter.json> [--host=<name>]');
30
+ return JSON.parse(fs.readFileSync(path.resolve(file), 'utf8'));
31
+ }
32
+
33
+ function readCatalog(flags) {
34
+ if (flags['catalog-json']) return JSON.parse(String(flags['catalog-json']));
35
+ const file = flags['catalog-file'];
36
+ if (!file || file === true) return null;
37
+ return JSON.parse(fs.readFileSync(path.resolve(file), 'utf8'));
38
+ }
39
+
40
+ async function main(argv = process.argv.slice(2)) {
41
+ const flags = parseArgs(argv);
42
+ const catalog = readCatalog(flags);
43
+ if (catalog) {
44
+ process.stdout.write(client.renderCatalogExample(catalog, { host: flags.host }));
45
+ process.stdout.write('\n');
46
+ return;
47
+ }
48
+
49
+ const surface = readSurface(flags);
50
+ if (flags.submit === true || flags['dry-run'] === true) {
51
+ const selected = parseSelectedSteps(flags) || client.collectDefaultSelectedSteps(surface);
52
+ const result = await client.submitSelection(surface, selected, { dryRun: flags['dry-run'] === true });
53
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
54
+ return;
55
+ }
56
+ process.stdout.write(client.renderHostExample(surface, { host: flags.host }));
57
+ process.stdout.write('\n');
58
+ }
59
+
60
+ if (require.main === module) {
61
+ main().catch((err) => {
62
+ process.stderr.write(`${err.message}\n`);
63
+ process.exitCode = 2;
64
+ });
65
+ }
66
+
67
+ module.exports = client;