@brainervirus/workit-core 1.0.4 → 1.0.5
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/package.json +1 -1
- package/src/core/doctor.ts +125 -9
package/package.json
CHANGED
package/src/core/doctor.ts
CHANGED
|
@@ -27,6 +27,7 @@ import { getDiagnosticLogger, isConfigObject } from "./config";
|
|
|
27
27
|
import { packageRoot } from "./package-root";
|
|
28
28
|
import {
|
|
29
29
|
CURSOR_RUNTIME_PACKAGE,
|
|
30
|
+
OPENCODE_NPM_PIN,
|
|
30
31
|
cursorHookDrift,
|
|
31
32
|
cursorHooksEntry,
|
|
32
33
|
cursorMcpServerEntry,
|
|
@@ -111,6 +112,8 @@ export type DoctorOptions = {
|
|
|
111
112
|
dev?: string;
|
|
112
113
|
cwd?: string;
|
|
113
114
|
opencodeConfig?: string;
|
|
115
|
+
/** OpenCode npm `@latest` package cache root (test seam). */
|
|
116
|
+
opencodePackageCacheDir?: string;
|
|
114
117
|
cursorSettings?: string;
|
|
115
118
|
cursorMcp?: string;
|
|
116
119
|
cursorPluginDir?: string;
|
|
@@ -129,6 +132,7 @@ type Resolved = {
|
|
|
129
132
|
cwd: string;
|
|
130
133
|
dev: string | null;
|
|
131
134
|
opencodeConfig: string;
|
|
135
|
+
opencodePackageCacheDir: string;
|
|
132
136
|
cursorSettings: string;
|
|
133
137
|
cursorMcp: string;
|
|
134
138
|
cursorPluginDir: string;
|
|
@@ -179,6 +183,9 @@ const resolve = (options: DoctorOptions): Resolved => {
|
|
|
179
183
|
dev,
|
|
180
184
|
opencodeConfig:
|
|
181
185
|
options.opencodeConfig ?? path.join(home, ".config", "opencode", "opencode.json"),
|
|
186
|
+
opencodePackageCacheDir:
|
|
187
|
+
options.opencodePackageCacheDir ??
|
|
188
|
+
path.join(home, ".cache", "opencode", "packages", "@brainervirus", "workit-opencode@latest"),
|
|
182
189
|
cursorSettings: options.cursorSettings ?? path.join(home, ".cursor", "settings.json"),
|
|
183
190
|
cursorMcp: options.cursorMcp ?? path.join(home, ".cursor", "mcp.json"),
|
|
184
191
|
cursorPluginDir:
|
|
@@ -771,6 +778,13 @@ const checkStalePin = (res: Resolved): DoctorCheck => {
|
|
|
771
778
|
// `@latest` installs skip the probe entirely: the selector resolves fresh
|
|
772
779
|
// at launch, so the installed package.json version is metadata, not a
|
|
773
780
|
// freshness signal.
|
|
781
|
+
// OpenCode npm pins (`@brainervirus/workit-opencode` / `@…@latest`) are different:
|
|
782
|
+
// OpenCode freezes the first resolved version under
|
|
783
|
+
// `~/.cache/opencode/packages/@brainervirus/workit-opencode@latest` and does not
|
|
784
|
+
// re-resolve on later launches. Doctor compares that cache to the published
|
|
785
|
+
// package (same CA-04 fail-open seam) and tells the user to delete the cache
|
|
786
|
+
// directory so OpenCode can resolve `@latest` again. Exact `@version` pins and
|
|
787
|
+
// `file://` checkout pins skip the comparison.
|
|
774
788
|
// Test seams (no spawns on the canonical path): WORKIT_DOCTOR_STALE_REGISTRY_VERSION
|
|
775
789
|
// short-circuits the probe with the resolved latest version; the optional
|
|
776
790
|
// WORKIT_DOCTOR_STALE_REGISTRY_CMD replaces the `npm view` binary (a
|
|
@@ -786,7 +800,15 @@ const pluginMcpSelectors = (res: Resolved): string[] | null => {
|
|
|
786
800
|
return args.map(String).filter((a) => a.startsWith("--package="));
|
|
787
801
|
};
|
|
788
802
|
|
|
789
|
-
const
|
|
803
|
+
const isFloatingOpenCodeNpmPin = (entry: string): boolean => {
|
|
804
|
+
const s = entry.trim();
|
|
805
|
+
return s === OPENCODE_NPM_PIN || s === `${OPENCODE_NPM_PIN}@latest`;
|
|
806
|
+
};
|
|
807
|
+
|
|
808
|
+
const registryLatestVersion = (
|
|
809
|
+
res: Resolved,
|
|
810
|
+
pkgName: string = CURSOR_RUNTIME_PACKAGE,
|
|
811
|
+
): string | null => {
|
|
790
812
|
const seam = res.env.WORKIT_DOCTOR_STALE_REGISTRY_VERSION;
|
|
791
813
|
if (typeof seam === "string" && seam) {
|
|
792
814
|
return /^\d+(?:\.\d+){1,2}$/.test(seam) ? seam : null;
|
|
@@ -795,7 +817,7 @@ const registryLatestVersion = (res: Resolved): string | null => {
|
|
|
795
817
|
res.env.WORKIT_DOCTOR_STALE_REGISTRY_CMD ?? (commandOnPath("npm", res.env) ? "npm" : null);
|
|
796
818
|
if (!cmd) return null;
|
|
797
819
|
try {
|
|
798
|
-
const r = spawnSync(cmd, ["view",
|
|
820
|
+
const r = spawnSync(cmd, ["view", pkgName, "version"], {
|
|
799
821
|
encoding: "utf8",
|
|
800
822
|
timeout: 20_000,
|
|
801
823
|
env: res.env,
|
|
@@ -812,12 +834,96 @@ const installedPluginVersion = (res: Resolved): string | null => {
|
|
|
812
834
|
return typeof pkg?.version === "string" && pkg.version ? pkg.version : null;
|
|
813
835
|
};
|
|
814
836
|
|
|
837
|
+
const installedOpenCodeCacheVersion = (res: Resolved): string | null => {
|
|
838
|
+
const pkg = readJson(
|
|
839
|
+
path.join(
|
|
840
|
+
res.opencodePackageCacheDir,
|
|
841
|
+
"node_modules",
|
|
842
|
+
"@brainervirus",
|
|
843
|
+
"workit-opencode",
|
|
844
|
+
"package.json",
|
|
845
|
+
),
|
|
846
|
+
);
|
|
847
|
+
return typeof pkg?.version === "string" && pkg.version ? pkg.version : null;
|
|
848
|
+
};
|
|
849
|
+
|
|
850
|
+
/** OpenCode freezes the first `@latest` resolve in its package cache — detect lag. */
|
|
851
|
+
const checkOpenCodePackageCache = (res: Resolved): DoctorCheck & { registryProbed?: boolean } => {
|
|
852
|
+
if (!existsSync(res.opencodeConfig)) {
|
|
853
|
+
return {
|
|
854
|
+
id: "stale_install",
|
|
855
|
+
status: "pass",
|
|
856
|
+
detail: "no opencode config — package cache not inspected",
|
|
857
|
+
};
|
|
858
|
+
}
|
|
859
|
+
const entries = pluginEntries(readJson(res.opencodeConfig));
|
|
860
|
+
if (entries.length === 0) {
|
|
861
|
+
return {
|
|
862
|
+
id: "stale_install",
|
|
863
|
+
status: "pass",
|
|
864
|
+
detail: "no workit opencode pin — package cache not inspected",
|
|
865
|
+
};
|
|
866
|
+
}
|
|
867
|
+
const pin = entries[0]!;
|
|
868
|
+
if (pin.startsWith("file:") || pin.startsWith("git+file:")) {
|
|
869
|
+
return {
|
|
870
|
+
id: "stale_install",
|
|
871
|
+
status: "pass",
|
|
872
|
+
detail: "opencode checkout pin — package cache not used for npm resolution",
|
|
873
|
+
};
|
|
874
|
+
}
|
|
875
|
+
if (!isFloatingOpenCodeNpmPin(pin)) {
|
|
876
|
+
return {
|
|
877
|
+
id: "stale_install",
|
|
878
|
+
status: "pass",
|
|
879
|
+
detail: `opencode pin ${pin} is an exact version — not compared to registry latest`,
|
|
880
|
+
};
|
|
881
|
+
}
|
|
882
|
+
const installed = installedOpenCodeCacheVersion(res);
|
|
883
|
+
if (installed === null) {
|
|
884
|
+
return {
|
|
885
|
+
id: "stale_install",
|
|
886
|
+
status: "pass",
|
|
887
|
+
detail: "opencode @latest package cache empty — OpenCode will resolve on next launch",
|
|
888
|
+
};
|
|
889
|
+
}
|
|
890
|
+
const expected = registryLatestVersion(res, OPENCODE_NPM_PIN);
|
|
891
|
+
if (expected === null) {
|
|
892
|
+
return {
|
|
893
|
+
id: "registry_unreachable",
|
|
894
|
+
status: "warn",
|
|
895
|
+
registryProbed: true,
|
|
896
|
+
detail:
|
|
897
|
+
"registry_unreachable: cannot compare OpenCode @latest cache against published workit-opencode",
|
|
898
|
+
fix: `Retry when npm is reachable, or remove ${res.opencodePackageCacheDir} and restart OpenCode to re-resolve`,
|
|
899
|
+
};
|
|
900
|
+
}
|
|
901
|
+
if (!semverAtLeast(installed, expected)) {
|
|
902
|
+
return {
|
|
903
|
+
id: "stale_install",
|
|
904
|
+
status: "fail",
|
|
905
|
+
registryProbed: true,
|
|
906
|
+
detail: `stale_install: OpenCode @latest cache has workit-opencode ${installed} behind published ${expected}`,
|
|
907
|
+
fix: `Remove ${res.opencodePackageCacheDir} and restart OpenCode — it re-resolves @latest on the next launch`,
|
|
908
|
+
};
|
|
909
|
+
}
|
|
910
|
+
return {
|
|
911
|
+
id: "stale_install",
|
|
912
|
+
status: "pass",
|
|
913
|
+
registryProbed: true,
|
|
914
|
+
detail: `OpenCode @latest cache workit-opencode ${installed} matches published ${expected}`,
|
|
915
|
+
};
|
|
916
|
+
};
|
|
917
|
+
|
|
815
918
|
const checkStaleInstall = (res: Resolved): DoctorCheck & { registryProbed?: boolean } => {
|
|
919
|
+
if (res.host === "opencode") {
|
|
920
|
+
return checkOpenCodePackageCache(res);
|
|
921
|
+
}
|
|
816
922
|
if (res.host !== "cursor" && res.host !== "cli") {
|
|
817
923
|
return {
|
|
818
924
|
id: "stale_install",
|
|
819
925
|
status: "pass",
|
|
820
|
-
detail: "cursor plugin not inspected on
|
|
926
|
+
detail: "cursor plugin not inspected on this host",
|
|
821
927
|
};
|
|
822
928
|
}
|
|
823
929
|
// Symlink installs into pnpm dlx / _npx caches break when the cache is cleared.
|
|
@@ -930,21 +1036,31 @@ const checkStaleInstall = (res: Resolved): DoctorCheck & { registryProbed?: bool
|
|
|
930
1036
|
fix: "Re-run install-cursor-plugin.sh — it refreshes the plugin directory with the current build",
|
|
931
1037
|
};
|
|
932
1038
|
}
|
|
933
|
-
|
|
934
|
-
id: "stale_install",
|
|
935
|
-
status: "pass",
|
|
1039
|
+
const cursorOk = {
|
|
1040
|
+
id: "stale_install" as const,
|
|
1041
|
+
status: "pass" as const,
|
|
936
1042
|
registryProbed: true,
|
|
937
1043
|
detail: `installed local-dist workit-cursor ${installed} matches the published runtime ${expected}`,
|
|
938
1044
|
};
|
|
1045
|
+
if (res.host === "cli") {
|
|
1046
|
+
const oc = checkOpenCodePackageCache(res);
|
|
1047
|
+
if (oc.status !== "pass") return oc;
|
|
1048
|
+
}
|
|
1049
|
+
return cursorOk;
|
|
939
1050
|
}
|
|
940
|
-
|
|
941
|
-
id: "stale_install",
|
|
942
|
-
status: "pass",
|
|
1051
|
+
const cursorOk = {
|
|
1052
|
+
id: "stale_install" as const,
|
|
1053
|
+
status: "pass" as const,
|
|
943
1054
|
detail:
|
|
944
1055
|
installed === null
|
|
945
1056
|
? "installed workit-cursor selectors are canonical"
|
|
946
1057
|
: `installed workit-cursor ${installed} is metadata on the canonical @latest install (fresh at launch)`,
|
|
947
1058
|
};
|
|
1059
|
+
if (res.host === "cli") {
|
|
1060
|
+
const oc = checkOpenCodePackageCache(res);
|
|
1061
|
+
if (oc.status !== "pass") return oc;
|
|
1062
|
+
}
|
|
1063
|
+
return cursorOk;
|
|
948
1064
|
};
|
|
949
1065
|
|
|
950
1066
|
const checkDuplicateRegistration = (res: Resolved): DoctorCheck => {
|