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.
Files changed (73) hide show
  1. package/AGENTS.md +56 -0
  2. package/CONTRIBUTING.md +35 -0
  3. package/README.md +38 -58
  4. package/SECURITY.md +24 -0
  5. package/bin/orkestrate.ts +2 -0
  6. package/docs/concepts.md +119 -0
  7. package/docs/demo-extension-builder.md +82 -0
  8. package/docs/extensions/adapters.md +57 -0
  9. package/docs/extensions/architecture.md +49 -0
  10. package/docs/extensions/introduction.md +26 -0
  11. package/docs/getting-started.md +85 -0
  12. package/docs/hosted-registry.md +90 -0
  13. package/docs/pack-authoring.md +75 -0
  14. package/docs/roadmap.md +59 -0
  15. package/docs/troubleshooting.md +28 -0
  16. package/extensions/opencode-adapter/index.ts +106 -0
  17. package/extensions/opencode-adapter/orkestrate.extension.json +17 -0
  18. package/package.json +40 -33
  19. package/packs/coding/harnesses/opencode/agents/coding.md +8 -0
  20. package/packs/coding/harnesses/opencode/opencode.json +24 -0
  21. package/packs/coding/harnesses/opencode/skills/orkestrate/SKILL.md +57 -0
  22. package/packs/coding/pack.yaml +5 -0
  23. package/packs/extension-builder/harnesses/opencode/agents/extension-builder.md +8 -0
  24. package/packs/extension-builder/harnesses/opencode/opencode.json +31 -0
  25. package/packs/extension-builder/harnesses/opencode/skills/orkestrate/SKILL.md +54 -0
  26. package/packs/extension-builder/harnesses/opencode/skills/orkestrate-pack-author/SKILL.md +59 -0
  27. package/packs/extension-builder/pack.yaml +5 -0
  28. package/src/cli/cmd/extension-submit.ts +267 -0
  29. package/src/cli/cmd/pack-create.ts +43 -0
  30. package/src/cli/cmd/pack.ts +53 -0
  31. package/src/cli/cmd/profile-create.ts +199 -0
  32. package/src/cli/cmd/profile-submit.ts +236 -0
  33. package/src/cli/cmd/profile-validate.ts +5 -0
  34. package/src/cli/cmd/registry.ts +66 -0
  35. package/src/cli/cmd/run.ts +37 -0
  36. package/src/cli/index.ts +163 -0
  37. package/src/cli/tui.ts +355 -0
  38. package/src/cli/ui/welcome.ts +73 -0
  39. package/src/cli.ts +1 -0
  40. package/src/sdk/cross-platform.ts +25 -0
  41. package/src/sdk/extensions/loader.ts +89 -0
  42. package/src/sdk/extensions/manifest.ts +193 -0
  43. package/src/sdk/extensions/types.ts +12 -0
  44. package/src/sdk/harness/sync-slice.ts +57 -0
  45. package/src/sdk/launch/broker.ts +87 -0
  46. package/src/sdk/launch/runner.ts +57 -0
  47. package/src/sdk/launch/terminal.ts +75 -0
  48. package/src/sdk/launch/types.ts +7 -0
  49. package/src/sdk/launch/windows.ts +109 -0
  50. package/src/sdk/packs/catalog.ts +172 -0
  51. package/src/sdk/packs/create.ts +99 -0
  52. package/src/sdk/packs/fs.ts +52 -0
  53. package/src/sdk/packs/github.ts +249 -0
  54. package/src/sdk/packs/paths.ts +19 -0
  55. package/src/sdk/packs/registry.ts +40 -0
  56. package/src/sdk/packs/schema.ts +51 -0
  57. package/src/sdk/packs/store.ts +172 -0
  58. package/src/sdk/profiles/catalog.ts +199 -0
  59. package/src/sdk/profiles/github.ts +177 -0
  60. package/src/sdk/profiles/install.ts +161 -0
  61. package/src/sdk/profiles/load.ts +209 -0
  62. package/src/sdk/profiles/materialize.ts +85 -0
  63. package/src/sdk/profiles/pack.ts +128 -0
  64. package/src/sdk/profiles/schema.ts +201 -0
  65. package/src/sdk/registry.ts +19 -0
  66. package/src/sdk/runs/registry.ts +142 -0
  67. package/src/sdk/runs/types.ts +15 -0
  68. package/src/sdk/types.ts +39 -0
  69. package/src/version.ts +3 -0
  70. package/dist/cli.js +0 -1668
  71. package/dist/cli.js.map +0 -1
  72. package/dist/mcp-entry.js +0 -181
  73. package/dist/mcp-entry.js.map +0 -1
@@ -0,0 +1,142 @@
1
+ import { mkdir, readdir, readFile, writeFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { randomBytes } from "node:crypto";
4
+ import type { RunRecord, RunState } from "./types";
5
+ import { workspaceRunsDir } from "../packs/paths";
6
+
7
+ const STARTING_GRACE_MS = 12_000;
8
+
9
+ export function runDir(runId: string): string {
10
+ return join(workspaceRunsDir, runId);
11
+ }
12
+
13
+ export function isProcessAlive(pid: number): boolean {
14
+ try {
15
+ process.kill(pid, 0);
16
+ return true;
17
+ } catch (error) {
18
+ const code = (error as NodeJS.ErrnoException).code;
19
+ return code === "EPERM";
20
+ }
21
+ }
22
+
23
+ function startedMs(run: RunRecord): number {
24
+ return Date.parse(run.startedAt) || 0;
25
+ }
26
+
27
+ function ageMs(run: RunRecord): number {
28
+ return Date.now() - startedMs(run);
29
+ }
30
+
31
+ /** Whether this run should show as active in the TUI. */
32
+ export function isActiveRun(run: RunRecord): boolean {
33
+ if (run.state === "running") {
34
+ return run.pid != null && isProcessAlive(run.pid);
35
+ }
36
+ if (run.state === "starting") {
37
+ return ageMs(run) < STARTING_GRACE_MS;
38
+ }
39
+ return false;
40
+ }
41
+
42
+ export function activeRunsForPack(runs: RunRecord[], packId: string): RunRecord[] {
43
+ return runs.filter((r) => r.packId === packId && isActiveRun(r));
44
+ }
45
+
46
+ /** Mark stale or dead runs so the TUI can show idle accurately. */
47
+ export async function reconcileRuns(): Promise<void> {
48
+ const runs = await listRuns();
49
+ const now = new Date().toISOString();
50
+
51
+ for (const run of runs) {
52
+ if (run.state === "starting") {
53
+ if (run.pid && isProcessAlive(run.pid)) continue;
54
+ if (!run.pid && ageMs(run) < STARTING_GRACE_MS) continue;
55
+ await updateRun(run.id, {
56
+ state: "failed",
57
+ endedAt: now,
58
+ });
59
+ continue;
60
+ }
61
+
62
+ if (run.state === "running") {
63
+ if (run.pid && isProcessAlive(run.pid)) continue;
64
+ await updateRun(run.id, {
65
+ state: "exited",
66
+ endedAt: run.endedAt ?? now,
67
+ });
68
+ }
69
+ }
70
+ }
71
+
72
+ export function runRecordPath(runId: string): string {
73
+ return join(runDir(runId), "run.json");
74
+ }
75
+
76
+ export function newRunId(): string {
77
+ return randomBytes(4).toString("hex");
78
+ }
79
+
80
+ export async function ensureRunsDir(): Promise<void> {
81
+ await mkdir(workspaceRunsDir, { recursive: true });
82
+ }
83
+
84
+ export async function createRun(input: {
85
+ packId: string;
86
+ harness: string;
87
+ cwd: string;
88
+ title: string;
89
+ runId?: string;
90
+ }): Promise<RunRecord> {
91
+ await ensureRunsDir();
92
+ const id = input.runId ?? newRunId();
93
+ const dir = runDir(id);
94
+ await mkdir(dir, { recursive: true });
95
+ const record: RunRecord = {
96
+ id,
97
+ packId: input.packId,
98
+ harness: input.harness,
99
+ cwd: input.cwd,
100
+ state: "starting",
101
+ startedAt: new Date().toISOString(),
102
+ title: input.title,
103
+ terminalMode: "window",
104
+ };
105
+ await writeFile(runRecordPath(id), JSON.stringify(record, null, 2) + "\n");
106
+ return record;
107
+ }
108
+
109
+ export async function updateRun(runId: string, patch: Partial<RunRecord>): Promise<RunRecord> {
110
+ const record = await getRun(runId);
111
+ const next = { ...record, ...patch };
112
+ await writeFile(runRecordPath(runId), JSON.stringify(next, null, 2) + "\n");
113
+ return next;
114
+ }
115
+
116
+ export async function getRun(runId: string): Promise<RunRecord> {
117
+ const text = await readFile(runRecordPath(runId), "utf-8");
118
+ return JSON.parse(text) as RunRecord;
119
+ }
120
+
121
+ export async function listRuns(): Promise<RunRecord[]> {
122
+ await ensureRunsDir();
123
+ let names: string[] = [];
124
+ try {
125
+ names = await readdir(workspaceRunsDir);
126
+ } catch {
127
+ return [];
128
+ }
129
+ const records: RunRecord[] = [];
130
+ for (const name of names) {
131
+ try {
132
+ records.push(await getRun(name));
133
+ } catch {
134
+ // skip corrupt
135
+ }
136
+ }
137
+ return records.sort((a, b) => b.startedAt.localeCompare(a.startedAt));
138
+ }
139
+
140
+ export async function setRunState(runId: string, state: RunState, extra?: Partial<RunRecord>): Promise<void> {
141
+ await updateRun(runId, { state, ...extra });
142
+ }
@@ -0,0 +1,15 @@
1
+ export type RunState = "starting" | "running" | "exited" | "failed" | "stopped";
2
+
3
+ export type RunRecord = {
4
+ id: string;
5
+ packId: string;
6
+ harness: string;
7
+ cwd: string;
8
+ state: RunState;
9
+ pid?: number;
10
+ startedAt: string;
11
+ endedAt?: string;
12
+ exitCode?: number;
13
+ title: string;
14
+ terminalMode: "window";
15
+ };
@@ -0,0 +1,39 @@
1
+ import type { Pack } from "./packs/schema";
2
+
3
+ export interface HarnessStatus {
4
+ installed: boolean;
5
+ version?: string;
6
+ path?: string;
7
+ error?: string;
8
+ }
9
+
10
+ export interface CompileContext {
11
+ cwd: string;
12
+ runId: string;
13
+ /** Persistent harness home for this pack (shared across runs of the same pack). */
14
+ packHome: string;
15
+ crossPlatform: CrossPlatformUtility;
16
+ }
17
+
18
+ export interface LaunchPlan {
19
+ command: string;
20
+ args: string[];
21
+ cwd: string;
22
+ env: Record<string, string>;
23
+ title: string;
24
+ }
25
+
26
+ export interface CrossPlatformUtility {
27
+ hijackHome(fakeHomePath: string): Record<string, string>;
28
+ }
29
+
30
+ /** Harness driver: compile pack + run home → launch plan (core spawns). */
31
+ export interface HarnessDriver {
32
+ id: string;
33
+ name: string;
34
+ detect(): Promise<HarnessStatus>;
35
+ compile(pack: Pack, context: CompileContext): Promise<LaunchPlan>;
36
+ }
37
+
38
+ /** @deprecated use HarnessDriver */
39
+ export type HarnessAdapter = HarnessDriver;
package/src/version.ts ADDED
@@ -0,0 +1,3 @@
1
+ import pkg from "../package.json" with { type: "json" };
2
+
3
+ export const VERSION = pkg.version;