farming-code 2.2.17 → 2.2.19
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/backend/agent-manager.js +226 -49
- package/backend/bundle-update-helper.js +109 -0
- package/backend/config-manager.js +110 -4
- package/backend/farming-session-store.js +62 -2
- package/backend/server.js +364 -58
- package/backend/update-service.js +46 -23
- package/dist/assets/App-CbXYBccJ.js +210 -0
- package/dist/assets/{FileEditorPane-DNy3LAqS.js → FileEditorPane-CJs6kfZO.js} +1 -1
- package/dist/assets/{IconGlyphs-BsUfqpKN.js → IconGlyphs-CZzDr5o2.js} +1 -1
- package/dist/assets/{ProjectFilesSection-BhDEUVHA.js → ProjectFilesSection-DWCV07lA.js} +1 -1
- package/dist/assets/{ReviewPage-ChtByBe3.js → ReviewPage-DBKNOWsJ.js} +1 -1
- package/dist/assets/code-dark-qIBiLBWV.css +1 -0
- package/dist/assets/{index-C8ZFKye1.js → index-DQdtPfCk.js} +2 -2
- package/dist/assets/main-yzZEfgft.css +1 -0
- package/dist/index.html +1 -1
- package/frontend/skins/crt/app.js +91 -9
- package/frontend/skins/crt/index.html +16 -28
- package/package.json +1 -1
- package/dist/assets/App-6yWdwzj8.js +0 -210
- package/dist/assets/code-dark-5esj0bRu.css +0 -1
- package/dist/assets/main-DSO3i_Gs.css +0 -1
|
@@ -222,13 +222,14 @@ class ConfigManager {
|
|
|
222
222
|
if (!expanded) continue;
|
|
223
223
|
const resolved = path.resolve(expanded);
|
|
224
224
|
if (resolved === path.parse(resolved).root || this.isInternalWorkspace(resolved)) continue;
|
|
225
|
-
let canonical;
|
|
225
|
+
let canonical = resolved;
|
|
226
226
|
try {
|
|
227
227
|
canonical = fs.realpathSync(resolved);
|
|
228
|
-
if (!fs.statSync(canonical).isDirectory()) continue;
|
|
229
228
|
} catch {
|
|
230
|
-
|
|
229
|
+
// Project membership is durable. A temporarily unavailable path remains
|
|
230
|
+
// until the user explicitly removes it.
|
|
231
231
|
}
|
|
232
|
+
if (this.isInternalWorkspace(canonical)) continue;
|
|
232
233
|
if (seen.has(canonical)) continue;
|
|
233
234
|
seen.add(canonical);
|
|
234
235
|
result.push(canonical);
|
|
@@ -752,6 +753,97 @@ class ConfigManager {
|
|
|
752
753
|
};
|
|
753
754
|
}
|
|
754
755
|
|
|
756
|
+
mountProjectWorkspace(workspace) {
|
|
757
|
+
const expanded = this.expandWorkspacePath(workspace);
|
|
758
|
+
const resolved = expanded ? path.resolve(expanded) : '';
|
|
759
|
+
let canonicalWorkspace = '';
|
|
760
|
+
if (resolved) {
|
|
761
|
+
try {
|
|
762
|
+
canonicalWorkspace = fs.realpathSync(resolved);
|
|
763
|
+
if (!fs.statSync(canonicalWorkspace).isDirectory()) canonicalWorkspace = '';
|
|
764
|
+
} catch {
|
|
765
|
+
canonicalWorkspace = '';
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
if (
|
|
769
|
+
!canonicalWorkspace
|
|
770
|
+
|| canonicalWorkspace === path.parse(canonicalWorkspace).root
|
|
771
|
+
|| this.isInternalWorkspace(canonicalWorkspace)
|
|
772
|
+
) {
|
|
773
|
+
throw new Error('Project workspace is invalid or unavailable');
|
|
774
|
+
}
|
|
775
|
+
const current = this.settings.projectWorkspaces || [];
|
|
776
|
+
if (!current.includes(canonicalWorkspace)) {
|
|
777
|
+
this.commitProjectMembership({
|
|
778
|
+
projectWorkspaces: [canonicalWorkspace, ...current],
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
return {
|
|
782
|
+
workspace: canonicalWorkspace,
|
|
783
|
+
projectWorkspaces: [...(this.settings.projectWorkspaces || [])],
|
|
784
|
+
pinnedProjectWorkspaces: [...(this.settings.pinnedProjectWorkspaces || [])],
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
removeProjectWorkspace(workspace) {
|
|
789
|
+
const expanded = this.expandWorkspacePath(workspace);
|
|
790
|
+
if (!expanded) throw new Error('Project workspace is required');
|
|
791
|
+
const resolved = path.resolve(expanded);
|
|
792
|
+
const candidates = new Set([expanded, resolved]);
|
|
793
|
+
try {
|
|
794
|
+
candidates.add(fs.realpathSync(resolved));
|
|
795
|
+
} catch {
|
|
796
|
+
// A deleted worktree can still be removed by its last canonical path.
|
|
797
|
+
}
|
|
798
|
+
const current = this.settings.projectWorkspaces || [];
|
|
799
|
+
const nextProjects = current.filter(entry => !candidates.has(entry));
|
|
800
|
+
const currentPinned = this.settings.pinnedProjectWorkspaces || [];
|
|
801
|
+
const nextPinned = currentPinned.filter(entry => !candidates.has(entry));
|
|
802
|
+
if (nextProjects.length !== current.length || nextPinned.length !== currentPinned.length) {
|
|
803
|
+
this.commitProjectMembership({
|
|
804
|
+
projectWorkspaces: nextProjects,
|
|
805
|
+
pinnedProjectWorkspaces: nextPinned,
|
|
806
|
+
});
|
|
807
|
+
}
|
|
808
|
+
return {
|
|
809
|
+
workspace: current.find(entry => candidates.has(entry)) || resolved,
|
|
810
|
+
projectWorkspaces: [...(this.settings.projectWorkspaces || [])],
|
|
811
|
+
pinnedProjectWorkspaces: [...(this.settings.pinnedProjectWorkspaces || [])],
|
|
812
|
+
};
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
setProjectWorkspacePinned(workspace, pinned) {
|
|
816
|
+
const [canonicalWorkspace] = this.normalizeProjectWorkspaces([workspace]);
|
|
817
|
+
if (!canonicalWorkspace || !(this.settings.projectWorkspaces || []).includes(canonicalWorkspace)) {
|
|
818
|
+
throw new Error('Project does not exist');
|
|
819
|
+
}
|
|
820
|
+
const current = this.settings.pinnedProjectWorkspaces || [];
|
|
821
|
+
const nextPinned = pinned
|
|
822
|
+
? [...current.filter(entry => entry !== canonicalWorkspace), canonicalWorkspace]
|
|
823
|
+
: current.filter(entry => entry !== canonicalWorkspace);
|
|
824
|
+
if (
|
|
825
|
+
nextPinned.length !== current.length
|
|
826
|
+
|| nextPinned.some((entry, index) => entry !== current[index])
|
|
827
|
+
) {
|
|
828
|
+
this.commitProjectMembership({ pinnedProjectWorkspaces: nextPinned });
|
|
829
|
+
}
|
|
830
|
+
return {
|
|
831
|
+
workspace: canonicalWorkspace,
|
|
832
|
+
projectWorkspaces: [...(this.settings.projectWorkspaces || [])],
|
|
833
|
+
pinnedProjectWorkspaces: [...(this.settings.pinnedProjectWorkspaces || [])],
|
|
834
|
+
};
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
commitProjectMembership(settingsPatch) {
|
|
838
|
+
const previousSettings = this.settings;
|
|
839
|
+
try {
|
|
840
|
+
this.updateSettings(settingsPatch);
|
|
841
|
+
} catch (error) {
|
|
842
|
+
this.settings = previousSettings;
|
|
843
|
+
throw error;
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
|
|
755
847
|
getMainPageSessionKeys() {
|
|
756
848
|
return this.sessionStore ? this.sessionStore.getMainPageSessionKeys() : [];
|
|
757
849
|
}
|
|
@@ -776,6 +868,10 @@ class ConfigManager {
|
|
|
776
868
|
return this.sessionStore ? this.sessionStore.ensureRecordForAgent(agent, patch) : '';
|
|
777
869
|
}
|
|
778
870
|
|
|
871
|
+
getAgentSessionRecordForProviderSessionKey(sessionKey) {
|
|
872
|
+
return this.sessionStore ? this.sessionStore.getRecordForProviderSessionKey(sessionKey) : null;
|
|
873
|
+
}
|
|
874
|
+
|
|
779
875
|
setProviderSessionDisplayState(sessionKey, patch = {}) {
|
|
780
876
|
return this.sessionStore ? this.sessionStore.setProviderSessionDisplayState(sessionKey, patch) : '';
|
|
781
877
|
}
|
|
@@ -794,7 +890,17 @@ class ConfigManager {
|
|
|
794
890
|
|
|
795
891
|
writeSettingsFile() {
|
|
796
892
|
fs.mkdirSync(this.farmingDir, { recursive: true });
|
|
797
|
-
|
|
893
|
+
const tempFile = `${this.settingsFile}.${process.pid}.${Date.now()}.tmp`;
|
|
894
|
+
try {
|
|
895
|
+
fs.writeFileSync(tempFile, JSON.stringify(this.settings, null, 2));
|
|
896
|
+
fs.renameSync(tempFile, this.settingsFile);
|
|
897
|
+
} finally {
|
|
898
|
+
try {
|
|
899
|
+
fs.unlinkSync(tempFile);
|
|
900
|
+
} catch {
|
|
901
|
+
// A successful rename already removed the temporary path.
|
|
902
|
+
}
|
|
903
|
+
}
|
|
798
904
|
}
|
|
799
905
|
|
|
800
906
|
appendTaskHistory(entry) {
|
|
@@ -6,6 +6,24 @@ const storageLayout = require('./storage-layout');
|
|
|
6
6
|
|
|
7
7
|
const SESSION_ID_PREFIX = 'fsess';
|
|
8
8
|
const MAX_MAIN_PAGE_SESSION_KEYS = 50;
|
|
9
|
+
const PRODUCT_STATE_FIELDS = [
|
|
10
|
+
'projectWorkspace',
|
|
11
|
+
'task',
|
|
12
|
+
'workflowTemplate',
|
|
13
|
+
'pinned',
|
|
14
|
+
'projectOrder',
|
|
15
|
+
'pinnedOrder',
|
|
16
|
+
'attentionSeq',
|
|
17
|
+
'readAttentionSeq',
|
|
18
|
+
'attentionUpdatedAt',
|
|
19
|
+
'readAttentionAt',
|
|
20
|
+
'attentionReason',
|
|
21
|
+
'attentionOutputEpoch',
|
|
22
|
+
'attentionOutputSeq',
|
|
23
|
+
'readOutputEpoch',
|
|
24
|
+
'readOutputSeq',
|
|
25
|
+
'customTitle',
|
|
26
|
+
];
|
|
9
27
|
|
|
10
28
|
function now() {
|
|
11
29
|
return Date.now();
|
|
@@ -184,10 +202,15 @@ class FarmingSessionStore {
|
|
|
184
202
|
attentionUpdatedAt: typeof agent.attentionUpdatedAt === 'number' ? agent.attentionUpdatedAt : null,
|
|
185
203
|
readAttentionAt: typeof agent.readAttentionAt === 'number' ? agent.readAttentionAt : null,
|
|
186
204
|
attentionReason: typeof agent.attentionReason === 'string' ? agent.attentionReason : '',
|
|
205
|
+
attentionOutputEpoch: typeof agent.attentionOutputEpoch === 'string' ? agent.attentionOutputEpoch : '',
|
|
187
206
|
attentionOutputSeq: typeof agent.attentionOutputSeq === 'number' ? agent.attentionOutputSeq : null,
|
|
207
|
+
readOutputEpoch: typeof agent.readOutputEpoch === 'string' ? agent.readOutputEpoch : '',
|
|
208
|
+
readOutputSeq: typeof agent.readOutputSeq === 'number' ? agent.readOutputSeq : null,
|
|
188
209
|
archived: agent.archived === true,
|
|
189
210
|
archivedAt: typeof agent.archivedAt === 'number' ? agent.archivedAt : null,
|
|
190
|
-
|
|
211
|
+
...(typeof agent.customTitle === 'string' && agent.customTitle
|
|
212
|
+
? { customTitle: agent.customTitle }
|
|
213
|
+
: {}),
|
|
191
214
|
title: typeof agent.customTitle === 'string' && agent.customTitle
|
|
192
215
|
? agent.customTitle
|
|
193
216
|
: (typeof agent.providerSessionTitle === 'string' && agent.providerSessionTitle
|
|
@@ -222,6 +245,9 @@ class FarmingSessionStore {
|
|
|
222
245
|
providerSessionTemporary: false,
|
|
223
246
|
updatedAt: now(),
|
|
224
247
|
};
|
|
248
|
+
if (typeof record.customTitle === 'string' && record.customTitle) {
|
|
249
|
+
record.title = record.customTitle;
|
|
250
|
+
}
|
|
225
251
|
index.providerSessionRecords[sessionKey] = id;
|
|
226
252
|
this.writeRecord(record);
|
|
227
253
|
this.writeIndex();
|
|
@@ -231,10 +257,38 @@ class FarmingSessionStore {
|
|
|
231
257
|
ensureRecordForAgent(agent, patch = {}) {
|
|
232
258
|
const providerSessionKey = this.providerSessionKeyForAgent(agent);
|
|
233
259
|
if (providerSessionKey) {
|
|
260
|
+
const previousId = safeSessionFileName(agent?.persistentSessionId)
|
|
261
|
+
? agent.persistentSessionId
|
|
262
|
+
: '';
|
|
263
|
+
const previous = previousId ? this.readRecord(previousId) : null;
|
|
264
|
+
const canonical = this.getRecordForProviderSessionKey(providerSessionKey);
|
|
265
|
+
const agentPatch = this.recordPatchFromAgent(agent);
|
|
266
|
+
if (canonical && previousId && canonical.id !== previousId) {
|
|
267
|
+
PRODUCT_STATE_FIELDS.forEach(field => {
|
|
268
|
+
if (Object.prototype.hasOwnProperty.call(canonical, field)) {
|
|
269
|
+
agentPatch[field] = canonical[field];
|
|
270
|
+
} else if (previous && Object.prototype.hasOwnProperty.call(previous, field)) {
|
|
271
|
+
agentPatch[field] = previous[field];
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
}
|
|
234
275
|
const id = this.ensureRecordForProviderSessionKey(providerSessionKey, {
|
|
235
|
-
...
|
|
276
|
+
...agentPatch,
|
|
236
277
|
...patch,
|
|
237
278
|
}, agent?.persistentSessionId || '');
|
|
279
|
+
if (previousId && previousId !== id) {
|
|
280
|
+
if (previous && (previous.providerSessionTemporary === true || !previous.providerSessionKey)) {
|
|
281
|
+
this.writeRecord({
|
|
282
|
+
...previous,
|
|
283
|
+
runtimeAgentId: '',
|
|
284
|
+
visibleOnMainPage: false,
|
|
285
|
+
archived: true,
|
|
286
|
+
archivedAt: now(),
|
|
287
|
+
mergedInto: id,
|
|
288
|
+
updatedAt: now(),
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
}
|
|
238
292
|
return id;
|
|
239
293
|
}
|
|
240
294
|
|
|
@@ -345,6 +399,12 @@ class FarmingSessionStore {
|
|
|
345
399
|
return this.ensureIndex().mainPageSessionKeys.slice();
|
|
346
400
|
}
|
|
347
401
|
|
|
402
|
+
getRecordForProviderSessionKey(sessionKey) {
|
|
403
|
+
const index = this.ensureIndex();
|
|
404
|
+
const id = index.providerSessionRecords[sessionKey];
|
|
405
|
+
return safeSessionFileName(id) ? this.readRecord(id) : null;
|
|
406
|
+
}
|
|
407
|
+
|
|
348
408
|
listAgentRecords() {
|
|
349
409
|
this.ensureIndex();
|
|
350
410
|
let names = [];
|