@stigmer/cli 3.12.6 → 3.12.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.
@@ -7,13 +7,19 @@
7
7
  // Local agent execution is Anthropic-only (the native runner only constructs
8
8
  // Anthropic clients and the registry's native entries are Anthropic models),
9
9
  // so the interactive flow is a single API-key prompt rather than a provider
10
- // menu. There is no model concept here at all: the platform model registry
11
- // owns the execution default, and per-run overrides belong to `stigmer run
12
- // --model` (oss#314 removed the dead setup-level pin).
10
+ // menu, followed by the skippable operator-identity prompt (oss#796). There
11
+ // is no model concept here at all: the platform model registry owns the
12
+ // execution default, and per-run overrides belong to `stigmer run --model`
13
+ // (oss#314 removed the dead setup-level pin).
13
14
 
14
15
  import type { Config } from "../../config/config.js";
15
16
  import { type LlmSettings, setLlm } from "../llm-config.js";
16
- import { promptSecret } from "./prompt.js";
17
+ import {
18
+ type OperatorSettings,
19
+ setOperator,
20
+ validateOperatorIdentity,
21
+ } from "../operator-config.js";
22
+ import { promptLine, promptSecret } from "./prompt.js";
17
23
 
18
24
  /**
19
25
  * The providers `stigmer setup` accepts. This is the single source of truth
@@ -47,25 +53,88 @@ export function applyChoice(config: Config, choice: ProviderChoice, inputs: Sele
47
53
  return setLlm(config, buildLlmForChoice(choice, inputs));
48
54
  }
49
55
 
56
+ /**
57
+ * Resolve operator-identity inputs to the settings to persist, validating the
58
+ * way the server's boot check will (oss#796) — the pure decision core shared
59
+ * by the flag path and the interactive prompt, like buildLlmForChoice. An
60
+ * empty email with an empty name yields `undefined` (nothing to persist —
61
+ * identity stays whatever it was; the anonymous default is the absence of
62
+ * the section, not an empty one). Throws on inputs the server would refuse
63
+ * at boot, so the failure surfaces at setup time.
64
+ */
65
+ export function buildOperatorForInputs(email: string, name: string): OperatorSettings | undefined {
66
+ const error = validateOperatorIdentity(email, name);
67
+ if (error !== undefined) {
68
+ throw new Error(error);
69
+ }
70
+ const trimmedEmail = email.trim();
71
+ if (trimmedEmail === "") return undefined;
72
+ const trimmedName = name.trim();
73
+ return trimmedName === "" ? { email: trimmedEmail } : { email: trimmedEmail, name: trimmedName };
74
+ }
75
+
76
+ /** Apply operator-identity inputs to a config, returning the updated copy. */
77
+ export function applyOperatorIdentity(config: Config, email: string, name: string): Config {
78
+ const settings = buildOperatorForInputs(email, name);
79
+ return settings === undefined ? config : setOperator(config, settings);
80
+ }
81
+
50
82
  /**
51
83
  * Run the interactive setup against the current terminal: one Anthropic
52
- * API-key prompt. A key already in the environment is used without prompting
53
- * (and never persisted — env precedence makes a copy redundant); an empty
54
- * entry is an explicit skip.
84
+ * API-key prompt, then the (skippable) operator-identity prompt. A key
85
+ * already in the environment is used without prompting (and never persisted —
86
+ * env precedence makes a copy redundant); an empty entry is an explicit skip.
55
87
  */
56
88
  export async function runInteractiveWizard(config: Config, env: NodeJS.ProcessEnv = process.env): Promise<Config> {
57
89
  process.stderr.write("\nStigmer runs local agents on Anthropic Claude models.\n");
58
90
  process.stderr.write("The model is chosen automatically from the platform model registry.\n\n");
59
91
 
92
+ let updated: Config;
60
93
  if (env.ANTHROPIC_API_KEY) {
61
94
  process.stderr.write("Using ANTHROPIC_API_KEY from environment\n");
62
- return applyChoice(config, "anthropic");
95
+ updated = applyChoice(config, "anthropic");
96
+ } else {
97
+ const key = (await promptSecret("Enter your Anthropic API key (press Enter to skip)")).trim();
98
+ if (key === "") {
99
+ process.stderr.write("Skipped. Agents won't execute until a provider is configured (stigmer setup).\n");
100
+ updated = applyChoice(config, "skip");
101
+ } else {
102
+ updated = applyChoice(config, "anthropic", { apiKey: key });
103
+ }
104
+ }
105
+
106
+ return runOperatorIdentityStep(updated, env);
107
+ }
108
+
109
+ /**
110
+ * The operator-identity step (oss#796): opt-in, skippable, and env-aware in
111
+ * the same way the API-key step is — an env-provided identity is used without
112
+ * prompting and never persisted (env precedence makes a copy redundant). An
113
+ * invalid entry is refused and NOT persisted (re-run `stigmer setup`), the
114
+ * same rule the server enforces at boot, just earlier.
115
+ */
116
+ async function runOperatorIdentityStep(config: Config, env: NodeJS.ProcessEnv): Promise<Config> {
117
+ process.stderr.write(
118
+ "\nAn operator identity attributes local creates to you and unlocks identity-gated MCP tools.\n",
119
+ );
120
+
121
+ if ((env.STIGMER_OPERATOR_EMAIL ?? "").trim() !== "") {
122
+ process.stderr.write("Using STIGMER_OPERATOR_EMAIL from environment\n");
123
+ return config;
124
+ }
125
+
126
+ const email = (await promptLine("Operator email (press Enter to skip): ")).trim();
127
+ if (email === "") {
128
+ return config;
63
129
  }
130
+ const name = (await promptLine("Operator display name (optional): ")).trim();
64
131
 
65
- const key = (await promptSecret("Enter your Anthropic API key (press Enter to skip)")).trim();
66
- if (key === "") {
67
- process.stderr.write("Skipped. Agents won't execute until a provider is configured (stigmer setup).\n");
68
- return applyChoice(config, "skip");
132
+ try {
133
+ return applyOperatorIdentity(config, email, name);
134
+ } catch (error) {
135
+ process.stderr.write(
136
+ `Not saved: ${error instanceof Error ? error.message : String(error)} — re-run stigmer setup.\n`,
137
+ );
138
+ return config;
69
139
  }
70
- return applyChoice(config, "anthropic", { apiKey: key });
71
140
  }