flightdesk 0.2.1 → 0.2.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/main.js +260 -62
- package/main.js.map +2 -2
- package/package.json +1 -1
package/main.js
CHANGED
|
@@ -3579,6 +3579,86 @@ var fs2 = __toESM(require("node:fs"));
|
|
|
3579
3579
|
var readline2 = __toESM(require("node:readline"));
|
|
3580
3580
|
var playwright = null;
|
|
3581
3581
|
var USER_DATA_DIR = path2.join(os2.homedir(), ".flightdesk", "chromium-profile");
|
|
3582
|
+
var STORAGE_STATE_FILE = path2.join(os2.homedir(), ".flightdesk", "auth-state.json");
|
|
3583
|
+
var PersistentBrowser = class {
|
|
3584
|
+
constructor(headless = true) {
|
|
3585
|
+
this.browser = null;
|
|
3586
|
+
this.context = null;
|
|
3587
|
+
this.page = null;
|
|
3588
|
+
this.headless = headless;
|
|
3589
|
+
}
|
|
3590
|
+
/**
|
|
3591
|
+
* Initialize the browser context (if not already initialized)
|
|
3592
|
+
*/
|
|
3593
|
+
async init() {
|
|
3594
|
+
if (this.context) return;
|
|
3595
|
+
if (!await isPlaywrightAvailable()) {
|
|
3596
|
+
throw new Error("Playwright not available");
|
|
3597
|
+
}
|
|
3598
|
+
ensureUserDataDir();
|
|
3599
|
+
this.browser = await playwright.chromium.launch({
|
|
3600
|
+
headless: this.headless
|
|
3601
|
+
});
|
|
3602
|
+
const hasAuthState = fs2.existsSync(STORAGE_STATE_FILE);
|
|
3603
|
+
const contextOptions = {
|
|
3604
|
+
viewport: { width: 1280, height: 720 },
|
|
3605
|
+
userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
|
|
3606
|
+
};
|
|
3607
|
+
if (hasAuthState) {
|
|
3608
|
+
contextOptions.storageState = STORAGE_STATE_FILE;
|
|
3609
|
+
}
|
|
3610
|
+
this.context = await this.browser.newContext(contextOptions);
|
|
3611
|
+
this.page = await this.context.newPage();
|
|
3612
|
+
this.page.setDefaultTimeout(3e4);
|
|
3613
|
+
}
|
|
3614
|
+
/**
|
|
3615
|
+
* Check if user is logged into Claude
|
|
3616
|
+
*/
|
|
3617
|
+
async checkAuth() {
|
|
3618
|
+
await this.init();
|
|
3619
|
+
try {
|
|
3620
|
+
await this.page.goto("https://claude.ai/", { waitUntil: "networkidle", timeout: 3e4 });
|
|
3621
|
+
await this.page.waitForTimeout(2e3);
|
|
3622
|
+
const url = this.page.url();
|
|
3623
|
+
console.log(" Final URL:", url);
|
|
3624
|
+
return !url.includes("/login") && !url.includes("/oauth") && !url.includes("accounts.google") && url.includes("claude.ai");
|
|
3625
|
+
} catch (error) {
|
|
3626
|
+
console.error("Auth check failed:", error.message);
|
|
3627
|
+
return false;
|
|
3628
|
+
}
|
|
3629
|
+
}
|
|
3630
|
+
/**
|
|
3631
|
+
* Monitor a Claude Code session URL
|
|
3632
|
+
* Uses shared scrapeSession helper for consistent behavior with one-shot mode.
|
|
3633
|
+
*/
|
|
3634
|
+
async monitorSession(sessionUrl, options = {}) {
|
|
3635
|
+
await this.init();
|
|
3636
|
+
if (!this.page) {
|
|
3637
|
+
return { status: "error", error: "Browser page not initialized" };
|
|
3638
|
+
}
|
|
3639
|
+
return scrapeSession(this.page, sessionUrl, options);
|
|
3640
|
+
}
|
|
3641
|
+
/**
|
|
3642
|
+
* Close the browser context and browser
|
|
3643
|
+
*/
|
|
3644
|
+
async close() {
|
|
3645
|
+
if (this.context) {
|
|
3646
|
+
try {
|
|
3647
|
+
await this.context.close();
|
|
3648
|
+
} catch {
|
|
3649
|
+
}
|
|
3650
|
+
this.context = null;
|
|
3651
|
+
this.page = null;
|
|
3652
|
+
}
|
|
3653
|
+
if (this.browser) {
|
|
3654
|
+
try {
|
|
3655
|
+
await this.browser.close();
|
|
3656
|
+
} catch {
|
|
3657
|
+
}
|
|
3658
|
+
this.browser = null;
|
|
3659
|
+
}
|
|
3660
|
+
}
|
|
3661
|
+
};
|
|
3582
3662
|
async function isPlaywrightAvailable() {
|
|
3583
3663
|
try {
|
|
3584
3664
|
playwright = await import("playwright");
|
|
@@ -3589,7 +3669,23 @@ async function isPlaywrightAvailable() {
|
|
|
3589
3669
|
}
|
|
3590
3670
|
function ensureUserDataDir() {
|
|
3591
3671
|
if (!fs2.existsSync(USER_DATA_DIR)) {
|
|
3592
|
-
fs2.mkdirSync(USER_DATA_DIR, { recursive: true });
|
|
3672
|
+
fs2.mkdirSync(USER_DATA_DIR, { recursive: true, mode: 448 });
|
|
3673
|
+
}
|
|
3674
|
+
}
|
|
3675
|
+
async function ensureStorageDir() {
|
|
3676
|
+
const storageDir = path2.dirname(STORAGE_STATE_FILE);
|
|
3677
|
+
try {
|
|
3678
|
+
await fs2.promises.mkdir(storageDir, { recursive: true, mode: 448 });
|
|
3679
|
+
} catch (err) {
|
|
3680
|
+
if (err?.code !== "EEXIST") {
|
|
3681
|
+
throw err;
|
|
3682
|
+
}
|
|
3683
|
+
}
|
|
3684
|
+
}
|
|
3685
|
+
async function setStorageFilePermissions() {
|
|
3686
|
+
try {
|
|
3687
|
+
await fs2.promises.chmod(STORAGE_STATE_FILE, 384);
|
|
3688
|
+
} catch {
|
|
3593
3689
|
}
|
|
3594
3690
|
}
|
|
3595
3691
|
async function launchBrowser(headless) {
|
|
@@ -3597,24 +3693,28 @@ async function launchBrowser(headless) {
|
|
|
3597
3693
|
throw new Error("Playwright not available");
|
|
3598
3694
|
}
|
|
3599
3695
|
ensureUserDataDir();
|
|
3600
|
-
const
|
|
3601
|
-
|
|
3696
|
+
const browser = await playwright.chromium.launch({ headless });
|
|
3697
|
+
const hasAuthState = fs2.existsSync(STORAGE_STATE_FILE);
|
|
3698
|
+
const contextOptions = {
|
|
3602
3699
|
viewport: { width: 1280, height: 720 },
|
|
3603
3700
|
userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
|
|
3604
|
-
}
|
|
3605
|
-
|
|
3701
|
+
};
|
|
3702
|
+
if (hasAuthState) {
|
|
3703
|
+
contextOptions.storageState = STORAGE_STATE_FILE;
|
|
3704
|
+
}
|
|
3705
|
+
const context = await browser.newContext(contextOptions);
|
|
3606
3706
|
return { browser, context };
|
|
3607
3707
|
}
|
|
3608
3708
|
async function checkAuth() {
|
|
3609
3709
|
if (!await isPlaywrightAvailable()) {
|
|
3610
3710
|
throw new Error("Playwright not installed");
|
|
3611
3711
|
}
|
|
3612
|
-
const { context } = await launchBrowser(true);
|
|
3712
|
+
const { browser, context } = await launchBrowser(true);
|
|
3613
3713
|
try {
|
|
3614
3714
|
const page = await context.newPage();
|
|
3615
3715
|
page.setDefaultTimeout(3e4);
|
|
3616
|
-
await page.goto("https://claude.ai/", { waitUntil: "
|
|
3617
|
-
await page.waitForTimeout(
|
|
3716
|
+
await page.goto("https://claude.ai/", { waitUntil: "networkidle", timeout: 3e4 });
|
|
3717
|
+
await page.waitForTimeout(2e3);
|
|
3618
3718
|
const url = page.url();
|
|
3619
3719
|
console.log(" Final URL:", url);
|
|
3620
3720
|
const isLoggedIn = !url.includes("/login") && !url.includes("/oauth") && !url.includes("accounts.google") && url.includes("claude.ai");
|
|
@@ -3624,6 +3724,7 @@ async function checkAuth() {
|
|
|
3624
3724
|
return false;
|
|
3625
3725
|
} finally {
|
|
3626
3726
|
await context.close();
|
|
3727
|
+
await browser.close();
|
|
3627
3728
|
}
|
|
3628
3729
|
}
|
|
3629
3730
|
async function openForLogin() {
|
|
@@ -3643,6 +3744,13 @@ async function openForLogin() {
|
|
|
3643
3744
|
const url = page.url();
|
|
3644
3745
|
console.log(" Final URL:", url);
|
|
3645
3746
|
const isLoggedIn = !url.includes("/login") && !url.includes("/oauth") && !url.includes("accounts.google") && url.includes("claude.ai");
|
|
3747
|
+
if (isLoggedIn) {
|
|
3748
|
+
await ensureStorageDir();
|
|
3749
|
+
console.log("Saving session state...");
|
|
3750
|
+
await context.storageState({ path: STORAGE_STATE_FILE });
|
|
3751
|
+
await setStorageFilePermissions();
|
|
3752
|
+
console.log(` Saved to: ${STORAGE_STATE_FILE}`);
|
|
3753
|
+
}
|
|
3646
3754
|
console.log("Closing browser...");
|
|
3647
3755
|
return isLoggedIn;
|
|
3648
3756
|
} finally {
|
|
@@ -3664,17 +3772,15 @@ function waitForEnter() {
|
|
|
3664
3772
|
});
|
|
3665
3773
|
});
|
|
3666
3774
|
}
|
|
3667
|
-
async function
|
|
3668
|
-
|
|
3669
|
-
return { status: "error", error: "Playwright not installed" };
|
|
3670
|
-
}
|
|
3671
|
-
const { headless = true, timeout = 3e4, autoPr = false } = options;
|
|
3672
|
-
const { context } = await launchBrowser(headless);
|
|
3775
|
+
async function scrapeSession(page, sessionUrl, options = {}) {
|
|
3776
|
+
const { timeout = 3e4, autoPr = false } = options;
|
|
3673
3777
|
try {
|
|
3674
|
-
const page = await context.newPage();
|
|
3675
3778
|
page.setDefaultTimeout(timeout);
|
|
3676
|
-
await page.goto(sessionUrl, { waitUntil: "
|
|
3677
|
-
await page.
|
|
3779
|
+
await page.goto(sessionUrl, { waitUntil: "networkidle" });
|
|
3780
|
+
await page.waitForSelector('[data-testid="conversation-turn"], .code-spinner-animate, button:has-text("Create PR")', {
|
|
3781
|
+
timeout: 1e4
|
|
3782
|
+
}).catch(() => {
|
|
3783
|
+
});
|
|
3678
3784
|
const result = { status: "active" };
|
|
3679
3785
|
const archiveIndicator = await page.$("text=This session has been archived");
|
|
3680
3786
|
if (archiveIndicator) {
|
|
@@ -3687,15 +3793,18 @@ async function monitorSession(sessionUrl, options = {}) {
|
|
|
3687
3793
|
error: "Not logged in to Claude. Run: flightdesk auth"
|
|
3688
3794
|
};
|
|
3689
3795
|
}
|
|
3796
|
+
const isActive = await detectActiveSpinner(page);
|
|
3797
|
+
result.isActive = isActive;
|
|
3690
3798
|
const branchName = await extractBranchName(page);
|
|
3691
3799
|
if (branchName) {
|
|
3692
3800
|
result.branchName = branchName;
|
|
3693
3801
|
}
|
|
3694
|
-
const
|
|
3695
|
-
|
|
3696
|
-
|
|
3802
|
+
const createPrButton = await page.$('button:has-text("Create PR"):not([aria-haspopup])');
|
|
3803
|
+
const viewPrButton = await page.$('button:has-text("View PR")');
|
|
3804
|
+
result.hasPrButton = !!createPrButton && !viewPrButton;
|
|
3805
|
+
if (autoPr && createPrButton && !viewPrButton) {
|
|
3697
3806
|
console.log(' Clicking "Create PR" button...');
|
|
3698
|
-
await
|
|
3807
|
+
await createPrButton.click();
|
|
3699
3808
|
await page.waitForTimeout(5e3);
|
|
3700
3809
|
const prUrl = await extractPrUrl(page);
|
|
3701
3810
|
if (prUrl) {
|
|
@@ -3708,29 +3817,54 @@ async function monitorSession(sessionUrl, options = {}) {
|
|
|
3708
3817
|
status: "error",
|
|
3709
3818
|
error: error instanceof Error ? error.message : String(error)
|
|
3710
3819
|
};
|
|
3711
|
-
}
|
|
3712
|
-
|
|
3820
|
+
}
|
|
3821
|
+
}
|
|
3822
|
+
function isValidBranchName(text) {
|
|
3823
|
+
if (!text) return false;
|
|
3824
|
+
const cleaned = text.trim();
|
|
3825
|
+
return cleaned.length > 2 && !cleaned.includes(" ");
|
|
3826
|
+
}
|
|
3827
|
+
function matchesBranchPattern(text) {
|
|
3828
|
+
return /^[a-zA-Z0-9_-]+\/[a-zA-Z0-9_-]+/.test(text) && !text.includes(" ");
|
|
3829
|
+
}
|
|
3830
|
+
async function tryExtractFromElement(page, selector) {
|
|
3831
|
+
try {
|
|
3832
|
+
const element = await page.$(selector);
|
|
3833
|
+
if (!element) return null;
|
|
3834
|
+
const text = await element.textContent();
|
|
3835
|
+
const cleaned = text?.trim() ?? null;
|
|
3836
|
+
return isValidBranchName(cleaned) ? cleaned : null;
|
|
3837
|
+
} catch {
|
|
3838
|
+
return null;
|
|
3713
3839
|
}
|
|
3714
3840
|
}
|
|
3715
3841
|
async function extractBranchName(page) {
|
|
3842
|
+
if (!page) return null;
|
|
3716
3843
|
const primarySelectors = [
|
|
3717
|
-
String.raw`button.group\/copy span.truncate
|
|
3844
|
+
String.raw`button.group\/copy span.truncate`,
|
|
3718
3845
|
// New branch name (verified)
|
|
3846
|
+
'button[class*="group/copy"] span.truncate',
|
|
3847
|
+
// Alternative escaping
|
|
3848
|
+
String.raw`button[class*="group\/copy"] span.truncate`
|
|
3849
|
+
// CSS escape
|
|
3719
3850
|
];
|
|
3720
3851
|
for (const selector of primarySelectors) {
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3852
|
+
const result = await tryExtractFromElement(page, selector);
|
|
3853
|
+
if (result) return result;
|
|
3854
|
+
}
|
|
3855
|
+
return await tryExtractBranchFromSpans(page);
|
|
3856
|
+
}
|
|
3857
|
+
async function tryExtractBranchFromSpans(page) {
|
|
3858
|
+
try {
|
|
3859
|
+
const allSpans = await page.$$("span.truncate");
|
|
3860
|
+
for (const span of allSpans) {
|
|
3861
|
+
const text = await span.textContent();
|
|
3862
|
+
const cleaned = text?.trim();
|
|
3863
|
+
if (cleaned && matchesBranchPattern(cleaned)) {
|
|
3864
|
+
return cleaned;
|
|
3731
3865
|
}
|
|
3732
|
-
} catch {
|
|
3733
3866
|
}
|
|
3867
|
+
} catch {
|
|
3734
3868
|
}
|
|
3735
3869
|
return null;
|
|
3736
3870
|
}
|
|
@@ -3768,6 +3902,36 @@ async function extractPrUrl(page) {
|
|
|
3768
3902
|
}
|
|
3769
3903
|
return null;
|
|
3770
3904
|
}
|
|
3905
|
+
async function detectActiveSpinner(page) {
|
|
3906
|
+
try {
|
|
3907
|
+
const spinner = await page.$(".code-spinner-animate");
|
|
3908
|
+
if (spinner) {
|
|
3909
|
+
return true;
|
|
3910
|
+
}
|
|
3911
|
+
const spinnerByContent = await page.$('span:has-text("\u273D")');
|
|
3912
|
+
if (spinnerByContent) {
|
|
3913
|
+
const hasAnimation = await spinnerByContent.evaluate((el) => {
|
|
3914
|
+
let current = el;
|
|
3915
|
+
while (current) {
|
|
3916
|
+
const classList = current.classList;
|
|
3917
|
+
if (classList?.contains("code-spinner-animate")) {
|
|
3918
|
+
return true;
|
|
3919
|
+
}
|
|
3920
|
+
const style = globalThis.getComputedStyle(current);
|
|
3921
|
+
if (style.animationName && style.animationName !== "none") {
|
|
3922
|
+
return true;
|
|
3923
|
+
}
|
|
3924
|
+
current = current.parentElement;
|
|
3925
|
+
}
|
|
3926
|
+
return false;
|
|
3927
|
+
});
|
|
3928
|
+
return hasAnimation;
|
|
3929
|
+
}
|
|
3930
|
+
return false;
|
|
3931
|
+
} catch {
|
|
3932
|
+
return false;
|
|
3933
|
+
}
|
|
3934
|
+
}
|
|
3771
3935
|
|
|
3772
3936
|
// apps/cli/src/commands/auth.ts
|
|
3773
3937
|
async function authCommand() {
|
|
@@ -4066,17 +4230,23 @@ async function watchCommand(options) {
|
|
|
4066
4230
|
console.log(` Auto-PR: ${options.autoPr ? "enabled" : "disabled"}`);
|
|
4067
4231
|
console.log("");
|
|
4068
4232
|
const playwrightAvailable = await isPlaywrightAvailable();
|
|
4233
|
+
let browser = null;
|
|
4069
4234
|
if (!playwrightAvailable) {
|
|
4070
4235
|
console.log("\u26A0\uFE0F Playwright not installed. Session monitoring disabled.");
|
|
4071
4236
|
console.log(" Install with: pnpm add playwright && npx playwright install chromium");
|
|
4072
4237
|
console.log("");
|
|
4073
4238
|
} else {
|
|
4074
|
-
|
|
4239
|
+
browser = new PersistentBrowser(options.headless !== false);
|
|
4240
|
+
console.log("Checking Claude authentication...");
|
|
4241
|
+
const isAuthenticated = await browser.checkAuth();
|
|
4075
4242
|
if (!isAuthenticated) {
|
|
4076
4243
|
console.log("\u26A0\uFE0F Not logged into Claude. Run: flightdesk auth");
|
|
4077
4244
|
console.log("");
|
|
4245
|
+
await browser.close();
|
|
4246
|
+
browser = null;
|
|
4078
4247
|
} else {
|
|
4079
4248
|
console.log("\u2705 Playwright ready, Claude authenticated");
|
|
4249
|
+
console.log(" (Browser session kept alive for monitoring)");
|
|
4080
4250
|
console.log("");
|
|
4081
4251
|
}
|
|
4082
4252
|
}
|
|
@@ -4111,38 +4281,49 @@ async function watchCommand(options) {
|
|
|
4111
4281
|
if (reconciled) {
|
|
4112
4282
|
continue;
|
|
4113
4283
|
}
|
|
4114
|
-
if (task2.sessionViewUrl &&
|
|
4284
|
+
if (task2.sessionViewUrl && browser) {
|
|
4115
4285
|
console.log(` Checking session...`);
|
|
4116
|
-
const sessionInfo = await monitorSession(task2.sessionViewUrl, {
|
|
4117
|
-
headless: options.headless !== false,
|
|
4286
|
+
const sessionInfo = await browser.monitorSession(task2.sessionViewUrl, {
|
|
4118
4287
|
autoPr: options.autoPr && !task2.prUrl
|
|
4119
4288
|
// Only auto-PR if no PR exists
|
|
4120
4289
|
});
|
|
4121
4290
|
await processSessionInfo(api, task2, sessionInfo);
|
|
4122
4291
|
} else if (!task2.sessionViewUrl) {
|
|
4123
4292
|
console.log(" No session URL registered");
|
|
4293
|
+
} else if (!browser) {
|
|
4294
|
+
console.log(" Browser not available - cannot monitor session");
|
|
4124
4295
|
}
|
|
4125
4296
|
}
|
|
4126
4297
|
} catch (error) {
|
|
4127
4298
|
console.error(` Error: ${error}`);
|
|
4128
4299
|
}
|
|
4129
4300
|
}
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4301
|
+
try {
|
|
4302
|
+
await runCheck();
|
|
4303
|
+
if (options.once) {
|
|
4304
|
+
console.log("\nDone (--once flag specified)");
|
|
4305
|
+
return;
|
|
4306
|
+
}
|
|
4307
|
+
console.log(`
|
|
4136
4308
|
Watching... (Ctrl+C to stop)
|
|
4137
4309
|
`);
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4310
|
+
const intervalId = setInterval(runCheck, intervalMinutes * 60 * 1e3);
|
|
4311
|
+
process.on("SIGINT", async () => {
|
|
4312
|
+
console.log("\nShutting down...");
|
|
4313
|
+
clearInterval(intervalId);
|
|
4314
|
+
if (browser) {
|
|
4315
|
+
console.log("Closing browser...");
|
|
4316
|
+
await browser.close();
|
|
4317
|
+
}
|
|
4318
|
+
process.exit(0);
|
|
4319
|
+
});
|
|
4320
|
+
await new Promise((_resolve) => {
|
|
4321
|
+
});
|
|
4322
|
+
} finally {
|
|
4323
|
+
if (browser) {
|
|
4324
|
+
await browser.close();
|
|
4325
|
+
}
|
|
4326
|
+
}
|
|
4146
4327
|
}
|
|
4147
4328
|
async function reconcileTaskStatus(api, task2) {
|
|
4148
4329
|
let expectedStatus = null;
|
|
@@ -4186,7 +4367,25 @@ async function processSessionInfo(api, task2, info) {
|
|
|
4186
4367
|
console.log(" \u{1F4E6} Session archived");
|
|
4187
4368
|
return;
|
|
4188
4369
|
}
|
|
4189
|
-
|
|
4370
|
+
let activityIcon;
|
|
4371
|
+
let activityLabel;
|
|
4372
|
+
if (info.isActive === true) {
|
|
4373
|
+
activityIcon = "\u26A1";
|
|
4374
|
+
activityLabel = "Claude is working";
|
|
4375
|
+
} else if (info.isActive === false) {
|
|
4376
|
+
activityIcon = "\u{1F4A4}";
|
|
4377
|
+
activityLabel = "Waiting for input";
|
|
4378
|
+
} else {
|
|
4379
|
+
activityIcon = "\u2754";
|
|
4380
|
+
activityLabel = "Activity unknown";
|
|
4381
|
+
}
|
|
4382
|
+
console.log(` ${activityIcon} ${activityLabel}`);
|
|
4383
|
+
const updates = {
|
|
4384
|
+
// Update session activity state and timestamp
|
|
4385
|
+
// Preserve null when activity detection fails (info.isActive is undefined)
|
|
4386
|
+
sessionActive: info.isActive ?? null,
|
|
4387
|
+
sessionCheckedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4388
|
+
};
|
|
4190
4389
|
if (info.branchName && info.branchName !== task2.branchName) {
|
|
4191
4390
|
console.log(` \u{1F33F} Branch detected: ${info.branchName}`);
|
|
4192
4391
|
updates.branchName = info.branchName;
|
|
@@ -4206,15 +4405,14 @@ async function processSessionInfo(api, task2, info) {
|
|
|
4206
4405
|
if (info.hasPrButton && !info.prUrl && !task2.prUrl) {
|
|
4207
4406
|
console.log(' \u{1F4DD} "Create PR" button available');
|
|
4208
4407
|
}
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4408
|
+
const hasNonActivityUpdates = Object.keys(updates).some((k) => !["sessionActive", "sessionCheckedAt"].includes(k));
|
|
4409
|
+
try {
|
|
4410
|
+
await api.updateTask(task2.id, updates);
|
|
4411
|
+
if (hasNonActivityUpdates) {
|
|
4212
4412
|
console.log(" \u2705 Task updated");
|
|
4213
|
-
} catch (error) {
|
|
4214
|
-
console.log(` \u274C Failed to update task: ${error}`);
|
|
4215
4413
|
}
|
|
4216
|
-
}
|
|
4217
|
-
console.log(
|
|
4414
|
+
} catch (error) {
|
|
4415
|
+
console.log(` \u274C Failed to update task: ${error}`);
|
|
4218
4416
|
}
|
|
4219
4417
|
}
|
|
4220
4418
|
|
|
@@ -5300,7 +5498,7 @@ async function handleTeardown(api, options) {
|
|
|
5300
5498
|
|
|
5301
5499
|
// apps/cli/src/main.ts
|
|
5302
5500
|
var program2 = new Command();
|
|
5303
|
-
program2.name("flightdesk").description("FlightDesk CLI - AI task management for Claude Code sessions").version("0.2.
|
|
5501
|
+
program2.name("flightdesk").description("FlightDesk CLI - AI task management for Claude Code sessions").version("0.2.2").option("--dev", "Use local development API (localhost:3000)").option("--api <url>", "Use custom API URL");
|
|
5304
5502
|
program2.hook("preAction", () => {
|
|
5305
5503
|
const opts = program2.opts();
|
|
5306
5504
|
if (opts.api) {
|