@intentic/cli 1.0.2

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.
Files changed (174) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +80 -0
  3. package/dist/add-app/add-app.command.d.ts +3 -0
  4. package/dist/add-app/add-app.command.d.ts.map +1 -0
  5. package/dist/add-app/add-app.command.js +50 -0
  6. package/dist/add-app/add-app.command.js.map +1 -0
  7. package/dist/adopt/adopt.command.d.ts +3 -0
  8. package/dist/adopt/adopt.command.d.ts.map +1 -0
  9. package/dist/adopt/adopt.command.js +77 -0
  10. package/dist/adopt/adopt.command.js.map +1 -0
  11. package/dist/adopt/adopt.d.ts +21 -0
  12. package/dist/adopt/adopt.d.ts.map +1 -0
  13. package/dist/adopt/adopt.js +31 -0
  14. package/dist/adopt/adopt.js.map +1 -0
  15. package/dist/app.d.ts +2 -0
  16. package/dist/app.d.ts.map +1 -0
  17. package/dist/app.js +46 -0
  18. package/dist/app.js.map +1 -0
  19. package/dist/apply/access.d.ts +16 -0
  20. package/dist/apply/access.d.ts.map +1 -0
  21. package/dist/apply/access.js +72 -0
  22. package/dist/apply/access.js.map +1 -0
  23. package/dist/apply/apply-lock.d.ts +12 -0
  24. package/dist/apply/apply-lock.d.ts.map +1 -0
  25. package/dist/apply/apply-lock.js +102 -0
  26. package/dist/apply/apply-lock.js.map +1 -0
  27. package/dist/apply/apply.command.d.ts +3 -0
  28. package/dist/apply/apply.command.d.ts.map +1 -0
  29. package/dist/apply/apply.command.js +144 -0
  30. package/dist/apply/apply.command.js.map +1 -0
  31. package/dist/apply/migrate.d.ts +20 -0
  32. package/dist/apply/migrate.d.ts.map +1 -0
  33. package/dist/apply/migrate.js +112 -0
  34. package/dist/apply/migrate.js.map +1 -0
  35. package/dist/cli.d.ts +3 -0
  36. package/dist/cli.d.ts.map +1 -0
  37. package/dist/cli.js +5 -0
  38. package/dist/cli.js.map +1 -0
  39. package/dist/demo.d.ts +2 -0
  40. package/dist/demo.d.ts.map +1 -0
  41. package/dist/demo.js +281 -0
  42. package/dist/demo.js.map +1 -0
  43. package/dist/deployments/deployments.command.d.ts +3 -0
  44. package/dist/deployments/deployments.command.d.ts.map +1 -0
  45. package/dist/deployments/deployments.command.js +19 -0
  46. package/dist/deployments/deployments.command.js.map +1 -0
  47. package/dist/deployments/deployments.d.ts +14 -0
  48. package/dist/deployments/deployments.d.ts.map +1 -0
  49. package/dist/deployments/deployments.js +83 -0
  50. package/dist/deployments/deployments.js.map +1 -0
  51. package/dist/env.config.d.ts +25 -0
  52. package/dist/env.config.d.ts.map +1 -0
  53. package/dist/env.config.js +31 -0
  54. package/dist/env.config.js.map +1 -0
  55. package/dist/host-ssh-tunnel/host-ssh-tunnel.command.d.ts +3 -0
  56. package/dist/host-ssh-tunnel/host-ssh-tunnel.command.d.ts.map +1 -0
  57. package/dist/host-ssh-tunnel/host-ssh-tunnel.command.js +39 -0
  58. package/dist/host-ssh-tunnel/host-ssh-tunnel.command.js.map +1 -0
  59. package/dist/host-ssh-tunnel/host-ssh-tunnel.d.ts +14 -0
  60. package/dist/host-ssh-tunnel/host-ssh-tunnel.d.ts.map +1 -0
  61. package/dist/host-ssh-tunnel/host-ssh-tunnel.js +21 -0
  62. package/dist/host-ssh-tunnel/host-ssh-tunnel.js.map +1 -0
  63. package/dist/index.d.ts +5 -0
  64. package/dist/index.d.ts.map +1 -0
  65. package/dist/index.js +5 -0
  66. package/dist/index.js.map +1 -0
  67. package/dist/init/init.command.d.ts +3 -0
  68. package/dist/init/init.command.d.ts.map +1 -0
  69. package/dist/init/init.command.js +45 -0
  70. package/dist/init/init.command.js.map +1 -0
  71. package/dist/init/init.d.ts +7 -0
  72. package/dist/init/init.d.ts.map +1 -0
  73. package/dist/init/init.js +39 -0
  74. package/dist/init/init.js.map +1 -0
  75. package/dist/init/scaffold-app.d.ts +2 -0
  76. package/dist/init/scaffold-app.d.ts.map +1 -0
  77. package/dist/init/scaffold-app.js +27 -0
  78. package/dist/init/scaffold-app.js.map +1 -0
  79. package/dist/lib/artifact.d.ts +10 -0
  80. package/dist/lib/artifact.d.ts.map +1 -0
  81. package/dist/lib/artifact.js +29 -0
  82. package/dist/lib/artifact.js.map +1 -0
  83. package/dist/lib/cf-tunnel.d.ts +11 -0
  84. package/dist/lib/cf-tunnel.d.ts.map +1 -0
  85. package/dist/lib/cf-tunnel.js +30 -0
  86. package/dist/lib/cf-tunnel.js.map +1 -0
  87. package/dist/lib/known-hosts.d.ts +3 -0
  88. package/dist/lib/known-hosts.d.ts.map +1 -0
  89. package/dist/lib/known-hosts.js +18 -0
  90. package/dist/lib/known-hosts.js.map +1 -0
  91. package/dist/lib/output.d.ts +14 -0
  92. package/dist/lib/output.d.ts.map +1 -0
  93. package/dist/lib/output.js +56 -0
  94. package/dist/lib/output.js.map +1 -0
  95. package/dist/lib/run-log.d.ts +3 -0
  96. package/dist/lib/run-log.d.ts.map +1 -0
  97. package/dist/lib/run-log.js +62 -0
  98. package/dist/lib/run-log.js.map +1 -0
  99. package/dist/lib/templates.d.ts +2 -0
  100. package/dist/lib/templates.d.ts.map +1 -0
  101. package/dist/lib/templates.js +6 -0
  102. package/dist/lib/templates.js.map +1 -0
  103. package/dist/lib/version.d.ts +2 -0
  104. package/dist/lib/version.d.ts.map +1 -0
  105. package/dist/lib/version.js +3 -0
  106. package/dist/lib/version.js.map +1 -0
  107. package/dist/logs/logs.command.d.ts +3 -0
  108. package/dist/logs/logs.command.d.ts.map +1 -0
  109. package/dist/logs/logs.command.js +86 -0
  110. package/dist/logs/logs.command.js.map +1 -0
  111. package/dist/monorepo/monorepo.command.d.ts +3 -0
  112. package/dist/monorepo/monorepo.command.d.ts.map +1 -0
  113. package/dist/monorepo/monorepo.command.js +28 -0
  114. package/dist/monorepo/monorepo.command.js.map +1 -0
  115. package/dist/pipelines/adopt-pipelines.d.ts +35 -0
  116. package/dist/pipelines/adopt-pipelines.d.ts.map +1 -0
  117. package/dist/pipelines/adopt-pipelines.js +77 -0
  118. package/dist/pipelines/adopt-pipelines.js.map +1 -0
  119. package/dist/pipelines/control-plane-sync.d.ts +24 -0
  120. package/dist/pipelines/control-plane-sync.d.ts.map +1 -0
  121. package/dist/pipelines/control-plane-sync.js +62 -0
  122. package/dist/pipelines/control-plane-sync.js.map +1 -0
  123. package/dist/plan/plan.command.d.ts +3 -0
  124. package/dist/plan/plan.command.d.ts.map +1 -0
  125. package/dist/plan/plan.command.js +33 -0
  126. package/dist/plan/plan.command.js.map +1 -0
  127. package/dist/resolve/resolve.command.d.ts +3 -0
  128. package/dist/resolve/resolve.command.d.ts.map +1 -0
  129. package/dist/resolve/resolve.command.js +78 -0
  130. package/dist/resolve/resolve.command.js.map +1 -0
  131. package/dist/resolve/resolve.d.ts +4 -0
  132. package/dist/resolve/resolve.d.ts.map +1 -0
  133. package/dist/resolve/resolve.js +33 -0
  134. package/dist/resolve/resolve.js.map +1 -0
  135. package/dist/restore/restore.command.d.ts +3 -0
  136. package/dist/restore/restore.command.d.ts.map +1 -0
  137. package/dist/restore/restore.command.js +68 -0
  138. package/dist/restore/restore.command.js.map +1 -0
  139. package/dist/sandbox-tunnel/sandbox-tunnel.command.d.ts +3 -0
  140. package/dist/sandbox-tunnel/sandbox-tunnel.command.d.ts.map +1 -0
  141. package/dist/sandbox-tunnel/sandbox-tunnel.command.js +62 -0
  142. package/dist/sandbox-tunnel/sandbox-tunnel.command.js.map +1 -0
  143. package/dist/sandbox-tunnel/sandbox-tunnel.d.ts +18 -0
  144. package/dist/sandbox-tunnel/sandbox-tunnel.d.ts.map +1 -0
  145. package/dist/sandbox-tunnel/sandbox-tunnel.js +37 -0
  146. package/dist/sandbox-tunnel/sandbox-tunnel.js.map +1 -0
  147. package/dist/secrets/generated-secrets.d.ts +6 -0
  148. package/dist/secrets/generated-secrets.d.ts.map +1 -0
  149. package/dist/secrets/generated-secrets.js +26 -0
  150. package/dist/secrets/generated-secrets.js.map +1 -0
  151. package/dist/secrets/secret-store.d.ts +14 -0
  152. package/dist/secrets/secret-store.d.ts.map +1 -0
  153. package/dist/secrets/secret-store.js +111 -0
  154. package/dist/secrets/secret-store.js.map +1 -0
  155. package/dist/secrets/secrets.command.d.ts +6 -0
  156. package/dist/secrets/secrets.command.d.ts.map +1 -0
  157. package/dist/secrets/secrets.command.js +106 -0
  158. package/dist/secrets/secrets.command.js.map +1 -0
  159. package/dist/secrets/secrets.d.ts +7 -0
  160. package/dist/secrets/secrets.d.ts.map +1 -0
  161. package/dist/secrets/secrets.js +14 -0
  162. package/dist/secrets/secrets.js.map +1 -0
  163. package/package.json +69 -0
  164. package/templates/access.md.eta +21 -0
  165. package/templates/demo/Dockerfile.eta +5 -0
  166. package/templates/demo/deploy.config.ts.eta +23 -0
  167. package/templates/demo/env.eta +2 -0
  168. package/templates/env-example.eta +6 -0
  169. package/templates/scaffold/Dockerfile.eta +7 -0
  170. package/templates/scaffold/deploy.config.selfhost.ts.eta +18 -0
  171. package/templates/scaffold/deploy.config.ts.eta +22 -0
  172. package/templates/scaffold/server.js.eta +9 -0
  173. package/templates/workflows/apply.yaml.eta +28 -0
  174. package/templates/workflows/resolve.yaml.eta +26 -0
@@ -0,0 +1,111 @@
1
+ import { existsSync } from "node:fs";
2
+ import { readFile, writeFile } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { createStore, resolveInputs } from "@intentic/engine";
5
+ import { hostTarget } from "@intentic/providers";
6
+ import { SECRETS_FILE } from "../lib/artifact.js";
7
+ export const createLocalSecretStore = (dir) => {
8
+ const path = join(dir, SECRETS_FILE);
9
+ const read = async () => existsSync(path) ? JSON.parse(await readFile(path, "utf8")) : {};
10
+ return {
11
+ get: async (key) => (await read())[key],
12
+ set: async (key, value) => {
13
+ const store = await read();
14
+ store[key] = value;
15
+ await writeFile(path, `${JSON.stringify(store, undefined, 4)}\n`, { mode: 0o600 });
16
+ },
17
+ };
18
+ };
19
+ const HOST_SECRETS_PATH = "/opt/intentic/secrets.json";
20
+ export const createHostSecretStore = (target, executor) => {
21
+ let cache;
22
+ const load = async () => {
23
+ if (cache !== undefined) {
24
+ return cache;
25
+ }
26
+ const session = await executor.connect(target);
27
+ try {
28
+ const result = await session.exec(`cat ${HOST_SECRETS_PATH} 2>/dev/null || echo '{}'`);
29
+ cache = JSON.parse(result.stdout.trim() || "{}");
30
+ }
31
+ finally {
32
+ await session.dispose();
33
+ }
34
+ return cache;
35
+ };
36
+ return {
37
+ get: async (key) => (await load())[key],
38
+ set: async (key, value) => {
39
+ const store = await load();
40
+ store[key] = value;
41
+ const json = JSON.stringify(store, undefined, 4);
42
+ const session = await executor.connect(target);
43
+ try {
44
+ await session.exec(`mkdir -p /opt/intentic && cat > ${HOST_SECRETS_PATH}.tmp <<'INTENTIC_SECRETS_EOF'\n${json}\nINTENTIC_SECRETS_EOF\nchmod 600 ${HOST_SECRETS_PATH}.tmp && mv ${HOST_SECRETS_PATH}.tmp ${HOST_SECRETS_PATH}`);
45
+ }
46
+ finally {
47
+ await session.dispose();
48
+ }
49
+ },
50
+ };
51
+ };
52
+ export const createLayeredSecretStore = (layers, options) => {
53
+ const log = options.log ?? (() => { });
54
+ return {
55
+ get: async (key) => {
56
+ const values = [];
57
+ for (const layer of layers) {
58
+ try {
59
+ values.push(await layer.get(key));
60
+ }
61
+ catch (error) {
62
+ log(`secret-store: a layer's read of "${key}" failed, skipping it: ${String(error)}`);
63
+ values.push(undefined);
64
+ }
65
+ }
66
+ const result = values.find((value) => value !== undefined);
67
+ if (result !== undefined && options.backfill) {
68
+ for (const [i, layer] of layers.entries()) {
69
+ if (values[i] === result) {
70
+ continue;
71
+ }
72
+ try {
73
+ await layer.set(key, result);
74
+ }
75
+ catch (error) {
76
+ log(`secret-store: failed to backfill "${key}" into a layer: ${String(error)}`);
77
+ }
78
+ }
79
+ }
80
+ return result;
81
+ },
82
+ set: async (key, value) => {
83
+ let persisted = false;
84
+ for (const layer of layers) {
85
+ try {
86
+ await layer.set(key, value);
87
+ persisted = true;
88
+ }
89
+ catch (error) {
90
+ log(`secret-store: a layer's write of "${key}" failed: ${String(error)}`);
91
+ }
92
+ }
93
+ if (!persisted) {
94
+ throw new Error(`failed to persist generated secret "${key}" to any store`);
95
+ }
96
+ },
97
+ };
98
+ };
99
+ export const generatedSecretStore = (graph, dir, ssh, backfill, log) => {
100
+ const local = createLocalSecretStore(dir);
101
+ const forgejo = Object.values(graph.resources).find((node) => node.type === "forgejo");
102
+ const serverRef = forgejo?.inputs["server"];
103
+ const hostId = typeof serverRef === "object" && serverRef !== null && "$ref" in serverRef ? serverRef.$ref : undefined;
104
+ const hostNode = hostId !== undefined ? graph.resources[hostId] : undefined;
105
+ if (hostNode === undefined) {
106
+ return local;
107
+ }
108
+ const target = hostTarget(resolveInputs(hostNode.inputs, createStore(), process.env, { lenient: false }));
109
+ return createLayeredSecretStore([createHostSecretStore(target, ssh), local], { backfill, log });
110
+ };
111
+ //# sourceMappingURL=secret-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secret-store.js","sourceRoot":"","sources":["../../src/secrets/secret-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { type ForgejoApi } from "@intentic/providers";
2
+ import { type CommandContext } from "@stricli/core";
3
+ import { type Output } from "../lib/output.js";
4
+ export declare const pushSecrets: (out: Output, artifact: string, api?: ForgejoApi) => Promise<void>;
5
+ export declare const secretsCommand: import("@stricli/core").RouteMap<CommandContext>;
6
+ //# sourceMappingURL=secrets.command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secrets.command.d.ts","sourceRoot":"","sources":["../../src/secrets/secrets.command.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,UAAU,EAAc,MAAM,qBAAqB,CAAC;AAGlE,OAAO,EAA+B,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAGjF,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,kBAAkB,CAAC;AA+B7D,eAAO,MAAM,WAAW,GAAU,KAAK,MAAM,EAAE,UAAU,MAAM,EAAE,MAAK,UAAuB,KAAG,OAAO,CAAC,IAAI,CAmE3G,CAAC;AAYF,eAAO,MAAM,cAAc,kDAGzB,CAAC"}
@@ -0,0 +1,106 @@
1
+ import { dirname } from "node:path";
2
+ import { forgejoApi } from "@intentic/providers";
3
+ import { collectSecretInventory, readSyncState, secretDigest, writeSyncState } from "@intentic/scaffold";
4
+ import { buildCommand, buildRouteMap } from "@stricli/core";
5
+ import { loadConfig } from "../env.config.js";
6
+ import { ARTIFACT_FILE, ARTIFACT_PATH, CONFIG_FILE, INTENT_DIR, loadEnvFile, readArtifact, TARGET_DIR } from "../lib/artifact.js";
7
+ import { createOutput } from "../lib/output.js";
8
+ import { version } from "../lib/version.js";
9
+ import { APPLY_WORKFLOW_PATH, applyWorkflowYaml, collectSecretValues, setRepoSecrets, writeWorkflow } from "../pipelines/adopt-pipelines.js";
10
+ import { forgejoIdentity } from "../pipelines/control-plane-sync.js";
11
+ import { readGeneratedSecrets } from "../secrets/generated-secrets.js";
12
+ const entryLine = (entry) => {
13
+ const requiredBy = entry.requiredBy.length > 0 ? ` → required by ${entry.requiredBy.map((r) => r.resourceId).join(", ")}` : "";
14
+ const ci = entry.ci === undefined ? "" : ` [CI: ${entry.ci.synced ? "synced" : "out of date"}]`;
15
+ return `${entry.key} ${entry.status} (${entry.kind}, ${entry.storedAt})${requiredBy}${ci}`;
16
+ };
17
+ const list = buildCommand({
18
+ docs: { brief: "List every secret the workspace knows about — status, consumers, and CI sync state (never values)" },
19
+ parameters: {
20
+ flags: { artifact: { kind: "parsed", parse: String, optional: true, brief: `Path to the artifact (default: ${ARTIFACT_PATH})` } },
21
+ },
22
+ async func(flags) {
23
+ const out = createOutput(this.process.stdout, loadConfig().intenticOutput);
24
+ const entries = await collectSecretInventory(dirname(flags.artifact ?? ARTIFACT_PATH));
25
+ for (const entry of entries) {
26
+ out.text(entryLine(entry));
27
+ }
28
+ out.result({ entries });
29
+ },
30
+ });
31
+ export const pushSecrets = async (out, artifact, api = forgejoApi) => {
32
+ const targetDir = dirname(artifact);
33
+ const sync = await readSyncState(targetDir);
34
+ if (Object.keys(sync).length === 0) {
35
+ out.text("not adopted (no CI secret record) — nothing to push");
36
+ out.result({ pushed: [], reason: "not adopted" });
37
+ return;
38
+ }
39
+ loadEnvFile(targetDir);
40
+ const graph = await readArtifact(artifact);
41
+ const { domain, user, adminPasswordRef: ref } = forgejoIdentity(graph);
42
+ const generatedValues = await readGeneratedSecrets(targetDir);
43
+ const password = ref.source === "generated" ? generatedValues[ref.key] : process.env[ref.key];
44
+ if (password === undefined || password === "") {
45
+ throw new Error(`forgejo admin password (${ref.source} secret ${ref.key}) is not available`);
46
+ }
47
+ const current = collectSecretValues(graph, process.env, generatedValues);
48
+ const changed = Object.entries(current).filter(([key, value]) => sync[key]?.digest !== secretDigest(value));
49
+ const baseUrl = `https://${domain}`;
50
+ if (changed.length > 0) {
51
+ await setRepoSecrets({ api, baseUrl, user, password, owner: user, name: TARGET_DIR, secrets: Object.fromEntries(changed) });
52
+ const cloudflare = changed.find(([key]) => key === "CLOUDFLARE_API_TOKEN");
53
+ if (cloudflare !== undefined) {
54
+ await setRepoSecrets({
55
+ api,
56
+ baseUrl,
57
+ user,
58
+ password,
59
+ owner: user,
60
+ name: INTENT_DIR,
61
+ secrets: { CLOUDFLARE_API_TOKEN: cloudflare[1] },
62
+ });
63
+ }
64
+ }
65
+ const keySetChanged = Object.keys(current).some((key) => sync[key] === undefined);
66
+ if (keySetChanged) {
67
+ const inputs = {
68
+ cliVersion: version,
69
+ user,
70
+ domain,
71
+ configFile: CONFIG_FILE,
72
+ artifactFile: ARTIFACT_FILE,
73
+ intentRepo: INTENT_DIR,
74
+ desiredStateRepo: TARGET_DIR,
75
+ applySecretKeys: Object.keys(current).toSorted(),
76
+ forgejoPasswordKey: ref.key,
77
+ };
78
+ await writeWorkflow(targetDir, APPLY_WORKFLOW_PATH, applyWorkflowYaml(inputs));
79
+ out.text(`regenerated ${APPLY_WORKFLOW_PATH} for the new key set — commit and push desired-state to activate it`);
80
+ }
81
+ const pushedAt = new Date().toISOString();
82
+ await writeSyncState(targetDir, {
83
+ ...sync,
84
+ ...Object.fromEntries(changed.map(([key, value]) => [key, { digest: secretDigest(value), pushedAt }])),
85
+ });
86
+ const pushed = changed.map(([key]) => key).toSorted();
87
+ const skipped = Object.keys(current)
88
+ .filter((key) => !pushed.includes(key))
89
+ .toSorted();
90
+ out.text(pushed.length > 0 ? `pushed ${pushed.length} secret(s) to ${user}/${TARGET_DIR}: ${pushed.join(", ")}` : "CI secrets already in sync");
91
+ out.result({ pushed, skipped });
92
+ };
93
+ const push = buildCommand({
94
+ docs: { brief: "Push changed secret values to the provisioned Forgejo's Actions secrets (no-op before adopt)" },
95
+ parameters: {
96
+ flags: { artifact: { kind: "parsed", parse: String, optional: true, brief: `Path to the artifact (default: ${ARTIFACT_PATH})` } },
97
+ },
98
+ async func(flags) {
99
+ await pushSecrets(createOutput(this.process.stdout, loadConfig().intenticOutput), flags.artifact ?? ARTIFACT_PATH);
100
+ },
101
+ });
102
+ export const secretsCommand = buildRouteMap({
103
+ routes: { list, push },
104
+ docs: { brief: "Inspect and sync the workspace's secrets (keys and status only — values never print)" },
105
+ });
106
+ //# sourceMappingURL=secrets.command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secrets.command.js","sourceRoot":"","sources":["../../src/secrets/secrets.command.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import { type DesiredStateGraph } from "@intentic/graph";
2
+ export declare const collectSecrets: (graph: DesiredStateGraph) => {
3
+ readonly env: string[];
4
+ readonly generated: string[];
5
+ };
6
+ export declare const writeEnvExample: (path: string, keys: readonly string[]) => Promise<void>;
7
+ //# sourceMappingURL=secrets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secrets.d.ts","sourceRoot":"","sources":["../../src/secrets/secrets.ts"],"names":[],"mappings":"AACA,OAAO,EAAsB,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAK7E,eAAO,MAAM,cAAc,GAAI,OAAO,iBAAiB,KAAG;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,CAAA;CAM/G,CAAC;AAIF,eAAO,MAAM,eAAe,GAAU,MAAM,MAAM,EAAE,MAAM,SAAS,MAAM,EAAE,KAAG,OAAO,CAAC,IAAI,CAEzF,CAAC"}
@@ -0,0 +1,14 @@
1
+ import { writeFile } from "node:fs/promises";
2
+ import { collectSecretUsage } from "@intentic/graph";
3
+ import { renderTemplate } from "../lib/templates.js";
4
+ export const collectSecrets = (graph) => {
5
+ const usage = collectSecretUsage(graph);
6
+ return {
7
+ env: usage.filter((u) => u.source === "env").map((u) => u.key),
8
+ generated: usage.filter((u) => u.source === "generated").map((u) => u.key),
9
+ };
10
+ };
11
+ export const writeEnvExample = async (path, keys) => {
12
+ await writeFile(path, renderTemplate("env-example", { keys }));
13
+ };
14
+ //# sourceMappingURL=secrets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secrets.js","sourceRoot":"","sources":["../../src/secrets/secrets.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,kBAAkB,EAA0B,MAAM,iBAAiB,CAAC;AAC7E,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAIrD,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,KAAwB,EAA4D,EAAE;IACjH,MAAM,KAAK,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;IACxC,OAAO;QACH,GAAG,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;QAC9D,SAAS,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;KAC7E,CAAC;AACN,CAAC,CAAC;AAIF,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,EAAE,IAAY,EAAE,IAAuB,EAAiB,EAAE;IAC1F,MAAM,SAAS,CAAC,IAAI,EAAE,cAAc,CAAC,aAAa,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AACnE,CAAC,CAAC"}
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "@intentic/cli",
3
+ "version": "1.0.2",
4
+ "description": "",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://gitlab.com/radarsu/intentic.git",
10
+ "directory": "_apps/cli"
11
+ },
12
+ "files": [
13
+ "dist",
14
+ "templates"
15
+ ],
16
+ "bin": {
17
+ "intentic": "./dist/cli.js"
18
+ },
19
+ "publishConfig": {
20
+ "access": "public",
21
+ "registry": "https://registry.npmjs.org/"
22
+ },
23
+ "exports": {
24
+ ".": {
25
+ "types": "./dist/index.d.ts",
26
+ "import": {
27
+ "@intentic/src": "./src/index.ts",
28
+ "default": "./dist/index.js"
29
+ },
30
+ "require": {
31
+ "@intentic/src": "./src/index.ts",
32
+ "default": "./dist/index.js"
33
+ }
34
+ }
35
+ },
36
+ "dependencies": {
37
+ "@puristic/env": "1.4.1",
38
+ "@stricli/core": "1.2.8",
39
+ "eta": "4.6.0",
40
+ "tslib": "2.8.1",
41
+ "zod": "4.4.3",
42
+ "@intentic/engine": "1.0.2",
43
+ "@intentic/graph": "1.0.2",
44
+ "@intentic/need-resolver": "1.0.2",
45
+ "@intentic/providers": "1.0.2",
46
+ "@intentic/sandbox-contract": "1.0.2",
47
+ "@intentic/scaffold": "0.0.0",
48
+ "@intentic/state-resolver": "1.0.2"
49
+ },
50
+ "devDependencies": {
51
+ "@types/node": "24.13.2",
52
+ "@types/ssh2": "1.15.5",
53
+ "ssh2": "1.17.0",
54
+ "testcontainers": "12.0.3",
55
+ "typescript": "5.9.3",
56
+ "vitest": "4.1.9",
57
+ "yaml": "2.9.0",
58
+ "@intentic/sdk": "1.0.2",
59
+ "@intentic/tsconfig": "0.0.0"
60
+ },
61
+ "scripts": {
62
+ "build": "tsc",
63
+ "dev": "tsc --build --watch",
64
+ "watch": "tsc --build --watch",
65
+ "test": "vitest run",
66
+ "test:watch": "vitest",
67
+ "e2e": "INTENTIC_E2E=1 vitest run cli.e2e"
68
+ }
69
+ }
@@ -0,0 +1,21 @@
1
+ # Access
2
+
3
+ Generated by `intentic apply`. User-supplied passwords are the values you set in `.env`; generated passwords are stored in `.secrets.json` (gitignored).
4
+ <% if (it.services.length > 0) { -%>
5
+
6
+ | Service | URL | Username | Password |
7
+ | --- | --- | --- | --- |
8
+ <% it.services.forEach(function (service) { -%>
9
+ | <%= service.label %> | <%= service.url %> | <%= service.username %> | <%= service.password %> |
10
+ <% }) -%>
11
+ <% } -%>
12
+ <% if (it.apps.length > 0) { -%>
13
+
14
+ ## App environments
15
+
16
+ | Environment | URL |
17
+ | --- | --- |
18
+ <% it.apps.forEach(function (app) { -%>
19
+ | <%= app.id %> | <%= app.url %> |
20
+ <% }) -%>
21
+ <% } -%>
@@ -0,0 +1,5 @@
1
+ FROM busybox:1.37.0
2
+ RUN mkdir -p /www && printf '%s' '<%= it.appBody %>' > /www/index.html
3
+ ENV PORT=8080
4
+ EXPOSE 8080
5
+ CMD ["sh","-c","httpd -f -v -p ${PORT} -h /www"]
@@ -0,0 +1,23 @@
1
+ import { env } from "@intentic/graph";
2
+ import { defineIntent } from "@intentic/sdk";
3
+
4
+ export const intent = defineIntent((i) => {
5
+ const host = i.have.host("host", {
6
+ address: "127.0.0.1",
7
+ user: "root",
8
+ sshKey: env("HOST_SSH_KEY"),
9
+ port: <%= it.sshPort %>,
10
+ });
11
+
12
+ const cf = i.have.cloudflare("cf", {
13
+ apiToken: env("CLOUDFLARE_API_TOKEN"),
14
+ });
15
+
16
+ i.want.app("<%= it.app %>", {
17
+ on: host,
18
+ expose: cf,
19
+ environments: {
20
+ <%= it.env %>: { domain: "<%= it.domain %>", branch: "main", env: { PORT: "<%= it.appPort %>" } },
21
+ },
22
+ });
23
+ });
@@ -0,0 +1,2 @@
1
+ HOST_SSH_KEY="<%= it.privateKey %>"
2
+ CLOUDFLARE_API_TOKEN=<%= it.apiToken %>
@@ -0,0 +1,6 @@
1
+ # Secrets required by desired-state.json. Copy this file to .env and fill in each value.
2
+ # Regenerated by `intentic resolve`.
3
+
4
+ <% it.keys.forEach(function (key) { -%>
5
+ <%= key %>=
6
+ <% }) -%>
@@ -0,0 +1,7 @@
1
+ # intentic starter Dockerfile — replace with your app's real build.
2
+ FROM node:24.18.0-alpine3.24
3
+ WORKDIR /app
4
+ COPY . .
5
+ ENV PORT=8080
6
+ EXPOSE 8080
7
+ CMD ["node", "server.js"]
@@ -0,0 +1,18 @@
1
+ import { env } from "@intentic/graph";
2
+ import { defineIntent } from "@intentic/sdk";
3
+
4
+ export const intent = defineIntent((i) => {
5
+ const cf = i.have.cloudflare("cf", {
6
+ apiToken: env("CLOUDFLARE_API_TOKEN"),
7
+ });
8
+
9
+ // `self` is your local deploy target (this machine / its Docker-in-Docker host). intentic registers it in
10
+ // the managed `// <intentic>` block at the top of this file — reference it with `on: self`, don't redeclare it.
11
+ i.want.app("my-app", {
12
+ on: self,
13
+ expose: cf,
14
+ environments: {
15
+ production: { domain: "app.<%= it.zone %>", branch: "main" },
16
+ },
17
+ });
18
+ });
@@ -0,0 +1,22 @@
1
+ import { env } from "@intentic/graph";
2
+ import { defineIntent } from "@intentic/sdk";
3
+
4
+ export const intent = defineIntent((i) => {
5
+ const host = i.have.host("host", {
6
+ address: "203.0.113.10",
7
+ user: "deploy",
8
+ sshKey: env("HOST_SSH_KEY"),
9
+ });
10
+
11
+ const cf = i.have.cloudflare("cf", {
12
+ apiToken: env("CLOUDFLARE_API_TOKEN"),
13
+ });
14
+
15
+ i.want.app("my-app", {
16
+ on: host,
17
+ expose: cf,
18
+ environments: {
19
+ production: { domain: "app.example.com", branch: "main", env: { DATABASE_URL: env("PRODUCTION_DATABASE_URL") } },
20
+ },
21
+ });
22
+ });
@@ -0,0 +1,9 @@
1
+ import { createServer } from "node:http";
2
+
3
+ // Komodo sets PORT in production; the sandbox's app process manager injects PORT for the live preview.
4
+ const port = Number(process.env.PORT ?? 3000);
5
+
6
+ createServer((_req, res) => {
7
+ res.writeHead(200, { "content-type": "text/html; charset=utf-8" });
8
+ res.end("<!doctype html><title>intentic app</title><h1>It works 🎉</h1><p>Edit <code>server.js</code> — the agent works on this repo.</p>");
9
+ }).listen(port, () => console.log(`app listening on :${port}`));
@@ -0,0 +1,28 @@
1
+ # Generated by intentic: apply the desired-state artifact, pruning resources it no longer declares.
2
+ on:
3
+ push:
4
+ branches: [ "main" ]
5
+ paths: [ "<%= it.artifactFile %>" ]
6
+ jobs:
7
+ apply:
8
+ runs-on: docker
9
+ env:
10
+ <% it.envEntries.forEach(function (entry) { -%>
11
+ <%= entry.env %>: ${{ secrets.<%= entry.secret %> }}
12
+ <% }) -%>
13
+ steps:
14
+ - uses: https://github.com/actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
15
+ with:
16
+ fetch-depth: 0
17
+ - name: apply and prune
18
+ run: |
19
+ corepack enable
20
+ PREV=''
21
+ if git rev-parse -q --verify refs/tags/<%= it.appliedTag %> >/dev/null; then \
22
+ git show <%= it.appliedTag %>:<%= it.artifactFile %> > /tmp/previous.json && PREV="--previous /tmp/previous.json"; fi
23
+ pnpm dlx <%= it.cliPackage %>@<%= it.cliVersion %> apply --artifact <%= it.artifactFile %> $PREV
24
+ - name: mark applied
25
+ run: |
26
+ AUTH="$(printf '%s:%s' '<%= it.user %>' "$<%= it.forgejoPasswordKey %>" | base64 -w0)"
27
+ git tag -f <%= it.appliedTag %> HEAD
28
+ git -c http.extraHeader="Authorization: basic $AUTH" push -f origin refs/tags/<%= it.appliedTag %>
@@ -0,0 +1,26 @@
1
+ # Generated by intentic: resolve deploy.config.ts and push the new artifact into the desired-state repo.
2
+ on:
3
+ push:
4
+ branches: [ "main" ]
5
+ paths: [ "<%= it.configFile %>", "package.json", "pnpm-lock.yaml" ]
6
+ jobs:
7
+ resolve:
8
+ runs-on: docker
9
+ env:
10
+ CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
11
+ <%= it.gitUserEnv %>: ${{ secrets.<%= it.gitUserSecret %> }}
12
+ <%= it.gitTokenEnv %>: ${{ secrets.<%= it.gitTokenSecret %> }}
13
+ steps:
14
+ - uses: https://github.com/actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
15
+ - name: resolve and push desired-state
16
+ run: |
17
+ corepack enable
18
+ pnpm install --ignore-workspace
19
+ AUTH="$(printf '%s:%s' "$GIT_USER" "$GIT_TOKEN" | base64 -w0)"
20
+ git -c http.extraHeader="Authorization: basic $AUTH" clone <%= it.desiredStateCloneUrl %> /tmp/ds
21
+ pnpm dlx <%= it.cliPackage %>@<%= it.cliVersion %> resolve --config <%= it.configFile %> --out /tmp/ds/<%= it.artifactFile %> --sync-control-plane
22
+ cd /tmp/ds
23
+ git add -A
24
+ if git diff --cached --quiet; then echo 'desired-state unchanged'; exit 0; fi
25
+ git -c user.name="<%= it.user %>" -c user.email="<%= it.user %>@<%= it.domain %>" commit -m "intentic resolve"
26
+ git -c http.extraHeader="Authorization: basic $AUTH" push origin HEAD:main