openpalm 0.9.5 → 0.9.7

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.
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Bun-only launcher for the CLI setup wizard server.
3
+ *
4
+ * Called from Playwright tests as a child process:
5
+ * bun run packages/cli/e2e/start-wizard-server.ts <port>
6
+ *
7
+ * Starts the wizard server on the given port with a temp config directory
8
+ * so tests do not affect real dev state. Prints "WIZARD_READY:<port>" to
9
+ * stdout when listening, which the Playwright test waits for.
10
+ */
11
+ import { createSetupServer } from "../src/setup-wizard/server.ts";
12
+ import { mkdirSync, writeFileSync } from "node:fs";
13
+ import type { CoreAssetProvider } from "@openpalm/lib";
14
+
15
+ const port = parseInt(Bun.argv[2] || "18100", 10);
16
+ const tmpBase = `/tmp/openpalm-wizard-test-${port}`;
17
+
18
+ // Create minimal directory structure so the server can start.
19
+ // API endpoints that need real files are mocked at the browser level
20
+ // by Playwright's page.route(), so these dirs just prevent crashes.
21
+ mkdirSync(`${tmpBase}/config`, { recursive: true });
22
+ mkdirSync(`${tmpBase}/data`, { recursive: true });
23
+ mkdirSync(`${tmpBase}/state/artifacts`, { recursive: true });
24
+
25
+ writeFileSync(`${tmpBase}/config/secrets.env`, "# test\n");
26
+ writeFileSync(`${tmpBase}/state/artifacts/stack.env`, "OPENPALM_SETUP_COMPLETE=false\n");
27
+
28
+ // No-op asset provider — mocked tests intercept API calls before they
29
+ // reach performSetup(), so these methods are never invoked.
30
+ const noopAssetProvider: CoreAssetProvider = {
31
+ coreCompose: () => "",
32
+ caddyfile: () => "",
33
+ ollamaCompose: () => "",
34
+ agentsMd: () => "",
35
+ opencodeConfig: () => "",
36
+ adminOpencodeConfig: () => "",
37
+ secretsSchema: () => "",
38
+ stackSchema: () => "",
39
+ cleanupLogs: () => "",
40
+ cleanupData: () => "",
41
+ validateConfig: () => "",
42
+ };
43
+
44
+ // Override state/config home so the server doesn't touch real dirs.
45
+ process.env.OPENPALM_CONFIG_HOME = `${tmpBase}/config`;
46
+ process.env.OPENPALM_STATE_HOME = `${tmpBase}/state`;
47
+ process.env.OPENPALM_DATA_HOME = `${tmpBase}/data`;
48
+
49
+ const { server } = createSetupServer(port, {
50
+ configDir: `${tmpBase}/config`,
51
+ assetProvider: noopAssetProvider,
52
+ });
53
+
54
+ console.log(`WIZARD_READY:${port}`);
55
+
56
+ // Keep alive until killed
57
+ process.on("SIGTERM", () => {
58
+ server.stop();
59
+ process.exit(0);
60
+ });
61
+ process.on("SIGINT", () => {
62
+ server.stop();
63
+ process.exit(0);
64
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openpalm",
3
- "version": "0.9.5",
3
+ "version": "0.9.7",
4
4
  "type": "module",
5
5
  "license": "MPL-2.0",
6
6
  "description": "OpenPalm CLI — install and manage a self-hosted OpenPalm stack",
@@ -24,7 +24,7 @@
24
24
  "build:windows-arm64": "bun build src/main.ts --compile --target=bun-windows-arm64 --outfile dist/openpalm-cli-windows-arm64.exe"
25
25
  },
26
26
  "dependencies": {
27
- "@openpalm/lib": "workspace:*",
27
+ "@openpalm/lib": "0.9.6",
28
28
  "citty": "^0.2.1"
29
29
  }
30
30
  }
@@ -0,0 +1,24 @@
1
+ import { describe, expect, it } from 'bun:test';
2
+ import { buildDeployStatusEntries, buildInstallServiceNames } from './install-services.ts';
3
+
4
+ describe('install service helpers', () => {
5
+ it('appends admin services to managed services', () => {
6
+ expect(buildInstallServiceNames(['caddy', 'memory'])).toEqual([
7
+ 'caddy',
8
+ 'memory',
9
+ 'admin',
10
+ 'docker-socket-proxy',
11
+ ]);
12
+ });
13
+
14
+ it('builds deploy status entries for the full install service list', () => {
15
+ const services = buildInstallServiceNames(['caddy', 'memory']);
16
+
17
+ expect(buildDeployStatusEntries(services, 'pending', 'Waiting...')).toEqual([
18
+ { service: 'caddy', status: 'pending', label: 'Waiting...' },
19
+ { service: 'memory', status: 'pending', label: 'Waiting...' },
20
+ { service: 'admin', status: 'pending', label: 'Waiting...' },
21
+ { service: 'docker-socket-proxy', status: 'pending', label: 'Waiting...' },
22
+ ]);
23
+ });
24
+ });
@@ -0,0 +1,13 @@
1
+ export type DeployStatusState = 'pending' | 'pulling';
2
+
3
+ export function buildInstallServiceNames(managedServices: string[]): string[] {
4
+ return [...managedServices, 'admin', 'docker-socket-proxy'];
5
+ }
6
+
7
+ export function buildDeployStatusEntries(
8
+ services: string[],
9
+ status: DeployStatusState,
10
+ label: string,
11
+ ): Array<{ service: string; status: DeployStatusState; label: string }> {
12
+ return services.map(service => ({ service, status, label }));
13
+ }
@@ -12,6 +12,7 @@ import { detectHostInfo } from '../lib/host-info.ts';
12
12
  import { loadAdminToken } from '../lib/env.ts';
13
13
  import { ensureStagedState, fullComposeArgs, buildManagedServiceNames } from '../lib/staging.ts';
14
14
  import { createSetupServer } from '../setup-wizard/server.ts';
15
+ import { buildInstallServiceNames, buildDeployStatusEntries } from './install-services.ts';
15
16
 
16
17
  const DEFAULT_INSTALL_REF = cliPkg.version ? `v${cliPkg.version}` : 'main';
17
18
  const SETUP_WIZARD_PORT = 8100;
@@ -220,27 +221,24 @@ export async function bootstrapInstall(options: InstallOptions): Promise<void> {
220
221
  const state = await ensureStagedState();
221
222
  const composeArgs = fullComposeArgs(state);
222
223
  const managedServices = buildManagedServiceNames(state);
224
+ const allServices = buildInstallServiceNames(managedServices);
223
225
 
224
- wizard.updateDeployStatus(
225
- managedServices.map(s => ({ service: s, status: 'pending', label: 'Waiting...' })),
226
- );
226
+ wizard.updateDeployStatus(buildDeployStatusEntries(allServices, 'pending', 'Waiting...'));
227
227
 
228
- await runDockerCompose([...composeArgs, 'pull', ...managedServices]).catch(() => {
228
+ await runDockerCompose([...composeArgs, '--profile', 'admin', 'pull', ...allServices]).catch(() => {
229
229
  // Pull failure is non-fatal — images may already be cached
230
230
  });
231
231
 
232
- wizard.updateDeployStatus(
233
- managedServices.map(s => ({ service: s, status: 'pulling', label: 'Starting...' })),
234
- );
232
+ wizard.updateDeployStatus(buildDeployStatusEntries(allServices, 'pulling', 'Starting...'));
235
233
 
236
- await runDockerCompose([...composeArgs, 'up', '-d', ...managedServices]);
234
+ await runDockerCompose([...composeArgs, '--profile', 'admin', 'up', '-d', ...allServices]);
237
235
 
238
236
  wizard.markAllRunning();
239
237
 
240
238
  console.log(JSON.stringify({
241
239
  ok: true,
242
240
  mode: 'install',
243
- services: managedServices,
241
+ services: allServices,
244
242
  }, null, 2));
245
243
 
246
244
  // Give the browser a moment to poll the final status, then stop
@@ -264,12 +262,13 @@ export async function bootstrapInstall(options: InstallOptions): Promise<void> {
264
262
  const state = await ensureStagedState();
265
263
  const composeArgs = fullComposeArgs(state);
266
264
  const managedServices = buildManagedServiceNames(state);
265
+ const allServices = buildInstallServiceNames(managedServices);
267
266
 
268
- await runDockerCompose([...composeArgs, 'up', '-d', ...managedServices]);
267
+ await runDockerCompose([...composeArgs, '--profile', 'admin', 'up', '-d', ...allServices]);
269
268
 
270
269
  console.log(JSON.stringify({
271
270
  ok: true,
272
271
  mode: 'update',
273
- services: managedServices,
272
+ services: allServices,
274
273
  }, null, 2));
275
274
  }
@@ -16,8 +16,8 @@ export default defineCommand({
16
16
  },
17
17
  'with-admin': {
18
18
  type: 'boolean',
19
- description: 'Include admin UI and docker-socket-proxy',
20
- default: false,
19
+ description: 'Include admin UI and docker-socket-proxy (use --no-with-admin to skip)',
20
+ default: true,
21
21
  },
22
22
  },
23
23
  async run({ args }) {