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

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 apps/cli/ai/auth.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
AI_PROVIDER_PRIORITY,
AI_PROVIDERS,
DEFAULT_AI_PROVIDER,
getAiProviderDefinition,
hasInlineWpcomAuth,
Expand Down Expand Up @@ -55,7 +56,14 @@ export async function resolveInitialAiProvider(): Promise< AiProviderId > {
return 'wpcom';
}

const { aiProvider: savedProvider } = await readCliConfig();
// The config schema accepts provider ids from other Studio builds (e.g.
// `claude-code` from the ACP experiment) so a shared cli.json never breaks
// parsing; ids this build doesn't implement are ignored here.
const { aiProvider: savedConfigProvider } = await readCliConfig();
const savedProvider =
savedConfigProvider && savedConfigProvider in AI_PROVIDERS
? ( savedConfigProvider as AiProviderId )
: undefined;
if ( savedProvider ) {
const definition = getAiProviderDefinition( savedProvider );
if (
Expand Down
2 changes: 1 addition & 1 deletion apps/cli/lib/cli-config/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ const CLI_CONFIG_VERSION = 1;

// IMPORTANT: Always consider that independently installed versions of the CLI (from npm) may also
// read this file, and any updates to this schema may require updating the `version` field.
export const aiProviderSchema = z.enum( [ 'wpcom', 'anthropic-api-key' ] );
export const aiProviderSchema = z.enum( [ 'wpcom', 'anthropic-api-key', 'claude-code' ] );

export const updateCheckSchema = z.object( {
lastChecked: z.number(),
Expand Down
3 changes: 3 additions & 0 deletions apps/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,13 @@
"http-proxy": "^1.18.1",
"ignore": "^7.0.5",
"node-forge": "^1.4.0",
"node-pty": "^1.1.0",
"picospinner": "^3.0.0",
"playwright": "^1.52.0",
"semver": "^7.7.4",
"tar": "^7.5.21",
"trash": "^10.0.1",
"ws": "^8.21.1",
"yargs": "^18.0.0",
"yargs-parser": "^22.0.0",
"yauzl": "^3.3.0",
Expand All @@ -81,6 +83,7 @@
"@types/express": "^4.17.23",
"@types/http-proxy": "^1.17.17",
"@types/node-forge": "^1.3.14",
"@types/ws": "^8.18.1",
"@types/yargs": "^17.0.35",
"vite": "^8.0.16",
"vite-plugin-static-copy": "^4.1.0"
Expand Down
18 changes: 18 additions & 0 deletions apps/local/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ import { pullSite, pushSite } from '@studio/common/sites/sync';
import express from 'express';
import { rateLimit } from 'express-rate-limit';
import { isEditor, isTerminal, openInEditor, openInTerminal, openPath } from './open-in-os';
import { createTerminalManager } from './terminal';
import type { SiteListItem } from '@studio/common/lib/cli-events';
import type { EditSiteOptions } from '@studio/common/sites/edit';
import type { SyncSite } from '@studio/common/types/sync';
Expand Down Expand Up @@ -190,6 +191,7 @@ export async function startLocalServer( options: LocalServerOptions ): Promise<
const cliRunner = createCliRunner( { cliBinary, nodeBinary } );
const execute = cliRunner.executeCliCommand;
const uploads = new Map< string, { path: string; directory: string } >();
const resolvedNodeBinary = nodeBinary ?? process.execPath;

// --- Server-Sent Events: one stream carries live UI updates ----------------
const sseClients = new Set< Response >();
Expand Down Expand Up @@ -330,6 +332,19 @@ export async function startLocalServer( options: LocalServerOptions ): Promise<

app.use( express.json() );

// --- Embedded Claude Code terminal ---------------------------------------
const terminalManager = createTerminalManager( {
cliBinary,
nodeBinary: resolvedNodeBinary,
sseSend,
resolveSite: async ( siteId: string ) => {
const site = ( await listSites( execute ) ).find( ( s ) => s.id === siteId );
return site ? { id: site.id, name: site.name, path: site.path } : undefined;
},
allowedOrigins,
} );
terminalManager.registerRoutes( api );

api.get( '/events', ( req: Request, res: Response ) => {
res.setHeader( 'Content-Type', 'text/event-stream' );
res.setHeader( 'Cache-Control', 'no-cache' );
Expand Down Expand Up @@ -1288,6 +1303,8 @@ export async function startLocalServer( options: LocalServerOptions ): Promise<
const listening = app.listen( port, host, () => resolve( listening ) );
} );

terminalManager.handleUpgrade( server );

const address = server.address();
const listeningPort = typeof address === 'object' && address ? address.port : port;
const url = `http://localhost:${ listeningPort }`;
Expand All @@ -1296,6 +1313,7 @@ export async function startLocalServer( options: LocalServerOptions ): Promise<
url,
port: listeningPort,
async close() {
await terminalManager.closeAll();
cliRunner.killAll();
for ( const watch of publishWatches.values() ) {
watch.cancelled = true;
Expand Down
Loading
Loading