@flisk-sh/flisk 1.3.204 → 1.3.206
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 +26 -0
- package/cli/dist/cli.js +251 -90
- package/cli/dist/commands/doctor.js +29 -15
- package/cli/dist/commands/project.js +108 -60
- package/cli/dist/commands/resource.js +3 -1
- package/cli/dist/commands/up.js +8 -5
- package/cli/dist/generator/template-engine.js +17 -9
- package/cli/dist/utils/cli-command.js +3 -1
- package/cli/dist/utils/colima.js +23 -4
- package/cli/dist/utils/demo-stage.js +315 -0
- package/cli/dist/utils/doctor-fix.js +35 -9
- package/cli/dist/utils/machine-output.js +27 -0
- package/cli/dist/utils/native-windows.js +20 -0
- package/cli/dist/utils/one-command.js +15 -8
- package/cli/dist/utils/project-templates.js +1 -0
- package/cli/dist-bundle/chunk-3QQGL3NQ.js +35 -0
- package/cli/dist-bundle/chunk-4W6OXDLK.js +17 -0
- package/cli/dist-bundle/chunk-53M5D4KE.js +2 -0
- package/cli/dist-bundle/{chunk-BS6G2SLM.js → chunk-5CFVN3W3.js} +1 -1
- package/cli/dist-bundle/{chunk-RFHMXJTK.js → chunk-6Y6G7PSD.js} +1 -1
- package/cli/dist-bundle/chunk-7C5VA6CW.js +57 -0
- package/cli/dist-bundle/chunk-D2BXXJDM.js +7 -0
- package/cli/dist-bundle/chunk-GANRXKBM.js +2 -0
- package/cli/dist-bundle/chunk-GIMHTZNR.js +2 -0
- package/cli/dist-bundle/chunk-GOEBYLFN.js +2 -0
- package/cli/dist-bundle/chunk-LI3BXAX5.js +2 -0
- package/cli/dist-bundle/{chunk-WMF4AFAE.js → chunk-NCEK46NX.js} +10 -10
- package/cli/dist-bundle/chunk-ODVBKN6S.js +2 -0
- package/cli/dist-bundle/{chunk-Z6UD7UZH.js → chunk-PHI5AT3H.js} +2 -2
- package/cli/dist-bundle/chunk-XSPMLWAE.js +10 -0
- package/cli/dist-bundle/chunk-YZRPX76N.js +2 -0
- package/cli/dist-bundle/cli.js +269 -267
- package/cli/dist-bundle/cold-preflight-4I24DQLW.js +2 -0
- package/cli/dist-bundle/{colima-4ID4BGHC.js → colima-SVZX2UML.js} +1 -1
- package/cli/dist-bundle/demo-stage-EOMANLAD.js +7 -0
- package/cli/dist-bundle/native-windows-X5XVLFJS.js +2 -0
- package/cli/dist-bundle/one-command-P6XVAXWV.js +5 -0
- package/cli/dist-bundle/open-6374RNL6.js +2 -0
- package/cli/dist-bundle/project-IRWFRXWA.js +2 -0
- package/cli/dist-bundle/project-templates-JDRE7AVL.js +2 -0
- package/cli/dist-bundle/services-VIIBMWD4.js +2 -0
- package/cli/dist-bundle/tilt-port-owner-AN3ITRTR.js +2 -0
- package/cli/dist-bundle/url-actions-D6ROTHZG.js +2 -0
- package/cli/package.json +4 -4
- package/engine/assets/docker/wake-gateway/Dockerfile +1 -1
- package/engine/topologies/platform/docker/compose/compose.star +20 -18
- package/engine/topologies/platform/docker/compose/unsafe_value.star +23 -0
- package/engine/topologies/platform/docker/networking/traefik.star +19 -0
- package/package.json +2 -2
- package/cli/dist-bundle/chunk-CBFVK3TF.js +0 -21
- package/cli/dist-bundle/chunk-CF46CD6R.js +0 -6
- package/cli/dist-bundle/chunk-DTIHDMED.js +0 -5
- package/cli/dist-bundle/chunk-EPBYRJE3.js +0 -2
- package/cli/dist-bundle/chunk-GD5676UE.js +0 -52
- package/cli/dist-bundle/chunk-MKJNQTIN.js +0 -2
- package/cli/dist-bundle/chunk-QSPFSZCS.js +0 -34
- package/cli/dist-bundle/chunk-ZVGY7COV.js +0 -4
- package/cli/dist-bundle/cold-preflight-FXOZ5SR5.js +0 -2
- package/cli/dist-bundle/one-command-YP6L6MMM.js +0 -5
- package/cli/dist-bundle/open-UQ6DF3XN.js +0 -2
- package/cli/dist-bundle/project-GTYHCCFG.js +0 -2
- package/cli/dist-bundle/spinner-YLB6FYQH.js +0 -2
- package/cli/dist-bundle/tilt-port-owner-KLNX2ZI2.js +0 -2
- /package/cli/dist-bundle/{devtools-ZUHXW33W.js → devtools-MGITLOWZ.js} +0 -0
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
import { execFileSync } from "node:child_process";
|
|
4
4
|
import { cpSync, existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
5
|
-
import { basename, dirname, join, resolve } from "node:path";
|
|
5
|
+
import { basename, dirname, isAbsolute, join, resolve } from "node:path";
|
|
6
6
|
import { cwd } from "node:process";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
8
|
import chalk from "chalk";
|
|
@@ -86,7 +86,15 @@ export function templateTargetDir(templateName, targetDir) {
|
|
|
86
86
|
const fromRepo = template?.repo.split("/").pop() ?? templateName;
|
|
87
87
|
return fromRepo.replace(/\.git$/, "");
|
|
88
88
|
}
|
|
89
|
-
|
|
89
|
+
/** The last non-empty line of a child process error, for a one-line failure message. */
|
|
90
|
+
function lastLine(text) {
|
|
91
|
+
const lines = text
|
|
92
|
+
.split("\n")
|
|
93
|
+
.map((line) => line.trim())
|
|
94
|
+
.filter(Boolean);
|
|
95
|
+
return lines.pop() ?? "unknown error";
|
|
96
|
+
}
|
|
97
|
+
export async function cloneProjectTemplate(templateName, targetDir, runtimeNextSteps, options = {}) {
|
|
90
98
|
const template = PROJECT_TEMPLATES[templateName];
|
|
91
99
|
if (!template) {
|
|
92
100
|
console.log(chalk.red(`\n❌ Unknown template: "${templateName}"`));
|
|
@@ -101,14 +109,18 @@ export async function cloneProjectTemplate(templateName, targetDir, runtimeNextS
|
|
|
101
109
|
if (!isPathSafe(dirName)) {
|
|
102
110
|
errorFactories.invalidPath(dirName).exit();
|
|
103
111
|
}
|
|
104
|
-
|
|
112
|
+
// If targetDir is an absolute path, use it directly as the destination
|
|
113
|
+
// Otherwise resolve it relative to cwd()
|
|
114
|
+
const destination = isAbsolute(dirName) ? dirName : resolve(cwd(), dirName);
|
|
105
115
|
if (existsSync(destination) && readdirSync(destination).length > 0) {
|
|
106
116
|
errorFactories.directoryExists(destination).exit();
|
|
107
117
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
118
|
+
if (!options.quiet) {
|
|
119
|
+
showCommandHeader(`Cloning template: ${templateName}`);
|
|
120
|
+
showDetail(`${template.description}\n`, 0);
|
|
121
|
+
showDetail(`Source: ${template.bundledPath ? "bundled Flisk example" : template.repo}`, 0);
|
|
122
|
+
showDetail(`Destination: ${destination}\n`, 0);
|
|
123
|
+
}
|
|
112
124
|
try {
|
|
113
125
|
if (template.bundledPath) {
|
|
114
126
|
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../../../");
|
|
@@ -127,15 +139,28 @@ export async function cloneProjectTemplate(templateName, targetDir, runtimeNextS
|
|
|
127
139
|
}
|
|
128
140
|
}
|
|
129
141
|
else {
|
|
130
|
-
|
|
142
|
+
// Piped, not inherited: a raw git progress dump is noise for the caller and, in the
|
|
143
|
+
// one-command flow, it interleaves with the frame that is already on screen.
|
|
144
|
+
execFileSync("git", ["clone", template.repo, destination], {
|
|
145
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
146
|
+
encoding: "utf-8",
|
|
147
|
+
});
|
|
131
148
|
}
|
|
132
149
|
}
|
|
133
|
-
catch {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
150
|
+
catch (error) {
|
|
151
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
152
|
+
const message = template.bundledPath
|
|
153
|
+
? `Bundled example copy failed (${lastLine(detail)}). Reinstall the Flisk CLI package and try again.`
|
|
154
|
+
: `git clone failed: ${lastLine(detail)}. Check you have git installed and network access.`;
|
|
155
|
+
// A caller that paints its own surface (the one-command flow) needs to report the
|
|
156
|
+
// failure itself instead of having the process die mid-paint.
|
|
157
|
+
if (options.quiet)
|
|
158
|
+
throw new Error(message);
|
|
159
|
+
showErrorAndExit(message);
|
|
137
160
|
}
|
|
138
161
|
clearDiscoveryCache();
|
|
162
|
+
if (options.quiet)
|
|
163
|
+
return;
|
|
139
164
|
console.log(chalk.green(`\n✅ Cloned "${templateName}" into ${dirName}/`));
|
|
140
165
|
showDetail("\nNext steps:", 0);
|
|
141
166
|
showDetail(`1. cd ${dirName}`);
|
|
@@ -195,22 +220,26 @@ export const projectCommand = addYesOption(new Command("project"))
|
|
|
195
220
|
.option("--check", "Check if master configs exist and are in sync")
|
|
196
221
|
.option("--force", "Overwrite existing configuration (dangerous)")
|
|
197
222
|
.option("--config-file <path>", "Load project config from existing JSON file")
|
|
223
|
+
.option("-q, --quiet", "Only print what fails; used by `flisk demo`", false)
|
|
198
224
|
.action(async (template, options) => {
|
|
199
225
|
const { assertMachineReadyOrExit, detectRuntimePrerequisites, formatRuntimeNextSteps } = await import("../utils/cold-preflight.js");
|
|
200
226
|
// Project setup only writes files: it must not need a running Docker daemon
|
|
201
227
|
// or an installed Tilt. The runtime is checked on `flisk up`.
|
|
202
228
|
await assertMachineReadyOrExit({ scope: "scaffold" });
|
|
203
229
|
const runtimeNextSteps = formatRuntimeNextSteps(detectRuntimePrerequisites());
|
|
230
|
+
const quiet = Boolean(options.quiet);
|
|
204
231
|
if (template) {
|
|
205
|
-
await runCommand(async () => cloneProjectTemplate(template, options.path, runtimeNextSteps));
|
|
232
|
+
await runCommand(async () => cloneProjectTemplate(template, options.path, runtimeNextSteps, { quiet }));
|
|
206
233
|
return;
|
|
207
234
|
}
|
|
208
235
|
await runCommand(async () => {
|
|
209
236
|
let projectRoot = findProjectRoot();
|
|
210
237
|
if (!projectRoot) {
|
|
211
238
|
projectRoot = cwd();
|
|
212
|
-
|
|
213
|
-
|
|
239
|
+
if (!quiet) {
|
|
240
|
+
showStep("🚀 Initializing new Flisk project...\n");
|
|
241
|
+
showDetail(`Location: ${projectRoot}\n`, 0);
|
|
242
|
+
}
|
|
214
243
|
}
|
|
215
244
|
const tdkDir = join(projectRoot, ".tdk");
|
|
216
245
|
const projectJsonPath = join(tdkDir, PROJECT_JSON);
|
|
@@ -237,15 +266,19 @@ export const projectCommand = addYesOption(new Command("project"))
|
|
|
237
266
|
process.exit(1);
|
|
238
267
|
}
|
|
239
268
|
}
|
|
240
|
-
|
|
241
|
-
|
|
269
|
+
if (!quiet) {
|
|
270
|
+
showCommandHeader("Project Configuration");
|
|
271
|
+
showDetail(`Project root: ${projectRoot}\n`, 0);
|
|
272
|
+
}
|
|
242
273
|
if (!existsSync(tdkDir)) {
|
|
243
274
|
ensureDirectory(tdkDir);
|
|
244
|
-
|
|
275
|
+
if (!quiet)
|
|
276
|
+
showSuccess("Created: .tdk/ directory");
|
|
245
277
|
}
|
|
246
278
|
const projectJsonExists = existsSync(projectJsonPath);
|
|
247
279
|
if (projectJsonExists && !options.force) {
|
|
248
|
-
|
|
280
|
+
if (!quiet)
|
|
281
|
+
showSuccess(".tdk/project.json exists");
|
|
249
282
|
const rawProjectConfig = JSON.parse(readFileSync(projectJsonPath, "utf-8"));
|
|
250
283
|
const projectConfig = readProjectConfig(projectRoot);
|
|
251
284
|
const discoveredStacks = discoverExistingServiceStacks(projectRoot);
|
|
@@ -254,32 +287,36 @@ export const projectCommand = addYesOption(new Command("project"))
|
|
|
254
287
|
if (shouldRewriteProjectConfig) {
|
|
255
288
|
writeJsonFile(projectJsonPath, projectConfig);
|
|
256
289
|
}
|
|
257
|
-
if (autoEnabledStacks.length > 0) {
|
|
290
|
+
if (autoEnabledStacks.length > 0 && !quiet) {
|
|
258
291
|
showDetail(`Auto-enabled discovered service stacks: ${autoEnabledStacks.join(", ")}`, 0);
|
|
259
292
|
}
|
|
260
|
-
|
|
261
|
-
|
|
293
|
+
if (!quiet)
|
|
294
|
+
showStep("\n📋 Regenerating master configuration files...\n");
|
|
295
|
+
await generateMasterConfigs(projectRoot, { quiet });
|
|
262
296
|
if (ensureEnvFile(projectRoot)) {
|
|
263
|
-
|
|
297
|
+
if (!quiet)
|
|
298
|
+
showSuccess("Created: .env (the defaults work as-is)");
|
|
264
299
|
}
|
|
265
300
|
else {
|
|
266
301
|
const added = completeEnvFile(projectRoot);
|
|
267
|
-
if (added.length > 0) {
|
|
302
|
+
if (added.length > 0 && !quiet) {
|
|
268
303
|
showSuccess(`Updated: .env (added ${added.join(", ")}; existing values unchanged)`);
|
|
269
304
|
}
|
|
270
305
|
}
|
|
271
306
|
const ignored = ensureGitignore(projectRoot);
|
|
272
|
-
if (ignored.length > 0) {
|
|
307
|
+
if (ignored.length > 0 && !quiet) {
|
|
273
308
|
showSuccess(`Added to .gitignore: ${ignored.join(", ")}`);
|
|
274
309
|
}
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
310
|
+
if (!quiet) {
|
|
311
|
+
console.log(chalk.green("\n✅ Project configuration regenerated!"));
|
|
312
|
+
showDetail("\nGenerated in .tdk/.tdk-out/:", 0);
|
|
313
|
+
for (const file of MASTER_CONFIG_FILES) {
|
|
314
|
+
showDetail(`- ${file}`);
|
|
315
|
+
}
|
|
316
|
+
showDetail("\nRuntime readiness:", 0);
|
|
317
|
+
for (const line of runtimeNextSteps)
|
|
318
|
+
showDetail(line, 0);
|
|
279
319
|
}
|
|
280
|
-
showDetail("\nRuntime readiness:", 0);
|
|
281
|
-
for (const line of runtimeNextSteps)
|
|
282
|
-
showDetail(line, 0);
|
|
283
320
|
return;
|
|
284
321
|
}
|
|
285
322
|
if (projectJsonExists && options.force && !options.yes) {
|
|
@@ -302,12 +339,14 @@ export const projectCommand = addYesOption(new Command("project"))
|
|
|
302
339
|
}
|
|
303
340
|
const configContent = readFileSync(configFilePath, "utf-8");
|
|
304
341
|
projectConfig = JSON.parse(configContent);
|
|
305
|
-
|
|
342
|
+
if (!quiet)
|
|
343
|
+
showSuccess(`Loaded config from: ${configFilePath}`);
|
|
306
344
|
}
|
|
307
345
|
else if (options.yes) {
|
|
308
346
|
projectConfig = JSON.parse(JSON.stringify(DEFAULT_PROJECT_JSON));
|
|
309
347
|
projectConfig.project.name = projectRoot.split("/").pop() || "my-project";
|
|
310
|
-
|
|
348
|
+
if (!quiet)
|
|
349
|
+
console.log(chalk.gray("Using default configuration (non-interactive mode)"));
|
|
311
350
|
}
|
|
312
351
|
else {
|
|
313
352
|
showStep("📝 Project Setup Wizard\n");
|
|
@@ -399,27 +438,34 @@ export const projectCommand = addYesOption(new Command("project"))
|
|
|
399
438
|
: DEFAULT_PROJECT_JSON.discovery.paths;
|
|
400
439
|
}
|
|
401
440
|
const autoEnabledStacks = syncDiscoveredStacksToPreAlpha(projectConfig, discoveredStacks);
|
|
402
|
-
if (autoEnabledStacks.length > 0) {
|
|
441
|
+
if (autoEnabledStacks.length > 0 && !quiet) {
|
|
403
442
|
showDetail(`Auto-enabled discovered service stacks: ${autoEnabledStacks.join(", ")}`, 0);
|
|
404
443
|
}
|
|
405
|
-
|
|
444
|
+
if (!quiet)
|
|
445
|
+
showStep("\n📋 Creating project configuration...\n");
|
|
406
446
|
writeJsonFile(projectJsonPath, projectConfig);
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
447
|
+
if (!quiet) {
|
|
448
|
+
showSuccess("Created: .tdk/project.json");
|
|
449
|
+
showDetail(`→ Project: ${projectConfig.project.name}`);
|
|
450
|
+
showStep("\n📋 Generating master configuration files...\n");
|
|
451
|
+
}
|
|
452
|
+
await generateMasterConfigs(projectRoot, { quiet });
|
|
453
|
+
if (!quiet)
|
|
454
|
+
showStep("\n🔧 Setting up environment variables...\n");
|
|
412
455
|
const envCreated = ensureEnvFile(projectRoot);
|
|
413
456
|
if (envCreated) {
|
|
414
|
-
|
|
457
|
+
if (!quiet)
|
|
458
|
+
showSuccess("Created: .env (the defaults work as-is)");
|
|
415
459
|
}
|
|
416
460
|
else {
|
|
417
461
|
// An .env from an older CLI lacks keys a newer one expects; add them without changing anything already there.
|
|
418
462
|
const added = completeEnvFile(projectRoot);
|
|
419
463
|
if (added.length > 0) {
|
|
420
|
-
|
|
464
|
+
if (!quiet) {
|
|
465
|
+
showSuccess(`Updated: .env (added ${added.join(", ")}; existing values unchanged)`);
|
|
466
|
+
}
|
|
421
467
|
}
|
|
422
|
-
else {
|
|
468
|
+
else if (!quiet) {
|
|
423
469
|
showSuccess("Found: .env (environment already configured)");
|
|
424
470
|
}
|
|
425
471
|
}
|
|
@@ -432,24 +478,26 @@ export const projectCommand = addYesOption(new Command("project"))
|
|
|
432
478
|
showDetail("→ Edit .env and fill in these values before running `flisk up`", 1);
|
|
433
479
|
}
|
|
434
480
|
const ignored = ensureGitignore(projectRoot);
|
|
435
|
-
if (ignored.length > 0) {
|
|
481
|
+
if (ignored.length > 0 && !quiet) {
|
|
436
482
|
showSuccess(`Added to .gitignore: ${ignored.join(", ")}`);
|
|
437
483
|
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
484
|
+
if (!quiet) {
|
|
485
|
+
console.log(chalk.green("\n✅ Project configuration complete!"));
|
|
486
|
+
showDetail("\nGenerated files in .tdk/.tdk-out/:", 0);
|
|
487
|
+
for (const file of MASTER_CONFIG_FILES) {
|
|
488
|
+
showDetail(`- ${file}`);
|
|
489
|
+
}
|
|
490
|
+
showDetail("\nEnvironment configuration:", 0);
|
|
491
|
+
showDetail("- .env (contains all required variables)");
|
|
492
|
+
showDetail("\nSource file:", 0);
|
|
493
|
+
showDetail("- .tdk/project.json (edit this to change project structure)");
|
|
494
|
+
showDetail("\nNext steps:", 0);
|
|
495
|
+
showDetail("1. flisk resource api --type backend --stack app # scaffold a service");
|
|
496
|
+
showDetail("2. flisk up app # start it");
|
|
497
|
+
showDetail("\nRuntime readiness:", 0);
|
|
498
|
+
for (const line of runtimeNextSteps)
|
|
499
|
+
showDetail(line, 0);
|
|
500
|
+
showDetail("\nAfter editing .tdk/project.json, run `flisk config regenerate`.", 0);
|
|
442
501
|
}
|
|
443
|
-
showDetail("\nEnvironment configuration:", 0);
|
|
444
|
-
showDetail("- .env (contains all required variables)");
|
|
445
|
-
showDetail("\nSource file:", 0);
|
|
446
|
-
showDetail("- .tdk/project.json (edit this to change project structure)");
|
|
447
|
-
showDetail("\nNext steps:", 0);
|
|
448
|
-
showDetail("1. flisk resource api --type backend --stack app # scaffold a service");
|
|
449
|
-
showDetail("2. flisk up app # start it");
|
|
450
|
-
showDetail("\nRuntime readiness:", 0);
|
|
451
|
-
for (const line of runtimeNextSteps)
|
|
452
|
-
showDetail(line, 0);
|
|
453
|
-
showDetail("\nAfter editing .tdk/project.json, run `flisk config regenerate`.", 0);
|
|
454
502
|
});
|
|
455
503
|
});
|
|
@@ -214,7 +214,9 @@ export function createPackageJson(name, type, frameworkId, prismaEnabled = false
|
|
|
214
214
|
// image's `bun run build` fails with TS2688. ^20 matches the services that build.
|
|
215
215
|
"@types/node": "^20.0.0",
|
|
216
216
|
typescript: "^7.0.2",
|
|
217
|
-
|
|
217
|
+
// The same version the CLI tests itself with (cli/package.json devDependencies.vitest).
|
|
218
|
+
// Dependabot pins it exactly to keep CVE-2026-112950 fixed, and the scaffold follows.
|
|
219
|
+
vitest: "5.0.5",
|
|
218
220
|
"@biomejs/biome": "^2.5.13",
|
|
219
221
|
// vitest 5 peers on vite ^6.4.0 || ^7 || ^8; vite 5 makes `vitest run` fail at startup with
|
|
220
222
|
// ERR_PACKAGE_PATH_NOT_EXPORTED ('./module-runner'), in every resource once it is hoisted.
|
package/cli/dist/commands/up.js
CHANGED
|
@@ -19,6 +19,7 @@ import { exportHostPortPlan, getHostPortPlan, writeSavedHostPortPlan, } from "..
|
|
|
19
19
|
import { formatHostPortPlan } from "../utils/host-port-plan.js";
|
|
20
20
|
import { createJsonEmitter } from "../utils/json-output.js";
|
|
21
21
|
import { toMachineError } from "../utils/machine-output.js";
|
|
22
|
+
import { nativeWindowsStackRefusal } from "../utils/native-windows.js";
|
|
22
23
|
import { findProjectRoot, getPackageVersion } from "../utils/paths.js";
|
|
23
24
|
import { findAvailablePort } from "../utils/port-assignment.js";
|
|
24
25
|
import { formatPortFallbackNotice } from "../utils/port-fallback-notice.js";
|
|
@@ -48,11 +49,13 @@ export function formatUpSuccess(port, appUrls = []) {
|
|
|
48
49
|
"Stop: flisk down",
|
|
49
50
|
];
|
|
50
51
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
/**
|
|
53
|
+
* The native Windows boundary for a command that starts a stack.
|
|
54
|
+
*
|
|
55
|
+
* It lives here because `flisk up` is where the boundary has always been enforced; the
|
|
56
|
+
* one-command demo flow refuses with the same message before it clones or installs anything.
|
|
57
|
+
*/
|
|
58
|
+
export const nativeWindowsUpRefusal = nativeWindowsStackRefusal;
|
|
56
59
|
/** Why `flisk up` cannot work on this host, or null when it may proceed. Docker reachability is only probed in container hosts. */
|
|
57
60
|
export async function hostUpRefusal(host = detectHost(), dockerReachable = defaultDockerReachable) {
|
|
58
61
|
if (host.kind === "webcontainer")
|
|
@@ -503,7 +503,8 @@ export async function generateMasterConfigs(projectRoot, options = {}) {
|
|
|
503
503
|
if (fs.existsSync(tiltignoreSource)) {
|
|
504
504
|
if (!fs.existsSync(tiltignoreTarget)) {
|
|
505
505
|
fs.copyFileSync(tiltignoreSource, tiltignoreTarget);
|
|
506
|
-
|
|
506
|
+
if (!options.quiet)
|
|
507
|
+
console.log(`✓ Copied: .tiltignore → project root`);
|
|
507
508
|
}
|
|
508
509
|
else if (options.discardHandEdits) {
|
|
509
510
|
// Only a file Flisk wrote (it carries the header) may be restored; one without the
|
|
@@ -512,17 +513,23 @@ export async function generateMasterConfigs(projectRoot, options = {}) {
|
|
|
512
513
|
if (current.includes(TDK_GENERATED_MARKER) &&
|
|
513
514
|
current !== fs.readFileSync(tiltignoreSource, "utf-8")) {
|
|
514
515
|
fs.copyFileSync(tiltignoreSource, tiltignoreTarget);
|
|
515
|
-
|
|
516
|
+
if (!options.quiet) {
|
|
517
|
+
console.log("✓ Restored: .tiltignore → project root (hand edits discarded)");
|
|
518
|
+
}
|
|
516
519
|
}
|
|
517
520
|
}
|
|
518
521
|
}
|
|
519
522
|
const copiedAssets = ensureProjectRuntimeAssets(projectRoot);
|
|
520
|
-
|
|
521
|
-
|
|
523
|
+
if (!options.quiet) {
|
|
524
|
+
for (const asset of copiedAssets) {
|
|
525
|
+
console.log(`✓ Copied: ${asset} → project root`);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
await vendorTdkExtension(projectRoot, { quiet: options.quiet });
|
|
529
|
+
if (!options.quiet) {
|
|
530
|
+
console.log("");
|
|
531
|
+
console.log("💡 To start Tilt: flisk up");
|
|
522
532
|
}
|
|
523
|
-
await vendorTdkExtension(projectRoot);
|
|
524
|
-
console.log("");
|
|
525
|
-
console.log("💡 To start Tilt: flisk up");
|
|
526
533
|
}
|
|
527
534
|
// The generated Tiltfile loads the Flisk Tilt extension (engine, discovery,
|
|
528
535
|
// specs, ext starlark modules). The extension repo may stay private, so we
|
|
@@ -618,7 +625,7 @@ export function ensureProjectRuntimeAssets(projectRoot) {
|
|
|
618
625
|
}
|
|
619
626
|
return copied;
|
|
620
627
|
}
|
|
621
|
-
async function vendorTdkExtension(projectRoot) {
|
|
628
|
+
async function vendorTdkExtension(projectRoot, options = {}) {
|
|
622
629
|
const vendoredDir = assertTdkGeneratedPath(projectRoot, path.join(".tdk", ".tdk-out", "tdk-cli-ext"));
|
|
623
630
|
// Source resolution order:
|
|
624
631
|
// 1. $TDK_EXTENSION_SOURCE (explicit override)
|
|
@@ -669,7 +676,8 @@ async function vendorTdkExtension(projectRoot) {
|
|
|
669
676
|
filter: (source) => !isTestFile(source),
|
|
670
677
|
});
|
|
671
678
|
}
|
|
672
|
-
|
|
679
|
+
if (!options.quiet)
|
|
680
|
+
console.log(`✓ Vendored Flisk extension → .tdk/.tdk-out/tdk-cli-ext/`);
|
|
673
681
|
// Best-effort: if TDK_LICENSE_KEY is set and grants any paid resource,
|
|
674
682
|
// overlay the premium bundle over the stub files just vendored above.
|
|
675
683
|
// No-ops silently (beyond a warning) if there's no key, the fetch
|
|
@@ -107,7 +107,9 @@ export function ensureGlobalInstall(env = process.env) {
|
|
|
107
107
|
// Not installed under the old name, or the manager is missing.
|
|
108
108
|
}
|
|
109
109
|
try {
|
|
110
|
-
|
|
110
|
+
// Silent on purpose: the caller owns the screen, and npm's install banner would paint
|
|
111
|
+
// over whatever it is showing. Progress is reported by the caller, not by npm.
|
|
112
|
+
execFileSync(manager, ["install", "-g", CLI_PACKAGE], { stdio: "ignore", timeout: 300000 });
|
|
111
113
|
return "installed";
|
|
112
114
|
}
|
|
113
115
|
catch {
|
package/cli/dist/utils/colima.js
CHANGED
|
@@ -114,10 +114,13 @@ export function checkColima(deps = defaultDeps()) {
|
|
|
114
114
|
export function planColimaRepair(status, dockerAnswers) {
|
|
115
115
|
if (!status.isColima)
|
|
116
116
|
return undefined;
|
|
117
|
+
// A Lima that is an x86_64 build on Apple Silicon, or a colima CLI that cannot see the VM because of it,
|
|
118
|
+
// needs a native limactl before anything else can start.
|
|
119
|
+
if (status.problem && /x86_64|rosetta|native arch|cannot see it/i.test(status.problem)) {
|
|
120
|
+
return "native-lima";
|
|
121
|
+
}
|
|
117
122
|
if (!status.running)
|
|
118
123
|
return "start";
|
|
119
|
-
if (status.problem?.includes("cannot see it"))
|
|
120
|
-
return "native-lima";
|
|
121
124
|
if (!dockerAnswers)
|
|
122
125
|
return "restart";
|
|
123
126
|
return undefined;
|
|
@@ -125,15 +128,31 @@ export function planColimaRepair(status, dockerAnswers) {
|
|
|
125
128
|
/** Runs a colima command with a native limactl first on PATH, when one is installed. */
|
|
126
129
|
export function nativeLimaPath(env = process.env) {
|
|
127
130
|
const home = env.HOME ?? "";
|
|
131
|
+
// Prefer the Apple Silicon Homebrew location first; an x86_64 limactl must never be treated as native.
|
|
128
132
|
const candidates = [
|
|
133
|
+
"/opt/homebrew/bin",
|
|
129
134
|
home ? `${home}/.local/bin` : "",
|
|
130
135
|
home ? `${home}/bin` : "",
|
|
131
|
-
"/opt/homebrew/bin",
|
|
132
136
|
].filter(Boolean);
|
|
133
137
|
for (const dir of candidates) {
|
|
134
138
|
const candidate = join(dir, "limactl");
|
|
135
139
|
try {
|
|
136
|
-
if (existsSync(candidate))
|
|
140
|
+
if (!existsSync(candidate))
|
|
141
|
+
continue;
|
|
142
|
+
// Verify the binary is actually a native (arm64/aarch64) build, not a Rosetta x86_64 one.
|
|
143
|
+
let archOutput = "";
|
|
144
|
+
try {
|
|
145
|
+
archOutput = execFileSync("file", ["-bL", candidate], {
|
|
146
|
+
encoding: "utf-8",
|
|
147
|
+
timeout: 5_000,
|
|
148
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
// `file` unavailable: fall back to accepting the candidate.
|
|
153
|
+
return dir;
|
|
154
|
+
}
|
|
155
|
+
if (/arm64|aarch64/i.test(archOutput))
|
|
137
156
|
return dir;
|
|
138
157
|
}
|
|
139
158
|
catch {
|