@objectstack/runtime 1.0.11 → 1.0.12

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
@@ -237,6 +237,7 @@ var RestServer = class {
237
237
  apiPath: api.apiPath,
238
238
  enableCrud: api.enableCrud ?? true,
239
239
  enableMetadata: api.enableMetadata ?? true,
240
+ enableUi: api.enableUi ?? true,
240
241
  enableBatch: api.enableBatch ?? true,
241
242
  enableDiscovery: api.enableDiscovery ?? true,
242
243
  documentation: api.documentation,
@@ -302,6 +303,9 @@ var RestServer = class {
302
303
  if (this.config.api.enableMetadata) {
303
304
  this.registerMetadataEndpoints(basePath);
304
305
  }
306
+ if (this.config.api.enableUi) {
307
+ this.registerUiEndpoints(basePath);
308
+ }
305
309
  if (this.config.api.enableCrud) {
306
310
  this.registerCrudEndpoints(basePath);
307
311
  }
@@ -327,6 +331,9 @@ var RestServer = class {
327
331
  if (this.config.api.enableMetadata) {
328
332
  discovery.endpoints.metadata = `${basePath}${this.config.metadata.prefix}`;
329
333
  }
334
+ if (this.config.api.enableUi) {
335
+ discovery.endpoints.ui = `${basePath}/ui`;
336
+ }
330
337
  if (discovery.endpoints.auth) {
331
338
  discovery.endpoints.auth = `${basePath}/auth`;
332
339
  }
@@ -372,7 +379,8 @@ var RestServer = class {
372
379
  path: `${metaPath}/:type`,
373
380
  handler: async (req, res) => {
374
381
  try {
375
- const items = await this.protocol.getMetaItems({ type: req.params.type });
382
+ const packageId = req.query?.package || void 0;
383
+ const items = await this.protocol.getMetaItems({ type: req.params.type, packageId });
376
384
  res.json(items);
377
385
  } catch (error) {
378
386
  res.status(404).json({ error: error.message });
@@ -456,6 +464,35 @@ var RestServer = class {
456
464
  }
457
465
  });
458
466
  }
467
+ /**
468
+ * Register UI endpoints
469
+ */
470
+ registerUiEndpoints(basePath) {
471
+ const uiPath = `${basePath}/ui`;
472
+ this.routeManager.register({
473
+ method: "GET",
474
+ path: `${uiPath}/view/:object/:type`,
475
+ handler: async (req, res) => {
476
+ try {
477
+ if (this.protocol.getUiView) {
478
+ const view = await this.protocol.getUiView({
479
+ object: req.params.object,
480
+ type: req.params.type
481
+ });
482
+ res.json(view);
483
+ } else {
484
+ res.status(501).json({ error: "UI View resolution not supported by protocol implementation" });
485
+ }
486
+ } catch (error) {
487
+ res.status(404).json({ error: error.message });
488
+ }
489
+ },
490
+ metadata: {
491
+ summary: "Resolve UI View for object",
492
+ tags: ["ui"]
493
+ }
494
+ });
495
+ }
459
496
  /**
460
497
  * Register CRUD endpoints for data operations
461
498
  */
@@ -844,17 +881,31 @@ var AppPlugin = class {
844
881
  } else {
845
882
  ctx.logger.debug("No runtime.onEnable function found", { appId });
846
883
  }
884
+ const seedDatasets = [];
885
+ if (Array.isArray(this.bundle.data)) {
886
+ seedDatasets.push(...this.bundle.data);
887
+ }
847
888
  const manifest = this.bundle.manifest || this.bundle;
848
889
  if (manifest && Array.isArray(manifest.data)) {
849
- ctx.logger.info(`[AppPlugin] Found initial data for ${appId}`, { count: manifest.data.length });
850
- for (const dataset of manifest.data) {
890
+ seedDatasets.push(...manifest.data);
891
+ }
892
+ const namespace = (this.bundle.manifest || this.bundle)?.namespace;
893
+ const RESERVED_NS = /* @__PURE__ */ new Set(["base", "system"]);
894
+ const toFQN = (name) => {
895
+ if (name.includes("__") || !namespace || RESERVED_NS.has(namespace)) return name;
896
+ return `${namespace}__${name}`;
897
+ };
898
+ if (seedDatasets.length > 0) {
899
+ ctx.logger.info(`[AppPlugin] Found ${seedDatasets.length} seed datasets for ${appId}`);
900
+ for (const dataset of seedDatasets) {
851
901
  if (dataset.object && Array.isArray(dataset.records)) {
852
- ctx.logger.info(`[Seeder] Seeding ${dataset.records.length} records for ${dataset.object}`);
902
+ const objectFQN = toFQN(dataset.object);
903
+ ctx.logger.info(`[Seeder] Seeding ${dataset.records.length} records for ${objectFQN}`);
853
904
  for (const record of dataset.records) {
854
905
  try {
855
- await ql.insert(dataset.object, record);
906
+ await ql.insert(objectFQN, record);
856
907
  } catch (err) {
857
- ctx.logger.warn(`[Seeder] Failed to insert ${dataset.object} record:`, { error: err.message });
908
+ ctx.logger.warn(`[Seeder] Failed to insert ${objectFQN} record:`, { error: err.message });
858
909
  }
859
910
  }
860
911
  }
@@ -1015,19 +1066,25 @@ var HttpDispatcher = class {
1015
1066
  const hasFiles = !!(services[CoreServiceName.enum["file-storage"]] || services["storage"]?.supportsFiles);
1016
1067
  const hasAnalytics = !!services[CoreServiceName.enum.analytics];
1017
1068
  const hasHub = !!services[CoreServiceName.enum.hub];
1069
+ const routes = {
1070
+ data: `${prefix}/data`,
1071
+ metadata: `${prefix}/meta`,
1072
+ packages: `${prefix}/packages`,
1073
+ auth: `${prefix}/auth`,
1074
+ ui: `${prefix}/ui`,
1075
+ graphql: hasGraphQL ? `${prefix}/graphql` : void 0,
1076
+ storage: hasFiles ? `${prefix}/storage` : void 0,
1077
+ analytics: hasAnalytics ? `${prefix}/analytics` : void 0,
1078
+ hub: hasHub ? `${prefix}/hub` : void 0,
1079
+ automation: `${prefix}/automation`
1080
+ };
1018
1081
  return {
1019
1082
  name: "ObjectOS",
1020
1083
  version: "1.0.0",
1021
1084
  environment: getEnv("NODE_ENV", "development"),
1022
- routes: {
1023
- data: `${prefix}/data`,
1024
- metadata: `${prefix}/meta`,
1025
- auth: `${prefix}/auth`,
1026
- graphql: hasGraphQL ? `${prefix}/graphql` : void 0,
1027
- storage: hasFiles ? `${prefix}/storage` : void 0,
1028
- analytics: hasAnalytics ? `${prefix}/analytics` : void 0,
1029
- hub: hasHub ? `${prefix}/hub` : void 0
1030
- },
1085
+ routes,
1086
+ endpoints: routes,
1087
+ // Alias for backward compatibility with some clients
1031
1088
  features: {
1032
1089
  graphql: hasGraphQL,
1033
1090
  search: hasSearch,
@@ -1080,11 +1137,21 @@ var HttpDispatcher = class {
1080
1137
  * Standard: /metadata/:type/:name
1081
1138
  * Fallback for backward compat: /metadata (all objects), /metadata/:objectName (get object)
1082
1139
  */
1083
- async handleMetadata(path, context, method, body) {
1140
+ async handleMetadata(path, context, method, body, query) {
1084
1141
  const broker = this.ensureBroker();
1085
1142
  const parts = path.replace(/^\/+/, "").split("/").filter(Boolean);
1086
1143
  if (parts[0] === "types") {
1087
- return { handled: true, response: this.success({ types: ["objects", "apps", "plugins"] }) };
1144
+ const protocol = this.kernel?.context?.getService ? this.kernel.context.getService("protocol") : null;
1145
+ if (protocol && typeof protocol.getMetaTypes === "function") {
1146
+ const result = await protocol.getMetaTypes({});
1147
+ return { handled: true, response: this.success(result) };
1148
+ }
1149
+ try {
1150
+ const data = await broker.call("metadata.types", {}, { request: context.request });
1151
+ return { handled: true, response: this.success(data) };
1152
+ } catch {
1153
+ return { handled: true, response: this.success({ types: ["object", "app", "plugin"] }) };
1154
+ }
1088
1155
  }
1089
1156
  if (parts.length === 2) {
1090
1157
  const [type, name] = parts;
@@ -1106,11 +1173,21 @@ var HttpDispatcher = class {
1106
1173
  }
1107
1174
  }
1108
1175
  try {
1109
- if (type === "objects") {
1176
+ if (type === "objects" || type === "object") {
1110
1177
  const data2 = await broker.call("metadata.getObject", { objectName: name }, { request: context.request });
1111
1178
  return { handled: true, response: this.success(data2) };
1112
1179
  }
1113
- const data = await broker.call(`metadata.get${this.capitalize(type.slice(0, -1))}`, { name }, { request: context.request });
1180
+ const singularType = type.endsWith("s") ? type.slice(0, -1) : type;
1181
+ const protocol = this.kernel?.context?.getService ? this.kernel.context.getService("protocol") : null;
1182
+ if (protocol && typeof protocol.getMetaItem === "function") {
1183
+ try {
1184
+ const data2 = await protocol.getMetaItem({ type: singularType, name });
1185
+ return { handled: true, response: this.success(data2) };
1186
+ } catch (e) {
1187
+ }
1188
+ }
1189
+ const method2 = `metadata.get${this.capitalize(singularType)}`;
1190
+ const data = await broker.call(method2, { name }, { request: context.request });
1114
1191
  return { handled: true, response: this.success(data) };
1115
1192
  } catch (e) {
1116
1193
  return { handled: true, response: this.error(e.message, 404) };
@@ -1118,20 +1195,47 @@ var HttpDispatcher = class {
1118
1195
  }
1119
1196
  if (parts.length === 1) {
1120
1197
  const typeOrName = parts[0];
1121
- if (["objects", "apps", "plugins"].includes(typeOrName)) {
1198
+ const packageId = query?.package || void 0;
1199
+ const protocol = this.kernel?.context?.getService ? this.kernel.context.getService("protocol") : null;
1200
+ if (protocol && typeof protocol.getMetaItems === "function") {
1201
+ try {
1202
+ const data = await protocol.getMetaItems({ type: typeOrName, packageId });
1203
+ if (data && (data.items !== void 0 || Array.isArray(data))) {
1204
+ return { handled: true, response: this.success(data) };
1205
+ }
1206
+ } catch {
1207
+ }
1208
+ }
1209
+ try {
1122
1210
  if (typeOrName === "objects") {
1123
- const data3 = await broker.call("metadata.objects", {}, { request: context.request });
1124
- return { handled: true, response: this.success(data3) };
1211
+ const data2 = await broker.call("metadata.objects", { packageId }, { request: context.request });
1212
+ return { handled: true, response: this.success(data2) };
1125
1213
  }
1126
- const data2 = await broker.call(`metadata.${typeOrName}`, {}, { request: context.request });
1127
- return { handled: true, response: this.success(data2) };
1214
+ const data = await broker.call(`metadata.${typeOrName}`, { packageId }, { request: context.request });
1215
+ if (data !== null && data !== void 0) {
1216
+ return { handled: true, response: this.success(data) };
1217
+ }
1218
+ } catch {
1219
+ }
1220
+ try {
1221
+ const data = await broker.call("metadata.getObject", { objectName: typeOrName }, { request: context.request });
1222
+ return { handled: true, response: this.success(data) };
1223
+ } catch (e) {
1224
+ return { handled: true, response: this.error(e.message, 404) };
1128
1225
  }
1129
- const data = await broker.call("metadata.getObject", { objectName: typeOrName }, { request: context.request });
1130
- return { handled: true, response: this.success(data) };
1131
1226
  }
1132
1227
  if (parts.length === 0) {
1133
- const data = await broker.call("metadata.objects", {}, { request: context.request });
1134
- return { handled: true, response: this.success(data) };
1228
+ const protocol = this.kernel?.context?.getService ? this.kernel.context.getService("protocol") : null;
1229
+ if (protocol && typeof protocol.getMetaTypes === "function") {
1230
+ const result = await protocol.getMetaTypes({});
1231
+ return { handled: true, response: this.success(result) };
1232
+ }
1233
+ try {
1234
+ const data = await broker.call("metadata.types", {}, { request: context.request });
1235
+ return { handled: true, response: this.success(data) };
1236
+ } catch {
1237
+ return { handled: true, response: this.success({ types: ["object", "app", "plugin"] }) };
1238
+ }
1135
1239
  }
1136
1240
  return { handled: false };
1137
1241
  }
@@ -1151,7 +1255,7 @@ var HttpDispatcher = class {
1151
1255
  const action = parts[1];
1152
1256
  if (action === "query" && m === "POST") {
1153
1257
  const result = await broker.call("data.query", { object: objectName, ...body }, { request: context.request });
1154
- return { handled: true, response: this.success(result.data, { count: result.count, limit: body.limit, skip: body.skip }) };
1258
+ return { handled: true, response: this.success(result) };
1155
1259
  }
1156
1260
  if (action === "batch" && m === "POST") {
1157
1261
  const result = await broker.call("data.batch", { object: objectName, ...body }, { request: context.request });
@@ -1159,27 +1263,27 @@ var HttpDispatcher = class {
1159
1263
  }
1160
1264
  if (parts.length === 2 && m === "GET") {
1161
1265
  const id = parts[1];
1162
- const data = await broker.call("data.get", { object: objectName, id, ...query }, { request: context.request });
1163
- return { handled: true, response: this.success(data) };
1266
+ const result = await broker.call("data.get", { object: objectName, id, ...query }, { request: context.request });
1267
+ return { handled: true, response: this.success(result) };
1164
1268
  }
1165
1269
  if (parts.length === 2 && m === "PATCH") {
1166
1270
  const id = parts[1];
1167
- const data = await broker.call("data.update", { object: objectName, id, data: body }, { request: context.request });
1168
- return { handled: true, response: this.success(data) };
1271
+ const result = await broker.call("data.update", { object: objectName, id, data: body }, { request: context.request });
1272
+ return { handled: true, response: this.success(result) };
1169
1273
  }
1170
1274
  if (parts.length === 2 && m === "DELETE") {
1171
1275
  const id = parts[1];
1172
- await broker.call("data.delete", { object: objectName, id }, { request: context.request });
1173
- return { handled: true, response: this.success({ id, deleted: true }) };
1276
+ const result = await broker.call("data.delete", { object: objectName, id }, { request: context.request });
1277
+ return { handled: true, response: this.success(result) };
1174
1278
  }
1175
1279
  } else {
1176
1280
  if (m === "GET") {
1177
1281
  const result = await broker.call("data.query", { object: objectName, filters: query }, { request: context.request });
1178
- return { handled: true, response: this.success(result.data, { count: result.count }) };
1282
+ return { handled: true, response: this.success(result) };
1179
1283
  }
1180
1284
  if (m === "POST") {
1181
- const data = await broker.call("data.create", { object: objectName, data: body }, { request: context.request });
1182
- const res = this.success(data);
1285
+ const result = await broker.call("data.create", { object: objectName, data: body }, { request: context.request });
1286
+ const res = this.success(result);
1183
1287
  res.status = 201;
1184
1288
  return { handled: true, response: res };
1185
1289
  }
@@ -1209,6 +1313,118 @@ var HttpDispatcher = class {
1209
1313
  }
1210
1314
  return { handled: false };
1211
1315
  }
1316
+ /**
1317
+ * Handles Package Management requests
1318
+ *
1319
+ * REST Endpoints:
1320
+ * - GET /packages → list all installed packages
1321
+ * - GET /packages/:id → get a specific package
1322
+ * - POST /packages → install a new package
1323
+ * - DELETE /packages/:id → uninstall a package
1324
+ * - PATCH /packages/:id/enable → enable a package
1325
+ * - PATCH /packages/:id/disable → disable a package
1326
+ *
1327
+ * Uses ObjectQL SchemaRegistry directly (via the 'objectql' service)
1328
+ * with broker fallback for backward compatibility.
1329
+ */
1330
+ async handlePackages(path, method, body, query, context) {
1331
+ const m = method.toUpperCase();
1332
+ const parts = path.replace(/^\/+/, "").split("/").filter(Boolean);
1333
+ const qlService = this.getObjectQLService();
1334
+ const registry = qlService?.registry;
1335
+ if (!registry) {
1336
+ if (this.kernel.broker) {
1337
+ return this.handlePackagesViaBroker(parts, m, body, query, context);
1338
+ }
1339
+ return { handled: true, response: this.error("Package service not available", 503) };
1340
+ }
1341
+ try {
1342
+ if (parts.length === 0 && m === "GET") {
1343
+ let packages = registry.getAllPackages();
1344
+ if (query?.status) {
1345
+ packages = packages.filter((p) => p.status === query.status);
1346
+ }
1347
+ if (query?.type) {
1348
+ packages = packages.filter((p) => p.manifest?.type === query.type);
1349
+ }
1350
+ return { handled: true, response: this.success({ packages, total: packages.length }) };
1351
+ }
1352
+ if (parts.length === 0 && m === "POST") {
1353
+ const pkg = registry.installPackage(body.manifest || body, body.settings);
1354
+ const res = this.success(pkg);
1355
+ res.status = 201;
1356
+ return { handled: true, response: res };
1357
+ }
1358
+ if (parts.length === 2 && parts[1] === "enable" && m === "PATCH") {
1359
+ const id = decodeURIComponent(parts[0]);
1360
+ const pkg = registry.enablePackage(id);
1361
+ if (!pkg) return { handled: true, response: this.error(`Package '${id}' not found`, 404) };
1362
+ return { handled: true, response: this.success(pkg) };
1363
+ }
1364
+ if (parts.length === 2 && parts[1] === "disable" && m === "PATCH") {
1365
+ const id = decodeURIComponent(parts[0]);
1366
+ const pkg = registry.disablePackage(id);
1367
+ if (!pkg) return { handled: true, response: this.error(`Package '${id}' not found`, 404) };
1368
+ return { handled: true, response: this.success(pkg) };
1369
+ }
1370
+ if (parts.length === 1 && m === "GET") {
1371
+ const id = decodeURIComponent(parts[0]);
1372
+ const pkg = registry.getPackage(id);
1373
+ if (!pkg) return { handled: true, response: this.error(`Package '${id}' not found`, 404) };
1374
+ return { handled: true, response: this.success(pkg) };
1375
+ }
1376
+ if (parts.length === 1 && m === "DELETE") {
1377
+ const id = decodeURIComponent(parts[0]);
1378
+ const success = registry.uninstallPackage(id);
1379
+ if (!success) return { handled: true, response: this.error(`Package '${id}' not found`, 404) };
1380
+ return { handled: true, response: this.success({ success: true }) };
1381
+ }
1382
+ } catch (e) {
1383
+ return { handled: true, response: this.error(e.message, e.statusCode || 500) };
1384
+ }
1385
+ return { handled: false };
1386
+ }
1387
+ /**
1388
+ * Fallback: handle packages via broker (for backward compatibility)
1389
+ */
1390
+ async handlePackagesViaBroker(parts, m, body, query, context) {
1391
+ const broker = this.kernel.broker;
1392
+ try {
1393
+ if (parts.length === 0 && m === "GET") {
1394
+ const result = await broker.call("package.list", query || {}, { request: context.request });
1395
+ return { handled: true, response: this.success(result) };
1396
+ }
1397
+ if (parts.length === 0 && m === "POST") {
1398
+ const result = await broker.call("package.install", body, { request: context.request });
1399
+ const res = this.success(result);
1400
+ res.status = 201;
1401
+ return { handled: true, response: res };
1402
+ }
1403
+ if (parts.length === 2 && parts[1] === "enable" && m === "PATCH") {
1404
+ const id = decodeURIComponent(parts[0]);
1405
+ const result = await broker.call("package.enable", { id }, { request: context.request });
1406
+ return { handled: true, response: this.success(result) };
1407
+ }
1408
+ if (parts.length === 2 && parts[1] === "disable" && m === "PATCH") {
1409
+ const id = decodeURIComponent(parts[0]);
1410
+ const result = await broker.call("package.disable", { id }, { request: context.request });
1411
+ return { handled: true, response: this.success(result) };
1412
+ }
1413
+ if (parts.length === 1 && m === "GET") {
1414
+ const id = decodeURIComponent(parts[0]);
1415
+ const result = await broker.call("package.get", { id }, { request: context.request });
1416
+ return { handled: true, response: this.success(result) };
1417
+ }
1418
+ if (parts.length === 1 && m === "DELETE") {
1419
+ const id = decodeURIComponent(parts[0]);
1420
+ const result = await broker.call("package.uninstall", { id }, { request: context.request });
1421
+ return { handled: true, response: this.success(result) };
1422
+ }
1423
+ } catch (e) {
1424
+ return { handled: true, response: this.error(e.message, e.statusCode || 500) };
1425
+ }
1426
+ return { handled: false };
1427
+ }
1212
1428
  /**
1213
1429
  * Handles Hub requests
1214
1430
  * path: sub-path after /hub/
@@ -1308,6 +1524,29 @@ var HttpDispatcher = class {
1308
1524
  }
1309
1525
  return { handled: false };
1310
1526
  }
1527
+ /**
1528
+ * Handles UI requests
1529
+ * path: sub-path after /ui/
1530
+ */
1531
+ async handleUi(path, query, _context) {
1532
+ const parts = path.replace(/^\/+/, "").split("/").filter(Boolean);
1533
+ if (parts[0] === "view" && parts[1]) {
1534
+ const objectName = parts[1];
1535
+ const type = parts[2] || query?.type || "list";
1536
+ const protocol = this.kernel?.context?.getService ? this.kernel.context.getService("protocol") : null;
1537
+ if (protocol && typeof protocol.getUiView === "function") {
1538
+ try {
1539
+ const result = await protocol.getUiView({ object: objectName, type });
1540
+ return { handled: true, response: this.success(result) };
1541
+ } catch (e) {
1542
+ return { handled: true, response: this.error(e.message, 500) };
1543
+ }
1544
+ } else {
1545
+ return { handled: true, response: this.error("Protocol service not available", 503) };
1546
+ }
1547
+ }
1548
+ return { handled: false };
1549
+ }
1311
1550
  /**
1312
1551
  * Handles Automation requests
1313
1552
  * path: sub-path after /automation/
@@ -1339,6 +1578,29 @@ var HttpDispatcher = class {
1339
1578
  const services = this.getServicesMap();
1340
1579
  return services[name];
1341
1580
  }
1581
+ /**
1582
+ * Get the ObjectQL service which provides access to SchemaRegistry.
1583
+ * Tries multiple access patterns since kernel structure varies.
1584
+ */
1585
+ getObjectQLService() {
1586
+ if (typeof this.kernel.getService === "function") {
1587
+ try {
1588
+ const svc = this.kernel.getService("objectql");
1589
+ if (svc?.registry) return svc;
1590
+ } catch {
1591
+ }
1592
+ }
1593
+ if (this.kernel?.context?.getService) {
1594
+ try {
1595
+ const svc = this.kernel.context.getService("objectql");
1596
+ if (svc?.registry) return svc;
1597
+ } catch {
1598
+ }
1599
+ }
1600
+ const services = this.getServicesMap();
1601
+ if (services["objectql"]?.registry) return services["objectql"];
1602
+ return null;
1603
+ }
1342
1604
  capitalize(s) {
1343
1605
  return s.charAt(0).toUpperCase() + s.slice(1);
1344
1606
  }
@@ -1348,11 +1610,18 @@ var HttpDispatcher = class {
1348
1610
  */
1349
1611
  async dispatch(method, path, body, query, context) {
1350
1612
  const cleanPath = path.replace(/\/$/, "");
1613
+ if (cleanPath === "" && method === "GET") {
1614
+ const info = this.getDiscoveryInfo("");
1615
+ return {
1616
+ handled: true,
1617
+ response: this.success(info)
1618
+ };
1619
+ }
1351
1620
  if (cleanPath.startsWith("/auth")) {
1352
1621
  return this.handleAuth(cleanPath.substring(5), method, body, context);
1353
1622
  }
1354
1623
  if (cleanPath.startsWith("/meta")) {
1355
- return this.handleMetadata(cleanPath.substring(5), context);
1624
+ return this.handleMetadata(cleanPath.substring(5), context, method, body, query);
1356
1625
  }
1357
1626
  if (cleanPath.startsWith("/data")) {
1358
1627
  return this.handleData(cleanPath.substring(5), method, body, query, context);
@@ -1363,6 +1632,9 @@ var HttpDispatcher = class {
1363
1632
  if (cleanPath.startsWith("/storage")) {
1364
1633
  return this.handleStorage(cleanPath.substring(8), method, body, context);
1365
1634
  }
1635
+ if (cleanPath.startsWith("/ui")) {
1636
+ return this.handleUi(cleanPath.substring(3), query, context);
1637
+ }
1366
1638
  if (cleanPath.startsWith("/automation")) {
1367
1639
  return this.handleAutomation(cleanPath.substring(11), method, body, context);
1368
1640
  }
@@ -1372,6 +1644,9 @@ var HttpDispatcher = class {
1372
1644
  if (cleanPath.startsWith("/hub")) {
1373
1645
  return this.handleHub(cleanPath.substring(4), method, body, query, context);
1374
1646
  }
1647
+ if (cleanPath.startsWith("/packages")) {
1648
+ return this.handlePackages(cleanPath.substring(9), method, body, query, context);
1649
+ }
1375
1650
  if (cleanPath === "/openapi.json" && method === "GET") {
1376
1651
  const broker = this.ensureBroker();
1377
1652
  try {