@cygnuxlabs/tracekit 0.0.0-stage → 1.2.2

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 (40) hide show
  1. package/README.md +294 -2
  2. package/bin/tracekit.mjs +30 -0
  3. package/dist/v1.d.ts +97 -0
  4. package/dist/v1.js +535 -0
  5. package/dist/v2/adapters/claude-agent-sdk.d.ts +45 -0
  6. package/dist/v2/adapters/claude-agent-sdk.js +142 -0
  7. package/dist/v2/adapters/gate.d.ts +62 -0
  8. package/dist/v2/adapters/gate.js +195 -0
  9. package/dist/v2/adapters/openai-agents.d.ts +85 -0
  10. package/dist/v2/adapters/openai-agents.js +114 -0
  11. package/dist/v2/adapters/vercel-ai.d.ts +51 -0
  12. package/dist/v2/adapters/vercel-ai.js +132 -0
  13. package/dist/v2/client.d.ts +159 -0
  14. package/dist/v2/client.js +749 -0
  15. package/dist/v2/executor.d.ts +49 -0
  16. package/dist/v2/executor.js +57 -0
  17. package/dist/v2/jcs.d.ts +5 -0
  18. package/dist/v2/jcs.js +12 -0
  19. package/dist/v2/launcher.d.ts +10 -0
  20. package/dist/v2/launcher.js +50 -0
  21. package/dist/v2/rpc_schema.d.ts +3 -0
  22. package/dist/v2/rpc_schema.js +43 -0
  23. package/dist/v2/validate.d.ts +6 -0
  24. package/dist/v2/validate.js +70 -0
  25. package/dist/verify/cli.d.ts +1 -0
  26. package/dist/verify/cli.js +46 -0
  27. package/dist/verify/detect.d.ts +41 -0
  28. package/dist/verify/detect.js +289 -0
  29. package/dist/verify/index.d.ts +34 -0
  30. package/dist/verify/index.js +930 -0
  31. package/dist/verify/jcs.d.ts +19 -0
  32. package/dist/verify/jcs.js +154 -0
  33. package/dist/verify/primitives.d.ts +62 -0
  34. package/dist/verify/primitives.js +298 -0
  35. package/dist/verify/schema.d.ts +11 -0
  36. package/dist/verify/schema.js +98 -0
  37. package/dist/verify/tracekit.event.v2.json +4588 -0
  38. package/dist/verify/zip.d.ts +17 -0
  39. package/dist/verify/zip.js +145 -0
  40. package/package.json +42 -4
@@ -0,0 +1,17 @@
1
+ /**
2
+ * The zip reader of docs/format-v2.md §9: one canonical layout, stored and deflated entries (Web Crypto's sibling
3
+ * DecompressionStream), no zip64, under the bundle limits. Names that could escape a directory, symlinks and duplicate
4
+ * entries are refused.
5
+ */
6
+ export declare const MAX_ENTRIES = 10000;
7
+ export declare const MAX_ENTRY: number;
8
+ export declare const MAX_TOTAL: number;
9
+ export declare class Unusable extends Error {
10
+ }
11
+ /**
12
+ * {name: bytes} of a zip in the one layout of docs/format-v2.md §9, entry by entry under the count and size caps: the
13
+ * end record ends the file, with no comment; the central directory sits between the last entry and it; the entries are
14
+ * contiguous from offset 0, each local header repeating its central entry; no zip64, extra or comment field, or
15
+ * general-purpose flag.
16
+ */
17
+ export declare function readZip(data: Uint8Array): Promise<Map<string, Uint8Array>>;
@@ -0,0 +1,145 @@
1
+ /**
2
+ * The zip reader of docs/format-v2.md §9: one canonical layout, stored and deflated entries (Web Crypto's sibling
3
+ * DecompressionStream), no zip64, under the bundle limits. Names that could escape a directory, symlinks and duplicate
4
+ * entries are refused.
5
+ */
6
+ export const MAX_ENTRIES = 10_000;
7
+ export const MAX_ENTRY = 64 << 20;
8
+ export const MAX_TOTAL = 512 << 20;
9
+ const NAME = /^[A-Za-z0-9_-][A-Za-z0-9._-]*(\/[A-Za-z0-9._-]+)*$/;
10
+ export class Unusable extends Error {
11
+ }
12
+ let CRC;
13
+ function crc32(data) {
14
+ if (!CRC) {
15
+ CRC = new Uint32Array(256);
16
+ for (let n = 0; n < 256; n++) {
17
+ let c = n;
18
+ for (let k = 0; k < 8; k++)
19
+ c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
20
+ CRC[n] = c >>> 0;
21
+ }
22
+ }
23
+ let c = 0xffffffff;
24
+ for (const b of data)
25
+ c = CRC[(c ^ b) & 0xff] ^ (c >>> 8);
26
+ return (c ^ 0xffffffff) >>> 0;
27
+ }
28
+ /** The first `limit` bytes a raw deflate stream inflates to; stops reading there, so a zip bomb costs nothing. */
29
+ async function inflate(raw, limit) {
30
+ const reader = new Blob([raw]).stream().pipeThrough(new DecompressionStream("deflate-raw")).getReader();
31
+ const parts = [];
32
+ let n = 0;
33
+ while (n < limit) {
34
+ const { done, value } = await reader.read();
35
+ if (done)
36
+ break;
37
+ parts.push(value);
38
+ n += value.length;
39
+ }
40
+ if (n >= limit)
41
+ await reader.cancel().catch(() => { });
42
+ const out = new Uint8Array(n);
43
+ let at = 0;
44
+ for (const p of parts)
45
+ out.set(p, (at += p.length) - p.length);
46
+ return out.subarray(0, limit);
47
+ }
48
+ /**
49
+ * {name: bytes} of a zip in the one layout of docs/format-v2.md §9, entry by entry under the count and size caps: the
50
+ * end record ends the file, with no comment; the central directory sits between the last entry and it; the entries are
51
+ * contiguous from offset 0, each local header repeating its central entry; no zip64, extra or comment field, or
52
+ * general-purpose flag.
53
+ */
54
+ export async function readZip(data) {
55
+ const v = new DataView(data.buffer, data.byteOffset, data.byteLength);
56
+ const need = (at, n) => {
57
+ if (at < 0 || at + n > data.length)
58
+ throw new Unusable("truncated zip");
59
+ };
60
+ const eocd = data.length - 22;
61
+ if (eocd < 0 || v.getUint32(eocd, true) !== 0x06054b50)
62
+ throw new Unusable("not a zip file, or bytes after its end record");
63
+ const count = v.getUint16(eocd + 10, true), cdSize = v.getUint32(eocd + 12, true), cdOffset = v.getUint32(eocd + 16, true);
64
+ if (v.getUint16(eocd + 4, true) || v.getUint16(eocd + 6, true) || v.getUint16(eocd + 8, true) !== count
65
+ || v.getUint16(eocd + 20, true) || cdOffset + cdSize !== eocd)
66
+ throw new Unusable("not the canonical zip layout: end record");
67
+ if (count > MAX_ENTRIES)
68
+ throw new Unusable("too many entries");
69
+ const entries = [];
70
+ for (let at = cdOffset; at < eocd;) {
71
+ if (at + 46 > eocd)
72
+ throw new Unusable("truncated zip");
73
+ const n = v.getUint16(at + 28, true);
74
+ if (v.getUint32(at, true) !== 0x02014b50 || v.getUint16(at + 30, true) || v.getUint16(at + 32, true) || at + 46 + n > eocd)
75
+ throw new Unusable("bad central directory entry");
76
+ const raw = data.subarray(at + 46, at + 46 + n);
77
+ entries.push({ name: new TextDecoder().decode(raw), raw, flags: v.getUint16(at + 8, true),
78
+ method: v.getUint16(at + 10, true), crc: v.getUint32(at + 16, true), csize: v.getUint32(at + 20, true),
79
+ usize: v.getUint32(at + 24, true), attr: v.getUint32(at + 38, true), local: v.getUint32(at + 42, true) });
80
+ at += 46 + n;
81
+ }
82
+ if (entries.length !== count)
83
+ throw new Unusable("not the canonical zip layout: entry count");
84
+ let pos = 0;
85
+ for (const e of entries) {
86
+ const n = JSON.stringify(e.name.slice(0, 80));
87
+ if (e.csize === 0xffffffff || e.usize === 0xffffffff || e.local === 0xffffffff)
88
+ throw new Unusable("zip64 is not supported");
89
+ if (e.method !== 0 && e.method !== 8 || e.method === 0 && e.csize !== e.usize)
90
+ throw new Unusable(`${n}: compression method ${e.method} (only stored and deflate)`);
91
+ if (e.flags & 1)
92
+ throw new Unusable(`${n} is encrypted`);
93
+ if (e.flags)
94
+ throw new Unusable(`${n}: general-purpose flags 0x${e.flags.toString(16)}`);
95
+ if (e.local !== pos)
96
+ throw new Unusable(`not the canonical zip layout: ${n} does not follow the entry before it`);
97
+ need(e.local, 30 + e.raw.length);
98
+ const local = data.subarray(e.local + 30, e.local + 30 + e.raw.length);
99
+ if (v.getUint32(e.local, true) !== 0x04034b50 || v.getUint16(e.local + 6, true) !== e.flags
100
+ || v.getUint16(e.local + 8, true) !== e.method || v.getUint32(e.local + 14, true) !== e.crc
101
+ || v.getUint32(e.local + 18, true) !== e.csize || v.getUint32(e.local + 22, true) !== e.usize
102
+ || v.getUint16(e.local + 26, true) !== e.raw.length || v.getUint16(e.local + 28, true)
103
+ || local.some((b, i) => b !== e.raw[i]))
104
+ throw new Unusable(`bad local header for ${n}: it does not repeat the central entry`);
105
+ pos = e.local + 30 + e.raw.length + e.csize;
106
+ }
107
+ if (pos !== cdOffset)
108
+ throw new Unusable("not the canonical zip layout: bytes before the central directory");
109
+ const files = new Map();
110
+ let total = 0;
111
+ for (const e of entries) {
112
+ const n = e.name;
113
+ if (!NAME.test(n) || n.split("/").some((p) => /^\.+$/.test(p)))
114
+ throw new Unusable(`bad entry name ${JSON.stringify(n.slice(0, 80))}`);
115
+ if (files.has(n))
116
+ throw new Unusable(`duplicate entry ${n}`);
117
+ if (((e.attr >>> 16) & 0o170000) === 0o120000)
118
+ throw new Unusable(`symlink entry ${n}`);
119
+ const start = e.local + 30 + e.raw.length;
120
+ need(start, e.csize);
121
+ const body = data.subarray(start, start + e.csize);
122
+ const cap = Math.min(MAX_ENTRY, MAX_TOTAL - total);
123
+ const limit = Math.min(e.usize, cap + 1);
124
+ let out;
125
+ if (e.method === 0)
126
+ out = body.subarray(0, limit);
127
+ else {
128
+ try {
129
+ out = await inflate(body, limit);
130
+ }
131
+ catch (x) {
132
+ throw new Unusable(`${n}: ${x.message}`);
133
+ }
134
+ }
135
+ if (out.length > cap)
136
+ throw new Unusable(`${n} is larger than the bundle limits`);
137
+ if (out.length !== e.usize)
138
+ throw new Unusable(`${n}: ${out.length} bytes, not the ${e.usize} its entry says`);
139
+ if (crc32(out) !== e.crc)
140
+ throw new Unusable(`${n}: bad CRC-32`);
141
+ files.set(n, out);
142
+ total += out.length;
143
+ }
144
+ return files;
145
+ }
package/package.json CHANGED
@@ -1,6 +1,44 @@
1
1
  {
2
2
  "name": "@cygnuxlabs/tracekit",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.2.2",
4
+ "description": "Tracekit for TypeScript/JavaScript agents: a native client of the Tracekit signer: policy-gated tool calls and signed evidence, no runtime dependencies.",
5
+ "author": "Cygnux Labs",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "main": "dist/v2/client.js",
9
+ "types": "dist/v2/client.d.ts",
10
+ "exports": {
11
+ ".": { "types": "./dist/v2/client.d.ts", "import": "./dist/v2/client.js" },
12
+ "./v1": { "types": "./dist/v1.d.ts", "import": "./dist/v1.js" },
13
+ "./v2": { "types": "./dist/v2/client.d.ts", "import": "./dist/v2/client.js" },
14
+ "./v2/openai-agents": { "types": "./dist/v2/adapters/openai-agents.d.ts", "import": "./dist/v2/adapters/openai-agents.js" },
15
+ "./v2/vercel-ai": { "types": "./dist/v2/adapters/vercel-ai.d.ts", "import": "./dist/v2/adapters/vercel-ai.js" },
16
+ "./v2/claude-agent-sdk": { "types": "./dist/v2/adapters/claude-agent-sdk.d.ts", "import": "./dist/v2/adapters/claude-agent-sdk.js" },
17
+ "./v2/executor": { "types": "./dist/v2/executor.d.ts", "import": "./dist/v2/executor.js" },
18
+ "./verify": { "types": "./dist/verify/index.d.ts", "import": "./dist/verify/index.js" }
19
+ },
20
+ "bin": { "tracekit": "bin/tracekit.mjs" },
21
+ "files": ["dist", "bin", "README.md"],
22
+ "engines": { "node": ">=18" },
23
+ "scripts": {
24
+ "build": "tsc -p .",
25
+ "test": "npm run build && node --test test/*.test.mjs"
26
+ },
27
+ "optionalDependencies": {
28
+ "@cygnuxlabs/tracekit-signer-darwin-arm64": "1.2.2",
29
+ "@cygnuxlabs/tracekit-signer-linux-x64-gnu": "1.2.2",
30
+ "@cygnuxlabs/tracekit-signer-linux-arm64-gnu": "1.2.2",
31
+ "@cygnuxlabs/tracekit-signer-win32-x64": "1.2.2"
32
+ },
33
+ "peerDependencies": { "@openai/agents": ">=0.20.0", "ai": ">=7.0.0", "@anthropic-ai/claude-agent-sdk": ">=0.3.0" },
34
+ "peerDependenciesMeta": {
35
+ "@openai/agents": { "optional": true }, "ai": { "optional": true }, "@anthropic-ai/claude-agent-sdk": { "optional": true }
36
+ },
37
+ "devDependencies": {
38
+ "typescript": "^5.6.0",
39
+ "@types/node": "^22.0.0",
40
+ "openai": "*",
41
+ "@anthropic-ai/sdk": "*"
42
+ },
43
+ "repository": { "type": "git", "url": "https://github.com/Cygnux-Labs/Tracekit" }
44
+ }