Skip to content
This repository was archived by the owner on Mar 30, 2026. It is now read-only.
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
10 changes: 9 additions & 1 deletion packages/core/src/dep-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,15 @@ export GITHUB_TOKEN="\$_GH_TOKEN"
"brave-search": {
displayName: "Brave Search",
installScript: "", // config-only, no binary to install
postInstallScript: "", // config patching stays in config-generator.ts
postInstallScript: `
# Configure Brave Search in OpenClaw
if [ -n "\${BRAVE_API_KEY}" ]; then
openclaw config set tools.web.search '{"provider":"brave","apiKey":"'"\${BRAVE_API_KEY}"'"}'
echo "Brave Search configured successfully"
else
echo "WARNING: BRAVE_API_KEY not set, skipping Brave Search config"
fi
`,
secrets: {
BraveApiKey: {
envVar: "BRAVE_API_KEY",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ describe("cloud-init pipeline — plugins and deps", () => {
expect(hooksPreIdx).toBeGreaterThan(configIdx);
});

it("dep install + post-install scripts are base64 encoded; Brave search in config", () => {
it("dep install + post-install scripts are merged into postProvisionHooks with runAs", () => {
const config: CloudInitConfig = {
...ANTHROPIC_CLOUD,
deps: [
Expand All @@ -377,29 +377,85 @@ describe("cloud-init pipeline — plugins and deps", () => {
},
],
depSecrets: {
BRAVE_API_KEY: "brave-key-test",
GITHUB_TOKEN: "ghp_test",
ANTHROPIC_API_KEY: "sk-ant-api03-test-key",
},
};
const provConfig = buildConfig(config);

// Root-level install script encoded
expect(provConfig.depsRoot.length).toBe(1);
const installDecoded = Buffer.from(provConfig.depsRoot[0].script, "base64").toString();
// Install script in postProvisionHooks with runAs: root
const installHook = provConfig.postProvisionHooks.find((h) => h.name === "dep:gh:install");
expect(installHook).toBeTruthy();
expect(installHook!.runAs).toBe("root");
const installDecoded = Buffer.from(installHook!.script, "base64").toString();
expect(installDecoded).toContain("MARKER_GH_INSTALL");

// Post-install script encoded
expect(provConfig.depsPostInstall.length).toBe(1);
const postDecoded = Buffer.from(provConfig.depsPostInstall[0].script, "base64").toString();
// Post-install script in postProvisionHooks with runAs: ubuntu
const postHook = provConfig.postProvisionHooks.find((h) => h.name === "dep:gh:post");
expect(postHook).toBeTruthy();
expect(postHook!.runAs).toBe("ubuntu");
const postDecoded = Buffer.from(postHook!.script, "base64").toString();
expect(postDecoded).toContain("MARKER_GH_POST_INSTALL");
});

// Brave search in config set commands
it("brave-search is NOT in configSetCommands (handled by dep postInstallScript)", () => {
const config: CloudInitConfig = {
...ANTHROPIC_CLOUD,
depSecrets: {
BRAVE_API_KEY: "brave-key-test",
ANTHROPIC_API_KEY: "sk-ant-api03-test-key",
},
};
const provConfig = buildConfig(config);
const braveCmds = provConfig.configSetCommands.filter(
(c) => c.key === "tools.web.search",
);
expect(braveCmds.length).toBe(1);
expect(braveCmds[0].value).toEqual({ provider: "brave", apiKey: "brave-key-test" });
expect(braveCmds.length).toBe(0);
});

it("dep hooks come before extraHooks and plugin hooks in postProvisionHooks", () => {
const config: CloudInitConfig = {
...ANTHROPIC_CLOUD,
deps: [
{
name: "gh",
installScript: 'apt-get install -y gh',
postInstallScript: 'gh auth login',
secrets: { GithubToken: { envVar: "GITHUB_TOKEN" } },
},
],
extraHooks: {
postProvision: [{ label: "swarm", script: 'echo "SWARM"' }],
},
plugins: [
{
name: "test-plugin",
installable: true,
config: {},
hooks: { postProvision: 'echo "PLUGIN"' },
},
],
depSecrets: { GITHUB_TOKEN: "ghp_test", ANTHROPIC_API_KEY: "sk-ant-api03-test-key" },
};
const provConfig = buildConfig(config);
const names = provConfig.postProvisionHooks.map((h) => h.name);
expect(names).toEqual([
"dep:gh:install",
"dep:gh:post",
"swarm",
"plugin:test-plugin",
]);
});

it("non-dep hooks default runAs to undefined (template defaults to ubuntu)", () => {
const config: CloudInitConfig = {
...ANTHROPIC_CLOUD,
extraHooks: {
postProvision: [{ label: "swarm", script: 'echo "SWARM"' }],
},
};
const provConfig = buildConfig(config);
expect(provConfig.postProvisionHooks[0].runAs).toBeUndefined();
});
});

Expand Down
14 changes: 0 additions & 14 deletions packages/pulumi/src/components/config-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,6 @@ export interface OpenClawConfigOptions {
customConfig?: Record<string, unknown>;
/** Dynamic plugin configurations */
plugins?: PluginEntry[];
/** Brave Search API key for web search */
braveApiKey?: string;
}

export interface OpenClawConfig {
Expand All @@ -77,14 +75,6 @@ export interface OpenClawConfig {
port: number;
};
model?: string;
tools?: {
web: {
search: {
provider: string;
apiKey: string;
};
};
};
[key: string]: unknown;
}

Expand Down Expand Up @@ -114,10 +104,6 @@ export function generateOpenClawConfig(options: OpenClawConfigOptions): OpenClaw
};
}

if (options.braveApiKey) {
config.tools = { web: { search: { provider: "brave", apiKey: options.braveApiKey } } };
}

// Merge custom config
if (options.customConfig) {
Object.assign(config, options.customConfig);
Expand Down
33 changes: 9 additions & 24 deletions packages/pulumi/src/components/provisioner-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,9 @@ export interface ProvisionerConfig {
// Workspace files (gzip+base64 encoded content)
workspaceFiles: Array<{ path: string; gzipBase64: string }>;

// Deps (base64-encoded bash scripts)
depsRoot: Array<{ name: string; script: string }>;
depsPostInstall: Array<{ name: string; script: string }>;

// Hooks (base64-encoded bash scripts)
postProvisionHooks: Array<{ name: string; script: string }>;
preStartHooks: Array<{ name: string; script: string }>;
postProvisionHooks: Array<{ name: string; script: string; runAs?: "root" | "ubuntu" }>;
preStartHooks: Array<{ name: string; script: string; runAs?: "root" | "ubuntu" }>;

// Daemon / gateway
foregroundMode: boolean;
Expand Down Expand Up @@ -395,16 +391,6 @@ function buildConfigSetCommands(config: CloudInitConfig): ConfigSetCommand[] {
});
}

// 15. Brave search
const braveApiKey = allSecrets["BRAVE_API_KEY"];
if (braveApiKey) {
cmds.push({
key: "tools.web.search",
value: { provider: "brave", apiKey: braveApiKey },
comment: "Brave Search API",
});
}

return cmds;
}

Expand Down Expand Up @@ -556,17 +542,18 @@ export function buildProvisionerConfig(
},
);

// --- Deps ---
const depsRoot = (config.deps ?? [])
// --- Hooks (deps merged into postProvisionHooks) ---
const depInstallHooks = (config.deps ?? [])
.filter((d) => d.installScript)
.map((d) => ({ name: d.name, script: b64(d.installScript) }));
.map((d) => ({ name: `dep:${d.name}:install`, script: b64(d.installScript), runAs: "root" as const }));

const depsPostInstall = (config.deps ?? [])
const depPostHooks = (config.deps ?? [])
.filter((d) => d.postInstallScript)
.map((d) => ({ name: d.name, script: b64(d.postInstallScript) }));
.map((d) => ({ name: `dep:${d.name}:post`, script: b64(d.postInstallScript), runAs: "ubuntu" as const }));

// --- Hooks ---
const postProvisionHooks = [
...depInstallHooks,
...depPostHooks,
...(config.extraHooks?.postProvision ?? [])
.map((h) => ({ name: h.label, script: b64(h.script) })),
...(config.plugins ?? [])
Expand Down Expand Up @@ -620,8 +607,6 @@ export function buildProvisionerConfig(
clawhubSkills: config.clawhubSkills ?? [],

workspaceFiles,
depsRoot,
depsPostInstall,
postProvisionHooks,
preStartHooks,

Expand Down
32 changes: 6 additions & 26 deletions packages/pulumi/src/components/provisioner-template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,16 +109,6 @@ phase_tailscale() {
tailscale up $ts_args || echo "WARNING: Tailscale setup failed. Run 'sudo tailscale up' manually."
}

# ===== PHASE: Deps (root install) =====
phase_deps_root() {
local count=$(cfg_len '.depsRoot')
for i in $(seq 0 $((count - 1))); do
local name=$(cfg ".depsRoot[$i].name")
echo "Installing dep: $name..."
run_encoded_script "$(cfg ".depsRoot[$i].script")" root
done
}

# ===== PHASE: NVM + Node.js + OpenClaw =====
phase_nvm_node() {
local node_version=$(cfg '.nodeVersion')
Expand Down Expand Up @@ -188,16 +178,6 @@ phase_env_vars() {
fi
}

# ===== PHASE: Deps post-install (as ubuntu) =====
phase_deps_post() {
local count=$(cfg_len '.depsPostInstall')
for i in $(seq 0 $((count - 1))); do
local name=$(cfg ".depsPostInstall[$i].name")
echo "Post-install: $name..."
run_encoded_script "$(cfg ".depsPostInstall[$i].script")" ubuntu
done
}

# ===== PHASE: Systemd =====
phase_systemd() {
echo "Enabling systemd linger for ubuntu user..."
Expand All @@ -217,8 +197,9 @@ phase_hooks_post_provision() {
local count=$(cfg_len '.postProvisionHooks')
for i in $(seq 0 $((count - 1))); do
local name=$(cfg ".postProvisionHooks[$i].name")
echo "Running postProvision hook for $name..."
run_encoded_script "$(cfg ".postProvisionHooks[$i].script")" ubuntu
local run_as=$(cfg ".postProvisionHooks[$i].runAs // \\"ubuntu\\"")
echo "Running postProvision hook for $name (as $run_as)..."
run_encoded_script "$(cfg ".postProvisionHooks[$i].script")" "$run_as"
echo "postProvision hook for $name complete"
done
}
Expand Down Expand Up @@ -309,8 +290,9 @@ phase_hooks_pre_start() {
local count=$(cfg_len '.preStartHooks')
for i in $(seq 0 $((count - 1))); do
local name=$(cfg ".preStartHooks[$i].name")
echo "Running preStart hook for $name..."
run_encoded_script "$(cfg ".preStartHooks[$i].script")" ubuntu
local run_as=$(cfg ".preStartHooks[$i].runAs // \\"ubuntu\\"")
echo "Running preStart hook for $name (as $run_as)..."
run_encoded_script "$(cfg ".preStartHooks[$i].script")" "$run_as"
echo "preStart hook for $name complete"
done
}
Expand Down Expand Up @@ -409,11 +391,9 @@ phase_system

[ "$(cfg '.tailscale.skip')" != "true" ] && phase_tailscale

phase_deps_root
phase_nvm_node
phase_coding_agent
phase_env_vars
phase_deps_post

[ "$(cfg '.foregroundMode')" != "true" ] && phase_systemd

Expand Down