@flisk-sh/flisk 1.3.195 → 1.3.199
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 +56 -11
- package/cli/dist/commands/mcp.js +4 -1
- package/cli/dist/commands/open.js +35 -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 +112 -19
- package/cli/dist-bundle/{chunk-CXFHRZLM.js → chunk-3SPYAO2E.js} +1 -1
- package/cli/dist-bundle/chunk-6357RMGA.js +4 -0
- package/cli/dist-bundle/{chunk-32NCVCS7.js → chunk-7IWOM6M2.js} +1 -1
- package/cli/dist-bundle/chunk-CF46CD6R.js +6 -0
- package/cli/dist-bundle/chunk-DTIHDMED.js +5 -0
- package/cli/dist-bundle/chunk-GW7N23FV.js +21 -0
- package/cli/dist-bundle/{chunk-3RB2FQ46.js → chunk-HSNRMF4B.js} +9 -9
- package/cli/dist-bundle/{chunk-ZDH5BP3H.js → chunk-WMK2PS63.js} +2 -2
- package/cli/dist-bundle/cli.js +259 -265
- package/cli/dist-bundle/cold-preflight-WR2M53Z6.js +2 -0
- package/cli/dist-bundle/one-command-7OW747ML.js +4 -0
- package/cli/dist-bundle/open-635MSZHV.js +2 -0
- package/cli/dist-bundle/project-R7P6QBF4.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,69 @@ 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
|
+
// A stack for this project may already be running (the same example in another folder
|
|
57
|
+
// shares its compose project). Then there is nothing to start: open what is up.
|
|
58
|
+
const { findReadyFrontend } = await import("./commands/open.js");
|
|
59
|
+
const running = await findReadyFrontend(destination, 0);
|
|
60
|
+
if (running) {
|
|
61
|
+
console.log(chalk.gray(`A stack for this project is already running (${running.name}).`));
|
|
62
|
+
const { openFirstFrontend } = await import("./commands/open.js");
|
|
63
|
+
await openFirstFrontend(destination);
|
|
64
|
+
process.exit(0);
|
|
65
|
+
}
|
|
66
|
+
// A fresh clone has no host dependencies, and the stack typechecks the services on the
|
|
67
|
+
// host, so install them before the stack starts.
|
|
68
|
+
const { installServiceDependencies } = await import("./utils/one-command.js");
|
|
69
|
+
const installSpinner = startSpinner("Installing the service dependencies...");
|
|
70
|
+
const installs = await installServiceDependencies(destination);
|
|
71
|
+
const failed = installs.filter((entry) => !entry.ok);
|
|
72
|
+
if (failed.length > 0) {
|
|
73
|
+
installSpinner.fail("Some services could not install their dependencies.");
|
|
74
|
+
for (const entry of failed) {
|
|
75
|
+
console.log(chalk.yellow(` - ${entry.dir}: run "bun install" there`));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
installSpinner.succeed("The service dependencies are installed.");
|
|
80
|
+
}
|
|
81
|
+
const spinner = startSpinner("Starting the stack (the first build takes a minute)...");
|
|
82
|
+
const outcome = await runStackQuiet(binPath, destination);
|
|
83
|
+
if (!outcome.ok) {
|
|
84
|
+
spinner.fail("The stack did not come up.");
|
|
85
|
+
console.log(chalk.yellow(outcome.error ?? "The stack stopped before it was ready."));
|
|
86
|
+
console.log(chalk.gray("Run `flisk up` in that folder to see the full output."));
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
spinner.succeed("The stack is up.");
|
|
90
|
+
// The browser only opens when a frontend actually answers, the same way `flisk open` does.
|
|
91
|
+
const opened = await openFirstFrontend(destination).catch((error) => {
|
|
92
|
+
console.log(chalk.yellow(`Could not open a browser: ${error instanceof Error ? error.message : String(error)}`));
|
|
93
|
+
return null;
|
|
52
94
|
});
|
|
95
|
+
if (!opened) {
|
|
96
|
+
console.log(chalk.gray(`The stack is running. Open it yourself: cd ${destination} && flisk open`));
|
|
97
|
+
}
|
|
53
98
|
}
|
|
54
99
|
process.exit(0);
|
|
55
100
|
}
|
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,21 +32,35 @@ 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
|
}
|
|
56
|
+
/**
|
|
57
|
+
* The first frontend of the project that answers, or undefined when none does. With a wait of
|
|
58
|
+
* zero this is a cheap "is a stack for this project already running?" check.
|
|
59
|
+
*/
|
|
60
|
+
export async function findReadyFrontend(projectRoot, waitMs = 0, onWait) {
|
|
61
|
+
const targets = await listFrontendTargets(projectRoot);
|
|
62
|
+
return pickReadyFrontend(targets, waitMs, onWait);
|
|
63
|
+
}
|
|
49
64
|
/**
|
|
50
65
|
* Opens the first ready frontend of the project in the browser. Returns the URL it opened, or
|
|
51
66
|
* null when the project has no frontend.
|
|
@@ -68,9 +83,21 @@ export async function openFirstFrontend(projectRootOverride) {
|
|
|
68
83
|
for (const target of targets) {
|
|
69
84
|
console.log(chalk.gray(` - ${target.name}: ${target.url}`));
|
|
70
85
|
}
|
|
71
|
-
const
|
|
72
|
-
|
|
86
|
+
const spinner = startSpinner("Waiting for the frontend to answer...");
|
|
87
|
+
const first = await pickReadyFrontend(targets, 120000, (elapsed) => {
|
|
88
|
+
spinner.text = `Waiting for the frontend to answer... ${Math.round(elapsed / 1000)}s`;
|
|
89
|
+
});
|
|
90
|
+
if (!first) {
|
|
91
|
+
spinner.fail("No frontend answered yet.");
|
|
92
|
+
console.log(chalk.yellow("The stack is not serving the frontend yet."));
|
|
93
|
+
console.log(chalk.gray(" - Start the stack in another terminal: flisk up"));
|
|
94
|
+
console.log(chalk.gray(" - Then run this again: flisk open"));
|
|
95
|
+
for (const target of targets) {
|
|
96
|
+
console.log(chalk.gray(` - ${target.name}: ${target.url}`));
|
|
97
|
+
}
|
|
73
98
|
return null;
|
|
99
|
+
}
|
|
100
|
+
spinner.succeed(`The frontend is up: ${first.name}`);
|
|
74
101
|
const opened = await openUrl(first.url);
|
|
75
102
|
console.log(opened
|
|
76
103
|
? 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
|
/**
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// Copyright (c) 2026 Flisk Landscape contributors
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
import { spawn } from "node:child_process";
|
|
4
|
+
import { readdirSync } from "node:fs";
|
|
5
|
+
import { join } from "node:path";
|
|
4
6
|
/** The marker the stack prints before the frontend URLs. */
|
|
5
7
|
const FRONTEND_MARKER = "Frontend URLs";
|
|
6
8
|
/** Picks the first frontend URL out of the `flisk up` output, if it printed one. */
|
|
@@ -18,33 +20,124 @@ export function findFrontendUrl(output) {
|
|
|
18
20
|
}
|
|
19
21
|
return undefined;
|
|
20
22
|
}
|
|
23
|
+
/** Reads the `{schemaVersion, data, errors}` envelope `flisk up --json` prints when it is done. */
|
|
24
|
+
export function parseStackEnvelope(output) {
|
|
25
|
+
for (const line of output.split("\n")) {
|
|
26
|
+
const trimmed = line.trim();
|
|
27
|
+
if (!trimmed.startsWith("{"))
|
|
28
|
+
continue;
|
|
29
|
+
try {
|
|
30
|
+
const parsed = JSON.parse(trimmed);
|
|
31
|
+
const data = parsed.data ?? {};
|
|
32
|
+
const ok = data.ok === true;
|
|
33
|
+
return {
|
|
34
|
+
ok,
|
|
35
|
+
error: ok ? undefined : (parsed.errors?.[0]?.message ?? "the stack did not come up"),
|
|
36
|
+
raw: output,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// Not the envelope; keep looking.
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
21
45
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
46
|
+
* Starts the stack quietly and reports when it is ready.
|
|
47
|
+
*
|
|
48
|
+
* `flisk up --json` prints one JSON object when the stack is up (or has failed) and keeps
|
|
49
|
+
* Tilt's own build logs out of the way, so the caller can show a progress line instead of
|
|
50
|
+
* thousands of Docker and Traefik lines. The stack keeps running in the background.
|
|
25
51
|
*/
|
|
26
|
-
export function
|
|
52
|
+
export function runStackQuiet(binPath, cwd, env = process.env, spawnFn = spawn) {
|
|
27
53
|
return new Promise((resolve) => {
|
|
28
|
-
const child = spawnFn(process.execPath, [binPath, "up"], {
|
|
54
|
+
const child = spawnFn(process.execPath, [binPath, "up", "--json"], {
|
|
29
55
|
cwd,
|
|
30
56
|
env,
|
|
31
|
-
stdio: ["inherit", "pipe", "
|
|
57
|
+
stdio: ["inherit", "pipe", "pipe"],
|
|
32
58
|
});
|
|
33
59
|
let output = "";
|
|
34
|
-
let
|
|
60
|
+
let settled = false;
|
|
61
|
+
const settle = (outcome) => {
|
|
62
|
+
if (settled)
|
|
63
|
+
return;
|
|
64
|
+
settled = true;
|
|
65
|
+
// The stack outlives this command: let the child run on its own.
|
|
66
|
+
child.unref?.();
|
|
67
|
+
resolve(outcome);
|
|
68
|
+
};
|
|
35
69
|
child.stdout?.on("data", (chunk) => {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
onFrontendUrl?.(url);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
70
|
+
output += String(chunk);
|
|
71
|
+
const envelope = parseStackEnvelope(output);
|
|
72
|
+
if (envelope)
|
|
73
|
+
settle(envelope);
|
|
74
|
+
});
|
|
75
|
+
child.stderr?.on("data", (chunk) => {
|
|
76
|
+
output += String(chunk);
|
|
46
77
|
});
|
|
47
|
-
child.on("error", (error) =>
|
|
48
|
-
child.on("close", (code) =>
|
|
78
|
+
child.on("error", (error) => settle({ ok: false, error: error.message, raw: output }));
|
|
79
|
+
child.on("close", (code) => settle({
|
|
80
|
+
ok: false,
|
|
81
|
+
error: `the stack stopped before it was ready (code ${code})`,
|
|
82
|
+
raw: output,
|
|
83
|
+
}));
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* A free directory for the project. A second demo in the same folder gets its own directory
|
|
88
|
+
* (`name-2`, `name-3`, ...) instead of failing because the first one is still there.
|
|
89
|
+
*/
|
|
90
|
+
export function nextAvailableProjectDir(base, isTaken) {
|
|
91
|
+
if (!isTaken(base))
|
|
92
|
+
return base;
|
|
93
|
+
for (let suffix = 2; suffix < 1000; suffix += 1) {
|
|
94
|
+
const candidate = `${base}-${suffix}`;
|
|
95
|
+
if (!isTaken(candidate))
|
|
96
|
+
return candidate;
|
|
97
|
+
}
|
|
98
|
+
return base;
|
|
99
|
+
}
|
|
100
|
+
/** The service folders of a project that have their own package.json. */
|
|
101
|
+
export function findServiceDirs(projectRoot, maxDepth = 4) {
|
|
102
|
+
const dirs = [];
|
|
103
|
+
const walk = (dir, depth) => {
|
|
104
|
+
if (depth > maxDepth)
|
|
105
|
+
return;
|
|
106
|
+
let entries;
|
|
107
|
+
try {
|
|
108
|
+
entries = readdirSync(dir, { withFileTypes: true }).map((entry) => entry.name);
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (entries.includes("package.json"))
|
|
114
|
+
dirs.push(dir);
|
|
115
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
116
|
+
if (!entry.isDirectory() || entry.name === "node_modules" || entry.name.startsWith("."))
|
|
117
|
+
continue;
|
|
118
|
+
walk(join(dir, entry.name), depth + 1);
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
walk(projectRoot, 0);
|
|
122
|
+
return dirs;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Installs the host dependencies of the services. The stack typechecks the services on the
|
|
126
|
+
* host, so a fresh clone needs its dependencies before it can come up.
|
|
127
|
+
*/
|
|
128
|
+
export async function installServiceDependencies(projectRoot, run = defaultRun) {
|
|
129
|
+
const results = [];
|
|
130
|
+
for (const dir of findServiceDirs(projectRoot)) {
|
|
131
|
+
const code = await run("bun", ["install"], dir);
|
|
132
|
+
results.push({ dir, ok: code === 0 });
|
|
133
|
+
}
|
|
134
|
+
return results;
|
|
135
|
+
}
|
|
136
|
+
/** Runs a command and resolves with its exit code. */
|
|
137
|
+
function defaultRun(command, args, cwd) {
|
|
138
|
+
return new Promise((resolve) => {
|
|
139
|
+
const child = spawn(command, args, { cwd, stdio: "ignore" });
|
|
140
|
+
child.on("error", () => resolve(1));
|
|
141
|
+
child.on("close", (code) => resolve(code ?? 1));
|
|
49
142
|
});
|
|
50
143
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import{createRequire as __tdkRequire}from'node:module';const require=__tdkRequire(import.meta.url);
|
|
2
|
-
import{c as z,e as Q}from"./chunk-42IVF3YT.js";import{
|
|
2
|
+
import{c as z,e as Q}from"./chunk-42IVF3YT.js";import{Fa as J,Ga as B,Qa as O,Ta as Y,cb as X,d as v,e as k,g as q,ga as L,h as x,i as P,j as F,k as W,l as A,p as d,q as N}from"./chunk-GW7N23FV.js";import{b as V}from"./chunk-CF46CD6R.js";import{a as $}from"./chunk-6EA4DGCY.js";import{appendFileSync as le,existsSync as C,mkdirSync as fe,readFileSync as de,writeFileSync as Z}from"node:fs";import{dirname as pe,resolve as w}from"node:path";function ft(t,e,n){for(let[r,s]of e.entries())console.log(V.blue(`${s.emoji} ${s.description}...`)),s.type==="json"?me(t,s.filename,s.content):he(t,s.filename,String(s.content)),n&&n(s,r,e.length)}function ue(t,e,n=2){let r=`${JSON.stringify(e,null,n)}
|
|
3
3
|
`;Z(t,r,"utf-8")}function me(t,e,n,r=2){let s=w(t,e);ue(s,n,r)}function U(t,e){Z(t,e,"utf-8")}function he(t,e,n){let r=w(t,e);U(r,n)}function dt(t){C(t)||fe(t,{recursive:!0})}var ge=[".env",".tdk/.tdk-out/",".tdk/.project-id",".tdk/smoke/","node_modules/"];function Se(t){let e=w(t,".gitignore"),n=C(e)?de(e,"utf-8"):"",r=new Set(n.split(`
|
|
4
4
|
`).map(f=>f.trim().replace(/\/$/,""))),s=ge.filter(f=>!r.has(f.replace(/\/$/,"")));if(s.length===0)return[];let c=n&&!n.endsWith(`
|
|
5
5
|
`)?`
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{createRequire as __tdkRequire}from'node:module';const require=__tdkRequire(import.meta.url);
|
|
2
|
+
import{a as x}from"./chunk-DTIHDMED.js";import{g as w,h as y}from"./chunk-7IWOM6M2.js";import{Ra as h,ba as d,ca as u,d as l,k as f,ra as m,s as p,ta as g}from"./chunk-GW7N23FV.js";import{b as a}from"./chunk-CF46CD6R.js";import{Command as O}from"commander";import{readFileSync as $}from"node:fs";import{join as j}from"node:path";function A(n){let e=n.config;return e?Array.isArray(e.featuresEnabled)&&e.featuresEnabled.includes(l)?!0:(Array.isArray(e.dependsOn)?e.dependsOn:[]).some(t=>y.includes(t)):!1}function H(n,e){let i=n.replace(/-/g,"_"),t=[];for(let o of[...e].sort((r,s)=>r.name.localeCompare(s.name))){let r=o.resources.filter(A).map(s=>s.name);r.length!==0&&t.push({name:`${i}_${o.name.replace(/-/g,"_")}`,stack:o.name,services:r.sort()})}return t.length===0?null:{name:"postgres",databases:t}}function k(n){try{let e=JSON.parse($(j(n,".tdk",f),"utf-8"));return typeof e?.project?.name=="string"?e.project.name:null}catch{return null}}import{spawn as P}from"node:child_process";function N(n){try{let e=new URL(n);return e.protocol==="http:"||e.protocol==="https:"}catch{return!1}}function S(n,e=process.platform,i=P){if(!N(n))return Promise.resolve(!1);let t,o;return e==="darwin"?(t="open",o=[n]):e==="win32"?(t="rundll32.exe",o=["url.dll,FileProtocolHandler",n]):(t="xdg-open",o=[n]),new Promise(r=>{try{let s=i(t,o,{stdio:"ignore",detached:!0,windowsHide:!0});s.once("error",()=>r(!1)),s.once("spawn",()=>{typeof s.unref=="function"&&s.unref(),r(!0)})}catch{r(!1)}})}function B(n,e=process.platform,i=P){return new Promise(t=>{let o,r;e==="darwin"?(o="pbcopy",r=[]):e==="win32"?(o="clip",r=[]):(o="xclip",r=["-selection","clipboard"]);try{let s=i(o,r,{stdio:["pipe","ignore","ignore"],windowsHide:!0});s.on("error",()=>{t(!1)}),s.on("close",c=>{t(c===0)}),s.stdin?(s.stdin.on("error",()=>{t(!1)}),s.stdin.write(n,"utf-8"),s.stdin.end()):t(!1)}catch{t(!1)}})}async function E(n){let{resources:e}=h(n),i=await w(n,{inspectDocker:!1}),{appBase:t}=m(i?.ingressHttp,n);return e.filter(o=>o.config?.appType==="frontend").map(o=>({name:o.name,url:`${t}${g(o)}`}))}async function b(n,e=2e3){try{return(await fetch(n,{signal:AbortSignal.timeout(e)})).status<500}catch{return!1}}async function T(n,e=3e4,i){if(n.length===0)return;let t=Date.now(),o=t+e;for(;;){for(let s of n)if(await b(s.url))return s;let r=Date.now()-t;if(r>=e)return;i?.(r),await new Promise(s=>setTimeout(s,2e3))}}async function Q(n,e=0,i){let t=await E(n);return T(t,e,i)}async function F(n){let e=n??p();if(!e)return console.log(a.yellow("Not in a Flisk project, so there is no frontend to open.")),console.log(a.gray("Run `flisk project --yes` first, or `flisk open` inside a project.")),null;let i=await E(e);if(i.length===0)return console.log(a.yellow("This project has no frontend service to open.")),console.log(a.gray("Start the stack with `flisk up` and add a frontend with `flisk resource`.")),null;let t=k(e);d(`Opening the first frontend of ${t??"this project"}`);for(let c of i)console.log(a.gray(` - ${c.name}: ${c.url}`));let o=x("Waiting for the frontend to answer..."),r=await T(i,12e4,c=>{o.text=`Waiting for the frontend to answer... ${Math.round(c/1e3)}s`});if(!r){o.fail("No frontend answered yet."),console.log(a.yellow("The stack is not serving the frontend yet.")),console.log(a.gray(" - Start the stack in another terminal: flisk up")),console.log(a.gray(" - Then run this again: flisk open"));for(let c of i)console.log(a.gray(` - ${c.name}: ${c.url}`));return null}o.succeed(`The frontend is up: ${r.name}`);let s=await S(r.url);return console.log(s?a.green(`
|
|
3
|
+
Opened ${r.url}`):a.yellow(`
|
|
4
|
+
Could not open a browser. Open ${r.url} yourself.`)),u("Run `flisk open` again to open the next frontend."),r.url}var X=new O("open").description("Open the first frontend of the project in your browser").action(async()=>{await F()});export{H as a,k as b,S as c,B as d,E as e,b as f,T as g,Q as h,F as i,X as j};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import{createRequire as __tdkRequire}from'node:module';const require=__tdkRequire(import.meta.url);
|
|
2
|
-
import{
|
|
2
|
+
import{Ta as w,cb as A,d as P,k as T}from"./chunk-GW7N23FV.js";import{execFileSync as x}from"node:child_process";import{existsSync as G,mkdirSync as W,readFileSync as D,realpathSync as Y,writeFileSync as q}from"node:fs";import{join as S,resolve as V,sep as X}from"node:path";import{createServer as U}from"node:net";import{connect as K}from"node:net";function N(e,s=500){return new Promise(t=>{let r=K({port:e,host:"127.0.0.1"}),o=n=>{r.destroy(),t(n)};r.setTimeout(s,()=>o(!1)),r.once("connect",()=>o(!0)),r.once("error",()=>o(!1))})}var J={ingressHttp:{start:8080,end:8180},ingressHttps:{start:8443,end:8543},postgres:{start:15432,end:15532}},E={ingressHttp:"TDK_HTTP_PORT",ingressHttps:"TDK_HTTPS_PORT",postgres:"TDK_POSTGRES_PORT"},H={ingressHttp:80,ingressHttps:443,postgres:5432},k={ingressHttp:H.ingressHttp,ingressHttps:H.ingressHttps};function j(e,s){return e==="EACCES"&&s==="darwin"}async function b(e){return await N(e)?!1:new Promise(s=>{let t=U();t.once("error",r=>{s(j(r.code,process.platform))}),t.listen(e,"127.0.0.1",()=>t.close(()=>s(!0)))})}function B(e,s){let t=Number(s);if(!Number.isInteger(t)||t<1||t>65535)throw new Error(`${e} must be an integer between 1 and 65535 (received ${JSON.stringify(s)})`);return t}async function O(e={}){let s=e.env??process.env,t=e.isAvailable??b,r={ingressHttp:!1,ingressHttps:!1,postgres:!1},o={ingressHttp:"",ingressHttps:"",postgres:""},n={explicit:r},c=new Set;for(let a of["ingressHttp","ingressHttps","postgres"]){let f=s[E[a]],i=f!==void 0&&f.trim()!=="",u=e.ranges?.[a]??J[a],p=k[a],h=i?[B(E[a],f)]:[...p===void 0?[]:[p],...Array.from({length:u.end-u.start+1},(g,_)=>u.start+_)],d;for(let g of h)if(!c.has(g)){if(await t(g)){d=g;break}if(i)throw new Error(`${E[a]}=${g} is already in use. Choose a free host port and retry.`)}if(d===void 0)throw new Error(`No available host port in ${u.start}-${u.end} for ${a}. Set ${E[a]} to a free port and retry.`);c.add(d),n[a]=d,r[a]=i,o[a]=i?"explicit override":d===p?"requested port is free":`selected from fallback range ${u.start}-${u.end}`}return{...n,requested:{...H},reason:o}}function ue(e){return`Host ports: HTTP ${e.ingressHttp}, HTTPS ${e.ingressHttps}, Postgres ${e.postgres}`}var R=S(".tdk",".tdk-out","host-ports.json");function Q(e,s,t){return e.split(`
|
|
3
3
|
`).some(r=>{let[o="",n=""]=r.split("|"),c=o.toLowerCase(),a=s.toLowerCase();return(c.startsWith(`${a}_`)||c.startsWith(`${a}-`))&&n.includes(`:${t}->`)})}function F(e){let s=S(e,R);if(!G(s))return null;try{let t=JSON.parse(D(s,"utf-8"));if(!t||typeof t!="object")return null;let r=t;return!Number.isInteger(r.ingressHttp)||!Number.isInteger(r.ingressHttps)||!Number.isInteger(r.postgres)||!r.requested||!r.explicit||!r.reason?null:r}catch{return null}}function Se(e,s){let t=S(e,R);W(S(e,".tdk",".tdk-out"),{recursive:!0}),q(t,`${JSON.stringify(s,null,2)}
|
|
4
4
|
`,{encoding:"utf-8",mode:384})}function _e(e){process.env.TDK_HTTP_PORT=String(e.ingressHttp),process.env.TDK_HTTPS_PORT=String(e.ingressHttps),process.env.TDK_POSTGRES_PORT=String(e.postgres)}var z="com.docker.compose.project.working_dir";function Z(e,s,t){let r=new Set([V(s)]);try{r.add(Y(s))}catch{}return e.split(`
|
|
5
5
|
`).some(o=>{let[,n="",c=""]=o.split("|");return!c||!n.includes(`:${t}->`)?!1:[...r].some(a=>c===a||c.startsWith(`${a}${X}`))})}function Te(e,s,t=()=>x("docker",["ps","--format",`{{.Names}}|{{.Ports}}|{{.Label "${z}"}}`],{encoding:"utf-8",timeout:5e3})){let r=F(e);if(!r||r.ingressHttp===s)return s;try{if(Z(t(),e,r.ingressHttp))return r.ingressHttp}catch{}return s}async function Ee(e,s={}){let t=F(e),r=["TDK_HTTP_PORT","TDK_HTTPS_PORT","TDK_POSTGRES_PORT"].some(i=>process.env[i]);if(s.inspectDocker===!1&&t&&!r)return t;let o="";try{let i=JSON.parse(D(S(e,".tdk",T),"utf-8"));o=String(i?.project?.name??"tdk-project").replace(/-/g,"_").toLowerCase()}catch{o="tdk_project"}let n="";if(s.inspectDocker!==!1)try{n=x("docker",["ps","--format","{{.Names}}|{{.Ports}}"],{encoding:"utf-8",timeout:5e3})}catch{}let c=i=>Q(n,o,i),a=s.isPortFree??b,f=async i=>await a(i)||c(i);if(t&&!r){for(let i of[t.ingressHttp,t.ingressHttps,t.postgres])if(!await f(i))return O({isAvailable:f});for(let[i,u]of Object.entries(k)){let p=t[i];if(u!==void 0&&p!==u&&await f(u))return O({isAvailable:f})}return t}return O({isAvailable:f})}import{existsSync as ee,readFileSync as v}from"node:fs";import{join as l}from"node:path";var $=["postgres",P],L=["pre_alpha","alpha","beta","out_of_scope"];function te(e){return $.includes(e)}function C(e){try{return JSON.parse(v(l(e,".tdk",T),"utf-8"))}catch{return}}function m(e){try{return ee(e)?v(e,"utf-8"):void 0}catch{return}}function I(e,s){let t=o=>{let n=o;for(;n<e.length&&/\s/.test(e[n]);)n+=1;return n},r=0;for(;;){let o=e.indexOf(s,r);if(o===-1)return;let n=t(o+s.length);if(e[n]==="="&&(n=t(n+1),e[n]==="[")){let c=e.indexOf("]",n+1);return c===-1?void 0:[...e.slice(n+1,c).matchAll(/"([^"]+)"|'([^']+)'/g)].map(a=>a[1]??a[2]??"")}r=o+s.length}}function M(e){let s=l(e,".tdk",".tdk-out","Tiltfile"),t=m(s);if(t)return I(t,"ALWAYS_ENABLED_INFRA")}function se(e){let s=l(e,".tdk",".tdk-out","spec.master"),t=m(s);if(t!==void 0)return/"database-management"\s*:\s*True/.test(t)}function re(e){let s=l(e,".tdk",".tdk-out"),t=m(l(s,"Tiltfile")),r=m(l(s,"spec.master")),o=m(l(s,"tdk-cli-ext","discovery","config.star")),n=m(l(s,"tdk-cli-ext","engine","topologies","tilt","config","profiles.star"));if(!t||!r||!o||!n)return!1;let c=r.match(/PRE_ALPHA_RESOURCES\s*=\s*\{([^}]*)\}/)?.[1],a=I(o,"CORE_INFRA");return/FOCUS_MODE\s*,\s*FOCUS_ENABLED_ALL\s*,\s*FOCUS_ENABLED_RESOURCES\s*=\s*Config\.apply_focus\(cfg\)/.test(t)&&/if FOCUS_MODE and FOCUS_ENABLED_ALL\s*:/.test(t)&&/"[^"]+"\s*:\s*True|'[^']+'\s*:\s*True/.test(c??"")&&(a?.includes("postgres")??!1)&&/["']postgres["']\s*:\s*["']database-management["']/.test(o)&&/for infra in CORE_INFRA_EXPORT\s*:/.test(n)&&/needed\[INFRA_STACK_MAP_EXPORT\[infra\]\]\s*=\s*True/.test(n)}function ne(e){let s=C(e);if(!s)return!1;let t=s.phases??{};for(let o of L){let n=t[o]?.enabledStacks;if(Array.isArray(n)&&n.includes(P))return!0}let r=s.always_enabled_infra??A;return Array.isArray(r)&&r.includes(P)}function oe(e,s){let t=new Set;for(let n of $)t.add(n);for(let n of s)t.add(n.name),n.stack&&t.add(n.stack);let r=C(e);if(r){let n=r.phases??{};for(let a of L){let f=n[a]?.enabledStacks;if(Array.isArray(f))for(let i of f)t.add(i)}let c=r.always_enabled_infra??A;if(Array.isArray(c))for(let a of c)t.add(a)}let o=M(e);if(Array.isArray(o))for(let n of o)t.add(n);return t}function be(e,s=w(e)){let t=ne(e),r=M(e),o=Array.isArray(r)&&r.includes(P),n=se(e)===!0,c=t||o||n,a=!t&&re(e),f=oe(e,s),i=[],u=[];for(let d of s){let g=d.config?.dependsOn??[],_=!1;for(let y of g){if(te(y)){_=!0;continue}f.has(y)||u.push({resource:d.name,name:y})}_&&i.push(d.name)}let p=c||i.length>0,h=c?"feature":i.length>0?"dependsOn":"none";return{featureOn:c,featureOnFromProjectJson:t,featureOnFromTiltfile:o,featureOnFromSpecMaster:n,focusWouldEnableDatabaseManagement:a,dependsOnUsers:i,willStart:p,reason:h,unknownDependsOnNames:u}}export{O as a,ue as b,F as c,Se as d,_e as e,Te as f,Ee as g,$ as h,be as i};
|
|
@@ -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};
|