panopticon-cli 0.4.15 → 0.4.16
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/dashboard/server.js +146 -41
- package/package.json +1 -1
package/dist/dashboard/server.js
CHANGED
|
@@ -85167,6 +85167,21 @@ var IssueDataService = class {
|
|
|
85167
85167
|
}
|
|
85168
85168
|
}
|
|
85169
85169
|
}
|
|
85170
|
+
/**
|
|
85171
|
+
* Look up which tracker an issue belongs to by its identifier.
|
|
85172
|
+
* Returns 'github' | 'linear' | 'rally' | null.
|
|
85173
|
+
*/
|
|
85174
|
+
getIssueSource(identifier) {
|
|
85175
|
+
const id = identifier.toLowerCase();
|
|
85176
|
+
for (const [trackerName, state] of Object.entries(this.trackers)) {
|
|
85177
|
+
for (const issue of state.lastFetchedIssues) {
|
|
85178
|
+
if ((issue.identifier || "").toLowerCase() === id) {
|
|
85179
|
+
return trackerName;
|
|
85180
|
+
}
|
|
85181
|
+
}
|
|
85182
|
+
}
|
|
85183
|
+
return null;
|
|
85184
|
+
}
|
|
85170
85185
|
/**
|
|
85171
85186
|
* Get all issues from cache. Applies shadow state and filtering.
|
|
85172
85187
|
* This is the hot path — must be fast.
|
|
@@ -90635,6 +90650,23 @@ function getLinearApiKey2() {
|
|
|
90635
90650
|
}
|
|
90636
90651
|
return process.env.LINEAR_API_KEY || null;
|
|
90637
90652
|
}
|
|
90653
|
+
function getRallyConfig2() {
|
|
90654
|
+
const envFile = join43(homedir20(), ".panopticon.env");
|
|
90655
|
+
if (!existsSync44(envFile))
|
|
90656
|
+
return null;
|
|
90657
|
+
const content = readFileSync37(envFile, "utf-8");
|
|
90658
|
+
const apiKeyMatch = content.match(/RALLY_API_KEY=(.+)/);
|
|
90659
|
+
if (!apiKeyMatch)
|
|
90660
|
+
return null;
|
|
90661
|
+
const apiKey = apiKeyMatch[1].trim();
|
|
90662
|
+
const serverMatch = content.match(/RALLY_SERVER=(.+)/);
|
|
90663
|
+
const server2 = serverMatch?.[1].trim();
|
|
90664
|
+
const workspaceMatch = content.match(/RALLY_WORKSPACE=(.+)/);
|
|
90665
|
+
const workspace = workspaceMatch?.[1].trim();
|
|
90666
|
+
const projectMatch = content.match(/RALLY_PROJECT=(.+)/);
|
|
90667
|
+
const project = projectMatch?.[1].trim();
|
|
90668
|
+
return { apiKey, server: server2, workspace, project };
|
|
90669
|
+
}
|
|
90638
90670
|
function getGitHubConfig2() {
|
|
90639
90671
|
const envFile = join43(homedir20(), ".panopticon.env");
|
|
90640
90672
|
if (!existsSync44(envFile))
|
|
@@ -95346,9 +95378,10 @@ app.post("/api/issues/:issueId/close", async (req, res) => {
|
|
|
95346
95378
|
const workspacePath = join43(projectPath, "workspaces", `feature-${issueLower}`);
|
|
95347
95379
|
const branchName = `feature/${issueLower}`;
|
|
95348
95380
|
try {
|
|
95349
|
-
const
|
|
95381
|
+
const githubCheck = isGitHubIssue(issueId);
|
|
95382
|
+
const issueSource = issueDataService.getIssueSource(issueId);
|
|
95350
95383
|
const apiKey = getLinearApiKey2();
|
|
95351
|
-
if (
|
|
95384
|
+
if (githubCheck.isGitHub) {
|
|
95352
95385
|
const ghConfig = getGitHubConfig2();
|
|
95353
95386
|
const number = parseInt(issueId.split("-")[1], 10);
|
|
95354
95387
|
const repoConfig = ghConfig?.repos.find((r) => r.prefix === "PAN") || ghConfig?.repos[0];
|
|
@@ -95369,6 +95402,23 @@ app.post("/api/issues/:issueId/close", async (req, res) => {
|
|
|
95369
95402
|
});
|
|
95370
95403
|
}
|
|
95371
95404
|
}
|
|
95405
|
+
} else if (issueSource === "rally") {
|
|
95406
|
+
const rallyConfig = getRallyConfig2();
|
|
95407
|
+
if (rallyConfig) {
|
|
95408
|
+
try {
|
|
95409
|
+
const { RallyTracker: RallyTracker2 } = await Promise.resolve().then(() => (init_rally(), rally_exports));
|
|
95410
|
+
const tracker = new RallyTracker2({
|
|
95411
|
+
apiKey: rallyConfig.apiKey,
|
|
95412
|
+
server: rallyConfig.server,
|
|
95413
|
+
workspace: rallyConfig.workspace,
|
|
95414
|
+
project: rallyConfig.project
|
|
95415
|
+
});
|
|
95416
|
+
await tracker.transitionIssue(issueId, "closed");
|
|
95417
|
+
console.log(`Closed Rally issue ${issueId}`);
|
|
95418
|
+
} catch (rallyErr) {
|
|
95419
|
+
console.error(`Rally close failed for ${issueId}:`, rallyErr.message);
|
|
95420
|
+
}
|
|
95421
|
+
}
|
|
95372
95422
|
} else if (apiKey) {
|
|
95373
95423
|
const getIssueQuery = `
|
|
95374
95424
|
query GetIssue($id: String!) {
|
|
@@ -95421,7 +95471,7 @@ app.post("/api/issues/:issueId/close", async (req, res) => {
|
|
|
95421
95471
|
console.error("Error removing worktree:", wtError.message);
|
|
95422
95472
|
}
|
|
95423
95473
|
}
|
|
95424
|
-
if (
|
|
95474
|
+
if (githubCheck.isGitHub) {
|
|
95425
95475
|
try {
|
|
95426
95476
|
await execAsync15("pan sync", { encoding: "utf-8", timeout: 3e4 });
|
|
95427
95477
|
console.log("pan sync completed");
|
|
@@ -95432,9 +95482,12 @@ app.post("/api/issues/:issueId/close", async (req, res) => {
|
|
|
95432
95482
|
success: true,
|
|
95433
95483
|
message: `Closed ${issueId}${reason ? ": " + reason : ""}`
|
|
95434
95484
|
});
|
|
95435
|
-
if (
|
|
95485
|
+
if (githubCheck.isGitHub) {
|
|
95436
95486
|
issueDataService.invalidateTracker("github").catch(() => {
|
|
95437
95487
|
});
|
|
95488
|
+
} else if (issueSource === "rally") {
|
|
95489
|
+
issueDataService.invalidateTracker("rally").catch(() => {
|
|
95490
|
+
});
|
|
95438
95491
|
} else {
|
|
95439
95492
|
issueDataService.invalidateTracker("linear").catch(() => {
|
|
95440
95493
|
});
|
|
@@ -97355,6 +97408,8 @@ app.post("/api/issues/:id/reset", async (req, res) => {
|
|
|
97355
97408
|
});
|
|
97356
97409
|
issueDataService.invalidateTracker("linear").catch(() => {
|
|
97357
97410
|
});
|
|
97411
|
+
issueDataService.invalidateTracker("rally").catch(() => {
|
|
97412
|
+
});
|
|
97358
97413
|
} catch (error) {
|
|
97359
97414
|
console.error("Reset failed:", error);
|
|
97360
97415
|
res.status(500).json({
|
|
@@ -97368,37 +97423,66 @@ app.post("/api/issues/:id/reopen", async (req, res) => {
|
|
|
97368
97423
|
const { id } = req.params;
|
|
97369
97424
|
const { skipPlan = false } = req.body || {};
|
|
97370
97425
|
try {
|
|
97371
|
-
const
|
|
97372
|
-
|
|
97373
|
-
|
|
97374
|
-
|
|
97375
|
-
|
|
97376
|
-
|
|
97377
|
-
|
|
97378
|
-
|
|
97379
|
-
|
|
97380
|
-
|
|
97381
|
-
|
|
97382
|
-
|
|
97383
|
-
|
|
97384
|
-
|
|
97385
|
-
|
|
97386
|
-
|
|
97387
|
-
|
|
97388
|
-
|
|
97389
|
-
|
|
97390
|
-
|
|
97391
|
-
|
|
97392
|
-
|
|
97426
|
+
const githubCheck = isGitHubIssue(id);
|
|
97427
|
+
const issueSource = issueDataService.getIssueSource(id);
|
|
97428
|
+
if (issueSource === "rally") {
|
|
97429
|
+
const rallyConfig = getRallyConfig2();
|
|
97430
|
+
if (!rallyConfig) {
|
|
97431
|
+
return res.status(400).json({ error: "Rally not configured" });
|
|
97432
|
+
}
|
|
97433
|
+
const { RallyTracker: RallyTracker2 } = await Promise.resolve().then(() => (init_rally(), rally_exports));
|
|
97434
|
+
const tracker = new RallyTracker2({
|
|
97435
|
+
apiKey: rallyConfig.apiKey,
|
|
97436
|
+
server: rallyConfig.server,
|
|
97437
|
+
workspace: rallyConfig.workspace,
|
|
97438
|
+
project: rallyConfig.project
|
|
97439
|
+
});
|
|
97440
|
+
await tracker.transitionIssue(id, "open");
|
|
97441
|
+
console.log(`Reopened Rally issue ${id}`);
|
|
97442
|
+
res.json({
|
|
97443
|
+
success: true,
|
|
97444
|
+
message: `Issue ${id} reopened`,
|
|
97445
|
+
issueId: id,
|
|
97446
|
+
newState: "Backlog"
|
|
97447
|
+
});
|
|
97448
|
+
issueDataService.invalidateTracker("rally").catch(() => {
|
|
97449
|
+
});
|
|
97450
|
+
} else {
|
|
97451
|
+
const linearKey = process.env.LINEAR_API_KEY || "";
|
|
97452
|
+
if (!linearKey) {
|
|
97453
|
+
return res.status(400).json({ error: "LINEAR_API_KEY not configured" });
|
|
97454
|
+
}
|
|
97455
|
+
const { LinearClient: LinearClient2 } = await Promise.resolve().then(() => __toESM(require_index_cjs_min(), 1));
|
|
97456
|
+
const client = new LinearClient2({ apiKey: linearKey });
|
|
97457
|
+
const issue = await client.issue(id);
|
|
97458
|
+
if (!issue) {
|
|
97459
|
+
return res.status(404).json({ error: `Issue ${id} not found` });
|
|
97460
|
+
}
|
|
97461
|
+
const team = await issue.team;
|
|
97462
|
+
if (!team) {
|
|
97463
|
+
return res.status(400).json({ error: "Could not determine team for issue" });
|
|
97464
|
+
}
|
|
97465
|
+
const states = await team.states();
|
|
97466
|
+
const backlogState = states.nodes.find((s) => s.type === "backlog");
|
|
97467
|
+
if (!backlogState) {
|
|
97468
|
+
return res.status(400).json({ error: "Could not find Backlog state for team" });
|
|
97469
|
+
}
|
|
97470
|
+
await issue.update({ stateId: backlogState.id });
|
|
97471
|
+
console.log(`Reopened issue ${id} - moved to Backlog`);
|
|
97472
|
+
res.json({
|
|
97473
|
+
success: true,
|
|
97474
|
+
message: `Issue ${id} reopened and moved to Backlog`,
|
|
97475
|
+
issueId: issue.identifier,
|
|
97476
|
+
newState: "Backlog"
|
|
97477
|
+
});
|
|
97478
|
+
if (githubCheck.isGitHub) {
|
|
97479
|
+
issueDataService.invalidateTracker("github").catch(() => {
|
|
97480
|
+
});
|
|
97481
|
+
} else {
|
|
97482
|
+
issueDataService.invalidateTracker("linear").catch(() => {
|
|
97483
|
+
});
|
|
97484
|
+
}
|
|
97393
97485
|
}
|
|
97394
|
-
res.json({
|
|
97395
|
-
success: true,
|
|
97396
|
-
message: `Issue ${id} reopened and moved to Backlog`,
|
|
97397
|
-
issueId: issue.identifier,
|
|
97398
|
-
newState: "Backlog"
|
|
97399
|
-
});
|
|
97400
|
-
issueDataService.invalidateTracker("linear").catch(() => {
|
|
97401
|
-
});
|
|
97402
97486
|
} catch (error) {
|
|
97403
97487
|
console.error("Error reopening issue:", error);
|
|
97404
97488
|
res.status(500).json({ error: "Failed to reopen issue: " + error.message });
|
|
@@ -97423,15 +97507,34 @@ app.post("/api/issues/:id/move-status", async (req, res) => {
|
|
|
97423
97507
|
};
|
|
97424
97508
|
const issueState = canonicalToIssueState[targetStatus];
|
|
97425
97509
|
const shadowResult = await updateShadowState2(id, issueState, "dashboard-drag-drop", targetStatus);
|
|
97510
|
+
const issueSource = issueDataService.getIssueSource(id);
|
|
97511
|
+
const githubCheck = isGitHubIssue(id);
|
|
97426
97512
|
if (syncToTracker) {
|
|
97427
|
-
const linearKey = process.env.LINEAR_API_KEY || "";
|
|
97428
|
-
if (!linearKey) {
|
|
97429
|
-
return res.status(400).json({ error: "LINEAR_API_KEY not configured for sync" });
|
|
97430
|
-
}
|
|
97431
|
-
const githubCheck = isGitHubIssue(id);
|
|
97432
97513
|
if (githubCheck.isGitHub) {
|
|
97433
97514
|
console.log(`GitHub issue ${id} - skipping tracker sync`);
|
|
97515
|
+
} else if (issueSource === "rally") {
|
|
97516
|
+
const rallyConfig = getRallyConfig2();
|
|
97517
|
+
if (!rallyConfig) {
|
|
97518
|
+
return res.status(400).json({ error: "Rally not configured for sync" });
|
|
97519
|
+
}
|
|
97520
|
+
try {
|
|
97521
|
+
const { RallyTracker: RallyTracker2 } = await Promise.resolve().then(() => (init_rally(), rally_exports));
|
|
97522
|
+
const tracker = new RallyTracker2({
|
|
97523
|
+
apiKey: rallyConfig.apiKey,
|
|
97524
|
+
server: rallyConfig.server,
|
|
97525
|
+
workspace: rallyConfig.workspace,
|
|
97526
|
+
project: rallyConfig.project
|
|
97527
|
+
});
|
|
97528
|
+
await tracker.transitionIssue(id, issueState);
|
|
97529
|
+
console.log(`Synced issue ${id} to Rally state: ${issueState}`);
|
|
97530
|
+
} catch (rallyErr) {
|
|
97531
|
+
console.error(`Rally sync failed for ${id}:`, rallyErr.message);
|
|
97532
|
+
}
|
|
97434
97533
|
} else {
|
|
97534
|
+
const linearKey = process.env.LINEAR_API_KEY || "";
|
|
97535
|
+
if (!linearKey) {
|
|
97536
|
+
return res.status(400).json({ error: "LINEAR_API_KEY not configured for sync" });
|
|
97537
|
+
}
|
|
97435
97538
|
const { LinearClient: LinearClient2 } = await Promise.resolve().then(() => __toESM(require_index_cjs_min(), 1));
|
|
97436
97539
|
const client = new LinearClient2({ apiKey: linearKey });
|
|
97437
97540
|
const issue = await client.issue(id);
|
|
@@ -97468,10 +97571,12 @@ app.post("/api/issues/:id/move-status", async (req, res) => {
|
|
|
97468
97571
|
syncToTracker,
|
|
97469
97572
|
shadowState: shadowResult
|
|
97470
97573
|
});
|
|
97471
|
-
|
|
97472
|
-
if (githubMoveCheck.isGitHub) {
|
|
97574
|
+
if (githubCheck.isGitHub) {
|
|
97473
97575
|
issueDataService.invalidateTracker("github").catch(() => {
|
|
97474
97576
|
});
|
|
97577
|
+
} else if (issueSource === "rally") {
|
|
97578
|
+
issueDataService.invalidateTracker("rally").catch(() => {
|
|
97579
|
+
});
|
|
97475
97580
|
} else {
|
|
97476
97581
|
issueDataService.invalidateTracker("linear").catch(() => {
|
|
97477
97582
|
});
|