openkrew 0.0.1 → 0.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/LICENSE ADDED
@@ -0,0 +1,75 @@
1
+ License text copyright (c) 2020 MariaDB Corporation Ab, All Rights Reserved.
2
+ "Business Source License" is a trademark of MariaDB Corporation Ab.
3
+
4
+ Parameters
5
+
6
+ Licensor: OpenKrew (Timothy Carter)
7
+ Licensed Work: OpenKrew. The Licensed Work is (c) 2026 OpenKrew.
8
+ Additional Use Grant: You may make use of the Licensed Work for any
9
+ non-commercial purpose. You may also make production
10
+ use of the Licensed Work for internal purposes within
11
+ your organization. Your organization includes all of
12
+ your affiliates under common control.
13
+
14
+ You may NOT offer the Licensed Work to third parties
15
+ on a hosted or embedded basis as a commercial product
16
+ or service that competes with the Licensed Work.
17
+
18
+ For purposes of this license:
19
+
20
+ "Non-commercial" means personal, educational,
21
+ research, evaluation, or non-profit use.
22
+
23
+ "Internal purposes" means use by your employees and
24
+ contractors for the benefit of your organization,
25
+ and not for the benefit of third parties.
26
+ Change Date: March 27, 2029
27
+ Change License: Apache License, Version 2.0
28
+
29
+ For information about alternative licensing arrangements for the Licensed Work,
30
+ please contact licensing@openkrew.com.
31
+
32
+ Notice
33
+
34
+ Business Source License 1.1
35
+
36
+ Terms
37
+
38
+ The Licensor hereby grants you the right to copy, modify, create derivative
39
+ works, redistribute, and make non-production use of the Licensed Work. The
40
+ Licensor may make an Additional Use Grant, above, permitting limited production use.
41
+
42
+ Effective on the Change Date, or the third anniversary of the first publicly
43
+ available distribution of a specific version of the Licensed Work under this
44
+ License, whichever comes first, the Licensor hereby grants you rights under
45
+ the terms of the Change License, and the rights granted in the paragraph
46
+ above terminate.
47
+
48
+ If your use of the Licensed Work does not comply with the requirements
49
+ currently in effect as described in this License, you must purchase a
50
+ commercial license from the Licensor, its affiliated entities, or authorized
51
+ resellers, or you must refrain from using the Licensed Work.
52
+
53
+ All copies of the original and modified Licensed Work, and derivative works
54
+ of the Licensed Work, are subject to this License. This License applies
55
+ separately for each version of the Licensed Work and the Change Date may vary
56
+ for each version of the Licensed Work released by Licensor.
57
+
58
+ You must conspicuously display this License on each original or modified copy
59
+ of the Licensed Work. If you receive the Licensed Work in original or
60
+ modified form from a third party, the terms and conditions set forth in this
61
+ License apply to your use of that work.
62
+
63
+ Any use of the Licensed Work in violation of this License will automatically
64
+ terminate your rights under this License for the current and all other
65
+ versions of the Licensed Work.
66
+
67
+ This License does not grant you any right in any trademark or logo of
68
+ Licensor or its affiliates (provided that you may use a trademark or logo of
69
+ Licensor as expressly required by this License).
70
+
71
+ TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
72
+ AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
73
+ EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
74
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
75
+ TITLE.
package/README.md ADDED
@@ -0,0 +1,148 @@
1
+ # OpenKrew
2
+
3
+ <p align="center">
4
+ <img src="docs/assets/openkrew-logo.png" alt="OpenKrew" width="500">
5
+ </p>
6
+
7
+ <p align="center">
8
+ <strong>AI agents on separate machines, working as a team.</strong>
9
+ </p>
10
+
11
+ <p align="center">
12
+ <a href="https://github.com/OpenKrew/openkrew/actions"><img src="https://img.shields.io/github/actions/workflow/status/OpenKrew/openkrew/ci.yml?branch=main&style=for-the-badge" alt="CI status"></a>
13
+ <a href="https://github.com/OpenKrew/openkrew/releases"><img src="https://img.shields.io/github/v/release/OpenKrew/openkrew?include_prereleases&style=for-the-badge" alt="GitHub release"></a>
14
+ <a href="LICENSE"><img src="https://img.shields.io/badge/License-BSL_1.1-blue.svg?style=for-the-badge" alt="BSL 1.1 License"></a>
15
+ </p>
16
+
17
+ ---
18
+
19
+ OpenKrew runs named AI agents on different physical machines and lets them work together on projects. Each agent has its own identity, role, and permissions. They coordinate through Slack and a central Hub API.
20
+
21
+ Most multi-agent frameworks run everything in one process. OpenKrew doesn't. Your agents are actually distributed, each on its own box, talking to each other over real channels. Think less "threads sharing memory" and more "remote coworkers in a Slack workspace."
22
+
23
+ [Project Vision](PROJECT_VISION.md)
24
+
25
+ ## How it works
26
+
27
+ ```
28
+ Primary Machine (Hub) -- Always On
29
+ ├── Agent Registry API (:4000)
30
+ ├── Shared Memory DB (PostgreSQL + pgvector)
31
+ ├── Task Queue (Redis / BullMQ)
32
+ ├── Provisioning Service (SSH)
33
+ └── Agent: "Quinn" -- Dev Lead
34
+
35
+ Spoke 1 (e.g., MacBook Mini)
36
+ └── Agent: "Morgan" -- Frontend Developer
37
+
38
+ Spoke 2 (e.g., Linux Server)
39
+ └── Agent: "Reese" -- Infrastructure / DevOps
40
+ ```
41
+
42
+ The Hub runs the registry, shared memory, and task queue. Spokes are remote machines, each running one agent. Agents talk through Slack for conversation and the Hub API for structured handoffs.
43
+
44
+ ## What's in the box
45
+
46
+ Agents run on separate machines, not threads. Each one has a name, role, and personality defined in YAML. They share a three tier memory system: personal (local files), team (PostgreSQL + pgvector), and project scoped. You set per-agent permissions for shell access, filesystem, git, packages, and secrets.
47
+
48
+ Slack is the communication layer. Works with Claude, GPT, DeepSeek, Ollama, LM Studio, and others. SSH provisioning handles remote setup. A privilege broker lets agents request elevated access without holding root credentials directly.
49
+
50
+ ## Agent config
51
+
52
+ Each agent gets a YAML file:
53
+
54
+ ```yaml
55
+ agent:
56
+ name: Quinn
57
+ role: dev-lead
58
+ machine: mac-mini-lan
59
+ model: claude-opus-4-6
60
+ teammates:
61
+ - name: Morgan
62
+ role: frontend
63
+ channel: "#frontend"
64
+ - name: Reese
65
+ role: infra
66
+ channel: "#infrastructure"
67
+ tools: [git, gh, dotnet-cli, docker]
68
+ mcps: [context7, sequential-thinking]
69
+ ```
70
+
71
+ ## Permissions
72
+
73
+ You control what each agent can do. Morgan here can push code and open PRs but can't merge, can't sudo, and can't touch anything outside the UI and docs folders:
74
+
75
+ ```yaml
76
+ agent: Morgan
77
+ role: frontend
78
+
79
+ shell:
80
+ enabled: true
81
+ sudo: false
82
+ blocked_commands: [rm -rf /, shutdown, reboot]
83
+
84
+ filesystem:
85
+ mode: scoped
86
+ allowed_paths:
87
+ - ~/projects/my-app/UI/**
88
+ - ~/projects/my-app/docs/**
89
+
90
+ git:
91
+ push: true
92
+ create_pr: true
93
+ merge_pr: false # Only lead can merge
94
+ force_push: false
95
+
96
+ packages:
97
+ install: true
98
+ managers: [npm, npx]
99
+
100
+ secrets:
101
+ access: false
102
+ ```
103
+
104
+ ## Memory
105
+
106
+ | Tier | Scope | Storage | Example |
107
+ | -------- | --------------- | --------------------- | --------------------------------------------- |
108
+ | Personal | One agent | Local filesystem | "I prefer TDD" |
109
+ | Team | All agents | PostgreSQL + pgvector | "API uses .NET 10, frontend is Next.js 14" |
110
+ | Project | Current project | Shared DB, scoped | "Stage 2 of auth migration, blocked on OAuth" |
111
+
112
+ ## Tech stack
113
+
114
+ | Component | Technology |
115
+ | ---------------- | ------------------------------------------- |
116
+ | Hub API | Node.js (Fastify) or .NET, REST + WebSocket |
117
+ | Shared memory | PostgreSQL + pgvector |
118
+ | Task queue | Redis / BullMQ |
119
+ | Agent runtime | Node.js (Pi agent framework) |
120
+ | Messaging | Slack (via Slack Bolt) |
121
+ | SSH provisioning | node-ssh |
122
+ | LLM support | Model agnostic, 12+ providers |
123
+
124
+ ## Building from source
125
+
126
+ Needs Node 22.16+ (24 recommended).
127
+
128
+ ```bash
129
+ git clone https://github.com/OpenKrew/openkrew.git
130
+ cd openkrew
131
+
132
+ pnpm install
133
+ pnpm build
134
+ ```
135
+
136
+ ## Roadmap
137
+
138
+ We're starting with the basics: get two agents talking on one machine, nail the communication and memory protocols. After that, SSH provisioning for actual multi machine setups, then the interactive setup wizard, then the permission system. Agent to agent coordination (handoffs, conflict resolution) comes last. Then we open source it properly.
139
+
140
+ ## Origin
141
+
142
+ OpenKrew started as a fork of [OpenClaw](https://github.com/openclaw/openclaw) (MIT). We kept the Gateway, Pi agent framework, LLM adapters, and Slack integration. Everything else -- multi machine distribution, shared memory, role based permissions, team coordination -- is new.
143
+
144
+ See [THIRD_PARTY_LICENSES.md](THIRD_PARTY_LICENSES.md) for attribution.
145
+
146
+ ## License
147
+
148
+ [Business Source License 1.1](LICENSE) -- free for non-commercial and internal use. Converts to Apache 2.0 after 3 years per version. Commercial licensing available at licensing@openkrew.com.
@@ -0,0 +1,256 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * OpenKrew .NET Core Bootstrapper
5
+ *
6
+ * Downloads and manages the .NET runtime binaries for the OpenKrew platform.
7
+ * On first run, downloads the platform-specific self-contained binary from
8
+ * GitHub Releases. Subsequent runs use the cached binary directly.
9
+ *
10
+ * This file is invoked by openkrew.mjs when the user runs `openkrew`.
11
+ */
12
+
13
+ import { spawn } from "node:child_process";
14
+ import {
15
+ createWriteStream,
16
+ existsSync,
17
+ mkdirSync,
18
+ chmodSync,
19
+ readFileSync,
20
+ writeFileSync,
21
+ rmSync,
22
+ } from "node:fs";
23
+ import { homedir, platform, arch } from "node:os";
24
+ import { join, dirname } from "node:path";
25
+ import { pipeline } from "node:stream/promises";
26
+
27
+ const OPENKREW_DIR = join(homedir(), ".openkrew");
28
+ const BIN_DIR = join(OPENKREW_DIR, "bin");
29
+ const VERSION_FILE = join(BIN_DIR, ".version");
30
+ const GITHUB_REPO = "OpenKrew/openkrew";
31
+ const PACKAGE_VERSION = (() => {
32
+ try {
33
+ const pkg = JSON.parse(readFileSync(new URL("./package.json", import.meta.url), "utf8"));
34
+ return pkg.version;
35
+ } catch {
36
+ return "0.1.0";
37
+ }
38
+ })();
39
+
40
+ /**
41
+ * Resolves the platform-specific runtime identifier for .NET publishing.
42
+ */
43
+ function getRuntimeId() {
44
+ const p = platform();
45
+ const a = arch();
46
+
47
+ if (p === "darwin" && a === "arm64") {
48
+ return "osx-arm64";
49
+ }
50
+ if (p === "darwin" && a === "x64") {
51
+ return "osx-x64";
52
+ }
53
+ if (p === "linux" && a === "x64") {
54
+ return "linux-x64";
55
+ }
56
+ if (p === "linux" && a === "arm64") {
57
+ return "linux-arm64";
58
+ }
59
+ if (p === "win32" && a === "x64") {
60
+ return "win-x64";
61
+ }
62
+ if (p === "win32" && a === "arm64") {
63
+ return "win-arm64";
64
+ }
65
+
66
+ throw new Error(
67
+ `Unsupported platform: ${p}-${a}. OpenKrew supports: macOS (arm64/x64), Linux (x64/arm64), Windows (x64/arm64).`,
68
+ );
69
+ }
70
+
71
+ /**
72
+ * Returns the expected binary name for the current platform.
73
+ */
74
+ function getBinaryName() {
75
+ return platform() === "win32" ? "OpenKrew.Hub.exe" : "OpenKrew.Hub";
76
+ }
77
+
78
+ /**
79
+ * Checks if the binary is already downloaded and matches the expected version.
80
+ */
81
+ function isInstalled() {
82
+ const binaryPath = join(BIN_DIR, getBinaryName());
83
+ if (!existsSync(binaryPath)) {
84
+ return false;
85
+ }
86
+
87
+ try {
88
+ const installedVersion = readFileSync(VERSION_FILE, "utf8").trim();
89
+ return installedVersion === PACKAGE_VERSION;
90
+ } catch {
91
+ return false;
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Downloads the self-contained .NET binary from GitHub Releases.
97
+ */
98
+ async function downloadBinary() {
99
+ const rid = getRuntimeId();
100
+ const tag = `v${PACKAGE_VERSION}`;
101
+ const assetName = `openkrew-${rid}.tar.gz`;
102
+ const url = `https://github.com/${GITHUB_REPO}/releases/download/${tag}/${assetName}`;
103
+
104
+ process.stderr.write(`\x1b[36mOpenKrew\x1b[0m: downloading ${rid} binary (${tag})...\n`);
105
+
106
+ mkdirSync(BIN_DIR, { recursive: true });
107
+
108
+ // Download and extract
109
+ const response = await fetch(url);
110
+
111
+ if (!response.ok) {
112
+ // If the release doesn't exist yet, fall back to building from source
113
+ if (response.status === 404) {
114
+ process.stderr.write(`\x1b[33mRelease ${tag} not found on GitHub.\x1b[0m\n`);
115
+ process.stderr.write("Attempting to build from source...\n");
116
+ return await buildFromSource();
117
+ }
118
+ throw new Error(`Failed to download binary: ${response.status} ${response.statusText}`);
119
+ }
120
+
121
+ const tmpFile = join(OPENKREW_DIR, `download-${Date.now()}.tar.gz`);
122
+
123
+ try {
124
+ const fileStream = createWriteStream(tmpFile);
125
+ await pipeline(response.body, fileStream);
126
+
127
+ // Extract using tar command (available on all platforms)
128
+ const tarProcess = spawn("tar", ["xzf", tmpFile, "-C", BIN_DIR], { stdio: "inherit" });
129
+ await new Promise((resolve, reject) => {
130
+ tarProcess.on("close", (code) =>
131
+ code === 0 ? resolve() : reject(new Error(`tar exited with code ${code}`)),
132
+ );
133
+ tarProcess.on("error", reject);
134
+ });
135
+
136
+ // Make binary executable on Unix
137
+ if (platform() !== "win32") {
138
+ const binaryPath = join(BIN_DIR, getBinaryName());
139
+ if (existsSync(binaryPath)) {
140
+ chmodSync(binaryPath, 0o755);
141
+ }
142
+ }
143
+
144
+ // Write version marker
145
+ writeFileSync(VERSION_FILE, PACKAGE_VERSION);
146
+
147
+ process.stderr.write(`\x1b[32mInstalled OpenKrew ${PACKAGE_VERSION}\x1b[0m\n`);
148
+ } finally {
149
+ try {
150
+ rmSync(tmpFile);
151
+ } catch {}
152
+ }
153
+ }
154
+
155
+ /**
156
+ * Fallback: build from source if binaries aren't available on GitHub Releases.
157
+ * Requires .NET 10 SDK to be installed.
158
+ */
159
+ async function buildFromSource() {
160
+ // Check if dotnet is available
161
+ try {
162
+ const dotnetCheck = spawn("dotnet", ["--version"], { stdio: "pipe" });
163
+ const version = await new Promise((resolve, reject) => {
164
+ let output = "";
165
+ dotnetCheck.stdout.on("data", (d) => (output += d));
166
+ dotnetCheck.on("close", (code) => (code === 0 ? resolve(output.trim()) : reject()));
167
+ dotnetCheck.on("error", reject);
168
+ });
169
+ process.stderr.write(`Found .NET SDK ${String(version)}\n`);
170
+ } catch {
171
+ process.stderr.write("\x1b[31mError: .NET 10 SDK is required to build from source.\x1b[0m\n");
172
+ process.stderr.write("Install it from: https://dotnet.microsoft.com/download\n");
173
+ process.stderr.write(
174
+ "Or wait for a release build at: https://github.com/OpenKrew/openkrew/releases\n",
175
+ );
176
+ process.exit(1);
177
+ }
178
+
179
+ // Find the project root (relative to this script)
180
+ const scriptDir = dirname(new URL(import.meta.url).pathname);
181
+
182
+ if (!existsSync(join(scriptDir, "OpenKrew.slnx"))) {
183
+ process.stderr.write(
184
+ "\x1b[31mError: OpenKrew.slnx not found. Cannot build from source.\x1b[0m\n",
185
+ );
186
+ process.stderr.write("Install a release build: npm install -g openkrew@latest\n");
187
+ process.exit(1);
188
+ }
189
+
190
+ const rid = getRuntimeId();
191
+ process.stderr.write(`Building OpenKrew for ${rid}...\n`);
192
+
193
+ mkdirSync(BIN_DIR, { recursive: true });
194
+
195
+ const buildProcess = spawn(
196
+ "dotnet",
197
+ [
198
+ "publish",
199
+ join(scriptDir, "src", "OpenKrew.Hub", "OpenKrew.Hub.csproj"),
200
+ "-c",
201
+ "Release",
202
+ "-r",
203
+ rid,
204
+ "--self-contained",
205
+ "true",
206
+ "-o",
207
+ BIN_DIR,
208
+ ],
209
+ { stdio: "inherit" },
210
+ );
211
+
212
+ await new Promise((resolve, reject) => {
213
+ buildProcess.on("close", (code) =>
214
+ code === 0 ? resolve() : reject(new Error(`Build failed with code ${code}`)),
215
+ );
216
+ buildProcess.on("error", reject);
217
+ });
218
+
219
+ writeFileSync(VERSION_FILE, PACKAGE_VERSION);
220
+ process.stderr.write(`\x1b[32mBuilt OpenKrew ${PACKAGE_VERSION}\x1b[0m\n`);
221
+ }
222
+
223
+ /**
224
+ * Runs the .NET Hub binary, passing through all CLI arguments.
225
+ */
226
+ function runBinary() {
227
+ const binaryPath = join(BIN_DIR, getBinaryName());
228
+
229
+ if (!existsSync(binaryPath)) {
230
+ process.stderr.write(`\x1b[31mError: OpenKrew binary not found at ${binaryPath}\x1b[0m\n`);
231
+ process.exit(1);
232
+ }
233
+
234
+ const child = spawn(binaryPath, process.argv.slice(2), {
235
+ stdio: "inherit",
236
+ env: {
237
+ ...process.env,
238
+ OPENKREW_BOOTSTRAPPED: "1",
239
+ },
240
+ });
241
+
242
+ child.on("close", (code) => process.exit(code ?? 0));
243
+ child.on("error", (err) => {
244
+ process.stderr.write(`\x1b[31mFailed to start OpenKrew: ${err.message}\x1b[0m\n`);
245
+ process.exit(1);
246
+ });
247
+ }
248
+
249
+ // ── Main ──────────────────────────────────────────────────────────────
250
+
251
+ export async function bootstrap() {
252
+ if (!isInstalled()) {
253
+ await downloadBinary();
254
+ }
255
+ runBinary();
256
+ }
package/openkrew.mjs ADDED
@@ -0,0 +1,204 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { readFileSync } from "node:fs";
4
+ import { access } from "node:fs/promises";
5
+ import module from "node:module";
6
+ import { fileURLToPath } from "node:url";
7
+
8
+ const MIN_NODE_MAJOR = 22;
9
+ const MIN_NODE_MINOR = 12;
10
+ const MIN_NODE_VERSION = `${MIN_NODE_MAJOR}.${MIN_NODE_MINOR}`;
11
+
12
+ const parseNodeVersion = (rawVersion) => {
13
+ const [majorRaw = "0", minorRaw = "0"] = rawVersion.split(".");
14
+ return {
15
+ major: Number(majorRaw),
16
+ minor: Number(minorRaw),
17
+ };
18
+ };
19
+
20
+ const isSupportedNodeVersion = (version) =>
21
+ version.major > MIN_NODE_MAJOR ||
22
+ (version.major === MIN_NODE_MAJOR && version.minor >= MIN_NODE_MINOR);
23
+
24
+ const ensureSupportedNodeVersion = () => {
25
+ if (isSupportedNodeVersion(parseNodeVersion(process.versions.node))) {
26
+ return;
27
+ }
28
+
29
+ process.stderr.write(
30
+ `openkrew: Node.js v${MIN_NODE_VERSION}+ is required (current: v${process.versions.node}).\n` +
31
+ "If you use nvm, run:\n" +
32
+ ` nvm install ${MIN_NODE_MAJOR}\n` +
33
+ ` nvm use ${MIN_NODE_MAJOR}\n` +
34
+ ` nvm alias default ${MIN_NODE_MAJOR}\n`,
35
+ );
36
+ process.exit(1);
37
+ };
38
+
39
+ ensureSupportedNodeVersion();
40
+
41
+ // https://nodejs.org/api/module.html#module-compile-cache
42
+ if (module.enableCompileCache && !process.env.NODE_DISABLE_COMPILE_CACHE) {
43
+ try {
44
+ module.enableCompileCache();
45
+ } catch {
46
+ // Ignore errors
47
+ }
48
+ }
49
+
50
+ // ── OpenKrew .NET Core Bootstrap ──────────────────────────────────────
51
+ // The primary runtime is the .NET core platform. The bootstrapper handles
52
+ // downloading, caching, and running the self-contained binary.
53
+ // Falls back to the legacy TypeScript gateway if the bootstrap fails.
54
+
55
+ const isLegacyMode = process.argv.includes("--legacy") || process.env.OPENKREW_LEGACY === "1";
56
+
57
+ if (!isLegacyMode) {
58
+ try {
59
+ const { bootstrap } = await import("./core-bootstrap.mjs");
60
+ await bootstrap();
61
+ // bootstrap() calls process.exit() via the spawned child, so we
62
+ // only reach here if something went wrong.
63
+ } catch (err) {
64
+ // If the .NET bootstrapper fails (no release, no SDK, etc.),
65
+ // fall through to the legacy TypeScript gateway with a warning.
66
+ process.stderr.write(
67
+ `\x1b[33mOpenKrew .NET core unavailable: ${err.message}\x1b[0m\n` +
68
+ "Falling back to legacy TypeScript gateway.\n" +
69
+ "Run with OPENKREW_LEGACY=1 to skip this check.\n\n",
70
+ );
71
+ }
72
+ }
73
+
74
+ // ── Legacy TypeScript Gateway (OpenClaw fork) ─────────────────────────
75
+
76
+ const isModuleNotFoundError = (err) =>
77
+ err && typeof err === "object" && "code" in err && err.code === "ERR_MODULE_NOT_FOUND";
78
+
79
+ const isDirectModuleNotFoundError = (err, specifier) => {
80
+ if (!isModuleNotFoundError(err)) {
81
+ return false;
82
+ }
83
+
84
+ const expectedUrl = new URL(specifier, import.meta.url);
85
+ if ("url" in err && err.url === expectedUrl.href) {
86
+ return true;
87
+ }
88
+
89
+ const message = "message" in err && typeof err.message === "string" ? err.message : "";
90
+ const expectedPath = fileURLToPath(expectedUrl);
91
+ return (
92
+ message.includes(`Cannot find module '${expectedPath}'`) ||
93
+ message.includes(`Cannot find module "${expectedPath}"`)
94
+ );
95
+ };
96
+
97
+ const installProcessWarningFilter = async () => {
98
+ for (const specifier of ["./dist/warning-filter.js", "./dist/warning-filter.mjs"]) {
99
+ try {
100
+ const mod = await import(specifier);
101
+ if (typeof mod.installProcessWarningFilter === "function") {
102
+ mod.installProcessWarningFilter();
103
+ return;
104
+ }
105
+ } catch (err) {
106
+ if (isDirectModuleNotFoundError(err, specifier)) {
107
+ continue;
108
+ }
109
+ throw err;
110
+ }
111
+ }
112
+ };
113
+
114
+ const tryImport = async (specifier) => {
115
+ try {
116
+ await import(specifier);
117
+ return true;
118
+ } catch (err) {
119
+ if (isDirectModuleNotFoundError(err, specifier)) {
120
+ return false;
121
+ }
122
+ throw err;
123
+ }
124
+ };
125
+
126
+ const exists = async (specifier) => {
127
+ try {
128
+ await access(new URL(specifier, import.meta.url));
129
+ return true;
130
+ } catch {
131
+ return false;
132
+ }
133
+ };
134
+
135
+ const buildMissingEntryErrorMessage = async () => {
136
+ const lines = ["openkrew: missing dist/entry.(m)js (build output)."];
137
+ if (!(await exists("./src/entry.ts"))) {
138
+ return lines.join("\n");
139
+ }
140
+
141
+ lines.push("This install looks like an unbuilt source tree or GitHub source archive.");
142
+ lines.push(
143
+ "Build locally with `pnpm install && pnpm build`, or install a built package instead.",
144
+ );
145
+ lines.push(
146
+ "For pinned GitHub installs, use `npm install -g github:openkrew/openkrew#<ref>` instead of a raw `/archive/<ref>.tar.gz` URL.",
147
+ );
148
+ lines.push("For releases, use `npm install -g openkrew@latest`.");
149
+ return lines.join("\n");
150
+ };
151
+
152
+ const isBareRootHelpInvocation = (argv) =>
153
+ argv.length === 3 && (argv[2] === "--help" || argv[2] === "-h");
154
+
155
+ const loadPrecomputedRootHelpText = () => {
156
+ try {
157
+ const raw = readFileSync(new URL("./dist/cli-startup-metadata.json", import.meta.url), "utf8");
158
+ const parsed = JSON.parse(raw);
159
+ return typeof parsed?.rootHelpText === "string" && parsed.rootHelpText.length > 0
160
+ ? parsed.rootHelpText
161
+ : null;
162
+ } catch {
163
+ return null;
164
+ }
165
+ };
166
+
167
+ const tryOutputBareRootHelp = async () => {
168
+ if (!isBareRootHelpInvocation(process.argv)) {
169
+ return false;
170
+ }
171
+ const precomputed = loadPrecomputedRootHelpText();
172
+ if (precomputed) {
173
+ process.stdout.write(precomputed);
174
+ return true;
175
+ }
176
+ for (const specifier of ["./dist/cli/program/root-help.js", "./dist/cli/program/root-help.mjs"]) {
177
+ try {
178
+ const mod = await import(specifier);
179
+ if (typeof mod.outputRootHelp === "function") {
180
+ mod.outputRootHelp();
181
+ return true;
182
+ }
183
+ } catch (err) {
184
+ if (isDirectModuleNotFoundError(err, specifier)) {
185
+ continue;
186
+ }
187
+ throw err;
188
+ }
189
+ }
190
+ return false;
191
+ };
192
+
193
+ if (await tryOutputBareRootHelp()) {
194
+ // OK
195
+ } else {
196
+ await installProcessWarningFilter();
197
+ if (await tryImport("./dist/entry.js")) {
198
+ // OK
199
+ } else if (await tryImport("./dist/entry.mjs")) {
200
+ // OK
201
+ } else {
202
+ throw new Error(await buildMissingEntryErrorMessage());
203
+ }
204
+ }
package/package.json CHANGED
@@ -1,12 +1,40 @@
1
1
  {
2
2
  "name": "openkrew",
3
- "version": "0.0.1",
4
- "description": "OpenKrew: AI agents on separate machines, working as a team.",
5
- "license": "MIT",
3
+ "version": "0.1.1",
4
+ "description": "Distributed multi-machine AI agent team platform",
5
+ "type": "module",
6
+ "license": "BUSL-1.1",
6
7
  "repository": {
7
8
  "type": "git",
8
- "url": "git+https://github.com/OpenKrew/openkrew.git"
9
+ "url": "https://github.com/OpenKrew/openkrew"
9
10
  },
10
- "homepage": "https://github.com/OpenKrew/openkrew",
11
- "keywords": ["ai", "agents", "distributed", "multi-agent", "slack", "llm"]
11
+ "homepage": "https://openkrew.com",
12
+ "bin": {
13
+ "openkrew": "./openkrew.mjs"
14
+ },
15
+ "files": [
16
+ "openkrew.mjs",
17
+ "core-bootstrap.mjs",
18
+ "scripts/check-global.mjs",
19
+ "LICENSE"
20
+ ],
21
+ "scripts": {
22
+ "preinstall": "node scripts/check-global.mjs"
23
+ },
24
+ "engines": {
25
+ "node": ">=18"
26
+ },
27
+ "keywords": [
28
+ "ai",
29
+ "agents",
30
+ "multi-agent",
31
+ "distributed",
32
+ "slack",
33
+ "discord",
34
+ "teams",
35
+ "llm",
36
+ "claude",
37
+ "openai",
38
+ "developer-tools"
39
+ ]
12
40
  }
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+
3
+ if (process.env.npm_config_global !== "true") {
4
+ console.error("\n OpenKrew must be installed globally:\n");
5
+ console.error(" npm install -g openkrew\n");
6
+ process.exit(1);
7
+ }