@warmhub/cli 0.62.0 → 0.63.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 +348 -76
  2. package/package.json +1 -1
package/dist/wh.js CHANGED
@@ -27353,7 +27353,7 @@ function findSystemComponent(componentId) {
27353
27353
  // ../../packages/sdk-ts/package.json
27354
27354
  var package_default = {
27355
27355
  name: "@warmhub/sdk-ts",
27356
- version: "0.62.0",
27356
+ version: "0.62.1",
27357
27357
  private: false,
27358
27358
  type: "module",
27359
27359
  description: "The TypeScript SDK for WarmHub — create repos, commit and query data, and compound knowledge with your AI agents.",
@@ -27439,6 +27439,7 @@ var package_default = {
27439
27439
  "@trpc/client": "11.17.0"
27440
27440
  },
27441
27441
  devDependencies: {
27442
+ "@trpc/server": "11.17.0",
27442
27443
  "@vitest/coverage-v8": "catalog:",
27443
27444
  "@warmhub/backend": "workspace:*",
27444
27445
  "@warmhub/rules": "workspace:*",
@@ -27697,6 +27698,14 @@ function resolveRetryPolicy(retry) {
27697
27698
  };
27698
27699
  }
27699
27700
  var DEFINITE_CLIENT_REJECT_CODES = new Set([
27701
+ "BAD_REQUEST",
27702
+ "METHOD_NOT_SUPPORTED",
27703
+ "PARSE_ERROR",
27704
+ "PAYLOAD_TOO_LARGE",
27705
+ "PRECONDITION_FAILED",
27706
+ "UNAUTHORIZED",
27707
+ "UNPROCESSABLE_CONTENT",
27708
+ "UNSUPPORTED_MEDIA_TYPE",
27700
27709
  "UNAUTHENTICATED",
27701
27710
  "FORBIDDEN",
27702
27711
  "VALIDATION_ERROR",
@@ -27709,6 +27718,7 @@ var DEFINITE_CLIENT_REJECT_CODES = new Set([
27709
27718
  "ALREADY_RETRACTED",
27710
27719
  "ARCHIVED",
27711
27720
  "RATE_LIMITED",
27721
+ "TOO_MANY_REQUESTS",
27712
27722
  "UNRESOLVED_TOKEN"
27713
27723
  ]);
27714
27724
  function extractErrorCode(cause) {
@@ -27726,9 +27736,24 @@ function extractErrorCode(cause) {
27726
27736
  return dc;
27727
27737
  return;
27728
27738
  }
27739
+ function extractHttpStatus(cause) {
27740
+ if (!cause || typeof cause !== "object")
27741
+ return;
27742
+ const status = cause.data?.httpStatus;
27743
+ if (typeof status === "number")
27744
+ return status;
27745
+ const warmhubStatus = cause.data?.warmhub?.status;
27746
+ if (typeof warmhubStatus === "number")
27747
+ return warmhubStatus;
27748
+ const direct = cause.status;
27749
+ return typeof direct === "number" ? direct : undefined;
27750
+ }
27751
+ function isDefiniteClientRejectionStatus(status) {
27752
+ return status !== undefined && status >= 400 && status < 500 && status !== 408;
27753
+ }
27729
27754
  function isDefiniteClientError(cause) {
27730
27755
  const code = extractErrorCode(cause);
27731
- return code !== undefined && DEFINITE_CLIENT_REJECT_CODES.has(code);
27756
+ return code !== undefined && DEFINITE_CLIENT_REJECT_CODES.has(code) || isDefiniteClientRejectionStatus(extractHttpStatus(cause));
27732
27757
  }
27733
27758
  function isTransientStreamFailure(cause) {
27734
27759
  if (isDefiniteClientError(cause))
@@ -28075,6 +28100,48 @@ function sanitizeSubscriptionUpdateInput(input) {
28075
28100
  } = input;
28076
28101
  return supported;
28077
28102
  }
28103
+ function trpcClientCodeToWarmHubCode(code) {
28104
+ switch (code) {
28105
+ case "BAD_REQUEST":
28106
+ case "PARSE_ERROR":
28107
+ case "PAYLOAD_TOO_LARGE":
28108
+ case "UNPROCESSABLE_CONTENT":
28109
+ case "UNSUPPORTED_MEDIA_TYPE":
28110
+ return "VALIDATION_ERROR";
28111
+ case "METHOD_NOT_SUPPORTED":
28112
+ return "NOT_FOUND";
28113
+ case "UNAUTHORIZED":
28114
+ return "UNAUTHENTICATED";
28115
+ case "FORBIDDEN":
28116
+ return "FORBIDDEN";
28117
+ case "NOT_FOUND":
28118
+ return "NOT_FOUND";
28119
+ case "CONFLICT":
28120
+ return "CONFLICT";
28121
+ case "PRECONDITION_FAILED":
28122
+ return "PRECONDITION_FAILED";
28123
+ case "TOO_MANY_REQUESTS":
28124
+ return "RATE_LIMITED";
28125
+ default:
28126
+ return;
28127
+ }
28128
+ }
28129
+ function trpcHttpStatusToWarmHubCode(status) {
28130
+ if (status === undefined || status === 408)
28131
+ return;
28132
+ if (status === 405)
28133
+ return "NOT_FOUND";
28134
+ if (status === 412)
28135
+ return "PRECONDITION_FAILED";
28136
+ if (status === 422)
28137
+ return "VALIDATION_ERROR";
28138
+ const mapped = httpStatusToWarmHubCode(status);
28139
+ if (mapped !== "BACKEND")
28140
+ return mapped;
28141
+ if (status >= 400 && status < 500)
28142
+ return "VALIDATION_ERROR";
28143
+ return mapped;
28144
+ }
28078
28145
  var INTERNAL_PATTERNS = [
28079
28146
  /\bselect\b.+?\bfrom\b/i,
28080
28147
  /\binsert\b.+?\binto\b/i,
@@ -28136,8 +28203,10 @@ function toWarmHubError(error) {
28136
28203
  }
28137
28204
  const data = error.data;
28138
28205
  const wireCode = data?.warmhub?.code;
28206
+ const trpcCode = typeof data?.code === "string" ? trpcClientCodeToWarmHubCode(data.code) : undefined;
28207
+ const trpcStatus = trpcHttpStatusToWarmHubCode(data?.httpStatus);
28139
28208
  const message = data?.warmhub?.message ?? sanitizeErrorMessage(error.message);
28140
- return new WarmHubError(wireCode ?? "BACKEND", message, data?.warmhub?.status, data?.warmhub?.hint, data?.warmhub?.retryAfter, wireCode, data?.warmhub?.details);
28209
+ return new WarmHubError(wireCode ?? trpcCode ?? trpcStatus ?? "BACKEND", message, data?.warmhub?.status ?? data?.httpStatus, data?.warmhub?.hint, data?.warmhub?.retryAfter ?? data?.retryAfter, wireCode, data?.warmhub?.details);
28141
28210
  }
28142
28211
  if (error instanceof Error) {
28143
28212
  const warmhubLike = error;
@@ -30576,7 +30645,12 @@ function parseArgs(argv) {
30576
30645
  positionals.push(arg);
30577
30646
  i += 1;
30578
30647
  }
30579
- return { positionals, flags, ...terminator ? { terminator } : {} };
30648
+ return {
30649
+ positionals,
30650
+ flags,
30651
+ ...terminator ? { terminator } : {},
30652
+ rawArgv: [...argv]
30653
+ };
30580
30654
  }
30581
30655
  function getStringFlag(flags, ...keys) {
30582
30656
  for (const key of keys) {
@@ -30598,8 +30672,9 @@ function getBoolFlag(flags, ...keys) {
30598
30672
  function getNumberFlag(flags, ...keys) {
30599
30673
  for (const key of keys) {
30600
30674
  const val = flags[key];
30601
- if (typeof val === "string") {
30602
- const num = Number(val);
30675
+ const scalar = Array.isArray(val) ? val[val.length - 1] : val;
30676
+ if (typeof scalar === "string") {
30677
+ const num = Number(scalar);
30603
30678
  if (!Number.isNaN(num))
30604
30679
  return num;
30605
30680
  }
@@ -31448,10 +31523,21 @@ function writeStore(store, path) {
31448
31523
  }
31449
31524
  }
31450
31525
  }
31526
+ function hasProfile(store, name) {
31527
+ return Object.hasOwn(store.profiles, name);
31528
+ }
31529
+ function setProfile(store, name, profile) {
31530
+ Object.defineProperty(store.profiles, name, {
31531
+ value: profile,
31532
+ enumerable: true,
31533
+ configurable: true,
31534
+ writable: true
31535
+ });
31536
+ }
31451
31537
  function saveProfile(name, profile, path) {
31452
31538
  const p = path ?? getAuthPath();
31453
31539
  const store = loadProfileStore(p);
31454
- store.profiles[name] = profile;
31540
+ setProfile(store, name, profile);
31455
31541
  writeStore(store, p);
31456
31542
  }
31457
31543
  function saveProfileWhileLocked(name, profile, path) {
@@ -31460,7 +31546,7 @@ function saveProfileWhileLocked(name, profile, path) {
31460
31546
  function getProfile(name, path) {
31461
31547
  const p = path ?? getAuthPath();
31462
31548
  const store = loadProfileStore(p);
31463
- return store.profiles[name] ?? null;
31549
+ return hasProfile(store, name) ? store.profiles[name] : null;
31464
31550
  }
31465
31551
  async function modifyStore(mutator, path) {
31466
31552
  const p = path ?? getAuthPath();
@@ -31473,12 +31559,12 @@ async function modifyStore(mutator, path) {
31473
31559
  }
31474
31560
  async function saveProfileLocked(name, profile, path) {
31475
31561
  await modifyStore((store) => {
31476
- store.profiles[name] = profile;
31562
+ setProfile(store, name, profile);
31477
31563
  }, path);
31478
31564
  }
31479
31565
  async function deleteProfileLocked(name, path) {
31480
31566
  await modifyStore((store) => {
31481
- if (!(name in store.profiles))
31567
+ if (!hasProfile(store, name))
31482
31568
  return;
31483
31569
  delete store.profiles[name];
31484
31570
  }, path);
@@ -31714,8 +31800,52 @@ var package_default2 = {
31714
31800
  }
31715
31801
  };
31716
31802
 
31803
+ // ../../packages/warmhub-cli/src/cli-trace-context.ts
31804
+ import { randomBytes } from "node:crypto";
31805
+ var TRACE_VERSION = "00";
31806
+ var TRACE_FLAGS_SAMPLED = "01";
31807
+ var activeTrace;
31808
+ function randomHex(byteLength) {
31809
+ let value = "";
31810
+ do {
31811
+ value = randomBytes(byteLength).toString("hex");
31812
+ } while (/^0+$/.test(value));
31813
+ return value;
31814
+ }
31815
+ function createTraceContext() {
31816
+ return {
31817
+ traceId: randomHex(16),
31818
+ rootSpanId: randomHex(8)
31819
+ };
31820
+ }
31821
+ function startCliTraceContext() {
31822
+ activeTrace = createTraceContext();
31823
+ return activeTrace;
31824
+ }
31825
+ function getCliTraceContext() {
31826
+ activeTrace ??= createTraceContext();
31827
+ return activeTrace;
31828
+ }
31829
+ function cliTraceLogFields() {
31830
+ const trace = getCliTraceContext();
31831
+ return {
31832
+ traceId: trace.traceId,
31833
+ spanId: trace.rootSpanId
31834
+ };
31835
+ }
31836
+ function createCliTraceparent() {
31837
+ const trace = getCliTraceContext();
31838
+ return [
31839
+ TRACE_VERSION,
31840
+ trace.traceId,
31841
+ trace.rootSpanId,
31842
+ TRACE_FLAGS_SAMPLED
31843
+ ].join("-");
31844
+ }
31845
+
31717
31846
  // ../../packages/warmhub-cli/src/client.ts
31718
31847
  var BENCHMARK_HEADER = "x-warmhub-benchmark-id";
31848
+ var TRACEPARENT_HEADER = "traceparent";
31719
31849
  var REQUEST_TOTAL_MS_HEADER = "x-warmhub-request-total-ms";
31720
31850
  var REQUEST_DB_QUERY_COUNT_HEADER = "x-warmhub-request-db-query-count";
31721
31851
  var benchmarkTelemetry = emptyBenchmarkTelemetry();
@@ -31753,10 +31883,9 @@ function recordBenchmarkResponse(response, benchmarkId) {
31753
31883
  }
31754
31884
  }
31755
31885
  function createBenchmarkAwareFetch(benchmarkId, signal) {
31756
- if (!benchmarkId && !signal)
31757
- return;
31758
31886
  return async (input, init) => {
31759
31887
  const headers = new Headers(init?.headers);
31888
+ headers.set(TRACEPARENT_HEADER, createCliTraceparent());
31760
31889
  if (benchmarkId)
31761
31890
  headers.set(BENCHMARK_HEADER, benchmarkId);
31762
31891
  const response = await fetch(input, {
@@ -31812,6 +31941,15 @@ function createClient(config, opts = {}) {
31812
31941
  client: cliClientIdentity()
31813
31942
  });
31814
31943
  }
31944
+ function createUnauthenticatedClient(config, opts = {}) {
31945
+ const benchmarkId = process.env.WH_BENCHMARK_ID?.trim();
31946
+ return new WarmHubClient({
31947
+ apiUrl: config.apiUrl,
31948
+ fetch: createBenchmarkAwareFetch(benchmarkId),
31949
+ functionLogs: opts.functionLogs,
31950
+ client: cliClientIdentity()
31951
+ });
31952
+ }
31815
31953
  function wantsStructuredLiveOutput(format) {
31816
31954
  return format === "json" || format === "jsonl";
31817
31955
  }
@@ -32186,9 +32324,18 @@ class DomainRegistry {
32186
32324
  throw new Error(`Domain '${def.name}' already registered`);
32187
32325
  }
32188
32326
  const spec = this.buildSpec(def);
32189
- this.domains.set(def.name, spec);
32190
- this.registerHandlers(def);
32191
32327
  this.validateDomain(spec, def);
32328
+ const handlers = new Map;
32329
+ this.collectHandlers(def, handlers);
32330
+ for (const key of handlers.keys()) {
32331
+ if (this.handlers.has(key)) {
32332
+ throw new Error(`Handler '${key}' already registered`);
32333
+ }
32334
+ }
32335
+ this.domains.set(def.name, spec);
32336
+ for (const [key, handler] of handlers) {
32337
+ this.handlers.set(key, handler);
32338
+ }
32192
32339
  }
32193
32340
  buildSpec(def) {
32194
32341
  if (def.kind === "flat") {
@@ -32241,39 +32388,48 @@ class DomainRegistry {
32241
32388
  aliases: f.aliases
32242
32389
  }));
32243
32390
  }
32244
- registerHandlers(def, path = [def.name]) {
32391
+ collectHandlers(def, handlers, path = [def.name]) {
32245
32392
  if (def.kind === "flat") {
32246
32393
  const key = [...path, ""].join(":");
32247
- if (this.handlers.has(key)) {
32394
+ if (handlers.has(key)) {
32248
32395
  throw new Error(`Handler '${key}' already registered`);
32249
32396
  }
32250
- this.handlers.set(key, def.handler);
32397
+ handlers.set(key, def.handler);
32251
32398
  return;
32252
32399
  }
32253
32400
  for (const [verbName, v] of Object.entries(def.verbs)) {
32254
32401
  const key = [...path, verbName].join(":");
32255
- if (this.handlers.has(key)) {
32402
+ if (handlers.has(key)) {
32256
32403
  throw new Error(`Handler '${key}' already registered`);
32257
32404
  }
32258
- this.handlers.set(key, v.handler);
32405
+ handlers.set(key, v.handler);
32259
32406
  }
32260
32407
  if (def.subdomains) {
32261
32408
  for (const [subName, subDef] of Object.entries(def.subdomains)) {
32262
- this.registerHandlers(subDef, [...path, subName]);
32409
+ this.collectHandlers(subDef, handlers, [...path, subName]);
32263
32410
  }
32264
32411
  }
32265
32412
  }
32266
- validateDomain(spec, def) {
32413
+ validateDomain(spec, def, path = [def.name]) {
32267
32414
  if (spec.kind === "noun") {
32415
+ const displayPath = path.join(".");
32268
32416
  for (const [verbName, v] of Object.entries(spec.verbs)) {
32269
32417
  for (const alias of v.verbAliases ?? []) {
32270
32418
  if (spec.verbs[alias]) {
32271
- throw new Error(`Alias '${alias}' for ${spec.name}.${verbName} conflicts with verb '${alias}'`);
32419
+ throw new Error(`Alias '${alias}' for ${displayPath}.${verbName} conflicts with verb '${alias}'`);
32272
32420
  }
32273
32421
  }
32274
32422
  }
32275
32423
  if (def.kind !== "flat" && def.defaultVerb && !def.verbs[def.defaultVerb]) {
32276
- throw new Error(`Domain '${def.name}': defaultVerb '${def.defaultVerb}' does not name a canonical verb`);
32424
+ throw new Error(`Domain '${displayPath}': defaultVerb '${def.defaultVerb}' does not name a canonical verb`);
32425
+ }
32426
+ if (def.kind !== "flat" && def.subdomains) {
32427
+ for (const [subName, subDef] of Object.entries(def.subdomains)) {
32428
+ const subSpec = spec.subdomains?.[subName];
32429
+ if (subSpec) {
32430
+ this.validateDomain(subSpec, subDef, [...path, subName]);
32431
+ }
32432
+ }
32277
32433
  }
32278
32434
  }
32279
32435
  }
@@ -32299,6 +32455,14 @@ class DomainRegistry {
32299
32455
  path.push(tokens[i]);
32300
32456
  i++;
32301
32457
  }
32458
+ if (cursor.kind === "flat") {
32459
+ return {
32460
+ domain,
32461
+ domainPath: path,
32462
+ verb: undefined,
32463
+ remaining: tokens.slice(i)
32464
+ };
32465
+ }
32302
32466
  const verb = tokens[i];
32303
32467
  const remaining = tokens.slice(i + 1);
32304
32468
  return { domain, domainPath: path, verb, remaining };
@@ -35720,8 +35884,9 @@ var handleLogin = async (ctx, { flags }) => {
35720
35884
  try {
35721
35885
  clientId = await ctx.client.auth.getClientId();
35722
35886
  } catch {
35723
- await deleteProfileLocked(profile);
35724
- clientId = await ctx.client.auth.getClientId();
35887
+ clientId = await createUnauthenticatedClient(ctx.config, {
35888
+ functionLogs: ctx.functionLogMode
35889
+ }).auth.getClientId();
35725
35890
  }
35726
35891
  if (!clientId) {
35727
35892
  throw new CliError(5 /* Auth */, "AUTH", "Backend has no WORKOS_CLIENT_ID configured.");
@@ -37818,7 +37983,8 @@ function resolveInvocation(parsed) {
37818
37983
  subcommand,
37819
37984
  positional: positionals,
37820
37985
  flags,
37821
- ...parsed.terminator ? { terminator: parsed.terminator } : {}
37986
+ ...parsed.terminator ? { terminator: parsed.terminator } : {},
37987
+ ...parsed.rawArgv ? { rawArgv: [...parsed.rawArgv] } : {}
37822
37988
  };
37823
37989
  }
37824
37990
 
@@ -37829,6 +37995,68 @@ function isRawGlobalFlagForComponentExec(key) {
37829
37995
  return true;
37830
37996
  return isGlobalShortFlag(key) && key !== key.toLowerCase();
37831
37997
  }
37998
+ function isNegativeNumericToken(token) {
37999
+ if (!token.startsWith("-") || token === "-")
38000
+ return false;
38001
+ return Number.isFinite(Number(token));
38002
+ }
38003
+ function isNumericMethodArg(method, name) {
38004
+ return method.args.some((arg) => arg.name === name && (arg.type === "number" || arg.type === "integer"));
38005
+ }
38006
+ function normalizeNegativeNumericMethodArgs(method, argv) {
38007
+ const normalized = [];
38008
+ for (let i = 0;i < argv.length; i += 1) {
38009
+ const token = argv[i];
38010
+ if (token.startsWith("--") && !token.includes("=") && isNumericMethodArg(method, token.slice(2)) && argv[i + 1] !== undefined && isNegativeNumericToken(argv[i + 1])) {
38011
+ normalized.push(`${token}=${argv[i + 1]}`);
38012
+ i += 1;
38013
+ continue;
38014
+ }
38015
+ normalized.push(token);
38016
+ }
38017
+ return normalized;
38018
+ }
38019
+ function flagValueTokenLength(argv, index) {
38020
+ const token = argv[index];
38021
+ if (token === undefined)
38022
+ return 0;
38023
+ if (token.startsWith("--")) {
38024
+ if (token.includes("="))
38025
+ return 1;
38026
+ const next = argv[index + 1];
38027
+ return next !== undefined && !next.startsWith("-") ? 2 : 1;
38028
+ }
38029
+ if (token.startsWith("-") && token !== "-") {
38030
+ const next = argv[index + 1];
38031
+ return next !== undefined && !next.startsWith("-") ? 2 : 1;
38032
+ }
38033
+ return 0;
38034
+ }
38035
+ function findComponentMethodArgStart(rawArgv, componentName, methodName) {
38036
+ const terminatorIndex = rawArgv.indexOf("--");
38037
+ const scanEnd = terminatorIndex === -1 ? rawArgv.length : terminatorIndex;
38038
+ const positionalIndexes = [];
38039
+ for (let i = 0;i < scanEnd; ) {
38040
+ const consumedFlagTokens = flagValueTokenLength(rawArgv, i);
38041
+ if (consumedFlagTokens > 0) {
38042
+ i += consumedFlagTokens;
38043
+ continue;
38044
+ }
38045
+ positionalIndexes.push(i);
38046
+ i += 1;
38047
+ }
38048
+ const positionals = positionalIndexes.map((index) => rawArgv[index]);
38049
+ if (positionals[0] === "component" && positionals[1] === "exec" && positionals[2] === componentName && positionals[3] === methodName) {
38050
+ return positionalIndexes[3] + 1;
38051
+ }
38052
+ if (positionals[0] === componentName && positionals[1] === methodName) {
38053
+ return positionalIndexes[1] + 1;
38054
+ }
38055
+ return;
38056
+ }
38057
+ function parseComponentMethodTokenStream(method, argv) {
38058
+ return parseArgs(normalizeNegativeNumericMethodArgs(method, argv));
38059
+ }
37832
38060
  function parseMethodArgs(method, rawFlags, terminatorFlags = {}) {
37833
38061
  const argsByName = new Map;
37834
38062
  for (const a of method.args)
@@ -37971,9 +38199,6 @@ var handleComponentExec = async (ctx, { args, terminator }) => {
37971
38199
  }
37972
38200
  const componentName = args[0];
37973
38201
  const methodName = args[1];
37974
- if (args.length > 2) {
37975
- throw new CliError(2 /* UserInput */, "USER_INPUT", `Unexpected argument: '${args[2]}'`, undefined, "'wh component exec' accepts at most 2 positional arguments (<component> <method>); pass method inputs as flags");
37976
- }
37977
38202
  const { org, repo } = parseOrgRepo(getRepoRef(ctx), ctx.config);
37978
38203
  const installRepo = `${org}/${repo}`;
37979
38204
  const cache = await ensureFreshInstallSnapshotCache(installRepo, ctx.client);
@@ -37996,14 +38221,29 @@ var handleComponentExec = async (ctx, { args, terminator }) => {
37996
38221
  renderMethodHelp(ctx, componentName, method);
37997
38222
  return;
37998
38223
  }
37999
- const terminatorArgs = terminator && terminator.length > 0 ? parseArgs([...terminator]) : undefined;
38224
+ const rawArgv = ctx.invocation.rawArgv;
38225
+ const methodArgStart = rawArgv === undefined ? undefined : findComponentMethodArgStart(rawArgv, componentName, methodName);
38226
+ const terminatorIndex = rawArgv?.indexOf("--") ?? -1;
38227
+ const preTerminatorEnd = rawArgv === undefined ? undefined : terminatorIndex === -1 ? rawArgv.length : terminatorIndex;
38228
+ const preMethodArgs = rawArgv !== undefined && methodArgStart !== undefined ? parseComponentMethodTokenStream(method, rawArgv.slice(0, methodArgStart)) : undefined;
38229
+ const methodArgs = rawArgv !== undefined && methodArgStart !== undefined && preTerminatorEnd !== undefined ? parseComponentMethodTokenStream(method, rawArgv.slice(methodArgStart, preTerminatorEnd)) : undefined;
38230
+ const unexpectedMethodPositional = methodArgs?.positionals[0] ?? (methodArgStart === undefined ? args[2] : undefined);
38231
+ if (unexpectedMethodPositional !== undefined) {
38232
+ throw new CliError(2 /* UserInput */, "USER_INPUT", `Unexpected argument: '${unexpectedMethodPositional}'`, undefined, "'wh component exec' accepts at most 2 positional arguments (<component> <method>); pass method inputs as flags");
38233
+ }
38234
+ const terminatorArgs = terminator && terminator.length > 0 ? parseComponentMethodTokenStream(method, terminator) : undefined;
38000
38235
  if (terminatorArgs?.positionals.length) {
38001
38236
  throw new CliError(2 /* UserInput */, "USER_INPUT", `Unexpected argument: '${terminatorArgs.positionals[0]}'`, undefined, "Component CLI method inputs must be passed as named flags, even after --");
38002
38237
  }
38003
38238
  const terminatorFlags = terminatorArgs?.flags ?? {};
38004
38239
  let parsedArgs;
38240
+ const rawFlags = {
38241
+ ...methodArgStart === undefined ? ctx.invocation.flags : {},
38242
+ ...preMethodArgs?.flags,
38243
+ ...methodArgs?.flags
38244
+ };
38005
38245
  try {
38006
- parsedArgs = parseMethodArgs(method, ctx.invocation.flags, terminatorFlags);
38246
+ parsedArgs = parseMethodArgs(method, rawFlags, terminatorFlags);
38007
38247
  } catch (error) {
38008
38248
  withMethodHelpHint(error, methodHelpHint(componentName, methodName, installRepo));
38009
38249
  }
@@ -41479,6 +41719,17 @@ function buildContentPrompt(ctx) {
41479
41719
  }
41480
41720
 
41481
41721
  // ../../packages/warmhub-cli/src/domains/repo/helpers.ts
41722
+ function parseExplicitOrgRepoArg(ref, usage, example) {
41723
+ if (!ref?.includes("/")) {
41724
+ usageError(usage, example);
41725
+ }
41726
+ const parts = ref.split("/");
41727
+ if (parts.length !== 2 || !parts[0] || !parts[1]) {
41728
+ usageError(`Invalid repo format "${ref}". Expected "org/repo" with no extra slashes or empty segments.`, example);
41729
+ }
41730
+ const [orgName, repoName] = parts;
41731
+ return { orgName, repoName };
41732
+ }
41482
41733
  function ensureNonEmptyDisplayName(value, exampleCommand) {
41483
41734
  if (value !== undefined && value.trim() === "") {
41484
41735
  usageError("--display-name requires a non-empty value", exampleCommand);
@@ -41730,11 +41981,7 @@ var confirmFlags2 = {
41730
41981
  yes: flag.boolean({ short: "y", description: "Skip confirmation prompt" })
41731
41982
  };
41732
41983
  var handleArchive2 = async (ctx, { flags, args }) => {
41733
- const orgRepo = args[0];
41734
- if (!orgRepo?.includes("/")) {
41735
- usageError("Usage: wh repo archive <org/repo> [--yes]", "wh repo archive myorg/old-repo");
41736
- }
41737
- const [orgName, repoName] = orgRepo.split("/", 2);
41984
+ const { orgName, repoName } = parseExplicitOrgRepoArg(args[0], "Usage: wh repo archive <org/repo> [--yes]", "wh repo archive myorg/old-repo");
41738
41985
  if (!flags.yes) {
41739
41986
  const confirmed = await ctx.confirm?.(`Archive repo "${orgName}/${repoName}"? This will block new commits.`);
41740
41987
  if (!confirmed) {
@@ -41749,11 +41996,7 @@ var handleArchive2 = async (ctx, { flags, args }) => {
41749
41996
  });
41750
41997
  };
41751
41998
  var handleUnarchive2 = async (ctx, { args }) => {
41752
- const orgRepo = args[0];
41753
- if (!orgRepo?.includes("/")) {
41754
- usageError("Usage: wh repo unarchive <org/repo>", "wh repo unarchive myorg/old-repo");
41755
- }
41756
- const [orgName, repoName] = orgRepo.split("/", 2);
41999
+ const { orgName, repoName } = parseExplicitOrgRepoArg(args[0], "Usage: wh repo unarchive <org/repo>", "wh repo unarchive myorg/old-repo");
41757
42000
  const c = ctx.colors;
41758
42001
  const result = await ctx.client.repo.unarchive(orgName, repoName);
41759
42002
  writeOutput(ctx, result, () => {
@@ -41764,11 +42007,7 @@ var deleteFlags = {
41764
42007
  yes: flag.boolean({ short: "y", description: "Skip confirmation prompt" })
41765
42008
  };
41766
42009
  var handleDelete2 = async (ctx, { flags, args }) => {
41767
- const orgRepo = args[0];
41768
- if (!orgRepo?.includes("/")) {
41769
- usageError("Usage: wh repo delete <org/repo> [--yes]", "wh repo delete myorg/old-repo");
41770
- }
41771
- const [orgName, repoName] = orgRepo.split("/", 2);
42010
+ const { orgName, repoName } = parseExplicitOrgRepoArg(args[0], "Usage: wh repo delete <org/repo> [--yes]", "wh repo delete myorg/old-repo");
41772
42011
  const c = ctx.colors;
41773
42012
  if (!flags.yes) {
41774
42013
  const prompt = `Delete "${orgName}/${repoName}"? The repo will be hidden immediately and permanently deleted after a 30-day grace window.`;
@@ -41866,21 +42105,23 @@ var handleRepoRename = async (ctx, { args, flags }) => {
41866
42105
  const flagSlug = flags.slug;
41867
42106
  const displayName2 = flags["display-name"];
41868
42107
  const rawSlugFromInvocation = ctx.invocation.positional[2];
42108
+ const usage = 'Usage: wh repo rename <org/repoName> [<newName>] [--slug <slug>] [--display-name "..."]';
42109
+ const example = 'wh repo rename myorg/oldrepo newrepo --display-name "New Repo"';
41869
42110
  if (!orgRepo?.includes("/")) {
41870
- usageError('Usage: wh repo rename <org/repoName> [<newName>] [--slug <slug>] [--display-name "..."]', 'wh repo rename myorg/oldrepo newrepo --display-name "New Repo"');
42111
+ usageError(usage, example);
41871
42112
  }
41872
42113
  if (positionalSlug && flagSlug !== undefined && positionalSlug !== flagSlug) {
41873
42114
  usageError("Specify the new slug as either a positional or --slug, not both", "wh repo rename myorg/foo bar # or: wh repo rename myorg/foo --slug bar");
41874
42115
  }
41875
42116
  const newSlug = positionalSlug !== undefined ? positionalSlug : flagSlug;
41876
42117
  if (newSlug === undefined && displayName2 === undefined) {
41877
- usageError('Usage: wh repo rename <org/repoName> [<newName>] [--slug <slug>] [--display-name "..."]', 'wh repo rename myorg/oldrepo newrepo --display-name "New Repo"');
42118
+ usageError(usage, example);
41878
42119
  }
41879
42120
  if (newSlug !== undefined && newSlug.length === 0 || rawSlugFromInvocation === "" || flagSlug === "") {
41880
42121
  usageError("New slug must be a non-empty string", "wh repo rename myorg/oldrepo newrepo");
41881
42122
  }
41882
42123
  ensureNonEmptyDisplayName(displayName2, 'wh repo rename myorg/oldrepo --display-name "New Repo"');
41883
- const [orgName, oldName] = orgRepo.split("/", 2);
42124
+ const { orgName, repoName: oldName } = parseExplicitOrgRepoArg(orgRepo, usage, example);
41884
42125
  const c = ctx.colors;
41885
42126
  const slugChange = newSlug && newSlug !== oldName ? newSlug : undefined;
41886
42127
  if (displayName2 !== undefined && slugChange) {
@@ -41909,14 +42150,12 @@ var updateFlags3 = {
41909
42150
  description: flag.string({ short: "d", description: "New repo description" })
41910
42151
  };
41911
42152
  var handleUpdate3 = async (ctx, { args, flags }) => {
41912
- const orgRepo = args[0];
41913
- if (!orgRepo?.includes("/")) {
41914
- usageError('Usage: wh repo update <org/repo> --description "..."', 'wh repo update myorg/myrepo -d "My repo description"');
41915
- }
42153
+ const usage = 'Usage: wh repo update <org/repo> --description "..."';
42154
+ const example = 'wh repo update myorg/myrepo -d "My repo description"';
42155
+ const { orgName, repoName } = parseExplicitOrgRepoArg(args[0], usage, example);
41916
42156
  if (flags.description === undefined) {
41917
42157
  usageError("At least one field must be specified to update", 'wh repo update myorg/myrepo --description "New description"');
41918
42158
  }
41919
- const [orgName, repoName] = orgRepo.split("/", 2);
41920
42159
  const c = ctx.colors;
41921
42160
  const description = flags.description === "" ? undefined : flags.description;
41922
42161
  const result = await ctx.client.repo.setDescription(orgName, repoName, description);
@@ -44345,12 +44584,13 @@ function extractFlags(flags, specs) {
44345
44584
  result[spec.long] = Boolean(value);
44346
44585
  break;
44347
44586
  case "number": {
44348
- if (typeof value === "boolean") {
44587
+ const scalarValue = Array.isArray(value) ? value[value.length - 1] : value;
44588
+ if (typeof scalarValue === "boolean") {
44349
44589
  throw new CliError(2 /* UserInput */, "USER_INPUT", `Flag --${spec.long} requires a numeric value`, undefined, `Usage: --${spec.long} <number>`);
44350
44590
  }
44351
- const n = Number(value);
44591
+ const n = Number(scalarValue);
44352
44592
  if (!Number.isFinite(n)) {
44353
- throw new CliError(2 /* UserInput */, "USER_INPUT", `Invalid number for --${spec.long}: '${value}'`, undefined, `Expected a number, e.g. --${spec.long} 10`);
44593
+ throw new CliError(2 /* UserInput */, "USER_INPUT", `Invalid number for --${spec.long}: '${scalarValue}'`, undefined, `Expected a number, e.g. --${spec.long} 10`);
44354
44594
  }
44355
44595
  result[spec.long] = n;
44356
44596
  break;
@@ -44558,6 +44798,11 @@ function walkSubdomainsForHelpAll(out) {
44558
44798
  for (const [verbName, verb] of Object.entries(sub.verbs)) {
44559
44799
  out(` ${domain.name} ${subName} ${verbName.padEnd(12)} ${verb.summary}`);
44560
44800
  }
44801
+ } else {
44802
+ out(` ${domain.name} ${subName.padEnd(12)} ${sub.summary}`);
44803
+ if (sub.args) {
44804
+ out(` Usage: wh ${domain.name} ${subName} ${sub.args}`);
44805
+ }
44561
44806
  }
44562
44807
  }
44563
44808
  }
@@ -44725,6 +44970,20 @@ function printVerbHelp(out, domain, verbName, verb, format, domainPath) {
44725
44970
  }
44726
44971
 
44727
44972
  // ../../packages/warmhub-cli/src/domain-dispatch-core.ts
44973
+ function resolveDomainPathSpec(domain, domainPath) {
44974
+ let cursor = domain;
44975
+ for (const seg of domainPath.slice(1)) {
44976
+ if (cursor.kind !== "noun") {
44977
+ throw new CliError(1 /* Runtime */, "UNKNOWN", `Invalid subdomain path: ${seg}`);
44978
+ }
44979
+ const next = cursor.subdomains?.[seg];
44980
+ if (!next) {
44981
+ throw new CliError(1 /* Runtime */, "UNKNOWN", `Invalid subdomain path: ${seg}`);
44982
+ }
44983
+ cursor = next;
44984
+ }
44985
+ return cursor;
44986
+ }
44728
44987
  async function dispatchDomain(ctx, depth = 0) {
44729
44988
  const tokens = [
44730
44989
  ctx.invocation.command ?? "",
@@ -44752,9 +45011,10 @@ async function dispatchDomain(ctx, depth = 0) {
44752
45011
  }
44753
45012
  const { domain, domainPath, verb, remaining } = resolved;
44754
45013
  const helpRequested = ctx.invocation.flags.help === true || ctx.invocation.flags.h === true;
44755
- if (domain.kind === "flat") {
45014
+ const pathSpec = resolveDomainPathSpec(domain, domainPath);
45015
+ if (pathSpec.kind === "flat") {
44756
45016
  if (helpRequested) {
44757
- printDomainVerbHelp(ctx.out, domain, ctx.format, domainPath);
45017
+ printDomainVerbHelp(ctx.out, pathSpec, ctx.format, domainPath);
44758
45018
  return true;
44759
45019
  }
44760
45020
  if (remaining.length > 0) {
@@ -44764,13 +45024,13 @@ async function dispatchDomain(ctx, depth = 0) {
44764
45024
  throwRenameHint(flatRenameHint);
44765
45025
  }
44766
45026
  }
44767
- const maxPositional = countMaxPositional(domain.args);
45027
+ const maxPositional = countMaxPositional(pathSpec.args);
44768
45028
  if (remaining.length > maxPositional) {
44769
45029
  const extra = remaining[maxPositional];
44770
- const usageHint = maxPositional === 0 ? `'wh ${domainPath.join(" ")}' takes no positional arguments` : `'wh ${domainPath.join(" ")}' accepts at most ${maxPositional} positional argument${maxPositional === 1 ? "" : "s"} (${domain.args})`;
45030
+ const usageHint = maxPositional === 0 ? `'wh ${domainPath.join(" ")}' takes no positional arguments` : `'wh ${domainPath.join(" ")}' accepts at most ${maxPositional} positional argument${maxPositional === 1 ? "" : "s"} (${pathSpec.args})`;
44771
45031
  throw new CliError(2 /* UserInput */, "USER_INPUT", `Unexpected argument: '${extra}'`, undefined, usageHint);
44772
45032
  }
44773
- const typedFlags2 = extractFlags(ctx.invocation.flags, domain.flags);
45033
+ const typedFlags2 = extractFlags(ctx.invocation.flags, pathSpec.flags);
44774
45034
  const allArgs = verb ? [verb, ...remaining] : remaining;
44775
45035
  if (ctx.dryRun) {
44776
45036
  ctx.out(JSON.stringify({
@@ -44793,12 +45053,7 @@ async function dispatchDomain(ctx, depth = 0) {
44793
45053
  });
44794
45054
  return true;
44795
45055
  }
44796
- const specCursor = domainPath.slice(1).reduce((d, seg) => {
44797
- const next = d.subdomains?.[seg];
44798
- if (!next || next.kind === "flat")
44799
- throw new CliError(1 /* Runtime */, "UNKNOWN", `Invalid subdomain path: ${seg}`);
44800
- return next;
44801
- }, domain);
45056
+ const specCursor = pathSpec;
44802
45057
  const verbs = Object.keys(specCursor.verbs);
44803
45058
  const invokeSingleVerb = async (args) => {
44804
45059
  if (verbs.length !== 1 || helpRequested)
@@ -45108,6 +45363,8 @@ function parseFunctionLogMode(flags, env) {
45108
45363
  async function runCli(argv, opts) {
45109
45364
  const version = opts?.version ?? package_default2.version;
45110
45365
  setCliClientVersion(version);
45366
+ startCliTraceContext();
45367
+ const traceFields = cliTraceLogFields();
45111
45368
  const startedAt = Date.now();
45112
45369
  const { out, err } = stdioWriter();
45113
45370
  const parsed = parseArgs(argv);
@@ -45135,7 +45392,8 @@ async function runCli(argv, opts) {
45135
45392
  cwd: process.cwd(),
45136
45393
  node: process.version,
45137
45394
  pid: process.pid,
45138
- profile: getStringFlag(invocation.flags, "profile", "P") ?? undefined
45395
+ profile: getStringFlag(invocation.flags, "profile", "P") ?? undefined,
45396
+ ...traceFields
45139
45397
  });
45140
45398
  let exitCode = 0 /* Ok */;
45141
45399
  let removeSignalListeners;
@@ -45194,14 +45452,14 @@ async function runCli(argv, opts) {
45194
45452
  let cancelledExitCode;
45195
45453
  const handleSigint = () => {
45196
45454
  ac.abort();
45197
- logger.info("cli.cancelled", { signal: "SIGINT" });
45455
+ logger.info("cli.cancelled", { signal: "SIGINT", ...traceFields });
45198
45456
  cancelledExitCode = cancellationExitCode("SIGINT", liveMode);
45199
45457
  if (!liveMode)
45200
45458
  process.exitCode = 130 /* Cancelled */;
45201
45459
  };
45202
45460
  const handleSigterm = () => {
45203
45461
  ac.abort();
45204
- logger.info("cli.cancelled", { signal: "SIGTERM" });
45462
+ logger.info("cli.cancelled", { signal: "SIGTERM", ...traceFields });
45205
45463
  cancelledExitCode = cancellationExitCode("SIGTERM", liveMode);
45206
45464
  process.exitCode = 143;
45207
45465
  };
@@ -45241,7 +45499,8 @@ async function runCli(argv, opts) {
45241
45499
  const cliError = toCliError2(error);
45242
45500
  const fields = {
45243
45501
  kind: cliError.kind,
45244
- exit_code: cliError.code
45502
+ exit_code: cliError.code,
45503
+ ...traceFields
45245
45504
  };
45246
45505
  if (cliError.kind !== "USER_INPUT" && cliError.kind !== "CONFIG") {
45247
45506
  fields.message = cliError.message;
@@ -45261,7 +45520,8 @@ async function runCli(argv, opts) {
45261
45520
  removeSignalListeners?.();
45262
45521
  logger.info("cli.end", {
45263
45522
  exit_code: exitCode,
45264
- duration_ms: Date.now() - startedAt
45523
+ duration_ms: Date.now() - startedAt,
45524
+ ...traceFields
45265
45525
  });
45266
45526
  await logger.shutdown();
45267
45527
  }
@@ -45314,7 +45574,7 @@ function resolveLogLevel(flags, env) {
45314
45574
  // package.json
45315
45575
  var package_default3 = {
45316
45576
  name: "@warmhub/cli",
45317
- version: "0.62.0",
45577
+ version: "0.63.0",
45318
45578
  private: false,
45319
45579
  type: "module",
45320
45580
  description: "The wh CLI for WarmHub — create repos, commit and query data, and compound knowledge with your AI agents.",
@@ -45374,6 +45634,14 @@ var package_default3 = {
45374
45634
  };
45375
45635
 
45376
45636
  // ../../packages/warmhub-cli/src/component-shell.ts
45637
+ function registeredComponentUsageHint(verb) {
45638
+ const example = verb === "install" ? "wh component install warmhub/veritas --repo myorg/myrepo" : "wh component update warmhub/identity --repo org/repo";
45639
+ return [
45640
+ `Usage: wh component ${verb} <org/name> --repo org/repo`,
45641
+ `Example: ${example}`
45642
+ ].join(`
45643
+ `);
45644
+ }
45377
45645
  async function maybeHandleComponentShellBoundary(argv, deps = {}) {
45378
45646
  const parsed = parseArgs(argv);
45379
45647
  const invocation = resolveInvocation(parsed);
@@ -45397,6 +45665,11 @@ async function maybeHandleComponentShellBoundary(argv, deps = {}) {
45397
45665
  if (getBoolFlag(invocation.flags, "dry-run")) {
45398
45666
  return;
45399
45667
  }
45668
+ const source = invocation.positional[1];
45669
+ if (source && invocation.positional.length > 2) {
45670
+ const unexpected = invocation.positional[2];
45671
+ throw new CliError(2 /* UserInput */, "USER_INPUT", `Unexpected argument '${unexpected}' for wh component ${componentShellVerb}.`, undefined, registeredComponentUsageHint(componentShellVerb));
45672
+ }
45400
45673
  const { config, client } = resolveCliContext({
45401
45674
  invocation,
45402
45675
  format,
@@ -45404,7 +45677,6 @@ async function maybeHandleComponentShellBoundary(argv, deps = {}) {
45404
45677
  config: deps.config,
45405
45678
  client: deps.client
45406
45679
  });
45407
- const source = invocation.positional[1];
45408
45680
  if (!source) {
45409
45681
  return;
45410
45682
  }
@@ -45956,4 +46228,4 @@ if (!updateCheckSuppressedByArgv && shouldRunUpdateCheck(updateEligibility)) {
45956
46228
  var interceptedExitCode = await maybeHandleComponentShellBoundary(dispatchArgv);
45957
46229
  process.exitCode = interceptedExitCode === undefined ? await runCli(dispatchArgv, { version: package_default3.version }) : interceptedExitCode;
45958
46230
 
45959
- //# debugId=FB7C860F078FFF2164756E2164756E21
46231
+ //# debugId=AA99DB1239BF58B064756E2164756E21
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@warmhub/cli",
3
- "version": "0.62.0",
3
+ "version": "0.63.0",
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.",