@akanjs/cli 3.0.0-alpha.80 → 3.0.0-alpha.82
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/.build-stamp +1 -1
- package/{application.command-gmcgen9y.js → application.command-4fq5pmdd.js} +2 -2
- package/{cloud.command-gpq7qxrc.js → cloud.command-wm3mtfve.js} +4 -4
- package/commandManifest.json +1 -1
- package/{index-3c9n3nt8.js → index-1tgp56ke.js} +1 -1
- package/{index-f7wewj2v.js → index-m7e0f1sy.js} +1 -1
- package/{index-xnhf8tr9.js → index-m9c7k0gm.js} +57 -7
- package/{index-ee8z8adx.js → index-ypvdw8nr.js} +2 -2
- package/index.js +5 -5
- package/{localRegistry.command-pjdxa01g.js → localRegistry.command-gk773cte.js} +3 -3
- package/package.json +2 -2
- package/{fleet.command-3bkyyd3a.js → subspace.command-nr32nm35.js} +237 -318
- package/{workspace.command-mbb5s3wx.js → workspace.command-4ztb4fdh.js} +4 -4
package/.build-stamp
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
133d9fd6deee4acd90c4506d240dc80a0ae269fc5a4e0e37b76f334390c27745
|
|
@@ -11,13 +11,13 @@ import"./index-ayy5ecax.js";
|
|
|
11
11
|
import"./index-6wwzs1bc.js";
|
|
12
12
|
import {
|
|
13
13
|
ApplicationScript
|
|
14
|
-
} from "./index-
|
|
14
|
+
} from "./index-m7e0f1sy.js";
|
|
15
15
|
import"./index-xxe90kqy.js";
|
|
16
16
|
import"./index-ctphds8v.js";
|
|
17
17
|
import {
|
|
18
18
|
AbstractDoc
|
|
19
19
|
} from "./index-meh558jy.js";
|
|
20
|
-
import"./index-
|
|
20
|
+
import"./index-m9c7k0gm.js";
|
|
21
21
|
import {
|
|
22
22
|
getMobileTargetChoices
|
|
23
23
|
} from "./index-47pj3aee.js";
|
|
@@ -9,16 +9,16 @@ import {
|
|
|
9
9
|
} from "./index-ft1sfjz0.js";
|
|
10
10
|
import"./index-k1n0h7dk.js";
|
|
11
11
|
import"./index-6wwzs1bc.js";
|
|
12
|
-
import"./index-
|
|
12
|
+
import"./index-m7e0f1sy.js";
|
|
13
13
|
import"./index-xxe90kqy.js";
|
|
14
14
|
import"./index-ctphds8v.js";
|
|
15
15
|
import"./index-mfm35dhh.js";
|
|
16
16
|
import"./index-a8h184wr.js";
|
|
17
|
-
import"./index-
|
|
17
|
+
import"./index-1tgp56ke.js";
|
|
18
18
|
import {
|
|
19
19
|
CloudScript
|
|
20
|
-
} from "./index-
|
|
21
|
-
import"./index-
|
|
20
|
+
} from "./index-ypvdw8nr.js";
|
|
21
|
+
import"./index-m9c7k0gm.js";
|
|
22
22
|
|
|
23
23
|
// pkgs/@akanjs/cli/cloud/cloud.command.ts
|
|
24
24
|
var localRegistryUrl = () => process.env.AKAN_NPM_REGISTRY ?? "http://127.0.0.1:4873";
|
package/commandManifest.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"byCommand":{"create-workspace":"workspace","generate-agent-rules":"workspace","lint":"workspace","lint-all":"workspace","sync-all":"workspace","init":"workspace","agent":"agent","create-application":"application","remove-application":"application","plan-slice":"application","sync":"application","compact":"application","script":"application","console":"application","logs":"application","build":"application","b":"application","typecheck":"application","t":"application","test":"application","build-ios":"application","bi":"application","build-android":"application","ba":"application","start":"application","s":"application","start-ios":"application","si":"application","start-android":"application","sa":"application","release-ios":"application","release-android":"application","release-source":"application","codepush":"application","dbup":"application","dbdown":"application","configure-app":"application","create-library":"library","remove-library":"library","sync-library":"library","library-status":"library","install-library":"library","start-registry":"localRegistry","reset-registry":"localRegistry","smoke-registry":"localRegistry","version":"package","create-package":"package","remove-package":"package","sync-package":"package","build-package":"package","verify-dist-package":"package","verify-akan-publish-packages":"package","create-module":"module","remove-module":"module","create-service":"module","create-view":"module","create-unit":"module","create-template":"module","create-crud-page":"page","context":"context","doctor":"context","mcp-install":"context","mcp":"context","mcp-call":"context","login":"cloud","logout":"cloud","set-llm":"cloud","reset-llm":"cloud","ask":"cloud","deploy-akan":"cloud","update":"cloud","download-env":"cloud","upload-env":"cloud","
|
|
1
|
+
{"byCommand":{"create-workspace":"workspace","generate-agent-rules":"workspace","lint":"workspace","lint-all":"workspace","sync-all":"workspace","init":"workspace","agent":"agent","create-application":"application","remove-application":"application","plan-slice":"application","sync":"application","compact":"application","script":"application","console":"application","logs":"application","build":"application","b":"application","typecheck":"application","t":"application","test":"application","build-ios":"application","bi":"application","build-android":"application","ba":"application","start":"application","s":"application","start-ios":"application","si":"application","start-android":"application","sa":"application","release-ios":"application","release-android":"application","release-source":"application","codepush":"application","dbup":"application","dbdown":"application","configure-app":"application","create-library":"library","remove-library":"library","sync-library":"library","library-status":"library","install-library":"library","start-registry":"localRegistry","reset-registry":"localRegistry","smoke-registry":"localRegistry","version":"package","create-package":"package","remove-package":"package","sync-package":"package","build-package":"package","verify-dist-package":"package","verify-akan-publish-packages":"package","create-module":"module","remove-module":"module","create-service":"module","create-view":"module","create-unit":"module","create-template":"module","create-crud-page":"page","context":"context","doctor":"context","mcp-install":"context","mcp":"context","mcp-call":"context","login":"cloud","logout":"cloud","set-llm":"cloud","reset-llm":"cloud","ask":"cloud","deploy-akan":"cloud","update":"cloud","download-env":"cloud","upload-env":"cloud","subspace":"subspace","guideline":"guideline","generate-instruction":"guideline","update-instruction":"guideline","generate-document":"guideline","reapply-instruction":"guideline","create-scalar":"scalar","remove-scalar":"scalar","create-ui":"primitive","add-field":"primitive","add-enum-field":"primitive","quality":"quality","repair":"repair","workflow":"workflow"}}
|
|
@@ -12,8 +12,55 @@ class SlicePlanner {
|
|
|
12
12
|
"@biomejs/biome",
|
|
13
13
|
"@types/bun",
|
|
14
14
|
"akanjs",
|
|
15
|
+
"tailwindcss",
|
|
15
16
|
"typescript"
|
|
16
17
|
];
|
|
18
|
+
static akanPackages = ["akanjs", "@akanjs/devkit", "@akanjs/cli"];
|
|
19
|
+
static async#frameworkPeers(workspace) {
|
|
20
|
+
const peers = new Set;
|
|
21
|
+
let found = false;
|
|
22
|
+
for (const akanPackage of SlicePlanner.akanPackages) {
|
|
23
|
+
for (const prefix of ["node_modules", "pkgs"]) {
|
|
24
|
+
const manifestPath = `${prefix}/${akanPackage}/package.json`;
|
|
25
|
+
if (!await workspace.exists(manifestPath))
|
|
26
|
+
continue;
|
|
27
|
+
const manifest = await workspace.readJson(manifestPath);
|
|
28
|
+
for (const peer of Object.keys(manifest.peerDependencies ?? {}))
|
|
29
|
+
peers.add(peer);
|
|
30
|
+
found = true;
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return found ? peers : null;
|
|
35
|
+
}
|
|
36
|
+
static typesPackageOf(dependency) {
|
|
37
|
+
return dependency.startsWith("@") ? `@types/${dependency.slice(1).replace("/", "__")}` : `@types/${dependency}`;
|
|
38
|
+
}
|
|
39
|
+
static async pruneDependencies(workspace, rootPackageJson, required) {
|
|
40
|
+
const peers = await SlicePlanner.#frameworkPeers(workspace);
|
|
41
|
+
const { workspaces: _workspaces, ...rest } = rootPackageJson;
|
|
42
|
+
if (!peers)
|
|
43
|
+
return {
|
|
44
|
+
packageJson: rest,
|
|
45
|
+
pruned: [],
|
|
46
|
+
warnings: [`dependencies carried whole \u2014 none of ${SlicePlanner.akanPackages.join(", ")} is installed`]
|
|
47
|
+
};
|
|
48
|
+
const keep = new Set([...SlicePlanner.toolchainDependencies, ...peers, ...required]);
|
|
49
|
+
for (const dependency of [...keep])
|
|
50
|
+
keep.add(SlicePlanner.typesPackageOf(dependency));
|
|
51
|
+
const pick = (entries) => Object.fromEntries(Object.entries(entries ?? {}).filter(([name]) => keep.has(name)));
|
|
52
|
+
const patched = rootPackageJson.patchedDependencies ?? {};
|
|
53
|
+
const packageJson = {
|
|
54
|
+
...rest,
|
|
55
|
+
dependencies: pick(rootPackageJson.dependencies),
|
|
56
|
+
devDependencies: pick(rootPackageJson.devDependencies),
|
|
57
|
+
...Object.keys(patched).length ? {
|
|
58
|
+
patchedDependencies: Object.fromEntries(Object.entries(patched).filter(([spec]) => keep.has(spec.slice(0, spec.lastIndexOf("@")))))
|
|
59
|
+
} : {}
|
|
60
|
+
};
|
|
61
|
+
const pruned = Object.keys({ ...rootPackageJson.dependencies, ...rootPackageJson.devDependencies }).filter((name) => !keep.has(name)).sort();
|
|
62
|
+
return { packageJson, pruned, warnings: [] };
|
|
63
|
+
}
|
|
17
64
|
#app;
|
|
18
65
|
constructor(app) {
|
|
19
66
|
this.#app = app;
|
|
@@ -38,7 +85,6 @@ class SlicePlanner {
|
|
|
38
85
|
...[...appInfo.getLibInfos().values()].map((lib) => lib.getScanResult())
|
|
39
86
|
];
|
|
40
87
|
return new Set([
|
|
41
|
-
...SlicePlanner.toolchainDependencies,
|
|
42
88
|
...scanResults.flatMap((scanResult) => [
|
|
43
89
|
...scanResult.dependencies,
|
|
44
90
|
...scanResult.devDependencies,
|
|
@@ -64,12 +110,15 @@ class SlicePlanner {
|
|
|
64
110
|
const libFiles = Object.fromEntries(await Promise.all(libs.map(async (lib) => [lib, await this.#trackedFiles([`libs/${lib}`])])));
|
|
65
111
|
const rootFiles = allTracked.filter((file) => !this.#isMemberFile(file));
|
|
66
112
|
const required = SlicePlanner.#requiredDependencies(appInfo);
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
113
|
+
const {
|
|
114
|
+
packageJson,
|
|
115
|
+
pruned,
|
|
116
|
+
warnings: manifestWarnings
|
|
117
|
+
} = await SlicePlanner.pruneDependencies(this.#app.workspace, rootPackageJson, required);
|
|
70
118
|
const warnings = [
|
|
71
119
|
...untracked.length ? [`${untracked.length} untracked file(s) under the slice paths: ${untracked.join(", ")}`] : [],
|
|
72
120
|
...rootPackageJson.workspaces ? ["root `workspaces` dropped \u2014 a slice consumes akanjs from the registry"] : [],
|
|
121
|
+
...manifestWarnings,
|
|
73
122
|
...await this.#patchWarnings(rootPackageJson)
|
|
74
123
|
];
|
|
75
124
|
return {
|
|
@@ -78,8 +127,9 @@ class SlicePlanner {
|
|
|
78
127
|
appFiles,
|
|
79
128
|
libFiles,
|
|
80
129
|
rootFiles,
|
|
81
|
-
packageJson
|
|
82
|
-
|
|
130
|
+
packageJson,
|
|
131
|
+
requiredDependencies: [...required].sort(),
|
|
132
|
+
unusedDependencies: pruned,
|
|
83
133
|
warnings
|
|
84
134
|
};
|
|
85
135
|
}
|
|
@@ -94,7 +144,7 @@ function formatSlicePlan(plan) {
|
|
|
94
144
|
...libCounts,
|
|
95
145
|
`workspace shell: ${plan.rootFiles.length} files across ${rootEntriesOf(plan.rootFiles).join(", ")}`,
|
|
96
146
|
"",
|
|
97
|
-
`
|
|
147
|
+
`Root dependencies this slice does not use (${plan.unusedDependencies.length}) \u2014 pruned:`,
|
|
98
148
|
"",
|
|
99
149
|
...plan.unusedDependencies.length ? plan.unusedDependencies.map((dep) => ` - ${dep}`) : [" (none)"],
|
|
100
150
|
"",
|
|
@@ -12,13 +12,13 @@ import {
|
|
|
12
12
|
} from "./index-ft1sfjz0.js";
|
|
13
13
|
import {
|
|
14
14
|
ApplicationScript
|
|
15
|
-
} from "./index-
|
|
15
|
+
} from "./index-m7e0f1sy.js";
|
|
16
16
|
import {
|
|
17
17
|
PackageScript
|
|
18
18
|
} from "./index-a8h184wr.js";
|
|
19
19
|
import {
|
|
20
20
|
CloudRunner
|
|
21
|
-
} from "./index-
|
|
21
|
+
} from "./index-1tgp56ke.js";
|
|
22
22
|
|
|
23
23
|
// pkgs/@akanjs/cli/cloud/cloud.script.ts
|
|
24
24
|
import { Logger } from "akanjs/common";
|
package/index.js
CHANGED
|
@@ -14,17 +14,17 @@ import path from "path";
|
|
|
14
14
|
|
|
15
15
|
// pkgs/@akanjs/cli/commandModules.ts
|
|
16
16
|
var commandModules = {
|
|
17
|
-
workspace: async () => (await import("./workspace.command-
|
|
17
|
+
workspace: async () => (await import("./workspace.command-4ztb4fdh.js")).WorkspaceCommand,
|
|
18
18
|
agent: async () => (await import("./agent.command-180hh0ye.js")).AgentCommand,
|
|
19
|
-
application: async () => (await import("./application.command-
|
|
19
|
+
application: async () => (await import("./application.command-4fq5pmdd.js")).ApplicationCommand,
|
|
20
20
|
library: async () => (await import("./library.command-wpedct6c.js")).LibraryCommand,
|
|
21
|
-
localRegistry: async () => (await import("./localRegistry.command-
|
|
21
|
+
localRegistry: async () => (await import("./localRegistry.command-gk773cte.js")).LocalRegistryCommand,
|
|
22
22
|
package: async () => (await import("./package.command-fcbd0b1d.js")).PackageCommand,
|
|
23
23
|
module: async () => (await import("./module.command-8n4kct3e.js")).ModuleCommand,
|
|
24
24
|
page: async () => (await import("./page.command-x4379grq.js")).PageCommand,
|
|
25
25
|
context: async () => (await import("./context.command-dk2m2sjx.js")).ContextCommand,
|
|
26
|
-
cloud: async () => (await import("./cloud.command-
|
|
27
|
-
|
|
26
|
+
cloud: async () => (await import("./cloud.command-wm3mtfve.js")).CloudCommand,
|
|
27
|
+
subspace: async () => (await import("./subspace.command-nr32nm35.js")).SubspaceCommand,
|
|
28
28
|
guideline: async () => (await import("./guideline.command-w6ddv13r.js")).GuidelineCommand,
|
|
29
29
|
scalar: async () => (await import("./scalar.command-2emk89eh.js")).ScalarCommand,
|
|
30
30
|
primitive: async () => (await import("./primitive.command-8etmnqhq.js")).PrimitiveCommand,
|
|
@@ -12,19 +12,19 @@ import"./index-ayy5ecax.js";
|
|
|
12
12
|
import"./index-6wwzs1bc.js";
|
|
13
13
|
import {
|
|
14
14
|
ApplicationScript
|
|
15
|
-
} from "./index-
|
|
15
|
+
} from "./index-m7e0f1sy.js";
|
|
16
16
|
import"./index-xxe90kqy.js";
|
|
17
17
|
import"./index-ctphds8v.js";
|
|
18
18
|
import"./index-wet2ytnr.js";
|
|
19
19
|
import {
|
|
20
20
|
getNpmRegistryUrl,
|
|
21
21
|
CloudRunner
|
|
22
|
-
} from "./index-
|
|
22
|
+
} from "./index-1tgp56ke.js";
|
|
23
23
|
import"./index-mfm35dhh.js";
|
|
24
24
|
import {
|
|
25
25
|
PackageScript
|
|
26
26
|
} from "./index-a8h184wr.js";
|
|
27
|
-
import"./index-
|
|
27
|
+
import"./index-m9c7k0gm.js";
|
|
28
28
|
|
|
29
29
|
// pkgs/@akanjs/cli/localRegistry/localRegistry.runner.ts
|
|
30
30
|
import { mkdir, rm } from "fs/promises";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akanjs/cli",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.82",
|
|
4
4
|
"sourceType": "module",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"@langchain/openai": "^1.4.6",
|
|
34
34
|
"@tailwindcss/node": "^4.3.0",
|
|
35
35
|
"@trapezedev/project": "^7.1.4",
|
|
36
|
-
"akanjs": "3.0.0-alpha.
|
|
36
|
+
"akanjs": "3.0.0-alpha.82",
|
|
37
37
|
"chalk": "^5.6.2",
|
|
38
38
|
"commander": "^14.0.3",
|
|
39
39
|
"dayjs": "^1.11.20",
|
|
@@ -8,8 +8,7 @@ import {
|
|
|
8
8
|
import {
|
|
9
9
|
Executor,
|
|
10
10
|
WorkspaceExecutor,
|
|
11
|
-
AppExecutor
|
|
12
|
-
LibExecutor
|
|
11
|
+
AppExecutor
|
|
13
12
|
} from "./index-k1n0h7dk.js";
|
|
14
13
|
import {
|
|
15
14
|
FileSys
|
|
@@ -17,134 +16,21 @@ import {
|
|
|
17
16
|
import"./index-6wwzs1bc.js";
|
|
18
17
|
import {
|
|
19
18
|
SlicePlanner
|
|
20
|
-
} from "./index-
|
|
19
|
+
} from "./index-m9c7k0gm.js";
|
|
21
20
|
import {
|
|
22
21
|
LibSource
|
|
23
22
|
} from "./index-3d9y066c.js";
|
|
24
23
|
|
|
25
|
-
// pkgs/@akanjs/devkit/
|
|
26
|
-
import path from "path";
|
|
27
|
-
class FleetGuard {
|
|
28
|
-
static anchorFile = "akan.fleet.json";
|
|
29
|
-
static hubOwnedRootFiles = ["package.json", "tsconfig.json", "biome.json", "bunfig.toml"];
|
|
30
|
-
static spokeOwnedDirs = ["env"];
|
|
31
|
-
static contact = "Ask the akansoft team to make this change in the hub, then pull it back down.";
|
|
32
|
-
#workspace;
|
|
33
|
-
constructor(workspace) {
|
|
34
|
-
this.#workspace = workspace;
|
|
35
|
-
}
|
|
36
|
-
async#readAnchorFile() {
|
|
37
|
-
const anchorPath = path.join(this.#workspace.workspaceRoot, FleetGuard.anchorFile);
|
|
38
|
-
if (!await FileSys.fileExists(anchorPath))
|
|
39
|
-
return null;
|
|
40
|
-
return await FileSys.readJson(anchorPath);
|
|
41
|
-
}
|
|
42
|
-
async#anchorCommit() {
|
|
43
|
-
const commit = await this.#workspace.spawn("git", ["log", "-1", "--format=%H", "--", FleetGuard.anchorFile]);
|
|
44
|
-
return commit.trim() || null;
|
|
45
|
-
}
|
|
46
|
-
#isSpokeOwned(file) {
|
|
47
|
-
const segments = file.split("/");
|
|
48
|
-
if (segments[0] !== "apps" && segments[0] !== "libs")
|
|
49
|
-
return false;
|
|
50
|
-
return FleetGuard.spokeOwnedDirs.includes(segments[2] ?? "");
|
|
51
|
-
}
|
|
52
|
-
#violationOf(file) {
|
|
53
|
-
if (this.#isSpokeOwned(file))
|
|
54
|
-
return null;
|
|
55
|
-
if (file === FleetGuard.anchorFile)
|
|
56
|
-
return { path: file, reason: "the fleet anchor is written by a push" };
|
|
57
|
-
if (FleetGuard.hubOwnedRootFiles.includes(file))
|
|
58
|
-
return { path: file, reason: "workspace manifests are owned by the hub" };
|
|
59
|
-
if (file.startsWith("libs/"))
|
|
60
|
-
return { path: file, reason: "shared libraries are owned by the hub" };
|
|
61
|
-
return null;
|
|
62
|
-
}
|
|
63
|
-
async#changedFiles({ staged }) {
|
|
64
|
-
if (staged) {
|
|
65
|
-
const cached = await this.#workspace.spawn("git", ["diff", "--cached", "--name-only"]);
|
|
66
|
-
return cached.split(`
|
|
67
|
-
`).filter((file) => !!file.trim());
|
|
68
|
-
}
|
|
69
|
-
const anchor = await this.#anchorCommit();
|
|
70
|
-
const sinceAnchor = anchor ? (await this.#workspace.spawn("git", ["diff", "--name-only", `${anchor}..HEAD`])).split(`
|
|
71
|
-
`).filter((file) => !!file.trim()) : [];
|
|
72
|
-
const working = (await this.#workspace.spawn("git", ["status", "--porcelain"])).split(`
|
|
73
|
-
`).filter((line) => !!line.trim()).map((line) => line.slice(3).split(" -> ").at(-1) ?? "").filter((file) => !!file);
|
|
74
|
-
return [...new Set([...sinceAnchor, ...working])];
|
|
75
|
-
}
|
|
76
|
-
async#libDrift() {
|
|
77
|
-
const libNames = await this.#workspace.getLibs();
|
|
78
|
-
const statuses = await Promise.all(libNames.map(async (libName) => await new LibSource(LibExecutor.from(this.#workspace, libName)).status()));
|
|
79
|
-
return statuses.filter((status) => status.drift === "drifted").map((status) => ({ lib: status.lib, expected: status.stamp?.hash ?? "", actual: status.hash }));
|
|
80
|
-
}
|
|
81
|
-
async check({ staged = false } = {}) {
|
|
82
|
-
const spoke = await this.#readAnchorFile();
|
|
83
|
-
if (!spoke)
|
|
84
|
-
return { spoke: null, anchor: null, violations: [], driftedLibs: [] };
|
|
85
|
-
const [anchor, changed, driftedLibs] = await Promise.all([
|
|
86
|
-
this.#anchorCommit(),
|
|
87
|
-
this.#changedFiles({ staged }),
|
|
88
|
-
staged ? Promise.resolve([]) : this.#libDrift()
|
|
89
|
-
]);
|
|
90
|
-
const violations = changed.map((file) => this.#violationOf(file)).filter((violation) => !!violation);
|
|
91
|
-
return { spoke, anchor, violations, driftedLibs };
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
function formatFleetGuardReport(report, { warn = false } = {}) {
|
|
95
|
-
if (!report.spoke)
|
|
96
|
-
return "Akan Fleet Guard: not a fleet spoke (no akan.fleet.json) \u2014 nothing to check.";
|
|
97
|
-
const clean = !report.violations.length && !report.driftedLibs.length;
|
|
98
|
-
const sections = [
|
|
99
|
-
"Akan Fleet Guard",
|
|
100
|
-
`hub: ${report.spoke.hub}#${report.spoke.branch} apps: ${report.spoke.apps.join(", ")}`,
|
|
101
|
-
`last push: ${report.anchor?.slice(0, 12) ?? "(none)"}`,
|
|
102
|
-
"",
|
|
103
|
-
...clean ? [" no hub-owned file was changed here."] : [],
|
|
104
|
-
...report.violations.length ? [
|
|
105
|
-
`Hub-owned files changed in this repo (${report.violations.length}):`,
|
|
106
|
-
"",
|
|
107
|
-
...report.violations.map((violation) => ` ${violation.path} \u2014 ${violation.reason}`)
|
|
108
|
-
] : [],
|
|
109
|
-
...report.driftedLibs.length ? [
|
|
110
|
-
"",
|
|
111
|
-
`Libraries edited since the last push (${report.driftedLibs.length}):`,
|
|
112
|
-
"",
|
|
113
|
-
...report.driftedLibs.map((drift) => ` libs/${drift.lib} stamped ${drift.expected.slice(0, 12)}, now ${drift.actual.slice(0, 12)}`)
|
|
114
|
-
] : [],
|
|
115
|
-
...clean ? [] : ["", ` ${FleetGuard.contact}`, ...warn ? [" (warning only \u2014 this commit is not blocked)"] : []]
|
|
116
|
-
];
|
|
117
|
-
return sections.join(`
|
|
118
|
-
`);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// pkgs/@akanjs/devkit/fleetSpoke.ts
|
|
24
|
+
// pkgs/@akanjs/devkit/subspace.ts
|
|
122
25
|
import { mkdir, readdir, rename, rm } from "fs/promises";
|
|
123
|
-
import
|
|
124
|
-
class
|
|
125
|
-
static anchorFile = "akan.
|
|
126
|
-
static
|
|
26
|
+
import path from "path";
|
|
27
|
+
class Subspace {
|
|
28
|
+
static anchorFile = "akan.subspace.json";
|
|
29
|
+
static workspaceOnlyEntries = ["akan.subspace.ts"];
|
|
127
30
|
static memberDirs = ["apps", "libs"];
|
|
128
|
-
static
|
|
31
|
+
static subspaceOwnedDirs = ["env"];
|
|
129
32
|
static secretsMarkers = ["# akan:secrets (managed by akan.config.ts \u2014 do not edit)", "# akan:secrets:end"];
|
|
130
|
-
static holdDir = ".akan-
|
|
131
|
-
static guardHookLine = "bunx akan fleet check --staged --warn";
|
|
132
|
-
static guardWorkflow = [
|
|
133
|
-
"name: akan fleet guard",
|
|
134
|
-
"on: [push, pull_request]",
|
|
135
|
-
"jobs:",
|
|
136
|
-
" guard:",
|
|
137
|
-
" runs-on: ubuntu-latest",
|
|
138
|
-
" steps:",
|
|
139
|
-
" - uses: actions/checkout@v4",
|
|
140
|
-
" with:",
|
|
141
|
-
" fetch-depth: 0",
|
|
142
|
-
" - uses: oven-sh/setup-bun@v2",
|
|
143
|
-
" - run: bun install",
|
|
144
|
-
" - run: bunx akan fleet check",
|
|
145
|
-
""
|
|
146
|
-
].join(`
|
|
147
|
-
`);
|
|
33
|
+
static holdDir = ".akan-subspace-hold";
|
|
148
34
|
#workspace;
|
|
149
35
|
#config;
|
|
150
36
|
#declaration;
|
|
@@ -160,10 +46,10 @@ class FleetSpoke {
|
|
|
160
46
|
return this.#declaration.apps;
|
|
161
47
|
}
|
|
162
48
|
get #remote() {
|
|
163
|
-
return `
|
|
49
|
+
return `subspace-${this.#declaration.name}`;
|
|
164
50
|
}
|
|
165
51
|
get #clonePath() {
|
|
166
|
-
return
|
|
52
|
+
return path.join(this.#workspace.workspaceRoot, ".akan/subspace", this.#declaration.name);
|
|
167
53
|
}
|
|
168
54
|
async#git(args) {
|
|
169
55
|
return await this.#workspace.spawn("git", args);
|
|
@@ -190,7 +76,7 @@ class FleetSpoke {
|
|
|
190
76
|
}
|
|
191
77
|
async anchor(branch) {
|
|
192
78
|
const ref = `${this.#remote}/${branch}`;
|
|
193
|
-
const commit = (await this.#git(["log", "-1", "--format=%H", ref, "--",
|
|
79
|
+
const commit = (await this.#git(["log", "-1", "--format=%H", ref, "--", Subspace.anchorFile])).trim();
|
|
194
80
|
if (!commit)
|
|
195
81
|
return { commit: null, incoming: [] };
|
|
196
82
|
const log = await this.#git(["log", "--format=%H%x09%an%x09%s", `${commit}..${ref}`]);
|
|
@@ -207,11 +93,11 @@ class FleetSpoke {
|
|
|
207
93
|
const paths = [...this.#declaration.apps.map((app) => `apps/${app}`), ...libs.map((lib) => `libs/${lib}`)];
|
|
208
94
|
return { plans, libs, paths };
|
|
209
95
|
}
|
|
210
|
-
#
|
|
96
|
+
#isSubspaceOwned(file) {
|
|
211
97
|
const segments = file.split("/");
|
|
212
|
-
if (!
|
|
98
|
+
if (!Subspace.memberDirs.includes(segments[0] ?? ""))
|
|
213
99
|
return false;
|
|
214
|
-
return
|
|
100
|
+
return Subspace.subspaceOwnedDirs.includes(segments[2] ?? "");
|
|
215
101
|
}
|
|
216
102
|
#isStamped(file) {
|
|
217
103
|
return /^libs\/[^/]+\/package\.json$/.test(file);
|
|
@@ -249,37 +135,37 @@ class FleetSpoke {
|
|
|
249
135
|
const ref = `${this.#remote}/${branch}`;
|
|
250
136
|
const diff = await this.#git(["diff", "--name-only", "HEAD", ref, "--", ...paths]);
|
|
251
137
|
const behindPaths = await this.#meaningfulFiles(ref, diff.split(`
|
|
252
|
-
`).filter((file) => !!file.trim()).filter((file) => !this.#
|
|
138
|
+
`).filter((file) => !!file.trim()).filter((file) => !this.#isSubspaceOwned(file)));
|
|
253
139
|
const driftedLibs = libs.filter((lib) => behindPaths.some((file) => file.startsWith(`libs/${lib}/`)));
|
|
254
140
|
return { name: this.name, branch, hasBranch, anchor, behindPaths, driftedLibs };
|
|
255
141
|
}
|
|
256
142
|
async#ensureClone(branch) {
|
|
257
|
-
if (!await FileSys.dirExists(
|
|
143
|
+
if (!await FileSys.dirExists(path.join(this.#clonePath, ".git"))) {
|
|
258
144
|
await rm(this.#clonePath, { recursive: true, force: true });
|
|
259
|
-
await mkdir(
|
|
145
|
+
await mkdir(path.dirname(this.#clonePath), { recursive: true });
|
|
260
146
|
await this.#workspace.spawn("git", ["clone", "--quiet", this.#declaration.repo, this.#clonePath]);
|
|
261
147
|
}
|
|
262
|
-
const clone = new Executor(`
|
|
148
|
+
const clone = new Executor(`subspace-${this.name}`, this.#clonePath);
|
|
263
149
|
await clone.spawn("git", ["fetch", "--quiet", "origin", branch]);
|
|
264
150
|
await clone.spawn("git", ["checkout", "--quiet", "-B", branch, `origin/${branch}`]);
|
|
265
151
|
await clone.spawn("git", ["clean", "-qfd"]);
|
|
266
152
|
return clone;
|
|
267
153
|
}
|
|
268
|
-
async#
|
|
154
|
+
async#holdSubspaceOwned() {
|
|
269
155
|
const held = [];
|
|
270
|
-
const holdRoot =
|
|
156
|
+
const holdRoot = path.join(this.#clonePath, Subspace.holdDir);
|
|
271
157
|
await rm(holdRoot, { recursive: true, force: true });
|
|
272
|
-
for (const member of
|
|
273
|
-
const memberRoot =
|
|
158
|
+
for (const member of Subspace.memberDirs) {
|
|
159
|
+
const memberRoot = path.join(this.#clonePath, member);
|
|
274
160
|
if (!await FileSys.dirExists(memberRoot))
|
|
275
161
|
continue;
|
|
276
162
|
for (const name of await readdir(memberRoot)) {
|
|
277
|
-
for (const owned of
|
|
278
|
-
const from =
|
|
163
|
+
for (const owned of Subspace.subspaceOwnedDirs) {
|
|
164
|
+
const from = path.join(memberRoot, name, owned);
|
|
279
165
|
if (!await FileSys.dirExists(from))
|
|
280
166
|
continue;
|
|
281
|
-
const to =
|
|
282
|
-
await mkdir(
|
|
167
|
+
const to = path.join(holdRoot, member, name, owned);
|
|
168
|
+
await mkdir(path.dirname(to), { recursive: true });
|
|
283
169
|
await rename(from, to);
|
|
284
170
|
held.push({ from, to });
|
|
285
171
|
}
|
|
@@ -287,18 +173,18 @@ class FleetSpoke {
|
|
|
287
173
|
}
|
|
288
174
|
return held;
|
|
289
175
|
}
|
|
290
|
-
async#
|
|
176
|
+
async#restoreSubspaceOwned(held) {
|
|
291
177
|
for (const { from, to } of held)
|
|
292
|
-
await
|
|
293
|
-
await rm(
|
|
178
|
+
await Subspace.#copyMissing(to, from);
|
|
179
|
+
await rm(path.join(this.#clonePath, Subspace.holdDir), { recursive: true, force: true });
|
|
294
180
|
}
|
|
295
181
|
static async#copyMissing(from, to) {
|
|
296
182
|
await mkdir(to, { recursive: true });
|
|
297
183
|
for (const entry of await readdir(from, { withFileTypes: true })) {
|
|
298
|
-
const source =
|
|
299
|
-
const target =
|
|
184
|
+
const source = path.join(from, entry.name);
|
|
185
|
+
const target = path.join(to, entry.name);
|
|
300
186
|
if (entry.isDirectory())
|
|
301
|
-
await
|
|
187
|
+
await Subspace.#copyMissing(source, target);
|
|
302
188
|
else if (!await FileSys.entryExists(target))
|
|
303
189
|
await rename(source, target);
|
|
304
190
|
}
|
|
@@ -320,17 +206,17 @@ class FleetSpoke {
|
|
|
320
206
|
};
|
|
321
207
|
}
|
|
322
208
|
async#writeLocalEnv() {
|
|
323
|
-
const excludePath =
|
|
209
|
+
const excludePath = path.join(this.#clonePath, ".git/info/exclude");
|
|
324
210
|
const exclude = await FileSys.fileExists(excludePath) ? await FileSys.readText(excludePath) : "";
|
|
325
211
|
if (!exclude.split(`
|
|
326
212
|
`).includes("/.env")) {
|
|
327
|
-
await mkdir(
|
|
213
|
+
await mkdir(path.dirname(excludePath), { recursive: true });
|
|
328
214
|
await FileSys.writeText(excludePath, exclude.trim() ? `${exclude.replace(/\n*$/, `
|
|
329
215
|
`)}/.env
|
|
330
216
|
` : `/.env
|
|
331
217
|
`);
|
|
332
218
|
}
|
|
333
|
-
const envPath =
|
|
219
|
+
const envPath = path.join(this.#clonePath, ".env");
|
|
334
220
|
if (await FileSys.fileExists(envPath))
|
|
335
221
|
return;
|
|
336
222
|
const lines = Object.entries(this.#localEnv()).map(([key, value]) => `${key}=${value}`);
|
|
@@ -338,29 +224,44 @@ class FleetSpoke {
|
|
|
338
224
|
`)}
|
|
339
225
|
`);
|
|
340
226
|
}
|
|
227
|
+
async#rewriteManifest(plans) {
|
|
228
|
+
const manifestPath = path.join(this.#clonePath, "package.json");
|
|
229
|
+
if (!await FileSys.fileExists(manifestPath))
|
|
230
|
+
return [];
|
|
231
|
+
const rootPackageJson = await FileSys.readJson(manifestPath);
|
|
232
|
+
const { packageJson, pruned, warnings } = await SlicePlanner.pruneDependencies(this.#workspace, rootPackageJson, plans.flatMap((plan) => plan.requiredDependencies));
|
|
233
|
+
for (const warning of warnings)
|
|
234
|
+
this.#workspace.logger.warn(warning);
|
|
235
|
+
await FileSys.writeJson(manifestPath, {
|
|
236
|
+
...packageJson,
|
|
237
|
+
name: this.name,
|
|
238
|
+
description: `${this.name} workspace`
|
|
239
|
+
});
|
|
240
|
+
return pruned;
|
|
241
|
+
}
|
|
341
242
|
async#applySlice(clone, branch) {
|
|
342
|
-
const { libs, paths } = await this.slice();
|
|
343
|
-
const held = await this.#
|
|
344
|
-
for (const member of
|
|
345
|
-
await rm(
|
|
243
|
+
const { libs, paths, plans } = await this.slice();
|
|
244
|
+
const held = await this.#holdSubspaceOwned();
|
|
245
|
+
for (const member of Subspace.memberDirs)
|
|
246
|
+
await rm(path.join(this.#clonePath, member), { recursive: true, force: true });
|
|
346
247
|
await this.#extract(["."], ["apps/*", "libs/*", "pkgs/*"]);
|
|
347
248
|
await this.#extract(paths);
|
|
348
|
-
await this.#
|
|
349
|
-
for (const entry of [...
|
|
350
|
-
await rm(
|
|
249
|
+
await this.#restoreSubspaceOwned(held);
|
|
250
|
+
for (const entry of [...Subspace.workspaceOnlyEntries, ...this.#config.exclude])
|
|
251
|
+
await rm(path.join(this.#clonePath, entry), { recursive: true, force: true });
|
|
351
252
|
await this.#rewriteGitignore();
|
|
253
|
+
const pruned = await this.#rewriteManifest(plans);
|
|
352
254
|
await this.#rewriteWorkspaceSection(libs);
|
|
353
|
-
await this.#writeGuards();
|
|
354
255
|
await this.#writeLocalEnv();
|
|
355
256
|
for (const lib of libs)
|
|
356
257
|
await this.#stampLib(clone, lib, branch);
|
|
357
|
-
return { libs };
|
|
258
|
+
return { libs, pruned };
|
|
358
259
|
}
|
|
359
260
|
async#rewriteGitignore() {
|
|
360
|
-
const gitignorePath =
|
|
261
|
+
const gitignorePath = path.join(this.#clonePath, ".gitignore");
|
|
361
262
|
if (!await FileSys.fileExists(gitignorePath))
|
|
362
263
|
return;
|
|
363
|
-
const [begin = "", end = ""] =
|
|
264
|
+
const [begin = "", end = ""] = Subspace.secretsMarkers;
|
|
364
265
|
const lines = (await FileSys.readText(gitignorePath)).split(`
|
|
365
266
|
`);
|
|
366
267
|
const beginIdx = lines.indexOf(begin);
|
|
@@ -380,7 +281,7 @@ class FleetSpoke {
|
|
|
380
281
|
[/^- Libraries: .*$/m, `- Libraries: ${libs.length ? libs.join(", ") : "(none)"}`]
|
|
381
282
|
];
|
|
382
283
|
for (const file of ["AGENTS.md", "CLAUDE.md"]) {
|
|
383
|
-
const filePath =
|
|
284
|
+
const filePath = path.join(this.#clonePath, file);
|
|
384
285
|
if (!await FileSys.fileExists(filePath))
|
|
385
286
|
continue;
|
|
386
287
|
const content = await FileSys.readText(filePath);
|
|
@@ -390,7 +291,7 @@ class FleetSpoke {
|
|
|
390
291
|
}
|
|
391
292
|
}
|
|
392
293
|
async#stampLib(clone, lib, branch) {
|
|
393
|
-
const manifestPath =
|
|
294
|
+
const manifestPath = path.join(this.#clonePath, "libs", lib, "package.json");
|
|
394
295
|
if (!await FileSys.fileExists(manifestPath))
|
|
395
296
|
return;
|
|
396
297
|
const origin = `${this.#workspace.repoName}#${branch}`;
|
|
@@ -423,10 +324,10 @@ class FleetSpoke {
|
|
|
423
324
|
"--",
|
|
424
325
|
`libs/${lib}`
|
|
425
326
|
]);
|
|
426
|
-
const files = listed.split("\x00").filter((file) => !!file && !this.#
|
|
327
|
+
const files = listed.split("\x00").filter((file) => !!file && !this.#isSubspaceOwned(file)).sort();
|
|
427
328
|
const hasher = new Bun.CryptoHasher("sha256");
|
|
428
329
|
for (const file of files) {
|
|
429
|
-
const content = await FileSys.readText(
|
|
330
|
+
const content = await FileSys.readText(path.join(this.#clonePath, file));
|
|
430
331
|
hasher.update(file);
|
|
431
332
|
hasher.update("\x00");
|
|
432
333
|
if (file === `libs/${lib}/package.json`) {
|
|
@@ -441,7 +342,7 @@ class FleetSpoke {
|
|
|
441
342
|
}
|
|
442
343
|
async diff(branch, filter) {
|
|
443
344
|
if (!await this.fetch(branch))
|
|
444
|
-
throw new Error(`
|
|
345
|
+
throw new Error(`Subspace "${this.name}" has no branch "${branch}"`);
|
|
445
346
|
const { libs, paths } = await this.slice();
|
|
446
347
|
const libPaths = libs.map((lib) => `libs/${lib}`);
|
|
447
348
|
const appPaths = paths.filter((entry) => !libPaths.includes(entry));
|
|
@@ -459,31 +360,11 @@ class FleetSpoke {
|
|
|
459
360
|
const pathspec = filter ? [filter] : scoped;
|
|
460
361
|
const names = await this.#git(["diff", "--name-only", from, "HEAD", "--", ...pathspec]);
|
|
461
362
|
const files = await this.#meaningfulFiles(from, names.split(`
|
|
462
|
-
`).filter((file) => !!file.trim()).filter((file) => !this.#
|
|
363
|
+
`).filter((file) => !!file.trim()).filter((file) => !this.#isSubspaceOwned(file)));
|
|
463
364
|
if (!files.length)
|
|
464
365
|
return { files: [], patch: "" };
|
|
465
366
|
return { files, patch: await this.#git(["diff", from, "HEAD", "--", ...files]) };
|
|
466
367
|
}
|
|
467
|
-
async#writeGuards() {
|
|
468
|
-
const { ci, preCommit } = this.#config.guard;
|
|
469
|
-
if (ci === "github") {
|
|
470
|
-
const workflowPath = path2.join(this.#clonePath, ".github/workflows/akan-fleet-guard.yml");
|
|
471
|
-
await mkdir(path2.dirname(workflowPath), { recursive: true });
|
|
472
|
-
await FileSys.writeText(workflowPath, FleetSpoke.guardWorkflow);
|
|
473
|
-
}
|
|
474
|
-
if (!preCommit)
|
|
475
|
-
return;
|
|
476
|
-
const hookPath = path2.join(this.#clonePath, ".husky/pre-commit");
|
|
477
|
-
const existing = await FileSys.fileExists(hookPath) ? await FileSys.readText(hookPath) : "";
|
|
478
|
-
if (existing.includes(FleetSpoke.guardHookLine))
|
|
479
|
-
return;
|
|
480
|
-
await mkdir(path2.dirname(hookPath), { recursive: true });
|
|
481
|
-
const content = existing.trim() ? `${existing.replace(/\n*$/, `
|
|
482
|
-
`)}${FleetSpoke.guardHookLine}
|
|
483
|
-
` : `${FleetSpoke.guardHookLine}
|
|
484
|
-
`;
|
|
485
|
-
await FileSys.writeText(hookPath, content);
|
|
486
|
-
}
|
|
487
368
|
async#verify(clone) {
|
|
488
369
|
const env = { ...process.env, ...this.#localEnv() };
|
|
489
370
|
try {
|
|
@@ -498,14 +379,20 @@ class FleetSpoke {
|
|
|
498
379
|
async push(branch, { verify = true } = {}) {
|
|
499
380
|
this.#config.assertPushable(branch);
|
|
500
381
|
if (await this.#workspace.hasChanges())
|
|
501
|
-
return { name: this.name, outcome: "refused", reason: "
|
|
382
|
+
return { name: this.name, outcome: "refused", reason: "workspace working tree is dirty", changedFiles: 0 };
|
|
502
383
|
if (!await this.fetch(branch))
|
|
503
|
-
return { name: this.name, outcome: "refused", reason: `
|
|
384
|
+
return { name: this.name, outcome: "refused", reason: `subspace has no branch "${branch}"`, changedFiles: 0 };
|
|
504
385
|
const clone = await this.#ensureClone(branch);
|
|
505
|
-
await this.#applySlice(clone, branch);
|
|
386
|
+
const { pruned } = await this.#applySlice(clone, branch);
|
|
506
387
|
const dirty = (await clone.spawn("git", ["status", "--porcelain"])).trim();
|
|
507
388
|
if (!dirty)
|
|
508
|
-
return {
|
|
389
|
+
return {
|
|
390
|
+
name: this.name,
|
|
391
|
+
outcome: "skipped",
|
|
392
|
+
reason: "already up to date",
|
|
393
|
+
changedFiles: 0,
|
|
394
|
+
prunedDependencies: pruned
|
|
395
|
+
};
|
|
509
396
|
const changedFiles = dirty.split(`
|
|
510
397
|
`).length;
|
|
511
398
|
if (verify) {
|
|
@@ -515,19 +402,20 @@ class FleetSpoke {
|
|
|
515
402
|
}
|
|
516
403
|
await this.#writeAnchorFile(branch);
|
|
517
404
|
await clone.spawn("git", ["add", "-A"]);
|
|
518
|
-
const message = `chore(
|
|
405
|
+
const message = `chore(subspace): sync from ${this.#workspace.repoName}@${await this.headSha()}`;
|
|
519
406
|
await clone.spawn("git", ["commit", "--quiet", "-m", message]);
|
|
520
407
|
await clone.spawn("git", ["push", "--quiet", "origin", branch]);
|
|
521
408
|
return {
|
|
522
409
|
name: this.name,
|
|
523
410
|
outcome: "pushed",
|
|
524
411
|
commit: (await clone.spawn("git", ["rev-parse", "--short", "HEAD"])).trim(),
|
|
525
|
-
changedFiles
|
|
412
|
+
changedFiles,
|
|
413
|
+
prunedDependencies: pruned
|
|
526
414
|
};
|
|
527
415
|
}
|
|
528
416
|
async#writeAnchorFile(branch) {
|
|
529
|
-
await FileSys.writeJson(
|
|
530
|
-
|
|
417
|
+
await FileSys.writeJson(path.join(this.#clonePath, Subspace.anchorFile), {
|
|
418
|
+
workspace: this.#workspace.repoName,
|
|
531
419
|
hubSha: await this.headSha(),
|
|
532
420
|
branch,
|
|
533
421
|
apps: this.#declaration.apps,
|
|
@@ -536,18 +424,18 @@ class FleetSpoke {
|
|
|
536
424
|
}
|
|
537
425
|
async pull(branch, { adoptLibs = false } = {}) {
|
|
538
426
|
if (!await this.fetch(branch))
|
|
539
|
-
throw new Error(`
|
|
427
|
+
throw new Error(`Subspace "${this.name}" has no branch "${branch}"`);
|
|
540
428
|
const anchor = await this.anchor(branch);
|
|
541
429
|
if (!anchor.commit)
|
|
542
|
-
throw new Error(`
|
|
430
|
+
throw new Error(`Subspace "${this.name}" carries no ${Subspace.anchorFile} \u2014 it has never received a push`);
|
|
543
431
|
if (!anchor.incoming.length)
|
|
544
432
|
return { name: this.name, applied: [], libPatch: null, ignored: [], incoming: [] };
|
|
545
433
|
const range = `${anchor.commit}..${this.#remote}/${branch}`;
|
|
546
434
|
const changed = (await this.#git(["diff", "--name-only", range])).split(`
|
|
547
435
|
`).filter((file) => !!file.trim());
|
|
548
436
|
const appPrefixes = this.#declaration.apps.map((app) => `apps/${app}/`);
|
|
549
|
-
const appFiles = changed.filter((file) => appPrefixes.some((prefix) => file.startsWith(prefix)) && !this.#
|
|
550
|
-
const libFiles = changed.filter((file) => file.startsWith("libs/") && !this.#
|
|
437
|
+
const appFiles = changed.filter((file) => appPrefixes.some((prefix) => file.startsWith(prefix)) && !this.#isSubspaceOwned(file));
|
|
438
|
+
const libFiles = changed.filter((file) => file.startsWith("libs/") && !this.#isSubspaceOwned(file));
|
|
551
439
|
const ignored = changed.filter((file) => !appFiles.includes(file) && !libFiles.includes(file));
|
|
552
440
|
const applied = adoptLibs ? [...appFiles, ...libFiles] : appFiles;
|
|
553
441
|
if (applied.length)
|
|
@@ -561,20 +449,20 @@ class FleetSpoke {
|
|
|
561
449
|
await this.#git(["apply", "--3way", absolute]);
|
|
562
450
|
}
|
|
563
451
|
async#savePatch(range, files, label) {
|
|
564
|
-
const patchPath =
|
|
565
|
-
await mkdir(
|
|
452
|
+
const patchPath = path.join(this.#workspace.workspaceRoot, ".akan/subspace", `${this.name}-${label}.patch`);
|
|
453
|
+
await mkdir(path.dirname(patchPath), { recursive: true });
|
|
566
454
|
await FileSys.writeText(patchPath, await this.#git(["diff", range, "--", ...files]));
|
|
567
|
-
return { path:
|
|
455
|
+
return { path: path.relative(this.#workspace.workspaceRoot, patchPath), absolute: patchPath, files };
|
|
568
456
|
}
|
|
569
457
|
}
|
|
570
|
-
function
|
|
458
|
+
function formatSubspaceStatuses(statuses) {
|
|
571
459
|
const sections = [
|
|
572
|
-
"Akan
|
|
460
|
+
"Akan Subspace Status",
|
|
573
461
|
`branch: ${statuses[0]?.branch ?? "(none)"}`,
|
|
574
462
|
"",
|
|
575
463
|
...statuses.flatMap((status) => {
|
|
576
464
|
if (!status.hasBranch)
|
|
577
|
-
return [` ${status.name}: no such branch in the
|
|
465
|
+
return [` ${status.name}: no such branch in the subspace \u2014 push is refused`];
|
|
578
466
|
const behind = status.behindPaths.length ? `${status.behindPaths.length} file(s) behind` : "up to date";
|
|
579
467
|
const incoming = status.anchor.incoming.length ? `${status.anchor.incoming.length} customer commit(s) to pull` : "no customer commits";
|
|
580
468
|
const drift = status.driftedLibs.length ? ` DRIFTED LIBS: ${status.driftedLibs.join(", ")}` : null;
|
|
@@ -588,23 +476,28 @@ function formatFleetStatuses(statuses) {
|
|
|
588
476
|
return sections.join(`
|
|
589
477
|
`);
|
|
590
478
|
}
|
|
591
|
-
function
|
|
479
|
+
function formatSubspacePushResults(results) {
|
|
592
480
|
const sections = [
|
|
593
|
-
"Akan
|
|
481
|
+
"Akan Subspace Push",
|
|
594
482
|
"",
|
|
595
483
|
...results.flatMap((result) => {
|
|
596
484
|
const detail = result.outcome === "pushed" ? `${result.commit} (${result.changedFiles} files)` : result.reason ?? "";
|
|
597
485
|
const [first = "", ...rest] = detail.split(`
|
|
598
486
|
`);
|
|
599
|
-
|
|
487
|
+
const pruned = result.prunedDependencies?.length ? [` ${result.prunedDependencies.length} unused root dependenc(ies) left out of package.json`] : [];
|
|
488
|
+
return [
|
|
489
|
+
` ${result.outcome.padEnd(8)} ${result.name} ${first}`,
|
|
490
|
+
...rest.map((line) => ` ${line}`),
|
|
491
|
+
...pruned
|
|
492
|
+
];
|
|
600
493
|
})
|
|
601
494
|
];
|
|
602
495
|
return sections.join(`
|
|
603
496
|
`);
|
|
604
497
|
}
|
|
605
|
-
function
|
|
498
|
+
function formatSubspacePullResult(result) {
|
|
606
499
|
const sections = [
|
|
607
|
-
`Akan
|
|
500
|
+
`Akan Subspace Pull \u2014 ${result.name}`,
|
|
608
501
|
"",
|
|
609
502
|
`Customer commits since the last push (${result.incoming.length}):`,
|
|
610
503
|
"",
|
|
@@ -620,22 +513,22 @@ function formatFleetPullResult(result) {
|
|
|
620
513
|
...result.libPatch.files.map((file) => ` ${file}`),
|
|
621
514
|
"",
|
|
622
515
|
` patch: ${result.libPatch.path}`,
|
|
623
|
-
" adopt with: akan
|
|
516
|
+
" adopt with: akan subspace pull <name> --adopt-libs"
|
|
624
517
|
] : [],
|
|
625
|
-
...result.ignored.length ? ["", `Ignored (
|
|
518
|
+
...result.ignored.length ? ["", `Ignored (workspace-owned or subspace-owned): ${result.ignored.length} file(s)`] : []
|
|
626
519
|
];
|
|
627
520
|
return sections.join(`
|
|
628
521
|
`);
|
|
629
522
|
}
|
|
630
|
-
function
|
|
523
|
+
function formatSubspaceDiff(result) {
|
|
631
524
|
const total = result.app.files.length + result.libs.files.length;
|
|
632
525
|
const sections = [
|
|
633
|
-
`Akan
|
|
634
|
-
"what `akan
|
|
526
|
+
`Akan Subspace Diff \u2014 ${result.name} (${result.branch})`,
|
|
527
|
+
"what `akan subspace push` would change in the subspace",
|
|
635
528
|
"",
|
|
636
|
-
...total ? [] : ["
|
|
529
|
+
...total ? [] : [" subspace is identical to the workspace for this slice."],
|
|
637
530
|
...result.libs.files.length ? [
|
|
638
|
-
`LIBRARY changes (${result.libs.files.length}) \u2014 the
|
|
531
|
+
`LIBRARY changes (${result.libs.files.length}) \u2014 the subspace edited shared code:`,
|
|
639
532
|
"",
|
|
640
533
|
...result.libs.files.map((file) => ` ${file}`),
|
|
641
534
|
"",
|
|
@@ -653,184 +546,210 @@ function formatFleetDiff(result) {
|
|
|
653
546
|
`);
|
|
654
547
|
}
|
|
655
548
|
|
|
656
|
-
// pkgs/@akanjs/cli/
|
|
549
|
+
// pkgs/@akanjs/cli/subspace/subspace.script.ts
|
|
657
550
|
import { Logger } from "akanjs/common";
|
|
658
551
|
|
|
659
|
-
// pkgs/@akanjs/devkit/
|
|
660
|
-
import
|
|
661
|
-
class
|
|
662
|
-
static fileName = "akan.
|
|
552
|
+
// pkgs/@akanjs/devkit/subspaceConfig.ts
|
|
553
|
+
import path2 from "path";
|
|
554
|
+
class SubspaceConfig {
|
|
555
|
+
static fileName = "akan.subspace.ts";
|
|
663
556
|
static defaultPushableBranches = ["main", "develop", "debug"];
|
|
664
|
-
static defaultGuard = { ci: "github", preCommit: true };
|
|
665
557
|
static async from(workspace) {
|
|
666
|
-
const configPath =
|
|
558
|
+
const configPath = path2.join(workspace.workspaceRoot, SubspaceConfig.fileName);
|
|
667
559
|
if (!await FileSys.fileExists(configPath))
|
|
668
560
|
return null;
|
|
669
561
|
const input = await import(configPath).then((mod) => mod.default);
|
|
670
|
-
return new
|
|
562
|
+
return new SubspaceConfig(input);
|
|
671
563
|
}
|
|
672
564
|
pushableBranches;
|
|
673
565
|
exclude;
|
|
674
|
-
|
|
675
|
-
spokes;
|
|
566
|
+
subspaces;
|
|
676
567
|
constructor(input) {
|
|
677
|
-
this.pushableBranches = input.pushableBranches ??
|
|
568
|
+
this.pushableBranches = input.pushableBranches ?? SubspaceConfig.defaultPushableBranches;
|
|
678
569
|
this.exclude = input.exclude ?? [];
|
|
679
|
-
this.
|
|
680
|
-
this.spokes = input.spokes ?? [];
|
|
570
|
+
this.subspaces = input.subspaces ?? [];
|
|
681
571
|
this.#assertValid();
|
|
682
572
|
}
|
|
683
573
|
#assertValid() {
|
|
684
574
|
const seenNames = new Set;
|
|
685
575
|
const appOwners = new Map;
|
|
686
|
-
for (const
|
|
687
|
-
if (!
|
|
688
|
-
throw new Error(`${
|
|
689
|
-
if (seenNames.has(
|
|
690
|
-
throw new Error(`${
|
|
691
|
-
seenNames.add(
|
|
692
|
-
if (!
|
|
693
|
-
throw new Error(`${
|
|
694
|
-
for (const app of
|
|
576
|
+
for (const subspace of this.subspaces) {
|
|
577
|
+
if (!subspace.name || !subspace.repo)
|
|
578
|
+
throw new Error(`${SubspaceConfig.fileName}: every subspace needs a name and a repo`);
|
|
579
|
+
if (seenNames.has(subspace.name))
|
|
580
|
+
throw new Error(`${SubspaceConfig.fileName}: duplicate subspace "${subspace.name}"`);
|
|
581
|
+
seenNames.add(subspace.name);
|
|
582
|
+
if (!subspace.apps?.length)
|
|
583
|
+
throw new Error(`${SubspaceConfig.fileName}: subspace "${subspace.name}" declares no apps`);
|
|
584
|
+
for (const app of subspace.apps) {
|
|
695
585
|
const owner = appOwners.get(app);
|
|
696
586
|
if (owner)
|
|
697
|
-
throw new Error(`${
|
|
698
|
-
appOwners.set(app,
|
|
587
|
+
throw new Error(`${SubspaceConfig.fileName}: app "${app}" is claimed by both "${owner}" and "${subspace.name}"`);
|
|
588
|
+
appOwners.set(app, subspace.name);
|
|
699
589
|
}
|
|
700
590
|
}
|
|
701
591
|
}
|
|
702
592
|
select(names) {
|
|
703
593
|
if (!names.length)
|
|
704
|
-
return this.
|
|
594
|
+
return this.subspaces;
|
|
705
595
|
return names.map((name) => {
|
|
706
|
-
const
|
|
707
|
-
if (!
|
|
708
|
-
throw new Error(`${
|
|
709
|
-
return
|
|
596
|
+
const subspace = this.subspaces.find((candidate) => candidate.name === name);
|
|
597
|
+
if (!subspace)
|
|
598
|
+
throw new Error(`${SubspaceConfig.fileName}: unknown subspace "${name}"`);
|
|
599
|
+
return subspace;
|
|
710
600
|
});
|
|
711
601
|
}
|
|
712
602
|
assertPushable(branch) {
|
|
713
603
|
if (this.pushableBranches.includes(branch))
|
|
714
604
|
return;
|
|
715
|
-
throw new Error(`Branch "${branch}" is not pushable. ${
|
|
605
|
+
throw new Error(`Branch "${branch}" is not pushable. ${SubspaceConfig.fileName} allows: ${this.pushableBranches.join(", ")}`);
|
|
716
606
|
}
|
|
717
607
|
}
|
|
718
608
|
|
|
719
|
-
// pkgs/@akanjs/cli/
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
609
|
+
// pkgs/@akanjs/cli/subspace/subspace.runner.ts
|
|
610
|
+
var loadPrompts = async () => await import("@inquirer/prompts");
|
|
611
|
+
|
|
612
|
+
class SubspaceRunner extends runner("subspace") {
|
|
613
|
+
static multiTargetActions = ["status", "push"];
|
|
614
|
+
async config(workspace) {
|
|
615
|
+
const config = await SubspaceConfig.from(workspace);
|
|
723
616
|
if (!config)
|
|
724
|
-
throw new Error(`No ${
|
|
725
|
-
|
|
617
|
+
throw new Error(`No ${SubspaceConfig.fileName} at the workspace root. Declare the customer repos there first.`);
|
|
618
|
+
return config;
|
|
619
|
+
}
|
|
620
|
+
async subspaces(workspace, names) {
|
|
621
|
+
const config = await this.config(workspace);
|
|
622
|
+
const subspaces = config.select(names).map((declaration) => new Subspace(workspace, config, declaration));
|
|
726
623
|
const branch = (await workspace.spawn("git", ["rev-parse", "--abbrev-ref", "HEAD"])).trim();
|
|
727
|
-
return { config,
|
|
624
|
+
return { config, subspaces, branch };
|
|
625
|
+
}
|
|
626
|
+
async selectNames(workspace, action) {
|
|
627
|
+
const config = await this.config(workspace);
|
|
628
|
+
const interactive = process.stdin.isTTY === true && process.stdout.isTTY === true;
|
|
629
|
+
return await SubspaceRunner.chooseNames(config, action, { interactive });
|
|
630
|
+
}
|
|
631
|
+
static async chooseNames(config, action, { interactive, prompts }) {
|
|
632
|
+
const multiple = SubspaceRunner.multiTargetActions.includes(action);
|
|
633
|
+
if (!config.subspaces.length)
|
|
634
|
+
throw new Error(`${SubspaceConfig.fileName} declares no subspaces.`);
|
|
635
|
+
if (!interactive) {
|
|
636
|
+
throw new Error(multiple ? `\`akan subspace ${action}\` needs a subspace name when it cannot prompt. Pass one, or run \`akan subspace ${action}-all\` for every subspace.` : `\`akan subspace ${action}\` needs a subspace name when it cannot prompt \u2014 it is reviewed one repo at a time.`);
|
|
637
|
+
}
|
|
638
|
+
const { checkbox, select } = prompts ?? await loadPrompts();
|
|
639
|
+
const choices = config.subspaces.map((subspace) => ({
|
|
640
|
+
name: subspace.name,
|
|
641
|
+
value: subspace.name,
|
|
642
|
+
description: `${subspace.repo} \xB7 ${subspace.apps.join(", ")}`
|
|
643
|
+
}));
|
|
644
|
+
if (multiple)
|
|
645
|
+
return await checkbox({ message: `Select subspaces to ${action}`, choices, required: true });
|
|
646
|
+
return [await select({ message: `Select a subspace to ${action}`, choices })];
|
|
728
647
|
}
|
|
729
648
|
async status(workspace, names) {
|
|
730
|
-
const {
|
|
649
|
+
const { subspaces, branch } = await this.subspaces(workspace, names);
|
|
731
650
|
const statuses = [];
|
|
732
|
-
for (const
|
|
733
|
-
statuses.push(await
|
|
651
|
+
for (const subspace of subspaces)
|
|
652
|
+
statuses.push(await subspace.status(branch));
|
|
734
653
|
return statuses;
|
|
735
654
|
}
|
|
736
655
|
async push(workspace, names, { verify } = {}) {
|
|
737
|
-
const { config,
|
|
656
|
+
const { config, subspaces, branch } = await this.subspaces(workspace, names);
|
|
738
657
|
config.assertPushable(branch);
|
|
739
658
|
const results = [];
|
|
740
|
-
for (const
|
|
741
|
-
results.push(await
|
|
659
|
+
for (const subspace of subspaces)
|
|
660
|
+
results.push(await subspace.push(branch, { verify }));
|
|
742
661
|
return results;
|
|
743
662
|
}
|
|
744
663
|
async diff(workspace, name, filter) {
|
|
745
|
-
const {
|
|
746
|
-
const [
|
|
747
|
-
if (!
|
|
748
|
-
throw new Error(`Unknown
|
|
749
|
-
return await
|
|
750
|
-
}
|
|
751
|
-
async check(workspace, { staged } = {}) {
|
|
752
|
-
return await new FleetGuard(workspace).check({ staged });
|
|
664
|
+
const { subspaces, branch } = await this.subspaces(workspace, [name]);
|
|
665
|
+
const [subspace] = subspaces;
|
|
666
|
+
if (!subspace)
|
|
667
|
+
throw new Error(`Unknown subspace "${name}"`);
|
|
668
|
+
return await subspace.diff(branch, filter);
|
|
753
669
|
}
|
|
754
670
|
async pull(workspace, name, { adoptLibs } = {}) {
|
|
755
|
-
const {
|
|
756
|
-
const [
|
|
757
|
-
if (!
|
|
758
|
-
throw new Error(`Unknown
|
|
759
|
-
return await
|
|
671
|
+
const { subspaces, branch } = await this.subspaces(workspace, [name]);
|
|
672
|
+
const [subspace] = subspaces;
|
|
673
|
+
if (!subspace)
|
|
674
|
+
throw new Error(`Unknown subspace "${name}"`);
|
|
675
|
+
return await subspace.pull(branch, { adoptLibs });
|
|
760
676
|
}
|
|
761
677
|
}
|
|
762
678
|
|
|
763
|
-
// pkgs/@akanjs/cli/
|
|
764
|
-
class
|
|
679
|
+
// pkgs/@akanjs/cli/subspace/subspace.script.ts
|
|
680
|
+
class SubspaceScript extends script("subspace", [SubspaceRunner]) {
|
|
681
|
+
async select(workspace, action) {
|
|
682
|
+
return await this.subspaceRunner.selectNames(workspace, action);
|
|
683
|
+
}
|
|
765
684
|
async status(workspace, names, format = "text") {
|
|
766
|
-
const spinner = workspace.spinning("Checking
|
|
767
|
-
const statuses = await this.
|
|
685
|
+
const spinner = workspace.spinning("Checking subspaces...");
|
|
686
|
+
const statuses = await this.subspaceRunner.status(workspace, names);
|
|
768
687
|
const drifted = statuses.filter((status) => status.driftedLibs.length).length;
|
|
769
|
-
spinner.succeed(`Checked ${statuses.length}
|
|
770
|
-
Logger.rawLog(format === "json" ? JSON.stringify(statuses, null, 2) :
|
|
688
|
+
spinner.succeed(`Checked ${statuses.length} subspace(s) (${drifted} with library drift)`);
|
|
689
|
+
Logger.rawLog(format === "json" ? JSON.stringify(statuses, null, 2) : formatSubspaceStatuses(statuses));
|
|
771
690
|
}
|
|
772
691
|
async push(workspace, names, { verify, format }) {
|
|
773
|
-
const spinner = workspace.spinning("Pushing to
|
|
774
|
-
const results = await this.
|
|
692
|
+
const spinner = workspace.spinning("Pushing to subspaces...");
|
|
693
|
+
const results = await this.subspaceRunner.push(workspace, names, { verify });
|
|
775
694
|
const pushed = results.filter((result) => result.outcome === "pushed").length;
|
|
776
|
-
spinner.succeed(`Pushed ${pushed} of ${results.length}
|
|
777
|
-
Logger.rawLog(format === "json" ? JSON.stringify(results, null, 2) :
|
|
695
|
+
spinner.succeed(`Pushed ${pushed} of ${results.length} subspace(s)`);
|
|
696
|
+
Logger.rawLog(format === "json" ? JSON.stringify(results, null, 2) : formatSubspacePushResults(results));
|
|
778
697
|
if (results.some((result) => result.outcome === "refused"))
|
|
779
698
|
process.exitCode = 1;
|
|
780
699
|
}
|
|
781
700
|
async diff(workspace, name, filter, format = "text") {
|
|
782
701
|
const spinner = workspace.spinning(`Diffing ${name}...`);
|
|
783
|
-
const result = await this.
|
|
702
|
+
const result = await this.subspaceRunner.diff(workspace, name, filter);
|
|
784
703
|
const total = result.app.files.length + result.libs.files.length;
|
|
785
704
|
spinner.succeed(`${total} file(s) differ (${result.libs.files.length} in shared libraries)`);
|
|
786
|
-
Logger.rawLog(format === "json" ? JSON.stringify(result, null, 2) :
|
|
787
|
-
}
|
|
788
|
-
async check(workspace, { staged, warn, format }) {
|
|
789
|
-
const report = await this.fleetRunner.check(workspace, { staged });
|
|
790
|
-
Logger.rawLog(format === "json" ? JSON.stringify(report, null, 2) : formatFleetGuardReport(report, { warn }));
|
|
791
|
-
const failed = !!report.violations.length || !!report.driftedLibs.length;
|
|
792
|
-
if (failed && !warn)
|
|
793
|
-
process.exitCode = 1;
|
|
705
|
+
Logger.rawLog(format === "json" ? JSON.stringify(result, null, 2) : formatSubspaceDiff(result));
|
|
794
706
|
}
|
|
795
707
|
async pull(workspace, name, { adoptLibs, format }) {
|
|
796
708
|
const spinner = workspace.spinning(`Pulling customer commits from ${name}...`);
|
|
797
|
-
const result = await this.
|
|
709
|
+
const result = await this.subspaceRunner.pull(workspace, name, { adoptLibs });
|
|
798
710
|
spinner.succeed(`${result.incoming.length} customer commit(s), ${result.applied.length} file(s) applied`);
|
|
799
|
-
Logger.rawLog(format === "json" ? JSON.stringify(result, null, 2) :
|
|
711
|
+
Logger.rawLog(format === "json" ? JSON.stringify(result, null, 2) : formatSubspacePullResult(result));
|
|
800
712
|
}
|
|
801
713
|
}
|
|
802
714
|
|
|
803
|
-
// pkgs/@akanjs/cli/
|
|
804
|
-
class
|
|
805
|
-
|
|
806
|
-
desc: "
|
|
715
|
+
// pkgs/@akanjs/cli/subspace/subspace.command.ts
|
|
716
|
+
class SubspaceCommand extends command("subspace", [SubspaceScript], ({ public: target }) => ({
|
|
717
|
+
subspace: target({ desc: "Mirror apps and libraries between this workspace and the customer repos it serves" }).arg("action", String, {
|
|
718
|
+
desc: "subspace action; status-all and push-all take every declared subspace",
|
|
807
719
|
default: "status",
|
|
808
|
-
enum: ["status", "diff", "push", "
|
|
809
|
-
}).arg("
|
|
720
|
+
enum: ["status", "status-all", "diff", "push", "push-all", "pull"]
|
|
721
|
+
}).arg("subspace", String, {
|
|
722
|
+
desc: "subspace name; asks which one(s) when omitted, and is refused by status-all / push-all",
|
|
723
|
+
nullable: true
|
|
724
|
+
}).with(Workspace).option("format", String, { desc: "output format", default: "text", enum: ["text", "json"] }).option("verify", Boolean, {
|
|
810
725
|
flag: "y",
|
|
811
|
-
desc: "run bun install + akan sync in the
|
|
726
|
+
desc: "run bun install + akan sync in the subspace before committing",
|
|
812
727
|
default: true
|
|
813
|
-
}).option("adoptLibs", Boolean, { desc: "pull: also apply the
|
|
814
|
-
const names = spoke ? [spoke] : [];
|
|
728
|
+
}).option("adoptLibs", Boolean, { desc: "pull: also apply the subspace's library edits", default: false }).option("path", String, { desc: "diff: limit to one path", nullable: true }).exec(async function(action, subspace, workspace, format, verify, adoptLibs, filePath) {
|
|
815
729
|
const output = format;
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
730
|
+
const all = action === "status-all" || action === "push-all";
|
|
731
|
+
const base = all ? action.slice(0, -"-all".length) : action;
|
|
732
|
+
if (base !== "status" && base !== "diff" && base !== "push" && base !== "pull")
|
|
733
|
+
throw new Error(`Unknown subspace action: ${action}. Use status, status-all, diff, push, push-all or pull.`);
|
|
734
|
+
if (all && subspace)
|
|
735
|
+
throw new Error(`\`akan subspace ${action}\` takes no name. Run \`akan subspace ${base} ${subspace}\` instead.`);
|
|
736
|
+
const names = subspace ? [subspace] : all ? [] : await this.subspaceScript.select(workspace, base);
|
|
737
|
+
if (base === "status")
|
|
738
|
+
await this.subspaceScript.status(workspace, names, output);
|
|
739
|
+
else if (base === "push")
|
|
740
|
+
await this.subspaceScript.push(workspace, names, { verify, format: output });
|
|
741
|
+
else {
|
|
742
|
+
const [name] = names;
|
|
743
|
+
if (!name)
|
|
744
|
+
throw new Error(`\`akan subspace ${base}\` needs a name \u2014 it is reviewed one repo at a time.`);
|
|
745
|
+
if (base === "diff")
|
|
746
|
+
await this.subspaceScript.diff(workspace, name, filePath, output);
|
|
827
747
|
else
|
|
828
|
-
await this.
|
|
829
|
-
}
|
|
830
|
-
throw new Error(`Unknown fleet action: ${action}. Use status, diff, push, pull or check.`);
|
|
748
|
+
await this.subspaceScript.pull(workspace, name, { adoptLibs, format: output });
|
|
749
|
+
}
|
|
831
750
|
})
|
|
832
751
|
})) {
|
|
833
752
|
}
|
|
834
753
|
export {
|
|
835
|
-
|
|
754
|
+
SubspaceCommand
|
|
836
755
|
};
|
|
@@ -40,7 +40,7 @@ import {
|
|
|
40
40
|
} from "./index-5n0hf8r9.js";
|
|
41
41
|
import {
|
|
42
42
|
ApplicationScript
|
|
43
|
-
} from "./index-
|
|
43
|
+
} from "./index-m7e0f1sy.js";
|
|
44
44
|
import {
|
|
45
45
|
LibraryScript
|
|
46
46
|
} from "./index-xxe90kqy.js";
|
|
@@ -52,10 +52,10 @@ import {
|
|
|
52
52
|
import {
|
|
53
53
|
getNpmRegistryUrl,
|
|
54
54
|
getLatestPackageVersion
|
|
55
|
-
} from "./index-
|
|
55
|
+
} from "./index-1tgp56ke.js";
|
|
56
56
|
import {
|
|
57
57
|
CloudScript
|
|
58
|
-
} from "./index-
|
|
58
|
+
} from "./index-ypvdw8nr.js";
|
|
59
59
|
import {
|
|
60
60
|
ContextScript
|
|
61
61
|
} from "./index-dq6wq1tv.js";
|
|
@@ -66,7 +66,7 @@ import"./index-k86zc21w.js";
|
|
|
66
66
|
import"./index-1kzsn94d.js";
|
|
67
67
|
import"./index-cd5x18qh.js";
|
|
68
68
|
import"./index-v0s24t8j.js";
|
|
69
|
-
import"./index-
|
|
69
|
+
import"./index-m9c7k0gm.js";
|
|
70
70
|
|
|
71
71
|
// pkgs/@akanjs/cli/workspace/workspace.runner.ts
|
|
72
72
|
import path from "path";
|