claude-code-infinite 1.0.11 → 1.0.16
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/README.md +214 -47
- package/dist/cc-request.d.ts +94 -0
- package/dist/cc-request.d.ts.map +1 -0
- package/dist/cc-request.js +194 -0
- package/dist/cc-request.js.map +1 -0
- package/dist/claude-env.d.ts +30 -0
- package/dist/claude-env.d.ts.map +1 -0
- package/dist/claude-env.js +55 -0
- package/dist/claude-env.js.map +1 -0
- package/dist/cli-args.d.ts +32 -0
- package/dist/cli-args.d.ts.map +1 -0
- package/dist/cli-args.js +72 -0
- package/dist/cli-args.js.map +1 -0
- package/dist/cli-lifecycle.d.ts +17 -0
- package/dist/cli-lifecycle.d.ts.map +1 -0
- package/dist/cli-lifecycle.js +30 -0
- package/dist/cli-lifecycle.js.map +1 -0
- package/dist/cli-main.d.ts +15 -0
- package/dist/cli-main.d.ts.map +1 -0
- package/dist/cli-main.js +464 -0
- package/dist/cli-main.js.map +1 -0
- package/dist/cli.js +13 -178
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +3 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +29 -3
- package/dist/config.js.map +1 -1
- package/dist/count-tokens.d.ts +78 -0
- package/dist/count-tokens.d.ts.map +1 -0
- package/dist/count-tokens.js +277 -0
- package/dist/count-tokens.js.map +1 -0
- package/dist/final-index.d.ts +10 -0
- package/dist/final-index.d.ts.map +1 -0
- package/dist/final-index.js +37 -0
- package/dist/final-index.js.map +1 -0
- package/dist/hooks.d.ts +239 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +618 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/memtree-fetch.d.ts +24 -0
- package/dist/memtree-fetch.d.ts.map +1 -0
- package/dist/memtree-fetch.js +121 -0
- package/dist/memtree-fetch.js.map +1 -0
- package/dist/memtree-finder.d.ts +99 -0
- package/dist/memtree-finder.d.ts.map +1 -0
- package/dist/memtree-finder.js +235 -0
- package/dist/memtree-finder.js.map +1 -0
- package/dist/memtree-links.d.ts +16 -0
- package/dist/memtree-links.d.ts.map +1 -0
- package/dist/memtree-links.js +66 -0
- package/dist/memtree-links.js.map +1 -0
- package/dist/memtree-mcp-config.d.ts +28 -0
- package/dist/memtree-mcp-config.d.ts.map +1 -0
- package/dist/memtree-mcp-config.js +121 -0
- package/dist/memtree-mcp-config.js.map +1 -0
- package/dist/memtree-mcp.d.ts +265 -0
- package/dist/memtree-mcp.d.ts.map +1 -0
- package/dist/memtree-mcp.js +667 -0
- package/dist/memtree-mcp.js.map +1 -0
- package/dist/memtree-newest.d.ts +74 -0
- package/dist/memtree-newest.d.ts.map +1 -0
- package/dist/memtree-newest.js +217 -0
- package/dist/memtree-newest.js.map +1 -0
- package/dist/memtree-tools.d.ts +105 -0
- package/dist/memtree-tools.d.ts.map +1 -0
- package/dist/memtree-tools.js +408 -0
- package/dist/memtree-tools.js.map +1 -0
- package/dist/memtree.d.ts +430 -0
- package/dist/memtree.d.ts.map +1 -0
- package/dist/memtree.js +1286 -0
- package/dist/memtree.js.map +1 -0
- package/dist/notices.d.ts +177 -0
- package/dist/notices.d.ts.map +1 -0
- package/dist/notices.js +469 -0
- package/dist/notices.js.map +1 -0
- package/dist/payment-gate.d.ts +53 -0
- package/dist/payment-gate.d.ts.map +1 -0
- package/dist/payment-gate.js +97 -0
- package/dist/payment-gate.js.map +1 -0
- package/dist/project-meta.d.ts +15 -0
- package/dist/project-meta.d.ts.map +1 -0
- package/dist/project-meta.js +80 -0
- package/dist/project-meta.js.map +1 -0
- package/dist/prompt-accounting.d.ts +12 -0
- package/dist/prompt-accounting.d.ts.map +1 -0
- package/dist/prompt-accounting.js +70 -0
- package/dist/prompt-accounting.js.map +1 -0
- package/dist/proxy.d.ts +204 -0
- package/dist/proxy.d.ts.map +1 -0
- package/dist/proxy.js +4299 -0
- package/dist/proxy.js.map +1 -0
- package/dist/reqlog.d.ts +427 -0
- package/dist/reqlog.d.ts.map +1 -0
- package/dist/reqlog.js +146 -0
- package/dist/reqlog.js.map +1 -0
- package/dist/request-sizing.d.ts +74 -0
- package/dist/request-sizing.d.ts.map +1 -0
- package/dist/request-sizing.js +121 -0
- package/dist/request-sizing.js.map +1 -0
- package/dist/resume-repair-file.d.ts +17 -0
- package/dist/resume-repair-file.d.ts.map +1 -0
- package/dist/resume-repair-file.js +100 -0
- package/dist/resume-repair-file.js.map +1 -0
- package/dist/resume-repair.d.ts +27 -0
- package/dist/resume-repair.d.ts.map +1 -0
- package/dist/resume-repair.js +198 -0
- package/dist/resume-repair.js.map +1 -0
- package/dist/route-cache.d.ts +5 -0
- package/dist/route-cache.d.ts.map +1 -0
- package/dist/route-cache.js +63 -0
- package/dist/route-cache.js.map +1 -0
- package/dist/route-fallback.d.ts +67 -0
- package/dist/route-fallback.d.ts.map +1 -0
- package/dist/route-fallback.js +126 -0
- package/dist/route-fallback.js.map +1 -0
- package/dist/route-identity.d.ts +6 -0
- package/dist/route-identity.d.ts.map +1 -0
- package/dist/route-identity.js +100 -0
- package/dist/route-identity.js.map +1 -0
- package/dist/transcript-usage.d.ts +57 -0
- package/dist/transcript-usage.d.ts.map +1 -0
- package/dist/transcript-usage.js +570 -0
- package/dist/transcript-usage.js.map +1 -0
- package/dist/turns.d.ts +77 -0
- package/dist/turns.d.ts.map +1 -0
- package/dist/turns.js +330 -0
- package/dist/turns.js.map +1 -0
- package/dist/update-check.d.ts +38 -0
- package/dist/update-check.d.ts.map +1 -0
- package/dist/update-check.js +67 -0
- package/dist/update-check.js.map +1 -0
- package/package.json +6 -2
package/dist/notices.js
ADDED
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MemTree notice copy plus compatibility cleanup for legacy injected markers.
|
|
3
|
+
*
|
|
4
|
+
* Live notices are now rendered through Claude Code's display-only hooks (see
|
|
5
|
+
* hooks.ts), never as Anthropic assistant content. The marker helpers and old
|
|
6
|
+
* SSE/JSON rewriters remain exported so existing contaminated transcripts and
|
|
7
|
+
* integrations can be cleaned safely. The marker is therefore a stable legacy
|
|
8
|
+
* contract and must not change.
|
|
9
|
+
*/
|
|
10
|
+
import { randomUUID } from "node:crypto";
|
|
11
|
+
import { StringDecoder } from "node:string_decoder";
|
|
12
|
+
import { LINK_LABEL, linkLines } from "./hooks.js";
|
|
13
|
+
import { UPGRADE_COMMAND } from "./update-check.js";
|
|
14
|
+
export const NOTICE_OPEN = "<cc-infinite-notice>";
|
|
15
|
+
export const NOTICE_CLOSE = "</cc-infinite-notice>";
|
|
16
|
+
export const COMPRESSED_NOTICE = "✓ MemTree · conversation optimized";
|
|
17
|
+
/**
|
|
18
|
+
* The success line with the newest ready MemTree page for this conversation
|
|
19
|
+
* appended, when the client has one it has not shown yet — plain text form
|
|
20
|
+
* (monochrome rendering, docs, tests). The hook renderer styles the text and
|
|
21
|
+
* leaves the URL bare so Claude Code's linkifier gets a clean link.
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* The success line with the conversation's size before and after
|
|
25
|
+
* compression: `✓ MemTree · conversation optimized · ~861k → 426k tokens`.
|
|
26
|
+
* Without both counts, or when "after" is not smaller, the plain line.
|
|
27
|
+
*/
|
|
28
|
+
export function compressedTotalsText(originalTokens, compressedTokens) {
|
|
29
|
+
const ok = (n) => typeof n === "number" && Number.isFinite(n) && n > 0;
|
|
30
|
+
if (!ok(originalTokens) || !ok(compressedTokens) || compressedTokens >= originalTokens) {
|
|
31
|
+
return COMPRESSED_NOTICE;
|
|
32
|
+
}
|
|
33
|
+
return `${COMPRESSED_NOTICE} · ~${formatTokenCount(originalTokens)} → ${formatTokenCount(compressedTokens)} tokens`;
|
|
34
|
+
}
|
|
35
|
+
/** "861k", "1.3m": whole thousands, millions to one decimal. */
|
|
36
|
+
function formatTokenCount(tokens) {
|
|
37
|
+
return new Intl.NumberFormat("en-US", {
|
|
38
|
+
notation: "compact",
|
|
39
|
+
maximumFractionDigits: tokens >= 1_000_000 ? 1 : 0,
|
|
40
|
+
}).format(tokens).toLowerCase();
|
|
41
|
+
}
|
|
42
|
+
export function compressedNoticeText(memtreeUrl) {
|
|
43
|
+
return memtreeUrl ? `${COMPRESSED_NOTICE}\n ${memtreeUrl}` : COMPRESSED_NOTICE;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The trailer under a finished assistant message naming the newest MemTree
|
|
47
|
+
* page for the conversation, plain-text form. The hook renderer styles the
|
|
48
|
+
* label green the first time an index is shown and dim afterwards, with the
|
|
49
|
+
* URL bare either way.
|
|
50
|
+
*/
|
|
51
|
+
export function memtreeTrailerText(memtreeUrl, note) {
|
|
52
|
+
return linkLines(LINK_LABEL, memtreeUrl, note);
|
|
53
|
+
}
|
|
54
|
+
/** Trailer qualifier: the index is built, but the conversation still fits the budget and went out whole. */
|
|
55
|
+
export const NOT_COMPRESSED_NOTE = "/memtree-compact to compact session";
|
|
56
|
+
/** @deprecated Present only to recognize old notice copy in callers/tests. */
|
|
57
|
+
export const MODEL_HIDDEN_NOTICE = "<model does not see this message>";
|
|
58
|
+
export const DEGRADED_NOTICE = "⚠ MemTree degraded — this turn ran uncompressed";
|
|
59
|
+
export const PAYMENT_REQUIRED_NOTICE = "⚠ MemTree is off — payment required (compression + indexing disabled). Visit polychat.co to enable.";
|
|
60
|
+
export const SLOW_FIRST_TOKEN_NOTICE = "✨ Something special is happening — please wait…";
|
|
61
|
+
export const STARTUP_NOTICE = "∞ MemTree · Infinite Context Enabled ∞";
|
|
62
|
+
/**
|
|
63
|
+
* Session-start banner delivered inside Claude Code via the SessionStart
|
|
64
|
+
* hook's systemMessage. Styling mirrors the notice queue's conventions:
|
|
65
|
+
* named-color SGR only, resetting foreground/intensity rather than issuing a
|
|
66
|
+
* full reset, so surrounding renderer styles are preserved.
|
|
67
|
+
*
|
|
68
|
+
* Leading newlines are load-bearing: Claude Code renders a hook systemMessage
|
|
69
|
+
* as `<hookName> says: <content>` on one line, so the first breaks the banner
|
|
70
|
+
* off the label and the second leaves a blank line between them.
|
|
71
|
+
*/
|
|
72
|
+
export function startupNoticeText(color, update = null) {
|
|
73
|
+
const banner = color
|
|
74
|
+
? "\n\n\x1b[2m·─╼\x1b[22m \x1b[36m∞\x1b[39m \x1b[1mMemTree\x1b[22m · " +
|
|
75
|
+
"\x1b[32mInfinite Context Enabled\x1b[39m \x1b[36m∞\x1b[39m \x1b[2m╾─·\x1b[22m"
|
|
76
|
+
: `\n\n·─╼ ${STARTUP_NOTICE} ╾─·`;
|
|
77
|
+
return update ? `${banner}\n${updateNoticeText(update, color)}` : banner;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* One line under the banner when npm has a newer release. Versions come from
|
|
81
|
+
* the registry document and are re-validated here so a hostile or malformed
|
|
82
|
+
* response can never inject terminal controls into the hook payload.
|
|
83
|
+
*/
|
|
84
|
+
export function updateNoticeText(update, color) {
|
|
85
|
+
const latest = sanitizeNoticeDetail(update.latest, 40);
|
|
86
|
+
const current = sanitizeNoticeDetail(update.current, 40);
|
|
87
|
+
const plain = `↑ claude-code-infinite ${latest} available (you have ${current}) — ${UPGRADE_COMMAND}`;
|
|
88
|
+
if (!color)
|
|
89
|
+
return plain;
|
|
90
|
+
return (`\x1b[33m↑ claude-code-infinite ${latest} available\x1b[39m ` +
|
|
91
|
+
`\x1b[2m(you have ${current})\x1b[22m — \x1b[1m${UPGRADE_COMMAND}\x1b[22m`);
|
|
92
|
+
}
|
|
93
|
+
/** Make server-provided detail safe and compact before terminal rendering. */
|
|
94
|
+
export function sanitizeNoticeDetail(text, maxLength = 300) {
|
|
95
|
+
return text
|
|
96
|
+
.replace(/[\u0000-\u001f\u007f-\u009f]/g, " ")
|
|
97
|
+
.replace(/\s+/g, " ")
|
|
98
|
+
.trim()
|
|
99
|
+
.slice(0, maxLength);
|
|
100
|
+
}
|
|
101
|
+
export function wrapNotice(text) {
|
|
102
|
+
return `${NOTICE_OPEN}${text}${NOTICE_CLOSE}`;
|
|
103
|
+
}
|
|
104
|
+
const NOTICE_SPAN_RE = new RegExp(`${escapeRegExp(NOTICE_OPEN)}[\\s\\S]*?${escapeRegExp(NOTICE_CLOSE)}`, "g");
|
|
105
|
+
const NOTICE_CAPTURE_RE = new RegExp(`${escapeRegExp(NOTICE_OPEN)}([\\s\\S]*?)${escapeRegExp(NOTICE_CLOSE)}`, "g");
|
|
106
|
+
const LEGACY_NOTICE_PREFIXES = [
|
|
107
|
+
"MemTree working - conversation consolidated",
|
|
108
|
+
"⚠ MemTree degraded",
|
|
109
|
+
"⚠ MemTree is off",
|
|
110
|
+
"✨ Something special is happening",
|
|
111
|
+
];
|
|
112
|
+
function escapeRegExp(s) {
|
|
113
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
114
|
+
}
|
|
115
|
+
/** Remove every complete <cc-infinite-notice>…</cc-infinite-notice> span, keeping surrounding text. */
|
|
116
|
+
export function exciseNoticeSpans(text) {
|
|
117
|
+
return text.replace(NOTICE_SPAN_RE, "");
|
|
118
|
+
}
|
|
119
|
+
/** Targeted cleanup for system text, where users may legitimately quote tags. */
|
|
120
|
+
export function exciseKnownLegacyNoticeSpans(text) {
|
|
121
|
+
return text.replace(NOTICE_CAPTURE_RE, (whole, inner) => LEGACY_NOTICE_PREFIXES.some((prefix) => inner.startsWith(prefix)) ? "" : whole);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* At least one complete marker span — exact envelope, or a notice CC merged
|
|
125
|
+
* into surrounding text. Works on any raw text (a block's text, a whole
|
|
126
|
+
* transcript line or file); a bare open tag with no close never matches.
|
|
127
|
+
*/
|
|
128
|
+
export function containsNoticeSpan(text) {
|
|
129
|
+
const open = text.indexOf(NOTICE_OPEN);
|
|
130
|
+
return open !== -1 && text.indexOf(NOTICE_CLOSE, open) !== -1;
|
|
131
|
+
}
|
|
132
|
+
function sanitizeContent(content, knownLegacyOnly = false) {
|
|
133
|
+
const excise = knownLegacyOnly
|
|
134
|
+
? exciseKnownLegacyNoticeSpans
|
|
135
|
+
: exciseNoticeSpans;
|
|
136
|
+
if (typeof content === "string") {
|
|
137
|
+
const cleaned = excise(content);
|
|
138
|
+
if (cleaned === content) {
|
|
139
|
+
return { content, stripped: false, empty: false };
|
|
140
|
+
}
|
|
141
|
+
return { content: cleaned, stripped: true, empty: cleaned.trim() === "" };
|
|
142
|
+
}
|
|
143
|
+
if (!Array.isArray(content)) {
|
|
144
|
+
return { content, stripped: false, empty: false };
|
|
145
|
+
}
|
|
146
|
+
let stripped = false;
|
|
147
|
+
const kept = [];
|
|
148
|
+
for (const part of content) {
|
|
149
|
+
if (typeof part === "string" && excise(part) !== part) {
|
|
150
|
+
stripped = true;
|
|
151
|
+
const cleaned = excise(part);
|
|
152
|
+
if (cleaned.trim() !== "")
|
|
153
|
+
kept.push(cleaned);
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
if (part &&
|
|
157
|
+
typeof part === "object" &&
|
|
158
|
+
part.type === "text" &&
|
|
159
|
+
typeof part.text === "string" &&
|
|
160
|
+
excise(part.text) !== part.text) {
|
|
161
|
+
stripped = true;
|
|
162
|
+
const cleaned = excise(part.text);
|
|
163
|
+
if (cleaned.trim() !== "")
|
|
164
|
+
kept.push({ ...part, text: cleaned });
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
kept.push(part); // untouched blocks retain object identity/signatures
|
|
168
|
+
}
|
|
169
|
+
return stripped
|
|
170
|
+
? { content: kept, stripped: true, empty: kept.length === 0 }
|
|
171
|
+
: { content, stripped: false, empty: false };
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Strip pass: remove legacy injected notice content from request history.
|
|
175
|
+
* Runs on EVERY /v1/messages and count_tokens body, on all paths, before the
|
|
176
|
+
* dedupe hash. Complete marker spans are removed from legacy assistant/system
|
|
177
|
+
* content, while human user messages are left byte-verbatim even if they quote
|
|
178
|
+
* the envelope. Bare/incomplete tags are left untouched. Untouched blocks survive byte-identical to what Anthropic
|
|
179
|
+
* produced (important for replayed thinking-block signatures); a text block
|
|
180
|
+
* that merely CONTAINS the marker span (e.g. CC merged adjacent text blocks)
|
|
181
|
+
* has the span excised and the surrounding text kept — any block containing
|
|
182
|
+
* the marker is proxy-contaminated by definition, so byte-identity doesn't
|
|
183
|
+
* apply. Returns the original array untouched when nothing matches, so
|
|
184
|
+
* unmodified bodies stay verbatim.
|
|
185
|
+
*/
|
|
186
|
+
export function stripNoticeBlocks(messages) {
|
|
187
|
+
let stripped = false;
|
|
188
|
+
const out = [];
|
|
189
|
+
for (const msg of messages) {
|
|
190
|
+
if (msg?.role !== "assistant" && msg?.role !== "system") {
|
|
191
|
+
out.push(msg);
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
const cleaned = sanitizeContent(msg?.content, msg?.role === "system");
|
|
195
|
+
if (!cleaned.stripped) {
|
|
196
|
+
out.push(msg);
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
stripped = true;
|
|
200
|
+
if (!cleaned.empty)
|
|
201
|
+
out.push({ ...msg, content: cleaned.content });
|
|
202
|
+
}
|
|
203
|
+
return stripped ? { messages: out, stripped } : { messages, stripped };
|
|
204
|
+
}
|
|
205
|
+
/** Remove legacy complete marker spans from Anthropic's top-level system value. */
|
|
206
|
+
export function stripNoticeSystem(system) {
|
|
207
|
+
const cleaned = sanitizeContent(system, true);
|
|
208
|
+
if (!cleaned.stripped)
|
|
209
|
+
return { system, stripped: false };
|
|
210
|
+
return {
|
|
211
|
+
system: cleaned.empty ? undefined : cleaned.content,
|
|
212
|
+
stripped: true,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
// ---------------------------------------------------------------------------
|
|
216
|
+
// SSE machinery
|
|
217
|
+
// ---------------------------------------------------------------------------
|
|
218
|
+
function sseEvent(type, data) {
|
|
219
|
+
return `event: ${type}\ndata: ${JSON.stringify(data)}\n\n`;
|
|
220
|
+
}
|
|
221
|
+
/** content_block_start/delta/stop triple for a plain text block. */
|
|
222
|
+
export function textBlockEvents(index, text) {
|
|
223
|
+
return (sseEvent("content_block_start", {
|
|
224
|
+
type: "content_block_start",
|
|
225
|
+
index,
|
|
226
|
+
content_block: { type: "text", text: "" },
|
|
227
|
+
}) +
|
|
228
|
+
sseEvent("content_block_delta", {
|
|
229
|
+
type: "content_block_delta",
|
|
230
|
+
index,
|
|
231
|
+
delta: { type: "text_delta", text },
|
|
232
|
+
}) +
|
|
233
|
+
sseEvent("content_block_stop", { type: "content_block_stop", index }));
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Claude Code keeps at most this many characters of a recap (the joined text
|
|
237
|
+
* of the reply), truncating the end. Checked against Claude Code 2.1.278.
|
|
238
|
+
*/
|
|
239
|
+
export const RECAP_MAX_CHARS = 400;
|
|
240
|
+
/**
|
|
241
|
+
* The recap's link line, or undefined when it would not fit under Claude
|
|
242
|
+
* Code's cap and would come back clipped mid-URL.
|
|
243
|
+
*
|
|
244
|
+
* Ends with a newline: for its first few recaps Claude Code appends
|
|
245
|
+
* " (disable recaps in /config)" to the recap text, which would otherwise land
|
|
246
|
+
* on the URL's line and read as part of the MemTree note.
|
|
247
|
+
*/
|
|
248
|
+
export function recapLinkText(link, streamedTextChars, note) {
|
|
249
|
+
const text = `\n${linkLines(LINK_LABEL, link, note)}\n`;
|
|
250
|
+
return streamedTextChars + text.length <= RECAP_MAX_CHARS ? text : undefined;
|
|
251
|
+
}
|
|
252
|
+
/** content_block_start/delta/stop triple for a notice text block. */
|
|
253
|
+
export function noticeBlockEvents(index, noticeText) {
|
|
254
|
+
return (sseEvent("content_block_start", {
|
|
255
|
+
type: "content_block_start",
|
|
256
|
+
index,
|
|
257
|
+
content_block: { type: "text", text: "" },
|
|
258
|
+
}) +
|
|
259
|
+
sseEvent("content_block_delta", {
|
|
260
|
+
type: "content_block_delta",
|
|
261
|
+
index,
|
|
262
|
+
delta: { type: "text_delta", text: wrapNotice(noticeText) },
|
|
263
|
+
}) +
|
|
264
|
+
sseEvent("content_block_stop", { type: "content_block_stop", index }));
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Fabricated stream prelude for the mid-stall case: a synthetic message_start
|
|
268
|
+
* plus the notice as content block 0. Upstream's real events follow with its
|
|
269
|
+
* message_start dropped and indexes shifted +1 (SseNoticeRewriter). Cost we
|
|
270
|
+
* accept: upstream message_start carried input-token usage, which is lost —
|
|
271
|
+
* message_delta usage at end of turn still passes through.
|
|
272
|
+
*/
|
|
273
|
+
export function fabricatedPrelude(model, noticeText) {
|
|
274
|
+
return (sseEvent("message_start", {
|
|
275
|
+
type: "message_start",
|
|
276
|
+
message: {
|
|
277
|
+
id: `msg_ccinfinite_${randomUUID()}`,
|
|
278
|
+
type: "message",
|
|
279
|
+
role: "assistant",
|
|
280
|
+
model,
|
|
281
|
+
content: [],
|
|
282
|
+
stop_reason: null,
|
|
283
|
+
stop_sequence: null,
|
|
284
|
+
usage: { input_tokens: 0, output_tokens: 0 },
|
|
285
|
+
},
|
|
286
|
+
}) + noticeBlockEvents(0, noticeText));
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Incremental SSE event rewriter. Feed raw upstream bytes with push(); write
|
|
290
|
+
* the returned string to the client. Events we don't modify pass through as
|
|
291
|
+
* their original bytes.
|
|
292
|
+
*/
|
|
293
|
+
export class SseNoticeRewriter {
|
|
294
|
+
opts;
|
|
295
|
+
decoder = new StringDecoder("utf8");
|
|
296
|
+
buf = "";
|
|
297
|
+
maxIndexSeen = -1;
|
|
298
|
+
injectedResponseNotice = false;
|
|
299
|
+
injectedEndNotice = false;
|
|
300
|
+
injectedEndText = false;
|
|
301
|
+
streamedTextChars = 0;
|
|
302
|
+
constructor(opts) {
|
|
303
|
+
this.opts = opts;
|
|
304
|
+
}
|
|
305
|
+
push(chunk) {
|
|
306
|
+
this.buf += this.decoder.write(chunk);
|
|
307
|
+
let out = "";
|
|
308
|
+
while (true) {
|
|
309
|
+
const lfSep = this.buf.indexOf("\n\n");
|
|
310
|
+
const crlfSep = this.buf.indexOf("\r\n\r\n");
|
|
311
|
+
if (lfSep === -1 && crlfSep === -1)
|
|
312
|
+
break;
|
|
313
|
+
const useCrlf = crlfSep !== -1 && (lfSep === -1 || crlfSep < lfSep);
|
|
314
|
+
const sep = useCrlf ? crlfSep : lfSep;
|
|
315
|
+
const frameEnd = sep + (useCrlf ? 4 : 2);
|
|
316
|
+
const rawEvent = this.buf.slice(0, frameEnd);
|
|
317
|
+
this.buf = this.buf.slice(frameEnd);
|
|
318
|
+
out += this.transformEvent(rawEvent);
|
|
319
|
+
}
|
|
320
|
+
return out;
|
|
321
|
+
}
|
|
322
|
+
/** Anything buffered after the stream ends (normally empty). */
|
|
323
|
+
flush() {
|
|
324
|
+
const rest = this.buf + this.decoder.end();
|
|
325
|
+
this.buf = "";
|
|
326
|
+
return rest;
|
|
327
|
+
}
|
|
328
|
+
transformEvent(rawEvent) {
|
|
329
|
+
const dataLine = rawEvent
|
|
330
|
+
.split("\n")
|
|
331
|
+
.find((line) => line.startsWith("data:"));
|
|
332
|
+
if (!dataLine)
|
|
333
|
+
return rawEvent; // comment/ping-only frame
|
|
334
|
+
let data;
|
|
335
|
+
try {
|
|
336
|
+
data = JSON.parse(dataLine.slice(5).trim());
|
|
337
|
+
}
|
|
338
|
+
catch {
|
|
339
|
+
return rawEvent;
|
|
340
|
+
}
|
|
341
|
+
const type = data?.type;
|
|
342
|
+
if (this.opts.onEvent) {
|
|
343
|
+
try {
|
|
344
|
+
this.opts.onEvent(data);
|
|
345
|
+
}
|
|
346
|
+
catch {
|
|
347
|
+
// observer must never break the stream
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
if (type === "content_block_delta" &&
|
|
351
|
+
data.delta?.type === "text_delta" &&
|
|
352
|
+
typeof data.delta.text === "string") {
|
|
353
|
+
this.streamedTextChars += data.delta.text.length;
|
|
354
|
+
}
|
|
355
|
+
const baseShift = this.opts.renumberBy ?? 0;
|
|
356
|
+
if (baseShift && type === "message_start")
|
|
357
|
+
return ""; // fabricated prelude sent ours
|
|
358
|
+
const isContentEvent = type === "content_block_start" ||
|
|
359
|
+
type === "content_block_delta" ||
|
|
360
|
+
type === "content_block_stop";
|
|
361
|
+
if (isContentEvent && typeof data.index === "number") {
|
|
362
|
+
const originalIndex = data.index;
|
|
363
|
+
const insertResponseNotice = type === "content_block_start" &&
|
|
364
|
+
this.opts.beforeResponseNotice !== undefined &&
|
|
365
|
+
!this.injectedResponseNotice &&
|
|
366
|
+
!isThinkingBlock(data.content_block);
|
|
367
|
+
if (insertResponseNotice)
|
|
368
|
+
this.injectedResponseNotice = true;
|
|
369
|
+
const shift = baseShift + (this.injectedResponseNotice ? 1 : 0);
|
|
370
|
+
if (shift)
|
|
371
|
+
data.index = originalIndex + shift;
|
|
372
|
+
if (data.index > this.maxIndexSeen)
|
|
373
|
+
this.maxIndexSeen = data.index;
|
|
374
|
+
const transformed = shift ? sseEvent(type, data) : rawEvent;
|
|
375
|
+
if (insertResponseNotice) {
|
|
376
|
+
const noticeIndex = originalIndex + baseShift;
|
|
377
|
+
if (noticeIndex > this.maxIndexSeen)
|
|
378
|
+
this.maxIndexSeen = noticeIndex;
|
|
379
|
+
return (noticeBlockEvents(noticeIndex, this.opts.beforeResponseNotice) +
|
|
380
|
+
transformed);
|
|
381
|
+
}
|
|
382
|
+
return transformed;
|
|
383
|
+
}
|
|
384
|
+
if (type === "message_delta" || type === "message_stop") {
|
|
385
|
+
let notices = "";
|
|
386
|
+
// A max-token response can end after thinking without ever starting an
|
|
387
|
+
// answer/tool block. Still surface compression success, after thinking.
|
|
388
|
+
if (this.opts.beforeResponseNotice !== undefined &&
|
|
389
|
+
!this.injectedResponseNotice) {
|
|
390
|
+
this.injectedResponseNotice = true;
|
|
391
|
+
const noticeIndex = Math.max(this.maxIndexSeen + 1, baseShift);
|
|
392
|
+
this.maxIndexSeen = noticeIndex;
|
|
393
|
+
notices += noticeBlockEvents(noticeIndex, this.opts.beforeResponseNotice);
|
|
394
|
+
}
|
|
395
|
+
if (this.opts.endOfTurnNotice && !this.injectedEndNotice) {
|
|
396
|
+
this.injectedEndNotice = true;
|
|
397
|
+
const noticeIndex = this.maxIndexSeen + 1;
|
|
398
|
+
this.maxIndexSeen = noticeIndex;
|
|
399
|
+
notices += noticeBlockEvents(noticeIndex, this.opts.endOfTurnNotice);
|
|
400
|
+
}
|
|
401
|
+
if (this.opts.endOfTurnText && !this.injectedEndText) {
|
|
402
|
+
this.injectedEndText = true;
|
|
403
|
+
let text;
|
|
404
|
+
try {
|
|
405
|
+
text = this.opts.endOfTurnText(this.streamedTextChars);
|
|
406
|
+
}
|
|
407
|
+
catch {
|
|
408
|
+
text = undefined; // a formatter must never break the stream
|
|
409
|
+
}
|
|
410
|
+
if (text) {
|
|
411
|
+
const index = this.maxIndexSeen + 1;
|
|
412
|
+
this.maxIndexSeen = index;
|
|
413
|
+
notices += textBlockEvents(index, text);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
if (notices)
|
|
417
|
+
return notices + rawEvent;
|
|
418
|
+
}
|
|
419
|
+
return rawEvent;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
function isThinkingBlock(part) {
|
|
423
|
+
return part?.type === "thinking" || part?.type === "redacted_thinking";
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Non-streaming success responses: insert a notice after any leading thinking
|
|
427
|
+
* blocks and before the answer/tool content. Keeping the real answer last is
|
|
428
|
+
* important for `claude -p --output-format json`, which reports the last text
|
|
429
|
+
* block as `.result`.
|
|
430
|
+
*/
|
|
431
|
+
export function insertNoticeBeforeResponseContent(body, noticeText) {
|
|
432
|
+
try {
|
|
433
|
+
const json = JSON.parse(body.toString("utf-8"));
|
|
434
|
+
if (json?.type !== "message" || !Array.isArray(json.content))
|
|
435
|
+
return null;
|
|
436
|
+
const firstResponsePart = json.content.findIndex((part) => !isThinkingBlock(part));
|
|
437
|
+
const index = firstResponsePart === -1 ? json.content.length : firstResponsePart;
|
|
438
|
+
json.content.splice(index, 0, { type: "text", text: wrapNotice(noticeText) });
|
|
439
|
+
return Buffer.from(JSON.stringify(json), "utf-8");
|
|
440
|
+
}
|
|
441
|
+
catch {
|
|
442
|
+
return null;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Non-streaming responses: append the notice text block to the JSON body's
|
|
447
|
+
* `content`. Returns null if the body isn't the expected shape (caller sends
|
|
448
|
+
* the original bytes unmodified).
|
|
449
|
+
*/
|
|
450
|
+
export function appendNoticeToJsonBody(body, noticeText) {
|
|
451
|
+
try {
|
|
452
|
+
const json = JSON.parse(body.toString("utf-8"));
|
|
453
|
+
if (json?.type !== "message" || !Array.isArray(json.content))
|
|
454
|
+
return null;
|
|
455
|
+
json.content.push({ type: "text", text: wrapNotice(noticeText) });
|
|
456
|
+
return Buffer.from(JSON.stringify(json), "utf-8");
|
|
457
|
+
}
|
|
458
|
+
catch {
|
|
459
|
+
return null;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
/** In-stream error frame for failures after a fabricated prelude has been sent. */
|
|
463
|
+
export function sseErrorEvent(message) {
|
|
464
|
+
return sseEvent("error", {
|
|
465
|
+
type: "error",
|
|
466
|
+
error: { type: "api_error", message },
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
//# sourceMappingURL=notices.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notices.js","sourceRoot":"","sources":["../src/notices.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Startup payment gate for interactive `ccc` sessions.
|
|
3
|
+
*
|
|
4
|
+
* GET /v1/context_memory/status says whether the MemTree key is paid up. When
|
|
5
|
+
* it is not, the launcher used to print a warning and start claude anyway —
|
|
6
|
+
* but the TUI covers the terminal within a second, so the warning was never
|
|
7
|
+
* read. Instead, an unpaid interactive session now stops on a prompt:
|
|
8
|
+
*
|
|
9
|
+
* [Enter] subscribe now [c] use claude without MemTree [q] quit
|
|
10
|
+
*
|
|
11
|
+
* Everything that needs no terminal lives here so it can be unit-tested:
|
|
12
|
+
* parsing the status body, formatting the notice, and mapping keystrokes to a
|
|
13
|
+
* choice. The prompt loop itself is in cli.ts.
|
|
14
|
+
*/
|
|
15
|
+
export declare const FALLBACK_SUBSCRIBE_URL = "https://polychat.co/pricing";
|
|
16
|
+
/** Query flag on the checkout link so the server's success page says "go back to your terminal". */
|
|
17
|
+
export declare const CHECKOUT_SOURCE_PARAM = "source=ccc";
|
|
18
|
+
export declare const PAYMENT_GATE_HEADLINE = "\u26A0 MemTree is off \u2014 payment required (compression + indexing disabled).";
|
|
19
|
+
export declare const PAYMENT_GATE_PROMPT = "[Enter] subscribe now [c] use claude without MemTree [q] quit: ";
|
|
20
|
+
export type PaymentStatus = {
|
|
21
|
+
paid: boolean;
|
|
22
|
+
/** Plain-text prompt from the server, or null when paid. */
|
|
23
|
+
message: string | null;
|
|
24
|
+
/** Subscribe link: the server's payment_url, else the first /payment?… link in the message. */
|
|
25
|
+
url: string | null;
|
|
26
|
+
};
|
|
27
|
+
export type PaymentChoice = "subscribe" | "continue" | "quit";
|
|
28
|
+
/**
|
|
29
|
+
* Parse a status body. Returns null when the body does not carry a boolean
|
|
30
|
+
* `paid` (old server, wrong shape): the caller treats that as "unknown" and
|
|
31
|
+
* stays quiet, exactly like a network error.
|
|
32
|
+
*/
|
|
33
|
+
export declare function parsePaymentStatus(body: unknown): PaymentStatus | null;
|
|
34
|
+
/** Append `source=ccc` to a checkout link (idempotent). */
|
|
35
|
+
export declare function withCheckoutSource(url: string): string;
|
|
36
|
+
/** First `/payment?…` link in a payment prompt, or null. */
|
|
37
|
+
export declare function extractPaymentUrl(message: string | null): string | null;
|
|
38
|
+
/**
|
|
39
|
+
* Terminal text for an unpaid key: the headline and one line with the
|
|
40
|
+
* recommended plan (when the server's prose names one) and the subscribe
|
|
41
|
+
* link. The full server message is deliberately not echoed — it is several
|
|
42
|
+
* paragraphs written for a chat window, and the prompt below is the point.
|
|
43
|
+
*/
|
|
44
|
+
export declare function formatPaymentNotice(status: PaymentStatus, options?: {
|
|
45
|
+
hyperlinks?: boolean;
|
|
46
|
+
}): string;
|
|
47
|
+
/** OSC 8 terminal hyperlink: `text` shown, `url` opened on click. */
|
|
48
|
+
export declare function hyperlink(text: string, url: string): string;
|
|
49
|
+
/** "Starter plan, $5/month" from the recommender's prose, or null. */
|
|
50
|
+
export declare function extractRecommendedPlan(message: string | null): string | null;
|
|
51
|
+
/** Map a prompt answer to a choice. Enter (empty) or `s` subscribes. */
|
|
52
|
+
export declare function parsePaymentChoice(answer: string): PaymentChoice;
|
|
53
|
+
//# sourceMappingURL=payment-gate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payment-gate.d.ts","sourceRoot":"","sources":["../src/payment-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,eAAO,MAAM,sBAAsB,gCAAgC,CAAC;AAEpE,oGAAoG;AACpG,eAAO,MAAM,qBAAqB,eAAe,CAAC;AAElD,eAAO,MAAM,qBAAqB,qFACwC,CAAC;AAE3E,eAAO,MAAM,mBAAmB,wEACuC,CAAC;AAExE,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,OAAO,CAAC;IACd,4DAA4D;IAC5D,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,+FAA+F;IAC/F,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG,UAAU,GAAG,MAAM,CAAC;AAQ9D;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,GAAG,aAAa,GAAG,IAAI,CAkBtE;AAED,2DAA2D;AAC3D,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAGtD;AAED,4DAA4D;AAC5D,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAIvE;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,aAAa,EACrB,OAAO,GAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAA;CAAO,GACrC,MAAM,CAOR;AAED,qEAAqE;AACrE,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED,sEAAsE;AACtE,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAI5E;AAED,wEAAwE;AACxE,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,CAKhE"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Startup payment gate for interactive `ccc` sessions.
|
|
3
|
+
*
|
|
4
|
+
* GET /v1/context_memory/status says whether the MemTree key is paid up. When
|
|
5
|
+
* it is not, the launcher used to print a warning and start claude anyway —
|
|
6
|
+
* but the TUI covers the terminal within a second, so the warning was never
|
|
7
|
+
* read. Instead, an unpaid interactive session now stops on a prompt:
|
|
8
|
+
*
|
|
9
|
+
* [Enter] subscribe now [c] use claude without MemTree [q] quit
|
|
10
|
+
*
|
|
11
|
+
* Everything that needs no terminal lives here so it can be unit-tested:
|
|
12
|
+
* parsing the status body, formatting the notice, and mapping keystrokes to a
|
|
13
|
+
* choice. The prompt loop itself is in cli.ts.
|
|
14
|
+
*/
|
|
15
|
+
export const FALLBACK_SUBSCRIBE_URL = "https://polychat.co/pricing";
|
|
16
|
+
/** Query flag on the checkout link so the server's success page says "go back to your terminal". */
|
|
17
|
+
export const CHECKOUT_SOURCE_PARAM = "source=ccc";
|
|
18
|
+
export const PAYMENT_GATE_HEADLINE = "⚠ MemTree is off — payment required (compression + indexing disabled).";
|
|
19
|
+
export const PAYMENT_GATE_PROMPT = "[Enter] subscribe now [c] use claude without MemTree [q] quit: ";
|
|
20
|
+
const PAYMENT_URL_RE = /https?:\/\/\S+\/payment\?\S+/;
|
|
21
|
+
// "we recommend 🌱 Starter plan for $5/month" → plan name + price.
|
|
22
|
+
const RECOMMENDED_PLAN_RE = /recommend\s+(?:\S+\s+)?([A-Za-z][\w ]*?plan)\s+for\s+(\$[\d.]+\/month)/i;
|
|
23
|
+
// C0 controls, DEL, and C1 controls.
|
|
24
|
+
const CONTROL_CHARS_RE = /[\u0000-\u001f\u007f-\u009f]/g;
|
|
25
|
+
/**
|
|
26
|
+
* Parse a status body. Returns null when the body does not carry a boolean
|
|
27
|
+
* `paid` (old server, wrong shape): the caller treats that as "unknown" and
|
|
28
|
+
* stays quiet, exactly like a network error.
|
|
29
|
+
*/
|
|
30
|
+
export function parsePaymentStatus(body) {
|
|
31
|
+
if (typeof body !== "object" || body === null)
|
|
32
|
+
return null;
|
|
33
|
+
const record = body;
|
|
34
|
+
if (typeof record.paid !== "boolean")
|
|
35
|
+
return null;
|
|
36
|
+
const message = typeof record.payment_message === "string" && record.payment_message.trim()
|
|
37
|
+
? record.payment_message
|
|
38
|
+
: null;
|
|
39
|
+
const explicitUrl = typeof record.payment_url === "string" && record.payment_url.trim()
|
|
40
|
+
? record.payment_url.trim()
|
|
41
|
+
: null;
|
|
42
|
+
const url = explicitUrl ?? extractPaymentUrl(message);
|
|
43
|
+
return {
|
|
44
|
+
paid: record.paid,
|
|
45
|
+
message,
|
|
46
|
+
url: url ? withCheckoutSource(url) : null,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/** Append `source=ccc` to a checkout link (idempotent). */
|
|
50
|
+
export function withCheckoutSource(url) {
|
|
51
|
+
if (url.includes(CHECKOUT_SOURCE_PARAM))
|
|
52
|
+
return url;
|
|
53
|
+
return `${url}${url.includes("?") ? "&" : "?"}${CHECKOUT_SOURCE_PARAM}`;
|
|
54
|
+
}
|
|
55
|
+
/** First `/payment?…` link in a payment prompt, or null. */
|
|
56
|
+
export function extractPaymentUrl(message) {
|
|
57
|
+
if (!message)
|
|
58
|
+
return null;
|
|
59
|
+
const match = PAYMENT_URL_RE.exec(message);
|
|
60
|
+
return match ? match[0] : null;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Terminal text for an unpaid key: the headline and one line with the
|
|
64
|
+
* recommended plan (when the server's prose names one) and the subscribe
|
|
65
|
+
* link. The full server message is deliberately not echoed — it is several
|
|
66
|
+
* paragraphs written for a chat window, and the prompt below is the point.
|
|
67
|
+
*/
|
|
68
|
+
export function formatPaymentNotice(status, options = {}) {
|
|
69
|
+
const plan = extractRecommendedPlan(status.message);
|
|
70
|
+
const url = status.url ?? FALLBACK_SUBSCRIBE_URL;
|
|
71
|
+
if (!options.hyperlinks)
|
|
72
|
+
return `${PAYMENT_GATE_HEADLINE}\n ${plan ? `${plan}: ` : ""}${url}`;
|
|
73
|
+
// Interactive terminals: the plan name is an OSC 8 link to checkout, and the
|
|
74
|
+
// URL is printed below it for terminals without hyperlinks (and to copy).
|
|
75
|
+
return `${PAYMENT_GATE_HEADLINE}\n ${hyperlink(plan ?? "Subscribe", url)}\n ${url}`;
|
|
76
|
+
}
|
|
77
|
+
/** OSC 8 terminal hyperlink: `text` shown, `url` opened on click. */
|
|
78
|
+
export function hyperlink(text, url) {
|
|
79
|
+
return `\x1b]8;;${url}\x1b\\${text}\x1b]8;;\x1b\\`;
|
|
80
|
+
}
|
|
81
|
+
/** "Starter plan, $5/month" from the recommender's prose, or null. */
|
|
82
|
+
export function extractRecommendedPlan(message) {
|
|
83
|
+
if (!message)
|
|
84
|
+
return null;
|
|
85
|
+
const match = RECOMMENDED_PLAN_RE.exec(message.replace(CONTROL_CHARS_RE, " "));
|
|
86
|
+
return match ? `${match[1].trim()}, ${match[2]}` : null;
|
|
87
|
+
}
|
|
88
|
+
/** Map a prompt answer to a choice. Enter (empty) or `s` subscribes. */
|
|
89
|
+
export function parsePaymentChoice(answer) {
|
|
90
|
+
const key = answer.trim().toLowerCase();
|
|
91
|
+
if (key === "" || key === "s" || key === "subscribe")
|
|
92
|
+
return "subscribe";
|
|
93
|
+
if (key === "q" || key === "quit" || key === "exit")
|
|
94
|
+
return "quit";
|
|
95
|
+
return "continue";
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=payment-gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payment-gate.js","sourceRoot":"","sources":["../src/payment-gate.ts"],"names":[],"mappings":"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"}
|