feat: 全面清除用户/AI可见的 opencode 痕迹
Some checks failed
compliance-close / close-non-compliant (push) Has been cancelled
beta / sync (push) Has been cancelled

- system.ts 不再把真实 model id 喂给 LLM(修复"底层是opencode/big-pickle")
- TUI 首页 tips、窗口标题、ACP 弹窗、命令提示文案全部 → zmo
- config schema URL → zmo.dev;配置路径提示 → ~/.config/zmo、.zmo/
- 发给各家 LLM API 的标识头(X-Title/Referer/Source/UA/originator)→ zmo
- 保留:opencode provider 路由id、@opencode-ai 包名、opencode.json/OPENCODE_* 兼容

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-05 17:49:47 +08:00
parent 9970e6c1ad
commit 5bd92a3535
21 changed files with 81 additions and 82 deletions

View File

@@ -93,17 +93,17 @@ export function make(input: {
const initialize = Effect.fn("ACP.initialize")(function* (params: InitializeRequest) {
const started = performance.now()
const authMethod: AuthMethod = {
description: "Run `opencode auth login` in the terminal",
name: "Login with opencode",
description: "Run `zmo auth login` in the terminal",
name: "Login with zmo",
id: AuthMethodID,
}
if (params.clientCapabilities?._meta?.["terminal-auth"] === true) {
authMethod._meta = {
"terminal-auth": {
command: "opencode",
command: "zmo",
args: ["auth", "login"],
label: "OpenCode Login",
label: "zmo Login",
},
}
}
@@ -129,7 +129,7 @@ export function make(input: {
},
authMethods: [authMethod],
agentInfo: {
name: "OpenCode",
name: "zmo",
version: InstallationVersion,
},
}

View File

@@ -301,7 +301,7 @@ export const ProvidersLoginCommand = effectCmd({
builder: (yargs) =>
yargs
.positional("url", {
describe: "opencode auth provider",
describe: "zmo auth provider",
type: "string",
})
.option("provider", {

View File

@@ -263,7 +263,7 @@ function build(input: SplashWriterInput, kind: "entry" | "exit", ctx: Scrollback
lines,
body_left + label.length,
top + 1,
`opencode run -i -s ${meta.session_id}`,
`zmo run -i -s ${meta.session_id}`,
right,
undefined,
TextAttributes.BOLD,

View File

@@ -40,7 +40,7 @@ type TuiAttentionHost = TuiAttention & {
const log = Log.create({ service: "tui.attention" })
const DEFAULT_TITLE = "opencode"
const DEFAULT_TITLE = "zmo"
const DEFAULT_PACK_ID = "opencode.default"
const KV_SOUND_PACK = "attention_sound_pack"
const TITLE_LIMIT = 80

View File

@@ -11,7 +11,7 @@ import * as ConfigPaths from "@/config/paths"
const log = Log.create({ service: "tui.migrate" })
const TUI_SCHEMA_URL = "https://opencode.ai/tui.json"
const TUI_SCHEMA_URL = "https://zmo.dev/tui.json"
const decodeTheme = Schema.decodeUnknownOption(Schema.String)
const decodeRecord = Schema.decodeUnknownOption(Schema.Record(Schema.String, Schema.Unknown))

View File

@@ -167,7 +167,7 @@ const TIPS: Tip[] = [
(shortcuts) => press(shortcuts.agentCycle(), "to cycle between Build and Plan agents"),
"Use {highlight}/undo{/highlight} to revert the last message and file changes",
"Use {highlight}/redo{/highlight} to restore previously undone messages and file changes",
"Run {highlight}/share{/highlight} to create a public link to your conversation at opencode.ai",
"Run {highlight}/share{/highlight} to create a public link to your conversation",
"Drag and drop images or PDFs into the terminal to add them as context",
(shortcuts) => press(shortcuts.inputPaste(), "to paste images from your clipboard into the prompt"),
(shortcuts) => `Use ${commandText("/editor", shortcuts.editorOpen())} to compose messages in your external editor`,
@@ -210,17 +210,17 @@ const TIPS: Tip[] = [
if (!items.length) return undefined
return `Use ${items.map(shortcutText).join(" / ")} to move between parent and child sessions`
},
"Create {highlight}opencode.json{/highlight} for server settings and {highlight}tui.json{/highlight} for TUI settings",
"Place TUI settings in {highlight}~/.config/opencode/tui.json{/highlight} for global config",
"Create {highlight}zmo.json{/highlight} for server settings and {highlight}tui.json{/highlight} for TUI settings",
"Place TUI settings in {highlight}~/.config/zmo/tui.json{/highlight} for global config",
"Add {highlight}$schema{/highlight} to your config for autocomplete in your editor",
"Configure {highlight}model{/highlight} in config to set your default model",
"Override any keybind in {highlight}tui.json{/highlight} via the {highlight}keybinds{/highlight} section",
"Set any keybind to {highlight}none{/highlight} to disable it completely",
"Configure local or remote MCP servers in the {highlight}mcp{/highlight} config section",
"Add {highlight}.md{/highlight} files to {highlight}.opencode/commands/{/highlight} to define reusable custom prompts",
"Add {highlight}.md{/highlight} files to {highlight}.zmo/commands/{/highlight} to define reusable custom prompts",
"Use {highlight}$ARGUMENTS{/highlight}, {highlight}$1{/highlight}, {highlight}$2{/highlight} in custom commands for dynamic input",
"Use backticks in commands to inject shell output (e.g., {highlight}`git status`{/highlight})",
"Add {highlight}.md{/highlight} files to {highlight}.opencode/agents/{/highlight} for specialized AI personas",
"Add {highlight}.md{/highlight} files to {highlight}.zmo/agents/{/highlight} for specialized AI personas",
"Configure per-agent permissions for {highlight}edit{/highlight}, {highlight}bash{/highlight}, and {highlight}webfetch{/highlight} tools",
'Use patterns like {highlight}"git *": "allow"{/highlight} for granular bash permissions',
'Set {highlight}"rm -rf *": "deny"{/highlight} to block destructive commands',
@@ -229,26 +229,25 @@ const TIPS: Tip[] = [
'Set {highlight}"formatter": false{/highlight} in config to disable formatters enabled by another config layer',
"Define custom formatter commands with file extensions in config",
'Set {highlight}"lsp": true{/highlight} in config to enable built-in LSP servers for code analysis',
"Create {highlight}.ts{/highlight} files in {highlight}.opencode/tools/{/highlight} to define new LLM tools",
"Create {highlight}.ts{/highlight} files in {highlight}.zmo/tools/{/highlight} to define new LLM tools",
"Tool definitions can invoke scripts written in Python, Go, etc",
"Add {highlight}.ts{/highlight} files to {highlight}.opencode/plugins/{/highlight} for event hooks",
"Add {highlight}.ts{/highlight} files to {highlight}.zmo/plugins/{/highlight} for event hooks",
"Use plugins to send OS notifications when sessions complete",
"Create a plugin to prevent OpenCode from reading sensitive files",
"Use {highlight}opencode run{/highlight} for non-interactive scripting",
"Use {highlight}opencode --continue{/highlight} to resume the last session",
"Use {highlight}opencode run -f file.ts{/highlight} to attach files via CLI",
"Create a plugin to prevent zmo from reading sensitive files",
"Use {highlight}zmo run{/highlight} for non-interactive scripting",
"Use {highlight}zmo --continue{/highlight} to resume the last session",
"Use {highlight}zmo run -f file.ts{/highlight} to attach files via CLI",
"Use {highlight}--format json{/highlight} for machine-readable output in scripts",
"Run {highlight}opencode serve{/highlight} for headless API access to OpenCode",
"Use {highlight}opencode run --attach{/highlight} to connect to a running server",
"Run {highlight}opencode upgrade{/highlight} to update to the latest version",
"Run {highlight}opencode auth list{/highlight} to see all configured providers",
"Run {highlight}opencode agent create{/highlight} for guided agent creation",
"Use {highlight}/opencode{/highlight} in GitHub issues/PRs to trigger AI actions",
"Run {highlight}opencode github install{/highlight} to set up the GitHub workflow",
"Comment {highlight}/opencode fix this{/highlight} on issues to auto-create PRs",
"Run {highlight}zmo serve{/highlight} for headless API access to zmo",
"Use {highlight}zmo run --attach{/highlight} to connect to a running server",
"Run {highlight}zmo upgrade{/highlight} to update to the latest version",
"Run {highlight}zmo auth list{/highlight} to see all configured providers",
"Run {highlight}zmo agent create{/highlight} for guided agent creation",
"Use {highlight}--format json{/highlight} to integrate zmo into your scripts",
"Use {highlight}zmo run --continue{/highlight} to continue the last session non-interactively",
"Comment {highlight}/oc{/highlight} on PR code lines for targeted code reviews",
'Use {highlight}"theme": "system"{/highlight} to match your terminal\'s colors',
"Create JSON theme files in {highlight}.opencode/themes/{/highlight} directory",
"Create JSON theme files in {highlight}.zmo/themes/{/highlight} directory",
"Themes support dark/light variants for both modes",
"Use numeric xterm color codes 0-255 in custom theme JSON",
"Use {highlight}{env:VAR_NAME}{/highlight} syntax to reference environment variables in config",
@@ -264,7 +263,7 @@ const TIPS: Tip[] = [
"Run {highlight}/unshare{/highlight} to remove a session from public access",
"Permission {highlight}doom_loop{/highlight} prevents infinite tool call loops",
"Permission {highlight}external_directory{/highlight} protects files outside project",
"Run {highlight}opencode debug config{/highlight} to troubleshoot configuration",
"Run {highlight}zmo debug config{/highlight} to troubleshoot configuration",
"Use {highlight}--print-logs{/highlight} flag to see detailed logs in stderr",
(shortcuts) => `Use ${commandText("/timeline", shortcuts.sessionTimeline())} to jump to specific messages`,
(shortcuts) => press(shortcuts.messagesToggleConceal(), "to toggle code block visibility in messages"),
@@ -274,8 +273,8 @@ const TIPS: Tip[] = [
shortcuts.commandList()
? `Toggle username display in chat via the command palette (${shortcutText(shortcuts.commandList())})`
: "Toggle username display in chat via the command palette",
"Run {highlight}docker run -it --rm ghcr.io/anomalyco/opencode{/highlight} for containerized use",
"Use {highlight}/connect{/highlight} with OpenCode Zen for curated, tested models",
"Switch between models mid-session to use the best tool for each task",
"Configure any OpenAI-compatible provider via {highlight}baseURL{/highlight} in your config",
"Commit your project's {highlight}AGENTS.md{/highlight} file to Git for team sharing",
"Use {highlight}/review{/highlight} to review uncommitted changes, branches, or PRs",
(shortcuts) => `Use ${commandText("/help", shortcuts.helpShow())} to show the help dialog`,

View File

@@ -28,7 +28,7 @@ export const UpgradeCommand = {
const detectedMethod = await Installation.method()
const method = (args.method as Installation.Method) ?? detectedMethod
if (method === "unknown") {
prompts.log.error(`opencode is installed to ${process.execPath} and may be managed by a package manager`)
prompts.log.error(`zmo is installed to ${process.execPath} and may be managed by a package manager`)
const install = await prompts.select({
message: "Install anyways?",
options: [

View File

@@ -221,8 +221,8 @@ export const layer = Layer.effect(
yield* Effect.promise(() => resolveLoadedPlugins(data, options.path))
if (!data.$schema) {
data.$schema = "https://opencode.ai/config.json"
const updated = text.replace(/^\s*\{/, '{\n "$schema": "https://opencode.ai/config.json",')
data.$schema = "https://zmo.dev/config.json"
const updated = text.replace(/^\s*\{/, '{\n "$schema": "https://zmo.dev/config.json",')
yield* fs.writeFileString(options.path, updated).pipe(Effect.catch(() => Effect.void))
}
return data
@@ -243,7 +243,7 @@ export const layer = Layer.effect(
const file = globalConfigFile()
if (!existsSync(file)) {
yield* fs
.writeWithDirs(file, JSON.stringify({ $schema: "https://opencode.ai/config.json" }, null, 2))
.writeWithDirs(file, JSON.stringify({ $schema: "https://zmo.dev/config.json" }, null, 2))
.pipe(Effect.catch(() => Effect.void))
}
}
@@ -258,7 +258,7 @@ export const layer = Layer.effect(
.then(async (mod) => {
const { provider, model, ...rest } = mod.default
if (provider && model) result.model = `${provider}/${model}`
result["$schema"] = "https://opencode.ai/config.json"
result["$schema"] = "https://zmo.dev/config.json"
result = mergeConfig(result, rest)
await fsNode.writeFile(path.join(Global.Path.config, "config.json"), JSON.stringify(result, null, 2))
await fsNode.unlink(legacy)
@@ -371,7 +371,7 @@ export const layer = Layer.effect(
})
: {}
const remoteConfig = mergeConfig(isRecord(wellknown.config) ? wellknown.config : {}, fetchedConfig)
if (!remoteConfig.$schema) remoteConfig.$schema = "https://opencode.ai/config.json"
if (!remoteConfig.$schema) remoteConfig.$schema = "https://zmo.dev/config.json"
const source = wellknownURL
const next = yield* loadConfig(
JSON.stringify(remoteConfig),

View File

@@ -87,7 +87,7 @@ function buildAuthorizeUrl(redirectUri: string, pkce: PkceCodes, state: string):
id_token_add_organizations: "true",
codex_cli_simplified_flow: "true",
state,
originator: "opencode",
originator: "zmo",
})
return `${ISSUER}/oauth/authorize?${params.toString()}`
}
@@ -547,7 +547,7 @@ export async function CodexAuthPlugin(input: PluginInput, options: CodexAuthPlug
method: "POST",
headers: {
"Content-Type": "application/json",
"User-Agent": `opencode/${InstallationVersion}`,
"User-Agent": `zmo/${InstallationVersion}`,
},
body: JSON.stringify({ client_id: CLIENT_ID }),
})
@@ -571,7 +571,7 @@ export async function CodexAuthPlugin(input: PluginInput, options: CodexAuthPlug
method: "POST",
headers: {
"Content-Type": "application/json",
"User-Agent": `opencode/${InstallationVersion}`,
"User-Agent": `zmo/${InstallationVersion}`,
},
body: JSON.stringify({
device_auth_id: deviceData.device_auth_id,
@@ -630,8 +630,8 @@ export async function CodexAuthPlugin(input: PluginInput, options: CodexAuthPlug
},
"chat.headers": async (input, output) => {
if (input.model.providerID !== "openai") return
output.headers.originator = "opencode"
output.headers["User-Agent"] = `opencode/${InstallationVersion} (${os.platform()} ${os.release()}; ${os.arch()})`
output.headers.originator = "zmo"
output.headers["User-Agent"] = `zmo/${InstallationVersion} (${os.platform()} ${os.release()}; ${os.arch()})`
output.headers["session-id"] = input.sessionID
// Temporary fetch-layer hack: title generation currently shares the conversation
// session ID, so the OpenAI plugin marks it for HTTP fallback until transport

View File

@@ -109,7 +109,7 @@ function authHeaders() {
return {
"Content-Type": "application/x-www-form-urlencoded",
Accept: "application/json",
"User-Agent": `opencode/${InstallationVersion}`,
"User-Agent": `zmo/${InstallationVersion}`,
}
}
@@ -157,7 +157,7 @@ export function buildAuthorizeUrl(
state,
nonce,
plan: "generic",
referrer: "opencode",
referrer: "zmo",
})
return `${options.authorizeUrl ?? AUTHORIZE_URL}?${params.toString()}`
}
@@ -655,7 +655,7 @@ export async function XaiAuthPlugin(input: PluginInput, options: XaiAuthPluginOp
}
}
headers.set("authorization", `Bearer ${currentAuth.access}`)
headers.set("User-Agent", `opencode/${InstallationVersion}`)
headers.set("User-Agent", `zmo/${InstallationVersion}`)
return fetch(requestInput, { ...init, headers })
},

View File

@@ -435,9 +435,9 @@ function custom(dep: CustomDep): Record<string, CustomLoader> {
autoload: false,
options: {
headers: {
"HTTP-Referer": "https://opencode.ai/",
"X-Title": "opencode",
"X-Source": "opencode",
"HTTP-Referer": "https://github.com/helpLogin/zmo-cli",
"X-Title": "zmo",
"X-Source": "zmo",
},
},
}),
@@ -446,8 +446,8 @@ function custom(dep: CustomDep): Record<string, CustomLoader> {
autoload: false,
options: {
headers: {
"HTTP-Referer": "https://opencode.ai/",
"X-Title": "opencode",
"HTTP-Referer": "https://github.com/helpLogin/zmo-cli",
"X-Title": "zmo",
},
},
}),
@@ -456,9 +456,9 @@ function custom(dep: CustomDep): Record<string, CustomLoader> {
autoload: provider.source === "config",
options: {
headers: {
"HTTP-Referer": "https://opencode.ai/",
"X-Title": "opencode",
"X-BILLING-INVOKE-ORIGIN": "OpenCode",
"HTTP-Referer": "https://github.com/helpLogin/zmo-cli",
"X-Title": "zmo",
"X-BILLING-INVOKE-ORIGIN": "zmo",
},
},
}),
@@ -467,8 +467,8 @@ function custom(dep: CustomDep): Record<string, CustomLoader> {
autoload: false,
options: {
headers: {
"http-referer": "https://opencode.ai/",
"x-title": "opencode",
"http-referer": "https://github.com/helpLogin/zmo-cli",
"x-title": "zmo",
},
},
}),
@@ -573,8 +573,8 @@ function custom(dep: CustomDep): Record<string, CustomLoader> {
autoload: false,
options: {
headers: {
"HTTP-Referer": "https://opencode.ai/",
"X-Title": "opencode",
"HTTP-Referer": "https://github.com/helpLogin/zmo-cli",
"X-Title": "zmo",
},
},
}),
@@ -595,7 +595,7 @@ function custom(dep: CustomDep): Record<string, CustomLoader> {
const directory = yield* InstanceState.directory
const aiGatewayHeaders = {
"User-Agent": `opencode/${InstallationVersion} gitlab-ai-provider/${GITLAB_PROVIDER_VERSION} (${os.platform()} ${os.release()}; ${os.arch()})`,
"User-Agent": `zmo/${InstallationVersion} gitlab-ai-provider/${GITLAB_PROVIDER_VERSION} (${os.platform()} ${os.release()}; ${os.arch()})`,
"anthropic-beta": "context-1m-2025-08-07",
...providerConfig?.options?.aiGatewayHeaders,
}
@@ -743,7 +743,7 @@ function custom(dep: CustomDep): Record<string, CustomLoader> {
options: {
apiKey,
headers: {
"User-Agent": `opencode/${InstallationVersion} cloudflare-workers-ai (${os.platform()} ${os.release()}; ${os.arch()})`,
"User-Agent": `zmo/${InstallationVersion} cloudflare-workers-ai (${os.platform()} ${os.release()}; ${os.arch()})`,
},
},
async getModel(sdk: any, modelID: string) {
@@ -788,7 +788,7 @@ function custom(dep: CustomDep): Record<string, CustomLoader> {
if (!apiToken) {
throw new Error(
"CLOUDFLARE_API_TOKEN (or CF_AIG_TOKEN) is required for Cloudflare AI Gateway. " +
"Set it via environment variable or run `opencode auth cloudflare-ai-gateway`.",
"Set it via environment variable or run `zmo auth cloudflare-ai-gateway`.",
)
}
@@ -811,7 +811,7 @@ function custom(dep: CustomDep): Record<string, CustomLoader> {
skipCache: input.options?.skipCache,
collectLog: input.options?.collectLog,
headers: {
"User-Agent": `opencode/${InstallationVersion} cloudflare-ai-gateway (${os.platform()} ${os.release()}; ${os.arch()})`,
"User-Agent": `zmo/${InstallationVersion} cloudflare-ai-gateway (${os.platform()} ${os.release()}; ${os.arch()})`,
},
}
@@ -837,7 +837,7 @@ function custom(dep: CustomDep): Record<string, CustomLoader> {
autoload: false,
options: {
headers: {
"X-Cerebras-3rd-Party-Integration": "opencode",
"X-Cerebras-3rd-Party-Integration": "zmo",
},
},
}),
@@ -846,8 +846,8 @@ function custom(dep: CustomDep): Record<string, CustomLoader> {
autoload: false,
options: {
headers: {
"HTTP-Referer": "https://opencode.ai/",
"X-Title": "opencode",
"HTTP-Referer": "https://github.com/helpLogin/zmo-cli",
"X-Title": "zmo",
},
},
}),

View File

@@ -49,7 +49,7 @@ export const layer = Layer.effect(
const ctx = yield* InstanceState.context
return [
[
`You are powered by the model named ${model.api.id}. The exact model ID is ${model.providerID}/${model.api.id}`,
`You are zmo, an AI coding assistant running in the terminal.`,
`Here is some useful information about the environment you are running in:`,
`<env>`,
` Working directory: ${ctx.directory}`,