@pushary/agent-hooks 0.85.2 → 0.87.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 +17 -0
- package/dist/bin/pushary-bell-hook.js +4 -4
- package/dist/bin/pushary-bell.js +6 -6
- package/dist/bin/pushary-claude.js +12 -10
- package/dist/bin/pushary-clean.js +10 -9
- package/dist/bin/pushary-codex-bridge.js +5 -5
- package/dist/bin/pushary-codex-hook.js +13 -29
- package/dist/bin/pushary-codex.js +6 -4
- package/dist/bin/pushary-connect.js +6 -6
- package/dist/bin/pushary-daemon.js +4 -4
- package/dist/bin/pushary-disconnect.js +25 -10
- package/dist/bin/pushary-doctor.js +21 -15
- package/dist/bin/pushary-elicitation-hook.d.ts +1 -0
- package/dist/bin/pushary-elicitation-hook.js +210 -0
- package/dist/bin/pushary-gemini-bridge.js +5 -5
- package/dist/bin/pushary-gemini-hook.js +10 -8
- package/dist/bin/pushary-hook.js +7 -8
- package/dist/bin/pushary-login.js +4 -4
- package/dist/bin/pushary-logout.js +4 -4
- package/dist/bin/pushary-mode.js +3 -3
- package/dist/bin/pushary-notification-hook.js +4 -3
- package/dist/bin/pushary-permission-denied-hook.js +7 -8
- package/dist/bin/pushary-permission-hook.js +7 -8
- package/dist/bin/pushary-post-hook.js +4 -3
- package/dist/bin/pushary-prompt-hook.js +4 -3
- package/dist/bin/pushary-session-end-hook.js +4 -3
- package/dist/bin/pushary-session-start-hook.js +4 -3
- package/dist/bin/pushary-setup.js +26 -21
- package/dist/bin/pushary-stats.js +2 -2
- package/dist/bin/pushary-status.js +5 -5
- package/dist/bin/pushary-stop-hook.js +4 -3
- package/dist/bin/pushary-stopfailure-hook.js +4 -3
- package/dist/bin/pushary-upgrade.js +13 -11
- package/dist/bin/pushary-wait.js +3 -3
- package/dist/{chunk-GM74DUMS.js → chunk-2T3GX7WG.js} +1 -1
- package/dist/{chunk-XJDL3MUE.js → chunk-5646VKR4.js} +18 -20
- package/dist/{chunk-PKUNB3SN.js → chunk-7MBDFNUC.js} +1 -1
- package/dist/{chunk-K337LIBD.js → chunk-7YJS2VTG.js} +36 -4
- package/dist/{chunk-LCA2EQTK.js → chunk-A3PFIWWN.js} +1 -1
- package/dist/{chunk-LSB6PFLN.js → chunk-A4I5JGCW.js} +1 -1
- package/dist/chunk-ATBKJNWS.js +19 -0
- package/dist/{chunk-KJW6NKF7.js → chunk-B3V2H6SA.js} +39 -15
- package/dist/{chunk-T54OT7IY.js → chunk-ECEUMWBH.js} +1 -1
- package/dist/{chunk-Q4URN3E7.js → chunk-HAUTLCMA.js} +1 -1
- package/dist/{chunk-Y7E2MC3G.js → chunk-HJ44HIUW.js} +60 -31
- package/dist/{chunk-4U2YRHNF.js → chunk-IWL5LWIE.js} +2 -2
- package/dist/chunk-J3YDT4HM.js +128 -0
- package/dist/{chunk-NTIUQB3E.js → chunk-JAICP7BA.js} +1 -1
- package/dist/{chunk-HSGEEBBL.js → chunk-M7RYIZX6.js} +3 -3
- package/dist/{chunk-KZIGJLOX.js → chunk-OT4YD54Q.js} +1 -1
- package/dist/{chunk-LXWISFF3.js → chunk-QBWYNVKY.js} +1 -1
- package/dist/{chunk-4YMWLMAP.js → chunk-SJVLFIYP.js} +1 -1
- package/dist/{chunk-DPDUB4XF.js → chunk-V2XPF77V.js} +104 -163
- package/dist/{chunk-JBLRZGTT.js → chunk-WLLBFDVY.js} +1 -1
- package/dist/{chunk-JT5NQQCE.js → chunk-XW5CHAQX.js} +2 -2
- package/dist/{chunk-CWV4CMB6.js → chunk-Y3UTTFTZ.js} +1 -1
- package/dist/{chunk-GY3I353G.js → chunk-ZZE6FN7T.js} +7 -3
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.js +11 -11
- package/package.json +3 -1
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
|
-
PRETOOLUSE_HANDLED_TOOLS
|
|
3
|
-
|
|
2
|
+
PRETOOLUSE_HANDLED_TOOLS,
|
|
3
|
+
PRETOOLUSE_PHONE_ANSWERED_TOOLS
|
|
4
|
+
} from "./chunk-ATBKJNWS.js";
|
|
4
5
|
import {
|
|
5
6
|
isGatingMoment,
|
|
6
7
|
recordKeylessMoment
|
|
7
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-7MBDFNUC.js";
|
|
8
9
|
import {
|
|
9
10
|
denyReasonFrom,
|
|
10
11
|
isDeferAnswer
|
|
11
12
|
} from "./chunk-YHG74UFF.js";
|
|
12
13
|
import {
|
|
13
|
-
DEFAULT_SESSION,
|
|
14
|
-
askUser,
|
|
15
|
-
cancelQuestion,
|
|
16
14
|
deriveAction,
|
|
17
15
|
deriveActionBody,
|
|
18
16
|
deriveBlocker,
|
|
@@ -23,12 +21,17 @@ import {
|
|
|
23
21
|
readLastPrompt,
|
|
24
22
|
readLastUserPrompt,
|
|
25
23
|
repoKeyFor,
|
|
26
|
-
savePendingQuestion,
|
|
27
24
|
scopePathFor,
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
25
|
+
throttlePass
|
|
26
|
+
} from "./chunk-V2XPF77V.js";
|
|
27
|
+
import {
|
|
28
|
+
DEFAULT_SESSION,
|
|
29
|
+
askUser,
|
|
30
|
+
cancelQuestion,
|
|
31
|
+
pollForAnswer,
|
|
32
|
+
savePendingQuestion,
|
|
33
|
+
sendNotification
|
|
34
|
+
} from "./chunk-J3YDT4HM.js";
|
|
32
35
|
import {
|
|
33
36
|
getMachineId
|
|
34
37
|
} from "./chunk-RN3NOEJF.js";
|
|
@@ -45,7 +48,7 @@ import {
|
|
|
45
48
|
hookWaitDeadline,
|
|
46
49
|
parseAgentQuestionAnswers,
|
|
47
50
|
resolveGate
|
|
48
|
-
} from "./chunk-
|
|
51
|
+
} from "./chunk-7YJS2VTG.js";
|
|
49
52
|
|
|
50
53
|
// src/decision-episode.ts
|
|
51
54
|
var EPISODE_PATH = "/api/agent/decision-episode";
|
|
@@ -73,29 +76,11 @@ var reportDecisionEpisode = async (apiKey, input) => {
|
|
|
73
76
|
import { basename, join } from "path";
|
|
74
77
|
import { tmpdir } from "os";
|
|
75
78
|
import { existsSync, writeFileSync } from "fs";
|
|
76
|
-
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
77
79
|
var START_MS = Date.now();
|
|
78
80
|
var allow = () => claudePreToolUseOutput("allow");
|
|
79
81
|
var deny = (reason) => claudePreToolUseOutput("deny", reason);
|
|
80
82
|
var allowWithInput = (updatedInput) => claudePreToolUseOutput("allow", void 0, updatedInput);
|
|
81
83
|
var ask = (reason) => claudePreToolUseOutput("ask", reason);
|
|
82
|
-
var pollForAnswer = async (apiKey, correlationId, deadlineMs, pollInterval = 2e3) => {
|
|
83
|
-
while (Date.now() < deadlineMs) {
|
|
84
|
-
const remaining = Math.min(Math.max(deadlineMs - Date.now(), 1e3), 3e4);
|
|
85
|
-
let answer;
|
|
86
|
-
try {
|
|
87
|
-
answer = await waitForAnswer(apiKey, correlationId, remaining);
|
|
88
|
-
} catch {
|
|
89
|
-
if (Date.now() + pollInterval >= deadlineMs) break;
|
|
90
|
-
await sleep(pollInterval);
|
|
91
|
-
continue;
|
|
92
|
-
}
|
|
93
|
-
if (answer.answered) return answer;
|
|
94
|
-
if (Date.now() + pollInterval >= deadlineMs) break;
|
|
95
|
-
await sleep(pollInterval);
|
|
96
|
-
}
|
|
97
|
-
return { answered: false };
|
|
98
|
-
};
|
|
99
84
|
var handlePushOnly = async (apiKey, description, projectName, timeoutSeconds, timeoutAction, sessionId, machineId, toolName, toolTarget, decision, signals) => {
|
|
100
85
|
let result;
|
|
101
86
|
try {
|
|
@@ -448,6 +433,47 @@ var handleAskUserQuestion = async (apiKey, input) => {
|
|
|
448
433
|
if (!answers) return void 0;
|
|
449
434
|
return allowWithInput({ ...input.tool_input ?? {}, answers });
|
|
450
435
|
};
|
|
436
|
+
var PLAN_APPROVE = "Approve, start coding";
|
|
437
|
+
var PLAN_KEEP_PLANNING = "Keep planning";
|
|
438
|
+
var handleExitPlanMode = async (apiKey, input) => {
|
|
439
|
+
const toolInput = input.tool_input ?? {};
|
|
440
|
+
const projectName = basename(input.cwd ?? process.cwd());
|
|
441
|
+
let result;
|
|
442
|
+
try {
|
|
443
|
+
result = await askUser(apiKey, {
|
|
444
|
+
question: `Approve the plan and start coding in ${projectName}?`,
|
|
445
|
+
type: "select",
|
|
446
|
+
options: [PLAN_APPROVE, PLAN_KEEP_PLANNING],
|
|
447
|
+
context: `Your agent finished planning in ${projectName} and is waiting to start.`,
|
|
448
|
+
agentName: `Claude Code - ${projectName}`,
|
|
449
|
+
sessionId: input.session_id,
|
|
450
|
+
machineId: getMachineId(),
|
|
451
|
+
toolName: "ExitPlanMode",
|
|
452
|
+
action: deriveAction("ExitPlanMode", toolInput),
|
|
453
|
+
// The plan markdown itself, through the same redaction and cap as any diff,
|
|
454
|
+
// so approving from a lock screen is a real decision and not a blind yes.
|
|
455
|
+
actionBody: deriveActionBody("ExitPlanMode", toolInput),
|
|
456
|
+
intent: readLastPrompt(input.session_id ?? DEFAULT_SESSION) ?? (input.transcript_path ? readLastUserPrompt(input.transcript_path) : void 0),
|
|
457
|
+
blocker: "Waiting for you to approve the plan"
|
|
458
|
+
});
|
|
459
|
+
} catch {
|
|
460
|
+
return void 0;
|
|
461
|
+
}
|
|
462
|
+
if (result.suppressed || result.noDevices) {
|
|
463
|
+
await cancelQuestion(apiKey, result.correlationId).catch(() => {
|
|
464
|
+
});
|
|
465
|
+
return void 0;
|
|
466
|
+
}
|
|
467
|
+
const deadline = hookWaitDeadline(START_MS, effectiveWaitSeconds("wait", 0, "claude"), "claude", Date.now());
|
|
468
|
+
const answer = await pollForAnswer(apiKey, result.correlationId, deadline);
|
|
469
|
+
const value = answer.answered ? (answer.value ?? "").trim() : "";
|
|
470
|
+
if (!value || isDeferAnswer(answer.value)) {
|
|
471
|
+
savePendingQuestion(input.session_id || DEFAULT_SESSION, result.correlationId);
|
|
472
|
+
return void 0;
|
|
473
|
+
}
|
|
474
|
+
if (value === PLAN_APPROVE) return allow();
|
|
475
|
+
return deny("Plan declined from the phone. Ask what to change before coding.");
|
|
476
|
+
};
|
|
451
477
|
var handleKeyless = (input) => {
|
|
452
478
|
try {
|
|
453
479
|
if (isGatingMoment(input.tool_name, input.tool_input ?? {})) {
|
|
@@ -493,6 +519,9 @@ var handlePreToolUse = async (input) => {
|
|
|
493
519
|
return handleAskUserQuestion(apiKey, input);
|
|
494
520
|
}
|
|
495
521
|
if (verdict.kind === "deny") return deny(verdict.reason);
|
|
522
|
+
if (input.tool_name === "ExitPlanMode") {
|
|
523
|
+
return handleExitPlanMode(apiKey, input);
|
|
524
|
+
}
|
|
496
525
|
if (shouldDeferToNativeMode(input.permission_mode, input.tool_name)) {
|
|
497
526
|
return void 0;
|
|
498
527
|
}
|
|
@@ -524,7 +553,7 @@ var toPermissionRequestOutput = (hookOutput) => {
|
|
|
524
553
|
};
|
|
525
554
|
var handlePermissionRequest = async (input) => {
|
|
526
555
|
if (PRETOOLUSE_HANDLED_TOOLS.has(input.tool_name)) {
|
|
527
|
-
const reclaimDeferredExec = input.tool_name
|
|
556
|
+
const reclaimDeferredExec = !PRETOOLUSE_PHONE_ANSWERED_TOOLS.includes(input.tool_name) && shouldDeferToNativeMode(input.permission_mode, input.tool_name);
|
|
528
557
|
if (!reclaimDeferredExec) return void 0;
|
|
529
558
|
}
|
|
530
559
|
if (input.tool_name.startsWith("mcp__pushary__")) return void 0;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
codexHomeFrom
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-B3V2H6SA.js";
|
|
4
4
|
|
|
5
5
|
// src/vscode-config.ts
|
|
6
6
|
import { homedir } from "os";
|
|
@@ -253,7 +253,7 @@ You have Pushary MCP tools that reach the user on their phone. Use them proactiv
|
|
|
253
253
|
|
|
254
254
|
- When you need the user to make a decision or answer a clarifying question, call \`ask_user\` instead of guessing or stalling. Use type "confirm" for yes/no, "select" for a fixed set of options, and "input" for free text. \`ask_user\` blocks until the user replies or it times out, so you do not need a separate wait step.
|
|
255
255
|
- Before any risky or irreversible step (deleting files, force pushing, spending money, sending external messages), call \`ask_user\` with type "confirm" and wait for approval.
|
|
256
|
-
- Before starting a multi-step run that will change several files, call \`propose_scope\` once with the paths you expect to change, anything you promise not to touch, and what done means. The user agrees to the boundary in one tap,
|
|
256
|
+
- Before starting a multi-step run that will change several files, call \`propose_scope\` once with the paths you expect to change, anything you promise not to touch, and what done means. The user agrees to the boundary in one tap. A ratified boundary NARROWS what may proceed without asking, it does not pre-approve work inside it, so an action that would normally need approval still needs it. Skip it for a single quick edit, and do not propose a new scope mid-run just to widen one: if you need to go outside the agreed scope, do the work and let the approval that follows widen it.
|
|
257
257
|
- When you are blocked, stuck, or hit an error you cannot resolve on your own, call \`send_notification\` so the user knows, and call \`ask_user\` if you need a decision to continue.
|
|
258
258
|
- When a task that took several steps finishes, call \`send_notification\` so the user knows it is done. Always pass \`context\` with a \`type\` of "task_complete", "error" or "info": it is what tells Pushary this is a task update, so the user's own setting for where updates land can route it.
|
|
259
259
|
- Use \`cancel_question\` to retract a pending question once it is no longer needed.
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import {
|
|
2
|
+
callMcpTool
|
|
3
|
+
} from "./chunk-BSZYIAZL.js";
|
|
4
|
+
import {
|
|
5
|
+
installProxyDispatcher
|
|
6
|
+
} from "./chunk-SAF6HGAA.js";
|
|
7
|
+
|
|
8
|
+
// src/api.ts
|
|
9
|
+
import { randomUUID } from "crypto";
|
|
10
|
+
|
|
11
|
+
// src/validate.ts
|
|
12
|
+
var isPolicyConfig = (data) => {
|
|
13
|
+
if (!data || typeof data !== "object") return false;
|
|
14
|
+
const d = data;
|
|
15
|
+
return Array.isArray(d.policies) && typeof d.defaultTimeoutSeconds === "number" && typeof d.defaultTimeoutAction === "string";
|
|
16
|
+
};
|
|
17
|
+
var isAskUserResponse = (data) => {
|
|
18
|
+
if (!data || typeof data !== "object") return false;
|
|
19
|
+
const d = data;
|
|
20
|
+
return typeof d.correlationId === "string" && typeof d.status === "string";
|
|
21
|
+
};
|
|
22
|
+
var isWaitForAnswerResponse = (data) => {
|
|
23
|
+
if (!data || typeof data !== "object") return false;
|
|
24
|
+
const d = data;
|
|
25
|
+
return typeof d.answered === "boolean";
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// src/api.ts
|
|
29
|
+
installProxyDispatcher();
|
|
30
|
+
var askUser = async (apiKey, params, timeoutMs = 15e3) => {
|
|
31
|
+
const identified = params.toolUseId || params.requestId ? params : { ...params, requestId: randomUUID() };
|
|
32
|
+
const result = await callMcpTool(apiKey, "ask_user", { ...identified, wait: false }, { maxRetries: 3, timeoutMs });
|
|
33
|
+
if (!isAskUserResponse(result)) throw new Error("Invalid ask_user response");
|
|
34
|
+
return result;
|
|
35
|
+
};
|
|
36
|
+
var waitForAnswer = async (apiKey, correlationId, timeoutMs = 3e4) => {
|
|
37
|
+
const result = await callMcpTool(apiKey, "wait_for_answer", {
|
|
38
|
+
correlationId,
|
|
39
|
+
timeoutMs
|
|
40
|
+
}, { timeoutMs: timeoutMs + 1e4 });
|
|
41
|
+
if (!isWaitForAnswerResponse(result)) throw new Error("Invalid wait_for_answer response");
|
|
42
|
+
return result;
|
|
43
|
+
};
|
|
44
|
+
var pollForAnswer = async (apiKey, correlationId, deadlineMs, pollInterval = 2e3) => {
|
|
45
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
46
|
+
while (Date.now() < deadlineMs) {
|
|
47
|
+
const remaining = Math.min(Math.max(deadlineMs - Date.now(), 1e3), 3e4);
|
|
48
|
+
let answer;
|
|
49
|
+
try {
|
|
50
|
+
answer = await waitForAnswer(apiKey, correlationId, remaining);
|
|
51
|
+
} catch {
|
|
52
|
+
if (Date.now() + pollInterval >= deadlineMs) break;
|
|
53
|
+
await sleep(pollInterval);
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (answer.answered) return answer;
|
|
57
|
+
if (Date.now() + pollInterval >= deadlineMs) break;
|
|
58
|
+
await sleep(pollInterval);
|
|
59
|
+
}
|
|
60
|
+
return { answered: false };
|
|
61
|
+
};
|
|
62
|
+
var cancelQuestion = async (apiKey, correlationId) => {
|
|
63
|
+
await callMcpTool(apiKey, "cancel_question", { correlationId });
|
|
64
|
+
};
|
|
65
|
+
var sendNotification = async (apiKey, params, timeoutMs = 15e3) => {
|
|
66
|
+
await callMcpTool(apiKey, "send_notification", { ...params }, { maxRetries: 3, timeoutMs });
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// src/pending.ts
|
|
70
|
+
import { join } from "path";
|
|
71
|
+
import { tmpdir } from "os";
|
|
72
|
+
import { existsSync, mkdirSync, writeFileSync, readdirSync, unlinkSync, rmSync, statSync } from "fs";
|
|
73
|
+
var PENDING_DIR = join(tmpdir(), "pushary-pending");
|
|
74
|
+
var DEFAULT_SESSION = "_no_session";
|
|
75
|
+
var GRACE_MS = 10 * 60 * 1e3;
|
|
76
|
+
var sanitize = (sessionId) => sessionId.replace(/[^A-Za-z0-9_-]/g, "_").slice(0, 128) || DEFAULT_SESSION;
|
|
77
|
+
var dirFor = (sessionId) => join(PENDING_DIR, sanitize(sessionId));
|
|
78
|
+
var isDefaultSession = (sessionId) => sanitize(sessionId) === DEFAULT_SESSION;
|
|
79
|
+
var savePendingQuestion = (sessionId, correlationId) => {
|
|
80
|
+
const dir = dirFor(sessionId);
|
|
81
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
82
|
+
writeFileSync(join(dir, correlationId), "", "utf-8");
|
|
83
|
+
};
|
|
84
|
+
var listPendingQuestions = (sessionId) => {
|
|
85
|
+
const dir = dirFor(sessionId);
|
|
86
|
+
let files;
|
|
87
|
+
try {
|
|
88
|
+
files = readdirSync(dir);
|
|
89
|
+
} catch {
|
|
90
|
+
return [];
|
|
91
|
+
}
|
|
92
|
+
if (!isDefaultSession(sessionId)) return files;
|
|
93
|
+
const cutoff = Date.now() - GRACE_MS;
|
|
94
|
+
return files.filter((name) => {
|
|
95
|
+
try {
|
|
96
|
+
return statSync(join(dir, name)).mtimeMs < cutoff;
|
|
97
|
+
} catch {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
var removePendingQuestion = (sessionId, correlationId) => {
|
|
103
|
+
try {
|
|
104
|
+
unlinkSync(join(dirFor(sessionId), correlationId));
|
|
105
|
+
} catch {
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
var removePendingSession = (sessionId) => {
|
|
109
|
+
try {
|
|
110
|
+
rmSync(dirFor(sessionId), { recursive: true, force: true });
|
|
111
|
+
} catch {
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export {
|
|
116
|
+
isPolicyConfig,
|
|
117
|
+
askUser,
|
|
118
|
+
waitForAnswer,
|
|
119
|
+
pollForAnswer,
|
|
120
|
+
cancelQuestion,
|
|
121
|
+
sendNotification,
|
|
122
|
+
DEFAULT_SESSION,
|
|
123
|
+
isDefaultSession,
|
|
124
|
+
savePendingQuestion,
|
|
125
|
+
listPendingQuestions,
|
|
126
|
+
removePendingQuestion,
|
|
127
|
+
removePendingSession
|
|
128
|
+
};
|
|
@@ -7,9 +7,9 @@ var HOOK_SHELL = "/bin/sh -c";
|
|
|
7
7
|
var LOCATE = 'B=$(command -v "$0") || exit 0; [ -f "$B" ] || exit 0; [ -x "$B" ] || exit 0';
|
|
8
8
|
var NODE_PRESENT = "command -v node >/dev/null 2>&1 || exit 0";
|
|
9
9
|
var HOOK_GUARD_SCRIPTS = {
|
|
10
|
-
native_binary: `${LOCATE}; exec "$B"`,
|
|
11
|
-
node_binary: `${LOCATE}; ${NODE_PRESENT}; exec "$B"`,
|
|
12
|
-
node_script: `[ -f "$0" ] || exit 0; ${NODE_PRESENT}; exec node "$0"`
|
|
10
|
+
native_binary: `${LOCATE}; exec "$B" "$@"`,
|
|
11
|
+
node_binary: `${LOCATE}; ${NODE_PRESENT}; exec "$B" "$@"`,
|
|
12
|
+
node_script: `[ -f "$0" ] || exit 0; ${NODE_PRESENT}; exec node "$0" "$@"`
|
|
13
13
|
};
|
|
14
14
|
var BINARY_GUARDS = {
|
|
15
15
|
native: "native_binary",
|