flightdesk 0.2.4 → 0.3.0
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/main.js +255 -90
- package/main.js.map +3 -3
- package/package.json +1 -1
package/main.js
CHANGED
|
@@ -3216,6 +3216,8 @@ var FlightDeskAPI = class _FlightDeskAPI {
|
|
|
3216
3216
|
status
|
|
3217
3217
|
branchName
|
|
3218
3218
|
prUrl
|
|
3219
|
+
sessionViewUrl
|
|
3220
|
+
sessionTeleportId
|
|
3219
3221
|
}
|
|
3220
3222
|
}
|
|
3221
3223
|
`;
|
|
@@ -3286,7 +3288,10 @@ var FlightDeskAPI = class _FlightDeskAPI {
|
|
|
3286
3288
|
input.projectId = options.projectId;
|
|
3287
3289
|
}
|
|
3288
3290
|
if (options?.status) {
|
|
3289
|
-
input.status = [options.status];
|
|
3291
|
+
input.status = Array.isArray(options.status) ? options.status : [options.status];
|
|
3292
|
+
}
|
|
3293
|
+
if (options?.limit) {
|
|
3294
|
+
input.limit = options.limit;
|
|
3290
3295
|
}
|
|
3291
3296
|
const result = await this.graphql(query, { input });
|
|
3292
3297
|
return result.userTasks;
|
|
@@ -3389,6 +3394,11 @@ var FlightDeskAPI = class _FlightDeskAPI {
|
|
|
3389
3394
|
createdAt
|
|
3390
3395
|
updatedAt
|
|
3391
3396
|
sshConnectionString
|
|
3397
|
+
processUrls {
|
|
3398
|
+
name
|
|
3399
|
+
url
|
|
3400
|
+
primary
|
|
3401
|
+
}
|
|
3392
3402
|
}
|
|
3393
3403
|
}
|
|
3394
3404
|
`;
|
|
@@ -3577,7 +3587,51 @@ var path2 = __toESM(require("node:path"));
|
|
|
3577
3587
|
var os2 = __toESM(require("node:os"));
|
|
3578
3588
|
var fs2 = __toESM(require("node:fs"));
|
|
3579
3589
|
var readline2 = __toESM(require("node:readline"));
|
|
3590
|
+
var import_node_child_process = require("node:child_process");
|
|
3580
3591
|
var playwright = null;
|
|
3592
|
+
var PlaywrightBrowserNotInstalledError = class extends Error {
|
|
3593
|
+
constructor(autoInstallError) {
|
|
3594
|
+
const baseMessage = "Playwright browser not installed.";
|
|
3595
|
+
const autoInstallInfo = autoInstallError ? `
|
|
3596
|
+
|
|
3597
|
+
Auto-install failed: ${autoInstallError}
|
|
3598
|
+
` : "\n\n";
|
|
3599
|
+
super(
|
|
3600
|
+
baseMessage + autoInstallInfo + "Run one of the following commands:\n\n npx playwright install chromium # Just Chromium (recommended)\n npx playwright install # All browsers\n\nThen retry your command."
|
|
3601
|
+
);
|
|
3602
|
+
this.name = "PlaywrightBrowserNotInstalledError";
|
|
3603
|
+
}
|
|
3604
|
+
};
|
|
3605
|
+
function isBrowserNotInstalledError(error) {
|
|
3606
|
+
if (!(error instanceof Error)) return false;
|
|
3607
|
+
return error.message.includes("Executable doesn't exist") || error.message.includes("browserType.launch") || error.message.includes("npx playwright install");
|
|
3608
|
+
}
|
|
3609
|
+
var autoInstallAttempted = false;
|
|
3610
|
+
function tryAutoInstallBrowsers() {
|
|
3611
|
+
if (autoInstallAttempted) {
|
|
3612
|
+
return false;
|
|
3613
|
+
}
|
|
3614
|
+
autoInstallAttempted = true;
|
|
3615
|
+
console.log("");
|
|
3616
|
+
console.log("\u{1F4E6} Playwright browser not found. Installing automatically...");
|
|
3617
|
+
console.log("");
|
|
3618
|
+
try {
|
|
3619
|
+
(0, import_node_child_process.execSync)("npx playwright install chromium", {
|
|
3620
|
+
stdio: "inherit",
|
|
3621
|
+
timeout: 12e4
|
|
3622
|
+
// 2 minute timeout
|
|
3623
|
+
});
|
|
3624
|
+
console.log("");
|
|
3625
|
+
console.log("\u2705 Browser installed successfully!");
|
|
3626
|
+
console.log("");
|
|
3627
|
+
return true;
|
|
3628
|
+
} catch (error) {
|
|
3629
|
+
console.error("");
|
|
3630
|
+
console.error("\u274C Auto-install failed:", error instanceof Error ? error.message : String(error));
|
|
3631
|
+
console.error("");
|
|
3632
|
+
return false;
|
|
3633
|
+
}
|
|
3634
|
+
}
|
|
3581
3635
|
var USER_DATA_DIR = path2.join(os2.homedir(), ".flightdesk", "chromium-profile");
|
|
3582
3636
|
var STORAGE_STATE_FILE = path2.join(os2.homedir(), ".flightdesk", "auth-state.json");
|
|
3583
3637
|
var PersistentBrowser = class {
|
|
@@ -3605,9 +3659,29 @@ var PersistentBrowser = class {
|
|
|
3605
3659
|
throw new Error("Playwright not available");
|
|
3606
3660
|
}
|
|
3607
3661
|
ensureUserDataDir();
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3662
|
+
try {
|
|
3663
|
+
this.browser = await playwright.chromium.launch({
|
|
3664
|
+
headless: this.headless
|
|
3665
|
+
});
|
|
3666
|
+
} catch (error) {
|
|
3667
|
+
if (isBrowserNotInstalledError(error)) {
|
|
3668
|
+
if (tryAutoInstallBrowsers()) {
|
|
3669
|
+
try {
|
|
3670
|
+
this.browser = await playwright.chromium.launch({
|
|
3671
|
+
headless: this.headless
|
|
3672
|
+
});
|
|
3673
|
+
} catch (retryError) {
|
|
3674
|
+
throw new PlaywrightBrowserNotInstalledError(
|
|
3675
|
+
retryError instanceof Error ? retryError.message : String(retryError)
|
|
3676
|
+
);
|
|
3677
|
+
}
|
|
3678
|
+
} else {
|
|
3679
|
+
throw new PlaywrightBrowserNotInstalledError();
|
|
3680
|
+
}
|
|
3681
|
+
} else {
|
|
3682
|
+
throw error;
|
|
3683
|
+
}
|
|
3684
|
+
}
|
|
3611
3685
|
const hasAuthState = fs2.existsSync(STORAGE_STATE_FILE);
|
|
3612
3686
|
const contextOptions = {
|
|
3613
3687
|
viewport: { width: 1280, height: 720 },
|
|
@@ -3702,7 +3776,26 @@ async function launchBrowser(headless) {
|
|
|
3702
3776
|
throw new Error("Playwright not available");
|
|
3703
3777
|
}
|
|
3704
3778
|
ensureUserDataDir();
|
|
3705
|
-
|
|
3779
|
+
let browser;
|
|
3780
|
+
try {
|
|
3781
|
+
browser = await playwright.chromium.launch({ headless });
|
|
3782
|
+
} catch (error) {
|
|
3783
|
+
if (isBrowserNotInstalledError(error)) {
|
|
3784
|
+
if (tryAutoInstallBrowsers()) {
|
|
3785
|
+
try {
|
|
3786
|
+
browser = await playwright.chromium.launch({ headless });
|
|
3787
|
+
} catch (retryError) {
|
|
3788
|
+
throw new PlaywrightBrowserNotInstalledError(
|
|
3789
|
+
retryError instanceof Error ? retryError.message : String(retryError)
|
|
3790
|
+
);
|
|
3791
|
+
}
|
|
3792
|
+
} else {
|
|
3793
|
+
throw new PlaywrightBrowserNotInstalledError();
|
|
3794
|
+
}
|
|
3795
|
+
} else {
|
|
3796
|
+
throw error;
|
|
3797
|
+
}
|
|
3798
|
+
}
|
|
3706
3799
|
const hasAuthState = fs2.existsSync(STORAGE_STATE_FILE);
|
|
3707
3800
|
const contextOptions = {
|
|
3708
3801
|
viewport: { width: 1280, height: 720 },
|
|
@@ -3781,44 +3874,63 @@ function waitForEnter() {
|
|
|
3781
3874
|
});
|
|
3782
3875
|
});
|
|
3783
3876
|
}
|
|
3877
|
+
async function navigateAndPrepare(page, sessionUrl, timeout, debug) {
|
|
3878
|
+
if (debug) console.log(` [DEBUG] Navigating to: ${sessionUrl}`);
|
|
3879
|
+
await page.goto(sessionUrl, { waitUntil: "domcontentloaded", timeout });
|
|
3880
|
+
const dismissBtn = await page.$(`button:has-text("Don't ask me again"), button:has-text("Got it")`);
|
|
3881
|
+
if (dismissBtn) {
|
|
3882
|
+
if (debug) console.log(" [DEBUG] Dismissing notification modal");
|
|
3883
|
+
await dismissBtn.click();
|
|
3884
|
+
await page.waitForTimeout(500);
|
|
3885
|
+
}
|
|
3886
|
+
const matchedElement = await page.waitForSelector('[data-testid="conversation-turn"], .cursor-pointer.bg-bg-300, button:has-text("Create PR")', {
|
|
3887
|
+
timeout: 1e4
|
|
3888
|
+
}).catch(() => null);
|
|
3889
|
+
if (debug) {
|
|
3890
|
+
if (matchedElement) {
|
|
3891
|
+
const tagName = await matchedElement.evaluate((el) => `${el.tagName}.${el.className}`);
|
|
3892
|
+
console.log(` [DEBUG] waitForSelector matched: ${tagName}`);
|
|
3893
|
+
} else {
|
|
3894
|
+
console.log(" [DEBUG] waitForSelector timed out - no expected elements found");
|
|
3895
|
+
}
|
|
3896
|
+
}
|
|
3897
|
+
}
|
|
3898
|
+
async function captureDebugInfo(page) {
|
|
3899
|
+
console.log(` [DEBUG] Current URL after navigation: ${page.url()}`);
|
|
3900
|
+
const screenshotDir = path2.join(os2.homedir(), ".flightdesk", "debug-screenshots");
|
|
3901
|
+
if (!fs2.existsSync(screenshotDir)) fs2.mkdirSync(screenshotDir, { recursive: true });
|
|
3902
|
+
const screenshotPath = path2.join(screenshotDir, `session-${Date.now()}.png`);
|
|
3903
|
+
await page.screenshot({ path: screenshotPath, fullPage: true });
|
|
3904
|
+
console.log(` [DEBUG] Screenshot saved: ${screenshotPath}`);
|
|
3905
|
+
}
|
|
3906
|
+
async function autoCreatePr(page, createPrButton) {
|
|
3907
|
+
console.log(' Clicking "Create PR" button...');
|
|
3908
|
+
await createPrButton.click();
|
|
3909
|
+
await page.waitForTimeout(5e3);
|
|
3910
|
+
return await extractPrUrl(page) ?? void 0;
|
|
3911
|
+
}
|
|
3784
3912
|
async function scrapeSession(page, sessionUrl, options = {}) {
|
|
3785
3913
|
const { timeout = 3e4, autoPr = false } = options;
|
|
3786
3914
|
try {
|
|
3787
3915
|
page.setDefaultTimeout(timeout);
|
|
3788
|
-
|
|
3789
|
-
await page
|
|
3790
|
-
|
|
3791
|
-
}).catch(() => {
|
|
3792
|
-
});
|
|
3793
|
-
const result = { status: "active" };
|
|
3916
|
+
const debug = process.env.FLIGHTDESK_DEBUG === "1";
|
|
3917
|
+
await navigateAndPrepare(page, sessionUrl, timeout, debug);
|
|
3918
|
+
if (debug) await captureDebugInfo(page);
|
|
3794
3919
|
const archiveIndicator = await page.$("text=This session has been archived");
|
|
3795
|
-
if (archiveIndicator) {
|
|
3796
|
-
return { status: "archived" };
|
|
3797
|
-
}
|
|
3920
|
+
if (archiveIndicator) return { status: "archived" };
|
|
3798
3921
|
const url = page.url();
|
|
3799
3922
|
if (url.includes("/login") || url.includes("/oauth")) {
|
|
3800
|
-
return {
|
|
3801
|
-
status: "error",
|
|
3802
|
-
error: "Not logged in to Claude. Run: flightdesk auth"
|
|
3803
|
-
};
|
|
3923
|
+
return { status: "error", error: "Not logged in to Claude. Run: flightdesk auth" };
|
|
3804
3924
|
}
|
|
3805
|
-
const
|
|
3806
|
-
result.isActive =
|
|
3925
|
+
const result = { status: "active" };
|
|
3926
|
+
result.isActive = await detectActiveSpinner(page, debug);
|
|
3807
3927
|
const branchName = await extractBranchName(page);
|
|
3808
|
-
if (branchName)
|
|
3809
|
-
result.branchName = branchName;
|
|
3810
|
-
}
|
|
3928
|
+
if (branchName) result.branchName = branchName;
|
|
3811
3929
|
const createPrButton = await page.$('button:has-text("Create PR"):not([aria-haspopup])');
|
|
3812
3930
|
const viewPrButton = await page.$('button:has-text("View PR")');
|
|
3813
3931
|
result.hasPrButton = !!createPrButton && !viewPrButton;
|
|
3814
3932
|
if (autoPr && createPrButton && !viewPrButton) {
|
|
3815
|
-
|
|
3816
|
-
await createPrButton.click();
|
|
3817
|
-
await page.waitForTimeout(5e3);
|
|
3818
|
-
const prUrl = await extractPrUrl(page);
|
|
3819
|
-
if (prUrl) {
|
|
3820
|
-
result.prUrl = prUrl;
|
|
3821
|
-
}
|
|
3933
|
+
result.prUrl = await autoCreatePr(page, createPrButton);
|
|
3822
3934
|
}
|
|
3823
3935
|
return result;
|
|
3824
3936
|
} catch (error) {
|
|
@@ -3911,33 +4023,27 @@ async function extractPrUrl(page) {
|
|
|
3911
4023
|
}
|
|
3912
4024
|
return null;
|
|
3913
4025
|
}
|
|
3914
|
-
async function detectActiveSpinner(page) {
|
|
4026
|
+
async function detectActiveSpinner(page, debug = false) {
|
|
3915
4027
|
try {
|
|
3916
|
-
const
|
|
3917
|
-
if (
|
|
4028
|
+
const selectedRowSpinner = await page.$(".cursor-pointer.bg-bg-300 .code-spinner-animate");
|
|
4029
|
+
if (debug) {
|
|
4030
|
+
const globalSpinner = await page.$(".code-spinner-animate");
|
|
4031
|
+
console.log(` [DEBUG] .code-spinner-animate global: ${!!globalSpinner}, in selected row: ${!!selectedRowSpinner}`);
|
|
4032
|
+
}
|
|
4033
|
+
if (selectedRowSpinner) {
|
|
4034
|
+
if (debug) console.log(" [DEBUG] Selected sidebar row has spinner: ACTIVE");
|
|
3918
4035
|
return true;
|
|
3919
4036
|
}
|
|
3920
|
-
const
|
|
3921
|
-
if (
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
const classList = current.classList;
|
|
3926
|
-
if (classList?.contains("code-spinner-animate")) {
|
|
3927
|
-
return true;
|
|
3928
|
-
}
|
|
3929
|
-
const style = globalThis.getComputedStyle(current);
|
|
3930
|
-
if (style.animationName && style.animationName !== "none") {
|
|
3931
|
-
return true;
|
|
3932
|
-
}
|
|
3933
|
-
current = current.parentElement;
|
|
3934
|
-
}
|
|
3935
|
-
return false;
|
|
3936
|
-
});
|
|
3937
|
-
return hasAnimation;
|
|
4037
|
+
const stopButton = await page.$('button:has-text("Stop")');
|
|
4038
|
+
if (debug) console.log(` [DEBUG] Stop button found: ${!!stopButton}`);
|
|
4039
|
+
if (stopButton) {
|
|
4040
|
+
if (debug) console.log(" [DEBUG] Stop button detected: ACTIVE");
|
|
4041
|
+
return true;
|
|
3938
4042
|
}
|
|
4043
|
+
if (debug) console.log(" [DEBUG] No activity indicators: IDLE");
|
|
3939
4044
|
return false;
|
|
3940
|
-
} catch {
|
|
4045
|
+
} catch (error) {
|
|
4046
|
+
if (debug) console.log(` [DEBUG] Spinner detection error: ${error}`);
|
|
3941
4047
|
return false;
|
|
3942
4048
|
}
|
|
3943
4049
|
}
|
|
@@ -3954,30 +4060,48 @@ async function authCommand() {
|
|
|
3954
4060
|
console.log(`Profile directory: ${USER_DATA_DIR}
|
|
3955
4061
|
`);
|
|
3956
4062
|
console.log("Checking current authentication status...");
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
4063
|
+
try {
|
|
4064
|
+
const isAuthenticated = await checkAuth();
|
|
4065
|
+
if (isAuthenticated) {
|
|
4066
|
+
console.log("\u2705 Already logged in to Claude!");
|
|
4067
|
+
console.log("\nThe watch daemon will be able to monitor your sessions.");
|
|
4068
|
+
return;
|
|
4069
|
+
}
|
|
4070
|
+
} catch (error) {
|
|
4071
|
+
if (error instanceof PlaywrightBrowserNotInstalledError) {
|
|
4072
|
+
console.error("");
|
|
4073
|
+
console.error("\u274C " + error.message);
|
|
4074
|
+
process.exit(1);
|
|
4075
|
+
}
|
|
4076
|
+
throw error;
|
|
3962
4077
|
}
|
|
3963
4078
|
console.log("\u274C Not logged in to Claude.\n");
|
|
3964
4079
|
console.log("Opening browser for login...");
|
|
3965
4080
|
console.log("Please log in to your Claude account.\n");
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
|
|
4081
|
+
try {
|
|
4082
|
+
const loginSuccessful = await openForLogin();
|
|
4083
|
+
if (loginSuccessful) {
|
|
4084
|
+
console.log("\n\u2705 Successfully logged in!");
|
|
4085
|
+
console.log("The watch daemon can now monitor your Claude Code sessions.");
|
|
4086
|
+
} else {
|
|
4087
|
+
console.log("\n\u274C Login was not detected.");
|
|
4088
|
+
console.log("Please try again with: flightdesk auth");
|
|
4089
|
+
}
|
|
4090
|
+
} catch (error) {
|
|
4091
|
+
if (error instanceof PlaywrightBrowserNotInstalledError) {
|
|
4092
|
+
console.error("");
|
|
4093
|
+
console.error("\u274C " + error.message);
|
|
4094
|
+
process.exit(1);
|
|
4095
|
+
}
|
|
4096
|
+
throw error;
|
|
3973
4097
|
}
|
|
3974
4098
|
}
|
|
3975
4099
|
|
|
3976
4100
|
// apps/cli/src/lib/git.ts
|
|
3977
|
-
var
|
|
4101
|
+
var import_node_child_process2 = require("node:child_process");
|
|
3978
4102
|
function detectGitRepo() {
|
|
3979
4103
|
try {
|
|
3980
|
-
const remoteUrl = (0,
|
|
4104
|
+
const remoteUrl = (0, import_node_child_process2.execSync)("git remote get-url origin", {
|
|
3981
4105
|
encoding: "utf-8",
|
|
3982
4106
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3983
4107
|
}).trim();
|
|
@@ -3985,7 +4109,7 @@ function detectGitRepo() {
|
|
|
3985
4109
|
if (!repoFullName) {
|
|
3986
4110
|
return null;
|
|
3987
4111
|
}
|
|
3988
|
-
const branch = (0,
|
|
4112
|
+
const branch = (0, import_node_child_process2.execSync)("git rev-parse --abbrev-ref HEAD", {
|
|
3989
4113
|
encoding: "utf-8",
|
|
3990
4114
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3991
4115
|
}).trim();
|
|
@@ -4263,16 +4387,25 @@ async function watchCommand(options) {
|
|
|
4263
4387
|
} else {
|
|
4264
4388
|
browser = new PersistentBrowser(options.headless !== false);
|
|
4265
4389
|
console.log("Checking Claude authentication...");
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4390
|
+
try {
|
|
4391
|
+
const isAuthenticated = await browser.checkAuth();
|
|
4392
|
+
if (!isAuthenticated) {
|
|
4393
|
+
console.log("\u26A0\uFE0F Not logged into Claude. Run: flightdesk auth");
|
|
4394
|
+
console.log("");
|
|
4395
|
+
await browser.close();
|
|
4396
|
+
browser = null;
|
|
4397
|
+
} else {
|
|
4398
|
+
console.log("\u2705 Playwright ready, Claude authenticated");
|
|
4399
|
+
console.log(" (Browser session kept alive for monitoring)");
|
|
4400
|
+
console.log("");
|
|
4401
|
+
}
|
|
4402
|
+
} catch (error) {
|
|
4403
|
+
if (error instanceof PlaywrightBrowserNotInstalledError) {
|
|
4404
|
+
console.error("");
|
|
4405
|
+
console.error("\u274C " + error.message);
|
|
4406
|
+
process.exit(1);
|
|
4407
|
+
}
|
|
4408
|
+
throw error;
|
|
4276
4409
|
}
|
|
4277
4410
|
}
|
|
4278
4411
|
const api = FlightDeskAPI.fromConfig(config, org2);
|
|
@@ -4280,7 +4413,11 @@ async function watchCommand(options) {
|
|
|
4280
4413
|
const timestamp = (/* @__PURE__ */ new Date()).toLocaleTimeString();
|
|
4281
4414
|
console.log(`[${timestamp}] Checking tasks...`);
|
|
4282
4415
|
try {
|
|
4283
|
-
const
|
|
4416
|
+
const ACTIVE_STATUSES = ["PENDING", "DISPATCHED", "IN_PROGRESS", "BRANCH_CREATED"];
|
|
4417
|
+
const tasks = await api.listTasks({
|
|
4418
|
+
status: ACTIVE_STATUSES,
|
|
4419
|
+
limit: 200
|
|
4420
|
+
});
|
|
4284
4421
|
const activeTasks = tasks.filter(
|
|
4285
4422
|
(t) => ["DISPATCHED", "IN_PROGRESS", "BRANCH_CREATED"].includes(t.status) || t.status === "PENDING" && t.sessionViewUrl
|
|
4286
4423
|
);
|
|
@@ -4528,15 +4665,34 @@ ${task2.description}`);
|
|
|
4528
4665
|
Created: ${new Date(task2.createdAt).toLocaleString()}`);
|
|
4529
4666
|
console.log(`Updated: ${new Date(task2.updatedAt).toLocaleString()}`);
|
|
4530
4667
|
}
|
|
4668
|
+
function parseSessionId(value) {
|
|
4669
|
+
const urlPrefix = "https://claude.ai/code/";
|
|
4670
|
+
if (value.startsWith(urlPrefix)) {
|
|
4671
|
+
return value.slice(urlPrefix.length);
|
|
4672
|
+
}
|
|
4673
|
+
return value;
|
|
4674
|
+
}
|
|
4531
4675
|
async function handleUpdate(api, options) {
|
|
4532
4676
|
const input = {};
|
|
4533
4677
|
if (options.status) input.status = options.status;
|
|
4534
4678
|
if (options.branch) input.branchName = options.branch;
|
|
4535
4679
|
if (options.prUrl) input.prUrl = options.prUrl;
|
|
4680
|
+
if (options.session) {
|
|
4681
|
+
const sessionId = parseSessionId(options.session);
|
|
4682
|
+
input.sessionViewUrl = `https://claude.ai/code/${sessionId}`;
|
|
4683
|
+
input.sessionTeleportId = sessionId;
|
|
4684
|
+
}
|
|
4536
4685
|
if (Object.keys(input).length === 0) {
|
|
4537
|
-
console.error("No updates specified. Use --status, --branch,
|
|
4686
|
+
console.error("No updates specified. Use --status, --branch, --pr-url, or --session");
|
|
4538
4687
|
process.exit(1);
|
|
4539
4688
|
}
|
|
4689
|
+
if (options.branch && !options.status) {
|
|
4690
|
+
const current = await api.getTask(options.taskId);
|
|
4691
|
+
if (current?.status === "DISPATCHED") {
|
|
4692
|
+
input.status = "IN_PROGRESS";
|
|
4693
|
+
console.log(` Auto-transitioning DISPATCHED \u2192 IN_PROGRESS`);
|
|
4694
|
+
}
|
|
4695
|
+
}
|
|
4540
4696
|
console.log(`Updating task ${options.taskId}...`);
|
|
4541
4697
|
const task2 = await api.updateTask(options.taskId, input);
|
|
4542
4698
|
console.log(`
|
|
@@ -4544,6 +4700,7 @@ async function handleUpdate(api, options) {
|
|
|
4544
4700
|
console.log(` Status: ${task2.status}`);
|
|
4545
4701
|
if (task2.branchName) console.log(` Branch: ${task2.branchName}`);
|
|
4546
4702
|
if (task2.prUrl) console.log(` PR: ${task2.prUrl}`);
|
|
4703
|
+
if (task2.sessionViewUrl) console.log(` Session: ${task2.sessionViewUrl}`);
|
|
4547
4704
|
}
|
|
4548
4705
|
function getStatusEmoji2(status) {
|
|
4549
4706
|
switch (status) {
|
|
@@ -5254,7 +5411,7 @@ ${projects.length} project(s)`);
|
|
|
5254
5411
|
}
|
|
5255
5412
|
|
|
5256
5413
|
// apps/cli/src/commands/preview.ts
|
|
5257
|
-
var
|
|
5414
|
+
var import_node_child_process3 = require("node:child_process");
|
|
5258
5415
|
var path3 = __toESM(require("node:path"));
|
|
5259
5416
|
var os3 = __toESM(require("node:os"));
|
|
5260
5417
|
var fs4 = __toESM(require("node:fs"));
|
|
@@ -5331,7 +5488,15 @@ async function handleStatus2(api, options) {
|
|
|
5331
5488
|
console.log("\u2500".repeat(50));
|
|
5332
5489
|
console.log(`${emoji} Status: ${instance.status}`);
|
|
5333
5490
|
console.log(` ID: ${instance.id.substring(0, 8)}`);
|
|
5334
|
-
|
|
5491
|
+
if (instance.processUrls && instance.processUrls.length > 0) {
|
|
5492
|
+
console.log(" URLs:");
|
|
5493
|
+
for (const pu of instance.processUrls) {
|
|
5494
|
+
const label = pu.primary ? `${pu.name} (primary)` : pu.name;
|
|
5495
|
+
console.log(` ${label}: ${pu.url}`);
|
|
5496
|
+
}
|
|
5497
|
+
} else {
|
|
5498
|
+
console.log(` Preview URL: ${instance.previewUrl}`);
|
|
5499
|
+
}
|
|
5335
5500
|
console.log(` SSH: ${instance.sshConnectionString}`);
|
|
5336
5501
|
console.log(` Container: ${instance.containerId.substring(0, 12)}`);
|
|
5337
5502
|
if (instance.lastActivityAt) {
|
|
@@ -5360,7 +5525,7 @@ async function handleLogs(api, options) {
|
|
|
5360
5525
|
`);
|
|
5361
5526
|
validateSSHParams(instance);
|
|
5362
5527
|
const sshCommand = `docker logs -f ${instance.containerId}`;
|
|
5363
|
-
const ssh = (0,
|
|
5528
|
+
const ssh = (0, import_node_child_process3.spawn)("ssh", [
|
|
5364
5529
|
"-o",
|
|
5365
5530
|
"StrictHostKeyChecking=no",
|
|
5366
5531
|
"-o",
|
|
@@ -5400,13 +5565,13 @@ async function handleMount(api, options) {
|
|
|
5400
5565
|
await new Promise((resolve) => setTimeout(resolve, 3e3));
|
|
5401
5566
|
}
|
|
5402
5567
|
try {
|
|
5403
|
-
(0,
|
|
5568
|
+
(0, import_node_child_process3.execSync)("which sshfs", { stdio: "ignore" });
|
|
5404
5569
|
} catch {
|
|
5405
5570
|
console.error("\u274C sshfs is not installed.");
|
|
5406
5571
|
console.error("");
|
|
5407
5572
|
console.error("Install it with:");
|
|
5408
5573
|
if (process.platform === "darwin") {
|
|
5409
|
-
console.error(" brew install macfuse sshfs");
|
|
5574
|
+
console.error(" brew install macfuse gromgit/fuse/sshfs-mac");
|
|
5410
5575
|
} else if (process.platform === "linux") {
|
|
5411
5576
|
console.error(" sudo apt install sshfs");
|
|
5412
5577
|
} else {
|
|
@@ -5421,7 +5586,7 @@ async function handleMount(api, options) {
|
|
|
5421
5586
|
fs4.mkdirSync(mountDir, { recursive: true });
|
|
5422
5587
|
}
|
|
5423
5588
|
try {
|
|
5424
|
-
const mounted = (0,
|
|
5589
|
+
const mounted = (0, import_node_child_process3.execSync)("mount", { encoding: "utf8" });
|
|
5425
5590
|
if (mounted.includes(mountDir)) {
|
|
5426
5591
|
console.log(`\u{1F4C1} Already mounted at ${mountDir}`);
|
|
5427
5592
|
return;
|
|
@@ -5447,7 +5612,7 @@ async function handleMount(api, options) {
|
|
|
5447
5612
|
mountDir
|
|
5448
5613
|
];
|
|
5449
5614
|
try {
|
|
5450
|
-
const result = (0,
|
|
5615
|
+
const result = (0, import_node_child_process3.spawnSync)("sshfs", sshfsArgs, { stdio: "inherit" });
|
|
5451
5616
|
if (result.status !== 0) {
|
|
5452
5617
|
throw new Error(`sshfs exited with code ${result.status}`);
|
|
5453
5618
|
}
|
|
@@ -5481,9 +5646,9 @@ async function handleUnmount(_api, options) {
|
|
|
5481
5646
|
try {
|
|
5482
5647
|
let result;
|
|
5483
5648
|
if (process.platform === "darwin") {
|
|
5484
|
-
result = (0,
|
|
5649
|
+
result = (0, import_node_child_process3.spawnSync)("umount", [mountDir], { stdio: "inherit" });
|
|
5485
5650
|
} else {
|
|
5486
|
-
result = (0,
|
|
5651
|
+
result = (0, import_node_child_process3.spawnSync)("fusermount", ["-u", mountDir], { stdio: "inherit" });
|
|
5487
5652
|
}
|
|
5488
5653
|
if (result.status !== 0) {
|
|
5489
5654
|
throw new Error(`Unmount exited with code ${result.status}`);
|
|
@@ -5523,7 +5688,7 @@ async function handleTeardown(api, options) {
|
|
|
5523
5688
|
|
|
5524
5689
|
// apps/cli/src/main.ts
|
|
5525
5690
|
var program2 = new Command();
|
|
5526
|
-
program2.name("flightdesk").description("FlightDesk CLI - AI task management for Claude Code sessions").version("0.
|
|
5691
|
+
program2.name("flightdesk").description("FlightDesk CLI - AI task management for Claude Code sessions").version("0.3.0").option("--dev", "Use local development API (localhost:3000)").option("--api <url>", "Use custom API URL");
|
|
5527
5692
|
program2.hook("preAction", () => {
|
|
5528
5693
|
const opts = program2.opts();
|
|
5529
5694
|
if (opts.api) {
|
|
@@ -5544,7 +5709,7 @@ var task = program2.command("task").description("Task management commands");
|
|
|
5544
5709
|
task.command("create").description("Create a new task").requiredOption("-p, --project <id>", "Project ID").requiredOption("-t, --title <title>", "Task title").option("-d, --description <description>", "Task description").action((options) => taskCommand("create", options));
|
|
5545
5710
|
task.command("list").description("List tasks").option("-p, --project <id>", "Filter by project ID").option("--status <status>", "Filter by status").action((options) => taskCommand("list", options));
|
|
5546
5711
|
task.command("status <task-id>").description("Get task status").action((taskId) => taskCommand("status", { taskId }));
|
|
5547
|
-
task.command("update <task-id>").description("Update task").option("-s, --status <status>", "New status").option("--branch <branch>", "Branch name").option("--pr-url <url>", "Pull request URL").action((taskId, options) => taskCommand("update", { taskId, ...options }));
|
|
5712
|
+
task.command("update <task-id>").description("Update task").option("-s, --status <status>", "New status").option("--branch <branch>", "Branch name").option("--pr-url <url>", "Pull request URL").option("--session <session>", "Claude Code session (URL or session ID)").action((taskId, options) => taskCommand("update", { taskId, ...options }));
|
|
5548
5713
|
program2.command("watch").description("Start the Playwright daemon to monitor Claude Code sessions").option("--interval <minutes>", "Check interval in minutes", "5").option("--once", "Run once and exit").option("--auto-pr", 'Automatically click "Create PR" button when found').option("--no-headless", "Run browser in visible mode (for debugging)").action(watchCommand);
|
|
5549
5714
|
program2.command("import").description("Scan Claude.ai for existing sessions and import them as tasks").option("--dry-run", "Show what would be imported without making changes").option("--limit <n>", "Maximum number of sessions to scan", "50").option("-p, --project <id>", "Import all sessions into a specific project").option("--no-headless", "Run browser in visible mode (for debugging)").option("-v, --verbose", "Show detailed progress").action(importCommand);
|
|
5550
5715
|
program2.command("status").description("Show status of all active tasks").option("-p, --project <id>", "Filter by project").action(statusCommand);
|