orkestrate 0.1.14 → 0.2.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/AGENTS.md +56 -0
- package/CONTRIBUTING.md +35 -0
- package/README.md +38 -58
- package/SECURITY.md +24 -0
- package/bin/orkestrate.ts +2 -0
- package/docs/concepts.md +119 -0
- package/docs/demo-extension-builder.md +82 -0
- package/docs/extensions/adapters.md +57 -0
- package/docs/extensions/architecture.md +49 -0
- package/docs/extensions/introduction.md +26 -0
- package/docs/getting-started.md +85 -0
- package/docs/hosted-registry.md +90 -0
- package/docs/pack-authoring.md +75 -0
- package/docs/roadmap.md +59 -0
- package/docs/troubleshooting.md +28 -0
- package/extensions/opencode-adapter/index.ts +106 -0
- package/extensions/opencode-adapter/orkestrate.extension.json +17 -0
- package/package.json +40 -33
- package/packs/coding/harnesses/opencode/agents/coding.md +8 -0
- package/packs/coding/harnesses/opencode/opencode.json +24 -0
- package/packs/coding/harnesses/opencode/skills/orkestrate/SKILL.md +57 -0
- package/packs/coding/pack.yaml +5 -0
- package/packs/extension-builder/harnesses/opencode/agents/extension-builder.md +8 -0
- package/packs/extension-builder/harnesses/opencode/opencode.json +31 -0
- package/packs/extension-builder/harnesses/opencode/skills/orkestrate/SKILL.md +54 -0
- package/packs/extension-builder/harnesses/opencode/skills/orkestrate-pack-author/SKILL.md +59 -0
- package/packs/extension-builder/pack.yaml +5 -0
- package/src/cli/cmd/extension-submit.ts +267 -0
- package/src/cli/cmd/pack-create.ts +43 -0
- package/src/cli/cmd/pack.ts +53 -0
- package/src/cli/cmd/profile-create.ts +199 -0
- package/src/cli/cmd/profile-submit.ts +236 -0
- package/src/cli/cmd/profile-validate.ts +5 -0
- package/src/cli/cmd/registry.ts +66 -0
- package/src/cli/cmd/run.ts +37 -0
- package/src/cli/index.ts +163 -0
- package/src/cli/tui.ts +355 -0
- package/src/cli/ui/welcome.ts +73 -0
- package/src/cli.ts +1 -0
- package/src/sdk/cross-platform.ts +25 -0
- package/src/sdk/extensions/loader.ts +89 -0
- package/src/sdk/extensions/manifest.ts +193 -0
- package/src/sdk/extensions/types.ts +12 -0
- package/src/sdk/harness/sync-slice.ts +57 -0
- package/src/sdk/launch/broker.ts +87 -0
- package/src/sdk/launch/runner.ts +57 -0
- package/src/sdk/launch/terminal.ts +75 -0
- package/src/sdk/launch/types.ts +7 -0
- package/src/sdk/launch/windows.ts +109 -0
- package/src/sdk/packs/catalog.ts +172 -0
- package/src/sdk/packs/create.ts +99 -0
- package/src/sdk/packs/fs.ts +52 -0
- package/src/sdk/packs/github.ts +249 -0
- package/src/sdk/packs/paths.ts +19 -0
- package/src/sdk/packs/registry.ts +40 -0
- package/src/sdk/packs/schema.ts +51 -0
- package/src/sdk/packs/store.ts +172 -0
- package/src/sdk/profiles/catalog.ts +199 -0
- package/src/sdk/profiles/github.ts +177 -0
- package/src/sdk/profiles/install.ts +161 -0
- package/src/sdk/profiles/load.ts +209 -0
- package/src/sdk/profiles/materialize.ts +85 -0
- package/src/sdk/profiles/pack.ts +128 -0
- package/src/sdk/profiles/schema.ts +201 -0
- package/src/sdk/registry.ts +19 -0
- package/src/sdk/runs/registry.ts +142 -0
- package/src/sdk/runs/types.ts +15 -0
- package/src/sdk/types.ts +39 -0
- package/src/version.ts +3 -0
- package/dist/cli.js +0 -1668
- package/dist/cli.js.map +0 -1
- package/dist/mcp-entry.js +0 -181
- package/dist/mcp-entry.js.map +0 -1
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { listProfiles, loadProfile } from "../../sdk/profiles/load";
|
|
2
|
+
import { parseProfile, type Profile } from "../../sdk/profiles/schema";
|
|
3
|
+
|
|
4
|
+
const GREEN = "\x1b[32m";
|
|
5
|
+
const RED = "\x1b[31m";
|
|
6
|
+
const YELLOW = "\x1b[33m";
|
|
7
|
+
const BOLD = "\x1b[1m";
|
|
8
|
+
const RESET = "\x1b[0m";
|
|
9
|
+
const BLUE = "\x1b[34m";
|
|
10
|
+
|
|
11
|
+
const REGISTRY_URL = process.env.ORKESTRATE_REGISTRY_URL || "https://orkestrate.space/api/registry";
|
|
12
|
+
|
|
13
|
+
export async function runProfileSubmit(name: string): Promise<void> {
|
|
14
|
+
const profiles = await listProfiles({ warn: true });
|
|
15
|
+
const profile = profiles.find((p: { name: string }) => p.name === name);
|
|
16
|
+
|
|
17
|
+
if (!profile) {
|
|
18
|
+
console.error(`${RED}Error:${RESET} Profile "${name}" not found`);
|
|
19
|
+
process.exitCode = 1;
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
console.log(`${BOLD}Submitting profile: ${profile.name}${RESET}`);
|
|
24
|
+
console.log("");
|
|
25
|
+
|
|
26
|
+
// Load full profile with source path
|
|
27
|
+
let fullProfile: Profile;
|
|
28
|
+
try {
|
|
29
|
+
fullProfile = await loadProfile(profile.name);
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.error(`${RED}Error:${RESET} Failed to load profile:`, error instanceof Error ? error.message : String(error));
|
|
32
|
+
process.exitCode = 1;
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Validate
|
|
37
|
+
try {
|
|
38
|
+
parseProfile(fullProfile);
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.error(`${RED}Error:${RESET} Profile validation failed:`, error instanceof Error ? error.message : String(error));
|
|
41
|
+
process.exitCode = 1;
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
console.log(`${GREEN}✓${RESET} Profile validation passed`);
|
|
46
|
+
console.log("");
|
|
47
|
+
|
|
48
|
+
// Check for existing auth token
|
|
49
|
+
const token = await getAuthToken();
|
|
50
|
+
if (!token) {
|
|
51
|
+
console.log(`${YELLOW}Authentication required${RESET}`);
|
|
52
|
+
console.log("This will open a browser window for GitHub OAuth via Supabase.");
|
|
53
|
+
console.log("");
|
|
54
|
+
|
|
55
|
+
const proceed = await confirm("Continue with authentication?");
|
|
56
|
+
if (!proceed) {
|
|
57
|
+
console.log("Cancelled.");
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Start device flow or web auth
|
|
62
|
+
const authResult = await startAuthFlow();
|
|
63
|
+
if (!authResult) {
|
|
64
|
+
console.error(`${RED}Error:${RESET} Authentication failed or cancelled`);
|
|
65
|
+
process.exitCode = 1;
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Submit to registry
|
|
71
|
+
console.log(`${BLUE}Submitting to registry...${RESET}`);
|
|
72
|
+
try {
|
|
73
|
+
const response = await fetch(`${REGISTRY_URL}/submit`, {
|
|
74
|
+
method: "POST",
|
|
75
|
+
headers: {
|
|
76
|
+
"Content-Type": "application/json",
|
|
77
|
+
"Authorization": `Bearer ${token}`,
|
|
78
|
+
},
|
|
79
|
+
body: JSON.stringify({
|
|
80
|
+
profile: fullProfile,
|
|
81
|
+
source: "cli",
|
|
82
|
+
}),
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
if (!response.ok) {
|
|
86
|
+
const error = await response.json().catch(() => ({ message: "Unknown error" }));
|
|
87
|
+
throw new Error(`Registry error: ${response.status} - ${error.message ?? "Unknown"}`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const result = await response.json();
|
|
91
|
+
console.log(`${GREEN}✓${RESET} Profile submitted successfully!`);
|
|
92
|
+
console.log("");
|
|
93
|
+
console.log(`Submission ID: ${result.submission_id}`);
|
|
94
|
+
console.log(`Status: ${result.status}`);
|
|
95
|
+
console.log(`Review URL: ${result.review_url ?? "N/A"}`);
|
|
96
|
+
console.log("");
|
|
97
|
+
console.log("The Orkestrate team will review your submission.");
|
|
98
|
+
console.log("You'll receive a notification when the review is complete.");
|
|
99
|
+
|
|
100
|
+
} catch (error) {
|
|
101
|
+
console.error(`${RED}Error:${RESET} Submission failed:`, error instanceof Error ? error.message : String(error));
|
|
102
|
+
console.log("");
|
|
103
|
+
console.log("You can also submit manually by:");
|
|
104
|
+
console.log(` 1. Go to ${BLUE}https://orkestrate.space/submit${RESET}`);
|
|
105
|
+
console.log(" 2. Sign in with GitHub");
|
|
106
|
+
console.log(" 3. Paste the profile JSON or upload the file");
|
|
107
|
+
process.exitCode = 1;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function getAuthToken(): Promise<string | null> {
|
|
112
|
+
// Check for stored token (in ~/.orkestrate/auth.json or env)
|
|
113
|
+
const authPath = `${process.env.HOME || process.env.USERPROFILE}/.orkestrate/auth.json`;
|
|
114
|
+
try {
|
|
115
|
+
const file = Bun.file(authPath);
|
|
116
|
+
if (await file.exists()) {
|
|
117
|
+
const auth = await file.json();
|
|
118
|
+
if (auth.access_token && auth.expires_at && Date.now() < auth.expires_at) {
|
|
119
|
+
return auth.access_token;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
} catch {}
|
|
123
|
+
|
|
124
|
+
// Check env var
|
|
125
|
+
if (process.env.ORKESTRATE_AUTH_TOKEN) {
|
|
126
|
+
return process.env.ORKESTRATE_AUTH_TOKEN;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async function startAuthFlow(): Promise<string | null> {
|
|
133
|
+
// Use Supabase device flow or redirect to web auth
|
|
134
|
+
console.log(`${BLUE}Opening browser for GitHub authentication...${RESET}`);
|
|
135
|
+
|
|
136
|
+
const authUrl = `${REGISTRY_URL}/auth/device`;
|
|
137
|
+
try {
|
|
138
|
+
const response = await fetch(authUrl, { method: "POST" });
|
|
139
|
+
const data = await response.json();
|
|
140
|
+
|
|
141
|
+
if (data.device_code && data.verification_uri_complete) {
|
|
142
|
+
// Open browser
|
|
143
|
+
const { spawn } = await import("node:child_process");
|
|
144
|
+
const url = data.verification_uri_complete;
|
|
145
|
+
|
|
146
|
+
let opened = false;
|
|
147
|
+
if (process.platform === "darwin") {
|
|
148
|
+
spawn("open", [url]);
|
|
149
|
+
opened = true;
|
|
150
|
+
} else if (process.platform === "win32") {
|
|
151
|
+
spawn("cmd", ["/c", "start", url]);
|
|
152
|
+
opened = true;
|
|
153
|
+
} else if (process.platform === "linux") {
|
|
154
|
+
spawn("xdg-open", [url]);
|
|
155
|
+
opened = true;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (!opened) {
|
|
159
|
+
console.log(`Please open this URL in your browser: ${BLUE}${url}${RESET}`);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
console.log(`Or visit: ${BLUE}${data.verification_uri}${RESET} and enter code: ${BOLD}${data.user_code}${RESET}`);
|
|
163
|
+
console.log("");
|
|
164
|
+
console.log("Waiting for authentication...");
|
|
165
|
+
|
|
166
|
+
// Poll for token
|
|
167
|
+
const token = await pollForToken(data.device_code, data.interval ?? 5);
|
|
168
|
+
return token;
|
|
169
|
+
}
|
|
170
|
+
} catch (error) {
|
|
171
|
+
console.error("Auth flow error:", error);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async function pollForToken(deviceCode: string, interval: number): Promise<string | null> {
|
|
178
|
+
const maxAttempts = 180 / interval; // 3 minutes max
|
|
179
|
+
|
|
180
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
181
|
+
await new Promise(r => setTimeout(r, interval * 1000));
|
|
182
|
+
|
|
183
|
+
try {
|
|
184
|
+
const response = await fetch(`${REGISTRY_URL}/auth/token`, {
|
|
185
|
+
method: "POST",
|
|
186
|
+
headers: { "Content-Type": "application/json" },
|
|
187
|
+
body: JSON.stringify({ device_code: deviceCode, grant_type: "urn:ietf:params:oauth:grant-type:device_code" }),
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
if (response.ok) {
|
|
191
|
+
const data = await response.json();
|
|
192
|
+
if (data.access_token) {
|
|
193
|
+
// Save token
|
|
194
|
+
const authPath = `${process.env.HOME || process.env.USERPROFILE}/.orkestrate/auth.json`;
|
|
195
|
+
await Bun.write(authPath, JSON.stringify({
|
|
196
|
+
access_token: data.access_token,
|
|
197
|
+
refresh_token: data.refresh_token,
|
|
198
|
+
expires_at: Date.now() + (data.expires_in * 1000),
|
|
199
|
+
}, null, 2));
|
|
200
|
+
|
|
201
|
+
console.log(`${GREEN}✓${RESET} Authentication successful!`);
|
|
202
|
+
return data.access_token;
|
|
203
|
+
}
|
|
204
|
+
} else if (response.status === 400) {
|
|
205
|
+
const error = await response.json().catch(() => ({}));
|
|
206
|
+
if (error.error === "authorization_pending") {
|
|
207
|
+
continue; // Keep polling
|
|
208
|
+
} else if (error.error === "slow_down") {
|
|
209
|
+
interval += 5;
|
|
210
|
+
continue;
|
|
211
|
+
} else if (error.error === "expired_token") {
|
|
212
|
+
console.log("Authentication expired. Please try again.");
|
|
213
|
+
return null;
|
|
214
|
+
} else if (error.error === "access_denied") {
|
|
215
|
+
console.log("Authentication denied.");
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
} catch (error) {
|
|
220
|
+
console.error("Polling error:", error);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
console.log("Authentication timed out.");
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function confirm(message: string): Promise<boolean> {
|
|
229
|
+
return new Promise(resolve => {
|
|
230
|
+
console.log(`${message} (y/N): `);
|
|
231
|
+
process.stdin.once("data", data => {
|
|
232
|
+
const input = data.toString().trim().toLowerCase();
|
|
233
|
+
resolve(input === "y" || input === "yes");
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import {
|
|
2
|
+
installCatalogPack,
|
|
3
|
+
listBundledCatalog,
|
|
4
|
+
listFullCatalog,
|
|
5
|
+
listRegistryCatalog,
|
|
6
|
+
} from "../../sdk/packs/catalog";
|
|
7
|
+
import { REGISTRY_URL } from "../../sdk/packs/registry";
|
|
8
|
+
|
|
9
|
+
export async function runRegistryList(): Promise<void> {
|
|
10
|
+
const bundled = await listBundledCatalog();
|
|
11
|
+
console.log("Bundled packs:\n");
|
|
12
|
+
for (const e of bundled) {
|
|
13
|
+
console.log(` ${e.slug} ${e.pack.description}`);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
const remote = await listRegistryCatalog();
|
|
18
|
+
if (remote.length === 0) {
|
|
19
|
+
console.log(`\nRegistry (${REGISTRY_URL}): no installable packs.`);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
console.log(`\nRegistry (${REGISTRY_URL}):\n`);
|
|
23
|
+
for (const e of remote) {
|
|
24
|
+
const tag = e.installed ? " [installed]" : "";
|
|
25
|
+
console.log(` ${e.slug}${tag} ${e.description ?? e.pack.description}`);
|
|
26
|
+
if (e.github) console.log(` ${e.github}`);
|
|
27
|
+
}
|
|
28
|
+
} catch (error) {
|
|
29
|
+
console.warn(`\nRegistry unavailable: ${error instanceof Error ? error.message : String(error)}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function runRegistrySearch(query: string): Promise<void> {
|
|
34
|
+
const q = query.toLowerCase();
|
|
35
|
+
const all = await listFullCatalog();
|
|
36
|
+
const hits = all.filter(
|
|
37
|
+
(e) =>
|
|
38
|
+
e.slug.toLowerCase().includes(q) ||
|
|
39
|
+
e.pack.id.toLowerCase().includes(q) ||
|
|
40
|
+
e.pack.description.toLowerCase().includes(q) ||
|
|
41
|
+
(e.pack.name && e.pack.name.toLowerCase().includes(q))
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
if (hits.length === 0) {
|
|
45
|
+
console.log(`No packs matching "${query}".`);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
for (const e of hits) {
|
|
50
|
+
const src = e.source === "registry" ? "registry" : "bundled";
|
|
51
|
+
console.log(`${e.slug} (${src})${e.installed ? " [installed]" : ""}`);
|
|
52
|
+
console.log(` ${e.pack.description}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export async function runRegistryInstall(
|
|
57
|
+
slug: string,
|
|
58
|
+
options?: { global?: boolean; overwrite?: boolean }
|
|
59
|
+
): Promise<void> {
|
|
60
|
+
const pack = await installCatalogPack(slug, {
|
|
61
|
+
target: options?.global ? "global" : "workspace",
|
|
62
|
+
overwrite: options?.overwrite,
|
|
63
|
+
});
|
|
64
|
+
console.log(`Installed pack: ${pack.id}`);
|
|
65
|
+
console.log(` ${pack.packRoot}`);
|
|
66
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { launchPack, stopRun } from "../../sdk/launch/broker";
|
|
2
|
+
import { getRun, listRuns, reconcileRuns } from "../../sdk/runs/registry";
|
|
3
|
+
|
|
4
|
+
export async function runLaunch(packId: string): Promise<void> {
|
|
5
|
+
const run = await launchPack(packId);
|
|
6
|
+
console.log(`runId=${run.id}`);
|
|
7
|
+
console.log(`pack=${run.packId}`);
|
|
8
|
+
console.log(`state=${run.state}`);
|
|
9
|
+
console.log(`Opened new terminal: ${run.title}`);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export async function runSpawn(packId: string): Promise<void> {
|
|
13
|
+
await runLaunch(packId);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function runList(): Promise<void> {
|
|
17
|
+
await reconcileRuns();
|
|
18
|
+
const runs = await listRuns();
|
|
19
|
+
if (runs.length === 0) {
|
|
20
|
+
console.log("No runs.");
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
for (const run of runs) {
|
|
24
|
+
console.log(`${run.id} ${run.packId} ${run.state} ${run.startedAt}`);
|
|
25
|
+
if (run.pid) console.log(` pid=${run.pid}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function runStatus(runId: string): Promise<void> {
|
|
30
|
+
const run = await getRun(runId);
|
|
31
|
+
console.log(JSON.stringify(run, null, 2));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function runStop(runId: string): Promise<void> {
|
|
35
|
+
const run = await stopRun(runId);
|
|
36
|
+
console.log(`Stopped ${run.id} (${run.state})`);
|
|
37
|
+
}
|
package/src/cli/index.ts
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { loadExtensions } from "../sdk/extensions/loader";
|
|
2
|
+
import { runTui } from "./tui";
|
|
3
|
+
import { listInstalledPacks } from "../sdk/packs/catalog";
|
|
4
|
+
import { getAdapter } from "../sdk/registry";
|
|
5
|
+
import { VERSION } from "../version";
|
|
6
|
+
|
|
7
|
+
await loadExtensions();
|
|
8
|
+
|
|
9
|
+
const command = Bun.argv[2];
|
|
10
|
+
|
|
11
|
+
if (!command) {
|
|
12
|
+
await runTui();
|
|
13
|
+
} else if (command === "doctor") {
|
|
14
|
+
const installed = await listInstalledPacks();
|
|
15
|
+
console.log(`Orkestrate Doctor v${VERSION}`);
|
|
16
|
+
console.log(`Packs: ${installed.length}`);
|
|
17
|
+
for (const { pack } of installed) {
|
|
18
|
+
const adapter = getAdapter(pack.harness);
|
|
19
|
+
const status = adapter ? await adapter.detect() : { installed: false, error: "no driver" };
|
|
20
|
+
console.log(` ${pack.id}: ${pack.harness} (${status.installed ? "ok" : status.error ?? "missing"})`);
|
|
21
|
+
}
|
|
22
|
+
} else if (command === "pack") {
|
|
23
|
+
const sub = Bun.argv[3];
|
|
24
|
+
if (sub === "list") {
|
|
25
|
+
const { runPackList } = await import("./cmd/pack");
|
|
26
|
+
await runPackList();
|
|
27
|
+
} else if (sub === "install") {
|
|
28
|
+
const slug = Bun.argv[4];
|
|
29
|
+
if (!slug) {
|
|
30
|
+
console.error("Usage: orkestrate pack install <slug> [--global]");
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
const { runPackInstall } = await import("./cmd/pack");
|
|
34
|
+
await runPackInstall(slug, Bun.argv.includes("--global"));
|
|
35
|
+
} else if (sub === "validate") {
|
|
36
|
+
const id = Bun.argv[4];
|
|
37
|
+
if (!id) {
|
|
38
|
+
console.error("Usage: orkestrate pack validate <pack-id>");
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
const { runPackValidate } = await import("./cmd/pack");
|
|
42
|
+
await runPackValidate(id);
|
|
43
|
+
} else if (sub === "create") {
|
|
44
|
+
const id = Bun.argv[4];
|
|
45
|
+
if (!id) {
|
|
46
|
+
console.error("Usage: orkestrate pack create <pack-id> [--from coding] [--description \"...\"] [--global]");
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
const fromIdx = Bun.argv.indexOf("--from");
|
|
50
|
+
const descIdx = Bun.argv.indexOf("--description");
|
|
51
|
+
const { runPackCreate } = await import("./cmd/pack-create");
|
|
52
|
+
await runPackCreate(id, {
|
|
53
|
+
template: fromIdx >= 0 ? Bun.argv[fromIdx + 1] : undefined,
|
|
54
|
+
description: descIdx >= 0 ? Bun.argv[descIdx + 1] : undefined,
|
|
55
|
+
global: Bun.argv.includes("--global"),
|
|
56
|
+
});
|
|
57
|
+
} else {
|
|
58
|
+
console.error("Usage: orkestrate pack <list|install|validate|create>");
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
} else if (command === "run") {
|
|
62
|
+
const sub = Bun.argv[3];
|
|
63
|
+
if (sub === "launch" || sub === "spawn") {
|
|
64
|
+
const packId = Bun.argv[4];
|
|
65
|
+
if (!packId) {
|
|
66
|
+
console.error(`Usage: orkestrate run ${sub} <pack-id>`);
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
const { runLaunch, runSpawn } = await import("./cmd/run");
|
|
70
|
+
if (sub === "spawn") await runSpawn(packId);
|
|
71
|
+
else await runLaunch(packId);
|
|
72
|
+
} else if (sub === "list") {
|
|
73
|
+
const { runList } = await import("./cmd/run");
|
|
74
|
+
await runList();
|
|
75
|
+
} else if (sub === "status") {
|
|
76
|
+
const id = Bun.argv[4];
|
|
77
|
+
if (!id) {
|
|
78
|
+
console.error("Usage: orkestrate run status <run-id>");
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
const { runStatus } = await import("./cmd/run");
|
|
82
|
+
await runStatus(id);
|
|
83
|
+
} else if (sub === "stop") {
|
|
84
|
+
const id = Bun.argv[4];
|
|
85
|
+
if (!id) {
|
|
86
|
+
console.error("Usage: orkestrate run stop <run-id>");
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
const { runStop } = await import("./cmd/run");
|
|
90
|
+
await runStop(id);
|
|
91
|
+
} else {
|
|
92
|
+
console.error("Usage: orkestrate run <launch|spawn|list|status|stop>");
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
} else if (command === "profile") {
|
|
96
|
+
const sub = Bun.argv[3];
|
|
97
|
+
if (sub === "validate") {
|
|
98
|
+
const name = Bun.argv[4];
|
|
99
|
+
if (!name) {
|
|
100
|
+
console.error("Usage: orkestrate profile validate <pack-id>");
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
const { runPackValidate } = await import("./cmd/pack");
|
|
104
|
+
await runPackValidate(name);
|
|
105
|
+
} else if (sub === "create") {
|
|
106
|
+
console.error("Use: orkestrate pack create <pack-id> [--from coding] [--global]");
|
|
107
|
+
process.exit(1);
|
|
108
|
+
} else {
|
|
109
|
+
console.error("Profile commands are deprecated. Use: orkestrate pack … / orkestrate run …");
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
} else if (command === "registry") {
|
|
113
|
+
const subCommand = Bun.argv[3];
|
|
114
|
+
if (subCommand === "list") {
|
|
115
|
+
const { runRegistryList } = await import("./cmd/registry");
|
|
116
|
+
await runRegistryList();
|
|
117
|
+
} else if (subCommand === "search") {
|
|
118
|
+
const query = Bun.argv[4];
|
|
119
|
+
if (!query) {
|
|
120
|
+
console.error("Usage: orkestrate registry search <query>");
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
const { runRegistrySearch } = await import("./cmd/registry");
|
|
124
|
+
await runRegistrySearch(query);
|
|
125
|
+
} else if (subCommand === "install") {
|
|
126
|
+
const slug = Bun.argv[4];
|
|
127
|
+
if (!slug) {
|
|
128
|
+
console.error("Usage: orkestrate registry install <slug> [--global] [--overwrite]");
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
const { runRegistryInstall } = await import("./cmd/registry");
|
|
132
|
+
await runRegistryInstall(slug, {
|
|
133
|
+
global: Bun.argv.includes("--global"),
|
|
134
|
+
overwrite: Bun.argv.includes("--overwrite"),
|
|
135
|
+
});
|
|
136
|
+
} else {
|
|
137
|
+
console.error("Usage: orkestrate registry <list|search|install>");
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
} else if (command === "extension" && Bun.argv[3] === "validate") {
|
|
141
|
+
const target = Bun.argv[4];
|
|
142
|
+
if (!target) {
|
|
143
|
+
console.error("Usage: orkestrate extension validate <path>");
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
146
|
+
const { resolve } = await import("node:path");
|
|
147
|
+
try {
|
|
148
|
+
const mod = await import(resolve(target));
|
|
149
|
+
const ext = mod.default || mod.extension || mod;
|
|
150
|
+
console.log("Extension valid:", ext.id, ext.version);
|
|
151
|
+
} catch (error) {
|
|
152
|
+
console.error(error);
|
|
153
|
+
process.exit(1);
|
|
154
|
+
}
|
|
155
|
+
} else {
|
|
156
|
+
console.log(`Orkestrate v${VERSION}`);
|
|
157
|
+
console.log("Commands:");
|
|
158
|
+
console.log(" orkestrate TUI workbench");
|
|
159
|
+
console.log(" orkestrate pack list|install|validate|create");
|
|
160
|
+
console.log(" orkestrate registry list|search|install");
|
|
161
|
+
console.log(" orkestrate run launch|spawn|list|status|stop");
|
|
162
|
+
console.log(" orkestrate doctor");
|
|
163
|
+
}
|