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 +12 -6
- package/dist/core/slash-commands.js +2 -0
- package/dist/migrations.js +39 -6
- package/dist/modes/interactive/interactive-mode.js +52 -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/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
|
|
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,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
|
+
"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.",
|