@warmhub/cli 0.48.0 → 0.49.1

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 +426 -48
  2. package/package.json +1 -1
package/dist/wh.js CHANGED
@@ -19527,7 +19527,7 @@ function findSystemComponent(componentId) {
19527
19527
  // ../../packages/sdk-ts/package.json
19528
19528
  var package_default = {
19529
19529
  name: "@warmhub/sdk-ts",
19530
- version: "0.47.0",
19530
+ version: "0.49.0",
19531
19531
  private: false,
19532
19532
  type: "module",
19533
19533
  description: "The TypeScript SDK for WarmHub — create repos, commit and query data, and compound knowledge with your AI agents.",
@@ -19626,12 +19626,16 @@ var package_default = {
19626
19626
 
19627
19627
  // ../../packages/sdk-ts/src/operation-normalize.ts
19628
19628
  function toBackendStreamOperation(operation) {
19629
+ if (operation.expectedVersion !== undefined && operation.operation !== "revise") {
19630
+ throw new Error("expectedVersion is only valid on revise operations — set operation: 'revise'");
19631
+ }
19629
19632
  if (operation.operation === "retract") {
19630
19633
  return {
19631
19634
  operation: "retract",
19632
19635
  name: operation.name,
19633
19636
  ...operation.kind ? { kind: operation.kind } : {},
19634
- ...operation.reason ? { reason: operation.reason } : {}
19637
+ ...operation.reason ? { reason: operation.reason } : {},
19638
+ ...operation.leaseId ? { leaseId: operation.leaseId } : {}
19635
19639
  };
19636
19640
  }
19637
19641
  if (operation.operation === "revise") {
@@ -19654,14 +19658,18 @@ function toBackendStreamOperation(operation) {
19654
19658
  operation: "revise",
19655
19659
  kind: "assertion",
19656
19660
  name,
19657
- data: operation.data
19661
+ data: operation.data,
19662
+ ...operation.expectedVersion !== undefined ? { expectedVersion: operation.expectedVersion } : {},
19663
+ ...operation.leaseId ? { leaseId: operation.leaseId } : {}
19658
19664
  };
19659
19665
  }
19660
19666
  return {
19661
19667
  operation: "revise",
19662
19668
  kind: kind2,
19663
19669
  name,
19664
- data: operation.data
19670
+ data: operation.data,
19671
+ ...operation.expectedVersion !== undefined ? { expectedVersion: operation.expectedVersion } : {},
19672
+ ...operation.leaseId ? { leaseId: operation.leaseId } : {}
19665
19673
  };
19666
19674
  }
19667
19675
  const kind = operation.kind ?? inferKind(operation.name, operation);
@@ -20155,7 +20163,8 @@ class WarmHubError extends Error {
20155
20163
  hint;
20156
20164
  retryAfter;
20157
20165
  backendCode;
20158
- constructor(code, message, status, hint, retryAfter, backendCode) {
20166
+ details;
20167
+ constructor(code, message, status, hint, retryAfter, backendCode, details) {
20159
20168
  super(message);
20160
20169
  this.name = "WarmHubError";
20161
20170
  this.code = code;
@@ -20163,6 +20172,7 @@ class WarmHubError extends Error {
20163
20172
  this.hint = hint;
20164
20173
  this.retryAfter = retryAfter;
20165
20174
  this.backendCode = backendCode;
20175
+ this.details = details;
20166
20176
  }
20167
20177
  get kind() {
20168
20178
  return this.code;
@@ -20187,12 +20197,12 @@ function toWarmHubError(error) {
20187
20197
  const data = error.data;
20188
20198
  const wireCode = data?.warmhub?.code;
20189
20199
  const message = data?.warmhub?.message ?? sanitizeErrorMessage(error.message);
20190
- return new WarmHubError(wireCode ?? "BACKEND", message, data?.warmhub?.status, data?.warmhub?.hint, data?.warmhub?.retryAfter, wireCode);
20200
+ return new WarmHubError(wireCode ?? "BACKEND", message, data?.warmhub?.status, data?.warmhub?.hint, data?.warmhub?.retryAfter, wireCode, data?.warmhub?.details);
20191
20201
  }
20192
20202
  if (error instanceof Error) {
20193
20203
  const warmhubLike = error;
20194
20204
  if (error.name === "WarmHubError" && typeof warmhubLike.code === "string") {
20195
- return new WarmHubError(warmhubLike.code, sanitizeErrorMessage(error.message), typeof warmhubLike.status === "number" ? warmhubLike.status : undefined, typeof warmhubLike.hint === "string" ? warmhubLike.hint : undefined, typeof warmhubLike.retryAfter === "number" ? warmhubLike.retryAfter : undefined, typeof warmhubLike.backendCode === "string" ? warmhubLike.backendCode : undefined);
20205
+ return new WarmHubError(warmhubLike.code, sanitizeErrorMessage(error.message), typeof warmhubLike.status === "number" ? warmhubLike.status : undefined, typeof warmhubLike.hint === "string" ? warmhubLike.hint : undefined, typeof warmhubLike.retryAfter === "number" ? warmhubLike.retryAfter : undefined, typeof warmhubLike.backendCode === "string" ? warmhubLike.backendCode : undefined, warmhubLike.details);
20196
20206
  }
20197
20207
  if (error.name === "AbortError") {
20198
20208
  return new WarmHubError("CANCELLED", error.message);
@@ -21320,6 +21330,30 @@ class WarmHubClient {
21320
21330
  throw toWarmHubError(error);
21321
21331
  }
21322
21332
  },
21333
+ getWithLease: async (orgName, repoName, wref, opts) => {
21334
+ try {
21335
+ return await this.trpc.thing.getWithLease.mutate({
21336
+ orgName,
21337
+ repoName,
21338
+ wref,
21339
+ ttlMs: opts?.ttlMs
21340
+ });
21341
+ } catch (error) {
21342
+ throw toWarmHubError(error);
21343
+ }
21344
+ },
21345
+ releaseLease: async (orgName, repoName, wref, leaseId) => {
21346
+ try {
21347
+ await this.trpc.thing.releaseLease.mutate({
21348
+ orgName,
21349
+ repoName,
21350
+ wref,
21351
+ leaseId
21352
+ });
21353
+ } catch (error) {
21354
+ throw toWarmHubError(error);
21355
+ }
21356
+ },
21323
21357
  graph: async (orgName, repoName, wref, opts) => {
21324
21358
  try {
21325
21359
  return await this.trpc.thing.graph.query({
@@ -22057,6 +22091,10 @@ function classifyConflict(message, code) {
22057
22091
  return "archived";
22058
22092
  case "REPO_PENDING_DELETE":
22059
22093
  return "pending-delete";
22094
+ case "LEASE_UNAVAILABLE":
22095
+ return "lease-held";
22096
+ default:
22097
+ break;
22060
22098
  }
22061
22099
  const lower = message.toLowerCase();
22062
22100
  if (lower.includes("already exists") || lower.includes("already a member")) {
@@ -22086,6 +22124,8 @@ function conflictHint(message, code) {
22086
22124
  return "Other things reference this. Retract those assertions first, or use a tombstone.";
22087
22125
  case "pending-delete":
22088
22126
  return "Repo is being deleted. Wait for the delete to finish or cancel it before retrying.";
22127
+ case "lease-held":
22128
+ return "This thing is leased by another holder. Back off until the lease expires (see leaseExpiresAt / retryAfter), then retry.";
22089
22129
  case "other":
22090
22130
  return "Resolve the conflicting existing state and try again.";
22091
22131
  }
@@ -22109,7 +22149,8 @@ var CONFLICT_SHAPED_CODES = new Set([
22109
22149
  "ARCHIVED",
22110
22150
  "HAS_INBOUND_REFS",
22111
22151
  "REPO_PENDING_DELETE",
22112
- "ALREADY_RETRACTED"
22152
+ "ALREADY_RETRACTED",
22153
+ "LEASE_UNAVAILABLE"
22113
22154
  ]);
22114
22155
  function fromWh(exit, kind, err, hint = err.hint) {
22115
22156
  return new CliError(exit, kind, err.message, err, hint, undefined, err.backendCode);
@@ -22269,6 +22310,11 @@ function generateSuggestions(code, message, context, backendCode) {
22269
22310
  action: "Wait for the repo delete to finish or cancel it before retrying"
22270
22311
  });
22271
22312
  break;
22313
+ case "lease-held":
22314
+ suggestions.push({
22315
+ action: "Another holder leased this thing. Back off until the lease expires (leaseExpiresAt / retryAfter), then retry"
22316
+ });
22317
+ break;
22272
22318
  case "other":
22273
22319
  suggestions.push({
22274
22320
  action: "Resolve the conflicting existing state and retry"
@@ -22480,6 +22526,21 @@ function safeParseJson(input, label, options) {
22480
22526
  throw new CliError(2 /* UserInput */, "USER_INPUT", `Invalid JSON for ${label}: ${input.length > 80 ? `${input.slice(0, 77)}...` : input}`, undefined, hint);
22481
22527
  }
22482
22528
  }
22529
+ function parsePositiveIntFlag(value, label, example) {
22530
+ if (value !== undefined && (!Number.isInteger(value) || value < 1)) {
22531
+ usageError(`${label} must be a positive integer`, example);
22532
+ }
22533
+ return value;
22534
+ }
22535
+ function requireLeaseIdFlag(raw, example = `wh thing revise Player/alice --data '{"score":2}' --lease-id <id>`) {
22536
+ if (raw === undefined)
22537
+ return;
22538
+ const trimmed = raw.trim();
22539
+ if (trimmed === "") {
22540
+ usageError("--lease-id must be a non-empty lease token from `wh thing lease`", example);
22541
+ }
22542
+ return trimmed;
22543
+ }
22483
22544
  function parseArgs(argv) {
22484
22545
  const positionals = [];
22485
22546
  const flags = {};
@@ -23859,6 +23920,17 @@ function parseOrgRepo(ref, config) {
23859
23920
  }
23860
23921
  throw new CliError(3 /* Config */, "CONFIG", "No repo specified. Use --repo org/repo, set WARMHUB_REPO, or run: wh use org/repo");
23861
23922
  }
23923
+ function parseOrgRepoOptional(ref, config) {
23924
+ try {
23925
+ return parseOrgRepo(ref, config);
23926
+ } catch (error) {
23927
+ const suppliedRepoInfo = ref !== undefined && ref !== "" || Boolean(config.defaultRepo);
23928
+ if (suppliedRepoInfo) {
23929
+ throw error;
23930
+ }
23931
+ return {};
23932
+ }
23933
+ }
23862
23934
 
23863
23935
  // ../../packages/warmhub-cli/src/domain-registry.ts
23864
23936
  class DomainRegistry {
@@ -24276,9 +24348,15 @@ function renderWarningLine(out, c, chars, op) {
24276
24348
  const moreSuffix = remaining > 0 ? ` ${c.dim}(+${remaining} more)${c.reset}` : "";
24277
24349
  out(` ${c.yellow}${chars.warn}${c.reset} ${total} ${noun} not declared in shape${shapeLabel}: ${c.dim}${fieldsList}${c.reset}${moreSuffix}`);
24278
24350
  }
24351
+ function renderCommitterEcho(out, c, committer) {
24352
+ if (!committer)
24353
+ return;
24354
+ out(` ${c.dim}committer:${c.reset} ${pinnedWref(c, committer)}`);
24355
+ }
24279
24356
  function renderSingleOpSuccess(out, c, chars, op, opts) {
24280
24357
  const name = op.name ?? opts.fallbackName;
24281
24358
  const suffix = opts.suffix ?? "";
24359
+ renderCommitterEcho(out, c, opts.committer);
24282
24360
  out(`${opts.color}${opts.marker}${c.reset} ${displayName(c, name)}${suffix}`);
24283
24361
  renderWarningLine(out, c, chars, op);
24284
24362
  }
@@ -24357,6 +24435,10 @@ async function readContentInput(filePath, inline, stdinStream) {
24357
24435
  }
24358
24436
 
24359
24437
  // ../../packages/warmhub-cli/src/domains/thing.ts
24438
+ var DURABLE_ID_PATTERN_RE = /^[0-9a-zA-HJ-NP-Tv-z]{60}(@(v\d+|HEAD|ALL))?$/i;
24439
+ function looksLikeDurableId(wref) {
24440
+ return DURABLE_ID_PATTERN_RE.test(wref);
24441
+ }
24360
24442
  function usageError2(usage, example) {
24361
24443
  throw new CliError(2 /* UserInput */, "USER_INPUT", usage, undefined, `Example: ${example}`);
24362
24444
  }
@@ -24453,6 +24535,10 @@ function renderHead(out, c, chars, result, org, repo, shape, kind) {
24453
24535
  const truncated = preview.length > 80 ? `${preview.slice(0, 77)}...` : preview;
24454
24536
  out(` ${c.dim}${truncated}${c.reset}`);
24455
24537
  }
24538
+ const itemMeta = item.metadata;
24539
+ if (itemMeta?.durableId) {
24540
+ out(` ${c.dim}durableId:${c.reset} ${itemMeta.durableId}`);
24541
+ }
24456
24542
  }
24457
24543
  out(`${c.dim}${items.length} item(s)${c.reset}`);
24458
24544
  }
@@ -24472,6 +24558,19 @@ function renderThing(out, c, result) {
24472
24558
  if (aboutWref) {
24473
24559
  out(` ${c.dim}about:${c.reset} ${String(aboutWref)}`);
24474
24560
  }
24561
+ const meta = result.metadata;
24562
+ if (meta?.durableId || meta?.thingCreatedAt || meta?.versionCreatedAt) {
24563
+ const now = Date.now();
24564
+ if (meta.durableId) {
24565
+ out(` ${c.dim}durableId:${c.reset} ${meta.durableId}`);
24566
+ }
24567
+ if (meta.thingCreatedAt) {
24568
+ out(` ${c.dim}thingCreated:${c.reset} ${formatTime(meta.thingCreatedAt, now)}`);
24569
+ }
24570
+ if (meta.versionCreatedAt) {
24571
+ out(` ${c.dim}versionCreated:${c.reset}${formatTime(meta.versionCreatedAt, now)}`);
24572
+ }
24573
+ }
24475
24574
  const fields = shapeName && result.data ? collectionFields(shapeName, result.data) : null;
24476
24575
  if (fields) {
24477
24576
  for (const field of fields) {
@@ -24562,6 +24661,10 @@ function renderHistory(out, c, result) {
24562
24661
  const wref = result.thing.wref ?? result.thing.name ?? "(unknown)";
24563
24662
  out(`${c.bold}History: ${pinnedWref(c, wref)}${c.reset} ${kindLabel(c, result.thing.kind ?? "thing")}`);
24564
24663
  }
24664
+ const firstMeta = result.versions?.[0]?.metadata;
24665
+ if (firstMeta?.durableId) {
24666
+ out(` ${c.dim}durableId:${c.reset} ${firstMeta.durableId}`);
24667
+ }
24565
24668
  const versions = result.versions ?? [];
24566
24669
  const now = Date.now();
24567
24670
  for (const ver of versions) {
@@ -24578,7 +24681,9 @@ function renderHistory(out, c, result) {
24578
24681
  const wref = ver.wref ?? ver.thingName;
24579
24682
  const wrefStr = wref ? pinnedWref(c, wref, ver.version) : "";
24580
24683
  const by = ver.committerWref ? ` ${c.dim}by${c.reset} ${pinnedWref(c, ver.committerWref)}` : "";
24581
- out(` ${wrefStr} ${op} ${c.dim}${time}${c.reset}${by}`);
24684
+ const thingCreatedAt = ver.metadata?.thingCreatedAt;
24685
+ const thingCreatedStr = thingCreatedAt ? ` ${c.dim}born:${formatTime(thingCreatedAt, now)}${c.reset}` : "";
24686
+ out(` ${wrefStr} ${op} ${c.dim}${time}${c.reset}${by}${thingCreatedStr}`);
24582
24687
  }
24583
24688
  }
24584
24689
  function renderRefs(out, c, result, wref, direction) {
@@ -24846,9 +24951,9 @@ async function collectWrefs(opts) {
24846
24951
  ]));
24847
24952
  }
24848
24953
  async function runSingleView(ctx, wref, flags) {
24849
- const { org, repo } = parseOrgRepo(getRepoRef(ctx), ctx.config);
24850
24954
  const version = flags.version;
24851
24955
  const depth = flags.depth;
24956
+ const { org, repo } = looksLikeDurableId(wref) && depth === undefined ? parseOrgRepoOptional(getRepoRef(ctx), ctx.config) : parseOrgRepo(getRepoRef(ctx), ctx.config);
24852
24957
  const includeRetracted = flags["include-retracted"] || version !== undefined;
24853
24958
  if (depth !== undefined && (depth < 1 || depth > 5)) {
24854
24959
  usageError2("Usage: wh thing view <wref> --depth <1-5>", "wh thing view Game/base --depth 2");
@@ -24892,7 +24997,8 @@ async function runSingleView(ctx, wref, flags) {
24892
24997
  writeOutput(ctx, result, () => renderThing(ctx.out, ctx.colors, result));
24893
24998
  }
24894
24999
  async function runBatchView(ctx, wrefs, flags) {
24895
- const { org, repo } = parseOrgRepo(getRepoRef(ctx), ctx.config);
25000
+ const allDurable = wrefs.length > 0 && wrefs.every(looksLikeDurableId);
25001
+ const { org, repo } = allDurable ? parseOrgRepoOptional(getRepoRef(ctx), ctx.config) : parseOrgRepo(getRepoRef(ctx), ctx.config);
24896
25002
  const includeRetracted = flags["include-retracted"] || flags.version !== undefined;
24897
25003
  const result = await ctx.client.thing.getMany(org, repo, wrefs, flags.version, { includeRetracted });
24898
25004
  if (ctx.format === "jsonl") {
@@ -24944,6 +25050,53 @@ var handleView = async (ctx, { flags, args, terminator }) => {
24944
25050
  const singleWref = wrefs[0];
24945
25051
  return runSingleView(ctx, singleWref, flags);
24946
25052
  };
25053
+ var LEASE_MIN_TTL_MS = 1000;
25054
+ var leaseFlags = {
25055
+ ttl: flag.number({
25056
+ description: `Lease duration in ms (default 5000, min ${LEASE_MIN_TTL_MS}; max is operator-configured)`
25057
+ })
25058
+ };
25059
+ function renderLease(out, c, result) {
25060
+ renderThing(out, c, result);
25061
+ out("");
25062
+ out(` ${c.dim}lease id:${c.reset} ${result.lease.id}`);
25063
+ out(` ${c.dim}expires at:${c.reset} ${result.lease.expiresAt}`);
25064
+ }
25065
+ var handleLease = async (ctx, { flags, args }) => {
25066
+ const wref = args[0];
25067
+ if (!wref) {
25068
+ usageError2("Usage: wh thing lease <wref> [--ttl <ms>]", "wh thing lease Player/alice --ttl 5000");
25069
+ }
25070
+ const ttl = flags.ttl;
25071
+ if (ttl !== undefined && (!Number.isInteger(ttl) || ttl < LEASE_MIN_TTL_MS)) {
25072
+ usageError2(`--ttl must be an integer >= ${LEASE_MIN_TTL_MS} (ms)`, "wh thing lease Player/alice --ttl 5000");
25073
+ }
25074
+ const { org, repo } = parseOrgRepo(getRepoRef(ctx), ctx.config);
25075
+ const result = await ctx.client.thing.getWithLease(org, repo, wref, {
25076
+ ttlMs: ttl
25077
+ });
25078
+ writeOutput(ctx, result, () => renderLease(ctx.out, ctx.colors, result));
25079
+ };
25080
+ var releaseLeaseFlags = {
25081
+ "lease-id": flag.string({
25082
+ description: "Lease token from `wh thing lease` to release early"
25083
+ })
25084
+ };
25085
+ var handleReleaseLease = async (ctx, { flags, args }) => {
25086
+ const wref = args[0];
25087
+ if (!wref) {
25088
+ usageError2("Usage: wh thing release-lease <wref> --lease-id <id>", "wh thing release-lease Player/alice --lease-id <id>");
25089
+ }
25090
+ const leaseId = requireLeaseIdFlag(flags["lease-id"], "wh thing release-lease Player/alice --lease-id <id>");
25091
+ if (leaseId === undefined) {
25092
+ usageError2("--lease-id is required for release-lease", "wh thing release-lease Player/alice --lease-id <id>");
25093
+ }
25094
+ const { org, repo } = parseOrgRepo(getRepoRef(ctx), ctx.config);
25095
+ await ctx.client.thing.releaseLease(org, repo, wref, leaseId);
25096
+ writeOutput(ctx, { released: true, wref, leaseId }, () => {
25097
+ ctx.out(`Released lease ${leaseId} on ${wref}`);
25098
+ });
25099
+ };
24947
25100
  var graphFlags = {
24948
25101
  depth: flag.number({
24949
25102
  description: "Resolve embedded graph to this depth (default: 2, max: 5)"
@@ -24983,7 +25136,7 @@ var historyFlags = {
24983
25136
  };
24984
25137
  var handleHistory = async (ctx, { flags, args }) => {
24985
25138
  const wref = args[0];
24986
- const { org, repo } = parseOrgRepo(getRepoRef(ctx), ctx.config);
25139
+ const { org, repo } = wref !== undefined && looksLikeDurableId(wref) ? parseOrgRepoOptional(getRepoRef(ctx), ctx.config) : parseOrgRepo(getRepoRef(ctx), ctx.config);
24987
25140
  const shape = flags.shape;
24988
25141
  const about = flags.about;
24989
25142
  const limit = flags.limit;
@@ -25082,7 +25235,7 @@ var handleResolve = async (ctx, { args }) => {
25082
25235
  if (!wref) {
25083
25236
  usageError2("Usage: wh thing resolve <wref>", "wh thing resolve player");
25084
25237
  }
25085
- const { org, repo } = parseOrgRepo(getRepoRef(ctx), ctx.config);
25238
+ const { org, repo } = looksLikeDurableId(wref) ? parseOrgRepoOptional(getRepoRef(ctx), ctx.config) : parseOrgRepo(getRepoRef(ctx), ctx.config);
25086
25239
  const c = ctx.colors;
25087
25240
  const result = await ctx.client.thing.resolve(org, repo, wref);
25088
25241
  writeOutput(ctx, result, () => {
@@ -25098,6 +25251,12 @@ var reviseFlags = {
25098
25251
  message: flag.string({ short: "m", description: "Commit message" }),
25099
25252
  committer: flag.string({
25100
25253
  description: "Committer thing wref (e.g. Agent/bot-1)"
25254
+ }),
25255
+ "expected-version": flag.number({
25256
+ description: "Only apply if the target is still at this version (optimistic concurrency)"
25257
+ }),
25258
+ "lease-id": flag.string({
25259
+ description: "Read-lease token from `wh thing lease` (auto-released on success)"
25101
25260
  })
25102
25261
  };
25103
25262
  var createFlags = {
@@ -25138,7 +25297,8 @@ var handleCreate = async (ctx, { flags, args }) => {
25138
25297
  writeOutput(ctx, result, () => renderSingleOpSuccess(ctx.out, c, ctx.chars, result.operations[0] ?? {}, {
25139
25298
  marker: "+",
25140
25299
  color: c.green,
25141
- fallbackName: name
25300
+ fallbackName: name,
25301
+ committer
25142
25302
  }));
25143
25303
  };
25144
25304
  var handleRevise = async (ctx, { flags, args }) => {
@@ -25149,8 +25309,10 @@ var handleRevise = async (ctx, { flags, args }) => {
25149
25309
  const { org, repo } = parseOrgRepo(getRepoRef(ctx), ctx.config);
25150
25310
  const committer = flags.committer;
25151
25311
  const message = flags.message;
25312
+ const leaseId = requireLeaseIdFlag(flags["lease-id"]);
25152
25313
  const data = flags.data ? safeParseJson(flags.data, "--data") : undefined;
25153
25314
  const c = ctx.colors;
25315
+ const expectedVersion = parsePositiveIntFlag(flags["expected-version"], "--expected-version", "wh thing revise Player/alice --data '{...}' --expected-version 3");
25154
25316
  if (!data) {
25155
25317
  usageError2("--data is required for revise", `wh thing revise Location/player --data '{"x":1}'`);
25156
25318
  }
@@ -25159,13 +25321,16 @@ var handleRevise = async (ctx, { flags, args }) => {
25159
25321
  operation: "revise",
25160
25322
  kind: "thing",
25161
25323
  name,
25162
- data
25324
+ data,
25325
+ ...expectedVersion !== undefined ? { expectedVersion } : {},
25326
+ ...leaseId ? { leaseId } : {}
25163
25327
  }
25164
25328
  ], { committer });
25165
25329
  assertSingleOpSuccess(result);
25166
25330
  writeOutput(ctx, result, () => renderSingleOpSuccess(ctx.out, c, ctx.chars, result.operations[0] ?? {}, {
25167
25331
  marker: "~",
25168
- color: c.yellow
25332
+ color: c.yellow,
25333
+ committer
25169
25334
  }));
25170
25335
  };
25171
25336
  var retractFlags = {
@@ -25176,6 +25341,9 @@ var retractFlags = {
25176
25341
  message: flag.string({ short: "m", description: "Commit message" }),
25177
25342
  committer: flag.string({
25178
25343
  description: "Committer thing wref (e.g. Agent/bot-1)"
25344
+ }),
25345
+ "lease-id": flag.string({
25346
+ description: "Read-lease token from `wh thing lease` (auto-released on success)"
25179
25347
  })
25180
25348
  };
25181
25349
  var handleThingRetract = async (ctx, { flags, args }) => {
@@ -25188,18 +25356,21 @@ var handleThingRetract = async (ctx, { flags, args }) => {
25188
25356
  const message = flags.message;
25189
25357
  const kind = flags.kind;
25190
25358
  const reason = flags.reason;
25359
+ const leaseId = requireLeaseIdFlag(flags["lease-id"], "wh thing retract Player/alice --lease-id <id>");
25191
25360
  const c = ctx.colors;
25192
25361
  const result = await ctx.client.commit.apply(org, repo, message ?? `retract ${name}`, [
25193
25362
  {
25194
25363
  operation: "retract",
25195
25364
  name,
25196
25365
  ...kind ? { kind } : {},
25197
- ...reason ? { reason } : {}
25366
+ ...reason ? { reason } : {},
25367
+ ...leaseId ? { leaseId } : {}
25198
25368
  }
25199
25369
  ], { committer });
25200
25370
  assertSingleOpSuccess(result);
25201
25371
  writeOutput(ctx, result, () => {
25202
25372
  const op = result.operations[0];
25373
+ renderCommitterEcho(ctx.out, c, committer);
25203
25374
  ctx.out(`${c.red}-${c.reset} ${displayName(c, op?.name ?? name)}`);
25204
25375
  });
25205
25376
  };
@@ -25542,7 +25713,7 @@ var handleAbout = async (ctx, { flags, args }) => {
25542
25713
  if (!wref) {
25543
25714
  usageError2("Usage: wh thing about <wref> [--shape] [--match] [--limit] [--include-retracted]", "wh thing about Location/cave");
25544
25715
  }
25545
- const { org, repo } = parseOrgRepo(getRepoRef(ctx), ctx.config);
25716
+ const { org, repo } = looksLikeDurableId(wref) ? parseOrgRepoOptional(getRepoRef(ctx), ctx.config) : parseOrgRepo(getRepoRef(ctx), ctx.config);
25546
25717
  const includeRetracted = flags["include-retracted"];
25547
25718
  const limit = flags.limit;
25548
25719
  const cursor = flags.cursor;
@@ -25662,7 +25833,7 @@ var handleRefs = async (ctx, { flags, args }) => {
25662
25833
  if (!wref) {
25663
25834
  usageError2("Usage: wh thing refs <wref> [--inbound|--outbound] [--field FIELD] [--limit N]", "wh thing refs Loc/player");
25664
25835
  }
25665
- const { org, repo } = parseOrgRepo(getRepoRef(ctx), ctx.config);
25836
+ const { org, repo } = looksLikeDurableId(wref) ? parseOrgRepoOptional(getRepoRef(ctx), ctx.config) : parseOrgRepo(getRepoRef(ctx), ctx.config);
25666
25837
  if (flags.inbound && flags.outbound) {
25667
25838
  usageError2("Cannot specify both --inbound and --outbound", "wh thing refs Loc/player --outbound");
25668
25839
  }
@@ -25743,6 +25914,25 @@ var THING_DOMAIN = defineDomain({
25743
25914
  ],
25744
25915
  handler: handleView
25745
25916
  },
25917
+ lease: {
25918
+ prime: true,
25919
+ summary: "Acquire a short, exclusive read lease on a thing (read + lease in one call)",
25920
+ args: "<wref>",
25921
+ flags: leaseFlags,
25922
+ examples: [
25923
+ "wh thing lease Player/alice",
25924
+ "wh thing lease Player/alice --ttl 5000"
25925
+ ],
25926
+ handler: handleLease
25927
+ },
25928
+ "release-lease": {
25929
+ prime: true,
25930
+ summary: "Release a read lease early (idempotent)",
25931
+ args: "<wref>",
25932
+ flags: releaseLeaseFlags,
25933
+ examples: ["wh thing release-lease Player/alice --lease-id <id>"],
25934
+ handler: handleReleaseLease
25935
+ },
25746
25936
  history: {
25747
25937
  prime: true,
25748
25938
  summary: "Show version history",
@@ -26056,7 +26246,8 @@ var handleRevise2 = async (ctx, { flags, args }) => {
26056
26246
  assertSingleOpSuccess(result);
26057
26247
  writeOutput(ctx, result, () => renderSingleOpSuccess(ctx.out, c, ctx.chars, result.operations[0] ?? {}, {
26058
26248
  marker: "~",
26059
- color: c.yellow
26249
+ color: c.yellow,
26250
+ committer: flags.committer
26060
26251
  }));
26061
26252
  };
26062
26253
  var retractFlags2 = {
@@ -26085,6 +26276,7 @@ var handleRetract = async (ctx, { flags, args }) => {
26085
26276
  assertSingleOpSuccess(result);
26086
26277
  writeOutput(ctx, result, () => {
26087
26278
  const op = result.operations[0];
26279
+ renderCommitterEcho(ctx.out, ctx.colors, flags.committer);
26088
26280
  ctx.out(`${ctx.colors.red}-${ctx.colors.reset} ${displayName(ctx.colors, op?.name ?? name)}`);
26089
26281
  });
26090
26282
  };
@@ -26181,7 +26373,8 @@ var handleCreate2 = async (ctx, { flags, args }) => {
26181
26373
  writeOutput(ctx, result, () => renderSingleOpSuccess(ctx.out, c, ctx.chars, result.operations[0] ?? {}, {
26182
26374
  marker: "+",
26183
26375
  color: c.green,
26184
- suffix: ` ${c.dim}(${shape})${c.reset}`
26376
+ suffix: ` ${c.dim}(${shape})${c.reset}`,
26377
+ committer
26185
26378
  }));
26186
26379
  };
26187
26380
  var listFlags = {
@@ -27613,6 +27806,59 @@ import { createReadStream as createReadStream2 } from "node:fs";
27613
27806
  import { readFile, writeFile } from "node:fs/promises";
27614
27807
  import { createInterface as createInterface2 } from "node:readline";
27615
27808
 
27809
+ // ../../packages/warmhub-cli/src/commit-payload-validate.ts
27810
+ var NUL = String.fromCharCode(0);
27811
+ function findInvalidControlByte(data, rootLabel = "data") {
27812
+ const segs = [];
27813
+ const pos = visit(data, segs);
27814
+ return pos === -1 ? null : { path: buildPath(rootLabel, segs), position: pos };
27815
+ }
27816
+ function visit(value, segs) {
27817
+ if (typeof value === "string")
27818
+ return value.indexOf(NUL);
27819
+ if (value === null || typeof value !== "object")
27820
+ return -1;
27821
+ if (Array.isArray(value)) {
27822
+ for (let i = 0;i < value.length; i++) {
27823
+ segs.push(i);
27824
+ const p = visit(value[i], segs);
27825
+ if (p !== -1)
27826
+ return p;
27827
+ segs.pop();
27828
+ }
27829
+ return -1;
27830
+ }
27831
+ const keys = Object.keys(value);
27832
+ for (let i = 0;i < keys.length; i++) {
27833
+ const k = keys[i];
27834
+ segs.push(k);
27835
+ const p = visit(value[k], segs);
27836
+ if (p !== -1)
27837
+ return p;
27838
+ segs.pop();
27839
+ }
27840
+ return -1;
27841
+ }
27842
+ function buildPath(root, segs) {
27843
+ let out = root;
27844
+ for (const s of segs)
27845
+ out += typeof s === "number" ? `[${s}]` : `.${s}`;
27846
+ return out;
27847
+ }
27848
+ function formatInvalidControlByteMessage(hit, locator) {
27849
+ return {
27850
+ message: `${locator}: ${hit.path} contains literal U+0000 byte at position ${hit.position}, ` + "which PostgreSQL `text` cannot store (SQLSTATE 22P05).",
27851
+ hint: "Strip NUL bytes before submit, e.g. `.replace(/\\u0000/g, '')`, or pass --allow-nul-bytes to send anyway."
27852
+ };
27853
+ }
27854
+ function assertNoNulBytes(data, locator) {
27855
+ const hit = findInvalidControlByte(data);
27856
+ if (!hit)
27857
+ return;
27858
+ const { message, hint } = formatInvalidControlByteMessage(hit, locator);
27859
+ throw new CliError(2 /* UserInput */, "USER_INPUT", message, undefined, hint);
27860
+ }
27861
+
27616
27862
  // ../../packages/warmhub-cli/src/domains/stream-progress.ts
27617
27863
  import { createReadStream } from "node:fs";
27618
27864
  import { createInterface } from "node:readline";
@@ -27914,7 +28160,9 @@ async function applyJsonlCommit(ctx, args) {
27914
28160
  } catch (cause) {
27915
28161
  const completed = operationOffset + opCount;
27916
28162
  if (opCount > 0 || firstJsonlAppendErrorMayHaveCommitted(cause)) {
27917
- throw new CliError(4 /* Backend */, "BACKEND", `Stream append failed after ${completed} acknowledged operation(s).`, cause, "The failed append may also have landed; inspect repository state before submitting any remaining JSONL operations.");
28163
+ const backendCode = toWarmHubError(cause).backendCode;
28164
+ const suffix = backendCode ? ` (backend: ${backendCode})` : "";
28165
+ throw new CliError(4 /* Backend */, "BACKEND", `Stream append failed after ${completed} acknowledged operation(s).${suffix}`, cause, "The failed append may also have landed; inspect repository state before submitting any remaining JSONL operations.", undefined, backendCode);
27918
28166
  }
27919
28167
  throw cause;
27920
28168
  }
@@ -27972,6 +28220,24 @@ async function applyJsonlCommit(ctx, args) {
27972
28220
  continue;
27973
28221
  assertWithinStreamOpLimit(parsedOpCount + 1);
27974
28222
  const operation = safeParseJson(trimmed, `${args.lineLabel} JSONL line ${lineNumber}`);
28223
+ if (args.allowNulBytes !== true) {
28224
+ try {
28225
+ assertNoNulBytes("data" in operation ? operation.data : undefined, `${args.lineLabel} JSONL line ${lineNumber}`);
28226
+ } catch (cause) {
28227
+ if (cause instanceof CliError) {
28228
+ if (opCount > 0) {
28229
+ const completed = operationOffset + opCount;
28230
+ const baseHint = cause.hint && cause.hint.length > 0 ? `${cause.hint} ` : "";
28231
+ throw new CliError(4 /* Backend */, "BACKEND", `${cause.message} (${completed} earlier operation(s) already acknowledged by the server before this NUL-byte rejection).`, undefined, `${baseHint}The earlier chunks have landed; inspect repository state before submitting any remaining JSONL operations.`);
28232
+ }
28233
+ if (operationOffset > 0) {
28234
+ const baseHint = cause.hint && cause.hint.length > 0 ? `${cause.hint} ` : "";
28235
+ throw new CliError(cause.code, cause.kind, `${cause.message} (${operationOffset} earlier operation(s) already acknowledged by the server in a prior run; this run made no backend writes before the NUL-byte rejection).`, undefined, `${baseHint}Fix the offending input; the prior-run chunks remain landed and unaffected by this local validation failure.`);
28236
+ }
28237
+ }
28238
+ throw cause;
28239
+ }
28240
+ }
27975
28241
  parsedOpCount += 1;
27976
28242
  chunk.push(withSkipExisting(operation, args.skipExisting === true));
27977
28243
  if (chunk.length < chunkSize)
@@ -28189,7 +28455,7 @@ function buildAddOperations(input) {
28189
28455
  });
28190
28456
  }
28191
28457
  function buildRetractOperations(input) {
28192
- const { retractNames, kinds, reasons } = input;
28458
+ const { retractNames, kinds, reasons, leaseId } = input;
28193
28459
  const assertCardinality = (flagName, values) => {
28194
28460
  if (values.length === 0)
28195
28461
  return;
@@ -28215,7 +28481,8 @@ function buildRetractOperations(input) {
28215
28481
  operation: "retract",
28216
28482
  name,
28217
28483
  ...kind ? { kind } : {},
28218
- ...reason ? { reason } : {}
28484
+ ...reason ? { reason } : {},
28485
+ ...leaseId ? { leaseId } : {}
28219
28486
  };
28220
28487
  });
28221
28488
  }
@@ -28272,6 +28539,9 @@ var createFlags3 = {
28272
28539
  "chunk-size": flag.number({
28273
28540
  description: `Streamed ops per append chunk for --stream or .jsonl --file (default: ${DEFAULT_STREAM_APPEND_CHUNK_SIZE}, max: ${MAX_STREAM_APPEND_OPERATION_COUNT})`
28274
28541
  }),
28542
+ "allow-nul-bytes": flag.boolean({
28543
+ description: "Skip the client-side pre-check that rejects literal U+0000 bytes in op data. PostgreSQL `text` columns still cannot store these — use only when the backend storage type is known to tolerate them."
28544
+ }),
28275
28545
  "timing-out": flag.string({
28276
28546
  description: "Write per-append timing sidecar JSON to this path (debug/bench instrumentation)"
28277
28547
  }),
@@ -28322,6 +28592,12 @@ var createFlags3 = {
28322
28592
  }),
28323
28593
  members: flag.string({
28324
28594
  description: "Collection members (comma-separated wrefs)"
28595
+ }),
28596
+ "expected-version": flag.number({
28597
+ description: "Only apply if the --revise target is still at this version (optimistic concurrency). Requires --revise."
28598
+ }),
28599
+ "lease-id": flag.string({
28600
+ description: "Read-lease token from `wh thing lease`, bound to --revise/--retract (auto-released on success)"
28325
28601
  })
28326
28602
  };
28327
28603
  var handleSubmit = async (ctx, { flags, args }) => {
@@ -28334,6 +28610,7 @@ var handleSubmit = async (ctx, { flags, args }) => {
28334
28610
  const progressRequested = flags.progress === true;
28335
28611
  const skipExisting = flags["skip-existing"] === true;
28336
28612
  const chunkSize = flags["chunk-size"];
28613
+ const allowNulBytes = flags["allow-nul-bytes"] === true;
28337
28614
  const timingOut = flags["timing-out"];
28338
28615
  const resumeStreamId = flags["stream-id"];
28339
28616
  const resumeAllocatedTokens = parseAllocatedTokensFlag(flags["allocated-tokens"]);
@@ -28347,6 +28624,14 @@ var handleSubmit = async (ctx, { flags, args }) => {
28347
28624
  const abouts = flags.about ?? [];
28348
28625
  const reasons = flags.reason ?? [];
28349
28626
  const kinds = flags.kind ?? [];
28627
+ const expectedVersion = parsePositiveIntFlag(flags["expected-version"], "--expected-version", "wh commit submit --revise Player/alice --data '{...}' --expected-version 3");
28628
+ if (expectedVersion !== undefined && !reviseName) {
28629
+ usageError4("--expected-version requires --revise", "wh commit submit --revise Player/alice --data '{...}' --expected-version 3");
28630
+ }
28631
+ const leaseIdFlag = requireLeaseIdFlag(flags["lease-id"], `wh commit submit --revise Player/alice --data '{"score":2}' --lease-id <id>`);
28632
+ if (leaseIdFlag !== undefined && reviseName === undefined && retractNames.length === 0) {
28633
+ throw new CliError(2 /* UserInput */, "USER_INPUT", "--lease-id requires --revise or --retract.", undefined, 'For --ops/--file/--stream writes, carry "leaseId" inline on the revise/retract op.');
28634
+ }
28350
28635
  const validKinds = [
28351
28636
  "thing",
28352
28637
  "assertion",
@@ -28438,7 +28723,8 @@ var handleSubmit = async (ctx, { flags, args }) => {
28438
28723
  operations = buildRetractOperations({
28439
28724
  retractNames,
28440
28725
  kinds,
28441
- reasons
28726
+ reasons,
28727
+ leaseId: leaseIdFlag
28442
28728
  });
28443
28729
  } else if (reviseName) {
28444
28730
  if (dataJsons.length > 1) {
@@ -28451,13 +28737,30 @@ var handleSubmit = async (ctx, { flags, args }) => {
28451
28737
  const data = rawData ? safeParseJson(rawData, "--data") : undefined;
28452
28738
  const kindFlag = kinds[0];
28453
28739
  const kind = kindFlag ?? "thing";
28454
- operations = [{ operation: "revise", kind, name: reviseName, data }];
28740
+ operations = [
28741
+ {
28742
+ operation: "revise",
28743
+ kind,
28744
+ name: reviseName,
28745
+ data,
28746
+ ...expectedVersion !== undefined ? { expectedVersion } : {},
28747
+ ...leaseIdFlag ? { leaseId: leaseIdFlag } : {}
28748
+ }
28749
+ ];
28455
28750
  } else {
28456
28751
  usageError4("Usage: wh commit submit --ops '<json>' or --add <name> [--data <json>]", `wh commit submit --add player --shape location --data '{"x":0,"y":0}'`);
28457
28752
  }
28458
28753
  if (!streamInput && operations.length > 0) {
28459
28754
  rejectLegacyLifecycleOperations(operations);
28460
28755
  assertWithinStreamOpLimit(operations.length);
28756
+ if (!allowNulBytes) {
28757
+ const source = opsJson ? "--ops" : opsFile ? opsFile : addNames.length > 0 ? "--add" : reviseName !== undefined ? "--revise" : collectionType ? "--type" : "op";
28758
+ for (let i = 0;i < operations.length; i++) {
28759
+ const op = operations[i];
28760
+ const namePart = op.name ? ` (${op.name})` : "";
28761
+ assertNoNulBytes("data" in op ? op.data : undefined, `${source} op ${i}${namePart}`);
28762
+ }
28763
+ }
28461
28764
  }
28462
28765
  const message = streamInput || jsonlFile ? messageFlag : messageFlag ?? synthesizeCommitMessage(operations);
28463
28766
  let result;
@@ -28472,7 +28775,8 @@ var handleSubmit = async (ctx, { flags, args }) => {
28472
28775
  skipExisting,
28473
28776
  streamId: resumeStreamId,
28474
28777
  allocatedTokens: resumeAllocatedTokens,
28475
- operationOffset: resumeOperationOffset
28778
+ operationOffset: resumeOperationOffset,
28779
+ allowNulBytes
28476
28780
  }) : jsonlFile ? await applyJsonlFileCommit(ctx, {
28477
28781
  org,
28478
28782
  repo,
@@ -28485,7 +28789,8 @@ var handleSubmit = async (ctx, { flags, args }) => {
28485
28789
  skipExisting,
28486
28790
  streamId: resumeStreamId,
28487
28791
  allocatedTokens: resumeAllocatedTokens,
28488
- operationOffset: resumeOperationOffset
28792
+ operationOffset: resumeOperationOffset,
28793
+ allowNulBytes
28489
28794
  }) : await ctx.client.commit.apply(org, repo, message, operations, {
28490
28795
  committer,
28491
28796
  skipExisting
@@ -28502,6 +28807,7 @@ var handleSubmit = async (ctx, { flags, args }) => {
28502
28807
  writeOutput(ctx, result, () => {
28503
28808
  const failureSuffix = failedCount > 0 ? `, ${failedCount} failed` : "";
28504
28809
  ctx.out(`${message ?? "(no message)"} ${c.dim}(${result.operationCount} ops${failureSuffix})${c.reset}`);
28810
+ renderCommitterEcho(ctx.out, c, committer);
28505
28811
  for (const op of result.operations) {
28506
28812
  const failed = isFailedOpStatus(op.status);
28507
28813
  const marker = failed ? "!" : op.operation === "add" ? "+" : op.operation === "revise" ? "~" : "-";
@@ -31064,12 +31370,28 @@ var updateFlags = {
31064
31370
  })
31065
31371
  };
31066
31372
  var validateFlags = {};
31067
- var listFlags2 = {};
31373
+ var listFlags2 = {
31374
+ limit: flag.number({
31375
+ description: "Maximum components per page (default: 50, max: 500)"
31376
+ }),
31377
+ cursor: flag.string({ description: "Opaque pagination cursor" }),
31378
+ all: flag.boolean({
31379
+ description: "Fetch all pages (auto-paginate until exhausted)"
31380
+ })
31381
+ };
31068
31382
  var viewFlags3 = {};
31069
31383
  var teardownFlags = {};
31070
31384
  var doctorFlags = {};
31071
31385
  var initFlags = {};
31072
- var searchFlags2 = {};
31386
+ var searchFlags2 = {
31387
+ limit: flag.number({
31388
+ description: "Maximum components per page (default: 50, max: 500)"
31389
+ }),
31390
+ cursor: flag.string({ description: "Opaque pagination cursor" }),
31391
+ all: flag.boolean({
31392
+ description: "Fetch all pages before filtering (auto-paginate until exhausted)"
31393
+ })
31394
+ };
31073
31395
  var registerFlags = {
31074
31396
  org: flag.string({ description: "Owner org for the registered component" }),
31075
31397
  "source-url": flag.string({ description: "Source GitHub URL for installs" }),
@@ -31449,12 +31771,33 @@ function normalizeGithubRemote(remote) {
31449
31771
  }
31450
31772
  return;
31451
31773
  }
31452
- var handleList2 = async (ctx) => {
31774
+ async function fetchComponentPages(client, org, repo, opts) {
31775
+ const firstPage = await client.component.list(org, repo, {
31776
+ limit: opts.limit,
31777
+ cursor: opts.cursor
31778
+ });
31779
+ const items = [...firstPage.items ?? []];
31780
+ let nextCursor = firstPage.nextCursor;
31781
+ if (opts.all) {
31782
+ while (nextCursor) {
31783
+ const page = await client.component.list(org, repo, {
31784
+ limit: opts.limit,
31785
+ cursor: nextCursor
31786
+ });
31787
+ items.push(...page.items ?? []);
31788
+ nextCursor = page.nextCursor;
31789
+ }
31790
+ }
31791
+ return { items, nextCursor };
31792
+ }
31793
+ var handleList2 = async (ctx, { flags }) => {
31453
31794
  const { org, repo } = parseOrgRepo(getRepoRef(ctx), ctx.config);
31454
- const result = await ctx.client.component.list(org, repo);
31455
- const items = result.items ?? [];
31795
+ const c = ctx.colors;
31796
+ const { items, nextCursor } = await fetchComponentPages(ctx.client, org, repo, { all: flags.all, limit: flags.limit, cursor: flags.cursor });
31797
+ if (!flags.all && nextCursor) {
31798
+ ctx.status(`${c.yellow}${items.length} shown, more available${c.reset} — use ${c.cyan}--all${c.reset} to fetch every page, ` + `or re-run the same command plus ${c.cyan}--cursor=${nextCursor}${c.reset} to resume from here.`);
31799
+ }
31456
31800
  writeOutput(ctx, items, () => {
31457
- const c = ctx.colors;
31458
31801
  if (!items.length) {
31459
31802
  ctx.status(`${c.dim}No components installed${c.reset}`);
31460
31803
  return;
@@ -31470,17 +31813,20 @@ var handleList2 = async (ctx) => {
31470
31813
  }
31471
31814
  });
31472
31815
  };
31473
- var handleSearch2 = async (ctx, { args }) => {
31816
+ var handleSearch2 = async (ctx, { flags, args }) => {
31474
31817
  const query = args[0]?.trim();
31475
31818
  if (!query) {
31476
31819
  usageError("Usage: wh component search <query> --repo org/repo", "wh component search research --repo myorg/myrepo");
31477
31820
  }
31478
31821
  const { org, repo } = parseOrgRepo(getRepoRef(ctx), ctx.config);
31479
- const result = await ctx.client.component.list(org, repo);
31822
+ const c = ctx.colors;
31823
+ const { items: allItems, nextCursor } = await fetchComponentPages(ctx.client, org, repo, { all: flags.all, limit: flags.limit, cursor: flags.cursor });
31480
31824
  const needle = query.toLowerCase();
31481
- const items = (result.items ?? []).filter((item) => [item.componentId, item.componentName, item.source].filter((value) => typeof value === "string").some((value) => value.toLowerCase().includes(needle)));
31825
+ const items = allItems.filter((item) => [item.componentId, item.componentName, item.source].filter((value) => typeof value === "string").some((value) => value.toLowerCase().includes(needle)));
31826
+ if (!flags.all && nextCursor) {
31827
+ 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.`);
31828
+ }
31482
31829
  writeOutput(ctx, items, () => {
31483
- const c = ctx.colors;
31484
31830
  if (!items.length) {
31485
31831
  ctx.status(`${c.dim}No components matching "${query}" in ${org}/${repo}${c.reset}`);
31486
31832
  return;
@@ -33610,7 +33956,7 @@ cat wrefs.txt | wh thing view --format jsonl # one JSON line per *deduped
33610
33956
  - \`wh thing history [wref] [--shape] [--about] [--include-retracted]\` — Version history
33611
33957
  - \`wh thing resolve <wref>\` — Resolve wref
33612
33958
  - \`wh thing create <name|Shape/name> --data <json> [--shape] [--message] [--committer]\` — Create
33613
- - \`wh thing revise <name> [--data] [--message] [--committer]\` — Revise
33959
+ - \`wh thing revise <name> [--data] [--message] [--committer] [--expected-version]\` — Revise (CONFLICT if HEAD≠n)
33614
33960
  - \`wh thing retract <wref> -m <message> [--reason] [--kind]\` — Retract
33615
33961
  - \`wh thing query [--shape] [--kind] [--about] [--match]\` — Query by filters
33616
33962
  - \`wh thing search <query> [--shape] [--kind] [--about] [--mode]\` — Search text
@@ -33619,7 +33965,7 @@ cat wrefs.txt | wh thing view --format jsonl # one JSON line per *deduped
33619
33965
  - \`wh thing about <wref> [--shape] [--match] [--depth] [--resolve-collections] [--limit] [--include-retracted]\` — Show assertions about the target identity; \`--resolve-collections\` expands collection members for bare/@HEAD/@ALL inputs, not pinned @vN
33620
33966
 
33621
33967
  ### commit — Write operations
33622
- - \`wh commit submit [--ops|--file|--add|--revise|--retract] [--kind] [--data] [--reason] [--chunk-size] [--skip-existing] [--progress] [--stream-id] [--allocated-tokens] [--operation-offset]\` — Submit operations (bare \`wh commit\` is equivalent). Use \`--file ops.jsonl --chunk-size 5000 --skip-existing\` for bulk ingest.
33968
+ - \`wh commit submit [--ops|--file|--add|--revise|--retract] [--kind] [--data] [--reason] [--expected-version] [--chunk-size] [--skip-existing] [--progress] [--stream-id] [--allocated-tokens] [--operation-offset]\` — Submit operations (bare \`wh commit\` is equivalent). Use \`--file ops.jsonl --chunk-size 5000 --skip-existing\` for bulk ingest.
33623
33969
  - \`wh shape template <shape> [shape2 ...] [--kind] [--operation add|revise|retract] [-o file]\` — Generate sample ops
33624
33970
 
33625
33971
  ### assertion — Assertion operations
@@ -33941,6 +34287,13 @@ var handleCreate5 = async (ctx, { flags, args }) => {
33941
34287
  var repoListFlags = {
33942
34288
  "include-archived": flag.boolean({
33943
34289
  description: "Include archived repositories"
34290
+ }),
34291
+ limit: flag.number({
34292
+ description: "Maximum repos per page (default: 50, max: 200)"
34293
+ }),
34294
+ cursor: flag.string({ description: "Opaque pagination cursor" }),
34295
+ all: flag.boolean({
34296
+ description: "Fetch all pages (auto-paginate until exhausted)"
33944
34297
  })
33945
34298
  };
33946
34299
  var handleList5 = async (ctx, { flags, args }) => {
@@ -33949,16 +34302,36 @@ var handleList5 = async (ctx, { flags, args }) => {
33949
34302
  usageError8("Usage: wh repo list <org> (or set WARMHUB_ORG)", "wh repo list myorg");
33950
34303
  }
33951
34304
  const c = ctx.colors;
33952
- const repos = await ctx.client.repo.list(orgName, {
33953
- includeArchived: flags["include-archived"]
34305
+ const all = flags.all;
34306
+ const includeArchived = flags["include-archived"];
34307
+ const firstPage = await ctx.client.repo.list(orgName, {
34308
+ includeArchived,
34309
+ limit: flags.limit,
34310
+ cursor: flags.cursor
33954
34311
  });
33955
- writeOutput(ctx, repos.items, () => {
33956
- if (!repos.items.length) {
34312
+ const items = [...firstPage.items];
34313
+ let nextCursor = firstPage.nextCursor;
34314
+ if (all) {
34315
+ while (nextCursor) {
34316
+ const page = await ctx.client.repo.list(orgName, {
34317
+ includeArchived,
34318
+ limit: flags.limit,
34319
+ cursor: nextCursor
34320
+ });
34321
+ items.push(...page.items);
34322
+ nextCursor = page.nextCursor;
34323
+ }
34324
+ }
34325
+ if (!all && nextCursor) {
34326
+ ctx.status(`${c.yellow}${items.length} shown, more available${c.reset} — use ${c.cyan}--all${c.reset} to fetch every page, ` + `or re-run the same command plus ${c.cyan}--cursor=${nextCursor}${c.reset} to resume from here.`);
34327
+ }
34328
+ writeOutput(ctx, items, () => {
34329
+ if (!items.length) {
33957
34330
  ctx.status(`${c.dim}No repos in ${orgName}${c.reset}`);
33958
34331
  return;
33959
34332
  }
33960
34333
  ctx.out(`${c.bold}Repos in ${orgName}:${c.reset}`);
33961
- for (const r of repos.items) {
34334
+ for (const r of items) {
33962
34335
  const display = r.displayName && r.displayName !== r.name ? ` ${c.dim}${r.displayName}${c.reset}` : "";
33963
34336
  const desc = r.description ? ` ${c.dim}${r.description}${c.reset}` : "";
33964
34337
  const archived = r.archivedAt ? ` ${c.yellow}[archived]${c.reset}` : "";
@@ -34388,6 +34761,8 @@ var handleContentSet = async (ctx, { args, flags }) => {
34388
34761
  });
34389
34762
  break;
34390
34763
  }
34764
+ default:
34765
+ throw new CliError(2 /* UserInput */, "USER_INPUT", `--kind ${kind} does not support \`set\`.`);
34391
34766
  }
34392
34767
  };
34393
34768
  var handleContentGenerate = async (ctx, { args, flags }) => {
@@ -34417,6 +34792,8 @@ var handleContentGenerate = async (ctx, { args, flags }) => {
34417
34792
  });
34418
34793
  break;
34419
34794
  }
34795
+ default:
34796
+ throw new CliError(2 /* UserInput */, "USER_INPUT", `--kind ${kind} does not support \`generate\`.`);
34420
34797
  }
34421
34798
  };
34422
34799
  var CONTENT_SUBDOMAIN = defineDomain({
@@ -34774,6 +35151,7 @@ var handleRetract2 = async (ctx, { flags, args }) => {
34774
35151
  ], { committer: flags.committer });
34775
35152
  assertSingleOpSuccess(result);
34776
35153
  writeOutput(ctx, result, () => {
35154
+ renderCommitterEcho(ctx.out, c, flags.committer);
34777
35155
  ctx.out(`${c.red}Retracted${c.reset} ${c.magenta}${shapeName}${c.reset}`);
34778
35156
  });
34779
35157
  };
@@ -37203,7 +37581,7 @@ async function runCli(argv, opts) {
37203
37581
  return exitCode;
37204
37582
  }
37205
37583
  if (globalHelpAllRequested) {
37206
- printDomainHelp(out, { allFlags: true, allDomains: true });
37584
+ await printDomainHelp(out, { allFlags: true, allDomains: true });
37207
37585
  exitCode = 0 /* Ok */;
37208
37586
  return exitCode;
37209
37587
  }
@@ -37355,7 +37733,7 @@ function resolveLogLevel(flags, env) {
37355
37733
  // package.json
37356
37734
  var package_default3 = {
37357
37735
  name: "@warmhub/cli",
37358
- version: "0.48.0",
37736
+ version: "0.49.1",
37359
37737
  private: false,
37360
37738
  type: "module",
37361
37739
  description: "The wh CLI for WarmHub — create repos, commit and query data, and compound knowledge with your AI agents.",
@@ -38382,4 +38760,4 @@ if (!updateCheckSuppressedByArgv && shouldRunUpdateCheck(updateEligibility)) {
38382
38760
  var interceptedExitCode = await maybeHandleComponentShellBoundary(dispatchArgv);
38383
38761
  process.exitCode = interceptedExitCode === undefined ? await runCli(dispatchArgv, { version: package_default3.version }) : interceptedExitCode;
38384
38762
 
38385
- //# debugId=4217C1CF64E0C7F164756E2164756E21
38763
+ //# debugId=C7B962EC82038B1364756E2164756E21
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@warmhub/cli",
3
- "version": "0.48.0",
3
+ "version": "0.49.1",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "The wh CLI for WarmHub — create repos, commit and query data, and compound knowledge with your AI agents.",