@warmhub/cli 0.52.1 → 0.53.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 (2) hide show
  1. package/dist/wh.js +311 -233
  2. package/package.json +1 -1
package/dist/wh.js CHANGED
@@ -18914,6 +18914,10 @@ var RUNTIME_ACCESS_BUILTINS = new Set([
18914
18914
  "ComponentConfig",
18915
18915
  ...BUILTIN_SHAPE_NAMES
18916
18916
  ]);
18917
+ var SUBSCRIPTION_TRIGGER_BUILTINS = new Set([
18918
+ "ComponentInstall",
18919
+ "ComponentConfig"
18920
+ ]);
18917
18921
  function validateProvisioning(obj, path, errors) {
18918
18922
  if (!("provisioning" in obj))
18919
18923
  return;
@@ -19206,8 +19210,19 @@ function validateManifestSemantics(manifest) {
19206
19210
  }
19207
19211
  const shapeNames = new Set(manifest.shapes.map((s) => s.name));
19208
19212
  const credentialNames = new Set(manifest.credentials.map((c) => c.name));
19209
- const knownShapes = new Set([...shapeNames, "ComponentConfig"]);
19213
+ const knownSubscriptionTriggerShapes = new Set([
19214
+ ...shapeNames,
19215
+ ...SUBSCRIPTION_TRIGGER_BUILTINS
19216
+ ]);
19217
+ const knownSeedShapes = new Set([...shapeNames, "ComponentConfig"]);
19210
19218
  for (const sub of manifest.subscriptions) {
19219
+ if (sub.trigger.kind === "event" && !knownSubscriptionTriggerShapes.has(sub.trigger.shape)) {
19220
+ findings.push({
19221
+ level: "error",
19222
+ code: "MISSING_SUBSCRIPTION_TRIGGER_SHAPE_REF",
19223
+ message: `subscription "${sub.name}" trigger references shape "${sub.trigger.shape}" which is not declared and is not a supported subscription trigger built-in`
19224
+ });
19225
+ }
19211
19226
  if (sub.credentials) {
19212
19227
  if (sub.credentials.length > 1) {
19213
19228
  findings.push({
@@ -19236,7 +19251,7 @@ function validateManifestSemantics(manifest) {
19236
19251
  }
19237
19252
  }
19238
19253
  for (const seed of manifest.seeds) {
19239
- if (!knownShapes.has(seed.shape)) {
19254
+ if (!knownSeedShapes.has(seed.shape)) {
19240
19255
  findings.push({
19241
19256
  level: "error",
19242
19257
  code: "MISSING_SHAPE_REF",
@@ -19381,6 +19396,11 @@ function joinFieldIdentityPath(...segments) {
19381
19396
  function joinFieldPathWithEscaper(escapeSegment, segments) {
19382
19397
  return segments.map((segment) => typeof segment === "number" ? `[${segment}]` : escapeSegment(segment)).join(".").replace(/\.\[/g, "[");
19383
19398
  }
19399
+ // ../../packages/rules/src/permission-scopes.ts
19400
+ var PUBLIC_ORG_PERMISSIONS = new Set([
19401
+ "org:read",
19402
+ "components:read"
19403
+ ]);
19384
19404
  // ../../packages/rules/src/platform-status.ts
19385
19405
  var PLATFORM_STATUS_CATALOG_SHAPE = "PlatformStatusCatalog";
19386
19406
  var PLATFORM_STATUS_CATALOG_NAME = "main";
@@ -21213,6 +21233,7 @@ function nestedObjectFields(typeDef) {
21213
21233
  }
21214
21234
  // ../../packages/rules/src/system-components/system.ts
21215
21235
  var SYSTEM_COMPONENT_ID = "com.warmhub.system";
21236
+ var SYSTEM_REGISTERED_COMPONENT_REF = "warmhub/system";
21216
21237
  var COMPONENT_INSTALL_FIELDS = {
21217
21238
  componentId: "string",
21218
21239
  name: "string",
@@ -21273,7 +21294,7 @@ function findSystemComponent(componentId) {
21273
21294
  // ../../packages/sdk-ts/package.json
21274
21295
  var package_default = {
21275
21296
  name: "@warmhub/sdk-ts",
21276
- version: "0.52.0",
21297
+ version: "0.52.1",
21277
21298
  private: false,
21278
21299
  type: "module",
21279
21300
  description: "The TypeScript SDK for WarmHub — create repos, commit and query data, and compound knowledge with your AI agents.",
@@ -21601,7 +21622,7 @@ async function submitOperationsViaStream(client, args) {
21601
21622
  orgName: args.orgName,
21602
21623
  repoName: args.repoName,
21603
21624
  streamId,
21604
- componentId: args.componentId,
21625
+ componentRef: args.componentRef,
21605
21626
  committer: args.committer,
21606
21627
  message: args.message,
21607
21628
  operations: chunk
@@ -22107,23 +22128,9 @@ class WarmHubClient {
22107
22128
  }
22108
22129
  };
22109
22130
  access = {
22110
- checkRepoPermission: async (orgName, repoName, permission) => {
22131
+ resolve: async (input) => {
22111
22132
  try {
22112
- return await this.trpc.access.checkRepoPermission.query({
22113
- orgName,
22114
- repoName,
22115
- permission
22116
- });
22117
- } catch (error) {
22118
- throw toWarmHubError(error);
22119
- }
22120
- },
22121
- checkOrgPermission: async (orgName, permission) => {
22122
- try {
22123
- return await this.trpc.access.checkOrgPermission.query({
22124
- orgName,
22125
- permission
22126
- });
22133
+ return await this.trpc.access.resolve.query(input);
22127
22134
  } catch (error) {
22128
22135
  throw toWarmHubError(error);
22129
22136
  }
@@ -22177,12 +22184,12 @@ class WarmHubClient {
22177
22184
  throw toWarmHubError(error);
22178
22185
  }
22179
22186
  },
22180
- get: async (orgName, repoName, componentId) => {
22187
+ get: async (orgName, repoName, componentRef) => {
22181
22188
  try {
22182
22189
  return await this.trpc.component.get.query({
22183
22190
  orgName,
22184
22191
  repoName,
22185
- componentId
22192
+ componentRef
22186
22193
  });
22187
22194
  } catch (error) {
22188
22195
  throw toWarmHubError(error);
@@ -22277,7 +22284,7 @@ class WarmHubClient {
22277
22284
  repoName,
22278
22285
  committer: opts?.committer,
22279
22286
  message,
22280
- componentId: opts?.componentId,
22287
+ componentRef: opts?.componentRef,
22281
22288
  chunkSize: opts?.chunkSize,
22282
22289
  skipExisting: opts?.skipExisting,
22283
22290
  streamId: opts?.streamId,
@@ -22715,7 +22722,7 @@ class WarmHubClient {
22715
22722
  orgName,
22716
22723
  repoName,
22717
22724
  match: opts?.match,
22718
- componentId: opts?.componentId,
22725
+ componentRef: opts?.componentRef,
22719
22726
  excludeComponents: opts?.excludeComponents,
22720
22727
  includeRetracted: opts?.includeRetracted
22721
22728
  });
@@ -23061,7 +23068,7 @@ class WarmHubClient {
23061
23068
  includeRetracted: opts?.includeRetracted,
23062
23069
  limit: opts?.limit,
23063
23070
  cursor: opts?.cursor,
23064
- componentId: opts?.componentId,
23071
+ componentRef: opts?.componentRef,
23065
23072
  excludeComponents: opts?.excludeComponents,
23066
23073
  excludeInfraShapes: opts?.excludeInfraShapes,
23067
23074
  where: opts?.where
@@ -23282,7 +23289,7 @@ class WarmHubClient {
23282
23289
  resolveCollections: opts?.resolveCollections,
23283
23290
  limit: opts?.limit,
23284
23291
  cursor: opts?.cursor,
23285
- componentId: opts?.componentId,
23292
+ componentRef: opts?.componentRef,
23286
23293
  excludeComponents: opts?.excludeComponents,
23287
23294
  excludeInfraShapes: opts?.excludeInfraShapes,
23288
23295
  where: opts?.where
@@ -23313,7 +23320,7 @@ class WarmHubClient {
23313
23320
  resolveCollections: opts?.resolveCollections,
23314
23321
  limit: opts?.limit,
23315
23322
  cursor: opts?.cursor,
23316
- componentId: opts?.componentId,
23323
+ componentRef: opts?.componentRef,
23317
23324
  excludeComponents: opts?.excludeComponents,
23318
23325
  excludeInfraShapes: opts?.excludeInfraShapes,
23319
23326
  mode: opts?.mode
@@ -23334,7 +23341,7 @@ class WarmHubClient {
23334
23341
  match: opts?.match,
23335
23342
  includeRetracted: opts?.includeRetracted,
23336
23343
  resolveCollections: opts?.resolveCollections,
23337
- componentId: opts?.componentId,
23344
+ componentRef: opts?.componentRef,
23338
23345
  excludeComponents: opts?.excludeComponents,
23339
23346
  excludeInfraShapes: opts?.excludeInfraShapes,
23340
23347
  where: opts?.where
@@ -25176,7 +25183,7 @@ function loadProfileStore(path) {
25176
25183
  } catch (cause) {
25177
25184
  throw corruptedAuthError(p, cause);
25178
25185
  }
25179
- if (parsed === null || typeof parsed !== "object" || parsed.version !== 1 || parsed.profiles === null || typeof parsed.profiles !== "object") {
25186
+ if (parsed === null || typeof parsed !== "object" || parsed.version !== 1 || parsed.profiles === null || typeof parsed.profiles !== "object" || Array.isArray(parsed.profiles)) {
25180
25187
  throw corruptedAuthError(p);
25181
25188
  }
25182
25189
  return parsed;
@@ -25481,20 +25488,34 @@ function recordBenchmarkResponse(response, benchmarkId) {
25481
25488
  benchmarkTelemetry.requestDbQueryCountSum += requestDbQueryCount;
25482
25489
  }
25483
25490
  }
25484
- function createBenchmarkAwareFetch(benchmarkId) {
25485
- if (!benchmarkId)
25491
+ function createBenchmarkAwareFetch(benchmarkId, signal) {
25492
+ if (!benchmarkId && !signal)
25486
25493
  return;
25487
25494
  return async (input, init) => {
25488
25495
  const headers = new Headers(init?.headers);
25489
- headers.set(BENCHMARK_HEADER, benchmarkId);
25496
+ if (benchmarkId)
25497
+ headers.set(BENCHMARK_HEADER, benchmarkId);
25490
25498
  const response = await fetch(input, {
25491
25499
  ...init,
25492
- headers
25500
+ headers,
25501
+ signal: mergeAbortSignals(init?.signal, signal)
25493
25502
  });
25494
- recordBenchmarkResponse(response, benchmarkId);
25503
+ if (benchmarkId)
25504
+ recordBenchmarkResponse(response, benchmarkId);
25495
25505
  return response;
25496
25506
  };
25497
25507
  }
25508
+ function mergeAbortSignals(requestSignal, liveSignal) {
25509
+ if (!requestSignal)
25510
+ return liveSignal;
25511
+ if (!liveSignal)
25512
+ return requestSignal;
25513
+ if (requestSignal.aborted)
25514
+ return requestSignal;
25515
+ if (liveSignal.aborted)
25516
+ return liveSignal;
25517
+ return AbortSignal.any([requestSignal, liveSignal]);
25518
+ }
25498
25519
  function resetBenchmarkTelemetry() {
25499
25520
  benchmarkTelemetry = emptyBenchmarkTelemetry();
25500
25521
  }
@@ -25522,18 +25543,34 @@ function createClient(config, opts = {}) {
25522
25543
  function wantsStructuredLiveOutput(format) {
25523
25544
  return format === "json" || format === "jsonl";
25524
25545
  }
25546
+ var LIVE_ABORTED = Symbol("live-aborted");
25547
+ function pollUntilAbort(poll, signal) {
25548
+ if (signal.aborted)
25549
+ return Promise.resolve(LIVE_ABORTED);
25550
+ return new Promise((resolve, reject) => {
25551
+ const onAbort = () => resolve(LIVE_ABORTED);
25552
+ signal.addEventListener("abort", onAbort, { once: true });
25553
+ poll.then((result) => {
25554
+ signal.removeEventListener("abort", onAbort);
25555
+ resolve(result);
25556
+ }, (error) => {
25557
+ signal.removeEventListener("abort", onAbort);
25558
+ reject(error);
25559
+ });
25560
+ });
25561
+ }
25525
25562
  async function runLive(opts) {
25526
25563
  const auth = opts.auth ?? {
25527
25564
  getToken: async () => await getValidToken(opts.profile) ?? undefined
25528
25565
  };
25529
25566
  const benchmarkId = process.env.WH_BENCHMARK_ID?.trim();
25567
+ const controller = new AbortController;
25530
25568
  const client = new WarmHubClient({
25531
25569
  apiUrl: opts.apiUrl,
25532
25570
  auth,
25533
- fetch: createBenchmarkAwareFetch(benchmarkId),
25571
+ fetch: createBenchmarkAwareFetch(benchmarkId, controller.signal),
25534
25572
  functionLogs: opts.functionLogs
25535
25573
  });
25536
- const controller = new AbortController;
25537
25574
  if (opts.signal) {
25538
25575
  if (opts.signal.aborted)
25539
25576
  controller.abort();
@@ -25557,7 +25594,12 @@ async function runLive(opts) {
25557
25594
  startTimer = setTimeout(() => controller.abort(), opts.startTimeoutMs);
25558
25595
  }
25559
25596
  while (!controller.signal.aborted) {
25560
- const result = await opts.poll(client);
25597
+ const poll = opts.poll(client, controller.signal);
25598
+ const result = await pollUntilAbort(poll, controller.signal);
25599
+ if (result === LIVE_ABORTED) {
25600
+ poll.catch(() => {});
25601
+ break;
25602
+ }
25561
25603
  updates += 1;
25562
25604
  if (startTimer) {
25563
25605
  clearTimeout(startTimer);
@@ -26570,14 +26612,14 @@ function renderBatchView(out, c, result, wrefs, flagsVersion) {
26570
26612
  // ../../packages/warmhub-cli/src/domains/thing/graph.ts
26571
26613
  var graphFlags = {
26572
26614
  depth: flag.number({
26573
- description: "Resolve embedded graph to this depth (default: 2, max: 5)"
26615
+ description: "Resolve outbound wref fields, assertion backlinks, and assertion about targets to this depth (default: 2, max: 5)"
26574
26616
  }),
26575
26617
  version: flag.number({ description: "Specific version number" })
26576
26618
  };
26577
26619
  var handleThingGraph = async (ctx, { flags, args }) => {
26578
26620
  const wref = args[0];
26579
26621
  if (!wref) {
26580
- usageError("Usage: wh thing graph <wref> [--depth N]", "wh thing graph Game/base");
26622
+ usageError("Usage: wh thing graph <wref> [--depth N]", "wh thing graph Game/base", "wh thing refs Game/base --inbound");
26581
26623
  }
26582
26624
  const depth = flags.depth;
26583
26625
  if (depth !== undefined && (depth < 1 || depth > 5)) {
@@ -26758,8 +26800,8 @@ function validateKind(value, flagName = "--kind") {
26758
26800
  function resolveRepoContext(ctx) {
26759
26801
  return parseOrgRepo(getRepoRef(ctx), ctx.config);
26760
26802
  }
26761
- function validateComponentFilters(componentId, excludeComponents, ...examples) {
26762
- if (componentId && excludeComponents) {
26803
+ function validateComponentFilters(componentRef, excludeComponents, ...examples) {
26804
+ if (componentRef && excludeComponents) {
26763
26805
  usageError("Cannot combine --component with --exclude-components.", ...examples);
26764
26806
  }
26765
26807
  }
@@ -26835,7 +26877,7 @@ var headFlags = {
26835
26877
  description: "Include retracted things"
26836
26878
  }),
26837
26879
  component: flag.string({
26838
- description: "Filter to things owned by this component ID"
26880
+ description: "Filter to things owned by this component (Org/Name ref)"
26839
26881
  }),
26840
26882
  "exclude-components": flag.boolean({
26841
26883
  description: "Exclude component-owned things from results"
@@ -26861,15 +26903,15 @@ var handleHead = async (ctx, { flags, args }) => {
26861
26903
  if (cursor || all || limit || ctx.liveMode) {
26862
26904
  usageError("Usage: wh thing list --count [--shape SHAPE] [--kind KIND] [--match PATTERN]", "wh thing list --shape Player --count");
26863
26905
  }
26864
- const componentId2 = flags.component;
26906
+ const componentRef2 = flags.component;
26865
26907
  const strictExclude2 = !!flags["exclude-components"];
26866
- validateComponentFilters(componentId2, strictExclude2, "wh thing list --component com.example.pkg");
26908
+ validateComponentFilters(componentRef2, strictExclude2, "wh thing list --component acme/veritas");
26867
26909
  return handleCount(ctx, org, repo, {
26868
26910
  shape,
26869
26911
  kind,
26870
26912
  match,
26871
26913
  includeRetracted,
26872
- componentId: componentId2,
26914
+ componentRef: componentRef2,
26873
26915
  excludeComponents: strictExclude2,
26874
26916
  excludeInfraShapes: !strictExclude2 && !shape,
26875
26917
  where: where.length > 0 ? where : undefined
@@ -26883,10 +26925,10 @@ var handleHead = async (ctx, { flags, args }) => {
26883
26925
  }
26884
26926
  const boundedLimit = Math.min(limit ?? DEFAULT_PAGE_LIMIT, MAX_PAGE_LIMIT);
26885
26927
  const pageLimit = all ? Math.min(limit ?? AUTO_PAGE_LIMIT, MAX_PAGE_LIMIT) : boundedLimit;
26886
- const componentId = flags.component;
26928
+ const componentRef = flags.component;
26887
26929
  const hasShape = !!shape;
26888
26930
  const strictExclude = !!flags["exclude-components"];
26889
- validateComponentFilters(componentId, strictExclude, "wh thing list --component com.example.pkg");
26931
+ validateComponentFilters(componentRef, strictExclude, "wh thing list --component acme/veritas");
26890
26932
  const excludeComponents = strictExclude;
26891
26933
  const excludeInfraShapes = !strictExclude && !hasShape;
26892
26934
  const headOpts = {
@@ -26896,7 +26938,7 @@ var handleHead = async (ctx, { flags, args }) => {
26896
26938
  includeRetracted,
26897
26939
  limit: pageLimit,
26898
26940
  cursor,
26899
- componentId,
26941
+ componentRef,
26900
26942
  excludeComponents,
26901
26943
  excludeInfraShapes,
26902
26944
  where: where.length > 0 ? where : undefined
@@ -26928,7 +26970,7 @@ var handleHead = async (ctx, { flags, args }) => {
26928
26970
  includeRetracted,
26929
26971
  limit: pageLimit,
26930
26972
  cursor,
26931
- componentId,
26973
+ componentRef,
26932
26974
  excludeComponents,
26933
26975
  excludeInfraShapes,
26934
26976
  where: where.length > 0 ? where : undefined
@@ -26939,7 +26981,7 @@ var handleHead = async (ctx, { flags, args }) => {
26939
26981
  includeRetracted,
26940
26982
  limit: boundedLimit,
26941
26983
  cursor,
26942
- componentId,
26984
+ componentRef,
26943
26985
  excludeComponents,
26944
26986
  excludeInfraShapes,
26945
26987
  where: where.length > 0 ? where : undefined
@@ -26960,7 +27002,7 @@ async function fetchAllHeadPages(ctx, org, repo, opts) {
26960
27002
  includeRetracted: opts.includeRetracted,
26961
27003
  limit: opts.limit,
26962
27004
  cursor,
26963
- componentId: opts.componentId,
27005
+ componentRef: opts.componentRef,
26964
27006
  excludeComponents: opts.excludeComponents,
26965
27007
  excludeInfraShapes: opts.excludeInfraShapes,
26966
27008
  where: opts.where
@@ -26992,7 +27034,7 @@ var queryFlags = {
26992
27034
  description: "Include assertions about collections (Pair/Triple/Set/List) containing the target. Only applies with --about."
26993
27035
  }),
26994
27036
  component: flag.string({
26995
- description: "Filter to things owned by this component ID"
27037
+ description: "Filter to things owned by this component (Org/Name ref)"
26996
27038
  }),
26997
27039
  "exclude-components": flag.boolean({
26998
27040
  description: "Exclude component-owned things from results"
@@ -27014,10 +27056,10 @@ var handleQuery = async (ctx, { flags }) => {
27014
27056
  const count = flags.count;
27015
27057
  const includeRetracted = flags["include-retracted"];
27016
27058
  const resolveCollections = flags["resolve-collections"];
27017
- const componentId = flags.component;
27059
+ const componentRef = flags.component;
27018
27060
  const hasShape = !!shape;
27019
27061
  const strictExclude = !!flags["exclude-components"];
27020
- validateComponentFilters(componentId, strictExclude, "wh thing query --component com.example.pkg");
27062
+ validateComponentFilters(componentRef, strictExclude, "wh thing query --component acme/veritas");
27021
27063
  const excludeComponents = strictExclude;
27022
27064
  const excludeInfraShapes = !strictExclude && !hasShape;
27023
27065
  const c = ctx.colors;
@@ -27034,7 +27076,7 @@ var handleQuery = async (ctx, { flags }) => {
27034
27076
  about,
27035
27077
  includeRetracted,
27036
27078
  resolveCollections,
27037
- componentId,
27079
+ componentRef,
27038
27080
  excludeComponents,
27039
27081
  excludeInfraShapes,
27040
27082
  where: where.length > 0 ? where : undefined
@@ -27057,7 +27099,7 @@ var handleQuery = async (ctx, { flags }) => {
27057
27099
  resolveCollections,
27058
27100
  limit: pageLimit,
27059
27101
  cursor,
27060
- componentId,
27102
+ componentRef,
27061
27103
  excludeComponents,
27062
27104
  excludeInfraShapes,
27063
27105
  where: where.length > 0 ? where : undefined
@@ -27091,7 +27133,7 @@ var handleQuery = async (ctx, { flags }) => {
27091
27133
  resolveCollections,
27092
27134
  limit: pageLimit,
27093
27135
  cursor,
27094
- componentId,
27136
+ componentRef,
27095
27137
  excludeComponents,
27096
27138
  excludeInfraShapes,
27097
27139
  where: where.length > 0 ? where : undefined
@@ -27104,7 +27146,7 @@ var handleQuery = async (ctx, { flags }) => {
27104
27146
  resolveCollections,
27105
27147
  limit: boundedLimit,
27106
27148
  cursor,
27107
- componentId,
27149
+ componentRef,
27108
27150
  excludeComponents,
27109
27151
  excludeInfraShapes,
27110
27152
  where: where.length > 0 ? where : undefined
@@ -27127,7 +27169,7 @@ async function fetchAllQueryPages(ctx, org, repo, opts) {
27127
27169
  resolveCollections: opts.resolveCollections,
27128
27170
  limit: opts.limit,
27129
27171
  cursor,
27130
- componentId: opts.componentId,
27172
+ componentRef: opts.componentRef,
27131
27173
  excludeComponents: opts.excludeComponents,
27132
27174
  excludeInfraShapes: opts.excludeInfraShapes,
27133
27175
  where: opts.where
@@ -27405,7 +27447,7 @@ var searchFlags = {
27405
27447
  cursor: flag.string({ description: "Opaque pagination cursor (text mode)" }),
27406
27448
  all: flag.boolean({ description: "Fetch all pages (text mode)" }),
27407
27449
  component: flag.string({
27408
- description: "Filter to things owned by this component ID"
27450
+ description: "Filter to things owned by this component (Org/Name ref)"
27409
27451
  }),
27410
27452
  "exclude-components": flag.boolean({
27411
27453
  description: "Exclude component-owned things from results"
@@ -27442,10 +27484,10 @@ var handleSearch = async (ctx, { flags, args }) => {
27442
27484
  const pageLimit = all ? Math.min(limit ?? AUTO_PAGE_LIMIT, MAX_PAGE_LIMIT) : boundedTextLimit;
27443
27485
  const resolveCollections = mode !== "hybrid" ? flags["resolve-collections"] : undefined;
27444
27486
  const supportsComponentFilters = !mode || mode === "text";
27445
- const componentId = supportsComponentFilters ? flags.component : undefined;
27487
+ const componentRef = supportsComponentFilters ? flags.component : undefined;
27446
27488
  const hasShape = !!flags.shape;
27447
27489
  const strictExclude = supportsComponentFilters ? !!flags["exclude-components"] : false;
27448
- validateComponentFilters(componentId, strictExclude, 'wh thing search "policy" --component com.example.pkg');
27490
+ validateComponentFilters(componentRef, strictExclude, 'wh thing search "policy" --component acme/veritas');
27449
27491
  const excludeComponents = supportsComponentFilters && strictExclude ? true : undefined;
27450
27492
  const excludeInfraShapes = supportsComponentFilters && !strictExclude && !hasShape ? true : undefined;
27451
27493
  const rawResult = all ? await fetchAllSearchPages(ctx, org, repo, queryText, {
@@ -27456,7 +27498,7 @@ var handleSearch = async (ctx, { flags, args }) => {
27456
27498
  resolveCollections,
27457
27499
  limit: pageLimit,
27458
27500
  cursor,
27459
- componentId,
27501
+ componentRef,
27460
27502
  excludeComponents,
27461
27503
  excludeInfraShapes
27462
27504
  }) : await ctx.client.thing.search(org, repo, queryText, {
@@ -27468,7 +27510,7 @@ var handleSearch = async (ctx, { flags, args }) => {
27468
27510
  limit: boundedTextLimit,
27469
27511
  cursor,
27470
27512
  mode,
27471
- componentId,
27513
+ componentRef,
27472
27514
  excludeComponents,
27473
27515
  excludeInfraShapes
27474
27516
  });
@@ -27494,7 +27536,7 @@ async function fetchAllSearchPages(ctx, org, repo, queryText, opts) {
27494
27536
  limit: opts.limit,
27495
27537
  cursor,
27496
27538
  mode: "text",
27497
- componentId: opts.componentId,
27539
+ componentRef: opts.componentRef,
27498
27540
  excludeComponents: opts.excludeComponents,
27499
27541
  excludeInfraShapes: opts.excludeInfraShapes
27500
27542
  });
@@ -27847,13 +27889,16 @@ var THING_DOMAIN = defineDomain({
27847
27889
  },
27848
27890
  graph: {
27849
27891
  prime: true,
27850
- summary: "Fetch a thing's graph (forward + reverse references) at a given depth",
27892
+ summary: "Fetch a thing's graph (outbound wref fields, assertion backlinks, and assertion about targets)",
27851
27893
  args: "<wref>",
27852
27894
  flags: graphFlags,
27853
27895
  examples: [
27854
27896
  "wh thing graph Game/base",
27855
27897
  "wh thing graph Game/base --depth 2"
27856
27898
  ],
27899
+ notes: [
27900
+ "`graph` does not traverse inbound wref-field references; use `wh thing refs <wref> --inbound` to find things whose fields point at this thing."
27901
+ ],
27857
27902
  handler: handleThingGraph
27858
27903
  }
27859
27904
  }
@@ -29417,7 +29462,7 @@ function ndjsonMessage(msg) {
29417
29462
  return `${JSON.stringify(msg)}
29418
29463
  `;
29419
29464
  }
29420
- function createMessageReader(input, onMessage) {
29465
+ function createMessageReader(input, onMessage, onError = () => {}) {
29421
29466
  let buffer = "";
29422
29467
  let mode = null;
29423
29468
  function drain() {
@@ -29440,9 +29485,12 @@ function createMessageReader(input, onMessage) {
29440
29485
  continue;
29441
29486
  }
29442
29487
  } else {
29443
- const idx = buffer.indexOf(HEADER_DELIMITER);
29444
- if (idx !== -1) {
29445
- buffer = buffer.slice(idx + 4);
29488
+ const headerEnd = buffer.indexOf(HEADER_DELIMITER);
29489
+ if (headerEnd !== -1) {
29490
+ const resyncFrom = headerEnd + HEADER_DELIMITER.length;
29491
+ const nextHeaderMatch = /Content-Length:/i.exec(buffer.slice(resyncFrom));
29492
+ const nextHeader = nextHeaderMatch === null ? -1 : resyncFrom + nextHeaderMatch.index;
29493
+ buffer = nextHeader === -1 ? "" : buffer.slice(nextHeader);
29446
29494
  continue;
29447
29495
  }
29448
29496
  }
@@ -29462,12 +29510,14 @@ function createMessageReader(input, onMessage) {
29462
29510
  };
29463
29511
  input.setEncoding("utf8");
29464
29512
  input.on("data", onData);
29513
+ input.on("error", onError);
29465
29514
  if ("resume" in input && typeof input.resume === "function") {
29466
29515
  input.resume();
29467
29516
  }
29468
29517
  return {
29469
29518
  close() {
29470
29519
  input.removeListener("data", onData);
29520
+ input.removeListener("error", onError);
29471
29521
  }
29472
29522
  };
29473
29523
  }
@@ -29477,9 +29527,25 @@ var MCP_PROTOCOL_VERSION2 = "2025-11-25";
29477
29527
  function createChannelServer(opts) {
29478
29528
  let reader = null;
29479
29529
  let resolveReady;
29480
- const onReady = new Promise((resolve) => {
29530
+ let rejectReady;
29531
+ let readySettled = false;
29532
+ const onReady = new Promise((resolve, reject) => {
29481
29533
  resolveReady = resolve;
29534
+ rejectReady = reject;
29482
29535
  });
29536
+ function settleReady() {
29537
+ if (readySettled)
29538
+ return;
29539
+ readySettled = true;
29540
+ resolveReady();
29541
+ }
29542
+ function failReady(error) {
29543
+ if (readySettled)
29544
+ return;
29545
+ readySettled = true;
29546
+ reader?.close();
29547
+ rejectReady(error);
29548
+ }
29483
29549
  function send(msg) {
29484
29550
  opts.output.write(ndjsonMessage(msg));
29485
29551
  }
@@ -29502,7 +29568,7 @@ function createChannelServer(opts) {
29502
29568
  return;
29503
29569
  }
29504
29570
  if (method === "notifications/initialized") {
29505
- resolveReady();
29571
+ settleReady();
29506
29572
  return;
29507
29573
  }
29508
29574
  if (method === "ping") {
@@ -29519,7 +29585,7 @@ function createChannelServer(opts) {
29519
29585
  }
29520
29586
  return {
29521
29587
  start() {
29522
- reader = createMessageReader(opts.input, handleMessage);
29588
+ reader = createMessageReader(opts.input, handleMessage, failReady);
29523
29589
  },
29524
29590
  notify(content, meta) {
29525
29591
  send({
@@ -29531,6 +29597,7 @@ function createChannelServer(opts) {
29531
29597
  onReady,
29532
29598
  close() {
29533
29599
  reader?.close();
29600
+ settleReady();
29534
29601
  }
29535
29602
  };
29536
29603
  }
@@ -29593,7 +29660,16 @@ var handleChannel = async (ctx) => {
29593
29660
  else
29594
29661
  signal.addEventListener("abort", () => resolve("aborted"), { once: true });
29595
29662
  });
29596
- const ready = await Promise.race([server.onReady, aborted]);
29663
+ let ready;
29664
+ try {
29665
+ ready = await Promise.race([server.onReady.then(() => {
29666
+ return;
29667
+ }), aborted]);
29668
+ } catch (err) {
29669
+ server.close();
29670
+ const msg = err instanceof Error ? err.message : String(err);
29671
+ throw new CliError(4 /* Backend */, "BACKEND", `channel startup failed: ${msg}`);
29672
+ }
29597
29673
  if (ready === "aborted") {
29598
29674
  server.close();
29599
29675
  return;
@@ -30969,13 +31045,16 @@ async function refreshFromSummaries(repoSlug, parsed, activeItems, client, now,
30969
31045
  const components = {};
30970
31046
  let detailReadFailures = 0;
30971
31047
  for (const item of activeItems) {
31048
+ if (!item.ref)
31049
+ continue;
31050
+ const itemRef = item.ref;
30972
31051
  let detail;
30973
31052
  try {
30974
- detail = await client.component.get(parsed.org, parsed.repo, item.componentId);
31053
+ detail = await client.component.get(parsed.org, parsed.repo, itemRef);
30975
31054
  } catch {
30976
31055
  detailReadFailures++;
30977
31056
  const previousEntry = previousCache?.components[item.componentName];
30978
- if (previousEntry?.componentId === item.componentId) {
31057
+ if (previousEntry?.ref === itemRef) {
30979
31058
  components[item.componentName] = previousEntry;
30980
31059
  }
30981
31060
  continue;
@@ -30990,7 +31069,7 @@ async function refreshFromSummaries(repoSlug, parsed, activeItems, client, now,
30990
31069
  if (!ref)
30991
31070
  continue;
30992
31071
  components[item.componentName] = {
30993
- componentId: item.componentId,
31072
+ ref: itemRef,
30994
31073
  ownerOrgName: ref.ownerOrgName,
30995
31074
  registeredComponentName: ref.registeredComponentName,
30996
31075
  version: manifest.component.version ?? item.version ?? "",
@@ -31350,7 +31429,7 @@ var handleComponentExec = async (ctx, { args, terminator }) => {
31350
31429
  }
31351
31430
  const method = entry.methods.find((m) => m.name === methodName);
31352
31431
  if (!method) {
31353
- throw new CliError(2 /* UserInput */, "USER_INPUT", `Method '${methodName}' not found on component '${componentName}'.`, undefined, `Available: ${entry.methods.map((m) => m.name).join(", ")}. Run wh component update ${componentName} --repo ${installRepo} if you expect a newer method.`);
31432
+ throw new CliError(2 /* UserInput */, "USER_INPUT", `Method '${methodName}' not found on component '${componentName}'.`, undefined, `Available: ${entry.methods.map((m) => m.name).join(", ")}. Run wh component update ${entry.ref} --repo ${installRepo} if you expect a newer method.`);
31354
31433
  }
31355
31434
  if (helpRequested) {
31356
31435
  renderMethodHelp(ctx, componentName, method);
@@ -31459,7 +31538,7 @@ async function ensureSharedInfra(client, org, repo) {
31459
31538
  name: shape.name,
31460
31539
  data: { fields: shape.fields }
31461
31540
  }
31462
- ], { componentId: SYSTEM_COMPONENT_ID });
31541
+ ], { componentRef: SYSTEM_REGISTERED_COMPONENT_REF });
31463
31542
  } catch (err) {
31464
31543
  if (!isAlreadyExistsError(err)) {
31465
31544
  throw err;
@@ -31472,7 +31551,7 @@ async function ensureSharedInfra(client, org, repo) {
31472
31551
  name: shape.name,
31473
31552
  data: { fields: shape.fields }
31474
31553
  }
31475
- ], { componentId: SYSTEM_COMPONENT_ID });
31554
+ ], { componentRef: SYSTEM_REGISTERED_COMPONENT_REF });
31476
31555
  } catch {}
31477
31556
  }
31478
31557
  }
@@ -31505,6 +31584,15 @@ async function doctorComponent(client, org, repo, componentId) {
31505
31584
  };
31506
31585
  }
31507
31586
  const componentName = typeof installData.name === "string" ? installData.name : componentId;
31587
+ const registeredComponentRef = typeof installData.registeredComponentRef === "string" && installData.registeredComponentRef.length > 0 ? installData.registeredComponentRef : undefined;
31588
+ if (!registeredComponentRef) {
31589
+ findings.push({
31590
+ resource: "ComponentInstall",
31591
+ status: "error",
31592
+ message: "Install record is missing registeredComponentRef"
31593
+ });
31594
+ return { componentId, componentName, state: "error", findings };
31595
+ }
31508
31596
  const existingState = typeof installData.state === "string" ? installData.state : undefined;
31509
31597
  if (existingState === "error") {
31510
31598
  findings.push({
@@ -31543,12 +31631,12 @@ async function doctorComponent(client, org, repo, componentId) {
31543
31631
  message: `Shape "${shape.name}" exists but is inactive`
31544
31632
  });
31545
31633
  } else {
31546
- const shapeComponentId = s && typeof s === "object" && "componentId" in s ? s.componentId : undefined;
31547
- if (shapeComponentId !== undefined && shapeComponentId !== componentId) {
31634
+ const shapeComponentRef = s && typeof s === "object" && "componentRef" in s ? s.componentRef : undefined;
31635
+ if (shapeComponentRef !== undefined && shapeComponentRef !== registeredComponentRef) {
31548
31636
  findings.push({
31549
31637
  resource: `shape:${shape.name}`,
31550
31638
  status: "warning",
31551
- message: `Shape "${shape.name}" exists but is owned by "${shapeComponentId}"`
31639
+ message: `Shape "${shape.name}" exists but is owned by "${shapeComponentRef}"`
31552
31640
  });
31553
31641
  } else {
31554
31642
  const expectedShapeData = manifestShapeData(shape);
@@ -31582,19 +31670,19 @@ async function doctorComponent(client, org, repo, componentId) {
31582
31670
  hasSubs = true;
31583
31671
  try {
31584
31672
  const s = await client.subscription.get(org, repo, sub.name);
31585
- const subComponentId = s && typeof s === "object" && "componentId" in s ? s.componentId : undefined;
31586
- if (!s.active) {
31673
+ const subComponentRef = s && typeof s === "object" && "componentRef" in s ? s.componentRef : undefined;
31674
+ if (subComponentRef !== undefined && subComponentRef !== registeredComponentRef) {
31675
+ allSubsPaused = false;
31587
31676
  findings.push({
31588
31677
  resource: `sub:${sub.name}`,
31589
- status: "inactive",
31590
- message: `Subscription "${sub.name}" is paused`
31678
+ status: "warning",
31679
+ message: `Subscription "${sub.name}" is owned by "${subComponentRef}"`
31591
31680
  });
31592
- } else if (subComponentId !== undefined && subComponentId !== componentId) {
31593
- allSubsPaused = false;
31681
+ } else if (!s.active) {
31594
31682
  findings.push({
31595
31683
  resource: `sub:${sub.name}`,
31596
- status: "warning",
31597
- message: `Subscription "${sub.name}" is owned by "${subComponentId}"`
31684
+ status: "inactive",
31685
+ message: `Subscription "${sub.name}" is paused`
31598
31686
  });
31599
31687
  } else {
31600
31688
  allSubsPaused = false;
@@ -31664,10 +31752,11 @@ async function doctorComponent(client, org, repo, componentId) {
31664
31752
  const hasMissing = findings.some((f) => f.status === "missing");
31665
31753
  const hasInactive = findings.some((f) => f.status === "inactive" && !f.resource.startsWith("sub:"));
31666
31754
  const hasShapeDrift = findings.some((f) => f.resource.startsWith("shape:") && f.status === "warning" && f.message.includes("differs from installed manifest"));
31755
+ const hasOwnershipConflict = findings.some((f) => (f.resource.startsWith("shape:") || f.resource.startsWith("sub:")) && f.status === "warning" && f.message.includes("owned by"));
31667
31756
  let state;
31668
- if (hasSubs && allSubsPaused && !hasMissing && !hasInactive && !hasShapeDrift) {
31757
+ if (hasSubs && allSubsPaused && !hasMissing && !hasInactive && !hasShapeDrift && !hasOwnershipConflict) {
31669
31758
  state = "paused";
31670
- } else if (hasMissing || hasInactive || hasShapeDrift) {
31759
+ } else if (hasMissing || hasInactive || hasShapeDrift || hasOwnershipConflict) {
31671
31760
  state = "degraded";
31672
31761
  } else {
31673
31762
  state = "ready";
@@ -31680,7 +31769,7 @@ async function doctorComponent(client, org, repo, componentId) {
31680
31769
  name: `ComponentInstall/${componentId}`,
31681
31770
  data: { ...installData, state, checkedAt: new Date().toISOString() }
31682
31771
  }
31683
- ], { componentId: SYSTEM_COMPONENT_ID });
31772
+ ], { componentRef: SYSTEM_REGISTERED_COMPONENT_REF });
31684
31773
  } catch {
31685
31774
  findings.push({
31686
31775
  resource: "state-update",
@@ -31774,6 +31863,22 @@ async function teardownComponent(client, org, repo, componentId) {
31774
31863
  };
31775
31864
  }
31776
31865
  const componentName = typeof installData.name === "string" ? installData.name : componentId;
31866
+ const registeredComponentRef = typeof installData.registeredComponentRef === "string" && installData.registeredComponentRef.length > 0 ? installData.registeredComponentRef : undefined;
31867
+ if (!registeredComponentRef) {
31868
+ return {
31869
+ componentId,
31870
+ componentName,
31871
+ state: "error",
31872
+ steps: [
31873
+ {
31874
+ step: "load-install",
31875
+ status: "error",
31876
+ message: "Install record is missing registeredComponentRef"
31877
+ }
31878
+ ],
31879
+ errors: ["Install record is missing registeredComponentRef"]
31880
+ };
31881
+ }
31777
31882
  let subscriptionNames = [];
31778
31883
  let teardownPolicy = {};
31779
31884
  try {
@@ -31796,9 +31901,9 @@ async function teardownComponent(client, org, repo, componentId) {
31796
31901
  for (const subName of subscriptionNames) {
31797
31902
  try {
31798
31903
  const sub = await client.subscription.get(org, repo, subName);
31799
- const ownerComponentId = sub && typeof sub === "object" && "componentId" in sub ? sub.componentId : undefined;
31800
- if (typeof ownerComponentId === "string" && ownerComponentId !== componentId) {
31801
- const msg = `Subscription "${subName}" is owned by "${ownerComponentId}", not "${componentId}"`;
31904
+ const ownerComponentRef = sub && typeof sub === "object" && "componentRef" in sub ? sub.componentRef : undefined;
31905
+ if (typeof ownerComponentRef === "string" && ownerComponentRef !== registeredComponentRef) {
31906
+ const msg = `Subscription "${subName}" is owned by "${ownerComponentRef}", not "${registeredComponentRef}"`;
31802
31907
  errors.push(msg);
31803
31908
  steps.push({
31804
31909
  step: `pause-${subName}`,
@@ -31847,7 +31952,7 @@ async function teardownComponent(client, org, repo, componentId) {
31847
31952
  updatedAt: new Date().toISOString()
31848
31953
  }
31849
31954
  }
31850
- ], { componentId: SYSTEM_COMPONENT_ID });
31955
+ ], { componentRef: SYSTEM_REGISTERED_COMPONENT_REF });
31851
31956
  steps.push({ step: "update-state", status: "ok" });
31852
31957
  } catch (err) {
31853
31958
  const msg = err instanceof Error ? err.message : String(err);
@@ -32069,38 +32174,11 @@ function componentInstallCanonicalId(item) {
32069
32174
  const data = asRecord(item.data);
32070
32175
  return componentInstallIdFromWref(item.wref) ?? readString(data, "id") ?? readString(data, "componentId") ?? item.name;
32071
32176
  }
32072
- function uniqueStrings(values) {
32073
- return [...new Set(values.filter((value) => Boolean(value)))];
32074
- }
32075
- function componentInstallIdentityIds(item) {
32076
- const data = asRecord(item.data);
32077
- return uniqueStrings([
32078
- componentInstallCanonicalId(item),
32079
- readString(data, "id"),
32080
- readString(data, "componentId"),
32081
- item.name
32082
- ]);
32083
- }
32084
- function componentInstallHasExactId(item, name) {
32085
- return componentInstallIdentityIds(item).includes(name);
32177
+ function componentInstallHasRegisteredRef(item, ref) {
32178
+ return readString(asRecord(item.data), "registeredComponentRef") === ref;
32086
32179
  }
32087
- function componentInstallHasDisplayName(item, name) {
32088
- return readString(asRecord(item.data), "name") === name;
32089
- }
32090
- function formatComponentInstallAmbiguity(name, matches) {
32091
- const componentIds = uniqueStrings(matches.map((item) => componentInstallCanonicalId(item)));
32092
- const listedIds = componentIds.map((id) => `- ${id}`).join(`
32093
- `);
32094
- const exampleId = componentIds[0] ?? name;
32095
- return {
32096
- message: `Component name "${name}" is ambiguous. It matches multiple installed components:
32097
- ${listedIds}`,
32098
- hint: `Use a component id instead, for example: wh component doctor ${exampleId}`
32099
- };
32100
- }
32101
- async function resolveComponentDoctorId(client, org, repo, name) {
32180
+ async function resolveInstalledComponentRefId(client, org, repo, ref) {
32102
32181
  let cursor;
32103
- const displayNameMatches = [];
32104
32182
  do {
32105
32183
  const result = await client.thing.head(org, repo, {
32106
32184
  shape: "ComponentInstall",
@@ -32109,35 +32187,27 @@ async function resolveComponentDoctorId(client, org, repo, name) {
32109
32187
  cursor
32110
32188
  });
32111
32189
  const items = result.items ?? [];
32112
- const exactMatch = items.find((item) => componentInstallHasExactId(item, name));
32190
+ const exactMatch = items.find((item) => componentInstallHasRegisteredRef(item, ref));
32113
32191
  const resolved = exactMatch ? componentInstallCanonicalId(exactMatch) : undefined;
32114
32192
  if (resolved) {
32115
32193
  return resolved;
32116
32194
  }
32117
- displayNameMatches.push(...items.filter((item) => componentInstallHasDisplayName(item, name)));
32118
32195
  cursor = result.nextCursor;
32119
32196
  } while (cursor);
32120
- if (displayNameMatches.length === 1) {
32121
- return componentInstallCanonicalId(displayNameMatches[0]) ?? name;
32122
- }
32123
- if (displayNameMatches.length > 1) {
32124
- const { message, hint } = formatComponentInstallAmbiguity(name, displayNameMatches);
32125
- throw new CliError(2 /* UserInput */, "USER_INPUT", message, undefined, hint);
32126
- }
32127
- return name;
32197
+ throw new CliError(2 /* UserInput */, "USER_INPUT", `Component '${ref}' is not installed. Run 'wh component install <org>/<name>' first.`);
32128
32198
  }
32129
32199
  var handleUpdate = async (ctx, { args }) => {
32130
- const name = args[0];
32131
- if (!name) {
32132
- usageError("Usage: wh component update <name> --repo org/repo", "wh component update research-knowledge --repo org/repo");
32200
+ const ref = args[0];
32201
+ const usage = "Usage: wh component update <org/name> --repo org/repo";
32202
+ const example = "wh component update warmhub/research-knowledge --repo org/repo";
32203
+ if (!ref) {
32204
+ usageError(usage, example);
32133
32205
  }
32134
- const { org, repo } = parseOrgRepo(getRepoRef(ctx), ctx.config);
32135
- try {
32136
- await ctx.client.thing.get(org, repo, `ComponentInstall/${name}`);
32137
- } catch {
32138
- throw new CliError(2 /* UserInput */, "USER_INPUT", `Component '${name}' is not installed. Run 'wh component install <org>/<name>' first.`);
32206
+ resolveRegisteredComponentRef(ref, usage, example);
32207
+ if (ctx.liveMode === false) {
32208
+ usageError("Dry-run is not supported for component update.", "wh component update warmhub/research-knowledge --repo org/repo");
32139
32209
  }
32140
- throw new CliError(2 /* UserInput */, "USER_INPUT", `Component '${name}' was not installed from a registered component and can no longer be updated in place.`, undefined, "Register the component (`wh component register <name> --org <org> --manifest <path>`) and reinstall it with `wh component install <org>/<name>`.");
32210
+ throw new CliError(2 /* UserInput */, "USER_INPUT", `Component '${ref}' could not be updated through the component shell boundary.`, undefined, "Run `wh component update <org/name> --repo org/repo` without --dry-run.");
32141
32211
  };
32142
32212
  var handleRegister = async (ctx, { flags, args }) => {
32143
32213
  const componentName = args[0];
@@ -32230,8 +32300,7 @@ var handleRegistryUpdate = async (ctx, { flags, args }) => {
32230
32300
  };
32231
32301
  var handleInstall = async (ctx, { args }) => {
32232
32302
  const source = args[0];
32233
- const usage = `Usage: wh component install <source> --repo org/repo
32234
- source: <org>/<name> (registered component)`;
32303
+ const usage = "Usage: wh component install <org/name> --repo org/repo";
32235
32304
  const example = "wh component install warmhub/veritas --repo myorg/myrepo";
32236
32305
  if (!source) {
32237
32306
  usageError(usage, example);
@@ -32276,7 +32345,7 @@ var handleList2 = async (ctx, { flags }) => {
32276
32345
  const state = item.state ?? "unknown";
32277
32346
  const source = item.source ?? "-";
32278
32347
  const stateColor = stateToColor(c, state);
32279
- ctx.out(` ${c.cyan}${item.componentId}${c.reset} ${stateColor}${state}${c.reset} ${c.dim}v${version}${c.reset} ${source}`);
32348
+ ctx.out(` ${c.cyan}${item.ref ?? item.componentName}${c.reset} ${stateColor}${state}${c.reset} ${c.dim}v${version}${c.reset} ${source}`);
32280
32349
  }
32281
32350
  });
32282
32351
  };
@@ -32289,7 +32358,7 @@ var handleSearch2 = async (ctx, { flags, args }) => {
32289
32358
  const c = ctx.colors;
32290
32359
  const { items: allItems, nextCursor } = await fetchComponentPages(ctx.client, org, repo, { all: flags.all, limit: flags.limit, cursor: flags.cursor });
32291
32360
  const needle = query.toLowerCase();
32292
- const items = allItems.filter((item) => [item.componentId, item.componentName, item.source].filter((value) => typeof value === "string").some((value) => value.toLowerCase().includes(needle)));
32361
+ const items = allItems.filter((item) => [item.ref, item.componentName, item.source].filter((value) => typeof value === "string").some((value) => value.toLowerCase().includes(needle)));
32293
32362
  if (!flags.all && nextCursor) {
32294
32363
  ctx.status(`${c.yellow}${allItems.length} component(s) searched, more pages available${c.reset} — matches beyond this page were not searched. ` + `Use ${c.cyan}--all${c.reset} to search every page, ` + `or re-run the same command plus ${c.cyan}--cursor=${nextCursor}${c.reset} to continue from here.`);
32295
32364
  }
@@ -32305,22 +32374,22 @@ var handleSearch2 = async (ctx, { flags, args }) => {
32305
32374
  const state = item.state ?? "unknown";
32306
32375
  const source = item.source ?? "-";
32307
32376
  const stateColor = stateToColor(c, state);
32308
- ctx.out(` ${c.cyan}${item.componentId}${c.reset} ${stateColor}${state}${c.reset} ${c.dim}v${version}${c.reset} ${source}`);
32377
+ ctx.out(` ${c.cyan}${item.ref ?? item.componentName}${c.reset} ${stateColor}${state}${c.reset} ${c.dim}v${version}${c.reset} ${source}`);
32309
32378
  }
32310
32379
  });
32311
32380
  };
32312
32381
  var handleView3 = async (ctx, { args }) => {
32313
- const name = args[0];
32314
- const usage = "Usage: wh component view <name> --repo org/repo";
32315
- const example = "wh component view research-knowledge --repo myorg/myrepo";
32316
- if (!name) {
32382
+ const ref = args[0];
32383
+ const usage = "Usage: wh component view <org/name> --repo org/repo";
32384
+ const example = "wh component view myorg/research-knowledge --repo myorg/myrepo";
32385
+ if (!ref) {
32317
32386
  usageError(usage, example);
32318
32387
  }
32319
32388
  const { org, repo } = parseOrgRepo(getRepoRef(ctx), ctx.config);
32320
- const viewData = await ctx.client.component.get(org, repo, name);
32389
+ const viewData = await ctx.client.component.get(org, repo, ref);
32321
32390
  writeOutput(ctx, viewData, () => {
32322
32391
  const c = ctx.colors;
32323
- ctx.out(`${c.bold}Component:${c.reset} ${c.cyan}${name}${c.reset}`);
32392
+ ctx.out(`${c.bold}Component:${c.reset} ${c.cyan}${viewData.ref ?? ref}${c.reset}`);
32324
32393
  ctx.out(`${c.bold}Version:${c.reset} ${viewData.version ?? "unknown"}`);
32325
32394
  ctx.out(`${c.bold}State:${c.reset} ${viewData.state ?? "-"}`);
32326
32395
  ctx.out(`${c.bold}Source:${c.reset} ${viewData.source ?? "-"}`);
@@ -32396,23 +32465,24 @@ function renderValidationResult(ctx, result) {
32396
32465
  var handleDoctor = async (ctx, { args }) => {
32397
32466
  const name = args[0];
32398
32467
  if (!name) {
32399
- usageError("Usage: wh component doctor <name> --repo org/repo", "wh component doctor research-knowledge --repo myorg/myrepo");
32468
+ usageError("Usage: wh component doctor <org/name> --repo org/repo", "wh component doctor warmhub/research-knowledge --repo myorg/myrepo");
32400
32469
  }
32401
32470
  const { org, repo } = parseOrgRepo(getRepoRef(ctx), ctx.config);
32402
- const componentId = await resolveComponentDoctorId(ctx.client, org, repo, name);
32471
+ const componentId = await resolveInstalledComponentRefId(ctx.client, org, repo, name);
32403
32472
  const result = await doctorComponent(ctx.client, org, repo, componentId);
32404
32473
  writeOutput(ctx, result, () => renderDoctorResult(ctx, result));
32405
32474
  };
32406
32475
  var handleTeardown = async (ctx, { args }) => {
32407
32476
  const name = args[0];
32408
32477
  if (!name) {
32409
- usageError("Usage: wh component teardown <name> --repo org/repo", "wh component teardown research-knowledge --repo myorg/myrepo");
32478
+ usageError("Usage: wh component teardown <org/name> --repo org/repo", "wh component teardown warmhub/research-knowledge --repo myorg/myrepo");
32410
32479
  }
32411
32480
  const { org, repo } = parseOrgRepo(getRepoRef(ctx), ctx.config);
32412
32481
  const repoSlug = `${org}/${repo}`;
32482
+ const componentId = await resolveInstalledComponentRefId(ctx.client, org, repo, name);
32413
32483
  let result;
32414
32484
  try {
32415
- result = await teardownComponent(ctx.client, org, repo, name);
32485
+ result = await teardownComponent(ctx.client, org, repo, componentId);
32416
32486
  } catch (error) {
32417
32487
  invalidateInstallSnapshotCache(repoSlug);
32418
32488
  throw error;
@@ -32543,7 +32613,7 @@ var COMPONENT_DOMAIN = defineDomain({
32543
32613
  install: {
32544
32614
  prime: true,
32545
32615
  summary: "Install a registered component by its <org>/<name> ref",
32546
- args: "<source>",
32616
+ args: "<org/name>",
32547
32617
  flags: installFlags,
32548
32618
  examples: [
32549
32619
  "wh component install warmhub/identity --repo org/repo",
@@ -32553,9 +32623,9 @@ var COMPONENT_DOMAIN = defineDomain({
32553
32623
  },
32554
32624
  update: {
32555
32625
  summary: "Update an installed registered component to the latest manifest",
32556
- args: "<name>",
32626
+ args: "<org/name>",
32557
32627
  flags: updateFlags,
32558
- examples: ["wh component update research-knowledge --repo org/repo"],
32628
+ examples: ["wh component update warmhub/identity --repo org/repo"],
32559
32629
  handler: handleUpdate
32560
32630
  },
32561
32631
  list: {
@@ -32569,25 +32639,25 @@ var COMPONENT_DOMAIN = defineDomain({
32569
32639
  view: {
32570
32640
  prime: true,
32571
32641
  summary: "Show component details",
32572
- args: "<name>",
32642
+ args: "<org/name>",
32573
32643
  flags: viewFlags3,
32574
- examples: ["wh component view research-knowledge --repo org/repo"],
32644
+ examples: ["wh component view warmhub/identity --repo org/repo"],
32575
32645
  handler: handleView3
32576
32646
  },
32577
32647
  teardown: {
32578
32648
  prime: true,
32579
32649
  summary: "Pause component subscriptions",
32580
- args: "<name>",
32650
+ args: "<org/name>",
32581
32651
  flags: teardownFlags,
32582
- examples: ["wh component teardown research-knowledge --repo org/repo"],
32652
+ examples: ["wh component teardown warmhub/identity --repo org/repo"],
32583
32653
  handler: handleTeardown
32584
32654
  },
32585
32655
  doctor: {
32586
32656
  prime: true,
32587
32657
  summary: "Run component health checks",
32588
- args: "<name>",
32658
+ args: "<org/name>",
32589
32659
  flags: doctorFlags,
32590
- examples: ["wh component doctor research-knowledge --repo org/repo"],
32660
+ examples: ["wh component doctor warmhub/identity --repo org/repo"],
32591
32661
  handler: handleDoctor
32592
32662
  },
32593
32663
  init: {
@@ -33035,7 +33105,7 @@ import { basename } from "node:path";
33035
33105
  // ../../packages/warmhub-cli/src/harness.ts
33036
33106
  import { mkdir, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
33037
33107
  import { homedir as homedir4 } from "node:os";
33038
- import { dirname as dirname6, join as join6 } from "node:path";
33108
+ import { dirname as dirname6, join as join5 } from "node:path";
33039
33109
  var AGENTS_BEGIN_MARKER = "<!-- BEGIN WARMHUB CLI INTEGRATION -->";
33040
33110
  var AGENTS_END_MARKER = "<!-- END WARMHUB CLI INTEGRATION -->";
33041
33111
  var PRIME_COMMAND = "wh prime";
@@ -33055,10 +33125,10 @@ function getHarnessPaths(opts) {
33055
33125
  const userHome = opts?.homeDir ?? homedir4();
33056
33126
  return {
33057
33127
  repoDir,
33058
- agentsPath: join6(repoDir, "AGENTS.md"),
33059
- projectSettingsPath: join6(repoDir, ".claude", "settings.json"),
33060
- projectLocalSettingsPath: join6(repoDir, ".claude", "settings.local.json"),
33061
- globalSettingsPath: join6(userHome, ".claude", "settings.json")
33128
+ agentsPath: join5(repoDir, "AGENTS.md"),
33129
+ projectSettingsPath: join5(repoDir, ".claude", "settings.json"),
33130
+ projectLocalSettingsPath: join5(repoDir, ".claude", "settings.local.json"),
33131
+ globalSettingsPath: join5(userHome, ".claude", "settings.json")
33062
33132
  };
33063
33133
  }
33064
33134
  async function readJsonFile(path2) {
@@ -34459,17 +34529,18 @@ cat wrefs.txt | wh thing view --format jsonl # one JSON line per *deduped
34459
34529
  - \`wh credential revoke <setName> [--repo org/repo | --org org] [--reason]\` — Revoke a credential set (blocks new binds and strips auth from existing webhook deliveries)
34460
34530
 
34461
34531
  ### component — Component management
34462
- - \`wh component validate <path>\` — Validate a component package from disk
34463
- - \`wh component install <source>\` — Install a component from a bundled system id or a registered \`<org>/<name>\`
34464
- - \`wh component register <name> --org <org> --manifest <path> [flags]\` — Register a component identity (first manifest version) for \`install <org/name>\`
34532
+ - \`wh component validate <path>\` — Validate package
34533
+ - \`wh component install <org/name>\` — Install a registered component
34534
+ - \`wh component register <name> --org <org> --manifest <path> [flags]\` — Register component identity
34465
34535
  - \`wh component unregister <org/name>\` — Remove a registered component identity
34466
34536
  - \`wh component registry list --org <org>\` — List registered components
34467
34537
  - \`wh component registry view <org/name>\` — View a registered component
34468
34538
  - \`wh component registry update <org/name> [flags]\` — Update a registered component
34469
34539
  - \`wh component list\` — List installed components
34470
- - \`wh component view <name>\` — Show component details (alias: show)
34471
- - \`wh component doctor <name>\` — Run component health checks
34472
- - \`wh component teardown <name>\` — Pause component subscriptions
34540
+ - \`wh component update <org/name>\` — Update installed component
34541
+ - \`wh component view <org/name>\` — Show component details (alias: show)
34542
+ - \`wh component doctor <org/name>\` — Run component health checks
34543
+ - \`wh component teardown <org/name>\` — Pause component subscriptions
34473
34544
 
34474
34545
  ### Getting More Info
34475
34546
  - \`wh help\` — full help overview
@@ -35481,7 +35552,7 @@ var FIELD_CONSTRAINTS_NOTES = [
35481
35552
  var listFlags4 = {
35482
35553
  match: flag.string({ description: "Filter by name glob pattern" }),
35483
35554
  component: flag.string({
35484
- description: "Filter to shapes owned by this component ID"
35555
+ description: "Filter to shapes owned by this component (Org/Name ref)"
35485
35556
  }),
35486
35557
  "exclude-components": flag.boolean({
35487
35558
  description: "Exclude component-owned shapes from results"
@@ -35494,13 +35565,13 @@ var handleList6 = async (ctx, { flags }) => {
35494
35565
  const { org, repo } = parseOrgRepo(getRepoRef(ctx), ctx.config);
35495
35566
  const c = ctx.colors;
35496
35567
  const match = flags.match;
35497
- const componentId = flags.component;
35568
+ const componentRef = flags.component;
35498
35569
  const excludeComponents = !!flags["exclude-components"];
35499
35570
  const includeRetracted = flags["include-retracted"];
35500
- validateComponentFilters(componentId, excludeComponents, "wh shape list --component com.example.pkg", "wh shape list --exclude-components");
35571
+ validateComponentFilters(componentRef, excludeComponents, "wh shape list --component acme/veritas", "wh shape list --exclude-components");
35501
35572
  const result = await ctx.client.shape.list(org, repo, {
35502
35573
  match,
35503
- componentId,
35574
+ componentRef,
35504
35575
  excludeComponents,
35505
35576
  includeRetracted
35506
35577
  });
@@ -35567,9 +35638,9 @@ var handleView7 = async (ctx, { flags, args }) => {
35567
35638
  if (typeof shapeDescription === "string" && shapeDescription) {
35568
35639
  ctx.out(` ${c.dim}${shapeDescription}${c.reset}`);
35569
35640
  }
35570
- const cid = result.componentId;
35571
- if (cid) {
35572
- ctx.out(` ${c.dim}owner:${c.reset} ${c.yellow}${cid}${c.reset}`);
35641
+ const componentRef = result.componentRef;
35642
+ if (componentRef) {
35643
+ ctx.out(` ${c.dim}owner:${c.reset} ${c.yellow}${componentRef}${c.reset}`);
35573
35644
  }
35574
35645
  const fields = shapeData?.fields;
35575
35646
  if (fields) {
@@ -37869,10 +37940,6 @@ async function dispatch(ctx) {
37869
37940
  const { command } = ctx.invocation;
37870
37941
  if (!command || command === "help") {
37871
37942
  const helpTarget = command === "help" ? ctx.invocation.positional[0] : undefined;
37872
- if (ctx.format === "json") {
37873
- printJson(ctx.out, buildCliSpec());
37874
- return;
37875
- }
37876
37943
  if (helpTarget) {
37877
37944
  const domain = registry.getDomain(helpTarget);
37878
37945
  if (domain) {
@@ -37881,6 +37948,10 @@ async function dispatch(ctx) {
37881
37948
  throw new CliError(2 /* UserInput */, "USER_INPUT", `Unknown command: ${helpTarget}`, undefined, "Run 'wh help' for available commands.");
37882
37949
  }
37883
37950
  } else {
37951
+ if (ctx.format === "json") {
37952
+ printJson(ctx.out, buildCliSpec());
37953
+ return;
37954
+ }
37884
37955
  let repoSlug;
37885
37956
  try {
37886
37957
  const { org, repo } = parseOrgRepo(getRepoRef(ctx), ctx.config);
@@ -37932,6 +38003,7 @@ async function reconcileComponent(client, org, repo, pkg, source, existingInstal
37932
38003
  const errors = [];
37933
38004
  const { meta, manifest } = pkg;
37934
38005
  const componentId = meta.id;
38006
+ const componentRef = source.registeredComponentRef;
37935
38007
  const validation = validateComponentPackage(pkg);
37936
38008
  if (!validation.valid) {
37937
38009
  const validationErrors = validation.findings.filter((f) => f.level === "error").map((f) => f.message);
@@ -37982,7 +38054,7 @@ async function reconcileComponent(client, org, repo, pkg, source, existingInstal
37982
38054
  const oldCredNames = wasIncomplete ? new Set : new Set(oldManifest?.credentials?.map((c) => c.name) ?? []);
37983
38055
  const liveShapeNames = new Set;
37984
38056
  const liveShapeDataByName = new Map;
37985
- const liveShapeComponentIdByName = new Map;
38057
+ const liveShapeComponentRefByName = new Map;
37986
38058
  for (const shape of reconcileShapes) {
37987
38059
  if (isFailedInstallState || previousState !== "degraded" && !oldShapeNames.has(shape.name)) {
37988
38060
  continue;
@@ -37999,7 +38071,7 @@ async function reconcileComponent(client, org, repo, pkg, source, existingInstal
37999
38071
  includeRetracted: true
38000
38072
  });
38001
38073
  liveShapeDataByName.set(shape.name, liveShape.version?.data);
38002
- liveShapeComponentIdByName.set(shape.name, liveShape.componentId);
38074
+ liveShapeComponentRefByName.set(shape.name, liveShape.componentRef);
38003
38075
  } catch {}
38004
38076
  } catch {}
38005
38077
  }
@@ -38034,7 +38106,7 @@ async function reconcileComponent(client, org, repo, pkg, source, existingInstal
38034
38106
  data: manifestShapeData(shape)
38035
38107
  }));
38036
38108
  try {
38037
- await client.commit.apply(org, repo, `Add new shapes for ${meta.name}`, shapeOps, { componentId });
38109
+ await client.commit.apply(org, repo, `Add new shapes for ${meta.name}`, shapeOps, { componentRef });
38038
38110
  steps.push({ step: "add-new-shapes", status: "ok" });
38039
38111
  } catch (err) {
38040
38112
  const msg = err instanceof Error ? err.message : String(err);
@@ -38050,8 +38122,8 @@ async function reconcileComponent(client, org, repo, pkg, source, existingInstal
38050
38122
  if (!liveShapeNames.has(shape.name))
38051
38123
  return false;
38052
38124
  const liveData = liveShapeDataByName.get(shape.name);
38053
- const liveComponentId = liveShapeComponentIdByName.get(shape.name);
38054
- return liveComponentId !== componentId || !shapeDataEquals(manifestShapeData(shape), liveData);
38125
+ const liveComponentRef = liveShapeComponentRefByName.get(shape.name);
38126
+ return liveComponentRef !== componentRef || !shapeDataEquals(manifestShapeData(shape), liveData);
38055
38127
  });
38056
38128
  if (changedShapes.length > 0) {
38057
38129
  const shapeOps = changedShapes.map((shape) => ({
@@ -38061,7 +38133,7 @@ async function reconcileComponent(client, org, repo, pkg, source, existingInstal
38061
38133
  data: manifestShapeData(shape)
38062
38134
  }));
38063
38135
  try {
38064
- await client.commit.apply(org, repo, `Reconcile shapes for ${meta.name}`, shapeOps, { componentId });
38136
+ await client.commit.apply(org, repo, `Reconcile shapes for ${meta.name}`, shapeOps, { componentRef });
38065
38137
  steps.push({ step: "claim-existing-shapes", status: "ok" });
38066
38138
  } catch (err) {
38067
38139
  const msg = err instanceof Error ? err.message : String(err);
@@ -38127,7 +38199,7 @@ async function reconcileComponent(client, org, repo, pkg, source, existingInstal
38127
38199
  filterJson: compiled.filterJson,
38128
38200
  webhookUrl: compiled.webhookUrl,
38129
38201
  fallbackWebhookUrl: compiled.fallbackWebhookUrl,
38130
- componentId
38202
+ componentRef
38131
38203
  });
38132
38204
  steps.push({ step: `add-sub-${sub.name}`, status: "ok" });
38133
38205
  } catch (err) {
@@ -38166,8 +38238,8 @@ async function reconcileComponent(client, org, repo, pkg, source, existingInstal
38166
38238
  }
38167
38239
  try {
38168
38240
  const liveSub = await client.subscription.get(org, repo, sub.name);
38169
- if (liveSub.componentId && liveSub.componentId !== componentId) {
38170
- const msg = `Subscription "${sub.name}" is owned by component "${liveSub.componentId}"`;
38241
+ if (liveSub.componentRef && liveSub.componentRef !== componentRef) {
38242
+ const msg = `Subscription "${sub.name}" is owned by component "${liveSub.componentRef}"`;
38171
38243
  errors.push(msg);
38172
38244
  steps.push({ step: stepName, status: "error", message: msg });
38173
38245
  continue;
@@ -38185,7 +38257,7 @@ async function reconcileComponent(client, org, repo, pkg, source, existingInstal
38185
38257
  filterJson: compiled.filterJson,
38186
38258
  webhookUrl: compiled.webhookUrl,
38187
38259
  fallbackWebhookUrl: compiled.fallbackWebhookUrl,
38188
- componentId
38260
+ componentRef
38189
38261
  });
38190
38262
  } catch (createErr) {
38191
38263
  const createMsg = createErr instanceof Error ? createErr.message : String(createErr);
@@ -38242,7 +38314,7 @@ async function reconcileComponent(client, org, repo, pkg, source, existingInstal
38242
38314
  name: `${seed.shape}/${seed.name}`,
38243
38315
  data: seed.data
38244
38316
  }
38245
- ], { componentId });
38317
+ ], { componentRef });
38246
38318
  } catch (reviseErr) {
38247
38319
  const reviseMsg = reviseErr instanceof Error ? reviseErr.message : String(reviseErr);
38248
38320
  const reviseLooksMissing = reviseMsg.includes("not found") || reviseMsg.includes("NOT_FOUND");
@@ -38258,7 +38330,7 @@ async function reconcileComponent(client, org, repo, pkg, source, existingInstal
38258
38330
  name: `${seed.shape}/${seed.name}`,
38259
38331
  data: seed.data
38260
38332
  }
38261
- ], { componentId });
38333
+ ], { componentRef });
38262
38334
  } catch (addErr) {
38263
38335
  const msg = addErr instanceof Error ? addErr.message : String(addErr);
38264
38336
  if (!isAlreadyExistsError(addErr)) {
@@ -38283,8 +38355,8 @@ async function reconcileComponent(client, org, repo, pkg, source, existingInstal
38283
38355
  componentId,
38284
38356
  name: meta.name,
38285
38357
  version: meta.version,
38286
- source: source.githubUrl ?? "local",
38287
- sourceKind: source.sourceKind ?? (source.githubUrl ? "github" : "local"),
38358
+ source: "registry",
38359
+ sourceKind: "registered",
38288
38360
  sourceRef: source.ref ?? "",
38289
38361
  resolvedSha: source.resolvedSha ?? "",
38290
38362
  ...source.registeredComponentRef ? { registeredComponentRef: source.registeredComponentRef } : {},
@@ -38296,7 +38368,7 @@ async function reconcileComponent(client, org, repo, pkg, source, existingInstal
38296
38368
  ...source.installId ?? persistedInstallId ? { installId: source.installId ?? persistedInstallId } : {}
38297
38369
  }
38298
38370
  }
38299
- ], { componentId: SYSTEM_COMPONENT_ID });
38371
+ ], { componentRef: SYSTEM_REGISTERED_COMPONENT_REF });
38300
38372
  steps.push({ step: "update-install-record", status: "ok" });
38301
38373
  } catch (err) {
38302
38374
  const msg = err instanceof Error ? err.message : String(err);
@@ -38411,8 +38483,8 @@ async function installComponent(client, org, repo, pkg, source) {
38411
38483
  componentId,
38412
38484
  name: meta.name,
38413
38485
  version: meta.version,
38414
- source: source.githubUrl ?? "local",
38415
- sourceKind: source.sourceKind ?? (source.githubUrl ? "github" : "local"),
38486
+ source: "registry",
38487
+ sourceKind: "registered",
38416
38488
  sourceRef: source.ref ?? "",
38417
38489
  resolvedSha: source.resolvedSha ?? "",
38418
38490
  ...source.registeredComponentRef ? { registeredComponentRef: source.registeredComponentRef } : {},
@@ -38423,7 +38495,7 @@ async function installComponent(client, org, repo, pkg, source) {
38423
38495
  ...source.installId ? { installId: source.installId } : {}
38424
38496
  }
38425
38497
  }
38426
- ], { componentId: SYSTEM_COMPONENT_ID });
38498
+ ], { componentRef: SYSTEM_REGISTERED_COMPONENT_REF });
38427
38499
  steps.push({ step: "create-install-record", status: "ok" });
38428
38500
  } catch (err) {
38429
38501
  const msg = err instanceof Error ? err.message : String(err);
@@ -38439,13 +38511,13 @@ async function installComponent(client, org, repo, pkg, source) {
38439
38511
  data: manifestShapeData(shape)
38440
38512
  }));
38441
38513
  try {
38442
- await client.commit.apply(org, repo, `Add shapes for ${meta.name}`, shapeOps, { componentId });
38514
+ await client.commit.apply(org, repo, `Add shapes for ${meta.name}`, shapeOps, { componentRef: source.registeredComponentRef });
38443
38515
  steps.push({ step: "create-shapes", status: "ok" });
38444
38516
  } catch (err) {
38445
38517
  const msg = err instanceof Error ? err.message : String(err);
38446
38518
  if (isAlreadyExistsError(err)) {
38447
38519
  try {
38448
- await reconcileShapesAfterCreateConflict(client, org, repo, pkg);
38520
+ await reconcileShapesAfterCreateConflict(client, org, repo, pkg, source);
38449
38521
  steps.push({
38450
38522
  step: "create-shapes",
38451
38523
  status: "ok",
@@ -38522,7 +38594,7 @@ async function installComponent(client, org, repo, pkg, source) {
38522
38594
  filterJson: compiled.filterJson,
38523
38595
  webhookUrl: compiled.webhookUrl,
38524
38596
  fallbackWebhookUrl: compiled.fallbackWebhookUrl,
38525
- componentId
38597
+ componentRef: source.registeredComponentRef
38526
38598
  });
38527
38599
  steps.push({ step: `create-sub-${sub.name}`, status: "ok" });
38528
38600
  } catch (err) {
@@ -38561,7 +38633,7 @@ async function installComponent(client, org, repo, pkg, source) {
38561
38633
  name: seedName,
38562
38634
  data: seed.data
38563
38635
  }
38564
- ], { componentId });
38636
+ ], { componentRef: source.registeredComponentRef });
38565
38637
  steps.push({ step: `seed-${seedName}`, status: "ok" });
38566
38638
  } catch (err) {
38567
38639
  const msg = err instanceof Error ? err.message : String(err);
@@ -38588,8 +38660,8 @@ async function installComponent(client, org, repo, pkg, source) {
38588
38660
  componentId,
38589
38661
  name: meta.name,
38590
38662
  version: meta.version,
38591
- source: source.githubUrl ?? "local",
38592
- sourceKind: source.sourceKind ?? (source.githubUrl ? "github" : "local"),
38663
+ source: "registry",
38664
+ sourceKind: "registered",
38593
38665
  sourceRef: source.ref ?? "",
38594
38666
  resolvedSha: source.resolvedSha ?? "",
38595
38667
  ...source.registeredComponentRef ? { registeredComponentRef: source.registeredComponentRef } : {},
@@ -38601,7 +38673,7 @@ async function installComponent(client, org, repo, pkg, source) {
38601
38673
  ...source.installId ? { installId: source.installId } : {}
38602
38674
  }
38603
38675
  }
38604
- ], { componentId: SYSTEM_COMPONENT_ID });
38676
+ ], { componentRef: SYSTEM_REGISTERED_COMPONENT_REF });
38605
38677
  steps.push({ step: "finalize", status: "ok" });
38606
38678
  } catch (err) {
38607
38679
  const msg = err instanceof Error ? err.message : String(err);
@@ -38619,12 +38691,12 @@ function assertCliMethodsAllowedForSource(pkg, source) {
38619
38691
  const hasCliMethods = (pkg.manifest.cli?.methods?.length ?? 0) > 0;
38620
38692
  if (!hasCliMethods)
38621
38693
  return;
38622
- if (source.sourceKind === "registered" && typeof source.registeredComponentRef === "string" && source.registeredComponentRef.length > 0) {
38694
+ if (typeof source.registeredComponentRef === "string" && source.registeredComponentRef.length > 0) {
38623
38695
  return;
38624
38696
  }
38625
38697
  throw new CliError(2 /* UserInput */, "USER_INPUT", "Components that declare manifest.cli.methods must be installed from the registry.", undefined, "Register the component, then install it as <org>/<name>. Local path and GitHub URL installs cannot expose CLI methods.");
38626
38698
  }
38627
- async function reconcileShapesAfterCreateConflict(client, org, repo, pkg) {
38699
+ async function reconcileShapesAfterCreateConflict(client, org, repo, pkg, source) {
38628
38700
  const { meta, manifest } = pkg;
38629
38701
  const existingShapes = new Map;
38630
38702
  for (const shape of manifest.shapes) {
@@ -38632,8 +38704,8 @@ async function reconcileShapesAfterCreateConflict(client, org, repo, pkg) {
38632
38704
  const existingShape = await client.shape.get(org, repo, shape.name, {
38633
38705
  includeRetracted: true
38634
38706
  });
38635
- if (existingShape.componentId !== meta.id) {
38636
- const owner = existingShape.componentId ? `owned by component "${existingShape.componentId}"` : "not owned by this component";
38707
+ if (existingShape.componentRef !== source.registeredComponentRef) {
38708
+ const owner = existingShape.componentRef ? `owned by component "${existingShape.componentRef}"` : "not owned by this component";
38637
38709
  throw new Error(`Shape "${shape.name}" already exists and is ${owner}; cannot reconcile during fresh install`);
38638
38710
  }
38639
38711
  existingShapes.set(shape.name, existingShape);
@@ -38655,7 +38727,7 @@ async function reconcileShapesAfterCreateConflict(client, org, repo, pkg) {
38655
38727
  name: shape.name,
38656
38728
  data
38657
38729
  }
38658
- ], { componentId: meta.id });
38730
+ ], { componentRef: source.registeredComponentRef });
38659
38731
  continue;
38660
38732
  }
38661
38733
  try {
@@ -38666,7 +38738,7 @@ async function reconcileShapesAfterCreateConflict(client, org, repo, pkg) {
38666
38738
  name: shape.name,
38667
38739
  data
38668
38740
  }
38669
- ], { componentId: meta.id });
38741
+ ], { componentRef: source.registeredComponentRef });
38670
38742
  } catch (err) {
38671
38743
  if (isAlreadyExistsError(err)) {
38672
38744
  throw new Error(`Shape "${shape.name}" already exists but ownership could not be verified; cannot reconcile during fresh install`);
@@ -39081,7 +39153,7 @@ function resolveLogLevel(flags, env) {
39081
39153
  // package.json
39082
39154
  var package_default3 = {
39083
39155
  name: "@warmhub/cli",
39084
- version: "0.52.1",
39156
+ version: "0.53.0",
39085
39157
  private: false,
39086
39158
  type: "module",
39087
39159
  description: "The wh CLI for WarmHub — create repos, commit and query data, and compound knowledge with your AI agents.",
@@ -39228,14 +39300,17 @@ async function maybeHandleComponentShellBoundary(argv, deps = {}) {
39228
39300
  callRegisteredSetup: callRegisteredSetupImpl
39229
39301
  });
39230
39302
  }
39231
- const name = invocation.positional[1];
39232
- if (!name) {
39303
+ const componentRef = invocation.positional[1];
39304
+ if (!componentRef) {
39233
39305
  return;
39234
39306
  }
39307
+ if (!isRegisteredComponentSource(componentRef)) {
39308
+ throw new CliError(2 /* UserInput */, "USER_INPUT", `Invalid component reference '${componentRef}'. Expected '<org>/<name>' with no extra slashes and no empty parts.`, undefined, "Usage: wh component update <org/name> --repo org/repo");
39309
+ }
39235
39310
  const repoFlag = getStringFlag(invocation.flags, "repo") ?? config.defaultRepo;
39236
39311
  const refOverride = getStringFlag(invocation.flags, "ref");
39237
39312
  return await handleComponentUpdate({
39238
- name,
39313
+ componentRef,
39239
39314
  repoFlag,
39240
39315
  refOverride,
39241
39316
  client,
@@ -39306,7 +39381,6 @@ async function performRegisteredInstall(args) {
39306
39381
  invalidateInstallSnapshotCache(installRepo);
39307
39382
  const result = await args.installFromManifest(args.client, args.parsedRepo.org, args.parsedRepo.repo, resolved.manifest, {
39308
39383
  installId,
39309
- sourceKind: "registered",
39310
39384
  registeredComponentRef: args.componentRef
39311
39385
  });
39312
39386
  if (!result.ok) {
@@ -39371,11 +39445,15 @@ function warmHubErrorBackendCode(error) {
39371
39445
  async function handleComponentUpdate(args) {
39372
39446
  const parsedRepo = parseOrgRepo(args.repoFlag, args.config);
39373
39447
  const installRepo = `${parsedRepo.org}/${parsedRepo.repo}`;
39374
- const installThing = await args.client.thing.get(parsedRepo.org, parsedRepo.repo, `ComponentInstall/${args.name}`);
39448
+ const componentId = await resolveInstalledComponentRefId(args.client, parsedRepo.org, parsedRepo.repo, args.componentRef);
39449
+ const installThing = await args.client.thing.get(parsedRepo.org, parsedRepo.repo, `ComponentInstall/${componentId}`);
39375
39450
  const installData = installThing.data && typeof installThing.data === "object" ? installThing.data : {};
39376
39451
  const source = resolveComponentUpdateSource(installData);
39377
39452
  if (source.kind === "registered") {
39378
39453
  rejectRefForRegistered(source.componentRef, args.refOverride);
39454
+ if (source.componentRef !== args.componentRef) {
39455
+ throw new CliError(2 /* UserInput */, "USER_INPUT", `Component '${args.componentRef}' resolved to an install for '${source.componentRef}', which is inconsistent with the install record.`);
39456
+ }
39379
39457
  return performRegisteredInstall({
39380
39458
  componentRef: source.componentRef,
39381
39459
  persistedInstallData: installData,
@@ -39391,9 +39469,9 @@ async function handleComponentUpdate(args) {
39391
39469
  });
39392
39470
  }
39393
39471
  if (source.kind === "system") {
39394
- throw new CliError(2 /* UserInput */, "USER_INPUT", `Component '${args.name}' is a platform-managed system install and cannot be updated via the CLI; it is reconciled automatically by the WarmHub backend.`);
39472
+ throw new CliError(2 /* UserInput */, "USER_INPUT", `Component '${args.componentRef}' is a platform-managed system install and cannot be updated via the CLI; it is reconciled automatically by the WarmHub backend.`);
39395
39473
  }
39396
- throw new CliError(2 /* UserInput */, "USER_INPUT", `Component '${args.name}' was not installed from a registered component and can no longer be updated in place.`, undefined, "Register the component (`wh component register <name> --org <org> --manifest <path>`) and reinstall it with `wh component install <org>/<name>`.");
39474
+ throw new CliError(2 /* UserInput */, "USER_INPUT", `Component '${args.componentRef}' was not installed from a registered component and can no longer be updated in place.`, undefined, "Register the component (`wh component register <name> --org <org> --manifest <path>`) and reinstall it with `wh component install <org>/<name>`.");
39397
39475
  }
39398
39476
  async function writeInstallResult(writeOut, writeErr, format, verb, result, installRepo, client) {
39399
39477
  if (result.ok) {
@@ -39866,4 +39944,4 @@ if (!updateCheckSuppressedByArgv && shouldRunUpdateCheck(updateEligibility)) {
39866
39944
  var interceptedExitCode = await maybeHandleComponentShellBoundary(dispatchArgv);
39867
39945
  process.exitCode = interceptedExitCode === undefined ? await runCli(dispatchArgv, { version: package_default3.version }) : interceptedExitCode;
39868
39946
 
39869
- //# debugId=4117FD49D270041C64756E2164756E21
39947
+ //# debugId=E69740B781D6D4C864756E2164756E21