@tensor.chat/companion 0.4.1 → 0.4.3
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/README.md +10 -5
- package/bin/tensor-companion.js +25 -2
- package/package.json +1 -1
- package/src/daemon.js +44 -11
- package/src/setup/i18n.js +157 -0
- package/src/setup/layout.js +49 -0
- package/src/setup/places.js +113 -0
- package/src/setup/term.js +94 -0
- package/src/setup/tree.js +174 -0
- package/src/setup/wizard.js +457 -0
- package/src/tools/outside.js +44 -15
package/src/tools/outside.js
CHANGED
|
@@ -24,17 +24,32 @@ const WHOLE_PATH = /^(?:[A-Za-z]:[\\/]|\\\\|\.\.(?:[\\/]|$)|\/)/;
|
|
|
24
24
|
|
|
25
25
|
// The command as this machine reads it: PowerShell's $env:NAME, cmd's %NAME%, the
|
|
26
26
|
// POSIX $NAME and ${NAME}, and ~ for the home folder. A name it does not know stays.
|
|
27
|
-
|
|
27
|
+
// A variable that holds a list of folders (PATH and its kind) stays as written too: a
|
|
28
|
+
// list names no one place, and most commands only add to it - put in, every folder on
|
|
29
|
+
// the list read as named, and a test run was asked about ten folders. Its entries go to
|
|
30
|
+
// `lists`, under the name the command wrote.
|
|
31
|
+
function expand(command, { env, home, platform }, lists = []) {
|
|
28
32
|
const keys = Object.keys(env);
|
|
33
|
+
const sep = platform === 'win32' ? ';' : ':';
|
|
34
|
+
const absolute = platform === 'win32' ? path.win32.isAbsolute : path.posix.isAbsolute;
|
|
29
35
|
const get = (name) => {
|
|
30
36
|
const key = platform === 'win32' ? keys.find((k) => k.toLowerCase() === name.toLowerCase()) : (name in env ? name : null);
|
|
31
37
|
if (key != null && env[key] != null) return String(env[key]);
|
|
32
38
|
return /^(home|userprofile)$/i.test(name) ? home : null;
|
|
33
39
|
};
|
|
40
|
+
const put = (m, name) => {
|
|
41
|
+
const value = get(name);
|
|
42
|
+
if (value == null) return m;
|
|
43
|
+
// A list holds at least two paths of this system ("C:\x" is no list where ":" separates)
|
|
44
|
+
const entries = value.split(sep).filter(Boolean);
|
|
45
|
+
if (entries.filter(absolute).length < 2) return value;
|
|
46
|
+
if (!lists.some((l) => l.name === name)) lists.push({ name, entries });
|
|
47
|
+
return m;
|
|
48
|
+
};
|
|
34
49
|
return String(command == null ? '' : command)
|
|
35
|
-
.replace(/\$\{env:([A-Za-z_][\w()]*)\}|\$env:([A-Za-z_]\w*)/gi, (m, a, b) =>
|
|
36
|
-
.replace(/%([A-Za-z_][\w()]*)%/g, (m, n) =>
|
|
37
|
-
.replace(/\$\{([A-Za-z_]\w*)\}|\$([A-Za-z_]\w*)/g, (m, a, b) =>
|
|
50
|
+
.replace(/\$\{env:([A-Za-z_][\w()]*)\}|\$env:([A-Za-z_]\w*)/gi, (m, a, b) => put(m, a || b))
|
|
51
|
+
.replace(/%([A-Za-z_][\w()]*)%/g, (m, n) => put(m, n))
|
|
52
|
+
.replace(/\$\{([A-Za-z_]\w*)\}|\$([A-Za-z_]\w*)/g, (m, a, b) => put(m, a || b))
|
|
38
53
|
.replace(/(^|[\s"'=(])~(?=[\\/]|$|[\s"')])/g, (m, pre) => pre + home);
|
|
39
54
|
}
|
|
40
55
|
|
|
@@ -78,22 +93,36 @@ function folderOf(word, cwd) {
|
|
|
78
93
|
|
|
79
94
|
function outsideFolders(roots, command, { cwd, env = process.env, home = os.homedir(), platform = process.platform } = {}) {
|
|
80
95
|
const shared = roots.okRoots().map((r) => r.real);
|
|
96
|
+
const outside = (folder) => !shared.some((r) => isInside(realExisting(folder) || folder, r));
|
|
97
|
+
const same = (a, b) => isInside(a, b) && isInside(b, a);
|
|
81
98
|
const found = [];
|
|
82
|
-
|
|
99
|
+
const lists = [];
|
|
100
|
+
for (const word of words(expand(command, { env, home, platform }, lists), platform)) {
|
|
83
101
|
const folder = folderOf(word, cwd || home);
|
|
84
|
-
if (!folder) continue;
|
|
85
|
-
|
|
86
|
-
if (shared.some((r) => isInside(real, r))) continue;
|
|
87
|
-
if (!found.some((f) => isInside(folder, f) && isInside(f, folder))) found.push(folder);
|
|
102
|
+
if (!folder || !outside(folder)) continue;
|
|
103
|
+
if (!found.some((f) => same(folder, f))) found.push(folder);
|
|
88
104
|
}
|
|
89
105
|
// A folder inside another one found is covered by it
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
106
|
+
const named = found.filter((f) => !found.some((g) => g !== f && isInside(f, g) && !isInside(g, f)));
|
|
107
|
+
// A list's folders come after the named ones and cover none of them: whatever the
|
|
108
|
+
// server makes of the list, a folder the command names is asked for by itself
|
|
109
|
+
const listed = [];
|
|
110
|
+
for (const { name, entries } of lists) {
|
|
111
|
+
for (const entry of entries) {
|
|
112
|
+
const st = path.isAbsolute(entry) ? statOf(entry) : null;
|
|
113
|
+
if (!st || !st.isDirectory() || !outside(entry)) continue;
|
|
114
|
+
if (![...named, ...listed.map((l) => l.path)].some((f) => same(entry, f))) listed.push({ path: entry, from: name });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// A whole drive, the home folder or what holds it is too much to share in one click
|
|
118
|
+
const broad = (f) => path.parse(f).root === f || isInside(home, f);
|
|
119
|
+
const folders = [
|
|
120
|
+
...named.map((f) => ({ path: f, broad: broad(f) })),
|
|
121
|
+
...listed.map((l) => ({ path: l.path, broad: broad(l.path), from: l.from })),
|
|
122
|
+
].slice(0, MAX_FOLDERS);
|
|
123
|
+
const said = (f) => (f.from ? `${f.path} (from ${f.from})` : f.path);
|
|
95
124
|
const text = folders.length
|
|
96
|
-
? `(outside the shared folders: ${folders.map(
|
|
125
|
+
? `(outside the shared folders: ${folders.map(said).join(', ')})`
|
|
97
126
|
: '(nothing outside the shared folders)';
|
|
98
127
|
return { ok: true, text, folders };
|
|
99
128
|
}
|