create-nola-lang 0.1.8 → 0.1.10

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.
@@ -151,7 +151,13 @@ var TEMPLATES = [
151
151
  { name: "extract-invoice", label: "same-file type references, optional fields", source: "example" },
152
152
  { name: "classify-message", label: "closed label sets: union alias, string enum, inline union", source: "example" },
153
153
  { name: "chain-of-thought", label: "two asks sharing accumulating context", source: "example" },
154
- { name: "research-notes", label: "TS control flow orchestrating nola functions", source: "example" }
154
+ { name: "research-notes", label: "TS control flow orchestrating nola functions", source: "example" },
155
+ {
156
+ name: "triage-ticket",
157
+ label: "ticket triage: literal unions and booleans as typed questions",
158
+ source: "example",
159
+ provider: { label: "typesafe.ai", envVar: "TYPESAFE_API_KEY" }
160
+ }
155
161
  ];
156
162
  function templateByName(name) {
157
163
  return TEMPLATES.find((t) => t.name === name);
@@ -1465,7 +1471,8 @@ var EDITOR_OPTIONS = [
1465
1471
  { value: "vscode", label: "VS Code", hint: ".vscode/ with a debug launch config + extension recommendation" }
1466
1472
  ];
1467
1473
  function templateOption(t) {
1468
- return { value: t.name, label: t.source === "example" ? `example: ${t.name}` : t.name, hint: t.label };
1474
+ const vendor = t.provider ? ` (${t.provider.label})` : "";
1475
+ return { value: t.name, label: t.source === "example" ? `example: ${t.name}${vendor}` : `${t.name}${vendor}`, hint: t.label };
1469
1476
  }
1470
1477
  async function resolveSetup(input, prompter, dir) {
1471
1478
  const ide = input.ide === "vscode" || input.ide === "none" ? input.ide : void 0;
@@ -1519,10 +1526,10 @@ function flaggedProvider(input) {
1519
1526
  }
1520
1527
  return input.provider;
1521
1528
  }
1522
- async function resolveProvider(input, prompter) {
1529
+ async function resolveProvider(input, prompter, pinned = false) {
1523
1530
  const flagged = flaggedProvider(input);
1524
1531
  if (flagged !== void 0) return flagged;
1525
- if (!input.interactive) return "none";
1532
+ if (pinned || !input.interactive) return "none";
1526
1533
  const path = input.keyPath ?? { kind: "trial" };
1527
1534
  while (true) {
1528
1535
  const choice = await prompter.select(PROVIDER_QUESTION, providerOptions(path));
@@ -1532,8 +1539,8 @@ async function resolveProvider(input, prompter) {
1532
1539
  return choice;
1533
1540
  }
1534
1541
  }
1535
- async function resolveExtras(input, prompter, dir) {
1536
- const provider = await resolveProvider(input, prompter);
1542
+ async function resolveExtras(input, prompter, dir, pinnedProvider = false) {
1543
+ const provider = await resolveProvider(input, prompter, pinnedProvider);
1537
1544
  if (provider === null) return null;
1538
1545
  const setup = await resolveSetup(input, prompter, dir);
1539
1546
  return setup === null ? null : { provider, ...setup };
@@ -1630,7 +1637,7 @@ async function resolveScaffoldOptions(input, prompter) {
1630
1637
  template = choice;
1631
1638
  }
1632
1639
  }
1633
- const extras = await resolveExtras(input, prompter, dir);
1640
+ const extras = await resolveExtras(input, prompter, dir, templateByName(template)?.provider !== void 0);
1634
1641
  if (extras === null) return { kind: "cancelled" };
1635
1642
  return { kind: "scaffold", dir, template, force, ...extras };
1636
1643
  }
@@ -1679,7 +1686,7 @@ function commented(command, note) {
1679
1686
  }
1680
1687
  function nextSteps(outcome, fileCount, name, pm, installed = false, grant = null) {
1681
1688
  const cmd = packageManagerCommands(pm);
1682
- const vendorEnv = providerById(outcome.provider)?.envVar;
1689
+ const vendorEnv = providerById(outcome.provider)?.envVar ?? (outcome.provider === "none" ? templateByName(outcome.template)?.provider?.envVar : void 0);
1683
1690
  const startNote = grant ? grant.source === "trial" ? "25 free Nola runs \u2014 key in .env" : "key in .env (run `npx nola-lang account` to check the balance)" : vendorEnv ? `set ${vendorEnv} in .env first` : outcome.template === "empty" ? "set OPENAI_API_KEY first" : "runs offline \u2014 no API key needed";
1684
1691
  const lines = [
1685
1692
  ...outcome.dir === "." ? [] : [`cd ${outcome.dir}`],
package/dist/flow.js CHANGED
@@ -46,7 +46,9 @@ const EDITOR_OPTIONS = [
46
46
  { value: "vscode", label: "VS Code", hint: ".vscode/ with a debug launch config + extension recommendation" },
47
47
  ];
48
48
  function templateOption(t) {
49
- return { value: t.name, label: t.source === "example" ? `example: ${t.name}` : t.name, hint: t.label };
49
+ // a pinned template names its vendor in the row itself: `example: triage-ticket (typesafe.ai)`
50
+ const vendor = t.provider ? ` (${t.provider.label})` : "";
51
+ return { value: t.name, label: t.source === "example" ? `example: ${t.name}${vendor}` : `${t.name}${vendor}`, hint: t.label };
50
52
  }
51
53
  /**
52
54
  * The setup step: a yes/no gate (default yes), then editor + coding agents in
@@ -133,11 +135,13 @@ function flaggedProvider(input) {
133
135
  * one Enter away, Ctrl+C cancels). The key itself is minted later, on the
134
136
  * stored session, after every question — nothing is consumed by a cancel.
135
137
  */
136
- async function resolveProvider(input, prompter) {
138
+ async function resolveProvider(input, prompter, pinned = false) {
137
139
  const flagged = flaggedProvider(input);
138
140
  if (flagged !== undefined)
139
141
  return flagged;
140
- if (!input.interactive)
142
+ // A template whose own config names its vendor (triage-ticket → typesafe())
143
+ // is not asked: "none" keeps that config, and the outro names its env var.
144
+ if (pinned || !input.interactive)
141
145
  return "none";
142
146
  const path = input.keyPath ?? { kind: "trial" };
143
147
  while (true) {
@@ -152,8 +156,8 @@ async function resolveProvider(input, prompter) {
152
156
  }
153
157
  }
154
158
  /** The provider and setup (editor + agents) questions, in that order, shared by every outcome site; null = cancelled. */
155
- async function resolveExtras(input, prompter, dir) {
156
- const provider = await resolveProvider(input, prompter);
159
+ async function resolveExtras(input, prompter, dir, pinnedProvider = false) {
160
+ const provider = await resolveProvider(input, prompter, pinnedProvider);
157
161
  if (provider === null)
158
162
  return null;
159
163
  const setup = await resolveSetup(input, prompter, dir);
@@ -263,7 +267,7 @@ export async function resolveScaffoldOptions(input, prompter) {
263
267
  template = choice;
264
268
  }
265
269
  }
266
- const extras = await resolveExtras(input, prompter, dir);
270
+ const extras = await resolveExtras(input, prompter, dir, templateByName(template)?.provider !== undefined);
267
271
  if (extras === null)
268
272
  return { kind: "cancelled" };
269
273
  return { kind: "scaffold", dir, template, force, ...extras };
@@ -320,7 +324,9 @@ function commented(command, note) {
320
324
  }
321
325
  function nextSteps(outcome, fileCount, name, pm, installed = false, grant = null) {
322
326
  const cmd = packageManagerCommands(pm);
323
- const vendorEnv = providerById(outcome.provider)?.envVar;
327
+ // a chosen vendor's env var, else the one the template's own config reads (a pinned template under "none")
328
+ const vendorEnv = providerById(outcome.provider)?.envVar ??
329
+ (outcome.provider === "none" ? templateByName(outcome.template)?.provider?.envVar : undefined);
324
330
  const startNote = grant
325
331
  ? grant.source === "trial"
326
332
  ? "25 free Nola runs — key in .env"
package/dist/index.js CHANGED
@@ -85,7 +85,7 @@ import {
85
85
  writeEnvKey,
86
86
  writeSession,
87
87
  writeVscodeSetup
88
- } from "./chunk-GUYF4NM5.js";
88
+ } from "./chunk-G2TOUJVB.js";
89
89
  import {
90
90
  clackPrompter
91
91
  } from "./chunk-7IQIRMK6.js";
package/dist/main.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  CREATE_COMMAND,
4
4
  dispatch,
5
5
  ownVersion
6
- } from "./chunk-GUYF4NM5.js";
6
+ } from "./chunk-G2TOUJVB.js";
7
7
  import "./chunk-FPS2V3S4.js";
8
8
 
9
9
  // src/main.ts
@@ -9,6 +9,16 @@ export interface TemplateDef {
9
9
  /** one-line menu description */
10
10
  label: string;
11
11
  source: "builtin" | "example";
12
+ /**
13
+ * The vendor the template's OWN config names. The flow skips the provider
14
+ * question for such a template (its config is the point of the example),
15
+ * the menu row carries `(<label>)`, and the outro names the env var; an
16
+ * explicit --provider flag still wins.
17
+ */
18
+ provider?: {
19
+ label: string;
20
+ envVar: string;
21
+ };
12
22
  }
13
23
  export declare const TEMPLATES: readonly TemplateDef[];
14
24
  export declare function templateByName(name: string): TemplateDef | undefined;
package/dist/registry.js CHANGED
@@ -7,6 +7,12 @@ export const TEMPLATES = [
7
7
  { name: "classify-message", label: "closed label sets: union alias, string enum, inline union", source: "example" },
8
8
  { name: "chain-of-thought", label: "two asks sharing accumulating context", source: "example" },
9
9
  { name: "research-notes", label: "TS control flow orchestrating nola functions", source: "example" },
10
+ {
11
+ name: "triage-ticket",
12
+ label: "ticket triage: literal unions and booleans as typed questions",
13
+ source: "example",
14
+ provider: { label: "typesafe.ai", envVar: "TYPESAFE_API_KEY" },
15
+ },
10
16
  ];
11
17
  export function templateByName(name) {
12
18
  return TEMPLATES.find((t) => t.name === name);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-nola-lang",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "Scaffold a new Nola project (npm create nola, or npm create nola-lang)",
5
5
  "keywords": [
6
6
  "nola",
@@ -10,7 +10,7 @@ import { openai, mockProvider, withRetry } from "@nola-lang/providers";
10
10
  `defineConfig`, the `nola` namespace (`nola.infer()`, `nola.tracer()`),
11
11
  `terminalTrace()` and everything app-facing come from `@nola-lang/runtime`.
12
12
  Everything bring-your-own — vendor factories (`openai`, `anthropic`,
13
- `google`, `mockProvider`), resilience combinators (`withRetry`, `fallback`,
13
+ `google`, `typesafe`, `mockProvider`), resilience combinators (`withRetry`, `fallback`,
14
14
  `roundRobin`, `constant`, `exponential`) and record/replay (`record`,
15
15
  `replay`) — comes from `@nola-lang/providers`.
16
16
 
@@ -40,6 +40,13 @@ export default defineConfig({
40
40
  `openai({ model })` requires an explicit model; optional fields are `apiKey`,
41
41
  `apiKeyEnv` (default `"OPENAI_API_KEY"`), `baseUrl` and `fetch`.
42
42
 
43
+ `typesafe()` (typesafe.ai's Jev, reads `TYPESAFE_API_KEY`, model defaults to
44
+ `jev-latest`) is NOT a chat model: it serves only asks whose output type is a
45
+ string or number literal union, a boolean, or a flat object of those, and
46
+ fails definitively before the network on anything else. Use it behind
47
+ `fallback([typesafe(), openai({ model: "gpt-5-mini" })])` so other asks
48
+ escalate to a general model. Never scaffold it as the only model.
49
+
43
50
  `nola` is a NAMESPACE, not a function (`nola({})` is a type error).
44
51
  `nola.infer()` is the platform model — the Nola platform (api.nola.sh, or a
45
52
  self-hosted console at `baseUrl`) serves inference and reads `NOLA_API_KEY`;
@@ -203,11 +210,11 @@ the providers package:
203
210
  "check": "nola check"
204
211
  },
205
212
  "dependencies": {
206
- "@nola-lang/providers": "^0.1.8",
207
- "@nola-lang/runtime": "^0.1.8"
213
+ "@nola-lang/providers": "^0.1.10",
214
+ "@nola-lang/runtime": "^0.1.10"
208
215
  },
209
216
  "devDependencies": {
210
- "nola-lang": "^0.1.8",
217
+ "nola-lang": "^0.1.10",
211
218
  "typescript": "^5.6.0"
212
219
  },
213
220
  "engines": { "node": ">=22.18" }