@h-rig/server 0.0.6-alpha.1 → 0.0.6-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/index.js +216 -33
- package/dist/src/server-helpers/github-auth-store.js +52 -0
- package/dist/src/server-helpers/http-router.js +427 -22
- package/dist/src/server-helpers/issue-analysis.js +4 -1
- package/dist/src/server-helpers/run-mutations.js +91 -14
- package/dist/src/server-helpers/snapshot-orchestrator.js +1 -1
- package/dist/src/server-helpers/snapshot-service.js +1 -1
- package/dist/src/server-helpers/ws-router.js +3 -3
- package/dist/src/server.js +216 -33
- package/package.json +4 -4
|
@@ -459,6 +459,7 @@ import {
|
|
|
459
459
|
} from "@rig/runtime/control-plane/tasks/source-lifecycle";
|
|
460
460
|
|
|
461
461
|
// packages/server/src/server-helpers/github-auth-store.ts
|
|
462
|
+
import { randomBytes } from "crypto";
|
|
462
463
|
import { chmodSync, existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync, writeFileSync as writeFileSync3 } from "fs";
|
|
463
464
|
import { resolve as resolve6 } from "path";
|
|
464
465
|
function cleanString(value) {
|
|
@@ -472,6 +473,24 @@ function cleanScopes(value) {
|
|
|
472
473
|
return clean ? [clean] : [];
|
|
473
474
|
});
|
|
474
475
|
}
|
|
476
|
+
function parseApiSessions(value) {
|
|
477
|
+
if (!Array.isArray(value))
|
|
478
|
+
return [];
|
|
479
|
+
return value.flatMap((entry) => {
|
|
480
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
481
|
+
return [];
|
|
482
|
+
const record = entry;
|
|
483
|
+
const token = cleanString(record.token);
|
|
484
|
+
if (!token)
|
|
485
|
+
return [];
|
|
486
|
+
return [{
|
|
487
|
+
token,
|
|
488
|
+
login: cleanString(record.login),
|
|
489
|
+
userId: cleanString(record.userId),
|
|
490
|
+
createdAt: cleanString(record.createdAt) ?? undefined
|
|
491
|
+
}];
|
|
492
|
+
});
|
|
493
|
+
}
|
|
475
494
|
function readStoredAuth(stateFile) {
|
|
476
495
|
if (!existsSync3(stateFile))
|
|
477
496
|
return {};
|
|
@@ -485,6 +504,7 @@ function readStoredAuth(stateFile) {
|
|
|
485
504
|
selectedRepo: cleanString(parsed.selectedRepo),
|
|
486
505
|
tokenSource: parsed.tokenSource === "oauth-device" || parsed.tokenSource === "manual-token" || parsed.tokenSource === "env" ? parsed.tokenSource : undefined,
|
|
487
506
|
pendingDevice: parsePendingDevice(parsed.pendingDevice),
|
|
507
|
+
apiSessions: parseApiSessions(parsed.apiSessions),
|
|
488
508
|
updatedAt: cleanString(parsed.updatedAt) ?? undefined
|
|
489
509
|
};
|
|
490
510
|
} catch {
|
|
@@ -503,6 +523,9 @@ function parsePendingDevice(value) {
|
|
|
503
523
|
return null;
|
|
504
524
|
return { pollId, deviceCode, expiresAt, intervalSeconds };
|
|
505
525
|
}
|
|
526
|
+
function newApiSessionToken() {
|
|
527
|
+
return `rig_${randomBytes(32).toString("base64url")}`;
|
|
528
|
+
}
|
|
506
529
|
function writeStoredAuth(stateFile, payload) {
|
|
507
530
|
mkdirSync3(resolve6(stateFile, ".."), { recursive: true });
|
|
508
531
|
writeFileSync3(stateFile, `${JSON.stringify(payload, null, 2)}
|
|
@@ -545,9 +568,38 @@ function createGitHubAuthStore(projectRoot) {
|
|
|
545
568
|
scopes: input.scopes ?? [],
|
|
546
569
|
selectedRepo: input.selectedRepo ?? previous.selectedRepo ?? null,
|
|
547
570
|
pendingDevice: null,
|
|
571
|
+
apiSessions: previous.apiSessions ?? [],
|
|
548
572
|
updatedAt: new Date().toISOString()
|
|
549
573
|
});
|
|
550
574
|
},
|
|
575
|
+
createApiSession() {
|
|
576
|
+
const previous = readStoredAuth(stateFile);
|
|
577
|
+
const token = newApiSessionToken();
|
|
578
|
+
const session = {
|
|
579
|
+
token,
|
|
580
|
+
login: cleanString(previous.login),
|
|
581
|
+
userId: cleanString(previous.userId),
|
|
582
|
+
createdAt: new Date().toISOString()
|
|
583
|
+
};
|
|
584
|
+
writeStoredAuth(stateFile, {
|
|
585
|
+
...previous,
|
|
586
|
+
apiSessions: [...(previous.apiSessions ?? []).slice(-9), session],
|
|
587
|
+
updatedAt: new Date().toISOString()
|
|
588
|
+
});
|
|
589
|
+
return { token, login: session.login ?? null, userId: session.userId ?? null };
|
|
590
|
+
},
|
|
591
|
+
readApiSession(token) {
|
|
592
|
+
const clean = cleanString(token);
|
|
593
|
+
if (!clean)
|
|
594
|
+
return null;
|
|
595
|
+
const previous = readStoredAuth(stateFile);
|
|
596
|
+
const session = (previous.apiSessions ?? []).find((candidate) => candidate.token === clean);
|
|
597
|
+
return session ? { login: cleanString(session.login), userId: cleanString(session.userId) } : null;
|
|
598
|
+
},
|
|
599
|
+
copyToProjectRoot(projectRoot2) {
|
|
600
|
+
const targetFile = resolveGitHubAuthStateFile(projectRoot2);
|
|
601
|
+
writeStoredAuth(targetFile, readStoredAuth(stateFile));
|
|
602
|
+
},
|
|
551
603
|
savePendingDevice(input) {
|
|
552
604
|
const previous = readStoredAuth(stateFile);
|
|
553
605
|
writeStoredAuth(stateFile, {
|
|
@@ -775,10 +827,10 @@ function extractGitHubIssueNodeId(task) {
|
|
|
775
827
|
}
|
|
776
828
|
|
|
777
829
|
// packages/server/src/server-helpers/http-router.ts
|
|
778
|
-
var
|
|
830
|
+
var RIG_CONFIG_PACKAGE_DIST_TAG = "latest";
|
|
779
831
|
var RIG_CONFIG_DEV_DEPENDENCIES = {
|
|
780
|
-
"@rig/core": `npm:@h-rig/core@${
|
|
781
|
-
"@rig/standard-plugin": `npm:@h-rig/standard-plugin@${
|
|
832
|
+
"@rig/core": `npm:@h-rig/core@${RIG_CONFIG_PACKAGE_DIST_TAG}`,
|
|
833
|
+
"@rig/standard-plugin": `npm:@h-rig/standard-plugin@${RIG_CONFIG_PACKAGE_DIST_TAG}`
|
|
782
834
|
};
|
|
783
835
|
|
|
784
836
|
// packages/server/src/server-helpers/ws-router.ts
|
|
@@ -995,7 +1047,7 @@ async function readWorkspaceTasks(projectRoot) {
|
|
|
995
1047
|
description: normalizeString(entry.description),
|
|
996
1048
|
acceptanceCriteria: normalizeString(entry.acceptance_criteria),
|
|
997
1049
|
status: normalizedStatus ?? "unknown",
|
|
998
|
-
sourceStatus: normalizedStatus ? null : rawStatus,
|
|
1050
|
+
sourceStatus: normalizedStatus && rawStatus !== normalizedStatus ? rawStatus : normalizedStatus ? null : rawStatus,
|
|
999
1051
|
priority: typeof entry.priority === "number" ? entry.priority : typeof entry.priority === "string" ? Number(entry.priority) : null,
|
|
1000
1052
|
issueType: normalizeString(entry.issue_type),
|
|
1001
1053
|
role: normalizeString(config.role) ?? null,
|
|
@@ -1120,15 +1172,36 @@ async function syncProjectStatusForRunLifecycle(projectRoot, run, status, config
|
|
|
1120
1172
|
if (!run.taskId)
|
|
1121
1173
|
return;
|
|
1122
1174
|
const issueNodeId = extractGitHubIssueNodeId(runSourceTaskIdentity(run));
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1175
|
+
try {
|
|
1176
|
+
const result = await syncGitHubProjectStatusForTaskUpdate({
|
|
1177
|
+
taskId: run.taskId,
|
|
1178
|
+
status,
|
|
1179
|
+
issueNodeId,
|
|
1180
|
+
token: createGitHubAuthStore(projectRoot).readToken(),
|
|
1181
|
+
config
|
|
1182
|
+
});
|
|
1183
|
+
if (!result.synced && result.reason !== "project-sync-disabled") {
|
|
1184
|
+
appendRunLogEntry(projectRoot, run.runId, {
|
|
1185
|
+
id: `log:${run.runId}:github-project-sync:${status}`,
|
|
1186
|
+
title: "GitHub Project sync skipped",
|
|
1187
|
+
detail: `Project status sync for ${run.taskId} could not run: ${result.reason}.`,
|
|
1188
|
+
tone: "warn",
|
|
1189
|
+
status: "running",
|
|
1190
|
+
createdAt: new Date().toISOString(),
|
|
1191
|
+
payload: { reason: result.reason, issueNodeId }
|
|
1192
|
+
});
|
|
1193
|
+
}
|
|
1194
|
+
} catch (error) {
|
|
1195
|
+
appendRunLogEntry(projectRoot, run.runId, {
|
|
1196
|
+
id: `log:${run.runId}:github-project-sync-error:${status}`,
|
|
1197
|
+
title: "GitHub Project sync failed",
|
|
1198
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
1199
|
+
tone: "error",
|
|
1200
|
+
status: "running",
|
|
1201
|
+
createdAt: new Date().toISOString(),
|
|
1202
|
+
payload: { issueNodeId }
|
|
1203
|
+
});
|
|
1204
|
+
}
|
|
1132
1205
|
}
|
|
1133
1206
|
async function autoAssignRunIssue(projectRoot, run) {
|
|
1134
1207
|
if (!run.taskId)
|
|
@@ -1402,7 +1475,11 @@ async function startLocalRun(state, runId, options) {
|
|
|
1402
1475
|
RIG_SERVER_INTERNAL_EXEC: "1",
|
|
1403
1476
|
...serverUrl ? { RIG_SERVER_URL: serverUrl } : {},
|
|
1404
1477
|
...bridgeAuthToken ? { RIG_AUTH_TOKEN: bridgeAuthToken } : {},
|
|
1405
|
-
...bridgeGitHubToken ? {
|
|
1478
|
+
...bridgeGitHubToken ? {
|
|
1479
|
+
RIG_GITHUB_TOKEN: bridgeGitHubToken,
|
|
1480
|
+
GITHUB_TOKEN: bridgeGitHubToken,
|
|
1481
|
+
GH_TOKEN: bridgeGitHubToken
|
|
1482
|
+
} : {}
|
|
1406
1483
|
},
|
|
1407
1484
|
stdio: ["ignore", "pipe", "pipe"]
|
|
1408
1485
|
});
|
|
@@ -699,7 +699,7 @@ async function buildRigSnapshotPayload(projectRoot, readers) {
|
|
|
699
699
|
const taskSummaries = (await readers.readWorkspaceTasks(projectRoot)).map((task) => ({
|
|
700
700
|
...toTaskSummary(workspace.id, {
|
|
701
701
|
...task,
|
|
702
|
-
status: queuedTaskIds.has(task.id) && (task.status === "open" || task.status === "ready" || task.status === "draft") ? "queued" : task.status
|
|
702
|
+
status: queuedTaskIds.has(task.id) && (task.status === "open" || task.status === "ready" || task.status === "draft" || task.status === "failed" || task.sourceStatus === "failed") ? "queued" : task.status
|
|
703
703
|
}),
|
|
704
704
|
graphId: graph.id
|
|
705
705
|
}));
|
|
@@ -711,7 +711,7 @@ async function buildRigSnapshotPayload(projectRoot, readers) {
|
|
|
711
711
|
const taskSummaries = (await readers.readWorkspaceTasks(projectRoot)).map((task) => ({
|
|
712
712
|
...toTaskSummary(workspace.id, {
|
|
713
713
|
...task,
|
|
714
|
-
status: queuedTaskIds.has(task.id) && (task.status === "open" || task.status === "ready" || task.status === "draft") ? "queued" : task.status
|
|
714
|
+
status: queuedTaskIds.has(task.id) && (task.status === "open" || task.status === "ready" || task.status === "draft" || task.status === "failed" || task.sourceStatus === "failed") ? "queued" : task.status
|
|
715
715
|
}),
|
|
716
716
|
graphId: graph.id
|
|
717
717
|
}));
|
|
@@ -437,10 +437,10 @@ import {
|
|
|
437
437
|
buildTaskRunLifecycleComment as buildTaskRunLifecycleComment2,
|
|
438
438
|
updateConfiguredTaskSourceTask as updateConfiguredTaskSourceTask2
|
|
439
439
|
} from "@rig/runtime/control-plane/tasks/source-lifecycle";
|
|
440
|
-
var
|
|
440
|
+
var RIG_CONFIG_PACKAGE_DIST_TAG = "latest";
|
|
441
441
|
var RIG_CONFIG_DEV_DEPENDENCIES = {
|
|
442
|
-
"@rig/core": `npm:@h-rig/core@${
|
|
443
|
-
"@rig/standard-plugin": `npm:@h-rig/standard-plugin@${
|
|
442
|
+
"@rig/core": `npm:@h-rig/core@${RIG_CONFIG_PACKAGE_DIST_TAG}`,
|
|
443
|
+
"@rig/standard-plugin": `npm:@h-rig/standard-plugin@${RIG_CONFIG_PACKAGE_DIST_TAG}`
|
|
444
444
|
};
|
|
445
445
|
|
|
446
446
|
// packages/server/src/server-helpers/inspector-jobs.ts
|
package/dist/src/server.js
CHANGED
|
@@ -1786,7 +1786,7 @@ async function buildRigSnapshotPayload(projectRoot, readers) {
|
|
|
1786
1786
|
const taskSummaries = (await readers.readWorkspaceTasks(projectRoot)).map((task) => ({
|
|
1787
1787
|
...toTaskSummary(workspace.id, {
|
|
1788
1788
|
...task,
|
|
1789
|
-
status: queuedTaskIds.has(task.id) && (task.status === "open" || task.status === "ready" || task.status === "draft") ? "queued" : task.status
|
|
1789
|
+
status: queuedTaskIds.has(task.id) && (task.status === "open" || task.status === "ready" || task.status === "draft" || task.status === "failed" || task.sourceStatus === "failed") ? "queued" : task.status
|
|
1790
1790
|
}),
|
|
1791
1791
|
graphId: graph.id
|
|
1792
1792
|
}));
|
|
@@ -2463,7 +2463,10 @@ function createPiIssueAnalyzer(input = {}) {
|
|
|
2463
2463
|
const runCommand = input.runCommand ?? createDefaultPiIssueAnalysisCommandRunner();
|
|
2464
2464
|
return async ({ prompt }) => {
|
|
2465
2465
|
const args = ["--print", "--mode", "json", "--no-session"];
|
|
2466
|
-
const
|
|
2466
|
+
const provider = input.provider?.trim() || process.env.RIG_ISSUE_ANALYSIS_PROVIDER?.trim() || process.env.RIG_PI_PROVIDER?.trim();
|
|
2467
|
+
const model = input.model?.trim() || process.env.RIG_ISSUE_ANALYSIS_MODEL?.trim() || process.env.RIG_PI_MODEL?.trim() || "openai-codex/gpt-5.5";
|
|
2468
|
+
if (provider)
|
|
2469
|
+
args.push("--provider", provider);
|
|
2467
2470
|
if (model)
|
|
2468
2471
|
args.push("--model", model);
|
|
2469
2472
|
args.push(prompt);
|
|
@@ -3304,6 +3307,7 @@ function summarizeRunValidationFailure(projectRoot, run) {
|
|
|
3304
3307
|
}
|
|
3305
3308
|
|
|
3306
3309
|
// packages/server/src/server-helpers/github-auth-store.ts
|
|
3310
|
+
import { randomBytes } from "crypto";
|
|
3307
3311
|
import { chmodSync, existsSync as existsSync6, mkdirSync as mkdirSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync5 } from "fs";
|
|
3308
3312
|
import { resolve as resolve13 } from "path";
|
|
3309
3313
|
function cleanString(value) {
|
|
@@ -3317,6 +3321,24 @@ function cleanScopes(value) {
|
|
|
3317
3321
|
return clean ? [clean] : [];
|
|
3318
3322
|
});
|
|
3319
3323
|
}
|
|
3324
|
+
function parseApiSessions(value) {
|
|
3325
|
+
if (!Array.isArray(value))
|
|
3326
|
+
return [];
|
|
3327
|
+
return value.flatMap((entry) => {
|
|
3328
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
3329
|
+
return [];
|
|
3330
|
+
const record = entry;
|
|
3331
|
+
const token = cleanString(record.token);
|
|
3332
|
+
if (!token)
|
|
3333
|
+
return [];
|
|
3334
|
+
return [{
|
|
3335
|
+
token,
|
|
3336
|
+
login: cleanString(record.login),
|
|
3337
|
+
userId: cleanString(record.userId),
|
|
3338
|
+
createdAt: cleanString(record.createdAt) ?? undefined
|
|
3339
|
+
}];
|
|
3340
|
+
});
|
|
3341
|
+
}
|
|
3320
3342
|
function readStoredAuth(stateFile) {
|
|
3321
3343
|
if (!existsSync6(stateFile))
|
|
3322
3344
|
return {};
|
|
@@ -3330,6 +3352,7 @@ function readStoredAuth(stateFile) {
|
|
|
3330
3352
|
selectedRepo: cleanString(parsed.selectedRepo),
|
|
3331
3353
|
tokenSource: parsed.tokenSource === "oauth-device" || parsed.tokenSource === "manual-token" || parsed.tokenSource === "env" ? parsed.tokenSource : undefined,
|
|
3332
3354
|
pendingDevice: parsePendingDevice(parsed.pendingDevice),
|
|
3355
|
+
apiSessions: parseApiSessions(parsed.apiSessions),
|
|
3333
3356
|
updatedAt: cleanString(parsed.updatedAt) ?? undefined
|
|
3334
3357
|
};
|
|
3335
3358
|
} catch {
|
|
@@ -3348,6 +3371,9 @@ function parsePendingDevice(value) {
|
|
|
3348
3371
|
return null;
|
|
3349
3372
|
return { pollId, deviceCode, expiresAt, intervalSeconds };
|
|
3350
3373
|
}
|
|
3374
|
+
function newApiSessionToken() {
|
|
3375
|
+
return `rig_${randomBytes(32).toString("base64url")}`;
|
|
3376
|
+
}
|
|
3351
3377
|
function writeStoredAuth(stateFile, payload) {
|
|
3352
3378
|
mkdirSync6(resolve13(stateFile, ".."), { recursive: true });
|
|
3353
3379
|
writeFileSync5(stateFile, `${JSON.stringify(payload, null, 2)}
|
|
@@ -3390,9 +3416,38 @@ function createGitHubAuthStore(projectRoot) {
|
|
|
3390
3416
|
scopes: input.scopes ?? [],
|
|
3391
3417
|
selectedRepo: input.selectedRepo ?? previous.selectedRepo ?? null,
|
|
3392
3418
|
pendingDevice: null,
|
|
3419
|
+
apiSessions: previous.apiSessions ?? [],
|
|
3393
3420
|
updatedAt: new Date().toISOString()
|
|
3394
3421
|
});
|
|
3395
3422
|
},
|
|
3423
|
+
createApiSession() {
|
|
3424
|
+
const previous = readStoredAuth(stateFile);
|
|
3425
|
+
const token = newApiSessionToken();
|
|
3426
|
+
const session = {
|
|
3427
|
+
token,
|
|
3428
|
+
login: cleanString(previous.login),
|
|
3429
|
+
userId: cleanString(previous.userId),
|
|
3430
|
+
createdAt: new Date().toISOString()
|
|
3431
|
+
};
|
|
3432
|
+
writeStoredAuth(stateFile, {
|
|
3433
|
+
...previous,
|
|
3434
|
+
apiSessions: [...(previous.apiSessions ?? []).slice(-9), session],
|
|
3435
|
+
updatedAt: new Date().toISOString()
|
|
3436
|
+
});
|
|
3437
|
+
return { token, login: session.login ?? null, userId: session.userId ?? null };
|
|
3438
|
+
},
|
|
3439
|
+
readApiSession(token) {
|
|
3440
|
+
const clean = cleanString(token);
|
|
3441
|
+
if (!clean)
|
|
3442
|
+
return null;
|
|
3443
|
+
const previous = readStoredAuth(stateFile);
|
|
3444
|
+
const session = (previous.apiSessions ?? []).find((candidate) => candidate.token === clean);
|
|
3445
|
+
return session ? { login: cleanString(session.login), userId: cleanString(session.userId) } : null;
|
|
3446
|
+
},
|
|
3447
|
+
copyToProjectRoot(projectRoot2) {
|
|
3448
|
+
const targetFile = resolveGitHubAuthStateFile(projectRoot2);
|
|
3449
|
+
writeStoredAuth(targetFile, readStoredAuth(stateFile));
|
|
3450
|
+
},
|
|
3396
3451
|
savePendingDevice(input) {
|
|
3397
3452
|
const previous = readStoredAuth(stateFile);
|
|
3398
3453
|
writeStoredAuth(stateFile, {
|
|
@@ -3690,15 +3745,36 @@ async function syncProjectStatusForRunLifecycle(projectRoot, run, status, config
|
|
|
3690
3745
|
if (!run.taskId)
|
|
3691
3746
|
return;
|
|
3692
3747
|
const issueNodeId = extractGitHubIssueNodeId(runSourceTaskIdentity(run));
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3748
|
+
try {
|
|
3749
|
+
const result = await syncGitHubProjectStatusForTaskUpdate({
|
|
3750
|
+
taskId: run.taskId,
|
|
3751
|
+
status,
|
|
3752
|
+
issueNodeId,
|
|
3753
|
+
token: createGitHubAuthStore(projectRoot).readToken(),
|
|
3754
|
+
config
|
|
3755
|
+
});
|
|
3756
|
+
if (!result.synced && result.reason !== "project-sync-disabled") {
|
|
3757
|
+
appendRunLogEntry(projectRoot, run.runId, {
|
|
3758
|
+
id: `log:${run.runId}:github-project-sync:${status}`,
|
|
3759
|
+
title: "GitHub Project sync skipped",
|
|
3760
|
+
detail: `Project status sync for ${run.taskId} could not run: ${result.reason}.`,
|
|
3761
|
+
tone: "warn",
|
|
3762
|
+
status: "running",
|
|
3763
|
+
createdAt: new Date().toISOString(),
|
|
3764
|
+
payload: { reason: result.reason, issueNodeId }
|
|
3765
|
+
});
|
|
3766
|
+
}
|
|
3767
|
+
} catch (error) {
|
|
3768
|
+
appendRunLogEntry(projectRoot, run.runId, {
|
|
3769
|
+
id: `log:${run.runId}:github-project-sync-error:${status}`,
|
|
3770
|
+
title: "GitHub Project sync failed",
|
|
3771
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
3772
|
+
tone: "error",
|
|
3773
|
+
status: "running",
|
|
3774
|
+
createdAt: new Date().toISOString(),
|
|
3775
|
+
payload: { issueNodeId }
|
|
3776
|
+
});
|
|
3777
|
+
}
|
|
3702
3778
|
}
|
|
3703
3779
|
async function autoAssignRunIssue(projectRoot, run) {
|
|
3704
3780
|
if (!run.taskId)
|
|
@@ -3972,7 +4048,11 @@ async function startLocalRun(state, runId, options) {
|
|
|
3972
4048
|
RIG_SERVER_INTERNAL_EXEC: "1",
|
|
3973
4049
|
...serverUrl ? { RIG_SERVER_URL: serverUrl } : {},
|
|
3974
4050
|
...bridgeAuthToken ? { RIG_AUTH_TOKEN: bridgeAuthToken } : {},
|
|
3975
|
-
...bridgeGitHubToken ? {
|
|
4051
|
+
...bridgeGitHubToken ? {
|
|
4052
|
+
RIG_GITHUB_TOKEN: bridgeGitHubToken,
|
|
4053
|
+
GITHUB_TOKEN: bridgeGitHubToken,
|
|
4054
|
+
GH_TOKEN: bridgeGitHubToken
|
|
4055
|
+
} : {}
|
|
3976
4056
|
},
|
|
3977
4057
|
stdio: ["ignore", "pipe", "pipe"]
|
|
3978
4058
|
});
|
|
@@ -4886,10 +4966,10 @@ function normalizeCommit(value) {
|
|
|
4886
4966
|
function asPlainRecord(value) {
|
|
4887
4967
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
4888
4968
|
}
|
|
4889
|
-
var
|
|
4969
|
+
var RIG_CONFIG_PACKAGE_DIST_TAG = "latest";
|
|
4890
4970
|
var RIG_CONFIG_DEV_DEPENDENCIES = {
|
|
4891
|
-
"@rig/core": `npm:@h-rig/core@${
|
|
4892
|
-
"@rig/standard-plugin": `npm:@h-rig/standard-plugin@${
|
|
4971
|
+
"@rig/core": `npm:@h-rig/core@${RIG_CONFIG_PACKAGE_DIST_TAG}`,
|
|
4972
|
+
"@rig/standard-plugin": `npm:@h-rig/standard-plugin@${RIG_CONFIG_PACKAGE_DIST_TAG}`
|
|
4893
4973
|
};
|
|
4894
4974
|
function repoParts(repoSlug) {
|
|
4895
4975
|
const [owner, repo] = repoSlug.split("/");
|
|
@@ -5243,13 +5323,13 @@ function bearerTokenFromRequest(req) {
|
|
|
5243
5323
|
function isLoopbackRequest(req) {
|
|
5244
5324
|
try {
|
|
5245
5325
|
const hostname = new URL(req.url).hostname.toLowerCase();
|
|
5246
|
-
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname === "[::1]";
|
|
5326
|
+
return hostname === "localhost" || hostname === "rig.local" || hostname.endsWith(".localhost") || hostname === "127.0.0.1" || hostname === "::1" || hostname === "[::1]";
|
|
5247
5327
|
} catch {
|
|
5248
5328
|
return false;
|
|
5249
5329
|
}
|
|
5250
5330
|
}
|
|
5251
5331
|
function isPublicRigAuthBootstrapRoute(pathname) {
|
|
5252
|
-
return pathname === "/" || pathname === "/health" || pathname === "/api/health" || pathname === "/api/
|
|
5332
|
+
return pathname === "/" || pathname === "/health" || pathname === "/api/health" || pathname === "/api/github/auth/status" || pathname === "/api/github/auth/token" || pathname === "/api/github/auth/device/start" || pathname === "/api/github/auth/device/poll";
|
|
5253
5333
|
}
|
|
5254
5334
|
function normalizePrMode(value) {
|
|
5255
5335
|
const mode = normalizeString(value);
|
|
@@ -5262,6 +5342,10 @@ function authorizeRigHttpRequest(input) {
|
|
|
5262
5342
|
const bearer = bearerTokenFromRequest(input.req);
|
|
5263
5343
|
const store = createGitHubAuthStore(input.projectRoot);
|
|
5264
5344
|
const storedToken = store.readToken();
|
|
5345
|
+
const session = bearer ? store.readApiSession(bearer) : null;
|
|
5346
|
+
if (session) {
|
|
5347
|
+
return { authorized: true, actor: session.login ?? "github-operator", reason: "github-session" };
|
|
5348
|
+
}
|
|
5265
5349
|
if (bearer && storedToken && bearer === storedToken) {
|
|
5266
5350
|
const status = store.status({ oauthConfigured: Boolean(process.env.RIG_GITHUB_OAUTH_CLIENT_ID?.trim()) });
|
|
5267
5351
|
return { authorized: true, actor: status.login ?? "github-operator", reason: "github-token" };
|
|
@@ -5269,8 +5353,11 @@ function authorizeRigHttpRequest(input) {
|
|
|
5269
5353
|
if (isPublicRigAuthBootstrapRoute(input.pathname)) {
|
|
5270
5354
|
return { authorized: true, actor: null, reason: "public-bootstrap" };
|
|
5271
5355
|
}
|
|
5272
|
-
if (!input.serverAuthToken && !storedToken
|
|
5273
|
-
|
|
5356
|
+
if (!input.serverAuthToken && !storedToken) {
|
|
5357
|
+
if (isLoopbackRequest(input.req)) {
|
|
5358
|
+
return { authorized: true, actor: null, reason: "loopback-dev-no-auth" };
|
|
5359
|
+
}
|
|
5360
|
+
return { authorized: false, actor: null, reason: "auth-required" };
|
|
5274
5361
|
}
|
|
5275
5362
|
return { authorized: false, actor: null, reason: storedToken ? "github-token-required" : "auth-required" };
|
|
5276
5363
|
}
|
|
@@ -5501,7 +5588,7 @@ function selectNextWorkspaceTask(projectRoot, tasks) {
|
|
|
5501
5588
|
if (runnable.length === 0)
|
|
5502
5589
|
return null;
|
|
5503
5590
|
const queue = readQueueState(projectRoot);
|
|
5504
|
-
const queueRank = new Map(queue.map((entry, index) => [entry.taskId, { score: entry.score, position: index }]));
|
|
5591
|
+
const queueRank = new Map(queue.map((entry, index) => [String(entry.taskId), { score: entry.score, position: index }]));
|
|
5505
5592
|
return runnable.toSorted((left, right) => {
|
|
5506
5593
|
const leftId = taskIdOf(left) ?? "";
|
|
5507
5594
|
const rightId = taskIdOf(right) ?? "";
|
|
@@ -5541,6 +5628,27 @@ function filterWorkspaceTasks(projectRoot, tasks, searchParams) {
|
|
|
5541
5628
|
}
|
|
5542
5629
|
return filtered;
|
|
5543
5630
|
}
|
|
5631
|
+
function issueAnalysisTargetFor(source) {
|
|
5632
|
+
if (!source)
|
|
5633
|
+
return null;
|
|
5634
|
+
const candidate = source;
|
|
5635
|
+
if (typeof candidate.updateTask !== "function")
|
|
5636
|
+
return null;
|
|
5637
|
+
return {
|
|
5638
|
+
...typeof candidate.get === "function" ? { get: candidate.get.bind(candidate) } : {},
|
|
5639
|
+
updateTask: candidate.updateTask.bind(candidate),
|
|
5640
|
+
...typeof candidate.addLabels === "function" ? { addLabels: candidate.addLabels.bind(candidate) } : {},
|
|
5641
|
+
...typeof candidate.removeLabels === "function" ? { removeLabels: candidate.removeLabels.bind(candidate) } : {},
|
|
5642
|
+
...typeof candidate.createIssue === "function" ? { createIssue: candidate.createIssue.bind(candidate) } : {}
|
|
5643
|
+
};
|
|
5644
|
+
}
|
|
5645
|
+
function uniqueStringList(value) {
|
|
5646
|
+
const raw = Array.isArray(value) ? value : typeof value === "string" ? [value] : [];
|
|
5647
|
+
return [...new Set(raw.map((entry) => String(entry).trim()).filter(Boolean))];
|
|
5648
|
+
}
|
|
5649
|
+
function taskRecordId(task) {
|
|
5650
|
+
return String(task.id ?? "");
|
|
5651
|
+
}
|
|
5544
5652
|
function redactRemoteEndpoint(endpoint) {
|
|
5545
5653
|
const { token, ...rest } = endpoint;
|
|
5546
5654
|
return {
|
|
@@ -5627,7 +5735,7 @@ function createRigServerFetch(state, deps) {
|
|
|
5627
5735
|
}
|
|
5628
5736
|
const isLinearWebhook = url.pathname === "/api/linear/webhook" && req.method === "POST";
|
|
5629
5737
|
const isInspectorStream = url.pathname === "/api/inspector/stream" && req.method === "GET";
|
|
5630
|
-
const legacyAuthorizedHttpRequest = isInspectorStream && isAuthorizedInspectorStreamRequest(req, state.authToken) ? true : deps.isAuthorizedHttpRequest(req, state.authToken);
|
|
5738
|
+
const legacyAuthorizedHttpRequest = Boolean(state.authToken) && (isInspectorStream && isAuthorizedInspectorStreamRequest(req, state.authToken) ? true : deps.isAuthorizedHttpRequest(req, state.authToken));
|
|
5631
5739
|
const requestAuth = authorizeRigHttpRequest({
|
|
5632
5740
|
req,
|
|
5633
5741
|
pathname: url.pathname,
|
|
@@ -5883,6 +5991,67 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
|
|
|
5883
5991
|
note: "GitHub issue lifecycle labels are created on demand by the configured task source when supported."
|
|
5884
5992
|
});
|
|
5885
5993
|
}
|
|
5994
|
+
if (url.pathname === "/api/workspace/issue-analysis/run" && req.method === "POST") {
|
|
5995
|
+
const body = await deps.readJsonBody(req);
|
|
5996
|
+
const ids = uniqueStringList(body.ids ?? body.id);
|
|
5997
|
+
const analyzeAll = deps.isTruthyQuery(String(body.all ?? ""));
|
|
5998
|
+
if (ids.length === 0 && !analyzeAll) {
|
|
5999
|
+
return deps.badRequest("ids is required unless all=true");
|
|
6000
|
+
}
|
|
6001
|
+
const ctx = await getCachedPluginHostContext(state.projectRoot);
|
|
6002
|
+
const [source] = ctx?.taskSourceRegistry.list() ?? [];
|
|
6003
|
+
const target = issueAnalysisTargetFor(source);
|
|
6004
|
+
if (!source || !target) {
|
|
6005
|
+
return deps.badRequest("Configured task source does not support issue-analysis writeback");
|
|
6006
|
+
}
|
|
6007
|
+
const allTasks = [...await source.list()];
|
|
6008
|
+
const issues = analyzeAll ? allTasks.slice(0, Math.max(1, Math.min(25, Number(body.limit ?? 25) || 25))) : (await Promise.all(ids.map(async (id) => {
|
|
6009
|
+
const cached = allTasks.find((task) => taskRecordId(task) === id);
|
|
6010
|
+
if (cached)
|
|
6011
|
+
return cached;
|
|
6012
|
+
return typeof source.get === "function" ? await source.get(id) : undefined;
|
|
6013
|
+
}))).filter((task) => Boolean(task));
|
|
6014
|
+
if (issues.length === 0) {
|
|
6015
|
+
return deps.jsonResponse({ ok: false, error: "No matching issues found for issue analysis", ids }, 404);
|
|
6016
|
+
}
|
|
6017
|
+
const config = ctx?.config && typeof ctx.config === "object" ? ctx.config : {};
|
|
6018
|
+
const issueAnalysis = config.issueAnalysis && typeof config.issueAnalysis === "object" ? config.issueAnalysis : {};
|
|
6019
|
+
const runtime = config.runtime && typeof config.runtime === "object" ? config.runtime : {};
|
|
6020
|
+
const model = normalizeString(issueAnalysis.model) ?? normalizeString(runtime.model);
|
|
6021
|
+
const service = createIssueAnalysisService({
|
|
6022
|
+
analyzer: createPiIssueAnalyzer({
|
|
6023
|
+
...model ? { model } : {},
|
|
6024
|
+
env: { RIG_PROJECT_ROOT: state.projectRoot }
|
|
6025
|
+
}),
|
|
6026
|
+
writeBack: createIssueAnalysisWriteBack({ target })
|
|
6027
|
+
});
|
|
6028
|
+
const reason = normalizeString(body.reason) ?? "http-issue-analysis";
|
|
6029
|
+
let results;
|
|
6030
|
+
try {
|
|
6031
|
+
results = await service.analyze(issues, { reason, neighbors: ids.length > 0 ? issues : allTasks });
|
|
6032
|
+
} catch (error) {
|
|
6033
|
+
return deps.jsonResponse({
|
|
6034
|
+
ok: false,
|
|
6035
|
+
error: `Issue analysis failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
6036
|
+
reason,
|
|
6037
|
+
ids: issues.map((issue) => issue.id)
|
|
6038
|
+
}, 502);
|
|
6039
|
+
}
|
|
6040
|
+
deps.snapshotService.invalidate("issue-analysis-http-run");
|
|
6041
|
+
await state.taskProjectionReconciler?.tick("issue-analysis-http-run").catch(() => {
|
|
6042
|
+
return;
|
|
6043
|
+
});
|
|
6044
|
+
deps.broadcastSnapshotInvalidation(state, "issue-analysis-http-run");
|
|
6045
|
+
return deps.jsonResponse({
|
|
6046
|
+
ok: true,
|
|
6047
|
+
reason,
|
|
6048
|
+
analyzed: results.map((entry) => ({
|
|
6049
|
+
id: entry.issue.id,
|
|
6050
|
+
title: entry.issue.title ?? null,
|
|
6051
|
+
result: entry.result
|
|
6052
|
+
}))
|
|
6053
|
+
});
|
|
6054
|
+
}
|
|
5886
6055
|
if (url.pathname === "/api/server/status") {
|
|
5887
6056
|
const config = buildProjectConfigStatus(state.projectRoot);
|
|
5888
6057
|
const taskSource = await buildTaskSourceStatus(state, config);
|
|
@@ -6023,6 +6192,9 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
|
|
|
6023
6192
|
}
|
|
6024
6193
|
const normalizedRoot = resolve18(requestedRoot);
|
|
6025
6194
|
const exists = existsSync11(normalizedRoot);
|
|
6195
|
+
if (exists) {
|
|
6196
|
+
createGitHubAuthStore(state.projectRoot).copyToProjectRoot(normalizedRoot);
|
|
6197
|
+
}
|
|
6026
6198
|
const control = buildServerControlStatus();
|
|
6027
6199
|
const switchCommand = process.env.RIG_PROJECT_ROOT_SWITCH_COMMAND?.trim();
|
|
6028
6200
|
if (!exists) {
|
|
@@ -6111,21 +6283,30 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
|
|
|
6111
6283
|
const body = await deps.readJsonBody(req);
|
|
6112
6284
|
const token = normalizeString(body.token);
|
|
6113
6285
|
const selectedRepo = normalizeString(body.selectedRepo);
|
|
6286
|
+
const requestedProjectRoot = normalizeString(body.projectRoot);
|
|
6114
6287
|
if (!token) {
|
|
6115
6288
|
return deps.badRequest("token is required");
|
|
6116
6289
|
}
|
|
6117
6290
|
try {
|
|
6118
6291
|
const user = await fetchGitHubUserInfo(token);
|
|
6119
|
-
const
|
|
6120
|
-
|
|
6121
|
-
|
|
6122
|
-
|
|
6123
|
-
|
|
6124
|
-
|
|
6125
|
-
|
|
6126
|
-
|
|
6127
|
-
|
|
6128
|
-
|
|
6292
|
+
const storeRoots = [
|
|
6293
|
+
state.projectRoot,
|
|
6294
|
+
...requestedProjectRoot && isAbsolute3(requestedProjectRoot) && existsSync11(resolve18(requestedProjectRoot)) ? [resolve18(requestedProjectRoot)] : []
|
|
6295
|
+
].filter((root, index, roots) => roots.indexOf(root) === index);
|
|
6296
|
+
const stores = storeRoots.map((root) => createGitHubAuthStore(root));
|
|
6297
|
+
for (const store2 of stores) {
|
|
6298
|
+
store2.saveToken({
|
|
6299
|
+
token,
|
|
6300
|
+
tokenSource: "manual-token",
|
|
6301
|
+
login: user.login,
|
|
6302
|
+
userId: user.userId,
|
|
6303
|
+
scopes: user.scopes,
|
|
6304
|
+
selectedRepo
|
|
6305
|
+
});
|
|
6306
|
+
}
|
|
6307
|
+
const store = stores[stores.length - 1] ?? createGitHubAuthStore(state.projectRoot);
|
|
6308
|
+
const apiSession = store.createApiSession();
|
|
6309
|
+
return deps.jsonResponse({ ok: true, ...store.status({ oauthConfigured: Boolean(process.env.RIG_GITHUB_OAUTH_CLIENT_ID?.trim()) }), apiSessionToken: apiSession.token });
|
|
6129
6310
|
} catch (error) {
|
|
6130
6311
|
const message = error instanceof Error ? error.message : String(error);
|
|
6131
6312
|
return deps.jsonResponse({ ok: false, error: message }, 400);
|
|
@@ -6193,7 +6374,8 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
|
|
|
6193
6374
|
const token = result.payload.access_token;
|
|
6194
6375
|
const user = await fetchGitHubUserInfo(token);
|
|
6195
6376
|
store.saveToken({ token, tokenSource: "oauth-device", login: user.login, userId: user.userId, scopes: user.scopes });
|
|
6196
|
-
|
|
6377
|
+
const apiSession = store.createApiSession();
|
|
6378
|
+
return deps.jsonResponse({ ok: true, status: "signed-in", ...store.status({ oauthConfigured: true }), apiSessionToken: apiSession.token });
|
|
6197
6379
|
}
|
|
6198
6380
|
if (url.pathname === "/api/github/repo/probe" && req.method === "POST") {
|
|
6199
6381
|
const body = await deps.readJsonBody(req);
|
|
@@ -11999,7 +12181,7 @@ async function readWorkspaceTasks(projectRoot) {
|
|
|
11999
12181
|
description: normalizeString(entry.description),
|
|
12000
12182
|
acceptanceCriteria: normalizeString(entry.acceptance_criteria),
|
|
12001
12183
|
status: normalizedStatus ?? "unknown",
|
|
12002
|
-
sourceStatus: normalizedStatus ? null : rawStatus,
|
|
12184
|
+
sourceStatus: normalizedStatus && rawStatus !== normalizedStatus ? rawStatus : normalizedStatus ? null : rawStatus,
|
|
12003
12185
|
priority: typeof entry.priority === "number" ? entry.priority : typeof entry.priority === "string" ? Number(entry.priority) : null,
|
|
12004
12186
|
issueType: normalizeString(entry.issue_type),
|
|
12005
12187
|
role: normalizeString(config.role) ?? null,
|
|
@@ -12479,6 +12661,7 @@ async function createRigServer(options, projectRoot = resolveProjectRoot()) {
|
|
|
12479
12661
|
const server = Bun.serve({
|
|
12480
12662
|
hostname: options.host,
|
|
12481
12663
|
port: options.port,
|
|
12664
|
+
idleTimeout: Math.max(10, Math.min(255, Number.parseInt(process.env.RIG_SERVER_IDLE_TIMEOUT_SECONDS || "255", 10) || 255)),
|
|
12482
12665
|
fetch: (req, server2) => createRigServerFetch2(state)(req, server2),
|
|
12483
12666
|
websocket: {
|
|
12484
12667
|
open(ws) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@h-rig/server",
|
|
3
|
-
"version": "0.0.6-alpha.
|
|
3
|
+
"version": "0.0.6-alpha.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Rig package",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
"rig-server": "./dist/src/server.js"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@rig/contracts": "npm:@h-rig/contracts@0.0.6-alpha.
|
|
29
|
-
"@rig/core": "npm:@h-rig/core@0.0.6-alpha.
|
|
30
|
-
"@rig/runtime": "npm:@h-rig/runtime@0.0.6-alpha.
|
|
28
|
+
"@rig/contracts": "npm:@h-rig/contracts@0.0.6-alpha.3",
|
|
29
|
+
"@rig/core": "npm:@h-rig/core@0.0.6-alpha.3",
|
|
30
|
+
"@rig/runtime": "npm:@h-rig/runtime@0.0.6-alpha.3",
|
|
31
31
|
"effect": "4.0.0-beta.78"
|
|
32
32
|
}
|
|
33
33
|
}
|