@openpalm/lib 0.11.0-beta.14 → 0.11.0-beta.16
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 +1 -1
- package/src/control-plane/akm-user-env.ts +1 -1
- package/src/control-plane/config-persistence.ts +3 -3
- package/src/control-plane/home.ts +1 -2
- package/src/control-plane/install-edge-cases.test.ts +0 -2
- package/src/control-plane/lifecycle.ts +5 -1
- package/src/control-plane/opencode-client.ts +1 -1
- package/src/control-plane/paths.ts +1 -4
- package/src/control-plane/setup.ts +1 -7
- package/src/control-plane/skeleton-guardrail.test.ts +1 -1
- package/src/control-plane/spec-to-env.test.ts +7 -0
- package/src/control-plane/spec-to-env.ts +1 -2
- package/src/control-plane/stack-spec.ts +1 -3
package/package.json
CHANGED
|
@@ -56,7 +56,7 @@ const ENV_FILE_MODE = 0o600;
|
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
58
|
* Build the env that points akm at the shared OpenPalm stash. We mirror the
|
|
59
|
-
* layout that the assistant
|
|
59
|
+
* layout that the assistant container uses (see
|
|
60
60
|
* `.openpalm/config/stack/core.compose.yml`) so host-side and container-side
|
|
61
61
|
* runs resolve to the same files.
|
|
62
62
|
*
|
|
@@ -14,6 +14,7 @@ import { ensureSecret } from './secrets-files.js';
|
|
|
14
14
|
import type { ControlPlaneState, ArtifactMeta } from "./types.js";
|
|
15
15
|
import { listEnabledAddonIds } from "./registry.js";
|
|
16
16
|
import { resolveOperatorIds, hasUsableOperatorId } from "./operator-ids.js";
|
|
17
|
+
import { SPEC_DEFAULTS } from "./stack-spec.js";
|
|
17
18
|
|
|
18
19
|
import {
|
|
19
20
|
readCoreCompose,
|
|
@@ -130,9 +131,8 @@ function generateFallbackSystemEnv(state: ControlPlaneState): string {
|
|
|
130
131
|
"# ── Ports (38XX range) ──────────────────────────────────────────────",
|
|
131
132
|
"# Guardian is network-only (no host port) — channels reach it via",
|
|
132
133
|
"# http://guardian:8080 over the channel_lan Docker network.",
|
|
133
|
-
`OP_ASSISTANT_PORT
|
|
134
|
-
`
|
|
135
|
-
`OP_ADMIN_OPENCODE_PORT=3881`,
|
|
134
|
+
`OP_ASSISTANT_PORT=${SPEC_DEFAULTS.ports.assistant}`,
|
|
135
|
+
`OP_HOST_UI_PORT=${SPEC_DEFAULTS.ports.hostUi}`,
|
|
136
136
|
""
|
|
137
137
|
].join("\n");
|
|
138
138
|
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* config/ — user-editable config + system config files (akm/)
|
|
6
6
|
* config/stack/ — compose runtime + stack config (stack.env, stack.yml, auth.json, fixed compose files)
|
|
7
7
|
* data/ — persistent service data, logs, backups, rollback
|
|
8
|
-
* knowledge/ — akm knowledge (
|
|
8
|
+
* knowledge/ — akm knowledge (env, secrets, tasks)
|
|
9
9
|
* workspace/ — shared assistant work area
|
|
10
10
|
* config/stack/ — compose runtime assets + stack config (stack.env, stack.yml)
|
|
11
11
|
*/
|
|
@@ -94,7 +94,6 @@ export function ensureHomeDirs(): void {
|
|
|
94
94
|
`${home}/data/assistant/.local/bin`,
|
|
95
95
|
`${home}/data/assistant/.local/share/opencode`,
|
|
96
96
|
`${home}/data/assistant/.local/state/opencode`,
|
|
97
|
-
`${home}/data/admin`, // admin home bind mount
|
|
98
97
|
`${home}/data/guardian`, // guardian runtime data
|
|
99
98
|
`${home}/data/akm/cache`, // akm cache
|
|
100
99
|
`${home}/data/akm/data`, // akm durable data
|
|
@@ -23,7 +23,6 @@ import {
|
|
|
23
23
|
performSetup,
|
|
24
24
|
buildSecretsFromSetup,
|
|
25
25
|
buildAuthJsonFromSetup,
|
|
26
|
-
buildSystemSecretsFromSetup,
|
|
27
26
|
} from "./setup.js";
|
|
28
27
|
import type { SetupSpec, SetupConnection } from "./setup.js";
|
|
29
28
|
import type { ControlPlaneState } from "./types.js";
|
|
@@ -113,7 +112,6 @@ function createFullDirTree(): void {
|
|
|
113
112
|
stackDir,
|
|
114
113
|
dataDir,
|
|
115
114
|
join(dataDir, "assistant"),
|
|
116
|
-
join(dataDir, "admin"),
|
|
117
115
|
join(dataDir, "guardian"),
|
|
118
116
|
join(dataDir, "akm", "cache"),
|
|
119
117
|
join(dataDir, "akm", "data"),
|
|
@@ -184,10 +184,14 @@ export async function updateStackEnvToLatestImageTag(state: ControlPlaneState):
|
|
|
184
184
|
throw new Error(`Invalid image namespace in system.env: ${namespace}`);
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
+
// `assistant` is the version-of-record image: all platform images
|
|
188
|
+
// (assistant, guardian, channel, voice) are published in lockstep under the
|
|
189
|
+
// same OP_IMAGE_TAG, so its newest tag is the canonical platform version.
|
|
190
|
+
|
|
187
191
|
let response: Response;
|
|
188
192
|
try {
|
|
189
193
|
response = await fetch(
|
|
190
|
-
`https://registry.hub.docker.com/v2/repositories/${namespace}/
|
|
194
|
+
`https://registry.hub.docker.com/v2/repositories/${namespace}/assistant/tags?page_size=25&ordering=last_updated`,
|
|
191
195
|
{ headers: { Accept: "application/json" } }
|
|
192
196
|
);
|
|
193
197
|
} catch (e) {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Shared OpenCode REST API client.
|
|
3
3
|
*
|
|
4
4
|
* Factory function that returns typed accessors for an OpenCode server
|
|
5
|
-
* at a configurable base URL. Used by both the admin (
|
|
5
|
+
* at a configurable base URL. Used by both the admin UI (host process) and
|
|
6
6
|
* CLI (host subprocess) to talk to OpenCode.
|
|
7
7
|
*/
|
|
8
8
|
|
|
@@ -24,7 +24,7 @@ import type { ControlPlaneState } from "./types.js";
|
|
|
24
24
|
export const authJsonPath = (s: ControlPlaneState): string => `${s.stashDir}/secrets/auth.json`;
|
|
25
25
|
/** akm config directory mounted at /etc/akm */
|
|
26
26
|
export const akmConfigDir = (s: ControlPlaneState): string => `${s.configDir}/akm`;
|
|
27
|
-
/** akm setup config file (written by admin
|
|
27
|
+
/** akm setup config file (written by the admin UI AKM action and CLI install) */
|
|
28
28
|
export const akmConfigPath = (s: ControlPlaneState): string => `${s.configDir}/akm/config.json`;
|
|
29
29
|
export const tasksDir = (s: ControlPlaneState): string => `${s.stashDir}/tasks`;
|
|
30
30
|
export const assistantConfigDir = (s: ControlPlaneState): string => `${s.configDir}/assistant`;
|
|
@@ -67,14 +67,11 @@ export const logsDir = (s: ControlPlaneState): string => `${s.data
|
|
|
67
67
|
* chat + tool activity.
|
|
68
68
|
*/
|
|
69
69
|
export const guardianAuditPath = (s: ControlPlaneState): string => `${s.dataDir}/logs/guardian-audit.log`;
|
|
70
|
-
/** One-shot 0.11.0 migration log (OP_UI_TOKEN → OPENCODE_SERVER_PASSWORD, endpoints.json move) */
|
|
71
|
-
export const migration0110LogPath = (s: ControlPlaneState): string => `${s.dataDir}/logs/migration-0.11.0.log`;
|
|
72
70
|
export const backupsDir = (s: ControlPlaneState): string => `${s.dataDir}/backups`;
|
|
73
71
|
|
|
74
72
|
// ── State directory — persistent service data ───────────────────────────────
|
|
75
73
|
|
|
76
74
|
export const assistantServiceDir = (s: ControlPlaneState): string => `${s.dataDir}/assistant`;
|
|
77
|
-
export const adminServiceDir = (s: ControlPlaneState): string => `${s.dataDir}/admin`;
|
|
78
75
|
export const guardianServiceDir = (s: ControlPlaneState): string => `${s.dataDir}/guardian`;
|
|
79
76
|
export const guardianAkmDir = (s: ControlPlaneState): string => `${s.dataDir}/guardian/akm`;
|
|
80
77
|
/** akm durable data — NOT config, which lives in config/akm/ */
|
|
@@ -19,7 +19,6 @@ import {
|
|
|
19
19
|
updateSecretsEnv,
|
|
20
20
|
patchSecretsEnvFile,
|
|
21
21
|
ensureOpenCodeConfig,
|
|
22
|
-
readStackEnv,
|
|
23
22
|
writeAuthJsonProviderKeys,
|
|
24
23
|
} from "./secrets.js";
|
|
25
24
|
import { createState } from "./lifecycle.js";
|
|
@@ -132,13 +131,9 @@ export function buildAuthJsonFromSetup(
|
|
|
132
131
|
*
|
|
133
132
|
* `OP_OPENCODE_PASSWORD` may be supplied explicitly as a file-based secret in
|
|
134
133
|
* `knowledge/secrets/op_opencode_password` when OpenCode auth is enabled.
|
|
135
|
-
*
|
|
136
|
-
* `existingSystemEnv` is unused now but the parameter is kept so callers
|
|
137
|
-
* compile unchanged. It can be removed in a follow-up cleanup.
|
|
138
134
|
*/
|
|
139
135
|
export function buildSystemSecretsFromSetup(
|
|
140
136
|
uiLoginPassword: string,
|
|
141
|
-
_existingSystemEnv: Record<string, string> = {}
|
|
142
137
|
): Record<string, string> {
|
|
143
138
|
return {
|
|
144
139
|
OP_UI_LOGIN_PASSWORD: uiLoginPassword,
|
|
@@ -215,7 +210,6 @@ export async function performSetup(
|
|
|
215
210
|
try {
|
|
216
211
|
ensureHomeDirs();
|
|
217
212
|
ensureSecrets(state);
|
|
218
|
-
const existingSystemEnv = readStackEnv(state.stackDir);
|
|
219
213
|
const channelSecretUpdates = channelCredentials ? buildChannelCredentialEnvVars(channelCredentials) : {};
|
|
220
214
|
// Pick up channel credential env vars not already provided in the spec
|
|
221
215
|
for (const mapping of Object.values(CHANNEL_CREDENTIAL_ENV_MAP)) {
|
|
@@ -225,7 +219,7 @@ export async function performSetup(
|
|
|
225
219
|
}
|
|
226
220
|
updateSecretsEnv(state, updates);
|
|
227
221
|
updateSecretsEnv(state, channelSecretUpdates);
|
|
228
|
-
patchSecretsEnvFile(state.stackDir, buildSystemSecretsFromSetup(security.uiLoginPassword
|
|
222
|
+
patchSecretsEnvFile(state.stackDir, buildSystemSecretsFromSetup(security.uiLoginPassword));
|
|
229
223
|
// Provider API keys land in OpenCode's auth.json (bind-mounted into
|
|
230
224
|
// the assistant container) — never in stack.env.
|
|
231
225
|
writeAuthJsonProviderKeys(state, providerKeys);
|
|
@@ -117,7 +117,7 @@ describe("skeleton: .openpalm/knowledge/ structure", () => {
|
|
|
117
117
|
// ── data/ service dirs ────────────────────────────────────────────────
|
|
118
118
|
|
|
119
119
|
describe("skeleton: .openpalm/data/ service directories", () => {
|
|
120
|
-
const serviceDirs = ["assistant", "
|
|
120
|
+
const serviceDirs = ["assistant", "guardian"];
|
|
121
121
|
|
|
122
122
|
for (const dir of serviceDirs) {
|
|
123
123
|
test(`data/${dir}/ exists`, () => {
|
|
@@ -23,6 +23,13 @@ describe("deriveSystemEnvFromSpec", () => {
|
|
|
23
23
|
test("produces default port values", () => {
|
|
24
24
|
const result = deriveSystemEnvFromSpec("/home/op");
|
|
25
25
|
expect(result.OP_ASSISTANT_PORT).toBe("3800");
|
|
26
|
+
expect(result.OP_HOST_UI_PORT).toBe("3880");
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("does not emit the retired OP_ADMIN_PORT/OP_ADMIN_OPENCODE_PORT vars", () => {
|
|
30
|
+
const result = deriveSystemEnvFromSpec("/home/op");
|
|
31
|
+
expect(result.OP_ADMIN_PORT).toBeUndefined();
|
|
32
|
+
expect(result.OP_ADMIN_OPENCODE_PORT).toBeUndefined();
|
|
26
33
|
});
|
|
27
34
|
|
|
28
35
|
test("does not emit OP_GUARDIAN_PORT (guardian is network-only, no host mapping)", () => {
|
|
@@ -42,8 +42,7 @@ export function deriveSystemEnvFromSpec(homeDir: string): Record<string, string>
|
|
|
42
42
|
// network-only (no host port mapping) so OP_GUARDIAN_PORT is no longer
|
|
43
43
|
// emitted; channels reach it via Docker DNS at http://guardian:8080.
|
|
44
44
|
result["OP_ASSISTANT_PORT"] = String(ports.assistant);
|
|
45
|
-
result["
|
|
46
|
-
result["OP_ADMIN_OPENCODE_PORT"] = String(ports.adminOpencode);
|
|
45
|
+
result["OP_HOST_UI_PORT"] = String(ports.hostUi);
|
|
47
46
|
result["OP_ASSISTANT_SSH_PORT"] = String(ports.assistantSsh);
|
|
48
47
|
|
|
49
48
|
return result;
|