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.
- package/CHANGELOG.md +1943 -0
- package/LICENSE +21 -0
- package/README.md +330 -0
- package/data/SKILL.md +591 -0
- package/data/cowork/SKILL.md +77 -0
- package/data/cursor-plugin/.cursor-plugin/plugin.json +25 -0
- package/data/cursor-plugin/CHANGELOG.md +70 -0
- package/data/cursor-plugin/CONTRIBUTING.md +40 -0
- package/data/cursor-plugin/LICENSE +21 -0
- package/data/cursor-plugin/README.md +113 -0
- package/data/cursor-plugin/SECURITY.md +33 -0
- package/data/cursor-plugin/assets/logo.png +0 -0
- package/data/cursor-plugin/commands/notify-when-done.md +14 -0
- package/data/cursor-plugin/commands/pushary-test.md +13 -0
- package/data/cursor-plugin/hooks/hooks.json +75 -0
- package/data/cursor-plugin/mcp.json +11 -0
- package/data/cursor-plugin/rules/pushary.mdc +40 -0
- package/data/cursor-plugin/scripts/pushary-gate.mjs +724 -0
- package/data/cursor-plugin/scripts/pushary-gate.test.mjs +137 -0
- package/data/cursor-plugin/scripts/redaction.mjs +51 -0
- package/data/cursor-plugin/skills/pushary/SKILL.md +584 -0
- package/data/vscode-plugin/.claude-plugin/plugin.json +28 -0
- package/data/vscode-plugin/.mcp.json +11 -0
- package/data/vscode-plugin/CHANGELOG.md +49 -0
- package/data/vscode-plugin/CONTRIBUTING.md +40 -0
- package/data/vscode-plugin/LICENSE +21 -0
- package/data/vscode-plugin/README.md +128 -0
- package/data/vscode-plugin/SECURITY.md +56 -0
- package/data/vscode-plugin/assets/logo.png +0 -0
- package/data/vscode-plugin/commands/notify-when-done.md +14 -0
- package/data/vscode-plugin/commands/pushary-test.md +13 -0
- package/data/vscode-plugin/hooks/hooks.json +60 -0
- package/data/vscode-plugin/scripts/pushary-gate.mjs +762 -0
- package/data/vscode-plugin/scripts/redaction.mjs +51 -0
- package/data/vscode-plugin/skills/pushary/SKILL.md +584 -0
- package/dist/bin/pushary-bell-hook.d.ts +1 -0
- package/dist/bin/pushary-bell-hook.js +46 -0
- package/dist/bin/pushary-bell.d.ts +1 -0
- package/dist/bin/pushary-bell.js +146 -0
- package/dist/bin/pushary-claude.d.ts +1 -0
- package/dist/bin/pushary-claude.js +1916 -0
- package/dist/bin/pushary-clean.d.ts +1 -0
- package/dist/bin/pushary-clean.js +1300 -0
- package/dist/bin/pushary-codex-bridge.d.ts +1 -0
- package/dist/bin/pushary-codex-bridge.js +599 -0
- package/dist/bin/pushary-codex-hook.d.ts +1 -0
- package/dist/bin/pushary-codex-hook.js +886 -0
- package/dist/bin/pushary-codex.d.ts +1 -0
- package/dist/bin/pushary-codex.js +125 -0
- package/dist/bin/pushary-connect.d.ts +1 -0
- package/dist/bin/pushary-connect.js +100 -0
- package/dist/bin/pushary-cowork.d.ts +1 -0
- package/dist/bin/pushary-cowork.js +36 -0
- package/dist/bin/pushary-daemon-supervisor.d.ts +1 -0
- package/dist/bin/pushary-daemon-supervisor.js +27 -0
- package/dist/bin/pushary-daemon.d.ts +1 -0
- package/dist/bin/pushary-daemon.js +878 -0
- package/dist/bin/pushary-disconnect.d.ts +1 -0
- package/dist/bin/pushary-disconnect.js +264 -0
- package/dist/bin/pushary-doctor.d.ts +1 -0
- package/dist/bin/pushary-doctor.js +1433 -0
- package/dist/bin/pushary-elicitation-hook.d.ts +1 -0
- package/dist/bin/pushary-elicitation-hook.js +149 -0
- package/dist/bin/pushary-gemini-bridge.d.ts +1 -0
- package/dist/bin/pushary-gemini-bridge.js +339 -0
- package/dist/bin/pushary-gemini-hook.d.ts +1 -0
- package/dist/bin/pushary-gemini-hook.js +381 -0
- package/dist/bin/pushary-hook.d.ts +1 -0
- package/dist/bin/pushary-hook.js +95 -0
- package/dist/bin/pushary-login.d.ts +1 -0
- package/dist/bin/pushary-login.js +198 -0
- package/dist/bin/pushary-logout.d.ts +1 -0
- package/dist/bin/pushary-logout.js +147 -0
- package/dist/bin/pushary-mcp.d.ts +1 -0
- package/dist/bin/pushary-mcp.js +114 -0
- package/dist/bin/pushary-mode.d.ts +1 -0
- package/dist/bin/pushary-mode.js +130 -0
- package/dist/bin/pushary-notification-hook.d.ts +1 -0
- package/dist/bin/pushary-notification-hook.js +56 -0
- package/dist/bin/pushary-opencode-hook.d.ts +1 -0
- package/dist/bin/pushary-opencode-hook.js +338 -0
- package/dist/bin/pushary-permission-denied-hook.d.ts +1 -0
- package/dist/bin/pushary-permission-denied-hook.js +60 -0
- package/dist/bin/pushary-permission-hook.d.ts +1 -0
- package/dist/bin/pushary-permission-hook.js +65 -0
- package/dist/bin/pushary-post-hook.d.ts +1 -0
- package/dist/bin/pushary-post-hook.js +70 -0
- package/dist/bin/pushary-prompt-hook.d.ts +1 -0
- package/dist/bin/pushary-prompt-hook.js +61 -0
- package/dist/bin/pushary-session-end-hook.d.ts +1 -0
- package/dist/bin/pushary-session-end-hook.js +59 -0
- package/dist/bin/pushary-session-start-hook.d.ts +1 -0
- package/dist/bin/pushary-session-start-hook.js +72 -0
- package/dist/bin/pushary-setup.d.ts +1 -0
- package/dist/bin/pushary-setup.js +2660 -0
- package/dist/bin/pushary-stats.d.ts +1 -0
- package/dist/bin/pushary-stats.js +44 -0
- package/dist/bin/pushary-status.d.ts +1 -0
- package/dist/bin/pushary-status.js +262 -0
- package/dist/bin/pushary-stop-hook.d.ts +1 -0
- package/dist/bin/pushary-stop-hook.js +63 -0
- package/dist/bin/pushary-stopfailure-hook.d.ts +1 -0
- package/dist/bin/pushary-stopfailure-hook.js +56 -0
- package/dist/bin/pushary-suggestions.d.ts +1 -0
- package/dist/bin/pushary-suggestions.js +100 -0
- package/dist/bin/pushary-transcript-register.d.ts +1 -0
- package/dist/bin/pushary-transcript-register.js +40 -0
- package/dist/bin/pushary-transcripts.d.ts +1 -0
- package/dist/bin/pushary-transcripts.js +77 -0
- package/dist/bin/pushary-upgrade.d.ts +1 -0
- package/dist/bin/pushary-upgrade.js +599 -0
- package/dist/bin/pushary-wait.d.ts +1 -0
- package/dist/bin/pushary-wait.js +122 -0
- package/dist/bin/pushary.d.ts +1 -0
- package/dist/bin/pushary.js +74 -0
- package/dist/chunk-2ABTSGFT.js +173 -0
- package/dist/chunk-2OY3ZZ5N.js +124 -0
- package/dist/chunk-3EGEA4KH.js +44 -0
- package/dist/chunk-3EVPNIBE.js +185 -0
- package/dist/chunk-5OP5MQI7.js +45 -0
- package/dist/chunk-6J2JC2RT.js +149 -0
- package/dist/chunk-7QLSKOSU.js +19 -0
- package/dist/chunk-7Z7Q4GGS.js +88 -0
- package/dist/chunk-A2A2YTMG.js +835 -0
- package/dist/chunk-AHV4LNB5.js +377 -0
- package/dist/chunk-AXPCESYO.js +197 -0
- package/dist/chunk-C2WCPBF7.js +86 -0
- package/dist/chunk-CJVVKOSR.js +73 -0
- package/dist/chunk-CULUZTWQ.js +2117 -0
- package/dist/chunk-CVQYGMSR.js +142 -0
- package/dist/chunk-CVWG2N3Y.js +16 -0
- package/dist/chunk-D2FX3EPW.js +15 -0
- package/dist/chunk-DB3ONZB4.js +44 -0
- package/dist/chunk-DE4Y6TCC.js +160 -0
- package/dist/chunk-E4FJTUH4.js +249 -0
- package/dist/chunk-EPCXUP3P.js +41 -0
- package/dist/chunk-EX3GAPA4.js +44 -0
- package/dist/chunk-EYK3GSRH.js +143 -0
- package/dist/chunk-GMXKITVA.js +70 -0
- package/dist/chunk-GPEGOGRJ.js +49 -0
- package/dist/chunk-GU3EJCVV.js +43 -0
- package/dist/chunk-HQ6G3B5R.js +59 -0
- package/dist/chunk-HZVVNGEW.js +22 -0
- package/dist/chunk-IPVKH2ST.js +231 -0
- package/dist/chunk-J5TIV3J6.js +238 -0
- package/dist/chunk-JU6W3XAU.js +64 -0
- package/dist/chunk-JYG4B33G.js +227 -0
- package/dist/chunk-JZTHGAPA.js +503 -0
- package/dist/chunk-KGDWDSNL.js +102 -0
- package/dist/chunk-KVQBWI5T.js +99 -0
- package/dist/chunk-KZERVKTD.js +16 -0
- package/dist/chunk-LXQ5FBBD.js +47 -0
- package/dist/chunk-M5ICYGHT.js +138 -0
- package/dist/chunk-M5JDZ45W.js +943 -0
- package/dist/chunk-MDEPU45E.js +1226 -0
- package/dist/chunk-MPWXGONV.js +157 -0
- package/dist/chunk-N6IOSZOE.js +185 -0
- package/dist/chunk-NGDD6TXY.js +192 -0
- package/dist/chunk-NQTHFF4W.js +2830 -0
- package/dist/chunk-OKXS7WDZ.js +68 -0
- package/dist/chunk-OLQRRFBX.js +21 -0
- package/dist/chunk-P7WQEVWA.js +307 -0
- package/dist/chunk-POFBE6ZY.js +12 -0
- package/dist/chunk-PQ4K74WL.js +30 -0
- package/dist/chunk-PUZZELE6.js +37 -0
- package/dist/chunk-Q7E4I6UA.js +767 -0
- package/dist/chunk-QCGNGEGE.js +15 -0
- package/dist/chunk-R7437YI3.js +151 -0
- package/dist/chunk-RHR6NARM.js +305 -0
- package/dist/chunk-SAXBC4AZ.js +60 -0
- package/dist/chunk-SGZJ5LYH.js +76 -0
- package/dist/chunk-SIFVFP6T.js +70 -0
- package/dist/chunk-SLFABHW3.js +150 -0
- package/dist/chunk-T7E75EKA.js +67 -0
- package/dist/chunk-TY4JT7W3.js +38 -0
- package/dist/chunk-U4TMAANR.js +111 -0
- package/dist/chunk-UI36QSBI.js +177 -0
- package/dist/chunk-VQKRG3AC.js +339 -0
- package/dist/chunk-WE2J62NC.js +1131 -0
- package/dist/chunk-XHKBHWLX.js +14 -0
- package/dist/chunk-ZSZSSJCV.js +275 -0
- package/dist/chunk-ZVSKMPBZ.js +135 -0
- package/dist/reapply-CW3MA66V.js +34 -0
- package/dist/src/index.d.ts +204 -0
- package/dist/src/index.js +64 -0
- package/package.json +108 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CLIENT_REPORT_DISABLE_ENV,
|
|
3
|
+
isDisabledByEnv
|
|
4
|
+
} from "./chunk-POFBE6ZY.js";
|
|
5
|
+
import {
|
|
6
|
+
isNetworkFailure
|
|
7
|
+
} from "./chunk-2OY3ZZ5N.js";
|
|
8
|
+
import {
|
|
9
|
+
clientReportClaims
|
|
10
|
+
} from "./chunk-A2A2YTMG.js";
|
|
11
|
+
import {
|
|
12
|
+
getApiKey,
|
|
13
|
+
getBaseUrl
|
|
14
|
+
} from "./chunk-GPEGOGRJ.js";
|
|
15
|
+
import {
|
|
16
|
+
CLIENT_REPORT_PATH,
|
|
17
|
+
degradedLine
|
|
18
|
+
} from "./chunk-NQTHFF4W.js";
|
|
19
|
+
|
|
20
|
+
// src/client-report-claim.ts
|
|
21
|
+
import { createHash } from "crypto";
|
|
22
|
+
import { closeSync, mkdirSync, openSync, readdirSync, unlinkSync } from "fs";
|
|
23
|
+
import { join } from "path";
|
|
24
|
+
var CLIENT_REPORT_WINDOW_MS = 6e4;
|
|
25
|
+
var CLAIM_DIRECTORY_MODE = 448;
|
|
26
|
+
var CLAIM_FILE_MODE = 384;
|
|
27
|
+
var CLAIM_KEY_HASH_LENGTH = 16;
|
|
28
|
+
var CLAIM_FILE_PATTERN = /^[0-9a-f]{16}-(\d+)$/;
|
|
29
|
+
var EXCLUSIVE_CREATE = "wx";
|
|
30
|
+
var ALREADY_EXISTS = "EEXIST";
|
|
31
|
+
var WINDOWS_KEPT_BEHIND = 1;
|
|
32
|
+
var reportWindowOf = (now) => Math.floor(now / CLIENT_REPORT_WINDOW_MS);
|
|
33
|
+
var claimFileName = (key, window) => `${createHash("sha256").update(key).digest("hex").slice(0, CLAIM_KEY_HASH_LENGTH)}-${window}`;
|
|
34
|
+
var errorCodeOf = (error) => typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : null;
|
|
35
|
+
var claimWindowOf = (fileName) => {
|
|
36
|
+
const match = CLAIM_FILE_PATTERN.exec(fileName);
|
|
37
|
+
return match ? Number(match[1]) : null;
|
|
38
|
+
};
|
|
39
|
+
var isStaleClaim = (fileName, window) => {
|
|
40
|
+
const claimedWindow = claimWindowOf(fileName);
|
|
41
|
+
return claimedWindow !== null && claimedWindow < window - WINDOWS_KEPT_BEHIND;
|
|
42
|
+
};
|
|
43
|
+
var removeQuietly = (filePath) => {
|
|
44
|
+
try {
|
|
45
|
+
unlinkSync(filePath);
|
|
46
|
+
} catch {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
var removeStaleClaims = (claimsDir, window) => {
|
|
51
|
+
try {
|
|
52
|
+
for (const fileName of readdirSync(claimsDir)) {
|
|
53
|
+
if (isStaleClaim(fileName, window)) removeQuietly(join(claimsDir, fileName));
|
|
54
|
+
}
|
|
55
|
+
} catch {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
var claimReportWindow = (claimsDir, key, now) => {
|
|
60
|
+
const window = reportWindowOf(now);
|
|
61
|
+
try {
|
|
62
|
+
mkdirSync(claimsDir, { recursive: true, mode: CLAIM_DIRECTORY_MODE });
|
|
63
|
+
closeSync(openSync(join(claimsDir, claimFileName(key, window)), EXCLUSIVE_CREATE, CLAIM_FILE_MODE));
|
|
64
|
+
} catch (error) {
|
|
65
|
+
return errorCodeOf(error) === ALREADY_EXISTS ? "already_claimed" : "unavailable";
|
|
66
|
+
}
|
|
67
|
+
removeStaleClaims(claimsDir, window);
|
|
68
|
+
return "claimed";
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// src/client-report.ts
|
|
72
|
+
var CLIENT_REPORT_TIMEOUT_MS = 1e3;
|
|
73
|
+
var readApiKey = () => {
|
|
74
|
+
try {
|
|
75
|
+
return getApiKey();
|
|
76
|
+
} catch {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
var defaultDeps = () => ({
|
|
81
|
+
disabled: () => isDisabledByEnv(process.env, CLIENT_REPORT_DISABLE_ENV),
|
|
82
|
+
fetchImpl: fetch,
|
|
83
|
+
apiKey: readApiKey,
|
|
84
|
+
baseUrl: getBaseUrl,
|
|
85
|
+
now: Date.now,
|
|
86
|
+
claimsDir: clientReportClaims()
|
|
87
|
+
});
|
|
88
|
+
var reportKey = (event) => `${event.kind}:${event.code}`;
|
|
89
|
+
var createClientErrorReporter = (deps) => {
|
|
90
|
+
const windowClaimedInProcess = /* @__PURE__ */ new Map();
|
|
91
|
+
const claimInProcess = (key, now) => {
|
|
92
|
+
if (windowClaimedInProcess.get(key) === reportWindowOf(now)) return false;
|
|
93
|
+
windowClaimedInProcess.set(key, reportWindowOf(now));
|
|
94
|
+
return true;
|
|
95
|
+
};
|
|
96
|
+
const claimReport = (key, now) => {
|
|
97
|
+
const claim = claimReportWindow(deps.claimsDir, key, now);
|
|
98
|
+
return claim === "unavailable" ? claimInProcess(key, now) : claim === "claimed";
|
|
99
|
+
};
|
|
100
|
+
return async (event) => {
|
|
101
|
+
try {
|
|
102
|
+
if (deps.disabled()) return;
|
|
103
|
+
const apiKey = deps.apiKey();
|
|
104
|
+
const now = deps.now();
|
|
105
|
+
if (!apiKey || !claimReport(reportKey(event), now)) return;
|
|
106
|
+
await deps.fetchImpl(`${deps.baseUrl()}${CLIENT_REPORT_PATH}`, {
|
|
107
|
+
method: "POST",
|
|
108
|
+
headers: { "Authorization": `Bearer ${apiKey}`, "Content-Type": "application/json" },
|
|
109
|
+
body: JSON.stringify({ ...event, occurredAt: new Date(now).toISOString() }),
|
|
110
|
+
signal: AbortSignal.timeout(CLIENT_REPORT_TIMEOUT_MS)
|
|
111
|
+
});
|
|
112
|
+
} catch {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
};
|
|
117
|
+
var processReporter = null;
|
|
118
|
+
var inFlightReports = /* @__PURE__ */ new Set();
|
|
119
|
+
var reportClientError = (event) => {
|
|
120
|
+
processReporter ??= createClientErrorReporter(defaultDeps());
|
|
121
|
+
const report = processReporter(event);
|
|
122
|
+
inFlightReports.add(report);
|
|
123
|
+
void report.finally(() => inFlightReports.delete(report));
|
|
124
|
+
return report;
|
|
125
|
+
};
|
|
126
|
+
var exitAfterClientReports = async (code) => {
|
|
127
|
+
await Promise.all(inFlightReports);
|
|
128
|
+
return process.exit(code);
|
|
129
|
+
};
|
|
130
|
+
var clientErrorKindOf = (error) => isNetworkFailure(error) ? "network" : "unexpected";
|
|
131
|
+
var writeDegraded = (subsystem, report) => {
|
|
132
|
+
process.stderr.write(`${degradedLine(subsystem, report)}
|
|
133
|
+
`);
|
|
134
|
+
void reportClientError({ kind: "degraded", code: `${subsystem}:${report.reason}` });
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
export {
|
|
138
|
+
reportClientError,
|
|
139
|
+
exitAfterClientReports,
|
|
140
|
+
clientErrorKindOf,
|
|
141
|
+
writeDegraded
|
|
142
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// src/wait-ladder.ts
|
|
2
|
+
var HAND_BACK = "then hand back to the agent's own prompt";
|
|
3
|
+
var describeWaitLadder = (p) => {
|
|
4
|
+
if (p.mode === "terminal_only") return "no approval alerts, the agent's own prompt decides";
|
|
5
|
+
if (p.mode === "notify_only") return "send an approval alert, then the agent's own prompt decides";
|
|
6
|
+
if (p.mode === "push_only") {
|
|
7
|
+
if (p.timeoutAction === "wait") return `wait for your phone, ${HAND_BACK}`;
|
|
8
|
+
const tail = p.timeoutAction === "approve" ? "then auto-approve" : p.timeoutAction === "deny" ? "then auto-deny" : HAND_BACK;
|
|
9
|
+
return `wait for your phone up to ${p.timeoutSeconds}s, ${tail}`;
|
|
10
|
+
}
|
|
11
|
+
return `when you are away or Pushary cannot tell, ask your phone for ${p.pushFirstSeconds}s, ${HAND_BACK}`;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export {
|
|
15
|
+
describeWaitLadder
|
|
16
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {
|
|
2
|
+
HOOK_HOLD_BUDGET_ENV,
|
|
3
|
+
hookHoldBudgetProof
|
|
4
|
+
} from "./chunk-NQTHFF4W.js";
|
|
5
|
+
|
|
6
|
+
// src/hold-budget.ts
|
|
7
|
+
var takeHookHoldBudget = (args = process.argv.slice(2), env = process.env) => {
|
|
8
|
+
const proof = hookHoldBudgetProof(args, env);
|
|
9
|
+
delete env[HOOK_HOLD_BUDGET_ENV];
|
|
10
|
+
return proof;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export {
|
|
14
|
+
takeHookHoldBudget
|
|
15
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import {
|
|
2
|
+
configFilePath
|
|
3
|
+
} from "./chunk-GPEGOGRJ.js";
|
|
4
|
+
import {
|
|
5
|
+
normalizeInstallSource
|
|
6
|
+
} from "./chunk-NQTHFF4W.js";
|
|
7
|
+
|
|
8
|
+
// src/install-source.ts
|
|
9
|
+
import { readFileSync } from "fs";
|
|
10
|
+
var ENV_OVERRIDE = "PUSHARY_INSTALL_SOURCE";
|
|
11
|
+
var PLUGIN_ROOT = "CLAUDE_PLUGIN_ROOT";
|
|
12
|
+
var EDITOR_PLUGIN_MARKERS = ["cursor-plugin", "vscode-plugin"];
|
|
13
|
+
var detectInstallSource = (env = process.env, argv = process.argv) => {
|
|
14
|
+
const declared = env[ENV_OVERRIDE]?.trim();
|
|
15
|
+
if (declared) return normalizeInstallSource(declared);
|
|
16
|
+
const pluginRoot = env[PLUGIN_ROOT]?.trim();
|
|
17
|
+
if (pluginRoot) {
|
|
18
|
+
return EDITOR_PLUGIN_MARKERS.some((marker) => pluginRoot.includes(marker)) ? "editor_plugin" : "claude_plugin";
|
|
19
|
+
}
|
|
20
|
+
const entry = argv[1] ?? "";
|
|
21
|
+
if (entry.includes("_npx")) return "npx";
|
|
22
|
+
if (entry) return "npm";
|
|
23
|
+
return "unknown";
|
|
24
|
+
};
|
|
25
|
+
var storedInstallSource = () => {
|
|
26
|
+
try {
|
|
27
|
+
const parsed = JSON.parse(readFileSync(configFilePath(), "utf-8"));
|
|
28
|
+
const stored = parsed.installSource;
|
|
29
|
+
return typeof stored === "string" && stored ? normalizeInstallSource(stored) : void 0;
|
|
30
|
+
} catch {
|
|
31
|
+
return void 0;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
var resolveInstallSourceFor = (config) => {
|
|
35
|
+
const stored = config.installSource;
|
|
36
|
+
if (typeof stored === "string" && stored) return normalizeInstallSource(stored);
|
|
37
|
+
return detectInstallSource();
|
|
38
|
+
};
|
|
39
|
+
var reportedInstallSource = () => storedInstallSource() ?? detectInstallSource();
|
|
40
|
+
|
|
41
|
+
export {
|
|
42
|
+
resolveInstallSourceFor,
|
|
43
|
+
reportedInstallSource
|
|
44
|
+
};
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import {
|
|
2
|
+
detectProviderLiveCapabilities,
|
|
3
|
+
hasSpawnProvider,
|
|
4
|
+
resolveControlPolicy
|
|
5
|
+
} from "./chunk-HQ6G3B5R.js";
|
|
6
|
+
import {
|
|
7
|
+
daemonHasRemoteProof,
|
|
8
|
+
daemonServiceManagerAvailable,
|
|
9
|
+
hasDaemonServiceDefinition,
|
|
10
|
+
readDaemonNeedsLogin,
|
|
11
|
+
readDaemonStatus
|
|
12
|
+
} from "./chunk-WE2J62NC.js";
|
|
13
|
+
import {
|
|
14
|
+
readControlMode,
|
|
15
|
+
readKeySource
|
|
16
|
+
} from "./chunk-P7WQEVWA.js";
|
|
17
|
+
import {
|
|
18
|
+
getMachineId
|
|
19
|
+
} from "./chunk-LXQ5FBBD.js";
|
|
20
|
+
import {
|
|
21
|
+
getBaseUrl
|
|
22
|
+
} from "./chunk-GPEGOGRJ.js";
|
|
23
|
+
import {
|
|
24
|
+
getPackageVersion
|
|
25
|
+
} from "./chunk-NQTHFF4W.js";
|
|
26
|
+
|
|
27
|
+
// src/machines.ts
|
|
28
|
+
import { hostname } from "os";
|
|
29
|
+
var MACHINES_PATH = "/api/v1/server/machines";
|
|
30
|
+
var HEARTBEAT_INTERVAL_MS = 6e4;
|
|
31
|
+
var sendHeartbeat = async (apiKey, input = {}, deps = {}) => {
|
|
32
|
+
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
33
|
+
const baseUrl = deps.baseUrl ?? getBaseUrl();
|
|
34
|
+
try {
|
|
35
|
+
const res = await fetchImpl(`${baseUrl}${MACHINES_PATH}`, {
|
|
36
|
+
method: "POST",
|
|
37
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` },
|
|
38
|
+
body: JSON.stringify({
|
|
39
|
+
machineId: getMachineId(),
|
|
40
|
+
hostname: hostname(),
|
|
41
|
+
cliVersion: getPackageVersion(),
|
|
42
|
+
repoKey: input.repoKey ?? null,
|
|
43
|
+
capabilities: input.capabilities ?? []
|
|
44
|
+
}),
|
|
45
|
+
signal: AbortSignal.timeout(1e4)
|
|
46
|
+
});
|
|
47
|
+
if (res.status === 401 || res.status === 403) return { kind: "unauthorized" };
|
|
48
|
+
if (!res.ok) return { kind: "failed", detail: `HTTP ${res.status}` };
|
|
49
|
+
return { kind: "ok" };
|
|
50
|
+
} catch (err) {
|
|
51
|
+
return { kind: "failed", detail: err instanceof Error ? err.message : "network error" };
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
var parseMachine = (value) => {
|
|
55
|
+
if (typeof value !== "object" || value === null) return null;
|
|
56
|
+
const raw = value;
|
|
57
|
+
if (typeof raw.machineId !== "string" || typeof raw.hostname !== "string") return null;
|
|
58
|
+
return {
|
|
59
|
+
machineId: raw.machineId,
|
|
60
|
+
hostname: raw.hostname,
|
|
61
|
+
cliVersion: typeof raw.cliVersion === "string" ? raw.cliVersion : "unknown",
|
|
62
|
+
repoKey: typeof raw.repoKey === "string" ? raw.repoKey : null,
|
|
63
|
+
capabilities: Array.isArray(raw.capabilities) ? raw.capabilities.filter((c) => typeof c === "string") : [],
|
|
64
|
+
lastSeenAt: typeof raw.lastSeenAt === "string" ? raw.lastSeenAt : "",
|
|
65
|
+
lastControlAt: typeof raw.lastControlAt === "string" ? raw.lastControlAt : raw.lastControlAt === null ? null : void 0,
|
|
66
|
+
online: raw.online === true
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
var machineReadiness = (machines, machineId, expectedCapabilities, now = Date.now()) => {
|
|
70
|
+
const machine = machines.find((item) => item.machineId === machineId);
|
|
71
|
+
if (!machine) return { kind: "not-ready", detail: "this machine has not heartbeated to the server" };
|
|
72
|
+
if (!machine.online) return { kind: "not-ready", detail: "the server reports this machine offline" };
|
|
73
|
+
if (machine.lastControlAt === void 0) {
|
|
74
|
+
return { kind: "unsupported", detail: "the server does not report control readiness yet" };
|
|
75
|
+
}
|
|
76
|
+
if (machine.lastControlAt === null || !Number.isFinite(Date.parse(machine.lastControlAt))) {
|
|
77
|
+
return { kind: "not-ready", detail: "the server has not observed this machine polling the control queue" };
|
|
78
|
+
}
|
|
79
|
+
if (!daemonHasRemoteProof({ kind: "running", lastSuccessfulHeartbeatAt: machine.lastSeenAt, lastSuccessfulControlAt: machine.lastControlAt }, now)) {
|
|
80
|
+
return { kind: "not-ready", detail: "the heartbeat or control-queue proof is stale; wake this machine and retry pushary doctor" };
|
|
81
|
+
}
|
|
82
|
+
const missing = expectedCapabilities.filter((capability) => !machine.capabilities.includes(capability));
|
|
83
|
+
return missing.length === 0 ? { kind: "ready", machine } : { kind: "not-ready", detail: `the server is missing capabilities: ${missing.join(", ")}` };
|
|
84
|
+
};
|
|
85
|
+
var readMachineReadiness = async (apiKey, expectedCapabilities, deps = {}) => {
|
|
86
|
+
const listed = await listMachines(apiKey, deps);
|
|
87
|
+
return listed.kind === "ok" ? machineReadiness(listed.machines, deps.machineId ?? getMachineId(), expectedCapabilities, deps.now?.()) : listed.kind === "unsupported" ? { kind: "unsupported", detail: "the server does not report machine readiness yet" } : listed;
|
|
88
|
+
};
|
|
89
|
+
var waitForMachineReadiness = async (apiKey, expectedCapabilities, deps = {}) => {
|
|
90
|
+
const attempts = deps.attempts ?? 21;
|
|
91
|
+
const sleep = deps.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
92
|
+
let result = { kind: "not-ready", detail: "this machine has not heartbeated to the server" };
|
|
93
|
+
for (let attempt = 0; attempt < attempts; attempt++) {
|
|
94
|
+
result = await readMachineReadiness(apiKey, expectedCapabilities, deps);
|
|
95
|
+
if (result.kind === "ready" || result.kind === "unsupported" || result.kind === "failed" && /^HTTP (?:401|403)$/.test(result.detail)) return result;
|
|
96
|
+
if (attempt + 1 < attempts) await sleep(250);
|
|
97
|
+
}
|
|
98
|
+
return result;
|
|
99
|
+
};
|
|
100
|
+
var listMachines = async (apiKey, deps = {}) => {
|
|
101
|
+
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
102
|
+
const baseUrl = deps.baseUrl ?? getBaseUrl();
|
|
103
|
+
try {
|
|
104
|
+
const res = await fetchImpl(`${baseUrl}${MACHINES_PATH}`, {
|
|
105
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
106
|
+
signal: AbortSignal.timeout(1e4)
|
|
107
|
+
});
|
|
108
|
+
if (res.status === 404) return { kind: "unsupported" };
|
|
109
|
+
if (!res.ok) return { kind: "failed", detail: `HTTP ${res.status}` };
|
|
110
|
+
const body = await res.json().catch(() => null);
|
|
111
|
+
const list = body && Array.isArray(body.machines) ? body.machines : [];
|
|
112
|
+
return { kind: "ok", machines: list.map(parseMachine).filter((m) => m !== null) };
|
|
113
|
+
} catch (err) {
|
|
114
|
+
return { kind: "failed", detail: err instanceof Error ? err.message : "network error" };
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
var describeLastSeen = (iso, now = Date.now()) => {
|
|
118
|
+
const seen = Date.parse(iso);
|
|
119
|
+
if (!Number.isFinite(seen)) return "unknown";
|
|
120
|
+
const seconds = Math.max(0, Math.round((now - seen) / 1e3));
|
|
121
|
+
if (seconds < 60) return "just now";
|
|
122
|
+
const minutes = Math.round(seconds / 60);
|
|
123
|
+
if (minutes < 60) return `${minutes}m ago`;
|
|
124
|
+
const hours = Math.round(minutes / 60);
|
|
125
|
+
if (hours < 48) return `${hours}h ago`;
|
|
126
|
+
return `${Math.round(hours / 24)}d ago`;
|
|
127
|
+
};
|
|
128
|
+
var readManagedControlEvidence = (apiKey, providers, serverProof) => {
|
|
129
|
+
const now = Date.now();
|
|
130
|
+
const daemon = readDaemonStatus();
|
|
131
|
+
const capabilities = detectProviderLiveCapabilities(providers);
|
|
132
|
+
const providerAvailable = hasSpawnProvider(capabilities);
|
|
133
|
+
const managerAvailable = daemonServiceManagerAvailable();
|
|
134
|
+
const serviceInstalled = hasDaemonServiceDefinition();
|
|
135
|
+
const keyCurrent = Boolean(apiKey) && readKeySource().key === apiKey && readDaemonNeedsLogin().kind !== "blocked";
|
|
136
|
+
const policy = resolveControlPolicy({ mode: readControlMode(), providerAvailable, managerAvailable, servicePresent: serviceInstalled || daemon.kind !== "absent" });
|
|
137
|
+
const localProof = daemonHasRemoteProof(daemon, now);
|
|
138
|
+
const remote = serverProof?.kind === "ready" ? machineReadiness([serverProof.machine], daemon.machineId ?? "", ["spawn", ...capabilities], now) : serverProof;
|
|
139
|
+
return {
|
|
140
|
+
ready: keyCurrent && policy === "enabled" && providerAvailable && managerAvailable && serviceInstalled && localProof && remote?.kind === "ready",
|
|
141
|
+
keyCurrent,
|
|
142
|
+
providerAvailable,
|
|
143
|
+
managerAvailable,
|
|
144
|
+
serviceInstalled,
|
|
145
|
+
policy,
|
|
146
|
+
localProof,
|
|
147
|
+
serverProof: remote?.kind ?? "unchecked",
|
|
148
|
+
checkedAt: new Date(now).toISOString()
|
|
149
|
+
};
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
export {
|
|
153
|
+
HEARTBEAT_INTERVAL_MS,
|
|
154
|
+
sendHeartbeat,
|
|
155
|
+
readMachineReadiness,
|
|
156
|
+
waitForMachineReadiness,
|
|
157
|
+
listMachines,
|
|
158
|
+
describeLastSeen,
|
|
159
|
+
readManagedControlEvidence
|
|
160
|
+
};
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SETUP_AGENT_CHOICES
|
|
3
|
+
} from "./chunk-NQTHFF4W.js";
|
|
4
|
+
|
|
5
|
+
// src/cli/commands.ts
|
|
6
|
+
var NPX_LATEST_COMMAND = "npx @pushary/agent-hooks@latest";
|
|
7
|
+
var COMMAND_LIST = [
|
|
8
|
+
{
|
|
9
|
+
name: "cowork",
|
|
10
|
+
module: "pushary-cowork",
|
|
11
|
+
summary: "Set up Claude Desktop and Cowork with the Pushary connector"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
name: "setup",
|
|
15
|
+
module: "pushary-setup",
|
|
16
|
+
summary: "Configure Claude Code, Codex, Gemini CLI, Hermes, Cursor, VS Code, OpenCode, or fx with Pushary"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
name: "claude",
|
|
20
|
+
module: "pushary-claude",
|
|
21
|
+
summary: "Run Claude Code through Pushary, reachable from your phone even when idle",
|
|
22
|
+
detail: [
|
|
23
|
+
"Send an instruction and it drives a fully idle agent. Ctrl-] takes the terminal back.",
|
|
24
|
+
"Add --remote to start headless."
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: "daemon",
|
|
29
|
+
module: "pushary-daemon",
|
|
30
|
+
summary: "Keep this machine reachable so your phone can START a new session",
|
|
31
|
+
detail: [
|
|
32
|
+
"Install the native per-user service for restart-on-login and restart-on-failure.",
|
|
33
|
+
"A bare `pushary daemon` remains foreground mode for diagnostics."
|
|
34
|
+
]
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: "status",
|
|
38
|
+
module: "pushary-status",
|
|
39
|
+
summary: "One screen: which key is in force, whether the server accepts it, what can receive an approval"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: "login",
|
|
43
|
+
module: "pushary-login",
|
|
44
|
+
summary: "Sign in from this terminal, no key to copy (--with-token to pipe one in)"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: "logout",
|
|
48
|
+
module: "pushary-logout",
|
|
49
|
+
summary: "Remove the key this machine stores, and say what it does not remove"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: "connect",
|
|
53
|
+
module: "pushary-connect",
|
|
54
|
+
summary: "Connect a phone without re-running setup (--app for the Pushary app)"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
name: "bell",
|
|
58
|
+
module: "pushary-bell",
|
|
59
|
+
summary: "Free local bell when an agent finishes or needs you. No account, no key, no network",
|
|
60
|
+
detail: [
|
|
61
|
+
"A different, smaller product: it makes a noise on this machine and nothing else.",
|
|
62
|
+
"It cannot tell you which of several agents is asking, and it cannot reach your phone."
|
|
63
|
+
]
|
|
64
|
+
},
|
|
65
|
+
{ name: "doctor", module: "pushary-doctor", summary: "Verify your Pushary installation is working" },
|
|
66
|
+
{ name: "disconnect", module: "pushary-disconnect", summary: "Turn off one agent (claude|codex|gemini|cursor)" },
|
|
67
|
+
{ name: "clean", module: "pushary-clean", summary: "Remove Pushary from this machine (--everything: the Mac app's setup too, --dry-run: preview)" },
|
|
68
|
+
{ name: "mode", module: "pushary-mode", summary: "Choose where approval requests appear" },
|
|
69
|
+
{ name: "wait", module: "pushary-wait", summary: 'Show or set the "wait for your phone" ladder (pushary wait 45)' },
|
|
70
|
+
{ name: "stats", module: "pushary-stats", summary: "Show the approval moments your agents hit while not connected" },
|
|
71
|
+
{
|
|
72
|
+
name: "suggestions",
|
|
73
|
+
module: "pushary-suggestions",
|
|
74
|
+
summary: "List rules mined from your own approvals",
|
|
75
|
+
detail: ["Accept one with 'pushary suggestions accept <id>' to always-allow that pattern."]
|
|
76
|
+
},
|
|
77
|
+
{ name: "upgrade", module: "pushary-upgrade", summary: "Update the globally installed hooks to the latest version" },
|
|
78
|
+
{
|
|
79
|
+
name: "hook",
|
|
80
|
+
module: "pushary-hook",
|
|
81
|
+
summary: "Run as a PreToolUse hook (reads stdin, writes stdout)"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: "transcripts",
|
|
85
|
+
module: "pushary-transcripts",
|
|
86
|
+
summary: "Show or re-trust the keys this machine seals transcripts to",
|
|
87
|
+
detail: [
|
|
88
|
+
"Sync stops if the phone key or the compliance recovery key changes, because a",
|
|
89
|
+
"change is either your reinstall or a server handing itself a copy. Compare the",
|
|
90
|
+
"fingerprint with the app under Settings, then accept it with --rotate."
|
|
91
|
+
]
|
|
92
|
+
}
|
|
93
|
+
];
|
|
94
|
+
var COMMANDS = COMMAND_LIST;
|
|
95
|
+
var findCommand = (name) => COMMANDS.find((command) => command.name === name);
|
|
96
|
+
var isCommandName = (name) => COMMANDS.some((command) => command.name === name);
|
|
97
|
+
var COMMAND_OPTIONS = {
|
|
98
|
+
cowork: [
|
|
99
|
+
["--open", "Also open the setup guide in your browser"],
|
|
100
|
+
["--instructions", "Print standing instructions to paste into Cowork"],
|
|
101
|
+
["--skill", "Print the packaged Cowork SKILL.md for upload"],
|
|
102
|
+
["--json", "Print setup steps and capabilities as JSON; does not claim connection is verified"]
|
|
103
|
+
],
|
|
104
|
+
clean: [
|
|
105
|
+
["--dry-run", "List everything it would remove and change nothing"],
|
|
106
|
+
["--everything", "Also remove what the Mac app set up: quits it, then its keychain items, local state and editor extension"],
|
|
107
|
+
["--yes", "Remove everything without asking"]
|
|
108
|
+
],
|
|
109
|
+
transcripts: [
|
|
110
|
+
["status", "Show the pinned keys and their fingerprints (default)"],
|
|
111
|
+
["trust --rotate", "Accept the keys the server is offering now and resume syncing"]
|
|
112
|
+
],
|
|
113
|
+
mode: [
|
|
114
|
+
["<mode>", "One of: push_only, push_first, terminal_only, notify_only"],
|
|
115
|
+
["status", "Show the current override"],
|
|
116
|
+
["clear", "Drop the override and go back to per-tool policies"],
|
|
117
|
+
["--for <30m|2h>", "Expire the override after this long"]
|
|
118
|
+
],
|
|
119
|
+
wait: [
|
|
120
|
+
["<seconds>", `Default push window, 0-300`],
|
|
121
|
+
["status", "Show the whole ladder"],
|
|
122
|
+
["clear", "Reset the default window"]
|
|
123
|
+
],
|
|
124
|
+
status: [
|
|
125
|
+
["--json", "Emit the whole report as one object instead of the screen"],
|
|
126
|
+
["--devices", "Also list the machines registered to this site"]
|
|
127
|
+
],
|
|
128
|
+
logout: [
|
|
129
|
+
["--revoke", "Also kill the key server-side, so it stops working everywhere"],
|
|
130
|
+
["--json", "Emit the result as one object instead of the screen"]
|
|
131
|
+
],
|
|
132
|
+
doctor: [
|
|
133
|
+
["--json", "Emit every check as one object instead of the screen"],
|
|
134
|
+
["--roundtrip", "Send a synthetic question and report the returned answer; does not test hooks"],
|
|
135
|
+
["--activation claude_code --activation-target <path>", "Read correlated evidence for one unique scratch file; sends no test question"],
|
|
136
|
+
["--no-push", "Run every check without waking a device"],
|
|
137
|
+
["--bundle", "Write a redacted diagnostics file you can attach to a support thread"]
|
|
138
|
+
],
|
|
139
|
+
suggestions: [["accept <id>", "Turn a mined suggestion into an always-allow rule"]],
|
|
140
|
+
upgrade: [
|
|
141
|
+
["--auto=off", "Stop Pushary updating itself in the background"],
|
|
142
|
+
["--auto=on", "Update in the background again: once a day at session start, within the same major version"]
|
|
143
|
+
],
|
|
144
|
+
bell: [
|
|
145
|
+
["(no flags)", "Turn the bell on for Claude Code"],
|
|
146
|
+
["--test", "Ring once, now, so you can hear what it does"],
|
|
147
|
+
["--status", "Say whether the bell is on and how many agents are running"],
|
|
148
|
+
["--off", "Remove it. Touches nothing else"]
|
|
149
|
+
],
|
|
150
|
+
claude: [
|
|
151
|
+
["--remote", "Start headless and drive it entirely from your phone"],
|
|
152
|
+
["-p <prompt>", "Initial prompt"]
|
|
153
|
+
],
|
|
154
|
+
daemon: [
|
|
155
|
+
["install", "Install and start the native per-user service"],
|
|
156
|
+
["start | stop | restart", "Control the installed service"],
|
|
157
|
+
["status", "Show local daemon health and PID"],
|
|
158
|
+
["uninstall", "Stop and remove the native service"],
|
|
159
|
+
["foreground", "Run attached to this terminal for diagnostics"]
|
|
160
|
+
]
|
|
161
|
+
};
|
|
162
|
+
var SETUP_OPTIONS = [
|
|
163
|
+
["--agents cowork", "Show Claude Desktop/Cowork connector setup without installing CLI hooks"],
|
|
164
|
+
["--key <pk_xxx.xxx>", "Use this API key instead of prompting. Visible in the process list; prefer --key-stdin in CI."],
|
|
165
|
+
["--key-stdin", "Read the API key from the first line of stdin, so it never appears in argv or shell history"],
|
|
166
|
+
["--agents <list>", 'Comma-separated agent ids, or "auto" for everything detected, or "none" to save the key only'],
|
|
167
|
+
["--yes, -y", "Accept every default and ask nothing. Implies --agents auto unless --agents is given"],
|
|
168
|
+
["--connect app", "Pair the native Pushary app, no key to paste (default)"],
|
|
169
|
+
["--connect browser", "Approve in a browser tab instead. For a machine with no app"],
|
|
170
|
+
["--connect web", "Legacy browser subscribe page"],
|
|
171
|
+
["--connect none", "Skip the phone connect step, same as --skip-phone"],
|
|
172
|
+
["--control auto|on|off", "Manage phone-start automatically (default), require it, or disable its background service"],
|
|
173
|
+
["--transcripts on|off", "Enable or disable encrypted transcript sync on this machine"],
|
|
174
|
+
["--skip-phone", "Skip the phone connect step"],
|
|
175
|
+
["--take-over-hooks", "Replace hooks the Pushary Mac app owns with the CLI's. Without it the app's wiring is kept"],
|
|
176
|
+
["--verify roundtrip", "Send a real question and wait for you to answer it (default when a human is present)"],
|
|
177
|
+
["--verify push", "Only prove a notification was delivered. Automatic when nobody is at the terminal"],
|
|
178
|
+
["--verify none", "Skip the final check entirely"],
|
|
179
|
+
["--dry-run", "Print what would be written and change nothing"],
|
|
180
|
+
["--json", "Emit one machine-readable object instead of the human report"]
|
|
181
|
+
];
|
|
182
|
+
var AGENT_IDS = SETUP_AGENT_CHOICES.join(", ");
|
|
183
|
+
var USAGE_EXAMPLES = [
|
|
184
|
+
"npx @pushary/agent-hooks@latest setup",
|
|
185
|
+
"npx @pushary/agent-hooks@latest setup --agents auto --yes --skip-phone",
|
|
186
|
+
"npx @pushary/agent-hooks@latest setup --key-stdin --agents claude_code --yes < key.txt",
|
|
187
|
+
"npx @pushary/agent-hooks@latest doctor",
|
|
188
|
+
"npx @pushary/agent-hooks@latest mode push_only --for 30m",
|
|
189
|
+
"pushary claude # native terminal, reachable from your phone",
|
|
190
|
+
'pushary claude --remote -p "start the refactor" # start headless, drive from your phone',
|
|
191
|
+
"pushary daemon # keep this machine ready to spawn a session"
|
|
192
|
+
];
|
|
193
|
+
var pad = (text, width) => text + " ".repeat(Math.max(0, width - text.length));
|
|
194
|
+
var renderHelp = (version) => {
|
|
195
|
+
const width = Math.max(...COMMANDS.map((command) => command.name.length)) + 2;
|
|
196
|
+
const commandLines = COMMANDS.flatMap((command) => [
|
|
197
|
+
` ${pad(command.name, width)}${command.summary}`,
|
|
198
|
+
...(command.detail ?? []).map((line) => ` ${" ".repeat(width)}${line}`)
|
|
199
|
+
]);
|
|
200
|
+
const optionWidth = Math.max(...SETUP_OPTIONS.map(([flag]) => flag.length)) + 2;
|
|
201
|
+
const optionLines = SETUP_OPTIONS.map(([flag, text]) => ` ${pad(flag, optionWidth)}${text}`);
|
|
202
|
+
return [
|
|
203
|
+
"",
|
|
204
|
+
`Pushary Agent Hooks v${version}`,
|
|
205
|
+
"",
|
|
206
|
+
"Usage: pushary <command> [options]",
|
|
207
|
+
"",
|
|
208
|
+
"Commands:",
|
|
209
|
+
...commandLines,
|
|
210
|
+
"",
|
|
211
|
+
"Setup options:",
|
|
212
|
+
...optionLines,
|
|
213
|
+
"",
|
|
214
|
+
`Agent ids: ${AGENT_IDS}`,
|
|
215
|
+
"",
|
|
216
|
+
"Examples:",
|
|
217
|
+
...USAGE_EXAMPLES.map((line) => ` ${line}`),
|
|
218
|
+
"",
|
|
219
|
+
"Run `pushary <command> --help` for a single command.",
|
|
220
|
+
""
|
|
221
|
+
].join("\n");
|
|
222
|
+
};
|
|
223
|
+
var renderCommandHelp = (name, version) => {
|
|
224
|
+
const spec = findCommand(name);
|
|
225
|
+
const options = name === "setup" ? SETUP_OPTIONS : COMMAND_OPTIONS[name] ?? [];
|
|
226
|
+
const width = options.length > 0 ? Math.max(...options.map(([flag]) => flag.length)) + 2 : 0;
|
|
227
|
+
return [
|
|
228
|
+
"",
|
|
229
|
+
`pushary ${name} ${version ? `(v${version})` : ""}`.trimEnd(),
|
|
230
|
+
"",
|
|
231
|
+
...spec ? [` ${spec.summary}`, ...(spec.detail ?? []).map((line) => ` ${line}`), ""] : [],
|
|
232
|
+
...options.length > 0 ? ["Options:", ...options.map(([flag, text]) => ` ${pad(flag, width)}${text}`), ""] : [],
|
|
233
|
+
...name === "setup" ? [
|
|
234
|
+
`Agent ids: ${AGENT_IDS}`,
|
|
235
|
+
"",
|
|
236
|
+
"Non-interactive runs need --agents or --yes. Without a terminal on stdin,",
|
|
237
|
+
"setup takes the defaults rather than waiting for a keypress that can never arrive.",
|
|
238
|
+
""
|
|
239
|
+
] : []
|
|
240
|
+
].join("\n");
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
export {
|
|
244
|
+
NPX_LATEST_COMMAND,
|
|
245
|
+
COMMANDS,
|
|
246
|
+
isCommandName,
|
|
247
|
+
renderHelp,
|
|
248
|
+
renderCommandHelp
|
|
249
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PRETOOLUSE_GATED_TOOLS
|
|
3
|
+
} from "./chunk-NQTHFF4W.js";
|
|
4
|
+
|
|
5
|
+
// src/claude-tools.ts
|
|
6
|
+
var POSTTOOLUSE_MIRROR_ONLY_TOOLS = ["TodoWrite", "Task", "WebFetch", "WebSearch"];
|
|
7
|
+
var POSTTOOLUSE_MIRRORED_TOOLS = [...PRETOOLUSE_GATED_TOOLS, ...POSTTOOLUSE_MIRROR_ONLY_TOOLS];
|
|
8
|
+
|
|
9
|
+
// src/claude-notifications.ts
|
|
10
|
+
var IDLE_PROMPT_NOTIFICATION = "idle_prompt";
|
|
11
|
+
var NOTIFICATION_MATCHED_TYPES = [
|
|
12
|
+
// Blocks. The agent, or one of its workers, is waiting on a person.
|
|
13
|
+
"permission_prompt",
|
|
14
|
+
"idle_prompt",
|
|
15
|
+
"agent_needs_input",
|
|
16
|
+
"worker_permission_prompt",
|
|
17
|
+
// An MCP server asking the person for a value. The Elicitation hook answers it;
|
|
18
|
+
// this is the feed row saying one is open.
|
|
19
|
+
"elicitation_dialog",
|
|
20
|
+
"elicitation_url_dialog",
|
|
21
|
+
// Turn boundary.
|
|
22
|
+
"agent_completed",
|
|
23
|
+
// Quota. The island already reads plan usage, and these say when a limit hit,
|
|
24
|
+
// went stale, or was turned off, which no polling of the usage endpoint reveals.
|
|
25
|
+
"quota_auto_resume_fired",
|
|
26
|
+
"quota_auto_resume_stale",
|
|
27
|
+
"quota_auto_resume_disabled"
|
|
28
|
+
];
|
|
29
|
+
var NOTIFICATION_PUSH_TYPES = /* @__PURE__ */ new Set([
|
|
30
|
+
"idle_prompt",
|
|
31
|
+
"agent_needs_input",
|
|
32
|
+
"worker_permission_prompt"
|
|
33
|
+
]);
|
|
34
|
+
|
|
35
|
+
export {
|
|
36
|
+
POSTTOOLUSE_MIRROR_ONLY_TOOLS,
|
|
37
|
+
POSTTOOLUSE_MIRRORED_TOOLS,
|
|
38
|
+
IDLE_PROMPT_NOTIFICATION,
|
|
39
|
+
NOTIFICATION_MATCHED_TYPES,
|
|
40
|
+
NOTIFICATION_PUSH_TYPES
|
|
41
|
+
};
|