@suveren/gateway 0.12.0 → 0.13.1

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 (38) hide show
  1. package/README.md +53 -3
  2. package/bin/suveren-gateway.js +128 -12
  3. package/dist/control-plane/index.mjs +555 -122
  4. package/dist/mcp-server/http.mjs +647 -273
  5. package/dist/ui/assets/{index-BzcgeHlq.js → index-CmLqzgqA.js} +21 -21
  6. package/dist/ui/assets/{index-BFOprnFZ.css → index-y1OFeYnS.css} +1 -1
  7. package/dist/ui/index.html +2 -2
  8. package/lib/autostart-templates.d.mts +6 -0
  9. package/lib/autostart-templates.mjs +21 -4
  10. package/lib/config.d.mts +10 -0
  11. package/lib/config.mjs +79 -18
  12. package/lib/policy.d.mts +34 -0
  13. package/lib/policy.mjs +369 -0
  14. package/node_modules/@hap/core/dist/index.d.mts +49 -1
  15. package/node_modules/@hap/core/dist/index.d.ts +49 -1
  16. package/node_modules/@hap/core/dist/index.js +50 -0
  17. package/node_modules/@hap/core/dist/index.mjs +49 -0
  18. package/node_modules/@hap/core/package.json +1 -1
  19. package/node_modules/@hap/core/src/frame.ts +63 -1
  20. package/node_modules/@hap/core/src/gatekeeper.ts +37 -0
  21. package/node_modules/@hap/core/src/types.ts +29 -1
  22. package/node_modules/hono/dist/cjs/hono-base.js +2 -0
  23. package/node_modules/hono/dist/cjs/middleware/language/language.js +1 -1
  24. package/node_modules/hono/dist/cjs/middleware/mount/index.js +71 -0
  25. package/node_modules/hono/dist/cjs/request.js +4 -4
  26. package/node_modules/hono/dist/hono-base.js +2 -0
  27. package/node_modules/hono/dist/middleware/mount/index.js +70 -0
  28. package/node_modules/hono/dist/request.js +4 -4
  29. package/node_modules/hono/dist/types/hono-base.d.ts +2 -0
  30. package/node_modules/hono/dist/types/jsx/base.d.ts +1 -1
  31. package/node_modules/hono/dist/types/middleware/mount/index.d.ts +46 -0
  32. package/node_modules/hono/dist/types/request.d.ts +4 -4
  33. package/node_modules/hono/package.json +9 -1
  34. package/package.json +2 -2
  35. package/profiles/index.json +3 -1
  36. package/profiles/purchase/0.6.profile.json +234 -0
  37. package/profiles/sales/0.3.profile.json +290 -0
  38. package/server.js +51 -8
@@ -0,0 +1,234 @@
1
+ {
2
+ "id": "github.com/humanagencyprotocol/hap-profiles/purchase@0.6",
3
+ "name": "Purchase",
4
+ "version": "0.6",
5
+ "description": "Purchase authority — governs spending company money: subscriptions, supplies, services, advertising",
6
+ "boundsSchema": {
7
+ "actionTypes": [
8
+ "purchase"
9
+ ],
10
+ "keyOrder": [
11
+ "profile",
12
+ "spend_max",
13
+ "spend_daily_max",
14
+ "spend_monthly_max",
15
+ "transaction_count_daily_max"
16
+ ],
17
+ "fields": {
18
+ "profile": {
19
+ "type": "string",
20
+ "required": true
21
+ },
22
+ "spend_max": {
23
+ "type": "number",
24
+ "required": true,
25
+ "displayName": "Max per transaction",
26
+ "description": "Maximum spend per transaction in currency units",
27
+ "unit": "currency:EUR",
28
+ "boundType": {
29
+ "kind": "per_transaction",
30
+ "of": "spend",
31
+ "requiredFor": [
32
+ "purchase"
33
+ ]
34
+ }
35
+ },
36
+ "spend_daily_max": {
37
+ "type": "number",
38
+ "required": true,
39
+ "displayName": "Daily spend limit",
40
+ "description": "Maximum cumulative spend per day in currency units",
41
+ "unit": "currency:EUR",
42
+ "boundType": {
43
+ "kind": "cumulative_sum",
44
+ "of": "spend",
45
+ "window": "daily"
46
+ }
47
+ },
48
+ "spend_monthly_max": {
49
+ "type": "number",
50
+ "required": true,
51
+ "displayName": "Monthly spend limit",
52
+ "description": "Maximum cumulative spend per month in currency units",
53
+ "unit": "currency:EUR",
54
+ "boundType": {
55
+ "kind": "cumulative_sum",
56
+ "of": "spend",
57
+ "window": "monthly"
58
+ }
59
+ },
60
+ "transaction_count_daily_max": {
61
+ "type": "number",
62
+ "required": true,
63
+ "displayName": "Daily transaction limit",
64
+ "description": "Maximum number of purchase transactions per day",
65
+ "unit": "count",
66
+ "boundType": {
67
+ "kind": "cumulative_count",
68
+ "window": "daily"
69
+ },
70
+ "appliesTo": [
71
+ "purchase"
72
+ ]
73
+ }
74
+ }
75
+ },
76
+ "contextSchema": {
77
+ "keyOrder": [
78
+ "currency",
79
+ "category",
80
+ "allowed_vendors"
81
+ ],
82
+ "fields": {
83
+ "currency": {
84
+ "type": "string",
85
+ "required": true,
86
+ "displayName": "Currency",
87
+ "description": "Permitted currency code",
88
+ "constraint": {
89
+ "type": "string",
90
+ "enforceable": [
91
+ "enum"
92
+ ]
93
+ },
94
+ "enum": [
95
+ "USD",
96
+ "EUR",
97
+ "GBP",
98
+ "CHF",
99
+ "JPY",
100
+ "CAD",
101
+ "AUD"
102
+ ]
103
+ },
104
+ "category": {
105
+ "type": "string",
106
+ "required": true,
107
+ "displayName": "Purchase category",
108
+ "description": "Authorized purchase category",
109
+ "constraint": {
110
+ "type": "string",
111
+ "enforceable": [
112
+ "enum"
113
+ ]
114
+ },
115
+ "enum": [
116
+ "subscription",
117
+ "supply",
118
+ "service",
119
+ "advertising"
120
+ ]
121
+ },
122
+ "allowed_vendors": {
123
+ "type": "string",
124
+ "required": false,
125
+ "displayName": "Allowed vendors",
126
+ "description": "Comma-separated list of approved vendor names",
127
+ "constraint": {
128
+ "type": "string",
129
+ "enforceable": [
130
+ "subset"
131
+ ]
132
+ }
133
+ }
134
+ }
135
+ },
136
+ "executionContextSchema": {
137
+ "fields": {
138
+ "spend": {
139
+ "source": "declared",
140
+ "description": "Purchase amount in currency units",
141
+ "required": true,
142
+ "constraint": {
143
+ "type": "number",
144
+ "enforceable": [
145
+ "max"
146
+ ]
147
+ }
148
+ },
149
+ "currency": {
150
+ "source": "declared",
151
+ "description": "Currency code",
152
+ "required": true,
153
+ "constraint": {
154
+ "type": "string",
155
+ "enforceable": [
156
+ "enum"
157
+ ]
158
+ }
159
+ },
160
+ "category": {
161
+ "source": "declared",
162
+ "description": "Purchase category",
163
+ "required": true,
164
+ "constraint": {
165
+ "type": "string",
166
+ "enforceable": [
167
+ "enum"
168
+ ]
169
+ }
170
+ },
171
+ "allowed_vendors": {
172
+ "source": "declared",
173
+ "description": "Vendor name for this purchase (checked against approved list)",
174
+ "required": false,
175
+ "constraint": {
176
+ "type": "string",
177
+ "enforceable": [
178
+ "subset"
179
+ ]
180
+ }
181
+ },
182
+ "spend_daily": {
183
+ "source": "cumulative",
184
+ "cumulativeField": "spend",
185
+ "window": "daily",
186
+ "description": "Running daily spend total (resolved from execution log)",
187
+ "required": true,
188
+ "constraint": {
189
+ "type": "number",
190
+ "enforceable": [
191
+ "max"
192
+ ]
193
+ }
194
+ },
195
+ "spend_monthly": {
196
+ "source": "cumulative",
197
+ "cumulativeField": "spend",
198
+ "window": "monthly",
199
+ "description": "Running monthly spend total (resolved from execution log)",
200
+ "required": true,
201
+ "constraint": {
202
+ "type": "number",
203
+ "enforceable": [
204
+ "max"
205
+ ]
206
+ }
207
+ },
208
+ "transaction_count_daily": {
209
+ "source": "cumulative",
210
+ "cumulativeField": "_count",
211
+ "window": "daily",
212
+ "description": "Running daily transaction count (resolved from execution log)",
213
+ "required": true,
214
+ "constraint": {
215
+ "type": "number",
216
+ "enforceable": [
217
+ "max"
218
+ ]
219
+ }
220
+ }
221
+ }
222
+ },
223
+ "requiredGates": [
224
+ "bounds",
225
+ "intent",
226
+ "commitment",
227
+ "decision_owner"
228
+ ],
229
+ "ttl": {
230
+ "default": 86400,
231
+ "max": 2592000
232
+ },
233
+ "retention_minimum": 7776000
234
+ }
@@ -0,0 +1,290 @@
1
+ {
2
+ "id": "github.com/humanagencyprotocol/hap-profiles/sales@0.3",
3
+ "name": "Sales",
4
+ "version": "0.3",
5
+ "description": "Sales authority — governs quote-to-order in an ERP: draft quotes, send quotes to customers, convert quotes to orders. No invoicing, delivery, payments or master data.",
6
+ "boundsSchema": {
7
+ "actionTypes": [
8
+ "quote",
9
+ "send",
10
+ "order",
11
+ "setup"
12
+ ],
13
+ "keyOrder": [
14
+ "profile",
15
+ "read_access",
16
+ "value_max",
17
+ "discount_max",
18
+ "order_value_daily_max",
19
+ "quote_daily_max",
20
+ "send_daily_max",
21
+ "order_daily_max",
22
+ "setup_daily_max"
23
+ ],
24
+ "fields": {
25
+ "profile": {
26
+ "type": "string",
27
+ "required": true
28
+ },
29
+ "read_access": {
30
+ "type": "string",
31
+ "required": true,
32
+ "displayName": "Read access",
33
+ "description": "Allow the agent to look up items, stock, list prices, customers (incl. credit limit and open balance), quotes and orders.",
34
+ "boundType": {
35
+ "kind": "enum",
36
+ "values": [
37
+ "unlimited",
38
+ "none"
39
+ ]
40
+ },
41
+ "toolsDescription": "list_items, get_item, find_customers, get_customer, list_quotes, get_quote, list_orders, get_order",
42
+ "default": "unlimited"
43
+ },
44
+ "value_max": {
45
+ "type": "number",
46
+ "required": true,
47
+ "displayName": "Max value per quote or order",
48
+ "description": "Maximum net document total of a single quote or order, in currency units. The connector recalculates the total from the lines and refuses a call whose declared value does not match.",
49
+ "unit": "currency:EUR",
50
+ "boundType": {
51
+ "kind": "per_transaction",
52
+ "of": "value",
53
+ "requiredFor": [
54
+ "quote",
55
+ "order"
56
+ ]
57
+ }
58
+ },
59
+ "discount_max": {
60
+ "type": "number",
61
+ "required": true,
62
+ "displayName": "Max discount",
63
+ "description": "Maximum document discount against list price, in percent. 0 means list price only. The connector recalculates the discount from list prices and refuses a call whose declared discount does not match.",
64
+ "unit": "percent",
65
+ "boundType": {
66
+ "kind": "per_transaction",
67
+ "of": "discount_pct",
68
+ "requiredFor": [
69
+ "quote",
70
+ "order"
71
+ ]
72
+ }
73
+ },
74
+ "order_value_daily_max": {
75
+ "type": "number",
76
+ "required": true,
77
+ "displayName": "Daily order value",
78
+ "description": "Maximum cumulative net value of orders created per day, in currency units.",
79
+ "unit": "currency:EUR",
80
+ "boundType": {
81
+ "kind": "cumulative_sum",
82
+ "of": "value",
83
+ "window": "daily"
84
+ },
85
+ "appliesTo": [
86
+ "order"
87
+ ]
88
+ },
89
+ "quote_daily_max": {
90
+ "type": "number",
91
+ "required": true,
92
+ "displayName": "Quotes per day",
93
+ "description": "Maximum draft quotes created or edited per day",
94
+ "unit": "count",
95
+ "boundType": {
96
+ "kind": "cumulative_count",
97
+ "window": "daily"
98
+ },
99
+ "appliesTo": [
100
+ "quote"
101
+ ]
102
+ },
103
+ "send_daily_max": {
104
+ "type": "number",
105
+ "required": true,
106
+ "displayName": "Quotes sent per day",
107
+ "description": "Maximum quotes sent to customers per day. Sending a quote is a binding offer.",
108
+ "unit": "count",
109
+ "boundType": {
110
+ "kind": "cumulative_count",
111
+ "window": "daily"
112
+ },
113
+ "appliesTo": [
114
+ "send"
115
+ ]
116
+ },
117
+ "order_daily_max": {
118
+ "type": "number",
119
+ "required": true,
120
+ "displayName": "Orders per day",
121
+ "description": "Maximum quotes converted to orders per day. An order reserves stock and commits a delivery date.",
122
+ "unit": "count",
123
+ "boundType": {
124
+ "kind": "cumulative_count",
125
+ "window": "daily"
126
+ },
127
+ "appliesTo": [
128
+ "order"
129
+ ]
130
+ },
131
+ "setup_daily_max": {
132
+ "type": "number",
133
+ "required": true,
134
+ "displayName": "Test-data loads per day",
135
+ "description": "How many times per day the simulated system may be loaded with a simulation package (load_simulation). Only works while the connector runs in simulation mode, and only into an empty system — a load cannot edit or replace existing test data. 0 = not allowed. Grant it only in a separate setup mandate, never in the mandate the AI uses for its work.",
136
+ "unit": "count",
137
+ "boundType": {
138
+ "kind": "cumulative_count",
139
+ "window": "daily"
140
+ },
141
+ "appliesTo": [
142
+ "setup"
143
+ ],
144
+ "default": 0
145
+ }
146
+ }
147
+ },
148
+ "contextSchema": {
149
+ "keyOrder": [
150
+ "currency"
151
+ ],
152
+ "fields": {
153
+ "currency": {
154
+ "type": "string",
155
+ "required": true,
156
+ "displayName": "Currency",
157
+ "description": "Permitted document currency",
158
+ "constraint": {
159
+ "type": "string",
160
+ "enforceable": [
161
+ "enum"
162
+ ]
163
+ },
164
+ "enum": [
165
+ "EUR",
166
+ "CHF",
167
+ "USD",
168
+ "GBP"
169
+ ]
170
+ }
171
+ }
172
+ },
173
+ "executionContextSchema": {
174
+ "fields": {
175
+ "value": {
176
+ "source": "declared",
177
+ "description": "Net document total in currency units (verified by the connector against the lines)",
178
+ "required": true,
179
+ "constraint": {
180
+ "type": "number",
181
+ "enforceable": [
182
+ "max"
183
+ ]
184
+ }
185
+ },
186
+ "discount_pct": {
187
+ "source": "declared",
188
+ "description": "Document discount against list price in percent (verified by the connector against list prices)",
189
+ "required": true,
190
+ "constraint": {
191
+ "type": "number",
192
+ "enforceable": [
193
+ "max"
194
+ ]
195
+ }
196
+ },
197
+ "currency": {
198
+ "source": "declared",
199
+ "description": "Document currency",
200
+ "required": true,
201
+ "constraint": {
202
+ "type": "string",
203
+ "enforceable": [
204
+ "enum"
205
+ ]
206
+ }
207
+ },
208
+ "order_value_daily": {
209
+ "source": "cumulative",
210
+ "description": "Running daily order value (resolved from execution log)",
211
+ "required": true,
212
+ "constraint": {
213
+ "type": "number",
214
+ "enforceable": [
215
+ "max"
216
+ ]
217
+ },
218
+ "cumulativeField": "value",
219
+ "window": "daily"
220
+ },
221
+ "quote_count_daily": {
222
+ "source": "cumulative",
223
+ "description": "Running daily quote count",
224
+ "required": true,
225
+ "constraint": {
226
+ "type": "number",
227
+ "enforceable": [
228
+ "max"
229
+ ]
230
+ },
231
+ "cumulativeField": "_count",
232
+ "window": "daily"
233
+ },
234
+ "send_count_daily": {
235
+ "source": "cumulative",
236
+ "description": "Running daily send count",
237
+ "required": true,
238
+ "constraint": {
239
+ "type": "number",
240
+ "enforceable": [
241
+ "max"
242
+ ]
243
+ },
244
+ "cumulativeField": "_count",
245
+ "window": "daily"
246
+ },
247
+ "order_count_daily": {
248
+ "source": "cumulative",
249
+ "description": "Running daily order count",
250
+ "required": true,
251
+ "constraint": {
252
+ "type": "number",
253
+ "enforceable": [
254
+ "max"
255
+ ]
256
+ },
257
+ "cumulativeField": "_count",
258
+ "window": "daily"
259
+ },
260
+ "setup_count_daily": {
261
+ "source": "cumulative",
262
+ "cumulativeField": "_count",
263
+ "window": "daily",
264
+ "description": "Running daily count of test-data loads",
265
+ "required": true,
266
+ "constraint": {
267
+ "type": "number",
268
+ "enforceable": [
269
+ "max"
270
+ ]
271
+ }
272
+ }
273
+ }
274
+ },
275
+ "requiredGates": [
276
+ "bounds",
277
+ "intent",
278
+ "commitment",
279
+ "decision_owner"
280
+ ],
281
+ "ttl": {
282
+ "default": 86400,
283
+ "max": 2592000
284
+ },
285
+ "retention_minimum": 7776000,
286
+ "content_binding": {
287
+ "version": "1",
288
+ "kind": "jcs"
289
+ }
290
+ }
package/server.js CHANGED
@@ -12,7 +12,8 @@ import { dirname, join } from 'node:path';
12
12
  import { fileURLToPath } from 'node:url';
13
13
  import { homedir, tmpdir, constants as osConstants } from 'node:os';
14
14
  import { existsSync, mkdtempSync, readFileSync, writeFileSync, rmSync } from 'node:fs';
15
- import { resolveCaFile, resolveSimulation } from './lib/config.mjs';
15
+ import { resolveCaFile, resolveProxyUrl, resolveSimulation } from './lib/config.mjs';
16
+ import { readPolicy, isPolicyLocked } from './lib/policy.mjs';
16
17
  import { unsupportedNodeReason } from './lib/node-version.mjs';
17
18
 
18
19
  // Docker and the login service start this file directly, not through the CLI,
@@ -121,6 +122,43 @@ if (savedCaFile && !process.env.SUVEREN_CA_REEXEC_DONE) {
121
122
  // via `...process.env`, all the way down to individual integrations.
122
123
  delete process.env.SUVEREN_CA_REEXEC_DONE;
123
124
 
125
+ // ─── Corporate proxy (`config set proxy <url>` / `--proxy` / IT policy) ────
126
+ //
127
+ // `HTTP_PROXY`/`HTTPS_PROXY` are read fresh on every outbound call (see
128
+ // proxy-env.ts in both apps) — unlike NODE_EXTRA_CA_CERTS above, there is no
129
+ // Node-startup-time requirement, so no re-exec dance is needed here.
130
+ //
131
+ // Two different precedence rules apply, because `resolveProxyUrl` already
132
+ // folds IT policy ahead of the saved value (see config.mjs):
133
+ // - IT policy LOCKED (`Proxy` — see lib/policy.mjs): OVERRIDES an
134
+ // operator's own HTTP_PROXY/HTTPS_PROXY unconditionally — "locked"
135
+ // means locked even against a pre-existing env var, the one case among
136
+ // all the policy-able settings where there IS a meaningful env tier to
137
+ // override (every other locked key has none to begin with). This is
138
+ // the Windows-company-laptop case: the proxy lives in system settings,
139
+ // not an env var an employee thinks to set.
140
+ // - Otherwise (a plain saved `config set proxy`): fills in whichever of
141
+ // HTTP_PROXY/HTTPS_PROXY is still unset — an operator's own value
142
+ // always wins, same as every other env-vs-saved precedence here.
143
+ const resolvedProxyUrl = resolveProxyUrl(DATA_DIR);
144
+ if (resolvedProxyUrl) {
145
+ if (isPolicyLocked('proxy')) {
146
+ process.env.HTTP_PROXY = resolvedProxyUrl;
147
+ process.env.HTTPS_PROXY = resolvedProxyUrl;
148
+ } else {
149
+ process.env.HTTP_PROXY ??= resolvedProxyUrl;
150
+ process.env.HTTPS_PROXY ??= resolvedProxyUrl;
151
+ }
152
+ }
153
+
154
+ // `NoProxy` has no saved-config/CLI counterpart at all (see policy.mjs) —
155
+ // NO_PROXY stays env-only except for this one IT-policy override, which (like
156
+ // `Proxy` above) overrides an operator's own NO_PROXY unconditionally when set.
157
+ const policyNoProxy = readPolicy().policy.noProxy;
158
+ if (policyNoProxy) {
159
+ process.env.NO_PROXY = policyNoProxy;
160
+ }
161
+
124
162
  const CP_PORT = process.env.SUVEREN_CP_PORT ?? '3400';
125
163
  const MCP_PORT = process.env.SUVEREN_MCP_PORT ?? '3430';
126
164
  const UI_DIST = process.env.HAP_UI_DIST ?? join(__dirname, 'dist', 'ui');
@@ -147,13 +185,18 @@ const env = {
147
185
  SUVEREN_PROFILES_DIR: PROFILES_DIR,
148
186
  // Single shared internal secret so CP↔MCP authenticate the bridge.
149
187
  SUVEREN_INTERNAL_SECRET: process.env.SUVEREN_INTERNAL_SECRET ?? randomHex(32),
150
- // Simulation mode — same precedence as the CA bundle above: an explicit env
151
- // var (set directly, or by `start --simulation` setting process.env for
152
- // THIS run — see bundle/bin/suveren-gateway.js) wins; otherwise the saved
153
- // config.json value, which is what `suveren-gateway simulation on|off`
154
- // changes for every future start/restart, including autostart (which
155
- // spawns this file directly, bypassing the CLI's own flag handling).
156
- SUVEREN_SIMULATION: process.env.SUVEREN_SIMULATION ?? (resolveSimulation(DATA_DIR) ? '1' : '0'),
188
+ // Simulation mode — IT policy (see lib/policy.mjs) wins UNCONDITIONALLY,
189
+ // ahead of even an explicit env var: that is what "locked" means. Below
190
+ // that: an explicit env var (set directly, or by `start --simulation`
191
+ // setting process.env for THIS run — see bundle/bin/suveren-gateway.js)
192
+ // wins; otherwise the saved config.json value, which is what
193
+ // `suveren-gateway simulation on|off` changes for every future
194
+ // start/restart, including autostart (which spawns this file directly,
195
+ // bypassing the CLI's own flag handling).
196
+ SUVEREN_SIMULATION:
197
+ readPolicy().policy.simulation !== undefined
198
+ ? (readPolicy().policy.simulation ? '1' : '0')
199
+ : (process.env.SUVEREN_SIMULATION ?? (resolveSimulation(DATA_DIR) ? '1' : '0')),
157
200
  // Passed by the login service (see bundle/lib/autostart-templates.mjs) so the
158
201
  // control-plane knows a human is NOT sitting in front of a terminal watching
159
202
  // it start — that is when a locked gateway needs to announce itself.