@hivecommons/promptargs 0.7.0 → 0.8.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.
Files changed (80) hide show
  1. package/README.md +100 -15
  2. package/dist/autodetect.js +24 -9
  3. package/dist/autodetect.js.map +1 -1
  4. package/dist/cli.js +51 -147
  5. package/dist/cli.js.map +1 -1
  6. package/dist/commands.d.ts +4 -0
  7. package/dist/commands.js +130 -0
  8. package/dist/commands.js.map +1 -0
  9. package/dist/flags.d.ts +14 -0
  10. package/dist/flags.js +57 -0
  11. package/dist/flags.js.map +1 -0
  12. package/dist/loader.d.ts +2 -0
  13. package/dist/loader.js +64 -11
  14. package/dist/loader.js.map +1 -1
  15. package/dist/log.d.ts +19 -0
  16. package/dist/log.js +41 -0
  17. package/dist/log.js.map +1 -0
  18. package/dist/parser.js +33 -7
  19. package/dist/parser.js.map +1 -1
  20. package/dist/resolver.d.ts +2 -0
  21. package/dist/resolver.js +60 -30
  22. package/dist/resolver.js.map +1 -1
  23. package/dist/status.d.ts +1 -1
  24. package/dist/status.js +5 -4
  25. package/dist/status.js.map +1 -1
  26. package/dist/ui.html +18 -5
  27. package/dist/ui.js +36 -11
  28. package/dist/ui.js.map +1 -1
  29. package/package.json +13 -4
  30. package/dist/autodetect.test.d.ts +0 -1
  31. package/dist/autodetect.test.js +0 -207
  32. package/dist/autodetect.test.js.map +0 -1
  33. package/dist/cli.test.d.ts +0 -1
  34. package/dist/cli.test.js +0 -310
  35. package/dist/cli.test.js.map +0 -1
  36. package/dist/index.test.d.ts +0 -1
  37. package/dist/index.test.js +0 -59
  38. package/dist/index.test.js.map +0 -1
  39. package/dist/iterate.test.d.ts +0 -1
  40. package/dist/iterate.test.js +0 -20
  41. package/dist/iterate.test.js.map +0 -1
  42. package/dist/loader.test.d.ts +0 -1
  43. package/dist/loader.test.js +0 -65
  44. package/dist/loader.test.js.map +0 -1
  45. package/dist/parser.test.d.ts +0 -1
  46. package/dist/parser.test.js +0 -93
  47. package/dist/parser.test.js.map +0 -1
  48. package/dist/resolver.test.d.ts +0 -1
  49. package/dist/resolver.test.js +0 -184
  50. package/dist/resolver.test.js.map +0 -1
  51. package/dist/sanitize.test.d.ts +0 -1
  52. package/dist/sanitize.test.js +0 -38
  53. package/dist/sanitize.test.js.map +0 -1
  54. package/dist/status.test.d.ts +0 -1
  55. package/dist/status.test.js +0 -33
  56. package/dist/status.test.js.map +0 -1
  57. package/dist/ui-bootstrap.test.d.ts +0 -1
  58. package/dist/ui-bootstrap.test.js +0 -479
  59. package/dist/ui-bootstrap.test.js.map +0 -1
  60. package/dist/ui-builder.test.d.ts +0 -1
  61. package/dist/ui-builder.test.js +0 -272
  62. package/dist/ui-builder.test.js.map +0 -1
  63. package/dist/ui-command-quoting.test.d.ts +0 -1
  64. package/dist/ui-command-quoting.test.js +0 -95
  65. package/dist/ui-command-quoting.test.js.map +0 -1
  66. package/dist/ui-harness.d.ts +0 -20
  67. package/dist/ui-harness.js +0 -40
  68. package/dist/ui-harness.js.map +0 -1
  69. package/dist/ui-harness.test.d.ts +0 -1
  70. package/dist/ui-harness.test.js +0 -30
  71. package/dist/ui-harness.test.js.map +0 -1
  72. package/dist/ui-inline.test.d.ts +0 -1
  73. package/dist/ui-inline.test.js +0 -90
  74. package/dist/ui-inline.test.js.map +0 -1
  75. package/dist/ui-interactions.test.d.ts +0 -1
  76. package/dist/ui-interactions.test.js +0 -489
  77. package/dist/ui-interactions.test.js.map +0 -1
  78. package/dist/ui.test.d.ts +0 -1
  79. package/dist/ui.test.js +0 -283
  80. package/dist/ui.test.js.map +0 -1
package/dist/resolver.js CHANGED
@@ -6,31 +6,46 @@ import { createInterface } from 'node:readline';
6
6
  import { readFileSync, readdirSync, existsSync } from 'node:fs';
7
7
  import { join } from 'node:path';
8
8
  import { autodetect } from './autodetect.js';
9
+ import { UsageError } from './flags.js';
9
10
  import { cartesian, zip } from './iterate.js';
10
- function expandArrayValue(raw) {
11
- // @file — read lines from file
12
- if (raw.startsWith('@') && existsSync(raw.slice(1))) {
13
- return readFileSync(raw.slice(1), 'utf8')
14
- .split('\n')
15
- .map((l) => l.trim())
16
- .filter(Boolean);
17
- }
18
- // - (stdin) handled at CLI level before this
19
- if (raw === '-')
20
- return [raw];
11
+ function expandPiece(piece) {
21
12
  // Glob pattern — expanded in-process (never via a shell) to avoid
22
13
  // command injection through crafted values.
23
- if (raw.includes('*') || raw.includes('?')) {
24
- const files = expandGlob(raw);
14
+ if (piece.includes('*') || piece.includes('?')) {
15
+ const files = expandGlob(piece);
25
16
  if (files.length > 0)
26
17
  return files;
27
18
  // Not a valid glob, treat as literal
28
19
  }
29
- // Comma-separated
20
+ return [piece];
21
+ }
22
+ function expandArrayValue(raw, varName) {
23
+ // @file — read lines from file
24
+ if (raw.startsWith('@') && existsSync(raw.slice(1))) {
25
+ const filePath = raw.slice(1);
26
+ try {
27
+ return readFileSync(filePath, 'utf8')
28
+ .split('\n')
29
+ .map((l) => l.trim())
30
+ .filter(Boolean)
31
+ .flatMap(expandPiece);
32
+ }
33
+ catch (err) {
34
+ const e = err;
35
+ if (e.code === 'EISDIR') {
36
+ throw new UsageError(`Variable "${varName}": @${filePath} is a directory, not a file; pass @FILE with one value per line`);
37
+ }
38
+ throw new UsageError(`Variable "${varName}": could not read @${filePath}: ${e.message}`);
39
+ }
40
+ }
41
+ // - (stdin) handled at CLI level before this
42
+ if (raw === '-')
43
+ return [raw];
44
+ // Comma-separated: split first so each piece can be a glob
30
45
  if (raw.includes(',')) {
31
- return raw.split(',').map(s => s.trim()).filter(Boolean);
46
+ return raw.split(',').map(s => s.trim()).filter(Boolean).flatMap(expandPiece);
32
47
  }
33
- return [raw];
48
+ return expandPiece(raw);
34
49
  }
35
50
  function segmentToRegExp(segment) {
36
51
  const escaped = segment
@@ -78,21 +93,24 @@ function expandGlob(pattern) {
78
93
  return bases.sort();
79
94
  }
80
95
  export async function resolve(vars, flags, interactive, cross = false) {
81
- const values = {};
82
- const arrayVars = {};
96
+ const values = Object.create(null);
97
+ const arrayVars = Object.create(null);
98
+ const defaulted = new Set();
83
99
  let hasArrays = false;
84
100
  for (const v of vars) {
85
101
  // 1. Check flags
86
- if (v.name in flags) {
87
- const expanded = expandArrayValue(flags[v.name]);
102
+ if (Object.hasOwn(flags, v.name)) {
103
+ const expanded = expandArrayValue(flags[v.name], v.name);
88
104
  if (expanded.length > 1) {
89
105
  arrayVars[v.name] = expanded;
90
106
  hasArrays = true;
107
+ continue;
91
108
  }
92
- else {
109
+ // An empty expansion falls through as if the flag were absent
110
+ if (expanded.length === 1) {
93
111
  values[v.name] = expanded[0];
112
+ continue;
94
113
  }
95
- continue;
96
114
  }
97
115
  // 2. Auto-detect
98
116
  const auto = autodetect(v.name);
@@ -103,42 +121,54 @@ export async function resolve(vars, flags, interactive, cross = false) {
103
121
  // 3. Default
104
122
  if (v.defaultValue !== undefined) {
105
123
  values[v.name] = v.defaultValue;
124
+ defaulted.add(v.name);
106
125
  continue;
107
126
  }
108
127
  // 4. Ask interactively
109
128
  if (interactive) {
110
- const answer = await ask(` ${v.name}: `);
111
- const expanded = expandArrayValue(answer);
129
+ const answer = await ask(` ${v.name}: `, v.name);
130
+ const expanded = expandArrayValue(answer, v.name);
112
131
  if (expanded.length > 1) {
113
132
  arrayVars[v.name] = expanded;
114
133
  hasArrays = true;
115
134
  }
116
135
  else {
117
- values[v.name] = expanded[0];
136
+ values[v.name] = expanded[0] ?? '';
118
137
  }
119
138
  }
120
139
  }
121
140
  // Build iterations
122
141
  if (!hasArrays) {
123
- return { values, iterations: [values] };
142
+ return { values, iterations: [values], defaulted };
124
143
  }
125
144
  const arrayNames = Object.keys(arrayVars);
126
145
  const iterations = [];
127
146
  const arrays = arrayNames.map(n => arrayVars[n]);
128
147
  const combos = cross ? cartesian(arrays) : zip(arrays);
129
148
  for (const combo of combos) {
130
- const iter = { ...values };
149
+ const iter = Object.assign(Object.create(null), values);
131
150
  for (let j = 0; j < arrayNames.length; j++) {
132
151
  iter[arrayNames[j]] = combo[j];
133
152
  }
134
153
  iterations.push(iter);
135
154
  }
136
- return { values, iterations };
155
+ return { values, iterations, defaulted };
137
156
  }
138
- function ask(prompt) {
157
+ function ask(prompt, name) {
139
158
  const rl = createInterface({ input: process.stdin, output: process.stderr });
140
- return new Promise(resolve => {
159
+ return new Promise((resolve, reject) => {
160
+ let settled = false;
161
+ // Fires on stdin EOF before an answer; without it the Promise never settles.
162
+ rl.on('close', () => {
163
+ if (settled)
164
+ return;
165
+ settled = true;
166
+ reject(new UsageError(`Variable "${name}" has no value and stdin is closed; pass --${name}=<value> or --no-interactive`));
167
+ });
141
168
  rl.question(prompt, (answer) => {
169
+ if (settled)
170
+ return;
171
+ settled = true;
142
172
  rl.close();
143
173
  resolve(answer.trim());
144
174
  });
@@ -1 +1 @@
1
- {"version":3,"file":"resolver.js","sourceRoot":"","sources":["../src/resolver.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"resolver.js","sourceRoot":"","sources":["../src/resolver.ts"],"names":[],"mappings":"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"}
package/dist/status.d.ts CHANGED
@@ -6,4 +6,4 @@ import type { TemplateVar } from './parser.js';
6
6
  export declare function renderStatus(templateName: string, vars: TemplateVar[], values: Record<string, string>, iteration?: {
7
7
  current: number;
8
8
  total: number;
9
- }): string;
9
+ }, defaulted?: ReadonlySet<string>): string;
package/dist/status.js CHANGED
@@ -3,19 +3,20 @@
3
3
  * Shows current template state: filled vars, unfilled vars, progress.
4
4
  */
5
5
  import { sanitizeForTerminal } from './sanitize.js';
6
- export function renderStatus(templateName, vars, values, iteration) {
6
+ export function renderStatus(templateName, vars, values, iteration, defaulted) {
7
7
  const parts = [];
8
8
  for (const v of vars) {
9
- const val = values[v.name];
9
+ const val = Object.hasOwn(values, v.name) ? values[v.name] : undefined;
10
10
  if (val !== undefined) {
11
- const source = v.defaultValue === val ? '(default)' : '';
11
+ const isDefault = defaulted ? defaulted.has(v.name) : v.defaultValue === val;
12
+ const source = isDefault ? '(default)' : '';
12
13
  parts.push(`${v.name}=${val}${source}`);
13
14
  }
14
15
  else {
15
16
  parts.push(`${v.name}=___`);
16
17
  }
17
18
  }
18
- const allFilled = vars.every(v => v.name in values);
19
+ const allFilled = vars.every(v => Object.hasOwn(values, v.name));
19
20
  const icon = allFilled ? '✅' : '📋';
20
21
  const progress = iteration ? ` [${iteration.current}/${iteration.total}]` : '';
21
22
  // Values may come from repo-controlled sources (templates, git diff);
@@ -1 +1 @@
1
- {"version":3,"file":"status.js","sourceRoot":"","sources":["../src/status.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAEpD,MAAM,UAAU,YAAY,CAC1B,YAAoB,EACpB,IAAmB,EACnB,MAA8B,EAC9B,SAA8C;IAE9C,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACrB,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC3B,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YACtB,MAAM,MAAM,GAAG,CAAC,CAAC,YAAY,KAAK,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;YACzD,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,GAAG,GAAG,MAAM,EAAE,CAAC,CAAC;QAC1C,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,MAAM,CAAC,CAAC;IACpD,MAAM,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;IACpC,MAAM,QAAQ,GAAG,SAAS,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,OAAO,IAAI,SAAS,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAE/E,sEAAsE;IACtE,+DAA+D;IAC/D,OAAO,mBAAmB,CAAC,GAAG,IAAI,IAAI,YAAY,GAAG,QAAQ,KAAK,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACxF,CAAC"}
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../src/status.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAEpD,MAAM,UAAU,YAAY,CAC1B,YAAoB,EACpB,IAAmB,EACnB,MAA8B,EAC9B,SAA8C,EAC9C,SAA+B;IAE/B,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACrB,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACvE,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YACtB,MAAM,SAAS,GAAG,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,KAAK,GAAG,CAAC;YAC7E,MAAM,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;YAC5C,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,GAAG,GAAG,MAAM,EAAE,CAAC,CAAC;QAC1C,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IACjE,MAAM,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;IACpC,MAAM,QAAQ,GAAG,SAAS,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,OAAO,IAAI,SAAS,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAE/E,sEAAsE;IACtE,+DAA+D;IAC/D,OAAO,mBAAmB,CAAC,GAAG,IAAI,IAAI,YAAY,GAAG,QAAQ,KAAK,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACxF,CAAC"}
package/dist/ui.html CHANGED
@@ -749,7 +749,12 @@ import { cartesian as sharedCartesian, zip as sharedZip } from './iterate.js';
749
749
 
750
750
  function syncVarsFromTemplate() {
751
751
  const text = tmpl.value;
752
- const parsed = parseVars(text);
752
+ let parsed;
753
+ try {
754
+ parsed = parseVars(text);
755
+ } catch {
756
+ return; // update() reports the parse error in the preview
757
+ }
753
758
  const existing = getRowData();
754
759
  const existingNames = new Set(existing.map(r => r.name));
755
760
 
@@ -849,10 +854,18 @@ import { cartesian as sharedCartesian, zip as sharedZip } from './iterate.js';
849
854
  }
850
855
 
851
856
  const results = [];
852
- for (const combo of combos) {
853
- const vals = { ...scalarValues };
854
- arrayVars.forEach((v, i) => { vals[v.name] = combo[i]; });
855
- results.push({ values: { ...vals }, expanded: sharedExpand(text, vals) });
857
+ try {
858
+ for (const combo of combos) {
859
+ const vals = { ...scalarValues };
860
+ arrayVars.forEach((v, i) => { vals[v.name] = combo[i]; });
861
+ results.push({ values: { ...vals }, expanded: sharedExpand(text, vals) });
862
+ }
863
+ } catch (err) {
864
+ previewBody.innerHTML = `<span class="preview-empty">${escHtml(err instanceof Error ? err.message : String(err))}</span>`;
865
+ previewCount.textContent = 'error';
866
+ previewCount.className = 'preview-count empty';
867
+ cliText.innerHTML = '<span class="dollar">$</span>promptargs';
868
+ return;
856
869
  }
857
870
 
858
871
  const count = results.length;
package/dist/ui.js CHANGED
@@ -3,6 +3,7 @@ import { readFileSync } from 'node:fs';
3
3
  import { join, dirname } from 'node:path';
4
4
  import { fileURLToPath } from 'node:url';
5
5
  import { autodetect, isSensitiveEnvName, isSensitiveEnvValue, AUTODETECT_VARS } from './autodetect.js';
6
+ import { diag, classifyError, jsonLogEnabled, logRequest } from './log.js';
6
7
  const DEFAULT_PORT = 3700;
7
8
  const OPEN_DELAY_MS = 500;
8
9
  const DIFF_PREVIEW_MAX_CHARS = 60;
@@ -50,7 +51,9 @@ export function buildUIHtml(rawHtml, envVars) {
50
51
  // Escape "<" so env values cannot break out of the script tag (e.g. "</script>").
51
52
  const envJson = JSON.stringify(envVars).replace(/</g, '\\u003c');
52
53
  const envScript = `<script>window.__PROMPTARGS_ENV__ = ${envJson};</script>`;
53
- return rawHtml.replace('<script>', envScript + '\n<script>');
54
+ // Function replacer: a replacement *string* would expand `$&`, "$`" and "$'"
55
+ // found in env values into surrounding raw HTML, undoing the escaping above.
56
+ return rawHtml.replace(/<script\b/, (match) => `${envScript}\n${match}`);
54
57
  }
55
58
  // Browser-facing modules the builder UI imports directly instead of
56
59
  // hand-reimplementing their logic (see buildUIHtml / ui.html). Serving the
@@ -68,18 +71,24 @@ export function startUI(port = DEFAULT_PORT, options = {}) {
68
71
  rawHtml = readFileSync(htmlPath, 'utf-8');
69
72
  }
70
73
  catch {
71
- console.error('UI file not found. Reinstall @hivecommons/promptargs.');
74
+ diag('ui_start_error', { error_class: 'not_found' }, 'UI file not found. Reinstall @hivecommons/promptargs.');
72
75
  process.exit(1);
73
76
  }
74
77
  const html = buildUIHtml(rawHtml, collectEnvVars());
75
- const ALLOWED_HOSTS = new Set([
76
- `localhost:${port}`,
77
- `127.0.0.1:${port}`,
78
- `[::1]:${port}`,
79
- ]);
78
+ // Filled once the server is bound, because port 0 means the OS picks one.
79
+ const ALLOWED_HOSTS = new Set();
80
+ let rejectedHosts = 0;
80
81
  const server = createServer((req, res) => {
82
+ if (jsonLogEnabled()) {
83
+ const startedAt = process.hrtime.bigint();
84
+ res.on('finish', () => logRequest(res.statusCode, startedAt));
85
+ }
81
86
  // Reject non-local Host headers to block DNS-rebinding reads of env data.
82
87
  if (!req.headers.host || !ALLOWED_HOSTS.has(req.headers.host)) {
88
+ // Never log the Host header or URL: both are attacker-controlled.
89
+ if (rejectedHosts++ === 0) {
90
+ diag('ui_host_rejected', { status: 403 }, 'promptargs: rejected request with unexpected Host header (further rejections suppressed)');
91
+ }
83
92
  res.writeHead(403, { 'Content-Type': 'text/plain; charset=utf-8' });
84
93
  res.end('Forbidden');
85
94
  return;
@@ -91,7 +100,8 @@ export function startUI(port = DEFAULT_PORT, options = {}) {
91
100
  res.writeHead(200, { 'Content-Type': 'text/javascript; charset=utf-8' });
92
101
  res.end(js);
93
102
  }
94
- catch {
103
+ catch (err) {
104
+ diag('ui_asset_error', { status: 404, error_class: classifyError(err) }, `promptargs: failed to load static asset ${req.url} (${err.code ?? 'unknown'})`);
95
105
  res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' });
96
106
  res.end('Not found');
97
107
  }
@@ -103,7 +113,11 @@ export function startUI(port = DEFAULT_PORT, options = {}) {
103
113
  // Bind loopback only: the page embeds environment variables and must never
104
114
  // be reachable from other machines on the network.
105
115
  server.listen(port, '127.0.0.1', () => {
106
- const url = `http://localhost:${port}`;
116
+ const boundPort = server.address().port;
117
+ ALLOWED_HOSTS.add(`localhost:${boundPort}`);
118
+ ALLOWED_HOSTS.add(`127.0.0.1:${boundPort}`);
119
+ ALLOWED_HOSTS.add(`[::1]:${boundPort}`);
120
+ const url = `http://localhost:${boundPort}`;
107
121
  console.log(`promptargs builder running at ${url}`);
108
122
  console.log('Press Ctrl+C to stop.\n');
109
123
  if (options.openBrowser ?? true) {
@@ -111,14 +125,25 @@ export function startUI(port = DEFAULT_PORT, options = {}) {
111
125
  import('node:child_process').then(({ exec }) => {
112
126
  const cmd = process.platform === 'darwin' ? 'open' :
113
127
  process.platform === 'win32' ? 'start' : 'xdg-open';
114
- exec(`${cmd} ${url}`);
128
+ exec(`${cmd} ${url}`, err => {
129
+ if (!err)
130
+ return;
131
+ // Through a shell, a missing opener surfaces as exit status 127 rather than ENOENT.
132
+ const why = err.code === 127 ? 'not found' : String(err.code ?? err.message);
133
+ diag('ui_browser_open_failed', { error_class: classifyError(err) }, `promptargs: could not open a browser (${cmd}: ${why}); open ${url} manually`);
134
+ });
115
135
  });
116
136
  }, OPEN_DELAY_MS);
117
137
  }
118
138
  });
139
+ server.on('close', () => {
140
+ if (rejectedHosts > 1) {
141
+ diag('ui_host_rejected_total', { count: rejectedHosts }, `promptargs: rejected ${rejectedHosts} requests with unexpected Host headers in total`);
142
+ }
143
+ });
119
144
  server.on('error', (err) => {
120
145
  if (err.code === 'EADDRINUSE') {
121
- console.error(`Port ${port} is in use. Try: promptargs ui --port=${port + 1}`);
146
+ diag('ui_start_error', { error_class: 'port_in_use' }, `Port ${port} is in use. Try: promptargs ui --port=${port + 1}`);
122
147
  process.exit(1);
123
148
  }
124
149
  throw err;
package/dist/ui.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ui.js","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ui.js","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hivecommons/promptargs",
3
- "version": "0.7.0",
4
- "description": "Template arguments for AI prompts — variable substitution for Claude Code, Copilot, and more",
3
+ "version": "0.8.0",
4
+ "description": "Template arguments for AI prompts — variable substitution for Claude Code, Copilot, Goose, Bob, and more",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "bin": {
@@ -21,7 +21,12 @@
21
21
  "claude",
22
22
  "copilot",
23
23
  "args",
24
- "cli"
24
+ "cli",
25
+ "claude-code",
26
+ "goose",
27
+ "bob",
28
+ "agent-skills",
29
+ "prompt-templates"
25
30
  ],
26
31
  "author": "hivecommons",
27
32
  "license": "Apache-2.0",
@@ -34,10 +39,14 @@
34
39
  },
35
40
  "homepage": "https://github.com/hivecommons/promptargs#readme",
36
41
  "engines": {
37
- "node": ">=18"
42
+ "node": ">=22"
38
43
  },
39
44
  "files": [
40
45
  "dist",
46
+ "!dist/**/*.test.js",
47
+ "!dist/**/*.test.js.map",
48
+ "!dist/**/*.test.d.ts",
49
+ "!dist/ui-harness.*",
41
50
  "README.md",
42
51
  "LICENSE"
43
52
  ],
@@ -1 +0,0 @@
1
- export {};
@@ -1,207 +0,0 @@
1
- import { describe, it, before, after } from 'node:test';
2
- import assert from 'node:assert';
3
- import { execSync } from 'node:child_process';
4
- import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
5
- import { join, delimiter } from 'node:path';
6
- import { tmpdir } from 'node:os';
7
- import { autodetect, autodetectAll, isSensitiveEnvName, isSensitiveEnvValue, AUTODETECT_VARS } from './autodetect.js';
8
- // git-backed detectors read the process cwd, so run them inside a scratch
9
- // repo with a known origin remote (each test file is its own process).
10
- let repo;
11
- let prevCwd;
12
- before(() => {
13
- prevCwd = process.cwd();
14
- repo = mkdtempSync(join(process.cwd(), '.pa-auto-'));
15
- process.chdir(repo);
16
- const run = (cmd) => execSync(cmd, { stdio: 'pipe' });
17
- run('git init -q -b pa-test-branch');
18
- run('git config user.email pa@test.local');
19
- run('git config user.name "PA Tester"');
20
- run('git remote add origin https://github.com/pa-test-org/pa-test-repo.git');
21
- run('git commit -q --allow-empty -m init');
22
- });
23
- after(() => {
24
- process.chdir(prevCwd);
25
- rmSync(repo, { recursive: true, force: true });
26
- });
27
- describe('git detectors', () => {
28
- it('detects the current branch', () => {
29
- assert.strictEqual(autodetect('branch'), 'pa-test-branch');
30
- });
31
- it('detects repo name from the origin remote, stripping .git', () => {
32
- assert.strictEqual(autodetect('repo'), 'pa-test-repo');
33
- });
34
- it('detects the org from the origin remote', () => {
35
- assert.strictEqual(autodetect('org'), 'pa-test-org');
36
- });
37
- it('detects repo/org from ssh-style remotes', () => {
38
- execSync('git remote set-url origin git@github.com:ssh-org/ssh-repo.git', { stdio: 'pipe' });
39
- try {
40
- assert.strictEqual(autodetect('repo'), 'ssh-repo');
41
- assert.strictEqual(autodetect('org'), 'ssh-org');
42
- }
43
- finally {
44
- execSync('git remote set-url origin https://github.com/pa-test-org/pa-test-repo.git', { stdio: 'pipe' });
45
- }
46
- });
47
- it('detects the git user name', () => {
48
- assert.strictEqual(autodetect('user'), 'PA Tester');
49
- });
50
- it('returns undefined for branch on a detached HEAD', () => {
51
- // `git branch --show-current` exits 0 and prints nothing when detached;
52
- // that must not become an empty value that beats the template default.
53
- execSync('git checkout -q --detach', { stdio: 'pipe' });
54
- try {
55
- assert.strictEqual(autodetect('branch'), undefined);
56
- assert.ok(!('branch' in autodetectAll(['branch'])));
57
- }
58
- finally {
59
- execSync('git checkout -q pa-test-branch', { stdio: 'pipe' });
60
- }
61
- });
62
- it('returns undefined for diff on a clean tree and the diff once there is one', () => {
63
- assert.strictEqual(autodetect('diff'), undefined);
64
- writeFileSync(join(repo, 'tracked.txt'), 'one\n');
65
- execSync('git add tracked.txt && git commit -q -m add', { stdio: 'pipe' });
66
- writeFileSync(join(repo, 'tracked.txt'), 'two\n');
67
- try {
68
- assert.match(autodetect('diff') ?? '', /^diff --git a\/tracked\.txt/);
69
- }
70
- finally {
71
- execSync('git checkout -q -- tracked.txt', { stdio: 'pipe' });
72
- }
73
- });
74
- });
75
- describe('date detector', () => {
76
- it('returns an ISO date (YYYY-MM-DD)', () => {
77
- assert.match(autodetect('date'), /^\d{4}-\d{2}-\d{2}$/);
78
- });
79
- });
80
- describe('environment fallback', () => {
81
- it('falls back to environment variables for unknown names', () => {
82
- process.env.pa_custom_var = 'env-value';
83
- try {
84
- assert.strictEqual(autodetect('pa_custom_var'), 'env-value');
85
- }
86
- finally {
87
- delete process.env.pa_custom_var;
88
- }
89
- });
90
- it('returns undefined for unknown names not in the environment', () => {
91
- assert.strictEqual(autodetect('pa_definitely_not_set'), undefined);
92
- });
93
- it('never expands credential-looking env vars', () => {
94
- process.env.PA_TEST_TOKEN = 'sekrit';
95
- process.env.PA_TEST_API_KEY = 'sekrit';
96
- try {
97
- assert.strictEqual(autodetect('PA_TEST_TOKEN'), undefined);
98
- assert.strictEqual(autodetect('PA_TEST_API_KEY'), undefined);
99
- }
100
- finally {
101
- delete process.env.PA_TEST_TOKEN;
102
- delete process.env.PA_TEST_API_KEY;
103
- }
104
- });
105
- it('never expands credential-shaped values under innocuous names', () => {
106
- process.env.PA_TEST_DATABASE_URL = 'postgres://app:hunter2@db.internal/app';
107
- process.env.PA_TEST_PLAIN_URL = 'postgres://db.internal/app';
108
- try {
109
- assert.strictEqual(autodetect('PA_TEST_DATABASE_URL'), undefined);
110
- assert.strictEqual(autodetect('PA_TEST_PLAIN_URL'), 'postgres://db.internal/app');
111
- }
112
- finally {
113
- delete process.env.PA_TEST_DATABASE_URL;
114
- delete process.env.PA_TEST_PLAIN_URL;
115
- }
116
- });
117
- });
118
- describe('isSensitiveEnvName', () => {
119
- it('flags credential-looking names', () => {
120
- for (const name of [
121
- 'GITHUB_TOKEN', 'GH_TOKEN', 'NPM_TOKEN', 'MY_SECRET', 'DB_PASSWORD',
122
- 'PGPASSWD', 'AWS_SECRET_ACCESS_KEY', 'OPENAI_API_KEY', 'APIKEY',
123
- 'GOOGLE_APPLICATION_CREDENTIALS', 'SSH_PRIVATE_KEY', 'AUTH_HEADER',
124
- 'X_AUTH', 'SESSION_COOKIE', 'BEARER_VALUE', 'gh_token',
125
- // Suffix conventions outside API_/ACCESS_/PRIVATE_KEY
126
- 'OPENAI_KEY', 'STRIPE_KEY', 'SIGNING_KEY', 'ENCRYPTION_KEY', 'HIVE_HEARTBEAT_KEY',
127
- 'LICENSE_KEY', 'SSH_KEYS', 'KEY', 'MYSQL_PWD', 'ORACLE_PWD', 'GITHUB_PAT', 'AZURE_DEVOPS_PAT',
128
- 'PAT', 'GIT_PASS', 'SSH_PASSPHRASE', 'SLACK_WEBHOOK_URL', 'DISCORD_WEBHOOK', 'SENTRY_DSN',
129
- 'DB_CONNECTION_STRING', 'AZURE_CONN_STR', 'JWT_SIGNING', 'HMAC_VALUE', 'OAUTH_CLIENT_ID',
130
- ]) {
131
- assert.strictEqual(isSensitiveEnvName(name), true, `should flag ${name}`);
132
- }
133
- });
134
- it('does not flag ordinary names', () => {
135
- for (const name of [
136
- 'EDITOR', 'PATH', 'GOPATH', 'AUTHOR', 'AUTHORIZED_USERS_FILE', 'branch', 'KEYBOARD',
137
- 'PWD', 'OLDPWD', 'PATTERN', 'PATCH_LEVEL', 'PASSENGER_COUNT', 'MONKEY', 'DSNAME', 'COMPASS',
138
- ]) {
139
- assert.strictEqual(isSensitiveEnvName(name), false, `should not flag ${name}`);
140
- }
141
- });
142
- });
143
- describe('isSensitiveEnvValue', () => {
144
- it('flags credential-shaped values regardless of name', () => {
145
- for (const value of [
146
- 'postgres://app:hunter2@db.internal:5432/app',
147
- 'redis://:p4ss@cache.internal/0',
148
- 'mongodb+srv://user:pw@cluster0.example.net/db',
149
- '-----BEGIN OPENSSH PRIVATE KEY-----\nabc',
150
- 'eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIn0.sig',
151
- 'ghp_' + 'A'.repeat(36),
152
- 'github_pat_' + 'A'.repeat(22) + '_' + 'b'.repeat(59),
153
- 'glpat-' + 'x'.repeat(20),
154
- 'sk-' + 'x'.repeat(48),
155
- 'sk_live_' + 'x'.repeat(24),
156
- 'xoxb-' + '1'.repeat(12) + '-abc',
157
- 'AKIA' + 'A'.repeat(16),
158
- 'AIza' + 'a'.repeat(35),
159
- 'npm_' + 'a'.repeat(36),
160
- 'hvs.' + 'a'.repeat(24),
161
- ]) {
162
- assert.strictEqual(isSensitiveEnvValue(value), true, `should flag ${value.slice(0, 12)}...`);
163
- }
164
- });
165
- it('does not flag ordinary values', () => {
166
- for (const value of [
167
- 'main', '/usr/local/bin:/usr/bin', 'https://github.com/hivecommons/promptargs',
168
- 'postgres://db.internal/app', 'https://user@example.com/path', 'user@example.com',
169
- 'skills', 'sk-short', 'eyJ-not-a-jwt', 'AKIA-nope', 'Hello, world', '42',
170
- ]) {
171
- assert.strictEqual(isSensitiveEnvValue(value), false, `should not flag ${value}`);
172
- }
173
- });
174
- });
175
- describe('autodetectAll', () => {
176
- it('collects only detectable values', () => {
177
- const result = autodetectAll(['branch', 'pa_definitely_not_set', 'date']);
178
- assert.strictEqual(result.branch, 'pa-test-branch');
179
- assert.ok('date' in result);
180
- assert.ok(!('pa_definitely_not_set' in result));
181
- });
182
- it('AUTODETECT_VARS lists the built-in detectors', () => {
183
- for (const name of ['branch', 'repo', 'org', 'diff', 'pr', 'user', 'date']) {
184
- assert.ok(AUTODETECT_VARS.includes(name), `missing ${name}`);
185
- }
186
- });
187
- });
188
- describe('pr detector', () => {
189
- it('returns undefined when gh exits 0 with empty output', t => {
190
- // A branch with no PR: gh succeeds but prints nothing, so the detector
191
- // must map '' to undefined rather than returning an empty value.
192
- if (process.platform === 'win32')
193
- return t.skip('POSIX shim script');
194
- const shim = mkdtempSync(join(tmpdir(), 'pa-gh-shim-'));
195
- writeFileSync(join(shim, 'gh'), '#!/bin/sh\nexit 0\n', { mode: 0o755 });
196
- const prevPath = process.env.PATH;
197
- process.env.PATH = `${shim}${delimiter}${prevPath ?? ''}`;
198
- try {
199
- assert.strictEqual(autodetect('pr'), undefined);
200
- }
201
- finally {
202
- process.env.PATH = prevPath;
203
- rmSync(shim, { recursive: true, force: true });
204
- }
205
- });
206
- });
207
- //# sourceMappingURL=autodetect.test.js.map