@opendatalabs/personal-server-ts-server 0.0.1-canary.de6bc6f → 0.0.1-canary.dfa1648

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.
@@ -1,26 +1,8 @@
1
- import type { SchemaRegistrarPort } from "@opendatalabs/personal-server-ts-core/ports";
2
- import type { ServerSigner } from "@opendatalabs/personal-server-ts-core/signing";
3
- import type { Logger } from "pino";
4
1
  /**
5
- * Definition used for scopes that carry unstructured/binary data. Nothing in
6
- * this stack fetches the definitionUrl — it only needs to be a stable string —
7
- * but consuming builders may dereference it, so it points at a permissive doc.
2
+ * The no-schema registrar now lives in core so the in-browser PS-Lite runtime
3
+ * can register schemas for binary uploads too (see
4
+ * `packages/core/src/api/schema-registrar.ts`). Re-exported here for backwards
5
+ * compatibility with existing server imports.
8
6
  */
9
- export declare const NO_SCHEMA_NAME = "No Schema";
10
- export declare const NO_SCHEMA_DEFINITION_URL = "https://schemas.vana.org/no-schema/v1.json";
11
- export declare const NO_SCHEMA_DIALECT = "none";
12
- export interface SchemaRegistrarDeps {
13
- gatewayUrl: string;
14
- signer: ServerSigner;
15
- logger?: Logger;
16
- }
17
- /**
18
- * Registers (idempotently, by scope) a permissive "no-schema" schema with the
19
- * gateway via POST /v1/schemas, signing an EIP-712 SchemaRegistration message.
20
- *
21
- * The gateway requires the EIP-712 signer to equal the registered ownerAddress
22
- * (no server-delegation branch for schemas), so the schema is owned by the
23
- * server signing account.
24
- */
25
- export declare function createSchemaRegistrar(deps: SchemaRegistrarDeps): SchemaRegistrarPort;
7
+ export { createSchemaRegistrar, NO_SCHEMA_DEFINITION_URL, NO_SCHEMA_DIALECT, NO_SCHEMA_NAME, type SchemaRegistrarDeps, } from "@opendatalabs/personal-server-ts-core/api";
26
8
  //# sourceMappingURL=schema-registrar.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"schema-registrar.d.ts","sourceRoot":"","sources":["../src/schema-registrar.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,mBAAmB,EACpB,MAAM,6CAA6C,CAAC;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+CAA+C,CAAC;AAClF,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAEnC;;;;GAIG;AACH,eAAO,MAAM,cAAc,cAAc,CAAC;AAC1C,eAAO,MAAM,wBAAwB,+CACS,CAAC;AAC/C,eAAO,MAAM,iBAAiB,SAAS,CAAC;AAExC,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAoBD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,mBAAmB,GACxB,mBAAmB,CA8CrB"}
1
+ {"version":3,"file":"schema-registrar.d.ts","sourceRoot":"","sources":["../src/schema-registrar.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACL,qBAAqB,EACrB,wBAAwB,EACxB,iBAAiB,EACjB,cAAc,EACd,KAAK,mBAAmB,GACzB,MAAM,2CAA2C,CAAC"}
@@ -1,61 +1,8 @@
1
1
  /**
2
- * Definition used for scopes that carry unstructured/binary data. Nothing in
3
- * this stack fetches the definitionUrl — it only needs to be a stable string —
4
- * but consuming builders may dereference it, so it points at a permissive doc.
2
+ * The no-schema registrar now lives in core so the in-browser PS-Lite runtime
3
+ * can register schemas for binary uploads too (see
4
+ * `packages/core/src/api/schema-registrar.ts`). Re-exported here for backwards
5
+ * compatibility with existing server imports.
5
6
  */
6
- export const NO_SCHEMA_NAME = "No Schema";
7
- export const NO_SCHEMA_DEFINITION_URL = "https://schemas.vana.org/no-schema/v1.json";
8
- export const NO_SCHEMA_DIALECT = "none";
9
- function extractSchemaId(body) {
10
- return (body.schemaId ??
11
- body.id ??
12
- body.data?.schemaId ??
13
- body.data?.id ??
14
- undefined);
15
- }
16
- /**
17
- * Registers (idempotently, by scope) a permissive "no-schema" schema with the
18
- * gateway via POST /v1/schemas, signing an EIP-712 SchemaRegistration message.
19
- *
20
- * The gateway requires the EIP-712 signer to equal the registered ownerAddress
21
- * (no server-delegation branch for schemas), so the schema is owned by the
22
- * server signing account.
23
- */
24
- export function createSchemaRegistrar(deps) {
25
- const base = deps.gatewayUrl.replace(/\/+$/, "");
26
- return {
27
- async registerNoSchema(scope) {
28
- const ownerAddress = deps.signer.address;
29
- const message = {
30
- ownerAddress,
31
- name: NO_SCHEMA_NAME,
32
- definitionUrl: NO_SCHEMA_DEFINITION_URL,
33
- scope,
34
- dialect: NO_SCHEMA_DIALECT,
35
- };
36
- const signature = await deps.signer.signSchemaRegistration(message);
37
- const res = await fetch(`${base}/v1/schemas`, {
38
- method: "POST",
39
- headers: {
40
- "Content-Type": "application/json",
41
- Authorization: `Web3Signed ${signature}`,
42
- },
43
- body: JSON.stringify(message),
44
- });
45
- if (!res.ok) {
46
- const detail = await res.text().catch(() => res.statusText);
47
- throw new Error(`Schema registration failed: ${res.status} ${detail}`);
48
- }
49
- const body = (await res
50
- .json()
51
- .catch(() => ({})));
52
- const schemaId = extractSchemaId(body);
53
- if (!schemaId) {
54
- throw new Error(`Schema registration for "${scope}" did not return a schemaId`);
55
- }
56
- deps.logger?.info({ scope, schemaId }, "Registered no-schema schema with gateway");
57
- return { schemaId, definitionUrl: NO_SCHEMA_DEFINITION_URL };
58
- },
59
- };
60
- }
7
+ export { createSchemaRegistrar, NO_SCHEMA_DEFINITION_URL, NO_SCHEMA_DIALECT, NO_SCHEMA_NAME, } from "@opendatalabs/personal-server-ts-core/api";
61
8
  //# sourceMappingURL=schema-registrar.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"schema-registrar.js","sourceRoot":"","sources":["../src/schema-registrar.ts"],"names":[],"mappings":"AAOA;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,WAAW,CAAC;AAC1C,MAAM,CAAC,MAAM,wBAAwB,GACnC,4CAA4C,CAAC;AAC/C,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC;AAcxC,SAAS,eAAe,CACtB,IAAoC;IAEpC,OAAO,CACL,IAAI,CAAC,QAAQ;QACb,IAAI,CAAC,EAAE;QACP,IAAI,CAAC,IAAI,EAAE,QAAQ;QACnB,IAAI,CAAC,IAAI,EAAE,EAAE;QACb,SAAS,CACV,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CACnC,IAAyB;IAEzB,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAEjD,OAAO;QACL,KAAK,CAAC,gBAAgB,CAAC,KAAa;YAClC,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC;YACzC,MAAM,OAAO,GAAG;gBACd,YAAY;gBACZ,IAAI,EAAE,cAAc;gBACpB,aAAa,EAAE,wBAAwB;gBACvC,KAAK;gBACL,OAAO,EAAE,iBAAiB;aAC3B,CAAC;YACF,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC;YAEpE,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,aAAa,EAAE;gBAC5C,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,cAAc,EAAE,kBAAkB;oBAClC,aAAa,EAAE,cAAc,SAAS,EAAE;iBACzC;gBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;aAC9B,CAAC,CAAC;YAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;gBACZ,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;gBAC5D,MAAM,IAAI,KAAK,CAAC,+BAA+B,GAAG,CAAC,MAAM,IAAI,MAAM,EAAE,CAAC,CAAC;YACzE,CAAC;YAED,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG;iBACpB,IAAI,EAAE;iBACN,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAmC,CAAC;YACxD,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;YACvC,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,MAAM,IAAI,KAAK,CACb,4BAA4B,KAAK,6BAA6B,CAC/D,CAAC;YACJ,CAAC;YAED,IAAI,CAAC,MAAM,EAAE,IAAI,CACf,EAAE,KAAK,EAAE,QAAQ,EAAE,EACnB,0CAA0C,CAC3C,CAAC;YACF,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,wBAAwB,EAAE,CAAC;QAC/D,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"schema-registrar.js","sourceRoot":"","sources":["../src/schema-registrar.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACL,qBAAqB,EACrB,wBAAwB,EACxB,iBAAiB,EACjB,cAAc,GAEf,MAAM,2CAA2C,CAAC"}
@@ -105446,6 +105446,12 @@ function deleteMapPrefix(map2, pathPrefix) {
105446
105446
  }
105447
105447
  }
105448
105448
  }
105449
+ function describeStorageError(error51) {
105450
+ if (error51 instanceof Error) {
105451
+ return { errorClass: error51.name, message: error51.message };
105452
+ }
105453
+ return { errorClass: typeof error51, message: String(error51) };
105454
+ }
105449
105455
  async function getOrCreateOpfsDirectory(root, parts) {
105450
105456
  let dir = root;
105451
105457
  for (const part of parts) {
@@ -105640,8 +105646,8 @@ async function createPersistentPsLiteStorage(adapter, persistence = createIndexe
105640
105646
  const snapshot = {
105641
105647
  ...state,
105642
105648
  envelopes: fileStore.kind === "indexeddb" ? Array.from(fallbackEnvelopes.values()) : [],
105643
- blockManifests: fileStore.kind === "indexeddb" ? Array.from(fallbackBlockManifests.entries()).map(([path, manifest]) => ({ path, manifest })) : [],
105644
- blockPayloads: fileStore.kind === "indexeddb" ? Array.from(fallbackBlockPayloads.entries()).map(([path, block]) => ({ path, block })) : []
105649
+ blockManifests: Array.from(fallbackBlockManifests.entries()).map(([path, manifest]) => ({ path, manifest })),
105650
+ blockPayloads: Array.from(fallbackBlockPayloads.entries()).map(([path, block]) => ({ path, block }))
105645
105651
  };
105646
105652
  const write = persistQueue.then(() => persistence.write(snapshot));
105647
105653
  persistQueue = write.catch(() => void 0);
@@ -105749,15 +105755,34 @@ async function createPersistentPsLiteStorage(adapter, persistence = createIndexe
105749
105755
  return Boolean(manifest);
105750
105756
  },
105751
105757
  async writeBlockManifest(scope, collectedAt2, manifest, blocks) {
105752
- if (!fileStore.writeBlockManifest || !fileStore.writeBlockPayload) {
105753
- throw new Error("Block sidecar storage is not available");
105754
- }
105755
- await fileStore.deleteBlockTree?.(blockTreePath(scope, collectedAt2));
105756
- for (const block of blocks) {
105757
- await fileStore.writeBlockPayload(blockPayloadPath(scope, collectedAt2, block.id), block);
105758
- }
105759
- await fileStore.writeBlockManifest(blockManifestPath(scope, collectedAt2), manifest);
105760
- if (fileStore.kind !== "indexeddb") {
105758
+ const writeTree = async (store) => {
105759
+ if (!store.writeBlockManifest || !store.writeBlockPayload) {
105760
+ throw new Error("Block sidecar storage is not available");
105761
+ }
105762
+ await store.deleteBlockTree?.(blockTreePath(scope, collectedAt2));
105763
+ for (const block of blocks) {
105764
+ await store.writeBlockPayload(blockPayloadPath(scope, collectedAt2, block.id), block);
105765
+ }
105766
+ await store.writeBlockManifest(blockManifestPath(scope, collectedAt2), manifest);
105767
+ };
105768
+ let wrotePrimary = false;
105769
+ try {
105770
+ await writeTree(fileStore);
105771
+ wrotePrimary = true;
105772
+ } catch (error51) {
105773
+ if (fileStore === fallbackStore) {
105774
+ throw error51;
105775
+ }
105776
+ console.warn({
105777
+ scope,
105778
+ collectedAt: collectedAt2,
105779
+ fileStoreKind: fileStore.kind,
105780
+ fallbackStoreKind: fallbackStore.kind,
105781
+ ...describeStorageError(error51)
105782
+ }, "Primary block sidecar write failed; using IndexedDB fallback");
105783
+ await writeTree(fallbackStore);
105784
+ }
105785
+ if (wrotePrimary && fileStore.kind !== "indexeddb") {
105761
105786
  await fallbackStore.deleteBlockTree?.(blockTreePath(scope, collectedAt2));
105762
105787
  }
105763
105788
  await persist();
@@ -107803,6 +107828,49 @@ async function syncFileContract(input) {
107803
107828
  return contractOk({ fileId: input.fileId, status: "started" }, 202);
107804
107829
  }
107805
107830
 
107831
+ // ../core/dist/api/schema-registrar.js
107832
+ var NO_SCHEMA_NAME = "No Schema";
107833
+ var NO_SCHEMA_DEFINITION_URL = "https://schemas.vana.org/no-schema/v1.json";
107834
+ var NO_SCHEMA_DIALECT = "none";
107835
+ function extractSchemaId(body) {
107836
+ return body.schemaId ?? body.id ?? body.data?.schemaId ?? body.data?.id ?? void 0;
107837
+ }
107838
+ function createSchemaRegistrar(deps) {
107839
+ const base = deps.gatewayUrl.replace(/\/+$/, "");
107840
+ return {
107841
+ async registerNoSchema(scope) {
107842
+ const ownerAddress = deps.signer.address;
107843
+ const message = {
107844
+ ownerAddress,
107845
+ name: NO_SCHEMA_NAME,
107846
+ definitionUrl: NO_SCHEMA_DEFINITION_URL,
107847
+ scope,
107848
+ dialect: NO_SCHEMA_DIALECT
107849
+ };
107850
+ const signature = await deps.signer.signSchemaRegistration(message);
107851
+ const res = await fetch(`${base}/v1/schemas`, {
107852
+ method: "POST",
107853
+ headers: {
107854
+ "Content-Type": "application/json",
107855
+ Authorization: `Web3Signed ${signature}`
107856
+ },
107857
+ body: JSON.stringify(message)
107858
+ });
107859
+ if (!res.ok) {
107860
+ const detail = await res.text().catch(() => res.statusText);
107861
+ throw new Error(`Schema registration failed: ${res.status} ${detail}`);
107862
+ }
107863
+ const body = await res.json().catch(() => ({}));
107864
+ const schemaId = extractSchemaId(body);
107865
+ if (!schemaId) {
107866
+ throw new Error(`Schema registration for "${scope}" did not return a schemaId`);
107867
+ }
107868
+ deps.logger?.info({ scope, schemaId }, "Registered no-schema schema with gateway");
107869
+ return { schemaId, definitionUrl: NO_SCHEMA_DEFINITION_URL };
107870
+ }
107871
+ };
107872
+ }
107873
+
107806
107874
  // ../core/dist/api/index.js
107807
107875
  function jsonResponse(body, init) {
107808
107876
  const headers = new Headers(init?.headers);
@@ -110827,6 +110895,23 @@ function uniqueSources(connection) {
110827
110895
  }
110828
110896
  return Array.from(set2).sort();
110829
110897
  }
110898
+ function normalizeScopeListInput(value) {
110899
+ if (!Array.isArray(value))
110900
+ return [];
110901
+ const scopes = [];
110902
+ const seen = /* @__PURE__ */ new Set();
110903
+ for (const raw of value) {
110904
+ if (typeof raw !== "string")
110905
+ continue;
110906
+ const scope = raw.trim();
110907
+ if (!scope || scope.length > SEARCH_SCOPE_MAX_CHARS || seen.has(scope)) {
110908
+ continue;
110909
+ }
110910
+ seen.add(scope);
110911
+ scopes.push(scope);
110912
+ }
110913
+ return scopes;
110914
+ }
110830
110915
  var DEFAULT_SEARCH_LIMIT = 5;
110831
110916
  var MAX_SEARCH_LIMIT = 50;
110832
110917
  var MAX_SEARCH_SCOPES = 200;
@@ -111189,6 +111274,36 @@ var listGrantedScopes = {
111189
111274
  return textResult({ scopes: scopeEntries });
111190
111275
  }
111191
111276
  };
111277
+ var requestScopeAccess = {
111278
+ name: "request_scope_access",
111279
+ title: "Request scope access",
111280
+ description: "Check which requested scopes are missing from this MCP grant and tell the user to approve them in Vana.",
111281
+ inputSchema: {
111282
+ scopes: external_exports.array(external_exports.string().min(1).max(SEARCH_SCOPE_MAX_CHARS)).min(1).max(SEARCH_REQUESTED_SCOPES_LIMIT).describe("Exact scope ids the MCP client wants to read."),
111283
+ reason: external_exports.string().max(500).optional().describe("Brief reason to show the user.")
111284
+ },
111285
+ async handler(args, { connection }) {
111286
+ const requestedScopes = normalizeScopeListInput(args.scopes);
111287
+ const grantedRequestedScopes = requestedScopes.filter((scope) => Boolean(resolveGrantForScope(connection, scope)));
111288
+ const missingScopes = requestedScopes.filter((scope) => !resolveGrantForScope(connection, scope));
111289
+ const reason = typeof args.reason === "string" ? args.reason.trim() : "";
111290
+ const approvalRequired = missingScopes.length > 0;
111291
+ return textResult({
111292
+ approvalRequired,
111293
+ client: {
111294
+ id: connection.id,
111295
+ displayName: connection.displayName,
111296
+ status: connection.status
111297
+ },
111298
+ requestedScopes,
111299
+ grantedRequestedScopes,
111300
+ missingScopes,
111301
+ grantedScopes: uniqueScopes(connection),
111302
+ reason: reason || void 0,
111303
+ nextAction: approvalRequired ? "Ask the user to open Vana's Personal Server page and add the missing scopes to this MCP client. This tool cannot grant access by itself." : "No new grant is needed for the requested scopes."
111304
+ });
111305
+ }
111306
+ };
111192
111307
  var readScope = {
111193
111308
  name: "read_scope",
111194
111309
  title: "Read scope",
@@ -111661,6 +111776,7 @@ var getScopeFile = {
111661
111776
  var MCP_TOOLS = [
111662
111777
  listGrantedSources,
111663
111778
  listGrantedScopes,
111779
+ requestScopeAccess,
111664
111780
  readScope,
111665
111781
  getScopeFile,
111666
111782
  searchPersonalContext
@@ -123177,6 +123293,15 @@ function createPsLiteRuntime(options) {
123177
123293
  }
123178
123294
  const tokenStore = options.tokenStore ?? createDefaultTokenStore();
123179
123295
  const saveConfig = options.saveConfig ?? createDefaultSaveConfig();
123296
+ const schemaRegistrarSigner = options.serverSigner;
123297
+ const schemaRegistrarGatewayUrl = options.config?.gateway?.url;
123298
+ const schemaRegistrar = schemaRegistrarSigner?.signSchemaRegistration && schemaRegistrarSigner.address && schemaRegistrarGatewayUrl ? createSchemaRegistrar({
123299
+ gatewayUrl: schemaRegistrarGatewayUrl,
123300
+ signer: {
123301
+ address: schemaRegistrarSigner.address,
123302
+ signSchemaRegistration: schemaRegistrarSigner.signSchemaRegistration
123303
+ }
123304
+ }) : void 0;
123180
123305
  const deviceSessions = createMemoryDeviceSessionStore();
123181
123306
  const mcpOAuthAuthorizationStore = options.mcpOAuthAuthorizationStore ?? createInMemoryMcpOAuthAuthorizationStore();
123182
123307
  const activityRecorder = options.mcpConnectionStore ? new McpActivityRecorder() : void 0;
@@ -123389,6 +123514,7 @@ function createPsLiteRuntime(options) {
123389
123514
  storage: dataStorage,
123390
123515
  auth,
123391
123516
  schemaResolver: options.gateway,
123517
+ schemaRegistrar,
123392
123518
  accessLogWriter,
123393
123519
  syncManager: options.syncManager ?? null,
123394
123520
  now,
@@ -124043,9 +124169,17 @@ async function downloadOne(deps, record2) {
124043
124169
  const { storage, storageAdapter, gateway, masterKey, logger, diagnostics } = deps;
124044
124170
  const existing = storage.findByFileId(record2.fileId);
124045
124171
  if (existing) {
124046
- await repairMissingBlockSidecars(storage, logger, record2, existing, diagnostics);
124047
- logger.debug({ fileId: record2.fileId }, "File already in index, skipping");
124048
- return null;
124172
+ const repairResult = await repairMissingBlockSidecars(storage, logger, record2, existing, diagnostics);
124173
+ if (repairResult === "missing-envelope") {
124174
+ logger.warn({
124175
+ fileId: record2.fileId,
124176
+ scope: existing.scope,
124177
+ collectedAt: existing.collectedAt
124178
+ }, "Local index entry was missing its envelope; re-downloading file");
124179
+ } else {
124180
+ logger.debug({ fileId: record2.fileId }, "File already in index, skipping");
124181
+ return null;
124182
+ }
124049
124183
  }
124050
124184
  diagnostics?.onDownloadStart(record2.fileId);
124051
124185
  let encrypted;
@@ -124101,17 +124235,24 @@ async function downloadOne(deps, record2) {
124101
124235
  if (existingByVersion.fileId !== record2.fileId) {
124102
124236
  await storage.updateFileId(existingByVersion.path, record2.fileId);
124103
124237
  }
124104
- await repairMissingBlockSidecars(storage, logger, record2, {
124238
+ const repairResult = await repairMissingBlockSidecars(storage, logger, record2, {
124105
124239
  ...existingByVersion,
124106
124240
  fileId: record2.fileId,
124107
124241
  schemaId: existingByVersion.schemaId ?? record2.schemaId
124108
124242
  }, diagnostics);
124109
- logger.debug({
124243
+ if (repairResult !== "missing-envelope") {
124244
+ logger.debug({
124245
+ fileId: record2.fileId,
124246
+ scope: envelope.scope,
124247
+ collectedAt: envelope.collectedAt
124248
+ }, "File version already exists locally, skipping");
124249
+ return null;
124250
+ }
124251
+ logger.warn({
124110
124252
  fileId: record2.fileId,
124111
124253
  scope: envelope.scope,
124112
124254
  collectedAt: envelope.collectedAt
124113
- }, "File version already exists locally, skipping");
124114
- return null;
124255
+ }, "Local version entry was missing its envelope; rewriting downloaded file");
124115
124256
  }
124116
124257
  diagnostics?.onIndexStart(record2.fileId, envelope.scope);
124117
124258
  let relativePath;
@@ -124150,8 +124291,11 @@ async function downloadOne(deps, record2) {
124150
124291
  async function downloadAll(deps) {
124151
124292
  const { gateway, cursor, serverOwner, logger } = deps;
124152
124293
  const syncRunId = createSyncRunId();
124153
- await repairLocalMissingBlockSidecars(deps);
124154
- const lastProcessedTimestamp = await cursor.read();
124294
+ const repairSummary = await repairLocalMissingBlockSidecars(deps);
124295
+ const lastProcessedTimestamp = repairSummary.missingEnvelopeEntries > 0 ? null : await cursor.read();
124296
+ if (repairSummary.missingEnvelopeEntries > 0) {
124297
+ logger.warn({ missingEnvelopeEntries: repairSummary.missingEnvelopeEntries }, "Resetting this sync listing to repair stale local index entries");
124298
+ }
124155
124299
  const { files, cursor: nextCursor } = await gateway.listFilesSince(serverOwner, lastProcessedTimestamp, { includeDeleted: true });
124156
124300
  const results = [];
124157
124301
  let failed = false;
@@ -124210,13 +124354,16 @@ async function downloadAll(deps) {
124210
124354
  }
124211
124355
  async function repairLocalMissingBlockSidecars(deps) {
124212
124356
  const { storage, logger, diagnostics } = deps;
124213
- if (!storage.writeBlockManifest || !storage.hasScopeBlocks)
124214
- return 0;
124357
+ if (!storage.writeBlockManifest || !storage.hasScopeBlocks) {
124358
+ return { repaired: 0, missingEnvelopeEntries: 0 };
124359
+ }
124215
124360
  let repaired = 0;
124361
+ let missingEnvelopeEntries = 0;
124216
124362
  let offset = 0;
124217
124363
  const limit = 100;
124218
124364
  while (true) {
124219
124365
  const page = storage.listScopes({ limit, offset });
124366
+ let deletedEntryInPage = false;
124220
124367
  for (const scopeSummary of page.scopes) {
124221
124368
  const entry = storage.findEntry({
124222
124369
  scope: scopeSummary.scope,
@@ -124224,24 +124371,33 @@ async function repairLocalMissingBlockSidecars(deps) {
124224
124371
  });
124225
124372
  if (!entry)
124226
124373
  continue;
124227
- const repairedScope = await repairMissingBlockSidecars(storage, logger, {
124374
+ const repairResult = await repairMissingBlockSidecars(storage, logger, {
124228
124375
  fileId: entry.fileId ?? entry.path,
124229
124376
  owner: "",
124230
124377
  url: "",
124231
124378
  schemaId: entry.schemaId ?? "",
124232
124379
  createdAt: entry.createdAt
124233
124380
  }, entry, diagnostics);
124234
- if (repairedScope)
124381
+ if (repairResult === "repaired")
124235
124382
  repaired += 1;
124383
+ if (repairResult === "missing-envelope") {
124384
+ missingEnvelopeEntries += 1;
124385
+ deletedEntryInPage = true;
124386
+ }
124387
+ }
124388
+ if (!deletedEntryInPage) {
124389
+ offset += page.scopes.length;
124236
124390
  }
124237
- offset += page.scopes.length;
124238
124391
  if (offset >= page.total || page.scopes.length === 0)
124239
124392
  break;
124240
124393
  }
124241
124394
  if (repaired > 0) {
124242
124395
  logger.info({ repaired }, "Repaired missing bounded block sidecars for local indexed data");
124243
124396
  }
124244
- return repaired;
124397
+ if (missingEnvelopeEntries > 0) {
124398
+ logger.warn({ missingEnvelopeEntries }, "Removed stale local index entries missing local envelopes");
124399
+ }
124400
+ return { repaired, missingEnvelopeEntries };
124245
124401
  }
124246
124402
  function createSyncRunId() {
124247
124403
  return typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? crypto.randomUUID() : `download-${Date.now().toString(36)}`;
@@ -124299,8 +124455,9 @@ async function writeBlockSidecars2(storage, envelope, logger, record2, relativeP
124299
124455
  }
124300
124456
  }
124301
124457
  async function repairMissingBlockSidecars(storage, logger, record2, entry, diagnostics) {
124302
- if (!storage.writeBlockManifest || !storage.hasScopeBlocks)
124303
- return false;
124458
+ if (!storage.writeBlockManifest || !storage.hasScopeBlocks) {
124459
+ return "not-repaired";
124460
+ }
124304
124461
  let hasBlocks = false;
124305
124462
  try {
124306
124463
  hasBlocks = await storage.hasScopeBlocks(entry.scope, entry.collectedAt);
@@ -124311,10 +124468,10 @@ async function repairMissingBlockSidecars(storage, logger, record2, entry, diagn
124311
124468
  collectedAt: entry.collectedAt,
124312
124469
  error: error51.message
124313
124470
  }, "Bounded block sidecar repair skipped after readiness check failed");
124314
- return false;
124471
+ return "not-repaired";
124315
124472
  }
124316
124473
  if (hasBlocks)
124317
- return false;
124474
+ return "already-ready";
124318
124475
  diagnostics?.onRepair(entry.scope, "indexed entry missing block manifest; rebuilding from local envelope");
124319
124476
  let envelope;
124320
124477
  try {
@@ -124326,7 +124483,18 @@ async function repairMissingBlockSidecars(storage, logger, record2, entry, diagn
124326
124483
  collectedAt: entry.collectedAt,
124327
124484
  error: error51.message
124328
124485
  }, "Bounded block sidecar repair skipped because local envelope is unavailable");
124329
- return false;
124486
+ if (entry.fileId) {
124487
+ diagnostics?.onRepair(entry.scope, "index exists without local payload; removing stale local index entry");
124488
+ const removed = await storage.deleteByFileId(entry.fileId);
124489
+ logger.warn({
124490
+ fileId: entry.fileId,
124491
+ scope: entry.scope,
124492
+ collectedAt: entry.collectedAt,
124493
+ removed
124494
+ }, "Removed stale local index entry missing local envelope");
124495
+ return removed ? "missing-envelope" : "not-repaired";
124496
+ }
124497
+ return "not-repaired";
124330
124498
  }
124331
124499
  await writeBlockSidecars2(storage, envelope, logger, {
124332
124500
  ...record2,
@@ -124334,7 +124502,7 @@ async function repairMissingBlockSidecars(storage, logger, record2, entry, diagn
124334
124502
  schemaId: entry.schemaId ?? record2.schemaId
124335
124503
  }, entry.path, diagnostics);
124336
124504
  try {
124337
- return await storage.hasScopeBlocks(entry.scope, entry.collectedAt);
124505
+ return await storage.hasScopeBlocks(entry.scope, entry.collectedAt) ? "repaired" : "not-repaired";
124338
124506
  } catch (error51) {
124339
124507
  logger.warn({
124340
124508
  fileId: entry.fileId ?? record2.fileId,
@@ -124342,7 +124510,7 @@ async function repairMissingBlockSidecars(storage, logger, record2, entry, diagn
124342
124510
  collectedAt: entry.collectedAt,
124343
124511
  error: error51.message
124344
124512
  }, "Bounded block sidecar repair completed but readiness could not be verified");
124345
- return false;
124513
+ return "not-repaired";
124346
124514
  }
124347
124515
  }
124348
124516
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opendatalabs/personal-server-ts-server",
3
- "version": "0.0.1-canary.de6bc6f",
3
+ "version": "0.0.1-canary.dfa1648",
4
4
  "description": "Hono HTTP server for the Vana Personal Server — routes, middleware, composition root",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -44,8 +44,8 @@
44
44
  },
45
45
  "dependencies": {
46
46
  "@hono/node-server": "^1.19.13",
47
- "@opendatalabs/personal-server-ts-core": "0.0.1-canary.de6bc6f",
48
- "@opendatalabs/personal-server-ts-lite": "0.0.1-canary.de6bc6f",
47
+ "@opendatalabs/personal-server-ts-core": "0.0.1-canary.dfa1648",
48
+ "@opendatalabs/personal-server-ts-lite": "0.0.1-canary.dfa1648",
49
49
  "@opendatalabs/vana-sdk": "^3.4.1",
50
50
  "better-sqlite3": "^12.9.0",
51
51
  "hono": "^4.12.18",