@pushary/agent-hooks 0.95.5 → 0.95.7
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 +14 -0
- package/data/cursor-plugin/.cursor-plugin/plugin.json +1 -1
- package/data/cursor-plugin/CHANGELOG.md +7 -0
- package/data/cursor-plugin/scripts/pushary-gate.mjs +72 -17
- package/data/vscode-plugin/.claude-plugin/plugin.json +1 -1
- package/data/vscode-plugin/CHANGELOG.md +6 -0
- package/data/vscode-plugin/scripts/pushary-gate.mjs +76 -24
- package/dist/bin/pushary-bell-hook.js +18 -18
- package/dist/bin/pushary-bell.js +7 -7
- package/dist/bin/pushary-claude.js +24 -24
- package/dist/bin/pushary-clean.js +18 -18
- package/dist/bin/pushary-codex-bridge.js +29 -12
- package/dist/bin/pushary-codex-hook.js +29 -29
- package/dist/bin/pushary-codex.js +7 -7
- package/dist/bin/pushary-connect.js +8 -8
- package/dist/bin/pushary-cowork.js +5 -5
- package/dist/bin/pushary-daemon.js +24 -24
- package/dist/bin/pushary-disconnect.js +8 -8
- package/dist/bin/pushary-doctor.js +91 -36
- package/dist/bin/pushary-elicitation-hook.js +17 -17
- package/dist/bin/pushary-gemini-bridge.js +9 -9
- package/dist/bin/pushary-gemini-hook.js +65 -50
- package/dist/bin/pushary-hook.js +24 -24
- package/dist/bin/pushary-login.js +9 -9
- package/dist/bin/pushary-logout.js +6 -6
- package/dist/bin/pushary-mode.js +5 -5
- package/dist/bin/pushary-notification-hook.js +19 -19
- package/dist/bin/pushary-opencode-hook.js +29 -29
- package/dist/bin/pushary-permission-denied-hook.js +22 -22
- package/dist/bin/pushary-permission-hook.js +22 -22
- package/dist/bin/pushary-post-hook.js +19 -19
- package/dist/bin/pushary-prompt-hook.js +19 -19
- package/dist/bin/pushary-session-end-hook.js +19 -19
- package/dist/bin/pushary-session-start-hook.js +22 -22
- package/dist/bin/pushary-setup.js +45 -38
- package/dist/bin/pushary-stats.js +4 -4
- package/dist/bin/pushary-status.js +14 -10
- package/dist/bin/pushary-stop-hook.js +19 -19
- package/dist/bin/pushary-stopfailure-hook.js +19 -19
- package/dist/bin/pushary-suggestions.js +2 -2
- package/dist/bin/pushary-transcript-register.js +16 -16
- package/dist/bin/pushary-transcripts.js +7 -7
- package/dist/bin/pushary-upgrade.js +16 -16
- package/dist/bin/pushary-wait.js +8 -7
- package/dist/bin/pushary.js +1 -1
- package/dist/{chunk-N4VNLISN.js → chunk-4OSQZ5TB.js} +2 -2
- package/dist/{chunk-IM2A5H33.js → chunk-54W7ELDQ.js} +1 -1
- package/dist/{chunk-UCS65VVR.js → chunk-5GKNAQTG.js} +1 -1
- package/dist/{chunk-Z2UWFEJW.js → chunk-6W2WGNO5.js} +7 -7
- package/dist/{chunk-2D7BAOMY.js → chunk-7RNY3UHJ.js} +3 -3
- package/dist/{chunk-OW6AZDY2.js → chunk-BGX4QUQF.js} +1 -1
- package/dist/{chunk-I6GZX3FT.js → chunk-BQL7UDIB.js} +1 -1
- package/dist/{chunk-HGUZOUB2.js → chunk-CNUDZS4V.js} +3 -3
- package/dist/chunk-CVWG2N3Y.js +16 -0
- package/dist/{chunk-BMANRGJQ.js → chunk-EMGGUO2Q.js} +1 -1
- package/dist/{chunk-QHWXUNMN.js → chunk-FRXZMCVA.js} +1 -1
- package/dist/{chunk-DLLAWCQB.js → chunk-GOQHN2UH.js} +1 -1
- package/dist/{chunk-HBHEX3JQ.js → chunk-GQRU4KJF.js} +3 -3
- package/dist/{chunk-4RF52LSU.js → chunk-HAXZ6CBF.js} +8 -8
- package/dist/{chunk-OEKFD5BC.js → chunk-IZVK6FKD.js} +1 -1
- package/dist/{chunk-KAVDAZ7A.js → chunk-L7NOG547.js} +1 -1
- package/dist/{chunk-D6PRC3YQ.js → chunk-LZQVAEWP.js} +2 -1
- package/dist/{chunk-WDKHZRAS.js → chunk-MC5WUOJJ.js} +41 -0
- package/dist/{chunk-6VANQN5Q.js → chunk-NKUE5SJB.js} +2 -2
- package/dist/{chunk-QQGNFHIK.js → chunk-PQGLDN4V.js} +2 -2
- package/dist/{chunk-CUEZMPLJ.js → chunk-QJPYJVL3.js} +1 -1
- package/dist/{chunk-RLXVW4AM.js → chunk-SDNTRS76.js} +1 -1
- package/dist/{chunk-4PX4QK2Z.js → chunk-SKPXQRKN.js} +1 -1
- package/dist/{chunk-VTO3TNIB.js → chunk-TY6IJY6I.js} +1 -1
- package/dist/{chunk-HQIVR7MM.js → chunk-U5RIXR7I.js} +1 -1
- package/dist/{chunk-WRDLHR2F.js → chunk-UKMLSQXA.js} +2 -2
- package/dist/{chunk-6AEM5RCS.js → chunk-USWT4PTR.js} +1 -1
- package/dist/{chunk-D62EONG3.js → chunk-VDI645ZX.js} +1 -1
- package/dist/{chunk-OI3DJA7N.js → chunk-VERXHIQF.js} +1 -1
- package/dist/{chunk-67YNQ3QA.js → chunk-VIUOGMT5.js} +18 -14
- package/dist/{chunk-S5CMLR35.js → chunk-VMQU7YEJ.js} +1 -1
- package/dist/{chunk-XLOELLAS.js → chunk-VMZ4SWQD.js} +4 -4
- package/dist/{chunk-32RMQO5Z.js → chunk-WAUAYRUQ.js} +47 -4
- package/dist/{chunk-VOSGZ6PH.js → chunk-WPS5IM7B.js} +3 -3
- package/dist/{chunk-HCMM3BOW.js → chunk-XJQOVWBQ.js} +14 -11
- package/dist/{chunk-KI4PA7MQ.js → chunk-XO3JR3OP.js} +90 -18
- package/dist/{chunk-IVQMNLSA.js → chunk-XUTSZYYJ.js} +1 -1
- package/dist/{chunk-2ZTL3PGN.js → chunk-XZWUOCJZ.js} +1 -1
- package/dist/{chunk-BDI2MXTT.js → chunk-Z4JVEL7R.js} +1 -1
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.js +10 -10
- package/package.json +1 -1
- package/dist/chunk-IIENZE2J.js +0 -15
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
describeWaitLadder
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-CVWG2N3Y.js";
|
|
5
5
|
import {
|
|
6
6
|
readServiceExecPath,
|
|
7
7
|
versionManagerOf
|
|
8
8
|
} from "../chunk-GU3EJCVV.js";
|
|
9
9
|
import {
|
|
10
|
-
readMachineReadiness
|
|
11
|
-
|
|
10
|
+
readMachineReadiness,
|
|
11
|
+
readManagedControlEvidence
|
|
12
|
+
} from "../chunk-WAUAYRUQ.js";
|
|
12
13
|
import {
|
|
13
14
|
CURSOR_GATE_SCRIPT
|
|
14
15
|
} from "../chunk-KBITFQST.js";
|
|
@@ -20,28 +21,28 @@ import {
|
|
|
20
21
|
isDetected,
|
|
21
22
|
isPluginRegistered,
|
|
22
23
|
vscodeSettingsTargets
|
|
23
|
-
} from "../chunk-
|
|
24
|
+
} from "../chunk-NKUE5SJB.js";
|
|
24
25
|
import {
|
|
25
26
|
OPENCODE_PLUGIN_PLACEHOLDER,
|
|
26
27
|
openCodePluginDir,
|
|
27
28
|
openCodePluginFile,
|
|
28
29
|
openCodeWired
|
|
29
|
-
} from "../chunk-
|
|
30
|
-
import "../chunk-
|
|
30
|
+
} from "../chunk-U5RIXR7I.js";
|
|
31
|
+
import "../chunk-CNUDZS4V.js";
|
|
31
32
|
import {
|
|
32
33
|
detectProviderLiveCapabilities,
|
|
33
34
|
hasSpawnProvider,
|
|
34
35
|
resolveControlPolicy
|
|
35
|
-
} from "../chunk-
|
|
36
|
+
} from "../chunk-54W7ELDQ.js";
|
|
36
37
|
import {
|
|
37
38
|
readLedgerSummary
|
|
38
|
-
} from "../chunk-
|
|
39
|
+
} from "../chunk-5GKNAQTG.js";
|
|
39
40
|
import {
|
|
40
41
|
describeBridgeResolution,
|
|
41
42
|
isBridgeLocator,
|
|
42
43
|
liveBridgeProbe,
|
|
43
44
|
resolveBridgeStub
|
|
44
|
-
} from "../chunk-
|
|
45
|
+
} from "../chunk-QJPYJVL3.js";
|
|
45
46
|
import {
|
|
46
47
|
claudeOtherHolders,
|
|
47
48
|
claudeWired,
|
|
@@ -56,7 +57,7 @@ import {
|
|
|
56
57
|
geminiWired,
|
|
57
58
|
holdsLong,
|
|
58
59
|
pusharyHookExecutable
|
|
59
|
-
} from "../chunk-
|
|
60
|
+
} from "../chunk-GQRU4KJF.js";
|
|
60
61
|
import {
|
|
61
62
|
daemonHasRemoteProof,
|
|
62
63
|
daemonServiceManagerAvailable,
|
|
@@ -65,22 +66,22 @@ import {
|
|
|
65
66
|
readDaemonNeedsLogin,
|
|
66
67
|
readDaemonStatus,
|
|
67
68
|
readInstalledDaemonService
|
|
68
|
-
} from "../chunk-
|
|
69
|
+
} from "../chunk-VMZ4SWQD.js";
|
|
69
70
|
import {
|
|
70
71
|
GEMINI_HOOK_BINARY,
|
|
71
72
|
hasGeminiHooks,
|
|
72
73
|
missingGeminiHookEvents
|
|
73
|
-
} from "../chunk-
|
|
74
|
+
} from "../chunk-7RNY3UHJ.js";
|
|
74
75
|
import "../chunk-3AJIK5AO.js";
|
|
75
76
|
import "../chunk-XHKBHWLX.js";
|
|
76
77
|
import {
|
|
77
78
|
execNpm
|
|
78
79
|
} from "../chunk-L4ZCFMWW.js";
|
|
79
|
-
import "../chunk-
|
|
80
|
-
import "../chunk-
|
|
80
|
+
import "../chunk-Z4JVEL7R.js";
|
|
81
|
+
import "../chunk-XUTSZYYJ.js";
|
|
81
82
|
import {
|
|
82
83
|
getMachineId
|
|
83
|
-
} from "../chunk-
|
|
84
|
+
} from "../chunk-4OSQZ5TB.js";
|
|
84
85
|
import {
|
|
85
86
|
CODEX_HOOK_EVENTS,
|
|
86
87
|
claudeJson,
|
|
@@ -102,27 +103,28 @@ import {
|
|
|
102
103
|
pusharyConfigFile,
|
|
103
104
|
readCodexMcpAuth,
|
|
104
105
|
vscodePluginDir
|
|
105
|
-
} from "../chunk-
|
|
106
|
+
} from "../chunk-BGX4QUQF.js";
|
|
106
107
|
import {
|
|
107
108
|
describeReach,
|
|
108
109
|
describeRoundTrip,
|
|
109
110
|
fetchChannels,
|
|
110
111
|
reachVerdict,
|
|
112
|
+
roundTripEvidence,
|
|
111
113
|
verifyRoundTrip
|
|
112
|
-
} from "../chunk-
|
|
114
|
+
} from "../chunk-XO3JR3OP.js";
|
|
113
115
|
import "../chunk-3EGEA4KH.js";
|
|
114
116
|
import {
|
|
115
117
|
checkApiKey,
|
|
116
118
|
describeKeyCheck
|
|
117
|
-
} from "../chunk-
|
|
119
|
+
} from "../chunk-BQL7UDIB.js";
|
|
118
120
|
import "../chunk-QCGNGEGE.js";
|
|
119
121
|
import {
|
|
120
122
|
createIo
|
|
121
123
|
} from "../chunk-5RUIFDTP.js";
|
|
122
124
|
import {
|
|
123
125
|
exitOnHelpFlag
|
|
124
|
-
} from "../chunk-
|
|
125
|
-
import "../chunk-
|
|
126
|
+
} from "../chunk-GOQHN2UH.js";
|
|
127
|
+
import "../chunk-LZQVAEWP.js";
|
|
126
128
|
import {
|
|
127
129
|
parseFlags
|
|
128
130
|
} from "../chunk-GMXKITVA.js";
|
|
@@ -133,7 +135,7 @@ import {
|
|
|
133
135
|
keyPrefix,
|
|
134
136
|
readControlMode,
|
|
135
137
|
readKeySource
|
|
136
|
-
} from "../chunk-
|
|
138
|
+
} from "../chunk-VMQU7YEJ.js";
|
|
137
139
|
import {
|
|
138
140
|
callMcpTool,
|
|
139
141
|
sendMcpRequest
|
|
@@ -145,18 +147,21 @@ import {
|
|
|
145
147
|
import {
|
|
146
148
|
EXIT
|
|
147
149
|
} from "../chunk-KZERVKTD.js";
|
|
148
|
-
import "../chunk-
|
|
150
|
+
import "../chunk-L7NOG547.js";
|
|
149
151
|
import {
|
|
150
152
|
getBaseUrl
|
|
151
153
|
} from "../chunk-2UMNXADU.js";
|
|
152
154
|
import {
|
|
155
|
+
AGENTS,
|
|
153
156
|
CURSOR_GATE_EVENTS,
|
|
154
|
-
HOOK_LOCATOR_BINARY
|
|
155
|
-
|
|
157
|
+
HOOK_LOCATOR_BINARY,
|
|
158
|
+
hookActivationResult,
|
|
159
|
+
isHookActivationReport
|
|
160
|
+
} from "../chunk-MC5WUOJJ.js";
|
|
156
161
|
|
|
157
162
|
// bin/pushary-doctor.ts
|
|
158
163
|
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
159
|
-
import { join } from "path";
|
|
164
|
+
import { join, resolve } from "path";
|
|
160
165
|
import { homedir as homedir2 } from "os";
|
|
161
166
|
import { execFileSync } from "child_process";
|
|
162
167
|
import { confirm } from "@inquirer/prompts";
|
|
@@ -215,6 +220,29 @@ var resolveDoctorExit = ({ keyExit, failures }) => {
|
|
|
215
220
|
return EXIT.PROBLEMS_FOUND;
|
|
216
221
|
};
|
|
217
222
|
|
|
223
|
+
// src/diagnostics/activation.ts
|
|
224
|
+
var readHookActivation = async (apiKey, agentType, target, deps = {}) => {
|
|
225
|
+
const machineId = deps.machineId ?? getMachineId();
|
|
226
|
+
const url = new URL("/api/agent/onboarding/activation", deps.baseUrl ?? getBaseUrl());
|
|
227
|
+
url.searchParams.set("machineId", machineId);
|
|
228
|
+
url.searchParams.set("agent", agentType);
|
|
229
|
+
url.searchParams.set("target", target);
|
|
230
|
+
try {
|
|
231
|
+
const response = await (deps.fetchImpl ?? fetch)(url, {
|
|
232
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
233
|
+
signal: AbortSignal.timeout(1e4)
|
|
234
|
+
});
|
|
235
|
+
if (!response.ok) return { kind: "unavailable", detail: response.status === 404 ? "This server does not report hook activation yet." : `Evidence request returned HTTP ${response.status}.` };
|
|
236
|
+
const body = await response.json();
|
|
237
|
+
if (!isHookActivationReport(body) || body.machineId !== machineId || body.agentType !== agentType || body.target !== target) {
|
|
238
|
+
return { kind: "unavailable", detail: "The server returned invalid or mismatched activation evidence." };
|
|
239
|
+
}
|
|
240
|
+
return { kind: "ok", report: body };
|
|
241
|
+
} catch {
|
|
242
|
+
return { kind: "unavailable", detail: "Activation evidence could not be read. Retry when the connection is available." };
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
|
|
218
246
|
// src/diagnostics/roundtrip-plan.ts
|
|
219
247
|
var planRoundTrip = (context) => {
|
|
220
248
|
if (context.roundtrip) return { kind: "always" };
|
|
@@ -238,17 +266,25 @@ var firstToken = (command) => {
|
|
|
238
266
|
// bin/pushary-doctor.ts
|
|
239
267
|
exitOnHelpFlag("doctor");
|
|
240
268
|
var parseDoctorOptions = (argv) => {
|
|
241
|
-
const flags = parseFlags(argv, { bools: ["json", "roundtrip", "no-push", "bundle", "help", "h"], values: [] });
|
|
269
|
+
const flags = parseFlags(argv, { bools: ["json", "roundtrip", "no-push", "bundle", "help", "h"], values: ["activation", "activation-target"] });
|
|
242
270
|
if (flags.command !== void 0 && flags.command !== "doctor") {
|
|
243
271
|
throw new UsageError(`doctor takes no positional arguments (got ${flags.command})`);
|
|
244
272
|
}
|
|
245
273
|
const roundtrip = flags.bools.has("roundtrip");
|
|
246
|
-
const
|
|
274
|
+
const activation2 = flags.values.get("activation") ?? null;
|
|
275
|
+
if (activation2 && (!Object.hasOwn(AGENTS, activation2) || !AGENTS[activation2].activationGuide)) throw new UsageError("This build supports --activation claude_code. Other integrations remain unverified.");
|
|
276
|
+
const rawTarget = flags.values.get("activation-target");
|
|
277
|
+
if (Boolean(activation2) !== Boolean(rawTarget)) throw new UsageError("--activation requires --activation-target with the unique scratch-file path used for this check.");
|
|
278
|
+
const activationTarget = rawTarget ? resolve(rawTarget) : null;
|
|
279
|
+
const noPush = flags.bools.has("no-push") || activation2 !== null && !roundtrip;
|
|
247
280
|
if (roundtrip && noPush) {
|
|
248
281
|
throw new UsageError("--roundtrip needs to send a push, so it cannot be combined with --no-push");
|
|
249
282
|
}
|
|
250
|
-
return { json: flags.bools.has("json"), roundtrip, noPush, bundle: flags.bools.has("bundle") };
|
|
283
|
+
return { activationTarget, activation: activation2, json: flags.bools.has("json"), roundtrip, noPush, bundle: flags.bools.has("bundle") };
|
|
251
284
|
};
|
|
285
|
+
var activation = null;
|
|
286
|
+
var hookActivation = "unverified";
|
|
287
|
+
var roundTrip = null;
|
|
252
288
|
var options;
|
|
253
289
|
try {
|
|
254
290
|
options = parseDoctorOptions(process.argv.slice(2));
|
|
@@ -642,8 +678,8 @@ var main = async () => {
|
|
|
642
678
|
const controlUnsupported = controlMode === "on" && controlPolicy !== "enabled";
|
|
643
679
|
const daemonOk = controlUnsupported ? false : controlPolicy === "disabled" ? daemon.kind === "absent" && !serviceDefined : controlPolicy === "enabled" ? daemonHasRemoteProof(daemon) : controlPolicy === "legacy" ? false : daemon.kind === "absent" && !serviceDefined;
|
|
644
680
|
const needsLogin = loginBlock.kind === "blocked" ? `needs login: ${describeDaemonLoginBlock(loginBlock.record)}` : null;
|
|
645
|
-
const daemonFailure = daemonOk ? null : needsLogin ?? (controlUnsupported ? "
|
|
646
|
-
const daemonDetail = daemonFailure ? needsLogin ? daemonFailure : controlUnsupported ? `${daemonFailure}; re-run setup with \`--control off\`, or
|
|
681
|
+
const daemonFailure = daemonOk ? null : needsLogin ?? (controlUnsupported ? !controlSupported ? "install and configure a supported Claude Code, Codex or Gemini provider" : "no native background-service manager is available in this OS session" : controlPolicy === "enabled" && daemon.kind === "running" ? "the daemon has not completed an authenticated heartbeat and control poll" : controlPolicy === "disabled" ? `control is disabled but ${serviceDefined ? "the service definition remains" : `local state is ${daemon.kind}`}` : controlPolicy === "legacy" ? "a legacy service exists without a usable provider/service-manager pair" : `local state is ${daemon.kind}`);
|
|
682
|
+
const daemonDetail = daemonFailure ? needsLogin ? daemonFailure : controlUnsupported ? `${daemonFailure}; re-run setup with \`--control off\`, or repair the missing prerequisite` : controlPolicy === "disabled" ? `${daemonFailure}; re-run setup with \`--control off\`` : controlPolicy === "legacy" ? `${daemonFailure}; run setup with \`--control off\`, or repair the missing prerequisite` : `${daemonFailure}; run \`pushary daemon install\` to install or repair the background service` : controlPolicy === "disabled" ? "disabled by setup" : controlPolicy === "preserve-absent" ? "legacy absence preserved" : !controlRequired && daemon.kind === "absent" ? "managed control is unsupported on this provider or operating-system session" : null;
|
|
647
683
|
check(
|
|
648
684
|
daemonOk,
|
|
649
685
|
"Remote-control daemon",
|
|
@@ -672,8 +708,9 @@ var main = async () => {
|
|
|
672
708
|
} else if (serviceFile) {
|
|
673
709
|
console.log(` ${warn} Remote-control service runtime ${dim(`(could not read the node path out of ${serviceFile.path})`)}`);
|
|
674
710
|
}
|
|
711
|
+
let serverProof = null;
|
|
675
712
|
if (controlPolicy === "enabled" && apiKey && daemon.machineId) {
|
|
676
|
-
|
|
713
|
+
serverProof = await readMachineReadiness(apiKey, ["spawn", ...controlCapabilities], {
|
|
677
714
|
machineId: daemon.machineId
|
|
678
715
|
});
|
|
679
716
|
if (serverProof.kind === "unsupported") {
|
|
@@ -975,7 +1012,7 @@ var main = async () => {
|
|
|
975
1012
|
stdinIsTty: process.stdin.isTTY === true
|
|
976
1013
|
});
|
|
977
1014
|
const testQuestion = roundTripPlan.kind === "always" ? true : roundTripPlan.kind === "never" ? false : await confirm({
|
|
978
|
-
message: "Test question
|
|
1015
|
+
message: "Test a synthetic question and answer? (sends a notification; does not test agent hooks)",
|
|
979
1016
|
default: roundTripPlan.defaultAnswer
|
|
980
1017
|
});
|
|
981
1018
|
if (testQuestion) {
|
|
@@ -983,8 +1020,9 @@ var main = async () => {
|
|
|
983
1020
|
console.log(` ${dim("Question Roundtrip")}`);
|
|
984
1021
|
console.log(` ${dim("\u2192")} Question sent, waiting for your answer...`);
|
|
985
1022
|
const outcome = await verifyRoundTrip(apiKey, "Pushary Doctor");
|
|
1023
|
+
roundTrip = outcome;
|
|
986
1024
|
const report = describeRoundTrip(outcome);
|
|
987
|
-
check(report.ok, "
|
|
1025
|
+
check(report.ok, "Synthetic answer test", report.message);
|
|
988
1026
|
for (const line of report.next) console.log(` ${dim(line)}`);
|
|
989
1027
|
}
|
|
990
1028
|
}
|
|
@@ -1000,7 +1038,8 @@ var main = async () => {
|
|
|
1000
1038
|
console.log(` ${dim("Wait Ladder")} ${dim("(what happens when you don't answer)")}`);
|
|
1001
1039
|
const wildcard = policy.policies?.find((p) => p.tool === "*");
|
|
1002
1040
|
const defaultWindow = policy.defaultPushFirstSeconds ?? 20;
|
|
1003
|
-
|
|
1041
|
+
const defaultLadder = describeWaitLadder(wildcard ?? { tool: "*", mode: "push_first", pushFirstSeconds: defaultWindow, timeoutSeconds: 0, timeoutAction: "escalate" });
|
|
1042
|
+
console.log(` ${dim("\xB7")} default: ${cyan(`${defaultWindow}s`)} ${dim("\xB7")} ${defaultLadder}`);
|
|
1004
1043
|
if (policy.modeOverride) {
|
|
1005
1044
|
console.log(` ${warn} mode override active: ${cyan(policy.modeOverride)} ${dim("(pushary mode status)")}`);
|
|
1006
1045
|
}
|
|
@@ -1013,7 +1052,19 @@ var main = async () => {
|
|
|
1013
1052
|
} catch {
|
|
1014
1053
|
}
|
|
1015
1054
|
}
|
|
1055
|
+
if (options.activation) {
|
|
1056
|
+
activation = apiKey ? await readHookActivation(apiKey, options.activation, options.activationTarget) : { kind: "unavailable", detail: "Sign in before reading this workspace\u2019s hook evidence." };
|
|
1057
|
+
const latest = activation.kind === "ok" ? activation.report.evidence[0] : void 0;
|
|
1058
|
+
hookActivation = hookActivationResult(activation.kind === "ok" ? activation.report : null);
|
|
1059
|
+
const observed = hookActivation === "approved_executed" || hookActivation === "denied_observed";
|
|
1060
|
+
check(observed, "Approval for the selected scratch file (last 10 minutes)", activation.kind !== "ok" ? activation.detail : activation.report.truncated ? "Too many recent decisions to establish a unique correlation. Start a quiet test session and retry." : activation.report.evidence.length > 1 ? `${hookActivation}: multiple decisions for this target. Use a new scratch file for each attempt; inspect hookActivationEvidence for all results.` : latest ? `${latest.result}; answer source: ${latest.answerSource ?? "unknown"}; decision ${latest.correlationId}; session ${latest.sessionId}; tool call ${latest.toolUseId}` : "Unverified: no recent runtime-tagged Write approval for this scratch file on this machine.");
|
|
1061
|
+
const guide = AGENTS[options.activation].activationGuide;
|
|
1062
|
+
console.log(` ${dim(`Real-agent check: https://pushary.com${guide}`)}`);
|
|
1063
|
+
}
|
|
1064
|
+
const managedControl = readManagedControlEvidence(apiKey, configuredManagedProviders, serverProof);
|
|
1065
|
+
console.log(` ${dim(`Phone-start and steering: ${managedControl.ready ? "local and server checks passed for this run" : "unverified; hook approvals are a separate capability"}.`)}`);
|
|
1016
1066
|
console.log();
|
|
1067
|
+
if (!options.activation) console.log(` ${dim("Agent hook activation is unverified by these checks. Use --activation claude_code --activation-target <unique scratch file> after one real approval.")}`);
|
|
1017
1068
|
const failed = results.filter((r) => !r.passed);
|
|
1018
1069
|
if (failed.length === 0) {
|
|
1019
1070
|
console.log(` ${green(bold("All checks passed."))}`);
|
|
@@ -1050,18 +1101,22 @@ var main = async () => {
|
|
|
1050
1101
|
}
|
|
1051
1102
|
}
|
|
1052
1103
|
if (options.json) {
|
|
1053
|
-
process.stdout.write(`${JSON.stringify({
|
|
1104
|
+
await new Promise((resolve2, reject) => process.stdout.write(`${JSON.stringify({
|
|
1054
1105
|
ok: exitCode === EXIT.OK,
|
|
1055
1106
|
exitCode,
|
|
1056
1107
|
keyCheck: keyVerdict?.kind ?? null,
|
|
1108
|
+
roundTripEvidence: roundTripEvidence(roundTrip),
|
|
1109
|
+
hookActivation,
|
|
1110
|
+
hookActivationEvidence: activation,
|
|
1057
1111
|
daemon,
|
|
1112
|
+
managedControl,
|
|
1058
1113
|
daemonNeedsLogin: loginBlock.kind === "blocked" ? loginBlock.record : null,
|
|
1059
1114
|
passed: results.filter((r) => r.passed).length,
|
|
1060
1115
|
failed: failed.length,
|
|
1061
1116
|
checks: results.map((r) => ({ label: r.label, passed: r.passed, detail: r.detail ?? null })),
|
|
1062
1117
|
otherHolders: otherHoldersByAgent
|
|
1063
1118
|
}, null, 2)}
|
|
1064
|
-
|
|
1119
|
+
`, (error) => error ? reject(error) : resolve2()));
|
|
1065
1120
|
}
|
|
1066
1121
|
process.exit(exitCode);
|
|
1067
1122
|
};
|
|
@@ -1,43 +1,43 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
readHookInput
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-IZVK6FKD.js";
|
|
5
5
|
import {
|
|
6
6
|
isDeferAnswer
|
|
7
7
|
} from "../chunk-YHG74UFF.js";
|
|
8
|
-
import "../chunk-
|
|
9
|
-
import "../chunk-
|
|
10
|
-
import "../chunk-
|
|
11
|
-
import "../chunk-
|
|
12
|
-
import "../chunk-
|
|
13
|
-
import "../chunk-
|
|
14
|
-
import "../chunk-
|
|
15
|
-
import "../chunk-
|
|
8
|
+
import "../chunk-HAXZ6CBF.js";
|
|
9
|
+
import "../chunk-EMGGUO2Q.js";
|
|
10
|
+
import "../chunk-WPS5IM7B.js";
|
|
11
|
+
import "../chunk-XZWUOCJZ.js";
|
|
12
|
+
import "../chunk-QJPYJVL3.js";
|
|
13
|
+
import "../chunk-GQRU4KJF.js";
|
|
14
|
+
import "../chunk-VMZ4SWQD.js";
|
|
15
|
+
import "../chunk-7RNY3UHJ.js";
|
|
16
16
|
import "../chunk-GOPIMNVC.js";
|
|
17
17
|
import "../chunk-3AJIK5AO.js";
|
|
18
|
-
import "../chunk-
|
|
18
|
+
import "../chunk-SKPXQRKN.js";
|
|
19
19
|
import "../chunk-EQRS3SGM.js";
|
|
20
|
-
import "../chunk-
|
|
20
|
+
import "../chunk-VERXHIQF.js";
|
|
21
21
|
import "../chunk-XHKBHWLX.js";
|
|
22
22
|
import "../chunk-L4ZCFMWW.js";
|
|
23
|
+
import "../chunk-XUTSZYYJ.js";
|
|
23
24
|
import {
|
|
24
25
|
askUser,
|
|
25
26
|
isDecisionStopped,
|
|
26
27
|
waitForQuestion
|
|
27
28
|
} from "../chunk-XS33QBJA.js";
|
|
28
|
-
import "../chunk-IVQMNLSA.js";
|
|
29
29
|
import {
|
|
30
30
|
getMachineId
|
|
31
|
-
} from "../chunk-
|
|
32
|
-
import "../chunk-
|
|
31
|
+
} from "../chunk-4OSQZ5TB.js";
|
|
32
|
+
import "../chunk-BGX4QUQF.js";
|
|
33
33
|
import "../chunk-GMXKITVA.js";
|
|
34
34
|
import "../chunk-DINDL2CF.js";
|
|
35
|
-
import "../chunk-
|
|
35
|
+
import "../chunk-VMQU7YEJ.js";
|
|
36
36
|
import "../chunk-EYRGBYZG.js";
|
|
37
37
|
import "../chunk-SAF6HGAA.js";
|
|
38
38
|
import "../chunk-7QLSKOSU.js";
|
|
39
39
|
import "../chunk-KZERVKTD.js";
|
|
40
|
-
import "../chunk-
|
|
40
|
+
import "../chunk-L7NOG547.js";
|
|
41
41
|
import {
|
|
42
42
|
getApiKey
|
|
43
43
|
} from "../chunk-2UMNXADU.js";
|
|
@@ -51,7 +51,7 @@ import {
|
|
|
51
51
|
planElicitationAsk,
|
|
52
52
|
readElicitationFields,
|
|
53
53
|
renderElicitationOutput
|
|
54
|
-
} from "../chunk-
|
|
54
|
+
} from "../chunk-MC5WUOJJ.js";
|
|
55
55
|
|
|
56
56
|
// bin/pushary-elicitation-hook.ts
|
|
57
57
|
import { basename } from "path";
|
|
@@ -5,19 +5,19 @@ import {
|
|
|
5
5
|
postLiveSession,
|
|
6
6
|
postLiveSessionUntilAccepted,
|
|
7
7
|
startLiveSessionHeartbeat
|
|
8
|
-
} from "../chunk-
|
|
8
|
+
} from "../chunk-FRXZMCVA.js";
|
|
9
9
|
import {
|
|
10
10
|
SESSION_IDLE_TIMEOUT_MS,
|
|
11
11
|
describeIdleWindow,
|
|
12
12
|
idleDeadlinePassed,
|
|
13
13
|
startProcessOwnership,
|
|
14
14
|
withSpawnProcessOwnership
|
|
15
|
-
} from "../chunk-
|
|
16
|
-
import "../chunk-
|
|
15
|
+
} from "../chunk-PQGLDN4V.js";
|
|
16
|
+
import "../chunk-SKPXQRKN.js";
|
|
17
17
|
import "../chunk-EQRS3SGM.js";
|
|
18
18
|
import {
|
|
19
19
|
transcriptsEnabled
|
|
20
|
-
} from "../chunk-
|
|
20
|
+
} from "../chunk-VERXHIQF.js";
|
|
21
21
|
import {
|
|
22
22
|
spawnClaude
|
|
23
23
|
} from "../chunk-XHKBHWLX.js";
|
|
@@ -26,15 +26,15 @@ import {
|
|
|
26
26
|
} from "../chunk-L4ZCFMWW.js";
|
|
27
27
|
import {
|
|
28
28
|
getMachineId
|
|
29
|
-
} from "../chunk-
|
|
30
|
-
import "../chunk-
|
|
31
|
-
import "../chunk-
|
|
32
|
-
import "../chunk-
|
|
29
|
+
} from "../chunk-4OSQZ5TB.js";
|
|
30
|
+
import "../chunk-BGX4QUQF.js";
|
|
31
|
+
import "../chunk-VMQU7YEJ.js";
|
|
32
|
+
import "../chunk-L7NOG547.js";
|
|
33
33
|
import {
|
|
34
34
|
getApiKey,
|
|
35
35
|
getBaseUrl
|
|
36
36
|
} from "../chunk-2UMNXADU.js";
|
|
37
|
-
import "../chunk-
|
|
37
|
+
import "../chunk-MC5WUOJJ.js";
|
|
38
38
|
|
|
39
39
|
// bin/pushary-gemini-bridge.ts
|
|
40
40
|
import { existsSync } from "fs";
|
|
@@ -2,27 +2,27 @@
|
|
|
2
2
|
import {
|
|
3
3
|
isGatingMoment,
|
|
4
4
|
recordKeylessMoment
|
|
5
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-5GKNAQTG.js";
|
|
6
6
|
import {
|
|
7
7
|
readHookInput
|
|
8
|
-
} from "../chunk-
|
|
8
|
+
} from "../chunk-IZVK6FKD.js";
|
|
9
9
|
import {
|
|
10
10
|
denyReasonFrom,
|
|
11
11
|
isDeferAnswer
|
|
12
12
|
} from "../chunk-YHG74UFF.js";
|
|
13
|
-
import "../chunk-
|
|
14
|
-
import "../chunk-
|
|
15
|
-
import "../chunk-
|
|
16
|
-
import "../chunk-
|
|
17
|
-
import "../chunk-
|
|
18
|
-
import "../chunk-
|
|
19
|
-
import "../chunk-
|
|
20
|
-
import "../chunk-
|
|
13
|
+
import "../chunk-HAXZ6CBF.js";
|
|
14
|
+
import "../chunk-EMGGUO2Q.js";
|
|
15
|
+
import "../chunk-WPS5IM7B.js";
|
|
16
|
+
import "../chunk-XZWUOCJZ.js";
|
|
17
|
+
import "../chunk-QJPYJVL3.js";
|
|
18
|
+
import "../chunk-GQRU4KJF.js";
|
|
19
|
+
import "../chunk-VMZ4SWQD.js";
|
|
20
|
+
import "../chunk-7RNY3UHJ.js";
|
|
21
21
|
import "../chunk-GOPIMNVC.js";
|
|
22
22
|
import "../chunk-3AJIK5AO.js";
|
|
23
|
-
import "../chunk-
|
|
23
|
+
import "../chunk-SKPXQRKN.js";
|
|
24
24
|
import "../chunk-EQRS3SGM.js";
|
|
25
|
-
import "../chunk-
|
|
25
|
+
import "../chunk-VERXHIQF.js";
|
|
26
26
|
import "../chunk-XHKBHWLX.js";
|
|
27
27
|
import "../chunk-L4ZCFMWW.js";
|
|
28
28
|
import {
|
|
@@ -38,7 +38,16 @@ import {
|
|
|
38
38
|
readLastUserPrompt,
|
|
39
39
|
repoKeyFor,
|
|
40
40
|
reportEvent
|
|
41
|
-
} from "../chunk-
|
|
41
|
+
} from "../chunk-XJQOVWBQ.js";
|
|
42
|
+
import {
|
|
43
|
+
UNNAMED_ACTION,
|
|
44
|
+
deriveActionBody,
|
|
45
|
+
deriveBlocker,
|
|
46
|
+
deriveToolTarget,
|
|
47
|
+
describeToolCall,
|
|
48
|
+
scopePathFor
|
|
49
|
+
} from "../chunk-Z4JVEL7R.js";
|
|
50
|
+
import "../chunk-XUTSZYYJ.js";
|
|
42
51
|
import {
|
|
43
52
|
DECISION_STOP_REASON,
|
|
44
53
|
askUser,
|
|
@@ -47,27 +56,18 @@ import {
|
|
|
47
56
|
sendNotification,
|
|
48
57
|
waitForQuestion
|
|
49
58
|
} from "../chunk-XS33QBJA.js";
|
|
50
|
-
import {
|
|
51
|
-
UNNAMED_ACTION,
|
|
52
|
-
deriveActionBody,
|
|
53
|
-
deriveBlocker,
|
|
54
|
-
deriveToolTarget,
|
|
55
|
-
describeToolCall,
|
|
56
|
-
scopePathFor
|
|
57
|
-
} from "../chunk-BDI2MXTT.js";
|
|
58
|
-
import "../chunk-IVQMNLSA.js";
|
|
59
59
|
import {
|
|
60
60
|
getMachineId
|
|
61
|
-
} from "../chunk-
|
|
62
|
-
import "../chunk-
|
|
61
|
+
} from "../chunk-4OSQZ5TB.js";
|
|
62
|
+
import "../chunk-BGX4QUQF.js";
|
|
63
63
|
import "../chunk-GMXKITVA.js";
|
|
64
64
|
import "../chunk-DINDL2CF.js";
|
|
65
|
-
import "../chunk-
|
|
65
|
+
import "../chunk-VMQU7YEJ.js";
|
|
66
66
|
import "../chunk-EYRGBYZG.js";
|
|
67
67
|
import "../chunk-SAF6HGAA.js";
|
|
68
68
|
import "../chunk-7QLSKOSU.js";
|
|
69
69
|
import "../chunk-KZERVKTD.js";
|
|
70
|
-
import "../chunk-
|
|
70
|
+
import "../chunk-L7NOG547.js";
|
|
71
71
|
import {
|
|
72
72
|
getApiKey
|
|
73
73
|
} from "../chunk-2UMNXADU.js";
|
|
@@ -79,7 +79,7 @@ import {
|
|
|
79
79
|
hookWaitClamped,
|
|
80
80
|
hookWaitDeadline,
|
|
81
81
|
resolveGate
|
|
82
|
-
} from "../chunk-
|
|
82
|
+
} from "../chunk-MC5WUOJJ.js";
|
|
83
83
|
|
|
84
84
|
// bin/pushary-gemini-hook.ts
|
|
85
85
|
import { basename } from "path";
|
|
@@ -115,6 +115,30 @@ var toGeminiPolicyLookup = (toolName, toolInput, cwd, mcpContext) => {
|
|
|
115
115
|
var geminiAllow = () => ({ kind: "allow" });
|
|
116
116
|
var geminiDeny = (reason) => ({ kind: "deny", reason });
|
|
117
117
|
var geminiPass = () => ({ kind: "pass" });
|
|
118
|
+
var NO_LOCAL_INPUT = "This unattended Gemini session has no local approval input. Use an interactive session or Every time.";
|
|
119
|
+
var NO_ANSWER_IN_TIME = "No answer from your phone in time, and this unattended Gemini session has no local approval input. Retry when you can answer on your phone.";
|
|
120
|
+
var HANDLED_ON_MACHINE = "You chose to handle this on your machine, but this unattended Gemini session has no local approval input. Use an interactive session.";
|
|
121
|
+
var NO_DEVICE = "No phone or other notification channel is connected to Pushary, and this unattended Gemini session has no local approval input. Connect one in Pushary, then retry.";
|
|
122
|
+
var geminiLocalHandoff = (bridge, reason = NO_LOCAL_INPUT) => geminiOwnsLifecycle(bridge) ? geminiPass() : geminiDeny(reason);
|
|
123
|
+
var geminiPushOnlyHandoff = (askStatus, bridge) => askStatus === "notified" || askStatus === "terminal" ? geminiLocalHandoff(bridge) : geminiPass();
|
|
124
|
+
var fromPhoneAnswer = (answer) => answer.value === "yes" ? geminiAllow() : geminiDeny(denyReasonFrom(answer.value, answer.note));
|
|
125
|
+
var geminiPushFirstDecision = (pushed, bridge) => {
|
|
126
|
+
if (!pushed) return geminiPass();
|
|
127
|
+
const { answer, localHandoff } = pushed;
|
|
128
|
+
if (isDecisionStopped(answer)) return geminiDeny(DECISION_STOP_REASON);
|
|
129
|
+
if (localHandoff) return geminiLocalHandoff(bridge, pushed.noDevices ? NO_DEVICE : NO_LOCAL_INPUT);
|
|
130
|
+
if (answer.answered) return isDeferAnswer(answer.value) ? geminiLocalHandoff(bridge, HANDLED_ON_MACHINE) : fromPhoneAnswer(answer);
|
|
131
|
+
return geminiLocalHandoff(bridge, NO_ANSWER_IN_TIME);
|
|
132
|
+
};
|
|
133
|
+
var geminiPushOnlyDecision = (pushed, timeoutAction, bridge) => {
|
|
134
|
+
if (!pushed) return geminiDeny("Pushary could not create a verifiable approval. Retry the action.");
|
|
135
|
+
const { answer, localHandoff, clampCut, askStatus } = pushed;
|
|
136
|
+
if (isDecisionStopped(answer)) return geminiDeny(DECISION_STOP_REASON);
|
|
137
|
+
if (localHandoff) return geminiPushOnlyHandoff(askStatus, bridge);
|
|
138
|
+
if (answer.answered) return isDeferAnswer(answer.value) ? geminiPass() : fromPhoneAnswer(answer);
|
|
139
|
+
if (clampCut) return geminiPass();
|
|
140
|
+
return geminiTimeoutDecision(timeoutAction);
|
|
141
|
+
};
|
|
118
142
|
var toGeminiWire = (decision) => {
|
|
119
143
|
if (decision.kind === "pass") return null;
|
|
120
144
|
return gateOutputFor(
|
|
@@ -150,12 +174,18 @@ var pushQuestion = async (apiKey, input, lookup, waitSeconds, mode, pollInterval
|
|
|
150
174
|
blocker: deriveBlocker(mode),
|
|
151
175
|
actionBody: deriveActionBody(input.tool_name ?? "", input.tool_input ?? {}),
|
|
152
176
|
repoKey: repoKeyFor(input.cwd)
|
|
153
|
-
})
|
|
177
|
+
});
|
|
154
178
|
const now = Date.now();
|
|
155
179
|
const deadline = hookWaitDeadline(START_MS, waitSeconds, "gemini", now);
|
|
156
180
|
const clampCut = hookWaitClamped(START_MS, waitSeconds, "gemini", now);
|
|
157
181
|
const answer = await waitForQuestion(apiKey, result, deadline, pollInterval);
|
|
158
|
-
return {
|
|
182
|
+
return {
|
|
183
|
+
answer,
|
|
184
|
+
localHandoff: !answer.answered && isLocalQuestionHandoff(result),
|
|
185
|
+
clampCut,
|
|
186
|
+
askStatus: result.status,
|
|
187
|
+
noDevices: result.noDevices === true
|
|
188
|
+
};
|
|
159
189
|
};
|
|
160
190
|
var notifyApprovalNeeded = async (apiKey, input, lookup) => {
|
|
161
191
|
try {
|
|
@@ -174,33 +204,18 @@ var handlePushOnly = async (apiKey, input, lookup, timeoutSeconds, timeoutAction
|
|
|
174
204
|
try {
|
|
175
205
|
pushed = await pushQuestion(apiKey, input, lookup, effectiveWaitSeconds(timeoutAction, timeoutSeconds, "gemini"), "push_only");
|
|
176
206
|
} catch {
|
|
177
|
-
|
|
178
|
-
}
|
|
179
|
-
const { answer, localHandoff, clampCut } = pushed;
|
|
180
|
-
if (isDecisionStopped(answer)) return geminiDeny(DECISION_STOP_REASON);
|
|
181
|
-
if (localHandoff) return geminiPass();
|
|
182
|
-
if (answer.answered) {
|
|
183
|
-
if (isDeferAnswer(answer.value)) return geminiPass();
|
|
184
|
-
return answer.value === "yes" ? geminiAllow() : geminiDeny(denyReasonFrom(answer.value, answer.note));
|
|
207
|
+
pushed = void 0;
|
|
185
208
|
}
|
|
186
|
-
|
|
187
|
-
return geminiTimeoutDecision(timeoutAction);
|
|
209
|
+
return geminiPushOnlyDecision(pushed, timeoutAction, process.env.PUSHARY_GEMINI_BRIDGE);
|
|
188
210
|
};
|
|
189
211
|
var handlePushFirst = async (apiKey, input, lookup, pushFirstSeconds) => {
|
|
190
212
|
let pushed;
|
|
191
213
|
try {
|
|
192
214
|
pushed = await pushQuestion(apiKey, input, lookup, pushFirstSeconds, "push_first", 1500);
|
|
193
215
|
} catch {
|
|
194
|
-
|
|
216
|
+
pushed = void 0;
|
|
195
217
|
}
|
|
196
|
-
|
|
197
|
-
if (isDecisionStopped(answer)) return geminiDeny(DECISION_STOP_REASON);
|
|
198
|
-
if (localHandoff) return geminiPass();
|
|
199
|
-
if (answer.answered) {
|
|
200
|
-
if (isDeferAnswer(answer.value)) return geminiPass();
|
|
201
|
-
return answer.value === "yes" ? geminiAllow() : geminiDeny(denyReasonFrom(answer.value, answer.note));
|
|
202
|
-
}
|
|
203
|
-
return geminiPass();
|
|
218
|
+
return geminiPushFirstDecision(pushed, process.env.PUSHARY_GEMINI_BRIDGE);
|
|
204
219
|
};
|
|
205
220
|
var decideBeforeTool = async (input) => {
|
|
206
221
|
let apiKey;
|
|
@@ -246,12 +261,12 @@ var decideBeforeTool = async (input) => {
|
|
|
246
261
|
case "push_only":
|
|
247
262
|
return handlePushOnly(apiKey, input, lookup, toolPolicy.timeoutSeconds, toolPolicy.timeoutAction);
|
|
248
263
|
case "terminal_only":
|
|
249
|
-
return
|
|
264
|
+
return geminiLocalHandoff(process.env.PUSHARY_GEMINI_BRIDGE);
|
|
250
265
|
case "push_first":
|
|
251
266
|
return handlePushFirst(apiKey, input, lookup, toolPolicy.pushFirstSeconds);
|
|
252
267
|
case "notify_only":
|
|
253
268
|
await notifyApprovalNeeded(apiKey, input, lookup);
|
|
254
|
-
return
|
|
269
|
+
return geminiLocalHandoff(process.env.PUSHARY_GEMINI_BRIDGE);
|
|
255
270
|
default:
|
|
256
271
|
return handlePushFirst(apiKey, input, lookup, toolPolicy.pushFirstSeconds);
|
|
257
272
|
}
|