pushary 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. package/CHANGELOG.md +1943 -0
  2. package/LICENSE +21 -0
  3. package/README.md +330 -0
  4. package/data/SKILL.md +591 -0
  5. package/data/cowork/SKILL.md +77 -0
  6. package/data/cursor-plugin/.cursor-plugin/plugin.json +25 -0
  7. package/data/cursor-plugin/CHANGELOG.md +70 -0
  8. package/data/cursor-plugin/CONTRIBUTING.md +40 -0
  9. package/data/cursor-plugin/LICENSE +21 -0
  10. package/data/cursor-plugin/README.md +113 -0
  11. package/data/cursor-plugin/SECURITY.md +33 -0
  12. package/data/cursor-plugin/assets/logo.png +0 -0
  13. package/data/cursor-plugin/commands/notify-when-done.md +14 -0
  14. package/data/cursor-plugin/commands/pushary-test.md +13 -0
  15. package/data/cursor-plugin/hooks/hooks.json +75 -0
  16. package/data/cursor-plugin/mcp.json +11 -0
  17. package/data/cursor-plugin/rules/pushary.mdc +40 -0
  18. package/data/cursor-plugin/scripts/pushary-gate.mjs +724 -0
  19. package/data/cursor-plugin/scripts/pushary-gate.test.mjs +137 -0
  20. package/data/cursor-plugin/scripts/redaction.mjs +51 -0
  21. package/data/cursor-plugin/skills/pushary/SKILL.md +584 -0
  22. package/data/vscode-plugin/.claude-plugin/plugin.json +28 -0
  23. package/data/vscode-plugin/.mcp.json +11 -0
  24. package/data/vscode-plugin/CHANGELOG.md +49 -0
  25. package/data/vscode-plugin/CONTRIBUTING.md +40 -0
  26. package/data/vscode-plugin/LICENSE +21 -0
  27. package/data/vscode-plugin/README.md +128 -0
  28. package/data/vscode-plugin/SECURITY.md +56 -0
  29. package/data/vscode-plugin/assets/logo.png +0 -0
  30. package/data/vscode-plugin/commands/notify-when-done.md +14 -0
  31. package/data/vscode-plugin/commands/pushary-test.md +13 -0
  32. package/data/vscode-plugin/hooks/hooks.json +60 -0
  33. package/data/vscode-plugin/scripts/pushary-gate.mjs +762 -0
  34. package/data/vscode-plugin/scripts/redaction.mjs +51 -0
  35. package/data/vscode-plugin/skills/pushary/SKILL.md +584 -0
  36. package/dist/bin/pushary-bell-hook.d.ts +1 -0
  37. package/dist/bin/pushary-bell-hook.js +46 -0
  38. package/dist/bin/pushary-bell.d.ts +1 -0
  39. package/dist/bin/pushary-bell.js +146 -0
  40. package/dist/bin/pushary-claude.d.ts +1 -0
  41. package/dist/bin/pushary-claude.js +1916 -0
  42. package/dist/bin/pushary-clean.d.ts +1 -0
  43. package/dist/bin/pushary-clean.js +1300 -0
  44. package/dist/bin/pushary-codex-bridge.d.ts +1 -0
  45. package/dist/bin/pushary-codex-bridge.js +599 -0
  46. package/dist/bin/pushary-codex-hook.d.ts +1 -0
  47. package/dist/bin/pushary-codex-hook.js +886 -0
  48. package/dist/bin/pushary-codex.d.ts +1 -0
  49. package/dist/bin/pushary-codex.js +125 -0
  50. package/dist/bin/pushary-connect.d.ts +1 -0
  51. package/dist/bin/pushary-connect.js +100 -0
  52. package/dist/bin/pushary-cowork.d.ts +1 -0
  53. package/dist/bin/pushary-cowork.js +36 -0
  54. package/dist/bin/pushary-daemon-supervisor.d.ts +1 -0
  55. package/dist/bin/pushary-daemon-supervisor.js +27 -0
  56. package/dist/bin/pushary-daemon.d.ts +1 -0
  57. package/dist/bin/pushary-daemon.js +878 -0
  58. package/dist/bin/pushary-disconnect.d.ts +1 -0
  59. package/dist/bin/pushary-disconnect.js +264 -0
  60. package/dist/bin/pushary-doctor.d.ts +1 -0
  61. package/dist/bin/pushary-doctor.js +1433 -0
  62. package/dist/bin/pushary-elicitation-hook.d.ts +1 -0
  63. package/dist/bin/pushary-elicitation-hook.js +149 -0
  64. package/dist/bin/pushary-gemini-bridge.d.ts +1 -0
  65. package/dist/bin/pushary-gemini-bridge.js +339 -0
  66. package/dist/bin/pushary-gemini-hook.d.ts +1 -0
  67. package/dist/bin/pushary-gemini-hook.js +381 -0
  68. package/dist/bin/pushary-hook.d.ts +1 -0
  69. package/dist/bin/pushary-hook.js +95 -0
  70. package/dist/bin/pushary-login.d.ts +1 -0
  71. package/dist/bin/pushary-login.js +198 -0
  72. package/dist/bin/pushary-logout.d.ts +1 -0
  73. package/dist/bin/pushary-logout.js +147 -0
  74. package/dist/bin/pushary-mcp.d.ts +1 -0
  75. package/dist/bin/pushary-mcp.js +114 -0
  76. package/dist/bin/pushary-mode.d.ts +1 -0
  77. package/dist/bin/pushary-mode.js +130 -0
  78. package/dist/bin/pushary-notification-hook.d.ts +1 -0
  79. package/dist/bin/pushary-notification-hook.js +56 -0
  80. package/dist/bin/pushary-opencode-hook.d.ts +1 -0
  81. package/dist/bin/pushary-opencode-hook.js +338 -0
  82. package/dist/bin/pushary-permission-denied-hook.d.ts +1 -0
  83. package/dist/bin/pushary-permission-denied-hook.js +60 -0
  84. package/dist/bin/pushary-permission-hook.d.ts +1 -0
  85. package/dist/bin/pushary-permission-hook.js +65 -0
  86. package/dist/bin/pushary-post-hook.d.ts +1 -0
  87. package/dist/bin/pushary-post-hook.js +70 -0
  88. package/dist/bin/pushary-prompt-hook.d.ts +1 -0
  89. package/dist/bin/pushary-prompt-hook.js +61 -0
  90. package/dist/bin/pushary-session-end-hook.d.ts +1 -0
  91. package/dist/bin/pushary-session-end-hook.js +59 -0
  92. package/dist/bin/pushary-session-start-hook.d.ts +1 -0
  93. package/dist/bin/pushary-session-start-hook.js +72 -0
  94. package/dist/bin/pushary-setup.d.ts +1 -0
  95. package/dist/bin/pushary-setup.js +2660 -0
  96. package/dist/bin/pushary-stats.d.ts +1 -0
  97. package/dist/bin/pushary-stats.js +44 -0
  98. package/dist/bin/pushary-status.d.ts +1 -0
  99. package/dist/bin/pushary-status.js +262 -0
  100. package/dist/bin/pushary-stop-hook.d.ts +1 -0
  101. package/dist/bin/pushary-stop-hook.js +63 -0
  102. package/dist/bin/pushary-stopfailure-hook.d.ts +1 -0
  103. package/dist/bin/pushary-stopfailure-hook.js +56 -0
  104. package/dist/bin/pushary-suggestions.d.ts +1 -0
  105. package/dist/bin/pushary-suggestions.js +100 -0
  106. package/dist/bin/pushary-transcript-register.d.ts +1 -0
  107. package/dist/bin/pushary-transcript-register.js +40 -0
  108. package/dist/bin/pushary-transcripts.d.ts +1 -0
  109. package/dist/bin/pushary-transcripts.js +77 -0
  110. package/dist/bin/pushary-upgrade.d.ts +1 -0
  111. package/dist/bin/pushary-upgrade.js +599 -0
  112. package/dist/bin/pushary-wait.d.ts +1 -0
  113. package/dist/bin/pushary-wait.js +122 -0
  114. package/dist/bin/pushary.d.ts +1 -0
  115. package/dist/bin/pushary.js +74 -0
  116. package/dist/chunk-2ABTSGFT.js +173 -0
  117. package/dist/chunk-2OY3ZZ5N.js +124 -0
  118. package/dist/chunk-3EGEA4KH.js +44 -0
  119. package/dist/chunk-3EVPNIBE.js +185 -0
  120. package/dist/chunk-5OP5MQI7.js +45 -0
  121. package/dist/chunk-6J2JC2RT.js +149 -0
  122. package/dist/chunk-7QLSKOSU.js +19 -0
  123. package/dist/chunk-7Z7Q4GGS.js +88 -0
  124. package/dist/chunk-A2A2YTMG.js +835 -0
  125. package/dist/chunk-AHV4LNB5.js +377 -0
  126. package/dist/chunk-AXPCESYO.js +197 -0
  127. package/dist/chunk-C2WCPBF7.js +86 -0
  128. package/dist/chunk-CJVVKOSR.js +73 -0
  129. package/dist/chunk-CULUZTWQ.js +2117 -0
  130. package/dist/chunk-CVQYGMSR.js +142 -0
  131. package/dist/chunk-CVWG2N3Y.js +16 -0
  132. package/dist/chunk-D2FX3EPW.js +15 -0
  133. package/dist/chunk-DB3ONZB4.js +44 -0
  134. package/dist/chunk-DE4Y6TCC.js +160 -0
  135. package/dist/chunk-E4FJTUH4.js +249 -0
  136. package/dist/chunk-EPCXUP3P.js +41 -0
  137. package/dist/chunk-EX3GAPA4.js +44 -0
  138. package/dist/chunk-EYK3GSRH.js +143 -0
  139. package/dist/chunk-GMXKITVA.js +70 -0
  140. package/dist/chunk-GPEGOGRJ.js +49 -0
  141. package/dist/chunk-GU3EJCVV.js +43 -0
  142. package/dist/chunk-HQ6G3B5R.js +59 -0
  143. package/dist/chunk-HZVVNGEW.js +22 -0
  144. package/dist/chunk-IPVKH2ST.js +231 -0
  145. package/dist/chunk-J5TIV3J6.js +238 -0
  146. package/dist/chunk-JU6W3XAU.js +64 -0
  147. package/dist/chunk-JYG4B33G.js +227 -0
  148. package/dist/chunk-JZTHGAPA.js +503 -0
  149. package/dist/chunk-KGDWDSNL.js +102 -0
  150. package/dist/chunk-KVQBWI5T.js +99 -0
  151. package/dist/chunk-KZERVKTD.js +16 -0
  152. package/dist/chunk-LXQ5FBBD.js +47 -0
  153. package/dist/chunk-M5ICYGHT.js +138 -0
  154. package/dist/chunk-M5JDZ45W.js +943 -0
  155. package/dist/chunk-MDEPU45E.js +1226 -0
  156. package/dist/chunk-MPWXGONV.js +157 -0
  157. package/dist/chunk-N6IOSZOE.js +185 -0
  158. package/dist/chunk-NGDD6TXY.js +192 -0
  159. package/dist/chunk-NQTHFF4W.js +2830 -0
  160. package/dist/chunk-OKXS7WDZ.js +68 -0
  161. package/dist/chunk-OLQRRFBX.js +21 -0
  162. package/dist/chunk-P7WQEVWA.js +307 -0
  163. package/dist/chunk-POFBE6ZY.js +12 -0
  164. package/dist/chunk-PQ4K74WL.js +30 -0
  165. package/dist/chunk-PUZZELE6.js +37 -0
  166. package/dist/chunk-Q7E4I6UA.js +767 -0
  167. package/dist/chunk-QCGNGEGE.js +15 -0
  168. package/dist/chunk-R7437YI3.js +151 -0
  169. package/dist/chunk-RHR6NARM.js +305 -0
  170. package/dist/chunk-SAXBC4AZ.js +60 -0
  171. package/dist/chunk-SGZJ5LYH.js +76 -0
  172. package/dist/chunk-SIFVFP6T.js +70 -0
  173. package/dist/chunk-SLFABHW3.js +150 -0
  174. package/dist/chunk-T7E75EKA.js +67 -0
  175. package/dist/chunk-TY4JT7W3.js +38 -0
  176. package/dist/chunk-U4TMAANR.js +111 -0
  177. package/dist/chunk-UI36QSBI.js +177 -0
  178. package/dist/chunk-VQKRG3AC.js +339 -0
  179. package/dist/chunk-WE2J62NC.js +1131 -0
  180. package/dist/chunk-XHKBHWLX.js +14 -0
  181. package/dist/chunk-ZSZSSJCV.js +275 -0
  182. package/dist/chunk-ZVSKMPBZ.js +135 -0
  183. package/dist/reapply-CW3MA66V.js +34 -0
  184. package/dist/src/index.d.ts +204 -0
  185. package/dist/src/index.js +64 -0
  186. package/package.json +108 -0
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
@@ -0,0 +1,149 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ readHookInput
4
+ } from "../chunk-KGDWDSNL.js";
5
+ import {
6
+ announceSubscriptionRequired,
7
+ isDeferAnswer
8
+ } from "../chunk-5OP5MQI7.js";
9
+ import "../chunk-JZTHGAPA.js";
10
+ import "../chunk-SGZJ5LYH.js";
11
+ import "../chunk-EYK3GSRH.js";
12
+ import "../chunk-ZVSKMPBZ.js";
13
+ import "../chunk-C2WCPBF7.js";
14
+ import "../chunk-6J2JC2RT.js";
15
+ import "../chunk-WE2J62NC.js";
16
+ import "../chunk-3EVPNIBE.js";
17
+ import "../chunk-OKXS7WDZ.js";
18
+ import "../chunk-MPWXGONV.js";
19
+ import "../chunk-SAXBC4AZ.js";
20
+ import "../chunk-OLQRRFBX.js";
21
+ import "../chunk-XHKBHWLX.js";
22
+ import "../chunk-N6IOSZOE.js";
23
+ import "../chunk-EPCXUP3P.js";
24
+ import {
25
+ askUser,
26
+ isDecisionStopped,
27
+ waitForQuestion
28
+ } from "../chunk-NGDD6TXY.js";
29
+ import "../chunk-CVQYGMSR.js";
30
+ import "../chunk-POFBE6ZY.js";
31
+ import "../chunk-GMXKITVA.js";
32
+ import "../chunk-P7WQEVWA.js";
33
+ import {
34
+ SubscriptionRequiredError
35
+ } from "../chunk-M5ICYGHT.js";
36
+ import "../chunk-2ABTSGFT.js";
37
+ import "../chunk-2OY3ZZ5N.js";
38
+ import {
39
+ getMachineId
40
+ } from "../chunk-LXQ5FBBD.js";
41
+ import "../chunk-7QLSKOSU.js";
42
+ import "../chunk-KZERVKTD.js";
43
+ import "../chunk-DB3ONZB4.js";
44
+ import "../chunk-A2A2YTMG.js";
45
+ import "../chunk-T7E75EKA.js";
46
+ import {
47
+ getApiKey
48
+ } from "../chunk-GPEGOGRJ.js";
49
+ import "../chunk-E4FJTUH4.js";
50
+ import {
51
+ ELICITATION_ANSWERABLE_MODE,
52
+ effectiveWaitSeconds,
53
+ elicitationContent,
54
+ elicitationContentFromAnswers,
55
+ hookWaitDeadline,
56
+ parseAgentQuestionAnswers,
57
+ planElicitationAsk,
58
+ readElicitationFields,
59
+ renderElicitationOutput
60
+ } from "../chunk-NQTHFF4W.js";
61
+
62
+ // bin/pushary-elicitation-hook.ts
63
+ import { basename } from "path";
64
+ var START_MS = Date.now();
65
+ var answered = (ask, answer) => {
66
+ if (!answer.answered || isDeferAnswer(answer.value)) return null;
67
+ if (ask.kind === "questions") {
68
+ const values = answer.value ? parseAgentQuestionAnswers([...ask.questions], answer.value) : null;
69
+ return values ? elicitationContentFromAnswers(ask, values) : null;
70
+ }
71
+ return answer.value ? elicitationContent(ask, answer.value) : null;
72
+ };
73
+ var main = async () => {
74
+ const input = await readHookInput("claude", "Elicitation");
75
+ if (!input || input.hook_event_name !== "Elicitation") return;
76
+ if (input.mode !== ELICITATION_ANSWERABLE_MODE) return;
77
+ const message = (input.message ?? "").trim();
78
+ if (!message) return;
79
+ const fields = readElicitationFields(input.requested_schema);
80
+ if (!fields) return;
81
+ const ask = planElicitationAsk(message, fields);
82
+ if (!ask) return;
83
+ const apiKey = getApiKey();
84
+ if (!apiKey) return;
85
+ const project = basename(input.cwd ?? process.cwd());
86
+ const deadline = hookWaitDeadline(START_MS, effectiveWaitSeconds("wait", 0, "claude"), "claude", Date.now());
87
+ const common = {
88
+ context: `${input.mcp_server_name ?? "An MCP server"} is asking for input`,
89
+ agentName: `Claude Code - ${project}`,
90
+ sessionId: input.session_id,
91
+ machineId: getMachineId(),
92
+ toolName: "Elicitation",
93
+ toolTarget: input.mcp_server_name
94
+ };
95
+ const prompts = ask.kind === "questions" ? [...ask.questions.entries()].map(([index, question]) => ({
96
+ question: question.question,
97
+ type: "select",
98
+ options: question.options.map((option) => option.label),
99
+ questions: [question],
100
+ toolUseId: input.elicitation_id ? `${input.elicitation_id}:${index}` : void 0
101
+ })) : [{
102
+ question: ask.question,
103
+ type: ask.kind,
104
+ ...ask.kind === "select" ? { options: [...ask.options] } : {},
105
+ toolUseId: input.elicitation_id
106
+ }];
107
+ let simpleAnswer;
108
+ const questionAnswers = {};
109
+ for (const prompt of prompts) {
110
+ let created;
111
+ try {
112
+ created = await askUser(apiKey, { ...common, ...prompt });
113
+ } catch (error) {
114
+ if (error instanceof SubscriptionRequiredError) {
115
+ announceSubscriptionRequired(error.upgradeUrl, input.session_id);
116
+ return;
117
+ }
118
+ process.stdout.write(renderElicitationOutput(input.hook_event_name ?? "Elicitation", "cancel") ?? "");
119
+ return;
120
+ }
121
+ if (isDecisionStopped(created)) {
122
+ process.stdout.write(renderElicitationOutput(input.hook_event_name ?? "Elicitation", "cancel") ?? "");
123
+ return;
124
+ }
125
+ const answer = await waitForQuestion(apiKey, created, deadline);
126
+ if (isDecisionStopped(answer)) {
127
+ process.stdout.write(renderElicitationOutput(input.hook_event_name ?? "Elicitation", "cancel") ?? "");
128
+ return;
129
+ }
130
+ if (ask.kind === "questions") {
131
+ const question = "questions" in prompt ? prompt.questions[0] : void 0;
132
+ const parsed = question && answer.answered && answer.value ? parseAgentQuestionAnswers([question], answer.value) : null;
133
+ if (parsed) Object.assign(questionAnswers, parsed);
134
+ else {
135
+ return;
136
+ }
137
+ } else {
138
+ simpleAnswer = answer;
139
+ }
140
+ }
141
+ const content = ask.kind === "questions" ? elicitationContentFromAnswers(ask, questionAnswers) : simpleAnswer ? answered(ask, simpleAnswer) : null;
142
+ if (!content) {
143
+ return;
144
+ }
145
+ const output = renderElicitationOutput("Elicitation", "accept", content);
146
+ if (output) process.stdout.write(output);
147
+ };
148
+ main().catch(() => {
149
+ });
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
@@ -0,0 +1,339 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ LiveTranscriptRelay,
4
+ liveSessionSleep,
5
+ postLiveSession,
6
+ postLiveSessionUntilAccepted,
7
+ startLiveSessionHeartbeat
8
+ } from "../chunk-R7437YI3.js";
9
+ import {
10
+ SESSION_IDLE_TIMEOUT_MS,
11
+ describeIdleWindow,
12
+ idleDeadlinePassed,
13
+ startProcessOwnership,
14
+ withSpawnProcessOwnership
15
+ } from "../chunk-ZSZSSJCV.js";
16
+ import "../chunk-MPWXGONV.js";
17
+ import "../chunk-SAXBC4AZ.js";
18
+ import {
19
+ transcriptsEnabled
20
+ } from "../chunk-OLQRRFBX.js";
21
+ import {
22
+ spawnClaude
23
+ } from "../chunk-XHKBHWLX.js";
24
+ import "../chunk-P7WQEVWA.js";
25
+ import {
26
+ getMachineId
27
+ } from "../chunk-LXQ5FBBD.js";
28
+ import "../chunk-DB3ONZB4.js";
29
+ import {
30
+ resolveBinary
31
+ } from "../chunk-A2A2YTMG.js";
32
+ import "../chunk-T7E75EKA.js";
33
+ import {
34
+ getApiKey,
35
+ getBaseUrl
36
+ } from "../chunk-GPEGOGRJ.js";
37
+ import "../chunk-E4FJTUH4.js";
38
+ import "../chunk-NQTHFF4W.js";
39
+
40
+ // bin/pushary-gemini-bridge.ts
41
+ import { existsSync } from "fs";
42
+
43
+ // src/gemini-bridge.ts
44
+ import { spawn } from "child_process";
45
+ import { randomUUID } from "crypto";
46
+ import { basename } from "path";
47
+ var MAX_STREAM_LINE = 1024 * 1024;
48
+ var MAX_TRANSCRIPT_TEXT = 32 * 1024;
49
+ var object = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
50
+ var string = (value) => typeof value === "string" && value.length > 0 ? value : null;
51
+ var geminiArgs = (options) => [
52
+ ...options.bridge.command.args ?? [],
53
+ ...options.bridge.model ? ["--model", options.bridge.model] : [],
54
+ options.startNew ? "--session-id" : "--resume",
55
+ options.sessionId,
56
+ "--output-format",
57
+ "stream-json",
58
+ "--prompt",
59
+ options.text
60
+ ];
61
+ var runGeminiTurn = (options) => new Promise((resolve, reject) => {
62
+ const spawnOptions = {
63
+ cwd: options.bridge.cwd,
64
+ env: { ...process.env, PUSHARY_GEMINI_BRIDGE: "1" },
65
+ stdio: ["ignore", "pipe", "pipe"],
66
+ windowsHide: true
67
+ };
68
+ const child = process.platform === "win32" ? spawnClaude(options.bridge.command.command, geminiArgs(options), spawnOptions) : spawn(options.bridge.command.command, geminiArgs(options), spawnOptions);
69
+ let buffer = "";
70
+ let assistant = "";
71
+ let initialized = false;
72
+ let successful = false;
73
+ let resultError = null;
74
+ let linkError;
75
+ let linking = null;
76
+ let settled = false;
77
+ const stop2 = () => {
78
+ if (child.exitCode == null) child.kill();
79
+ };
80
+ const fail = (error) => {
81
+ if (settled) return;
82
+ settled = true;
83
+ options.bridge.signal.removeEventListener("abort", stop2);
84
+ stop2();
85
+ reject(error instanceof Error ? error : new Error(String(error)));
86
+ };
87
+ const handle = (line) => {
88
+ let event;
89
+ try {
90
+ event = JSON.parse(line);
91
+ } catch {
92
+ return;
93
+ }
94
+ if (event.type === "init") {
95
+ if (event.session_id !== options.sessionId) {
96
+ fail(new Error("Gemini returned a different session id"));
97
+ return;
98
+ }
99
+ initialized = true;
100
+ linking ??= options.onSession().catch((error) => {
101
+ linkError = error;
102
+ stop2();
103
+ });
104
+ }
105
+ if (event.type === "message" && event.role === "assistant") {
106
+ const content = string(event.content);
107
+ if (content) assistant = `${assistant}${content}`.slice(0, MAX_TRANSCRIPT_TEXT);
108
+ }
109
+ if (event.type === "result") {
110
+ successful = event.status === "success";
111
+ resultError = string(object(event.error)?.message);
112
+ }
113
+ };
114
+ const read = (chunk) => {
115
+ buffer += chunk;
116
+ if (buffer.length > MAX_STREAM_LINE && !buffer.includes("\n")) {
117
+ fail(new Error("Gemini sent an oversized stream event"));
118
+ return;
119
+ }
120
+ let newline = buffer.indexOf("\n");
121
+ while (newline >= 0) {
122
+ const line = buffer.slice(0, newline);
123
+ buffer = buffer.slice(newline + 1);
124
+ if (line.length > MAX_STREAM_LINE) {
125
+ fail(new Error("Gemini sent an oversized stream event"));
126
+ return;
127
+ }
128
+ handle(line);
129
+ newline = buffer.indexOf("\n");
130
+ }
131
+ };
132
+ options.bridge.signal.addEventListener("abort", stop2, { once: true });
133
+ child.stdout?.setEncoding("utf8");
134
+ child.stdout?.on("data", (chunk) => read(String(chunk)));
135
+ child.stderr?.resume();
136
+ child.once("error", fail);
137
+ child.once("close", async (code) => {
138
+ if (settled) return;
139
+ await linking;
140
+ if (linkError) return fail(linkError);
141
+ settled = true;
142
+ options.bridge.signal.removeEventListener("abort", stop2);
143
+ if (options.bridge.signal.aborted) return resolve("aborted");
144
+ if (!initialized) return reject(new Error("Gemini did not initialize a session"));
145
+ if (code !== 0 || !successful) return reject(new Error(resultError ?? `Gemini exited with code ${code ?? "unknown"}`));
146
+ options.relay?.add({
147
+ type: "user",
148
+ uuid: `${options.commandId}-user`,
149
+ message: { role: "user", content: options.text }
150
+ });
151
+ if (assistant) {
152
+ options.relay?.add({
153
+ type: "assistant",
154
+ uuid: `${options.commandId}-assistant`,
155
+ message: { role: "assistant", content: assistant }
156
+ });
157
+ }
158
+ resolve("delivered");
159
+ });
160
+ });
161
+ var runGeminiBridge = async (options) => {
162
+ const sessionId = options.resumeSessionId ?? randomUUID();
163
+ const now = options.now ?? Date.now;
164
+ const idleTimeoutMs = options.idleTimeoutMs ?? SESSION_IDLE_TIMEOUT_MS;
165
+ const ownership = startProcessOwnership({ sessionId, pid: process.pid, spawnId: options.spawnId });
166
+ let linked = false;
167
+ let stopHeartbeat = () => {
168
+ };
169
+ let firstTurn = !options.resumeSessionId;
170
+ let idleSince = now();
171
+ let idledOut = false;
172
+ const relay = options.transcripts === false ? null : new LiveTranscriptRelay(options, sessionId);
173
+ const reportBody = (event, action) => ({
174
+ event,
175
+ action,
176
+ agentType: "gemini_cli",
177
+ agentName: `Gemini - ${basename(options.cwd)}`,
178
+ sessionId,
179
+ geminiSessionId: sessionId,
180
+ spawnId: options.spawnId,
181
+ machineId: options.machineId,
182
+ pid: process.pid,
183
+ cwd: options.cwd
184
+ });
185
+ const report = (event, action) => postLiveSession(options, "/api/agent/event", reportBody(event, action));
186
+ const link = async () => {
187
+ if (linked) return;
188
+ await postLiveSessionUntilAccepted(
189
+ options,
190
+ "/api/agent/event",
191
+ reportBody("session_start", options.resumeSessionId ? "Session resumed" : "Session started")
192
+ );
193
+ linked = true;
194
+ stopHeartbeat = startLiveSessionHeartbeat(
195
+ options,
196
+ reportBody("heartbeat", "Session online"),
197
+ void 0,
198
+ () => ownership.touch()
199
+ );
200
+ };
201
+ const turn = (text, commandId) => {
202
+ const result = runGeminiTurn({
203
+ bridge: options,
204
+ sessionId,
205
+ text,
206
+ commandId,
207
+ startNew: firstTurn,
208
+ relay,
209
+ onSession: link
210
+ });
211
+ firstTurn = false;
212
+ return result;
213
+ };
214
+ try {
215
+ await ownership.ready;
216
+ if (options.resumeSessionId && !options.prompt) await link();
217
+ if (options.prompt) {
218
+ try {
219
+ await turn(options.prompt, `spawn-${options.spawnId}`);
220
+ } catch (error) {
221
+ if (!linked) throw error;
222
+ await report("turn_error", "Gemini turn failed").catch(() => null);
223
+ }
224
+ idleSince = now();
225
+ }
226
+ let pendingAck = null;
227
+ while (!options.signal.aborted) {
228
+ if (idleDeadlinePassed(idleSince, now(), idleTimeoutMs)) {
229
+ idledOut = true;
230
+ break;
231
+ }
232
+ try {
233
+ if (pendingAck) {
234
+ const acknowledged = await postLiveSession(options, "/api/agent/command/ack", pendingAck);
235
+ if (acknowledged.ok) pendingAck = null;
236
+ }
237
+ if (pendingAck) {
238
+ await liveSessionSleep(options.pollIntervalMs ?? 2e3, options.signal);
239
+ continue;
240
+ }
241
+ const response = await postLiveSession(options, "/api/agent/command/drain", {
242
+ sessionId,
243
+ machineId: options.machineId,
244
+ ackMode: true
245
+ });
246
+ if (response.ok) {
247
+ const command = object((await response.json()).command);
248
+ const commandId = string(command?.id);
249
+ const text = string(command?.text);
250
+ if (commandId && text) {
251
+ idleSince = now();
252
+ try {
253
+ const outcome2 = await turn(text, commandId);
254
+ idleSince = now();
255
+ if (outcome2 === "aborted") break;
256
+ void report("stop", "Turn completed").catch(() => null);
257
+ pendingAck = {
258
+ commandId,
259
+ sessionId,
260
+ machineId: options.machineId,
261
+ state: "delivered"
262
+ };
263
+ } catch (error) {
264
+ pendingAck = {
265
+ commandId,
266
+ sessionId,
267
+ machineId: options.machineId,
268
+ state: "failed",
269
+ failureCode: "gemini_rejected",
270
+ failureMessage: (error instanceof Error ? error.message : String(error)).slice(0, 240)
271
+ };
272
+ }
273
+ }
274
+ }
275
+ } catch {
276
+ }
277
+ await relay?.flush();
278
+ await liveSessionSleep(options.pollIntervalMs ?? 2e3, options.signal);
279
+ }
280
+ await relay?.flush();
281
+ await report(
282
+ "session_closed",
283
+ idledOut ? `Session ended after ${describeIdleWindow(idleTimeoutMs)} idle` : "Session stopped"
284
+ ).catch(() => null);
285
+ return "stopped";
286
+ } catch (error) {
287
+ if (!linked && options.claimId) {
288
+ const message = error instanceof Error ? error.message : String(error);
289
+ for (let attempt = 0; attempt < 3; attempt++) {
290
+ const acknowledged = await postLiveSession(options, "/api/agent/spawn/ack", {
291
+ spawnId: options.spawnId,
292
+ claimId: options.claimId,
293
+ machineId: options.machineId,
294
+ state: "failed",
295
+ failure: {
296
+ code: "bridge_failed",
297
+ message: message.slice(0, 240),
298
+ retryable: false
299
+ }
300
+ }).catch(() => null);
301
+ if (acknowledged?.ok) break;
302
+ await liveSessionSleep(200 * (attempt + 1), options.signal);
303
+ }
304
+ }
305
+ await report("error", "Live bridge failed").catch(() => null);
306
+ return "failed";
307
+ } finally {
308
+ stopHeartbeat();
309
+ ownership.stop();
310
+ }
311
+ };
312
+
313
+ // bin/pushary-gemini-bridge.ts
314
+ var spawnId = process.env.PUSHARY_SPAWN_ID?.trim();
315
+ var requestedCwd = process.env.PUSHARY_BRIDGE_CWD?.trim();
316
+ if (!spawnId || !requestedCwd || !existsSync(requestedCwd)) {
317
+ process.stderr.write("[pushary] Gemini bridge received an invalid launch request\n");
318
+ process.exit(1);
319
+ }
320
+ var abort = new AbortController();
321
+ var signals = process.platform === "win32" ? ["SIGINT", "SIGTERM"] : ["SIGINT", "SIGTERM", "SIGHUP"];
322
+ var stop = () => abort.abort();
323
+ for (const signal of signals) process.on(signal, stop);
324
+ var outcome = await withSpawnProcessOwnership(spawnId, () => runGeminiBridge({
325
+ apiKey: getApiKey(),
326
+ baseUrl: getBaseUrl(),
327
+ machineId: getMachineId(),
328
+ spawnId,
329
+ claimId: process.env.PUSHARY_SPAWN_CLAIM_ID?.trim() || void 0,
330
+ cwd: requestedCwd,
331
+ prompt: process.env.PUSHARY_BRIDGE_PROMPT?.trim() || void 0,
332
+ model: process.env.PUSHARY_BRIDGE_MODEL?.trim() || void 0,
333
+ resumeSessionId: process.env.PUSHARY_BRIDGE_RESUME_SESSION_ID?.trim() || void 0,
334
+ command: { command: process.env.PUSHARY_GEMINI_COMMAND?.trim() || resolveBinary("gemini") || "gemini" },
335
+ signal: abort.signal,
336
+ transcripts: transcriptsEnabled()
337
+ }));
338
+ for (const signal of signals) process.off(signal, stop);
339
+ process.exit(outcome === "stopped" ? 0 : 1);
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node