@flisk-sh/flisk 1.3.189 → 1.3.191
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 +9 -9
- package/LICENSE +1 -1
- package/README.md +41 -41
- package/Tiltfile +2 -2
- package/cli/bin/{flisk.js → tdk.js} +1 -1
- package/cli/dist/cli.js +61 -0
- package/cli/dist/commands/__tests__/project-e2e-helpers.js +2 -2
- package/cli/dist/commands/completion.js +26 -26
- package/cli/dist/commands/config.js +1 -1
- package/cli/dist/commands/import.js +4 -4
- package/cli/dist/commands/maintainers.js +1 -1
- package/cli/dist/commands/mcp.js +1 -4
- package/cli/dist/commands/project.js +1 -1
- package/cli/dist/commands/resource.js +1 -1
- package/cli/dist/commands/ui.js +1 -1
- package/cli/dist/commands/up.js +77 -3
- package/cli/dist/commands/upgrade.js +39 -25
- package/cli/dist/generator/extension-fetch.js +3 -3
- package/cli/dist/generator/template-engine.js +8 -8
- package/cli/dist/mcp/tools.js +2 -5
- package/cli/dist/program.js +2 -0
- package/cli/dist/utils/cli-command.js +107 -0
- package/cli/dist/utils/cold-preflight.js +2 -1
- package/cli/dist/utils/constants.js +1 -1
- package/cli/dist/utils/doctor-fix.js +4 -5
- package/cli/dist/utils/doctor-runtime.js +1 -1
- package/cli/dist/utils/doctor-starlark.js +1 -1
- package/cli/dist/utils/doctor-wiring.js +2 -2
- package/cli/dist/utils/env-selection.js +201 -0
- package/cli/dist/utils/env-validator.js +6 -0
- package/cli/dist/utils/one-command.js +41 -0
- package/cli/dist/utils/paths.js +1 -1
- package/cli/dist/utils/platform.js +2 -6
- package/cli/dist/utils/project-templates.js +11 -7
- package/cli/dist/utils/secret-redaction.js +15 -6
- package/cli/dist/utils/service-manifest.js +1 -1
- package/cli/dist/utils/smoke.js +2 -2
- package/cli/dist/utils/tar.js +1 -1
- package/cli/dist-bundle/{chunk-BZ4H7MM4.js → chunk-42IVF3YT.js} +2 -2
- package/cli/dist-bundle/chunk-GOEBYLFN.js +2 -0
- package/cli/dist-bundle/chunk-KGKKDD4K.js +2 -0
- package/cli/dist-bundle/chunk-LCSYOVPP.js +438 -0
- package/cli/dist-bundle/chunk-R3OOKBPG.js +34 -0
- package/cli/dist-bundle/chunk-V6K6MWSE.js +75 -0
- package/cli/dist-bundle/cli.js +344 -372
- package/cli/dist-bundle/cold-preflight-WDQRF5G6.js +2 -0
- package/cli/dist-bundle/{extension-fetch-J4FFH7TJ.js → extension-fetch-6XNTDF44.js} +1 -1
- package/cli/dist-bundle/one-command-BSYKMVAQ.js +4 -0
- package/cli/dist-bundle/project-WPYN4RTL.js +2 -0
- package/cli/dist-bundle/project-templates-BHURA3LT.js +2 -0
- package/cli/dist-bundle/url-actions-D6ROTHZG.js +2 -0
- package/cli/package.json +2 -4
- package/cli/shared-platform-engineering/docker-templates/bun-hoisted-symlink-fix.sh +1 -1
- package/cli/shared-platform-engineering/docker-templates/infisical-entrypoint.sh +1 -1
- package/cli/shared-platform-engineering/docker-templates/install-deps.sh +1 -1
- package/cli/shared-platform-engineering/docker-templates/prisma-bun-client-link-fix.sh +1 -1
- package/cli/shared-platform-engineering/docker-templates/prisma-normalize-client.sh +1 -1
- package/cli/templates/Tiltfile.hbs +1 -1
- package/discovery/AGENTS.md +1 -1
- package/discovery/TILT_DISCOVERY.star +3 -3
- package/discovery/Tiltfile +3 -3
- package/discovery/config.star +1 -1
- package/discovery/constants.star +1 -1
- package/discovery/discovery_orchestrator.star +1 -1
- package/discovery/identity-management-frontend/service.yaml +1 -1
- package/discovery/json_manifest_scanner.star +1 -1
- package/discovery/manifest/constants.star +1 -1
- package/discovery/manifest/loading.star +2 -2
- package/discovery/manifest/normalize.star +1 -1
- package/discovery/registry.star +1 -1
- package/discovery/resource_snapshot.py +1 -1
- package/discovery/resource_snapshot.star +1 -1
- package/discovery/test_resource_snapshot.py +2 -2
- package/engine/AGENTS.md +7 -7
- package/engine/README.md +1 -1
- package/engine/Tiltfile +3 -3
- package/engine/assets/docker/wake-gateway/decide.ts +1 -1
- package/engine/assets/docker/wake-gateway/server.ts +1 -1
- package/engine/assets/scripts/mac-m1-resource-monitor.sh +8 -8
- package/engine/lifecycle/orchestrator.star +6 -6
- package/engine/paths.star +1 -1
- package/engine/schemas/project-schema.json +2 -2
- package/engine/schemas/service-schema.json +3 -3
- package/engine/scripts/debug-container.sh +2 -2
- package/engine/spec.master +1 -1
- package/engine/topologies/platform/docker/compose/compose.star +1 -1
- package/engine/topologies/platform/docker/compose/traefik_standalone.star +1 -1
- package/engine/topologies/platform/docker/config/healthcheck.star +1 -1
- package/engine/topologies/platform/docker/constants.star +2 -2
- package/engine/topologies/platform/docker/dockerfile/language_dockerfile.star +3 -3
- package/engine/topologies/platform/docker/layers/l1_base_layers.star +1 -1
- package/engine/topologies/platform/docker/layers/l2_dependency_layers.star +1 -1
- package/engine/topologies/platform/docker/layers/l3_builder_layers.star +1 -1
- package/engine/topologies/platform/docker/layers/l4_runtime_layers.star +1 -1
- package/engine/topologies/platform/docker/layers/prisma/constants.star +1 -1
- package/engine/topologies/platform/docker/layers/prisma/prisma_build.star +2 -2
- package/engine/topologies/platform/docker/networking/api_path_constants.star +1 -1
- package/engine/topologies/platform/docker/networking/traefik_static_routes.star +1 -1
- package/engine/topologies/platform/docker/secrets/env_secrets.star +1 -1
- package/engine/topologies/platform/docker/secrets/infisical.star +1 -1
- package/engine/topologies/platform/security/secrets.star +1 -1
- package/engine/topologies/tilt/common/utils.star +5 -3
- package/engine/topologies/tilt/common/utils_debug.star +1 -1
- package/engine/topologies/tilt/common/utils_deterministic.star +1 -1
- package/engine/topologies/tilt/common/utils_env.star +23 -3
- package/engine/topologies/tilt/config/profiles.star +1 -1
- package/engine/topologies/tilt/generators/infisical/organization.star +1 -1
- package/engine/topologies/tilt/generators/npmrc.star +4 -4
- package/engine/topologies/tilt/generators/typescript/backend_tsconfig.star +1 -1
- package/engine/topologies/tilt/generators/vite/helpers.star +1 -1
- package/engine/topologies/tilt/generators/vite/templates.star +1 -1
- package/engine/topologies/tilt/manifest/json_to_yaml.py +2 -2
- package/engine/topologies/tilt/manifest/loader.star +1 -1
- package/engine/topologies/tilt/manifest/schema.star +2 -2
- package/engine/topologies/tilt/manifest/tests/test_loader_parser.star +3 -3
- package/engine/topologies/tilt/manifest/tests/test_validator.star +2 -2
- package/engine/topologies/tilt/manifest/yaml_to_json.py +1 -1
- package/engine/topologies/tilt/resources/databases.star +1 -1
- package/engine/topologies/tilt/resources/infra-loader.star +8 -6
- package/engine/topologies/tilt/resources/libs_paths.star +1 -1
- package/engine/topologies/tilt/resources/libs_resource_definition.star +1 -1
- package/engine/topologies/tilt/resources/libs_setup.star +1 -1
- package/engine/topologies/tilt/resources/orchestrator/apply_compose_resource_registration.star +13 -8
- package/engine/topologies/tilt/resources/orchestrator/generators/frontend.star +1 -1
- package/engine/topologies/tilt/resources/orchestrator/generators/manifest_resource.star +1 -1
- package/engine/topologies/tilt/resources/watchers.star +7 -7
- package/engine/where-things-live.star +1 -1
- package/examples/tdk-example/LICENSE +1 -1
- package/examples/tdk-example/Tiltfile +7 -7
- package/examples/tdk-example/services/shop/orders-api/service.json +1 -1
- package/examples/tdk-example/services/shop/orders-app/index.html +1 -1
- package/examples/tdk-example/services/shop/orders-app/service.json +1 -1
- package/examples/tdk-example/services/shop/orders-worker/service.json +1 -1
- package/ext/Tiltfile +3 -3
- package/ext/ide-components/code_viewer/server.py +1 -1
- package/ext/ide-components/config_inspector/server.py +1 -1
- package/ext/ide-components/file_browser/server.py +1 -1
- package/ext/ide-components/shared/config_service.py +2 -2
- package/ext/ide-components/shared/file_utils.py +2 -2
- package/ext/ide-components/shared/http_utils.py +1 -1
- package/ext/ide-components/shared/syntax_highlighter.py +1 -1
- package/ext/ide-components/shared/template_engine.py +1 -1
- package/ext/ide-components/shared/templates/base.html +1 -1
- package/ext/ide-components/shared/test_config_service.py +1 -1
- package/ext/ide-components/shared/tilt_integration.py +1 -1
- package/ext/ui-enhancements/tiltfile.py +1 -1
- package/package.json +6 -10
- package/shared-platform-engineering/docker-templates/bun-hoisted-symlink-fix.sh +1 -1
- package/shared-platform-engineering/docker-templates/infisical-entrypoint.sh +1 -1
- package/shared-platform-engineering/docker-templates/install-deps.sh +1 -1
- package/shared-platform-engineering/docker-templates/prisma-bun-client-link-fix.sh +1 -1
- package/shared-platform-engineering/docker-templates/prisma-normalize-client.sh +1 -1
- package/cli/dist-bundle/chunk-QWYXYAX6.js +0 -510
- package/cli/dist-bundle/cold-preflight-MM46GFQA.js +0 -2
|
@@ -23,12 +23,12 @@ export function loadTemplate(filename) {
|
|
|
23
23
|
path.join(__dirname, "..", "..", "templates"), // Standard: dist/generator -> templates (also covers src/generator -> ../../templates in dev/test)
|
|
24
24
|
path.join(__dirname, "..", "templates"), // Installed: dist/generator -> dist/templates
|
|
25
25
|
path.join(__dirname, "..", "..", "..", "templates"), // Development: cli/dist/generator -> cli/templates
|
|
26
|
-
path.join(process.cwd(), "node_modules", "flisk", "templates"), // Monorepo root
|
|
26
|
+
path.join(process.cwd(), "node_modules", "@flisk-sh", "flisk", "templates"), // Monorepo root
|
|
27
27
|
// Compiled single-file binary (bun build --compile): __dirname resolves inside the
|
|
28
28
|
// virtual $bunfs filesystem, not a real path, so fall back to the directory the actual
|
|
29
|
-
// executable lives in. release-binaries.sh bundles cli/templates at
|
|
29
|
+
// executable lives in. release-binaries.sh bundles cli/templates at tdk-cli/cli/templates
|
|
30
30
|
// next to the platform binaries.
|
|
31
|
-
path.join(path.dirname(process.execPath), "
|
|
31
|
+
path.join(path.dirname(process.execPath), "tdk-cli", "cli", "templates"),
|
|
32
32
|
];
|
|
33
33
|
const triedPaths = [];
|
|
34
34
|
for (const templateDir of templateDirs) {
|
|
@@ -587,8 +587,8 @@ export function findCliAssetRoot() {
|
|
|
587
587
|
}
|
|
588
588
|
const exeDir = path.dirname(process.execPath);
|
|
589
589
|
const candidates = [
|
|
590
|
-
path.join(exeDir, "
|
|
591
|
-
path.join(process.cwd(), "node_modules", "flisk"),
|
|
590
|
+
path.join(exeDir, "tdk-cli"),
|
|
591
|
+
path.join(process.cwd(), "node_modules", "@flisk-sh", "flisk"),
|
|
592
592
|
];
|
|
593
593
|
return (candidates.find((candidate) => fs.existsSync(path.join(candidate, "shared-platform-engineering", "docker-templates"))) ?? null);
|
|
594
594
|
}
|
|
@@ -624,7 +624,7 @@ async function vendorTdkExtension(projectRoot) {
|
|
|
624
624
|
// 1. $TDK_EXTENSION_SOURCE (explicit override)
|
|
625
625
|
// 2. Self-contained repo (engine/ ships next to cli/ - public flisk)
|
|
626
626
|
// 3. Executable-adjacent checkout (bundled installs)
|
|
627
|
-
// 4. Relative ../
|
|
627
|
+
// 4. Relative ../tdk-cli sibling (monorepo maintainer layout)
|
|
628
628
|
const sources = [];
|
|
629
629
|
if (process.env.TDK_EXTENSION_SOURCE) {
|
|
630
630
|
sources.push(process.env.TDK_EXTENSION_SOURCE);
|
|
@@ -641,8 +641,8 @@ async function vendorTdkExtension(projectRoot) {
|
|
|
641
641
|
// Bun's process object - it was always undefined, silently falling
|
|
642
642
|
// back to cwd() instead of the binary's real location.
|
|
643
643
|
const exeDir = path.dirname(process.execPath);
|
|
644
|
-
sources.push(path.join(exeDir, "
|
|
645
|
-
sources.push(path.join(projectRoot, "..", "
|
|
644
|
+
sources.push(path.join(exeDir, "tdk-cli"));
|
|
645
|
+
sources.push(path.join(projectRoot, "..", "tdk-cli"));
|
|
646
646
|
let sourceRoot = "";
|
|
647
647
|
for (const candidate of sources) {
|
|
648
648
|
if (candidate &&
|
package/cli/dist/mcp/tools.js
CHANGED
|
@@ -79,10 +79,7 @@ export function toToolResult(run) {
|
|
|
79
79
|
return {
|
|
80
80
|
data: {
|
|
81
81
|
errors: [
|
|
82
|
-
{
|
|
83
|
-
code: "NO_JSON",
|
|
84
|
-
message: run.stderr.trim() || `flisk exited with code ${run.exitCode}`,
|
|
85
|
-
},
|
|
82
|
+
{ code: "NO_JSON", message: run.stderr.trim() || `flisk exited with code ${run.exitCode}` },
|
|
86
83
|
],
|
|
87
84
|
},
|
|
88
85
|
isError: true,
|
|
@@ -123,7 +120,7 @@ export const defaultUpDeps = {
|
|
|
123
120
|
},
|
|
124
121
|
sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
125
122
|
now: () => Date.now(),
|
|
126
|
-
logFile: () => join(tmpdir(), `
|
|
123
|
+
logFile: () => join(tmpdir(), `tdk-mcp-up-${Date.now()}.log`),
|
|
127
124
|
};
|
|
128
125
|
export function buildUpArgs(args) {
|
|
129
126
|
const stack = optionalName(args.stack, "stack");
|
package/cli/dist/program.js
CHANGED
|
@@ -20,6 +20,8 @@ export function createProgram() {
|
|
|
20
20
|
.description("Flisk CLI — start services on your machine.")
|
|
21
21
|
.version(pkg.version, "-v, --version", "Display version number")
|
|
22
22
|
.option("--verbose", "Enable verbose output", false)
|
|
23
|
+
.option("--template <name>", "Create a project from a starter template in one command (restaurant, saas, erp, ecommerce, example)")
|
|
24
|
+
.option("--project <dir>", "Directory to create the project in (default: the template name)")
|
|
23
25
|
.configureOutput({
|
|
24
26
|
outputError: (str, write) => write(chalk.red(str)),
|
|
25
27
|
writeOut: (str) => process.stdout.write(str),
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// Copyright (c) 2026 Flisk Landscape contributors
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
import { execFileSync } from "node:child_process";
|
|
4
|
+
import { accessSync, constants, readdirSync } from "node:fs";
|
|
5
|
+
import { delimiter, join } from "node:path";
|
|
6
|
+
/** The command users install and type. */
|
|
7
|
+
export const CLI_COMMAND = "flisk";
|
|
8
|
+
/** The npm package that installs it. */
|
|
9
|
+
export const CLI_PACKAGE = "@flisk-sh/flisk";
|
|
10
|
+
/** The package the CLI was published under before the rename. It owns the old `flisk` command. */
|
|
11
|
+
export const LEGACY_PACKAGE = "@tdk-landscape/tdk-cli-core";
|
|
12
|
+
/** True when the command can be typed as-is, so `flisk ...` resolves. */
|
|
13
|
+
export function isCommandOnPath(command = CLI_COMMAND, env = process.env) {
|
|
14
|
+
const path = env.PATH ?? env.Path ?? "";
|
|
15
|
+
for (const dir of path.split(delimiter)) {
|
|
16
|
+
if (!dir)
|
|
17
|
+
continue;
|
|
18
|
+
const candidates = [
|
|
19
|
+
join(dir, command),
|
|
20
|
+
join(dir, `${command}.cmd`),
|
|
21
|
+
join(dir, `${command}.exe`),
|
|
22
|
+
];
|
|
23
|
+
for (const candidate of candidates) {
|
|
24
|
+
try {
|
|
25
|
+
accessSync(candidate, constants.X_OK);
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// Not on this PATH entry; keep looking.
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* A folder to start a project in. It follows the platform convention: Documents when the
|
|
37
|
+
* machine has one, a plain folder in the home directory otherwise.
|
|
38
|
+
*/
|
|
39
|
+
export function suggestedWorkspace(env = process.env) {
|
|
40
|
+
const home = env.HOME ?? env.USERPROFILE ?? "";
|
|
41
|
+
if (!home)
|
|
42
|
+
return "flisk-workspace";
|
|
43
|
+
const documents = join(home, "Documents");
|
|
44
|
+
try {
|
|
45
|
+
accessSync(documents, constants.F_OK);
|
|
46
|
+
return join(documents, "flisk");
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return join(home, "flisk");
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/** True when the directory holds no visible entries, so a project can start there as-is. */
|
|
53
|
+
export function isEmptyWorkspace(dir) {
|
|
54
|
+
try {
|
|
55
|
+
return readdirSync(dir).every((entry) => entry.startsWith("."));
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The next step to print once the machine is ready.
|
|
63
|
+
*
|
|
64
|
+
* An ephemeral run (`npx @flisk-sh/flisk`) never installs the command, so pointing at
|
|
65
|
+
* `flisk ...` there would fail with "command not found"; that case says how to install it
|
|
66
|
+
* instead. A project also should not start in whatever folder the shell happens to be in,
|
|
67
|
+
* so a directory that already holds files gets a workspace to move into first.
|
|
68
|
+
*/
|
|
69
|
+
export function readyNextStep(env = process.env, cwd = process.cwd()) {
|
|
70
|
+
if (!isCommandOnPath(CLI_COMMAND, env)) {
|
|
71
|
+
return (`The \`${CLI_COMMAND}\` command is not on PATH (this run is not a global install). ` +
|
|
72
|
+
`Install it with \`npm install -g ${CLI_PACKAGE}\`, then run \`${CLI_COMMAND} project --yes\`. ` +
|
|
73
|
+
`If npm reports EEXIST for \`flisk\`, the old package still owns that command: ` +
|
|
74
|
+
`run \`npm uninstall -g ${LEGACY_PACKAGE}\` first.`);
|
|
75
|
+
}
|
|
76
|
+
if (isEmptyWorkspace(cwd)) {
|
|
77
|
+
return `Next: ${CLI_COMMAND} project --yes`;
|
|
78
|
+
}
|
|
79
|
+
const workspace = suggestedWorkspace(env);
|
|
80
|
+
return (`Next: mkdir -p ${workspace} && cd ${workspace} && ${CLI_COMMAND} project --yes\n` +
|
|
81
|
+
`(${CLI_COMMAND} project writes .tdk/ and the master configs into the current folder, ` +
|
|
82
|
+
`so it starts clean in a folder of its own. Or clone a starter: ${CLI_COMMAND} project example)`);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Installs the CLI globally when it is only running ephemerally (`npx @flisk-sh/flisk`),
|
|
86
|
+
* so the `flisk` command keeps working after the run. The old package is removed first:
|
|
87
|
+
* it owns the `flisk` command and would make the install fail with EEXIST.
|
|
88
|
+
*/
|
|
89
|
+
export function ensureGlobalInstall(env = process.env) {
|
|
90
|
+
if (isCommandOnPath(CLI_COMMAND, env))
|
|
91
|
+
return "present";
|
|
92
|
+
const manager = env.npm_execpath?.includes("bun") ? "bun" : "npm";
|
|
93
|
+
const remove = manager === "bun" ? "bun remove -g" : "npm uninstall -g";
|
|
94
|
+
try {
|
|
95
|
+
execFileSync(remove, [LEGACY_PACKAGE], { stdio: "ignore", timeout: 120000 });
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
// Not installed under the old name, or the manager is missing.
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
execFileSync(manager, ["install", "-g", CLI_PACKAGE], { stdio: "inherit", timeout: 300000 });
|
|
102
|
+
return "installed";
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return "failed";
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -6,6 +6,7 @@ import { join } from "node:path";
|
|
|
6
6
|
import { checkDockerCompose, checkDockerRuntime, checkDockerVersions, checkTilt, DOCTOR_FIXES, } from "../commands/doctor.js";
|
|
7
7
|
import { BUN_FLOOR_LABEL, bunMeetsFloor } from "./bun-floor.js";
|
|
8
8
|
import { projectNeedsBun } from "./bun-requirement.js";
|
|
9
|
+
import { readyNextStep } from "./cli-command.js";
|
|
9
10
|
import { PROJECT_JSON } from "./constants.js";
|
|
10
11
|
import { checkNatsBroker } from "./doctor-wiring.js";
|
|
11
12
|
import { getHostPortPlan } from "./host-port-config.js";
|
|
@@ -200,7 +201,7 @@ export async function runColdPreflight(opts = {}) {
|
|
|
200
201
|
? ""
|
|
201
202
|
: inProject
|
|
202
203
|
? "Environment ready for Flisk"
|
|
203
|
-
:
|
|
204
|
+
: `This machine is ready for Flisk.\n${readyNextStep()}`;
|
|
204
205
|
return { ok: failures.length === 0, inProject, items: failures, header, footer };
|
|
205
206
|
}
|
|
206
207
|
/** Formats only failed preflight checks and the applicable readiness message. */
|
|
@@ -89,7 +89,7 @@ export const STANDARD_PORTS = {
|
|
|
89
89
|
tiltUi: 10350,
|
|
90
90
|
traefik: 8080,
|
|
91
91
|
};
|
|
92
|
-
export const QUICKSTART_DOCS_URL = "https://
|
|
92
|
+
export const QUICKSTART_DOCS_URL = "https://flisk.sh/docs/quickstart/";
|
|
93
93
|
export const BASE_DOMAIN_DEFAULT = "tdk-project.localhost";
|
|
94
94
|
export const APP_SUBDOMAIN_PREFIX = "app";
|
|
95
95
|
export const API_SUBDOMAIN_PREFIX = "api";
|
|
@@ -7,13 +7,12 @@ import { join } from "node:path";
|
|
|
7
7
|
import { promisify } from "node:util";
|
|
8
8
|
import { DISK_WARN_GIB } from "./doctor-host.js";
|
|
9
9
|
import { findProjectTiltUpPids, projectNetworkNames, tdkNetworkOwner, tdkNetworkPrefixes, } from "./down-cleanup.js";
|
|
10
|
-
import { executableName, legacyExecutableName } from "./platform.js";
|
|
11
10
|
import { tiltGetUiResources } from "./up-readiness.js";
|
|
12
11
|
import { findAllOnPath } from "./which.js";
|
|
13
12
|
const execFilePromise = promisify(execFile);
|
|
14
13
|
const DOCKER_TIMEOUT_MS = 15_000;
|
|
15
14
|
/** The networks `flisk doctor` creates to test capacity; an interrupted run leaves one behind. */
|
|
16
|
-
const PROBE_NETWORK = /^
|
|
15
|
+
const PROBE_NETWORK = /^tdk_doctor_probe_(\d+)$/;
|
|
17
16
|
const runDocker = async (args) => {
|
|
18
17
|
const { stdout } = await execFilePromise("docker", args, {
|
|
19
18
|
encoding: "utf-8",
|
|
@@ -335,8 +334,8 @@ async function readTdkVersion(path) {
|
|
|
335
334
|
return null;
|
|
336
335
|
}
|
|
337
336
|
}
|
|
338
|
-
/** Every distinct
|
|
339
|
-
export async function findTdkInstalls(paths =
|
|
337
|
+
/** Every distinct `flisk` on PATH (symlinks to one binary count once), in PATH order. */
|
|
338
|
+
export async function findTdkInstalls(paths = findAllOnPath("flisk"), readVersion = readTdkVersion) {
|
|
340
339
|
const seen = new Set();
|
|
341
340
|
const installs = [];
|
|
342
341
|
for (const path of paths) {
|
|
@@ -367,7 +366,7 @@ export async function checkDuplicateTdkInstalls(installs) {
|
|
|
367
366
|
didPass: false,
|
|
368
367
|
isWarning: true,
|
|
369
368
|
message: `${found.length} flisk installs on PATH${versions.size > 1 ? " with different versions" : ""}: ${list}`,
|
|
370
|
-
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/tdk directly may use another",
|
|
371
370
|
};
|
|
372
371
|
}
|
|
373
372
|
/**
|
|
@@ -311,7 +311,7 @@ export function checkPrivateNpmRegistry(exec = execSync, projectRoot = findProje
|
|
|
311
311
|
return {
|
|
312
312
|
name: "Private npm registry",
|
|
313
313
|
didPass: false,
|
|
314
|
-
message: `Private npm registry (Verdaccio) is not reachable at ${registryUrl}. Docker ImageBuilds will fail with ConnectionRefused downloading package manifests for @
|
|
314
|
+
message: `Private npm registry (Verdaccio) is not reachable at ${registryUrl}. Docker ImageBuilds will fail with ConnectionRefused downloading package manifests for @flisk-sh/* and project-scoped packages.`,
|
|
315
315
|
fix: "Start the registry with `bun run verdaccio:start` (or `docker compose -f docker-compose.verdaccio.yml up -d`), publish packages with `bun run publish:all` / `scripts/build/publish-to-verdaccio.sh`, then `tilt trigger` failed resources or re-run `flisk up`.",
|
|
316
316
|
};
|
|
317
317
|
}
|
|
@@ -22,7 +22,7 @@ export function findPrivateStarlarkLoadExports(content) {
|
|
|
22
22
|
return Array.from(privateExports).sort();
|
|
23
23
|
}
|
|
24
24
|
export function resolveStarlarkLoadTarget(file, modulePath, projectRoot) {
|
|
25
|
-
if (modulePath === "ext://
|
|
25
|
+
if (modulePath === "ext://tdk-cli") {
|
|
26
26
|
return join(projectRoot, ".tdk", ".tdk-out", "tdk-cli-ext", "Tiltfile");
|
|
27
27
|
}
|
|
28
28
|
if (modulePath.startsWith(".") || (!modulePath.includes("://") && !modulePath.startsWith("@"))) {
|
|
@@ -667,7 +667,7 @@ export function checkTiltInstances(projectRoot = findProjectRoot() ?? process.cw
|
|
|
667
667
|
return { name: "Tilt processes", didPass: true, message: "No stray Tilt processes" };
|
|
668
668
|
}
|
|
669
669
|
export async function checkDockerNetworkCapacity(exec = execAsync) {
|
|
670
|
-
const probe = `
|
|
670
|
+
const probe = `tdk_doctor_probe_${process.pid}`;
|
|
671
671
|
try {
|
|
672
672
|
await exec(`docker network create ${probe}`, EXEC_TIMEOUT_MS);
|
|
673
673
|
}
|
|
@@ -692,7 +692,7 @@ export async function checkDockerNetworkCapacity(exec = execAsync) {
|
|
|
692
692
|
await exec(`docker network rm ${probe}`, EXEC_TIMEOUT_MS);
|
|
693
693
|
}
|
|
694
694
|
catch {
|
|
695
|
-
// A leftover probe network is harmless and
|
|
695
|
+
// A leftover probe network is harmless and tdk_doctor_probe_* is easy to prune.
|
|
696
696
|
}
|
|
697
697
|
return { name: "Docker networks", didPass: true, message: "Docker can create networks" };
|
|
698
698
|
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
// Copyright (c) 2026 Flisk Landscape contributors
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
import { existsSync, readFileSync, realpathSync, statSync } from "node:fs";
|
|
4
|
+
import { isAbsolute, relative, resolve, sep } from "node:path";
|
|
5
|
+
import { COMPLETED_ENV_VARS, parseEnv } from "./env-validator.js";
|
|
6
|
+
import { TdkError } from "./errors.js";
|
|
7
|
+
/**
|
|
8
|
+
* The environment variable the engine reads the env file of the run from. The CLI resolves the
|
|
9
|
+
* requested path and exports it, so the engine reads one file for the whole run: the Tilt
|
|
10
|
+
* environment (engine/topologies/tilt/common/utils_env.star) and the Compose interpolation alike.
|
|
11
|
+
*/
|
|
12
|
+
export const PROJECT_ENV_FILE_VAR = "TDK_ENV_FILE";
|
|
13
|
+
/**
|
|
14
|
+
* The keys the platform needs in the env file of a run. They are the keys `completeEnvFile` keeps
|
|
15
|
+
* complete in the project `.env`: the Tilt environment, the Postgres password and the JWT secret.
|
|
16
|
+
* A selected file replaces the project `.env` for the run, so it has to carry them too.
|
|
17
|
+
*/
|
|
18
|
+
export const REQUIRED_ENV_FILE_KEYS = COMPLETED_ENV_VARS;
|
|
19
|
+
/** Exit code for a rejected `--env-file`: invalid arguments (see docs/reference/machine-readable-cli.md). */
|
|
20
|
+
export const ENV_FILE_ERROR_EXIT_CODE = 2;
|
|
21
|
+
/** Exit code when the project's vendored engine is too old for the option: a blocking finding. */
|
|
22
|
+
export const ENV_FILE_ENGINE_EXIT_CODE = 1;
|
|
23
|
+
/**
|
|
24
|
+
* A rejected `--env-file`. It carries a code so a JSON consumer can tell an unusable file from
|
|
25
|
+
* another failure, and it is a TdkError so the CLI shows it in the house style.
|
|
26
|
+
*/
|
|
27
|
+
export class EnvFileError extends TdkError {
|
|
28
|
+
code;
|
|
29
|
+
constructor(code, message, suggestions = [], exitCode = ENV_FILE_ERROR_EXIT_CODE) {
|
|
30
|
+
super(message, suggestions, exitCode);
|
|
31
|
+
this.code = code;
|
|
32
|
+
this.name = "EnvFileError";
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/** True when the path is inside the project directory. */
|
|
36
|
+
export function isInsideProject(projectRoot, path) {
|
|
37
|
+
const relativePath = relative(resolve(projectRoot), resolve(path));
|
|
38
|
+
if (relativePath === "")
|
|
39
|
+
return true;
|
|
40
|
+
if (isAbsolute(relativePath))
|
|
41
|
+
return false;
|
|
42
|
+
return relativePath !== ".." && !relativePath.startsWith(`..${sep}`);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Resolves the requested env file to an absolute path inside the project.
|
|
46
|
+
*
|
|
47
|
+
* A relative path is resolved against the current directory, the way `docker compose --env-file`
|
|
48
|
+
* and Node.js' own `--env-file` resolve it. Resolving it the same way keeps the check Node.js does
|
|
49
|
+
* before the CLI starts in step with this one.
|
|
50
|
+
*
|
|
51
|
+
* The file has to live in the project: it is the project env file of the run, and the wake gateway
|
|
52
|
+
* (which mounts the project read-only and runs Compose itself) can only read a file inside it. A
|
|
53
|
+
* path outside the project would also turn the flag into a way to read any file on the machine.
|
|
54
|
+
*
|
|
55
|
+
* @throws TdkError when the path is empty, leaves the project, or does not exist
|
|
56
|
+
*/
|
|
57
|
+
export function resolveEnvFilePath(projectRoot, requested, options = {}) {
|
|
58
|
+
const trimmed = requested.trim();
|
|
59
|
+
if (!trimmed) {
|
|
60
|
+
throw new EnvFileError("ENV_FILE_INVALID", "No env file was given", [
|
|
61
|
+
"Pass the path of a project env file, for example: flisk up --env-file .env.integration",
|
|
62
|
+
]);
|
|
63
|
+
}
|
|
64
|
+
const baseDir = options.baseDir ?? process.cwd();
|
|
65
|
+
const absolute = isAbsolute(trimmed) ? resolve(trimmed) : resolve(baseDir, trimmed);
|
|
66
|
+
if (!existsSync(absolute)) {
|
|
67
|
+
throw new EnvFileError("ENV_FILE_INVALID", `Env file not found: ${trimmed}`, [
|
|
68
|
+
"Create it first, for example: cp .env .env.integration",
|
|
69
|
+
"A relative path is resolved against the current directory",
|
|
70
|
+
]);
|
|
71
|
+
}
|
|
72
|
+
if (!statSync(absolute).isFile()) {
|
|
73
|
+
throw new EnvFileError("ENV_FILE_INVALID", `Env file is not a file: ${trimmed}`, [
|
|
74
|
+
"Point --env-file at an env file, not at a directory",
|
|
75
|
+
]);
|
|
76
|
+
}
|
|
77
|
+
// The real paths decide whether the file belongs to the project: a project directory reached
|
|
78
|
+
// through a symbolic link (macOS /var -> /private/var) is still the project, and a symbolic link
|
|
79
|
+
// inside the project can point at a file outside it. The engine reads the file through the
|
|
80
|
+
// project mount, so a file outside the project would break the run in the wake gateway.
|
|
81
|
+
if (!isInsideProject(realPathOrSelf(projectRoot), realPathOrSelf(absolute))) {
|
|
82
|
+
throw new EnvFileError("ENV_FILE_INVALID", `Env file is outside the project: ${trimmed}`, [
|
|
83
|
+
"The env file of a run belongs to the project, next to its .env",
|
|
84
|
+
`The project directory is ${projectRoot}`,
|
|
85
|
+
]);
|
|
86
|
+
}
|
|
87
|
+
return absolute;
|
|
88
|
+
}
|
|
89
|
+
/** The real path of a file, or the path itself when it cannot be resolved. */
|
|
90
|
+
function realPathOrSelf(path) {
|
|
91
|
+
try {
|
|
92
|
+
return realpathSync(path);
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return path;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Reads the env file. The values are secrets, so they are only used to configure the run and to
|
|
100
|
+
* mask output; they are never printed.
|
|
101
|
+
*
|
|
102
|
+
* @throws TdkError when the file cannot be read
|
|
103
|
+
*/
|
|
104
|
+
export function readEnvFileValues(path) {
|
|
105
|
+
let content;
|
|
106
|
+
try {
|
|
107
|
+
content = readFileSync(path, "utf-8");
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
throw new EnvFileError("ENV_FILE_INVALID", `Could not read env file ${path}: ${error instanceof Error ? error.message : String(error)}`, ["Check the permissions of the env file"]);
|
|
111
|
+
}
|
|
112
|
+
return parseEnv(content);
|
|
113
|
+
}
|
|
114
|
+
/** The platform keys the file is missing, so the run would start without them. */
|
|
115
|
+
export function missingRequiredEnvKeys(values) {
|
|
116
|
+
return REQUIRED_ENV_FILE_KEYS.filter((key) => !(values.get(key) ?? "").trim());
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Selects the env file of the run and checks that it can carry the run.
|
|
120
|
+
*
|
|
121
|
+
* The selected file replaces the project `.env` for the run, so it has to carry the same keys. Flisk
|
|
122
|
+
* does not complete it: a file the user named may well be committed, and writing a generated
|
|
123
|
+
* credential into it would leak one.
|
|
124
|
+
*
|
|
125
|
+
* @throws TdkError when the file is unusable or misses a key the platform needs
|
|
126
|
+
*/
|
|
127
|
+
export function selectEnvFile(projectRoot, requested, options = {}) {
|
|
128
|
+
const path = resolveEnvFilePath(projectRoot, requested, options);
|
|
129
|
+
const values = readEnvFileValues(path);
|
|
130
|
+
const missing = missingRequiredEnvKeys(values);
|
|
131
|
+
if (missing.length > 0) {
|
|
132
|
+
throw new EnvFileError("ENV_FILE_INVALID", `Env file ${requested} is missing ${missing.join(", ")}`, [
|
|
133
|
+
"The selected env file replaces the project .env for this run, so it needs the same keys",
|
|
134
|
+
"Copy them from the project .env (they are per-project credentials)",
|
|
135
|
+
"Run without --env-file to use the project .env",
|
|
136
|
+
]);
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
path,
|
|
140
|
+
relativePath: relative(projectRoot, path).split(sep).join("/"),
|
|
141
|
+
values,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Points the engine at the selected env file for this run. The engine reads the same variable for
|
|
146
|
+
* the Tilt environment and for the Compose interpolation, so one file drives both.
|
|
147
|
+
*/
|
|
148
|
+
export function applyEnvFileSelection(selection) {
|
|
149
|
+
process.env[PROJECT_ENV_FILE_VAR] = selection.path;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* The vendored engine copy that resolves the env file of the run. A project vendors it at generation
|
|
153
|
+
* time, so a project created before this option existed runs an engine that ignores the selection.
|
|
154
|
+
*/
|
|
155
|
+
const VENDORED_ENGINE_ENV_FILE = ".tdk/.tdk-out/tdk-cli-ext/engine/topologies/tilt/common/utils_env.star";
|
|
156
|
+
/**
|
|
157
|
+
* Refuses the run when the project's vendored engine cannot honour the selection. Without this check
|
|
158
|
+
* the flag would be ignored silently and the run would use the project .env after all.
|
|
159
|
+
*
|
|
160
|
+
* @throws TdkError when the vendored engine is older than this option
|
|
161
|
+
*/
|
|
162
|
+
export function assertEngineSupportsEnvFile(projectRoot) {
|
|
163
|
+
const vendored = resolve(projectRoot, VENDORED_ENGINE_ENV_FILE);
|
|
164
|
+
if (!existsSync(vendored))
|
|
165
|
+
return;
|
|
166
|
+
let content;
|
|
167
|
+
try {
|
|
168
|
+
content = readFileSync(vendored, "utf-8");
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
// An unreadable engine copy fails on its own, with a clearer message than this one.
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
if (content.includes(PROJECT_ENV_FILE_VAR))
|
|
175
|
+
return;
|
|
176
|
+
throw new EnvFileError("ENV_FILE_ENGINE_TOO_OLD", "The Flisk engine in this project does not support --env-file yet", [
|
|
177
|
+
"Run `flisk config regenerate` to refresh the vendored engine, then run `flisk up --env-file ...` again",
|
|
178
|
+
`The engine copy is at ${VENDORED_ENGINE_ENV_FILE}`,
|
|
179
|
+
], ENV_FILE_ENGINE_EXIT_CODE);
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* A warning when the selected env file would break the local database. The Postgres volume keeps the
|
|
183
|
+
* password it was created with, so a different DB_PASSWORD does not change it.
|
|
184
|
+
*/
|
|
185
|
+
export function envFileWarning(projectRoot, selection) {
|
|
186
|
+
const projectEnvPath = resolve(projectRoot, ".env");
|
|
187
|
+
if (!existsSync(projectEnvPath))
|
|
188
|
+
return null;
|
|
189
|
+
let projectValues;
|
|
190
|
+
try {
|
|
191
|
+
projectValues = parseEnv(readFileSync(projectEnvPath, "utf-8"));
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
const projectPassword = (projectValues.get("DB_PASSWORD") ?? "").trim();
|
|
197
|
+
const selectedPassword = (selection.values.get("DB_PASSWORD") ?? "").trim();
|
|
198
|
+
if (!projectPassword || !selectedPassword || projectPassword === selectedPassword)
|
|
199
|
+
return null;
|
|
200
|
+
return "The selected env file sets a different DB_PASSWORD than the project .env. The local Postgres volume keeps the password it was created with, so the database may refuse the new one.";
|
|
201
|
+
}
|
|
@@ -53,6 +53,12 @@ const REQUIRED_ENV_VARS = [
|
|
|
53
53
|
];
|
|
54
54
|
const generateDbPassword = () => randomBytes(16).toString("hex");
|
|
55
55
|
const generateJwtSecret = () => randomBytes(32).toString("hex");
|
|
56
|
+
/**
|
|
57
|
+
* The keys `completeEnvFile` keeps complete in the project `.env`. An env file that replaces the
|
|
58
|
+
* project `.env` for a run (`flisk up --env-file`) carries the same keys, so it is read from here
|
|
59
|
+
* instead of from a second list.
|
|
60
|
+
*/
|
|
61
|
+
export const COMPLETED_ENV_VARS = REQUIRED_ENV_VARS.filter((v) => v.complete).map((v) => v.name);
|
|
56
62
|
function generatedValue(name, password) {
|
|
57
63
|
if (name === "DB_PASSWORD")
|
|
58
64
|
return password;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Copyright (c) 2026 Flisk Landscape contributors
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
import { spawn } from "node:child_process";
|
|
4
|
+
/** The marker the stack prints before the frontend URLs. */
|
|
5
|
+
const FRONTEND_MARKER = "Frontend URLs";
|
|
6
|
+
/** Picks the first frontend URL out of the `flisk up` output, if it printed one. */
|
|
7
|
+
export function findFrontendUrl(output) {
|
|
8
|
+
const lines = output.split("\n");
|
|
9
|
+
const markerIndex = lines.findIndex((line) => line.includes(FRONTEND_MARKER));
|
|
10
|
+
if (markerIndex === -1)
|
|
11
|
+
return undefined;
|
|
12
|
+
for (const line of lines.slice(markerIndex + 1)) {
|
|
13
|
+
const match = line.match(/^\s*-\s+\S+:\s+(https?:\/\/\S+)/);
|
|
14
|
+
if (match)
|
|
15
|
+
return match[1];
|
|
16
|
+
if (line.trim() === "" || line.includes("URLs:"))
|
|
17
|
+
break;
|
|
18
|
+
}
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Runs `flisk up` in the project directory and returns its output. The output is streamed to
|
|
23
|
+
* the terminal as it arrives, so the stack keeps its live progress.
|
|
24
|
+
*/
|
|
25
|
+
export function runStack(binPath, cwd, env = process.env, spawnFn = spawn) {
|
|
26
|
+
return new Promise((resolve) => {
|
|
27
|
+
const child = spawnFn(process.execPath, [binPath, "up"], {
|
|
28
|
+
cwd,
|
|
29
|
+
env,
|
|
30
|
+
stdio: ["inherit", "pipe", "inherit"],
|
|
31
|
+
});
|
|
32
|
+
let output = "";
|
|
33
|
+
child.stdout?.on("data", (chunk) => {
|
|
34
|
+
const text = String(chunk);
|
|
35
|
+
output += text;
|
|
36
|
+
process.stdout.write(text);
|
|
37
|
+
});
|
|
38
|
+
child.on("error", (error) => resolve({ code: 1, output: `${output}\n${error.message}` }));
|
|
39
|
+
child.on("close", (code) => resolve({ code, output }));
|
|
40
|
+
});
|
|
41
|
+
}
|
package/cli/dist/utils/paths.js
CHANGED
|
@@ -32,7 +32,7 @@ function getPackageInfo() {
|
|
|
32
32
|
}
|
|
33
33
|
const packageInfo = parsed;
|
|
34
34
|
packageCache = {
|
|
35
|
-
name: String(packageInfo.name ?? "flisk"),
|
|
35
|
+
name: String(packageInfo.name ?? "@flisk-sh/flisk"),
|
|
36
36
|
version: String(packageInfo.version ?? "0.0.0"),
|
|
37
37
|
fullPackage: packageInfo,
|
|
38
38
|
};
|
|
@@ -33,18 +33,14 @@ export function binaryAssetNameFor(platformName, architecture) {
|
|
|
33
33
|
return null;
|
|
34
34
|
if (osName === "windows" && archName !== "amd64")
|
|
35
35
|
return null;
|
|
36
|
-
const base = `
|
|
36
|
+
const base = `tdk-${osName}-${archName}`;
|
|
37
37
|
return osName === "windows" ? `${base}.exe` : base;
|
|
38
38
|
}
|
|
39
39
|
export function binaryAssetName() {
|
|
40
40
|
return binaryAssetNameFor(process.platform, process.arch);
|
|
41
41
|
}
|
|
42
42
|
export function executableName() {
|
|
43
|
-
return isWindows() ? "
|
|
44
|
-
}
|
|
45
|
-
/** Installs published before the Flisk rename shipped the binary as `tdk`. */
|
|
46
|
-
export function legacyExecutableName() {
|
|
47
|
-
return isWindows() ? "tdk.exe" : "tdk";
|
|
43
|
+
return isWindows() ? "tdk.exe" : "flisk";
|
|
48
44
|
}
|
|
49
45
|
export function pathLookups(binName, windows = isWindows()) {
|
|
50
46
|
if (!windows)
|
|
@@ -1,32 +1,36 @@
|
|
|
1
1
|
/** Public starter repositories plus the bundled default product example. */
|
|
2
2
|
export const PROJECT_TEMPLATES = {
|
|
3
|
+
demo: {
|
|
4
|
+
repo: "https://github.com/flisk-sh/tdk-restaurant-example.git",
|
|
5
|
+
description: "The demo stack: restaurant ops with reservations, kitchen and floor control",
|
|
6
|
+
},
|
|
3
7
|
restaurant: {
|
|
4
|
-
repo: "https://github.com/
|
|
8
|
+
repo: "https://github.com/flisk-sh/tdk-restaurant-example.git",
|
|
5
9
|
description: "Restaurant ops: reservations, kitchen, floor control (Hono + Vite + Bun)",
|
|
6
10
|
},
|
|
7
11
|
saas: {
|
|
8
|
-
repo: "https://github.com/
|
|
12
|
+
repo: "https://github.com/flisk-sh/tdk-saas-starter.git",
|
|
9
13
|
description: "SaaS starter: account dashboard and a working checkout button (Hono + Vite + Bun)",
|
|
10
14
|
},
|
|
11
15
|
erp: {
|
|
12
|
-
repo: "https://github.com/
|
|
16
|
+
repo: "https://github.com/flisk-sh/tdk-erp-system.git",
|
|
13
17
|
description: "Enterprise ERP: 100 microservices across 7 business domains",
|
|
14
18
|
},
|
|
15
19
|
"user-management": {
|
|
16
|
-
repo: "https://github.com/
|
|
20
|
+
repo: "https://github.com/flisk-sh/tdk-user-management.git",
|
|
17
21
|
description: "User/auth management services",
|
|
18
22
|
},
|
|
19
23
|
ecommerce: {
|
|
20
|
-
repo: "https://github.com/
|
|
24
|
+
repo: "https://github.com/flisk-sh/tdk-ecommerce-example.git",
|
|
21
25
|
description: "Vue 3 storefront and Hono catalog API (needs Flisk 1.3.75+ for --framework vue)",
|
|
22
26
|
},
|
|
23
27
|
example: {
|
|
24
|
-
repo: "https://github.com/
|
|
28
|
+
repo: "https://github.com/flisk-sh/tdk-example.git",
|
|
25
29
|
bundledPath: "examples/tdk-example",
|
|
26
30
|
description: "Hono + Postgres + NATS worker + Vite example with a routed write path",
|
|
27
31
|
},
|
|
28
32
|
"agent-stack": {
|
|
29
|
-
repo: "https://github.com/
|
|
33
|
+
repo: "https://github.com/flisk-sh/tdk-agent-stack.git",
|
|
30
34
|
description: "AI agent stack: Bun agent + MCP tool server + Postgres + NATS",
|
|
31
35
|
},
|
|
32
36
|
};
|