@opendatalabs/personal-server-ts-server 1.13.1 → 1.14.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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"question-store.d.ts","sourceRoot":"","sources":["../../src/storage/question-store.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,
|
|
1
|
+
{"version":3,"file":"question-store.d.ts","sourceRoot":"","sources":["../../src/storage/question-store.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAKL,KAAK,aAAa,EACnB,MAAM,mDAAmD,CAAC;AAgF3D,wBAAgB,yBAAyB,CACvC,EAAE,EAAE,QAAQ,CAAC,QAAQ,GACpB,aAAa,CAmGf"}
|
|
@@ -15,12 +15,26 @@ CREATE TABLE IF NOT EXISTS derivative_questions (
|
|
|
15
15
|
registered_by TEXT NOT NULL,
|
|
16
16
|
status TEXT NOT NULL,
|
|
17
17
|
error TEXT,
|
|
18
|
+
error_code TEXT,
|
|
18
19
|
created_at TEXT NOT NULL,
|
|
19
20
|
updated_at TEXT NOT NULL,
|
|
20
21
|
last_computed_at TEXT,
|
|
21
22
|
derived_version INTEGER,
|
|
22
23
|
derived_collected_at TEXT
|
|
23
24
|
)`;
|
|
25
|
+
/**
|
|
26
|
+
* Databases created before the column existed migrate in place. ALTER is
|
|
27
|
+
* guarded by a pragma check because SQLite has no ADD COLUMN IF NOT EXISTS;
|
|
28
|
+
* existing rows read back as null (pre-classification failures).
|
|
29
|
+
*/
|
|
30
|
+
function ensureErrorCodeColumn(db) {
|
|
31
|
+
const columns = db
|
|
32
|
+
.prepare("PRAGMA table_info(derivative_questions)")
|
|
33
|
+
.all();
|
|
34
|
+
if (columns.some((column) => column.name === "error_code"))
|
|
35
|
+
return;
|
|
36
|
+
db.exec("ALTER TABLE derivative_questions ADD COLUMN error_code TEXT");
|
|
37
|
+
}
|
|
24
38
|
const CREATE_INDEX_SQL = "CREATE INDEX IF NOT EXISTS idx_derivative_questions_derived_scope ON derivative_questions (derived_scope)";
|
|
25
39
|
// Databases created before the recompute policy existed migrate in place;
|
|
26
40
|
// their registrations keep the old follow-every-change behavior.
|
|
@@ -36,6 +50,7 @@ function toRegistration(row) {
|
|
|
36
50
|
registeredBy: JSON.parse(row.registered_by),
|
|
37
51
|
status: row.status,
|
|
38
52
|
error: row.error,
|
|
53
|
+
errorCode: row.error_code ?? null,
|
|
39
54
|
createdAt: row.created_at,
|
|
40
55
|
updatedAt: row.updated_at,
|
|
41
56
|
lastComputedAt: row.last_computed_at,
|
|
@@ -45,6 +60,7 @@ function toRegistration(row) {
|
|
|
45
60
|
}
|
|
46
61
|
export function createSqliteQuestionStore(db) {
|
|
47
62
|
db.exec(CREATE_TABLE_SQL);
|
|
63
|
+
ensureErrorCodeColumn(db);
|
|
48
64
|
db.exec(CREATE_INDEX_SQL);
|
|
49
65
|
const columns = db
|
|
50
66
|
.prepare("PRAGMA table_info(derivative_questions)")
|
|
@@ -53,21 +69,25 @@ export function createSqliteQuestionStore(db) {
|
|
|
53
69
|
db.exec(ADD_RECOMPUTE_COLUMN_SQL);
|
|
54
70
|
}
|
|
55
71
|
const listAll = db.prepare("SELECT * FROM derivative_questions ORDER BY created_at ASC, question_id ASC");
|
|
72
|
+
// The status route polls list({ derivedScope }) on every reader request;
|
|
73
|
+
// this is the query idx_derivative_questions_derived_scope exists for.
|
|
74
|
+
const listByDerivedScope = db.prepare("SELECT * FROM derivative_questions WHERE derived_scope = ? ORDER BY created_at ASC, question_id ASC");
|
|
56
75
|
const getOne = db.prepare("SELECT * FROM derivative_questions WHERE question_id = ?");
|
|
57
76
|
const insertOne = db.prepare(`
|
|
58
77
|
INSERT INTO derivative_questions (
|
|
59
78
|
question_id, derived_scope, source_scopes, question, model, recompute,
|
|
60
|
-
registered_by, status, error, created_at, updated_at,
|
|
79
|
+
registered_by, status, error, error_code, created_at, updated_at,
|
|
61
80
|
last_computed_at, derived_version, derived_collected_at
|
|
62
81
|
) VALUES (
|
|
63
82
|
@question_id, @derived_scope, @source_scopes, @question, @model,
|
|
64
|
-
@recompute, @registered_by, @status, @error, @created_at, @updated_at,
|
|
83
|
+
@recompute, @registered_by, @status, @error, @error_code, @created_at, @updated_at,
|
|
65
84
|
@last_computed_at, @derived_version, @derived_collected_at
|
|
66
85
|
)`);
|
|
67
86
|
const updateOne = db.prepare(`
|
|
68
87
|
UPDATE derivative_questions SET
|
|
69
88
|
status = @status,
|
|
70
89
|
error = @error,
|
|
90
|
+
error_code = @error_code,
|
|
71
91
|
updated_at = @updated_at,
|
|
72
92
|
last_computed_at = @last_computed_at,
|
|
73
93
|
derived_version = @derived_version,
|
|
@@ -76,7 +96,10 @@ export function createSqliteQuestionStore(db) {
|
|
|
76
96
|
const deleteOne = db.prepare("DELETE FROM derivative_questions WHERE question_id = ?");
|
|
77
97
|
return {
|
|
78
98
|
async list(filter) {
|
|
79
|
-
|
|
99
|
+
const rows = filter?.derivedScope !== undefined
|
|
100
|
+
? listByDerivedScope.all(filter.derivedScope)
|
|
101
|
+
: listAll.all();
|
|
102
|
+
return rows
|
|
80
103
|
.map(toRegistration)
|
|
81
104
|
.filter((registration) => matchesQuestionFilter(registration, filter));
|
|
82
105
|
},
|
|
@@ -95,6 +118,7 @@ export function createSqliteQuestionStore(db) {
|
|
|
95
118
|
registered_by: JSON.stringify(registration.registeredBy),
|
|
96
119
|
status: registration.status,
|
|
97
120
|
error: registration.error,
|
|
121
|
+
error_code: registration.errorCode,
|
|
98
122
|
created_at: registration.createdAt,
|
|
99
123
|
updated_at: registration.updatedAt,
|
|
100
124
|
last_computed_at: registration.lastComputedAt,
|
|
@@ -111,6 +135,7 @@ export function createSqliteQuestionStore(db) {
|
|
|
111
135
|
question_id: questionId,
|
|
112
136
|
status: merged.status,
|
|
113
137
|
error: merged.error,
|
|
138
|
+
error_code: merged.errorCode ?? null,
|
|
114
139
|
updated_at: merged.updatedAt,
|
|
115
140
|
last_computed_at: merged.lastComputedAt,
|
|
116
141
|
derived_version: merged.derivedVersion,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"question-store.js","sourceRoot":"","sources":["../../src/storage/question-store.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACL,qBAAqB,
|
|
1
|
+
{"version":3,"file":"question-store.js","sourceRoot":"","sources":["../../src/storage/question-store.ts"],"names":[],"mappings":"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"}
|
package/dist/ui/ps-lite-debug.js
CHANGED
|
@@ -114043,6 +114043,7 @@ function questionRegistrationView(registration) {
|
|
|
114043
114043
|
registeredBy: registration.registeredBy,
|
|
114044
114044
|
status: registration.status,
|
|
114045
114045
|
error: registration.error,
|
|
114046
|
+
errorCode: registration.errorCode,
|
|
114046
114047
|
createdAt: registration.createdAt,
|
|
114047
114048
|
updatedAt: registration.updatedAt,
|
|
114048
114049
|
lastComputedAt: registration.lastComputedAt,
|
|
@@ -114055,6 +114056,8 @@ function questionRegistrationView(registration) {
|
|
|
114055
114056
|
function clone2(registration) {
|
|
114056
114057
|
return {
|
|
114057
114058
|
...registration,
|
|
114059
|
+
// Registrations persisted before the field existed load as null.
|
|
114060
|
+
errorCode: registration.errorCode ?? null,
|
|
114058
114061
|
sourceScopes: [...registration.sourceScopes],
|
|
114059
114062
|
registeredBy: { ...registration.registeredBy }
|
|
114060
114063
|
};
|
|
@@ -114241,6 +114244,7 @@ async function createQuestionRegistration(input) {
|
|
|
114241
114244
|
registeredBy: input.registeredBy,
|
|
114242
114245
|
status: "pending",
|
|
114243
114246
|
error: null,
|
|
114247
|
+
errorCode: null,
|
|
114244
114248
|
createdAt: at3,
|
|
114245
114249
|
updatedAt: at3,
|
|
114246
114250
|
lastComputedAt: null,
|
|
@@ -114619,11 +114623,25 @@ async function withRetries(deps, attempt, retryable) {
|
|
|
114619
114623
|
}
|
|
114620
114624
|
}
|
|
114621
114625
|
var ComputeFailure = class extends Error {
|
|
114622
|
-
|
|
114626
|
+
code;
|
|
114627
|
+
constructor(message, code) {
|
|
114623
114628
|
super(message);
|
|
114624
114629
|
this.name = "ComputeFailure";
|
|
114630
|
+
this.code = code;
|
|
114625
114631
|
}
|
|
114626
114632
|
};
|
|
114633
|
+
function classifyComputeError(err2) {
|
|
114634
|
+
if (err2 instanceof ComputeFailure)
|
|
114635
|
+
return err2.code ?? "internal";
|
|
114636
|
+
if (err2 instanceof InferenceRequestError) {
|
|
114637
|
+
return isRetryableInferenceError(err2) ? "inference_unavailable" : "internal";
|
|
114638
|
+
}
|
|
114639
|
+
if (err2 instanceof ProtocolError) {
|
|
114640
|
+
const grantShaped = err2.errorCode === "DERIVATIVE_SOURCE_NOT_GRANTED" || err2.errorCode === "SCOPE_MISMATCH" || err2.errorCode === "UNREGISTERED_BUILDER" || err2.errorCode.startsWith("GRANT_");
|
|
114641
|
+
return grantShaped ? "grant_invalid" : "internal";
|
|
114642
|
+
}
|
|
114643
|
+
return "internal";
|
|
114644
|
+
}
|
|
114627
114645
|
function shortError(err2) {
|
|
114628
114646
|
if (err2 instanceof ComputeFailure)
|
|
114629
114647
|
return err2.message;
|
|
@@ -114708,10 +114726,10 @@ async function loadSource(deps, scope) {
|
|
|
114708
114726
|
const entry = deps.storage.findEntry({ scope });
|
|
114709
114727
|
const deletion = await resolveReadDeletion({ scopeDeletions: deps.scopeDeletions, serverOwner: deps.serverOwner }, scope, entry);
|
|
114710
114728
|
if (deletion) {
|
|
114711
|
-
throw new ComputeFailure(`source scope ${scope} is deleted
|
|
114729
|
+
throw new ComputeFailure(`source scope ${scope} is deleted`, "source_missing");
|
|
114712
114730
|
}
|
|
114713
114731
|
if (!entry) {
|
|
114714
|
-
throw new ComputeFailure(`source scope ${scope} has no local data
|
|
114732
|
+
throw new ComputeFailure(`source scope ${scope} has no local data`, "source_missing");
|
|
114715
114733
|
}
|
|
114716
114734
|
let envelope;
|
|
114717
114735
|
try {
|
|
@@ -114847,6 +114865,7 @@ async function computeQuestion(questionId, deps) {
|
|
|
114847
114865
|
const updated = await deps.store.update(questionId, {
|
|
114848
114866
|
status: "ready",
|
|
114849
114867
|
error: null,
|
|
114868
|
+
errorCode: null,
|
|
114850
114869
|
updatedAt: computedAt,
|
|
114851
114870
|
lastComputedAt: computedAt,
|
|
114852
114871
|
derivedVersion: entry?.version ?? null,
|
|
@@ -114884,28 +114903,49 @@ async function computeQuestion(questionId, deps) {
|
|
|
114884
114903
|
};
|
|
114885
114904
|
} catch (err2) {
|
|
114886
114905
|
const error51 = shortError(err2);
|
|
114906
|
+
const errorCode = classifyComputeError(err2);
|
|
114887
114907
|
const at3 = now().toISOString();
|
|
114888
114908
|
const updated = await deps.store.update(questionId, {
|
|
114889
114909
|
status: "failed",
|
|
114890
114910
|
error: error51,
|
|
114911
|
+
errorCode,
|
|
114891
114912
|
updatedAt: at3
|
|
114892
114913
|
});
|
|
114893
114914
|
deps.logger?.warn?.({ questionId, derivedScope: registration.derivedScope, error: error51 }, "Derivative question compute failed");
|
|
114894
114915
|
return {
|
|
114895
114916
|
status: "failed",
|
|
114896
|
-
registration: updated ?? {
|
|
114917
|
+
registration: updated ?? {
|
|
114918
|
+
...registration,
|
|
114919
|
+
status: "failed",
|
|
114920
|
+
error: error51,
|
|
114921
|
+
errorCode
|
|
114922
|
+
},
|
|
114897
114923
|
error: error51
|
|
114898
114924
|
};
|
|
114899
114925
|
}
|
|
114900
114926
|
}
|
|
114901
114927
|
|
|
114902
114928
|
// ../core/dist/derivatives/scheduler.js
|
|
114929
|
+
var DEFAULT_RETRY_DELAYS_MS2 = [6e4, 3e5, 18e5];
|
|
114930
|
+
function isRuntimeSkipOutcome(outcome) {
|
|
114931
|
+
if (typeof outcome !== "object" || outcome === null)
|
|
114932
|
+
return false;
|
|
114933
|
+
const shaped = outcome;
|
|
114934
|
+
return shaped.status === "skipped" && shaped.reason === "runtime-unavailable";
|
|
114935
|
+
}
|
|
114936
|
+
function isTransientFailureOutcome(outcome) {
|
|
114937
|
+
if (typeof outcome !== "object" || outcome === null)
|
|
114938
|
+
return false;
|
|
114939
|
+
const shaped = outcome;
|
|
114940
|
+
return shaped.status === "failed" && shaped.registration?.errorCode === "inference_unavailable";
|
|
114941
|
+
}
|
|
114903
114942
|
var defaultTimers = {
|
|
114904
114943
|
setTimeout: (callback, ms3) => setTimeout(callback, ms3),
|
|
114905
114944
|
clearTimeout: (handle) => clearTimeout(handle)
|
|
114906
114945
|
};
|
|
114907
114946
|
function createRecomputeScheduler(options) {
|
|
114908
114947
|
const debounceMs = options.debounceMs ?? 5e3;
|
|
114948
|
+
const retryDelaysMs = options.retryDelaysMs ?? DEFAULT_RETRY_DELAYS_MS2;
|
|
114909
114949
|
const timers = options.timers ?? defaultTimers;
|
|
114910
114950
|
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
114911
114951
|
const states = /* @__PURE__ */ new Map();
|
|
@@ -114924,7 +114964,15 @@ function createRecomputeScheduler(options) {
|
|
|
114924
114964
|
function stateFor(questionId) {
|
|
114925
114965
|
let state = states.get(questionId);
|
|
114926
114966
|
if (!state) {
|
|
114927
|
-
state = {
|
|
114967
|
+
state = {
|
|
114968
|
+
timer: null,
|
|
114969
|
+
running: null,
|
|
114970
|
+
rerun: false,
|
|
114971
|
+
retryAttempts: 0,
|
|
114972
|
+
retryAtMs: null,
|
|
114973
|
+
retryScheduled: false,
|
|
114974
|
+
retryRunning: false
|
|
114975
|
+
};
|
|
114928
114976
|
states.set(questionId, state);
|
|
114929
114977
|
}
|
|
114930
114978
|
return state;
|
|
@@ -114937,17 +114985,42 @@ function createRecomputeScheduler(options) {
|
|
|
114937
114985
|
state.rerun = true;
|
|
114938
114986
|
return;
|
|
114939
114987
|
}
|
|
114940
|
-
state.running = track(Promise.resolve().then(() => options.compute(questionId)).then(() =>
|
|
114941
|
-
|
|
114942
|
-
|
|
114943
|
-
|
|
114988
|
+
state.running = track(Promise.resolve().then(() => options.compute(questionId)).then((outcome) => outcome, (err2) => {
|
|
114989
|
+
warn({
|
|
114990
|
+
questionId,
|
|
114991
|
+
error: err2 instanceof Error ? err2.name : String(err2)
|
|
114992
|
+
}, "Derivative compute threw");
|
|
114993
|
+
return void 0;
|
|
114994
|
+
}).then(async (outcome) => {
|
|
114944
114995
|
state.running = null;
|
|
114996
|
+
state.retryAtMs = null;
|
|
114997
|
+
state.retryRunning = false;
|
|
114998
|
+
const retryable = isTransientFailureOutcome(outcome) || // A runtime-unavailable skip DURING a chain consumes an attempt
|
|
114999
|
+
// instead of ending the chain; on a fresh compute it keeps the
|
|
115000
|
+
// pre-retry behavior (pending/stale, reswept by start()).
|
|
115001
|
+
isRuntimeSkipOutcome(outcome) && state.retryAttempts > 0;
|
|
114945
115002
|
if (state.rerun) {
|
|
114946
115003
|
state.rerun = false;
|
|
115004
|
+
state.retryAttempts = 0;
|
|
114947
115005
|
await markStale(questionId);
|
|
114948
115006
|
schedule(questionId, 0);
|
|
114949
|
-
} else if (
|
|
114950
|
-
|
|
115007
|
+
} else if (retryable && // A pending timer means a source change or explicit recompute
|
|
115008
|
+
// arrived during the run and already scheduled a fresher run;
|
|
115009
|
+
// replacing its 5s debounce with a 60s retry would delay the
|
|
115010
|
+
// recompute of data this compute never saw.
|
|
115011
|
+
state.timer === null && // After stop() nothing may be scheduled; without this a compute
|
|
115012
|
+
// that settles late would record a retryAtMs no timer backs.
|
|
115013
|
+
!stopped && state.retryAttempts < retryDelaysMs.length) {
|
|
115014
|
+
const delayMs = retryDelaysMs[state.retryAttempts];
|
|
115015
|
+
state.retryAttempts += 1;
|
|
115016
|
+
state.retryAtMs = now().getTime() + delayMs;
|
|
115017
|
+
schedule(questionId, delayMs);
|
|
115018
|
+
state.retryScheduled = true;
|
|
115019
|
+
} else {
|
|
115020
|
+
state.retryAttempts = 0;
|
|
115021
|
+
if (!states.get(questionId)?.timer) {
|
|
115022
|
+
states.delete(questionId);
|
|
115023
|
+
}
|
|
114951
115024
|
}
|
|
114952
115025
|
}));
|
|
114953
115026
|
}
|
|
@@ -114959,6 +115032,9 @@ function createRecomputeScheduler(options) {
|
|
|
114959
115032
|
timers.clearTimeout(state.timer);
|
|
114960
115033
|
state.timer = timers.setTimeout(() => {
|
|
114961
115034
|
state.timer = null;
|
|
115035
|
+
state.retryAtMs = null;
|
|
115036
|
+
state.retryRunning = state.retryScheduled;
|
|
115037
|
+
state.retryScheduled = false;
|
|
114962
115038
|
run(questionId);
|
|
114963
115039
|
}, delayMs);
|
|
114964
115040
|
}
|
|
@@ -114969,11 +115045,29 @@ function createRecomputeScheduler(options) {
|
|
|
114969
115045
|
if (registration.status === "ready" || registration.status === "failed") {
|
|
114970
115046
|
await options.store.update(questionId, {
|
|
114971
115047
|
status: "stale",
|
|
115048
|
+
// The reader contract says errorCode is null unless failed; a stale
|
|
115049
|
+
// question is a recompute in progress, not a terminal failure.
|
|
115050
|
+
errorCode: null,
|
|
114972
115051
|
updatedAt: now().toISOString()
|
|
114973
115052
|
});
|
|
114974
115053
|
}
|
|
114975
115054
|
}
|
|
115055
|
+
function resetRetry(questionId) {
|
|
115056
|
+
const state = states.get(questionId);
|
|
115057
|
+
if (!state)
|
|
115058
|
+
return;
|
|
115059
|
+
state.retryAttempts = 0;
|
|
115060
|
+
state.retryAtMs = null;
|
|
115061
|
+
state.retryScheduled = false;
|
|
115062
|
+
}
|
|
114976
115063
|
return {
|
|
115064
|
+
nextRetryAt(questionId) {
|
|
115065
|
+
const retryAtMs = states.get(questionId)?.retryAtMs;
|
|
115066
|
+
return retryAtMs == null ? null : new Date(retryAtMs).toISOString();
|
|
115067
|
+
},
|
|
115068
|
+
retryInFlight(questionId) {
|
|
115069
|
+
return states.get(questionId)?.retryRunning ?? false;
|
|
115070
|
+
},
|
|
114977
115071
|
markSourceChanged(scope, opts) {
|
|
114978
115072
|
if (stopped)
|
|
114979
115073
|
return;
|
|
@@ -114988,6 +115082,7 @@ function createRecomputeScheduler(options) {
|
|
|
114988
115082
|
continue;
|
|
114989
115083
|
}
|
|
114990
115084
|
await markStale(registration.questionId);
|
|
115085
|
+
resetRetry(registration.questionId);
|
|
114991
115086
|
schedule(registration.questionId, debounceMs);
|
|
114992
115087
|
}
|
|
114993
115088
|
})().catch((err2) => warn({ scope, error: err2 instanceof Error ? err2.name : String(err2) }, "Could not mark derivative questions stale")));
|
|
@@ -114998,7 +115093,10 @@ function createRecomputeScheduler(options) {
|
|
|
114998
115093
|
void track(markStale(questionId).catch((err2) => warn({
|
|
114999
115094
|
questionId,
|
|
115000
115095
|
error: err2 instanceof Error ? err2.name : String(err2)
|
|
115001
|
-
}, "Could not mark derivative question stale")).then(() =>
|
|
115096
|
+
}, "Could not mark derivative question stale")).then(() => {
|
|
115097
|
+
resetRetry(questionId);
|
|
115098
|
+
schedule(questionId, opts?.immediate ? 0 : debounceMs);
|
|
115099
|
+
}));
|
|
115002
115100
|
},
|
|
115003
115101
|
async whenIdle() {
|
|
115004
115102
|
const waitForTimers = !options.timers;
|
|
@@ -115018,6 +115116,9 @@ function createRecomputeScheduler(options) {
|
|
|
115018
115116
|
if (state.timer !== null)
|
|
115019
115117
|
timers.clearTimeout(state.timer);
|
|
115020
115118
|
state.timer = null;
|
|
115119
|
+
state.retryAttempts = 0;
|
|
115120
|
+
state.retryAtMs = null;
|
|
115121
|
+
state.retryScheduled = false;
|
|
115021
115122
|
}
|
|
115022
115123
|
},
|
|
115023
115124
|
start() {
|
|
@@ -115038,6 +115139,7 @@ function createRecomputeScheduler(options) {
|
|
|
115038
115139
|
|
|
115039
115140
|
// ../core/dist/derivatives/api.js
|
|
115040
115141
|
var MAX_QUESTION_BODY_BYTES = 16 * 1024;
|
|
115142
|
+
var RETRY_IN_FLIGHT_POLL_SECONDS = 5;
|
|
115041
115143
|
function jsonResponse3(body, init) {
|
|
115042
115144
|
const headers = new Headers(init?.headers);
|
|
115043
115145
|
headers.set("Content-Type", "application/json");
|
|
@@ -115090,12 +115192,63 @@ async function loadForCaller(deps, request2, store, questionId) {
|
|
|
115090
115192
|
}
|
|
115091
115193
|
return { registration, writer };
|
|
115092
115194
|
}
|
|
115195
|
+
async function handleStatusRoute(request2, url2, deps, store, scheduler, now) {
|
|
115196
|
+
if (request2.method !== "GET") {
|
|
115197
|
+
return errorResponse2(405, "METHOD_NOT_ALLOWED", "Method not allowed");
|
|
115198
|
+
}
|
|
115199
|
+
const derivedScopeParam = url2.searchParams.get("derivedScope");
|
|
115200
|
+
if (!derivedScopeParam) {
|
|
115201
|
+
throw new DerivativeDerivedScopeRequiredError();
|
|
115202
|
+
}
|
|
115203
|
+
const scopeResult = parseDataScopeContract(derivedScopeParam);
|
|
115204
|
+
if (!scopeResult.ok) {
|
|
115205
|
+
return errorResponse2(scopeResult.status, scopeResult.body.error, scopeResult.body.message);
|
|
115206
|
+
}
|
|
115207
|
+
const derivedScope = scopeResult.scope;
|
|
115208
|
+
const auth = await deps.auth.authorizeBuilderRead({
|
|
115209
|
+
request: request2,
|
|
115210
|
+
scope: derivedScope,
|
|
115211
|
+
grantId: selectedGrantId(request2, url2)
|
|
115212
|
+
}) ?? void 0;
|
|
115213
|
+
const registrations = await store.list({ derivedScope });
|
|
115214
|
+
if (registrations.length === 0) {
|
|
115215
|
+
throw new DerivativeQuestionNotFoundError({ derivedScope });
|
|
115216
|
+
}
|
|
115217
|
+
const STATUS_PRECEDENCE = {
|
|
115218
|
+
ready: 0,
|
|
115219
|
+
stale: 1,
|
|
115220
|
+
pending: 2,
|
|
115221
|
+
failed: 3
|
|
115222
|
+
};
|
|
115223
|
+
const registration = registrations.reduce((best, candidate) => {
|
|
115224
|
+
const byStatus = STATUS_PRECEDENCE[candidate.status] - STATUS_PRECEDENCE[best.status];
|
|
115225
|
+
if (byStatus !== 0)
|
|
115226
|
+
return byStatus < 0 ? candidate : best;
|
|
115227
|
+
return candidate.updatedAt >= best.updatedAt ? candidate : best;
|
|
115228
|
+
});
|
|
115229
|
+
const nextRetryAt = scheduler.nextRetryAt?.(registration.questionId) ?? null;
|
|
115230
|
+
const retryAfterSeconds = nextRetryAt !== null ? Math.max(1, Math.ceil((Date.parse(nextRetryAt) - now().getTime()) / 1e3)) : scheduler.retryInFlight?.(registration.questionId) ? RETRY_IN_FLIGHT_POLL_SECONDS : null;
|
|
115231
|
+
const registrarView = auth === void 0 || auth.grantId === "owner" || auth.grantId === "policy-bypass" || registration.registeredBy.kind === "builder" && typeof auth.builder === "string" && sameBuilder(registration.registeredBy.builder, auth.builder);
|
|
115232
|
+
const disclosedErrorCode = registration.errorCode === "grant_invalid" && !registrarView ? "internal" : registration.errorCode;
|
|
115233
|
+
return jsonResponse3({
|
|
115234
|
+
derivedScope: registration.derivedScope,
|
|
115235
|
+
status: registration.status,
|
|
115236
|
+
lastComputedAt: registration.lastComputedAt,
|
|
115237
|
+
derivedVersion: registration.derivedVersion,
|
|
115238
|
+
derivedCollectedAt: registration.derivedCollectedAt,
|
|
115239
|
+
// Null unless failed, whatever an old store row holds: a stale question
|
|
115240
|
+
// is a recompute in progress, not a terminal failure.
|
|
115241
|
+
errorCode: registration.status === "failed" ? disclosedErrorCode : null,
|
|
115242
|
+
retryAfterSeconds
|
|
115243
|
+
});
|
|
115244
|
+
}
|
|
115093
115245
|
async function handlePersonalServerDerivativesRequest(request2, deps, options = {}) {
|
|
115094
115246
|
try {
|
|
115095
115247
|
const url2 = new URL(request2.url);
|
|
115096
115248
|
const pathname = stripBasePath2(url2.pathname, options.basePath);
|
|
115097
115249
|
const parts = pathname.split("/").filter(Boolean);
|
|
115098
|
-
|
|
115250
|
+
const isStatusRoute = parts[0] === "status" && parts.length === 1;
|
|
115251
|
+
if (!isStatusRoute && (parts[0] !== "questions" || parts.length > 3)) {
|
|
115099
115252
|
return errorResponse2(404, "NOT_FOUND", "Not found");
|
|
115100
115253
|
}
|
|
115101
115254
|
const compute = deps.compute;
|
|
@@ -115103,6 +115256,9 @@ async function handlePersonalServerDerivativesRequest(request2, deps, options =
|
|
|
115103
115256
|
throw new DerivativeComputeUnavailableError();
|
|
115104
115257
|
const { store, scheduler } = compute;
|
|
115105
115258
|
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
115259
|
+
if (isStatusRoute) {
|
|
115260
|
+
return await handleStatusRoute(request2, url2, deps, store, scheduler, now);
|
|
115261
|
+
}
|
|
115106
115262
|
if (parts.length === 1) {
|
|
115107
115263
|
if (request2.method === "GET") {
|
|
115108
115264
|
const derivedScope = url2.searchParams.get("derivedScope") ?? void 0;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opendatalabs/personal-server-ts-server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.14.0",
|
|
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": "1.
|
|
48
|
-
"@opendatalabs/personal-server-ts-lite": "1.
|
|
47
|
+
"@opendatalabs/personal-server-ts-core": "1.14.0",
|
|
48
|
+
"@opendatalabs/personal-server-ts-lite": "1.14.0",
|
|
49
49
|
"@opendatalabs/vana-sdk": "3.14.0",
|
|
50
50
|
"better-sqlite3": "^12.11.1",
|
|
51
51
|
"hono": "^4.12.27",
|