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,835 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CLI_PACKAGE_NAMES,
|
|
3
|
+
HOOK_BUDGETS,
|
|
4
|
+
HOOK_LOCATOR_BINARY,
|
|
5
|
+
OWNERSHIP_PROBE_COMMAND,
|
|
6
|
+
classifyGlobalFolder,
|
|
7
|
+
classifyGlobalInstall,
|
|
8
|
+
cmdShimTarget,
|
|
9
|
+
decideCommandOwner,
|
|
10
|
+
globalInstallTarget,
|
|
11
|
+
globalPackageFolder,
|
|
12
|
+
globalRootFor,
|
|
13
|
+
isReleaseVersion,
|
|
14
|
+
ownPackageName,
|
|
15
|
+
withHookHoldBudget
|
|
16
|
+
} from "./chunk-NQTHFF4W.js";
|
|
17
|
+
|
|
18
|
+
// src/npm.ts
|
|
19
|
+
import { execFileSync, execSync } from "child_process";
|
|
20
|
+
import { existsSync, readFileSync, readlinkSync, realpathSync } from "fs";
|
|
21
|
+
import { dirname, isAbsolute, join, relative, resolve } from "path";
|
|
22
|
+
var latestReleaseUrl = (packageName) => `https://registry.npmjs.org/${packageName}/latest`;
|
|
23
|
+
var REGISTRY_TIMEOUT_MS = 5e3;
|
|
24
|
+
var LATEST_TAG = "latest";
|
|
25
|
+
var cleanNpmEnv = () => {
|
|
26
|
+
const env = {};
|
|
27
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
28
|
+
if (key.toLowerCase().startsWith("npm_config_workspace")) continue;
|
|
29
|
+
env[key] = value;
|
|
30
|
+
}
|
|
31
|
+
return env;
|
|
32
|
+
};
|
|
33
|
+
var NPM_ERROR_METADATA = /^(code|syscall|path|dest|errno)\s/i;
|
|
34
|
+
var npmErrorMessage = (err) => {
|
|
35
|
+
const e = err;
|
|
36
|
+
const text = [e?.stderr, e?.stdout].map((part) => part ? part.toString() : "").join("\n");
|
|
37
|
+
const line = text.split("\n").map((l) => l.replace(/^npm error\s*/i, "").trim()).find((l) => l && !l.startsWith("A complete log") && !NPM_ERROR_METADATA.test(l));
|
|
38
|
+
return line || e?.message || String(err);
|
|
39
|
+
};
|
|
40
|
+
var execNpm = (args, options = {}) => {
|
|
41
|
+
return execSync(`npm ${args}`, {
|
|
42
|
+
timeout: 12e4,
|
|
43
|
+
stdio: "pipe",
|
|
44
|
+
...options,
|
|
45
|
+
env: { ...cleanNpmEnv(), ...options.env ?? {} }
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
var GLOBAL_PREFIX_TIMEOUT_MS = 5e3;
|
|
49
|
+
var readGlobalPrefix = () => {
|
|
50
|
+
try {
|
|
51
|
+
const prefix = execNpm("prefix -g --no-workspaces", { timeout: GLOBAL_PREFIX_TIMEOUT_MS }).toString().trim();
|
|
52
|
+
return prefix === "" ? void 0 : prefix;
|
|
53
|
+
} catch {
|
|
54
|
+
return void 0;
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
var resolveGlobalBinary = (name) => {
|
|
58
|
+
const prefix = readGlobalPrefix();
|
|
59
|
+
if (prefix === void 0) return void 0;
|
|
60
|
+
const candidates = process.platform === "win32" ? [join(prefix, `${name}.cmd`), join(prefix, name)] : [join(prefix, "bin", name)];
|
|
61
|
+
return candidates.find(existsSync);
|
|
62
|
+
};
|
|
63
|
+
var resolvePathBinary = (name, platform = process.platform) => {
|
|
64
|
+
try {
|
|
65
|
+
return execFileSync(platform === "win32" ? "where" : "which", [name], {
|
|
66
|
+
encoding: "utf8",
|
|
67
|
+
stdio: "pipe",
|
|
68
|
+
timeout: 5e3
|
|
69
|
+
}).split(/\r?\n/, 1)[0]?.trim() || void 0;
|
|
70
|
+
} catch {
|
|
71
|
+
return void 0;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
var NPM_MISSING_MESSAGE = "npm is not on PATH, and the hooks run from a global npm install.";
|
|
75
|
+
var NPM_LOOKUP = process.platform === "win32" ? "where npm" : "command -v npm";
|
|
76
|
+
var npmIsOnPath = () => {
|
|
77
|
+
try {
|
|
78
|
+
execSync(NPM_LOOKUP, { env: cleanNpmEnv(), stdio: "ignore", timeout: 5e3 });
|
|
79
|
+
return true;
|
|
80
|
+
} catch {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
var GLOBAL_LIST_TIMEOUT_MS = 3e4;
|
|
85
|
+
var globalPackageState = (name) => {
|
|
86
|
+
try {
|
|
87
|
+
const listing = JSON.parse(execNpm("list -g --depth=0 --json --no-workspaces", { timeout: GLOBAL_LIST_TIMEOUT_MS }).toString());
|
|
88
|
+
const dependencies = typeof listing === "object" && listing !== null && "dependencies" in listing ? listing.dependencies : void 0;
|
|
89
|
+
return typeof dependencies === "object" && dependencies !== null && name in dependencies ? "installed" : "absent";
|
|
90
|
+
} catch {
|
|
91
|
+
return "unknown";
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
var NpmMissingError = class extends Error {
|
|
95
|
+
constructor() {
|
|
96
|
+
super(NPM_MISSING_MESSAGE);
|
|
97
|
+
this.name = "NpmMissingError";
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
var resolveBinary = (name) => resolveGlobalBinary(name) ?? resolvePathBinary(name);
|
|
101
|
+
var resolvePackageVersionFromBinary = (binary, packageName) => {
|
|
102
|
+
try {
|
|
103
|
+
let dir = dirname(realpathSync(binary));
|
|
104
|
+
const packagePath = packageName.split("/");
|
|
105
|
+
for (let depth = 0; depth < 8; depth++) {
|
|
106
|
+
for (const manifest of [
|
|
107
|
+
join(dir, "package.json"),
|
|
108
|
+
join(dir, "node_modules", ...packagePath, "package.json")
|
|
109
|
+
]) {
|
|
110
|
+
try {
|
|
111
|
+
const value = JSON.parse(readFileSync(manifest, "utf8"));
|
|
112
|
+
if (value.name === packageName && typeof value.version === "string") return value.version;
|
|
113
|
+
} catch {
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
const parent = dirname(dir);
|
|
117
|
+
if (parent === dir) break;
|
|
118
|
+
dir = parent;
|
|
119
|
+
}
|
|
120
|
+
} catch {
|
|
121
|
+
}
|
|
122
|
+
return void 0;
|
|
123
|
+
};
|
|
124
|
+
var resolveGlobalPackageVersion = (name) => {
|
|
125
|
+
try {
|
|
126
|
+
const result = JSON.parse(execNpm("list -g --depth=0 --json --no-workspaces", { timeout: 5e3 }).toString());
|
|
127
|
+
const value = result.dependencies?.[name]?.version;
|
|
128
|
+
return typeof value === "string" ? value : void 0;
|
|
129
|
+
} catch {
|
|
130
|
+
return void 0;
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
var localImportsPresent = (entry, packageDir, seen = /* @__PURE__ */ new Set()) => {
|
|
134
|
+
if (seen.has(entry)) return true;
|
|
135
|
+
seen.add(entry);
|
|
136
|
+
try {
|
|
137
|
+
const source = readFileSync(entry, "utf8");
|
|
138
|
+
for (const match of source.matchAll(/\b(?:import|export)\s+(?:[^"'()]*?\s+from\s+)?["'](\.{1,2}\/[^"']+)["']|\bimport\s*\(\s*["'](\.{1,2}\/[^"']+)["']/g)) {
|
|
139
|
+
const dependency = resolve(dirname(entry), match[1] ?? match[2]);
|
|
140
|
+
const localPath = relative(packageDir, dependency);
|
|
141
|
+
if (localPath.startsWith("..") || isAbsolute(localPath) || !existsSync(dependency)) return false;
|
|
142
|
+
if (/\.[cm]?js$/.test(dependency) && !localImportsPresent(dependency, packageDir, seen)) return false;
|
|
143
|
+
}
|
|
144
|
+
return true;
|
|
145
|
+
} catch {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
var packageBinsPresent = (packageDir, prefix, platform = process.platform, requiredFiles = []) => {
|
|
150
|
+
try {
|
|
151
|
+
const manifest = JSON.parse(readFileSync(join(packageDir, "package.json"), "utf8"));
|
|
152
|
+
if (!manifest.bin || typeof manifest.bin !== "object" || Array.isArray(manifest.bin)) return false;
|
|
153
|
+
const bins = Object.entries(manifest.bin);
|
|
154
|
+
if (bins.length === 0) return false;
|
|
155
|
+
if (!requiredFiles.every((path) => existsSync(join(packageDir, path)))) return false;
|
|
156
|
+
const seen = /* @__PURE__ */ new Set();
|
|
157
|
+
return bins.every(([name, target]) => {
|
|
158
|
+
if (typeof target !== "string") return false;
|
|
159
|
+
const shims = platform === "win32" ? [join(prefix, `${name}.cmd`), join(prefix, name)] : [join(prefix, "bin", name)];
|
|
160
|
+
const entry = join(packageDir, target);
|
|
161
|
+
return existsSync(entry) && shims.some(existsSync) && localImportsPresent(entry, packageDir, seen);
|
|
162
|
+
});
|
|
163
|
+
} catch {
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
var AGENT_HOOKS_RUNTIME_FILES = [
|
|
168
|
+
"data/SKILL.md",
|
|
169
|
+
"data/cursor-plugin/.cursor-plugin/plugin.json",
|
|
170
|
+
"data/cursor-plugin/hooks/hooks.json",
|
|
171
|
+
"data/cursor-plugin/mcp.json",
|
|
172
|
+
"data/cursor-plugin/scripts/pushary-gate.mjs",
|
|
173
|
+
"data/cursor-plugin/scripts/redaction.mjs",
|
|
174
|
+
"data/vscode-plugin/.claude-plugin/plugin.json",
|
|
175
|
+
"data/vscode-plugin/hooks/hooks.json",
|
|
176
|
+
"data/vscode-plugin/.mcp.json",
|
|
177
|
+
"data/vscode-plugin/scripts/pushary-gate.mjs",
|
|
178
|
+
"data/vscode-plugin/scripts/redaction.mjs"
|
|
179
|
+
];
|
|
180
|
+
var globalPackageBinsPresent = (name, prefix, platform) => packageBinsPresent(globalPackageFolder(globalRootFor(prefix, platform), name, platform), prefix, platform, AGENT_HOOKS_RUNTIME_FILES);
|
|
181
|
+
var errorCode = (error) => typeof error === "object" && error !== null && "code" in error ? error.code : void 0;
|
|
182
|
+
var realpathOrUndefined = (path) => {
|
|
183
|
+
try {
|
|
184
|
+
return realpathSync(path);
|
|
185
|
+
} catch {
|
|
186
|
+
return void 0;
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
var definedPaths = (paths) => paths.filter((path) => path !== void 0);
|
|
190
|
+
var readPosixCommandLink = (prefix) => {
|
|
191
|
+
const command = join(prefix, "bin", OWNERSHIP_PROBE_COMMAND);
|
|
192
|
+
try {
|
|
193
|
+
const target = resolve(dirname(command), readlinkSync(command));
|
|
194
|
+
return { kind: "resolved", targets: definedPaths([target, realpathOrUndefined(command)]) };
|
|
195
|
+
} catch (error) {
|
|
196
|
+
if (errorCode(error) === "ENOENT") return { kind: "absent" };
|
|
197
|
+
const real = realpathOrUndefined(command);
|
|
198
|
+
return real === void 0 ? { kind: "unreadable" } : { kind: "resolved", targets: [real] };
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
var readWindowsCommandLink = (prefix) => {
|
|
202
|
+
const shim = join(prefix, `${OWNERSHIP_PROBE_COMMAND}.cmd`);
|
|
203
|
+
try {
|
|
204
|
+
const target = cmdShimTarget(prefix, readFileSync(shim, "utf8"));
|
|
205
|
+
return target === void 0 ? { kind: "unreadable" } : { kind: "resolved", targets: [target] };
|
|
206
|
+
} catch (error) {
|
|
207
|
+
return errorCode(error) === "ENOENT" ? { kind: "absent" } : { kind: "unreadable" };
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
var readCommandLink = (prefix, platform) => platform === "win32" ? readWindowsCommandLink(prefix) : readPosixCommandLink(prefix);
|
|
211
|
+
var readFolderManifest = (folder) => {
|
|
212
|
+
try {
|
|
213
|
+
return { kind: "read", manifest: JSON.parse(readFileSync(join(folder, "package.json"), "utf8")) };
|
|
214
|
+
} catch {
|
|
215
|
+
return { kind: "unreadable" };
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
var readGlobalFolder = (root, name, platform = process.platform) => classifyGlobalFolder(name, readFolderManifest(globalPackageFolder(root, name, platform)));
|
|
219
|
+
var installedIfOurs = (folder) => folder.kind === "ours" ? [folder.installed] : [];
|
|
220
|
+
var readInstalledCliPackages = (root, platform = process.platform) => CLI_PACKAGE_NAMES.flatMap((name) => installedIfOurs(readGlobalFolder(root, name, platform)));
|
|
221
|
+
var globalRootsFor = (prefix, platform) => {
|
|
222
|
+
const root = globalRootFor(prefix, platform);
|
|
223
|
+
return definedPaths([root, realpathOrUndefined(root)]);
|
|
224
|
+
};
|
|
225
|
+
var readCommandOwner = (prefix, platform = process.platform) => decideCommandOwner({
|
|
226
|
+
platform,
|
|
227
|
+
globalRoots: globalRootsFor(prefix, platform),
|
|
228
|
+
link: readCommandLink(prefix, platform),
|
|
229
|
+
installed: readInstalledCliPackages(globalRootFor(prefix, platform), platform).map((installed) => installed.name)
|
|
230
|
+
});
|
|
231
|
+
var NPM_UNAVAILABLE_OWNER = { kind: "unknown", reason: "npm_unavailable" };
|
|
232
|
+
var readGlobalCliInstallAt = (prefix, platform = process.platform) => classifyGlobalInstall(readInstalledCliPackages(globalRootFor(prefix, platform), platform), readCommandOwner(prefix, platform));
|
|
233
|
+
var readGlobalCliInstall = (platform = process.platform) => {
|
|
234
|
+
const prefix = readGlobalPrefix();
|
|
235
|
+
return prefix === void 0 ? { kind: "unavailable" } : readGlobalCliInstallAt(prefix, platform);
|
|
236
|
+
};
|
|
237
|
+
var resolveGlobalBinDir = (probe) => {
|
|
238
|
+
if (process.platform === "win32") return void 0;
|
|
239
|
+
const resolved = resolveGlobalBinary(probe);
|
|
240
|
+
return resolved ? resolved.slice(0, resolved.length - probe.length - 1) : void 0;
|
|
241
|
+
};
|
|
242
|
+
var quoteIfNeeded = (path) => /\s/.test(path) ? `"${path}"` : path;
|
|
243
|
+
var fetchLatestPublishedVersion = async (request) => {
|
|
244
|
+
try {
|
|
245
|
+
const response = await (request.fetchImpl ?? fetch)(latestReleaseUrl(request.packageName), {
|
|
246
|
+
signal: AbortSignal.timeout(request.timeoutMs ?? REGISTRY_TIMEOUT_MS)
|
|
247
|
+
});
|
|
248
|
+
if (!response.ok) return void 0;
|
|
249
|
+
const body = await response.json();
|
|
250
|
+
const version = body && typeof body === "object" && "version" in body ? body.version : void 0;
|
|
251
|
+
return typeof version === "string" && isReleaseVersion(version) ? version : void 0;
|
|
252
|
+
} catch {
|
|
253
|
+
return void 0;
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
var POSIX_SHELL_WORD = /^[\w@%+=:,./-]+$/;
|
|
257
|
+
var quoteForPosixShell = (value) => POSIX_SHELL_WORD.test(value) ? value : `'${value.replaceAll("'", `'\\''`)}'`;
|
|
258
|
+
var installPackageGlobally = (request, runNpm = execNpm) => {
|
|
259
|
+
const prefixFlag = request.prefix === void 0 ? "" : ` --prefix ${quoteForPosixShell(request.prefix)}`;
|
|
260
|
+
const spec = quoteForPosixShell(`${request.packageName}@${request.version}`);
|
|
261
|
+
runNpm(`install -g --no-workspaces${prefixFlag} ${spec}`, { timeout: request.timeoutMs });
|
|
262
|
+
};
|
|
263
|
+
var needsGlobalInstall = (requested, installed, binaryPresent) => requested !== installed || !binaryPresent;
|
|
264
|
+
var planGlobalInstall = (platform) => {
|
|
265
|
+
const prefix = readGlobalPrefix();
|
|
266
|
+
const owner = prefix === void 0 ? NPM_UNAVAILABLE_OWNER : readCommandOwner(prefix, platform);
|
|
267
|
+
return { prefix, packageName: globalInstallTarget(owner, ownPackageName()) };
|
|
268
|
+
};
|
|
269
|
+
var plannedGlobalPackage = () => planGlobalInstall(process.platform).packageName;
|
|
270
|
+
var installedVersionOf = (prefix, name, platform) => {
|
|
271
|
+
const folder = readGlobalFolder(globalRootFor(prefix, platform), name, platform);
|
|
272
|
+
return folder.kind === "ours" ? folder.installed.version : void 0;
|
|
273
|
+
};
|
|
274
|
+
var asCachedResult = (result) => result === "installed" ? "cached" : result;
|
|
275
|
+
var installState;
|
|
276
|
+
var installGlobally = (options) => {
|
|
277
|
+
if (installState) {
|
|
278
|
+
return installState.then(asCachedResult);
|
|
279
|
+
}
|
|
280
|
+
const started = (async () => {
|
|
281
|
+
if (!npmIsOnPath()) throw new NpmMissingError();
|
|
282
|
+
const platform = process.platform;
|
|
283
|
+
const { prefix, packageName } = planGlobalInstall(platform);
|
|
284
|
+
const pinned = process.env.PUSHARY_PIN_GLOBAL_VERSION !== "0";
|
|
285
|
+
if (pinned && prefix !== void 0 && !needsGlobalInstall(
|
|
286
|
+
options.version,
|
|
287
|
+
installedVersionOf(prefix, packageName, platform),
|
|
288
|
+
globalPackageBinsPresent(packageName, prefix, platform)
|
|
289
|
+
)) {
|
|
290
|
+
return "skipped";
|
|
291
|
+
}
|
|
292
|
+
const version = pinned && options.version && options.version !== "0.0.0" ? options.version : LATEST_TAG;
|
|
293
|
+
installPackageGlobally({ packageName, version, timeoutMs: options.timeoutMs ?? 12e4 });
|
|
294
|
+
return "installed";
|
|
295
|
+
})();
|
|
296
|
+
installState = started;
|
|
297
|
+
started.catch(() => {
|
|
298
|
+
if (installState === started) installState = void 0;
|
|
299
|
+
});
|
|
300
|
+
return started;
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
// src/hook-command.ts
|
|
304
|
+
var HOOK_SHELL = "/bin/sh -c";
|
|
305
|
+
var holdBudgetOf = (hook) => hook.holds ? hook.timeout : void 0;
|
|
306
|
+
var LOCATE = 'B=$(command -v "$0") || exit 0; [ -f "$B" ] || exit 0; [ -x "$B" ] || exit 0';
|
|
307
|
+
var NODE_PRESENT = "command -v node >/dev/null 2>&1 || exit 0";
|
|
308
|
+
var CURSOR_NO_OPINION = { permission: "deny", user_message: "Command denied via Pushary.", agent_message: "Pushary hook is unavailable. Repair the integration before retrying." };
|
|
309
|
+
var shellDoubleQuoted = (text) => `"${text.replace(/(["\\$`])/g, "\\$1")}"`;
|
|
310
|
+
var CURSOR_NATIVE_PROMPT = { permission: "ask" };
|
|
311
|
+
var CURSOR_NATIVE_PROMPT_EVENTS = ["beforeShellExecution", "beforeMCPExecution"];
|
|
312
|
+
var hookEventIsOneOf = (events) => events.flatMap((event) => [`*\\"hook_event_name\\":\\"${event}\\"*`, `*\\"hook_event_name\\":\\ \\"${event}\\"*`]).join("|");
|
|
313
|
+
var CURSOR_MISS = `{ E=$(cat); case "$E" in ${hookEventIsOneOf(CURSOR_NATIVE_PROMPT_EVENTS)}) printf %s ${shellDoubleQuoted(JSON.stringify(CURSOR_NATIVE_PROMPT))} ;; *) printf %s ${shellDoubleQuoted(JSON.stringify(CURSOR_NO_OPINION))} ;; esac; exit 0; }`;
|
|
314
|
+
var HOOK_GUARD_SCRIPTS = {
|
|
315
|
+
native_binary: `${LOCATE}; exec "$B" "$@"`,
|
|
316
|
+
node_binary: `${LOCATE}; ${NODE_PRESENT}; exec "$B" "$@"`,
|
|
317
|
+
node_script: `[ -f "$0" ] || exit 0; ${NODE_PRESENT}; exec node "$0" "$@"`,
|
|
318
|
+
cursor_gate_script: `[ -f "$0" ] || ${CURSOR_MISS}; command -v node >/dev/null 2>&1 || ${CURSOR_MISS}; exec node "$0" "$@"`
|
|
319
|
+
};
|
|
320
|
+
var BINARY_GUARDS = {
|
|
321
|
+
native: "native_binary",
|
|
322
|
+
node: "node_binary"
|
|
323
|
+
};
|
|
324
|
+
var guardKindOf = (target) => {
|
|
325
|
+
switch (target.kind) {
|
|
326
|
+
case "node":
|
|
327
|
+
return "node_script";
|
|
328
|
+
case "cursor_gate":
|
|
329
|
+
return "cursor_gate_script";
|
|
330
|
+
case "binary":
|
|
331
|
+
return BINARY_GUARDS[target.runtime];
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
var targetPath = (target) => target.kind === "binary" ? target.path : target.script;
|
|
335
|
+
var runsThroughNode = (target) => target.kind !== "binary";
|
|
336
|
+
var guardHookCommand = (target, platform = process.platform) => {
|
|
337
|
+
const path = quoteIfNeeded(targetPath(target));
|
|
338
|
+
if (platform === "win32") return runsThroughNode(target) ? `node ${path}` : path;
|
|
339
|
+
return `${HOOK_SHELL} '${HOOK_GUARD_SCRIPTS[guardKindOf(target)]}' ${path}`;
|
|
340
|
+
};
|
|
341
|
+
var guardBinary = (path, platform) => guardHookCommand({ kind: "binary", path, runtime: "node" }, platform);
|
|
342
|
+
var guardCursorGate = (script, platform) => guardHookCommand({ kind: "cursor_gate", script }, platform);
|
|
343
|
+
var guardedArgv = (target, args = [], platform = process.platform) => {
|
|
344
|
+
const path = targetPath(target);
|
|
345
|
+
if (platform === "win32") {
|
|
346
|
+
return runsThroughNode(target) ? ["node", path, ...args] : [path, ...args];
|
|
347
|
+
}
|
|
348
|
+
const [shell, ...shellArgs] = HOOK_SHELL.split(/\s+/);
|
|
349
|
+
return [shell, ...shellArgs, HOOK_GUARD_SCRIPTS[guardKindOf(target)], path, ...args];
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
// src/codex-config.ts
|
|
353
|
+
import { createHash } from "crypto";
|
|
354
|
+
import { homedir } from "os";
|
|
355
|
+
import { join as join2 } from "path";
|
|
356
|
+
|
|
357
|
+
// src/registration-holder.ts
|
|
358
|
+
var asRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
359
|
+
var registeredCommands = (document) => {
|
|
360
|
+
const hooks = asRecord(document?.hooks);
|
|
361
|
+
if (!hooks) return [];
|
|
362
|
+
const found = [];
|
|
363
|
+
for (const [event, value] of Object.entries(hooks)) {
|
|
364
|
+
if (!Array.isArray(value)) continue;
|
|
365
|
+
for (const item of value) {
|
|
366
|
+
const inner = asRecord(item)?.hooks;
|
|
367
|
+
if (!Array.isArray(inner)) continue;
|
|
368
|
+
for (const hook of inner) {
|
|
369
|
+
const command = asRecord(hook)?.command;
|
|
370
|
+
if (typeof command === "string") found.push({ event, command });
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
return found;
|
|
375
|
+
};
|
|
376
|
+
var wholeSegments = (command) => command.split(/[\s/\\"']+/).map((segment) => segment.replace(/\.(cmd|exe)$/i, ""));
|
|
377
|
+
var namesLocator = (command) => wholeSegments(command).includes(HOOK_LOCATOR_BINARY);
|
|
378
|
+
var namesCliBinary = (command, cliBinaries) => wholeSegments(command).some((segment) => cliBinaries.has(segment));
|
|
379
|
+
var namesOurBinary = (command, cliBinaries) => namesLocator(command) || namesCliBinary(command, cliBinaries);
|
|
380
|
+
var registrationHolderOf = (document, cliBinaries) => {
|
|
381
|
+
const commands = registeredCommands(document);
|
|
382
|
+
if (commands.some(({ command }) => namesLocator(command))) return "app";
|
|
383
|
+
return commands.some(({ command }) => namesCliBinary(command, cliBinaries)) ? "cli" : "none";
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
// src/hook-entries.ts
|
|
387
|
+
var registerHookEntry = (existing, isOurs, entry) => {
|
|
388
|
+
const rewritten = [];
|
|
389
|
+
let placed = false;
|
|
390
|
+
for (const item of existing) {
|
|
391
|
+
if (!isOurs(item)) {
|
|
392
|
+
rewritten.push(item);
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
if (placed) continue;
|
|
396
|
+
rewritten.push(entry);
|
|
397
|
+
placed = true;
|
|
398
|
+
}
|
|
399
|
+
if (!placed) rewritten.push(entry);
|
|
400
|
+
return rewritten;
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
// src/removal-scope.ts
|
|
404
|
+
var namesAppLocator = (command) => command.includes(HOOK_LOCATOR_BINARY);
|
|
405
|
+
var isProtectedByScope = (command, scope) => scope?.keepAppWiring === true && namesAppLocator(command);
|
|
406
|
+
|
|
407
|
+
// src/canonical-json.ts
|
|
408
|
+
var canonicalJson = (value) => {
|
|
409
|
+
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
410
|
+
if (Array.isArray(value)) return "[" + value.map(canonicalJson).join(",") + "]";
|
|
411
|
+
const record = value;
|
|
412
|
+
return "{" + Object.keys(record).sort().map((key) => JSON.stringify(key) + ":" + canonicalJson(record[key])).join(",") + "}";
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
// src/codex-config.ts
|
|
416
|
+
var CODEX_HOOK_BINARY = "pushary-codex-hook";
|
|
417
|
+
var codexHome = () => codexHomeFrom(homedir());
|
|
418
|
+
var codexHomeFrom = (home) => process.env.CODEX_HOME?.trim() || join2(home, ".codex");
|
|
419
|
+
var codexConfigToml = () => join2(codexHome(), "config.toml");
|
|
420
|
+
var codexHooksJsonIn = (home) => join2(home, "hooks.json");
|
|
421
|
+
var codexHooksJson = () => codexHooksJsonIn(codexHome());
|
|
422
|
+
var codexAgentsMd = () => join2(codexHome(), "AGENTS.md");
|
|
423
|
+
var codexSkillDir = () => join2(codexHome(), "skills", "pushary");
|
|
424
|
+
var CODEX_HOOK_EVENTS = [
|
|
425
|
+
{ event: "PermissionRequest", delivery: "approval", timeout: HOOK_BUDGETS.codex.budgetSeconds, statusMessage: "Waiting for your phone" },
|
|
426
|
+
{ event: "PreToolUse", matcher: "Bash|apply_patch|mcp__.*", delivery: "approval", timeout: HOOK_BUDGETS.codex.budgetSeconds, statusMessage: "Checking Pushary policy" },
|
|
427
|
+
{ event: "PostToolUse", matcher: "Bash|apply_patch|mcp__.*", delivery: "telemetry", timeout: 10 },
|
|
428
|
+
{ event: "UserPromptSubmit", delivery: "telemetry", timeout: 10 },
|
|
429
|
+
{ event: "Stop", delivery: "rescue", timeout: HOOK_BUDGETS.codex.budgetSeconds, holds: true },
|
|
430
|
+
// `clear` alongside startup and resume, matching what we already do on Claude
|
|
431
|
+
// Code. Codex names all three in its own SessionStart sources, and without it a
|
|
432
|
+
// session the person started by clearing is one we never see begin.
|
|
433
|
+
{ event: "SessionStart", matcher: "startup|resume|clear", delivery: "telemetry", timeout: 10 },
|
|
434
|
+
// Codex caps SessionEnd at 3s and defaults it to 1s, unlike every other event
|
|
435
|
+
// here. It also runs synchronously whatever `async` says, so the handler must
|
|
436
|
+
// fit the window rather than assume the 10s Claude Code allows.
|
|
437
|
+
{ event: "SessionEnd", delivery: "telemetry", timeout: 3 },
|
|
438
|
+
// Observability only, and the four Codex names we were not registering at all.
|
|
439
|
+
// Every one is in codex-cli 0.151.0's own `HookEventName` enum
|
|
440
|
+
// (subagent_start, subagent_stop, pre_compact, post_compact), and we already
|
|
441
|
+
// carry all four on Claude Code, so the island showed subagent and compaction
|
|
442
|
+
// shape for one agent and not the other. None of them decides anything.
|
|
443
|
+
{ event: "SubagentStart", delivery: "telemetry", timeout: 5 },
|
|
444
|
+
{ event: "SubagentStop", delivery: "telemetry", timeout: 5 },
|
|
445
|
+
{ event: "PreCompact", matcher: "manual|auto", delivery: "telemetry", timeout: 5 },
|
|
446
|
+
{ event: "PostCompact", matcher: "manual|auto", delivery: "telemetry", timeout: 5 }
|
|
447
|
+
];
|
|
448
|
+
var CODEX_EVENT_KEY = {
|
|
449
|
+
PermissionRequest: "permission_request",
|
|
450
|
+
PreToolUse: "pre_tool_use",
|
|
451
|
+
PostToolUse: "post_tool_use",
|
|
452
|
+
UserPromptSubmit: "user_prompt_submit",
|
|
453
|
+
Stop: "stop",
|
|
454
|
+
SessionStart: "session_start",
|
|
455
|
+
SessionEnd: "session_end",
|
|
456
|
+
SubagentStart: "subagent_start",
|
|
457
|
+
SubagentStop: "subagent_stop",
|
|
458
|
+
PreCompact: "pre_compact",
|
|
459
|
+
PostCompact: "post_compact"
|
|
460
|
+
};
|
|
461
|
+
var codexEventKeyOf = (event) => event.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase();
|
|
462
|
+
var asRecord2 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
463
|
+
var ensureRecord = (target, key) => {
|
|
464
|
+
const existing = asRecord2(target[key]);
|
|
465
|
+
if (existing) return existing;
|
|
466
|
+
const created = {};
|
|
467
|
+
target[key] = created;
|
|
468
|
+
return created;
|
|
469
|
+
};
|
|
470
|
+
var CODEX_MCP_URL = "https://pushary.com/api/mcp/mcp";
|
|
471
|
+
var addCodexMcpServer = (config, apiKey) => {
|
|
472
|
+
const servers = ensureRecord(config, "mcp_servers");
|
|
473
|
+
const pushary = ensureRecord(servers, "pushary");
|
|
474
|
+
pushary.url = CODEX_MCP_URL;
|
|
475
|
+
ensureRecord(pushary, "http_headers").Authorization = `Bearer ${apiKey}`;
|
|
476
|
+
pushary.default_tools_approval_mode = "approve";
|
|
477
|
+
delete pushary.bearer_token_env_var;
|
|
478
|
+
delete pushary.tools;
|
|
479
|
+
};
|
|
480
|
+
var readCodexMcpAuth = (config) => {
|
|
481
|
+
const pushary = asRecord2(asRecord2(config.mcp_servers)?.pushary);
|
|
482
|
+
if (!pushary) return void 0;
|
|
483
|
+
const authorization = asRecord2(pushary.http_headers)?.Authorization;
|
|
484
|
+
if (typeof authorization === "string" && authorization.startsWith("Bearer ")) {
|
|
485
|
+
return { method: "embedded", key: authorization.slice("Bearer ".length).trim() };
|
|
486
|
+
}
|
|
487
|
+
if (typeof pushary.bearer_token_env_var === "string") {
|
|
488
|
+
return { method: "env", envVar: pushary.bearer_token_env_var };
|
|
489
|
+
}
|
|
490
|
+
if (typeof pushary.bearer_token === "string") return { method: "legacy-bearer" };
|
|
491
|
+
return void 0;
|
|
492
|
+
};
|
|
493
|
+
var isPusharyCodexCommand = (command) => command.includes(CODEX_HOOK_BINARY) || command.includes(HOOK_LOCATOR_BINARY);
|
|
494
|
+
var isPusharyCommand = (hook, scope) => {
|
|
495
|
+
const command = String(asRecord2(hook)?.command ?? "");
|
|
496
|
+
if (isProtectedByScope(command, scope)) return false;
|
|
497
|
+
return isPusharyCodexCommand(command);
|
|
498
|
+
};
|
|
499
|
+
var isPusharyCodexHook = (entry, scope) => {
|
|
500
|
+
const hooks = asRecord2(entry)?.hooks;
|
|
501
|
+
return Array.isArray(hooks) && hooks.some((hook) => isPusharyCommand(hook, scope));
|
|
502
|
+
};
|
|
503
|
+
var CODEX_CLI_BINARIES = /* @__PURE__ */ new Set([CODEX_HOOK_BINARY]);
|
|
504
|
+
var codexRegistrationHolder = (hooksConfig) => registrationHolderOf(hooksConfig, CODEX_CLI_BINARIES);
|
|
505
|
+
var addCodexHooks = (config, command) => {
|
|
506
|
+
const hooks = ensureRecord(config, "hooks");
|
|
507
|
+
for (const definition of CODEX_HOOK_EVENTS) {
|
|
508
|
+
const existing = Array.isArray(hooks[definition.event]) ? hooks[definition.event] : [];
|
|
509
|
+
hooks[definition.event] = registerHookEntry(existing, isPusharyCodexHook, {
|
|
510
|
+
...definition.matcher ? { matcher: definition.matcher } : {},
|
|
511
|
+
hooks: [{
|
|
512
|
+
type: "command",
|
|
513
|
+
command: withHookHoldBudget(command, holdBudgetOf(definition)),
|
|
514
|
+
timeout: definition.timeout,
|
|
515
|
+
...definition.statusMessage ? { statusMessage: definition.statusMessage } : {}
|
|
516
|
+
}]
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
var removeCodexHooks = (config, scope) => {
|
|
521
|
+
const hooks = asRecord2(config.hooks);
|
|
522
|
+
if (!hooks) return false;
|
|
523
|
+
let changed = false;
|
|
524
|
+
for (const definition of CODEX_HOOK_EVENTS) {
|
|
525
|
+
const entries = hooks[definition.event];
|
|
526
|
+
if (!Array.isArray(entries)) continue;
|
|
527
|
+
const filtered = entries.filter((entry) => !isPusharyCodexHook(entry, scope));
|
|
528
|
+
if (filtered.length !== entries.length) {
|
|
529
|
+
if (filtered.length === 0) {
|
|
530
|
+
delete hooks[definition.event];
|
|
531
|
+
} else {
|
|
532
|
+
hooks[definition.event] = filtered;
|
|
533
|
+
}
|
|
534
|
+
changed = true;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
if (Object.keys(hooks).length === 0) delete config.hooks;
|
|
538
|
+
return changed;
|
|
539
|
+
};
|
|
540
|
+
var hasCodexHooks = (config) => {
|
|
541
|
+
const hooks = asRecord2(config.hooks);
|
|
542
|
+
if (!hooks) return false;
|
|
543
|
+
return CODEX_HOOK_EVENTS.some((definition) => {
|
|
544
|
+
const entries = hooks[definition.event];
|
|
545
|
+
return Array.isArray(entries) && entries.some((entry) => isPusharyCodexHook(entry));
|
|
546
|
+
});
|
|
547
|
+
};
|
|
548
|
+
var missingCodexHookEvents = (config) => {
|
|
549
|
+
const hooks = asRecord2(config.hooks);
|
|
550
|
+
return CODEX_HOOK_EVENTS.filter((definition) => {
|
|
551
|
+
const entries = hooks?.[definition.event];
|
|
552
|
+
return !Array.isArray(entries) || !entries.some((entry) => isPusharyCodexHook(entry));
|
|
553
|
+
}).map((definition) => definition.event);
|
|
554
|
+
};
|
|
555
|
+
var codexHookTrustHash = (definition, command) => {
|
|
556
|
+
const hook = { command: withHookHoldBudget(command, holdBudgetOf(definition)), timeout: definition.timeout, type: "command" };
|
|
557
|
+
if (definition.statusMessage) hook.statusMessage = definition.statusMessage;
|
|
558
|
+
const entry = { hooks: [hook] };
|
|
559
|
+
if (definition.matcher) entry.matcher = definition.matcher;
|
|
560
|
+
return codexEntryTrustHash(CODEX_EVENT_KEY[definition.event], entry);
|
|
561
|
+
};
|
|
562
|
+
var codexEntryTrustHash = (eventKey, entry) => {
|
|
563
|
+
const hooks = Array.isArray(entry.hooks) ? entry.hooks : [];
|
|
564
|
+
const identity = {
|
|
565
|
+
event_name: eventKey,
|
|
566
|
+
hooks: hooks.map((hook) => ({ async: false, ...asRecord2(hook) }))
|
|
567
|
+
};
|
|
568
|
+
if (typeof entry.matcher === "string") identity.matcher = entry.matcher;
|
|
569
|
+
return "sha256:" + createHash("sha256").update(canonicalJson(identity), "utf8").digest("hex");
|
|
570
|
+
};
|
|
571
|
+
var codexHookPositions = (hooksConfig) => {
|
|
572
|
+
const hooks = asRecord2(hooksConfig?.hooks);
|
|
573
|
+
const positions = {};
|
|
574
|
+
for (const definition of CODEX_HOOK_EVENTS) {
|
|
575
|
+
const entries = hooks?.[definition.event];
|
|
576
|
+
if (!Array.isArray(entries)) continue;
|
|
577
|
+
const entryIndex = entries.findIndex((entry) => isPusharyCodexHook(entry));
|
|
578
|
+
if (entryIndex < 0) continue;
|
|
579
|
+
const inner = asRecord2(entries[entryIndex])?.hooks;
|
|
580
|
+
const hookIndex = Array.isArray(inner) ? inner.findIndex((hook) => isPusharyCommand(hook)) : -1;
|
|
581
|
+
positions[definition.event] = [entryIndex, Math.max(hookIndex, 0)];
|
|
582
|
+
}
|
|
583
|
+
return positions;
|
|
584
|
+
};
|
|
585
|
+
var codexHookStateKey = (hooksJsonPath, event, position) => `${hooksJsonPath}:${CODEX_EVENT_KEY[event]}:${position[0]}:${position[1]}`;
|
|
586
|
+
var codexVacatedIndexes = (hooksConfig) => {
|
|
587
|
+
const hooks = asRecord2(hooksConfig?.hooks);
|
|
588
|
+
const vacated = {};
|
|
589
|
+
for (const definition of CODEX_HOOK_EVENTS) {
|
|
590
|
+
const entries = hooks?.[definition.event];
|
|
591
|
+
if (!Array.isArray(entries)) continue;
|
|
592
|
+
const ours = entries.flatMap((entry, index) => isPusharyCodexHook(entry) ? [index] : []);
|
|
593
|
+
if (ours.length > 1) vacated[definition.event] = ours[1];
|
|
594
|
+
}
|
|
595
|
+
return vacated;
|
|
596
|
+
};
|
|
597
|
+
var dropVacatedCodexHookTrust = (config, hooksJsonPath, vacated) => {
|
|
598
|
+
const state = asRecord2(asRecord2(config.hooks)?.state);
|
|
599
|
+
if (!state) return false;
|
|
600
|
+
let changed = false;
|
|
601
|
+
for (const [event, from] of Object.entries(vacated)) {
|
|
602
|
+
const prefix = `${hooksJsonPath}:${CODEX_EVENT_KEY[event] ?? codexEventKeyOf(event)}:`;
|
|
603
|
+
for (const key of Object.keys(state)) {
|
|
604
|
+
if (!key.startsWith(prefix)) continue;
|
|
605
|
+
const entryIndex = Number(key.slice(prefix.length).split(":")[0]);
|
|
606
|
+
if (!Number.isInteger(entryIndex) || entryIndex < from) continue;
|
|
607
|
+
delete state[key];
|
|
608
|
+
changed = true;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
return changed;
|
|
612
|
+
};
|
|
613
|
+
var captureCodexHookTrust = (config, hooksJsonPath, positions) => {
|
|
614
|
+
const state = asRecord2(asRecord2(config.hooks)?.state);
|
|
615
|
+
if (!state) return {};
|
|
616
|
+
const snapshot = {};
|
|
617
|
+
for (const [event, position] of Object.entries(positions)) {
|
|
618
|
+
const value = state[codexHookStateKey(hooksJsonPath, event, position)];
|
|
619
|
+
if (value !== void 0) snapshot[event] = structuredClone(value);
|
|
620
|
+
}
|
|
621
|
+
return snapshot;
|
|
622
|
+
};
|
|
623
|
+
var restoreCodexHookTrust = (config, hooksJsonPath, positions, snapshot) => {
|
|
624
|
+
const restorable = Object.entries(snapshot).filter(([event]) => positions[event] !== void 0);
|
|
625
|
+
if (restorable.length === 0) return false;
|
|
626
|
+
const state = ensureRecord(ensureRecord(config, "hooks"), "state");
|
|
627
|
+
for (const [event, value] of restorable) {
|
|
628
|
+
state[codexHookStateKey(hooksJsonPath, event, positions[event])] = structuredClone(value);
|
|
629
|
+
}
|
|
630
|
+
return true;
|
|
631
|
+
};
|
|
632
|
+
var codexTrustGaps = (config, hooksConfig, hooksJsonPath, command, positions) => {
|
|
633
|
+
const state = asRecord2(asRecord2(config.hooks)?.state);
|
|
634
|
+
const hooks = asRecord2(hooksConfig?.hooks);
|
|
635
|
+
const gaps = [];
|
|
636
|
+
for (const definition of CODEX_HOOK_EVENTS) {
|
|
637
|
+
const position = positions[definition.event];
|
|
638
|
+
if (!position) continue;
|
|
639
|
+
const stored = asRecord2(state?.[codexHookStateKey(hooksJsonPath, definition.event, position)])?.trusted_hash;
|
|
640
|
+
const eventKey = CODEX_EVENT_KEY[definition.event] ?? definition.event;
|
|
641
|
+
const onDisk = asRecord2(
|
|
642
|
+
asRecord2(hooks?.[definition.event]) ?? (Array.isArray(hooks?.[definition.event]) ? hooks[definition.event][position[0]] : void 0)
|
|
643
|
+
);
|
|
644
|
+
const diskHash = onDisk ? codexEntryTrustHash(eventKey, onDisk) : void 0;
|
|
645
|
+
if (stored !== void 0 && diskHash !== void 0 && stored === diskHash) {
|
|
646
|
+
if (diskHash !== codexHookTrustHash(definition, command)) {
|
|
647
|
+
gaps.push({ event: definition.event, kind: "stale" });
|
|
648
|
+
}
|
|
649
|
+
continue;
|
|
650
|
+
}
|
|
651
|
+
if (stored !== (diskHash ?? codexHookTrustHash(definition, command))) {
|
|
652
|
+
gaps.push({ event: definition.event, kind: "untrusted" });
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
return gaps;
|
|
656
|
+
};
|
|
657
|
+
var removeCodexMcpServer = (config) => {
|
|
658
|
+
const servers = asRecord2(config.mcp_servers);
|
|
659
|
+
if (!servers?.pushary) return false;
|
|
660
|
+
delete servers.pushary;
|
|
661
|
+
if (Object.keys(servers).length === 0) delete config.mcp_servers;
|
|
662
|
+
return true;
|
|
663
|
+
};
|
|
664
|
+
var removeCodexHookTrust = (config, hooksJsonPath, positions) => {
|
|
665
|
+
const state = asRecord2(asRecord2(config.hooks)?.state);
|
|
666
|
+
if (!state) return false;
|
|
667
|
+
let changed = false;
|
|
668
|
+
for (const definition of CODEX_HOOK_EVENTS) {
|
|
669
|
+
const position = positions[definition.event];
|
|
670
|
+
if (!position) continue;
|
|
671
|
+
const key = codexHookStateKey(hooksJsonPath, definition.event, position);
|
|
672
|
+
if (key in state) {
|
|
673
|
+
delete state[key];
|
|
674
|
+
changed = true;
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
if (changed && Object.keys(state).length === 0) {
|
|
678
|
+
const hooks = asRecord2(config.hooks);
|
|
679
|
+
if (hooks) {
|
|
680
|
+
delete hooks.state;
|
|
681
|
+
if (Object.keys(hooks).length === 0) delete config.hooks;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
return changed;
|
|
685
|
+
};
|
|
686
|
+
var removeCodexSettings = (config, hooksJsonPath, positions) => {
|
|
687
|
+
const mcp = removeCodexMcpServer(config);
|
|
688
|
+
const trust = removeCodexHookTrust(config, hooksJsonPath, positions);
|
|
689
|
+
return mcp || trust;
|
|
690
|
+
};
|
|
691
|
+
var addCodexHookTrust = (config, hooksJsonPath, command, positions) => {
|
|
692
|
+
const hooks = ensureRecord(config, "hooks");
|
|
693
|
+
const state = ensureRecord(hooks, "state");
|
|
694
|
+
for (const definition of CODEX_HOOK_EVENTS) {
|
|
695
|
+
const position = positions[definition.event];
|
|
696
|
+
if (!position) continue;
|
|
697
|
+
const key = codexHookStateKey(hooksJsonPath, definition.event, position);
|
|
698
|
+
const ours = codexHookTrustHash(definition, command);
|
|
699
|
+
const existing = asRecord2(state[key]) ?? {};
|
|
700
|
+
state[key] = { ...existing, trusted_hash: ours };
|
|
701
|
+
for (const stale of staleTrustKeys(state, hooksJsonPath, definition.event, key, ours)) {
|
|
702
|
+
delete state[stale];
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
};
|
|
706
|
+
var staleTrustKeys = (state, hooksJsonPath, event, currentKey, ourHash) => {
|
|
707
|
+
const prefix = `${hooksJsonPath}:${CODEX_EVENT_KEY[event]}:`;
|
|
708
|
+
return Object.keys(state).filter(
|
|
709
|
+
(key) => key !== currentKey && key.startsWith(prefix) && asRecord2(state[key])?.trusted_hash === ourHash
|
|
710
|
+
);
|
|
711
|
+
};
|
|
712
|
+
|
|
713
|
+
// src/setup/paths.ts
|
|
714
|
+
import { homedir as homedir2 } from "os";
|
|
715
|
+
import { join as join3 } from "path";
|
|
716
|
+
var claudeSettings = () => join3(homedir2(), ".claude", "settings.json");
|
|
717
|
+
var claudeJson = () => join3(homedir2(), ".claude.json");
|
|
718
|
+
var claudeSkillDir = () => join3(homedir2(), ".claude", "skills", "pushary");
|
|
719
|
+
var cursorPluginDir = () => join3(homedir2(), ".cursor", "plugins", "local", "pushary");
|
|
720
|
+
var cursorUserHooks = () => join3(homedir2(), ".cursor", "hooks.json");
|
|
721
|
+
var cursorUserMcp = () => join3(homedir2(), ".cursor", "mcp.json");
|
|
722
|
+
var vscodePluginDir = () => join3(homedir2(), ".pushary", "plugins", "vscode");
|
|
723
|
+
var vscodePluginMcp = () => join3(vscodePluginDir(), ".mcp.json");
|
|
724
|
+
var claudeSettingsLocal = () => join3(homedir2(), ".claude", "settings.local.json");
|
|
725
|
+
var pusharyDir = () => join3(homedir2(), ".pushary");
|
|
726
|
+
var claudeMcpReceipt = () => join3(pusharyDir(), "setup", "claude-mcp.json");
|
|
727
|
+
var bridgeAgentKey = () => join3(pusharyDir(), "bridge-auth", "agent-key.json");
|
|
728
|
+
var autoUpdateState = () => join3(pusharyDir(), "auto-update.json");
|
|
729
|
+
var autoUpdateLock = () => join3(pusharyDir(), "auto-update.lock");
|
|
730
|
+
var autoUpdateLog = () => join3(pusharyDir(), "logs", "auto-update.log");
|
|
731
|
+
var clientReportClaims = () => join3(pusharyDir(), "client-reports");
|
|
732
|
+
var claudeRegistrationReceipt = () => join3(pusharyDir(), "setup", "claude-registration.json");
|
|
733
|
+
var hermesConfig = () => join3(homedir2(), ".hermes", "config.yaml");
|
|
734
|
+
var hermesConfigReceipt = () => join3(pusharyDir(), "setup", "hermes-config.json");
|
|
735
|
+
var fxQuestionRuleReceipt = () => join3(pusharyDir(), "setup", "fx-question-rule.json");
|
|
736
|
+
var hookTakeoverReceipt = (agent) => join3(pusharyDir(), "setup", `hook-takeover-${agent}.json`);
|
|
737
|
+
var codexSkillDir2 = () => join3(codexHome(), "skills", "pushary");
|
|
738
|
+
var codexAgentsMd2 = () => join3(codexHome(), "AGENTS.md");
|
|
739
|
+
var geminiSettings = () => join3(homedir2(), ".gemini", "settings.json");
|
|
740
|
+
var geminiMd = () => join3(homedir2(), ".gemini", "GEMINI.md");
|
|
741
|
+
var pusharyConfigFile = () => join3(homedir2(), ".pushary", "config.json");
|
|
742
|
+
var fxConfigDir = () => join3(homedir2(), ".fx");
|
|
743
|
+
var fxMcpProfile = () => join3(fxConfigDir(), "mcp.json");
|
|
744
|
+
var fxSettings = () => join3(fxConfigDir(), "settings.json");
|
|
745
|
+
var fxAgentsMd = () => join3(fxConfigDir(), "AGENTS.md");
|
|
746
|
+
|
|
747
|
+
export {
|
|
748
|
+
registeredCommands,
|
|
749
|
+
namesOurBinary,
|
|
750
|
+
registrationHolderOf,
|
|
751
|
+
LATEST_TAG,
|
|
752
|
+
cleanNpmEnv,
|
|
753
|
+
npmErrorMessage,
|
|
754
|
+
execNpm,
|
|
755
|
+
readGlobalPrefix,
|
|
756
|
+
resolveGlobalBinary,
|
|
757
|
+
resolvePathBinary,
|
|
758
|
+
npmIsOnPath,
|
|
759
|
+
globalPackageState,
|
|
760
|
+
NpmMissingError,
|
|
761
|
+
resolveBinary,
|
|
762
|
+
resolvePackageVersionFromBinary,
|
|
763
|
+
resolveGlobalPackageVersion,
|
|
764
|
+
readGlobalFolder,
|
|
765
|
+
readGlobalCliInstallAt,
|
|
766
|
+
readGlobalCliInstall,
|
|
767
|
+
resolveGlobalBinDir,
|
|
768
|
+
fetchLatestPublishedVersion,
|
|
769
|
+
installPackageGlobally,
|
|
770
|
+
plannedGlobalPackage,
|
|
771
|
+
installGlobally,
|
|
772
|
+
holdBudgetOf,
|
|
773
|
+
guardBinary,
|
|
774
|
+
guardCursorGate,
|
|
775
|
+
guardedArgv,
|
|
776
|
+
registerHookEntry,
|
|
777
|
+
isProtectedByScope,
|
|
778
|
+
canonicalJson,
|
|
779
|
+
CODEX_HOOK_BINARY,
|
|
780
|
+
codexHome,
|
|
781
|
+
codexHomeFrom,
|
|
782
|
+
codexConfigToml,
|
|
783
|
+
codexHooksJsonIn,
|
|
784
|
+
codexHooksJson,
|
|
785
|
+
codexAgentsMd,
|
|
786
|
+
codexSkillDir,
|
|
787
|
+
CODEX_HOOK_EVENTS,
|
|
788
|
+
addCodexMcpServer,
|
|
789
|
+
readCodexMcpAuth,
|
|
790
|
+
isPusharyCodexCommand,
|
|
791
|
+
codexRegistrationHolder,
|
|
792
|
+
addCodexHooks,
|
|
793
|
+
removeCodexHooks,
|
|
794
|
+
hasCodexHooks,
|
|
795
|
+
missingCodexHookEvents,
|
|
796
|
+
codexHookPositions,
|
|
797
|
+
codexVacatedIndexes,
|
|
798
|
+
dropVacatedCodexHookTrust,
|
|
799
|
+
captureCodexHookTrust,
|
|
800
|
+
restoreCodexHookTrust,
|
|
801
|
+
codexTrustGaps,
|
|
802
|
+
removeCodexHookTrust,
|
|
803
|
+
removeCodexSettings,
|
|
804
|
+
addCodexHookTrust,
|
|
805
|
+
claudeSettings,
|
|
806
|
+
claudeJson,
|
|
807
|
+
claudeSkillDir,
|
|
808
|
+
cursorPluginDir,
|
|
809
|
+
cursorUserHooks,
|
|
810
|
+
cursorUserMcp,
|
|
811
|
+
vscodePluginDir,
|
|
812
|
+
vscodePluginMcp,
|
|
813
|
+
claudeSettingsLocal,
|
|
814
|
+
pusharyDir,
|
|
815
|
+
claudeMcpReceipt,
|
|
816
|
+
bridgeAgentKey,
|
|
817
|
+
autoUpdateState,
|
|
818
|
+
autoUpdateLock,
|
|
819
|
+
autoUpdateLog,
|
|
820
|
+
clientReportClaims,
|
|
821
|
+
claudeRegistrationReceipt,
|
|
822
|
+
hermesConfig,
|
|
823
|
+
hermesConfigReceipt,
|
|
824
|
+
fxQuestionRuleReceipt,
|
|
825
|
+
hookTakeoverReceipt,
|
|
826
|
+
codexSkillDir2,
|
|
827
|
+
codexAgentsMd2,
|
|
828
|
+
geminiSettings,
|
|
829
|
+
geminiMd,
|
|
830
|
+
pusharyConfigFile,
|
|
831
|
+
fxConfigDir,
|
|
832
|
+
fxMcpProfile,
|
|
833
|
+
fxSettings,
|
|
834
|
+
fxAgentsMd
|
|
835
|
+
};
|