@fungi.computer/bonsai 0.2.2 → 0.2.4
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/CHANGELOG.md +30 -0
- package/package.json +11 -11
- package/shared/contract.ts +1 -1
- package/test/packed-trellis.test.mjs +138 -0
- package/web/app-build-panel.tsx +5 -0
- package/web/bonsai.css +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# @fungi.computer/bonsai
|
|
2
2
|
|
|
3
|
+
## 0.2.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Changes since 0.2.3:
|
|
8
|
+
|
|
9
|
+
- fix(oyster): ship starter dependencies and continue failed installs
|
|
10
|
+
- fix(oyster): bound builder failures and interrupt lost builds
|
|
11
|
+
|
|
12
|
+
- Updated dependencies
|
|
13
|
+
- Updated dependencies
|
|
14
|
+
- @fungi.computer/oyster@0.4.1
|
|
15
|
+
- @fungi.computer/trellis@0.2.3
|
|
16
|
+
|
|
17
|
+
## 0.2.3
|
|
18
|
+
|
|
19
|
+
### Patch Changes
|
|
20
|
+
|
|
21
|
+
- Updated dependencies
|
|
22
|
+
- Updated dependencies
|
|
23
|
+
- Updated dependencies
|
|
24
|
+
- @fungi.computer/caps@0.3.0
|
|
25
|
+
- @fungi.computer/issues@0.2.0
|
|
26
|
+
- @fungi.computer/oyster@0.4.0
|
|
27
|
+
- @fungi.computer/agar@0.3.1
|
|
28
|
+
- @fungi.computer/trellis@0.2.2
|
|
29
|
+
- @fungi.computer/braid@0.2.2
|
|
30
|
+
- @fungi.computer/nacre@0.0.2
|
|
31
|
+
- @fungi.computer/workflows-wire@0.2.2
|
|
32
|
+
|
|
3
33
|
## 0.2.2
|
|
4
34
|
|
|
5
35
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fungi.computer/bonsai",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"author": "Levi <levi@technomancy.dev>",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Bonsai: a git platform for many agents working at once, on Workers and Artifacts",
|
|
7
7
|
"dependencies": {
|
|
8
|
-
"@fungi.computer/agar": "0.3.
|
|
9
|
-
"@fungi.computer/caps": "0.
|
|
10
|
-
"@fungi.computer/issues": "0.
|
|
11
|
-
"@fungi.computer/oyster": "0.
|
|
8
|
+
"@fungi.computer/agar": "0.3.1",
|
|
9
|
+
"@fungi.computer/caps": "0.3.0",
|
|
10
|
+
"@fungi.computer/issues": "0.2.0",
|
|
11
|
+
"@fungi.computer/oyster": "0.4.1",
|
|
12
12
|
"@fungi.computer/puffball": "0.2.0",
|
|
13
13
|
"@fungi.computer/sigils": "0.2.0",
|
|
14
14
|
"@fungi.computer/stipe": "0.1.0",
|
|
@@ -29,10 +29,10 @@
|
|
|
29
29
|
"@fungi.computer/maosaico": "0.1.0",
|
|
30
30
|
"@visx/hierarchy": "3.12.0",
|
|
31
31
|
"@fungi.computer/gills": "0.2.0",
|
|
32
|
-
"@fungi.computer/nacre": "0.0.
|
|
33
|
-
"@fungi.computer/trellis": "0.2.
|
|
34
|
-
"@fungi.computer/braid": "0.2.
|
|
35
|
-
"@fungi.computer/workflows-wire": "0.2.
|
|
32
|
+
"@fungi.computer/nacre": "0.0.2",
|
|
33
|
+
"@fungi.computer/trellis": "0.2.3",
|
|
34
|
+
"@fungi.computer/braid": "0.2.2",
|
|
35
|
+
"@fungi.computer/workflows-wire": "0.2.2"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"playwright": "1.62.1",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"eslint": "10.9.1",
|
|
53
53
|
"oxlint": "1.78.0",
|
|
54
54
|
"jsdom": "29.0.1",
|
|
55
|
-
"@fungi.computer/computer-contract": "0.0.
|
|
55
|
+
"@fungi.computer/computer-contract": "0.0.3"
|
|
56
56
|
},
|
|
57
57
|
"botanical": {
|
|
58
58
|
"role": "app",
|
|
@@ -176,7 +176,7 @@
|
|
|
176
176
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
177
177
|
"lint": "eslint backend web shared test --max-warnings=0 && oxlint --config ../../oxlint.config.ts --deny-warnings backend web shared test",
|
|
178
178
|
"test": "pnpm run test:unit && pnpm run test:merge",
|
|
179
|
-
"test:unit": "vitest run && node --import tsx --test test/seed-resume.test.mjs test/insight-layout.test.mjs test/checks-workflow-template.test.mjs test/merge-template-build.test.mjs test/storyboard-save-template.test.mjs test/favicon.test.mjs",
|
|
179
|
+
"test:unit": "vitest run && node --import tsx --test test/seed-resume.test.mjs test/insight-layout.test.mjs test/checks-workflow-template.test.mjs test/merge-template-build.test.mjs test/storyboard-save-template.test.mjs test/favicon.test.mjs test/packed-trellis.test.mjs",
|
|
180
180
|
"test:merge": "node --import tsx test/git-push.native.mjs",
|
|
181
181
|
"format:check": "node ../../tooling/repository/scripts/format-package-check.mjs",
|
|
182
182
|
"lint:anti-slop": "node ../../tooling/repository/scripts/lint-package-anti-slop.mjs",
|
package/shared/contract.ts
CHANGED
|
@@ -420,7 +420,7 @@ export const appBuildView = z.strictObject({
|
|
|
420
420
|
appId: z.string().min(1).max(256),
|
|
421
421
|
releaseId: z.string().min(1).max(256),
|
|
422
422
|
sourceRevision: z.string().min(1).max(256),
|
|
423
|
-
status: z.enum(["running", "succeeded", "failed", "revoked"]),
|
|
423
|
+
status: z.enum(["running", "succeeded", "failed", "interrupted", "revoked"]),
|
|
424
424
|
detail: z.string().max(65_536).nullable(),
|
|
425
425
|
/** The recorded Release, parsed by its owner where it is installed. */
|
|
426
426
|
release: z.unknown(),
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { execFileSync } from "node:child_process";
|
|
3
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join, posix, resolve } from "node:path";
|
|
6
|
+
import test from "node:test";
|
|
7
|
+
import ts from "typescript";
|
|
8
|
+
|
|
9
|
+
const root = resolve(import.meta.dirname, "../../..");
|
|
10
|
+
|
|
11
|
+
test("packed Bonsai imports and Tailwind sources stay in its snapshot and packed Trellis", () => {
|
|
12
|
+
const out = mkdtempSync(join(tmpdir(), "bonsai-pack-"));
|
|
13
|
+
try {
|
|
14
|
+
const packed = (name) => {
|
|
15
|
+
const archive = execFileSync(
|
|
16
|
+
"pnpm",
|
|
17
|
+
["--dir", `apps/${name}`, "pack", "--pack-destination", out],
|
|
18
|
+
{ cwd: root, encoding: "utf8" },
|
|
19
|
+
)
|
|
20
|
+
.trim()
|
|
21
|
+
.split("\n")
|
|
22
|
+
.at(-1);
|
|
23
|
+
assert.ok(archive, `${name} packed`);
|
|
24
|
+
const paths = new Set(
|
|
25
|
+
execFileSync("tar", ["tzf", archive], { encoding: "utf8" })
|
|
26
|
+
.trim()
|
|
27
|
+
.split("\n")
|
|
28
|
+
.map((path) => path.replace(/^package\//u, "")),
|
|
29
|
+
);
|
|
30
|
+
const read = (path) =>
|
|
31
|
+
execFileSync("tar", ["xOzf", archive, `package/${path}`], {
|
|
32
|
+
encoding: "utf8",
|
|
33
|
+
});
|
|
34
|
+
return { paths, read };
|
|
35
|
+
};
|
|
36
|
+
const bonsai = packed("bonsai");
|
|
37
|
+
const trellis = packed("trellis");
|
|
38
|
+
const packages = { bonsai, trellis };
|
|
39
|
+
const source = (name, path) => {
|
|
40
|
+
const archive = packages[name];
|
|
41
|
+
assert.ok(archive, name);
|
|
42
|
+
assert.ok(archive.paths.has(path), `${name} ships ${path}`);
|
|
43
|
+
return archive.read(path);
|
|
44
|
+
};
|
|
45
|
+
const resolveSource = (name, from, specifier) => {
|
|
46
|
+
let path;
|
|
47
|
+
let owner = name;
|
|
48
|
+
if (specifier.startsWith("@fungi.computer/trellis/")) {
|
|
49
|
+
owner = "trellis";
|
|
50
|
+
const exportName = `./${specifier.slice("@fungi.computer/trellis/".length)}`;
|
|
51
|
+
const target = JSON.parse(source("trellis", "package.json")).exports[
|
|
52
|
+
exportName
|
|
53
|
+
];
|
|
54
|
+
assert.ok(target, `Trellis exports ${exportName}`);
|
|
55
|
+
path = target.replace(/^\.\//u, "");
|
|
56
|
+
} else {
|
|
57
|
+
path = posix.normalize(posix.join(posix.dirname(from), specifier));
|
|
58
|
+
assert.ok(
|
|
59
|
+
!path.startsWith("../"),
|
|
60
|
+
`${name}/${from}: ${specifier} escapes`,
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
const found = [
|
|
64
|
+
path,
|
|
65
|
+
`${path}.ts`,
|
|
66
|
+
`${path}.tsx`,
|
|
67
|
+
`${path}.json`,
|
|
68
|
+
path.replace(/\.js$/u, ".ts"),
|
|
69
|
+
path.replace(/\.js$/u, ".tsx"),
|
|
70
|
+
posix.join(path, "index.ts"),
|
|
71
|
+
].find((candidate) => packages[owner].paths.has(candidate));
|
|
72
|
+
assert.ok(
|
|
73
|
+
found,
|
|
74
|
+
`${name}/${from}: ${specifier} must be shipped by ${owner}`,
|
|
75
|
+
);
|
|
76
|
+
return [owner, found];
|
|
77
|
+
};
|
|
78
|
+
const visited = new Set();
|
|
79
|
+
const inspect = (name, path) => {
|
|
80
|
+
if (visited.has(`${name}/${path}`)) return;
|
|
81
|
+
visited.add(`${name}/${path}`);
|
|
82
|
+
const text = source(name, path);
|
|
83
|
+
if (path.endsWith(".css")) {
|
|
84
|
+
for (const [, specifier] of text.matchAll(/@source\s+"([^"]+)"/gu)) {
|
|
85
|
+
if (specifier === "./") continue;
|
|
86
|
+
if (specifier.startsWith("./") || specifier.startsWith("../")) {
|
|
87
|
+
const directory = posix.normalize(
|
|
88
|
+
posix.join(posix.dirname(path), specifier),
|
|
89
|
+
);
|
|
90
|
+
assert.ok(
|
|
91
|
+
!directory.startsWith("../"),
|
|
92
|
+
`${name}/${path}: @source escapes snapshot`,
|
|
93
|
+
);
|
|
94
|
+
const dependency = "node_modules/@fungi.computer/trellis/";
|
|
95
|
+
const owner = directory.startsWith(dependency) ? "trellis" : name;
|
|
96
|
+
const target =
|
|
97
|
+
owner === "trellis"
|
|
98
|
+
? directory.slice(dependency.length)
|
|
99
|
+
: directory;
|
|
100
|
+
assert.ok(
|
|
101
|
+
[...packages[owner].paths].some(
|
|
102
|
+
(file) => file.startsWith(`${target}/`) || file === target,
|
|
103
|
+
),
|
|
104
|
+
`${name}/${path}: @source ${specifier} has no shipped files`,
|
|
105
|
+
);
|
|
106
|
+
} else {
|
|
107
|
+
assert.fail(`${name}/${path}: unsupported @source ${specifier}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const ast = ts.createSourceFile(path, text, ts.ScriptTarget.Latest, true);
|
|
113
|
+
const visit = (node) => {
|
|
114
|
+
const specifier =
|
|
115
|
+
ts.isImportDeclaration(node) || ts.isExportDeclaration(node)
|
|
116
|
+
? node.moduleSpecifier
|
|
117
|
+
: undefined;
|
|
118
|
+
if (
|
|
119
|
+
specifier &&
|
|
120
|
+
ts.isStringLiteral(specifier) &&
|
|
121
|
+
(specifier.text.startsWith(".") ||
|
|
122
|
+
specifier.text.startsWith("@fungi.computer/trellis/"))
|
|
123
|
+
) {
|
|
124
|
+
const [owner, target] = resolveSource(name, path, specifier.text);
|
|
125
|
+
if (/\.(?:ts|tsx|js|css)$/u.test(target)) inspect(owner, target);
|
|
126
|
+
}
|
|
127
|
+
ts.forEachChild(node, visit);
|
|
128
|
+
};
|
|
129
|
+
visit(ast);
|
|
130
|
+
};
|
|
131
|
+
for (const path of bonsai.paths) {
|
|
132
|
+
if (/^(web|backend|shared)\/.*\.(?:ts|tsx|css)$/u.test(path))
|
|
133
|
+
inspect("bonsai", path);
|
|
134
|
+
}
|
|
135
|
+
} finally {
|
|
136
|
+
rmSync(out, { recursive: true, force: true });
|
|
137
|
+
}
|
|
138
|
+
});
|
package/web/app-build-panel.tsx
CHANGED
|
@@ -236,6 +236,11 @@ export function AppBuildPanel({ repo }: { repo: string }) {
|
|
|
236
236
|
: ""}
|
|
237
237
|
</p>
|
|
238
238
|
)}
|
|
239
|
+
{settled?.status === "interrupted" ? (
|
|
240
|
+
<p role="status">
|
|
241
|
+
Build interrupted. Start a new build to retry.
|
|
242
|
+
</p>
|
|
243
|
+
) : null}
|
|
239
244
|
{status.isError ? <p role="alert">{status.error.message}</p> : null}
|
|
240
245
|
{settled?.installation?.kind === "needs_consent" ? (
|
|
241
246
|
<p role="status">
|
package/web/bonsai.css
CHANGED