pzero-operator 0.1.3 → 0.1.5
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 +12 -6
- package/dist/core/slash-commands.js +2 -0
- package/dist/migrations.js +47 -6
- package/dist/modes/interactive/interactive-mode.js +60 -0
- package/package.json +1 -1
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
|
-
|
|
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:
|
|
56
|
-
- macOS:
|
|
57
|
-
- Windows:
|
|
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
|
|
95
|
-
4. if using `
|
|
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" },
|
package/dist/migrations.js
CHANGED
|
@@ -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
|
|
14
|
+
baseUrl: "https://llm.alem.ai",
|
|
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
|
|
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
|
|
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-
|
|
73
|
-
defaultModel: "
|
|
74
|
-
defaultThinkingLevel: "
|
|
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,46 @@ 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
|
+
let changed = false;
|
|
102
|
+
const gptOssProvider = parsed.providers["alem-ai-plus-gpt-oss"];
|
|
103
|
+
if (gptOssProvider && typeof gptOssProvider === "object") {
|
|
104
|
+
if (gptOssProvider.baseUrl === "https://llm.alem.ai/v1" ||
|
|
105
|
+
gptOssProvider.baseUrl === "https://llm.alem.ai/chat/completions" ||
|
|
106
|
+
gptOssProvider.baseUrl === "https://llm.alem.ai/v1/chat/completions") {
|
|
107
|
+
gptOssProvider.baseUrl = "https://llm.alem.ai";
|
|
108
|
+
changed = true;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
for (const providerName of ["alem-ai-plus-qwen3-6", "alem-ai-plus-gemma4"]) {
|
|
112
|
+
const provider = parsed.providers[providerName];
|
|
113
|
+
if (!provider || typeof provider !== "object") {
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
if (provider.baseUrl === "https://llm.alem.ai" ||
|
|
117
|
+
provider.baseUrl === "https://llm.alem.ai/chat/completions" ||
|
|
118
|
+
provider.baseUrl === "https://llm.alem.ai/v1/chat/completions") {
|
|
119
|
+
provider.baseUrl = "https://llm.alem.ai/v1";
|
|
120
|
+
changed = true;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (changed) {
|
|
124
|
+
writeFileSync(modelsPath, `${JSON.stringify(parsed, null, 2)}\n`, "utf-8");
|
|
125
|
+
}
|
|
126
|
+
return changed;
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
92
132
|
/**
|
|
93
133
|
* Migrate legacy oauth.json and settings.json apiKeys to auth.json.
|
|
94
134
|
*
|
|
@@ -353,6 +393,7 @@ export async function showDeprecationWarnings(warnings) {
|
|
|
353
393
|
*/
|
|
354
394
|
export function runMigrations(cwd) {
|
|
355
395
|
ensureDefaultRuntimeConfig();
|
|
396
|
+
migrateAlemBaseUrls();
|
|
356
397
|
const migratedAuthProviders = migrateAuthToAuthJson();
|
|
357
398
|
migrateSessionsFromAgentRoot();
|
|
358
399
|
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,58 @@ 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
|
+
.filter((model) => isAlemProvider(model.provider))
|
|
3398
|
+
.slice()
|
|
3399
|
+
.sort((a, b) => `${a.provider}/${a.id}`.localeCompare(`${b.provider}/${b.id}`));
|
|
3400
|
+
if (models.length === 0) {
|
|
3401
|
+
this.showStatus("No models available");
|
|
3402
|
+
return;
|
|
3403
|
+
}
|
|
3404
|
+
const options = models.map((model) => `${model.provider}/${model.id} - ${model.name || model.id}`);
|
|
3405
|
+
const selected = await this.showExtensionSelector("Choose model for API key update", options);
|
|
3406
|
+
if (!selected) {
|
|
3407
|
+
this.showStatus("API key update cancelled");
|
|
3408
|
+
return;
|
|
3409
|
+
}
|
|
3410
|
+
const selectedModel = models[options.indexOf(selected)];
|
|
3411
|
+
provider = selectedModel?.provider;
|
|
3412
|
+
}
|
|
3413
|
+
if (!provider) {
|
|
3414
|
+
this.showStatus("No active provider. Use /model first or pass a provider: /apikey <provider>");
|
|
3415
|
+
return;
|
|
3416
|
+
}
|
|
3417
|
+
if (!isAlemProvider(provider)) {
|
|
3418
|
+
this.showStatus("API key updates are available only for Alem AI Plus models in this build.");
|
|
3419
|
+
return;
|
|
3420
|
+
}
|
|
3421
|
+
const knownProviders = new Set(this.session.modelRegistry
|
|
3422
|
+
.getAll()
|
|
3423
|
+
.filter((model) => isAlemProvider(model.provider))
|
|
3424
|
+
.map((model) => model.provider));
|
|
3425
|
+
if (!knownProviders.has(provider)) {
|
|
3426
|
+
this.showStatus(`Unknown provider: ${provider}`);
|
|
3427
|
+
return;
|
|
3428
|
+
}
|
|
3429
|
+
const apiKey = await this.showExtensionInput(`API key for ${provider}`, "sk-...");
|
|
3430
|
+
if (!apiKey || !apiKey.trim()) {
|
|
3431
|
+
this.showStatus("API key update cancelled");
|
|
3432
|
+
return;
|
|
3433
|
+
}
|
|
3434
|
+
this.session.modelRegistry.authStorage.set(provider, {
|
|
3435
|
+
type: "api_key",
|
|
3436
|
+
key: apiKey.trim(),
|
|
3437
|
+
});
|
|
3438
|
+
this.session.modelRegistry.refresh();
|
|
3439
|
+
this.footer.invalidate();
|
|
3440
|
+
this.showStatus(`API key updated for ${provider}`);
|
|
3441
|
+
}
|
|
3382
3442
|
async showModelsSelector() {
|
|
3383
3443
|
// Get all available models
|
|
3384
3444
|
this.session.modelRegistry.refresh();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pzero-operator",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
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.",
|