flightdesk 0.2.0 → 0.2.1
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 +145 -20
- package/main.js.map +4 -4
- package/package.json +1 -1
package/main.js
CHANGED
|
@@ -3800,11 +3800,54 @@ async function authCommand() {
|
|
|
3800
3800
|
}
|
|
3801
3801
|
}
|
|
3802
3802
|
|
|
3803
|
+
// apps/cli/src/lib/git.ts
|
|
3804
|
+
var import_node_child_process = require("node:child_process");
|
|
3805
|
+
function detectGitRepo() {
|
|
3806
|
+
try {
|
|
3807
|
+
const remoteUrl = (0, import_node_child_process.execSync)("git remote get-url origin", {
|
|
3808
|
+
encoding: "utf-8",
|
|
3809
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
3810
|
+
}).trim();
|
|
3811
|
+
const repoFullName = parseGitRemoteUrl(remoteUrl);
|
|
3812
|
+
if (!repoFullName) {
|
|
3813
|
+
return null;
|
|
3814
|
+
}
|
|
3815
|
+
const branch = (0, import_node_child_process.execSync)("git rev-parse --abbrev-ref HEAD", {
|
|
3816
|
+
encoding: "utf-8",
|
|
3817
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
3818
|
+
}).trim();
|
|
3819
|
+
return {
|
|
3820
|
+
remote: repoFullName,
|
|
3821
|
+
branch,
|
|
3822
|
+
remoteUrl
|
|
3823
|
+
};
|
|
3824
|
+
} catch {
|
|
3825
|
+
return null;
|
|
3826
|
+
}
|
|
3827
|
+
}
|
|
3828
|
+
function parseGitRemoteUrl(remoteUrl) {
|
|
3829
|
+
const sshPattern = /git@github\.com:([^/]+\/[^/]+?)(?:\.git)?$/;
|
|
3830
|
+
const sshMatch = sshPattern.exec(remoteUrl);
|
|
3831
|
+
if (sshMatch) {
|
|
3832
|
+
return sshMatch[1];
|
|
3833
|
+
}
|
|
3834
|
+
const httpsPattern = /https:\/\/github\.com\/([^/]+\/[^/]+?)(?:\.git)?$/;
|
|
3835
|
+
const httpsMatch = httpsPattern.exec(remoteUrl);
|
|
3836
|
+
if (httpsMatch) {
|
|
3837
|
+
return httpsMatch[1];
|
|
3838
|
+
}
|
|
3839
|
+
return null;
|
|
3840
|
+
}
|
|
3841
|
+
|
|
3803
3842
|
// apps/cli/src/commands/register.ts
|
|
3804
|
-
async function registerCommand(
|
|
3843
|
+
async function registerCommand(taskId, options) {
|
|
3805
3844
|
const { config, org: org2 } = requireActiveOrg();
|
|
3806
3845
|
const api = FlightDeskAPI.fromConfig(config, org2);
|
|
3807
3846
|
try {
|
|
3847
|
+
let projectId = options.project;
|
|
3848
|
+
if (!projectId) {
|
|
3849
|
+
projectId = await autoDetectProject(api, org2);
|
|
3850
|
+
}
|
|
3808
3851
|
let viewUrl = options.viewUrl;
|
|
3809
3852
|
let teleportId = options.teleportId;
|
|
3810
3853
|
if (!process.stdin.isTTY) {
|
|
@@ -3875,6 +3918,53 @@ function parseClaudeOutput(output) {
|
|
|
3875
3918
|
}
|
|
3876
3919
|
return result;
|
|
3877
3920
|
}
|
|
3921
|
+
async function autoDetectProject(api, activeOrg) {
|
|
3922
|
+
const repoInfo = detectGitRepo();
|
|
3923
|
+
if (!repoInfo) {
|
|
3924
|
+
console.error("\u274C Not in a git repository. Please provide a project ID.");
|
|
3925
|
+
console.error(" Usage: flightdesk register --project <project-id> [task-id]");
|
|
3926
|
+
process.exit(1);
|
|
3927
|
+
}
|
|
3928
|
+
console.log(`\u{1F50D} Detecting project from repository: ${repoInfo.remote}`);
|
|
3929
|
+
const mappedOrg = getOrganizationByRepo(repoInfo.remote);
|
|
3930
|
+
if (!mappedOrg) {
|
|
3931
|
+
console.error(`\u274C Repository "${repoInfo.remote}" is not mapped to any organization.`);
|
|
3932
|
+
console.error(" Run: flightdesk sync");
|
|
3933
|
+
console.error(" Or provide a project ID: flightdesk register --project <project-id> [task-id]");
|
|
3934
|
+
process.exit(1);
|
|
3935
|
+
}
|
|
3936
|
+
if (mappedOrg.id !== activeOrg.id) {
|
|
3937
|
+
console.error(`\u274C Repository "${repoInfo.remote}" is mapped to organization "${mappedOrg.name}",`);
|
|
3938
|
+
console.error(` but your active organization is "${activeOrg.name}".`);
|
|
3939
|
+
console.error(" Switch with: flightdesk org switch " + mappedOrg.name);
|
|
3940
|
+
console.error(" Or provide a project ID: flightdesk register --project <project-id> [task-id]");
|
|
3941
|
+
process.exit(1);
|
|
3942
|
+
}
|
|
3943
|
+
const projects = await api.listProjects();
|
|
3944
|
+
const matchingProjects = projects.filter(
|
|
3945
|
+
(p) => p.githubRepo?.toLowerCase() === repoInfo.remote.toLowerCase()
|
|
3946
|
+
);
|
|
3947
|
+
if (matchingProjects.length === 0) {
|
|
3948
|
+
console.error(`\u274C No FlightDesk project found for repository "${repoInfo.remote}".`);
|
|
3949
|
+
console.error(" Available projects in this organization:");
|
|
3950
|
+
for (const p of projects) {
|
|
3951
|
+
console.error(` - ${p.name} (${p.githubRepo || "no repo"}) [${p.id}]`);
|
|
3952
|
+
}
|
|
3953
|
+
console.error("\n Create a project or provide a project ID explicitly.");
|
|
3954
|
+
process.exit(1);
|
|
3955
|
+
}
|
|
3956
|
+
if (matchingProjects.length > 1) {
|
|
3957
|
+
console.error(`\u274C Multiple projects found for repository "${repoInfo.remote}":`);
|
|
3958
|
+
for (const p of matchingProjects) {
|
|
3959
|
+
console.error(` - ${p.name} [${p.id}]`);
|
|
3960
|
+
}
|
|
3961
|
+
console.error("\n Please specify the project ID explicitly.");
|
|
3962
|
+
process.exit(1);
|
|
3963
|
+
}
|
|
3964
|
+
const project2 = matchingProjects[0];
|
|
3965
|
+
console.log(`\u2705 Found project: ${project2.name} (${project2.id})`);
|
|
3966
|
+
return project2.id;
|
|
3967
|
+
}
|
|
3878
3968
|
|
|
3879
3969
|
// apps/cli/src/commands/status.ts
|
|
3880
3970
|
async function statusCommand(options) {
|
|
@@ -4941,10 +5031,27 @@ ${projects.length} project(s)`);
|
|
|
4941
5031
|
}
|
|
4942
5032
|
|
|
4943
5033
|
// apps/cli/src/commands/preview.ts
|
|
4944
|
-
var
|
|
4945
|
-
var path3 = __toESM(require("path"));
|
|
4946
|
-
var os3 = __toESM(require("os"));
|
|
4947
|
-
var fs4 = __toESM(require("fs"));
|
|
5034
|
+
var import_node_child_process2 = require("node:child_process");
|
|
5035
|
+
var path3 = __toESM(require("node:path"));
|
|
5036
|
+
var os3 = __toESM(require("node:os"));
|
|
5037
|
+
var fs4 = __toESM(require("node:fs"));
|
|
5038
|
+
function isValidContainerId(id) {
|
|
5039
|
+
return /^[a-fA-F0-9]+$/.test(id) && id.length >= 12 && id.length <= 64;
|
|
5040
|
+
}
|
|
5041
|
+
function validateSSHParams(instance) {
|
|
5042
|
+
if (instance.containerId && !isValidContainerId(instance.containerId)) {
|
|
5043
|
+
throw new Error(`Invalid container ID format: ${instance.containerId}`);
|
|
5044
|
+
}
|
|
5045
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(instance.sshUser)) {
|
|
5046
|
+
throw new Error(`Invalid SSH user format: ${instance.sshUser}`);
|
|
5047
|
+
}
|
|
5048
|
+
if (!/^[a-zA-Z0-9.-]+$/.test(instance.sshHost)) {
|
|
5049
|
+
throw new Error(`Invalid SSH host format: ${instance.sshHost}`);
|
|
5050
|
+
}
|
|
5051
|
+
if (!Number.isInteger(instance.sshPort) || instance.sshPort < 1 || instance.sshPort > 65535) {
|
|
5052
|
+
throw new Error(`Invalid SSH port: ${instance.sshPort}`);
|
|
5053
|
+
}
|
|
5054
|
+
}
|
|
4948
5055
|
async function previewCommand(action, options) {
|
|
4949
5056
|
const { config, org: org2 } = requireActiveOrg();
|
|
4950
5057
|
const api = FlightDeskAPI.fromConfig(config, org2);
|
|
@@ -5028,8 +5135,9 @@ async function handleLogs(api, options) {
|
|
|
5028
5135
|
`);
|
|
5029
5136
|
console.log(`(Press Ctrl+C to stop)
|
|
5030
5137
|
`);
|
|
5138
|
+
validateSSHParams(instance);
|
|
5031
5139
|
const sshCommand = `docker logs -f ${instance.containerId}`;
|
|
5032
|
-
const ssh = (0,
|
|
5140
|
+
const ssh = (0, import_node_child_process2.spawn)("ssh", [
|
|
5033
5141
|
"-o",
|
|
5034
5142
|
"StrictHostKeyChecking=no",
|
|
5035
5143
|
"-o",
|
|
@@ -5069,7 +5177,7 @@ async function handleMount(api, options) {
|
|
|
5069
5177
|
await new Promise((resolve) => setTimeout(resolve, 3e3));
|
|
5070
5178
|
}
|
|
5071
5179
|
try {
|
|
5072
|
-
(0,
|
|
5180
|
+
(0, import_node_child_process2.execSync)("which sshfs", { stdio: "ignore" });
|
|
5073
5181
|
} catch {
|
|
5074
5182
|
console.error("\u274C sshfs is not installed.");
|
|
5075
5183
|
console.error("");
|
|
@@ -5083,12 +5191,14 @@ async function handleMount(api, options) {
|
|
|
5083
5191
|
}
|
|
5084
5192
|
process.exit(1);
|
|
5085
5193
|
}
|
|
5086
|
-
const
|
|
5194
|
+
const rawTaskIdPrefix = options.taskId.substring(0, 8);
|
|
5195
|
+
const safeTaskId = path3.basename(rawTaskIdPrefix).replaceAll(/[^a-zA-Z0-9_-]/g, "") || "task";
|
|
5196
|
+
const mountDir = options.directory || path3.join(os3.homedir(), "flightdesk-mounts", safeTaskId);
|
|
5087
5197
|
if (!fs4.existsSync(mountDir)) {
|
|
5088
5198
|
fs4.mkdirSync(mountDir, { recursive: true });
|
|
5089
5199
|
}
|
|
5090
5200
|
try {
|
|
5091
|
-
const mounted = (0,
|
|
5201
|
+
const mounted = (0, import_node_child_process2.execSync)("mount", { encoding: "utf8" });
|
|
5092
5202
|
if (mounted.includes(mountDir)) {
|
|
5093
5203
|
console.log(`\u{1F4C1} Already mounted at ${mountDir}`);
|
|
5094
5204
|
return;
|
|
@@ -5096,8 +5206,8 @@ async function handleMount(api, options) {
|
|
|
5096
5206
|
} catch {
|
|
5097
5207
|
}
|
|
5098
5208
|
console.log(`\u{1F4C1} Mounting preview environment to ${mountDir}...`);
|
|
5099
|
-
|
|
5100
|
-
|
|
5209
|
+
validateSSHParams(instance);
|
|
5210
|
+
const sshfsArgs = [
|
|
5101
5211
|
"-o",
|
|
5102
5212
|
"StrictHostKeyChecking=no",
|
|
5103
5213
|
"-o",
|
|
@@ -5114,7 +5224,10 @@ async function handleMount(api, options) {
|
|
|
5114
5224
|
mountDir
|
|
5115
5225
|
];
|
|
5116
5226
|
try {
|
|
5117
|
-
(0,
|
|
5227
|
+
const result = (0, import_node_child_process2.spawnSync)("sshfs", sshfsArgs, { stdio: "inherit" });
|
|
5228
|
+
if (result.status !== 0) {
|
|
5229
|
+
throw new Error(`sshfs exited with code ${result.status}`);
|
|
5230
|
+
}
|
|
5118
5231
|
console.log("");
|
|
5119
5232
|
console.log("\u2705 Mounted successfully!");
|
|
5120
5233
|
console.log(` Location: ${mountDir}`);
|
|
@@ -5134,21 +5247,27 @@ async function handleMount(api, options) {
|
|
|
5134
5247
|
}
|
|
5135
5248
|
}
|
|
5136
5249
|
async function handleUnmount(_api, options) {
|
|
5137
|
-
const
|
|
5250
|
+
const rawTaskIdPrefix = options.taskId.substring(0, 8);
|
|
5251
|
+
const safeTaskId = path3.basename(rawTaskIdPrefix).replaceAll(/[^a-zA-Z0-9_-]/g, "") || "task";
|
|
5252
|
+
const mountDir = path3.join(os3.homedir(), "flightdesk-mounts", safeTaskId);
|
|
5138
5253
|
if (!fs4.existsSync(mountDir)) {
|
|
5139
5254
|
console.log("Mount directory does not exist");
|
|
5140
5255
|
return;
|
|
5141
5256
|
}
|
|
5142
5257
|
console.log(`\u{1F4C1} Unmounting ${mountDir}...`);
|
|
5143
5258
|
try {
|
|
5259
|
+
let result;
|
|
5144
5260
|
if (process.platform === "darwin") {
|
|
5145
|
-
(0,
|
|
5261
|
+
result = (0, import_node_child_process2.spawnSync)("umount", [mountDir], { stdio: "inherit" });
|
|
5146
5262
|
} else {
|
|
5147
|
-
(0,
|
|
5263
|
+
result = (0, import_node_child_process2.spawnSync)("fusermount", ["-u", mountDir], { stdio: "inherit" });
|
|
5264
|
+
}
|
|
5265
|
+
if (result.status !== 0) {
|
|
5266
|
+
throw new Error(`Unmount exited with code ${result.status}`);
|
|
5148
5267
|
}
|
|
5149
5268
|
console.log("\u2705 Unmounted successfully");
|
|
5150
5269
|
try {
|
|
5151
|
-
fs4.
|
|
5270
|
+
fs4.rmSync(mountDir, { recursive: true, force: true });
|
|
5152
5271
|
} catch {
|
|
5153
5272
|
}
|
|
5154
5273
|
} catch (error) {
|
|
@@ -5181,7 +5300,7 @@ async function handleTeardown(api, options) {
|
|
|
5181
5300
|
|
|
5182
5301
|
// apps/cli/src/main.ts
|
|
5183
5302
|
var program2 = new Command();
|
|
5184
|
-
program2.name("flightdesk").description("FlightDesk CLI - AI task management for Claude Code sessions").version("0.2.
|
|
5303
|
+
program2.name("flightdesk").description("FlightDesk CLI - AI task management for Claude Code sessions").version("0.2.1").option("--dev", "Use local development API (localhost:3000)").option("--api <url>", "Use custom API URL");
|
|
5185
5304
|
program2.hook("preAction", () => {
|
|
5186
5305
|
const opts = program2.opts();
|
|
5187
5306
|
if (opts.api) {
|
|
@@ -5195,7 +5314,7 @@ program2.hook("preAction", () => {
|
|
|
5195
5314
|
});
|
|
5196
5315
|
program2.command("init").description("Configure FlightDesk CLI with your API credentials").action(initCommand);
|
|
5197
5316
|
program2.command("auth").description("Log in to Claude for session monitoring").action(authCommand);
|
|
5198
|
-
program2.command("register
|
|
5317
|
+
program2.command("register [task-id]").description("Register a Claude Code session with a FlightDesk task (auto-detects project from git repo)").option("-p, --project <id>", "Project ID (auto-detected from git repo if not provided)").option("--view-url <url>", "Claude Code session view URL").option("--teleport-id <id>", "Claude Code teleport ID").option("--title <title>", "Task title (creates new task if task-id not provided)").option("--description <description>", "Task description").action(registerCommand);
|
|
5199
5318
|
var project = program2.command("project").description("Project management commands");
|
|
5200
5319
|
project.command("list").description("List projects in the active organization").action(() => projectCommand("list", {}));
|
|
5201
5320
|
var task = program2.command("task").description("Task management commands");
|
|
@@ -5215,7 +5334,10 @@ program2.command("context").description("Show current repository context and map
|
|
|
5215
5334
|
program2.command("sync").description("Refresh project-to-repository mappings from all organizations").action(syncCommand);
|
|
5216
5335
|
var preview = program2.command("preview").description("Preview environment management");
|
|
5217
5336
|
preview.command("status <task-id>").description("Show preview environment status").action((taskId) => previewCommand("status", { taskId }));
|
|
5218
|
-
preview.command("logs <task-id>").description("Get logs from preview environment").option("-n, --lines <lines>", "Number of log lines", "100").option("-f, --follow", "Follow log output").action((taskId, options) =>
|
|
5337
|
+
preview.command("logs <task-id>").description("Get logs from preview environment").option("-n, --lines <lines>", "Number of log lines (1-10000)", "100").option("-f, --follow", "Follow log output").action((taskId, options) => {
|
|
5338
|
+
const lines = Math.min(Math.max(Number.parseInt(options.lines || "100"), 1), 1e4);
|
|
5339
|
+
previewCommand("logs", { taskId, lines, follow: options.follow });
|
|
5340
|
+
});
|
|
5219
5341
|
preview.command("mount <task-id>").description("Mount preview environment filesystem via SSHFS").option("-d, --directory <path>", "Custom mount directory").action((taskId, options) => previewCommand("mount", { taskId, directory: options.directory }));
|
|
5220
5342
|
preview.command("unmount <task-id>").description("Unmount preview environment filesystem").action((taskId) => previewCommand("unmount", { taskId }));
|
|
5221
5343
|
preview.command("restart <task-id>").description("Restart preview environment processes").action((taskId) => previewCommand("restart", { taskId }));
|
|
@@ -5223,6 +5345,9 @@ preview.command("resume <task-id>").description("Resume a suspended preview envi
|
|
|
5223
5345
|
preview.command("teardown <task-id>").description("Tear down preview environment").action((taskId) => previewCommand("teardown", { taskId }));
|
|
5224
5346
|
program2.command("mount <task-id>").description('Mount preview environment filesystem (shorthand for "preview mount")').option("-d, --directory <path>", "Custom mount directory").action((taskId, options) => previewCommand("mount", { taskId, directory: options.directory }));
|
|
5225
5347
|
program2.command("unmount <task-id>").description('Unmount preview environment filesystem (shorthand for "preview unmount")').action((taskId) => previewCommand("unmount", { taskId }));
|
|
5226
|
-
program2.command("logs <task-id>").description('Get logs from preview environment (
|
|
5348
|
+
program2.command("logs <task-id>").description('Get logs from preview environment (equivalent to "preview logs")').option("-n, --lines <lines>", "Number of log lines (1-10000)", "100").option("-f, --follow", "Follow log output").action((taskId, options) => {
|
|
5349
|
+
const lines = Math.min(Math.max(Number.parseInt(options.lines || "100"), 1), 1e4);
|
|
5350
|
+
previewCommand("logs", { taskId, lines, follow: options.follow });
|
|
5351
|
+
});
|
|
5227
5352
|
program2.parse();
|
|
5228
5353
|
//# sourceMappingURL=main.js.map
|