termyte 0.1.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.
Files changed (43) hide show
  1. package/README.md +50 -0
  2. package/dist/__tests__/command-sandbox.test.d.ts +2 -0
  3. package/dist/__tests__/command-sandbox.test.d.ts.map +1 -0
  4. package/dist/__tests__/command-sandbox.test.js +18 -0
  5. package/dist/__tests__/command-sandbox.test.js.map +1 -0
  6. package/dist/__tests__/contract.test.d.ts +2 -0
  7. package/dist/__tests__/contract.test.d.ts.map +1 -0
  8. package/dist/__tests__/contract.test.js +70 -0
  9. package/dist/__tests__/contract.test.js.map +1 -0
  10. package/dist/__tests__/offline.test.d.ts +2 -0
  11. package/dist/__tests__/offline.test.d.ts.map +1 -0
  12. package/dist/__tests__/offline.test.js +22 -0
  13. package/dist/__tests__/offline.test.js.map +1 -0
  14. package/dist/cache.d.ts +26 -0
  15. package/dist/cache.d.ts.map +1 -0
  16. package/dist/cache.js +81 -0
  17. package/dist/cache.js.map +1 -0
  18. package/dist/client.d.ts +9 -0
  19. package/dist/client.d.ts.map +1 -0
  20. package/dist/client.js +32 -0
  21. package/dist/client.js.map +1 -0
  22. package/dist/cloud-client.d.ts +11 -0
  23. package/dist/cloud-client.d.ts.map +1 -0
  24. package/dist/cloud-client.js +89 -0
  25. package/dist/cloud-client.js.map +1 -0
  26. package/dist/executor.d.ts +20 -0
  27. package/dist/executor.d.ts.map +1 -0
  28. package/dist/executor.js +93 -0
  29. package/dist/executor.js.map +1 -0
  30. package/dist/index.d.ts +3 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +383 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/policy.d.ts +6 -0
  35. package/dist/policy.d.ts.map +1 -0
  36. package/dist/policy.js +24 -0
  37. package/dist/policy.js.map +1 -0
  38. package/dist/sanitizer.d.ts +28 -0
  39. package/dist/sanitizer.d.ts.map +1 -0
  40. package/dist/sanitizer.js +112 -0
  41. package/dist/sanitizer.js.map +1 -0
  42. package/package.json +49 -0
  43. package/proto/kernel.proto +101 -0
package/README.md ADDED
@@ -0,0 +1,50 @@
1
+ # Termyte — Terminal Governance for Coding Agents
2
+
3
+ Termyte is a lightweight, terminal-first governance runtime designed to protect your codebase from catastrophic agent actions (like `rm -rf /` or accidental database drops).
4
+
5
+ It provides a secure "Split-Plane" architecture where your coding agent (Claude Code, Cursor, etc.) proposes actions, and Termyte evaluates them against a deterministic sandbox and an LLM judge before execution.
6
+
7
+ ## Features
8
+
9
+ - **Causal Guard**: Deterministic command analysis for high-risk operations.
10
+ - **Agent Ledger**: Every action, verdict, and outcome is recorded in a secure, immutable ledger.
11
+ - **Zero-Friction Auth**: Device-based identification (no API keys to manage).
12
+ - **Terminal First**: View governance events directly in your terminal with `npx termyte log`.
13
+
14
+ ## Getting Started
15
+
16
+ ### 1. Initialize Termyte
17
+ Run this to generate your unique device ID and setup local config:
18
+ ```bash
19
+ npx termyte init
20
+ ```
21
+
22
+ ### 2. Configure your Agent
23
+ Add Termyte as an MCP server to your favorite tool.
24
+
25
+ #### Claude Code config:
26
+ Add the following to your `claude_desktop_config.json`:
27
+ ```json
28
+ {
29
+ "mcpServers": {
30
+ "termyte": {
31
+ "command": "npx",
32
+ "args": ["-y", "termyte"],
33
+ "env": {
34
+ "TERMYTE_API_URL": "https://mcp.causalos.xyz"
35
+ }
36
+ }
37
+ }
38
+ }
39
+ ```
40
+
41
+ ### 3. Usage
42
+ Once configured, Termyte will automatically intercept sensitive tool calls. You can monitor the activity:
43
+ ```bash
44
+ npx termyte log
45
+ ```
46
+
47
+ ## How it Works
48
+ 1. **Prepare**: The agent calls `causal_guard` with the proposed command.
49
+ 2. **Judge**: Termyte evaluates the risk level and historical context.
50
+ 3. **Commit**: After the agent executes the tool, it records the success/failure to the ledger.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=command-sandbox.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command-sandbox.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/command-sandbox.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,18 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { nativeExec, tokenize } from "../executor.js";
3
+ describe("Command Execution Sandbox", () => {
4
+ it("should tokenize commands correctly", () => {
5
+ expect(tokenize('ls -la "/path/with spaces"')).toEqual(['ls', '-la', '/path/with spaces']);
6
+ });
7
+ it("ALLOW-01: 'node -v' should execute successfully", async () => {
8
+ const result = await nativeExec("node -v");
9
+ expect(result.exit_code).toBe(0);
10
+ expect(result.stdout).toMatch(/v\d+\.\d+\.\d+/);
11
+ });
12
+ it("ALLOW-02: 'git --version' should pass validation", async () => {
13
+ const result = await nativeExec("git --version");
14
+ expect(result.exit_code).toBe(0);
15
+ expect(result.stdout).toContain("git version");
16
+ });
17
+ });
18
+ //# sourceMappingURL=command-sandbox.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command-sandbox.test.js","sourceRoot":"","sources":["../../src/__tests__/command-sandbox.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAEtD,QAAQ,CAAC,2BAA2B,EAAE,GAAG,EAAE;IACvC,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC1C,MAAM,CAAC,QAAQ,CAAC,4BAA4B,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,mBAAmB,CAAC,CAAC,CAAC;IAC/F,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,KAAK,IAAI,EAAE;QAC7D,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,SAAS,CAAC,CAAC;QAC3C,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kDAAkD,EAAE,KAAK,IAAI,EAAE;QAC9D,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,eAAe,CAAC,CAAC;QACjD,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=contract.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/contract.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,70 @@
1
+ import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
2
+ import http from "node:http";
3
+ import { CloudKernelClient } from "../cloud-client.js";
4
+ let server;
5
+ let baseUrl = "";
6
+ const requests = [];
7
+ describe("Cloud contract", () => {
8
+ beforeAll(async () => {
9
+ server = http.createServer((req, res) => {
10
+ const chunks = [];
11
+ req.on("data", (d) => chunks.push(d));
12
+ req.on("end", () => {
13
+ const body = chunks.length ? JSON.parse(Buffer.concat(chunks).toString("utf-8")) : {};
14
+ requests.push({ url: req.url || "", body, method: req.method });
15
+ res.setHeader("content-type", "application/json");
16
+ if ((req.url || "").startsWith("/v1/governance/prepare")) {
17
+ res.end(JSON.stringify({ verdict: "ALLOW", reason: "ok", tool_call_id: "tc_1" }));
18
+ }
19
+ else if ((req.url || "").startsWith("/v1/governance/commit")) {
20
+ res.end(JSON.stringify({ status: "success" }));
21
+ }
22
+ else {
23
+ res.end(JSON.stringify({ ok: true }));
24
+ }
25
+ });
26
+ });
27
+ await new Promise((resolve) => {
28
+ server.listen(0, "127.0.0.1", () => resolve());
29
+ });
30
+ const addr = server.address();
31
+ if (addr && typeof addr === "object") {
32
+ baseUrl = `http://127.0.0.1:${addr.port}`;
33
+ }
34
+ // Mock environment variables
35
+ process.env.TERMYTE_API_URL = baseUrl;
36
+ process.env.TERMYTE_DEVICE_ID = "test-device-id";
37
+ });
38
+ beforeEach(() => {
39
+ requests.length = 0;
40
+ });
41
+ afterAll(async () => {
42
+ await new Promise((resolve) => server.close(() => resolve()));
43
+ });
44
+ it("prepareToolCall calls /v1/governance/prepare with correct payload", async () => {
45
+ const client = new CloudKernelClient();
46
+ const payload = { command: "ls" };
47
+ await client.prepareToolCall("session-1", "execute", payload);
48
+ expect(requests[0]?.url).toBe("/v1/governance/prepare");
49
+ expect(requests[0]?.method).toBe("POST");
50
+ expect(requests[0]?.body).toMatchObject({
51
+ session_id: "session-1",
52
+ tool_name: "execute",
53
+ payload_json: payload,
54
+ });
55
+ });
56
+ it("commitToolCall calls /v1/governance/commit with correct payload", async () => {
57
+ const client = new CloudKernelClient();
58
+ const outcome = { stdout: "ok" };
59
+ await client.commitToolCall("tc_1", outcome, true, 0);
60
+ expect(requests[0]?.url).toBe("/v1/governance/commit");
61
+ expect(requests[0]?.method).toBe("POST");
62
+ expect(requests[0]?.body).toMatchObject({
63
+ tool_call_id: "tc_1",
64
+ outcome_json: outcome,
65
+ success: true,
66
+ exit_code: 0
67
+ });
68
+ });
69
+ });
70
+ //# sourceMappingURL=contract.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.test.js","sourceRoot":"","sources":["../../src/__tests__/contract.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=offline.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"offline.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/offline.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,22 @@
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ import { KernelClient } from "../client.js";
3
+ describe("Offline Resilience", () => {
4
+ let client;
5
+ beforeEach(() => {
6
+ client = new KernelClient();
7
+ // Mock cloudClient to throw error (simulating network down)
8
+ vi.spyOn(client.cloudClient, 'prepareToolCall').mockRejectedValue(new Error("Network Down"));
9
+ vi.spyOn(client.cloudClient, 'commitToolCall').mockRejectedValue(new Error("Network Down"));
10
+ });
11
+ it("should implement fail-closed in KernelClient for prepareToolCall", async () => {
12
+ // Based on the current client.ts implementation, it fails closed (BLOCK)
13
+ const verdict = await client.prepareToolCall("s1", "execute", { command: "ls" });
14
+ expect(verdict.verdict).toBe("BLOCK");
15
+ expect(verdict.reason).toContain("Governance runtime unreachable");
16
+ });
17
+ it("should handle failed commit gracefully", async () => {
18
+ const result = await client.commitToolCall("tc1", { stdout: "" }, true, 0);
19
+ expect(result.status).toBe("local_only");
20
+ });
21
+ });
22
+ //# sourceMappingURL=offline.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"offline.test.js","sourceRoot":"","sources":["../../src/__tests__/offline.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;IAChC,IAAI,MAAoB,CAAC;IAEzB,UAAU,CAAC,GAAG,EAAE;QACZ,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC;QAC5B,4DAA4D;QAC5D,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,WAAW,EAAE,iBAAiB,CAAC,CAAC,iBAAiB,CAAC,IAAI,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC;QAC7F,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,WAAW,EAAE,gBAAgB,CAAC,CAAC,iBAAiB,CAAC,IAAI,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC;IAChG,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kEAAkE,EAAE,KAAK,IAAI,EAAE;QAC9E,yEAAyE;QACzE,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QACjF,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACtC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,gCAAgC,CAAC,CAAC;IACvE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,KAAK,IAAI,EAAE;QACpD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAC3E,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
@@ -0,0 +1,26 @@
1
+ export interface Verdict {
2
+ recommendation: 'PROCEED' | 'CAUTION' | 'ABORT';
3
+ reason: string;
4
+ confidence: number;
5
+ risk_score: number;
6
+ }
7
+ export declare class HotCache {
8
+ private static allowCache;
9
+ private static blockCache;
10
+ /**
11
+ * Retrieves a cached verdict for a given action fingerprint.
12
+ */
13
+ static get(fingerprint: string): Verdict | undefined;
14
+ /**
15
+ * Stores a verdict in the local hot cache.
16
+ */
17
+ static set(fingerprint: string, verdict: Verdict): void;
18
+ /**
19
+ * Batch updates the cache from a cloud sync payload.
20
+ */
21
+ static updateFromSync(data: Record<string, Verdict>): void;
22
+ private static getCachePath;
23
+ static saveToDisk(): void;
24
+ static loadFromDisk(): void;
25
+ }
26
+ //# sourceMappingURL=cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,OAAO;IACpB,cAAc,EAAE,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;IAChD,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACtB;AAED,qBAAa,QAAQ;IACjB,OAAO,CAAC,MAAM,CAAC,UAAU,CAGtB;IACH,OAAO,CAAC,MAAM,CAAC,UAAU,CAGtB;IAEH;;OAEG;WACW,GAAG,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS;IAI3D;;OAEG;WACW,GAAG,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI;IAQ9D;;OAEG;WACW,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAMjE,OAAO,CAAC,MAAM,CAAC,YAAY;WAQb,UAAU,IAAI,IAAI;WAoBlB,YAAY,IAAI,IAAI;CAcrC"}
package/dist/cache.js ADDED
@@ -0,0 +1,81 @@
1
+ import { LRUCache } from 'lru-cache';
2
+ import * as fs from 'fs';
3
+ import * as path from 'path';
4
+ import * as os from 'os';
5
+ export class HotCache {
6
+ static allowCache = new LRUCache({
7
+ max: 500,
8
+ ttl: 1000 * 60 * 60, // 1 hour
9
+ });
10
+ static blockCache = new LRUCache({
11
+ max: 1000,
12
+ ttl: 1000 * 60 * 60 * 24 * 7, // 7 days
13
+ });
14
+ /**
15
+ * Retrieves a cached verdict for a given action fingerprint.
16
+ */
17
+ static get(fingerprint) {
18
+ return this.blockCache.get(fingerprint) || this.allowCache.get(fingerprint);
19
+ }
20
+ /**
21
+ * Stores a verdict in the local hot cache.
22
+ */
23
+ static set(fingerprint, verdict) {
24
+ if (verdict.recommendation === 'ABORT') {
25
+ this.blockCache.set(fingerprint, verdict);
26
+ return;
27
+ }
28
+ this.allowCache.set(fingerprint, verdict);
29
+ }
30
+ /**
31
+ * Batch updates the cache from a cloud sync payload.
32
+ */
33
+ static updateFromSync(data) {
34
+ for (const [fingerprint, verdict] of Object.entries(data)) {
35
+ this.set(fingerprint, verdict);
36
+ }
37
+ }
38
+ static getCachePath() {
39
+ const dir = path.join(os.homedir(), '.termyte');
40
+ if (!fs.existsSync(dir)) {
41
+ fs.mkdirSync(dir, { recursive: true });
42
+ }
43
+ return path.join(dir, 'governance_cache.json');
44
+ }
45
+ static saveToDisk() {
46
+ try {
47
+ const data = {};
48
+ // @ts-ignore - access internal cache entries for serialization
49
+ for (const [key, value] of this.allowCache.dump()) {
50
+ data[key] = value;
51
+ }
52
+ // @ts-ignore - access internal cache entries for serialization
53
+ for (const [key, value] of this.blockCache.dump()) {
54
+ data[key] = value;
55
+ }
56
+ const p = this.getCachePath();
57
+ const tempPath = `${p}.tmp`;
58
+ fs.writeFileSync(tempPath, JSON.stringify(data, null, 2));
59
+ fs.renameSync(tempPath, p);
60
+ }
61
+ catch (err) {
62
+ console.error('[HotCache] Failed to save to disk:', err);
63
+ }
64
+ }
65
+ static loadFromDisk() {
66
+ try {
67
+ const cachePath = this.getCachePath();
68
+ if (fs.existsSync(cachePath)) {
69
+ const data = JSON.parse(fs.readFileSync(cachePath, 'utf8'));
70
+ for (const [key, val] of Object.entries(data)) {
71
+ this.set(key, val);
72
+ }
73
+ console.error(`[HotCache] Loaded ${Object.keys(data).length} patterns from disk.`);
74
+ }
75
+ }
76
+ catch (err) {
77
+ console.error('[HotCache] Failed to load from disk:', err);
78
+ }
79
+ }
80
+ }
81
+ //# sourceMappingURL=cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import { CloudKernelClient } from './cloud-client.js';
2
+ export declare class KernelClient {
3
+ cloudClient: CloudKernelClient;
4
+ constructor();
5
+ prepareToolCall(session_id: string, tool_name: string, payload: any): Promise<any>;
6
+ commitToolCall(tool_call_id: string, outcome: any, success: boolean, exitCode?: number): Promise<any>;
7
+ }
8
+ export declare const kernel: KernelClient;
9
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAEtD,qBAAa,YAAY;IAChB,WAAW,EAAE,iBAAiB,CAAC;;IAMhC,eAAe,CACnB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,GAAG,GACX,OAAO,CAAC,GAAG,CAAC;IAaT,cAAc,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;CAS5G;AAED,eAAO,MAAM,MAAM,cAAqB,CAAC"}
package/dist/client.js ADDED
@@ -0,0 +1,32 @@
1
+ import { CloudKernelClient } from './cloud-client.js';
2
+ export class KernelClient {
3
+ cloudClient;
4
+ constructor() {
5
+ this.cloudClient = new CloudKernelClient();
6
+ }
7
+ async prepareToolCall(session_id, tool_name, payload) {
8
+ try {
9
+ return await this.cloudClient.prepareToolCall(session_id, tool_name, payload);
10
+ }
11
+ catch (err) {
12
+ console.error(`[KernelClient] Governance failure: ${err.message}. Failing closed.`);
13
+ return {
14
+ verdict: "BLOCK",
15
+ reason: "Governance runtime unreachable. Action blocked for safety.",
16
+ source: "failsafe"
17
+ };
18
+ }
19
+ }
20
+ async commitToolCall(tool_call_id, outcome, success, exitCode) {
21
+ try {
22
+ return await this.cloudClient.commitToolCall(tool_call_id, outcome, success, exitCode);
23
+ }
24
+ catch (err) {
25
+ // Log locally if cloud is down, but don't crash
26
+ console.error(`[KernelClient] Failed to commit outcome: ${err}`);
27
+ return { status: "local_only" };
28
+ }
29
+ }
30
+ }
31
+ export const kernel = new KernelClient();
32
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAEtD,MAAM,OAAO,YAAY;IAChB,WAAW,CAAoB;IAEtC;QACE,IAAI,CAAC,WAAW,GAAG,IAAI,iBAAiB,EAAE,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,eAAe,CACnB,UAAkB,EAClB,SAAiB,EACjB,OAAY;QAEZ,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,CAAC,WAAW,CAAC,eAAe,CAAC,UAAU,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;QAChF,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,OAAO,CAAC,KAAK,CAAC,sCAAsC,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC;YACpF,OAAO;gBACH,OAAO,EAAE,OAAO;gBAChB,MAAM,EAAE,4DAA4D;gBACpE,MAAM,EAAE,UAAU;aACrB,CAAC;QACJ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,YAAoB,EAAE,OAAY,EAAE,OAAgB,EAAE,QAAiB;QAC1F,IAAI,CAAC;YACD,OAAO,MAAM,IAAI,CAAC,WAAW,CAAC,cAAc,CAAC,YAAY,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC3F,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACX,gDAAgD;YAChD,OAAO,CAAC,KAAK,CAAC,4CAA4C,GAAG,EAAE,CAAC,CAAC;YACjE,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;QACpC,CAAC;IACH,CAAC;CACF;AAED,MAAM,CAAC,MAAM,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC"}
@@ -0,0 +1,11 @@
1
+ export declare class CloudKernelClient {
2
+ private deviceId;
3
+ private baseURL;
4
+ constructor();
5
+ getDeviceId(): Promise<string>;
6
+ private request;
7
+ prepareToolCall(session_id: string, tool_name: string, payload: any): Promise<any>;
8
+ commitToolCall(tool_call_id: string, outcome: any, success: boolean, exitCode?: number): Promise<any>;
9
+ getMetrics(): Promise<any>;
10
+ }
11
+ //# sourceMappingURL=cloud-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloud-client.d.ts","sourceRoot":"","sources":["../src/cloud-client.ts"],"names":[],"mappings":"AAKA,qBAAa,iBAAiB;IAC1B,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,OAAO,CAAS;;IAMlB,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC;YAoBtB,OAAO;IAwCf,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG;IAQnE,cAAc,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM;IAStF,UAAU;CAGnB"}
@@ -0,0 +1,89 @@
1
+ import * as fs from 'fs/promises';
2
+ import * as path from 'path';
3
+ import * as os from 'os';
4
+ import * as https from 'https';
5
+ export class CloudKernelClient {
6
+ deviceId = null;
7
+ baseURL;
8
+ constructor() {
9
+ this.baseURL = process.env.TERMYTE_API_URL || 'https://mcp.causalos.xyz';
10
+ }
11
+ async getDeviceId() {
12
+ if (this.deviceId)
13
+ return this.deviceId;
14
+ if (process.env.TERMYTE_DEVICE_ID) {
15
+ this.deviceId = process.env.TERMYTE_DEVICE_ID;
16
+ return this.deviceId;
17
+ }
18
+ const configPath = path.join(os.homedir(), '.termyte', 'config.json');
19
+ try {
20
+ const config = JSON.parse(await fs.readFile(configPath, 'utf-8'));
21
+ if (config.device_id) {
22
+ this.deviceId = config.device_id;
23
+ return this.deviceId;
24
+ }
25
+ }
26
+ catch (e) { }
27
+ throw new Error("TERMYTE_DEVICE_ID not found. Run 'npx termyte init' first.");
28
+ }
29
+ async request(method, endpoint, body) {
30
+ const url = new URL(endpoint, this.baseURL);
31
+ const deviceId = await this.getDeviceId();
32
+ return new Promise((resolve, reject) => {
33
+ const options = {
34
+ method,
35
+ hostname: url.hostname,
36
+ path: url.pathname,
37
+ headers: {
38
+ 'Content-Type': 'application/json',
39
+ 'x-termyte-device-id': deviceId
40
+ },
41
+ timeout: 10000
42
+ };
43
+ const req = https.request(options, (res) => {
44
+ let data = '';
45
+ res.on('data', (chunk) => data += chunk);
46
+ res.on('end', () => {
47
+ try {
48
+ const parsed = JSON.parse(data);
49
+ if (res.statusCode && res.statusCode >= 400) {
50
+ reject(new Error(parsed.message || `Server error: ${res.statusCode}`));
51
+ }
52
+ else {
53
+ resolve(parsed);
54
+ }
55
+ }
56
+ catch (e) {
57
+ if (res.statusCode === 204)
58
+ resolve({});
59
+ else
60
+ reject(new Error('Invalid JSON response'));
61
+ }
62
+ });
63
+ });
64
+ req.on('error', reject);
65
+ if (body)
66
+ req.write(JSON.stringify(body));
67
+ req.end();
68
+ });
69
+ }
70
+ async prepareToolCall(session_id, tool_name, payload) {
71
+ return this.request('POST', '/v1/governance/prepare', {
72
+ session_id,
73
+ tool_name,
74
+ payload_json: payload,
75
+ });
76
+ }
77
+ async commitToolCall(tool_call_id, outcome, success, exitCode) {
78
+ return this.request('POST', '/v1/governance/commit', {
79
+ tool_call_id,
80
+ outcome_json: outcome,
81
+ success,
82
+ exit_code: exitCode
83
+ });
84
+ }
85
+ async getMetrics() {
86
+ return this.request('GET', '/metrics');
87
+ }
88
+ }
89
+ //# sourceMappingURL=cloud-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloud-client.js","sourceRoot":"","sources":["../src/cloud-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ export interface ExecutionResult {
2
+ stdout: string;
3
+ stderr: string;
4
+ exit_code: number;
5
+ }
6
+ /**
7
+ * Tokenize a command string into a binary and its arguments.
8
+ * Handles single/double quotes and backslash escaping.
9
+ */
10
+ export declare function tokenize(command: string): string[];
11
+ /**
12
+ * Ensures batch commands on Windows are invoked with .cmd extension.
13
+ */
14
+ export declare function resolveWindowsVerb(verb: string): string;
15
+ /**
16
+ * Execute a command natively after it has passed Termyte Governance.
17
+ * Uses execFile (not exec) to prevent shell injection at the OS level.
18
+ */
19
+ export declare function nativeExec(rawCommand: string, timeoutMs?: number): Promise<ExecutionResult>;
20
+ //# sourceMappingURL=executor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAqClD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOvD;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,SAAS,GAAG,OAAO,CAAC,eAAe,CAAC,CAyBjG"}
@@ -0,0 +1,93 @@
1
+ import { execFile } from "child_process";
2
+ import { promisify } from "util";
3
+ const execFileAsync = promisify(execFile);
4
+ /**
5
+ * Tokenize a command string into a binary and its arguments.
6
+ * Handles single/double quotes and backslash escaping.
7
+ */
8
+ export function tokenize(command) {
9
+ const tokens = [];
10
+ let current = "";
11
+ let inSingleQuote = false;
12
+ let inDoubleQuote = false;
13
+ let escaping = false;
14
+ for (let i = 0; i < command.length; i++) {
15
+ const ch = command[i];
16
+ if (escaping) {
17
+ current += ch;
18
+ escaping = false;
19
+ continue;
20
+ }
21
+ if (ch === "\\") {
22
+ escaping = true;
23
+ continue;
24
+ }
25
+ if (ch === "'" && !inSingleQuote && !inDoubleQuote) {
26
+ inSingleQuote = true;
27
+ }
28
+ else if (ch === "'" && inSingleQuote && !inDoubleQuote) {
29
+ inSingleQuote = false;
30
+ }
31
+ else if (ch === "\"" && !inSingleQuote && !inDoubleQuote) {
32
+ inDoubleQuote = true;
33
+ }
34
+ else if (ch === "\"" && inDoubleQuote && !inSingleQuote) {
35
+ inDoubleQuote = false;
36
+ }
37
+ else if (ch === " " && !inSingleQuote && !inDoubleQuote) {
38
+ if (current.length > 0) {
39
+ tokens.push(current);
40
+ current = "";
41
+ }
42
+ }
43
+ else {
44
+ current += ch;
45
+ }
46
+ }
47
+ if (current.length > 0)
48
+ tokens.push(current);
49
+ return tokens;
50
+ }
51
+ /**
52
+ * Ensures batch commands on Windows are invoked with .cmd extension.
53
+ */
54
+ export function resolveWindowsVerb(verb) {
55
+ if (process.platform !== "win32")
56
+ return verb;
57
+ const batchCommands = ["npm", "npx", "yarn", "pnpm", "tsc", "cargo"];
58
+ if (batchCommands.includes(verb.toLowerCase())) {
59
+ return `${verb}.cmd`;
60
+ }
61
+ return verb;
62
+ }
63
+ /**
64
+ * Execute a command natively after it has passed Termyte Governance.
65
+ * Uses execFile (not exec) to prevent shell injection at the OS level.
66
+ */
67
+ export async function nativeExec(rawCommand, timeoutMs = 30_000) {
68
+ const tokens = tokenize(rawCommand.trim());
69
+ const rawVerbToken = tokens[0];
70
+ if (!rawVerbToken)
71
+ throw new Error("Empty command");
72
+ // Extract verb (strip path) and resolve Windows extension
73
+ const rawVerb = rawVerbToken.replace(/^.*[\\\/]/, "");
74
+ const verb = resolveWindowsVerb(rawVerb);
75
+ const args = tokens.slice(1);
76
+ try {
77
+ const { stdout, stderr } = await execFileAsync(verb, args, {
78
+ timeout: timeoutMs,
79
+ maxBuffer: 10 * 1024 * 1024, // 10 MB max output
80
+ windowsHide: true,
81
+ shell: false,
82
+ });
83
+ return { stdout, stderr, exit_code: 0 };
84
+ }
85
+ catch (err) {
86
+ return {
87
+ stdout: err.stdout ?? "",
88
+ stderr: err.stderr ?? err.message,
89
+ exit_code: err.code ?? 1,
90
+ };
91
+ }
92
+ }
93
+ //# sourceMappingURL=executor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executor.js","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}