pzero-operator 0.1.3 → 0.1.4

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/README.md CHANGED
@@ -38,7 +38,7 @@ The long-term goal is a stronger device-level operator surface inside the `Proje
38
38
 
39
39
  ## Install
40
40
 
41
- From npm:
41
+ One command for Linux, macOS, and Windows:
42
42
 
43
43
  ```bash
44
44
  npm install -g pzero-operator
@@ -52,9 +52,15 @@ operator
52
52
 
53
53
  ## Platform status
54
54
 
55
- - Linux: primary supported path
56
- - macOS: expected path, should be validated per environment
57
- - Windows: install path exists, but runtime support should currently be treated as experimental until the Windows startup path is fully hardened
55
+ - Linux: supported
56
+ - macOS: supported
57
+ - Windows: supported
58
+
59
+ Requirements:
60
+
61
+ - Node.js 20+
62
+ - npm
63
+ - a terminal environment such as Terminal, iTerm, Windows Terminal, PowerShell, or a standard shell session
58
64
 
59
65
  ## Model rails
60
66
 
@@ -91,8 +97,8 @@ Typical first use:
91
97
 
92
98
  1. install the package from npm
93
99
  2. run `operator`
94
- 3. choose a runtime rail
95
- 4. if using `Alem AI Plus`, enter the API key when asked
100
+ 3. if using `Alem AI Plus`, choose a model and enter the API key when asked
101
+ 4. if using `Ollama`, switch with `operator use ollama`
96
102
  5. start working inside the terminal
97
103
 
98
104
  ## What `operator` can do now
@@ -1,5 +1,7 @@
1
1
  export const BUILTIN_SLASH_COMMANDS = [
2
2
  { name: "model", description: "Select model (opens selector UI)" },
3
+ { name: "apikey", description: "Set or replace API key for the current provider" },
4
+ { name: "auth", description: "Alias for /apikey" },
3
5
  { name: "thinking", description: "Set thinking level: low, medium, or high" },
4
6
  { name: "copy", description: "Copy last agent message to clipboard" },
5
7
  { name: "name", description: "Set session display name" },
@@ -11,7 +11,7 @@ const EXTENSIONS_DOC_URL = "https://github.com/operator/blob/main/packages/codin
11
11
  const DEFAULT_MODELS_CONFIG = {
12
12
  providers: {
13
13
  "alem-ai-plus-gpt-oss": {
14
- baseUrl: "https://llm.alem.ai/chat/completions",
14
+ baseUrl: "https://llm.alem.ai/v1",
15
15
  api: "openai-completions",
16
16
  authHeader: true,
17
17
  compat: {
@@ -27,7 +27,7 @@ const DEFAULT_MODELS_CONFIG = {
27
27
  ],
28
28
  },
29
29
  "alem-ai-plus-qwen3-6": {
30
- baseUrl: "https://llm.alem.ai/v1/chat/completions",
30
+ baseUrl: "https://llm.alem.ai/v1",
31
31
  api: "openai-completions",
32
32
  authHeader: true,
33
33
  compat: {
@@ -43,7 +43,7 @@ const DEFAULT_MODELS_CONFIG = {
43
43
  ],
44
44
  },
45
45
  "alem-ai-plus-gemma4": {
46
- baseUrl: "https://llm.alem.ai/v1/chat/completions",
46
+ baseUrl: "https://llm.alem.ai/v1",
47
47
  api: "openai-completions",
48
48
  authHeader: true,
49
49
  compat: {
@@ -69,9 +69,9 @@ const DEFAULT_MODELS_CONFIG = {
69
69
  },
70
70
  };
71
71
  const DEFAULT_SETTINGS_CONFIG = {
72
- defaultProvider: "alem-ai-plus-gpt-oss",
73
- defaultModel: "gpt-oss",
74
- defaultThinkingLevel: "low",
72
+ defaultProvider: "alem-ai-plus-qwen3-6",
73
+ defaultModel: "qwen3-6",
74
+ defaultThinkingLevel: "high",
75
75
  };
76
76
  function ensureDefaultRuntimeConfig() {
77
77
  const agentDir = getAgentDir();
@@ -89,6 +89,38 @@ function ensureDefaultRuntimeConfig() {
89
89
  writeFileSync(settingsPath, `${JSON.stringify(DEFAULT_SETTINGS_CONFIG, null, 2)}\n`, "utf-8");
90
90
  }
91
91
  }
92
+ function migrateAlemBaseUrls() {
93
+ const modelsPath = getModelsPath();
94
+ if (!existsSync(modelsPath))
95
+ return false;
96
+ try {
97
+ const parsed = JSON.parse(readFileSync(modelsPath, "utf-8"));
98
+ if (!parsed || typeof parsed !== "object" || !parsed.providers || typeof parsed.providers !== "object") {
99
+ return false;
100
+ }
101
+ const expectedProviders = ["alem-ai-plus-gpt-oss", "alem-ai-plus-qwen3-6", "alem-ai-plus-gemma4"];
102
+ let changed = false;
103
+ for (const providerName of expectedProviders) {
104
+ const provider = parsed.providers[providerName];
105
+ if (!provider || typeof provider !== "object") {
106
+ continue;
107
+ }
108
+ if (provider.baseUrl === "https://llm.alem.ai" ||
109
+ provider.baseUrl === "https://llm.alem.ai/chat/completions" ||
110
+ provider.baseUrl === "https://llm.alem.ai/v1/chat/completions") {
111
+ provider.baseUrl = "https://llm.alem.ai/v1";
112
+ changed = true;
113
+ }
114
+ }
115
+ if (changed) {
116
+ writeFileSync(modelsPath, `${JSON.stringify(parsed, null, 2)}\n`, "utf-8");
117
+ }
118
+ return changed;
119
+ }
120
+ catch {
121
+ return false;
122
+ }
123
+ }
92
124
  /**
93
125
  * Migrate legacy oauth.json and settings.json apiKeys to auth.json.
94
126
  *
@@ -353,6 +385,7 @@ export async function showDeprecationWarnings(warnings) {
353
385
  */
354
386
  export function runMigrations(cwd) {
355
387
  ensureDefaultRuntimeConfig();
388
+ migrateAlemBaseUrls();
356
389
  const migratedAuthProviders = migrateAuthToAuthJson();
357
390
  migrateSessionsFromAgentRoot();
358
391
  migrateToolsToBin();
@@ -2043,6 +2043,14 @@ export class InteractiveMode {
2043
2043
  await this.handleModelCommand(searchTerm);
2044
2044
  return;
2045
2045
  }
2046
+ if (text === "/apikey" ||
2047
+ text.startsWith("/apikey ") ||
2048
+ text === "/auth" ||
2049
+ text.startsWith("/auth ")) {
2050
+ this.editor.setText("");
2051
+ await this.handleApiKeyCommand(text);
2052
+ return;
2053
+ }
2046
2054
  if (text === "/thinking" || text.startsWith("/thinking ")) {
2047
2055
  this.editor.setText("");
2048
2056
  if (this.isBusyForModelOrThinkingChange()) {
@@ -3379,6 +3387,50 @@ export class InteractiveMode {
3379
3387
  this.session.modelRegistry.refresh();
3380
3388
  return true;
3381
3389
  }
3390
+ async handleApiKeyCommand(text) {
3391
+ const match = text.match(/^\/(?:apikey|auth)\s*(.*)$/);
3392
+ const requestedProvider = match?.[1]?.trim();
3393
+ let provider = requestedProvider || this.session.model?.provider;
3394
+ if (!requestedProvider) {
3395
+ const models = this.session.modelRegistry
3396
+ .getAll()
3397
+ .slice()
3398
+ .sort((a, b) => `${a.provider}/${a.id}`.localeCompare(`${b.provider}/${b.id}`));
3399
+ if (models.length === 0) {
3400
+ this.showStatus("No models available");
3401
+ return;
3402
+ }
3403
+ const options = models.map((model) => `${model.provider}/${model.id} - ${model.name || model.id}`);
3404
+ const selected = await this.showExtensionSelector("Choose model for API key update", options);
3405
+ if (!selected) {
3406
+ this.showStatus("API key update cancelled");
3407
+ return;
3408
+ }
3409
+ const selectedModel = models[options.indexOf(selected)];
3410
+ provider = selectedModel?.provider;
3411
+ }
3412
+ if (!provider) {
3413
+ this.showStatus("No active provider. Use /model first or pass a provider: /apikey <provider>");
3414
+ return;
3415
+ }
3416
+ const knownProviders = new Set(this.session.modelRegistry.getAll().map((model) => model.provider));
3417
+ if (!knownProviders.has(provider)) {
3418
+ this.showStatus(`Unknown provider: ${provider}`);
3419
+ return;
3420
+ }
3421
+ const apiKey = await this.showExtensionInput(`API key for ${provider}`, "sk-...");
3422
+ if (!apiKey || !apiKey.trim()) {
3423
+ this.showStatus("API key update cancelled");
3424
+ return;
3425
+ }
3426
+ this.session.modelRegistry.authStorage.set(provider, {
3427
+ type: "api_key",
3428
+ key: apiKey.trim(),
3429
+ });
3430
+ this.session.modelRegistry.refresh();
3431
+ this.footer.invalidate();
3432
+ this.showStatus(`API key updated for ${provider}`);
3433
+ }
3382
3434
  async showModelsSelector() {
3383
3435
  // Get all available models
3384
3436
  this.session.modelRegistry.refresh();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pzero-operator",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Operator is a coding-first terminal AI agent from ProjectZero for software development, shell execution, local project workflows, and broader device-level operator control.",