flowcollab 0.3.16 → 0.3.18
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/bin/_client.mjs +33 -18
- package/bin/create.mjs +6 -1
- package/bin/edit.mjs +1 -1
- package/bin/scan.mjs +6 -10
- package/package.json +1 -1
package/bin/_client.mjs
CHANGED
|
@@ -9,6 +9,7 @@ import 'dotenv/config';
|
|
|
9
9
|
import { homedir } from 'os';
|
|
10
10
|
import { readFileSync, writeFileSync, mkdirSync, chmodSync, renameSync } from 'fs';
|
|
11
11
|
import { join } from 'path';
|
|
12
|
+
import { get as httpsGet } from 'https';
|
|
12
13
|
|
|
13
14
|
// On Windows, Node's bundled CA store doesn't include certs added by corporate/AV
|
|
14
15
|
// TLS inspection software, causing fetch() to throw "fetch failed". Re-spawn with
|
|
@@ -44,21 +45,31 @@ process.on('exit', () => {
|
|
|
44
45
|
});
|
|
45
46
|
|
|
46
47
|
if (!_uc.checkedAt || (Date.now() - _uc.checkedAt) > 86_400_000) {
|
|
47
|
-
// B21 (audit):
|
|
48
|
-
//
|
|
49
|
-
//
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
.
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
48
|
+
// B21/CLI-6 (audit): a pending network request pins the event loop open AFTER the command is done.
|
|
49
|
+
// fetch()'s undici socket can't be unref'd per-request, so use https.get + UNREF the socket — the
|
|
50
|
+
// request never delays the CLI's exit. Stamp checkedAt SYNCHRONOUSLY first so the 24h gate holds
|
|
51
|
+
// even for instant commands that exit before the request finishes (else unref'ing would re-check
|
|
52
|
+
// every run); the request just updates `latest` in the background when it completes.
|
|
53
|
+
try {
|
|
54
|
+
mkdirSync(join(homedir(), '.flow'), { recursive: true });
|
|
55
|
+
writeFileSync(_updateCache, JSON.stringify({ checkedAt: Date.now(), latest: _uc.latest ?? null }));
|
|
56
|
+
} catch {}
|
|
57
|
+
try {
|
|
58
|
+
const _ureq = httpsGet('https://registry.npmjs.org/flowcollab/latest', { headers: { accept: 'application/json' } }, (res) => {
|
|
59
|
+
if (res.statusCode !== 200) { res.resume(); return; }
|
|
60
|
+
let buf = '';
|
|
61
|
+
res.on('data', d => { buf += d; });
|
|
62
|
+
res.on('end', () => {
|
|
63
|
+
try {
|
|
64
|
+
const { version: latest } = JSON.parse(buf);
|
|
65
|
+
if (latest) { writeFileSync(_updateCache, JSON.stringify({ checkedAt: Date.now(), latest })); _uc = { checkedAt: Date.now(), latest }; }
|
|
66
|
+
} catch {}
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
_ureq.on('socket', s => s.unref?.()); // don't let the update check hold the CLI's exit
|
|
70
|
+
_ureq.on('error', () => {});
|
|
71
|
+
_ureq.setTimeout(4000, () => _ureq.destroy());
|
|
72
|
+
} catch {}
|
|
62
73
|
}
|
|
63
74
|
|
|
64
75
|
function loadGlobalConfig() {
|
|
@@ -110,9 +121,11 @@ export async function flowFetch(path, { method = 'GET', body } = {}) {
|
|
|
110
121
|
'content-type': 'application/json',
|
|
111
122
|
};
|
|
112
123
|
if (actingViaClaude()) headers['x-flow-acting-via'] = 'claude';
|
|
113
|
-
|
|
124
|
+
// CLI-5 (audit): config-first then env (consistent with token at 94 + actor at 138) so a stale env
|
|
125
|
+
// var can't override a logged-in session's org/project.
|
|
126
|
+
const org = _gc.orgId || process.env.FLOW_ORG_ID || '';
|
|
114
127
|
if (org) headers['x-flow-org'] = org;
|
|
115
|
-
const proj = process.env.FLOW_PROJECT_ID ||
|
|
128
|
+
const proj = _gc.projectId || process.env.FLOW_PROJECT_ID || '';
|
|
116
129
|
if (proj) headers['x-flow-project'] = proj;
|
|
117
130
|
|
|
118
131
|
const res = await fetch(url, {
|
|
@@ -179,7 +192,9 @@ export function arg(name, fallback) {
|
|
|
179
192
|
}
|
|
180
193
|
|
|
181
194
|
export function positional(idx) {
|
|
182
|
-
|
|
195
|
+
// CLI-3 (audit): only filter REAL flag tokens (--word / --word=value), not a free-text positional that
|
|
196
|
+
// merely begins with '--' (e.g. a flow-comment body "-- reverted the change" was being dropped).
|
|
197
|
+
const pos = process.argv.slice(2).filter(a => !/^--[a-zA-Z][\w-]*(=|$)/.test(a));
|
|
183
198
|
return pos[idx];
|
|
184
199
|
}
|
|
185
200
|
|
package/bin/create.mjs
CHANGED
|
@@ -29,7 +29,12 @@ async function fetchGithubIssue(issueNum) {
|
|
|
29
29
|
if (!repo) die('FLOW_GITHUB_REPO is not set. Set it in .env to use --from-issue.');
|
|
30
30
|
const url = `https://api.github.com/repos/${repo}/issues/${issueNum}`;
|
|
31
31
|
const res = await fetch(url, { headers: githubHeaders() });
|
|
32
|
-
|
|
32
|
+
// CLI-4 (audit): a PRIVATE repo with no token returns 404 (unauthenticated) — make that legible
|
|
33
|
+
// instead of a misleading "not found" (githubHeaders() omits Authorization when no token is set).
|
|
34
|
+
if (res.status === 404) {
|
|
35
|
+
const hint = process.env.FLOW_GITHUB_TOKEN ? '' : ' — if it is a PRIVATE repo, set FLOW_GITHUB_TOKEN (none is set)';
|
|
36
|
+
die(`GitHub Issue #${issueNum} not found in ${repo}${hint}.`);
|
|
37
|
+
}
|
|
33
38
|
if (!res.ok) die(`GitHub API error ${res.status} fetching issue #${issueNum}.`);
|
|
34
39
|
return res.json();
|
|
35
40
|
}
|
package/bin/edit.mjs
CHANGED
|
@@ -20,7 +20,7 @@ const FIELD_MAP = {
|
|
|
20
20
|
title: v => ({ title: v }),
|
|
21
21
|
priority: v => ({ priority: normalizePriority(v) }),
|
|
22
22
|
area: v => ({ area: v }),
|
|
23
|
-
due: v =>
|
|
23
|
+
due: v => { if (!/^\d{4}-\d{2}-\d{2}$/.test(v)) die(`--due must be YYYY-MM-DD (got: ${v})`); return { due_at: v + 'T12:00:00Z' }; }, // CLI-7 (audit): validate instead of sending a bad string (e.g. "tomorrow") through raw
|
|
24
24
|
milestone: v => ({ milestone: v }),
|
|
25
25
|
'blocked-by': v => ({ blocked_by: v === 'null' ? [] : v.split(',').map(s => s.trim()).filter(Boolean) }), // B9: split the comma list; prefixes are resolved after the loop
|
|
26
26
|
};
|
package/bin/scan.mjs
CHANGED
|
@@ -228,8 +228,9 @@ async function main() {
|
|
|
228
228
|
if (all || doTodos) {
|
|
229
229
|
process.stdout.write(' [todos] scanning...\n');
|
|
230
230
|
const s = scanTodos(files, dir);
|
|
231
|
-
|
|
232
|
-
|
|
231
|
+
// CLI-2 (audit): print when explicitly requested OR (all + results); NO early return, so combining
|
|
232
|
+
// focused flags (e.g. `flow-scan --todos --issues`) runs every requested scan, not only the first.
|
|
233
|
+
if (doTodos || (all && s.length)) printSection('TODO / FIXME items', s);
|
|
233
234
|
else if (all) process.stdout.write('\n[todos] None found.\n');
|
|
234
235
|
}
|
|
235
236
|
|
|
@@ -241,10 +242,8 @@ async function main() {
|
|
|
241
242
|
// no FLOW_GITHUB_REPO reports "Skipped" instead of a misleading "Untracked Issues (0) None found".
|
|
242
243
|
if (s === null) process.stdout.write('\n[issues] Skipped — FLOW_GITHUB_REPO not set.\n');
|
|
243
244
|
else if (issueErr) process.stdout.write('\n[issues] Scan failed — check FLOW_GITHUB_REPO and token.\n');
|
|
244
|
-
else if (doIssues)
|
|
245
|
-
else if (all && s.length) printSection('Untracked GitHub Issues', s);
|
|
245
|
+
else if (doIssues || (all && s.length)) printSection('Untracked GitHub Issues', s); // CLI-2 (audit): no early return — flags combine
|
|
246
246
|
else if (all) process.stdout.write('\n[issues] All open Issues are tracked in Flow.\n');
|
|
247
|
-
if (doIssues) return; // explicit --issues exits after its own report (Skipped/error already printed)
|
|
248
247
|
}
|
|
249
248
|
|
|
250
249
|
if (all || doPRs) {
|
|
@@ -254,17 +253,14 @@ async function main() {
|
|
|
254
253
|
// B14 (audit): null/error before the explicit-flag return (see the issues block above).
|
|
255
254
|
if (s === null) process.stdout.write('\n[prs] Skipped — FLOW_GITHUB_REPO not set.\n');
|
|
256
255
|
else if (prErr) process.stdout.write('\n[prs] Scan failed — check FLOW_GITHUB_REPO and token.\n');
|
|
257
|
-
else if (doPRs)
|
|
258
|
-
else if (all && s.length) printSection('Unlinked open PRs', s);
|
|
256
|
+
else if (doPRs || (all && s.length)) printSection('Unlinked open PRs', s); // CLI-2 (audit): no early return — flags combine
|
|
259
257
|
else if (all) process.stdout.write('\n[prs] All open PRs are linked to Flow tasks.\n');
|
|
260
|
-
if (doPRs) return; // explicit --prs exits after its own report
|
|
261
258
|
}
|
|
262
259
|
|
|
263
260
|
if (all || doSecurity) {
|
|
264
261
|
process.stdout.write(' [security] scanning patterns...\n');
|
|
265
262
|
const s = scanSecurity(dir);
|
|
266
|
-
if (doSecurity)
|
|
267
|
-
if (all && s.length) printSection('Security concerns', s);
|
|
263
|
+
if (doSecurity || (all && s.length)) printSection('Security concerns', s); // CLI-2 (audit): no early return — flags combine
|
|
268
264
|
else if (all) process.stdout.write('\n[security] No common patterns found.\n');
|
|
269
265
|
}
|
|
270
266
|
|