@opendatalabs/personal-server-ts-server 1.8.0 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api-auth.d.ts.map +1 -1
- package/dist/api-auth.js +1 -0
- package/dist/api-auth.js.map +1 -1
- package/dist/app.d.ts +9 -0
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +30 -3
- package/dist/app.js.map +1 -1
- package/dist/bootstrap.d.ts +8 -0
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +60 -0
- package/dist/bootstrap.js.map +1 -1
- package/dist/routes/data.d.ts +6 -0
- package/dist/routes/data.d.ts.map +1 -1
- package/dist/routes/data.js +1 -0
- package/dist/routes/data.js.map +1 -1
- package/dist/routes/derivatives.d.ts +33 -0
- package/dist/routes/derivatives.d.ts.map +1 -0
- package/dist/routes/derivatives.js +33 -0
- package/dist/routes/derivatives.js.map +1 -0
- package/dist/storage/question-store.d.ts +9 -0
- package/dist/storage/question-store.d.ts.map +1 -0
- package/dist/storage/question-store.js +114 -0
- package/dist/storage/question-store.js.map +1 -0
- package/dist/ui/ps-lite-debug.js +2000 -96
- package/package.json +3 -3
package/dist/ui/ps-lite-debug.js
CHANGED
|
@@ -4404,7 +4404,7 @@ function weierstrass(curveDef) {
|
|
|
4404
4404
|
function prepSig(msgHash, privateKey, opts = defaultSigOpts) {
|
|
4405
4405
|
if (["recovered", "canonical"].some((k10) => k10 in opts))
|
|
4406
4406
|
throw new Error("sign() legacy options not supported");
|
|
4407
|
-
const { hash: hash2, randomBytes:
|
|
4407
|
+
const { hash: hash2, randomBytes: randomBytes5 } = CURVE4;
|
|
4408
4408
|
let { lowS, prehash, extraEntropy: ent } = opts;
|
|
4409
4409
|
if (lowS == null)
|
|
4410
4410
|
lowS = true;
|
|
@@ -4416,7 +4416,7 @@ function weierstrass(curveDef) {
|
|
|
4416
4416
|
const d10 = normPrivateKeyToScalar(privateKey);
|
|
4417
4417
|
const seedArgs = [int2octets(d10), int2octets(h1int)];
|
|
4418
4418
|
if (ent != null && ent !== false) {
|
|
4419
|
-
const e10 = ent === true ?
|
|
4419
|
+
const e10 = ent === true ? randomBytes5(Fp2.BYTES) : ent;
|
|
4420
4420
|
seedArgs.push(ensureBytes("extraEntropy", e10));
|
|
4421
4421
|
}
|
|
4422
4422
|
const seed = concatBytes3(...seedArgs);
|
|
@@ -105697,6 +105697,11 @@ var ServerNotConfiguredError = class extends ProtocolError {
|
|
|
105697
105697
|
super(500, "SERVER_NOT_CONFIGURED", "Server is not configured", details);
|
|
105698
105698
|
}
|
|
105699
105699
|
};
|
|
105700
|
+
var ContentTooLargeError = class extends ProtocolError {
|
|
105701
|
+
constructor(details) {
|
|
105702
|
+
super(413, "CONTENT_TOO_LARGE", "Content too large", details);
|
|
105703
|
+
}
|
|
105704
|
+
};
|
|
105700
105705
|
var LineageInvalidError = class extends ProtocolError {
|
|
105701
105706
|
constructor(message, details) {
|
|
105702
105707
|
super(400, "LINEAGE_INVALID", message, details);
|
|
@@ -105747,6 +105752,31 @@ var DataDeletedError = class extends ProtocolError {
|
|
|
105747
105752
|
super(410, "DATA_DELETED", "Data point has been deleted", details);
|
|
105748
105753
|
}
|
|
105749
105754
|
};
|
|
105755
|
+
var DerivativeQuestionInvalidError = class extends ProtocolError {
|
|
105756
|
+
constructor(message, details) {
|
|
105757
|
+
super(400, "DERIVATIVE_QUESTION_INVALID", message, details);
|
|
105758
|
+
}
|
|
105759
|
+
};
|
|
105760
|
+
var DerivativeQuestionNotFoundError = class extends ProtocolError {
|
|
105761
|
+
constructor(details) {
|
|
105762
|
+
super(404, "DERIVATIVE_QUESTION_NOT_FOUND", "Question not found", details);
|
|
105763
|
+
}
|
|
105764
|
+
};
|
|
105765
|
+
var DerivativeCycleError = class extends ProtocolError {
|
|
105766
|
+
constructor(details) {
|
|
105767
|
+
super(409, "DERIVATIVE_CYCLE", `Registering this question would make "${details.derivedScope}" a transitive source of itself; recompute would never settle`, details);
|
|
105768
|
+
}
|
|
105769
|
+
};
|
|
105770
|
+
var DerivativeSourceNotGrantedError = class extends ProtocolError {
|
|
105771
|
+
constructor(details) {
|
|
105772
|
+
super(403, "DERIVATIVE_SOURCE_NOT_GRANTED", "The builder's grant does not cover reading every source scope of this question", details);
|
|
105773
|
+
}
|
|
105774
|
+
};
|
|
105775
|
+
var DerivativeComputeUnavailableError = class extends ProtocolError {
|
|
105776
|
+
constructor(details) {
|
|
105777
|
+
super(503, "DERIVATIVE_COMPUTE_UNAVAILABLE", "This server has no derivative compute configured", details);
|
|
105778
|
+
}
|
|
105779
|
+
};
|
|
105750
105780
|
|
|
105751
105781
|
// ../core/dist/sync/data-point-id.js
|
|
105752
105782
|
function computeDataPointId(ownerAddress, scope) {
|
|
@@ -109719,6 +109749,21 @@ var DEFAULTS = {
|
|
|
109719
109749
|
enabled: true,
|
|
109720
109750
|
serverAddr: "frpc.server.vana.org",
|
|
109721
109751
|
serverPort: 7e3
|
|
109752
|
+
},
|
|
109753
|
+
inference: {
|
|
109754
|
+
// OpenAI-compatible chat completions endpoint the derivative compute
|
|
109755
|
+
// layer calls. Point it at the Vana inference relay (which holds the
|
|
109756
|
+
// provider key) or straight at a provider for local development.
|
|
109757
|
+
baseUrl: "https://inference.phala.com/v1",
|
|
109758
|
+
model: "z-ai/glm-5.2",
|
|
109759
|
+
// End to end encryption of prompt and answer to the Phala gateway
|
|
109760
|
+
// (E2EE v2): the relay only sees ciphertext. Set false only for local
|
|
109761
|
+
// development against a provider without ACI attestation.
|
|
109762
|
+
e2ee: true,
|
|
109763
|
+
// Newest-first items kept per source scope when a prompt is assembled.
|
|
109764
|
+
maxSourceItems: 50,
|
|
109765
|
+
// Quiet period after a source scope changes before a recompute starts.
|
|
109766
|
+
recomputeDebounceMs: 5e3
|
|
109722
109767
|
}
|
|
109723
109768
|
};
|
|
109724
109769
|
var StorageBackend = external_exports.enum([
|
|
@@ -109772,7 +109817,14 @@ var ServerConfigSchema = external_exports.object({
|
|
|
109772
109817
|
enabled: external_exports.boolean().default(DEFAULTS.tunnel.enabled),
|
|
109773
109818
|
serverAddr: external_exports.string().default(DEFAULTS.tunnel.serverAddr),
|
|
109774
109819
|
serverPort: external_exports.number().int().min(1).max(65535).default(DEFAULTS.tunnel.serverPort)
|
|
109775
|
-
}).default(DEFAULTS.tunnel)
|
|
109820
|
+
}).default(DEFAULTS.tunnel),
|
|
109821
|
+
inference: external_exports.object({
|
|
109822
|
+
baseUrl: external_exports.url().default(DEFAULTS.inference.baseUrl),
|
|
109823
|
+
model: external_exports.string().min(1).default(DEFAULTS.inference.model),
|
|
109824
|
+
e2ee: external_exports.boolean().default(DEFAULTS.inference.e2ee),
|
|
109825
|
+
maxSourceItems: external_exports.number().int().min(1).max(1e4).default(DEFAULTS.inference.maxSourceItems),
|
|
109826
|
+
recomputeDebounceMs: external_exports.number().int().min(0).max(36e5).default(DEFAULTS.inference.recomputeDebounceMs)
|
|
109827
|
+
}).default(DEFAULTS.inference)
|
|
109776
109828
|
});
|
|
109777
109829
|
|
|
109778
109830
|
// ../lite/dist/state.js
|
|
@@ -112676,6 +112728,18 @@ function apiLoggerAsLogger(logger) {
|
|
|
112676
112728
|
error: (payload, message) => (logger?.error ?? noop)(payload, message ?? "")
|
|
112677
112729
|
};
|
|
112678
112730
|
}
|
|
112731
|
+
function notifyDataWritten(deps, event) {
|
|
112732
|
+
if (!deps.onDataWritten)
|
|
112733
|
+
return;
|
|
112734
|
+
try {
|
|
112735
|
+
deps.onDataWritten(event);
|
|
112736
|
+
} catch (err2) {
|
|
112737
|
+
deps.logger?.warn?.({
|
|
112738
|
+
scope: event.scope,
|
|
112739
|
+
error: err2 instanceof Error ? err2.message : String(err2)
|
|
112740
|
+
}, "onDataWritten hook failed; record already stored");
|
|
112741
|
+
}
|
|
112742
|
+
}
|
|
112679
112743
|
function notifyNewData(syncManager) {
|
|
112680
112744
|
if (!syncManager)
|
|
112681
112745
|
return;
|
|
@@ -113040,6 +113104,11 @@ async function handlePersonalServerDataRequest(request2, deps, options = {}) {
|
|
|
113040
113104
|
}, "Binary data file ingested");
|
|
113041
113105
|
await logBuilderWrite();
|
|
113042
113106
|
notifyNewData(deps.syncManager);
|
|
113107
|
+
notifyDataWritten(deps, {
|
|
113108
|
+
scope: scopeResult.scope,
|
|
113109
|
+
collectedAt: collectedAtValue,
|
|
113110
|
+
lineageSources: lineage2?.sources
|
|
113111
|
+
});
|
|
113043
113112
|
return jsonResponse(result2.response, { status: 201 });
|
|
113044
113113
|
}
|
|
113045
113114
|
const parsed = await parseJsonObjectBody(request2, "Request body must be valid JSON");
|
|
@@ -113067,6 +113136,11 @@ async function handlePersonalServerDataRequest(request2, deps, options = {}) {
|
|
|
113067
113136
|
}, "Data file ingested");
|
|
113068
113137
|
await logBuilderWrite();
|
|
113069
113138
|
notifyNewData(deps.syncManager);
|
|
113139
|
+
notifyDataWritten(deps, {
|
|
113140
|
+
scope: scopeResult.scope,
|
|
113141
|
+
collectedAt: collectedAtValue,
|
|
113142
|
+
lineageSources: lineage?.sources
|
|
113143
|
+
});
|
|
113070
113144
|
return jsonResponse(result.response, { status: 201 });
|
|
113071
113145
|
} catch (err2) {
|
|
113072
113146
|
if (err2 instanceof IngestPersistedError) {
|
|
@@ -113293,6 +113367,93 @@ async function handlePersonalServerOauthTokenRequest(request2, deps) {
|
|
|
113293
113367
|
});
|
|
113294
113368
|
}
|
|
113295
113369
|
|
|
113370
|
+
// ../core/dist/derivatives/types.js
|
|
113371
|
+
function questionRegistrationView(registration) {
|
|
113372
|
+
return {
|
|
113373
|
+
questionId: registration.questionId,
|
|
113374
|
+
derivedScope: registration.derivedScope,
|
|
113375
|
+
sourceScopes: [...registration.sourceScopes],
|
|
113376
|
+
question: registration.question,
|
|
113377
|
+
model: registration.model,
|
|
113378
|
+
registeredBy: registration.registeredBy,
|
|
113379
|
+
status: registration.status,
|
|
113380
|
+
error: registration.error,
|
|
113381
|
+
createdAt: registration.createdAt,
|
|
113382
|
+
updatedAt: registration.updatedAt,
|
|
113383
|
+
lastComputedAt: registration.lastComputedAt,
|
|
113384
|
+
derivedVersion: registration.derivedVersion,
|
|
113385
|
+
derivedCollectedAt: registration.derivedCollectedAt
|
|
113386
|
+
};
|
|
113387
|
+
}
|
|
113388
|
+
|
|
113389
|
+
// ../core/dist/derivatives/store.js
|
|
113390
|
+
function clone2(registration) {
|
|
113391
|
+
return {
|
|
113392
|
+
...registration,
|
|
113393
|
+
sourceScopes: [...registration.sourceScopes],
|
|
113394
|
+
registeredBy: { ...registration.registeredBy }
|
|
113395
|
+
};
|
|
113396
|
+
}
|
|
113397
|
+
function matchesQuestionFilter(registration, filter) {
|
|
113398
|
+
if (!filter)
|
|
113399
|
+
return true;
|
|
113400
|
+
if (filter.derivedScope && registration.derivedScope !== filter.derivedScope)
|
|
113401
|
+
return false;
|
|
113402
|
+
if (filter.sourceScope && !registration.sourceScopes.includes(filter.sourceScope))
|
|
113403
|
+
return false;
|
|
113404
|
+
if (filter.builder) {
|
|
113405
|
+
const by2 = registration.registeredBy;
|
|
113406
|
+
if (by2.kind !== "builder" || by2.builder.toLowerCase() !== filter.builder.toLowerCase())
|
|
113407
|
+
return false;
|
|
113408
|
+
}
|
|
113409
|
+
return true;
|
|
113410
|
+
}
|
|
113411
|
+
function sortQuestions(registrations) {
|
|
113412
|
+
return [...registrations].sort((a10, b10) => a10.createdAt.localeCompare(b10.createdAt) || a10.questionId.localeCompare(b10.questionId));
|
|
113413
|
+
}
|
|
113414
|
+
function createInMemoryQuestionStore(options = {}) {
|
|
113415
|
+
const byId = /* @__PURE__ */ new Map();
|
|
113416
|
+
for (const registration of options.initial ?? []) {
|
|
113417
|
+
byId.set(registration.questionId, clone2(registration));
|
|
113418
|
+
}
|
|
113419
|
+
async function changed() {
|
|
113420
|
+
if (!options.onChange)
|
|
113421
|
+
return;
|
|
113422
|
+
await options.onChange(sortQuestions([...byId.values()]).map(clone2));
|
|
113423
|
+
}
|
|
113424
|
+
return {
|
|
113425
|
+
async list(filter) {
|
|
113426
|
+
return sortQuestions([...byId.values()].filter((registration) => matchesQuestionFilter(registration, filter))).map(clone2);
|
|
113427
|
+
},
|
|
113428
|
+
async get(questionId) {
|
|
113429
|
+
const registration = byId.get(questionId);
|
|
113430
|
+
return registration ? clone2(registration) : null;
|
|
113431
|
+
},
|
|
113432
|
+
async insert(registration) {
|
|
113433
|
+
if (byId.has(registration.questionId)) {
|
|
113434
|
+
throw new Error(`Question ${registration.questionId} is already registered`);
|
|
113435
|
+
}
|
|
113436
|
+
byId.set(registration.questionId, clone2(registration));
|
|
113437
|
+
await changed();
|
|
113438
|
+
},
|
|
113439
|
+
async update(questionId, patch) {
|
|
113440
|
+
const current = byId.get(questionId);
|
|
113441
|
+
if (!current)
|
|
113442
|
+
return null;
|
|
113443
|
+
const next = { ...current, ...patch };
|
|
113444
|
+
byId.set(questionId, next);
|
|
113445
|
+
await changed();
|
|
113446
|
+
return clone2(next);
|
|
113447
|
+
},
|
|
113448
|
+
async delete(questionId) {
|
|
113449
|
+
const existed = byId.delete(questionId);
|
|
113450
|
+
if (existed)
|
|
113451
|
+
await changed();
|
|
113452
|
+
return existed;
|
|
113453
|
+
}
|
|
113454
|
+
};
|
|
113455
|
+
}
|
|
113456
|
+
|
|
113296
113457
|
// ../core/dist/policy/data-read.js
|
|
113297
113458
|
function parseGrantExpiresAtSeconds(value) {
|
|
113298
113459
|
if (value === null || value === void 0 || value === "0")
|
|
@@ -113382,6 +113543,1635 @@ async function verifyDataReadPolicy(input, ports) {
|
|
|
113382
113543
|
return grant;
|
|
113383
113544
|
}
|
|
113384
113545
|
|
|
113546
|
+
// ../core/dist/policy/data-write.js
|
|
113547
|
+
var WRITE_SCOPE_PREFIX = "write:";
|
|
113548
|
+
function isWriteScopeEntry(entry) {
|
|
113549
|
+
return entry.startsWith(WRITE_SCOPE_PREFIX);
|
|
113550
|
+
}
|
|
113551
|
+
function writeScopePatterns(grantScopes) {
|
|
113552
|
+
return grantScopes.filter(isWriteScopeEntry).map((entry) => entry.slice(WRITE_SCOPE_PREFIX.length)).filter((pattern) => pattern.length > 0);
|
|
113553
|
+
}
|
|
113554
|
+
function scopeCoveredByWriteGrant(requestedScope, grantScopes) {
|
|
113555
|
+
return writeScopePatterns(grantScopes).some((pattern) => scopeMatchesPattern(requestedScope, pattern));
|
|
113556
|
+
}
|
|
113557
|
+
async function verifyDataWritePolicy(input, ports) {
|
|
113558
|
+
const available = await ports.runtimeAvailability?.isAvailable();
|
|
113559
|
+
if (available === false) {
|
|
113560
|
+
throw new PsUnavailableError();
|
|
113561
|
+
}
|
|
113562
|
+
const builder = await ports.authSessionVerifier.getBuilder(input.signer);
|
|
113563
|
+
if (!builder) {
|
|
113564
|
+
throw new UnregisteredBuilderError();
|
|
113565
|
+
}
|
|
113566
|
+
if (!input.grantId) {
|
|
113567
|
+
throw new GrantRequiredError({
|
|
113568
|
+
reason: "No grantId bound to the write session"
|
|
113569
|
+
});
|
|
113570
|
+
}
|
|
113571
|
+
const grant = await ports.grantVerifier.getGrant(input.grantId);
|
|
113572
|
+
if (!grant) {
|
|
113573
|
+
throw new GrantRequiredError({
|
|
113574
|
+
reason: "Grant not found",
|
|
113575
|
+
grantId: input.grantId
|
|
113576
|
+
});
|
|
113577
|
+
}
|
|
113578
|
+
if (grant.revokedAt !== null) {
|
|
113579
|
+
throw new GrantRevokedError({ grantId: grant.id });
|
|
113580
|
+
}
|
|
113581
|
+
if (!grant.scopes || writeScopePatterns(grant.scopes).length === 0) {
|
|
113582
|
+
throw new ScopeMismatchError({
|
|
113583
|
+
requestedScope: input.requestedScope,
|
|
113584
|
+
reason: "Grant has no write scopes"
|
|
113585
|
+
});
|
|
113586
|
+
}
|
|
113587
|
+
if (grant.expiresAt !== null && grant.expiresAt !== void 0) {
|
|
113588
|
+
const expiresAtSec = parseGrantExpiresAtSeconds(grant.expiresAt);
|
|
113589
|
+
if (expiresAtSec === null) {
|
|
113590
|
+
throw new ScopeMismatchError({
|
|
113591
|
+
requestedScope: input.requestedScope,
|
|
113592
|
+
reason: "Grant expiry is invalid"
|
|
113593
|
+
});
|
|
113594
|
+
}
|
|
113595
|
+
if (expiresAtSec > 0) {
|
|
113596
|
+
const nowSec = Math.floor(Date.now() / 1e3);
|
|
113597
|
+
if (expiresAtSec < nowSec) {
|
|
113598
|
+
throw new GrantExpiredError({
|
|
113599
|
+
expiresAt: expiresAtSec
|
|
113600
|
+
});
|
|
113601
|
+
}
|
|
113602
|
+
}
|
|
113603
|
+
}
|
|
113604
|
+
if (!scopeCoveredByWriteGrant(input.requestedScope, grant.scopes)) {
|
|
113605
|
+
throw new ScopeMismatchError({
|
|
113606
|
+
requestedScope: input.requestedScope,
|
|
113607
|
+
grantedScopes: grant.scopes,
|
|
113608
|
+
reason: "Grant does not authorize writing to this scope"
|
|
113609
|
+
});
|
|
113610
|
+
}
|
|
113611
|
+
if (builder.id.toLowerCase() !== grant.granteeId.toLowerCase()) {
|
|
113612
|
+
throw new InvalidSignatureError3({
|
|
113613
|
+
reason: "Write signer is not the grant builder",
|
|
113614
|
+
expected: grant.granteeId,
|
|
113615
|
+
actual: input.signer
|
|
113616
|
+
});
|
|
113617
|
+
}
|
|
113618
|
+
if (!input.serverOwner) {
|
|
113619
|
+
throw new ServerNotConfiguredError({
|
|
113620
|
+
reason: "serverOwner is required to verify grant ownership"
|
|
113621
|
+
});
|
|
113622
|
+
}
|
|
113623
|
+
if (!grant.grantorAddress || grant.grantorAddress.toLowerCase() !== input.serverOwner.toLowerCase()) {
|
|
113624
|
+
throw new GrantOwnerMismatchError({
|
|
113625
|
+
grantId: grant.id,
|
|
113626
|
+
expected: input.serverOwner,
|
|
113627
|
+
actual: grant.grantorAddress ?? null
|
|
113628
|
+
});
|
|
113629
|
+
}
|
|
113630
|
+
await ports.writeFeeVerifier?.assertWriteAllowed({
|
|
113631
|
+
builder: input.signer,
|
|
113632
|
+
grant,
|
|
113633
|
+
scope: input.requestedScope
|
|
113634
|
+
});
|
|
113635
|
+
return grant;
|
|
113636
|
+
}
|
|
113637
|
+
|
|
113638
|
+
// ../core/dist/derivatives/registration.js
|
|
113639
|
+
var MAX_QUESTION_SOURCE_SCOPES = 16;
|
|
113640
|
+
var MAX_QUESTION_CHARS = 8e3;
|
|
113641
|
+
var MAX_MODEL_CHARS = 128;
|
|
113642
|
+
var MAX_ECHOED_SCOPE_CHARS = 128;
|
|
113643
|
+
var MODEL_ID = /^[A-Za-z0-9][A-Za-z0-9._:/-]*$/;
|
|
113644
|
+
function isRecord4(value) {
|
|
113645
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
113646
|
+
}
|
|
113647
|
+
function parseScope(value, field) {
|
|
113648
|
+
if (typeof value !== "string") {
|
|
113649
|
+
throw new DerivativeQuestionInvalidError(`${field} must be a scope string`, {
|
|
113650
|
+
field
|
|
113651
|
+
});
|
|
113652
|
+
}
|
|
113653
|
+
const parsed = parseDataScopeContract(value);
|
|
113654
|
+
if (!parsed.ok) {
|
|
113655
|
+
throw new DerivativeQuestionInvalidError(`${field} is not a valid scope: ${parsed.body.message}`, { field, scope: value.slice(0, MAX_ECHOED_SCOPE_CHARS) });
|
|
113656
|
+
}
|
|
113657
|
+
return parsed.scope;
|
|
113658
|
+
}
|
|
113659
|
+
function parseQuestionInput(body) {
|
|
113660
|
+
if (!isRecord4(body)) {
|
|
113661
|
+
throw new DerivativeQuestionInvalidError("Body must be a JSON object");
|
|
113662
|
+
}
|
|
113663
|
+
const derivedScope = parseScope(body.derivedScope, "derivedScope");
|
|
113664
|
+
if (!Array.isArray(body.sourceScopes) || body.sourceScopes.length === 0) {
|
|
113665
|
+
throw new DerivativeQuestionInvalidError("sourceScopes must be a non-empty array of scopes", { field: "sourceScopes" });
|
|
113666
|
+
}
|
|
113667
|
+
if (body.sourceScopes.length > MAX_QUESTION_SOURCE_SCOPES) {
|
|
113668
|
+
throw new DerivativeQuestionInvalidError(`sourceScopes lists ${body.sourceScopes.length} scopes; the maximum is ${MAX_QUESTION_SOURCE_SCOPES}`, { field: "sourceScopes", max: MAX_QUESTION_SOURCE_SCOPES });
|
|
113669
|
+
}
|
|
113670
|
+
const sourceScopes = [];
|
|
113671
|
+
for (const entry of body.sourceScopes) {
|
|
113672
|
+
const scope = parseScope(entry, "sourceScopes[]");
|
|
113673
|
+
if (sourceScopes.includes(scope)) {
|
|
113674
|
+
throw new DerivativeQuestionInvalidError("sourceScopes lists the same scope twice", { field: "sourceScopes", duplicate: scope });
|
|
113675
|
+
}
|
|
113676
|
+
if (scope === derivedScope) {
|
|
113677
|
+
throw new DerivativeQuestionInvalidError("derivedScope cannot be one of its own sources", { field: "sourceScopes", scope });
|
|
113678
|
+
}
|
|
113679
|
+
sourceScopes.push(scope);
|
|
113680
|
+
}
|
|
113681
|
+
if (typeof body.question !== "string" || body.question.trim() === "") {
|
|
113682
|
+
throw new DerivativeQuestionInvalidError("question must be a non-empty string", { field: "question" });
|
|
113683
|
+
}
|
|
113684
|
+
if (body.question.length > MAX_QUESTION_CHARS) {
|
|
113685
|
+
throw new DerivativeQuestionInvalidError(`question is ${body.question.length} characters; the maximum is ${MAX_QUESTION_CHARS}`, { field: "question", max: MAX_QUESTION_CHARS });
|
|
113686
|
+
}
|
|
113687
|
+
let model = null;
|
|
113688
|
+
if (body.model !== void 0 && body.model !== null) {
|
|
113689
|
+
if (typeof body.model !== "string" || body.model.length > MAX_MODEL_CHARS || !MODEL_ID.test(body.model)) {
|
|
113690
|
+
throw new DerivativeQuestionInvalidError("model must be a provider model id", { field: "model" });
|
|
113691
|
+
}
|
|
113692
|
+
model = body.model;
|
|
113693
|
+
}
|
|
113694
|
+
assertDerivedScopeNaming(derivedScope, sourceScopes);
|
|
113695
|
+
return { derivedScope, sourceScopes, question: body.question, model };
|
|
113696
|
+
}
|
|
113697
|
+
function findDerivationCycle(candidate, existing) {
|
|
113698
|
+
const sourcesOf = /* @__PURE__ */ new Map();
|
|
113699
|
+
const add2 = (derived, sources) => {
|
|
113700
|
+
const set2 = sourcesOf.get(derived) ?? /* @__PURE__ */ new Set();
|
|
113701
|
+
for (const source of sources)
|
|
113702
|
+
set2.add(source);
|
|
113703
|
+
sourcesOf.set(derived, set2);
|
|
113704
|
+
};
|
|
113705
|
+
for (const registration of existing) {
|
|
113706
|
+
add2(registration.derivedScope, registration.sourceScopes);
|
|
113707
|
+
}
|
|
113708
|
+
add2(candidate.derivedScope, candidate.sourceScopes);
|
|
113709
|
+
const target = candidate.derivedScope;
|
|
113710
|
+
const visited = /* @__PURE__ */ new Set();
|
|
113711
|
+
const stack = [
|
|
113712
|
+
{ scope: target, path: [target] }
|
|
113713
|
+
];
|
|
113714
|
+
while (stack.length > 0) {
|
|
113715
|
+
const { scope, path } = stack.pop();
|
|
113716
|
+
for (const source of sourcesOf.get(scope) ?? []) {
|
|
113717
|
+
if (source === target)
|
|
113718
|
+
return [...path, source];
|
|
113719
|
+
if (visited.has(source))
|
|
113720
|
+
continue;
|
|
113721
|
+
visited.add(source);
|
|
113722
|
+
stack.push({ scope: source, path: [...path, source] });
|
|
113723
|
+
}
|
|
113724
|
+
}
|
|
113725
|
+
return null;
|
|
113726
|
+
}
|
|
113727
|
+
async function createQuestionRegistration(input) {
|
|
113728
|
+
const parsed = parseQuestionInput(input.body);
|
|
113729
|
+
const cycle = findDerivationCycle(parsed, await input.store.list());
|
|
113730
|
+
if (cycle) {
|
|
113731
|
+
throw new DerivativeCycleError({
|
|
113732
|
+
derivedScope: parsed.derivedScope,
|
|
113733
|
+
path: cycle
|
|
113734
|
+
});
|
|
113735
|
+
}
|
|
113736
|
+
const at3 = input.now().toISOString();
|
|
113737
|
+
const registration = {
|
|
113738
|
+
questionId: input.questionId,
|
|
113739
|
+
derivedScope: parsed.derivedScope,
|
|
113740
|
+
sourceScopes: parsed.sourceScopes,
|
|
113741
|
+
question: parsed.question,
|
|
113742
|
+
model: parsed.model,
|
|
113743
|
+
registeredBy: input.registeredBy,
|
|
113744
|
+
status: "pending",
|
|
113745
|
+
error: null,
|
|
113746
|
+
createdAt: at3,
|
|
113747
|
+
updatedAt: at3,
|
|
113748
|
+
lastComputedAt: null,
|
|
113749
|
+
derivedVersion: null,
|
|
113750
|
+
derivedCollectedAt: null
|
|
113751
|
+
};
|
|
113752
|
+
await input.store.insert(registration);
|
|
113753
|
+
return registration;
|
|
113754
|
+
}
|
|
113755
|
+
function uncoveredSourceScopes(sourceScopes, grantScopes) {
|
|
113756
|
+
const readEntries = (grantScopes ?? []).filter((entry) => !isWriteScopeEntry(entry));
|
|
113757
|
+
return sourceScopes.filter((scope) => !scopeCoveredByGrant(scope, readEntries));
|
|
113758
|
+
}
|
|
113759
|
+
|
|
113760
|
+
// ../core/dist/derivatives/prompt.js
|
|
113761
|
+
var DEFAULT_MAX_SOURCE_ITEMS = 50;
|
|
113762
|
+
var DEFAULT_MAX_SOURCE_CHARS = 2e5;
|
|
113763
|
+
var TIMESTAMP_KEYS = [
|
|
113764
|
+
"collectedAt",
|
|
113765
|
+
"updatedAt",
|
|
113766
|
+
"updated_at",
|
|
113767
|
+
"update_time",
|
|
113768
|
+
"createdAt",
|
|
113769
|
+
"created_at",
|
|
113770
|
+
"create_time",
|
|
113771
|
+
"timestamp",
|
|
113772
|
+
"time",
|
|
113773
|
+
"date",
|
|
113774
|
+
"publishedAt",
|
|
113775
|
+
"published_at"
|
|
113776
|
+
];
|
|
113777
|
+
var RESERVED_KEYS = /* @__PURE__ */ new Set(["$lineage", "$writtenBy", "$binary"]);
|
|
113778
|
+
function isRecord5(value) {
|
|
113779
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
113780
|
+
}
|
|
113781
|
+
function timestampOf(item) {
|
|
113782
|
+
if (!isRecord5(item))
|
|
113783
|
+
return null;
|
|
113784
|
+
for (const key of TIMESTAMP_KEYS) {
|
|
113785
|
+
const value = item[key];
|
|
113786
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
113787
|
+
return value < 1e12 ? value * 1e3 : value;
|
|
113788
|
+
}
|
|
113789
|
+
if (typeof value === "string") {
|
|
113790
|
+
const parsed = Date.parse(value);
|
|
113791
|
+
if (!Number.isNaN(parsed))
|
|
113792
|
+
return parsed;
|
|
113793
|
+
}
|
|
113794
|
+
}
|
|
113795
|
+
return null;
|
|
113796
|
+
}
|
|
113797
|
+
function sortNewestFirst(items) {
|
|
113798
|
+
const indexed = items.map((item, index) => ({
|
|
113799
|
+
item,
|
|
113800
|
+
index,
|
|
113801
|
+
at: timestampOf(item)
|
|
113802
|
+
}));
|
|
113803
|
+
const dated = indexed.filter((entry) => entry.at !== null).sort((a10, b10) => b10.at - a10.at || b10.index - a10.index);
|
|
113804
|
+
const undated = indexed.filter((entry) => entry.at === null).reverse();
|
|
113805
|
+
return [...dated, ...undated].map((entry) => entry.item);
|
|
113806
|
+
}
|
|
113807
|
+
function trimSourceData(data, options = {}) {
|
|
113808
|
+
const maxItems = Math.max(1, options.maxItems ?? DEFAULT_MAX_SOURCE_ITEMS);
|
|
113809
|
+
const maxChars = Math.max(1, options.maxChars ?? DEFAULT_MAX_SOURCE_CHARS);
|
|
113810
|
+
let kept = 0;
|
|
113811
|
+
let total = 0;
|
|
113812
|
+
const trimArray = (items, limit2) => {
|
|
113813
|
+
total += items.length;
|
|
113814
|
+
const sorted = sortNewestFirst(items).slice(0, limit2);
|
|
113815
|
+
kept += sorted.length;
|
|
113816
|
+
return sorted;
|
|
113817
|
+
};
|
|
113818
|
+
const build = (limit2) => {
|
|
113819
|
+
kept = 0;
|
|
113820
|
+
total = 0;
|
|
113821
|
+
if (Array.isArray(data))
|
|
113822
|
+
return trimArray(data, limit2);
|
|
113823
|
+
if (isRecord5(data)) {
|
|
113824
|
+
const out = {};
|
|
113825
|
+
for (const [key, value] of Object.entries(data)) {
|
|
113826
|
+
if (RESERVED_KEYS.has(key))
|
|
113827
|
+
continue;
|
|
113828
|
+
out[key] = Array.isArray(value) ? trimArray(value, limit2) : value;
|
|
113829
|
+
}
|
|
113830
|
+
return out;
|
|
113831
|
+
}
|
|
113832
|
+
return data;
|
|
113833
|
+
};
|
|
113834
|
+
let limit = maxItems;
|
|
113835
|
+
let result = build(limit);
|
|
113836
|
+
let text = JSON.stringify(result) ?? "null";
|
|
113837
|
+
while (text.length > maxChars && limit > 1) {
|
|
113838
|
+
limit = Math.max(1, Math.floor(limit / 2));
|
|
113839
|
+
result = build(limit);
|
|
113840
|
+
text = JSON.stringify(result) ?? "null";
|
|
113841
|
+
}
|
|
113842
|
+
if (text.length > maxChars) {
|
|
113843
|
+
return {
|
|
113844
|
+
data: `${text.slice(0, maxChars)}...[truncated]`,
|
|
113845
|
+
kept,
|
|
113846
|
+
total,
|
|
113847
|
+
truncated: true
|
|
113848
|
+
};
|
|
113849
|
+
}
|
|
113850
|
+
return { data: result, kept, total, truncated: false };
|
|
113851
|
+
}
|
|
113852
|
+
var SYSTEM_PROMPT = [
|
|
113853
|
+
"You answer a question about a person using ONLY the user data provided in the message.",
|
|
113854
|
+
"Do not use outside knowledge and do not guess; if the data does not support an answer, say so in the answer.",
|
|
113855
|
+
"Respond with a single JSON object and nothing else, with exactly these fields:",
|
|
113856
|
+
' "answer": string, the answer to the question, written for the person the data belongs to;',
|
|
113857
|
+
' "evidence": string, a short summary of which parts of the data support the answer.'
|
|
113858
|
+
].join("\n");
|
|
113859
|
+
function buildQuestionMessages(input) {
|
|
113860
|
+
const sections = input.sources.map((source) => {
|
|
113861
|
+
const note = source.total > source.kept ? ` (newest ${source.kept} of ${source.total} items)` : "";
|
|
113862
|
+
const cut = source.truncated ? " (truncated)" : "";
|
|
113863
|
+
return [
|
|
113864
|
+
`### Scope: ${source.scope}${note}${cut}`,
|
|
113865
|
+
`Collected at: ${source.collectedAt}`,
|
|
113866
|
+
JSON.stringify(source.data)
|
|
113867
|
+
].join("\n");
|
|
113868
|
+
});
|
|
113869
|
+
const user = [
|
|
113870
|
+
"## Question",
|
|
113871
|
+
input.question,
|
|
113872
|
+
"",
|
|
113873
|
+
"## User data",
|
|
113874
|
+
...sections,
|
|
113875
|
+
"",
|
|
113876
|
+
"Answer the question as a JSON object with the fields answer and evidence."
|
|
113877
|
+
].join("\n");
|
|
113878
|
+
return [
|
|
113879
|
+
{ role: "system", content: SYSTEM_PROMPT },
|
|
113880
|
+
{ role: "user", content: user }
|
|
113881
|
+
];
|
|
113882
|
+
}
|
|
113883
|
+
function parseAnswer(content) {
|
|
113884
|
+
const candidates = [content.trim()];
|
|
113885
|
+
const fenced = /```(?:json)?\s*([\s\S]*?)```/i.exec(content);
|
|
113886
|
+
if (fenced?.[1])
|
|
113887
|
+
candidates.unshift(fenced[1].trim());
|
|
113888
|
+
const first = content.indexOf("{");
|
|
113889
|
+
const last2 = content.lastIndexOf("}");
|
|
113890
|
+
if (first !== -1 && last2 > first) {
|
|
113891
|
+
candidates.push(content.slice(first, last2 + 1));
|
|
113892
|
+
}
|
|
113893
|
+
for (const candidate of candidates) {
|
|
113894
|
+
try {
|
|
113895
|
+
const parsed = JSON.parse(candidate);
|
|
113896
|
+
if (isRecord5(parsed) && typeof parsed.answer === "string") {
|
|
113897
|
+
return {
|
|
113898
|
+
answer: parsed.answer,
|
|
113899
|
+
evidence: typeof parsed.evidence === "string" ? parsed.evidence : null
|
|
113900
|
+
};
|
|
113901
|
+
}
|
|
113902
|
+
} catch {
|
|
113903
|
+
}
|
|
113904
|
+
}
|
|
113905
|
+
return { answer: content.trim(), evidence: null };
|
|
113906
|
+
}
|
|
113907
|
+
|
|
113908
|
+
// ../core/dist/derivatives/inference.js
|
|
113909
|
+
var DEFAULT_INFERENCE_BASE_URL = "https://inference.phala.com/v1";
|
|
113910
|
+
var DEFAULT_INFERENCE_MODEL = "z-ai/glm-5.2";
|
|
113911
|
+
var DEFAULT_INFERENCE_TIMEOUT_MS = 12e4;
|
|
113912
|
+
var DEFAULT_INFERENCE_MAX_TOKENS = 2048;
|
|
113913
|
+
var DEFAULT_INFERENCE_REQUEST_FIELDS = {
|
|
113914
|
+
provider: { aci_verified: true, zdr: true }
|
|
113915
|
+
};
|
|
113916
|
+
var InferenceRequestError = class extends Error {
|
|
113917
|
+
status;
|
|
113918
|
+
/** OpenAI-style `error.type` of the rejection, when the body had one. */
|
|
113919
|
+
errorType;
|
|
113920
|
+
/**
|
|
113921
|
+
* Explicit retry hint. When undefined the compute layer falls back to the
|
|
113922
|
+
* status: no response, 429 or 5xx are retried.
|
|
113923
|
+
*/
|
|
113924
|
+
retryable;
|
|
113925
|
+
constructor(message, status2, options = {}) {
|
|
113926
|
+
super(message);
|
|
113927
|
+
this.status = status2;
|
|
113928
|
+
this.name = "InferenceRequestError";
|
|
113929
|
+
this.errorType = options.errorType ?? null;
|
|
113930
|
+
this.retryable = options.retryable;
|
|
113931
|
+
}
|
|
113932
|
+
};
|
|
113933
|
+
function isRecord6(value) {
|
|
113934
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
113935
|
+
}
|
|
113936
|
+
function readUsage(value) {
|
|
113937
|
+
if (!isRecord6(value))
|
|
113938
|
+
return void 0;
|
|
113939
|
+
const num2 = (v10) => typeof v10 === "number" ? v10 : void 0;
|
|
113940
|
+
const usage = {
|
|
113941
|
+
promptTokens: num2(value.prompt_tokens),
|
|
113942
|
+
completionTokens: num2(value.completion_tokens),
|
|
113943
|
+
totalTokens: num2(value.total_tokens)
|
|
113944
|
+
};
|
|
113945
|
+
return usage;
|
|
113946
|
+
}
|
|
113947
|
+
function readContent(body) {
|
|
113948
|
+
if (!isRecord6(body) || !Array.isArray(body.choices))
|
|
113949
|
+
return null;
|
|
113950
|
+
const first = body.choices[0];
|
|
113951
|
+
if (!isRecord6(first) || !isRecord6(first.message))
|
|
113952
|
+
return null;
|
|
113953
|
+
const index = typeof first.index === "number" ? first.index : 0;
|
|
113954
|
+
const field = `choices.${index}.message.content`;
|
|
113955
|
+
const id2 = typeof body.id === "string" ? body.id : "";
|
|
113956
|
+
const content = first.message.content;
|
|
113957
|
+
if (typeof content === "string")
|
|
113958
|
+
return { content, field, id: id2 };
|
|
113959
|
+
if (Array.isArray(content)) {
|
|
113960
|
+
const text = content.map((part) => isRecord6(part) && typeof part.text === "string" ? part.text : "").join("");
|
|
113961
|
+
return { content: text, field, id: id2 };
|
|
113962
|
+
}
|
|
113963
|
+
return null;
|
|
113964
|
+
}
|
|
113965
|
+
async function readErrorType(response) {
|
|
113966
|
+
try {
|
|
113967
|
+
const body = await response.json();
|
|
113968
|
+
if (isRecord6(body) && isRecord6(body.error)) {
|
|
113969
|
+
return typeof body.error.type === "string" ? body.error.type : null;
|
|
113970
|
+
}
|
|
113971
|
+
} catch {
|
|
113972
|
+
}
|
|
113973
|
+
return null;
|
|
113974
|
+
}
|
|
113975
|
+
function createOpenAiCompatibleInferenceProvider(options = {}) {
|
|
113976
|
+
const base = (options.baseUrl ?? DEFAULT_INFERENCE_BASE_URL).replace(/\/+$/, "");
|
|
113977
|
+
const defaultModel = options.model ?? DEFAULT_INFERENCE_MODEL;
|
|
113978
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_INFERENCE_TIMEOUT_MS;
|
|
113979
|
+
const doFetch = options.fetch ?? fetch;
|
|
113980
|
+
const requestFields = options.requestFields ?? DEFAULT_INFERENCE_REQUEST_FIELDS;
|
|
113981
|
+
const encryption = options.encryption;
|
|
113982
|
+
async function send(input) {
|
|
113983
|
+
const headers = new Headers({ "Content-Type": "application/json" });
|
|
113984
|
+
if (options.apiKey) {
|
|
113985
|
+
headers.set("Authorization", `Bearer ${options.apiKey}`);
|
|
113986
|
+
}
|
|
113987
|
+
const model = input.model || defaultModel;
|
|
113988
|
+
let messages = input.messages;
|
|
113989
|
+
let encrypted = null;
|
|
113990
|
+
if (encryption) {
|
|
113991
|
+
encrypted = await encryption.encryptRequest({
|
|
113992
|
+
model,
|
|
113993
|
+
messages,
|
|
113994
|
+
headers
|
|
113995
|
+
});
|
|
113996
|
+
messages = encrypted.messages;
|
|
113997
|
+
}
|
|
113998
|
+
const body = {
|
|
113999
|
+
...requestFields,
|
|
114000
|
+
model,
|
|
114001
|
+
messages,
|
|
114002
|
+
max_tokens: input.maxTokens ?? DEFAULT_INFERENCE_MAX_TOKENS
|
|
114003
|
+
};
|
|
114004
|
+
let response;
|
|
114005
|
+
try {
|
|
114006
|
+
response = await doFetch(`${base}/chat/completions`, {
|
|
114007
|
+
method: "POST",
|
|
114008
|
+
headers,
|
|
114009
|
+
body: JSON.stringify(body),
|
|
114010
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
114011
|
+
});
|
|
114012
|
+
} catch (err2) {
|
|
114013
|
+
const name = err2 instanceof Error ? err2.name : "Error";
|
|
114014
|
+
throw new InferenceRequestError(`inference request failed before a response (${name})`, null);
|
|
114015
|
+
}
|
|
114016
|
+
if (!response.ok) {
|
|
114017
|
+
const errorType = await readErrorType(response);
|
|
114018
|
+
const retry = await encryption?.onRejected?.({
|
|
114019
|
+
status: response.status,
|
|
114020
|
+
errorType,
|
|
114021
|
+
headers: response.headers
|
|
114022
|
+
}) === true;
|
|
114023
|
+
return {
|
|
114024
|
+
ok: false,
|
|
114025
|
+
retry,
|
|
114026
|
+
error: new InferenceRequestError(`inference request failed with status ${response.status}${errorType ? ` (${errorType})` : ""}`, response.status, { errorType })
|
|
114027
|
+
};
|
|
114028
|
+
}
|
|
114029
|
+
let parsed;
|
|
114030
|
+
try {
|
|
114031
|
+
parsed = await response.json();
|
|
114032
|
+
} catch {
|
|
114033
|
+
throw new InferenceRequestError("inference response was not JSON", response.status);
|
|
114034
|
+
}
|
|
114035
|
+
const choice = readContent(parsed);
|
|
114036
|
+
if (choice === null) {
|
|
114037
|
+
throw new InferenceRequestError("inference response carried no assistant content", response.status);
|
|
114038
|
+
}
|
|
114039
|
+
let content = choice.content;
|
|
114040
|
+
if (encrypted) {
|
|
114041
|
+
content = await encrypted.decryptResponse({
|
|
114042
|
+
content,
|
|
114043
|
+
field: choice.field,
|
|
114044
|
+
id: choice.id,
|
|
114045
|
+
headers: response.headers
|
|
114046
|
+
});
|
|
114047
|
+
}
|
|
114048
|
+
if (content.trim() === "") {
|
|
114049
|
+
throw new InferenceRequestError("inference response carried no assistant content", response.status);
|
|
114050
|
+
}
|
|
114051
|
+
const receiptId = response.headers.get("x-receipt-id") ?? void 0;
|
|
114052
|
+
const aciIdentity = response.headers.get("x-aci-identity") ?? void 0;
|
|
114053
|
+
return {
|
|
114054
|
+
ok: true,
|
|
114055
|
+
result: {
|
|
114056
|
+
content,
|
|
114057
|
+
usage: readUsage(isRecord6(parsed) ? parsed.usage : void 0),
|
|
114058
|
+
...receiptId ? { receiptId } : {},
|
|
114059
|
+
...aciIdentity ? { aciIdentity } : {}
|
|
114060
|
+
}
|
|
114061
|
+
};
|
|
114062
|
+
}
|
|
114063
|
+
return {
|
|
114064
|
+
defaultModel,
|
|
114065
|
+
async chat(input) {
|
|
114066
|
+
const first = await send(input);
|
|
114067
|
+
if (first.ok)
|
|
114068
|
+
return first.result;
|
|
114069
|
+
if (!first.retry)
|
|
114070
|
+
throw first.error;
|
|
114071
|
+
const second = await send(input);
|
|
114072
|
+
if (second.ok)
|
|
114073
|
+
return second.result;
|
|
114074
|
+
throw second.error;
|
|
114075
|
+
}
|
|
114076
|
+
};
|
|
114077
|
+
}
|
|
114078
|
+
|
|
114079
|
+
// ../core/dist/derivatives/compute.js
|
|
114080
|
+
var DEFAULT_RETRY_DELAYS_MS = [1e3, 4e3];
|
|
114081
|
+
function isRetryableInferenceError(err2) {
|
|
114082
|
+
if (!(err2 instanceof InferenceRequestError))
|
|
114083
|
+
return false;
|
|
114084
|
+
if (err2.retryable !== void 0)
|
|
114085
|
+
return err2.retryable;
|
|
114086
|
+
return err2.status === null || err2.status === 429 || err2.status >= 500;
|
|
114087
|
+
}
|
|
114088
|
+
async function withRetries(deps, attempt, retryable) {
|
|
114089
|
+
const delays = deps.retryDelaysMs ?? DEFAULT_RETRY_DELAYS_MS;
|
|
114090
|
+
const sleep2 = deps.sleep ?? ((ms3) => new Promise((r10) => setTimeout(r10, ms3)));
|
|
114091
|
+
for (let index = 0; ; index += 1) {
|
|
114092
|
+
try {
|
|
114093
|
+
return await attempt();
|
|
114094
|
+
} catch (err2) {
|
|
114095
|
+
if (index >= delays.length || !retryable(err2))
|
|
114096
|
+
throw err2;
|
|
114097
|
+
await sleep2(delays[index]);
|
|
114098
|
+
}
|
|
114099
|
+
}
|
|
114100
|
+
}
|
|
114101
|
+
var ComputeFailure = class extends Error {
|
|
114102
|
+
constructor(message) {
|
|
114103
|
+
super(message);
|
|
114104
|
+
this.name = "ComputeFailure";
|
|
114105
|
+
}
|
|
114106
|
+
};
|
|
114107
|
+
function shortError(err2) {
|
|
114108
|
+
if (err2 instanceof ComputeFailure)
|
|
114109
|
+
return err2.message;
|
|
114110
|
+
if (err2 instanceof InferenceRequestError)
|
|
114111
|
+
return err2.message;
|
|
114112
|
+
if (err2 instanceof ProtocolError)
|
|
114113
|
+
return `${err2.errorCode}: ${err2.message}`;
|
|
114114
|
+
return `compute failed (${err2 instanceof Error ? err2.name : "Error"})`;
|
|
114115
|
+
}
|
|
114116
|
+
function collectedAtStamp(now, isTaken) {
|
|
114117
|
+
const base = now();
|
|
114118
|
+
base.setUTCMilliseconds(0);
|
|
114119
|
+
for (let bump = 0; bump < 60; bump += 1) {
|
|
114120
|
+
const candidate = new Date(base.getTime() + bump * 1e3).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
114121
|
+
if (!isTaken(candidate))
|
|
114122
|
+
return candidate;
|
|
114123
|
+
}
|
|
114124
|
+
throw new ComputeFailure("could not allocate a version stamp");
|
|
114125
|
+
}
|
|
114126
|
+
async function tombstoneMarker(scopeDeletions, scope) {
|
|
114127
|
+
if (!scopeDeletions)
|
|
114128
|
+
return null;
|
|
114129
|
+
const verdict = await scopeDeletions.resolve(scope);
|
|
114130
|
+
if (!verdict.deleted || verdict.version === null)
|
|
114131
|
+
return null;
|
|
114132
|
+
const version4 = Number(verdict.version);
|
|
114133
|
+
return Number.isSafeInteger(version4) ? version4 : null;
|
|
114134
|
+
}
|
|
114135
|
+
function localScopesById2(storage, serverOwner) {
|
|
114136
|
+
const byId = /* @__PURE__ */ new Map();
|
|
114137
|
+
for (let offset = 0; ; offset += LOCAL_SCOPE_SCAN_PAGE) {
|
|
114138
|
+
const { scopes, total } = storage.listScopes({
|
|
114139
|
+
limit: LOCAL_SCOPE_SCAN_PAGE,
|
|
114140
|
+
offset
|
|
114141
|
+
});
|
|
114142
|
+
for (const summary of scopes) {
|
|
114143
|
+
byId.set(computeDataPointId(serverOwner, summary.scope), summary.scope);
|
|
114144
|
+
}
|
|
114145
|
+
if (scopes.length === 0 || offset + scopes.length >= total)
|
|
114146
|
+
break;
|
|
114147
|
+
}
|
|
114148
|
+
return byId;
|
|
114149
|
+
}
|
|
114150
|
+
async function assertNoLineageCycle(deps, registration, serverOwner, sourceLineage) {
|
|
114151
|
+
const derivedId = computeDataPointId(serverOwner, registration.derivedScope);
|
|
114152
|
+
let byId = null;
|
|
114153
|
+
const visited = /* @__PURE__ */ new Set();
|
|
114154
|
+
const stack = [];
|
|
114155
|
+
for (const [scope, sources] of sourceLineage) {
|
|
114156
|
+
for (const id2 of sources)
|
|
114157
|
+
stack.push({ id: id2, path: [scope] });
|
|
114158
|
+
}
|
|
114159
|
+
while (stack.length > 0) {
|
|
114160
|
+
const { id: id2, path } = stack.pop();
|
|
114161
|
+
if (id2 === derivedId) {
|
|
114162
|
+
throw new DerivativeCycleError({
|
|
114163
|
+
derivedScope: registration.derivedScope,
|
|
114164
|
+
path: [registration.derivedScope, ...path, registration.derivedScope]
|
|
114165
|
+
});
|
|
114166
|
+
}
|
|
114167
|
+
if (visited.has(id2))
|
|
114168
|
+
continue;
|
|
114169
|
+
visited.add(id2);
|
|
114170
|
+
byId ??= localScopesById2(deps.storage, serverOwner);
|
|
114171
|
+
const scope = byId.get(id2);
|
|
114172
|
+
if (!scope)
|
|
114173
|
+
continue;
|
|
114174
|
+
const entry = deps.storage.findEntry({ scope });
|
|
114175
|
+
if (!entry)
|
|
114176
|
+
continue;
|
|
114177
|
+
let sources = [];
|
|
114178
|
+
try {
|
|
114179
|
+
const envelope = await deps.storage.readEnvelope(scope, entry.collectedAt);
|
|
114180
|
+
sources = readStoredLineage(envelope.data)?.sources ?? [];
|
|
114181
|
+
} catch {
|
|
114182
|
+
}
|
|
114183
|
+
for (const next of sources)
|
|
114184
|
+
stack.push({ id: next, path: [...path, scope] });
|
|
114185
|
+
}
|
|
114186
|
+
}
|
|
114187
|
+
async function loadSource(deps, scope) {
|
|
114188
|
+
const entry = deps.storage.findEntry({ scope });
|
|
114189
|
+
const deletion = await resolveReadDeletion({ scopeDeletions: deps.scopeDeletions, serverOwner: deps.serverOwner }, scope, entry);
|
|
114190
|
+
if (deletion) {
|
|
114191
|
+
throw new ComputeFailure(`source scope ${scope} is deleted`);
|
|
114192
|
+
}
|
|
114193
|
+
if (!entry) {
|
|
114194
|
+
throw new ComputeFailure(`source scope ${scope} has no local data`);
|
|
114195
|
+
}
|
|
114196
|
+
let envelope;
|
|
114197
|
+
try {
|
|
114198
|
+
envelope = await deps.storage.readEnvelope(scope, entry.collectedAt);
|
|
114199
|
+
} catch {
|
|
114200
|
+
throw new ComputeFailure(`source scope ${scope} could not be read`);
|
|
114201
|
+
}
|
|
114202
|
+
let lineageSources = [];
|
|
114203
|
+
try {
|
|
114204
|
+
lineageSources = readStoredLineage(envelope.data)?.sources ?? [];
|
|
114205
|
+
} catch {
|
|
114206
|
+
}
|
|
114207
|
+
const raw = isBinaryEnvelope(envelope) ? {
|
|
114208
|
+
binary: true,
|
|
114209
|
+
note: "binary record; its content is not included in the prompt"
|
|
114210
|
+
} : envelope.data;
|
|
114211
|
+
const trimmed = trimSourceData(raw, {
|
|
114212
|
+
maxItems: deps.maxSourceItems,
|
|
114213
|
+
maxChars: deps.maxSourceChars
|
|
114214
|
+
});
|
|
114215
|
+
return {
|
|
114216
|
+
source: {
|
|
114217
|
+
scope,
|
|
114218
|
+
collectedAt: entry.collectedAt,
|
|
114219
|
+
version: entry.version,
|
|
114220
|
+
data: trimmed.data,
|
|
114221
|
+
kept: trimmed.kept,
|
|
114222
|
+
total: trimmed.total,
|
|
114223
|
+
truncated: trimmed.truncated
|
|
114224
|
+
},
|
|
114225
|
+
lineageSources
|
|
114226
|
+
};
|
|
114227
|
+
}
|
|
114228
|
+
async function assertGrantStillValid(deps, registration) {
|
|
114229
|
+
if (registration.registeredBy.kind !== "builder")
|
|
114230
|
+
return;
|
|
114231
|
+
if (!deps.writePolicyPorts) {
|
|
114232
|
+
throw new ComputeFailure("builder grant verification is not configured");
|
|
114233
|
+
}
|
|
114234
|
+
if (!deps.serverOwner) {
|
|
114235
|
+
throw new ComputeFailure("server owner is not configured");
|
|
114236
|
+
}
|
|
114237
|
+
const { builder, grantId } = registration.registeredBy;
|
|
114238
|
+
const ports = deps.writePolicyPorts;
|
|
114239
|
+
const serverOwner = deps.serverOwner;
|
|
114240
|
+
const grant = await withRetries(deps, () => verifyDataWritePolicy({
|
|
114241
|
+
signer: builder,
|
|
114242
|
+
grantId,
|
|
114243
|
+
requestedScope: registration.derivedScope,
|
|
114244
|
+
serverOwner
|
|
114245
|
+
}, ports), (err2) => !(err2 instanceof ProtocolError));
|
|
114246
|
+
const uncovered = uncoveredSourceScopes(registration.sourceScopes, grant.scopes ?? []);
|
|
114247
|
+
if (uncovered.length > 0) {
|
|
114248
|
+
throw new DerivativeSourceNotGrantedError({ scopes: uncovered });
|
|
114249
|
+
}
|
|
114250
|
+
}
|
|
114251
|
+
async function computeQuestion(questionId, deps) {
|
|
114252
|
+
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
114253
|
+
if (await deps.runtimeAvailability?.isAvailable() === false) {
|
|
114254
|
+
return { status: "skipped", reason: "runtime-unavailable" };
|
|
114255
|
+
}
|
|
114256
|
+
const registration = await deps.store.get(questionId);
|
|
114257
|
+
if (!registration)
|
|
114258
|
+
return { status: "skipped", reason: "unknown-question" };
|
|
114259
|
+
try {
|
|
114260
|
+
if (!deps.serverOwner) {
|
|
114261
|
+
throw new ComputeFailure("server owner is not configured");
|
|
114262
|
+
}
|
|
114263
|
+
const serverOwner = deps.serverOwner;
|
|
114264
|
+
await assertGrantStillValid(deps, registration);
|
|
114265
|
+
assertDerivedScopeNaming(registration.derivedScope, registration.sourceScopes);
|
|
114266
|
+
const sources = [];
|
|
114267
|
+
const sourceLineage = /* @__PURE__ */ new Map();
|
|
114268
|
+
for (const scope of registration.sourceScopes) {
|
|
114269
|
+
const loaded = await loadSource(deps, scope);
|
|
114270
|
+
sources.push(loaded.source);
|
|
114271
|
+
sourceLineage.set(scope, loaded.lineageSources);
|
|
114272
|
+
}
|
|
114273
|
+
await assertNoLineageCycle(deps, registration, serverOwner, sourceLineage);
|
|
114274
|
+
const messages = buildQuestionMessages({
|
|
114275
|
+
question: registration.question,
|
|
114276
|
+
sources
|
|
114277
|
+
});
|
|
114278
|
+
const model = registration.model ?? deps.provider.defaultModel;
|
|
114279
|
+
const reply = await withRetries(deps, () => deps.provider.chat({ model, messages, maxTokens: deps.maxTokens }), isRetryableInferenceError);
|
|
114280
|
+
const parsed = parseAnswer(reply.content);
|
|
114281
|
+
const computedAt = now().toISOString();
|
|
114282
|
+
const lineageIds = registration.sourceScopes.map((scope) => computeDataPointId(serverOwner, scope));
|
|
114283
|
+
const record2 = {
|
|
114284
|
+
questionId: registration.questionId,
|
|
114285
|
+
question: registration.question,
|
|
114286
|
+
answer: parsed.answer,
|
|
114287
|
+
evidence: parsed.evidence,
|
|
114288
|
+
model,
|
|
114289
|
+
computedAt,
|
|
114290
|
+
sources: sources.map((source) => ({
|
|
114291
|
+
scope: source.scope,
|
|
114292
|
+
version: source.version,
|
|
114293
|
+
collectedAt: source.collectedAt
|
|
114294
|
+
})),
|
|
114295
|
+
lineage: lineageIds,
|
|
114296
|
+
...reply.receiptId || reply.aciIdentity ? {
|
|
114297
|
+
inference: {
|
|
114298
|
+
...reply.receiptId ? { receiptId: reply.receiptId } : {},
|
|
114299
|
+
...reply.aciIdentity ? { aciIdentity: reply.aciIdentity } : {}
|
|
114300
|
+
}
|
|
114301
|
+
} : {}
|
|
114302
|
+
};
|
|
114303
|
+
const lineage = {
|
|
114304
|
+
sources: lineageIds,
|
|
114305
|
+
writtenAt: computedAt
|
|
114306
|
+
};
|
|
114307
|
+
const collectedAt2 = collectedAtStamp(now, (candidate) => deps.storage.findEntry({
|
|
114308
|
+
scope: registration.derivedScope,
|
|
114309
|
+
at: candidate
|
|
114310
|
+
})?.collectedAt === candidate);
|
|
114311
|
+
const written = await ingestDataContract({
|
|
114312
|
+
storage: deps.storage,
|
|
114313
|
+
scopeParam: registration.derivedScope,
|
|
114314
|
+
body: record2,
|
|
114315
|
+
collectedAt: collectedAt2,
|
|
114316
|
+
status: deps.syncManager ? "syncing" : "stored",
|
|
114317
|
+
lineage,
|
|
114318
|
+
afterTombstoneVersion: await tombstoneMarker(deps.scopeDeletions, registration.derivedScope)
|
|
114319
|
+
});
|
|
114320
|
+
if (!written.ok) {
|
|
114321
|
+
throw new ComputeFailure(`derived record rejected: ${written.body.error}`);
|
|
114322
|
+
}
|
|
114323
|
+
const entry = deps.storage.findEntry({
|
|
114324
|
+
scope: registration.derivedScope,
|
|
114325
|
+
at: collectedAt2
|
|
114326
|
+
});
|
|
114327
|
+
const updated = await deps.store.update(questionId, {
|
|
114328
|
+
status: "ready",
|
|
114329
|
+
error: null,
|
|
114330
|
+
updatedAt: computedAt,
|
|
114331
|
+
lastComputedAt: computedAt,
|
|
114332
|
+
derivedVersion: entry?.version ?? null,
|
|
114333
|
+
derivedCollectedAt: collectedAt2
|
|
114334
|
+
});
|
|
114335
|
+
if (deps.syncManager?.notifyNewData) {
|
|
114336
|
+
deps.syncManager.notifyNewData();
|
|
114337
|
+
} else if (deps.syncManager?.trigger) {
|
|
114338
|
+
void deps.syncManager.trigger().catch(() => void 0);
|
|
114339
|
+
}
|
|
114340
|
+
try {
|
|
114341
|
+
deps.onDerivedWritten?.({
|
|
114342
|
+
scope: registration.derivedScope,
|
|
114343
|
+
collectedAt: collectedAt2,
|
|
114344
|
+
lineageSources: lineageIds
|
|
114345
|
+
});
|
|
114346
|
+
} catch (err2) {
|
|
114347
|
+
deps.logger?.warn?.({
|
|
114348
|
+
questionId,
|
|
114349
|
+
derivedScope: registration.derivedScope,
|
|
114350
|
+
error: err2 instanceof Error ? err2.name : String(err2)
|
|
114351
|
+
}, "onDerivedWritten hook failed; derivative already written");
|
|
114352
|
+
}
|
|
114353
|
+
deps.logger?.info?.({
|
|
114354
|
+
questionId,
|
|
114355
|
+
derivedScope: registration.derivedScope,
|
|
114356
|
+
sourceScopes: registration.sourceScopes,
|
|
114357
|
+
model,
|
|
114358
|
+
version: entry?.version ?? null,
|
|
114359
|
+
receiptId: reply.receiptId ?? null
|
|
114360
|
+
}, "Derivative question computed");
|
|
114361
|
+
return {
|
|
114362
|
+
status: "ready",
|
|
114363
|
+
registration: updated ?? { ...registration, status: "ready" }
|
|
114364
|
+
};
|
|
114365
|
+
} catch (err2) {
|
|
114366
|
+
const error51 = shortError(err2);
|
|
114367
|
+
const at3 = now().toISOString();
|
|
114368
|
+
const updated = await deps.store.update(questionId, {
|
|
114369
|
+
status: "failed",
|
|
114370
|
+
error: error51,
|
|
114371
|
+
updatedAt: at3
|
|
114372
|
+
});
|
|
114373
|
+
deps.logger?.warn?.({ questionId, derivedScope: registration.derivedScope, error: error51 }, "Derivative question compute failed");
|
|
114374
|
+
return {
|
|
114375
|
+
status: "failed",
|
|
114376
|
+
registration: updated ?? { ...registration, status: "failed", error: error51 },
|
|
114377
|
+
error: error51
|
|
114378
|
+
};
|
|
114379
|
+
}
|
|
114380
|
+
}
|
|
114381
|
+
|
|
114382
|
+
// ../core/dist/derivatives/scheduler.js
|
|
114383
|
+
var defaultTimers = {
|
|
114384
|
+
setTimeout: (callback, ms3) => setTimeout(callback, ms3),
|
|
114385
|
+
clearTimeout: (handle) => clearTimeout(handle)
|
|
114386
|
+
};
|
|
114387
|
+
function createRecomputeScheduler(options) {
|
|
114388
|
+
const debounceMs = options.debounceMs ?? 5e3;
|
|
114389
|
+
const timers = options.timers ?? defaultTimers;
|
|
114390
|
+
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
114391
|
+
const states = /* @__PURE__ */ new Map();
|
|
114392
|
+
const pending = /* @__PURE__ */ new Set();
|
|
114393
|
+
let stopped = false;
|
|
114394
|
+
function track(promise2) {
|
|
114395
|
+
pending.add(promise2);
|
|
114396
|
+
const done = () => pending.delete(promise2);
|
|
114397
|
+
promise2.then(done, done);
|
|
114398
|
+
return promise2;
|
|
114399
|
+
}
|
|
114400
|
+
function warn(payload, message) {
|
|
114401
|
+
options.logger?.warn?.(payload, message);
|
|
114402
|
+
}
|
|
114403
|
+
function stateFor(questionId) {
|
|
114404
|
+
let state = states.get(questionId);
|
|
114405
|
+
if (!state) {
|
|
114406
|
+
state = { timer: null, running: null, rerun: false };
|
|
114407
|
+
states.set(questionId, state);
|
|
114408
|
+
}
|
|
114409
|
+
return state;
|
|
114410
|
+
}
|
|
114411
|
+
function run(questionId) {
|
|
114412
|
+
if (stopped)
|
|
114413
|
+
return;
|
|
114414
|
+
const state = stateFor(questionId);
|
|
114415
|
+
if (state.running) {
|
|
114416
|
+
state.rerun = true;
|
|
114417
|
+
return;
|
|
114418
|
+
}
|
|
114419
|
+
state.running = track(Promise.resolve().then(() => options.compute(questionId)).then(() => void 0, (err2) => warn({
|
|
114420
|
+
questionId,
|
|
114421
|
+
error: err2 instanceof Error ? err2.name : String(err2)
|
|
114422
|
+
}, "Derivative compute threw")).then(async () => {
|
|
114423
|
+
state.running = null;
|
|
114424
|
+
if (state.rerun) {
|
|
114425
|
+
state.rerun = false;
|
|
114426
|
+
await markStale(questionId);
|
|
114427
|
+
schedule(questionId, 0);
|
|
114428
|
+
} else if (!states.get(questionId)?.timer) {
|
|
114429
|
+
states.delete(questionId);
|
|
114430
|
+
}
|
|
114431
|
+
}));
|
|
114432
|
+
}
|
|
114433
|
+
function schedule(questionId, delayMs) {
|
|
114434
|
+
if (stopped)
|
|
114435
|
+
return;
|
|
114436
|
+
const state = stateFor(questionId);
|
|
114437
|
+
if (state.timer !== null)
|
|
114438
|
+
timers.clearTimeout(state.timer);
|
|
114439
|
+
state.timer = timers.setTimeout(() => {
|
|
114440
|
+
state.timer = null;
|
|
114441
|
+
run(questionId);
|
|
114442
|
+
}, delayMs);
|
|
114443
|
+
}
|
|
114444
|
+
async function markStale(questionId) {
|
|
114445
|
+
const registration = await options.store.get(questionId);
|
|
114446
|
+
if (!registration)
|
|
114447
|
+
return;
|
|
114448
|
+
if (registration.status === "ready" || registration.status === "failed") {
|
|
114449
|
+
await options.store.update(questionId, {
|
|
114450
|
+
status: "stale",
|
|
114451
|
+
updatedAt: now().toISOString()
|
|
114452
|
+
});
|
|
114453
|
+
}
|
|
114454
|
+
}
|
|
114455
|
+
return {
|
|
114456
|
+
markSourceChanged(scope, opts) {
|
|
114457
|
+
if (stopped)
|
|
114458
|
+
return;
|
|
114459
|
+
const lineage = new Set((opts?.lineageSources ?? []).map((id2) => id2.toLowerCase()));
|
|
114460
|
+
void track((async () => {
|
|
114461
|
+
const affected = await options.store.list({ sourceScope: scope });
|
|
114462
|
+
for (const registration of affected) {
|
|
114463
|
+
if (options.serverOwner && lineage.has(computeDataPointId(options.serverOwner, registration.derivedScope))) {
|
|
114464
|
+
continue;
|
|
114465
|
+
}
|
|
114466
|
+
await markStale(registration.questionId);
|
|
114467
|
+
schedule(registration.questionId, debounceMs);
|
|
114468
|
+
}
|
|
114469
|
+
})().catch((err2) => warn({ scope, error: err2 instanceof Error ? err2.name : String(err2) }, "Could not mark derivative questions stale")));
|
|
114470
|
+
},
|
|
114471
|
+
requestRecompute(questionId, opts) {
|
|
114472
|
+
if (stopped)
|
|
114473
|
+
return;
|
|
114474
|
+
void track(markStale(questionId).catch((err2) => warn({
|
|
114475
|
+
questionId,
|
|
114476
|
+
error: err2 instanceof Error ? err2.name : String(err2)
|
|
114477
|
+
}, "Could not mark derivative question stale")).then(() => schedule(questionId, opts?.immediate ? 0 : debounceMs)));
|
|
114478
|
+
},
|
|
114479
|
+
async whenIdle() {
|
|
114480
|
+
const waitForTimers = !options.timers;
|
|
114481
|
+
for (; ; ) {
|
|
114482
|
+
while (pending.size > 0) {
|
|
114483
|
+
await Promise.allSettled([...pending]);
|
|
114484
|
+
}
|
|
114485
|
+
const busy = [...states.values()].some((state) => state.running !== null || waitForTimers && state.timer !== null);
|
|
114486
|
+
if (!busy)
|
|
114487
|
+
return;
|
|
114488
|
+
await new Promise((resolve) => setTimeout(resolve, 5));
|
|
114489
|
+
}
|
|
114490
|
+
},
|
|
114491
|
+
stop() {
|
|
114492
|
+
stopped = true;
|
|
114493
|
+
for (const state of states.values()) {
|
|
114494
|
+
if (state.timer !== null)
|
|
114495
|
+
timers.clearTimeout(state.timer);
|
|
114496
|
+
state.timer = null;
|
|
114497
|
+
}
|
|
114498
|
+
},
|
|
114499
|
+
start() {
|
|
114500
|
+
if (!stopped)
|
|
114501
|
+
return;
|
|
114502
|
+
stopped = false;
|
|
114503
|
+
void track((async () => {
|
|
114504
|
+
for (const registration of await options.store.list()) {
|
|
114505
|
+
if (registration.status === "pending" || registration.status === "stale") {
|
|
114506
|
+
schedule(registration.questionId, 0);
|
|
114507
|
+
}
|
|
114508
|
+
}
|
|
114509
|
+
})().catch((err2) => warn({ error: err2 instanceof Error ? err2.name : String(err2) }, "Could not reschedule derivative questions")));
|
|
114510
|
+
}
|
|
114511
|
+
};
|
|
114512
|
+
}
|
|
114513
|
+
|
|
114514
|
+
// ../core/dist/derivatives/api.js
|
|
114515
|
+
var MAX_QUESTION_BODY_BYTES = 16 * 1024;
|
|
114516
|
+
function jsonResponse2(body, init) {
|
|
114517
|
+
const headers = new Headers(init?.headers);
|
|
114518
|
+
headers.set("Content-Type", "application/json");
|
|
114519
|
+
return new Response(JSON.stringify(body), { ...init, headers });
|
|
114520
|
+
}
|
|
114521
|
+
function errorResponse2(status2, errorCode, message) {
|
|
114522
|
+
return jsonResponse2({ error: { code: status2, errorCode, message } }, { status: status2 });
|
|
114523
|
+
}
|
|
114524
|
+
function stripBasePath2(pathname, basePath) {
|
|
114525
|
+
if (!basePath || basePath === "/")
|
|
114526
|
+
return pathname;
|
|
114527
|
+
if (pathname === basePath)
|
|
114528
|
+
return "/";
|
|
114529
|
+
if (pathname.startsWith(`${basePath}/`))
|
|
114530
|
+
return pathname.slice(basePath.length);
|
|
114531
|
+
return pathname;
|
|
114532
|
+
}
|
|
114533
|
+
async function authorizeOwnerOrWriter(deps, request2, scope) {
|
|
114534
|
+
if (deps.auth.authorizeWrite) {
|
|
114535
|
+
return await deps.auth.authorizeWrite({ request: request2, scope }) ?? void 0;
|
|
114536
|
+
}
|
|
114537
|
+
await deps.auth.authorizeOwner(request2);
|
|
114538
|
+
return void 0;
|
|
114539
|
+
}
|
|
114540
|
+
function sameBuilder(a10, b10) {
|
|
114541
|
+
return a10.toLowerCase() === b10.toLowerCase();
|
|
114542
|
+
}
|
|
114543
|
+
async function loadForCaller(deps, request2, store, questionId) {
|
|
114544
|
+
const registration = await store.get(questionId);
|
|
114545
|
+
if (!registration) {
|
|
114546
|
+
await deps.auth.authorizeOwner(request2);
|
|
114547
|
+
throw new DerivativeQuestionNotFoundError({ questionId });
|
|
114548
|
+
}
|
|
114549
|
+
const writer = await authorizeOwnerOrWriter(deps, request2, registration.derivedScope);
|
|
114550
|
+
if (writer) {
|
|
114551
|
+
const by2 = registration.registeredBy;
|
|
114552
|
+
if (by2.kind !== "builder" || !sameBuilder(by2.builder, writer.builder)) {
|
|
114553
|
+
await writer.releaseProof?.();
|
|
114554
|
+
throw new DerivativeQuestionNotFoundError({ questionId });
|
|
114555
|
+
}
|
|
114556
|
+
}
|
|
114557
|
+
return { registration, writer };
|
|
114558
|
+
}
|
|
114559
|
+
async function handlePersonalServerDerivativesRequest(request2, deps, options = {}) {
|
|
114560
|
+
try {
|
|
114561
|
+
const url2 = new URL(request2.url);
|
|
114562
|
+
const pathname = stripBasePath2(url2.pathname, options.basePath);
|
|
114563
|
+
const parts = pathname.split("/").filter(Boolean);
|
|
114564
|
+
if (parts[0] !== "questions" || parts.length > 3) {
|
|
114565
|
+
return errorResponse2(404, "NOT_FOUND", "Not found");
|
|
114566
|
+
}
|
|
114567
|
+
const compute = deps.compute;
|
|
114568
|
+
if (!compute)
|
|
114569
|
+
throw new DerivativeComputeUnavailableError();
|
|
114570
|
+
const { store, scheduler } = compute;
|
|
114571
|
+
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
114572
|
+
if (parts.length === 1) {
|
|
114573
|
+
if (request2.method === "GET") {
|
|
114574
|
+
const derivedScope = url2.searchParams.get("derivedScope") ?? void 0;
|
|
114575
|
+
if (derivedScope) {
|
|
114576
|
+
const writer = await authorizeOwnerOrWriter(deps, request2, derivedScope);
|
|
114577
|
+
const registrations2 = await store.list({
|
|
114578
|
+
derivedScope,
|
|
114579
|
+
...writer ? { builder: writer.builder } : {}
|
|
114580
|
+
});
|
|
114581
|
+
return jsonResponse2({
|
|
114582
|
+
questions: registrations2.map(questionRegistrationView)
|
|
114583
|
+
});
|
|
114584
|
+
}
|
|
114585
|
+
await deps.auth.authorizeOwner(request2);
|
|
114586
|
+
const registrations = await store.list();
|
|
114587
|
+
return jsonResponse2({
|
|
114588
|
+
questions: registrations.map(questionRegistrationView)
|
|
114589
|
+
});
|
|
114590
|
+
}
|
|
114591
|
+
if (request2.method === "POST") {
|
|
114592
|
+
const declared = Number(request2.headers.get("content-length") ?? "0");
|
|
114593
|
+
if (declared > MAX_QUESTION_BODY_BYTES) {
|
|
114594
|
+
throw new ContentTooLargeError({ max: MAX_QUESTION_BODY_BYTES });
|
|
114595
|
+
}
|
|
114596
|
+
const bodyBytes = new Uint8Array(await request2.clone().arrayBuffer());
|
|
114597
|
+
if (bodyBytes.byteLength > MAX_QUESTION_BODY_BYTES) {
|
|
114598
|
+
throw new ContentTooLargeError({ max: MAX_QUESTION_BODY_BYTES });
|
|
114599
|
+
}
|
|
114600
|
+
const parsed = await parseJsonObjectBody(request2.clone(), "Request body must be valid JSON");
|
|
114601
|
+
if (!parsed.ok) {
|
|
114602
|
+
return jsonResponse2(parsed.result.body, {
|
|
114603
|
+
status: parsed.result.status
|
|
114604
|
+
});
|
|
114605
|
+
}
|
|
114606
|
+
const rawScope = parsed.body.derivedScope;
|
|
114607
|
+
const scopeForAuth = typeof rawScope === "string" ? rawScope : "";
|
|
114608
|
+
const writer = await authorizeOwnerOrWriter(deps, request2, scopeForAuth);
|
|
114609
|
+
const registeredBy = writer ? {
|
|
114610
|
+
kind: "builder",
|
|
114611
|
+
builder: writer.builder,
|
|
114612
|
+
grantId: writer.grantId
|
|
114613
|
+
} : { kind: "owner" };
|
|
114614
|
+
let registration;
|
|
114615
|
+
try {
|
|
114616
|
+
if (writer) {
|
|
114617
|
+
const input = parseQuestionInput(parsed.body);
|
|
114618
|
+
const uncovered = uncoveredSourceScopes(input.sourceScopes, writer.grantScopes);
|
|
114619
|
+
if (uncovered.length > 0) {
|
|
114620
|
+
throw new DerivativeSourceNotGrantedError({ scopes: uncovered });
|
|
114621
|
+
}
|
|
114622
|
+
}
|
|
114623
|
+
registration = await createQuestionRegistration({
|
|
114624
|
+
body: parsed.body,
|
|
114625
|
+
registeredBy,
|
|
114626
|
+
store,
|
|
114627
|
+
questionId: deps.createQuestionId?.() ?? crypto.randomUUID(),
|
|
114628
|
+
now
|
|
114629
|
+
});
|
|
114630
|
+
} catch (err2) {
|
|
114631
|
+
await writer?.releaseProof?.();
|
|
114632
|
+
throw err2;
|
|
114633
|
+
}
|
|
114634
|
+
deps.logger?.info?.({
|
|
114635
|
+
questionId: registration.questionId,
|
|
114636
|
+
derivedScope: registration.derivedScope,
|
|
114637
|
+
sourceScopes: registration.sourceScopes,
|
|
114638
|
+
registeredBy: registeredBy.kind,
|
|
114639
|
+
...writer ? { builder: writer.builder, grantId: writer.grantId } : {}
|
|
114640
|
+
}, "Derivative question registered");
|
|
114641
|
+
scheduler.requestRecompute(registration.questionId, {
|
|
114642
|
+
immediate: true
|
|
114643
|
+
});
|
|
114644
|
+
return jsonResponse2(questionRegistrationView(registration), {
|
|
114645
|
+
status: 201
|
|
114646
|
+
});
|
|
114647
|
+
}
|
|
114648
|
+
return errorResponse2(405, "METHOD_NOT_ALLOWED", "Method not allowed");
|
|
114649
|
+
}
|
|
114650
|
+
const questionId = decodeURIComponent(parts[1] ?? "");
|
|
114651
|
+
if (parts.length === 3) {
|
|
114652
|
+
if (parts[2] !== "recompute") {
|
|
114653
|
+
return errorResponse2(404, "NOT_FOUND", "Not found");
|
|
114654
|
+
}
|
|
114655
|
+
if (request2.method !== "POST") {
|
|
114656
|
+
return errorResponse2(405, "METHOD_NOT_ALLOWED", "Method not allowed");
|
|
114657
|
+
}
|
|
114658
|
+
const { registration } = await loadForCaller(deps, request2, store, questionId);
|
|
114659
|
+
scheduler.requestRecompute(questionId, { immediate: true });
|
|
114660
|
+
return jsonResponse2({
|
|
114661
|
+
questionId,
|
|
114662
|
+
status: registration.status === "pending" ? "pending" : "stale",
|
|
114663
|
+
derivedScope: registration.derivedScope
|
|
114664
|
+
}, { status: 202 });
|
|
114665
|
+
}
|
|
114666
|
+
if (request2.method === "GET") {
|
|
114667
|
+
const { registration } = await loadForCaller(deps, request2, store, questionId);
|
|
114668
|
+
return jsonResponse2(questionRegistrationView(registration));
|
|
114669
|
+
}
|
|
114670
|
+
if (request2.method === "DELETE") {
|
|
114671
|
+
const { registration } = await loadForCaller(deps, request2, store, questionId);
|
|
114672
|
+
await store.delete(questionId);
|
|
114673
|
+
deps.logger?.info?.({ questionId, derivedScope: registration.derivedScope }, "Derivative question deleted");
|
|
114674
|
+
return jsonResponse2({ questionId, deleted: true });
|
|
114675
|
+
}
|
|
114676
|
+
return errorResponse2(405, "METHOD_NOT_ALLOWED", "Method not allowed");
|
|
114677
|
+
} catch (err2) {
|
|
114678
|
+
if (err2 instanceof ProtocolError) {
|
|
114679
|
+
return jsonResponse2(err2.toJSON(), { status: err2.code });
|
|
114680
|
+
}
|
|
114681
|
+
return errorResponse2(500, "INTERNAL_ERROR", "Internal server error");
|
|
114682
|
+
}
|
|
114683
|
+
}
|
|
114684
|
+
|
|
114685
|
+
// ../core/dist/derivatives/e2ee/jcs.js
|
|
114686
|
+
function canonicalizeJson(value) {
|
|
114687
|
+
if (value === null)
|
|
114688
|
+
return "null";
|
|
114689
|
+
switch (typeof value) {
|
|
114690
|
+
case "boolean":
|
|
114691
|
+
return value ? "true" : "false";
|
|
114692
|
+
case "number":
|
|
114693
|
+
if (!Number.isFinite(value)) {
|
|
114694
|
+
throw new TypeError("JCS: non-finite number");
|
|
114695
|
+
}
|
|
114696
|
+
return JSON.stringify(value);
|
|
114697
|
+
case "string":
|
|
114698
|
+
return JSON.stringify(value);
|
|
114699
|
+
case "object": {
|
|
114700
|
+
if (Array.isArray(value)) {
|
|
114701
|
+
return `[${value.map((item) => canonicalizeJson(item)).join(",")}]`;
|
|
114702
|
+
}
|
|
114703
|
+
const record2 = value;
|
|
114704
|
+
const keys = Object.keys(record2).sort();
|
|
114705
|
+
const members = [];
|
|
114706
|
+
for (const key of keys) {
|
|
114707
|
+
const member = record2[key];
|
|
114708
|
+
if (member === void 0) {
|
|
114709
|
+
throw new TypeError(`JCS: undefined member "${key}"`);
|
|
114710
|
+
}
|
|
114711
|
+
members.push(`${JSON.stringify(key)}:${canonicalizeJson(member)}`);
|
|
114712
|
+
}
|
|
114713
|
+
return `{${members.join(",")}}`;
|
|
114714
|
+
}
|
|
114715
|
+
default:
|
|
114716
|
+
throw new TypeError(`JCS: unsupported value of type ${typeof value}`);
|
|
114717
|
+
}
|
|
114718
|
+
}
|
|
114719
|
+
var encoder3 = new TextEncoder();
|
|
114720
|
+
function canonicalJsonBytes(value) {
|
|
114721
|
+
return encoder3.encode(canonicalizeJson(value));
|
|
114722
|
+
}
|
|
114723
|
+
|
|
114724
|
+
// ../core/dist/derivatives/e2ee/aad.js
|
|
114725
|
+
var E2EE_REQUEST_AAD_PURPOSE = "aci.e2ee.request.v2";
|
|
114726
|
+
var E2EE_RESPONSE_AAD_PURPOSE = "aci.e2ee.response.v2";
|
|
114727
|
+
function requestFieldAad(context, field) {
|
|
114728
|
+
return canonicalJsonBytes({
|
|
114729
|
+
purpose: E2EE_REQUEST_AAD_PURPOSE,
|
|
114730
|
+
algo: context.algo,
|
|
114731
|
+
model: context.model,
|
|
114732
|
+
field,
|
|
114733
|
+
nonce: context.nonce,
|
|
114734
|
+
ts: context.ts
|
|
114735
|
+
});
|
|
114736
|
+
}
|
|
114737
|
+
function responseFieldAad(context, field, id2) {
|
|
114738
|
+
return canonicalJsonBytes({
|
|
114739
|
+
purpose: E2EE_RESPONSE_AAD_PURPOSE,
|
|
114740
|
+
algo: context.algo,
|
|
114741
|
+
model: context.model,
|
|
114742
|
+
id: id2,
|
|
114743
|
+
field,
|
|
114744
|
+
nonce: context.nonce,
|
|
114745
|
+
ts: context.ts
|
|
114746
|
+
});
|
|
114747
|
+
}
|
|
114748
|
+
|
|
114749
|
+
// ../core/dist/derivatives/e2ee/suite.js
|
|
114750
|
+
var E2EE_ALGO_X25519 = "x25519-aes-256-gcm-hkdf-sha256";
|
|
114751
|
+
var E2EE_HKDF_INFO_X25519 = "aci.e2ee.v2.x25519";
|
|
114752
|
+
var X25519_PUBLIC_KEY_BYTES = 32;
|
|
114753
|
+
var AES_GCM_NONCE_BYTES = 12;
|
|
114754
|
+
var AES_GCM_TAG_BYTES = 16;
|
|
114755
|
+
var E2eeCipherError = class extends Error {
|
|
114756
|
+
constructor(message) {
|
|
114757
|
+
super(message);
|
|
114758
|
+
this.name = "E2eeCipherError";
|
|
114759
|
+
}
|
|
114760
|
+
};
|
|
114761
|
+
function subtle() {
|
|
114762
|
+
const api = globalThis.crypto?.subtle;
|
|
114763
|
+
if (!api)
|
|
114764
|
+
throw new E2eeCipherError("WebCrypto is not available");
|
|
114765
|
+
return api;
|
|
114766
|
+
}
|
|
114767
|
+
function randomBytes4(length) {
|
|
114768
|
+
const bytes2 = new Uint8Array(length);
|
|
114769
|
+
globalThis.crypto.getRandomValues(bytes2);
|
|
114770
|
+
return bytes2;
|
|
114771
|
+
}
|
|
114772
|
+
function bytesToHex6(bytes2) {
|
|
114773
|
+
let out = "";
|
|
114774
|
+
for (const byte of bytes2)
|
|
114775
|
+
out += byte.toString(16).padStart(2, "0");
|
|
114776
|
+
return out;
|
|
114777
|
+
}
|
|
114778
|
+
function hexToBytes4(hex3) {
|
|
114779
|
+
const clean2 = hex3.startsWith("0x") || hex3.startsWith("0X") ? hex3.slice(2) : hex3;
|
|
114780
|
+
if (clean2.length % 2 !== 0 || !/^[0-9a-fA-F]*$/.test(clean2)) {
|
|
114781
|
+
throw new E2eeCipherError("malformed hex");
|
|
114782
|
+
}
|
|
114783
|
+
const out = new Uint8Array(clean2.length / 2);
|
|
114784
|
+
for (let i10 = 0; i10 < out.length; i10 += 1) {
|
|
114785
|
+
out[i10] = parseInt(clean2.slice(i10 * 2, i10 * 2 + 2), 16);
|
|
114786
|
+
}
|
|
114787
|
+
return out;
|
|
114788
|
+
}
|
|
114789
|
+
function parseX25519PublicKey(hex3) {
|
|
114790
|
+
const bytes2 = hexToBytes4(hex3);
|
|
114791
|
+
if (bytes2.length !== X25519_PUBLIC_KEY_BYTES) {
|
|
114792
|
+
throw new E2eeCipherError("X25519 public key must be 32 bytes");
|
|
114793
|
+
}
|
|
114794
|
+
return bytes2;
|
|
114795
|
+
}
|
|
114796
|
+
function concat2(...parts) {
|
|
114797
|
+
const total = parts.reduce((sum, part) => sum + part.length, 0);
|
|
114798
|
+
const out = new Uint8Array(total);
|
|
114799
|
+
let offset = 0;
|
|
114800
|
+
for (const part of parts) {
|
|
114801
|
+
out.set(part, offset);
|
|
114802
|
+
offset += part.length;
|
|
114803
|
+
}
|
|
114804
|
+
return out;
|
|
114805
|
+
}
|
|
114806
|
+
function toBuffer(bytes2) {
|
|
114807
|
+
const out = new ArrayBuffer(bytes2.byteLength);
|
|
114808
|
+
new Uint8Array(out).set(bytes2);
|
|
114809
|
+
return out;
|
|
114810
|
+
}
|
|
114811
|
+
async function generateX25519KeyPair() {
|
|
114812
|
+
const pair = await subtle().generateKey({ name: "X25519" }, false, [
|
|
114813
|
+
"deriveBits"
|
|
114814
|
+
]);
|
|
114815
|
+
const raw = await subtle().exportKey("raw", pair.publicKey);
|
|
114816
|
+
return { privateKey: pair.privateKey, publicKey: new Uint8Array(raw) };
|
|
114817
|
+
}
|
|
114818
|
+
async function deriveFieldKey(privateKey, peerPublicKey, usage) {
|
|
114819
|
+
const api = subtle();
|
|
114820
|
+
const peer = await api.importKey("raw", toBuffer(peerPublicKey), { name: "X25519" }, false, []);
|
|
114821
|
+
const shared = await api.deriveBits({ name: "X25519", public: peer }, privateKey, X25519_PUBLIC_KEY_BYTES * 8);
|
|
114822
|
+
const ikm = await api.importKey("raw", shared, "HKDF", false, ["deriveKey"]);
|
|
114823
|
+
return api.deriveKey({
|
|
114824
|
+
name: "HKDF",
|
|
114825
|
+
hash: "SHA-256",
|
|
114826
|
+
salt: new Uint8Array(0),
|
|
114827
|
+
info: new TextEncoder().encode(E2EE_HKDF_INFO_X25519)
|
|
114828
|
+
}, ikm, { name: "AES-GCM", length: 256 }, false, [usage]);
|
|
114829
|
+
}
|
|
114830
|
+
async function encryptField(input) {
|
|
114831
|
+
const ephemeral = input.ephemeral ?? await generateX25519KeyPair();
|
|
114832
|
+
const nonce = input.nonce ?? randomBytes4(AES_GCM_NONCE_BYTES);
|
|
114833
|
+
if (nonce.length !== AES_GCM_NONCE_BYTES) {
|
|
114834
|
+
throw new E2eeCipherError("AES-GCM nonce must be 12 bytes");
|
|
114835
|
+
}
|
|
114836
|
+
const key = await deriveFieldKey(ephemeral.privateKey, input.recipientPublicKey, "encrypt");
|
|
114837
|
+
const sealed = await subtle().encrypt({
|
|
114838
|
+
name: "AES-GCM",
|
|
114839
|
+
iv: toBuffer(nonce),
|
|
114840
|
+
additionalData: toBuffer(input.aad),
|
|
114841
|
+
tagLength: AES_GCM_TAG_BYTES * 8
|
|
114842
|
+
}, key, new TextEncoder().encode(input.plaintext));
|
|
114843
|
+
return bytesToHex6(concat2(ephemeral.publicKey, nonce, new Uint8Array(sealed)));
|
|
114844
|
+
}
|
|
114845
|
+
async function decryptField(input) {
|
|
114846
|
+
let bytes2;
|
|
114847
|
+
try {
|
|
114848
|
+
bytes2 = hexToBytes4(input.wire);
|
|
114849
|
+
} catch {
|
|
114850
|
+
throw new E2eeCipherError("ciphertext is not hex");
|
|
114851
|
+
}
|
|
114852
|
+
const minimum = X25519_PUBLIC_KEY_BYTES + AES_GCM_NONCE_BYTES + AES_GCM_TAG_BYTES;
|
|
114853
|
+
if (bytes2.length < minimum) {
|
|
114854
|
+
throw new E2eeCipherError("ciphertext is too short");
|
|
114855
|
+
}
|
|
114856
|
+
const ephemeralPublicKey = bytes2.subarray(0, X25519_PUBLIC_KEY_BYTES);
|
|
114857
|
+
const nonce = bytes2.subarray(X25519_PUBLIC_KEY_BYTES, X25519_PUBLIC_KEY_BYTES + AES_GCM_NONCE_BYTES);
|
|
114858
|
+
const sealed = bytes2.subarray(X25519_PUBLIC_KEY_BYTES + AES_GCM_NONCE_BYTES);
|
|
114859
|
+
let key;
|
|
114860
|
+
try {
|
|
114861
|
+
key = await deriveFieldKey(input.privateKey, ephemeralPublicKey, "decrypt");
|
|
114862
|
+
} catch {
|
|
114863
|
+
throw new E2eeCipherError("key agreement failed");
|
|
114864
|
+
}
|
|
114865
|
+
let opened;
|
|
114866
|
+
try {
|
|
114867
|
+
opened = await subtle().decrypt({
|
|
114868
|
+
name: "AES-GCM",
|
|
114869
|
+
iv: toBuffer(nonce),
|
|
114870
|
+
additionalData: toBuffer(input.aad),
|
|
114871
|
+
tagLength: AES_GCM_TAG_BYTES * 8
|
|
114872
|
+
}, key, toBuffer(sealed));
|
|
114873
|
+
} catch {
|
|
114874
|
+
throw new E2eeCipherError("authentication failed");
|
|
114875
|
+
}
|
|
114876
|
+
return new TextDecoder().decode(opened);
|
|
114877
|
+
}
|
|
114878
|
+
|
|
114879
|
+
// ../core/dist/derivatives/e2ee/attestation.js
|
|
114880
|
+
var ACI_API_VERSION = "aci/1";
|
|
114881
|
+
var ACI_REPORT_DATA_PURPOSE = "aci.report_data.v1";
|
|
114882
|
+
var E2EE_VERSION = "2";
|
|
114883
|
+
var PHALA_GATEWAY_BASE_URL = "https://inference.phala.com/v1";
|
|
114884
|
+
var MAX_KEYSET_LIFETIME_S = 400 * 24 * 3600;
|
|
114885
|
+
var DEFAULT_FETCH_TIMEOUT_MS = 15e3;
|
|
114886
|
+
var E2eeAttestationError = class extends Error {
|
|
114887
|
+
code;
|
|
114888
|
+
constructor(code, message) {
|
|
114889
|
+
super(message);
|
|
114890
|
+
this.code = code;
|
|
114891
|
+
this.name = "E2eeAttestationError";
|
|
114892
|
+
}
|
|
114893
|
+
};
|
|
114894
|
+
function isRecord7(value) {
|
|
114895
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
114896
|
+
}
|
|
114897
|
+
async function sha256Hex2(bytes2) {
|
|
114898
|
+
const buffer = new ArrayBuffer(bytes2.byteLength);
|
|
114899
|
+
new Uint8Array(buffer).set(bytes2);
|
|
114900
|
+
const digest = await globalThis.crypto.subtle.digest("SHA-256", buffer);
|
|
114901
|
+
return bytesToHex6(new Uint8Array(digest));
|
|
114902
|
+
}
|
|
114903
|
+
async function workloadKeysetDigest(keyset) {
|
|
114904
|
+
return `sha256:${await sha256Hex2(canonicalJsonBytes(keyset))}`;
|
|
114905
|
+
}
|
|
114906
|
+
async function reportDataFor(keysetDigest, nonce) {
|
|
114907
|
+
const nonceJson = nonce === null ? "null" : `"${nonce}"`;
|
|
114908
|
+
const statement = `{"keyset_digest":"${keysetDigest}","nonce":${nonceJson},"purpose":"${ACI_REPORT_DATA_PURPOSE}"}`;
|
|
114909
|
+
return sha256Hex2(new TextEncoder().encode(statement));
|
|
114910
|
+
}
|
|
114911
|
+
function parseReport(body) {
|
|
114912
|
+
if (!isRecord7(body) || typeof body.api_version !== "string" || typeof body.workload_keyset_digest !== "string" || !isRecord7(body.attestation) || typeof body.attestation.tee_type !== "string" || typeof body.attestation.report_data !== "string" || !isRecord7(body.attestation.workload_keyset)) {
|
|
114913
|
+
throw new E2eeAttestationError("malformed_report", "attestation report is missing required fields");
|
|
114914
|
+
}
|
|
114915
|
+
const keyset = body.attestation.workload_keyset;
|
|
114916
|
+
if (typeof keyset.not_after !== "number" || !Array.isArray(keyset.e2ee_public_keys)) {
|
|
114917
|
+
throw new E2eeAttestationError("malformed_report", "workload keyset is missing not_after or e2ee_public_keys");
|
|
114918
|
+
}
|
|
114919
|
+
return body;
|
|
114920
|
+
}
|
|
114921
|
+
async function verifyAciReportBinding(report, nonce, nowSeconds) {
|
|
114922
|
+
if (report.api_version !== ACI_API_VERSION) {
|
|
114923
|
+
throw new E2eeAttestationError("malformed_report", `unsupported attestation api_version ${report.api_version}`);
|
|
114924
|
+
}
|
|
114925
|
+
if (report.attestation.tee_type !== "tdx") {
|
|
114926
|
+
throw new E2eeAttestationError("malformed_report", `unsupported tee_type ${report.attestation.tee_type}`);
|
|
114927
|
+
}
|
|
114928
|
+
const versions = report.service_capabilities?.supported_e2ee_versions;
|
|
114929
|
+
if (!Array.isArray(versions) || !versions.includes(E2EE_VERSION)) {
|
|
114930
|
+
throw new E2eeAttestationError("e2ee_unsupported", "service does not advertise E2EE version 2");
|
|
114931
|
+
}
|
|
114932
|
+
const keyset = report.attestation.workload_keyset;
|
|
114933
|
+
const digest = await workloadKeysetDigest(keyset);
|
|
114934
|
+
if (digest !== report.workload_keyset_digest) {
|
|
114935
|
+
throw new E2eeAttestationError("keyset_digest_mismatch", "workload_keyset_digest does not match the embedded keyset");
|
|
114936
|
+
}
|
|
114937
|
+
const expectedReportData = await reportDataFor(digest, nonce);
|
|
114938
|
+
const reportData = report.attestation.report_data.toLowerCase();
|
|
114939
|
+
if (reportData !== expectedReportData) {
|
|
114940
|
+
throw new E2eeAttestationError("report_data_mismatch", "report_data does not bind the keyset digest and our nonce");
|
|
114941
|
+
}
|
|
114942
|
+
const quoteReportData = report.attestation.evidence?.quote_report_data;
|
|
114943
|
+
if (typeof quoteReportData === "string") {
|
|
114944
|
+
const slot = quoteReportData.toLowerCase();
|
|
114945
|
+
if (slot.length !== 128 || !slot.startsWith(reportData) || !/^0+$/.test(slot.slice(64))) {
|
|
114946
|
+
throw new E2eeAttestationError("report_data_mismatch", "quote report-data slot does not carry report_data zero-padded");
|
|
114947
|
+
}
|
|
114948
|
+
}
|
|
114949
|
+
if (!(nowSeconds < keyset.not_after)) {
|
|
114950
|
+
throw new E2eeAttestationError("keyset_expired", "workload keyset has expired");
|
|
114951
|
+
}
|
|
114952
|
+
if (keyset.not_after - nowSeconds > MAX_KEYSET_LIFETIME_S) {
|
|
114953
|
+
throw new E2eeAttestationError("keyset_expired", "workload keyset not_after is implausibly far in the future");
|
|
114954
|
+
}
|
|
114955
|
+
return digest;
|
|
114956
|
+
}
|
|
114957
|
+
function selectX25519Key(keyset, expectedKeyId) {
|
|
114958
|
+
for (const entry of keyset.e2ee_public_keys) {
|
|
114959
|
+
if (!isRecord7(entry) || entry.algo !== E2EE_ALGO_X25519)
|
|
114960
|
+
continue;
|
|
114961
|
+
if (typeof entry.public_key !== "string")
|
|
114962
|
+
continue;
|
|
114963
|
+
if (expectedKeyId !== void 0 && entry.key_id !== expectedKeyId) {
|
|
114964
|
+
continue;
|
|
114965
|
+
}
|
|
114966
|
+
let publicKey;
|
|
114967
|
+
try {
|
|
114968
|
+
publicKey = parseX25519PublicKey(entry.public_key);
|
|
114969
|
+
} catch {
|
|
114970
|
+
throw new E2eeAttestationError("no_e2ee_key", "the X25519 E2EE key in the keyset is malformed");
|
|
114971
|
+
}
|
|
114972
|
+
return { entry, publicKey };
|
|
114973
|
+
}
|
|
114974
|
+
throw new E2eeAttestationError("no_e2ee_key", expectedKeyId ? `keyset has no ${E2EE_ALGO_X25519} key with key_id ${expectedKeyId}` : `keyset has no ${E2EE_ALGO_X25519} key`);
|
|
114975
|
+
}
|
|
114976
|
+
function attestationUrl(baseUrl, nonce) {
|
|
114977
|
+
return `${baseUrl.replace(/\/+$/, "")}/aci/attestation?nonce=${nonce}`;
|
|
114978
|
+
}
|
|
114979
|
+
async function fetchReport(doFetch, url2, timeoutMs) {
|
|
114980
|
+
let response;
|
|
114981
|
+
try {
|
|
114982
|
+
response = await doFetch(url2, {
|
|
114983
|
+
method: "GET",
|
|
114984
|
+
headers: { Accept: "application/json" },
|
|
114985
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
114986
|
+
});
|
|
114987
|
+
} catch (err2) {
|
|
114988
|
+
const name = err2 instanceof Error ? err2.name : "Error";
|
|
114989
|
+
throw new E2eeAttestationError("fetch_failed", `attestation fetch failed before a response (${name})`);
|
|
114990
|
+
}
|
|
114991
|
+
if (!response.ok) {
|
|
114992
|
+
return { status: response.status, body: null };
|
|
114993
|
+
}
|
|
114994
|
+
try {
|
|
114995
|
+
return { status: response.status, body: await response.json() };
|
|
114996
|
+
} catch {
|
|
114997
|
+
throw new E2eeAttestationError("malformed_report", "attestation response was not JSON");
|
|
114998
|
+
}
|
|
114999
|
+
}
|
|
115000
|
+
async function fetchGatewayE2eeKey(options) {
|
|
115001
|
+
const doFetch = options.fetch ?? fetch;
|
|
115002
|
+
const clock = options.clock ?? Date.now;
|
|
115003
|
+
const random = options.random ?? randomBytes4;
|
|
115004
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS;
|
|
115005
|
+
const nonce = bytesToHex6(random(32));
|
|
115006
|
+
const fallback = options.fallbackBaseUrl === void 0 ? PHALA_GATEWAY_BASE_URL : options.fallbackBaseUrl;
|
|
115007
|
+
let source = options.baseUrl;
|
|
115008
|
+
let fetched = await fetchReport(doFetch, attestationUrl(source, nonce), timeoutMs);
|
|
115009
|
+
if ((fetched.status === 404 || fetched.status === 405) && fallback !== null && fallback.replace(/\/+$/, "") !== source.replace(/\/+$/, "")) {
|
|
115010
|
+
source = fallback;
|
|
115011
|
+
fetched = await fetchReport(doFetch, attestationUrl(source, nonce), timeoutMs);
|
|
115012
|
+
}
|
|
115013
|
+
if (fetched.body === null) {
|
|
115014
|
+
throw new E2eeAttestationError("fetch_failed", `attestation fetch failed with status ${fetched.status}`);
|
|
115015
|
+
}
|
|
115016
|
+
const report = parseReport(fetched.body);
|
|
115017
|
+
const nowSeconds = Math.floor(clock() / 1e3);
|
|
115018
|
+
const keysetDigest = await verifyAciReportBinding(report, nonce, nowSeconds);
|
|
115019
|
+
const { entry, publicKey } = selectX25519Key(report.attestation.workload_keyset, options.expectedKeyId);
|
|
115020
|
+
if (options.verifyEvidence) {
|
|
115021
|
+
try {
|
|
115022
|
+
await options.verifyEvidence(report);
|
|
115023
|
+
} catch (err2) {
|
|
115024
|
+
const reason = err2 instanceof Error ? err2.message : String(err2);
|
|
115025
|
+
throw new E2eeAttestationError("evidence_rejected", `attestation evidence rejected: ${reason}`);
|
|
115026
|
+
}
|
|
115027
|
+
}
|
|
115028
|
+
return {
|
|
115029
|
+
algo: entry.algo,
|
|
115030
|
+
keyId: entry.key_id,
|
|
115031
|
+
publicKey,
|
|
115032
|
+
publicKeyHex: entry.public_key,
|
|
115033
|
+
keysetDigest,
|
|
115034
|
+
notAfter: report.attestation.workload_keyset.not_after,
|
|
115035
|
+
source,
|
|
115036
|
+
report
|
|
115037
|
+
};
|
|
115038
|
+
}
|
|
115039
|
+
|
|
115040
|
+
// ../core/dist/derivatives/e2ee/phala.js
|
|
115041
|
+
var E2EE_HEADER_VERSION = "X-E2EE-Version";
|
|
115042
|
+
var E2EE_HEADER_CLIENT_PUB_KEY = "X-Client-Pub-Key";
|
|
115043
|
+
var E2EE_HEADER_MODEL_PUB_KEY = "X-Model-Pub-Key";
|
|
115044
|
+
var E2EE_HEADER_NONCE = "X-E2EE-Nonce";
|
|
115045
|
+
var E2EE_HEADER_TIMESTAMP = "X-E2EE-Timestamp";
|
|
115046
|
+
var ACI_HEADER_KEYSET_DIGEST = "X-ACI-Keyset-Digest";
|
|
115047
|
+
var DEFAULT_E2EE_KEY_TTL_MS = 5 * 6e4;
|
|
115048
|
+
function keyFetchError(err2) {
|
|
115049
|
+
if (err2 instanceof E2eeAttestationError) {
|
|
115050
|
+
return new InferenceRequestError(`e2ee key fetch failed (${err2.code}): ${err2.message}`, null, {
|
|
115051
|
+
errorType: `e2ee_attestation_${err2.code}`,
|
|
115052
|
+
retryable: err2.code === "fetch_failed"
|
|
115053
|
+
});
|
|
115054
|
+
}
|
|
115055
|
+
const name = err2 instanceof Error ? err2.name : "Error";
|
|
115056
|
+
return new InferenceRequestError(`e2ee key fetch failed (${name})`, null, {
|
|
115057
|
+
retryable: false
|
|
115058
|
+
});
|
|
115059
|
+
}
|
|
115060
|
+
function createPhalaE2eeEncryption(options) {
|
|
115061
|
+
const clock = options.clock ?? Date.now;
|
|
115062
|
+
const ttlMs = options.keyTtlMs ?? DEFAULT_E2EE_KEY_TTL_MS;
|
|
115063
|
+
let cached2 = null;
|
|
115064
|
+
let inFlight = null;
|
|
115065
|
+
const fetchKey = async () => {
|
|
115066
|
+
const key = await fetchGatewayE2eeKey({
|
|
115067
|
+
baseUrl: options.baseUrl,
|
|
115068
|
+
fetch: options.fetch,
|
|
115069
|
+
clock,
|
|
115070
|
+
expectedKeyId: options.expectedKeyId,
|
|
115071
|
+
verifyEvidence: options.verifyEvidence,
|
|
115072
|
+
fallbackBaseUrl: options.fallbackAttestationBaseUrl
|
|
115073
|
+
});
|
|
115074
|
+
options.logger?.info?.({
|
|
115075
|
+
keyId: key.keyId,
|
|
115076
|
+
algo: key.algo,
|
|
115077
|
+
keysetDigest: key.keysetDigest,
|
|
115078
|
+
notAfter: key.notAfter,
|
|
115079
|
+
source: key.source,
|
|
115080
|
+
evidenceVerified: Boolean(options.verifyEvidence)
|
|
115081
|
+
}, "E2EE gateway key verified");
|
|
115082
|
+
return key;
|
|
115083
|
+
};
|
|
115084
|
+
const getKey = async () => {
|
|
115085
|
+
const now = clock();
|
|
115086
|
+
if (cached2 && now - cached2.fetchedAt < ttlMs && Math.floor(now / 1e3) < cached2.key.notAfter) {
|
|
115087
|
+
return cached2.key;
|
|
115088
|
+
}
|
|
115089
|
+
if (!inFlight) {
|
|
115090
|
+
inFlight = fetchKey().then((key) => {
|
|
115091
|
+
cached2 = { key, fetchedAt: clock() };
|
|
115092
|
+
return key;
|
|
115093
|
+
}).finally(() => {
|
|
115094
|
+
inFlight = null;
|
|
115095
|
+
});
|
|
115096
|
+
}
|
|
115097
|
+
try {
|
|
115098
|
+
return await inFlight;
|
|
115099
|
+
} catch (err2) {
|
|
115100
|
+
throw keyFetchError(err2);
|
|
115101
|
+
}
|
|
115102
|
+
};
|
|
115103
|
+
const invalidateKey = () => {
|
|
115104
|
+
cached2 = null;
|
|
115105
|
+
};
|
|
115106
|
+
return {
|
|
115107
|
+
invalidateKey,
|
|
115108
|
+
currentKey: () => cached2?.key ?? null,
|
|
115109
|
+
async encryptRequest({ model, messages, headers }) {
|
|
115110
|
+
if (typeof model !== "string" || model === "") {
|
|
115111
|
+
throw new InferenceRequestError("e2ee requires a non-empty request model", null, { errorType: "e2ee_invalid_payload_model", retryable: false });
|
|
115112
|
+
}
|
|
115113
|
+
const gatewayKey = await getKey();
|
|
115114
|
+
const client = await generateX25519KeyPair();
|
|
115115
|
+
const context = {
|
|
115116
|
+
algo: gatewayKey.algo,
|
|
115117
|
+
model,
|
|
115118
|
+
nonce: bytesToHex6(randomBytes4(32)),
|
|
115119
|
+
ts: Math.floor(clock() / 1e3)
|
|
115120
|
+
};
|
|
115121
|
+
headers.set(E2EE_HEADER_VERSION, "2");
|
|
115122
|
+
headers.set(E2EE_HEADER_CLIENT_PUB_KEY, bytesToHex6(client.publicKey));
|
|
115123
|
+
headers.set(E2EE_HEADER_MODEL_PUB_KEY, gatewayKey.publicKeyHex);
|
|
115124
|
+
headers.set(E2EE_HEADER_NONCE, context.nonce);
|
|
115125
|
+
headers.set(E2EE_HEADER_TIMESTAMP, String(context.ts));
|
|
115126
|
+
const encrypted = [];
|
|
115127
|
+
for (let index = 0; index < messages.length; index += 1) {
|
|
115128
|
+
const message = messages[index];
|
|
115129
|
+
let content;
|
|
115130
|
+
try {
|
|
115131
|
+
content = await encryptField({
|
|
115132
|
+
plaintext: message.content,
|
|
115133
|
+
recipientPublicKey: gatewayKey.publicKey,
|
|
115134
|
+
aad: requestFieldAad(context, `messages.${index}.content`)
|
|
115135
|
+
});
|
|
115136
|
+
} catch (err2) {
|
|
115137
|
+
const name = err2 instanceof Error ? err2.name : "Error";
|
|
115138
|
+
throw new InferenceRequestError(`e2ee request encryption failed (${name})`, null, { retryable: false });
|
|
115139
|
+
}
|
|
115140
|
+
encrypted.push({ ...message, content });
|
|
115141
|
+
}
|
|
115142
|
+
const request2 = {
|
|
115143
|
+
messages: encrypted,
|
|
115144
|
+
async decryptResponse({ content, field, id: id2, headers: response }) {
|
|
115145
|
+
const served = response.get(ACI_HEADER_KEYSET_DIGEST);
|
|
115146
|
+
if (served && served !== gatewayKey.keysetDigest) {
|
|
115147
|
+
options.logger?.warn?.({ pinned: gatewayKey.keysetDigest, served }, "E2EE keyset digest changed; refetching the attestation report");
|
|
115148
|
+
invalidateKey();
|
|
115149
|
+
}
|
|
115150
|
+
try {
|
|
115151
|
+
return await decryptField({
|
|
115152
|
+
wire: content,
|
|
115153
|
+
privateKey: client.privateKey,
|
|
115154
|
+
aad: responseFieldAad(context, field, id2)
|
|
115155
|
+
});
|
|
115156
|
+
} catch (err2) {
|
|
115157
|
+
const detail = err2 instanceof E2eeCipherError ? err2.message : "unexpected error";
|
|
115158
|
+
throw new InferenceRequestError(`e2ee response decryption failed for ${field} (${detail})`, null, { errorType: "e2ee_decryption_failed", retryable: false });
|
|
115159
|
+
}
|
|
115160
|
+
}
|
|
115161
|
+
};
|
|
115162
|
+
return request2;
|
|
115163
|
+
},
|
|
115164
|
+
async onRejected({ errorType }) {
|
|
115165
|
+
if (errorType === "e2ee_model_key_mismatch") {
|
|
115166
|
+
options.logger?.warn?.({ keyId: cached2?.key.keyId ?? null }, "E2EE model key rejected by the gateway; refetching the attestation report");
|
|
115167
|
+
invalidateKey();
|
|
115168
|
+
return true;
|
|
115169
|
+
}
|
|
115170
|
+
return false;
|
|
115171
|
+
}
|
|
115172
|
+
};
|
|
115173
|
+
}
|
|
115174
|
+
|
|
113385
115175
|
// ../core/dist/mcp/store.js
|
|
113386
115176
|
function createInMemoryMcpOAuthAuthorizationStore() {
|
|
113387
115177
|
const byId = /* @__PURE__ */ new Map();
|
|
@@ -124090,7 +125880,7 @@ var Protocol = class {
|
|
|
124090
125880
|
const capturedTransport = this._transport;
|
|
124091
125881
|
const relatedTaskId = request2.params?._meta?.[RELATED_TASK_META_KEY]?.taskId;
|
|
124092
125882
|
if (handler === void 0) {
|
|
124093
|
-
const
|
|
125883
|
+
const errorResponse4 = {
|
|
124094
125884
|
jsonrpc: "2.0",
|
|
124095
125885
|
id: request2.id,
|
|
124096
125886
|
error: {
|
|
@@ -124101,11 +125891,11 @@ var Protocol = class {
|
|
|
124101
125891
|
if (relatedTaskId && this._taskMessageQueue) {
|
|
124102
125892
|
this._enqueueTaskMessage(relatedTaskId, {
|
|
124103
125893
|
type: "error",
|
|
124104
|
-
message:
|
|
125894
|
+
message: errorResponse4,
|
|
124105
125895
|
timestamp: Date.now()
|
|
124106
125896
|
}, capturedTransport?.sessionId).catch((error51) => this._onerror(new Error(`Failed to enqueue error response: ${error51}`)));
|
|
124107
125897
|
} else {
|
|
124108
|
-
capturedTransport?.send(
|
|
125898
|
+
capturedTransport?.send(errorResponse4).catch((error51) => this._onerror(new Error(`Failed to send an error response: ${error51}`)));
|
|
124109
125899
|
}
|
|
124110
125900
|
return;
|
|
124111
125901
|
}
|
|
@@ -124175,7 +125965,7 @@ var Protocol = class {
|
|
|
124175
125965
|
if (abortController.signal.aborted) {
|
|
124176
125966
|
return;
|
|
124177
125967
|
}
|
|
124178
|
-
const
|
|
125968
|
+
const errorResponse4 = {
|
|
124179
125969
|
jsonrpc: "2.0",
|
|
124180
125970
|
id: request2.id,
|
|
124181
125971
|
error: {
|
|
@@ -124187,11 +125977,11 @@ var Protocol = class {
|
|
|
124187
125977
|
if (relatedTaskId && this._taskMessageQueue) {
|
|
124188
125978
|
await this._enqueueTaskMessage(relatedTaskId, {
|
|
124189
125979
|
type: "error",
|
|
124190
|
-
message:
|
|
125980
|
+
message: errorResponse4,
|
|
124191
125981
|
timestamp: Date.now()
|
|
124192
125982
|
}, capturedTransport?.sessionId);
|
|
124193
125983
|
} else {
|
|
124194
|
-
await capturedTransport?.send(
|
|
125984
|
+
await capturedTransport?.send(errorResponse4);
|
|
124195
125985
|
}
|
|
124196
125986
|
}).catch((error51) => this._onerror(new Error(`Failed to send response: ${error51}`))).finally(() => {
|
|
124197
125987
|
if (this._requestHandlerAbortControllers.get(request2.id) === abortController) {
|
|
@@ -126729,7 +128519,7 @@ var WebStandardStreamableHTTPServerTransport = class {
|
|
|
126729
128519
|
* Only sends if eventStore is configured (opt-in for resumability) and
|
|
126730
128520
|
* the client's protocol version supports empty SSE data (>= 2025-11-25).
|
|
126731
128521
|
*/
|
|
126732
|
-
async writePrimingEvent(controller,
|
|
128522
|
+
async writePrimingEvent(controller, encoder4, streamId, protocolVersion) {
|
|
126733
128523
|
if (!this._eventStore) {
|
|
126734
128524
|
return;
|
|
126735
128525
|
}
|
|
@@ -126748,7 +128538,7 @@ data:
|
|
|
126748
128538
|
|
|
126749
128539
|
`;
|
|
126750
128540
|
}
|
|
126751
|
-
controller.enqueue(
|
|
128541
|
+
controller.enqueue(encoder4.encode(primingEvent));
|
|
126752
128542
|
}
|
|
126753
128543
|
/**
|
|
126754
128544
|
* Handles GET requests for SSE stream
|
|
@@ -126777,7 +128567,7 @@ data:
|
|
|
126777
128567
|
this.onerror?.(new Error("Conflict: Only one SSE stream is allowed per session"));
|
|
126778
128568
|
return this.createJsonErrorResponse(409, -32e3, "Conflict: Only one SSE stream is allowed per session");
|
|
126779
128569
|
}
|
|
126780
|
-
const
|
|
128570
|
+
const encoder4 = new TextEncoder();
|
|
126781
128571
|
let streamController;
|
|
126782
128572
|
const readable = new ReadableStream({
|
|
126783
128573
|
start: (controller) => {
|
|
@@ -126797,7 +128587,7 @@ data:
|
|
|
126797
128587
|
}
|
|
126798
128588
|
this._streamMapping.set(this._standaloneSseStreamId, {
|
|
126799
128589
|
controller: streamController,
|
|
126800
|
-
encoder:
|
|
128590
|
+
encoder: encoder4,
|
|
126801
128591
|
cleanup: () => {
|
|
126802
128592
|
this._streamMapping.delete(this._standaloneSseStreamId);
|
|
126803
128593
|
try {
|
|
@@ -126838,7 +128628,7 @@ data:
|
|
|
126838
128628
|
if (this.sessionId !== void 0) {
|
|
126839
128629
|
headers["mcp-session-id"] = this.sessionId;
|
|
126840
128630
|
}
|
|
126841
|
-
const
|
|
128631
|
+
const encoder4 = new TextEncoder();
|
|
126842
128632
|
let streamController;
|
|
126843
128633
|
const readable = new ReadableStream({
|
|
126844
128634
|
start: (controller) => {
|
|
@@ -126849,7 +128639,7 @@ data:
|
|
|
126849
128639
|
});
|
|
126850
128640
|
const replayedStreamId = await this._eventStore.replayEventsAfter(lastEventId, {
|
|
126851
128641
|
send: async (eventId, message) => {
|
|
126852
|
-
const success2 = this.writeSSEEvent(streamController,
|
|
128642
|
+
const success2 = this.writeSSEEvent(streamController, encoder4, message, eventId);
|
|
126853
128643
|
if (!success2) {
|
|
126854
128644
|
this.onerror?.(new Error("Failed replay events"));
|
|
126855
128645
|
try {
|
|
@@ -126861,7 +128651,7 @@ data:
|
|
|
126861
128651
|
});
|
|
126862
128652
|
this._streamMapping.set(replayedStreamId, {
|
|
126863
128653
|
controller: streamController,
|
|
126864
|
-
encoder:
|
|
128654
|
+
encoder: encoder4,
|
|
126865
128655
|
cleanup: () => {
|
|
126866
128656
|
this._streamMapping.delete(replayedStreamId);
|
|
126867
128657
|
try {
|
|
@@ -126879,7 +128669,7 @@ data:
|
|
|
126879
128669
|
/**
|
|
126880
128670
|
* Writes an event to an SSE stream via controller with proper formatting
|
|
126881
128671
|
*/
|
|
126882
|
-
writeSSEEvent(controller,
|
|
128672
|
+
writeSSEEvent(controller, encoder4, message, eventId) {
|
|
126883
128673
|
try {
|
|
126884
128674
|
let eventData = `event: message
|
|
126885
128675
|
`;
|
|
@@ -126890,7 +128680,7 @@ data:
|
|
|
126890
128680
|
eventData += `data: ${JSON.stringify(message)}
|
|
126891
128681
|
|
|
126892
128682
|
`;
|
|
126893
|
-
controller.enqueue(
|
|
128683
|
+
controller.enqueue(encoder4.encode(eventData));
|
|
126894
128684
|
return true;
|
|
126895
128685
|
} catch (error51) {
|
|
126896
128686
|
this.onerror?.(error51);
|
|
@@ -127012,7 +128802,7 @@ data:
|
|
|
127012
128802
|
}
|
|
127013
128803
|
});
|
|
127014
128804
|
}
|
|
127015
|
-
const
|
|
128805
|
+
const encoder4 = new TextEncoder();
|
|
127016
128806
|
let streamController;
|
|
127017
128807
|
const readable = new ReadableStream({
|
|
127018
128808
|
start: (controller) => {
|
|
@@ -127034,7 +128824,7 @@ data:
|
|
|
127034
128824
|
if (isJSONRPCRequest(message)) {
|
|
127035
128825
|
this._streamMapping.set(streamId, {
|
|
127036
128826
|
controller: streamController,
|
|
127037
|
-
encoder:
|
|
128827
|
+
encoder: encoder4,
|
|
127038
128828
|
cleanup: () => {
|
|
127039
128829
|
this._streamMapping.delete(streamId);
|
|
127040
128830
|
try {
|
|
@@ -127046,7 +128836,7 @@ data:
|
|
|
127046
128836
|
this._requestToStreamMapping.set(message.id, streamId);
|
|
127047
128837
|
}
|
|
127048
128838
|
}
|
|
127049
|
-
await this.writePrimingEvent(streamController,
|
|
128839
|
+
await this.writePrimingEvent(streamController, encoder4, streamId, clientProtocolVersion);
|
|
127050
128840
|
for (const message of messages) {
|
|
127051
128841
|
let closeSSEStream;
|
|
127052
128842
|
let closeStandaloneSSEStream;
|
|
@@ -127814,16 +129604,16 @@ async function collectDiagnosticsWithTimeout(recorder, options, timeoutMs = DIAG
|
|
|
127814
129604
|
}
|
|
127815
129605
|
|
|
127816
129606
|
// ../lite/dist/runtime.js
|
|
127817
|
-
function
|
|
129607
|
+
function jsonResponse3(body, init) {
|
|
127818
129608
|
const headers = new Headers(init?.headers);
|
|
127819
129609
|
headers.set("Content-Type", "application/json");
|
|
127820
129610
|
return new Response(JSON.stringify(body), { ...init, headers });
|
|
127821
129611
|
}
|
|
127822
129612
|
function protocolErrorResponse2(err2) {
|
|
127823
|
-
return
|
|
129613
|
+
return jsonResponse3(err2.toJSON(), { status: err2.code });
|
|
127824
129614
|
}
|
|
127825
|
-
function
|
|
127826
|
-
return
|
|
129615
|
+
function errorResponse3(status2, errorCode, message) {
|
|
129616
|
+
return jsonResponse3({
|
|
127827
129617
|
error: {
|
|
127828
129618
|
code: status2,
|
|
127829
129619
|
errorCode,
|
|
@@ -127832,7 +129622,7 @@ function errorResponse2(status2, errorCode, message) {
|
|
|
127832
129622
|
}, { status: status2 });
|
|
127833
129623
|
}
|
|
127834
129624
|
function mcpUnauthorized(origin, message = "MCP authorization required") {
|
|
127835
|
-
return
|
|
129625
|
+
return jsonResponse3({
|
|
127836
129626
|
error: {
|
|
127837
129627
|
code: 401,
|
|
127838
129628
|
errorCode: "MCP_AUTH_REQUIRED",
|
|
@@ -127881,7 +129671,7 @@ function redirectWithOAuthError(redirectUri, error51, description, state) {
|
|
|
127881
129671
|
url2.searchParams.set("state", state);
|
|
127882
129672
|
return Response.redirect(url2.toString(), 302);
|
|
127883
129673
|
} catch {
|
|
127884
|
-
return
|
|
129674
|
+
return jsonResponse3({
|
|
127885
129675
|
error: error51,
|
|
127886
129676
|
error_description: description,
|
|
127887
129677
|
...state ? { state } : {}
|
|
@@ -128087,11 +129877,11 @@ function createPsLiteRuntime(options) {
|
|
|
128087
129877
|
if (err2 instanceof ProtocolError) {
|
|
128088
129878
|
return protocolErrorResponse2(err2);
|
|
128089
129879
|
}
|
|
128090
|
-
return
|
|
129880
|
+
return errorResponse3(500, "INTERNAL_ERROR", "Internal server error");
|
|
128091
129881
|
}
|
|
128092
129882
|
}
|
|
128093
129883
|
function sendContractResult(result) {
|
|
128094
|
-
return
|
|
129884
|
+
return jsonResponse3(result.body, { status: result.status });
|
|
128095
129885
|
}
|
|
128096
129886
|
function ownerAddress() {
|
|
128097
129887
|
return options.serverOwner ?? options.identity?.address;
|
|
@@ -128099,7 +129889,7 @@ function createPsLiteRuntime(options) {
|
|
|
128099
129889
|
async function handleAuthDevice(request2, url2) {
|
|
128100
129890
|
if (url2.pathname === "/auth/device") {
|
|
128101
129891
|
if (request2.method !== "POST") {
|
|
128102
|
-
return
|
|
129892
|
+
return errorResponse3(405, "METHOD_NOT_ALLOWED", "Method not allowed");
|
|
128103
129893
|
}
|
|
128104
129894
|
return sendContractResult(initiateDeviceSessionContract({
|
|
128105
129895
|
sessionStore: deviceSessions,
|
|
@@ -128113,7 +129903,7 @@ function createPsLiteRuntime(options) {
|
|
|
128113
129903
|
}
|
|
128114
129904
|
if (url2.pathname === "/auth/device/poll") {
|
|
128115
129905
|
if (request2.method !== "GET") {
|
|
128116
|
-
return
|
|
129906
|
+
return errorResponse3(405, "METHOD_NOT_ALLOWED", "Method not allowed");
|
|
128117
129907
|
}
|
|
128118
129908
|
return sendContractResult(pollDeviceSessionContract({
|
|
128119
129909
|
sessionStore: deviceSessions,
|
|
@@ -128125,11 +129915,11 @@ function createPsLiteRuntime(options) {
|
|
|
128125
129915
|
if (url2.pathname === "/auth/device/approve") {
|
|
128126
129916
|
const sessionId = url2.searchParams.get("session");
|
|
128127
129917
|
if (!sessionId) {
|
|
128128
|
-
return request2.method === "GET" ? new Response("Missing session parameter", { status: 400 }) :
|
|
129918
|
+
return request2.method === "GET" ? new Response("Missing session parameter", { status: 400 }) : jsonResponse3({ error: { code: 400, message: "Missing session parameter" } }, { status: 400 });
|
|
128129
129919
|
}
|
|
128130
129920
|
const session = deviceSessions.get(sessionId);
|
|
128131
129921
|
if (!session) {
|
|
128132
|
-
return request2.method === "GET" ? new Response("Session expired or invalid", { status: 404 }) :
|
|
129922
|
+
return request2.method === "GET" ? new Response("Session expired or invalid", { status: 404 }) : jsonResponse3({ error: { code: 404, message: "Session expired or invalid" } }, { status: 404 });
|
|
128133
129923
|
}
|
|
128134
129924
|
if (request2.method === "GET") {
|
|
128135
129925
|
return new Response("Device authorization pending", {
|
|
@@ -128137,10 +129927,10 @@ function createPsLiteRuntime(options) {
|
|
|
128137
129927
|
});
|
|
128138
129928
|
}
|
|
128139
129929
|
if (request2.method !== "POST") {
|
|
128140
|
-
return
|
|
129930
|
+
return errorResponse3(405, "METHOD_NOT_ALLOWED", "Method not allowed");
|
|
128141
129931
|
}
|
|
128142
129932
|
if (session.status === "approved") {
|
|
128143
|
-
return
|
|
129933
|
+
return jsonResponse3({ status: "already_approved" });
|
|
128144
129934
|
}
|
|
128145
129935
|
await auth.authorizeOwner(request2);
|
|
128146
129936
|
return sendContractResult(await approveDeviceSessionContract({
|
|
@@ -128156,7 +129946,7 @@ function createPsLiteRuntime(options) {
|
|
|
128156
129946
|
if (request2.method === "DELETE") {
|
|
128157
129947
|
const token2 = bearerToken(request2);
|
|
128158
129948
|
if (!token2) {
|
|
128159
|
-
return
|
|
129949
|
+
return jsonResponse3({ error: { code: 401, message: "Missing Bearer token" } }, { status: 401 });
|
|
128160
129950
|
}
|
|
128161
129951
|
return sendContractResult(await revokeDeviceTokenContract({
|
|
128162
129952
|
tokenStore,
|
|
@@ -128164,11 +129954,11 @@ function createPsLiteRuntime(options) {
|
|
|
128164
129954
|
}));
|
|
128165
129955
|
}
|
|
128166
129956
|
if (request2.method !== "POST") {
|
|
128167
|
-
return
|
|
129957
|
+
return errorResponse3(405, "METHOD_NOT_ALLOWED", "Method not allowed");
|
|
128168
129958
|
}
|
|
128169
129959
|
const token = bearerToken(request2);
|
|
128170
129960
|
if (!options.accessToken || token !== options.accessToken) {
|
|
128171
|
-
return
|
|
129961
|
+
return jsonResponse3({
|
|
128172
129962
|
error: {
|
|
128173
129963
|
code: 403,
|
|
128174
129964
|
message: "Only control-plane tokens can provision Personal Server session tokens"
|
|
@@ -128179,10 +129969,10 @@ function createPsLiteRuntime(options) {
|
|
|
128179
129969
|
try {
|
|
128180
129970
|
body = await request2.json();
|
|
128181
129971
|
} catch {
|
|
128182
|
-
return
|
|
129972
|
+
return jsonResponse3({ error: { code: 400, message: "Request body must be valid JSON" } }, { status: 400 });
|
|
128183
129973
|
}
|
|
128184
129974
|
if (!body.token || typeof body.token !== "string") {
|
|
128185
|
-
return
|
|
129975
|
+
return jsonResponse3({ error: { code: 400, message: "Missing token" } }, { status: 400 });
|
|
128186
129976
|
}
|
|
128187
129977
|
return sendContractResult(await provisionDeviceTokenContract({
|
|
128188
129978
|
tokenStore,
|
|
@@ -128198,10 +129988,12 @@ function createPsLiteRuntime(options) {
|
|
|
128198
129988
|
activate() {
|
|
128199
129989
|
active = true;
|
|
128200
129990
|
options.syncManager?.start?.();
|
|
129991
|
+
options.derivatives?.scheduler.start();
|
|
128201
129992
|
},
|
|
128202
129993
|
deactivate() {
|
|
128203
129994
|
active = false;
|
|
128204
129995
|
void options.syncManager?.stop?.();
|
|
129996
|
+
options.derivatives?.scheduler.stop();
|
|
128205
129997
|
},
|
|
128206
129998
|
isAvailable() {
|
|
128207
129999
|
return active;
|
|
@@ -128235,7 +130027,7 @@ function createPsLiteRuntime(options) {
|
|
|
128235
130027
|
accessLogs: accessLogReader.capabilities?.accessLogs ?? "custom",
|
|
128236
130028
|
config: options.stateCapabilities?.config ?? (options.saveConfig ? "custom" : "indexeddb")
|
|
128237
130029
|
};
|
|
128238
|
-
return
|
|
130030
|
+
return jsonResponse3({
|
|
128239
130031
|
status: active ? "healthy" : "unavailable",
|
|
128240
130032
|
runtime: "ps-lite",
|
|
128241
130033
|
storage: options.storage.kind,
|
|
@@ -128253,7 +130045,7 @@ function createPsLiteRuntime(options) {
|
|
|
128253
130045
|
}
|
|
128254
130046
|
if (url2.pathname === "/v1/diagnostics") {
|
|
128255
130047
|
if (request2.method !== "GET") {
|
|
128256
|
-
return
|
|
130048
|
+
return errorResponse3(405, "METHOD_NOT_ALLOWED", "Method not allowed");
|
|
128257
130049
|
}
|
|
128258
130050
|
try {
|
|
128259
130051
|
await auth.authorizeOwner(request2);
|
|
@@ -128264,7 +130056,7 @@ function createPsLiteRuntime(options) {
|
|
|
128264
130056
|
throw err2;
|
|
128265
130057
|
}
|
|
128266
130058
|
if (!options.diagnostics) {
|
|
128267
|
-
return
|
|
130059
|
+
return jsonResponse3({
|
|
128268
130060
|
error: {
|
|
128269
130061
|
code: 404,
|
|
128270
130062
|
errorCode: "DIAGNOSTICS_NOT_CONFIGURED",
|
|
@@ -128278,7 +130070,7 @@ function createPsLiteRuntime(options) {
|
|
|
128278
130070
|
syncStatus: syncStatus2,
|
|
128279
130071
|
storage: dataStorage
|
|
128280
130072
|
});
|
|
128281
|
-
return
|
|
130073
|
+
return jsonResponse3(snapshot);
|
|
128282
130074
|
}
|
|
128283
130075
|
if (!active) {
|
|
128284
130076
|
return unavailableResponse();
|
|
@@ -128313,9 +130105,16 @@ function createPsLiteRuntime(options) {
|
|
|
128313
130105
|
// serverOwner is absent the accessRecord is safely omitted.
|
|
128314
130106
|
serverOwner: options.serverOwner,
|
|
128315
130107
|
serverSigner: x402ServerSigner,
|
|
128316
|
-
lineageGateway: options.lineageGateway
|
|
130108
|
+
lineageGateway: options.lineageGateway,
|
|
130109
|
+
// Recompute on refresh: a new local version marks every
|
|
130110
|
+
// question that reads the scope stale.
|
|
130111
|
+
onDataWritten: options.derivatives ? (event) => options.derivatives?.scheduler.markSourceChanged(event.scope, { lineageSources: event.lineageSources }) : void 0
|
|
128317
130112
|
}, { basePath: dataPrefix });
|
|
128318
130113
|
}
|
|
130114
|
+
const derivativesPrefix = "/v1/derivatives";
|
|
130115
|
+
if (url2.pathname.startsWith(`${derivativesPrefix}/`)) {
|
|
130116
|
+
return handlePersonalServerDerivativesRequest(request2, { auth, compute: options.derivatives ?? null, now }, { basePath: derivativesPrefix });
|
|
130117
|
+
}
|
|
128319
130118
|
if (url2.pathname.startsWith("/auth/device")) {
|
|
128320
130119
|
const response = await handleAuthDevice(request2, url2);
|
|
128321
130120
|
if (response)
|
|
@@ -128404,7 +130203,7 @@ function createPsLiteRuntime(options) {
|
|
|
128404
130203
|
if (mcpResponse)
|
|
128405
130204
|
return mcpResponse;
|
|
128406
130205
|
}
|
|
128407
|
-
return
|
|
130206
|
+
return errorResponse3(404, "NOT_FOUND", "Not found");
|
|
128408
130207
|
});
|
|
128409
130208
|
}
|
|
128410
130209
|
};
|
|
@@ -128415,22 +130214,22 @@ async function handleMcpRoute(input) {
|
|
|
128415
130214
|
const ownerAuthorizationPrefix = "/v1/mcp/oauth/authorizations";
|
|
128416
130215
|
if (pathname === "/.well-known/oauth-protected-resource/mcp") {
|
|
128417
130216
|
if (!resolveMcpApprovalUrl(input.approvalUrl)) {
|
|
128418
|
-
return
|
|
130217
|
+
return errorResponse3(404, "MCP_OAUTH_NOT_CONFIGURED", "MCP OAuth is not configured");
|
|
128419
130218
|
}
|
|
128420
|
-
return
|
|
130219
|
+
return jsonResponse3(protectedResourceMetadata(input.serverOrigin));
|
|
128421
130220
|
}
|
|
128422
130221
|
if (pathname === "/.well-known/oauth-authorization-server") {
|
|
128423
130222
|
if (!resolveMcpApprovalUrl(input.approvalUrl)) {
|
|
128424
|
-
return
|
|
130223
|
+
return errorResponse3(404, "MCP_OAUTH_NOT_CONFIGURED", "MCP OAuth is not configured");
|
|
128425
130224
|
}
|
|
128426
|
-
return
|
|
130225
|
+
return jsonResponse3(authorizationServerMetadata(input.serverOrigin));
|
|
128427
130226
|
}
|
|
128428
130227
|
if (pathname === "/mcp/oauth/register") {
|
|
128429
130228
|
if (!resolveMcpApprovalUrl(input.approvalUrl)) {
|
|
128430
|
-
return
|
|
130229
|
+
return errorResponse3(404, "MCP_OAUTH_NOT_CONFIGURED", "MCP OAuth is not configured");
|
|
128431
130230
|
}
|
|
128432
130231
|
if (input.request.method !== "POST") {
|
|
128433
|
-
return
|
|
130232
|
+
return errorResponse3(405, "METHOD_NOT_ALLOWED", "Method not allowed");
|
|
128434
130233
|
}
|
|
128435
130234
|
let body = {};
|
|
128436
130235
|
try {
|
|
@@ -128438,7 +130237,7 @@ async function handleMcpRoute(input) {
|
|
|
128438
130237
|
} catch {
|
|
128439
130238
|
body = {};
|
|
128440
130239
|
}
|
|
128441
|
-
return
|
|
130240
|
+
return jsonResponse3({
|
|
128442
130241
|
client_id: `mcp-client-${crypto.randomUUID()}`,
|
|
128443
130242
|
client_name: body.client_name ?? "Claude",
|
|
128444
130243
|
redirect_uris: Array.isArray(body.redirect_uris) ? body.redirect_uris : [],
|
|
@@ -128449,10 +130248,10 @@ async function handleMcpRoute(input) {
|
|
|
128449
130248
|
}
|
|
128450
130249
|
if (pathname === "/mcp/oauth/authorize") {
|
|
128451
130250
|
if (!resolveMcpApprovalUrl(input.approvalUrl)) {
|
|
128452
|
-
return
|
|
130251
|
+
return errorResponse3(404, "MCP_OAUTH_NOT_CONFIGURED", "MCP OAuth is not configured");
|
|
128453
130252
|
}
|
|
128454
130253
|
if (input.request.method !== "GET") {
|
|
128455
|
-
return
|
|
130254
|
+
return errorResponse3(405, "METHOD_NOT_ALLOWED", "Method not allowed");
|
|
128456
130255
|
}
|
|
128457
130256
|
const responseType = input.url.searchParams.get("response_type");
|
|
128458
130257
|
const clientId = input.url.searchParams.get("client_id") ?? "";
|
|
@@ -128480,7 +130279,7 @@ async function handleMcpRoute(input) {
|
|
|
128480
130279
|
});
|
|
128481
130280
|
const approvalUrl = resolveMcpApprovalUrl(input.approvalUrl);
|
|
128482
130281
|
if (!approvalUrl) {
|
|
128483
|
-
return
|
|
130282
|
+
return errorResponse3(500, "MCP_APPROVAL_URL_MISSING", "MCP OAuth approval URL is not configured");
|
|
128484
130283
|
}
|
|
128485
130284
|
const approve = new URL(approvalUrl);
|
|
128486
130285
|
approve.searchParams.set("mcp_authorization", created.authorizationId);
|
|
@@ -128493,14 +130292,14 @@ async function handleMcpRoute(input) {
|
|
|
128493
130292
|
}
|
|
128494
130293
|
if (pathname === "/mcp/oauth/token") {
|
|
128495
130294
|
if (!resolveMcpApprovalUrl(input.approvalUrl)) {
|
|
128496
|
-
return
|
|
130295
|
+
return errorResponse3(404, "MCP_OAUTH_NOT_CONFIGURED", "MCP OAuth is not configured");
|
|
128497
130296
|
}
|
|
128498
130297
|
if (input.request.method !== "POST") {
|
|
128499
|
-
return
|
|
130298
|
+
return errorResponse3(405, "METHOD_NOT_ALLOWED", "Method not allowed");
|
|
128500
130299
|
}
|
|
128501
130300
|
const body = await parseFormBody(input.request);
|
|
128502
130301
|
if (body.get("grant_type") !== "authorization_code") {
|
|
128503
|
-
return
|
|
130302
|
+
return jsonResponse3({
|
|
128504
130303
|
error: "unsupported_grant_type",
|
|
128505
130304
|
error_description: "Only authorization_code is supported"
|
|
128506
130305
|
}, { status: 400 });
|
|
@@ -128516,13 +130315,13 @@ async function handleMcpRoute(input) {
|
|
|
128516
130315
|
connectionStore: input.store,
|
|
128517
130316
|
now: input.now
|
|
128518
130317
|
});
|
|
128519
|
-
return
|
|
130318
|
+
return jsonResponse3({
|
|
128520
130319
|
access_token: token.accessToken,
|
|
128521
130320
|
token_type: "Bearer",
|
|
128522
130321
|
...token.scope ? { scope: token.scope } : {}
|
|
128523
130322
|
});
|
|
128524
130323
|
} catch (err2) {
|
|
128525
|
-
return
|
|
130324
|
+
return jsonResponse3({
|
|
128526
130325
|
error: err2 instanceof McpOAuthAuthorizationError ? err2.code : "invalid_grant",
|
|
128527
130326
|
error_description: err2 instanceof Error ? err2.message : String(err2)
|
|
128528
130327
|
}, { status: 400 });
|
|
@@ -128540,28 +130339,28 @@ async function handleMcpRoute(input) {
|
|
|
128540
130339
|
const tail = pathname.slice(ownerAuthorizationPrefix.length + 1);
|
|
128541
130340
|
const [id2, action] = tail.split("/");
|
|
128542
130341
|
if (!id2) {
|
|
128543
|
-
return
|
|
130342
|
+
return errorResponse3(404, "NOT_FOUND", "Not found");
|
|
128544
130343
|
}
|
|
128545
130344
|
if (!action && input.request.method === "GET") {
|
|
128546
130345
|
const record2 = await input.authorizationStore.getById(id2);
|
|
128547
130346
|
if (!record2) {
|
|
128548
|
-
return
|
|
130347
|
+
return errorResponse3(404, "NOT_FOUND", "Authorization not found");
|
|
128549
130348
|
}
|
|
128550
|
-
return
|
|
130349
|
+
return jsonResponse3(toMcpOAuthAuthorizationView(record2));
|
|
128551
130350
|
}
|
|
128552
130351
|
if (action === "approve") {
|
|
128553
130352
|
if (input.request.method !== "POST") {
|
|
128554
|
-
return
|
|
130353
|
+
return errorResponse3(405, "METHOD_NOT_ALLOWED", "Method not allowed");
|
|
128555
130354
|
}
|
|
128556
130355
|
let body = {};
|
|
128557
130356
|
try {
|
|
128558
130357
|
body = await input.request.json();
|
|
128559
130358
|
} catch {
|
|
128560
|
-
return
|
|
130359
|
+
return errorResponse3(400, "INVALID_BODY", "Body must be JSON");
|
|
128561
130360
|
}
|
|
128562
130361
|
if (Array.isArray(body.scopes) && body.scopes.length > 0) {
|
|
128563
130362
|
if (!input.gateway || !input.gatewayConfig?.url) {
|
|
128564
|
-
return
|
|
130363
|
+
return errorResponse3(500, "SERVER_NOT_CONFIGURED", "Gateway config is not configured");
|
|
128565
130364
|
}
|
|
128566
130365
|
try {
|
|
128567
130366
|
const approved = await approveMcpOAuthAuthorizationWithScopes({
|
|
@@ -128579,16 +130378,16 @@ async function handleMcpRoute(input) {
|
|
|
128579
130378
|
serverSigner: input.serverSigner,
|
|
128580
130379
|
now: input.now
|
|
128581
130380
|
});
|
|
128582
|
-
return
|
|
130381
|
+
return jsonResponse3({ redirectTo: approved.redirectTo });
|
|
128583
130382
|
} catch (err2) {
|
|
128584
130383
|
if (err2 instanceof McpOAuthAuthorizationError) {
|
|
128585
|
-
return
|
|
130384
|
+
return errorResponse3(err2.status, err2.code, err2.message);
|
|
128586
130385
|
}
|
|
128587
130386
|
throw err2;
|
|
128588
130387
|
}
|
|
128589
130388
|
}
|
|
128590
130389
|
if (!Array.isArray(body.grants) || body.grants.length === 0) {
|
|
128591
|
-
return
|
|
130390
|
+
return errorResponse3(400, "GRANTS_REQUIRED", "Approve requires grants or scopes");
|
|
128592
130391
|
}
|
|
128593
130392
|
try {
|
|
128594
130393
|
const approved = await approveMcpOAuthAuthorization({ authorizationId: id2, grants: body.grants }, {
|
|
@@ -128596,15 +130395,15 @@ async function handleMcpRoute(input) {
|
|
|
128596
130395
|
authorizationStore: input.authorizationStore,
|
|
128597
130396
|
now: input.now
|
|
128598
130397
|
});
|
|
128599
|
-
return
|
|
130398
|
+
return jsonResponse3({ redirectTo: approved.redirectTo });
|
|
128600
130399
|
} catch (err2) {
|
|
128601
130400
|
if (err2 instanceof McpOAuthAuthorizationError) {
|
|
128602
|
-
return
|
|
130401
|
+
return errorResponse3(err2.status, err2.code, err2.message);
|
|
128603
130402
|
}
|
|
128604
130403
|
throw err2;
|
|
128605
130404
|
}
|
|
128606
130405
|
}
|
|
128607
|
-
return
|
|
130406
|
+
return errorResponse3(404, "NOT_FOUND", "Not found");
|
|
128608
130407
|
}
|
|
128609
130408
|
if (pathname === ownerPrefix || pathname.startsWith(`${ownerPrefix}/`)) {
|
|
128610
130409
|
try {
|
|
@@ -128628,41 +130427,41 @@ async function handleMcpRoute(input) {
|
|
|
128628
130427
|
publicOrigin: input.serverOrigin,
|
|
128629
130428
|
now: input.now
|
|
128630
130429
|
});
|
|
128631
|
-
return
|
|
130430
|
+
return jsonResponse3(created, { status: 201 });
|
|
128632
130431
|
}
|
|
128633
130432
|
if (input.request.method === "GET") {
|
|
128634
130433
|
const records = await listMcpConnectionViews(input.store);
|
|
128635
|
-
return
|
|
130434
|
+
return jsonResponse3({ connections: records });
|
|
128636
130435
|
}
|
|
128637
|
-
return
|
|
130436
|
+
return errorResponse3(405, "METHOD_NOT_ALLOWED", "Method not allowed");
|
|
128638
130437
|
}
|
|
128639
130438
|
const tail = pathname.slice(ownerPrefix.length + 1);
|
|
128640
130439
|
const [id2, action] = tail.split("/");
|
|
128641
130440
|
if (!id2) {
|
|
128642
|
-
return
|
|
130441
|
+
return errorResponse3(404, "NOT_FOUND", "Not found");
|
|
128643
130442
|
}
|
|
128644
130443
|
if (action === "approve") {
|
|
128645
130444
|
if (input.request.method !== "POST") {
|
|
128646
|
-
return
|
|
130445
|
+
return errorResponse3(405, "METHOD_NOT_ALLOWED", "Method not allowed");
|
|
128647
130446
|
}
|
|
128648
130447
|
let body = {};
|
|
128649
130448
|
try {
|
|
128650
130449
|
body = await input.request.json();
|
|
128651
130450
|
} catch {
|
|
128652
|
-
return
|
|
130451
|
+
return errorResponse3(400, "INVALID_BODY", "Body must be JSON");
|
|
128653
130452
|
}
|
|
128654
130453
|
if (!Array.isArray(body.grants) || body.grants.length === 0) {
|
|
128655
|
-
return
|
|
130454
|
+
return errorResponse3(400, "GRANTS_REQUIRED", "Approve requires at least one grant \u2014 mint grants in the consent flow first");
|
|
128656
130455
|
}
|
|
128657
130456
|
try {
|
|
128658
130457
|
const updated = await approveMcpConnection({ connectionId: id2, grants: body.grants }, { store: input.store, now: input.now });
|
|
128659
|
-
return
|
|
130458
|
+
return jsonResponse3(toMcpConnectionView(updated));
|
|
128660
130459
|
} catch (err2) {
|
|
128661
130460
|
if (err2 instanceof McpConnectionNotFoundError) {
|
|
128662
|
-
return
|
|
130461
|
+
return errorResponse3(404, "NOT_FOUND", err2.message);
|
|
128663
130462
|
}
|
|
128664
130463
|
if (err2 instanceof McpConnectionStateError) {
|
|
128665
|
-
return
|
|
130464
|
+
return errorResponse3(409, "INVALID_STATE", err2.message);
|
|
128666
130465
|
}
|
|
128667
130466
|
throw err2;
|
|
128668
130467
|
}
|
|
@@ -128674,22 +130473,22 @@ async function handleMcpRoute(input) {
|
|
|
128674
130473
|
store: input.store,
|
|
128675
130474
|
now: input.now
|
|
128676
130475
|
});
|
|
128677
|
-
return
|
|
130476
|
+
return jsonResponse3(toMcpConnectionView(updated));
|
|
128678
130477
|
} catch (err2) {
|
|
128679
130478
|
if (err2 instanceof McpConnectionNotFoundError) {
|
|
128680
|
-
return
|
|
130479
|
+
return errorResponse3(404, "NOT_FOUND", err2.message);
|
|
128681
130480
|
}
|
|
128682
130481
|
throw err2;
|
|
128683
130482
|
}
|
|
128684
130483
|
}
|
|
128685
|
-
return
|
|
130484
|
+
return errorResponse3(405, "METHOD_NOT_ALLOWED", "Method not allowed");
|
|
128686
130485
|
}
|
|
128687
|
-
return
|
|
130486
|
+
return errorResponse3(404, "NOT_FOUND", "Not found");
|
|
128688
130487
|
}
|
|
128689
130488
|
async function handleMcpToken(rawToken, options) {
|
|
128690
130489
|
if (!rawToken) {
|
|
128691
130490
|
if (!options.oauthChallenge) {
|
|
128692
|
-
return
|
|
130491
|
+
return errorResponse3(401, "INVALID_TOKEN", "Missing MCP connection token");
|
|
128693
130492
|
}
|
|
128694
130493
|
return mcpUnauthorized(input.serverOrigin);
|
|
128695
130494
|
}
|
|
@@ -128697,7 +130496,7 @@ async function handleMcpRoute(input) {
|
|
|
128697
130496
|
const record2 = await input.store.getByTokenHash(tokenHash);
|
|
128698
130497
|
if (!record2) {
|
|
128699
130498
|
if (!options.oauthChallenge) {
|
|
128700
|
-
return
|
|
130499
|
+
return errorResponse3(401, "INVALID_TOKEN", "Unknown or revoked MCP connection");
|
|
128701
130500
|
}
|
|
128702
130501
|
return mcpUnauthorized(input.serverOrigin, "Unknown or revoked MCP connection");
|
|
128703
130502
|
}
|
|
@@ -128741,10 +130540,10 @@ async function handleMcpRoute(input) {
|
|
|
128741
130540
|
throw err2;
|
|
128742
130541
|
}
|
|
128743
130542
|
if (input.request.method !== "GET") {
|
|
128744
|
-
return
|
|
130543
|
+
return errorResponse3(405, "METHOD_NOT_ALLOWED", "Method not allowed");
|
|
128745
130544
|
}
|
|
128746
130545
|
const snapshot = input.activityRecorder ? input.activityRecorder.snapshot() : { events: [], running: 0, total: 0 };
|
|
128747
|
-
return
|
|
130546
|
+
return jsonResponse3(snapshot);
|
|
128748
130547
|
}
|
|
128749
130548
|
if (pathname === "/mcp") {
|
|
128750
130549
|
return handleMcpToken(bearerToken(input.request), {
|
|
@@ -128755,7 +130554,7 @@ async function handleMcpRoute(input) {
|
|
|
128755
130554
|
if (pathname.startsWith(mcpPrefix)) {
|
|
128756
130555
|
const rawToken = decodeURIComponent(pathname.slice(mcpPrefix.length));
|
|
128757
130556
|
if (!rawToken || rawToken.includes("/")) {
|
|
128758
|
-
return
|
|
130557
|
+
return errorResponse3(404, "NOT_FOUND", "Not found");
|
|
128759
130558
|
}
|
|
128760
130559
|
return handleMcpToken(rawToken, { oauthChallenge: false });
|
|
128761
130560
|
}
|
|
@@ -129402,6 +131201,24 @@ async function downloadOne(deps, record2) {
|
|
|
129402
131201
|
}
|
|
129403
131202
|
diagnostics?.onIndexEnd(record2.id, envelope.scope);
|
|
129404
131203
|
logger.info({ dataPointId: record2.id, scope: envelope.scope, path: relativePath }, "Downloaded and indexed data point");
|
|
131204
|
+
if (deps.onDataPointIndexed) {
|
|
131205
|
+
try {
|
|
131206
|
+
let lineageSources;
|
|
131207
|
+
try {
|
|
131208
|
+
lineageSources = readStoredLineage(envelope.data)?.sources;
|
|
131209
|
+
} catch {
|
|
131210
|
+
}
|
|
131211
|
+
deps.onDataPointIndexed({
|
|
131212
|
+
scope: envelope.scope,
|
|
131213
|
+
dataPointId: record2.id,
|
|
131214
|
+
version: Number(record2.expectedVersion),
|
|
131215
|
+
collectedAt: envelope.collectedAt,
|
|
131216
|
+
lineageSources
|
|
131217
|
+
});
|
|
131218
|
+
} catch (err2) {
|
|
131219
|
+
logger.warn({ scope: envelope.scope, error: err2.message }, "onDataPointIndexed hook failed; data point already indexed");
|
|
131220
|
+
}
|
|
131221
|
+
}
|
|
129405
131222
|
return {
|
|
129406
131223
|
dataPointId: record2.id,
|
|
129407
131224
|
scope: envelope.scope,
|
|
@@ -130481,7 +132298,8 @@ async function createPsLiteSyncManager(options) {
|
|
|
130481
132298
|
logger,
|
|
130482
132299
|
diagnostics: downloadDiagnostics,
|
|
130483
132300
|
dataPointFeed,
|
|
130484
|
-
scopeDeletions
|
|
132301
|
+
scopeDeletions,
|
|
132302
|
+
onDataPointIndexed: options.onDataPointIndexed
|
|
130485
132303
|
}, {
|
|
130486
132304
|
deleteData,
|
|
130487
132305
|
pendingBlobDeletions,
|
|
@@ -130509,6 +132327,65 @@ async function createPsLiteSyncManager(options) {
|
|
|
130509
132327
|
return { syncManager, serverOwner, dataPointFeed, scopeDeletions };
|
|
130510
132328
|
}
|
|
130511
132329
|
|
|
132330
|
+
// ../lite/dist/derivatives.js
|
|
132331
|
+
var QUESTIONS_KEY = "derivative-questions-v1";
|
|
132332
|
+
async function createPsLiteQuestionStore(stateStore) {
|
|
132333
|
+
const saved = await stateStore.get(QUESTIONS_KEY);
|
|
132334
|
+
const initial = saved?.version === 1 ? saved.questions : [];
|
|
132335
|
+
return createInMemoryQuestionStore({
|
|
132336
|
+
initial,
|
|
132337
|
+
onChange: (questions) => stateStore.set(QUESTIONS_KEY, {
|
|
132338
|
+
version: 1,
|
|
132339
|
+
questions
|
|
132340
|
+
})
|
|
132341
|
+
});
|
|
132342
|
+
}
|
|
132343
|
+
function psLiteInferenceConfigured(config2) {
|
|
132344
|
+
return config2.inference.baseUrl.replace(/\/+$/, "") !== DEFAULT_INFERENCE_BASE_URL.replace(/\/+$/, "");
|
|
132345
|
+
}
|
|
132346
|
+
function createPsLiteDerivativeCompute(options) {
|
|
132347
|
+
const logger = options.logger ? {
|
|
132348
|
+
info: (payload, message) => options.logger?.info(payload, message),
|
|
132349
|
+
warn: (payload, message) => options.logger?.warn(payload, message)
|
|
132350
|
+
} : void 0;
|
|
132351
|
+
const provider = options.provider ?? createOpenAiCompatibleInferenceProvider({
|
|
132352
|
+
baseUrl: options.config.inference.baseUrl,
|
|
132353
|
+
model: options.config.inference.model,
|
|
132354
|
+
// E2EE v2 to the Phala gateway (WebCrypto only, so it runs in the
|
|
132355
|
+
// browser): the relay sees ciphertext. `inference.e2ee: false` turns
|
|
132356
|
+
// it off for local development against a provider without ACI.
|
|
132357
|
+
encryption: options.config.inference.e2ee ? createPhalaE2eeEncryption({
|
|
132358
|
+
baseUrl: options.config.inference.baseUrl,
|
|
132359
|
+
logger
|
|
132360
|
+
}) : void 0
|
|
132361
|
+
});
|
|
132362
|
+
const scheduler = createRecomputeScheduler({
|
|
132363
|
+
store: options.store,
|
|
132364
|
+
debounceMs: options.config.inference.recomputeDebounceMs,
|
|
132365
|
+
serverOwner: options.serverOwner,
|
|
132366
|
+
now: options.now,
|
|
132367
|
+
logger,
|
|
132368
|
+
compute: (questionId) => computeQuestion(questionId, {
|
|
132369
|
+
// A -> B -> C: a question reading this derived scope recomputes.
|
|
132370
|
+
onDerivedWritten: (event) => scheduler.markSourceChanged(event.scope, {
|
|
132371
|
+
lineageSources: event.lineageSources
|
|
132372
|
+
}),
|
|
132373
|
+
runtimeAvailability: options.runtimeAvailability,
|
|
132374
|
+
storage: options.storage,
|
|
132375
|
+
store: options.store,
|
|
132376
|
+
provider,
|
|
132377
|
+
serverOwner: options.serverOwner,
|
|
132378
|
+
maxSourceItems: options.config.inference.maxSourceItems,
|
|
132379
|
+
syncManager: options.syncManager?.() ?? null,
|
|
132380
|
+
scopeDeletions: options.scopeDeletions?.(),
|
|
132381
|
+
writePolicyPorts: options.writePolicyPorts,
|
|
132382
|
+
now: options.now,
|
|
132383
|
+
logger
|
|
132384
|
+
})
|
|
132385
|
+
});
|
|
132386
|
+
return { store: options.store, scheduler, provider };
|
|
132387
|
+
}
|
|
132388
|
+
|
|
130512
132389
|
// ../lite/dist/persistence.js
|
|
130513
132390
|
function assertCompletePsLitePersistenceBundle(bundle) {
|
|
130514
132391
|
if (!bundle || typeof bundle !== "object") {
|
|
@@ -130573,6 +132450,29 @@ async function createIndexedDbPsLiteRuntime(options) {
|
|
|
130573
132450
|
});
|
|
130574
132451
|
let syncManager = options.syncManager ?? null;
|
|
130575
132452
|
let scopeDeletions = options.scopeDeletions;
|
|
132453
|
+
let runtimeRef = null;
|
|
132454
|
+
let derivatives = options.derivatives ?? null;
|
|
132455
|
+
if (derivatives === null && options.derivatives === void 0 && (options.inferenceProvider || psLiteInferenceConfigured(config2))) {
|
|
132456
|
+
derivatives = createPsLiteDerivativeCompute({
|
|
132457
|
+
config: config2,
|
|
132458
|
+
storage,
|
|
132459
|
+
store: await createPsLiteQuestionStore(stateStore),
|
|
132460
|
+
serverOwner,
|
|
132461
|
+
syncManager: () => syncManager,
|
|
132462
|
+
scopeDeletions: () => scopeDeletions,
|
|
132463
|
+
writePolicyPorts: {
|
|
132464
|
+
authSessionVerifier: gateway,
|
|
132465
|
+
grantVerifier: gateway
|
|
132466
|
+
},
|
|
132467
|
+
runtimeAvailability: {
|
|
132468
|
+
isAvailable: () => runtimeRef?.isAvailable() ?? Boolean(options.active)
|
|
132469
|
+
},
|
|
132470
|
+
provider: options.inferenceProvider,
|
|
132471
|
+
logger: options.logger
|
|
132472
|
+
});
|
|
132473
|
+
} else if (derivatives === null && options.derivatives === void 0) {
|
|
132474
|
+
options.logger?.warn({ baseUrl: config2.inference.baseUrl }, "Derivative compute disabled: inference.baseUrl is the direct-provider default; point it at the Vana inference relay");
|
|
132475
|
+
}
|
|
130576
132476
|
if (!syncManager && config2.sync.enabled) {
|
|
130577
132477
|
const sync = await createPsLiteSyncManager({
|
|
130578
132478
|
config: config2,
|
|
@@ -130586,12 +132486,14 @@ async function createIndexedDbPsLiteRuntime(options) {
|
|
|
130586
132486
|
scopeDeletions,
|
|
130587
132487
|
diagnostics,
|
|
130588
132488
|
logger: options.logger,
|
|
130589
|
-
lineageGateway
|
|
132489
|
+
lineageGateway,
|
|
132490
|
+
onDataPointIndexed: (event) => derivatives?.scheduler.markSourceChanged(event.scope, {
|
|
132491
|
+
lineageSources: event.lineageSources
|
|
132492
|
+
})
|
|
130590
132493
|
});
|
|
130591
132494
|
syncManager = sync.syncManager;
|
|
130592
132495
|
scopeDeletions = sync.scopeDeletions;
|
|
130593
132496
|
}
|
|
130594
|
-
let runtimeRef = null;
|
|
130595
132497
|
const auth = options.auth ?? createWeb3SignedPsLiteAuth({
|
|
130596
132498
|
origin: () => options.runtimeOrigin ?? config2.server.origin,
|
|
130597
132499
|
ownerAddress: serverOwner,
|
|
@@ -130621,6 +132523,7 @@ async function createIndexedDbPsLiteRuntime(options) {
|
|
|
130621
132523
|
scopeDeletions,
|
|
130622
132524
|
diagnostics,
|
|
130623
132525
|
lineageGateway,
|
|
132526
|
+
derivatives,
|
|
130624
132527
|
saveConfig: async (nextConfig) => {
|
|
130625
132528
|
const saved = await savePsLiteConfig(stateStore, nextConfig);
|
|
130626
132529
|
Object.assign(config2, saved);
|
|
@@ -130643,7 +132546,8 @@ async function createIndexedDbPsLiteRuntime(options) {
|
|
|
130643
132546
|
storage,
|
|
130644
132547
|
tokenStore,
|
|
130645
132548
|
accessLogStore,
|
|
130646
|
-
syncManager
|
|
132549
|
+
syncManager,
|
|
132550
|
+
derivatives
|
|
130647
132551
|
};
|
|
130648
132552
|
}
|
|
130649
132553
|
|