gitnexus 1.6.8-rc.21 → 1.6.8-rc.22
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.
|
@@ -24,7 +24,10 @@ const fs = require('fs');
|
|
|
24
24
|
const path = require('path');
|
|
25
25
|
const { spawnSync } = require('child_process');
|
|
26
26
|
const { acquireHookSlot } = require('./hook-lock.cjs');
|
|
27
|
-
const {
|
|
27
|
+
const {
|
|
28
|
+
hasGitNexusDbLockedByGitNexusServer,
|
|
29
|
+
resolveUnixGuardTimeout,
|
|
30
|
+
} = require('./hook-db-lock-probe.cjs');
|
|
28
31
|
const { formatAnalyzeCommand } = require('./resolve-analyze-cmd.cjs');
|
|
29
32
|
|
|
30
33
|
function readInput() {
|
|
@@ -198,10 +201,73 @@ function resolveCliPath() {
|
|
|
198
201
|
return cliPath;
|
|
199
202
|
}
|
|
200
203
|
|
|
204
|
+
// Debounce for the unguarded-CLI diagnostic below (#2163 follow-up review):
|
|
205
|
+
// at most one line per (short-lived) hook process, even if a future change
|
|
206
|
+
// runs the CLI more than once.
|
|
207
|
+
let unguardedCliWarned = false;
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Unix orphan containment (#2163 follow-up): the augment CLI is the
|
|
211
|
+
* longest-lived hook child (inner spawnSync timeout 7s locally, 12s via
|
|
212
|
+
* npx), so on Unix it gets the same SIGKILL-surviving coreutils `timeout`
|
|
213
|
+
* wrapper as the probe's lsof/ps. The wrapper budget is ceil(inner/1000)+1
|
|
214
|
+
* seconds — STRICTLY greater than the inner spawnSync timeout, so on the
|
|
215
|
+
* supervised path Node's SIGTERM always fires first and the existing
|
|
216
|
+
* error/status contract is untouched. Once the hook itself has been
|
|
217
|
+
* SIGKILLed (exactly the orphan case the wrapper exists for), the guard
|
|
218
|
+
* semantics differ per branch:
|
|
219
|
+
* - direct exec (the CLI is the guard's CHILD): `-k 1` TERM-first — a
|
|
220
|
+
* SIGTERM-immune CLI can hold the guard ~1s past the inner timeout
|
|
221
|
+
* before the `-k` SIGKILL escalation reaps it.
|
|
222
|
+
* - npx (the CLI is a GRANDCHILD: guard → npx → CLI): `-s KILL` — the
|
|
223
|
+
* budget expiry SIGKILLs the whole process group outright. TERM-first
|
|
224
|
+
* would kill only the obedient npx parent, making `timeout` reap it and
|
|
225
|
+
* return before the `-k` escalation ever fires, stranding a
|
|
226
|
+
* SIGTERM-immune CLI grandchild unbounded (reproduced on coreutils
|
|
227
|
+
* 9.x). `-k 1` is retained alongside `-s KILL` as a harmless belt: with
|
|
228
|
+
* `-s KILL` the `-k` escalation signal is also KILL. Two residual gaps
|
|
229
|
+
* on this branch, both bounded by "no worse than pre-fix" (where the
|
|
230
|
+
* grandchild received no signal at all): the group-wide SIGKILL is
|
|
231
|
+
* coreutils semantics — a busybox `timeout` passes the self-test (it
|
|
232
|
+
* has `-k` and propagates exit status) but signals only its direct
|
|
233
|
+
* child, so a busybox guard cannot reach the grandchild; and on the
|
|
234
|
+
* SUPERVISED path (hook alive, inner spawnSync timeout SIGTERMs the
|
|
235
|
+
* guard) coreutils forwards TERM rather than the `-s` signal, npx dies,
|
|
236
|
+
* and the guard exits before any KILL fires — so a SIGTERM-immune CLI
|
|
237
|
+
* grandchild still escapes in those two cases.
|
|
238
|
+
* If the sibling probe predates the resolveUnixGuardTimeout export (version
|
|
239
|
+
* skew), the adapter degrades to the unwrapped invocation instead of
|
|
240
|
+
* throwing. Windows is deliberately NOT wrapped — there is no coreutils
|
|
241
|
+
* timeout to resolve there and the resolver's self-test spawns /bin/sh — so
|
|
242
|
+
* on win32 (the npx.cmd path) and whenever the guard resolves to null (e.g.
|
|
243
|
+
* macOS without Homebrew coreutils — reported once under GITNEXUS_DEBUG)
|
|
244
|
+
* the argv stays byte-identical to the pre-wrap invocation.
|
|
245
|
+
*/
|
|
201
246
|
function runGitNexusCli(cliPath, args, cwd, timeout) {
|
|
202
247
|
const isWin = process.platform === 'win32';
|
|
248
|
+
// Version-skew guard (#2163 follow-up review): an older sibling probe
|
|
249
|
+
// without the resolveUnixGuardTimeout export must degrade to the unwrapped
|
|
250
|
+
// invocation — a TypeError here would be swallowed by the caller's catch
|
|
251
|
+
// and silently kill the augment.
|
|
252
|
+
const guard =
|
|
253
|
+
isWin || typeof resolveUnixGuardTimeout !== 'function' ? null : resolveUnixGuardTimeout();
|
|
254
|
+
if (!isWin && !guard && !unguardedCliWarned && isDebugEnabled()) {
|
|
255
|
+
// Diagnose the "stays unwrapped" Unix paths once per hook process: no
|
|
256
|
+
// usable coreutils timeout/gtimeout (e.g. macOS without Homebrew
|
|
257
|
+
// coreutils), GITNEXUS_HOOK_TIMEOUT_PATH=disabled, or probe skew above.
|
|
258
|
+
unguardedCliWarned = true;
|
|
259
|
+
process.stderr.write(
|
|
260
|
+
'[GitNexus hook] no usable timeout/gtimeout guard; augment CLI child runs unguarded\n',
|
|
261
|
+
);
|
|
262
|
+
}
|
|
203
263
|
if (cliPath) {
|
|
204
|
-
|
|
264
|
+
const [cmd, cmdArgs] = guard
|
|
265
|
+
? [
|
|
266
|
+
guard,
|
|
267
|
+
['-k', '1', String(Math.ceil(timeout / 1000) + 1), process.execPath, cliPath, ...args],
|
|
268
|
+
]
|
|
269
|
+
: [process.execPath, [cliPath, ...args]];
|
|
270
|
+
return spawnSync(cmd, cmdArgs, {
|
|
205
271
|
encoding: 'utf-8',
|
|
206
272
|
timeout,
|
|
207
273
|
cwd,
|
|
@@ -209,7 +275,27 @@ function runGitNexusCli(cliPath, args, cwd, timeout) {
|
|
|
209
275
|
windowsHide: true,
|
|
210
276
|
});
|
|
211
277
|
}
|
|
212
|
-
|
|
278
|
+
// A non-null guard implies non-Windows, so the wrapped arm can hardcode
|
|
279
|
+
// plain `npx`. The wrapped arm leads with `-s KILL` (NOT TERM-first like
|
|
280
|
+
// the direct branch above): the CLI here is a grandchild behind npx — see
|
|
281
|
+
// the docblock.
|
|
282
|
+
const [cmd, cmdArgs] = guard
|
|
283
|
+
? [
|
|
284
|
+
guard,
|
|
285
|
+
[
|
|
286
|
+
'-s',
|
|
287
|
+
'KILL',
|
|
288
|
+
'-k',
|
|
289
|
+
'1',
|
|
290
|
+
String(Math.ceil((timeout + 5000) / 1000) + 1),
|
|
291
|
+
'npx',
|
|
292
|
+
'-y',
|
|
293
|
+
'gitnexus',
|
|
294
|
+
...args,
|
|
295
|
+
],
|
|
296
|
+
]
|
|
297
|
+
: [isWin ? 'npx.cmd' : 'npx', ['-y', 'gitnexus', ...args]];
|
|
298
|
+
return spawnSync(cmd, cmdArgs, {
|
|
213
299
|
encoding: 'utf-8',
|
|
214
300
|
timeout: timeout + 5000,
|
|
215
301
|
cwd,
|
|
@@ -15,7 +15,10 @@ const fs = require('fs');
|
|
|
15
15
|
const path = require('path');
|
|
16
16
|
const { spawnSync } = require('child_process');
|
|
17
17
|
const { acquireHookSlot } = require('./hook-lock.cjs');
|
|
18
|
-
const {
|
|
18
|
+
const {
|
|
19
|
+
hasGitNexusDbLockedByGitNexusServer,
|
|
20
|
+
resolveUnixGuardTimeout,
|
|
21
|
+
} = require('./hook-db-lock-probe.cjs');
|
|
19
22
|
const { formatAnalyzeCommand } = require('./resolve-analyze-cmd.cjs');
|
|
20
23
|
|
|
21
24
|
/**
|
|
@@ -218,14 +221,76 @@ function resolveCliPath() {
|
|
|
218
221
|
return cliPath;
|
|
219
222
|
}
|
|
220
223
|
|
|
224
|
+
// Debounce for the unguarded-CLI diagnostic below (#2163 follow-up review):
|
|
225
|
+
// at most one line per (short-lived) hook process, even if a future change
|
|
226
|
+
// runs the CLI more than once.
|
|
227
|
+
let unguardedCliWarned = false;
|
|
228
|
+
|
|
221
229
|
/**
|
|
222
230
|
* Spawn a gitnexus CLI command synchronously.
|
|
223
231
|
* Returns the stderr output (KuzuDB captures stdout at OS level).
|
|
232
|
+
*
|
|
233
|
+
* Unix orphan containment (#2163 follow-up): the augment CLI is the
|
|
234
|
+
* longest-lived hook child (inner spawnSync timeout 7s locally, 12s via
|
|
235
|
+
* npx), so on Unix it gets the same SIGKILL-surviving coreutils `timeout`
|
|
236
|
+
* wrapper as the probe's lsof/ps. The wrapper budget is ceil(inner/1000)+1
|
|
237
|
+
* seconds — STRICTLY greater than the inner spawnSync timeout, so on the
|
|
238
|
+
* supervised path Node's SIGTERM always fires first and the existing
|
|
239
|
+
* error/status contract is untouched. Once the hook itself has been
|
|
240
|
+
* SIGKILLed (exactly the orphan case the wrapper exists for), the guard
|
|
241
|
+
* semantics differ per branch:
|
|
242
|
+
* - direct exec (the CLI is the guard's CHILD): `-k 1` TERM-first — a
|
|
243
|
+
* SIGTERM-immune CLI can hold the guard ~1s past the inner timeout
|
|
244
|
+
* before the `-k` SIGKILL escalation reaps it.
|
|
245
|
+
* - npx (the CLI is a GRANDCHILD: guard → npx → CLI): `-s KILL` — the
|
|
246
|
+
* budget expiry SIGKILLs the whole process group outright. TERM-first
|
|
247
|
+
* would kill only the obedient npx parent, making `timeout` reap it and
|
|
248
|
+
* return before the `-k` escalation ever fires, stranding a
|
|
249
|
+
* SIGTERM-immune CLI grandchild unbounded (reproduced on coreutils
|
|
250
|
+
* 9.x). `-k 1` is retained alongside `-s KILL` as a harmless belt: with
|
|
251
|
+
* `-s KILL` the `-k` escalation signal is also KILL. Two residual gaps
|
|
252
|
+
* on this branch, both bounded by "no worse than pre-fix" (where the
|
|
253
|
+
* grandchild received no signal at all): the group-wide SIGKILL is
|
|
254
|
+
* coreutils semantics — a busybox `timeout` passes the self-test (it
|
|
255
|
+
* has `-k` and propagates exit status) but signals only its direct
|
|
256
|
+
* child, so a busybox guard cannot reach the grandchild; and on the
|
|
257
|
+
* SUPERVISED path (hook alive, inner spawnSync timeout SIGTERMs the
|
|
258
|
+
* guard) coreutils forwards TERM rather than the `-s` signal, npx dies,
|
|
259
|
+
* and the guard exits before any KILL fires — so a SIGTERM-immune CLI
|
|
260
|
+
* grandchild still escapes in those two cases.
|
|
261
|
+
* If the sibling probe predates the resolveUnixGuardTimeout export (version
|
|
262
|
+
* skew), the adapter degrades to the unwrapped invocation instead of
|
|
263
|
+
* throwing. Windows is deliberately NOT wrapped — there is no coreutils
|
|
264
|
+
* timeout to resolve there and the resolver's self-test spawns /bin/sh — so
|
|
265
|
+
* on win32 (the npx.cmd path) and whenever the guard resolves to null (e.g.
|
|
266
|
+
* macOS without Homebrew coreutils — reported once under GITNEXUS_DEBUG)
|
|
267
|
+
* the argv stays byte-identical to the pre-wrap invocation.
|
|
224
268
|
*/
|
|
225
269
|
function runGitNexusCli(cliPath, args, cwd, timeout) {
|
|
226
270
|
const isWin = process.platform === 'win32';
|
|
271
|
+
// Version-skew guard (#2163 follow-up review): an older sibling probe
|
|
272
|
+
// without the resolveUnixGuardTimeout export must degrade to the unwrapped
|
|
273
|
+
// invocation — a TypeError here would be swallowed by the caller's catch
|
|
274
|
+
// and silently kill the augment.
|
|
275
|
+
const guard =
|
|
276
|
+
isWin || typeof resolveUnixGuardTimeout !== 'function' ? null : resolveUnixGuardTimeout();
|
|
277
|
+
if (!isWin && !guard && !unguardedCliWarned && isDebugEnabled()) {
|
|
278
|
+
// Diagnose the "stays unwrapped" Unix paths once per hook process: no
|
|
279
|
+
// usable coreutils timeout/gtimeout (e.g. macOS without Homebrew
|
|
280
|
+
// coreutils), GITNEXUS_HOOK_TIMEOUT_PATH=disabled, or probe skew above.
|
|
281
|
+
unguardedCliWarned = true;
|
|
282
|
+
process.stderr.write(
|
|
283
|
+
'[GitNexus hook] no usable timeout/gtimeout guard; augment CLI child runs unguarded\n',
|
|
284
|
+
);
|
|
285
|
+
}
|
|
227
286
|
if (cliPath) {
|
|
228
|
-
|
|
287
|
+
const [cmd, cmdArgs] = guard
|
|
288
|
+
? [
|
|
289
|
+
guard,
|
|
290
|
+
['-k', '1', String(Math.ceil(timeout / 1000) + 1), process.execPath, cliPath, ...args],
|
|
291
|
+
]
|
|
292
|
+
: [process.execPath, [cliPath, ...args]];
|
|
293
|
+
return spawnSync(cmd, cmdArgs, {
|
|
229
294
|
encoding: 'utf-8',
|
|
230
295
|
timeout,
|
|
231
296
|
cwd,
|
|
@@ -233,8 +298,27 @@ function runGitNexusCli(cliPath, args, cwd, timeout) {
|
|
|
233
298
|
windowsHide: true,
|
|
234
299
|
});
|
|
235
300
|
}
|
|
236
|
-
// On Windows, invoke npx.cmd directly (no shell needed)
|
|
237
|
-
|
|
301
|
+
// On Windows, invoke npx.cmd directly (no shell needed). A non-null guard
|
|
302
|
+
// implies non-Windows, so the wrapped arm can hardcode plain `npx`. The
|
|
303
|
+
// wrapped arm leads with `-s KILL` (NOT TERM-first like the direct branch
|
|
304
|
+
// above): the CLI here is a grandchild behind npx — see the docblock.
|
|
305
|
+
const [cmd, cmdArgs] = guard
|
|
306
|
+
? [
|
|
307
|
+
guard,
|
|
308
|
+
[
|
|
309
|
+
'-s',
|
|
310
|
+
'KILL',
|
|
311
|
+
'-k',
|
|
312
|
+
'1',
|
|
313
|
+
String(Math.ceil((timeout + 5000) / 1000) + 1),
|
|
314
|
+
'npx',
|
|
315
|
+
'-y',
|
|
316
|
+
'gitnexus',
|
|
317
|
+
...args,
|
|
318
|
+
],
|
|
319
|
+
]
|
|
320
|
+
: [isWin ? 'npx.cmd' : 'npx', ['-y', 'gitnexus', ...args]];
|
|
321
|
+
return spawnSync(cmd, cmdArgs, {
|
|
238
322
|
encoding: 'utf-8',
|
|
239
323
|
timeout: timeout + 5000,
|
|
240
324
|
cwd,
|
|
@@ -20,13 +20,18 @@
|
|
|
20
20
|
* it 1s later — orphan lifetime is bounded at ~3s instead of unbounded.
|
|
21
21
|
* - GITNEXUS_HOOK_TIMEOUT_PATH: the sentinel value `disabled` switches the
|
|
22
22
|
* wrapper off deterministically; any other value is adopted only when it
|
|
23
|
-
* exists AND passes a one-shot `-k` self-test — otherwise
|
|
24
|
-
* THROUGH to the built-in candidate list (first self-test
|
|
25
|
-
* no malformed value of any shape can silently disable
|
|
23
|
+
* exists AND passes a one-shot `-k` exit-propagation self-test — otherwise
|
|
24
|
+
* resolution FALLS THROUGH to the built-in candidate list (first self-test
|
|
25
|
+
* pass wins), so no malformed value of any shape can silently disable
|
|
26
|
+
* orphan containment.
|
|
26
27
|
* - The gitnexus server is lazy-open + sticky-hold: an idle MCP server holds
|
|
27
28
|
* ZERO lbug fds until the repo's first MCP query, then keeps the fd open.
|
|
28
29
|
* A probe before that first query is therefore always false — a known,
|
|
29
30
|
* pre-existing race, not a bug in this probe.
|
|
31
|
+
* - resolveUnixGuardTimeout is exported so the hook adapters can wrap the
|
|
32
|
+
* `gitnexus augment` CLI child — the longest-lived hook subprocess (7s
|
|
33
|
+
* local / 12s npx inner budgets) — in the same guard; see runGitNexusCli
|
|
34
|
+
* in the adapters (#2163 follow-up).
|
|
30
35
|
*/
|
|
31
36
|
|
|
32
37
|
const fs = require('fs');
|
|
@@ -70,38 +75,53 @@ let unixGuardTimeoutCache;
|
|
|
70
75
|
|
|
71
76
|
/**
|
|
72
77
|
* Resolve a coreutils `timeout`/`gtimeout` binary to wrap lsof/ps with
|
|
73
|
-
* (#2163).
|
|
78
|
+
* (#2163). Unix-only by contract: the probe's win32 dispatch returns before
|
|
79
|
+
* reaching it, and the exported callers (the adapters' runGitNexusCli,
|
|
80
|
+
* #2163 follow-up) must check the platform first — the self-test below
|
|
81
|
+
* spawns /bin/sh. The memoized result is module-wide, so probe and adapter
|
|
82
|
+
* share one lazy self-test per hook process.
|
|
74
83
|
*
|
|
75
84
|
* GITNEXUS_HOOK_TIMEOUT_PATH semantics: the sentinel `disabled` turns the
|
|
76
85
|
* wrapper off; any other value is only a CANDIDATE — an existing file path
|
|
77
|
-
* is tried first, but it must pass the `-k` self-test to
|
|
78
|
-
* failure (non-existent path, directory, non-executable
|
|
79
|
-
* without `-k` support, …) resolution
|
|
80
|
-
* candidates below, tried in order, first
|
|
81
|
-
* strictly stronger than the sibling
|
|
82
|
-
* GITNEXUS_HOOK_PS_PATH overrides (which only
|
|
83
|
-
* value of ANY shape can silently disable
|
|
86
|
+
* is tried first, but it must pass the `-k` exit-propagation self-test to
|
|
87
|
+
* be adopted. On any failure (non-existent path, directory, non-executable
|
|
88
|
+
* file, wrapper without `-k` support, always-exit-0 stub, …) resolution
|
|
89
|
+
* falls through to the built-in candidates below, tried in order, first
|
|
90
|
+
* self-test pass wins. This is strictly stronger than the sibling
|
|
91
|
+
* GITNEXUS_HOOK_LSOF_PATH / GITNEXUS_HOOK_PS_PATH overrides (which only
|
|
92
|
+
* check existence): no bad env value of ANY shape can silently disable
|
|
93
|
+
* orphan containment.
|
|
84
94
|
*
|
|
85
95
|
* Lazy self-test: candidates are probed only when the lsof/ps fallback is
|
|
86
96
|
* first reached, and the result is memoized. A candidate is adopted only
|
|
87
|
-
* when `timeout -k 1 1 /bin/sh -c
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
97
|
+
* when `timeout -k 1 1 /bin/sh -c 'exit 42'` exits 42 — i.e. it must RUN
|
|
98
|
+
* the wrapped command AND PROPAGATE its exit status. This rejects two
|
|
99
|
+
* failure shapes: wrappers without the coreutils `-k` flag — busybox <1.34,
|
|
100
|
+
* toybox, broken symlinks — which would exit with a usage error without
|
|
101
|
+
* ever running lsof, silently converting the lsof-ETIMEDOUT fail-closed
|
|
102
|
+
* contract into fail-open (#1492 regression); and always-exit-0 stubs
|
|
103
|
+
* (/bin/true shapes), which would otherwise be adopted and "succeed" every
|
|
104
|
+
* wrapped spawn instantly without running it — a constant no-owner probe
|
|
105
|
+
* answer and, worse, a silently dead augment (status 0, empty stderr passes
|
|
106
|
+
* the adapters' success check with no context; #2163 follow-up review).
|
|
107
|
+
* Only when EVERY candidate fails does the probe fall back to the unwrapped
|
|
108
|
+
* status quo (memoized null). busybox ≥1.34 passes the test and is fully
|
|
109
|
+
* usable for everything THIS file spawns (lsof/ps are the guard's direct
|
|
110
|
+
* children) and for the adapters' direct-exec arm. The adapters' npx arm
|
|
111
|
+
* additionally relies on coreutils' process-GROUP signalling for its
|
|
112
|
+
* `-s KILL` grandchild reaping; busybox signals only its direct child, and
|
|
113
|
+
* this self-test deliberately does not probe that capability — see the
|
|
114
|
+
* adapter docblocks for the residual-gap statement.
|
|
95
115
|
*/
|
|
96
116
|
function passesGuardSelfTest(guard) {
|
|
97
117
|
try {
|
|
98
|
-
const selfTest = spawnSync(guard, ['-k', '1', '1', '/bin/sh', '-c', '
|
|
118
|
+
const selfTest = spawnSync(guard, ['-k', '1', '1', '/bin/sh', '-c', 'exit 42'], {
|
|
99
119
|
encoding: 'utf-8',
|
|
100
120
|
timeout: 3000,
|
|
101
121
|
stdio: ['ignore', 'ignore', 'ignore'],
|
|
102
122
|
windowsHide: true,
|
|
103
123
|
});
|
|
104
|
-
return !selfTest.error && selfTest.status ===
|
|
124
|
+
return !selfTest.error && selfTest.status === 42;
|
|
105
125
|
} catch {
|
|
106
126
|
return false;
|
|
107
127
|
}
|
|
@@ -359,4 +379,16 @@ function hasGitNexusDbLockedByGitNexusServer(dbPath, myPid) {
|
|
|
359
379
|
|
|
360
380
|
module.exports = {
|
|
361
381
|
hasGitNexusDbLockedByGitNexusServer,
|
|
382
|
+
// #2163 follow-up: the hook adapters wrap the augment CLI in the same
|
|
383
|
+
// guard. Returns a self-tested wrapper path — the built-in candidates are
|
|
384
|
+
// always absolute; a GITNEXUS_HOOK_TIMEOUT_PATH override is adopted as the
|
|
385
|
+
// exact string that passed the self-test. Same string is also the same
|
|
386
|
+
// RESOLUTION for absolute paths and for slashless names (PATH lookup is
|
|
387
|
+
// cwd-independent); a slash-containing RELATIVE override, however, is
|
|
388
|
+
// existsSync-checked and self-tested against this process's cwd while the
|
|
389
|
+
// adapters spawn the CLI with a `cwd` option (chdir-before-exec), so such
|
|
390
|
+
// a value can pass here yet ENOENT at the augment call site — set the
|
|
391
|
+
// override to an absolute path. Returns null when the wrapper is
|
|
392
|
+
// disabled/unavailable. Never call on win32 (see its JSDoc).
|
|
393
|
+
resolveUnixGuardTimeout,
|
|
362
394
|
};
|
package/package.json
CHANGED