@automagik/omni 2.260718.4 → 2.260718.6

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.
package/dist/index.js CHANGED
@@ -73282,6 +73282,38 @@ class ApiKeyService {
73282
73282
  if (options.name !== undefined && await this.isPrimaryKey(id)) {
73283
73283
  throw new Error("Cannot rename primary API key");
73284
73284
  }
73285
+ const updates = this.buildUpdateValues(options);
73286
+ const [updated] = await this.db.update(apiKeys).set(updates).where(eq2(apiKeys.id, id)).returning();
73287
+ if (updated) {
73288
+ await apiKeyCache.delete(CacheKeys.apiKey(updated.keyHash));
73289
+ log20.info("API key updated", { id, fields: Object.keys(options) });
73290
+ }
73291
+ return updated ?? null;
73292
+ }
73293
+ async updateWithAuthorityGuard(id, options, guard) {
73294
+ const result = await this.db.transaction(async (tx2) => {
73295
+ const [current] = await tx2.select().from(apiKeys).where(eq2(apiKeys.id, id)).limit(1).for("update");
73296
+ if (!current)
73297
+ return { status: "not_found" };
73298
+ if (options.name !== undefined && current.name === PRIMARY_KEY_NAME) {
73299
+ throw new Error("Cannot rename primary API key");
73300
+ }
73301
+ const updates = this.buildUpdateValues(options);
73302
+ const next = { ...current, ...updates };
73303
+ if (!await guard(current, next))
73304
+ return { status: "denied" };
73305
+ const [updated] = await tx2.update(apiKeys).set(updates).where(eq2(apiKeys.id, id)).returning();
73306
+ if (!updated)
73307
+ return { status: "not_found" };
73308
+ return { status: "updated", key: updated };
73309
+ });
73310
+ if (result.status === "updated") {
73311
+ await apiKeyCache.delete(CacheKeys.apiKey(result.key.keyHash));
73312
+ log20.info("API key updated", { id, fields: Object.keys(options) });
73313
+ }
73314
+ return result;
73315
+ }
73316
+ buildUpdateValues(options) {
73285
73317
  const updates = { updatedAt: new Date };
73286
73318
  if (options.name !== undefined)
73287
73319
  updates.name = options.name;
@@ -73304,12 +73336,7 @@ class ApiKeyService {
73304
73336
  if (options.contextInstanceId !== undefined || options.contextChatId !== undefined || options.contextMessageId !== undefined) {
73305
73337
  updates.contextUpdatedAt = new Date;
73306
73338
  }
73307
- const [updated] = await this.db.update(apiKeys).set(updates).where(eq2(apiKeys.id, id)).returning();
73308
- if (updated) {
73309
- await apiKeyCache.delete(CacheKeys.apiKey(updated.keyHash));
73310
- log20.info("API key updated", { id, fields: Object.keys(options) });
73311
- }
73312
- return updated ?? null;
73339
+ return updates;
73313
73340
  }
73314
73341
  async list() {
73315
73342
  return this.db.select().from(apiKeys).orderBy(apiKeys.createdAt);
@@ -125164,7 +125191,7 @@ import { fileURLToPath } from "url";
125164
125191
  // package.json
125165
125192
  var package_default = {
125166
125193
  name: "@automagik/omni",
125167
- version: "2.260718.4",
125194
+ version: "2.260718.6",
125168
125195
  description: "LLM-optimized CLI for Omni",
125169
125196
  type: "module",
125170
125197
  bin: {
@@ -227026,7 +227026,7 @@ var init_sentry_scrub = __esm(() => {
227026
227026
  var require_package7 = __commonJS((exports, module) => {
227027
227027
  module.exports = {
227028
227028
  name: "@omni/api",
227029
- version: "2.260718.4",
227029
+ version: "2.260718.6",
227030
227030
  type: "module",
227031
227031
  exports: {
227032
227032
  ".": {
@@ -285713,6 +285713,38 @@ class ApiKeyService {
285713
285713
  if (options.name !== undefined && await this.isPrimaryKey(id)) {
285714
285714
  throw new Error("Cannot rename primary API key");
285715
285715
  }
285716
+ const updates = this.buildUpdateValues(options);
285717
+ const [updated] = await this.db.update(apiKeys).set(updates).where(eq(apiKeys.id, id)).returning();
285718
+ if (updated) {
285719
+ await apiKeyCache.delete(CacheKeys.apiKey(updated.keyHash));
285720
+ log58.info("API key updated", { id, fields: Object.keys(options) });
285721
+ }
285722
+ return updated ?? null;
285723
+ }
285724
+ async updateWithAuthorityGuard(id, options, guard) {
285725
+ const result = await this.db.transaction(async (tx) => {
285726
+ const [current] = await tx.select().from(apiKeys).where(eq(apiKeys.id, id)).limit(1).for("update");
285727
+ if (!current)
285728
+ return { status: "not_found" };
285729
+ if (options.name !== undefined && current.name === PRIMARY_KEY_NAME) {
285730
+ throw new Error("Cannot rename primary API key");
285731
+ }
285732
+ const updates = this.buildUpdateValues(options);
285733
+ const next = { ...current, ...updates };
285734
+ if (!await guard(current, next))
285735
+ return { status: "denied" };
285736
+ const [updated] = await tx.update(apiKeys).set(updates).where(eq(apiKeys.id, id)).returning();
285737
+ if (!updated)
285738
+ return { status: "not_found" };
285739
+ return { status: "updated", key: updated };
285740
+ });
285741
+ if (result.status === "updated") {
285742
+ await apiKeyCache.delete(CacheKeys.apiKey(result.key.keyHash));
285743
+ log58.info("API key updated", { id, fields: Object.keys(options) });
285744
+ }
285745
+ return result;
285746
+ }
285747
+ buildUpdateValues(options) {
285716
285748
  const updates = { updatedAt: new Date };
285717
285749
  if (options.name !== undefined)
285718
285750
  updates.name = options.name;
@@ -285735,12 +285767,7 @@ class ApiKeyService {
285735
285767
  if (options.contextInstanceId !== undefined || options.contextChatId !== undefined || options.contextMessageId !== undefined) {
285736
285768
  updates.contextUpdatedAt = new Date;
285737
285769
  }
285738
- const [updated] = await this.db.update(apiKeys).set(updates).where(eq(apiKeys.id, id)).returning();
285739
- if (updated) {
285740
- await apiKeyCache.delete(CacheKeys.apiKey(updated.keyHash));
285741
- log58.info("API key updated", { id, fields: Object.keys(options) });
285742
- }
285743
- return updated ?? null;
285770
+ return updates;
285744
285771
  }
285745
285772
  async list() {
285746
285773
  return this.db.select().from(apiKeys).orderBy(apiKeys.createdAt);
@@ -357988,11 +358015,62 @@ function enforceScopeCeiling(c, requestedScopes) {
357988
358015
  }
357989
358016
  }, 403);
357990
358017
  }
357991
- function enforceInstanceCeiling(c, requestedInstanceIds) {
357992
- const apiKey = c.get("apiKey");
357993
- if (apiKey?.instanceIds === null)
358018
+ function isStringArray(value) {
358019
+ return Array.isArray(value) && value.every((item) => typeof item === "string");
358020
+ }
358021
+ function normalizeInstanceIds(values2) {
358022
+ return new Set(values2.map((value) => value.toLowerCase()));
358023
+ }
358024
+ function intersectAuthorities(left, right) {
358025
+ if (left === null)
358026
+ return right;
358027
+ if (right === null)
358028
+ return left;
358029
+ return new Set([...left].filter((instanceId) => right.has(instanceId)));
358030
+ }
358031
+ function deriveInstanceAuthorities(input) {
358032
+ if (input.instanceIds !== null && !isStringArray(input.instanceIds))
358033
+ return null;
358034
+ if (input.instanceAllowlist !== undefined && !isStringArray(input.instanceAllowlist))
358035
+ return null;
358036
+ if (input.profile !== undefined && input.profile !== null && (typeof input.profile !== "string" || !KNOWN_API_KEY_PROFILES.has(input.profile))) {
357994
358037
  return null;
357995
- const exceedsCaller = !apiKey || !Array.isArray(apiKey.instanceIds) || requestedInstanceIds === null || requestedInstanceIds.some((instanceId) => !apiKey.instanceIds?.includes(instanceId));
358038
+ }
358039
+ const legacy = input.instanceIds === null ? null : normalizeInstanceIds(input.instanceIds);
358040
+ const legacyEmptyInactive = input.instanceIds === null || input.instanceIds.length === 0 ? null : legacy;
358041
+ const instanceAllowlist = input.instanceAllowlist ?? [];
358042
+ const profileAware = isLockActive(input.profile, "instanceAllowlist", instanceAllowlist) ? normalizeInstanceIds(instanceAllowlist) : null;
358043
+ return {
358044
+ legacy,
358045
+ legacyEmptyInactive,
358046
+ effective: intersectAuthorities(legacy, profileAware),
358047
+ profileAware
358048
+ };
358049
+ }
358050
+ function isAuthoritySubset(requested, allowed) {
358051
+ if (allowed === null)
358052
+ return true;
358053
+ if (requested === null)
358054
+ return false;
358055
+ return [...requested].every((instanceId) => allowed.has(instanceId));
358056
+ }
358057
+ function enforceInstanceCeiling(c, requestedAuthority) {
358058
+ const apiKey = c.get("apiKey");
358059
+ if (!apiKey) {
358060
+ return c.json({
358061
+ error: {
358062
+ code: "FORBIDDEN",
358063
+ message: "Cannot grant instance access that exceeds the caller's own."
358064
+ }
358065
+ }, 403);
358066
+ }
358067
+ const callerAuthorities = deriveInstanceAuthorities({
358068
+ instanceIds: apiKey.instanceIds,
358069
+ profile: apiKey.profile,
358070
+ instanceAllowlist: apiKey.instanceAllowlist
358071
+ });
358072
+ const childAuthorities = deriveInstanceAuthorities(requestedAuthority);
358073
+ const exceedsCaller = callerAuthorities === null || childAuthorities === null || !isAuthoritySubset(childAuthorities.legacy, callerAuthorities.legacy) || !isAuthoritySubset(childAuthorities.legacyEmptyInactive, callerAuthorities.legacyEmptyInactive) || !isAuthoritySubset(childAuthorities.profileAware, callerAuthorities.profileAware) || !isAuthoritySubset(childAuthorities.effective, callerAuthorities.effective);
357996
358074
  if (!exceedsCaller)
357997
358075
  return null;
357998
358076
  return c.json({
@@ -358016,7 +358094,11 @@ async function handleProfileCreate(c, data, services) {
358016
358094
  const ceilingDenied = enforceScopeCeiling(c, resolved.scopes);
358017
358095
  if (ceilingDenied)
358018
358096
  return ceilingDenied;
358019
- const instanceCeilingDenied = enforceInstanceCeiling(c, data.instanceIds ?? null);
358097
+ const instanceCeilingDenied = enforceInstanceCeiling(c, {
358098
+ instanceIds: data.instanceIds ?? null,
358099
+ profile: resolved.profile,
358100
+ instanceAllowlist: resolved.instanceAllowlist
358101
+ });
358020
358102
  if (instanceCeilingDenied)
358021
358103
  return instanceCeilingDenied;
358022
358104
  const result = await services.apiKeys.create({
@@ -358054,7 +358136,11 @@ async function handleLegacyCreate(c, data, services) {
358054
358136
  const ceilingDenied = enforceScopeCeiling(c, data.scopes);
358055
358137
  if (ceilingDenied)
358056
358138
  return ceilingDenied;
358057
- const instanceCeilingDenied = enforceInstanceCeiling(c, data.instanceIds ?? null);
358139
+ const instanceCeilingDenied = enforceInstanceCeiling(c, {
358140
+ instanceIds: data.instanceIds ?? null,
358141
+ profile: null,
358142
+ instanceAllowlist: []
358143
+ });
358058
358144
  if (instanceCeilingDenied)
358059
358145
  return instanceCeilingDenied;
358060
358146
  const result = await services.apiKeys.create({
@@ -358068,13 +358154,14 @@ async function handleLegacyCreate(c, data, services) {
358068
358154
  });
358069
358155
  return c.json({ data: { ...result.key, plainTextKey: result.plainTextKey } }, 201);
358070
358156
  }
358071
- var keysRoutes, NON_ADMIN_PROFILES, profileOverridesSchema, createKeySchema, updateKeySchema, revokeKeySchema, listQuerySchema13, auditQuerySchema;
358157
+ var keysRoutes, NON_ADMIN_PROFILES, KNOWN_API_KEY_PROFILES, profileOverridesSchema, createKeySchema, updateKeySchema, revokeKeySchema, listQuerySchema13, auditQuerySchema;
358072
358158
  var init_keys = __esm(() => {
358073
358159
  init_dist6();
358074
358160
  init_dist2();
358075
358161
  init_zod();
358076
358162
  init_resolve_profile();
358077
358163
  init_signed_host_scope_context();
358164
+ init_scope_enforcer();
358078
358165
  init_date_query();
358079
358166
  init_api_keys();
358080
358167
  keysRoutes = new Hono2;
@@ -358087,6 +358174,7 @@ var init_keys = __esm(() => {
358087
358174
  "console-operator",
358088
358175
  "console-admin"
358089
358176
  ];
358177
+ KNOWN_API_KEY_PROFILES = new Set(["admin", ...NON_ADMIN_PROFILES]);
358090
358178
  profileOverridesSchema = exports_external.object({
358091
358179
  add: exports_external.array(exports_external.string()).optional(),
358092
358180
  remove: exports_external.array(exports_external.string()).optional(),
@@ -358179,21 +358267,33 @@ var init_keys = __esm(() => {
358179
358267
  const id = c.req.param("id");
358180
358268
  const data = c.req.valid("json");
358181
358269
  const services = c.get("services");
358182
- if (data.scopes) {
358183
- const ceilingDenied = enforceScopeCeiling(c, data.scopes);
358184
- if (ceilingDenied)
358185
- return ceilingDenied;
358186
- }
358187
- if (data.instanceIds !== undefined) {
358188
- const instanceCeilingDenied = enforceInstanceCeiling(c, data.instanceIds);
358189
- if (instanceCeilingDenied)
358190
- return instanceCeilingDenied;
358191
- }
358270
+ const updateOptions = {
358271
+ ...data,
358272
+ expiresAt: data.expiresAt === null ? null : data.expiresAt ? new Date(data.expiresAt) : undefined
358273
+ };
358192
358274
  try {
358193
- const updated = await services.apiKeys.update(id, {
358194
- ...data,
358195
- expiresAt: data.expiresAt === null ? null : data.expiresAt ? new Date(data.expiresAt) : undefined
358196
- });
358275
+ if (data.instanceIds !== undefined || data.scopes !== undefined) {
358276
+ let authorityDenied;
358277
+ const result = await services.apiKeys.updateWithAuthorityGuard(id, updateOptions, (_current, next) => {
358278
+ authorityDenied = enforceScopeCeiling(c, next.scopes);
358279
+ if (authorityDenied)
358280
+ return false;
358281
+ authorityDenied = enforceInstanceCeiling(c, {
358282
+ instanceIds: next.instanceIds,
358283
+ profile: next.profile,
358284
+ instanceAllowlist: next.instanceAllowlist
358285
+ });
358286
+ return authorityDenied == null;
358287
+ });
358288
+ if (result.status === "denied") {
358289
+ return authorityDenied ?? c.json({ error: { code: "FORBIDDEN", message: "API key authority exceeds caller authority" } }, 403);
358290
+ }
358291
+ if (result.status === "not_found") {
358292
+ return c.json({ error: { code: "NOT_FOUND", message: "API key not found" } }, 404);
358293
+ }
358294
+ return c.json({ data: result.key });
358295
+ }
358296
+ const updated = await services.apiKeys.update(id, updateOptions);
358197
358297
  if (!updated) {
358198
358298
  return c.json({ error: { code: "NOT_FOUND", message: "API key not found" } }, 404);
358199
358299
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automagik/omni",
3
- "version": "2.260718.4",
3
+ "version": "2.260718.6",
4
4
  "description": "LLM-optimized CLI for Omni",
5
5
  "type": "module",
6
6
  "bin": {