@trawlme/cli 3.5.1 → 3.6.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.
- package/dist/commands/scraps.js +11 -0
- package/dist/lib/config.d.ts +4 -0
- package/dist/lib/config.js +1 -0
- package/dist/lib/tips.d.ts +30 -0
- package/dist/lib/tips.js +69 -0
- package/package.json +1 -1
package/dist/commands/scraps.js
CHANGED
|
@@ -9,6 +9,7 @@ import { classifyError, reportError, UsageError, RefusalError } from '../lib/err
|
|
|
9
9
|
import { confirmDestructive, isInteractive } from '../lib/confirm.js';
|
|
10
10
|
import { formatDoctor, formatAutofix, fetchRunAndFix, pickRun, pickFix } from './doctor.js';
|
|
11
11
|
import { renderPinch, pinchEnabled } from '../lib/pinch.js';
|
|
12
|
+
import { maybeShowReferralTip } from '../lib/tips.js';
|
|
12
13
|
/**
|
|
13
14
|
* Print a usage/validation error consistently: human text to stderr, or a
|
|
14
15
|
* machine envelope on stdout under --json (never both — reportError is the
|
|
@@ -738,6 +739,16 @@ export function attachRunCommand(parent, attachOpts = {}) {
|
|
|
738
739
|
// of what the watched run actually did.
|
|
739
740
|
await pollRunProgress(id, beforeRun, { json: opts.json });
|
|
740
741
|
}
|
|
742
|
+
// #151 — throttled post-run referral tip. Reached only when the
|
|
743
|
+
// launch call above didn't throw (a hard failure rejects it, unwinding
|
|
744
|
+
// straight to index.ts's central catch before this line ever runs)
|
|
745
|
+
// — and, when --watch polled to a terminal state, only when
|
|
746
|
+
// pollRunProgress didn't flag a genuine run failure/timeout/poll-error
|
|
747
|
+
// via a non-zero process.exitCode (#107 review F1). See lib/tips.ts
|
|
748
|
+
// for the throttle/TTY/json/opt-out gating itself.
|
|
749
|
+
const runFailed = typeof process.exitCode === 'number' && process.exitCode !== 0;
|
|
750
|
+
if (!runFailed)
|
|
751
|
+
maybeShowReferralTip({ json: opts.json });
|
|
741
752
|
});
|
|
742
753
|
}
|
|
743
754
|
attachRunCommand(scraps, { hidden: true });
|
package/dist/lib/config.d.ts
CHANGED
|
@@ -9,6 +9,10 @@ interface TrawlConfig {
|
|
|
9
9
|
* only an explicit `false` disables it. No default entry needed since it's
|
|
10
10
|
* optional. (#129) */
|
|
11
11
|
updateNotifier?: boolean;
|
|
12
|
+
/** Opt-out switch for the throttled post-run referral tip (lib/tips.ts).
|
|
13
|
+
* Same optional/absent-means-enabled shape as `updateNotifier` above. */
|
|
14
|
+
tips?: boolean;
|
|
15
|
+
referralTipShownAt: number;
|
|
12
16
|
}
|
|
13
17
|
declare const config: Conf<TrawlConfig>;
|
|
14
18
|
/**
|
package/dist/lib/config.js
CHANGED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure gate: true when the tip should print. Every external signal (json
|
|
3
|
+
* flag, TTY-ness, opt-out, persisted timestamp, current time) is a
|
|
4
|
+
* parameter rather than read internally, so this is trivially testable
|
|
5
|
+
* without mocking process.env/stdout/Date/config.
|
|
6
|
+
*/
|
|
7
|
+
export declare function isReferralTipDue(opts: {
|
|
8
|
+
json?: boolean;
|
|
9
|
+
isTTY: boolean;
|
|
10
|
+
optedOut: boolean;
|
|
11
|
+
lastShownAt: number;
|
|
12
|
+
now: number;
|
|
13
|
+
}): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Best-effort: print one subtle line inviting the user to refer a builder,
|
|
16
|
+
* throttled to once per 7 days via a timestamp persisted in the CLI's
|
|
17
|
+
* existing Conf-backed config store (lib/config.ts — same store as
|
|
18
|
+
* apiUrl/token/telemetry/updateNotifier).
|
|
19
|
+
*
|
|
20
|
+
* This only gates on throttle/TTY/`--json`/opt-out — it does NOT know
|
|
21
|
+
* whether the underlying command actually succeeded. Callers must only
|
|
22
|
+
* invoke it on a genuine success (see commands/scraps.ts `run <id>`'s
|
|
23
|
+
* action, which also checks pollRunProgress's exit code under `--watch`
|
|
24
|
+
* before calling this). Any state read/write failure (corrupt config file,
|
|
25
|
+
* disk error, …) is swallowed silently — a broken tip must never break a
|
|
26
|
+
* run.
|
|
27
|
+
*/
|
|
28
|
+
export declare function maybeShowReferralTip(opts?: {
|
|
29
|
+
json?: boolean;
|
|
30
|
+
}): void;
|
package/dist/lib/tips.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Throttled post-run referral tip (#151) — after a successful `trawl run`,
|
|
3
|
+
* occasionally invite the user to refer a builder. Mirrors
|
|
4
|
+
* lib/updateNotifier.ts's shape: synchronous, instant, self-guarded so a
|
|
5
|
+
* broken tip can never turn a successful command into a failed one.
|
|
6
|
+
*/
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
import config from './config.js';
|
|
9
|
+
/** Max once per 7 days. */
|
|
10
|
+
const TIP_THROTTLE_MS = 7 * 24 * 60 * 60 * 1000;
|
|
11
|
+
const REFERRAL_TIP_TEXT = 'Invite a builder — they get 500, you get 1,000 compute → trawl.me → account → Referrals';
|
|
12
|
+
/**
|
|
13
|
+
* Opted out via env (presence-based, mirrors NO_COLOR / the update
|
|
14
|
+
* notifier's TRAWL_NO_UPDATE_NOTIFIER — see lib/updateNotifier.ts) or an
|
|
15
|
+
* explicit `false` config value. Absent/undefined config key means enabled.
|
|
16
|
+
*/
|
|
17
|
+
function isOptedOut() {
|
|
18
|
+
if (process.env['TRAWL_NO_TIPS'] !== undefined)
|
|
19
|
+
return true;
|
|
20
|
+
return config.get('tips') === false;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Pure gate: true when the tip should print. Every external signal (json
|
|
24
|
+
* flag, TTY-ness, opt-out, persisted timestamp, current time) is a
|
|
25
|
+
* parameter rather than read internally, so this is trivially testable
|
|
26
|
+
* without mocking process.env/stdout/Date/config.
|
|
27
|
+
*/
|
|
28
|
+
export function isReferralTipDue(opts) {
|
|
29
|
+
if (opts.json)
|
|
30
|
+
return false;
|
|
31
|
+
if (!opts.isTTY)
|
|
32
|
+
return false;
|
|
33
|
+
if (opts.optedOut)
|
|
34
|
+
return false;
|
|
35
|
+
return opts.now - opts.lastShownAt >= TIP_THROTTLE_MS;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Best-effort: print one subtle line inviting the user to refer a builder,
|
|
39
|
+
* throttled to once per 7 days via a timestamp persisted in the CLI's
|
|
40
|
+
* existing Conf-backed config store (lib/config.ts — same store as
|
|
41
|
+
* apiUrl/token/telemetry/updateNotifier).
|
|
42
|
+
*
|
|
43
|
+
* This only gates on throttle/TTY/`--json`/opt-out — it does NOT know
|
|
44
|
+
* whether the underlying command actually succeeded. Callers must only
|
|
45
|
+
* invoke it on a genuine success (see commands/scraps.ts `run <id>`'s
|
|
46
|
+
* action, which also checks pollRunProgress's exit code under `--watch`
|
|
47
|
+
* before calling this). Any state read/write failure (corrupt config file,
|
|
48
|
+
* disk error, …) is swallowed silently — a broken tip must never break a
|
|
49
|
+
* run.
|
|
50
|
+
*/
|
|
51
|
+
export function maybeShowReferralTip(opts = {}) {
|
|
52
|
+
try {
|
|
53
|
+
const lastShownAt = config.get('referralTipShownAt') || 0;
|
|
54
|
+
const due = isReferralTipDue({
|
|
55
|
+
json: opts.json,
|
|
56
|
+
isTTY: Boolean(process.stdout.isTTY),
|
|
57
|
+
optedOut: isOptedOut(),
|
|
58
|
+
lastShownAt,
|
|
59
|
+
now: Date.now(),
|
|
60
|
+
});
|
|
61
|
+
if (!due)
|
|
62
|
+
return;
|
|
63
|
+
console.log(chalk.dim(REFERRAL_TIP_TEXT));
|
|
64
|
+
config.set('referralTipShownAt', Date.now());
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// silent — #151: a broken tip must never break a run
|
|
68
|
+
}
|
|
69
|
+
}
|