pushary 1.9.87 → 1.9.89
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/CHANGELOG.md +13 -0
- package/data/SKILL.md +35 -24
- package/data/cowork/SKILL.md +4 -4
- package/data/cursor-plugin/.cursor-plugin/plugin.json +1 -1
- package/data/cursor-plugin/CHANGELOG.md +8 -0
- package/data/cursor-plugin/skills/pushary/SKILL.md +34 -23
- package/data/vscode-plugin/.claude-plugin/plugin.json +1 -1
- package/data/vscode-plugin/CHANGELOG.md +8 -0
- package/data/vscode-plugin/skills/pushary/SKILL.md +34 -23
- package/dist/{api-ZDMC22LN.js → api-RMTM2T46.js} +9 -9
- package/dist/bin/pushary-bell-hook.js +22 -22
- package/dist/bin/pushary-bell.js +9 -9
- package/dist/bin/pushary-claude.js +23 -23
- package/dist/bin/pushary-clean.js +30 -30
- package/dist/bin/pushary-codex-bridge.js +19 -19
- package/dist/bin/pushary-codex-hook.js +25 -25
- package/dist/bin/pushary-codex-session.js +8 -8
- package/dist/bin/pushary-codex.js +11 -11
- package/dist/bin/pushary-connect.js +23 -23
- package/dist/bin/pushary-cowork.js +3 -3
- package/dist/bin/pushary-daemon.js +35 -35
- package/dist/bin/pushary-disconnect.js +22 -22
- package/dist/bin/pushary-display-hook.js +14 -14
- package/dist/bin/pushary-doctor.js +36 -36
- package/dist/bin/pushary-elicitation-hook.js +22 -22
- package/dist/bin/pushary-gemini-bridge.js +14 -14
- package/dist/bin/pushary-gemini-hook.js +23 -23
- package/dist/bin/pushary-hook.js +25 -25
- package/dist/bin/pushary-login.js +18 -18
- package/dist/bin/pushary-logout.js +20 -20
- package/dist/bin/pushary-mcp.js +10 -10
- package/dist/bin/pushary-mode.js +13 -13
- package/dist/bin/pushary-notification-hook.js +21 -21
- package/dist/bin/pushary-opencode-hook.js +22 -22
- package/dist/bin/pushary-permission-denied-hook.js +24 -24
- package/dist/bin/pushary-permission-hook.js +24 -24
- package/dist/bin/pushary-post-hook.js +21 -21
- package/dist/bin/pushary-prompt-hook.js +21 -21
- package/dist/bin/pushary-session-end-hook.js +23 -23
- package/dist/bin/pushary-session-start-hook.js +31 -31
- package/dist/bin/pushary-setup.js +43 -43
- package/dist/bin/pushary-stats.js +3 -3
- package/dist/bin/pushary-status.js +27 -27
- package/dist/bin/pushary-stop-hook.js +21 -21
- package/dist/bin/pushary-stopfailure-hook.js +21 -21
- package/dist/bin/pushary-suggestions.js +13 -13
- package/dist/bin/pushary-transcript-register.js +15 -15
- package/dist/bin/pushary-transcripts.js +56 -27
- package/dist/bin/pushary-upgrade.js +36 -36
- package/dist/bin/pushary-wait.js +13 -13
- package/dist/bin/pushary.js +6 -6
- package/dist/{chunk-LXTTFH4P.js → chunk-2UQDRCEI.js} +5 -5
- package/dist/{chunk-7SY2F6JA.js → chunk-2WRQQTOZ.js} +2 -2
- package/dist/{chunk-FQ2EBMDA.js → chunk-3YN7TPIQ.js} +6 -6
- package/dist/{chunk-E5DZ74QT.js → chunk-4D6PR2MK.js} +5 -5
- package/dist/{chunk-VNROKR2K.js → chunk-53CFDLGE.js} +2 -2
- package/dist/{chunk-K4I2LESS.js → chunk-54RUDSDE.js} +2 -2
- package/dist/{chunk-VADZ6C26.js → chunk-55K5ST53.js} +2 -2
- package/dist/{chunk-CT2HRZLL.js → chunk-662O4JBY.js} +3 -3
- package/dist/{chunk-4RZXECJS.js → chunk-CV3ALIG7.js} +7 -7
- package/dist/{chunk-42KJLBX2.js → chunk-DBWFWMHB.js} +6 -6
- package/dist/{chunk-ZLO3K5TP.js → chunk-F6WJEBBK.js} +4 -4
- package/dist/{chunk-XPE745FL.js → chunk-F7FED2QT.js} +5 -5
- package/dist/{chunk-GPP7AUCM.js → chunk-FMOIAZXN.js} +5 -5
- package/dist/{chunk-GVMOTYFU.js → chunk-G3GSI7E6.js} +2 -2
- package/dist/{chunk-B34K6NXW.js → chunk-H4CKXYWR.js} +2 -2
- package/dist/{chunk-JILOKJ3W.js → chunk-IBYD6P5T.js} +141 -90
- package/dist/{chunk-STQEIEYJ.js → chunk-IJKSZTFR.js} +1 -1
- package/dist/{chunk-IOJZPPDB.js → chunk-ILBVL464.js} +1 -1
- package/dist/{chunk-YLWPJ2MP.js → chunk-JBR2FQKG.js} +2 -2
- package/dist/{chunk-S33CCO4J.js → chunk-JVWD6UWG.js} +1 -1
- package/dist/{chunk-NP65LEAY.js → chunk-L4FQ3I7N.js} +1 -1
- package/dist/{chunk-7TZDVGMC.js → chunk-LC4OFGTU.js} +1 -1
- package/dist/{chunk-P4W5TP2X.js → chunk-LVOBBWSN.js} +1 -1
- package/dist/{chunk-NFQCFVKJ.js → chunk-M7MVHBKI.js} +5 -5
- package/dist/{chunk-GAYYYXMU.js → chunk-MV34QALO.js} +1 -1
- package/dist/{chunk-K34J2V5G.js → chunk-N574OILO.js} +2 -2
- package/dist/{chunk-MY6CX7WE.js → chunk-OCRNH4YT.js} +4 -4
- package/dist/{chunk-GVZAEST7.js → chunk-OMS5V4ER.js} +3 -3
- package/dist/{chunk-TQ43EX2E.js → chunk-P6EUHG5X.js} +3 -3
- package/dist/{chunk-XGW7J7KT.js → chunk-PF2COPAW.js} +2 -2
- package/dist/{chunk-UDYWZ4DI.js → chunk-PPJSAS57.js} +2 -2
- package/dist/{chunk-QN2EFKXV.js → chunk-R3LNXXPV.js} +2 -2
- package/dist/{chunk-RPXPRBBA.js → chunk-R5F2A2IT.js} +1 -1
- package/dist/{chunk-KWWHFTNJ.js → chunk-RC65MCPA.js} +2 -1
- package/dist/{chunk-2JTWOTOC.js → chunk-RJXWGSXY.js} +1 -1
- package/dist/{chunk-NW3GZBQA.js → chunk-RQGGP52F.js} +4 -4
- package/dist/{chunk-YRQWA4ZS.js → chunk-SDRHH6LW.js} +1 -1
- package/dist/{chunk-7AH6F44G.js → chunk-SOBEBLLW.js} +4 -4
- package/dist/{chunk-DWUD47H5.js → chunk-SUEB56Y4.js} +1 -1
- package/dist/{chunk-4VHT5SWU.js → chunk-SXELNIYK.js} +3 -3
- package/dist/{chunk-RHDHLXUK.js → chunk-TBKO5EOS.js} +3 -3
- package/dist/{chunk-VNYSGRCL.js → chunk-TEFWZEL2.js} +9 -9
- package/dist/{chunk-3ZWJBPQ5.js → chunk-TXDXHR2G.js} +1 -1
- package/dist/{chunk-UOM5SUWJ.js → chunk-U4DG4RC4.js} +2 -2
- package/dist/{chunk-5YDIHB67.js → chunk-UEWTZZY4.js} +3 -3
- package/dist/{chunk-R5SANCZC.js → chunk-UYKOZLKX.js} +4 -4
- package/dist/{chunk-VU5PKE2N.js → chunk-UYVCRG3J.js} +38 -38
- package/dist/{chunk-4PS27SCG.js → chunk-VHO3HV7P.js} +25 -7
- package/dist/{chunk-XMRCP7TM.js → chunk-VHQCMNIO.js} +2 -2
- package/dist/{chunk-D766FEAS.js → chunk-WNW2HFNT.js} +3 -3
- package/dist/{chunk-7LRY76MV.js → chunk-X5MW6LB5.js} +5 -5
- package/dist/{chunk-6OVWYAET.js → chunk-XGVKU7PM.js} +4 -4
- package/dist/{chunk-UAB3K3TA.js → chunk-XLR4QO4R.js} +3 -3
- package/dist/{chunk-FE3I7SCA.js → chunk-XZYUFVUW.js} +5 -5
- package/dist/{chunk-WJ2APMU5.js → chunk-Y7FF5WKE.js} +4 -4
- package/dist/{chunk-IXUJCONI.js → chunk-YCPUVARV.js} +2 -2
- package/dist/{chunk-RKN7BJRQ.js → chunk-YFNUFWSK.js} +1 -1
- package/dist/{chunk-NFKB7ZHA.js → chunk-YH2ZWZSG.js} +2 -2
- package/dist/{chunk-M66W7UD7.js → chunk-YKA6HMYO.js} +2 -2
- package/dist/{chunk-X5INK4OE.js → chunk-YQYCQ2KY.js} +10 -10
- package/dist/{chunk-SLMCMHVY.js → chunk-ZOKA75MZ.js} +2 -2
- package/dist/{chunk-2CJNGJQS.js → chunk-ZQMR6OMT.js} +3 -3
- package/dist/{codex-app-cards-switch-ARYZG5DV.js → codex-app-cards-switch-BSMP3FMX.js} +10 -10
- package/dist/{codex-desktop-launch-6P2IXUIZ.js → codex-desktop-launch-LZL2LBBZ.js} +1 -1
- package/dist/{codex-session-launch-WUZSHGSI.js → codex-session-launch-LDY5BQCQ.js} +16 -16
- package/dist/{config-ZDOHQUNU.js → config-RTZAI4JI.js} +2 -2
- package/dist/{global-upgrade-QGHGUP5V.js → global-upgrade-Y6AEYIJ5.js} +9 -9
- package/dist/{identity-WRUYTQ4O.js → identity-HKL7Z55E.js} +3 -3
- package/dist/{live-capabilities-AJJILIIZ.js → live-capabilities-BUP4EEKC.js} +4 -4
- package/dist/{paths-Q5A6GBRA.js → paths-P5K3AYLX.js} +2 -2
- package/dist/{reapply-BMNUBJ3Z.js → reapply-DN4VZ54U.js} +24 -24
- package/dist/src/index.js +24 -24
- package/dist/{upgrade-lock-NGCFJRRN.js → upgrade-lock-FW74BNQL.js} +3 -3
- package/dist/{usage-3EPWR2WQ.js → usage-J2D7WYM4.js} +2 -2
- package/package.json +1 -1
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SESSION_HISTORY_RETENTION_DAYS,
|
|
3
|
+
readOwnerOnlyJson,
|
|
3
4
|
savedTranscriptReadPositions
|
|
4
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-UYVCRG3J.js";
|
|
5
6
|
import {
|
|
6
7
|
ensureDaemonRunning
|
|
7
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-XZYUFVUW.js";
|
|
8
9
|
import {
|
|
9
10
|
hookOwnerOf
|
|
10
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-54RUDSDE.js";
|
|
11
12
|
import {
|
|
12
13
|
buildTranscriptRecords,
|
|
13
14
|
decodePublicKey,
|
|
@@ -18,17 +19,17 @@ import {
|
|
|
18
19
|
recordSealerFor,
|
|
19
20
|
uploadTranscriptRecords,
|
|
20
21
|
wrapSessionKey
|
|
21
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-VHO3HV7P.js";
|
|
22
23
|
import {
|
|
23
24
|
external_exports
|
|
24
25
|
} from "./chunk-XMSDA5WA.js";
|
|
25
26
|
import {
|
|
26
27
|
transcriptEnvironmentPreference,
|
|
27
28
|
transcriptsEnabled
|
|
28
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-ILBVL464.js";
|
|
29
30
|
import {
|
|
30
31
|
readTranscriptPreference
|
|
31
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-P6EUHG5X.js";
|
|
32
33
|
import {
|
|
33
34
|
hookMeaning
|
|
34
35
|
} from "./chunk-TXJ65MIV.js";
|
|
@@ -36,15 +37,15 @@ import {
|
|
|
36
37
|
claudeConfigDir,
|
|
37
38
|
codexHome,
|
|
38
39
|
pusharyDir
|
|
39
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-JBR2FQKG.js";
|
|
40
41
|
import {
|
|
41
42
|
writeJsonAtomic,
|
|
42
43
|
writeJsonAtomicDurable
|
|
43
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-UEWTZZY4.js";
|
|
44
45
|
import {
|
|
45
46
|
getApiKey,
|
|
46
47
|
getBaseUrl
|
|
47
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-L4FQ3I7N.js";
|
|
48
49
|
import {
|
|
49
50
|
PENDING_COMMAND_PREFIX,
|
|
50
51
|
SESSION_HISTORY_BACKFILL_LIMIT_BYTES,
|
|
@@ -55,30 +56,30 @@ import {
|
|
|
55
56
|
} from "./chunk-5TLDFPW6.js";
|
|
56
57
|
|
|
57
58
|
// src/transcript-recipient-pin.ts
|
|
58
|
-
import {
|
|
59
|
+
import { createHash } from "crypto";
|
|
60
|
+
import { mkdirSync, rmSync } from "fs";
|
|
59
61
|
import { homedir } from "os";
|
|
60
62
|
import { dirname, join } from "path";
|
|
61
|
-
var
|
|
63
|
+
var recipientPinFile = () => process.env.PUSHARY_RECIPIENT_PIN_FILE?.trim() || join(homedir(), ".pushary", "recipients.json");
|
|
62
64
|
var asPin = (offer) => ({
|
|
63
65
|
phonePublicKey: offer.phonePublicKey,
|
|
64
66
|
providerKeyId: offer.provider?.keyId ?? null,
|
|
65
67
|
providerPublicKey: offer.provider?.publicKey ?? null
|
|
66
68
|
});
|
|
67
|
-
var
|
|
68
|
-
|
|
69
|
+
var pinnedRecipientsSchema = external_exports.object({
|
|
70
|
+
phonePublicKey: external_exports.string().min(1).max(256),
|
|
71
|
+
providerKeyId: external_exports.string().min(1).max(256).nullable().default(null),
|
|
72
|
+
providerPublicKey: external_exports.string().min(1).max(256).nullable().default(null)
|
|
73
|
+
});
|
|
74
|
+
var loadPin = (path = recipientPinFile()) => {
|
|
69
75
|
try {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
return
|
|
73
|
-
|
|
74
|
-
providerKeyId: typeof raw.providerKeyId === "string" ? raw.providerKeyId : null,
|
|
75
|
-
providerPublicKey: typeof raw.providerPublicKey === "string" ? raw.providerPublicKey : null
|
|
76
|
-
};
|
|
77
|
-
} catch {
|
|
78
|
-
return null;
|
|
76
|
+
return pinnedRecipientsSchema.parse(readOwnerOnlyJson(path, 16384));
|
|
77
|
+
} catch (error) {
|
|
78
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") return null;
|
|
79
|
+
throw new Error("Existing history trust could not be read. Nothing was replaced.");
|
|
79
80
|
}
|
|
80
81
|
};
|
|
81
|
-
var savePin = (pin, path =
|
|
82
|
+
var savePin = (pin, path = recipientPinFile()) => {
|
|
82
83
|
try {
|
|
83
84
|
mkdirSync(dirname(path), { recursive: true, mode: 448 });
|
|
84
85
|
writeJsonAtomicDurable(path, pin, 384);
|
|
@@ -89,7 +90,7 @@ var savePin = (pin, path = pinFile()) => {
|
|
|
89
90
|
};
|
|
90
91
|
var clearPin = () => {
|
|
91
92
|
try {
|
|
92
|
-
rmSync(
|
|
93
|
+
rmSync(recipientPinFile(), { force: true });
|
|
93
94
|
} catch {
|
|
94
95
|
}
|
|
95
96
|
};
|
|
@@ -102,28 +103,72 @@ var verifyRecipients = (offer, pinned) => {
|
|
|
102
103
|
if (next.providerPublicKey !== pinned.providerPublicKey) changed.push("the compliance recovery key");
|
|
103
104
|
return changed.length === 0 ? { kind: "match" } : { kind: "changed", changed };
|
|
104
105
|
};
|
|
105
|
-
var fingerprint = (publicKey) =>
|
|
106
|
-
const hex = Buffer.from(publicKey, "base64").toString("hex").toUpperCase();
|
|
107
|
-
return (hex.slice(0, 8).match(/.{2}/g) ?? []).join(" ");
|
|
108
|
-
};
|
|
106
|
+
var fingerprint = (publicKey) => createHash("sha256").update(publicKey).digest("hex");
|
|
109
107
|
var changedRecipientsMessage = (verdict, offer) => [
|
|
110
108
|
"[pushary] Transcript sync stopped: the keys this machine seals transcripts to have changed.",
|
|
111
109
|
...verdict.changed.map((what) => `[pushary] changed: ${what}`),
|
|
112
110
|
`[pushary] now offered: phone ${fingerprint(offer.phonePublicKey)}${offer.provider ? `, recovery ${offer.provider.keyId.slice(0, 8)}` : ", no recovery key"}`,
|
|
113
111
|
"[pushary] If you reinstalled the app or rotated a key, this is expected. Compare the",
|
|
114
112
|
"[pushary] fingerprint above with the one in the app under Settings, then run:",
|
|
115
|
-
"[pushary] pushary transcripts trust --
|
|
113
|
+
"[pushary] pushary transcripts trust --fingerprint <fingerprint-from-phone-settings>",
|
|
116
114
|
"[pushary] Until then nothing is uploaded, so no turn is sealed to a key you have not seen."
|
|
117
115
|
].join("\n");
|
|
118
116
|
|
|
117
|
+
// src/transcript-phone-trust.ts
|
|
118
|
+
import { createHash as createHash2 } from "crypto";
|
|
119
|
+
import { mkdirSync as mkdirSync2 } from "fs";
|
|
120
|
+
import { dirname as dirname2, join as join2 } from "path";
|
|
121
|
+
var approvedPhoneSchema = external_exports.object({ version: external_exports.literal(1), sourcePinHash: external_exports.string().regex(/^[a-f0-9]{64}$/), phonePublicKey: external_exports.string().min(1).max(256) });
|
|
122
|
+
var pinHash = (pin) => createHash2("sha256").update(JSON.stringify(pin)).digest("hex");
|
|
123
|
+
var approvalFile = (path, pin, publicKey) => join2(`${path}.trusted-phones`, `${pinHash(pin)}-${fingerprint(publicKey)}.json`);
|
|
124
|
+
var sameRecoveryRecipient = (offer, pin) => (offer.provider?.keyId ?? null) === pin.providerKeyId && (offer.provider?.publicKey ?? null) === pin.providerPublicKey;
|
|
125
|
+
var canonicalPublicKey = (key) => {
|
|
126
|
+
const bytes = Buffer.from(key, "base64");
|
|
127
|
+
return bytes.length === 32 && bytes.toString("base64") === key;
|
|
128
|
+
};
|
|
129
|
+
var phoneWasApproved = (offer, pin, path) => {
|
|
130
|
+
try {
|
|
131
|
+
const approval = approvedPhoneSchema.parse(readOwnerOnlyJson(approvalFile(path, pin, offer.phonePublicKey), 16384));
|
|
132
|
+
if (approval.sourcePinHash !== pinHash(pin) || approval.phonePublicKey !== offer.phonePublicKey) throw new Error("Mismatched approval");
|
|
133
|
+
return true;
|
|
134
|
+
} catch (error) {
|
|
135
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") return false;
|
|
136
|
+
throw new Error("Saved phone verification could not be read. Nothing was replaced.");
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
var verifyTrustedRecipients = (offer, path = recipientPinFile()) => {
|
|
140
|
+
const pin = loadPin(path);
|
|
141
|
+
const verdict = verifyRecipients(offer, pin);
|
|
142
|
+
if (verdict.kind !== "changed" || !pin || !sameRecoveryRecipient(offer, pin)) return verdict;
|
|
143
|
+
return phoneWasApproved(offer, pin, path) ? { kind: "match" } : verdict;
|
|
144
|
+
};
|
|
145
|
+
var assertVerifiedPhone = (offer, pin, expectedFingerprint) => {
|
|
146
|
+
if (!canonicalPublicKey(offer.phonePublicKey) || fingerprint(offer.phonePublicKey) !== expectedFingerprint.trim().toLowerCase()) {
|
|
147
|
+
throw new Error("The fingerprint does not match this phone. Nothing was changed.");
|
|
148
|
+
}
|
|
149
|
+
if (!pin) throw new Error("No existing history trust was found. Nothing was changed.");
|
|
150
|
+
if (!sameRecoveryRecipient(offer, pin)) throw new Error("The recovery recipient also changed. Phone verification cannot approve that change.");
|
|
151
|
+
};
|
|
152
|
+
var trustPhoneRecipient = (request) => {
|
|
153
|
+
const { offer, pinFile } = request;
|
|
154
|
+
const pin = loadPin(pinFile);
|
|
155
|
+
assertVerifiedPhone(offer, pin, request.expectedFingerprint);
|
|
156
|
+
if (!pin) throw new Error("No existing history trust was found.");
|
|
157
|
+
if (verifyTrustedRecipients(offer, pinFile).kind === "match") return "already_trusted";
|
|
158
|
+
const path = approvalFile(pinFile, pin, offer.phonePublicKey);
|
|
159
|
+
mkdirSync2(dirname2(path), { recursive: true, mode: 448 });
|
|
160
|
+
writeJsonAtomicDurable(path, { version: 1, sourcePinHash: pinHash(pin), phonePublicKey: offer.phonePublicKey }, 384);
|
|
161
|
+
return "trusted";
|
|
162
|
+
};
|
|
163
|
+
|
|
119
164
|
// src/transcript-registration.ts
|
|
120
|
-
import { createHash as
|
|
121
|
-
import { lstatSync as lstatSync3, readFileSync as
|
|
122
|
-
import { basename, dirname as
|
|
165
|
+
import { createHash as createHash6 } from "crypto";
|
|
166
|
+
import { lstatSync as lstatSync3, readFileSync as readFileSync6, readdirSync as readdirSync2, realpathSync, statSync as statSync3, unlinkSync as unlinkSync2 } from "fs";
|
|
167
|
+
import { basename, dirname as dirname4, isAbsolute, join as join7, relative, resolve as resolve2, sep } from "path";
|
|
123
168
|
|
|
124
169
|
// src/claude-display-draft.ts
|
|
125
|
-
import { closeSync, fstatSync, lstatSync, openSync, readFileSync
|
|
126
|
-
import { join as
|
|
170
|
+
import { closeSync, fstatSync, lstatSync, openSync, readFileSync, readSync, unlinkSync } from "fs";
|
|
171
|
+
import { join as join3 } from "path";
|
|
127
172
|
|
|
128
173
|
// src/transcript-redaction.ts
|
|
129
174
|
var redactDraft = (text, complete = false) => {
|
|
@@ -176,14 +221,14 @@ var stateSchema = external_exports.object({
|
|
|
176
221
|
startedAt: external_exports.number().int().positive(),
|
|
177
222
|
updatedAt: external_exports.number().int().positive()
|
|
178
223
|
});
|
|
179
|
-
var fileFor = (id, directory) =>
|
|
224
|
+
var fileFor = (id, directory) => join3(directory, `${id}.display`);
|
|
180
225
|
var readState = (sessionId, runtime) => {
|
|
181
226
|
if (!identifier.safeParse(sessionId).success) return null;
|
|
182
227
|
try {
|
|
183
228
|
const path = fileFor(sessionId, runtime.directory);
|
|
184
229
|
const info = lstatSync(path);
|
|
185
230
|
if (!info.isFile() || info.nlink !== 1 || info.size > MAX_TAIL || (info.mode & 511) !== 384) return null;
|
|
186
|
-
const envelope = external_exports.object({ content: external_exports.string() }).parse(JSON.parse(
|
|
231
|
+
const envelope = external_exports.object({ content: external_exports.string() }).parse(JSON.parse(readFileSync(path, "utf8")));
|
|
187
232
|
const plain = decryptRecord(envelope.content, runtime.sessionKey);
|
|
188
233
|
if (!plain) return null;
|
|
189
234
|
const state = stateSchema.parse(JSON.parse(plain));
|
|
@@ -266,18 +311,18 @@ var readDisplayDraft = (sessionId, runtime) => {
|
|
|
266
311
|
};
|
|
267
312
|
|
|
268
313
|
// src/transcript-session-key.ts
|
|
269
|
-
import { existsSync
|
|
314
|
+
import { existsSync, mkdirSync as mkdirSync3, readFileSync as readFileSync2, readdirSync, rmSync as rmSync2, statSync } from "fs";
|
|
270
315
|
import { homedir as homedir2 } from "os";
|
|
271
|
-
import { join as
|
|
272
|
-
var sessionKeyDirectory = () => process.env.PUSHARY_SESSION_KEY_DIR?.trim() ||
|
|
316
|
+
import { join as join4 } from "path";
|
|
317
|
+
var sessionKeyDirectory = () => process.env.PUSHARY_SESSION_KEY_DIR?.trim() || join4(homedir2(), ".pushary", "sessions");
|
|
273
318
|
var SESSION_DISPLAY_TTL_MS = 48 * 60 * 60 * 1e3;
|
|
274
319
|
var SAFE_SESSION_ID = /^[A-Za-z0-9._-]{1,128}$/;
|
|
275
|
-
var fileFor2 = (sessionId, directory) => SAFE_SESSION_ID.test(sessionId) ?
|
|
320
|
+
var fileFor2 = (sessionId, directory) => SAFE_SESSION_ID.test(sessionId) ? join4(directory, `${sessionId}.json`) : null;
|
|
276
321
|
var loadSessionKey = (sessionId, directory = sessionKeyDirectory()) => {
|
|
277
322
|
const path = fileFor2(sessionId, directory);
|
|
278
|
-
if (!path || !
|
|
323
|
+
if (!path || !existsSync(path)) return null;
|
|
279
324
|
try {
|
|
280
|
-
const raw = JSON.parse(
|
|
325
|
+
const raw = JSON.parse(readFileSync2(path, "utf-8"));
|
|
281
326
|
const key = typeof raw.sessionKey === "string" ? Buffer.from(raw.sessionKey, "base64") : null;
|
|
282
327
|
if (!key || key.length !== 32) return null;
|
|
283
328
|
return {
|
|
@@ -296,7 +341,7 @@ var saveSessionKey = (sessionId, value, directory = sessionKeyDirectory()) => {
|
|
|
296
341
|
const path = fileFor2(sessionId, directory);
|
|
297
342
|
if (!path) return false;
|
|
298
343
|
try {
|
|
299
|
-
|
|
344
|
+
mkdirSync3(directory, { recursive: true, mode: 448 });
|
|
300
345
|
writeJsonAtomicDurable(path, {
|
|
301
346
|
sessionKey: Buffer.from(value.sessionKey).toString("base64"),
|
|
302
347
|
wrappedKey: value.wrappedKey,
|
|
@@ -311,12 +356,12 @@ var saveSessionKey = (sessionId, value, directory = sessionKeyDirectory()) => {
|
|
|
311
356
|
}
|
|
312
357
|
};
|
|
313
358
|
var sweepStaleSessionDisplays = (now = Date.now(), dir = sessionKeyDirectory()) => {
|
|
314
|
-
if (!
|
|
359
|
+
if (!existsSync(dir)) return 0;
|
|
315
360
|
let removed = 0;
|
|
316
361
|
try {
|
|
317
362
|
for (const name of readdirSync(dir)) {
|
|
318
363
|
if (!name.endsWith(".display")) continue;
|
|
319
|
-
const path =
|
|
364
|
+
const path = join4(dir, name);
|
|
320
365
|
try {
|
|
321
366
|
if (now - statSync(path).mtimeMs <= SESSION_DISPLAY_TTL_MS) continue;
|
|
322
367
|
rmSync2(path, { force: true });
|
|
@@ -426,9 +471,9 @@ var startDisplayPublisher = (options) => {
|
|
|
426
471
|
};
|
|
427
472
|
|
|
428
473
|
// src/transcript-reader.ts
|
|
429
|
-
import { createHash as
|
|
430
|
-
import { join as
|
|
431
|
-
import { closeSync as closeSync3, fstatSync as fstatSync3, openSync as openSync3, readFileSync as
|
|
474
|
+
import { createHash as createHash5 } from "crypto";
|
|
475
|
+
import { join as join5 } from "path";
|
|
476
|
+
import { closeSync as closeSync3, fstatSync as fstatSync3, openSync as openSync3, readFileSync as readFileSync5, readSync as readSync2, statSync as statSync2 } from "fs";
|
|
432
477
|
|
|
433
478
|
// src/transcript-source.ts
|
|
434
479
|
var normalizeTranscriptLines = (lines, source) => {
|
|
@@ -483,10 +528,10 @@ var transcriptLinesSince = (lines, oldestLineMs, dropCompactionSummaries) => lin
|
|
|
483
528
|
});
|
|
484
529
|
|
|
485
530
|
// src/transcript-snapshot-file.ts
|
|
486
|
-
import { closeSync as closeSync2, fstatSync as fstatSync2, openSync as openSync2, readFileSync as
|
|
531
|
+
import { closeSync as closeSync2, fstatSync as fstatSync2, openSync as openSync2, readFileSync as readFileSync3 } from "fs";
|
|
487
532
|
|
|
488
533
|
// src/transcript-snapshot-source.ts
|
|
489
|
-
import { createHash } from "crypto";
|
|
534
|
+
import { createHash as createHash3 } from "crypto";
|
|
490
535
|
var identity = external_exports.string().min(1).max(256);
|
|
491
536
|
var timestamp = external_exports.string().refine((value) => Number.isFinite(Date.parse(value)));
|
|
492
537
|
var geminiTool = external_exports.object({ id: identity, name: external_exports.string(), args: external_exports.record(external_exports.string(), external_exports.unknown()), result: external_exports.unknown().optional(), status: external_exports.string() });
|
|
@@ -612,7 +657,7 @@ var parseCursorSnapshot = (text) => {
|
|
|
612
657
|
const record = JSON.parse(line);
|
|
613
658
|
if (isRecord(record) && (record.type === "metadata" || record.type === "turn_ended")) continue;
|
|
614
659
|
const message = cursorMessage.parse(record);
|
|
615
|
-
const fingerprint2 =
|
|
660
|
+
const fingerprint2 = createHash3("sha256").update(canonicalJsonText(message)).digest("hex");
|
|
616
661
|
const occurrence = occurrences.get(fingerprint2) ?? 0;
|
|
617
662
|
occurrences.set(fingerprint2, occurrence + 1);
|
|
618
663
|
messages.push({ id: `cursor:${fingerprint2}:${occurrence}`, role: message.role, content: message.message.content });
|
|
@@ -648,7 +693,7 @@ var readTranscriptSnapshot = (path, source, sessionId) => {
|
|
|
648
693
|
try {
|
|
649
694
|
const before = fstatSync2(descriptor);
|
|
650
695
|
if (!before.isFile() || before.size === 0 || before.size > MAXIMUM_SNAPSHOT_FILE_BYTES) throw new Error("History snapshot is empty or exceeds its size limit");
|
|
651
|
-
const text =
|
|
696
|
+
const text = readFileSync3(descriptor, "utf8");
|
|
652
697
|
const after = fstatSync2(descriptor);
|
|
653
698
|
if (before.size !== after.size || before.mtimeMs !== after.mtimeMs) throw new Error("History snapshot changed while reading");
|
|
654
699
|
let messages;
|
|
@@ -670,11 +715,11 @@ var readTranscriptSnapshot = (path, source, sessionId) => {
|
|
|
670
715
|
};
|
|
671
716
|
|
|
672
717
|
// src/transcript-snapshot-journal.ts
|
|
673
|
-
import { lstatSync as lstatSync2, mkdirSync as
|
|
674
|
-
import { dirname as
|
|
718
|
+
import { lstatSync as lstatSync2, mkdirSync as mkdirSync4, readFileSync as readFileSync4 } from "fs";
|
|
719
|
+
import { dirname as dirname3 } from "path";
|
|
675
720
|
|
|
676
721
|
// src/transcript-projection.ts
|
|
677
|
-
import { createHash as
|
|
722
|
+
import { createHash as createHash4 } from "crypto";
|
|
678
723
|
var newestWithinUploadLimits = (messages, limits) => {
|
|
679
724
|
let start = messages.length;
|
|
680
725
|
let contentBytes = 0;
|
|
@@ -703,7 +748,7 @@ var projectTranscriptSnapshot = (previous, messages, older) => {
|
|
|
703
748
|
const order = message.sourcePosition;
|
|
704
749
|
if (!message.id || message.id.length > 256 || Object.hasOwn(entries, message.id)) throw new Error("Invalid or duplicate history message identity");
|
|
705
750
|
const record = { timestamp: message.timestamp, observedAtMs: message.observedAtMs, historyMessage: { role: message.role, content: message.content } };
|
|
706
|
-
const digest =
|
|
751
|
+
const digest = createHash4("sha256").update(JSON.stringify(record)).digest("hex");
|
|
707
752
|
entries[message.id] = { digest, order };
|
|
708
753
|
const earlier = previous?.entries[message.id];
|
|
709
754
|
if (earlier?.digest === digest && earlier.order === order) continue;
|
|
@@ -748,7 +793,7 @@ var TranscriptSnapshotJournal = class {
|
|
|
748
793
|
return;
|
|
749
794
|
}
|
|
750
795
|
if (!info.isFile() || info.nlink !== 1 || info.size > MAXIMUM_JOURNAL_BYTES || process.getuid && (info.uid !== process.getuid() || (info.mode & 511) !== 384)) throw new Error("Unsafe history checkpoint");
|
|
751
|
-
const saved = journalSchema.parse(JSON.parse(
|
|
796
|
+
const saved = journalSchema.parse(JSON.parse(readFileSync4(path, "utf8")));
|
|
752
797
|
if (saved.binding !== binding) throw new Error("History checkpoint belongs to another source");
|
|
753
798
|
this.current = saved.epochStartedAtMs === epochStartedAtMs ? saved : { ...saved, epochStartedAtMs, initialized: false, observedAtMs: {}, pending: null, excludedIds: [], projection: { revision: Math.max(saved.projection?.revision ?? 0, saved.pending?.next.revision ?? 0), entries: {} } };
|
|
754
799
|
}
|
|
@@ -799,7 +844,7 @@ var TranscriptSnapshotJournal = class {
|
|
|
799
844
|
}
|
|
800
845
|
store(next) {
|
|
801
846
|
if (Buffer.byteLength(JSON.stringify(next)) > MAXIMUM_JOURNAL_BYTES) throw new Error("History checkpoint exceeds its size limit");
|
|
802
|
-
|
|
847
|
+
mkdirSync4(dirname3(this.path), { recursive: true, mode: 448 });
|
|
803
848
|
writeJsonAtomicDurable(this.path, next, 384);
|
|
804
849
|
this.current = journalSchema.parse(next);
|
|
805
850
|
}
|
|
@@ -895,7 +940,7 @@ var appendOnlyReader = (path, source, epoch) => ({
|
|
|
895
940
|
read(retentionDays, nowMs) {
|
|
896
941
|
const version = sourceVersion(path);
|
|
897
942
|
const read2 = epoch ? linesSeenDuringEpoch(epoch, path, source, nowMs, retentionDays) : null;
|
|
898
|
-
const lines = read2?.lines ?? normalizeTranscriptLines(
|
|
943
|
+
const lines = read2?.lines ?? normalizeTranscriptLines(readFileSync5(path, "utf8").split("\n"), source);
|
|
899
944
|
return {
|
|
900
945
|
lines,
|
|
901
946
|
acknowledge() {
|
|
@@ -911,8 +956,8 @@ var caughtUpReadStillCurrent = (caughtUp, version, nowMs) => caughtUp !== null &
|
|
|
911
956
|
var UNCHANGED_SNAPSHOT_READ = { lines: [], acknowledge: () => "unchanged" };
|
|
912
957
|
var snapshotReader = (options, source) => {
|
|
913
958
|
const { path, epoch } = options;
|
|
914
|
-
const binding =
|
|
915
|
-
const journalPath =
|
|
959
|
+
const binding = createHash5("sha256").update(JSON.stringify([options.baseUrl, options.apiKey, options.machineId, options.sessionId, source, path])).digest("hex");
|
|
960
|
+
const journalPath = join5(options.keyDirectory, "snapshots", binding + ".json");
|
|
916
961
|
const epochStart = epoch?.startedAtMs ?? 0;
|
|
917
962
|
const backfill = epoch ? epoch.backfill : SESSION_HISTORY_BACKFILL;
|
|
918
963
|
let journal;
|
|
@@ -967,11 +1012,11 @@ var transcriptHealthReporter = (target) => {
|
|
|
967
1012
|
};
|
|
968
1013
|
|
|
969
1014
|
// src/transcript-sync.ts
|
|
970
|
-
import { join as
|
|
1015
|
+
import { join as join6, resolve } from "path";
|
|
971
1016
|
var claudeTranscriptPath = (cwd, sessionId) => {
|
|
972
1017
|
const projectId = resolve(cwd).replace(/[^a-zA-Z0-9-]/g, "-");
|
|
973
1018
|
const configDir = claudeConfigDir();
|
|
974
|
-
return
|
|
1019
|
+
return join6(configDir, "projects", projectId, `${sessionId}.jsonl`);
|
|
975
1020
|
};
|
|
976
1021
|
var createSyncState = () => ({
|
|
977
1022
|
sessionKey: null,
|
|
@@ -1102,7 +1147,7 @@ var startTranscriptSync = (opts) => {
|
|
|
1102
1147
|
recipientFailure = reason;
|
|
1103
1148
|
});
|
|
1104
1149
|
if (!offer) return null;
|
|
1105
|
-
const verdict =
|
|
1150
|
+
const verdict = verifyTrustedRecipients(offer, opts.storage?.recipientPinFile);
|
|
1106
1151
|
if (verdict.kind === "first-use" && !savePin(verdict.pin, opts.storage?.recipientPinFile)) return null;
|
|
1107
1152
|
if (verdict.kind === "changed") {
|
|
1108
1153
|
recipientFailure = "phone_key_changed";
|
|
@@ -1168,13 +1213,14 @@ var startTranscriptSync = (opts) => {
|
|
|
1168
1213
|
if (ending) opts.onDrained?.();
|
|
1169
1214
|
return;
|
|
1170
1215
|
}
|
|
1216
|
+
waitingForRecipients = true;
|
|
1171
1217
|
const recipients = await fetchPinnedRecipients();
|
|
1172
1218
|
if (!recipients) {
|
|
1173
|
-
waitingForRecipients = true;
|
|
1174
1219
|
if (recipientFailure === "off_for_workspace") passOverConversationWhileHistoryIsOff();
|
|
1175
1220
|
await reportHealth(recipientFailure);
|
|
1176
1221
|
return;
|
|
1177
1222
|
}
|
|
1223
|
+
waitingForRecipients = false;
|
|
1178
1224
|
if (reader.requiresRevisions && recipients.phoneReadsTranscriptRevisions !== true) {
|
|
1179
1225
|
await reportHealth("phone_upgrade_required");
|
|
1180
1226
|
return;
|
|
@@ -1253,15 +1299,15 @@ var storedRegistrationSchema = external_exports.object({
|
|
|
1253
1299
|
endedAt: external_exports.number().positive().optional(),
|
|
1254
1300
|
drained: external_exports.boolean().optional()
|
|
1255
1301
|
});
|
|
1256
|
-
var defaultRegistryDirectory = () => process.env.PUSHARY_TRANSCRIPT_REGISTRY_DIR ||
|
|
1302
|
+
var defaultRegistryDirectory = () => process.env.PUSHARY_TRANSCRIPT_REGISTRY_DIR || join7(pusharyDir(), "transcript-capture");
|
|
1257
1303
|
var cliRegistry = (apiKey = getApiKey()) => ({ apiKey, baseUrl: getBaseUrl(), directory: defaultRegistryDirectory() });
|
|
1258
|
-
var accountFor = (registry) =>
|
|
1304
|
+
var accountFor = (registry) => createHash6("sha256").update(`${registry.baseUrl}\0${registry.apiKey}`).digest("hex");
|
|
1259
1305
|
var TTL = 48 * 60 * 60 * 1e3;
|
|
1260
|
-
var registeredReadPositionsFile = (registry) =>
|
|
1306
|
+
var registeredReadPositionsFile = (registry) => join7(registry.directory, "read-positions", `${accountFor(registry)}.json`);
|
|
1261
1307
|
var SNAPSHOT_KEYS_DIRECTORY_NAME = "snapshot-keys";
|
|
1262
1308
|
var SNAPSHOT_RECIPIENT_PIN_FILE_NAME = "recipients.json";
|
|
1263
|
-
var snapshotKeyDirectory = (registry) =>
|
|
1264
|
-
var snapshotRecipientPinFile = (registry) =>
|
|
1309
|
+
var snapshotKeyDirectory = (registry) => join7(registry.directory, SNAPSHOT_KEYS_DIRECTORY_NAME, accountFor(registry));
|
|
1310
|
+
var snapshotRecipientPinFile = (registry = cliRegistry()) => join7(snapshotKeyDirectory(registry), SNAPSHOT_RECIPIENT_PIN_FILE_NAME);
|
|
1265
1311
|
var entryAt = (path) => {
|
|
1266
1312
|
try {
|
|
1267
1313
|
return lstatSync3(path);
|
|
@@ -1276,18 +1322,18 @@ var removedPinFile = (path) => {
|
|
|
1276
1322
|
return true;
|
|
1277
1323
|
};
|
|
1278
1324
|
var clearSnapshotRecipientPins = (registryDirectory = defaultRegistryDirectory()) => {
|
|
1279
|
-
const accounts =
|
|
1325
|
+
const accounts = join7(registryDirectory, SNAPSHOT_KEYS_DIRECTORY_NAME);
|
|
1280
1326
|
if (!entryAt(accounts)?.isDirectory()) return false;
|
|
1281
|
-
return readdirSync2(accounts, { withFileTypes: true }).filter((account) => account.isDirectory()).map((account) => removedPinFile(
|
|
1327
|
+
return readdirSync2(accounts, { withFileTypes: true }).filter((account) => account.isDirectory()).map((account) => removedPinFile(join7(accounts, account.name, SNAPSHOT_RECIPIENT_PIN_FILE_NAME))).includes(true);
|
|
1282
1328
|
};
|
|
1283
1329
|
var read = (path) => {
|
|
1284
1330
|
if (statSync3(path).size > 16384) throw new Error("History registration exceeds its size limit");
|
|
1285
|
-
return storedRegistrationSchema.parse(JSON.parse(
|
|
1331
|
+
return storedRegistrationSchema.parse(JSON.parse(readFileSync6(path, "utf8")));
|
|
1286
1332
|
};
|
|
1287
1333
|
var consent = (record) => record.consentOverride ?? readTranscriptPreference() ?? true;
|
|
1288
1334
|
var withoutTrailingSlash = (url) => url.replace(/\/+$/, "");
|
|
1289
1335
|
var sameServer = (url, registry) => withoutTrailingSlash(url) === withoutTrailingSlash(registry.baseUrl);
|
|
1290
|
-
var fileFor3 = (registry, sessionId) =>
|
|
1336
|
+
var fileFor3 = (registry, sessionId) => join7(registry.directory, `${createHash6("sha256").update(accountFor(registry) + sessionId).digest("hex")}.json`);
|
|
1291
1337
|
var permitted = (registry, raw) => raw.account === accountFor(registry) && consent(raw) && !raw.drained && (raw.source !== "claude" || !!raw.expectedSiteId || !macOwnsClaudeHooks(raw.cwd)) && (!raw.expectedSiteId || raw.expectedSiteId === raw.verifiedSiteId && !!raw.expectedBaseUrl && sameServer(raw.expectedBaseUrl, registry));
|
|
1292
1338
|
var registeredTranscriptEnabled = (apiKey, sessionId, registry = cliRegistry(apiKey)) => {
|
|
1293
1339
|
try {
|
|
@@ -1297,12 +1343,12 @@ var registeredTranscriptEnabled = (apiKey, sessionId, registry = cliRegistry(api
|
|
|
1297
1343
|
}
|
|
1298
1344
|
};
|
|
1299
1345
|
var macOwnsClaudeHooks = (cwd) => [
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1346
|
+
join7(claudeConfigDir(), "settings.json"),
|
|
1347
|
+
join7(cwd, ".claude", "settings.json"),
|
|
1348
|
+
join7(cwd, ".claude", "settings.local.json")
|
|
1303
1349
|
].some((path) => {
|
|
1304
1350
|
try {
|
|
1305
|
-
return hookOwnerOf(JSON.parse(
|
|
1351
|
+
return hookOwnerOf(JSON.parse(readFileSync6(path, "utf8"))) === "app";
|
|
1306
1352
|
} catch {
|
|
1307
1353
|
return false;
|
|
1308
1354
|
}
|
|
@@ -1310,12 +1356,12 @@ var macOwnsClaudeHooks = (cwd) => [
|
|
|
1310
1356
|
var TRANSCRIPT_ROOT_NAME = { claude: "projects", codex: "sessions", gemini: "tmp", cursor: "projects", hermes: "hermes-history" };
|
|
1311
1357
|
var transcriptRoots = (source) => {
|
|
1312
1358
|
const home = homedir3();
|
|
1313
|
-
if (source === "codex") return [
|
|
1314
|
-
if (source === "cursor") return [
|
|
1315
|
-
if (source === "hermes") return [
|
|
1316
|
-
if (source === "gemini") return [
|
|
1359
|
+
if (source === "codex") return [join7(codexHome(), TRANSCRIPT_ROOT_NAME.codex)];
|
|
1360
|
+
if (source === "cursor") return [join7(home, ".cursor", TRANSCRIPT_ROOT_NAME.cursor)];
|
|
1361
|
+
if (source === "hermes") return [join7(home, ".pushary", TRANSCRIPT_ROOT_NAME.hermes)];
|
|
1362
|
+
if (source === "gemini") return [join7(process.env.GEMINI_CLI_HOME || home, ".gemini", "tmp"), join7(home, ".cache", ".gemini", "tmp")];
|
|
1317
1363
|
const configured = process.env.CLAUDE_CONFIG_DIR;
|
|
1318
|
-
return [
|
|
1364
|
+
return [join7(configured || join7(home, ".claude"), TRANSCRIPT_ROOT_NAME.claude), join7(home, ".claude", TRANSCRIPT_ROOT_NAME.claude)];
|
|
1319
1365
|
};
|
|
1320
1366
|
var isAgentTranscriptRoot = (root, source) => isAbsolute(root) && resolve2(root) === root && basename(root) === TRANSCRIPT_ROOT_NAME[source];
|
|
1321
1367
|
var acceptedTranscriptRoots = (source, registeredRoot) => typeof registeredRoot === "string" && isAgentTranscriptRoot(registeredRoot, source) ? [...transcriptRoots(source), registeredRoot] : transcriptRoots(source);
|
|
@@ -1324,8 +1370,8 @@ var resolvedSourcePath = (path) => {
|
|
|
1324
1370
|
try {
|
|
1325
1371
|
return realpathSync(absolute);
|
|
1326
1372
|
} catch {
|
|
1327
|
-
const parent =
|
|
1328
|
-
return parent === absolute ? absolute :
|
|
1373
|
+
const parent = dirname4(absolute);
|
|
1374
|
+
return parent === absolute ? absolute : join7(resolvedSourcePath(parent), basename(absolute));
|
|
1329
1375
|
}
|
|
1330
1376
|
};
|
|
1331
1377
|
var transcriptRootContaining = (candidate, roots) => {
|
|
@@ -1345,7 +1391,7 @@ var captureRegistration = (source, value, registeredRoot) => {
|
|
|
1345
1391
|
const segments = relative(root, path).split(sep);
|
|
1346
1392
|
if (source === "hermes" && (segments.length !== 2 || !/^[a-f0-9]{64}$/.test(segments[0] ?? "") || basename(path) !== `${sessionId}.json`)) return null;
|
|
1347
1393
|
if (source === "gemini" && (segments.length !== 3 || segments[1] !== "chats" || !basename(path).startsWith("session-"))) return null;
|
|
1348
|
-
if (source === "cursor" && (segments[1] !== "agent-transcripts" || basename(path) !== `${sessionId}.jsonl` || !(segments.length === 3 || segments.length === 4 && basename(
|
|
1394
|
+
if (source === "cursor" && (segments[1] !== "agent-transcripts" || basename(path) !== `${sessionId}.jsonl` || !(segments.length === 3 || segments.length === 4 && basename(dirname4(path)) === sessionId))) return null;
|
|
1349
1395
|
return { sessionId, cwd: input.cwd, path, source, root };
|
|
1350
1396
|
};
|
|
1351
1397
|
var SESSION_END_EVENT = "SessionEnd";
|
|
@@ -1419,7 +1465,7 @@ var startRegisteredTranscripts = (apiKey, machineId, options = {}) => {
|
|
|
1419
1465
|
try {
|
|
1420
1466
|
let unreadable = 0;
|
|
1421
1467
|
const files = readdirSync2(registry.directory).filter((name) => name.endsWith(".json")).flatMap((name) => {
|
|
1422
|
-
const path =
|
|
1468
|
+
const path = join7(registry.directory, name);
|
|
1423
1469
|
try {
|
|
1424
1470
|
const time = statSync3(path).mtimeMs;
|
|
1425
1471
|
if (Date.now() - time > TTL) {
|
|
@@ -1517,15 +1563,20 @@ var startRegisteredTranscripts = (apiKey, machineId, options = {}) => {
|
|
|
1517
1563
|
};
|
|
1518
1564
|
|
|
1519
1565
|
export {
|
|
1566
|
+
recipientPinFile,
|
|
1567
|
+
loadPin,
|
|
1568
|
+
clearPin,
|
|
1569
|
+
fingerprint,
|
|
1570
|
+
verifyTrustedRecipients,
|
|
1571
|
+
assertVerifiedPhone,
|
|
1572
|
+
trustPhoneRecipient,
|
|
1520
1573
|
redactDraft,
|
|
1521
1574
|
captureDisplayDraft,
|
|
1522
1575
|
sessionKeyDirectory,
|
|
1523
1576
|
loadSessionKey,
|
|
1524
1577
|
startDraftPublisher,
|
|
1525
|
-
loadPin,
|
|
1526
|
-
clearPin,
|
|
1527
|
-
fingerprint,
|
|
1528
1578
|
claudeTranscriptPath,
|
|
1579
|
+
snapshotRecipientPinFile,
|
|
1529
1580
|
clearSnapshotRecipientPins,
|
|
1530
1581
|
registeredTranscriptEnabled,
|
|
1531
1582
|
captureRegistration,
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
quoteIfNeeded,
|
|
5
5
|
shellDoubleQuoted,
|
|
6
6
|
writeFileAtomic
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-UEWTZZY4.js";
|
|
8
8
|
import {
|
|
9
9
|
cmdShimScriptOf
|
|
10
10
|
} from "./chunk-ZVU6WMMU.js";
|
|
@@ -798,7 +798,7 @@ You have Pushary MCP tools that reach the user on their phone. Use them proactiv
|
|
|
798
798
|
- Before a task of more than two steps, break it into steps and find the forks. Settle by yourself every fork that a file, a command or a tool call can answer, then ask the rest as one numbered round with your recommended answer against each. A question in the terminal is free; a push is not. When the user is at the terminal, ask the whole round there. When the user is away, send only the fork that stops the run. Decide another fork on your recommendation only if it is reversible, spends nothing and stays inside the task, and list it in your final message; ask the rest once the first answer is back.
|
|
799
799
|
- When you are blocked, stuck, or hit an error you cannot resolve on your own, call \`send_notification\` so the user knows, and call \`ask_user\` if you need a decision to continue.
|
|
800
800
|
- When meaningful work finishes while the user is away, or they requested an alert, call \`send_notification\` so the user knows it is done. Skip trivial completions and duplicate reports to someone already reading. Always pass \`context\` with a \`type\` of "task_complete", "error" or "info": it is what tells Pushary this is a task update, so the user's own setting for where updates land can route it.
|
|
801
|
-
- Confirm notifications can offer lock-screen actions; arbitrary choices and text open the app. The Mac notch
|
|
801
|
+
- Confirm notifications can offer lock-screen actions; arbitrary choices and text open the app. The Mac notch answers questions too.
|
|
802
802
|
- Use \`cancel_question\` to retract a pending question once it is no longer needed.
|
|
803
803
|
- When the user asks to be reminded later, call \`schedule_reminder\` with \`body\` and either \`inMinutes\` or \`at\` with an explicit UTC offset. Confirm the returned time, never guess their timezone, and do not use \`ask_user\` as a reminder. Pushary saves it and delivers after this agent exits. Call with no arguments to list pending reminders or \`cancelReminderId\` to cancel one before dispatch starts.
|
|
804
804
|
- Respect the user's Pushary delivery mode: it decides where an approval is surfaced (phone, terminal, or awareness only), not whether it is required. In Terminal mode do not expect a phone answer; the approval waits in the terminal. If the Pushary hook already gated an action, a single approval is enough, do not ask again.
|