apple-mail-mcp 2.20.21 → 2.20.23
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/build/cli.js +58 -21
- package/build/index.js +89 -28
- package/package.json +1 -1
package/build/cli.js
CHANGED
|
@@ -12265,44 +12265,77 @@ function sensitiveRoots() {
|
|
|
12265
12265
|
}
|
|
12266
12266
|
});
|
|
12267
12267
|
}
|
|
12268
|
+
function homeCanonical() {
|
|
12269
|
+
try {
|
|
12270
|
+
return canonicalize(homedir());
|
|
12271
|
+
} catch {
|
|
12272
|
+
return void 0;
|
|
12273
|
+
}
|
|
12274
|
+
}
|
|
12268
12275
|
function isWithinRoot(candidate, root) {
|
|
12269
12276
|
return candidate === root || candidate.startsWith(root + sep);
|
|
12270
12277
|
}
|
|
12271
12278
|
function hasHiddenPathSegment(candidate) {
|
|
12272
12279
|
return candidate.split(sep).some((segment) => segment.startsWith(".") && segment.length > 1);
|
|
12273
12280
|
}
|
|
12274
|
-
function
|
|
12281
|
+
function userLibraryTail(candidate) {
|
|
12282
|
+
const segments = candidate.split(sep).filter(Boolean);
|
|
12283
|
+
const lower = segments.map((segment) => segment.toLowerCase());
|
|
12284
|
+
const volumeTops = [
|
|
12285
|
+
0,
|
|
12286
|
+
lower[0] === "volumes" ? 2 : -1,
|
|
12287
|
+
lower.slice(0, 3).join("/") === "system/volumes/data" ? 3 : -1
|
|
12288
|
+
];
|
|
12289
|
+
for (const i of volumeTops) {
|
|
12290
|
+
if (i >= 0 && lower[i] === "users" && lower[i + 2] === "library") return lower.slice(i + 3);
|
|
12291
|
+
}
|
|
12292
|
+
const home = homeCanonical() ?? resolve3(homedir());
|
|
12293
|
+
if (home === sep || !isWithinRoot(candidate, home)) return void 0;
|
|
12294
|
+
const relative = candidate.slice(home.length).split(sep).filter(Boolean);
|
|
12295
|
+
if (relative[0]?.toLowerCase() !== "library") return void 0;
|
|
12296
|
+
return relative.slice(1).map((segment) => segment.toLowerCase());
|
|
12297
|
+
}
|
|
12298
|
+
function isLibraryDocument(tail) {
|
|
12299
|
+
return LIBRARY_DOCUMENT_FOLDERS.some((folder) => folder.every((name2, i) => tail[i] === name2));
|
|
12300
|
+
}
|
|
12301
|
+
function isProtectedPath(candidate, exemptRoots = []) {
|
|
12275
12302
|
if (hasHiddenPathSegment(candidate)) return true;
|
|
12276
12303
|
if (sensitiveRoots().some((root) => isWithinRoot(candidate, root))) return true;
|
|
12277
|
-
|
|
12304
|
+
const tail = userLibraryTail(candidate);
|
|
12305
|
+
if (!tail || isLibraryDocument(tail)) return false;
|
|
12306
|
+
const home = homeCanonical();
|
|
12307
|
+
return !exemptRoots.some(
|
|
12308
|
+
(root) => isWithinRoot(candidate, root) && !(home && isWithinRoot(home, root))
|
|
12309
|
+
);
|
|
12310
|
+
}
|
|
12311
|
+
function canonicalTmpdir() {
|
|
12278
12312
|
try {
|
|
12279
|
-
|
|
12313
|
+
return [canonicalize(tmpdir())];
|
|
12280
12314
|
} catch {
|
|
12281
|
-
|
|
12315
|
+
return [];
|
|
12282
12316
|
}
|
|
12283
|
-
if (!isWithinRoot(candidate, home)) return false;
|
|
12284
|
-
const relative = candidate.slice(home.length).split(sep).filter(Boolean);
|
|
12285
|
-
return relative.length >= 4 && relative[0].toLowerCase() === "library" && relative[1].toLowerCase() === "application support" && relative.at(-1)?.toLowerCase() === "config.json";
|
|
12286
12317
|
}
|
|
12287
|
-
function
|
|
12288
|
-
const raw = env[ATTACHMENT_READ_ROOTS_ENV];
|
|
12289
|
-
const extraRoots = raw === void 0 ? [] : raw.split(delimiter2).map((root) => root.trim()).filter(Boolean);
|
|
12290
|
-
const requested = [...DEFAULT_ATTACHMENT_READ_ROOTS, ...extraRoots];
|
|
12291
|
-
for (const root of requested) {
|
|
12292
|
-
if (!isAbsolute(root)) {
|
|
12293
|
-
throw new Error(`${ATTACHMENT_READ_ROOTS_ENV} entries must be absolute paths.`);
|
|
12294
|
-
}
|
|
12295
|
-
}
|
|
12318
|
+
function canonicalRoots(requested) {
|
|
12296
12319
|
const resolved = [];
|
|
12297
12320
|
for (const root of requested) {
|
|
12298
12321
|
try {
|
|
12299
12322
|
const canonical = canonicalize(resolve3(root));
|
|
12300
|
-
if (!resolved.includes(canonical)) resolved.push(canonical);
|
|
12323
|
+
if (canonical !== sep && !resolved.includes(canonical)) resolved.push(canonical);
|
|
12301
12324
|
} catch {
|
|
12302
12325
|
}
|
|
12303
12326
|
}
|
|
12304
12327
|
return resolved;
|
|
12305
12328
|
}
|
|
12329
|
+
function explicitRoots(env) {
|
|
12330
|
+
const raw = env[ATTACHMENT_READ_ROOTS_ENV];
|
|
12331
|
+
const extraRoots = raw === void 0 ? [] : raw.split(delimiter2).map((root) => root.trim()).filter(Boolean);
|
|
12332
|
+
for (const root of extraRoots) {
|
|
12333
|
+
if (!isAbsolute(root)) {
|
|
12334
|
+
throw new Error(`${ATTACHMENT_READ_ROOTS_ENV} entries must be absolute paths.`);
|
|
12335
|
+
}
|
|
12336
|
+
}
|
|
12337
|
+
return canonicalRoots(extraRoots);
|
|
12338
|
+
}
|
|
12306
12339
|
function resolveAttachmentReadPath(filePath, env = process.env) {
|
|
12307
12340
|
if (!isAbsolute(filePath)) {
|
|
12308
12341
|
throw new Error(`Attachment path must be absolute: "${filePath}"`);
|
|
@@ -12321,19 +12354,22 @@ function resolveAttachmentReadPath(filePath, env = process.env) {
|
|
|
12321
12354
|
if (error2 instanceof Error && error2.message.includes("not a regular file")) throw error2;
|
|
12322
12355
|
throw new Error(`Attachment file not found: "${filePath}"`);
|
|
12323
12356
|
}
|
|
12324
|
-
|
|
12357
|
+
const explicit = explicitRoots(env);
|
|
12358
|
+
const exempt = [...explicit, ...canonicalTmpdir()];
|
|
12359
|
+
if (isProtectedPath(canonical, exempt) || isProtectedPath(resolve3(filePath), exempt)) {
|
|
12325
12360
|
throw new Error(
|
|
12326
|
-
`Attachment path is in a protected location: "${filePath}". Hidden files
|
|
12361
|
+
`Attachment path is in a protected location: "${filePath}". Hidden files, credential locations and user Library folders (except iCloud Drive and CloudStorage) cannot be sent as attachments unless ${ATTACHMENT_READ_ROOTS_ENV} names a root for them.`
|
|
12327
12362
|
);
|
|
12328
12363
|
}
|
|
12329
|
-
|
|
12364
|
+
const roots = [...canonicalRoots(DEFAULT_ATTACHMENT_READ_ROOTS), ...explicit];
|
|
12365
|
+
if (!roots.some((root) => isWithinRoot(canonical, root))) {
|
|
12330
12366
|
throw new Error(
|
|
12331
12367
|
`Attachment path is outside the allowed read roots: "${filePath}". Use an ordinary home-directory, /Volumes, or temporary path, or configure ${ATTACHMENT_READ_ROOTS_ENV} for an additional explicit root.`
|
|
12332
12368
|
);
|
|
12333
12369
|
}
|
|
12334
12370
|
return canonical;
|
|
12335
12371
|
}
|
|
12336
|
-
var ATTACHMENT_READ_ROOTS_ENV, DEFAULT_ATTACHMENT_READ_ROOTS, SENSITIVE_HOME_ROOTS;
|
|
12372
|
+
var ATTACHMENT_READ_ROOTS_ENV, DEFAULT_ATTACHMENT_READ_ROOTS, SENSITIVE_HOME_ROOTS, LIBRARY_DOCUMENT_FOLDERS;
|
|
12337
12373
|
var init_attachmentReadPolicy = __esm({
|
|
12338
12374
|
"src/utils/attachmentReadPolicy.ts"() {
|
|
12339
12375
|
"use strict";
|
|
@@ -12345,6 +12381,7 @@ var init_attachmentReadPolicy = __esm({
|
|
|
12345
12381
|
join(homedir(), ".config", "gh"),
|
|
12346
12382
|
join(homedir(), "Library", "Keychains")
|
|
12347
12383
|
];
|
|
12384
|
+
LIBRARY_DOCUMENT_FOLDERS = [["mobile documents", "com~apple~clouddocs"], ["cloudstorage"]];
|
|
12348
12385
|
}
|
|
12349
12386
|
});
|
|
12350
12387
|
|
package/build/index.js
CHANGED
|
@@ -64830,44 +64830,77 @@ function sensitiveRoots() {
|
|
|
64830
64830
|
}
|
|
64831
64831
|
});
|
|
64832
64832
|
}
|
|
64833
|
+
function homeCanonical() {
|
|
64834
|
+
try {
|
|
64835
|
+
return canonicalize(homedir());
|
|
64836
|
+
} catch {
|
|
64837
|
+
return void 0;
|
|
64838
|
+
}
|
|
64839
|
+
}
|
|
64833
64840
|
function isWithinRoot(candidate, root) {
|
|
64834
64841
|
return candidate === root || candidate.startsWith(root + sep);
|
|
64835
64842
|
}
|
|
64836
64843
|
function hasHiddenPathSegment(candidate) {
|
|
64837
64844
|
return candidate.split(sep).some((segment) => segment.startsWith(".") && segment.length > 1);
|
|
64838
64845
|
}
|
|
64839
|
-
function
|
|
64846
|
+
function userLibraryTail(candidate) {
|
|
64847
|
+
const segments = candidate.split(sep).filter(Boolean);
|
|
64848
|
+
const lower = segments.map((segment) => segment.toLowerCase());
|
|
64849
|
+
const volumeTops = [
|
|
64850
|
+
0,
|
|
64851
|
+
lower[0] === "volumes" ? 2 : -1,
|
|
64852
|
+
lower.slice(0, 3).join("/") === "system/volumes/data" ? 3 : -1
|
|
64853
|
+
];
|
|
64854
|
+
for (const i of volumeTops) {
|
|
64855
|
+
if (i >= 0 && lower[i] === "users" && lower[i + 2] === "library") return lower.slice(i + 3);
|
|
64856
|
+
}
|
|
64857
|
+
const home = homeCanonical() ?? resolve3(homedir());
|
|
64858
|
+
if (home === sep || !isWithinRoot(candidate, home)) return void 0;
|
|
64859
|
+
const relative = candidate.slice(home.length).split(sep).filter(Boolean);
|
|
64860
|
+
if (relative[0]?.toLowerCase() !== "library") return void 0;
|
|
64861
|
+
return relative.slice(1).map((segment) => segment.toLowerCase());
|
|
64862
|
+
}
|
|
64863
|
+
function isLibraryDocument(tail) {
|
|
64864
|
+
return LIBRARY_DOCUMENT_FOLDERS.some((folder) => folder.every((name2, i) => tail[i] === name2));
|
|
64865
|
+
}
|
|
64866
|
+
function isProtectedPath(candidate, exemptRoots = []) {
|
|
64840
64867
|
if (hasHiddenPathSegment(candidate)) return true;
|
|
64841
64868
|
if (sensitiveRoots().some((root) => isWithinRoot(candidate, root))) return true;
|
|
64842
|
-
|
|
64869
|
+
const tail = userLibraryTail(candidate);
|
|
64870
|
+
if (!tail || isLibraryDocument(tail)) return false;
|
|
64871
|
+
const home = homeCanonical();
|
|
64872
|
+
return !exemptRoots.some(
|
|
64873
|
+
(root) => isWithinRoot(candidate, root) && !(home && isWithinRoot(home, root))
|
|
64874
|
+
);
|
|
64875
|
+
}
|
|
64876
|
+
function canonicalTmpdir() {
|
|
64843
64877
|
try {
|
|
64844
|
-
|
|
64878
|
+
return [canonicalize(tmpdir())];
|
|
64845
64879
|
} catch {
|
|
64846
|
-
|
|
64880
|
+
return [];
|
|
64847
64881
|
}
|
|
64848
|
-
if (!isWithinRoot(candidate, home)) return false;
|
|
64849
|
-
const relative = candidate.slice(home.length).split(sep).filter(Boolean);
|
|
64850
|
-
return relative.length >= 4 && relative[0].toLowerCase() === "library" && relative[1].toLowerCase() === "application support" && relative.at(-1)?.toLowerCase() === "config.json";
|
|
64851
64882
|
}
|
|
64852
|
-
function
|
|
64853
|
-
const raw = env[ATTACHMENT_READ_ROOTS_ENV];
|
|
64854
|
-
const extraRoots = raw === void 0 ? [] : raw.split(delimiter2).map((root) => root.trim()).filter(Boolean);
|
|
64855
|
-
const requested = [...DEFAULT_ATTACHMENT_READ_ROOTS, ...extraRoots];
|
|
64856
|
-
for (const root of requested) {
|
|
64857
|
-
if (!isAbsolute(root)) {
|
|
64858
|
-
throw new Error(`${ATTACHMENT_READ_ROOTS_ENV} entries must be absolute paths.`);
|
|
64859
|
-
}
|
|
64860
|
-
}
|
|
64883
|
+
function canonicalRoots(requested) {
|
|
64861
64884
|
const resolved = [];
|
|
64862
64885
|
for (const root of requested) {
|
|
64863
64886
|
try {
|
|
64864
64887
|
const canonical = canonicalize(resolve3(root));
|
|
64865
|
-
if (!resolved.includes(canonical)) resolved.push(canonical);
|
|
64888
|
+
if (canonical !== sep && !resolved.includes(canonical)) resolved.push(canonical);
|
|
64866
64889
|
} catch {
|
|
64867
64890
|
}
|
|
64868
64891
|
}
|
|
64869
64892
|
return resolved;
|
|
64870
64893
|
}
|
|
64894
|
+
function explicitRoots(env) {
|
|
64895
|
+
const raw = env[ATTACHMENT_READ_ROOTS_ENV];
|
|
64896
|
+
const extraRoots = raw === void 0 ? [] : raw.split(delimiter2).map((root) => root.trim()).filter(Boolean);
|
|
64897
|
+
for (const root of extraRoots) {
|
|
64898
|
+
if (!isAbsolute(root)) {
|
|
64899
|
+
throw new Error(`${ATTACHMENT_READ_ROOTS_ENV} entries must be absolute paths.`);
|
|
64900
|
+
}
|
|
64901
|
+
}
|
|
64902
|
+
return canonicalRoots(extraRoots);
|
|
64903
|
+
}
|
|
64871
64904
|
function resolveAttachmentReadPath(filePath, env = process.env) {
|
|
64872
64905
|
if (!isAbsolute(filePath)) {
|
|
64873
64906
|
throw new Error(`Attachment path must be absolute: "${filePath}"`);
|
|
@@ -64886,19 +64919,22 @@ function resolveAttachmentReadPath(filePath, env = process.env) {
|
|
|
64886
64919
|
if (error3 instanceof Error && error3.message.includes("not a regular file")) throw error3;
|
|
64887
64920
|
throw new Error(`Attachment file not found: "${filePath}"`);
|
|
64888
64921
|
}
|
|
64889
|
-
|
|
64922
|
+
const explicit = explicitRoots(env);
|
|
64923
|
+
const exempt = [...explicit, ...canonicalTmpdir()];
|
|
64924
|
+
if (isProtectedPath(canonical, exempt) || isProtectedPath(resolve3(filePath), exempt)) {
|
|
64890
64925
|
throw new Error(
|
|
64891
|
-
`Attachment path is in a protected location: "${filePath}". Hidden files
|
|
64926
|
+
`Attachment path is in a protected location: "${filePath}". Hidden files, credential locations and user Library folders (except iCloud Drive and CloudStorage) cannot be sent as attachments unless ${ATTACHMENT_READ_ROOTS_ENV} names a root for them.`
|
|
64892
64927
|
);
|
|
64893
64928
|
}
|
|
64894
|
-
|
|
64929
|
+
const roots = [...canonicalRoots(DEFAULT_ATTACHMENT_READ_ROOTS), ...explicit];
|
|
64930
|
+
if (!roots.some((root) => isWithinRoot(canonical, root))) {
|
|
64895
64931
|
throw new Error(
|
|
64896
64932
|
`Attachment path is outside the allowed read roots: "${filePath}". Use an ordinary home-directory, /Volumes, or temporary path, or configure ${ATTACHMENT_READ_ROOTS_ENV} for an additional explicit root.`
|
|
64897
64933
|
);
|
|
64898
64934
|
}
|
|
64899
64935
|
return canonical;
|
|
64900
64936
|
}
|
|
64901
|
-
var ATTACHMENT_READ_ROOTS_ENV, DEFAULT_ATTACHMENT_READ_ROOTS, SENSITIVE_HOME_ROOTS;
|
|
64937
|
+
var ATTACHMENT_READ_ROOTS_ENV, DEFAULT_ATTACHMENT_READ_ROOTS, SENSITIVE_HOME_ROOTS, LIBRARY_DOCUMENT_FOLDERS;
|
|
64902
64938
|
var init_attachmentReadPolicy = __esm({
|
|
64903
64939
|
"src/utils/attachmentReadPolicy.ts"() {
|
|
64904
64940
|
"use strict";
|
|
@@ -64910,6 +64946,7 @@ var init_attachmentReadPolicy = __esm({
|
|
|
64910
64946
|
join(homedir(), ".config", "gh"),
|
|
64911
64947
|
join(homedir(), "Library", "Keychains")
|
|
64912
64948
|
];
|
|
64949
|
+
LIBRARY_DOCUMENT_FOLDERS = [["mobile documents", "com~apple~clouddocs"], ["cloudstorage"]];
|
|
64913
64950
|
}
|
|
64914
64951
|
});
|
|
64915
64952
|
|
|
@@ -83781,19 +83818,32 @@ function mailboxPathFragment(mailboxVar, outputVar) {
|
|
|
83781
83818
|
set _pathParent to _pathNext
|
|
83782
83819
|
end repeat`;
|
|
83783
83820
|
}
|
|
83784
|
-
function mailboxLookupFragment(collExpr, path3, outputVar) {
|
|
83785
|
-
|
|
83786
|
-
|
|
83821
|
+
function mailboxLookupFragment(collExpr, path3, outputVar, hintIndex) {
|
|
83822
|
+
const safePath = escapeForAppleScript(path3);
|
|
83823
|
+
const hint = hintIndex === void 0 ? "" : `
|
|
83824
|
+
try
|
|
83825
|
+
set _mbc to item ${hintIndex} of (${collExpr})
|
|
83826
|
+
set _mbcPath to ""
|
|
83827
|
+
${mailboxPathFragment("_mbc", "_mbcPath")}
|
|
83828
|
+
ignoring case
|
|
83829
|
+
if _mbcPath is "${safePath}" then set ${outputVar} to _mbc
|
|
83830
|
+
end ignoring
|
|
83831
|
+
end try`;
|
|
83832
|
+
const walk = `
|
|
83787
83833
|
repeat with _mbc in (${collExpr})
|
|
83788
83834
|
set _mbcPath to ""
|
|
83789
83835
|
${mailboxPathFragment("_mbc", "_mbcPath")}
|
|
83790
83836
|
ignoring case
|
|
83791
|
-
if _mbcPath is "${
|
|
83837
|
+
if _mbcPath is "${safePath}" then
|
|
83792
83838
|
set ${outputVar} to _mbc
|
|
83793
83839
|
exit repeat
|
|
83794
83840
|
end if
|
|
83795
83841
|
end ignoring
|
|
83796
83842
|
end repeat`;
|
|
83843
|
+
return `
|
|
83844
|
+
set ${outputVar} to missing value${hint}${hintIndex === void 0 ? walk : `
|
|
83845
|
+
if ${outputVar} is missing value then${walk}
|
|
83846
|
+
end if`}`;
|
|
83797
83847
|
}
|
|
83798
83848
|
var MAILBOX_ALIASES = {
|
|
83799
83849
|
inbox: ["INBOX", "Inbox", "inbox"],
|
|
@@ -84789,6 +84839,17 @@ ${indent}end try${this.sanitizeFragment("_uacct", indent)}${this.sanitizeFragmen
|
|
|
84789
84839
|
resolveMailbox(mailbox, account) {
|
|
84790
84840
|
return resolveAppleMailboxPath(mailbox, this.getCachedMailboxNames(account));
|
|
84791
84841
|
}
|
|
84842
|
+
/**
|
|
84843
|
+
* The 1-based position of `path` in the account's cached mailbox list, which is
|
|
84844
|
+
* built by walking the account's `mailboxes` in order. `mailboxLookupFragment`
|
|
84845
|
+
* checks that one mailbox first instead of working out the path of every one.
|
|
84846
|
+
* Undefined when the path is not in the list; a stale position is harmless, the
|
|
84847
|
+
* fragment verifies the path and falls back to the full walk.
|
|
84848
|
+
*/
|
|
84849
|
+
mailboxIndexHint(account, path3) {
|
|
84850
|
+
const index = this.getCachedMailboxNames(account).indexOf(path3);
|
|
84851
|
+
return index >= 0 ? index + 1 : void 0;
|
|
84852
|
+
}
|
|
84792
84853
|
/**
|
|
84793
84854
|
* Non-throwing `resolveMailbox`, for callers comparing mailbox names AFTER
|
|
84794
84855
|
* a destructive op already ran (the forensics `sameMailbox` closures). An
|
|
@@ -84955,7 +85016,7 @@ ${indent}end try${this.sanitizeFragment("_uacct", indent)}${this.sanitizeFragmen
|
|
|
84955
85016
|
${dateSetup}set outputText to ""
|
|
84956
85017
|
set _timedOut to false
|
|
84957
85018
|
set _notSearched to ""
|
|
84958
|
-
${mailboxLookupFragment(mbIter, targetMailbox, "theMailbox")}
|
|
85019
|
+
${mailboxLookupFragment(mbIter, targetMailbox, "theMailbox", this.mailboxIndexHint(targetAccount, targetMailbox))}
|
|
84959
85020
|
set msgCount to 0
|
|
84960
85021
|
if theMailbox is missing value then
|
|
84961
85022
|
set _timedOut to true
|
|
@@ -85687,7 +85748,7 @@ ${indent}end try${this.sanitizeFragment("_uacct", indent)}${this.sanitizeFragmen
|
|
|
85687
85748
|
set outputText to ""
|
|
85688
85749
|
set _timedOut to false
|
|
85689
85750
|
set _notSearched to ""
|
|
85690
|
-
${mailboxLookupFragment(mbIter, targetMailbox, "theMailbox")}
|
|
85751
|
+
${mailboxLookupFragment(mbIter, targetMailbox, "theMailbox", this.mailboxIndexHint(targetAccount, targetMailbox))}
|
|
85691
85752
|
set msgCount to 0
|
|
85692
85753
|
set skipped to 0
|
|
85693
85754
|
if theMailbox is missing value then
|
|
@@ -87183,7 +87244,7 @@ ${this.errorEmit(" ")}
|
|
|
87183
87244
|
const targetAccount = this.resolveAccount(account);
|
|
87184
87245
|
const targetMailbox = this.resolveMailbox(mailbox || "INBOX", targetAccount);
|
|
87185
87246
|
const command = `
|
|
87186
|
-
${mailboxLookupFragment("mailboxes", targetMailbox, "theMailbox")}
|
|
87247
|
+
${mailboxLookupFragment("mailboxes", targetMailbox, "theMailbox", this.mailboxIndexHint(targetAccount, targetMailbox))}
|
|
87187
87248
|
if theMailbox is missing value then error "Mailbox \\"${escapeForAppleScript(targetMailbox)}\\" not found"
|
|
87188
87249
|
return unread count of theMailbox`;
|
|
87189
87250
|
const script = buildAccountScopedScript(targetAccount, command);
|
package/package.json
CHANGED