@mestreyoda/fabrica 0.2.43 → 0.2.45
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/README.md +39 -3
- package/dist/index.js +26 -15
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -53,14 +53,31 @@ The heartbeat ticks every 60 seconds. On each tick, Fabrica alternates between a
|
|
|
53
53
|
|
|
54
54
|
## Requirements
|
|
55
55
|
|
|
56
|
-
- [OpenClaw](https://openclaw.dev) runtime >= 2026.3.13
|
|
56
|
+
- [OpenClaw](https://openclaw.dev) runtime >= 2026.3.13
|
|
57
|
+
- OpenClaw gateway operational on the local machine (default port 18789)
|
|
57
58
|
- Git (for repository operations and local development)
|
|
58
|
-
- Node.js
|
|
59
|
-
- `gh` CLI authenticated to GitHub (required for issue and
|
|
59
|
+
- Node.js 22+ with npm/npx available
|
|
60
|
+
- `gh` CLI authenticated to GitHub (required for repo, issue, PR, and comment operations)
|
|
60
61
|
- A GitHub organization or personal account where repositories will be created
|
|
61
62
|
- For Python stacks, Fabrica provisions `uv` and project-local environments itself without `sudo`
|
|
62
63
|
- (Optional) Telegram bot token and group chat IDs for DM bootstrap and notifications
|
|
63
64
|
|
|
65
|
+
### Host prerequisites vs project provisioning
|
|
66
|
+
|
|
67
|
+
Fabrica provisions a lot inside each project runtime, but it is not a universal host bootstrapper.
|
|
68
|
+
|
|
69
|
+
What should already exist on the machine:
|
|
70
|
+
- OpenClaw installed and working
|
|
71
|
+
- Node/npm usable
|
|
72
|
+
- Git usable
|
|
73
|
+
- `gh auth` completed with permissions to create repositories, issues, PRs, and comments
|
|
74
|
+
|
|
75
|
+
What Fabrica provisions at project/runtime level:
|
|
76
|
+
- Python `uv` bootstrapping when needed
|
|
77
|
+
- project-local `.venv` for Python stacks
|
|
78
|
+
- project scaffolding and QA contract files
|
|
79
|
+
- stack-specific environment preparation before developer/tester pickup
|
|
80
|
+
|
|
64
81
|
## Installation
|
|
65
82
|
|
|
66
83
|
### Via npm (recommended)
|
|
@@ -106,6 +123,7 @@ gh auth status || gh auth login
|
|
|
106
123
|
```
|
|
107
124
|
|
|
108
125
|
Fabrica uses authenticated `gh` CLI for GitHub operations in the default setup.
|
|
126
|
+
Make sure the authenticated identity can create repositories, issues, PRs, and comments in the target account or organization.
|
|
109
127
|
|
|
110
128
|
**2. Install Fabrica**:
|
|
111
129
|
|
|
@@ -150,6 +168,13 @@ If `projectsForumChatId` is missing while DM bootstrap is enabled, Fabrica can a
|
|
|
150
168
|
|
|
151
169
|
Tip: if you export `FABRICA_PROJECTS_CHANNEL_ID` before running `openclaw fabrica setup`, Fabrica now copies that value into `plugins.entries.fabrica.config.telegram.projectsForumChatId` automatically during setup.
|
|
152
170
|
|
|
171
|
+
Example:
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
export FABRICA_PROJECTS_CHANNEL_ID="<YOUR_PROJECTS_FORUM_CHAT_ID>"
|
|
175
|
+
openclaw fabrica setup --workspace /path/to/workspace --new-agent fabrica
|
|
176
|
+
```
|
|
177
|
+
|
|
153
178
|
**6. Validate operational readiness**:
|
|
154
179
|
|
|
155
180
|
```bash
|
|
@@ -339,6 +364,17 @@ worker start, worker completion, review queueing, reviewer reject/approve, and
|
|
|
339
364
|
operational recovery events, with cycle-aware dedupe so late deliveries from an
|
|
340
365
|
older dispatch do not masquerade as current work.
|
|
341
366
|
|
|
367
|
+
## Minimal path without Telegram
|
|
368
|
+
|
|
369
|
+
Telegram is the recommended human-facing flow, but it is not required to use Fabrica.
|
|
370
|
+
|
|
371
|
+
If you want a minimal path without Telegram:
|
|
372
|
+
1. authenticate `gh`
|
|
373
|
+
2. install the plugin
|
|
374
|
+
3. run `openclaw fabrica doctor workspace --workspace /path/to/workspace`
|
|
375
|
+
4. run `openclaw fabrica setup --workspace /path/to/workspace --new-agent fabrica`
|
|
376
|
+
5. use the programmatic genesis path below to trigger the pipeline
|
|
377
|
+
|
|
342
378
|
## Programmatic genesis
|
|
343
379
|
|
|
344
380
|
In addition to Telegram DM bootstrap, the full pipeline can be triggered from a CLI script — no Telegram or running agent session required:
|
package/dist/index.js
CHANGED
|
@@ -113905,8 +113905,8 @@ import fsSync from "node:fs";
|
|
|
113905
113905
|
import path5 from "node:path";
|
|
113906
113906
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
113907
113907
|
function getCurrentVersion() {
|
|
113908
|
-
if ("0.2.
|
|
113909
|
-
return "0.2.
|
|
113908
|
+
if ("0.2.45") {
|
|
113909
|
+
return "0.2.45";
|
|
113910
113910
|
}
|
|
113911
113911
|
try {
|
|
113912
113912
|
const pkgPath = path5.join(THIS_DIR, "..", "..", "package.json");
|
|
@@ -134190,21 +134190,22 @@ async function migrateChannelBinding(api, channel, fromAgentId, toAgentId) {
|
|
|
134190
134190
|
init_logger();
|
|
134191
134191
|
import fs26 from "node:fs/promises";
|
|
134192
134192
|
import path25 from "node:path";
|
|
134193
|
-
async function createAgent(api, name, runCommand, channelBinding) {
|
|
134193
|
+
async function createAgent(api, name, runCommand, channelBinding, workspacePath) {
|
|
134194
134194
|
const rc = runCommand;
|
|
134195
134195
|
const agentId = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
134196
134196
|
const args = ["agents", "add", agentId, "--non-interactive"];
|
|
134197
134197
|
if (channelBinding) args.push("--bind", channelBinding);
|
|
134198
|
+
if (workspacePath) args.push("--workspace", workspacePath);
|
|
134198
134199
|
try {
|
|
134199
134200
|
await rc(["openclaw", ...args], { timeoutMs: 3e4 });
|
|
134200
134201
|
} catch (err) {
|
|
134201
134202
|
throw new Error(`Failed to create agent "${name}": ${err.message}`);
|
|
134202
134203
|
}
|
|
134203
134204
|
const runtime = "runtime" in api ? api.runtime : api;
|
|
134204
|
-
const
|
|
134205
|
-
await cleanupWorkspace(
|
|
134205
|
+
const resolvedWorkspacePath = workspacePath ?? resolveWorkspacePath(runtime, agentId);
|
|
134206
|
+
await cleanupWorkspace(resolvedWorkspacePath);
|
|
134206
134207
|
await updateAgentDisplayName(runtime, agentId, name);
|
|
134207
|
-
return { agentId, workspacePath };
|
|
134208
|
+
return { agentId, workspacePath: resolvedWorkspacePath };
|
|
134208
134209
|
}
|
|
134209
134210
|
function resolveWorkspacePath(api, agentId) {
|
|
134210
134211
|
const runtime = "runtime" in api ? api.runtime : api;
|
|
@@ -147377,7 +147378,13 @@ async function runSetup(opts) {
|
|
|
147377
147378
|
async function resolveOrCreateAgent(opts, warnings) {
|
|
147378
147379
|
if (opts.newAgentName) {
|
|
147379
147380
|
if (!opts.runCommand) throw new Error("runCommand is required when creating a new agent");
|
|
147380
|
-
const { agentId, workspacePath } = await createAgent(
|
|
147381
|
+
const { agentId, workspacePath } = await createAgent(
|
|
147382
|
+
opts.runtime,
|
|
147383
|
+
opts.newAgentName,
|
|
147384
|
+
opts.runCommand,
|
|
147385
|
+
opts.channelBinding,
|
|
147386
|
+
opts.workspacePath
|
|
147387
|
+
);
|
|
147381
147388
|
const bindingMigrated = await tryMigrateBinding(opts, agentId, warnings);
|
|
147382
147389
|
return { agentId, workspacePath, agentCreated: true, bindingMigrated };
|
|
147383
147390
|
}
|
|
@@ -147416,6 +147423,11 @@ function buildModelConfig(overrides) {
|
|
|
147416
147423
|
}
|
|
147417
147424
|
return result;
|
|
147418
147425
|
}
|
|
147426
|
+
function ensureYamlMapNode(doc, value) {
|
|
147427
|
+
if (value instanceof import_yaml3.default.YAMLMap) return value;
|
|
147428
|
+
const seeded = value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
147429
|
+
return doc.createNode(seeded);
|
|
147430
|
+
}
|
|
147419
147431
|
function getDefaultWorkspacePath(runtime) {
|
|
147420
147432
|
try {
|
|
147421
147433
|
const config2 = runtime.config.loadConfig();
|
|
@@ -147433,16 +147445,15 @@ async function writeModelsToWorkflow(workspacePath, models) {
|
|
|
147433
147445
|
}
|
|
147434
147446
|
const doc = content ? import_yaml3.default.parseDocument(content) : new import_yaml3.default.Document({});
|
|
147435
147447
|
if (!doc.has("roles")) {
|
|
147436
|
-
doc.set("roles", {});
|
|
147448
|
+
doc.set("roles", doc.createNode({}));
|
|
147437
147449
|
}
|
|
147438
|
-
const roles = doc.getIn(["roles"], true);
|
|
147450
|
+
const roles = ensureYamlMapNode(doc, doc.getIn(["roles"], true));
|
|
147451
|
+
doc.set("roles", roles);
|
|
147439
147452
|
for (const [role, levels] of Object.entries(models)) {
|
|
147440
|
-
|
|
147441
|
-
|
|
147442
|
-
|
|
147443
|
-
|
|
147444
|
-
roleNode.set("models", doc.createNode(levels));
|
|
147445
|
-
}
|
|
147453
|
+
const existingRoleNode = roles.get(role, true);
|
|
147454
|
+
const roleNode = ensureYamlMapNode(doc, existingRoleNode);
|
|
147455
|
+
roleNode.set("models", doc.createNode(levels));
|
|
147456
|
+
roles.set(role, roleNode);
|
|
147446
147457
|
}
|
|
147447
147458
|
await fs39.writeFile(workflowPath, doc.toString({ lineWidth: 120 }), "utf-8");
|
|
147448
147459
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mestreyoda/fabrica",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.45",
|
|
4
4
|
"description": "Autonomous software engineering pipeline for OpenClaw. Turns ideas into deployed code via intake, dispatch, review, test, and merge.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|