@tinycloud/cli 1.1.0-beta.18 → 1.1.0-beta.20
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.
- package/dist/index.js.map +1 -1
- package/dist/legacy-entry.js +794 -198
- package/dist/legacy-entry.js.map +1 -1
- package/package.json +4 -4
package/dist/legacy-entry.js
CHANGED
|
@@ -591,8 +591,8 @@ function canonicalizeAddress(address) {
|
|
|
591
591
|
const trimmed = address.trim();
|
|
592
592
|
return trimmed.startsWith("0x") ? `0x${trimmed.slice(2).toLowerCase()}` : trimmed.toLowerCase();
|
|
593
593
|
}
|
|
594
|
-
function normalizePkhIdentifier(
|
|
595
|
-
return
|
|
594
|
+
function normalizePkhIdentifier(identifier2) {
|
|
595
|
+
return identifier2.replace(
|
|
596
596
|
/(eip155:\d+:)(0x[0-9a-fA-F]{40})(?=[:#]|$)/,
|
|
597
597
|
(_match, prefix, address) => prefix + address.toLowerCase()
|
|
598
598
|
);
|
|
@@ -10000,7 +10000,7 @@ var AsyncQueue = class {
|
|
|
10000
10000
|
}
|
|
10001
10001
|
const waiter = this.waiters.shift();
|
|
10002
10002
|
if (waiter) {
|
|
10003
|
-
waiter({ value, done: false });
|
|
10003
|
+
waiter.resolve({ value, done: false });
|
|
10004
10004
|
return;
|
|
10005
10005
|
}
|
|
10006
10006
|
this.values.push(value);
|
|
@@ -10012,7 +10012,22 @@ var AsyncQueue = class {
|
|
|
10012
10012
|
this.closed = true;
|
|
10013
10013
|
while (this.waiters.length > 0) {
|
|
10014
10014
|
const waiter = this.waiters.shift();
|
|
10015
|
-
waiter?.({ value: void 0, done: true });
|
|
10015
|
+
waiter?.resolve({ value: void 0, done: true });
|
|
10016
|
+
}
|
|
10017
|
+
}
|
|
10018
|
+
/**
|
|
10019
|
+
* Fail the queue: pending and future `next()` calls reject with `error`,
|
|
10020
|
+
* so a `for await` consumer sees the iterator throw.
|
|
10021
|
+
*/
|
|
10022
|
+
fail(error) {
|
|
10023
|
+
if (this.closed) {
|
|
10024
|
+
return;
|
|
10025
|
+
}
|
|
10026
|
+
this.closed = true;
|
|
10027
|
+
this.failure = error;
|
|
10028
|
+
while (this.waiters.length > 0) {
|
|
10029
|
+
const waiter = this.waiters.shift();
|
|
10030
|
+
waiter?.reject(error);
|
|
10016
10031
|
}
|
|
10017
10032
|
}
|
|
10018
10033
|
next() {
|
|
@@ -10020,20 +10035,26 @@ var AsyncQueue = class {
|
|
|
10020
10035
|
const value = this.values.shift();
|
|
10021
10036
|
return Promise.resolve({ value, done: false });
|
|
10022
10037
|
}
|
|
10038
|
+
if (this.failure !== void 0) {
|
|
10039
|
+
return Promise.reject(this.failure);
|
|
10040
|
+
}
|
|
10023
10041
|
if (this.closed) {
|
|
10024
10042
|
return Promise.resolve({ value: void 0, done: true });
|
|
10025
10043
|
}
|
|
10026
|
-
return new Promise((resolve5) => {
|
|
10027
|
-
this.waiters.push(resolve5);
|
|
10044
|
+
return new Promise((resolve5, reject) => {
|
|
10045
|
+
this.waiters.push({ resolve: resolve5, reject });
|
|
10028
10046
|
});
|
|
10029
10047
|
}
|
|
10030
10048
|
};
|
|
10049
|
+
var HOOK_STREAM_RETRY_BASE_DELAY_MS = 250;
|
|
10050
|
+
var HOOK_STREAM_RETRY_MAX_DELAY_MS = 3e4;
|
|
10051
|
+
var HOOK_STREAM_HEALTHY_MS = 5e3;
|
|
10031
10052
|
var HooksService = class extends BaseService {
|
|
10032
10053
|
constructor(config = {}) {
|
|
10033
10054
|
super();
|
|
10034
10055
|
this._subscribers = /* @__PURE__ */ new Set();
|
|
10035
|
-
this.
|
|
10036
|
-
this.
|
|
10056
|
+
this._supervising = false;
|
|
10057
|
+
this._sessionGeneration = 0;
|
|
10037
10058
|
this._config = config;
|
|
10038
10059
|
}
|
|
10039
10060
|
get config() {
|
|
@@ -10049,35 +10070,32 @@ var HooksService = class extends BaseService {
|
|
|
10049
10070
|
if (subscriptions.length === 0) {
|
|
10050
10071
|
throw new Error("At least one hook subscription is required");
|
|
10051
10072
|
}
|
|
10052
|
-
const
|
|
10073
|
+
const requested = subscriptions.map(normalizeSubscription);
|
|
10053
10074
|
const subscriber = {
|
|
10054
|
-
requested
|
|
10075
|
+
requested,
|
|
10076
|
+
signatures: requested.map(subscriptionSignature),
|
|
10055
10077
|
ttlSeconds: options.ttlSeconds,
|
|
10056
10078
|
queue: new AsyncQueue()
|
|
10057
10079
|
};
|
|
10058
|
-
|
|
10059
|
-
const abortHandler = () => {
|
|
10080
|
+
const leave = () => {
|
|
10060
10081
|
this._subscribers.delete(subscriber);
|
|
10061
10082
|
subscriber.queue.close();
|
|
10062
|
-
|
|
10083
|
+
this.wake();
|
|
10063
10084
|
};
|
|
10064
|
-
|
|
10065
|
-
|
|
10066
|
-
|
|
10067
|
-
} else {
|
|
10068
|
-
options.signal.addEventListener("abort", abortHandler, { once: true });
|
|
10069
|
-
}
|
|
10085
|
+
const { signal } = options;
|
|
10086
|
+
if (signal?.aborted) {
|
|
10087
|
+
return;
|
|
10070
10088
|
}
|
|
10071
|
-
|
|
10089
|
+
signal?.addEventListener("abort", leave, { once: true });
|
|
10090
|
+
this._subscribers.add(subscriber);
|
|
10091
|
+
this.wake();
|
|
10072
10092
|
try {
|
|
10073
10093
|
for await (const event of subscriber.queue) {
|
|
10074
10094
|
yield event;
|
|
10075
10095
|
}
|
|
10076
10096
|
} finally {
|
|
10077
|
-
|
|
10078
|
-
|
|
10079
|
-
}
|
|
10080
|
-
abortHandler();
|
|
10097
|
+
signal?.removeEventListener("abort", leave);
|
|
10098
|
+
leave();
|
|
10081
10099
|
}
|
|
10082
10100
|
}
|
|
10083
10101
|
async register(webhook) {
|
|
@@ -10218,146 +10236,292 @@ var HooksService = class extends BaseService {
|
|
|
10218
10236
|
return err(wrapError2("hooks", error));
|
|
10219
10237
|
}
|
|
10220
10238
|
}
|
|
10221
|
-
|
|
10222
|
-
|
|
10223
|
-
|
|
10224
|
-
|
|
10225
|
-
|
|
10226
|
-
|
|
10227
|
-
|
|
10228
|
-
|
|
10229
|
-
|
|
10230
|
-
|
|
10231
|
-
|
|
10232
|
-
if (state.signature !== this._activeSignature) {
|
|
10233
|
-
this._activeSignature = state.signature;
|
|
10234
|
-
this.abortSharedStream();
|
|
10239
|
+
/**
|
|
10240
|
+
* Called after anything that may change what the supervisor should do:
|
|
10241
|
+
* subscribe, unsubscribe, session change, sign-out. Interrupts the running
|
|
10242
|
+
* step if the plan moved away from it, and starts the supervisor if none
|
|
10243
|
+
* runs. Touches only SDK-owned state, so it never throws into callers such
|
|
10244
|
+
* as `ServiceContext.setSession`.
|
|
10245
|
+
*/
|
|
10246
|
+
wake() {
|
|
10247
|
+
const phase = this._phase;
|
|
10248
|
+
if (phase && phase.key !== this.plan()?.key) {
|
|
10249
|
+
phase.interrupt.abort();
|
|
10235
10250
|
}
|
|
10236
|
-
if (
|
|
10237
|
-
|
|
10238
|
-
if (!isAbortError(error)) {
|
|
10239
|
-
throw error;
|
|
10240
|
-
}
|
|
10241
|
-
}).finally(() => {
|
|
10242
|
-
this._sharedStreamTask = void 0;
|
|
10243
|
-
this._sharedStreamAbort = void 0;
|
|
10244
|
-
if (this._subscribers.size > 0) {
|
|
10245
|
-
void this.scheduleSharedStreamRefresh();
|
|
10246
|
-
}
|
|
10247
|
-
});
|
|
10251
|
+
if (this._supervising || this._subscribers.size === 0) {
|
|
10252
|
+
return;
|
|
10248
10253
|
}
|
|
10254
|
+
this._supervising = true;
|
|
10255
|
+
this.supervise().catch(() => this.collapse());
|
|
10249
10256
|
}
|
|
10250
|
-
|
|
10251
|
-
|
|
10252
|
-
|
|
10253
|
-
|
|
10254
|
-
|
|
10255
|
-
|
|
10257
|
+
/**
|
|
10258
|
+
* The only owner of the shared stream. Each iteration is one attempt
|
|
10259
|
+
* (mint → open → drain), then a classification and, unless the attempt was
|
|
10260
|
+
* superseded or released its subscribers, a backoff. Every await is a
|
|
10261
|
+
* `phase` that wake() can interrupt; an interrupted phase yields
|
|
10262
|
+
* `undefined`, so a superseded mint can never act. Outside `attempt` and
|
|
10263
|
+
* `pause`, which each own one catch, the loop touches only SDK state.
|
|
10264
|
+
*/
|
|
10265
|
+
async supervise() {
|
|
10266
|
+
let streak = 0;
|
|
10267
|
+
for (; ; ) {
|
|
10268
|
+
const plan = this.plan();
|
|
10269
|
+
if (!plan) {
|
|
10270
|
+
this._supervising = false;
|
|
10271
|
+
return;
|
|
10272
|
+
}
|
|
10273
|
+
const health = { openedAt: 0, delivered: false };
|
|
10274
|
+
const outcome = await this.phase(
|
|
10275
|
+
plan.key,
|
|
10276
|
+
(signal) => this.attempt(plan, signal, health)
|
|
10277
|
+
);
|
|
10278
|
+
if (health.delivered || health.openedAt > 0 && performance.now() - health.openedAt >= HOOK_STREAM_HEALTHY_MS) {
|
|
10279
|
+
streak = 0;
|
|
10280
|
+
}
|
|
10281
|
+
if (!outcome) {
|
|
10282
|
+
continue;
|
|
10283
|
+
}
|
|
10284
|
+
if (this.plan()?.key !== plan.key) {
|
|
10285
|
+
continue;
|
|
10286
|
+
}
|
|
10287
|
+
if (outcome.kind === "stopped") {
|
|
10288
|
+
this.release();
|
|
10289
|
+
continue;
|
|
10256
10290
|
}
|
|
10257
|
-
|
|
10258
|
-
|
|
10291
|
+
if (outcome.kind === "refused" && outcome.terminal) {
|
|
10292
|
+
this.release(outcome.error);
|
|
10293
|
+
this.report(outcome.error);
|
|
10294
|
+
continue;
|
|
10295
|
+
}
|
|
10296
|
+
if (outcome.kind !== "ended") {
|
|
10297
|
+
this.report(outcome.error);
|
|
10259
10298
|
}
|
|
10299
|
+
streak += 1;
|
|
10300
|
+
await this.phase(
|
|
10301
|
+
plan.key,
|
|
10302
|
+
(signal) => this.pause(streak, signal)
|
|
10303
|
+
);
|
|
10304
|
+
}
|
|
10305
|
+
}
|
|
10306
|
+
/**
|
|
10307
|
+
* Run one step under an interrupt that wake() fires when the plan key moves
|
|
10308
|
+
* on. An interrupted step's result is discarded. The interrupt is also
|
|
10309
|
+
* fired when the step returns, which releases whatever it left open: the
|
|
10310
|
+
* request signal and its listeners, an unread response body, a timer.
|
|
10311
|
+
*/
|
|
10312
|
+
async phase(key, step) {
|
|
10313
|
+
const interrupt = new AbortController();
|
|
10314
|
+
this._phase = { key, interrupt };
|
|
10315
|
+
try {
|
|
10316
|
+
if (this.plan()?.key !== key) {
|
|
10317
|
+
interrupt.abort();
|
|
10318
|
+
}
|
|
10319
|
+
const result = await step(interrupt.signal);
|
|
10320
|
+
return interrupt.signal.aborted ? void 0 : result;
|
|
10321
|
+
} finally {
|
|
10322
|
+
this._phase = void 0;
|
|
10323
|
+
interrupt.abort();
|
|
10260
10324
|
}
|
|
10261
|
-
const subscriptions = [...merged.values()].sort(
|
|
10262
|
-
(left, right) => subscriptionSignature(left).localeCompare(subscriptionSignature(right))
|
|
10263
|
-
);
|
|
10264
|
-
const ttlSeconds = ttlCandidates.length > 0 ? Math.min(...ttlCandidates) : void 0;
|
|
10265
|
-
const signature = JSON.stringify({
|
|
10266
|
-
subscriptions: subscriptions.map(subscriptionSignature),
|
|
10267
|
-
ttlSeconds
|
|
10268
|
-
});
|
|
10269
|
-
return {
|
|
10270
|
-
subscriptions,
|
|
10271
|
-
ttlSeconds,
|
|
10272
|
-
signature
|
|
10273
|
-
};
|
|
10274
10325
|
}
|
|
10275
|
-
|
|
10276
|
-
|
|
10277
|
-
|
|
10326
|
+
/**
|
|
10327
|
+
* One mint → open → drain. The single catch site for everything the
|
|
10328
|
+
* attempt touches that the SDK does not own: context accessors, invoke,
|
|
10329
|
+
* fetch, response bodies, the SSE parser. Never rejects.
|
|
10330
|
+
*/
|
|
10331
|
+
async attempt(plan, signal, health) {
|
|
10332
|
+
let phase = "mint";
|
|
10333
|
+
let request;
|
|
10334
|
+
const owns = () => !signal.aborted && !request?.signal.aborted && this.plan()?.key === plan.key;
|
|
10278
10335
|
try {
|
|
10279
|
-
const
|
|
10280
|
-
const
|
|
10281
|
-
|
|
10282
|
-
|
|
10283
|
-
|
|
10336
|
+
const context = this.context;
|
|
10337
|
+
const contextSignal = context.abortSignal;
|
|
10338
|
+
this._lastContextSignal = contextSignal;
|
|
10339
|
+
if (!context.isAuthenticated || contextSignal?.aborted) {
|
|
10340
|
+
return { kind: "stopped" };
|
|
10341
|
+
}
|
|
10342
|
+
request = this.createRequestSignal(signal, 0);
|
|
10343
|
+
const host = this.host;
|
|
10344
|
+
const minted = await awaitOwned(
|
|
10345
|
+
request.signal,
|
|
10346
|
+
() => context.fetch(`${host}/hooks/tickets`, {
|
|
10347
|
+
method: "POST",
|
|
10348
|
+
headers: {
|
|
10349
|
+
...serviceHeadersToRecord(this.createInvokeHeaders(plan.subscriptions)),
|
|
10350
|
+
"content-type": "application/json"
|
|
10351
|
+
},
|
|
10352
|
+
body: JSON.stringify({
|
|
10353
|
+
subscriptions: plan.subscriptions,
|
|
10354
|
+
ttlSeconds: plan.ttlSeconds
|
|
10355
|
+
}),
|
|
10356
|
+
signal: request.signal
|
|
10357
|
+
})
|
|
10284
10358
|
);
|
|
10285
|
-
|
|
10286
|
-
|
|
10287
|
-
|
|
10288
|
-
|
|
10359
|
+
if (!owns()) return { kind: "stopped" };
|
|
10360
|
+
if (!minted.ok) {
|
|
10361
|
+
const status = minted.status;
|
|
10362
|
+
const error = await awaitOwned(
|
|
10363
|
+
request.signal,
|
|
10364
|
+
() => responseError("hooks", "failed to mint hook ticket", minted)
|
|
10365
|
+
);
|
|
10366
|
+
if (!owns()) return { kind: "stopped" };
|
|
10367
|
+
return { kind: "refused", terminal: isTerminalMintRefusal(status), error };
|
|
10368
|
+
}
|
|
10369
|
+
const body = await awaitOwned(request.signal, () => minted.json());
|
|
10370
|
+
if (!owns()) return { kind: "stopped" };
|
|
10371
|
+
const ticket = body !== null && typeof body === "object" && "ticket" in body ? body.ticket : void 0;
|
|
10372
|
+
if (typeof ticket !== "string" || ticket.length === 0) {
|
|
10373
|
+
throw new Error("Hook ticket response did not include a ticket");
|
|
10374
|
+
}
|
|
10375
|
+
phase = "open";
|
|
10376
|
+
if (!owns()) return { kind: "stopped" };
|
|
10377
|
+
const stream = await awaitOwned(
|
|
10378
|
+
request.signal,
|
|
10379
|
+
() => context.fetch(`${host}/hooks/events?ticket=${encodeURIComponent(ticket)}`, {
|
|
10380
|
+
method: "GET",
|
|
10381
|
+
headers: { accept: "text/event-stream" },
|
|
10382
|
+
signal: request.signal
|
|
10383
|
+
})
|
|
10289
10384
|
);
|
|
10290
|
-
|
|
10291
|
-
|
|
10292
|
-
|
|
10293
|
-
|
|
10294
|
-
|
|
10295
|
-
|
|
10296
|
-
|
|
10297
|
-
|
|
10298
|
-
|
|
10299
|
-
if (
|
|
10300
|
-
|
|
10301
|
-
|
|
10385
|
+
if (!owns()) return { kind: "stopped" };
|
|
10386
|
+
if (!stream.ok) {
|
|
10387
|
+
return { kind: "refused", terminal: false, error: hookStreamRefusal(stream.status) };
|
|
10388
|
+
}
|
|
10389
|
+
health.openedAt = performance.now();
|
|
10390
|
+
const iterator = parseSseStream(stream.body, request.signal)[Symbol.asyncIterator]();
|
|
10391
|
+
try {
|
|
10392
|
+
for (; ; ) {
|
|
10393
|
+
const next = await awaitOwned(request.signal, () => iterator.next());
|
|
10394
|
+
if (!owns()) return { kind: "stopped" };
|
|
10395
|
+
if (next.done) break;
|
|
10396
|
+
const message = next.value;
|
|
10397
|
+
if (!message.data) continue;
|
|
10398
|
+
const event = parseHookEvent(message);
|
|
10399
|
+
if (!owns()) return { kind: "stopped" };
|
|
10400
|
+
this.dispatch(event);
|
|
10401
|
+
health.delivered = true;
|
|
10302
10402
|
}
|
|
10403
|
+
} finally {
|
|
10404
|
+
const closing = iterator.return?.();
|
|
10405
|
+
if (closing) void Promise.resolve(closing).then(() => void 0, () => void 0);
|
|
10303
10406
|
}
|
|
10407
|
+
return owns() ? { kind: "ended" } : { kind: "stopped" };
|
|
10408
|
+
} catch (thrown) {
|
|
10409
|
+
return request?.signal.aborted || thrown === SUPERSEDED ? { kind: "stopped" } : { kind: "failed", error: hookStreamFailure(phase, thrown) };
|
|
10410
|
+
}
|
|
10411
|
+
}
|
|
10412
|
+
/**
|
|
10413
|
+
* Back off before the next attempt. `config.streamRetry` overrides the
|
|
10414
|
+
* delay and the clock; whatever they do, the pause ends by the delay or the
|
|
10415
|
+
* interrupt, then yields one timer turn so an instant clock cannot starve
|
|
10416
|
+
* due timers or the process.
|
|
10417
|
+
*/
|
|
10418
|
+
async pause(attempt, signal) {
|
|
10419
|
+
try {
|
|
10420
|
+
this._lastContextSignal = this.context.abortSignal;
|
|
10421
|
+
} catch {
|
|
10422
|
+
}
|
|
10423
|
+
const contextSignal = this._lastContextSignal;
|
|
10424
|
+
const waits = [];
|
|
10425
|
+
const aborts = [];
|
|
10426
|
+
try {
|
|
10427
|
+
const retry = this._config.streamRetry;
|
|
10428
|
+
const delayMs = retry?.delay?.(attempt) ?? hookStreamRetryDelay(attempt);
|
|
10429
|
+
waits.push((retry?.wait ?? sleep)(delayMs, attempt, signal));
|
|
10430
|
+
} catch {
|
|
10431
|
+
waits.push(Promise.resolve());
|
|
10432
|
+
}
|
|
10433
|
+
aborts.push(abortPromise(signal));
|
|
10434
|
+
if (contextSignal) aborts.push(abortPromise(contextSignal));
|
|
10435
|
+
try {
|
|
10436
|
+
await Promise.race([...waits, ...aborts.map(({ promise }) => promise)]);
|
|
10437
|
+
} catch {
|
|
10304
10438
|
} finally {
|
|
10305
|
-
|
|
10306
|
-
|
|
10439
|
+
for (const abort of aborts) abort.dispose();
|
|
10440
|
+
}
|
|
10441
|
+
await new Promise((resolve5) => setTimeout(resolve5, 0));
|
|
10442
|
+
}
|
|
10443
|
+
/** The stream the current subscribers need, or undefined when none remain. */
|
|
10444
|
+
plan() {
|
|
10445
|
+
if (this._subscribers.size === 0) {
|
|
10446
|
+
return void 0;
|
|
10447
|
+
}
|
|
10448
|
+
const merged = /* @__PURE__ */ new Map();
|
|
10449
|
+
let ttlSeconds;
|
|
10450
|
+
for (const subscriber of this._subscribers) {
|
|
10451
|
+
subscriber.requested.forEach(
|
|
10452
|
+
(subscription, index) => merged.set(subscriber.signatures[index], subscription)
|
|
10453
|
+
);
|
|
10454
|
+
if (typeof subscriber.ttlSeconds === "number") {
|
|
10455
|
+
ttlSeconds = Math.min(subscriber.ttlSeconds, ttlSeconds ?? Infinity);
|
|
10307
10456
|
}
|
|
10308
10457
|
}
|
|
10458
|
+
const signatures = [...merged.keys()].sort();
|
|
10459
|
+
return {
|
|
10460
|
+
key: JSON.stringify([this._sessionGeneration, signatures, ttlSeconds ?? null]),
|
|
10461
|
+
subscriptions: signatures.map((signature) => merged.get(signature)),
|
|
10462
|
+
ttlSeconds
|
|
10463
|
+
};
|
|
10309
10464
|
}
|
|
10310
|
-
|
|
10311
|
-
this.
|
|
10465
|
+
dispatch(event) {
|
|
10466
|
+
for (const subscriber of this._subscribers) {
|
|
10467
|
+
if (matchesAnySubscription(event, subscriber.requested)) {
|
|
10468
|
+
subscriber.queue.push(event);
|
|
10469
|
+
}
|
|
10470
|
+
}
|
|
10312
10471
|
}
|
|
10313
|
-
|
|
10314
|
-
|
|
10472
|
+
/**
|
|
10473
|
+
* Detach every subscriber. With `error`, their iterators throw it after
|
|
10474
|
+
* draining buffered events; without, they complete.
|
|
10475
|
+
*/
|
|
10476
|
+
release(error) {
|
|
10477
|
+
const subscribers = [...this._subscribers];
|
|
10478
|
+
this._subscribers.clear();
|
|
10479
|
+
for (const { queue } of subscribers) {
|
|
10480
|
+
if (error) {
|
|
10481
|
+
queue.fail(error);
|
|
10482
|
+
} else {
|
|
10483
|
+
queue.close();
|
|
10484
|
+
}
|
|
10485
|
+
}
|
|
10315
10486
|
}
|
|
10316
|
-
|
|
10317
|
-
|
|
10318
|
-
|
|
10319
|
-
const ticketResponse = await this.context.fetch(`${host}/hooks/tickets`, {
|
|
10320
|
-
method: "POST",
|
|
10321
|
-
headers: {
|
|
10322
|
-
...serviceHeadersToRecord(headers),
|
|
10323
|
-
"content-type": "application/json"
|
|
10324
|
-
},
|
|
10325
|
-
body: JSON.stringify({
|
|
10326
|
-
subscriptions,
|
|
10327
|
-
ttlSeconds
|
|
10328
|
-
}),
|
|
10329
|
-
signal
|
|
10330
|
-
});
|
|
10331
|
-
if (!ticketResponse.ok) {
|
|
10332
|
-
throw await responseError(
|
|
10333
|
-
"hooks",
|
|
10334
|
-
"failed to mint hook ticket",
|
|
10335
|
-
ticketResponse
|
|
10336
|
-
);
|
|
10487
|
+
report(error) {
|
|
10488
|
+
if (error.meta && typeof error.meta === "object") {
|
|
10489
|
+
Object.freeze(error.meta);
|
|
10337
10490
|
}
|
|
10338
|
-
|
|
10339
|
-
|
|
10340
|
-
|
|
10491
|
+
Object.freeze(error);
|
|
10492
|
+
try {
|
|
10493
|
+
this.emitError(error, "stream");
|
|
10494
|
+
} catch {
|
|
10341
10495
|
}
|
|
10342
|
-
return ticketJson;
|
|
10343
10496
|
}
|
|
10344
|
-
|
|
10345
|
-
|
|
10346
|
-
|
|
10347
|
-
|
|
10348
|
-
|
|
10349
|
-
|
|
10350
|
-
|
|
10351
|
-
|
|
10497
|
+
/**
|
|
10498
|
+
* Unreachable by construction. If the loop ever rejects anyway, fail its
|
|
10499
|
+
* subscribers visibly instead of stranding them or crashing the process.
|
|
10500
|
+
*/
|
|
10501
|
+
collapse() {
|
|
10502
|
+
this._supervising = false;
|
|
10503
|
+
this._phase?.interrupt.abort();
|
|
10504
|
+
this._phase = void 0;
|
|
10505
|
+
const error = serviceError(
|
|
10506
|
+
ErrorCodes.NETWORK_ERROR,
|
|
10507
|
+
"hook stream supervisor stopped unexpectedly",
|
|
10508
|
+
"hooks"
|
|
10352
10509
|
);
|
|
10353
|
-
|
|
10354
|
-
|
|
10355
|
-
|
|
10356
|
-
|
|
10357
|
-
|
|
10358
|
-
|
|
10359
|
-
|
|
10360
|
-
|
|
10510
|
+
this.report(error);
|
|
10511
|
+
this.release(error);
|
|
10512
|
+
}
|
|
10513
|
+
onSessionChange(session) {
|
|
10514
|
+
super.onSessionChange(session);
|
|
10515
|
+
this._sessionGeneration += 1;
|
|
10516
|
+
this.wake();
|
|
10517
|
+
}
|
|
10518
|
+
onSignOut() {
|
|
10519
|
+
super.onSignOut();
|
|
10520
|
+
this.release();
|
|
10521
|
+
this.wake();
|
|
10522
|
+
}
|
|
10523
|
+
createHookHeaders(action, path) {
|
|
10524
|
+
return this.context.invoke(this.session, "hooks", path, action);
|
|
10361
10525
|
}
|
|
10362
10526
|
createInvokeHeaders(subscriptions) {
|
|
10363
10527
|
const entries = subscriptions.map((subscription) => ({
|
|
@@ -10388,11 +10552,168 @@ function buildScopePath(service, pathPrefix) {
|
|
|
10388
10552
|
const normalized = normalizePathPrefix(pathPrefix);
|
|
10389
10553
|
return normalized ? `${service}/${normalized}` : service;
|
|
10390
10554
|
}
|
|
10555
|
+
function hookStreamRetryDelay(attempt) {
|
|
10556
|
+
const cap = Math.min(
|
|
10557
|
+
HOOK_STREAM_RETRY_BASE_DELAY_MS * 2 ** Math.max(0, attempt - 1),
|
|
10558
|
+
HOOK_STREAM_RETRY_MAX_DELAY_MS
|
|
10559
|
+
);
|
|
10560
|
+
return cap / 2 + Math.random() * (cap / 2);
|
|
10561
|
+
}
|
|
10562
|
+
function isTerminalMintRefusal(status) {
|
|
10563
|
+
return typeof status === "number" && status >= 400 && status < 500 && status !== 408 && status !== 429;
|
|
10564
|
+
}
|
|
10565
|
+
var SUPERSEDED = /* @__PURE__ */ Symbol("hooks attempt superseded");
|
|
10566
|
+
async function awaitOwned(signal, operation) {
|
|
10567
|
+
if (signal.aborted) {
|
|
10568
|
+
if (typeof operation !== "function") {
|
|
10569
|
+
void operation.then(cancelLateResponse, () => void 0);
|
|
10570
|
+
}
|
|
10571
|
+
throw SUPERSEDED;
|
|
10572
|
+
}
|
|
10573
|
+
const promise = typeof operation === "function" ? operation() : operation;
|
|
10574
|
+
return new Promise((resolve5, reject) => {
|
|
10575
|
+
let settled = false;
|
|
10576
|
+
const cleanup = () => signal.removeEventListener("abort", onAbort);
|
|
10577
|
+
const onAbort = () => {
|
|
10578
|
+
if (settled) return;
|
|
10579
|
+
settled = true;
|
|
10580
|
+
cleanup();
|
|
10581
|
+
reject(SUPERSEDED);
|
|
10582
|
+
};
|
|
10583
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
10584
|
+
promise.then(
|
|
10585
|
+
(value) => {
|
|
10586
|
+
if (settled) {
|
|
10587
|
+
cancelLateResponse(value);
|
|
10588
|
+
return;
|
|
10589
|
+
}
|
|
10590
|
+
settled = true;
|
|
10591
|
+
cleanup();
|
|
10592
|
+
resolve5(value);
|
|
10593
|
+
},
|
|
10594
|
+
(error) => {
|
|
10595
|
+
if (settled) return;
|
|
10596
|
+
settled = true;
|
|
10597
|
+
cleanup();
|
|
10598
|
+
reject(error);
|
|
10599
|
+
}
|
|
10600
|
+
);
|
|
10601
|
+
});
|
|
10602
|
+
}
|
|
10603
|
+
function cancelLateResponse(value) {
|
|
10604
|
+
if (!(value instanceof Response) || !value.body) return;
|
|
10605
|
+
try {
|
|
10606
|
+
const cancellation = value.body.cancel();
|
|
10607
|
+
void cancellation.then(() => void 0, () => void 0);
|
|
10608
|
+
} catch {
|
|
10609
|
+
}
|
|
10610
|
+
}
|
|
10611
|
+
function hookStreamRefusal(status) {
|
|
10612
|
+
const code3 = typeof status === "number" ? status : void 0;
|
|
10613
|
+
return serviceError(
|
|
10614
|
+
ErrorCodes.NETWORK_ERROR,
|
|
10615
|
+
`hook stream open refused: ${code3 ?? "unknown status"}`,
|
|
10616
|
+
"hooks",
|
|
10617
|
+
{ meta: { phase: "open", status: code3 } }
|
|
10618
|
+
);
|
|
10619
|
+
}
|
|
10620
|
+
var STREAM_ERROR_NAMES = /* @__PURE__ */ new Set([
|
|
10621
|
+
"Error",
|
|
10622
|
+
"TypeError",
|
|
10623
|
+
"AbortError",
|
|
10624
|
+
"TimeoutError",
|
|
10625
|
+
"SyntaxError",
|
|
10626
|
+
"RangeError",
|
|
10627
|
+
"NetworkError"
|
|
10628
|
+
]);
|
|
10629
|
+
function hookStreamFailure(phase, thrown) {
|
|
10630
|
+
const candidateName = readProperty(thrown, "name");
|
|
10631
|
+
const name = typeof candidateName === "string" && STREAM_ERROR_NAMES.has(candidateName) ? candidateName : "Error";
|
|
10632
|
+
const code3 = identifier(readProperty(thrown, "code")) ?? identifier(readProperty(readProperty(thrown, "cause"), "code"));
|
|
10633
|
+
return serviceError(
|
|
10634
|
+
ErrorCodes.NETWORK_ERROR,
|
|
10635
|
+
`hook stream ${phase} failed: ${name}${code3 ? ` (${code3})` : ""}`,
|
|
10636
|
+
"hooks",
|
|
10637
|
+
{ meta: { phase, errorName: name, ...code3 ? { errorCode: code3 } : {} } }
|
|
10638
|
+
);
|
|
10639
|
+
}
|
|
10640
|
+
var STREAM_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
10641
|
+
"ECONNREFUSED",
|
|
10642
|
+
"ECONNRESET",
|
|
10643
|
+
"ECONNABORTED",
|
|
10644
|
+
"ETIMEDOUT",
|
|
10645
|
+
"EPIPE",
|
|
10646
|
+
"ENOTFOUND",
|
|
10647
|
+
"EAI_AGAIN",
|
|
10648
|
+
"EHOSTUNREACH",
|
|
10649
|
+
"ENETUNREACH",
|
|
10650
|
+
"ENETDOWN",
|
|
10651
|
+
"EADDRNOTAVAIL",
|
|
10652
|
+
"UND_ERR_SOCKET",
|
|
10653
|
+
"UND_ERR_CONNECT_TIMEOUT",
|
|
10654
|
+
"UND_ERR_HEADERS_TIMEOUT",
|
|
10655
|
+
"UND_ERR_BODY_TIMEOUT",
|
|
10656
|
+
"UND_ERR_CLOSED",
|
|
10657
|
+
"UND_ERR_ABORTED",
|
|
10658
|
+
"UND_ERR_RESPONSE_STATUS_CODE",
|
|
10659
|
+
"ERR_TLS_CERT_ALTNAME_INVALID",
|
|
10660
|
+
"CERT_HAS_EXPIRED",
|
|
10661
|
+
"DEPTH_ZERO_SELF_SIGNED_CERT",
|
|
10662
|
+
"UNABLE_TO_VERIFY_LEAF_SIGNATURE",
|
|
10663
|
+
"SELF_SIGNED_CERT_IN_CHAIN",
|
|
10664
|
+
"ERR_SSL_WRONG_VERSION_NUMBER"
|
|
10665
|
+
]);
|
|
10666
|
+
function identifier(value) {
|
|
10667
|
+
return typeof value === "string" && STREAM_ERROR_CODES.has(value) ? value : void 0;
|
|
10668
|
+
}
|
|
10669
|
+
function readProperty(value, key) {
|
|
10670
|
+
if (value === null || typeof value !== "object" && typeof value !== "function") {
|
|
10671
|
+
return void 0;
|
|
10672
|
+
}
|
|
10673
|
+
try {
|
|
10674
|
+
return Reflect.get(value, key);
|
|
10675
|
+
} catch {
|
|
10676
|
+
return void 0;
|
|
10677
|
+
}
|
|
10678
|
+
}
|
|
10679
|
+
function sleep(delayMs, _attempt, signal) {
|
|
10680
|
+
return new Promise((resolve5) => {
|
|
10681
|
+
const finish = () => {
|
|
10682
|
+
clearTimeout(timer);
|
|
10683
|
+
signal.removeEventListener("abort", stop);
|
|
10684
|
+
resolve5();
|
|
10685
|
+
};
|
|
10686
|
+
const timer = setTimeout(finish, delayMs);
|
|
10687
|
+
timer.unref?.();
|
|
10688
|
+
const stop = () => finish();
|
|
10689
|
+
if (signal.aborted) {
|
|
10690
|
+
finish();
|
|
10691
|
+
} else {
|
|
10692
|
+
signal.addEventListener("abort", stop, { once: true });
|
|
10693
|
+
}
|
|
10694
|
+
});
|
|
10695
|
+
}
|
|
10696
|
+
function abortPromise(signal) {
|
|
10697
|
+
let resolve5;
|
|
10698
|
+
const promise = new Promise((done) => {
|
|
10699
|
+
resolve5 = done;
|
|
10700
|
+
});
|
|
10701
|
+
const onAbort = () => resolve5();
|
|
10702
|
+
if (signal.aborted) {
|
|
10703
|
+
resolve5();
|
|
10704
|
+
} else {
|
|
10705
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
10706
|
+
}
|
|
10707
|
+
return {
|
|
10708
|
+
promise,
|
|
10709
|
+
dispose: () => signal.removeEventListener("abort", onAbort)
|
|
10710
|
+
};
|
|
10711
|
+
}
|
|
10391
10712
|
function normalizeSubscription(subscription) {
|
|
10392
10713
|
return {
|
|
10393
10714
|
...subscription,
|
|
10394
10715
|
pathPrefix: normalizePathPrefix(subscription.pathPrefix),
|
|
10395
|
-
abilities: subscription.abilities
|
|
10716
|
+
abilities: subscription.abilities ? [...subscription.abilities] : []
|
|
10396
10717
|
};
|
|
10397
10718
|
}
|
|
10398
10719
|
function subscriptionSignature(subscription) {
|
|
@@ -10458,9 +10779,6 @@ async function responseError(service, message, response) {
|
|
|
10458
10779
|
meta: { ...error.meta, status: response.status, statusText: response.statusText }
|
|
10459
10780
|
};
|
|
10460
10781
|
}
|
|
10461
|
-
function isAbortError(error) {
|
|
10462
|
-
return error instanceof DOMException && error.name === "AbortError";
|
|
10463
|
-
}
|
|
10464
10782
|
async function* parseSseStream(body, signal) {
|
|
10465
10783
|
if (!body) {
|
|
10466
10784
|
throw new Error("Hook stream response does not expose a readable body");
|
|
@@ -10489,11 +10807,24 @@ async function* parseSseStream(body, signal) {
|
|
|
10489
10807
|
async function* readBodyChunks(body, signal) {
|
|
10490
10808
|
const asyncIterable = body;
|
|
10491
10809
|
if (typeof asyncIterable?.[Symbol.asyncIterator] === "function") {
|
|
10492
|
-
|
|
10493
|
-
|
|
10494
|
-
|
|
10810
|
+
const iterator = asyncIterable[Symbol.asyncIterator]();
|
|
10811
|
+
const cancel = () => {
|
|
10812
|
+
try {
|
|
10813
|
+
const closing = iterator.return?.();
|
|
10814
|
+
if (closing) void Promise.resolve(closing).then(() => void 0, () => void 0);
|
|
10815
|
+
} catch {
|
|
10495
10816
|
}
|
|
10496
|
-
|
|
10817
|
+
};
|
|
10818
|
+
signal?.addEventListener("abort", cancel, { once: true });
|
|
10819
|
+
try {
|
|
10820
|
+
while (!signal?.aborted) {
|
|
10821
|
+
const next = await iterator.next();
|
|
10822
|
+
if (next.done) break;
|
|
10823
|
+
yield next.value;
|
|
10824
|
+
}
|
|
10825
|
+
} finally {
|
|
10826
|
+
signal?.removeEventListener("abort", cancel);
|
|
10827
|
+
cancel();
|
|
10497
10828
|
}
|
|
10498
10829
|
return;
|
|
10499
10830
|
}
|
|
@@ -10502,21 +10833,23 @@ async function* readBodyChunks(body, signal) {
|
|
|
10502
10833
|
throw new Error("Unsupported hook stream body type");
|
|
10503
10834
|
}
|
|
10504
10835
|
const reader = stream.getReader();
|
|
10836
|
+
const cancelReader = () => {
|
|
10837
|
+
try {
|
|
10838
|
+
const cancellation = reader.cancel?.();
|
|
10839
|
+
if (cancellation) void cancellation.then(() => void 0, () => void 0);
|
|
10840
|
+
} catch {
|
|
10841
|
+
}
|
|
10842
|
+
};
|
|
10843
|
+
signal?.addEventListener("abort", cancelReader, { once: true });
|
|
10505
10844
|
try {
|
|
10506
10845
|
while (!signal?.aborted) {
|
|
10507
10846
|
const { done, value } = await reader.read();
|
|
10508
|
-
if (done)
|
|
10509
|
-
|
|
10510
|
-
}
|
|
10511
|
-
if (value) {
|
|
10512
|
-
yield value;
|
|
10513
|
-
}
|
|
10847
|
+
if (done) break;
|
|
10848
|
+
if (value) yield value;
|
|
10514
10849
|
}
|
|
10515
10850
|
} finally {
|
|
10516
|
-
|
|
10517
|
-
|
|
10518
|
-
} catch {
|
|
10519
|
-
}
|
|
10851
|
+
signal?.removeEventListener("abort", cancelReader);
|
|
10852
|
+
cancelReader();
|
|
10520
10853
|
reader.releaseLock?.();
|
|
10521
10854
|
}
|
|
10522
10855
|
}
|
|
@@ -12897,10 +13230,10 @@ var EncryptionService = class extends BaseService {
|
|
|
12897
13230
|
get crypto() {
|
|
12898
13231
|
return this._config.crypto;
|
|
12899
13232
|
}
|
|
12900
|
-
async discoverNetwork(
|
|
13233
|
+
async discoverNetwork(identifier2, ownerDid) {
|
|
12901
13234
|
this.assertActive();
|
|
12902
13235
|
const result = await discoverNetwork({
|
|
12903
|
-
identifier,
|
|
13236
|
+
identifier: identifier2,
|
|
12904
13237
|
...ownerDid !== void 0 ? { ownerDid } : {},
|
|
12905
13238
|
...this._config.node !== void 0 ? { node: this._config.node } : {},
|
|
12906
13239
|
...this._config.wellKnown !== void 0 ? { wellKnown: this._config.wellKnown } : {}
|
|
@@ -13804,6 +14137,9 @@ function permissionRequestsPath(profile) {
|
|
|
13804
14137
|
function grantHistoryPath(profile) {
|
|
13805
14138
|
return join4(PROFILES_DIR, profile, "auth-grants.jsonl");
|
|
13806
14139
|
}
|
|
14140
|
+
function localGrantArtifactsPath(profile) {
|
|
14141
|
+
return join4(PROFILES_DIR, profile, "auth-grant-artifacts.json");
|
|
14142
|
+
}
|
|
13807
14143
|
function createPermissionRequestArtifact(params) {
|
|
13808
14144
|
return buildPermissionRequestArtifact({
|
|
13809
14145
|
profile: params.profileName,
|
|
@@ -13828,6 +14164,32 @@ function didWithoutFragment(did) {
|
|
|
13828
14164
|
async function loadAdditionalDelegations(profile) {
|
|
13829
14165
|
return readAdditionalDelegations(profile);
|
|
13830
14166
|
}
|
|
14167
|
+
async function loadLocalGrantArtifacts(profile) {
|
|
14168
|
+
const path = localGrantArtifactsPath(profile);
|
|
14169
|
+
if (!await fileExists(path)) return [];
|
|
14170
|
+
let parsed;
|
|
14171
|
+
try {
|
|
14172
|
+
parsed = JSON.parse(await readFile2(path, "utf8"));
|
|
14173
|
+
} catch {
|
|
14174
|
+
return [];
|
|
14175
|
+
}
|
|
14176
|
+
if (!Array.isArray(parsed)) return [];
|
|
14177
|
+
return parsed.filter(
|
|
14178
|
+
(value) => value !== null && typeof value === "object" && typeof value.delegationCid === "string" && value.delegation !== null && typeof value.delegation === "object" && value.delegation.cid === value.delegationCid && typeof value.delegation.delegationHeader?.Authorization === "string"
|
|
14179
|
+
);
|
|
14180
|
+
}
|
|
14181
|
+
async function appendLocalGrantArtifact(profile, delegation) {
|
|
14182
|
+
await ProfileManager.ensureProfileDir(profile);
|
|
14183
|
+
await withProfileLock2(profile, async () => {
|
|
14184
|
+
await refuseWriteToDeletedProfile2(profile);
|
|
14185
|
+
const records = await loadLocalGrantArtifacts(profile);
|
|
14186
|
+
if (records.some((record) => record.delegationCid === delegation.cid)) return;
|
|
14187
|
+
await writeJsonAtomic(localGrantArtifactsPath(profile), [
|
|
14188
|
+
...records,
|
|
14189
|
+
{ delegationCid: delegation.cid, delegation }
|
|
14190
|
+
]);
|
|
14191
|
+
});
|
|
14192
|
+
}
|
|
13831
14193
|
async function appendAdditionalDelegations(profile, entries) {
|
|
13832
14194
|
const { mergeDelegationsWithoutRequest } = await import("@tinycloud/operations/delegation-binding");
|
|
13833
14195
|
await updateProfileStore(profile, "additional-delegations", (stored) => ({
|
|
@@ -15246,11 +15608,15 @@ function ownerSpaceId(space, ownerDid) {
|
|
|
15246
15608
|
function permissionTuples(permissions, ownerDid) {
|
|
15247
15609
|
return new Set(permissions.flatMap((permission) => actionTuples(permission, ownerDid)));
|
|
15248
15610
|
}
|
|
15611
|
+
function isRawCidRevocationPermission(permission) {
|
|
15612
|
+
return permission.service === "tinycloud.delegation" && permission.space?.startsWith("urn:cid:") === true && permission.path === "" && permission.actions.includes("tinycloud.delegation/revoke");
|
|
15613
|
+
}
|
|
15249
15614
|
function actionTuples(permission, ownerDid) {
|
|
15250
15615
|
const service = permission.service.startsWith("tinycloud.") ? permission.service : `tinycloud.${permission.service}`;
|
|
15251
|
-
const
|
|
15252
|
-
const
|
|
15253
|
-
const
|
|
15616
|
+
const rawEncryption = isVerifiedRawEncryptionPermission(permission);
|
|
15617
|
+
const rawCidRevocation = isRawCidRevocationPermission(permission);
|
|
15618
|
+
const space = rawEncryption ? ENCRYPTION_MANIFEST_SPACE2 : rawCidRevocation ? permission.space : normalizePkhIdentifier(ownerSpaceId(permission.space ?? "", ownerDid));
|
|
15619
|
+
const path = rawEncryption ? normalizePkhIdentifier(permission.path) : permission.path;
|
|
15254
15620
|
return permission.actions.map((action) => JSON.stringify([service, space, path, action.includes("/") ? action : `${service}/${action}`]));
|
|
15255
15621
|
}
|
|
15256
15622
|
function isLegacyNestedDecrypt(permission, requested, ownerDid, spaceId) {
|
|
@@ -15321,7 +15687,7 @@ function declinedPermissions(requested, signed, ownerDid) {
|
|
|
15321
15687
|
const missing = [...permissionTuples(requested, ownerDid)].filter((tuple) => !granted.has(tuple));
|
|
15322
15688
|
const requestedScopes = /* @__PURE__ */ new Map();
|
|
15323
15689
|
for (const entry of requested) {
|
|
15324
|
-
const space = isVerifiedRawEncryptionPermission(entry) ? entry.space ?? ENCRYPTION_MANIFEST_SPACE2 : ownerSpaceId(entry.space ?? "", ownerDid);
|
|
15690
|
+
const space = isVerifiedRawEncryptionPermission(entry) ? entry.space ?? ENCRYPTION_MANIFEST_SPACE2 : isRawCidRevocationPermission(entry) ? entry.space : ownerSpaceId(entry.space ?? "", ownerDid);
|
|
15325
15691
|
for (const tuple of actionTuples(entry, ownerDid)) {
|
|
15326
15692
|
requestedScopes.set(tuple, { space, path: entry.path });
|
|
15327
15693
|
}
|
|
@@ -15344,7 +15710,10 @@ function scopedLoginPermissions(permissions) {
|
|
|
15344
15710
|
}
|
|
15345
15711
|
function grantRequestPermissions(group, anchorSpace) {
|
|
15346
15712
|
const request = group.map((p) => isRawEncryptionPermission(p) ? { ...p, space: ENCRYPTION_MANIFEST_SPACE2, path: canonicalNetworkUrn(p.path) } : p);
|
|
15347
|
-
|
|
15713
|
+
const capabilitySpace = request.find(
|
|
15714
|
+
(permission) => !isRawEncryptionPermission(permission) && !isRawCidRevocationPermission(permission)
|
|
15715
|
+
)?.space ?? anchorSpace;
|
|
15716
|
+
return withCapabilitiesRead(request, capabilitySpace);
|
|
15348
15717
|
}
|
|
15349
15718
|
function withCapabilitiesRead(permissions, space) {
|
|
15350
15719
|
const hasRead = permissions.some((p) => p.service === "tinycloud.capabilities" && p.path === "" && p.actions.includes(CAPABILITIES_READ) && normalizePkhIdentifier(p.space ?? "") === normalizePkhIdentifier(space));
|
|
@@ -15378,7 +15747,10 @@ function verifySignedSession(data, key, sessionDid, expected = {}) {
|
|
|
15378
15747
|
if (/expir/i.test(error instanceof Error ? error.message : String(error))) {
|
|
15379
15748
|
throw new CLIError("AUTH_EXPIRED", `The approved session has expired. ${notStored}`, ExitCode.AUTH_REQUIRED);
|
|
15380
15749
|
}
|
|
15381
|
-
throw
|
|
15750
|
+
throw Object.assign(
|
|
15751
|
+
new CLIError("OPENKEY_PROOF_INVALID", `OpenKey did not return a complete, verifiable session proof. ${notStored}`, ExitCode.AUTH_REQUIRED),
|
|
15752
|
+
{ cause: error }
|
|
15753
|
+
);
|
|
15382
15754
|
}
|
|
15383
15755
|
const signedExpiry = Date.parse(expiresAt);
|
|
15384
15756
|
if (signedExpiry <= Date.now()) {
|
|
@@ -15395,10 +15767,23 @@ function verifySignedSession(data, key, sessionDid, expected = {}) {
|
|
|
15395
15767
|
}
|
|
15396
15768
|
function verifyScopedLogin(data, key, sessionDid, requested, expected = {}) {
|
|
15397
15769
|
const notStored = expected.purpose === "grant" ? "No grant was stored." : "No scoped session was saved.";
|
|
15398
|
-
|
|
15770
|
+
let signed;
|
|
15771
|
+
try {
|
|
15772
|
+
signed = verifySignedSession(data, key, sessionDid, expected);
|
|
15773
|
+
} catch (error) {
|
|
15774
|
+
const cause = error instanceof Error ? error.cause : void 0;
|
|
15775
|
+
if (requested.some(isRawCidRevocationPermission) && /invalid ReCap resource URI/i.test(cause instanceof Error ? cause.message : String(cause ?? ""))) {
|
|
15776
|
+
throw new CLIError(
|
|
15777
|
+
"RAW_RECAP_RESOURCE_UNSUPPORTED",
|
|
15778
|
+
"This WASM build cannot verify raw urn:cid ReCap resources.",
|
|
15779
|
+
ExitCode.PERMISSION_DENIED
|
|
15780
|
+
);
|
|
15781
|
+
}
|
|
15782
|
+
throw error;
|
|
15783
|
+
}
|
|
15399
15784
|
const spaceId = data.spaceId;
|
|
15400
15785
|
for (const permission of requested) {
|
|
15401
|
-
if (isRawEncryptionPermission(permission)) continue;
|
|
15786
|
+
if (isRawEncryptionPermission(permission) || isRawCidRevocationPermission(permission)) continue;
|
|
15402
15787
|
if (normalizePkhIdentifier(ownerSpaceId(permission.space ?? "", signed.ownerDid)) !== normalizePkhIdentifier(spaceId)) {
|
|
15403
15788
|
throw new CLIError("OPENKEY_SCOPE_MISMATCH", `The approved space differs from the requested space. ${notStored}`, ExitCode.PERMISSION_DENIED);
|
|
15404
15789
|
}
|
|
@@ -16439,6 +16824,13 @@ function registerAuthCommand(program) {
|
|
|
16439
16824
|
yes: options.yes === true
|
|
16440
16825
|
});
|
|
16441
16826
|
const grant = await grantAuthRequest(node, resolvedRequest);
|
|
16827
|
+
await appendLocalGrantArtifact(ctx.profile, grant.delegation);
|
|
16828
|
+
await appendGrantHistory(ctx.profile, {
|
|
16829
|
+
addedCaps: grant.permissions,
|
|
16830
|
+
source: "cli",
|
|
16831
|
+
delegationCid: grant.delegationCid,
|
|
16832
|
+
expiry: grant.expiry
|
|
16833
|
+
});
|
|
16442
16834
|
outputJson(grant);
|
|
16443
16835
|
} catch (error) {
|
|
16444
16836
|
handleError(error);
|
|
@@ -16795,7 +17187,10 @@ async function activateAndStoreOpenKeyGrants(profileName2, host, node, grants, s
|
|
|
16795
17187
|
});
|
|
16796
17188
|
}
|
|
16797
17189
|
async function ensureDelegationAuthority(params) {
|
|
16798
|
-
if (!params.force && params.node.hasRuntimePermissions(params.requested)) return;
|
|
17190
|
+
if (!params.force && params.node.hasRuntimePermissions(params.requested)) return void 0;
|
|
17191
|
+
if (!params.consentAlreadyGiven && (params.persist === false || params.profile.authMethod !== "openkey")) {
|
|
17192
|
+
await requirePermissionConsent(params.requested, params.yes);
|
|
17193
|
+
}
|
|
16799
17194
|
if (params.profile.authMethod === "openkey") {
|
|
16800
17195
|
const key = await ProfileManager.getKey(params.ctx.profile);
|
|
16801
17196
|
if (!key) {
|
|
@@ -16815,6 +17210,37 @@ async function ensureDelegationAuthority(params) {
|
|
|
16815
17210
|
expiry: expiryCap
|
|
16816
17211
|
};
|
|
16817
17212
|
const anchorSpace = params.anchorSpace ?? params.profile.spaceId ?? params.profile.spaceName;
|
|
17213
|
+
if (params.persist === false) {
|
|
17214
|
+
const request = grantRequestPermissions(params.requested, anchorSpace);
|
|
17215
|
+
let delegationData;
|
|
17216
|
+
try {
|
|
17217
|
+
delegationData = await acquireOpenKey(params.profile.did, {
|
|
17218
|
+
jwk: key,
|
|
17219
|
+
host: params.ctx.host,
|
|
17220
|
+
permissions: request,
|
|
17221
|
+
reason: permissionGrantReason(params.reason, request),
|
|
17222
|
+
openkeyHost,
|
|
17223
|
+
expiry: expiryCap === void 0 ? void 0 : openKeyExpiryParam(expiryCap)
|
|
17224
|
+
});
|
|
17225
|
+
} catch (error) {
|
|
17226
|
+
if (params.requested.some(isRawCidRevocationPermission) && /invalid ReCap resource URI/i.test(error instanceof Error ? error.message : String(error))) {
|
|
17227
|
+
throw new CLIError(
|
|
17228
|
+
"RAW_RECAP_RESOURCE_UNSUPPORTED",
|
|
17229
|
+
"The OpenKey/WASM ReCap encoder does not support raw urn:cid revocation resources.",
|
|
17230
|
+
ExitCode.PERMISSION_DENIED
|
|
17231
|
+
);
|
|
17232
|
+
}
|
|
17233
|
+
throw error;
|
|
17234
|
+
}
|
|
17235
|
+
const delegation = portableFromOpenKeyDelegation(
|
|
17236
|
+
delegationData,
|
|
17237
|
+
request,
|
|
17238
|
+
params.ctx.host,
|
|
17239
|
+
proof
|
|
17240
|
+
);
|
|
17241
|
+
await params.node.useRuntimeDelegation(delegation);
|
|
17242
|
+
return delegation;
|
|
17243
|
+
}
|
|
16818
17244
|
const grants = [];
|
|
16819
17245
|
for (const group of groupPermissionsBySpace(params.requested)) {
|
|
16820
17246
|
const request = grantRequestPermissions(group, anchorSpace);
|
|
@@ -16832,21 +17258,11 @@ async function ensureDelegationAuthority(params) {
|
|
|
16832
17258
|
await activateAndStoreOpenKeyGrants(params.ctx.profile, params.ctx.host, params.node, grants, "cli");
|
|
16833
17259
|
return;
|
|
16834
17260
|
}
|
|
16835
|
-
if (isInteractive()) {
|
|
16836
|
-
if (!params.yes) {
|
|
16837
|
-
await confirmPermissionRequest(params.requested);
|
|
16838
|
-
}
|
|
16839
|
-
} else if (!params.yes) {
|
|
16840
|
-
throw new CLIError(
|
|
16841
|
-
"CONFIRMATION_REQUIRED",
|
|
16842
|
-
"Local-key auth grants in non-interactive mode require --yes.",
|
|
16843
|
-
ExitCode.USAGE_ERROR
|
|
16844
|
-
);
|
|
16845
|
-
}
|
|
16846
17261
|
const delegations = await params.node.grantRuntimePermissions(
|
|
16847
17262
|
params.requested,
|
|
16848
17263
|
params.expiryOption !== void 0 ? { expiry: params.expiryOption } : void 0
|
|
16849
17264
|
);
|
|
17265
|
+
if (params.persist === false) return delegations[0];
|
|
16850
17266
|
for (const delegation of delegations) {
|
|
16851
17267
|
const covering = permissionsFromDelegation(delegation);
|
|
16852
17268
|
await appendAdditionalDelegation(
|
|
@@ -16924,6 +17340,17 @@ async function confirmPermissionRequest(permissions) {
|
|
|
16924
17340
|
throw new CLIError("REQUEST_CANCELLED", "Permission request cancelled.", ExitCode.ERROR);
|
|
16925
17341
|
}
|
|
16926
17342
|
}
|
|
17343
|
+
async function requirePermissionConsent(permissions, yes) {
|
|
17344
|
+
if (isInteractive()) {
|
|
17345
|
+
if (!yes) await confirmPermissionRequest(permissions);
|
|
17346
|
+
} else if (!yes) {
|
|
17347
|
+
throw new CLIError(
|
|
17348
|
+
"CONFIRMATION_REQUIRED",
|
|
17349
|
+
"Permission grants in non-interactive mode require --yes.",
|
|
17350
|
+
ExitCode.USAGE_ERROR
|
|
17351
|
+
);
|
|
17352
|
+
}
|
|
17353
|
+
}
|
|
16927
17354
|
function isDangerousPermission(permission) {
|
|
16928
17355
|
if (permission.path === "" || permission.path === "/") return true;
|
|
16929
17356
|
return permission.actions.some(
|
|
@@ -16952,7 +17379,7 @@ function groupPermissionsBySpace(permissions) {
|
|
|
16952
17379
|
const groups = /* @__PURE__ */ new Map();
|
|
16953
17380
|
const rawEntries = [];
|
|
16954
17381
|
for (const permission of permissions) {
|
|
16955
|
-
if (isRawEncryptionPermission(permission)) {
|
|
17382
|
+
if (isRawEncryptionPermission(permission) || isRawCidRevocationPermission(permission)) {
|
|
16956
17383
|
rawEntries.push(permission);
|
|
16957
17384
|
continue;
|
|
16958
17385
|
}
|
|
@@ -16983,11 +17410,13 @@ function portableFromOpenKeyDelegation(data, requested, host, proof) {
|
|
|
16983
17410
|
}
|
|
16984
17411
|
const returnedSpace = data.spaceId;
|
|
16985
17412
|
const effective = session.permissions;
|
|
16986
|
-
const spaced = effective.filter(
|
|
17413
|
+
const spaced = effective.filter(
|
|
17414
|
+
(permission) => !isRawEncryptionPermission(permission) && !isRawCidRevocationPermission(permission)
|
|
17415
|
+
);
|
|
16987
17416
|
const primary = spaced.find((permission) => permission.service !== "tinycloud.capabilities") ?? spaced[0] ?? effective[0];
|
|
16988
17417
|
const resources = effective.map((permission) => ({
|
|
16989
17418
|
service: permission.service.slice("tinycloud.".length),
|
|
16990
|
-
space: isVerifiedRawEncryptionPermission(permission) ? "encryption" : returnedSpace,
|
|
17419
|
+
space: isRawCidRevocationPermission(permission) ? permission.space : isVerifiedRawEncryptionPermission(permission) ? "encryption" : returnedSpace,
|
|
16991
17420
|
path: permission.path,
|
|
16992
17421
|
actions: [...permission.actions],
|
|
16993
17422
|
...permission.caveats === void 0 ? {} : { caveats: structuredClone(permission.caveats) }
|
|
@@ -17463,6 +17892,7 @@ init_profiles();
|
|
|
17463
17892
|
init_formatter();
|
|
17464
17893
|
init_errors();
|
|
17465
17894
|
init_constants();
|
|
17895
|
+
import { parseSignedCompactUcanAttenuation } from "@tinycloud/sdk-core";
|
|
17466
17896
|
function normalizeDid(input) {
|
|
17467
17897
|
const normalized = input.trim();
|
|
17468
17898
|
const fragmentIndex = normalized.indexOf("#");
|
|
@@ -17470,11 +17900,35 @@ function normalizeDid(input) {
|
|
|
17470
17900
|
}
|
|
17471
17901
|
function didMatches(actual, expected) {
|
|
17472
17902
|
if (!actual) return false;
|
|
17473
|
-
|
|
17474
|
-
|
|
17475
|
-
|
|
17476
|
-
|
|
17903
|
+
return normalizeDid(actual) === normalizeDid(expected);
|
|
17904
|
+
}
|
|
17905
|
+
function ownerDidFromSpace(spaceId) {
|
|
17906
|
+
const owner = /^tinycloud:((?:did:)?pkh:eip155:[1-9]\d*:0x[0-9a-fA-F]{40})(?::|\/|$)/.exec(spaceId ?? "")?.[1];
|
|
17907
|
+
if (!owner) return void 0;
|
|
17908
|
+
return owner.startsWith("did:") ? owner : `did:${owner}`;
|
|
17909
|
+
}
|
|
17910
|
+
async function resolveCidBoundTargetSpace(params) {
|
|
17911
|
+
if (!params.ownerDid) return void 0;
|
|
17912
|
+
const grants = await loadLocalGrantArtifacts(params.profileName);
|
|
17913
|
+
for (const grant of grants) {
|
|
17914
|
+
const delegation = grant.delegation;
|
|
17915
|
+
if (grant.delegationCid !== params.cid || delegation.cid !== params.cid) continue;
|
|
17916
|
+
try {
|
|
17917
|
+
const authorization = delegation.delegationHeader.Authorization.replace(/^Bearer /i, "");
|
|
17918
|
+
const signed = parseSignedCompactUcanAttenuation(authorization, params.cid);
|
|
17919
|
+
for (const resource of Object.keys(signed.payload.att)) {
|
|
17920
|
+
const space = /^(tinycloud:[^/]+)\/[^/]+\/.*$/.exec(resource)?.[1];
|
|
17921
|
+
if (!space) continue;
|
|
17922
|
+
const ownerMatch = /^tinycloud:((?:did:)?pkh:eip155:[1-9]\d*:0x[0-9a-fA-F]{40}):/.exec(space);
|
|
17923
|
+
const owner = ownerMatch?.[1];
|
|
17924
|
+
const ownerDid = owner?.startsWith("did:") ? owner : owner ? `did:${owner}` : void 0;
|
|
17925
|
+
if (ownerDid && normalizeDid(ownerDid) === normalizeDid(params.ownerDid)) return space;
|
|
17926
|
+
}
|
|
17927
|
+
} catch {
|
|
17928
|
+
continue;
|
|
17929
|
+
}
|
|
17477
17930
|
}
|
|
17931
|
+
return void 0;
|
|
17478
17932
|
}
|
|
17479
17933
|
function registerDelegationCommand(program) {
|
|
17480
17934
|
const delegation = program.command("delegation").description("Manage delegations");
|
|
@@ -17554,16 +18008,158 @@ function registerDelegationCommand(program) {
|
|
|
17554
18008
|
handleError(error);
|
|
17555
18009
|
}
|
|
17556
18010
|
});
|
|
17557
|
-
delegation.command("revoke <cid>").description("Revoke a delegation").action(async (cid,
|
|
18011
|
+
delegation.command("revoke <cid>").description("Revoke a delegation").option("--yes", "Skip local-key TTY confirmation", false).action(async (cid, options, cmd) => {
|
|
17558
18012
|
try {
|
|
17559
18013
|
const globalOpts = cmd.optsWithGlobals();
|
|
17560
18014
|
const ctx = await ProfileManager.resolveContext(globalOpts);
|
|
17561
18015
|
const node = await ensureAuthenticated(ctx);
|
|
17562
|
-
const
|
|
18016
|
+
const profile = await ProfileManager.getProfile(ctx.profile);
|
|
18017
|
+
const revokePermission = {
|
|
18018
|
+
service: "tinycloud.delegation",
|
|
18019
|
+
space: `urn:cid:${cid}`,
|
|
18020
|
+
path: "",
|
|
18021
|
+
actions: ["tinycloud.delegation/revoke"]
|
|
18022
|
+
};
|
|
18023
|
+
const rawAuthorityCovered = node.hasRuntimePermissions([revokePermission]);
|
|
18024
|
+
if (!rawAuthorityCovered) await requirePermissionConsent([revokePermission], options.yes);
|
|
18025
|
+
let targetFound = false;
|
|
18026
|
+
let targetSpaceSource;
|
|
18027
|
+
let queryDenied = false;
|
|
18028
|
+
let boundSpace;
|
|
18029
|
+
const accountSpaceId = node.accountSpaceId;
|
|
18030
|
+
if (!accountSpaceId) {
|
|
18031
|
+
throw new CLIError(
|
|
18032
|
+
"DELEGATION_QUERY_AUTHORITY_UNAVAILABLE",
|
|
18033
|
+
"Cannot determine the account space needed to query delegation records.",
|
|
18034
|
+
ExitCode.AUTH_REQUIRED
|
|
18035
|
+
);
|
|
18036
|
+
}
|
|
18037
|
+
let cursor;
|
|
18038
|
+
do {
|
|
18039
|
+
const query = await node.delegationManager.query({
|
|
18040
|
+
direction: "all",
|
|
18041
|
+
limit: 100,
|
|
18042
|
+
...cursor === void 0 ? {} : { cursor }
|
|
18043
|
+
});
|
|
18044
|
+
if (!query.ok) {
|
|
18045
|
+
if (query.error.meta?.status === 403) {
|
|
18046
|
+
queryDenied = true;
|
|
18047
|
+
break;
|
|
18048
|
+
}
|
|
18049
|
+
throw cliErrorFromService(query.error);
|
|
18050
|
+
}
|
|
18051
|
+
targetFound = query.data.items.some((delegation2) => delegation2.cid === cid);
|
|
18052
|
+
cursor = targetFound ? void 0 : query.data.nextCursor;
|
|
18053
|
+
} while (cursor !== void 0);
|
|
18054
|
+
targetSpaceSource = "node";
|
|
18055
|
+
if (!targetFound) {
|
|
18056
|
+
const grantHistory = await readGrantHistory(ctx.profile);
|
|
18057
|
+
const recordedGrant = [...grantHistory].reverse().find((entry) => entry.delegationCid === cid);
|
|
18058
|
+
if (recordedGrant) {
|
|
18059
|
+
targetSpaceSource = "local-grant-history";
|
|
18060
|
+
} else if (queryDenied) {
|
|
18061
|
+
boundSpace = await resolveCidBoundTargetSpace({
|
|
18062
|
+
profileName: ctx.profile,
|
|
18063
|
+
ownerDid: profile.ownerDid ?? ownerDidFromSpace(node.accountSpaceId) ?? ownerDidFromSpace(profile.spaceId),
|
|
18064
|
+
cid
|
|
18065
|
+
});
|
|
18066
|
+
if (boundSpace) {
|
|
18067
|
+
targetSpaceSource = "local-signed-grant-artifact";
|
|
18068
|
+
} else {
|
|
18069
|
+
throw new CLIError(
|
|
18070
|
+
"TARGET_NOT_FOUND",
|
|
18071
|
+
`Delegation "${cid}" was not found by the node or local grant sources.`,
|
|
18072
|
+
ExitCode.NOT_FOUND
|
|
18073
|
+
);
|
|
18074
|
+
}
|
|
18075
|
+
} else {
|
|
18076
|
+
throw new CLIError(
|
|
18077
|
+
"TARGET_NOT_FOUND",
|
|
18078
|
+
`Delegation "${cid}" was not found by the node or local grant history.`,
|
|
18079
|
+
ExitCode.NOT_FOUND
|
|
18080
|
+
);
|
|
18081
|
+
}
|
|
18082
|
+
}
|
|
18083
|
+
let authorityScopeSource = "cid-resource";
|
|
18084
|
+
let authorityScopeReason = "The revoke authority is scoped to the exact delegation CID.";
|
|
18085
|
+
let fallbackSpaceId;
|
|
18086
|
+
let commandAuthorityCid;
|
|
18087
|
+
const authorizeFallbackSpace = async (spaceId) => {
|
|
18088
|
+
fallbackSpaceId = spaceId;
|
|
18089
|
+
authorityScopeSource = "local-signed-grant-artifact";
|
|
18090
|
+
authorityScopeReason = "Raw-CID revoke authority could not be issued; the fallback space came from an owner-signed local grant whose Authorization recomputes to this CID.";
|
|
18091
|
+
targetSpaceSource = "local-signed-grant-artifact";
|
|
18092
|
+
const acquired = await ensureDelegationAuthority({
|
|
18093
|
+
ctx,
|
|
18094
|
+
profile,
|
|
18095
|
+
node,
|
|
18096
|
+
requested: [{
|
|
18097
|
+
service: "tinycloud.delegation",
|
|
18098
|
+
space: spaceId === node.accountSpaceId ? "default" : spaceId,
|
|
18099
|
+
path: "",
|
|
18100
|
+
actions: ["tinycloud.delegation/revoke"]
|
|
18101
|
+
}],
|
|
18102
|
+
expiryOption: void 0,
|
|
18103
|
+
reason: `Revoke delegation ${cid} using its CID-bound owner-signed grant artifact`,
|
|
18104
|
+
yes: options.yes,
|
|
18105
|
+
consentAlreadyGiven: true,
|
|
18106
|
+
persist: false
|
|
18107
|
+
});
|
|
18108
|
+
commandAuthorityCid = acquired?.cid;
|
|
18109
|
+
};
|
|
18110
|
+
if (!rawAuthorityCovered) {
|
|
18111
|
+
try {
|
|
18112
|
+
const acquired = await ensureDelegationAuthority({
|
|
18113
|
+
ctx,
|
|
18114
|
+
profile,
|
|
18115
|
+
node,
|
|
18116
|
+
requested: [revokePermission],
|
|
18117
|
+
expiryOption: void 0,
|
|
18118
|
+
reason: `Revoke delegation ${cid}`,
|
|
18119
|
+
yes: options.yes,
|
|
18120
|
+
consentAlreadyGiven: true,
|
|
18121
|
+
persist: false
|
|
18122
|
+
});
|
|
18123
|
+
commandAuthorityCid = acquired?.cid;
|
|
18124
|
+
} catch (error) {
|
|
18125
|
+
if (error === null || typeof error !== "object" || !("code" in error) || error.code !== "RAW_RECAP_RESOURCE_UNSUPPORTED") {
|
|
18126
|
+
throw error;
|
|
18127
|
+
}
|
|
18128
|
+
const fallbackSpace = boundSpace ?? await resolveCidBoundTargetSpace({
|
|
18129
|
+
profileName: ctx.profile,
|
|
18130
|
+
ownerDid: profile.ownerDid ?? ownerDidFromSpace(node.accountSpaceId) ?? ownerDidFromSpace(profile.spaceId),
|
|
18131
|
+
cid
|
|
18132
|
+
});
|
|
18133
|
+
if (!fallbackSpace) {
|
|
18134
|
+
throw new CLIError(
|
|
18135
|
+
"RAW_RECAP_RESOURCE_UNSUPPORTED",
|
|
18136
|
+
`Cannot safely revoke "${cid}": raw CID ReCap resources are unavailable and no matching owner-signed local grant artifact binds its space to the CID.`,
|
|
18137
|
+
ExitCode.PERMISSION_DENIED
|
|
18138
|
+
);
|
|
18139
|
+
}
|
|
18140
|
+
await authorizeFallbackSpace(fallbackSpace);
|
|
18141
|
+
}
|
|
18142
|
+
}
|
|
18143
|
+
const result = fallbackSpaceId ? await node.delegationManager.revoke(cid, {
|
|
18144
|
+
targetSpaceId: fallbackSpaceId,
|
|
18145
|
+
...commandAuthorityCid === void 0 ? {} : { authorityCid: commandAuthorityCid }
|
|
18146
|
+
}) : commandAuthorityCid === void 0 ? await node.revokeDelegation(cid) : await node.delegationManager.revoke(cid, { authorityCid: commandAuthorityCid });
|
|
17563
18147
|
if (!result.ok) {
|
|
18148
|
+
if (result.error.meta?.status === 403 && result.error.message === "Failed to revoke delegation: 403 - Unauthorized Revoker") {
|
|
18149
|
+
const profileDid = profile.ownerDid ?? ownerDidFromSpace(node.accountSpaceId) ?? ownerDidFromSpace(profile.spaceId) ?? "unknown";
|
|
18150
|
+
throw new CLIError(
|
|
18151
|
+
"REVOKE_UNAUTHORIZED",
|
|
18152
|
+
"The node rejected this revocation: Unauthorized Revoker.",
|
|
18153
|
+
ExitCode.PERMISSION_DENIED,
|
|
18154
|
+
{
|
|
18155
|
+
status: 403,
|
|
18156
|
+
hint: `Use a different profile belonging to the delegation's grantor, its recipient, or the owner of a space it covers; the rejected profile was "${ctx.profile}" (DID ${profileDid}). Retry with \`tc --profile <profile-name> delegation revoke ${cid}\`.`
|
|
18157
|
+
}
|
|
18158
|
+
);
|
|
18159
|
+
}
|
|
17564
18160
|
throw cliErrorFromService(result.error);
|
|
17565
18161
|
}
|
|
17566
|
-
outputJson({ cid, revoked: true });
|
|
18162
|
+
outputJson({ cid, revoked: true, targetSpaceSource, authorityScopeSource, authorityScopeReason });
|
|
17567
18163
|
} catch (error) {
|
|
17568
18164
|
handleError(error);
|
|
17569
18165
|
}
|
|
@@ -29959,17 +30555,17 @@ function validRecipientDid(value) {
|
|
|
29959
30555
|
if (value.length === 0 || value.length > 2048 || /[\u0000-\u0020\u007f]/.test(value)) return false;
|
|
29960
30556
|
const parts = value.split(":");
|
|
29961
30557
|
if (parts.length < 3 || parts[0] !== "did" || !/^[a-z0-9]+$/.test(parts[1] ?? "")) return false;
|
|
29962
|
-
const
|
|
29963
|
-
if (
|
|
30558
|
+
const identifier2 = parts.slice(2);
|
|
30559
|
+
if (identifier2.some((part) => part.length === 0)) return false;
|
|
29964
30560
|
if (parts[1] === "web") {
|
|
29965
|
-
const host =
|
|
30561
|
+
const host = identifier2[0] ?? "";
|
|
29966
30562
|
if (host.length > 253 || host.split(".").some((label) => !label || label.length > 63 || !/^[A-Za-z0-9-]+$/.test(label) || label.startsWith("-") || label.endsWith("-"))) return false;
|
|
29967
|
-
return
|
|
30563
|
+
return identifier2.slice(1).every((part) => /^[A-Za-z0-9._%-]+$/.test(part));
|
|
29968
30564
|
}
|
|
29969
|
-
if (parts[1] === "pkh") return
|
|
30565
|
+
if (parts[1] === "pkh") return identifier2.length >= 3 && identifier2.every((part) => /^[A-Za-z0-9._%-]+$/.test(part));
|
|
29970
30566
|
if (parts[1] === "key") {
|
|
29971
30567
|
try {
|
|
29972
|
-
const bytes3 = base58btc2.decode(
|
|
30568
|
+
const bytes3 = base58btc2.decode(identifier2.join(":"));
|
|
29973
30569
|
return bytes3.length === 34 && bytes3[0] === 237 && bytes3[1] === 1;
|
|
29974
30570
|
} catch {
|
|
29975
30571
|
return false;
|