inflight-cli 2.1.0 → 2.1.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/README.md +1 -2
- package/dist/commands/setup.js +2 -2
- package/dist/commands/share.js +32 -27
- package/dist/commands/vercel.js +64 -143
- package/dist/index.js +7 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -16,8 +16,7 @@ That's it. The CLI installs globally, logs you in, adds the widget to your proje
|
|
|
16
16
|
| --------------------- | -------------------------------- |
|
|
17
17
|
| `inflight setup` | Set up Inflight in your project |
|
|
18
18
|
| `inflight share` | Share a staging URL for feedback |
|
|
19
|
-
| `inflight
|
|
20
|
-
| `inflight workspaces` | Change your Inflight workspace |
|
|
19
|
+
| `inflight workspace` | Get or set your active workspace |
|
|
21
20
|
| `inflight login` | Log in to your Inflight account |
|
|
22
21
|
| `inflight logout` | Log out of your Inflight account |
|
|
23
22
|
|
package/dist/commands/setup.js
CHANGED
|
@@ -55,7 +55,7 @@ export async function setupCommand() {
|
|
|
55
55
|
const existingWorkspace = existingConfig ? workspaces.find((w) => w.id === existingConfig.workspaceId) : null;
|
|
56
56
|
if (existingWorkspace) {
|
|
57
57
|
workspaceId = existingWorkspace.id;
|
|
58
|
-
p.log.success(`Workspace: ${pc.bold(existingWorkspace.name)} ${pc.dim("(change anytime with inflight
|
|
58
|
+
p.log.success(`Workspace: ${pc.bold(existingWorkspace.name)} ${pc.dim("(change anytime with inflight workspace)")}`);
|
|
59
59
|
}
|
|
60
60
|
else if (workspaces.length === 0) {
|
|
61
61
|
p.log.error("No workspaces found. Create one at " + pc.cyan("inflight.co") + " first.");
|
|
@@ -67,7 +67,7 @@ export async function setupCommand() {
|
|
|
67
67
|
}
|
|
68
68
|
else {
|
|
69
69
|
const selected = await p.select({
|
|
70
|
-
message: "Select a workspace " + pc.dim("(change anytime with inflight
|
|
70
|
+
message: "Select a workspace " + pc.dim("(change anytime with inflight workspace)"),
|
|
71
71
|
options: workspaces.map((w) => ({ value: w.id, label: w.name })),
|
|
72
72
|
});
|
|
73
73
|
if (p.isCancel(selected)) {
|
package/dist/commands/share.js
CHANGED
|
@@ -7,6 +7,7 @@ import { providers } from "../providers/index.js";
|
|
|
7
7
|
import { apiGetMe, apiCreateVersion } from "../lib/api.js";
|
|
8
8
|
export async function shareCommand(opts = {}) {
|
|
9
9
|
const cwd = process.cwd();
|
|
10
|
+
// ── Step 1: Auth ──
|
|
10
11
|
const auth = readGlobalAuth();
|
|
11
12
|
if (!auth) {
|
|
12
13
|
if (opts.json) {
|
|
@@ -48,37 +49,41 @@ export async function shareCommand(opts = {}) {
|
|
|
48
49
|
await open(stagingUrl);
|
|
49
50
|
return;
|
|
50
51
|
}
|
|
51
|
-
// ──
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
52
|
+
// ── Step 2: Get workspaces ──
|
|
53
|
+
const me = await apiGetMe(auth.apiKey).catch((e) => {
|
|
54
|
+
p.log.error(e.message);
|
|
55
|
+
process.exit(1);
|
|
56
|
+
});
|
|
57
|
+
const workspaces = me.workspaces;
|
|
58
|
+
let workspaceId;
|
|
59
|
+
// Check if a workspace is already configured and still valid
|
|
60
|
+
const existingConfig = readWorkspaceConfig();
|
|
61
|
+
const existingWorkspace = existingConfig ? workspaces.find((w) => w.id === existingConfig.workspaceId) : null;
|
|
62
|
+
if (existingWorkspace) {
|
|
63
|
+
workspaceId = existingWorkspace.id;
|
|
64
|
+
}
|
|
65
|
+
else if (workspaces.length === 0) {
|
|
66
|
+
p.log.error("No workspaces found. Create one at " + pc.cyan("inflight.co") + " first.");
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
else if (me.workspaces.length === 1) {
|
|
70
|
+
workspaceId = me.workspaces[0].id;
|
|
71
|
+
p.log.success(`Workspace: ${pc.bold(workspaces[0].name)}`);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
const selected = await p.select({
|
|
75
|
+
message: "Select a workspace " + pc.dim("(change anytime with inflight workspace)"),
|
|
76
|
+
options: me.workspaces.map((w) => ({ value: w.id, label: w.name })),
|
|
58
77
|
});
|
|
59
|
-
if (
|
|
60
|
-
p.
|
|
61
|
-
process.exit(
|
|
62
|
-
}
|
|
63
|
-
else if (me.workspaces.length === 1) {
|
|
64
|
-
workspaceId = me.workspaces[0].id;
|
|
65
|
-
p.log.info(`Workspace: ${pc.bold(me.workspaces[0].name)}`);
|
|
66
|
-
}
|
|
67
|
-
else {
|
|
68
|
-
const selected = await p.select({
|
|
69
|
-
message: "Select a workspace " + pc.dim("(change anytime with inflight workspaces)"),
|
|
70
|
-
options: me.workspaces.map((w) => ({ value: w.id, label: w.name })),
|
|
71
|
-
});
|
|
72
|
-
if (p.isCancel(selected)) {
|
|
73
|
-
p.cancel("Cancelled.");
|
|
74
|
-
process.exit(0);
|
|
75
|
-
}
|
|
76
|
-
workspaceId = selected;
|
|
78
|
+
if (p.isCancel(selected)) {
|
|
79
|
+
p.cancel("Cancelled.");
|
|
80
|
+
process.exit(0);
|
|
77
81
|
}
|
|
78
|
-
|
|
82
|
+
workspaceId = selected;
|
|
79
83
|
}
|
|
84
|
+
writeWorkspaceConfig({ workspaceId });
|
|
80
85
|
if (!workspaceId) {
|
|
81
|
-
p.log.error("No workspace configured. Run " + pc.cyan("inflight
|
|
86
|
+
p.log.error("No workspace configured. Run " + pc.cyan("inflight workspace") + " or " + pc.cyan("inflight setup") + ".");
|
|
82
87
|
process.exit(1);
|
|
83
88
|
}
|
|
84
89
|
// Resolve staging URL
|
package/dist/commands/vercel.js
CHANGED
|
@@ -1,128 +1,68 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import { ensureVercelCli, ensureVercelAuth, getVercelToken, getVercelTeams, getVercelProjects, getRecentDeployments, getBranchAlias, } from "../lib/vercel.js";
|
|
5
|
-
import { pickVercelProject } from "../providers/vercel.js";
|
|
1
|
+
import { readVercelConfig } from "../lib/config.js";
|
|
2
|
+
import { getGitInfo, parseGitRepo, getGitRoot } from "../lib/git.js";
|
|
3
|
+
import { getVercelToken, getRecentDeployments, getBranchAlias, readLocalVercelProject, fetchAllProjectsWithLinks, matchProjectsByRepo, } from "../lib/vercel.js";
|
|
6
4
|
// --- Action handlers ---
|
|
7
|
-
async function
|
|
8
|
-
// Fast path: non-interactive set (for agents)
|
|
9
|
-
if (opts.team && opts.project) {
|
|
10
|
-
const token = requireVercelToken();
|
|
11
|
-
const teams = await getVercelTeams(token);
|
|
12
|
-
const team = teams.find((t) => t.id === opts.team);
|
|
13
|
-
if (!team) {
|
|
14
|
-
const msg = `Team '${opts.team}' not found. Available: ${teams.map((t) => `${t.name} (${t.id})`).join(", ")}`;
|
|
15
|
-
if (opts.json) {
|
|
16
|
-
console.log(JSON.stringify({ error: "team_not_found", message: msg }));
|
|
17
|
-
}
|
|
18
|
-
else {
|
|
19
|
-
p.log.error(msg);
|
|
20
|
-
}
|
|
21
|
-
process.exit(1);
|
|
22
|
-
}
|
|
23
|
-
const projects = await getVercelProjects(token, opts.team);
|
|
24
|
-
const project = projects.find((proj) => proj.id === opts.project);
|
|
25
|
-
if (!project) {
|
|
26
|
-
const msg = `Project '${opts.project}' not found on team '${team.name}'.`;
|
|
27
|
-
if (opts.json) {
|
|
28
|
-
console.log(JSON.stringify({ error: "project_not_found", message: msg }));
|
|
29
|
-
}
|
|
30
|
-
else {
|
|
31
|
-
p.log.error(msg);
|
|
32
|
-
}
|
|
33
|
-
process.exit(1);
|
|
34
|
-
}
|
|
35
|
-
writeVercelConfig({ teamId: team.id, teamName: team.name, projectId: project.id, projectName: project.name });
|
|
36
|
-
if (opts.json) {
|
|
37
|
-
console.log(JSON.stringify({
|
|
38
|
-
saved: true,
|
|
39
|
-
teamId: team.id,
|
|
40
|
-
teamName: team.name,
|
|
41
|
-
projectId: project.id,
|
|
42
|
-
projectName: project.name,
|
|
43
|
-
}));
|
|
44
|
-
}
|
|
45
|
-
else {
|
|
46
|
-
p.log.success(`Saved! Using ${pc.bold(project.name)} on ${pc.bold(team.name)}.`);
|
|
47
|
-
}
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
// Interactive path
|
|
51
|
-
const cliOk = await ensureVercelCli((msg) => p.log.step(msg));
|
|
52
|
-
if (!cliOk) {
|
|
53
|
-
p.log.error("Failed to install Vercel CLI. Install manually: " + pc.cyan("npm install -g vercel"));
|
|
54
|
-
process.exit(1);
|
|
55
|
-
}
|
|
56
|
-
p.log.step("Checking Vercel authentication...");
|
|
57
|
-
const token = await ensureVercelAuth();
|
|
58
|
-
if (!token) {
|
|
59
|
-
p.log.error("Vercel login failed.");
|
|
60
|
-
process.exit(1);
|
|
61
|
-
}
|
|
62
|
-
const current = readVercelConfig();
|
|
63
|
-
if (current) {
|
|
64
|
-
p.log.info(`Current: ${pc.bold(current.projectName)} on ${pc.bold(current.teamName)}`);
|
|
65
|
-
}
|
|
66
|
-
const config = await pickVercelProject(token);
|
|
67
|
-
if (!config)
|
|
68
|
-
process.exit(1);
|
|
69
|
-
p.log.success(`Saved! ${pc.cyan("inflight share")} will now use ${pc.bold(config.projectName)} on ${pc.bold(config.teamName)}.`);
|
|
70
|
-
}
|
|
71
|
-
async function listTeams() {
|
|
72
|
-
const token = requireVercelToken();
|
|
73
|
-
const teams = await getVercelTeams(token);
|
|
74
|
-
console.log(JSON.stringify(teams));
|
|
75
|
-
}
|
|
76
|
-
async function listProjects(opts) {
|
|
5
|
+
async function listProjects() {
|
|
77
6
|
const token = requireVercelToken();
|
|
78
|
-
const projects = await
|
|
79
|
-
console.log(JSON.stringify(projects));
|
|
7
|
+
const projects = await fetchAllProjectsWithLinks(token);
|
|
8
|
+
console.log(JSON.stringify(projects.map((p) => ({ id: p.id, name: p.name, teamId: p.teamId, teamName: p.teamName }))));
|
|
80
9
|
}
|
|
81
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Resolves teamId + projectId for subcommands.
|
|
12
|
+
* Priority: explicit flags → .vercel/project.json → git remote match → saved global config → error
|
|
13
|
+
*/
|
|
14
|
+
async function resolveProject(opts) {
|
|
15
|
+
if (opts.team && opts.project) {
|
|
16
|
+
return { teamId: opts.team, projectId: opts.project };
|
|
17
|
+
}
|
|
82
18
|
const token = requireVercelToken();
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
if (
|
|
86
|
-
const
|
|
87
|
-
if (
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
19
|
+
const cwd = process.cwd();
|
|
20
|
+
const gitRoot = getGitRoot(cwd);
|
|
21
|
+
if (gitRoot) {
|
|
22
|
+
const local = readLocalVercelProject(gitRoot);
|
|
23
|
+
if (local)
|
|
24
|
+
return { teamId: local.orgId, projectId: local.projectId };
|
|
25
|
+
}
|
|
26
|
+
const gitInfo = getGitInfo(cwd);
|
|
27
|
+
if (gitInfo.remoteUrl) {
|
|
28
|
+
const gitRepo = parseGitRepo(gitInfo.remoteUrl);
|
|
29
|
+
if (gitRepo) {
|
|
30
|
+
try {
|
|
31
|
+
const allProjects = await fetchAllProjectsWithLinks(token);
|
|
32
|
+
const matches = matchProjectsByRepo(allProjects, gitRepo.owner, gitRepo.name);
|
|
33
|
+
if (matches.length === 1) {
|
|
34
|
+
return { teamId: matches[0].teamId, projectId: matches[0].id };
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch { }
|
|
93
38
|
}
|
|
94
|
-
teamId = teamId ?? config.teamId;
|
|
95
|
-
projectId = projectId ?? config.projectId;
|
|
96
39
|
}
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
40
|
+
const config = readVercelConfig();
|
|
41
|
+
if (config)
|
|
42
|
+
return { teamId: config.teamId, projectId: config.projectId };
|
|
43
|
+
console.log(JSON.stringify({
|
|
44
|
+
error: "vercel_not_configured",
|
|
45
|
+
message: "Could not auto-detect Vercel project. Run 'inflight vercel projects' to list available projects, then pass --team and --project to 'inflight vercel deployments'.",
|
|
46
|
+
}));
|
|
47
|
+
process.exit(1);
|
|
102
48
|
}
|
|
103
|
-
async function
|
|
49
|
+
async function listDeployments(opts) {
|
|
104
50
|
const token = requireVercelToken();
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
if (alias) {
|
|
121
|
-
console.log(JSON.stringify({ url: alias.url, state: alias.state, branch: opts.branch }));
|
|
122
|
-
}
|
|
123
|
-
else {
|
|
124
|
-
console.log(JSON.stringify({ url: null, branch: opts.branch, message: "No deployment found for this branch." }));
|
|
125
|
-
}
|
|
51
|
+
const { teamId, projectId } = await resolveProject(opts);
|
|
52
|
+
const cwd = process.cwd();
|
|
53
|
+
const gitInfo = getGitInfo(cwd);
|
|
54
|
+
const currentBranch = gitInfo.branch;
|
|
55
|
+
const [deployments, branchAlias] = await Promise.all([
|
|
56
|
+
getRecentDeployments(token, teamId, projectId, {
|
|
57
|
+
limit: parseInt(opts.limit ?? "10"),
|
|
58
|
+
branch: opts.branch,
|
|
59
|
+
}),
|
|
60
|
+
currentBranch ? getBranchAlias(token, teamId, projectId, currentBranch) : Promise.resolve(null),
|
|
61
|
+
]);
|
|
62
|
+
console.log(JSON.stringify({
|
|
63
|
+
branchAlias: branchAlias ? { url: branchAlias.url, state: branchAlias.state, branch: currentBranch } : null,
|
|
64
|
+
deployments,
|
|
65
|
+
}));
|
|
126
66
|
}
|
|
127
67
|
/** Gets token silently, exits with JSON error if unavailable. */
|
|
128
68
|
function requireVercelToken() {
|
|
@@ -130,7 +70,7 @@ function requireVercelToken() {
|
|
|
130
70
|
if (!token) {
|
|
131
71
|
console.log(JSON.stringify({
|
|
132
72
|
error: "vercel_not_authenticated",
|
|
133
|
-
message: "Vercel auth expired or missing. Run '
|
|
73
|
+
message: "Vercel auth expired or missing. Run 'vercel login' first.",
|
|
134
74
|
}));
|
|
135
75
|
process.exit(1);
|
|
136
76
|
}
|
|
@@ -138,33 +78,14 @@ function requireVercelToken() {
|
|
|
138
78
|
}
|
|
139
79
|
// --- Command registration ---
|
|
140
80
|
export function registerVercelCommand(program) {
|
|
141
|
-
const vercel = program
|
|
142
|
-
|
|
143
|
-
.description("Set up or change your Vercel project")
|
|
144
|
-
.passThroughOptions()
|
|
145
|
-
.option("--team <id>", "Vercel team ID (non-interactive)")
|
|
146
|
-
.option("--project <id>", "Vercel project ID (non-interactive)")
|
|
147
|
-
.option("--json", "Output as JSON (non-interactive)")
|
|
148
|
-
.action(vercelSetup);
|
|
149
|
-
vercel.command("teams").description("List Vercel teams (JSON)").action(listTeams);
|
|
150
|
-
vercel
|
|
151
|
-
.command("projects")
|
|
152
|
-
.description("List projects for a Vercel team (JSON)")
|
|
153
|
-
.requiredOption("--team <id>", "Vercel team ID")
|
|
154
|
-
.action(listProjects);
|
|
155
|
-
vercel
|
|
156
|
-
.command("branch-url")
|
|
157
|
-
.description("Get the stable branch preview URL (JSON)")
|
|
158
|
-
.requiredOption("--branch <name>", "Git branch name")
|
|
159
|
-
.option("--team <id>", "Vercel team ID (reads from saved config if omitted)")
|
|
160
|
-
.option("--project <id>", "Vercel project ID (reads from saved config if omitted)")
|
|
161
|
-
.action(branchUrl);
|
|
81
|
+
const vercel = program.command("vercel").description("Vercel integration commands");
|
|
82
|
+
vercel.command("projects").description("List all Vercel projects (JSON)").action(listProjects);
|
|
162
83
|
vercel
|
|
163
84
|
.command("deployments")
|
|
164
|
-
.description("List recent deployments (JSON)")
|
|
165
|
-
.option("--team <id>", "Vercel team ID (
|
|
166
|
-
.option("--project <id>", "Vercel project ID (
|
|
167
|
-
.option("--branch <name>", "Filter by
|
|
85
|
+
.description("List recent deployments + branch alias (JSON)")
|
|
86
|
+
.option("--team <id>", "Vercel team ID (auto-detected if omitted)")
|
|
87
|
+
.option("--project <id>", "Vercel project ID (auto-detected if omitted)")
|
|
88
|
+
.option("--branch <name>", "Filter deployments by branch")
|
|
168
89
|
.option("--limit <n>", "Number of deployments", "10")
|
|
169
90
|
.action(listDeployments);
|
|
170
91
|
}
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,11 @@ import pkg from "../package.json" with { type: "json" };
|
|
|
12
12
|
const { version } = pkg;
|
|
13
13
|
updateNotifier({ pkg }).notify();
|
|
14
14
|
const program = new Command();
|
|
15
|
-
program
|
|
15
|
+
program
|
|
16
|
+
.name("inflight")
|
|
17
|
+
.description("Get feedback directly on your staging URL")
|
|
18
|
+
.version(version)
|
|
19
|
+
.enablePositionalOptions();
|
|
16
20
|
program.command("setup").description("Set up Inflight in your project").action(setupCommand);
|
|
17
21
|
program.command("login").description("Authenticate with your Inflight account").action(loginCommand);
|
|
18
22
|
program
|
|
@@ -22,16 +26,9 @@ program
|
|
|
22
26
|
.option("--workspace <id>", "Workspace ID (skips workspace selection)")
|
|
23
27
|
.option("--json", "Output result as JSON")
|
|
24
28
|
.action((opts) => shareCommand(opts));
|
|
25
|
-
// program
|
|
26
|
-
// .command("preview")
|
|
27
|
-
// .description("Preview a live component from your code")
|
|
28
|
-
// .option("-m, --message <message>", "Pre-fill the intent prompt")
|
|
29
|
-
// .option("--scope <mode>", "Skip scope prompt: branch, uncommitted, staged")
|
|
30
|
-
// .option("--no-open", "Don't open result in browser")
|
|
31
|
-
// .action((opts) => previewCommand(opts));
|
|
32
29
|
program
|
|
33
|
-
.command("
|
|
34
|
-
.description("
|
|
30
|
+
.command("workspace")
|
|
31
|
+
.description("Get or set your active workspace")
|
|
35
32
|
.option("--json", "Output as JSON")
|
|
36
33
|
.option("--set <id>", "Set the active workspace")
|
|
37
34
|
.action((opts) => workspacesCommand(opts));
|