@cirvix_ai/agent-control 0.1.2 → 0.1.5

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.
Files changed (70) hide show
  1. package/README.md +76 -17
  2. package/bin/cirvix.mjs +488 -40
  3. package/bin/escape-benchmark.mjs +67 -0
  4. package/package.json +36 -16
  5. package/src/adapters/base.mjs +150 -0
  6. package/src/adapters/claude-code.mjs +161 -0
  7. package/src/adapters/cline.mjs +107 -0
  8. package/src/adapters/codex.mjs +104 -0
  9. package/src/adapters/cursor.mjs +104 -0
  10. package/src/adapters/frameworks.mjs +110 -0
  11. package/src/adapters/gemini-cli.mjs +104 -0
  12. package/src/adapters/generic-mcp.mjs +101 -0
  13. package/src/adapters/index.mjs +209 -0
  14. package/src/adapters/roo-code.mjs +106 -0
  15. package/src/adapters/vscode.mjs +104 -0
  16. package/src/adapters/windsurf.mjs +107 -0
  17. package/src/commands/demo.mjs +56 -70
  18. package/src/commands/doctor.mjs +235 -0
  19. package/src/commands/init.mjs +292 -30
  20. package/src/commands/interactive.mjs +690 -0
  21. package/src/commands/kill.mjs +74 -0
  22. package/src/commands/login.mjs +227 -0
  23. package/src/commands/passport.mjs +149 -0
  24. package/src/commands/policy.mjs +10 -6
  25. package/src/commands/protect.mjs +293 -0
  26. package/src/commands/prove.mjs +209 -0
  27. package/src/commands/redteam.mjs +51 -0
  28. package/src/commands/scan.mjs +6 -4
  29. package/src/commands/shadow.mjs +62 -0
  30. package/src/commands/simulate.mjs +96 -0
  31. package/src/commands/status.mjs +121 -36
  32. package/src/commands/upgrade.mjs +17 -9
  33. package/src/commands/welcome.mjs +105 -0
  34. package/src/core/authority.mjs +909 -0
  35. package/src/core/baseline.mjs +97 -0
  36. package/src/core/config-store.mjs +280 -0
  37. package/src/core/cost.mjs +0 -0
  38. package/src/core/detect.mjs +4 -33
  39. package/src/core/entitlements.mjs +6 -0
  40. package/src/core/escape-benchmark.mjs +597 -0
  41. package/src/core/evidence.mjs +212 -0
  42. package/src/core/format.mjs +27 -0
  43. package/src/core/gateway.mjs +15 -211
  44. package/src/core/graph.mjs +270 -0
  45. package/src/core/guard.mjs +118 -4
  46. package/src/core/intent.mjs +166 -0
  47. package/src/core/journal.mjs +131 -40
  48. package/src/core/kill-switch.mjs +122 -0
  49. package/src/core/notices.mjs +22 -2
  50. package/src/core/packs.mjs +193 -0
  51. package/src/core/passport.mjs +555 -0
  52. package/src/core/pipeline.mjs +148 -6
  53. package/src/core/prompts.mjs +51 -0
  54. package/src/core/proof.mjs +440 -0
  55. package/src/core/redteam/index.mjs +185 -0
  56. package/src/core/referral.mjs +187 -0
  57. package/src/core/sandbox.mjs +139 -0
  58. package/src/core/session.mjs +172 -0
  59. package/src/core/shadow.mjs +95 -0
  60. package/src/core/trifecta.mjs +321 -0
  61. package/src/core/ui/controller.mjs +192 -0
  62. package/src/core/ui/decisions.mjs +55 -0
  63. package/src/core/ui/index.mjs +49 -0
  64. package/src/core/ui/intercept.mjs +103 -0
  65. package/src/core/ui/live.mjs +51 -0
  66. package/src/core/ui/primitives.mjs +123 -0
  67. package/src/core/ui/theme.mjs +92 -0
  68. package/src/core/verified.mjs +108 -0
  69. package/src/core/windows.mjs +270 -0
  70. package/src/index.mjs +25 -0
@@ -0,0 +1,270 @@
1
+ /**
2
+ * Windows first-class platform support for CIRVIX AgentControl.
3
+ *
4
+ * Provides cross-platform utilities designed to eliminate POSIX-only
5
+ * assumptions without bringing in external dependencies:
6
+ * - PATH and PATHEXT binary resolution (.exe, .cmd, .bat, etc.)
7
+ * - Global and user-level npm/node binary location discovery
8
+ * - Argument quoting and escaping for Windows shells
9
+ * - Process tree termination (preventing orphaned background processes)
10
+ * - Path canonicalization and case-folding
11
+ */
12
+
13
+ import { execFileSync, spawn } from "node:child_process";
14
+ import { accessSync, constants, statSync } from "node:fs";
15
+ import { homedir } from "node:os";
16
+ import { delimiter, dirname, isAbsolute, join, normalize, resolve } from "node:path";
17
+
18
+ export const IS_WINDOWS = process.platform === "win32";
19
+
20
+ /**
21
+ * Standard executable extensions on Windows.
22
+ */
23
+ export const DEFAULT_PATHEXT = IS_WINDOWS
24
+ ? (process.env.PATHEXT || ".COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC")
25
+ .split(";")
26
+ .map((ext) => ext.toLowerCase())
27
+ : [""];
28
+
29
+ /**
30
+ * Normalizes a filesystem path across platforms.
31
+ * Replaces backslashes with forward slashes for internal consistency
32
+ * and normalizes drive letters to uppercase.
33
+ */
34
+ export function normalizeFsPath(inputPath) {
35
+ if (typeof inputPath !== "string" || !inputPath) return "";
36
+ let p = inputPath.replace(/\\/g, "/");
37
+ // Normalize drive letter: 'c:/...' -> 'C:/...'
38
+ if (/^[a-zA-Z]:\//.test(p)) {
39
+ p = p.charAt(0).toUpperCase() + p.slice(1);
40
+ }
41
+ return p;
42
+ }
43
+
44
+ /**
45
+ * Checks if two filesystem paths refer to the same target (case-insensitive on Windows).
46
+ */
47
+ export function arePathsEqual(pathA, pathB) {
48
+ const a = normalizeFsPath(resolve(pathA));
49
+ const b = normalizeFsPath(resolve(pathB));
50
+ return IS_WINDOWS ? a.toLowerCase() === b.toLowerCase() : a === b;
51
+ }
52
+
53
+ /**
54
+ * Checks if a file exists and is executable.
55
+ */
56
+ function isFileExecutable(filePath) {
57
+ try {
58
+ const st = statSync(filePath);
59
+ if (!st.isFile()) return false;
60
+ accessSync(filePath, constants.X_OK | constants.R_OK);
61
+ return true;
62
+ } catch {
63
+ return false;
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Discovers common npm and global tool binary directories.
69
+ */
70
+ export function getGlobalBinaryDirectories() {
71
+ const dirs = [];
72
+ const home = homedir();
73
+
74
+ if (IS_WINDOWS) {
75
+ if (process.env.APPDATA) {
76
+ dirs.push(join(process.env.APPDATA, "npm"));
77
+ }
78
+ if (process.env.LOCALAPPDATA) {
79
+ dirs.push(join(process.env.LOCALAPPDATA, "Programs"));
80
+ dirs.push(join(process.env.LOCALAPPDATA, "pnpm"));
81
+ }
82
+ if (process.env.ProgramFiles) {
83
+ dirs.push(join(process.env.ProgramFiles, "nodejs"));
84
+ }
85
+ if (process.env["ProgramFiles(x86)"]) {
86
+ dirs.push(join(process.env["ProgramFiles(x86)"], "nodejs"));
87
+ }
88
+ dirs.push(join(home, "AppData", "Roaming", "npm"));
89
+ dirs.push(join(home, ".cargo", "bin"));
90
+ } else {
91
+ dirs.push("/usr/local/bin");
92
+ dirs.push("/usr/bin");
93
+ dirs.push(join(home, ".nvm", "versions", "node", process.version, "bin"));
94
+ dirs.push(join(home, ".local", "bin"));
95
+ dirs.push(join(home, ".cargo", "bin"));
96
+ }
97
+
98
+ return dirs.filter((d) => {
99
+ try {
100
+ return statSync(d).isDirectory();
101
+ } catch {
102
+ return false;
103
+ }
104
+ });
105
+ }
106
+
107
+ /**
108
+ * Resolves an executable command name to an absolute file path.
109
+ * On Windows, handles PATHEXT extensions (.exe, .cmd, .bat) and npm globals.
110
+ *
111
+ * @param {string} command - e.g. "npx", "node", "cirvix", "git"
112
+ * @param {object} [options]
113
+ * @param {string} [options.cwd]
114
+ * @param {string[]} [options.searchPaths]
115
+ * @returns {string|null} Absolute path or null if not found
116
+ */
117
+ export function resolveExecutable(command, { cwd = process.cwd(), searchPaths } = {}) {
118
+ if (typeof command !== "string" || !command) return null;
119
+
120
+ const raw = command.trim();
121
+ const pathext = DEFAULT_PATHEXT;
122
+
123
+ // If path is already absolute or explicitly relative (./ or ../)
124
+ if (isAbsolute(raw) || raw.startsWith("./") || raw.startsWith("../") || (IS_WINDOWS && /^[a-zA-Z]:[\\/]/.test(raw))) {
125
+ const candidate = resolve(cwd, raw);
126
+ if (isFileExecutable(candidate)) return candidate;
127
+ if (IS_WINDOWS) {
128
+ for (const ext of pathext) {
129
+ const withExt = candidate + ext;
130
+ if (isFileExecutable(withExt)) return withExt;
131
+ }
132
+ }
133
+ return null;
134
+ }
135
+
136
+ // Search in PATH and standard directories
137
+ const pathEnv = (process.env.PATH || "").split(delimiter).filter(Boolean);
138
+ const allSearchPaths = [
139
+ cwd,
140
+ ...(searchPaths ?? []),
141
+ ...pathEnv,
142
+ ...getGlobalBinaryDirectories(),
143
+ ];
144
+
145
+ for (const dir of allSearchPaths) {
146
+ const candidate = join(dir, raw);
147
+ if (isFileExecutable(candidate)) return candidate;
148
+
149
+ if (IS_WINDOWS) {
150
+ // Check if command already has an extension
151
+ const hasExt = pathext.some((ext) => raw.toLowerCase().endsWith(ext));
152
+ if (!hasExt) {
153
+ for (const ext of pathext) {
154
+ const withExt = candidate + ext;
155
+ if (isFileExecutable(withExt)) return withExt;
156
+ }
157
+ }
158
+ }
159
+ }
160
+
161
+ return null;
162
+ }
163
+
164
+ /**
165
+ * Prepares process spawn options for Windows to ensure batch scripts (.cmd, .bat)
166
+ * and executables with spaces run cleanly.
167
+ *
168
+ * @param {string} command
169
+ * @param {string[]} args
170
+ * @param {import("node:child_process").SpawnOptions} options
171
+ * @returns {{ command: string, args: string[], options: import("node:child_process").SpawnOptions }}
172
+ */
173
+ export function prepareSpawn(command, args = [], options = {}) {
174
+ const resolved = resolveExecutable(command, { cwd: options.cwd ? String(options.cwd) : process.cwd() });
175
+ const targetCommand = resolved || command;
176
+
177
+ if (IS_WINDOWS) {
178
+ const lower = targetCommand.toLowerCase();
179
+ const isBatch = lower.endsWith(".cmd") || lower.endsWith(".bat");
180
+
181
+ if (isBatch) {
182
+ const comspec = process.env.ComSpec || "cmd.exe";
183
+ return {
184
+ command: comspec,
185
+ args: ["/d", "/s", "/c", `"${quoteArg(targetCommand)}"`, ...args.map(quoteArg)],
186
+ options: {
187
+ ...options,
188
+ windowsVerbatimArguments: true,
189
+ },
190
+ };
191
+ }
192
+ }
193
+
194
+ return {
195
+ command: targetCommand,
196
+ args,
197
+ options,
198
+ };
199
+ }
200
+
201
+ /**
202
+ * Escapes and quotes an argument for Windows cmd.exe / PowerShell.
203
+ */
204
+ export function quoteArg(arg) {
205
+ const s = String(arg ?? "");
206
+ if (!s) return '""';
207
+ if (!/[\s"&|<>()^%!=]/.test(s)) return s;
208
+ return `"${s.replace(/"/g, '\\"')}"`;
209
+ }
210
+
211
+ /**
212
+ * Terminate a process and all of its spawned child processes cleanly.
213
+ * Uses taskkill /T /F on Windows; process group kill or fallback on POSIX.
214
+ *
215
+ * @param {import("node:child_process").ChildProcess|number} procOrPid
216
+ * @param {string} [signal="SIGTERM"]
217
+ */
218
+ export function killProcessTree(procOrPid, signal = "SIGTERM") {
219
+ const pid = typeof procOrPid === "number" ? procOrPid : procOrPid?.pid;
220
+ if (!pid) return;
221
+
222
+ if (IS_WINDOWS) {
223
+ try {
224
+ if (typeof procOrPid === "object" && typeof procOrPid.kill === "function") {
225
+ procOrPid.kill();
226
+ }
227
+ execFileSync("taskkill.exe", ["/PID", String(pid), "/T", "/F"], {
228
+ stdio: "ignore",
229
+ windowsHide: true,
230
+ timeout: 2000,
231
+ });
232
+ } catch {
233
+ // Process may already have terminated
234
+ }
235
+ } else {
236
+ try {
237
+ process.kill(-pid, signal);
238
+ } catch {
239
+ try {
240
+ if (typeof procOrPid === "object" && typeof procOrPid.kill === "function") {
241
+ procOrPid.kill(signal);
242
+ } else {
243
+ process.kill(pid, signal);
244
+ }
245
+ } catch {}
246
+ }
247
+ }
248
+ }
249
+
250
+ export const isWindows = IS_WINDOWS;
251
+
252
+ export function getPathext() {
253
+ return [...DEFAULT_PATHEXT];
254
+ }
255
+
256
+ export function quoteCmdArg(arg) {
257
+ return quoteArg(arg);
258
+ }
259
+
260
+ export function quotePowerShellArg(arg) {
261
+ const s = String(arg ?? "");
262
+ if (!s) return "''";
263
+ if (!/[\s"&|<>()^%!=']/.test(s)) return s;
264
+ return `'${s.replace(/'/g, "''")}'`;
265
+ }
266
+
267
+ export function getNamedPipePath(identifier) {
268
+ const slug = String(identifier).replace(/[^A-Za-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(-64);
269
+ return `\\\\.\\pipe\\cirvix-${slug || "default"}`;
270
+ }
package/src/index.mjs CHANGED
@@ -141,3 +141,28 @@ export { init, STARTER_POLICY } from "./commands/init.mjs";
141
141
  export { status } from "./commands/status.mjs";
142
142
  export { demo } from "./commands/demo.mjs";
143
143
  export { check as policyCheck, explain as policyExplain, list as policyList, loadPolicyFile, test as policyTest } from "./commands/policy.mjs";
144
+
145
+ /* Adapters & Platform ---------------------------------------------------- */
146
+ export * as adapters from "./adapters/index.mjs";
147
+ export * as windows from "./core/windows.mjs";
148
+ export { ConfigBackupManager, SafeConfigPatcher, parseConfigJson, stripJsonComments } from "./core/config-store.mjs";
149
+
150
+ /* Platform Transformation Primitives ------------------------------------- */
151
+ export { evaluateIntent, classifyIntent, INTENT_CATEGORIES } from "./core/intent.mjs";
152
+ export { SessionTracker, CHAIN_TYPES } from "./core/session.mjs";
153
+ export { BehavioralBaseline } from "./core/baseline.mjs";
154
+ export { KillSwitchEngine, globalKillSwitch, KILL_SCOPES } from "./core/kill-switch.mjs";
155
+ export { AgentSandbox, SANDBOX_ADAPTERS } from "./core/sandbox.mjs";
156
+ export { ShadowEngine } from "./core/shadow.mjs";
157
+ export { runRedTeamSuite, BUILTIN_ATTACK_PLUGINS, ATTACK_VECTORS } from "./core/redteam/index.mjs";
158
+ export { inspectMcpServer, VERIFICATION_STATUS } from "./core/verified.mjs";
159
+ export {
160
+ generateAgentKeypair,
161
+ issueCryptographicPassport,
162
+ verifyPassportSignature,
163
+ rotatePassportKeys,
164
+ } from "./core/passport.mjs";
165
+ export {
166
+ issueActionReceipt,
167
+ verifyActionReceipt,
168
+ } from "./core/proof.mjs";