@temporalabs/treasury 0.1.1 → 0.1.2

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.
Files changed (74) hide show
  1. package/NOTICE +4 -2
  2. package/README.md +68 -44
  3. package/THIRD_PARTY_NOTICES.connect-page.md +7420 -0
  4. package/THIRD_PARTY_NOTICES.md +4 -196
  5. package/dist/cli.d.ts +6 -0
  6. package/dist/cli.js +220 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/client.d.ts +224 -239
  9. package/dist/client.js +147 -25
  10. package/dist/client.js.map +1 -1
  11. package/dist/config/earn.d.ts +33 -12
  12. package/dist/config/earn.js +33 -12
  13. package/dist/config/earn.js.map +1 -1
  14. package/dist/connect/ack.d.ts +59 -0
  15. package/dist/connect/ack.js +111 -0
  16. package/dist/connect/ack.js.map +1 -0
  17. package/dist/connect/commands.d.ts +78 -0
  18. package/dist/connect/commands.js +411 -0
  19. package/dist/connect/commands.js.map +1 -0
  20. package/dist/connect/config.d.ts +22 -0
  21. package/dist/connect/config.js +33 -0
  22. package/dist/connect/config.js.map +1 -0
  23. package/dist/connect/gate.d.ts +37 -0
  24. package/dist/connect/gate.js +115 -0
  25. package/dist/connect/gate.js.map +1 -0
  26. package/dist/connect/http.d.ts +54 -0
  27. package/dist/connect/http.js +205 -0
  28. package/dist/connect/http.js.map +1 -0
  29. package/dist/connect/open.d.ts +2 -0
  30. package/dist/connect/open.js +35 -0
  31. package/dist/connect/open.js.map +1 -0
  32. package/dist/connect/page.d.ts +5 -0
  33. package/dist/connect/page.js +69 -0
  34. package/dist/connect/page.js.map +1 -0
  35. package/dist/connect/session.d.ts +16 -0
  36. package/dist/connect/session.js +73 -0
  37. package/dist/connect/session.js.map +1 -0
  38. package/dist/connect/verify.d.ts +37 -0
  39. package/dist/connect/verify.js +89 -0
  40. package/dist/connect/verify.js.map +1 -0
  41. package/dist/connect-page.js +5141 -0
  42. package/dist/disclosures.d.ts +21 -6
  43. package/dist/disclosures.js +32 -7
  44. package/dist/disclosures.js.map +1 -1
  45. package/dist/earn/commands.d.ts +46 -0
  46. package/dist/earn/commands.js +483 -0
  47. package/dist/earn/commands.js.map +1 -0
  48. package/dist/links.d.ts +2 -0
  49. package/dist/links.js +38 -14
  50. package/dist/links.js.map +1 -1
  51. package/dist/position.d.ts +7 -5
  52. package/dist/position.js +35 -18
  53. package/dist/position.js.map +1 -1
  54. package/dist/preflight.d.ts +9 -0
  55. package/dist/preflight.js +14 -3
  56. package/dist/preflight.js.map +1 -1
  57. package/dist/quote.js +24 -7
  58. package/dist/quote.js.map +1 -1
  59. package/dist/redact.d.ts +3 -3
  60. package/dist/redact.js +3 -3
  61. package/dist/redact.js.map +1 -1
  62. package/dist/registry-schema.d.ts +5 -5
  63. package/dist/registry-schema.js +30 -16
  64. package/dist/registry-schema.js.map +1 -1
  65. package/dist/registry.d.ts +29 -6
  66. package/dist/registry.js +76 -13
  67. package/dist/registry.js.map +1 -1
  68. package/dist/{mcp-server.mjs → treasury.mjs} +29276 -44702
  69. package/dist/version.js +1 -1
  70. package/package.json +5 -5
  71. package/registry/vaults.json +105 -4
  72. package/dist/mcp/server.d.ts +0 -8
  73. package/dist/mcp/server.js +0 -368
  74. package/dist/mcp/server.js.map +0 -1
@@ -0,0 +1,115 @@
1
+ /**
2
+ * The hard gate every call passes before a page opens: what `treasury connect` will put in front of
3
+ * the operator's wallet at all. It re-derives everything from the calldata and the registry and
4
+ * trusts nothing the call's own fields say about themselves (`description`, `args`, `function`).
5
+ *
6
+ * Allowed, and nothing else:
7
+ * - `approve(vault, n)` on a registry vault's own asset, for exactly the amount the deposit after
8
+ * it pulls — never unlimited, never to anything but that vault;
9
+ * - `deposit(assets, receiver)` on a registry vault, receiver = the connected account;
10
+ * - `withdraw(assets, receiver, owner)` / `redeem(shares, receiver, owner)` on a registry vault,
11
+ * receiver = owner = the connected account.
12
+ * Every call carries no value and names a supported chain that matches its vault.
13
+ *
14
+ * And one batch is one operation on one vault, on one chain, in one of the shapes the builders
15
+ * emit: `[approve, deposit]`, `[withdraw]` or `[redeem]`. Calldata must be exactly the canonical
16
+ * encoding of what it decodes to: no trailing bytes, no stray bits in an address word.
17
+ *
18
+ * This is the rule of AGENTS.md / CONTRIBUTING.md that the relay refuses any destination outside
19
+ * the registry, made executable. Paying an address other than the
20
+ * connected account is refused here on purpose: that is what the prepare commands are for, with the
21
+ * operator's own signer.
22
+ */
23
+ import { decodeFunctionData, encodeFunctionData, getAddress } from "viem";
24
+ import { erc4626Abi } from "../abi/erc4626.js";
25
+ import { isSupportedChainId } from "../client.js";
26
+ import { listVaults } from "../registry.js";
27
+ /** Anything at or above this is an "unlimited" approval, which the gate never admits. */
28
+ const UNLIMITED = 2n ** 128n;
29
+ const same = (a, b) => getAddress(a) === getAddress(b);
30
+ /** The only batches the builders emit; anything else is refused whole. */
31
+ const SHAPES = new Set(["approve,deposit", "withdraw", "redeem"]);
32
+ export function admit(calls, account) {
33
+ if (calls.length === 0)
34
+ throw new Error("refused: there is nothing to confirm");
35
+ // The page switches the wallet to one chain for the whole batch, so a batch never spans two.
36
+ if (calls.some((c) => c.chainId !== calls[0].chainId))
37
+ throw new Error("refused: the calls are on more than one chain");
38
+ const vaults = listVaults();
39
+ const out = [];
40
+ calls.forEach((c, i) => {
41
+ const at = `call ${i + 1} of ${calls.length}`;
42
+ if (!isSupportedChainId(c.chainId))
43
+ throw new Error(`refused (${at}): chain ${c.chainId} is not supported`);
44
+ if (BigInt(c.value) !== 0n)
45
+ throw new Error(`refused (${at}): it attaches native value, which no vault call needs`);
46
+ let decoded;
47
+ try {
48
+ decoded = decodeFunctionData({ abi: erc4626Abi, data: c.data });
49
+ }
50
+ catch {
51
+ throw new Error(`refused (${at}): its calldata is not an approve, deposit, withdraw or redeem`);
52
+ }
53
+ const name = decoded.functionName;
54
+ const args = (decoded.args ?? []);
55
+ // Decoding forgives trailing bytes and keeps only the low 20 bytes of an address word; the
56
+ // wallet would sign the bytes as sent. Admit only calldata that is its own canonical encoding.
57
+ const canonical = encodeFunctionData({ abi: erc4626Abi, functionName: name, args });
58
+ if (canonical !== c.data)
59
+ throw new Error(`refused (${at}): its calldata is not the canonical encoding of ${name} (trailing bytes, stray bits in an address, or hex not in lowercase)`);
60
+ if (name === "approve") {
61
+ const vault = vaults.find((v) => v.chainId === c.chainId && same(v.asset.address, c.to));
62
+ if (!vault)
63
+ throw new Error(`refused (${at}): approve on ${c.to}, which is not the asset of a listed vault on chain ${c.chainId}`);
64
+ const [spender, value] = args;
65
+ const target = vaults.find((v) => v.chainId === c.chainId && same(v.address, spender) && same(v.asset.address, c.to));
66
+ if (!target)
67
+ throw new Error(`refused (${at}): the approval goes to ${spender}, not to a listed vault for this asset`);
68
+ if (value === 0n)
69
+ throw new Error(`refused (${at}): an approval of 0`);
70
+ if (value >= UNLIMITED)
71
+ throw new Error(`refused (${at}): an unlimited approval`);
72
+ out.push({ kind: "approve", vault: target, amount: value });
73
+ return;
74
+ }
75
+ const vault = vaults.find((v) => v.chainId === c.chainId && same(v.address, c.to));
76
+ if (!vault)
77
+ throw new Error(`refused (${at}): ${c.to} is not a listed vault on chain ${c.chainId}`);
78
+ if (name === "deposit") {
79
+ const [assets, receiver] = args;
80
+ if (assets === 0n)
81
+ throw new Error(`refused (${at}): a deposit of 0`);
82
+ if (!same(receiver, account))
83
+ throw new Error(`refused (${at}): the shares would go to ${receiver}, not the connected account ${account}`);
84
+ out.push({ kind: "deposit", vault, amount: assets });
85
+ return;
86
+ }
87
+ if (name === "withdraw" || name === "redeem") {
88
+ const [amount, receiver, owner] = args;
89
+ if (amount === 0n)
90
+ throw new Error(`refused (${at}): a ${name} of 0`);
91
+ if (!same(receiver, account))
92
+ throw new Error(`refused (${at}): the funds would go to ${receiver}, not the connected account ${account}`);
93
+ if (!same(owner, account))
94
+ throw new Error(`refused (${at}): it burns the shares of ${owner}, not the connected account ${account}`);
95
+ out.push({ kind: name, vault, amount });
96
+ return;
97
+ }
98
+ throw new Error(`refused (${at}): ${name} is not something this page hands to a wallet`);
99
+ });
100
+ // An approval only ever precedes the deposit it pays for: same vault, same amount, next call.
101
+ out.forEach((a, i) => {
102
+ if (a.kind !== "approve")
103
+ return;
104
+ const next = out[i + 1];
105
+ if (!next || next.kind !== "deposit" || next.vault.address !== a.vault.address || next.amount !== a.amount) {
106
+ throw new Error(`refused (call ${i + 1}): an approval must be followed by a deposit of exactly that amount into the same vault`);
107
+ }
108
+ });
109
+ const shape = out.map((a) => a.kind).join(",");
110
+ // With the pairing above, these shapes also mean one vault per batch.
111
+ if (!SHAPES.has(shape))
112
+ throw new Error(`refused: ${shape} is not a batch this page confirms (only approve+deposit, withdraw, or redeem)`);
113
+ return out;
114
+ }
115
+ //# sourceMappingURL=gate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gate.js","sourceRoot":"","sources":["../../src/connect/gate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ export type Accepted<T> = {
2
+ ok: true;
3
+ final: true;
4
+ value: T;
5
+ reply?: Record<string, unknown>;
6
+ } | {
7
+ ok: true;
8
+ final: false;
9
+ reply?: Record<string, unknown>;
10
+ } | {
11
+ ok: false;
12
+ reason: string;
13
+ fatal?: boolean;
14
+ };
15
+ export type Settled<T> = {
16
+ ok: true;
17
+ value: T;
18
+ } | {
19
+ ok: false;
20
+ reason: string;
21
+ };
22
+ export interface Flow<T> {
23
+ mode: "connect" | "confirm";
24
+ ttlMs: number;
25
+ /** The page served at `/<mode>`, as a function of the flow's secret. */
26
+ html: (secret: string) => string;
27
+ csp: string;
28
+ /** Static assets served at their path. */
29
+ assets: Record<string, {
30
+ type: string;
31
+ body: string | Buffer;
32
+ }>;
33
+ info: () => Record<string, unknown>;
34
+ challenge?: (address: string, origin: string, nonce: string) => string;
35
+ accept: (body: Record<string, unknown>, ctx: {
36
+ nonce: string;
37
+ origin: string;
38
+ message?: string;
39
+ challengedAddress?: string;
40
+ }) => Promise<Accepted<T>>;
41
+ /** Called with the page's body when it reports a rejection, before the flow ends. */
42
+ onReject?: (body: Record<string, unknown>) => void;
43
+ /** Why the flow ended when the time limit ran out; the default says nothing happened. */
44
+ timeoutReason?: () => string;
45
+ /** Overrides the cancel grace period (tests). */
46
+ cancelGraceMs?: number;
47
+ port: number;
48
+ }
49
+ export interface Handle<T> {
50
+ url: string;
51
+ done: Promise<Settled<T>>;
52
+ close: () => void;
53
+ }
54
+ export declare function serveOnce<T>(flow: Flow<T>): Promise<Handle<T>>;
@@ -0,0 +1,205 @@
1
+ /**
2
+ * A one-shot localhost server: it exists for one sign-in or one confirmation, answers only requests
3
+ * that carry its random secret, its own Host and (for POSTs) its own Origin, and closes itself once
4
+ * the flow settles or times out.
5
+ *
6
+ * The page talks back by POSTing to `/challenge` (sign-in only) and `/result`. A `/result` handler
7
+ * may answer "not yet" (`final: false`) to keep the flow open — the confirm flow uses that to take
8
+ * one transaction at a time — and anything it refuses with `fatal` ends the flow at once.
9
+ */
10
+ import http from "node:http";
11
+ import { randomBytes, timingSafeEqual } from "node:crypto";
12
+ import { getAddress, isAddress } from "viem";
13
+ const MAX_BODY_BYTES = 16 * 1024;
14
+ /** How long a cancel waits for a result already being checked, so it can carry that verdict. */
15
+ const CANCEL_GRACE_MS = 30_000;
16
+ const same = (a, b) => {
17
+ const x = Buffer.from(a);
18
+ const y = Buffer.from(b);
19
+ return x.length === y.length && timingSafeEqual(x, y);
20
+ };
21
+ export async function serveOnce(flow) {
22
+ const secret = randomBytes(24).toString("hex");
23
+ // Privy matches allowed origins by exact string, and `localhost` is the host it documents.
24
+ const origin = `http://localhost:${flow.port}`;
25
+ let settled = false;
26
+ let busy = false;
27
+ /** A cancel or timeout that arrived while a result was being checked; applied once the check ends. */
28
+ let deferred;
29
+ const expiresAt = Date.now() + flow.ttlMs;
30
+ let resolveDone;
31
+ const done = new Promise((r) => (resolveDone = r));
32
+ let issued;
33
+ const json = (res, code, body) => {
34
+ res.writeHead(code, { "content-type": "application/json", "cache-control": "no-store", "x-content-type-options": "nosniff" });
35
+ res.end(JSON.stringify(body));
36
+ };
37
+ const readBody = (req) => new Promise((resolve, reject) => {
38
+ let size = 0;
39
+ const chunks = [];
40
+ req.on("data", (c) => {
41
+ size += c.length;
42
+ if (size > MAX_BODY_BYTES) {
43
+ reject(new Error("body too large"));
44
+ req.destroy();
45
+ return;
46
+ }
47
+ chunks.push(c);
48
+ });
49
+ req.on("end", () => {
50
+ try {
51
+ const v = JSON.parse(Buffer.concat(chunks).toString("utf8"));
52
+ if (v === null || typeof v !== "object" || Array.isArray(v))
53
+ throw new Error("not an object");
54
+ resolve(v);
55
+ }
56
+ catch {
57
+ reject(new Error("body is not a JSON object"));
58
+ }
59
+ });
60
+ req.on("error", reject);
61
+ });
62
+ const onRequest = (req, res) => {
63
+ void (async () => {
64
+ try {
65
+ if (String(req.headers.host ?? "") !== new URL(origin).host)
66
+ return json(res, 403, { ok: false, reason: "bad host" });
67
+ const url = new URL(req.url ?? "/", origin);
68
+ if (req.method === "GET") {
69
+ if (!same(url.searchParams.get("s") ?? "", secret))
70
+ return json(res, 404, { ok: false, reason: "not found" });
71
+ if (url.pathname === "/info") {
72
+ if (settled)
73
+ return json(res, 410, { ok: false, reason: "already finished" });
74
+ return json(res, 200, { ...flow.info(), msLeft: Math.max(0, expiresAt - Date.now()) });
75
+ }
76
+ if (url.pathname === `/${flow.mode}`) {
77
+ res.writeHead(200, {
78
+ "content-type": "text/html; charset=utf-8",
79
+ "cache-control": "no-store",
80
+ "referrer-policy": "no-referrer",
81
+ "content-security-policy": flow.csp,
82
+ "x-content-type-options": "nosniff",
83
+ });
84
+ return res.end(flow.html(secret));
85
+ }
86
+ const asset = flow.assets[url.pathname];
87
+ if (asset) {
88
+ res.writeHead(200, { "content-type": asset.type, "cache-control": "no-store", "x-content-type-options": "nosniff" });
89
+ return res.end(asset.body);
90
+ }
91
+ return json(res, 404, { ok: false, reason: "not found" });
92
+ }
93
+ if (req.method === "POST" && (url.pathname === "/challenge" || url.pathname === "/result")) {
94
+ if (req.headers.origin !== origin)
95
+ return json(res, 403, { ok: false, reason: "bad origin" });
96
+ if (!String(req.headers["content-type"] ?? "").startsWith("application/json"))
97
+ return json(res, 415, { ok: false, reason: "expected JSON" });
98
+ if (settled)
99
+ return json(res, 409, { ok: false, reason: "already finished" });
100
+ const body = await readBody(req);
101
+ if (typeof body["s"] !== "string" || !same(body["s"], secret))
102
+ return json(res, 404, { ok: false, reason: "not found" });
103
+ if (url.pathname === "/challenge") {
104
+ if (!flow.challenge)
105
+ return json(res, 404, { ok: false, reason: "not found" });
106
+ const a = body["address"];
107
+ if (typeof a !== "string" || !isAddress(a))
108
+ return json(res, 400, { ok: false, reason: "not an address" });
109
+ const address = getAddress(a);
110
+ const message = flow.challenge(address, origin, secret);
111
+ issued = { address, message };
112
+ return json(res, 200, { ok: true, message });
113
+ }
114
+ if (body["rejected"] === true) {
115
+ const reason = typeof body["reason"] === "string" ? body["reason"].slice(0, 300) : "declined in the browser";
116
+ flow.onReject?.(body);
117
+ finishWhenIdle({ ok: false, reason });
118
+ return json(res, 200, { ok: true });
119
+ }
120
+ // One result at a time: a double click must never hand the same call over twice.
121
+ if (busy)
122
+ return json(res, 409, { ok: false, reason: "already in progress" });
123
+ busy = true;
124
+ try {
125
+ const r = await flow.accept(body, { nonce: secret, origin, ...(issued ? { message: issued.message, challengedAddress: issued.address } : {}) });
126
+ if (!r.ok) {
127
+ if (r.fatal)
128
+ finish({ ok: false, reason: r.reason });
129
+ return json(res, 400, { ok: false, reason: r.reason });
130
+ }
131
+ if (r.final)
132
+ finish({ ok: true, value: r.value });
133
+ return json(res, 200, { ok: true, ...r.reply });
134
+ }
135
+ finally {
136
+ busy = false;
137
+ if (deferred)
138
+ finish(deferred);
139
+ }
140
+ }
141
+ return json(res, 405, { ok: false, reason: "method not allowed" });
142
+ }
143
+ catch (e) {
144
+ return json(res, 400, { ok: false, reason: e instanceof Error ? e.message : "bad request" });
145
+ }
146
+ })();
147
+ };
148
+ // `localhost` can resolve to either loopback address, so both are held: a browser that tries
149
+ // `::1` first must reach this server, never another process waiting on that address.
150
+ const servers = [http.createServer(onRequest), http.createServer(onRequest)];
151
+ const closeAll = () => {
152
+ for (const s of servers) {
153
+ s.close();
154
+ s.closeAllConnections();
155
+ }
156
+ };
157
+ const finish = (r) => {
158
+ if (settled)
159
+ return;
160
+ settled = true;
161
+ resolveDone(r);
162
+ // A short grace period lets the page receive its last answer before the socket closes.
163
+ setTimeout(closeAll, 1500).unref();
164
+ };
165
+ /**
166
+ * A cancel that arrives while a result is being checked waits for that check, so the command can
167
+ * report its verdict — but never longer than the grace period. The time limit does not wait: the
168
+ * transaction is already on record (`unverified`), and the command must return inside it.
169
+ */
170
+ const finishWhenIdle = (r) => {
171
+ if (!busy)
172
+ return finish(r);
173
+ if (deferred)
174
+ return;
175
+ deferred = r;
176
+ setTimeout(() => finish(r), flow.cancelGraceMs ?? CANCEL_GRACE_MS).unref();
177
+ };
178
+ const listen = (server, host, optional) => new Promise((resolve, reject) => {
179
+ server.once("error", (e) => {
180
+ if (e.code === "EADDRINUSE") {
181
+ return reject(new Error(`port ${flow.port} is in use on ${host} — another sign-in or confirmation page is probably still open; finish or close it, then retry`));
182
+ }
183
+ // A machine with IPv6 switched off has no `::1` for anyone to wait on.
184
+ if (optional && (e.code === "EADDRNOTAVAIL" || e.code === "EAFNOSUPPORT"))
185
+ return resolve();
186
+ reject(e);
187
+ });
188
+ server.listen({ port: flow.port, host, ipv6Only: host === "::1" }, () => resolve());
189
+ });
190
+ try {
191
+ await listen(servers[0], "127.0.0.1", false);
192
+ await listen(servers[1], "::1", true);
193
+ }
194
+ catch (e) {
195
+ closeAll();
196
+ throw e;
197
+ }
198
+ const timer = setTimeout(
199
+ // A cancel still waiting on a check is the operator's reason; it wins over the clock.
200
+ () => finish(deferred ?? { ok: false, reason: flow.timeoutReason?.() ?? `nothing happened in the browser within ${Math.round(flow.ttlMs / 60_000)} minutes` }), flow.ttlMs);
201
+ timer.unref();
202
+ void done.then(() => clearTimeout(timer));
203
+ return { url: `${origin}/${flow.mode}?s=${secret}`, done, close: () => finish({ ok: false, reason: "cancelled" }) };
204
+ }
205
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/connect/http.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AAE7C,MAAM,cAAc,GAAG,EAAE,GAAG,IAAI,CAAC;AACjC,gGAAgG;AAChG,MAAM,eAAe,GAAG,MAAM,CAAC;AAkC/B,MAAM,IAAI,GAAG,CAAC,CAAS,EAAE,CAAS,EAAE,EAAE;IACpC,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACzB,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACzB,OAAO,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,IAAI,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AACxD,CAAC,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,SAAS,CAAI,IAAa;IAC9C,MAAM,MAAM,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC/C,2FAA2F;IAC3F,MAAM,MAAM,GAAG,oBAAoB,IAAI,CAAC,IAAI,EAAE,CAAC;IAC/C,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,IAAI,GAAG,KAAK,CAAC;IACjB,sGAAsG;IACtG,IAAI,QAAgC,CAAC;IACrC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;IAC1C,IAAI,WAAqC,CAAC;IAC1C,MAAM,IAAI,GAAG,IAAI,OAAO,CAAa,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC;IAC/D,IAAI,MAAwD,CAAC;IAE7D,MAAM,IAAI,GAAG,CAAC,GAAwB,EAAE,IAAY,EAAE,IAAa,EAAE,EAAE;QACrE,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,eAAe,EAAE,UAAU,EAAE,wBAAwB,EAAE,SAAS,EAAE,CAAC,CAAC;QAC9H,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;IAChC,CAAC,CAAC;IAEF,MAAM,QAAQ,GAAG,CAAC,GAAyB,EAAE,EAAE,CAC7C,IAAI,OAAO,CAA0B,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACvD,IAAI,IAAI,GAAG,CAAC,CAAC;QACb,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAS,EAAE,EAAE;YAC3B,IAAI,IAAI,CAAC,CAAC,MAAM,CAAC;YACjB,IAAI,IAAI,GAAG,cAAc,EAAE,CAAC;gBAC1B,MAAM,CAAC,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC;gBACpC,GAAG,CAAC,OAAO,EAAE,CAAC;gBACd,OAAO;YACT,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjB,CAAC,CAAC,CAAC;QACH,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;YACjB,IAAI,CAAC;gBACH,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAY,CAAC;gBACxE,IAAI,CAAC,KAAK,IAAI,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;oBAAE,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;gBAC9F,OAAO,CAAC,CAA4B,CAAC,CAAC;YACxC,CAAC;YAAC,MAAM,CAAC;gBACP,MAAM,CAAC,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC,CAAC;YACjD,CAAC;QACH,CAAC,CAAC,CAAC;QACH,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;IAEL,MAAM,SAAS,GAAG,CAAC,GAAyB,EAAE,GAAwB,EAAE,EAAE;QACxE,KAAK,CAAC,KAAK,IAAI,EAAE;YACf,IAAI,CAAC;gBACH,IAAI,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC,KAAK,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI;oBAAE,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;gBACtH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,EAAE,MAAM,CAAC,CAAC;gBAE5C,IAAI,GAAG,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;oBACzB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,MAAM,CAAC;wBAAE,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;oBAC9G,IAAI,GAAG,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;wBAC7B,IAAI,OAAO;4BAAE,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC,CAAC;wBAC9E,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;oBACzF,CAAC;oBACD,IAAI,GAAG,CAAC,QAAQ,KAAK,IAAI,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;wBACrC,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE;4BACjB,cAAc,EAAE,0BAA0B;4BAC1C,eAAe,EAAE,UAAU;4BAC3B,iBAAiB,EAAE,aAAa;4BAChC,yBAAyB,EAAE,IAAI,CAAC,GAAG;4BACnC,wBAAwB,EAAE,SAAS;yBACpC,CAAC,CAAC;wBACH,OAAO,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;oBACpC,CAAC;oBACD,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;oBACxC,IAAI,KAAK,EAAE,CAAC;wBACV,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,KAAK,CAAC,IAAI,EAAE,eAAe,EAAE,UAAU,EAAE,wBAAwB,EAAE,SAAS,EAAE,CAAC,CAAC;wBACrH,OAAO,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;oBAC7B,CAAC;oBACD,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;gBAC5D,CAAC;gBAED,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,IAAI,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,IAAI,GAAG,CAAC,QAAQ,KAAK,SAAS,CAAC,EAAE,CAAC;oBAC3F,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,MAAM;wBAAE,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC,CAAC;oBAC9F,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC,CAAC,UAAU,CAAC,kBAAkB,CAAC;wBAAE,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC,CAAC;oBAC7I,IAAI,OAAO;wBAAE,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC,CAAC;oBAC9E,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,CAAC;oBACjC,IAAI,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,QAAQ,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;wBAAE,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;oBAEzH,IAAI,GAAG,CAAC,QAAQ,KAAK,YAAY,EAAE,CAAC;wBAClC,IAAI,CAAC,IAAI,CAAC,SAAS;4BAAE,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;wBAC/E,MAAM,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC;wBAC1B,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;4BAAE,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAC;wBAC3G,MAAM,OAAO,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;wBAC9B,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;wBACxD,MAAM,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;wBAC9B,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;oBAC/C,CAAC;oBAED,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,IAAI,EAAE,CAAC;wBAC9B,MAAM,MAAM,GAAG,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,yBAAyB,CAAC;wBAC7G,IAAI,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,CAAC;wBACtB,cAAc,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;wBACtC,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;oBACtC,CAAC;oBACD,iFAAiF;oBACjF,IAAI,IAAI;wBAAE,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,qBAAqB,EAAE,CAAC,CAAC;oBAC9E,IAAI,GAAG,IAAI,CAAC;oBACZ,IAAI,CAAC;wBACH,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,iBAAiB,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;wBAChJ,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;4BACV,IAAI,CAAC,CAAC,KAAK;gCAAE,MAAM,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;4BACrD,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;wBACzD,CAAC;wBACD,IAAI,CAAC,CAAC,KAAK;4BAAE,MAAM,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;wBAClD,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;oBAClD,CAAC;4BAAS,CAAC;wBACT,IAAI,GAAG,KAAK,CAAC;wBACb,IAAI,QAAQ;4BAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;oBACjC,CAAC;gBACH,CAAC;gBACD,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,oBAAoB,EAAE,CAAC,CAAC;YACrE,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC,CAAC;YAC/F,CAAC;QACH,CAAC,CAAC,EAAE,CAAC;IACP,CAAC,CAAC;IACF,6FAA6F;IAC7F,qFAAqF;IACrF,MAAM,OAAO,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,CAAU,CAAC;IACtF,MAAM,QAAQ,GAAG,GAAG,EAAE;QACpB,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YACxB,CAAC,CAAC,KAAK,EAAE,CAAC;YACV,CAAC,CAAC,mBAAmB,EAAE,CAAC;QAC1B,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,MAAM,GAAG,CAAC,CAAa,EAAE,EAAE;QAC/B,IAAI,OAAO;YAAE,OAAO;QACpB,OAAO,GAAG,IAAI,CAAC;QACf,WAAW,CAAC,CAAC,CAAC,CAAC;QACf,uFAAuF;QACvF,UAAU,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;IACrC,CAAC,CAAC;IACF;;;;OAIG;IACH,MAAM,cAAc,GAAG,CAAC,CAAa,EAAE,EAAE;QACvC,IAAI,CAAC,IAAI;YAAE,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;QAC5B,IAAI,QAAQ;YAAE,OAAO;QACrB,QAAQ,GAAG,CAAC,CAAC;QACb,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,aAAa,IAAI,eAAe,CAAC,CAAC,KAAK,EAAE,CAAC;IAC7E,CAAC,CAAC;IAEF,MAAM,MAAM,GAAG,CAAC,MAAmB,EAAE,IAAY,EAAE,QAAiB,EAAE,EAAE,CACtE,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACpC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAwB,EAAE,EAAE;YAChD,IAAI,CAAC,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;gBAC5B,OAAO,MAAM,CAAC,IAAI,KAAK,CAAC,QAAQ,IAAI,CAAC,IAAI,iBAAiB,IAAI,gGAAgG,CAAC,CAAC,CAAC;YACnK,CAAC;YACD,uEAAuE;YACvE,IAAI,QAAQ,IAAI,CAAC,CAAC,CAAC,IAAI,KAAK,eAAe,IAAI,CAAC,CAAC,IAAI,KAAK,cAAc,CAAC;gBAAE,OAAO,OAAO,EAAE,CAAC;YAC5F,MAAM,CAAC,CAAC,CAAC,CAAC;QACZ,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,KAAK,KAAK,EAAE,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;IACtF,CAAC,CAAC,CAAC;IACL,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,KAAK,CAAC,CAAC;QAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;IACxC,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,QAAQ,EAAE,CAAC;QACX,MAAM,CAAC,CAAC;IACV,CAAC;IACD,MAAM,KAAK,GAAG,UAAU;IACtB,sFAAsF;IACtF,GAAG,EAAE,CAAC,MAAM,CAAC,QAAQ,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,aAAa,EAAE,EAAE,IAAI,0CAA0C,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC,EAC9J,IAAI,CAAC,KAAK,CACX,CAAC;IACF,KAAK,CAAC,KAAK,EAAE,CAAC;IACd,KAAK,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC;IAC1C,OAAO,EAAE,GAAG,EAAE,GAAG,MAAM,IAAI,IAAI,CAAC,IAAI,MAAM,MAAM,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC;AACtH,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function canOpenBrowser(): boolean;
2
+ export declare function openBrowser(url: string): boolean;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Opens a URL in the operator's browser. Refuses — returning false, so the caller prints the URL
3
+ * instead — when there is evidently no browser to open: over SSH, on Linux with no display, or when
4
+ * `TREASURY_CONNECT_NO_OPEN` is set.
5
+ */
6
+ import { spawn } from "node:child_process";
7
+ import { noOpen } from "./config.js";
8
+ export function canOpenBrowser() {
9
+ if (noOpen())
10
+ return false;
11
+ if (process.env["SSH_CONNECTION"] || process.env["SSH_TTY"])
12
+ return false;
13
+ if (process.platform === "linux" && !process.env["DISPLAY"] && !process.env["WAYLAND_DISPLAY"])
14
+ return false;
15
+ return true;
16
+ }
17
+ export function openBrowser(url) {
18
+ if (!canOpenBrowser())
19
+ return false;
20
+ try {
21
+ // One literal program per platform: the boundary test pins what this file may start.
22
+ const child = process.platform === "darwin"
23
+ ? spawn("open", [url], { stdio: "ignore", detached: true })
24
+ : process.platform === "win32"
25
+ ? spawn("cmd", ["/c", "start", "", url], { stdio: "ignore", detached: true })
26
+ : spawn("xdg-open", [url], { stdio: "ignore", detached: true });
27
+ child.on("error", () => { });
28
+ child.unref();
29
+ return true;
30
+ }
31
+ catch {
32
+ return false;
33
+ }
34
+ }
35
+ //# sourceMappingURL=open.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"open.js","sourceRoot":"","sources":["../../src/connect/open.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,MAAM,UAAU,cAAc;IAC5B,IAAI,MAAM,EAAE;QAAE,OAAO,KAAK,CAAC;IAC3B,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1E,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;QAAE,OAAO,KAAK,CAAC;IAC7G,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,IAAI,CAAC,cAAc,EAAE;QAAE,OAAO,KAAK,CAAC;IACpC,IAAI,CAAC;QACH,qFAAqF;QACrF,MAAM,KAAK,GACT,OAAO,CAAC,QAAQ,KAAK,QAAQ;YAC3B,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;YAC3D,CAAC,CAAC,OAAO,CAAC,QAAQ,KAAK,OAAO;gBAC5B,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;gBAC7E,CAAC,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;QACtE,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAC5B,KAAK,CAAC,KAAK,EAAE,CAAC;QACd,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
@@ -0,0 +1,5 @@
1
+ /** Privy's own hosts: its auth and embedded-wallet iframe, its RPC, and the Cloudflare check it uses. */
2
+ export declare const PAGE_CSP: string;
3
+ export declare function readPageBundle(): Buffer;
4
+ /** The HTML shell. `secret` authorises every request the page makes back to this process. */
5
+ export declare function shell(secret: string, title: string): string;
@@ -0,0 +1,69 @@
1
+ /**
2
+ * The page shell both flows serve, and the browser bundle it loads.
3
+ *
4
+ * The bundle (`dist/connect-page.js`) is the React + Privy page built from `connect-page/` — one
5
+ * Connect button, one Privy modal, and the confirm view. It sits beside the CLI bundle at runtime,
6
+ * so it is found next to this module once bundled, and at `../../dist/` when run from source.
7
+ *
8
+ * The shell carries the visual tokens (temporalabs.com's palette) so the page is styled before the
9
+ * bundle runs, and a CSP that admits what Privy's modal needs (scripts and connections by host; images from any HTTPS host).
10
+ */
11
+ import { readFileSync, realpathSync } from "node:fs";
12
+ import { fileURLToPath, pathToFileURL } from "node:url";
13
+ /** Privy's own hosts: its auth and embedded-wallet iframe, its RPC, and the Cloudflare check it uses. */
14
+ export const PAGE_CSP = [
15
+ "default-src 'self'",
16
+ "script-src 'self' https://challenges.cloudflare.com",
17
+ "style-src 'self' 'unsafe-inline'",
18
+ "img-src 'self' data: blob: https:",
19
+ "font-src 'self' data:",
20
+ "object-src 'none'",
21
+ "base-uri 'self'",
22
+ "form-action 'none'",
23
+ "frame-ancestors 'none'",
24
+ "child-src https://auth.privy.io",
25
+ "frame-src https://auth.privy.io https://challenges.cloudflare.com",
26
+ "connect-src 'self' https://auth.privy.io https://*.rpc.privy.systems",
27
+ "worker-src 'self'",
28
+ "manifest-src 'self'",
29
+ ].join("; ");
30
+ const here = pathToFileURL(realpathSync(fileURLToPath(import.meta.url)));
31
+ export function readPageBundle() {
32
+ for (const rel of ["./connect-page.js", "../../dist/connect-page.js"]) {
33
+ try {
34
+ return readFileSync(new URL(rel, here));
35
+ }
36
+ catch {
37
+ // try the next layout
38
+ }
39
+ }
40
+ throw new Error("the connect page is not built (dist/connect-page.js is missing) — run `npm ci && npm run build` in connect-page/");
41
+ }
42
+ /** The HTML shell. `secret` authorises every request the page makes back to this process. */
43
+ export function shell(secret, title) {
44
+ return `<!doctype html>
45
+ <html lang="en">
46
+ <head>
47
+ <meta charset="utf-8">
48
+ <meta name="viewport" content="width=device-width, initial-scale=1">
49
+ <title>${title}</title>
50
+ <style>
51
+ :root { --ink:#22303F; --navy:#1E3553; --navy-700:#2A456A; --steel:#465569; --slate:#5F6B7B; --paper:#FFFFFF; --ground:#F3F6F9;
52
+ --hair:rgba(34,48,63,.12); --blue:#76A6D5; --lavender:#B9A7D6; --grey:#B2B2B2; --ok:#2E8B57; --bad:#B4362A; --warn:#8A5A00; }
53
+ html,body { margin:0; min-height:100%; color:var(--ink); font:15px/1.55 Inter,ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,sans-serif; -webkit-font-smoothing:antialiased; }
54
+ body { background:
55
+ radial-gradient(ellipse 48% 60% at 72% 60%, rgba(255,255,255,.9), rgba(255,255,255,0) 72%),
56
+ radial-gradient(ellipse 60% 75% at 0% 0%, rgba(118,166,213,.16), rgba(118,166,213,0) 70%),
57
+ radial-gradient(ellipse 55% 65% at 100% 100%, rgba(185,167,214,.17), rgba(185,167,214,0) 70%),
58
+ linear-gradient(90deg, #e9f1f9 0%, #eff1f6 50%, #efecf6 100%); min-height:100vh; }
59
+ :focus-visible { outline:2px solid #266cad; outline-offset:2px; }
60
+ </style>
61
+ </head>
62
+ <body>
63
+ <div id="root"></div>
64
+ <script src="/app.js?s=${secret}"></script>
65
+ </body>
66
+ </html>
67
+ `;
68
+ }
69
+ //# sourceMappingURL=page.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page.js","sourceRoot":"","sources":["../../src/connect/page.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAExD,yGAAyG;AACzG,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,oBAAoB;IACpB,qDAAqD;IACrD,kCAAkC;IAClC,mCAAmC;IACnC,uBAAuB;IACvB,mBAAmB;IACnB,iBAAiB;IACjB,oBAAoB;IACpB,wBAAwB;IACxB,iCAAiC;IACjC,mEAAmE;IACnE,sEAAsE;IACtE,mBAAmB;IACnB,qBAAqB;CACtB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb,MAAM,IAAI,GAAG,aAAa,CAAC,YAAY,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AAEzE,MAAM,UAAU,cAAc;IAC5B,KAAK,MAAM,GAAG,IAAI,CAAC,mBAAmB,EAAE,4BAA4B,CAAC,EAAE,CAAC;QACtE,IAAI,CAAC;YACH,OAAO,YAAY,CAAC,IAAI,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;QAC1C,CAAC;QAAC,MAAM,CAAC;YACP,sBAAsB;QACxB,CAAC;IACH,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,kHAAkH,CAAC,CAAC;AACtI,CAAC;AAED,6FAA6F;AAC7F,MAAM,UAAU,KAAK,CAAC,MAAc,EAAE,KAAa;IACjD,OAAO;;;;;SAKA,KAAK;;;;;;;;;;;;;;;yBAeW,MAAM;;;CAG9B,CAAC;AACF,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { PendingAck } from "./ack.js";
2
+ export interface Session {
3
+ account: string;
4
+ /** `external`: a browser wallet the operator controls. `embedded`: a Privy wallet opened by email or a social login. */
5
+ walletType: "external" | "embedded";
6
+ connectedAtIso: string;
7
+ }
8
+ export declare const sessionPath: () => string;
9
+ export declare function readSession(): Session | undefined;
10
+ export declare function writeSession(s: Session): void;
11
+ export declare function clearSession(): boolean;
12
+ /** The one acknowledgement waiting for `--ack` (`ack.ts`). Issuing another replaces it. */
13
+ export declare const pendingAckPath: () => string;
14
+ export declare function writePendingAck(a: PendingAck): void;
15
+ export declare function readPendingAck(): PendingAck | undefined;
16
+ export declare function clearPendingAck(): void;
@@ -0,0 +1,73 @@
1
+ /**
2
+ * The connected account, remembered between commands in a small file readable only by its owner
3
+ * (`~/.config/treasury/connect-session.json`, mode 0600; `TREASURY_CONNECT_HOME` moves it).
4
+ *
5
+ * It holds no credential: an address, how it signed in, and when. Being "connected" grants nothing
6
+ * by itself — every transaction is still confirmed by the operator in the browser and in their
7
+ * wallet — it only fixes WHICH account the confirm page will accept and pay.
8
+ */
9
+ import { chmodSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
10
+ import { homedir } from "node:os";
11
+ import { join } from "node:path";
12
+ import { getAddress, isAddress } from "viem";
13
+ import { connectHome } from "./config.js";
14
+ const dir = () => connectHome() ?? join(homedir(), ".config", "treasury");
15
+ export const sessionPath = () => join(dir(), "connect-session.json");
16
+ export function readSession() {
17
+ let raw;
18
+ try {
19
+ raw = readFileSync(sessionPath(), "utf8");
20
+ }
21
+ catch {
22
+ return undefined;
23
+ }
24
+ try {
25
+ const v = JSON.parse(raw);
26
+ if (typeof v.account !== "string" || !isAddress(v.account))
27
+ return undefined;
28
+ if (v.walletType !== "external" && v.walletType !== "embedded")
29
+ return undefined;
30
+ return { account: getAddress(v.account), walletType: v.walletType, connectedAtIso: String(v.connectedAtIso ?? "") };
31
+ }
32
+ catch {
33
+ // A file that does not parse is no session, never a guessed one.
34
+ return undefined;
35
+ }
36
+ }
37
+ function writeOwnerOnly(path, value) {
38
+ mkdirSync(dir(), { recursive: true, mode: 0o700 });
39
+ writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600 });
40
+ // `mode` applies only when the file is created; an existing file keeps its own.
41
+ chmodSync(path, 0o600);
42
+ }
43
+ export function writeSession(s) {
44
+ writeOwnerOnly(sessionPath(), s);
45
+ // A new connection voids any acknowledgement given for the one before it.
46
+ clearPendingAck();
47
+ }
48
+ export function clearSession() {
49
+ const had = readSession() !== undefined;
50
+ rmSync(sessionPath(), { force: true });
51
+ clearPendingAck();
52
+ return had;
53
+ }
54
+ /** The one acknowledgement waiting for `--ack` (`ack.ts`). Issuing another replaces it. */
55
+ export const pendingAckPath = () => join(dir(), "connect-ack.json");
56
+ export function writePendingAck(a) {
57
+ writeOwnerOnly(pendingAckPath(), a);
58
+ }
59
+ export function readPendingAck() {
60
+ try {
61
+ const v = JSON.parse(readFileSync(pendingAckPath(), "utf8"));
62
+ if (typeof v.code !== "string" || typeof v.digest !== "string" || typeof v.issuedAtIso !== "string" || typeof v.expiresAtIso !== "string")
63
+ return undefined;
64
+ return { code: v.code, digest: v.digest, issuedAtIso: v.issuedAtIso, expiresAtIso: v.expiresAtIso };
65
+ }
66
+ catch {
67
+ return undefined;
68
+ }
69
+ }
70
+ export function clearPendingAck() {
71
+ rmSync(pendingAckPath(), { force: true });
72
+ }
73
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/connect/session.ts"],"names":[],"mappings":"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"}