@intentic/providers 1.18.0

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 (207) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +26 -0
  3. package/dist/app.d.ts +4 -0
  4. package/dist/app.d.ts.map +1 -0
  5. package/dist/app.js +72 -0
  6. package/dist/app.js.map +1 -0
  7. package/dist/authentik-api.d.ts +23 -0
  8. package/dist/authentik-api.d.ts.map +1 -0
  9. package/dist/authentik-api.js +67 -0
  10. package/dist/authentik-api.js.map +1 -0
  11. package/dist/authentik-client.d.ts +4 -0
  12. package/dist/authentik-client.d.ts.map +1 -0
  13. package/dist/authentik-client.js +50 -0
  14. package/dist/authentik-client.js.map +1 -0
  15. package/dist/authentik.d.ts +4 -0
  16. package/dist/authentik.d.ts.map +1 -0
  17. package/dist/authentik.js +132 -0
  18. package/dist/authentik.js.map +1 -0
  19. package/dist/backing-ssh.d.ts +8 -0
  20. package/dist/backing-ssh.d.ts.map +1 -0
  21. package/dist/backing-ssh.js +44 -0
  22. package/dist/backing-ssh.js.map +1 -0
  23. package/dist/backup-restore.d.ts +15 -0
  24. package/dist/backup-restore.d.ts.map +1 -0
  25. package/dist/backup-restore.js +49 -0
  26. package/dist/backup-restore.js.map +1 -0
  27. package/dist/backup.d.ts +4 -0
  28. package/dist/backup.d.ts.map +1 -0
  29. package/dist/backup.js +150 -0
  30. package/dist/backup.js.map +1 -0
  31. package/dist/cf-route.d.ts +5 -0
  32. package/dist/cf-route.d.ts.map +1 -0
  33. package/dist/cf-route.js +74 -0
  34. package/dist/cf-route.js.map +1 -0
  35. package/dist/ci.d.ts +4 -0
  36. package/dist/ci.d.ts.map +1 -0
  37. package/dist/ci.js +151 -0
  38. package/dist/ci.js.map +1 -0
  39. package/dist/cloudflare-api.d.ts +85 -0
  40. package/dist/cloudflare-api.d.ts.map +1 -0
  41. package/dist/cloudflare-api.js +110 -0
  42. package/dist/cloudflare-api.js.map +1 -0
  43. package/dist/cloudflare.d.ts +4 -0
  44. package/dist/cloudflare.d.ts.map +1 -0
  45. package/dist/cloudflare.js +29 -0
  46. package/dist/cloudflare.js.map +1 -0
  47. package/dist/deploy-hook.d.ts +4 -0
  48. package/dist/deploy-hook.d.ts.map +1 -0
  49. package/dist/deploy-hook.js +79 -0
  50. package/dist/deploy-hook.js.map +1 -0
  51. package/dist/deployment.d.ts +4 -0
  52. package/dist/deployment.d.ts.map +1 -0
  53. package/dist/deployment.js +103 -0
  54. package/dist/deployment.js.map +1 -0
  55. package/dist/discord-api.d.ts +31 -0
  56. package/dist/discord-api.d.ts.map +1 -0
  57. package/dist/discord-api.js +85 -0
  58. package/dist/discord-api.js.map +1 -0
  59. package/dist/discord.d.ts +4 -0
  60. package/dist/discord.d.ts.map +1 -0
  61. package/dist/discord.js +133 -0
  62. package/dist/discord.js.map +1 -0
  63. package/dist/forgejo-api.d.ts +209 -0
  64. package/dist/forgejo-api.d.ts.map +1 -0
  65. package/dist/forgejo-api.fake.d.ts +3 -0
  66. package/dist/forgejo-api.fake.d.ts.map +1 -0
  67. package/dist/forgejo-api.fake.js +31 -0
  68. package/dist/forgejo-api.fake.js.map +1 -0
  69. package/dist/forgejo-api.js +181 -0
  70. package/dist/forgejo-api.js.map +1 -0
  71. package/dist/forgejo-notify.d.ts +4 -0
  72. package/dist/forgejo-notify.d.ts.map +1 -0
  73. package/dist/forgejo-notify.js +103 -0
  74. package/dist/forgejo-notify.js.map +1 -0
  75. package/dist/forgejo-org.d.ts +4 -0
  76. package/dist/forgejo-org.d.ts.map +1 -0
  77. package/dist/forgejo-org.js +43 -0
  78. package/dist/forgejo-org.js.map +1 -0
  79. package/dist/forgejo-runner.d.ts +4 -0
  80. package/dist/forgejo-runner.d.ts.map +1 -0
  81. package/dist/forgejo-runner.js +111 -0
  82. package/dist/forgejo-runner.js.map +1 -0
  83. package/dist/forgejo-team.d.ts +4 -0
  84. package/dist/forgejo-team.d.ts.map +1 -0
  85. package/dist/forgejo-team.js +68 -0
  86. package/dist/forgejo-team.js.map +1 -0
  87. package/dist/forgejo-user.d.ts +4 -0
  88. package/dist/forgejo-user.d.ts.map +1 -0
  89. package/dist/forgejo-user.js +50 -0
  90. package/dist/forgejo-user.js.map +1 -0
  91. package/dist/forgejo.d.ts +4 -0
  92. package/dist/forgejo.d.ts.map +1 -0
  93. package/dist/forgejo.js +169 -0
  94. package/dist/forgejo.js.map +1 -0
  95. package/dist/garage-bucket.d.ts +4 -0
  96. package/dist/garage-bucket.d.ts.map +1 -0
  97. package/dist/garage-bucket.js +92 -0
  98. package/dist/garage-bucket.js.map +1 -0
  99. package/dist/garage.d.ts +4 -0
  100. package/dist/garage.d.ts.map +1 -0
  101. package/dist/garage.js +124 -0
  102. package/dist/garage.js.map +1 -0
  103. package/dist/gh-ci.d.ts +4 -0
  104. package/dist/gh-ci.d.ts.map +1 -0
  105. package/dist/gh-ci.js +153 -0
  106. package/dist/gh-ci.js.map +1 -0
  107. package/dist/gh-deployment.d.ts +4 -0
  108. package/dist/gh-deployment.d.ts.map +1 -0
  109. package/dist/gh-deployment.js +92 -0
  110. package/dist/gh-deployment.js.map +1 -0
  111. package/dist/gh-repo.d.ts +4 -0
  112. package/dist/gh-repo.d.ts.map +1 -0
  113. package/dist/gh-repo.js +52 -0
  114. package/dist/gh-repo.js.map +1 -0
  115. package/dist/github-api.d.ts +70 -0
  116. package/dist/github-api.d.ts.map +1 -0
  117. package/dist/github-api.js +120 -0
  118. package/dist/github-api.js.map +1 -0
  119. package/dist/github.d.ts +4 -0
  120. package/dist/github.d.ts.map +1 -0
  121. package/dist/github.js +34 -0
  122. package/dist/github.js.map +1 -0
  123. package/dist/guarded-update.d.ts +14 -0
  124. package/dist/guarded-update.d.ts.map +1 -0
  125. package/dist/guarded-update.js +25 -0
  126. package/dist/guarded-update.js.map +1 -0
  127. package/dist/host.d.ts +4 -0
  128. package/dist/host.d.ts.map +1 -0
  129. package/dist/host.js +48 -0
  130. package/dist/host.js.map +1 -0
  131. package/dist/index.d.ts +42 -0
  132. package/dist/index.d.ts.map +1 -0
  133. package/dist/index.js +35 -0
  134. package/dist/index.js.map +1 -0
  135. package/dist/inputs.d.ts +23 -0
  136. package/dist/inputs.d.ts.map +1 -0
  137. package/dist/inputs.js +34 -0
  138. package/dist/inputs.js.map +1 -0
  139. package/dist/komodo-api.d.ts +150 -0
  140. package/dist/komodo-api.d.ts.map +1 -0
  141. package/dist/komodo-api.js +104 -0
  142. package/dist/komodo-api.js.map +1 -0
  143. package/dist/komodo-notify.d.ts +4 -0
  144. package/dist/komodo-notify.d.ts.map +1 -0
  145. package/dist/komodo-notify.js +88 -0
  146. package/dist/komodo-notify.js.map +1 -0
  147. package/dist/komodo-periphery.d.ts +4 -0
  148. package/dist/komodo-periphery.d.ts.map +1 -0
  149. package/dist/komodo-periphery.js +92 -0
  150. package/dist/komodo-periphery.js.map +1 -0
  151. package/dist/komodo-server.d.ts +4 -0
  152. package/dist/komodo-server.d.ts.map +1 -0
  153. package/dist/komodo-server.js +54 -0
  154. package/dist/komodo-server.js.map +1 -0
  155. package/dist/komodo-user.d.ts +4 -0
  156. package/dist/komodo-user.d.ts.map +1 -0
  157. package/dist/komodo-user.js +66 -0
  158. package/dist/komodo-user.js.map +1 -0
  159. package/dist/komodo.d.ts +4 -0
  160. package/dist/komodo.d.ts.map +1 -0
  161. package/dist/komodo.js +222 -0
  162. package/dist/komodo.js.map +1 -0
  163. package/dist/postgres-database.d.ts +4 -0
  164. package/dist/postgres-database.d.ts.map +1 -0
  165. package/dist/postgres-database.js +88 -0
  166. package/dist/postgres-database.js.map +1 -0
  167. package/dist/postgres.d.ts +4 -0
  168. package/dist/postgres.d.ts.map +1 -0
  169. package/dist/postgres.js +95 -0
  170. package/dist/postgres.js.map +1 -0
  171. package/dist/providers.d.ts +21 -0
  172. package/dist/providers.d.ts.map +1 -0
  173. package/dist/providers.js +86 -0
  174. package/dist/providers.js.map +1 -0
  175. package/dist/repo.d.ts +4 -0
  176. package/dist/repo.d.ts.map +1 -0
  177. package/dist/repo.js +80 -0
  178. package/dist/repo.js.map +1 -0
  179. package/dist/signoz.d.ts +4 -0
  180. package/dist/signoz.d.ts.map +1 -0
  181. package/dist/signoz.js +386 -0
  182. package/dist/signoz.js.map +1 -0
  183. package/dist/ssh-probe.d.ts +5 -0
  184. package/dist/ssh-probe.d.ts.map +1 -0
  185. package/dist/ssh-probe.js +22 -0
  186. package/dist/ssh-probe.js.map +1 -0
  187. package/dist/ssh.d.ts +27 -0
  188. package/dist/ssh.d.ts.map +1 -0
  189. package/dist/ssh.js +79 -0
  190. package/dist/ssh.js.map +1 -0
  191. package/dist/tunnel.d.ts +5 -0
  192. package/dist/tunnel.d.ts.map +1 -0
  193. package/dist/tunnel.js +126 -0
  194. package/dist/tunnel.js.map +1 -0
  195. package/dist/valkey-namespace.d.ts +4 -0
  196. package/dist/valkey-namespace.d.ts.map +1 -0
  197. package/dist/valkey-namespace.js +78 -0
  198. package/dist/valkey-namespace.js.map +1 -0
  199. package/dist/valkey.d.ts +4 -0
  200. package/dist/valkey.d.ts.map +1 -0
  201. package/dist/valkey.js +94 -0
  202. package/dist/valkey.js.map +1 -0
  203. package/dist/workspace.d.ts +4 -0
  204. package/dist/workspace.d.ts.map +1 -0
  205. package/dist/workspace.js +97 -0
  206. package/dist/workspace.js.map +1 -0
  207. package/package.json +53 -0
package/dist/backup.js ADDED
@@ -0,0 +1,150 @@
1
+ import { z } from "zod";
2
+ import { parseInputs, sshSchema, sshTarget } from "./inputs.js";
3
+ import { sshExecutor } from "./ssh.js";
4
+ const backupSchema = sshSchema.extend({
5
+ repo: z.string(),
6
+ password: z.string(),
7
+ image: z.string(),
8
+ signoz: z.coerce.boolean().default(false),
9
+ credentials: z.record(z.string(), z.string()).default({}),
10
+ schedule: z.string().default("0 3 * * *"),
11
+ retention: z
12
+ .object({ daily: z.coerce.number().default(7), weekly: z.coerce.number().default(4), monthly: z.coerce.number().default(6) })
13
+ .default({ daily: 7, weekly: 4, monthly: 6 }),
14
+ });
15
+ const parse = (inputs) => parseInputs(backupSchema, inputs, "backup");
16
+ const CONTAINER = "intentic-backup";
17
+ const STATE_DIR = "/opt/intentic/backup";
18
+ const ENV_FILE = `${STATE_DIR}/restic.env`;
19
+ const SCRIPT_FILE = `${STATE_DIR}/backup.sh`;
20
+ const CRONTAB_FILE = `${STATE_DIR}/crontab`;
21
+ const SEP = "|";
22
+ const volumeMounts = (signoz) => ({
23
+ "intentic-forgejo-data": "/volumes/forgejo",
24
+ "komodo_postgres-data": "/volumes/komodo-postgres",
25
+ komodo_keys: "/volumes/komodo-keys",
26
+ "komodo_ferretdb-state": "/volumes/komodo-ferretdb",
27
+ ...(signoz ? { "signoz_clickhouse-data": "/volumes/signoz-clickhouse", "signoz_signoz-data": "/volumes/signoz-signoz" } : {}),
28
+ });
29
+ const backupScript = (parsed) => [
30
+ "#!/bin/sh",
31
+ "set -eu",
32
+ "STAGING=/staging",
33
+ 'rm -rf "$STAGING"; mkdir -p "$STAGING"',
34
+ "# Forgejo: app-consistent dump (as the git user) copied out over the docker socket.",
35
+ "if docker exec -u git intentic-forgejo forgejo dump --type tar --file /tmp/intentic-forgejo.tar >/dev/null 2>&1; then",
36
+ ' docker cp intentic-forgejo:/tmp/intentic-forgejo.tar "$STAGING/forgejo-dump.tar"',
37
+ " docker exec intentic-forgejo rm -f /tmp/intentic-forgejo.tar",
38
+ 'else echo "forgejo dump skipped"; fi',
39
+ "# Komodo: logical pg_dump of the FerretDB-backing postgres (matched by its compose labels).",
40
+ "PG=$(docker ps -q -f label=com.docker.compose.project=komodo -f label=com.docker.compose.service=postgres)",
41
+ 'if [ -n "$PG" ]; then docker exec "$PG" pg_dump -U komodo -d postgres > "$STAGING/komodo.sql"; else echo "komodo pg_dump skipped"; fi',
42
+ `restic -r "${parsed.repo}" backup "$STAGING" /volumes /host-opt-intentic`,
43
+ `restic -r "${parsed.repo}" forget --keep-daily ${parsed.retention.daily} --keep-weekly ${parsed.retention.weekly} --keep-monthly ${parsed.retention.monthly} --prune`,
44
+ "",
45
+ ].join("\n");
46
+ const running = async (session) => {
47
+ const result = await session.exec(`docker ps --filter "name=^${CONTAINER}$" --format '{{.Names}}'`);
48
+ return result.stdout.trim() === CONTAINER;
49
+ };
50
+ const observe = async (session) => {
51
+ const result = await session.exec(`docker inspect --format '{{.Config.Image}}${SEP}{{index .Config.Labels "intentic.schedule"}}${SEP}{{index .Config.Labels "intentic.repo"}}' ${CONTAINER} 2>/dev/null || true`);
52
+ const [image = "", schedule = "", repo = ""] = result.stdout.trim().split(SEP);
53
+ return { image, schedule, repo };
54
+ };
55
+ const ensureFiles = async (session, parsed) => {
56
+ await session.exec(`mkdir -p ${STATE_DIR}`);
57
+ const envLines = [`RESTIC_PASSWORD=${parsed.password}`, ...Object.entries(parsed.credentials).map(([key, value]) => `${key}=${value}`)]
58
+ .map((line) => `'${line}'`)
59
+ .join(" ");
60
+ await session.exec(`test -f ${ENV_FILE} || { printf '%s\\n' ${envLines} > ${ENV_FILE} && chmod 600 ${ENV_FILE}; }`);
61
+ await session.exec(`cat > ${SCRIPT_FILE} <<'BACKUP_EOF'\n${backupScript(parsed)}BACKUP_EOF`);
62
+ await session.exec(`chmod +x ${SCRIPT_FILE}`);
63
+ await session.exec(`cat > ${CRONTAB_FILE} <<'CRON_EOF'\n${parsed.schedule} /bin/sh ${SCRIPT_FILE}\nCRON_EOF`);
64
+ };
65
+ const mountArgs = (parsed, dockerBin) => {
66
+ const volumes = Object.entries(volumeMounts(parsed.signoz))
67
+ .map(([name, path]) => `-v ${name}:${path}:ro`)
68
+ .join(" ");
69
+ return [
70
+ "-v /var/run/docker.sock:/var/run/docker.sock",
71
+ `-v ${dockerBin}:/usr/local/bin/docker:ro`,
72
+ volumes,
73
+ "-v /opt/intentic:/host-opt-intentic:ro",
74
+ `-v ${SCRIPT_FILE}:/backup.sh:ro`,
75
+ `-v ${CRONTAB_FILE}:/etc/crontabs/root:ro`,
76
+ `--env-file ${ENV_FILE}`,
77
+ ].join(" ");
78
+ };
79
+ export const createBackupProvider = (executor = sshExecutor) => ({
80
+ read: async (inputs, ctx) => {
81
+ const parsed = parse(inputs);
82
+ let session;
83
+ try {
84
+ session = await executor.connect(sshTarget(parsed));
85
+ }
86
+ catch (error) {
87
+ ctx.log(`backup "${ctx.id}": host not reachable over SSH, treating as not-yet-created: ${String(error)}`);
88
+ return undefined;
89
+ }
90
+ try {
91
+ if (!(await running(session))) {
92
+ return undefined;
93
+ }
94
+ const observed = await observe(session);
95
+ return { outputs: {}, detail: observed };
96
+ }
97
+ finally {
98
+ await session.dispose();
99
+ }
100
+ },
101
+ diff: (inputs, observed) => {
102
+ const parsed = parse(inputs);
103
+ const detail = observed.detail;
104
+ if (detail?.["image"] !== parsed.image) {
105
+ return { action: "update", reason: `backup image differs (running ${String(detail?.["image"])}, want ${parsed.image})` };
106
+ }
107
+ if (detail["schedule"] !== parsed.schedule) {
108
+ return { action: "update", reason: `backup schedule differs (running ${String(detail["schedule"])}, want ${parsed.schedule})` };
109
+ }
110
+ if (detail["repo"] !== parsed.repo) {
111
+ return { action: "update", reason: `backup repo differs (running ${String(detail["repo"])}, want ${parsed.repo})` };
112
+ }
113
+ return { action: "noop" };
114
+ },
115
+ apply: async (inputs, _observed, ctx) => {
116
+ const parsed = parse(inputs);
117
+ const session = await executor.connect(sshTarget(parsed));
118
+ try {
119
+ const dockerBin = (await session.exec("command -v docker")).stdout.trim();
120
+ if (dockerBin === "") {
121
+ throw new Error("backup: no docker CLI found on the host (the backup container needs it for app-consistent dumps)");
122
+ }
123
+ await ensureFiles(session, parsed);
124
+ await session.exec(`docker rm -f ${CONTAINER} 2>/dev/null || true`);
125
+ const run = await session.exec(`docker run -d --restart unless-stopped --name ${CONTAINER} --label intentic.id=${ctx.id} ` +
126
+ `--label "intentic.schedule=${parsed.schedule}" --label "intentic.repo=${parsed.repo}" ` +
127
+ `${mountArgs(parsed, dockerBin)} --entrypoint crond ${parsed.image} -f -l 8`);
128
+ if (run.code !== 0) {
129
+ throw new Error(`failed to start backup container on host: exited ${run.code}: ${run.stderr.trim()}`);
130
+ }
131
+ return {};
132
+ }
133
+ finally {
134
+ await session.dispose();
135
+ }
136
+ },
137
+ delete: async (inputs, ctx) => {
138
+ const parsed = parse(inputs);
139
+ const session = await executor.connect(sshTarget(parsed));
140
+ try {
141
+ await session.exec(`docker rm -f ${CONTAINER} 2>/dev/null || true`);
142
+ await session.exec(`rm -rf ${STATE_DIR}`);
143
+ ctx.log(`backup "${ctx.id}" removed; the restic repo and its snapshots are left untouched`);
144
+ }
145
+ finally {
146
+ await session.dispose();
147
+ }
148
+ },
149
+ });
150
+ //# sourceMappingURL=backup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backup.js","sourceRoot":"","sources":["../src/backup.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { Provider } from "@intentic/engine";
2
+ import type { CloudflareApi } from "./cloudflare-api.js";
3
+ export type DnsPropagationWait = (hostname: string, log: (message: string) => void) => Promise<void>;
4
+ export declare const createCfRouteProvider: (api?: CloudflareApi, awaitPropagation?: DnsPropagationWait) => Provider;
5
+ //# sourceMappingURL=cf-route.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cf-route.d.ts","sourceRoot":"","sources":["../src/cf-route.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAkB,MAAM,kBAAkB,CAAC;AAGjE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAczD,MAAM,MAAM,kBAAkB,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAoCrG,eAAO,MAAM,qBAAqB,GAC9B,MAAK,aAA6B,EAClC,mBAAkB,kBAA0C,KAC7D,QAqCD,CAAC"}
@@ -0,0 +1,74 @@
1
+ import { formatStamp } from "@intentic/graph";
2
+ import { z } from "zod";
3
+ import { cloudflareApi } from "./cloudflare-api.js";
4
+ import { parseInputs } from "./inputs.js";
5
+ const cfRouteSchema = z.object({ hostname: z.string(), zoneId: z.string(), apiToken: z.string(), cname: z.string() });
6
+ const parse = (inputs) => parseInputs(cfRouteSchema, inputs, "cf-route");
7
+ const dohResolves = async (hostname) => {
8
+ try {
9
+ const response = await fetch(`https://cloudflare-dns.com/dns-query?name=${encodeURIComponent(hostname)}&type=A`, {
10
+ headers: { accept: "application/dns-json" },
11
+ });
12
+ if (!response.ok) {
13
+ return false;
14
+ }
15
+ const body = (await response.json());
16
+ return body.Status === 0 && Array.isArray(body.Answer) && body.Answer.length > 0;
17
+ }
18
+ catch {
19
+ return false;
20
+ }
21
+ };
22
+ const waitForDnsPropagation = async (hostname, log) => {
23
+ const deadline = Date.now() + 90_000;
24
+ for (;;) {
25
+ if (await dohResolves(hostname)) {
26
+ return;
27
+ }
28
+ if (Date.now() >= deadline) {
29
+ log(`cf-route: ${hostname} not yet observable via DoH after 90s; proceeding (downstream calls may need a retry)`);
30
+ return;
31
+ }
32
+ await new Promise((resolve) => setTimeout(resolve, 2000));
33
+ }
34
+ };
35
+ export const createCfRouteProvider = (api = cloudflareApi, awaitPropagation = waitForDnsPropagation) => ({
36
+ read: async (inputs) => {
37
+ const { hostname, zoneId, apiToken } = parse(inputs);
38
+ const record = await api.findDnsRecord({ apiToken, zoneId, name: hostname });
39
+ if (record === undefined) {
40
+ return undefined;
41
+ }
42
+ return { outputs: { url: `https://${hostname}` }, detail: { content: record.content } };
43
+ },
44
+ diff: (inputs, observed) => {
45
+ const { cname } = parse(inputs);
46
+ const content = observed.detail?.["content"];
47
+ if (content === cname) {
48
+ return { action: "noop" };
49
+ }
50
+ return { action: "update", reason: `CNAME target "${String(content)}" differs from "${cname}"` };
51
+ },
52
+ apply: async (inputs, _observed, ctx) => {
53
+ const { hostname, zoneId, apiToken, cname } = parse(inputs);
54
+ const comment = formatStamp(ctx.id);
55
+ const record = await api.findDnsRecord({ apiToken, zoneId, name: hostname });
56
+ if (record === undefined) {
57
+ await api.createDnsRecord({ apiToken, zoneId, name: hostname, content: cname, comment });
58
+ }
59
+ else {
60
+ await api.updateDnsRecord({ apiToken, zoneId, recordId: record.id, name: hostname, content: cname, comment });
61
+ }
62
+ await awaitPropagation(hostname, ctx.log);
63
+ return { url: `https://${hostname}` };
64
+ },
65
+ delete: async (inputs) => {
66
+ const { hostname, zoneId, apiToken } = parse(inputs);
67
+ const record = await api.findDnsRecord({ apiToken, zoneId, name: hostname });
68
+ if (record === undefined) {
69
+ return;
70
+ }
71
+ await api.deleteDnsRecord({ apiToken, zoneId, recordId: record.id });
72
+ },
73
+ });
74
+ //# sourceMappingURL=cf-route.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cf-route.js","sourceRoot":"","sources":["../src/cf-route.ts"],"names":[],"mappings":"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"}
package/dist/ci.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ import type { Provider } from "@intentic/engine";
2
+ import type { ForgejoApi } from "./forgejo-api.js";
3
+ export declare const createCiProvider: (api?: ForgejoApi) => Provider;
4
+ //# sourceMappingURL=ci.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ci.d.ts","sourceRoot":"","sources":["../src/ci.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAkB,MAAM,kBAAkB,CAAC;AAEjE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAsGnD,eAAO,MAAM,gBAAgB,GAAI,MAAK,UAAuB,KAAG,QAoF9D,CAAC"}
package/dist/ci.js ADDED
@@ -0,0 +1,151 @@
1
+ import { z } from "zod";
2
+ import { forgejoApi } from "./forgejo-api.js";
3
+ import { parseInputs } from "./inputs.js";
4
+ const ciSchema = z.object({
5
+ forgejoUrl: z.string(),
6
+ adminUser: z.string(),
7
+ adminPassword: z.string(),
8
+ komodoPassword: z.string(),
9
+ owner: z.string(),
10
+ repoName: z.string(),
11
+ branch: z.string(),
12
+ registry: z.string(),
13
+ tag: z.string(),
14
+ packagesToken: z.string(),
15
+ komodoUrl: z.string(),
16
+ deployment: z.string(),
17
+ });
18
+ const parse = (inputs) => parseInputs(ciSchema, inputs, "ci");
19
+ const SECRET_REGISTRY = "REGISTRY_TOKEN";
20
+ const SECRET_KOMODO = "KOMODO_PASSWORD";
21
+ const workflowPath = (tag) => `.forgejo/workflows/build-${tag}.yaml`;
22
+ const DOCKERFILE_PATH = "Dockerfile";
23
+ const starterDockerfile = () => [
24
+ "# intentic starter Dockerfile — replace with your app's real build.",
25
+ "FROM busybox",
26
+ "RUN mkdir -p /www && printf '%s' 'intentic: replace this Dockerfile with your app' > /www/index.html",
27
+ "ENV PORT=8080",
28
+ "EXPOSE 8080",
29
+ 'CMD ["sh","-c","httpd -f -v -p $PORT -h /www"]',
30
+ "",
31
+ ].join("\n");
32
+ const workflowYaml = (parsed) => {
33
+ const base = `${parsed.registry}/${parsed.owner}/${parsed.repoName}`;
34
+ return [
35
+ "# Generated by intentic: build the image, push it to the Forgejo registry, notify Komodo to redeploy.",
36
+ "on:",
37
+ " push:",
38
+ ` branches: [ "${parsed.branch}" ]`,
39
+ "jobs:",
40
+ " build:",
41
+ " runs-on: docker",
42
+ " steps:",
43
+ " - uses: https://github.com/actions/checkout@v4",
44
+ " - uses: https://github.com/docker/login-action@v3",
45
+ " with:",
46
+ ` registry: ${parsed.registry}`,
47
+ ` username: ${parsed.adminUser}`,
48
+ ` password: \${{ secrets.${SECRET_REGISTRY} }}`,
49
+ " - uses: https://github.com/docker/build-push-action@v6",
50
+ " with:",
51
+ " context: .",
52
+ " push: true",
53
+ " tags: |",
54
+ ` ${base}:${parsed.tag}`,
55
+ ` ${base}:\${{ github.sha }}`,
56
+ " - name: notify komodo to redeploy",
57
+ " run: |",
58
+ ` KOMODO_PASSWORD='\${{ secrets.${SECRET_KOMODO} }}'`,
59
+ ` JWT=$(curl -sf ${parsed.komodoUrl}/auth/login/LoginLocalUser -H 'Content-Type: application/json' \\`,
60
+ ` -d '{"username":"${parsed.adminUser}","password":"'"$KOMODO_PASSWORD"'"}' | sed -n 's/.*"jwt":"\\([^"]*\\)".*/\\1/p')`,
61
+ ` curl -sf ${parsed.komodoUrl}/execute -H "Authorization: $JWT" -H 'Content-Type: application/json' \\`,
62
+ ` -d '{"type":"Deploy","params":{"deployment":"${parsed.deployment}"}}'`,
63
+ "",
64
+ ].join("\n");
65
+ };
66
+ const normalize = (yaml) => yaml
67
+ .split("\n")
68
+ .map((line) => line.replace(/\s+$/, ""))
69
+ .join("\n")
70
+ .trim();
71
+ export const createCiProvider = (api = forgejoApi) => ({
72
+ read: async (inputs, ctx) => {
73
+ if (typeof inputs["forgejoUrl"] !== "string" || typeof inputs["komodoUrl"] !== "string") {
74
+ return undefined;
75
+ }
76
+ const parsed = parse(inputs);
77
+ try {
78
+ const workflow = await api.readFile({
79
+ baseUrl: parsed.forgejoUrl,
80
+ user: parsed.adminUser,
81
+ password: parsed.adminPassword,
82
+ owner: parsed.owner,
83
+ name: parsed.repoName,
84
+ branch: parsed.branch,
85
+ path: workflowPath(parsed.tag),
86
+ });
87
+ if (workflow === undefined) {
88
+ return undefined;
89
+ }
90
+ return { outputs: {}, detail: { workflow } };
91
+ }
92
+ catch (error) {
93
+ ctx.log(`ci "${ctx.id}": forgejo not reachable yet, treating as not-yet-created: ${String(error)}`);
94
+ return undefined;
95
+ }
96
+ },
97
+ diff: (inputs, observed) => {
98
+ const workflow = observed.detail?.["workflow"];
99
+ if (typeof workflow !== "string" || normalize(workflow) !== normalize(workflowYaml(parse(inputs)))) {
100
+ return { action: "update", reason: "ci workflow differs from desired" };
101
+ }
102
+ return { action: "noop" };
103
+ },
104
+ apply: async (inputs) => {
105
+ const parsed = parse(inputs);
106
+ const repo = {
107
+ baseUrl: parsed.forgejoUrl,
108
+ user: parsed.adminUser,
109
+ password: parsed.adminPassword,
110
+ owner: parsed.owner,
111
+ name: parsed.repoName,
112
+ };
113
+ await api.setRepoSecret({ ...repo, secretName: SECRET_REGISTRY, data: parsed.packagesToken });
114
+ await api.setRepoSecret({ ...repo, secretName: SECRET_KOMODO, data: parsed.komodoPassword });
115
+ const dockerfile = await api.readFile({ ...repo, branch: parsed.branch, path: DOCKERFILE_PATH });
116
+ if (dockerfile === undefined) {
117
+ await api.commitFile({
118
+ ...repo,
119
+ branch: parsed.branch,
120
+ path: DOCKERFILE_PATH,
121
+ content: starterDockerfile(),
122
+ message: "intentic: starter Dockerfile",
123
+ });
124
+ }
125
+ await api.commitFile({
126
+ ...repo,
127
+ branch: parsed.branch,
128
+ path: workflowPath(parsed.tag),
129
+ content: workflowYaml(parsed),
130
+ message: "intentic: ci workflow",
131
+ });
132
+ return {};
133
+ },
134
+ delete: async (inputs) => {
135
+ if (typeof inputs["forgejoUrl"] !== "string") {
136
+ return;
137
+ }
138
+ const parsed = parse(inputs);
139
+ const repo = {
140
+ baseUrl: parsed.forgejoUrl,
141
+ user: parsed.adminUser,
142
+ password: parsed.adminPassword,
143
+ owner: parsed.owner,
144
+ name: parsed.repoName,
145
+ };
146
+ await api.deleteFile({ ...repo, branch: parsed.branch, path: workflowPath(parsed.tag), message: "intentic: remove ci workflow" });
147
+ await api.deleteRepoSecret({ ...repo, secretName: SECRET_REGISTRY });
148
+ await api.deleteRepoSecret({ ...repo, secretName: SECRET_KOMODO });
149
+ },
150
+ });
151
+ //# sourceMappingURL=ci.js.map
package/dist/ci.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ci.js","sourceRoot":"","sources":["../src/ci.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,85 @@
1
+ export interface IngressRule {
2
+ readonly hostname?: string;
3
+ readonly service: string;
4
+ }
5
+ export interface CloudflareApi {
6
+ readonly getZone: (args: {
7
+ readonly apiToken: string;
8
+ readonly zone: string;
9
+ }) => Promise<{
10
+ readonly id: string;
11
+ readonly accountId: string;
12
+ } | undefined>;
13
+ readonly listZones: (args: {
14
+ readonly apiToken: string;
15
+ }) => Promise<{
16
+ readonly id: string;
17
+ readonly name: string;
18
+ readonly accountId: string;
19
+ }[]>;
20
+ readonly findTunnel: (args: {
21
+ readonly accountId: string;
22
+ readonly apiToken: string;
23
+ readonly name: string;
24
+ }) => Promise<{
25
+ readonly id: string;
26
+ } | undefined>;
27
+ readonly createTunnel: (args: {
28
+ readonly accountId: string;
29
+ readonly apiToken: string;
30
+ readonly name: string;
31
+ }) => Promise<{
32
+ readonly id: string;
33
+ }>;
34
+ readonly getTunnelToken: (args: {
35
+ readonly accountId: string;
36
+ readonly apiToken: string;
37
+ readonly tunnelId: string;
38
+ }) => Promise<string>;
39
+ readonly getTunnelIngress: (args: {
40
+ readonly accountId: string;
41
+ readonly apiToken: string;
42
+ readonly tunnelId: string;
43
+ }) => Promise<IngressRule[] | undefined>;
44
+ readonly putTunnelIngress: (args: {
45
+ readonly accountId: string;
46
+ readonly apiToken: string;
47
+ readonly tunnelId: string;
48
+ readonly ingress: readonly IngressRule[];
49
+ }) => Promise<void>;
50
+ readonly findDnsRecord: (args: {
51
+ readonly apiToken: string;
52
+ readonly zoneId: string;
53
+ readonly name: string;
54
+ }) => Promise<{
55
+ readonly id: string;
56
+ readonly content: string;
57
+ } | undefined>;
58
+ readonly createDnsRecord: (args: {
59
+ readonly apiToken: string;
60
+ readonly zoneId: string;
61
+ readonly name: string;
62
+ readonly content: string;
63
+ readonly comment: string;
64
+ }) => Promise<void>;
65
+ readonly updateDnsRecord: (args: {
66
+ readonly apiToken: string;
67
+ readonly zoneId: string;
68
+ readonly recordId: string;
69
+ readonly name: string;
70
+ readonly content: string;
71
+ readonly comment: string;
72
+ }) => Promise<void>;
73
+ readonly deleteTunnel: (args: {
74
+ readonly accountId: string;
75
+ readonly apiToken: string;
76
+ readonly tunnelId: string;
77
+ }) => Promise<void>;
78
+ readonly deleteDnsRecord: (args: {
79
+ readonly apiToken: string;
80
+ readonly zoneId: string;
81
+ readonly recordId: string;
82
+ }) => Promise<void>;
83
+ }
84
+ export declare const cloudflareApi: CloudflareApi;
85
+ //# sourceMappingURL=cloudflare-api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloudflare-api.d.ts","sourceRoot":"","sources":["../src/cloudflare-api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,110 @@
1
+ import { z } from "zod";
2
+ import { parseResponse } from "./inputs.js";
3
+ const ingressRuleSchema = z.object({ hostname: z.string().optional(), service: z.string() });
4
+ const BASE = "https://api.cloudflare.com/client/v4";
5
+ const envelopeSchema = z.object({
6
+ success: z.boolean(),
7
+ errors: z.array(z.object({ code: z.number(), message: z.string() })),
8
+ result: z.unknown(),
9
+ result_info: z.unknown().optional(),
10
+ });
11
+ const request = async (apiToken, path, init) => {
12
+ const label = `Cloudflare API ${init?.method ?? "GET"} ${path}`;
13
+ const response = await fetch(`${BASE}${path}`, {
14
+ ...init,
15
+ headers: {
16
+ Authorization: `Bearer ${apiToken}`,
17
+ ...(init?.body !== undefined ? { "Content-Type": "application/json" } : {}),
18
+ },
19
+ });
20
+ const envelope = parseResponse(envelopeSchema, await response.json(), label);
21
+ if (!response.ok || !envelope.success) {
22
+ const detail = envelope.errors.map((error) => `${error.code} ${error.message}`).join("; ");
23
+ throw new Error(`${label} failed (HTTP ${response.status}): ${detail}`);
24
+ }
25
+ return envelope;
26
+ };
27
+ const call = async (apiToken, path, resultSchema, init) => {
28
+ const envelope = await request(apiToken, path, init);
29
+ return parseResponse(resultSchema, envelope.result, `Cloudflare API ${init?.method ?? "GET"} ${path}`);
30
+ };
31
+ export const cloudflareApi = {
32
+ getZone: async ({ apiToken, zone }) => {
33
+ const zones = await call(apiToken, `/zones?name=${encodeURIComponent(zone)}`, z.array(z.object({ id: z.string(), account: z.object({ id: z.string() }) })));
34
+ const found = zones[0];
35
+ if (found === undefined) {
36
+ return undefined;
37
+ }
38
+ return { id: found.id, accountId: found.account.id };
39
+ },
40
+ listZones: async ({ apiToken }) => {
41
+ const zones = [];
42
+ let page = 1;
43
+ let totalPages = 1;
44
+ do {
45
+ const envelope = await request(apiToken, `/zones?per_page=50&page=${page}`);
46
+ const parsed = parseResponse(z.array(z.object({ id: z.string(), name: z.string(), account: z.object({ id: z.string() }) })), envelope.result, `Cloudflare API GET /zones?per_page=50&page=${page}`);
47
+ for (const zone of parsed) {
48
+ zones.push({ id: zone.id, name: zone.name, accountId: zone.account.id });
49
+ }
50
+ totalPages = z.object({ total_pages: z.number() }).safeParse(envelope.result_info).data?.total_pages ?? 1;
51
+ page += 1;
52
+ } while (page <= totalPages);
53
+ return zones;
54
+ },
55
+ findTunnel: async ({ accountId, apiToken, name }) => {
56
+ const tunnels = await call(apiToken, `/accounts/${encodeURIComponent(accountId)}/cfd_tunnel?name=${encodeURIComponent(name)}&is_deleted=false`, z.array(z.object({ id: z.string() })));
57
+ const found = tunnels[0];
58
+ if (found === undefined) {
59
+ return undefined;
60
+ }
61
+ return { id: found.id };
62
+ },
63
+ createTunnel: ({ accountId, apiToken, name }) => call(apiToken, `/accounts/${encodeURIComponent(accountId)}/cfd_tunnel`, z.object({ id: z.string() }), {
64
+ method: "POST",
65
+ body: JSON.stringify({ name, config_src: "cloudflare" }),
66
+ }),
67
+ getTunnelToken: ({ accountId, apiToken, tunnelId }) => call(apiToken, `/accounts/${encodeURIComponent(accountId)}/cfd_tunnel/${encodeURIComponent(tunnelId)}/token`, z.string()),
68
+ getTunnelIngress: async ({ accountId, apiToken, tunnelId }) => {
69
+ const config = await call(apiToken, `/accounts/${encodeURIComponent(accountId)}/cfd_tunnel/${encodeURIComponent(tunnelId)}/configurations`, z.object({ config: z.object({ ingress: z.array(ingressRuleSchema).optional() }).optional() }).nullable());
70
+ const ingress = config?.config?.ingress;
71
+ return ingress?.map((rule) => (rule.hostname === undefined ? { service: rule.service } : { hostname: rule.hostname, service: rule.service }));
72
+ },
73
+ putTunnelIngress: async ({ accountId, apiToken, tunnelId, ingress }) => {
74
+ await call(apiToken, `/accounts/${encodeURIComponent(accountId)}/cfd_tunnel/${encodeURIComponent(tunnelId)}/configurations`, z.unknown(), {
75
+ method: "PUT",
76
+ body: JSON.stringify({ config: { ingress } }),
77
+ });
78
+ },
79
+ findDnsRecord: async ({ apiToken, zoneId, name }) => {
80
+ const records = await call(apiToken, `/zones/${encodeURIComponent(zoneId)}/dns_records?type=CNAME&name=${encodeURIComponent(name)}`, z.array(z.object({ id: z.string(), content: z.string() })));
81
+ const found = records[0];
82
+ if (found === undefined) {
83
+ return undefined;
84
+ }
85
+ return { id: found.id, content: found.content };
86
+ },
87
+ createDnsRecord: async ({ apiToken, zoneId, name, content, comment }) => {
88
+ await call(apiToken, `/zones/${encodeURIComponent(zoneId)}/dns_records`, z.unknown(), {
89
+ method: "POST",
90
+ body: JSON.stringify({ type: "CNAME", name, content, proxied: true, comment }),
91
+ });
92
+ },
93
+ updateDnsRecord: async ({ apiToken, zoneId, recordId, name, content, comment }) => {
94
+ await call(apiToken, `/zones/${encodeURIComponent(zoneId)}/dns_records/${encodeURIComponent(recordId)}`, z.unknown(), {
95
+ method: "PUT",
96
+ body: JSON.stringify({ type: "CNAME", name, content, proxied: true, comment }),
97
+ });
98
+ },
99
+ deleteTunnel: async ({ accountId, apiToken, tunnelId }) => {
100
+ await call(apiToken, `/accounts/${encodeURIComponent(accountId)}/cfd_tunnel/${encodeURIComponent(tunnelId)}`, z.unknown(), {
101
+ method: "DELETE",
102
+ });
103
+ },
104
+ deleteDnsRecord: async ({ apiToken, zoneId, recordId }) => {
105
+ await call(apiToken, `/zones/${encodeURIComponent(zoneId)}/dns_records/${encodeURIComponent(recordId)}`, z.unknown(), {
106
+ method: "DELETE",
107
+ });
108
+ },
109
+ };
110
+ //# sourceMappingURL=cloudflare-api.js.map