@kntic/kntic 0.4.0 → 0.4.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kntic/kntic",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "author": "Thomas Robak <contact@kntic.ai> (https://kntic.ai)",
5
5
  "description": "KNTIC CLI — bootstrap and manage KNTIC projects",
6
6
  "main": "src/index.js",
package/src/cli.js CHANGED
@@ -21,6 +21,13 @@ if (!subcommand || subcommand === "usage") {
21
21
  console.error(`Error: ${err.message}`);
22
22
  process.exit(1);
23
23
  }
24
+ } else if (subcommand === "stop") {
25
+ try {
26
+ commands.stop();
27
+ } catch (err) {
28
+ console.error(`Error: ${err.message}`);
29
+ process.exit(1);
30
+ }
24
31
  } else if (subcommand === "update") {
25
32
  commands.update().catch((err) => {
26
33
  console.error(`Error: ${err.message}`);
@@ -3,6 +3,7 @@
3
3
  const usage = require("./usage");
4
4
  const init = require("./init");
5
5
  const start = require("./start");
6
+ const stop = require("./stop");
6
7
  const update = require("./update");
7
8
 
8
- module.exports = { usage, init, start, update };
9
+ module.exports = { usage, init, start, stop, update };
@@ -13,6 +13,10 @@ function isScreenAvailable() {
13
13
  }
14
14
  }
15
15
 
16
+ function isInsideScreen() {
17
+ return !!process.env.STY;
18
+ }
19
+
16
20
  function getScreenName() {
17
21
  try {
18
22
  const content = readFileSync(".kntic.env", "utf8");
@@ -29,11 +33,14 @@ function getScreenName() {
29
33
  function start() {
30
34
  const composeCmd = "docker compose -f kntic.yml --env-file .kntic.env up --build";
31
35
 
32
- if (isScreenAvailable()) {
36
+ if (isScreenAvailable() && !isInsideScreen()) {
33
37
  const screenName = getScreenName();
34
38
  console.log(`Starting KNTIC services in screen session "${screenName}"…`);
35
39
  execSync(`screen -S ${screenName} ${composeCmd}`, { stdio: "inherit" });
36
40
  } else {
41
+ if (isInsideScreen()) {
42
+ console.log("Already inside a screen session, skipping screen wrapper.");
43
+ }
37
44
  console.log("Starting KNTIC services…");
38
45
  execSync(composeCmd, { stdio: "inherit" });
39
46
  }
@@ -6,9 +6,14 @@ const fs = require("fs");
6
6
  const path = require("path");
7
7
  const os = require("os");
8
8
 
9
- // We test the helper logic by requiring the module and inspecting behaviour.
10
- // Since start.js uses execSync (side-effecting), we test the screen-name
11
- // resolution logic and the screen-detection path indirectly.
9
+ // Helper: extract a named function from start.js source by evaluating it in isolation.
10
+ function extractFn(fnName) {
11
+ const src = fs.readFileSync(require.resolve("./start"), "utf8");
12
+ return new Function("require", "process",
13
+ src.replace(/^"use strict";\s*/, "")
14
+ .replace(/module\.exports\s*=\s*start;/, `return ${fnName};`)
15
+ )(require, process);
16
+ }
12
17
 
13
18
  describe("start — getScreenName resolution", () => {
14
19
  let tmpDir, origCwd;
@@ -26,47 +31,58 @@ describe("start — getScreenName resolution", () => {
26
31
 
27
32
  it("uses KNTIC_PRJ_PREFIX from .kntic.env when present", () => {
28
33
  fs.writeFileSync(path.join(tmpDir, ".kntic.env"), "UID=1000\nKNTIC_PRJ_PREFIX=myproject\nGID=1000\n");
29
- // Re-require to get fresh module (clear cache)
30
34
  delete require.cache[require.resolve("./start")];
31
- // We can't call start() directly (it execs docker), so we extract getScreenName
32
- // by reading the source and evaluating the helper in isolation.
33
- const src = fs.readFileSync(require.resolve("./start"), "utf8");
34
- const getScreenName = new Function("require", "process",
35
- src.replace(/^"use strict";\s*/, "")
36
- .replace(/module\.exports\s*=\s*start;/, "return getScreenName;")
37
- )(require, process);
35
+ const getScreenName = extractFn("getScreenName");
38
36
  assert.equal(getScreenName(), "myproject");
39
37
  });
40
38
 
41
39
  it("falls back to current directory name when KNTIC_PRJ_PREFIX is missing", () => {
42
40
  fs.writeFileSync(path.join(tmpDir, ".kntic.env"), "UID=1000\nGID=1000\n");
43
41
  delete require.cache[require.resolve("./start")];
44
- const src = fs.readFileSync(require.resolve("./start"), "utf8");
45
- const getScreenName = new Function("require", "process",
46
- src.replace(/^"use strict";\s*/, "")
47
- .replace(/module\.exports\s*=\s*start;/, "return getScreenName;")
48
- )(require, process);
42
+ const getScreenName = extractFn("getScreenName");
49
43
  assert.equal(getScreenName(), path.basename(tmpDir));
50
44
  });
51
45
 
52
46
  it("falls back to current directory name when .kntic.env does not exist", () => {
53
47
  delete require.cache[require.resolve("./start")];
54
- const src = fs.readFileSync(require.resolve("./start"), "utf8");
55
- const getScreenName = new Function("require", "process",
56
- src.replace(/^"use strict";\s*/, "")
57
- .replace(/module\.exports\s*=\s*start;/, "return getScreenName;")
58
- )(require, process);
48
+ const getScreenName = extractFn("getScreenName");
59
49
  assert.equal(getScreenName(), path.basename(tmpDir));
60
50
  });
61
51
 
62
52
  it("ignores empty KNTIC_PRJ_PREFIX value", () => {
63
53
  fs.writeFileSync(path.join(tmpDir, ".kntic.env"), "KNTIC_PRJ_PREFIX=\nUID=1000\n");
64
54
  delete require.cache[require.resolve("./start")];
65
- const src = fs.readFileSync(require.resolve("./start"), "utf8");
66
- const getScreenName = new Function("require", "process",
67
- src.replace(/^"use strict";\s*/, "")
68
- .replace(/module\.exports\s*=\s*start;/, "return getScreenName;")
69
- )(require, process);
55
+ const getScreenName = extractFn("getScreenName");
70
56
  assert.equal(getScreenName(), path.basename(tmpDir));
71
57
  });
72
58
  });
59
+
60
+ describe("start — isInsideScreen detection", () => {
61
+ let origSTY;
62
+
63
+ beforeEach(() => {
64
+ origSTY = process.env.STY;
65
+ });
66
+
67
+ afterEach(() => {
68
+ if (origSTY === undefined) {
69
+ delete process.env.STY;
70
+ } else {
71
+ process.env.STY = origSTY;
72
+ }
73
+ });
74
+
75
+ it("returns true when STY environment variable is set", () => {
76
+ process.env.STY = "12345.myscreen";
77
+ delete require.cache[require.resolve("./start")];
78
+ const isInsideScreen = extractFn("isInsideScreen");
79
+ assert.equal(isInsideScreen(), true);
80
+ });
81
+
82
+ it("returns false when STY environment variable is not set", () => {
83
+ delete process.env.STY;
84
+ delete require.cache[require.resolve("./start")];
85
+ const isInsideScreen = extractFn("isInsideScreen");
86
+ assert.equal(isInsideScreen(), false);
87
+ });
88
+ });
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+
3
+ const { execSync } = require("child_process");
4
+
5
+ function stop() {
6
+ const composeCmd = "docker compose -f kntic.yml --env-file .kntic.env stop";
7
+ console.log("Stopping KNTIC services…");
8
+ execSync(composeCmd, { stdio: "inherit" });
9
+ }
10
+
11
+ module.exports = stop;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+
3
+ const { describe, it } = require("node:test");
4
+ const assert = require("node:assert/strict");
5
+ const fs = require("fs");
6
+
7
+ describe("stop — module structure", () => {
8
+ it("exports a function", () => {
9
+ const stop = require("./stop");
10
+ assert.equal(typeof stop, "function");
11
+ });
12
+
13
+ it("source contains the correct docker compose stop command", () => {
14
+ const src = fs.readFileSync(require.resolve("./stop"), "utf8");
15
+ assert.ok(src.includes("docker compose -f kntic.yml --env-file .kntic.env stop"));
16
+ });
17
+ });
@@ -7,6 +7,7 @@ function usage() {
7
7
  console.log(" usage List all available sub-commands");
8
8
  console.log(" init Download and extract the KNTIC bootstrap template into the current directory");
9
9
  console.log(" start Build and start KNTIC services via docker compose (uses kntic.yml + .kntic.env)");
10
+ console.log(" stop Stop KNTIC services via docker compose");
10
11
  console.log(" update Download the latest KNTIC bootstrap and update .kntic/lib only");
11
12
  console.log("");
12
13
  }