Compare commits

..

1 Commits

Author SHA1 Message Date
Jarvis
ac79922c3f chore(release): bump mosaic cli to 0.0.32
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful
ci/woodpecker/push/ci Pipeline was successful
2026-06-20 15:56:43 -05:00
3 changed files with 15 additions and 70 deletions

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mosaicstack/mosaic", "name": "@mosaicstack/mosaic",
"version": "0.0.34", "version": "0.0.32",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://git.mosaicstack.dev/mosaicstack/stack.git", "url": "https://git.mosaicstack.dev/mosaicstack/stack.git",

View File

@@ -10,7 +10,6 @@ import {
getDefaultOperatorSourceLabel, getDefaultOperatorSourceLabel,
getRosterAgent, getRosterAgent,
loadFleetRoster, loadFleetRoster,
mergeAgentEnv,
registerFleetCommand, registerFleetCommand,
resolveFleetPaths, resolveFleetPaths,
type CommandRunner, type CommandRunner,
@@ -122,37 +121,6 @@ describe('fleet roster parsing', () => {
); );
}); });
it('preserves site-owned agent EnvironmentFile overrides while refreshing roster keys', () => {
const generated = [
'MOSAIC_AGENT_NAME=coder0',
'MOSAIC_AGENT_RUNTIME=codex',
'MOSAIC_AGENT_WORKDIR=/srv/new',
'MOSAIC_TMUX_SOCKET=mosaic-factory',
'',
].join('\n');
const existing = [
'MOSAIC_AGENT_NAME=old-name',
'MOSAIC_AGENT_RUNTIME=old-runtime',
'MOSAIC_AGENT_WORKDIR=/srv/old',
'MOSAIC_TMUX_SOCKET=old-socket',
'MOSAIC_AGENT_COMMAND=/home/jarvis/.config/mosaic/fleet/canary.sh',
'# site note',
'',
].join('\n');
expect(mergeAgentEnv(generated, existing)).toBe(
[
'MOSAIC_AGENT_NAME=coder0',
'MOSAIC_AGENT_RUNTIME=codex',
'MOSAIC_AGENT_WORKDIR=/srv/new',
'MOSAIC_TMUX_SOCKET=mosaic-factory',
'MOSAIC_AGENT_COMMAND=/home/jarvis/.config/mosaic/fleet/canary.sh',
'# site note',
'',
].join('\n'),
);
});
it('rejects unknown roster fields instead of silently defaulting', async () => { it('rejects unknown roster fields instead of silently defaulting', async () => {
cleanup = await tempDir(); cleanup = await tempDir();
const rosterPath = join(cleanup, 'roster.yaml'); const rosterPath = join(cleanup, 'roster.yaml');

View File

@@ -1,5 +1,5 @@
import { constants } from 'node:fs'; import { constants } from 'node:fs';
import { access, chmod, copyFile, mkdir, readFile, writeFile } from 'node:fs/promises'; import { access, copyFile, mkdir, readFile, writeFile } from 'node:fs/promises';
import { homedir, hostname } from 'node:os'; import { homedir, hostname } from 'node:os';
import { dirname, join, resolve } from 'node:path'; import { dirname, join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
@@ -148,29 +148,6 @@ export function generateAgentEnv(roster: FleetRoster, agent: FleetAgent): string
].join('\n'); ].join('\n');
} }
export function mergeAgentEnv(generatedEnv: string, existingEnv?: string): string {
if (!existingEnv?.trim()) {
return generatedEnv;
}
const generatedKeys = new Set(
generatedEnv
.split('\n')
.map((line) => line.match(/^([A-Za-z_][A-Za-z0-9_]*)=/)?.[1])
.filter((key): key is string => key !== undefined),
);
const preservedLines = existingEnv.split('\n').filter((line) => {
if (!line.trim()) {
return false;
}
const key = line.match(/^([A-Za-z_][A-Za-z0-9_]*)=/)?.[1];
return key === undefined || !generatedKeys.has(key);
});
if (preservedLines.length === 0) {
return generatedEnv;
}
return [generatedEnv.trimEnd(), ...preservedLines, ''].join('\n');
}
export function buildFleetServiceCommand(action: FleetServiceAction, agentName?: string): string[] { export function buildFleetServiceCommand(action: FleetServiceAction, agentName?: string): string[] {
const service = agentName ? `mosaic-agent@${agentName}.service` : 'mosaic-tmux-holder.service'; const service = agentName ? `mosaic-agent@${agentName}.service` : 'mosaic-tmux-holder.service';
return ['systemctl', '--user', action, service]; return ['systemctl', '--user', action, service];
@@ -478,19 +455,18 @@ async function installFleet(cmd: Command, frameworkRoot: string): Promise<void>
await mkdir(activePaths.systemdUserDir, { recursive: true }); await mkdir(activePaths.systemdUserDir, { recursive: true });
await mkdir(activePaths.agentEnvDir, { recursive: true }); await mkdir(activePaths.agentEnvDir, { recursive: true });
const startAgentSessionPath = join(activePaths.fleetToolsDir, 'start-agent-session.sh');
const sendMessagePath = join(activePaths.tmuxToolsDir, 'send-message.sh');
const agentSendPath = join(activePaths.tmuxToolsDir, 'agent-send.sh');
const executableToolPaths = [startAgentSessionPath, sendMessagePath, agentSendPath];
await copyFile( await copyFile(
join(frameworkRoot, 'tools', 'fleet', 'start-agent-session.sh'), join(frameworkRoot, 'tools', 'fleet', 'start-agent-session.sh'),
startAgentSessionPath, join(activePaths.fleetToolsDir, 'start-agent-session.sh'),
);
await copyFile(
join(frameworkRoot, 'tools', 'tmux', 'send-message.sh'),
join(activePaths.tmuxToolsDir, 'send-message.sh'),
);
await copyFile(
join(frameworkRoot, 'tools', 'tmux', 'agent-send.sh'),
join(activePaths.tmuxToolsDir, 'agent-send.sh'),
); );
await copyFile(join(frameworkRoot, 'tools', 'tmux', 'send-message.sh'), sendMessagePath);
await copyFile(join(frameworkRoot, 'tools', 'tmux', 'agent-send.sh'), agentSendPath);
for (const toolPath of executableToolPaths) {
await chmod(toolPath, 0o755);
}
await copyFile( await copyFile(
join(frameworkRoot, 'systemd', 'user', 'mosaic-tmux-holder.service'), join(frameworkRoot, 'systemd', 'user', 'mosaic-tmux-holder.service'),
join(activePaths.systemdUserDir, 'mosaic-tmux-holder.service'), join(activePaths.systemdUserDir, 'mosaic-tmux-holder.service'),
@@ -501,9 +477,10 @@ async function installFleet(cmd: Command, frameworkRoot: string): Promise<void>
); );
for (const agent of roster.agents) { for (const agent of roster.agents) {
const envPath = join(activePaths.agentEnvDir, `${agent.name}.env`); await writeFile(
const existingEnv = (await canRead(envPath)) ? await readFile(envPath, 'utf8') : undefined; join(activePaths.agentEnvDir, `${agent.name}.env`),
await writeFile(envPath, mergeAgentEnv(generateAgentEnv(roster, agent), existingEnv)); generateAgentEnv(roster, agent),
);
} }
console.log(`Installed fleet files for ${roster.agents.length} agent(s).`); console.log(`Installed fleet files for ${roster.agents.length} agent(s).`);