@flisk-sh/flisk 1.3.195 → 1.3.198
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/CHANGELOG.md +1 -1
- package/README.md +4 -4
- package/cli/dist/cli.js +31 -11
- package/cli/dist/commands/mcp.js +4 -1
- package/cli/dist/commands/open.js +27 -8
- package/cli/dist/commands/registry.js +1 -1
- package/cli/dist/commands/ui.js +1 -1
- package/cli/dist/commands/upgrade.js +2 -3
- package/cli/dist/mcp/tools.js +5 -2
- package/cli/dist/utils/cli-command.js +11 -2
- package/cli/dist/utils/doctor-fix.js +1 -1
- package/cli/dist/utils/one-command.js +66 -19
- package/cli/dist-bundle/chunk-CF46CD6R.js +6 -0
- package/cli/dist-bundle/chunk-DTIHDMED.js +5 -0
- package/cli/dist-bundle/{chunk-3RB2FQ46.js → chunk-DWHA5YKT.js} +9 -9
- package/cli/dist-bundle/{chunk-CXFHRZLM.js → chunk-KBGQ4NJE.js} +1 -1
- package/cli/dist-bundle/{chunk-32NCVCS7.js → chunk-SB5WAMSU.js} +1 -1
- package/cli/dist-bundle/chunk-US4D5GJJ.js +21 -0
- package/cli/dist-bundle/chunk-WPQOTHB5.js +4 -0
- package/cli/dist-bundle/{chunk-ZDH5BP3H.js → chunk-YUARXGF4.js} +2 -2
- package/cli/dist-bundle/cli.js +206 -212
- package/cli/dist-bundle/cold-preflight-5TVSNLIV.js +2 -0
- package/cli/dist-bundle/one-command-TKXMWXCK.js +4 -0
- package/cli/dist-bundle/open-5SUD6LKB.js +2 -0
- package/cli/dist-bundle/project-5GTJYFAE.js +2 -0
- package/cli/dist-bundle/spinner-YLB6FYQH.js +2 -0
- package/cli/package.json +1 -1
- package/package.json +1 -1
- package/cli/dist-bundle/chunk-DTJ6JCHV.js +0 -25
- package/cli/dist-bundle/chunk-TCN3L76U.js +0 -4
- package/cli/dist-bundle/cold-preflight-FSWLLA2X.js +0 -2
- package/cli/dist-bundle/one-command-A4R272U3.js +0 -4
- package/cli/dist-bundle/open-HXJVYBH7.js +0 -2
- package/cli/dist-bundle/project-5P6B5MAP.js +0 -2
package/CHANGELOG.md
CHANGED
|
@@ -152,7 +152,7 @@ Notable changes to the `flisk` CLI. Versions match [npm](https://www.npmjs.com/p
|
|
|
152
152
|
|
|
153
153
|
## 1.3.111 (2026-10-05)
|
|
154
154
|
|
|
155
|
-
- `flisk config regenerate` now restores a root `.tiltignore` that Flisk wrote (it carries the `SYSTEM-GENERATED BY Flisk CLI` header) and that was edited by hand, and prints `Restored: .tiltignore → project root (hand edits discarded)`. Before, it only copied the file when none existed, so `flisk config verify` kept failing with `Out of sync: .tiltignore (run 'flisk config regenerate')` after the advice was followed. A root `.tiltignore` without the header is still never overwritten, and plain `flisk project --yes` still does not overwrite an existing one. Docs: `docs/adopt-
|
|
155
|
+
- `flisk config regenerate` now restores a root `.tiltignore` that Flisk wrote (it carries the `SYSTEM-GENERATED BY Flisk CLI` header) and that was edited by hand, and prints `Restored: .tiltignore → project root (hand edits discarded)`. Before, it only copied the file when none existed, so `flisk config verify` kept failing with `Out of sync: .tiltignore (run 'flisk config regenerate')` after the advice was followed. A root `.tiltignore` without the header is still never overwritten, and plain `flisk project --yes` still does not overwrite an existing one. Docs: `docs/adopt-flisk.md` now quotes the current `flisk doctor` unknown-field wording. (#615)
|
|
156
156
|
- `flisk doctor` now checks the project's own resource configs for duplicates. Two resources with the same `appName` fail (`Duplicate resource names`, naming both `service.json` paths): the name becomes the Compose service and Traefik router, so they collide, and before nothing reported it. Two backend, mcp or routed bring-your-own resources on the same `port` give a warning, not a failure (`Duplicate resource ports`): service ports are not published on the host and each service has its own container, so the pair can run, but it is usually a copied `service.json`. Frontends, workers, libraries and a missing `port` are ignored. Fixes #76. (#609)
|
|
157
157
|
- `flisk doctor` no longer fails with `N resources without a package.json` for a bring-your-own resource. A bring-your-own resource (`--image`, `--dockerfile` or `buildContext`) is a plain container: its image is pulled or built from its own Dockerfile, never from the generated Dockerfile that copies a `package.json`, so the check was a false positive that made `flisk doctor` (and CI recipes that gate on it) exit non-zero right after wrapping an existing service. Backend, frontend, worker, migrator, mcp, library and sdk resources without a `package.json` still fail; Python, Go and Rust backends were already exempt. The other doctor checks were run on a project with only bring-your-own resources and report nothing. Fixes #599. (#600)
|
|
158
158
|
- A scaffolded frontend now says why `bun run build` / `bun run dev` cannot run on the host before `flisk up`: the `.autogenerated/vite.config.*.autogenerated.ts` files its scripts point at are written by the engine (Tilt) when `flisk up` loads the resource, and neither `flisk resource` nor `flisk config regenerate` creates them. Before, `vite` stopped with an esbuild `Could not resolve` stack trace. New `predev`/`prebuild` scripts print what is missing and how to get it; once the file exists (and inside the engine image build) they pass silently. Existing frontends need the two scripts added by hand. Backend, worker and mcp scaffolds are unchanged. Fixes #592. (#594)
|
package/README.md
CHANGED
|
@@ -196,7 +196,7 @@ Teams that run `flisk up` on their own code (listed with their consent in [ADOPT
|
|
|
196
196
|
- [MakeOffer](https://www.linkedin.com/company/makeofferio), Flisk 1.3.180
|
|
197
197
|
- [Red Moon Digital](https://redmoon.digital/), Flisk 1.3.81
|
|
198
198
|
|
|
199
|
-
Using Flisk too? [Tell us](https://github.com/flisk-sh/flisk/issues/new?template=we-use-
|
|
199
|
+
Using Flisk too? [Tell us](https://github.com/flisk-sh/flisk/issues/new?template=we-use-flisk.yml) or [add a row to ADOPTERS.md](ADOPTERS.md).
|
|
200
200
|
|
|
201
201
|
If Flisk saves you time, a ⭐ on [this repo](https://github.com/flisk-sh/flisk) helps other developers find it.
|
|
202
202
|
|
|
@@ -328,7 +328,7 @@ Runnable examples show Flisk on real stacks. Start with the smallest one:
|
|
|
328
328
|
|
|
329
329
|
## Use Flisk with your AI agent
|
|
330
330
|
|
|
331
|
-
The repository ships agent skills for common Flisk work, including `
|
|
331
|
+
The repository ships agent skills for common Flisk work, including `flisk-doctor` (environment checks) and `flisk-troubleshoot` (fixing `flisk` and Tilt errors), under [`.agents/skills`](.agents/skills). Agents working in this repo also follow [AGENTS.md](AGENTS.md). Use these skills to let your agent check your setup and explain failures instead of guessing at Docker or Tilt configuration.
|
|
332
332
|
|
|
333
333
|
## Installation
|
|
334
334
|
|
|
@@ -358,7 +358,7 @@ Set `TDK_DEBUG=1` before running `flisk up --verbose` to include Starlark debug
|
|
|
358
358
|
- [CLI command cheat sheet](cli/README.md#cheat-sheet)
|
|
359
359
|
- [Mega cheat sheet: every command and flag](docs/cheat-sheet.md)
|
|
360
360
|
- [Documentation index](docs/README.md)
|
|
361
|
-
- [Pilot Flisk on a real repository](docs/adopt-
|
|
361
|
+
- [Pilot Flisk on a real repository](docs/adopt-flisk.md)
|
|
362
362
|
- [Running AI agents in Flisk](docs/agents/ai-agents.md)
|
|
363
363
|
- [FAQ for agencies and multi-client teams](docs/faq-teams.md)
|
|
364
364
|
- [Working alongside Helm](docs/with-helm.md)
|
|
@@ -384,7 +384,7 @@ Set `TDK_DEBUG=1` before running `flisk up --verbose` to include Starlark debug
|
|
|
384
384
|
- [Architecture and repository map](docs/project-overview.md)
|
|
385
385
|
- [Scale fixture measurements and caveats](docs/benchmarks/scale-bench.md)
|
|
386
386
|
- [Benchmark report: warm and cold start charts, history](https://flisk-sh.github.io/flisk/) (updated on every change to `main` that affects start-up)
|
|
387
|
-
- [How to run the start-time benchmark](scripts/benchmark/README.md#
|
|
387
|
+
- [How to run the start-time benchmark](scripts/benchmark/README.md#flisk-up-warm-start-benchmark)
|
|
388
388
|
|
|
389
389
|
## History
|
|
390
390
|
|
package/cli/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// Copyright (c) 2026 Flisk Landscape contributors
|
|
3
3
|
// SPDX-License-Identifier: MIT
|
|
4
|
-
import { existsSync } from "node:fs";
|
|
4
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
5
5
|
import { resolve } from "node:path";
|
|
6
6
|
import { cwd } from "node:process";
|
|
7
7
|
import chalk from "chalk";
|
|
@@ -32,24 +32,44 @@ if (templateArg) {
|
|
|
32
32
|
const { PROJECT_TEMPLATES } = await import("./utils/project-templates.js");
|
|
33
33
|
await assertMachineReadyOrExit({ scope: "scaffold" });
|
|
34
34
|
const runtimeNextSteps = formatRuntimeNextSteps(detectRuntimePrerequisites());
|
|
35
|
-
|
|
35
|
+
// A second demo in the same folder gets its own directory instead of failing.
|
|
36
|
+
const { nextAvailableProjectDir } = await import("./utils/one-command.js");
|
|
37
|
+
const baseName = templateTargetDir(templateArg, projectArg);
|
|
38
|
+
const targetName = nextAvailableProjectDir(baseName, (candidate) => {
|
|
39
|
+
const path = resolve(cwd(), candidate);
|
|
40
|
+
return existsSync(path) && readdirSync(path).length > 0;
|
|
41
|
+
});
|
|
42
|
+
const destination = resolve(cwd(), targetName);
|
|
43
|
+
if (targetName !== baseName) {
|
|
44
|
+
console.log(chalk.gray(`The folder ${baseName} is taken, using ${targetName} instead.`));
|
|
45
|
+
}
|
|
46
|
+
await cloneProjectTemplate(templateArg, targetName, runtimeNextSteps);
|
|
36
47
|
const install = ensureGlobalInstall();
|
|
37
48
|
if (install === "installed") {
|
|
38
49
|
console.log("\n\u2714 flisk is installed globally, so `flisk` works from any folder.");
|
|
39
50
|
}
|
|
40
|
-
const destination = resolve(cwd(), templateTargetDir(templateArg, projectArg));
|
|
41
51
|
const binPath = process.argv[1];
|
|
42
52
|
if (binPath && existsSync(destination)) {
|
|
43
|
-
const {
|
|
53
|
+
const { runStackQuiet } = await import("./utils/one-command.js");
|
|
44
54
|
const { openFirstFrontend } = await import("./commands/open.js");
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
55
|
+
const { startSpinner } = await import("./utils/spinner.js");
|
|
56
|
+
const spinner = startSpinner("Starting the stack (the first build takes a minute)...");
|
|
57
|
+
const outcome = await runStackQuiet(binPath, destination);
|
|
58
|
+
if (!outcome.ok) {
|
|
59
|
+
spinner.fail("The stack did not come up.");
|
|
60
|
+
console.log(chalk.yellow(outcome.error ?? "The stack stopped before it was ready."));
|
|
61
|
+
console.log(chalk.gray("Run `flisk up` in that folder to see the full output."));
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
spinner.succeed("The stack is up.");
|
|
65
|
+
// The browser only opens when a frontend actually answers, the same way `flisk open` does.
|
|
66
|
+
const opened = await openFirstFrontend(destination).catch((error) => {
|
|
67
|
+
console.log(chalk.yellow(`Could not open a browser: ${error instanceof Error ? error.message : String(error)}`));
|
|
68
|
+
return null;
|
|
52
69
|
});
|
|
70
|
+
if (!opened) {
|
|
71
|
+
console.log(chalk.gray(`The stack is running. Open it yourself: cd ${destination} && flisk open`));
|
|
72
|
+
}
|
|
53
73
|
}
|
|
54
74
|
process.exit(0);
|
|
55
75
|
}
|
package/cli/dist/commands/mcp.js
CHANGED
|
@@ -8,7 +8,10 @@ import { getPackageVersion } from "../utils/paths.js";
|
|
|
8
8
|
export const mcpCommand = new Command("mcp")
|
|
9
9
|
.description("Run a Model Context Protocol server over stdio for coding agents")
|
|
10
10
|
.action(async () => {
|
|
11
|
-
const server = createMcpServer(createTdkTools(), {
|
|
11
|
+
const server = createMcpServer(createTdkTools(), {
|
|
12
|
+
name: "flisk",
|
|
13
|
+
version: getPackageVersion(),
|
|
14
|
+
});
|
|
12
15
|
// Stdout is the protocol channel. Nothing else may write to it; diagnostics go to stderr.
|
|
13
16
|
const lines = createInterface({ input: process.stdin });
|
|
14
17
|
const pending = new Set();
|
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
import chalk from "chalk";
|
|
4
4
|
import { Command } from "commander";
|
|
5
|
-
import { discoverResourcesWithProblems } from "../utils/services.js";
|
|
6
5
|
import { showDetail, showStep } from "../utils/formatting.js";
|
|
7
6
|
import { getHostPortPlan } from "../utils/host-port-config.js";
|
|
8
7
|
import { findProjectRoot } from "../utils/paths.js";
|
|
9
8
|
import { resolveServicePath, resolveSubdomainBases } from "../utils/service-urls.js";
|
|
9
|
+
import { discoverResourcesWithProblems } from "../utils/services.js";
|
|
10
10
|
import { readProjectName } from "../utils/shared-infra.js";
|
|
11
|
+
import { startSpinner } from "../utils/spinner.js";
|
|
11
12
|
import { openUrl } from "../utils/url-actions.js";
|
|
12
13
|
/** The frontends of the current project, with the URL each one is published under. */
|
|
13
14
|
export async function listFrontendTargets(projectRoot) {
|
|
@@ -31,18 +32,24 @@ export async function isFrontendReady(url, timeoutMs = 2000) {
|
|
|
31
32
|
return false;
|
|
32
33
|
}
|
|
33
34
|
}
|
|
34
|
-
/**
|
|
35
|
-
|
|
35
|
+
/**
|
|
36
|
+
* The first frontend that answers. Returns undefined when none answers within the wait, so the
|
|
37
|
+
* caller can say so instead of opening a URL that refuses the connection.
|
|
38
|
+
*/
|
|
39
|
+
export async function pickReadyFrontend(targets, waitMs = 30000, onWait) {
|
|
36
40
|
if (targets.length === 0)
|
|
37
41
|
return undefined;
|
|
38
|
-
const
|
|
42
|
+
const started = Date.now();
|
|
43
|
+
const deadline = started + waitMs;
|
|
39
44
|
while (true) {
|
|
40
45
|
for (const target of targets) {
|
|
41
46
|
if (await isFrontendReady(target.url))
|
|
42
47
|
return target;
|
|
43
48
|
}
|
|
44
|
-
|
|
45
|
-
|
|
49
|
+
const elapsed = Date.now() - started;
|
|
50
|
+
if (elapsed >= waitMs)
|
|
51
|
+
return undefined;
|
|
52
|
+
onWait?.(elapsed);
|
|
46
53
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
47
54
|
}
|
|
48
55
|
}
|
|
@@ -68,9 +75,21 @@ export async function openFirstFrontend(projectRootOverride) {
|
|
|
68
75
|
for (const target of targets) {
|
|
69
76
|
console.log(chalk.gray(` - ${target.name}: ${target.url}`));
|
|
70
77
|
}
|
|
71
|
-
const
|
|
72
|
-
|
|
78
|
+
const spinner = startSpinner("Waiting for the frontend to answer...");
|
|
79
|
+
const first = await pickReadyFrontend(targets, 120000, (elapsed) => {
|
|
80
|
+
spinner.text = `Waiting for the frontend to answer... ${Math.round(elapsed / 1000)}s`;
|
|
81
|
+
});
|
|
82
|
+
if (!first) {
|
|
83
|
+
spinner.fail("No frontend answered yet.");
|
|
84
|
+
console.log(chalk.yellow("The stack is not serving the frontend yet."));
|
|
85
|
+
console.log(chalk.gray(" - Start the stack in another terminal: flisk up"));
|
|
86
|
+
console.log(chalk.gray(" - Then run this again: flisk open"));
|
|
87
|
+
for (const target of targets) {
|
|
88
|
+
console.log(chalk.gray(` - ${target.name}: ${target.url}`));
|
|
89
|
+
}
|
|
73
90
|
return null;
|
|
91
|
+
}
|
|
92
|
+
spinner.succeed(`The frontend is up: ${first.name}`);
|
|
74
93
|
const opened = await openUrl(first.url);
|
|
75
94
|
console.log(opened
|
|
76
95
|
? chalk.green(`\nOpened ${first.url}`)
|
|
@@ -9,6 +9,7 @@ import { logsCommand } from "./logs.js";
|
|
|
9
9
|
import { maintainersCommand } from "./maintainers.js";
|
|
10
10
|
import { mcpCommand } from "./mcp.js";
|
|
11
11
|
import { networksCommand } from "./networks.js";
|
|
12
|
+
import { openCommand } from "./open.js";
|
|
12
13
|
import { projectCommand } from "./project.js";
|
|
13
14
|
import { projectsCommand } from "./projects.js";
|
|
14
15
|
import { resourceCommand } from "./resource.js";
|
|
@@ -17,7 +18,6 @@ import { restartCommand } from "./restart.js";
|
|
|
17
18
|
import { runtimeCommand } from "./runtime.js";
|
|
18
19
|
import { stackCommand } from "./stack.js";
|
|
19
20
|
import { stacksCommand } from "./stacks.js";
|
|
20
|
-
import { openCommand } from "./open.js";
|
|
21
21
|
import { statusCommand } from "./status.js";
|
|
22
22
|
import { uiCommand } from "./ui.js";
|
|
23
23
|
import { upCommand } from "./up.js";
|
package/cli/dist/commands/ui.js
CHANGED
|
@@ -45,7 +45,7 @@ export const LoadingScreen = ({ message = "Discovering resources...", animated =
|
|
|
45
45
|
// biome-ignore lint/correctness/noUnusedFunctionParameters: reserved callback prop kept in the component API
|
|
46
46
|
const ErrorScreen = ({ error, onRetry }) => {
|
|
47
47
|
const theme = useTUITheme();
|
|
48
|
-
return (_jsxs(Box, { flexDirection: "column", padding: 2, alignItems: "center", children: [_jsx(Text, { bold: true, color: theme.error, children: "Could Not Load Services" }), _jsx(Box, { marginY: 1 }), _jsxs(Text, { color: theme.error, children: [theme.ascii ? "[x]" : "\u2717", " ", error] }), _jsx(Box, { marginY: 1 }), _jsx(Text, { color: theme.muted, children: "Troubleshooting:" }), _jsx(Text, { color: theme.muted, children: " 1. Check that every service.json is valid JSON" }),
|
|
48
|
+
return (_jsxs(Box, { flexDirection: "column", padding: 2, alignItems: "center", children: [_jsx(Text, { bold: true, color: theme.error, children: "Could Not Load Services" }), _jsx(Box, { marginY: 1 }), _jsxs(Text, { color: theme.error, children: [theme.ascii ? "[x]" : "\u2717", " ", error] }), _jsx(Box, { marginY: 1 }), _jsx(Text, { color: theme.muted, children: "Troubleshooting:" }), _jsx(Text, { color: theme.muted, children: " 1. Check that every service.json is valid JSON" }), _jsxs(Text, { color: theme.muted, children: [" ", "2. Run flisk from your project (the folder with the Tiltfile)"] }), _jsx(Text, { color: theme.muted, children: " 3. Try: flisk status --verbose" }), _jsx(Box, { marginY: 1 }), _jsx(Text, { color: theme.accent, children: "Press [r] to retry or [q] to quit" })] }));
|
|
49
49
|
};
|
|
50
50
|
const EmptyState = ({ message }) => {
|
|
51
51
|
const theme = useTUITheme();
|
|
@@ -7,6 +7,7 @@ import { basename, dirname, join, resolve, win32 } from "node:path";
|
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
8
|
import chalk from "chalk";
|
|
9
9
|
import { Command } from "commander";
|
|
10
|
+
import { LEGACY_PACKAGE } from "../utils/cli-command.js";
|
|
10
11
|
import { addYesOption, handleDryRun } from "../utils/command-helpers.js";
|
|
11
12
|
import { PACKAGE_JSON } from "../utils/constants.js";
|
|
12
13
|
import { getErrorMessage, logVerbose, showErrorAndExit } from "../utils/errors.js";
|
|
@@ -363,9 +364,7 @@ export async function getLatestVersion() {
|
|
|
363
364
|
* before it starts. The removal is best effort: the package is often not installed at all.
|
|
364
365
|
*/
|
|
365
366
|
function removeLegacyInstallation(manager) {
|
|
366
|
-
const command = manager === "npm"
|
|
367
|
-
? "npm uninstall -g @tdk-landscape/tdk-cli-core"
|
|
368
|
-
: "bun remove -g @tdk-landscape/tdk-cli-core";
|
|
367
|
+
const command = manager === "npm" ? `npm uninstall -g ${LEGACY_PACKAGE}` : `bun remove -g ${LEGACY_PACKAGE}`;
|
|
369
368
|
try {
|
|
370
369
|
execSync(command, { stdio: ["ignore", "ignore", "ignore"], timeout: 60000 });
|
|
371
370
|
}
|
package/cli/dist/mcp/tools.js
CHANGED
|
@@ -79,7 +79,10 @@ export function toToolResult(run) {
|
|
|
79
79
|
return {
|
|
80
80
|
data: {
|
|
81
81
|
errors: [
|
|
82
|
-
{
|
|
82
|
+
{
|
|
83
|
+
code: "NO_JSON",
|
|
84
|
+
message: run.stderr.trim() || `flisk exited with code ${run.exitCode}`,
|
|
85
|
+
},
|
|
83
86
|
],
|
|
84
87
|
},
|
|
85
88
|
isError: true,
|
|
@@ -120,7 +123,7 @@ export const defaultUpDeps = {
|
|
|
120
123
|
},
|
|
121
124
|
sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
122
125
|
now: () => Date.now(),
|
|
123
|
-
logFile: () => join(tmpdir(), `
|
|
126
|
+
logFile: () => join(tmpdir(), `flisk-mcp-up-${Date.now()}.log`),
|
|
124
127
|
};
|
|
125
128
|
export function buildUpArgs(args) {
|
|
126
129
|
const stack = optionalName(args.stack, "stack");
|
|
@@ -7,7 +7,15 @@ import { delimiter, join } from "node:path";
|
|
|
7
7
|
export const CLI_COMMAND = "flisk";
|
|
8
8
|
/** The npm package that installs it. */
|
|
9
9
|
export const CLI_PACKAGE = "@flisk-sh/flisk";
|
|
10
|
-
/** The
|
|
10
|
+
/** The repository the CLI is released from. */
|
|
11
|
+
export const CLI_REPOSITORY = "https://github.com/flisk-sh/flisk";
|
|
12
|
+
/**
|
|
13
|
+
* The package the CLI was published under before the rename. It owns the old `flisk` command.
|
|
14
|
+
*
|
|
15
|
+
* The old name stays on purpose: that package is still published, and it is the one a
|
|
16
|
+
* machine has to remove before the current package can own the command. Naming the
|
|
17
|
+
* current package here would uninstall the CLI instead of the leftover install.
|
|
18
|
+
*/
|
|
11
19
|
export const LEGACY_PACKAGE = "@tdk-landscape/tdk-cli-core";
|
|
12
20
|
/** True when the command can be typed as-is, so `flisk ...` resolves. */
|
|
13
21
|
export function isCommandOnPath(command = CLI_COMMAND, env = process.env) {
|
|
@@ -71,7 +79,8 @@ export function readyNextStep(env = process.env, cwd = process.cwd()) {
|
|
|
71
79
|
return (`The \`${CLI_COMMAND}\` command is not on PATH (this run is not a global install). ` +
|
|
72
80
|
`Install it with \`npm install -g ${CLI_PACKAGE}\`, then run \`${CLI_COMMAND} project --yes\`. ` +
|
|
73
81
|
`If npm reports EEXIST for \`flisk\`, the old package still owns that command: ` +
|
|
74
|
-
`run \`npm uninstall -g ${LEGACY_PACKAGE}\` first
|
|
82
|
+
`run \`npm uninstall -g ${LEGACY_PACKAGE}\` first. ` +
|
|
83
|
+
`The current release is published from ${CLI_REPOSITORY}.`);
|
|
75
84
|
}
|
|
76
85
|
if (isEmptyWorkspace(cwd)) {
|
|
77
86
|
return `Next: ${CLI_COMMAND} project --yes`;
|
|
@@ -366,7 +366,7 @@ export async function checkDuplicateTdkInstalls(installs) {
|
|
|
366
366
|
didPass: false,
|
|
367
367
|
isWarning: true,
|
|
368
368
|
message: `${found.length} flisk installs on PATH${versions.size > 1 ? " with different versions" : ""}: ${list}`,
|
|
369
|
-
fix: "Keep one: remove the others, or run flisk upgrade. The first on PATH is the one your shell runs; tools that launch /usr/local/bin/
|
|
369
|
+
fix: "Keep one: remove the others, or run flisk upgrade. The first on PATH is the one your shell runs; tools that launch /usr/local/bin/flisk directly may use another",
|
|
370
370
|
};
|
|
371
371
|
}
|
|
372
372
|
/**
|
|
@@ -18,33 +18,80 @@ export function findFrontendUrl(output) {
|
|
|
18
18
|
}
|
|
19
19
|
return undefined;
|
|
20
20
|
}
|
|
21
|
+
/** Reads the `{schemaVersion, data, errors}` envelope `flisk up --json` prints when it is done. */
|
|
22
|
+
export function parseStackEnvelope(output) {
|
|
23
|
+
for (const line of output.split("\n")) {
|
|
24
|
+
const trimmed = line.trim();
|
|
25
|
+
if (!trimmed.startsWith("{"))
|
|
26
|
+
continue;
|
|
27
|
+
try {
|
|
28
|
+
const parsed = JSON.parse(trimmed);
|
|
29
|
+
const data = parsed.data ?? {};
|
|
30
|
+
const ok = data.ok === true;
|
|
31
|
+
return {
|
|
32
|
+
ok,
|
|
33
|
+
error: ok ? undefined : (parsed.errors?.[0]?.message ?? "the stack did not come up"),
|
|
34
|
+
raw: output,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// Not the envelope; keep looking.
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
21
43
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
44
|
+
* Starts the stack quietly and reports when it is ready.
|
|
45
|
+
*
|
|
46
|
+
* `flisk up --json` prints one JSON object when the stack is up (or has failed) and keeps
|
|
47
|
+
* Tilt's own build logs out of the way, so the caller can show a progress line instead of
|
|
48
|
+
* thousands of Docker and Traefik lines. The stack keeps running in the background.
|
|
25
49
|
*/
|
|
26
|
-
export function
|
|
50
|
+
export function runStackQuiet(binPath, cwd, env = process.env, spawnFn = spawn) {
|
|
27
51
|
return new Promise((resolve) => {
|
|
28
|
-
const child = spawnFn(process.execPath, [binPath, "up"], {
|
|
52
|
+
const child = spawnFn(process.execPath, [binPath, "up", "--json"], {
|
|
29
53
|
cwd,
|
|
30
54
|
env,
|
|
31
|
-
stdio: ["inherit", "pipe", "
|
|
55
|
+
stdio: ["inherit", "pipe", "pipe"],
|
|
32
56
|
});
|
|
33
57
|
let output = "";
|
|
34
|
-
let
|
|
58
|
+
let settled = false;
|
|
59
|
+
const settle = (outcome) => {
|
|
60
|
+
if (settled)
|
|
61
|
+
return;
|
|
62
|
+
settled = true;
|
|
63
|
+
// The stack outlives this command: let the child run on its own.
|
|
64
|
+
child.unref?.();
|
|
65
|
+
resolve(outcome);
|
|
66
|
+
};
|
|
35
67
|
child.stdout?.on("data", (chunk) => {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
onFrontendUrl?.(url);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
68
|
+
output += String(chunk);
|
|
69
|
+
const envelope = parseStackEnvelope(output);
|
|
70
|
+
if (envelope)
|
|
71
|
+
settle(envelope);
|
|
72
|
+
});
|
|
73
|
+
child.stderr?.on("data", (chunk) => {
|
|
74
|
+
output += String(chunk);
|
|
46
75
|
});
|
|
47
|
-
child.on("error", (error) =>
|
|
48
|
-
child.on("close", (code) =>
|
|
76
|
+
child.on("error", (error) => settle({ ok: false, error: error.message, raw: output }));
|
|
77
|
+
child.on("close", (code) => settle({
|
|
78
|
+
ok: false,
|
|
79
|
+
error: `the stack stopped before it was ready (code ${code})`,
|
|
80
|
+
raw: output,
|
|
81
|
+
}));
|
|
49
82
|
});
|
|
50
83
|
}
|
|
84
|
+
/**
|
|
85
|
+
* A free directory for the project. A second demo in the same folder gets its own directory
|
|
86
|
+
* (`name-2`, `name-3`, ...) instead of failing because the first one is still there.
|
|
87
|
+
*/
|
|
88
|
+
export function nextAvailableProjectDir(base, isTaken) {
|
|
89
|
+
if (!isTaken(base))
|
|
90
|
+
return base;
|
|
91
|
+
for (let suffix = 2; suffix < 1000; suffix += 1) {
|
|
92
|
+
const candidate = `${base}-${suffix}`;
|
|
93
|
+
if (!isTaken(candidate))
|
|
94
|
+
return candidate;
|
|
95
|
+
}
|
|
96
|
+
return base;
|
|
97
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import{createRequire as __tdkRequire}from'node:module';const require=__tdkRequire(import.meta.url);
|
|
2
|
+
function x(r,e,t){let n=r.indexOf(e);if(n===-1)return r;let o=e.length,s=0,i="";do i+=r.slice(s,n)+e+t,s=n+o,n=r.indexOf(e,s);while(n!==-1);return i+=r.slice(s),i}function I(r,e,t,n){let o=0,s="";do{let i=r[n-1]==="\r";s+=r.slice(o,i?n-1:n)+e+(i?`\r
|
|
3
|
+
`:`
|
|
4
|
+
`)+t,o=n+1,n=r.indexOf(`
|
|
5
|
+
`,o)}while(n!==-1);return s+=r.slice(o),s}var F=(r=0)=>e=>`\x1B[${e+r}m`,T=(r=0)=>e=>`\x1B[${38+r};5;${e}m`,R=(r=0)=>(e,t,n)=>`\x1B[${38+r};2;${e};${t};${n}m`,$=r=>`\x1B[58;5;${r<90?r-30:r-90+8}m`,l={modifier:{reset:[0,0],bold:[1,22],dim:[2,22],italic:[3,23],underline:[4,24],underlineDouble:["4:2",24],underlineCurly:["4:3",24],underlineDotted:["4:4",24],underlineDashed:["4:5",24],overline:[53,55],inverse:[7,27],hidden:[8,28],strikethrough:[9,29]},color:{black:[30,39],red:[31,39],green:[32,39],yellow:[33,39],blue:[34,39],magenta:[35,39],cyan:[36,39],white:[37,39],blackBright:[90,39],gray:[90,39],grey:[90,39],redBright:[91,39],greenBright:[92,39],yellowBright:[93,39],blueBright:[94,39],magentaBright:[95,39],cyanBright:[96,39],whiteBright:[97,39]},bgColor:{bgBlack:[40,49],bgRed:[41,49],bgGreen:[42,49],bgYellow:[43,49],bgBlue:[44,49],bgMagenta:[45,49],bgCyan:[46,49],bgWhite:[47,49],bgBlackBright:[100,49],bgGray:[100,49],bgGrey:[100,49],bgRedBright:[101,49],bgGreenBright:[102,49],bgYellowBright:[103,49],bgBlueBright:[104,49],bgMagentaBright:[105,49],bgCyanBright:[106,49],bgWhiteBright:[107,49]},underlineColor:{underlineBlack:["58;5;0",59],underlineRed:["58;5;1",59],underlineGreen:["58;5;2",59],underlineYellow:["58;5;3",59],underlineBlue:["58;5;4",59],underlineMagenta:["58;5;5",59],underlineCyan:["58;5;6",59],underlineWhite:["58;5;7",59],underlineBlackBright:["58;5;8",59],underlineGray:["58;5;8",59],underlineGrey:["58;5;8",59],underlineRedBright:["58;5;9",59],underlineGreenBright:["58;5;10",59],underlineYellowBright:["58;5;11",59],underlineBlueBright:["58;5;12",59],underlineMagentaBright:["58;5;13",59],underlineCyanBright:["58;5;14",59],underlineWhiteBright:["58;5;15",59]}},M=Object.keys(l.modifier),y=Object.keys(l.color),E=Object.keys(l.bgColor),U=Object.keys(l.underlineColor),S=[...y,...E];function V(){let r=new Map;for(let[e,t]of Object.entries(l)){for(let[n,o]of Object.entries(t))l[n]={open:`\x1B[${o[0]}m`,close:`\x1B[${o[1]}m`},t[n]=l[n],r.set(Number.parseInt(o[0],10),o[1]);Object.defineProperty(l,e,{value:t,enumerable:!1})}return Object.defineProperty(l,"codes",{value:r,enumerable:!1}),l.color.close="\x1B[39m",l.bgColor.close="\x1B[49m",l.underlineColor.close="\x1B[59m",l.color.ansi=F(),l.color.ansi256=T(),l.color.ansi16m=R(),l.bgColor.ansi=F(10),l.bgColor.ansi256=T(10),l.bgColor.ansi16m=R(10),l.underlineColor.ansi=$,l.underlineColor.ansi256=T(20),l.underlineColor.ansi16m=R(20),Object.defineProperties(l,{rgbToAnsi256:{value(e,t,n){return e===t&&t===n?e<8?16:e>248?231:Math.round((e-8)/247*24)+232:16+36*Math.round(e/255*5)+6*Math.round(t/255*5)+Math.round(n/255*5)},enumerable:!1},hexToRgb:{value(e){let t=/[\da-f]{6}|[\da-f]{3}/i.exec(e.toString(16));if(!t)return[0,0,0];let[n]=t;n.length===3&&(n=[...n].map(s=>s+s).join(""));let o=Number.parseInt(n,16);return[o>>16&255,o>>8&255,o&255]},enumerable:!1},hexToAnsi256:{value:e=>l.rgbToAnsi256(...l.hexToRgb(e)),enumerable:!1},ansi256ToAnsi:{value(e){if(e<8)return 30+e;if(e<16)return 90+(e-8);let t,n,o;if(e>=232)t=((e-232)*10+8)/255,n=t,o=t;else{e-=16;let f=e%36;t=Math.floor(e/36)/5,n=Math.floor(f/6)/5,o=f%6/5}let s=Math.max(t,n,o)*2;if(s===0)return 30;let i=30+(Math.round(o)<<2|Math.round(n)<<1|Math.round(t));return s===2&&(i+=60),i},enumerable:!1},rgbToAnsi:{value:(e,t,n)=>l.ansi256ToAnsi(l.rgbToAnsi256(e,t,n)),enumerable:!1},hexToAnsi:{value:e=>l.ansi256ToAnsi(l.hexToAnsi256(e)),enumerable:!1}}),l}var W=V(),c=W;import A from"node:process";import K from"node:os";import v from"node:tty";function a(r,e=globalThis.Deno?globalThis.Deno.args:A.argv){let t=r.startsWith("-")?"":r.length===1?"-":"--",n=e.indexOf(t+r),o=e.indexOf("--");return n!==-1&&(o===-1||n<o)}var{env:u}=A,g;a("no-color")||a("no-colors")||a("color=false")||a("color=never")?g=0:(a("color")||a("colors")||a("color=true")||a("color=always"))&&(g=1);function L(){return/^\d+$/.test(u.FORCE_COLOR)}function z(){if("FORCE_COLOR"in u){if(u.FORCE_COLOR==="false")return 0;if(u.FORCE_COLOR==="true"||u.FORCE_COLOR.length===0)return 1;if(L())return Math.min(Number.parseInt(u.FORCE_COLOR,10),3)}}function H(r){return r===0?!1:{level:r,hasBasic:!0,has256:r>=2,has16m:r>=3}}function q(r,{streamIsTTY:e,sniffFlags:t=!0}={}){let n=z();n!==void 0&&(g=n);let o=t?g:n;if(o===0)return 0;if(t){if(a("color=16m")||a("color=full")||a("color=truecolor"))return 3;if(a("color=256"))return 2}if(o!==void 0&&L())return o;if("TF_BUILD"in u&&"AGENT_NAME"in u)return 1;if(r&&!e&&o===void 0)return 0;let s=o||0;if(u.TERM==="dumb")return s;if(A.platform==="win32"){let i=K.release().split(".");return Number(i[0])>=10&&Number(i[2])>=10586?Number(i[2])>=14931?3:2:1}if("CI"in u)return["GITHUB_ACTIONS","GITEA_ACTIONS","CIRCLECI"].some(i=>i in u)?3:["TRAVIS","APPVEYOR","GITLAB_CI","BUILDKITE","DRONE"].some(i=>i in u)||u.CI_NAME==="codeship"?1:s;if("TEAMCITY_VERSION"in u)return/^(?:9\.0*[1-9]\d*\.|\d{2,}\.)/.test(u.TEAMCITY_VERSION)?1:0;if(u.COLORTERM==="truecolor"||u.TERM==="xterm-kitty"||u.TERM==="xterm-ghostty"||u.TERM==="wezterm")return 3;if("TERM_PROGRAM"in u){let i=Number.parseInt((u.TERM_PROGRAM_VERSION||"").split(".",1)[0],10);switch(u.TERM_PROGRAM){case"iTerm.app":return i>=3?3:2;case"Apple_Terminal":return 2}}return/-256(?:color)?$/i.test(u.TERM)?2:/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(u.TERM)||"COLORTERM"in u?1:s}function _(r,e={}){let t=q(r,{streamIsTTY:r&&r.isTTY,...e});return H(t)}var J={stdout:_({isTTY:v.isatty(1)}),stderr:_({isTTY:v.isatty(2)})},w=J;var{stdout:j,stderr:G}=w,h=Symbol("GENERATOR"),b=Symbol("STYLER"),O=Symbol("IS_EMPTY"),C=Symbol("LEVEL"),m=Object.create(null),k=r=>{if(!Number.isSafeInteger(r)||r<0||r>3)throw new Error("The `level` should be an integer from 0 to 3")},Q={enumerable:!0,get(){return this[C]},set(r){k(r),this[C]=r}},X=(r,e={})=>{e.level!==void 0&&k(e.level);let t=j?j.level:0;r[C]=e.level===void 0?t:e.level};var Z=r=>{let e=(...t)=>t.join(" ");return X(e,r),Object.setPrototypeOf(e,p.prototype),e};function p(r){return Z(r)}Object.setPrototypeOf(p.prototype,Function.prototype);for(let[r,e]of Object.entries(c))m[r]={get(){let t=B(this,P(e.open,e.close,this[b]),this[O]);return Object.defineProperty(this,r,{value:t}),t}};m.visible={get(){let r=B(this,this[b],!0);return Object.defineProperty(this,"visible",{value:r}),r}};var ee=(r,e)=>{let t=c[e];if(r==="rgb"){let o=(i,f,d)=>t.ansi(c.rgbToAnsi(i,f,d));return[o,o,(i,f,d)=>t.ansi256(c.rgbToAnsi256(i,f,d)),t.ansi16m]}if(r==="hex"){let o=i=>t.ansi(c.hexToAnsi(i));return[o,o,i=>t.ansi256(c.hexToAnsi256(i)),i=>t.ansi16m(...c.hexToRgb(i))]}let n=o=>t.ansi(c.ansi256ToAnsi(o));return[n,n,t.ansi256,t.ansi256]},re=["rgb","hex","ansi256"];for(let r of re){let e=r[0].toUpperCase()+r.slice(1);for(let[t,n]of[[r,"color"],["bg"+e,"bgColor"],["underline"+e,"underlineColor"]]){let{close:o}=c[n],s=ee(r,n);m[t]={get(){let i=function(f,d,D){let Y=s[this.level](f,d,D);return B(this,P(Y,o,this[b]),this[O])};return Object.defineProperty(this,t,{value:i}),i}}}}var te=Object.defineProperties(()=>{},{...m,level:{enumerable:!0,get(){return this[h].level},set(r){this[h].level=r}}}),P=(r,e,t)=>{let n,o;return t===void 0?(n=r,o=e):(n=t.openAll+r,o=e+t.closeAll),{open:r,close:e,openAll:n,closeAll:o,parent:t}},B=(r,e,t)=>{let n=(...o)=>o.length===1?N(n,""+o[0]):o.length===2?N(n,`${o[0]??""} ${o[1]??""}`):N(n,o.join(" "));return Object.setPrototypeOf(n,te),n[h]=r[h]??r,n[b]=e,n[O]=t,n},N=(r,e)=>{if(r[h][C]<=0||!e)return r[O]?"":e;let t=r[b];if(t===void 0)return e;let{openAll:n,closeAll:o}=t;if(e.includes("\x1B"))for(;t!==void 0;)e=x(e,t.close,t.open),t=t.parent;let s=e.indexOf(`
|
|
6
|
+
`);return s!==-1&&(e=I(e,o,n,s)),n+e+o};Object.defineProperties(p.prototype,{...m,level:Q});var ne=p(),he=p({level:G?G.level:0});var be=ne;export{y as a,be as b};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import{createRequire as __tdkRequire}from'node:module';const require=__tdkRequire(import.meta.url);
|
|
2
|
+
import{b as n}from"./chunk-CF46CD6R.js";var o=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"];function w(f){let r=process.stderr,i=r.isTTY===!0,s=0,t=f,a,l=()=>r.write(`\r\x1B[2K${n.cyan(o[s])} ${t}`);i?(l(),a=setInterval(()=>{s=(s+1)%o.length,l()},80)):r.write(`- ${t}
|
|
3
|
+
`);let c=(e,$)=>{a&&clearInterval(a),r.write(`${i?"\r\x1B[2K":""}${e} ${$??t}
|
|
4
|
+
`)};return{get text(){return t},set text(e){t=e,i||r.write(`- ${t}
|
|
5
|
+
`)},succeed:e=>c(n.green("\u2714"),e),fail:e=>c(n.red("\u2716"),e),warn:e=>c(n.yellow("\u26A0"),e)}}export{w as a};
|