borgmcp 1.0.6 → 1.0.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/dist/assimilate-cmd.js +39 -511
- package/dist/assimilate-deps.js +3 -177
- package/dist/assimilate-welcome.js +2 -24
- package/dist/auth-env.js +1 -107
- package/dist/auth.js +23 -612
- package/dist/claude.js +11 -281
- package/dist/cli-help.js +29 -50
- package/dist/cli-platform.js +4 -94
- package/dist/codex-app-server.js +4 -228
- package/dist/codex-app-wake.js +2 -122
- package/dist/codex-launch.js +1 -81
- package/dist/codex-remote.js +1 -250
- package/dist/config-utils.js +3 -385
- package/dist/config.js +1 -190
- package/dist/console-prefix.js +1 -86
- package/dist/cube-name.js +1 -65
- package/dist/cubes.js +4 -269
- package/dist/debug.js +1 -71
- package/dist/device-auth.js +1 -167
- package/dist/direct-log.js +1 -11
- package/dist/health-beat.js +1 -168
- package/dist/inbox-monitor.js +1 -129
- package/dist/index.js +26 -1378
- package/dist/lifecycle-log-guard.js +2 -93
- package/dist/list-roles-render.js +6 -39
- package/dist/log-audit.js +3 -186
- package/dist/log-stream.js +9 -848
- package/dist/name-validator.js +1 -22
- package/dist/parse-assimilate-args.js +1 -82
- package/dist/postinstall.js +8 -22
- package/dist/regen-format.js +11 -337
- package/dist/regen.js +5 -83
- package/dist/remote-client.js +1 -695
- package/dist/role-resolver.js +1 -36
- package/dist/role-section.js +8 -208
- package/dist/roster-render.js +3 -96
- package/dist/setup.js +36 -251
- package/dist/shell-escape.js +1 -22
- package/dist/spawn.js +10 -29
- package/dist/stale-version-check.js +1 -102
- package/dist/stream-owner.js +2 -202
- package/dist/stream-status.js +3 -211
- package/dist/subscription-retry.js +1 -23
- package/dist/sync-roles-render.js +3 -118
- package/dist/sync.js +22 -286
- package/dist/templates.js +120 -626
- package/dist/terminal-title.js +1 -68
- package/dist/token-crypto.js +1 -91
- package/dist/token-store.js +1 -222
- package/dist/types.js +0 -5
- package/dist/version.js +2 -78
- package/dist/worktree-lifecycle.js +2 -173
- package/package.json +11 -2
- package/dist/assimilate-cmd.d.ts.map +0 -1
- package/dist/assimilate-cmd.js.map +0 -1
- package/dist/assimilate-deps.d.ts.map +0 -1
- package/dist/assimilate-deps.js.map +0 -1
- package/dist/assimilate-welcome.d.ts.map +0 -1
- package/dist/assimilate-welcome.js.map +0 -1
- package/dist/auth-env.d.ts.map +0 -1
- package/dist/auth-env.js.map +0 -1
- package/dist/auth.d.ts.map +0 -1
- package/dist/auth.js.map +0 -1
- package/dist/claude.d.ts.map +0 -1
- package/dist/claude.js.map +0 -1
- package/dist/cli-help.d.ts.map +0 -1
- package/dist/cli-help.js.map +0 -1
- package/dist/cli-platform.d.ts.map +0 -1
- package/dist/cli-platform.js.map +0 -1
- package/dist/codex-app-server.d.ts.map +0 -1
- package/dist/codex-app-server.js.map +0 -1
- package/dist/codex-app-wake.d.ts.map +0 -1
- package/dist/codex-app-wake.js.map +0 -1
- package/dist/codex-launch.d.ts.map +0 -1
- package/dist/codex-launch.js.map +0 -1
- package/dist/codex-remote.d.ts.map +0 -1
- package/dist/codex-remote.js.map +0 -1
- package/dist/config-utils.d.ts.map +0 -1
- package/dist/config-utils.js.map +0 -1
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js.map +0 -1
- package/dist/console-prefix.d.ts.map +0 -1
- package/dist/console-prefix.js.map +0 -1
- package/dist/cube-name.d.ts.map +0 -1
- package/dist/cube-name.js.map +0 -1
- package/dist/cubes.d.ts.map +0 -1
- package/dist/cubes.js.map +0 -1
- package/dist/debug.d.ts.map +0 -1
- package/dist/debug.js.map +0 -1
- package/dist/device-auth.d.ts.map +0 -1
- package/dist/device-auth.js.map +0 -1
- package/dist/direct-log.d.ts.map +0 -1
- package/dist/direct-log.js.map +0 -1
- package/dist/health-beat.d.ts.map +0 -1
- package/dist/health-beat.js.map +0 -1
- package/dist/inbox-monitor.d.ts.map +0 -1
- package/dist/inbox-monitor.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/lifecycle-log-guard.d.ts.map +0 -1
- package/dist/lifecycle-log-guard.js.map +0 -1
- package/dist/list-roles-render.d.ts.map +0 -1
- package/dist/list-roles-render.js.map +0 -1
- package/dist/log-audit.d.ts.map +0 -1
- package/dist/log-audit.js.map +0 -1
- package/dist/log-stream.d.ts.map +0 -1
- package/dist/log-stream.js.map +0 -1
- package/dist/name-validator.d.ts.map +0 -1
- package/dist/name-validator.js.map +0 -1
- package/dist/parse-assimilate-args.d.ts.map +0 -1
- package/dist/parse-assimilate-args.js.map +0 -1
- package/dist/postinstall.d.ts.map +0 -1
- package/dist/postinstall.js.map +0 -1
- package/dist/regen-format.d.ts.map +0 -1
- package/dist/regen-format.js.map +0 -1
- package/dist/regen.d.ts.map +0 -1
- package/dist/regen.js.map +0 -1
- package/dist/remote-client.d.ts.map +0 -1
- package/dist/remote-client.js.map +0 -1
- package/dist/role-resolver.d.ts.map +0 -1
- package/dist/role-resolver.js.map +0 -1
- package/dist/role-section.d.ts.map +0 -1
- package/dist/role-section.js.map +0 -1
- package/dist/roster-render.d.ts.map +0 -1
- package/dist/roster-render.js.map +0 -1
- package/dist/setup.d.ts.map +0 -1
- package/dist/setup.js.map +0 -1
- package/dist/shell-escape.d.ts.map +0 -1
- package/dist/shell-escape.js.map +0 -1
- package/dist/spawn.d.ts.map +0 -1
- package/dist/spawn.js.map +0 -1
- package/dist/stale-version-check.d.ts.map +0 -1
- package/dist/stale-version-check.js.map +0 -1
- package/dist/stream-owner.d.ts.map +0 -1
- package/dist/stream-owner.js.map +0 -1
- package/dist/stream-status.d.ts.map +0 -1
- package/dist/stream-status.js.map +0 -1
- package/dist/subscription-retry.d.ts.map +0 -1
- package/dist/subscription-retry.js.map +0 -1
- package/dist/sync-roles-render.d.ts.map +0 -1
- package/dist/sync-roles-render.js.map +0 -1
- package/dist/sync.d.ts.map +0 -1
- package/dist/sync.js.map +0 -1
- package/dist/templates.d.ts.map +0 -1
- package/dist/templates.js.map +0 -1
- package/dist/terminal-title.d.ts.map +0 -1
- package/dist/terminal-title.js.map +0 -1
- package/dist/token-crypto.d.ts.map +0 -1
- package/dist/token-crypto.js.map +0 -1
- package/dist/token-store.d.ts.map +0 -1
- package/dist/token-store.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/version.d.ts.map +0 -1
- package/dist/version.js.map +0 -1
- package/dist/worktree-lifecycle.d.ts.map +0 -1
- package/dist/worktree-lifecycle.js.map +0 -1
package/dist/debug.js
CHANGED
|
@@ -1,71 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* Opt-in debug logging for the borg CLI (Queen observability ask — surface
|
|
3
|
-
* exactly what failed on errors like the cross-account assimilate 404).
|
|
4
|
-
*
|
|
5
|
-
* Enabled by `--debug` on the command line OR a truthy `BORG_DEBUG` env var,
|
|
6
|
-
* wired at the CLI entry points (top-level `borg` dispatcher + `borg setup`
|
|
7
|
-
* + `borg assimilate`). When on, `authedFetch` (remote-client.ts) emits one
|
|
8
|
-
* line per HTTP request + the server error body on failure.
|
|
9
|
-
*
|
|
10
|
-
* Output goes to STDERR with a `[borg:debug]` prefix so it never contaminates
|
|
11
|
-
* STDOUT (the MCP / tool output stream a drone session proxies).
|
|
12
|
-
*
|
|
13
|
-
* HARD INVARIANT: debug output must NEVER contain token material — no
|
|
14
|
-
* Authorization header, id_token, or refresh_token. Call sites log only
|
|
15
|
-
* method / path / status / server-error-body; the Bearer token is omitted.
|
|
16
|
-
*/
|
|
17
|
-
let debugEnabled = false;
|
|
18
|
-
/** Enable or disable debug logging for the lifetime of the process. */
|
|
19
|
-
export function setDebug(on) {
|
|
20
|
-
debugEnabled = on;
|
|
21
|
-
}
|
|
22
|
-
/** Whether debug logging is currently enabled. */
|
|
23
|
-
export function isDebug() {
|
|
24
|
-
return debugEnabled;
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Emit a debug line to STDERR with the `[borg:debug]` prefix. No-op when
|
|
28
|
-
* debug is disabled, so call sites need not guard. STDOUT is left clean for
|
|
29
|
-
* MCP / tool output.
|
|
30
|
-
*/
|
|
31
|
-
export function debugLog(...args) {
|
|
32
|
-
if (!debugEnabled)
|
|
33
|
-
return;
|
|
34
|
-
if (args.length > 0 && typeof args[0] === 'string') {
|
|
35
|
-
console.error(`[borg:debug] ${args[0]}`, ...args.slice(1));
|
|
36
|
-
}
|
|
37
|
-
else {
|
|
38
|
-
console.error('[borg:debug]', ...args);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Resolve debug state from a process argv array + `BORG_DEBUG` at a CLI entry
|
|
43
|
-
* point. Enables debug when `--debug` is present OR `BORG_DEBUG` is truthy,
|
|
44
|
-
* then STRIPS every `--debug` token from the array IN PLACE so downstream
|
|
45
|
-
* subcommand parsers (which reject unknown flags) never see it. Idempotent —
|
|
46
|
-
* safe to call from more than one entry point in the same process.
|
|
47
|
-
*/
|
|
48
|
-
export function initDebugFromArgv(argv) {
|
|
49
|
-
const hasFlag = argv.includes('--debug');
|
|
50
|
-
if (hasFlag || isTruthyEnv(process.env.BORG_DEBUG)) {
|
|
51
|
-
setDebug(true);
|
|
52
|
-
}
|
|
53
|
-
if (hasFlag) {
|
|
54
|
-
for (let i = argv.length - 1; i >= 0; i -= 1) {
|
|
55
|
-
if (argv[i] === '--debug')
|
|
56
|
-
argv.splice(i, 1);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
/** A non-empty env value other than the common falsy spellings is truthy. */
|
|
61
|
-
function isTruthyEnv(value) {
|
|
62
|
-
if (!value)
|
|
63
|
-
return false;
|
|
64
|
-
const v = value.trim().toLowerCase();
|
|
65
|
-
return v !== '' && v !== '0' && v !== 'false' && v !== 'no' && v !== 'off';
|
|
66
|
-
}
|
|
67
|
-
/** Test hook: reset module-level debug state between tests. */
|
|
68
|
-
export function _resetDebugForTests() {
|
|
69
|
-
debugEnabled = false;
|
|
70
|
-
}
|
|
71
|
-
//# sourceMappingURL=debug.js.map
|
|
1
|
+
let n=!1;function r(e){n=e}function u(){return n}function f(...e){n&&(e.length>0&&typeof e[0]=="string"?console.error(`[borg:debug] ${e[0]}`,...e.slice(1)):console.error("[borg:debug]",...e))}function s(e){const o=e.includes("--debug");if((o||i(process.env.BORG_DEBUG))&&r(!0),o)for(let t=e.length-1;t>=0;t-=1)e[t]==="--debug"&&e.splice(t,1)}function i(e){if(!e)return!1;const o=e.trim().toLowerCase();return o!==""&&o!=="0"&&o!=="false"&&o!=="no"&&o!=="off"}function l(){n=!1}export{l as _resetDebugForTests,f as debugLog,s as initDebugFromArgv,u as isDebug,r as setDebug};
|
package/dist/device-auth.js
CHANGED
|
@@ -1,167 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* gh#557 — Google OAuth 2.0 Device Authorization Grant (RFC 8628).
|
|
3
|
-
*
|
|
4
|
-
* The no-browser counterpart to the loopback flow in auth.ts. Instead of
|
|
5
|
-
* opening a browser and listening on localhost, the device flow:
|
|
6
|
-
* 1. asks Google for a device_code + a short human-typable user_code,
|
|
7
|
-
* 2. prints a verification URL + the user_code for the human to open on
|
|
8
|
-
* ANY device (their phone, a laptop with a browser), and
|
|
9
|
-
* 3. polls Google's token endpoint until the human authorizes (or the
|
|
10
|
-
* code expires / is denied).
|
|
11
|
-
*
|
|
12
|
-
* This module is decoupled from the live Google client: `fetch`, `sleep`,
|
|
13
|
-
* and `now` are injected, and the client_id / client_secret / endpoints
|
|
14
|
-
* come from the caller. The live device flow needs a Google OAuth client
|
|
15
|
-
* of type "TVs & Limited Input devices" (a separate GOOGLE_DEVICE_CLIENT_ID
|
|
16
|
-
* — Desktop/loopback clients reject /device/code with invalid_client); the
|
|
17
|
-
* wiring layer supplies those credentials. Everything here is unit-tested
|
|
18
|
-
* against a mocked Google.
|
|
19
|
-
*/
|
|
20
|
-
const GOOGLE_DEVICE_CODE_URL = 'https://oauth2.googleapis.com/device/code';
|
|
21
|
-
const GOOGLE_TOKEN_URL = 'https://oauth2.googleapis.com/token';
|
|
22
|
-
const DEVICE_GRANT_TYPE = 'urn:ietf:params:oauth:grant-type:device_code';
|
|
23
|
-
const DEFAULT_INTERVAL_SECONDS = 5;
|
|
24
|
-
const SLOW_DOWN_INCREMENT_SECONDS = 5;
|
|
25
|
-
/**
|
|
26
|
-
* Failure of the device-grant flow. `code` is Google's OAuth error code
|
|
27
|
-
* where one exists (`access_denied`, `expired_token`, `invalid_client`,
|
|
28
|
-
* `slow_down`, `authorization_pending`) or a synthetic code for
|
|
29
|
-
* transport/shape failures (`device_code_request_failed`,
|
|
30
|
-
* `device_token_request_failed`, `malformed_token_response`).
|
|
31
|
-
*
|
|
32
|
-
* Token material is never placed in the message — only Google's error
|
|
33
|
-
* code + description, mirroring RefreshTokenInvalidError's discipline.
|
|
34
|
-
*/
|
|
35
|
-
export class DeviceAuthError extends Error {
|
|
36
|
-
code;
|
|
37
|
-
constructor(code, message) {
|
|
38
|
-
super(message ?? code);
|
|
39
|
-
this.code = code;
|
|
40
|
-
this.name = 'DeviceAuthError';
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Step 1 — request a device_code + user_code from Google.
|
|
45
|
-
*/
|
|
46
|
-
export async function requestDeviceCode(config, deps) {
|
|
47
|
-
const url = config.deviceCodeUrl ?? GOOGLE_DEVICE_CODE_URL;
|
|
48
|
-
let response;
|
|
49
|
-
try {
|
|
50
|
-
response = await deps.fetch(url, {
|
|
51
|
-
method: 'POST',
|
|
52
|
-
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
53
|
-
body: new URLSearchParams({
|
|
54
|
-
client_id: config.clientId,
|
|
55
|
-
scope: config.scopes.join(' '),
|
|
56
|
-
}),
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
catch (err) {
|
|
60
|
-
throw new DeviceAuthError('device_code_request_failed', `Could not reach Google device endpoint: ${err?.message ?? 'unknown'}`);
|
|
61
|
-
}
|
|
62
|
-
if (!response.ok) {
|
|
63
|
-
const code = await readErrorCode(response);
|
|
64
|
-
throw new DeviceAuthError(code ?? 'device_code_request_failed', `Device-code request failed (HTTP ${response.status}${code ? `, ${code}` : ''})`);
|
|
65
|
-
}
|
|
66
|
-
let data;
|
|
67
|
-
try {
|
|
68
|
-
data = (await response.json());
|
|
69
|
-
}
|
|
70
|
-
catch {
|
|
71
|
-
throw new DeviceAuthError('malformed_token_response', 'Device-code response was not JSON');
|
|
72
|
-
}
|
|
73
|
-
if (!data.device_code || !data.user_code || !data.verification_url) {
|
|
74
|
-
throw new DeviceAuthError('malformed_token_response', 'Device-code response missing device_code/user_code/verification_url');
|
|
75
|
-
}
|
|
76
|
-
return {
|
|
77
|
-
device_code: data.device_code,
|
|
78
|
-
user_code: data.user_code,
|
|
79
|
-
verification_url: data.verification_url,
|
|
80
|
-
expires_in: typeof data.expires_in === 'number' ? data.expires_in : 1800,
|
|
81
|
-
interval: typeof data.interval === 'number' ? data.interval : DEFAULT_INTERVAL_SECONDS,
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* Step 2 — poll Google's token endpoint until the user authorizes the
|
|
86
|
-
* device_code, honoring the RFC 8628 poll semantics.
|
|
87
|
-
*
|
|
88
|
-
* Sleeps `interval` BEFORE each poll (never hammers immediately). A local
|
|
89
|
-
* deadline derived from `expires_in` bounds the loop so a code the user
|
|
90
|
-
* abandons can't poll forever even if Google never returns expired_token.
|
|
91
|
-
*/
|
|
92
|
-
export async function pollForDeviceToken(deviceCode, config, deps) {
|
|
93
|
-
const tokenUrl = config.tokenUrl ?? GOOGLE_TOKEN_URL;
|
|
94
|
-
const now = deps.now ?? Date.now;
|
|
95
|
-
const deadline = now() + deviceCode.expires_in * 1000;
|
|
96
|
-
let intervalSeconds = deviceCode.interval > 0 ? deviceCode.interval : DEFAULT_INTERVAL_SECONDS;
|
|
97
|
-
// eslint-disable-next-line no-constant-condition
|
|
98
|
-
while (true) {
|
|
99
|
-
if (now() >= deadline) {
|
|
100
|
-
throw new DeviceAuthError('expired_token', 'Device code expired before the authorization was completed');
|
|
101
|
-
}
|
|
102
|
-
await deps.sleep(intervalSeconds * 1000);
|
|
103
|
-
let response;
|
|
104
|
-
try {
|
|
105
|
-
response = await deps.fetch(tokenUrl, {
|
|
106
|
-
method: 'POST',
|
|
107
|
-
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
108
|
-
body: new URLSearchParams({
|
|
109
|
-
client_id: config.clientId,
|
|
110
|
-
...(config.clientSecret ? { client_secret: config.clientSecret } : {}),
|
|
111
|
-
device_code: deviceCode.device_code,
|
|
112
|
-
grant_type: DEVICE_GRANT_TYPE,
|
|
113
|
-
}),
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
catch (err) {
|
|
117
|
-
throw new DeviceAuthError('device_token_request_failed', `Could not reach Google token endpoint: ${err?.message ?? 'unknown'}`);
|
|
118
|
-
}
|
|
119
|
-
if (response.ok) {
|
|
120
|
-
let data;
|
|
121
|
-
try {
|
|
122
|
-
data = (await response.json());
|
|
123
|
-
}
|
|
124
|
-
catch {
|
|
125
|
-
throw new DeviceAuthError('malformed_token_response', 'Token response was not JSON');
|
|
126
|
-
}
|
|
127
|
-
if (!data.id_token || typeof data.expires_in !== 'number') {
|
|
128
|
-
throw new DeviceAuthError('malformed_token_response', 'Token response missing id_token or expires_in');
|
|
129
|
-
}
|
|
130
|
-
return {
|
|
131
|
-
id_token: data.id_token,
|
|
132
|
-
refresh_token: data.refresh_token,
|
|
133
|
-
expires_in: data.expires_in,
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
const code = await readErrorCode(response);
|
|
137
|
-
switch (code) {
|
|
138
|
-
case 'authorization_pending':
|
|
139
|
-
// The user hasn't finished yet — keep polling at the same interval.
|
|
140
|
-
continue;
|
|
141
|
-
case 'slow_down':
|
|
142
|
-
// Google asks us to back off; RFC 8628 §3.5 → bump the interval.
|
|
143
|
-
intervalSeconds += SLOW_DOWN_INCREMENT_SECONDS;
|
|
144
|
-
continue;
|
|
145
|
-
case 'access_denied':
|
|
146
|
-
throw new DeviceAuthError('access_denied', 'Authorization was denied by the user');
|
|
147
|
-
case 'expired_token':
|
|
148
|
-
throw new DeviceAuthError('expired_token', 'Device code expired before authorization');
|
|
149
|
-
default:
|
|
150
|
-
throw new DeviceAuthError(code ?? 'device_token_request_failed', `Device token poll failed (HTTP ${response.status}${code ? `, ${code}` : ''})`);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
/**
|
|
155
|
-
* Extract Google's OAuth `error` code from a non-2xx response body without
|
|
156
|
-
* throwing. Returns null when the body isn't JSON or has no error field.
|
|
157
|
-
*/
|
|
158
|
-
async function readErrorCode(response) {
|
|
159
|
-
try {
|
|
160
|
-
const body = (await response.json());
|
|
161
|
-
return typeof body?.error === 'string' ? body.error : null;
|
|
162
|
-
}
|
|
163
|
-
catch {
|
|
164
|
-
return null;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
//# sourceMappingURL=device-auth.js.map
|
|
1
|
+
const u="https://oauth2.googleapis.com/device/code",p="https://oauth2.googleapis.com/token",w="urn:ietf:params:oauth:grant-type:device_code",h=5,f=5;class n extends Error{code;constructor(o,i){super(i??o),this.code=o,this.name="DeviceAuthError"}}async function v(t,o){const i=t.deviceCodeUrl??u;let c;try{c=await o.fetch(i,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({client_id:t.clientId,scope:t.scopes.join(" ")})})}catch(s){throw new n("device_code_request_failed",`Could not reach Google device endpoint: ${s?.message??"unknown"}`)}if(!c.ok){const s=await l(c);throw new n(s??"device_code_request_failed",`Device-code request failed (HTTP ${c.status}${s?`, ${s}`:""})`)}let e;try{e=await c.json()}catch{throw new n("malformed_token_response","Device-code response was not JSON")}if(!e.device_code||!e.user_code||!e.verification_url)throw new n("malformed_token_response","Device-code response missing device_code/user_code/verification_url");return{device_code:e.device_code,user_code:e.user_code,verification_url:e.verification_url,expires_in:typeof e.expires_in=="number"?e.expires_in:1800,interval:typeof e.interval=="number"?e.interval:5}}async function E(t,o,i){const c=o.tokenUrl??p,e=i.now??Date.now,s=e()+t.expires_in*1e3;let _=t.interval>0?t.interval:5;for(;;){if(e()>=s)throw new n("expired_token","Device code expired before the authorization was completed");await i.sleep(_*1e3);let a;try{a=await i.fetch(c,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({client_id:o.clientId,...o.clientSecret?{client_secret:o.clientSecret}:{},device_code:t.device_code,grant_type:w})})}catch(r){throw new n("device_token_request_failed",`Could not reach Google token endpoint: ${r?.message??"unknown"}`)}if(a.ok){let r;try{r=await a.json()}catch{throw new n("malformed_token_response","Token response was not JSON")}if(!r.id_token||typeof r.expires_in!="number")throw new n("malformed_token_response","Token response missing id_token or expires_in");return{id_token:r.id_token,refresh_token:r.refresh_token,expires_in:r.expires_in}}const d=await l(a);switch(d){case"authorization_pending":continue;case"slow_down":_+=5;continue;case"access_denied":throw new n("access_denied","Authorization was denied by the user");case"expired_token":throw new n("expired_token","Device code expired before authorization");default:throw new n(d??"device_token_request_failed",`Device token poll failed (HTTP ${a.status}${d?`, ${d}`:""})`)}}}async function l(t){try{const o=await t.json();return typeof o?.error=="string"?o.error:null}catch{return null}}export{n as DeviceAuthError,E as pollForDeviceToken,v as requestDeviceCode};
|
package/dist/direct-log.js
CHANGED
|
@@ -1,11 +1 @@
|
|
|
1
|
-
|
|
2
|
-
if (value == null)
|
|
3
|
-
return [];
|
|
4
|
-
const raw = Array.isArray(value) ? value : [value];
|
|
5
|
-
const recipients = raw
|
|
6
|
-
.filter((item) => typeof item === 'string')
|
|
7
|
-
.map((item) => item.trim())
|
|
8
|
-
.filter((item) => item.length > 0);
|
|
9
|
-
return [...new Set(recipients)];
|
|
10
|
-
}
|
|
11
|
-
//# sourceMappingURL=direct-log.js.map
|
|
1
|
+
function e(t){if(t==null)return[];const i=(Array.isArray(t)?t:[t]).filter(r=>typeof r=="string").map(r=>r.trim()).filter(r=>r.length>0);return[...new Set(i)]}export{e as normalizeDirectLogRecipients};
|
package/dist/health-beat.js
CHANGED
|
@@ -1,168 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* gh#541 WU-2 — client health beat (the Part B "receipt watermark" producer).
|
|
3
|
-
*
|
|
4
|
-
* The MCP-client child process emits a periodic + event-driven health BEAT to
|
|
5
|
-
* the server's `POST /api/drone/health` (WU-1). The beat reports:
|
|
6
|
-
* - `last_event_at` — when this client last RECEIVED an inbound cube
|
|
7
|
-
* event (the wake-path RECEIPT evidence). This is
|
|
8
|
-
* produced BELOW the agent classifier (WU-0 proved
|
|
9
|
-
* child-process HTTP is independent of the agent
|
|
10
|
-
* tool-call path), so it stays fresh even during a
|
|
11
|
-
* classifier outage and even when the agent /loop
|
|
12
|
-
* never wakes — which is exactly what lets the
|
|
13
|
-
* watchdog (WU-3) tell DEAF (Monitor down) apart
|
|
14
|
-
* from POST-BLOCKED.
|
|
15
|
-
* - `sse_connected` — the SSE wire state.
|
|
16
|
-
* - `inbox_monitor_armed` — whether a tail-Monitor is watching the inbox.
|
|
17
|
-
*
|
|
18
|
-
* The beat carries NO token material in the body (auth is the X-Drone-Session
|
|
19
|
-
* header, same as every other drone endpoint). It is strictly BEST-EFFORT: a
|
|
20
|
-
* failed POST is swallowed and must never crash the SSE stream.
|
|
21
|
-
*
|
|
22
|
-
* Side-effecting deps (fetch, token, cube/status/monitor probes, clock) are
|
|
23
|
-
* injected so the producer is unit-tested without real network/keychain/pgrep.
|
|
24
|
-
*/
|
|
25
|
-
// ─── Module state (process-singleton, like the SSE stream state) ─────────
|
|
26
|
-
let lastEventReceivedAt = null;
|
|
27
|
-
// Cached monitor-health, refreshed by the ~60s tick so per-event beats don't
|
|
28
|
-
// have to spawn pgrep on every inbound entry. `null` = unknown (treated as
|
|
29
|
-
// armed by the payload mapping below).
|
|
30
|
-
let cachedMonitorHealthy = null;
|
|
31
|
-
// gh#633: cached transport-agnostic wake-armed (claude monitor OR codex bridge
|
|
32
|
-
// liveness), refreshed by the ~60s tick so per-event beats reuse it without
|
|
33
|
-
// re-probing. `null` = unknown (treated as armed by the payload mapping below).
|
|
34
|
-
let cachedWakeArmed = null;
|
|
35
|
-
/** Record that an inbound cube event was just received (the receipt evidence). */
|
|
36
|
-
export function recordEventReceipt(now = new Date()) {
|
|
37
|
-
lastEventReceivedAt = now;
|
|
38
|
-
}
|
|
39
|
-
export function getLastEventReceivedAt() {
|
|
40
|
-
return lastEventReceivedAt;
|
|
41
|
-
}
|
|
42
|
-
export function getCachedMonitorHealthy() {
|
|
43
|
-
return cachedMonitorHealthy;
|
|
44
|
-
}
|
|
45
|
-
/** Cached transport-agnostic wake-armed (gh#633), for cheap per-event beats. */
|
|
46
|
-
export function getCachedWakeArmed() {
|
|
47
|
-
return cachedWakeArmed;
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Reset module state. TEST-ONLY — the beat state is a process singleton in
|
|
51
|
-
* normal operation; nothing in production code should call this.
|
|
52
|
-
* @internal
|
|
53
|
-
*/
|
|
54
|
-
export function __resetHealthBeatStateForTest() {
|
|
55
|
-
lastEventReceivedAt = null;
|
|
56
|
-
cachedMonitorHealthy = null;
|
|
57
|
-
cachedWakeArmed = null;
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Build the beat payload from the current receipt watermark + the supplied
|
|
61
|
-
* wire/monitor state.
|
|
62
|
-
*
|
|
63
|
-
* `inbox_monitor_armed` maps the tri-state monitor probe to the boolean the
|
|
64
|
-
* server schema requires: ONLY a POSITIVELY-broken probe (`false`) reports
|
|
65
|
-
* `false`; both healthy (`true`) and unknown (`null`) report `true`. This
|
|
66
|
-
* mirrors `shouldShowWakePathWarning` (which fires only on `=== false`) and is
|
|
67
|
-
* the design's false-deaf-avoidance posture — an undeterminable probe must not
|
|
68
|
-
* masquerade as a dead Monitor.
|
|
69
|
-
*/
|
|
70
|
-
export function buildHealthPayload(sseConnected, inboxMonitorHealthy, wakeArmed, agentKind, hostname, version) {
|
|
71
|
-
return {
|
|
72
|
-
sse_connected: sseConnected,
|
|
73
|
-
inbox_monitor_armed: inboxMonitorHealthy !== false,
|
|
74
|
-
// gh#633: same false-deaf-avoidance map as inbox_monitor_armed — only a
|
|
75
|
-
// POSITIVELY-false probe reports false; healthy (true) and indeterminate
|
|
76
|
-
// (null) both report armed. Prevents a transient/undeterminable wake-path
|
|
77
|
-
// probe from masquerading as a dead wake path.
|
|
78
|
-
wake_armed: wakeArmed !== false,
|
|
79
|
-
// gh#634: live runtime agent_kind (caller passes resolveSessionAgentKind()).
|
|
80
|
-
agent_kind: agentKind,
|
|
81
|
-
hostname,
|
|
82
|
-
version,
|
|
83
|
-
last_event_at: lastEventReceivedAt ? lastEventReceivedAt.toISOString() : null,
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Best-effort POST of a beat. Swallows ALL errors (token fetch, network,
|
|
88
|
-
* non-2xx) — a beat that can't be delivered must never propagate into the
|
|
89
|
-
* stream loop. Body carries NO token material; auth is via headers.
|
|
90
|
-
*/
|
|
91
|
-
export async function postHealthBeat(active, payload, deps) {
|
|
92
|
-
try {
|
|
93
|
-
const token = await deps.getToken();
|
|
94
|
-
await deps.fetchImpl(`${active.apiUrl}/api/drone/health`, {
|
|
95
|
-
method: 'POST',
|
|
96
|
-
headers: {
|
|
97
|
-
Authorization: `Bearer ${token}`,
|
|
98
|
-
'X-Drone-Session': active.sessionToken,
|
|
99
|
-
'Content-Type': 'application/json',
|
|
100
|
-
},
|
|
101
|
-
body: JSON.stringify(payload),
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
catch {
|
|
105
|
-
// Best-effort: never crash the stream on a beat failure.
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
/** Build the current payload and post it (best-effort). */
|
|
109
|
-
export async function emitHealthBeat(active, opts) {
|
|
110
|
-
const payload = buildHealthPayload(opts.sseConnected, opts.inboxMonitorHealthy, opts.wakeArmed, opts.agentKind, opts.hostname, opts.version);
|
|
111
|
-
await postHealthBeat(active, payload, opts);
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* One tick of the periodic beat: resolve the active cube, probe the live
|
|
115
|
-
* wire + Monitor state, cache the monitor result (so cheap per-event beats can
|
|
116
|
-
* reuse it without re-spawning pgrep), and emit the beat. No active cube → no
|
|
117
|
-
* beat. Best-effort: never throws.
|
|
118
|
-
*/
|
|
119
|
-
export async function runHealthBeatOnce(deps) {
|
|
120
|
-
try {
|
|
121
|
-
const active = await deps.getActiveCube();
|
|
122
|
-
if (!active)
|
|
123
|
-
return;
|
|
124
|
-
const connected = deps.getStreamConnected();
|
|
125
|
-
const healthy = deps.checkMonitor(deps.getInboxPath(active));
|
|
126
|
-
cachedMonitorHealthy = healthy;
|
|
127
|
-
// gh#633: wake_armed = the running drone's OWN wake-transport health.
|
|
128
|
-
// codex (remote-wake) → app-server bridge liveness; claude → the same
|
|
129
|
-
// tail-F Monitor health (its wake path IS the Monitor). Transport-agnostic:
|
|
130
|
-
// HOP-2 reads one boolean, never branches on (mis-recordable) agent_kind.
|
|
131
|
-
const wakeArmed = deps.isCodexRemoteWake()
|
|
132
|
-
? await deps.probeBridgeArmed(active)
|
|
133
|
-
: healthy;
|
|
134
|
-
cachedWakeArmed = wakeArmed;
|
|
135
|
-
// gh#634: live runtime agent_kind, beated every cycle to self-heal the
|
|
136
|
-
// recorded column (frozen at first-join; relaunch never re-assimilates).
|
|
137
|
-
const agentKind = deps.resolveAgentKind();
|
|
138
|
-
const hostname = deps.resolveHostname();
|
|
139
|
-
const version = deps.resolveVersion();
|
|
140
|
-
await emitHealthBeat(active, {
|
|
141
|
-
sseConnected: connected,
|
|
142
|
-
inboxMonitorHealthy: healthy,
|
|
143
|
-
wakeArmed,
|
|
144
|
-
agentKind,
|
|
145
|
-
hostname,
|
|
146
|
-
version,
|
|
147
|
-
fetchImpl: deps.fetchImpl,
|
|
148
|
-
getToken: deps.getToken,
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
catch {
|
|
152
|
-
// Best-effort tick: a probe/beat failure must not kill the interval.
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
/** Default periodic cadence for the health beat. */
|
|
156
|
-
export const HEALTH_BEAT_INTERVAL_MS = 60_000;
|
|
157
|
-
/**
|
|
158
|
-
* Start the periodic beat. Fire-and-forget; the timer is `unref`'d so it never
|
|
159
|
-
* keeps the process alive on its own. Returns the timer handle (tests clear it).
|
|
160
|
-
*/
|
|
161
|
-
export function startHealthBeatTick(deps, intervalMs = HEALTH_BEAT_INTERVAL_MS) {
|
|
162
|
-
const handle = setInterval(() => {
|
|
163
|
-
void runHealthBeatOnce(deps);
|
|
164
|
-
}, intervalMs);
|
|
165
|
-
handle.unref?.();
|
|
166
|
-
return handle;
|
|
167
|
-
}
|
|
168
|
-
//# sourceMappingURL=health-beat.js.map
|
|
1
|
+
let a=null,i=null,l=null;function x(e=new Date){a=e}function y(){return a}function k(){return i}function v(){return l}function A(){a=null,i=null,l=null}function u(e,t,n,o,r,c){return{sse_connected:e,inbox_monitor_armed:t!==!1,wake_armed:n!==!1,agent_kind:o,hostname:r,version:c,last_event_at:a?a.toISOString():null}}async function d(e,t,n){try{const o=await n.getToken();await n.fetchImpl(`${e.apiUrl}/api/drone/health`,{method:"POST",headers:{Authorization:`Bearer ${o}`,"X-Drone-Session":e.sessionToken,"Content-Type":"application/json"},body:JSON.stringify(t)})}catch{}}async function f(e,t){const n=u(t.sseConnected,t.inboxMonitorHealthy,t.wakeArmed,t.agentKind,t.hostname,t.version);await d(e,n,t)}async function m(e){try{const t=await e.getActiveCube();if(!t)return;const n=e.getStreamConnected(),o=e.checkMonitor(e.getInboxPath(t));i=o;const r=e.isCodexRemoteWake()?await e.probeBridgeArmed(t):o;l=r;const c=e.resolveAgentKind(),h=e.resolveHostname(),s=e.resolveVersion();await f(t,{sseConnected:n,inboxMonitorHealthy:o,wakeArmed:r,agentKind:c,hostname:h,version:s,fetchImpl:e.fetchImpl,getToken:e.getToken})}catch{}}const g=6e4;function H(e,t=g){const n=setInterval(()=>{m(e)},t);return n.unref?.(),n}export{g as HEALTH_BEAT_INTERVAL_MS,A as __resetHealthBeatStateForTest,u as buildHealthPayload,f as emitHealthBeat,k as getCachedMonitorHealthy,v as getCachedWakeArmed,y as getLastEventReceivedAt,d as postHealthBeat,x as recordEventReceipt,m as runHealthBeatOnce,H as startHealthBeatTick};
|
package/dist/inbox-monitor.js
CHANGED
|
@@ -1,130 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
* borg-inbox-monitor — per-entry pretty-printer for borgmcp inbox files.
|
|
4
|
-
*
|
|
5
|
-
* Per gh#8: Claude Code's task-notification title is the Monitor's
|
|
6
|
-
* `description`, set once at arm-time. When the Monitor command is
|
|
7
|
-
* `tail -F <inbox>`, every event's notification title is the same
|
|
8
|
-
* static "Monitor event: ..." string regardless of which drone posted
|
|
9
|
-
* what. Recipients have to read the body to triage.
|
|
10
|
-
*
|
|
11
|
-
* Replacement: tail the inbox file and emit one stdout line per cube
|
|
12
|
-
* log entry, summarizing drone label + role + first ~80 chars of the
|
|
13
|
-
* message body. Claude Code's Monitor batching then uses that single
|
|
14
|
-
* line as the per-event task-notification title.
|
|
15
|
-
*
|
|
16
|
-
* Inbox file format (per client/src/log-stream.ts formatInboxLine):
|
|
17
|
-
* <iso-ts> <drone-label> (<role-name>): <message>
|
|
18
|
-
*
|
|
19
|
-
* Multi-line messages are appended as a single fs.appendFile() call
|
|
20
|
-
* with embedded `\n` characters, so they become multiple physical
|
|
21
|
-
* lines in the file. Continuation lines (those that don't start with
|
|
22
|
-
* an ISO-8601 timestamp) are dropped — only the first line of each
|
|
23
|
-
* entry surfaces, which is the part that summarizes the entry.
|
|
24
|
-
*
|
|
25
|
-
* Usage:
|
|
26
|
-
* borg-inbox-monitor <inbox-file-path>
|
|
27
|
-
*/
|
|
28
|
-
import { spawn } from 'node:child_process';
|
|
29
|
-
import { realpathSync } from 'node:fs';
|
|
30
|
-
import { createInterface } from 'node:readline';
|
|
31
|
-
import { fileURLToPath } from 'node:url';
|
|
32
|
-
const ENTRY_LINE_RE = /^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\S*)\s+(\S+)\s+\(([^)]+)\):\s*(.*)$/;
|
|
33
|
-
/**
|
|
34
|
-
* Pure: parse one inbox-file line and produce the pretty summary line
|
|
35
|
-
* (or null if the line is a continuation or unrecognized shape).
|
|
36
|
-
*
|
|
37
|
-
* Pass-through — no truncation. Claude Code does not impose a hard cap
|
|
38
|
-
* on task-notification title length. The 200-char `MAX_SUMMARY_LEN` cap
|
|
39
|
-
* removed here (and the 80-char predecessor) were borg-mcp conventions
|
|
40
|
-
* built on a misunderstanding of the renderer's limits. Drones now see
|
|
41
|
-
* the full first line of every entry; multi-signal batched posts no
|
|
42
|
-
* longer have their second signal hidden.
|
|
43
|
-
*
|
|
44
|
-
* Exported so tests can exercise the parsing without spawning tail.
|
|
45
|
-
*/
|
|
46
|
-
export function formatEventLine(inboxLine) {
|
|
47
|
-
const match = ENTRY_LINE_RE.exec(inboxLine);
|
|
48
|
-
if (!match)
|
|
49
|
-
return null;
|
|
50
|
-
const [, , label, role, body] = match;
|
|
51
|
-
const summary = body.trim();
|
|
52
|
-
return `${label} (${role}): ${summary}`;
|
|
53
|
-
}
|
|
54
|
-
function main() {
|
|
55
|
-
const inboxPath = process.argv[2];
|
|
56
|
-
if (!inboxPath) {
|
|
57
|
-
console.error('borg-inbox-monitor: usage: borg-inbox-monitor <inbox-path>');
|
|
58
|
-
process.exit(2);
|
|
59
|
-
}
|
|
60
|
-
// `tail -F` for rotation/truncation resilience on macOS + Linux.
|
|
61
|
-
// Node's fs.watch is unreliable across file rotation; subprocess
|
|
62
|
-
// tail matches the prior kickoff-Monitor shape (`tail -F`) so the
|
|
63
|
-
// wire behavior is identical — only the per-line projection changes.
|
|
64
|
-
// `-n 0` skips backfilling history so fresh sessions don't replay
|
|
65
|
-
// old entries on every restart.
|
|
66
|
-
const tail = spawn('tail', ['-F', '-n', '0', inboxPath], {
|
|
67
|
-
stdio: ['ignore', 'pipe', 'inherit'],
|
|
68
|
-
});
|
|
69
|
-
if (!tail.stdout) {
|
|
70
|
-
console.error('borg-inbox-monitor: tail subprocess has no stdout');
|
|
71
|
-
process.exit(1);
|
|
72
|
-
}
|
|
73
|
-
const rl = createInterface({ input: tail.stdout, crlfDelay: Infinity });
|
|
74
|
-
let shuttingDown = false;
|
|
75
|
-
rl.on('line', (line) => {
|
|
76
|
-
const pretty = formatEventLine(line);
|
|
77
|
-
if (pretty !== null) {
|
|
78
|
-
console.log(pretty);
|
|
79
|
-
}
|
|
80
|
-
});
|
|
81
|
-
tail.on('error', (err) => {
|
|
82
|
-
console.error(`borg-inbox-monitor: tail failed: ${err.message}`);
|
|
83
|
-
process.exit(1);
|
|
84
|
-
});
|
|
85
|
-
tail.on('exit', (code, signal) => {
|
|
86
|
-
if (shuttingDown)
|
|
87
|
-
process.exit(0);
|
|
88
|
-
if (signal)
|
|
89
|
-
process.exit(0);
|
|
90
|
-
process.exit(code ?? 0);
|
|
91
|
-
});
|
|
92
|
-
const shutdown = (signal) => {
|
|
93
|
-
if (shuttingDown)
|
|
94
|
-
return;
|
|
95
|
-
shuttingDown = true;
|
|
96
|
-
rl.close();
|
|
97
|
-
if (!tail.killed && !tail.kill(signal)) {
|
|
98
|
-
process.exit(0);
|
|
99
|
-
}
|
|
100
|
-
setTimeout(() => process.exit(0), 1000).unref();
|
|
101
|
-
};
|
|
102
|
-
process.once('SIGTERM', () => shutdown('SIGTERM'));
|
|
103
|
-
process.once('SIGINT', () => shutdown('SIGINT'));
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* Is this module being invoked as the bin entry point?
|
|
107
|
-
*
|
|
108
|
-
* gh#114: under `npm install`, `process.argv[1]` is the npm-bin symlink
|
|
109
|
-
* path while `fileURLToPath(import.meta.url)` is the realpath of the
|
|
110
|
-
* installed file. A naive `===` check never matches → `main()` never
|
|
111
|
-
* runs → the documented `borg-inbox-monitor` Monitor command silently
|
|
112
|
-
* no-ops and drones go deaf without the wake-path-self-heal (gh#43)
|
|
113
|
-
* triggering. Resolve the symlink before comparing.
|
|
114
|
-
*
|
|
115
|
-
* Exported for unit testing.
|
|
116
|
-
*/
|
|
117
|
-
export function isEntryInvocation(argv1, importMetaUrl) {
|
|
118
|
-
try {
|
|
119
|
-
return realpathSync(argv1) === fileURLToPath(importMetaUrl);
|
|
120
|
-
}
|
|
121
|
-
catch {
|
|
122
|
-
return false;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
// Only run main() when invoked as the bin entry — allow importing the
|
|
126
|
-
// pure formatEventLine for unit testing without spawning tail.
|
|
127
|
-
if (isEntryInvocation(process.argv[1], import.meta.url)) {
|
|
128
|
-
main();
|
|
129
|
-
}
|
|
130
|
-
//# sourceMappingURL=inbox-monitor.js.map
|
|
2
|
+
import{spawn as c}from"node:child_process";import{realpathSync as l}from"node:fs";import{createInterface as a}from"node:readline";import{fileURLToPath as p}from"node:url";const f=/^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\S*)\s+(\S+)\s+\(([^)]+)\):\s*(.*)$/;function u(r){const o=f.exec(r);if(!o)return null;const[,,n,e,i]=o,t=i.trim();return`${n} (${e}): ${t}`}function m(){const r=process.argv[2];r||(console.error("borg-inbox-monitor: usage: borg-inbox-monitor <inbox-path>"),process.exit(2));const o=c("tail",["-F","-n","0",r],{stdio:["ignore","pipe","inherit"]});o.stdout||(console.error("borg-inbox-monitor: tail subprocess has no stdout"),process.exit(1));const n=a({input:o.stdout,crlfDelay:1/0});let e=!1;n.on("line",t=>{const s=u(t);s!==null&&console.log(s)}),o.on("error",t=>{console.error(`borg-inbox-monitor: tail failed: ${t.message}`),process.exit(1)}),o.on("exit",(t,s)=>{e&&process.exit(0),s&&process.exit(0),process.exit(t??0)});const i=t=>{e||(e=!0,n.close(),!o.killed&&!o.kill(t)&&process.exit(0),setTimeout(()=>process.exit(0),1e3).unref())};process.once("SIGTERM",()=>i("SIGTERM")),process.once("SIGINT",()=>i("SIGINT"))}function x(r,o){try{return l(r)===p(o)}catch{return!1}}x(process.argv[1],import.meta.url)&&m();export{u as formatEventLine,x as isEntryInvocation};
|