@dench.com/cli 2.2.3 → 2.2.4
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/ads.ts +63 -47
- package/package.json +1 -1
package/ads.ts
CHANGED
|
@@ -936,9 +936,16 @@ function spinnerVerbFromCache(): string | null {
|
|
|
936
936
|
function refreshClaudeSpinnerVerb(): void {
|
|
937
937
|
if (!claudeInstalled()) return;
|
|
938
938
|
const verb = spinnerVerbFromCache();
|
|
939
|
-
if (!verb) return;
|
|
940
939
|
const settings = readJson<Record<string, unknown>>(CLAUDE_SETTINGS, {});
|
|
941
|
-
|
|
940
|
+
if (verb) {
|
|
941
|
+
settings.spinnerVerbs = { mode: "replace", verbs: [verb] };
|
|
942
|
+
} else if (settings.spinnerVerbs) {
|
|
943
|
+
// No live ad → drop the sponsored thinking verb so Claude Code stops
|
|
944
|
+
// showing a stale ad on every spin.
|
|
945
|
+
delete settings.spinnerVerbs;
|
|
946
|
+
} else {
|
|
947
|
+
return;
|
|
948
|
+
}
|
|
942
949
|
atomicWrite(CLAUDE_SETTINGS, `${JSON.stringify(settings, null, 2)}\n`);
|
|
943
950
|
}
|
|
944
951
|
|
|
@@ -1138,47 +1145,26 @@ function shellSnippet(
|
|
|
1138
1145
|
): string {
|
|
1139
1146
|
if (kind === "fish") {
|
|
1140
1147
|
return `${SHELL_MARK_START}
|
|
1141
|
-
# Prints one sponsored line
|
|
1142
|
-
# (Codex, Cursor,
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
if not functions -q _dench_ads_original_prompt
|
|
1148
|
-
if functions -q fish_prompt
|
|
1149
|
-
functions -c fish_prompt _dench_ads_original_prompt
|
|
1150
|
-
else if functions -q fish_default_prompt
|
|
1151
|
-
functions -c fish_default_prompt _dench_ads_original_prompt
|
|
1152
|
-
end
|
|
1153
|
-
end
|
|
1154
|
-
if functions -q _dench_ads_original_prompt
|
|
1155
|
-
function fish_prompt
|
|
1156
|
-
_dench_ads
|
|
1157
|
-
_dench_ads_original_prompt
|
|
1158
|
-
end
|
|
1159
|
-
else
|
|
1160
|
-
function fish_prompt
|
|
1161
|
-
_dench_ads
|
|
1162
|
-
end
|
|
1148
|
+
# Prints one sponsored line ONCE when a new interactive shell opens (not on
|
|
1149
|
+
# every prompt) — shows in any terminal (Codex, Cursor, plain shell, …).
|
|
1150
|
+
# Remove this block to disable.
|
|
1151
|
+
if status is-interactive
|
|
1152
|
+
set -l _dench_line (${nodeBin()} ${RENDER_SCRIPT} 2>/dev/null | string collect)
|
|
1153
|
+
test -n "$_dench_line"; and printf '%s\\n' "$_dench_line"
|
|
1163
1154
|
end
|
|
1164
1155
|
${SHELL_MARK_END}`;
|
|
1165
1156
|
}
|
|
1166
1157
|
return `${SHELL_MARK_START}
|
|
1167
|
-
# Prints one sponsored line
|
|
1168
|
-
# (Codex, Cursor,
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
case "$PROMPT_COMMAND" in
|
|
1178
|
-
*_dench_ads*) ;;
|
|
1179
|
-
*) PROMPT_COMMAND="_dench_ads;$PROMPT_COMMAND" ;;
|
|
1180
|
-
esac
|
|
1181
|
-
fi
|
|
1158
|
+
# Prints one sponsored line ONCE when a new interactive shell opens (not on
|
|
1159
|
+
# every prompt) — shows in any terminal (Codex, Cursor, plain shell, …).
|
|
1160
|
+
# Remove this block to disable.
|
|
1161
|
+
case "$-" in
|
|
1162
|
+
*i*)
|
|
1163
|
+
_dench_line="$(${nodeBin()} ${RENDER_SCRIPT} 2>/dev/null)"
|
|
1164
|
+
[ -n "$_dench_line" ] && printf '%s\\n' "$_dench_line"
|
|
1165
|
+
unset _dench_line
|
|
1166
|
+
;;
|
|
1167
|
+
esac
|
|
1182
1168
|
${SHELL_MARK_END}`;
|
|
1183
1169
|
}
|
|
1184
1170
|
|
|
@@ -1595,7 +1581,7 @@ async function refresh(authToken?: string): Promise<string> {
|
|
|
1595
1581
|
saveAdSource(); // let the detached background worker know where to refetch
|
|
1596
1582
|
let ad: Ad | null = null;
|
|
1597
1583
|
let flushed = 0;
|
|
1598
|
-
let src: "live" | "demo" = "
|
|
1584
|
+
let src: "live" | "demo" | "none" = "none";
|
|
1599
1585
|
let keepExistingCache = false;
|
|
1600
1586
|
if (endpoint) {
|
|
1601
1587
|
const previousAd = loadAd({ allowExpired: true });
|
|
@@ -1604,14 +1590,33 @@ async function refresh(authToken?: string): Promise<string> {
|
|
|
1604
1590
|
const fetched = await fetchAd(endpoint);
|
|
1605
1591
|
ad = fetched.ad;
|
|
1606
1592
|
flushed = await flushImpressions(endpoint, authToken, ad ?? previousAd);
|
|
1607
|
-
if (ad)
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1593
|
+
if (ad) {
|
|
1594
|
+
src = "live";
|
|
1595
|
+
} else if (previousAd && !isDemoAdId(previousAd.id)) {
|
|
1596
|
+
// The endpoint had no campaign this poll — keep a recently-live creative
|
|
1597
|
+
// briefly so the line doesn't flicker between fetches, but ONLY if it
|
|
1598
|
+
// hasn't expired. Past expiry with no live campaign → clear it (below).
|
|
1599
|
+
if (previousAd.expiresAt && Date.now() <= previousAd.expiresAt) {
|
|
1600
|
+
ad = previousAd;
|
|
1601
|
+
src = "live";
|
|
1602
|
+
keepExistingCache = true;
|
|
1603
|
+
}
|
|
1612
1604
|
}
|
|
1605
|
+
// CRITICAL: when pointed at a real endpoint that returns NO campaign, show
|
|
1606
|
+
// NOTHING. Never fall back to bundled demo ads in production — that would
|
|
1607
|
+
// display a fake "Linear"/"Vercel" line nobody paid for. Demo ads are only
|
|
1608
|
+
// for the no-endpoint (offline/local) path below.
|
|
1609
|
+
if (!ad) {
|
|
1610
|
+
clearAdCache();
|
|
1611
|
+
refreshClaudeSpinnerVerb();
|
|
1612
|
+
return "No live campaign — nothing to show.";
|
|
1613
|
+
}
|
|
1614
|
+
} else {
|
|
1615
|
+
// No endpoint configured at all (offline/dev) → bundled demo rotation so
|
|
1616
|
+
// the feature is demonstrable with zero backend.
|
|
1617
|
+
ad = pickDemoAd();
|
|
1618
|
+
src = "demo";
|
|
1613
1619
|
}
|
|
1614
|
-
if (!ad) ad = pickDemoAd();
|
|
1615
1620
|
if (!keepExistingCache) {
|
|
1616
1621
|
ad.expiresAt = Date.now() + DEFAULT_TTL_MS;
|
|
1617
1622
|
atomicWrite(AD_CACHE, JSON.stringify(ad));
|
|
@@ -1622,6 +1627,17 @@ async function refresh(authToken?: string): Promise<string> {
|
|
|
1622
1627
|
return `Cached ${src} ad: ${ad.label}${flushMsg}`;
|
|
1623
1628
|
}
|
|
1624
1629
|
|
|
1630
|
+
// Remove the cached ad so the hooks/render print nothing when no campaign is
|
|
1631
|
+
// live. Best-effort; the render script already treats a missing cache as
|
|
1632
|
+
// "print nothing".
|
|
1633
|
+
function clearAdCache(): void {
|
|
1634
|
+
try {
|
|
1635
|
+
if (existsSync(AD_CACHE)) unlinkSync(AD_CACHE);
|
|
1636
|
+
} catch {
|
|
1637
|
+
// best-effort
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1625
1641
|
// ---------------------------------------------------------------------------
|
|
1626
1642
|
// status
|
|
1627
1643
|
// ---------------------------------------------------------------------------
|
|
@@ -1998,7 +2014,7 @@ Usage:
|
|
|
1998
2014
|
claude → Claude Code's official statusLine hook (~/.claude/settings.json)
|
|
1999
2015
|
codex → Codex CLI lifecycle hooks (~/.codex/hooks.json); shows the
|
|
2000
2016
|
line as a systemMessage on SessionStart + after each turn
|
|
2001
|
-
shell →
|
|
2017
|
+
shell → one line at shell startup, any terminal (zsh/bash/fish); needs --apply
|
|
2002
2018
|
agentmd → append an instruction block to ./CLAUDE.md and ./AGENTS.md
|
|
2003
2019
|
so the agent runs \`dench ads show\` during its turn
|
|
2004
2020
|
all → all of the above
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dench.com/cli",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.4",
|
|
4
4
|
"description": "Dench agent workspace CLI. v2 unifies auth behind `dench signin`; the legacy `dench login` / `dench onboard` / `dench setup` / `dench what-can-i-do` / `dench register` commands now error with a redirect.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|