flightdesk 0.1.7 → 0.1.10
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 +603 -606
- package/main.js.map +4 -4
- package/package.json +1 -1
package/main.js
CHANGED
|
@@ -3052,410 +3052,130 @@ function setApiUrl(url) {
|
|
|
3052
3052
|
apiUrlOverride = url;
|
|
3053
3053
|
}
|
|
3054
3054
|
function getApiUrl() {
|
|
3055
|
-
|
|
3055
|
+
const config = loadConfig();
|
|
3056
|
+
return apiUrlOverride ?? config.apiUrl ?? DEFAULT_API_URL;
|
|
3056
3057
|
}
|
|
3057
3058
|
function loadConfig() {
|
|
3058
3059
|
try {
|
|
3059
3060
|
if (fs.existsSync(CONFIG_FILE)) {
|
|
3060
3061
|
const content = fs.readFileSync(CONFIG_FILE, "utf-8");
|
|
3061
|
-
|
|
3062
|
+
const parsed = JSON.parse(content);
|
|
3063
|
+
if (parsed.organizations?.[0]?.apiKey) {
|
|
3064
|
+
return migrateOldConfig(parsed);
|
|
3065
|
+
}
|
|
3066
|
+
return {
|
|
3067
|
+
apiUrl: DEFAULT_API_URL,
|
|
3068
|
+
organizations: [],
|
|
3069
|
+
repoMapping: {},
|
|
3070
|
+
...parsed
|
|
3071
|
+
};
|
|
3062
3072
|
}
|
|
3063
3073
|
} catch (error) {
|
|
3064
3074
|
console.error("Warning: Failed to load config file:", error);
|
|
3065
3075
|
}
|
|
3066
3076
|
return {
|
|
3077
|
+
apiUrl: DEFAULT_API_URL,
|
|
3067
3078
|
organizations: [],
|
|
3068
3079
|
repoMapping: {}
|
|
3069
3080
|
};
|
|
3070
3081
|
}
|
|
3082
|
+
function migrateOldConfig(oldConfig) {
|
|
3083
|
+
console.log("\u{1F4E6} Migrating config to new format...");
|
|
3084
|
+
const defaultOrg = oldConfig.defaultOrganization ? oldConfig.organizations.find((o) => o.id === oldConfig.defaultOrganization) : oldConfig.organizations[0];
|
|
3085
|
+
const newConfig = {
|
|
3086
|
+
apiKey: defaultOrg?.apiKey,
|
|
3087
|
+
apiUrl: defaultOrg?.apiUrl || DEFAULT_API_URL,
|
|
3088
|
+
activeOrganization: defaultOrg?.id,
|
|
3089
|
+
organizations: oldConfig.organizations.map((o) => ({
|
|
3090
|
+
id: o.id,
|
|
3091
|
+
name: o.name
|
|
3092
|
+
})),
|
|
3093
|
+
repoMapping: oldConfig.repoMapping || {}
|
|
3094
|
+
};
|
|
3095
|
+
saveConfig(newConfig);
|
|
3096
|
+
console.log("\u2705 Config migrated successfully");
|
|
3097
|
+
return newConfig;
|
|
3098
|
+
}
|
|
3071
3099
|
function saveConfig(config) {
|
|
3072
3100
|
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
3073
3101
|
}
|
|
3074
|
-
function
|
|
3102
|
+
function getActiveOrganization() {
|
|
3075
3103
|
const config = loadConfig();
|
|
3076
|
-
if (config.
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
return
|
|
3104
|
+
if (!config.activeOrganization) {
|
|
3105
|
+
if (config.organizations.length === 1) {
|
|
3106
|
+
return config.organizations[0];
|
|
3107
|
+
}
|
|
3108
|
+
return null;
|
|
3081
3109
|
}
|
|
3082
|
-
return null;
|
|
3110
|
+
return config.organizations.find((o) => o.id === config.activeOrganization) || null;
|
|
3083
3111
|
}
|
|
3084
|
-
function
|
|
3112
|
+
function setActiveOrganization(orgId) {
|
|
3085
3113
|
const config = loadConfig();
|
|
3086
|
-
const
|
|
3087
|
-
if (
|
|
3088
|
-
|
|
3114
|
+
const org2 = config.organizations.find((o) => o.id === orgId || o.name.toLowerCase() === orgId.toLowerCase());
|
|
3115
|
+
if (!org2) {
|
|
3116
|
+
throw new Error(`Organization not found: ${orgId}`);
|
|
3089
3117
|
}
|
|
3090
|
-
|
|
3118
|
+
config.activeOrganization = org2.id;
|
|
3119
|
+
saveConfig(config);
|
|
3091
3120
|
}
|
|
3092
|
-
function
|
|
3121
|
+
function updateOrganizations(orgs) {
|
|
3093
3122
|
const config = loadConfig();
|
|
3094
|
-
|
|
3095
|
-
if (
|
|
3096
|
-
config.
|
|
3097
|
-
} else {
|
|
3098
|
-
config.organizations.push(org2);
|
|
3099
|
-
}
|
|
3100
|
-
if (config.organizations.length === 1) {
|
|
3101
|
-
config.defaultOrganization = org2.id;
|
|
3123
|
+
config.organizations = orgs;
|
|
3124
|
+
if (config.activeOrganization && !orgs.find((o) => o.id === config.activeOrganization)) {
|
|
3125
|
+
config.activeOrganization = orgs[0]?.id;
|
|
3102
3126
|
}
|
|
3103
3127
|
saveConfig(config);
|
|
3104
3128
|
}
|
|
3105
|
-
function
|
|
3129
|
+
function getOrganizationByRepo(repoFullName) {
|
|
3106
3130
|
const config = loadConfig();
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
delete config.repoMapping[repo];
|
|
3111
|
-
}
|
|
3112
|
-
}
|
|
3113
|
-
if (config.defaultOrganization === orgId) {
|
|
3114
|
-
config.defaultOrganization = config.organizations[0]?.id;
|
|
3115
|
-
}
|
|
3116
|
-
saveConfig(config);
|
|
3131
|
+
const orgId = config.repoMapping[repoFullName];
|
|
3132
|
+
if (!orgId) return null;
|
|
3133
|
+
return config.organizations.find((o) => o.id === orgId) || null;
|
|
3117
3134
|
}
|
|
3118
3135
|
function isConfigured() {
|
|
3119
3136
|
const config = loadConfig();
|
|
3120
|
-
return config.organizations.length > 0;
|
|
3121
|
-
}
|
|
3122
|
-
|
|
3123
|
-
// apps/cli/src/commands/init.ts
|
|
3124
|
-
var readline = __toESM(require("readline"));
|
|
3125
|
-
function question(rl, prompt) {
|
|
3126
|
-
return new Promise((resolve) => {
|
|
3127
|
-
rl.question(prompt, (answer) => {
|
|
3128
|
-
resolve(answer.trim());
|
|
3129
|
-
});
|
|
3130
|
-
});
|
|
3131
|
-
}
|
|
3132
|
-
async function initCommand() {
|
|
3133
|
-
const rl = readline.createInterface({
|
|
3134
|
-
input: process.stdin,
|
|
3135
|
-
output: process.stdout
|
|
3136
|
-
});
|
|
3137
|
-
console.log("\n\u{1F6EB} FlightDesk CLI Setup\n");
|
|
3138
|
-
try {
|
|
3139
|
-
const config = loadConfig();
|
|
3140
|
-
if (config.organizations.length > 0) {
|
|
3141
|
-
console.log("Existing organizations configured:");
|
|
3142
|
-
config.organizations.forEach((org3, i) => {
|
|
3143
|
-
console.log(` ${i + 1}. ${org3.name} (${org3.id})`);
|
|
3144
|
-
});
|
|
3145
|
-
console.log("");
|
|
3146
|
-
const addMore = await question(rl, "Add another organization? (y/N): ");
|
|
3147
|
-
if (addMore.toLowerCase() !== "y") {
|
|
3148
|
-
console.log("\nConfiguration unchanged.");
|
|
3149
|
-
rl.close();
|
|
3150
|
-
return;
|
|
3151
|
-
}
|
|
3152
|
-
}
|
|
3153
|
-
const orgName = await question(rl, "Organization name: ");
|
|
3154
|
-
if (!orgName) {
|
|
3155
|
-
console.error("Organization name is required");
|
|
3156
|
-
rl.close();
|
|
3157
|
-
return;
|
|
3158
|
-
}
|
|
3159
|
-
const apiKey = await question(rl, "API Key: ");
|
|
3160
|
-
if (!apiKey) {
|
|
3161
|
-
console.error("API Key is required");
|
|
3162
|
-
rl.close();
|
|
3163
|
-
return;
|
|
3164
|
-
}
|
|
3165
|
-
const apiUrl = getApiUrl();
|
|
3166
|
-
console.log(`Using API: ${apiUrl}`);
|
|
3167
|
-
let orgId;
|
|
3168
|
-
const keyMatch = apiKey.match(/^fd_key_([^_]+)_/);
|
|
3169
|
-
if (keyMatch) {
|
|
3170
|
-
orgId = keyMatch[1];
|
|
3171
|
-
} else {
|
|
3172
|
-
orgId = `org_${Date.now()}`;
|
|
3173
|
-
}
|
|
3174
|
-
const org2 = {
|
|
3175
|
-
id: orgId,
|
|
3176
|
-
name: orgName,
|
|
3177
|
-
apiKey,
|
|
3178
|
-
apiUrl
|
|
3179
|
-
};
|
|
3180
|
-
console.log("\nTesting connection...");
|
|
3181
|
-
try {
|
|
3182
|
-
const response = await fetch(`${apiUrl}/graphql`, {
|
|
3183
|
-
method: "POST",
|
|
3184
|
-
headers: {
|
|
3185
|
-
"Content-Type": "application/json",
|
|
3186
|
-
"Authorization": `Bearer ${apiKey}`
|
|
3187
|
-
},
|
|
3188
|
-
body: JSON.stringify({
|
|
3189
|
-
query: "{ __typename }"
|
|
3190
|
-
})
|
|
3191
|
-
});
|
|
3192
|
-
if (!response.ok) {
|
|
3193
|
-
throw new Error(`API returned ${response.status}`);
|
|
3194
|
-
}
|
|
3195
|
-
console.log("\u2705 Connection successful!");
|
|
3196
|
-
} catch (error) {
|
|
3197
|
-
console.error(`\u274C Connection failed: ${error}`);
|
|
3198
|
-
const proceed = await question(rl, "Save configuration anyway? (y/N): ");
|
|
3199
|
-
if (proceed.toLowerCase() !== "y") {
|
|
3200
|
-
rl.close();
|
|
3201
|
-
return;
|
|
3202
|
-
}
|
|
3203
|
-
}
|
|
3204
|
-
addOrganization(org2);
|
|
3205
|
-
if (config.organizations.length > 0) {
|
|
3206
|
-
const setDefault = await question(rl, "Set as default organization? (y/N): ");
|
|
3207
|
-
if (setDefault.toLowerCase() === "y") {
|
|
3208
|
-
const updatedConfig = loadConfig();
|
|
3209
|
-
updatedConfig.defaultOrganization = orgId;
|
|
3210
|
-
saveConfig(updatedConfig);
|
|
3211
|
-
}
|
|
3212
|
-
}
|
|
3213
|
-
console.log(`
|
|
3214
|
-
\u2705 Configuration saved to ${CONFIG_FILE}`);
|
|
3215
|
-
console.log("\nYou can now use:");
|
|
3216
|
-
console.log(' flightdesk task create -p <project-id> -t "Task title"');
|
|
3217
|
-
console.log(" flightdesk status");
|
|
3218
|
-
console.log(" flightdesk watch");
|
|
3219
|
-
} finally {
|
|
3220
|
-
rl.close();
|
|
3221
|
-
}
|
|
3222
|
-
}
|
|
3223
|
-
|
|
3224
|
-
// apps/cli/src/lib/session-monitor.ts
|
|
3225
|
-
var path2 = __toESM(require("node:path"));
|
|
3226
|
-
var os2 = __toESM(require("node:os"));
|
|
3227
|
-
var fs2 = __toESM(require("node:fs"));
|
|
3228
|
-
var readline2 = __toESM(require("node:readline"));
|
|
3229
|
-
var playwright = null;
|
|
3230
|
-
var USER_DATA_DIR = path2.join(os2.homedir(), ".flightdesk", "chromium-profile");
|
|
3231
|
-
async function isPlaywrightAvailable() {
|
|
3232
|
-
try {
|
|
3233
|
-
playwright = await import("playwright");
|
|
3234
|
-
return true;
|
|
3235
|
-
} catch {
|
|
3236
|
-
return false;
|
|
3237
|
-
}
|
|
3238
|
-
}
|
|
3239
|
-
function ensureUserDataDir() {
|
|
3240
|
-
if (!fs2.existsSync(USER_DATA_DIR)) {
|
|
3241
|
-
fs2.mkdirSync(USER_DATA_DIR, { recursive: true });
|
|
3242
|
-
}
|
|
3243
|
-
}
|
|
3244
|
-
async function launchBrowser(headless) {
|
|
3245
|
-
if (!playwright) {
|
|
3246
|
-
throw new Error("Playwright not available");
|
|
3247
|
-
}
|
|
3248
|
-
ensureUserDataDir();
|
|
3249
|
-
const context = await playwright.chromium.launchPersistentContext(USER_DATA_DIR, {
|
|
3250
|
-
headless,
|
|
3251
|
-
viewport: { width: 1280, height: 720 },
|
|
3252
|
-
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"
|
|
3253
|
-
});
|
|
3254
|
-
const browser = context.browser();
|
|
3255
|
-
return { browser, context };
|
|
3256
|
-
}
|
|
3257
|
-
async function checkAuth() {
|
|
3258
|
-
if (!await isPlaywrightAvailable()) {
|
|
3259
|
-
throw new Error("Playwright not installed");
|
|
3260
|
-
}
|
|
3261
|
-
const { context } = await launchBrowser(true);
|
|
3262
|
-
try {
|
|
3263
|
-
const page = await context.newPage();
|
|
3264
|
-
page.setDefaultTimeout(3e4);
|
|
3265
|
-
await page.goto("https://claude.ai/", { waitUntil: "domcontentloaded", timeout: 3e4 });
|
|
3266
|
-
await page.waitForTimeout(3e3);
|
|
3267
|
-
const url = page.url();
|
|
3268
|
-
console.log(" Final URL:", url);
|
|
3269
|
-
const isLoggedIn = !url.includes("/login") && !url.includes("/oauth") && !url.includes("accounts.google") && url.includes("claude.ai");
|
|
3270
|
-
return isLoggedIn;
|
|
3271
|
-
} catch (error) {
|
|
3272
|
-
console.error("Auth check failed:", error.message);
|
|
3273
|
-
return false;
|
|
3274
|
-
} finally {
|
|
3275
|
-
await context.close();
|
|
3276
|
-
}
|
|
3277
|
-
}
|
|
3278
|
-
async function openForLogin() {
|
|
3279
|
-
if (!await isPlaywrightAvailable()) {
|
|
3280
|
-
throw new Error("Playwright not installed");
|
|
3281
|
-
}
|
|
3282
|
-
console.log("Opening browser for Claude login...");
|
|
3283
|
-
const { context } = await launchBrowser(false);
|
|
3284
|
-
try {
|
|
3285
|
-
const page = await context.newPage();
|
|
3286
|
-
await page.goto("https://claude.ai/", { waitUntil: "domcontentloaded", timeout: 6e4 });
|
|
3287
|
-
console.log("\n\u{1F449} Press ENTER here when you have logged in...\n");
|
|
3288
|
-
await waitForEnter();
|
|
3289
|
-
console.log("Verifying login in current session...");
|
|
3290
|
-
await page.goto("https://claude.ai/", { waitUntil: "domcontentloaded", timeout: 3e4 });
|
|
3291
|
-
await page.waitForTimeout(2e3);
|
|
3292
|
-
const url = page.url();
|
|
3293
|
-
console.log(" Final URL:", url);
|
|
3294
|
-
const isLoggedIn = !url.includes("/login") && !url.includes("/oauth") && !url.includes("accounts.google") && url.includes("claude.ai");
|
|
3295
|
-
console.log("Closing browser...");
|
|
3296
|
-
return isLoggedIn;
|
|
3297
|
-
} finally {
|
|
3298
|
-
try {
|
|
3299
|
-
await context.close();
|
|
3300
|
-
} catch {
|
|
3301
|
-
}
|
|
3302
|
-
}
|
|
3303
|
-
}
|
|
3304
|
-
function waitForEnter() {
|
|
3305
|
-
return new Promise((resolve) => {
|
|
3306
|
-
const rl = readline2.createInterface({
|
|
3307
|
-
input: process.stdin,
|
|
3308
|
-
output: process.stdout
|
|
3309
|
-
});
|
|
3310
|
-
rl.question("", () => {
|
|
3311
|
-
rl.close();
|
|
3312
|
-
resolve();
|
|
3313
|
-
});
|
|
3314
|
-
});
|
|
3315
|
-
}
|
|
3316
|
-
async function monitorSession(sessionUrl, options = {}) {
|
|
3317
|
-
if (!await isPlaywrightAvailable()) {
|
|
3318
|
-
return { status: "error", error: "Playwright not installed" };
|
|
3319
|
-
}
|
|
3320
|
-
const { headless = true, timeout = 3e4, autoPr = false } = options;
|
|
3321
|
-
const { context } = await launchBrowser(headless);
|
|
3322
|
-
try {
|
|
3323
|
-
const page = await context.newPage();
|
|
3324
|
-
page.setDefaultTimeout(timeout);
|
|
3325
|
-
await page.goto(sessionUrl, { waitUntil: "domcontentloaded" });
|
|
3326
|
-
await page.waitForTimeout(2e3);
|
|
3327
|
-
const result = { status: "active" };
|
|
3328
|
-
const archiveIndicator = await page.$("text=This session has been archived");
|
|
3329
|
-
if (archiveIndicator) {
|
|
3330
|
-
return { status: "archived" };
|
|
3331
|
-
}
|
|
3332
|
-
const url = page.url();
|
|
3333
|
-
if (url.includes("/login") || url.includes("/oauth")) {
|
|
3334
|
-
return {
|
|
3335
|
-
status: "error",
|
|
3336
|
-
error: "Not logged in to Claude. Run: flightdesk auth"
|
|
3337
|
-
};
|
|
3338
|
-
}
|
|
3339
|
-
const branchName = await extractBranchName(page);
|
|
3340
|
-
if (branchName) {
|
|
3341
|
-
result.branchName = branchName;
|
|
3342
|
-
}
|
|
3343
|
-
const prButton = await page.$('button:has-text("Create PR"):not([aria-haspopup])');
|
|
3344
|
-
result.hasPrButton = !!prButton;
|
|
3345
|
-
if (autoPr && prButton) {
|
|
3346
|
-
console.log(' Clicking "Create PR" button...');
|
|
3347
|
-
await prButton.click();
|
|
3348
|
-
await page.waitForTimeout(5e3);
|
|
3349
|
-
const prUrl = await extractPrUrl(page);
|
|
3350
|
-
if (prUrl) {
|
|
3351
|
-
result.prUrl = prUrl;
|
|
3352
|
-
}
|
|
3353
|
-
}
|
|
3354
|
-
return result;
|
|
3355
|
-
} catch (error) {
|
|
3356
|
-
return {
|
|
3357
|
-
status: "error",
|
|
3358
|
-
error: error instanceof Error ? error.message : String(error)
|
|
3359
|
-
};
|
|
3360
|
-
} finally {
|
|
3361
|
-
await context.close();
|
|
3362
|
-
}
|
|
3363
|
-
}
|
|
3364
|
-
async function extractBranchName(page) {
|
|
3365
|
-
const primarySelectors = [
|
|
3366
|
-
String.raw`button.group\/copy span.truncate`
|
|
3367
|
-
// New branch name (verified)
|
|
3368
|
-
];
|
|
3369
|
-
for (const selector of primarySelectors) {
|
|
3370
|
-
try {
|
|
3371
|
-
const element = await page.$(selector);
|
|
3372
|
-
if (element) {
|
|
3373
|
-
const text = await element.textContent();
|
|
3374
|
-
if (text) {
|
|
3375
|
-
const cleaned = text.trim();
|
|
3376
|
-
if (cleaned && cleaned.length > 2 && !cleaned.includes(" ")) {
|
|
3377
|
-
return cleaned;
|
|
3378
|
-
}
|
|
3379
|
-
}
|
|
3380
|
-
}
|
|
3381
|
-
} catch {
|
|
3382
|
-
}
|
|
3383
|
-
}
|
|
3384
|
-
return null;
|
|
3137
|
+
return !!config.apiKey && config.organizations.length > 0;
|
|
3385
3138
|
}
|
|
3386
|
-
|
|
3387
|
-
const
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
'a[href*="bitbucket.org"][href*="/pull-requests/"]'
|
|
3391
|
-
];
|
|
3392
|
-
for (const selector of selectors) {
|
|
3393
|
-
try {
|
|
3394
|
-
const element = await page.$(selector);
|
|
3395
|
-
if (element) {
|
|
3396
|
-
const href = await element.getAttribute("href");
|
|
3397
|
-
if (href) {
|
|
3398
|
-
return href;
|
|
3399
|
-
}
|
|
3400
|
-
}
|
|
3401
|
-
} catch {
|
|
3402
|
-
}
|
|
3403
|
-
}
|
|
3404
|
-
try {
|
|
3405
|
-
const pageContent = await page.content();
|
|
3406
|
-
const prPatterns = [
|
|
3407
|
-
/(https:\/\/github\.com\/[^/]+\/[^/]+\/pull\/\d+)/,
|
|
3408
|
-
/(https:\/\/gitlab\.com\/[^/]+\/[^/]+\/-\/merge_requests\/\d+)/
|
|
3409
|
-
];
|
|
3410
|
-
for (const pattern of prPatterns) {
|
|
3411
|
-
const match = pageContent.match(pattern);
|
|
3412
|
-
if (match && match[1]) {
|
|
3413
|
-
return match[1];
|
|
3414
|
-
}
|
|
3415
|
-
}
|
|
3416
|
-
} catch {
|
|
3417
|
-
}
|
|
3418
|
-
return null;
|
|
3419
|
-
}
|
|
3420
|
-
|
|
3421
|
-
// apps/cli/src/commands/auth.ts
|
|
3422
|
-
async function authCommand() {
|
|
3423
|
-
console.log("\u{1F510} FlightDesk Authentication\n");
|
|
3424
|
-
const playwrightAvailable = await isPlaywrightAvailable();
|
|
3425
|
-
if (!playwrightAvailable) {
|
|
3426
|
-
console.error("Playwright is not installed.");
|
|
3427
|
-
console.error("Install with: pnpm add playwright && npx playwright install chromium");
|
|
3139
|
+
function requireConfig() {
|
|
3140
|
+
const config = loadConfig();
|
|
3141
|
+
if (!config.apiKey) {
|
|
3142
|
+
console.error("\u274C FlightDesk is not configured. Run: flightdesk init");
|
|
3428
3143
|
process.exit(1);
|
|
3429
3144
|
}
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
const isAuthenticated = await checkAuth();
|
|
3434
|
-
if (isAuthenticated) {
|
|
3435
|
-
console.log("\u2705 Already logged in to Claude!");
|
|
3436
|
-
console.log("\nThe watch daemon will be able to monitor your sessions.");
|
|
3437
|
-
return;
|
|
3145
|
+
if (config.organizations.length === 0) {
|
|
3146
|
+
console.error("\u274C No organizations found. Run: flightdesk init");
|
|
3147
|
+
process.exit(1);
|
|
3438
3148
|
}
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
const
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
console.
|
|
3446
|
-
|
|
3447
|
-
console.log(
|
|
3448
|
-
|
|
3149
|
+
return config;
|
|
3150
|
+
}
|
|
3151
|
+
function requireActiveOrg() {
|
|
3152
|
+
const config = requireConfig();
|
|
3153
|
+
const org2 = getActiveOrganization();
|
|
3154
|
+
if (!org2) {
|
|
3155
|
+
console.error("\u274C No active organization. Run: flightdesk org switch <org-name>");
|
|
3156
|
+
console.log("\nAvailable organizations:");
|
|
3157
|
+
config.organizations.forEach((o) => console.log(` - ${o.name}`));
|
|
3158
|
+
process.exit(1);
|
|
3449
3159
|
}
|
|
3160
|
+
return { config, org: org2 };
|
|
3450
3161
|
}
|
|
3451
3162
|
|
|
3163
|
+
// apps/cli/src/commands/init.ts
|
|
3164
|
+
var readline = __toESM(require("readline"));
|
|
3165
|
+
|
|
3452
3166
|
// apps/cli/src/lib/api.ts
|
|
3453
|
-
var FlightDeskAPI = class {
|
|
3454
|
-
constructor(org2) {
|
|
3455
|
-
this.apiUrl =
|
|
3456
|
-
this.apiKey =
|
|
3167
|
+
var FlightDeskAPI = class _FlightDeskAPI {
|
|
3168
|
+
constructor(config, org2) {
|
|
3169
|
+
this.apiUrl = getApiUrl();
|
|
3170
|
+
this.apiKey = config.apiKey;
|
|
3457
3171
|
this.organizationId = org2.id;
|
|
3458
3172
|
}
|
|
3173
|
+
/**
|
|
3174
|
+
* Create an API client from config, using the active organization
|
|
3175
|
+
*/
|
|
3176
|
+
static fromConfig(config, org2) {
|
|
3177
|
+
return new _FlightDeskAPI(config, org2);
|
|
3178
|
+
}
|
|
3459
3179
|
async graphql(query, variables) {
|
|
3460
3180
|
const response = await fetch(`${this.apiUrl}/graphql`, {
|
|
3461
3181
|
method: "POST",
|
|
@@ -3600,71 +3320,428 @@ var FlightDeskAPI = class {
|
|
|
3600
3320
|
tokenTtlHours
|
|
3601
3321
|
}
|
|
3602
3322
|
}
|
|
3603
|
-
`;
|
|
3604
|
-
const result = await this.graphql(query, { projectId });
|
|
3605
|
-
return result.userProject;
|
|
3323
|
+
`;
|
|
3324
|
+
const result = await this.graphql(query, { projectId });
|
|
3325
|
+
return result.userProject;
|
|
3326
|
+
}
|
|
3327
|
+
async createProject(input) {
|
|
3328
|
+
const query = `
|
|
3329
|
+
mutation CreateProject($input: UserCreateProjectInput!) {
|
|
3330
|
+
userCreateProject(input: $input) {
|
|
3331
|
+
id
|
|
3332
|
+
name
|
|
3333
|
+
githubRepo
|
|
3334
|
+
organizationId
|
|
3335
|
+
createdAt
|
|
3336
|
+
}
|
|
3337
|
+
}
|
|
3338
|
+
`;
|
|
3339
|
+
const result = await this.graphql(query, { input });
|
|
3340
|
+
return result.userCreateProject;
|
|
3341
|
+
}
|
|
3342
|
+
// ============================================================================
|
|
3343
|
+
// Task Token Operations (for agent authentication)
|
|
3344
|
+
// ============================================================================
|
|
3345
|
+
async createTaskToken(taskId) {
|
|
3346
|
+
const query = `
|
|
3347
|
+
mutation CreateTaskToken($taskId: String!) {
|
|
3348
|
+
userCreateTaskToken(taskId: $taskId) {
|
|
3349
|
+
token
|
|
3350
|
+
expiresAt
|
|
3351
|
+
}
|
|
3352
|
+
}
|
|
3353
|
+
`;
|
|
3354
|
+
const result = await this.graphql(query, { taskId });
|
|
3355
|
+
return result.userCreateTaskToken;
|
|
3356
|
+
}
|
|
3357
|
+
// ============================================================================
|
|
3358
|
+
// Prompt Operations
|
|
3359
|
+
// ============================================================================
|
|
3360
|
+
async getTaskPrompts(taskId) {
|
|
3361
|
+
const query = `
|
|
3362
|
+
query GetTaskPrompts($taskId: String!) {
|
|
3363
|
+
userTaskPrompts(taskId: $taskId) {
|
|
3364
|
+
type
|
|
3365
|
+
content
|
|
3366
|
+
available
|
|
3367
|
+
reason
|
|
3368
|
+
}
|
|
3369
|
+
}
|
|
3370
|
+
`;
|
|
3371
|
+
const result = await this.graphql(query, { taskId });
|
|
3372
|
+
return result.userTaskPrompts;
|
|
3373
|
+
}
|
|
3374
|
+
};
|
|
3375
|
+
async function fetchUserInfo(apiKey, apiUrl) {
|
|
3376
|
+
const url = apiUrl || "https://api.flightdesk.dev";
|
|
3377
|
+
const response = await fetch(`${url}/graphql`, {
|
|
3378
|
+
method: "POST",
|
|
3379
|
+
headers: {
|
|
3380
|
+
"Content-Type": "application/json",
|
|
3381
|
+
"Authorization": `Bearer ${apiKey}`
|
|
3382
|
+
},
|
|
3383
|
+
body: JSON.stringify({
|
|
3384
|
+
query: `{
|
|
3385
|
+
me {
|
|
3386
|
+
id
|
|
3387
|
+
emails {
|
|
3388
|
+
email
|
|
3389
|
+
primary
|
|
3390
|
+
}
|
|
3391
|
+
organizations {
|
|
3392
|
+
organization {
|
|
3393
|
+
id
|
|
3394
|
+
name
|
|
3395
|
+
}
|
|
3396
|
+
}
|
|
3397
|
+
}
|
|
3398
|
+
}`
|
|
3399
|
+
})
|
|
3400
|
+
});
|
|
3401
|
+
if (!response.ok) {
|
|
3402
|
+
throw new Error(`API returned ${response.status}`);
|
|
3403
|
+
}
|
|
3404
|
+
const result = await response.json();
|
|
3405
|
+
if (result.errors?.length) {
|
|
3406
|
+
throw new Error(result.errors.map((e) => e.message).join(", "));
|
|
3407
|
+
}
|
|
3408
|
+
if (!result.data?.me) {
|
|
3409
|
+
throw new Error("Invalid API response");
|
|
3410
|
+
}
|
|
3411
|
+
return result.data.me;
|
|
3412
|
+
}
|
|
3413
|
+
|
|
3414
|
+
// apps/cli/src/commands/init.ts
|
|
3415
|
+
function question(rl, prompt) {
|
|
3416
|
+
return new Promise((resolve) => {
|
|
3417
|
+
rl.question(prompt, (answer) => {
|
|
3418
|
+
resolve(answer.trim());
|
|
3419
|
+
});
|
|
3420
|
+
});
|
|
3421
|
+
}
|
|
3422
|
+
async function initCommand() {
|
|
3423
|
+
const rl = readline.createInterface({
|
|
3424
|
+
input: process.stdin,
|
|
3425
|
+
output: process.stdout
|
|
3426
|
+
});
|
|
3427
|
+
console.log("\n\u{1F6EB} FlightDesk CLI Setup\n");
|
|
3428
|
+
try {
|
|
3429
|
+
if (isConfigured()) {
|
|
3430
|
+
const config = loadConfig();
|
|
3431
|
+
console.log("FlightDesk is already configured.");
|
|
3432
|
+
console.log(` API Key: ${config.apiKey?.substring(0, 20)}...`);
|
|
3433
|
+
console.log(` Organizations: ${config.organizations.map((o) => o.name).join(", ")}`);
|
|
3434
|
+
console.log("");
|
|
3435
|
+
const reconfigure = await question(rl, "Reconfigure? (y/N): ");
|
|
3436
|
+
if (reconfigure.toLowerCase() !== "y") {
|
|
3437
|
+
console.log("\nConfiguration unchanged.");
|
|
3438
|
+
console.log('Use "flightdesk org refresh" to update organizations.');
|
|
3439
|
+
console.log('Use "flightdesk org switch <name>" to change active organization.');
|
|
3440
|
+
return;
|
|
3441
|
+
}
|
|
3442
|
+
}
|
|
3443
|
+
console.log("Enter your FlightDesk API key.");
|
|
3444
|
+
console.log("You can find this at https://flightdesk.dev/settings/api-keys\n");
|
|
3445
|
+
const apiKey = await question(rl, "API Key: ");
|
|
3446
|
+
if (!apiKey) {
|
|
3447
|
+
console.error("API Key is required");
|
|
3448
|
+
return;
|
|
3449
|
+
}
|
|
3450
|
+
const apiUrl = getApiUrl();
|
|
3451
|
+
console.log("\nConnecting to FlightDesk...");
|
|
3452
|
+
try {
|
|
3453
|
+
const userInfo = await fetchUserInfo(apiKey, apiUrl);
|
|
3454
|
+
if (userInfo.organizations.length === 0) {
|
|
3455
|
+
console.error("\u274C No organizations found for this user");
|
|
3456
|
+
console.log("Please make sure you are a member of at least one organization.");
|
|
3457
|
+
return;
|
|
3458
|
+
}
|
|
3459
|
+
const primaryEmail = userInfo.emails.find((e) => e.primary)?.email || userInfo.emails[0]?.email || "unknown";
|
|
3460
|
+
console.log("\u2705 Connection successful!");
|
|
3461
|
+
console.log(` Logged in as: ${primaryEmail}`);
|
|
3462
|
+
console.log(` Organizations: ${userInfo.organizations.length}`);
|
|
3463
|
+
const organizations = userInfo.organizations.map((m) => ({
|
|
3464
|
+
id: m.organization.id,
|
|
3465
|
+
name: m.organization.name
|
|
3466
|
+
}));
|
|
3467
|
+
let activeOrganization;
|
|
3468
|
+
if (organizations.length === 1) {
|
|
3469
|
+
activeOrganization = organizations[0].id;
|
|
3470
|
+
console.log(`
|
|
3471
|
+
Active organization: ${organizations[0].name}`);
|
|
3472
|
+
} else {
|
|
3473
|
+
console.log("\nYour organizations:");
|
|
3474
|
+
organizations.forEach((org2, i) => {
|
|
3475
|
+
console.log(` ${i + 1}. ${org2.name}`);
|
|
3476
|
+
});
|
|
3477
|
+
const choice = await question(rl, `
|
|
3478
|
+
Select active organization (1-${organizations.length}): `);
|
|
3479
|
+
const choiceIndex = parseInt(choice, 10) - 1;
|
|
3480
|
+
if (choiceIndex < 0 || choiceIndex >= organizations.length) {
|
|
3481
|
+
console.error("Invalid selection, using first organization");
|
|
3482
|
+
activeOrganization = organizations[0].id;
|
|
3483
|
+
} else {
|
|
3484
|
+
activeOrganization = organizations[choiceIndex].id;
|
|
3485
|
+
}
|
|
3486
|
+
}
|
|
3487
|
+
const config = loadConfig();
|
|
3488
|
+
config.apiKey = apiKey;
|
|
3489
|
+
config.apiUrl = apiUrl;
|
|
3490
|
+
config.organizations = organizations;
|
|
3491
|
+
config.activeOrganization = activeOrganization;
|
|
3492
|
+
saveConfig(config);
|
|
3493
|
+
console.log(`
|
|
3494
|
+
\u2705 Configuration saved to ${CONFIG_FILE}`);
|
|
3495
|
+
console.log("\nYou can now use:");
|
|
3496
|
+
console.log(" flightdesk project list - List projects");
|
|
3497
|
+
console.log(" flightdesk task create - Create a task");
|
|
3498
|
+
console.log(" flightdesk org list - Show organizations");
|
|
3499
|
+
console.log(" flightdesk org switch <name> - Switch active organization");
|
|
3500
|
+
} catch (error) {
|
|
3501
|
+
console.error(`
|
|
3502
|
+
\u274C Connection failed: ${error}`);
|
|
3503
|
+
console.log("\nPlease check:");
|
|
3504
|
+
console.log(" - Your API key is correct");
|
|
3505
|
+
console.log(" - You have network connectivity");
|
|
3506
|
+
console.log(" - The FlightDesk API is reachable");
|
|
3507
|
+
}
|
|
3508
|
+
} finally {
|
|
3509
|
+
rl.close();
|
|
3510
|
+
}
|
|
3511
|
+
}
|
|
3512
|
+
|
|
3513
|
+
// apps/cli/src/lib/session-monitor.ts
|
|
3514
|
+
var path2 = __toESM(require("node:path"));
|
|
3515
|
+
var os2 = __toESM(require("node:os"));
|
|
3516
|
+
var fs2 = __toESM(require("node:fs"));
|
|
3517
|
+
var readline2 = __toESM(require("node:readline"));
|
|
3518
|
+
var playwright = null;
|
|
3519
|
+
var USER_DATA_DIR = path2.join(os2.homedir(), ".flightdesk", "chromium-profile");
|
|
3520
|
+
async function isPlaywrightAvailable() {
|
|
3521
|
+
try {
|
|
3522
|
+
playwright = await import("playwright");
|
|
3523
|
+
return true;
|
|
3524
|
+
} catch {
|
|
3525
|
+
return false;
|
|
3526
|
+
}
|
|
3527
|
+
}
|
|
3528
|
+
function ensureUserDataDir() {
|
|
3529
|
+
if (!fs2.existsSync(USER_DATA_DIR)) {
|
|
3530
|
+
fs2.mkdirSync(USER_DATA_DIR, { recursive: true });
|
|
3531
|
+
}
|
|
3532
|
+
}
|
|
3533
|
+
async function launchBrowser(headless) {
|
|
3534
|
+
if (!playwright) {
|
|
3535
|
+
throw new Error("Playwright not available");
|
|
3536
|
+
}
|
|
3537
|
+
ensureUserDataDir();
|
|
3538
|
+
const context = await playwright.chromium.launchPersistentContext(USER_DATA_DIR, {
|
|
3539
|
+
headless,
|
|
3540
|
+
viewport: { width: 1280, height: 720 },
|
|
3541
|
+
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"
|
|
3542
|
+
});
|
|
3543
|
+
const browser = context.browser();
|
|
3544
|
+
return { browser, context };
|
|
3545
|
+
}
|
|
3546
|
+
async function checkAuth() {
|
|
3547
|
+
if (!await isPlaywrightAvailable()) {
|
|
3548
|
+
throw new Error("Playwright not installed");
|
|
3549
|
+
}
|
|
3550
|
+
const { context } = await launchBrowser(true);
|
|
3551
|
+
try {
|
|
3552
|
+
const page = await context.newPage();
|
|
3553
|
+
page.setDefaultTimeout(3e4);
|
|
3554
|
+
await page.goto("https://claude.ai/", { waitUntil: "domcontentloaded", timeout: 3e4 });
|
|
3555
|
+
await page.waitForTimeout(3e3);
|
|
3556
|
+
const url = page.url();
|
|
3557
|
+
console.log(" Final URL:", url);
|
|
3558
|
+
const isLoggedIn = !url.includes("/login") && !url.includes("/oauth") && !url.includes("accounts.google") && url.includes("claude.ai");
|
|
3559
|
+
return isLoggedIn;
|
|
3560
|
+
} catch (error) {
|
|
3561
|
+
console.error("Auth check failed:", error.message);
|
|
3562
|
+
return false;
|
|
3563
|
+
} finally {
|
|
3564
|
+
await context.close();
|
|
3565
|
+
}
|
|
3566
|
+
}
|
|
3567
|
+
async function openForLogin() {
|
|
3568
|
+
if (!await isPlaywrightAvailable()) {
|
|
3569
|
+
throw new Error("Playwright not installed");
|
|
3570
|
+
}
|
|
3571
|
+
console.log("Opening browser for Claude login...");
|
|
3572
|
+
const { context } = await launchBrowser(false);
|
|
3573
|
+
try {
|
|
3574
|
+
const page = await context.newPage();
|
|
3575
|
+
await page.goto("https://claude.ai/", { waitUntil: "domcontentloaded", timeout: 6e4 });
|
|
3576
|
+
console.log("\n\u{1F449} Press ENTER here when you have logged in...\n");
|
|
3577
|
+
await waitForEnter();
|
|
3578
|
+
console.log("Verifying login in current session...");
|
|
3579
|
+
await page.goto("https://claude.ai/", { waitUntil: "domcontentloaded", timeout: 3e4 });
|
|
3580
|
+
await page.waitForTimeout(2e3);
|
|
3581
|
+
const url = page.url();
|
|
3582
|
+
console.log(" Final URL:", url);
|
|
3583
|
+
const isLoggedIn = !url.includes("/login") && !url.includes("/oauth") && !url.includes("accounts.google") && url.includes("claude.ai");
|
|
3584
|
+
console.log("Closing browser...");
|
|
3585
|
+
return isLoggedIn;
|
|
3586
|
+
} finally {
|
|
3587
|
+
try {
|
|
3588
|
+
await context.close();
|
|
3589
|
+
} catch {
|
|
3590
|
+
}
|
|
3591
|
+
}
|
|
3592
|
+
}
|
|
3593
|
+
function waitForEnter() {
|
|
3594
|
+
return new Promise((resolve) => {
|
|
3595
|
+
const rl = readline2.createInterface({
|
|
3596
|
+
input: process.stdin,
|
|
3597
|
+
output: process.stdout
|
|
3598
|
+
});
|
|
3599
|
+
rl.question("", () => {
|
|
3600
|
+
rl.close();
|
|
3601
|
+
resolve();
|
|
3602
|
+
});
|
|
3603
|
+
});
|
|
3604
|
+
}
|
|
3605
|
+
async function monitorSession(sessionUrl, options = {}) {
|
|
3606
|
+
if (!await isPlaywrightAvailable()) {
|
|
3607
|
+
return { status: "error", error: "Playwright not installed" };
|
|
3608
|
+
}
|
|
3609
|
+
const { headless = true, timeout = 3e4, autoPr = false } = options;
|
|
3610
|
+
const { context } = await launchBrowser(headless);
|
|
3611
|
+
try {
|
|
3612
|
+
const page = await context.newPage();
|
|
3613
|
+
page.setDefaultTimeout(timeout);
|
|
3614
|
+
await page.goto(sessionUrl, { waitUntil: "domcontentloaded" });
|
|
3615
|
+
await page.waitForTimeout(2e3);
|
|
3616
|
+
const result = { status: "active" };
|
|
3617
|
+
const archiveIndicator = await page.$("text=This session has been archived");
|
|
3618
|
+
if (archiveIndicator) {
|
|
3619
|
+
return { status: "archived" };
|
|
3620
|
+
}
|
|
3621
|
+
const url = page.url();
|
|
3622
|
+
if (url.includes("/login") || url.includes("/oauth")) {
|
|
3623
|
+
return {
|
|
3624
|
+
status: "error",
|
|
3625
|
+
error: "Not logged in to Claude. Run: flightdesk auth"
|
|
3626
|
+
};
|
|
3627
|
+
}
|
|
3628
|
+
const branchName = await extractBranchName(page);
|
|
3629
|
+
if (branchName) {
|
|
3630
|
+
result.branchName = branchName;
|
|
3631
|
+
}
|
|
3632
|
+
const prButton = await page.$('button:has-text("Create PR"):not([aria-haspopup])');
|
|
3633
|
+
result.hasPrButton = !!prButton;
|
|
3634
|
+
if (autoPr && prButton) {
|
|
3635
|
+
console.log(' Clicking "Create PR" button...');
|
|
3636
|
+
await prButton.click();
|
|
3637
|
+
await page.waitForTimeout(5e3);
|
|
3638
|
+
const prUrl = await extractPrUrl(page);
|
|
3639
|
+
if (prUrl) {
|
|
3640
|
+
result.prUrl = prUrl;
|
|
3641
|
+
}
|
|
3642
|
+
}
|
|
3643
|
+
return result;
|
|
3644
|
+
} catch (error) {
|
|
3645
|
+
return {
|
|
3646
|
+
status: "error",
|
|
3647
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3648
|
+
};
|
|
3649
|
+
} finally {
|
|
3650
|
+
await context.close();
|
|
3606
3651
|
}
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3652
|
+
}
|
|
3653
|
+
async function extractBranchName(page) {
|
|
3654
|
+
const primarySelectors = [
|
|
3655
|
+
String.raw`button.group\/copy span.truncate`
|
|
3656
|
+
// New branch name (verified)
|
|
3657
|
+
];
|
|
3658
|
+
for (const selector of primarySelectors) {
|
|
3659
|
+
try {
|
|
3660
|
+
const element = await page.$(selector);
|
|
3661
|
+
if (element) {
|
|
3662
|
+
const text = await element.textContent();
|
|
3663
|
+
if (text) {
|
|
3664
|
+
const cleaned = text.trim();
|
|
3665
|
+
if (cleaned && cleaned.length > 2 && !cleaned.includes(" ")) {
|
|
3666
|
+
return cleaned;
|
|
3667
|
+
}
|
|
3616
3668
|
}
|
|
3617
3669
|
}
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
return result.userCreateProject;
|
|
3670
|
+
} catch {
|
|
3671
|
+
}
|
|
3621
3672
|
}
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3673
|
+
return null;
|
|
3674
|
+
}
|
|
3675
|
+
async function extractPrUrl(page) {
|
|
3676
|
+
const selectors = [
|
|
3677
|
+
'a[href*="github.com"][href*="/pull/"]',
|
|
3678
|
+
'a[href*="gitlab.com"][href*="/merge_requests/"]',
|
|
3679
|
+
'a[href*="bitbucket.org"][href*="/pull-requests/"]'
|
|
3680
|
+
];
|
|
3681
|
+
for (const selector of selectors) {
|
|
3682
|
+
try {
|
|
3683
|
+
const element = await page.$(selector);
|
|
3684
|
+
if (element) {
|
|
3685
|
+
const href = await element.getAttribute("href");
|
|
3686
|
+
if (href) {
|
|
3687
|
+
return href;
|
|
3631
3688
|
}
|
|
3632
3689
|
}
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
return result.userCreateTaskToken;
|
|
3690
|
+
} catch {
|
|
3691
|
+
}
|
|
3636
3692
|
}
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
reason
|
|
3648
|
-
}
|
|
3693
|
+
try {
|
|
3694
|
+
const pageContent = await page.content();
|
|
3695
|
+
const prPatterns = [
|
|
3696
|
+
/(https:\/\/github\.com\/[^/]+\/[^/]+\/pull\/\d+)/,
|
|
3697
|
+
/(https:\/\/gitlab\.com\/[^/]+\/[^/]+\/-\/merge_requests\/\d+)/
|
|
3698
|
+
];
|
|
3699
|
+
for (const pattern of prPatterns) {
|
|
3700
|
+
const match = pageContent.match(pattern);
|
|
3701
|
+
if (match && match[1]) {
|
|
3702
|
+
return match[1];
|
|
3649
3703
|
}
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
return result.userTaskPrompts;
|
|
3704
|
+
}
|
|
3705
|
+
} catch {
|
|
3653
3706
|
}
|
|
3654
|
-
|
|
3707
|
+
return null;
|
|
3708
|
+
}
|
|
3655
3709
|
|
|
3656
|
-
// apps/cli/src/commands/
|
|
3657
|
-
async function
|
|
3658
|
-
|
|
3659
|
-
|
|
3710
|
+
// apps/cli/src/commands/auth.ts
|
|
3711
|
+
async function authCommand() {
|
|
3712
|
+
console.log("\u{1F510} FlightDesk Authentication\n");
|
|
3713
|
+
const playwrightAvailable = await isPlaywrightAvailable();
|
|
3714
|
+
if (!playwrightAvailable) {
|
|
3715
|
+
console.error("Playwright is not installed.");
|
|
3716
|
+
console.error("Install with: pnpm add playwright && npx playwright install chromium");
|
|
3660
3717
|
process.exit(1);
|
|
3661
3718
|
}
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3719
|
+
console.log(`Profile directory: ${USER_DATA_DIR}
|
|
3720
|
+
`);
|
|
3721
|
+
console.log("Checking current authentication status...");
|
|
3722
|
+
const isAuthenticated = await checkAuth();
|
|
3723
|
+
if (isAuthenticated) {
|
|
3724
|
+
console.log("\u2705 Already logged in to Claude!");
|
|
3725
|
+
console.log("\nThe watch daemon will be able to monitor your sessions.");
|
|
3726
|
+
return;
|
|
3666
3727
|
}
|
|
3667
|
-
|
|
3728
|
+
console.log("\u274C Not logged in to Claude.\n");
|
|
3729
|
+
console.log("Opening browser for login...");
|
|
3730
|
+
console.log("Please log in to your Claude account.\n");
|
|
3731
|
+
const loginSuccessful = await openForLogin();
|
|
3732
|
+
if (loginSuccessful) {
|
|
3733
|
+
console.log("\n\u2705 Successfully logged in!");
|
|
3734
|
+
console.log("The watch daemon can now monitor your Claude Code sessions.");
|
|
3735
|
+
} else {
|
|
3736
|
+
console.log("\n\u274C Login was not detected.");
|
|
3737
|
+
console.log("Please try again with: flightdesk auth");
|
|
3738
|
+
}
|
|
3739
|
+
}
|
|
3740
|
+
|
|
3741
|
+
// apps/cli/src/commands/register.ts
|
|
3742
|
+
async function registerCommand(projectId, taskId, options) {
|
|
3743
|
+
const { config, org: org2 } = requireActiveOrg();
|
|
3744
|
+
const api = FlightDeskAPI.fromConfig(config, org2);
|
|
3668
3745
|
try {
|
|
3669
3746
|
let viewUrl = options.viewUrl;
|
|
3670
3747
|
let teleportId = options.teleportId;
|
|
@@ -3739,16 +3816,8 @@ function parseClaudeOutput(output) {
|
|
|
3739
3816
|
|
|
3740
3817
|
// apps/cli/src/commands/status.ts
|
|
3741
3818
|
async function statusCommand(options) {
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
process.exit(1);
|
|
3745
|
-
}
|
|
3746
|
-
const org2 = getDefaultOrganization();
|
|
3747
|
-
if (!org2) {
|
|
3748
|
-
console.error("No organization configured. Run: flightdesk init");
|
|
3749
|
-
process.exit(1);
|
|
3750
|
-
}
|
|
3751
|
-
const api = new FlightDeskAPI(org2);
|
|
3819
|
+
const { config, org: org2 } = requireActiveOrg();
|
|
3820
|
+
const api = FlightDeskAPI.fromConfig(config, org2);
|
|
3752
3821
|
try {
|
|
3753
3822
|
const tasks = await api.listTasks({ projectId: options.project });
|
|
3754
3823
|
if (tasks.length === 0) {
|
|
@@ -3833,15 +3902,7 @@ function getStatusEmoji(status) {
|
|
|
3833
3902
|
|
|
3834
3903
|
// apps/cli/src/commands/watch.ts
|
|
3835
3904
|
async function watchCommand(options) {
|
|
3836
|
-
|
|
3837
|
-
console.error("FlightDesk is not configured. Run: flightdesk init");
|
|
3838
|
-
process.exit(1);
|
|
3839
|
-
}
|
|
3840
|
-
const org2 = getDefaultOrganization();
|
|
3841
|
-
if (!org2) {
|
|
3842
|
-
console.error("No organization configured. Run: flightdesk init");
|
|
3843
|
-
process.exit(1);
|
|
3844
|
-
}
|
|
3905
|
+
const { config, org: org2 } = requireActiveOrg();
|
|
3845
3906
|
const intervalMinutes = parseInt(options.interval, 10);
|
|
3846
3907
|
if (isNaN(intervalMinutes) || intervalMinutes < 1) {
|
|
3847
3908
|
console.error("Invalid interval. Must be a positive number of minutes.");
|
|
@@ -3867,7 +3928,7 @@ async function watchCommand(options) {
|
|
|
3867
3928
|
console.log("");
|
|
3868
3929
|
}
|
|
3869
3930
|
}
|
|
3870
|
-
const api =
|
|
3931
|
+
const api = FlightDeskAPI.fromConfig(config, org2);
|
|
3871
3932
|
async function runCheck() {
|
|
3872
3933
|
const timestamp = (/* @__PURE__ */ new Date()).toLocaleTimeString();
|
|
3873
3934
|
console.log(`[${timestamp}] Checking tasks...`);
|
|
@@ -3961,16 +4022,8 @@ async function processSessionInfo(api, task2, info) {
|
|
|
3961
4022
|
|
|
3962
4023
|
// apps/cli/src/commands/task.ts
|
|
3963
4024
|
async function taskCommand(action, options) {
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
process.exit(1);
|
|
3967
|
-
}
|
|
3968
|
-
const org2 = getDefaultOrganization();
|
|
3969
|
-
if (!org2) {
|
|
3970
|
-
console.error("No organization configured. Run: flightdesk init");
|
|
3971
|
-
process.exit(1);
|
|
3972
|
-
}
|
|
3973
|
-
const api = new FlightDeskAPI(org2);
|
|
4025
|
+
const { config, org: org2 } = requireActiveOrg();
|
|
4026
|
+
const api = FlightDeskAPI.fromConfig(config, org2);
|
|
3974
4027
|
try {
|
|
3975
4028
|
switch (action) {
|
|
3976
4029
|
case "create":
|
|
@@ -4106,16 +4159,8 @@ function getStatusEmoji2(status) {
|
|
|
4106
4159
|
|
|
4107
4160
|
// apps/cli/src/commands/prompt.ts
|
|
4108
4161
|
async function promptCommand(taskId, options) {
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
process.exit(1);
|
|
4112
|
-
}
|
|
4113
|
-
const org2 = getDefaultOrganization();
|
|
4114
|
-
if (!org2) {
|
|
4115
|
-
console.error("No organization configured. Run: flightdesk init");
|
|
4116
|
-
process.exit(1);
|
|
4117
|
-
}
|
|
4118
|
-
const api = new FlightDeskAPI(org2);
|
|
4162
|
+
const { config, org: org2 } = requireActiveOrg();
|
|
4163
|
+
const api = FlightDeskAPI.fromConfig(config, org2);
|
|
4119
4164
|
const validTypes = ["review", "test_plan", "summary", "handoff"];
|
|
4120
4165
|
if (!validTypes.includes(options.type)) {
|
|
4121
4166
|
console.error(`Invalid prompt type: ${options.type}`);
|
|
@@ -4141,148 +4186,107 @@ async function promptCommand(taskId, options) {
|
|
|
4141
4186
|
}
|
|
4142
4187
|
|
|
4143
4188
|
// apps/cli/src/commands/org.ts
|
|
4144
|
-
var readline3 = __toESM(require("readline"));
|
|
4145
|
-
function question2(rl, prompt) {
|
|
4146
|
-
return new Promise((resolve) => {
|
|
4147
|
-
rl.question(prompt, (answer) => {
|
|
4148
|
-
resolve(answer.trim());
|
|
4149
|
-
});
|
|
4150
|
-
});
|
|
4151
|
-
}
|
|
4152
4189
|
async function orgListCommand() {
|
|
4190
|
+
if (!isConfigured()) {
|
|
4191
|
+
console.log("FlightDesk is not configured.");
|
|
4192
|
+
console.log("Run: flightdesk init");
|
|
4193
|
+
return;
|
|
4194
|
+
}
|
|
4153
4195
|
const config = loadConfig();
|
|
4196
|
+
const activeOrg = getActiveOrganization();
|
|
4197
|
+
console.log("\n\u{1F4CB} Organizations\n");
|
|
4154
4198
|
if (config.organizations.length === 0) {
|
|
4155
|
-
console.log("No organizations
|
|
4156
|
-
console.log("Run: flightdesk
|
|
4199
|
+
console.log("No organizations found.");
|
|
4200
|
+
console.log("Run: flightdesk org refresh");
|
|
4157
4201
|
return;
|
|
4158
4202
|
}
|
|
4159
|
-
console.log("\n\u{1F4CB} Connected Organizations\n");
|
|
4160
4203
|
for (const org2 of config.organizations) {
|
|
4161
|
-
const
|
|
4162
|
-
const
|
|
4163
|
-
console.log(` ${org2.name}${
|
|
4164
|
-
console.log(` ID: ${org2.id}`);
|
|
4165
|
-
console.log(` API: ${org2.apiUrl}`);
|
|
4166
|
-
console.log("");
|
|
4167
|
-
}
|
|
4168
|
-
const mappings = Object.entries(config.repoMapping);
|
|
4169
|
-
if (mappings.length > 0) {
|
|
4170
|
-
console.log("\u{1F4C1} Repository Mappings\n");
|
|
4171
|
-
for (const [repo, orgId] of mappings) {
|
|
4172
|
-
const org2 = config.organizations.find((o) => o.id === orgId);
|
|
4173
|
-
console.log(` ${repo} \u2192 ${org2?.name || orgId}`);
|
|
4174
|
-
}
|
|
4175
|
-
console.log("");
|
|
4204
|
+
const isActive = org2.id === activeOrg?.id;
|
|
4205
|
+
const activeTag = isActive ? " \u2190 active" : "";
|
|
4206
|
+
console.log(` ${isActive ? "\u25CF" : "\u25CB"} ${org2.name}${activeTag}`);
|
|
4176
4207
|
}
|
|
4208
|
+
console.log("");
|
|
4209
|
+
console.log("Commands:");
|
|
4210
|
+
console.log(" flightdesk org switch <name> Switch active organization");
|
|
4211
|
+
console.log(" flightdesk org refresh Refresh organizations from API");
|
|
4212
|
+
console.log("");
|
|
4177
4213
|
}
|
|
4178
|
-
async function
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
console.log("\n\u{1F517} Add Organization\n");
|
|
4184
|
-
try {
|
|
4185
|
-
const orgName = await question2(rl, "Organization name: ");
|
|
4186
|
-
if (!orgName) {
|
|
4187
|
-
console.error("Organization name is required");
|
|
4188
|
-
return;
|
|
4189
|
-
}
|
|
4190
|
-
const apiKey = await question2(rl, "API Key: ");
|
|
4191
|
-
if (!apiKey) {
|
|
4192
|
-
console.error("API Key is required");
|
|
4193
|
-
return;
|
|
4194
|
-
}
|
|
4195
|
-
const apiUrl = getApiUrl();
|
|
4196
|
-
console.log(`Using API: ${apiUrl}`);
|
|
4197
|
-
let orgId;
|
|
4198
|
-
const keyMatch = apiKey.match(/^fd_key_([^_]+)_/);
|
|
4199
|
-
if (keyMatch) {
|
|
4200
|
-
orgId = keyMatch[1];
|
|
4201
|
-
} else {
|
|
4202
|
-
orgId = `org_${Date.now()}`;
|
|
4203
|
-
}
|
|
4204
|
-
const org2 = {
|
|
4205
|
-
id: orgId,
|
|
4206
|
-
name: orgName,
|
|
4207
|
-
apiKey,
|
|
4208
|
-
apiUrl
|
|
4209
|
-
};
|
|
4210
|
-
console.log("\nTesting connection...");
|
|
4211
|
-
try {
|
|
4212
|
-
const response = await fetch(`${apiUrl}/graphql`, {
|
|
4213
|
-
method: "POST",
|
|
4214
|
-
headers: {
|
|
4215
|
-
"Content-Type": "application/json",
|
|
4216
|
-
"Authorization": `Bearer ${apiKey}`
|
|
4217
|
-
},
|
|
4218
|
-
body: JSON.stringify({
|
|
4219
|
-
query: "{ __typename }"
|
|
4220
|
-
})
|
|
4221
|
-
});
|
|
4222
|
-
if (!response.ok) {
|
|
4223
|
-
throw new Error(`API returned ${response.status}`);
|
|
4224
|
-
}
|
|
4225
|
-
console.log("\u2705 Connection successful!");
|
|
4226
|
-
} catch (error) {
|
|
4227
|
-
console.error(`\u274C Connection failed: ${error}`);
|
|
4228
|
-
const proceed = await question2(rl, "Save anyway? (y/N): ");
|
|
4229
|
-
if (proceed.toLowerCase() !== "y") {
|
|
4230
|
-
return;
|
|
4231
|
-
}
|
|
4232
|
-
}
|
|
4233
|
-
addOrganization(org2);
|
|
4234
|
-
console.log(`
|
|
4235
|
-
\u2705 Added organization: ${orgName}`);
|
|
4236
|
-
const config = loadConfig();
|
|
4237
|
-
if (config.organizations.length > 1) {
|
|
4238
|
-
const setDefault = await question2(rl, "Set as default? (y/N): ");
|
|
4239
|
-
if (setDefault.toLowerCase() === "y") {
|
|
4240
|
-
config.defaultOrganization = orgId;
|
|
4241
|
-
saveConfig(config);
|
|
4242
|
-
console.log("Set as default organization");
|
|
4243
|
-
}
|
|
4244
|
-
}
|
|
4245
|
-
} finally {
|
|
4246
|
-
rl.close();
|
|
4214
|
+
async function orgSwitchCommand(orgIdentifier) {
|
|
4215
|
+
if (!isConfigured()) {
|
|
4216
|
+
console.log("FlightDesk is not configured.");
|
|
4217
|
+
console.log("Run: flightdesk init");
|
|
4218
|
+
return;
|
|
4247
4219
|
}
|
|
4248
|
-
}
|
|
4249
|
-
async function orgRemoveCommand(orgId) {
|
|
4250
4220
|
const config = loadConfig();
|
|
4251
|
-
const org2 = config.organizations.find(
|
|
4221
|
+
const org2 = config.organizations.find(
|
|
4222
|
+
(o) => o.id === orgIdentifier || o.name.toLowerCase() === orgIdentifier.toLowerCase()
|
|
4223
|
+
);
|
|
4252
4224
|
if (!org2) {
|
|
4253
|
-
console.error(`Organization not found: ${
|
|
4225
|
+
console.error(`Organization not found: ${orgIdentifier}`);
|
|
4254
4226
|
console.log("\nAvailable organizations:");
|
|
4255
4227
|
for (const o of config.organizations) {
|
|
4256
|
-
console.log(` - ${o.name}
|
|
4228
|
+
console.log(` - ${o.name}`);
|
|
4257
4229
|
}
|
|
4258
4230
|
process.exit(1);
|
|
4259
4231
|
}
|
|
4260
|
-
const
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
try {
|
|
4265
|
-
const confirm = await question2(rl, `Remove "${org2.name}"? (y/N): `);
|
|
4266
|
-
if (confirm.toLowerCase() !== "y") {
|
|
4267
|
-
console.log("Cancelled");
|
|
4268
|
-
return;
|
|
4269
|
-
}
|
|
4270
|
-
removeOrganization(org2.id);
|
|
4271
|
-
console.log(`\u2705 Removed organization: ${org2.name}`);
|
|
4272
|
-
} finally {
|
|
4273
|
-
rl.close();
|
|
4232
|
+
const currentOrg = getActiveOrganization();
|
|
4233
|
+
if (currentOrg?.id === org2.id) {
|
|
4234
|
+
console.log(`Already using organization: ${org2.name}`);
|
|
4235
|
+
return;
|
|
4274
4236
|
}
|
|
4237
|
+
setActiveOrganization(org2.id);
|
|
4238
|
+
console.log(`\u2705 Switched to organization: ${org2.name}`);
|
|
4275
4239
|
}
|
|
4276
|
-
async function
|
|
4240
|
+
async function orgRefreshCommand() {
|
|
4241
|
+
if (!isConfigured()) {
|
|
4242
|
+
console.log("FlightDesk is not configured.");
|
|
4243
|
+
console.log("Run: flightdesk init");
|
|
4244
|
+
return;
|
|
4245
|
+
}
|
|
4277
4246
|
const config = loadConfig();
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
console.
|
|
4281
|
-
|
|
4247
|
+
if (!config.apiKey) {
|
|
4248
|
+
console.error("No API key configured.");
|
|
4249
|
+
console.log("Run: flightdesk init");
|
|
4250
|
+
return;
|
|
4251
|
+
}
|
|
4252
|
+
console.log("Refreshing organizations...");
|
|
4253
|
+
try {
|
|
4254
|
+
const apiUrl = getApiUrl();
|
|
4255
|
+
const userInfo = await fetchUserInfo(config.apiKey, apiUrl);
|
|
4256
|
+
if (userInfo.organizations.length === 0) {
|
|
4257
|
+
console.log("No organizations found for this user.");
|
|
4258
|
+
return;
|
|
4259
|
+
}
|
|
4260
|
+
const organizations = userInfo.organizations.map((m) => ({
|
|
4261
|
+
id: m.organization.id,
|
|
4262
|
+
name: m.organization.name
|
|
4263
|
+
}));
|
|
4264
|
+
const currentIds = new Set(config.organizations.map((o) => o.id));
|
|
4265
|
+
const newOrgs = organizations.filter((o) => !currentIds.has(o.id));
|
|
4266
|
+
const newIds = new Set(organizations.map((o) => o.id));
|
|
4267
|
+
const removedOrgs = config.organizations.filter((o) => !newIds.has(o.id));
|
|
4268
|
+
updateOrganizations(organizations);
|
|
4269
|
+
console.log(`\u2705 Found ${organizations.length} organization(s)`);
|
|
4270
|
+
if (newOrgs.length > 0) {
|
|
4271
|
+
console.log("\n New organizations:");
|
|
4272
|
+
for (const org2 of newOrgs) {
|
|
4273
|
+
console.log(` + ${org2.name}`);
|
|
4274
|
+
}
|
|
4275
|
+
}
|
|
4276
|
+
if (removedOrgs.length > 0) {
|
|
4277
|
+
console.log("\n Removed organizations:");
|
|
4278
|
+
for (const org2 of removedOrgs) {
|
|
4279
|
+
console.log(` - ${org2.name}`);
|
|
4280
|
+
}
|
|
4281
|
+
}
|
|
4282
|
+
const activeOrg = getActiveOrganization();
|
|
4283
|
+
if (activeOrg) {
|
|
4284
|
+
console.log(`
|
|
4285
|
+
Active: ${activeOrg.name}`);
|
|
4286
|
+
}
|
|
4287
|
+
} catch (error) {
|
|
4288
|
+
console.error(`\u274C Failed to refresh: ${error}`);
|
|
4282
4289
|
}
|
|
4283
|
-
config.defaultOrganization = org2.id;
|
|
4284
|
-
saveConfig(config);
|
|
4285
|
-
console.log(`\u2705 Default organization set to: ${org2.name}`);
|
|
4286
4290
|
}
|
|
4287
4291
|
|
|
4288
4292
|
// apps/cli/src/commands/context.ts
|
|
@@ -4318,11 +4322,12 @@ async function contextCommand() {
|
|
|
4318
4322
|
console.log("\u{1F4C1} Not in a git repository (or no remote configured)");
|
|
4319
4323
|
console.log("");
|
|
4320
4324
|
const config = loadConfig();
|
|
4325
|
+
const activeOrg2 = getActiveOrganization();
|
|
4321
4326
|
if (config.organizations.length > 0) {
|
|
4322
|
-
console.log("
|
|
4323
|
-
for (const
|
|
4324
|
-
const
|
|
4325
|
-
console.log(`
|
|
4327
|
+
console.log("Organizations:");
|
|
4328
|
+
for (const org2 of config.organizations) {
|
|
4329
|
+
const isActive = org2.id === activeOrg2?.id;
|
|
4330
|
+
console.log(` ${isActive ? "\u25CF" : "\u25CB"} ${org2.name}${isActive ? " (active)" : ""}`);
|
|
4326
4331
|
}
|
|
4327
4332
|
} else {
|
|
4328
4333
|
console.log("No organizations configured. Run: flightdesk init");
|
|
@@ -4332,10 +4337,11 @@ async function contextCommand() {
|
|
|
4332
4337
|
console.log(`\u{1F4C1} Repository: ${repoInfo.remote}`);
|
|
4333
4338
|
console.log(`\u{1F33F} Branch: ${repoInfo.branch}`);
|
|
4334
4339
|
console.log("");
|
|
4335
|
-
const
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
console.log(
|
|
4340
|
+
const mappedOrg = getOrganizationByRepo(repoInfo.remote);
|
|
4341
|
+
const activeOrg = getActiveOrganization();
|
|
4342
|
+
if (mappedOrg) {
|
|
4343
|
+
console.log(`\u{1F3E2} Organization: ${mappedOrg.name}`);
|
|
4344
|
+
console.log(` API: ${getApiUrl()}`);
|
|
4339
4345
|
console.log("");
|
|
4340
4346
|
console.log("This repository is mapped to the above organization.");
|
|
4341
4347
|
console.log("Commands will use this organization automatically.");
|
|
@@ -4344,10 +4350,10 @@ async function contextCommand() {
|
|
|
4344
4350
|
console.log("");
|
|
4345
4351
|
const config = loadConfig();
|
|
4346
4352
|
if (config.organizations.length > 0) {
|
|
4347
|
-
console.log("
|
|
4348
|
-
for (const
|
|
4349
|
-
const
|
|
4350
|
-
console.log(`
|
|
4353
|
+
console.log("Organizations:");
|
|
4354
|
+
for (const org2 of config.organizations) {
|
|
4355
|
+
const isActive = org2.id === activeOrg?.id;
|
|
4356
|
+
console.log(` ${isActive ? "\u25CF" : "\u25CB"} ${org2.name}${isActive ? " (active)" : ""}`);
|
|
4351
4357
|
}
|
|
4352
4358
|
console.log("");
|
|
4353
4359
|
console.log("Run: flightdesk sync to refresh repository mappings");
|
|
@@ -4359,7 +4365,7 @@ async function contextCommand() {
|
|
|
4359
4365
|
|
|
4360
4366
|
// apps/cli/src/commands/sync.ts
|
|
4361
4367
|
async function syncCommand() {
|
|
4362
|
-
const config =
|
|
4368
|
+
const config = requireConfig();
|
|
4363
4369
|
if (config.organizations.length === 0) {
|
|
4364
4370
|
console.error("No organizations configured. Run: flightdesk init");
|
|
4365
4371
|
process.exit(1);
|
|
@@ -4370,7 +4376,7 @@ async function syncCommand() {
|
|
|
4370
4376
|
for (const org2 of config.organizations) {
|
|
4371
4377
|
console.log(` ${org2.name}...`);
|
|
4372
4378
|
try {
|
|
4373
|
-
const api =
|
|
4379
|
+
const api = FlightDeskAPI.fromConfig(config, org2);
|
|
4374
4380
|
const projects = await api.listProjects();
|
|
4375
4381
|
console.log(` Found ${projects.length} project(s)`);
|
|
4376
4382
|
for (const project of projects) {
|
|
@@ -4554,15 +4560,7 @@ var ClaudeSelectors = {
|
|
|
4554
4560
|
var fs3 = __toESM(require("fs"));
|
|
4555
4561
|
var playwright2 = null;
|
|
4556
4562
|
async function importCommand(options) {
|
|
4557
|
-
|
|
4558
|
-
console.error("FlightDesk is not configured. Run: flightdesk init");
|
|
4559
|
-
process.exit(1);
|
|
4560
|
-
}
|
|
4561
|
-
const org2 = getDefaultOrganization();
|
|
4562
|
-
if (!org2) {
|
|
4563
|
-
console.error("No organization configured. Run: flightdesk init");
|
|
4564
|
-
process.exit(1);
|
|
4565
|
-
}
|
|
4563
|
+
const { config, org: org2 } = requireActiveOrg();
|
|
4566
4564
|
if (!await isPlaywrightAvailable()) {
|
|
4567
4565
|
console.error("Playwright is required for import. Install it with:");
|
|
4568
4566
|
console.error(" npm install -g playwright");
|
|
@@ -4570,7 +4568,7 @@ async function importCommand(options) {
|
|
|
4570
4568
|
process.exit(1);
|
|
4571
4569
|
}
|
|
4572
4570
|
playwright2 = await import("playwright");
|
|
4573
|
-
const api =
|
|
4571
|
+
const api = FlightDeskAPI.fromConfig(config, org2);
|
|
4574
4572
|
console.log("\n\u{1F50D} FlightDesk Import - Scanning Claude Sessions\n");
|
|
4575
4573
|
if (options.dryRun) {
|
|
4576
4574
|
console.log("\u{1F4CB} DRY RUN - No changes will be made\n");
|
|
@@ -4797,7 +4795,7 @@ async function scanClaudeSessions(options) {
|
|
|
4797
4795
|
|
|
4798
4796
|
// apps/cli/src/main.ts
|
|
4799
4797
|
var program2 = new Command();
|
|
4800
|
-
program2.name("flightdesk").description("FlightDesk CLI - AI task management for Claude Code sessions").version("0.1.
|
|
4798
|
+
program2.name("flightdesk").description("FlightDesk CLI - AI task management for Claude Code sessions").version("0.1.10").option("--dev", "Use local development API (localhost:3000)").option("--api <url>", "Use custom API URL");
|
|
4801
4799
|
program2.hook("preAction", () => {
|
|
4802
4800
|
const opts = program2.opts();
|
|
4803
4801
|
if (opts.api) {
|
|
@@ -4822,10 +4820,9 @@ program2.command("import").description("Scan Claude.ai for existing sessions and
|
|
|
4822
4820
|
program2.command("status").description("Show status of all active tasks").option("-p, --project <id>", "Filter by project").action(statusCommand);
|
|
4823
4821
|
program2.command("prompt <task-id>").description("Get a prompt for a task (ready to paste into Claude)").option("--type <type>", "Prompt type: review, test_plan, summary, handoff", "review").action(promptCommand);
|
|
4824
4822
|
var org = program2.command("org").description("Organization management");
|
|
4825
|
-
org.command("list").description("List
|
|
4826
|
-
org.command("
|
|
4827
|
-
org.command("
|
|
4828
|
-
org.command("default <org-id>").description("Set default organization").action(orgSetDefaultCommand);
|
|
4823
|
+
org.command("list").description("List your organizations").action(orgListCommand);
|
|
4824
|
+
org.command("switch <name>").description("Switch active organization").action(orgSwitchCommand);
|
|
4825
|
+
org.command("refresh").description("Refresh organizations from API").action(orgRefreshCommand);
|
|
4829
4826
|
program2.command("context").description("Show current repository context and mapped organization").action(contextCommand);
|
|
4830
4827
|
program2.command("sync").description("Refresh project-to-repository mappings from all organizations").action(syncCommand);
|
|
4831
4828
|
program2.command("mount <task-id>").description("Mount preview environment filesystem via SSHFS").option("--vscode", "Open in VS Code after mounting").action((taskId) => {
|