orkestrator 2.34.4-beta → 2.34.5-beta
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 +12 -0
- package/bin/orkestrator-launcher.js +42 -3
- package/dist/client.js +1800 -147
- package/dist/main.js +180427 -168862
- package/package.json +4 -4
- package/resources/acp-bridge/dist/index.js +22719 -0
- package/resources/claude-bridge/dist/index.js +140 -27
- package/resources/codex-bridge/dist/index.js +558 -48
- package/resources/cursor-bridge/dist/index.js +36696 -0
- package/resources/cursor-bridge/runtime/bun.lock +40 -0
- package/resources/cursor-bridge/runtime/install.json +4 -0
- package/resources/cursor-bridge/runtime/package.json +11 -0
- package/resources/cursor-bridge/runtime/patches/@cursor%2Fsdk@1.2.0.patch +19 -0
- package/resources/pi-bridge/dist/index.js +36035 -0
- package/resources/pi-bridge/runtime/bun.lock +259 -0
- package/resources/pi-bridge/runtime/install.json +4 -0
- package/resources/pi-bridge/runtime/package.json +10 -0
package/dist/client.js
CHANGED
|
@@ -147,13 +147,16 @@ __export(exports_standalone_service, {
|
|
|
147
147
|
STANDALONE_MAX_BOOT_ATTEMPTS: () => STANDALONE_MAX_BOOT_ATTEMPTS,
|
|
148
148
|
STANDALONE_PACKAGE: () => STANDALONE_PACKAGE,
|
|
149
149
|
activateRelease: () => activateRelease,
|
|
150
|
+
effectiveServiceArguments: () => effectiveServiceArguments,
|
|
150
151
|
installLauncher: () => installLauncher,
|
|
151
152
|
installRelease: () => installRelease,
|
|
152
153
|
isNewerVersion: () => isNewerVersion,
|
|
153
154
|
linkedVersion: () => linkedVersion,
|
|
155
|
+
nextRollbackTarget: () => nextRollbackTarget,
|
|
154
156
|
readLauncherState: () => readLauncherState,
|
|
155
157
|
readReleaseManifest: () => readReleaseManifest,
|
|
156
158
|
readServiceConfig: () => readServiceConfig,
|
|
159
|
+
readStartupFailure: () => readStartupFailure,
|
|
157
160
|
readText: () => readText,
|
|
158
161
|
releaseDirectory: () => releaseDirectory,
|
|
159
162
|
releaseEntry: () => releaseEntry,
|
|
@@ -202,9 +205,31 @@ function readServiceConfig(layout) {
|
|
|
202
205
|
bun: value.bun,
|
|
203
206
|
bunVersion: typeof value.bunVersion === "string" ? value.bunVersion : "",
|
|
204
207
|
args: value.args.filter((arg) => typeof arg === "string"),
|
|
208
|
+
...typeof value.rollbackTarget?.version === "string" && Array.isArray(value.rollbackTarget.args) ? {
|
|
209
|
+
rollbackTarget: {
|
|
210
|
+
version: value.rollbackTarget.version,
|
|
211
|
+
args: value.rollbackTarget.args.filter((arg) => typeof arg === "string")
|
|
212
|
+
}
|
|
213
|
+
} : {},
|
|
214
|
+
...typeof value.installGeneration === "string" ? { installGeneration: value.installGeneration } : {},
|
|
205
215
|
installedAt: typeof value.installedAt === "string" ? value.installedAt : ""
|
|
206
216
|
};
|
|
207
217
|
}
|
|
218
|
+
function nextRollbackTarget(layout, config, before, version) {
|
|
219
|
+
if (!config || !before)
|
|
220
|
+
return;
|
|
221
|
+
const target = config.rollbackTarget;
|
|
222
|
+
if (before === version)
|
|
223
|
+
return target?.version === version ? undefined : target;
|
|
224
|
+
return {
|
|
225
|
+
version: before,
|
|
226
|
+
args: effectiveServiceArguments(config, before, linkedVersion(layout.previous))
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
function effectiveServiceArguments(config, current, previous) {
|
|
230
|
+
const target = config?.rollbackTarget;
|
|
231
|
+
return target && target.version === current && previous === current ? target.args : config?.args ?? [];
|
|
232
|
+
}
|
|
208
233
|
async function writeServiceConfig(layout, config) {
|
|
209
234
|
await mkdir(layout.root, { recursive: true });
|
|
210
235
|
await writeAtomically(layout.serviceFile, `${JSON.stringify(config, null, 2)}
|
|
@@ -213,13 +238,30 @@ async function writeServiceConfig(layout, config) {
|
|
|
213
238
|
function readLauncherState(layout) {
|
|
214
239
|
const value = readJson(layout.stateFile);
|
|
215
240
|
const rollback = value?.rollback;
|
|
241
|
+
const bootFailure = value?.bootFailure;
|
|
216
242
|
return {
|
|
217
243
|
version: 1,
|
|
244
|
+
...typeof value?.installGeneration === "string" ? { installGeneration: value.installGeneration } : {},
|
|
218
245
|
lastReadyVersion: typeof value?.lastReadyVersion === "string" ? value.lastReadyVersion : null,
|
|
219
246
|
bootAttempts: value?.bootAttempts && typeof value.bootAttempts === "object" ? value.bootAttempts : {},
|
|
220
|
-
rollback: rollback && typeof rollback.failedVersion === "string" && typeof rollback.restoredVersion === "string" && typeof rollback.at === "string" ? rollback : null
|
|
247
|
+
rollback: rollback && typeof rollback.failedVersion === "string" && typeof rollback.restoredVersion === "string" && typeof rollback.at === "string" ? rollback : null,
|
|
248
|
+
bootFailure: bootFailure && typeof bootFailure.version === "string" && typeof bootFailure.exits === "number" && typeof bootFailure.at === "string" ? bootFailure : null
|
|
221
249
|
};
|
|
222
250
|
}
|
|
251
|
+
function readStartupFailure(layout, version, sinceMs, installGeneration) {
|
|
252
|
+
const state = readLauncherState(layout);
|
|
253
|
+
const rollback = state.rollback;
|
|
254
|
+
if (rollback?.failedVersion === version && (installGeneration === undefined || rollback.installGeneration === installGeneration) && Date.parse(rollback.at) >= sinceMs) {
|
|
255
|
+
return { restoredVersion: rollback.restoredVersion };
|
|
256
|
+
}
|
|
257
|
+
const failure = state.bootFailure;
|
|
258
|
+
if (failure?.version !== version || installGeneration !== undefined && failure.installGeneration !== installGeneration || failure.exits < STANDALONE_MAX_BOOT_ATTEMPTS || Date.parse(failure.at) < sinceMs) {
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
const previous = linkedVersion(layout.previous);
|
|
262
|
+
const rollbackComing = previous && previous !== version && state.lastReadyVersion !== version;
|
|
263
|
+
return rollbackComing ? null : { restoredVersion: null };
|
|
264
|
+
}
|
|
223
265
|
async function writeAtomically(file, contents) {
|
|
224
266
|
const temporary = `${file}.${process.pid}.${Date.now()}.tmp`;
|
|
225
267
|
await writeFile(temporary, contents, { mode: 384 });
|
|
@@ -3457,7 +3499,7 @@ var require_crc32 = __commonJS(function(exports, module) {
|
|
|
3457
3499
|
});
|
|
3458
3500
|
|
|
3459
3501
|
// ../../node_modules/.bun/yauzl@3.4.0/node_modules/yauzl/index.js
|
|
3460
|
-
function
|
|
3502
|
+
function open2(path, options2, callback) {
|
|
3461
3503
|
if (typeof options2 === "function") {
|
|
3462
3504
|
callback = options2;
|
|
3463
3505
|
options2 = null;
|
|
@@ -3773,7 +3815,7 @@ var init_yauzl = __esm(() => {
|
|
|
3773
3815
|
PassThrough = __require("stream").PassThrough;
|
|
3774
3816
|
Writable = __require("stream").Writable;
|
|
3775
3817
|
crc32 = typeof zlib.crc32 === "function" ? zlib.crc32 : require_crc32();
|
|
3776
|
-
$open =
|
|
3818
|
+
$open = open2;
|
|
3777
3819
|
$validateFileName = validateFileName;
|
|
3778
3820
|
util.inherits(ZipFile, EventEmitter);
|
|
3779
3821
|
ZipFile.prototype.close = function() {
|
|
@@ -4391,10 +4433,10 @@ function pinnedToolchainArtifacts(platform = process.platform, architecture = pr
|
|
|
4391
4433
|
var PINNED_TOOLCHAIN_VERSIONS, PINNED_SUPPORT_TOOL_VERSIONS, PINNED_TOOL_VERSIONS, GITHUB_RELEASE_HOSTS, NPM_REGISTRY_HOSTS, GROK_DOWNLOAD_HOSTS, CODEX_RELEASE_BASE, PI_RELEASE_BASE, GH_RELEASE_BASE, OPENCODE_RELEASE_BASE, CODEX_CODE_MODE_HOST_FILE_NAME = "codex-code-mode-host", PINNED_TOOLCHAIN_ARTIFACTS;
|
|
4392
4434
|
var init_manifest = __esm(() => {
|
|
4393
4435
|
PINNED_TOOLCHAIN_VERSIONS = {
|
|
4394
|
-
claude: "2.1.
|
|
4395
|
-
codex: "0.
|
|
4396
|
-
grok: "1.0.
|
|
4397
|
-
opencode: "1.19.
|
|
4436
|
+
claude: "2.1.314",
|
|
4437
|
+
codex: "0.166.0",
|
|
4438
|
+
grok: "1.0.70",
|
|
4439
|
+
opencode: "1.19.5",
|
|
4398
4440
|
pi: "1.4.0"
|
|
4399
4441
|
};
|
|
4400
4442
|
PINNED_SUPPORT_TOOL_VERSIONS = {
|
|
@@ -4425,23 +4467,23 @@ var init_manifest = __esm(() => {
|
|
|
4425
4467
|
format: "tar.gz",
|
|
4426
4468
|
url: `${CODEX_RELEASE_BASE}/codex-aarch64-apple-darwin.tar.gz`,
|
|
4427
4469
|
entryPath: "codex-aarch64-apple-darwin",
|
|
4428
|
-
size:
|
|
4429
|
-
sha256: "
|
|
4470
|
+
size: 107610108,
|
|
4471
|
+
sha256: "c48824462fc57681eafb0925d97b11b589010475613904f570b42fea53e240d5",
|
|
4430
4472
|
allowedHosts: GITHUB_RELEASE_HOSTS
|
|
4431
4473
|
},
|
|
4432
4474
|
companions: [
|
|
4433
4475
|
codexCodeModeHost("aarch64-apple-darwin", {
|
|
4434
|
-
size:
|
|
4435
|
-
sha256: "
|
|
4476
|
+
size: 24769716,
|
|
4477
|
+
sha256: "7abd50fe5f033495a7b693c45fb82dc57093a18f1c4ba3e8043577de46c5c7c5"
|
|
4436
4478
|
}, {
|
|
4437
|
-
size:
|
|
4438
|
-
sha256: "
|
|
4479
|
+
size: 69182864,
|
|
4480
|
+
sha256: "fd37051bfdce0b65cf22a7077e265720a7bd6709d5e400dcbc26197f3041400a"
|
|
4439
4481
|
})
|
|
4440
4482
|
],
|
|
4441
4483
|
executable: {
|
|
4442
4484
|
fileName: "codex",
|
|
4443
|
-
size:
|
|
4444
|
-
sha256: "
|
|
4485
|
+
size: 268972064,
|
|
4486
|
+
sha256: "39acf979729dd290f9a24ee38b2914e036c91b65952872e28ae2a6b88a7db7c1"
|
|
4445
4487
|
}
|
|
4446
4488
|
},
|
|
4447
4489
|
{
|
|
@@ -4453,23 +4495,23 @@ var init_manifest = __esm(() => {
|
|
|
4453
4495
|
format: "tar.gz",
|
|
4454
4496
|
url: `${CODEX_RELEASE_BASE}/codex-x86_64-apple-darwin.tar.gz`,
|
|
4455
4497
|
entryPath: "codex-x86_64-apple-darwin",
|
|
4456
|
-
size:
|
|
4457
|
-
sha256: "
|
|
4498
|
+
size: 117027024,
|
|
4499
|
+
sha256: "db0c38713c36199b12e7c8d1629386c35a4ce8f99e1c9ce72f1725a74df67b57",
|
|
4458
4500
|
allowedHosts: GITHUB_RELEASE_HOSTS
|
|
4459
4501
|
},
|
|
4460
4502
|
companions: [
|
|
4461
4503
|
codexCodeModeHost("x86_64-apple-darwin", {
|
|
4462
|
-
size:
|
|
4463
|
-
sha256: "
|
|
4504
|
+
size: 26748407,
|
|
4505
|
+
sha256: "b35c775d5e5f8d60d05e0de7154e5bbbf320b6d0e28a3237d42cc8a742ccaf65"
|
|
4464
4506
|
}, {
|
|
4465
|
-
size:
|
|
4466
|
-
sha256: "
|
|
4507
|
+
size: 73183424,
|
|
4508
|
+
sha256: "9d96de017ecec30ec3f8689f489584151c167c255a787dc6e297b6edc9ebe08c"
|
|
4467
4509
|
})
|
|
4468
4510
|
],
|
|
4469
4511
|
executable: {
|
|
4470
4512
|
fileName: "codex",
|
|
4471
|
-
size:
|
|
4472
|
-
sha256: "
|
|
4513
|
+
size: 289445104,
|
|
4514
|
+
sha256: "1361e18268866b7f9f5e8703fabcea0a2a990a439eedcce03940abb4ba9eaaeb"
|
|
4473
4515
|
}
|
|
4474
4516
|
},
|
|
4475
4517
|
{
|
|
@@ -4481,23 +4523,23 @@ var init_manifest = __esm(() => {
|
|
|
4481
4523
|
format: "tar.gz",
|
|
4482
4524
|
url: `${CODEX_RELEASE_BASE}/codex-aarch64-unknown-linux-musl.tar.gz`,
|
|
4483
4525
|
entryPath: "codex-aarch64-unknown-linux-musl",
|
|
4484
|
-
size:
|
|
4485
|
-
sha256: "
|
|
4526
|
+
size: 113382496,
|
|
4527
|
+
sha256: "94eb707605ec2646b2e05b7edf7c4e7b3ccf02b16237c34ae9beb8cfb7f810a9",
|
|
4486
4528
|
allowedHosts: GITHUB_RELEASE_HOSTS
|
|
4487
4529
|
},
|
|
4488
4530
|
companions: [
|
|
4489
4531
|
codexCodeModeHost("aarch64-unknown-linux-musl", {
|
|
4490
|
-
size:
|
|
4491
|
-
sha256: "
|
|
4532
|
+
size: 27023046,
|
|
4533
|
+
sha256: "ac98d1f878e353a16cba711fd47bfef2f6c814109bdd819ee2b6b6d6a19701e7"
|
|
4492
4534
|
}, {
|
|
4493
|
-
size:
|
|
4494
|
-
sha256: "
|
|
4535
|
+
size: 70838624,
|
|
4536
|
+
sha256: "587436a943a59c18cabdbe83f07800d237b60ddd2e0f54d05b23d8b37a0b36f4"
|
|
4495
4537
|
})
|
|
4496
4538
|
],
|
|
4497
4539
|
executable: {
|
|
4498
4540
|
fileName: "codex",
|
|
4499
|
-
size:
|
|
4500
|
-
sha256: "
|
|
4541
|
+
size: 277549176,
|
|
4542
|
+
sha256: "f2e6c980c937e734644bed1b4a9a018d3e63a0ccbe71aafc85c86a8c6e4b462a"
|
|
4501
4543
|
}
|
|
4502
4544
|
},
|
|
4503
4545
|
{
|
|
@@ -4509,23 +4551,23 @@ var init_manifest = __esm(() => {
|
|
|
4509
4551
|
format: "tar.gz",
|
|
4510
4552
|
url: `${CODEX_RELEASE_BASE}/codex-x86_64-unknown-linux-musl.tar.gz`,
|
|
4511
4553
|
entryPath: "codex-x86_64-unknown-linux-musl",
|
|
4512
|
-
size:
|
|
4513
|
-
sha256: "
|
|
4554
|
+
size: 121764138,
|
|
4555
|
+
sha256: "a0bf572d2f93f056c6712a6da348e96078dc84f3885fd7be67807ef389cb3978",
|
|
4514
4556
|
allowedHosts: GITHUB_RELEASE_HOSTS
|
|
4515
4557
|
},
|
|
4516
4558
|
companions: [
|
|
4517
4559
|
codexCodeModeHost("x86_64-unknown-linux-musl", {
|
|
4518
|
-
size:
|
|
4519
|
-
sha256: "
|
|
4560
|
+
size: 28565521,
|
|
4561
|
+
sha256: "7588334fd47bc4003d970b70dfd4d1e4578491b1e9baa72976a4c2d7e97590da"
|
|
4520
4562
|
}, {
|
|
4521
|
-
size:
|
|
4522
|
-
sha256: "
|
|
4563
|
+
size: 77997328,
|
|
4564
|
+
sha256: "657f4e902726e05867c937b583049d5997e057f8cd0ab40450633c9c9ef8d630"
|
|
4523
4565
|
})
|
|
4524
4566
|
],
|
|
4525
4567
|
executable: {
|
|
4526
4568
|
fileName: "codex",
|
|
4527
|
-
size:
|
|
4528
|
-
sha256: "
|
|
4569
|
+
size: 319346888,
|
|
4570
|
+
sha256: "b8a11fedf24b2dfb59a70c9e52b030f9a508dcd49cd2dd0d41956f4c785156b8"
|
|
4529
4571
|
}
|
|
4530
4572
|
},
|
|
4531
4573
|
{
|
|
@@ -4537,14 +4579,14 @@ var init_manifest = __esm(() => {
|
|
|
4537
4579
|
format: "zip",
|
|
4538
4580
|
url: `${OPENCODE_RELEASE_BASE}/opencode-darwin-arm64.zip`,
|
|
4539
4581
|
entryPath: "opencode",
|
|
4540
|
-
size:
|
|
4541
|
-
sha256: "
|
|
4582
|
+
size: 64278924,
|
|
4583
|
+
sha256: "c1c9e62521192192ef4c3ed1355ada2144055bddb61682217e997b03c55203c1",
|
|
4542
4584
|
allowedHosts: GITHUB_RELEASE_HOSTS
|
|
4543
4585
|
},
|
|
4544
4586
|
executable: {
|
|
4545
4587
|
fileName: "opencode",
|
|
4546
|
-
size:
|
|
4547
|
-
sha256: "
|
|
4588
|
+
size: 180672544,
|
|
4589
|
+
sha256: "feaafead8cd563af0c2dcb386b0757e4355e327ebb6bd1aaad5440fe2f83596c",
|
|
4548
4590
|
repairInvalidMacSignature: true
|
|
4549
4591
|
}
|
|
4550
4592
|
},
|
|
@@ -4557,14 +4599,14 @@ var init_manifest = __esm(() => {
|
|
|
4557
4599
|
format: "zip",
|
|
4558
4600
|
url: `${OPENCODE_RELEASE_BASE}/opencode-darwin-x64.zip`,
|
|
4559
4601
|
entryPath: "opencode",
|
|
4560
|
-
size:
|
|
4561
|
-
sha256: "
|
|
4602
|
+
size: 69100888,
|
|
4603
|
+
sha256: "b0212efa6db347187225a17fa3a31c6b261bf4c9f1589a290b0ca902d720d250",
|
|
4562
4604
|
allowedHosts: GITHUB_RELEASE_HOSTS
|
|
4563
4605
|
},
|
|
4564
4606
|
executable: {
|
|
4565
4607
|
fileName: "opencode",
|
|
4566
|
-
size:
|
|
4567
|
-
sha256: "
|
|
4608
|
+
size: 193954400,
|
|
4609
|
+
sha256: "4b15c8aab1844936e9e1d1327213355d94c820de8c7084fad88d3812a067d3d0",
|
|
4568
4610
|
repairInvalidMacSignature: true
|
|
4569
4611
|
}
|
|
4570
4612
|
},
|
|
@@ -4577,14 +4619,14 @@ var init_manifest = __esm(() => {
|
|
|
4577
4619
|
format: "tar.gz",
|
|
4578
4620
|
url: `${OPENCODE_RELEASE_BASE}/opencode-linux-arm64.tar.gz`,
|
|
4579
4621
|
entryPath: "opencode",
|
|
4580
|
-
size:
|
|
4581
|
-
sha256: "
|
|
4622
|
+
size: 78513091,
|
|
4623
|
+
sha256: "fae5be262d693b7c5e562856cc473e59e741772936db0c0600ac0d5ed5bd088b",
|
|
4582
4624
|
allowedHosts: GITHUB_RELEASE_HOSTS
|
|
4583
4625
|
},
|
|
4584
4626
|
executable: {
|
|
4585
4627
|
fileName: "opencode",
|
|
4586
|
-
size:
|
|
4587
|
-
sha256: "
|
|
4628
|
+
size: 211209208,
|
|
4629
|
+
sha256: "dab85b46f4ea4813da5367997f99a60aab64a2118d58ebf552980e792747436c"
|
|
4588
4630
|
}
|
|
4589
4631
|
},
|
|
4590
4632
|
{
|
|
@@ -4596,14 +4638,14 @@ var init_manifest = __esm(() => {
|
|
|
4596
4638
|
format: "tar.gz",
|
|
4597
4639
|
url: `${OPENCODE_RELEASE_BASE}/opencode-linux-x64.tar.gz`,
|
|
4598
4640
|
entryPath: "opencode",
|
|
4599
|
-
size:
|
|
4600
|
-
sha256: "
|
|
4641
|
+
size: 79651896,
|
|
4642
|
+
sha256: "588310a336fa3b9df36b3f0225b26f679d453efaad0cf3d66c3651ef3ec1647a",
|
|
4601
4643
|
allowedHosts: GITHUB_RELEASE_HOSTS
|
|
4602
4644
|
},
|
|
4603
4645
|
executable: {
|
|
4604
4646
|
fileName: "opencode",
|
|
4605
|
-
size:
|
|
4606
|
-
sha256: "
|
|
4647
|
+
size: 214386864,
|
|
4648
|
+
sha256: "7df4e4136301f16589b0f30bf91a61380fca5f8ef22dd36570f5ebe1d4ec802c"
|
|
4607
4649
|
}
|
|
4608
4650
|
},
|
|
4609
4651
|
{
|
|
@@ -4615,14 +4657,14 @@ var init_manifest = __esm(() => {
|
|
|
4615
4657
|
format: "tar.gz",
|
|
4616
4658
|
url: claudeArchiveUrl("darwin-arm64"),
|
|
4617
4659
|
entryPath: "package/claude",
|
|
4618
|
-
size:
|
|
4619
|
-
sha256: "
|
|
4660
|
+
size: 124539940,
|
|
4661
|
+
sha256: "e4524422963f1c79a4894b5ecb70e123cc45a3dd3a0915f312f6135f70aa570c",
|
|
4620
4662
|
allowedHosts: NPM_REGISTRY_HOSTS
|
|
4621
4663
|
},
|
|
4622
4664
|
executable: {
|
|
4623
4665
|
fileName: "claude",
|
|
4624
|
-
size:
|
|
4625
|
-
sha256: "
|
|
4666
|
+
size: 278848096,
|
|
4667
|
+
sha256: "21d6e7b9a6f07b4cfcb7df13760b3b72aa25e084e13a5354413382987ba832ed"
|
|
4626
4668
|
}
|
|
4627
4669
|
},
|
|
4628
4670
|
{
|
|
@@ -4634,14 +4676,14 @@ var init_manifest = __esm(() => {
|
|
|
4634
4676
|
format: "tar.gz",
|
|
4635
4677
|
url: claudeArchiveUrl("darwin-x64"),
|
|
4636
4678
|
entryPath: "package/claude",
|
|
4637
|
-
size:
|
|
4638
|
-
sha256: "
|
|
4679
|
+
size: 129924753,
|
|
4680
|
+
sha256: "f0041d107ad1a613e82fa4d8149e44390c975c3488ca34812fa27ccc9a92b8d6",
|
|
4639
4681
|
allowedHosts: NPM_REGISTRY_HOSTS
|
|
4640
4682
|
},
|
|
4641
4683
|
executable: {
|
|
4642
4684
|
fileName: "claude",
|
|
4643
|
-
size:
|
|
4644
|
-
sha256: "
|
|
4685
|
+
size: 290710016,
|
|
4686
|
+
sha256: "404779a308a358ecec8b25c8bccf0ce79a94cf261d12cb74065518d189c174f8"
|
|
4645
4687
|
}
|
|
4646
4688
|
},
|
|
4647
4689
|
{
|
|
@@ -4653,14 +4695,14 @@ var init_manifest = __esm(() => {
|
|
|
4653
4695
|
format: "tar.gz",
|
|
4654
4696
|
url: claudeArchiveUrl("linux-arm64"),
|
|
4655
4697
|
entryPath: "package/claude",
|
|
4656
|
-
size:
|
|
4657
|
-
sha256: "
|
|
4698
|
+
size: 136687968,
|
|
4699
|
+
sha256: "5a8087f6b0df5796326bb8e58c6e89637c58f28a16aea45434a7e914e854b0dc",
|
|
4658
4700
|
allowedHosts: NPM_REGISTRY_HOSTS
|
|
4659
4701
|
},
|
|
4660
4702
|
executable: {
|
|
4661
4703
|
fileName: "claude",
|
|
4662
|
-
size:
|
|
4663
|
-
sha256: "
|
|
4704
|
+
size: 297067016,
|
|
4705
|
+
sha256: "a66c0a8f5d0c87a439953d797985192bbcbaefbce2e0d7b2eaa8df30ef0f3ef2"
|
|
4664
4706
|
}
|
|
4665
4707
|
},
|
|
4666
4708
|
{
|
|
@@ -4672,14 +4714,14 @@ var init_manifest = __esm(() => {
|
|
|
4672
4714
|
format: "tar.gz",
|
|
4673
4715
|
url: claudeArchiveUrl("linux-x64"),
|
|
4674
4716
|
entryPath: "package/claude",
|
|
4675
|
-
size:
|
|
4676
|
-
sha256: "
|
|
4717
|
+
size: 137217467,
|
|
4718
|
+
sha256: "c6b690e52ba2d038c4c8d2566b2ec35acef4995c4321f7d57fa464a7088c2b78",
|
|
4677
4719
|
allowedHosts: NPM_REGISTRY_HOSTS
|
|
4678
4720
|
},
|
|
4679
4721
|
executable: {
|
|
4680
4722
|
fileName: "claude",
|
|
4681
|
-
size:
|
|
4682
|
-
sha256: "
|
|
4723
|
+
size: 298229464,
|
|
4724
|
+
sha256: "4487042b4c4f5d88d4568f68ae98aae0b1ffae58864cf57fb868641bc5f84594"
|
|
4683
4725
|
}
|
|
4684
4726
|
},
|
|
4685
4727
|
{
|
|
@@ -4691,14 +4733,14 @@ var init_manifest = __esm(() => {
|
|
|
4691
4733
|
format: "raw",
|
|
4692
4734
|
url: `https://storage.googleapis.com/grok-build-public-artifacts/cli/grok-${PINNED_TOOLCHAIN_VERSIONS.grok}-macos-aarch64`,
|
|
4693
4735
|
entryPath: "",
|
|
4694
|
-
size:
|
|
4695
|
-
sha256: "
|
|
4736
|
+
size: 179063008,
|
|
4737
|
+
sha256: "5c8a688251bf61e35370b828b6a353b0a20fcc3f64000de8a91d426039f29cec",
|
|
4696
4738
|
allowedHosts: GROK_DOWNLOAD_HOSTS
|
|
4697
4739
|
},
|
|
4698
4740
|
executable: {
|
|
4699
4741
|
fileName: "grok",
|
|
4700
|
-
size:
|
|
4701
|
-
sha256: "
|
|
4742
|
+
size: 179063008,
|
|
4743
|
+
sha256: "5c8a688251bf61e35370b828b6a353b0a20fcc3f64000de8a91d426039f29cec"
|
|
4702
4744
|
}
|
|
4703
4745
|
},
|
|
4704
4746
|
{
|
|
@@ -4710,14 +4752,14 @@ var init_manifest = __esm(() => {
|
|
|
4710
4752
|
format: "raw",
|
|
4711
4753
|
url: `https://storage.googleapis.com/grok-build-public-artifacts/cli/grok-${PINNED_TOOLCHAIN_VERSIONS.grok}-macos-x86_64`,
|
|
4712
4754
|
entryPath: "",
|
|
4713
|
-
size:
|
|
4714
|
-
sha256: "
|
|
4755
|
+
size: 193578304,
|
|
4756
|
+
sha256: "d9ecd462f1f3c3b40a7a131944987ee7ed1c38069b7009cc4107398ab767f484",
|
|
4715
4757
|
allowedHosts: GROK_DOWNLOAD_HOSTS
|
|
4716
4758
|
},
|
|
4717
4759
|
executable: {
|
|
4718
4760
|
fileName: "grok",
|
|
4719
|
-
size:
|
|
4720
|
-
sha256: "
|
|
4761
|
+
size: 193578304,
|
|
4762
|
+
sha256: "d9ecd462f1f3c3b40a7a131944987ee7ed1c38069b7009cc4107398ab767f484"
|
|
4721
4763
|
}
|
|
4722
4764
|
},
|
|
4723
4765
|
{
|
|
@@ -4729,14 +4771,14 @@ var init_manifest = __esm(() => {
|
|
|
4729
4771
|
format: "raw",
|
|
4730
4772
|
url: `https://storage.googleapis.com/grok-build-public-artifacts/cli/grok-${PINNED_TOOLCHAIN_VERSIONS.grok}-linux-aarch64`,
|
|
4731
4773
|
entryPath: "",
|
|
4732
|
-
size:
|
|
4733
|
-
sha256: "
|
|
4774
|
+
size: 166639048,
|
|
4775
|
+
sha256: "3aa36883574b263088e5addffe241f55430e8daaee1a0c4934b02180007a9761",
|
|
4734
4776
|
allowedHosts: GROK_DOWNLOAD_HOSTS
|
|
4735
4777
|
},
|
|
4736
4778
|
executable: {
|
|
4737
4779
|
fileName: "grok",
|
|
4738
|
-
size:
|
|
4739
|
-
sha256: "
|
|
4780
|
+
size: 166639048,
|
|
4781
|
+
sha256: "3aa36883574b263088e5addffe241f55430e8daaee1a0c4934b02180007a9761"
|
|
4740
4782
|
}
|
|
4741
4783
|
},
|
|
4742
4784
|
{
|
|
@@ -4748,14 +4790,14 @@ var init_manifest = __esm(() => {
|
|
|
4748
4790
|
format: "raw",
|
|
4749
4791
|
url: `https://storage.googleapis.com/grok-build-public-artifacts/cli/grok-${PINNED_TOOLCHAIN_VERSIONS.grok}-linux-x86_64`,
|
|
4750
4792
|
entryPath: "",
|
|
4751
|
-
size:
|
|
4752
|
-
sha256: "
|
|
4793
|
+
size: 195700192,
|
|
4794
|
+
sha256: "22d281c5964c5444147328096ba2a221f3f0874c6858e27c825656a80f6e58e9",
|
|
4753
4795
|
allowedHosts: GROK_DOWNLOAD_HOSTS
|
|
4754
4796
|
},
|
|
4755
4797
|
executable: {
|
|
4756
4798
|
fileName: "grok",
|
|
4757
|
-
size:
|
|
4758
|
-
sha256: "
|
|
4799
|
+
size: 195700192,
|
|
4800
|
+
sha256: "22d281c5964c5444147328096ba2a221f3f0874c6858e27c825656a80f6e58e9"
|
|
4759
4801
|
}
|
|
4760
4802
|
},
|
|
4761
4803
|
{
|
|
@@ -4893,7 +4935,7 @@ var init_manifest = __esm(() => {
|
|
|
4893
4935
|
|
|
4894
4936
|
// ../toolchain/src/manager.ts
|
|
4895
4937
|
import { spawn } from "child_process";
|
|
4896
|
-
import { createHash as createHash4, randomUUID as
|
|
4938
|
+
import { createHash as createHash4, randomUUID as randomUUID5 } from "crypto";
|
|
4897
4939
|
import { createReadStream, createWriteStream } from "fs";
|
|
4898
4940
|
import {
|
|
4899
4941
|
chmod,
|
|
@@ -4901,13 +4943,13 @@ import {
|
|
|
4901
4943
|
lstat,
|
|
4902
4944
|
mkdir as mkdir3,
|
|
4903
4945
|
mkdtemp as mkdtemp2,
|
|
4904
|
-
open as
|
|
4946
|
+
open as open3,
|
|
4905
4947
|
readFile as readFile2,
|
|
4906
4948
|
readdir as readdir3,
|
|
4907
4949
|
readlink,
|
|
4908
4950
|
rename as rename2,
|
|
4909
4951
|
rm as rm3,
|
|
4910
|
-
stat as
|
|
4952
|
+
stat as stat3,
|
|
4911
4953
|
symlink as symlink2,
|
|
4912
4954
|
utimes
|
|
4913
4955
|
} from "fs/promises";
|
|
@@ -5214,7 +5256,7 @@ async function acquireInstallLock(rootDir, onProgress, timings, openLockFile, ow
|
|
|
5214
5256
|
let announcedWait = false;
|
|
5215
5257
|
while (Date.now() - startedAt < timings.lockWaitTimeoutMs) {
|
|
5216
5258
|
const owner = {
|
|
5217
|
-
token:
|
|
5259
|
+
token: randomUUID5(),
|
|
5218
5260
|
pid: process.pid,
|
|
5219
5261
|
createdAt: new Date().toISOString()
|
|
5220
5262
|
};
|
|
@@ -5260,7 +5302,7 @@ async function acquireInstallLock(rootDir, onProgress, timings, openLockFile, ow
|
|
|
5260
5302
|
continue;
|
|
5261
5303
|
}
|
|
5262
5304
|
}
|
|
5263
|
-
const lockStat = await
|
|
5305
|
+
const lockStat = await stat3(lockPath).catch(() => null);
|
|
5264
5306
|
if (!observedOwner && lockStat && Date.now() - lockStat.mtimeMs > timings.lockStaleAfterMs) {
|
|
5265
5307
|
const currentOwner = await readInstallLockOwner(lockPath);
|
|
5266
5308
|
if (!currentOwner) {
|
|
@@ -5307,7 +5349,7 @@ async function downloadArchive(label, archive, archivePath, fetchImpl, onBytes,
|
|
|
5307
5349
|
if (contentLength !== null && Number(contentLength) !== archive.size) {
|
|
5308
5350
|
throw new Error(`${label} archive size header did not match the pinned manifest`);
|
|
5309
5351
|
}
|
|
5310
|
-
handle = await
|
|
5352
|
+
handle = await open3(archivePath, "wx", 384);
|
|
5311
5353
|
const reader = response.body.getReader();
|
|
5312
5354
|
const hash = createHash4("sha256");
|
|
5313
5355
|
let received = 0;
|
|
@@ -5583,7 +5625,7 @@ async function repairInvalidMacSignature(executablePath, artifact, timeoutMs, sp
|
|
|
5583
5625
|
await runCodesign(["--sign", "-", "--force", executablePath], `${artifact.name} could not be ad-hoc signed after source verification`, timeoutMs, spawnProcess);
|
|
5584
5626
|
}
|
|
5585
5627
|
async function prepareRepairableExecutable(sourcePath, executablePath, artifact, skipExecutableProbeForTests, processTimeoutMs, spawnProcess) {
|
|
5586
|
-
const temporaryPath = path17.join(path17.dirname(executablePath), `.repair-${artifact.executable.fileName}-${
|
|
5628
|
+
const temporaryPath = path17.join(path17.dirname(executablePath), `.repair-${artifact.executable.fileName}-${randomUUID5()}.tmp`);
|
|
5587
5629
|
try {
|
|
5588
5630
|
await copyFile2(sourcePath, temporaryPath);
|
|
5589
5631
|
await chmod(temporaryPath, 448);
|
|
@@ -5743,7 +5785,7 @@ async function acquireVersionLease(rootDir, artifact, openLeaseFile) {
|
|
|
5743
5785
|
return;
|
|
5744
5786
|
processVersionLeases.delete(leaseKey);
|
|
5745
5787
|
const owner = {
|
|
5746
|
-
token:
|
|
5788
|
+
token: randomUUID5(),
|
|
5747
5789
|
pid: process.pid,
|
|
5748
5790
|
createdAt: new Date().toISOString()
|
|
5749
5791
|
};
|
|
@@ -5799,7 +5841,7 @@ async function pruneSupersededVersions(rootDir, artifacts, retainSupersededMs, o
|
|
|
5799
5841
|
await Promise.all(entries.filter((entry) => entry.isDirectory() && !keep.has(entry.name)).map(async (entry) => {
|
|
5800
5842
|
const versionDir = path17.join(toolDir, entry.name);
|
|
5801
5843
|
try {
|
|
5802
|
-
if ((await
|
|
5844
|
+
if ((await stat3(versionDir)).mtimeMs > staleBefore)
|
|
5803
5845
|
return;
|
|
5804
5846
|
if (await versionHasLiveLease(rootDir, name, entry.name, ownerProcessExists))
|
|
5805
5847
|
return;
|
|
@@ -5855,7 +5897,7 @@ async function activateExecutables(rootDir, artifacts) {
|
|
|
5855
5897
|
const executables = {};
|
|
5856
5898
|
const activate = async (linkName, target) => {
|
|
5857
5899
|
const activePath = path17.join(binDir, linkName);
|
|
5858
|
-
const temporaryLink = path17.join(binDir, `.${linkName}-${
|
|
5900
|
+
const temporaryLink = path17.join(binDir, `.${linkName}-${randomUUID5()}.tmp`);
|
|
5859
5901
|
const existingTarget = await readlink(activePath).catch(() => null);
|
|
5860
5902
|
if (existingTarget !== target) {
|
|
5861
5903
|
await symlink2(target, temporaryLink, "file");
|
|
@@ -5921,7 +5963,7 @@ async function ensurePinnedToolchains(options2) {
|
|
|
5921
5963
|
phase: "waiting",
|
|
5922
5964
|
completedTools: 0,
|
|
5923
5965
|
message
|
|
5924
|
-
}), timings, options2.openLockFileForTests ??
|
|
5966
|
+
}), timings, options2.openLockFileForTests ?? open3, ownerProcessExists);
|
|
5925
5967
|
try {
|
|
5926
5968
|
await cleanStagingDirectories(rootDir);
|
|
5927
5969
|
const statuses = await Promise.all(artifacts.map((artifact) => installationStatus(rootDir, artifact)));
|
|
@@ -5995,7 +6037,7 @@ async function ensurePinnedToolchains(options2) {
|
|
|
5995
6037
|
const result = await activateExecutables(rootDir, artifacts);
|
|
5996
6038
|
await Promise.all(artifacts.map((artifact) => touchArtifactDirectory(rootDir, artifact)));
|
|
5997
6039
|
if (!options2.skipVersionLeaseForTests) {
|
|
5998
|
-
await Promise.all(artifacts.map((artifact) => acquireVersionLease(rootDir, artifact, options2.openLeaseFileForTests ??
|
|
6040
|
+
await Promise.all(artifacts.map((artifact) => acquireVersionLease(rootDir, artifact, options2.openLeaseFileForTests ?? open3)));
|
|
5999
6041
|
}
|
|
6000
6042
|
await options2.onActivated?.(result);
|
|
6001
6043
|
await pruneSupersededVersions(rootDir, artifacts, timings.retainSupersededMs, ownerProcessExists, options2.removeSupersededVersionForTests ?? rm3);
|
|
@@ -6072,7 +6114,7 @@ var init_platform_selection = __esm(() => {
|
|
|
6072
6114
|
});
|
|
6073
6115
|
|
|
6074
6116
|
// ../toolchain/src/install.ts
|
|
6075
|
-
import { randomUUID as
|
|
6117
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
6076
6118
|
import { lstat as lstat2, mkdir as mkdir4, readdir as readdir4, readlink as readlink2, rename as rename3, rm as rm4, symlink as symlink3 } from "fs/promises";
|
|
6077
6119
|
import path19 from "path";
|
|
6078
6120
|
async function resolveToolchainSelection(options2) {
|
|
@@ -6150,7 +6192,7 @@ async function pointCurrentAt(rootDir, binDir) {
|
|
|
6150
6192
|
}
|
|
6151
6193
|
if (existing && await readlink2(link) === target)
|
|
6152
6194
|
return link;
|
|
6153
|
-
const temporary = path19.join(binRoot, `.${CURRENT_TOOLCHAIN_LINK}-${
|
|
6195
|
+
const temporary = path19.join(binRoot, `.${CURRENT_TOOLCHAIN_LINK}-${randomUUID6()}.tmp`);
|
|
6154
6196
|
await symlink3(target, temporary, "dir");
|
|
6155
6197
|
try {
|
|
6156
6198
|
await rename3(temporary, link);
|
|
@@ -6443,7 +6485,7 @@ var PUBLIC_ACTIONS = {
|
|
|
6443
6485
|
effects: [],
|
|
6444
6486
|
observation: "immediate",
|
|
6445
6487
|
milestone: "A",
|
|
6446
|
-
summary: "List native-agent sessions of one environment without reading transcripts."
|
|
6488
|
+
summary: "List native-agent sessions of one environment or Coordinator conversation without reading transcripts."
|
|
6447
6489
|
},
|
|
6448
6490
|
"session.get": {
|
|
6449
6491
|
version: 1,
|
|
@@ -6488,10 +6530,10 @@ var PUBLIC_ACTIONS = {
|
|
|
6488
6530
|
"session.stop": {
|
|
6489
6531
|
version: 1,
|
|
6490
6532
|
mutation: true,
|
|
6491
|
-
effects: ["provider-session"],
|
|
6533
|
+
effects: ["metadata", "provider-session", "filesystem", "process"],
|
|
6492
6534
|
observation: "receipt",
|
|
6493
6535
|
milestone: "C",
|
|
6494
|
-
summary: "Stop the current turn
|
|
6536
|
+
summary: "Stop the current turn and pause its goal, optionally bound to an expected run."
|
|
6495
6537
|
},
|
|
6496
6538
|
"session.steer": {
|
|
6497
6539
|
version: 1,
|
|
@@ -6565,6 +6607,46 @@ var PUBLIC_ACTIONS = {
|
|
|
6565
6607
|
milestone: "C",
|
|
6566
6608
|
summary: "Pause or resume one pending interaction's countdown where its deadline is enforced."
|
|
6567
6609
|
},
|
|
6610
|
+
"session.goal.set": {
|
|
6611
|
+
version: 1,
|
|
6612
|
+
mutation: true,
|
|
6613
|
+
effects: ["provider-session", "filesystem", "process"],
|
|
6614
|
+
observation: "receipt",
|
|
6615
|
+
milestone: "D",
|
|
6616
|
+
summary: "Give one agent tab a durable objective: the backend sends turns until a check proves it, the agent needs a person, or a limit stops it."
|
|
6617
|
+
},
|
|
6618
|
+
"session.goal.get": {
|
|
6619
|
+
version: 1,
|
|
6620
|
+
mutation: false,
|
|
6621
|
+
effects: [],
|
|
6622
|
+
observation: "immediate",
|
|
6623
|
+
milestone: "D",
|
|
6624
|
+
summary: "Read one session's goal and its recent history, without starting anything; check output on request."
|
|
6625
|
+
},
|
|
6626
|
+
"session.goal.pause": {
|
|
6627
|
+
version: 1,
|
|
6628
|
+
mutation: true,
|
|
6629
|
+
effects: ["metadata", "provider-session", "filesystem", "process"],
|
|
6630
|
+
observation: "receipt",
|
|
6631
|
+
milestone: "D",
|
|
6632
|
+
summary: "Pause a running goal; no further turn starts until it is resumed."
|
|
6633
|
+
},
|
|
6634
|
+
"session.goal.resume": {
|
|
6635
|
+
version: 1,
|
|
6636
|
+
mutation: true,
|
|
6637
|
+
effects: ["provider-session", "filesystem", "process"],
|
|
6638
|
+
observation: "receipt",
|
|
6639
|
+
milestone: "D",
|
|
6640
|
+
summary: "Resume a stopped goal, optionally with a higher iteration cap or token budget."
|
|
6641
|
+
},
|
|
6642
|
+
"session.goal.clear": {
|
|
6643
|
+
version: 1,
|
|
6644
|
+
mutation: true,
|
|
6645
|
+
effects: ["metadata", "provider-session", "filesystem", "process"],
|
|
6646
|
+
observation: "receipt",
|
|
6647
|
+
milestone: "D",
|
|
6648
|
+
summary: "Remove a session's goal in any status; it stays readable in the goal history."
|
|
6649
|
+
},
|
|
6568
6650
|
"session.transcript": {
|
|
6569
6651
|
version: 1,
|
|
6570
6652
|
mutation: false,
|
|
@@ -6733,6 +6815,158 @@ var PUBLIC_ACTIONS = {
|
|
|
6733
6815
|
milestone: "D",
|
|
6734
6816
|
summary: "Start the Resolve Conflicts agent session in an environment (the Resolve tab): merge the target branch, resolve and validate conflicts, commit and push."
|
|
6735
6817
|
},
|
|
6818
|
+
"pr.get": {
|
|
6819
|
+
version: 1,
|
|
6820
|
+
mutation: false,
|
|
6821
|
+
effects: [],
|
|
6822
|
+
observation: "immediate",
|
|
6823
|
+
milestone: "D",
|
|
6824
|
+
summary: "Read the pull request attached to an environment's branch: attachment, URL, state, draft, base and head branches, head SHA, mergeability, checks, reviewers, labels and conversation comments."
|
|
6825
|
+
},
|
|
6826
|
+
"pr.list": {
|
|
6827
|
+
version: 1,
|
|
6828
|
+
mutation: false,
|
|
6829
|
+
effects: [],
|
|
6830
|
+
observation: "immediate",
|
|
6831
|
+
milestone: "D",
|
|
6832
|
+
summary: "List a project's pull requests from GitHub, by filter and search text."
|
|
6833
|
+
},
|
|
6834
|
+
"pr.timeline": {
|
|
6835
|
+
version: 1,
|
|
6836
|
+
mutation: false,
|
|
6837
|
+
effects: [],
|
|
6838
|
+
observation: "immediate",
|
|
6839
|
+
milestone: "D",
|
|
6840
|
+
summary: "Read the timeline of an environment's pull request: commits, comments, reviews and events, oldest first."
|
|
6841
|
+
},
|
|
6842
|
+
"pr.files": {
|
|
6843
|
+
version: 1,
|
|
6844
|
+
mutation: false,
|
|
6845
|
+
effects: [],
|
|
6846
|
+
observation: "immediate",
|
|
6847
|
+
milestone: "D",
|
|
6848
|
+
summary: "Read the changed files and review threads of an environment's pull request; with path, that file's diff hunks."
|
|
6849
|
+
},
|
|
6850
|
+
"pr.options": {
|
|
6851
|
+
version: 1,
|
|
6852
|
+
mutation: false,
|
|
6853
|
+
effects: [],
|
|
6854
|
+
observation: "immediate",
|
|
6855
|
+
milestone: "D",
|
|
6856
|
+
summary: "Read the labels and assignable users of the repository of an environment's pull request, for pr.label and pr.reviewers."
|
|
6857
|
+
},
|
|
6858
|
+
"pr.merge": {
|
|
6859
|
+
version: 1,
|
|
6860
|
+
mutation: true,
|
|
6861
|
+
effects: ["external-repository", "filesystem", "process"],
|
|
6862
|
+
observation: "receipt",
|
|
6863
|
+
milestone: "D",
|
|
6864
|
+
summary: "Merge an environment's pull request into its target branch (the Merge button, squash by default): a draft is marked ready first and the head branch is deleted unless deleteBranch is false. With cleanup, the Merge and Cleanup button: also delete the environment and its worktree once the merge is confirmed."
|
|
6865
|
+
},
|
|
6866
|
+
"pr.close": {
|
|
6867
|
+
version: 1,
|
|
6868
|
+
mutation: true,
|
|
6869
|
+
effects: ["external-repository"],
|
|
6870
|
+
observation: "receipt",
|
|
6871
|
+
milestone: "D",
|
|
6872
|
+
summary: "Close an environment's pull request without merging; pr.reopen undoes it."
|
|
6873
|
+
},
|
|
6874
|
+
"pr.reopen": {
|
|
6875
|
+
version: 1,
|
|
6876
|
+
mutation: true,
|
|
6877
|
+
effects: ["external-repository"],
|
|
6878
|
+
observation: "receipt",
|
|
6879
|
+
milestone: "D",
|
|
6880
|
+
summary: "Reopen an environment's closed pull request."
|
|
6881
|
+
},
|
|
6882
|
+
"pr.ready": {
|
|
6883
|
+
version: 1,
|
|
6884
|
+
mutation: true,
|
|
6885
|
+
effects: ["external-repository"],
|
|
6886
|
+
observation: "receipt",
|
|
6887
|
+
milestone: "D",
|
|
6888
|
+
summary: "Mark an environment's draft pull request ready for review."
|
|
6889
|
+
},
|
|
6890
|
+
"pr.draft": {
|
|
6891
|
+
version: 1,
|
|
6892
|
+
mutation: true,
|
|
6893
|
+
effects: ["external-repository"],
|
|
6894
|
+
observation: "receipt",
|
|
6895
|
+
milestone: "D",
|
|
6896
|
+
summary: "Convert an environment's pull request back to a draft."
|
|
6897
|
+
},
|
|
6898
|
+
"pr.comment": {
|
|
6899
|
+
version: 1,
|
|
6900
|
+
mutation: true,
|
|
6901
|
+
effects: ["external-repository"],
|
|
6902
|
+
observation: "receipt",
|
|
6903
|
+
milestone: "D",
|
|
6904
|
+
summary: "Add a conversation comment to an environment's pull request."
|
|
6905
|
+
},
|
|
6906
|
+
"pr.comment.edit": {
|
|
6907
|
+
version: 1,
|
|
6908
|
+
mutation: true,
|
|
6909
|
+
effects: ["external-repository"],
|
|
6910
|
+
observation: "receipt",
|
|
6911
|
+
milestone: "D",
|
|
6912
|
+
summary: "Edit a conversation comment on an environment's pull request; the GitHub user must be allowed to edit it."
|
|
6913
|
+
},
|
|
6914
|
+
"pr.review": {
|
|
6915
|
+
version: 1,
|
|
6916
|
+
mutation: true,
|
|
6917
|
+
effects: ["external-repository"],
|
|
6918
|
+
observation: "receipt",
|
|
6919
|
+
milestone: "D",
|
|
6920
|
+
summary: "Submit a review of an environment's pull request: approve, request changes or comment, with optional inline comments."
|
|
6921
|
+
},
|
|
6922
|
+
"pr.review.comment": {
|
|
6923
|
+
version: 1,
|
|
6924
|
+
mutation: true,
|
|
6925
|
+
effects: ["external-repository"],
|
|
6926
|
+
observation: "receipt",
|
|
6927
|
+
milestone: "D",
|
|
6928
|
+
summary: "Add one inline review comment to a line of an environment's pull request."
|
|
6929
|
+
},
|
|
6930
|
+
"pr.thread.reply": {
|
|
6931
|
+
version: 1,
|
|
6932
|
+
mutation: true,
|
|
6933
|
+
effects: ["external-repository"],
|
|
6934
|
+
observation: "receipt",
|
|
6935
|
+
milestone: "D",
|
|
6936
|
+
summary: "Reply to a review thread on an environment's pull request (threadId from pr.files)."
|
|
6937
|
+
},
|
|
6938
|
+
"pr.thread.resolve": {
|
|
6939
|
+
version: 1,
|
|
6940
|
+
mutation: true,
|
|
6941
|
+
effects: ["external-repository"],
|
|
6942
|
+
observation: "receipt",
|
|
6943
|
+
milestone: "D",
|
|
6944
|
+
summary: "Resolve or unresolve a review thread on an environment's pull request (threadId from pr.files)."
|
|
6945
|
+
},
|
|
6946
|
+
"pr.file.viewed": {
|
|
6947
|
+
version: 1,
|
|
6948
|
+
mutation: true,
|
|
6949
|
+
effects: ["external-repository"],
|
|
6950
|
+
observation: "receipt",
|
|
6951
|
+
milestone: "D",
|
|
6952
|
+
summary: "Mark a changed file of an environment's pull request as viewed or not viewed for the GitHub user."
|
|
6953
|
+
},
|
|
6954
|
+
"pr.reviewers": {
|
|
6955
|
+
version: 1,
|
|
6956
|
+
mutation: true,
|
|
6957
|
+
effects: ["external-repository"],
|
|
6958
|
+
observation: "receipt",
|
|
6959
|
+
milestone: "D",
|
|
6960
|
+
summary: "Request or remove reviewers on an environment's pull request."
|
|
6961
|
+
},
|
|
6962
|
+
"pr.label": {
|
|
6963
|
+
version: 1,
|
|
6964
|
+
mutation: true,
|
|
6965
|
+
effects: ["external-repository"],
|
|
6966
|
+
observation: "receipt",
|
|
6967
|
+
milestone: "D",
|
|
6968
|
+
summary: "Add or remove one label on an environment's pull request."
|
|
6969
|
+
},
|
|
6736
6970
|
"attention.list": {
|
|
6737
6971
|
version: 1,
|
|
6738
6972
|
mutation: false,
|
|
@@ -7003,7 +7237,12 @@ var PUBLIC_EXIT = Object.freeze({
|
|
|
7003
7237
|
deadline: 5,
|
|
7004
7238
|
interactionRequired: 6,
|
|
7005
7239
|
unknownDispatch: 7,
|
|
7006
|
-
conflict: 8
|
|
7240
|
+
conflict: 8,
|
|
7241
|
+
goalClaimed: 10,
|
|
7242
|
+
goalBlocked: 11,
|
|
7243
|
+
goalLimited: 12,
|
|
7244
|
+
goalPaused: 13,
|
|
7245
|
+
goalGone: 14
|
|
7007
7246
|
});
|
|
7008
7247
|
var PUBLIC_SIGNAL_EXIT = Object.freeze({ SIGINT: 130, SIGTERM: 143 });
|
|
7009
7248
|
var PUBLIC_ERROR_EXIT = {
|
|
@@ -7055,7 +7294,12 @@ var PUBLIC_ERROR_EXIT = {
|
|
|
7055
7294
|
"session-idle": PUBLIC_EXIT.conflict,
|
|
7056
7295
|
"interaction-stale": PUBLIC_EXIT.conflict,
|
|
7057
7296
|
"dispatch-parked": PUBLIC_EXIT.conflict,
|
|
7058
|
-
"node-unsupported": PUBLIC_EXIT.conflict
|
|
7297
|
+
"node-unsupported": PUBLIC_EXIT.conflict,
|
|
7298
|
+
"goal-claimed": PUBLIC_EXIT.goalClaimed,
|
|
7299
|
+
"goal-blocked": PUBLIC_EXIT.goalBlocked,
|
|
7300
|
+
"goal-limited": PUBLIC_EXIT.goalLimited,
|
|
7301
|
+
"goal-paused": PUBLIC_EXIT.goalPaused,
|
|
7302
|
+
"goal-gone": PUBLIC_EXIT.goalGone
|
|
7059
7303
|
};
|
|
7060
7304
|
function isPublicErrorCode(value) {
|
|
7061
7305
|
return typeof value === "string" && Object.hasOwn(PUBLIC_ERROR_EXIT, value);
|
|
@@ -7550,7 +7794,7 @@ function splitFlag(token) {
|
|
|
7550
7794
|
}
|
|
7551
7795
|
|
|
7552
7796
|
// src/client/fix.ts
|
|
7553
|
-
import { existsSync as existsSync3, realpathSync as
|
|
7797
|
+
import { existsSync as existsSync3, realpathSync as realpathSync3 } from "fs";
|
|
7554
7798
|
import { mkdtemp, readdir as readdir2, rmdir } from "fs/promises";
|
|
7555
7799
|
import os4 from "os";
|
|
7556
7800
|
import path6 from "path";
|
|
@@ -7562,16 +7806,19 @@ var COORDINATOR_CONTROL_TOOL_NAMES = Object.freeze([
|
|
|
7562
7806
|
"add_ticket_comment",
|
|
7563
7807
|
"address_multi_review",
|
|
7564
7808
|
"adopt_workflow",
|
|
7809
|
+
"answer_worker_question",
|
|
7565
7810
|
"cancel_build_pipeline",
|
|
7566
7811
|
"cancel_graph_run",
|
|
7567
7812
|
"cancel_multi_review",
|
|
7568
7813
|
"cancel_schedule",
|
|
7814
|
+
"clear_worker_goal",
|
|
7569
7815
|
"compare_project_files",
|
|
7570
7816
|
"create_pr",
|
|
7571
7817
|
"create_schedule",
|
|
7572
7818
|
"create_ticket",
|
|
7573
7819
|
"custom_fix_multi_review",
|
|
7574
7820
|
"edit_worker_tab",
|
|
7821
|
+
"fork_worker",
|
|
7575
7822
|
"get_build_pipeline",
|
|
7576
7823
|
"get_environment",
|
|
7577
7824
|
"get_feature_plan",
|
|
@@ -7584,6 +7831,9 @@ var COORDINATOR_CONTROL_TOOL_NAMES = Object.freeze([
|
|
|
7584
7831
|
"get_tab_state",
|
|
7585
7832
|
"get_tab_transcript",
|
|
7586
7833
|
"get_ticket",
|
|
7834
|
+
"get_usage_limits",
|
|
7835
|
+
"get_worker_changes",
|
|
7836
|
+
"get_worker_goal",
|
|
7587
7837
|
"launch_environment",
|
|
7588
7838
|
"launch_feature_plan",
|
|
7589
7839
|
"launch_graph",
|
|
@@ -7598,14 +7848,18 @@ var COORDINATOR_CONTROL_TOOL_NAMES = Object.freeze([
|
|
|
7598
7848
|
"list_node_environments",
|
|
7599
7849
|
"list_nodes",
|
|
7600
7850
|
"list_projects",
|
|
7851
|
+
"list_provider_accounts",
|
|
7601
7852
|
"list_schedules",
|
|
7602
7853
|
"list_tabs",
|
|
7603
7854
|
"list_tickets",
|
|
7855
|
+
"list_worker_commands",
|
|
7604
7856
|
"list_workflows",
|
|
7605
7857
|
"open_multi_review",
|
|
7606
7858
|
"open_multi_review_fix",
|
|
7607
7859
|
"open_worker_browser",
|
|
7608
7860
|
"pause_build_pipeline",
|
|
7861
|
+
"pause_schedule",
|
|
7862
|
+
"pause_worker_goal",
|
|
7609
7863
|
"propose_agents_md_update",
|
|
7610
7864
|
"read_messages",
|
|
7611
7865
|
"read_node_environment",
|
|
@@ -7614,18 +7868,25 @@ var COORDINATOR_CONTROL_TOOL_NAMES = Object.freeze([
|
|
|
7614
7868
|
"request_project_file_update",
|
|
7615
7869
|
"request_project_on_node",
|
|
7616
7870
|
"request_root_changes",
|
|
7871
|
+
"resolve_conflicts",
|
|
7617
7872
|
"resume_build_pipeline",
|
|
7873
|
+
"resume_schedule",
|
|
7874
|
+
"run_worker_command",
|
|
7618
7875
|
"send_message",
|
|
7619
7876
|
"send_node_message",
|
|
7620
7877
|
"send_prompt_to_node_tab",
|
|
7621
7878
|
"send_prompt_to_tab",
|
|
7879
|
+
"set_worker_goal",
|
|
7622
7880
|
"start_build_pipeline",
|
|
7623
7881
|
"start_environment",
|
|
7624
7882
|
"start_multi_review",
|
|
7625
7883
|
"start_node_environment",
|
|
7626
7884
|
"stop_environment",
|
|
7627
7885
|
"stop_node_environment",
|
|
7886
|
+
"stop_worker_turn",
|
|
7887
|
+
"switch_provider_account",
|
|
7628
7888
|
"transfer_files",
|
|
7889
|
+
"update_schedule",
|
|
7629
7890
|
"update_ticket"
|
|
7630
7891
|
]);
|
|
7631
7892
|
|
|
@@ -8066,12 +8327,13 @@ async function writePrivateFile(file, contents) {
|
|
|
8066
8327
|
}
|
|
8067
8328
|
|
|
8068
8329
|
// ../../apps/backend/src/data-dir.ts
|
|
8330
|
+
import { realpathSync as realpathSync2 } from "fs";
|
|
8069
8331
|
import os3 from "os";
|
|
8070
8332
|
import path5 from "path";
|
|
8071
8333
|
// ../../apps/backend/package.json
|
|
8072
8334
|
var package_default = {
|
|
8073
8335
|
name: "@orkestrator/backend",
|
|
8074
|
-
version: "2.34.
|
|
8336
|
+
version: "2.34.5-beta",
|
|
8075
8337
|
private: true,
|
|
8076
8338
|
type: "module",
|
|
8077
8339
|
exports: {
|
|
@@ -8091,7 +8353,7 @@ var package_default = {
|
|
|
8091
8353
|
dependencies: {
|
|
8092
8354
|
"@modelcontextprotocol/node": "2.1.1",
|
|
8093
8355
|
"@modelcontextprotocol/server": "2.3.1",
|
|
8094
|
-
"@opencode-ai/sdk": "1.19.
|
|
8356
|
+
"@opencode-ai/sdk": "1.19.5",
|
|
8095
8357
|
"@orkestrator/protocol": "workspace:*",
|
|
8096
8358
|
"@orkestrator/toolchain": "workspace:*",
|
|
8097
8359
|
"@xterm/addon-serialize": "0.14.0",
|
|
@@ -8191,6 +8453,16 @@ function defaultDataDir(platform = process.platform, env = process.env, home = o
|
|
|
8191
8453
|
return path5.join(home, "Library", "Application Support", APP_SLUG);
|
|
8192
8454
|
return path5.join(env.XDG_CONFIG_HOME ?? path5.join(home, ".config"), APP_SLUG);
|
|
8193
8455
|
}
|
|
8456
|
+
function isDefaultDataDir(dataDir, platform = process.platform, env = process.env, home = os3.homedir()) {
|
|
8457
|
+
const canonical = (value) => {
|
|
8458
|
+
try {
|
|
8459
|
+
return realpathSync2(value);
|
|
8460
|
+
} catch {
|
|
8461
|
+
return path5.resolve(value);
|
|
8462
|
+
}
|
|
8463
|
+
};
|
|
8464
|
+
return canonical(dataDir) === canonical(defaultDataDir(platform, env, home));
|
|
8465
|
+
}
|
|
8194
8466
|
|
|
8195
8467
|
// src/client/fix.ts
|
|
8196
8468
|
var FIX_ISSUE_REPOSITORY = BUG_REPORT_ISSUE_REPOSITORY;
|
|
@@ -8292,10 +8564,10 @@ function resolveFixAgent(name, options2) {
|
|
|
8292
8564
|
if (onPath) {
|
|
8293
8565
|
let isManaged = false;
|
|
8294
8566
|
try {
|
|
8295
|
-
const root =
|
|
8296
|
-
const target =
|
|
8567
|
+
const root = realpathSync3(toolchainRootDir(options2.dataDir));
|
|
8568
|
+
const target = realpathSync3(onPath);
|
|
8297
8569
|
const relative = path6.relative(root, target);
|
|
8298
|
-
isManaged = !relative.startsWith(`..${path6.sep}`) && relative !== ".." && !path6.isAbsolute(relative) || existsSync3(managed) && target ===
|
|
8570
|
+
isManaged = !relative.startsWith(`..${path6.sep}`) && relative !== ".." && !path6.isAbsolute(relative) || existsSync3(managed) && target === realpathSync3(managed);
|
|
8299
8571
|
} catch {}
|
|
8300
8572
|
return { agent, executable: onPath, source: isManaged ? "managed" : "path" };
|
|
8301
8573
|
}
|
|
@@ -8305,13 +8577,13 @@ function resolveFixAgent(name, options2) {
|
|
|
8305
8577
|
}
|
|
8306
8578
|
async function diagnosticConnection(dataDir, env) {
|
|
8307
8579
|
try {
|
|
8308
|
-
const descriptor =
|
|
8580
|
+
const descriptor = realpathSync3(path6.join(dataDir, "backend-instance.json"));
|
|
8309
8581
|
const config = await new CliConfigStore(defaultCliConfigDir(env)).read();
|
|
8310
8582
|
for (const [name, connection] of Object.entries(config.connections)) {
|
|
8311
8583
|
if (connection.kind !== "descriptor")
|
|
8312
8584
|
continue;
|
|
8313
8585
|
try {
|
|
8314
|
-
if (
|
|
8586
|
+
if (realpathSync3(connection.descriptorPath) === descriptor)
|
|
8315
8587
|
return name;
|
|
8316
8588
|
} catch {}
|
|
8317
8589
|
}
|
|
@@ -8538,6 +8810,63 @@ async function observe(check, options2) {
|
|
|
8538
8810
|
}
|
|
8539
8811
|
}
|
|
8540
8812
|
|
|
8813
|
+
// ../protocol/src/native-agent-goal.ts
|
|
8814
|
+
init_agent_platforms();
|
|
8815
|
+
function goalIsRunning(status) {
|
|
8816
|
+
return status === "active" || status === "checking" || status === "verifying";
|
|
8817
|
+
}
|
|
8818
|
+
var NATIVE_AGENT_GOAL_LIMITS = Object.freeze({
|
|
8819
|
+
objectiveMaxChars: 4000,
|
|
8820
|
+
checkMaxBytes: 4 * 1024,
|
|
8821
|
+
reportMessageMaxBytes: 2 * 1024,
|
|
8822
|
+
checkOutputMaxBytes: 8 * 1024,
|
|
8823
|
+
statusDetailMaxBytes: 1024,
|
|
8824
|
+
maxIterationsDefault: 20,
|
|
8825
|
+
maxIterationsLimit: 100,
|
|
8826
|
+
noProgressLimit: 3,
|
|
8827
|
+
checkTimeoutDefaultMs: 10 * 60000,
|
|
8828
|
+
checkTimeoutMaxMs: 30 * 60000,
|
|
8829
|
+
verifierModelMaxChars: 200,
|
|
8830
|
+
verifierTimeoutDefaultMs: 30 * 60000,
|
|
8831
|
+
verdictSummaryMaxBytes: 2 * 1024,
|
|
8832
|
+
verdictReasonsMax: 16,
|
|
8833
|
+
verdictReasonMaxBytes: 512,
|
|
8834
|
+
historyLimit: 5
|
|
8835
|
+
});
|
|
8836
|
+
var AGENT_GOAL_TOOL_NAMES = Object.freeze(["report_goal"]);
|
|
8837
|
+
var GOAL_QUALIFIED_PLATFORMS = Object.freeze([
|
|
8838
|
+
"claude",
|
|
8839
|
+
"codex",
|
|
8840
|
+
"opencode",
|
|
8841
|
+
"cursor",
|
|
8842
|
+
"grok",
|
|
8843
|
+
"pi"
|
|
8844
|
+
]);
|
|
8845
|
+
var GOAL_TOKEN_BUDGET_PLATFORMS = Object.freeze([
|
|
8846
|
+
"claude",
|
|
8847
|
+
"codex",
|
|
8848
|
+
"opencode",
|
|
8849
|
+
"cursor",
|
|
8850
|
+
"grok",
|
|
8851
|
+
"pi"
|
|
8852
|
+
]);
|
|
8853
|
+
function parseVerifierFlag(text) {
|
|
8854
|
+
const colon = text.indexOf(":");
|
|
8855
|
+
const agent = colon < 0 ? text : text.slice(0, colon);
|
|
8856
|
+
const model = colon < 0 ? undefined : text.slice(colon + 1);
|
|
8857
|
+
if (!isAgentPlatform(agent)) {
|
|
8858
|
+
return { error: "--verifier takes an agent, optionally with a model: --verifier codex:o3." };
|
|
8859
|
+
}
|
|
8860
|
+
if (model === undefined)
|
|
8861
|
+
return { agent };
|
|
8862
|
+
if (!model || model.length > NATIVE_AGENT_GOAL_LIMITS.verifierModelMaxChars) {
|
|
8863
|
+
return {
|
|
8864
|
+
error: `The verifier's model name must be 1 to ${NATIVE_AGENT_GOAL_LIMITS.verifierModelMaxChars} characters.`
|
|
8865
|
+
};
|
|
8866
|
+
}
|
|
8867
|
+
return { agent, model };
|
|
8868
|
+
}
|
|
8869
|
+
|
|
8541
8870
|
// ../protocol/src/public-api-wait.ts
|
|
8542
8871
|
function isTerminalPublicReceipt(receipt) {
|
|
8543
8872
|
return !["admitted", "running", "unknown"].includes(receipt.state);
|
|
@@ -8592,6 +8921,34 @@ function terminalReceiptErrorCode(receipt) {
|
|
|
8592
8921
|
return "run-unknown";
|
|
8593
8922
|
return receipt.error?.code ?? "operation-failed";
|
|
8594
8923
|
}
|
|
8924
|
+
var GOAL_END_CODES = {
|
|
8925
|
+
claimed: "goal-claimed",
|
|
8926
|
+
blocked: "goal-blocked",
|
|
8927
|
+
limited: "goal-limited",
|
|
8928
|
+
paused: "goal-paused"
|
|
8929
|
+
};
|
|
8930
|
+
function classifyGoalForWait(read, goalId, options2 = {}) {
|
|
8931
|
+
const current = read.goal?.goalId === goalId ? read.goal : null;
|
|
8932
|
+
if (!current) {
|
|
8933
|
+
const past = read.history.findLast((goal) => goal.goalId === goalId);
|
|
8934
|
+
if (past?.status === "complete")
|
|
8935
|
+
return { kind: "complete", goal: past };
|
|
8936
|
+
if (past?.status === "claimed")
|
|
8937
|
+
return { kind: "stopped", code: "goal-claimed", goal: past };
|
|
8938
|
+
return past ? { kind: "gone", goal: past } : { kind: "gone" };
|
|
8939
|
+
}
|
|
8940
|
+
if (goalIsRunning(current.status)) {
|
|
8941
|
+
const needsAnswer = read.activity === "waiting" || (read.pendingInteractionCount ?? 0) > 0;
|
|
8942
|
+
return needsAnswer && !options2.continueOnInteraction ? { kind: "interaction", goal: current } : { kind: "pending", goal: current };
|
|
8943
|
+
}
|
|
8944
|
+
if (current.status === "complete")
|
|
8945
|
+
return { kind: "complete", goal: current };
|
|
8946
|
+
return {
|
|
8947
|
+
kind: "stopped",
|
|
8948
|
+
code: GOAL_END_CODES[current.status],
|
|
8949
|
+
goal: current
|
|
8950
|
+
};
|
|
8951
|
+
}
|
|
8595
8952
|
|
|
8596
8953
|
// src/client/commands/common.ts
|
|
8597
8954
|
var REQUEST_ID_OPTION = {
|
|
@@ -9862,6 +10219,7 @@ var PROJECTION_FIELD_SET = new Set([
|
|
|
9862
10219
|
"queue",
|
|
9863
10220
|
"asyncQuestionResponses",
|
|
9864
10221
|
"contextUsage",
|
|
10222
|
+
"goal",
|
|
9865
10223
|
"rateLimits",
|
|
9866
10224
|
"runtime",
|
|
9867
10225
|
"runtimeHealthAuthoritative",
|
|
@@ -9886,6 +10244,7 @@ var OPTIONAL_PROJECTION_FIELD_SET = new Set([
|
|
|
9886
10244
|
"queue",
|
|
9887
10245
|
"asyncQuestionResponses",
|
|
9888
10246
|
"contextUsage",
|
|
10247
|
+
"goal",
|
|
9889
10248
|
"rateLimits",
|
|
9890
10249
|
"runtime",
|
|
9891
10250
|
"runtimeHealthAuthoritative",
|
|
@@ -10079,6 +10438,11 @@ var VERIFICATION_VERDICT_SCHEMA = Object.freeze({
|
|
|
10079
10438
|
properties: VERIFICATION_VERDICT_PROPERTIES
|
|
10080
10439
|
});
|
|
10081
10440
|
var MAX_BUILD_PIPELINE_ITERATIONS = 10;
|
|
10441
|
+
var SETUP_BUILD_PHASES = new Set([
|
|
10442
|
+
"creating-environment",
|
|
10443
|
+
"starting-environment",
|
|
10444
|
+
"waiting-for-setup"
|
|
10445
|
+
]);
|
|
10082
10446
|
var BUILD_PHASES = new Set([
|
|
10083
10447
|
"creating-environment",
|
|
10084
10448
|
"starting-environment",
|
|
@@ -10114,6 +10478,15 @@ var FAILURE_KINDS = new Set([
|
|
|
10114
10478
|
var INTERACTION_PAYLOAD_ENCODER = new TextEncoder;
|
|
10115
10479
|
var INTERACTION_PROVIDERS = new Set(AGENT_INTERACTION_PROVIDERS);
|
|
10116
10480
|
var INTERACTION_KINDS = new Set(AGENT_INTERACTION_KINDS);
|
|
10481
|
+
var VALIDATION_RESULT_STATUSES = new Set([
|
|
10482
|
+
"pending",
|
|
10483
|
+
"queued",
|
|
10484
|
+
"running",
|
|
10485
|
+
"passed",
|
|
10486
|
+
"failed",
|
|
10487
|
+
"skipped",
|
|
10488
|
+
"incomplete"
|
|
10489
|
+
]);
|
|
10117
10490
|
|
|
10118
10491
|
// ../protocol/src/public-api-inputs-build.ts
|
|
10119
10492
|
init_agent_platforms();
|
|
@@ -10401,7 +10774,11 @@ function sessionLines(session) {
|
|
|
10401
10774
|
["activity", session.activity],
|
|
10402
10775
|
["latest request", session.latestRequestId],
|
|
10403
10776
|
["pending interactions", session.pendingInteractionCount],
|
|
10404
|
-
["recoverable dispatch", session.recoverableDispatch]
|
|
10777
|
+
["recoverable dispatch", session.recoverableDispatch],
|
|
10778
|
+
[
|
|
10779
|
+
"goal",
|
|
10780
|
+
session.goal ? `${session.goal.status} (${session.goal.iterations}/${session.goal.maxIterations})` : undefined
|
|
10781
|
+
]
|
|
10405
10782
|
]);
|
|
10406
10783
|
}
|
|
10407
10784
|
function receiptLines(receipt) {
|
|
@@ -15001,6 +15378,15 @@ function parseEndpoints(value) {
|
|
|
15001
15378
|
return { role: entry.role, url: entry.url };
|
|
15002
15379
|
});
|
|
15003
15380
|
}
|
|
15381
|
+
function parseControlMcp(value) {
|
|
15382
|
+
if (!isRecord3(value) || typeof value.running !== "boolean" || value.url !== undefined && !isHttpUrl(value.url)) {
|
|
15383
|
+
throw new Error("Backend instance descriptor field is invalid: controlMcp");
|
|
15384
|
+
}
|
|
15385
|
+
return {
|
|
15386
|
+
running: value.running,
|
|
15387
|
+
...value.url !== undefined ? { url: value.url } : {}
|
|
15388
|
+
};
|
|
15389
|
+
}
|
|
15004
15390
|
function parseBackendInstanceDescriptor(value) {
|
|
15005
15391
|
if (!isRecord3(value))
|
|
15006
15392
|
throw new Error("Backend instance descriptor must be an object");
|
|
@@ -15023,6 +15409,7 @@ function parseBackendInstanceDescriptor(value) {
|
|
|
15023
15409
|
throw new Error("Backend instance descriptor field is invalid: browserUrl");
|
|
15024
15410
|
}
|
|
15025
15411
|
const endpoints = value.endpoints === undefined ? undefined : parseEndpoints(value.endpoints);
|
|
15412
|
+
const controlMcp = value.controlMcp === undefined ? undefined : parseControlMcp(value.controlMcp);
|
|
15026
15413
|
if (!Number.isSafeInteger(value.publicApiSchemaVersion) || value.publicApiSchemaVersion < 1) {
|
|
15027
15414
|
throw new Error("Backend instance descriptor field is invalid: publicApiSchemaVersion");
|
|
15028
15415
|
}
|
|
@@ -15041,6 +15428,7 @@ function parseBackendInstanceDescriptor(value) {
|
|
|
15041
15428
|
url: value.url,
|
|
15042
15429
|
...value.browserUrl !== undefined ? { browserUrl: value.browserUrl } : {},
|
|
15043
15430
|
...endpoints !== undefined ? { endpoints } : {},
|
|
15431
|
+
...controlMcp !== undefined ? { controlMcp } : {},
|
|
15044
15432
|
authFile,
|
|
15045
15433
|
dataDir,
|
|
15046
15434
|
appVersion: text("appVersion", 64),
|
|
@@ -17120,7 +17508,11 @@ var SCHEDULED_JOB_LIMITS = Object.freeze({
|
|
|
17120
17508
|
busyRetryDelayMs: 30000,
|
|
17121
17509
|
fireStaleMs: 90000,
|
|
17122
17510
|
fireHeartbeatMs: 20000,
|
|
17123
|
-
maxFireAttempts: 3
|
|
17511
|
+
maxFireAttempts: 3,
|
|
17512
|
+
maxSelfTimerDelayMs: 7 * 24 * 60 * 60000,
|
|
17513
|
+
maxPendingSelfTimers: 20,
|
|
17514
|
+
selfTimerGrantWindowMs: 2 * 60 * 60000,
|
|
17515
|
+
userInstructionMaxChars: 500
|
|
17124
17516
|
});
|
|
17125
17517
|
function describeScheduleTrigger(trigger, timeZone) {
|
|
17126
17518
|
switch (trigger.kind) {
|
|
@@ -17701,12 +18093,19 @@ var mailCommands = [
|
|
|
17701
18093
|
];
|
|
17702
18094
|
|
|
17703
18095
|
// src/client/commands/service.ts
|
|
17704
|
-
import {
|
|
18096
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
18097
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
17705
18098
|
import { existsSync as existsSync6 } from "fs";
|
|
17706
|
-
import { mkdir as mkdir2, rm as rm2, writeFile as writeFile2 } from "fs/promises";
|
|
18099
|
+
import { mkdir as mkdir2, rm as rm2, stat as stat2, writeFile as writeFile2 } from "fs/promises";
|
|
18100
|
+
import { createServer } from "net";
|
|
17707
18101
|
import path16 from "path";
|
|
17708
18102
|
|
|
18103
|
+
// ../../apps/backend/src/fixed-ports.ts
|
|
18104
|
+
var DEFAULT_GATEWAY_PORT = 34121;
|
|
18105
|
+
var DEFAULT_CONTROL_MCP_PORT = 34122;
|
|
18106
|
+
|
|
17709
18107
|
// src/client/service-connection.ts
|
|
18108
|
+
import { open } from "fs/promises";
|
|
17710
18109
|
import path13 from "path";
|
|
17711
18110
|
async function currentDescriptorGeneration(dataDir) {
|
|
17712
18111
|
try {
|
|
@@ -17715,20 +18114,49 @@ async function currentDescriptorGeneration(dataDir) {
|
|
|
17715
18114
|
return null;
|
|
17716
18115
|
}
|
|
17717
18116
|
}
|
|
18117
|
+
var LOG_EXCERPT_LINES = 20;
|
|
18118
|
+
var LOG_EXCERPT_MAX_LINE = 500;
|
|
18119
|
+
var LOG_EXCERPT_MAX_BYTES = 256 * 1024;
|
|
18120
|
+
async function startupLogExcerpt(logFile, offset, version) {
|
|
18121
|
+
let text;
|
|
18122
|
+
try {
|
|
18123
|
+
const file = await open(logFile);
|
|
18124
|
+
try {
|
|
18125
|
+
const { size } = await file.stat();
|
|
18126
|
+
const start = Math.max(offset, size - LOG_EXCERPT_MAX_BYTES);
|
|
18127
|
+
const buffer = Buffer.alloc(Math.max(size - start, 0));
|
|
18128
|
+
await file.read(buffer, 0, buffer.length, start);
|
|
18129
|
+
text = buffer.toString("utf8");
|
|
18130
|
+
} finally {
|
|
18131
|
+
await file.close();
|
|
18132
|
+
}
|
|
18133
|
+
} catch {
|
|
18134
|
+
return [];
|
|
18135
|
+
}
|
|
18136
|
+
const lines = text.split(`
|
|
18137
|
+
`);
|
|
18138
|
+
const exit = lines.findLastIndex((line) => line.includes(` ${version} exited (`));
|
|
18139
|
+
const rollback = lines.findIndex((line) => line.includes(` ${version} did not start after `) && line.includes("; rolling back to "));
|
|
18140
|
+
const nextStart = lines.findIndex((line) => /^\[launcher\] \S+ starting /.test(line) && !line.endsWith(` starting ${version}`));
|
|
18141
|
+
const end = exit >= 0 ? exit : Math.min(...[rollback, nextStart, lines.length].filter((index) => index >= 0));
|
|
18142
|
+
return lines.slice(0, end).filter((line) => line.trim() !== "").slice(-LOG_EXCERPT_LINES).map((line) => line.slice(0, LOG_EXCERPT_MAX_LINE));
|
|
18143
|
+
}
|
|
17718
18144
|
async function awaitServiceConnection(dataDir, previousGeneration, options2) {
|
|
17719
18145
|
const file = path13.join(dataDir, BACKEND_INSTANCE_DESCRIPTOR_FILE);
|
|
17720
18146
|
try {
|
|
17721
18147
|
const descriptor = await observe(async () => {
|
|
17722
18148
|
try {
|
|
17723
18149
|
const value = await readDescriptor(file);
|
|
17724
|
-
const fresh = value.generation !== previousGeneration && path13.resolve(value.dataDir) === path13.resolve(dataDir) && processIsAlive(value.pid);
|
|
17725
|
-
|
|
17726
|
-
|
|
17727
|
-
|
|
17728
|
-
|
|
18150
|
+
const fresh = value.generation !== previousGeneration && path13.resolve(value.dataDir) === path13.resolve(dataDir) && (options2.expectedVersion === undefined || value.appVersion === options2.expectedVersion) && processIsAlive(value.pid);
|
|
18151
|
+
if (fresh)
|
|
18152
|
+
return { done: true, value };
|
|
18153
|
+
} catch {}
|
|
18154
|
+
await options2.assertStarting?.();
|
|
18155
|
+
return { done: false };
|
|
17729
18156
|
}, options2);
|
|
17730
18157
|
const connection = {
|
|
17731
18158
|
browserUrl: descriptor.browserUrl ?? (isLoopbackHost(new URL(descriptor.url).hostname) || descriptor.endpoints?.some((endpoint) => endpoint.url === descriptor.url) ? null : descriptor.url),
|
|
18159
|
+
clientUrls: (descriptor.endpoints ?? []).filter((endpoint) => endpoint.role === "client").map((endpoint) => endpoint.url),
|
|
17732
18160
|
authFile: descriptor.authFile,
|
|
17733
18161
|
token: null
|
|
17734
18162
|
};
|
|
@@ -17752,6 +18180,7 @@ async function awaitServiceConnection(dataDir, previousGeneration, options2) {
|
|
|
17752
18180
|
}
|
|
17753
18181
|
|
|
17754
18182
|
// src/client/service-setup.ts
|
|
18183
|
+
import { spawnSync } from "child_process";
|
|
17755
18184
|
import { existsSync as existsSync4 } from "fs";
|
|
17756
18185
|
import os6 from "os";
|
|
17757
18186
|
var HOSTED_WEB_ORIGINS = "https://orkestrator.dev,https://www.orkestrator.dev";
|
|
@@ -17764,7 +18193,24 @@ var systemSetupProbe = {
|
|
|
17764
18193
|
return MACOS_TAILSCALE_APP_CLI;
|
|
17765
18194
|
}
|
|
17766
18195
|
return typeof Bun === "undefined" ? null : Bun.which("tailscale");
|
|
17767
|
-
}
|
|
18196
|
+
},
|
|
18197
|
+
tailscaleOperatorNeeded: (cli) => {
|
|
18198
|
+
if (process.platform !== "linux")
|
|
18199
|
+
return null;
|
|
18200
|
+
const user = os6.userInfo();
|
|
18201
|
+
if (user.uid === 0)
|
|
18202
|
+
return null;
|
|
18203
|
+
const prefs = spawnSync(cli, ["debug", "prefs"], { encoding: "utf8", timeout: 1e4 });
|
|
18204
|
+
if (prefs.status !== 0)
|
|
18205
|
+
return null;
|
|
18206
|
+
try {
|
|
18207
|
+
const { OperatorUser } = JSON.parse(prefs.stdout);
|
|
18208
|
+
return OperatorUser === user.username ? null : user.username;
|
|
18209
|
+
} catch {
|
|
18210
|
+
return null;
|
|
18211
|
+
}
|
|
18212
|
+
},
|
|
18213
|
+
grantTailscaleOperator: (cli, user) => spawnSync("sudo", [cli, "set", `--operator=${user}`], { stdio: "inherit" }).status === 0
|
|
17768
18214
|
};
|
|
17769
18215
|
function isTailscaleIPv4(address) {
|
|
17770
18216
|
const [first, second] = address.split(".").map(Number);
|
|
@@ -17808,9 +18254,44 @@ async function confirm(io, prompt, fallback) {
|
|
|
17808
18254
|
`);
|
|
17809
18255
|
}
|
|
17810
18256
|
}
|
|
18257
|
+
async function askGatewayPort(io) {
|
|
18258
|
+
io.stderr(`
|
|
18259
|
+
This is not the default data directory, so ports ${DEFAULT_GATEWAY_PORT} and ${DEFAULT_CONTROL_MCP_PORT} stay with the main install. Without a port of its own, this backend takes a free one that changes on each start.
|
|
18260
|
+
`);
|
|
18261
|
+
for (;; ) {
|
|
18262
|
+
const answer = await ask(io, "Gateway port for a stable address (empty for a free one): ");
|
|
18263
|
+
if (answer === "")
|
|
18264
|
+
return null;
|
|
18265
|
+
const port = Number(answer);
|
|
18266
|
+
if (/^\d+$/.test(answer) && port >= 1 && port <= 65535 && port !== DEFAULT_GATEWAY_PORT && port !== DEFAULT_CONTROL_MCP_PORT) {
|
|
18267
|
+
return String(port);
|
|
18268
|
+
}
|
|
18269
|
+
io.stderr(`Enter a port from 1 to 65535, other than ${DEFAULT_GATEWAY_PORT} and ${DEFAULT_CONTROL_MCP_PORT}.
|
|
18270
|
+
`);
|
|
18271
|
+
}
|
|
18272
|
+
}
|
|
17811
18273
|
function quote(arg) {
|
|
17812
18274
|
return /^[\w@%+=:,./-]+$/.test(arg) ? arg : `'${arg.replaceAll("'", `'\\''`)}'`;
|
|
17813
18275
|
}
|
|
18276
|
+
function operatorMessage(cli, user) {
|
|
18277
|
+
return [
|
|
18278
|
+
`
|
|
18279
|
+
Tailscale only lets root or its operator configure Serve, and ${user} is neither.`,
|
|
18280
|
+
"The backend cannot start with --tailscale-serve until this has been run once:",
|
|
18281
|
+
` sudo ${quote(cli)} set --operator=${quote(user)}
|
|
18282
|
+
`
|
|
18283
|
+
].join(`
|
|
18284
|
+
`);
|
|
18285
|
+
}
|
|
18286
|
+
function warnIfTailscaleOperatorNeeded(io, serveArgs, probe = systemSetupProbe) {
|
|
18287
|
+
if (!serveArgs.includes("--tailscale-serve"))
|
|
18288
|
+
return;
|
|
18289
|
+
const binFlag = serveArgs.indexOf("--tailscale-bin");
|
|
18290
|
+
const cli = (binFlag >= 0 ? serveArgs[binFlag + 1] : undefined) ?? io.env.ORKESTRATOR_TAILSCALE_BIN ?? probe.findTailscaleCli();
|
|
18291
|
+
const user = cli ? probe.tailscaleOperatorNeeded(cli) : null;
|
|
18292
|
+
if (cli && user)
|
|
18293
|
+
io.stderr(operatorMessage(cli, user));
|
|
18294
|
+
}
|
|
17814
18295
|
async function askServeArguments(io, options2) {
|
|
17815
18296
|
const source = io;
|
|
17816
18297
|
io = {
|
|
@@ -17843,16 +18324,30 @@ async function askServeArguments(io, options2) {
|
|
|
17843
18324
|
This machine has no Tailscale address yet. Connect Tailscale before the service starts, or it will not serve.
|
|
17844
18325
|
`);
|
|
17845
18326
|
}
|
|
18327
|
+
if (access === "tailnet" && options2.secondBackend) {
|
|
18328
|
+
const port = await askGatewayPort(io);
|
|
18329
|
+
if (port)
|
|
18330
|
+
args.push("--port", port);
|
|
18331
|
+
}
|
|
17846
18332
|
if (access === "serve") {
|
|
17847
18333
|
args.push("--tailscale-serve");
|
|
17848
|
-
|
|
18334
|
+
let cli = probe.findTailscaleCli();
|
|
18335
|
+
if (!cli) {
|
|
17849
18336
|
io.stderr(`
|
|
17850
18337
|
The Tailscale CLI was not found, and Tailscale Serve needs it.
|
|
17851
18338
|
`);
|
|
17852
|
-
|
|
18339
|
+
cli = await ask(io, "Path to the tailscale executable (empty to keep `tailscale`): ");
|
|
17853
18340
|
if (cli !== "")
|
|
17854
18341
|
args.push("--tailscale-bin", cli);
|
|
17855
18342
|
}
|
|
18343
|
+
const operator = cli ? probe.tailscaleOperatorNeeded(cli) : null;
|
|
18344
|
+
if (cli && operator) {
|
|
18345
|
+
io.stderr(operatorMessage(cli, operator));
|
|
18346
|
+
const granted = !options2.dryRun && await confirm(io, "Run it now with sudo?", true) && probe.grantTailscaleOperator(cli, operator);
|
|
18347
|
+
if (!granted && !options2.dryRun)
|
|
18348
|
+
io.stderr(`Continuing without it.
|
|
18349
|
+
`);
|
|
18350
|
+
}
|
|
17856
18351
|
if (await confirm(io, `
|
|
17857
18352
|
Allow the hosted web client at orkestrator.dev?`, true)) {
|
|
17858
18353
|
args.push("--allowed-origins", HOSTED_WEB_ORIGINS);
|
|
@@ -17934,6 +18429,8 @@ ${args}
|
|
|
17934
18429
|
}
|
|
17935
18430
|
const name = `orkestrator-backend${tag}.service`;
|
|
17936
18431
|
const configHome = options2.env.XDG_CONFIG_HOME ?? path14.join(home, ".config");
|
|
18432
|
+
const xdgEnvironment = options2.env.XDG_CONFIG_HOME === undefined ? "" : `Environment=${systemdWord(`XDG_CONFIG_HOME=${options2.env.XDG_CONFIG_HOME}`)}
|
|
18433
|
+
`;
|
|
17937
18434
|
return {
|
|
17938
18435
|
manager: "systemd",
|
|
17939
18436
|
name,
|
|
@@ -17947,7 +18444,7 @@ Type=simple
|
|
|
17947
18444
|
ExecStart=${[options2.bun, options2.launcher, options2.dataDir].map(systemdWord).join(" ")}
|
|
17948
18445
|
Environment=${systemdWord(`PATH=${pathValue}`)}
|
|
17949
18446
|
Environment=NODE_ENV=production
|
|
17950
|
-
Restart=always
|
|
18447
|
+
${xdgEnvironment}Restart=always
|
|
17951
18448
|
RestartSec=10
|
|
17952
18449
|
StandardOutput=append:${options2.logFile}
|
|
17953
18450
|
StandardError=append:${options2.logFile}
|
|
@@ -18032,8 +18529,43 @@ function unitFor(context, dataDir, bun) {
|
|
|
18032
18529
|
};
|
|
18033
18530
|
});
|
|
18034
18531
|
}
|
|
18532
|
+
async function startupFailedError(version, failure, logFile, logOffset) {
|
|
18533
|
+
const excerpt = await startupLogExcerpt(logFile, logOffset, version);
|
|
18534
|
+
return new CliError("operation-failed", [
|
|
18535
|
+
failure.restoredVersion ? `orkestrator@${version} did not start, so the service went back to ${failure.restoredVersion}.` : `orkestrator@${version} did not start, and the service keeps retrying it.`,
|
|
18536
|
+
...excerpt.length > 0 ? ["", ...excerpt, ""] : [],
|
|
18537
|
+
`Fix the cause, then run this command again. Full log: ${logFile}`
|
|
18538
|
+
].join(`
|
|
18539
|
+
`), { retryable: true });
|
|
18540
|
+
}
|
|
18541
|
+
async function freeServicePort(allocate = probeServicePort) {
|
|
18542
|
+
for (let attempt = 0;attempt < 32; attempt += 1) {
|
|
18543
|
+
const port = await allocate();
|
|
18544
|
+
if (validServicePort(String(port)))
|
|
18545
|
+
return String(port);
|
|
18546
|
+
}
|
|
18547
|
+
throw new Error("Unable to allocate an unreserved service port");
|
|
18548
|
+
}
|
|
18549
|
+
function validServicePort(value) {
|
|
18550
|
+
const port = Number(value);
|
|
18551
|
+
return value !== undefined && String(port) === value && Number.isInteger(port) && port > 0 && port <= 65535 && port !== DEFAULT_GATEWAY_PORT && port !== DEFAULT_CONTROL_MCP_PORT ? value : undefined;
|
|
18552
|
+
}
|
|
18553
|
+
async function probeServicePort() {
|
|
18554
|
+
return new Promise((resolve, reject) => {
|
|
18555
|
+
const probe = createServer();
|
|
18556
|
+
probe.once("error", reject);
|
|
18557
|
+
probe.listen(0, "127.0.0.1", () => {
|
|
18558
|
+
const address = probe.address();
|
|
18559
|
+
probe.close(() => resolve(typeof address === "object" && address ? address.port : 0));
|
|
18560
|
+
});
|
|
18561
|
+
});
|
|
18562
|
+
}
|
|
18563
|
+
function portArgument(args) {
|
|
18564
|
+
const index = args?.indexOf("--port") ?? -1;
|
|
18565
|
+
return index >= 0 ? args?.[index + 1] : undefined;
|
|
18566
|
+
}
|
|
18035
18567
|
function runManager(argv, allowFailure = false) {
|
|
18036
|
-
const result =
|
|
18568
|
+
const result = spawnSync2(argv[0], argv.slice(1), { encoding: "utf8", timeout: 60000 });
|
|
18037
18569
|
const ok = result.status === 0;
|
|
18038
18570
|
if (!ok && !allowFailure) {
|
|
18039
18571
|
throw new CliError("operation-failed", `${argv.join(" ")} failed: ${(result.stderr || result.stdout || "").trim().slice(0, 300)}`);
|
|
@@ -18055,10 +18587,19 @@ var serviceCommands = [
|
|
|
18055
18587
|
"With none, in a terminal, it asks how clients will connect (Tailscale Serve, the",
|
|
18056
18588
|
"tailnet address or SSH) and prints the equivalent command; --yes skips the questions.",
|
|
18057
18589
|
"The Bun running this command is the Bun the service uses; reinstall to change it.",
|
|
18590
|
+
"",
|
|
18591
|
+
"Only the default data directory's backend uses the fixed gateway port. With another",
|
|
18592
|
+
"--data-dir and no `--port`, the backend takes a free port that changes on each start,",
|
|
18593
|
+
"so pass `-- --port PORT` for a stable address. --tailscale-serve gets a free port",
|
|
18594
|
+
"chosen once, here, with strict binding so it cannot move to a fallback port.",
|
|
18058
18595
|
"On Linux, run `loginctl enable-linger $USER` so the service runs without a login.",
|
|
18059
18596
|
"",
|
|
18060
18597
|
"Once the service is running, the address it serves and its gateway token are printed.",
|
|
18061
|
-
"The token is shown only in this terminal: never in --output json or the service log."
|
|
18598
|
+
"The token is shown only in this terminal: never in --output json or the service log.",
|
|
18599
|
+
"If the release cannot start, the command fails with what the service logged.",
|
|
18600
|
+
"",
|
|
18601
|
+
"On Linux, --tailscale-serve needs `sudo tailscale set --operator=$USER` once; the",
|
|
18602
|
+
"questions offer to run it, and a scripted install warns when it is missing."
|
|
18062
18603
|
].join(`
|
|
18063
18604
|
`),
|
|
18064
18605
|
positionals: [],
|
|
@@ -18115,13 +18656,30 @@ var serviceCommands = [
|
|
|
18115
18656
|
if (readyWaitMs > PUBLIC_API_LIMITS.waitMaxMs) {
|
|
18116
18657
|
throw new CliError("invalid-input", "--timeout may be at most 24h");
|
|
18117
18658
|
}
|
|
18118
|
-
const
|
|
18659
|
+
const secondBackend = !isDefaultDataDir(dataDir, process.platform, context.io.env);
|
|
18660
|
+
const requestedArgs = interactive ? await askServeArguments(context.io, {
|
|
18119
18661
|
installCommand: installCommandLine(parsed),
|
|
18120
|
-
signal: context.signal
|
|
18662
|
+
signal: context.signal,
|
|
18663
|
+
dryRun: parsed.options["dry-run"] === true,
|
|
18664
|
+
secondBackend
|
|
18121
18665
|
}) : parsed.rest;
|
|
18666
|
+
if (!interactive)
|
|
18667
|
+
warnIfTailscaleOperatorNeeded(context.io, requestedArgs);
|
|
18122
18668
|
const bun = process.execPath;
|
|
18123
18669
|
const bunVersion = typeof Bun === "undefined" ? "" : Bun.version;
|
|
18124
18670
|
const { service, layout, unit } = await unitFor(context, dataDir, bun);
|
|
18671
|
+
const pinsPort = secondBackend && requestedArgs.includes("--tailscale-serve") && !requestedArgs.includes("--port");
|
|
18672
|
+
const serveArgs = pinsPort && parsed.options["dry-run"] !== true ? [
|
|
18673
|
+
...requestedArgs,
|
|
18674
|
+
"--port",
|
|
18675
|
+
validServicePort(portArgument(service.readServiceConfig(layout)?.args)) ?? await freeServicePort(),
|
|
18676
|
+
"--strict-gateway-port"
|
|
18677
|
+
] : requestedArgs;
|
|
18678
|
+
const portNote = pinsPort ? [
|
|
18679
|
+
"This is not the default data directory, so its gateway keeps a free port chosen at install instead of the fixed one."
|
|
18680
|
+
] : secondBackend && !requestedArgs.includes("--port") ? [
|
|
18681
|
+
"This is not the default data directory, so its gateway takes a free port that changes on each start. For a stable address, reinstall with `-- --port PORT`."
|
|
18682
|
+
] : [];
|
|
18125
18683
|
const summary = {
|
|
18126
18684
|
dataDir,
|
|
18127
18685
|
version,
|
|
@@ -18143,6 +18701,7 @@ var serviceCommands = [
|
|
|
18143
18701
|
["unit", `${unit.manager} ${unit.name} \u2192 ${unit.file}`],
|
|
18144
18702
|
["serve arguments", serveArgs.join(" ") || "(none)"]
|
|
18145
18703
|
]),
|
|
18704
|
+
...portNote,
|
|
18146
18705
|
"",
|
|
18147
18706
|
"Dry run: nothing was installed."
|
|
18148
18707
|
]
|
|
@@ -18158,13 +18717,17 @@ var serviceCommands = [
|
|
|
18158
18717
|
throw new CliError("operation-failed", `Could not install orkestrator@${version}: ${error instanceof Error ? error.message : String(error)}`, { retryable: true });
|
|
18159
18718
|
}
|
|
18160
18719
|
await service.installLauncher(layout, releaseDir);
|
|
18720
|
+
const rollbackTarget = service.nextRollbackTarget(layout, service.readServiceConfig(layout), service.linkedVersion(layout.current), version);
|
|
18161
18721
|
await service.activateRelease(layout, version);
|
|
18722
|
+
const installGeneration = randomUUID4();
|
|
18162
18723
|
await service.writeServiceConfig(layout, {
|
|
18724
|
+
installGeneration,
|
|
18163
18725
|
version: 1,
|
|
18164
18726
|
dataDir,
|
|
18165
18727
|
bun,
|
|
18166
18728
|
bunVersion,
|
|
18167
18729
|
args: serveArgs,
|
|
18730
|
+
...rollbackTarget ? { rollbackTarget } : {},
|
|
18168
18731
|
installedAt: new Date().toISOString()
|
|
18169
18732
|
});
|
|
18170
18733
|
await mkdir2(layout.logs, { recursive: true });
|
|
@@ -18175,6 +18738,8 @@ var serviceCommands = [
|
|
|
18175
18738
|
let connection = null;
|
|
18176
18739
|
if (started) {
|
|
18177
18740
|
const previousGeneration = await currentDescriptorGeneration(dataDir);
|
|
18741
|
+
const loadedAt = Date.now();
|
|
18742
|
+
const logOffset = await stat2(logFile).then((file) => file.size, () => 0);
|
|
18178
18743
|
const commands = serviceManagerCommands(unit);
|
|
18179
18744
|
commands.load.forEach((argv, index) => runManager(argv, unit.manager === "launchd" && index === 0));
|
|
18180
18745
|
if (readyWaitMs > 0) {
|
|
@@ -18182,7 +18747,13 @@ var serviceCommands = [
|
|
|
18182
18747
|
`);
|
|
18183
18748
|
connection = await awaitServiceConnection(dataDir, previousGeneration, {
|
|
18184
18749
|
timeoutMs: readyWaitMs,
|
|
18185
|
-
signal: context.signal
|
|
18750
|
+
signal: context.signal,
|
|
18751
|
+
expectedVersion: version,
|
|
18752
|
+
assertStarting: async () => {
|
|
18753
|
+
const failure = service.readStartupFailure(layout, version, loadedAt, installGeneration);
|
|
18754
|
+
if (failure)
|
|
18755
|
+
throw await startupFailedError(version, failure, logFile, logOffset);
|
|
18756
|
+
}
|
|
18186
18757
|
});
|
|
18187
18758
|
}
|
|
18188
18759
|
}
|
|
@@ -18193,6 +18764,7 @@ var serviceCommands = [
|
|
|
18193
18764
|
started,
|
|
18194
18765
|
ready: connection !== null,
|
|
18195
18766
|
browserUrl: connection?.browserUrl ?? null,
|
|
18767
|
+
clientUrls: connection?.clientUrls ?? [],
|
|
18196
18768
|
authFile: connection?.authFile ?? null,
|
|
18197
18769
|
...connection?.credentialError ? { credentialError: connection.credentialError } : {},
|
|
18198
18770
|
...summary
|
|
@@ -18209,6 +18781,7 @@ var serviceCommands = [
|
|
|
18209
18781
|
"",
|
|
18210
18782
|
...keyValues([
|
|
18211
18783
|
["address", connection.browserUrl ?? "(no browser listener; see the log)"],
|
|
18784
|
+
...connection.clientUrls.length > 0 ? [["local listeners", connection.clientUrls.join(", ")]] : [],
|
|
18212
18785
|
["auth file", connection.authFile],
|
|
18213
18786
|
...context.io.stdoutIsTty && context.global.output === "human" && connection.token ? [["token", connection.token]] : []
|
|
18214
18787
|
]),
|
|
@@ -18217,6 +18790,7 @@ var serviceCommands = [
|
|
|
18217
18790
|
"",
|
|
18218
18791
|
`The backend has not reported ready yet. Follow ${logFile}; its address and token file are logged there.`
|
|
18219
18792
|
] : [],
|
|
18793
|
+
...portNote,
|
|
18220
18794
|
...unit.manager === "systemd" ? ["Run `loginctl enable-linger $USER` so it keeps running after you log out."] : []
|
|
18221
18795
|
]
|
|
18222
18796
|
};
|
|
@@ -18269,15 +18843,17 @@ var serviceCommands = [
|
|
|
18269
18843
|
const state = service.readLauncherState(layout);
|
|
18270
18844
|
const installed = existsSync6(unit.file);
|
|
18271
18845
|
const running = installed ? runManager(serviceManagerCommands(unit).status, true).ok : false;
|
|
18846
|
+
const current = service.linkedVersion(layout.current);
|
|
18847
|
+
const previous = service.linkedVersion(layout.previous);
|
|
18272
18848
|
const result = {
|
|
18273
18849
|
dataDir,
|
|
18274
18850
|
installed,
|
|
18275
18851
|
running,
|
|
18276
18852
|
unit: { manager: unit.manager, name: unit.name, file: unit.file },
|
|
18277
|
-
current
|
|
18278
|
-
previous
|
|
18853
|
+
current,
|
|
18854
|
+
previous,
|
|
18279
18855
|
bun: config?.bun ?? null,
|
|
18280
|
-
args: config
|
|
18856
|
+
args: service.effectiveServiceArguments(config, current, previous),
|
|
18281
18857
|
lastReadyVersion: state.lastReadyVersion,
|
|
18282
18858
|
rollback: state.rollback
|
|
18283
18859
|
};
|
|
@@ -18850,18 +19426,357 @@ var runCommands = [
|
|
|
18850
19426
|
}
|
|
18851
19427
|
];
|
|
18852
19428
|
|
|
18853
|
-
// src/client/commands/
|
|
18854
|
-
var
|
|
18855
|
-
|
|
18856
|
-
|
|
18857
|
-
|
|
18858
|
-
|
|
18859
|
-
|
|
18860
|
-
|
|
18861
|
-
|
|
18862
|
-
|
|
18863
|
-
|
|
18864
|
-
|
|
19429
|
+
// src/client/commands/session-goal.ts
|
|
19430
|
+
var WAIT_OPTIONS2 = [
|
|
19431
|
+
{
|
|
19432
|
+
name: "wait",
|
|
19433
|
+
kind: "boolean",
|
|
19434
|
+
description: "Observe the goal until it stops. Exit 0 for complete, 10 claimed, 11 blocked, 12 limited, 13 paused, 14 cleared or replaced."
|
|
19435
|
+
},
|
|
19436
|
+
TIMEOUT_OPTION,
|
|
19437
|
+
{
|
|
19438
|
+
name: "continue-on-interaction",
|
|
19439
|
+
kind: "boolean",
|
|
19440
|
+
description: "While waiting, keep waiting when a turn needs an answer (another client may answer)."
|
|
19441
|
+
}
|
|
19442
|
+
];
|
|
19443
|
+
var LIMIT_OPTIONS = [
|
|
19444
|
+
{
|
|
19445
|
+
name: "max-iterations",
|
|
19446
|
+
kind: "integer",
|
|
19447
|
+
valueName: "N",
|
|
19448
|
+
description: `Turns the backend may send, at most ${NATIVE_AGENT_GOAL_LIMITS.maxIterationsLimit}.`
|
|
19449
|
+
},
|
|
19450
|
+
{
|
|
19451
|
+
name: "budget",
|
|
19452
|
+
kind: "integer",
|
|
19453
|
+
valueName: "TOKENS",
|
|
19454
|
+
description: "Tokens the goal may spend; refused where the provider reports no usage."
|
|
19455
|
+
}
|
|
19456
|
+
];
|
|
19457
|
+
var LIMIT_FIELDS = { budget: "tokenBudget" };
|
|
19458
|
+
function limitsInput(options2) {
|
|
19459
|
+
return {
|
|
19460
|
+
...options2["max-iterations"] !== undefined ? { maxIterations: options2["max-iterations"] } : {},
|
|
19461
|
+
...options2.budget !== undefined ? { tokenBudget: options2.budget } : {}
|
|
19462
|
+
};
|
|
19463
|
+
}
|
|
19464
|
+
function goalLines(read) {
|
|
19465
|
+
const goal = read.goal;
|
|
19466
|
+
if (!goal)
|
|
19467
|
+
return ["no goal"];
|
|
19468
|
+
return keyValues([
|
|
19469
|
+
["goal", goal.goalId],
|
|
19470
|
+
["status", goal.status],
|
|
19471
|
+
["reason", read.description],
|
|
19472
|
+
["iterations", `${goal.iterations}/${goal.maxIterations}`],
|
|
19473
|
+
[
|
|
19474
|
+
"tokens",
|
|
19475
|
+
goal.tokenBudget !== undefined ? `${goal.tokensUsed ?? 0}/${goal.tokenBudget}` : undefined
|
|
19476
|
+
],
|
|
19477
|
+
["check", goal.check],
|
|
19478
|
+
[
|
|
19479
|
+
"verifier",
|
|
19480
|
+
goal.verifier ? `${goal.verifier.agent}${goal.verifier.model ? `:${goal.verifier.model}` : ""}` : undefined
|
|
19481
|
+
],
|
|
19482
|
+
["last check", goal.lastCheck ? goal.lastCheck.passed ? "passed" : "failed" : undefined],
|
|
19483
|
+
["objective", goal.objective]
|
|
19484
|
+
]);
|
|
19485
|
+
}
|
|
19486
|
+
function goalDetails(sessionId, goal) {
|
|
19487
|
+
return {
|
|
19488
|
+
sessionId,
|
|
19489
|
+
goalId: goal.goalId,
|
|
19490
|
+
status: goal.status,
|
|
19491
|
+
...goal.statusReason ? { statusReason: goal.statusReason } : {},
|
|
19492
|
+
iterations: goal.iterations,
|
|
19493
|
+
maxIterations: goal.maxIterations
|
|
19494
|
+
};
|
|
19495
|
+
}
|
|
19496
|
+
var STOPPED_MESSAGES = {
|
|
19497
|
+
"goal-claimed": "The agent says the goal is done, and nothing verified it",
|
|
19498
|
+
"goal-blocked": "The goal is blocked: the agent says it needs a person",
|
|
19499
|
+
"goal-limited": "The goal reached its iteration cap or token budget",
|
|
19500
|
+
"goal-paused": "The goal is paused"
|
|
19501
|
+
};
|
|
19502
|
+
async function waitForGoal(context, session, sessionId, goalId, timeoutMs, options2) {
|
|
19503
|
+
let latest;
|
|
19504
|
+
const receiptOption = options2.receipt ? { receipt: options2.receipt } : {};
|
|
19505
|
+
try {
|
|
19506
|
+
return await observe(async () => {
|
|
19507
|
+
const read = await session.read("session.goal.get", {
|
|
19508
|
+
sessionId,
|
|
19509
|
+
...options2.includeCheckOutput ? { includeCheckOutput: true } : {}
|
|
19510
|
+
});
|
|
19511
|
+
const verdict = classifyGoalForWait(read, goalId, {
|
|
19512
|
+
continueOnInteraction: options2.continueOnInteraction
|
|
19513
|
+
});
|
|
19514
|
+
if (verdict.kind === "pending") {
|
|
19515
|
+
latest = verdict.goal;
|
|
19516
|
+
return { done: false };
|
|
19517
|
+
}
|
|
19518
|
+
if (verdict.kind === "complete")
|
|
19519
|
+
return {
|
|
19520
|
+
done: true,
|
|
19521
|
+
value: {
|
|
19522
|
+
...read,
|
|
19523
|
+
goal: verdict.goal,
|
|
19524
|
+
description: "Complete",
|
|
19525
|
+
...read.goal?.goalId !== goalId ? { checkOutput: undefined } : {}
|
|
19526
|
+
}
|
|
19527
|
+
};
|
|
19528
|
+
if (verdict.kind === "interaction") {
|
|
19529
|
+
throw new CliError("interaction-required", "The goal's turn is waiting for an answer to a pending interaction", {
|
|
19530
|
+
...receiptOption,
|
|
19531
|
+
details: {
|
|
19532
|
+
...goalDetails(sessionId, verdict.goal),
|
|
19533
|
+
pendingInteractionCount: read.pendingInteractionCount
|
|
19534
|
+
}
|
|
19535
|
+
});
|
|
19536
|
+
}
|
|
19537
|
+
if (verdict.kind === "gone") {
|
|
19538
|
+
throw new CliError("goal-gone", "The goal was cleared or replaced before it ended", {
|
|
19539
|
+
...receiptOption,
|
|
19540
|
+
details: {
|
|
19541
|
+
sessionId,
|
|
19542
|
+
goalId,
|
|
19543
|
+
...verdict.goal ? { status: verdict.goal.status } : {}
|
|
19544
|
+
}
|
|
19545
|
+
});
|
|
19546
|
+
}
|
|
19547
|
+
throw new CliError(verdict.code, STOPPED_MESSAGES[verdict.code], {
|
|
19548
|
+
...receiptOption,
|
|
19549
|
+
details: goalDetails(sessionId, verdict.goal)
|
|
19550
|
+
});
|
|
19551
|
+
}, { timeoutMs, signal: context.signal });
|
|
19552
|
+
} catch (error) {
|
|
19553
|
+
if (!(error instanceof ObservationStopped))
|
|
19554
|
+
throw error;
|
|
19555
|
+
const details = { sessionId, goalId, ...latest ? goalDetails(sessionId, latest) : {} };
|
|
19556
|
+
if (error.reason === "signal") {
|
|
19557
|
+
throw new CliError("observation-interrupted", "Stopped observing the goal; it continues in the backend", { ...receiptOption, details: { ...details, signal: signalName(context.signal) } });
|
|
19558
|
+
}
|
|
19559
|
+
throw new CliError("deadline-exceeded", "Timed out observing the goal; it continues in the backend. `session goal get --wait` observes it again", { ...receiptOption, details });
|
|
19560
|
+
}
|
|
19561
|
+
}
|
|
19562
|
+
function waitOutcome(base, read) {
|
|
19563
|
+
return {
|
|
19564
|
+
...base,
|
|
19565
|
+
result: read,
|
|
19566
|
+
human: goalLines(read),
|
|
19567
|
+
ids: read.goal ? [read.goal.goalId] : []
|
|
19568
|
+
};
|
|
19569
|
+
}
|
|
19570
|
+
async function runLoopMutation(context, parsed, action, input) {
|
|
19571
|
+
const options2 = parsed.options;
|
|
19572
|
+
const sessionId = parsed.positionals.session;
|
|
19573
|
+
validateWaitOptions(options2);
|
|
19574
|
+
if (options2.wait !== true) {
|
|
19575
|
+
const { session, result, receipt, warnings } = await submit(context, action, input, options2);
|
|
19576
|
+
return {
|
|
19577
|
+
action,
|
|
19578
|
+
result,
|
|
19579
|
+
receipt,
|
|
19580
|
+
warnings,
|
|
19581
|
+
connection: session.identity,
|
|
19582
|
+
ids: result.goal ? [result.goal.goalId] : [],
|
|
19583
|
+
human: result.goal ? keyValues([
|
|
19584
|
+
["goal", result.goal.goalId],
|
|
19585
|
+
["status", result.goal.status],
|
|
19586
|
+
["iterations", `${result.goal.iterations}/${result.goal.maxIterations}`]
|
|
19587
|
+
]) : receipt ? receiptLines(receipt) : []
|
|
19588
|
+
};
|
|
19589
|
+
}
|
|
19590
|
+
const budget = options2.timeout ?? DEFAULT_WAIT_MS;
|
|
19591
|
+
const started = Date.now();
|
|
19592
|
+
const submitted = await submitForWait(context, action, input, options2, budget);
|
|
19593
|
+
const { session, warnings } = submitted;
|
|
19594
|
+
let receipt = submitted.receipt;
|
|
19595
|
+
let mutation = submitted.result;
|
|
19596
|
+
if (!mutation && receipt) {
|
|
19597
|
+
receipt = await waitForOperation(context, session, receipt, budget - (Date.now() - started));
|
|
19598
|
+
const failure = receiptFailure(receipt);
|
|
19599
|
+
if (failure)
|
|
19600
|
+
throw failure;
|
|
19601
|
+
mutation = receipt.result;
|
|
19602
|
+
}
|
|
19603
|
+
const goalId = mutation?.goal?.goalId;
|
|
19604
|
+
if (!goalId)
|
|
19605
|
+
throw new CliError("response-invalid", `${action} returned no goal`);
|
|
19606
|
+
const read = await waitForGoal(context, session, sessionId, goalId, Math.max(0, budget - (Date.now() - started)), {
|
|
19607
|
+
continueOnInteraction: options2["continue-on-interaction"] === true,
|
|
19608
|
+
...receipt ? { receipt } : {}
|
|
19609
|
+
});
|
|
19610
|
+
return waitOutcome({ action, result: read, receipt, warnings, connection: session.identity }, read);
|
|
19611
|
+
}
|
|
19612
|
+
var sessionGoalCommands = [
|
|
19613
|
+
{
|
|
19614
|
+
path: ["session", "goal", "set"],
|
|
19615
|
+
action: "session.goal.set",
|
|
19616
|
+
summary: "Give a session a goal: the backend sends turns until a check proves it or it stops.",
|
|
19617
|
+
description: "Pass the objective with --objective, --objective-file or --objective-stdin. Without --check or --verifier, a done report ends the goal as claimed: nothing verified it, and --wait exits 10. A session holds one goal, and a running goal is refused (exit 8) until it is cleared. A goal never changes the permission mode, so an approval still parks a turn (--wait exits 6).",
|
|
19618
|
+
positionals: [{ name: "session", required: true }],
|
|
19619
|
+
options: [
|
|
19620
|
+
{
|
|
19621
|
+
name: "objective-file",
|
|
19622
|
+
kind: "string",
|
|
19623
|
+
valueName: "PATH",
|
|
19624
|
+
description: "Read the objective from a local file."
|
|
19625
|
+
},
|
|
19626
|
+
{ name: "objective-stdin", kind: "boolean", description: "Read the objective from stdin." },
|
|
19627
|
+
{ name: "objective", kind: "string", valueName: "TEXT", description: "Inline objective." },
|
|
19628
|
+
{
|
|
19629
|
+
name: "check",
|
|
19630
|
+
kind: "string",
|
|
19631
|
+
valueName: "COMMAND",
|
|
19632
|
+
description: "One-line shell command run in the workspace on a done report; exit 0 completes the goal."
|
|
19633
|
+
},
|
|
19634
|
+
{
|
|
19635
|
+
name: "verifier",
|
|
19636
|
+
kind: "string",
|
|
19637
|
+
valueName: "AGENT[:MODEL]",
|
|
19638
|
+
description: "A fresh read-only session on that agent judges the work on a done report; with --check, both must pass."
|
|
19639
|
+
},
|
|
19640
|
+
...LIMIT_OPTIONS,
|
|
19641
|
+
...WAIT_OPTIONS2,
|
|
19642
|
+
REQUEST_ID_OPTION
|
|
19643
|
+
],
|
|
19644
|
+
inputFields: { ...LIMIT_FIELDS, "objective-file": "objective", "objective-stdin": "objective" },
|
|
19645
|
+
idOutput: "goal ID",
|
|
19646
|
+
examples: [
|
|
19647
|
+
'orkestrator session goal set ses_\u2026 --objective "Make the tests pass" --check "bun test" --max-iterations 10 --wait --timeout 2h'
|
|
19648
|
+
],
|
|
19649
|
+
async run(context, parsed) {
|
|
19650
|
+
const options2 = parsed.options;
|
|
19651
|
+
validateWaitOptions(options2);
|
|
19652
|
+
const verifier = options2.verifier === undefined ? undefined : parseVerifierFlag(String(options2.verifier));
|
|
19653
|
+
if (verifier && "error" in verifier)
|
|
19654
|
+
throw new CliError("invalid-input", verifier.error);
|
|
19655
|
+
const objective = await readTextSource(context.io, {
|
|
19656
|
+
file: options2["objective-file"],
|
|
19657
|
+
stdin: options2["objective-stdin"],
|
|
19658
|
+
inline: options2.objective,
|
|
19659
|
+
label: "Objective",
|
|
19660
|
+
maxBytes: NATIVE_AGENT_GOAL_LIMITS.objectiveMaxChars * 4,
|
|
19661
|
+
names: {
|
|
19662
|
+
file: "--objective-file",
|
|
19663
|
+
stdin: "--objective-stdin",
|
|
19664
|
+
inline: "--objective"
|
|
19665
|
+
},
|
|
19666
|
+
required: true
|
|
19667
|
+
});
|
|
19668
|
+
return runLoopMutation(context, parsed, "session.goal.set", {
|
|
19669
|
+
sessionId: parsed.positionals.session,
|
|
19670
|
+
objective,
|
|
19671
|
+
...options2.check !== undefined ? { check: options2.check } : {},
|
|
19672
|
+
...verifier ? { verifier } : {},
|
|
19673
|
+
...limitsInput(options2)
|
|
19674
|
+
});
|
|
19675
|
+
}
|
|
19676
|
+
},
|
|
19677
|
+
{
|
|
19678
|
+
path: ["session", "goal", "get"],
|
|
19679
|
+
action: "session.goal.get",
|
|
19680
|
+
summary: "Show a session's goal: status, iterations, limits and the last check.",
|
|
19681
|
+
description: "Reads the session record and starts nothing. With no goal the result's goal is null and the exit is 0, unless --wait is given, which exits 14. --wait observes the goal the session holds now, so it picks up a goal after an earlier --wait timed out.",
|
|
19682
|
+
positionals: [{ name: "session", required: true }],
|
|
19683
|
+
options: [
|
|
19684
|
+
{
|
|
19685
|
+
name: "check-output",
|
|
19686
|
+
kind: "boolean",
|
|
19687
|
+
description: "Include the last check's output tail. It is untrusted command output."
|
|
19688
|
+
},
|
|
19689
|
+
...WAIT_OPTIONS2
|
|
19690
|
+
],
|
|
19691
|
+
inputFields: { "check-output": "includeCheckOutput" },
|
|
19692
|
+
idOutput: "goal ID",
|
|
19693
|
+
async run(context, parsed) {
|
|
19694
|
+
const options2 = parsed.options;
|
|
19695
|
+
validateWaitOptions(options2);
|
|
19696
|
+
const sessionId = parsed.positionals.session;
|
|
19697
|
+
const session = await context.session();
|
|
19698
|
+
const first = await session.read("session.goal.get", {
|
|
19699
|
+
sessionId,
|
|
19700
|
+
...options2["check-output"] === true ? { includeCheckOutput: true } : {}
|
|
19701
|
+
});
|
|
19702
|
+
const base = { action: "session.goal.get", connection: session.identity };
|
|
19703
|
+
if (options2.wait !== true)
|
|
19704
|
+
return waitOutcome({ ...base, result: first }, first);
|
|
19705
|
+
const goalId = first.goal?.goalId;
|
|
19706
|
+
if (!goalId) {
|
|
19707
|
+
throw new CliError("goal-gone", "The session has no goal to wait for", {
|
|
19708
|
+
details: { sessionId }
|
|
19709
|
+
});
|
|
19710
|
+
}
|
|
19711
|
+
const read = await waitForGoal(context, session, sessionId, goalId, options2.timeout ?? DEFAULT_WAIT_MS, {
|
|
19712
|
+
continueOnInteraction: options2["continue-on-interaction"] === true,
|
|
19713
|
+
includeCheckOutput: options2["check-output"] === true
|
|
19714
|
+
});
|
|
19715
|
+
return waitOutcome({ ...base, result: read }, read);
|
|
19716
|
+
}
|
|
19717
|
+
},
|
|
19718
|
+
{
|
|
19719
|
+
path: ["session", "goal", "pause"],
|
|
19720
|
+
action: "session.goal.pause",
|
|
19721
|
+
summary: "Pause a running goal; no further turn starts until it is resumed.",
|
|
19722
|
+
description: "A turn already running finishes. Use `session stop` to stop it too. Refused (exit 8) when no goal is running.",
|
|
19723
|
+
positionals: [{ name: "session", required: true }],
|
|
19724
|
+
options: [REQUEST_ID_OPTION],
|
|
19725
|
+
idOutput: "goal ID",
|
|
19726
|
+
run: (context, parsed) => runGoalControl(context, parsed, "session.goal.pause")
|
|
19727
|
+
},
|
|
19728
|
+
{
|
|
19729
|
+
path: ["session", "goal", "resume"],
|
|
19730
|
+
action: "session.goal.resume",
|
|
19731
|
+
summary: "Resume a stopped goal, optionally with a higher limit.",
|
|
19732
|
+
description: "A goal stopped by its limit needs a higher --max-iterations or --budget. An ended goal (claimed, complete) cannot resume; set a new one. Refused with exit 8.",
|
|
19733
|
+
positionals: [{ name: "session", required: true }],
|
|
19734
|
+
options: [...LIMIT_OPTIONS, ...WAIT_OPTIONS2, REQUEST_ID_OPTION],
|
|
19735
|
+
inputFields: LIMIT_FIELDS,
|
|
19736
|
+
idOutput: "goal ID",
|
|
19737
|
+
run: (context, parsed) => runLoopMutation(context, parsed, "session.goal.resume", {
|
|
19738
|
+
sessionId: parsed.positionals.session,
|
|
19739
|
+
...limitsInput(parsed.options)
|
|
19740
|
+
})
|
|
19741
|
+
},
|
|
19742
|
+
{
|
|
19743
|
+
path: ["session", "goal", "clear"],
|
|
19744
|
+
action: "session.goal.clear",
|
|
19745
|
+
summary: "Remove a session's goal, in any status.",
|
|
19746
|
+
description: "What the agent did stays, and the goal stays readable in the goal history. A session with no goal stays that way and exits 0.",
|
|
19747
|
+
positionals: [{ name: "session", required: true }],
|
|
19748
|
+
options: [REQUEST_ID_OPTION],
|
|
19749
|
+
idOutput: "goal ID",
|
|
19750
|
+
run: (context, parsed) => runGoalControl(context, parsed, "session.goal.clear")
|
|
19751
|
+
}
|
|
19752
|
+
];
|
|
19753
|
+
async function runGoalControl(context, parsed, action) {
|
|
19754
|
+
const { session, result, receipt, warnings } = await submit(context, action, { sessionId: parsed.positionals.session }, parsed.options);
|
|
19755
|
+
return {
|
|
19756
|
+
action,
|
|
19757
|
+
result,
|
|
19758
|
+
receipt,
|
|
19759
|
+
warnings,
|
|
19760
|
+
connection: session.identity,
|
|
19761
|
+
ids: result.goal ? [result.goal.goalId] : [],
|
|
19762
|
+
human: action === "session.goal.clear" ? [result.cleared ? "cleared" : "no goal"] : [
|
|
19763
|
+
`paused${result.goal ? ` at ${result.goal.iterations}/${result.goal.maxIterations}` : ""}`
|
|
19764
|
+
]
|
|
19765
|
+
};
|
|
19766
|
+
}
|
|
19767
|
+
|
|
19768
|
+
// src/client/commands/ticket.ts
|
|
19769
|
+
var STATUSES = ["backlog", "in-progress", "review", "done"];
|
|
19770
|
+
var KINDS3 = ["feature", "work-brief"];
|
|
19771
|
+
var PROJECT_OPTION3 = {
|
|
19772
|
+
name: "project",
|
|
19773
|
+
kind: "string",
|
|
19774
|
+
valueName: "PROJECT_ID",
|
|
19775
|
+
description: "Project ID (project list)."
|
|
19776
|
+
};
|
|
19777
|
+
var STATUS_OPTION = {
|
|
19778
|
+
name: "status",
|
|
19779
|
+
kind: "string",
|
|
18865
19780
|
valueName: "COLUMN",
|
|
18866
19781
|
description: `Column: ${STATUSES.join(", ")}.`
|
|
18867
19782
|
};
|
|
@@ -19267,6 +20182,742 @@ var toolchainCommands = [
|
|
|
19267
20182
|
}
|
|
19268
20183
|
];
|
|
19269
20184
|
|
|
20185
|
+
// ../protocol/src/public-api-inputs-pr.ts
|
|
20186
|
+
var PUBLIC_PR_BODY_MAX_CHARS = 65536;
|
|
20187
|
+
var PUBLIC_PR_REVIEW_COMMENTS_MAX = 200;
|
|
20188
|
+
var PUBLIC_PR_REVIEWERS_MAX = 100;
|
|
20189
|
+
var PUBLIC_PR_MERGE_METHODS = Object.freeze(["squash", "merge", "rebase"]);
|
|
20190
|
+
var PUBLIC_PR_LIST_FILTERS = Object.freeze([
|
|
20191
|
+
"open",
|
|
20192
|
+
"mine",
|
|
20193
|
+
"review-requested",
|
|
20194
|
+
"closed"
|
|
20195
|
+
]);
|
|
20196
|
+
var PUBLIC_PR_REVIEW_EVENTS = Object.freeze([
|
|
20197
|
+
"APPROVE",
|
|
20198
|
+
"REQUEST_CHANGES",
|
|
20199
|
+
"COMMENT"
|
|
20200
|
+
]);
|
|
20201
|
+
var PUBLIC_PR_SIDES = Object.freeze(["LEFT", "RIGHT"]);
|
|
20202
|
+
function schema4(properties, required = []) {
|
|
20203
|
+
return { type: "object", properties, required, additionalProperties: false };
|
|
20204
|
+
}
|
|
20205
|
+
var environmentId = {
|
|
20206
|
+
type: "string",
|
|
20207
|
+
minLength: 1,
|
|
20208
|
+
maxLength: PUBLIC_API_LIMITS.idMaxChars,
|
|
20209
|
+
description: "Environment ID, from environment.list. Its attached pull request is the target."
|
|
20210
|
+
};
|
|
20211
|
+
var projectId = {
|
|
20212
|
+
type: "string",
|
|
20213
|
+
minLength: 1,
|
|
20214
|
+
maxLength: PUBLIC_API_LIMITS.idMaxChars,
|
|
20215
|
+
description: "Project ID, from project.list."
|
|
20216
|
+
};
|
|
20217
|
+
var body = {
|
|
20218
|
+
type: "string",
|
|
20219
|
+
minLength: 1,
|
|
20220
|
+
maxLength: PUBLIC_PR_BODY_MAX_CHARS,
|
|
20221
|
+
description: "Markdown text, posted as the GitHub user configured in Settings."
|
|
20222
|
+
};
|
|
20223
|
+
var filePath = {
|
|
20224
|
+
type: "string",
|
|
20225
|
+
minLength: 1,
|
|
20226
|
+
maxLength: PUBLIC_API_LIMITS.pathMaxChars,
|
|
20227
|
+
description: "Repository-relative path of a changed file, from pr.files."
|
|
20228
|
+
};
|
|
20229
|
+
var threadId = {
|
|
20230
|
+
type: "string",
|
|
20231
|
+
minLength: 1,
|
|
20232
|
+
maxLength: PUBLIC_API_LIMITS.idMaxChars,
|
|
20233
|
+
description: "Review thread ID, from pr.files."
|
|
20234
|
+
};
|
|
20235
|
+
var commitSha = {
|
|
20236
|
+
type: "string",
|
|
20237
|
+
minLength: 40,
|
|
20238
|
+
maxLength: 40
|
|
20239
|
+
};
|
|
20240
|
+
var line = {
|
|
20241
|
+
type: "integer",
|
|
20242
|
+
minimum: 1,
|
|
20243
|
+
description: "Line in the new file (RIGHT) or the old file (LEFT)."
|
|
20244
|
+
};
|
|
20245
|
+
var side = {
|
|
20246
|
+
type: "string",
|
|
20247
|
+
enum: PUBLIC_PR_SIDES,
|
|
20248
|
+
description: "RIGHT: the new file. LEFT: the old file."
|
|
20249
|
+
};
|
|
20250
|
+
var startLine = {
|
|
20251
|
+
type: "integer",
|
|
20252
|
+
minimum: 1,
|
|
20253
|
+
description: "First line of a multi-line comment; below line."
|
|
20254
|
+
};
|
|
20255
|
+
var reviewerList = {
|
|
20256
|
+
type: "array",
|
|
20257
|
+
maxItems: PUBLIC_PR_REVIEWERS_MAX,
|
|
20258
|
+
items: { type: "string", minLength: 1, maxLength: PUBLIC_API_LIMITS.nameMaxChars }
|
|
20259
|
+
};
|
|
20260
|
+
var environmentOnly = schema4({ environmentId }, ["environmentId"]);
|
|
20261
|
+
var environmentExamples = [{ environmentId: "env-1" }];
|
|
20262
|
+
var PR_ACTION_INPUTS = Object.freeze({
|
|
20263
|
+
"pr.get": {
|
|
20264
|
+
capability: "sessions.read",
|
|
20265
|
+
confirmation: "never",
|
|
20266
|
+
input: environmentOnly,
|
|
20267
|
+
examples: environmentExamples
|
|
20268
|
+
},
|
|
20269
|
+
"pr.list": {
|
|
20270
|
+
capability: "sessions.read",
|
|
20271
|
+
confirmation: "never",
|
|
20272
|
+
input: schema4({
|
|
20273
|
+
projectId,
|
|
20274
|
+
filter: {
|
|
20275
|
+
type: "string",
|
|
20276
|
+
enum: PUBLIC_PR_LIST_FILTERS,
|
|
20277
|
+
description: "Which pull requests (default open)."
|
|
20278
|
+
},
|
|
20279
|
+
query: {
|
|
20280
|
+
type: "string",
|
|
20281
|
+
minLength: 1,
|
|
20282
|
+
maxLength: 256,
|
|
20283
|
+
description: "Search text, as in the pull request list's search box."
|
|
20284
|
+
}
|
|
20285
|
+
}, ["projectId"]),
|
|
20286
|
+
examples: [{ projectId: "project-1" }, { projectId: "project-1", filter: "closed" }]
|
|
20287
|
+
},
|
|
20288
|
+
"pr.timeline": {
|
|
20289
|
+
capability: "sessions.read",
|
|
20290
|
+
confirmation: "never",
|
|
20291
|
+
input: environmentOnly,
|
|
20292
|
+
examples: environmentExamples
|
|
20293
|
+
},
|
|
20294
|
+
"pr.files": {
|
|
20295
|
+
capability: "sessions.read",
|
|
20296
|
+
confirmation: "never",
|
|
20297
|
+
input: schema4({
|
|
20298
|
+
environmentId,
|
|
20299
|
+
path: {
|
|
20300
|
+
...filePath,
|
|
20301
|
+
description: "Return only this file, with its diff hunks. Omitted: every file without hunks, to keep the result small."
|
|
20302
|
+
}
|
|
20303
|
+
}, ["environmentId"]),
|
|
20304
|
+
examples: [{ environmentId: "env-1" }, { environmentId: "env-1", path: "src/index.ts" }]
|
|
20305
|
+
},
|
|
20306
|
+
"pr.options": {
|
|
20307
|
+
capability: "sessions.read",
|
|
20308
|
+
confirmation: "never",
|
|
20309
|
+
input: environmentOnly,
|
|
20310
|
+
examples: environmentExamples
|
|
20311
|
+
},
|
|
20312
|
+
"pr.merge": {
|
|
20313
|
+
capability: "environments.destructive",
|
|
20314
|
+
confirmation: "always",
|
|
20315
|
+
input: schema4({
|
|
20316
|
+
environmentId,
|
|
20317
|
+
method: {
|
|
20318
|
+
type: "string",
|
|
20319
|
+
enum: PUBLIC_PR_MERGE_METHODS,
|
|
20320
|
+
description: "How to merge (default squash, as on the Merge button)."
|
|
20321
|
+
},
|
|
20322
|
+
deleteBranch: {
|
|
20323
|
+
type: "boolean",
|
|
20324
|
+
description: "Delete the head branch after merging (default true). A project's home checkout keeps its branch."
|
|
20325
|
+
},
|
|
20326
|
+
cleanup: {
|
|
20327
|
+
type: "boolean",
|
|
20328
|
+
description: "Merge and Cleanup: delete this environment and its worktree once the merge is confirmed. Cannot be combined with deleteBranch false."
|
|
20329
|
+
},
|
|
20330
|
+
expectedPrUrl: {
|
|
20331
|
+
type: "string",
|
|
20332
|
+
description: "Refuse if the attached PR or the explicit cleanup/branch effects changed since confirmation."
|
|
20333
|
+
},
|
|
20334
|
+
expectedHeadSha: {
|
|
20335
|
+
...commitSha,
|
|
20336
|
+
description: "headSha from pr.get. The merge is refused if the branch has moved since, so what merges is what was read."
|
|
20337
|
+
}
|
|
20338
|
+
}, ["environmentId"]),
|
|
20339
|
+
examples: [
|
|
20340
|
+
{ environmentId: "env-1" },
|
|
20341
|
+
{ environmentId: "env-1", method: "rebase", deleteBranch: false },
|
|
20342
|
+
{ environmentId: "env-1", cleanup: true },
|
|
20343
|
+
{ environmentId: "env-1", expectedHeadSha: "0123456789abcdef0123456789abcdef01234567" }
|
|
20344
|
+
]
|
|
20345
|
+
},
|
|
20346
|
+
"pr.close": {
|
|
20347
|
+
capability: "sessions.control",
|
|
20348
|
+
confirmation: "always",
|
|
20349
|
+
input: environmentOnly,
|
|
20350
|
+
examples: environmentExamples
|
|
20351
|
+
},
|
|
20352
|
+
"pr.reopen": {
|
|
20353
|
+
capability: "sessions.control",
|
|
20354
|
+
confirmation: "voice",
|
|
20355
|
+
input: environmentOnly,
|
|
20356
|
+
examples: environmentExamples
|
|
20357
|
+
},
|
|
20358
|
+
"pr.ready": {
|
|
20359
|
+
capability: "sessions.control",
|
|
20360
|
+
confirmation: "voice",
|
|
20361
|
+
input: environmentOnly,
|
|
20362
|
+
examples: environmentExamples
|
|
20363
|
+
},
|
|
20364
|
+
"pr.draft": {
|
|
20365
|
+
capability: "sessions.control",
|
|
20366
|
+
confirmation: "voice",
|
|
20367
|
+
input: environmentOnly,
|
|
20368
|
+
examples: environmentExamples
|
|
20369
|
+
},
|
|
20370
|
+
"pr.comment": {
|
|
20371
|
+
capability: "sessions.control",
|
|
20372
|
+
confirmation: "voice",
|
|
20373
|
+
input: schema4({ environmentId, body }, ["environmentId", "body"]),
|
|
20374
|
+
examples: [{ environmentId: "env-1", body: "Merging once CI is green." }]
|
|
20375
|
+
},
|
|
20376
|
+
"pr.comment.edit": {
|
|
20377
|
+
capability: "sessions.control",
|
|
20378
|
+
confirmation: "voice",
|
|
20379
|
+
input: schema4({
|
|
20380
|
+
environmentId,
|
|
20381
|
+
commentId: {
|
|
20382
|
+
type: "integer",
|
|
20383
|
+
minimum: 1,
|
|
20384
|
+
description: "Comment ID, from pr.get or a pr.comment result."
|
|
20385
|
+
},
|
|
20386
|
+
body
|
|
20387
|
+
}, ["environmentId", "commentId", "body"]),
|
|
20388
|
+
examples: [{ environmentId: "env-1", commentId: 123, body: "Merged after the fix." }]
|
|
20389
|
+
},
|
|
20390
|
+
"pr.review": {
|
|
20391
|
+
capability: "sessions.control",
|
|
20392
|
+
confirmation: "always",
|
|
20393
|
+
input: schema4({
|
|
20394
|
+
environmentId,
|
|
20395
|
+
event: {
|
|
20396
|
+
type: "string",
|
|
20397
|
+
enum: PUBLIC_PR_REVIEW_EVENTS,
|
|
20398
|
+
description: "The verdict. REQUEST_CHANGES needs body; COMMENT needs body or comments. GitHub refuses approving your own pull request."
|
|
20399
|
+
},
|
|
20400
|
+
body: { ...body, description: "Review summary." },
|
|
20401
|
+
commitId: {
|
|
20402
|
+
...commitSha,
|
|
20403
|
+
description: "The commit reviewed (headSha from pr.get). Omitted: the current head."
|
|
20404
|
+
},
|
|
20405
|
+
comments: {
|
|
20406
|
+
type: "array",
|
|
20407
|
+
maxItems: PUBLIC_PR_REVIEW_COMMENTS_MAX,
|
|
20408
|
+
description: "Inline comments submitted with the review.",
|
|
20409
|
+
items: {
|
|
20410
|
+
type: "object",
|
|
20411
|
+
properties: { path: filePath, line, side, startLine, body },
|
|
20412
|
+
required: ["path", "line", "side", "body"],
|
|
20413
|
+
additionalProperties: false
|
|
20414
|
+
}
|
|
20415
|
+
}
|
|
20416
|
+
}, ["environmentId", "event"]),
|
|
20417
|
+
examples: [
|
|
20418
|
+
{ environmentId: "env-1", event: "APPROVE" },
|
|
20419
|
+
{
|
|
20420
|
+
environmentId: "env-1",
|
|
20421
|
+
event: "COMMENT",
|
|
20422
|
+
comments: [{ path: "src/index.ts", line: 12, side: "RIGHT", body: "Is this bounded?" }]
|
|
20423
|
+
}
|
|
20424
|
+
]
|
|
20425
|
+
},
|
|
20426
|
+
"pr.review.comment": {
|
|
20427
|
+
capability: "sessions.control",
|
|
20428
|
+
confirmation: "voice",
|
|
20429
|
+
input: schema4({
|
|
20430
|
+
environmentId,
|
|
20431
|
+
path: filePath,
|
|
20432
|
+
line,
|
|
20433
|
+
side,
|
|
20434
|
+
startLine,
|
|
20435
|
+
body,
|
|
20436
|
+
commitId: {
|
|
20437
|
+
...commitSha,
|
|
20438
|
+
description: "The commit commented on. Omitted: the pull request's current head."
|
|
20439
|
+
}
|
|
20440
|
+
}, ["environmentId", "path", "line", "side", "body"]),
|
|
20441
|
+
examples: [
|
|
20442
|
+
{ environmentId: "env-1", path: "src/index.ts", line: 12, side: "RIGHT", body: "Why?" }
|
|
20443
|
+
]
|
|
20444
|
+
},
|
|
20445
|
+
"pr.thread.reply": {
|
|
20446
|
+
capability: "sessions.control",
|
|
20447
|
+
confirmation: "voice",
|
|
20448
|
+
input: schema4({ environmentId, threadId, body }, ["environmentId", "threadId", "body"]),
|
|
20449
|
+
examples: [{ environmentId: "env-1", threadId: "PRRT_1", body: "Fixed." }]
|
|
20450
|
+
},
|
|
20451
|
+
"pr.thread.resolve": {
|
|
20452
|
+
capability: "sessions.control",
|
|
20453
|
+
confirmation: "voice",
|
|
20454
|
+
input: schema4({
|
|
20455
|
+
environmentId,
|
|
20456
|
+
threadId,
|
|
20457
|
+
resolved: { type: "boolean", description: "False to unresolve (default true)." }
|
|
20458
|
+
}, ["environmentId", "threadId"]),
|
|
20459
|
+
examples: [
|
|
20460
|
+
{ environmentId: "env-1", threadId: "PRRT_1" },
|
|
20461
|
+
{ environmentId: "env-1", threadId: "PRRT_1", resolved: false }
|
|
20462
|
+
]
|
|
20463
|
+
},
|
|
20464
|
+
"pr.file.viewed": {
|
|
20465
|
+
capability: "sessions.control",
|
|
20466
|
+
confirmation: "never",
|
|
20467
|
+
input: schema4({
|
|
20468
|
+
environmentId,
|
|
20469
|
+
path: filePath,
|
|
20470
|
+
viewed: { type: "boolean", description: "False to clear it (default true)." }
|
|
20471
|
+
}, ["environmentId", "path"]),
|
|
20472
|
+
examples: [{ environmentId: "env-1", path: "src/index.ts" }]
|
|
20473
|
+
},
|
|
20474
|
+
"pr.reviewers": {
|
|
20475
|
+
capability: "sessions.control",
|
|
20476
|
+
confirmation: "voice",
|
|
20477
|
+
input: schema4({
|
|
20478
|
+
environmentId,
|
|
20479
|
+
add: {
|
|
20480
|
+
...reviewerList,
|
|
20481
|
+
description: "Logins or org/team-slug entries to request a review from."
|
|
20482
|
+
},
|
|
20483
|
+
remove: {
|
|
20484
|
+
...reviewerList,
|
|
20485
|
+
description: "Logins or org/team-slug entries whose request to remove."
|
|
20486
|
+
}
|
|
20487
|
+
}, ["environmentId"]),
|
|
20488
|
+
examples: [{ environmentId: "env-1", add: ["octocat"] }]
|
|
20489
|
+
},
|
|
20490
|
+
"pr.label": {
|
|
20491
|
+
capability: "sessions.control",
|
|
20492
|
+
confirmation: "voice",
|
|
20493
|
+
input: schema4({
|
|
20494
|
+
environmentId,
|
|
20495
|
+
name: {
|
|
20496
|
+
type: "string",
|
|
20497
|
+
minLength: 1,
|
|
20498
|
+
maxLength: 100,
|
|
20499
|
+
description: "Label name, from pr.options."
|
|
20500
|
+
},
|
|
20501
|
+
present: { type: "boolean", description: "False to remove the label (default true)." }
|
|
20502
|
+
}, ["environmentId", "name"]),
|
|
20503
|
+
examples: [{ environmentId: "env-1", name: "bug" }]
|
|
20504
|
+
}
|
|
20505
|
+
});
|
|
20506
|
+
|
|
20507
|
+
// src/client/commands/pull-requests.ts
|
|
20508
|
+
var ENVIRONMENT = { name: "environment", required: true, description: "Its pull request." };
|
|
20509
|
+
var YES_OPTION = {
|
|
20510
|
+
name: "yes",
|
|
20511
|
+
kind: "boolean",
|
|
20512
|
+
description: "Confirm this change on GitHub. Required."
|
|
20513
|
+
};
|
|
20514
|
+
var BODY_OPTIONS = [
|
|
20515
|
+
{ name: "body", kind: "string", valueName: "TEXT", description: "Inline Markdown text." },
|
|
20516
|
+
{
|
|
20517
|
+
name: "body-file",
|
|
20518
|
+
kind: "string",
|
|
20519
|
+
valueName: "PATH",
|
|
20520
|
+
description: "Read the text from a local file."
|
|
20521
|
+
}
|
|
20522
|
+
];
|
|
20523
|
+
var INLINE_OPTIONS = [
|
|
20524
|
+
{ name: "path", kind: "string", valueName: "PATH", description: "Changed file." },
|
|
20525
|
+
{ name: "line", kind: "string", valueName: "N", description: "Line of the comment." },
|
|
20526
|
+
{ name: "side", kind: "string", valueName: "SIDE", description: "RIGHT (new) or LEFT (old)." },
|
|
20527
|
+
{ name: "start-line", kind: "string", valueName: "N", description: "First line of a range." }
|
|
20528
|
+
];
|
|
20529
|
+
function requireYes(parsed, what) {
|
|
20530
|
+
if (parsed.options.yes !== true) {
|
|
20531
|
+
throw new CliError("invalid-input", `${what}; pass --yes to confirm`);
|
|
20532
|
+
}
|
|
20533
|
+
}
|
|
20534
|
+
function requiredOption(parsed, name) {
|
|
20535
|
+
const value = stringOption(parsed.options, name);
|
|
20536
|
+
if (!value)
|
|
20537
|
+
throw new CliError("invalid-input", `--${name} is required`);
|
|
20538
|
+
return value;
|
|
20539
|
+
}
|
|
20540
|
+
function integerOption(parsed, name) {
|
|
20541
|
+
const value = stringOption(parsed.options, name);
|
|
20542
|
+
if (value === undefined)
|
|
20543
|
+
return;
|
|
20544
|
+
const number = Number(value);
|
|
20545
|
+
if (!Number.isSafeInteger(number) || number < 1) {
|
|
20546
|
+
throw new CliError("invalid-input", `--${name} must be a positive integer`);
|
|
20547
|
+
}
|
|
20548
|
+
return number;
|
|
20549
|
+
}
|
|
20550
|
+
function requiredInteger(parsed, name) {
|
|
20551
|
+
const number = integerOption(parsed, name);
|
|
20552
|
+
if (number === undefined)
|
|
20553
|
+
throw new CliError("invalid-input", `--${name} is required`);
|
|
20554
|
+
return number;
|
|
20555
|
+
}
|
|
20556
|
+
async function bodyText(context, parsed, required) {
|
|
20557
|
+
const body = await readTextSource(context.io, {
|
|
20558
|
+
file: parsed.options["body-file"],
|
|
20559
|
+
inline: parsed.options.body,
|
|
20560
|
+
label: "Body",
|
|
20561
|
+
maxBytes: PUBLIC_PR_BODY_MAX_CHARS * 4,
|
|
20562
|
+
names: { file: "--body-file", stdin: "", inline: "--body" },
|
|
20563
|
+
required
|
|
20564
|
+
});
|
|
20565
|
+
if (body !== undefined && body.length > PUBLIC_PR_BODY_MAX_CHARS) {
|
|
20566
|
+
throw new CliError("input-too-large", `Body is longer than ${PUBLIC_PR_BODY_MAX_CHARS} characters`);
|
|
20567
|
+
}
|
|
20568
|
+
return body;
|
|
20569
|
+
}
|
|
20570
|
+
function repeatedList(value) {
|
|
20571
|
+
return typeof value === "string" ? value.split(",").map((entry) => entry.trim()).filter(Boolean) : [];
|
|
20572
|
+
}
|
|
20573
|
+
function changeHuman(result) {
|
|
20574
|
+
return keyValues(Object.entries(result).filter(([key, value]) => key !== "environmentId" && typeof value !== "object"));
|
|
20575
|
+
}
|
|
20576
|
+
function change(name, action, summary, options2, input, extras = {}) {
|
|
20577
|
+
return {
|
|
20578
|
+
path: ["pr", name],
|
|
20579
|
+
action,
|
|
20580
|
+
summary,
|
|
20581
|
+
positionals: [ENVIRONMENT],
|
|
20582
|
+
options: [...options2, REQUEST_ID_OPTION],
|
|
20583
|
+
idOutput: "pull request URL",
|
|
20584
|
+
async run(context, parsed) {
|
|
20585
|
+
const environmentId = parsed.positionals.environment;
|
|
20586
|
+
const { session, result, receipt, warnings } = await submit(context, action, { environmentId, ...await input(context, parsed) }, parsed.options);
|
|
20587
|
+
return {
|
|
20588
|
+
action,
|
|
20589
|
+
result,
|
|
20590
|
+
receipt,
|
|
20591
|
+
warnings,
|
|
20592
|
+
connection: session.identity,
|
|
20593
|
+
ids: typeof result.url === "string" ? [result.url] : [],
|
|
20594
|
+
human: changeHuman(result)
|
|
20595
|
+
};
|
|
20596
|
+
},
|
|
20597
|
+
...extras
|
|
20598
|
+
};
|
|
20599
|
+
}
|
|
20600
|
+
function read(name, action, summary, options2, input, human) {
|
|
20601
|
+
return {
|
|
20602
|
+
path: ["pr", name],
|
|
20603
|
+
action,
|
|
20604
|
+
summary,
|
|
20605
|
+
positionals: [ENVIRONMENT],
|
|
20606
|
+
options: options2,
|
|
20607
|
+
async run(context, parsed) {
|
|
20608
|
+
const session = await context.session();
|
|
20609
|
+
const result = await session.read(action, {
|
|
20610
|
+
environmentId: parsed.positionals.environment,
|
|
20611
|
+
...input(parsed)
|
|
20612
|
+
});
|
|
20613
|
+
return { action, result, connection: session.identity, human: human(result) };
|
|
20614
|
+
}
|
|
20615
|
+
};
|
|
20616
|
+
}
|
|
20617
|
+
function pullRequestLines(result) {
|
|
20618
|
+
const pullRequest = result.pullRequest;
|
|
20619
|
+
if (!pullRequest) {
|
|
20620
|
+
return keyValues([
|
|
20621
|
+
["attachment", result.attachment],
|
|
20622
|
+
["url", result.url],
|
|
20623
|
+
["error", result.error]
|
|
20624
|
+
]);
|
|
20625
|
+
}
|
|
20626
|
+
return keyValues([
|
|
20627
|
+
["attachment", result.attachment],
|
|
20628
|
+
["number", pullRequest.number],
|
|
20629
|
+
["title", pullRequest.title],
|
|
20630
|
+
["url", pullRequest.htmlUrl],
|
|
20631
|
+
["state", pullRequest.isDraft ? `${pullRequest.state} (draft)` : pullRequest.state],
|
|
20632
|
+
["branch", `${pullRequest.headRef} into ${pullRequest.baseRef}`],
|
|
20633
|
+
["head", pullRequest.headSha],
|
|
20634
|
+
["mergeability", pullRequest.mergeability],
|
|
20635
|
+
["checks", pullRequest.checksState]
|
|
20636
|
+
]);
|
|
20637
|
+
}
|
|
20638
|
+
function timelineLines(result) {
|
|
20639
|
+
const items = result.items;
|
|
20640
|
+
const lines = items.flatMap((item) => {
|
|
20641
|
+
if (item.kind === "comment") {
|
|
20642
|
+
return [
|
|
20643
|
+
`comment \xB7 ${item.comment.author?.login ?? "unknown"} \xB7 ${item.comment.createdAt}`,
|
|
20644
|
+
item.comment.body
|
|
20645
|
+
];
|
|
20646
|
+
}
|
|
20647
|
+
if (item.kind === "event") {
|
|
20648
|
+
return [
|
|
20649
|
+
`event \xB7 ${item.actor?.login ?? "unknown"} \xB7 ${item.createdAt}`,
|
|
20650
|
+
`${item.event}${item.detail ? `: ${item.detail}` : ""}`
|
|
20651
|
+
];
|
|
20652
|
+
}
|
|
20653
|
+
return [
|
|
20654
|
+
`${item.kind} \xB7 ${item.author?.login ?? (item.kind === "commit" ? item.authorName : undefined) ?? "unknown"} \xB7 ${item.createdAt}`,
|
|
20655
|
+
item.kind === "commit" ? `${item.sha} ${item.message}` : `${item.state}${item.body ? `: ${item.body}` : ""}`
|
|
20656
|
+
];
|
|
20657
|
+
});
|
|
20658
|
+
if (!items.length)
|
|
20659
|
+
lines.push("No timeline entries.");
|
|
20660
|
+
if (result.truncated)
|
|
20661
|
+
lines.push("Timeline truncated; more entries are available on GitHub.");
|
|
20662
|
+
return lines;
|
|
20663
|
+
}
|
|
20664
|
+
function fileLines(result) {
|
|
20665
|
+
const lines = keyValues([
|
|
20666
|
+
["number", result.number],
|
|
20667
|
+
["head", result.headSha]
|
|
20668
|
+
]);
|
|
20669
|
+
if (Array.isArray(result.files)) {
|
|
20670
|
+
lines.push(...table(["FILE", "STATUS", "+", "-"], result.files.map((file) => [
|
|
20671
|
+
file.path,
|
|
20672
|
+
file.status,
|
|
20673
|
+
String(file.additions),
|
|
20674
|
+
String(file.deletions)
|
|
20675
|
+
])));
|
|
20676
|
+
} else {
|
|
20677
|
+
const file = result.file;
|
|
20678
|
+
lines.push(`${file.path} (${file.status}, +${file.additions} -${file.deletions})`);
|
|
20679
|
+
if (!file.patchAvailable)
|
|
20680
|
+
lines.push("Patch unavailable; view this file on GitHub.");
|
|
20681
|
+
else
|
|
20682
|
+
for (const hunk of file.hunks) {
|
|
20683
|
+
lines.push(hunk.header, ...hunk.lines.map((line) => `${line.type === "add" ? "+" : line.type === "remove" ? "-" : line.type === "no-newline" ? "\\" : " "}${line.text}`));
|
|
20684
|
+
}
|
|
20685
|
+
}
|
|
20686
|
+
for (const thread of result.threads ?? []) {
|
|
20687
|
+
lines.push(`Thread ${thread.id} \xB7 ${thread.path}:${thread.line ?? "outdated"} \xB7 ${thread.isResolved ? "resolved" : "unresolved"}${thread.isOutdated ? " \xB7 outdated" : ""}`);
|
|
20688
|
+
for (const comment of thread.comments)
|
|
20689
|
+
lines.push(`${comment.author?.login ?? "unknown"} \xB7 ${comment.createdAt}: ${comment.body}`);
|
|
20690
|
+
if (thread.commentsTruncated)
|
|
20691
|
+
lines.push("Thread comments truncated; see GitHub for more.");
|
|
20692
|
+
}
|
|
20693
|
+
if (result.truncated)
|
|
20694
|
+
lines.push("File list truncated; more files are available on GitHub.");
|
|
20695
|
+
return lines;
|
|
20696
|
+
}
|
|
20697
|
+
async function reviewComments(context, parsed) {
|
|
20698
|
+
const file = parsed.options["comments-file"];
|
|
20699
|
+
if (file === undefined)
|
|
20700
|
+
return;
|
|
20701
|
+
const text = await readTextSource(context.io, {
|
|
20702
|
+
file,
|
|
20703
|
+
label: "Review comments",
|
|
20704
|
+
maxBytes: PUBLIC_PR_BODY_MAX_CHARS * 4,
|
|
20705
|
+
names: { file: "--comments-file", stdin: "" },
|
|
20706
|
+
required: true
|
|
20707
|
+
});
|
|
20708
|
+
let comments;
|
|
20709
|
+
try {
|
|
20710
|
+
comments = JSON.parse(text);
|
|
20711
|
+
} catch {
|
|
20712
|
+
throw new CliError("invalid-input", "--comments-file must contain a JSON array of inline comments");
|
|
20713
|
+
}
|
|
20714
|
+
if (!Array.isArray(comments) || comments.length > PUBLIC_PR_REVIEW_COMMENTS_MAX)
|
|
20715
|
+
throw new CliError("invalid-input", `--comments-file must contain at most ${PUBLIC_PR_REVIEW_COMMENTS_MAX} inline comments in a JSON array`);
|
|
20716
|
+
return comments;
|
|
20717
|
+
}
|
|
20718
|
+
var pullRequestCommands = [
|
|
20719
|
+
read("get", "pr.get", "Read the pull request attached to an environment's branch.", [], () => ({}), pullRequestLines),
|
|
20720
|
+
{
|
|
20721
|
+
path: ["pr", "list"],
|
|
20722
|
+
action: "pr.list",
|
|
20723
|
+
summary: "List a project's pull requests from GitHub.",
|
|
20724
|
+
positionals: [{ name: "project", required: true }],
|
|
20725
|
+
options: [
|
|
20726
|
+
{
|
|
20727
|
+
name: "filter",
|
|
20728
|
+
kind: "string",
|
|
20729
|
+
valueName: "FILTER",
|
|
20730
|
+
description: "open (default), mine, review-requested or closed."
|
|
20731
|
+
},
|
|
20732
|
+
{ name: "query", kind: "string", valueName: "TEXT", description: "Search text." }
|
|
20733
|
+
],
|
|
20734
|
+
idOutput: "pull request URLs",
|
|
20735
|
+
async run(context, parsed) {
|
|
20736
|
+
const session = await context.session();
|
|
20737
|
+
const filter = stringOption(parsed.options, "filter");
|
|
20738
|
+
const query = stringOption(parsed.options, "query");
|
|
20739
|
+
const result = await session.read("pr.list", {
|
|
20740
|
+
projectId: parsed.positionals.project,
|
|
20741
|
+
...filter !== undefined ? { filter: oneOf(filter, PUBLIC_PR_LIST_FILTERS, "--filter") } : {},
|
|
20742
|
+
...query !== undefined ? { query } : {}
|
|
20743
|
+
});
|
|
20744
|
+
return {
|
|
20745
|
+
action: "pr.list",
|
|
20746
|
+
result,
|
|
20747
|
+
connection: session.identity,
|
|
20748
|
+
ids: result.pullRequests.map((pullRequest) => pullRequest.htmlUrl),
|
|
20749
|
+
human: table(["NUMBER", "STATE", "TITLE"], result.pullRequests.map((pullRequest) => [
|
|
20750
|
+
String(pullRequest.number),
|
|
20751
|
+
pullRequest.state,
|
|
20752
|
+
pullRequest.title
|
|
20753
|
+
]))
|
|
20754
|
+
};
|
|
20755
|
+
}
|
|
20756
|
+
},
|
|
20757
|
+
read("timeline", "pr.timeline", "Read a pull request's commits, comments, reviews and events.", [], () => ({}), timelineLines),
|
|
20758
|
+
read("files", "pr.files", "Read a pull request's changed files and review threads; with --path, one file's diff.", [
|
|
20759
|
+
{
|
|
20760
|
+
name: "path",
|
|
20761
|
+
kind: "string",
|
|
20762
|
+
valueName: "PATH",
|
|
20763
|
+
description: "Only this file, with hunks."
|
|
20764
|
+
}
|
|
20765
|
+
], (parsed) => {
|
|
20766
|
+
const path = stringOption(parsed.options, "path");
|
|
20767
|
+
return path !== undefined ? { path } : {};
|
|
20768
|
+
}, fileLines),
|
|
20769
|
+
read("options", "pr.options", "Read the repository's labels and assignable users.", [], () => ({}), (result) => keyValues([
|
|
20770
|
+
[
|
|
20771
|
+
"labels",
|
|
20772
|
+
result.labels.map((label) => label.name).join(", ")
|
|
20773
|
+
],
|
|
20774
|
+
[
|
|
20775
|
+
"assignable users",
|
|
20776
|
+
result.assignableUsers.map((user) => user.login).join(", ")
|
|
20777
|
+
]
|
|
20778
|
+
])),
|
|
20779
|
+
change("merge", "pr.merge", "Merge the environment's pull request into its target branch (squash by default).", [
|
|
20780
|
+
{
|
|
20781
|
+
name: "method",
|
|
20782
|
+
kind: "string",
|
|
20783
|
+
valueName: "METHOD",
|
|
20784
|
+
description: "squash (default), merge or rebase."
|
|
20785
|
+
},
|
|
20786
|
+
{ name: "keep-branch", kind: "boolean", description: "Keep the head branch." },
|
|
20787
|
+
{
|
|
20788
|
+
name: "cleanup",
|
|
20789
|
+
kind: "boolean",
|
|
20790
|
+
description: "Merge and Cleanup: also delete the environment once merged."
|
|
20791
|
+
},
|
|
20792
|
+
{
|
|
20793
|
+
name: "expected-head-sha",
|
|
20794
|
+
kind: "string",
|
|
20795
|
+
valueName: "SHA",
|
|
20796
|
+
description: "Refuse if the branch has moved from this commit."
|
|
20797
|
+
},
|
|
20798
|
+
YES_OPTION
|
|
20799
|
+
], (_context, parsed) => {
|
|
20800
|
+
requireYes(parsed, "Merging cannot be undone");
|
|
20801
|
+
const method = stringOption(parsed.options, "method");
|
|
20802
|
+
const expectedHeadSha = stringOption(parsed.options, "expected-head-sha");
|
|
20803
|
+
return {
|
|
20804
|
+
...method !== undefined ? { method: oneOf(method, PUBLIC_PR_MERGE_METHODS, "--method") } : {},
|
|
20805
|
+
...parsed.options["keep-branch"] === true ? { deleteBranch: false } : {},
|
|
20806
|
+
...parsed.options.cleanup === true ? { cleanup: true } : {},
|
|
20807
|
+
...expectedHeadSha !== undefined ? { expectedHeadSha } : {}
|
|
20808
|
+
};
|
|
20809
|
+
}, { inputFields: { "keep-branch": "deleteBranch", yes: null } }),
|
|
20810
|
+
change("close", "pr.close", "Close the environment's pull request without merging.", [YES_OPTION], (_context, parsed) => {
|
|
20811
|
+
requireYes(parsed, "This closes the pull request");
|
|
20812
|
+
return {};
|
|
20813
|
+
}, { inputFields: { yes: null } }),
|
|
20814
|
+
change("reopen", "pr.reopen", "Reopen the environment's closed pull request.", [], () => ({})),
|
|
20815
|
+
change("ready", "pr.ready", "Mark the environment's draft pull request ready.", [], () => ({})),
|
|
20816
|
+
change("draft", "pr.draft", "Convert the environment's pull request to a draft.", [], () => ({})),
|
|
20817
|
+
change("comment", "pr.comment", "Add a conversation comment to the environment's pull request.", BODY_OPTIONS, async (context, parsed) => ({ body: await bodyText(context, parsed, true) }), { inputFields: { "body-file": "body" } }),
|
|
20818
|
+
change("comment-edit", "pr.comment.edit", "Edit a conversation comment on the environment's pull request.", [
|
|
20819
|
+
{ name: "comment-id", kind: "string", valueName: "ID", description: "From pr get." },
|
|
20820
|
+
...BODY_OPTIONS
|
|
20821
|
+
], async (context, parsed) => ({
|
|
20822
|
+
commentId: requiredInteger(parsed, "comment-id"),
|
|
20823
|
+
body: await bodyText(context, parsed, true)
|
|
20824
|
+
}), { inputFields: { "body-file": "body" } }),
|
|
20825
|
+
change("review", "pr.review", "Submit a review: approve, request changes or comment.", [
|
|
20826
|
+
{
|
|
20827
|
+
name: "event",
|
|
20828
|
+
kind: "string",
|
|
20829
|
+
valueName: "EVENT",
|
|
20830
|
+
description: "APPROVE, REQUEST_CHANGES or COMMENT."
|
|
20831
|
+
},
|
|
20832
|
+
...BODY_OPTIONS,
|
|
20833
|
+
{ name: "commit-id", kind: "string", valueName: "SHA", description: "The commit reviewed." },
|
|
20834
|
+
{
|
|
20835
|
+
name: "comments-file",
|
|
20836
|
+
kind: "string",
|
|
20837
|
+
valueName: "PATH",
|
|
20838
|
+
description: "JSON array of inline comments (path, line, side, body and optional startLine)."
|
|
20839
|
+
},
|
|
20840
|
+
YES_OPTION
|
|
20841
|
+
], async (context, parsed) => {
|
|
20842
|
+
requireYes(parsed, "This submits a review as your GitHub user");
|
|
20843
|
+
const body = await bodyText(context, parsed, false);
|
|
20844
|
+
const commitId = stringOption(parsed.options, "commit-id");
|
|
20845
|
+
const comments = await reviewComments(context, parsed);
|
|
20846
|
+
return {
|
|
20847
|
+
...comments !== undefined ? { comments } : {},
|
|
20848
|
+
event: oneOf(parsed.options.event, PUBLIC_PR_REVIEW_EVENTS, "--event"),
|
|
20849
|
+
...body !== undefined ? { body } : {},
|
|
20850
|
+
...commitId !== undefined ? { commitId } : {}
|
|
20851
|
+
};
|
|
20852
|
+
}, { inputFields: { "body-file": "body", "comments-file": "comments", yes: null } }),
|
|
20853
|
+
change("review-comment", "pr.review.comment", "Add one inline review comment to the environment's pull request.", [
|
|
20854
|
+
...INLINE_OPTIONS,
|
|
20855
|
+
...BODY_OPTIONS,
|
|
20856
|
+
{
|
|
20857
|
+
name: "commit-id",
|
|
20858
|
+
kind: "string",
|
|
20859
|
+
valueName: "SHA",
|
|
20860
|
+
description: "Omitted: the current head."
|
|
20861
|
+
}
|
|
20862
|
+
], async (context, parsed) => {
|
|
20863
|
+
const startLine = integerOption(parsed, "start-line");
|
|
20864
|
+
const commitId = stringOption(parsed.options, "commit-id");
|
|
20865
|
+
return {
|
|
20866
|
+
path: requiredOption(parsed, "path"),
|
|
20867
|
+
line: requiredInteger(parsed, "line"),
|
|
20868
|
+
side: oneOf(parsed.options.side, PUBLIC_PR_SIDES, "--side"),
|
|
20869
|
+
...startLine !== undefined ? { startLine } : {},
|
|
20870
|
+
body: await bodyText(context, parsed, true),
|
|
20871
|
+
...commitId !== undefined ? { commitId } : {}
|
|
20872
|
+
};
|
|
20873
|
+
}, { inputFields: { "body-file": "body" } }),
|
|
20874
|
+
change("thread-reply", "pr.thread.reply", "Reply to a review thread on the environment's pull request.", [
|
|
20875
|
+
{ name: "thread-id", kind: "string", valueName: "ID", description: "From pr files." },
|
|
20876
|
+
...BODY_OPTIONS
|
|
20877
|
+
], async (context, parsed) => ({
|
|
20878
|
+
threadId: requiredOption(parsed, "thread-id"),
|
|
20879
|
+
body: await bodyText(context, parsed, true)
|
|
20880
|
+
}), { inputFields: { "body-file": "body" } }),
|
|
20881
|
+
change("thread-resolve", "pr.thread.resolve", "Resolve a review thread on the environment's pull request.", [
|
|
20882
|
+
{ name: "thread-id", kind: "string", valueName: "ID", description: "From pr files." },
|
|
20883
|
+
{ name: "unresolve", kind: "boolean", description: "Unresolve it instead." }
|
|
20884
|
+
], (_context, parsed) => ({
|
|
20885
|
+
threadId: requiredOption(parsed, "thread-id"),
|
|
20886
|
+
...parsed.options.unresolve === true ? { resolved: false } : {}
|
|
20887
|
+
}), { inputFields: { unresolve: "resolved" } }),
|
|
20888
|
+
change("file-viewed", "pr.file.viewed", "Mark a changed file as viewed on the environment's pull request.", [
|
|
20889
|
+
{ name: "path", kind: "string", valueName: "PATH", description: "Changed file." },
|
|
20890
|
+
{ name: "unviewed", kind: "boolean", description: "Clear it instead." }
|
|
20891
|
+
], (_context, parsed) => ({
|
|
20892
|
+
path: requiredOption(parsed, "path"),
|
|
20893
|
+
...parsed.options.unviewed === true ? { viewed: false } : {}
|
|
20894
|
+
}), { inputFields: { unviewed: "viewed" } }),
|
|
20895
|
+
change("reviewers", "pr.reviewers", "Request or remove reviewers on the environment's pull request.", [
|
|
20896
|
+
{
|
|
20897
|
+
name: "add",
|
|
20898
|
+
kind: "string",
|
|
20899
|
+
valueName: "LIST",
|
|
20900
|
+
description: "Comma-separated logins or org/team-slugs."
|
|
20901
|
+
},
|
|
20902
|
+
{
|
|
20903
|
+
name: "remove",
|
|
20904
|
+
kind: "string",
|
|
20905
|
+
valueName: "LIST",
|
|
20906
|
+
description: "Comma-separated logins or org/team-slugs."
|
|
20907
|
+
}
|
|
20908
|
+
], (_context, parsed) => ({
|
|
20909
|
+
add: repeatedList(parsed.options.add),
|
|
20910
|
+
remove: repeatedList(parsed.options.remove)
|
|
20911
|
+
})),
|
|
20912
|
+
change("label", "pr.label", "Add or remove one label on the environment's pull request.", [
|
|
20913
|
+
{ name: "name", kind: "string", valueName: "LABEL", description: "Label name." },
|
|
20914
|
+
{ name: "remove", kind: "boolean", description: "Remove it instead." }
|
|
20915
|
+
], (_context, parsed) => ({
|
|
20916
|
+
name: requiredOption(parsed, "name"),
|
|
20917
|
+
...parsed.options.remove === true ? { present: false } : {}
|
|
20918
|
+
}), { inputFields: { remove: "present" } })
|
|
20919
|
+
];
|
|
20920
|
+
|
|
19270
20921
|
// src/client/help.ts
|
|
19271
20922
|
var GROUP_SUMMARIES = {
|
|
19272
20923
|
connection: "Select and verify the backend this client talks to.",
|
|
@@ -19628,8 +21279,10 @@ var CLIENT_COMMANDS = [
|
|
|
19628
21279
|
...projectCommands,
|
|
19629
21280
|
...environmentCommands,
|
|
19630
21281
|
...sessionCommands,
|
|
21282
|
+
...sessionGoalCommands,
|
|
19631
21283
|
...changesCommands,
|
|
19632
21284
|
...workflowCommands,
|
|
21285
|
+
...pullRequestCommands,
|
|
19633
21286
|
...buildCommands,
|
|
19634
21287
|
...graphCommands,
|
|
19635
21288
|
...attentionCommands,
|