@kindgi/cli 0.1.6-rc.0 → 0.1.7-rc.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 (220) hide show
  1. package/README.md +23 -12
  2. package/dist/commands/dev.d.ts.map +1 -1
  3. package/dist/commands/dev.js +84 -13
  4. package/dist/commands/dev.js.map +1 -1
  5. package/dist/commands/doctor.d.ts +5 -1
  6. package/dist/commands/doctor.d.ts.map +1 -1
  7. package/dist/commands/doctor.js +102 -18
  8. package/dist/commands/doctor.js.map +1 -1
  9. package/dist/commands/guardrails.d.ts.map +1 -1
  10. package/dist/commands/guardrails.js +13 -2
  11. package/dist/commands/guardrails.js.map +1 -1
  12. package/dist/commands/index.d.ts.map +1 -1
  13. package/dist/commands/index.js +4 -0
  14. package/dist/commands/index.js.map +1 -1
  15. package/dist/commands/init.d.ts +1 -1
  16. package/dist/commands/init.d.ts.map +1 -1
  17. package/dist/commands/init.js +23 -3
  18. package/dist/commands/init.js.map +1 -1
  19. package/dist/commands/license.d.ts +3 -0
  20. package/dist/commands/license.d.ts.map +1 -0
  21. package/dist/commands/license.js +187 -0
  22. package/dist/commands/license.js.map +1 -0
  23. package/dist/commands/mcp.js +1 -1
  24. package/dist/commands/providers.d.ts.map +1 -1
  25. package/dist/commands/providers.js +11 -9
  26. package/dist/commands/providers.js.map +1 -1
  27. package/dist/commands/test.d.ts.map +1 -1
  28. package/dist/commands/test.js +21 -9
  29. package/dist/commands/test.js.map +1 -1
  30. package/dist/commands/webhooks.d.ts +20 -0
  31. package/dist/commands/webhooks.d.ts.map +1 -0
  32. package/dist/commands/webhooks.js +444 -0
  33. package/dist/commands/webhooks.js.map +1 -0
  34. package/dist/context.d.ts +4 -0
  35. package/dist/context.d.ts.map +1 -1
  36. package/dist/context.js +1 -0
  37. package/dist/context.js.map +1 -1
  38. package/dist/dev/already-running.d.ts +22 -0
  39. package/dist/dev/already-running.d.ts.map +1 -0
  40. package/dist/dev/already-running.js +72 -0
  41. package/dist/dev/already-running.js.map +1 -0
  42. package/dist/dev/defaults.d.ts.map +1 -1
  43. package/dist/dev/defaults.js +5 -2
  44. package/dist/dev/defaults.js.map +1 -1
  45. package/dist/dev/dev-lock.d.ts +50 -0
  46. package/dist/dev/dev-lock.d.ts.map +1 -0
  47. package/dist/dev/dev-lock.js +203 -0
  48. package/dist/dev/dev-lock.js.map +1 -0
  49. package/dist/dev/port.d.ts +6 -2
  50. package/dist/dev/port.d.ts.map +1 -1
  51. package/dist/dev/port.js +8 -4
  52. package/dist/dev/port.js.map +1 -1
  53. package/dist/dev/providers.d.ts.map +1 -1
  54. package/dist/dev/providers.js +112 -23
  55. package/dist/dev/providers.js.map +1 -1
  56. package/dist/dev/runners.d.ts +21 -2
  57. package/dist/dev/runners.d.ts.map +1 -1
  58. package/dist/dev/runtime-container.d.ts +78 -3
  59. package/dist/dev/runtime-container.d.ts.map +1 -1
  60. package/dist/dev/runtime-container.js +256 -25
  61. package/dist/dev/runtime-container.js.map +1 -1
  62. package/dist/dev/runtime-image.d.ts +1 -1
  63. package/dist/dev/runtime-image.js +1 -1
  64. package/dist/dev/start-phases.d.ts +41 -0
  65. package/dist/dev/start-phases.d.ts.map +1 -0
  66. package/dist/dev/start-phases.js +103 -0
  67. package/dist/dev/start-phases.js.map +1 -0
  68. package/dist/help.js +1 -1
  69. package/dist/help.js.map +1 -1
  70. package/dist/init/augment-scaffolder.d.ts +20 -7
  71. package/dist/init/augment-scaffolder.d.ts.map +1 -1
  72. package/dist/init/augment-scaffolder.js +94 -21
  73. package/dist/init/augment-scaffolder.js.map +1 -1
  74. package/dist/init/pinning.d.ts +39 -0
  75. package/dist/init/pinning.d.ts.map +1 -0
  76. package/dist/init/pinning.js +130 -0
  77. package/dist/init/pinning.js.map +1 -0
  78. package/dist/init/template-files.d.ts +1 -1
  79. package/dist/init/tsconfig-paths.d.ts +29 -0
  80. package/dist/init/tsconfig-paths.d.ts.map +1 -0
  81. package/dist/init/tsconfig-paths.js +235 -0
  82. package/dist/init/tsconfig-paths.js.map +1 -0
  83. package/dist/init/vitest-setup.d.ts +41 -0
  84. package/dist/init/vitest-setup.d.ts.map +1 -0
  85. package/dist/init/vitest-setup.js +163 -0
  86. package/dist/init/vitest-setup.js.map +1 -0
  87. package/dist/license/aws.d.ts +24 -0
  88. package/dist/license/aws.d.ts.map +1 -0
  89. package/dist/license/aws.js +291 -0
  90. package/dist/license/aws.js.map +1 -0
  91. package/dist/license/cloud.d.ts +13 -0
  92. package/dist/license/cloud.d.ts.map +1 -0
  93. package/dist/license/cloud.js +193 -0
  94. package/dist/license/cloud.js.map +1 -0
  95. package/dist/license/expiry.d.ts +33 -0
  96. package/dist/license/expiry.d.ts.map +1 -0
  97. package/dist/license/expiry.js +35 -0
  98. package/dist/license/expiry.js.map +1 -0
  99. package/dist/license/license-key.d.ts +32 -0
  100. package/dist/license/license-key.d.ts.map +1 -0
  101. package/dist/license/license-key.js +91 -0
  102. package/dist/license/license-key.js.map +1 -0
  103. package/dist/license/protocol.d.ts +63 -0
  104. package/dist/license/protocol.d.ts.map +1 -0
  105. package/dist/license/protocol.js +102 -0
  106. package/dist/license/protocol.js.map +1 -0
  107. package/dist/license/renew.d.ts +59 -0
  108. package/dist/license/renew.d.ts.map +1 -0
  109. package/dist/license/renew.js +153 -0
  110. package/dist/license/renew.js.map +1 -0
  111. package/dist/license/stores.d.ts +19 -0
  112. package/dist/license/stores.d.ts.map +1 -0
  113. package/dist/license/stores.js +150 -0
  114. package/dist/license/stores.js.map +1 -0
  115. package/dist/main.d.ts +3 -0
  116. package/dist/main.d.ts.map +1 -1
  117. package/dist/main.js +1 -0
  118. package/dist/main.js.map +1 -1
  119. package/dist/package-manager.d.ts.map +1 -1
  120. package/dist/package-manager.js +2 -1
  121. package/dist/package-manager.js.map +1 -1
  122. package/dist/providers/preset-loader.d.ts +52 -1
  123. package/dist/providers/preset-loader.d.ts.map +1 -1
  124. package/dist/providers/preset-loader.js +85 -6
  125. package/dist/providers/preset-loader.js.map +1 -1
  126. package/dist/providers/presets/anthropic.json +1 -1
  127. package/dist/providers/presets/azure-openai.json +65 -0
  128. package/dist/providers/presets/bedrock.json +102 -0
  129. package/dist/sdk-skills/kindgi-authoring-agents/SKILL.md +3 -3
  130. package/dist/sdk-skills/kindgi-authoring-flows/SKILL.md +3 -3
  131. package/dist/sdk-skills/kindgi-authoring-guardrails/SKILL.md +11 -10
  132. package/dist/sdk-skills/kindgi-authoring-mcp-servers/SKILL.md +4 -4
  133. package/dist/sdk-skills/kindgi-authoring-providers/SKILL.md +16 -9
  134. package/dist/sdk-skills/kindgi-authoring-tools/SKILL.md +9 -11
  135. package/dist/sdk-skills/kindgi-framework-feedback/SKILL.md +3 -3
  136. package/dist/sdk-skills/kindgi-getting-started/SKILL.md +28 -5
  137. package/dist/sdk-skills/kindgi-java-authoring-agents/SKILL.md +1 -1
  138. package/dist/sdk-skills/kindgi-java-authoring-flows/SKILL.md +1 -1
  139. package/dist/sdk-skills/kindgi-java-authoring-guardrails/SKILL.md +1 -1
  140. package/dist/sdk-skills/kindgi-java-authoring-tools/SKILL.md +1 -1
  141. package/dist/sdk-skills/kindgi-java-getting-started/SKILL.md +6 -1
  142. package/dist/sdk-skills/kindgi-python-authoring-agents/SKILL.md +1 -1
  143. package/dist/sdk-skills/kindgi-python-authoring-flows/SKILL.md +1 -1
  144. package/dist/sdk-skills/kindgi-python-authoring-guardrails/SKILL.md +1 -1
  145. package/dist/sdk-skills/kindgi-python-authoring-tools/SKILL.md +1 -1
  146. package/dist/sdk-skills/kindgi-python-getting-started/SKILL.md +6 -1
  147. package/dist/sdk-skills/kindgi-scala-authoring-agents/SKILL.md +1 -1
  148. package/dist/sdk-skills/kindgi-scala-authoring-flows/SKILL.md +1 -1
  149. package/dist/sdk-skills/kindgi-scala-authoring-guardrails/SKILL.md +1 -1
  150. package/dist/sdk-skills/kindgi-scala-authoring-tools/SKILL.md +1 -1
  151. package/dist/sdk-skills/kindgi-scala-getting-started/SKILL.md +6 -1
  152. package/dist/sdk-skills/kindgi-using/SKILL.md +236 -0
  153. package/dist/templates/drupal/.nvmrc +1 -0
  154. package/dist/templates/drupal/AGENTS.md +62 -0
  155. package/dist/templates/drupal/README.md.tmpl +321 -0
  156. package/dist/templates/drupal/agents/content-agent/index.test.ts.tmpl +47 -0
  157. package/dist/templates/drupal/agents/content-agent/index.ts.tmpl +69 -0
  158. package/dist/templates/drupal/agents/content-reviewer/index.test.ts.tmpl +37 -0
  159. package/dist/templates/drupal/agents/content-reviewer/index.ts.tmpl +57 -0
  160. package/dist/templates/drupal/fixtures/node-updated.json +174 -0
  161. package/dist/templates/drupal/flows/content-review/index.ts.tmpl +66 -0
  162. package/dist/templates/drupal/gitignore +8 -0
  163. package/dist/templates/drupal/guardrails/no-contact-details/index.test.ts.tmpl +29 -0
  164. package/dist/templates/drupal/guardrails/no-contact-details/index.ts.tmpl +73 -0
  165. package/dist/templates/drupal/kindgi.config.ts.tmpl +41 -0
  166. package/dist/templates/drupal/lib/drupal.ts.tmpl +64 -0
  167. package/dist/templates/drupal/package.json.tmpl +31 -0
  168. package/dist/templates/drupal/pnpm-workspace.yaml +9 -0
  169. package/dist/templates/drupal/scripts/connect-drupal.mjs.tmpl +57 -0
  170. package/dist/templates/drupal/tools/check-content-event/index.test.ts.tmpl +72 -0
  171. package/dist/templates/drupal/tools/check-content-event/index.ts.tmpl +115 -0
  172. package/dist/templates/drupal/tsconfig.json.tmpl +28 -0
  173. package/dist/templates/drupal/vitest.config.ts.tmpl +14 -0
  174. package/dist/templates/java/AGENTS.md +6 -0
  175. package/dist/templates/java/README.md.tmpl +1 -1
  176. package/dist/templates/minimal/AGENTS.md +6 -0
  177. package/dist/templates/minimal/README.md.tmpl +3 -2
  178. package/dist/templates/minimal/package.json.tmpl +3 -2
  179. package/dist/templates/python/AGENTS.md +3 -2
  180. package/dist/templates/sample/AGENTS.md +6 -0
  181. package/dist/templates/sample/package.json.tmpl +3 -2
  182. package/dist/templates/scala/AGENTS.md +6 -0
  183. package/dist/templates/scala/README.md.tmpl +1 -1
  184. package/dist/templates/woocommerce/.nvmrc +1 -0
  185. package/dist/templates/woocommerce/AGENTS.md +49 -0
  186. package/dist/templates/woocommerce/README.md.tmpl +273 -0
  187. package/dist/templates/woocommerce/agents/order-reviewer/index.test.ts.tmpl +28 -0
  188. package/dist/templates/woocommerce/agents/order-reviewer/index.ts.tmpl +45 -0
  189. package/dist/templates/woocommerce/agents/store-agent/index.test.ts.tmpl +47 -0
  190. package/dist/templates/woocommerce/agents/store-agent/index.ts.tmpl +79 -0
  191. package/dist/templates/woocommerce/fixtures/order-created.json +67 -0
  192. package/dist/templates/woocommerce/flows/order-review/index.ts.tmpl +64 -0
  193. package/dist/templates/woocommerce/gitignore +8 -0
  194. package/dist/templates/woocommerce/guardrails/no-customer-contact-details/index.test.ts.tmpl +29 -0
  195. package/dist/templates/woocommerce/guardrails/no-customer-contact-details/index.ts.tmpl +74 -0
  196. package/dist/templates/woocommerce/kindgi.config.ts.tmpl +41 -0
  197. package/dist/templates/woocommerce/lib/fake-store.ts.tmpl +82 -0
  198. package/dist/templates/woocommerce/lib/woo.ts.tmpl +182 -0
  199. package/dist/templates/woocommerce/package.json.tmpl +30 -0
  200. package/dist/templates/woocommerce/pnpm-workspace.yaml +9 -0
  201. package/dist/templates/woocommerce/tools/check-order-event/index.test.ts.tmpl +76 -0
  202. package/dist/templates/woocommerce/tools/check-order-event/index.ts.tmpl +131 -0
  203. package/dist/templates/woocommerce/tools/find-orders/index.ts.tmpl +80 -0
  204. package/dist/templates/woocommerce/tools/publish-product/index.ts.tmpl +34 -0
  205. package/dist/templates/woocommerce/tools/refund/index.test.ts.tmpl +125 -0
  206. package/dist/templates/woocommerce/tools/refund/index.ts.tmpl +70 -0
  207. package/dist/templates/woocommerce/tools/refund-large/index.test.ts.tmpl +53 -0
  208. package/dist/templates/woocommerce/tools/refund-large/index.ts.tmpl +44 -0
  209. package/dist/templates/woocommerce/tools/set-price/index.ts.tmpl +36 -0
  210. package/dist/templates/woocommerce/tsconfig.json.tmpl +28 -0
  211. package/dist/templates/woocommerce/vitest.config.ts.tmpl +14 -0
  212. package/dist/test/defaults.d.ts +1 -1
  213. package/dist/test/defaults.d.ts.map +1 -1
  214. package/dist/test/defaults.js +2 -7
  215. package/dist/test/defaults.js.map +1 -1
  216. package/dist/test/runners.d.ts +6 -0
  217. package/dist/test/runners.d.ts.map +1 -1
  218. package/dist/test/runners.js +18 -1
  219. package/dist/test/runners.js.map +1 -1
  220. package/package.json +14 -14
@@ -0,0 +1,291 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (C) 2026 Kindgi Inc.
3
+ /**
4
+ * A secret in AWS Secrets Manager, for `kindgi license renew` and `enroll`:
5
+ * `aws:<region>:<secret name>`, or `aws:<the secret's ARN>`. Reads
6
+ * `AWSCURRENT` (`GetSecretValue`), writes a new version
7
+ * (`PutSecretValue`), signed with Signature Version 4.
8
+ *
9
+ * Credentials, the first that answers: the environment
10
+ * (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_SESSION_TOKEN`; what
11
+ * CI sets), a web identity (`AWS_ROLE_ARN` + `AWS_WEB_IDENTITY_TOKEN_FILE`:
12
+ * EKS IRSA, CI's OIDC), the container endpoint (ECS, EKS Pod Identity),
13
+ * the instance's role (IMDSv2 only), and on a laptop the AWS CLI's
14
+ * (`aws configure export-credentials`: profiles, SSO).
15
+ */
16
+ import { createHash, createHmac, randomUUID } from 'node:crypto';
17
+ import { readFile } from 'node:fs/promises';
18
+ import { headerValueProblem } from '@kindgi/types';
19
+ import { CloudSecretError } from './cloud.js';
20
+ const REGION = /^[a-z]{2,4}(?:-[a-z]+)+-\d{1,2}$/;
21
+ const ARN = /^arn:aws(?:-[a-z]+)*:secretsmanager:([a-z]{2,4}(?:-[a-z]+)+-\d{1,2}):\d{12}:secret:[A-Za-z0-9/_+=.@-]{1,512}$/;
22
+ const SECRET_NAME = /^[A-Za-z0-9/_+=.@-]{1,512}$/;
23
+ const METADATA_TIMEOUT_MS = 1500;
24
+ /** The DNS suffix of a region's partition: China's differs. */
25
+ const suffixOf = (region) => region.startsWith('cn-') ? 'amazonaws.com.cn' : 'amazonaws.com';
26
+ export function awsSecretStore(ref, deps) {
27
+ const arn = ARN.exec(ref);
28
+ let region;
29
+ let secretId;
30
+ if (arn !== null) {
31
+ region = arn[1] ?? '';
32
+ secretId = ref;
33
+ }
34
+ else {
35
+ const colon = ref.indexOf(':');
36
+ region = ref.slice(0, colon);
37
+ secretId = ref.slice(colon + 1);
38
+ if (colon === -1 || !REGION.test(region) || !SECRET_NAME.test(secretId))
39
+ return undefined;
40
+ }
41
+ const describe = `the Secrets Manager secret ${secretId} (${region})`;
42
+ // Found once per store: a renewal reads, then maybe writes.
43
+ let credentials;
44
+ const send = async (target, body) => {
45
+ credentials ??= awsCredentials(deps, region);
46
+ const host = `secretsmanager.${region}.${suffixOf(region)}`;
47
+ const payload = JSON.stringify(body);
48
+ const headers = signV4({
49
+ method: 'POST',
50
+ host,
51
+ path: '/',
52
+ headers: {
53
+ 'content-type': 'application/x-amz-json-1.1',
54
+ 'x-amz-target': `secretsmanager.${target}`,
55
+ },
56
+ body: payload,
57
+ region,
58
+ service: 'secretsmanager',
59
+ credentials: await credentials,
60
+ now: new Date(),
61
+ });
62
+ return deps.fetch(`https://${host}/`, {
63
+ method: 'POST',
64
+ headers,
65
+ body: payload,
66
+ signal: AbortSignal.timeout(30_000),
67
+ });
68
+ };
69
+ return {
70
+ async read() {
71
+ const res = await send('GetSecretValue', { SecretId: secretId });
72
+ if (!res.ok) {
73
+ const error = await awsError(res);
74
+ if (error.type === 'ResourceNotFoundException')
75
+ return undefined;
76
+ throw refused(describe, res.status, error, 'secretsmanager:GetSecretValue');
77
+ }
78
+ const value = (await res.json()).SecretString?.trim() ?? '';
79
+ return value === '' ? undefined : value;
80
+ },
81
+ async write(value) {
82
+ // A raw request names the new version itself: the SDKs and the AWS CLI
83
+ // add a UUID, and without one Secrets Manager answers 400
84
+ // (InvalidRequestException). It becomes the version's id.
85
+ const res = await send('PutSecretValue', {
86
+ SecretId: secretId,
87
+ SecretString: value,
88
+ ClientRequestToken: randomUUID(),
89
+ });
90
+ if (!res.ok) {
91
+ throw refused(describe, res.status, await awsError(res), 'secretsmanager:PutSecretValue');
92
+ }
93
+ },
94
+ describe,
95
+ };
96
+ }
97
+ async function awsError(res) {
98
+ const body = (await res.json().catch(() => ({})));
99
+ const type = body.__type?.split('#').pop();
100
+ const message = body.message ?? body.Message;
101
+ return { ...(type !== undefined && { type }), ...(message !== undefined && { message }) };
102
+ }
103
+ function refused(what, status, error, action) {
104
+ // AWS's own words with the type: what to fix (never a value: Secrets
105
+ // Manager's messages don't echo the secret).
106
+ const why = error.type !== undefined && error.message !== undefined
107
+ ? `${error.type}: ${error.message}`
108
+ : (error.type ?? error.message);
109
+ const denied = error.type === 'AccessDeniedException' || status === 403;
110
+ return new CloudSecretError(`${what} answered ${status}${why !== undefined ? ` (${why})` : ''}.${denied ? ` The identity running this needs ${action} on that secret.` : ''}`);
111
+ }
112
+ // ---- Signature Version 4 ------------------------------------------------------------
113
+ /** The headers a SigV4-signed request carries: the given ones, `host`, `x-amz-date`, the token, `authorization`. */
114
+ export function signV4(input) {
115
+ const amzDate = input.now
116
+ .toISOString()
117
+ .replace(/[-:]/g, '')
118
+ .replace(/\.\d{3}/, '');
119
+ const date = amzDate.slice(0, 8);
120
+ const headers = {
121
+ ...Object.fromEntries(Object.entries(input.headers).map(([k, v]) => [k.toLowerCase(), v.trim()])),
122
+ host: input.host,
123
+ 'x-amz-date': amzDate,
124
+ ...(input.credentials.sessionToken !== undefined && {
125
+ 'x-amz-security-token': input.credentials.sessionToken,
126
+ }),
127
+ };
128
+ const names = Object.keys(headers).sort();
129
+ const signedHeaders = names.join(';');
130
+ const query = Object.entries(input.query ?? {})
131
+ .map(([k, v]) => [encode(k), encode(v)])
132
+ .sort(([a, x], [b, y]) => (a === b ? (x < y ? -1 : 1) : a < b ? -1 : 1))
133
+ .map(([k, v]) => `${k}=${v}`)
134
+ .join('&');
135
+ const canonical = [
136
+ input.method,
137
+ input.path,
138
+ query,
139
+ ...names.map((n) => `${n}:${headers[n]}`),
140
+ '',
141
+ signedHeaders,
142
+ sha256Hex(input.body),
143
+ ].join('\n');
144
+ const scope = `${date}/${input.region}/${input.service}/aws4_request`;
145
+ const toSign = ['AWS4-HMAC-SHA256', amzDate, scope, sha256Hex(canonical)].join('\n');
146
+ const key = [date, input.region, input.service, 'aws4_request'].reduce((k, part) => hmac(k, part), Buffer.from(`AWS4${input.credentials.secretAccessKey}`, 'utf8'));
147
+ const signature = createHmac('sha256', key).update(toSign, 'utf8').digest('hex');
148
+ return {
149
+ ...headers,
150
+ authorization: `AWS4-HMAC-SHA256 Credential=${input.credentials.accessKeyId}/${scope}, SignedHeaders=${signedHeaders}, Signature=${signature}`,
151
+ };
152
+ }
153
+ const hmac = (key, data) => createHmac('sha256', key).update(data, 'utf8').digest();
154
+ const sha256Hex = (data) => createHash('sha256').update(data, 'utf8').digest('hex');
155
+ /** RFC 3986 encoding, as SigV4 wants it. */
156
+ const encode = (s) => encodeURIComponent(s).replace(/[!'()*]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`);
157
+ // ---- credentials ----------------------------------------------------------------------
158
+ /** The first credentials that answer (see the module's comment), or a message saying none did. */
159
+ export async function awsCredentials(deps, region) {
160
+ const sources = [fromEnv, fromWebIdentity, fromContainer, fromInstance, fromAwsCli];
161
+ for (const source of sources) {
162
+ const found = await source(deps, region);
163
+ if (found !== undefined)
164
+ return found;
165
+ }
166
+ throw new CloudSecretError("No AWS credentials here: none in the environment, no web identity, no container or instance role, and `aws configure export-credentials` gave nothing. Run it where the deployment's role is, or sign in with the AWS CLI.");
167
+ }
168
+ async function fromEnv(deps) {
169
+ return asCredentials(deps.env.AWS_ACCESS_KEY_ID, deps.env.AWS_SECRET_ACCESS_KEY, deps.env.AWS_SESSION_TOKEN, 'the environment (AWS_ACCESS_KEY_ID, AWS_SESSION_TOKEN)');
170
+ }
171
+ /** STS AssumeRoleWithWebIdentity (unsigned), regionally. */
172
+ async function fromWebIdentity(deps, region) {
173
+ const roleArn = deps.env.AWS_ROLE_ARN;
174
+ const tokenFile = deps.env.AWS_WEB_IDENTITY_TOKEN_FILE;
175
+ if (!roleArn || !tokenFile)
176
+ return undefined;
177
+ const token = (await readFile(tokenFile, 'utf8')).trim();
178
+ const params = new URLSearchParams({
179
+ Action: 'AssumeRoleWithWebIdentity',
180
+ Version: '2011-06-15',
181
+ RoleArn: roleArn,
182
+ RoleSessionName: 'kindgi-license',
183
+ WebIdentityToken: token,
184
+ });
185
+ const res = await deps.fetch(`https://sts.${region}.${suffixOf(region)}/`, {
186
+ method: 'POST',
187
+ headers: { 'content-type': 'application/x-www-form-urlencoded', accept: 'application/json' },
188
+ body: params.toString(),
189
+ signal: AbortSignal.timeout(30_000),
190
+ });
191
+ if (!res.ok) {
192
+ throw new CloudSecretError(`STS refused the web identity for ${roleArn} (${res.status}).`);
193
+ }
194
+ const body = (await res.json());
195
+ const c = body.AssumeRoleWithWebIdentityResponse?.AssumeRoleWithWebIdentityResult?.Credentials;
196
+ return asCredentials(c?.AccessKeyId, c?.SecretAccessKey, c?.SessionToken);
197
+ }
198
+ /** ECS's relative URI, or EKS Pod Identity's full URI and its token. */
199
+ async function fromContainer(deps) {
200
+ const relative = deps.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI;
201
+ const full = deps.env.AWS_CONTAINER_CREDENTIALS_FULL_URI;
202
+ const url = relative ? `http://169.254.170.2${relative}` : full;
203
+ if (!url)
204
+ return undefined;
205
+ const tokenFile = deps.env.AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE;
206
+ const token = tokenFile
207
+ ? (await readFile(tokenFile, 'utf8')).trim()
208
+ : deps.env.AWS_CONTAINER_AUTHORIZATION_TOKEN?.trim();
209
+ const tokenProblem = token ? headerValueProblem(token) : undefined;
210
+ if (tokenProblem !== undefined) {
211
+ throw new CloudSecretError(`The container's authorization token (${tokenFile ? 'AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE' : 'AWS_CONTAINER_AUTHORIZATION_TOKEN'}) can't be sent in a header: it has ${tokenProblem}.`);
212
+ }
213
+ const res = await deps.fetch(url, {
214
+ headers: token ? { authorization: token } : {},
215
+ signal: AbortSignal.timeout(5000),
216
+ });
217
+ if (!res.ok)
218
+ throw new CloudSecretError(`The container credentials endpoint answered ${res.status}.`);
219
+ const c = (await res.json());
220
+ return asCredentials(c.AccessKeyId, c.SecretAccessKey, c.Token);
221
+ }
222
+ /** The instance's role, IMDSv2 only: no session token, no credentials. */
223
+ async function fromInstance(deps) {
224
+ const imds = 'http://169.254.169.254/latest';
225
+ let c;
226
+ try {
227
+ const t = await deps.fetch(`${imds}/api/token`, {
228
+ method: 'PUT',
229
+ headers: { 'x-aws-ec2-metadata-token-ttl-seconds': '300' },
230
+ signal: AbortSignal.timeout(METADATA_TIMEOUT_MS),
231
+ });
232
+ if (!t.ok)
233
+ return undefined;
234
+ const headers = { 'x-aws-ec2-metadata-token': (await t.text()).trim() };
235
+ const roles = await deps.fetch(`${imds}/meta-data/iam/security-credentials/`, {
236
+ headers,
237
+ signal: AbortSignal.timeout(METADATA_TIMEOUT_MS),
238
+ });
239
+ const role = roles.ok ? (await roles.text()).split('\n')[0]?.trim() : undefined;
240
+ if (!role)
241
+ return undefined;
242
+ const res = await deps.fetch(`${imds}/meta-data/iam/security-credentials/${role}`, {
243
+ headers,
244
+ signal: AbortSignal.timeout(METADATA_TIMEOUT_MS),
245
+ });
246
+ if (!res.ok)
247
+ return undefined;
248
+ c = (await res.json());
249
+ }
250
+ catch {
251
+ return undefined;
252
+ }
253
+ // Outside the `try`: a credential that can't be sent is said, not "none here".
254
+ return asCredentials(c?.AccessKeyId, c?.SecretAccessKey, c?.Token, 'the instance role (IMDS)');
255
+ }
256
+ async function fromAwsCli(deps) {
257
+ let c;
258
+ try {
259
+ const out = await deps.run('aws', ['configure', 'export-credentials', '--format', 'process']);
260
+ c = JSON.parse(out);
261
+ }
262
+ catch {
263
+ return undefined;
264
+ }
265
+ // Outside the `try`: a credential that can't be sent is said, not "none here".
266
+ return asCredentials(c?.AccessKeyId, c?.SecretAccessKey, c?.SessionToken, 'the AWS CLI (aws configure export-credentials)');
267
+ }
268
+ /**
269
+ * Credentials as found, trimmed: a trailing newline would sign one request
270
+ * and send another (a 403 that blames the role). The key id and the session
271
+ * token go into headers, so one fetch can't send is refused by where it
272
+ * came from (`from`), never quoted.
273
+ */
274
+ function asCredentials(rawAccessKeyId, rawSecretAccessKey, rawSessionToken, from = "AWS's answer") {
275
+ const accessKeyId = rawAccessKeyId?.trim();
276
+ const secretAccessKey = rawSecretAccessKey?.trim();
277
+ const sessionToken = rawSessionToken?.trim();
278
+ if (!accessKeyId || !secretAccessKey)
279
+ return undefined;
280
+ for (const [what, value] of [
281
+ ['access key id', accessKeyId],
282
+ ['session token', sessionToken],
283
+ ]) {
284
+ const problem = value ? headerValueProblem(value) : undefined;
285
+ if (problem !== undefined) {
286
+ throw new CloudSecretError(`The AWS ${what} from ${from} can't be sent in a header: it has ${problem}.`);
287
+ }
288
+ }
289
+ return { accessKeyId, secretAccessKey, ...(sessionToken ? { sessionToken } : {}) };
290
+ }
291
+ //# sourceMappingURL=aws.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aws.js","sourceRoot":"","sources":["../../src/license/aws.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { SecretStore } from './stores.js';
2
+ /** How a cloud store reaches its API and its identity: injected in tests. */
3
+ export interface CloudDeps {
4
+ readonly fetch: typeof fetch;
5
+ readonly env: Readonly<Record<string, string | undefined>>;
6
+ /** Runs a command (`gcloud`, `az`) and returns its stdout; throws when it fails. */
7
+ readonly run: (command: string, args: readonly string[]) => Promise<string>;
8
+ }
9
+ export declare class CloudSecretError extends Error {
10
+ }
11
+ export declare function gcpSecretStore(ref: string, deps: CloudDeps): SecretStore | undefined;
12
+ export declare function azureSecretStore(ref: string, deps: CloudDeps): SecretStore | undefined;
13
+ //# sourceMappingURL=cloud.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloud.d.ts","sourceRoot":"","sources":["../../src/license/cloud.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,6EAA6E;AAC7E,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,KAAK,EAAE,OAAO,KAAK,CAAC;IAC7B,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;IAC3D,oFAAoF;IACpF,QAAQ,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAC7E;AAUD,qBAAa,gBAAiB,SAAQ,KAAK;CAAG;AAI9C,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,WAAW,GAAG,SAAS,CA0CpF;AA2BD,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,WAAW,GAAG,SAAS,CAkCtF"}
@@ -0,0 +1,193 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (C) 2026 Kindgi Inc.
3
+ /**
4
+ * A secret in a cloud secret manager, read and written with the platform's
5
+ * own identity, for `kindgi license renew` and `enroll`:
6
+ *
7
+ * - `gcp:projects/<project>/secrets/<name>`: Google Secret Manager. Reads
8
+ * `versions/latest`, writes a new version (`:addVersion`), so the
9
+ * runtime's `latest` reference picks it up at its next start. The token
10
+ * comes from the metadata server (Cloud Run, GCE) or, elsewhere, `gcloud
11
+ * auth print-access-token` (CI, a person).
12
+ * - `azure:https://<vault>.vault.azure.net/secrets/<name>`: Azure Key
13
+ * Vault. Reads the current version, writes a new one. The token comes
14
+ * from the managed identity (Container Apps and App Service's
15
+ * `IDENTITY_ENDPOINT`, else IMDS), with `KINDGI_AZURE_CLIENT_ID` for a
16
+ * user-assigned one, or, elsewhere, `az account get-access-token`.
17
+ *
18
+ * A value is never in a message; a failure names the secret and the
19
+ * answer's status, and what to check.
20
+ */
21
+ import { bearerValueProblem } from '@kindgi/types';
22
+ const GCP_REF = /^projects\/([a-z][a-z0-9-]{4,28}[a-z0-9])\/secrets\/([A-Za-z0-9_-]{1,255})$/;
23
+ /** A Key Vault secret's URL, in the public, China or US Government cloud. */
24
+ const AZURE_REF = /^https:\/\/([a-z0-9-]{3,24})\.(vault\.azure\.net|vault\.azure\.cn|vault\.usgovcloudapi\.net)\/secrets\/([A-Za-z0-9-]{1,127})$/;
25
+ /** How long a metadata endpoint may take before it's taken as not there (not on that platform). */
26
+ const METADATA_TIMEOUT_MS = 1500;
27
+ const CALL_TIMEOUT_MS = 30_000;
28
+ export class CloudSecretError extends Error {
29
+ }
30
+ // ---- Google Secret Manager -----------------------------------------------------
31
+ export function gcpSecretStore(ref, deps) {
32
+ const m = GCP_REF.exec(ref);
33
+ if (m === null)
34
+ return undefined;
35
+ const project = m[1] ?? '';
36
+ const name = m[2] ?? '';
37
+ const base = `https://secretmanager.googleapis.com/v1/projects/${project}/secrets/${name}`;
38
+ const describe = `the Secret Manager secret ${name} (project ${project})`;
39
+ const token = () => gcpToken(deps);
40
+ return {
41
+ async read() {
42
+ const res = await call(deps, `${base}/versions/latest:access`, { method: 'GET' }, await token());
43
+ if (res.status === 404)
44
+ return undefined;
45
+ if (!res.ok)
46
+ throw await refused(res, describe, gcpHint(name, 'roles/secretmanager.secretAccessor'));
47
+ const body = (await res.json());
48
+ const value = Buffer.from(body.payload?.data ?? '', 'base64')
49
+ .toString('utf8')
50
+ .trim();
51
+ return value === '' ? undefined : value;
52
+ },
53
+ async write(value) {
54
+ const res = await call(deps, `${base}:addVersion`, {
55
+ method: 'POST',
56
+ body: JSON.stringify({
57
+ payload: { data: Buffer.from(value, 'utf8').toString('base64') },
58
+ }),
59
+ }, await token());
60
+ if (!res.ok)
61
+ throw await refused(res, describe, gcpHint(name, 'roles/secretmanager.secretVersionAdder'));
62
+ },
63
+ describe,
64
+ };
65
+ }
66
+ function gcpHint(name, role) {
67
+ return `The identity running this needs ${role} on the secret ${name}.`;
68
+ }
69
+ /** The metadata server's token (Cloud Run, GCE), else gcloud's. */
70
+ async function gcpToken(deps) {
71
+ const fromMetadata = await metadata(deps, 'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token', { 'Metadata-Flavor': 'Google' });
72
+ if (fromMetadata !== undefined)
73
+ return fromMetadata;
74
+ try {
75
+ const token = (await deps.run('gcloud', ['auth', 'print-access-token'])).trim();
76
+ if (token !== '')
77
+ return token;
78
+ }
79
+ catch {
80
+ // Said below.
81
+ }
82
+ throw new CloudSecretError("No Google identity here: not on Cloud Run or GCE (no metadata server), and `gcloud auth print-access-token` gave nothing. Run it where the deployment's identity is, or sign in with gcloud.");
83
+ }
84
+ // ---- Azure Key Vault --------------------------------------------------------------
85
+ export function azureSecretStore(ref, deps) {
86
+ const m = AZURE_REF.exec(ref);
87
+ if (m === null)
88
+ return undefined;
89
+ const vault = m[1] ?? '';
90
+ const suffix = m[2] ?? 'vault.azure.net';
91
+ const name = m[3] ?? '';
92
+ const url = `${ref}?api-version=7.4`;
93
+ const describe = `the Key Vault secret ${name} (vault ${vault})`;
94
+ const token = () => azureToken(deps, suffix);
95
+ return {
96
+ async read() {
97
+ const res = await call(deps, url, { method: 'GET' }, await token());
98
+ if (res.status === 404)
99
+ return undefined;
100
+ if (!res.ok)
101
+ throw await refused(res, describe, azureHint(vault, 'Key Vault Secrets User'));
102
+ const value = (await res.json()).value?.trim() ?? '';
103
+ return value === '' ? undefined : value;
104
+ },
105
+ async write(value) {
106
+ const res = await call(deps, url, { method: 'PUT', body: JSON.stringify({ value }) }, await token());
107
+ if (!res.ok) {
108
+ throw await refused(res, describe, azureHint(vault, 'a role with Microsoft.KeyVault/vaults/secrets/setSecret/action'));
109
+ }
110
+ },
111
+ describe,
112
+ };
113
+ }
114
+ function azureHint(vault, role) {
115
+ return `The identity running this needs ${role} on that secret (or the vault ${vault}), and the vault's network rules must let it in.`;
116
+ }
117
+ /**
118
+ * A token for Key Vault (`https://<suffix>`, `vault.azure.net` in the public
119
+ * cloud) from the managed identity, else the Azure CLI's sign-in. Never
120
+ * `AZURE_CLIENT_SECRET` or other bare `AZURE_*` settings.
121
+ */
122
+ async function azureToken(deps, suffix) {
123
+ const resource = `https://${suffix}`;
124
+ const clientId = deps.env.KINDGI_AZURE_CLIENT_ID?.trim();
125
+ const endpoint = deps.env.IDENTITY_ENDPOINT;
126
+ const header = deps.env.IDENTITY_HEADER;
127
+ const query = (extra) => `resource=${encodeURIComponent(resource)}${extra}${clientId ? `&client_id=${encodeURIComponent(clientId)}` : ''}`;
128
+ if (endpoint !== undefined && endpoint !== '' && header !== undefined && header !== '') {
129
+ // Container Apps, App Service, Functions.
130
+ const token = await metadata(deps, `${endpoint}?${query('&api-version=2019-08-01')}`, { 'X-IDENTITY-HEADER': header }, CALL_TIMEOUT_MS);
131
+ if (token !== undefined)
132
+ return token;
133
+ throw new CloudSecretError(`The managed identity endpoint gave no token for ${resource}. Is a managed identity assigned to this app${clientId ? ` with client id ${clientId}` : ''}?`);
134
+ }
135
+ const imds = await metadata(deps, `http://169.254.169.254/metadata/identity/oauth2/token?${query('&api-version=2018-02-01')}`, { Metadata: 'true' });
136
+ if (imds !== undefined)
137
+ return imds;
138
+ try {
139
+ const token = (await deps.run('az', [
140
+ 'account',
141
+ 'get-access-token',
142
+ '--resource',
143
+ resource,
144
+ '--query',
145
+ 'accessToken',
146
+ '-o',
147
+ 'tsv',
148
+ ])).trim();
149
+ if (token !== '')
150
+ return token;
151
+ }
152
+ catch {
153
+ // Said below.
154
+ }
155
+ throw new CloudSecretError("No Azure identity here: no managed identity (IDENTITY_ENDPOINT, IMDS), and `az account get-access-token` gave nothing. Run it where the deployment's identity is, or sign in with az login.");
156
+ }
157
+ // ---- shared -----------------------------------------------------------------------
158
+ /** A token from a metadata endpoint, or `undefined` when there's none (not on that platform). */
159
+ async function metadata(deps, url, headers, timeoutMs = METADATA_TIMEOUT_MS) {
160
+ try {
161
+ const res = await deps.fetch(url, { headers, signal: AbortSignal.timeout(timeoutMs) });
162
+ if (!res.ok)
163
+ return undefined;
164
+ const token = (await res.json()).access_token;
165
+ return typeof token === 'string' && token !== '' ? token : undefined;
166
+ }
167
+ catch {
168
+ return undefined;
169
+ }
170
+ }
171
+ async function call(deps, url, init, token) {
172
+ // From the gcloud or az output, or a metadata server: never quoted into
173
+ // fetch's error, which would print it.
174
+ const problem = bearerValueProblem(token);
175
+ if (problem !== undefined) {
176
+ throw new CloudSecretError(`The cloud access token can't be sent in a header: it has ${problem}. Check what gcloud, az or the metadata server gave.`);
177
+ }
178
+ return deps.fetch(url, {
179
+ method: init.method,
180
+ headers: {
181
+ authorization: `Bearer ${token}`,
182
+ ...(init.body !== undefined && { 'content-type': 'application/json' }),
183
+ },
184
+ ...(init.body !== undefined && { body: init.body }),
185
+ signal: AbortSignal.timeout(CALL_TIMEOUT_MS),
186
+ });
187
+ }
188
+ async function refused(res, what, hint) {
189
+ const body = (await res.json().catch(() => ({})));
190
+ const why = body.error?.message ?? body.error?.status ?? body.error?.code;
191
+ return new CloudSecretError(`${what} answered ${res.status}${why !== undefined ? ` (${why})` : ''}. ${res.status === 401 || res.status === 403 ? hint : ''}`.trim());
192
+ }
193
+ //# sourceMappingURL=cloud.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloud.js","sourceRoot":"","sources":["../../src/license/cloud.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Where a license key stands, as the runtime judges it at startup, and
3
+ * what to do about it: `kindgi doctor` and `kindgi license renew` say the
4
+ * same thing the runtime's banner does.
5
+ *
6
+ * - more than 30 days left: valid;
7
+ * - 30 days or fewer: expiring (the runtime warns);
8
+ * - expired less than 14 days ago: the runtime still starts, with a warning;
9
+ * - after that: the runtime doesn't start with it.
10
+ */
11
+ import type { LicenseClaims } from './license-key.js';
12
+ export declare const LICENSE_WARN_DAYS = 30;
13
+ export declare const LICENSE_GRACE_DAYS = 14;
14
+ export type LicenseStanding = {
15
+ readonly kind: 'valid' | 'expiring';
16
+ readonly daysLeft: number;
17
+ } | {
18
+ readonly kind: 'grace';
19
+ readonly lastStart: Date;
20
+ } | {
21
+ readonly kind: 'expired';
22
+ };
23
+ export declare function standingOf(claims: LicenseClaims, now: number): LicenseStanding;
24
+ /** A key from access.kindgi.com (`gh-<id>`, `dom-<domain>`), not one Kindgi issued by hand. */
25
+ export declare function isSelfServe(subject: string): boolean;
26
+ /**
27
+ * How to get the next key, in the runtime's startup banner's words, so
28
+ * `kindgi doctor`, the banner and the console say the same: for a
29
+ * self-serve key, renew it from the deployment (`renewCommand`) or sign in
30
+ * at access.kindgi.com; for a key Kindgi issued, write to Kindgi.
31
+ */
32
+ export declare function renewAdvice(claims: LicenseClaims, renewCommand: string): string;
33
+ //# sourceMappingURL=expiry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expiry.d.ts","sourceRoot":"","sources":["../../src/license/expiry.ts"],"names":[],"mappings":"AAGA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,eAAO,MAAM,iBAAiB,KAAK,CAAC;AACpC,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAGrC,MAAM,MAAM,eAAe,GACvB;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,UAAU,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAClE;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAA;CAAE,GACpD;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC;AAEjC,wBAAgB,UAAU,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,GAAG,eAAe,CAa9E;AAED,+FAA+F;AAC/F,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAI/E"}
@@ -0,0 +1,35 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (C) 2026 Kindgi Inc.
3
+ export const LICENSE_WARN_DAYS = 30;
4
+ export const LICENSE_GRACE_DAYS = 14;
5
+ const DAY_MS = 86_400_000;
6
+ export function standingOf(claims, now) {
7
+ const left = claims.expiresAt.getTime() - now;
8
+ if (left > 0) {
9
+ const daysLeft = Math.floor(left / DAY_MS);
10
+ return { kind: daysLeft <= LICENSE_WARN_DAYS ? 'expiring' : 'valid', daysLeft };
11
+ }
12
+ if (-left < LICENSE_GRACE_DAYS * DAY_MS) {
13
+ return {
14
+ kind: 'grace',
15
+ lastStart: new Date(claims.expiresAt.getTime() + LICENSE_GRACE_DAYS * DAY_MS),
16
+ };
17
+ }
18
+ return { kind: 'expired' };
19
+ }
20
+ /** A key from access.kindgi.com (`gh-<id>`, `dom-<domain>`), not one Kindgi issued by hand. */
21
+ export function isSelfServe(subject) {
22
+ return /^gh-\d+$/.test(subject) || /^dom-[a-z0-9.-]+$/.test(subject);
23
+ }
24
+ /**
25
+ * How to get the next key, in the runtime's startup banner's words, so
26
+ * `kindgi doctor`, the banner and the console say the same: for a
27
+ * self-serve key, renew it from the deployment (`renewCommand`) or sign in
28
+ * at access.kindgi.com; for a key Kindgi issued, write to Kindgi.
29
+ */
30
+ export function renewAdvice(claims, renewCommand) {
31
+ return isSelfServe(claims.subject)
32
+ ? `Renew it: from this deployment (${renewCommand}), or sign in at https://access.kindgi.com`
33
+ : 'Renew it: contact@kindgi.com';
34
+ }
35
+ //# sourceMappingURL=expiry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expiry.js","sourceRoot":"","sources":["../../src/license/expiry.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,iCAAiC;AAejC,MAAM,CAAC,MAAM,iBAAiB,GAAG,EAAE,CAAC;AACpC,MAAM,CAAC,MAAM,kBAAkB,GAAG,EAAE,CAAC;AACrC,MAAM,MAAM,GAAG,UAAU,CAAC;AAO1B,MAAM,UAAU,UAAU,CAAC,MAAqB,EAAE,GAAW;IAC3D,MAAM,IAAI,GAAG,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC;IAC9C,IAAI,IAAI,GAAG,CAAC,EAAE,CAAC;QACb,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC;QAC3C,OAAO,EAAE,IAAI,EAAE,QAAQ,IAAI,iBAAiB,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,CAAC;IAClF,CAAC;IACD,IAAI,CAAC,IAAI,GAAG,kBAAkB,GAAG,MAAM,EAAE,CAAC;QACxC,OAAO;YACL,IAAI,EAAE,OAAO;YACb,SAAS,EAAE,IAAI,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,kBAAkB,GAAG,MAAM,CAAC;SAC9E,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;AAC7B,CAAC;AAED,+FAA+F;AAC/F,MAAM,UAAU,WAAW,CAAC,OAAe;IACzC,OAAO,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,mBAAmB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AACvE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,MAAqB,EAAE,YAAoB;IACrE,OAAO,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC;QAChC,CAAC,CAAC,mCAAmC,YAAY,4CAA4C;QAC7F,CAAC,CAAC,8BAA8B,CAAC;AACrC,CAAC"}
@@ -0,0 +1,32 @@
1
+ export declare const LICENSE_KEY_PREFIX = "kgi_lk_";
2
+ /**
3
+ * The license signing keys a released runtime trusts, by `kid`: their
4
+ * Ed25519 public keys (base64). A new `kid` is added here, in a CLI
5
+ * release, before Kindgi signs with it: otherwise renewal refuses a
6
+ * correctly renewed key.
7
+ */
8
+ export declare const KINDGI_LICENSE_PUBLIC_KEYS: Readonly<Record<string, string>>;
9
+ export type LicenseUse = 'production' | 'non-production';
10
+ /** What a key says. */
11
+ export interface LicenseClaims {
12
+ readonly keyId: string;
13
+ readonly subject: string;
14
+ readonly name: string;
15
+ readonly use: LicenseUse;
16
+ readonly issuedAt: Date;
17
+ readonly expiresAt: Date;
18
+ }
19
+ export type LicenseKeyCheck = {
20
+ readonly kind: 'ok';
21
+ readonly claims: LicenseClaims;
22
+ } | {
23
+ readonly kind: 'err';
24
+ /** `malformed`: not a key; `unknown-key`: signed with a `kid` this CLI doesn't know; `bad-signature`. */
25
+ readonly reason: 'malformed' | 'unknown-key' | 'bad-signature';
26
+ };
27
+ /**
28
+ * Whether `key` is a license key Kindgi signed, and what it says. The
29
+ * dates aren't judged here: renewal compares them with the current key's.
30
+ */
31
+ export declare function checkLicenseKey(key: string, publicKeys?: Readonly<Record<string, string>>): LicenseKeyCheck;
32
+ //# sourceMappingURL=license-key.d.ts.map