@tinycloud/cli 1.1.0-beta.10 → 1.1.0-beta.12

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.
@@ -6967,11 +6967,31 @@ function authRequiredError(service) {
6967
6967
  service
6968
6968
  };
6969
6969
  }
6970
- function timeoutError(service) {
6970
+ var RequestTimeoutError = class extends Error {
6971
+ constructor(timeoutMs) {
6972
+ super(`Request timed out after ${timeoutMs}ms.`);
6973
+ this.name = "TimeoutError";
6974
+ this.timeoutMs = timeoutMs;
6975
+ }
6976
+ };
6977
+ function isRequestTimeoutError(error) {
6978
+ return error instanceof RequestTimeoutError || error instanceof Error && error.name === "TimeoutError" && typeof error.timeoutMs === "number";
6979
+ }
6980
+ function timeoutError(service, timeoutMs, cause) {
6981
+ if (timeoutMs === void 0) {
6982
+ return {
6983
+ code: ErrorCodes.TIMEOUT,
6984
+ message: "Request timed out.",
6985
+ service,
6986
+ ...cause === void 0 ? {} : { cause }
6987
+ };
6988
+ }
6971
6989
  return {
6972
6990
  code: ErrorCodes.TIMEOUT,
6973
- message: "Request timed out.",
6974
- service
6991
+ message: `Request timed out after ${timeoutMs}ms.`,
6992
+ service,
6993
+ ...cause === void 0 ? {} : { cause },
6994
+ meta: { timeoutMs }
6975
6995
  };
6976
6996
  }
6977
6997
  function abortedError(service) {
@@ -7118,6 +7138,9 @@ function storageRejectionError(service, status, meta, responseText) {
7118
7138
  }
7119
7139
  function wrapError2(service, error, defaultCode = ErrorCodes.NETWORK_ERROR) {
7120
7140
  if (error instanceof Error) {
7141
+ if (isRequestTimeoutError(error)) {
7142
+ return timeoutError(service, error.timeoutMs, error);
7143
+ }
7121
7144
  if (error.name === "AbortError") {
7122
7145
  return abortedError(service);
7123
7146
  }
@@ -7137,6 +7160,7 @@ function wrapError2(service, error, defaultCode = ErrorCodes.NETWORK_ERROR) {
7137
7160
  service
7138
7161
  };
7139
7162
  }
7163
+ var MAX_TIMER_DELAY_MS = 2147483647;
7140
7164
  var BaseService = class {
7141
7165
  constructor() {
7142
7166
  this.abortController = new AbortController();
@@ -7310,6 +7334,64 @@ var BaseService = class {
7310
7334
  }
7311
7335
  return controller.signal;
7312
7336
  }
7337
+ /**
7338
+ * Create the abort signal for one request, bounded by the request timeout.
7339
+ *
7340
+ * The signal aborts when the service signs out, the context aborts, the
7341
+ * caller's `signal` aborts, or the timeout elapses, whichever comes first.
7342
+ * A timeout aborts with a {@link RequestTimeoutError} (`TimeoutError`), which
7343
+ * `wrapError` maps to `ErrorCodes.TIMEOUT`; the other sources keep their own
7344
+ * reason (normally `AbortError`, mapped to `ErrorCodes.ABORTED`).
7345
+ *
7346
+ * The timeout is `timeoutMs` when given, otherwise `config.timeout`. Only a
7347
+ * positive, finite value (at most 2^31-1 ms) applies; `undefined`, `0`, and
7348
+ * `Infinity` mean no timeout, so a per-call `0` opts out of a configured one.
7349
+ *
7350
+ * Callers must call `dispose()` once the response body has been consumed,
7351
+ * which clears the timer and removes the listeners this adds to the
7352
+ * long-lived service and context signals.
7353
+ *
7354
+ * @param signal - The caller's abort signal for this request
7355
+ * @param timeoutMs - Per-request timeout override in milliseconds
7356
+ */
7357
+ createRequestSignal(signal, timeoutMs) {
7358
+ const controller = new AbortController();
7359
+ const detachers = [];
7360
+ let timer;
7361
+ const dispose = () => {
7362
+ if (timer !== void 0) {
7363
+ clearTimeout(timer);
7364
+ timer = void 0;
7365
+ }
7366
+ for (const detach of detachers.splice(0)) {
7367
+ detach();
7368
+ }
7369
+ };
7370
+ const abort = (reason) => {
7371
+ dispose();
7372
+ controller.abort(reason);
7373
+ };
7374
+ const requestSignal = { signal: controller.signal, dispose };
7375
+ const parents = [
7376
+ this.abortController.signal,
7377
+ this.context?.abortSignal,
7378
+ signal
7379
+ ].filter((parent) => parent !== void 0);
7380
+ for (const parent of parents) {
7381
+ if (parent.aborted) {
7382
+ abort(parent.reason);
7383
+ return requestSignal;
7384
+ }
7385
+ const onAbort = () => abort(parent.reason);
7386
+ parent.addEventListener("abort", onAbort, { once: true });
7387
+ detachers.push(() => parent.removeEventListener("abort", onAbort));
7388
+ }
7389
+ const timeout = timeoutMs ?? this._config.timeout;
7390
+ if (typeof timeout === "number" && timeout > 0 && timeout <= MAX_TIMER_DELAY_MS) {
7391
+ timer = setTimeout(() => abort(new RequestTimeoutError(timeout)), timeout);
7392
+ }
7393
+ return requestSignal;
7394
+ }
7313
7395
  /**
7314
7396
  * Wrap an operation with error handling and telemetry.
7315
7397
  *
@@ -7564,7 +7646,7 @@ var KVService = class extends BaseService {
7564
7646
  * @param path - Resource path
7565
7647
  * @param action - KV action
7566
7648
  * @param body - Optional request body
7567
- * @param signal - Optional abort signal
7649
+ * @param signal - Request signal from createRequestSignal()
7568
7650
  * @returns Fetch response
7569
7651
  */
7570
7652
  async invokeOperation(path, action, body, signal, extraHeaders) {
@@ -7580,7 +7662,7 @@ var KVService = class extends BaseService {
7580
7662
  method: "POST",
7581
7663
  headers: requestHeaders,
7582
7664
  body,
7583
- signal: this.combineSignals(signal)
7665
+ signal
7584
7666
  });
7585
7667
  }
7586
7668
  /**
@@ -7757,6 +7839,7 @@ var KVService = class extends BaseService {
7757
7839
  "kv"
7758
7840
  ));
7759
7841
  }
7842
+ const request = this.createRequestSignal(options?.signal, options?.timeout);
7760
7843
  try {
7761
7844
  const session = this.context.session;
7762
7845
  const invocationHeaders = this.context.invokeAny(
@@ -7778,7 +7861,7 @@ var KVService = class extends BaseService {
7778
7861
  const response = await this.context.fetch(`${this.host}/invoke`, {
7779
7862
  method: "POST",
7780
7863
  headers,
7781
- signal: this.combineSignals(options?.signal)
7864
+ signal: request.signal
7782
7865
  });
7783
7866
  if (!response.ok) {
7784
7867
  const errorText = response.status === 401 || response.status === 403 ? await this.readAuthorizationText(response) : await response.text();
@@ -7843,6 +7926,8 @@ var KVService = class extends BaseService {
7843
7926
  return ok({ results, count: results.length });
7844
7927
  } catch (error) {
7845
7928
  return err(wrapError2("kv", error));
7929
+ } finally {
7930
+ request.dispose();
7846
7931
  }
7847
7932
  }
7848
7933
  /**
@@ -7975,12 +8060,13 @@ var KVService = class extends BaseService {
7975
8060
  ));
7976
8061
  }
7977
8062
  const path = this.getFullPath(key, options?.prefix);
8063
+ const request = this.createRequestSignal(options?.signal, options?.timeout);
7978
8064
  try {
7979
8065
  const response = await this.invokeOperation(
7980
8066
  path,
7981
8067
  KVAction.GET,
7982
8068
  void 0,
7983
- options?.signal,
8069
+ request.signal,
7984
8070
  options?.maxResponseBytes === void 0 ? void 0 : { "x-tinycloud-max-response-bytes": String(options.maxResponseBytes) }
7985
8071
  );
7986
8072
  if (!response.ok) {
@@ -8037,6 +8123,8 @@ var KVService = class extends BaseService {
8037
8123
  });
8038
8124
  } catch (error) {
8039
8125
  return err(wrapError2("kv", error));
8126
+ } finally {
8127
+ request.dispose();
8040
8128
  }
8041
8129
  });
8042
8130
  }
@@ -8064,12 +8152,13 @@ var KVService = class extends BaseService {
8064
8152
  }
8065
8153
  const path = this.getFullPath(key, options?.prefix);
8066
8154
  const body = this.serializePutValue(value, options?.contentType);
8155
+ const request = this.createRequestSignal(options?.signal, options?.timeout);
8067
8156
  try {
8068
8157
  const response = await this.invokeOperation(
8069
8158
  path,
8070
8159
  KVAction.PUT,
8071
8160
  body,
8072
- options?.signal,
8161
+ request.signal,
8073
8162
  {
8074
8163
  ...options?.ifMatch === void 0 ? {} : { "if-match": options.ifMatch },
8075
8164
  ...options?.ifNoneMatch === void 0 ? {} : { "if-none-match": options.ifNoneMatch }
@@ -8125,6 +8214,8 @@ var KVService = class extends BaseService {
8125
8214
  });
8126
8215
  } catch (error) {
8127
8216
  return err(wrapError2("kv", error));
8217
+ } finally {
8218
+ request.dispose();
8128
8219
  }
8129
8220
  });
8130
8221
  }
@@ -8164,6 +8255,7 @@ var KVService = class extends BaseService {
8164
8255
  seen.add(path);
8165
8256
  }
8166
8257
  let requestMayHaveDispatched = false;
8258
+ const request = this.createRequestSignal(options?.signal, options?.timeout);
8167
8259
  try {
8168
8260
  const body = new FormData();
8169
8261
  for (let index = 0; index < items.length; index++) {
@@ -8186,7 +8278,7 @@ var KVService = class extends BaseService {
8186
8278
  method: "POST",
8187
8279
  headers,
8188
8280
  body,
8189
- signal: this.combineSignals(options?.signal)
8281
+ signal: request.signal
8190
8282
  };
8191
8283
  const fetchFn = this.context.fetch;
8192
8284
  requestMayHaveDispatched = true;
@@ -8237,14 +8329,16 @@ var KVService = class extends BaseService {
8237
8329
  rawBody = await response.json();
8238
8330
  } catch (jsonError) {
8239
8331
  const cause = jsonError instanceof Error ? jsonError : new Error(String(jsonError));
8332
+ const timedOut = isRequestTimeoutError(cause);
8240
8333
  return err(
8241
8334
  serviceError(
8242
- ErrorCodes.NETWORK_ERROR,
8243
- `KV batchPut response was not valid JSON: ${cause.message}`,
8335
+ timedOut ? ErrorCodes.TIMEOUT : ErrorCodes.NETWORK_ERROR,
8336
+ timedOut ? `KV batchPut response body did not arrive in time: ${cause.message}` : `KV batchPut response was not valid JSON: ${cause.message}`,
8244
8337
  "kv",
8245
8338
  {
8246
8339
  cause,
8247
8340
  meta: {
8341
+ ...timedOut ? { timeoutMs: cause.timeoutMs } : {},
8248
8342
  requestMayHaveDispatched: true,
8249
8343
  responseReceived: true,
8250
8344
  status: response.status,
@@ -8299,6 +8393,8 @@ var KVService = class extends BaseService {
8299
8393
  ...wrapped,
8300
8394
  meta: { ...wrapped.meta, requestMayHaveDispatched }
8301
8395
  });
8396
+ } finally {
8397
+ request.dispose();
8302
8398
  }
8303
8399
  });
8304
8400
  }
@@ -8321,12 +8417,13 @@ var KVService = class extends BaseService {
8321
8417
  if (options?.path) {
8322
8418
  listPath = listPath ? `${listPath}/${options.path}` : options.path;
8323
8419
  }
8420
+ const request = this.createRequestSignal(options?.signal, options?.timeout);
8324
8421
  try {
8325
8422
  const response = await this.invokeOperation(
8326
8423
  listPath,
8327
8424
  KVAction.LIST,
8328
8425
  void 0,
8329
- options?.signal,
8426
+ request.signal,
8330
8427
  options?.limit === void 0 && options?.cursor === void 0 ? void 0 : {
8331
8428
  ...options?.limit === void 0 ? {} : { "x-tinycloud-limit": String(options.limit) },
8332
8429
  ...options?.cursor === void 0 ? {} : { "x-tinycloud-cursor": options.cursor }
@@ -8367,6 +8464,8 @@ var KVService = class extends BaseService {
8367
8464
  });
8368
8465
  } catch (error) {
8369
8466
  return err(wrapError2("kv", error));
8467
+ } finally {
8468
+ request.dispose();
8370
8469
  }
8371
8470
  });
8372
8471
  }
@@ -8379,12 +8478,13 @@ var KVService = class extends BaseService {
8379
8478
  return err(authRequiredError("kv"));
8380
8479
  }
8381
8480
  const path = this.getFullPath(key, options?.prefix);
8481
+ const request = this.createRequestSignal(options?.signal, options?.timeout);
8382
8482
  try {
8383
8483
  const response = await this.invokeOperation(
8384
8484
  path,
8385
8485
  KVAction.DELETE,
8386
8486
  void 0,
8387
- options?.signal,
8487
+ request.signal,
8388
8488
  options?.ifMatch === void 0 ? void 0 : { "if-match": options.ifMatch }
8389
8489
  );
8390
8490
  if (!response.ok) {
@@ -8432,6 +8532,8 @@ var KVService = class extends BaseService {
8432
8532
  });
8433
8533
  } catch (error) {
8434
8534
  return err(wrapError2("kv", error));
8535
+ } finally {
8536
+ request.dispose();
8435
8537
  }
8436
8538
  });
8437
8539
  }
@@ -8444,12 +8546,13 @@ var KVService = class extends BaseService {
8444
8546
  return err(authRequiredError("kv"));
8445
8547
  }
8446
8548
  const path = this.getFullPath(key, options?.prefix);
8549
+ const request = this.createRequestSignal(options?.signal, options?.timeout);
8447
8550
  try {
8448
8551
  const response = await this.invokeOperation(
8449
8552
  path,
8450
8553
  KVAction.HEAD,
8451
8554
  void 0,
8452
- options?.signal
8555
+ request.signal
8453
8556
  );
8454
8557
  if (!response.ok) {
8455
8558
  if (response.status === 401 || response.status === 403) {
@@ -8480,6 +8583,8 @@ var KVService = class extends BaseService {
8480
8583
  });
8481
8584
  } catch (error) {
8482
8585
  return err(wrapError2("kv", error));
8586
+ } finally {
8587
+ request.dispose();
8483
8588
  }
8484
8589
  });
8485
8590
  }
@@ -8517,12 +8622,13 @@ var KVService = class extends BaseService {
8517
8622
  if (options?.etag !== void 0) {
8518
8623
  body.etag = options.etag;
8519
8624
  }
8625
+ const request = this.createRequestSignal(options?.signal, options?.timeout);
8520
8626
  try {
8521
8627
  const response = await this.context.fetch(`${this.host}/signed/kv`, {
8522
8628
  method: "POST",
8523
8629
  headers: this.withJsonContentType(headers),
8524
8630
  body: JSON.stringify(body),
8525
- signal: this.combineSignals(options?.signal)
8631
+ signal: request.signal
8526
8632
  });
8527
8633
  if (!response.ok) {
8528
8634
  return this.createSignedReadUrlError(response, key, path);
@@ -8542,6 +8648,8 @@ var KVService = class extends BaseService {
8542
8648
  return ok(signedUrl);
8543
8649
  } catch (error) {
8544
8650
  return err(wrapError2("kv", error));
8651
+ } finally {
8652
+ request.dispose();
8545
8653
  }
8546
8654
  });
8547
8655
  }
@@ -8741,6 +8849,7 @@ var SQLService = class extends BaseService {
8741
8849
  if (!this.requireAuth()) {
8742
8850
  return err(authRequiredError("sql"));
8743
8851
  }
8852
+ const request = this.createRequestSignal(options?.signal);
8744
8853
  try {
8745
8854
  const body = {
8746
8855
  action: "query",
@@ -8754,7 +8863,7 @@ var SQLService = class extends BaseService {
8754
8863
  dbName,
8755
8864
  action,
8756
8865
  body,
8757
- options?.signal
8866
+ request.signal
8758
8867
  );
8759
8868
  if (!response.ok) {
8760
8869
  return this.handleErrorResponse(response, "query", dbName, [action]);
@@ -8763,6 +8872,8 @@ var SQLService = class extends BaseService {
8763
8872
  return ok(data);
8764
8873
  } catch (error) {
8765
8874
  return err(wrapError2("sql", error));
8875
+ } finally {
8876
+ request.dispose();
8766
8877
  }
8767
8878
  });
8768
8879
  }
@@ -8771,6 +8882,7 @@ var SQLService = class extends BaseService {
8771
8882
  if (!this.requireAuth()) {
8772
8883
  return err(authRequiredError("sql"));
8773
8884
  }
8885
+ const request = this.createRequestSignal(options?.signal);
8774
8886
  try {
8775
8887
  const body = {
8776
8888
  action: "execute",
@@ -8791,7 +8903,7 @@ var SQLService = class extends BaseService {
8791
8903
  dbName,
8792
8904
  requestedActions2,
8793
8905
  body,
8794
- options?.signal
8906
+ request.signal
8795
8907
  );
8796
8908
  if (!response.ok) {
8797
8909
  return this.handleErrorResponse(response, "execute", dbName, requestedActions2);
@@ -8800,6 +8912,8 @@ var SQLService = class extends BaseService {
8800
8912
  return ok(data);
8801
8913
  } catch (error) {
8802
8914
  return err(wrapError2("sql", error));
8915
+ } finally {
8916
+ request.dispose();
8803
8917
  }
8804
8918
  });
8805
8919
  }
@@ -8808,13 +8922,14 @@ var SQLService = class extends BaseService {
8808
8922
  if (!this.requireAuth()) {
8809
8923
  return err(authRequiredError("sql"));
8810
8924
  }
8925
+ const request = this.createRequestSignal(options?.signal);
8811
8926
  try {
8812
8927
  const requestedActions2 = this.actionsForSqlBatch(statements);
8813
8928
  const response = await this.invokeSQL(
8814
8929
  dbName,
8815
8930
  requestedActions2,
8816
8931
  { action: "batch", statements },
8817
- options?.signal
8932
+ request.signal
8818
8933
  );
8819
8934
  if (!response.ok) {
8820
8935
  return this.handleErrorResponse(response, "batch", dbName, requestedActions2);
@@ -8823,6 +8938,8 @@ var SQLService = class extends BaseService {
8823
8938
  return ok(data);
8824
8939
  } catch (error) {
8825
8940
  return err(wrapError2("sql", error));
8941
+ } finally {
8942
+ request.dispose();
8826
8943
  }
8827
8944
  });
8828
8945
  }
@@ -8831,12 +8948,13 @@ var SQLService = class extends BaseService {
8831
8948
  if (!this.requireAuth()) {
8832
8949
  return err(authRequiredError("sql"));
8833
8950
  }
8951
+ const request = this.createRequestSignal(options?.signal);
8834
8952
  try {
8835
8953
  const response = await this.invokeSQL(
8836
8954
  dbName,
8837
8955
  SQLAction.WRITE,
8838
8956
  { action: "execute_statement", name, params: params ?? [] },
8839
- options?.signal
8957
+ request.signal
8840
8958
  );
8841
8959
  if (!response.ok) {
8842
8960
  return this.handleErrorResponse(response, "executeStatement", dbName, [SQLAction.WRITE]);
@@ -8845,6 +8963,8 @@ var SQLService = class extends BaseService {
8845
8963
  return ok(data);
8846
8964
  } catch (error) {
8847
8965
  return err(wrapError2("sql", error));
8966
+ } finally {
8967
+ request.dispose();
8848
8968
  }
8849
8969
  });
8850
8970
  }
@@ -8853,12 +8973,13 @@ var SQLService = class extends BaseService {
8853
8973
  if (!this.requireAuth()) {
8854
8974
  return err(authRequiredError("sql"));
8855
8975
  }
8976
+ const request = this.createRequestSignal(options?.signal);
8856
8977
  try {
8857
8978
  const response = await this.invokeSQL(
8858
8979
  dbName,
8859
8980
  SQLAction.READ,
8860
8981
  { action: "export" },
8861
- options?.signal
8982
+ request.signal
8862
8983
  );
8863
8984
  if (!response.ok) {
8864
8985
  return this.handleErrorResponse(response, "export", dbName, [SQLAction.READ]);
@@ -8872,6 +8993,8 @@ var SQLService = class extends BaseService {
8872
8993
  return ok(text);
8873
8994
  } catch (error) {
8874
8995
  return err(wrapError2("sql", error));
8996
+ } finally {
8997
+ request.dispose();
8875
8998
  }
8876
8999
  });
8877
9000
  }
@@ -8962,7 +9085,7 @@ var SQLService = class extends BaseService {
8962
9085
  "Content-Type": "application/json"
8963
9086
  },
8964
9087
  body: JSON.stringify(body),
8965
- signal: this.combineSignals(signal)
9088
+ signal
8966
9089
  });
8967
9090
  }
8968
9091
  actionForSql(sql, fallback) {
@@ -9219,12 +9342,13 @@ var DuckDbService = class extends BaseService {
9219
9342
  if (!this.requireAuth()) {
9220
9343
  return err(authRequiredError("duckdb"));
9221
9344
  }
9345
+ const request = this.createRequestSignal(options?.signal);
9222
9346
  try {
9223
9347
  const response = await this.invokeDuckDb(
9224
9348
  dbName,
9225
9349
  DuckDbAction.READ,
9226
9350
  { action: "query", sql, params: params ?? [] },
9227
- options?.signal
9351
+ request.signal
9228
9352
  );
9229
9353
  if (!response.ok) {
9230
9354
  return this.handleErrorResponse(response, "query");
@@ -9233,6 +9357,8 @@ var DuckDbService = class extends BaseService {
9233
9357
  return ok(data);
9234
9358
  } catch (error) {
9235
9359
  return err(wrapError2("duckdb", error));
9360
+ } finally {
9361
+ request.dispose();
9236
9362
  }
9237
9363
  });
9238
9364
  }
@@ -9241,12 +9367,13 @@ var DuckDbService = class extends BaseService {
9241
9367
  if (!this.requireAuth()) {
9242
9368
  return err(authRequiredError("duckdb"));
9243
9369
  }
9370
+ const request = this.createRequestSignal(options?.signal);
9244
9371
  try {
9245
9372
  const response = await this.invokeDuckDb(
9246
9373
  dbName,
9247
9374
  DuckDbAction.READ,
9248
9375
  { action: "query", sql, params: params ?? [] },
9249
- options?.signal,
9376
+ request.signal,
9250
9377
  { Accept: "application/vnd.apache.arrow.stream" }
9251
9378
  );
9252
9379
  if (!response.ok) {
@@ -9256,6 +9383,8 @@ var DuckDbService = class extends BaseService {
9256
9383
  return ok(buffer);
9257
9384
  } catch (error) {
9258
9385
  return err(wrapError2("duckdb", error));
9386
+ } finally {
9387
+ request.dispose();
9259
9388
  }
9260
9389
  });
9261
9390
  }
@@ -9264,6 +9393,7 @@ var DuckDbService = class extends BaseService {
9264
9393
  if (!this.requireAuth()) {
9265
9394
  return err(authRequiredError("duckdb"));
9266
9395
  }
9396
+ const request = this.createRequestSignal(options?.signal);
9267
9397
  try {
9268
9398
  const body = {
9269
9399
  action: "execute",
@@ -9277,7 +9407,7 @@ var DuckDbService = class extends BaseService {
9277
9407
  dbName,
9278
9408
  DuckDbAction.WRITE,
9279
9409
  body,
9280
- options?.signal
9410
+ request.signal
9281
9411
  );
9282
9412
  if (!response.ok) {
9283
9413
  return this.handleErrorResponse(response, "execute");
@@ -9286,6 +9416,8 @@ var DuckDbService = class extends BaseService {
9286
9416
  return ok(data);
9287
9417
  } catch (error) {
9288
9418
  return err(wrapError2("duckdb", error));
9419
+ } finally {
9420
+ request.dispose();
9289
9421
  }
9290
9422
  });
9291
9423
  }
@@ -9294,6 +9426,7 @@ var DuckDbService = class extends BaseService {
9294
9426
  if (!this.requireAuth()) {
9295
9427
  return err(authRequiredError("duckdb"));
9296
9428
  }
9429
+ const request = this.createRequestSignal(options?.signal);
9297
9430
  try {
9298
9431
  const body = {
9299
9432
  action: "batch",
@@ -9306,7 +9439,7 @@ var DuckDbService = class extends BaseService {
9306
9439
  dbName,
9307
9440
  DuckDbAction.WRITE,
9308
9441
  body,
9309
- options?.signal
9442
+ request.signal
9310
9443
  );
9311
9444
  if (!response.ok) {
9312
9445
  return this.handleErrorResponse(response, "batch");
@@ -9315,6 +9448,8 @@ var DuckDbService = class extends BaseService {
9315
9448
  return ok(data);
9316
9449
  } catch (error) {
9317
9450
  return err(wrapError2("duckdb", error));
9451
+ } finally {
9452
+ request.dispose();
9318
9453
  }
9319
9454
  });
9320
9455
  }
@@ -9323,12 +9458,13 @@ var DuckDbService = class extends BaseService {
9323
9458
  if (!this.requireAuth()) {
9324
9459
  return err(authRequiredError("duckdb"));
9325
9460
  }
9461
+ const request = this.createRequestSignal(options?.signal);
9326
9462
  try {
9327
9463
  const response = await this.invokeDuckDb(
9328
9464
  dbName,
9329
9465
  DuckDbAction.WRITE,
9330
9466
  { action: "executeStatement", name, params: params ?? [] },
9331
- options?.signal
9467
+ request.signal
9332
9468
  );
9333
9469
  if (!response.ok) {
9334
9470
  return this.handleErrorResponse(response, "executeStatement");
@@ -9337,6 +9473,8 @@ var DuckDbService = class extends BaseService {
9337
9473
  return ok(data);
9338
9474
  } catch (error) {
9339
9475
  return err(wrapError2("duckdb", error));
9476
+ } finally {
9477
+ request.dispose();
9340
9478
  }
9341
9479
  });
9342
9480
  }
@@ -9345,12 +9483,13 @@ var DuckDbService = class extends BaseService {
9345
9483
  if (!this.requireAuth()) {
9346
9484
  return err(authRequiredError("duckdb"));
9347
9485
  }
9486
+ const request = this.createRequestSignal(options?.signal);
9348
9487
  try {
9349
9488
  const response = await this.invokeDuckDb(
9350
9489
  dbName,
9351
9490
  DuckDbAction.READ,
9352
9491
  { action: "describe" },
9353
- options?.signal
9492
+ request.signal
9354
9493
  );
9355
9494
  if (!response.ok) {
9356
9495
  return this.handleErrorResponse(response, "describe");
@@ -9359,6 +9498,8 @@ var DuckDbService = class extends BaseService {
9359
9498
  return ok(data);
9360
9499
  } catch (error) {
9361
9500
  return err(wrapError2("duckdb", error));
9501
+ } finally {
9502
+ request.dispose();
9362
9503
  }
9363
9504
  });
9364
9505
  }
@@ -9367,12 +9508,13 @@ var DuckDbService = class extends BaseService {
9367
9508
  if (!this.requireAuth()) {
9368
9509
  return err(authRequiredError("duckdb"));
9369
9510
  }
9511
+ const request = this.createRequestSignal(options?.signal);
9370
9512
  try {
9371
9513
  const response = await this.invokeDuckDb(
9372
9514
  dbName,
9373
9515
  DuckDbAction.EXPORT,
9374
9516
  { action: "export" },
9375
- options?.signal
9517
+ request.signal
9376
9518
  );
9377
9519
  if (!response.ok) {
9378
9520
  return this.handleErrorResponse(response, "export");
@@ -9381,6 +9523,8 @@ var DuckDbService = class extends BaseService {
9381
9523
  return ok(blob);
9382
9524
  } catch (error) {
9383
9525
  return err(wrapError2("duckdb", error));
9526
+ } finally {
9527
+ request.dispose();
9384
9528
  }
9385
9529
  });
9386
9530
  }
@@ -9389,6 +9533,7 @@ var DuckDbService = class extends BaseService {
9389
9533
  if (!this.requireAuth()) {
9390
9534
  return err(authRequiredError("duckdb"));
9391
9535
  }
9536
+ const request = this.createRequestSignal(options?.signal);
9392
9537
  try {
9393
9538
  const session = this.context.session;
9394
9539
  const headers = this.context.invoke(
@@ -9404,7 +9549,7 @@ var DuckDbService = class extends BaseService {
9404
9549
  "Content-Type": "application/x-duckdb"
9405
9550
  },
9406
9551
  body: new Blob([data]),
9407
- signal: this.combineSignals(options?.signal)
9552
+ signal: request.signal
9408
9553
  });
9409
9554
  if (!response.ok) {
9410
9555
  return this.handleErrorResponse(response, "import");
@@ -9412,6 +9557,8 @@ var DuckDbService = class extends BaseService {
9412
9557
  return ok(void 0);
9413
9558
  } catch (error) {
9414
9559
  return err(wrapError2("duckdb", error));
9560
+ } finally {
9561
+ request.dispose();
9415
9562
  }
9416
9563
  });
9417
9564
  }
@@ -9427,7 +9574,7 @@ var DuckDbService = class extends BaseService {
9427
9574
  ...extraHeaders
9428
9575
  },
9429
9576
  body: JSON.stringify(body),
9430
- signal: this.combineSignals(signal)
9577
+ signal
9431
9578
  });
9432
9579
  }
9433
9580
  async handleErrorResponse(response, operation) {
@@ -14970,6 +15117,34 @@ async function commitLogin(profileName, snapshot, commit) {
14970
15117
  });
14971
15118
  }
14972
15119
 
15120
+ // src/auth/owner-key.ts
15121
+ init_theme();
15122
+ function openKeyPrimaryFlag(delegation) {
15123
+ return typeof delegation.primary === "boolean" ? delegation.primary : void 0;
15124
+ }
15125
+ function withOwnerKeyPrimary(profile, primary) {
15126
+ const { ownerKeyPrimary: _previous, ...rest } = profile;
15127
+ return primary === void 0 ? rest : { ...rest, ownerKeyPrimary: primary };
15128
+ }
15129
+ function nonPrimaryKeyWarning(ownerDid) {
15130
+ const address = ownerDid.split(":")[4] ?? ownerDid;
15131
+ return `${theme.warn("Warning:")} you signed in with OpenKey key ${address} (${ownerDid}), which is not your account's primary OpenKey key. This key is a separate owner with its own spaces and data, so this profile does not see the data stored under your primary key.
15132
+ To use your primary key, log in again and choose the primary key in OpenKey, for example into a new profile: tc init --name <profile>
15133
+ `;
15134
+ }
15135
+ function warnIfNotPrimaryKey(ownerDid, primary) {
15136
+ if (primary !== false || ownerDid === void 0) return;
15137
+ process.stderr.write(nonPrimaryKeyWarning(ownerDid));
15138
+ }
15139
+ function ownerLoginPermissions(owner) {
15140
+ const space = ownerSpaceId("default", owner);
15141
+ return [
15142
+ { service: "tinycloud.kv", space, path: "", actions: ["tinycloud.kv/put", "tinycloud.kv/get", "tinycloud.kv/del", "tinycloud.kv/list", "tinycloud.kv/metadata"] },
15143
+ { service: "tinycloud.sql", space, path: "", actions: ["tinycloud.sql/read", "tinycloud.sql/write", "tinycloud.sql/admin"] },
15144
+ { service: "tinycloud.capabilities", space, path: "", actions: ["tinycloud.capabilities/read"] }
15145
+ ];
15146
+ }
15147
+
14973
15148
  // src/auth/device-auth.ts
14974
15149
  var DEVICE_DELEGATION_MAX_SECONDS = 30 * 24 * 60 * 60;
14975
15150
  var DEVICE_APPROVAL_WINDOW_MAX_SECONDS = 60 * 60;
@@ -15174,7 +15349,8 @@ async function verifyApproval(input) {
15174
15349
  spaceId: delegation.spaceId,
15175
15350
  expiresAt: session.expiresAt,
15176
15351
  approved: permissionsFromTuples(signed),
15177
- declined: declinedPermissions(input.requested, session.permissions, ownerDid)
15352
+ declined: declinedPermissions(input.requested, session.permissions, ownerDid),
15353
+ ...openKeyPrimaryFlag(delegation) === void 0 ? {} : { primary: openKeyPrimaryFlag(delegation) }
15178
15354
  };
15179
15355
  }
15180
15356
  function writeApprovalPrompt(prompt) {
@@ -15351,7 +15527,7 @@ async function loginWithDeviceAuthorization(input) {
15351
15527
  openkeyHost: input.openkeyHost ?? resolveDeviceApiHost(existing),
15352
15528
  expectedOwner
15353
15529
  });
15354
- const profile = {
15530
+ const profile = withOwnerKeyPrimary({
15355
15531
  ...existing,
15356
15532
  name: input.profileName,
15357
15533
  host: input.persistHost === true || !existing?.host ? input.nodeOrigin : existing.host,
@@ -15365,7 +15541,7 @@ async function loginWithDeviceAuthorization(input) {
15365
15541
  posture: "owner-openkey",
15366
15542
  operatorType: existing?.operatorType ?? "human",
15367
15543
  authMethod: "openkey"
15368
- };
15544
+ }, result.primary);
15369
15545
  await commitLogin(input.profileName, snapshot, {
15370
15546
  key,
15371
15547
  session: result.session,
@@ -15373,6 +15549,7 @@ async function loginWithDeviceAuthorization(input) {
15373
15549
  // The signed permissions, with their caveats; `approved` is the action summary.
15374
15550
  approved: { scope: result.session.permissions, ownerDid: result.ownerDid, replaceSession: input.replaceSession === true }
15375
15551
  });
15552
+ warnIfNotPrimaryKey(result.ownerDid, result.primary);
15376
15553
  return { profile, result };
15377
15554
  }
15378
15555
 
@@ -15410,7 +15587,7 @@ async function promptAuthMethod() {
15410
15587
  }
15411
15588
  function registerAuthCommand(program) {
15412
15589
  const auth = program.command("auth").description("Authentication management");
15413
- auth.command("login").description("Authenticate with TinyCloud").option("--device", "Approve on another device (e.g. a phone) through OpenKey device authorization; requires a KV-scoped --manifest (SQL and secret decrypt need browser or --paste login)").option("--paste", "Use manual paste mode instead of browser callback").option("--no-popup", "Print the OpenKey URL without opening a browser").option("--method <method>", "Authentication method: local or openkey").option("--manifest <fileOrBase64>", `Request only this manifest's permissions (one space, plus raw encryption network entries such as a secrets decrypt grant); ${SHARE_PUBLISHING_MANIFEST_REF} covers \`tc share publish\``).option("--expiry <duration>", "OpenKey session lifetime, e.g. 1h or 7d (device login: at most 30d, default 30d)").option("--owner <did>", "Require this existing primary DID (did:pkh:eip155:CHAIN:ADDRESS) for scoped login; names the secrets owner for a manifest's `secrets` on a profile with no recorded owner").option("--replace-session", "Scoped or device login: replace this profile's live session even though the new scope would narrow, change or shorten it (prefer a new profile)").action(async (options, cmd) => {
15590
+ auth.command("login").description("Authenticate with TinyCloud").option("--device", "Approve on another device (e.g. a phone) through OpenKey device authorization; requires a KV-scoped --manifest (SQL and secret decrypt need browser or --paste login)").option("--paste", "Use manual paste mode instead of browser callback").option("--no-popup", "Print the OpenKey URL without opening a browser").option("--method <method>", "Authentication method: local or openkey").option("--manifest <fileOrBase64>", `Request only this manifest's permissions (one space, plus raw encryption network entries such as a secrets decrypt grant); ${SHARE_PUBLISHING_MANIFEST_REF} covers \`tc share publish\``).option("--expiry <duration>", "OpenKey session lifetime, e.g. 1h or 7d (device login: at most 30d, default 30d)").option("--owner <did>", "Sign in as this owner (did:pkh:eip155:CHAIN:ADDRESS): OpenKey preselects that key, e.g. one that is not the account's primary key, and an approval by any other identity is refused. With --manifest, also names the secrets owner for a manifest's `secrets` on a profile with no recorded owner").option("--replace-session", "Scoped or device login: replace this profile's live session even though the new scope would narrow, change or shorten it (prefer a new profile)").action(async (options, cmd) => {
15414
15591
  try {
15415
15592
  if (options.device && options.paste) {
15416
15593
  throw new CLIError("INVALID_ARGUMENT", "--device and --paste are mutually exclusive.", ExitCode.USAGE_ERROR);
@@ -15419,9 +15596,6 @@ function registerAuthCommand(program) {
15419
15596
  if (scoped && options.method === "local") {
15420
15597
  throw new CLIError("INVALID_ARGUMENT", "--device, --manifest, --expiry and --owner require OpenKey login.", ExitCode.USAGE_ERROR);
15421
15598
  }
15422
- if (options.owner && !options.manifest) {
15423
- throw new CLIError("INVALID_ARGUMENT", "--owner requires --manifest so the signed identity is verified.", ExitCode.USAGE_ERROR);
15424
- }
15425
15599
  if (options.device && !options.manifest) {
15426
15600
  throw new CLIError(
15427
15601
  "MANIFEST_REQUIRED",
@@ -15544,6 +15718,7 @@ function registerAuthCommand(program) {
15544
15718
  did: profile?.did ?? null,
15545
15719
  sessionDid: profile?.sessionDid ?? null,
15546
15720
  ownerDid: profile?.ownerDid ?? null,
15721
+ ownerKeyPrimary: profile?.ownerKeyPrimary ?? null,
15547
15722
  spaceId: profile?.spaceId ?? null,
15548
15723
  host: ctx.host,
15549
15724
  profile: ctx.profile,
@@ -15564,6 +15739,7 @@ function registerAuthCommand(program) {
15564
15739
  process.stdout.write(formatField("DID", profile?.did ?? null) + "\n");
15565
15740
  process.stdout.write(formatField("Session DID", profile?.sessionDid ?? null) + "\n");
15566
15741
  process.stdout.write(formatField("Owner DID", profile?.ownerDid ?? null) + "\n");
15742
+ process.stdout.write(formatField("Primary Key", profile?.ownerKeyPrimary ?? "unknown") + "\n");
15567
15743
  process.stdout.write(formatField("Address", profile?.address ?? null) + "\n");
15568
15744
  process.stdout.write(formatField("Space ID", profile?.spaceId ?? null) + "\n");
15569
15745
  process.stdout.write(formatField("Has Key", hasKey !== null) + "\n");
@@ -16636,7 +16812,9 @@ async function refreshOpenKeySession(profileName, host, options = {}) {
16636
16812
  jwk: key,
16637
16813
  host,
16638
16814
  openkeyHost: resolveOpenKeyHost(profile),
16639
- permissions,
16815
+ // A plain login with --owner names that owner's space, so OpenKey
16816
+ // preselects its key; the signed owner is verified below.
16817
+ permissions: permissions ?? (options.expectedOwner === void 0 ? void 0 : ownerLoginPermissions(expectedOwner)),
16640
16818
  expiry: openKeyExpiry,
16641
16819
  ...permissions ? { reason: "Allow this TinyCloud CLI profile to use the permissions in the requested manifest." } : {}
16642
16820
  });
@@ -16662,7 +16840,8 @@ async function refreshOpenKeySession(profileName, host, options = {}) {
16662
16840
  sanitizedSession = Object.fromEntries(Object.entries(withoutTrustFields(merged)).filter(([name]) => name !== "siwe" && name !== "signature"));
16663
16841
  }
16664
16842
  }
16665
- const updatedProfile = {
16843
+ const ownerKeyPrimary = verifiedOwner === void 0 ? void 0 : openKeyPrimaryFlag(delegationData);
16844
+ const updatedProfile = withOwnerKeyPrimary({
16666
16845
  ...profile,
16667
16846
  host: options.persistHost === true || !profile.host ? host : profile.host,
16668
16847
  sessionDid: profile.sessionDid ?? profile.did,
@@ -16671,13 +16850,14 @@ async function refreshOpenKeySession(profileName, host, options = {}) {
16671
16850
  authMethod: "openkey",
16672
16851
  ...typeof sanitizedSession.spaceId === "string" ? { spaceId: sanitizedSession.spaceId } : {},
16673
16852
  ...verifiedOwner === void 0 ? {} : { ownerDid: verifiedOwner }
16674
- };
16853
+ }, ownerKeyPrimary);
16675
16854
  await commitLogin(profileName, snapshot, {
16676
16855
  key,
16677
16856
  session: sanitizedSession,
16678
16857
  profile: updatedProfile,
16679
16858
  ...permissions && verifiedOwner ? { approved: { scope: sanitizedSession.permissions, ownerDid: verifiedOwner, replaceSession: options.replaceSession === true } } : {}
16680
16859
  });
16860
+ warnIfNotPrimaryKey(verifiedOwner, ownerKeyPrimary);
16681
16861
  return { profile: updatedProfile, delegationData: sanitizedSession, declined, legacyNested };
16682
16862
  }
16683
16863