@openpalm/lib 0.11.0-beta.6 → 0.11.0-beta.8
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openpalm/lib",
|
|
3
|
-
"version": "0.11.0-beta.
|
|
3
|
+
"version": "0.11.0-beta.8",
|
|
4
4
|
"license": "MPL-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Shared control-plane library for OpenPalm — lifecycle, staging, secrets, channels, connections, scheduler",
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"scripts": {
|
|
11
11
|
"test": "bun test"
|
|
12
12
|
},
|
|
13
|
+
"types": "./src/index.ts",
|
|
13
14
|
"exports": {
|
|
14
15
|
".": "./src/index.ts",
|
|
15
16
|
"./provider-constants": "./src/provider-constants.ts",
|
|
@@ -87,7 +87,8 @@ export function writeSystemEnv(state: ControlPlaneState): void {
|
|
|
87
87
|
sectionHeader: "# ── Admin-managed ──────────────────────────────────────────────────"
|
|
88
88
|
});
|
|
89
89
|
|
|
90
|
-
writeFileSync(systemEnvPath, content);
|
|
90
|
+
writeFileSync(systemEnvPath, content, { mode: 0o600 });
|
|
91
|
+
chmodSync(systemEnvPath, 0o600);
|
|
91
92
|
}
|
|
92
93
|
|
|
93
94
|
function generateFallbackSystemEnv(state: ControlPlaneState): string {
|
|
@@ -95,11 +95,16 @@ function resolveAssetVersion(): string {
|
|
|
95
95
|
}
|
|
96
96
|
const VERSION = resolveAssetVersion();
|
|
97
97
|
|
|
98
|
-
// Persona files (openpalm.md, system.md)
|
|
99
|
-
// in this list
|
|
100
|
-
//
|
|
98
|
+
// Persona files (openpalm.md, system.md), stash seeds, and user-editable config
|
|
99
|
+
// files are intentionally NOT in this list. They are seeded once (never
|
|
100
|
+
// overwritten) via seedOpenPalmDir (skipExisting) or SEEDED_ASSETS below.
|
|
101
101
|
const MANAGED_ASSETS: { relPath: string; githubFilename: string }[] = [
|
|
102
|
-
{ relPath: "config/stack/core.compose.yml",
|
|
102
|
+
{ relPath: "config/stack/core.compose.yml", githubFilename: ".openpalm/config/stack/core.compose.yml" },
|
|
103
|
+
];
|
|
104
|
+
|
|
105
|
+
// Seeded once — written only when the file does not exist yet.
|
|
106
|
+
// User edits always win; upgrade never touches these files.
|
|
107
|
+
const SEEDED_ASSETS: { relPath: string; githubFilename: string }[] = [
|
|
103
108
|
{ relPath: "config/assistant/opencode.jsonc", githubFilename: ".openpalm/config/assistant/opencode.jsonc" },
|
|
104
109
|
];
|
|
105
110
|
|
|
@@ -149,6 +154,16 @@ export async function refreshCoreAssets(): Promise<{
|
|
|
149
154
|
updated.push(asset.relPath);
|
|
150
155
|
}
|
|
151
156
|
|
|
157
|
+
// Seed user-editable assets only when missing — never overwrite.
|
|
158
|
+
for (const asset of SEEDED_ASSETS) {
|
|
159
|
+
const targetPath = join(homeDir, asset.relPath);
|
|
160
|
+
if (existsSync(targetPath)) continue;
|
|
161
|
+
const freshContent = await downloadAsset(asset.githubFilename);
|
|
162
|
+
mkdirSync(dirname(targetPath), { recursive: true });
|
|
163
|
+
writeFileSync(targetPath, freshContent);
|
|
164
|
+
updated.push(asset.relPath);
|
|
165
|
+
}
|
|
166
|
+
|
|
152
167
|
return { backupDir, updated };
|
|
153
168
|
}
|
|
154
169
|
|
package/src/control-plane/env.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { parse as dotenvParse } from 'dotenv';
|
|
2
|
-
import { readFileSync, existsSync } from 'node:fs';
|
|
2
|
+
import { readFileSync, existsSync, copyFileSync } from 'node:fs';
|
|
3
3
|
|
|
4
4
|
export function parseEnvContent(content: string): Record<string, string> {
|
|
5
5
|
return dotenvParse(content);
|
|
@@ -10,6 +10,9 @@ export function parseEnvFile(filePath: string): Record<string, string> {
|
|
|
10
10
|
try {
|
|
11
11
|
return dotenvParse(readFileSync(filePath, 'utf-8'));
|
|
12
12
|
} catch {
|
|
13
|
+
// File is unreadable or malformed — back it up before returning empty so
|
|
14
|
+
// the next write doesn't silently discard all existing values.
|
|
15
|
+
try { copyFileSync(filePath, `${filePath}.corrupt-${Date.now()}`); } catch { /* best-effort */ }
|
|
13
16
|
return {};
|
|
14
17
|
}
|
|
15
18
|
}
|
|
@@ -300,6 +300,24 @@ export async function performUpgrade(state: ControlPlaneState): Promise<UpgradeR
|
|
|
300
300
|
};
|
|
301
301
|
}
|
|
302
302
|
|
|
303
|
+
/**
|
|
304
|
+
* Set a specific image tag in stack.env then pull images and restart containers.
|
|
305
|
+
* Used by the admin "set version" action — skips the auto-detect step in performUpgrade.
|
|
306
|
+
*/
|
|
307
|
+
export async function applyTagChange(state: ControlPlaneState, tag: string): Promise<UpgradeResult> {
|
|
308
|
+
const stackEnvPath = `${state.stackDir}/stack.env`;
|
|
309
|
+
const currentContent = existsSync(stackEnvPath) ? readFileSync(stackEnvPath, "utf-8") : "";
|
|
310
|
+
writeFileSync(stackEnvPath, mergeEnvContent(currentContent, { OP_IMAGE_TAG: tag }, { uncomment: true }));
|
|
311
|
+
const upgradeResult = await applyUpgrade(state);
|
|
312
|
+
return {
|
|
313
|
+
imageTag: tag,
|
|
314
|
+
namespace: "openpalm",
|
|
315
|
+
backupDir: upgradeResult.backupDir,
|
|
316
|
+
assetsUpdated: upgradeResult.updated,
|
|
317
|
+
restarted: upgradeResult.restarted,
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
|
|
303
321
|
export function buildComposeFileList(state: ControlPlaneState): string[] {
|
|
304
322
|
return discoverStackOverlays(state.stackDir);
|
|
305
323
|
}
|
|
@@ -241,11 +241,17 @@ function parseChecksumsFile(content: string): Map<string, string> {
|
|
|
241
241
|
return map;
|
|
242
242
|
}
|
|
243
243
|
|
|
244
|
-
export
|
|
244
|
+
export function readCurrentUiBuildVersion(stateDir: string): string | null {
|
|
245
|
+
const versionFile = join(stateDir, 'ui', 'version.txt');
|
|
246
|
+
if (!existsSync(versionFile)) return null;
|
|
247
|
+
return readFileSync(versionFile, 'utf-8').trim() || null;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export async function seedUiBuild(repoRef: string, stateDir: string, options?: { forceRemote?: boolean }): Promise<void> {
|
|
245
251
|
const uiDir = join(stateDir, 'ui');
|
|
246
252
|
mkdirSync(uiDir, { recursive: true });
|
|
247
253
|
|
|
248
|
-
const local = resolveLocalUiBuild();
|
|
254
|
+
const local = options?.forceRemote ? null : resolveLocalUiBuild();
|
|
249
255
|
if (local) {
|
|
250
256
|
logger.debug('seeding UI build from local source', { src: local });
|
|
251
257
|
copyTree(local, uiDir);
|
|
@@ -284,6 +290,9 @@ export async function seedUiBuild(repoRef: string, stateDir: string): Promise<vo
|
|
|
284
290
|
|
|
285
291
|
writeFileSync(tmpTar, tarData);
|
|
286
292
|
|
|
293
|
+
// Clear stale files before extracting so old build files don't persist
|
|
294
|
+
rmSync(uiDir, { recursive: true, force: true });
|
|
295
|
+
mkdirSync(uiDir, { recursive: true });
|
|
287
296
|
// Cross-platform extraction via the `tar` npm package — no shell dependency
|
|
288
297
|
await tarExtract({ file: tmpTar, cwd: uiDir, strip: 1 });
|
|
289
298
|
writeFileSync(join(uiDir, 'version.txt'), repoRef.replace(/^v/, ''));
|
package/src/index.ts
CHANGED
|
@@ -181,6 +181,7 @@ export {
|
|
|
181
181
|
applyUninstall,
|
|
182
182
|
applyUpgrade,
|
|
183
183
|
performUpgrade,
|
|
184
|
+
applyTagChange,
|
|
184
185
|
updateStackEnvToLatestImageTag,
|
|
185
186
|
buildComposeFileList,
|
|
186
187
|
buildManagedServices,
|
|
@@ -308,4 +309,5 @@ export {
|
|
|
308
309
|
resolveUiBuildDir,
|
|
309
310
|
seedUiBuild,
|
|
310
311
|
checkAndUpdateUiBuild,
|
|
312
|
+
readCurrentUiBuildVersion,
|
|
311
313
|
} from "./control-plane/ui-assets.js";
|