@openpalm/lib 0.9.9 → 0.10.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 +31 -71
- package/package.json +1 -1
- package/src/control-plane/audit.ts +4 -4
- package/src/control-plane/backup.ts +31 -0
- package/src/control-plane/channels.ts +88 -156
- package/src/control-plane/cleanup-guardrails.test.ts +289 -0
- package/src/control-plane/compose-args.test.ts +170 -0
- package/src/control-plane/compose-args.ts +57 -0
- package/src/control-plane/config-persistence.ts +270 -0
- package/src/control-plane/core-assets.ts +58 -234
- package/src/control-plane/crypto.ts +14 -0
- package/src/control-plane/docker.ts +94 -204
- package/src/control-plane/env-schema-validation.test.ts +118 -0
- package/src/control-plane/extends-support.test.ts +105 -0
- package/src/control-plane/home.ts +133 -0
- package/src/control-plane/install-edge-cases.test.ts +314 -717
- package/src/control-plane/lifecycle.ts +215 -233
- package/src/control-plane/lock.test.ts +194 -0
- package/src/control-plane/lock.ts +176 -0
- package/src/control-plane/memory-config.ts +34 -160
- package/src/control-plane/opencode-client.test.ts +154 -0
- package/src/control-plane/opencode-client.ts +113 -0
- package/src/control-plane/provider-config.ts +34 -0
- package/src/control-plane/redact-schema.ts +50 -0
- package/src/control-plane/registry-components.test.ts +313 -0
- package/src/control-plane/registry.test.ts +414 -0
- package/src/control-plane/registry.ts +418 -0
- package/src/control-plane/rollback.ts +128 -0
- package/src/control-plane/scheduler.ts +18 -190
- package/src/control-plane/secret-backend.test.ts +359 -0
- package/src/control-plane/secret-backend.ts +322 -0
- package/src/control-plane/secret-mappings.ts +185 -0
- package/src/control-plane/secrets.ts +186 -112
- package/src/control-plane/setup-config.schema.json +306 -0
- package/src/control-plane/setup-status.ts +15 -8
- package/src/control-plane/setup-validation.ts +90 -0
- package/src/control-plane/setup.test.ts +336 -929
- package/src/control-plane/setup.ts +158 -886
- package/src/control-plane/spec-to-env.test.ts +100 -0
- package/src/control-plane/spec-to-env.ts +195 -0
- package/src/control-plane/spec-validator.ts +159 -0
- package/src/control-plane/stack-spec.test.ts +150 -0
- package/src/control-plane/stack-spec.ts +101 -22
- package/src/control-plane/types.ts +6 -99
- package/src/control-plane/validate.ts +107 -0
- package/src/index.ts +101 -159
- package/src/provider-constants.ts +2 -31
- package/src/control-plane/connection-mapping.ts +0 -191
- package/src/control-plane/connection-migration-flags.ts +0 -40
- package/src/control-plane/connection-profiles.ts +0 -317
- package/src/control-plane/core-asset-provider.ts +0 -21
- package/src/control-plane/fs-asset-provider.ts +0 -65
- package/src/control-plane/fs-registry-provider.ts +0 -46
- package/src/control-plane/paths.ts +0 -77
- package/src/control-plane/registry-provider.ts +0 -19
- package/src/control-plane/staging.ts +0 -399
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Verify that Compose `extends` is supported as an optional addon pattern.
|
|
3
|
+
*
|
|
4
|
+
* This is a narrow smoke test proving the canonical compose resolution
|
|
5
|
+
* works when an addon uses Compose `extends` to inherit from a base service.
|
|
6
|
+
*/
|
|
7
|
+
import { describe, test, expect, beforeAll, afterAll } from "bun:test";
|
|
8
|
+
import { mkdirSync, writeFileSync, rmSync, existsSync } from "node:fs";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { tmpdir } from "node:os";
|
|
11
|
+
|
|
12
|
+
describe("compose extends support", () => {
|
|
13
|
+
let fixtureDir: string;
|
|
14
|
+
const skipDockerAssertions = process.env.CI === "true";
|
|
15
|
+
|
|
16
|
+
beforeAll(() => {
|
|
17
|
+
fixtureDir = join(tmpdir(), `openpalm-extends-test-${Date.now()}`);
|
|
18
|
+
mkdirSync(join(fixtureDir, "stack/addons/extended-addon"), { recursive: true });
|
|
19
|
+
|
|
20
|
+
// Write a minimal core compose
|
|
21
|
+
writeFileSync(
|
|
22
|
+
join(fixtureDir, "stack/core.compose.yml"),
|
|
23
|
+
[
|
|
24
|
+
"services:",
|
|
25
|
+
" base-service:",
|
|
26
|
+
" image: alpine:latest",
|
|
27
|
+
" environment:",
|
|
28
|
+
" BASE_VAR: base-value",
|
|
29
|
+
"",
|
|
30
|
+
].join("\n")
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
// Write an addon that uses `extends`
|
|
34
|
+
writeFileSync(
|
|
35
|
+
join(fixtureDir, "stack/addons/extended-addon/compose.yml"),
|
|
36
|
+
[
|
|
37
|
+
"services:",
|
|
38
|
+
" extended-service:",
|
|
39
|
+
" extends:",
|
|
40
|
+
" service: base-service",
|
|
41
|
+
` file: ${join(fixtureDir, "stack/core.compose.yml")}`,
|
|
42
|
+
" environment:",
|
|
43
|
+
" ADDON_VAR: addon-value",
|
|
44
|
+
"",
|
|
45
|
+
].join("\n")
|
|
46
|
+
);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
afterAll(() => {
|
|
50
|
+
if (fixtureDir && existsSync(fixtureDir)) {
|
|
51
|
+
rmSync(fixtureDir, { recursive: true, force: true });
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("fixture files exist", () => {
|
|
56
|
+
expect(existsSync(join(fixtureDir, "stack/core.compose.yml"))).toBe(true);
|
|
57
|
+
expect(existsSync(join(fixtureDir, "stack/addons/extended-addon/compose.yml"))).toBe(true);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("extends addon composes correctly with discoverStackOverlays", async () => {
|
|
61
|
+
const { discoverStackOverlays } = await import("./config-persistence.js");
|
|
62
|
+
const overlays = discoverStackOverlays(join(fixtureDir, "stack"));
|
|
63
|
+
|
|
64
|
+
expect(overlays.length).toBe(2);
|
|
65
|
+
expect(overlays[0]).toContain("core.compose.yml");
|
|
66
|
+
expect(overlays[1]).toContain("extended-addon/compose.yml");
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test.skipIf(skipDockerAssertions)("extends addon passes docker compose config preflight (requires Docker)", async () => {
|
|
70
|
+
// This test validates that Compose `extends` actually merges correctly.
|
|
71
|
+
// Skipped when Docker is unavailable.
|
|
72
|
+
const { checkDocker, composePreflight } = await import("./docker.js");
|
|
73
|
+
const dockerCheck = await checkDocker();
|
|
74
|
+
if (!dockerCheck.ok) {
|
|
75
|
+
console.log(" [skip] Docker not available — extends preflight test skipped");
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const { discoverStackOverlays } = await import("./config-persistence.js");
|
|
80
|
+
const files = discoverStackOverlays(join(fixtureDir, "stack"));
|
|
81
|
+
|
|
82
|
+
const result = await composePreflight({ files });
|
|
83
|
+
expect(result.ok).toBe(true);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test.skipIf(skipDockerAssertions)("extends addon resolves services correctly via compose config (requires Docker)", async () => {
|
|
87
|
+
const { checkDocker, composeConfigServices } = await import("./docker.js");
|
|
88
|
+
const dockerCheck = await checkDocker();
|
|
89
|
+
if (!dockerCheck.ok) {
|
|
90
|
+
console.log(" [skip] Docker not available — extends service discovery test skipped");
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const { discoverStackOverlays } = await import("./config-persistence.js");
|
|
95
|
+
const files = discoverStackOverlays(join(fixtureDir, "stack"));
|
|
96
|
+
|
|
97
|
+
const result = await composeConfigServices({ files });
|
|
98
|
+
if (result.ok) {
|
|
99
|
+
// When Docker is available, the resolved service list should include
|
|
100
|
+
// both the base service and the extended service
|
|
101
|
+
expect(result.services).toContain("base-service");
|
|
102
|
+
expect(result.services).toContain("extended-service");
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
});
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Home directory layout for the OpenPalm control plane (v0.10.0+).
|
|
3
|
+
*
|
|
4
|
+
* Replaces the XDG three-tier model with a single ~/.openpalm/ root:
|
|
5
|
+
* config/ — user-editable, non-secret configuration
|
|
6
|
+
* vault/ — secrets boundary (user.env, system.env)
|
|
7
|
+
* data/ — service-managed persistent data
|
|
8
|
+
* logs/ — consolidated audit/debug output
|
|
9
|
+
*
|
|
10
|
+
* Cache and rollback data live in ~/.cache/openpalm/ (ephemeral).
|
|
11
|
+
*/
|
|
12
|
+
import { mkdirSync } from "node:fs";
|
|
13
|
+
import { homedir, tmpdir } from "node:os";
|
|
14
|
+
import { resolve as resolvePath } from "node:path";
|
|
15
|
+
|
|
16
|
+
// ── Path Resolution ──────────────────────────────────────────────────
|
|
17
|
+
|
|
18
|
+
export function resolveHome(): string {
|
|
19
|
+
const home = homedir();
|
|
20
|
+
if (home) return home;
|
|
21
|
+
|
|
22
|
+
return tmpdir();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function resolveOpenPalmHome(): string {
|
|
26
|
+
const raw = process.env.OP_HOME;
|
|
27
|
+
if (raw) return resolvePath(raw);
|
|
28
|
+
return `${resolveHome()}/.openpalm`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function resolveConfigDir(): string {
|
|
32
|
+
return `${resolveOpenPalmHome()}/config`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function resolveVaultDir(): string {
|
|
36
|
+
return `${resolveOpenPalmHome()}/vault`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function resolveDataDir(): string {
|
|
40
|
+
return `${resolveOpenPalmHome()}/data`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function resolveLogsDir(): string {
|
|
44
|
+
return `${resolveOpenPalmHome()}/logs`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function resolveCacheHome(): string {
|
|
48
|
+
return `${resolveHome()}/.cache/openpalm`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function resolveRollbackDir(): string {
|
|
52
|
+
return `${resolveCacheHome()}/rollback`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function resolveRegistryDir(): string {
|
|
56
|
+
return `${resolveOpenPalmHome()}/registry`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function resolveRegistryAddonsDir(): string {
|
|
60
|
+
return `${resolveRegistryDir()}/addons`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function resolveRegistryAutomationsDir(): string {
|
|
64
|
+
return `${resolveRegistryDir()}/automations`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function resolveStackDir(): string {
|
|
68
|
+
return `${resolveOpenPalmHome()}/stack`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function resolveBackupsDir(): string {
|
|
72
|
+
return `${resolveOpenPalmHome()}/backups`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function resolveWorkspaceDir(): string {
|
|
76
|
+
return `${resolveOpenPalmHome()}/data/workspace`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ── Directory Setup ──────────────────────────────────────────────────
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Create the full ~/.openpalm/ directory tree and cache directories.
|
|
83
|
+
*/
|
|
84
|
+
export function ensureHomeDirs(): void {
|
|
85
|
+
const home = resolveOpenPalmHome();
|
|
86
|
+
const cache = resolveCacheHome();
|
|
87
|
+
|
|
88
|
+
for (const dir of [
|
|
89
|
+
// config/ — user-editable, non-secret
|
|
90
|
+
`${home}/config`,
|
|
91
|
+
`${home}/config/automations`,
|
|
92
|
+
`${home}/config/assistant`,
|
|
93
|
+
`${home}/config/guardian`,
|
|
94
|
+
|
|
95
|
+
// vault/ — secrets boundary
|
|
96
|
+
`${home}/vault`,
|
|
97
|
+
`${home}/vault/stack`,
|
|
98
|
+
`${home}/vault/user`,
|
|
99
|
+
|
|
100
|
+
// data/ — service-managed persistent data
|
|
101
|
+
`${home}/data`,
|
|
102
|
+
`${home}/data/assistant`,
|
|
103
|
+
`${home}/data/admin`,
|
|
104
|
+
`${home}/data/memory`,
|
|
105
|
+
`${home}/data/guardian`,
|
|
106
|
+
`${home}/data/stash`,
|
|
107
|
+
|
|
108
|
+
// stack/ — compose files
|
|
109
|
+
`${home}/stack`,
|
|
110
|
+
`${home}/stack/addons`,
|
|
111
|
+
|
|
112
|
+
// registry/ — available catalog
|
|
113
|
+
`${home}/registry`,
|
|
114
|
+
`${home}/registry/addons`,
|
|
115
|
+
`${home}/registry/automations`,
|
|
116
|
+
|
|
117
|
+
// backups/ — user backups
|
|
118
|
+
`${home}/backups`,
|
|
119
|
+
|
|
120
|
+
// data/workspace/ — shared assistant workspace (compose: $OP_HOME/data/workspace:/work)
|
|
121
|
+
`${home}/data/workspace`,
|
|
122
|
+
|
|
123
|
+
// logs/ — consolidated audit/debug
|
|
124
|
+
`${home}/logs`,
|
|
125
|
+
`${home}/logs/opencode`,
|
|
126
|
+
|
|
127
|
+
// cache/ — ephemeral, regenerable
|
|
128
|
+
cache,
|
|
129
|
+
`${cache}/rollback`,
|
|
130
|
+
]) {
|
|
131
|
+
mkdirSync(dir, { recursive: true });
|
|
132
|
+
}
|
|
133
|
+
}
|