forked from doctly/switchboard
-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathremote-hosts.js
More file actions
121 lines (103 loc) · 3.45 KB
/
Copy pathremote-hosts.js
File metadata and controls
121 lines (103 loc) · 3.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
// see .ai/contexts/session-cache.md ("Remote hosts")
'use strict';
const path = require('path');
const FOLDER_SEP = '::';
const ALIAS_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,62}$/;
const MIN_REFRESH_MS = 60_000;
const DEFAULT_REFRESH_MS = 300_000;
function isValidAlias(alias) {
return typeof alias === 'string' && ALIAS_RE.test(alias) && !alias.includes(FOLDER_SEP);
}
function joinFolderKey(alias, folder) {
return alias + FOLDER_SEP + folder;
}
function parseFolderKey(key) {
const s = key == null ? '' : String(key);
const i = s.indexOf(FOLDER_SEP);
if (i < 0) return { alias: null, folder: s };
const alias = s.slice(0, i);
if (!isValidAlias(alias)) return { alias: null, folder: s };
return { alias, folder: s.slice(i + FOLDER_SEP.length) };
}
function isRemoteFolder(key) {
return parseFolderKey(key).alias !== null;
}
function normalizeHosts(raw) {
if (!Array.isArray(raw)) return [];
const out = [];
const seen = new Set();
for (const entry of raw) {
if (!entry || typeof entry !== 'object') continue;
const alias = typeof entry.alias === 'string' ? entry.alias.trim() : '';
if (!isValidAlias(alias) || seen.has(alias)) continue;
seen.add(alias);
out.push({
alias,
label: typeof entry.label === 'string' && entry.label.trim() ? entry.label.trim().slice(0, 64) : alias,
enabled: entry.enabled !== false,
});
}
return out;
}
function enabledHosts(raw) {
return normalizeHosts(raw).filter(h => h.enabled);
}
function normalizeRefreshMs(value) {
const n = Number(value);
if (!Number.isFinite(n) || n <= 0) return DEFAULT_REFRESH_MS;
return Math.max(MIN_REFRESH_MS, Math.floor(n));
}
function mirrorDirFor(dataDir, alias) {
return path.join(dataDir, 'remote', alias);
}
function mirrorProjectsDirFor(dataDir, alias) {
return path.join(mirrorDirFor(dataDir, alias), 'projects');
}
function manifestPathFor(dataDir, alias) {
return path.join(mirrorDirFor(dataDir, alias), 'inventory.json');
}
// Validated before it is joined onto a local path or handed to scp.
// A leading '-' is allowed: the CLI's own folder names start with one. see .ai/contexts/session-cache.md ("Remote SSH hosts")
const SAFE_REL_RE = /^[A-Za-z0-9._-]+(\/[A-Za-z0-9._-]+)*$/;
function isSafeRelPath(rel) {
if (typeof rel !== 'string' || rel.length === 0 || rel.length > 512) return false;
if (!rel.endsWith('.jsonl')) return false;
if (rel.includes('..')) return false;
if (rel.split('/').some(seg => seg === '.')) return false;
return SAFE_REL_RE.test(rel);
}
// see .ai/contexts/session-cache.md ("Remote hosts — meta.json sidecars")
function isSafeMetaRelPath(rel) {
if (typeof rel !== 'string' || rel.length === 0 || rel.length > 512) return false;
if (!rel.endsWith('.meta.json')) return false;
if (rel.includes('..')) return false;
if (rel.split('/').some(seg => seg === '.')) return false;
return SAFE_REL_RE.test(rel);
}
// see .ai/contexts/session-cache.md ("Remote hosts — meta.json sidecars")
function isSafeMirrorRelPath(rel) {
return isSafeRelPath(rel) || isSafeMetaRelPath(rel);
}
function topFolderOf(rel) {
const i = rel.indexOf('/');
return i < 0 ? null : rel.slice(0, i);
}
module.exports = {
FOLDER_SEP,
MIN_REFRESH_MS,
DEFAULT_REFRESH_MS,
isValidAlias,
joinFolderKey,
parseFolderKey,
isRemoteFolder,
normalizeHosts,
enabledHosts,
normalizeRefreshMs,
mirrorDirFor,
mirrorProjectsDirFor,
manifestPathFor,
isSafeRelPath,
isSafeMetaRelPath,
isSafeMirrorRelPath,
topFolderOf,
};