@qodalis/cli-files 2.0.0-beta.12 → 2.0.0-beta.14
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/package.json +2 -2
- package/public-api.d.mts +89 -2
- package/public-api.d.ts +89 -2
- package/public-api.js +357 -60
- package/public-api.js.map +1 -1
- package/public-api.mjs +352 -62
- package/public-api.mjs.map +1 -1
- package/umd/index.global.js +209 -39
package/public-api.js
CHANGED
|
@@ -6,7 +6,7 @@ var cliCore = require('@qodalis/cli-core');
|
|
|
6
6
|
var IFileSystemService_TOKEN = "cli-file-system-service";
|
|
7
7
|
|
|
8
8
|
// src/lib/version.ts
|
|
9
|
-
var LIBRARY_VERSION = "2.0.0-beta.
|
|
9
|
+
var LIBRARY_VERSION = "2.0.0-beta.14";
|
|
10
10
|
var API_VERSION = 2;
|
|
11
11
|
|
|
12
12
|
// src/lib/processors/cli-ls-command-processor.ts
|
|
@@ -36,7 +36,7 @@ var CliLsCommandProcessor = class {
|
|
|
36
36
|
];
|
|
37
37
|
}
|
|
38
38
|
async processCommand(command, context) {
|
|
39
|
-
const fs = context.services.
|
|
39
|
+
const fs = context.services.getRequired(
|
|
40
40
|
IFileSystemService_TOKEN
|
|
41
41
|
);
|
|
42
42
|
const targetPath = command.value || fs.getCurrentDirectory();
|
|
@@ -107,7 +107,7 @@ var CliCdCommandProcessor = class {
|
|
|
107
107
|
this.metadata = { icon: "\u{1F4C1}", module: "file management" };
|
|
108
108
|
}
|
|
109
109
|
async processCommand(command, context) {
|
|
110
|
-
const fs = context.services.
|
|
110
|
+
const fs = context.services.getRequired(
|
|
111
111
|
IFileSystemService_TOKEN
|
|
112
112
|
);
|
|
113
113
|
const target = command.value || "~";
|
|
@@ -128,7 +128,7 @@ var CliPwdCommandProcessor = class {
|
|
|
128
128
|
this.metadata = { icon: "\u{1F4C2}", module: "file management" };
|
|
129
129
|
}
|
|
130
130
|
async processCommand(command, context) {
|
|
131
|
-
const fs = context.services.
|
|
131
|
+
const fs = context.services.getRequired(
|
|
132
132
|
IFileSystemService_TOKEN
|
|
133
133
|
);
|
|
134
134
|
context.writer.writeln(fs.getCurrentDirectory());
|
|
@@ -154,7 +154,7 @@ var CliMkdirCommandProcessor = class {
|
|
|
154
154
|
];
|
|
155
155
|
}
|
|
156
156
|
async processCommand(command, context) {
|
|
157
|
-
const fs = context.services.
|
|
157
|
+
const fs = context.services.getRequired(
|
|
158
158
|
IFileSystemService_TOKEN
|
|
159
159
|
);
|
|
160
160
|
const path = command.value;
|
|
@@ -182,7 +182,7 @@ var CliRmdirCommandProcessor = class {
|
|
|
182
182
|
this.metadata = { icon: "\u{1F5D1}", module: "file management" };
|
|
183
183
|
}
|
|
184
184
|
async processCommand(command, context) {
|
|
185
|
-
const fs = context.services.
|
|
185
|
+
const fs = context.services.getRequired(
|
|
186
186
|
IFileSystemService_TOKEN
|
|
187
187
|
);
|
|
188
188
|
const path = command.value;
|
|
@@ -226,7 +226,7 @@ var CliTouchCommandProcessor = class {
|
|
|
226
226
|
this.metadata = { icon: "\u{1F4C4}", module: "file management" };
|
|
227
227
|
}
|
|
228
228
|
async processCommand(command, context) {
|
|
229
|
-
const fs = context.services.
|
|
229
|
+
const fs = context.services.getRequired(
|
|
230
230
|
IFileSystemService_TOKEN
|
|
231
231
|
);
|
|
232
232
|
const path = command.value;
|
|
@@ -253,7 +253,7 @@ var CliCatCommandProcessor = class {
|
|
|
253
253
|
this.metadata = { icon: "\u{1F4D6}", module: "file management" };
|
|
254
254
|
}
|
|
255
255
|
async processCommand(command, context) {
|
|
256
|
-
const fs = context.services.
|
|
256
|
+
const fs = context.services.getRequired(
|
|
257
257
|
IFileSystemService_TOKEN
|
|
258
258
|
);
|
|
259
259
|
const path = command.value;
|
|
@@ -292,7 +292,7 @@ var CliEchoCommandProcessor = class {
|
|
|
292
292
|
}
|
|
293
293
|
return;
|
|
294
294
|
}
|
|
295
|
-
const fs = context.services.
|
|
295
|
+
const fs = context.services.getRequired(
|
|
296
296
|
IFileSystemService_TOKEN
|
|
297
297
|
);
|
|
298
298
|
let text;
|
|
@@ -364,7 +364,7 @@ var CliRmCommandProcessor = class {
|
|
|
364
364
|
];
|
|
365
365
|
}
|
|
366
366
|
async processCommand(command, context) {
|
|
367
|
-
const fs = context.services.
|
|
367
|
+
const fs = context.services.getRequired(
|
|
368
368
|
IFileSystemService_TOKEN
|
|
369
369
|
);
|
|
370
370
|
const path = command.value;
|
|
@@ -413,7 +413,7 @@ var CliCpCommandProcessor = class {
|
|
|
413
413
|
];
|
|
414
414
|
}
|
|
415
415
|
async processCommand(command, context) {
|
|
416
|
-
const fs = context.services.
|
|
416
|
+
const fs = context.services.getRequired(
|
|
417
417
|
IFileSystemService_TOKEN
|
|
418
418
|
);
|
|
419
419
|
const recursive = command.args["recursive"] || command.args["r"] || command.args["R"];
|
|
@@ -449,7 +449,7 @@ var CliMvCommandProcessor = class {
|
|
|
449
449
|
this.metadata = { icon: "\u{1F4E6}", module: "file management" };
|
|
450
450
|
}
|
|
451
451
|
async processCommand(command, context) {
|
|
452
|
-
const fs = context.services.
|
|
452
|
+
const fs = context.services.getRequired(
|
|
453
453
|
IFileSystemService_TOKEN
|
|
454
454
|
);
|
|
455
455
|
const parts = this.parseArgs(command);
|
|
@@ -472,6 +472,14 @@ var CliMvCommandProcessor = class {
|
|
|
472
472
|
return raw.split(/\s+/).filter((t) => t && !t.startsWith("--") && !t.startsWith("-"));
|
|
473
473
|
}
|
|
474
474
|
};
|
|
475
|
+
function readableChildren(fs, path) {
|
|
476
|
+
try {
|
|
477
|
+
return fs.listDirectory(path);
|
|
478
|
+
} catch (e) {
|
|
479
|
+
if (cliCore.isCliAccessDeniedError(e)) return null;
|
|
480
|
+
throw e;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
475
483
|
var CliTreeCommandProcessor = class {
|
|
476
484
|
constructor() {
|
|
477
485
|
this.command = "tree";
|
|
@@ -491,7 +499,7 @@ var CliTreeCommandProcessor = class {
|
|
|
491
499
|
];
|
|
492
500
|
}
|
|
493
501
|
async processCommand(command, context) {
|
|
494
|
-
const fs = context.services.
|
|
502
|
+
const fs = context.services.getRequired(
|
|
495
503
|
IFileSystemService_TOKEN
|
|
496
504
|
);
|
|
497
505
|
const targetPath = command.value || fs.getCurrentDirectory();
|
|
@@ -518,7 +526,8 @@ var CliTreeCommandProcessor = class {
|
|
|
518
526
|
)
|
|
519
527
|
);
|
|
520
528
|
const counts = { dirs: 0, files: 0 };
|
|
521
|
-
this.
|
|
529
|
+
this.fs = fs;
|
|
530
|
+
this.printTree(node, "", true, 0, maxDepth, context, counts, resolvedPath);
|
|
522
531
|
context.writer.writeln();
|
|
523
532
|
context.writer.writeln(
|
|
524
533
|
`${counts.dirs} directories, ${counts.files} files`
|
|
@@ -527,11 +536,15 @@ var CliTreeCommandProcessor = class {
|
|
|
527
536
|
context.writer.writeError(e.message);
|
|
528
537
|
}
|
|
529
538
|
}
|
|
530
|
-
printTree(node, prefix, isRoot, depth, maxDepth, context, counts) {
|
|
531
|
-
if (
|
|
539
|
+
printTree(node, prefix, isRoot, depth, maxDepth, context, counts, path = "/") {
|
|
540
|
+
if (depth >= maxDepth) {
|
|
532
541
|
return;
|
|
533
542
|
}
|
|
534
|
-
const
|
|
543
|
+
const visible = this.fs ? readableChildren(this.fs, path) : node.children;
|
|
544
|
+
if (!visible) {
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
const children = [...visible].sort((a, b) => {
|
|
535
548
|
if (a.type !== b.type) {
|
|
536
549
|
return a.type === "directory" ? -1 : 1;
|
|
537
550
|
}
|
|
@@ -556,7 +569,8 @@ var CliTreeCommandProcessor = class {
|
|
|
556
569
|
depth + 1,
|
|
557
570
|
maxDepth,
|
|
558
571
|
context,
|
|
559
|
-
counts
|
|
572
|
+
counts,
|
|
573
|
+
path === "/" ? `/${child.name}` : `${path}/${child.name}`
|
|
560
574
|
);
|
|
561
575
|
} else {
|
|
562
576
|
counts.files++;
|
|
@@ -584,7 +598,7 @@ var CliHeadCommandProcessor = class {
|
|
|
584
598
|
];
|
|
585
599
|
}
|
|
586
600
|
async processCommand(command, context) {
|
|
587
|
-
const fs = context.services.
|
|
601
|
+
const fs = context.services.getRequired(
|
|
588
602
|
IFileSystemService_TOKEN
|
|
589
603
|
);
|
|
590
604
|
const count = parseInt(
|
|
@@ -649,7 +663,7 @@ var CliTailCommandProcessor = class {
|
|
|
649
663
|
];
|
|
650
664
|
}
|
|
651
665
|
async processCommand(command, context) {
|
|
652
|
-
const fs = context.services.
|
|
666
|
+
const fs = context.services.getRequired(
|
|
653
667
|
IFileSystemService_TOKEN
|
|
654
668
|
);
|
|
655
669
|
const count = parseInt(
|
|
@@ -728,7 +742,7 @@ var CliWcCommandProcessor = class {
|
|
|
728
742
|
];
|
|
729
743
|
}
|
|
730
744
|
async processCommand(command, context) {
|
|
731
|
-
const fs = context.services.
|
|
745
|
+
const fs = context.services.getRequired(
|
|
732
746
|
IFileSystemService_TOKEN
|
|
733
747
|
);
|
|
734
748
|
const showLines = command.args["lines"] || command.args["l"] || false;
|
|
@@ -822,7 +836,7 @@ var CliFindCommandProcessor = class {
|
|
|
822
836
|
];
|
|
823
837
|
}
|
|
824
838
|
async processCommand(command, context) {
|
|
825
|
-
const fs = context.services.
|
|
839
|
+
const fs = context.services.getRequired(
|
|
826
840
|
IFileSystemService_TOKEN
|
|
827
841
|
);
|
|
828
842
|
const searchPath = this.parseSearchPath(command) || fs.getCurrentDirectory();
|
|
@@ -846,6 +860,7 @@ var CliFindCommandProcessor = class {
|
|
|
846
860
|
const resolvedBase = fs.resolvePath(searchPath);
|
|
847
861
|
const nameRegex = namePattern ? this.globToRegex(namePattern) : null;
|
|
848
862
|
const results = [];
|
|
863
|
+
this.fs = fs;
|
|
849
864
|
this.walk(
|
|
850
865
|
node,
|
|
851
866
|
resolvedBase,
|
|
@@ -873,8 +888,9 @@ var CliFindCommandProcessor = class {
|
|
|
873
888
|
return new RegExp(`^${regexStr}$`, "i");
|
|
874
889
|
}
|
|
875
890
|
walk(node, currentPath, depth, maxDepth, nameRegex, typeFilter, results) {
|
|
876
|
-
|
|
877
|
-
|
|
891
|
+
const children = this.fs ? readableChildren(this.fs, currentPath) : node.children;
|
|
892
|
+
if (!children) return;
|
|
893
|
+
for (const child of children) {
|
|
878
894
|
const childPath = currentPath === "/" ? `/${child.name}` : `${currentPath}/${child.name}`;
|
|
879
895
|
let matches = true;
|
|
880
896
|
if (nameRegex && !nameRegex.test(child.name)) {
|
|
@@ -957,7 +973,7 @@ var CliGrepCommandProcessor = class {
|
|
|
957
973
|
];
|
|
958
974
|
}
|
|
959
975
|
async processCommand(command, context) {
|
|
960
|
-
const fs = context.services.
|
|
976
|
+
const fs = context.services.getRequired(
|
|
961
977
|
IFileSystemService_TOKEN
|
|
962
978
|
);
|
|
963
979
|
const ignoreCase = command.args["ignore-case"] || command.args["i"] || false;
|
|
@@ -1105,8 +1121,9 @@ var CliGrepCommandProcessor = class {
|
|
|
1105
1121
|
};
|
|
1106
1122
|
}
|
|
1107
1123
|
collectFiles(fs, basePath, node, result) {
|
|
1108
|
-
|
|
1109
|
-
|
|
1124
|
+
const children = readableChildren(fs, basePath);
|
|
1125
|
+
if (!children) return;
|
|
1126
|
+
for (const child of children) {
|
|
1110
1127
|
const childPath = basePath === "/" ? `/${child.name}` : `${basePath}/${child.name}`;
|
|
1111
1128
|
if (child.type === "file") {
|
|
1112
1129
|
result.push(childPath);
|
|
@@ -1127,7 +1144,7 @@ var CliTacCommandProcessor = class {
|
|
|
1127
1144
|
this.metadata = { icon: "\u{1F504}", module: "file management" };
|
|
1128
1145
|
}
|
|
1129
1146
|
async processCommand(command, context) {
|
|
1130
|
-
const fs = context.services.
|
|
1147
|
+
const fs = context.services.getRequired(
|
|
1131
1148
|
IFileSystemService_TOKEN
|
|
1132
1149
|
);
|
|
1133
1150
|
const paths = this.parsePaths(command);
|
|
@@ -1299,7 +1316,7 @@ var CliSortCommandProcessor = class {
|
|
|
1299
1316
|
];
|
|
1300
1317
|
}
|
|
1301
1318
|
async processCommand(command, context) {
|
|
1302
|
-
const fs = context.services.
|
|
1319
|
+
const fs = context.services.getRequired(
|
|
1303
1320
|
IFileSystemService_TOKEN
|
|
1304
1321
|
);
|
|
1305
1322
|
const reverse = !!command.args["r"] || !!command.args["reverse"];
|
|
@@ -1452,7 +1469,7 @@ var CliUniqCommandProcessor = class {
|
|
|
1452
1469
|
];
|
|
1453
1470
|
}
|
|
1454
1471
|
async processCommand(command, context) {
|
|
1455
|
-
const fs = context.services.
|
|
1472
|
+
const fs = context.services.getRequired(
|
|
1456
1473
|
IFileSystemService_TOKEN
|
|
1457
1474
|
);
|
|
1458
1475
|
const showCount = !!command.args["c"] || !!command.args["count"];
|
|
@@ -1583,7 +1600,7 @@ var CliCutCommandProcessor = class {
|
|
|
1583
1600
|
];
|
|
1584
1601
|
}
|
|
1585
1602
|
async processCommand(command, context) {
|
|
1586
|
-
const fs = context.services.
|
|
1603
|
+
const fs = context.services.getRequired(
|
|
1587
1604
|
IFileSystemService_TOKEN
|
|
1588
1605
|
);
|
|
1589
1606
|
const delimiter = command.args["d"] || command.args["delimiter"] || " ";
|
|
@@ -1733,7 +1750,7 @@ var CliPasteCommandProcessor = class {
|
|
|
1733
1750
|
];
|
|
1734
1751
|
}
|
|
1735
1752
|
async processCommand(command, context) {
|
|
1736
|
-
const fs = context.services.
|
|
1753
|
+
const fs = context.services.getRequired(
|
|
1737
1754
|
IFileSystemService_TOKEN
|
|
1738
1755
|
);
|
|
1739
1756
|
const delimiter = command.args["d"] || command.args["delimiters"] || " ";
|
|
@@ -1900,7 +1917,7 @@ var CliTrCommandProcessor = class {
|
|
|
1900
1917
|
];
|
|
1901
1918
|
}
|
|
1902
1919
|
async processCommand(command, context) {
|
|
1903
|
-
const fs = context.services.
|
|
1920
|
+
const fs = context.services.getRequired(
|
|
1904
1921
|
IFileSystemService_TOKEN
|
|
1905
1922
|
);
|
|
1906
1923
|
const raw = command.value || "";
|
|
@@ -2055,7 +2072,7 @@ var CliStatCommandProcessor = class {
|
|
|
2055
2072
|
this.parameters = [];
|
|
2056
2073
|
}
|
|
2057
2074
|
async processCommand(command, context) {
|
|
2058
|
-
const fs = context.services.
|
|
2075
|
+
const fs = context.services.getRequired(
|
|
2059
2076
|
IFileSystemService_TOKEN
|
|
2060
2077
|
);
|
|
2061
2078
|
const paths = this.parsePaths(command);
|
|
@@ -2065,6 +2082,10 @@ var CliStatCommandProcessor = class {
|
|
|
2065
2082
|
}
|
|
2066
2083
|
for (const path of paths) {
|
|
2067
2084
|
const resolved = fs.resolvePath(path);
|
|
2085
|
+
if (fs.canTraverse && !fs.canTraverse(resolved)) {
|
|
2086
|
+
context.writer.writeError(`stat: cannot stat '${path}': Permission denied`);
|
|
2087
|
+
continue;
|
|
2088
|
+
}
|
|
2068
2089
|
const node = fs.getNode(resolved);
|
|
2069
2090
|
if (!node) {
|
|
2070
2091
|
context.writer.writeError(
|
|
@@ -2108,7 +2129,7 @@ var CliChmodCommandProcessor = class {
|
|
|
2108
2129
|
];
|
|
2109
2130
|
}
|
|
2110
2131
|
async processCommand(command, context) {
|
|
2111
|
-
const fs = context.services.
|
|
2132
|
+
const fs = context.services.getRequired(
|
|
2112
2133
|
IFileSystemService_TOKEN
|
|
2113
2134
|
);
|
|
2114
2135
|
const { recursive, mode, paths } = this.parseArgs(command);
|
|
@@ -2228,7 +2249,7 @@ var CliChownCommandProcessor = class {
|
|
|
2228
2249
|
];
|
|
2229
2250
|
}
|
|
2230
2251
|
async processCommand(command, context) {
|
|
2231
|
-
const fs = context.services.
|
|
2252
|
+
const fs = context.services.getRequired(
|
|
2232
2253
|
IFileSystemService_TOKEN
|
|
2233
2254
|
);
|
|
2234
2255
|
const { recursive, ownerSpec, paths } = this.parseArgs(command);
|
|
@@ -2323,7 +2344,7 @@ var CliDuCommandProcessor = class {
|
|
|
2323
2344
|
];
|
|
2324
2345
|
}
|
|
2325
2346
|
async processCommand(command, context) {
|
|
2326
|
-
const fs = context.services.
|
|
2347
|
+
const fs = context.services.getRequired(
|
|
2327
2348
|
IFileSystemService_TOKEN
|
|
2328
2349
|
);
|
|
2329
2350
|
const { humanReadable, summaryOnly, maxDepth, paths } = this.parseArgs(command);
|
|
@@ -2333,6 +2354,10 @@ var CliDuCommandProcessor = class {
|
|
|
2333
2354
|
}
|
|
2334
2355
|
for (const path of paths) {
|
|
2335
2356
|
const resolved = fs.resolvePath(path);
|
|
2357
|
+
if (fs.canTraverse && !fs.canTraverse(resolved)) {
|
|
2358
|
+
context.writer.writeError(`du: cannot access '${path}': Permission denied`);
|
|
2359
|
+
continue;
|
|
2360
|
+
}
|
|
2336
2361
|
const node = fs.getNode(resolved);
|
|
2337
2362
|
if (!node) {
|
|
2338
2363
|
context.writer.writeError(
|
|
@@ -2346,6 +2371,7 @@ var CliDuCommandProcessor = class {
|
|
|
2346
2371
|
);
|
|
2347
2372
|
} else {
|
|
2348
2373
|
const entries = [];
|
|
2374
|
+
this.fs = fs;
|
|
2349
2375
|
this.collectSizes(node, resolved, 0, maxDepth, summaryOnly, entries);
|
|
2350
2376
|
for (const entry of entries) {
|
|
2351
2377
|
context.writer.writeln(
|
|
@@ -2368,8 +2394,9 @@ var CliDuCommandProcessor = class {
|
|
|
2368
2394
|
return node.size;
|
|
2369
2395
|
}
|
|
2370
2396
|
let total = 0;
|
|
2371
|
-
|
|
2372
|
-
|
|
2397
|
+
const children = this.fs ? readableChildren(this.fs, path) : node.children;
|
|
2398
|
+
if (children) {
|
|
2399
|
+
for (const child of children) {
|
|
2373
2400
|
const childPath = path === "/" ? `/${child.name}` : `${path}/${child.name}`;
|
|
2374
2401
|
total += this.collectSizes(
|
|
2375
2402
|
child,
|
|
@@ -2420,7 +2447,7 @@ var CliLnCommandProcessor = class {
|
|
|
2420
2447
|
];
|
|
2421
2448
|
}
|
|
2422
2449
|
async processCommand(command, context) {
|
|
2423
|
-
const fs = context.services.
|
|
2450
|
+
const fs = context.services.getRequired(
|
|
2424
2451
|
IFileSystemService_TOKEN
|
|
2425
2452
|
);
|
|
2426
2453
|
const { symbolic, target, linkName } = this.parseArgs(command);
|
|
@@ -2494,7 +2521,7 @@ var CliSedCommandProcessor = class {
|
|
|
2494
2521
|
];
|
|
2495
2522
|
}
|
|
2496
2523
|
async processCommand(command, context) {
|
|
2497
|
-
const fs = context.services.
|
|
2524
|
+
const fs = context.services.getRequired(
|
|
2498
2525
|
IFileSystemService_TOKEN
|
|
2499
2526
|
);
|
|
2500
2527
|
const { inPlace, suppress, expressions, filePath } = this.parseRawCommand(command);
|
|
@@ -2837,7 +2864,7 @@ var CliAwkCommandProcessor = class {
|
|
|
2837
2864
|
];
|
|
2838
2865
|
}
|
|
2839
2866
|
async processCommand(command, context) {
|
|
2840
|
-
const fs = context.services.
|
|
2867
|
+
const fs = context.services.getRequired(
|
|
2841
2868
|
IFileSystemService_TOKEN
|
|
2842
2869
|
);
|
|
2843
2870
|
const { program, filePath, fieldSep } = this.parseArgs(command);
|
|
@@ -3313,7 +3340,7 @@ var CliDiffCommandProcessor = class {
|
|
|
3313
3340
|
];
|
|
3314
3341
|
}
|
|
3315
3342
|
async processCommand(command, context) {
|
|
3316
|
-
const fs = context.services.
|
|
3343
|
+
const fs = context.services.getRequired(
|
|
3317
3344
|
IFileSystemService_TOKEN
|
|
3318
3345
|
);
|
|
3319
3346
|
const unified = command.args["unified"] || command.args["u"] || false;
|
|
@@ -3499,7 +3526,7 @@ var CliTeeCommandProcessor = class {
|
|
|
3499
3526
|
];
|
|
3500
3527
|
}
|
|
3501
3528
|
async processCommand(command, context) {
|
|
3502
|
-
const fs = context.services.
|
|
3529
|
+
const fs = context.services.getRequired(
|
|
3503
3530
|
IFileSystemService_TOKEN
|
|
3504
3531
|
);
|
|
3505
3532
|
const append = command.args["append"] || command.args["a"] || false;
|
|
@@ -3593,7 +3620,7 @@ var CliXargsCommandProcessor = class {
|
|
|
3593
3620
|
];
|
|
3594
3621
|
}
|
|
3595
3622
|
async processCommand(command, context) {
|
|
3596
|
-
const fs = context.services.
|
|
3623
|
+
const fs = context.services.getRequired(
|
|
3597
3624
|
IFileSystemService_TOKEN
|
|
3598
3625
|
);
|
|
3599
3626
|
const replaceStr = command.args["replace"] || command.args["I"] || null;
|
|
@@ -3706,7 +3733,7 @@ var CliShCommandProcessor = class {
|
|
|
3706
3733
|
];
|
|
3707
3734
|
}
|
|
3708
3735
|
async processCommand(command, context) {
|
|
3709
|
-
const fs = context.services.
|
|
3736
|
+
const fs = context.services.getRequired(
|
|
3710
3737
|
IFileSystemService_TOKEN
|
|
3711
3738
|
);
|
|
3712
3739
|
let filePath = (command.value || "").trim();
|
|
@@ -3832,14 +3859,14 @@ function substituteVariables(input, variables) {
|
|
|
3832
3859
|
);
|
|
3833
3860
|
return result;
|
|
3834
3861
|
}
|
|
3835
|
-
|
|
3836
|
-
// src/lib/services/indexed-db-file-system.service.ts
|
|
3837
3862
|
var DB_NAME = "qodalis-cli-filesystem";
|
|
3838
3863
|
var STORE_NAME = "filesystem";
|
|
3839
3864
|
var ROOT_KEY = "root";
|
|
3840
3865
|
var CWD_KEY = "cwd";
|
|
3841
3866
|
var HOME_KEY = "home";
|
|
3842
3867
|
var DEFAULT_HOME = "/";
|
|
3868
|
+
var SYSTEM_OWNERSHIP = { uid: "root", gid: "admin" };
|
|
3869
|
+
var ANONYMOUS_OWNERSHIP = { uid: "nobody", gid: "nogroup" };
|
|
3843
3870
|
function createSeedFileSystem() {
|
|
3844
3871
|
const now = Date.now();
|
|
3845
3872
|
return {
|
|
@@ -3887,6 +3914,88 @@ var IndexedDbFileSystemService = class {
|
|
|
3887
3914
|
this.homePath = DEFAULT_HOME;
|
|
3888
3915
|
this.currentUid = null;
|
|
3889
3916
|
this.currentGroups = [];
|
|
3917
|
+
this.systemDepth = 0;
|
|
3918
|
+
}
|
|
3919
|
+
// --- Access control ---
|
|
3920
|
+
setAccessPolicy(policy) {
|
|
3921
|
+
this.accessPolicy = policy;
|
|
3922
|
+
}
|
|
3923
|
+
runAsSystem(fn) {
|
|
3924
|
+
this.systemDepth++;
|
|
3925
|
+
try {
|
|
3926
|
+
return fn();
|
|
3927
|
+
} finally {
|
|
3928
|
+
this.systemDepth--;
|
|
3929
|
+
}
|
|
3930
|
+
}
|
|
3931
|
+
reassignOwner(fromUid, toUid) {
|
|
3932
|
+
const from = fromUid.toLowerCase();
|
|
3933
|
+
if (!from || !toUid) return 0;
|
|
3934
|
+
let changed = 0;
|
|
3935
|
+
const visit = (node) => {
|
|
3936
|
+
if (node.ownership && node.ownership.uid.toLowerCase() === from) {
|
|
3937
|
+
node.ownership = { ...node.ownership, uid: toUid };
|
|
3938
|
+
changed++;
|
|
3939
|
+
}
|
|
3940
|
+
node.children?.forEach(visit);
|
|
3941
|
+
};
|
|
3942
|
+
visit(this.root);
|
|
3943
|
+
return changed;
|
|
3944
|
+
}
|
|
3945
|
+
canTraverse(path) {
|
|
3946
|
+
try {
|
|
3947
|
+
this.assertTraverse(this.resolvePath(path), "stat", path);
|
|
3948
|
+
return true;
|
|
3949
|
+
} catch {
|
|
3950
|
+
return false;
|
|
3951
|
+
}
|
|
3952
|
+
}
|
|
3953
|
+
get enforcing() {
|
|
3954
|
+
return !!this.accessPolicy && this.systemDepth === 0;
|
|
3955
|
+
}
|
|
3956
|
+
deny(op, path, reason = "Permission denied") {
|
|
3957
|
+
throw new cliCore.CliAccessDeniedError(`${op}: ${path}: ${reason}`);
|
|
3958
|
+
}
|
|
3959
|
+
/** `x` on every existing directory on the way to `resolved` (excluding the target). */
|
|
3960
|
+
assertTraverse(resolved, op, display) {
|
|
3961
|
+
if (!this.enforcing) return;
|
|
3962
|
+
const parts = resolved.split("/").filter(Boolean);
|
|
3963
|
+
let current = this.root;
|
|
3964
|
+
for (let i = 0; i < parts.length; i++) {
|
|
3965
|
+
if (!current || current.type !== "directory") return;
|
|
3966
|
+
if (!this.accessPolicy.canAccess(current, "execute")) this.deny(op, display);
|
|
3967
|
+
current = current.children?.find((c) => c.name === parts[i]);
|
|
3968
|
+
}
|
|
3969
|
+
}
|
|
3970
|
+
assertAccess(node, action, op, display) {
|
|
3971
|
+
if (this.enforcing && !this.accessPolicy.canAccess(node, action)) this.deny(op, display);
|
|
3972
|
+
}
|
|
3973
|
+
/** Recursive delete: every directory inside must allow removing its entries (`w` + `x`, readable). */
|
|
3974
|
+
assertRemovableTree(dir, dirPath, display) {
|
|
3975
|
+
if (!this.enforcing) return;
|
|
3976
|
+
this.assertAccess(dir, "read", "rm", display);
|
|
3977
|
+
this.assertAccess(dir, "execute", "rm", display);
|
|
3978
|
+
if ((dir.children?.length ?? 0) > 0) this.assertAccess(dir, "write", "rm", display);
|
|
3979
|
+
for (const child of dir.children ?? []) {
|
|
3980
|
+
if (child.type === "directory") this.assertRemovableTree(child, `${dirPath}/${child.name}`, display);
|
|
3981
|
+
}
|
|
3982
|
+
}
|
|
3983
|
+
/** Copying reads everything it copies: `r` on every file, `r` + `x` on every directory. */
|
|
3984
|
+
assertReadableTree(node, display) {
|
|
3985
|
+
if (!this.enforcing) return;
|
|
3986
|
+
this.assertAccess(node, "read", "cp", display);
|
|
3987
|
+
if (node.type !== "directory") return;
|
|
3988
|
+
this.assertAccess(node, "execute", "cp", display);
|
|
3989
|
+
for (const child of node.children ?? []) {
|
|
3990
|
+
this.assertReadableTree(child, `${display.replace(/\/+$/, "")}/${child.name}`);
|
|
3991
|
+
}
|
|
3992
|
+
}
|
|
3993
|
+
/** Creating/removing entries needs `w` and `x` on the containing directory. */
|
|
3994
|
+
assertModifyDir(dir, dirPath, op, display) {
|
|
3995
|
+
if (!this.enforcing) return;
|
|
3996
|
+
this.assertTraverse(dirPath, op, display);
|
|
3997
|
+
this.assertAccess(dir, "execute", op, display);
|
|
3998
|
+
this.assertAccess(dir, "write", op, display);
|
|
3890
3999
|
}
|
|
3891
4000
|
// --- User context ---
|
|
3892
4001
|
setCurrentUser(uid, groups) {
|
|
@@ -3894,7 +4003,9 @@ var IndexedDbFileSystemService = class {
|
|
|
3894
4003
|
this.currentGroups = groups;
|
|
3895
4004
|
}
|
|
3896
4005
|
getDefaultOwnership() {
|
|
3897
|
-
if (!this.currentUid)
|
|
4006
|
+
if (!this.currentUid) {
|
|
4007
|
+
return this.accessPolicy ? { ...ANONYMOUS_OWNERSHIP } : void 0;
|
|
4008
|
+
}
|
|
3898
4009
|
return {
|
|
3899
4010
|
uid: this.currentUid,
|
|
3900
4011
|
gid: this.currentGroups[0] || "users"
|
|
@@ -3913,6 +4024,8 @@ var IndexedDbFileSystemService = class {
|
|
|
3913
4024
|
if (node.type !== "directory") {
|
|
3914
4025
|
throw new Error(`cd: ${path}: Not a directory`);
|
|
3915
4026
|
}
|
|
4027
|
+
this.assertTraverse(resolved, "cd", path);
|
|
4028
|
+
this.assertAccess(node, "execute", "cd", path);
|
|
3916
4029
|
this.cwd = resolved;
|
|
3917
4030
|
}
|
|
3918
4031
|
getHomePath() {
|
|
@@ -3969,6 +4082,8 @@ var IndexedDbFileSystemService = class {
|
|
|
3969
4082
|
if (node.type !== "directory") {
|
|
3970
4083
|
throw new Error(`ls: ${path}: Not a directory`);
|
|
3971
4084
|
}
|
|
4085
|
+
this.assertTraverse(this.resolvePath(path), "ls", path);
|
|
4086
|
+
this.assertAccess(node, "read", "ls", path);
|
|
3972
4087
|
return node.children || [];
|
|
3973
4088
|
}
|
|
3974
4089
|
readFile(path) {
|
|
@@ -3979,6 +4094,8 @@ var IndexedDbFileSystemService = class {
|
|
|
3979
4094
|
if (node.type === "directory") {
|
|
3980
4095
|
throw new Error(`cat: ${path}: Is a directory`);
|
|
3981
4096
|
}
|
|
4097
|
+
this.assertTraverse(this.resolvePath(path), "cat", path);
|
|
4098
|
+
this.assertAccess(node, "read", "cat", path);
|
|
3982
4099
|
return node.content ?? "";
|
|
3983
4100
|
}
|
|
3984
4101
|
exists(path) {
|
|
@@ -4003,6 +4120,10 @@ var IndexedDbFileSystemService = class {
|
|
|
4003
4120
|
}
|
|
4004
4121
|
let child = current.children.find((c) => c.name === part);
|
|
4005
4122
|
if (!child) {
|
|
4123
|
+
if (this.enforcing) {
|
|
4124
|
+
this.assertAccess(current, "execute", "mkdir", path);
|
|
4125
|
+
this.assertAccess(current, "write", "mkdir", path);
|
|
4126
|
+
}
|
|
4006
4127
|
const now = Date.now();
|
|
4007
4128
|
child = {
|
|
4008
4129
|
name: part,
|
|
@@ -4018,6 +4139,8 @@ var IndexedDbFileSystemService = class {
|
|
|
4018
4139
|
current.modifiedAt = now;
|
|
4019
4140
|
} else if (child.type !== "directory") {
|
|
4020
4141
|
throw new Error(`mkdir: ${part}: Not a directory`);
|
|
4142
|
+
} else {
|
|
4143
|
+
this.assertAccess(child, "execute", "mkdir", path);
|
|
4021
4144
|
}
|
|
4022
4145
|
current = child;
|
|
4023
4146
|
}
|
|
@@ -4036,6 +4159,7 @@ var IndexedDbFileSystemService = class {
|
|
|
4036
4159
|
if (parent.children?.find((c) => c.name === dirName)) {
|
|
4037
4160
|
throw new Error(`mkdir: ${path}: File exists`);
|
|
4038
4161
|
}
|
|
4162
|
+
this.assertModifyDir(parent, parentPath, "mkdir", path);
|
|
4039
4163
|
const now = Date.now();
|
|
4040
4164
|
if (!parent.children) {
|
|
4041
4165
|
parent.children = [];
|
|
@@ -4070,6 +4194,12 @@ var IndexedDbFileSystemService = class {
|
|
|
4070
4194
|
}
|
|
4071
4195
|
const existing = parent.children?.find((c) => c.name === fileName);
|
|
4072
4196
|
const now = Date.now();
|
|
4197
|
+
if (existing) {
|
|
4198
|
+
this.assertTraverse(resolved, "touch", path);
|
|
4199
|
+
this.assertAccess(existing, "write", "touch", path);
|
|
4200
|
+
} else {
|
|
4201
|
+
this.assertModifyDir(parent, parentPath, "touch", path);
|
|
4202
|
+
}
|
|
4073
4203
|
if (existing) {
|
|
4074
4204
|
existing.modifiedAt = now;
|
|
4075
4205
|
} else {
|
|
@@ -4096,6 +4226,8 @@ var IndexedDbFileSystemService = class {
|
|
|
4096
4226
|
if (node.type === "directory") {
|
|
4097
4227
|
throw new Error(`write: ${path}: Is a directory`);
|
|
4098
4228
|
}
|
|
4229
|
+
this.assertTraverse(resolved, "write", path);
|
|
4230
|
+
this.assertAccess(node, "write", "write", path);
|
|
4099
4231
|
const now = Date.now();
|
|
4100
4232
|
node.content = append ? (node.content ?? "") + content : content;
|
|
4101
4233
|
node.size = new Blob([node.content]).size;
|
|
@@ -4124,6 +4256,8 @@ var IndexedDbFileSystemService = class {
|
|
|
4124
4256
|
if (target.type === "directory" && !recursive) {
|
|
4125
4257
|
throw new Error(`rm: ${path}: Is a directory`);
|
|
4126
4258
|
}
|
|
4259
|
+
this.assertModifyDir(parent, parentPath, "rm", path);
|
|
4260
|
+
if (target.type === "directory") this.assertRemovableTree(target, resolved, path);
|
|
4127
4261
|
parent.children.splice(index, 1);
|
|
4128
4262
|
parent.modifiedAt = Date.now();
|
|
4129
4263
|
}
|
|
@@ -4138,10 +4272,13 @@ var IndexedDbFileSystemService = class {
|
|
|
4138
4272
|
`cp: -r not specified; omitting directory '${src}'`
|
|
4139
4273
|
);
|
|
4140
4274
|
}
|
|
4275
|
+
this.assertTraverse(srcResolved, "cp", src);
|
|
4276
|
+
this.assertReadableTree(srcNode, src);
|
|
4141
4277
|
const destResolved = this.resolvePath(dest);
|
|
4142
4278
|
const destNode = this.getNode(destResolved);
|
|
4143
4279
|
if (destNode && destNode.type === "directory") {
|
|
4144
|
-
|
|
4280
|
+
this.assertModifyDir(destNode, destResolved, "cp", dest);
|
|
4281
|
+
const clone = this.ownedByCurrentUser(this.cloneNode(srcNode));
|
|
4145
4282
|
if (!destNode.children) {
|
|
4146
4283
|
destNode.children = [];
|
|
4147
4284
|
}
|
|
@@ -4161,7 +4298,8 @@ var IndexedDbFileSystemService = class {
|
|
|
4161
4298
|
if (!parent || parent.type !== "directory") {
|
|
4162
4299
|
throw new Error(`cp: ${parentPath}: No such file or directory`);
|
|
4163
4300
|
}
|
|
4164
|
-
|
|
4301
|
+
this.assertModifyDir(parent, parentPath, "cp", dest);
|
|
4302
|
+
const clone = this.ownedByCurrentUser(this.cloneNode(srcNode));
|
|
4165
4303
|
clone.name = newName;
|
|
4166
4304
|
if (!parent.children) {
|
|
4167
4305
|
parent.children = [];
|
|
@@ -4193,10 +4331,12 @@ var IndexedDbFileSystemService = class {
|
|
|
4193
4331
|
if (!srcParent || !srcParent.children) {
|
|
4194
4332
|
throw new Error(`mv: ${src}: No such file or directory`);
|
|
4195
4333
|
}
|
|
4334
|
+
this.assertModifyDir(srcParent, srcParentPath, "mv", src);
|
|
4196
4335
|
const srcIndex = srcParent.children.findIndex(
|
|
4197
4336
|
(c) => c.name === srcNode.name
|
|
4198
4337
|
);
|
|
4199
4338
|
if (destNode && destNode.type === "directory") {
|
|
4339
|
+
this.assertModifyDir(destNode, destResolved, "mv", dest);
|
|
4200
4340
|
if (!destNode.children) {
|
|
4201
4341
|
destNode.children = [];
|
|
4202
4342
|
}
|
|
@@ -4219,6 +4359,7 @@ var IndexedDbFileSystemService = class {
|
|
|
4219
4359
|
`mv: ${destParentPath}: No such file or directory`
|
|
4220
4360
|
);
|
|
4221
4361
|
}
|
|
4362
|
+
this.assertModifyDir(destParent, destParentPath, "mv", dest);
|
|
4222
4363
|
srcParent.children.splice(srcIndex, 1);
|
|
4223
4364
|
srcNode.name = newName;
|
|
4224
4365
|
if (!destParent.children) {
|
|
@@ -4242,6 +4383,9 @@ var IndexedDbFileSystemService = class {
|
|
|
4242
4383
|
if (!node) {
|
|
4243
4384
|
throw new Error(`chmod: ${path}: No such file or directory`);
|
|
4244
4385
|
}
|
|
4386
|
+
if (this.enforcing && !this.accessPolicy.canChangeOwnership(node)) {
|
|
4387
|
+
this.deny("chmod", path, "Operation not permitted");
|
|
4388
|
+
}
|
|
4245
4389
|
node.permissions = permissions;
|
|
4246
4390
|
}
|
|
4247
4391
|
chown(path, ownership) {
|
|
@@ -4250,6 +4394,9 @@ var IndexedDbFileSystemService = class {
|
|
|
4250
4394
|
if (!node) {
|
|
4251
4395
|
throw new Error(`chown: ${path}: No such file or directory`);
|
|
4252
4396
|
}
|
|
4397
|
+
if (this.enforcing && !this.accessPolicy.canChangeOwnership(node, ownership)) {
|
|
4398
|
+
this.deny("chown", path, "Operation not permitted");
|
|
4399
|
+
}
|
|
4253
4400
|
node.ownership = ownership;
|
|
4254
4401
|
}
|
|
4255
4402
|
// --- Persistence ---
|
|
@@ -4291,6 +4438,18 @@ var IndexedDbFileSystemService = class {
|
|
|
4291
4438
|
}
|
|
4292
4439
|
}
|
|
4293
4440
|
// --- Private helpers ---
|
|
4441
|
+
/** A copy belongs to whoever made it (like `cp` without `-p`) */
|
|
4442
|
+
ownedByCurrentUser(node) {
|
|
4443
|
+
const owner = this.getDefaultOwnership();
|
|
4444
|
+
if (owner) {
|
|
4445
|
+
const apply = (n) => {
|
|
4446
|
+
n.ownership = { ...owner };
|
|
4447
|
+
n.children?.forEach(apply);
|
|
4448
|
+
};
|
|
4449
|
+
apply(node);
|
|
4450
|
+
}
|
|
4451
|
+
return node;
|
|
4452
|
+
}
|
|
4294
4453
|
cloneNode(node) {
|
|
4295
4454
|
const now = Date.now();
|
|
4296
4455
|
const clone = {
|
|
@@ -4332,8 +4491,6 @@ var IndexedDbFileSystemService = class {
|
|
|
4332
4491
|
});
|
|
4333
4492
|
}
|
|
4334
4493
|
};
|
|
4335
|
-
|
|
4336
|
-
// src/lib/services/server-file-system.service.ts
|
|
4337
4494
|
var DEFAULT_HOME2 = "/home/user";
|
|
4338
4495
|
function createSeedFileSystem2() {
|
|
4339
4496
|
const now = Date.now();
|
|
@@ -4383,6 +4540,7 @@ var ServerFileSystemService = class {
|
|
|
4383
4540
|
this.currentUid = null;
|
|
4384
4541
|
this.currentGroups = [];
|
|
4385
4542
|
this.baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
4543
|
+
this.headersProvider = options.headers;
|
|
4386
4544
|
}
|
|
4387
4545
|
// --- User context ---
|
|
4388
4546
|
setCurrentUser(uid, groups) {
|
|
@@ -4790,7 +4948,14 @@ var ServerFileSystemService = class {
|
|
|
4790
4948
|
// --- Private: server communication ---
|
|
4791
4949
|
async serverFetch(endpoint, options) {
|
|
4792
4950
|
const url = `${this.baseUrl}/api/qcli/fs${endpoint}`;
|
|
4793
|
-
const
|
|
4951
|
+
const mergedHeaders = {
|
|
4952
|
+
...cliCore.resolveHeaders(this.headersProvider),
|
|
4953
|
+
...options?.headers ?? {}
|
|
4954
|
+
};
|
|
4955
|
+
const response = await fetch(url, {
|
|
4956
|
+
...options,
|
|
4957
|
+
headers: mergedHeaders
|
|
4958
|
+
});
|
|
4794
4959
|
if (!response.ok) {
|
|
4795
4960
|
const body = await response.text();
|
|
4796
4961
|
throw new Error(
|
|
@@ -4959,6 +5124,115 @@ var VirtualFsFileTransferService = class {
|
|
|
4959
5124
|
return this._browserService.uploadFromBrowser(accept);
|
|
4960
5125
|
}
|
|
4961
5126
|
};
|
|
5127
|
+
var CliFilesPermissions = {
|
|
5128
|
+
/** Bypass all file permission checks */
|
|
5129
|
+
Admin: "files:admin",
|
|
5130
|
+
/** chmod/chown files you do not own */
|
|
5131
|
+
Chown: "files:chown"
|
|
5132
|
+
};
|
|
5133
|
+
var HOME_DIR_PERMISSIONS = "rwxr-x---";
|
|
5134
|
+
function authorization(context) {
|
|
5135
|
+
try {
|
|
5136
|
+
return context.services.get(cliCore.ICliAuthorizationService_TOKEN);
|
|
5137
|
+
} catch {
|
|
5138
|
+
return void 0;
|
|
5139
|
+
}
|
|
5140
|
+
}
|
|
5141
|
+
function createFileAccessPolicy(context) {
|
|
5142
|
+
return {
|
|
5143
|
+
// Nodes without an owner (system seed, legacy data) belong to root:admin
|
|
5144
|
+
canAccess: (node, action) => cliCore.cliCanAccess(
|
|
5145
|
+
context,
|
|
5146
|
+
node.ownership ? node : { ...node, ownership: SYSTEM_OWNERSHIP },
|
|
5147
|
+
action,
|
|
5148
|
+
[CliFilesPermissions.Admin]
|
|
5149
|
+
),
|
|
5150
|
+
canChangeOwnership: (node, newOwnership) => {
|
|
5151
|
+
const auth = authorization(context);
|
|
5152
|
+
if (!auth) return true;
|
|
5153
|
+
if (auth.can(CliFilesPermissions.Admin) || auth.can(CliFilesPermissions.Chown)) return true;
|
|
5154
|
+
const principal = auth.getPrincipal();
|
|
5155
|
+
if (!principal) return false;
|
|
5156
|
+
const owns = !!node.ownership && (node.ownership.uid === principal.name || node.ownership.uid === principal.id);
|
|
5157
|
+
const keepsOwner = !newOwnership || newOwnership.uid === node.ownership?.uid || newOwnership.uid === principal.name;
|
|
5158
|
+
return owns && keepsOwner;
|
|
5159
|
+
}
|
|
5160
|
+
};
|
|
5161
|
+
}
|
|
5162
|
+
function followUserSession(context, fs) {
|
|
5163
|
+
let sessions;
|
|
5164
|
+
try {
|
|
5165
|
+
sessions = context.services.get(cliCore.ICliUserSessionService_TOKEN);
|
|
5166
|
+
} catch {
|
|
5167
|
+
sessions = void 0;
|
|
5168
|
+
}
|
|
5169
|
+
if (!sessions) return void 0;
|
|
5170
|
+
let previousUser = null;
|
|
5171
|
+
return sessions.getUserSession().subscribe((session) => {
|
|
5172
|
+
if (!session?.user) {
|
|
5173
|
+
fs.setCurrentUser("", []);
|
|
5174
|
+
if (previousUser !== null || !canEnter(fs, fs.getCurrentDirectory())) {
|
|
5175
|
+
try {
|
|
5176
|
+
fs.setCurrentDirectory("/");
|
|
5177
|
+
void fs.persist();
|
|
5178
|
+
} catch {
|
|
5179
|
+
}
|
|
5180
|
+
}
|
|
5181
|
+
previousUser = null;
|
|
5182
|
+
return;
|
|
5183
|
+
}
|
|
5184
|
+
const user = session.user;
|
|
5185
|
+
fs.setCurrentUser(user.name, user.groups ?? []);
|
|
5186
|
+
if (user.homeDir) {
|
|
5187
|
+
const home = user.homeDir;
|
|
5188
|
+
const system = (fn) => fs.runAsSystem ? fs.runAsSystem(fn) : fn();
|
|
5189
|
+
if (!fs.exists(home)) {
|
|
5190
|
+
system(() => {
|
|
5191
|
+
fs.createDirectory(home, true);
|
|
5192
|
+
if (home !== "/") {
|
|
5193
|
+
fs.chown(home, { uid: user.name, gid: user.groups?.[0] ?? "users" });
|
|
5194
|
+
fs.chmod(home, HOME_DIR_PERMISSIONS);
|
|
5195
|
+
}
|
|
5196
|
+
});
|
|
5197
|
+
void fs.persist();
|
|
5198
|
+
}
|
|
5199
|
+
fs.setHomePath(home);
|
|
5200
|
+
if (previousUser !== user.id) {
|
|
5201
|
+
try {
|
|
5202
|
+
fs.setCurrentDirectory(home);
|
|
5203
|
+
} catch {
|
|
5204
|
+
}
|
|
5205
|
+
}
|
|
5206
|
+
}
|
|
5207
|
+
previousUser = user.id;
|
|
5208
|
+
});
|
|
5209
|
+
}
|
|
5210
|
+
function canEnter(fs, path) {
|
|
5211
|
+
try {
|
|
5212
|
+
fs.setCurrentDirectory(path);
|
|
5213
|
+
return true;
|
|
5214
|
+
} catch {
|
|
5215
|
+
return false;
|
|
5216
|
+
}
|
|
5217
|
+
}
|
|
5218
|
+
function followUserAccounts(context, fs) {
|
|
5219
|
+
let events;
|
|
5220
|
+
try {
|
|
5221
|
+
events = context.services.get(cliCore.ICliUserEventService_TOKEN);
|
|
5222
|
+
} catch {
|
|
5223
|
+
events = void 0;
|
|
5224
|
+
}
|
|
5225
|
+
if (!events || !fs.reassignOwner) return void 0;
|
|
5226
|
+
return events.events$.subscribe((event) => {
|
|
5227
|
+
let changed = 0;
|
|
5228
|
+
if (event.type === "user_updated" && event.previousName && event.previousName !== event.user?.name) {
|
|
5229
|
+
changed = fs.reassignOwner(event.previousName, event.user.name);
|
|
5230
|
+
} else if (event.type === "user_deleted" && event.user?.name) {
|
|
5231
|
+
changed = fs.reassignOwner(event.user.name, SYSTEM_OWNERSHIP.uid);
|
|
5232
|
+
}
|
|
5233
|
+
if (changed > 0) void fs.persist().catch(() => void 0);
|
|
5234
|
+
});
|
|
5235
|
+
}
|
|
4962
5236
|
|
|
4963
5237
|
// src/lib/completion/file-path-completion-provider.ts
|
|
4964
5238
|
var FILE_COMMANDS = /* @__PURE__ */ new Set([
|
|
@@ -5059,10 +5333,10 @@ var CliUploadCommandProcessor = class {
|
|
|
5059
5333
|
];
|
|
5060
5334
|
}
|
|
5061
5335
|
async processCommand(command, context) {
|
|
5062
|
-
const fs = context.services.
|
|
5336
|
+
const fs = context.services.getRequired(
|
|
5063
5337
|
IFileSystemService_TOKEN
|
|
5064
5338
|
);
|
|
5065
|
-
const fileService = context.services.
|
|
5339
|
+
const fileService = context.services.getRequired(
|
|
5066
5340
|
cliCore.ICliFileTransferService_TOKEN
|
|
5067
5341
|
);
|
|
5068
5342
|
if (!fileService) {
|
|
@@ -5072,7 +5346,7 @@ var CliUploadCommandProcessor = class {
|
|
|
5072
5346
|
const accept = command.args?.["accept"];
|
|
5073
5347
|
const destPath = command.value?.trim() || void 0;
|
|
5074
5348
|
context.spinner?.show("Waiting for file selection...");
|
|
5075
|
-
context.
|
|
5349
|
+
context.notifier.info("Waiting for file selection");
|
|
5076
5350
|
const picked = await fileService.uploadFromBrowser(accept);
|
|
5077
5351
|
if (!picked) {
|
|
5078
5352
|
context.spinner?.hide();
|
|
@@ -5085,7 +5359,7 @@ var CliUploadCommandProcessor = class {
|
|
|
5085
5359
|
filename.startsWith("/") ? filename : cwd + "/" + filename
|
|
5086
5360
|
);
|
|
5087
5361
|
context.spinner?.show(`Saving "${picked.name}" (${picked.content.length} bytes)...`);
|
|
5088
|
-
context.
|
|
5362
|
+
context.notifier.info(`Saving ${picked.name}`);
|
|
5089
5363
|
try {
|
|
5090
5364
|
fs.writeFile(resolved, picked.content);
|
|
5091
5365
|
await fs.persist();
|
|
@@ -5153,6 +5427,10 @@ var filesModule = {
|
|
|
5153
5427
|
new CliShCommandProcessor(),
|
|
5154
5428
|
new CliUploadCommandProcessor()
|
|
5155
5429
|
],
|
|
5430
|
+
permissions: [
|
|
5431
|
+
{ name: CliFilesPermissions.Admin, description: "Bypass file permission checks" },
|
|
5432
|
+
{ name: CliFilesPermissions.Chown, description: "chmod/chown files owned by others" }
|
|
5433
|
+
],
|
|
5156
5434
|
services: [
|
|
5157
5435
|
{
|
|
5158
5436
|
provide: IFileSystemService_TOKEN,
|
|
@@ -5184,7 +5462,7 @@ var filesModule = {
|
|
|
5184
5462
|
return { ...this, config };
|
|
5185
5463
|
},
|
|
5186
5464
|
async onInit(context) {
|
|
5187
|
-
const fs = context.services.
|
|
5465
|
+
const fs = context.services.getRequired(
|
|
5188
5466
|
IFileSystemService_TOKEN
|
|
5189
5467
|
);
|
|
5190
5468
|
await fs.initialize();
|
|
@@ -5195,7 +5473,7 @@ var filesModule = {
|
|
|
5195
5473
|
}
|
|
5196
5474
|
let dragDrop;
|
|
5197
5475
|
try {
|
|
5198
|
-
dragDrop = context.services.
|
|
5476
|
+
dragDrop = context.services.getRequired(cliCore.ICliDragDropService_TOKEN);
|
|
5199
5477
|
} catch {
|
|
5200
5478
|
}
|
|
5201
5479
|
if (dragDrop) {
|
|
@@ -5224,13 +5502,26 @@ var filesModule = {
|
|
|
5224
5502
|
});
|
|
5225
5503
|
}
|
|
5226
5504
|
},
|
|
5505
|
+
async onAfterBoot(context) {
|
|
5506
|
+
const fs = context.services.getRequired(IFileSystemService_TOKEN);
|
|
5507
|
+
fs.setAccessPolicy?.(createFileAccessPolicy(context));
|
|
5508
|
+
this._sessionSubscription?.unsubscribe();
|
|
5509
|
+
this._sessionSubscription = followUserSession(context, fs);
|
|
5510
|
+
this._accountsSubscription?.unsubscribe();
|
|
5511
|
+
this._accountsSubscription = followUserAccounts(context, fs);
|
|
5512
|
+
},
|
|
5227
5513
|
async onDestroy(_context) {
|
|
5228
5514
|
const sub = this._dragDropSubscription;
|
|
5229
5515
|
sub?.unsubscribe();
|
|
5230
5516
|
this._dragDropSubscription = void 0;
|
|
5517
|
+
this._sessionSubscription?.unsubscribe();
|
|
5518
|
+
this._sessionSubscription = void 0;
|
|
5519
|
+
this._accountsSubscription?.unsubscribe();
|
|
5520
|
+
this._accountsSubscription = void 0;
|
|
5231
5521
|
}
|
|
5232
5522
|
};
|
|
5233
5523
|
|
|
5524
|
+
exports.ANONYMOUS_OWNERSHIP = ANONYMOUS_OWNERSHIP;
|
|
5234
5525
|
exports.CliAwkCommandProcessor = CliAwkCommandProcessor;
|
|
5235
5526
|
exports.CliBasenameCommandProcessor = CliBasenameCommandProcessor;
|
|
5236
5527
|
exports.CliCatCommandProcessor = CliCatCommandProcessor;
|
|
@@ -5243,6 +5534,7 @@ exports.CliDiffCommandProcessor = CliDiffCommandProcessor;
|
|
|
5243
5534
|
exports.CliDirnameCommandProcessor = CliDirnameCommandProcessor;
|
|
5244
5535
|
exports.CliDuCommandProcessor = CliDuCommandProcessor;
|
|
5245
5536
|
exports.CliEchoCommandProcessor = CliEchoCommandProcessor;
|
|
5537
|
+
exports.CliFilesPermissions = CliFilesPermissions;
|
|
5246
5538
|
exports.CliFindCommandProcessor = CliFindCommandProcessor;
|
|
5247
5539
|
exports.CliGrepCommandProcessor = CliGrepCommandProcessor;
|
|
5248
5540
|
exports.CliHeadCommandProcessor = CliHeadCommandProcessor;
|
|
@@ -5268,11 +5560,16 @@ exports.CliUniqCommandProcessor = CliUniqCommandProcessor;
|
|
|
5268
5560
|
exports.CliWcCommandProcessor = CliWcCommandProcessor;
|
|
5269
5561
|
exports.CliXargsCommandProcessor = CliXargsCommandProcessor;
|
|
5270
5562
|
exports.FilePathCompletionProvider = FilePathCompletionProvider;
|
|
5563
|
+
exports.HOME_DIR_PERMISSIONS = HOME_DIR_PERMISSIONS;
|
|
5271
5564
|
exports.IFileSystemService_TOKEN = IFileSystemService_TOKEN;
|
|
5272
5565
|
exports.IndexedDbFileSystemService = IndexedDbFileSystemService;
|
|
5566
|
+
exports.SYSTEM_OWNERSHIP = SYSTEM_OWNERSHIP;
|
|
5273
5567
|
exports.ServerFileSystemService = ServerFileSystemService;
|
|
5274
5568
|
exports.VirtualFsFileTransferService = VirtualFsFileTransferService;
|
|
5569
|
+
exports.createFileAccessPolicy = createFileAccessPolicy;
|
|
5275
5570
|
exports.executeScript = executeScript;
|
|
5276
5571
|
exports.filesModule = filesModule;
|
|
5572
|
+
exports.followUserAccounts = followUserAccounts;
|
|
5573
|
+
exports.followUserSession = followUserSession;
|
|
5277
5574
|
//# sourceMappingURL=public-api.js.map
|
|
5278
5575
|
//# sourceMappingURL=public-api.js.map
|