@h-rig/server 0.0.6-alpha.1 → 0.0.6-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/index.js +124 -31
- package/dist/src/server-helpers/github-auth-store.js +52 -0
- package/dist/src/server-helpers/http-router.js +92 -20
- package/dist/src/server-helpers/run-mutations.js +86 -13
- 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 +124 -31
- package/package.json +4 -4
package/dist/src/index.js
CHANGED
|
@@ -2293,7 +2293,7 @@ async function buildRigSnapshotPayload(projectRoot, readers) {
|
|
|
2293
2293
|
const taskSummaries = (await readers.readWorkspaceTasks(projectRoot)).map((task) => ({
|
|
2294
2294
|
...toTaskSummary(workspace.id, {
|
|
2295
2295
|
...task,
|
|
2296
|
-
status: queuedTaskIds.has(task.id) && (task.status === "open" || task.status === "ready" || task.status === "draft") ? "queued" : task.status
|
|
2296
|
+
status: queuedTaskIds.has(task.id) && (task.status === "open" || task.status === "ready" || task.status === "draft" || task.status === "failed" || task.sourceStatus === "failed") ? "queued" : task.status
|
|
2297
2297
|
}),
|
|
2298
2298
|
graphId: graph.id
|
|
2299
2299
|
}));
|
|
@@ -3811,6 +3811,7 @@ function summarizeRunValidationFailure(projectRoot, run) {
|
|
|
3811
3811
|
}
|
|
3812
3812
|
|
|
3813
3813
|
// packages/server/src/server-helpers/github-auth-store.ts
|
|
3814
|
+
import { randomBytes } from "crypto";
|
|
3814
3815
|
import { chmodSync, existsSync as existsSync6, mkdirSync as mkdirSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync5 } from "fs";
|
|
3815
3816
|
import { resolve as resolve13 } from "path";
|
|
3816
3817
|
function cleanString(value) {
|
|
@@ -3824,6 +3825,24 @@ function cleanScopes(value) {
|
|
|
3824
3825
|
return clean ? [clean] : [];
|
|
3825
3826
|
});
|
|
3826
3827
|
}
|
|
3828
|
+
function parseApiSessions(value) {
|
|
3829
|
+
if (!Array.isArray(value))
|
|
3830
|
+
return [];
|
|
3831
|
+
return value.flatMap((entry) => {
|
|
3832
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
3833
|
+
return [];
|
|
3834
|
+
const record = entry;
|
|
3835
|
+
const token = cleanString(record.token);
|
|
3836
|
+
if (!token)
|
|
3837
|
+
return [];
|
|
3838
|
+
return [{
|
|
3839
|
+
token,
|
|
3840
|
+
login: cleanString(record.login),
|
|
3841
|
+
userId: cleanString(record.userId),
|
|
3842
|
+
createdAt: cleanString(record.createdAt) ?? undefined
|
|
3843
|
+
}];
|
|
3844
|
+
});
|
|
3845
|
+
}
|
|
3827
3846
|
function readStoredAuth(stateFile) {
|
|
3828
3847
|
if (!existsSync6(stateFile))
|
|
3829
3848
|
return {};
|
|
@@ -3837,6 +3856,7 @@ function readStoredAuth(stateFile) {
|
|
|
3837
3856
|
selectedRepo: cleanString(parsed.selectedRepo),
|
|
3838
3857
|
tokenSource: parsed.tokenSource === "oauth-device" || parsed.tokenSource === "manual-token" || parsed.tokenSource === "env" ? parsed.tokenSource : undefined,
|
|
3839
3858
|
pendingDevice: parsePendingDevice(parsed.pendingDevice),
|
|
3859
|
+
apiSessions: parseApiSessions(parsed.apiSessions),
|
|
3840
3860
|
updatedAt: cleanString(parsed.updatedAt) ?? undefined
|
|
3841
3861
|
};
|
|
3842
3862
|
} catch {
|
|
@@ -3855,6 +3875,9 @@ function parsePendingDevice(value) {
|
|
|
3855
3875
|
return null;
|
|
3856
3876
|
return { pollId, deviceCode, expiresAt, intervalSeconds };
|
|
3857
3877
|
}
|
|
3878
|
+
function newApiSessionToken() {
|
|
3879
|
+
return `rig_${randomBytes(32).toString("base64url")}`;
|
|
3880
|
+
}
|
|
3858
3881
|
function writeStoredAuth(stateFile, payload) {
|
|
3859
3882
|
mkdirSync6(resolve13(stateFile, ".."), { recursive: true });
|
|
3860
3883
|
writeFileSync5(stateFile, `${JSON.stringify(payload, null, 2)}
|
|
@@ -3897,9 +3920,38 @@ function createGitHubAuthStore(projectRoot) {
|
|
|
3897
3920
|
scopes: input.scopes ?? [],
|
|
3898
3921
|
selectedRepo: input.selectedRepo ?? previous.selectedRepo ?? null,
|
|
3899
3922
|
pendingDevice: null,
|
|
3923
|
+
apiSessions: previous.apiSessions ?? [],
|
|
3900
3924
|
updatedAt: new Date().toISOString()
|
|
3901
3925
|
});
|
|
3902
3926
|
},
|
|
3927
|
+
createApiSession() {
|
|
3928
|
+
const previous = readStoredAuth(stateFile);
|
|
3929
|
+
const token = newApiSessionToken();
|
|
3930
|
+
const session = {
|
|
3931
|
+
token,
|
|
3932
|
+
login: cleanString(previous.login),
|
|
3933
|
+
userId: cleanString(previous.userId),
|
|
3934
|
+
createdAt: new Date().toISOString()
|
|
3935
|
+
};
|
|
3936
|
+
writeStoredAuth(stateFile, {
|
|
3937
|
+
...previous,
|
|
3938
|
+
apiSessions: [...(previous.apiSessions ?? []).slice(-9), session],
|
|
3939
|
+
updatedAt: new Date().toISOString()
|
|
3940
|
+
});
|
|
3941
|
+
return { token, login: session.login ?? null, userId: session.userId ?? null };
|
|
3942
|
+
},
|
|
3943
|
+
readApiSession(token) {
|
|
3944
|
+
const clean = cleanString(token);
|
|
3945
|
+
if (!clean)
|
|
3946
|
+
return null;
|
|
3947
|
+
const previous = readStoredAuth(stateFile);
|
|
3948
|
+
const session = (previous.apiSessions ?? []).find((candidate) => candidate.token === clean);
|
|
3949
|
+
return session ? { login: cleanString(session.login), userId: cleanString(session.userId) } : null;
|
|
3950
|
+
},
|
|
3951
|
+
copyToProjectRoot(projectRoot2) {
|
|
3952
|
+
const targetFile = resolveGitHubAuthStateFile(projectRoot2);
|
|
3953
|
+
writeStoredAuth(targetFile, readStoredAuth(stateFile));
|
|
3954
|
+
},
|
|
3903
3955
|
savePendingDevice(input) {
|
|
3904
3956
|
const previous = readStoredAuth(stateFile);
|
|
3905
3957
|
writeStoredAuth(stateFile, {
|
|
@@ -4197,15 +4249,36 @@ async function syncProjectStatusForRunLifecycle(projectRoot, run, status, config
|
|
|
4197
4249
|
if (!run.taskId)
|
|
4198
4250
|
return;
|
|
4199
4251
|
const issueNodeId = extractGitHubIssueNodeId(runSourceTaskIdentity(run));
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4252
|
+
try {
|
|
4253
|
+
const result = await syncGitHubProjectStatusForTaskUpdate({
|
|
4254
|
+
taskId: run.taskId,
|
|
4255
|
+
status,
|
|
4256
|
+
issueNodeId,
|
|
4257
|
+
token: createGitHubAuthStore(projectRoot).readToken(),
|
|
4258
|
+
config
|
|
4259
|
+
});
|
|
4260
|
+
if (!result.synced && result.reason !== "project-sync-disabled") {
|
|
4261
|
+
appendRunLogEntry(projectRoot, run.runId, {
|
|
4262
|
+
id: `log:${run.runId}:github-project-sync:${status}`,
|
|
4263
|
+
title: "GitHub Project sync skipped",
|
|
4264
|
+
detail: `Project status sync for ${run.taskId} could not run: ${result.reason}.`,
|
|
4265
|
+
tone: "warn",
|
|
4266
|
+
status: "running",
|
|
4267
|
+
createdAt: new Date().toISOString(),
|
|
4268
|
+
payload: { reason: result.reason, issueNodeId }
|
|
4269
|
+
});
|
|
4270
|
+
}
|
|
4271
|
+
} catch (error) {
|
|
4272
|
+
appendRunLogEntry(projectRoot, run.runId, {
|
|
4273
|
+
id: `log:${run.runId}:github-project-sync-error:${status}`,
|
|
4274
|
+
title: "GitHub Project sync failed",
|
|
4275
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
4276
|
+
tone: "error",
|
|
4277
|
+
status: "running",
|
|
4278
|
+
createdAt: new Date().toISOString(),
|
|
4279
|
+
payload: { issueNodeId }
|
|
4280
|
+
});
|
|
4281
|
+
}
|
|
4209
4282
|
}
|
|
4210
4283
|
async function autoAssignRunIssue(projectRoot, run) {
|
|
4211
4284
|
if (!run.taskId)
|
|
@@ -5393,10 +5466,10 @@ function normalizeCommit(value) {
|
|
|
5393
5466
|
function asPlainRecord(value) {
|
|
5394
5467
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
5395
5468
|
}
|
|
5396
|
-
var
|
|
5469
|
+
var RIG_CONFIG_PACKAGE_DIST_TAG = "latest";
|
|
5397
5470
|
var RIG_CONFIG_DEV_DEPENDENCIES = {
|
|
5398
|
-
"@rig/core": `npm:@h-rig/core@${
|
|
5399
|
-
"@rig/standard-plugin": `npm:@h-rig/standard-plugin@${
|
|
5471
|
+
"@rig/core": `npm:@h-rig/core@${RIG_CONFIG_PACKAGE_DIST_TAG}`,
|
|
5472
|
+
"@rig/standard-plugin": `npm:@h-rig/standard-plugin@${RIG_CONFIG_PACKAGE_DIST_TAG}`
|
|
5400
5473
|
};
|
|
5401
5474
|
function repoParts(repoSlug) {
|
|
5402
5475
|
const [owner, repo] = repoSlug.split("/");
|
|
@@ -5750,13 +5823,13 @@ function bearerTokenFromRequest(req) {
|
|
|
5750
5823
|
function isLoopbackRequest(req) {
|
|
5751
5824
|
try {
|
|
5752
5825
|
const hostname = new URL(req.url).hostname.toLowerCase();
|
|
5753
|
-
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname === "[::1]";
|
|
5826
|
+
return hostname === "localhost" || hostname === "rig.local" || hostname.endsWith(".localhost") || hostname === "127.0.0.1" || hostname === "::1" || hostname === "[::1]";
|
|
5754
5827
|
} catch {
|
|
5755
5828
|
return false;
|
|
5756
5829
|
}
|
|
5757
5830
|
}
|
|
5758
5831
|
function isPublicRigAuthBootstrapRoute(pathname) {
|
|
5759
|
-
return pathname === "/" || pathname === "/health" || pathname === "/api/health" || pathname === "/api/
|
|
5832
|
+
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";
|
|
5760
5833
|
}
|
|
5761
5834
|
function normalizePrMode(value) {
|
|
5762
5835
|
const mode = normalizeString(value);
|
|
@@ -5769,6 +5842,10 @@ function authorizeRigHttpRequest(input) {
|
|
|
5769
5842
|
const bearer = bearerTokenFromRequest(input.req);
|
|
5770
5843
|
const store = createGitHubAuthStore(input.projectRoot);
|
|
5771
5844
|
const storedToken = store.readToken();
|
|
5845
|
+
const session = bearer ? store.readApiSession(bearer) : null;
|
|
5846
|
+
if (session) {
|
|
5847
|
+
return { authorized: true, actor: session.login ?? "github-operator", reason: "github-session" };
|
|
5848
|
+
}
|
|
5772
5849
|
if (bearer && storedToken && bearer === storedToken) {
|
|
5773
5850
|
const status = store.status({ oauthConfigured: Boolean(process.env.RIG_GITHUB_OAUTH_CLIENT_ID?.trim()) });
|
|
5774
5851
|
return { authorized: true, actor: status.login ?? "github-operator", reason: "github-token" };
|
|
@@ -5776,8 +5853,11 @@ function authorizeRigHttpRequest(input) {
|
|
|
5776
5853
|
if (isPublicRigAuthBootstrapRoute(input.pathname)) {
|
|
5777
5854
|
return { authorized: true, actor: null, reason: "public-bootstrap" };
|
|
5778
5855
|
}
|
|
5779
|
-
if (!input.serverAuthToken && !storedToken
|
|
5780
|
-
|
|
5856
|
+
if (!input.serverAuthToken && !storedToken) {
|
|
5857
|
+
if (isLoopbackRequest(input.req)) {
|
|
5858
|
+
return { authorized: true, actor: null, reason: "loopback-dev-no-auth" };
|
|
5859
|
+
}
|
|
5860
|
+
return { authorized: false, actor: null, reason: "auth-required" };
|
|
5781
5861
|
}
|
|
5782
5862
|
return { authorized: false, actor: null, reason: storedToken ? "github-token-required" : "auth-required" };
|
|
5783
5863
|
}
|
|
@@ -6008,7 +6088,7 @@ function selectNextWorkspaceTask(projectRoot, tasks) {
|
|
|
6008
6088
|
if (runnable.length === 0)
|
|
6009
6089
|
return null;
|
|
6010
6090
|
const queue = readQueueState(projectRoot);
|
|
6011
|
-
const queueRank = new Map(queue.map((entry, index) => [entry.taskId, { score: entry.score, position: index }]));
|
|
6091
|
+
const queueRank = new Map(queue.map((entry, index) => [String(entry.taskId), { score: entry.score, position: index }]));
|
|
6012
6092
|
return runnable.toSorted((left, right) => {
|
|
6013
6093
|
const leftId = taskIdOf(left) ?? "";
|
|
6014
6094
|
const rightId = taskIdOf(right) ?? "";
|
|
@@ -6134,7 +6214,7 @@ function createRigServerFetch(state, deps) {
|
|
|
6134
6214
|
}
|
|
6135
6215
|
const isLinearWebhook = url.pathname === "/api/linear/webhook" && req.method === "POST";
|
|
6136
6216
|
const isInspectorStream = url.pathname === "/api/inspector/stream" && req.method === "GET";
|
|
6137
|
-
const legacyAuthorizedHttpRequest = isInspectorStream && isAuthorizedInspectorStreamRequest(req, state.authToken) ? true : deps.isAuthorizedHttpRequest(req, state.authToken);
|
|
6217
|
+
const legacyAuthorizedHttpRequest = Boolean(state.authToken) && (isInspectorStream && isAuthorizedInspectorStreamRequest(req, state.authToken) ? true : deps.isAuthorizedHttpRequest(req, state.authToken));
|
|
6138
6218
|
const requestAuth = authorizeRigHttpRequest({
|
|
6139
6219
|
req,
|
|
6140
6220
|
pathname: url.pathname,
|
|
@@ -6530,6 +6610,9 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
|
|
|
6530
6610
|
}
|
|
6531
6611
|
const normalizedRoot = resolve18(requestedRoot);
|
|
6532
6612
|
const exists = existsSync11(normalizedRoot);
|
|
6613
|
+
if (exists) {
|
|
6614
|
+
createGitHubAuthStore(state.projectRoot).copyToProjectRoot(normalizedRoot);
|
|
6615
|
+
}
|
|
6533
6616
|
const control = buildServerControlStatus();
|
|
6534
6617
|
const switchCommand = process.env.RIG_PROJECT_ROOT_SWITCH_COMMAND?.trim();
|
|
6535
6618
|
if (!exists) {
|
|
@@ -6618,21 +6701,30 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
|
|
|
6618
6701
|
const body = await deps.readJsonBody(req);
|
|
6619
6702
|
const token = normalizeString(body.token);
|
|
6620
6703
|
const selectedRepo = normalizeString(body.selectedRepo);
|
|
6704
|
+
const requestedProjectRoot = normalizeString(body.projectRoot);
|
|
6621
6705
|
if (!token) {
|
|
6622
6706
|
return deps.badRequest("token is required");
|
|
6623
6707
|
}
|
|
6624
6708
|
try {
|
|
6625
6709
|
const user = await fetchGitHubUserInfo(token);
|
|
6626
|
-
const
|
|
6627
|
-
|
|
6628
|
-
|
|
6629
|
-
|
|
6630
|
-
|
|
6631
|
-
|
|
6632
|
-
|
|
6633
|
-
|
|
6634
|
-
|
|
6635
|
-
|
|
6710
|
+
const storeRoots = [
|
|
6711
|
+
state.projectRoot,
|
|
6712
|
+
...requestedProjectRoot && isAbsolute3(requestedProjectRoot) && existsSync11(resolve18(requestedProjectRoot)) ? [resolve18(requestedProjectRoot)] : []
|
|
6713
|
+
].filter((root, index, roots) => roots.indexOf(root) === index);
|
|
6714
|
+
const stores = storeRoots.map((root) => createGitHubAuthStore(root));
|
|
6715
|
+
for (const store2 of stores) {
|
|
6716
|
+
store2.saveToken({
|
|
6717
|
+
token,
|
|
6718
|
+
tokenSource: "manual-token",
|
|
6719
|
+
login: user.login,
|
|
6720
|
+
userId: user.userId,
|
|
6721
|
+
scopes: user.scopes,
|
|
6722
|
+
selectedRepo
|
|
6723
|
+
});
|
|
6724
|
+
}
|
|
6725
|
+
const store = stores[stores.length - 1] ?? createGitHubAuthStore(state.projectRoot);
|
|
6726
|
+
const apiSession = store.createApiSession();
|
|
6727
|
+
return deps.jsonResponse({ ok: true, ...store.status({ oauthConfigured: Boolean(process.env.RIG_GITHUB_OAUTH_CLIENT_ID?.trim()) }), apiSessionToken: apiSession.token });
|
|
6636
6728
|
} catch (error) {
|
|
6637
6729
|
const message = error instanceof Error ? error.message : String(error);
|
|
6638
6730
|
return deps.jsonResponse({ ok: false, error: message }, 400);
|
|
@@ -6700,7 +6792,8 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
|
|
|
6700
6792
|
const token = result.payload.access_token;
|
|
6701
6793
|
const user = await fetchGitHubUserInfo(token);
|
|
6702
6794
|
store.saveToken({ token, tokenSource: "oauth-device", login: user.login, userId: user.userId, scopes: user.scopes });
|
|
6703
|
-
|
|
6795
|
+
const apiSession = store.createApiSession();
|
|
6796
|
+
return deps.jsonResponse({ ok: true, status: "signed-in", ...store.status({ oauthConfigured: true }), apiSessionToken: apiSession.token });
|
|
6704
6797
|
}
|
|
6705
6798
|
if (url.pathname === "/api/github/repo/probe" && req.method === "POST") {
|
|
6706
6799
|
const body = await deps.readJsonBody(req);
|
|
@@ -12506,7 +12599,7 @@ async function readWorkspaceTasks(projectRoot) {
|
|
|
12506
12599
|
description: normalizeString(entry.description),
|
|
12507
12600
|
acceptanceCriteria: normalizeString(entry.acceptance_criteria),
|
|
12508
12601
|
status: normalizedStatus ?? "unknown",
|
|
12509
|
-
sourceStatus: normalizedStatus ? null : rawStatus,
|
|
12602
|
+
sourceStatus: normalizedStatus && rawStatus !== normalizedStatus ? rawStatus : normalizedStatus ? null : rawStatus,
|
|
12510
12603
|
priority: typeof entry.priority === "number" ? entry.priority : typeof entry.priority === "string" ? Number(entry.priority) : null,
|
|
12511
12604
|
issueType: normalizeString(entry.issue_type),
|
|
12512
12605
|
role: normalizeString(config.role) ?? null,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// packages/server/src/server-helpers/github-auth-store.ts
|
|
3
|
+
import { randomBytes } from "crypto";
|
|
3
4
|
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
4
5
|
import { resolve as resolve2 } from "path";
|
|
5
6
|
|
|
@@ -39,6 +40,24 @@ function cleanScopes(value) {
|
|
|
39
40
|
return clean ? [clean] : [];
|
|
40
41
|
});
|
|
41
42
|
}
|
|
43
|
+
function parseApiSessions(value) {
|
|
44
|
+
if (!Array.isArray(value))
|
|
45
|
+
return [];
|
|
46
|
+
return value.flatMap((entry) => {
|
|
47
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
48
|
+
return [];
|
|
49
|
+
const record = entry;
|
|
50
|
+
const token = cleanString(record.token);
|
|
51
|
+
if (!token)
|
|
52
|
+
return [];
|
|
53
|
+
return [{
|
|
54
|
+
token,
|
|
55
|
+
login: cleanString(record.login),
|
|
56
|
+
userId: cleanString(record.userId),
|
|
57
|
+
createdAt: cleanString(record.createdAt) ?? undefined
|
|
58
|
+
}];
|
|
59
|
+
});
|
|
60
|
+
}
|
|
42
61
|
function readStoredAuth(stateFile) {
|
|
43
62
|
if (!existsSync(stateFile))
|
|
44
63
|
return {};
|
|
@@ -52,6 +71,7 @@ function readStoredAuth(stateFile) {
|
|
|
52
71
|
selectedRepo: cleanString(parsed.selectedRepo),
|
|
53
72
|
tokenSource: parsed.tokenSource === "oauth-device" || parsed.tokenSource === "manual-token" || parsed.tokenSource === "env" ? parsed.tokenSource : undefined,
|
|
54
73
|
pendingDevice: parsePendingDevice(parsed.pendingDevice),
|
|
74
|
+
apiSessions: parseApiSessions(parsed.apiSessions),
|
|
55
75
|
updatedAt: cleanString(parsed.updatedAt) ?? undefined
|
|
56
76
|
};
|
|
57
77
|
} catch {
|
|
@@ -70,6 +90,9 @@ function parsePendingDevice(value) {
|
|
|
70
90
|
return null;
|
|
71
91
|
return { pollId, deviceCode, expiresAt, intervalSeconds };
|
|
72
92
|
}
|
|
93
|
+
function newApiSessionToken() {
|
|
94
|
+
return `rig_${randomBytes(32).toString("base64url")}`;
|
|
95
|
+
}
|
|
73
96
|
function writeStoredAuth(stateFile, payload) {
|
|
74
97
|
mkdirSync(resolve2(stateFile, ".."), { recursive: true });
|
|
75
98
|
writeFileSync(stateFile, `${JSON.stringify(payload, null, 2)}
|
|
@@ -112,9 +135,38 @@ function createGitHubAuthStore(projectRoot) {
|
|
|
112
135
|
scopes: input.scopes ?? [],
|
|
113
136
|
selectedRepo: input.selectedRepo ?? previous.selectedRepo ?? null,
|
|
114
137
|
pendingDevice: null,
|
|
138
|
+
apiSessions: previous.apiSessions ?? [],
|
|
115
139
|
updatedAt: new Date().toISOString()
|
|
116
140
|
});
|
|
117
141
|
},
|
|
142
|
+
createApiSession() {
|
|
143
|
+
const previous = readStoredAuth(stateFile);
|
|
144
|
+
const token = newApiSessionToken();
|
|
145
|
+
const session = {
|
|
146
|
+
token,
|
|
147
|
+
login: cleanString(previous.login),
|
|
148
|
+
userId: cleanString(previous.userId),
|
|
149
|
+
createdAt: new Date().toISOString()
|
|
150
|
+
};
|
|
151
|
+
writeStoredAuth(stateFile, {
|
|
152
|
+
...previous,
|
|
153
|
+
apiSessions: [...(previous.apiSessions ?? []).slice(-9), session],
|
|
154
|
+
updatedAt: new Date().toISOString()
|
|
155
|
+
});
|
|
156
|
+
return { token, login: session.login ?? null, userId: session.userId ?? null };
|
|
157
|
+
},
|
|
158
|
+
readApiSession(token) {
|
|
159
|
+
const clean = cleanString(token);
|
|
160
|
+
if (!clean)
|
|
161
|
+
return null;
|
|
162
|
+
const previous = readStoredAuth(stateFile);
|
|
163
|
+
const session = (previous.apiSessions ?? []).find((candidate) => candidate.token === clean);
|
|
164
|
+
return session ? { login: cleanString(session.login), userId: cleanString(session.userId) } : null;
|
|
165
|
+
},
|
|
166
|
+
copyToProjectRoot(projectRoot2) {
|
|
167
|
+
const targetFile = resolveGitHubAuthStateFile(projectRoot2);
|
|
168
|
+
writeStoredAuth(targetFile, readStoredAuth(stateFile));
|
|
169
|
+
},
|
|
118
170
|
savePendingDevice(input) {
|
|
119
171
|
const previous = readStoredAuth(stateFile);
|
|
120
172
|
writeStoredAuth(stateFile, {
|
|
@@ -535,6 +535,7 @@ import {
|
|
|
535
535
|
} from "@rig/runtime/control-plane/authority-files";
|
|
536
536
|
|
|
537
537
|
// packages/server/src/server-helpers/github-auth-store.ts
|
|
538
|
+
import { randomBytes } from "crypto";
|
|
538
539
|
import { chmodSync, existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
539
540
|
import { resolve as resolve7 } from "path";
|
|
540
541
|
function cleanString(value) {
|
|
@@ -548,6 +549,24 @@ function cleanScopes(value) {
|
|
|
548
549
|
return clean ? [clean] : [];
|
|
549
550
|
});
|
|
550
551
|
}
|
|
552
|
+
function parseApiSessions(value) {
|
|
553
|
+
if (!Array.isArray(value))
|
|
554
|
+
return [];
|
|
555
|
+
return value.flatMap((entry) => {
|
|
556
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
557
|
+
return [];
|
|
558
|
+
const record = entry;
|
|
559
|
+
const token = cleanString(record.token);
|
|
560
|
+
if (!token)
|
|
561
|
+
return [];
|
|
562
|
+
return [{
|
|
563
|
+
token,
|
|
564
|
+
login: cleanString(record.login),
|
|
565
|
+
userId: cleanString(record.userId),
|
|
566
|
+
createdAt: cleanString(record.createdAt) ?? undefined
|
|
567
|
+
}];
|
|
568
|
+
});
|
|
569
|
+
}
|
|
551
570
|
function readStoredAuth(stateFile) {
|
|
552
571
|
if (!existsSync4(stateFile))
|
|
553
572
|
return {};
|
|
@@ -561,6 +580,7 @@ function readStoredAuth(stateFile) {
|
|
|
561
580
|
selectedRepo: cleanString(parsed.selectedRepo),
|
|
562
581
|
tokenSource: parsed.tokenSource === "oauth-device" || parsed.tokenSource === "manual-token" || parsed.tokenSource === "env" ? parsed.tokenSource : undefined,
|
|
563
582
|
pendingDevice: parsePendingDevice(parsed.pendingDevice),
|
|
583
|
+
apiSessions: parseApiSessions(parsed.apiSessions),
|
|
564
584
|
updatedAt: cleanString(parsed.updatedAt) ?? undefined
|
|
565
585
|
};
|
|
566
586
|
} catch {
|
|
@@ -579,6 +599,9 @@ function parsePendingDevice(value) {
|
|
|
579
599
|
return null;
|
|
580
600
|
return { pollId, deviceCode, expiresAt, intervalSeconds };
|
|
581
601
|
}
|
|
602
|
+
function newApiSessionToken() {
|
|
603
|
+
return `rig_${randomBytes(32).toString("base64url")}`;
|
|
604
|
+
}
|
|
582
605
|
function writeStoredAuth(stateFile, payload) {
|
|
583
606
|
mkdirSync3(resolve7(stateFile, ".."), { recursive: true });
|
|
584
607
|
writeFileSync3(stateFile, `${JSON.stringify(payload, null, 2)}
|
|
@@ -621,8 +644,37 @@ function createGitHubAuthStore(projectRoot) {
|
|
|
621
644
|
scopes: input.scopes ?? [],
|
|
622
645
|
selectedRepo: input.selectedRepo ?? previous.selectedRepo ?? null,
|
|
623
646
|
pendingDevice: null,
|
|
647
|
+
apiSessions: previous.apiSessions ?? [],
|
|
648
|
+
updatedAt: new Date().toISOString()
|
|
649
|
+
});
|
|
650
|
+
},
|
|
651
|
+
createApiSession() {
|
|
652
|
+
const previous = readStoredAuth(stateFile);
|
|
653
|
+
const token = newApiSessionToken();
|
|
654
|
+
const session = {
|
|
655
|
+
token,
|
|
656
|
+
login: cleanString(previous.login),
|
|
657
|
+
userId: cleanString(previous.userId),
|
|
658
|
+
createdAt: new Date().toISOString()
|
|
659
|
+
};
|
|
660
|
+
writeStoredAuth(stateFile, {
|
|
661
|
+
...previous,
|
|
662
|
+
apiSessions: [...(previous.apiSessions ?? []).slice(-9), session],
|
|
624
663
|
updatedAt: new Date().toISOString()
|
|
625
664
|
});
|
|
665
|
+
return { token, login: session.login ?? null, userId: session.userId ?? null };
|
|
666
|
+
},
|
|
667
|
+
readApiSession(token) {
|
|
668
|
+
const clean = cleanString(token);
|
|
669
|
+
if (!clean)
|
|
670
|
+
return null;
|
|
671
|
+
const previous = readStoredAuth(stateFile);
|
|
672
|
+
const session = (previous.apiSessions ?? []).find((candidate) => candidate.token === clean);
|
|
673
|
+
return session ? { login: cleanString(session.login), userId: cleanString(session.userId) } : null;
|
|
674
|
+
},
|
|
675
|
+
copyToProjectRoot(projectRoot2) {
|
|
676
|
+
const targetFile = resolveGitHubAuthStateFile(projectRoot2);
|
|
677
|
+
writeStoredAuth(targetFile, readStoredAuth(stateFile));
|
|
626
678
|
},
|
|
627
679
|
savePendingDevice(input) {
|
|
628
680
|
const previous = readStoredAuth(stateFile);
|
|
@@ -1461,10 +1513,10 @@ function normalizeCommit(value) {
|
|
|
1461
1513
|
function asPlainRecord(value) {
|
|
1462
1514
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
1463
1515
|
}
|
|
1464
|
-
var
|
|
1516
|
+
var RIG_CONFIG_PACKAGE_DIST_TAG = "latest";
|
|
1465
1517
|
var RIG_CONFIG_DEV_DEPENDENCIES = {
|
|
1466
|
-
"@rig/core": `npm:@h-rig/core@${
|
|
1467
|
-
"@rig/standard-plugin": `npm:@h-rig/standard-plugin@${
|
|
1518
|
+
"@rig/core": `npm:@h-rig/core@${RIG_CONFIG_PACKAGE_DIST_TAG}`,
|
|
1519
|
+
"@rig/standard-plugin": `npm:@h-rig/standard-plugin@${RIG_CONFIG_PACKAGE_DIST_TAG}`
|
|
1468
1520
|
};
|
|
1469
1521
|
function repoParts(repoSlug) {
|
|
1470
1522
|
const [owner, repo] = repoSlug.split("/");
|
|
@@ -1818,13 +1870,13 @@ function bearerTokenFromRequest(req) {
|
|
|
1818
1870
|
function isLoopbackRequest(req) {
|
|
1819
1871
|
try {
|
|
1820
1872
|
const hostname = new URL(req.url).hostname.toLowerCase();
|
|
1821
|
-
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname === "[::1]";
|
|
1873
|
+
return hostname === "localhost" || hostname === "rig.local" || hostname.endsWith(".localhost") || hostname === "127.0.0.1" || hostname === "::1" || hostname === "[::1]";
|
|
1822
1874
|
} catch {
|
|
1823
1875
|
return false;
|
|
1824
1876
|
}
|
|
1825
1877
|
}
|
|
1826
1878
|
function isPublicRigAuthBootstrapRoute(pathname) {
|
|
1827
|
-
return pathname === "/" || pathname === "/health" || pathname === "/api/health" || pathname === "/api/
|
|
1879
|
+
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";
|
|
1828
1880
|
}
|
|
1829
1881
|
function normalizePrMode(value) {
|
|
1830
1882
|
const mode = normalizeString(value);
|
|
@@ -1837,6 +1889,10 @@ function authorizeRigHttpRequest(input) {
|
|
|
1837
1889
|
const bearer = bearerTokenFromRequest(input.req);
|
|
1838
1890
|
const store = createGitHubAuthStore(input.projectRoot);
|
|
1839
1891
|
const storedToken = store.readToken();
|
|
1892
|
+
const session = bearer ? store.readApiSession(bearer) : null;
|
|
1893
|
+
if (session) {
|
|
1894
|
+
return { authorized: true, actor: session.login ?? "github-operator", reason: "github-session" };
|
|
1895
|
+
}
|
|
1840
1896
|
if (bearer && storedToken && bearer === storedToken) {
|
|
1841
1897
|
const status = store.status({ oauthConfigured: Boolean(process.env.RIG_GITHUB_OAUTH_CLIENT_ID?.trim()) });
|
|
1842
1898
|
return { authorized: true, actor: status.login ?? "github-operator", reason: "github-token" };
|
|
@@ -1844,8 +1900,11 @@ function authorizeRigHttpRequest(input) {
|
|
|
1844
1900
|
if (isPublicRigAuthBootstrapRoute(input.pathname)) {
|
|
1845
1901
|
return { authorized: true, actor: null, reason: "public-bootstrap" };
|
|
1846
1902
|
}
|
|
1847
|
-
if (!input.serverAuthToken && !storedToken
|
|
1848
|
-
|
|
1903
|
+
if (!input.serverAuthToken && !storedToken) {
|
|
1904
|
+
if (isLoopbackRequest(input.req)) {
|
|
1905
|
+
return { authorized: true, actor: null, reason: "loopback-dev-no-auth" };
|
|
1906
|
+
}
|
|
1907
|
+
return { authorized: false, actor: null, reason: "auth-required" };
|
|
1849
1908
|
}
|
|
1850
1909
|
return { authorized: false, actor: null, reason: storedToken ? "github-token-required" : "auth-required" };
|
|
1851
1910
|
}
|
|
@@ -2076,7 +2135,7 @@ function selectNextWorkspaceTask(projectRoot, tasks) {
|
|
|
2076
2135
|
if (runnable.length === 0)
|
|
2077
2136
|
return null;
|
|
2078
2137
|
const queue = readQueueState(projectRoot);
|
|
2079
|
-
const queueRank = new Map(queue.map((entry, index) => [entry.taskId, { score: entry.score, position: index }]));
|
|
2138
|
+
const queueRank = new Map(queue.map((entry, index) => [String(entry.taskId), { score: entry.score, position: index }]));
|
|
2080
2139
|
return runnable.toSorted((left, right) => {
|
|
2081
2140
|
const leftId = taskIdOf(left) ?? "";
|
|
2082
2141
|
const rightId = taskIdOf(right) ?? "";
|
|
@@ -2202,7 +2261,7 @@ function createRigServerFetch(state, deps) {
|
|
|
2202
2261
|
}
|
|
2203
2262
|
const isLinearWebhook = url.pathname === "/api/linear/webhook" && req.method === "POST";
|
|
2204
2263
|
const isInspectorStream = url.pathname === "/api/inspector/stream" && req.method === "GET";
|
|
2205
|
-
const legacyAuthorizedHttpRequest = isInspectorStream && isAuthorizedInspectorStreamRequest(req, state.authToken) ? true : deps.isAuthorizedHttpRequest(req, state.authToken);
|
|
2264
|
+
const legacyAuthorizedHttpRequest = Boolean(state.authToken) && (isInspectorStream && isAuthorizedInspectorStreamRequest(req, state.authToken) ? true : deps.isAuthorizedHttpRequest(req, state.authToken));
|
|
2206
2265
|
const requestAuth = authorizeRigHttpRequest({
|
|
2207
2266
|
req,
|
|
2208
2267
|
pathname: url.pathname,
|
|
@@ -2598,6 +2657,9 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
|
|
|
2598
2657
|
}
|
|
2599
2658
|
const normalizedRoot = resolve11(requestedRoot);
|
|
2600
2659
|
const exists = existsSync8(normalizedRoot);
|
|
2660
|
+
if (exists) {
|
|
2661
|
+
createGitHubAuthStore(state.projectRoot).copyToProjectRoot(normalizedRoot);
|
|
2662
|
+
}
|
|
2601
2663
|
const control = buildServerControlStatus();
|
|
2602
2664
|
const switchCommand = process.env.RIG_PROJECT_ROOT_SWITCH_COMMAND?.trim();
|
|
2603
2665
|
if (!exists) {
|
|
@@ -2686,21 +2748,30 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
|
|
|
2686
2748
|
const body = await deps.readJsonBody(req);
|
|
2687
2749
|
const token = normalizeString(body.token);
|
|
2688
2750
|
const selectedRepo = normalizeString(body.selectedRepo);
|
|
2751
|
+
const requestedProjectRoot = normalizeString(body.projectRoot);
|
|
2689
2752
|
if (!token) {
|
|
2690
2753
|
return deps.badRequest("token is required");
|
|
2691
2754
|
}
|
|
2692
2755
|
try {
|
|
2693
2756
|
const user = await fetchGitHubUserInfo(token);
|
|
2694
|
-
const
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2757
|
+
const storeRoots = [
|
|
2758
|
+
state.projectRoot,
|
|
2759
|
+
...requestedProjectRoot && isAbsolute2(requestedProjectRoot) && existsSync8(resolve11(requestedProjectRoot)) ? [resolve11(requestedProjectRoot)] : []
|
|
2760
|
+
].filter((root, index, roots) => roots.indexOf(root) === index);
|
|
2761
|
+
const stores = storeRoots.map((root) => createGitHubAuthStore(root));
|
|
2762
|
+
for (const store2 of stores) {
|
|
2763
|
+
store2.saveToken({
|
|
2764
|
+
token,
|
|
2765
|
+
tokenSource: "manual-token",
|
|
2766
|
+
login: user.login,
|
|
2767
|
+
userId: user.userId,
|
|
2768
|
+
scopes: user.scopes,
|
|
2769
|
+
selectedRepo
|
|
2770
|
+
});
|
|
2771
|
+
}
|
|
2772
|
+
const store = stores[stores.length - 1] ?? createGitHubAuthStore(state.projectRoot);
|
|
2773
|
+
const apiSession = store.createApiSession();
|
|
2774
|
+
return deps.jsonResponse({ ok: true, ...store.status({ oauthConfigured: Boolean(process.env.RIG_GITHUB_OAUTH_CLIENT_ID?.trim()) }), apiSessionToken: apiSession.token });
|
|
2704
2775
|
} catch (error) {
|
|
2705
2776
|
const message = error instanceof Error ? error.message : String(error);
|
|
2706
2777
|
return deps.jsonResponse({ ok: false, error: message }, 400);
|
|
@@ -2768,7 +2839,8 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
|
|
|
2768
2839
|
const token = result.payload.access_token;
|
|
2769
2840
|
const user = await fetchGitHubUserInfo(token);
|
|
2770
2841
|
store.saveToken({ token, tokenSource: "oauth-device", login: user.login, userId: user.userId, scopes: user.scopes });
|
|
2771
|
-
|
|
2842
|
+
const apiSession = store.createApiSession();
|
|
2843
|
+
return deps.jsonResponse({ ok: true, status: "signed-in", ...store.status({ oauthConfigured: true }), apiSessionToken: apiSession.token });
|
|
2772
2844
|
}
|
|
2773
2845
|
if (url.pathname === "/api/github/repo/probe" && req.method === "POST") {
|
|
2774
2846
|
const body = await deps.readJsonBody(req);
|
|
@@ -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)
|
|
@@ -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
|
}));
|
|
@@ -3304,6 +3304,7 @@ function summarizeRunValidationFailure(projectRoot, run) {
|
|
|
3304
3304
|
}
|
|
3305
3305
|
|
|
3306
3306
|
// packages/server/src/server-helpers/github-auth-store.ts
|
|
3307
|
+
import { randomBytes } from "crypto";
|
|
3307
3308
|
import { chmodSync, existsSync as existsSync6, mkdirSync as mkdirSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync5 } from "fs";
|
|
3308
3309
|
import { resolve as resolve13 } from "path";
|
|
3309
3310
|
function cleanString(value) {
|
|
@@ -3317,6 +3318,24 @@ function cleanScopes(value) {
|
|
|
3317
3318
|
return clean ? [clean] : [];
|
|
3318
3319
|
});
|
|
3319
3320
|
}
|
|
3321
|
+
function parseApiSessions(value) {
|
|
3322
|
+
if (!Array.isArray(value))
|
|
3323
|
+
return [];
|
|
3324
|
+
return value.flatMap((entry) => {
|
|
3325
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
3326
|
+
return [];
|
|
3327
|
+
const record = entry;
|
|
3328
|
+
const token = cleanString(record.token);
|
|
3329
|
+
if (!token)
|
|
3330
|
+
return [];
|
|
3331
|
+
return [{
|
|
3332
|
+
token,
|
|
3333
|
+
login: cleanString(record.login),
|
|
3334
|
+
userId: cleanString(record.userId),
|
|
3335
|
+
createdAt: cleanString(record.createdAt) ?? undefined
|
|
3336
|
+
}];
|
|
3337
|
+
});
|
|
3338
|
+
}
|
|
3320
3339
|
function readStoredAuth(stateFile) {
|
|
3321
3340
|
if (!existsSync6(stateFile))
|
|
3322
3341
|
return {};
|
|
@@ -3330,6 +3349,7 @@ function readStoredAuth(stateFile) {
|
|
|
3330
3349
|
selectedRepo: cleanString(parsed.selectedRepo),
|
|
3331
3350
|
tokenSource: parsed.tokenSource === "oauth-device" || parsed.tokenSource === "manual-token" || parsed.tokenSource === "env" ? parsed.tokenSource : undefined,
|
|
3332
3351
|
pendingDevice: parsePendingDevice(parsed.pendingDevice),
|
|
3352
|
+
apiSessions: parseApiSessions(parsed.apiSessions),
|
|
3333
3353
|
updatedAt: cleanString(parsed.updatedAt) ?? undefined
|
|
3334
3354
|
};
|
|
3335
3355
|
} catch {
|
|
@@ -3348,6 +3368,9 @@ function parsePendingDevice(value) {
|
|
|
3348
3368
|
return null;
|
|
3349
3369
|
return { pollId, deviceCode, expiresAt, intervalSeconds };
|
|
3350
3370
|
}
|
|
3371
|
+
function newApiSessionToken() {
|
|
3372
|
+
return `rig_${randomBytes(32).toString("base64url")}`;
|
|
3373
|
+
}
|
|
3351
3374
|
function writeStoredAuth(stateFile, payload) {
|
|
3352
3375
|
mkdirSync6(resolve13(stateFile, ".."), { recursive: true });
|
|
3353
3376
|
writeFileSync5(stateFile, `${JSON.stringify(payload, null, 2)}
|
|
@@ -3390,9 +3413,38 @@ function createGitHubAuthStore(projectRoot) {
|
|
|
3390
3413
|
scopes: input.scopes ?? [],
|
|
3391
3414
|
selectedRepo: input.selectedRepo ?? previous.selectedRepo ?? null,
|
|
3392
3415
|
pendingDevice: null,
|
|
3416
|
+
apiSessions: previous.apiSessions ?? [],
|
|
3393
3417
|
updatedAt: new Date().toISOString()
|
|
3394
3418
|
});
|
|
3395
3419
|
},
|
|
3420
|
+
createApiSession() {
|
|
3421
|
+
const previous = readStoredAuth(stateFile);
|
|
3422
|
+
const token = newApiSessionToken();
|
|
3423
|
+
const session = {
|
|
3424
|
+
token,
|
|
3425
|
+
login: cleanString(previous.login),
|
|
3426
|
+
userId: cleanString(previous.userId),
|
|
3427
|
+
createdAt: new Date().toISOString()
|
|
3428
|
+
};
|
|
3429
|
+
writeStoredAuth(stateFile, {
|
|
3430
|
+
...previous,
|
|
3431
|
+
apiSessions: [...(previous.apiSessions ?? []).slice(-9), session],
|
|
3432
|
+
updatedAt: new Date().toISOString()
|
|
3433
|
+
});
|
|
3434
|
+
return { token, login: session.login ?? null, userId: session.userId ?? null };
|
|
3435
|
+
},
|
|
3436
|
+
readApiSession(token) {
|
|
3437
|
+
const clean = cleanString(token);
|
|
3438
|
+
if (!clean)
|
|
3439
|
+
return null;
|
|
3440
|
+
const previous = readStoredAuth(stateFile);
|
|
3441
|
+
const session = (previous.apiSessions ?? []).find((candidate) => candidate.token === clean);
|
|
3442
|
+
return session ? { login: cleanString(session.login), userId: cleanString(session.userId) } : null;
|
|
3443
|
+
},
|
|
3444
|
+
copyToProjectRoot(projectRoot2) {
|
|
3445
|
+
const targetFile = resolveGitHubAuthStateFile(projectRoot2);
|
|
3446
|
+
writeStoredAuth(targetFile, readStoredAuth(stateFile));
|
|
3447
|
+
},
|
|
3396
3448
|
savePendingDevice(input) {
|
|
3397
3449
|
const previous = readStoredAuth(stateFile);
|
|
3398
3450
|
writeStoredAuth(stateFile, {
|
|
@@ -3690,15 +3742,36 @@ async function syncProjectStatusForRunLifecycle(projectRoot, run, status, config
|
|
|
3690
3742
|
if (!run.taskId)
|
|
3691
3743
|
return;
|
|
3692
3744
|
const issueNodeId = extractGitHubIssueNodeId(runSourceTaskIdentity(run));
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3745
|
+
try {
|
|
3746
|
+
const result = await syncGitHubProjectStatusForTaskUpdate({
|
|
3747
|
+
taskId: run.taskId,
|
|
3748
|
+
status,
|
|
3749
|
+
issueNodeId,
|
|
3750
|
+
token: createGitHubAuthStore(projectRoot).readToken(),
|
|
3751
|
+
config
|
|
3752
|
+
});
|
|
3753
|
+
if (!result.synced && result.reason !== "project-sync-disabled") {
|
|
3754
|
+
appendRunLogEntry(projectRoot, run.runId, {
|
|
3755
|
+
id: `log:${run.runId}:github-project-sync:${status}`,
|
|
3756
|
+
title: "GitHub Project sync skipped",
|
|
3757
|
+
detail: `Project status sync for ${run.taskId} could not run: ${result.reason}.`,
|
|
3758
|
+
tone: "warn",
|
|
3759
|
+
status: "running",
|
|
3760
|
+
createdAt: new Date().toISOString(),
|
|
3761
|
+
payload: { reason: result.reason, issueNodeId }
|
|
3762
|
+
});
|
|
3763
|
+
}
|
|
3764
|
+
} catch (error) {
|
|
3765
|
+
appendRunLogEntry(projectRoot, run.runId, {
|
|
3766
|
+
id: `log:${run.runId}:github-project-sync-error:${status}`,
|
|
3767
|
+
title: "GitHub Project sync failed",
|
|
3768
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
3769
|
+
tone: "error",
|
|
3770
|
+
status: "running",
|
|
3771
|
+
createdAt: new Date().toISOString(),
|
|
3772
|
+
payload: { issueNodeId }
|
|
3773
|
+
});
|
|
3774
|
+
}
|
|
3702
3775
|
}
|
|
3703
3776
|
async function autoAssignRunIssue(projectRoot, run) {
|
|
3704
3777
|
if (!run.taskId)
|
|
@@ -4886,10 +4959,10 @@ function normalizeCommit(value) {
|
|
|
4886
4959
|
function asPlainRecord(value) {
|
|
4887
4960
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
4888
4961
|
}
|
|
4889
|
-
var
|
|
4962
|
+
var RIG_CONFIG_PACKAGE_DIST_TAG = "latest";
|
|
4890
4963
|
var RIG_CONFIG_DEV_DEPENDENCIES = {
|
|
4891
|
-
"@rig/core": `npm:@h-rig/core@${
|
|
4892
|
-
"@rig/standard-plugin": `npm:@h-rig/standard-plugin@${
|
|
4964
|
+
"@rig/core": `npm:@h-rig/core@${RIG_CONFIG_PACKAGE_DIST_TAG}`,
|
|
4965
|
+
"@rig/standard-plugin": `npm:@h-rig/standard-plugin@${RIG_CONFIG_PACKAGE_DIST_TAG}`
|
|
4893
4966
|
};
|
|
4894
4967
|
function repoParts(repoSlug) {
|
|
4895
4968
|
const [owner, repo] = repoSlug.split("/");
|
|
@@ -5243,13 +5316,13 @@ function bearerTokenFromRequest(req) {
|
|
|
5243
5316
|
function isLoopbackRequest(req) {
|
|
5244
5317
|
try {
|
|
5245
5318
|
const hostname = new URL(req.url).hostname.toLowerCase();
|
|
5246
|
-
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname === "[::1]";
|
|
5319
|
+
return hostname === "localhost" || hostname === "rig.local" || hostname.endsWith(".localhost") || hostname === "127.0.0.1" || hostname === "::1" || hostname === "[::1]";
|
|
5247
5320
|
} catch {
|
|
5248
5321
|
return false;
|
|
5249
5322
|
}
|
|
5250
5323
|
}
|
|
5251
5324
|
function isPublicRigAuthBootstrapRoute(pathname) {
|
|
5252
|
-
return pathname === "/" || pathname === "/health" || pathname === "/api/health" || pathname === "/api/
|
|
5325
|
+
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
5326
|
}
|
|
5254
5327
|
function normalizePrMode(value) {
|
|
5255
5328
|
const mode = normalizeString(value);
|
|
@@ -5262,6 +5335,10 @@ function authorizeRigHttpRequest(input) {
|
|
|
5262
5335
|
const bearer = bearerTokenFromRequest(input.req);
|
|
5263
5336
|
const store = createGitHubAuthStore(input.projectRoot);
|
|
5264
5337
|
const storedToken = store.readToken();
|
|
5338
|
+
const session = bearer ? store.readApiSession(bearer) : null;
|
|
5339
|
+
if (session) {
|
|
5340
|
+
return { authorized: true, actor: session.login ?? "github-operator", reason: "github-session" };
|
|
5341
|
+
}
|
|
5265
5342
|
if (bearer && storedToken && bearer === storedToken) {
|
|
5266
5343
|
const status = store.status({ oauthConfigured: Boolean(process.env.RIG_GITHUB_OAUTH_CLIENT_ID?.trim()) });
|
|
5267
5344
|
return { authorized: true, actor: status.login ?? "github-operator", reason: "github-token" };
|
|
@@ -5269,8 +5346,11 @@ function authorizeRigHttpRequest(input) {
|
|
|
5269
5346
|
if (isPublicRigAuthBootstrapRoute(input.pathname)) {
|
|
5270
5347
|
return { authorized: true, actor: null, reason: "public-bootstrap" };
|
|
5271
5348
|
}
|
|
5272
|
-
if (!input.serverAuthToken && !storedToken
|
|
5273
|
-
|
|
5349
|
+
if (!input.serverAuthToken && !storedToken) {
|
|
5350
|
+
if (isLoopbackRequest(input.req)) {
|
|
5351
|
+
return { authorized: true, actor: null, reason: "loopback-dev-no-auth" };
|
|
5352
|
+
}
|
|
5353
|
+
return { authorized: false, actor: null, reason: "auth-required" };
|
|
5274
5354
|
}
|
|
5275
5355
|
return { authorized: false, actor: null, reason: storedToken ? "github-token-required" : "auth-required" };
|
|
5276
5356
|
}
|
|
@@ -5501,7 +5581,7 @@ function selectNextWorkspaceTask(projectRoot, tasks) {
|
|
|
5501
5581
|
if (runnable.length === 0)
|
|
5502
5582
|
return null;
|
|
5503
5583
|
const queue = readQueueState(projectRoot);
|
|
5504
|
-
const queueRank = new Map(queue.map((entry, index) => [entry.taskId, { score: entry.score, position: index }]));
|
|
5584
|
+
const queueRank = new Map(queue.map((entry, index) => [String(entry.taskId), { score: entry.score, position: index }]));
|
|
5505
5585
|
return runnable.toSorted((left, right) => {
|
|
5506
5586
|
const leftId = taskIdOf(left) ?? "";
|
|
5507
5587
|
const rightId = taskIdOf(right) ?? "";
|
|
@@ -5627,7 +5707,7 @@ function createRigServerFetch(state, deps) {
|
|
|
5627
5707
|
}
|
|
5628
5708
|
const isLinearWebhook = url.pathname === "/api/linear/webhook" && req.method === "POST";
|
|
5629
5709
|
const isInspectorStream = url.pathname === "/api/inspector/stream" && req.method === "GET";
|
|
5630
|
-
const legacyAuthorizedHttpRequest = isInspectorStream && isAuthorizedInspectorStreamRequest(req, state.authToken) ? true : deps.isAuthorizedHttpRequest(req, state.authToken);
|
|
5710
|
+
const legacyAuthorizedHttpRequest = Boolean(state.authToken) && (isInspectorStream && isAuthorizedInspectorStreamRequest(req, state.authToken) ? true : deps.isAuthorizedHttpRequest(req, state.authToken));
|
|
5631
5711
|
const requestAuth = authorizeRigHttpRequest({
|
|
5632
5712
|
req,
|
|
5633
5713
|
pathname: url.pathname,
|
|
@@ -6023,6 +6103,9 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
|
|
|
6023
6103
|
}
|
|
6024
6104
|
const normalizedRoot = resolve18(requestedRoot);
|
|
6025
6105
|
const exists = existsSync11(normalizedRoot);
|
|
6106
|
+
if (exists) {
|
|
6107
|
+
createGitHubAuthStore(state.projectRoot).copyToProjectRoot(normalizedRoot);
|
|
6108
|
+
}
|
|
6026
6109
|
const control = buildServerControlStatus();
|
|
6027
6110
|
const switchCommand = process.env.RIG_PROJECT_ROOT_SWITCH_COMMAND?.trim();
|
|
6028
6111
|
if (!exists) {
|
|
@@ -6111,21 +6194,30 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
|
|
|
6111
6194
|
const body = await deps.readJsonBody(req);
|
|
6112
6195
|
const token = normalizeString(body.token);
|
|
6113
6196
|
const selectedRepo = normalizeString(body.selectedRepo);
|
|
6197
|
+
const requestedProjectRoot = normalizeString(body.projectRoot);
|
|
6114
6198
|
if (!token) {
|
|
6115
6199
|
return deps.badRequest("token is required");
|
|
6116
6200
|
}
|
|
6117
6201
|
try {
|
|
6118
6202
|
const user = await fetchGitHubUserInfo(token);
|
|
6119
|
-
const
|
|
6120
|
-
|
|
6121
|
-
|
|
6122
|
-
|
|
6123
|
-
|
|
6124
|
-
|
|
6125
|
-
|
|
6126
|
-
|
|
6127
|
-
|
|
6128
|
-
|
|
6203
|
+
const storeRoots = [
|
|
6204
|
+
state.projectRoot,
|
|
6205
|
+
...requestedProjectRoot && isAbsolute3(requestedProjectRoot) && existsSync11(resolve18(requestedProjectRoot)) ? [resolve18(requestedProjectRoot)] : []
|
|
6206
|
+
].filter((root, index, roots) => roots.indexOf(root) === index);
|
|
6207
|
+
const stores = storeRoots.map((root) => createGitHubAuthStore(root));
|
|
6208
|
+
for (const store2 of stores) {
|
|
6209
|
+
store2.saveToken({
|
|
6210
|
+
token,
|
|
6211
|
+
tokenSource: "manual-token",
|
|
6212
|
+
login: user.login,
|
|
6213
|
+
userId: user.userId,
|
|
6214
|
+
scopes: user.scopes,
|
|
6215
|
+
selectedRepo
|
|
6216
|
+
});
|
|
6217
|
+
}
|
|
6218
|
+
const store = stores[stores.length - 1] ?? createGitHubAuthStore(state.projectRoot);
|
|
6219
|
+
const apiSession = store.createApiSession();
|
|
6220
|
+
return deps.jsonResponse({ ok: true, ...store.status({ oauthConfigured: Boolean(process.env.RIG_GITHUB_OAUTH_CLIENT_ID?.trim()) }), apiSessionToken: apiSession.token });
|
|
6129
6221
|
} catch (error) {
|
|
6130
6222
|
const message = error instanceof Error ? error.message : String(error);
|
|
6131
6223
|
return deps.jsonResponse({ ok: false, error: message }, 400);
|
|
@@ -6193,7 +6285,8 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
|
|
|
6193
6285
|
const token = result.payload.access_token;
|
|
6194
6286
|
const user = await fetchGitHubUserInfo(token);
|
|
6195
6287
|
store.saveToken({ token, tokenSource: "oauth-device", login: user.login, userId: user.userId, scopes: user.scopes });
|
|
6196
|
-
|
|
6288
|
+
const apiSession = store.createApiSession();
|
|
6289
|
+
return deps.jsonResponse({ ok: true, status: "signed-in", ...store.status({ oauthConfigured: true }), apiSessionToken: apiSession.token });
|
|
6197
6290
|
}
|
|
6198
6291
|
if (url.pathname === "/api/github/repo/probe" && req.method === "POST") {
|
|
6199
6292
|
const body = await deps.readJsonBody(req);
|
|
@@ -11999,7 +12092,7 @@ async function readWorkspaceTasks(projectRoot) {
|
|
|
11999
12092
|
description: normalizeString(entry.description),
|
|
12000
12093
|
acceptanceCriteria: normalizeString(entry.acceptance_criteria),
|
|
12001
12094
|
status: normalizedStatus ?? "unknown",
|
|
12002
|
-
sourceStatus: normalizedStatus ? null : rawStatus,
|
|
12095
|
+
sourceStatus: normalizedStatus && rawStatus !== normalizedStatus ? rawStatus : normalizedStatus ? null : rawStatus,
|
|
12003
12096
|
priority: typeof entry.priority === "number" ? entry.priority : typeof entry.priority === "string" ? Number(entry.priority) : null,
|
|
12004
12097
|
issueType: normalizeString(entry.issue_type),
|
|
12005
12098
|
role: normalizeString(config.role) ?? null,
|
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.2",
|
|
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.2",
|
|
29
|
+
"@rig/core": "npm:@h-rig/core@0.0.6-alpha.2",
|
|
30
|
+
"@rig/runtime": "npm:@h-rig/runtime@0.0.6-alpha.2",
|
|
31
31
|
"effect": "4.0.0-beta.78"
|
|
32
32
|
}
|
|
33
33
|
}
|