@lizard-build/cli 4.0.35 → 4.0.37
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 +14 -12
- package/dist/commands/add.js +2 -2
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/git.js +1 -1
- package/dist/commands/git.js.map +1 -1
- package/dist/commands/login.d.ts +3 -1
- package/dist/commands/login.js +24 -11
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/restart.js +4 -2
- package/dist/commands/restart.js.map +1 -1
- package/dist/commands/ssh.js +1 -1
- package/dist/commands/ssh.js.map +1 -1
- package/dist/commands/upgrade.js +26 -12
- package/dist/commands/upgrade.js.map +1 -1
- package/dist/lib/skills-data.generated.js +1 -1
- package/dist/lib/skills-data.generated.js.map +1 -1
- package/dist/lib/updater.d.ts +9 -1
- package/dist/lib/updater.js +15 -1
- package/dist/lib/updater.js.map +1 -1
- package/package.json +1 -1
- package/skill-data/core/SKILL.md +53 -43
- package/src/commands/add.ts +2 -2
- package/src/commands/git.ts +1 -1
- package/src/commands/login.ts +25 -15
- package/src/commands/restart.ts +5 -2
- package/src/commands/ssh.ts +1 -1
- package/src/commands/upgrade.ts +37 -11
- package/src/lib/updater.ts +17 -1
- package/test/unit/login.test.ts +16 -3
package/src/commands/login.ts
CHANGED
|
@@ -66,7 +66,7 @@ export function accountInstruction(state: AccountState, sub: Subscription | null
|
|
|
66
66
|
case "not_owner":
|
|
67
67
|
return (
|
|
68
68
|
"This account deploys on its workspace owner's plan. Do NOT start or change a subscription: only the owner can." +
|
|
69
|
-
(sub?.plan === "none" ? "
|
|
69
|
+
(sub?.plan === "none" ? " Sandboxes and deploys stay blocked until the owner starts Pro." : "")
|
|
70
70
|
);
|
|
71
71
|
case "enterprise":
|
|
72
72
|
return "Enterprise plan, invoiced monthly. Nothing to set up.";
|
|
@@ -84,12 +84,12 @@ export function accountInstruction(state: AccountState, sub: Subscription | null
|
|
|
84
84
|
return "Pro is active. Do NOT open billing or start a subscription.";
|
|
85
85
|
case "trial_available":
|
|
86
86
|
return (
|
|
87
|
-
`No plan yet, and deploys need one. Offer the user the ${sub?.trial.days}-day Pro trial with ${usd(sub?.trial.creditCents ?? 0)} in credits: ` +
|
|
87
|
+
`No plan yet, and sandboxes and deploys need one. Offer the user the ${sub?.trial.days}-day Pro trial with ${usd(sub?.trial.creditCents ?? 0)} in credits: ` +
|
|
88
88
|
`no charge today, then ${price}/month, taxes included. ${start}`
|
|
89
89
|
);
|
|
90
90
|
case "subscription_required":
|
|
91
91
|
return (
|
|
92
|
-
"No plan yet, and deploys need one. This account has had its trial, so do NOT promise one. " +
|
|
92
|
+
"No plan yet, and sandboxes and deploys need one. This account has had its trial, so do NOT promise one. " +
|
|
93
93
|
`Pro is ${price}/month, taxes included, with ${usd(sub?.includedCents ?? 1900)} in credits each month. ${start}`
|
|
94
94
|
);
|
|
95
95
|
case "checkout_unavailable":
|
|
@@ -97,16 +97,33 @@ export function accountInstruction(state: AccountState, sub: Subscription | null
|
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
/** Commands an agent can run next. */
|
|
100
|
+
/** Commands an agent can run next. Sandboxes first; both they and deploys live in a project. */
|
|
101
101
|
export function nextSteps(linked: boolean): Array<{ command: string; why: string }> {
|
|
102
102
|
return [
|
|
103
103
|
{
|
|
104
104
|
command: "lizard skills get core",
|
|
105
105
|
why: "The guide to every command, matched to this CLI version. Read it before running others.",
|
|
106
106
|
},
|
|
107
|
-
...(linked
|
|
108
|
-
|
|
107
|
+
...(linked
|
|
108
|
+
? []
|
|
109
|
+
: [{ command: "lizard init", why: "Create a project and link the current folder to it. Sandboxes and deploys both need one." }]),
|
|
110
|
+
{
|
|
111
|
+
command: "lizard sandbox create",
|
|
112
|
+
why: "Start a sandbox in the project, then run commands in it with `lizard sandbox exec <id> -- <cmd>`.",
|
|
113
|
+
},
|
|
114
|
+
{ command: "lizard up", why: "Or deploy the linked folder." },
|
|
115
|
+
];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** The same steps for a person, as aligned lines for the closing note. */
|
|
119
|
+
export function nextStepsNote(linked: boolean): string {
|
|
120
|
+
const rows: Array<[string, string]> = [
|
|
121
|
+
...(linked ? [] : ([["lizard init", "Create a project and link this folder"]] as Array<[string, string]>)),
|
|
122
|
+
["lizard sandbox create", linked ? "Start a sandbox in this project" : "Start a sandbox in it"],
|
|
123
|
+
["lizard up", "Or deploy this folder"],
|
|
109
124
|
];
|
|
125
|
+
const width = Math.max(...rows.map(([cmd]) => cmd.length)) + 3;
|
|
126
|
+
return rows.map(([cmd, why]) => chalk.cyan(cmd) + " ".repeat(width - cmd.length) + why).join("\n");
|
|
110
127
|
}
|
|
111
128
|
|
|
112
129
|
async function fetchSubscription(): Promise<Subscription | null> {
|
|
@@ -169,7 +186,7 @@ async function offerPlan(canAsk: boolean): Promise<void> {
|
|
|
169
186
|
const offer = trial
|
|
170
187
|
? `Pro trial: ${sub.trial.days} days with ${usd(sub.trial.creditCents ?? 0)} in credits. No charge today, then ${usd(sub.priceCents)}/month, taxes included.`
|
|
171
188
|
: `Pro: ${usd(sub.priceCents)}/month, taxes included, with ${usd(sub.includedCents)} in credits each month.`;
|
|
172
|
-
p.log.info(`No plan yet.
|
|
189
|
+
p.log.info(`No plan yet. Sandboxes and deploys need one.\n${offer}`);
|
|
173
190
|
|
|
174
191
|
const go = canAsk ? await p.confirm({ message: trial ? "Start the trial now?" : "Start Pro now?" }) : false;
|
|
175
192
|
if (p.isCancel(go) || !go) {
|
|
@@ -204,14 +221,7 @@ async function loginInteractive(flag?: AuthProvider): Promise<void> {
|
|
|
204
221
|
|
|
205
222
|
await offerPlan(canAsk);
|
|
206
223
|
|
|
207
|
-
|
|
208
|
-
p.note(
|
|
209
|
-
(linked
|
|
210
|
-
? [`${chalk.cyan("lizard up")} Deploy this folder`]
|
|
211
|
-
: [`${chalk.cyan("lizard init")} Create a project and link this folder`, `${chalk.cyan("lizard up")} Deploy it`]
|
|
212
|
-
).join("\n"),
|
|
213
|
-
"Next",
|
|
214
|
-
);
|
|
224
|
+
p.note(nextStepsNote(Boolean(getProjectLink())), "Next");
|
|
215
225
|
p.outro("You're all set");
|
|
216
226
|
}
|
|
217
227
|
|
package/src/commands/restart.ts
CHANGED
|
@@ -57,6 +57,9 @@ export function registerRestart(program: Command) {
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
// `ps` takes no service argument: it lists every service in the project.
|
|
61
|
+
const psHint = `lizard ps${opts.project ? ` --project ${opts.project}` : ""}`;
|
|
62
|
+
|
|
60
63
|
const timeoutSeconds = parseInt(opts.timeout, 10);
|
|
61
64
|
if (!(timeoutSeconds > 0)) {
|
|
62
65
|
fail(`--timeout must be a positive number of seconds, got ${JSON.stringify(opts.timeout)}`, 1, "INVALID_ARGUMENT");
|
|
@@ -104,7 +107,7 @@ export function registerRestart(program: Command) {
|
|
|
104
107
|
printJSON({ id, status: "restarting" });
|
|
105
108
|
} else {
|
|
106
109
|
success("Restart started");
|
|
107
|
-
info(chalk.dim(` Check status:
|
|
110
|
+
info(chalk.dim(` Check status: ${psHint}`));
|
|
108
111
|
}
|
|
109
112
|
return;
|
|
110
113
|
}
|
|
@@ -138,7 +141,7 @@ export function registerRestart(program: Command) {
|
|
|
138
141
|
} else if (finalStatus) {
|
|
139
142
|
error("Restart failed");
|
|
140
143
|
} else {
|
|
141
|
-
info(chalk.dim(
|
|
144
|
+
info(chalk.dim(`Still restarting — check status with: ${psHint}`));
|
|
142
145
|
}
|
|
143
146
|
});
|
|
144
147
|
}
|
package/src/commands/ssh.ts
CHANGED
|
@@ -11,7 +11,7 @@ import * as http from "node:http";
|
|
|
11
11
|
export function registerSSH(program: Command) {
|
|
12
12
|
program
|
|
13
13
|
.command("ssh")
|
|
14
|
-
.description("Execute a command inside a running service container")
|
|
14
|
+
.description("Execute a command inside a running service container (disabled: the API answers 403 \"Shell access to services is disabled\"; for sandboxes use `lizard sandbox ssh`)")
|
|
15
15
|
.argument("[cmd...]", "Command and args to run inside the container (required; pass after `--` to stop flag parsing, e.g. `-- ls -la /app`)")
|
|
16
16
|
.option("-s, --service <id>", "Service name or ID")
|
|
17
17
|
.option("-p, --project <id>", "Project name, slug, or ID")
|
package/src/commands/upgrade.ts
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import { Command } from "commander";
|
|
3
3
|
import { info, success, isJSONMode, printJSON } from "../lib/format.js";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
CURRENT_VERSION,
|
|
6
|
+
canSelfUpdate,
|
|
7
|
+
getLatestVersion,
|
|
8
|
+
installerCommand,
|
|
9
|
+
isStandaloneBinary,
|
|
10
|
+
selfUpdate,
|
|
11
|
+
} from "../lib/updater.js";
|
|
5
12
|
|
|
6
13
|
export function registerUpgrade(program: Command) {
|
|
7
14
|
program
|
|
@@ -87,21 +94,40 @@ export function registerUpgrade(program: Command) {
|
|
|
87
94
|
return;
|
|
88
95
|
}
|
|
89
96
|
|
|
97
|
+
// No binary to swap in (the Windows build): say so and fail, instead of
|
|
98
|
+
// reporting an upgrade that never happened.
|
|
99
|
+
if (!canSelfUpdate()) {
|
|
100
|
+
throw notInstalled(`v${latest} is available, but this copy of Lizard CLI can't update itself.`);
|
|
101
|
+
}
|
|
102
|
+
|
|
90
103
|
info(`Upgrading v${CURRENT_VERSION} → ${chalk.green("v" + latest)}...`);
|
|
91
104
|
|
|
105
|
+
let upgraded: boolean;
|
|
92
106
|
try {
|
|
93
|
-
await selfUpdate((msg) => info(chalk.dim(msg)));
|
|
94
|
-
if (isJSONMode()) {
|
|
95
|
-
printJSON({
|
|
96
|
-
previousVersion: CURRENT_VERSION,
|
|
97
|
-
latestVersion: latest,
|
|
98
|
-
upgraded: true,
|
|
99
|
-
});
|
|
100
|
-
} else {
|
|
101
|
-
success(`Upgraded to v${latest}`);
|
|
102
|
-
}
|
|
107
|
+
upgraded = await selfUpdate((msg) => info(chalk.dim(msg)));
|
|
103
108
|
} catch (e: any) {
|
|
104
109
|
throw new Error(`Upgrade failed: ${e.message}`);
|
|
105
110
|
}
|
|
111
|
+
if (!upgraded) {
|
|
112
|
+
throw notInstalled(`Nothing was replaced: v${CURRENT_VERSION} is still installed.`);
|
|
113
|
+
}
|
|
114
|
+
if (isJSONMode()) {
|
|
115
|
+
printJSON({
|
|
116
|
+
previousVersion: CURRENT_VERSION,
|
|
117
|
+
latestVersion: latest,
|
|
118
|
+
upgraded: true,
|
|
119
|
+
});
|
|
120
|
+
} else {
|
|
121
|
+
success(`Upgraded to v${latest}`);
|
|
122
|
+
}
|
|
106
123
|
});
|
|
107
124
|
}
|
|
125
|
+
|
|
126
|
+
/** Exit 1 with the installer command that does the upgrade instead. */
|
|
127
|
+
function notInstalled(reason: string): Error {
|
|
128
|
+
const shell = process.platform === "win32" ? " in PowerShell" : "";
|
|
129
|
+
return Object.assign(
|
|
130
|
+
new Error(`${reason} Re-run the installer${shell}:\n ${installerCommand()}`),
|
|
131
|
+
{ code: "UPGRADE_NOT_INSTALLED" },
|
|
132
|
+
);
|
|
133
|
+
}
|
package/src/lib/updater.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { join, dirname } from "node:path";
|
|
|
5
5
|
import os from "node:os";
|
|
6
6
|
import { spawn } from "node:child_process";
|
|
7
7
|
|
|
8
|
-
export const CURRENT_VERSION = "4.0.
|
|
8
|
+
export const CURRENT_VERSION = "4.0.37";
|
|
9
9
|
const RELEASES_API = "https://api.github.com/repos/lizard-build/lizard-cli/releases/latest";
|
|
10
10
|
const RELEASE_BASE = "https://github.com/lizard-build/lizard-cli/releases/latest/download";
|
|
11
11
|
|
|
@@ -22,6 +22,22 @@ function getBinaryName(): string | null {
|
|
|
22
22
|
return null;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
/**
|
|
26
|
+
* True when selfUpdate() has a release asset to swap in for this platform.
|
|
27
|
+
* False on Windows: a running .exe can't be replaced in place, so the
|
|
28
|
+
* PowerShell installer does the update there.
|
|
29
|
+
*/
|
|
30
|
+
export function canSelfUpdate(): boolean {
|
|
31
|
+
return getBinaryName() !== null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** The installer that puts the latest standalone binary over this one. */
|
|
35
|
+
export function installerCommand(): string {
|
|
36
|
+
return process.platform === "win32"
|
|
37
|
+
? "irm https://lizard.build/api/cli/install.ps1 | iex"
|
|
38
|
+
: "curl -fsSL https://lizard.build/install.sh | bash";
|
|
39
|
+
}
|
|
40
|
+
|
|
25
41
|
/**
|
|
26
42
|
* True only when running as the Bun-compiled standalone binary. Under
|
|
27
43
|
* npm/node, `process.execPath` is the *node* executable — self-update would
|
package/test/unit/login.test.ts
CHANGED
|
@@ -4,7 +4,7 @@ import * as os from "node:os";
|
|
|
4
4
|
import * as path from "node:path";
|
|
5
5
|
import { Command } from "commander";
|
|
6
6
|
import { Readable } from "node:stream";
|
|
7
|
-
import { registerLogin, accountState, accountInstruction, nextSteps, readTokenFromStdin } from "../../src/commands/login.js";
|
|
7
|
+
import { registerLogin, accountState, accountInstruction, nextSteps, nextStepsNote, readTokenFromStdin } from "../../src/commands/login.js";
|
|
8
8
|
import { authUrlFor, providerFlag, pendingPayload } from "../../src/lib/signin.js";
|
|
9
9
|
import type { Subscription } from "../../src/commands/billing.js";
|
|
10
10
|
import { requireAuth } from "../../src/lib/auth.js";
|
|
@@ -184,9 +184,22 @@ describe("account state", () => {
|
|
|
184
184
|
);
|
|
185
185
|
});
|
|
186
186
|
|
|
187
|
+
test("the closing note leads with sandboxes, aligned in two columns", () => {
|
|
188
|
+
const plain = (t: string) => t.replace(/\x1b\[[0-9;]*m/g, "");
|
|
189
|
+
expect(plain(nextStepsNote(false)).split("\n")).toEqual([
|
|
190
|
+
"lizard init Create a project and link this folder",
|
|
191
|
+
"lizard sandbox create Start a sandbox in it",
|
|
192
|
+
"lizard up Or deploy this folder",
|
|
193
|
+
]);
|
|
194
|
+
expect(plain(nextStepsNote(true)).split("\n")).toEqual([
|
|
195
|
+
"lizard sandbox create Start a sandbox in this project",
|
|
196
|
+
"lizard up Or deploy this folder",
|
|
197
|
+
]);
|
|
198
|
+
});
|
|
199
|
+
|
|
187
200
|
test("an unlinked folder gets lizard init before lizard up", () => {
|
|
188
|
-
expect(nextSteps(false).map((s) => s.command)).toEqual(["lizard skills get core", "lizard init", "lizard up"]);
|
|
189
|
-
expect(nextSteps(true).map((s) => s.command)).toEqual(["lizard skills get core", "lizard up"]);
|
|
201
|
+
expect(nextSteps(false).map((s) => s.command)).toEqual(["lizard skills get core", "lizard init", "lizard sandbox create", "lizard up"]);
|
|
202
|
+
expect(nextSteps(true).map((s) => s.command)).toEqual(["lizard skills get core", "lizard sandbox create", "lizard up"]);
|
|
190
203
|
});
|
|
191
204
|
});
|
|
192
205
|
|