rovloom 0.4.0 → 0.6.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 (39) hide show
  1. package/README.md +21 -41
  2. package/dist/src/admin/index.js +1 -1
  3. package/dist/src/{chunk-smpevv7n.js → chunk-0dqx2mmz.js} +9 -16
  4. package/dist/src/{chunk-z1vhdr3c.js → chunk-0qgk7mhz.js} +16 -15
  5. package/dist/src/{chunk-qfyefed3.js → chunk-1bf0wmgk.js} +1 -1
  6. package/dist/src/{chunk-ymsx7hhd.js → chunk-3dvn33c4.js} +61 -62
  7. package/dist/src/chunk-5b8g62kw.js +438 -0
  8. package/dist/src/chunk-5j0gj6w2.js +7 -0
  9. package/dist/src/{chunk-v1httsp6.js → chunk-8ykrqhxh.js} +10 -4
  10. package/dist/src/{chunk-76wgt2g5.js → chunk-9zkq0kc8.js} +14 -8
  11. package/dist/src/{chunk-6hxy1fcg.js → chunk-bspzg8er.js} +2 -2
  12. package/dist/src/{chunk-dtj9y82v.js → chunk-d2kz25f2.js} +269 -39
  13. package/dist/src/{chunk-khmgp03q.js → chunk-ez7d5dac.js} +77 -16
  14. package/dist/src/{chunk-emcefrc1.js → chunk-g27b2gmx.js} +3 -4
  15. package/dist/src/{chunk-keg92d31.js → chunk-gyvqqnjb.js} +34 -59
  16. package/dist/src/{chunk-bjdqj43d.js → chunk-hpzr94hm.js} +23 -6
  17. package/dist/src/{chunk-t3pn1435.js → chunk-jf4r0r44.js} +4 -4
  18. package/dist/src/{chunk-4etdq0cp.js → chunk-jka7v3wb.js} +4 -7
  19. package/dist/src/{chunk-5vx1b9z8.js → chunk-m9c1pnv9.js} +5 -5
  20. package/dist/src/{chunk-7k9g0x5e.js → chunk-mg0fd6ph.js} +1 -1
  21. package/dist/src/{chunk-1fyfrgte.js → chunk-n6wtjyfb.js} +10 -18
  22. package/dist/src/{chunk-nyn75b8v.js → chunk-qjh9x872.js} +30 -1
  23. package/dist/src/{chunk-xmkgq3nh.js → chunk-s3ydr65k.js} +2 -2
  24. package/dist/src/{chunk-4af38z2b.js → chunk-srfbjwtk.js} +4 -3
  25. package/dist/src/{chunk-8x6ygvhh.js → chunk-ssp57p43.js} +27 -31
  26. package/dist/src/{chunk-qab3m6nh.js → chunk-wxqhscsw.js} +43 -7
  27. package/dist/src/{chunk-7q96e161.js → chunk-x0td5wn4.js} +6 -12
  28. package/dist/src/{chunk-ydmabg5t.js → chunk-ytpfmcp1.js} +7 -6
  29. package/dist/src/{chunk-wdr3sdsm.js → chunk-yv9px0f5.js} +60 -32
  30. package/dist/src/{chunk-se5daacy.js → chunk-z7kkfrgz.js} +1 -1
  31. package/dist/src/{chunk-fd9n51mc.js → chunk-zjkjjm81.js} +87 -61
  32. package/dist/src/cli/index.js +27 -19
  33. package/dist/src/cli/main.js +18 -13
  34. package/dist/src/mcp/index.js +6 -5
  35. package/dist/src/runtime/index.js +2 -2
  36. package/dist/src/service/index.js +3 -4
  37. package/dist/src/store.js +1 -1
  38. package/dist/src/webui/index.js +1107 -158
  39. package/package.json +1 -1
@@ -14,22 +14,25 @@ import {
14
14
  assertUsage,
15
15
  GATEWAY_LOCK_FORMAT2,
16
16
  Store2
17
- } from "./chunk-nyn75b8v.js";
17
+ } from "./chunk-qjh9x872.js";
18
18
  import"./chunk-09f9q26d.js";
19
+ import {
20
+ cliMessages2
21
+ } from "./chunk-5b8g62kw.js";
19
22
  import {
20
23
  readRuntimeConfig2
21
- } from "./chunk-6hxy1fcg.js";
24
+ } from "./chunk-bspzg8er.js";
22
25
  import {
23
26
  resolveRoleConfigPath2
24
- } from "./chunk-qfyefed3.js";
27
+ } from "./chunk-1bf0wmgk.js";
25
28
  import {
26
29
  runtimeReference
27
- } from "./chunk-se5daacy.js";
30
+ } from "./chunk-z7kkfrgz.js";
28
31
  import {
29
32
  CLI_ERROR_CODES,
30
33
  CliError2,
31
34
  readConfig2
32
- } from "./chunk-bjdqj43d.js";
35
+ } from "./chunk-hpzr94hm.js";
33
36
  import"./chunk-3nnahj0j.js";
34
37
  import {
35
38
  lockContent
@@ -131,17 +134,6 @@ function inspectStorage(input) {
131
134
  }
132
135
 
133
136
  // src/cli/storage-command.ts
134
- var STORAGE_HELP = `\u7528\u6CD5\uFF1A
135
- rovloom doctor --role gateway|connector [--config /absolute/config.json] [--json]
136
- rovloom storage check --role gateway [--config /absolute/config.json] [--json]
137
- rovloom storage backup --role gateway --output /path/to/gateway.backups/snapshot.db [--config /absolute/config.json] [--json]
138
- rovloom storage resume --role gateway [--config /absolute/config.json] [--json]
139
-
140
- Gateway \u547D\u4EE4\u987B\u5148\u505C\u6B62\u670D\u52A1\uFF1B\u6CBF\u7528\u540C\u4E00\u5199\u9501\uFF0C\u62D2\u7EDD\u6D3B\u52A8\u6216\u65E0\u6CD5\u5224\u5B9A\u7684\u6301\u6709\u8005\u3002
141
- check \u53EA\u8BFB\uFF1Bbackup \u4EC5\u4F7F\u7528\u771F\u5B9E\u72B6\u6001\u76EE\u5F55\u540C\u7EA7\u7684 <state>.backups \u666E\u901A\u76EE\u5F55\uFF0C\u5BFC\u51FA\u8131\u654F\u5FEB\u7167\uFF08\u4E0D\u542B\u539F\u6587\u53CA\u6709\u6548\u51ED\u636E\uFF09\uFF0C\u5B8C\u6574\u72B6\u6001\u9700\u53E6\u884C\u79BB\u7EBF\u5907\u4EFD\u3002
142
- resume \u662F\u672C\u673A\u64CD\u4F5C\u5458\u663E\u5F0F\u6279\u51C6\u7684\u89E3\u9664\u964D\u7EA7\uFF0C\u5B8C\u6574\u6027\u3001\u65F6\u949F\u6216\u9884\u7B97\u4E0D\u6B63\u5E38\u65F6\u4ECD\u62D2\u7EDD\u3002
143
- Connector doctor \u53EA\u68C0\u67E5\u914D\u7F6E\u53CA managed \u72B6\u6001\uFF0C\u4E0D\u542F\u52A8 native\uFF0C\u4E5F\u4E0D\u58F0\u660E native \u5C31\u7EEA\u3002
144
- `;
145
137
  function parse(args) {
146
138
  const action = args[0] === "doctor" ? "doctor" : args[1];
147
139
  if (!["doctor", "check", "backup", "resume"].includes(action ?? "")) {
@@ -201,7 +193,7 @@ async function handleStorageCommand(args, io, environment) {
201
193
  let json = args.includes("--json");
202
194
  try {
203
195
  if (args.length === 2 && args[1] === "--help") {
204
- io.stderr.write(STORAGE_HELP);
196
+ io.stderr.write(cliMessages2(io.language)["help.storage"]);
205
197
  return 0;
206
198
  }
207
199
  const flags = parse(args);
@@ -318,4 +310,4 @@ export {
318
310
  handleStorageCommand
319
311
  };
320
312
 
321
- //# debugId=007190958BDB411E64756E2164756E21
313
+ //# debugId=2C436BD5D6042D5D64756E2164756E21
@@ -1636,6 +1636,35 @@ class Store2 {
1636
1636
  revoke(machine) {
1637
1637
  this.devices.revoke(machine);
1638
1638
  }
1639
+ deleteDevice(machine, actor) {
1640
+ this.transaction((now) => {
1641
+ this.devices.applyRevoke(machine, now);
1642
+ for (const table of ["pairing_claims", "enrollments", "admin_pairings", "providers", "calls", "machines"]) {
1643
+ this.db.prepare(`DELETE FROM ${table} WHERE machine_id=?`).run(machine);
1644
+ }
1645
+ this.audit("machine_deleted", actor, now, undefined, { machine_id: machine });
1646
+ }, false);
1647
+ }
1648
+ listPairings(offset = 0) {
1649
+ return this.db.prepare(`SELECT p.pairing_id,p.machine_id,m.name,
1650
+ CASE WHEN p.status='pending' AND p.expires<=? THEN 'expired' ELSE p.status END AS status,p.expires
1651
+ FROM admin_pairings p JOIN machines m ON m.machine_id=p.machine_id ORDER BY p.expires DESC,p.pairing_id LIMIT 51 OFFSET ?`).all(this.clock(), offset);
1652
+ }
1653
+ deletePairing(pairing, actor) {
1654
+ return this.transaction((now) => {
1655
+ const application = this.devices.pairingApplication(pairing);
1656
+ if (!application) {
1657
+ throw new GatewayError2("pairing_not_found");
1658
+ }
1659
+ const unfinished = this.db.prepare("SELECT claim_id FROM pairing_claims WHERE pairing_id=? AND status IN ('approved','delivered')").get(pairing);
1660
+ if (unfinished) {
1661
+ this.devices.applyRevoke(application.machine_id, now);
1662
+ }
1663
+ this.db.prepare("DELETE FROM admin_pairings WHERE pairing_id=?").run(pairing);
1664
+ this.audit("pairing_deleted", actor, now, undefined, { pairing_id: pairing, machine_id: application.machine_id });
1665
+ return unfinished ? application.machine_id : null;
1666
+ }, false);
1667
+ }
1639
1668
  defineProvider(binding) {
1640
1669
  validateProvider2(binding);
1641
1670
  return this.transaction((now) => {
@@ -2275,4 +2304,4 @@ class Store2 {
2275
2304
 
2276
2305
  export { DEFAULT_RETENTION2, configuredSnapshotDirectory, SCHEMA_VERSION, assertBudget, assertUsage, GATEWAY_LOCK_FORMAT2, Store2 };
2277
2306
 
2278
- //# debugId=14A43F12EAAA7BD364756E2164756E21
2307
+ //# debugId=B8B3ABA270E159D964756E2164756E21
@@ -1,8 +1,8 @@
1
1
  // @bun
2
2
  // src/mcp/version.ts
3
3
  var MCP_REVISION2 = "2025-11-25";
4
- var PRODUCT_VERSION2 = "0.4.0";
4
+ var PRODUCT_VERSION2 = "0.6.0";
5
5
 
6
6
  export { MCP_REVISION2, PRODUCT_VERSION2 };
7
7
 
8
- //# debugId=37FCDB7BED42DE7D64756E2164756E21
8
+ //# debugId=F89FEF5D3124887164756E2164756E21
@@ -3,8 +3,9 @@ import {
3
3
  HTTP_BODY_BYTES2,
4
4
  createHttpFacade2,
5
5
  startHttpFacade2
6
- } from "./chunk-76wgt2g5.js";
7
- import"./chunk-smpevv7n.js";
6
+ } from "./chunk-9zkq0kc8.js";
7
+ import"./chunk-0dqx2mmz.js";
8
+ import"./chunk-5b8g62kw.js";
8
9
  import"./chunk-7crhmqn2.js";
9
10
  import"./chunk-d71r315c.js";
10
11
  export {
@@ -13,4 +14,4 @@ export {
13
14
  startHttpFacade2 as startHttpFacade
14
15
  };
15
16
 
16
- //# debugId=A0A48728336CC35C64756E2164756E21
17
+ //# debugId=7DE436763985B0CD64756E2164756E21
@@ -6,6 +6,10 @@ import {
6
6
  } from "./chunk-qt9m2kg2.js";
7
7
  import"./chunk-9tt13dm0.js";
8
8
  import"./chunk-g7pnjzkk.js";
9
+ import {
10
+ cliMessages2,
11
+ formatMessage2
12
+ } from "./chunk-5b8g62kw.js";
9
13
  import {
10
14
  setupFileStamp,
11
15
  writeSetupFile,
@@ -18,38 +22,23 @@ import {
18
22
  revokeCaller,
19
23
  summarize,
20
24
  isAdminPlaceholder
21
- } from "./chunk-qab3m6nh.js";
25
+ } from "./chunk-wxqhscsw.js";
22
26
  import {
23
27
  resolveRoleConfigPath2
24
- } from "./chunk-qfyefed3.js";
28
+ } from "./chunk-1bf0wmgk.js";
25
29
  import {
26
30
  runtimeReference,
27
31
  readRuntimeJson
28
- } from "./chunk-se5daacy.js";
32
+ } from "./chunk-z7kkfrgz.js";
29
33
  import {
30
34
  CLI_ERROR_CODES,
31
35
  CliError2,
32
36
  readConfig2
33
- } from "./chunk-bjdqj43d.js";
37
+ } from "./chunk-hpzr94hm.js";
34
38
  import"./chunk-3nnahj0j.js";
35
39
  import"./chunk-0y5542k8.js";
36
40
 
37
41
  // src/cli/token-command.ts
38
- var TOKEN_HELP = `\u7528\u6CD5\uFF1A
39
- rovloom token list [--config /absolute/path/gateway.json]
40
- rovloom token create --principal ID --like EXISTING_ID [--ttl-days N]
41
- rovloom token renew --principal ID [--ttl-days N]
42
- rovloom token rotate --principal ID [--ttl-days N] [--grace-minutes M]
43
- rovloom token revoke --principal ID
44
- rovloom token permissions --principal ID --discard-response true|false
45
-
46
- \u4EE4\u724C\u6CE8\u518C\u8868\u4F4D\u4E8E Gateway \u72B6\u6001\u76EE\u5F55\uFF1BGateway \u6BCF\u6B21\u8BA4\u8BC1\u91CD\u8BFB\uFF0C\u4FEE\u6539\u7ACB\u5373\u751F\u6548\u3001\u65E0\u9700\u91CD\u542F\u3002
47
- create/rotate \u628A\u65B0\u4EE4\u724C\u539F\u503C\u53EA\u5199\u5230 stdout \u4E00\u6B21\uFF08\u6CE8\u518C\u8868\u53EA\u4FDD\u5B58 SHA-256 \u6458\u8981\uFF09\uFF1B\u5176\u4F59\u4FE1\u606F\u5199\u5230 stderr\u3002
48
- create \u7684\u65B0\u4E3B\u4F53\u6743\u9650\u514B\u9686\u81EA --like \u6307\u5B9A\u7684\u73B0\u6709\u4E3B\u4F53\uFF0C\u4E0D\u6269\u5927\u6743\u9650\u3002renew \u5EF6\u957F\u5230\u671F\u65F6\u95F4\u3001\u4EE4\u724C\u4E0D\u53D8\u3002
49
- rotate \u7B7E\u53D1\u65B0\u4EE4\u724C\uFF1B--grace-minutes \u5185\u65E7\u4EE4\u724C\u7EE7\u7EED\u6709\u6548\uFF08\u9ED8\u8BA4 0\uFF0C\u7ACB\u5373\u5931\u6548\uFF09\u3002revoke \u5220\u9664\u4E3B\u4F53\u3002
50
- permissions \u663E\u5F0F\u7BA1\u7406\u7ED3\u679C\u5220\u9664\u6743\u9650\uFF0C\u70ED\u751F\u6548\uFF1B\u9ED8\u8BA4\u5173\u95ED\u3002\u5220\u9664\u7ED3\u679C\u524D\u5E94\u5148\u8BFB\u53D6\u5E76\u4FDD\u5B58\u6240\u9700\u5185\u5BB9\u3002
51
- ttl \u9ED8\u8BA4 30 \u5929\uFF081-365\uFF09\u3002
52
- `;
53
42
  function parse(args) {
54
43
  const sub = args[1];
55
44
  if (!sub || !["list", "create", "renew", "rotate", "revoke", "permissions"].includes(sub)) {
@@ -129,7 +118,7 @@ function syncClientFile(stateDir, principal, token, issued, expires) {
129
118
  async function handleTokenCommand(args, io, environment, now = Date.now) {
130
119
  try {
131
120
  if (args.length === 2 && args[1] === "--help") {
132
- io.stderr.write(TOKEN_HELP);
121
+ io.stderr.write(cliMessages2(io.language)["help.token"]);
133
122
  return 0;
134
123
  }
135
124
  const flags = parse(args);
@@ -154,31 +143,38 @@ async function handleTokenCommand(args, io, environment, now = Date.now) {
154
143
  if (flags.sub === "create") {
155
144
  const made = createCaller(registry, flags.principal, flags.like, at, flags.days);
156
145
  next = made.registry;
157
- io.stderr.write("created " + flags.principal + " expires " + new Date(made.expires_at).toISOString() + `
158
- `);
146
+ io.stderr.write(formatMessage2(cliMessages2(io.language)["token.created"], {
147
+ principal: flags.principal,
148
+ expires: new Date(made.expires_at).toISOString()
149
+ }));
159
150
  io.stdout.write(made.token + `
160
151
  `);
161
152
  } else if (flags.sub === "renew") {
162
153
  const renewed = renewCaller(registry, flags.principal, at, flags.days);
163
154
  next = renewed.registry;
164
- io.stderr.write("renewed " + flags.principal + " expires " + new Date(renewed.expires_at).toISOString() + `
165
- `);
155
+ io.stderr.write(formatMessage2(cliMessages2(io.language)["token.renewed"], {
156
+ principal: flags.principal,
157
+ expires: new Date(renewed.expires_at).toISOString()
158
+ }));
166
159
  } else if (flags.sub === "rotate") {
167
160
  const rotated = rotateCaller(registry, flags.principal, at, { days: flags.days, graceMinutes: flags.grace });
168
161
  next = rotated.registry;
169
- io.stderr.write("rotated " + flags.principal + " expires " + new Date(rotated.expires_at).toISOString() + `
170
- `);
162
+ io.stderr.write(formatMessage2(cliMessages2(io.language)["token.rotated"], {
163
+ principal: flags.principal,
164
+ expires: new Date(rotated.expires_at).toISOString()
165
+ }));
171
166
  issued = rotated.token;
172
167
  io.stdout.write(rotated.token + `
173
168
  `);
174
169
  } else if (flags.sub === "permissions") {
175
170
  next = setDiscardPermission(registry, flags.principal, flags.discard);
176
- io.stderr.write("permissions " + flags.principal + " discard_response=" + String(flags.discard) + `
177
- `);
171
+ io.stderr.write(formatMessage2(cliMessages2(io.language)["token.permissions"], {
172
+ principal: flags.principal,
173
+ discard: String(flags.discard)
174
+ }));
178
175
  } else {
179
176
  next = revokeCaller(registry, flags.principal);
180
- io.stderr.write("revoked " + flags.principal + `
181
- `);
177
+ io.stderr.write(formatMessage2(cliMessages2(io.language)["token.revoked"], { principal: flags.principal }));
182
178
  }
183
179
  writeSetupFile(path, boundedJson2(next, REGISTRY_MAX_BYTES), true, stamp);
184
180
  if (flags.sub === "renew" || flags.sub === "rotate") {
@@ -201,4 +197,4 @@ export {
201
197
  handleTokenCommand
202
198
  };
203
199
 
204
- //# debugId=FA979FCCD381000964756E2164756E21
200
+ //# debugId=97EFCC7F7ABCF40164756E2164756E21
@@ -6,12 +6,15 @@ import {
6
6
  boundedJson2,
7
7
  validatePolicies2
8
8
  } from "./chunk-qt9m2kg2.js";
9
+ import {
10
+ DEFAULT_PAIRING_POLICY_REF
11
+ } from "./chunk-5j0gj6w2.js";
9
12
  import {
10
13
  readRuntimeJson
11
- } from "./chunk-se5daacy.js";
14
+ } from "./chunk-z7kkfrgz.js";
12
15
  import {
13
16
  CliError2
14
- } from "./chunk-bjdqj43d.js";
17
+ } from "./chunk-hpzr94hm.js";
15
18
  import {
16
19
  privatePath,
17
20
  readPrivateBytes
@@ -130,7 +133,10 @@ function parseRegistry(value, resource) {
130
133
  const digests = new Set;
131
134
  for (const input of source.callers) {
132
135
  const caller = object2(input);
133
- fields2(caller, ["principal", "token_sha256", "issued_at", "expires_at"], ["previous"]);
136
+ fields2(caller, ["principal", "token_sha256", "issued_at", "expires_at"], ["previous", "token", "scope"]);
137
+ if (caller.scope !== undefined && caller.scope !== "gateway" || caller.token !== undefined && (typeof caller.token !== "string" || !/^[A-Za-z0-9_-]{43}$/.test(caller.token) || digestToken(caller.token) !== caller.token_sha256)) {
138
+ invalid();
139
+ }
134
140
  const principal = object2(caller.principal);
135
141
  fields2(principal, ["id", "policy"], ["permissions"]);
136
142
  if (principal.permissions !== undefined) {
@@ -187,7 +193,9 @@ function createCaller(registry, principal, like, now, days) {
187
193
  const expires_at = now + ttl(days);
188
194
  const entry = {
189
195
  principal: { ...structuredClone(template.principal), id: principal },
196
+ ...template.scope ? { scope: template.scope } : {},
190
197
  token_sha256: digestToken(token),
198
+ token,
191
199
  issued_at: now,
192
200
  expires_at
193
201
  };
@@ -220,7 +228,9 @@ function rotateCaller(registry, principal, now, options = {}) {
220
228
  const expires_at = now + ttl(options.days);
221
229
  const entry = {
222
230
  principal: old.principal,
231
+ ...old.scope ? { scope: old.scope } : {},
223
232
  token_sha256: digestToken(token),
233
+ token,
224
234
  issued_at: now,
225
235
  expires_at,
226
236
  ...grace > 0 ? { previous: { token_sha256: old.token_sha256, expires_at: Math.min(now + grace * 60000, old.expires_at) } } : {}
@@ -247,6 +257,22 @@ function summarize(registry, now) {
247
257
  rotating: entry.previous !== undefined && now < entry.previous.expires_at
248
258
  }));
249
259
  }
260
+ function createGatewayCaller(registry, principal, now, days = 365) {
261
+ if (!PRINCIPAL.test(principal) || registry.callers.some((entry) => entry.principal.id === principal) || registry.callers.length >= MAX_CALLERS) {
262
+ throw new CliError2("invalid_arguments");
263
+ }
264
+ const token = newToken();
265
+ const expires_at = now + ttl(days);
266
+ const entry = {
267
+ principal: { id: principal, policy: structuredClone(registry.local_policy) },
268
+ scope: "gateway",
269
+ token,
270
+ token_sha256: digestToken(token),
271
+ issued_at: now,
272
+ expires_at
273
+ };
274
+ return { registry: { ...registry, callers: [...registry.callers, entry] }, token, expires_at };
275
+ }
250
276
 
251
277
  // src/cli/admin-registry.ts
252
278
  var ADMIN_POLICY_REVISION = "rovloom_admin_v1";
@@ -255,7 +281,13 @@ var DEFAULT_ADMIN_TTL_DAYS = 30;
255
281
  var DAY2 = 86400000;
256
282
  var PRINCIPAL2 = /^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,127}$/;
257
283
  function emptyManagement(port) {
258
- return { schema_version: 1, kind: "management-session-source", port, sessions: [], pairing_policy_refs: [] };
284
+ return {
285
+ schema_version: 1,
286
+ kind: "management-session-source",
287
+ port,
288
+ sessions: [],
289
+ pairing_policy_refs: [DEFAULT_PAIRING_POLICY_REF]
290
+ };
259
291
  }
260
292
  function isAdminPlaceholder(entry) {
261
293
  return entry.principal.policy.revision === ADMIN_POLICY_REVISION && entry.principal.policy.grants.length === 0;
@@ -309,7 +341,11 @@ function issueAdmin(callers, management, principal, now, days) {
309
341
  placeholder
310
342
  ]
311
343
  },
312
- management: { ...management, sessions: [...others, session] },
344
+ management: {
345
+ ...management,
346
+ sessions: [...others, session],
347
+ pairing_policy_refs: management.pairing_policy_refs.length ? management.pairing_policy_refs : [DEFAULT_PAIRING_POLICY_REF]
348
+ },
313
349
  secret,
314
350
  expires_at
315
351
  };
@@ -338,6 +374,6 @@ function summarizeAdmins(management, now) {
338
374
  }));
339
375
  }
340
376
 
341
- export { setupFileStamp, writeSetupFile, readSetupJson, writeSetupJson, REGISTRY_MAX_BYTES, parseRegistry, createCaller, setDiscardPermission, renewCaller, rotateCaller, revokeCaller, summarize, DEFAULT_ADMIN_TTL_DAYS, emptyManagement, isAdminPlaceholder, issueAdmin, revokeAdmin, summarizeAdmins };
377
+ export { setupFileStamp, writeSetupFile, readSetupJson, writeSetupJson, REGISTRY_MAX_BYTES, digestToken, parseRegistry, createCaller, setDiscardPermission, renewCaller, rotateCaller, revokeCaller, summarize, createGatewayCaller, DEFAULT_ADMIN_TTL_DAYS, emptyManagement, isAdminPlaceholder, issueAdmin, revokeAdmin, summarizeAdmins };
342
378
 
343
- //# debugId=AF7C6D3D6A23BCC764756E2164756E21
379
+ //# debugId=69610EBBA61A8B4564756E2164756E21
@@ -1,4 +1,7 @@
1
1
  // @bun
2
+ import {
3
+ cliMessages2
4
+ } from "./chunk-5b8g62kw.js";
2
5
  import {
3
6
  isRole,
4
7
  servicePlatform,
@@ -10,15 +13,6 @@ import {
10
13
 
11
14
  // src/service/command.ts
12
15
  var actions = new Set(["install", "uninstall", "start", "restart", "stop"]);
13
- var SERVICE_HELP2 = `service install --role gateway|connector [--executable ABS] [--config ABS] --workdir ABS --user ACCOUNT
14
- service uninstall|start|restart|stop --role gateway|connector
15
- install registers boot startup without starting; uninstall only revokes future boot startup and preserves running instances and their control records.
16
- Omitted --executable uses the current standalone binary; a different binary is rejected.
17
- Omitted --config uses the same current-user role file as foreground startup. Installation fixes its absolute path; background startup does not select another home.
18
- Linux uses systemd system units; macOS uses system LaunchDaemons; Windows uses boot-triggered tasks, not SCM services or login tasks.
19
- Gateway services support Linux systemd, macOS system LaunchDaemons and Windows boot-triggered tasks; the local native Connector service supports Linux x64, Windows x64 and macOS (x64 / arm64).
20
- Windows uses the explicitly selected existing user with S4U, without storing a password or requiring login. It does not provide an interactive desktop or Windows network credentials/EFS access. Running services receive a local shutdown request; queued/start-racing tasks return service_busy without forced termination; uninstall never stops it.
21
- `;
22
16
  function parseServiceCommand2(args, platform, resolveDefaultConfig, executable) {
23
17
  if (args[0] !== "service") {
24
18
  return;
@@ -64,7 +58,7 @@ async function handleServiceCommand2(args, host) {
64
58
  }
65
59
  try {
66
60
  if (args.length === 2 && args[1] === "--help") {
67
- host.diagnostic(SERVICE_HELP2);
61
+ host.diagnostic(cliMessages2(host.language)["help.service"]);
68
62
  return 0;
69
63
  }
70
64
  const request = parseServiceCommand2(args, servicePlatform(host.platform), host.resolveDefaultConfig, host.executable);
@@ -79,6 +73,6 @@ async function handleServiceCommand2(args, host) {
79
73
  }
80
74
  }
81
75
 
82
- export { SERVICE_HELP2, parseServiceCommand2, handleServiceCommand2 };
76
+ export { parseServiceCommand2, handleServiceCommand2 };
83
77
 
84
- //# debugId=48AFF39224DEDE2664756E2164756E21
78
+ //# debugId=D7F60C51F033ABF664756E2164756E21
@@ -3,11 +3,12 @@ import {
3
3
  writeAdminFiles2,
4
4
  managementSourceExists2,
5
5
  handleAdminCommand2
6
- } from "./chunk-wdr3sdsm.js";
7
- import"./chunk-qab3m6nh.js";
8
- import"./chunk-qfyefed3.js";
9
- import"./chunk-se5daacy.js";
10
- import"./chunk-bjdqj43d.js";
6
+ } from "./chunk-yv9px0f5.js";
7
+ import"./chunk-wxqhscsw.js";
8
+ import"./chunk-1bf0wmgk.js";
9
+ import"./chunk-z7kkfrgz.js";
10
+ import"./chunk-hpzr94hm.js";
11
+ import"./chunk-5b8g62kw.js";
11
12
  import"./chunk-3nnahj0j.js";
12
13
  import"./chunk-0y5542k8.js";
13
14
  import"./chunk-g7pnjzkk.js";
@@ -21,4 +22,4 @@ export {
21
22
  writeAdminFiles2 as writeAdminFiles
22
23
  };
23
24
 
24
- //# debugId=214EC0FC923A8CDE64756E2164756E21
25
+ //# debugId=989933E8EAB2EB1A64756E2164756E21
@@ -4,6 +4,10 @@ import {
4
4
  fields2,
5
5
  boundedJson2
6
6
  } from "./chunk-qt9m2kg2.js";
7
+ import {
8
+ cliMessages2,
9
+ formatMessage2
10
+ } from "./chunk-5b8g62kw.js";
7
11
  import {
8
12
  setupFileStamp,
9
13
  writeSetupFile,
@@ -15,32 +19,25 @@ import {
15
19
  issueAdmin,
16
20
  revokeAdmin,
17
21
  summarizeAdmins
18
- } from "./chunk-qab3m6nh.js";
22
+ } from "./chunk-wxqhscsw.js";
19
23
  import {
20
24
  resolveRoleConfigPath2
21
- } from "./chunk-qfyefed3.js";
25
+ } from "./chunk-1bf0wmgk.js";
22
26
  import {
23
- runtimeReference,
24
- readRuntimeJson
25
- } from "./chunk-se5daacy.js";
27
+ runtimeReference
28
+ } from "./chunk-z7kkfrgz.js";
26
29
  import {
27
30
  CLI_ERROR_CODES,
28
31
  CliError2,
29
32
  readConfig2
30
- } from "./chunk-bjdqj43d.js";
33
+ } from "./chunk-hpzr94hm.js";
34
+ import {
35
+ canonicalLoopbackHost
36
+ } from "./chunk-6n9s7nfe.js";
31
37
 
32
38
  // src/cli/admin-command.ts
33
- var ADMIN_HELP = `\u7528\u6CD5\uFF1A
34
- rovloom admin list [--config /absolute/path/gateway.json]
35
- rovloom admin create [--principal ID] [--ttl-days N] [--config /absolute/path/gateway.json]
36
- rovloom admin revoke [--principal ID] [--config /absolute/path/gateway.json]
37
-
38
- \u7BA1\u7406\u9875\uFF08\u9ED8\u8BA4 http://127.0.0.1:9445/\uFF09\u7528 HTTP Basic \u767B\u5F55\uFF1A\u7528\u6237\u540D\u662F\u7BA1\u7406\u5458 ID\uFF08\u9ED8\u8BA4 admin\uFF09\uFF0C\u5BC6\u7801\u662F create \u8F93\u51FA\u7684\u53E3\u4EE4\u3002
39
- \u53E3\u4EE4\u539F\u503C\u53EA\u5728 create \u65F6\u5199\u5230 stdout \u4E00\u6B21\uFF08Gateway \u53EA\u4FDD\u5B58 SHA-256 \u6458\u8981\uFF09\uFF1B\u5176\u4F59\u4FE1\u606F\u5199\u5230 stderr\u3002\u4E22\u5931\u53E3\u4EE4\u5C31\u518D create \u4E00\u6B21\uFF1A
40
- \u540C\u540D\u7BA1\u7406\u5458\u88AB\u91CD\u65B0\u7B7E\u53D1\uFF0C\u65E7\u53E3\u4EE4\u4E0E\u5DF2\u767B\u5F55\u7684\u6D4F\u89C8\u5668\u4F1A\u8BDD\u7ACB\u5373\u5931\u6548\u3002revoke \u5220\u9664\u7BA1\u7406\u5458\uFF0C\u540C\u6837\u7ACB\u5373\u751F\u6548\u3002\u4FEE\u6539\u65E0\u9700\u91CD\u542F Gateway\u3002
41
- \u7BA1\u7406\u5458\u4E0D\u80FD\u8C03\u7528 MCP\uFF0C\u4E5F\u4E0E MCP \u4EE4\u724C\u3001\u8BBE\u5907\u51ED\u636E\u4E92\u4E0D\u901A\u7528\uFF1B\u6709\u6548\u671F\u9ED8\u8BA4 ${DEFAULT_ADMIN_TTL_DAYS} \u5929\uFF081-365\uFF09\u3002
42
- \u9700\u8981\u5148\u5B8C\u6210 Gateway \u9996\u6B21\u914D\u5BF9\uFF08\u6B64\u65F6\u624D\u5B58\u5728 MCP \u8C03\u7528\u65B9\u6CE8\u518C\u8868\uFF09\u3002
43
- `;
39
+ import { mkdirSync } from "fs";
40
+ import { dirname } from "path";
44
41
  function parse(args) {
45
42
  const sub = args[1];
46
43
  if (!sub || !["list", "create", "revoke"].includes(sub)) {
@@ -87,16 +84,44 @@ function loadManagement(path, port) {
87
84
  }
88
85
  return { file: value, exists: true };
89
86
  }
87
+ function callerResource(config) {
88
+ const hostname = canonicalLoopbackHost(config.mcp_hostname);
89
+ return new URL("http://" + hostname + ":" + config.mcp_port + "/mcp").href;
90
+ }
91
+ function emptyCallerRegistry(resource) {
92
+ return {
93
+ schema_version: 1,
94
+ kind: "mcp-caller-source",
95
+ resource,
96
+ local_policy: { revision: "rovloom_unpaired", grants: [] },
97
+ callers: []
98
+ };
99
+ }
100
+ function callersStamp(path) {
101
+ try {
102
+ return setupFileStamp(path);
103
+ } catch (error) {
104
+ if (error.code === "ENOENT") {
105
+ return;
106
+ }
107
+ throw error;
108
+ }
109
+ }
90
110
  function writeAdminFiles2(config, principal, at, days) {
91
111
  const callersPath = runtimeReference(config.state_dir, "mcp-callers");
92
112
  const managementPath = runtimeReference(config.state_dir, "management");
93
- const callersStamp = setupFileStamp(callersPath);
94
- const callers = parseRegistry(readRuntimeJson(callersPath, REGISTRY_MAX_BYTES));
113
+ mkdirSync(dirname(managementPath), { recursive: true, mode: 448 });
114
+ const stamp = callersStamp(callersPath);
115
+ const callers = parseRegistry(readSetupJson(callersPath, REGISTRY_MAX_BYTES) ?? emptyCallerRegistry(callerResource(config)));
95
116
  const { file, exists } = loadManagement(managementPath, config.management_port);
96
- const stamp = exists ? setupFileStamp(managementPath) : undefined;
117
+ const managementStamp = exists ? setupFileStamp(managementPath) : undefined;
97
118
  const issued = issueAdmin(callers, file, principal, at, days);
98
- writeSetupFile(callersPath, boundedJson2(issued.callers, REGISTRY_MAX_BYTES), true, callersStamp);
99
- writeSetupFile(managementPath, boundedJson2(issued.management, 65536), exists, stamp);
119
+ if (stamp === undefined) {
120
+ writeSetupFile(callersPath, boundedJson2(issued.callers, REGISTRY_MAX_BYTES), false);
121
+ } else {
122
+ writeSetupFile(callersPath, boundedJson2(issued.callers, REGISTRY_MAX_BYTES), true, stamp);
123
+ }
124
+ writeSetupFile(managementPath, boundedJson2(issued.management, 65536), exists, managementStamp);
100
125
  return { secret: issued.secret, expires_at: issued.expires_at };
101
126
  }
102
127
  function managementSourceExists2(config) {
@@ -105,7 +130,7 @@ function managementSourceExists2(config) {
105
130
  async function handleAdminCommand2(args, io, environment, now = Date.now) {
106
131
  try {
107
132
  if (args.length === 2 && args[1] === "--help") {
108
- io.stderr.write(ADMIN_HELP);
133
+ io.stderr.write(formatMessage2(cliMessages2(io.language)["help.admin"], { ttlDays: DEFAULT_ADMIN_TTL_DAYS }));
109
134
  return 0;
110
135
  }
111
136
  const flags = parse(args);
@@ -119,15 +144,19 @@ async function handleAdminCommand2(args, io, environment, now = Date.now) {
119
144
  const principal = flags.principal ?? "admin";
120
145
  if (flags.sub === "create") {
121
146
  const issued = writeAdminFiles2(config, principal, at, flags.days);
122
- io.stderr.write("created administrator " + principal + " expires " + new Date(issued.expires_at).toISOString() + `
123
- login at http://127.0.0.1:` + config.management_port + '/ with user "' + principal + `" and the password below (shown once)
124
- `);
147
+ io.stderr.write(formatMessage2(cliMessages2(io.language)["admin.create.created"], {
148
+ principal,
149
+ expires: new Date(issued.expires_at).toISOString()
150
+ }) + formatMessage2(cliMessages2(io.language)["admin.create.login"], {
151
+ url: "http://127.0.0.1:" + config.management_port + "/",
152
+ user: principal
153
+ }));
125
154
  io.stdout.write(issued.secret + `
126
155
  `);
127
156
  return 0;
128
157
  }
129
- const callersStamp = setupFileStamp(callersPath);
130
- const callers = parseRegistry(readRuntimeJson(callersPath, REGISTRY_MAX_BYTES));
158
+ const registryStamp = callersStamp(callersPath);
159
+ const callers = parseRegistry(readSetupJson(callersPath, REGISTRY_MAX_BYTES) ?? emptyCallerRegistry(callerResource(config)));
131
160
  const { file, exists } = loadManagement(managementPath, config.management_port);
132
161
  if (flags.sub === "list") {
133
162
  for (const row of summarizeAdmins(file, at)) {
@@ -139,9 +168,8 @@ login at http://127.0.0.1:` + config.management_port + '/ with user "' + princip
139
168
  const stamp = exists ? setupFileStamp(managementPath) : undefined;
140
169
  const revoked = revokeAdmin(callers, file, principal);
141
170
  writeSetupFile(managementPath, boundedJson2(revoked.management, 65536), true, stamp);
142
- writeSetupFile(callersPath, boundedJson2(revoked.callers, REGISTRY_MAX_BYTES), true, callersStamp);
143
- io.stderr.write("revoked administrator " + principal + `
144
- `);
171
+ writeSetupFile(callersPath, boundedJson2(revoked.callers, REGISTRY_MAX_BYTES), true, registryStamp);
172
+ io.stderr.write(formatMessage2(cliMessages2(io.language)["admin.revoked"], { principal }));
145
173
  return 0;
146
174
  } catch (error) {
147
175
  if (error instanceof CliError2 && CLI_ERROR_CODES.includes(error.code)) {
@@ -157,4 +185,4 @@ login at http://127.0.0.1:` + config.management_port + '/ with user "' + princip
157
185
 
158
186
  export { writeAdminFiles2, managementSourceExists2, handleAdminCommand2 };
159
187
 
160
- //# debugId=DF62A5BEF1087BFE64756E2164756E21
188
+ //# debugId=0EB59DEAE24D22AB64756E2164756E21
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-7crhmqn2.js";
5
5
  import {
6
6
  CliError2
7
- } from "./chunk-bjdqj43d.js";
7
+ } from "./chunk-hpzr94hm.js";
8
8
  import {
9
9
  readPrivateBytes
10
10
  } from "./chunk-0y5542k8.js";