@pushary/agent-hooks 1.9.44 → 1.9.45
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 +6 -0
- package/README.md +1 -1
- package/dist/bin/pushary-bell-hook.js +25 -26
- package/dist/bin/pushary-bell.js +9 -9
- package/dist/bin/pushary-claude.js +32 -34
- package/dist/bin/pushary-clean.js +30 -31
- package/dist/bin/pushary-codex-bridge.js +22 -690
- package/dist/bin/pushary-codex-hook.js +32 -34
- package/dist/bin/pushary-codex-session.js +437 -41
- package/dist/bin/pushary-codex.js +12 -13
- package/dist/bin/pushary-connect.js +24 -25
- package/dist/bin/pushary-cowork.js +5 -5
- package/dist/bin/pushary-daemon.js +42 -43
- package/dist/bin/pushary-disconnect.js +24 -25
- package/dist/bin/pushary-doctor.js +35 -36
- package/dist/bin/pushary-elicitation-hook.js +25 -26
- package/dist/bin/pushary-gemini-bridge.js +22 -22
- package/dist/bin/pushary-gemini-hook.js +29 -31
- package/dist/bin/pushary-hook.js +30 -31
- package/dist/bin/pushary-login.js +13 -13
- package/dist/bin/pushary-logout.js +24 -25
- package/dist/bin/pushary-mcp.js +9 -9
- package/dist/bin/pushary-mode.js +12 -12
- package/dist/bin/pushary-notification-hook.js +24 -25
- package/dist/bin/pushary-opencode-hook.js +28 -30
- package/dist/bin/pushary-permission-denied-hook.js +29 -30
- package/dist/bin/pushary-permission-hook.js +28 -29
- package/dist/bin/pushary-post-hook.js +24 -25
- package/dist/bin/pushary-prompt-hook.js +24 -25
- package/dist/bin/pushary-session-end-hook.js +24 -25
- package/dist/bin/pushary-session-start-hook.js +29 -30
- package/dist/bin/pushary-setup.js +37 -38
- package/dist/bin/pushary-stats.js +5 -5
- package/dist/bin/pushary-status.js +27 -28
- package/dist/bin/pushary-stop-hook.js +24 -25
- package/dist/bin/pushary-stopfailure-hook.js +24 -25
- package/dist/bin/pushary-suggestions.js +12 -12
- package/dist/bin/pushary-transcript-register.js +17 -17
- package/dist/bin/pushary-transcripts.js +7 -7
- package/dist/bin/pushary-upgrade.js +31 -32
- package/dist/bin/pushary-wait.js +12 -12
- package/dist/bin/pushary.js +6 -6
- package/dist/{chunk-MIBCRQWK.js → chunk-2DORVRDB.js} +2 -2
- package/dist/{chunk-KILK6SWZ.js → chunk-2FUHSKXH.js} +4 -4
- package/dist/{chunk-UY5E2UUW.js → chunk-2SXXH6JY.js} +5 -5
- package/dist/{chunk-UJXLVH66.js → chunk-335ZDJ7R.js} +1 -1
- package/dist/{chunk-OZ7MLVDM.js → chunk-3PM62C2R.js} +8 -8
- package/dist/{chunk-GRAPJKDK.js → chunk-46F4BRUQ.js} +1 -1
- package/dist/{chunk-WMO2XVG4.js → chunk-4A6VUVXV.js} +1 -1
- package/dist/{chunk-T3KMJI4W.js → chunk-57M4ERJK.js} +2 -2
- package/dist/{chunk-SI2NC6UU.js → chunk-5DCZL3BC.js} +3 -3
- package/dist/chunk-6JPDAUFP.js +866 -0
- package/dist/{chunk-S2ALW446.js → chunk-6TYKZ4DN.js} +3 -3
- package/dist/{chunk-WBD7HZEZ.js → chunk-7SGYCU5C.js} +1 -1
- package/dist/{chunk-KYLY6UMN.js → chunk-7URSJMAB.js} +1 -1
- package/dist/{chunk-T4CKP47U.js → chunk-ASK2MSD2.js} +5 -5
- package/dist/{chunk-W2TA4EVZ.js → chunk-AUOSFZJ3.js} +7 -7
- package/dist/{chunk-TWATOPHK.js → chunk-BT57FUMT.js} +2 -2
- package/dist/{chunk-GOEIW2VH.js → chunk-CDERSTBO.js} +182 -39
- package/dist/{chunk-M2L7625S.js → chunk-CFMCV6Z7.js} +2 -2
- package/dist/{chunk-FI2F5KZJ.js → chunk-DSGB5JLG.js} +4 -4
- package/dist/{chunk-2NVXFWQY.js → chunk-ECFEYB6Z.js} +1 -1
- package/dist/{chunk-DD6IBKEJ.js → chunk-EH5ZDG6V.js} +4 -4
- package/dist/{chunk-PQAFH4DF.js → chunk-EKGQEDQ4.js} +2 -2
- package/dist/{chunk-OWQOMGBF.js → chunk-EWC4P4ZL.js} +1 -1
- package/dist/{chunk-LEHBWBCG.js → chunk-FOUK4CBO.js} +4 -4
- package/dist/{chunk-4WBAHVBK.js → chunk-GQ5KCVGK.js} +6 -6
- package/dist/{chunk-BKK3JCTD.js → chunk-GZSSAY6N.js} +12 -14
- package/dist/{chunk-PGHN5D4N.js → chunk-H4ODQD3Y.js} +2 -2
- package/dist/{chunk-CAJCCQ7N.js → chunk-HHX6IRSH.js} +1 -1
- package/dist/{chunk-3IRC3XFG.js → chunk-IFT3GU7Y.js} +2 -2
- package/dist/{chunk-UC4PNLMK.js → chunk-IKYI63RF.js} +6 -6
- package/dist/{chunk-4JHEZ2PV.js → chunk-IOVJVGIT.js} +4 -4
- package/dist/{chunk-JNU3LV47.js → chunk-K7EGWMDU.js} +18 -3
- package/dist/{chunk-CESLE4UN.js → chunk-KS7NA5CK.js} +7 -7
- package/dist/{chunk-IWHDNMM3.js → chunk-N2K3BSCA.js} +5 -5
- package/dist/{chunk-HV4TEKNP.js → chunk-NJN5NRHZ.js} +4 -4
- package/dist/{chunk-6EV3B5CD.js → chunk-OHFC3GJY.js} +1 -1
- package/dist/{chunk-SWDPCDLT.js → chunk-P2F3JUFX.js} +4 -4
- package/dist/{chunk-TKTS3SIM.js → chunk-PWUIPQ2I.js} +3 -3
- package/dist/{chunk-ALY7ZBNB.js → chunk-QQXP3CQA.js} +1 -1
- package/dist/{chunk-FYNMFSIZ.js → chunk-R7ZQHAVL.js} +2 -2
- package/dist/{chunk-FKDZU6YX.js → chunk-RBJWPUI6.js} +4 -4
- package/dist/{chunk-MRQJCIFO.js → chunk-RFQWB5ZV.js} +1 -1
- package/dist/{chunk-XPQDBTW4.js → chunk-S6EGJBI5.js} +1 -1
- package/dist/{chunk-53UZN7JW.js → chunk-SFQSOBLM.js} +3 -3
- package/dist/{chunk-75M7NFFC.js → chunk-SIN7NJD7.js} +1 -1
- package/dist/{chunk-OSMNLNPF.js → chunk-U45OINYS.js} +1 -1
- package/dist/{chunk-E76QCZIA.js → chunk-U6Z56IQS.js} +2 -2
- package/dist/{chunk-GBCR2FH2.js → chunk-VO6XI6AX.js} +3 -3
- package/dist/{chunk-7UM4NCUI.js → chunk-WDIX4XZH.js} +2 -2
- package/dist/{chunk-CEZTYTBN.js → chunk-WM76AN4X.js} +11 -13
- package/dist/{chunk-L2ZM7LXN.js → chunk-WPPKXVV3.js} +2 -2
- package/dist/{chunk-RGBTKOCC.js → chunk-XFHISCJM.js} +5 -5
- package/dist/{chunk-TJZUQRMH.js → chunk-YK7FGPYS.js} +3 -3
- package/dist/{chunk-42CVAOC5.js → chunk-YWIFK2UB.js} +2 -2
- package/dist/{chunk-UZVF6MP2.js → chunk-YXGWG5PO.js} +5 -5
- package/dist/{chunk-OR6KJOHE.js → chunk-ZSY6BWUD.js} +2 -2
- package/dist/{global-upgrade-QSSDYZCZ.js → global-upgrade-U6NWZA5W.js} +6 -6
- package/dist/{paths-55DDFLY4.js → paths-IQTB3ZZI.js} +2 -2
- package/dist/{reapply-MEU66ALO.js → reapply-LILGWJO3.js} +21 -22
- package/dist/src/index.js +19 -20
- package/dist/{upgrade-lock-YLXAU4AD.js → upgrade-lock-2BVFIDU4.js} +3 -3
- package/package.json +1 -1
- package/dist/chunk-5ZSBQHCM.js +0 -161
- package/dist/chunk-SVCT5D7K.js +0 -196
|
@@ -1,36 +1,50 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
exitOnHelpFlag
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-ZSY6BWUD.js";
|
|
5
5
|
import {
|
|
6
|
-
CodexNativeApprovals
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
CodexNativeApprovals,
|
|
7
|
+
answerCodexElicitation,
|
|
8
|
+
answerCodexQuestions,
|
|
9
|
+
claimOwnedQuestion,
|
|
10
|
+
closesOwnedQuestion,
|
|
11
|
+
codexQuestionOwner,
|
|
12
|
+
planCodexElicitation,
|
|
13
|
+
reportOwnedQuestion
|
|
14
|
+
} from "../chunk-6JPDAUFP.js";
|
|
15
|
+
import "../chunk-EKGQEDQ4.js";
|
|
9
16
|
import {
|
|
10
17
|
resolveProviderBinary
|
|
11
|
-
} from "../chunk-
|
|
18
|
+
} from "../chunk-WPPKXVV3.js";
|
|
12
19
|
import "../chunk-RIVNNBEK.js";
|
|
13
|
-
import "../chunk-
|
|
14
|
-
import "../chunk-
|
|
20
|
+
import "../chunk-H4ODQD3Y.js";
|
|
21
|
+
import "../chunk-SIN7NJD7.js";
|
|
22
|
+
import "../chunk-RFQWB5ZV.js";
|
|
23
|
+
import "../chunk-XHKBHWLX.js";
|
|
15
24
|
import "../chunk-6ICPEJY5.js";
|
|
16
|
-
import
|
|
25
|
+
import {
|
|
26
|
+
writeDegraded
|
|
27
|
+
} from "../chunk-CDERSTBO.js";
|
|
17
28
|
import "../chunk-POFBE6ZY.js";
|
|
18
|
-
import "../chunk-
|
|
19
|
-
import "../chunk-
|
|
20
|
-
import "../chunk-
|
|
29
|
+
import "../chunk-SFQSOBLM.js";
|
|
30
|
+
import "../chunk-PWUIPQ2I.js";
|
|
31
|
+
import "../chunk-6TYKZ4DN.js";
|
|
21
32
|
import {
|
|
22
33
|
getMachineId
|
|
23
|
-
} from "../chunk-
|
|
34
|
+
} from "../chunk-4A6VUVXV.js";
|
|
24
35
|
import "../chunk-7QLSKOSU.js";
|
|
25
36
|
import "../chunk-KZERVKTD.js";
|
|
26
|
-
import "../chunk-
|
|
37
|
+
import "../chunk-IOVJVGIT.js";
|
|
27
38
|
import "../chunk-AQFEP65J.js";
|
|
28
39
|
import {
|
|
29
40
|
getApiKey,
|
|
30
41
|
getBaseUrl
|
|
31
|
-
} from "../chunk-
|
|
32
|
-
import "../chunk-
|
|
33
|
-
import
|
|
42
|
+
} from "../chunk-EWC4P4ZL.js";
|
|
43
|
+
import "../chunk-57M4ERJK.js";
|
|
44
|
+
import {
|
|
45
|
+
CODEX_NATIVE_APPROVAL_METHODS,
|
|
46
|
+
canonicalJsonText
|
|
47
|
+
} from "../chunk-K7EGWMDU.js";
|
|
34
48
|
|
|
35
49
|
// src/codex-session-launch.ts
|
|
36
50
|
import { spawn as spawn2 } from "child_process";
|
|
@@ -44,6 +58,94 @@ import { createServer } from "http";
|
|
|
44
58
|
import { tmpdir } from "os";
|
|
45
59
|
import { join } from "path";
|
|
46
60
|
import WebSocket, { WebSocketServer } from "ws";
|
|
61
|
+
|
|
62
|
+
// src/codex-shared-questions.ts
|
|
63
|
+
import { createHash, randomUUID } from "crypto";
|
|
64
|
+
var CodexSharedQuestions = class {
|
|
65
|
+
constructor(options) {
|
|
66
|
+
this.options = options;
|
|
67
|
+
}
|
|
68
|
+
generation = randomUUID();
|
|
69
|
+
pending = /* @__PURE__ */ new Map();
|
|
70
|
+
retired = /* @__PURE__ */ new Set();
|
|
71
|
+
tasks = /* @__PURE__ */ new Set();
|
|
72
|
+
closed = false;
|
|
73
|
+
handle(message, threadId) {
|
|
74
|
+
if (this.closed || !message.method) return;
|
|
75
|
+
const params = message.params ?? {};
|
|
76
|
+
if (message.id === void 0) {
|
|
77
|
+
for (const [id2, pending2] of this.pending) {
|
|
78
|
+
const closed = pending2.dispatch ? closesOwnedQuestion(pending2.dispatch.owner, message.method, params) : message.method === "serverRequest/resolved" && params.requestId === id2 || params.threadId === pending2.threadId && ["thread/closed", "thread/archived", "turn/completed"].includes(message.method);
|
|
79
|
+
if (closed) {
|
|
80
|
+
pending2.controller.abort();
|
|
81
|
+
this.pending.delete(id2);
|
|
82
|
+
this.retired.add(id2);
|
|
83
|
+
if (pending2.dispatch) {
|
|
84
|
+
const task2 = reportOwnedQuestion(this.options, pending2.dispatch, "request_closed").finally(() => this.tasks.delete(task2));
|
|
85
|
+
this.tasks.add(task2);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const id = message.id;
|
|
92
|
+
if (typeof id !== "string" && !(typeof id === "number" && Number.isSafeInteger(id))) return;
|
|
93
|
+
if (this.retired.has(id)) return;
|
|
94
|
+
const subjectHash = createHash("sha256").update(canonicalJsonText(params)).digest("hex");
|
|
95
|
+
const existing = this.pending.get(id);
|
|
96
|
+
if (existing) {
|
|
97
|
+
if (existing.subjectHash !== subjectHash) {
|
|
98
|
+
existing.controller.abort();
|
|
99
|
+
this.pending.delete(id);
|
|
100
|
+
this.retired.add(id);
|
|
101
|
+
}
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (this.pending.size >= 8) return;
|
|
105
|
+
const isQuestion = message.method === "item/tool/requestUserInput";
|
|
106
|
+
if (!isQuestion && message.method !== "mcpServer/elicitation/request") return;
|
|
107
|
+
const owner = isQuestion ? codexQuestionOwner(params, id, { machineId: this.options.machineId, generation: this.generation, threadId }) : null;
|
|
108
|
+
if (isQuestion ? !owner : !planCodexElicitation(params, threadId)) return;
|
|
109
|
+
const duration = params.autoResolutionMs;
|
|
110
|
+
if (duration != null && (typeof duration !== "number" || !Number.isFinite(duration) || duration < 0)) return;
|
|
111
|
+
const dispatch = owner ? { owner, correlationIds: [], deadline: typeof duration === "number" ? Date.now() + duration : null } : void 0;
|
|
112
|
+
const pending = { controller: new AbortController(), dispatch, threadId, subjectHash };
|
|
113
|
+
this.pending.set(id, pending);
|
|
114
|
+
const task = this.answer(id, params, pending).finally(() => this.tasks.delete(task));
|
|
115
|
+
this.tasks.add(task);
|
|
116
|
+
}
|
|
117
|
+
async answer(id, params, pending) {
|
|
118
|
+
const signal = AbortSignal.any([this.options.signal, pending.controller.signal]);
|
|
119
|
+
const options = { ...this.options, shared: true, signal };
|
|
120
|
+
const dispatch = pending.dispatch;
|
|
121
|
+
try {
|
|
122
|
+
const result = dispatch ? await answerCodexQuestions(options, params, signal, "request_user_input", dispatch) : await answerCodexElicitation(options, params, pending.threadId, signal);
|
|
123
|
+
if (signal.aborted || this.closed || "action" in result && result.action !== "accept") return;
|
|
124
|
+
if (dispatch) await claimOwnedQuestion(options, dispatch);
|
|
125
|
+
if (signal.aborted || this.closed || this.pending.get(id) !== pending) return;
|
|
126
|
+
if (dispatch?.deadline != null && Date.now() >= dispatch.deadline) throw new Error("Question expired");
|
|
127
|
+
this.options.reply(id, result);
|
|
128
|
+
if (dispatch) await reportOwnedQuestion(options, dispatch, "reply_queued");
|
|
129
|
+
} catch {
|
|
130
|
+
if (!signal.aborted) writeDegraded("questions", { reason: "shared_codex_question_unavailable", fallback: "native client", event: "question" });
|
|
131
|
+
} finally {
|
|
132
|
+
if (dispatch && (!dispatch.claimId || signal.aborted)) await reportOwnedQuestion(options, dispatch, "unavailable");
|
|
133
|
+
if (!dispatch?.claimId || signal.aborted) {
|
|
134
|
+
this.pending.delete(id);
|
|
135
|
+
this.retired.add(id);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
async close() {
|
|
140
|
+
this.closed = true;
|
|
141
|
+
for (const pending of this.pending.values()) pending.controller.abort();
|
|
142
|
+
await Promise.allSettled(this.tasks);
|
|
143
|
+
await Promise.allSettled([...this.pending.values()].flatMap((pending) => pending.dispatch ? [reportOwnedQuestion(this.options, pending.dispatch, "unavailable")] : []));
|
|
144
|
+
this.pending.clear();
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
// src/codex-shared-session.ts
|
|
47
149
|
var record = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
48
150
|
var requestId = (value) => typeof value === "string" || typeof value === "number" && Number.isSafeInteger(value);
|
|
49
151
|
var MAX_MESSAGE_BYTES = 16 * 1024 * 1024;
|
|
@@ -66,14 +168,14 @@ var startCodexSharedSession = async (options) => {
|
|
|
66
168
|
detached: true
|
|
67
169
|
});
|
|
68
170
|
child.stderr.resume();
|
|
69
|
-
const exited = new Promise((
|
|
171
|
+
const exited = new Promise((resolve3) => {
|
|
70
172
|
child.once("exit", () => {
|
|
71
173
|
controller.abort(new Error("Codex app-server stopped"));
|
|
72
|
-
|
|
174
|
+
resolve3();
|
|
73
175
|
});
|
|
74
176
|
child.once("error", () => {
|
|
75
177
|
controller.abort(new Error("Could not start Codex app-server"));
|
|
76
|
-
|
|
178
|
+
resolve3();
|
|
77
179
|
});
|
|
78
180
|
});
|
|
79
181
|
let terminal = null;
|
|
@@ -84,16 +186,20 @@ var startCodexSharedSession = async (options) => {
|
|
|
84
186
|
if (signal.aborted || child.stdin.destroyed) throw new Error("Codex connection closed");
|
|
85
187
|
child.stdin.write(JSON.stringify(message) + "\n");
|
|
86
188
|
};
|
|
87
|
-
const
|
|
189
|
+
const reply = (id, result) => {
|
|
88
190
|
if (!unanswered.delete(id)) throw new Error("Native request already answered");
|
|
89
191
|
write({ id, result });
|
|
90
|
-
}
|
|
192
|
+
};
|
|
193
|
+
const approvals = new CodexNativeApprovals({ ...options, signal, shared: true, reply });
|
|
194
|
+
const questions = new CodexSharedQuestions({ ...options, signal, reply });
|
|
91
195
|
const observeNative = (message) => {
|
|
92
196
|
if (typeof message.method !== "string") return;
|
|
93
197
|
if (requestId(message.id)) unanswered.add(message.id);
|
|
94
198
|
if (!record(message.params)) return;
|
|
95
199
|
if (message.method === "serverRequest/resolved" && requestId(message.params.requestId)) unanswered.delete(message.params.requestId);
|
|
96
|
-
|
|
200
|
+
const event = { id: message.id, method: message.method, params: message.params };
|
|
201
|
+
const threadId = typeof message.params.threadId === "string" ? message.params.threadId : null;
|
|
202
|
+
if (!approvals.handle(event, threadId)) questions.handle(event, threadId);
|
|
97
203
|
};
|
|
98
204
|
child.stdout.setEncoding("utf8");
|
|
99
205
|
child.stdout.on("data", (chunk) => {
|
|
@@ -135,6 +241,7 @@ var startCodexSharedSession = async (options) => {
|
|
|
135
241
|
if (requestId(message.id) && !("method" in message)) {
|
|
136
242
|
if (!unanswered.delete(message.id)) return;
|
|
137
243
|
approvals.handle({ method: "serverRequest/resolved", params: { requestId: message.id } }, null);
|
|
244
|
+
questions.handle({ method: "serverRequest/resolved", params: { requestId: message.id } }, null);
|
|
138
245
|
}
|
|
139
246
|
write(message);
|
|
140
247
|
} catch {
|
|
@@ -155,7 +262,7 @@ var startCodexSharedSession = async (options) => {
|
|
|
155
262
|
const stopServer = async () => {
|
|
156
263
|
signalServerGroup("SIGTERM");
|
|
157
264
|
const deadline = Date.now() + 2e3;
|
|
158
|
-
while (signalServerGroup(0) && Date.now() < deadline) await new Promise((
|
|
265
|
+
while (signalServerGroup(0) && Date.now() < deadline) await new Promise((resolve3) => setTimeout(resolve3, 25));
|
|
159
266
|
if (signalServerGroup(0)) signalServerGroup("SIGKILL");
|
|
160
267
|
await exited;
|
|
161
268
|
};
|
|
@@ -167,7 +274,7 @@ var startCodexSharedSession = async (options) => {
|
|
|
167
274
|
websocket.close();
|
|
168
275
|
http.close();
|
|
169
276
|
try {
|
|
170
|
-
await Promise.all([approvals.close(), stopServer()]);
|
|
277
|
+
await Promise.all([approvals.close(), questions.close(), stopServer()]);
|
|
171
278
|
} finally {
|
|
172
279
|
await rm(directory, { recursive: true, force: true });
|
|
173
280
|
}
|
|
@@ -178,9 +285,9 @@ var startCodexSharedSession = async (options) => {
|
|
|
178
285
|
void close();
|
|
179
286
|
}, { once: true });
|
|
180
287
|
try {
|
|
181
|
-
await new Promise((
|
|
288
|
+
await new Promise((resolve3, reject) => {
|
|
182
289
|
http.once("error", reject);
|
|
183
|
-
http.listen(socketPath,
|
|
290
|
+
http.listen(socketPath, resolve3);
|
|
184
291
|
});
|
|
185
292
|
signal.throwIfAborted();
|
|
186
293
|
return { endpoint: "unix://" + socketPath, signal, close };
|
|
@@ -200,7 +307,7 @@ var launchCodexSession = async (options) => {
|
|
|
200
307
|
if (!(await stat(options.cwd)).isDirectory()) throw new Error("Codex needs a project directory");
|
|
201
308
|
const session = await startCodexSharedSession(options);
|
|
202
309
|
try {
|
|
203
|
-
process.stderr.write("[pushary] Managed Codex session. Supported native permissions can reach Pushary; other prompts stay in this terminal.\n");
|
|
310
|
+
process.stderr.write("[pushary] Managed Codex session. Supported questions and native permissions can reach Pushary; other prompts stay in this terminal.\n");
|
|
204
311
|
const tui = spawn2(options.binary, ["--remote", session.endpoint], {
|
|
205
312
|
cwd: options.cwd,
|
|
206
313
|
env: { ...options.env ?? process.env, PUSHARY_CODEX_BRIDGE: "1" },
|
|
@@ -212,9 +319,9 @@ var launchCodexSession = async (options) => {
|
|
|
212
319
|
session.signal.addEventListener("abort", stop, { once: true });
|
|
213
320
|
if (session.signal.aborted) stop();
|
|
214
321
|
try {
|
|
215
|
-
return await new Promise((
|
|
322
|
+
return await new Promise((resolve3, reject) => {
|
|
216
323
|
tui.once("error", reject);
|
|
217
|
-
tui.once("close", (code) =>
|
|
324
|
+
tui.once("close", (code) => resolve3(code ?? 1));
|
|
218
325
|
});
|
|
219
326
|
} finally {
|
|
220
327
|
session.signal.removeEventListener("abort", stop);
|
|
@@ -224,25 +331,314 @@ var launchCodexSession = async (options) => {
|
|
|
224
331
|
}
|
|
225
332
|
};
|
|
226
333
|
|
|
334
|
+
// src/codex-desktop-launch.ts
|
|
335
|
+
import { spawn as spawn4, execFileSync } from "child_process";
|
|
336
|
+
import { existsSync } from "fs";
|
|
337
|
+
import { mkdtemp as mkdtemp2, readFile, rm as rm2, writeFile, access } from "fs/promises";
|
|
338
|
+
import { constants } from "fs";
|
|
339
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
340
|
+
import { join as join2, resolve as resolve2 } from "path";
|
|
341
|
+
|
|
342
|
+
// src/codex-desktop-proxy.ts
|
|
343
|
+
import { spawn as spawn3 } from "child_process";
|
|
344
|
+
import { isAbsolute } from "path";
|
|
345
|
+
import { PassThrough, Transform } from "stream";
|
|
346
|
+
var record2 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
347
|
+
var requestId2 = (value) => typeof value === "string" && value.length > 0 || typeof value === "number" && Number.isSafeInteger(value);
|
|
348
|
+
var THREAD_METHODS = ["thread/start", "thread/resume", "thread/fork", "thread/read"];
|
|
349
|
+
var MAX_MESSAGE_BYTES2 = 16 * 1024 * 1024;
|
|
350
|
+
var MAX_CONTEXTS = 1024;
|
|
351
|
+
var CodexDesktopApprovals = class {
|
|
352
|
+
threads = /* @__PURE__ */ new Map();
|
|
353
|
+
requests = /* @__PURE__ */ new Map();
|
|
354
|
+
answered = /* @__PURE__ */ new Map();
|
|
355
|
+
approvals;
|
|
356
|
+
closing;
|
|
357
|
+
constructor(options) {
|
|
358
|
+
this.approvals = new CodexNativeApprovals({ ...options, cwd: "/", shared: true, reply: (id, result) => {
|
|
359
|
+
if (this.closing || this.answered.get(id) !== false) throw new Error("Desktop already resolved this permission");
|
|
360
|
+
this.answered.set(id, true);
|
|
361
|
+
options.reply({ id, result });
|
|
362
|
+
} });
|
|
363
|
+
}
|
|
364
|
+
client(message) {
|
|
365
|
+
if (requestId2(message.id) && !("method" in message)) {
|
|
366
|
+
if (this.answered.get(message.id) === true) return false;
|
|
367
|
+
if (this.answered.has(message.id)) {
|
|
368
|
+
this.answered.set(message.id, true);
|
|
369
|
+
this.approvals.handle({ method: "serverRequest/resolved", params: { requestId: message.id } }, null);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
if (this.closing) return true;
|
|
373
|
+
if (requestId2(message.id) && typeof message.method === "string" && (THREAD_METHODS.includes(message.method) || message.method === "turn/start")) {
|
|
374
|
+
if (this.requests.size >= MAX_CONTEXTS) {
|
|
375
|
+
void this.close();
|
|
376
|
+
return true;
|
|
377
|
+
}
|
|
378
|
+
this.requests.set(message.id, message);
|
|
379
|
+
const p = message.params;
|
|
380
|
+
if (message.method === "turn/start" && record2(p) && typeof p.threadId === "string" && p.cwd != null) this.forgetThread(p.threadId);
|
|
381
|
+
}
|
|
382
|
+
return true;
|
|
383
|
+
}
|
|
384
|
+
server(message) {
|
|
385
|
+
if (this.closing) return;
|
|
386
|
+
if (requestId2(message.id) && !("method" in message)) {
|
|
387
|
+
const request = this.requests.get(message.id);
|
|
388
|
+
this.requests.delete(message.id);
|
|
389
|
+
if (request && record2(message.result)) {
|
|
390
|
+
this.rememberThread(message.result.thread);
|
|
391
|
+
const p2 = request.params;
|
|
392
|
+
if (request.method === "turn/start" && record2(p2)) this.rememberThread({ id: p2.threadId, cwd: p2.cwd });
|
|
393
|
+
}
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
if (typeof message.method !== "string" || !record2(message.params)) return;
|
|
397
|
+
const p = message.params;
|
|
398
|
+
if (message.method === "thread/started") this.rememberThread(p.thread);
|
|
399
|
+
if (message.method === "thread/closed" || message.method === "thread/archived") {
|
|
400
|
+
if (typeof p.threadId === "string") this.forgetThread(p.threadId);
|
|
401
|
+
}
|
|
402
|
+
if (message.method === "serverRequest/resolved" && requestId2(p.requestId) && this.answered.has(p.requestId)) this.answered.set(p.requestId, true);
|
|
403
|
+
const threadId = typeof p.threadId === "string" ? p.threadId : null;
|
|
404
|
+
const cwd = threadId ? this.threads.get(threadId) : void 0;
|
|
405
|
+
if (message.id !== void 0) {
|
|
406
|
+
if (!cwd || !requestId2(message.id) || !CODEX_NATIVE_APPROVAL_METHODS.some((method) => method === message.method)) return;
|
|
407
|
+
if (this.answered.has(message.id)) return;
|
|
408
|
+
if (this.answered.size >= 1e4) {
|
|
409
|
+
void this.close();
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
this.answered.set(message.id, false);
|
|
413
|
+
}
|
|
414
|
+
this.approvals.handle({ id: message.id, method: message.method, params: p }, threadId, cwd);
|
|
415
|
+
}
|
|
416
|
+
close() {
|
|
417
|
+
if (!this.closing) this.closing = this.approvals.close();
|
|
418
|
+
return this.closing;
|
|
419
|
+
}
|
|
420
|
+
forgetThread(id) {
|
|
421
|
+
this.threads.delete(id);
|
|
422
|
+
this.approvals.handle({ method: "thread/closed", params: { threadId: id } }, id);
|
|
423
|
+
}
|
|
424
|
+
rememberThread(thread) {
|
|
425
|
+
if (!record2(thread) || typeof thread.id !== "string" || typeof thread.cwd !== "string" || !isAbsolute(thread.cwd)) return;
|
|
426
|
+
if (this.threads.has(thread.id) && this.threads.get(thread.id) !== thread.cwd) this.forgetThread(thread.id);
|
|
427
|
+
if (this.threads.size >= MAX_CONTEXTS && !this.threads.has(thread.id)) return;
|
|
428
|
+
this.threads.set(thread.id, thread.cwd);
|
|
429
|
+
}
|
|
430
|
+
};
|
|
431
|
+
var codexProtocolLines = (observe, unavailable) => {
|
|
432
|
+
let buffered = Buffer.alloc(0), passthrough = false;
|
|
433
|
+
const forward = (stream, line) => {
|
|
434
|
+
try {
|
|
435
|
+
const message = JSON.parse(line.toString("utf8"));
|
|
436
|
+
if (!record2(message)) throw new Error("Unknown Codex protocol message");
|
|
437
|
+
if (observe(message)) stream.push(line);
|
|
438
|
+
} catch {
|
|
439
|
+
unavailable();
|
|
440
|
+
passthrough = true;
|
|
441
|
+
stream.push(line);
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
return new Transform({
|
|
445
|
+
transform(chunk, _encoding, callback) {
|
|
446
|
+
if (passthrough) {
|
|
447
|
+
this.push(chunk);
|
|
448
|
+
callback();
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
buffered = Buffer.concat([buffered, chunk]);
|
|
452
|
+
if (buffered.length > MAX_MESSAGE_BYTES2) {
|
|
453
|
+
unavailable();
|
|
454
|
+
passthrough = true;
|
|
455
|
+
this.push(buffered);
|
|
456
|
+
buffered = Buffer.alloc(0);
|
|
457
|
+
callback();
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
let end;
|
|
461
|
+
while ((end = buffered.indexOf(10)) >= 0) {
|
|
462
|
+
const line = buffered.subarray(0, end + 1);
|
|
463
|
+
buffered = buffered.subarray(end + 1);
|
|
464
|
+
if (passthrough || !line.toString("utf8").trim()) this.push(line);
|
|
465
|
+
else forward(this, line);
|
|
466
|
+
}
|
|
467
|
+
callback();
|
|
468
|
+
},
|
|
469
|
+
flush(callback) {
|
|
470
|
+
if (buffered.length) {
|
|
471
|
+
if (passthrough) this.push(buffered);
|
|
472
|
+
else forward(this, buffered);
|
|
473
|
+
}
|
|
474
|
+
callback();
|
|
475
|
+
}
|
|
476
|
+
});
|
|
477
|
+
};
|
|
478
|
+
var runCodexDesktopProxy = async (binary, args2, readAccount) => {
|
|
479
|
+
const stdioServer = args2[0] === "app-server" && !args2.some((arg) => arg === "--listen" || arg.startsWith("--listen="));
|
|
480
|
+
const child = spawn3(binary, [...args2], {
|
|
481
|
+
stdio: ["pipe", "pipe", "inherit"],
|
|
482
|
+
detached: true,
|
|
483
|
+
env: { ...process.env, CODEX_CLI_PATH: binary }
|
|
484
|
+
});
|
|
485
|
+
const account = stdioServer ? readAccount() : null;
|
|
486
|
+
const abort = new AbortController();
|
|
487
|
+
let approvals;
|
|
488
|
+
let timer;
|
|
489
|
+
let killTimer;
|
|
490
|
+
const unavailable = () => {
|
|
491
|
+
abort.abort();
|
|
492
|
+
clearInterval(timer);
|
|
493
|
+
void approvals?.close();
|
|
494
|
+
process.stderr.write("[pushary] Desktop forwarding unavailable; answer permissions in Codex.\n");
|
|
495
|
+
};
|
|
496
|
+
if (account && stdioServer) {
|
|
497
|
+
approvals = new CodexDesktopApprovals({ ...account, signal: abort.signal, reply: (message) => {
|
|
498
|
+
if (child.stdin.destroyed) throw new Error("Codex engine stopped");
|
|
499
|
+
child.stdin.write(JSON.stringify(message) + "\n");
|
|
500
|
+
} });
|
|
501
|
+
timer = setInterval(() => {
|
|
502
|
+
const current = readAccount();
|
|
503
|
+
if (!current || current.apiKey !== account.apiKey || current.baseUrl !== account.baseUrl || current.machineId !== account.machineId) unavailable();
|
|
504
|
+
}, 1e3);
|
|
505
|
+
}
|
|
506
|
+
const signalChild = (signal) => {
|
|
507
|
+
if (!child.pid) return;
|
|
508
|
+
try {
|
|
509
|
+
process.kill(-child.pid, signal);
|
|
510
|
+
} catch (error) {
|
|
511
|
+
if (!record2(error) || error.code !== "ESRCH") throw error;
|
|
512
|
+
}
|
|
513
|
+
};
|
|
514
|
+
const stop = () => {
|
|
515
|
+
if (killTimer) return;
|
|
516
|
+
abort.abort();
|
|
517
|
+
signalChild("SIGTERM");
|
|
518
|
+
killTimer = setTimeout(() => signalChild("SIGKILL"), 2e3);
|
|
519
|
+
};
|
|
520
|
+
const input = stdioServer ? codexProtocolLines((message) => approvals?.client(message) ?? true, unavailable) : new PassThrough();
|
|
521
|
+
const output = stdioServer ? codexProtocolLines((message) => {
|
|
522
|
+
approvals?.server(message);
|
|
523
|
+
return true;
|
|
524
|
+
}, unavailable) : new PassThrough();
|
|
525
|
+
process.stdin.pipe(input).pipe(child.stdin);
|
|
526
|
+
child.stdout.pipe(output).pipe(process.stdout, { end: false });
|
|
527
|
+
if (stdioServer) process.stdin.once("end", stop);
|
|
528
|
+
for (const stream of [process.stdin, input, child.stdin, child.stdout, output, process.stdout]) stream.on("error", stop);
|
|
529
|
+
for (const signal of ["SIGINT", "SIGTERM", "SIGHUP"]) process.on(signal, stop);
|
|
530
|
+
try {
|
|
531
|
+
return await new Promise((resolve3, reject) => {
|
|
532
|
+
child.once("error", reject);
|
|
533
|
+
child.once("close", (code) => resolve3(code ?? 1));
|
|
534
|
+
});
|
|
535
|
+
} finally {
|
|
536
|
+
abort.abort();
|
|
537
|
+
clearInterval(timer);
|
|
538
|
+
clearTimeout(killTimer);
|
|
539
|
+
signalChild("SIGKILL");
|
|
540
|
+
process.stdin.unpipe(input);
|
|
541
|
+
child.stdout.unpipe(output);
|
|
542
|
+
output.unpipe(process.stdout);
|
|
543
|
+
process.stdin.pause();
|
|
544
|
+
process.stdin.off("end", stop);
|
|
545
|
+
for (const stream of [process.stdin, input, child.stdin, child.stdout, output, process.stdout]) stream.off("error", stop);
|
|
546
|
+
for (const signal of ["SIGINT", "SIGTERM", "SIGHUP"]) process.off(signal, stop);
|
|
547
|
+
await approvals?.close();
|
|
548
|
+
}
|
|
549
|
+
};
|
|
550
|
+
|
|
551
|
+
// src/codex-desktop-launch.ts
|
|
552
|
+
var record3 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
553
|
+
var quote = (value) => "'" + value.replace(/'/g, "'\\''") + "'";
|
|
554
|
+
var TESTED_ENGINE_VERSION = "0.162.0-alpha.2";
|
|
555
|
+
var desktopEngineFromManifest = (app, manifest) => {
|
|
556
|
+
if (!record3(manifest) || manifest.layoutVersion !== 1 || manifest.entrypoint !== "bin/codex" || manifest.version !== TESTED_ENGINE_VERSION) {
|
|
557
|
+
throw new Error(`Experimental Desktop forwarding requires bundled Codex ${TESTED_ENGINE_VERSION}. Launch Codex normally for this version.`);
|
|
558
|
+
}
|
|
559
|
+
return join2(app, "Contents/Resources/codex-cli/bin/codex");
|
|
560
|
+
};
|
|
561
|
+
var writeDesktopProxy = async (entry, app, directory) => {
|
|
562
|
+
const shim = join2(directory, "codex");
|
|
563
|
+
await writeFile(shim, "#!/bin/sh\nexec " + [...entry, "--desktop-proxy", app, "--"].map(quote).join(" ") + ' "$@"\n', { mode: 448, flag: "wx" });
|
|
564
|
+
return shim;
|
|
565
|
+
};
|
|
566
|
+
var desktopEngine = async (app) => {
|
|
567
|
+
const manifest = JSON.parse(await readFile(join2(app, "Contents/Resources/codex-cli/codex-package.json"), "utf8"));
|
|
568
|
+
const engine = desktopEngineFromManifest(app, manifest);
|
|
569
|
+
await access(engine, constants.X_OK);
|
|
570
|
+
return engine;
|
|
571
|
+
};
|
|
572
|
+
var desktopExecutable = (app) => {
|
|
573
|
+
const plist = join2(app, "Contents/Info.plist");
|
|
574
|
+
const read = (key) => execFileSync("/usr/bin/plutil", ["-extract", key, "raw", plist], { encoding: "utf8" }).trim();
|
|
575
|
+
if (read("CFBundleIdentifier") !== "com.openai.codex") throw new Error("Choose the Codex Desktop application");
|
|
576
|
+
const name = read("CFBundleExecutable");
|
|
577
|
+
if (!name || name.includes("/") || name === "." || name === "..") throw new Error("Invalid Desktop executable");
|
|
578
|
+
try {
|
|
579
|
+
execFileSync("/usr/bin/pgrep", ["-x", name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")], { stdio: "ignore" });
|
|
580
|
+
} catch (error) {
|
|
581
|
+
if (record3(error) && error.status === 1) return join2(app, "Contents/MacOS", name);
|
|
582
|
+
throw error;
|
|
583
|
+
}
|
|
584
|
+
throw new Error("Codex Desktop is already running. Finish active work and quit it yourself, then retry. Pushary will not restart it.");
|
|
585
|
+
};
|
|
586
|
+
var codexDesktopCommand = async (args2, entry, readAccount) => {
|
|
587
|
+
if (args2[0] !== "--desktop" && args2[0] !== "--desktop-proxy") return null;
|
|
588
|
+
if (process.platform !== "darwin") throw new Error("Experimental Codex Desktop forwarding currently requires macOS");
|
|
589
|
+
if (args2[0] === "--desktop-proxy") {
|
|
590
|
+
if (!args2[1] || args2[2] !== "--") throw new Error("Invalid Desktop proxy invocation");
|
|
591
|
+
return runCodexDesktopProxy(await desktopEngine(args2[1]), args2.slice(3), readAccount);
|
|
592
|
+
}
|
|
593
|
+
if (args2.length > 2) throw new Error("Usage: pushary codex --desktop [path/to/Codex.app]");
|
|
594
|
+
if (!readAccount()) throw new Error("Connect Pushary before starting Desktop forwarding");
|
|
595
|
+
const app = resolve2(args2[1] ?? ["/Applications/ChatGPT.app", "/Applications/Codex.app"].find((candidate) => existsSync(join2(candidate, "Contents/Resources/codex-cli/codex-package.json"))) ?? "/Applications/Codex.app");
|
|
596
|
+
await desktopEngine(app);
|
|
597
|
+
const executable = desktopExecutable(app);
|
|
598
|
+
const directory = await mkdtemp2(join2(tmpdir2(), "pushary-codex-desktop-"));
|
|
599
|
+
try {
|
|
600
|
+
const shim = await writeDesktopProxy(entry, app, directory);
|
|
601
|
+
process.stderr.write("[pushary] Experimental Desktop launch. Supported approvals can reach Pushary; other prompts stay in Codex. Quit and open Codex normally to disable.\n");
|
|
602
|
+
const child = spawn4(executable, [], { stdio: "ignore", detached: true, env: { ...process.env, CODEX_CLI_PATH: shim } });
|
|
603
|
+
return await new Promise((resolve3, reject) => {
|
|
604
|
+
child.once("error", reject);
|
|
605
|
+
child.once("close", (code) => resolve3(code ?? 1));
|
|
606
|
+
});
|
|
607
|
+
} finally {
|
|
608
|
+
await rm2(directory, { recursive: true, force: true });
|
|
609
|
+
}
|
|
610
|
+
};
|
|
611
|
+
|
|
227
612
|
// bin/pushary-codex-session.ts
|
|
228
|
-
exitOnHelpFlag("codex");
|
|
229
613
|
var args = process.argv.slice(2);
|
|
230
614
|
if (args[0] === "codex") args.shift();
|
|
615
|
+
if (args[0] !== "--desktop-proxy") exitOnHelpFlag("codex");
|
|
616
|
+
var desktopAccount = () => {
|
|
617
|
+
try {
|
|
618
|
+
return { apiKey: getApiKey(), baseUrl: getBaseUrl(), machineId: getMachineId() };
|
|
619
|
+
} catch {
|
|
620
|
+
return null;
|
|
621
|
+
}
|
|
622
|
+
};
|
|
231
623
|
{
|
|
232
624
|
const abort = new AbortController();
|
|
233
625
|
const stop = () => abort.abort();
|
|
234
|
-
for (const signal of ["SIGINT", "SIGTERM", "SIGHUP"]) process.on(signal, stop);
|
|
626
|
+
if (!args[0]?.startsWith("--desktop")) for (const signal of ["SIGINT", "SIGTERM", "SIGHUP"]) process.on(signal, stop);
|
|
235
627
|
try {
|
|
236
|
-
const
|
|
237
|
-
if (
|
|
238
|
-
|
|
239
|
-
binary
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
628
|
+
const desktop = await codexDesktopCommand(args, [process.execPath, ...process.execArgv, process.argv[1], "codex"], desktopAccount);
|
|
629
|
+
if (desktop !== null) process.exitCode = desktop;
|
|
630
|
+
else {
|
|
631
|
+
const binary = resolveProviderBinary("codex");
|
|
632
|
+
if (!binary) throw new Error("Install Codex first, then run pushary codex");
|
|
633
|
+
process.exitCode = await launchCodexSession({
|
|
634
|
+
binary,
|
|
635
|
+
cwd: codexSessionDirectory(args, process.cwd()),
|
|
636
|
+
apiKey: getApiKey(),
|
|
637
|
+
baseUrl: getBaseUrl(),
|
|
638
|
+
machineId: getMachineId(),
|
|
639
|
+
signal: abort.signal
|
|
640
|
+
});
|
|
641
|
+
}
|
|
246
642
|
} catch (error) {
|
|
247
643
|
process.stderr.write(`[pushary] ${error instanceof Error ? error.message : "Codex session failed"}
|
|
248
644
|
`);
|
|
@@ -1,33 +1,32 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
reportEvent
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-
|
|
4
|
+
} from "../chunk-GZSSAY6N.js";
|
|
5
|
+
import "../chunk-HHX6IRSH.js";
|
|
6
|
+
import "../chunk-CFMCV6Z7.js";
|
|
6
7
|
import {
|
|
7
8
|
askUser,
|
|
8
9
|
isDecisionStopped,
|
|
9
10
|
waitForAnswer
|
|
10
|
-
} from "../chunk-
|
|
11
|
-
import "../chunk-M2L7625S.js";
|
|
12
|
-
import "../chunk-GOEIW2VH.js";
|
|
11
|
+
} from "../chunk-CDERSTBO.js";
|
|
13
12
|
import "../chunk-POFBE6ZY.js";
|
|
14
|
-
import "../chunk-
|
|
15
|
-
import "../chunk-
|
|
16
|
-
import "../chunk-
|
|
13
|
+
import "../chunk-SFQSOBLM.js";
|
|
14
|
+
import "../chunk-PWUIPQ2I.js";
|
|
15
|
+
import "../chunk-6TYKZ4DN.js";
|
|
17
16
|
import {
|
|
18
17
|
getMachineId
|
|
19
|
-
} from "../chunk-
|
|
18
|
+
} from "../chunk-4A6VUVXV.js";
|
|
20
19
|
import "../chunk-7QLSKOSU.js";
|
|
21
20
|
import "../chunk-KZERVKTD.js";
|
|
22
|
-
import "../chunk-
|
|
21
|
+
import "../chunk-IOVJVGIT.js";
|
|
23
22
|
import "../chunk-AQFEP65J.js";
|
|
24
23
|
import {
|
|
25
24
|
getApiKey
|
|
26
|
-
} from "../chunk-
|
|
25
|
+
} from "../chunk-EWC4P4ZL.js";
|
|
27
26
|
import {
|
|
28
27
|
NPX_LATEST_COMMAND
|
|
29
|
-
} from "../chunk-
|
|
30
|
-
import "../chunk-
|
|
28
|
+
} from "../chunk-57M4ERJK.js";
|
|
29
|
+
import "../chunk-K7EGWMDU.js";
|
|
31
30
|
|
|
32
31
|
// bin/pushary-codex.ts
|
|
33
32
|
import { basename } from "path";
|