@arke-institute/sdk 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{index.d.mts → client-dAk3E64p.d.cts} +1 -7
- package/dist/client-dAk3E64p.d.ts +183 -0
- package/dist/{index.mjs → collections/index.cjs} +34 -5
- package/dist/collections/index.cjs.map +1 -0
- package/dist/collections/index.d.cts +9 -0
- package/dist/collections/index.d.ts +9 -1
- package/dist/collections/index.js +5 -32
- package/dist/collections/index.js.map +1 -1
- package/dist/content/index.cjs +506 -0
- package/dist/content/index.cjs.map +1 -0
- package/dist/content/index.d.cts +403 -0
- package/dist/content/index.d.ts +403 -0
- package/dist/content/index.js +473 -0
- package/dist/content/index.js.map +1 -0
- package/dist/edit/index.cjs +1029 -0
- package/dist/edit/index.cjs.map +1 -0
- package/dist/edit/index.d.cts +78 -0
- package/dist/edit/index.d.ts +78 -0
- package/dist/edit/index.js +983 -0
- package/dist/edit/index.js.map +1 -0
- package/dist/errors-3L7IiHcr.d.cts +480 -0
- package/dist/errors-B82BMmRP.d.cts +343 -0
- package/dist/errors-B82BMmRP.d.ts +343 -0
- package/dist/errors-BTe8GKRQ.d.ts +480 -0
- package/dist/graph/index.cjs +433 -0
- package/dist/graph/index.cjs.map +1 -0
- package/dist/graph/index.d.cts +456 -0
- package/dist/graph/index.d.ts +456 -0
- package/dist/graph/index.js +402 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/index.cjs +3761 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +7 -0
- package/dist/index.d.ts +7 -189
- package/dist/index.js +3502 -30
- package/dist/index.js.map +1 -1
- package/dist/query/index.cjs +289 -0
- package/dist/query/index.cjs.map +1 -0
- package/dist/query/index.d.cts +541 -0
- package/dist/query/index.d.ts +541 -0
- package/dist/query/index.js +261 -0
- package/dist/query/index.js.map +1 -0
- package/dist/upload/index.cjs +1634 -0
- package/dist/upload/index.cjs.map +1 -0
- package/dist/upload/index.d.cts +150 -0
- package/dist/upload/index.d.ts +150 -0
- package/dist/upload/index.js +1597 -0
- package/dist/upload/index.js.map +1 -0
- package/package.json +43 -8
- package/dist/collections/index.d.mts +0 -1
- package/dist/collections/index.mjs +0 -204
- package/dist/collections/index.mjs.map +0 -1
- package/dist/index.mjs.map +0 -1
|
@@ -0,0 +1,1029 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/edit/index.ts
|
|
31
|
+
var edit_exports = {};
|
|
32
|
+
__export(edit_exports, {
|
|
33
|
+
CASConflictError: () => CASConflictError,
|
|
34
|
+
DiffEngine: () => DiffEngine,
|
|
35
|
+
EditClient: () => EditClient,
|
|
36
|
+
EditError: () => EditError,
|
|
37
|
+
EditSession: () => EditSession,
|
|
38
|
+
EntityNotFoundError: () => EntityNotFoundError,
|
|
39
|
+
PermissionError: () => PermissionError,
|
|
40
|
+
PromptBuilder: () => PromptBuilder,
|
|
41
|
+
ReprocessError: () => ReprocessError,
|
|
42
|
+
ValidationError: () => ValidationError
|
|
43
|
+
});
|
|
44
|
+
module.exports = __toCommonJS(edit_exports);
|
|
45
|
+
|
|
46
|
+
// src/edit/errors.ts
|
|
47
|
+
var EditError = class extends Error {
|
|
48
|
+
constructor(message, code = "UNKNOWN_ERROR", details) {
|
|
49
|
+
super(message);
|
|
50
|
+
this.code = code;
|
|
51
|
+
this.details = details;
|
|
52
|
+
this.name = "EditError";
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
var EntityNotFoundError = class extends EditError {
|
|
56
|
+
constructor(pi) {
|
|
57
|
+
super(`Entity not found: ${pi}`, "ENTITY_NOT_FOUND", { pi });
|
|
58
|
+
this.name = "EntityNotFoundError";
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
var CASConflictError = class extends EditError {
|
|
62
|
+
constructor(pi, expectedTip, actualTip) {
|
|
63
|
+
super(
|
|
64
|
+
`CAS conflict: entity ${pi} was modified (expected ${expectedTip}, got ${actualTip})`,
|
|
65
|
+
"CAS_CONFLICT",
|
|
66
|
+
{ pi, expectedTip, actualTip }
|
|
67
|
+
);
|
|
68
|
+
this.name = "CASConflictError";
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
var ReprocessError = class extends EditError {
|
|
72
|
+
constructor(message, batchId) {
|
|
73
|
+
super(message, "REPROCESS_ERROR", { batchId });
|
|
74
|
+
this.name = "ReprocessError";
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
var ValidationError = class extends EditError {
|
|
78
|
+
constructor(message, field) {
|
|
79
|
+
super(message, "VALIDATION_ERROR", { field });
|
|
80
|
+
this.name = "ValidationError";
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
var PermissionError = class extends EditError {
|
|
84
|
+
constructor(message, pi) {
|
|
85
|
+
super(message, "PERMISSION_DENIED", { pi });
|
|
86
|
+
this.name = "PermissionError";
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// src/edit/client.ts
|
|
91
|
+
var DEFAULT_RETRY_OPTIONS = {
|
|
92
|
+
maxRetries: 5,
|
|
93
|
+
initialDelayMs: 2e3,
|
|
94
|
+
// Start with 2s delay (orchestrator needs time to initialize)
|
|
95
|
+
maxDelayMs: 3e4,
|
|
96
|
+
// Cap at 30s
|
|
97
|
+
backoffMultiplier: 2
|
|
98
|
+
// Double each retry
|
|
99
|
+
};
|
|
100
|
+
var EditClient = class {
|
|
101
|
+
constructor(config) {
|
|
102
|
+
this.gatewayUrl = config.gatewayUrl.replace(/\/$/, "");
|
|
103
|
+
this.authToken = config.authToken;
|
|
104
|
+
this.statusUrlTransform = config.statusUrlTransform;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Update the auth token (useful for token refresh)
|
|
108
|
+
*/
|
|
109
|
+
setAuthToken(token) {
|
|
110
|
+
this.authToken = token;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Sleep for a given number of milliseconds
|
|
114
|
+
*/
|
|
115
|
+
sleep(ms) {
|
|
116
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Execute a fetch with exponential backoff retry on transient errors
|
|
120
|
+
*/
|
|
121
|
+
async fetchWithRetry(url, options, retryOptions = DEFAULT_RETRY_OPTIONS) {
|
|
122
|
+
let lastError = null;
|
|
123
|
+
let delay = retryOptions.initialDelayMs;
|
|
124
|
+
for (let attempt = 0; attempt <= retryOptions.maxRetries; attempt++) {
|
|
125
|
+
try {
|
|
126
|
+
const response = await fetch(url, options);
|
|
127
|
+
if (response.status >= 500 && attempt < retryOptions.maxRetries) {
|
|
128
|
+
lastError = new Error(`Server error: ${response.status} ${response.statusText}`);
|
|
129
|
+
await this.sleep(delay);
|
|
130
|
+
delay = Math.min(delay * retryOptions.backoffMultiplier, retryOptions.maxDelayMs);
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
return response;
|
|
134
|
+
} catch (error) {
|
|
135
|
+
lastError = error;
|
|
136
|
+
if (attempt < retryOptions.maxRetries) {
|
|
137
|
+
await this.sleep(delay);
|
|
138
|
+
delay = Math.min(delay * retryOptions.backoffMultiplier, retryOptions.maxDelayMs);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
throw lastError || new Error("Request failed after retries");
|
|
143
|
+
}
|
|
144
|
+
getHeaders() {
|
|
145
|
+
const headers = {
|
|
146
|
+
"Content-Type": "application/json"
|
|
147
|
+
};
|
|
148
|
+
if (this.authToken) {
|
|
149
|
+
headers["Authorization"] = `Bearer ${this.authToken}`;
|
|
150
|
+
}
|
|
151
|
+
return headers;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Handle common error responses
|
|
155
|
+
*/
|
|
156
|
+
handleErrorResponse(response, context) {
|
|
157
|
+
if (response.status === 403) {
|
|
158
|
+
throw new PermissionError(`Permission denied: ${context}`);
|
|
159
|
+
}
|
|
160
|
+
throw new EditError(
|
|
161
|
+
`${context}: ${response.statusText}`,
|
|
162
|
+
"API_ERROR",
|
|
163
|
+
{ status: response.status }
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
// ===========================================================================
|
|
167
|
+
// IPFS Wrapper Operations (via /api/*)
|
|
168
|
+
// ===========================================================================
|
|
169
|
+
/**
|
|
170
|
+
* Fetch an entity by PI
|
|
171
|
+
*/
|
|
172
|
+
async getEntity(pi) {
|
|
173
|
+
const response = await fetch(`${this.gatewayUrl}/api/entities/${pi}`, {
|
|
174
|
+
headers: this.getHeaders()
|
|
175
|
+
});
|
|
176
|
+
if (response.status === 404) {
|
|
177
|
+
throw new EntityNotFoundError(pi);
|
|
178
|
+
}
|
|
179
|
+
if (!response.ok) {
|
|
180
|
+
this.handleErrorResponse(response, `Failed to fetch entity ${pi}`);
|
|
181
|
+
}
|
|
182
|
+
return response.json();
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Fetch content by CID
|
|
186
|
+
*/
|
|
187
|
+
async getContent(cid) {
|
|
188
|
+
const response = await fetch(`${this.gatewayUrl}/api/cat/${cid}`, {
|
|
189
|
+
headers: this.getHeaders()
|
|
190
|
+
});
|
|
191
|
+
if (!response.ok) {
|
|
192
|
+
this.handleErrorResponse(response, `Failed to fetch content ${cid}`);
|
|
193
|
+
}
|
|
194
|
+
return response.text();
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Upload content and get CID
|
|
198
|
+
*/
|
|
199
|
+
async uploadContent(content, filename) {
|
|
200
|
+
const formData = new FormData();
|
|
201
|
+
const blob = new Blob([content], { type: "text/plain" });
|
|
202
|
+
formData.append("file", blob, filename);
|
|
203
|
+
const headers = {};
|
|
204
|
+
if (this.authToken) {
|
|
205
|
+
headers["Authorization"] = `Bearer ${this.authToken}`;
|
|
206
|
+
}
|
|
207
|
+
const response = await fetch(`${this.gatewayUrl}/api/upload`, {
|
|
208
|
+
method: "POST",
|
|
209
|
+
headers,
|
|
210
|
+
body: formData
|
|
211
|
+
});
|
|
212
|
+
if (!response.ok) {
|
|
213
|
+
this.handleErrorResponse(response, "Failed to upload content");
|
|
214
|
+
}
|
|
215
|
+
const result = await response.json();
|
|
216
|
+
return result[0].cid;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Update an entity with new components
|
|
220
|
+
*/
|
|
221
|
+
async updateEntity(pi, update) {
|
|
222
|
+
const response = await fetch(`${this.gatewayUrl}/api/entities/${pi}/versions`, {
|
|
223
|
+
method: "POST",
|
|
224
|
+
headers: this.getHeaders(),
|
|
225
|
+
body: JSON.stringify({
|
|
226
|
+
expect_tip: update.expect_tip,
|
|
227
|
+
components: update.components,
|
|
228
|
+
components_remove: update.components_remove,
|
|
229
|
+
note: update.note
|
|
230
|
+
})
|
|
231
|
+
});
|
|
232
|
+
if (response.status === 409) {
|
|
233
|
+
const entity = await this.getEntity(pi);
|
|
234
|
+
throw new CASConflictError(
|
|
235
|
+
pi,
|
|
236
|
+
update.expect_tip,
|
|
237
|
+
entity.manifest_cid
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
if (!response.ok) {
|
|
241
|
+
this.handleErrorResponse(response, `Failed to update entity ${pi}`);
|
|
242
|
+
}
|
|
243
|
+
return response.json();
|
|
244
|
+
}
|
|
245
|
+
// ===========================================================================
|
|
246
|
+
// Reprocess API Operations (via /reprocess/*)
|
|
247
|
+
// ===========================================================================
|
|
248
|
+
/**
|
|
249
|
+
* Trigger reprocessing for an entity
|
|
250
|
+
*/
|
|
251
|
+
async reprocess(request) {
|
|
252
|
+
const response = await fetch(`${this.gatewayUrl}/reprocess/reprocess`, {
|
|
253
|
+
method: "POST",
|
|
254
|
+
headers: this.getHeaders(),
|
|
255
|
+
body: JSON.stringify({
|
|
256
|
+
pi: request.pi,
|
|
257
|
+
phases: request.phases,
|
|
258
|
+
cascade: request.cascade,
|
|
259
|
+
options: request.options
|
|
260
|
+
})
|
|
261
|
+
});
|
|
262
|
+
if (response.status === 403) {
|
|
263
|
+
const error = await response.json().catch(() => ({}));
|
|
264
|
+
throw new PermissionError(
|
|
265
|
+
error.message || `Permission denied to reprocess ${request.pi}`,
|
|
266
|
+
request.pi
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
if (!response.ok) {
|
|
270
|
+
const error = await response.json().catch(() => ({}));
|
|
271
|
+
throw new ReprocessError(
|
|
272
|
+
error.message || `Reprocess failed: ${response.statusText}`,
|
|
273
|
+
void 0
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
return response.json();
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Get reprocessing status by batch ID
|
|
280
|
+
*
|
|
281
|
+
* Uses exponential backoff retry to handle transient 500 errors
|
|
282
|
+
* that occur when the orchestrator is initializing.
|
|
283
|
+
*
|
|
284
|
+
* @param statusUrl - The status URL returned from reprocess()
|
|
285
|
+
* @param isFirstPoll - If true, uses a longer initial delay (orchestrator warmup)
|
|
286
|
+
*/
|
|
287
|
+
async getReprocessStatus(statusUrl, isFirstPoll = false) {
|
|
288
|
+
const retryOptions = isFirstPoll ? { ...DEFAULT_RETRY_OPTIONS, initialDelayMs: 3e3 } : DEFAULT_RETRY_OPTIONS;
|
|
289
|
+
const fetchUrl = this.statusUrlTransform ? this.statusUrlTransform(statusUrl) : statusUrl;
|
|
290
|
+
const response = await this.fetchWithRetry(
|
|
291
|
+
fetchUrl,
|
|
292
|
+
{ headers: this.getHeaders() },
|
|
293
|
+
retryOptions
|
|
294
|
+
);
|
|
295
|
+
if (!response.ok) {
|
|
296
|
+
throw new EditError(
|
|
297
|
+
`Failed to fetch reprocess status: ${response.statusText}`,
|
|
298
|
+
"STATUS_ERROR",
|
|
299
|
+
{ status: response.status }
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
return response.json();
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
// src/edit/diff.ts
|
|
307
|
+
var Diff = __toESM(require("diff"), 1);
|
|
308
|
+
var DiffEngine = class {
|
|
309
|
+
/**
|
|
310
|
+
* Compute diff between two strings
|
|
311
|
+
*/
|
|
312
|
+
static diff(original, modified) {
|
|
313
|
+
const changes = Diff.diffLines(original, modified);
|
|
314
|
+
const diffs = [];
|
|
315
|
+
let lineNumber = 1;
|
|
316
|
+
for (const change of changes) {
|
|
317
|
+
if (change.added) {
|
|
318
|
+
diffs.push({
|
|
319
|
+
type: "addition",
|
|
320
|
+
modified: change.value.trimEnd(),
|
|
321
|
+
lineNumber
|
|
322
|
+
});
|
|
323
|
+
} else if (change.removed) {
|
|
324
|
+
diffs.push({
|
|
325
|
+
type: "deletion",
|
|
326
|
+
original: change.value.trimEnd(),
|
|
327
|
+
lineNumber
|
|
328
|
+
});
|
|
329
|
+
} else {
|
|
330
|
+
const lines = change.value.split("\n").length - 1;
|
|
331
|
+
lineNumber += lines;
|
|
332
|
+
}
|
|
333
|
+
if (change.added) {
|
|
334
|
+
lineNumber += change.value.split("\n").length - 1;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return diffs;
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Compute word-level diff for more granular changes
|
|
341
|
+
*/
|
|
342
|
+
static diffWords(original, modified) {
|
|
343
|
+
const changes = Diff.diffWords(original, modified);
|
|
344
|
+
const diffs = [];
|
|
345
|
+
for (const change of changes) {
|
|
346
|
+
if (change.added) {
|
|
347
|
+
diffs.push({
|
|
348
|
+
type: "addition",
|
|
349
|
+
modified: change.value
|
|
350
|
+
});
|
|
351
|
+
} else if (change.removed) {
|
|
352
|
+
diffs.push({
|
|
353
|
+
type: "deletion",
|
|
354
|
+
original: change.value
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return diffs;
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Create a ComponentDiff from original and modified content
|
|
362
|
+
*/
|
|
363
|
+
static createComponentDiff(componentName, original, modified) {
|
|
364
|
+
const diffs = this.diff(original, modified);
|
|
365
|
+
const hasChanges = diffs.length > 0;
|
|
366
|
+
let summary;
|
|
367
|
+
if (!hasChanges) {
|
|
368
|
+
summary = "No changes";
|
|
369
|
+
} else {
|
|
370
|
+
const additions = diffs.filter((d) => d.type === "addition").length;
|
|
371
|
+
const deletions = diffs.filter((d) => d.type === "deletion").length;
|
|
372
|
+
const parts = [];
|
|
373
|
+
if (additions > 0) parts.push(`${additions} addition${additions > 1 ? "s" : ""}`);
|
|
374
|
+
if (deletions > 0) parts.push(`${deletions} deletion${deletions > 1 ? "s" : ""}`);
|
|
375
|
+
summary = parts.join(", ");
|
|
376
|
+
}
|
|
377
|
+
return {
|
|
378
|
+
componentName,
|
|
379
|
+
diffs,
|
|
380
|
+
summary,
|
|
381
|
+
hasChanges
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Format diffs for AI prompt consumption
|
|
386
|
+
*/
|
|
387
|
+
static formatForPrompt(diffs) {
|
|
388
|
+
if (diffs.length === 0) {
|
|
389
|
+
return "No changes detected.";
|
|
390
|
+
}
|
|
391
|
+
const lines = [];
|
|
392
|
+
for (const diff of diffs) {
|
|
393
|
+
const linePrefix = diff.lineNumber ? `Line ${diff.lineNumber}: ` : "";
|
|
394
|
+
if (diff.type === "addition") {
|
|
395
|
+
lines.push(`${linePrefix}+ ${diff.modified}`);
|
|
396
|
+
} else if (diff.type === "deletion") {
|
|
397
|
+
lines.push(`${linePrefix}- ${diff.original}`);
|
|
398
|
+
} else if (diff.type === "change") {
|
|
399
|
+
lines.push(`${linePrefix}"${diff.original}" \u2192 "${diff.modified}"`);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
return lines.join("\n");
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Format component diffs for AI prompt
|
|
406
|
+
*/
|
|
407
|
+
static formatComponentDiffsForPrompt(componentDiffs) {
|
|
408
|
+
const sections = [];
|
|
409
|
+
for (const cd of componentDiffs) {
|
|
410
|
+
if (!cd.hasChanges) continue;
|
|
411
|
+
sections.push(`## Changes to ${cd.componentName}:`);
|
|
412
|
+
sections.push(this.formatForPrompt(cd.diffs));
|
|
413
|
+
sections.push("");
|
|
414
|
+
}
|
|
415
|
+
return sections.join("\n");
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Create a unified diff view
|
|
419
|
+
*/
|
|
420
|
+
static unifiedDiff(original, modified, options) {
|
|
421
|
+
const filename = options?.filename || "content";
|
|
422
|
+
const patch = Diff.createPatch(filename, original, modified, "", "", {
|
|
423
|
+
context: options?.context ?? 3
|
|
424
|
+
});
|
|
425
|
+
return patch;
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Extract corrections from diffs (specific text replacements)
|
|
429
|
+
*/
|
|
430
|
+
static extractCorrections(original, modified, sourceFile) {
|
|
431
|
+
const wordDiffs = Diff.diffWords(original, modified);
|
|
432
|
+
const corrections = [];
|
|
433
|
+
let i = 0;
|
|
434
|
+
while (i < wordDiffs.length) {
|
|
435
|
+
const current = wordDiffs[i];
|
|
436
|
+
if (current.removed && i + 1 < wordDiffs.length && wordDiffs[i + 1].added) {
|
|
437
|
+
const removed = current.value.trim();
|
|
438
|
+
const added = wordDiffs[i + 1].value.trim();
|
|
439
|
+
if (removed && added && removed !== added) {
|
|
440
|
+
corrections.push({
|
|
441
|
+
original: removed,
|
|
442
|
+
corrected: added,
|
|
443
|
+
sourceFile
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
i += 2;
|
|
447
|
+
} else {
|
|
448
|
+
i++;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
return corrections;
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Check if two strings are meaningfully different
|
|
455
|
+
* (ignoring whitespace differences)
|
|
456
|
+
*/
|
|
457
|
+
static hasSignificantChanges(original, modified) {
|
|
458
|
+
const normalizedOriginal = original.replace(/\s+/g, " ").trim();
|
|
459
|
+
const normalizedModified = modified.replace(/\s+/g, " ").trim();
|
|
460
|
+
return normalizedOriginal !== normalizedModified;
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
|
|
464
|
+
// src/edit/prompts.ts
|
|
465
|
+
var PromptBuilder = class {
|
|
466
|
+
/**
|
|
467
|
+
* Build prompt for AI-first mode (user provides instructions)
|
|
468
|
+
*/
|
|
469
|
+
static buildAIPrompt(userPrompt, component, entityContext, currentContent) {
|
|
470
|
+
const sections = [];
|
|
471
|
+
sections.push(`## Instructions for ${component}`);
|
|
472
|
+
sections.push(userPrompt);
|
|
473
|
+
sections.push("");
|
|
474
|
+
sections.push("## Entity Context");
|
|
475
|
+
sections.push(`- PI: ${entityContext.pi}`);
|
|
476
|
+
sections.push(`- Current version: ${entityContext.ver}`);
|
|
477
|
+
if (entityContext.parentPi) {
|
|
478
|
+
sections.push(`- Parent: ${entityContext.parentPi}`);
|
|
479
|
+
}
|
|
480
|
+
if (entityContext.childrenCount > 0) {
|
|
481
|
+
sections.push(`- Children: ${entityContext.childrenCount}`);
|
|
482
|
+
}
|
|
483
|
+
sections.push("");
|
|
484
|
+
if (currentContent) {
|
|
485
|
+
sections.push(`## Current ${component} content for reference:`);
|
|
486
|
+
sections.push("```");
|
|
487
|
+
sections.push(currentContent.slice(0, 2e3));
|
|
488
|
+
if (currentContent.length > 2e3) {
|
|
489
|
+
sections.push("... [truncated]");
|
|
490
|
+
}
|
|
491
|
+
sections.push("```");
|
|
492
|
+
}
|
|
493
|
+
return sections.join("\n");
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* Build prompt incorporating manual edits and diffs
|
|
497
|
+
*/
|
|
498
|
+
static buildEditReviewPrompt(componentDiffs, corrections, component, userInstructions) {
|
|
499
|
+
const sections = [];
|
|
500
|
+
sections.push("## Manual Edits Made");
|
|
501
|
+
sections.push("");
|
|
502
|
+
sections.push("The following manual edits were made to this entity:");
|
|
503
|
+
sections.push("");
|
|
504
|
+
const diffContent = DiffEngine.formatComponentDiffsForPrompt(componentDiffs);
|
|
505
|
+
if (diffContent) {
|
|
506
|
+
sections.push(diffContent);
|
|
507
|
+
}
|
|
508
|
+
if (corrections.length > 0) {
|
|
509
|
+
sections.push("## Corrections Identified");
|
|
510
|
+
sections.push("");
|
|
511
|
+
for (const correction of corrections) {
|
|
512
|
+
const source = correction.sourceFile ? ` (in ${correction.sourceFile})` : "";
|
|
513
|
+
sections.push(`- "${correction.original}" \u2192 "${correction.corrected}"${source}`);
|
|
514
|
+
}
|
|
515
|
+
sections.push("");
|
|
516
|
+
}
|
|
517
|
+
sections.push("## Instructions");
|
|
518
|
+
if (userInstructions) {
|
|
519
|
+
sections.push(userInstructions);
|
|
520
|
+
} else {
|
|
521
|
+
sections.push(
|
|
522
|
+
`Update the ${component} to accurately reflect these changes. Ensure any corrections are incorporated and the content is consistent.`
|
|
523
|
+
);
|
|
524
|
+
}
|
|
525
|
+
sections.push("");
|
|
526
|
+
sections.push("## Guidance");
|
|
527
|
+
switch (component) {
|
|
528
|
+
case "pinax":
|
|
529
|
+
sections.push(
|
|
530
|
+
"Update metadata fields to reflect any corrections. Pay special attention to dates, names, and other factual information that may have been corrected."
|
|
531
|
+
);
|
|
532
|
+
break;
|
|
533
|
+
case "description":
|
|
534
|
+
sections.push(
|
|
535
|
+
"Regenerate the description incorporating the changes. Maintain the overall tone and structure while ensuring accuracy based on the corrections."
|
|
536
|
+
);
|
|
537
|
+
break;
|
|
538
|
+
case "cheimarros":
|
|
539
|
+
sections.push(
|
|
540
|
+
"Update the knowledge graph to reflect any new or corrected entities, relationships, and facts identified in the changes."
|
|
541
|
+
);
|
|
542
|
+
break;
|
|
543
|
+
}
|
|
544
|
+
return sections.join("\n");
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* Build cascade-aware prompt additions
|
|
548
|
+
*/
|
|
549
|
+
static buildCascadePrompt(basePrompt, cascadeContext) {
|
|
550
|
+
const sections = [basePrompt];
|
|
551
|
+
sections.push("");
|
|
552
|
+
sections.push("## Cascade Context");
|
|
553
|
+
sections.push("");
|
|
554
|
+
sections.push(
|
|
555
|
+
"This edit is part of a cascading update. After updating this entity, parent entities will also be updated to reflect these changes."
|
|
556
|
+
);
|
|
557
|
+
sections.push("");
|
|
558
|
+
if (cascadeContext.path.length > 1) {
|
|
559
|
+
sections.push(`Cascade path: ${cascadeContext.path.join(" \u2192 ")}`);
|
|
560
|
+
sections.push(`Depth: ${cascadeContext.depth}`);
|
|
561
|
+
}
|
|
562
|
+
if (cascadeContext.stopAtPi) {
|
|
563
|
+
sections.push(`Cascade will stop at: ${cascadeContext.stopAtPi}`);
|
|
564
|
+
}
|
|
565
|
+
sections.push("");
|
|
566
|
+
sections.push(
|
|
567
|
+
"Ensure the content accurately represents the source material so parent aggregations will be correct."
|
|
568
|
+
);
|
|
569
|
+
return sections.join("\n");
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Build a general prompt combining multiple instructions
|
|
573
|
+
*/
|
|
574
|
+
static buildCombinedPrompt(generalPrompt, componentPrompt, component) {
|
|
575
|
+
const sections = [];
|
|
576
|
+
if (generalPrompt) {
|
|
577
|
+
sections.push("## General Instructions");
|
|
578
|
+
sections.push(generalPrompt);
|
|
579
|
+
sections.push("");
|
|
580
|
+
}
|
|
581
|
+
if (componentPrompt) {
|
|
582
|
+
sections.push(`## Specific Instructions for ${component}`);
|
|
583
|
+
sections.push(componentPrompt);
|
|
584
|
+
sections.push("");
|
|
585
|
+
}
|
|
586
|
+
if (sections.length === 0) {
|
|
587
|
+
return `Regenerate the ${component} based on the current entity content.`;
|
|
588
|
+
}
|
|
589
|
+
return sections.join("\n");
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* Build prompt for correction-based updates
|
|
593
|
+
*/
|
|
594
|
+
static buildCorrectionPrompt(corrections) {
|
|
595
|
+
if (corrections.length === 0) {
|
|
596
|
+
return "";
|
|
597
|
+
}
|
|
598
|
+
const sections = [];
|
|
599
|
+
sections.push("## Corrections Applied");
|
|
600
|
+
sections.push("");
|
|
601
|
+
sections.push("The following corrections were made to the source content:");
|
|
602
|
+
sections.push("");
|
|
603
|
+
for (const correction of corrections) {
|
|
604
|
+
const source = correction.sourceFile ? ` in ${correction.sourceFile}` : "";
|
|
605
|
+
sections.push(`- "${correction.original}" was corrected to "${correction.corrected}"${source}`);
|
|
606
|
+
if (correction.context) {
|
|
607
|
+
sections.push(` Context: ${correction.context}`);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
sections.push("");
|
|
611
|
+
sections.push(
|
|
612
|
+
"Update the metadata and description to reflect these corrections. Previous content may have contained errors based on the incorrect text."
|
|
613
|
+
);
|
|
614
|
+
return sections.join("\n");
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* Get component-specific regeneration guidance
|
|
618
|
+
*/
|
|
619
|
+
static getComponentGuidance(component) {
|
|
620
|
+
switch (component) {
|
|
621
|
+
case "pinax":
|
|
622
|
+
return "Extract and structure metadata including: institution, creator, title, date range, subjects, type, and other relevant fields. Ensure accuracy based on the source content.";
|
|
623
|
+
case "description":
|
|
624
|
+
return "Generate a clear, informative description that summarizes the entity content. Focus on what the material contains, its historical significance, and context. Write for a general audience unless otherwise specified.";
|
|
625
|
+
case "cheimarros":
|
|
626
|
+
return "Extract entities (people, places, organizations, events) and their relationships. Build a knowledge graph that captures the key facts and connections in the content.";
|
|
627
|
+
default:
|
|
628
|
+
return "";
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
};
|
|
632
|
+
|
|
633
|
+
// src/edit/session.ts
|
|
634
|
+
var DEFAULT_SCOPE = {
|
|
635
|
+
components: [],
|
|
636
|
+
cascade: false
|
|
637
|
+
};
|
|
638
|
+
var DEFAULT_POLL_OPTIONS = {
|
|
639
|
+
intervalMs: 2e3,
|
|
640
|
+
timeoutMs: 3e5
|
|
641
|
+
// 5 minutes
|
|
642
|
+
};
|
|
643
|
+
var EditSession = class {
|
|
644
|
+
constructor(client, pi, config) {
|
|
645
|
+
this.entity = null;
|
|
646
|
+
this.loadedComponents = {};
|
|
647
|
+
// AI mode state
|
|
648
|
+
this.prompts = {};
|
|
649
|
+
// Manual mode state
|
|
650
|
+
this.editedContent = {};
|
|
651
|
+
this.corrections = [];
|
|
652
|
+
// Scope
|
|
653
|
+
this.scope = { ...DEFAULT_SCOPE };
|
|
654
|
+
// Execution state
|
|
655
|
+
this.submitting = false;
|
|
656
|
+
this.result = null;
|
|
657
|
+
this.statusUrl = null;
|
|
658
|
+
this.client = client;
|
|
659
|
+
this.pi = pi;
|
|
660
|
+
this.mode = config?.mode ?? "ai-prompt";
|
|
661
|
+
this.aiReviewEnabled = config?.aiReviewEnabled ?? true;
|
|
662
|
+
}
|
|
663
|
+
// ===========================================================================
|
|
664
|
+
// Loading
|
|
665
|
+
// ===========================================================================
|
|
666
|
+
/**
|
|
667
|
+
* Load the entity and its key components
|
|
668
|
+
*/
|
|
669
|
+
async load() {
|
|
670
|
+
this.entity = await this.client.getEntity(this.pi);
|
|
671
|
+
const priorityComponents = ["description.md", "pinax.json", "cheimarros.json"];
|
|
672
|
+
await Promise.all(
|
|
673
|
+
priorityComponents.map(async (name) => {
|
|
674
|
+
const cid = this.entity.components[name];
|
|
675
|
+
if (cid) {
|
|
676
|
+
try {
|
|
677
|
+
this.loadedComponents[name] = await this.client.getContent(cid);
|
|
678
|
+
} catch {
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
})
|
|
682
|
+
);
|
|
683
|
+
}
|
|
684
|
+
/**
|
|
685
|
+
* Load a specific component on demand
|
|
686
|
+
*/
|
|
687
|
+
async loadComponent(name) {
|
|
688
|
+
if (this.loadedComponents[name]) {
|
|
689
|
+
return this.loadedComponents[name];
|
|
690
|
+
}
|
|
691
|
+
if (!this.entity) {
|
|
692
|
+
throw new ValidationError("Session not loaded");
|
|
693
|
+
}
|
|
694
|
+
const cid = this.entity.components[name];
|
|
695
|
+
if (!cid) {
|
|
696
|
+
return void 0;
|
|
697
|
+
}
|
|
698
|
+
const content = await this.client.getContent(cid);
|
|
699
|
+
this.loadedComponents[name] = content;
|
|
700
|
+
return content;
|
|
701
|
+
}
|
|
702
|
+
/**
|
|
703
|
+
* Get the loaded entity
|
|
704
|
+
*/
|
|
705
|
+
getEntity() {
|
|
706
|
+
if (!this.entity) {
|
|
707
|
+
throw new ValidationError("Session not loaded. Call load() first.");
|
|
708
|
+
}
|
|
709
|
+
return this.entity;
|
|
710
|
+
}
|
|
711
|
+
/**
|
|
712
|
+
* Get loaded component content
|
|
713
|
+
*/
|
|
714
|
+
getComponents() {
|
|
715
|
+
return { ...this.loadedComponents };
|
|
716
|
+
}
|
|
717
|
+
// ===========================================================================
|
|
718
|
+
// AI Prompt Mode
|
|
719
|
+
// ===========================================================================
|
|
720
|
+
/**
|
|
721
|
+
* Set a prompt for AI regeneration
|
|
722
|
+
*/
|
|
723
|
+
setPrompt(target, prompt) {
|
|
724
|
+
if (this.mode === "manual-only") {
|
|
725
|
+
throw new ValidationError("Cannot set prompts in manual-only mode");
|
|
726
|
+
}
|
|
727
|
+
this.prompts[target] = prompt;
|
|
728
|
+
}
|
|
729
|
+
/**
|
|
730
|
+
* Get all prompts
|
|
731
|
+
*/
|
|
732
|
+
getPrompts() {
|
|
733
|
+
return { ...this.prompts };
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* Clear a prompt
|
|
737
|
+
*/
|
|
738
|
+
clearPrompt(target) {
|
|
739
|
+
delete this.prompts[target];
|
|
740
|
+
}
|
|
741
|
+
// ===========================================================================
|
|
742
|
+
// Manual Edit Mode
|
|
743
|
+
// ===========================================================================
|
|
744
|
+
/**
|
|
745
|
+
* Set edited content for a component
|
|
746
|
+
*/
|
|
747
|
+
setContent(componentName, content) {
|
|
748
|
+
if (this.mode === "ai-prompt") {
|
|
749
|
+
throw new ValidationError("Cannot set content in ai-prompt mode");
|
|
750
|
+
}
|
|
751
|
+
this.editedContent[componentName] = content;
|
|
752
|
+
}
|
|
753
|
+
/**
|
|
754
|
+
* Get all edited content
|
|
755
|
+
*/
|
|
756
|
+
getEditedContent() {
|
|
757
|
+
return { ...this.editedContent };
|
|
758
|
+
}
|
|
759
|
+
/**
|
|
760
|
+
* Clear edited content for a component
|
|
761
|
+
*/
|
|
762
|
+
clearContent(componentName) {
|
|
763
|
+
delete this.editedContent[componentName];
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* Add a correction (for OCR fixes, etc.)
|
|
767
|
+
*/
|
|
768
|
+
addCorrection(original, corrected, sourceFile) {
|
|
769
|
+
this.corrections.push({ original, corrected, sourceFile });
|
|
770
|
+
}
|
|
771
|
+
/**
|
|
772
|
+
* Get all corrections
|
|
773
|
+
*/
|
|
774
|
+
getCorrections() {
|
|
775
|
+
return [...this.corrections];
|
|
776
|
+
}
|
|
777
|
+
/**
|
|
778
|
+
* Clear corrections
|
|
779
|
+
*/
|
|
780
|
+
clearCorrections() {
|
|
781
|
+
this.corrections = [];
|
|
782
|
+
}
|
|
783
|
+
// ===========================================================================
|
|
784
|
+
// Scope Configuration
|
|
785
|
+
// ===========================================================================
|
|
786
|
+
/**
|
|
787
|
+
* Set the edit scope
|
|
788
|
+
*/
|
|
789
|
+
setScope(scope) {
|
|
790
|
+
this.scope = { ...this.scope, ...scope };
|
|
791
|
+
}
|
|
792
|
+
/**
|
|
793
|
+
* Get the current scope
|
|
794
|
+
*/
|
|
795
|
+
getScope() {
|
|
796
|
+
return { ...this.scope };
|
|
797
|
+
}
|
|
798
|
+
// ===========================================================================
|
|
799
|
+
// Preview & Summary
|
|
800
|
+
// ===========================================================================
|
|
801
|
+
/**
|
|
802
|
+
* Get diffs for manual changes
|
|
803
|
+
*/
|
|
804
|
+
getDiff() {
|
|
805
|
+
const diffs = [];
|
|
806
|
+
for (const [name, edited] of Object.entries(this.editedContent)) {
|
|
807
|
+
const original = this.loadedComponents[name] || "";
|
|
808
|
+
if (DiffEngine.hasSignificantChanges(original, edited)) {
|
|
809
|
+
diffs.push(DiffEngine.createComponentDiff(name, original, edited));
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
return diffs;
|
|
813
|
+
}
|
|
814
|
+
/**
|
|
815
|
+
* Preview what prompts will be sent to AI
|
|
816
|
+
*/
|
|
817
|
+
previewPrompt() {
|
|
818
|
+
const result = {};
|
|
819
|
+
if (!this.entity) return result;
|
|
820
|
+
const entityContext = {
|
|
821
|
+
pi: this.entity.pi,
|
|
822
|
+
ver: this.entity.ver,
|
|
823
|
+
parentPi: this.entity.parent_pi,
|
|
824
|
+
childrenCount: this.entity.children_pi.length,
|
|
825
|
+
currentContent: this.loadedComponents
|
|
826
|
+
};
|
|
827
|
+
for (const component of this.scope.components) {
|
|
828
|
+
let prompt;
|
|
829
|
+
if (this.mode === "ai-prompt") {
|
|
830
|
+
const componentPrompt = this.prompts[component];
|
|
831
|
+
const generalPrompt = this.prompts["general"];
|
|
832
|
+
const combined = PromptBuilder.buildCombinedPrompt(generalPrompt, componentPrompt, component);
|
|
833
|
+
prompt = PromptBuilder.buildAIPrompt(
|
|
834
|
+
combined,
|
|
835
|
+
component,
|
|
836
|
+
entityContext,
|
|
837
|
+
this.loadedComponents[`${component}.json`] || this.loadedComponents[`${component}.md`]
|
|
838
|
+
);
|
|
839
|
+
} else {
|
|
840
|
+
const diffs = this.getDiff();
|
|
841
|
+
const userInstructions = this.prompts["general"] || this.prompts[component];
|
|
842
|
+
prompt = PromptBuilder.buildEditReviewPrompt(diffs, this.corrections, component, userInstructions);
|
|
843
|
+
}
|
|
844
|
+
if (this.scope.cascade) {
|
|
845
|
+
prompt = PromptBuilder.buildCascadePrompt(prompt, {
|
|
846
|
+
path: [this.entity.pi, this.entity.parent_pi || "root"].filter(Boolean),
|
|
847
|
+
depth: 0,
|
|
848
|
+
stopAtPi: this.scope.stopAtPi
|
|
849
|
+
});
|
|
850
|
+
}
|
|
851
|
+
result[component] = prompt;
|
|
852
|
+
}
|
|
853
|
+
return result;
|
|
854
|
+
}
|
|
855
|
+
/**
|
|
856
|
+
* Get a summary of pending changes
|
|
857
|
+
*/
|
|
858
|
+
getChangeSummary() {
|
|
859
|
+
const diffs = this.getDiff();
|
|
860
|
+
const hasManualEdits = diffs.some((d) => d.hasChanges);
|
|
861
|
+
return {
|
|
862
|
+
mode: this.mode,
|
|
863
|
+
hasManualEdits,
|
|
864
|
+
editedComponents: Object.keys(this.editedContent),
|
|
865
|
+
corrections: [...this.corrections],
|
|
866
|
+
prompts: { ...this.prompts },
|
|
867
|
+
scope: { ...this.scope },
|
|
868
|
+
willRegenerate: [...this.scope.components],
|
|
869
|
+
willCascade: this.scope.cascade,
|
|
870
|
+
willSave: hasManualEdits,
|
|
871
|
+
willReprocess: this.scope.components.length > 0
|
|
872
|
+
};
|
|
873
|
+
}
|
|
874
|
+
// ===========================================================================
|
|
875
|
+
// Execution
|
|
876
|
+
// ===========================================================================
|
|
877
|
+
/**
|
|
878
|
+
* Submit changes (saves first if manual edits, then reprocesses)
|
|
879
|
+
*/
|
|
880
|
+
async submit(note) {
|
|
881
|
+
if (this.submitting) {
|
|
882
|
+
throw new ValidationError("Submit already in progress");
|
|
883
|
+
}
|
|
884
|
+
if (!this.entity) {
|
|
885
|
+
throw new ValidationError("Session not loaded. Call load() first.");
|
|
886
|
+
}
|
|
887
|
+
this.submitting = true;
|
|
888
|
+
this.result = {};
|
|
889
|
+
try {
|
|
890
|
+
const diffs = this.getDiff();
|
|
891
|
+
const hasManualEdits = diffs.some((d) => d.hasChanges);
|
|
892
|
+
if (hasManualEdits) {
|
|
893
|
+
const componentUpdates = {};
|
|
894
|
+
for (const [name, content] of Object.entries(this.editedContent)) {
|
|
895
|
+
const original = this.loadedComponents[name] || "";
|
|
896
|
+
if (DiffEngine.hasSignificantChanges(original, content)) {
|
|
897
|
+
const cid = await this.client.uploadContent(content, name);
|
|
898
|
+
componentUpdates[name] = cid;
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
const version = await this.client.updateEntity(this.pi, {
|
|
902
|
+
expect_tip: this.entity.manifest_cid,
|
|
903
|
+
components: componentUpdates,
|
|
904
|
+
note
|
|
905
|
+
});
|
|
906
|
+
this.result.saved = {
|
|
907
|
+
pi: version.pi,
|
|
908
|
+
newVersion: version.ver,
|
|
909
|
+
newTip: version.tip
|
|
910
|
+
};
|
|
911
|
+
this.entity.manifest_cid = version.tip;
|
|
912
|
+
this.entity.ver = version.ver;
|
|
913
|
+
}
|
|
914
|
+
if (this.scope.components.length > 0) {
|
|
915
|
+
const customPrompts = this.buildCustomPrompts();
|
|
916
|
+
const reprocessResult = await this.client.reprocess({
|
|
917
|
+
pi: this.pi,
|
|
918
|
+
phases: this.scope.components,
|
|
919
|
+
cascade: this.scope.cascade,
|
|
920
|
+
options: {
|
|
921
|
+
stop_at_pi: this.scope.stopAtPi,
|
|
922
|
+
custom_prompts: customPrompts,
|
|
923
|
+
custom_note: note
|
|
924
|
+
}
|
|
925
|
+
});
|
|
926
|
+
this.result.reprocess = reprocessResult;
|
|
927
|
+
this.statusUrl = reprocessResult.status_url;
|
|
928
|
+
}
|
|
929
|
+
return this.result;
|
|
930
|
+
} finally {
|
|
931
|
+
this.submitting = false;
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
/**
|
|
935
|
+
* Wait for reprocessing to complete
|
|
936
|
+
*/
|
|
937
|
+
async waitForCompletion(options) {
|
|
938
|
+
const opts = { ...DEFAULT_POLL_OPTIONS, ...options };
|
|
939
|
+
if (!this.statusUrl) {
|
|
940
|
+
return {
|
|
941
|
+
phase: "complete",
|
|
942
|
+
saveComplete: true
|
|
943
|
+
};
|
|
944
|
+
}
|
|
945
|
+
const startTime = Date.now();
|
|
946
|
+
let isFirstPoll = true;
|
|
947
|
+
while (true) {
|
|
948
|
+
const status = await this.client.getReprocessStatus(this.statusUrl, isFirstPoll);
|
|
949
|
+
isFirstPoll = false;
|
|
950
|
+
const editStatus = {
|
|
951
|
+
phase: status.status === "DONE" ? "complete" : status.status === "ERROR" ? "error" : "reprocessing",
|
|
952
|
+
saveComplete: true,
|
|
953
|
+
reprocessStatus: status,
|
|
954
|
+
error: status.error
|
|
955
|
+
};
|
|
956
|
+
if (opts.onProgress) {
|
|
957
|
+
opts.onProgress(editStatus);
|
|
958
|
+
}
|
|
959
|
+
if (status.status === "DONE" || status.status === "ERROR") {
|
|
960
|
+
return editStatus;
|
|
961
|
+
}
|
|
962
|
+
if (Date.now() - startTime > opts.timeoutMs) {
|
|
963
|
+
return {
|
|
964
|
+
phase: "error",
|
|
965
|
+
saveComplete: true,
|
|
966
|
+
reprocessStatus: status,
|
|
967
|
+
error: "Timeout waiting for reprocessing to complete"
|
|
968
|
+
};
|
|
969
|
+
}
|
|
970
|
+
await new Promise((resolve) => setTimeout(resolve, opts.intervalMs));
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
/**
|
|
974
|
+
* Get current status without waiting
|
|
975
|
+
*/
|
|
976
|
+
async getStatus() {
|
|
977
|
+
if (!this.statusUrl) {
|
|
978
|
+
return {
|
|
979
|
+
phase: this.result?.saved ? "complete" : "idle",
|
|
980
|
+
saveComplete: !!this.result?.saved
|
|
981
|
+
};
|
|
982
|
+
}
|
|
983
|
+
const status = await this.client.getReprocessStatus(this.statusUrl);
|
|
984
|
+
return {
|
|
985
|
+
phase: status.status === "DONE" ? "complete" : status.status === "ERROR" ? "error" : "reprocessing",
|
|
986
|
+
saveComplete: true,
|
|
987
|
+
reprocessStatus: status,
|
|
988
|
+
error: status.error
|
|
989
|
+
};
|
|
990
|
+
}
|
|
991
|
+
// ===========================================================================
|
|
992
|
+
// Private Helpers
|
|
993
|
+
// ===========================================================================
|
|
994
|
+
buildCustomPrompts() {
|
|
995
|
+
const custom = {};
|
|
996
|
+
if (this.mode === "ai-prompt") {
|
|
997
|
+
if (this.prompts["general"]) custom.general = this.prompts["general"];
|
|
998
|
+
if (this.prompts["pinax"]) custom.pinax = this.prompts["pinax"];
|
|
999
|
+
if (this.prompts["description"]) custom.description = this.prompts["description"];
|
|
1000
|
+
if (this.prompts["cheimarros"]) custom.cheimarros = this.prompts["cheimarros"];
|
|
1001
|
+
} else {
|
|
1002
|
+
const diffs = this.getDiff();
|
|
1003
|
+
const diffContext = DiffEngine.formatComponentDiffsForPrompt(diffs);
|
|
1004
|
+
const correctionContext = PromptBuilder.buildCorrectionPrompt(this.corrections);
|
|
1005
|
+
const basePrompt = [diffContext, correctionContext, this.prompts["general"]].filter(Boolean).join("\n\n");
|
|
1006
|
+
if (basePrompt) {
|
|
1007
|
+
custom.general = basePrompt;
|
|
1008
|
+
}
|
|
1009
|
+
if (this.prompts["pinax"]) custom.pinax = this.prompts["pinax"];
|
|
1010
|
+
if (this.prompts["description"]) custom.description = this.prompts["description"];
|
|
1011
|
+
if (this.prompts["cheimarros"]) custom.cheimarros = this.prompts["cheimarros"];
|
|
1012
|
+
}
|
|
1013
|
+
return custom;
|
|
1014
|
+
}
|
|
1015
|
+
};
|
|
1016
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1017
|
+
0 && (module.exports = {
|
|
1018
|
+
CASConflictError,
|
|
1019
|
+
DiffEngine,
|
|
1020
|
+
EditClient,
|
|
1021
|
+
EditError,
|
|
1022
|
+
EditSession,
|
|
1023
|
+
EntityNotFoundError,
|
|
1024
|
+
PermissionError,
|
|
1025
|
+
PromptBuilder,
|
|
1026
|
+
ReprocessError,
|
|
1027
|
+
ValidationError
|
|
1028
|
+
});
|
|
1029
|
+
//# sourceMappingURL=index.cjs.map
|