claude-code-infinite 1.0.27 → 1.0.29
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 +63 -29
- package/dist/auto-update.d.ts +2 -0
- package/dist/auto-update.d.ts.map +1 -1
- package/dist/auto-update.js +4 -0
- package/dist/auto-update.js.map +1 -1
- package/dist/claude-auth.d.ts +27 -2
- package/dist/claude-auth.d.ts.map +1 -1
- package/dist/claude-auth.js +76 -4
- package/dist/claude-auth.js.map +1 -1
- package/dist/claude-install.d.ts +73 -0
- package/dist/claude-install.d.ts.map +1 -0
- package/dist/claude-install.js +142 -0
- package/dist/claude-install.js.map +1 -0
- package/dist/claude-plan-warning.d.ts +70 -7
- package/dist/claude-plan-warning.d.ts.map +1 -1
- package/dist/claude-plan-warning.js +152 -8
- package/dist/claude-plan-warning.js.map +1 -1
- package/dist/cli-args.d.ts +5 -0
- package/dist/cli-args.d.ts.map +1 -1
- package/dist/cli-args.js +9 -0
- package/dist/cli-args.js.map +1 -1
- package/dist/cli-main.js +222 -84
- package/dist/cli-main.js.map +1 -1
- package/dist/diagnostics-schema.d.ts +4 -0
- package/dist/diagnostics-schema.d.ts.map +1 -1
- package/dist/diagnostics-schema.js +20 -0
- package/dist/diagnostics-schema.js.map +1 -1
- package/dist/diagnostics.d.ts +24 -1
- package/dist/diagnostics.d.ts.map +1 -1
- package/dist/diagnostics.js +68 -4
- package/dist/diagnostics.js.map +1 -1
- package/dist/hooks.d.ts +8 -0
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +57 -25
- package/dist/hooks.js.map +1 -1
- package/dist/install-ping.d.ts +54 -0
- package/dist/install-ping.d.ts.map +1 -0
- package/dist/install-ping.js +177 -0
- package/dist/install-ping.js.map +1 -0
- package/dist/key-page.d.ts +17 -0
- package/dist/key-page.d.ts.map +1 -0
- package/dist/key-page.js +32 -0
- package/dist/key-page.js.map +1 -0
- package/dist/key-status.d.ts +79 -0
- package/dist/key-status.d.ts.map +1 -0
- package/dist/key-status.js +143 -0
- package/dist/key-status.js.map +1 -0
- package/dist/memtree-finder.d.ts +2 -1
- package/dist/memtree-finder.d.ts.map +1 -1
- package/dist/memtree-finder.js +4 -5
- package/dist/memtree-finder.js.map +1 -1
- package/dist/memtree-mcp.d.ts.map +1 -1
- package/dist/memtree-mcp.js +17 -7
- package/dist/memtree-mcp.js.map +1 -1
- package/dist/memtree-times.d.ts +35 -0
- package/dist/memtree-times.d.ts.map +1 -0
- package/dist/memtree-times.js +166 -0
- package/dist/memtree-times.js.map +1 -0
- package/dist/memtree-tools.d.ts +12 -0
- package/dist/memtree-tools.d.ts.map +1 -1
- package/dist/memtree-tools.js +63 -7
- package/dist/memtree-tools.js.map +1 -1
- package/dist/memtree.d.ts +23 -0
- package/dist/memtree.d.ts.map +1 -1
- package/dist/memtree.js +39 -3
- package/dist/memtree.js.map +1 -1
- package/dist/notices.d.ts +5 -0
- package/dist/notices.d.ts.map +1 -1
- package/dist/notices.js +13 -0
- package/dist/notices.js.map +1 -1
- package/dist/paused-session.d.ts +29 -0
- package/dist/paused-session.d.ts.map +1 -0
- package/dist/paused-session.js +148 -0
- package/dist/paused-session.js.map +1 -0
- package/dist/payment-gate.d.ts +44 -13
- package/dist/payment-gate.d.ts.map +1 -1
- package/dist/payment-gate.js +82 -23
- package/dist/payment-gate.js.map +1 -1
- package/dist/payment-link.d.ts +68 -0
- package/dist/payment-link.d.ts.map +1 -0
- package/dist/payment-link.js +123 -0
- package/dist/payment-link.js.map +1 -0
- package/dist/postinstall.d.ts +2 -0
- package/dist/postinstall.d.ts.map +1 -0
- package/dist/postinstall.js +4 -0
- package/dist/postinstall.js.map +1 -0
- package/dist/project-trees.d.ts +100 -0
- package/dist/project-trees.d.ts.map +1 -0
- package/dist/project-trees.js +196 -0
- package/dist/project-trees.js.map +1 -0
- package/dist/proxy.d.ts +29 -8
- package/dist/proxy.d.ts.map +1 -1
- package/dist/proxy.js +167 -25
- package/dist/proxy.js.map +1 -1
- package/dist/rate-limit.d.ts +64 -0
- package/dist/rate-limit.d.ts.map +1 -0
- package/dist/rate-limit.js +126 -0
- package/dist/rate-limit.js.map +1 -0
- package/dist/reqlog.d.ts +13 -2
- package/dist/reqlog.d.ts.map +1 -1
- package/dist/reqlog.js.map +1 -1
- package/package.json +2 -1
package/dist/payment-gate.d.ts
CHANGED
|
@@ -2,21 +2,41 @@
|
|
|
2
2
|
* Startup payment gate for interactive `ccc` sessions.
|
|
3
3
|
*
|
|
4
4
|
* GET /v1/context_memory/status says whether the MemTree key is paid up. When
|
|
5
|
-
* it is not,
|
|
6
|
-
*
|
|
7
|
-
* read. Instead, an unpaid interactive session now stops on a prompt:
|
|
5
|
+
* it is not, an interactive session stops on a short prompt before Claude Code
|
|
6
|
+
* takes the terminal (the TUI would cover a printed warning within a second):
|
|
8
7
|
*
|
|
9
|
-
*
|
|
8
|
+
* MemTree is paused: your free credit is used up.
|
|
9
|
+
* Without it, long sessions hit the context limit and get compacted, losing information,
|
|
10
|
+
* and your sessions aren't saved or searchable.
|
|
11
|
+
* Starter is $5/month: https://app.polychat.co/p/Ab3xY9…
|
|
12
|
+
* [Enter] subscribe ($5/mo) [c] continue without MemTree [q] quit:
|
|
13
|
+
*
|
|
14
|
+
* The plan, price, reason and short link come from POST /v1/payment/link
|
|
15
|
+
* (payment-link.ts); an older server without it gets the status probe's
|
|
16
|
+
* `/payment?…` link and the plan named in its prose.
|
|
10
17
|
*
|
|
11
18
|
* Everything that needs no terminal lives here so it can be unit-tested:
|
|
12
19
|
* parsing the status body, formatting the notice, and mapping keystrokes to a
|
|
13
|
-
* choice. The prompt loop itself is in cli.ts.
|
|
20
|
+
* choice. The prompt loop itself is in cli-main.ts.
|
|
14
21
|
*/
|
|
22
|
+
import { type PaymentLink } from "./payment-link.js";
|
|
15
23
|
export declare const FALLBACK_SUBSCRIBE_URL = "https://polychat.co/pricing";
|
|
16
24
|
/** Query flag on the checkout link so the server's success page says "go back to your terminal". */
|
|
17
25
|
export declare const CHECKOUT_SOURCE_PARAM = "source=ccc";
|
|
18
|
-
export declare const
|
|
19
|
-
export declare const
|
|
26
|
+
export declare const PAUSED_FREE_CREDIT_HEADLINE = "MemTree is paused: your free credit is used up.";
|
|
27
|
+
export declare const PAUSED_PLAN_LIMIT_HEADLINE = "MemTree is paused: your plan's usage limit is reached.";
|
|
28
|
+
export declare const PAUSED_HEADLINE = "MemTree is paused: payment required.";
|
|
29
|
+
/**
|
|
30
|
+
* What a paused session runs without. With [c] ccc starts plain Claude Code
|
|
31
|
+
* (cli-main.ts), whose own auto-compaction summarizes a long session, and nothing
|
|
32
|
+
* reaches MemTree, so the session is not indexed, saved there, or searchable.
|
|
33
|
+
*/
|
|
34
|
+
export declare const PAUSED_LOSS_LINES: string;
|
|
35
|
+
/** After [c]: the session runs as plain Claude Code; the next start checks again. */
|
|
36
|
+
export declare const MEMTREE_OFF_LINE = "MemTree is off for this session. ccc checks again the next time you start it.";
|
|
37
|
+
/** Print/non-TTY runs of an unpaid key: one stderr line, then plain Claude Code. */
|
|
38
|
+
export declare const RESUMED_PAUSED_LINE = "MemTree is paused, so this resumed session runs uncompressed. ccc checks again next start.";
|
|
39
|
+
export declare const PRINT_PAUSED_LINE: string;
|
|
20
40
|
export type PaymentStatus = {
|
|
21
41
|
paid: boolean;
|
|
22
42
|
/** Plain-text prompt from the server, or null when paid. */
|
|
@@ -31,19 +51,30 @@ export type PaymentChoice = "subscribe" | "continue" | "quit";
|
|
|
31
51
|
* stays quiet, exactly like a network error.
|
|
32
52
|
*/
|
|
33
53
|
export declare function parsePaymentStatus(body: unknown): PaymentStatus | null;
|
|
34
|
-
/** Append `source=ccc` to a
|
|
54
|
+
/** Append `source=ccc` to a `/payment?…` link (idempotent); a short link already implies it. */
|
|
35
55
|
export declare function withCheckoutSource(url: string): string;
|
|
36
|
-
/**
|
|
56
|
+
/** The short checkout link in a payment prompt, else its first `/payment?…` link, else null. */
|
|
37
57
|
export declare function extractPaymentUrl(message: string | null): string | null;
|
|
38
58
|
/**
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
|
|
42
|
-
|
|
59
|
+
* A 402 detail with its `/payment?…` link replaced by the short one, so every
|
|
60
|
+
* in-session notice built from it shows the short link. Unchanged without either.
|
|
61
|
+
*/
|
|
62
|
+
export declare function withShortPaymentUrl(detail: string, shortUrl: string | null | undefined): string;
|
|
63
|
+
/**
|
|
64
|
+
* The gate's text, at most four lines above the prompt: why MemTree is paused,
|
|
65
|
+
* what the session loses without it, and the plan, price and link. The full
|
|
66
|
+
* server message is deliberately not echoed: it is several paragraphs written
|
|
67
|
+
* for a chat window, and the prompt below is the point.
|
|
43
68
|
*/
|
|
44
69
|
export declare function formatPaymentNotice(status: PaymentStatus, options?: {
|
|
45
70
|
hyperlinks?: boolean;
|
|
71
|
+
color?: boolean;
|
|
72
|
+
link?: PaymentLink | null;
|
|
46
73
|
}): string;
|
|
74
|
+
/** The prompt line, with the server's price when it gave one. */
|
|
75
|
+
export declare function paymentGatePrompt(link?: PaymentLink | null): string;
|
|
76
|
+
/** The first line: why MemTree is paused, as far as the server said. */
|
|
77
|
+
export declare function pausedHeadline(reason: string | null): string;
|
|
47
78
|
/** OSC 8 terminal hyperlink: `text` shown, `url` opened on click. */
|
|
48
79
|
export declare function hyperlink(text: string, url: string): string;
|
|
49
80
|
/** "Starter plan, $5/month" from the recommender's prose, or null. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"payment-gate.d.ts","sourceRoot":"","sources":["../src/payment-gate.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"payment-gate.d.ts","sourceRoot":"","sources":["../src/payment-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EACL,KAAK,WAAW,EAIjB,MAAM,mBAAmB,CAAC;AAE3B,eAAO,MAAM,sBAAsB,gCAAgC,CAAC;AAEpE,oGAAoG;AACpG,eAAO,MAAM,qBAAqB,eAAe,CAAC;AAElD,eAAO,MAAM,2BAA2B,oDAAoD,CAAC;AAC7F,eAAO,MAAM,0BAA0B,2DAA2D,CAAC;AACnG,eAAO,MAAM,eAAe,yCAAyC,CAAC;AACtE;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,QAEgC,CAAC;AAC/D,qFAAqF;AACrF,eAAO,MAAM,gBAAgB,kFACoD,CAAC;AAClF,oFAAoF;AACpF,eAAO,MAAM,mBAAmB,+FAC8D,CAAC;AAC/F,eAAO,MAAM,iBAAiB,QAES,CAAC;AAExC,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;AAY9D;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,GAAG,aAAa,GAAG,IAAI,CAoBtE;AAED,gGAAgG;AAChG,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAGtD;AAED,gGAAgG;AAChG,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAIvE;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAG/F;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,aAAa,EACrB,OAAO,GAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,WAAW,GAAG,IAAI,CAAA;CAAO,GACjF,MAAM,CAUR;AAED,iEAAiE;AACjE,wBAAgB,iBAAiB,CAAC,IAAI,GAAE,WAAW,GAAG,IAAW,GAAG,MAAM,CAIzE;AAED,wEAAwE;AACxE,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAI5D;AAQD,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"}
|
package/dist/payment-gate.js
CHANGED
|
@@ -2,24 +2,50 @@
|
|
|
2
2
|
* Startup payment gate for interactive `ccc` sessions.
|
|
3
3
|
*
|
|
4
4
|
* GET /v1/context_memory/status says whether the MemTree key is paid up. When
|
|
5
|
-
* it is not,
|
|
6
|
-
*
|
|
7
|
-
* read. Instead, an unpaid interactive session now stops on a prompt:
|
|
5
|
+
* it is not, an interactive session stops on a short prompt before Claude Code
|
|
6
|
+
* takes the terminal (the TUI would cover a printed warning within a second):
|
|
8
7
|
*
|
|
9
|
-
*
|
|
8
|
+
* MemTree is paused: your free credit is used up.
|
|
9
|
+
* Without it, long sessions hit the context limit and get compacted, losing information,
|
|
10
|
+
* and your sessions aren't saved or searchable.
|
|
11
|
+
* Starter is $5/month: https://app.polychat.co/p/Ab3xY9…
|
|
12
|
+
* [Enter] subscribe ($5/mo) [c] continue without MemTree [q] quit:
|
|
13
|
+
*
|
|
14
|
+
* The plan, price, reason and short link come from POST /v1/payment/link
|
|
15
|
+
* (payment-link.ts); an older server without it gets the status probe's
|
|
16
|
+
* `/payment?…` link and the plan named in its prose.
|
|
10
17
|
*
|
|
11
18
|
* Everything that needs no terminal lives here so it can be unit-tested:
|
|
12
19
|
* parsing the status body, formatting the notice, and mapping keystrokes to a
|
|
13
|
-
* choice. The prompt loop itself is in cli.ts.
|
|
20
|
+
* choice. The prompt loop itself is in cli-main.ts.
|
|
14
21
|
*/
|
|
22
|
+
import { REASON_FREE_CREDIT_USED, REASON_PLAN_LIMIT, formatUsd, } from "./payment-link.js";
|
|
15
23
|
export const FALLBACK_SUBSCRIBE_URL = "https://polychat.co/pricing";
|
|
16
24
|
/** Query flag on the checkout link so the server's success page says "go back to your terminal". */
|
|
17
25
|
export const CHECKOUT_SOURCE_PARAM = "source=ccc";
|
|
18
|
-
export const
|
|
19
|
-
export const
|
|
26
|
+
export const PAUSED_FREE_CREDIT_HEADLINE = "MemTree is paused: your free credit is used up.";
|
|
27
|
+
export const PAUSED_PLAN_LIMIT_HEADLINE = "MemTree is paused: your plan's usage limit is reached.";
|
|
28
|
+
export const PAUSED_HEADLINE = "MemTree is paused: payment required.";
|
|
29
|
+
/**
|
|
30
|
+
* What a paused session runs without. With [c] ccc starts plain Claude Code
|
|
31
|
+
* (cli-main.ts), whose own auto-compaction summarizes a long session, and nothing
|
|
32
|
+
* reaches MemTree, so the session is not indexed, saved there, or searchable.
|
|
33
|
+
*/
|
|
34
|
+
export const PAUSED_LOSS_LINES = "Without it, long sessions hit the context limit and get compacted, losing\n" +
|
|
35
|
+
"information, and your sessions aren't saved or searchable.";
|
|
36
|
+
/** After [c]: the session runs as plain Claude Code; the next start checks again. */
|
|
37
|
+
export const MEMTREE_OFF_LINE = "MemTree is off for this session. ccc checks again the next time you start it.";
|
|
38
|
+
/** Print/non-TTY runs of an unpaid key: one stderr line, then plain Claude Code. */
|
|
39
|
+
export const RESUMED_PAUSED_LINE = "MemTree is paused, so this resumed session runs uncompressed. ccc checks again next start.";
|
|
40
|
+
export const PRINT_PAUSED_LINE = "ccc: MemTree is paused (payment required), so this run is plain Claude Code. " +
|
|
41
|
+
"Run ccc in a terminal to subscribe.";
|
|
20
42
|
const PAYMENT_URL_RE = /https?:\/\/\S+\/payment\?\S+/;
|
|
43
|
+
/** The short checkout link (payment-link.ts) that ccc puts in place of the `/payment?…` one. */
|
|
44
|
+
const SHORT_PAYMENT_URL_RE = /https?:\/\/[^\s/]+\/p\/[A-Za-z0-9]{12,64}(?![A-Za-z0-9])/;
|
|
21
45
|
// "we recommend 🌱 Starter plan for $5/month" → plan name + price.
|
|
22
46
|
const RECOMMENDED_PLAN_RE = /recommend\s+(?:\S+\s+)?([A-Za-z][\w ]*?plan)\s+for\s+(\$[\d.]+\/month)/i;
|
|
47
|
+
// No quotes, `$`, backticks or bare `%` (cmd.exe expands %VAR% when Windows opens it).
|
|
48
|
+
const SAFE_URL_RE = /^https?:\/\/(?:[^\s"'`$<>%\\\u0000-\u001f\u007f-\u009f]|%[0-9A-Fa-f]{2})+$/;
|
|
23
49
|
// C0 controls, DEL, and C1 controls.
|
|
24
50
|
const CONTROL_CHARS_RE = /[\u0000-\u001f\u007f-\u009f]/g;
|
|
25
51
|
/**
|
|
@@ -39,40 +65,73 @@ export function parsePaymentStatus(body) {
|
|
|
39
65
|
const explicitUrl = typeof record.payment_url === "string" && record.payment_url.trim()
|
|
40
66
|
? record.payment_url.trim()
|
|
41
67
|
: null;
|
|
42
|
-
const
|
|
68
|
+
const found = explicitUrl ?? extractPaymentUrl(message);
|
|
69
|
+
// Printed and handed to the browser opener: a plain http(s) URL only.
|
|
70
|
+
const url = found && SAFE_URL_RE.test(found) ? found : null;
|
|
43
71
|
return {
|
|
44
72
|
paid: record.paid,
|
|
45
73
|
message,
|
|
46
74
|
url: url ? withCheckoutSource(url) : null,
|
|
47
75
|
};
|
|
48
76
|
}
|
|
49
|
-
/** Append `source=ccc` to a
|
|
77
|
+
/** Append `source=ccc` to a `/payment?…` link (idempotent); a short link already implies it. */
|
|
50
78
|
export function withCheckoutSource(url) {
|
|
51
|
-
if (url.includes(CHECKOUT_SOURCE_PARAM))
|
|
79
|
+
if (url.includes(CHECKOUT_SOURCE_PARAM) || SHORT_PAYMENT_URL_RE.test(url))
|
|
52
80
|
return url;
|
|
53
81
|
return `${url}${url.includes("?") ? "&" : "?"}${CHECKOUT_SOURCE_PARAM}`;
|
|
54
82
|
}
|
|
55
|
-
/**
|
|
83
|
+
/** The short checkout link in a payment prompt, else its first `/payment?…` link, else null. */
|
|
56
84
|
export function extractPaymentUrl(message) {
|
|
57
85
|
if (!message)
|
|
58
86
|
return null;
|
|
59
|
-
const match = PAYMENT_URL_RE.exec(message);
|
|
87
|
+
const match = SHORT_PAYMENT_URL_RE.exec(message) ?? PAYMENT_URL_RE.exec(message);
|
|
60
88
|
return match ? match[0] : null;
|
|
61
89
|
}
|
|
62
90
|
/**
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
|
|
66
|
-
|
|
91
|
+
* A 402 detail with its `/payment?…` link replaced by the short one, so every
|
|
92
|
+
* in-session notice built from it shows the short link. Unchanged without either.
|
|
93
|
+
*/
|
|
94
|
+
export function withShortPaymentUrl(detail, shortUrl) {
|
|
95
|
+
if (!shortUrl)
|
|
96
|
+
return detail;
|
|
97
|
+
return detail.replace(new RegExp(PAYMENT_URL_RE.source, "g"), () => shortUrl);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* The gate's text, at most four lines above the prompt: why MemTree is paused,
|
|
101
|
+
* what the session loses without it, and the plan, price and link. The full
|
|
102
|
+
* server message is deliberately not echoed: it is several paragraphs written
|
|
103
|
+
* for a chat window, and the prompt below is the point.
|
|
67
104
|
*/
|
|
68
105
|
export function formatPaymentNotice(status, options = {}) {
|
|
69
|
-
const
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
106
|
+
const link = options.link ?? null;
|
|
107
|
+
const headline = pausedHeadline(link?.reason ?? null);
|
|
108
|
+
const url = link?.url ?? status.url ?? FALLBACK_SUBSCRIBE_URL;
|
|
109
|
+
const shownUrl = options.hyperlinks ? hyperlink(url, url) : url;
|
|
110
|
+
return [
|
|
111
|
+
options.color ? `\x1b[1;33m${headline}\x1b[0m` : headline,
|
|
112
|
+
PAUSED_LOSS_LINES,
|
|
113
|
+
`${offerLabel(status, link)}: ${shownUrl}`,
|
|
114
|
+
].join("\n");
|
|
115
|
+
}
|
|
116
|
+
/** The prompt line, with the server's price when it gave one. */
|
|
117
|
+
export function paymentGatePrompt(link = null) {
|
|
118
|
+
const verb = link?.reason === REASON_PLAN_LIMIT ? "upgrade" : "subscribe";
|
|
119
|
+
const price = link ? ` (${formatUsd(link.monthlyPriceUsd)}/mo)` : "";
|
|
120
|
+
return `[Enter] ${verb}${price} [c] continue without MemTree [q] quit: `;
|
|
121
|
+
}
|
|
122
|
+
/** The first line: why MemTree is paused, as far as the server said. */
|
|
123
|
+
export function pausedHeadline(reason) {
|
|
124
|
+
if (reason === REASON_FREE_CREDIT_USED)
|
|
125
|
+
return PAUSED_FREE_CREDIT_HEADLINE;
|
|
126
|
+
if (reason === REASON_PLAN_LIMIT)
|
|
127
|
+
return PAUSED_PLAN_LIMIT_HEADLINE;
|
|
128
|
+
return PAUSED_HEADLINE;
|
|
129
|
+
}
|
|
130
|
+
/** "Starter is $5/month", else the old server's "Starter plan, $5/month", else "Subscribe". */
|
|
131
|
+
function offerLabel(status, link) {
|
|
132
|
+
if (link)
|
|
133
|
+
return `${link.plan} is ${formatUsd(link.monthlyPriceUsd)}/month`;
|
|
134
|
+
return extractRecommendedPlan(status.message) ?? "Subscribe";
|
|
76
135
|
}
|
|
77
136
|
/** OSC 8 terminal hyperlink: `text` shown, `url` opened on click. */
|
|
78
137
|
export function hyperlink(text, url) {
|
package/dist/payment-gate.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"payment-gate.js","sourceRoot":"","sources":["../src/payment-gate.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"payment-gate.js","sourceRoot":"","sources":["../src/payment-gate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Short checkout link for an unpaid MemTree key.
|
|
3
|
+
*
|
|
4
|
+
* POST /v1/payment/link (authenticated with the MemTree key) answers
|
|
5
|
+
* `{url: "https://app.polychat.co/p/Ab3xY9…", plan, monthly_price_usd, reason, expires_at}`.
|
|
6
|
+
* The link opens Stripe Checkout for the key's own account with no sign-in, so the
|
|
7
|
+
* wrong-account and signed-out detours of the old `/payment?user_id=…` link go away.
|
|
8
|
+
* It works for an hour; the cache below fetches a new one when the old one is about
|
|
9
|
+
* to expire.
|
|
10
|
+
*
|
|
11
|
+
* Servers without the endpoint (404), timeouts and odd bodies all mean "no link":
|
|
12
|
+
* callers fall back to the status probe's `/payment?…` link, exactly as before.
|
|
13
|
+
*/
|
|
14
|
+
export declare const PAYMENT_LINK_PATH = "/v1/payment/link";
|
|
15
|
+
/** A link this close to expiry is replaced before it is shown or opened. */
|
|
16
|
+
export declare const PAYMENT_LINK_REFRESH_MARGIN_MS: number;
|
|
17
|
+
/** After a failed fetch (old server, outage), wait this long before asking again. */
|
|
18
|
+
export declare const PAYMENT_LINK_RETRY_MS: number;
|
|
19
|
+
export declare const PAYMENT_LINK_TIMEOUT_MS = 1500;
|
|
20
|
+
export declare const REASON_FREE_CREDIT_USED = "free_credit_used";
|
|
21
|
+
export declare const REASON_PLAN_LIMIT = "plan_limit";
|
|
22
|
+
export type PaymentLink = {
|
|
23
|
+
url: string;
|
|
24
|
+
/** Plan name to show, e.g. "Starter". */
|
|
25
|
+
plan: string;
|
|
26
|
+
/** Monthly price in US dollars, e.g. 5. */
|
|
27
|
+
monthlyPriceUsd: number;
|
|
28
|
+
/** Why MemTree is paused (REASON_*), or null when the server did not say. */
|
|
29
|
+
reason: string | null;
|
|
30
|
+
/** Epoch ms after which the link no longer works, or null when unknown. */
|
|
31
|
+
expiresAt: number | null;
|
|
32
|
+
};
|
|
33
|
+
export type FetchLike = (input: string, init?: RequestInit) => Promise<Response>;
|
|
34
|
+
export type PaymentLinkFetchOptions = {
|
|
35
|
+
baseUrl: string;
|
|
36
|
+
apiKey: string;
|
|
37
|
+
headers?: Record<string, string>;
|
|
38
|
+
timeoutMs?: number;
|
|
39
|
+
fetchImpl?: FetchLike;
|
|
40
|
+
};
|
|
41
|
+
/** Ask the server for a short link; null on any failure. Never throws. */
|
|
42
|
+
export declare function fetchPaymentLink(opts: PaymentLinkFetchOptions): Promise<PaymentLink | null>;
|
|
43
|
+
/** Validate the endpoint's body; null unless it carries a usable short link. */
|
|
44
|
+
export declare function parsePaymentLink(body: unknown): PaymentLink | null;
|
|
45
|
+
/** Whether `link` can still be shown or opened (not within the margin of expiring). */
|
|
46
|
+
export declare function paymentLinkFresh(link: PaymentLink, now?: number): boolean;
|
|
47
|
+
/** "$5", "$7.50": whole dollars without cents. */
|
|
48
|
+
export declare function formatUsd(amount: number): string;
|
|
49
|
+
/**
|
|
50
|
+
* One process's short link: fetched on demand, reused while fresh, refetched near
|
|
51
|
+
* expiry, and not asked for again for PAYMENT_LINK_RETRY_MS after a failure (so an
|
|
52
|
+
* old server's 404 costs one request, not one per 402).
|
|
53
|
+
*/
|
|
54
|
+
export declare class PaymentLinkCache {
|
|
55
|
+
private readonly fetchLink;
|
|
56
|
+
private readonly now;
|
|
57
|
+
private link;
|
|
58
|
+
private inFlight;
|
|
59
|
+
private failedAt;
|
|
60
|
+
constructor(fetchLink: () => Promise<PaymentLink | null>, now?: () => number);
|
|
61
|
+
/** The cached link while fresh, else null (never fetches). */
|
|
62
|
+
current(): PaymentLink | null;
|
|
63
|
+
/** A fresh link: the cached one, or one fetch shared by concurrent callers. */
|
|
64
|
+
get(): Promise<PaymentLink | null>;
|
|
65
|
+
/** Wait at most `ms` for a fetch already in flight; resolves at once when none is. */
|
|
66
|
+
settled(ms: number): Promise<void>;
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=payment-link.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payment-link.d.ts","sourceRoot":"","sources":["../src/payment-link.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,eAAO,MAAM,iBAAiB,qBAAqB,CAAC;AACpD,4EAA4E;AAC5E,eAAO,MAAM,8BAA8B,QAAgB,CAAC;AAC5D,qFAAqF;AACrF,eAAO,MAAM,qBAAqB,QAAiB,CAAC;AACpD,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAE5C,eAAO,MAAM,uBAAuB,qBAAqB,CAAC;AAC1D,eAAO,MAAM,iBAAiB,eAAe,CAAC;AAE9C,MAAM,MAAM,WAAW,GAAG;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,2CAA2C;IAC3C,eAAe,EAAE,MAAM,CAAC;IACxB,6EAA6E;IAC7E,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,2EAA2E;IAC3E,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAEjF,MAAM,MAAM,uBAAuB,GAAG;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB,CAAC;AAWF,0EAA0E;AAC1E,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,uBAAuB,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAejG;AAED,gFAAgF;AAChF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,GAAG,WAAW,GAAG,IAAI,CAelE;AAED,uFAAuF;AACvF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,SAAa,GAAG,OAAO,CAE7E;AAED,kDAAkD;AAClD,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED;;;;GAIG;AACH,qBAAa,gBAAgB;IAMzB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,GAAG;IANtB,OAAO,CAAC,IAAI,CAA4B;IACxC,OAAO,CAAC,QAAQ,CAA4C;IAC5D,OAAO,CAAC,QAAQ,CAAqB;gBAGlB,SAAS,EAAE,MAAM,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,EAC5C,GAAG,GAAE,MAAM,MAAiB;IAG/C,8DAA8D;IAC9D,OAAO,IAAI,WAAW,GAAG,IAAI;IAI7B,+EAA+E;IAC/E,GAAG,IAAI,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAgBlC,sFAAsF;IAChF,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CASzC"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Short checkout link for an unpaid MemTree key.
|
|
3
|
+
*
|
|
4
|
+
* POST /v1/payment/link (authenticated with the MemTree key) answers
|
|
5
|
+
* `{url: "https://app.polychat.co/p/Ab3xY9…", plan, monthly_price_usd, reason, expires_at}`.
|
|
6
|
+
* The link opens Stripe Checkout for the key's own account with no sign-in, so the
|
|
7
|
+
* wrong-account and signed-out detours of the old `/payment?user_id=…` link go away.
|
|
8
|
+
* It works for an hour; the cache below fetches a new one when the old one is about
|
|
9
|
+
* to expire.
|
|
10
|
+
*
|
|
11
|
+
* Servers without the endpoint (404), timeouts and odd bodies all mean "no link":
|
|
12
|
+
* callers fall back to the status probe's `/payment?…` link, exactly as before.
|
|
13
|
+
*/
|
|
14
|
+
export const PAYMENT_LINK_PATH = "/v1/payment/link";
|
|
15
|
+
/** A link this close to expiry is replaced before it is shown or opened. */
|
|
16
|
+
export const PAYMENT_LINK_REFRESH_MARGIN_MS = 5 * 60 * 1000;
|
|
17
|
+
/** After a failed fetch (old server, outage), wait this long before asking again. */
|
|
18
|
+
export const PAYMENT_LINK_RETRY_MS = 10 * 60 * 1000;
|
|
19
|
+
export const PAYMENT_LINK_TIMEOUT_MS = 1500;
|
|
20
|
+
export const REASON_FREE_CREDIT_USED = "free_credit_used";
|
|
21
|
+
export const REASON_PLAN_LIMIT = "plan_limit";
|
|
22
|
+
// The link must be our short path on one of our hosts: a hostile or broken body
|
|
23
|
+
// can't put an arbitrary URL (or terminal controls) on screen or into `open`.
|
|
24
|
+
const SHORT_LINK_RE = new RegExp("^(?:https:\\/\\/(?:(?:[a-z0-9-]+\\.)*polychat\\.co|polychat-[a-z0-9-]+(?:\\.[a-z0-9-]+)?\\.run\\.app)" +
|
|
25
|
+
"|http:\\/\\/(?:local\\.polychat\\.co|localhost|127\\.0\\.0\\.1)(?::\\d+)?)" +
|
|
26
|
+
"\\/p\\/[A-Za-z0-9]{12,64}$");
|
|
27
|
+
const PLAN_RE = /^[A-Za-z0-9 %.+-]{1,40}$/;
|
|
28
|
+
/** Ask the server for a short link; null on any failure. Never throws. */
|
|
29
|
+
export async function fetchPaymentLink(opts) {
|
|
30
|
+
try {
|
|
31
|
+
const res = await (opts.fetchImpl ?? fetch)(`${opts.baseUrl.replace(/\/$/, "")}${PAYMENT_LINK_PATH}`, {
|
|
32
|
+
method: "POST",
|
|
33
|
+
headers: { authorization: `Bearer ${opts.apiKey}`, ...opts.headers },
|
|
34
|
+
signal: AbortSignal.timeout(opts.timeoutMs ?? PAYMENT_LINK_TIMEOUT_MS),
|
|
35
|
+
});
|
|
36
|
+
if (!res.ok)
|
|
37
|
+
return null;
|
|
38
|
+
return parsePaymentLink(await res.json());
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/** Validate the endpoint's body; null unless it carries a usable short link. */
|
|
45
|
+
export function parsePaymentLink(body) {
|
|
46
|
+
if (typeof body !== "object" || body === null)
|
|
47
|
+
return null;
|
|
48
|
+
const record = body;
|
|
49
|
+
const { url, plan, monthly_price_usd: price } = record;
|
|
50
|
+
if (typeof url !== "string" || !SHORT_LINK_RE.test(url))
|
|
51
|
+
return null;
|
|
52
|
+
if (typeof plan !== "string" || !PLAN_RE.test(plan))
|
|
53
|
+
return null;
|
|
54
|
+
if (typeof price !== "number" || !Number.isFinite(price) || price <= 0 || price > 100_000) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
const expiresAt = typeof record.expires_at === "string" ? Date.parse(record.expires_at) : NaN;
|
|
58
|
+
const reason = record.reason === REASON_FREE_CREDIT_USED || record.reason === REASON_PLAN_LIMIT
|
|
59
|
+
? record.reason
|
|
60
|
+
: null;
|
|
61
|
+
return { url, plan, monthlyPriceUsd: price, reason,
|
|
62
|
+
expiresAt: Number.isFinite(expiresAt) ? expiresAt : null };
|
|
63
|
+
}
|
|
64
|
+
/** Whether `link` can still be shown or opened (not within the margin of expiring). */
|
|
65
|
+
export function paymentLinkFresh(link, now = Date.now()) {
|
|
66
|
+
return link.expiresAt === null || link.expiresAt - now > PAYMENT_LINK_REFRESH_MARGIN_MS;
|
|
67
|
+
}
|
|
68
|
+
/** "$5", "$7.50": whole dollars without cents. */
|
|
69
|
+
export function formatUsd(amount) {
|
|
70
|
+
return Number.isInteger(amount) ? `$${amount}` : `$${amount.toFixed(2)}`;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* One process's short link: fetched on demand, reused while fresh, refetched near
|
|
74
|
+
* expiry, and not asked for again for PAYMENT_LINK_RETRY_MS after a failure (so an
|
|
75
|
+
* old server's 404 costs one request, not one per 402).
|
|
76
|
+
*/
|
|
77
|
+
export class PaymentLinkCache {
|
|
78
|
+
fetchLink;
|
|
79
|
+
now;
|
|
80
|
+
link = null;
|
|
81
|
+
inFlight = null;
|
|
82
|
+
failedAt;
|
|
83
|
+
constructor(fetchLink, now = Date.now) {
|
|
84
|
+
this.fetchLink = fetchLink;
|
|
85
|
+
this.now = now;
|
|
86
|
+
}
|
|
87
|
+
/** The cached link while fresh, else null (never fetches). */
|
|
88
|
+
current() {
|
|
89
|
+
return this.link && paymentLinkFresh(this.link, this.now()) ? this.link : null;
|
|
90
|
+
}
|
|
91
|
+
/** A fresh link: the cached one, or one fetch shared by concurrent callers. */
|
|
92
|
+
get() {
|
|
93
|
+
const cached = this.current();
|
|
94
|
+
if (cached)
|
|
95
|
+
return Promise.resolve(cached);
|
|
96
|
+
if (this.inFlight)
|
|
97
|
+
return this.inFlight;
|
|
98
|
+
if (this.failedAt !== undefined && this.now() - this.failedAt < PAYMENT_LINK_RETRY_MS) {
|
|
99
|
+
return Promise.resolve(null);
|
|
100
|
+
}
|
|
101
|
+
this.inFlight = this.fetchLink().catch(() => null).then((link) => {
|
|
102
|
+
this.inFlight = null;
|
|
103
|
+
if (link)
|
|
104
|
+
this.link = link;
|
|
105
|
+
else
|
|
106
|
+
this.failedAt = this.now();
|
|
107
|
+
return link;
|
|
108
|
+
});
|
|
109
|
+
return this.inFlight;
|
|
110
|
+
}
|
|
111
|
+
/** Wait at most `ms` for a fetch already in flight; resolves at once when none is. */
|
|
112
|
+
async settled(ms) {
|
|
113
|
+
if (!this.inFlight)
|
|
114
|
+
return;
|
|
115
|
+
let timer;
|
|
116
|
+
await Promise.race([
|
|
117
|
+
this.inFlight,
|
|
118
|
+
new Promise((resolve) => { timer = setTimeout(resolve, ms); }),
|
|
119
|
+
]);
|
|
120
|
+
clearTimeout(timer);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=payment-link.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payment-link.js","sourceRoot":"","sources":["../src/payment-link.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postinstall.d.ts","sourceRoot":"","sources":["../src/postinstall.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postinstall.js","sourceRoot":"","sources":["../src/postinstall.ts"],"names":[],"mappings":"AAAA,uFAAuF;AACvF,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAEzC,IAAI,EAAE,CAAC"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Earlier MemTree sessions in this project, named to the model when a session
|
|
3
|
+
* starts, so earlier decisions and fixes are one lookup away.
|
|
4
|
+
*
|
|
5
|
+
* The server answers `GET /v1/memtree/project_trees?project=&limit=` (polychat
|
|
6
|
+
* routes/memtree_project_trees.py): the user's own newest sessions in this
|
|
7
|
+
* project that have a finished tree, each with its root summary and tree
|
|
8
|
+
* reference. ccc asks once at startup (before Claude Code is up) and again in
|
|
9
|
+
* the background at each later session start, so a SessionStart hook only
|
|
10
|
+
* waits briefly for an answer already on its way.
|
|
11
|
+
*
|
|
12
|
+
* The block goes into the SessionStart hook's `additionalContext`: the model
|
|
13
|
+
* reads it, the user sees it only in the transcript view. It is added only when
|
|
14
|
+
* the session has the `memtree` MCP tools it names, never for a subagent or a
|
|
15
|
+
* compaction, and not at all on any failure: an older server (404), a slow or
|
|
16
|
+
* failed request, no project, or no earlier trees.
|
|
17
|
+
*/
|
|
18
|
+
import type { KeyCheck } from "./key-status.js";
|
|
19
|
+
/** Trees named at most. */
|
|
20
|
+
export declare const PROJECT_TREES_LIMIT = 5;
|
|
21
|
+
/** Most a SessionStart hook waits for an answer still on its way. */
|
|
22
|
+
export declare const PROJECT_TREES_WAIT_MS = 300;
|
|
23
|
+
/** A request that has not answered by then is dropped. */
|
|
24
|
+
export declare const PROJECT_TREES_FETCH_TIMEOUT_MS = 5000;
|
|
25
|
+
/** A later session start reuses an answer this fresh instead of asking again. */
|
|
26
|
+
export declare const PROJECT_TREES_FRESH_MS = 60000;
|
|
27
|
+
/** Root summaries are cut to this many characters. */
|
|
28
|
+
export declare const PROJECT_TREES_SUMMARY_CHARS = 150;
|
|
29
|
+
export declare const PROJECT_TREES_HEADER: string;
|
|
30
|
+
export declare const PROJECT_TREES_FOOTER: string;
|
|
31
|
+
export interface ProjectTree {
|
|
32
|
+
session_id?: string | null;
|
|
33
|
+
last_at?: string | null;
|
|
34
|
+
title: string;
|
|
35
|
+
ref: string;
|
|
36
|
+
}
|
|
37
|
+
/** GET a polychat path with the user's key (MemtreeClient.fetchMemTree). */
|
|
38
|
+
export type ProjectTreesFetch = (pathAndQuery: string, signal: AbortSignal) => Promise<{
|
|
39
|
+
status: number;
|
|
40
|
+
body: Buffer;
|
|
41
|
+
}>;
|
|
42
|
+
export declare class ProjectTreesHint {
|
|
43
|
+
private readonly fetchJson;
|
|
44
|
+
private readonly project;
|
|
45
|
+
private readonly waitMs;
|
|
46
|
+
private readonly now;
|
|
47
|
+
private pending;
|
|
48
|
+
private latest;
|
|
49
|
+
private disabled;
|
|
50
|
+
private fetchedAt;
|
|
51
|
+
/** The server answered 404/405: it has no such endpoint; not asked again this run. */
|
|
52
|
+
private unsupported;
|
|
53
|
+
constructor(fetchJson: ProjectTreesFetch, project: string, waitMs?: number, now?: () => number);
|
|
54
|
+
/** No more blocks: the session lost the tools they name (memtree MCP config failed). */
|
|
55
|
+
disable(): void;
|
|
56
|
+
/** Start a lookup unless one is on its way; never throws. */
|
|
57
|
+
prefetch(): void;
|
|
58
|
+
/**
|
|
59
|
+
* The block for a session starting now, or undefined. Waits at most
|
|
60
|
+
* `waitMs` for a lookup on its way, then refreshes in the background for
|
|
61
|
+
* the next session start (an in-app `/clear` or `/resume`).
|
|
62
|
+
*/
|
|
63
|
+
contextFor(sessionId: string | undefined): Promise<string | undefined>;
|
|
64
|
+
private lookup;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The project name the server matches: the `owner/repo` of the `origin`
|
|
68
|
+
* remote. A directory name alone ("app", "src") would also match unrelated
|
|
69
|
+
* directories and repos of that name, so without a remote there is no hint.
|
|
70
|
+
*/
|
|
71
|
+
export declare function projectIdentity(meta: {
|
|
72
|
+
git_repo?: string;
|
|
73
|
+
} | undefined): string | undefined;
|
|
74
|
+
/** The server's `trees`, keeping only well-formed entries. */
|
|
75
|
+
export declare function parseProjectTrees(body: unknown): ProjectTree[] | undefined;
|
|
76
|
+
/** The context block, or undefined when no tree but the current session's is left. */
|
|
77
|
+
export declare function formatProjectTrees(trees: readonly ProjectTree[], currentSessionId: string | undefined): string | undefined;
|
|
78
|
+
/** What startup knows when it decides on the hint (cli-main.ts). */
|
|
79
|
+
export interface ProjectTreesStartup {
|
|
80
|
+
interactive: boolean;
|
|
81
|
+
/** The startup key check: "invalid" (401/403, even after [c]) means no MemTree calls. */
|
|
82
|
+
keyCheck: KeyCheck;
|
|
83
|
+
/** Unpaid and [c] at the payment gate (or a print run): MemTree is off this session. */
|
|
84
|
+
memtreeOff: boolean;
|
|
85
|
+
tools: readonly string[];
|
|
86
|
+
meta: {
|
|
87
|
+
git_repo?: string;
|
|
88
|
+
} | undefined;
|
|
89
|
+
env: NodeJS.ProcessEnv;
|
|
90
|
+
fetchJson: ProjectTreesFetch;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* The hint for this run, or undefined: interactive sessions only (print runs
|
|
94
|
+
* keep Claude Code's output as it is) with MemTree on (a valid key, not
|
|
95
|
+
* continued past the payment gate: the lookup could only fail), with both
|
|
96
|
+
* `search` and `read_node` (the block names them), a known project, and not
|
|
97
|
+
* `CCC_PROJECT_TREES=0`.
|
|
98
|
+
*/
|
|
99
|
+
export declare function projectTreesHintFor(s: ProjectTreesStartup): ProjectTreesHint | undefined;
|
|
100
|
+
//# sourceMappingURL=project-trees.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-trees.d.ts","sourceRoot":"","sources":["../src/project-trees.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAEhD,2BAA2B;AAC3B,eAAO,MAAM,mBAAmB,IAAI,CAAC;AACrC,qEAAqE;AACrE,eAAO,MAAM,qBAAqB,MAAM,CAAC;AACzC,0DAA0D;AAC1D,eAAO,MAAM,8BAA8B,OAAQ,CAAC;AACpD,iFAAiF;AACjF,eAAO,MAAM,sBAAsB,QAAS,CAAC;AAC7C,sDAAsD;AACtD,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAE/C,eAAO,MAAM,oBAAoB,QAGqB,CAAC;AACvD,eAAO,MAAM,oBAAoB,QAEL,CAAC;AAI7B,MAAM,WAAW,WAAW;IAC1B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED,4EAA4E;AAC5E,MAAM,MAAM,iBAAiB,GAAG,CAC9B,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,WAAW,KAChB,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAE/C,qBAAa,gBAAgB;IASzB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,GAAG;IAXtB,OAAO,CAAC,OAAO,CAAiD;IAChE,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,SAAS,CAAa;IAC9B,sFAAsF;IACtF,OAAO,CAAC,WAAW,CAAS;gBAGT,SAAS,EAAE,iBAAiB,EAC5B,OAAO,EAAE,MAAM,EACf,MAAM,SAAwB,EAC9B,GAAG,GAAE,MAAM,MAAiB;IAG/C,wFAAwF;IACxF,OAAO,IAAI,IAAI;IAIf,6DAA6D;IAC7D,QAAQ,IAAI,IAAI;IAgBhB;;;;OAIG;IACG,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;YAS9D,MAAM;CAkBrB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAE3F;AAED,8DAA8D;AAC9D,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,GAAG,WAAW,EAAE,GAAG,SAAS,CAS1E;AAED,sFAAsF;AACtF,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,SAAS,WAAW,EAAE,EAC7B,gBAAgB,EAAE,MAAM,GAAG,SAAS,GACnC,MAAM,GAAG,SAAS,CAWpB;AAqCD,oEAAoE;AACpE,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,OAAO,CAAC;IACrB,yFAAyF;IACzF,QAAQ,EAAE,QAAQ,CAAC;IACnB,wFAAwF;IACxF,UAAU,EAAE,OAAO,CAAC;IACpB,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,IAAI,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;IACxC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,SAAS,EAAE,iBAAiB,CAAC;CAC9B;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,mBAAmB,GAAG,gBAAgB,GAAG,SAAS,CAMxF"}
|