@x12i/memorix-service 3.3.0 → 3.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 (133) hide show
  1. package/README.md +23 -6
  2. package/dist/app.d.ts.map +1 -1
  3. package/dist/app.js +10 -0
  4. package/dist/app.js.map +1 -1
  5. package/dist/blob-store/config.d.ts +31 -0
  6. package/dist/blob-store/config.d.ts.map +1 -0
  7. package/dist/blob-store/config.js +68 -0
  8. package/dist/blob-store/config.js.map +1 -0
  9. package/dist/blob-store/disk.d.ts +10 -0
  10. package/dist/blob-store/disk.d.ts.map +1 -0
  11. package/dist/blob-store/disk.js +104 -0
  12. package/dist/blob-store/disk.js.map +1 -0
  13. package/dist/blob-store/index.d.ts +6 -0
  14. package/dist/blob-store/index.d.ts.map +1 -0
  15. package/dist/blob-store/index.js +6 -0
  16. package/dist/blob-store/index.js.map +1 -0
  17. package/dist/blob-store/memory.d.ts +4 -0
  18. package/dist/blob-store/memory.d.ts.map +1 -0
  19. package/dist/blob-store/memory.js +47 -0
  20. package/dist/blob-store/memory.js.map +1 -0
  21. package/dist/blob-store/s3.d.ts +52 -0
  22. package/dist/blob-store/s3.d.ts.map +1 -0
  23. package/dist/blob-store/s3.js +151 -0
  24. package/dist/blob-store/s3.js.map +1 -0
  25. package/dist/blob-store/types.d.ts +30 -0
  26. package/dist/blob-store/types.d.ts.map +1 -0
  27. package/dist/blob-store/types.js +38 -0
  28. package/dist/blob-store/types.js.map +1 -0
  29. package/dist/catalog-store-cli.d.ts.map +1 -1
  30. package/dist/catalog-store-cli.js +35 -4
  31. package/dist/catalog-store-cli.js.map +1 -1
  32. package/dist/connector-ops/checkpoint-store.d.ts +7 -0
  33. package/dist/connector-ops/checkpoint-store.d.ts.map +1 -0
  34. package/dist/connector-ops/checkpoint-store.js +135 -0
  35. package/dist/connector-ops/checkpoint-store.js.map +1 -0
  36. package/dist/connector-ops/checkpoint-types.d.ts +46 -0
  37. package/dist/connector-ops/checkpoint-types.d.ts.map +1 -0
  38. package/dist/connector-ops/checkpoint-types.js +14 -0
  39. package/dist/connector-ops/checkpoint-types.js.map +1 -0
  40. package/dist/connector-ops/continuous-types.d.ts +141 -0
  41. package/dist/connector-ops/continuous-types.d.ts.map +1 -0
  42. package/dist/connector-ops/continuous-types.js +13 -0
  43. package/dist/connector-ops/continuous-types.js.map +1 -0
  44. package/dist/connector-ops/coordinator.d.ts +88 -0
  45. package/dist/connector-ops/coordinator.d.ts.map +1 -0
  46. package/dist/connector-ops/coordinator.js +174 -0
  47. package/dist/connector-ops/coordinator.js.map +1 -0
  48. package/dist/connector-ops/index.d.ts +6 -0
  49. package/dist/connector-ops/index.d.ts.map +1 -1
  50. package/dist/connector-ops/index.js +6 -0
  51. package/dist/connector-ops/index.js.map +1 -1
  52. package/dist/connector-ops/run-store.d.ts +16 -0
  53. package/dist/connector-ops/run-store.d.ts.map +1 -0
  54. package/dist/connector-ops/run-store.js +249 -0
  55. package/dist/connector-ops/run-store.js.map +1 -0
  56. package/dist/connector-ops/schedule-lease-store.d.ts +8 -0
  57. package/dist/connector-ops/schedule-lease-store.d.ts.map +1 -0
  58. package/dist/connector-ops/schedule-lease-store.js +318 -0
  59. package/dist/connector-ops/schedule-lease-store.js.map +1 -0
  60. package/dist/incremental-state/index.d.ts +3 -0
  61. package/dist/incremental-state/index.d.ts.map +1 -0
  62. package/dist/incremental-state/index.js +3 -0
  63. package/dist/incremental-state/index.js.map +1 -0
  64. package/dist/incremental-state/state-store.d.ts +7 -0
  65. package/dist/incremental-state/state-store.d.ts.map +1 -0
  66. package/dist/incremental-state/state-store.js +183 -0
  67. package/dist/incremental-state/state-store.js.map +1 -0
  68. package/dist/incremental-state/types.d.ts +104 -0
  69. package/dist/incremental-state/types.d.ts.map +1 -0
  70. package/dist/incremental-state/types.js +8 -0
  71. package/dist/incremental-state/types.js.map +1 -0
  72. package/dist/index.d.ts +3 -0
  73. package/dist/index.d.ts.map +1 -1
  74. package/dist/index.js +3 -0
  75. package/dist/index.js.map +1 -1
  76. package/dist/integrations/index.d.ts +3 -0
  77. package/dist/integrations/index.d.ts.map +1 -0
  78. package/dist/integrations/index.js +3 -0
  79. package/dist/integrations/index.js.map +1 -0
  80. package/dist/integrations/instance-store.d.ts +5 -0
  81. package/dist/integrations/instance-store.d.ts.map +1 -0
  82. package/dist/integrations/instance-store.js +327 -0
  83. package/dist/integrations/instance-store.js.map +1 -0
  84. package/dist/integrations/types.d.ts +92 -0
  85. package/dist/integrations/types.d.ts.map +1 -0
  86. package/dist/integrations/types.js +40 -0
  87. package/dist/integrations/types.js.map +1 -0
  88. package/dist/magit/magit-pack-service.d.ts +187 -3
  89. package/dist/magit/magit-pack-service.d.ts.map +1 -1
  90. package/dist/magit/magit-pack-service.js +401 -8
  91. package/dist/magit/magit-pack-service.js.map +1 -1
  92. package/dist/openapi/openapi.json +1348 -2
  93. package/dist/platform.d.ts +44 -1
  94. package/dist/platform.d.ts.map +1 -1
  95. package/dist/platform.js +172 -1
  96. package/dist/platform.js.map +1 -1
  97. package/dist/restricted-store/crypto.d.ts +10 -0
  98. package/dist/restricted-store/crypto.d.ts.map +1 -0
  99. package/dist/restricted-store/crypto.js +43 -0
  100. package/dist/restricted-store/crypto.js.map +1 -0
  101. package/dist/restricted-store/index.d.ts +4 -0
  102. package/dist/restricted-store/index.d.ts.map +1 -0
  103. package/dist/restricted-store/index.js +4 -0
  104. package/dist/restricted-store/index.js.map +1 -0
  105. package/dist/restricted-store/object-store.d.ts +12 -0
  106. package/dist/restricted-store/object-store.d.ts.map +1 -0
  107. package/dist/restricted-store/object-store.js +233 -0
  108. package/dist/restricted-store/object-store.js.map +1 -0
  109. package/dist/restricted-store/types.d.ts +78 -0
  110. package/dist/restricted-store/types.d.ts.map +1 -0
  111. package/dist/restricted-store/types.js +12 -0
  112. package/dist/restricted-store/types.js.map +1 -0
  113. package/dist/routes/continuous-acquisition.d.ts +5 -0
  114. package/dist/routes/continuous-acquisition.d.ts.map +1 -0
  115. package/dist/routes/continuous-acquisition.js +259 -0
  116. package/dist/routes/continuous-acquisition.js.map +1 -0
  117. package/dist/routes/incremental-state.d.ts +5 -0
  118. package/dist/routes/incremental-state.d.ts.map +1 -0
  119. package/dist/routes/incremental-state.js +173 -0
  120. package/dist/routes/incremental-state.js.map +1 -0
  121. package/dist/routes/integrations.d.ts +5 -0
  122. package/dist/routes/integrations.d.ts.map +1 -0
  123. package/dist/routes/integrations.js +126 -0
  124. package/dist/routes/integrations.js.map +1 -0
  125. package/dist/routes/metadata.d.ts.map +1 -1
  126. package/dist/routes/metadata.js +328 -4
  127. package/dist/routes/metadata.js.map +1 -1
  128. package/dist/routes/restricted-store.d.ts +5 -0
  129. package/dist/routes/restricted-store.d.ts.map +1 -0
  130. package/dist/routes/restricted-store.js +117 -0
  131. package/dist/routes/restricted-store.js.map +1 -0
  132. package/openapi/openapi.json +1348 -2
  133. package/package.json +3 -2
@@ -137,20 +137,26 @@ export function resolveMagitRepoRootParent() {
137
137
  * Central Magit package-catalog tenant (FR-CPS-1). This is a fixed
138
138
  * developer/solution scope for Memorix's own package storage — it must NEVER
139
139
  * be a runtime `orgId`. Org only appears as a Restore install target, never as
140
- * the catalog key. Default `"memorix"`.
140
+ * the catalog key. Default `"x12i"` (dedicated Memorix catalog workspace).
141
141
  */
142
142
  export function resolveMagitCatalogTenant() {
143
- return process.env.MAGIT_CATALOG_TENANT?.trim() || "memorix";
143
+ return process.env.MAGIT_CATALOG_TENANT?.trim() || "x12i";
144
144
  }
145
145
  /**
146
146
  * Magit storage repo id for the central package catalog (FR-CPS). Defaults to
147
- * `memorix-metadata-_package-catalog`. Override with MAGIT_CATALOG_REPO when the
148
- * Authix token only grants a specific repo (e.g. `demo`).
147
+ * `memorix` (dedicated catalog repo). Override with MAGIT_CATALOG_REPO only when
148
+ * targeting a non-standard host layout.
149
149
  */
150
150
  export function resolveMagitCatalogRepoName() {
151
- return (process.env.MAGIT_CATALOG_REPO?.trim() ||
152
- `memorix-metadata-${PACKAGE_CATALOG_LOCAL_DIR}`);
151
+ return process.env.MAGIT_CATALOG_REPO?.trim() || "memorix";
153
152
  }
153
+ /** Write-once on createGlobalStore — kinds omitted can only be fixed with a second store. */
154
+ export const DEFAULT_TYPED_STORE_ALLOWED_KINDS = [
155
+ "agent",
156
+ "connector",
157
+ "service",
158
+ "agent-bundle",
159
+ ];
154
160
  /**
155
161
  * Env var *name* Magit uses to load the central-storage bearer token.
156
162
  * Priority: MAGIT_STORAGE_TOKEN_ENV (explicit name) → AUTHIX_TOKEN if set → MAGIT_TOKEN.
@@ -1127,14 +1133,18 @@ export class MagitPackService {
1127
1133
  * Provision a typed global package store (create + pushStore + verify).
1128
1134
  * Returns the selector EXACTLY ONCE — caller must write it to a secret store.
1129
1135
  * Duplicate packageTypeId stores abort unless force=true.
1136
+ * allowedKinds is write-once on the Magit store — decide the list before create.
1130
1137
  */
1131
1138
  async provisionTypedPackageStore(opts) {
1132
1139
  const packageTypeId = opts?.packageTypeId?.trim() ||
1133
1140
  process.env.MAGIT_PACKAGE_TYPE_ID?.trim() ||
1134
1141
  "memorix.packages.v1";
1135
1142
  const allowedKinds = opts?.allowedKinds && opts.allowedKinds.length > 0
1136
- ? opts.allowedKinds
1137
- : ["agent", "connector", "service", "agent-bundle"];
1143
+ ? opts.allowedKinds.map((k) => k.trim()).filter(Boolean)
1144
+ : [...DEFAULT_TYPED_STORE_ALLOWED_KINDS];
1145
+ if (allowedKinds.length === 0) {
1146
+ throw new ServiceError("VALIDATION", "allowedKinds must list at least one package kind (write-once on create)", 400);
1147
+ }
1138
1148
  try {
1139
1149
  const repo = await this.ensureCatalogRepo();
1140
1150
  const existing = await repo.listStores();
@@ -1157,6 +1167,7 @@ export class MagitPackService {
1157
1167
  storeId: created.storeId,
1158
1168
  storeSelector: created.storeSelector,
1159
1169
  packageTypeId: created.packageTypeId,
1170
+ allowedKinds: [...allowedKinds],
1160
1171
  pushed: {
1161
1172
  uploaded: pushed.uploaded ?? 0,
1162
1173
  skipped: pushed.skipped ?? 0,
@@ -1172,6 +1183,388 @@ export class MagitPackService {
1172
1183
  });
1173
1184
  }
1174
1185
  }
1186
+ /** Mode A required: package / release channels live on the typed store only. */
1187
+ requireTypedStoreConfig() {
1188
+ const store = resolveMagitTypedStoreConfig();
1189
+ if (!store) {
1190
+ throw new ServiceError("VALIDATION", "Package channels and release sets require Mode A (MAGIT_PACKAGE_STORE_SELECTOR + MAGIT_PACKAGE_TYPE_ID)", 400);
1191
+ }
1192
+ return store;
1193
+ }
1194
+ /**
1195
+ * Promote a package channel pointer (Mode A). Packages never move — only the pointer.
1196
+ */
1197
+ async setPackageChannel(opts) {
1198
+ const store = this.requireTypedStoreConfig();
1199
+ const kind = String(opts.kind ?? "").trim();
1200
+ const id = String(opts.id ?? "").trim();
1201
+ const channel = String(opts.channel ?? "").trim();
1202
+ const version = String(opts.version ?? "").trim();
1203
+ if (!kind || !id || !channel || !version) {
1204
+ throw new ServiceError("VALIDATION", "kind, id, channel, and version are required", 400);
1205
+ }
1206
+ try {
1207
+ const repo = await this.ensureCatalogRepo();
1208
+ const result = await repo.setPackageChannel({
1209
+ storeSelector: store.storeSelector,
1210
+ packageTypeId: store.packageTypeId,
1211
+ kind,
1212
+ id,
1213
+ channel,
1214
+ version,
1215
+ message: opts.message?.trim() || undefined,
1216
+ expectedVersion: opts.expectedVersion,
1217
+ publisher: store.publisher,
1218
+ });
1219
+ this.catalogHealth = { reachable: true, reason: null };
1220
+ return {
1221
+ storeId: result.storeId,
1222
+ kind: result.kind,
1223
+ id: result.id,
1224
+ channel: result.channel,
1225
+ version: result.version,
1226
+ previousVersion: result.previousVersion,
1227
+ seq: result.seq,
1228
+ movedAt: result.movedAt,
1229
+ message: result.message,
1230
+ unchanged: result.unchanged,
1231
+ };
1232
+ }
1233
+ catch (err) {
1234
+ if (err instanceof ServiceError)
1235
+ throw err;
1236
+ throw this.mapError(err, "failed to promote package channel", {
1237
+ catalog: true,
1238
+ });
1239
+ }
1240
+ }
1241
+ /** Resolve where a package channel points (Mode A; Magit pulls first by default). */
1242
+ async resolvePackageChannel(opts) {
1243
+ const store = this.requireTypedStoreConfig();
1244
+ const kind = String(opts.kind ?? "").trim();
1245
+ const id = String(opts.id ?? "").trim();
1246
+ const channel = String(opts.channel ?? "").trim();
1247
+ if (!kind || !id || !channel) {
1248
+ throw new ServiceError("VALIDATION", "kind, id, and channel are required", 400);
1249
+ }
1250
+ try {
1251
+ const repo = await this.ensureCatalogRepo();
1252
+ const result = await repo.resolvePackageChannel({
1253
+ storeSelector: store.storeSelector,
1254
+ packageTypeId: store.packageTypeId,
1255
+ kind,
1256
+ id,
1257
+ channel,
1258
+ sync: opts.sync,
1259
+ });
1260
+ this.catalogHealth = { reachable: true, reason: null };
1261
+ return {
1262
+ storeId: result.storeId,
1263
+ kind: result.kind,
1264
+ id: result.id,
1265
+ channel: result.channel,
1266
+ version: result.version,
1267
+ seq: result.seq,
1268
+ movedAt: result.movedAt,
1269
+ message: result.message,
1270
+ };
1271
+ }
1272
+ catch (err) {
1273
+ if (err instanceof ServiceError)
1274
+ throw err;
1275
+ throw this.mapError(err, "failed to resolve package channel", {
1276
+ catalog: true,
1277
+ });
1278
+ }
1279
+ }
1280
+ /** Cut an immutable release set (content-addressed members). Mode A only. */
1281
+ async packRelease(opts) {
1282
+ const store = this.requireTypedStoreConfig();
1283
+ const members = (opts.members ?? [])
1284
+ .map((m) => ({
1285
+ kind: String(m.kind ?? "").trim(),
1286
+ id: String(m.id ?? "").trim(),
1287
+ version: String(m.version ?? "").trim(),
1288
+ }))
1289
+ .filter((m) => m.kind && m.id && m.version);
1290
+ if (members.length === 0) {
1291
+ throw new ServiceError("VALIDATION", "members must list at least one { kind, id, version }", 400);
1292
+ }
1293
+ try {
1294
+ const repo = await this.ensureCatalogRepo();
1295
+ const result = await repo.packRelease({
1296
+ storeSelector: store.storeSelector,
1297
+ packageTypeId: store.packageTypeId,
1298
+ members,
1299
+ message: opts.message?.trim() || undefined,
1300
+ publisher: store.publisher,
1301
+ });
1302
+ this.catalogHealth = { reachable: true, reason: null };
1303
+ return {
1304
+ releaseId: result.release.releaseId,
1305
+ storeId: result.release.storeId,
1306
+ members: result.release.members.map((m) => ({
1307
+ kind: m.kind,
1308
+ id: m.id,
1309
+ version: m.version,
1310
+ })),
1311
+ reused: result.reused,
1312
+ published: result.published,
1313
+ message: result.release.message,
1314
+ createdAt: result.release.createdAt,
1315
+ };
1316
+ }
1317
+ catch (err) {
1318
+ if (err instanceof ServiceError)
1319
+ throw err;
1320
+ throw this.mapError(err, "failed to pack release set", { catalog: true });
1321
+ }
1322
+ }
1323
+ /** Point a release channel at a releaseId (atomic promote of a set). Mode A. */
1324
+ async setReleaseChannel(opts) {
1325
+ const store = this.requireTypedStoreConfig();
1326
+ const channel = String(opts.channel ?? "").trim();
1327
+ const releaseId = String(opts.releaseId ?? "").trim();
1328
+ if (!channel || !releaseId) {
1329
+ throw new ServiceError("VALIDATION", "channel and releaseId are required", 400);
1330
+ }
1331
+ try {
1332
+ const repo = await this.ensureCatalogRepo();
1333
+ const result = await repo.setReleaseChannel({
1334
+ storeSelector: store.storeSelector,
1335
+ packageTypeId: store.packageTypeId,
1336
+ channel,
1337
+ releaseId,
1338
+ message: opts.message?.trim() || undefined,
1339
+ expectedReleaseId: opts.expectedReleaseId,
1340
+ publisher: store.publisher,
1341
+ });
1342
+ this.catalogHealth = { reachable: true, reason: null };
1343
+ return {
1344
+ storeId: result.storeId,
1345
+ channel: result.channel,
1346
+ releaseId: result.releaseId,
1347
+ previousReleaseId: result.previousReleaseId,
1348
+ seq: result.seq,
1349
+ movedAt: result.movedAt,
1350
+ message: result.message,
1351
+ unchanged: result.unchanged,
1352
+ };
1353
+ }
1354
+ catch (err) {
1355
+ if (err instanceof ServiceError)
1356
+ throw err;
1357
+ throw this.mapError(err, "failed to promote release channel", {
1358
+ catalog: true,
1359
+ });
1360
+ }
1361
+ }
1362
+ /** Resolve a release channel with full member set (Magit pulls first). Mode A. */
1363
+ async resolveReleaseChannel(opts) {
1364
+ const store = this.requireTypedStoreConfig();
1365
+ const channel = String(opts.channel ?? "").trim();
1366
+ if (!channel) {
1367
+ throw new ServiceError("VALIDATION", "channel is required", 400);
1368
+ }
1369
+ try {
1370
+ const repo = await this.ensureCatalogRepo();
1371
+ const result = await repo.resolveReleaseChannel({
1372
+ storeSelector: store.storeSelector,
1373
+ packageTypeId: store.packageTypeId,
1374
+ channel,
1375
+ sync: opts.sync,
1376
+ });
1377
+ this.catalogHealth = { reachable: true, reason: null };
1378
+ return {
1379
+ storeId: result.storeId,
1380
+ channel: result.channel,
1381
+ releaseId: result.releaseId,
1382
+ seq: result.seq,
1383
+ movedAt: result.movedAt,
1384
+ message: result.message,
1385
+ release: {
1386
+ releaseId: result.release.releaseId,
1387
+ members: result.release.members.map((m) => ({
1388
+ kind: m.kind,
1389
+ id: m.id,
1390
+ version: m.version,
1391
+ })),
1392
+ message: result.release.message,
1393
+ createdAt: result.release.createdAt,
1394
+ },
1395
+ };
1396
+ }
1397
+ catch (err) {
1398
+ if (err instanceof ServiceError)
1399
+ throw err;
1400
+ throw this.mapError(err, "failed to resolve release channel", {
1401
+ catalog: true,
1402
+ });
1403
+ }
1404
+ }
1405
+ /** Rollback list for a release channel (Mode A). */
1406
+ async releaseChannelHistory(opts) {
1407
+ const store = this.requireTypedStoreConfig();
1408
+ const channel = String(opts.channel ?? "").trim();
1409
+ if (!channel) {
1410
+ throw new ServiceError("VALIDATION", "channel is required", 400);
1411
+ }
1412
+ try {
1413
+ const repo = await this.ensureCatalogRepo();
1414
+ const rows = await repo.releaseChannelHistory({
1415
+ storeSelector: store.storeSelector,
1416
+ packageTypeId: store.packageTypeId,
1417
+ channel,
1418
+ sync: opts.sync,
1419
+ });
1420
+ this.catalogHealth = { reachable: true, reason: null };
1421
+ return rows.map((r) => ({
1422
+ channel: r.channel,
1423
+ seq: r.seq,
1424
+ releaseId: r.releaseId,
1425
+ previousReleaseId: r.previousReleaseId,
1426
+ movedAt: r.movedAt,
1427
+ message: r.message,
1428
+ }));
1429
+ }
1430
+ catch (err) {
1431
+ if (err instanceof ServiceError)
1432
+ throw err;
1433
+ throw this.mapError(err, "failed to list release channel history", {
1434
+ catalog: true,
1435
+ });
1436
+ }
1437
+ }
1438
+ /**
1439
+ * List package channel heads (Mode A). Optionally filter by kind/id/channel.
1440
+ * Magit pulls first unless sync:false.
1441
+ */
1442
+ async listPackageChannels(opts = {}) {
1443
+ const store = this.requireTypedStoreConfig();
1444
+ try {
1445
+ const repo = await this.ensureCatalogRepo();
1446
+ const rows = await repo.listPackageChannels({
1447
+ storeSelector: store.storeSelector,
1448
+ packageTypeId: store.packageTypeId,
1449
+ kind: opts.kind?.trim() || undefined,
1450
+ id: opts.id?.trim() || undefined,
1451
+ channel: opts.channel?.trim() || undefined,
1452
+ sync: opts.sync,
1453
+ });
1454
+ this.catalogHealth = { reachable: true, reason: null };
1455
+ return rows.map((r) => ({
1456
+ storeId: r.storeId,
1457
+ kind: r.kind,
1458
+ id: r.id,
1459
+ channel: r.channel,
1460
+ version: r.version,
1461
+ seq: r.seq,
1462
+ movedAt: r.movedAt,
1463
+ message: r.message,
1464
+ }));
1465
+ }
1466
+ catch (err) {
1467
+ if (err instanceof ServiceError)
1468
+ throw err;
1469
+ throw this.mapError(err, "failed to list package channels", {
1470
+ catalog: true,
1471
+ });
1472
+ }
1473
+ }
1474
+ /** Every promotion ever made on a package channel (Mode A). */
1475
+ async packageChannelHistory(opts) {
1476
+ const store = this.requireTypedStoreConfig();
1477
+ const kind = String(opts.kind ?? "").trim();
1478
+ const id = String(opts.id ?? "").trim();
1479
+ const channel = String(opts.channel ?? "").trim();
1480
+ if (!kind || !id || !channel) {
1481
+ throw new ServiceError("VALIDATION", "kind, id, and channel are required", 400);
1482
+ }
1483
+ try {
1484
+ const repo = await this.ensureCatalogRepo();
1485
+ const rows = await repo.packageChannelHistory({
1486
+ storeSelector: store.storeSelector,
1487
+ packageTypeId: store.packageTypeId,
1488
+ kind,
1489
+ id,
1490
+ channel,
1491
+ sync: opts.sync,
1492
+ });
1493
+ this.catalogHealth = { reachable: true, reason: null };
1494
+ return rows.map((r) => ({
1495
+ kind: r.kind,
1496
+ id: r.id,
1497
+ channel: r.channel,
1498
+ version: r.version,
1499
+ previousVersion: r.previousVersion,
1500
+ seq: r.seq,
1501
+ movedAt: r.movedAt,
1502
+ message: r.message,
1503
+ }));
1504
+ }
1505
+ catch (err) {
1506
+ if (err instanceof ServiceError)
1507
+ throw err;
1508
+ throw this.mapError(err, "failed to list package channel history", {
1509
+ catalog: true,
1510
+ });
1511
+ }
1512
+ }
1513
+ /** List all release channel heads in the store (Mode A). */
1514
+ async listReleaseChannels(opts = {}) {
1515
+ const store = this.requireTypedStoreConfig();
1516
+ try {
1517
+ const repo = await this.ensureCatalogRepo();
1518
+ const rows = await repo.listReleaseChannels({
1519
+ storeSelector: store.storeSelector,
1520
+ packageTypeId: store.packageTypeId,
1521
+ sync: opts.sync,
1522
+ });
1523
+ this.catalogHealth = { reachable: true, reason: null };
1524
+ return rows.map((r) => ({
1525
+ storeId: r.storeId,
1526
+ channel: r.channel,
1527
+ releaseId: r.releaseId,
1528
+ seq: r.seq,
1529
+ movedAt: r.movedAt,
1530
+ message: r.message,
1531
+ release: {
1532
+ releaseId: r.release.releaseId,
1533
+ members: r.release.members.map((m) => ({
1534
+ kind: m.kind,
1535
+ id: m.id,
1536
+ version: m.version,
1537
+ })),
1538
+ message: r.release.message,
1539
+ createdAt: r.release.createdAt,
1540
+ },
1541
+ }));
1542
+ }
1543
+ catch (err) {
1544
+ if (err instanceof ServiceError)
1545
+ throw err;
1546
+ throw this.mapError(err, "failed to list release channels", {
1547
+ catalog: true,
1548
+ });
1549
+ }
1550
+ }
1551
+ /**
1552
+ * Safe catalog capability flags for Studio (never include selector/token).
1553
+ */
1554
+ packageCatalogCapabilities() {
1555
+ const store = resolveMagitTypedStoreConfig();
1556
+ const typed = Boolean(store);
1557
+ return {
1558
+ magitOn: true,
1559
+ typedStoreConfigured: typed,
1560
+ packageTypeId: store?.packageTypeId ?? null,
1561
+ canBrowse: true,
1562
+ canPublish: true,
1563
+ canPromote: typed,
1564
+ canUseChannels: typed,
1565
+ canUseReleases: typed,
1566
+ };
1567
+ }
1175
1568
  /**
1176
1569
  * Verify configured catalog reachability (typed store via pullStore, or flat pull).
1177
1570
  * Never prints selector/token values.