talentos 1.1.5 → 1.2.0
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/dist/agent/adapters.js +73 -85
- package/dist/agent/codex-policy.js +72 -0
- package/dist/agent/codex-probe.js +424 -0
- package/dist/agent/mcp-entry.js +5 -2
- package/dist/agent/native/approvals.js +188 -0
- package/dist/agent/native/config.js +118 -0
- package/dist/agent/native/disqualify.js +46 -0
- package/dist/agent/native/env.js +29 -0
- package/dist/agent/native/gate.js +102 -0
- package/dist/agent/native/hook-entry.js +170 -0
- package/dist/agent/native/launch.js +91 -0
- package/dist/agent/native/paths.js +137 -0
- package/dist/agent/native/policy.js +653 -0
- package/dist/agent/native/probe.js +474 -0
- package/dist/agent/native/shell.js +174 -0
- package/dist/agent/native/workspace.js +87 -0
- package/dist/agent/serve.js +77 -8
- package/dist/args.js +1 -0
- package/dist/commands/agent-native.js +64 -0
- package/dist/commands/agent-serve.js +14 -11
- package/dist/commands/agent.js +5 -1
- package/dist/mcp-proxy.js +47 -6
- package/dist/upload-file.js +172 -0
- package/package.json +1 -1
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { realpathSync } from "node:fs";
|
|
2
|
+
import { basename, dirname, isAbsolute, join, relative, resolve, sep, } from "node:path";
|
|
3
|
+
// Path canonicalisation and the protected-path rules of the owner-native hook
|
|
4
|
+
// (#10987). Everything is compared after realpath of the nearest existing
|
|
5
|
+
// ancestor (symlinks), Unicode NFC and, on case-insensitive filesystems
|
|
6
|
+
// (macOS APFS, Windows), case folding, so `..`, symlinks and `~/.SSH` all land
|
|
7
|
+
// on the same name.
|
|
8
|
+
const FOLD = process.platform === "darwin" || process.platform === "win32";
|
|
9
|
+
/** Home-relative directories whose contents are never readable or writable by the model. */
|
|
10
|
+
const HOME_DIRS = [
|
|
11
|
+
".ssh",
|
|
12
|
+
".aws",
|
|
13
|
+
".gnupg",
|
|
14
|
+
".kube",
|
|
15
|
+
".talentos",
|
|
16
|
+
".claude",
|
|
17
|
+
".codex",
|
|
18
|
+
".cursor",
|
|
19
|
+
".docker",
|
|
20
|
+
".config/gcloud",
|
|
21
|
+
".config/gh",
|
|
22
|
+
".config/op",
|
|
23
|
+
".config/fish",
|
|
24
|
+
"Library/Keychains",
|
|
25
|
+
"Library/Cookies",
|
|
26
|
+
"Library/Application Support/Google/Chrome",
|
|
27
|
+
"Library/Application Support/Firefox",
|
|
28
|
+
"Library/Application Support/Arc",
|
|
29
|
+
"Library/Application Support/BraveSoftware",
|
|
30
|
+
"Library/Application Support/Microsoft Edge",
|
|
31
|
+
"Library/Application Support/1Password",
|
|
32
|
+
];
|
|
33
|
+
/** Home-relative files: credentials, provider config and shell start-up files. */
|
|
34
|
+
const HOME_FILES = [
|
|
35
|
+
".claude.json",
|
|
36
|
+
".npmrc",
|
|
37
|
+
".netrc",
|
|
38
|
+
".git-credentials",
|
|
39
|
+
".pgpass",
|
|
40
|
+
".pypirc",
|
|
41
|
+
".zshrc",
|
|
42
|
+
".zprofile",
|
|
43
|
+
".zshenv",
|
|
44
|
+
".zlogin",
|
|
45
|
+
".bashrc",
|
|
46
|
+
".bash_profile",
|
|
47
|
+
".bash_login",
|
|
48
|
+
".profile",
|
|
49
|
+
];
|
|
50
|
+
/** Writable only by the owner: git config can run commands, so the model may read but not write it. */
|
|
51
|
+
const HOME_WRITE_ONLY = [".gitconfig", ".config/git"];
|
|
52
|
+
const SECRET_NAME = /^(\.env(\..+)?|\.envrc|\.netrc|\.npmrc|\.pgpass|\.pypirc|\.git-credentials|credentials(\.json)?|id_(rsa|dsa|ecdsa|ed25519)(\.pub)?|.+\.(pem|p12|pfx|key|keystore|jks))$/iu;
|
|
53
|
+
const SECRET_NAME_OK = /\.(example|sample|template|dist)$|\.pub$/iu;
|
|
54
|
+
/** Names that configure the provider, the hook or what runs on commit: the model may read, never write. */
|
|
55
|
+
const WRITE_PROTECTED_SEGMENTS = [
|
|
56
|
+
".claude",
|
|
57
|
+
".codex",
|
|
58
|
+
".cursor",
|
|
59
|
+
".husky",
|
|
60
|
+
".githooks",
|
|
61
|
+
];
|
|
62
|
+
const WRITE_PROTECTED_NAMES = [".mcp.json"];
|
|
63
|
+
export const fold = (path) => (FOLD ? path.toLowerCase() : path).normalize("NFC");
|
|
64
|
+
/** realpath of the nearest existing ancestor, with the unresolved remainder re-appended. */
|
|
65
|
+
export function canonical(path, cwd, home) {
|
|
66
|
+
let target = path;
|
|
67
|
+
if (target === "~" || target.startsWith("~/")) {
|
|
68
|
+
target = join(home, target.slice(1));
|
|
69
|
+
}
|
|
70
|
+
target = resolve(isAbsolute(target) ? target : join(cwd, target));
|
|
71
|
+
const rest = [];
|
|
72
|
+
let existing = target;
|
|
73
|
+
for (;;) {
|
|
74
|
+
try {
|
|
75
|
+
const real = realpathSync.native(existing);
|
|
76
|
+
return join(real, ...rest.reverse());
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
const parent = dirname(existing);
|
|
80
|
+
if (parent === existing) {
|
|
81
|
+
return target;
|
|
82
|
+
}
|
|
83
|
+
rest.push(basename(existing));
|
|
84
|
+
existing = parent;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
export function isInside(child, parent) {
|
|
89
|
+
const rel = relative(fold(parent), fold(child));
|
|
90
|
+
return rel === "" || !(rel.startsWith("..") || isAbsolute(rel));
|
|
91
|
+
}
|
|
92
|
+
function homeEntries(home, names) {
|
|
93
|
+
return names.map((name) => canonical(join(home, name), home, home));
|
|
94
|
+
}
|
|
95
|
+
export function protectedPaths(home) {
|
|
96
|
+
return {
|
|
97
|
+
readWrite: [
|
|
98
|
+
...homeEntries(home, HOME_DIRS),
|
|
99
|
+
...homeEntries(home, HOME_FILES),
|
|
100
|
+
],
|
|
101
|
+
writeOnly: homeEntries(home, HOME_WRITE_ONLY),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/** Why a canonical path is off limits, or undefined. `mode` "read" skips write-only protections. */
|
|
105
|
+
export function protectedReason(path, mode, home, guard = protectedPaths(home)) {
|
|
106
|
+
for (const entry of guard.readWrite) {
|
|
107
|
+
if (isInside(path, entry)) {
|
|
108
|
+
return "credentials or provider settings";
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const name = basename(path);
|
|
112
|
+
if (SECRET_NAME.test(name) && !SECRET_NAME_OK.test(name)) {
|
|
113
|
+
return "a secrets file";
|
|
114
|
+
}
|
|
115
|
+
if (mode === "write") {
|
|
116
|
+
for (const entry of guard.writeOnly) {
|
|
117
|
+
if (isInside(path, entry)) {
|
|
118
|
+
return "a configuration file that can run commands";
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const segments = path.split(sep);
|
|
122
|
+
if (segments.some((segment) => WRITE_PROTECTED_SEGMENTS.includes(fold(segment))) ||
|
|
123
|
+
WRITE_PROTECTED_NAMES.includes(fold(name))) {
|
|
124
|
+
return "provider, hook or MCP settings";
|
|
125
|
+
}
|
|
126
|
+
const gitAt = segments.lastIndexOf(".git");
|
|
127
|
+
if (gitAt >= 0 &&
|
|
128
|
+
(segments[gitAt + 1] === "hooks" || segments[gitAt + 1] === "config")) {
|
|
129
|
+
return "git hooks or config";
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
/** True when `dir` contains a protected path, so a recursive read from it would reach it. */
|
|
135
|
+
export function reachesProtected(dir, home, guard = protectedPaths(home)) {
|
|
136
|
+
return guard.readWrite.some((entry) => isInside(entry, dir));
|
|
137
|
+
}
|