openpalm 0.11.0-beta.1 → 0.11.0-beta.3
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/package.json +2 -2
- package/src/commands/install.ts +5 -3
- package/src/install-flow.test.ts +1 -1
- package/src/lib/ui-server.ts +14 -11
- package/src/main.test.ts +11 -11
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openpalm",
|
|
3
|
-
"version": "0.11.0-beta.
|
|
3
|
+
"version": "0.11.0-beta.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MPL-2.0",
|
|
6
6
|
"description": "OpenPalm CLI — install and manage a self-hosted OpenPalm stack",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"build:windows-arm64": "bun build src/main.ts --compile --target=bun-windows-arm64 --outfile dist/openpalm-cli-windows-arm64.exe"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@openpalm/lib": "0.11.0",
|
|
31
|
+
"@openpalm/lib": "0.11.0-beta.1",
|
|
32
32
|
"citty": "^0.2.1",
|
|
33
33
|
"yaml": "^2.8.0"
|
|
34
34
|
}
|
package/src/commands/install.ts
CHANGED
|
@@ -285,10 +285,12 @@ async function runFileInstall(filePath: string, noStart: boolean): Promise<void>
|
|
|
285
285
|
throw new Error('Setup config must contain a "capabilities" object (llm, embeddings).');
|
|
286
286
|
}
|
|
287
287
|
|
|
288
|
-
// Resolve security.
|
|
288
|
+
// Resolve security.uiLoginPassword from environment when not in spec.
|
|
289
|
+
// Phase 4 (auth/proxy refactor) renamed the env var to OP_UI_LOGIN_PASSWORD
|
|
290
|
+
// and the spec field to security.uiLoginPassword.
|
|
289
291
|
const security = (config.security ?? {}) as Record<string, unknown>;
|
|
290
|
-
if (!security.
|
|
291
|
-
security.
|
|
292
|
+
if (!security.uiLoginPassword && process.env.OP_UI_LOGIN_PASSWORD) {
|
|
293
|
+
security.uiLoginPassword = process.env.OP_UI_LOGIN_PASSWORD;
|
|
292
294
|
config.security = security;
|
|
293
295
|
}
|
|
294
296
|
|
package/src/install-flow.test.ts
CHANGED
|
@@ -105,7 +105,7 @@ function makeSetupSpec(): Record<string, unknown> {
|
|
|
105
105
|
version: 2,
|
|
106
106
|
llm: { provider: 'ollama', model: 'qwen2.5-coder:3b', baseUrl: 'http://host.docker.internal:11434' },
|
|
107
107
|
embedding: { provider: 'ollama', model: 'nomic-embed-text:latest', dims: 768, baseUrl: 'http://host.docker.internal:11434' },
|
|
108
|
-
security: {
|
|
108
|
+
security: { uiLoginPassword: 'test-admin-token-12345' },
|
|
109
109
|
owner: { name: 'Test', email: 'test@test.com' },
|
|
110
110
|
connections: [{
|
|
111
111
|
id: 'ollama',
|
package/src/lib/ui-server.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { join } from 'node:path';
|
|
10
10
|
import { existsSync } from 'node:fs';
|
|
11
|
-
import { resolveOpenPalmHome, resolveConfigDir, resolveUiBuildDir, createLogger } from '@openpalm/lib';
|
|
11
|
+
import { resolveOpenPalmHome, resolveConfigDir, resolveUiBuildDir, createLogger, readStackEnv } from '@openpalm/lib';
|
|
12
12
|
import { ensureValidState } from './cli-state.ts';
|
|
13
13
|
import { startOpenCodeSubprocess, type OpenCodeSubprocess } from './opencode-subprocess.ts';
|
|
14
14
|
import { openBrowser } from './browser.ts';
|
|
@@ -63,11 +63,14 @@ export async function startUIServer(opts: UIServerOptions = {}): Promise<void> {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
const state = ensureValidState();
|
|
66
|
-
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
|
|
70
|
-
|
|
66
|
+
// OP_UI_LOGIN_PASSWORD is unset during first-run install — the SvelteKit
|
|
67
|
+
// hooks detect that and redirect /* to /setup, where the wizard sets
|
|
68
|
+
// it. Don't short-circuit here, or the install wizard can never come up.
|
|
69
|
+
const stackEnv = readStackEnv(state.stackDir);
|
|
70
|
+
const uiLoginPassword =
|
|
71
|
+
process.env.OP_UI_LOGIN_PASSWORD
|
|
72
|
+
?? stackEnv.OP_UI_LOGIN_PASSWORD
|
|
73
|
+
?? '';
|
|
71
74
|
|
|
72
75
|
// Start OpenCode subprocess (non-fatal — UI still works without it)
|
|
73
76
|
let openCodeSub: OpenCodeSubprocess | null = null;
|
|
@@ -99,11 +102,11 @@ export async function startUIServer(opts: UIServerOptions = {}): Promise<void> {
|
|
|
99
102
|
// Pass resolved absolute OP_HOME so the child doesn't re-resolve a
|
|
100
103
|
// relative value (e.g. `.dev` from a repo-root .env) against its
|
|
101
104
|
// own cwd (packages/ui/build/).
|
|
102
|
-
OP_HOME:
|
|
103
|
-
HOST:
|
|
104
|
-
PORT:
|
|
105
|
-
ORIGIN:
|
|
106
|
-
|
|
105
|
+
OP_HOME: homeDir,
|
|
106
|
+
HOST: '127.0.0.1',
|
|
107
|
+
PORT: String(port),
|
|
108
|
+
ORIGIN: `http://127.0.0.1:${port}`,
|
|
109
|
+
OP_UI_LOGIN_PASSWORD: uiLoginPassword,
|
|
107
110
|
...(openCodeBaseUrl ? { OP_OPENCODE_URL: openCodeBaseUrl } : {}),
|
|
108
111
|
},
|
|
109
112
|
stdout: 'inherit',
|
package/src/main.test.ts
CHANGED
|
@@ -19,7 +19,7 @@ function writeMinimalSetupSpec(dir: string): string {
|
|
|
19
19
|
' model: text-embedding-3-small',
|
|
20
20
|
' dims: 1536',
|
|
21
21
|
'security:',
|
|
22
|
-
'
|
|
22
|
+
' uiLoginPassword: test-admin-token-12345',
|
|
23
23
|
'owner:',
|
|
24
24
|
' name: Test User',
|
|
25
25
|
' email: test@example.com',
|
|
@@ -113,7 +113,7 @@ describe('cli main', () => {
|
|
|
113
113
|
const originalWarn = console.warn;
|
|
114
114
|
const originalHome = process.env.OP_HOME;
|
|
115
115
|
const originalWorkDir = process.env.OP_WORK_DIR;
|
|
116
|
-
const
|
|
116
|
+
const originalLoginPassword = process.env.OP_UI_LOGIN_PASSWORD;
|
|
117
117
|
|
|
118
118
|
afterEach(() => {
|
|
119
119
|
globalThis.fetch = originalFetch;
|
|
@@ -122,7 +122,7 @@ describe('cli main', () => {
|
|
|
122
122
|
restoreDockerCli();
|
|
123
123
|
process.env.OP_HOME = originalHome;
|
|
124
124
|
process.env.OP_WORK_DIR = originalWorkDir;
|
|
125
|
-
process.env.
|
|
125
|
+
process.env.OP_UI_LOGIN_PASSWORD = originalLoginPassword;
|
|
126
126
|
});
|
|
127
127
|
|
|
128
128
|
it('runs bootstrap install directly without admin delegation', async () => {
|
|
@@ -133,7 +133,7 @@ describe('cli main', () => {
|
|
|
133
133
|
|
|
134
134
|
process.env.OP_HOME = base;
|
|
135
135
|
process.env.OP_WORK_DIR = workDir;
|
|
136
|
-
delete process.env.
|
|
136
|
+
delete process.env.OP_UI_LOGIN_PASSWORD;
|
|
137
137
|
|
|
138
138
|
mockDockerCli();
|
|
139
139
|
const fetchedUrls: string[] = [];
|
|
@@ -255,7 +255,7 @@ describe('cli main', () => {
|
|
|
255
255
|
// carries forward existing content.
|
|
256
256
|
mkdirSync(join(base, 'state'), { recursive: true });
|
|
257
257
|
mkdirSync(join(base, 'config', 'stack'), { recursive: true });
|
|
258
|
-
writeFileSync(join(base, 'config', 'stack', 'stack.env'), '
|
|
258
|
+
writeFileSync(join(base, 'config', 'stack', 'stack.env'), 'OP_UI_LOGIN_PASSWORD=existing-password\n');
|
|
259
259
|
writeFileSync(stackConfig, 'llm: old\n');
|
|
260
260
|
|
|
261
261
|
process.env.OP_HOME = base;
|
|
@@ -284,7 +284,7 @@ describe('cli main', () => {
|
|
|
284
284
|
const backups = readdirSync(backupsDir);
|
|
285
285
|
expect(backups.length).toBeGreaterThan(0);
|
|
286
286
|
expect(readFileSync(join(backupsDir, backups[0], 'config', 'stack.yml'), 'utf8')).toContain('llm: old');
|
|
287
|
-
expect(readFileSync(join(backupsDir, backups[0], 'config', 'stack', 'stack.env'), 'utf8')).toContain('
|
|
287
|
+
expect(readFileSync(join(backupsDir, backups[0], 'config', 'stack', 'stack.env'), 'utf8')).toContain('OP_UI_LOGIN_PASSWORD=existing-password');
|
|
288
288
|
} finally {
|
|
289
289
|
rmSync(base, { recursive: true, force: true });
|
|
290
290
|
}
|
|
@@ -304,7 +304,7 @@ describe('cli main', () => {
|
|
|
304
304
|
mkdirSync(chatAddonDir, { recursive: true });
|
|
305
305
|
writeFileSync(coreCompose, 'services:\n assistant:\n image: test\n');
|
|
306
306
|
writeFileSync(join(adminAddonDir, 'compose.yml'), 'services:\n admin:\n image: admin\n');
|
|
307
|
-
writeFileSync(join(adminAddonDir, '.env.schema'), '
|
|
307
|
+
writeFileSync(join(adminAddonDir, '.env.schema'), 'OP_UI_LOGIN_PASSWORD=\n');
|
|
308
308
|
writeFileSync(join(chatAddonDir, 'compose.yml'), 'services:\n chat:\n image: chat\n environment:\n CHANNEL_NAME: "Chat"\n CHANNEL_ID: "chat"\n');
|
|
309
309
|
writeFileSync(join(chatAddonDir, '.env.schema'), 'CHANNEL_CHAT_SECRET=\n');
|
|
310
310
|
writeFileSync(guardianEnv, '# Guardian channel HMAC secrets — managed by openpalm\n');
|
|
@@ -411,7 +411,7 @@ describe('validate command', () => {
|
|
|
411
411
|
const tempHome = mkdtempSync(join(tmpdir(), 'openpalm-test-'));
|
|
412
412
|
const stackDir = join(tempHome, 'config', 'stack');
|
|
413
413
|
mkdirSync(stackDir, { recursive: true });
|
|
414
|
-
writeFileSync(join(stackDir, 'stack.env'), '
|
|
414
|
+
writeFileSync(join(stackDir, 'stack.env'), 'OP_UI_LOGIN_PASSWORD=abc\n');
|
|
415
415
|
|
|
416
416
|
const originalHome = process.env.OP_HOME;
|
|
417
417
|
const originalExit = process.exit;
|
|
@@ -436,7 +436,7 @@ describe('scan command', () => {
|
|
|
436
436
|
const tempHome = mkdtempSync(join(tmpdir(), 'openpalm-test-'));
|
|
437
437
|
const stackDir = join(tempHome, 'config', 'stack');
|
|
438
438
|
mkdirSync(stackDir, { recursive: true });
|
|
439
|
-
writeFileSync(join(stackDir, 'stack.env'), '
|
|
439
|
+
writeFileSync(join(stackDir, 'stack.env'), 'OP_UI_LOGIN_PASSWORD=abc\nOPENAI_API_KEY=sk-test\n');
|
|
440
440
|
|
|
441
441
|
const originalHome = process.env.OP_HOME;
|
|
442
442
|
const originalExit = process.exit;
|
|
@@ -553,8 +553,8 @@ describe('install image tag pinning', () => {
|
|
|
553
553
|
});
|
|
554
554
|
|
|
555
555
|
it('preserves export prefix when upserting a key', () => {
|
|
556
|
-
expect(upsertEnvValue('export
|
|
557
|
-
'export
|
|
556
|
+
expect(upsertEnvValue('export OP_UI_LOGIN_PASSWORD=old\n', 'OP_UI_LOGIN_PASSWORD', 'new')).toBe(
|
|
557
|
+
'export OP_UI_LOGIN_PASSWORD=new\n',
|
|
558
558
|
);
|
|
559
559
|
});
|
|
560
560
|
|