@xortex/xcode 3.1.0 → 3.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/xcode +1 -1
- package/entrypoints/cli.tsx +1 -1
- package/macro.ts +1 -1
- package/package.json +54 -1
- package/INSTALLATION.md +0 -285
- package/QUICKSTART.md +0 -151
- package/SYSTEM_PROMPT.md +0 -583
- package/SYSTEM_PROMPT_EXTRACTED.md +0 -1
- package/Untitled +0 -1
- package/bun.lock +0 -645
- package/costHook.ts +0 -22
- package/dialogLaunchers.tsx +0 -133
- package/extract_prompt.ts +0 -304
- package/inspect.js +0 -7
- package/install.sh +0 -221
- package/moreright/useMoreRight.tsx +0 -26
- package/native-ts/color-diff/index.ts +0 -999
- package/native-ts/file-index/index.ts +0 -370
- package/native-ts/yoga-layout/enums.ts +0 -134
- package/native-ts/yoga-layout/index.ts +0 -2578
- package/outputStyles/loadOutputStylesDir.ts +0 -98
- package/patch-box.js +0 -54
- package/patch-compact.js +0 -13
- package/patch-condensed-center.js +0 -13
- package/patch-condensed-row.js +0 -13
- package/patch-condensed.js +0 -13
- package/patch-final.js +0 -58
- package/patch-input-body.js +0 -46
- package/patch-input-body2.js +0 -35
- package/patch-input-style.js +0 -13
- package/patch-input-width.js +0 -13
- package/patch-layout.js +0 -87
- package/patch-logo-row.js +0 -12
- package/patch-width.js +0 -13
- package/patch-width2.js +0 -13
- package/patch2.js +0 -74
- package/patch3.js +0 -13
- package/plugins/builtinPlugins.ts +0 -159
- package/plugins/bundled/index.ts +0 -23
- package/projectOnboardingState.ts +0 -83
- package/public/claude-files.png +0 -0
- package/public/leak-tweet.png +0 -0
- package/replLauncher.tsx +0 -27
- package/server/createDirectConnectSession.ts +0 -88
- package/server/directConnectManager.ts +0 -213
- package/server/types.ts +0 -57
- package/stub_types.sh +0 -13
- package/vim/motions.ts +0 -82
- package/vim/operators.ts +0 -556
- package/vim/textObjects.ts +0 -186
- package/vim/transitions.ts +0 -490
- package/vim/types.ts +0 -199
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Built-in Plugin Registry
|
|
3
|
-
*
|
|
4
|
-
* Manages built-in plugins that ship with the CLI and can be enabled/disabled
|
|
5
|
-
* by users via the /plugin UI.
|
|
6
|
-
*
|
|
7
|
-
* Built-in plugins differ from bundled skills (src/skills/bundled/) in that:
|
|
8
|
-
* - They appear in the /plugin UI under a "Built-in" section
|
|
9
|
-
* - Users can enable/disable them (persisted to user settings)
|
|
10
|
-
* - They can provide multiple components (skills, hooks, MCP servers)
|
|
11
|
-
*
|
|
12
|
-
* Plugin IDs use the format `{name}@builtin` to distinguish them from
|
|
13
|
-
* marketplace plugins (`{name}@{marketplace}`).
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import type { Command } from '../commands.js'
|
|
17
|
-
import type { BundledSkillDefinition } from '../skills/bundledSkills.js'
|
|
18
|
-
import type { BuiltinPluginDefinition, LoadedPlugin } from '../types/plugin.js'
|
|
19
|
-
import { getSettings_DEPRECATED } from '../utils/settings/settings.js'
|
|
20
|
-
|
|
21
|
-
const BUILTIN_PLUGINS: Map<string, BuiltinPluginDefinition> = new Map()
|
|
22
|
-
|
|
23
|
-
export const BUILTIN_MARKETPLACE_NAME = 'builtin'
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Register a built-in plugin. Call this from initBuiltinPlugins() at startup.
|
|
27
|
-
*/
|
|
28
|
-
export function registerBuiltinPlugin(
|
|
29
|
-
definition: BuiltinPluginDefinition,
|
|
30
|
-
): void {
|
|
31
|
-
BUILTIN_PLUGINS.set(definition.name, definition)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Check if a plugin ID represents a built-in plugin (ends with @builtin).
|
|
36
|
-
*/
|
|
37
|
-
export function isBuiltinPluginId(pluginId: string): boolean {
|
|
38
|
-
return pluginId.endsWith(`@${BUILTIN_MARKETPLACE_NAME}`)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Get a specific built-in plugin definition by name.
|
|
43
|
-
* Useful for the /plugin UI to show the skills/hooks/MCP list without
|
|
44
|
-
* a marketplace lookup.
|
|
45
|
-
*/
|
|
46
|
-
export function getBuiltinPluginDefinition(
|
|
47
|
-
name: string,
|
|
48
|
-
): BuiltinPluginDefinition | undefined {
|
|
49
|
-
return BUILTIN_PLUGINS.get(name)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Get all registered built-in plugins as LoadedPlugin objects, split into
|
|
54
|
-
* enabled/disabled based on user settings (with defaultEnabled as fallback).
|
|
55
|
-
* Plugins whose isAvailable() returns false are omitted entirely.
|
|
56
|
-
*/
|
|
57
|
-
export function getBuiltinPlugins(): {
|
|
58
|
-
enabled: LoadedPlugin[]
|
|
59
|
-
disabled: LoadedPlugin[]
|
|
60
|
-
} {
|
|
61
|
-
const settings = getSettings_DEPRECATED()
|
|
62
|
-
const enabled: LoadedPlugin[] = []
|
|
63
|
-
const disabled: LoadedPlugin[] = []
|
|
64
|
-
|
|
65
|
-
for (const [name, definition] of BUILTIN_PLUGINS) {
|
|
66
|
-
if (definition.isAvailable && !definition.isAvailable()) {
|
|
67
|
-
continue
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const pluginId = `${name}@${BUILTIN_MARKETPLACE_NAME}`
|
|
71
|
-
const userSetting = settings?.enabledPlugins?.[pluginId]
|
|
72
|
-
// Enabled state: user preference > plugin default > true
|
|
73
|
-
const isEnabled =
|
|
74
|
-
userSetting !== undefined
|
|
75
|
-
? userSetting === true
|
|
76
|
-
: (definition.defaultEnabled ?? true)
|
|
77
|
-
|
|
78
|
-
const plugin: LoadedPlugin = {
|
|
79
|
-
name,
|
|
80
|
-
manifest: {
|
|
81
|
-
name,
|
|
82
|
-
description: definition.description,
|
|
83
|
-
version: definition.version,
|
|
84
|
-
},
|
|
85
|
-
path: BUILTIN_MARKETPLACE_NAME, // sentinel — no filesystem path
|
|
86
|
-
source: pluginId,
|
|
87
|
-
repository: pluginId,
|
|
88
|
-
enabled: isEnabled,
|
|
89
|
-
isBuiltin: true,
|
|
90
|
-
hooksConfig: definition.hooks,
|
|
91
|
-
mcpServers: definition.mcpServers,
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if (isEnabled) {
|
|
95
|
-
enabled.push(plugin)
|
|
96
|
-
} else {
|
|
97
|
-
disabled.push(plugin)
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
return { enabled, disabled }
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Get skills from enabled built-in plugins as Command objects.
|
|
106
|
-
* Skills from disabled plugins are not returned.
|
|
107
|
-
*/
|
|
108
|
-
export function getBuiltinPluginSkillCommands(): Command[] {
|
|
109
|
-
const { enabled } = getBuiltinPlugins()
|
|
110
|
-
const commands: Command[] = []
|
|
111
|
-
|
|
112
|
-
for (const plugin of enabled) {
|
|
113
|
-
const definition = BUILTIN_PLUGINS.get(plugin.name)
|
|
114
|
-
if (!definition?.skills) continue
|
|
115
|
-
for (const skill of definition.skills) {
|
|
116
|
-
commands.push(skillDefinitionToCommand(skill))
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
return commands
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Clear built-in plugins registry (for testing).
|
|
125
|
-
*/
|
|
126
|
-
export function clearBuiltinPlugins(): void {
|
|
127
|
-
BUILTIN_PLUGINS.clear()
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// --
|
|
131
|
-
|
|
132
|
-
function skillDefinitionToCommand(definition: BundledSkillDefinition): Command {
|
|
133
|
-
return {
|
|
134
|
-
type: 'prompt',
|
|
135
|
-
name: definition.name,
|
|
136
|
-
description: definition.description,
|
|
137
|
-
hasUserSpecifiedDescription: true,
|
|
138
|
-
allowedTools: definition.allowedTools ?? [],
|
|
139
|
-
argumentHint: definition.argumentHint,
|
|
140
|
-
whenToUse: definition.whenToUse,
|
|
141
|
-
model: definition.model,
|
|
142
|
-
disableModelInvocation: definition.disableModelInvocation ?? false,
|
|
143
|
-
userInvocable: definition.userInvocable ?? true,
|
|
144
|
-
contentLength: 0,
|
|
145
|
-
// 'bundled' not 'builtin' — 'builtin' in Command.source means hardcoded
|
|
146
|
-
// slash commands (/help, /clear). Using 'bundled' keeps these skills in
|
|
147
|
-
// the Skill tool's listing, analytics name logging, and prompt-truncation
|
|
148
|
-
// exemption. The user-toggleable aspect is tracked on LoadedPlugin.isBuiltin.
|
|
149
|
-
source: 'bundled',
|
|
150
|
-
loadedFrom: 'bundled',
|
|
151
|
-
hooks: definition.hooks,
|
|
152
|
-
context: definition.context,
|
|
153
|
-
agent: definition.agent,
|
|
154
|
-
isEnabled: definition.isEnabled ?? (() => true),
|
|
155
|
-
isHidden: !(definition.userInvocable ?? true),
|
|
156
|
-
progressMessage: 'running',
|
|
157
|
-
getPromptForCommand: definition.getPromptForCommand,
|
|
158
|
-
}
|
|
159
|
-
}
|
package/plugins/bundled/index.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Built-in Plugin Initialization
|
|
3
|
-
*
|
|
4
|
-
* Initializes built-in plugins that ship with the CLI and appear in the
|
|
5
|
-
* /plugin UI for users to enable/disable.
|
|
6
|
-
*
|
|
7
|
-
* Not all bundled features should be built-in plugins — use this for
|
|
8
|
-
* features that users should be able to explicitly enable/disable. For
|
|
9
|
-
* features with complex setup or automatic-enabling logic (e.g.
|
|
10
|
-
* claude-in-chrome), use src/skills/bundled/ instead.
|
|
11
|
-
*
|
|
12
|
-
* To add a new built-in plugin:
|
|
13
|
-
* 1. Import registerBuiltinPlugin from '../builtinPlugins.js'
|
|
14
|
-
* 2. Call registerBuiltinPlugin() with the plugin definition here
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Initialize built-in plugins. Called during CLI startup.
|
|
19
|
-
*/
|
|
20
|
-
export function initBuiltinPlugins(): void {
|
|
21
|
-
// No built-in plugins registered yet — this is the scaffolding for
|
|
22
|
-
// migrating bundled skills that should be user-toggleable.
|
|
23
|
-
}
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import memoize from 'lodash-es/memoize.js'
|
|
2
|
-
import { join } from 'path'
|
|
3
|
-
import {
|
|
4
|
-
getCurrentProjectConfig,
|
|
5
|
-
saveCurrentProjectConfig,
|
|
6
|
-
} from './utils/config.js'
|
|
7
|
-
import { getCwd } from './utils/cwd.js'
|
|
8
|
-
import { isDirEmpty } from './utils/file.js'
|
|
9
|
-
import { getFsImplementation } from './utils/fsOperations.js'
|
|
10
|
-
|
|
11
|
-
export type Step = {
|
|
12
|
-
key: string
|
|
13
|
-
text: string
|
|
14
|
-
isComplete: boolean
|
|
15
|
-
isCompletable: boolean
|
|
16
|
-
isEnabled: boolean
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function getSteps(): Step[] {
|
|
20
|
-
const hasClaudeMd = getFsImplementation().existsSync(
|
|
21
|
-
join(getCwd(), 'CLAUDE.md'),
|
|
22
|
-
)
|
|
23
|
-
const isWorkspaceDirEmpty = isDirEmpty(getCwd())
|
|
24
|
-
|
|
25
|
-
return [
|
|
26
|
-
{
|
|
27
|
-
key: 'workspace',
|
|
28
|
-
text: 'Ask Claude to create a new app or clone a repository',
|
|
29
|
-
isComplete: false,
|
|
30
|
-
isCompletable: true,
|
|
31
|
-
isEnabled: isWorkspaceDirEmpty,
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
key: 'claudemd',
|
|
35
|
-
text: 'Run /init to create a CLAUDE.md file with instructions for Claude',
|
|
36
|
-
isComplete: hasClaudeMd,
|
|
37
|
-
isCompletable: true,
|
|
38
|
-
isEnabled: !isWorkspaceDirEmpty,
|
|
39
|
-
},
|
|
40
|
-
]
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export function isProjectOnboardingComplete(): boolean {
|
|
44
|
-
return getSteps()
|
|
45
|
-
.filter(({ isCompletable, isEnabled }) => isCompletable && isEnabled)
|
|
46
|
-
.every(({ isComplete }) => isComplete)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export function maybeMarkProjectOnboardingComplete(): void {
|
|
50
|
-
// Short-circuit on cached config — isProjectOnboardingComplete() hits
|
|
51
|
-
// the filesystem, and REPL.tsx calls this on every prompt submit.
|
|
52
|
-
if (getCurrentProjectConfig().hasCompletedProjectOnboarding) {
|
|
53
|
-
return
|
|
54
|
-
}
|
|
55
|
-
if (isProjectOnboardingComplete()) {
|
|
56
|
-
saveCurrentProjectConfig(current => ({
|
|
57
|
-
...current,
|
|
58
|
-
hasCompletedProjectOnboarding: true,
|
|
59
|
-
}))
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export const shouldShowProjectOnboarding = memoize((): boolean => {
|
|
64
|
-
const projectConfig = getCurrentProjectConfig()
|
|
65
|
-
// Short-circuit on cached config before isProjectOnboardingComplete()
|
|
66
|
-
// hits the filesystem — this runs during first render.
|
|
67
|
-
if (
|
|
68
|
-
projectConfig.hasCompletedProjectOnboarding ||
|
|
69
|
-
projectConfig.projectOnboardingSeenCount >= 4 ||
|
|
70
|
-
process.env.IS_DEMO
|
|
71
|
-
) {
|
|
72
|
-
return false
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return !isProjectOnboardingComplete()
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
export function incrementProjectOnboardingSeenCount(): void {
|
|
79
|
-
saveCurrentProjectConfig(current => ({
|
|
80
|
-
...current,
|
|
81
|
-
projectOnboardingSeenCount: current.projectOnboardingSeenCount + 1,
|
|
82
|
-
}))
|
|
83
|
-
}
|
package/public/claude-files.png
DELETED
|
Binary file
|
package/public/leak-tweet.png
DELETED
|
Binary file
|
package/replLauncher.tsx
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import type { StatsStore } from "./context/stats.js";
|
|
3
|
-
import type { Root } from "./ink.js";
|
|
4
|
-
import type { Props as REPLProps } from "./screens/REPL.js";
|
|
5
|
-
import type { AppState } from "./state/AppStateStore.js";
|
|
6
|
-
import type { FpsMetrics } from "./utils/fpsTracker.js";
|
|
7
|
-
type AppWrapperProps = {
|
|
8
|
-
getFpsMetrics: () => FpsMetrics | undefined;
|
|
9
|
-
stats?: StatsStore;
|
|
10
|
-
initialState: AppState;
|
|
11
|
-
};
|
|
12
|
-
export async function launchRepl(
|
|
13
|
-
root: Root,
|
|
14
|
-
appProps: AppWrapperProps,
|
|
15
|
-
replProps: REPLProps,
|
|
16
|
-
renderAndRun: (root: Root, element: React.ReactNode) => Promise<void>,
|
|
17
|
-
): Promise<void> {
|
|
18
|
-
const { App } = await import("./components/App.js");
|
|
19
|
-
const { REPL } = await import("./screens/REPL.js");
|
|
20
|
-
await renderAndRun(
|
|
21
|
-
root,
|
|
22
|
-
<App {...appProps}>
|
|
23
|
-
<REPL {...replProps} />
|
|
24
|
-
</App>,
|
|
25
|
-
);
|
|
26
|
-
}
|
|
27
|
-
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlN0YXRzU3RvcmUiLCJSb290IiwiUHJvcHMiLCJSRVBMUHJvcHMiLCJBcHBTdGF0ZSIsIkZwc01ldHJpY3MiLCJBcHBXcmFwcGVyUHJvcHMiLCJnZXRGcHNNZXRyaWNzIiwic3RhdHMiLCJpbml0aWFsU3RhdGUiLCJsYXVuY2hSZXBsIiwicm9vdCIsImFwcFByb3BzIiwicmVwbFByb3BzIiwicmVuZGVyQW5kUnVuIiwiZWxlbWVudCIsIlJlYWN0Tm9kZSIsIlByb21pc2UiLCJBcHAiLCJSRVBMIl0sInNvdXJjZXMiOlsicmVwbExhdW5jaGVyLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgdHlwZSB7IFN0YXRzU3RvcmUgfSBmcm9tICcuL2NvbnRleHQvc3RhdHMuanMnXG5pbXBvcnQgdHlwZSB7IFJvb3QgfSBmcm9tICcuL2luay5qcydcbmltcG9ydCB0eXBlIHsgUHJvcHMgYXMgUkVQTFByb3BzIH0gZnJvbSAnLi9zY3JlZW5zL1JFUEwuanMnXG5pbXBvcnQgdHlwZSB7IEFwcFN0YXRlIH0gZnJvbSAnLi9zdGF0ZS9BcHBTdGF0ZVN0b3JlLmpzJ1xuaW1wb3J0IHR5cGUgeyBGcHNNZXRyaWNzIH0gZnJvbSAnLi91dGlscy9mcHNUcmFja2VyLmpzJ1xuXG50eXBlIEFwcFdyYXBwZXJQcm9wcyA9IHtcbiAgZ2V0RnBzTWV0cmljczogKCkgPT4gRnBzTWV0cmljcyB8IHVuZGVmaW5lZFxuICBzdGF0cz86IFN0YXRzU3RvcmVcbiAgaW5pdGlhbFN0YXRlOiBBcHBTdGF0ZVxufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gbGF1bmNoUmVwbChcbiAgcm9vdDogUm9vdCxcbiAgYXBwUHJvcHM6IEFwcFdyYXBwZXJQcm9wcyxcbiAgcmVwbFByb3BzOiBSRVBMUHJvcHMsXG4gIHJlbmRlckFuZFJ1bjogKHJvb3Q6IFJvb3QsIGVsZW1lbnQ6IFJlYWN0LlJlYWN0Tm9kZSkgPT4gUHJvbWlzZTx2b2lkPixcbik6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCB7IEFwcCB9ID0gYXdhaXQgaW1wb3J0KCcuL2NvbXBvbmVudHMvQXBwLmpzJylcbiAgY29uc3QgeyBSRVBMIH0gPSBhd2FpdCBpbXBvcnQoJy4vc2NyZWVucy9SRVBMLmpzJylcbiAgYXdhaXQgcmVuZGVyQW5kUnVuKFxuICAgIHJvb3QsXG4gICAgPEFwcCB7Li4uYXBwUHJvcHN9PlxuICAgICAgPFJFUEwgey4uLnJlcGxQcm9wc30gLz5cbiAgICA8L0FwcD4sXG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEsT0FBT0EsS0FBSyxNQUFNLE9BQU87QUFDekIsY0FBY0MsVUFBVSxRQUFRLG9CQUFvQjtBQUNwRCxjQUFjQyxJQUFJLFFBQVEsVUFBVTtBQUNwQyxjQUFjQyxLQUFLLElBQUlDLFNBQVMsUUFBUSxtQkFBbUI7QUFDM0QsY0FBY0MsUUFBUSxRQUFRLDBCQUEwQjtBQUN4RCxjQUFjQyxVQUFVLFFBQVEsdUJBQXVCO0FBRXZELEtBQUtDLGVBQWUsR0FBRztFQUNyQkMsYUFBYSxFQUFFLEdBQUcsR0FBR0YsVUFBVSxHQUFHLFNBQVM7RUFDM0NHLEtBQUssQ0FBQyxFQUFFUixVQUFVO0VBQ2xCUyxZQUFZLEVBQUVMLFFBQVE7QUFDeEIsQ0FBQztBQUVELE9BQU8sZUFBZU0sVUFBVUEsQ0FDOUJDLElBQUksRUFBRVYsSUFBSSxFQUNWVyxRQUFRLEVBQUVOLGVBQWUsRUFDekJPLFNBQVMsRUFBRVYsU0FBUyxFQUNwQlcsWUFBWSxFQUFFLENBQUNILElBQUksRUFBRVYsSUFBSSxFQUFFYyxPQUFPLEVBQUVoQixLQUFLLENBQUNpQixTQUFTLEVBQUUsR0FBR0MsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUN0RSxFQUFFQSxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7RUFDZixNQUFNO0lBQUVDO0VBQUksQ0FBQyxHQUFHLE1BQU0sTUFBTSxDQUFDLHFCQUFxQixDQUFDO0VBQ25ELE1BQU07SUFBRUM7RUFBSyxDQUFDLEdBQUcsTUFBTSxNQUFNLENBQUMsbUJBQW1CLENBQUM7RUFDbEQsTUFBTUwsWUFBWSxDQUNoQkgsSUFBSSxFQUNKLENBQUMsR0FBRyxDQUFDLElBQUlDLFFBQVEsQ0FBQztBQUN0QixNQUFNLENBQUMsSUFBSSxDQUFDLElBQUlDLFNBQVMsQ0FBQztBQUMxQixJQUFJLEVBQUUsR0FBRyxDQUNQLENBQUM7QUFDSCIsImlnbm9yZUxpc3QiOltdfQ==
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
/* eslint-disable eslint-plugin-n/no-unsupported-features/node-builtins */
|
|
2
|
-
|
|
3
|
-
import { errorMessage } from '../utils/errors.js'
|
|
4
|
-
import { jsonStringify } from '../utils/slowOperations.js'
|
|
5
|
-
import type { DirectConnectConfig } from './directConnectManager.js'
|
|
6
|
-
import { connectResponseSchema } from './types.js'
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Errors thrown by createDirectConnectSession when the connection fails.
|
|
10
|
-
*/
|
|
11
|
-
export class DirectConnectError extends Error {
|
|
12
|
-
constructor(message: string) {
|
|
13
|
-
super(message)
|
|
14
|
-
this.name = 'DirectConnectError'
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Create a session on a direct-connect server.
|
|
20
|
-
*
|
|
21
|
-
* Posts to `${serverUrl}/sessions`, validates the response, and returns
|
|
22
|
-
* a DirectConnectConfig ready for use by the REPL or headless runner.
|
|
23
|
-
*
|
|
24
|
-
* Throws DirectConnectError on network, HTTP, or response-parsing failures.
|
|
25
|
-
*/
|
|
26
|
-
export async function createDirectConnectSession({
|
|
27
|
-
serverUrl,
|
|
28
|
-
authToken,
|
|
29
|
-
cwd,
|
|
30
|
-
dangerouslySkipPermissions,
|
|
31
|
-
}: {
|
|
32
|
-
serverUrl: string
|
|
33
|
-
authToken?: string
|
|
34
|
-
cwd: string
|
|
35
|
-
dangerouslySkipPermissions?: boolean
|
|
36
|
-
}): Promise<{
|
|
37
|
-
config: DirectConnectConfig
|
|
38
|
-
workDir?: string
|
|
39
|
-
}> {
|
|
40
|
-
const headers: Record<string, string> = {
|
|
41
|
-
'content-type': 'application/json',
|
|
42
|
-
}
|
|
43
|
-
if (authToken) {
|
|
44
|
-
headers['authorization'] = `Bearer ${authToken}`
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
let resp: Response
|
|
48
|
-
try {
|
|
49
|
-
resp = await fetch(`${serverUrl}/sessions`, {
|
|
50
|
-
method: 'POST',
|
|
51
|
-
headers,
|
|
52
|
-
body: jsonStringify({
|
|
53
|
-
cwd,
|
|
54
|
-
...(dangerouslySkipPermissions && {
|
|
55
|
-
dangerously_skip_permissions: true,
|
|
56
|
-
}),
|
|
57
|
-
}),
|
|
58
|
-
})
|
|
59
|
-
} catch (err) {
|
|
60
|
-
throw new DirectConnectError(
|
|
61
|
-
`Failed to connect to server at ${serverUrl}: ${errorMessage(err)}`,
|
|
62
|
-
)
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if (!resp.ok) {
|
|
66
|
-
throw new DirectConnectError(
|
|
67
|
-
`Failed to create session: ${resp.status} ${resp.statusText}`,
|
|
68
|
-
)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const result = connectResponseSchema().safeParse(await resp.json())
|
|
72
|
-
if (!result.success) {
|
|
73
|
-
throw new DirectConnectError(
|
|
74
|
-
`Invalid session response: ${result.error.message}`,
|
|
75
|
-
)
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const data = result.data
|
|
79
|
-
return {
|
|
80
|
-
config: {
|
|
81
|
-
serverUrl,
|
|
82
|
-
sessionId: data.session_id,
|
|
83
|
-
wsUrl: data.ws_url,
|
|
84
|
-
authToken,
|
|
85
|
-
},
|
|
86
|
-
workDir: data.work_dir,
|
|
87
|
-
}
|
|
88
|
-
}
|
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
/* eslint-disable eslint-plugin-n/no-unsupported-features/node-builtins */
|
|
2
|
-
|
|
3
|
-
import type { SDKMessage } from '../entrypoints/agentSdkTypes.js'
|
|
4
|
-
import type {
|
|
5
|
-
SDKControlPermissionRequest,
|
|
6
|
-
StdoutMessage,
|
|
7
|
-
} from '../entrypoints/sdk/controlTypes.js'
|
|
8
|
-
import type { RemotePermissionResponse } from '../remote/RemoteSessionManager.js'
|
|
9
|
-
import { logForDebugging } from '../utils/debug.js'
|
|
10
|
-
import { jsonParse, jsonStringify } from '../utils/slowOperations.js'
|
|
11
|
-
import type { RemoteMessageContent } from '../utils/teleport/api.js'
|
|
12
|
-
|
|
13
|
-
export type DirectConnectConfig = {
|
|
14
|
-
serverUrl: string
|
|
15
|
-
sessionId: string
|
|
16
|
-
wsUrl: string
|
|
17
|
-
authToken?: string
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export type DirectConnectCallbacks = {
|
|
21
|
-
onMessage: (message: SDKMessage) => void
|
|
22
|
-
onPermissionRequest: (
|
|
23
|
-
request: SDKControlPermissionRequest,
|
|
24
|
-
requestId: string,
|
|
25
|
-
) => void
|
|
26
|
-
onConnected?: () => void
|
|
27
|
-
onDisconnected?: () => void
|
|
28
|
-
onError?: (error: Error) => void
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function isStdoutMessage(value: unknown): value is StdoutMessage {
|
|
32
|
-
return (
|
|
33
|
-
typeof value === 'object' &&
|
|
34
|
-
value !== null &&
|
|
35
|
-
'type' in value &&
|
|
36
|
-
typeof value.type === 'string'
|
|
37
|
-
)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export class DirectConnectSessionManager {
|
|
41
|
-
private ws: WebSocket | null = null
|
|
42
|
-
private config: DirectConnectConfig
|
|
43
|
-
private callbacks: DirectConnectCallbacks
|
|
44
|
-
|
|
45
|
-
constructor(config: DirectConnectConfig, callbacks: DirectConnectCallbacks) {
|
|
46
|
-
this.config = config
|
|
47
|
-
this.callbacks = callbacks
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
connect(): void {
|
|
51
|
-
const headers: Record<string, string> = {}
|
|
52
|
-
if (this.config.authToken) {
|
|
53
|
-
headers['authorization'] = `Bearer ${this.config.authToken}`
|
|
54
|
-
}
|
|
55
|
-
// Bun's WebSocket supports headers option but the DOM typings don't
|
|
56
|
-
this.ws = new WebSocket(this.config.wsUrl, {
|
|
57
|
-
headers,
|
|
58
|
-
} as unknown as string[])
|
|
59
|
-
|
|
60
|
-
this.ws.addEventListener('open', () => {
|
|
61
|
-
this.callbacks.onConnected?.()
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
this.ws.addEventListener('message', event => {
|
|
65
|
-
const data = typeof event.data === 'string' ? event.data : ''
|
|
66
|
-
const lines = data.split('\n').filter((l: string) => l.trim())
|
|
67
|
-
|
|
68
|
-
for (const line of lines) {
|
|
69
|
-
let raw: unknown
|
|
70
|
-
try {
|
|
71
|
-
raw = jsonParse(line)
|
|
72
|
-
} catch {
|
|
73
|
-
continue
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
if (!isStdoutMessage(raw)) {
|
|
77
|
-
continue
|
|
78
|
-
}
|
|
79
|
-
const parsed = raw
|
|
80
|
-
|
|
81
|
-
// Handle control requests (permission requests)
|
|
82
|
-
if (parsed.type === 'control_request') {
|
|
83
|
-
if (parsed.request.subtype === 'can_use_tool') {
|
|
84
|
-
this.callbacks.onPermissionRequest(
|
|
85
|
-
parsed.request,
|
|
86
|
-
parsed.request_id,
|
|
87
|
-
)
|
|
88
|
-
} else {
|
|
89
|
-
// Send an error response for unrecognized subtypes so the
|
|
90
|
-
// server doesn't hang waiting for a reply that never comes.
|
|
91
|
-
logForDebugging(
|
|
92
|
-
`[DirectConnect] Unsupported control request subtype: ${parsed.request.subtype}`,
|
|
93
|
-
)
|
|
94
|
-
this.sendErrorResponse(
|
|
95
|
-
parsed.request_id,
|
|
96
|
-
`Unsupported control request subtype: ${parsed.request.subtype}`,
|
|
97
|
-
)
|
|
98
|
-
}
|
|
99
|
-
continue
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Forward SDK messages (assistant, result, system, etc.)
|
|
103
|
-
if (
|
|
104
|
-
parsed.type !== 'control_response' &&
|
|
105
|
-
parsed.type !== 'keep_alive' &&
|
|
106
|
-
parsed.type !== 'control_cancel_request' &&
|
|
107
|
-
parsed.type !== 'streamlined_text' &&
|
|
108
|
-
parsed.type !== 'streamlined_tool_use_summary' &&
|
|
109
|
-
!(parsed.type === 'system' && parsed.subtype === 'post_turn_summary')
|
|
110
|
-
) {
|
|
111
|
-
this.callbacks.onMessage(parsed)
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
this.ws.addEventListener('close', () => {
|
|
117
|
-
this.callbacks.onDisconnected?.()
|
|
118
|
-
})
|
|
119
|
-
|
|
120
|
-
this.ws.addEventListener('error', () => {
|
|
121
|
-
this.callbacks.onError?.(new Error('WebSocket connection error'))
|
|
122
|
-
})
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
sendMessage(content: RemoteMessageContent): boolean {
|
|
126
|
-
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
127
|
-
return false
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Must match SDKUserMessage format expected by `--input-format stream-json`
|
|
131
|
-
const message = jsonStringify({
|
|
132
|
-
type: 'user',
|
|
133
|
-
message: {
|
|
134
|
-
role: 'user',
|
|
135
|
-
content: content,
|
|
136
|
-
},
|
|
137
|
-
parent_tool_use_id: null,
|
|
138
|
-
session_id: '',
|
|
139
|
-
})
|
|
140
|
-
this.ws.send(message)
|
|
141
|
-
return true
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
respondToPermissionRequest(
|
|
145
|
-
requestId: string,
|
|
146
|
-
result: RemotePermissionResponse,
|
|
147
|
-
): void {
|
|
148
|
-
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
149
|
-
return
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// Must match SDKControlResponse format expected by StructuredIO
|
|
153
|
-
const response = jsonStringify({
|
|
154
|
-
type: 'control_response',
|
|
155
|
-
response: {
|
|
156
|
-
subtype: 'success',
|
|
157
|
-
request_id: requestId,
|
|
158
|
-
response: {
|
|
159
|
-
behavior: result.behavior,
|
|
160
|
-
...(result.behavior === 'allow'
|
|
161
|
-
? { updatedInput: result.updatedInput }
|
|
162
|
-
: { message: result.message }),
|
|
163
|
-
},
|
|
164
|
-
},
|
|
165
|
-
})
|
|
166
|
-
this.ws.send(response)
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Send an interrupt signal to cancel the current request
|
|
171
|
-
*/
|
|
172
|
-
sendInterrupt(): void {
|
|
173
|
-
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
174
|
-
return
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// Must match SDKControlRequest format expected by StructuredIO
|
|
178
|
-
const request = jsonStringify({
|
|
179
|
-
type: 'control_request',
|
|
180
|
-
request_id: crypto.randomUUID(),
|
|
181
|
-
request: {
|
|
182
|
-
subtype: 'interrupt',
|
|
183
|
-
},
|
|
184
|
-
})
|
|
185
|
-
this.ws.send(request)
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
private sendErrorResponse(requestId: string, error: string): void {
|
|
189
|
-
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
190
|
-
return
|
|
191
|
-
}
|
|
192
|
-
const response = jsonStringify({
|
|
193
|
-
type: 'control_response',
|
|
194
|
-
response: {
|
|
195
|
-
subtype: 'error',
|
|
196
|
-
request_id: requestId,
|
|
197
|
-
error,
|
|
198
|
-
},
|
|
199
|
-
})
|
|
200
|
-
this.ws.send(response)
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
disconnect(): void {
|
|
204
|
-
if (this.ws) {
|
|
205
|
-
this.ws.close()
|
|
206
|
-
this.ws = null
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
isConnected(): boolean {
|
|
211
|
-
return this.ws?.readyState === WebSocket.OPEN
|
|
212
|
-
}
|
|
213
|
-
}
|
package/server/types.ts
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import type { ChildProcess } from 'child_process'
|
|
2
|
-
import { z } from 'zod/v4'
|
|
3
|
-
import { lazySchema } from '../utils/lazySchema.js'
|
|
4
|
-
|
|
5
|
-
export const connectResponseSchema = lazySchema(() =>
|
|
6
|
-
z.object({
|
|
7
|
-
session_id: z.string(),
|
|
8
|
-
ws_url: z.string(),
|
|
9
|
-
work_dir: z.string().optional(),
|
|
10
|
-
}),
|
|
11
|
-
)
|
|
12
|
-
|
|
13
|
-
export type ServerConfig = {
|
|
14
|
-
port: number
|
|
15
|
-
host: string
|
|
16
|
-
authToken: string
|
|
17
|
-
unix?: string
|
|
18
|
-
/** Idle timeout for detached sessions (ms). 0 = never expire. */
|
|
19
|
-
idleTimeoutMs?: number
|
|
20
|
-
/** Maximum number of concurrent sessions. */
|
|
21
|
-
maxSessions?: number
|
|
22
|
-
/** Default workspace directory for sessions that don't specify cwd. */
|
|
23
|
-
workspace?: string
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export type SessionState =
|
|
27
|
-
| 'starting'
|
|
28
|
-
| 'running'
|
|
29
|
-
| 'detached'
|
|
30
|
-
| 'stopping'
|
|
31
|
-
| 'stopped'
|
|
32
|
-
|
|
33
|
-
export type SessionInfo = {
|
|
34
|
-
id: string
|
|
35
|
-
status: SessionState
|
|
36
|
-
createdAt: number
|
|
37
|
-
workDir: string
|
|
38
|
-
process: ChildProcess | null
|
|
39
|
-
sessionKey?: string
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Stable session key → session metadata. Persisted to ~/.claude/server-sessions.json
|
|
44
|
-
* so sessions can be resumed across server restarts.
|
|
45
|
-
*/
|
|
46
|
-
export type SessionIndexEntry = {
|
|
47
|
-
/** Server-assigned session ID (matches the subprocess's claude session). */
|
|
48
|
-
sessionId: string
|
|
49
|
-
/** The claude transcript session ID for --resume. Same as sessionId for direct sessions. */
|
|
50
|
-
transcriptSessionId: string
|
|
51
|
-
cwd: string
|
|
52
|
-
permissionMode?: string
|
|
53
|
-
createdAt: number
|
|
54
|
-
lastActiveAt: number
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export type SessionIndex = Record<string, SessionIndexEntry>
|
package/stub_types.sh
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Auto-generate stub types.ts files for all missing modules
|
|
3
|
-
|
|
4
|
-
cd /Users/vedantmahajan/Desktop/Xortex/claude-code
|
|
5
|
-
|
|
6
|
-
# Find all directories that need types.ts
|
|
7
|
-
find . -name "*.ts" -o -name "*.tsx" | xargs grep -h "from './types.js'" 2>/dev/null | sed "s/.*from '\([^']*\)'.*/\1/" | sort -u | while read path; do
|
|
8
|
-
dir=$(dirname "$path")
|
|
9
|
-
if [ ! -f "$dir/types.ts" ] && [ ! -f "$dir/types.js" ]; then
|
|
10
|
-
echo "// Stub types file for $dir" > "$dir/types.ts"
|
|
11
|
-
echo "Created stub: $dir/types.ts"
|
|
12
|
-
fi
|
|
13
|
-
done
|