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,68 @@
1
+ import {
2
+ guardBinary
3
+ } from "./chunk-A2A2YTMG.js";
4
+
5
+ // src/bell/install.ts
6
+ import { join } from "path";
7
+ var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
8
+ var ensureRecord = (parent, key) => {
9
+ const existing = asRecord(parent[key]);
10
+ if (existing) return existing;
11
+ const created = {};
12
+ parent[key] = created;
13
+ return created;
14
+ };
15
+ var BELL_HOOK_BINARY = "pushary-bell-hook";
16
+ var isBellHook = (entry) => {
17
+ const hooks = asRecord(entry)?.hooks;
18
+ if (!Array.isArray(hooks)) return false;
19
+ return hooks.some((hook) => String(asRecord(hook)?.command ?? "").includes(BELL_HOOK_BINARY));
20
+ };
21
+ var BELL_TIMEOUT_SECONDS = 5;
22
+ var BELL_NOTIFICATION_MATCHER = "idle_prompt|agent_needs_input|agent_completed";
23
+ var addBellHooks = (settings, binDir) => {
24
+ const resolve = (name) => guardBinary(binDir ? join(binDir, name) : name);
25
+ const hooks = ensureRecord(settings, "hooks");
26
+ const notification = (Array.isArray(hooks.Notification) ? hooks.Notification : []).filter((entry) => !isBellHook(entry));
27
+ notification.push({
28
+ matcher: BELL_NOTIFICATION_MATCHER,
29
+ hooks: [{ type: "command", command: resolve(BELL_HOOK_BINARY), timeout: BELL_TIMEOUT_SECONDS }]
30
+ });
31
+ hooks.Notification = notification;
32
+ const stop = Array.isArray(hooks.Stop) ? hooks.Stop.filter((entry) => !isBellHook(entry)) : [];
33
+ if (Array.isArray(hooks.Stop)) {
34
+ if (stop.length === 0) delete hooks.Stop;
35
+ else hooks.Stop = stop;
36
+ }
37
+ };
38
+ var removeBellHooks = (settings) => {
39
+ const hooks = asRecord(settings.hooks);
40
+ if (!hooks) return false;
41
+ let changed = false;
42
+ for (const key of ["Stop", "Notification"]) {
43
+ const entries = hooks[key];
44
+ if (!Array.isArray(entries)) continue;
45
+ const filtered = entries.filter((entry) => !isBellHook(entry));
46
+ if (filtered.length === entries.length) continue;
47
+ if (filtered.length === 0) delete hooks[key];
48
+ else hooks[key] = filtered;
49
+ changed = true;
50
+ }
51
+ if (Object.keys(hooks).length === 0) delete settings.hooks;
52
+ return changed;
53
+ };
54
+ var bellInstalled = (settings) => {
55
+ const hooks = asRecord(settings.hooks);
56
+ if (!hooks) return false;
57
+ return ["Stop", "Notification"].some((key) => {
58
+ const entries = hooks[key];
59
+ return Array.isArray(entries) && entries.some(isBellHook);
60
+ });
61
+ };
62
+
63
+ export {
64
+ BELL_HOOK_BINARY,
65
+ addBellHooks,
66
+ removeBellHooks,
67
+ bellInstalled
68
+ };
@@ -0,0 +1,21 @@
1
+ import {
2
+ readTranscriptPreference
3
+ } from "./chunk-P7WQEVWA.js";
4
+
5
+ // src/transcript-setting.ts
6
+ var transcriptEnvironmentPreference = () => {
7
+ const value = process.env.PUSHARY_TRANSCRIPTS?.trim().toLowerCase();
8
+ if (!value) return null;
9
+ if (["0", "false", "off"].includes(value)) return false;
10
+ if (["1", "true", "on"].includes(value)) return true;
11
+ return null;
12
+ };
13
+ var configuredPreference = () => transcriptEnvironmentPreference() ?? readTranscriptPreference();
14
+ var transcriptsEnabled = () => configuredPreference() ?? false;
15
+ var transcriptPreferenceForSetup = (explicit = null) => explicit ?? configuredPreference() ?? true;
16
+
17
+ export {
18
+ transcriptEnvironmentPreference,
19
+ transcriptsEnabled,
20
+ transcriptPreferenceForSetup
21
+ };
@@ -0,0 +1,307 @@
1
+ import {
2
+ resolveInstallSourceFor
3
+ } from "./chunk-DB3ONZB4.js";
4
+ import {
5
+ backupFile,
6
+ readJsonSafe,
7
+ writeFileAtomic,
8
+ writeJsonAtomic
9
+ } from "./chunk-T7E75EKA.js";
10
+ import {
11
+ configFilePath
12
+ } from "./chunk-GPEGOGRJ.js";
13
+
14
+ // src/shell-alias.ts
15
+ import { homedir } from "os";
16
+ import { existsSync, readFileSync, appendFileSync, writeFileSync } from "fs";
17
+ import { join } from "path";
18
+ var MARKER_START = "# >>> pushary >>>";
19
+ var MARKER_END = "# <<< pushary <<<";
20
+ var CANDIDATE_RCS = [".zshrc", ".zprofile", ".bashrc", ".bash_profile"];
21
+ var aliasBlock = () => `${MARKER_START}
22
+ # Make \`claude\` reachable from your phone when idle (runs it through Pushary).
23
+ # Remove this block, or run \`pushary clean\`, to undo.
24
+ alias claude='pushary claude'
25
+ ${MARKER_END}
26
+ `;
27
+ var hasClaudeAlias = (content) => content.includes(MARKER_START) || /alias\s+claude=['"]?pushary claude/.test(content);
28
+ var stripPusharyBlock = (content) => {
29
+ const lines = content.split("\n");
30
+ const out = [];
31
+ let inBlock = false;
32
+ for (const line of lines) {
33
+ const trimmed = line.trim();
34
+ if (trimmed === MARKER_START) {
35
+ inBlock = true;
36
+ continue;
37
+ }
38
+ if (trimmed === MARKER_END) {
39
+ inBlock = false;
40
+ continue;
41
+ }
42
+ if (!inBlock) out.push(line);
43
+ }
44
+ return out.join("\n").replace(/\n{3,}/g, "\n\n");
45
+ };
46
+ var resolveShellRc = () => {
47
+ if (process.platform === "win32") return null;
48
+ const shell = (process.env.SHELL ?? "").split("/").pop() ?? "";
49
+ const home = homedir();
50
+ if (shell === "zsh") return { path: join(home, ".zshrc"), shell };
51
+ if (shell === "bash") {
52
+ const bashrc = join(home, ".bashrc");
53
+ const profile = join(home, ".bash_profile");
54
+ if (process.platform === "darwin") return { path: profile, shell };
55
+ return { path: existsSync(bashrc) ? bashrc : profile, shell };
56
+ }
57
+ return null;
58
+ };
59
+ var installClaudeAlias = () => {
60
+ const rc = resolveShellRc();
61
+ if (!rc) return { installed: false, reason: "unsupported-shell" };
62
+ let content = "";
63
+ try {
64
+ content = readFileSync(rc.path, "utf-8");
65
+ } catch {
66
+ }
67
+ if (hasClaudeAlias(content)) return { installed: false, path: rc.path, reason: "exists" };
68
+ const gap = content.length > 0 && !content.endsWith("\n") ? "\n\n" : content.length > 0 ? "\n" : "";
69
+ appendFileSync(rc.path, `${gap}${aliasBlock()}`, "utf-8");
70
+ return { installed: true, path: rc.path };
71
+ };
72
+ var removeClaudeAlias = () => {
73
+ const removed = [];
74
+ for (const name of CANDIDATE_RCS) {
75
+ const path = join(homedir(), name);
76
+ if (!existsSync(path)) continue;
77
+ let content;
78
+ try {
79
+ content = readFileSync(path, "utf-8");
80
+ } catch {
81
+ continue;
82
+ }
83
+ const stripped = stripPusharyBlock(content);
84
+ if (stripped !== content) {
85
+ try {
86
+ writeFileSync(path, stripped, "utf-8");
87
+ removed.push(path);
88
+ } catch {
89
+ }
90
+ }
91
+ }
92
+ return removed;
93
+ };
94
+
95
+ // src/keystore.ts
96
+ import { chmodSync, existsSync as existsSync2, readFileSync as readFileSync2, realpathSync, writeFileSync as writeFileSync2 } from "fs";
97
+ import { homedir as homedir2 } from "os";
98
+ import { join as join2 } from "path";
99
+ var KEY_FILE_MODE = 384;
100
+ var FALLBACK_RCS = [".zshrc", ".zprofile", ".bashrc", ".bash_profile"];
101
+ var OUR_EXPORT = /^[ \t]*export[ \t]+PUSHARY_API_KEY=(['"]?)(pk_[a-f0-9]+\.[a-f0-9]+)\1[ \t]*$/;
102
+ var MENTIONS_KEY = /PUSHARY_API_KEY/;
103
+ var isComment = (line) => line.trimStart().startsWith("#");
104
+ var exportLine = (apiKey) => `export PUSHARY_API_KEY='${apiKey}'`;
105
+ var LOGOUT_MARK = "# removed by pushary logout";
106
+ var disabledExportLine = (line) => `# ${line.trim()} ${LOGOUT_MARK}`;
107
+ var isDisabledExport = (line) => {
108
+ const trimmed = line.trim();
109
+ if (!isComment(trimmed) || !trimmed.endsWith(LOGOUT_MARK)) return false;
110
+ return OUR_EXPORT.test(trimmed.slice(1, -LOGOUT_MARK.length).trim());
111
+ };
112
+ var keyPrefix = (key) => key.split(".")[0] ?? key;
113
+ var rcResult = (kind, over = {}) => ({
114
+ kind,
115
+ path: null,
116
+ shell: null,
117
+ previousKeyPrefix: null,
118
+ declinedLine: null,
119
+ backupPath: null,
120
+ detail: null,
121
+ ...over
122
+ });
123
+ var realPathOf = (filePath) => {
124
+ try {
125
+ return realpathSync(filePath);
126
+ } catch {
127
+ return filePath;
128
+ }
129
+ };
130
+ var rewriteRcExport = (rcPath, apiKey, shell = null) => {
131
+ const target = realPathOf(rcPath);
132
+ let content = "";
133
+ try {
134
+ content = readFileSync2(target, "utf-8");
135
+ } catch (err) {
136
+ const error = err;
137
+ if (error.code !== "ENOENT") {
138
+ return rcResult("unavailable", { path: target, shell, detail: error.message });
139
+ }
140
+ }
141
+ const lines = content.split("\n");
142
+ const foreign = lines.findIndex((line) => !isComment(line) && MENTIONS_KEY.test(line) && !OUR_EXPORT.test(line));
143
+ if (foreign >= 0) {
144
+ const match = /pk_[a-f0-9]+/.exec(lines[foreign] ?? "");
145
+ return rcResult("declined", {
146
+ path: target,
147
+ shell,
148
+ declinedLine: foreign + 1,
149
+ previousKeyPrefix: match?.[0] ?? null
150
+ });
151
+ }
152
+ const ours = lines.map((line, index) => ({ line, index })).filter((entry) => OUR_EXPORT.test(entry.line));
153
+ if (ours.length > 0) {
154
+ const previous = OUR_EXPORT.exec(ours[ours.length - 1].line)?.[2] ?? null;
155
+ if (ours.every((entry) => OUR_EXPORT.exec(entry.line)?.[2] === apiKey)) {
156
+ return rcResult("unchanged", {
157
+ path: target,
158
+ shell,
159
+ previousKeyPrefix: previous ? keyPrefix(previous) : null
160
+ });
161
+ }
162
+ const next = [...lines];
163
+ for (const entry of ours) next[entry.index] = exportLine(apiKey);
164
+ try {
165
+ const backupPath = backupFile(target);
166
+ writeFileAtomic(target, next.join("\n"), KEY_FILE_MODE);
167
+ return rcResult("updated", {
168
+ path: target,
169
+ shell,
170
+ previousKeyPrefix: previous ? keyPrefix(previous) : null,
171
+ backupPath
172
+ });
173
+ } catch (err) {
174
+ return rcResult("unavailable", { path: target, shell, detail: err.message });
175
+ }
176
+ }
177
+ const gap = content.length === 0 ? "" : content.endsWith("\n") ? "" : "\n";
178
+ try {
179
+ writeFileAtomic(target, `${content}${gap}${exportLine(apiKey)}
180
+ `, KEY_FILE_MODE);
181
+ } catch (err) {
182
+ return rcResult("unavailable", { path: target, shell, detail: err.message });
183
+ }
184
+ return rcResult("written", { path: target, shell });
185
+ };
186
+ var resolveRcTarget = () => {
187
+ const resolved = resolveShellRc();
188
+ if (resolved) return { path: resolved.path, shell: resolved.shell };
189
+ const fallback = FALLBACK_RCS.map((name) => join2(homedir2(), name)).find(existsSync2);
190
+ return fallback ? { path: fallback, shell: null } : null;
191
+ };
192
+ var writeKey = (apiKey, options = {}) => {
193
+ const configPath = configFilePath();
194
+ const existing = readJsonSafe(configPath);
195
+ let current = {};
196
+ let configBackupPath = null;
197
+ if (existing.kind === "unreadable") {
198
+ throw new Error(
199
+ `${configPath} exists but could not be read (${existing.error.message}). Fix its permissions or remove it, then re-run setup. Nothing was changed.`
200
+ );
201
+ }
202
+ if (existing.kind === "ok") {
203
+ current = existing.value;
204
+ } else if (existing.kind === "unparseable") {
205
+ configBackupPath = backupFile(configPath);
206
+ }
207
+ writeJsonAtomic(
208
+ configPath,
209
+ {
210
+ ...current,
211
+ apiKey,
212
+ installSource: resolveInstallSourceFor(current),
213
+ ...options.controlMode ? { controlMode: options.controlMode } : {},
214
+ ...options.transcriptsEnabled === void 0 ? {} : { transcriptsEnabled: options.transcriptsEnabled }
215
+ },
216
+ KEY_FILE_MODE
217
+ );
218
+ restrictFile(configPath);
219
+ const target = (options.resolveRc ?? resolveRcTarget)();
220
+ const rc = target ? rewriteRcExport(target.path, apiKey, target.shell) : rcResult("no-rc");
221
+ process.env.PUSHARY_API_KEY = apiKey;
222
+ return { configPath, rc, configBackupPath };
223
+ };
224
+ var clearKey = (options = {}) => {
225
+ const configPath = configFilePath();
226
+ const existing = readJsonSafe(configPath);
227
+ let removedFromConfig = false;
228
+ if (existing.kind === "ok" && typeof existing.value.apiKey === "string") {
229
+ const { apiKey: _dropped, ...rest } = existing.value;
230
+ writeJsonAtomic(configPath, rest, KEY_FILE_MODE);
231
+ restrictFile(configPath);
232
+ removedFromConfig = true;
233
+ }
234
+ const target = (options.resolveRc ?? resolveRcTarget)();
235
+ let rcLinesDisabled = 0;
236
+ if (target) {
237
+ try {
238
+ const real = realpathSync(target.path);
239
+ const lines = readFileSync2(real, "utf8").split("\n");
240
+ const next = lines.map((line) => {
241
+ if (!OUR_EXPORT.test(line)) return line;
242
+ rcLinesDisabled += 1;
243
+ return disabledExportLine(line);
244
+ });
245
+ if (rcLinesDisabled > 0) writeFileSync2(real, next.join("\n"));
246
+ } catch {
247
+ rcLinesDisabled = 0;
248
+ }
249
+ }
250
+ delete process.env.PUSHARY_API_KEY;
251
+ return {
252
+ removedFromConfig,
253
+ configPath,
254
+ rcPath: target?.path ?? null,
255
+ rcLinesDisabled
256
+ };
257
+ };
258
+ var restrictFile = (filePath) => {
259
+ try {
260
+ chmodSync(filePath, KEY_FILE_MODE);
261
+ } catch {
262
+ }
263
+ };
264
+ var readConfigKey = (configPath) => {
265
+ const result = readJsonSafe(configPath);
266
+ if (result.kind !== "ok") return null;
267
+ const key = typeof result.value.apiKey === "string" ? result.value.apiKey.trim() : "";
268
+ return key || null;
269
+ };
270
+ var readConfigFileKey = () => readConfigKey(configFilePath());
271
+ var readControlMode = (configPath = configFilePath()) => {
272
+ const result = readJsonSafe(configPath);
273
+ if (result.kind !== "ok") return null;
274
+ const value = result.value.controlMode;
275
+ return value === "auto" || value === "on" || value === "off" ? value : null;
276
+ };
277
+ var readTranscriptPreference = () => {
278
+ const result = readJsonSafe(configFilePath());
279
+ if (result.kind !== "ok") return null;
280
+ return typeof result.value.transcriptsEnabled === "boolean" ? result.value.transcriptsEnabled : null;
281
+ };
282
+ var readKeySource = () => {
283
+ const configPath = configFilePath();
284
+ const envKey = process.env.PUSHARY_API_KEY?.trim() || null;
285
+ const fileKey = readConfigKey(configPath);
286
+ const drift = envKey && fileKey && envKey !== fileKey ? { envPrefix: keyPrefix(envKey), configPrefix: keyPrefix(fileKey), configPath } : null;
287
+ if (envKey) return { kind: "env", key: envKey, origin: "PUSHARY_API_KEY", drift };
288
+ if (fileKey) return { kind: "config", key: fileKey, origin: configPath, drift: null };
289
+ return { kind: "none", key: null, origin: null, drift: null };
290
+ };
291
+
292
+ export {
293
+ CANDIDATE_RCS,
294
+ resolveShellRc,
295
+ installClaudeAlias,
296
+ removeClaudeAlias,
297
+ KEY_FILE_MODE,
298
+ exportLine,
299
+ isDisabledExport,
300
+ keyPrefix,
301
+ writeKey,
302
+ clearKey,
303
+ readConfigFileKey,
304
+ readControlMode,
305
+ readTranscriptPreference,
306
+ readKeySource
307
+ };
@@ -0,0 +1,12 @@
1
+ // src/cli/env-switch.ts
2
+ var CLIENT_REPORT_DISABLE_ENV = "PUSHARY_DISABLE_CLIENT_REPORTS";
3
+ var NOT_DISABLED_VALUES = /* @__PURE__ */ new Set(["", "0"]);
4
+ var isDisabledByEnv = (env, name) => {
5
+ const value = env[name];
6
+ return value !== void 0 && !NOT_DISABLED_VALUES.has(value.trim());
7
+ };
8
+
9
+ export {
10
+ CLIENT_REPORT_DISABLE_ENV,
11
+ isDisabledByEnv
12
+ };
@@ -0,0 +1,30 @@
1
+ // src/wrapper/claudeBinary.ts
2
+ import { statSync } from "fs";
3
+ import { join, delimiter } from "path";
4
+ var WRAPPER_ACTIVE_ENV = "PUSHARY_WRAPPER_ACTIVE";
5
+ var isFile = (path) => {
6
+ try {
7
+ return statSync(path).isFile();
8
+ } catch {
9
+ return false;
10
+ }
11
+ };
12
+ var findClaudeBinary = () => {
13
+ const override = process.env.PUSHARY_CLAUDE_BIN?.trim();
14
+ if (override) return isFile(override) ? override : null;
15
+ const pathVar = process.env.PATH ?? "";
16
+ const exts = process.platform === "win32" ? [".cmd", ".exe", ".bat", ""] : [""];
17
+ for (const dir of pathVar.split(delimiter)) {
18
+ if (!dir) continue;
19
+ for (const ext of exts) {
20
+ const candidate = join(dir, `claude${ext}`);
21
+ if (isFile(candidate)) return candidate;
22
+ }
23
+ }
24
+ return null;
25
+ };
26
+
27
+ export {
28
+ WRAPPER_ACTIVE_ENV,
29
+ findClaudeBinary
30
+ };
@@ -0,0 +1,37 @@
1
+ // src/cli/browser.ts
2
+ import { spawn } from "child_process";
3
+ var command = () => {
4
+ if (process.platform === "darwin") return { bin: "open", args: [] };
5
+ if (process.platform === "win32") return { bin: "cmd", args: ["/c", "start", ""] };
6
+ if (process.platform === "linux") return { bin: "xdg-open", args: [] };
7
+ return null;
8
+ };
9
+ var canOpenBrowser = () => {
10
+ if (process.env.PUSHARY_NO_BROWSER === "1") return false;
11
+ if (process.env.CI) return false;
12
+ if (process.env.SSH_TTY) return false;
13
+ if (process.platform === "linux" && !process.env.DISPLAY && !process.env.WAYLAND_DISPLAY) return false;
14
+ return command() !== null;
15
+ };
16
+ var openInBrowser = (url) => {
17
+ const resolved = command();
18
+ if (!resolved) return false;
19
+ try {
20
+ const child = spawn(resolved.bin, [...resolved.args, url], {
21
+ stdio: "ignore",
22
+ detached: true,
23
+ windowsHide: true
24
+ });
25
+ child.unref();
26
+ child.on("error", () => {
27
+ });
28
+ return true;
29
+ } catch {
30
+ return false;
31
+ }
32
+ };
33
+
34
+ export {
35
+ canOpenBrowser,
36
+ openInBrowser
37
+ };