@automagik/omni 2.260430.3 → 2.260430.5

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.
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Trust Commands — manage genie host fingerprint registrations.
3
+ *
4
+ * omni trust list List active genie hosts
5
+ * omni trust get <id> Show one host (active or revoked)
6
+ * omni trust update <id> --scope <a,b,c> Replace host scopes wholesale
7
+ * omni trust revoke <id> Soft-delete (stamps revoked_at)
8
+ *
9
+ * Wish: omni-host-fingerprint-trust, Group 1.2. Talks to the
10
+ * `/api/v2/trust/hosts` endpoints landed in Group 1.1 (#556).
11
+ *
12
+ * Why raw fetch instead of the OmniClient SDK: trust types aren't in the
13
+ * OpenAPI spec yet (the SDK is generated from there). Adding them requires
14
+ * a regen + version bump; out of scope for this PR. We use the same
15
+ * baseUrl / apiKey the SDK uses, so behavior is identical.
16
+ */
17
+ import { Command } from 'commander';
18
+ export declare function createTrustCommand(): Command;
19
+ //# sourceMappingURL=trust.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trust.d.ts","sourceRoot":"","sources":["../../src/commands/trust.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAwHpC,wBAAgB,kBAAkB,IAAI,OAAO,CAmB5C"}
package/dist/index.js CHANGED
@@ -113981,7 +113981,7 @@ import { fileURLToPath } from "url";
113981
113981
  // package.json
113982
113982
  var package_default = {
113983
113983
  name: "@automagik/omni",
113984
- version: "2.260430.3",
113984
+ version: "2.260430.5",
113985
113985
  description: "LLM-optimized CLI for Omni",
113986
113986
  type: "module",
113987
113987
  bin: {
@@ -124980,6 +124980,97 @@ function createStopCommand() {
124980
124980
  return new Command("stop").description("Stop all omni PM2 processes").action(runStop);
124981
124981
  }
124982
124982
 
124983
+ // src/commands/trust.ts
124984
+ init_config();
124985
+ init_output();
124986
+ function trustEndpoint(path) {
124987
+ if (!hasAuth()) {
124988
+ error("Not authenticated. Run: omni auth login --api-key <key>", undefined, 2);
124989
+ }
124990
+ const config2 = loadConfig();
124991
+ const baseUrl = (config2.apiUrl ?? "http://localhost:8882").replace(/\/+$/, "");
124992
+ return `${baseUrl}/api/v2/trust${path}`;
124993
+ }
124994
+ function authHeaders() {
124995
+ const config2 = loadConfig();
124996
+ const headers = { "Content-Type": "application/json" };
124997
+ if (config2.apiKey) {
124998
+ headers.Authorization = `Bearer ${config2.apiKey}`;
124999
+ }
125000
+ return headers;
125001
+ }
125002
+ async function callApi(method, path, body) {
125003
+ const res = await fetch(trustEndpoint(path), {
125004
+ method,
125005
+ headers: authHeaders(),
125006
+ body: body === undefined ? undefined : JSON.stringify(body)
125007
+ });
125008
+ if (!res.ok) {
125009
+ const text3 = await res.text().catch(() => "");
125010
+ throw new Error(`HTTP ${res.status} ${res.statusText}${text3 ? `: ${text3}` : ""}`);
125011
+ }
125012
+ return await res.json();
125013
+ }
125014
+ function formatHostRow(host) {
125015
+ return {
125016
+ id: host.id.slice(0, 8),
125017
+ hostname: host.hostname,
125018
+ scopes: host.scopes.join(", ") || "(none)",
125019
+ pubkeyPrefix: `${host.pubkey.slice(0, 12)}\u2026`,
125020
+ lastSeen: host.lastSeenAt ? new Date(host.lastSeenAt).toISOString().replace("T", " ").slice(0, 16) : "never",
125021
+ status: host.revokedAt ? "revoked" : "active"
125022
+ };
125023
+ }
125024
+ async function handleList4() {
125025
+ try {
125026
+ const { items } = await callApi("GET", "/hosts");
125027
+ list(items.map(formatHostRow), {
125028
+ emptyMessage: "No genie hosts registered. Run `genie omni handshake` from a genie installation to register one.",
125029
+ rawData: items
125030
+ });
125031
+ } catch (err2) {
125032
+ error(`Failed to list genie hosts: ${err2 instanceof Error ? err2.message : "Unknown error"}`);
125033
+ }
125034
+ }
125035
+ async function handleGet3(id) {
125036
+ try {
125037
+ const { data: data2 } = await callApi("GET", `/hosts/${encodeURIComponent(id)}`);
125038
+ data(data2);
125039
+ } catch (err2) {
125040
+ error(`Failed to get genie host: ${err2 instanceof Error ? err2.message : "Unknown error"}`);
125041
+ }
125042
+ }
125043
+ async function handleUpdate3(id, options3) {
125044
+ const scopes = options3.scope.split(",").map((s2) => s2.trim()).filter(Boolean);
125045
+ if (scopes.length === 0) {
125046
+ error("--scope must contain at least one scope (use `omni trust revoke <id>` to deny everything).");
125047
+ }
125048
+ try {
125049
+ const { data: data2 } = await callApi("PATCH", `/hosts/${encodeURIComponent(id)}`, { scopes });
125050
+ success(`Updated genie host ${data2.id} scopes: ${data2.scopes.join(", ")}`);
125051
+ data(data2);
125052
+ } catch (err2) {
125053
+ error(`Failed to update genie host: ${err2 instanceof Error ? err2.message : "Unknown error"}`);
125054
+ }
125055
+ }
125056
+ async function handleRevoke2(id) {
125057
+ try {
125058
+ const { data: data2 } = await callApi("DELETE", `/hosts/${encodeURIComponent(id)}`);
125059
+ success(`Revoked genie host ${data2.id} (${data2.hostname}). Tombstone kept for audit.`);
125060
+ data(data2);
125061
+ } catch (err2) {
125062
+ error(`Failed to revoke genie host: ${err2 instanceof Error ? err2.message : "Unknown error"}`);
125063
+ }
125064
+ }
125065
+ function createTrustCommand() {
125066
+ const trust = new Command("trust").description("Manage genie host fingerprint registrations");
125067
+ trust.command("list").description("List active (non-revoked) genie hosts").action(handleList4);
125068
+ trust.command("get <id>").description("Show details for one genie host (active or revoked)").action(handleGet3);
125069
+ trust.command("update <id>").description("Replace a genie host scopes (comma-separated)").requiredOption("--scope <list>", 'Comma-separated scope list, e.g. "agents:write,providers:write"').action(handleUpdate3);
125070
+ trust.command("revoke <id>").description("Revoke a genie host (irreversible \u2014 re-register with a fresh keypair to restore trust)").action(handleRevoke2);
125071
+ return trust;
125072
+ }
125073
+
124983
125074
  // src/commands/tts.ts
124984
125075
  init_output();
124985
125076
  function createTtsCommand() {
@@ -126213,6 +126304,12 @@ var COMMANDS = [
126213
126304
  helpGroup: "Management",
126214
126305
  helpDescription: "API key management"
126215
126306
  },
126307
+ {
126308
+ create: createTrustCommand,
126309
+ category: "advanced",
126310
+ helpGroup: "Management",
126311
+ helpDescription: "Genie host fingerprint trust (omni-host-fingerprint-trust wish)"
126312
+ },
126216
126313
  {
126217
126314
  create: createAccessCommand,
126218
126315
  category: "advanced",
@@ -224556,7 +224556,7 @@ var init_sentry_scrub = __esm(() => {
224556
224556
  var require_package8 = __commonJS((exports, module) => {
224557
224557
  module.exports = {
224558
224558
  name: "@omni/api",
224559
- version: "2.260430.3",
224559
+ version: "2.260430.5",
224560
224560
  type: "module",
224561
224561
  exports: {
224562
224562
  ".": {
@@ -241417,6 +241417,17 @@ var init_scope_enforcer = __esm(() => {
241417
241417
  });
241418
241418
 
241419
241419
  // ../api/src/middleware/require-signed-instance.ts
241420
+ function isUnlockOnlyBody(body) {
241421
+ if (!body || typeof body !== "object" || Array.isArray(body))
241422
+ return false;
241423
+ const obj = body;
241424
+ const keys = Object.keys(obj);
241425
+ if (keys.length !== 1)
241426
+ return false;
241427
+ if (keys[0] !== "requireGenieSignature")
241428
+ return false;
241429
+ return obj.requireGenieSignature === false;
241430
+ }
241420
241431
  async function safeReadJsonBody2(c) {
241421
241432
  const method = c.req.method.toUpperCase();
241422
241433
  if (method === "GET" || method === "DELETE" || method === "HEAD" || method === "OPTIONS")
@@ -241462,6 +241473,13 @@ var init_require_signed_instance = __esm(() => {
241462
241473
  if (signedBy) {
241463
241474
  return next();
241464
241475
  }
241476
+ if (method === "PATCH" && isUnlockOnlyBody(body)) {
241477
+ log61.info("allowing unlock-only PATCH on require_genie_signature instance", {
241478
+ instanceId: instance4.id,
241479
+ apiKeyId: c.get("apiKey")?.id
241480
+ });
241481
+ return next();
241482
+ }
241465
241483
  const apiKey = c.get("apiKey");
241466
241484
  log61.warn("rejecting unsigned request to require_genie_signature instance", {
241467
241485
  instanceId: instance4.id,
@@ -241472,7 +241490,7 @@ var init_require_signed_instance = __esm(() => {
241472
241490
  return c.json({
241473
241491
  error: {
241474
241492
  code: "GENIE_SIGNATURE_REQUIRED",
241475
- message: `Instance ${instance4.id} requires a verified X-Genie-Signature; bearer-only requests are rejected. Sign with \`genie omni handshake\` + per-request signing, or remove the requirement via \`omni instances update ${instance4.id} --no-require-genie-signature\`.`,
241493
+ message: `Instance ${instance4.id} requires a verified X-Genie-Signature; bearer-only requests are rejected. Sign with \`genie omni handshake\` + per-request signing, or unlock with PATCH /api/v2/instances/${instance4.id} body {"requireGenieSignature": false} (always allowed via bearer to prevent operator lockout).`,
241476
241494
  instance: instance4.id
241477
241495
  }
241478
241496
  }, 401);
@@ -281176,6 +281194,21 @@ class GenieHostsService {
281176
281194
  async touchLastSeen(id) {
281177
281195
  await this.db.update(genieHosts).set({ lastSeenAt: new Date, updatedAt: new Date }).where(and2(eq(genieHosts.id, id), isNull2(genieHosts.revokedAt)));
281178
281196
  }
281197
+ async updateScopes(id, scopes) {
281198
+ const [updated] = await this.db.update(genieHosts).set({ scopes, updatedAt: new Date }).where(and2(eq(genieHosts.id, id), isNull2(genieHosts.revokedAt))).returning();
281199
+ if (updated) {
281200
+ log87.info("genie host scopes updated", { hostId: updated.id, scopes });
281201
+ }
281202
+ return updated ?? null;
281203
+ }
281204
+ async revoke(id) {
281205
+ const now = new Date;
281206
+ const [revoked] = await this.db.update(genieHosts).set({ revokedAt: now, updatedAt: now }).where(and2(eq(genieHosts.id, id), isNull2(genieHosts.revokedAt))).returning();
281207
+ if (revoked) {
281208
+ log87.info("genie host revoked", { hostId: revoked.id, hostname: revoked.hostname });
281209
+ }
281210
+ return revoked ?? null;
281211
+ }
281179
281212
  }
281180
281213
  var log87;
281181
281214
  var init_genie_hosts = __esm(() => {
@@ -302631,7 +302664,7 @@ var init_settings3 = __esm(() => {
302631
302664
  });
302632
302665
 
302633
302666
  // ../api/src/routes/v2/trust.ts
302634
- var trustRoutes, PUBKEY_PATTERN, handshakeSchema;
302667
+ var trustRoutes, PUBKEY_PATTERN, handshakeSchema, idParamSchema6, updateScopesSchema;
302635
302668
  var init_trust = __esm(() => {
302636
302669
  init_dist6();
302637
302670
  init_dist2();
@@ -302658,6 +302691,38 @@ var init_trust = __esm(() => {
302658
302691
  const items = await services.genieHosts.listActive();
302659
302692
  return c.json({ items });
302660
302693
  });
302694
+ idParamSchema6 = exports_external.object({ id: exports_external.string().uuid() });
302695
+ trustRoutes.get("/hosts/:id", zValidator("param", idParamSchema6), async (c) => {
302696
+ const { id } = c.req.valid("param");
302697
+ const services = c.get("services");
302698
+ const host = await services.genieHosts.findById(id);
302699
+ if (!host) {
302700
+ return c.json({ error: { code: "NOT_FOUND", message: `genie host ${id} not found` } }, 404);
302701
+ }
302702
+ return c.json({ data: host });
302703
+ });
302704
+ updateScopesSchema = exports_external.object({
302705
+ scopes: exports_external.array(exports_external.string().min(1)).max(64)
302706
+ });
302707
+ trustRoutes.patch("/hosts/:id", zValidator("param", idParamSchema6), zValidator("json", updateScopesSchema), async (c) => {
302708
+ const { id } = c.req.valid("param");
302709
+ const { scopes } = c.req.valid("json");
302710
+ const services = c.get("services");
302711
+ const host = await services.genieHosts.updateScopes(id, scopes);
302712
+ if (!host) {
302713
+ return c.json({ error: { code: "NOT_FOUND", message: `genie host ${id} not found or revoked` } }, 404);
302714
+ }
302715
+ return c.json({ data: host });
302716
+ });
302717
+ trustRoutes.delete("/hosts/:id", zValidator("param", idParamSchema6), async (c) => {
302718
+ const { id } = c.req.valid("param");
302719
+ const services = c.get("services");
302720
+ const host = await services.genieHosts.revoke(id);
302721
+ if (!host) {
302722
+ return c.json({ error: { code: "NOT_FOUND", message: `genie host ${id} not found or already revoked` } }, 404);
302723
+ }
302724
+ return c.json({ data: host });
302725
+ });
302661
302726
  });
302662
302727
 
302663
302728
  // ../api/src/routes/v2/turns.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automagik/omni",
3
- "version": "2.260430.3",
3
+ "version": "2.260430.5",
4
4
  "description": "LLM-optimized CLI for Omni",
5
5
  "type": "module",
6
6
  "bin": {
@@ -50,15 +50,15 @@
50
50
  "qrcode-terminal": "^0.12.0"
51
51
  },
52
52
  "devDependencies": {
53
- "@omni/api": "2.260430.2",
54
- "@omni/channel-discord": "2.260430.2",
55
- "@omni/channel-gupshup": "2.260430.2",
56
- "@omni/channel-sdk": "2.260430.2",
57
- "@omni/channel-slack": "2.260430.2",
58
- "@omni/channel-telegram": "2.260430.2",
59
- "@omni/channel-whatsapp": "2.260430.2",
60
- "@omni/core": "2.260430.2",
61
- "@omni/sdk": "2.260430.2",
53
+ "@omni/api": "2.260430.4",
54
+ "@omni/channel-discord": "2.260430.4",
55
+ "@omni/channel-gupshup": "2.260430.4",
56
+ "@omni/channel-sdk": "2.260430.4",
57
+ "@omni/channel-slack": "2.260430.4",
58
+ "@omni/channel-telegram": "2.260430.4",
59
+ "@omni/channel-whatsapp": "2.260430.4",
60
+ "@omni/core": "2.260430.4",
61
+ "@omni/sdk": "2.260430.4",
62
62
  "@types/node": "^22.10.3",
63
63
  "@types/qrcode-terminal": "^0.12.2",
64
64
  "typescript": "^5.7.3"