@akanjs/devkit 3.0.0-beta.2 → 3.0.0-beta.21
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.ko.md +2 -2
- package/README.md +3 -2
- package/abstractDoc.test.ts +6 -15
- package/abstractDoc.ts +6 -8
- package/agentsIndex.test.ts +4 -7
- package/agentsIndex.ts +6 -22
- package/akanApp/BackendImportGraph.test.ts +8 -22
- package/akanApp/BackendImportGraph.ts +1 -20
- package/akanApp/akanApp.host.test.ts +1 -3
- package/akanApp/akanApp.host.ts +82 -254
- package/akanApp/devHostPolicy.test.ts +10 -34
- package/akanApp/devHostPolicy.ts +12 -98
- package/akanConfig/akanConfig.test.ts +142 -106
- package/akanConfig/akanConfig.ts +147 -191
- package/akanConfig/types.ts +4 -3
- package/akanContext.ts +110 -160
- package/akanMcpContract.ts +28 -73
- package/appSelectionMemory.ts +1 -4
- package/applicationBuildReporter.ts +37 -5
- package/applicationBuildRunner.test.ts +5 -3
- package/applicationBuildRunner.ts +54 -61
- package/applicationTestPreload.ts +3 -12
- package/artifact/implicitRootLayout.test.ts +32 -30
- package/artifact/implicitRootLayout.ts +58 -106
- package/artifact/routeSeedIndex.test.ts +7 -6
- package/artifact/routeSeedIndex.ts +11 -44
- package/biome.base.json +32 -1
- package/biomeBase.ts +2 -5
- package/cloud/cloudApi.test.ts +202 -0
- package/cloud/cloudApi.ts +77 -39
- package/cloud/configLock.test.ts +64 -0
- package/cloud/configLock.ts +96 -0
- package/cloud/constants.ts +26 -5
- package/cloud/globalConfig.ts +70 -32
- package/codeAgent/agent/AkanCodeServices.ts +97 -0
- package/codeAgent/agent/AkanContextFiles.ts +94 -0
- package/codeAgent/agent/CodeAgent.ts +551 -0
- package/codeAgent/agent/CodeAgentAsks.ts +66 -0
- package/codeAgent/agent/CodeAgentEventMapper.ts +225 -0
- package/codeAgent/agent/CodeAgentGate.ts +93 -0
- package/codeAgent/agent/CodeAgentProxy.ts +82 -0
- package/codeAgent/agent/CodeAgentRpcHost.ts +103 -0
- package/codeAgent/agent/CodeAgentRpcListener.ts +109 -0
- package/codeAgent/agent/CodeAgentStreamPrinter.ts +86 -0
- package/codeAgent/agent/CodeAgentSuspended.ts +85 -0
- package/codeAgent/agent/CodeAgentUi.ts +47 -0
- package/codeAgent/agent/CodeMailbox.ts +148 -0
- package/codeAgent/agent/CodeSessionFork.ts +56 -0
- package/codeAgent/agent/CodeSessionIndex.ts +78 -0
- package/codeAgent/agent/SubagentPool.ts +214 -0
- package/codeAgent/agent/akanCodeModel.ts +47 -0
- package/codeAgent/agent/akanCodePaths.ts +31 -0
- package/codeAgent/agent/akanContextFiles.test.ts +114 -0
- package/codeAgent/agent/akanSystemPrompt.ts +46 -0
- package/codeAgent/agent/codeAgent.test.ts +178 -0
- package/codeAgent/agent/codeAgentContract.test.ts +523 -0
- package/codeAgent/agent/codeAgentProxy.test.ts +74 -0
- package/codeAgent/agent/codeAgentRpcListener.test.ts +113 -0
- package/codeAgent/agent/codeAgentSuspended.test.ts +47 -0
- package/codeAgent/agent/codeMailbox.test.ts +76 -0
- package/codeAgent/agent/codeSessionFork.test.ts +58 -0
- package/codeAgent/agent/codeSessionIndex.test.ts +77 -0
- package/codeAgent/agent/consoleToStderr.ts +8 -0
- package/codeAgent/agent/jsonLines.ts +29 -0
- package/codeAgent/agent/stringArg.ts +5 -0
- package/codeAgent/feedback/DevLogFeedback.ts +82 -0
- package/codeAgent/feedback/PreviewChrome.ts +82 -0
- package/codeAgent/feedback/PreviewFeedback.ts +46 -0
- package/codeAgent/feedback/PreviewView.ts +95 -0
- package/codeAgent/feedback/TurnFeedback.ts +98 -0
- package/codeAgent/feedback/VerifyFeedback.ts +40 -0
- package/codeAgent/feedback/previewChrome.test.ts +73 -0
- package/codeAgent/index.ts +34 -0
- package/codeAgent/skills/akan-code/SKILL.md +130 -0
- package/codeAgent/skills/akan-debug/SKILL.md +42 -0
- package/codeAgent/skills/akan-deps/SKILL.md +50 -0
- package/codeAgent/skills/akan-err/SKILL.md +39 -0
- package/codeAgent/skills/akan-module/SKILL.md +45 -0
- package/codeAgent/skills/akan-scaffold/SKILL.md +76 -0
- package/codeAgent/skills/akan-scalar/SKILL.md +52 -0
- package/codeAgent/skills/akan-service/SKILL.md +34 -0
- package/codeAgent/skills/akan-store/SKILL.md +67 -0
- package/codeAgent/skills/akan-ui/SKILL.md +63 -0
- package/codeAgent/skills/akan-validate/SKILL.md +44 -0
- package/codeAgent/tools/AkanCli.ts +50 -0
- package/codeAgent/tools/AkanCodePlugins.ts +136 -0
- package/codeAgent/tools/AkanEditScope.ts +48 -0
- package/codeAgent/tools/AkanEnvKeys.ts +50 -0
- package/codeAgent/tools/AkanToolPack.ts +92 -0
- package/codeAgent/tools/AkanVerifier.ts +48 -0
- package/codeAgent/tools/AskToolPack.ts +99 -0
- package/codeAgent/tools/MailToolPack.ts +76 -0
- package/codeAgent/tools/McpClient.ts +187 -0
- package/codeAgent/tools/McpOAuth.ts +233 -0
- package/codeAgent/tools/McpOAuthLoopback.ts +64 -0
- package/codeAgent/tools/McpServerConfig.ts +129 -0
- package/codeAgent/tools/McpSignIn.ts +158 -0
- package/codeAgent/tools/McpTokenStore.ts +81 -0
- package/codeAgent/tools/McpToolPack.ts +199 -0
- package/codeAgent/tools/SessionToolPack.ts +91 -0
- package/codeAgent/tools/WebToolPack.ts +147 -0
- package/codeAgent/tools/akanCodePlugins.test.ts +677 -0
- package/codeAgent/tools/askToolPack.test.ts +116 -0
- package/codeAgent/tools/mcpOAuth.test.ts +612 -0
- package/codeAgent/tools/mcpServerConfig.test.ts +212 -0
- package/codegenLock.test.ts +4 -13
- package/codegenLock.ts +3 -17
- package/commandDecorators/argMeta.ts +1 -5
- package/commandDecorators/camelToKebabCase.ts +1 -0
- package/commandDecorators/command.ts +46 -102
- package/commandDecorators/commandBuilder.ts +30 -61
- package/commandDecorators/commandDecorators.test.ts +2 -10
- package/commandDecorators/dependencyBuilder.ts +10 -1
- package/commandDecorators/helpFormatter.ts +68 -145
- package/commandDecorators/index.ts +0 -2
- package/commandDecorators/targetMeta.ts +9 -7
- package/createTunnel.ts +7 -14
- package/dependencyScanner.test.ts +4 -16
- package/dependencyScanner.ts +31 -80
- package/devkitUtils.test.ts +48 -20
- package/executors.linkFallback.test.ts +103 -0
- package/executors.test.ts +132 -106
- package/executors.ts +195 -496
- package/fileEditor.ts +9 -37
- package/{getDirname.test.ts → fileSys.test.ts} +5 -3
- package/fileSys.ts +6 -8
- package/formSetterScanner.ts +3 -16
- package/frontendBuild/allRoutesBuilder.ts +7 -21
- package/frontendBuild/autoImportSync.test.ts +27 -38
- package/frontendBuild/autoImportSync.ts +31 -65
- package/frontendBuild/buildRouteClient.test.ts +62 -37
- package/frontendBuild/bundleDefine.ts +12 -0
- package/frontendBuild/clientBuildTypes.ts +5 -36
- package/frontendBuild/clientEntriesBundler.ts +9 -40
- package/frontendBuild/clientEntryDiscovery.ts +6 -41
- package/frontendBuild/csrArtifactBuilder.ts +6 -25
- package/frontendBuild/cssCandidateCache.test.ts +0 -6
- package/frontendBuild/cssCandidateCache.ts +6 -41
- package/frontendBuild/cssCompiler.ts +29 -78
- package/frontendBuild/cssImportResolver.ts +6 -19
- package/frontendBuild/devChangePlanner.ts +7 -10
- package/frontendBuild/devGeneratedIndexSync.test.ts +12 -27
- package/frontendBuild/devGeneratedIndexSync.ts +1 -4
- package/frontendBuild/fontOptimizer.test.ts +6 -14
- package/frontendBuild/fontOptimizer.ts +15 -72
- package/frontendBuild/fontPruner.test.ts +5 -10
- package/frontendBuild/fontPruner.ts +5 -20
- package/frontendBuild/frontendBuild.test.ts +46 -69
- package/frontendBuild/hmrChangeClassifier.ts +2 -2
- package/frontendBuild/hmrWatcher.test.ts +8 -32
- package/frontendBuild/hmrWatcher.ts +14 -71
- package/frontendBuild/pagesBundleBuilder.ts +4 -29
- package/frontendBuild/precompressArtifacts.ts +5 -18
- package/frontendBuild/routeClientBuilder.ts +23 -46
- package/frontendBuild/routesManifestArtifactSerializer.ts +5 -36
- package/frontendBuild/sourceMtimeIndex.test.ts +41 -97
- package/frontendBuild/sourceMtimeIndex.ts +19 -130
- package/frontendBuild/ssrBaseArtifactBuilder.ts +2 -8
- package/frontendBuild/styleContract.ts +1 -8
- package/frontendBuild/themeValidator.ts +4 -26
- package/frontendBuild/watchRootResolver.test.ts +23 -42
- package/frontendBuild/watchRootResolver.ts +5 -27
- package/getRelatedCnsts.ts +12 -92
- package/incrementalBuilder/buildBatch.proc.ts +29 -58
- package/incrementalBuilder/buildBatchProtocol.ts +8 -36
- package/incrementalBuilder/buildBatchRunner.test.ts +34 -0
- package/incrementalBuilder/buildBatchRunner.ts +10 -31
- package/incrementalBuilder/builderChannel.test.ts +24 -49
- package/incrementalBuilder/builderChannel.ts +6 -37
- package/incrementalBuilder/builderRequestRouter.test.ts +0 -8
- package/incrementalBuilder/builderRequestRouter.ts +5 -24
- package/incrementalBuilder/devWatchBatch.test.ts +21 -24
- package/incrementalBuilder/incrementalBuilder.host.test.ts +18 -55
- package/incrementalBuilder/incrementalBuilder.host.ts +18 -76
- package/incrementalBuilder/incrementalBuilder.proc.test.ts +81 -0
- package/incrementalBuilder/incrementalBuilder.proc.ts +49 -161
- package/index.ts +5 -19
- package/integration/devResourceProbe.ts +50 -56
- package/integration/devStability.integration.test.ts +47 -194
- package/integration/devStabilityHarness.test.ts +13 -28
- package/integration/devStabilityHarness.ts +65 -330
- package/integration/ssrMemoryProbe.ts +41 -167
- package/libSource.test.ts +4 -15
- package/libSource.ts +19 -31
- package/lint/__fixtures__/no-document-cookie/bad.tsx +4 -0
- package/lint/__fixtures__/no-document-cookie/good.tsx +7 -0
- package/lint/__fixtures__/no-web-only-api-outside-webkit/bad.tsx +7 -0
- package/lint/__fixtures__/no-web-only-api-outside-webkit/good.tsx +11 -0
- package/lint/__fixtures__/no-web-storage/bad.tsx +6 -0
- package/lint/__fixtures__/no-web-storage/good.tsx +7 -0
- package/lint/gritRules.test.ts +8 -24
- package/lint/no-deep-internal-import.grit +2 -2
- package/lint/no-document-cookie.grit +29 -0
- package/lint/no-redeclare-predefined-endpoint.grit +2 -1
- package/lint/no-web-only-api-outside-webkit.grit +44 -0
- package/lint/no-web-storage.grit +30 -0
- package/linter.test.ts +1 -2
- package/linter.ts +26 -116
- package/mobile/index.ts +6 -0
- package/mobile/mobileTarget.ts +16 -13
- package/mobile/nativeApi.test.ts +30 -0
- package/mobile/nativeApi.ts +40 -0
- package/mobile/nativeApp.test.ts +85 -0
- package/mobile/nativeApp.ts +210 -0
- package/mobile/nativeConfig.test.ts +199 -0
- package/mobile/nativeConfig.ts +184 -0
- package/mobile/nativeWebDir.test.ts +80 -0
- package/mobile/nativeWebDir.ts +62 -0
- package/mobile/placeholderAppId.test.ts +13 -0
- package/mobile/placeholderAppId.ts +16 -0
- package/mobile/usageDescriptions.test.ts +27 -0
- package/mobile/usageDescriptions.ts +24 -0
- package/package.json +10 -10
- package/packageExports.test.ts +3 -18
- package/packageExportsMap.ts +3 -19
- package/piDependencyPins.test.ts +46 -0
- package/platformTest/BunTestReport.test.ts +60 -0
- package/platformTest/BunTestReport.ts +65 -0
- package/platformTest/IdleWatchedProcess.test.ts +56 -0
- package/platformTest/IdleWatchedProcess.ts +73 -0
- package/platformTest/LinuxDockerTarget.ts +105 -0
- package/platformTest/PlatformTestRun.test.ts +64 -0
- package/platformTest/PlatformTestRun.ts +141 -0
- package/platformTest/PlatformTestTarget.ts +142 -0
- package/platformTest/TestSnapshot.test.ts +72 -0
- package/platformTest/TestSnapshot.ts +103 -0
- package/platformTest/WindowsSshTarget.ts +151 -0
- package/prompter.ts +3 -13
- package/qualityScanner.test.ts +9 -26
- package/qualityScanner.ts +200 -286
- package/recipeScanner.test.ts +7 -12
- package/recipeScanner.ts +9 -38
- package/repoIdentity.ts +2 -9
- package/routeSourceValidator.test.ts +0 -1
- package/routeSourceValidator.ts +7 -42
- package/scanInfo.ts +91 -225
- package/selectModel.ts +1 -2
- package/semver.ts +3 -5
- package/slicePlanner.test.ts +6 -24
- package/slicePlanner.ts +11 -32
- package/spinner.test.ts +3 -8
- package/spinner.ts +4 -16
- package/sshTunnel.ts +1 -6
- package/ssrScanner.test.ts +1 -6
- package/ssrScanner.ts +103 -113
- package/stripAnsi.ts +3 -0
- package/subspace.test.ts +30 -25
- package/subspace.ts +81 -115
- package/subspaceConfig.ts +11 -9
- package/testHelpers.ts +194 -0
- package/transforms/asyncDefaultExportDetector.ts +3 -11
- package/transforms/barrelAnalyzer.ts +43 -135
- package/transforms/barrelImportsPlugin.ts +38 -175
- package/transforms/externalizeFrameworkPlugin.test.ts +39 -118
- package/transforms/externalizeFrameworkPlugin.ts +13 -87
- package/transforms/moduleSyntax.ts +32 -0
- package/transforms/rscUseClientTransform.ts +35 -20
- package/transforms/transforms.test.ts +153 -44
- package/transforms/tsconfigPackageResolver.test.ts +6 -24
- package/transforms/useClientBundlePlugin.ts +2 -22
- package/typeChecker.ts +7 -63
- package/types.ts +1 -0
- package/uploadRelease.ts +10 -25
- package/workflow/artifacts.ts +18 -0
- package/workflow/executor.test.ts +1 -12
- package/workflow/executor.ts +101 -110
- package/workflow/index.ts +0 -1
- package/workflow/moduleIndex.test.ts +4 -15
- package/workflow/moduleIndex.ts +9 -49
- package/workflow/plan.ts +18 -25
- package/workflow/render.ts +80 -96
- package/workflow/rolloutGate.test.ts +1 -1
- package/workflow/source.ts +52 -69
- package/workflow/sourceAst.ts +50 -0
- package/workspaceLayout.test.ts +13 -3
- package/workspaceLayout.ts +20 -23
- package/abstractCompactor.ts +0 -112
- package/aiEditor.test.ts +0 -60
- package/aiEditor.ts +0 -387
- package/applicationReleasePackager.ts +0 -209
- package/capacitorApp.test.ts +0 -372
- package/capacitorApp.ts +0 -1648
- package/commandDecorators/commandMeta.ts +0 -7
- package/commandDecorators/types.ts +0 -10
- package/getDirname.ts +0 -4
- package/guideline.ts +0 -19
- package/local/optimize-resource/ipcprobe/child.ts +0 -1
- package/local/optimize-resource/ipcprobe/parent.ts +0 -11
- package/workflow/primitive.ts +0 -59
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import type { PlatformGatePolicy, WindowsTestTargetConfig } from "../cloud/constants";
|
|
5
|
+
import { IdleWatchedProcess } from "./IdleWatchedProcess";
|
|
6
|
+
import { type PlatformRunContext, PlatformTestTarget } from "./PlatformTestTarget";
|
|
7
|
+
|
|
8
|
+
export class WindowsSshTarget extends PlatformTestTarget {
|
|
9
|
+
static readonly utmctlPath = "/Applications/UTM.app/Contents/MacOS/utmctl";
|
|
10
|
+
static readonly defaultWorkRoot = "C:\\akan-test";
|
|
11
|
+
//* arm64 has neither a sharp@0.32 prebuilt nor a Chrome for puppeteer's postinstall; x64 users get both.
|
|
12
|
+
static readonly defaultTolerateScriptFailures = ["sharp", "puppeteer"];
|
|
13
|
+
static readonly #runDirPattern = "^\\d{8}-\\d{6}$";
|
|
14
|
+
static readonly #keptRuns = 2;
|
|
15
|
+
|
|
16
|
+
readonly platform = "windows";
|
|
17
|
+
readonly policy: PlatformGatePolicy;
|
|
18
|
+
protected override readonly tolerateScriptFailures: string[];
|
|
19
|
+
readonly #config: WindowsTestTargetConfig;
|
|
20
|
+
#host: string;
|
|
21
|
+
#runDir: string | null = null;
|
|
22
|
+
|
|
23
|
+
constructor(config: WindowsTestTargetConfig) {
|
|
24
|
+
super();
|
|
25
|
+
this.#config = config;
|
|
26
|
+
this.#host = process.env.AKAN_TEST_WINDOWS_HOST ?? config.host;
|
|
27
|
+
this.policy = config.policy ?? "warn";
|
|
28
|
+
this.tolerateScriptFailures = config.tolerateScriptFailures ?? WindowsSshTarget.defaultTolerateScriptFailures;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
get #workRoot() {
|
|
32
|
+
return this.#config.workRoot ?? WindowsSshTarget.defaultWorkRoot;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
protected async prepare(context: PlatformRunContext) {
|
|
36
|
+
await this.#connect();
|
|
37
|
+
const bunVersion = (await this.#ssh("bun --version")).stdout.trim();
|
|
38
|
+
if (bunVersion !== Bun.version)
|
|
39
|
+
throw new Error(
|
|
40
|
+
`the Windows host runs Bun ${bunVersion || "(none)"}, this host ${Bun.version} — align them first`,
|
|
41
|
+
);
|
|
42
|
+
if ((await this.#ssh("git --version")).exitCode !== 0) throw new Error("git is not installed on the Windows host");
|
|
43
|
+
const runDir = `${this.#workRoot}\\${context.runId}`;
|
|
44
|
+
const mkdir = await this.#ssh(`New-Item -ItemType Directory -Force -Path '${runDir}\\w' | Out-Null`);
|
|
45
|
+
if (mkdir.exitCode !== 0) throw new Error(`could not create ${runDir}: ${mkdir.stderr.trim()}`);
|
|
46
|
+
this.#runDir = runDir;
|
|
47
|
+
for (const [local, remoteName] of [
|
|
48
|
+
[context.snapshot.tarPath, "src.tar"],
|
|
49
|
+
[context.envPath, "env"],
|
|
50
|
+
] as const) {
|
|
51
|
+
const copied = await WindowsSshTarget.spawnText([
|
|
52
|
+
...this.#scpBase(),
|
|
53
|
+
local,
|
|
54
|
+
this.#remoteScpPath(`${runDir}\\${remoteName}`),
|
|
55
|
+
]);
|
|
56
|
+
if (copied.exitCode !== 0) throw new Error(`scp ${remoteName} failed: ${copied.stderr.trim()}`);
|
|
57
|
+
}
|
|
58
|
+
const extract = await this.exec(
|
|
59
|
+
"tar -xf ..\\src.tar && copy /Y ..\\env .env >nul && git init -q",
|
|
60
|
+
path.join(context.logDir, this.platform, "extract.log"),
|
|
61
|
+
PlatformTestTarget.installIdleMs,
|
|
62
|
+
);
|
|
63
|
+
if (extract.exitCode !== 0) throw new Error("extracting the snapshot on the Windows host failed");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
protected async exec(command: string, logPath: string, idleMs: number) {
|
|
67
|
+
const runDir = this.#runDir;
|
|
68
|
+
if (!runDir) throw new Error("the Windows run directory was not prepared");
|
|
69
|
+
let remotePid: string | null = null;
|
|
70
|
+
const script = [
|
|
71
|
+
"$ProgressPreference = 'SilentlyContinue'",
|
|
72
|
+
"[Console]::OutputEncoding = [Text.Encoding]::UTF8",
|
|
73
|
+
"'PID=' + $PID",
|
|
74
|
+
...Object.entries(PlatformTestTarget.gitIdentityEnv).map(([key, value]) => `$env:${key} = '${value}'`),
|
|
75
|
+
`Set-Location -LiteralPath '${runDir}\\w'`,
|
|
76
|
+
`cmd /c "${command} 2>&1"`,
|
|
77
|
+
"exit $LASTEXITCODE",
|
|
78
|
+
].join("; ");
|
|
79
|
+
return await new IdleWatchedProcess([...this.#sshBase(), script], {
|
|
80
|
+
logPath,
|
|
81
|
+
idleMs,
|
|
82
|
+
onOutput: (text) => {
|
|
83
|
+
remotePid ??= /PID=(\d+)/.exec(text)?.[1] ?? null;
|
|
84
|
+
},
|
|
85
|
+
onIdle: async () => {
|
|
86
|
+
if (remotePid) await this.#ssh(`taskkill /F /T /PID ${remotePid} | Out-Null`);
|
|
87
|
+
},
|
|
88
|
+
}).run();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
protected async cleanup() {
|
|
92
|
+
this.#runDir = null;
|
|
93
|
+
await this.#ssh(
|
|
94
|
+
[
|
|
95
|
+
`Get-ChildItem -LiteralPath '${this.#workRoot}' -Directory`,
|
|
96
|
+
`Where-Object { $_.Name -match '${WindowsSshTarget.#runDirPattern}' }`,
|
|
97
|
+
"Sort-Object Name -Descending",
|
|
98
|
+
`Select-Object -Skip ${WindowsSshTarget.#keptRuns}`,
|
|
99
|
+
"ForEach-Object { cmd /c rmdir /s /q $_.FullName }",
|
|
100
|
+
].join(" | "),
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async #connect() {
|
|
105
|
+
if ((await this.#ssh("exit 0")).exitCode === 0) return;
|
|
106
|
+
const vm = this.#config.utmVm;
|
|
107
|
+
if (!vm || !existsSync(WindowsSshTarget.utmctlPath))
|
|
108
|
+
throw new Error(`cannot reach ${this.#config.user}@${this.#host} over ssh`);
|
|
109
|
+
const status = (await WindowsSshTarget.spawnText([WindowsSshTarget.utmctlPath, "status", vm])).stdout.trim();
|
|
110
|
+
if (status !== "started") await WindowsSshTarget.spawnText([WindowsSshTarget.utmctlPath, "start", vm]);
|
|
111
|
+
for (let attempt = 0; attempt < 24; attempt++) {
|
|
112
|
+
const addresses = (await WindowsSshTarget.spawnText([WindowsSshTarget.utmctlPath, "ip-address", vm])).stdout;
|
|
113
|
+
const ipv4 = addresses.split(/\s+/).find((address) => /^\d+\.\d+\.\d+\.\d+$/.test(address));
|
|
114
|
+
if (ipv4) this.#host = ipv4;
|
|
115
|
+
if ((await this.#ssh("exit 0")).exitCode === 0) return;
|
|
116
|
+
await Bun.sleep(5_000);
|
|
117
|
+
}
|
|
118
|
+
throw new Error(`UTM VM "${vm}" did not answer ssh at ${this.#host}`);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
#sshOptions() {
|
|
122
|
+
const expand = (file: string) => (file.startsWith("~/") ? path.join(os.homedir(), file.slice(2)) : file);
|
|
123
|
+
return [
|
|
124
|
+
"-i",
|
|
125
|
+
expand(this.#config.identityFile),
|
|
126
|
+
...(this.#config.knownHostsFile ? ["-o", `UserKnownHostsFile=${expand(this.#config.knownHostsFile)}`] : []),
|
|
127
|
+
"-o",
|
|
128
|
+
"BatchMode=yes",
|
|
129
|
+
"-o",
|
|
130
|
+
"ConnectTimeout=8",
|
|
131
|
+
"-o",
|
|
132
|
+
"ServerAliveInterval=30",
|
|
133
|
+
];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
#sshBase() {
|
|
137
|
+
return ["ssh", ...this.#sshOptions(), `${this.#config.user}@${this.#host}`];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
#scpBase() {
|
|
141
|
+
return ["scp", "-q", ...this.#sshOptions()];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
#remoteScpPath(windowsPath: string) {
|
|
145
|
+
return `${this.#config.user}@${this.#host}:/${windowsPath.replaceAll("\\", "/")}`;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async #ssh(script: string) {
|
|
149
|
+
return await WindowsSshTarget.spawnText([...this.#sshBase(), script]);
|
|
150
|
+
}
|
|
151
|
+
}
|
package/prompter.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import fsPromise from "node:fs/promises";
|
|
2
2
|
import { input, select } from "@inquirer/prompts";
|
|
3
3
|
|
|
4
|
-
import { getDirname } from "./
|
|
5
|
-
import type { GuideGenerateJson } from "./guideline";
|
|
4
|
+
import { getDirname } from "./fileSys";
|
|
6
5
|
|
|
7
6
|
interface FileUpdateRequestProps {
|
|
8
7
|
context: string;
|
|
@@ -34,17 +33,8 @@ export class Prompter {
|
|
|
34
33
|
const guidelineRoot = await Prompter.#getGuidelineRoot();
|
|
35
34
|
return (await fsPromise.readdir(guidelineRoot)).filter((name) => !name.startsWith("_")).sort();
|
|
36
35
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const filePath = `${guidelineRoot}/${guideName}/${guideName}.generate.json`;
|
|
40
|
-
const guideJson = await fsPromise.readFile(filePath, "utf-8");
|
|
41
|
-
return JSON.parse(guideJson) as GuideGenerateJson;
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* The name is checked against the directory listing rather than interpolated straight into a path:
|
|
45
|
-
* it arrives from an MCP tool argument, and an unknown one has to name the valid set instead of
|
|
46
|
-
* surfacing an ENOENT that carries the host's own paths.
|
|
47
|
-
*/
|
|
36
|
+
// Checked against the listing, not interpolated into a path: the name is an MCP tool argument, and an unknown one
|
|
37
|
+
// must not surface an ENOENT carrying the host's paths.
|
|
48
38
|
static async getInstruction(guideName: string): Promise<string> {
|
|
49
39
|
const guideNames = await Prompter.listGuidelines();
|
|
50
40
|
if (!guideNames.includes(guideName))
|
package/qualityScanner.test.ts
CHANGED
|
@@ -1,32 +1,23 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
3
|
-
import os from "node:os";
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
4
2
|
import path from "node:path";
|
|
5
3
|
import { AbstractDoc } from "./abstractDoc";
|
|
6
4
|
import { AkanQualityScanner, type QualityScanResult } from "./qualityScanner";
|
|
5
|
+
import { tempDirs, writeText } from "./testHelpers";
|
|
7
6
|
|
|
8
|
-
const
|
|
7
|
+
const makeTempRoot = tempDirs("akan-quality-scanner-");
|
|
9
8
|
|
|
10
9
|
const makeWorkspace = async (files: Record<string, string>) => {
|
|
11
|
-
const root = await
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const absolutePath = path.join(root, filePath);
|
|
15
|
-
await mkdir(path.dirname(absolutePath), { recursive: true });
|
|
16
|
-
await writeFile(absolutePath, content);
|
|
17
|
-
}
|
|
10
|
+
const root = await makeTempRoot();
|
|
11
|
+
for (const [filePath, content] of Object.entries({ ".gitignore": "node_modules\n", ...files }))
|
|
12
|
+
await writeText(path.join(root, filePath), content);
|
|
18
13
|
return root;
|
|
19
14
|
};
|
|
20
15
|
|
|
21
16
|
const abstractOf = (lineNum: number) =>
|
|
22
17
|
["# post Abstract", ...Array.from({ length: lineNum - 1 }, (_, idx) => `- rule ${idx}`)].join("\n");
|
|
23
18
|
|
|
24
|
-
afterEach(async () => {
|
|
25
|
-
await Promise.all(tempRoots.splice(0).map((root) => rm(root, { recursive: true, force: true })));
|
|
26
|
-
});
|
|
27
|
-
|
|
28
19
|
describe("AkanQualityScanner abstract rule", () => {
|
|
29
|
-
test("warns on an abstract over the line limit and
|
|
20
|
+
test("warns on an abstract over the line limit and says how to trim it", async () => {
|
|
30
21
|
const root = await makeWorkspace({
|
|
31
22
|
"apps/demo/lib/post/post.abstract.md": abstractOf(AbstractDoc.maxLines + 1),
|
|
32
23
|
"apps/demo/lib/post/post.constant.ts": "export class Post {}\n",
|
|
@@ -41,7 +32,8 @@ describe("AkanQualityScanner abstract rule", () => {
|
|
|
41
32
|
expect(warnings).toHaveLength(1);
|
|
42
33
|
expect(warnings[0]?.file).toBe("apps/demo/lib/post/post.abstract.md");
|
|
43
34
|
expect(warnings[0]?.message).toContain(`${AbstractDoc.maxLines + 1} lines`);
|
|
44
|
-
expect(warnings[0]?.fix).toContain("
|
|
35
|
+
expect(warnings[0]?.fix).toContain("## Rules");
|
|
36
|
+
expect(warnings[0]?.fix).not.toContain("akan compact");
|
|
45
37
|
});
|
|
46
38
|
});
|
|
47
39
|
|
|
@@ -184,15 +176,6 @@ describe("AkanQualityScanner ssr rules", () => {
|
|
|
184
176
|
});
|
|
185
177
|
});
|
|
186
178
|
|
|
187
|
-
const signalOf = (entries: string) =>
|
|
188
|
-
[
|
|
189
|
-
`import { endpoint, slice } from "akanjs/signal";`,
|
|
190
|
-
`export class PostSlice extends slice(srv.post, { guards: {}, mcp: { get: true } }, (init) => ({`,
|
|
191
|
-
entries,
|
|
192
|
-
`})) {}`,
|
|
193
|
-
"",
|
|
194
|
-
].join("\n");
|
|
195
|
-
|
|
196
179
|
describe("AkanQualityScanner layout rules", () => {
|
|
197
180
|
test("flags an unknown app root file but not a facet entrypoint", async () => {
|
|
198
181
|
const root = await makeWorkspace({
|