@tenuo/core 0.2.5-beta.0 → 0.3.1-beta.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 (52) hide show
  1. package/README.md +18 -6
  2. package/dist/api.d.ts +32 -5
  3. package/dist/api.js.map +1 -1
  4. package/dist/build-info.json +8 -0
  5. package/dist/client.d.ts +7 -1
  6. package/dist/client.js +32 -22
  7. package/dist/client.js.map +1 -1
  8. package/dist/connect.d.ts +53 -0
  9. package/dist/connect.js +177 -0
  10. package/dist/connect.js.map +1 -0
  11. package/dist/constraints.d.ts +29 -2
  12. package/dist/constraints.js +68 -19
  13. package/dist/constraints.js.map +1 -1
  14. package/dist/errors.d.ts +0 -1
  15. package/dist/errors.js.map +1 -1
  16. package/dist/generated/tenuo_wasm.d.ts +31 -4
  17. package/dist/generated/tenuo_wasm.js +65 -4
  18. package/dist/generated/tenuo_wasm_bg.wasm +0 -0
  19. package/dist/generated/tenuo_wasm_bg.wasm.d.ts +3 -0
  20. package/dist/identity.d.ts +25 -0
  21. package/dist/identity.js +61 -0
  22. package/dist/identity.js.map +1 -0
  23. package/dist/index.d.ts +9 -3
  24. package/dist/index.js +5 -1
  25. package/dist/index.js.map +1 -1
  26. package/dist/mcp.d.ts +3 -4
  27. package/dist/mcp.js +11 -20
  28. package/dist/mcp.js.map +1 -1
  29. package/dist/nonce.d.ts +0 -1
  30. package/dist/nonce.js.map +1 -1
  31. package/dist/receipts.d.ts +27 -0
  32. package/dist/receipts.js +128 -0
  33. package/dist/receipts.js.map +1 -0
  34. package/dist/runtime.d.ts +51 -0
  35. package/dist/runtime.js +102 -0
  36. package/dist/runtime.js.map +1 -0
  37. package/dist/session.d.ts +7 -1
  38. package/dist/session.js +14 -0
  39. package/dist/session.js.map +1 -1
  40. package/dist/wasm.d.ts +49 -4
  41. package/dist/wasm.js +53 -6
  42. package/dist/wasm.js.map +1 -1
  43. package/package.json +2 -2
  44. package/dist/api.d.ts.map +0 -1
  45. package/dist/client.d.ts.map +0 -1
  46. package/dist/constraints.d.ts.map +0 -1
  47. package/dist/errors.d.ts.map +0 -1
  48. package/dist/index.d.ts.map +0 -1
  49. package/dist/mcp.d.ts.map +0 -1
  50. package/dist/nonce.d.ts.map +0 -1
  51. package/dist/session.d.ts.map +0 -1
  52. package/dist/wasm.d.ts.map +0 -1
@@ -1,12 +1,34 @@
1
+ import { TenuoConfigurationError } from "./errors.js";
2
+ /**
3
+ * Builders throw on a misspelled option instead of dropping it: an option
4
+ * that vanishes silently (`urlSafe({ domains })`) widens authority. The core
5
+ * applies the same rule to raw constraint objects when the policy compiles.
6
+ */
7
+ function rejectUnknownOptions(builder, options, allowed) {
8
+ if (options === undefined || options === null) {
9
+ return;
10
+ }
11
+ if (typeof options !== "object" || Array.isArray(options)) {
12
+ throw new TenuoConfigurationError(`tenuo.${builder}() expects an options object`);
13
+ }
14
+ const unknown = Object.keys(options).find((key) => !allowed.includes(key));
15
+ if (unknown !== undefined) {
16
+ throw new TenuoConfigurationError(`tenuo.${builder}() received unknown option "${unknown}"; allowed options: ${allowed.join(", ")}`);
17
+ }
18
+ }
1
19
  /**
2
20
  * Constraint builders. These produce plain marker objects; every one is
3
21
  * evaluated by the Rust core, never in TypeScript. The set matches the
4
- * Python SDK and the core `Constraint` enum one for one.
22
+ * Python SDK and the core `Constraint` enum one for one. Builders that
23
+ * validate their input throw `TenuoConfigurationError` at the call site.
24
+ * Deeper checks, such as regex syntax, run in the core when `session()`
25
+ * compiles the policy. Nothing here makes an authorization decision.
5
26
  */
6
27
  /** Directory prefix. Evaluated in core as Subpath — not a string prefix check in TS. */
7
28
  export function under(root, options) {
29
+ rejectUnknownOptions("under", options, ["caseSensitive", "allowEqual"]);
8
30
  if (!root.startsWith("/")) {
9
- throw new Error("tenuo.under() expects an absolute path (start with /)");
31
+ throw new TenuoConfigurationError("tenuo.under() expects an absolute path (start with /)");
10
32
  }
11
33
  const out = {
12
34
  kind: "under",
@@ -21,32 +43,34 @@ export function under(root, options) {
21
43
  return out;
22
44
  }
23
45
  export function email(options) {
46
+ rejectUnknownOptions("email", options, ["domain"]);
24
47
  return { kind: "email", domain: options.domain };
25
48
  }
26
49
  export function max(value) {
27
50
  if (!Number.isFinite(value)) {
28
- throw new Error("tenuo.max() requires a finite number");
51
+ throw new TenuoConfigurationError("tenuo.max() requires a finite number");
29
52
  }
30
53
  return { kind: "max", value };
31
54
  }
32
55
  export function min(value) {
33
56
  if (!Number.isFinite(value)) {
34
- throw new Error("tenuo.min() requires a finite number");
57
+ throw new TenuoConfigurationError("tenuo.min() requires a finite number");
35
58
  }
36
59
  return { kind: "min", value };
37
60
  }
38
61
  /** Numeric interval. Bounds are inclusive unless `minExclusive` / `maxExclusive`. */
39
62
  export function range(options) {
63
+ rejectUnknownOptions("range", options, ["min", "max", "minExclusive", "maxExclusive"]);
40
64
  if (options.min === undefined && options.max === undefined) {
41
- throw new Error("tenuo.range() requires min, max, or both");
65
+ throw new TenuoConfigurationError("tenuo.range() requires min, max, or both");
42
66
  }
43
67
  for (const bound of [options.min, options.max]) {
44
68
  if (bound !== undefined && !Number.isFinite(bound)) {
45
- throw new Error("tenuo.range() bounds must be finite numbers");
69
+ throw new TenuoConfigurationError("tenuo.range() bounds must be finite numbers");
46
70
  }
47
71
  }
48
72
  if (options.min !== undefined && options.max !== undefined && options.min > options.max) {
49
- throw new Error("tenuo.range() min must not exceed max");
73
+ throw new TenuoConfigurationError("tenuo.range() min must not exceed max");
50
74
  }
51
75
  const out = { kind: "range" };
52
76
  if (options.min !== undefined) {
@@ -65,27 +89,51 @@ export function range(options) {
65
89
  }
66
90
  export function oneOf(values) {
67
91
  if (values.length === 0) {
68
- throw new Error("tenuo.oneOf() requires at least one value");
92
+ throw new TenuoConfigurationError("tenuo.oneOf() requires at least one value");
69
93
  }
70
94
  return { kind: "oneOf", values };
71
95
  }
72
96
  /** Any value except these. */
73
97
  export function notOneOf(values) {
74
98
  if (values.length === 0) {
75
- throw new Error("tenuo.notOneOf() requires at least one value");
99
+ throw new TenuoConfigurationError("tenuo.notOneOf() requires at least one value");
76
100
  }
77
101
  return { kind: "notOneOf", values };
78
102
  }
103
+ /**
104
+ * Generic string glob. `*` matches `/`; this is not path containment.
105
+ * For filesystem arguments, use `pathGlob(root, glob)`.
106
+ */
79
107
  export function pattern(pattern) {
80
108
  if (pattern.length === 0) {
81
- throw new Error("tenuo.pattern() requires a non-empty pattern");
109
+ throw new TenuoConfigurationError("tenuo.pattern() requires a non-empty pattern");
82
110
  }
83
111
  return { kind: "pattern", pattern };
84
112
  }
113
+ /**
114
+ * A traversal-safe filesystem glob: the value must stay under `root` and
115
+ * match `glob`.
116
+ *
117
+ * The glob is tested against the whole path, not against the part below
118
+ * `root`, so it matches at any depth:
119
+ *
120
+ * ```ts
121
+ * pathGlob("/workspace", "*.json");
122
+ * // /workspace/reports/q3.json allowed
123
+ * // /workspace/secrets.env denied (glob)
124
+ * // /etc/passwd.json denied (root)
125
+ * ```
126
+ *
127
+ * A delegatee can narrow this to a tighter `pathGlob`, or to an `exact()`
128
+ * path the parent already admits.
129
+ */
130
+ export function pathGlob(root, glob, options) {
131
+ return all([under(root, options), pattern(glob)]);
132
+ }
85
133
  /** Regular expression, compiled and evaluated in core. */
86
134
  export function regex(source) {
87
135
  if (source.length === 0) {
88
- throw new Error("tenuo.regex() requires a non-empty expression");
136
+ throw new TenuoConfigurationError("tenuo.regex() requires a non-empty expression");
89
137
  }
90
138
  return { kind: "regex", source };
91
139
  }
@@ -99,14 +147,14 @@ export function wildcard() {
99
147
  /** IP address inside a network, e.g. `cidr("10.0.0.0/8")`. */
100
148
  export function cidr(network) {
101
149
  if (!network.includes("/")) {
102
- throw new Error("tenuo.cidr() expects CIDR notation like 10.0.0.0/8");
150
+ throw new TenuoConfigurationError("tenuo.cidr() expects CIDR notation like 10.0.0.0/8");
103
151
  }
104
152
  return { kind: "cidr", network };
105
153
  }
106
154
  /** URL glob, e.g. `urlPattern("https://*.example.com/api/*")`. Parsed as a URL, not a string. */
107
155
  export function urlPattern(pattern) {
108
156
  if (pattern.length === 0) {
109
- throw new Error("tenuo.urlPattern() requires a non-empty pattern");
157
+ throw new TenuoConfigurationError("tenuo.urlPattern() requires a non-empty pattern");
110
158
  }
111
159
  return { kind: "urlPattern", pattern };
112
160
  }
@@ -115,6 +163,7 @@ export function urlPattern(pattern) {
115
163
  * hosts rejected, optional allow and deny domain lists (`*.example.com`).
116
164
  */
117
165
  export function urlSafe(options) {
166
+ rejectUnknownOptions("urlSafe", options, ["schemes", "allowDomains", "denyDomains"]);
118
167
  const out = { kind: "urlSafe" };
119
168
  if (options?.schemes !== undefined) {
120
169
  out.schemes = options.schemes;
@@ -130,35 +179,35 @@ export function urlSafe(options) {
130
179
  /** Shell command whose first word must be one of `allow`. Parsed with shell quoting rules in core. */
131
180
  export function shlex(allow) {
132
181
  if (allow.length === 0) {
133
- throw new Error("tenuo.shlex() requires at least one allowed command");
182
+ throw new TenuoConfigurationError("tenuo.shlex() requires at least one allowed command");
134
183
  }
135
184
  return { kind: "shlex", allow };
136
185
  }
137
186
  /** List argument that must include every one of these values. */
138
187
  export function contains(values) {
139
188
  if (values.length === 0) {
140
- throw new Error("tenuo.contains() requires at least one value");
189
+ throw new TenuoConfigurationError("tenuo.contains() requires at least one value");
141
190
  }
142
191
  return { kind: "contains", values };
143
192
  }
144
193
  /** List argument whose every element must be one of these values. */
145
194
  export function subset(values) {
146
195
  if (values.length === 0) {
147
- throw new Error("tenuo.subset() requires at least one value");
196
+ throw new TenuoConfigurationError("tenuo.subset() requires at least one value");
148
197
  }
149
198
  return { kind: "subset", values };
150
199
  }
151
200
  /** Satisfied when at least one inner constraint is. */
152
201
  export function anyOf(constraints) {
153
202
  if (constraints.length === 0) {
154
- throw new Error("tenuo.anyOf() requires at least one constraint");
203
+ throw new TenuoConfigurationError("tenuo.anyOf() requires at least one constraint");
155
204
  }
156
205
  return { kind: "anyOf", constraints };
157
206
  }
158
207
  /** Satisfied only when every inner constraint is. */
159
208
  export function all(constraints) {
160
209
  if (constraints.length === 0) {
161
- throw new Error("tenuo.all() requires at least one constraint");
210
+ throw new TenuoConfigurationError("tenuo.all() requires at least one constraint");
162
211
  }
163
212
  return { kind: "all", constraints };
164
213
  }
@@ -169,7 +218,7 @@ export function not(constraint) {
169
218
  /** Common Expression Language predicate over the argument value, evaluated in core. */
170
219
  export function cel(expression) {
171
220
  if (expression.trim().length === 0) {
172
- throw new Error("tenuo.cel() requires a non-empty expression");
221
+ throw new TenuoConfigurationError("tenuo.cel() requires a non-empty expression");
173
222
  }
174
223
  return { kind: "cel", expression };
175
224
  }
@@ -1 +1 @@
1
- {"version":3,"file":"constraints.js","sourceRoot":"","sources":["../src/constraints.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"constraints.js","sourceRoot":"","sources":["../src/constraints.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n AllConstraint,\n AnyOfConstraint,\n CelConstraint,\n CidrConstraint,\n ConstraintExpr,\n ContainsConstraint,\n EmailConstraint,\n ExactConstraint,\n MaxConstraint,\n MinConstraint,\n NotConstraint,\n NotOneOfConstraint,\n OneOfConstraint,\n PatternConstraint,\n RangeConstraint,\n RegexConstraint,\n ShlexConstraint,\n SubsetConstraint,\n UnderConstraint,\n UrlPatternConstraint,\n UrlSafeConstraint,\n WildcardConstraint,\n} from \"./api.ts\";\nimport { TenuoConfigurationError } from \"./errors.ts\";\n\n/**\n * Builders throw on a misspelled option instead of dropping it: an option\n * that vanishes silently (`urlSafe({ domains })`) widens authority. The core\n * applies the same rule to raw constraint objects when the policy compiles.\n */\nfunction rejectUnknownOptions(\n builder: string,\n options: unknown,\n allowed: readonly string[],\n): void {\n if (options === undefined || options === null) {\n return;\n }\n if (typeof options !== \"object\" || Array.isArray(options)) {\n throw new TenuoConfigurationError(`tenuo.${builder}() expects an options object`);\n }\n const unknown = Object.keys(options).find((key) => !allowed.includes(key));\n if (unknown !== undefined) {\n throw new TenuoConfigurationError(\n `tenuo.${builder}() received unknown option \"${unknown}\"; allowed options: ${allowed.join(\", \")}`,\n );\n }\n}\n\n/**\n * Constraint builders. These produce plain marker objects; every one is\n * evaluated by the Rust core, never in TypeScript. The set matches the\n * Python SDK and the core `Constraint` enum one for one. Builders that\n * validate their input throw `TenuoConfigurationError` at the call site.\n * Deeper checks, such as regex syntax, run in the core when `session()`\n * compiles the policy. Nothing here makes an authorization decision.\n */\n\n/** Directory prefix. Evaluated in core as Subpath — not a string prefix check in TS. */\nexport function under(\n root: string,\n options?: { readonly caseSensitive?: boolean; readonly allowEqual?: boolean },\n): UnderConstraint {\n rejectUnknownOptions(\"under\", options, [\"caseSensitive\", \"allowEqual\"]);\n if (!root.startsWith(\"/\")) {\n throw new TenuoConfigurationError(\"tenuo.under() expects an absolute path (start with /)\");\n }\n const out: { kind: \"under\"; root: string; caseSensitive?: boolean; allowEqual?: boolean } = {\n kind: \"under\",\n root,\n };\n if (options?.caseSensitive !== undefined) {\n out.caseSensitive = options.caseSensitive;\n }\n if (options?.allowEqual !== undefined) {\n out.allowEqual = options.allowEqual;\n }\n return out;\n}\n\nexport function email(options: { domain: string }): EmailConstraint {\n rejectUnknownOptions(\"email\", options, [\"domain\"]);\n return { kind: \"email\", domain: options.domain };\n}\n\nexport function max(value: number): MaxConstraint {\n if (!Number.isFinite(value)) {\n throw new TenuoConfigurationError(\"tenuo.max() requires a finite number\");\n }\n return { kind: \"max\", value };\n}\n\nexport function min(value: number): MinConstraint {\n if (!Number.isFinite(value)) {\n throw new TenuoConfigurationError(\"tenuo.min() requires a finite number\");\n }\n return { kind: \"min\", value };\n}\n\n/** Numeric interval. Bounds are inclusive unless `minExclusive` / `maxExclusive`. */\nexport function range(options: {\n readonly min?: number;\n readonly max?: number;\n readonly minExclusive?: boolean;\n readonly maxExclusive?: boolean;\n}): RangeConstraint {\n rejectUnknownOptions(\"range\", options, [\"min\", \"max\", \"minExclusive\", \"maxExclusive\"]);\n if (options.min === undefined && options.max === undefined) {\n throw new TenuoConfigurationError(\"tenuo.range() requires min, max, or both\");\n }\n for (const bound of [options.min, options.max]) {\n if (bound !== undefined && !Number.isFinite(bound)) {\n throw new TenuoConfigurationError(\"tenuo.range() bounds must be finite numbers\");\n }\n }\n if (options.min !== undefined && options.max !== undefined && options.min > options.max) {\n throw new TenuoConfigurationError(\"tenuo.range() min must not exceed max\");\n }\n const out: {\n kind: \"range\";\n min?: number;\n max?: number;\n minExclusive?: boolean;\n maxExclusive?: boolean;\n } = { kind: \"range\" };\n if (options.min !== undefined) {\n out.min = options.min;\n }\n if (options.max !== undefined) {\n out.max = options.max;\n }\n if (options.minExclusive === true) {\n out.minExclusive = true;\n }\n if (options.maxExclusive === true) {\n out.maxExclusive = true;\n }\n return out;\n}\n\nexport function oneOf(values: readonly string[]): OneOfConstraint {\n if (values.length === 0) {\n throw new TenuoConfigurationError(\"tenuo.oneOf() requires at least one value\");\n }\n return { kind: \"oneOf\", values };\n}\n\n/** Any value except these. */\nexport function notOneOf(values: readonly string[]): NotOneOfConstraint {\n if (values.length === 0) {\n throw new TenuoConfigurationError(\"tenuo.notOneOf() requires at least one value\");\n }\n return { kind: \"notOneOf\", values };\n}\n\n/**\n * Generic string glob. `*` matches `/`; this is not path containment.\n * For filesystem arguments, use `pathGlob(root, glob)`.\n */\nexport function pattern(pattern: string): PatternConstraint {\n if (pattern.length === 0) {\n throw new TenuoConfigurationError(\"tenuo.pattern() requires a non-empty pattern\");\n }\n return { kind: \"pattern\", pattern };\n}\n\n/**\n * A traversal-safe filesystem glob: the value must stay under `root` and\n * match `glob`.\n *\n * The glob is tested against the whole path, not against the part below\n * `root`, so it matches at any depth:\n *\n * ```ts\n * pathGlob(\"/workspace\", \"*.json\");\n * // /workspace/reports/q3.json allowed\n * // /workspace/secrets.env denied (glob)\n * // /etc/passwd.json denied (root)\n * ```\n *\n * A delegatee can narrow this to a tighter `pathGlob`, or to an `exact()`\n * path the parent already admits.\n */\nexport function pathGlob(\n root: string,\n glob: string,\n options?: { readonly caseSensitive?: boolean; readonly allowEqual?: boolean },\n): AllConstraint {\n return all([under(root, options), pattern(glob)]);\n}\n\n/** Regular expression, compiled and evaluated in core. */\nexport function regex(source: string): RegexConstraint {\n if (source.length === 0) {\n throw new TenuoConfigurationError(\"tenuo.regex() requires a non-empty expression\");\n }\n return { kind: \"regex\", source };\n}\n\nexport function exact(value: string | number | boolean): ExactConstraint {\n return { kind: \"exact\", value };\n}\n\n/** Any value at all. Name it in a zero-trust policy to allow an argument without constraining it. */\nexport function wildcard(): WildcardConstraint {\n return { kind: \"wildcard\" };\n}\n\n/** IP address inside a network, e.g. `cidr(\"10.0.0.0/8\")`. */\nexport function cidr(network: string): CidrConstraint {\n if (!network.includes(\"/\")) {\n throw new TenuoConfigurationError(\"tenuo.cidr() expects CIDR notation like 10.0.0.0/8\");\n }\n return { kind: \"cidr\", network };\n}\n\n/** URL glob, e.g. `urlPattern(\"https://*.example.com/api/*\")`. Parsed as a URL, not a string. */\nexport function urlPattern(pattern: string): UrlPatternConstraint {\n if (pattern.length === 0) {\n throw new TenuoConfigurationError(\"tenuo.urlPattern() requires a non-empty pattern\");\n }\n return { kind: \"urlPattern\", pattern };\n}\n\n/**\n * SSRF-aware URL check: http/https only by default, private and link-local\n * hosts rejected, optional allow and deny domain lists (`*.example.com`).\n */\nexport function urlSafe(options?: {\n readonly schemes?: readonly string[];\n readonly allowDomains?: readonly string[];\n readonly denyDomains?: readonly string[];\n}): UrlSafeConstraint {\n rejectUnknownOptions(\"urlSafe\", options, [\"schemes\", \"allowDomains\", \"denyDomains\"]);\n const out: {\n kind: \"urlSafe\";\n schemes?: readonly string[];\n allowDomains?: readonly string[];\n denyDomains?: readonly string[];\n } = { kind: \"urlSafe\" };\n if (options?.schemes !== undefined) {\n out.schemes = options.schemes;\n }\n if (options?.allowDomains !== undefined) {\n out.allowDomains = options.allowDomains;\n }\n if (options?.denyDomains !== undefined) {\n out.denyDomains = options.denyDomains;\n }\n return out;\n}\n\n/** Shell command whose first word must be one of `allow`. Parsed with shell quoting rules in core. */\nexport function shlex(allow: readonly string[]): ShlexConstraint {\n if (allow.length === 0) {\n throw new TenuoConfigurationError(\"tenuo.shlex() requires at least one allowed command\");\n }\n return { kind: \"shlex\", allow };\n}\n\n/** List argument that must include every one of these values. */\nexport function contains(values: readonly (string | number | boolean)[]): ContainsConstraint {\n if (values.length === 0) {\n throw new TenuoConfigurationError(\"tenuo.contains() requires at least one value\");\n }\n return { kind: \"contains\", values };\n}\n\n/** List argument whose every element must be one of these values. */\nexport function subset(values: readonly (string | number | boolean)[]): SubsetConstraint {\n if (values.length === 0) {\n throw new TenuoConfigurationError(\"tenuo.subset() requires at least one value\");\n }\n return { kind: \"subset\", values };\n}\n\n/** Satisfied when at least one inner constraint is. */\nexport function anyOf(constraints: readonly ConstraintExpr[]): AnyOfConstraint {\n if (constraints.length === 0) {\n throw new TenuoConfigurationError(\"tenuo.anyOf() requires at least one constraint\");\n }\n return { kind: \"anyOf\", constraints };\n}\n\n/** Satisfied only when every inner constraint is. */\nexport function all(constraints: readonly ConstraintExpr[]): AllConstraint {\n if (constraints.length === 0) {\n throw new TenuoConfigurationError(\"tenuo.all() requires at least one constraint\");\n }\n return { kind: \"all\", constraints };\n}\n\n/** Satisfied when the inner constraint is not. Delegation cannot narrow a `not`; prefer `notOneOf`. */\nexport function not(constraint: ConstraintExpr): NotConstraint {\n return { kind: \"not\", constraint };\n}\n\n/** Common Expression Language predicate over the argument value, evaluated in core. */\nexport function cel(expression: string): CelConstraint {\n if (expression.trim().length === 0) {\n throw new TenuoConfigurationError(\"tenuo.cel() requires a non-empty expression\");\n }\n return { kind: \"cel\", expression };\n}\n"]}
package/dist/errors.d.ts CHANGED
@@ -22,4 +22,3 @@ export declare class ApprovalRequiredError extends TenuoError {
22
22
  readonly request?: ApprovalRequest;
23
23
  constructor(tool: string, required: number, received: number, message?: string, request?: ApprovalRequest);
24
24
  }
25
- //# sourceMappingURL=errors.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,UAAW,SAAQ,KAAK;IAC1B,IAAI,CAAiB;IAE9B,YAAY,IAAoB,EAAE,OAAe;QAC/C,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;QACzB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAED,MAAM,OAAO,uBAAwB,SAAQ,UAAU;IACrD,YAAY,OAAe,EAAE,OAAuB,qBAAqB;QACvE,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;IACxC,CAAC;CACF;AAED,MAAM,OAAO,wBAAyB,SAAQ,UAAU;IAC7C,KAAK,CAAU;IAExB,YAAY,IAAoB,EAAE,OAAe,EAAE,KAAc;QAC/D,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;QACvC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACrB,CAAC;IACH,CAAC;CACF;AAED,MAAM,OAAO,qBAAsB,SAAQ,UAAU;IAC1C,IAAI,CAAS;IACb,QAAQ,CAAS;IACjB,QAAQ,CAAS;IAC1B;;;;OAIG;IACM,OAAO,CAAmB;IAEnC,YAAY,IAAY,EAAE,QAAgB,EAAE,QAAgB,EAAE,OAAgB,EAAE,OAAyB;QACvG,KAAK,CAAC,yBAAyB,EAAE,OAAO,IAAI,yBAAyB,IAAI,EAAE,CAAC,CAAC;QAC7E,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;QACpC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACzB,CAAC;IACH,CAAC;CACF"}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,UAAW,SAAQ,KAAK;IAC1B,IAAI,CAAiB;IAE9B,YAAY,IAAoB,EAAE,OAAe;QAC/C,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;QACzB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAED,MAAM,OAAO,uBAAwB,SAAQ,UAAU;IACrD,YAAY,OAAe,EAAE,OAAuB,qBAAqB;QACvE,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;IACxC,CAAC;CACF;AAED,MAAM,OAAO,wBAAyB,SAAQ,UAAU;IAC7C,KAAK,CAAU;IAExB,YAAY,IAAoB,EAAE,OAAe,EAAE,KAAc;QAC/D,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;QACvC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACrB,CAAC;IACH,CAAC;CACF;AAED,MAAM,OAAO,qBAAsB,SAAQ,UAAU;IAC1C,IAAI,CAAS;IACb,QAAQ,CAAS;IACjB,QAAQ,CAAS;IAC1B;;;;OAIG;IACM,OAAO,CAAmB;IAEnC,YAAY,IAAY,EAAE,QAAgB,EAAE,QAAgB,EAAE,OAAgB,EAAE,OAAyB;QACvG,KAAK,CAAC,yBAAyB,EAAE,OAAO,IAAI,yBAAyB,IAAI,EAAE,CAAC,CAAC;QAC7E,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;QACpC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACzB,CAAC;IACH,CAAC;CACF","sourcesContent":["import type { ApprovalRequest, TenuoErrorCode } from \"./api.ts\";\n\nexport class TenuoError extends Error {\n readonly code: TenuoErrorCode;\n\n constructor(code: TenuoErrorCode, message: string) {\n super(message);\n this.name = \"TenuoError\";\n this.code = code;\n }\n}\n\nexport class TenuoConfigurationError extends TenuoError {\n constructor(message: string, code: TenuoErrorCode = \"TENUO_CONFIGURATION\") {\n super(code, message);\n this.name = \"TenuoConfigurationError\";\n }\n}\n\nexport class AuthorizationDeniedError extends TenuoError {\n readonly field?: string;\n\n constructor(code: TenuoErrorCode, message: string, field?: string) {\n super(code, message);\n this.name = \"AuthorizationDeniedError\";\n if (field !== undefined) {\n this.field = field;\n }\n }\n}\n\nexport class ApprovalRequiredError extends TenuoError {\n readonly tool: string;\n readonly required: number;\n readonly received: number;\n /**\n * What to send to an approval service, when the SDK had the session at\n * hand. Absent on the server-side verify path, where only the caller can\n * build it.\n */\n readonly request?: ApprovalRequest;\n\n constructor(tool: string, required: number, received: number, message?: string, request?: ApprovalRequest) {\n super(\"TENUO_APPROVAL_REQUIRED\", message ?? `Approval required for ${tool}`);\n this.name = \"ApprovalRequiredError\";\n this.tool = tool;\n this.required = required;\n this.received = received;\n if (request !== undefined) {\n this.request = request;\n }\n }\n}\n"]}
@@ -118,6 +118,11 @@ export class SdkContext {
118
118
  * `loadRevocationList` anywhere this issuer is a trusted root.
119
119
  */
120
120
  signRevocationListVersioned(ids: any, version?: number | null): string;
121
+ /**
122
+ * Sign verifier receipts with this 32-byte holder secret instead of an
123
+ * ephemeral key. Used by the TypeScript `Runtime`.
124
+ */
125
+ withReceiptSigner(secret: Uint8Array): SdkContext;
121
126
  }
122
127
 
123
128
  /**
@@ -172,6 +177,16 @@ export class SdkSession {
172
177
  warrantIds(): any;
173
178
  }
174
179
 
180
+ /**
181
+ * Typed preflight of a warrant's approval gates for `(tool, args)`.
182
+ *
183
+ * Returns `{ status: "not_gated"|"exempt"|"required"|"denied", tool, kind?,
184
+ * argument?, arguments, message?, code?, reason?, error? }`. Parse errors
185
+ * fail closed with `status: "required"` and `error` set (no fabricated
186
+ * `kind`). `denied` means the warrant's constraints refuse the call.
187
+ */
188
+ export function approval_requirement(warrant_b64: string, tool: string, args_json: any): any;
189
+
175
190
  export function check_access(warrant_b64: string, tool: string, args_json: any, trusted_root_hex: string, dry_run: boolean): any;
176
191
 
177
192
  /**
@@ -248,6 +263,9 @@ export function decode_warrant(base64_warrant: string): any;
248
263
  * Returns `{ approval_required: true/false, tool, error }`.
249
264
  * This reads `extensions["tenuo.approval_gates"]` from the warrant, parses it,
250
265
  * and runs the gate evaluation logic from tenuo-core.
266
+ *
267
+ * Malformed warrants, gate maps, or arguments fail closed (`approval_required: true`).
268
+ * Prefer `approval_requirement` when the caller must distinguish exemptions.
251
269
  */
252
270
  export function evaluate_approval_gates(warrant_b64: string, tool: string, args_json: any): any;
253
271
 
@@ -259,11 +277,20 @@ export function generate_keypair(): any;
259
277
  export function init_panic_hook(): void;
260
278
 
261
279
  /**
262
- * Parse a `TENUO_CONNECT_TOKEN` string into its component fields.
280
+ * Inspect how a warrant gates `tool`, without evaluating arguments.
281
+ *
282
+ * Returns `{ kind: "none"|"whole_tool"|"conditional", tool, arguments, message?, error? }`.
283
+ * Parse errors fail closed with `kind: "unknown"` and `error` set so callers
284
+ * cannot treat a malformed map as ungated.
285
+ */
286
+ export function inspect_approval_gate(warrant_b64: string, tool: string): any;
287
+
288
+ /**
289
+ * Parse a complete `tenuo_ct_…` token into its component fields.
263
290
  *
264
- * The token is a base64url-encoded JSON blob: `{ v, e, k, a?, t? }`.
265
- * This WASM binding keeps the parsing canonical so TypeScript doesn't need
266
- * to duplicate the decode logic.
291
+ * Accepts padded and unpadded Base64URL. Version must be 1; omitted `v`
292
+ * is an error. Trailing `/v1` is stripped from `e`.
293
+ * Registration-token aliases: `t`, `r`, `registration_token`.
267
294
  *
268
295
  * Returns `{ endpoint, apiKey, agentId?, registrationToken?, error? }`.
269
296
  */
@@ -371,6 +371,22 @@ class SdkContext {
371
371
  wasm.__wbindgen_free(deferred2_0, deferred2_1, 1);
372
372
  }
373
373
  }
374
+ /**
375
+ * Sign verifier receipts with this 32-byte holder secret instead of an
376
+ * ephemeral key. Used by the TypeScript `Runtime`.
377
+ * @param {Uint8Array} secret
378
+ * @returns {SdkContext}
379
+ */
380
+ withReceiptSigner(secret) {
381
+ const ptr = this.__destroy_into_raw();
382
+ const ptr0 = passArray8ToWasm0(secret, wasm.__wbindgen_malloc);
383
+ const len0 = WASM_VECTOR_LEN;
384
+ const ret = wasm.sdkcontext_withReceiptSigner(ptr, ptr0, len0);
385
+ if (ret[2]) {
386
+ throw takeFromExternrefTable0(ret[1]);
387
+ }
388
+ return SdkContext.__wrap(ret[0]);
389
+ }
374
390
  }
375
391
  if (Symbol.dispose) SdkContext.prototype[Symbol.dispose] = SdkContext.prototype.free;
376
392
  exports.SdkContext = SdkContext;
@@ -550,6 +566,28 @@ class SdkSession {
550
566
  if (Symbol.dispose) SdkSession.prototype[Symbol.dispose] = SdkSession.prototype.free;
551
567
  exports.SdkSession = SdkSession;
552
568
 
569
+ /**
570
+ * Typed preflight of a warrant's approval gates for `(tool, args)`.
571
+ *
572
+ * Returns `{ status: "not_gated"|"exempt"|"required"|"denied", tool, kind?,
573
+ * argument?, arguments, message?, code?, reason?, error? }`. Parse errors
574
+ * fail closed with `status: "required"` and `error` set (no fabricated
575
+ * `kind`). `denied` means the warrant's constraints refuse the call.
576
+ * @param {string} warrant_b64
577
+ * @param {string} tool
578
+ * @param {any} args_json
579
+ * @returns {any}
580
+ */
581
+ function approval_requirement(warrant_b64, tool, args_json) {
582
+ const ptr0 = passStringToWasm0(warrant_b64, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
583
+ const len0 = WASM_VECTOR_LEN;
584
+ const ptr1 = passStringToWasm0(tool, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
585
+ const len1 = WASM_VECTOR_LEN;
586
+ const ret = wasm.approval_requirement(ptr0, len0, ptr1, len1, args_json);
587
+ return ret;
588
+ }
589
+ exports.approval_requirement = approval_requirement;
590
+
553
591
  /**
554
592
  * @param {string} warrant_b64
555
593
  * @param {string} tool
@@ -767,6 +805,9 @@ exports.decode_warrant = decode_warrant;
767
805
  * Returns `{ approval_required: true/false, tool, error }`.
768
806
  * This reads `extensions["tenuo.approval_gates"]` from the warrant, parses it,
769
807
  * and runs the gate evaluation logic from tenuo-core.
808
+ *
809
+ * Malformed warrants, gate maps, or arguments fail closed (`approval_required: true`).
810
+ * Prefer `approval_requirement` when the caller must distinguish exemptions.
770
811
  * @param {string} warrant_b64
771
812
  * @param {string} tool
772
813
  * @param {any} args_json
@@ -798,11 +839,31 @@ function init_panic_hook() {
798
839
  exports.init_panic_hook = init_panic_hook;
799
840
 
800
841
  /**
801
- * Parse a `TENUO_CONNECT_TOKEN` string into its component fields.
842
+ * Inspect how a warrant gates `tool`, without evaluating arguments.
843
+ *
844
+ * Returns `{ kind: "none"|"whole_tool"|"conditional", tool, arguments, message?, error? }`.
845
+ * Parse errors fail closed with `kind: "unknown"` and `error` set so callers
846
+ * cannot treat a malformed map as ungated.
847
+ * @param {string} warrant_b64
848
+ * @param {string} tool
849
+ * @returns {any}
850
+ */
851
+ function inspect_approval_gate(warrant_b64, tool) {
852
+ const ptr0 = passStringToWasm0(warrant_b64, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
853
+ const len0 = WASM_VECTOR_LEN;
854
+ const ptr1 = passStringToWasm0(tool, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
855
+ const len1 = WASM_VECTOR_LEN;
856
+ const ret = wasm.inspect_approval_gate(ptr0, len0, ptr1, len1);
857
+ return ret;
858
+ }
859
+ exports.inspect_approval_gate = inspect_approval_gate;
860
+
861
+ /**
862
+ * Parse a complete `tenuo_ct_…` token into its component fields.
802
863
  *
803
- * The token is a base64url-encoded JSON blob: `{ v, e, k, a?, t? }`.
804
- * This WASM binding keeps the parsing canonical so TypeScript doesn't need
805
- * to duplicate the decode logic.
864
+ * Accepts padded and unpadded Base64URL. Version must be 1; omitted `v`
865
+ * is an error. Trailing `/v1` is stripped from `e`.
866
+ * Registration-token aliases: `t`, `r`, `registration_token`.
806
867
  *
807
868
  * Returns `{ endpoint, apiKey, agentId?, registrationToken?, error? }`.
808
869
  * @param {string} token
Binary file
@@ -23,6 +23,7 @@ export const sdkcontext_narrow: (a: number, b: number, c: any, d: any) => [numbe
23
23
  export const sdkcontext_new: () => number;
24
24
  export const sdkcontext_signPop: (a: number, b: number, c: number, d: number, e: any) => [number, number, number, number];
25
25
  export const sdkcontext_signRevocationList: (a: number, b: any) => [number, number, number, number];
26
+ export const sdkcontext_withReceiptSigner: (a: number, b: number, c: number) => [number, number, number];
26
27
  export const sdksession_dedupKey: (a: number, b: number, c: number, d: any) => [number, number, number, number];
27
28
  export const sdksession_describe: (a: number) => [number, number, number];
28
29
  export const sdksession_exportWire: (a: number) => [number, number, number];
@@ -32,6 +33,7 @@ export const sdksession_fromWire: (a: number, b: number, c: number, d: number) =
32
33
  export const sdksession_toStackWire: (a: number) => [number, number, number, number];
33
34
  export const sdksession_toWire: (a: number) => [number, number, number];
34
35
  export const sdksession_warrantIds: (a: number) => [number, number, number];
36
+ export const approval_requirement: (a: number, b: number, c: number, d: number, e: any) => any;
35
37
  export const check_access: (a: number, b: number, c: number, d: number, e: any, f: number, g: number, h: number) => any;
36
38
  export const check_access_with_pop: (a: number, b: number, c: number, d: number, e: any, f: number, g: number, h: number, i: number) => any;
37
39
  export const check_chain_access: (a: number, b: number, c: number, d: number, e: any, f: number, g: number, h: number) => any;
@@ -45,6 +47,7 @@ export const decode_pem_chain_wasm: (a: number, b: number) => any;
45
47
  export const decode_warrant: (a: number, b: number) => any;
46
48
  export const evaluate_approval_gates: (a: number, b: number, c: number, d: number, e: any) => any;
47
49
  export const generate_keypair: () => any;
50
+ export const inspect_approval_gate: (a: number, b: number, c: number, d: number) => any;
48
51
  export const parse_connect_token: (a: number, b: number) => any;
49
52
  export const sign: (a: number, b: number, c: number, d: number, e: number, f: number, g: any) => any;
50
53
  export const sign_receipt: (a: any, b: number, c: number) => any;
@@ -0,0 +1,25 @@
1
+ import type { PublicKeyHandle } from "./api.ts";
2
+ declare const inspect: unique symbol;
3
+ /**
4
+ * Holder keypair. Core owns the representation; persistence is the caller's
5
+ * job (or a later Node adapter). The secret never appears in stringification,
6
+ * inspection, or JSON.
7
+ */
8
+ export declare class HolderIdentity {
9
+ readonly publicKey: PublicKeyHandle;
10
+ constructor(holderKey: Uint8Array);
11
+ /** Copy of the 32-byte Ed25519 secret. */
12
+ get holderKey(): Uint8Array;
13
+ toJSON(): {
14
+ readonly publicKey: string;
15
+ };
16
+ toString(): string;
17
+ [inspect](): {
18
+ readonly publicKey: string;
19
+ };
20
+ }
21
+ /** Import a 32-byte holder secret. */
22
+ export declare function identityFromKey(holderKey: Uint8Array): HolderIdentity;
23
+ /** Fresh holder identity. Uses Web Crypto; does not touch the filesystem. */
24
+ export declare function generateIdentity(): HolderIdentity;
25
+ export {};
@@ -0,0 +1,61 @@
1
+ import { TenuoConfigurationError } from "./errors.js";
2
+ import { loadWasm, publicKeyHexFromHolderKey } from "./wasm.js";
3
+ const inspect = Symbol.for("nodejs.util.inspect.custom");
4
+ const secrets = new WeakMap();
5
+ /**
6
+ * Holder keypair. Core owns the representation; persistence is the caller's
7
+ * job (or a later Node adapter). The secret never appears in stringification,
8
+ * inspection, or JSON.
9
+ */
10
+ export class HolderIdentity {
11
+ publicKey;
12
+ constructor(holderKey) {
13
+ if (!(holderKey instanceof Uint8Array) || holderKey.length !== 32) {
14
+ throw new TenuoConfigurationError("HolderIdentity requires a 32-byte holder key");
15
+ }
16
+ loadWasm();
17
+ secrets.set(this, new Uint8Array(holderKey));
18
+ this.publicKey = {
19
+ kind: "public-key",
20
+ source: "bytes",
21
+ hex: publicKeyHexFromHolderKey(holderKey),
22
+ };
23
+ }
24
+ /** Copy of the 32-byte Ed25519 secret. */
25
+ get holderKey() {
26
+ const secret = secrets.get(this);
27
+ if (secret === undefined) {
28
+ throw new TenuoConfigurationError("identity is not bound to a holder key");
29
+ }
30
+ return new Uint8Array(secret);
31
+ }
32
+ toJSON() {
33
+ return { publicKey: this.publicKey.hex };
34
+ }
35
+ toString() {
36
+ return `TenuoIdentity(${this.publicKey.hex.slice(0, 8)}…)`;
37
+ }
38
+ [inspect]() {
39
+ return this.toJSON();
40
+ }
41
+ }
42
+ /** Import a 32-byte holder secret. */
43
+ export function identityFromKey(holderKey) {
44
+ if (!(holderKey instanceof Uint8Array) || holderKey.length !== 32) {
45
+ throw new TenuoConfigurationError("identity() requires a 32-byte holder key");
46
+ }
47
+ loadWasm();
48
+ return new HolderIdentity(holderKey);
49
+ }
50
+ /** Fresh holder identity. Uses Web Crypto; does not touch the filesystem. */
51
+ export function generateIdentity() {
52
+ return identityFromKey(randomSecret());
53
+ }
54
+ function randomSecret() {
55
+ const cryptoObj = globalThis.crypto;
56
+ if (cryptoObj === undefined || typeof cryptoObj.getRandomValues !== "function") {
57
+ throw new TenuoConfigurationError("generateIdentity() needs Web Crypto (globalThis.crypto.getRandomValues).");
58
+ }
59
+ return cryptoObj.getRandomValues(new Uint8Array(32));
60
+ }
61
+ //# sourceMappingURL=identity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.js","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAC;AAEhE,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;AACzD,MAAM,OAAO,GAAG,IAAI,OAAO,EAA8B,CAAC;AAE1D;;;;GAIG;AACH,MAAM,OAAO,cAAc;IAChB,SAAS,CAAkB;IAEpC,YAAY,SAAqB;QAC/B,IAAI,CAAC,CAAC,SAAS,YAAY,UAAU,CAAC,IAAI,SAAS,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;YAClE,MAAM,IAAI,uBAAuB,CAAC,8CAA8C,CAAC,CAAC;QACpF,CAAC;QACD,QAAQ,EAAE,CAAC;QACX,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC;QAC7C,IAAI,CAAC,SAAS,GAAG;YACf,IAAI,EAAE,YAAY;YAClB,MAAM,EAAE,OAAO;YACf,GAAG,EAAE,yBAAyB,CAAC,SAAS,CAAC;SAC1C,CAAC;IACJ,CAAC;IAED,0CAA0C;IAC1C,IAAI,SAAS;QACX,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACjC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,MAAM,IAAI,uBAAuB,CAAC,uCAAuC,CAAC,CAAC;QAC7E,CAAC;QACD,OAAO,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;IAChC,CAAC;IAED,MAAM;QACJ,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC;IAC3C,CAAC;IAED,QAAQ;QACN,OAAO,iBAAiB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC;IAC7D,CAAC;IAED,CAAC,OAAO,CAAC;QACP,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC;IACvB,CAAC;CACF;AAED,sCAAsC;AACtC,MAAM,UAAU,eAAe,CAAC,SAAqB;IACnD,IAAI,CAAC,CAAC,SAAS,YAAY,UAAU,CAAC,IAAI,SAAS,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;QAClE,MAAM,IAAI,uBAAuB,CAAC,0CAA0C,CAAC,CAAC;IAChF,CAAC;IACD,QAAQ,EAAE,CAAC;IACX,OAAO,IAAI,cAAc,CAAC,SAAS,CAAC,CAAC;AACvC,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,gBAAgB;IAC9B,OAAO,eAAe,CAAC,YAAY,EAAE,CAAC,CAAC;AACzC,CAAC;AAED,SAAS,YAAY;IACnB,MAAM,SAAS,GAAG,UAAU,CAAC,MAAM,CAAC;IACpC,IAAI,SAAS,KAAK,SAAS,IAAI,OAAO,SAAS,CAAC,eAAe,KAAK,UAAU,EAAE,CAAC;QAC/E,MAAM,IAAI,uBAAuB,CAC/B,0EAA0E,CAC3E,CAAC;IACJ,CAAC;IACD,OAAO,SAAS,CAAC,eAAe,CAAC,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;AACvD,CAAC","sourcesContent":["import type { PublicKeyHandle } from \"./api.ts\";\nimport { TenuoConfigurationError } from \"./errors.ts\";\nimport { loadWasm, publicKeyHexFromHolderKey } from \"./wasm.ts\";\n\nconst inspect = Symbol.for(\"nodejs.util.inspect.custom\");\nconst secrets = new WeakMap<HolderIdentity, Uint8Array>();\n\n/**\n * Holder keypair. Core owns the representation; persistence is the caller's\n * job (or a later Node adapter). The secret never appears in stringification,\n * inspection, or JSON.\n */\nexport class HolderIdentity {\n readonly publicKey: PublicKeyHandle;\n\n constructor(holderKey: Uint8Array) {\n if (!(holderKey instanceof Uint8Array) || holderKey.length !== 32) {\n throw new TenuoConfigurationError(\"HolderIdentity requires a 32-byte holder key\");\n }\n loadWasm();\n secrets.set(this, new Uint8Array(holderKey));\n this.publicKey = {\n kind: \"public-key\",\n source: \"bytes\",\n hex: publicKeyHexFromHolderKey(holderKey),\n };\n }\n\n /** Copy of the 32-byte Ed25519 secret. */\n get holderKey(): Uint8Array {\n const secret = secrets.get(this);\n if (secret === undefined) {\n throw new TenuoConfigurationError(\"identity is not bound to a holder key\");\n }\n return new Uint8Array(secret);\n }\n\n toJSON(): { readonly publicKey: string } {\n return { publicKey: this.publicKey.hex };\n }\n\n toString(): string {\n return `TenuoIdentity(${this.publicKey.hex.slice(0, 8)}…)`;\n }\n\n [inspect](): { readonly publicKey: string } {\n return this.toJSON();\n }\n}\n\n/** Import a 32-byte holder secret. */\nexport function identityFromKey(holderKey: Uint8Array): HolderIdentity {\n if (!(holderKey instanceof Uint8Array) || holderKey.length !== 32) {\n throw new TenuoConfigurationError(\"identity() requires a 32-byte holder key\");\n }\n loadWasm();\n return new HolderIdentity(holderKey);\n}\n\n/** Fresh holder identity. Uses Web Crypto; does not touch the filesystem. */\nexport function generateIdentity(): HolderIdentity {\n return identityFromKey(randomSecret());\n}\n\nfunction randomSecret(): Uint8Array {\n const cryptoObj = globalThis.crypto;\n if (cryptoObj === undefined || typeof cryptoObj.getRandomValues !== \"function\") {\n throw new TenuoConfigurationError(\n \"generateIdentity() needs Web Crypto (globalThis.crypto.getRandomValues).\",\n );\n }\n return cryptoObj.getRandomValues(new Uint8Array(32));\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -1,7 +1,13 @@
1
- export type { AllConstraint, AllowPolicy, AnyOfConstraint, ApprovalContextAttestation, ApprovalInfo, ApprovalRequest, ApprovalRequestSummary, ArgApprovalGate, CelConstraint, CidrConstraint, Clearance, ConstraintExpr, ContainsConstraint, ControlPlaneApprovalRequestV1, ControlPlaneApprovalResponseV1, CreateTenuoOptions, Decision, DevRoot, EmailConstraint, ExactConstraint, ExecuteOptions, ExplainedField, Explanation, IssueInput, IssuerKey, MaxConstraint, McpAttachOptions, McpCallParams, McpHandlerPolicy, McpJsonRpcError, McpVerifyOptions, MinConstraint, NarrowInput, NarrowOptions, NonceStore, NotConstraint, NotOneOfConstraint, OneOfConstraint, PatternConstraint, PresentedCall, ProtectedTool, PublicKeyHandle, RangeConstraint, ReceiptChainInfo, ReceiptInfo, RegexConstraint, RequireApproval, RevocationListInfo, RevocationListInput, Session, SessionAllow, SessionFromWireInput, SessionInfo, SessionInput, ShlexConstraint, SignApprovalOptions, SubsetConstraint, Tenuo, TenuoErrorCode, TenuoMcp, TenuoMcpMeta, ToolApprovalGate, ToolLike, ToolPolicy, UnderConstraint, UrlPatternConstraint, UrlSafeConstraint, WarrantPart, WildcardConstraint, } from "./api.ts";
1
+ export type { ApprovalGateInspection, ApprovalRequirement, ApprovalRequirementStatus, } from "./wasm.ts";
2
+ export { approvalRequirement, evaluateApprovalGates, inspectApprovalGate } from "./wasm.ts";
3
+ export type { AllConstraint, AllowPolicy, AnyOfConstraint, ApprovalContextAttestation, ApprovalInfo, ApprovalRequest, ApprovalRequestSummary, ArgApprovalGate, CelConstraint, CidrConstraint, Clearance, ConstraintExpr, ContainsConstraint, ControlPlaneApprovalRequestV1, ControlPlaneApprovalResponseV1, CreateTenuoOptions, Decision, DevRoot, EmailConstraint, ExactConstraint, ExecuteOptions, ExplainedField, Explanation, IssueInput, IssuerKey, MaxConstraint, McpAttachOptions, McpCallParams, McpHandlerPolicy, McpJsonRpcError, McpVerifyOptions, MinConstraint, NarrowInput, NarrowOptions, NonceStore, NotConstraint, NotOneOfConstraint, OneOfConstraint, PatternConstraint, PresentedCall, ProtectedTool, PublicKeyHandle, RangeConstraint, ReceiptChainInfo, ReceiptInfo, RegexConstraint, RequireApproval, RevocationListInfo, RevocationListInput, Session, SessionAllow, SessionFromWireInput, SessionInfo, SessionInput, ShlexConstraint, SignApprovalOptions, SubsetConstraint, Tenuo, LegacyTenuoErrorCode, ProtocolDenialCode, TenuoErrorCode, TenuoMcp, TenuoMcpMeta, ToolApprovalGate, ToolLike, ToolPolicy, UnderConstraint, UrlPatternConstraint, UrlSafeConstraint, WarrantPart, WildcardConstraint, } from "./api.ts";
2
4
  export { ApprovalRequiredError, AuthorizationDeniedError, TenuoConfigurationError, TenuoError, } from "./errors.ts";
3
- export { all, anyOf, cel, cidr, contains, email, exact, max, min, not, notOneOf, oneOf, pattern, range, regex, shlex, subset, under, urlPattern, urlSafe, wildcard, } from "./constraints.ts";
5
+ export { all, anyOf, cel, cidr, contains, email, exact, max, min, not, notOneOf, oneOf, pathGlob, pattern, range, regex, shlex, subset, under, urlPattern, urlSafe, wildcard, } from "./constraints.ts";
4
6
  export { memoryNonceStore } from "./nonce.ts";
5
7
  export { Session as SessionHandle, isSession } from "./session.ts";
8
+ export { ConnectToken, parseConnectToken } from "./connect.ts";
9
+ export type { ResolveEndpointOptions } from "./connect.ts";
10
+ export { HolderIdentity, generateIdentity, identityFromKey } from "./identity.ts";
11
+ export { Runtime } from "./runtime.ts";
12
+ export type { ReceiptsMode, RuntimeOptions, SessionWarrant } from "./runtime.ts";
6
13
  export { createTenuo } from "./client.ts";
7
- //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -1,6 +1,10 @@
1
+ export { approvalRequirement, evaluateApprovalGates, inspectApprovalGate } from "./wasm.js";
1
2
  export { ApprovalRequiredError, AuthorizationDeniedError, TenuoConfigurationError, TenuoError, } from "./errors.js";
2
- export { all, anyOf, cel, cidr, contains, email, exact, max, min, not, notOneOf, oneOf, pattern, range, regex, shlex, subset, under, urlPattern, urlSafe, wildcard, } from "./constraints.js";
3
+ export { all, anyOf, cel, cidr, contains, email, exact, max, min, not, notOneOf, oneOf, pathGlob, pattern, range, regex, shlex, subset, under, urlPattern, urlSafe, wildcard, } from "./constraints.js";
3
4
  export { memoryNonceStore } from "./nonce.js";
4
5
  export { Session as SessionHandle, isSession } from "./session.js";
6
+ export { ConnectToken, parseConnectToken } from "./connect.js";
7
+ export { HolderIdentity, generateIdentity, identityFromKey } from "./identity.js";
8
+ export { Runtime } from "./runtime.js";
5
9
  export { createTenuo } from "./client.js";
6
10
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAwEA,OAAO,EACL,qBAAqB,EACrB,wBAAwB,EACxB,uBAAuB,EACvB,UAAU,GACX,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,GAAG,EACH,KAAK,EACL,GAAG,EACH,IAAI,EACJ,QAAQ,EACR,KAAK,EACL,KAAK,EACL,GAAG,EACH,GAAG,EACH,GAAG,EACH,QAAQ,EACR,KAAK,EACL,OAAO,EACP,KAAK,EACL,KAAK,EACL,KAAK,EACL,MAAM,EACN,KAAK,EACL,UAAU,EACV,OAAO,EACP,QAAQ,GACT,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEnE,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AA4E5F,OAAO,EACL,qBAAqB,EACrB,wBAAwB,EACxB,uBAAuB,EACvB,UAAU,GACX,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,GAAG,EACH,KAAK,EACL,GAAG,EACH,IAAI,EACJ,QAAQ,EACR,KAAK,EACL,KAAK,EACL,GAAG,EACH,GAAG,EACH,GAAG,EACH,QAAQ,EACR,KAAK,EACL,QAAQ,EACR,OAAO,EACP,KAAK,EACL,KAAK,EACL,KAAK,EACL,MAAM,EACN,KAAK,EACL,UAAU,EACV,OAAO,EACP,QAAQ,GACT,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEnE,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAG/D,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAElF,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAGvC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC","sourcesContent":["export type {\n ApprovalGateInspection,\n ApprovalRequirement,\n ApprovalRequirementStatus,\n} from \"./wasm.ts\";\n\nexport { approvalRequirement, evaluateApprovalGates, inspectApprovalGate } from \"./wasm.ts\";\n\nexport type {\n AllConstraint,\n AllowPolicy,\n AnyOfConstraint,\n ApprovalContextAttestation,\n ApprovalInfo,\n ApprovalRequest,\n ApprovalRequestSummary,\n ArgApprovalGate,\n CelConstraint,\n CidrConstraint,\n Clearance,\n ConstraintExpr,\n ContainsConstraint,\n ControlPlaneApprovalRequestV1,\n ControlPlaneApprovalResponseV1,\n CreateTenuoOptions,\n Decision,\n DevRoot,\n EmailConstraint,\n ExactConstraint,\n ExecuteOptions,\n ExplainedField,\n Explanation,\n IssueInput,\n IssuerKey,\n MaxConstraint,\n McpAttachOptions,\n McpCallParams,\n McpHandlerPolicy,\n McpJsonRpcError,\n McpVerifyOptions,\n MinConstraint,\n NarrowInput,\n NarrowOptions,\n NonceStore,\n NotConstraint,\n NotOneOfConstraint,\n OneOfConstraint,\n PatternConstraint,\n PresentedCall,\n ProtectedTool,\n PublicKeyHandle,\n RangeConstraint,\n ReceiptChainInfo,\n ReceiptInfo,\n RegexConstraint,\n RequireApproval,\n RevocationListInfo,\n RevocationListInput,\n Session,\n SessionAllow,\n SessionFromWireInput,\n SessionInfo,\n SessionInput,\n ShlexConstraint,\n SignApprovalOptions,\n SubsetConstraint,\n Tenuo,\n LegacyTenuoErrorCode,\n ProtocolDenialCode,\n TenuoErrorCode,\n TenuoMcp,\n TenuoMcpMeta,\n ToolApprovalGate,\n ToolLike,\n ToolPolicy,\n UnderConstraint,\n UrlPatternConstraint,\n UrlSafeConstraint,\n WarrantPart,\n WildcardConstraint,\n} from \"./api.ts\";\n\nexport {\n ApprovalRequiredError,\n AuthorizationDeniedError,\n TenuoConfigurationError,\n TenuoError,\n} from \"./errors.ts\";\n\nexport {\n all,\n anyOf,\n cel,\n cidr,\n contains,\n email,\n exact,\n max,\n min,\n not,\n notOneOf,\n oneOf,\n pathGlob,\n pattern,\n range,\n regex,\n shlex,\n subset,\n under,\n urlPattern,\n urlSafe,\n wildcard,\n} from \"./constraints.ts\";\n\nexport { memoryNonceStore } from \"./nonce.ts\";\n\nexport { Session as SessionHandle, isSession } from \"./session.ts\";\n\nexport { ConnectToken, parseConnectToken } from \"./connect.ts\";\nexport type { ResolveEndpointOptions } from \"./connect.ts\";\n\nexport { HolderIdentity, generateIdentity, identityFromKey } from \"./identity.ts\";\n\nexport { Runtime } from \"./runtime.ts\";\nexport type { ReceiptsMode, RuntimeOptions, SessionWarrant } from \"./runtime.ts\";\n\nexport { createTenuo } from \"./client.ts\";\n"]}
package/dist/mcp.d.ts CHANGED
@@ -6,7 +6,7 @@ export type Decide = (decision: WasmDecision, tool: string, native?: object, arg
6
6
  * proof-of-possession over the call. Shared by `tenuo.present()` and
7
7
  * `tenuo.mcp.attach()`.
8
8
  */
9
- export declare function presentCall(context: WasmContext, decide: Decide, session: SessionContract, name: string, args: Readonly<Record<string, unknown>>, options: McpAttachOptions | undefined, label: string): {
9
+ export declare function presentCall(context: WasmContext, decide: Decide, session: SessionContract, name: string, args: Readonly<Record<string, unknown>>, options: McpAttachOptions | undefined, label: string, host?: object): {
10
10
  readonly presented: PresentedCall;
11
11
  readonly wireArgs: Record<string, unknown>;
12
12
  };
@@ -15,6 +15,5 @@ export declare function presentCall(context: WasmContext, decide: Decide, sessio
15
15
  * proof-of-possession, then apply the host ceiling. Shared by
16
16
  * `tenuo.verify()` and `tenuo.mcp.verify()`.
17
17
  */
18
- export declare function verifyPresented(context: WasmContext, decide: Decide, presented: PresentedCall, name: string, args: Readonly<Record<string, unknown>>, options: McpVerifyOptions | undefined, label: string): Promise<Readonly<Record<string, unknown>>>;
19
- export declare function createMcp(context: WasmContext, decide: Decide): TenuoMcp;
20
- //# sourceMappingURL=mcp.d.ts.map
18
+ export declare function verifyPresented(context: WasmContext, decide: Decide, presented: PresentedCall, name: string, args: Readonly<Record<string, unknown>>, options: McpVerifyOptions | undefined, label: string, host?: object): Promise<Readonly<Record<string, unknown>>>;
19
+ export declare function createMcp(context: WasmContext, decide: Decide, host?: object): TenuoMcp;