@warmhub/cli 0.133.0 → 0.134.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 +122 -19
  2. package/package.json +1 -1
package/dist/wh.js CHANGED
@@ -26118,6 +26118,16 @@ function illegalOpSequences(operations, errors, checkAddAdd, options) {
26118
26118
  }
26119
26119
  }
26120
26120
 
26121
+ // ../../packages/rules/src/reserved-roots.ts
26122
+ var RESERVED_THING_ROOT = "hub";
26123
+ var RESERVED_THING_ROOT_PREFIX = `${RESERVED_THING_ROOT}/`;
26124
+ function isReservedThingRoot(name) {
26125
+ return name === RESERVED_THING_ROOT || name.startsWith(RESERVED_THING_ROOT_PREFIX);
26126
+ }
26127
+ function reservedThingRootMessage(name) {
26128
+ return `Name "${name}" is under the reserved root "${RESERVED_THING_ROOT_PREFIX}"; only Shapes live there, as ${RESERVED_THING_ROOT_PREFIX}shapes/<Name>`;
26129
+ }
26130
+
26121
26131
  // ../../packages/rules/src/preflight-operation.ts
26122
26132
  var collectionTypes = ["arc", "bond", "set", "list", "pair"];
26123
26133
  var canonicalCollectionTypes = ["arc", "bond", "pair", "set", "list"];
@@ -26133,6 +26143,7 @@ function parseCollectionOp(value) {
26133
26143
  function preflightOpDiagnostics(op, operationIndex) {
26134
26144
  const errors = [];
26135
26145
  errors.push(...builtinShapeGuard(op, operationIndex));
26146
+ errors.push(...reservedRootGuard(op, operationIndex));
26136
26147
  errors.push(...contentNameGuard(op, operationIndex));
26137
26148
  errors.push(...plusSignGuard(op, operationIndex));
26138
26149
  errors.push(...validateCollectionAbouts(op, operationIndex));
@@ -26187,6 +26198,29 @@ function builtinShapeGuard(op, operationIndex) {
26187
26198
  }
26188
26199
  return errors;
26189
26200
  }
26201
+ function reservedRootRefusal(name, operationIndex) {
26202
+ return {
26203
+ code: "RESERVED_NAME",
26204
+ operationIndex,
26205
+ message: reservedThingRootMessage(name)
26206
+ };
26207
+ }
26208
+ function reservedRootGuard(op, operationIndex) {
26209
+ if (op.kind === "shape")
26210
+ return [];
26211
+ if (op.operation === "add" && op.name) {
26212
+ const stored = op.kind === "collection" ? op.name : splitLocalPath(op.name)?.bareName;
26213
+ if (stored !== undefined && isReservedThingRoot(stored)) {
26214
+ return [reservedRootRefusal(stored, operationIndex)];
26215
+ }
26216
+ return [];
26217
+ }
26218
+ const isShapeRename = op.name !== undefined && splitLocalPath(op.name) === null;
26219
+ if (op.operation === "rename" && !isShapeRename && op.newName !== undefined && isReservedThingRoot(op.newName)) {
26220
+ return [reservedRootRefusal(op.newName, operationIndex)];
26221
+ }
26222
+ return [];
26223
+ }
26190
26224
  function contentNameGuard(op, operationIndex) {
26191
26225
  if (op.kind !== "thing" || !op.name)
26192
26226
  return [];
@@ -45429,7 +45463,7 @@ function createStreamingSubmissionHandle(input, deps) {
45429
45463
  // ../../packages/sdk-ts/package.json
45430
45464
  var package_default = {
45431
45465
  name: "@warmhub/sdk-ts",
45432
- version: "0.131.0",
45466
+ version: "0.132.0",
45433
45467
  private: false,
45434
45468
  type: "module",
45435
45469
  description: "The TypeScript SDK for WarmHub — create repos, commit and query data, and compound knowledge with your AI agents.",
@@ -49791,10 +49825,10 @@ class WorkOsAuthError extends Error {
49791
49825
  this.status = status;
49792
49826
  }
49793
49827
  }
49794
- async function refreshAccessToken(tokens, signal) {
49828
+ async function refreshAccessToken(tokens, signal, options = {}) {
49795
49829
  const log = getLog();
49796
49830
  const { clientId } = tokens;
49797
- const workosUrl = "https://api.workos.com";
49831
+ const workosUrl = (options.workosUrl ?? "https://api.workos.com").replace(/\/$/, "");
49798
49832
  const path = "/user_management/authenticate";
49799
49833
  const requestId = `${process.pid}-${Date.now()}-${Math.floor(Math.random() * 1e6)}`;
49800
49834
  log.debug("http.request", {
@@ -49804,7 +49838,8 @@ async function refreshAccessToken(tokens, signal) {
49804
49838
  request_id: requestId
49805
49839
  });
49806
49840
  const startedAt = Date.now();
49807
- const requestSignal = signal ?? AbortSignal.timeout(WORKOS_REFRESH_TIMEOUT_MS);
49841
+ const timeoutSignal = AbortSignal.timeout(options.requestTimeoutMs ?? WORKOS_REFRESH_TIMEOUT_MS);
49842
+ const requestSignal = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
49808
49843
  let res;
49809
49844
  try {
49810
49845
  res = await fetch(`${workosUrl}${path}`, {
@@ -50379,6 +50414,9 @@ function corruptedAuthError(path, cause) {
50379
50414
  }
50380
50415
 
50381
50416
  // ../../packages/warmhub-cli/src/auth.ts
50417
+ var AUTH_REFRESH_BUDGET_MS = 30000;
50418
+ var AUTH_REFRESH_RETRY_DELAY_MS = 1000;
50419
+ var TRANSIENT_REFRESH_COOLDOWN_MS = 5000;
50382
50420
  function isAuthLockError(error51, authPath) {
50383
50421
  return error51 instanceof Error && error51.code === "ELOCKED" && (error51.file === undefined || error51.file === authPath);
50384
50422
  }
@@ -50400,6 +50438,17 @@ function delay2(ms, signal) {
50400
50438
  signal?.addEventListener("abort", onAbort, { once: true });
50401
50439
  });
50402
50440
  }
50441
+ function refreshCooldownActive(failedAt) {
50442
+ if (!failedAt)
50443
+ return false;
50444
+ const ageMs = Date.now() - new Date(failedAt).getTime();
50445
+ return ageMs >= 0 && ageMs < TRANSIENT_REFRESH_COOLDOWN_MS;
50446
+ }
50447
+ function withoutRefreshFailure(profile) {
50448
+ const next = { ...profile };
50449
+ delete next.transientRefreshFailedAt;
50450
+ return next;
50451
+ }
50403
50452
  function getTokenInfo(profile) {
50404
50453
  const envToken = process.env.WH_TOKEN;
50405
50454
  if (envToken) {
@@ -50428,7 +50477,7 @@ function getTokenInfo(profile) {
50428
50477
  canRefresh: source === "device" && !!tokens.refreshToken
50429
50478
  };
50430
50479
  }
50431
- async function getValidToken(profile, signal) {
50480
+ async function getValidToken(profile, signal, options = {}) {
50432
50481
  const log = getLog();
50433
50482
  const envToken = process.env.WH_TOKEN;
50434
50483
  if (envToken) {
@@ -50460,7 +50509,10 @@ async function getValidToken(profile, signal) {
50460
50509
  const classification = err.error === "invalid_grant" ? "invalid_grant" : "terminal";
50461
50510
  const staleProf = getProfile(profileName, authPath);
50462
50511
  if (staleProf) {
50463
- saveProfileWhileLocked(profileName, { ...staleProf, tokens: { ...staleProf.tokens, refreshToken: "" } }, authPath);
50512
+ saveProfileWhileLocked(profileName, withoutRefreshFailure({
50513
+ ...staleProf,
50514
+ tokens: { ...staleProf.tokens, refreshToken: "" }
50515
+ }), authPath);
50464
50516
  log.error("auth.refresh_token.wiped", {
50465
50517
  profile: profileName,
50466
50518
  reason: classification
@@ -50472,6 +50524,16 @@ async function getValidToken(profile, signal) {
50472
50524
  }
50473
50525
  throw new CliError(5 /* Auth */, "AUTH", `Session could not be refreshed (${err.error}).${err.errorDescription ? ` ${err.errorDescription}` : ""}`, err, `Run ${loginCmd} to re-authenticate.`);
50474
50526
  };
50527
+ const transientRefreshError = (cause) => new CliError(5 /* Auth */, "AUTH", "Token refresh failed (transient).", cause, `Check network connectivity, or run ${loginCmd} to re-authenticate.`);
50528
+ const markTransientRefreshFailure = () => {
50529
+ const failedProf = getProfile(profileName, authPath);
50530
+ if (!failedProf)
50531
+ return;
50532
+ saveProfileWhileLocked(profileName, {
50533
+ ...failedProf,
50534
+ transientRefreshFailedAt: new Date().toISOString()
50535
+ }, authPath);
50536
+ };
50475
50537
  try {
50476
50538
  return await withAuthLock(authPath, async () => {
50477
50539
  const lockedProf = getProfile(profileName, authPath);
@@ -50486,14 +50548,20 @@ async function getValidToken(profile, signal) {
50486
50548
  if (!lockedTokens.refreshToken) {
50487
50549
  throw new CliError(5 /* Auth */, "AUTH", "Session expired.", undefined, `Run ${loginCmd} to re-authenticate.`);
50488
50550
  }
50551
+ if (refreshCooldownActive(lockedProf.transientRefreshFailedAt)) {
50552
+ log.info("auth.refresh.cooldown", { profile: profileName });
50553
+ throw transientRefreshError();
50554
+ }
50555
+ const budgetSignal = AbortSignal.timeout(options.refreshBudgetMs ?? AUTH_REFRESH_BUDGET_MS);
50556
+ const refreshSignal = signal ? AbortSignal.any([signal, budgetSignal]) : budgetSignal;
50489
50557
  const refreshStarted = Date.now();
50490
50558
  log.info("auth.refresh.start", {
50491
50559
  profile: profileName,
50492
50560
  client_id: lockedTokens.clientId
50493
50561
  });
50494
50562
  try {
50495
- const refreshed = await refreshAccessToken(lockedTokens, signal);
50496
- saveProfileWhileLocked(profileName, { ...lockedProf, tokens: refreshed }, authPath);
50563
+ const refreshed = await refreshAccessToken(lockedTokens, refreshSignal);
50564
+ saveProfileWhileLocked(profileName, withoutRefreshFailure({ ...lockedProf, tokens: refreshed }), authPath);
50497
50565
  log.info("auth.refresh.ok", {
50498
50566
  profile: profileName,
50499
50567
  duration_ms: Date.now() - refreshStarted,
@@ -50521,15 +50589,25 @@ async function getValidToken(profile, signal) {
50521
50589
  duration_ms: Date.now() - refreshStarted,
50522
50590
  error_name: err instanceof Error ? err.name : "Unknown"
50523
50591
  });
50524
- await delay2(1000, signal);
50592
+ try {
50593
+ await delay2(options.retryDelayMs ?? AUTH_REFRESH_RETRY_DELAY_MS, refreshSignal);
50594
+ } catch (delayErr) {
50595
+ if (signal?.aborted)
50596
+ throw delayErr;
50597
+ markTransientRefreshFailure();
50598
+ throw transientRefreshError(delayErr);
50599
+ }
50525
50600
  log.info("auth.refresh.retry", { profile: profileName, attempt: 2 });
50526
50601
  let retryStarted = Date.now();
50527
50602
  try {
50528
50603
  const retryProf = getProfile(profileName, authPath);
50529
50604
  const retryTokens = retryProf?.tokens ?? lockedTokens;
50530
50605
  retryStarted = Date.now();
50531
- const refreshed = await refreshAccessToken(retryTokens, signal);
50532
- saveProfileWhileLocked(profileName, { ...retryProf ?? lockedProf, tokens: refreshed }, authPath);
50606
+ const refreshed = await refreshAccessToken(retryTokens, refreshSignal);
50607
+ saveProfileWhileLocked(profileName, withoutRefreshFailure({
50608
+ ...retryProf ?? lockedProf,
50609
+ tokens: refreshed
50610
+ }), authPath);
50533
50611
  log.info("auth.refresh.ok", {
50534
50612
  profile: profileName,
50535
50613
  duration_ms: Date.now() - retryStarted,
@@ -50554,7 +50632,8 @@ async function getValidToken(profile, signal) {
50554
50632
  failTerminalRefresh(retryErr);
50555
50633
  }
50556
50634
  log.info("auth.refresh.failed", fields);
50557
- throw new CliError(5 /* Auth */, "AUTH", "Token refresh failed (transient).", retryErr, `Check network connectivity, or run ${loginCmd} to re-authenticate.`);
50635
+ markTransientRefreshFailure();
50636
+ throw transientRefreshError(retryErr);
50558
50637
  }
50559
50638
  }
50560
50639
  });
@@ -50873,7 +50952,7 @@ function shouldGuardDomain(domainPath, canonicalVerb, args) {
50873
50952
  }
50874
50953
  async function probeCapabilities(ctx) {
50875
50954
  try {
50876
- return await ctx.client.diagnostics.capabilities();
50955
+ return await (ctx.compatibilityClient ?? ctx.client).diagnostics.capabilities();
50877
50956
  } catch {
50878
50957
  return;
50879
50958
  }
@@ -50887,7 +50966,7 @@ async function checkCompatibility(ctx, domainPath, canonicalVerb, args) {
50887
50966
  }
50888
50967
  let capabilities;
50889
50968
  try {
50890
- const diagnostics = ctx.client.diagnostics;
50969
+ const diagnostics = (ctx.compatibilityClient ?? ctx.client).diagnostics;
50891
50970
  if (typeof diagnostics.assertCompatible === "function") {
50892
50971
  await diagnostics.assertCompatible();
50893
50972
  if (clientFlags.length > 0)
@@ -50896,6 +50975,8 @@ async function checkCompatibility(ctx, domainPath, canonicalVerb, args) {
50896
50975
  capabilities = await diagnostics.capabilities();
50897
50976
  }
50898
50977
  } catch (err) {
50978
+ if (err instanceof CliError && err.kind === "AUTH")
50979
+ throw err;
50899
50980
  const message = err instanceof Error ? err.message : String(err);
50900
50981
  if (message.includes("Could not find") || message.includes("NOT_FOUND") || message.includes("is not a function")) {
50901
50982
  ctx.err("Backend does not support capabilities check (upgrade backend)");
@@ -55552,7 +55633,20 @@ Run \`wh auth login --profile ${explicitProfile}\` to create it.`);
55552
55633
  clientFlags,
55553
55634
  decorateResponses: decorate
55554
55635
  });
55555
- return { config: config2, profile: effectiveProfile, client, clientFlags, decorate };
55636
+ const compatibilityClient = args.client ?? createUnauthenticatedClient(config2, {
55637
+ functionLogs: args.functionLogs,
55638
+ signal: args.signal,
55639
+ clientFlags,
55640
+ decorateResponses: decorate
55641
+ });
55642
+ return {
55643
+ config: config2,
55644
+ profile: effectiveProfile,
55645
+ client,
55646
+ compatibilityClient,
55647
+ clientFlags,
55648
+ decorate
55649
+ };
55556
55650
  }
55557
55651
 
55558
55652
  // ../../packages/warmhub-cli/src/domains/auth-shared.ts
@@ -69712,9 +69806,17 @@ async function runPreparedCli(rawArgv, prepared, opts) {
69712
69806
  requestedMode: requestedFunctionLogs
69713
69807
  });
69714
69808
  const localOnlyCommand = isLocalOnlyInvocation(invocation);
69715
- const { config: config2, client, profile, clientFlags, decorate } = localOnlyCommand ? {
69809
+ const {
69810
+ config: config2,
69811
+ client,
69812
+ compatibilityClient,
69813
+ profile,
69814
+ clientFlags,
69815
+ decorate
69816
+ } = localOnlyCommand ? {
69716
69817
  config: loadConfig(),
69717
69818
  client: createLocalOnlyClient(),
69819
+ compatibilityClient: undefined,
69718
69820
  profile: "default",
69719
69821
  clientFlags: [],
69720
69822
  decorate: true
@@ -69735,6 +69837,7 @@ async function runPreparedCli(rawArgv, prepared, opts) {
69735
69837
  const signal = abortController.signal;
69736
69838
  const commandContext = {
69737
69839
  client,
69840
+ compatibilityClient,
69738
69841
  config: config2,
69739
69842
  invocation,
69740
69843
  profile,
@@ -69861,7 +69964,7 @@ function resolveLogLevel(flagLevel, env) {
69861
69964
  // package.json
69862
69965
  var package_default3 = {
69863
69966
  name: "@warmhub/cli",
69864
- version: "0.133.0",
69967
+ version: "0.134.0",
69865
69968
  private: false,
69866
69969
  type: "module",
69867
69970
  description: "The wh CLI for WarmHub — create repos, commit and query data, and compound knowledge with your AI agents.",
@@ -70486,5 +70589,5 @@ process.exitCode = interceptedExitCode === undefined ? await runPreparedCli(laun
70486
70589
  version: package_default3.version
70487
70590
  }) : interceptedExitCode;
70488
70591
 
70489
- //# debugId=AF530C2A20E5B7C564756E2164756E21
70490
- //# warmhub-cli-build-info {"cliVersion":"0.133.0","sdkVersion":"0.131.0"}
70592
+ //# debugId=59E58BA2C90F5E3C64756E2164756E21
70593
+ //# warmhub-cli-build-info {"cliVersion":"0.134.0","sdkVersion":"0.132.0"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@warmhub/cli",
3
- "version": "0.133.0",
3
+ "version": "0.134.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.",