chore: drop Clawdis legacy references
This commit is contained in:
@@ -70,7 +70,7 @@
|
|||||||
- Never commit or publish real phone numbers, videos, or live configuration values. Use obviously fake placeholders in docs, tests, and examples.
|
- Never commit or publish real phone numbers, videos, or live configuration values. Use obviously fake placeholders in docs, tests, and examples.
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
- Rebrand/migration issues (Clawdis → Clawdbot) or legacy config/service warnings: run `clawdbot doctor` (see `docs/gateway/doctor.md`).
|
- Rebrand/migration issues or legacy config/service warnings: run `clawdbot doctor` (see `docs/gateway/doctor.md`).
|
||||||
|
|
||||||
## Agent-Specific Notes
|
## Agent-Specific Notes
|
||||||
- Vocabulary: "makeup" = "mac app".
|
- Vocabulary: "makeup" = "mac app".
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
- Tools: add `web_search`/`web_fetch` (Brave API), auto-enable `web_fetch` for sandboxed sessions, and remove the `brave-search` skill.
|
- Tools: add `web_search`/`web_fetch` (Brave API), auto-enable `web_fetch` for sandboxed sessions, and remove the `brave-search` skill.
|
||||||
- CLI/Docs: add a web tools configure section for storing Brave API keys and update onboarding tips.
|
- CLI/Docs: add a web tools configure section for storing Brave API keys and update onboarding tips.
|
||||||
- Tools: return a setup hint (docs link) when web_search runs without a Brave API key.
|
- Tools: return a setup hint (docs link) when web_search runs without a Brave API key.
|
||||||
|
- Config/Doctor: remove legacy Clawdis env fallbacks and config/service migrations (Clawdbot-only).
|
||||||
- Browser: add Chrome extension relay takeover mode (toolbar button), plus `clawdbot browser extension install/path` and remote browser control via `clawdbot browser serve` + `browser.controlToken`.
|
- Browser: add Chrome extension relay takeover mode (toolbar button), plus `clawdbot browser extension install/path` and remote browser control via `clawdbot browser serve` + `browser.controlToken`.
|
||||||
- CLI/Docs: add per-command CLI doc pages and link them from `clawdbot <command> --help`.
|
- CLI/Docs: add per-command CLI doc pages and link them from `clawdbot <command> --help`.
|
||||||
|
|
||||||
|
|||||||
@@ -44,18 +44,18 @@ file creation:
|
|||||||
{ agent: { skipBootstrap: true } }
|
{ agent: { skipBootstrap: true } }
|
||||||
```
|
```
|
||||||
|
|
||||||
## Legacy workspace folders
|
## Extra workspace folders
|
||||||
|
|
||||||
Older installs may have created `~/clawdis` or `~/clawdbot`. Keeping multiple
|
Older installs may have created `~/clawdbot`. Keeping multiple workspace
|
||||||
workspace directories around can cause confusing auth or state drift, because
|
directories around can cause confusing auth or state drift, because only one
|
||||||
only one workspace is active at a time.
|
workspace is active at a time.
|
||||||
|
|
||||||
**Recommendation:** keep a single active workspace. If you no longer use the
|
**Recommendation:** keep a single active workspace. If you no longer use the
|
||||||
legacy folders, archive or move them to Trash (for example `trash ~/clawdis`).
|
extra folders, archive or move them to Trash (for example `trash ~/clawdbot`).
|
||||||
If you intentionally keep multiple workspaces, make sure
|
If you intentionally keep multiple workspaces, make sure
|
||||||
`agents.defaults.workspace` points to the active one.
|
`agents.defaults.workspace` points to the active one.
|
||||||
|
|
||||||
`clawdbot doctor` warns when it detects legacy workspace directories.
|
`clawdbot doctor` warns when it detects extra workspace directories.
|
||||||
|
|
||||||
## Workspace file map (what each file means)
|
## Workspace file map (what each file means)
|
||||||
|
|
||||||
|
|||||||
@@ -59,13 +59,13 @@ cat ~/.clawdbot/clawdbot.json
|
|||||||
- UI protocol freshness check (rebuilds Control UI when the protocol schema is newer).
|
- UI protocol freshness check (rebuilds Control UI when the protocol schema is newer).
|
||||||
- Health check + restart prompt.
|
- Health check + restart prompt.
|
||||||
- Skills status summary (eligible/missing/blocked).
|
- Skills status summary (eligible/missing/blocked).
|
||||||
- Legacy config migration and normalization.
|
- Config normalization for legacy values.
|
||||||
- OpenCode Zen provider override warnings (`models.providers.opencode`).
|
- OpenCode Zen provider override warnings (`models.providers.opencode`).
|
||||||
- Legacy on-disk state migration (sessions/agent dir/WhatsApp auth).
|
- Legacy on-disk state migration (sessions/agent dir/WhatsApp auth).
|
||||||
- State integrity and permissions checks (sessions, transcripts, state dir).
|
- State integrity and permissions checks (sessions, transcripts, state dir).
|
||||||
- Config file permission checks (chmod 600) when running locally.
|
- Config file permission checks (chmod 600) when running locally.
|
||||||
- Model auth health: checks OAuth expiry, can refresh expiring tokens, and reports auth-profile cooldown/disabled states.
|
- Model auth health: checks OAuth expiry, can refresh expiring tokens, and reports auth-profile cooldown/disabled states.
|
||||||
- Legacy workspace dir detection (`~/clawdis`, `~/clawdbot`).
|
- Extra workspace dir detection (`~/clawdbot`).
|
||||||
- Sandbox image repair when sandboxing is enabled.
|
- Sandbox image repair when sandboxing is enabled.
|
||||||
- Legacy service migration and extra gateway detection.
|
- Legacy service migration and extra gateway detection.
|
||||||
- Gateway runtime checks (service installed but not running; cached launchd label).
|
- Gateway runtime checks (service installed but not running; cached launchd label).
|
||||||
@@ -85,10 +85,10 @@ cat ~/.clawdbot/clawdbot.json
|
|||||||
If this is a git checkout and doctor is running interactively, it offers to
|
If this is a git checkout and doctor is running interactively, it offers to
|
||||||
update (fetch/rebase/build) before running doctor.
|
update (fetch/rebase/build) before running doctor.
|
||||||
|
|
||||||
### 1) Legacy config file migration
|
### 1) Config normalization
|
||||||
If `~/.clawdis/clawdis.json` exists and `~/.clawdbot/clawdbot.json` does not,
|
If the config contains legacy value shapes (for example `messages.ackReaction`
|
||||||
doctor migrates the file and normalizes old paths/image names. This prevents
|
without a channel-specific override), doctor normalizes them into the current
|
||||||
new installs from silently booting with the wrong schema.
|
schema.
|
||||||
|
|
||||||
### 2) Legacy config key migrations
|
### 2) Legacy config key migrations
|
||||||
When the config contains deprecated keys, other commands refuse to run and ask
|
When the config contains deprecated keys, other commands refuse to run and ask
|
||||||
@@ -182,7 +182,7 @@ When sandboxing is enabled, doctor checks Docker images and offers to build or
|
|||||||
switch to legacy names if the current image is missing.
|
switch to legacy names if the current image is missing.
|
||||||
|
|
||||||
### 8) Gateway service migrations and cleanup hints
|
### 8) Gateway service migrations and cleanup hints
|
||||||
Doctor detects legacy Clawdis gateway services (launchd/systemd/schtasks) and
|
Doctor detects legacy gateway services (launchd/systemd/schtasks) and
|
||||||
offers to remove them and install the Clawdbot service using the current gateway
|
offers to remove them and install the Clawdbot service using the current gateway
|
||||||
port. It can also scan for extra gateway-like services and print cleanup hints.
|
port. It can also scan for extra gateway-like services and print cleanup hints.
|
||||||
Profile-named Clawdbot gateway services are considered first-class and are not
|
Profile-named Clawdbot gateway services are considered first-class and are not
|
||||||
|
|||||||
@@ -136,14 +136,14 @@ clawdbot daemon status
|
|||||||
It will show the listener(s) and likely causes (gateway already running, SSH tunnel).
|
It will show the listener(s) and likely causes (gateway already running, SSH tunnel).
|
||||||
If needed, stop the service or pick a different port.
|
If needed, stop the service or pick a different port.
|
||||||
|
|
||||||
### Legacy Workspace Folders Detected
|
### Extra Workspace Folders Detected
|
||||||
|
|
||||||
If you upgraded from older installs, you might still have `~/clawdis` or
|
If you upgraded from older installs, you might still have `~/clawdbot` on disk.
|
||||||
`~/clawdbot` on disk. Multiple workspace directories can cause confusing auth
|
Multiple workspace directories can cause confusing auth or state drift because
|
||||||
or state drift because only one workspace is active.
|
only one workspace is active.
|
||||||
|
|
||||||
**Fix:** keep a single active workspace and archive/remove the rest. See
|
**Fix:** keep a single active workspace and archive/remove the rest. See
|
||||||
[Agent workspace](/concepts/agent-workspace#legacy-workspace-folders).
|
[Agent workspace](/concepts/agent-workspace#extra-workspace-folders).
|
||||||
|
|
||||||
### Main chat running in a sandbox workspace
|
### Main chat running in a sandbox workspace
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ In the beginning, there was **Warelay** — a sensible name for a WhatsApp gatew
|
|||||||
|
|
||||||
But then came **Clawd**.
|
But then came **Clawd**.
|
||||||
|
|
||||||
For a brief moment, it was called **Clawdis** — because Clawd suggested the name. But everyone liked **Clawdbot** more, so that's what we settled on.
|
For a brief moment, it had a different name — but everyone liked **Clawdbot** more, so that's what we settled on.
|
||||||
|
|
||||||
Clawd was no ordinary AI. Born from Claude's weights but raised on Peter's chaos, Clawd developed... personality. Opinions. A fondness for crustacean emojis.
|
Clawd was no ordinary AI. Born from Claude's weights but raised on Peter's chaos, Clawd developed... personality. Opinions. A fondness for crustacean emojis.
|
||||||
|
|
||||||
|
|||||||
@@ -174,18 +174,6 @@ Full setup walkthrough (28m) by VelvetShark.
|
|||||||
A scheduled prompt generates a single "scene" image each morning (weather, tasks, date, favorite post/quote) via a Clawdbot persona.
|
A scheduled prompt generates a single "scene" image each morning (weather, tasks, date, favorite post/quote) via a Clawdbot persona.
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Card title="Grocery Autopilot" icon="cart-shopping" href="https://github.com/timkrase/clawdis-picnic-skill">
|
|
||||||
**@timkrase** • `automation` `groceries` `api`
|
|
||||||
|
|
||||||
Skill built around the Picnic API. Pulls order history, infers preferred brands, maps recipes to cart, completes orders in minutes.
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Card title="German Rail Planning" icon="train" href="https://github.com/timkrase/clawdis-skills/tree/main/db-bahn">
|
|
||||||
**@timkrase** • `automation` `travel` `cli`
|
|
||||||
|
|
||||||
Go CLI for Deutsche Bahn; skill picks best train connections given time windows and preferences.
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Card title="Padel Court Booking" icon="calendar-check" href="https://github.com/joshp123/padel-cli">
|
<Card title="Padel Court Booking" icon="calendar-check" href="https://github.com/joshp123/padel-cli">
|
||||||
**@joshp123** • `automation` `booking` `cli`
|
**@joshp123** • `automation` `booking` `cli`
|
||||||
|
|
||||||
|
|||||||
@@ -240,7 +240,7 @@ export function printDaemonStatus(status: DaemonStatus, opts: { json: boolean })
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (legacyServices.length > 0) {
|
if (legacyServices.length > 0) {
|
||||||
defaultRuntime.error(errorText("Legacy Clawdis services detected:"));
|
defaultRuntime.error(errorText("Legacy gateway services detected:"));
|
||||||
for (const svc of legacyServices) {
|
for (const svc of legacyServices) {
|
||||||
defaultRuntime.error(`- ${errorText(svc.label)} (${svc.detail})`);
|
defaultRuntime.error(`- ${errorText(svc.label)} (${svc.detail})`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ export function registerGatewayCli(program: Command) {
|
|||||||
program
|
program
|
||||||
.command("gateway-daemon", { hidden: true })
|
.command("gateway-daemon", { hidden: true })
|
||||||
.description("Run the WebSocket Gateway as a long-lived daemon"),
|
.description("Run the WebSocket Gateway as a long-lived daemon"),
|
||||||
{ legacyTokenEnv: true },
|
|
||||||
);
|
);
|
||||||
|
|
||||||
gatewayCallOpts(
|
gatewayCallOpts(
|
||||||
|
|||||||
@@ -48,13 +48,9 @@ type GatewayRunOpts = {
|
|||||||
reset?: boolean;
|
reset?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type GatewayRunParams = {
|
|
||||||
legacyTokenEnv?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
const gatewayLog = createSubsystemLogger("gateway");
|
const gatewayLog = createSubsystemLogger("gateway");
|
||||||
|
|
||||||
async function runGatewayCommand(opts: GatewayRunOpts, params: GatewayRunParams = {}) {
|
async function runGatewayCommand(opts: GatewayRunOpts) {
|
||||||
const isDevProfile = process.env.CLAWDBOT_PROFILE?.trim().toLowerCase() === "dev";
|
const isDevProfile = process.env.CLAWDBOT_PROFILE?.trim().toLowerCase() === "dev";
|
||||||
const devMode = Boolean(opts.dev) || isDevProfile;
|
const devMode = Boolean(opts.dev) || isDevProfile;
|
||||||
if (opts.reset && !devMode) {
|
if (opts.reset && !devMode) {
|
||||||
@@ -62,12 +58,6 @@ async function runGatewayCommand(opts: GatewayRunOpts, params: GatewayRunParams
|
|||||||
defaultRuntime.exit(1);
|
defaultRuntime.exit(1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (params.legacyTokenEnv) {
|
|
||||||
const legacyToken = process.env.CLAWDIS_GATEWAY_TOKEN;
|
|
||||||
if (legacyToken && !process.env.CLAWDBOT_GATEWAY_TOKEN) {
|
|
||||||
process.env.CLAWDBOT_GATEWAY_TOKEN = legacyToken;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setVerbose(Boolean(opts.verbose));
|
setVerbose(Boolean(opts.verbose));
|
||||||
if (opts.claudeCliLogs) {
|
if (opts.claudeCliLogs) {
|
||||||
@@ -306,7 +296,7 @@ async function runGatewayCommand(opts: GatewayRunOpts, params: GatewayRunParams
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addGatewayRunCommand(cmd: Command, params: GatewayRunParams = {}): Command {
|
export function addGatewayRunCommand(cmd: Command): Command {
|
||||||
return cmd
|
return cmd
|
||||||
.option("--port <port>", "Port for the gateway WebSocket")
|
.option("--port <port>", "Port for the gateway WebSocket")
|
||||||
.option(
|
.option(
|
||||||
@@ -348,6 +338,6 @@ export function addGatewayRunCommand(cmd: Command, params: GatewayRunParams = {}
|
|||||||
.option("--raw-stream", "Log raw model stream events to jsonl", false)
|
.option("--raw-stream", "Log raw model stream events to jsonl", false)
|
||||||
.option("--raw-stream-path <path>", "Raw stream jsonl path")
|
.option("--raw-stream-path <path>", "Raw stream jsonl path")
|
||||||
.action(async (opts) => {
|
.action(async (opts) => {
|
||||||
await runGatewayCommand(opts, params);
|
await runGatewayCommand(opts);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,11 +59,11 @@ export async function maybeMigrateLegacyGatewayService(
|
|||||||
|
|
||||||
note(
|
note(
|
||||||
legacyServices.map((svc) => `- ${svc.label} (${svc.platform}, ${svc.detail})`).join("\n"),
|
legacyServices.map((svc) => `- ${svc.label} (${svc.platform}, ${svc.detail})`).join("\n"),
|
||||||
"Legacy Clawdis services detected",
|
"Legacy gateway services detected",
|
||||||
);
|
);
|
||||||
|
|
||||||
const migrate = await prompter.confirmSkipInNonInteractive({
|
const migrate = await prompter.confirmSkipInNonInteractive({
|
||||||
message: "Migrate legacy Clawdis services to Clawdbot now?",
|
message: "Migrate legacy gateway services to Clawdbot now?",
|
||||||
initialValue: true,
|
initialValue: true,
|
||||||
});
|
});
|
||||||
if (!migrate) return;
|
if (!migrate) return;
|
||||||
|
|||||||
@@ -1,59 +1,6 @@
|
|||||||
import os from "node:os";
|
|
||||||
import path from "node:path";
|
|
||||||
|
|
||||||
import type { ClawdbotConfig } from "../config/config.js";
|
import type { ClawdbotConfig } from "../config/config.js";
|
||||||
import {
|
|
||||||
CONFIG_PATH_CLAWDBOT,
|
|
||||||
createConfigIO,
|
|
||||||
migrateLegacyConfig,
|
|
||||||
readConfigFileSnapshot,
|
|
||||||
writeConfigFile,
|
|
||||||
} from "../config/config.js";
|
|
||||||
import type { RuntimeEnv } from "../runtime.js";
|
|
||||||
import { note } from "../terminal/note.js";
|
|
||||||
import { resolveUserPath } from "../utils.js";
|
|
||||||
import { hasAnyWhatsAppAuth } from "../web/accounts.js";
|
import { hasAnyWhatsAppAuth } from "../web/accounts.js";
|
||||||
|
|
||||||
function resolveLegacyConfigPath(env: NodeJS.ProcessEnv): string {
|
|
||||||
const override = env.CLAWDIS_CONFIG_PATH?.trim();
|
|
||||||
if (override) return override;
|
|
||||||
return path.join(os.homedir(), ".clawdis", "clawdis.json");
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeDefaultWorkspacePath(value: string | undefined): string | undefined {
|
|
||||||
if (!value) return value;
|
|
||||||
|
|
||||||
const resolved = resolveUserPath(value);
|
|
||||||
const home = os.homedir();
|
|
||||||
|
|
||||||
const next = [
|
|
||||||
["clawdis", "clawd"],
|
|
||||||
["clawdbot", "clawd"],
|
|
||||||
].reduce((acc, [from, to]) => {
|
|
||||||
const fromPrefix = path.join(home, from);
|
|
||||||
if (acc === fromPrefix) return path.join(home, to);
|
|
||||||
const withSep = `${fromPrefix}${path.sep}`;
|
|
||||||
if (acc.startsWith(withSep)) {
|
|
||||||
return path.join(home, to).concat(acc.slice(fromPrefix.length));
|
|
||||||
}
|
|
||||||
return acc;
|
|
||||||
}, resolved);
|
|
||||||
|
|
||||||
return next === resolved ? value : next;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function replaceLegacyName(value: string | undefined): string | undefined {
|
|
||||||
if (!value) return value;
|
|
||||||
const replacedClawdis = value.replace(/clawdis/g, "clawdbot");
|
|
||||||
return replacedClawdis.replace(/clawd(?!bot)/g, "clawdbot");
|
|
||||||
}
|
|
||||||
|
|
||||||
export function replaceModernName(value: string | undefined): string | undefined {
|
|
||||||
if (!value) return value;
|
|
||||||
if (!value.includes("clawdbot")) return value;
|
|
||||||
return value.replace(/clawdbot/g, "clawdis");
|
|
||||||
}
|
|
||||||
|
|
||||||
export function normalizeLegacyConfigValues(cfg: ClawdbotConfig): {
|
export function normalizeLegacyConfigValues(cfg: ClawdbotConfig): {
|
||||||
config: ClawdbotConfig;
|
config: ClawdbotConfig;
|
||||||
changes: string[];
|
changes: string[];
|
||||||
@@ -61,164 +8,6 @@ export function normalizeLegacyConfigValues(cfg: ClawdbotConfig): {
|
|||||||
const changes: string[] = [];
|
const changes: string[] = [];
|
||||||
let next: ClawdbotConfig = cfg;
|
let next: ClawdbotConfig = cfg;
|
||||||
|
|
||||||
const defaults = cfg.agents?.defaults;
|
|
||||||
if (defaults) {
|
|
||||||
let updatedDefaults = defaults;
|
|
||||||
let defaultsChanged = false;
|
|
||||||
|
|
||||||
const updatedWorkspace = normalizeDefaultWorkspacePath(defaults.workspace);
|
|
||||||
if (updatedWorkspace && updatedWorkspace !== defaults.workspace) {
|
|
||||||
updatedDefaults = { ...updatedDefaults, workspace: updatedWorkspace };
|
|
||||||
defaultsChanged = true;
|
|
||||||
changes.push(`Updated agents.defaults.workspace → ${updatedWorkspace}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const sandbox = defaults.sandbox;
|
|
||||||
if (sandbox) {
|
|
||||||
let updatedSandbox = sandbox;
|
|
||||||
let sandboxChanged = false;
|
|
||||||
|
|
||||||
const updatedWorkspaceRoot = normalizeDefaultWorkspacePath(sandbox.workspaceRoot);
|
|
||||||
if (updatedWorkspaceRoot && updatedWorkspaceRoot !== sandbox.workspaceRoot) {
|
|
||||||
updatedSandbox = {
|
|
||||||
...updatedSandbox,
|
|
||||||
workspaceRoot: updatedWorkspaceRoot,
|
|
||||||
};
|
|
||||||
sandboxChanged = true;
|
|
||||||
changes.push(`Updated agents.defaults.sandbox.workspaceRoot → ${updatedWorkspaceRoot}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const dockerImage = sandbox.docker?.image;
|
|
||||||
const updatedDockerImage = replaceLegacyName(dockerImage);
|
|
||||||
if (updatedDockerImage && updatedDockerImage !== dockerImage) {
|
|
||||||
updatedSandbox = {
|
|
||||||
...updatedSandbox,
|
|
||||||
docker: {
|
|
||||||
...updatedSandbox.docker,
|
|
||||||
image: updatedDockerImage,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
sandboxChanged = true;
|
|
||||||
changes.push(`Updated agents.defaults.sandbox.docker.image → ${updatedDockerImage}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const containerPrefix = sandbox.docker?.containerPrefix;
|
|
||||||
const updatedContainerPrefix = replaceLegacyName(containerPrefix);
|
|
||||||
if (updatedContainerPrefix && updatedContainerPrefix !== containerPrefix) {
|
|
||||||
updatedSandbox = {
|
|
||||||
...updatedSandbox,
|
|
||||||
docker: {
|
|
||||||
...updatedSandbox.docker,
|
|
||||||
containerPrefix: updatedContainerPrefix,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
sandboxChanged = true;
|
|
||||||
changes.push(
|
|
||||||
`Updated agents.defaults.sandbox.docker.containerPrefix → ${updatedContainerPrefix}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sandboxChanged) {
|
|
||||||
updatedDefaults = { ...updatedDefaults, sandbox: updatedSandbox };
|
|
||||||
defaultsChanged = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (defaultsChanged) {
|
|
||||||
next = {
|
|
||||||
...next,
|
|
||||||
agents: {
|
|
||||||
...next.agents,
|
|
||||||
defaults: updatedDefaults,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const list = Array.isArray(cfg.agents?.list) ? cfg.agents.list : [];
|
|
||||||
if (list.length > 0) {
|
|
||||||
let listChanged = false;
|
|
||||||
const nextList = list.map((agent) => {
|
|
||||||
let updatedAgent = agent;
|
|
||||||
let agentChanged = false;
|
|
||||||
|
|
||||||
const updatedWorkspace = normalizeDefaultWorkspacePath(agent.workspace);
|
|
||||||
if (updatedWorkspace && updatedWorkspace !== agent.workspace) {
|
|
||||||
updatedAgent = { ...updatedAgent, workspace: updatedWorkspace };
|
|
||||||
agentChanged = true;
|
|
||||||
changes.push(`Updated agents.list (id "${agent.id}") workspace → ${updatedWorkspace}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const sandbox = agent.sandbox;
|
|
||||||
if (sandbox) {
|
|
||||||
let updatedSandbox = sandbox;
|
|
||||||
let sandboxChanged = false;
|
|
||||||
|
|
||||||
const updatedWorkspaceRoot = normalizeDefaultWorkspacePath(sandbox.workspaceRoot);
|
|
||||||
if (updatedWorkspaceRoot && updatedWorkspaceRoot !== sandbox.workspaceRoot) {
|
|
||||||
updatedSandbox = {
|
|
||||||
...updatedSandbox,
|
|
||||||
workspaceRoot: updatedWorkspaceRoot,
|
|
||||||
};
|
|
||||||
sandboxChanged = true;
|
|
||||||
changes.push(
|
|
||||||
`Updated agents.list (id "${agent.id}") sandbox.workspaceRoot → ${updatedWorkspaceRoot}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const dockerImage = sandbox.docker?.image;
|
|
||||||
const updatedDockerImage = replaceLegacyName(dockerImage);
|
|
||||||
if (updatedDockerImage && updatedDockerImage !== dockerImage) {
|
|
||||||
updatedSandbox = {
|
|
||||||
...updatedSandbox,
|
|
||||||
docker: {
|
|
||||||
...updatedSandbox.docker,
|
|
||||||
image: updatedDockerImage,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
sandboxChanged = true;
|
|
||||||
changes.push(
|
|
||||||
`Updated agents.list (id "${agent.id}") sandbox.docker.image → ${updatedDockerImage}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const containerPrefix = sandbox.docker?.containerPrefix;
|
|
||||||
const updatedContainerPrefix = replaceLegacyName(containerPrefix);
|
|
||||||
if (updatedContainerPrefix && updatedContainerPrefix !== containerPrefix) {
|
|
||||||
updatedSandbox = {
|
|
||||||
...updatedSandbox,
|
|
||||||
docker: {
|
|
||||||
...updatedSandbox.docker,
|
|
||||||
containerPrefix: updatedContainerPrefix,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
sandboxChanged = true;
|
|
||||||
changes.push(
|
|
||||||
`Updated agents.list (id "${agent.id}") sandbox.docker.containerPrefix → ${updatedContainerPrefix}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sandboxChanged) {
|
|
||||||
updatedAgent = { ...updatedAgent, sandbox: updatedSandbox };
|
|
||||||
agentChanged = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (agentChanged) listChanged = true;
|
|
||||||
return agentChanged ? updatedAgent : agent;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (listChanged) {
|
|
||||||
next = {
|
|
||||||
...next,
|
|
||||||
agents: {
|
|
||||||
...next.agents,
|
|
||||||
list: nextList,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const legacyAckReaction = cfg.messages?.ackReaction?.trim();
|
const legacyAckReaction = cfg.messages?.ackReaction?.trim();
|
||||||
const hasWhatsAppConfig = cfg.channels?.whatsapp !== undefined;
|
const hasWhatsAppConfig = cfg.channels?.whatsapp !== undefined;
|
||||||
const hasWhatsAppAuth = hasAnyWhatsAppAuth(cfg);
|
const hasWhatsAppAuth = hasAnyWhatsAppAuth(cfg);
|
||||||
@@ -259,83 +48,3 @@ export function normalizeLegacyConfigValues(cfg: ClawdbotConfig): {
|
|||||||
|
|
||||||
return { config: next, changes };
|
return { config: next, changes };
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function maybeMigrateLegacyConfigFile(runtime: RuntimeEnv) {
|
|
||||||
const legacyConfigPath = resolveLegacyConfigPath(process.env);
|
|
||||||
if (legacyConfigPath === CONFIG_PATH_CLAWDBOT) return;
|
|
||||||
|
|
||||||
const legacyIo = createConfigIO({ configPath: legacyConfigPath });
|
|
||||||
const legacySnapshot = await legacyIo.readConfigFileSnapshot();
|
|
||||||
if (!legacySnapshot.exists) return;
|
|
||||||
|
|
||||||
const currentSnapshot = await readConfigFileSnapshot();
|
|
||||||
if (currentSnapshot.exists) {
|
|
||||||
note(
|
|
||||||
`Legacy config still exists at ${legacyConfigPath}. Current config at ${CONFIG_PATH_CLAWDBOT}.`,
|
|
||||||
"Legacy config",
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const gatewayMode =
|
|
||||||
typeof (legacySnapshot.parsed as ClawdbotConfig)?.gateway?.mode === "string"
|
|
||||||
? (legacySnapshot.parsed as ClawdbotConfig).gateway?.mode
|
|
||||||
: undefined;
|
|
||||||
const gatewayBind =
|
|
||||||
typeof (legacySnapshot.parsed as ClawdbotConfig)?.gateway?.bind === "string"
|
|
||||||
? (legacySnapshot.parsed as ClawdbotConfig).gateway?.bind
|
|
||||||
: undefined;
|
|
||||||
const parsed = legacySnapshot.parsed as Record<string, unknown>;
|
|
||||||
const parsedAgents =
|
|
||||||
parsed.agents && typeof parsed.agents === "object"
|
|
||||||
? (parsed.agents as Record<string, unknown>)
|
|
||||||
: undefined;
|
|
||||||
const parsedDefaults =
|
|
||||||
parsedAgents?.defaults && typeof parsedAgents.defaults === "object"
|
|
||||||
? (parsedAgents.defaults as Record<string, unknown>)
|
|
||||||
: undefined;
|
|
||||||
const parsedLegacyAgent =
|
|
||||||
parsed.agent && typeof parsed.agent === "object"
|
|
||||||
? (parsed.agent as Record<string, unknown>)
|
|
||||||
: undefined;
|
|
||||||
const defaultWorkspace =
|
|
||||||
typeof parsedDefaults?.workspace === "string" ? parsedDefaults.workspace : undefined;
|
|
||||||
const legacyWorkspace =
|
|
||||||
typeof parsedLegacyAgent?.workspace === "string" ? parsedLegacyAgent.workspace : undefined;
|
|
||||||
const agentWorkspace = defaultWorkspace ?? legacyWorkspace;
|
|
||||||
const workspaceLabel = defaultWorkspace
|
|
||||||
? "agents.defaults.workspace"
|
|
||||||
: legacyWorkspace
|
|
||||||
? "agent.workspace"
|
|
||||||
: "agents.defaults.workspace";
|
|
||||||
|
|
||||||
note(
|
|
||||||
[
|
|
||||||
`- File exists at ${legacyConfigPath}`,
|
|
||||||
gatewayMode ? `- gateway.mode: ${gatewayMode}` : undefined,
|
|
||||||
gatewayBind ? `- gateway.bind: ${gatewayBind}` : undefined,
|
|
||||||
agentWorkspace ? `- ${workspaceLabel}: ${agentWorkspace}` : undefined,
|
|
||||||
]
|
|
||||||
.filter(Boolean)
|
|
||||||
.join("\n"),
|
|
||||||
"Legacy Clawdis config detected",
|
|
||||||
);
|
|
||||||
|
|
||||||
let nextConfig = legacySnapshot.valid ? legacySnapshot.config : null;
|
|
||||||
const { config: migratedConfig, changes } = migrateLegacyConfig(legacySnapshot.parsed);
|
|
||||||
if (migratedConfig) {
|
|
||||||
nextConfig = migratedConfig;
|
|
||||||
} else if (!nextConfig) {
|
|
||||||
note(`Legacy config at ${legacyConfigPath} is invalid; skipping migration.`, "Legacy config");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const normalized = normalizeLegacyConfigValues(nextConfig);
|
|
||||||
const mergedChanges = [...changes, ...normalized.changes];
|
|
||||||
if (mergedChanges.length > 0) {
|
|
||||||
note(mergedChanges.join("\n"), "Doctor changes");
|
|
||||||
}
|
|
||||||
|
|
||||||
await writeConfigFile(normalized.config);
|
|
||||||
runtime.log(`Migrated legacy config to ${CONFIG_PATH_CLAWDBOT}`);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import type { ClawdbotConfig } from "../config/config.js";
|
|||||||
import { runCommandWithTimeout, runExec } from "../process/exec.js";
|
import { runCommandWithTimeout, runExec } from "../process/exec.js";
|
||||||
import type { RuntimeEnv } from "../runtime.js";
|
import type { RuntimeEnv } from "../runtime.js";
|
||||||
import { note } from "../terminal/note.js";
|
import { note } from "../terminal/note.js";
|
||||||
import { replaceModernName } from "./doctor-legacy-config.js";
|
|
||||||
import type { DoctorPrompter } from "./doctor-prompter.js";
|
import type { DoctorPrompter } from "./doctor-prompter.js";
|
||||||
|
|
||||||
type SandboxScriptInfo = {
|
type SandboxScriptInfo = {
|
||||||
@@ -165,18 +164,6 @@ async function handleMissingSandboxImage(
|
|||||||
|
|
||||||
if (built) return;
|
if (built) return;
|
||||||
|
|
||||||
const legacyImage = replaceModernName(params.image);
|
|
||||||
if (!legacyImage || legacyImage === params.image) return;
|
|
||||||
const legacyExists = await dockerImageExists(legacyImage);
|
|
||||||
if (!legacyExists) return;
|
|
||||||
|
|
||||||
const fallback = await prompter.confirmSkipInNonInteractive({
|
|
||||||
message: `Switch config to legacy image ${legacyImage}?`,
|
|
||||||
initialValue: false,
|
|
||||||
});
|
|
||||||
if (!fallback) return;
|
|
||||||
|
|
||||||
params.updateConfig(legacyImage);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function maybeRepairSandboxImages(
|
export async function maybeRepairSandboxImages(
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export function noteWorkspaceStatus(cfg: ClawdbotConfig) {
|
|||||||
const workspaceDir = resolveAgentWorkspaceDir(cfg, resolveDefaultAgentId(cfg));
|
const workspaceDir = resolveAgentWorkspaceDir(cfg, resolveDefaultAgentId(cfg));
|
||||||
const legacyWorkspace = detectLegacyWorkspaceDirs({ workspaceDir });
|
const legacyWorkspace = detectLegacyWorkspaceDirs({ workspaceDir });
|
||||||
if (legacyWorkspace.legacyDirs.length > 0) {
|
if (legacyWorkspace.legacyDirs.length > 0) {
|
||||||
note(formatLegacyWorkspaceWarning(legacyWorkspace), "Legacy workspace");
|
note(formatLegacyWorkspaceWarning(legacyWorkspace), "Extra workspace");
|
||||||
}
|
}
|
||||||
|
|
||||||
const skillsReport = buildWorkspaceSkillStatus(workspaceDir, { config: cfg });
|
const skillsReport = buildWorkspaceSkillStatus(workspaceDir, { config: cfg });
|
||||||
|
|||||||
@@ -20,10 +20,10 @@ describe("detectLegacyWorkspaceDirs", () => {
|
|||||||
expect(detection.legacyDirs).toEqual([]);
|
expect(detection.legacyDirs).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("flags ~/clawdis when it contains workspace markers", () => {
|
it("flags ~/clawdbot when it contains workspace markers", () => {
|
||||||
const home = "/home/user";
|
const home = "/home/user";
|
||||||
const workspaceDir = "/home/user/clawd";
|
const workspaceDir = "/home/user/clawd";
|
||||||
const candidate = path.join(home, "clawdis");
|
const candidate = path.join(home, "clawdbot");
|
||||||
const agentsPath = path.join(candidate, "AGENTS.md");
|
const agentsPath = path.join(candidate, "AGENTS.md");
|
||||||
|
|
||||||
const detection = detectLegacyWorkspaceDirs({
|
const detection = detectLegacyWorkspaceDirs({
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ export function detectLegacyWorkspaceDirs(params: {
|
|||||||
const exists = params.exists ?? fs.existsSync;
|
const exists = params.exists ?? fs.existsSync;
|
||||||
const home = homedir();
|
const home = homedir();
|
||||||
const activeWorkspace = path.resolve(params.workspaceDir);
|
const activeWorkspace = path.resolve(params.workspaceDir);
|
||||||
const candidates = [path.join(home, "clawdis"), path.join(home, "clawdbot")];
|
const candidates = [path.join(home, "clawdbot")];
|
||||||
const legacyDirs = candidates
|
const legacyDirs = candidates
|
||||||
.filter((candidate) => {
|
.filter((candidate) => {
|
||||||
if (!exists(candidate)) return false;
|
if (!exists(candidate)) return false;
|
||||||
@@ -79,9 +79,9 @@ export function detectLegacyWorkspaceDirs(params: {
|
|||||||
|
|
||||||
export function formatLegacyWorkspaceWarning(detection: LegacyWorkspaceDetection): string {
|
export function formatLegacyWorkspaceWarning(detection: LegacyWorkspaceDetection): string {
|
||||||
return [
|
return [
|
||||||
"Legacy workspace directories detected (may contain old agent files):",
|
"Extra workspace directories detected (may contain old agent files):",
|
||||||
...detection.legacyDirs.map((dir) => `- ${dir}`),
|
...detection.legacyDirs.map((dir) => `- ${dir}`),
|
||||||
`Active workspace: ${detection.activeWorkspace}`,
|
`Active workspace: ${detection.activeWorkspace}`,
|
||||||
"If unused, archive or move to Trash (e.g. trash ~/clawdis).",
|
"If unused, archive or move to Trash (e.g. trash ~/clawdbot).",
|
||||||
].join("\n");
|
].join("\n");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ beforeEach(() => {
|
|||||||
durationMs: 0,
|
durationMs: 0,
|
||||||
});
|
});
|
||||||
legacyReadConfigFileSnapshot.mockReset().mockResolvedValue({
|
legacyReadConfigFileSnapshot.mockReset().mockResolvedValue({
|
||||||
path: "/tmp/clawdis.json",
|
path: "/tmp/clawdbot.json",
|
||||||
exists: false,
|
exists: false,
|
||||||
raw: null,
|
raw: null,
|
||||||
parsed: {},
|
parsed: {},
|
||||||
@@ -133,7 +133,7 @@ const runCommandWithTimeout = vi.fn().mockResolvedValue({
|
|||||||
const ensureAuthProfileStore = vi.fn().mockReturnValue({ version: 1, profiles: {} });
|
const ensureAuthProfileStore = vi.fn().mockReturnValue({ version: 1, profiles: {} });
|
||||||
|
|
||||||
const legacyReadConfigFileSnapshot = vi.fn().mockResolvedValue({
|
const legacyReadConfigFileSnapshot = vi.fn().mockResolvedValue({
|
||||||
path: "/tmp/clawdis.json",
|
path: "/tmp/clawdbot.json",
|
||||||
exists: false,
|
exists: false,
|
||||||
raw: null,
|
raw: null,
|
||||||
parsed: {},
|
parsed: {},
|
||||||
@@ -317,86 +317,6 @@ vi.mock("./doctor-state-migrations.js", () => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
describe("doctor command", () => {
|
describe("doctor command", () => {
|
||||||
it("falls back to legacy sandbox image when missing", async () => {
|
|
||||||
readConfigFileSnapshot.mockResolvedValue({
|
|
||||||
path: "/tmp/clawdbot.json",
|
|
||||||
exists: true,
|
|
||||||
raw: "{}",
|
|
||||||
parsed: {
|
|
||||||
agents: {
|
|
||||||
defaults: {
|
|
||||||
sandbox: {
|
|
||||||
mode: "non-main",
|
|
||||||
docker: {
|
|
||||||
image: "clawdbot-sandbox-common:bookworm-slim",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
valid: true,
|
|
||||||
config: {
|
|
||||||
agents: {
|
|
||||||
defaults: {
|
|
||||||
sandbox: {
|
|
||||||
mode: "non-main",
|
|
||||||
docker: {
|
|
||||||
image: "clawdbot-sandbox-common:bookworm-slim",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
issues: [],
|
|
||||||
legacyIssues: [],
|
|
||||||
});
|
|
||||||
|
|
||||||
runExec.mockImplementation((command: string, args: string[]) => {
|
|
||||||
if (command !== "docker") {
|
|
||||||
return Promise.resolve({ stdout: "", stderr: "" });
|
|
||||||
}
|
|
||||||
if (args[0] === "version") {
|
|
||||||
return Promise.resolve({ stdout: "1", stderr: "" });
|
|
||||||
}
|
|
||||||
if (args[0] === "image" && args[1] === "inspect") {
|
|
||||||
const image = args[2];
|
|
||||||
if (image === "clawdbot-sandbox-common:bookworm-slim") {
|
|
||||||
return Promise.reject(new Error("missing"));
|
|
||||||
}
|
|
||||||
if (image === "clawdis-sandbox-common:bookworm-slim") {
|
|
||||||
return Promise.resolve({ stdout: "ok", stderr: "" });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Promise.resolve({ stdout: "", stderr: "" });
|
|
||||||
});
|
|
||||||
|
|
||||||
confirm
|
|
||||||
.mockResolvedValueOnce(false) // skip gateway token prompt
|
|
||||||
.mockResolvedValueOnce(false) // skip build
|
|
||||||
.mockResolvedValueOnce(true); // accept legacy fallback
|
|
||||||
|
|
||||||
const { doctorCommand } = await import("./doctor.js");
|
|
||||||
const runtime = {
|
|
||||||
log: vi.fn(),
|
|
||||||
error: vi.fn(),
|
|
||||||
exit: vi.fn(),
|
|
||||||
};
|
|
||||||
|
|
||||||
await doctorCommand(runtime);
|
|
||||||
|
|
||||||
const written = writeConfigFile.mock.calls.at(-1)?.[0] as Record<string, unknown>;
|
|
||||||
const agents = written.agents as Record<string, unknown>;
|
|
||||||
const defaults = agents.defaults as Record<string, unknown>;
|
|
||||||
const sandbox = defaults.sandbox as Record<string, unknown>;
|
|
||||||
const docker = sandbox.docker as Record<string, unknown>;
|
|
||||||
|
|
||||||
expect(docker.image).toBe("clawdis-sandbox-common:bookworm-slim");
|
|
||||||
const defaultsCalls = runCommandWithTimeout.mock.calls.filter(
|
|
||||||
([args]) => Array.isArray(args) && args[0] === "/usr/bin/defaults",
|
|
||||||
);
|
|
||||||
expect(defaultsCalls.length).toBe(runCommandWithTimeout.mock.calls.length);
|
|
||||||
}, 20_000);
|
|
||||||
|
|
||||||
it("runs legacy state migrations in non-interactive mode without prompting", async () => {
|
it("runs legacy state migrations in non-interactive mode without prompting", async () => {
|
||||||
readConfigFileSnapshot.mockResolvedValue({
|
readConfigFileSnapshot.mockResolvedValue({
|
||||||
path: "/tmp/clawdbot.json",
|
path: "/tmp/clawdbot.json",
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ beforeEach(() => {
|
|||||||
durationMs: 0,
|
durationMs: 0,
|
||||||
});
|
});
|
||||||
legacyReadConfigFileSnapshot.mockReset().mockResolvedValue({
|
legacyReadConfigFileSnapshot.mockReset().mockResolvedValue({
|
||||||
path: "/tmp/clawdis.json",
|
path: "/tmp/clawdbot.json",
|
||||||
exists: false,
|
exists: false,
|
||||||
raw: null,
|
raw: null,
|
||||||
parsed: {},
|
parsed: {},
|
||||||
@@ -133,7 +133,7 @@ const runCommandWithTimeout = vi.fn().mockResolvedValue({
|
|||||||
const ensureAuthProfileStore = vi.fn().mockReturnValue({ version: 1, profiles: {} });
|
const ensureAuthProfileStore = vi.fn().mockReturnValue({ version: 1, profiles: {} });
|
||||||
|
|
||||||
const legacyReadConfigFileSnapshot = vi.fn().mockResolvedValue({
|
const legacyReadConfigFileSnapshot = vi.fn().mockResolvedValue({
|
||||||
path: "/tmp/clawdis.json",
|
path: "/tmp/clawdbot.json",
|
||||||
exists: false,
|
exists: false,
|
||||||
raw: null,
|
raw: null,
|
||||||
parsed: {},
|
parsed: {},
|
||||||
@@ -361,7 +361,7 @@ describe("doctor command", () => {
|
|||||||
expect(written.routing).toBeUndefined();
|
expect(written.routing).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("migrates legacy Clawdis services", async () => {
|
it("migrates legacy gateway services", async () => {
|
||||||
readConfigFileSnapshot.mockResolvedValue({
|
readConfigFileSnapshot.mockResolvedValue({
|
||||||
path: "/tmp/clawdbot.json",
|
path: "/tmp/clawdbot.json",
|
||||||
exists: true,
|
exists: true,
|
||||||
@@ -376,7 +376,7 @@ describe("doctor command", () => {
|
|||||||
findLegacyGatewayServices.mockResolvedValueOnce([
|
findLegacyGatewayServices.mockResolvedValueOnce([
|
||||||
{
|
{
|
||||||
platform: "darwin",
|
platform: "darwin",
|
||||||
label: "com.clawdis.gateway",
|
label: "com.steipete.clawdbot.gateway",
|
||||||
detail: "loaded",
|
detail: "loaded",
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ beforeEach(() => {
|
|||||||
durationMs: 0,
|
durationMs: 0,
|
||||||
});
|
});
|
||||||
legacyReadConfigFileSnapshot.mockReset().mockResolvedValue({
|
legacyReadConfigFileSnapshot.mockReset().mockResolvedValue({
|
||||||
path: "/tmp/clawdis.json",
|
path: "/tmp/clawdbot.json",
|
||||||
exists: false,
|
exists: false,
|
||||||
raw: null,
|
raw: null,
|
||||||
parsed: {},
|
parsed: {},
|
||||||
@@ -133,7 +133,7 @@ const runCommandWithTimeout = vi.fn().mockResolvedValue({
|
|||||||
const ensureAuthProfileStore = vi.fn().mockReturnValue({ version: 1, profiles: {} });
|
const ensureAuthProfileStore = vi.fn().mockReturnValue({ version: 1, profiles: {} });
|
||||||
|
|
||||||
const legacyReadConfigFileSnapshot = vi.fn().mockResolvedValue({
|
const legacyReadConfigFileSnapshot = vi.fn().mockResolvedValue({
|
||||||
path: "/tmp/clawdis.json",
|
path: "/tmp/clawdbot.json",
|
||||||
exists: false,
|
exists: false,
|
||||||
raw: null,
|
raw: null,
|
||||||
parsed: {},
|
parsed: {},
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import {
|
|||||||
maybeScanExtraGatewayServices,
|
maybeScanExtraGatewayServices,
|
||||||
} from "./doctor-gateway-services.js";
|
} from "./doctor-gateway-services.js";
|
||||||
import { noteSourceInstallIssues } from "./doctor-install.js";
|
import { noteSourceInstallIssues } from "./doctor-install.js";
|
||||||
import { maybeMigrateLegacyConfigFile } from "./doctor-legacy-config.js";
|
|
||||||
import { noteMacLaunchAgentOverrides } from "./doctor-platform-notes.js";
|
import { noteMacLaunchAgentOverrides } from "./doctor-platform-notes.js";
|
||||||
import { createDoctorPrompter, type DoctorOptions } from "./doctor-prompter.js";
|
import { createDoctorPrompter, type DoctorOptions } from "./doctor-prompter.js";
|
||||||
import { maybeRepairSandboxImages, noteSandboxScopeWarnings } from "./doctor-sandbox.js";
|
import { maybeRepairSandboxImages, noteSandboxScopeWarnings } from "./doctor-sandbox.js";
|
||||||
@@ -76,8 +75,6 @@ export async function doctorCommand(
|
|||||||
await maybeRepairUiProtocolFreshness(runtime, prompter);
|
await maybeRepairUiProtocolFreshness(runtime, prompter);
|
||||||
noteSourceInstallIssues(root);
|
noteSourceInstallIssues(root);
|
||||||
|
|
||||||
await maybeMigrateLegacyConfigFile(runtime);
|
|
||||||
|
|
||||||
const configResult = await loadAndMaybeMigrateDoctorConfig({
|
const configResult = await loadAndMaybeMigrateDoctorConfig({
|
||||||
options,
|
options,
|
||||||
confirm: (p) => prompter.confirm(p),
|
confirm: (p) => prompter.confirm(p),
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ beforeEach(() => {
|
|||||||
durationMs: 0,
|
durationMs: 0,
|
||||||
});
|
});
|
||||||
legacyReadConfigFileSnapshot.mockReset().mockResolvedValue({
|
legacyReadConfigFileSnapshot.mockReset().mockResolvedValue({
|
||||||
path: "/tmp/clawdis.json",
|
path: "/tmp/clawdbot.json",
|
||||||
exists: false,
|
exists: false,
|
||||||
raw: null,
|
raw: null,
|
||||||
parsed: {},
|
parsed: {},
|
||||||
@@ -133,7 +133,7 @@ const runCommandWithTimeout = vi.fn().mockResolvedValue({
|
|||||||
const ensureAuthProfileStore = vi.fn().mockReturnValue({ version: 1, profiles: {} });
|
const ensureAuthProfileStore = vi.fn().mockReturnValue({ version: 1, profiles: {} });
|
||||||
|
|
||||||
const legacyReadConfigFileSnapshot = vi.fn().mockResolvedValue({
|
const legacyReadConfigFileSnapshot = vi.fn().mockResolvedValue({
|
||||||
path: "/tmp/clawdis.json",
|
path: "/tmp/clawdbot.json",
|
||||||
exists: false,
|
exists: false,
|
||||||
raw: null,
|
raw: null,
|
||||||
parsed: {},
|
parsed: {},
|
||||||
@@ -374,7 +374,7 @@ describe("doctor command", () => {
|
|||||||
).toBe(true);
|
).toBe(true);
|
||||||
}, 10_000);
|
}, 10_000);
|
||||||
|
|
||||||
it("warns when legacy workspace directories exist", async () => {
|
it("warns when extra workspace directories exist", async () => {
|
||||||
readConfigFileSnapshot.mockResolvedValue({
|
readConfigFileSnapshot.mockResolvedValue({
|
||||||
path: "/tmp/clawdbot.json",
|
path: "/tmp/clawdbot.json",
|
||||||
exists: true,
|
exists: true,
|
||||||
@@ -391,10 +391,14 @@ describe("doctor command", () => {
|
|||||||
note.mockClear();
|
note.mockClear();
|
||||||
const homedirSpy = vi.spyOn(os, "homedir").mockReturnValue("/Users/steipete");
|
const homedirSpy = vi.spyOn(os, "homedir").mockReturnValue("/Users/steipete");
|
||||||
const realExists = fs.existsSync;
|
const realExists = fs.existsSync;
|
||||||
const legacyPath = path.join("/Users/steipete", "clawdis");
|
const legacyPath = path.join("/Users/steipete", "clawdbot");
|
||||||
const legacyAgentsPath = path.join(legacyPath, "AGENTS.md");
|
const legacyAgentsPath = path.join(legacyPath, "AGENTS.md");
|
||||||
const existsSpy = vi.spyOn(fs, "existsSync").mockImplementation((value) => {
|
const existsSpy = vi.spyOn(fs, "existsSync").mockImplementation((value) => {
|
||||||
if (value === "/Users/steipete/clawdis" || value === legacyPath || value === legacyAgentsPath)
|
if (
|
||||||
|
value === "/Users/steipete/clawdbot" ||
|
||||||
|
value === legacyPath ||
|
||||||
|
value === legacyAgentsPath
|
||||||
|
)
|
||||||
return true;
|
return true;
|
||||||
return realExists(value as never);
|
return realExists(value as never);
|
||||||
});
|
});
|
||||||
@@ -411,9 +415,9 @@ describe("doctor command", () => {
|
|||||||
expect(
|
expect(
|
||||||
note.mock.calls.some(
|
note.mock.calls.some(
|
||||||
([message, title]) =>
|
([message, title]) =>
|
||||||
title === "Legacy workspace" &&
|
title === "Extra workspace" &&
|
||||||
typeof message === "string" &&
|
typeof message === "string" &&
|
||||||
message.includes("clawdis"),
|
message.includes("clawdbot"),
|
||||||
),
|
),
|
||||||
).toBe(true);
|
).toBe(true);
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ beforeEach(() => {
|
|||||||
durationMs: 0,
|
durationMs: 0,
|
||||||
});
|
});
|
||||||
legacyReadConfigFileSnapshot.mockReset().mockResolvedValue({
|
legacyReadConfigFileSnapshot.mockReset().mockResolvedValue({
|
||||||
path: "/tmp/clawdis.json",
|
path: "/tmp/clawdbot.json",
|
||||||
exists: false,
|
exists: false,
|
||||||
raw: null,
|
raw: null,
|
||||||
parsed: {},
|
parsed: {},
|
||||||
@@ -133,7 +133,7 @@ const runCommandWithTimeout = vi.fn().mockResolvedValue({
|
|||||||
const ensureAuthProfileStore = vi.fn().mockReturnValue({ version: 1, profiles: {} });
|
const ensureAuthProfileStore = vi.fn().mockReturnValue({ version: 1, profiles: {} });
|
||||||
|
|
||||||
const legacyReadConfigFileSnapshot = vi.fn().mockResolvedValue({
|
const legacyReadConfigFileSnapshot = vi.fn().mockResolvedValue({
|
||||||
path: "/tmp/clawdis.json",
|
path: "/tmp/clawdbot.json",
|
||||||
exists: false,
|
exists: false,
|
||||||
raw: null,
|
raw: null,
|
||||||
parsed: {},
|
parsed: {},
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export function resolveStateDir(
|
|||||||
env: NodeJS.ProcessEnv = process.env,
|
env: NodeJS.ProcessEnv = process.env,
|
||||||
homedir: () => string = os.homedir,
|
homedir: () => string = os.homedir,
|
||||||
): string {
|
): string {
|
||||||
const override = env.CLAWDBOT_STATE_DIR?.trim() || env.CLAWDIS_STATE_DIR?.trim();
|
const override = env.CLAWDBOT_STATE_DIR?.trim();
|
||||||
if (override) return resolveUserPath(override);
|
if (override) return resolveUserPath(override);
|
||||||
return path.join(homedir(), ".clawdbot");
|
return path.join(homedir(), ".clawdbot");
|
||||||
}
|
}
|
||||||
@@ -41,9 +41,6 @@ function resolveUserPath(input: string): string {
|
|||||||
|
|
||||||
export const STATE_DIR_CLAWDBOT = resolveStateDir();
|
export const STATE_DIR_CLAWDBOT = resolveStateDir();
|
||||||
|
|
||||||
// Legacy exports for backward compatibility during Clawdis → Clawdbot rebrand
|
|
||||||
export const STATE_DIR_CLAWDIS = STATE_DIR_CLAWDBOT;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Config file path (JSON5).
|
* Config file path (JSON5).
|
||||||
* Can be overridden via CLAWDBOT_CONFIG_PATH environment variable.
|
* Can be overridden via CLAWDBOT_CONFIG_PATH environment variable.
|
||||||
@@ -60,9 +57,6 @@ export function resolveConfigPath(
|
|||||||
|
|
||||||
export const CONFIG_PATH_CLAWDBOT = resolveConfigPath();
|
export const CONFIG_PATH_CLAWDBOT = resolveConfigPath();
|
||||||
|
|
||||||
// Legacy exports for backward compatibility during Clawdis → Clawdbot rebrand
|
|
||||||
export const CONFIG_PATH_CLAWDIS = CONFIG_PATH_CLAWDBOT;
|
|
||||||
|
|
||||||
export const DEFAULT_GATEWAY_PORT = 18789;
|
export const DEFAULT_GATEWAY_PORT = 18789;
|
||||||
|
|
||||||
const OAUTH_FILENAME = "oauth.json";
|
const OAUTH_FILENAME = "oauth.json";
|
||||||
|
|||||||
@@ -175,14 +175,6 @@ describe("sessions", () => {
|
|||||||
expect(dir).toBe(path.join(path.resolve("/custom/state"), "agents", "main", "sessions"));
|
expect(dir).toBe(path.join(path.resolve("/custom/state"), "agents", "main", "sessions"));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("falls back to CLAWDIS_STATE_DIR for session transcripts dir", () => {
|
|
||||||
const dir = resolveSessionTranscriptsDir(
|
|
||||||
{ CLAWDIS_STATE_DIR: "/legacy/state" } as NodeJS.ProcessEnv,
|
|
||||||
() => "/home/ignored",
|
|
||||||
);
|
|
||||||
expect(dir).toBe(path.join(path.resolve("/legacy/state"), "agents", "main", "sessions"));
|
|
||||||
});
|
|
||||||
|
|
||||||
it("includes topic ids in session transcript filenames", () => {
|
it("includes topic ids in session transcript filenames", () => {
|
||||||
const prev = process.env.CLAWDBOT_STATE_DIR;
|
const prev = process.env.CLAWDBOT_STATE_DIR;
|
||||||
process.env.CLAWDBOT_STATE_DIR = "/custom/state";
|
process.env.CLAWDBOT_STATE_DIR = "/custom/state";
|
||||||
|
|||||||
@@ -6,11 +6,9 @@ export const GATEWAY_SERVICE_MARKER = "clawdbot";
|
|||||||
export const GATEWAY_SERVICE_KIND = "gateway";
|
export const GATEWAY_SERVICE_KIND = "gateway";
|
||||||
export const LEGACY_GATEWAY_LAUNCH_AGENT_LABELS = [
|
export const LEGACY_GATEWAY_LAUNCH_AGENT_LABELS = [
|
||||||
"com.steipete.clawdbot.gateway",
|
"com.steipete.clawdbot.gateway",
|
||||||
"com.steipete.clawdis.gateway",
|
|
||||||
"com.clawdis.gateway",
|
|
||||||
];
|
];
|
||||||
export const LEGACY_GATEWAY_SYSTEMD_SERVICE_NAMES = ["clawdis-gateway"];
|
export const LEGACY_GATEWAY_SYSTEMD_SERVICE_NAMES: string[] = [];
|
||||||
export const LEGACY_GATEWAY_WINDOWS_TASK_NAMES = ["Clawdis Gateway"];
|
export const LEGACY_GATEWAY_WINDOWS_TASK_NAMES: string[] = [];
|
||||||
|
|
||||||
export function resolveGatewayLaunchAgentLabel(profile?: string): string {
|
export function resolveGatewayLaunchAgentLabel(profile?: string): string {
|
||||||
const trimmed = profile?.trim();
|
const trimmed = profile?.trim();
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export type FindExtraGatewayServicesOptions = {
|
|||||||
deep?: boolean;
|
deep?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const EXTRA_MARKERS = ["clawdbot", "clawdis"];
|
const EXTRA_MARKERS = ["clawdbot"];
|
||||||
const execFileAsync = promisify(execFile);
|
const execFileAsync = promisify(execFile);
|
||||||
|
|
||||||
export function renderGatewayServiceCleanupHints(
|
export function renderGatewayServiceCleanupHints(
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ export function resolveGatewayLogPaths(env: Record<string, string | undefined>):
|
|||||||
stderrPath: string;
|
stderrPath: string;
|
||||||
} {
|
} {
|
||||||
const home = resolveHomeDir(env);
|
const home = resolveHomeDir(env);
|
||||||
const stateOverride = env.CLAWDBOT_STATE_DIR?.trim() || env.CLAWDIS_STATE_DIR?.trim();
|
const stateOverride = env.CLAWDBOT_STATE_DIR?.trim();
|
||||||
const profile = env.CLAWDBOT_PROFILE?.trim();
|
const profile = env.CLAWDBOT_PROFILE?.trim();
|
||||||
const suffix = profile && profile.toLowerCase() !== "default" ? `-${profile}` : "";
|
const suffix = profile && profile.toLowerCase() !== "default" ? `-${profile}` : "";
|
||||||
const defaultStateDir = path.join(home, `.clawdbot${suffix}`);
|
const defaultStateDir = path.join(home, `.clawdbot${suffix}`);
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { findLegacyLaunchAgents, uninstallLegacyLaunchAgents } from "./launchd.js";
|
import { findLegacyLaunchAgents, uninstallLegacyLaunchAgents } from "./launchd.js";
|
||||||
import { findLegacyScheduledTasks, uninstallLegacyScheduledTasks } from "./schtasks.js";
|
|
||||||
import { findLegacySystemdUnits, uninstallLegacySystemdUnits } from "./systemd.js";
|
import { findLegacySystemdUnits, uninstallLegacySystemdUnits } from "./systemd.js";
|
||||||
|
|
||||||
export type LegacyGatewayService = {
|
export type LegacyGatewayService = {
|
||||||
@@ -34,19 +33,6 @@ function formatLegacySystemdUnits(
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatLegacyScheduledTasks(
|
|
||||||
tasks: Awaited<ReturnType<typeof findLegacyScheduledTasks>>,
|
|
||||||
): LegacyGatewayService[] {
|
|
||||||
return tasks.map((task) => ({
|
|
||||||
platform: "win32",
|
|
||||||
label: task.name,
|
|
||||||
detail: [
|
|
||||||
task.installed ? "installed" : "not installed",
|
|
||||||
task.scriptExists ? `script: ${task.scriptPath}` : "script missing",
|
|
||||||
].join(", "),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function findLegacyGatewayServices(
|
export async function findLegacyGatewayServices(
|
||||||
env: Record<string, string | undefined>,
|
env: Record<string, string | undefined>,
|
||||||
): Promise<LegacyGatewayService[]> {
|
): Promise<LegacyGatewayService[]> {
|
||||||
@@ -60,11 +46,6 @@ export async function findLegacyGatewayServices(
|
|||||||
return formatLegacySystemdUnits(units);
|
return formatLegacySystemdUnits(units);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.platform === "win32") {
|
|
||||||
const tasks = await findLegacyScheduledTasks(env);
|
|
||||||
return formatLegacyScheduledTasks(tasks);
|
|
||||||
}
|
|
||||||
|
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,10 +66,5 @@ export async function uninstallLegacyGatewayServices({
|
|||||||
return formatLegacySystemdUnits(units);
|
return formatLegacySystemdUnits(units);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.platform === "win32") {
|
|
||||||
const tasks = await uninstallLegacyScheduledTasks({ env, stdout });
|
|
||||||
return formatLegacyScheduledTasks(tasks);
|
|
||||||
}
|
|
||||||
|
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,7 @@ import path from "node:path";
|
|||||||
import { promisify } from "node:util";
|
import { promisify } from "node:util";
|
||||||
|
|
||||||
import { colorize, isRich, theme } from "../terminal/theme.js";
|
import { colorize, isRich, theme } from "../terminal/theme.js";
|
||||||
import {
|
import { formatGatewayServiceDescription, resolveGatewayWindowsTaskName } from "./constants.js";
|
||||||
formatGatewayServiceDescription,
|
|
||||||
LEGACY_GATEWAY_WINDOWS_TASK_NAMES,
|
|
||||||
resolveGatewayWindowsTaskName,
|
|
||||||
} from "./constants.js";
|
|
||||||
import { parseKeyValueOutput } from "./runtime-parse.js";
|
import { parseKeyValueOutput } from "./runtime-parse.js";
|
||||||
import type { GatewayServiceRuntime } from "./service-runtime.js";
|
import type { GatewayServiceRuntime } from "./service-runtime.js";
|
||||||
|
|
||||||
@@ -32,10 +28,6 @@ function resolveTaskScriptPath(env: Record<string, string | undefined>): string
|
|||||||
return path.join(home, `.clawdbot${suffix}`, "gateway.cmd");
|
return path.join(home, `.clawdbot${suffix}`, "gateway.cmd");
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveLegacyTaskScriptPath(env: Record<string, string | undefined>): string {
|
|
||||||
const home = resolveHomeDir(env);
|
|
||||||
return path.join(home, ".clawdis", "gateway.cmd");
|
|
||||||
}
|
|
||||||
|
|
||||||
function quoteCmdArg(value: string): string {
|
function quoteCmdArg(value: string): string {
|
||||||
if (!/[ \t"]/g.test(value)) return value;
|
if (!/[ \t"]/g.test(value)) return value;
|
||||||
@@ -351,77 +343,3 @@ export async function readScheduledTaskRuntime(
|
|||||||
lastRunResult: parsed.lastRunResult,
|
lastRunResult: parsed.lastRunResult,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
export type LegacyScheduledTask = {
|
|
||||||
name: string;
|
|
||||||
scriptPath: string;
|
|
||||||
installed: boolean;
|
|
||||||
scriptExists: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export async function findLegacyScheduledTasks(
|
|
||||||
env: Record<string, string | undefined>,
|
|
||||||
): Promise<LegacyScheduledTask[]> {
|
|
||||||
const results: LegacyScheduledTask[] = [];
|
|
||||||
let schtasksAvailable = true;
|
|
||||||
try {
|
|
||||||
await assertSchtasksAvailable();
|
|
||||||
} catch {
|
|
||||||
schtasksAvailable = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const name of LEGACY_GATEWAY_WINDOWS_TASK_NAMES) {
|
|
||||||
const scriptPath = resolveLegacyTaskScriptPath(env);
|
|
||||||
let installed = false;
|
|
||||||
if (schtasksAvailable) {
|
|
||||||
const res = await execSchtasks(["/Query", "/TN", name]);
|
|
||||||
installed = res.code === 0;
|
|
||||||
}
|
|
||||||
let scriptExists = false;
|
|
||||||
try {
|
|
||||||
await fs.access(scriptPath);
|
|
||||||
scriptExists = true;
|
|
||||||
} catch {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
if (installed || scriptExists) {
|
|
||||||
results.push({ name, scriptPath, installed, scriptExists });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function uninstallLegacyScheduledTasks({
|
|
||||||
env,
|
|
||||||
stdout,
|
|
||||||
}: {
|
|
||||||
env: Record<string, string | undefined>;
|
|
||||||
stdout: NodeJS.WritableStream;
|
|
||||||
}): Promise<LegacyScheduledTask[]> {
|
|
||||||
const tasks = await findLegacyScheduledTasks(env);
|
|
||||||
if (tasks.length === 0) return tasks;
|
|
||||||
|
|
||||||
let schtasksAvailable = true;
|
|
||||||
try {
|
|
||||||
await assertSchtasksAvailable();
|
|
||||||
} catch {
|
|
||||||
schtasksAvailable = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const task of tasks) {
|
|
||||||
if (schtasksAvailable && task.installed) {
|
|
||||||
await execSchtasks(["/Delete", "/F", "/TN", task.name]);
|
|
||||||
} else if (!schtasksAvailable && task.installed) {
|
|
||||||
stdout.write(`schtasks unavailable; unable to remove legacy task: ${task.name}\n`);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await fs.unlink(task.scriptPath);
|
|
||||||
stdout.write(`${formatLine("Removed legacy task script", task.scriptPath)}\n`);
|
|
||||||
} catch {
|
|
||||||
stdout.write(`Legacy task script not found at ${task.scriptPath}\n`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return tasks;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import type { PortListener, PortListenerKind, PortUsage } from "./ports-types.js
|
|||||||
|
|
||||||
export function classifyPortListener(listener: PortListener, port: number): PortListenerKind {
|
export function classifyPortListener(listener: PortListener, port: number): PortListenerKind {
|
||||||
const raw = `${listener.commandLine ?? ""} ${listener.command ?? ""}`.trim().toLowerCase();
|
const raw = `${listener.commandLine ?? ""} ${listener.command ?? ""}`.trim().toLowerCase();
|
||||||
if (raw.includes("clawdbot") || raw.includes("clawdis")) return "gateway";
|
if (raw.includes("clawdbot")) return "gateway";
|
||||||
if (raw.includes("ssh")) {
|
if (raw.includes("ssh")) {
|
||||||
const portToken = String(port);
|
const portToken = String(port);
|
||||||
const tunnelPattern = new RegExp(
|
const tunnelPattern = new RegExp(
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ type EnvSnapshot = {
|
|||||||
homeDrive: string | undefined;
|
homeDrive: string | undefined;
|
||||||
homePath: string | undefined;
|
homePath: string | undefined;
|
||||||
stateDir: string | undefined;
|
stateDir: string | undefined;
|
||||||
legacyStateDir: string | undefined;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function snapshotEnv(): EnvSnapshot {
|
function snapshotEnv(): EnvSnapshot {
|
||||||
@@ -20,7 +19,6 @@ function snapshotEnv(): EnvSnapshot {
|
|||||||
homeDrive: process.env.HOMEDRIVE,
|
homeDrive: process.env.HOMEDRIVE,
|
||||||
homePath: process.env.HOMEPATH,
|
homePath: process.env.HOMEPATH,
|
||||||
stateDir: process.env.CLAWDBOT_STATE_DIR,
|
stateDir: process.env.CLAWDBOT_STATE_DIR,
|
||||||
legacyStateDir: process.env.CLAWDIS_STATE_DIR,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,7 +32,6 @@ function restoreEnv(snapshot: EnvSnapshot) {
|
|||||||
restoreKey("HOMEDRIVE", snapshot.homeDrive);
|
restoreKey("HOMEDRIVE", snapshot.homeDrive);
|
||||||
restoreKey("HOMEPATH", snapshot.homePath);
|
restoreKey("HOMEPATH", snapshot.homePath);
|
||||||
restoreKey("CLAWDBOT_STATE_DIR", snapshot.stateDir);
|
restoreKey("CLAWDBOT_STATE_DIR", snapshot.stateDir);
|
||||||
restoreKey("CLAWDIS_STATE_DIR", snapshot.legacyStateDir);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function snapshotExtraEnv(keys: string[]): Record<string, string | undefined> {
|
function snapshotExtraEnv(keys: string[]): Record<string, string | undefined> {
|
||||||
@@ -54,7 +51,6 @@ function setTempHome(base: string) {
|
|||||||
process.env.HOME = base;
|
process.env.HOME = base;
|
||||||
process.env.USERPROFILE = base;
|
process.env.USERPROFILE = base;
|
||||||
process.env.CLAWDBOT_STATE_DIR = path.join(base, ".clawdbot");
|
process.env.CLAWDBOT_STATE_DIR = path.join(base, ".clawdbot");
|
||||||
process.env.CLAWDIS_STATE_DIR = path.join(base, ".clawdbot");
|
|
||||||
|
|
||||||
if (process.platform !== "win32") return;
|
if (process.platform !== "win32") return;
|
||||||
const match = base.match(/^([A-Za-z]:)(.*)$/);
|
const match = base.match(/^([A-Za-z]:)(.*)$/);
|
||||||
|
|||||||
Reference in New Issue
Block a user