openpalm 0.10.2 → 0.11.0-beta.2
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 +11 -19
- package/package.json +4 -2
- package/src/commands/addon.ts +5 -4
- package/src/commands/automations.ts +63 -0
- package/src/commands/install.ts +98 -280
- package/src/commands/logs.ts +1 -1
- package/src/commands/restart.ts +5 -4
- package/src/commands/rollback.ts +4 -3
- package/src/commands/scan.ts +66 -32
- package/src/commands/service.ts +5 -4
- package/src/commands/start.ts +5 -4
- package/src/commands/status.ts +1 -1
- package/src/commands/stop.ts +2 -4
- package/src/commands/uninstall.ts +3 -5
- package/src/commands/update.ts +19 -2
- package/src/commands/validate.ts +16 -34
- package/src/install-flow.test.ts +153 -154
- package/src/lib/admin-skills/index.test.ts +70 -0
- package/src/lib/admin-skills/index.ts +113 -0
- package/src/lib/browser.ts +20 -0
- package/src/lib/cli-compose.ts +2 -20
- package/src/lib/cli-state.ts +1 -1
- package/src/lib/docker.ts +8 -214
- package/src/lib/env.ts +12 -83
- package/src/lib/io.ts +130 -0
- package/src/lib/opencode-subprocess.ts +14 -6
- package/src/lib/paths.ts +2 -2
- package/src/lib/ui-server.ts +150 -0
- package/src/main.test.ts +76 -173
- package/src/main.ts +131 -7
- package/e2e/start-wizard-server.ts +0 -59
- package/src/commands/admin.ts +0 -43
- package/src/commands/install-services.test.ts +0 -13
- package/src/commands/install-services.ts +0 -9
- package/src/commands/upgrade.ts +0 -12
- package/src/lib/embedded-assets.ts +0 -115
- package/src/lib/varlock.ts +0 -126
- package/src/setup-wizard/index.html +0 -321
- package/src/setup-wizard/server-errors.test.ts +0 -418
- package/src/setup-wizard/server-integration.test.ts +0 -511
- package/src/setup-wizard/server.test.ts +0 -508
- package/src/setup-wizard/server.ts +0 -342
- package/src/setup-wizard/wizard-renderers.js +0 -1294
- package/src/setup-wizard/wizard-state.js +0 -346
- package/src/setup-wizard/wizard-validators.js +0 -81
- package/src/setup-wizard/wizard.css +0 -1611
- package/src/setup-wizard/wizard.js +0 -613
package/src/commands/scan.ts
CHANGED
|
@@ -1,47 +1,81 @@
|
|
|
1
1
|
import { defineCommand } from 'citty';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { existsSync } from 'node:fs';
|
|
4
|
+
import { resolveStackDir } from '@openpalm/lib';
|
|
5
|
+
import { parseEnvFile, isSensitiveEnvKey } from '@openpalm/lib';
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* `openpalm scan` — list sensitive env keys that carry a non-empty value
|
|
9
|
+
* in the live vault env files. Replaces the varlock-based scanner; the
|
|
10
|
+
* canonical inventory now lives in `akm vault` and the operator-managed
|
|
11
|
+
* `.env` files. Exits non-zero only on filesystem errors, never on the
|
|
12
|
+
* mere presence of secrets (that is the expected state).
|
|
13
|
+
*
|
|
14
|
+
* Output formats:
|
|
15
|
+
* --format json (default) machine-readable JSON
|
|
16
|
+
* { "files": [{ "path": "...", "keys": [{ "name": "...", "set": true }] }] }
|
|
17
|
+
* --format human grouped, one line per key:
|
|
18
|
+
* # /path/to/file.env
|
|
19
|
+
* KEY_NAME set
|
|
20
|
+
*/
|
|
7
21
|
export default defineCommand({
|
|
8
22
|
meta: {
|
|
9
23
|
name: 'scan',
|
|
10
|
-
description: '
|
|
24
|
+
description: 'List vault env keys whose name matches the secret pattern (_TOKEN/_SECRET/_KEY/_PASSWORD/_HMAC)',
|
|
11
25
|
},
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
26
|
+
args: {
|
|
27
|
+
format: {
|
|
28
|
+
type: 'string',
|
|
29
|
+
description: 'Output format: json (default) or human',
|
|
30
|
+
default: 'json',
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
async run({ args }) {
|
|
34
|
+
const format = String(args.format ?? 'json').toLowerCase();
|
|
35
|
+
if (format !== 'json' && format !== 'human') {
|
|
36
|
+
console.error(`Unknown --format value: ${args.format}. Expected 'json' or 'human'.`);
|
|
37
|
+
process.exit(2);
|
|
23
38
|
}
|
|
24
39
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
)
|
|
29
|
-
|
|
30
|
-
}
|
|
40
|
+
const stackDir = resolveStackDir();
|
|
41
|
+
const targets = [
|
|
42
|
+
join(stackDir, 'stack.env'),
|
|
43
|
+
join(stackDir, 'guardian.env'),
|
|
44
|
+
];
|
|
31
45
|
|
|
32
|
-
|
|
46
|
+
type FileResult = { path: string; keys: Array<{ name: string; set: boolean }> };
|
|
47
|
+
const results: FileResult[] = [];
|
|
33
48
|
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
49
|
+
for (const path of targets) {
|
|
50
|
+
if (!existsSync(path)) continue;
|
|
51
|
+
const parsed = parseEnvFile(path);
|
|
52
|
+
const sensitive = Object.keys(parsed)
|
|
53
|
+
.filter((k) => isSensitiveEnvKey(k))
|
|
54
|
+
.sort();
|
|
55
|
+
if (sensitive.length === 0) continue;
|
|
56
|
+
results.push({
|
|
57
|
+
path,
|
|
58
|
+
keys: sensitive.map((name) => ({
|
|
59
|
+
name,
|
|
60
|
+
set: typeof parsed[name] === 'string' && parsed[name].length > 0,
|
|
61
|
+
})),
|
|
40
62
|
});
|
|
41
|
-
exitCode = await proc.exited;
|
|
42
|
-
} finally {
|
|
43
|
-
await rm(tmpDir, { recursive: true, force: true });
|
|
44
63
|
}
|
|
45
|
-
|
|
64
|
+
|
|
65
|
+
if (format === 'json') {
|
|
66
|
+
console.log(JSON.stringify({ files: results }));
|
|
67
|
+
} else {
|
|
68
|
+
if (results.length === 0) {
|
|
69
|
+
console.log('No vault env files found. Run `openpalm install` first.');
|
|
70
|
+
} else {
|
|
71
|
+
for (const file of results) {
|
|
72
|
+
console.log(`# ${file.path}`);
|
|
73
|
+
for (const key of file.keys) {
|
|
74
|
+
console.log(` ${key.name}\t${key.set ? 'set' : 'empty'}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
process.exit(0);
|
|
46
80
|
},
|
|
47
81
|
});
|
package/src/commands/service.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { defineCommand } from 'citty';
|
|
2
|
+
import { buildManagedServices } from '@openpalm/lib';
|
|
2
3
|
import { ensureValidState } from '../lib/cli-state.ts';
|
|
3
|
-
import {
|
|
4
|
+
import { runComposeWithPreflight, runComposeReadOnly } from '../lib/cli-compose.ts';
|
|
4
5
|
import { runLogsAction } from './logs.ts';
|
|
5
6
|
import { runStartAction } from './start.ts';
|
|
6
7
|
import { runStopAction } from './stop.ts';
|
|
@@ -41,8 +42,8 @@ const logsCmd = defineCommand({
|
|
|
41
42
|
const updateCmd = defineCommand({
|
|
42
43
|
meta: { name: 'update', description: 'Pull latest images' },
|
|
43
44
|
async run() {
|
|
44
|
-
const state =
|
|
45
|
-
const managedServices = await
|
|
45
|
+
const state = ensureValidState();
|
|
46
|
+
const managedServices = await buildManagedServices(state);
|
|
46
47
|
console.log('Pulling latest images...');
|
|
47
48
|
await runComposeWithPreflight(state, ['pull', ...managedServices]);
|
|
48
49
|
console.log('Recreating containers...');
|
|
@@ -54,7 +55,7 @@ const updateCmd = defineCommand({
|
|
|
54
55
|
const statusCmd = defineCommand({
|
|
55
56
|
meta: { name: 'status', description: 'Show container status' },
|
|
56
57
|
async run() {
|
|
57
|
-
const state =
|
|
58
|
+
const state = ensureValidState();
|
|
58
59
|
await runComposeReadOnly(state, ['ps', '--format', 'table']);
|
|
59
60
|
},
|
|
60
61
|
});
|
package/src/commands/start.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { defineCommand } from 'citty';
|
|
2
|
+
import { buildManagedServices } from '@openpalm/lib';
|
|
2
3
|
import { ensureValidState } from '../lib/cli-state.ts';
|
|
3
|
-
import {
|
|
4
|
+
import { runComposeWithPreflight } from '../lib/cli-compose.ts';
|
|
4
5
|
|
|
5
6
|
export default defineCommand({
|
|
6
7
|
meta: {
|
|
@@ -25,14 +26,14 @@ export async function runStartAction(
|
|
|
25
26
|
): Promise<void> {
|
|
26
27
|
if (services.length === 0) {
|
|
27
28
|
// Stage artifacts and start all managed services (admin included if enabled)
|
|
28
|
-
const state =
|
|
29
|
-
const managedServices = await
|
|
29
|
+
const state = ensureValidState();
|
|
30
|
+
const managedServices = await buildManagedServices(state);
|
|
30
31
|
await runComposeWithPreflight(state, ['up', '-d', ...managedServices]);
|
|
31
32
|
return;
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
// Start specific services
|
|
35
|
-
const state =
|
|
36
|
+
const state = ensureValidState();
|
|
36
37
|
for (const service of services) {
|
|
37
38
|
await runComposeWithPreflight(state, ['up', '-d', service]);
|
|
38
39
|
}
|
package/src/commands/status.ts
CHANGED
package/src/commands/stop.ts
CHANGED
|
@@ -22,14 +22,12 @@ export default defineCommand({
|
|
|
22
22
|
|
|
23
23
|
export async function runStopAction(services: string[]): Promise<void> {
|
|
24
24
|
if (services.length === 0) {
|
|
25
|
-
|
|
26
|
-
// so `down` tears down all services including admin/socket-proxy.
|
|
27
|
-
const state = await ensureValidState();
|
|
25
|
+
const state = ensureValidState();
|
|
28
26
|
await runComposeWithPreflight(state, ['down']);
|
|
29
27
|
return;
|
|
30
28
|
}
|
|
31
29
|
|
|
32
|
-
const state =
|
|
30
|
+
const state = ensureValidState();
|
|
33
31
|
for (const service of services) {
|
|
34
32
|
await runComposeWithPreflight(state, ['stop', service]);
|
|
35
33
|
}
|
|
@@ -2,7 +2,7 @@ import { defineCommand } from 'citty';
|
|
|
2
2
|
import { rmSync } from 'node:fs';
|
|
3
3
|
import { ensureValidState } from '../lib/cli-state.ts';
|
|
4
4
|
import { runComposeWithPreflight } from '../lib/cli-compose.ts';
|
|
5
|
-
import { resolveConfigDir,
|
|
5
|
+
import { resolveConfigDir, resolveStateDir, resolveStashDir, resolveWorkspaceDir } from '@openpalm/lib';
|
|
6
6
|
|
|
7
7
|
export default defineCommand({
|
|
8
8
|
meta: {
|
|
@@ -22,14 +22,12 @@ export default defineCommand({
|
|
|
22
22
|
},
|
|
23
23
|
},
|
|
24
24
|
async run({ args }) {
|
|
25
|
-
|
|
26
|
-
// so `down` tears down all services including admin/socket-proxy.
|
|
27
|
-
const state = await ensureValidState();
|
|
25
|
+
const state = ensureValidState();
|
|
28
26
|
const downArgs = args.volumes || args.purge ? ['down', '-v'] : ['down'];
|
|
29
27
|
await runComposeWithPreflight(state, downArgs);
|
|
30
28
|
|
|
31
29
|
if (args.purge) {
|
|
32
|
-
const dirs = [resolveConfigDir(),
|
|
30
|
+
const dirs = [resolveConfigDir(), resolveStateDir(), resolveStashDir(), resolveWorkspaceDir()];
|
|
33
31
|
for (const dir of dirs) {
|
|
34
32
|
console.log(`Removing ${dir}`);
|
|
35
33
|
rmSync(dir, { recursive: true, force: true });
|
package/src/commands/update.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { defineCommand } from 'citty';
|
|
2
|
-
import { performUpgrade } from '@openpalm/lib';
|
|
2
|
+
import { performUpgrade, checkAndUpdateUiBuild } from '@openpalm/lib';
|
|
3
3
|
import { ensureValidState } from '../lib/cli-state.ts';
|
|
4
4
|
|
|
5
5
|
export default defineCommand({
|
|
@@ -13,7 +13,7 @@ export default defineCommand({
|
|
|
13
13
|
});
|
|
14
14
|
|
|
15
15
|
export async function runUpgradeAction(): Promise<void> {
|
|
16
|
-
const state =
|
|
16
|
+
const state = ensureValidState();
|
|
17
17
|
|
|
18
18
|
console.log('Upgrading stack...');
|
|
19
19
|
const result = await performUpgrade(state);
|
|
@@ -21,5 +21,22 @@ export async function runUpgradeAction(): Promise<void> {
|
|
|
21
21
|
if (result.assetsUpdated.length > 0) {
|
|
22
22
|
console.log(`Assets updated: ${result.assetsUpdated.join(', ')}`);
|
|
23
23
|
}
|
|
24
|
+
|
|
25
|
+
// Check for a newer UI build on GitHub and install it if available.
|
|
26
|
+
// Passes the pre-upgrade image tag as the reference version so any newer
|
|
27
|
+
// release (including the one just upgraded to) triggers a download.
|
|
28
|
+
// Existing state/ui/ is backed up to state/backups/ui-{timestamp}/ before
|
|
29
|
+
// replacement. Non-fatal — existing build remains on any error.
|
|
30
|
+
const currentUiVersion = state.imageTag ?? '0.0.0';
|
|
31
|
+
console.log('Checking for UI build update...');
|
|
32
|
+
const uiResult = await checkAndUpdateUiBuild(currentUiVersion, state.stateDir);
|
|
33
|
+
if (uiResult.updated) {
|
|
34
|
+
console.log(`UI build updated to v${uiResult.latestVersion}.`);
|
|
35
|
+
} else if (uiResult.error) {
|
|
36
|
+
console.warn(`Warning: UI build update skipped — ${uiResult.error}. Existing build still active.`);
|
|
37
|
+
} else {
|
|
38
|
+
console.log(`UI build is current (v${uiResult.latestVersion}).`);
|
|
39
|
+
}
|
|
40
|
+
|
|
24
41
|
console.log('Update complete.');
|
|
25
42
|
}
|
package/src/commands/validate.ts
CHANGED
|
@@ -1,46 +1,28 @@
|
|
|
1
|
-
import { defineCommand } from
|
|
2
|
-
import {
|
|
3
|
-
import { rm } from 'node:fs/promises';
|
|
4
|
-
import { resolveVaultDir } from '@openpalm/lib';
|
|
5
|
-
import { ensureVarlock, prepareVarlockDir } from '../lib/varlock.ts';
|
|
1
|
+
import { defineCommand } from "citty";
|
|
2
|
+
import { createState, validateProposedState } from "@openpalm/lib";
|
|
6
3
|
|
|
7
4
|
export default defineCommand({
|
|
8
5
|
meta: {
|
|
9
|
-
name:
|
|
10
|
-
description:
|
|
6
|
+
name: "validate",
|
|
7
|
+
description: "Validate environment configuration (key presence + non-empty required slots)",
|
|
11
8
|
},
|
|
12
9
|
async run() {
|
|
13
|
-
|
|
10
|
+
// Use createState() directly — validateProposedState only needs vaultDir,
|
|
11
|
+
// not the resolved compose artifacts ensureValidState() would pull in.
|
|
12
|
+
const state = createState();
|
|
13
|
+
const result = await validateProposedState(state);
|
|
14
14
|
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
if (!(await Bun.file(primarySchema).exists())) {
|
|
19
|
-
console.error(
|
|
20
|
-
`Error: vault/user/user.env.schema not found at ${primarySchema}.\nRun 'openpalm install' first.`,
|
|
21
|
-
);
|
|
22
|
-
process.exit(1);
|
|
15
|
+
for (const warning of result.warnings) {
|
|
16
|
+
console.warn(warning);
|
|
23
17
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
console.error(
|
|
27
|
-
`Error: user.env not found at ${envPath}.\nRun 'openpalm install' first.`,
|
|
28
|
-
);
|
|
29
|
-
process.exit(1);
|
|
18
|
+
for (const err of result.errors) {
|
|
19
|
+
console.error(err);
|
|
30
20
|
}
|
|
31
21
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
try {
|
|
36
|
-
const proc = Bun.spawn(
|
|
37
|
-
[varlockBin, 'load', '--path', `${tmpDir}/`],
|
|
38
|
-
{ stdout: 'inherit', stderr: 'inherit' },
|
|
39
|
-
);
|
|
40
|
-
exitCode = await proc.exited;
|
|
41
|
-
} finally {
|
|
42
|
-
await rm(tmpDir, { recursive: true, force: true });
|
|
22
|
+
if (result.ok) {
|
|
23
|
+
console.log("Configuration OK.");
|
|
24
|
+
process.exit(0);
|
|
43
25
|
}
|
|
44
|
-
process.exit(
|
|
26
|
+
process.exit(1);
|
|
45
27
|
},
|
|
46
28
|
});
|