@llm4ts/runner 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/Doctor.d.ts CHANGED
@@ -1,4 +1,26 @@
1
1
  import * as Effect from "effect/Effect";
2
2
  import type { ConnectorRegistryShape } from "@llm4ts/core/ConnectorRegistry";
3
+ export interface PrerequisiteReport {
4
+ readonly satisfied: boolean;
5
+ readonly summary: string;
6
+ readonly hint?: string;
7
+ }
8
+ /**
9
+ * The Gemini CLI resolves credentials from the environment before it ever
10
+ * looks at a prompt, and a Workspace or enterprise account with no project
11
+ * configured fails during auth setup with "No project found" / "requires
12
+ * setting the GOOGLE_CLOUD_PROJECT … env var" — a message that names nothing
13
+ * about llm4ts and is easy to misread as a flow bug.
14
+ *
15
+ * The CLI accepts several routes; any one of them is enough:
16
+ * - `GEMINI_API_KEY` (AI Studio key),
17
+ * - Vertex AI (`GOOGLE_GENAI_USE_VERTEXAI` plus a project or `GOOGLE_API_KEY`),
18
+ * - `GOOGLE_CLOUD_PROJECT` / `GOOGLE_CLOUD_PROJECT_ID` for Code Assist,
19
+ * - or a personal OAuth login, which needs no environment at all.
20
+ *
21
+ * Personal OAuth cannot be observed from the environment, so an unconfigured
22
+ * environment is reported as a caveat rather than a failure.
23
+ */
24
+ export declare const geminiPrerequisites: (environment: Readonly<Record<string, string | undefined>>) => PrerequisiteReport;
3
25
  export declare const makeDoctorProgram: (registry?: ConnectorRegistryShape, environment?: Readonly<Record<string, string | undefined>>) => Effect.Effect<string>;
4
26
  //# sourceMappingURL=Doctor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Doctor.d.ts","sourceRoot":"","sources":["../src/Doctor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAEvC,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAA;AAiB5E,eAAO,MAAM,iBAAiB,GAC5B,WAAU,sBAA8D,EACxE,cAAa,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAe,KACtE,MAAM,CAAC,MAAM,CAAC,MAAM,CAqBnB,CAAA"}
1
+ {"version":3,"file":"Doctor.d.ts","sourceRoot":"","sources":["../src/Doctor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAGvC,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAA;AAuB5E,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CACvB;AAED;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,mBAAmB,GAC9B,aAAa,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,KACxD,kBA0BF,CAAA;AAwBD,eAAO,MAAM,iBAAiB,GAC5B,WAAU,sBAA8D,EACxE,cAAa,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAe,KACtE,MAAM,CAAC,MAAM,CAAC,MAAM,CAwCnB,CAAA"}
package/dist/Doctor.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import * as Effect from "effect/Effect";
2
+ import { ConnectorIds } from "@llm4ts/core/Models";
2
3
  import { nodeFlowRunnerDependencies } from "./FlowRunner.js";
3
4
  const credentialKeys = [
4
5
  "OPENAI_API_KEY",
@@ -11,6 +12,70 @@ const statusLine = (id, status) => {
11
12
  const auth = status.authStatus === "Unknown" ? "" : ` auth: ${status.authStatus.toLowerCase()}`;
12
13
  return ` ${mark} ${id.padEnd(14)} ${status.availability.toLowerCase()}${auth}`;
13
14
  };
15
+ const isSet = (value) => value !== undefined && value.trim().length > 0;
16
+ const truthy = (value) => isSet(value) && ["1", "true", "yes"].includes(value.trim().toLowerCase());
17
+ /**
18
+ * The Gemini CLI resolves credentials from the environment before it ever
19
+ * looks at a prompt, and a Workspace or enterprise account with no project
20
+ * configured fails during auth setup with "No project found" / "requires
21
+ * setting the GOOGLE_CLOUD_PROJECT … env var" — a message that names nothing
22
+ * about llm4ts and is easy to misread as a flow bug.
23
+ *
24
+ * The CLI accepts several routes; any one of them is enough:
25
+ * - `GEMINI_API_KEY` (AI Studio key),
26
+ * - Vertex AI (`GOOGLE_GENAI_USE_VERTEXAI` plus a project or `GOOGLE_API_KEY`),
27
+ * - `GOOGLE_CLOUD_PROJECT` / `GOOGLE_CLOUD_PROJECT_ID` for Code Assist,
28
+ * - or a personal OAuth login, which needs no environment at all.
29
+ *
30
+ * Personal OAuth cannot be observed from the environment, so an unconfigured
31
+ * environment is reported as a caveat rather than a failure.
32
+ */
33
+ export const geminiPrerequisites = (environment) => {
34
+ const project = environment.GOOGLE_CLOUD_PROJECT ?? environment.GOOGLE_CLOUD_PROJECT_ID;
35
+ const vertex = truthy(environment.GOOGLE_GENAI_USE_VERTEXAI);
36
+ if (isSet(environment.GEMINI_API_KEY)) {
37
+ return { satisfied: true, summary: "GEMINI_API_KEY is set" };
38
+ }
39
+ if (vertex && (isSet(project) || isSet(environment.GOOGLE_API_KEY))) {
40
+ return {
41
+ satisfied: true,
42
+ summary: isSet(project)
43
+ ? `Vertex AI with GOOGLE_CLOUD_PROJECT=${project.trim()}`
44
+ : "Vertex AI with GOOGLE_API_KEY"
45
+ };
46
+ }
47
+ if (isSet(project)) {
48
+ return { satisfied: true, summary: `GOOGLE_CLOUD_PROJECT=${project.trim()}` };
49
+ }
50
+ return {
51
+ satisfied: false,
52
+ summary: "no project or API key in the environment",
53
+ hint: "a personal OAuth login still works; a Workspace or enterprise account fails at auth " +
54
+ 'setup with "No project found". Export GOOGLE_CLOUD_PROJECT (or GEMINI_API_KEY) in a file ' +
55
+ "every shell reads — a login-only file such as ~/.bashrc is not read by non-interactive " +
56
+ "shells, other shells, or IDE terminals."
57
+ };
58
+ };
59
+ /** Wraps a hint to the terminal-friendly width the rest of the report uses. */
60
+ const wrap = (text, width, indent) => {
61
+ const out = [];
62
+ let line = "";
63
+ for (const word of text.split(/\s+/).filter((part) => part.length > 0)) {
64
+ const candidate = line.length === 0 ? word : `${line} ${word}`;
65
+ if (candidate.length + indent.length > width && line.length > 0) {
66
+ out.push(`${indent}${line}`);
67
+ line = word;
68
+ }
69
+ else {
70
+ line = candidate;
71
+ }
72
+ }
73
+ if (line.length > 0) {
74
+ out.push(`${indent}${line}`);
75
+ }
76
+ return out;
77
+ };
78
+ const selectedCoder = (environment) => environment.LLM4TS_CODER ?? environment.LLM4ZIO_CODER ?? "claude (default)";
14
79
  export const makeDoctorProgram = (registry = nodeFlowRunnerDependencies().registry, environment = process.env) => Effect.map(registry.healthCheckAll, (statuses) => {
15
80
  const lines = [];
16
81
  lines.push("llm4ts doctor");
@@ -26,8 +91,23 @@ export const makeDoctorProgram = (registry = nodeFlowRunnerDependencies().regist
26
91
  const value = environment[key];
27
92
  lines.push(` ${value === undefined || value.length === 0 ? "✖" : "✔"} ${key}`);
28
93
  }
94
+ // Connector prerequisites the environment must carry BEFORE a run: shown
95
+ // when the connector is selected or the machine has its CLI, because that
96
+ // is exactly when a missing one turns into a confusing mid-run failure.
97
+ const coder = selectedCoder(environment);
98
+ const geminiRelevant = coder.startsWith("gemini") ||
99
+ Array.from(statuses.keys()).some((id) => id.value === ConnectorIds.GeminiCli.value || id.value === ConnectorIds.GeminiApi.value);
100
+ if (geminiRelevant) {
101
+ const gemini = geminiPrerequisites(environment);
102
+ lines.push("");
103
+ lines.push("prerequisites:");
104
+ lines.push(` ${gemini.satisfied ? "✔" : "?"} gemini: ${gemini.summary}`);
105
+ if (gemini.hint !== undefined) {
106
+ lines.push(...wrap(gemini.hint, 78, " "));
107
+ }
108
+ }
29
109
  lines.push("");
30
- lines.push(`coder: ${environment.LLM4TS_CODER ?? environment.LLM4ZIO_CODER ?? "claude (default)"}`);
110
+ lines.push(`coder: ${coder}`);
31
111
  return `${lines.join("\n")}\n`;
32
112
  });
33
113
  //# sourceMappingURL=Doctor.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Doctor.js","sourceRoot":"","sources":["../src/Doctor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAGvC,OAAO,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAA;AAE5D,MAAM,cAAc,GAAG;IACrB,gBAAgB;IAChB,mBAAmB;IACnB,gBAAgB;IAChB,gBAAgB;CACR,CAAA;AAEV,MAAM,UAAU,GAAG,CAAC,EAAU,EAAE,MAAoB,EAAU,EAAE;IAC9D,MAAM,IAAI,GACR,MAAM,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAA;IACzF,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,MAAM,CAAC,UAAU,CAAC,WAAW,EAAE,EAAE,CAAA;IAChG,OAAO,KAAK,IAAI,IAAI,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,MAAM,CAAC,YAAY,CAAC,WAAW,EAAE,GAAG,IAAI,EAAE,CAAA;AACjF,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAC/B,WAAmC,0BAA0B,EAAE,CAAC,QAAQ,EACxE,cAA4D,OAAO,CAAC,GAAG,EAChD,EAAE,CACzB,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,cAAc,EAAE,CAAC,QAAQ,EAAE,EAAE;IAC/C,MAAM,KAAK,GAAkB,EAAE,CAAA;IAC/B,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,CAAA;IAC3B,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IACd,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAA;IACzB,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAA;IAChG,KAAK,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QAClC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAA;IAC1C,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IACd,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,CAAA;IAC1B,KAAK,MAAM,GAAG,IAAI,cAAc,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,CAAA;QAC9B,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,EAAE,CAAC,CAAA;IACjF,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IACd,KAAK,CAAC,IAAI,CACR,UAAU,WAAW,CAAC,YAAY,IAAI,WAAW,CAAC,aAAa,IAAI,kBAAkB,EAAE,CACxF,CAAA;IACD,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAA;AAChC,CAAC,CAAC,CAAA"}
1
+ {"version":3,"file":"Doctor.js","sourceRoot":"","sources":["../src/Doctor.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llm4ts/runner",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Node runner, CLI, terminal, and MCP composition for llm4ts",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -55,8 +55,8 @@
55
55
  "typescript"
56
56
  ],
57
57
  "dependencies": {
58
- "@llm4ts/flow": "0.3.0",
59
- "@llm4ts/core": "0.3.0"
58
+ "@llm4ts/core": "0.3.1",
59
+ "@llm4ts/flow": "0.3.1"
60
60
  },
61
61
  "peerDependencies": {
62
62
  "effect": "^4.0.0-beta.102"
package/src/Doctor.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import * as Effect from "effect/Effect"
2
2
  import type { HealthStatus } from "@llm4ts/core/Models"
3
+ import { ConnectorIds } from "@llm4ts/core/Models"
3
4
  import type { ConnectorRegistryShape } from "@llm4ts/core/ConnectorRegistry"
4
5
  import { nodeFlowRunnerDependencies } from "./FlowRunner.ts"
5
6
 
@@ -17,6 +18,86 @@ const statusLine = (id: string, status: HealthStatus): string => {
17
18
  return ` ${mark} ${id.padEnd(14)} ${status.availability.toLowerCase()}${auth}`
18
19
  }
19
20
 
21
+ const isSet = (value: string | undefined): value is string =>
22
+ value !== undefined && value.trim().length > 0
23
+
24
+ const truthy = (value: string | undefined): boolean =>
25
+ isSet(value) && ["1", "true", "yes"].includes(value.trim().toLowerCase())
26
+
27
+ export interface PrerequisiteReport {
28
+ readonly satisfied: boolean
29
+ readonly summary: string
30
+ readonly hint?: string
31
+ }
32
+
33
+ /**
34
+ * The Gemini CLI resolves credentials from the environment before it ever
35
+ * looks at a prompt, and a Workspace or enterprise account with no project
36
+ * configured fails during auth setup with "No project found" / "requires
37
+ * setting the GOOGLE_CLOUD_PROJECT … env var" — a message that names nothing
38
+ * about llm4ts and is easy to misread as a flow bug.
39
+ *
40
+ * The CLI accepts several routes; any one of them is enough:
41
+ * - `GEMINI_API_KEY` (AI Studio key),
42
+ * - Vertex AI (`GOOGLE_GENAI_USE_VERTEXAI` plus a project or `GOOGLE_API_KEY`),
43
+ * - `GOOGLE_CLOUD_PROJECT` / `GOOGLE_CLOUD_PROJECT_ID` for Code Assist,
44
+ * - or a personal OAuth login, which needs no environment at all.
45
+ *
46
+ * Personal OAuth cannot be observed from the environment, so an unconfigured
47
+ * environment is reported as a caveat rather than a failure.
48
+ */
49
+ export const geminiPrerequisites = (
50
+ environment: Readonly<Record<string, string | undefined>>
51
+ ): PrerequisiteReport => {
52
+ const project = environment.GOOGLE_CLOUD_PROJECT ?? environment.GOOGLE_CLOUD_PROJECT_ID
53
+ const vertex = truthy(environment.GOOGLE_GENAI_USE_VERTEXAI)
54
+ if (isSet(environment.GEMINI_API_KEY)) {
55
+ return { satisfied: true, summary: "GEMINI_API_KEY is set" }
56
+ }
57
+ if (vertex && (isSet(project) || isSet(environment.GOOGLE_API_KEY))) {
58
+ return {
59
+ satisfied: true,
60
+ summary: isSet(project)
61
+ ? `Vertex AI with GOOGLE_CLOUD_PROJECT=${project.trim()}`
62
+ : "Vertex AI with GOOGLE_API_KEY"
63
+ }
64
+ }
65
+ if (isSet(project)) {
66
+ return { satisfied: true, summary: `GOOGLE_CLOUD_PROJECT=${project.trim()}` }
67
+ }
68
+ return {
69
+ satisfied: false,
70
+ summary: "no project or API key in the environment",
71
+ hint:
72
+ "a personal OAuth login still works; a Workspace or enterprise account fails at auth " +
73
+ 'setup with "No project found". Export GOOGLE_CLOUD_PROJECT (or GEMINI_API_KEY) in a file ' +
74
+ "every shell reads — a login-only file such as ~/.bashrc is not read by non-interactive " +
75
+ "shells, other shells, or IDE terminals."
76
+ }
77
+ }
78
+
79
+ /** Wraps a hint to the terminal-friendly width the rest of the report uses. */
80
+ const wrap = (text: string, width: number, indent: string): ReadonlyArray<string> => {
81
+ const out: Array<string> = []
82
+ let line = ""
83
+ for (const word of text.split(/\s+/).filter((part) => part.length > 0)) {
84
+ const candidate = line.length === 0 ? word : `${line} ${word}`
85
+ if (candidate.length + indent.length > width && line.length > 0) {
86
+ out.push(`${indent}${line}`)
87
+ line = word
88
+ } else {
89
+ line = candidate
90
+ }
91
+ }
92
+ if (line.length > 0) {
93
+ out.push(`${indent}${line}`)
94
+ }
95
+ return out
96
+ }
97
+
98
+ const selectedCoder = (environment: Readonly<Record<string, string | undefined>>): string =>
99
+ environment.LLM4TS_CODER ?? environment.LLM4ZIO_CODER ?? "claude (default)"
100
+
20
101
  export const makeDoctorProgram = (
21
102
  registry: ConnectorRegistryShape = nodeFlowRunnerDependencies().registry,
22
103
  environment: Readonly<Record<string, string | undefined>> = process.env
@@ -36,9 +117,28 @@ export const makeDoctorProgram = (
36
117
  const value = environment[key]
37
118
  lines.push(` ${value === undefined || value.length === 0 ? "✖" : "✔"} ${key}`)
38
119
  }
120
+
121
+ // Connector prerequisites the environment must carry BEFORE a run: shown
122
+ // when the connector is selected or the machine has its CLI, because that
123
+ // is exactly when a missing one turns into a confusing mid-run failure.
124
+ const coder = selectedCoder(environment)
125
+ const geminiRelevant =
126
+ coder.startsWith("gemini") ||
127
+ Array.from(statuses.keys()).some(
128
+ (id) =>
129
+ id.value === ConnectorIds.GeminiCli.value || id.value === ConnectorIds.GeminiApi.value
130
+ )
131
+ if (geminiRelevant) {
132
+ const gemini = geminiPrerequisites(environment)
133
+ lines.push("")
134
+ lines.push("prerequisites:")
135
+ lines.push(` ${gemini.satisfied ? "✔" : "?"} gemini: ${gemini.summary}`)
136
+ if (gemini.hint !== undefined) {
137
+ lines.push(...wrap(gemini.hint, 78, " "))
138
+ }
139
+ }
140
+
39
141
  lines.push("")
40
- lines.push(
41
- `coder: ${environment.LLM4TS_CODER ?? environment.LLM4ZIO_CODER ?? "claude (default)"}`
42
- )
142
+ lines.push(`coder: ${coder}`)
43
143
  return `${lines.join("\n")}\n`
44
144
  })