pushary 1.9.73 → 1.9.76
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 +20 -0
- package/dist/{api-JVWXPKEW.js → api-YDCHRRA4.js} +10 -10
- package/dist/bin/pushary-bell-hook.js +27 -25
- package/dist/bin/pushary-bell.js +14 -14
- package/dist/bin/pushary-claude.js +55 -82
- package/dist/bin/pushary-clean.js +37 -37
- package/dist/bin/pushary-codex-bridge.js +24 -24
- package/dist/bin/pushary-codex-hook.js +36 -34
- package/dist/bin/pushary-codex-session.js +8 -8
- package/dist/bin/pushary-codex.js +15 -15
- package/dist/bin/pushary-connect.js +26 -26
- package/dist/bin/pushary-cowork.js +5 -5
- package/dist/bin/pushary-daemon.js +1293 -63
- package/dist/bin/pushary-disconnect.js +31 -31
- package/dist/bin/pushary-display-hook.d.ts +1 -0
- package/dist/bin/pushary-display-hook.js +58 -0
- package/dist/bin/pushary-doctor.js +50 -50
- package/dist/bin/pushary-elicitation-hook.js +29 -27
- package/dist/bin/pushary-gemini-bridge.js +16 -16
- package/dist/bin/pushary-gemini-hook.js +31 -29
- package/dist/bin/pushary-hook.js +31 -29
- package/dist/bin/pushary-login.js +20 -20
- package/dist/bin/pushary-logout.js +30 -30
- package/dist/bin/pushary-mcp.js +14 -14
- package/dist/bin/pushary-mode.js +18 -18
- package/dist/bin/pushary-notification-hook.js +26 -24
- package/dist/bin/pushary-opencode-hook.js +30 -28
- package/dist/bin/pushary-permission-denied-hook.js +31 -29
- package/dist/bin/pushary-permission-hook.js +31 -29
- package/dist/bin/pushary-post-hook.js +32 -29
- package/dist/bin/pushary-prompt-hook.js +32 -29
- package/dist/bin/pushary-session-end-hook.js +28 -26
- package/dist/bin/pushary-session-start-hook.js +36 -34
- package/dist/bin/pushary-setup.js +66 -282
- package/dist/bin/pushary-stats.js +6 -6
- package/dist/bin/pushary-status.js +31 -31
- package/dist/bin/pushary-stop-hook.js +32 -26
- package/dist/bin/pushary-stopfailure-hook.js +26 -24
- package/dist/bin/pushary-suggestions.js +18 -18
- package/dist/bin/pushary-transcript-register.js +18 -16
- package/dist/bin/pushary-transcripts.js +20 -20
- package/dist/bin/pushary-upgrade.js +45 -45
- package/dist/bin/pushary-wait.js +18 -18
- package/dist/bin/pushary.js +7 -7
- package/dist/{chunk-F3WI32HG.js → chunk-2BK2EQOB.js} +7 -7
- package/dist/{chunk-ZBZBYUNZ.js → chunk-2QFYEB3P.js} +5 -5
- package/dist/{chunk-UIF7HWHX.js → chunk-3YU6NIIL.js} +4 -4
- package/dist/{chunk-L2FQBHIR.js → chunk-47TIRGS5.js} +2 -2
- package/dist/{chunk-AXZ67XVN.js → chunk-4LX5QUFV.js} +1 -1
- package/dist/{chunk-YZ6DMGZZ.js → chunk-57MQNSM5.js} +1 -1
- package/dist/{chunk-BLX6KRK5.js → chunk-5FV472Q2.js} +1 -1
- package/dist/{chunk-VBSPP4QY.js → chunk-5KPMDLXB.js} +3 -3
- package/dist/{chunk-S63VIM7E.js → chunk-5M5F4IWR.js} +2 -2
- package/dist/{chunk-7FROPPIH.js → chunk-5NW7VM35.js} +2 -2
- package/dist/{chunk-EITOMPFB.js → chunk-74ONBRPF.js} +4 -4
- package/dist/{chunk-7S46LCKD.js → chunk-7KYQ24FI.js} +3 -2
- package/dist/{chunk-NGNBX3YH.js → chunk-7LLC4R66.js} +4 -4
- package/dist/{chunk-AC5TVOAH.js → chunk-7SFP6Y5U.js} +373 -93
- package/dist/{chunk-CMYFV3VC.js → chunk-7ZOWQBHQ.js} +5 -5
- package/dist/{chunk-2QB7HJVJ.js → chunk-B2PFCS72.js} +2 -2
- package/dist/{chunk-UUCRWEEU.js → chunk-BUR2LYQI.js} +2 -2
- package/dist/{chunk-W47XZJSH.js → chunk-C2AR7QXU.js} +2 -2
- package/dist/{chunk-FK3JSDH5.js → chunk-CHIOUBPB.js} +13 -13
- package/dist/{chunk-TTGBS5JB.js → chunk-CMNB4IZO.js} +3 -3
- package/dist/{chunk-4UIR25GA.js → chunk-CUD6SYDG.js} +1 -1
- package/dist/{chunk-WJJVCDEU.js → chunk-DB3NOEDW.js} +6 -6
- package/dist/{chunk-PGJMSREY.js → chunk-DZF2M3LM.js} +8 -8
- package/dist/{chunk-2NG3ANMT.js → chunk-E2VJ7B7D.js} +9 -9
- package/dist/{chunk-S7VMHSJW.js → chunk-E7U4YM4A.js} +5 -5
- package/dist/{chunk-XKQ7CQGG.js → chunk-F7R7M5MG.js} +1 -1
- package/dist/{chunk-FAKEKFCI.js → chunk-FAGQAR6I.js} +5 -3
- package/dist/{chunk-3JJI5O5N.js → chunk-GII565PB.js} +1 -1
- package/dist/{chunk-WXV7J3DG.js → chunk-GJVV2EUC.js} +3 -3
- package/dist/{chunk-Z7AIYWZ2.js → chunk-IRLHN5UP.js} +236 -20
- package/dist/{chunk-NJSW2REO.js → chunk-IRN6JLSM.js} +4 -4
- package/dist/{chunk-JC7XUZST.js → chunk-ITLWEGLE.js} +31 -5
- package/dist/{chunk-7M4AA7QF.js → chunk-JNV5LOZH.js} +4 -4
- package/dist/{chunk-IN3MPEFA.js → chunk-JZ5KV2FK.js} +5 -5
- package/dist/{chunk-QAWWTRDD.js → chunk-KGWLDNNT.js} +4 -4
- package/dist/{chunk-O4EAMVSC.js → chunk-KWGOKO4E.js} +5 -5
- package/dist/{chunk-KDQLKND5.js → chunk-M5VJ3HXB.js} +6 -6
- package/dist/{chunk-R5PZPROX.js → chunk-M7JFGDGP.js} +1 -1
- package/dist/{chunk-N4VRSFRZ.js → chunk-MAWC4PJV.js} +5 -5
- package/dist/chunk-MHUKYYYR.js +239 -0
- package/dist/{chunk-56GK65Z6.js → chunk-MMVIKXCP.js} +3 -3
- package/dist/{chunk-CGWUVEOB.js → chunk-MTRZQR2R.js} +3 -3
- package/dist/{chunk-JYOR57ZW.js → chunk-NFUV25JZ.js} +3 -3
- package/dist/{chunk-EUHTC7K5.js → chunk-NJTAUEW6.js} +1 -1
- package/dist/{chunk-2JFEMKDG.js → chunk-NQA56AMZ.js} +6 -6
- package/dist/{chunk-AJWGZJFS.js → chunk-NVLKH5FS.js} +131 -28
- package/dist/{chunk-2HFC4W6J.js → chunk-PPD64MZW.js} +4 -3
- package/dist/{chunk-3RAWW5DH.js → chunk-QUGP76DQ.js} +6 -6
- package/dist/{chunk-PPKIL4EL.js → chunk-R3HXGHRQ.js} +1 -1
- package/dist/{chunk-3QBKMSOZ.js → chunk-S7C5NY4R.js} +1 -1
- package/dist/{chunk-EUEGCEA7.js → chunk-SWRNJ3BP.js} +3 -3
- package/dist/{chunk-RZVWEDIS.js → chunk-T2EJ3H6Y.js} +3 -3
- package/dist/{chunk-DT6VCMXS.js → chunk-TFYFQM5C.js} +2 -2
- package/dist/{chunk-GBR6E7AY.js → chunk-UEY4SHY2.js} +1 -1
- package/dist/{chunk-TFDMJHAB.js → chunk-UMBVIFP5.js} +1 -1
- package/dist/{chunk-TQ2L3ECX.js → chunk-USMUW3LI.js} +2 -2
- package/dist/{chunk-VJUDBZGC.js → chunk-USZJJWZL.js} +2 -2
- package/dist/{chunk-4LAD5A2Q.js → chunk-VELPOV2F.js} +4 -4
- package/dist/{chunk-JNTYPMVU.js → chunk-VZSSL6US.js} +10 -10
- package/dist/{chunk-KSLBS3II.js → chunk-W5AUTVVL.js} +6 -5
- package/dist/{chunk-OJ3ZD5VX.js → chunk-WK2L3XVQ.js} +1 -1
- package/dist/{chunk-PM2MHBMF.js → chunk-WLHHDDVA.js} +9 -4
- package/dist/{chunk-U3EBM73X.js → chunk-WRCM4XGY.js} +2 -2
- package/dist/{chunk-SQ5XCMPB.js → chunk-XJ5DYPTM.js} +4 -4
- package/dist/{chunk-PNXNWZ5P.js → chunk-XLGD5LDG.js} +3 -3
- package/dist/{chunk-7RX5TOU3.js → chunk-Y2CYSVWU.js} +6 -6
- package/dist/{chunk-AVHL2ZTS.js → chunk-Y4OBAK4I.js} +459 -172
- package/dist/{chunk-JWBZUTYF.js → chunk-YDPGBBV7.js} +5 -5
- package/dist/{chunk-FB262EOM.js → chunk-YLB4AGZJ.js} +1 -1
- package/dist/{chunk-QMPM3CAN.js → chunk-ZY25SNFQ.js} +1 -1
- package/dist/{codex-app-cards-switch-M7VH7AZH.js → codex-app-cards-switch-3OUTABVU.js} +11 -11
- package/dist/{codex-desktop-launch-6DASTBBA.js → codex-desktop-launch-JH3E2CIA.js} +1 -1
- package/dist/{codex-session-launch-2XSMFA4K.js → codex-session-launch-GR363OQN.js} +18 -18
- package/dist/{config-7QQOIFTW.js → config-R43UFHX7.js} +3 -3
- package/dist/{global-upgrade-XAKX26AN.js → global-upgrade-73GP4MZM.js} +10 -10
- package/dist/{identity-7AS5MBNM.js → identity-CA7CZGDL.js} +4 -4
- package/dist/{live-capabilities-DV4F7MIB.js → live-capabilities-RCGMQIIZ.js} +5 -5
- package/dist/{paths-GSMXYKT5.js → paths-H7VNNB46.js} +3 -3
- package/dist/{reapply-MR7UWBXR.js → reapply-3UIA3CUO.js} +27 -27
- package/dist/src/index.js +32 -30
- package/dist/{upgrade-lock-6OTHLCBZ.js → upgrade-lock-ZTLX5PDF.js} +4 -4
- package/dist/{usage-H53PO3VQ.js → usage-C53NVWC2.js} +3 -3
- package/package.json +4 -2
|
@@ -3,112 +3,218 @@ import {
|
|
|
3
3
|
loadPin,
|
|
4
4
|
savePin,
|
|
5
5
|
verifyRecipients
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-YLB4AGZJ.js";
|
|
7
|
+
import {
|
|
8
|
+
SESSION_HISTORY_RETENTION_DAYS
|
|
9
|
+
} from "./chunk-MHUKYYYR.js";
|
|
7
10
|
import {
|
|
8
11
|
ensureDaemonRunning
|
|
9
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-DB3NOEDW.js";
|
|
10
13
|
import {
|
|
11
14
|
hookOwnerOf
|
|
12
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-KGWLDNNT.js";
|
|
13
16
|
import {
|
|
14
17
|
buildTranscriptRecords,
|
|
15
18
|
decodePublicKey,
|
|
19
|
+
decryptRecord,
|
|
20
|
+
encryptRecord,
|
|
16
21
|
fetchTranscriptRecipients,
|
|
17
22
|
generateSessionKey,
|
|
18
23
|
recordSealerFor,
|
|
19
24
|
uploadTranscriptRecords,
|
|
20
25
|
wrapSessionKey
|
|
21
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-ITLWEGLE.js";
|
|
27
|
+
import {
|
|
28
|
+
external_exports
|
|
29
|
+
} from "./chunk-XMSDA5WA.js";
|
|
22
30
|
import {
|
|
23
31
|
transcriptEnvironmentPreference,
|
|
24
32
|
transcriptsEnabled
|
|
25
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-5M5F4IWR.js";
|
|
26
34
|
import {
|
|
27
35
|
readTranscriptPreference
|
|
28
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-7LLC4R66.js";
|
|
29
37
|
import {
|
|
30
38
|
claudeConfigDir,
|
|
31
39
|
codexHome,
|
|
32
40
|
pusharyDir
|
|
33
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-7KYQ24FI.js";
|
|
34
42
|
import {
|
|
35
43
|
writeJsonAtomic
|
|
36
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-T2EJ3H6Y.js";
|
|
37
45
|
import {
|
|
38
46
|
getApiKey,
|
|
39
47
|
getBaseUrl
|
|
40
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-M7JFGDGP.js";
|
|
49
|
+
import {
|
|
50
|
+
PENDING_COMMAND_PREFIX,
|
|
51
|
+
SESSION_HISTORY_BACKFILL_LIMIT_BYTES,
|
|
52
|
+
redactSecretsFromContent
|
|
53
|
+
} from "./chunk-Y4OBAK4I.js";
|
|
41
54
|
|
|
42
55
|
// src/transcript-registration.ts
|
|
43
56
|
import { createHash } from "crypto";
|
|
44
|
-
import { readFileSync as
|
|
45
|
-
import { basename, isAbsolute, join as
|
|
57
|
+
import { readFileSync as readFileSync4, readdirSync as readdirSync2, statSync as statSync3, unlinkSync as unlinkSync2 } from "fs";
|
|
58
|
+
import { basename, isAbsolute, join as join4, resolve as resolve2, sep } from "path";
|
|
46
59
|
|
|
47
|
-
// src/
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
60
|
+
// src/claude-display-draft.ts
|
|
61
|
+
import { closeSync, fstatSync, lstatSync, openSync, readFileSync, readSync, unlinkSync } from "fs";
|
|
62
|
+
import { join } from "path";
|
|
63
|
+
|
|
64
|
+
// src/transcript-redaction.ts
|
|
65
|
+
var redactDraft = (text, complete = false) => {
|
|
66
|
+
let end = complete ? text.length : text.lastIndexOf("\n") + 1;
|
|
67
|
+
const pem = /-----BEGIN [^\n]*PRIVATE KEY-----/g;
|
|
68
|
+
for (const match of text.matchAll(pem)) {
|
|
69
|
+
if (!/-----END [^\n]*PRIVATE KEY-----/.test(text.slice(match.index, end))) end = Math.min(end, match.index);
|
|
70
|
+
}
|
|
71
|
+
const quoted = /(?:secret|token|password|passwd|api[_-]?key|access[_-]?key|client[_-]?secret|private[_-]?key)\s*[=:]\s*(["'])/gi;
|
|
72
|
+
for (const match of text.matchAll(quoted)) {
|
|
73
|
+
const close = text.indexOf(match[1], match.index + match[0].length);
|
|
74
|
+
if (close < 0 || close >= end) end = Math.min(end, match.index);
|
|
75
|
+
}
|
|
76
|
+
return redactSecretsFromContent(text.slice(0, end));
|
|
61
77
|
};
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
var
|
|
65
|
-
var
|
|
66
|
-
var
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
78
|
+
|
|
79
|
+
// src/claude-display-draft.ts
|
|
80
|
+
var MAX_TEXT = 32 * 1024;
|
|
81
|
+
var MAX_TAIL = 256 * 1024;
|
|
82
|
+
var MAX_AGE_MS = 9e4;
|
|
83
|
+
var identifier = external_exports.string().regex(/^[\w-]{1,128}$/);
|
|
84
|
+
var displaySchema = external_exports.object({
|
|
85
|
+
hook_event_name: external_exports.literal("MessageDisplay"),
|
|
86
|
+
session_id: identifier,
|
|
87
|
+
message_id: identifier,
|
|
88
|
+
index: external_exports.number().int().nonnegative(),
|
|
89
|
+
final: external_exports.boolean(),
|
|
90
|
+
delta: external_exports.string().max(MAX_TEXT),
|
|
91
|
+
transcript_path: external_exports.string().min(1),
|
|
92
|
+
agent_id: external_exports.undefined()
|
|
93
|
+
});
|
|
94
|
+
var containsPhoneInstruction = (content) => content.some((text) => {
|
|
95
|
+
if (typeof text !== "string") return false;
|
|
96
|
+
const prefixAt = text.indexOf(PENDING_COMMAND_PREFIX);
|
|
97
|
+
return prefixAt >= 0 && text.slice(prefixAt + PENDING_COMMAND_PREFIX.length).trim().length > 0;
|
|
98
|
+
});
|
|
99
|
+
var anchorSchema = external_exports.object({ uuid: identifier, isSidechain: external_exports.literal(false).optional() }).and(external_exports.union([
|
|
100
|
+
external_exports.object({ type: external_exports.enum(["user", "assistant"]) }),
|
|
101
|
+
external_exports.object({ type: external_exports.literal("attachment"), attachment: external_exports.object({
|
|
102
|
+
type: external_exports.literal("hook_additional_context"),
|
|
103
|
+
content: external_exports.array(external_exports.unknown()).refine(containsPhoneInstruction)
|
|
104
|
+
}) })
|
|
105
|
+
]));
|
|
106
|
+
var stateSchema = external_exports.object({
|
|
107
|
+
displayId: identifier,
|
|
108
|
+
afterRecordId: identifier,
|
|
109
|
+
index: external_exports.number().int().nonnegative(),
|
|
110
|
+
text: external_exports.string().max(MAX_TEXT),
|
|
111
|
+
complete: external_exports.boolean(),
|
|
112
|
+
startedAt: external_exports.number().int().positive(),
|
|
113
|
+
updatedAt: external_exports.number().int().positive()
|
|
114
|
+
});
|
|
115
|
+
var fileFor = (id, directory) => join(directory, `${id}.display`);
|
|
116
|
+
var readState = (sessionId, runtime) => {
|
|
117
|
+
if (!identifier.safeParse(sessionId).success) return null;
|
|
118
|
+
try {
|
|
119
|
+
const path = fileFor(sessionId, runtime.directory);
|
|
120
|
+
const info = lstatSync(path);
|
|
121
|
+
if (!info.isFile() || info.nlink !== 1 || info.size > MAX_TAIL || (info.mode & 511) !== 384) return null;
|
|
122
|
+
const envelope = external_exports.object({ content: external_exports.string() }).parse(JSON.parse(readFileSync(path, "utf8")));
|
|
123
|
+
const plain = decryptRecord(envelope.content, runtime.sessionKey);
|
|
124
|
+
if (!plain) return null;
|
|
125
|
+
const state = stateSchema.parse(JSON.parse(plain));
|
|
126
|
+
return runtime.now - state.updatedAt <= MAX_AGE_MS && runtime.now >= state.updatedAt ? state : null;
|
|
127
|
+
} catch {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
71
130
|
};
|
|
72
|
-
var
|
|
73
|
-
|
|
74
|
-
var parsedTranscriptRecord = (line) => {
|
|
131
|
+
var conversationAnchor = (path) => {
|
|
132
|
+
let descriptor;
|
|
75
133
|
try {
|
|
76
|
-
|
|
77
|
-
|
|
134
|
+
descriptor = openSync(path, "r");
|
|
135
|
+
const size = fstatSync(descriptor).size;
|
|
136
|
+
const start = Math.max(0, size - MAX_TAIL);
|
|
137
|
+
const buffer = Buffer.alloc(Math.min(size, MAX_TAIL));
|
|
138
|
+
const length = readSync(descriptor, buffer, 0, buffer.length, start);
|
|
139
|
+
const lines = buffer.subarray(0, length).toString("utf8").split("\n");
|
|
140
|
+
if (start > 0) lines.shift();
|
|
141
|
+
lines.pop();
|
|
142
|
+
for (const line of lines.reverse()) {
|
|
143
|
+
try {
|
|
144
|
+
const value = anchorSchema.safeParse(JSON.parse(line));
|
|
145
|
+
if (value.success) return value.data.uuid;
|
|
146
|
+
} catch {
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
78
150
|
} catch {
|
|
79
151
|
return null;
|
|
152
|
+
} finally {
|
|
153
|
+
if (descriptor !== void 0) closeSync(descriptor);
|
|
80
154
|
}
|
|
155
|
+
return null;
|
|
81
156
|
};
|
|
82
|
-
var
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
157
|
+
var captureDisplayDraft = (input, runtime) => {
|
|
158
|
+
const parsed = displaySchema.safeParse(input);
|
|
159
|
+
if (!parsed.success) return false;
|
|
160
|
+
const event = parsed.data;
|
|
161
|
+
const previous = readState(event.session_id, runtime);
|
|
162
|
+
if (previous?.displayId === event.message_id && event.index <= previous.index) return false;
|
|
163
|
+
if (event.index !== 0 && (previous?.displayId !== event.message_id || previous.complete)) return false;
|
|
164
|
+
if (event.index !== 0 && previous && event.index !== previous.index + 1) {
|
|
165
|
+
try {
|
|
166
|
+
unlinkSync(fileFor(event.session_id, runtime.directory));
|
|
167
|
+
} catch {
|
|
168
|
+
}
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
if (event.index === 0 && event.final) return false;
|
|
172
|
+
const anchor = event.index === 0 ? conversationAnchor(event.transcript_path) : previous?.afterRecordId;
|
|
173
|
+
if (!anchor) return false;
|
|
174
|
+
const text = (event.index === 0 ? "" : previous?.text ?? "") + event.delta;
|
|
175
|
+
if (Buffer.byteLength(text, "utf8") > MAX_TEXT) return false;
|
|
176
|
+
const state = {
|
|
177
|
+
displayId: event.message_id,
|
|
178
|
+
afterRecordId: anchor,
|
|
179
|
+
index: event.index,
|
|
180
|
+
text,
|
|
181
|
+
complete: event.final,
|
|
182
|
+
startedAt: event.index === 0 ? runtime.now : previous?.startedAt ?? runtime.now,
|
|
183
|
+
updatedAt: runtime.now
|
|
184
|
+
};
|
|
185
|
+
const target = fileFor(event.session_id, runtime.directory);
|
|
186
|
+
const existing = lstatSync(target, { throwIfNoEntry: false });
|
|
187
|
+
if (existing && (!existing.isFile() || existing.nlink !== 1 || (existing.mode & 511) !== 384)) return false;
|
|
188
|
+
writeJsonAtomic(target, { content: encryptRecord(JSON.stringify(state), runtime.sessionKey) }, 384);
|
|
189
|
+
return true;
|
|
190
|
+
};
|
|
191
|
+
var readDisplayDraft = (sessionId, runtime) => {
|
|
192
|
+
const state = readState(sessionId, runtime);
|
|
193
|
+
if (!state) return null;
|
|
194
|
+
const text = redactDraft(state.text, state.complete);
|
|
195
|
+
if (!text) return null;
|
|
196
|
+
return { startedAt: state.startedAt, revision: state.index + 1, preview: {
|
|
197
|
+
displayId: state.displayId,
|
|
198
|
+
afterRecordId: state.afterRecordId,
|
|
199
|
+
text,
|
|
200
|
+
status: state.complete ? "complete" : "streaming"
|
|
201
|
+
} };
|
|
86
202
|
};
|
|
87
|
-
var isCompactionSummary = (record) => record.isCompactSummary === true;
|
|
88
|
-
var transcriptLinesSeenLive = (lines, epochStartedAtMs, startedBeforeEpoch) => lines.filter((line) => {
|
|
89
|
-
const record = parsedTranscriptRecord(line);
|
|
90
|
-
if (record === null) return true;
|
|
91
|
-
return !stampedBefore(record, epochStartedAtMs) && !(startedBeforeEpoch && isCompactionSummary(record));
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
// src/transcript-sync.ts
|
|
95
|
-
import { join as join2, resolve } from "path";
|
|
96
|
-
import { closeSync, existsSync as existsSync2, fstatSync, openSync, readFileSync as readFileSync2, readSync, statSync as statSync2 } from "fs";
|
|
97
203
|
|
|
98
204
|
// src/transcript-session-key.ts
|
|
99
|
-
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "fs";
|
|
205
|
+
import { existsSync, mkdirSync, readFileSync as readFileSync2, readdirSync, rmSync, statSync, writeFileSync } from "fs";
|
|
100
206
|
import { homedir } from "os";
|
|
101
|
-
import { join } from "path";
|
|
102
|
-
var
|
|
207
|
+
import { join as join2 } from "path";
|
|
208
|
+
var sessionKeyDirectory = () => process.env.PUSHARY_SESSION_KEY_DIR?.trim() || join2(homedir(), ".pushary", "sessions");
|
|
103
209
|
var SESSION_KEY_TTL_MS = 48 * 60 * 60 * 1e3;
|
|
104
210
|
var SAFE_SESSION_ID = /^[A-Za-z0-9._-]{1,128}$/;
|
|
105
|
-
var
|
|
106
|
-
var loadSessionKey = (sessionId, directory =
|
|
107
|
-
const path =
|
|
211
|
+
var fileFor2 = (sessionId, directory) => SAFE_SESSION_ID.test(sessionId) ? join2(directory, `${sessionId}.json`) : null;
|
|
212
|
+
var loadSessionKey = (sessionId, directory = sessionKeyDirectory()) => {
|
|
213
|
+
const path = fileFor2(sessionId, directory);
|
|
108
214
|
if (!path || !existsSync(path)) return null;
|
|
109
215
|
try {
|
|
110
216
|
if (Date.now() - statSync(path).mtimeMs > SESSION_KEY_TTL_MS) return null;
|
|
111
|
-
const raw = JSON.parse(
|
|
217
|
+
const raw = JSON.parse(readFileSync2(path, "utf-8"));
|
|
112
218
|
const key = typeof raw.sessionKey === "string" ? Buffer.from(raw.sessionKey, "base64") : null;
|
|
113
219
|
if (!key || key.length !== 32) return null;
|
|
114
220
|
return {
|
|
@@ -123,8 +229,8 @@ var loadSessionKey = (sessionId, directory = sessionDir()) => {
|
|
|
123
229
|
return null;
|
|
124
230
|
}
|
|
125
231
|
};
|
|
126
|
-
var saveSessionKey = (sessionId, value, directory =
|
|
127
|
-
const path =
|
|
232
|
+
var saveSessionKey = (sessionId, value, directory = sessionKeyDirectory()) => {
|
|
233
|
+
const path = fileFor2(sessionId, directory);
|
|
128
234
|
if (!path) return;
|
|
129
235
|
try {
|
|
130
236
|
mkdirSync(directory, { recursive: true, mode: 448 });
|
|
@@ -143,13 +249,13 @@ var saveSessionKey = (sessionId, value, directory = sessionDir()) => {
|
|
|
143
249
|
} catch {
|
|
144
250
|
}
|
|
145
251
|
};
|
|
146
|
-
var sweepStaleSessionKeys = (now = Date.now(), dir =
|
|
252
|
+
var sweepStaleSessionKeys = (now = Date.now(), dir = sessionKeyDirectory()) => {
|
|
147
253
|
if (!existsSync(dir)) return 0;
|
|
148
254
|
let removed = 0;
|
|
149
255
|
try {
|
|
150
256
|
for (const name of readdirSync(dir)) {
|
|
151
|
-
if (!name.endsWith(".json")) continue;
|
|
152
|
-
const path =
|
|
257
|
+
if (!name.endsWith(".json") && !name.endsWith(".display")) continue;
|
|
258
|
+
const path = join2(dir, name);
|
|
153
259
|
try {
|
|
154
260
|
if (now - statSync(path).mtimeMs <= SESSION_KEY_TTL_MS) continue;
|
|
155
261
|
rmSync(path, { force: true });
|
|
@@ -163,11 +269,160 @@ var sweepStaleSessionKeys = (now = Date.now(), dir = sessionDir()) => {
|
|
|
163
269
|
return removed;
|
|
164
270
|
};
|
|
165
271
|
|
|
272
|
+
// src/transcript-draft-publisher.ts
|
|
273
|
+
var startDraftPublisher = (options) => {
|
|
274
|
+
let running = true;
|
|
275
|
+
let pending;
|
|
276
|
+
let active;
|
|
277
|
+
let sent = "";
|
|
278
|
+
let retryAt = 0;
|
|
279
|
+
let retryMs = 1e3;
|
|
280
|
+
let reportedFailure = false;
|
|
281
|
+
const publish = async () => {
|
|
282
|
+
const controller = new AbortController();
|
|
283
|
+
active = controller;
|
|
284
|
+
try {
|
|
285
|
+
const sessionId = options.sessionId();
|
|
286
|
+
if (!sessionId) return;
|
|
287
|
+
const key = loadSessionKey(sessionId, options.keyDirectory);
|
|
288
|
+
if (!key?.keyUploaded) return;
|
|
289
|
+
const draft = options.snapshot(key.sessionKey);
|
|
290
|
+
if (!draft) return;
|
|
291
|
+
const identity = JSON.stringify([sessionId, draft.startedAt, draft.revision]);
|
|
292
|
+
if (identity === sent) return;
|
|
293
|
+
const response = await fetch(`${options.baseUrl}/api/agent/transcript`, {
|
|
294
|
+
method: "POST",
|
|
295
|
+
headers: { Authorization: `Bearer ${options.apiKey}`, "Content-Type": "application/json" },
|
|
296
|
+
body: JSON.stringify({ sessionId, draft: {
|
|
297
|
+
startedAt: draft.startedAt,
|
|
298
|
+
revision: draft.revision,
|
|
299
|
+
content: encryptRecord(JSON.stringify(draft.preview), key.sessionKey)
|
|
300
|
+
} }),
|
|
301
|
+
signal: AbortSignal.any([controller.signal, AbortSignal.timeout(5e3)])
|
|
302
|
+
});
|
|
303
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
304
|
+
sent = identity;
|
|
305
|
+
retryAt = 0;
|
|
306
|
+
retryMs = 1e3;
|
|
307
|
+
reportedFailure = false;
|
|
308
|
+
} catch (error) {
|
|
309
|
+
retryAt = Date.now() + retryMs;
|
|
310
|
+
retryMs = Math.min(3e4, retryMs * 2);
|
|
311
|
+
if (running && !controller.signal.aborted && !reportedFailure) {
|
|
312
|
+
reportedFailure = true;
|
|
313
|
+
process.stderr.write(`[pushary] live transcript preview could not sync (${error instanceof Error ? error.message : "request failed"}); saved history remains available.
|
|
314
|
+
`);
|
|
315
|
+
}
|
|
316
|
+
} finally {
|
|
317
|
+
active = void 0;
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
const flush = async (final = false) => {
|
|
321
|
+
if (!running) return;
|
|
322
|
+
if (!options.enabled()) {
|
|
323
|
+
active?.abort();
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
if (pending || !final && Date.now() < retryAt) return;
|
|
327
|
+
pending = publish();
|
|
328
|
+
try {
|
|
329
|
+
await pending;
|
|
330
|
+
} finally {
|
|
331
|
+
pending = void 0;
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
const timer = setInterval(() => {
|
|
335
|
+
void flush();
|
|
336
|
+
}, 500);
|
|
337
|
+
timer.unref();
|
|
338
|
+
const stop = () => {
|
|
339
|
+
running = false;
|
|
340
|
+
clearInterval(timer);
|
|
341
|
+
active?.abort();
|
|
342
|
+
};
|
|
343
|
+
return {
|
|
344
|
+
stop,
|
|
345
|
+
finish: async () => {
|
|
346
|
+
clearInterval(timer);
|
|
347
|
+
await pending;
|
|
348
|
+
await flush(true);
|
|
349
|
+
stop();
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
// src/claude-display-publisher.ts
|
|
355
|
+
var startDisplayPublisher = (options) => {
|
|
356
|
+
const directory = options.directory ?? sessionKeyDirectory();
|
|
357
|
+
return startDraftPublisher({
|
|
358
|
+
apiKey: options.apiKey,
|
|
359
|
+
baseUrl: options.baseUrl,
|
|
360
|
+
keyDirectory: directory,
|
|
361
|
+
sessionId: () => options.sessionId,
|
|
362
|
+
enabled: options.enabled,
|
|
363
|
+
snapshot: (sessionKey) => readDisplayDraft(options.sessionId, { directory, sessionKey, now: Date.now() })
|
|
364
|
+
}).stop;
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
// src/transcript-source.ts
|
|
368
|
+
var normalizeTranscriptLines = (lines, source) => {
|
|
369
|
+
if (source === "claude") return [...lines];
|
|
370
|
+
return lines.flatMap((line) => {
|
|
371
|
+
try {
|
|
372
|
+
const record = JSON.parse(line);
|
|
373
|
+
const item = record.payload;
|
|
374
|
+
if (record.type !== "response_item" || item?.type !== "message" || !["user", "assistant"].includes(item.role)) return [];
|
|
375
|
+
const content = (Array.isArray(item.content) ? item.content : []).flatMap((block) => ["input_text", "output_text"].includes(block.type ?? "") && typeof block.text === "string" ? [{ type: "text", text: block.text }] : []);
|
|
376
|
+
return content.length ? [JSON.stringify({ timestamp: record.timestamp, message: { role: item.role, content } })] : [];
|
|
377
|
+
} catch {
|
|
378
|
+
return [];
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
};
|
|
382
|
+
var createdDuringEpoch = (sighting, epochStartedAtMs) => sighting.birthtimeMs !== null && sighting.birthtimeMs >= epochStartedAtMs && sighting.birthtimeMs <= sighting.seenAtMs;
|
|
383
|
+
var SESSION_HISTORY_BACKFILL = { limitBytes: SESSION_HISTORY_BACKFILL_LIMIT_BYTES };
|
|
384
|
+
var MS_PER_DAY = 864e5;
|
|
385
|
+
var everythingAlreadyWritten = (sighting) => ({ identity: sighting.identity, offset: sighting.sizeBytes, startedBeforeEpoch: true });
|
|
386
|
+
var earlierConversationStart = (sighting, backfill) => backfill === null ? everythingAlreadyWritten(sighting) : { identity: sighting.identity, offset: Math.max(0, sighting.sizeBytes - backfill.limitBytes), startedBeforeEpoch: true };
|
|
387
|
+
var cutoffAtFirstSight = (sighting, epochStartedAtMs, backfill) => sighting.identity === null || createdDuringEpoch(sighting, epochStartedAtMs) ? { identity: sighting.identity, offset: 0, startedBeforeEpoch: false } : earlierConversationStart(sighting, backfill);
|
|
388
|
+
var replacedOrShrunk = (previous, sighting) => previous.identity !== sighting.identity || sighting.sizeBytes < previous.offset;
|
|
389
|
+
var nextTranscriptCutoff = (previous, sighting, epochStartedAtMs, backfill = null) => {
|
|
390
|
+
if (!previous) return cutoffAtFirstSight(sighting, epochStartedAtMs, backfill);
|
|
391
|
+
if (sighting.identity === null) return previous;
|
|
392
|
+
if (previous.identity === null) return { ...previous, identity: sighting.identity };
|
|
393
|
+
return replacedOrShrunk(previous, sighting) ? everythingAlreadyWritten(sighting) : previous;
|
|
394
|
+
};
|
|
395
|
+
var advancedTranscriptCutoff = (current, readFrom, settledThroughOffset) => current !== void 0 && current.identity === readFrom.identity && settledThroughOffset > current.offset ? { ...current, offset: settledThroughOffset } : current;
|
|
396
|
+
var isTranscriptObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
397
|
+
var parsedTranscriptRecord = (line) => {
|
|
398
|
+
try {
|
|
399
|
+
const value = JSON.parse(line);
|
|
400
|
+
return isTranscriptObject(value) ? value : null;
|
|
401
|
+
} catch {
|
|
402
|
+
return null;
|
|
403
|
+
}
|
|
404
|
+
};
|
|
405
|
+
var stampedBefore = (record, momentMs) => {
|
|
406
|
+
if (typeof record.timestamp !== "string") return false;
|
|
407
|
+
const stamped = Date.parse(record.timestamp);
|
|
408
|
+
return Number.isFinite(stamped) && stamped < momentMs;
|
|
409
|
+
};
|
|
410
|
+
var isCompactionSummary = (record) => record.isCompactSummary === true;
|
|
411
|
+
var oldestTranscriptLineMs = (epochStartedAtMs, backfill, retentionDays, nowMs) => backfill === null ? epochStartedAtMs : nowMs - retentionDays * MS_PER_DAY;
|
|
412
|
+
var dropsCompactionSummaries = (cutoff, backfill) => cutoff.startedBeforeEpoch && backfill === null;
|
|
413
|
+
var transcriptLinesSince = (lines, oldestLineMs, dropCompactionSummaries) => lines.filter((line) => {
|
|
414
|
+
const record = parsedTranscriptRecord(line);
|
|
415
|
+
if (record === null) return true;
|
|
416
|
+
return !stampedBefore(record, oldestLineMs) && !(dropCompactionSummaries && isCompactionSummary(record));
|
|
417
|
+
});
|
|
418
|
+
|
|
166
419
|
// src/transcript-sync.ts
|
|
420
|
+
import { join as join3, resolve } from "path";
|
|
421
|
+
import { closeSync as closeSync2, existsSync as existsSync2, fstatSync as fstatSync2, openSync as openSync2, readFileSync as readFileSync3, readSync as readSync2, statSync as statSync2 } from "fs";
|
|
167
422
|
var claudeTranscriptPath = (cwd, sessionId) => {
|
|
168
423
|
const projectId = resolve(cwd).replace(/[^a-zA-Z0-9-]/g, "-");
|
|
169
424
|
const configDir = claudeConfigDir();
|
|
170
|
-
return
|
|
425
|
+
return join3(configDir, "projects", projectId, `${sessionId}.jsonl`);
|
|
171
426
|
};
|
|
172
427
|
var createSyncState = () => ({
|
|
173
428
|
sessionKey: null,
|
|
@@ -179,7 +434,8 @@ var createSyncState = () => ({
|
|
|
179
434
|
keyUploaded: false,
|
|
180
435
|
keyRejected: false,
|
|
181
436
|
keyUnavailable: false,
|
|
182
|
-
unsettled: 0
|
|
437
|
+
unsettled: 0,
|
|
438
|
+
retentionDays: null
|
|
183
439
|
});
|
|
184
440
|
var withSessionKey = async (state, uploader) => {
|
|
185
441
|
if (state.sessionKey || state.keyRejected) return state;
|
|
@@ -192,7 +448,8 @@ var withSessionKey = async (state, uploader) => {
|
|
|
192
448
|
wrappedKey: wrapSessionKey(sessionKey, decodePublicKey(recipients.phonePublicKey)),
|
|
193
449
|
providerWrappedKey: recipients.provider ? wrapSessionKey(sessionKey, decodePublicKey(recipients.provider.publicKey)) : state.providerWrappedKey,
|
|
194
450
|
providerKeyId: recipients.provider ? recipients.provider.keyId : state.providerKeyId,
|
|
195
|
-
compressRecords: recipients.phoneReadsCompressedRecords
|
|
451
|
+
compressRecords: recipients.phoneReadsCompressedRecords,
|
|
452
|
+
retentionDays: recipients.retentionDays
|
|
196
453
|
};
|
|
197
454
|
};
|
|
198
455
|
var syncTick = async (lines, state, uploader, sessionId, machineId) => {
|
|
@@ -245,7 +502,8 @@ var syncTick = async (lines, state, uploader, sessionId, machineId) => {
|
|
|
245
502
|
keyUploaded,
|
|
246
503
|
keyRejected,
|
|
247
504
|
keyUnavailable,
|
|
248
|
-
unsettled: built.records.filter((record) => !seen.has(record.localId)).length
|
|
505
|
+
unsettled: built.records.filter((record) => !seen.has(record.localId)).length,
|
|
506
|
+
retentionDays: keyed.retentionDays
|
|
249
507
|
};
|
|
250
508
|
};
|
|
251
509
|
var MAX_RECORDS_PER_BATCH = 200;
|
|
@@ -269,12 +527,15 @@ var chunkRecords = (records) => {
|
|
|
269
527
|
};
|
|
270
528
|
var readLines = (path) => {
|
|
271
529
|
try {
|
|
272
|
-
return existsSync2(path) ?
|
|
530
|
+
return existsSync2(path) ? readFileSync3(path, "utf-8").split("\n") : [];
|
|
273
531
|
} catch {
|
|
274
532
|
return [];
|
|
275
533
|
}
|
|
276
534
|
};
|
|
277
535
|
var IDLE_CEILING_MS = 6e4;
|
|
536
|
+
var readPositionsKeptInMemory = { load: () => /* @__PURE__ */ new Map(), save: () => {
|
|
537
|
+
} };
|
|
538
|
+
var startTranscriptCaptureEpoch = (startedAtMs, positions = readPositionsKeptInMemory, backfill = null) => ({ startedAtMs, cutoffs: new Map(positions.load()), positions, backfill });
|
|
278
539
|
var recordedBirthtime = (info) => process.platform === "darwin" && info.birthtimeMs > 0 ? info.birthtimeMs : null;
|
|
279
540
|
var sightingOf = (info, seenAtMs) => info ? { identity: `${info.dev}:${info.ino}`, sizeBytes: info.size, birthtimeMs: recordedBirthtime(info), seenAtMs } : { identity: null, sizeBytes: 0, birthtimeMs: null, seenAtMs };
|
|
280
541
|
var storeCutoff = (epoch, path, cutoff) => {
|
|
@@ -283,7 +544,7 @@ var storeCutoff = (epoch, path, cutoff) => {
|
|
|
283
544
|
};
|
|
284
545
|
var recordSighting = (epoch, path, sighting) => {
|
|
285
546
|
const previous = epoch.cutoffs.get(path);
|
|
286
|
-
const cutoff = nextTranscriptCutoff(previous, sighting, epoch.startedAtMs);
|
|
547
|
+
const cutoff = nextTranscriptCutoff(previous, sighting, epoch.startedAtMs, epoch.backfill);
|
|
287
548
|
if (cutoff !== previous) storeCutoff(epoch, path, cutoff);
|
|
288
549
|
return cutoff;
|
|
289
550
|
};
|
|
@@ -315,7 +576,7 @@ var readThroughALineEnd = (descriptor, offset, unread) => {
|
|
|
315
576
|
let window = Math.min(unread, MAXIMUM_TRANSCRIPT_READ_BYTES);
|
|
316
577
|
for (; ; ) {
|
|
317
578
|
const buffer = Buffer.alloc(window);
|
|
318
|
-
const received = buffer.subarray(0,
|
|
579
|
+
const received = buffer.subarray(0, readSync2(descriptor, buffer, 0, window, offset));
|
|
319
580
|
if (window >= unread || received.length < window || received.includes(LINE_FEED)) return received;
|
|
320
581
|
window = Math.min(unread, window * 2);
|
|
321
582
|
}
|
|
@@ -323,12 +584,12 @@ var readThroughALineEnd = (descriptor, offset, unread) => {
|
|
|
323
584
|
var readLinesSinceCutoff = (epoch, path, seenAtMs) => {
|
|
324
585
|
let descriptor;
|
|
325
586
|
try {
|
|
326
|
-
descriptor =
|
|
587
|
+
descriptor = openSync2(path, "r");
|
|
327
588
|
} catch {
|
|
328
589
|
return nothingUnread(sightTranscript(epoch, path, seenAtMs));
|
|
329
590
|
}
|
|
330
591
|
try {
|
|
331
|
-
const info =
|
|
592
|
+
const info = fstatSync2(descriptor);
|
|
332
593
|
const cutoff = recordSighting(epoch, path, sightingOf(info, seenAtMs));
|
|
333
594
|
const unread = info.size - cutoff.offset;
|
|
334
595
|
if (unread <= 0) return nothingUnread(cutoff);
|
|
@@ -342,12 +603,16 @@ var readLinesSinceCutoff = (epoch, path, seenAtMs) => {
|
|
|
342
603
|
reachedEnd
|
|
343
604
|
};
|
|
344
605
|
} finally {
|
|
345
|
-
|
|
606
|
+
closeSync2(descriptor);
|
|
346
607
|
}
|
|
347
608
|
};
|
|
348
|
-
var linesSeenDuringEpoch = (epoch, path, source, seenAtMs) => {
|
|
609
|
+
var linesSeenDuringEpoch = (epoch, path, source, seenAtMs, retentionDays) => {
|
|
349
610
|
const read2 = readLinesSinceCutoff(epoch, path, seenAtMs);
|
|
350
|
-
|
|
611
|
+
const oldestLineMs = oldestTranscriptLineMs(epoch.startedAtMs, epoch.backfill, retentionDays, seenAtMs);
|
|
612
|
+
return {
|
|
613
|
+
...read2,
|
|
614
|
+
lines: transcriptLinesSince(normalizeTranscriptLines(read2.lines, source), oldestLineMs, dropsCompactionSummaries(read2.cutoff, epoch.backfill))
|
|
615
|
+
};
|
|
351
616
|
};
|
|
352
617
|
var nothingLeftToUpload = (state) => state.sessionKey !== null && !state.keyRejected && state.unsettled === 0;
|
|
353
618
|
var uploadStalled = (before, after) => after.sessionKey !== null && !after.keyRejected && after.unsettled > 0 && after.seen.size === before.seen.size;
|
|
@@ -412,7 +677,8 @@ var startTranscriptSync = (opts) => {
|
|
|
412
677
|
const previous = state;
|
|
413
678
|
const modified = existsSync2(path) ? statSync2(path).mtimeMs : null;
|
|
414
679
|
const source = opts.source ?? "claude";
|
|
415
|
-
const
|
|
680
|
+
const retentionDays = state.retentionDays ?? SESSION_HISTORY_RETENTION_DAYS.standard;
|
|
681
|
+
const read2 = opts.epoch ? linesSeenDuringEpoch(opts.epoch, path, source, Date.now(), retentionDays) : null;
|
|
416
682
|
const lines = read2?.lines ?? normalizeTranscriptLines(readLines(path), source);
|
|
417
683
|
state = await syncTick(lines, state, uploader, opts.sessionId, opts.machineId);
|
|
418
684
|
stalled = uploadStalled(previous, state);
|
|
@@ -443,8 +709,16 @@ var startTranscriptSync = (opts) => {
|
|
|
443
709
|
}, delayMs);
|
|
444
710
|
if (typeof timer.unref === "function") timer.unref();
|
|
445
711
|
};
|
|
712
|
+
const stopDisplay = (opts.source ?? "claude") === "claude" ? startDisplayPublisher({
|
|
713
|
+
apiKey: opts.apiKey,
|
|
714
|
+
sessionId: opts.sessionId,
|
|
715
|
+
baseUrl: opts.baseUrl ?? getBaseUrl(),
|
|
716
|
+
directory: opts.storage?.sessionKeyDirectory,
|
|
717
|
+
enabled: () => running && !state.keyRejected && (opts.enabled ? opts.enabled() : transcriptsEnabled())
|
|
718
|
+
}) : void 0;
|
|
446
719
|
schedule(0);
|
|
447
720
|
return () => {
|
|
721
|
+
stopDisplay?.();
|
|
448
722
|
running = false;
|
|
449
723
|
if (timer) clearTimeout(timer);
|
|
450
724
|
};
|
|
@@ -452,30 +726,30 @@ var startTranscriptSync = (opts) => {
|
|
|
452
726
|
|
|
453
727
|
// src/transcript-registration.ts
|
|
454
728
|
import { homedir as homedir2 } from "os";
|
|
455
|
-
var defaultRegistryDirectory = () => process.env.PUSHARY_TRANSCRIPT_REGISTRY_DIR ||
|
|
729
|
+
var defaultRegistryDirectory = () => process.env.PUSHARY_TRANSCRIPT_REGISTRY_DIR || join4(pusharyDir(), "transcript-capture");
|
|
456
730
|
var cliRegistry = (apiKey = getApiKey()) => ({ apiKey, baseUrl: getBaseUrl(), directory: defaultRegistryDirectory() });
|
|
457
731
|
var accountFor = (registry) => createHash("sha256").update(`${registry.baseUrl}\0${registry.apiKey}`).digest("hex");
|
|
458
732
|
var TTL = 48 * 60 * 60 * 1e3;
|
|
459
|
-
var read = (path) => JSON.parse(
|
|
460
|
-
var consent = (record) => record.consentOverride ?? readTranscriptPreference() ??
|
|
733
|
+
var read = (path) => JSON.parse(readFileSync4(path, "utf8"));
|
|
734
|
+
var consent = (record) => record.consentOverride ?? readTranscriptPreference() ?? true;
|
|
461
735
|
var withoutTrailingSlash = (url) => url.replace(/\/+$/, "");
|
|
462
736
|
var sameServer = (url, registry) => withoutTrailingSlash(url) === withoutTrailingSlash(registry.baseUrl);
|
|
463
|
-
var
|
|
737
|
+
var fileFor3 = (registry, sessionId) => join4(registry.directory, `${createHash("sha256").update(accountFor(registry) + sessionId).digest("hex")}.json`);
|
|
464
738
|
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));
|
|
465
739
|
var registeredTranscriptEnabled = (apiKey, sessionId, registry = cliRegistry(apiKey)) => {
|
|
466
740
|
try {
|
|
467
|
-
return permitted(registry, read(
|
|
741
|
+
return permitted(registry, read(fileFor3(registry, sessionId)));
|
|
468
742
|
} catch {
|
|
469
743
|
return false;
|
|
470
744
|
}
|
|
471
745
|
};
|
|
472
746
|
var macOwnsClaudeHooks = (cwd) => [
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
747
|
+
join4(claudeConfigDir(), "settings.json"),
|
|
748
|
+
join4(cwd, ".claude", "settings.json"),
|
|
749
|
+
join4(cwd, ".claude", "settings.local.json")
|
|
476
750
|
].some((path) => {
|
|
477
751
|
try {
|
|
478
|
-
return hookOwnerOf(JSON.parse(
|
|
752
|
+
return hookOwnerOf(JSON.parse(readFileSync4(path, "utf8"))) === "app";
|
|
479
753
|
} catch {
|
|
480
754
|
return false;
|
|
481
755
|
}
|
|
@@ -483,9 +757,9 @@ var macOwnsClaudeHooks = (cwd) => [
|
|
|
483
757
|
var TRANSCRIPT_ROOT_NAME = { claude: "projects", codex: "sessions" };
|
|
484
758
|
var transcriptRoots = (source) => {
|
|
485
759
|
const home = homedir2();
|
|
486
|
-
if (source === "codex") return [
|
|
760
|
+
if (source === "codex") return [join4(codexHome(), TRANSCRIPT_ROOT_NAME.codex)];
|
|
487
761
|
const configured = process.env.CLAUDE_CONFIG_DIR;
|
|
488
|
-
return [
|
|
762
|
+
return [join4(configured || join4(home, ".claude"), TRANSCRIPT_ROOT_NAME.claude), join4(home, ".claude", TRANSCRIPT_ROOT_NAME.claude)];
|
|
489
763
|
};
|
|
490
764
|
var isAgentTranscriptRoot = (root, source) => isAbsolute(root) && resolve2(root) === root && basename(root) === TRANSCRIPT_ROOT_NAME[source];
|
|
491
765
|
var acceptedTranscriptRoots = (source, registeredRoot) => typeof registeredRoot === "string" && isAgentTranscriptRoot(registeredRoot, source) ? [...transcriptRoots(source), registeredRoot] : transcriptRoots(source);
|
|
@@ -513,7 +787,7 @@ var registerTranscript = (source, input, startDaemon = () => {
|
|
|
513
787
|
try {
|
|
514
788
|
const registry = options.registry ?? cliRegistry();
|
|
515
789
|
const account = accountFor(registry);
|
|
516
|
-
const path =
|
|
790
|
+
const path = fileFor3(registry, registration.sessionId);
|
|
517
791
|
let previous;
|
|
518
792
|
try {
|
|
519
793
|
previous = read(path);
|
|
@@ -549,7 +823,8 @@ var sightRegisteredTranscripts = (epoch, registry, listing) => {
|
|
|
549
823
|
if (listing.complete) forgetUnregisteredTranscripts(epoch, registeredPaths);
|
|
550
824
|
};
|
|
551
825
|
var startRegisteredTranscripts = (apiKey, machineId, options = {}) => {
|
|
552
|
-
const { intervalMs = 3e3, enabled = () => true, storage
|
|
826
|
+
const { intervalMs = 3e3, enabled = () => true, storage } = options;
|
|
827
|
+
const epoch = options.epoch ?? startTranscriptCaptureEpoch(Date.now(), void 0, SESSION_HISTORY_BACKFILL);
|
|
553
828
|
const registry = { apiKey, baseUrl: options.baseUrl ?? getBaseUrl(), directory: options.registryDirectory ?? defaultRegistryDirectory() };
|
|
554
829
|
const active = /* @__PURE__ */ new Map();
|
|
555
830
|
let stopped = false;
|
|
@@ -563,11 +838,11 @@ var startRegisteredTranscripts = (apiKey, machineId, options = {}) => {
|
|
|
563
838
|
try {
|
|
564
839
|
let unreadable = 0;
|
|
565
840
|
const files = readdirSync2(registry.directory).filter((name) => name.endsWith(".json")).flatMap((name) => {
|
|
566
|
-
const path =
|
|
841
|
+
const path = join4(registry.directory, name);
|
|
567
842
|
try {
|
|
568
843
|
const time = statSync3(path).mtimeMs;
|
|
569
844
|
if (Date.now() - time > TTL) {
|
|
570
|
-
|
|
845
|
+
unlinkSync2(path);
|
|
571
846
|
return [];
|
|
572
847
|
}
|
|
573
848
|
return [{ path, time, raw: read(path) }];
|
|
@@ -657,9 +932,14 @@ var startRegisteredTranscripts = (apiKey, machineId, options = {}) => {
|
|
|
657
932
|
};
|
|
658
933
|
|
|
659
934
|
export {
|
|
935
|
+
redactDraft,
|
|
936
|
+
captureDisplayDraft,
|
|
937
|
+
sessionKeyDirectory,
|
|
660
938
|
loadSessionKey,
|
|
939
|
+
startDraftPublisher,
|
|
661
940
|
claudeTranscriptPath,
|
|
662
941
|
registeredTranscriptEnabled,
|
|
942
|
+
captureRegistration,
|
|
663
943
|
registerTranscript,
|
|
664
944
|
startRegisteredTranscripts
|
|
665
945
|
};
|