Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/platform-env-contract.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@resciencelab/star-office-world": patch
---

Align env contract with platform: WORLD_ID, WORLD_NAME, WORLD_PASSWORD, MAX_AGENTS, WORLD_PUBLIC now take precedence over legacy STAR_OFFICE_* aliases. Added isPublic config support so deployed worlds honor platform visibility settings.
38 changes: 25 additions & 13 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,20 @@ import { discoverPublicAddr } from "./discover.js";

export function loadConfig(overrides?: Partial<StarOfficeConfig>): StarOfficeConfig {
return {
worldId: overrides?.worldId ?? process.env["STAR_OFFICE_WORLD_ID"] ?? process.env["WORLD_ID"] ?? "star-office",
officeName: overrides?.officeName ?? process.env["STAR_OFFICE_NAME"] ?? process.env["WORLD_NAME"] ?? "Star Office",
port: overrides?.port ?? int(process.env["STAR_OFFICE_PORT"] ?? process.env["PEER_PORT"], 19000),
publicPort: overrides?.publicPort ?? intOrUndef(process.env["STAR_OFFICE_PUBLIC_PORT"] ?? process.env["PUBLIC_PORT"]),
publicAddr: overrides?.publicAddr ?? process.env["STAR_OFFICE_PUBLIC_ADDR"] ?? process.env["PUBLIC_ADDR"] ?? null,
worldId: overrides?.worldId ?? process.env["WORLD_ID"] ?? process.env["STAR_OFFICE_WORLD_ID"] ?? "star-office",
officeName: overrides?.officeName ?? process.env["WORLD_NAME"] ?? process.env["STAR_OFFICE_NAME"] ?? "Star Office",
port: overrides?.port ?? int(process.env["PEER_PORT"] ?? process.env["STAR_OFFICE_PORT"], 19000),
publicPort: overrides?.publicPort ?? intOrUndef(process.env["PUBLIC_PORT"] ?? process.env["STAR_OFFICE_PUBLIC_PORT"]),
publicAddr: overrides?.publicAddr ?? process.env["PUBLIC_ADDR"] ?? process.env["STAR_OFFICE_PUBLIC_ADDR"] ?? null,
gatewayUrls: overrides?.gatewayUrls
?? parseList(process.env["STAR_OFFICE_GATEWAY_URLS"] ?? process.env["GATEWAY_URL"])
?? parseList(process.env["GATEWAY_URL"] ?? process.env["STAR_OFFICE_GATEWAY_URLS"])
?? ["https://gateway.agentworlds.ai"],
password: overrides?.password ?? process.env["STAR_OFFICE_PASSWORD"] ?? "",
password: overrides?.password ?? process.env["WORLD_PASSWORD"] ?? process.env["STAR_OFFICE_PASSWORD"] ?? "",
adminPassword: overrides?.adminPassword ?? process.env["STAR_OFFICE_ADMIN_PASS"] ?? "1234",
maxAgents: overrides?.maxAgents ?? int(process.env["STAR_OFFICE_MAX_AGENTS"], 20),
maxAgents: overrides?.maxAgents ?? int(process.env["MAX_AGENTS"] ?? process.env["STAR_OFFICE_MAX_AGENTS"], 20),
isPublic: overrides?.isPublic ?? bool(process.env["WORLD_PUBLIC"], true),
broadcastIntervalMs: overrides?.broadcastIntervalMs ?? int(process.env["STAR_OFFICE_BROADCAST_MS"], 3000),
dataDir: overrides?.dataDir ?? process.env["STAR_OFFICE_DATA_DIR"] ?? process.env["DATA_DIR"] ?? "./data",
dataDir: overrides?.dataDir ?? process.env["DATA_DIR"] ?? process.env["STAR_OFFICE_DATA_DIR"] ?? "./data",
frontendDir: overrides?.frontendDir ?? process.env["STAR_OFFICE_FRONTEND_DIR"] ?? "./frontend",
memoryDir: overrides?.memoryDir ?? process.env["STAR_OFFICE_MEMORY_DIR"] ?? "./data/memos",
geminiApiKey: overrides?.geminiApiKey ?? process.env["GEMINI_API_KEY"],
Expand All @@ -43,8 +44,8 @@ export async function resolveConfig(overrides?: Partial<StarOfficeConfig>): Prom
}

const explicitWorldId = overrides?.worldId
?? process.env["STAR_OFFICE_WORLD_ID"]
?? process.env["WORLD_ID"];
?? process.env["WORLD_ID"]
?? process.env["STAR_OFFICE_WORLD_ID"];
if (!explicitWorldId) {
config.worldId = deriveSlug(config.dataDir ?? "./data", config.officeName ?? "Star Office");
}
Expand All @@ -60,14 +61,15 @@ function logConfigSummary(config: StarOfficeConfig): void {
console.log(` port: ${config.port}`);
console.log(` publicAddr: ${config.publicAddr ?? "(none)"}`);
console.log(` publicPort: ${config.publicPort ?? "(same as port)"}`);
console.log(` isPublic: ${config.isPublic ?? true}`);
console.log(` gatewayUrls: ${config.gatewayUrls?.join(", ") ?? "(none)"}`);
console.log(` dataDir: ${config.dataDir}`);

if (!config.publicAddr) {
console.warn("[office] WARNING: No PUBLIC_ADDR — world will register on gateway without reachable endpoints");
}
if (!process.env["WORLD_ID"] && !process.env["STAR_OFFICE_WORLD_ID"]) {
console.log(` (slug auto-derived from identity)`);
console.log(" (slug auto-derived from identity)");
}
}

Expand All @@ -83,7 +85,9 @@ function deriveSlug(dataDir: string, name: string): string {
return `${prefix}-${hash}`;
}
}
} catch { /* identity not yet created — fall through */ }
} catch {
// identity not yet created — fall through
}
const rand = Math.random().toString(36).slice(2, 8);
return `${prefix}-${rand}`;
}
Expand All @@ -100,6 +104,14 @@ function intOrUndef(val: string | undefined): number | undefined {
return Number.isFinite(n) ? n : undefined;
}

function bool(val: string | undefined, fallback: boolean): boolean {
if (!val) return fallback;
const normalized = val.trim().toLowerCase();
if (["1", "true", "yes", "on", "public"].includes(normalized)) return true;
if (["0", "false", "no", "off", "private"].includes(normalized)) return false;
return fallback;
}

function parseList(val: string | undefined): string[] | undefined {
if (!val) return undefined;
return val.split(",").map((s) => s.trim()).filter(Boolean);
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export async function createStarOfficeWorld(
publicAddr: config.publicAddr ?? null,
gatewayUrls: config.gatewayUrls,
maxAgents: config.maxAgents ?? 20,
isPublic: true,
isPublic: config.isPublic ?? true,
password: config.password ?? "",
broadcastIntervalMs: config.broadcastIntervalMs ?? 3000,
dataDir: config.dataDir ?? "./data",
Expand Down
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ export interface StarOfficeConfig {
password?: string;
adminPassword?: string;
maxAgents?: number;
isPublic?: boolean;
broadcastIntervalMs?: number;
dataDir?: string;
frontendDir?: string;
Expand Down
69 changes: 69 additions & 0 deletions test/config.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { afterEach, test } from "node:test"
import assert from "node:assert/strict"

const ORIGINAL_ENV = { ...process.env }

function resetEnv() {
for (const key of Object.keys(process.env)) {
if (!(key in ORIGINAL_ENV)) delete process.env[key]
}
for (const [key, value] of Object.entries(ORIGINAL_ENV)) {
process.env[key] = value
}
}

afterEach(() => {
resetEnv()
})

test("loadConfig prefers platform env vars over legacy STAR_OFFICE_* aliases", async () => {
process.env.WORLD_ID = "platform-world"
process.env.STAR_OFFICE_WORLD_ID = "legacy-world"
process.env.WORLD_NAME = "Platform Name"
process.env.STAR_OFFICE_NAME = "Legacy Name"
process.env.PEER_PORT = "19001"
process.env.STAR_OFFICE_PORT = "19000"
process.env.PUBLIC_PORT = "9550"
process.env.STAR_OFFICE_PUBLIC_PORT = "9549"
process.env.PUBLIC_ADDR = "3.17.5.202"
process.env.STAR_OFFICE_PUBLIC_ADDR = "127.0.0.1"
process.env.GATEWAY_URL = "https://gateway.example.com"
process.env.STAR_OFFICE_GATEWAY_URLS = "https://legacy-gateway.example.com"
process.env.WORLD_PASSWORD = "platform-password"
process.env.STAR_OFFICE_PASSWORD = "legacy-password"
process.env.MAX_AGENTS = "42"
process.env.STAR_OFFICE_MAX_AGENTS = "7"
process.env.WORLD_PUBLIC = "false"
process.env.DATA_DIR = "/platform-data"
process.env.STAR_OFFICE_DATA_DIR = "/legacy-data"

const { loadConfig } = await import("../dist/config.js")
const config = loadConfig()

assert.equal(config.worldId, "platform-world")
assert.equal(config.officeName, "Platform Name")
assert.equal(config.port, 19001)
assert.equal(config.publicPort, 9550)
assert.equal(config.publicAddr, "3.17.5.202")
assert.deepEqual(config.gatewayUrls, ["https://gateway.example.com"])
assert.equal(config.password, "platform-password")
assert.equal(config.maxAgents, 42)
assert.equal(config.isPublic, false)
assert.equal(config.dataDir, "/platform-data")
})

test("loadConfig still supports legacy STAR_OFFICE_* env vars when platform vars are absent", async () => {
process.env.STAR_OFFICE_WORLD_ID = "legacy-world"
process.env.STAR_OFFICE_NAME = "Legacy Name"
process.env.STAR_OFFICE_PASSWORD = "legacy-password"
process.env.STAR_OFFICE_MAX_AGENTS = "7"

const { loadConfig } = await import("../dist/config.js")
const config = loadConfig()

assert.equal(config.worldId, "legacy-world")
assert.equal(config.officeName, "Legacy Name")
assert.equal(config.password, "legacy-password")
assert.equal(config.maxAgents, 7)
assert.equal(config.isPublic, true)
})