@kybernesis/create 0.15.7 → 0.15.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/doctor.js CHANGED
@@ -9,6 +9,7 @@ import { classifyModelReach } from "./model-reach.js";
9
9
  import { countAbandonedRuns } from "./run-store.js";
10
10
  import { describeStaleSidecar, newestPackageChange, staleSidecars } from "./stale-sidecar.js";
11
11
  import { checkSelfVersion } from "./self-version.js";
12
+ import { envUrlProblems } from "./env-urls.js";
12
13
  const MARK = {
13
14
  pass: green("✓"),
14
15
  warn: yellow("!"),
@@ -215,6 +216,22 @@ export async function doctor() {
215
216
  };
216
217
  if (!existsSync(envPath))
217
218
  add("warn", ".env.local missing", "copy .env.example and fill it");
219
+ /**
220
+ * Config that is shaped wrong rather than missing.
221
+ *
222
+ * A URL-valued setting with no scheme does not fail at start; it fails inside
223
+ * whatever background drain uses it, once per turn, forever. The reference
224
+ * fleet logged 121 of these and lost every analytics event of a deployment
225
+ * while reporting healthy throughout. The value alone is enough to know, so
226
+ * this costs nothing and lands before a client's first turn.
227
+ */
228
+ const urlProblems = existsSync(envPath) ? envUrlProblems(readFileSync(envPath, "utf8")) : [];
229
+ for (const problem of urlProblems) {
230
+ add("fail", `${problem.name} cannot build a request (${problem.reason})`, problem.fix);
231
+ }
232
+ if (existsSync(envPath) && urlProblems.length === 0) {
233
+ add("pass", "every URL-valued setting is an absolute http(s) URL");
234
+ }
218
235
  const githubCheck = githubToolsDoctorCheck(existsSync(join(cwd, "agent/extensions/github.ts")), env);
219
236
  if (githubCheck)
220
237
  add(githubCheck.verdict, githubCheck.label, githubCheck.detail);
@@ -0,0 +1,17 @@
1
+ /** Does this name hold a whole URL? */
2
+ export declare function expectsUrl(name: string): boolean;
3
+ export type EnvUrlProblem = {
4
+ readonly name: string;
5
+ /** Why it cannot be used to build a request. Never includes the value. */
6
+ readonly reason: "no scheme" | "not a url" | "not http(s)";
7
+ readonly fix: string;
8
+ };
9
+ /**
10
+ * Check one value. Returns undefined when it is fine.
11
+ *
12
+ * The value never appears in the result: these files sit beside real secrets
13
+ * and a diagnostic that echoes them turns a config warning into a leak.
14
+ */
15
+ export declare function checkEnvUrl(name: string, value: string): EnvUrlProblem | undefined;
16
+ /** Every URL-shaped value in this env text that cannot build a request. */
17
+ export declare function envUrlProblems(text: string): EnvUrlProblem[];
@@ -0,0 +1,79 @@
1
+ import { parseEnv } from "./util.js";
2
+ /**
3
+ * Environment values that are used to build a URL, and whether they can be.
4
+ *
5
+ * An SDK handed `POSTHOG_HOST=eu.i.posthog.com` does not complain at start. It
6
+ * fails once per turn, forever, inside a background drain: `Failed to parse URL
7
+ * from eu.i.posthog.com/batch/`. On the reference fleet that was 121 logged
8
+ * failures and every analytics event of the deployment silently discarded,
9
+ * while the agent itself looked perfectly healthy. Nothing surfaces it because
10
+ * nothing that matters to the turn depends on it.
11
+ *
12
+ * Hence checking the SHAPE of the value at rest, which costs nothing and finds
13
+ * it before a client's first turn.
14
+ */
15
+ /**
16
+ * Names whose value is unambiguously a whole URL.
17
+ *
18
+ * `_HOST` is deliberately NOT here: plenty of them legitimately hold a bare
19
+ * hostname (`SMTP_HOST`, `PGHOST`, `REDIS_HOST`), and flagging those would make
20
+ * doctor cry wolf on a correct config.
21
+ */
22
+ const URL_SUFFIXES = ["_URL", "_ENDPOINT", "_ISSUER"];
23
+ /**
24
+ * `_HOST` names that specific SDKs use as a base URL, so they do need a scheme.
25
+ * An allowlist rather than a rule, because only the SDK knows which it is.
26
+ */
27
+ const BASE_URL_HOSTS = new Set(["POSTHOG_HOST"]);
28
+ /** Does this name hold a whole URL? */
29
+ export function expectsUrl(name) {
30
+ if (BASE_URL_HOSTS.has(name))
31
+ return true;
32
+ return URL_SUFFIXES.some((suffix) => name.endsWith(suffix));
33
+ }
34
+ /**
35
+ * Check one value. Returns undefined when it is fine.
36
+ *
37
+ * The value never appears in the result: these files sit beside real secrets
38
+ * and a diagnostic that echoes them turns a config warning into a leak.
39
+ */
40
+ export function checkEnvUrl(name, value) {
41
+ const trimmed = value.trim().replace(/^["']|["']$/g, "");
42
+ if (trimmed === "")
43
+ return undefined;
44
+ let parsed;
45
+ try {
46
+ parsed = new URL(trimmed);
47
+ }
48
+ catch {
49
+ // A bare host is by far the common case, and the remedy differs from junk.
50
+ const bare = /^[a-z0-9.-]+(:\d+)?(\/.*)?$/i.test(trimmed);
51
+ return {
52
+ name,
53
+ reason: bare ? "no scheme" : "not a url",
54
+ fix: bare
55
+ ? `${name} has no scheme, so anything built from it fails to parse. Prefix it with https://`
56
+ : `${name} is not a URL. Set it to an absolute https:// URL.`,
57
+ };
58
+ }
59
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
60
+ return {
61
+ name,
62
+ reason: "not http(s)",
63
+ fix: `${name} is a ${parsed.protocol.replace(":", "")} URL. Set it to an absolute https:// URL.`,
64
+ };
65
+ }
66
+ return undefined;
67
+ }
68
+ /** Every URL-shaped value in this env text that cannot build a request. */
69
+ export function envUrlProblems(text) {
70
+ const problems = [];
71
+ for (const [name, value] of Object.entries(parseEnv(text))) {
72
+ if (!expectsUrl(name))
73
+ continue;
74
+ const problem = checkEnvUrl(name, value);
75
+ if (problem)
76
+ problems.push(problem);
77
+ }
78
+ return problems;
79
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kybernesis/create",
3
- "version": "0.15.7",
3
+ "version": "0.15.8",
4
4
  "description": "The Kybernesis agent scaffolder and FDE toolkit: one command to a governed, remembering, multiplayer, self-testing eve agent — plus doctor and upgrade.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -46,5 +46,6 @@
46
46
  },
47
47
  "publishConfig": {
48
48
  "access": "public"
49
- }
49
+ },
50
+ "peerDependencies": {}
50
51
  }