farming-code 2.2.20 → 2.2.22
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/README.md +5 -5
- package/README.zh_cn.md +5 -5
- package/THIRD_PARTY_NOTICES.md +5 -8
- package/backend/acp/client-services.js +37 -5
- package/backend/acp/patch-decisions.js +19 -3
- package/backend/acp-runtime.js +1201 -198
- package/backend/acp-transcript.js +66 -0
- package/backend/agent-lifecycle-journal.js +133 -0
- package/backend/agent-manager.js +2715 -354
- package/backend/agent-order.js +8 -4
- package/backend/agent-runtime-binding.js +1 -0
- package/backend/bundle-update-helper.js +5 -1
- package/backend/config-manager.js +16 -0
- package/backend/control-api.js +146 -20
- package/backend/farming-app-cli.js +335 -10
- package/backend/farming-session-store.js +283 -36
- package/backend/json-cli-runtime.js +120 -6
- package/backend/npm-update-helper.js +97 -18
- package/backend/provider-adapters.js +18 -6
- package/backend/review-session-service.js +31 -17
- package/backend/server.js +332 -102
- package/backend/storage-layout.js +10 -0
- package/backend/theme-manager.js +23 -5
- package/backend/update-service.js +239 -27
- package/backend/workspace-file-router.js +9 -3
- package/backend/workspace-file-service.js +292 -62
- package/dist/acp/codex-acp-1.1.4.js +32 -1
- package/dist/assets/App-CmZdNavW.js +210 -0
- package/dist/assets/{FileEditorMarkdownPreview-R0jwwLME.js → FileEditorMarkdownPreview-KbnE8VC8.js} +1 -1
- package/dist/assets/FileEditorPane-5_HUejjd.js +2 -0
- package/dist/assets/IconGlyphs-C2WFcoJS.js +1 -0
- package/dist/assets/ProjectFilesSection-C1RAJWOE.js +12 -0
- package/dist/assets/{ReviewPage-DBKNOWsJ.js → ReviewPage-L1vh1Ggs.js} +3 -3
- package/dist/assets/code-dark-BKtvf0Xc.css +1 -0
- package/dist/assets/file-icons-CHWt5nSy.js +1 -0
- package/dist/assets/{index-DuMjesL2.js → index-RvUiMpGP.js} +2 -2
- package/dist/assets/main-XMXgyJJt.css +1 -0
- package/dist/assets/workspace-view-state-BqB4HM7r.js +7 -0
- package/dist/index.html +1 -1
- package/frontend/skins/crt/app.js +199 -81
- package/package.json +9 -3
- package/dist/assets/App-BupehIbc.js +0 -210
- package/dist/assets/FileEditorPane-CJs6kfZO.js +0 -2
- package/dist/assets/IconGlyphs-CZzDr5o2.js +0 -1
- package/dist/assets/ProjectFilesSection-DWCV07lA.js +0 -12
- package/dist/assets/code-dark-BAt0Q_Op.css +0 -1
- package/dist/assets/file-icons-DpbrD5ZP.js +0 -1
- package/dist/assets/main-y2aK_56c.css +0 -1
- package/dist/assets/workspace-view-state-BpPINu84.js +0 -7
|
@@ -61,10 +61,11 @@ async function stopProcess(pid, timeoutMs = 15_000) {
|
|
|
61
61
|
|
|
62
62
|
function validatePayload(payload) {
|
|
63
63
|
if (!payload || typeof payload !== 'object') throw new Error('Missing npm update payload');
|
|
64
|
+
if (!['prepare', 'apply'].includes(payload.action)) throw new Error('Invalid npm update action');
|
|
64
65
|
if (!/^[A-Za-z0-9@/._-]+$/.test(String(payload.packageName || ''))) throw new Error('Invalid npm package name');
|
|
65
66
|
if (!/^[0-9A-Za-z.+-]+$/.test(String(payload.targetVersion || ''))) throw new Error('Invalid npm target version');
|
|
66
67
|
if (!/^[0-9A-Za-z.+-]+$/.test(String(payload.previousVersion || ''))) throw new Error('Invalid npm previous version');
|
|
67
|
-
for (const key of ['stateFile', 'logPath', 'cliPath', 'packageRoot', 'configDir']) {
|
|
68
|
+
for (const key of ['stateFile', 'logPath', 'cliPath', 'packageRoot', 'configDir', 'stagingPrefix', 'stagingPackageRoot']) {
|
|
68
69
|
if (!path.isAbsolute(String(payload[key] || ''))) throw new Error(`Invalid npm update ${key}`);
|
|
69
70
|
}
|
|
70
71
|
if (payload.npmPrefix && !path.isAbsolute(String(payload.npmPrefix))) {
|
|
@@ -81,6 +82,13 @@ function validatePayload(payload) {
|
|
|
81
82
|
throw new Error('Invalid npm update registry');
|
|
82
83
|
}
|
|
83
84
|
}
|
|
85
|
+
const expectedStagingRoot = path.join(payload.stagingPrefix, 'lib', 'node_modules', payload.packageName);
|
|
86
|
+
if (path.resolve(payload.stagingPackageRoot) !== path.resolve(expectedStagingRoot)) {
|
|
87
|
+
throw new Error('Invalid npm update stagingPackageRoot');
|
|
88
|
+
}
|
|
89
|
+
if (path.resolve(payload.stagingPackageRoot) === path.resolve(payload.packageRoot)) {
|
|
90
|
+
throw new Error('npm update staging root must differ from the running package root');
|
|
91
|
+
}
|
|
84
92
|
return payload;
|
|
85
93
|
}
|
|
86
94
|
|
|
@@ -93,6 +101,8 @@ function stateFor(payload, phase, extra = {}) {
|
|
|
93
101
|
packageName: payload.packageName,
|
|
94
102
|
startedAt: payload.startedAt,
|
|
95
103
|
logPath: payload.logPath,
|
|
104
|
+
stagingPrefix: payload.stagingPrefix,
|
|
105
|
+
stagingPackageRoot: payload.stagingPackageRoot,
|
|
96
106
|
...extra,
|
|
97
107
|
};
|
|
98
108
|
}
|
|
@@ -118,13 +128,13 @@ function commandEnvironment() {
|
|
|
118
128
|
return env;
|
|
119
129
|
}
|
|
120
130
|
|
|
121
|
-
async function installPackage(payload, version) {
|
|
131
|
+
async function installPackage(payload, version, npmPrefix = payload.npmPrefix) {
|
|
122
132
|
const packageSpec = `${payload.packageName}@${version}`;
|
|
123
|
-
return installPackageFromRegistry(payload, packageSpec);
|
|
133
|
+
return installPackageFromRegistry(payload, packageSpec, '', npmPrefix);
|
|
124
134
|
}
|
|
125
135
|
|
|
126
|
-
function verifyInstalledVersion(payload, expectedVersion) {
|
|
127
|
-
const packageJsonPath = path.join(
|
|
136
|
+
function verifyInstalledVersion(payload, expectedVersion, packageRoot = payload.packageRoot) {
|
|
137
|
+
const packageJsonPath = path.join(packageRoot, 'package.json');
|
|
128
138
|
let metadata;
|
|
129
139
|
try {
|
|
130
140
|
metadata = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
@@ -153,10 +163,10 @@ function logSince(filePath, offset) {
|
|
|
153
163
|
}
|
|
154
164
|
}
|
|
155
165
|
|
|
156
|
-
async function installPackageFromRegistry(payload, packageSpec, registryUrl = '') {
|
|
166
|
+
async function installPackageFromRegistry(payload, packageSpec, registryUrl = '', npmPrefix = payload.npmPrefix) {
|
|
157
167
|
appendLog(payload.logPath, `Installing ${packageSpec}${registryUrl ? ' from the update-status registry' : ''}`);
|
|
158
168
|
const args = ['install', '--global'];
|
|
159
|
-
if (
|
|
169
|
+
if (npmPrefix) args.push('--prefix', npmPrefix);
|
|
160
170
|
if (registryUrl) args.push('--registry', registryUrl);
|
|
161
171
|
args.push(packageSpec, '--no-audit', '--no-fund');
|
|
162
172
|
const offset = logSize(payload.logPath);
|
|
@@ -169,7 +179,7 @@ async function installPackageFromRegistry(payload, packageSpec, registryUrl = ''
|
|
|
169
179
|
} catch (error) {
|
|
170
180
|
if (!registryUrl && payload.npmFallbackRegistryUrl && /(?:ETARGET|No matching version found)/.test(logSince(payload.logPath, offset))) {
|
|
171
181
|
appendLog(payload.logPath, `Configured npm registry has no ${packageSpec}; retrying from the update-status registry`);
|
|
172
|
-
return installPackageFromRegistry(payload, packageSpec, payload.npmFallbackRegistryUrl);
|
|
182
|
+
return installPackageFromRegistry(payload, packageSpec, payload.npmFallbackRegistryUrl, npmPrefix);
|
|
173
183
|
}
|
|
174
184
|
throw error;
|
|
175
185
|
}
|
|
@@ -184,38 +194,100 @@ async function startServer(payload, version = payload.targetVersion) {
|
|
|
184
194
|
});
|
|
185
195
|
}
|
|
186
196
|
|
|
187
|
-
async function
|
|
188
|
-
const payload = validatePayload(rawPayload);
|
|
197
|
+
async function prepareNpmUpdate(payload) {
|
|
189
198
|
try {
|
|
190
199
|
writeJsonAtomic(payload.stateFile, stateFor(payload, 'installing'));
|
|
191
|
-
|
|
192
|
-
|
|
200
|
+
fs.mkdirSync(payload.stagingPrefix, { recursive: true });
|
|
201
|
+
await installPackage(payload, payload.targetVersion, payload.stagingPrefix);
|
|
202
|
+
verifyInstalledVersion(payload, payload.targetVersion, payload.stagingPackageRoot);
|
|
203
|
+
const preparedAt = new Date().toISOString();
|
|
204
|
+
writeJsonAtomic(payload.stateFile, stateFor(payload, 'ready-to-restart', {
|
|
205
|
+
preparedAt,
|
|
206
|
+
}));
|
|
207
|
+
appendLog(payload.logPath, `Farming ${payload.targetVersion} is ready to restart`);
|
|
208
|
+
} catch (error) {
|
|
209
|
+
const message = error && error.message ? error.message : String(error);
|
|
210
|
+
appendLog(payload.logPath, `Update preparation failed: ${message}`);
|
|
211
|
+
try {
|
|
212
|
+
fs.rmSync(payload.stagingPrefix, { recursive: true, force: true });
|
|
213
|
+
} catch (cleanupError) {
|
|
214
|
+
appendLog(payload.logPath, `Update preparation cleanup failed: ${cleanupError.message || cleanupError}`);
|
|
215
|
+
}
|
|
216
|
+
writeJsonAtomic(payload.stateFile, stateFor(payload, 'failed', {
|
|
217
|
+
error: message,
|
|
218
|
+
completedAt: new Date().toISOString(),
|
|
219
|
+
}));
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
async function applyNpmUpdate(payload) {
|
|
224
|
+
const backupRoot = path.join(
|
|
225
|
+
path.dirname(payload.packageRoot),
|
|
226
|
+
`.${path.basename(payload.packageRoot)}.backup-${process.pid}`,
|
|
227
|
+
);
|
|
228
|
+
let stoppedOldServer = false;
|
|
229
|
+
let movedOldPackage = false;
|
|
230
|
+
let movedNewPackage = false;
|
|
231
|
+
try {
|
|
232
|
+
verifyInstalledVersion(payload, payload.previousVersion, payload.packageRoot);
|
|
233
|
+
verifyInstalledVersion(payload, payload.targetVersion, payload.stagingPackageRoot);
|
|
234
|
+
if (fs.existsSync(backupRoot)) throw new Error(`npm update backup already exists: ${backupRoot}`);
|
|
193
235
|
|
|
194
|
-
writeJsonAtomic(payload.stateFile, stateFor(payload, 'restarting'
|
|
236
|
+
writeJsonAtomic(payload.stateFile, stateFor(payload, 'restarting', {
|
|
237
|
+
preparedAt: payload.preparedAt,
|
|
238
|
+
}));
|
|
239
|
+
await new Promise(resolve => setTimeout(resolve, 1_000));
|
|
195
240
|
await stopProcess(Number(payload.serverPid));
|
|
241
|
+
stoppedOldServer = true;
|
|
242
|
+
fs.renameSync(payload.packageRoot, backupRoot);
|
|
243
|
+
movedOldPackage = true;
|
|
244
|
+
fs.renameSync(payload.stagingPackageRoot, payload.packageRoot);
|
|
245
|
+
movedNewPackage = true;
|
|
196
246
|
await startServer(payload);
|
|
197
247
|
|
|
198
248
|
writeJsonAtomic(payload.stateFile, stateFor(payload, 'succeeded', {
|
|
249
|
+
preparedAt: payload.preparedAt,
|
|
199
250
|
completedAt: new Date().toISOString(),
|
|
200
251
|
}));
|
|
201
252
|
appendLog(payload.logPath, `Farming updated to ${payload.targetVersion}`);
|
|
253
|
+
try {
|
|
254
|
+
fs.rmSync(backupRoot, { recursive: true, force: true });
|
|
255
|
+
fs.rmSync(payload.stagingPrefix, { recursive: true, force: true });
|
|
256
|
+
} catch (cleanupError) {
|
|
257
|
+
appendLog(payload.logPath, `Update cleanup failed: ${cleanupError.message || cleanupError}`);
|
|
258
|
+
}
|
|
202
259
|
} catch (error) {
|
|
203
260
|
const message = error && error.message ? error.message : String(error);
|
|
204
|
-
appendLog(payload.logPath, `Update failed: ${message}`);
|
|
261
|
+
appendLog(payload.logPath, `Update apply failed: ${message}`);
|
|
205
262
|
|
|
206
|
-
if (!isProcessRunning(Number(payload.serverPid))) {
|
|
263
|
+
if (stoppedOldServer || !isProcessRunning(Number(payload.serverPid))) {
|
|
207
264
|
try {
|
|
208
|
-
writeJsonAtomic(payload.stateFile, stateFor(payload, 'rolling-back', {
|
|
209
|
-
|
|
210
|
-
|
|
265
|
+
writeJsonAtomic(payload.stateFile, stateFor(payload, 'rolling-back', {
|
|
266
|
+
preparedAt: payload.preparedAt,
|
|
267
|
+
error: message,
|
|
268
|
+
}));
|
|
269
|
+
if (movedNewPackage && fs.existsSync(payload.packageRoot)) {
|
|
270
|
+
fs.mkdirSync(path.dirname(payload.stagingPackageRoot), { recursive: true });
|
|
271
|
+
fs.renameSync(payload.packageRoot, payload.stagingPackageRoot);
|
|
272
|
+
}
|
|
273
|
+
if (movedOldPackage && fs.existsSync(backupRoot)) {
|
|
274
|
+
fs.renameSync(backupRoot, payload.packageRoot);
|
|
275
|
+
}
|
|
276
|
+
verifyInstalledVersion(payload, payload.previousVersion, payload.packageRoot);
|
|
211
277
|
await startServer(payload, payload.previousVersion);
|
|
212
278
|
writeJsonAtomic(payload.stateFile, stateFor(payload, 'rolled-back', {
|
|
213
279
|
version: payload.previousVersion,
|
|
214
280
|
attemptedVersion: payload.targetVersion,
|
|
281
|
+
preparedAt: payload.preparedAt,
|
|
215
282
|
error: message,
|
|
216
283
|
completedAt: new Date().toISOString(),
|
|
217
284
|
}));
|
|
218
285
|
appendLog(payload.logPath, `Rolled back to ${payload.previousVersion}`);
|
|
286
|
+
try {
|
|
287
|
+
fs.rmSync(payload.stagingPrefix, { recursive: true, force: true });
|
|
288
|
+
} catch (cleanupError) {
|
|
289
|
+
appendLog(payload.logPath, `Rollback cleanup failed: ${cleanupError.message || cleanupError}`);
|
|
290
|
+
}
|
|
219
291
|
return;
|
|
220
292
|
} catch (rollbackError) {
|
|
221
293
|
const rollbackMessage = rollbackError && rollbackError.message ? rollbackError.message : String(rollbackError);
|
|
@@ -228,12 +300,19 @@ async function runNpmUpdate(rawPayload) {
|
|
|
228
300
|
}
|
|
229
301
|
|
|
230
302
|
writeJsonAtomic(payload.stateFile, stateFor(payload, 'failed', {
|
|
303
|
+
preparedAt: payload.preparedAt,
|
|
231
304
|
error: message,
|
|
232
305
|
completedAt: new Date().toISOString(),
|
|
233
306
|
}));
|
|
234
307
|
}
|
|
235
308
|
}
|
|
236
309
|
|
|
310
|
+
async function runNpmUpdate(rawPayload) {
|
|
311
|
+
const payload = validatePayload(rawPayload);
|
|
312
|
+
if (payload.action === 'prepare') return prepareNpmUpdate(payload);
|
|
313
|
+
return applyNpmUpdate(payload);
|
|
314
|
+
}
|
|
315
|
+
|
|
237
316
|
if (require.main === module) {
|
|
238
317
|
let payload;
|
|
239
318
|
try {
|
|
@@ -221,7 +221,7 @@ const PROVIDER_ADAPTERS = Object.freeze([
|
|
|
221
221
|
terminalResumeArgs: (args, sessionId) => ['resume', sessionId, ...args],
|
|
222
222
|
acp: { packageName: '@agentclientprotocol/codex-acp', version: '1.1.4' },
|
|
223
223
|
prepareAcpEnvironment: codexAcpEnvironment,
|
|
224
|
-
capabilities: { runtimeSwitch: true, terminalProfile: true, goals: false },
|
|
224
|
+
capabilities: { runtimeSwitch: true, terminalProfile: true, goals: false, sessionFork: true },
|
|
225
225
|
},
|
|
226
226
|
{
|
|
227
227
|
id: 'claude',
|
|
@@ -233,8 +233,12 @@ const PROVIDER_ADAPTERS = Object.freeze([
|
|
|
233
233
|
commands: ['claude'],
|
|
234
234
|
supportedRuntimes: ['terminal', 'acp'],
|
|
235
235
|
planSession: (rawArgs, launchArgs) => explicitSessionPlan('claude', rawArgs, launchArgs),
|
|
236
|
-
acp: {
|
|
237
|
-
|
|
236
|
+
acp: {
|
|
237
|
+
packageName: '@agentclientprotocol/claude-agent-acp',
|
|
238
|
+
version: '0.59.0',
|
|
239
|
+
forkMode: 'target-process',
|
|
240
|
+
},
|
|
241
|
+
capabilities: { runtimeSwitch: true, terminalProfile: false, goals: false, sessionFork: true },
|
|
238
242
|
},
|
|
239
243
|
{
|
|
240
244
|
id: 'opencode',
|
|
@@ -263,7 +267,7 @@ const PROVIDER_ADAPTERS = Object.freeze([
|
|
|
263
267
|
args: ['acp', '--cwd', path.resolve(options.cwd || process.cwd())],
|
|
264
268
|
}),
|
|
265
269
|
},
|
|
266
|
-
capabilities: { runtimeSwitch: true, terminalProfile: false, goals: false },
|
|
270
|
+
capabilities: { runtimeSwitch: true, terminalProfile: false, goals: false, sessionFork: true },
|
|
267
271
|
},
|
|
268
272
|
{
|
|
269
273
|
id: 'qoder',
|
|
@@ -279,7 +283,7 @@ const PROVIDER_ADAPTERS = Object.freeze([
|
|
|
279
283
|
version: 'native',
|
|
280
284
|
launch: options => ({ command: options.executable || 'qodercli', args: ['--acp'] }),
|
|
281
285
|
},
|
|
282
|
-
capabilities: { runtimeSwitch: true, terminalProfile: false, goals: false },
|
|
286
|
+
capabilities: { runtimeSwitch: true, terminalProfile: false, goals: false, sessionFork: true },
|
|
283
287
|
},
|
|
284
288
|
]);
|
|
285
289
|
|
|
@@ -303,10 +307,13 @@ function listProviderAdapters() {
|
|
|
303
307
|
function providerCapabilities(provider) {
|
|
304
308
|
const adapter = getProviderAdapter(provider);
|
|
305
309
|
return {
|
|
306
|
-
supportedRuntimes: adapter
|
|
310
|
+
supportedRuntimes: adapter
|
|
311
|
+
? adapter.supportedRuntimes.filter(runtime => runtime !== 'json')
|
|
312
|
+
: ['terminal'],
|
|
307
313
|
runtimeSwitch: adapter?.capabilities?.runtimeSwitch === true,
|
|
308
314
|
terminalProfile: adapter?.capabilities?.terminalProfile === true,
|
|
309
315
|
goals: adapter?.capabilities?.goals === true,
|
|
316
|
+
sessionFork: adapter?.capabilities?.sessionFork === true,
|
|
310
317
|
...(adapter
|
|
311
318
|
? chatCapabilitiesForProvider(provider)
|
|
312
319
|
: { chatRuntime: '', supportsChat: false, supportsSteer: false }),
|
|
@@ -317,6 +324,10 @@ function providerSupportsRuntime(provider, runtime) {
|
|
|
317
324
|
return getProviderAdapter(provider)?.supportedRuntimes.includes(runtime) === true;
|
|
318
325
|
}
|
|
319
326
|
|
|
327
|
+
function providerAcpForkMode(provider) {
|
|
328
|
+
return getProviderAdapter(provider)?.acp?.forkMode || 'source-then-load';
|
|
329
|
+
}
|
|
330
|
+
|
|
320
331
|
function applyProviderHomeEnvironment(env, provider, homePath) {
|
|
321
332
|
const key = getProviderAdapter(provider)?.homeEnvKey;
|
|
322
333
|
if (key && homePath) env[key] = homePath;
|
|
@@ -332,6 +343,7 @@ module.exports = {
|
|
|
332
343
|
applyProviderHomeEnvironment,
|
|
333
344
|
isFreshAcpSessionSource,
|
|
334
345
|
listProviderAdapters,
|
|
346
|
+
providerAcpForkMode,
|
|
335
347
|
providerCapabilities,
|
|
336
348
|
providerForProgram,
|
|
337
349
|
providerSupportsRuntime,
|
|
@@ -178,6 +178,18 @@ class ReviewSessionService {
|
|
|
178
178
|
this.reviewStateStore = reviewStateStore;
|
|
179
179
|
this.resolveAgentRoot = options.resolveAgentRoot;
|
|
180
180
|
this.resolveAcpReviewChanges = options.resolveAcpReviewChanges;
|
|
181
|
+
this.refreshQueues = new Map();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
enqueueRefresh(reviewId, operation) {
|
|
185
|
+
const previous = this.refreshQueues.get(reviewId) || Promise.resolve();
|
|
186
|
+
const next = previous.catch(() => {}).then(operation);
|
|
187
|
+
this.refreshQueues.set(reviewId, next);
|
|
188
|
+
const cleanup = () => {
|
|
189
|
+
if (this.refreshQueues.get(reviewId) === next) this.refreshQueues.delete(reviewId);
|
|
190
|
+
};
|
|
191
|
+
next.then(cleanup, cleanup);
|
|
192
|
+
return next;
|
|
181
193
|
}
|
|
182
194
|
|
|
183
195
|
async git(root, args, options = {}) {
|
|
@@ -373,24 +385,26 @@ class ReviewSessionService {
|
|
|
373
385
|
|
|
374
386
|
async refresh(reviewId) {
|
|
375
387
|
if (!REVIEW_ID_PATTERN.test(reviewId)) throw new ReviewSessionError('review session id is invalid');
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
388
|
+
return this.enqueueRefresh(reviewId, async () => {
|
|
389
|
+
const current = this.sessionStore.get(reviewId);
|
|
390
|
+
if (!current) throw new ReviewSessionError('review session not found', 404);
|
|
391
|
+
const paths = await this.capturePaths(current.root, current);
|
|
392
|
+
const tree = await this.captureStableTree(current.root, paths);
|
|
393
|
+
const previous = current.revisions[current.revisions.length - 1];
|
|
394
|
+
if (previous.tree === tree) return { ...publicRevision(current, previous), changedPaths: [], unchanged: true };
|
|
395
|
+
const nextNumber = previous.number + 1;
|
|
396
|
+
const changedPaths = await this.changedPaths(current.root, previous.tree, tree);
|
|
397
|
+
await this.keepRevision(current.root, reviewId, nextNumber, tree);
|
|
398
|
+
const result = this.sessionStore.appendRevision(reviewId, tree);
|
|
399
|
+
const next = result.session.revisions[result.session.revisions.length - 1];
|
|
400
|
+
this.reviewStateStore?.inheritPatchset?.({
|
|
401
|
+
changedPaths,
|
|
402
|
+
nextPatchset: tree,
|
|
403
|
+
previousPatchset: previous.tree,
|
|
404
|
+
reviewId,
|
|
405
|
+
});
|
|
406
|
+
return { ...publicRevision(result.session, next), changedPaths, unchanged: false };
|
|
392
407
|
});
|
|
393
|
-
return { ...publicRevision(result.session, next), changedPaths, unchanged: false };
|
|
394
408
|
}
|
|
395
409
|
|
|
396
410
|
get(reviewId) {
|