@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.
@@ -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
- function expand(command, { env, home, platform }) {
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) => get(a || b) ?? m)
36
- .replace(/%([A-Za-z_][\w()]*)%/g, (m, n) => get(n) ?? m)
37
- .replace(/\$\{([A-Za-z_]\w*)\}|\$([A-Za-z_]\w*)/g, (m, a, b) => get(a || b) ?? m)
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
- for (const word of words(expand(command, { env, home, platform }), platform)) {
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
- const real = realExisting(folder) || folder;
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 folders = found
91
- .filter((f) => !found.some((g) => g !== f && isInside(f, g) && !isInside(g, f)))
92
- .slice(0, MAX_FOLDERS)
93
- // A whole drive, the home folder or what holds it is too much to share in one click
94
- .map((f) => ({ path: f, broad: path.parse(f).root === f || isInside(home, f) }));
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((f) => f.path).join(', ')})`
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
  }