runwork 0.4.1 → 0.5.0
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/bundled-types/core-agent.d.ts +8 -0
- package/bundled-types/core-base-agent.d.ts +4 -0
- package/bundled-types/core-types.d.ts +101 -0
- package/bundled-types/core-workspace.d.ts +10 -0
- package/bundled-types/index.d.ts +2 -0
- package/bundled-types/shared/core-types.d.ts +101 -0
- package/bundled-types/shared/shared/core-types.d.ts +101 -0
- package/bundled-types/task-delegation/index.d.ts +50 -0
- package/bundled-types/task-delegation/sandbox-bridge.d.ts +88 -0
- package/bundled-types/task-delegation/session-manager.d.ts +86 -0
- package/bundled-types/task-delegation/stream-parser.d.ts +25 -0
- package/bundled-types/task-delegation/task-delegation/index.d.ts +30 -0
- package/bundled-types/task-delegation/task-delegation/sandbox-bridge.d.ts +88 -0
- package/bundled-types/task-delegation/task-delegation/session-manager.d.ts +86 -0
- package/bundled-types/task-delegation/task-delegation/stream-parser.d.ts +25 -0
- package/bundled-types/task-delegation/task-delegation/types.d.ts +165 -0
- package/bundled-types/task-delegation/types.d.ts +336 -0
- package/bundled-types/task-delegation.d.ts +2 -0
- package/dist/__tests__/curl-parser.test.d.ts +1 -0
- package/dist/__tests__/curl-parser.test.js +250 -0
- package/dist/__tests__/types-manager.test.js +22 -18
- package/dist/agents/claude-code.d.ts +10 -0
- package/dist/agents/claude-code.js +50 -0
- package/dist/agents/claude-desktop.d.ts +10 -0
- package/dist/agents/claude-desktop.js +141 -0
- package/dist/agents/codex.d.ts +10 -0
- package/dist/agents/codex.js +45 -0
- package/dist/agents/copilot-cli.d.ts +10 -0
- package/dist/agents/copilot-cli.js +46 -0
- package/dist/agents/copilot-vscode.d.ts +10 -0
- package/dist/agents/copilot-vscode.js +37 -0
- package/dist/agents/cursor.d.ts +10 -0
- package/dist/agents/cursor.js +55 -0
- package/dist/agents/detect.d.ts +4 -0
- package/dist/agents/detect.js +43 -0
- package/dist/agents/gemini.d.ts +10 -0
- package/dist/agents/gemini.js +47 -0
- package/dist/agents/types.d.ts +35 -0
- package/dist/agents/types.js +13 -0
- package/dist/agents/utils/json-config.d.ts +14 -0
- package/dist/agents/utils/json-config.js +42 -0
- package/dist/agents/utils/toml-config.d.ts +8 -0
- package/dist/agents/utils/toml-config.js +27 -0
- package/dist/agents/windsurf.d.ts +10 -0
- package/dist/agents/windsurf.js +50 -0
- package/dist/api/client.d.ts +97 -1
- package/dist/api/client.js +199 -0
- package/dist/auth/store.js +18 -0
- package/dist/commands/agents.d.ts +2 -0
- package/dist/commands/agents.js +160 -0
- package/dist/commands/api-keys.d.ts +2 -0
- package/dist/commands/api-keys.js +95 -0
- package/dist/commands/apps.d.ts +2 -0
- package/dist/commands/apps.js +82 -0
- package/dist/commands/components.d.ts +2 -0
- package/dist/commands/components.js +43 -0
- package/dist/commands/endpoints.d.ts +2 -0
- package/dist/commands/endpoints.js +188 -0
- package/dist/commands/entities.d.ts +2 -0
- package/dist/commands/entities.js +198 -0
- package/dist/commands/files.d.ts +2 -0
- package/dist/commands/files.js +192 -0
- package/dist/commands/info.d.ts +1 -0
- package/dist/commands/info.js +71 -127
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.js +10 -8
- package/dist/commands/integrations.js +105 -12
- package/dist/commands/mcp.d.ts +2 -0
- package/dist/commands/mcp.js +117 -0
- package/dist/commands/schedules.d.ts +2 -0
- package/dist/commands/schedules.js +221 -0
- package/dist/commands/setup.d.ts +2 -0
- package/dist/commands/setup.js +199 -0
- package/dist/commands/skills.d.ts +2 -0
- package/dist/commands/skills.js +134 -0
- package/dist/commands/sync.d.ts +2 -0
- package/dist/commands/sync.js +146 -0
- package/dist/commands/workflows.d.ts +2 -0
- package/dist/commands/workflows.js +187 -0
- package/dist/generated/bundled-types.js +35 -33
- package/dist/generated/version.d.ts +1 -1
- package/dist/generated/version.js +1 -1
- package/dist/index.js +26 -0
- package/dist/logs/tailer.js +41 -1
- package/dist/types.d.ts +204 -0
- package/dist/ui/banner.js +7 -6
- package/dist/utils/app-info.d.ts +66 -0
- package/dist/utils/app-info.js +150 -0
- package/dist/utils/curl-parser.d.ts +12 -0
- package/dist/utils/curl-parser.js +44 -0
- package/dist/utils/data-input.d.ts +1 -0
- package/dist/utils/data-input.js +38 -0
- package/dist/workspace/resolve.d.ts +19 -0
- package/dist/workspace/resolve.js +100 -0
- package/package.json +3 -2
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { execFileSync } from 'child_process';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { homedir } from 'os';
|
|
4
|
+
import { mergeJsonMcpServers } from './utils/json-config.js';
|
|
5
|
+
export class CopilotCliAdapter {
|
|
6
|
+
name = 'GitHub Copilot CLI';
|
|
7
|
+
slug = 'copilot-cli';
|
|
8
|
+
async detect() {
|
|
9
|
+
try {
|
|
10
|
+
execFileSync('which', ['copilot'], { stdio: 'pipe' });
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
try {
|
|
15
|
+
execFileSync('gh', ['copilot', '--version'], { stdio: 'pipe' });
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
supportsMcpScope(_scope) {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
supportsSkills() {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
async writeMcpServers(servers, scope) {
|
|
30
|
+
const filePath = scope === 'project'
|
|
31
|
+
? join(process.cwd(), '.copilot', 'mcp-config.json')
|
|
32
|
+
: join(homedir(), '.copilot', 'mcp-config.json');
|
|
33
|
+
const entries = {};
|
|
34
|
+
for (const s of servers) {
|
|
35
|
+
entries[s.name] = {
|
|
36
|
+
type: 'http',
|
|
37
|
+
url: s.url,
|
|
38
|
+
...(s.headers ? { headers: s.headers } : {}),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
mergeJsonMcpServers(filePath, entries, 'mcpServers');
|
|
42
|
+
}
|
|
43
|
+
async writeSkills(_skills, _scope) {
|
|
44
|
+
// Not supported
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AgentAdapter, McpServerEntry, SkillFile } from './types.js';
|
|
2
|
+
export declare class CopilotVscodeAdapter implements AgentAdapter {
|
|
3
|
+
name: string;
|
|
4
|
+
slug: string;
|
|
5
|
+
detect(): Promise<boolean>;
|
|
6
|
+
supportsMcpScope(scope: 'project' | 'user'): boolean;
|
|
7
|
+
supportsSkills(): boolean;
|
|
8
|
+
writeMcpServers(servers: McpServerEntry[], _scope: 'project' | 'user'): Promise<void>;
|
|
9
|
+
writeSkills(_skills: SkillFile[], _scope: 'project' | 'user'): Promise<void>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { execFileSync } from 'child_process';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { mergeJsonMcpServers } from './utils/json-config.js';
|
|
4
|
+
export class CopilotVscodeAdapter {
|
|
5
|
+
name = 'GitHub Copilot (VS Code)';
|
|
6
|
+
slug = 'copilot-vscode';
|
|
7
|
+
async detect() {
|
|
8
|
+
try {
|
|
9
|
+
const output = execFileSync('code', ['--list-extensions'], { stdio: 'pipe', encoding: 'utf-8' });
|
|
10
|
+
return output.includes('GitHub.copilot');
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
supportsMcpScope(scope) {
|
|
17
|
+
return scope === 'project';
|
|
18
|
+
}
|
|
19
|
+
supportsSkills() {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
async writeMcpServers(servers, _scope) {
|
|
23
|
+
const filePath = join(process.cwd(), '.vscode', 'mcp.json');
|
|
24
|
+
const entries = {};
|
|
25
|
+
for (const s of servers) {
|
|
26
|
+
entries[s.name] = {
|
|
27
|
+
type: 'http',
|
|
28
|
+
url: s.url,
|
|
29
|
+
...(s.headers ? { headers: s.headers } : {}),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
mergeJsonMcpServers(filePath, entries, 'servers');
|
|
33
|
+
}
|
|
34
|
+
async writeSkills(_skills, _scope) {
|
|
35
|
+
// Not supported
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AgentAdapter, McpServerEntry, SkillFile } from './types.js';
|
|
2
|
+
export declare class CursorAdapter implements AgentAdapter {
|
|
3
|
+
name: string;
|
|
4
|
+
slug: string;
|
|
5
|
+
detect(): Promise<boolean>;
|
|
6
|
+
supportsMcpScope(_scope: 'project' | 'user'): boolean;
|
|
7
|
+
supportsSkills(): boolean;
|
|
8
|
+
writeMcpServers(servers: McpServerEntry[], scope: 'project' | 'user'): Promise<void>;
|
|
9
|
+
writeSkills(skills: SkillFile[], scope: 'project' | 'user'): Promise<void>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, writeFileSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { homedir, platform } from 'os';
|
|
4
|
+
import { execFileSync } from 'child_process';
|
|
5
|
+
import { mergeJsonMcpServers } from './utils/json-config.js';
|
|
6
|
+
export class CursorAdapter {
|
|
7
|
+
name = 'Cursor';
|
|
8
|
+
slug = 'cursor';
|
|
9
|
+
async detect() {
|
|
10
|
+
const os = platform();
|
|
11
|
+
if (os === 'darwin' && existsSync('/Applications/Cursor.app'))
|
|
12
|
+
return true;
|
|
13
|
+
try {
|
|
14
|
+
execFileSync('which', ['cursor'], { stdio: 'pipe' });
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
supportsMcpScope(_scope) {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
supportsSkills() {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
async writeMcpServers(servers, scope) {
|
|
28
|
+
const filePath = scope === 'project'
|
|
29
|
+
? join(process.cwd(), '.cursor', 'mcp.json')
|
|
30
|
+
: join(homedir(), '.cursor', 'mcp.json');
|
|
31
|
+
const entries = {};
|
|
32
|
+
for (const s of servers) {
|
|
33
|
+
entries[s.name] = {
|
|
34
|
+
url: s.url,
|
|
35
|
+
...(s.headers ? { headers: s.headers } : {}),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
mergeJsonMcpServers(filePath, entries, 'mcpServers');
|
|
39
|
+
}
|
|
40
|
+
async writeSkills(skills, scope) {
|
|
41
|
+
if (scope === 'user')
|
|
42
|
+
return;
|
|
43
|
+
const rulesDir = join(process.cwd(), '.cursor', 'rules');
|
|
44
|
+
mkdirSync(rulesDir, { recursive: true });
|
|
45
|
+
for (const skill of skills) {
|
|
46
|
+
const mdcContent = `---
|
|
47
|
+
description: "${skill.description}"
|
|
48
|
+
alwaysApply: false
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
${skill.content}`;
|
|
52
|
+
writeFileSync(join(rulesDir, `${skill.filename}.mdc`), mdcContent);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { ClaudeCodeAdapter } from './claude-code.js';
|
|
2
|
+
import { ClaudeDesktopAdapter } from './claude-desktop.js';
|
|
3
|
+
import { CursorAdapter } from './cursor.js';
|
|
4
|
+
import { CodexAdapter } from './codex.js';
|
|
5
|
+
import { WindsurfAdapter } from './windsurf.js';
|
|
6
|
+
import { CopilotVscodeAdapter } from './copilot-vscode.js';
|
|
7
|
+
import { CopilotCliAdapter } from './copilot-cli.js';
|
|
8
|
+
import { GeminiAdapter } from './gemini.js';
|
|
9
|
+
const ALL_ADAPTERS = [
|
|
10
|
+
new ClaudeCodeAdapter(),
|
|
11
|
+
new ClaudeDesktopAdapter(),
|
|
12
|
+
new CursorAdapter(),
|
|
13
|
+
new CodexAdapter(),
|
|
14
|
+
new WindsurfAdapter(),
|
|
15
|
+
new CopilotVscodeAdapter(),
|
|
16
|
+
new CopilotCliAdapter(),
|
|
17
|
+
new GeminiAdapter(),
|
|
18
|
+
];
|
|
19
|
+
export async function detectAgents() {
|
|
20
|
+
const results = await Promise.all(ALL_ADAPTERS.map(async (adapter) => {
|
|
21
|
+
try {
|
|
22
|
+
const detected = await adapter.detect();
|
|
23
|
+
return detected ? adapter : null;
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
}));
|
|
29
|
+
return results.filter((a) => a !== null);
|
|
30
|
+
}
|
|
31
|
+
export function getAdapterBySlug(slug) {
|
|
32
|
+
return ALL_ADAPTERS.find(a => a.slug === slug);
|
|
33
|
+
}
|
|
34
|
+
export function printNoAgentsMessage() {
|
|
35
|
+
console.log('No supported AI coding agents detected.\n');
|
|
36
|
+
console.log('Install one to get started:\n');
|
|
37
|
+
console.log(' Claude Code: npm i -g @anthropic-ai/claude-code');
|
|
38
|
+
console.log(' Cursor: https://cursor.com');
|
|
39
|
+
console.log(' Codex CLI: npm i -g @openai/codex');
|
|
40
|
+
console.log(' Gemini CLI: https://github.com/google-gemini/gemini-cli');
|
|
41
|
+
console.log(' Windsurf: https://windsurf.com');
|
|
42
|
+
console.log('');
|
|
43
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AgentAdapter, McpServerEntry, SkillFile } from './types.js';
|
|
2
|
+
export declare class GeminiAdapter implements AgentAdapter {
|
|
3
|
+
name: string;
|
|
4
|
+
slug: string;
|
|
5
|
+
detect(): Promise<boolean>;
|
|
6
|
+
supportsMcpScope(_scope: 'project' | 'user'): boolean;
|
|
7
|
+
supportsSkills(): boolean;
|
|
8
|
+
writeMcpServers(servers: McpServerEntry[], scope: 'project' | 'user'): Promise<void>;
|
|
9
|
+
writeSkills(skills: SkillFile[], scope: 'project' | 'user'): Promise<void>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { homedir } from 'os';
|
|
4
|
+
import { execFileSync } from 'child_process';
|
|
5
|
+
import { buildSkillMd } from './types.js';
|
|
6
|
+
import { mergeJsonMcpServers } from './utils/json-config.js';
|
|
7
|
+
export class GeminiAdapter {
|
|
8
|
+
name = 'Gemini CLI';
|
|
9
|
+
slug = 'gemini';
|
|
10
|
+
async detect() {
|
|
11
|
+
try {
|
|
12
|
+
execFileSync('which', ['gemini'], { stdio: 'pipe' });
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
supportsMcpScope(_scope) {
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
supportsSkills() {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
async writeMcpServers(servers, scope) {
|
|
26
|
+
const filePath = scope === 'project'
|
|
27
|
+
? join(process.cwd(), '.gemini', 'settings.json')
|
|
28
|
+
: join(homedir(), '.gemini', 'settings.json');
|
|
29
|
+
const entries = {};
|
|
30
|
+
for (const s of servers) {
|
|
31
|
+
entries[s.name] = {
|
|
32
|
+
url: s.url,
|
|
33
|
+
...(s.headers ? { headers: s.headers } : {}),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
mergeJsonMcpServers(filePath, entries, 'mcpServers');
|
|
37
|
+
}
|
|
38
|
+
async writeSkills(skills, scope) {
|
|
39
|
+
const baseDir = scope === 'project'
|
|
40
|
+
? join(process.cwd(), '.gemini', 'commands')
|
|
41
|
+
: join(homedir(), '.gemini', 'commands');
|
|
42
|
+
mkdirSync(baseDir, { recursive: true });
|
|
43
|
+
for (const skill of skills) {
|
|
44
|
+
writeFileSync(join(baseDir, `${skill.filename}.md`), buildSkillMd(skill));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export interface McpServerEntry {
|
|
2
|
+
name: string;
|
|
3
|
+
url: string;
|
|
4
|
+
transport: 'sse' | 'streamable-http';
|
|
5
|
+
headers?: Record<string, string>;
|
|
6
|
+
}
|
|
7
|
+
export interface SkillFile {
|
|
8
|
+
name: string;
|
|
9
|
+
filename: string;
|
|
10
|
+
content: string;
|
|
11
|
+
description: string;
|
|
12
|
+
}
|
|
13
|
+
export interface AgentAdapter {
|
|
14
|
+
/** Display name, e.g. "Claude Code" */
|
|
15
|
+
name: string;
|
|
16
|
+
/** CLI identifier, e.g. "claude-code" */
|
|
17
|
+
slug: string;
|
|
18
|
+
/** Check if this agent is installed */
|
|
19
|
+
detect(): Promise<boolean>;
|
|
20
|
+
/** Write MCP server configs (merge with existing, only touch runwork-* entries) */
|
|
21
|
+
writeMcpServers(servers: McpServerEntry[], scope: 'project' | 'user'): Promise<void>;
|
|
22
|
+
/** Write skill files to agent's skills directory */
|
|
23
|
+
writeSkills(skills: SkillFile[], scope: 'project' | 'user'): Promise<void>;
|
|
24
|
+
/** Whether this agent supports skills/commands */
|
|
25
|
+
supportsSkills(): boolean;
|
|
26
|
+
/** Whether this agent supports MCP config at the given scope */
|
|
27
|
+
supportsMcpScope(scope: 'project' | 'user'): boolean;
|
|
28
|
+
}
|
|
29
|
+
/** Prefix for MCP server entries managed by runwork */
|
|
30
|
+
export declare const RUNWORK_MCP_PREFIX = "runwork-";
|
|
31
|
+
/**
|
|
32
|
+
* Build a SKILL.md file with YAML frontmatter.
|
|
33
|
+
* Ensures the content always has proper frontmatter for agent discovery.
|
|
34
|
+
*/
|
|
35
|
+
export declare function buildSkillMd(skill: SkillFile): string;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Prefix for MCP server entries managed by runwork */
|
|
2
|
+
export const RUNWORK_MCP_PREFIX = 'runwork-';
|
|
3
|
+
/**
|
|
4
|
+
* Build a SKILL.md file with YAML frontmatter.
|
|
5
|
+
* Ensures the content always has proper frontmatter for agent discovery.
|
|
6
|
+
*/
|
|
7
|
+
export function buildSkillMd(skill) {
|
|
8
|
+
// If content already has frontmatter, return as-is
|
|
9
|
+
if (skill.content.trimStart().startsWith('---\n')) {
|
|
10
|
+
return skill.content;
|
|
11
|
+
}
|
|
12
|
+
return `---\nname: ${skill.name}\ndescription: ${skill.description}\n---\n\n${skill.content}`;
|
|
13
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read a JSON config file. Returns empty object if file doesn't exist.
|
|
3
|
+
*/
|
|
4
|
+
export declare function readJsonConfig(filePath: string): Record<string, unknown>;
|
|
5
|
+
/**
|
|
6
|
+
* Write a JSON config file, creating parent directories as needed.
|
|
7
|
+
*/
|
|
8
|
+
export declare function writeJsonConfig(filePath: string, config: Record<string, unknown>): void;
|
|
9
|
+
/**
|
|
10
|
+
* Merge MCP server entries into a JSON config file.
|
|
11
|
+
* Removes all existing runwork-* entries, adds new ones.
|
|
12
|
+
* Preserves user's non-runwork entries.
|
|
13
|
+
*/
|
|
14
|
+
export declare function mergeJsonMcpServers(filePath: string, servers: Record<string, Record<string, unknown>>, topKey: string): void;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs';
|
|
2
|
+
import { dirname } from 'path';
|
|
3
|
+
import { RUNWORK_MCP_PREFIX } from '../types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Read a JSON config file. Returns empty object if file doesn't exist.
|
|
6
|
+
*/
|
|
7
|
+
export function readJsonConfig(filePath) {
|
|
8
|
+
if (!existsSync(filePath))
|
|
9
|
+
return {};
|
|
10
|
+
try {
|
|
11
|
+
return JSON.parse(readFileSync(filePath, 'utf-8'));
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return {};
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Write a JSON config file, creating parent directories as needed.
|
|
19
|
+
*/
|
|
20
|
+
export function writeJsonConfig(filePath, config) {
|
|
21
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
22
|
+
writeFileSync(filePath, JSON.stringify(config, null, 2) + '\n');
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Merge MCP server entries into a JSON config file.
|
|
26
|
+
* Removes all existing runwork-* entries, adds new ones.
|
|
27
|
+
* Preserves user's non-runwork entries.
|
|
28
|
+
*/
|
|
29
|
+
export function mergeJsonMcpServers(filePath, servers, topKey) {
|
|
30
|
+
const config = readJsonConfig(filePath);
|
|
31
|
+
const existing = (config[topKey] || {});
|
|
32
|
+
// Remove all runwork-* entries
|
|
33
|
+
for (const key of Object.keys(existing)) {
|
|
34
|
+
if (key.startsWith(RUNWORK_MCP_PREFIX)) {
|
|
35
|
+
delete existing[key];
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// Add new runwork entries
|
|
39
|
+
Object.assign(existing, servers);
|
|
40
|
+
config[topKey] = existing;
|
|
41
|
+
writeJsonConfig(filePath, config);
|
|
42
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merge MCP servers into a Codex config.toml file.
|
|
3
|
+
* Removes existing [mcp_servers.runwork-*] sections, adds new ones.
|
|
4
|
+
*/
|
|
5
|
+
export declare function mergeTomlMcpServers(filePath: string, servers: Record<string, {
|
|
6
|
+
url: string;
|
|
7
|
+
bearerTokenEnvVar?: string;
|
|
8
|
+
}>): void;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs';
|
|
2
|
+
import { dirname } from 'path';
|
|
3
|
+
/**
|
|
4
|
+
* Merge MCP servers into a Codex config.toml file.
|
|
5
|
+
* Removes existing [mcp_servers.runwork-*] sections, adds new ones.
|
|
6
|
+
*/
|
|
7
|
+
export function mergeTomlMcpServers(filePath, servers) {
|
|
8
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
9
|
+
let content = existsSync(filePath) ? readFileSync(filePath, 'utf-8') : '';
|
|
10
|
+
// Remove existing runwork-* mcp_servers sections
|
|
11
|
+
content = content.replace(/\[mcp_servers\.runwork-[^\]]*\]\n(?:[^\[]*\n?)*/g, '');
|
|
12
|
+
// Remove trailing blank lines
|
|
13
|
+
content = content.trimEnd();
|
|
14
|
+
// Append new sections
|
|
15
|
+
const sections = [];
|
|
16
|
+
for (const [name, config] of Object.entries(servers)) {
|
|
17
|
+
const lines = [`[mcp_servers.${name}]`, `url = "${config.url}"`];
|
|
18
|
+
if (config.bearerTokenEnvVar) {
|
|
19
|
+
lines.push(`bearer_token_env_var = "${config.bearerTokenEnvVar}"`);
|
|
20
|
+
}
|
|
21
|
+
sections.push(lines.join('\n'));
|
|
22
|
+
}
|
|
23
|
+
if (sections.length > 0) {
|
|
24
|
+
content += '\n\n' + sections.join('\n\n') + '\n';
|
|
25
|
+
}
|
|
26
|
+
writeFileSync(filePath, content);
|
|
27
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AgentAdapter, McpServerEntry, SkillFile } from './types.js';
|
|
2
|
+
export declare class WindsurfAdapter implements AgentAdapter {
|
|
3
|
+
name: string;
|
|
4
|
+
slug: string;
|
|
5
|
+
detect(): Promise<boolean>;
|
|
6
|
+
supportsMcpScope(scope: 'project' | 'user'): boolean;
|
|
7
|
+
supportsSkills(): boolean;
|
|
8
|
+
writeMcpServers(servers: McpServerEntry[], _scope: 'project' | 'user'): Promise<void>;
|
|
9
|
+
writeSkills(skills: SkillFile[], scope: 'project' | 'user'): Promise<void>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, writeFileSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { homedir, platform } from 'os';
|
|
4
|
+
import { execFileSync } from 'child_process';
|
|
5
|
+
import { mergeJsonMcpServers } from './utils/json-config.js';
|
|
6
|
+
function getConfigPath() {
|
|
7
|
+
return join(homedir(), '.codeium', 'windsurf', 'mcp_config.json');
|
|
8
|
+
}
|
|
9
|
+
export class WindsurfAdapter {
|
|
10
|
+
name = 'Windsurf';
|
|
11
|
+
slug = 'windsurf';
|
|
12
|
+
async detect() {
|
|
13
|
+
const os = platform();
|
|
14
|
+
if (os === 'darwin' && existsSync('/Applications/Windsurf.app'))
|
|
15
|
+
return true;
|
|
16
|
+
try {
|
|
17
|
+
execFileSync('which', ['windsurf'], { stdio: 'pipe' });
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
supportsMcpScope(scope) {
|
|
25
|
+
return scope === 'user';
|
|
26
|
+
}
|
|
27
|
+
supportsSkills() {
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
async writeMcpServers(servers, _scope) {
|
|
31
|
+
const entries = {};
|
|
32
|
+
for (const s of servers) {
|
|
33
|
+
entries[s.name] = {
|
|
34
|
+
serverUrl: s.url,
|
|
35
|
+
...(s.headers ? { headers: s.headers } : {}),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
mergeJsonMcpServers(getConfigPath(), entries, 'mcpServers');
|
|
39
|
+
}
|
|
40
|
+
async writeSkills(skills, scope) {
|
|
41
|
+
if (scope === 'user')
|
|
42
|
+
return;
|
|
43
|
+
const rulesDir = join(process.cwd(), '.windsurf', 'rules');
|
|
44
|
+
mkdirSync(rulesDir, { recursive: true });
|
|
45
|
+
for (const skill of skills) {
|
|
46
|
+
const content = `---\ntrigger: manual\n---\n\n${skill.content}`;
|
|
47
|
+
writeFileSync(join(rulesDir, `${skill.filename}.md`), content);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
package/dist/api/client.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Credentials, AppInfo, WorkspaceInfo, DevSession, WorkspaceAllData } from '../types.js';
|
|
1
|
+
import type { Credentials, AppInfo, WorkspaceInfo, DevSession, WorkspaceAllData, WorkspaceSkill, ExternalSkillRegistration, McpServerConfig, RegistryEntity, WorkflowInfo, TriggerWorkflowResponse, WorkflowInstancesData, WorkflowInstanceDetailData, ScheduleInfo, TriggerScheduleResponse, ScheduleHistoryData, WorkspaceEndpoint, FileStorageInfo, ListBucketFilesData, ComponentInfo, ApiKeyInfo, ApiKeyCreateResponse, AgentInfo, AgentStatusResponse, AgentExecutionRecord } from '../types.js';
|
|
2
2
|
export declare class ApiClient {
|
|
3
3
|
private baseUrl;
|
|
4
4
|
private apiKey;
|
|
@@ -92,4 +92,100 @@ export declare class ApiClient {
|
|
|
92
92
|
*/
|
|
93
93
|
getWorkspaceAll(workspaceId: string): Promise<WorkspaceAllData>;
|
|
94
94
|
getGitRemoteUrl(workspaceId: string, appId: string): string;
|
|
95
|
+
listWorkspaceSkills(workspaceId: string): Promise<WorkspaceSkill[]>;
|
|
96
|
+
listExternalSkills(workspaceId: string): Promise<ExternalSkillRegistration[]>;
|
|
97
|
+
importExternalSkill(workspaceId: string, skill: {
|
|
98
|
+
name: string;
|
|
99
|
+
description: string;
|
|
100
|
+
content: string;
|
|
101
|
+
importedFrom: string;
|
|
102
|
+
}): Promise<ExternalSkillRegistration>;
|
|
103
|
+
updateExternalSkill(workspaceId: string, skillId: string, skill: {
|
|
104
|
+
name: string;
|
|
105
|
+
description: string;
|
|
106
|
+
content: string;
|
|
107
|
+
}): Promise<ExternalSkillRegistration>;
|
|
108
|
+
getAppSkillContent(workspaceId: string, appId: string): Promise<string>;
|
|
109
|
+
listMcpServers(workspaceId: string): Promise<McpServerConfig[]>;
|
|
110
|
+
addMcpServer(workspaceId: string, server: {
|
|
111
|
+
name: string;
|
|
112
|
+
url: string;
|
|
113
|
+
transport: 'sse' | 'streamable-http';
|
|
114
|
+
}): Promise<McpServerConfig>;
|
|
115
|
+
removeMcpServer(workspaceId: string, serverId: string): Promise<void>;
|
|
116
|
+
listEntities(workspaceId: string): Promise<RegistryEntity[]>;
|
|
117
|
+
listEntityRecords(workspaceId: string, entityName: string, opts?: {
|
|
118
|
+
limit?: number;
|
|
119
|
+
cursor?: string;
|
|
120
|
+
deploymentMode?: string;
|
|
121
|
+
}): Promise<{
|
|
122
|
+
items: unknown[];
|
|
123
|
+
next: string | null;
|
|
124
|
+
}>;
|
|
125
|
+
getEntityRecord(workspaceId: string, entityName: string, recordId: string): Promise<{
|
|
126
|
+
item: unknown;
|
|
127
|
+
}>;
|
|
128
|
+
createEntityRecord(workspaceId: string, entityName: string, data: Record<string, unknown>): Promise<{
|
|
129
|
+
item: unknown;
|
|
130
|
+
}>;
|
|
131
|
+
updateEntityRecord(workspaceId: string, entityName: string, recordId: string, data: Record<string, unknown>): Promise<{
|
|
132
|
+
item: unknown;
|
|
133
|
+
}>;
|
|
134
|
+
deleteEntityRecord(workspaceId: string, entityName: string, recordId: string): Promise<{
|
|
135
|
+
success: boolean;
|
|
136
|
+
}>;
|
|
137
|
+
listWorkflows(workspaceId: string): Promise<WorkflowInfo[]>;
|
|
138
|
+
triggerWorkflow(workspaceId: string, opts: {
|
|
139
|
+
workflowName: string;
|
|
140
|
+
appId: string;
|
|
141
|
+
params?: Record<string, unknown>;
|
|
142
|
+
instanceId?: string;
|
|
143
|
+
}): Promise<TriggerWorkflowResponse>;
|
|
144
|
+
listWorkflowInstances(workspaceId: string, appId: string, opts?: {
|
|
145
|
+
workflowName?: string;
|
|
146
|
+
status?: string;
|
|
147
|
+
limit?: number;
|
|
148
|
+
}): Promise<WorkflowInstancesData>;
|
|
149
|
+
getWorkflowInstance(workspaceId: string, instanceId: string, appId?: string): Promise<WorkflowInstanceDetailData>;
|
|
150
|
+
listSchedules(workspaceId: string): Promise<ScheduleInfo[]>;
|
|
151
|
+
triggerSchedule(workspaceId: string, opts: {
|
|
152
|
+
scheduleName: string;
|
|
153
|
+
appId: string;
|
|
154
|
+
deploymentMode?: string;
|
|
155
|
+
}): Promise<TriggerScheduleResponse>;
|
|
156
|
+
getScheduleHistory(workspaceId: string, appId: string): Promise<ScheduleHistoryData>;
|
|
157
|
+
getScheduleStatus(workspaceId: string, appId: string): Promise<Record<string, unknown>>;
|
|
158
|
+
pauseSchedule(workspaceId: string, scheduleName: string, appId: string): Promise<void>;
|
|
159
|
+
resumeSchedule(workspaceId: string, scheduleName: string, appId: string): Promise<void>;
|
|
160
|
+
listEndpoints(workspaceId: string): Promise<WorkspaceEndpoint[]>;
|
|
161
|
+
listFileStorages(workspaceId: string): Promise<FileStorageInfo[]>;
|
|
162
|
+
listBucketFiles(workspaceId: string, bucketName: string, opts?: {
|
|
163
|
+
prefix?: string;
|
|
164
|
+
delimiter?: string;
|
|
165
|
+
}): Promise<ListBucketFilesData>;
|
|
166
|
+
getPresignedUrl(workspaceId: string, bucketName: string, opts: {
|
|
167
|
+
action: 'read' | 'write';
|
|
168
|
+
key: string;
|
|
169
|
+
expiresIn?: number;
|
|
170
|
+
}): Promise<{
|
|
171
|
+
url: string;
|
|
172
|
+
}>;
|
|
173
|
+
deleteBucketFile(workspaceId: string, bucketName: string, key: string): Promise<void>;
|
|
174
|
+
listComponents(workspaceId: string): Promise<ComponentInfo[]>;
|
|
175
|
+
listApiKeys(workspaceId: string): Promise<ApiKeyInfo[]>;
|
|
176
|
+
createApiKey(workspaceId: string, opts: {
|
|
177
|
+
name: string;
|
|
178
|
+
appId?: string;
|
|
179
|
+
allowedEndpoints?: string[];
|
|
180
|
+
scopes?: string[];
|
|
181
|
+
}): Promise<ApiKeyCreateResponse>;
|
|
182
|
+
listAgents(workspaceId: string): Promise<AgentInfo[]>;
|
|
183
|
+
getAgentStatus(workspaceId: string, agentName: string, appId: string): Promise<AgentStatusResponse>;
|
|
184
|
+
listAgentExecutions(workspaceId: string): Promise<AgentExecutionRecord[]>;
|
|
185
|
+
getAgentExecutionStream(workspaceId: string, sessionId: string): Promise<string>;
|
|
186
|
+
callIntegrationProxy(integrationDbId: string, method: string, path: string, opts?: {
|
|
187
|
+
body?: unknown;
|
|
188
|
+
headers?: Record<string, string>;
|
|
189
|
+
query?: string;
|
|
190
|
+
}): Promise<unknown>;
|
|
95
191
|
}
|