openspecui 1.5.0 → 1.6.0
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/dist/cli.mjs +280 -32
- package/dist/index.mjs +1 -1
- package/dist/{src-16GA3our.mjs → src-Brh3druE.mjs} +101 -4
- package/package.json +3 -3
- package/web/assets/{BufferResource-Bn1UWy0D.js → BufferResource-LpIscPOh.js} +1 -1
- package/web/assets/{CanvasRenderer-D8NiU8la.js → CanvasRenderer-hdBAgk8Z.js} +1 -1
- package/web/assets/{Filter-CRwq487x.js → Filter-DJVBXWdE.js} +1 -1
- package/web/assets/{RenderTargetSystem-CtoB_qTm.js → RenderTargetSystem-CEuUrT8d.js} +1 -1
- package/web/assets/{WebGLRenderer-BgKO8R0a.js → WebGLRenderer-DUSFltAk.js} +1 -1
- package/web/assets/{WebGPURenderer-CQeL2efC.js → WebGPURenderer-DafJULoy.js} +1 -1
- package/web/assets/{browserAll-DP6sOYev.js → browserAll-DYvdsmhE.js} +1 -1
- package/web/assets/{ghostty-web-evxujSxm.js → ghostty-web-BitlvuYh.js} +1 -1
- package/web/assets/{index-BnT52DZ8.js → index-5zLYkWge.js} +1 -1
- package/web/assets/{index-B0IbsqHi.js → index-B8AH-4mO.js} +1 -1
- package/web/assets/{index-D2Tp4F9B.js → index-BXlwoClD.js} +1 -1
- package/web/assets/{index-dSf1u0YV.js → index-BhxOfd1V.js} +1 -1
- package/web/assets/{index-f0QdJSzm.js → index-BwqUcS4o.js} +1 -1
- package/web/assets/{index-DTeOcXKn.js → index-CGTRVPmS.js} +1 -1
- package/web/assets/{index-T8xoxmUb.js → index-CKfGQiMr.js} +236 -205
- package/web/assets/{index-BejnsZfY.js → index-CYTyn82I.js} +1 -1
- package/web/assets/{index-DJqmTRAR.js → index-CjHce-bH.js} +1 -1
- package/web/assets/{index-B147AOgf.js → index-CuiBRRWA.js} +1 -1
- package/web/assets/{index-BPZ3nG0r.js → index-DEgCfC-b.js} +1 -1
- package/web/assets/{index-DcXyAs0z.js → index-DbrGteUX.js} +1 -1
- package/web/assets/{index-BMashGQn.js → index-Dge9ymDE.js} +1 -1
- package/web/assets/{index-CBCPR3Qb.js → index-DtJ1xKq6.js} +1 -1
- package/web/assets/index-USotIqwU.css +1 -0
- package/web/assets/{index-D6ardy54.js → index-gPPYc26D.js} +1 -1
- package/web/assets/{index-4MAU81Qk.js → index-hXwe0Xwc.js} +1 -1
- package/web/assets/{webworkerAll-DA2HufNb.js → webworkerAll-CFbGR7bA.js} +1 -1
- package/web/index.html +2 -2
- package/web/assets/index-Ys2MTD3W.css +0 -1
package/dist/cli.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as SchemaInfoSchema, c as
|
|
2
|
+
import { a as SchemaInfoSchema, c as toOpsxDisplayPath, d as DEFAULT_CONFIG, f as OpenSpecAdapter, i as SchemaDetailSchema, l as CliExecutor, m as __toESM, o as SchemaResolutionSchema, p as __commonJS, r as require_dist, s as TemplatesSchema, t as startServer, u as ConfigManager } from "./src-Brh3druE.mjs";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import { basename, dirname, extname, join, normalize, relative, resolve } from "path";
|
|
5
5
|
import { readFile } from "node:fs/promises";
|
|
@@ -8,7 +8,8 @@ import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync
|
|
|
8
8
|
import { readFileSync as readFileSync$1, readdirSync as readdirSync$1, statSync as statSync$1, writeFile as writeFile$1 } from "fs";
|
|
9
9
|
import { format, inspect } from "util";
|
|
10
10
|
import { fileURLToPath } from "url";
|
|
11
|
-
import { spawn } from "node:child_process";
|
|
11
|
+
import { execFile, spawn } from "node:child_process";
|
|
12
|
+
import { promisify as promisify$1 } from "node:util";
|
|
12
13
|
import { fileURLToPath as fileURLToPath$1 } from "node:url";
|
|
13
14
|
import { notStrictEqual, strictEqual } from "assert";
|
|
14
15
|
|
|
@@ -4506,26 +4507,12 @@ var yargs_default = Yargs;
|
|
|
4506
4507
|
//#endregion
|
|
4507
4508
|
//#region package.json
|
|
4508
4509
|
var import_dist = require_dist();
|
|
4509
|
-
var version = "1.
|
|
4510
|
-
var devDependencies = {
|
|
4511
|
-
"@hono/node-server": "^1.14.1",
|
|
4512
|
-
"@openspecui/server": "workspace:*",
|
|
4513
|
-
"@openspecui/web": "workspace:*",
|
|
4514
|
-
"@types/node": "^22.10.2",
|
|
4515
|
-
"@types/ws": "^8.5.13",
|
|
4516
|
-
"@types/yargs": "^17.0.35",
|
|
4517
|
-
"hono": "^4.7.3",
|
|
4518
|
-
"open": "^10.1.0",
|
|
4519
|
-
"tsdown": "^0.16.6",
|
|
4520
|
-
"tsx": "^4.19.2",
|
|
4521
|
-
"typescript": "^5.7.2",
|
|
4522
|
-
"vitest": "^2.1.8",
|
|
4523
|
-
"yargs": "^18.0.0"
|
|
4524
|
-
};
|
|
4510
|
+
var version = "1.6.0";
|
|
4525
4511
|
|
|
4526
4512
|
//#endregion
|
|
4527
4513
|
//#region src/export.ts
|
|
4528
4514
|
const __dirname$1 = dirname$1(fileURLToPath$1(import.meta.url));
|
|
4515
|
+
const execFileAsync = promisify$1(execFile);
|
|
4529
4516
|
function parseCliJson(raw, schema, label) {
|
|
4530
4517
|
const trimmed = raw.trim();
|
|
4531
4518
|
if (!trimmed) throw new Error(`${label} returned empty output`);
|
|
@@ -4574,6 +4561,184 @@ function parseSchemaYaml(content) {
|
|
|
4574
4561
|
if (!validated.success) throw new Error(`Invalid schema.yaml detail: ${validated.error.message}`);
|
|
4575
4562
|
return validated.data;
|
|
4576
4563
|
}
|
|
4564
|
+
function isAbsoluteFsPath(path$1) {
|
|
4565
|
+
const normalized = path$1.replace(/\\/g, "/");
|
|
4566
|
+
return normalized.startsWith("/") || /^[A-Za-z]:\//.test(normalized);
|
|
4567
|
+
}
|
|
4568
|
+
function toAbsoluteProjectPath(projectDir, path$1) {
|
|
4569
|
+
return isAbsoluteFsPath(path$1) ? path$1 : resolve$1(projectDir, path$1);
|
|
4570
|
+
}
|
|
4571
|
+
function normalizeGitPath(path$1) {
|
|
4572
|
+
return path$1.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
4573
|
+
}
|
|
4574
|
+
function parseRelatedChanges(paths) {
|
|
4575
|
+
const related = /* @__PURE__ */ new Set();
|
|
4576
|
+
for (const rawPath of paths) {
|
|
4577
|
+
const path$1 = normalizeGitPath(rawPath);
|
|
4578
|
+
const activeMatch = /^openspec\/changes\/([^/]+)\//.exec(path$1);
|
|
4579
|
+
if (activeMatch?.[1]) {
|
|
4580
|
+
related.add(activeMatch[1]);
|
|
4581
|
+
continue;
|
|
4582
|
+
}
|
|
4583
|
+
const archiveMatch = /^openspec\/changes\/archive\/([^/]+)\//.exec(path$1);
|
|
4584
|
+
if (archiveMatch?.[1]) related.add(archiveMatch[1].replace(/^\d{4}-\d{2}-\d{2}-/, ""));
|
|
4585
|
+
}
|
|
4586
|
+
return [...related].sort((a, b) => a.localeCompare(b));
|
|
4587
|
+
}
|
|
4588
|
+
function parseNumstat(numstatOutput) {
|
|
4589
|
+
let files = 0;
|
|
4590
|
+
let insertions = 0;
|
|
4591
|
+
let deletions = 0;
|
|
4592
|
+
for (const line of numstatOutput.split("\n")) {
|
|
4593
|
+
const trimmed = line.trim();
|
|
4594
|
+
if (!trimmed) continue;
|
|
4595
|
+
const [addRaw, delRaw] = trimmed.split(" ");
|
|
4596
|
+
if (!addRaw || !delRaw) continue;
|
|
4597
|
+
files += 1;
|
|
4598
|
+
if (addRaw !== "-") insertions += Number(addRaw) || 0;
|
|
4599
|
+
if (delRaw !== "-") deletions += Number(delRaw) || 0;
|
|
4600
|
+
}
|
|
4601
|
+
return {
|
|
4602
|
+
files,
|
|
4603
|
+
insertions,
|
|
4604
|
+
deletions
|
|
4605
|
+
};
|
|
4606
|
+
}
|
|
4607
|
+
async function readDefaultBranch(projectDir) {
|
|
4608
|
+
try {
|
|
4609
|
+
const { stdout } = await execFileAsync("git", [
|
|
4610
|
+
"symbolic-ref",
|
|
4611
|
+
"--quiet",
|
|
4612
|
+
"--short",
|
|
4613
|
+
"refs/remotes/origin/HEAD"
|
|
4614
|
+
], {
|
|
4615
|
+
cwd: projectDir,
|
|
4616
|
+
encoding: "utf8",
|
|
4617
|
+
maxBuffer: 1024 * 1024
|
|
4618
|
+
});
|
|
4619
|
+
const branch = stdout.trim();
|
|
4620
|
+
if (branch.length > 0) return branch;
|
|
4621
|
+
} catch {}
|
|
4622
|
+
try {
|
|
4623
|
+
const { stdout } = await execFileAsync("git", [
|
|
4624
|
+
"rev-parse",
|
|
4625
|
+
"--abbrev-ref",
|
|
4626
|
+
"HEAD"
|
|
4627
|
+
], {
|
|
4628
|
+
cwd: projectDir,
|
|
4629
|
+
encoding: "utf8",
|
|
4630
|
+
maxBuffer: 1024 * 1024
|
|
4631
|
+
});
|
|
4632
|
+
const branch = stdout.trim();
|
|
4633
|
+
if (branch.length > 0 && branch !== "HEAD") return branch;
|
|
4634
|
+
} catch {}
|
|
4635
|
+
return "main";
|
|
4636
|
+
}
|
|
4637
|
+
function parseRecentCommitLog(output) {
|
|
4638
|
+
const commits = [];
|
|
4639
|
+
let current = null;
|
|
4640
|
+
const pushCurrent = () => {
|
|
4641
|
+
if (!current) return;
|
|
4642
|
+
commits.push({
|
|
4643
|
+
hash: current.hash,
|
|
4644
|
+
title: current.title,
|
|
4645
|
+
committedAt: current.committedAt,
|
|
4646
|
+
relatedChanges: parseRelatedChanges(current.changedPaths),
|
|
4647
|
+
diff: parseNumstat(current.numstatLines.join("\n"))
|
|
4648
|
+
});
|
|
4649
|
+
current = null;
|
|
4650
|
+
};
|
|
4651
|
+
for (const line of output.split("\n")) {
|
|
4652
|
+
if (line.startsWith("__COMMIT__ ")) {
|
|
4653
|
+
pushCurrent();
|
|
4654
|
+
const [_, hash, tsRaw, ...titleParts] = line.split(" ");
|
|
4655
|
+
const committedAt = Number(tsRaw) * 1e3;
|
|
4656
|
+
current = {
|
|
4657
|
+
hash: hash ?? "",
|
|
4658
|
+
title: titleParts.join(" ").trim() || (hash ? hash.slice(0, 8) : "commit"),
|
|
4659
|
+
committedAt: Number.isFinite(committedAt) && committedAt > 0 ? committedAt : 0,
|
|
4660
|
+
numstatLines: [],
|
|
4661
|
+
changedPaths: []
|
|
4662
|
+
};
|
|
4663
|
+
continue;
|
|
4664
|
+
}
|
|
4665
|
+
if (!current) continue;
|
|
4666
|
+
const trimmed = line.trim();
|
|
4667
|
+
if (!trimmed) continue;
|
|
4668
|
+
const [addRaw, delRaw, ...pathParts] = trimmed.split(" ");
|
|
4669
|
+
if (!addRaw || !delRaw || pathParts.length === 0) continue;
|
|
4670
|
+
const path$1 = pathParts.join(" ");
|
|
4671
|
+
current.numstatLines.push(`${addRaw}\t${delRaw}\t${path$1}`);
|
|
4672
|
+
current.changedPaths.push(path$1);
|
|
4673
|
+
}
|
|
4674
|
+
pushCurrent();
|
|
4675
|
+
return commits.filter((commit) => commit.hash.length > 0);
|
|
4676
|
+
}
|
|
4677
|
+
function normalizeRepositoryUrl(raw) {
|
|
4678
|
+
const value = raw.trim();
|
|
4679
|
+
if (!value) return null;
|
|
4680
|
+
if (value.startsWith("http://") || value.startsWith("https://")) return value.replace(/\.git$/i, "");
|
|
4681
|
+
const gitAtMatch = /^git@([^:]+):(.+)$/.exec(value);
|
|
4682
|
+
if (gitAtMatch?.[1] && gitAtMatch[2]) return `https://${gitAtMatch[1]}/${gitAtMatch[2].replace(/\.git$/i, "")}`;
|
|
4683
|
+
if (value.startsWith("ssh://")) try {
|
|
4684
|
+
const parsed = new URL(value);
|
|
4685
|
+
const pathname = parsed.pathname.replace(/^\/+/, "").replace(/\.git$/i, "");
|
|
4686
|
+
if (!pathname) return null;
|
|
4687
|
+
return `https://${parsed.hostname}/${pathname}`;
|
|
4688
|
+
} catch {}
|
|
4689
|
+
return value.replace(/\.git$/i, "");
|
|
4690
|
+
}
|
|
4691
|
+
async function readSnapshotGit(projectDir) {
|
|
4692
|
+
try {
|
|
4693
|
+
const defaultBranch = await readDefaultBranch(projectDir);
|
|
4694
|
+
const { stdout: latestTsRaw } = await execFileAsync("git", [
|
|
4695
|
+
"log",
|
|
4696
|
+
"-1",
|
|
4697
|
+
"--format=%ct"
|
|
4698
|
+
], {
|
|
4699
|
+
cwd: projectDir,
|
|
4700
|
+
encoding: "utf8",
|
|
4701
|
+
maxBuffer: 1024 * 1024
|
|
4702
|
+
});
|
|
4703
|
+
const latestSeconds = Number(latestTsRaw.trim());
|
|
4704
|
+
const latestCommitTs = Number.isFinite(latestSeconds) && latestSeconds > 0 ? latestSeconds * 1e3 : null;
|
|
4705
|
+
let repositoryUrl = null;
|
|
4706
|
+
try {
|
|
4707
|
+
const { stdout: remoteRaw } = await execFileAsync("git", [
|
|
4708
|
+
"config",
|
|
4709
|
+
"--get",
|
|
4710
|
+
"remote.origin.url"
|
|
4711
|
+
], {
|
|
4712
|
+
cwd: projectDir,
|
|
4713
|
+
encoding: "utf8",
|
|
4714
|
+
maxBuffer: 1024 * 1024
|
|
4715
|
+
});
|
|
4716
|
+
repositoryUrl = normalizeRepositoryUrl(remoteRaw);
|
|
4717
|
+
} catch {
|
|
4718
|
+
repositoryUrl = null;
|
|
4719
|
+
}
|
|
4720
|
+
const { stdout: logOutput } = await execFileAsync("git", [
|
|
4721
|
+
"log",
|
|
4722
|
+
"-n",
|
|
4723
|
+
"5",
|
|
4724
|
+
"--format=__COMMIT__%x09%H%x09%ct%x09%s",
|
|
4725
|
+
"--numstat",
|
|
4726
|
+
"--"
|
|
4727
|
+
], {
|
|
4728
|
+
cwd: projectDir,
|
|
4729
|
+
encoding: "utf8",
|
|
4730
|
+
maxBuffer: 8 * 1024 * 1024
|
|
4731
|
+
});
|
|
4732
|
+
return {
|
|
4733
|
+
defaultBranch,
|
|
4734
|
+
repositoryUrl,
|
|
4735
|
+
latestCommitTs,
|
|
4736
|
+
recentCommits: parseRecentCommitLog(logOutput)
|
|
4737
|
+
};
|
|
4738
|
+
} catch {
|
|
4739
|
+
return;
|
|
4740
|
+
}
|
|
4741
|
+
}
|
|
4577
4742
|
/**
|
|
4578
4743
|
* Generate a complete data snapshot of the OpenSpec project
|
|
4579
4744
|
* (Kept for backwards compatibility and testing)
|
|
@@ -4656,8 +4821,10 @@ async function generateSnapshot(projectDir) {
|
|
|
4656
4821
|
let configYaml;
|
|
4657
4822
|
let schemas = [];
|
|
4658
4823
|
const schemaDetails = {};
|
|
4824
|
+
const schemaYamls = {};
|
|
4659
4825
|
const schemaResolutions = {};
|
|
4660
4826
|
const templates = {};
|
|
4827
|
+
const templateContents = {};
|
|
4661
4828
|
const changeMetadata = {};
|
|
4662
4829
|
try {
|
|
4663
4830
|
configYaml = await readFile(join$1(projectDir, "openspec", "config.yaml"), "utf-8");
|
|
@@ -4675,16 +4842,56 @@ async function generateSnapshot(projectDir) {
|
|
|
4675
4842
|
const resolutionResult = await cliExecutor.schemaWhich(schema.name);
|
|
4676
4843
|
if (resolutionResult.success) {
|
|
4677
4844
|
const resolution = parseCliJson(resolutionResult.stdout, SchemaResolutionSchema, "openspec schema which");
|
|
4678
|
-
schemaResolutions[schema.name] =
|
|
4845
|
+
schemaResolutions[schema.name] = {
|
|
4846
|
+
...resolution,
|
|
4847
|
+
displayPath: toOpsxDisplayPath(resolution.path, {
|
|
4848
|
+
source: resolution.source,
|
|
4849
|
+
projectDir
|
|
4850
|
+
}),
|
|
4851
|
+
shadows: resolution.shadows.map((shadow) => ({
|
|
4852
|
+
...shadow,
|
|
4853
|
+
displayPath: toOpsxDisplayPath(shadow.path, {
|
|
4854
|
+
source: shadow.source,
|
|
4855
|
+
projectDir
|
|
4856
|
+
})
|
|
4857
|
+
}))
|
|
4858
|
+
};
|
|
4679
4859
|
try {
|
|
4680
4860
|
const schemaContent = await readFile(join$1(resolution.path, "schema.yaml"), "utf-8");
|
|
4681
4861
|
schemaDetails[schema.name] = parseSchemaYaml(schemaContent);
|
|
4862
|
+
schemaYamls[schema.name] = schemaContent;
|
|
4682
4863
|
} catch {}
|
|
4683
4864
|
}
|
|
4684
4865
|
} catch {}
|
|
4685
4866
|
try {
|
|
4686
4867
|
const templatesResult = await cliExecutor.templates(schema.name);
|
|
4687
|
-
if (templatesResult.success)
|
|
4868
|
+
if (templatesResult.success) {
|
|
4869
|
+
const parsedTemplates = parseCliJson(templatesResult.stdout, TemplatesSchema, "openspec templates");
|
|
4870
|
+
const normalizedTemplates = Object.fromEntries(Object.entries(parsedTemplates).map(([artifactId, info]) => [artifactId, {
|
|
4871
|
+
...info,
|
|
4872
|
+
path: toAbsoluteProjectPath(projectDir, info.path),
|
|
4873
|
+
displayPath: toOpsxDisplayPath(info.path, {
|
|
4874
|
+
source: info.source,
|
|
4875
|
+
projectDir
|
|
4876
|
+
})
|
|
4877
|
+
}]));
|
|
4878
|
+
templates[schema.name] = normalizedTemplates;
|
|
4879
|
+
const contents = await Promise.all(Object.entries(normalizedTemplates).map(async ([artifactId, info]) => {
|
|
4880
|
+
let content = null;
|
|
4881
|
+
try {
|
|
4882
|
+
content = await readFile(info.path, "utf-8");
|
|
4883
|
+
} catch {
|
|
4884
|
+
content = null;
|
|
4885
|
+
}
|
|
4886
|
+
return [artifactId, {
|
|
4887
|
+
content,
|
|
4888
|
+
path: info.path,
|
|
4889
|
+
displayPath: info.displayPath,
|
|
4890
|
+
source: info.source
|
|
4891
|
+
}];
|
|
4892
|
+
}));
|
|
4893
|
+
templateContents[schema.name] = Object.fromEntries(contents);
|
|
4894
|
+
}
|
|
4688
4895
|
} catch {}
|
|
4689
4896
|
}
|
|
4690
4897
|
try {
|
|
@@ -4695,6 +4902,7 @@ async function generateSnapshot(projectDir) {
|
|
|
4695
4902
|
changeMetadata[changeId] = null;
|
|
4696
4903
|
}
|
|
4697
4904
|
} catch {}
|
|
4905
|
+
const git = await readSnapshotGit(projectDir);
|
|
4698
4906
|
return {
|
|
4699
4907
|
meta: {
|
|
4700
4908
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -4706,6 +4914,7 @@ async function generateSnapshot(projectDir) {
|
|
|
4706
4914
|
changesCount: changes.filter((c) => c !== null).length,
|
|
4707
4915
|
archivesCount: archives.length
|
|
4708
4916
|
},
|
|
4917
|
+
git,
|
|
4709
4918
|
config: uiConfig,
|
|
4710
4919
|
specs,
|
|
4711
4920
|
changes: changes.filter((c) => c !== null),
|
|
@@ -4716,8 +4925,10 @@ async function generateSnapshot(projectDir) {
|
|
|
4716
4925
|
configYaml,
|
|
4717
4926
|
schemas,
|
|
4718
4927
|
schemaDetails,
|
|
4928
|
+
schemaYamls,
|
|
4719
4929
|
schemaResolutions,
|
|
4720
4930
|
templates,
|
|
4931
|
+
templateContents,
|
|
4721
4932
|
changeMetadata
|
|
4722
4933
|
}
|
|
4723
4934
|
};
|
|
@@ -4738,7 +4949,7 @@ function runCommand(cmd, args, cwd) {
|
|
|
4738
4949
|
const child = spawn(cmd, args, {
|
|
4739
4950
|
stdio: "inherit",
|
|
4740
4951
|
cwd,
|
|
4741
|
-
shell:
|
|
4952
|
+
shell: false
|
|
4742
4953
|
});
|
|
4743
4954
|
child.on("close", (code) => {
|
|
4744
4955
|
if (code === 0) resolvePromise();
|
|
@@ -4747,6 +4958,11 @@ function runCommand(cmd, args, cwd) {
|
|
|
4747
4958
|
child.on("error", (err) => reject(err));
|
|
4748
4959
|
});
|
|
4749
4960
|
}
|
|
4961
|
+
const LOCAL_PACKAGE_PROTOCOLS = [
|
|
4962
|
+
"workspace:",
|
|
4963
|
+
"file:",
|
|
4964
|
+
"link:"
|
|
4965
|
+
];
|
|
4750
4966
|
/**
|
|
4751
4967
|
* Detect the package manager used in the current project
|
|
4752
4968
|
*/
|
|
@@ -4767,7 +4983,7 @@ function detectPackageManager() {
|
|
|
4767
4983
|
return "npm";
|
|
4768
4984
|
}
|
|
4769
4985
|
/**
|
|
4770
|
-
* Get the command to run a
|
|
4986
|
+
* Get the command to run a binary in a package-manager agnostic way.
|
|
4771
4987
|
*/
|
|
4772
4988
|
function getRunCommand(pm, bin) {
|
|
4773
4989
|
switch (pm) {
|
|
@@ -4777,11 +4993,11 @@ function getRunCommand(pm, bin) {
|
|
|
4777
4993
|
};
|
|
4778
4994
|
case "pnpm": return {
|
|
4779
4995
|
cmd: "pnpm",
|
|
4780
|
-
args: ["
|
|
4996
|
+
args: ["dlx", bin]
|
|
4781
4997
|
};
|
|
4782
4998
|
case "yarn": return {
|
|
4783
4999
|
cmd: "yarn",
|
|
4784
|
-
args: [bin]
|
|
5000
|
+
args: ["dlx", bin]
|
|
4785
5001
|
};
|
|
4786
5002
|
case "deno": return {
|
|
4787
5003
|
cmd: "deno",
|
|
@@ -4797,12 +5013,36 @@ function getRunCommand(pm, bin) {
|
|
|
4797
5013
|
};
|
|
4798
5014
|
}
|
|
4799
5015
|
}
|
|
5016
|
+
function findNearestPackageJson(startDir) {
|
|
5017
|
+
let currentDir = startDir;
|
|
5018
|
+
while (true) {
|
|
5019
|
+
const packageJsonPath = join$1(currentDir, "package.json");
|
|
5020
|
+
if (existsSync(packageJsonPath)) return packageJsonPath;
|
|
5021
|
+
const parentDir = dirname$1(currentDir);
|
|
5022
|
+
if (parentDir === currentDir) return null;
|
|
5023
|
+
currentDir = parentDir;
|
|
5024
|
+
}
|
|
5025
|
+
}
|
|
5026
|
+
function readWebPackageRangeFromPackageJson(startDir) {
|
|
5027
|
+
const packageJsonPath = findNearestPackageJson(startDir);
|
|
5028
|
+
if (!packageJsonPath) return null;
|
|
5029
|
+
try {
|
|
5030
|
+
const packageJsonRaw = readFileSync(packageJsonPath, "utf-8");
|
|
5031
|
+
const parsed = JSON.parse(packageJsonRaw);
|
|
5032
|
+
return parsed.dependencies?.["@openspecui/web"] ?? parsed.devDependencies?.["@openspecui/web"] ?? parsed.peerDependencies?.["@openspecui/web"] ?? parsed.optionalDependencies?.["@openspecui/web"] ?? null;
|
|
5033
|
+
} catch {
|
|
5034
|
+
return null;
|
|
5035
|
+
}
|
|
5036
|
+
}
|
|
5037
|
+
function isLocalPackageRange(range) {
|
|
5038
|
+
if (!range) return false;
|
|
5039
|
+
return LOCAL_PACKAGE_PROTOCOLS.some((protocol) => range.startsWith(protocol));
|
|
5040
|
+
}
|
|
4800
5041
|
/**
|
|
4801
5042
|
* Get the exec command for running a package binary
|
|
4802
5043
|
* Uses appropriate flags to ensure the correct version of @openspecui/web is installed
|
|
4803
5044
|
*/
|
|
4804
|
-
function getExecCommand(pm) {
|
|
4805
|
-
const webPkgSpec = `@openspecui/web@${devDependencies["@openspecui/web"]}`;
|
|
5045
|
+
function getExecCommand(pm, webPkgSpec) {
|
|
4806
5046
|
switch (pm) {
|
|
4807
5047
|
case "bun": return {
|
|
4808
5048
|
cmd: "bunx",
|
|
@@ -4867,7 +5107,9 @@ async function exportHtml(options) {
|
|
|
4867
5107
|
writeFileSync(dataJsonPath, JSON.stringify(snapshot, null, 2));
|
|
4868
5108
|
console.log(`Data snapshot written to ${dataJsonPath}`);
|
|
4869
5109
|
const localWebPkg = findLocalWebPackage();
|
|
4870
|
-
|
|
5110
|
+
const webPackageRange = readWebPackageRangeFromPackageJson(__dirname$1);
|
|
5111
|
+
if (isLocalPackageRange(webPackageRange)) {
|
|
5112
|
+
if (!localWebPkg) throw new Error(`Detected local/dev @openspecui/web range "${webPackageRange}" but local web package was not found`);
|
|
4871
5113
|
console.log("\n[Local dev mode] Running SSG from local web package...");
|
|
4872
5114
|
await runCommand("pnpm", [
|
|
4873
5115
|
"tsx",
|
|
@@ -4882,7 +5124,7 @@ async function exportHtml(options) {
|
|
|
4882
5124
|
} else {
|
|
4883
5125
|
console.log("\n[Production mode] Running SSG via @openspecui/web...");
|
|
4884
5126
|
const pm = detectPackageManager();
|
|
4885
|
-
const execCmd = getExecCommand(pm);
|
|
5127
|
+
const execCmd = getExecCommand(pm, `@openspecui/web@${webPackageRange || version}`);
|
|
4886
5128
|
try {
|
|
4887
5129
|
await runCommand(execCmd.cmd, [
|
|
4888
5130
|
...execCmd.args,
|
|
@@ -5027,6 +5269,10 @@ async function main() {
|
|
|
5027
5269
|
}).option("preview-port", {
|
|
5028
5270
|
describe: "Port for the preview server (used with --open)",
|
|
5029
5271
|
type: "number"
|
|
5272
|
+
}).option("port", {
|
|
5273
|
+
alias: "p",
|
|
5274
|
+
describe: "Alias of --open --preview-port <port>",
|
|
5275
|
+
type: "number"
|
|
5030
5276
|
}).option("preview-host", {
|
|
5031
5277
|
describe: "Host for the preview server (used with --open)",
|
|
5032
5278
|
type: "string"
|
|
@@ -5034,6 +5280,8 @@ async function main() {
|
|
|
5034
5280
|
}, async (argv) => {
|
|
5035
5281
|
const projectDir = resolve$1(originalCwd, argv.dir || ".");
|
|
5036
5282
|
const outputDir = resolve$1(originalCwd, argv.output);
|
|
5283
|
+
const previewPort = argv.port ?? argv["preview-port"];
|
|
5284
|
+
const shouldOpen = argv.open || argv.port !== void 0;
|
|
5037
5285
|
try {
|
|
5038
5286
|
await exportStaticSite({
|
|
5039
5287
|
projectDir,
|
|
@@ -5041,16 +5289,16 @@ async function main() {
|
|
|
5041
5289
|
format: argv.format,
|
|
5042
5290
|
basePath: argv["base-path"],
|
|
5043
5291
|
clean: argv.clean,
|
|
5044
|
-
open:
|
|
5045
|
-
previewPort
|
|
5292
|
+
open: shouldOpen,
|
|
5293
|
+
previewPort,
|
|
5046
5294
|
previewHost: argv["preview-host"]
|
|
5047
5295
|
});
|
|
5048
|
-
if (!
|
|
5296
|
+
if (!shouldOpen) process.exit(0);
|
|
5049
5297
|
} catch (error) {
|
|
5050
5298
|
console.error("❌ Export failed:", error);
|
|
5051
5299
|
process.exit(1);
|
|
5052
5300
|
}
|
|
5053
|
-
}).example("$0", "Start server in current directory").example("$0 ./my-project", "Start server with specific project").example("$0 -p 8080", "Start server on custom port").example("$0 export -o ./dist", "Export HTML to ./dist directory").example("$0 export -o ./dist -f json", "Export JSON data only").example("$0 export -o ./dist --base-path=/docs/", "Export for subdirectory deployment").example("$0 export -o ./dist --clean", "Clean output directory before export").version(getVersion()).alias("v", "version").help().alias("h", "help").parse();
|
|
5301
|
+
}).example("$0", "Start server in current directory").example("$0 ./my-project", "Start server with specific project").example("$0 -p 8080", "Start server on custom port").example("$0 export -o ./dist", "Export HTML to ./dist directory").example("$0 export -o ./dist -f json", "Export JSON data only").example("$0 export -o ./dist -p 8092", "Export and open preview on port 8092").example("$0 export -o ./dist --base-path=/docs/", "Export for subdirectory deployment").example("$0 export -o ./dist --clean", "Clean output directory before export").version(getVersion()).alias("v", "version").help().alias("h", "help").parse();
|
|
5054
5302
|
}
|
|
5055
5303
|
main();
|
|
5056
5304
|
|
package/dist/index.mjs
CHANGED
|
@@ -7094,6 +7094,68 @@ const DASHBOARD_METRIC_KEYS = [
|
|
|
7094
7094
|
"taskCompletionPercent"
|
|
7095
7095
|
];
|
|
7096
7096
|
|
|
7097
|
+
//#endregion
|
|
7098
|
+
//#region ../core/src/opsx-display-path.ts
|
|
7099
|
+
const VIRTUAL_PROJECT_DIRNAME = "project";
|
|
7100
|
+
const WINDOWS_DRIVE_PREFIX = /^[A-Za-z]:\//;
|
|
7101
|
+
function normalizeFsPath(path$1) {
|
|
7102
|
+
return path$1.replace(/\\/g, "/").replace(/\/+$/g, "");
|
|
7103
|
+
}
|
|
7104
|
+
function isAbsolutePath(path$1) {
|
|
7105
|
+
return path$1.startsWith("/") || WINDOWS_DRIVE_PREFIX.test(path$1);
|
|
7106
|
+
}
|
|
7107
|
+
function stripRelativePrefix(path$1) {
|
|
7108
|
+
return path$1.replace(/^\.?\//, "");
|
|
7109
|
+
}
|
|
7110
|
+
function splitSegments(path$1) {
|
|
7111
|
+
return normalizeFsPath(path$1).split("/").filter(Boolean);
|
|
7112
|
+
}
|
|
7113
|
+
function toNpmSpecifier(path$1) {
|
|
7114
|
+
const normalized = normalizeFsPath(path$1);
|
|
7115
|
+
const match$1 = /(?:^|\/)node_modules\/(?:\.pnpm\/[^/]+\/node_modules\/)?(@[^/]+\/[^/]+|[^/]+)(\/.*)?/.exec(normalized);
|
|
7116
|
+
const pkgName = match$1?.[1];
|
|
7117
|
+
if (!pkgName) return null;
|
|
7118
|
+
return `npm:${pkgName}${match$1[2] ?? ""}`;
|
|
7119
|
+
}
|
|
7120
|
+
function toVirtualProjectPath(path$1) {
|
|
7121
|
+
return `${VIRTUAL_PROJECT_DIRNAME}:${stripRelativePrefix(path$1)}`;
|
|
7122
|
+
}
|
|
7123
|
+
function isPathInside(root, target) {
|
|
7124
|
+
const normalizedRoot = normalizeFsPath(root);
|
|
7125
|
+
const normalizedTarget = normalizeFsPath(target);
|
|
7126
|
+
const rootLower = normalizedRoot.toLowerCase();
|
|
7127
|
+
const targetLower = normalizedTarget.toLowerCase();
|
|
7128
|
+
return targetLower === rootLower || targetLower.startsWith(`${rootLower}/`);
|
|
7129
|
+
}
|
|
7130
|
+
function toRelativeFromRoot(root, target) {
|
|
7131
|
+
const rootSegments = splitSegments(root);
|
|
7132
|
+
const targetSegments = splitSegments(target);
|
|
7133
|
+
let index = 0;
|
|
7134
|
+
while (index < rootSegments.length && index < targetSegments.length) {
|
|
7135
|
+
if (rootSegments[index]?.toLowerCase() !== targetSegments[index]?.toLowerCase()) break;
|
|
7136
|
+
index += 1;
|
|
7137
|
+
}
|
|
7138
|
+
return targetSegments.slice(index).join("/");
|
|
7139
|
+
}
|
|
7140
|
+
function findOpspecSlice(path$1) {
|
|
7141
|
+
const segments = splitSegments(path$1);
|
|
7142
|
+
const idx = segments.lastIndexOf("openspec");
|
|
7143
|
+
if (idx < 0) return null;
|
|
7144
|
+
return segments.slice(idx).join("/");
|
|
7145
|
+
}
|
|
7146
|
+
function toOpsxDisplayPath(absoluteOrRelativePath, options) {
|
|
7147
|
+
const normalized = normalizeFsPath(absoluteOrRelativePath);
|
|
7148
|
+
const npmSpecifier = toNpmSpecifier(normalized);
|
|
7149
|
+
if (options?.source === "package" || npmSpecifier) {
|
|
7150
|
+
if (npmSpecifier) return npmSpecifier;
|
|
7151
|
+
}
|
|
7152
|
+
if (!isAbsolutePath(normalized)) return toVirtualProjectPath(normalized);
|
|
7153
|
+
if (options?.projectDir && isPathInside(options.projectDir, normalized)) return toVirtualProjectPath(toRelativeFromRoot(options.projectDir, normalized));
|
|
7154
|
+
const openspecSlice = findOpspecSlice(normalized);
|
|
7155
|
+
if (openspecSlice) return toVirtualProjectPath(openspecSlice);
|
|
7156
|
+
return toVirtualProjectPath(splitSegments(normalized).slice(-4).join("/"));
|
|
7157
|
+
}
|
|
7158
|
+
|
|
7097
7159
|
//#endregion
|
|
7098
7160
|
//#region ../core/src/opsx-types.ts
|
|
7099
7161
|
const ArtifactStatusSchema = objectType({
|
|
@@ -7177,17 +7239,20 @@ const SchemaResolutionSchema = objectType({
|
|
|
7177
7239
|
"package"
|
|
7178
7240
|
]),
|
|
7179
7241
|
path: stringType(),
|
|
7242
|
+
displayPath: stringType().optional(),
|
|
7180
7243
|
shadows: arrayType(objectType({
|
|
7181
7244
|
source: enumType([
|
|
7182
7245
|
"project",
|
|
7183
7246
|
"user",
|
|
7184
7247
|
"package"
|
|
7185
7248
|
]),
|
|
7186
|
-
path: stringType()
|
|
7249
|
+
path: stringType(),
|
|
7250
|
+
displayPath: stringType().optional()
|
|
7187
7251
|
}))
|
|
7188
7252
|
});
|
|
7189
7253
|
const TemplatesSchema = recordType(objectType({
|
|
7190
7254
|
path: stringType(),
|
|
7255
|
+
displayPath: stringType().optional(),
|
|
7191
7256
|
source: enumType([
|
|
7192
7257
|
"project",
|
|
7193
7258
|
"user",
|
|
@@ -13884,6 +13949,12 @@ function parseCliJson(raw$1, schema$6, label) {
|
|
|
13884
13949
|
function toRelativePath(root, absolutePath) {
|
|
13885
13950
|
return relative$1(root, absolutePath).split(sep).join("/");
|
|
13886
13951
|
}
|
|
13952
|
+
function isAbsoluteFsPath(path$1) {
|
|
13953
|
+
return path$1.startsWith("/") || /^[A-Za-z]:\//.test(path$1);
|
|
13954
|
+
}
|
|
13955
|
+
function toAbsoluteProjectPath(projectDir, path$1) {
|
|
13956
|
+
return isAbsoluteFsPath(path$1.replace(/\\/g, "/")) ? path$1 : resolve$1(projectDir, path$1);
|
|
13957
|
+
}
|
|
13887
13958
|
async function readEntriesUnderRoot(root) {
|
|
13888
13959
|
if (!(await reactiveStat(root))?.isDirectory) return [];
|
|
13889
13960
|
const collectEntries = async (dir) => {
|
|
@@ -14322,7 +14393,21 @@ var OpsxKernel = class {
|
|
|
14322
14393
|
await touchOpsxProjectDeps(this.projectDir);
|
|
14323
14394
|
const result = await this.cliExecutor.schemaWhich(name);
|
|
14324
14395
|
if (!result.success) throw new Error(result.stderr || `openspec schema which failed (exit ${result.exitCode ?? "null"})`);
|
|
14325
|
-
|
|
14396
|
+
const parsed = parseCliJson(result.stdout, SchemaResolutionSchema, "openspec schema which");
|
|
14397
|
+
return {
|
|
14398
|
+
...parsed,
|
|
14399
|
+
displayPath: toOpsxDisplayPath(parsed.path, {
|
|
14400
|
+
source: parsed.source,
|
|
14401
|
+
projectDir: this.projectDir
|
|
14402
|
+
}),
|
|
14403
|
+
shadows: parsed.shadows.map((shadow) => ({
|
|
14404
|
+
...shadow,
|
|
14405
|
+
displayPath: toOpsxDisplayPath(shadow.path, {
|
|
14406
|
+
source: shadow.source,
|
|
14407
|
+
projectDir: this.projectDir
|
|
14408
|
+
})
|
|
14409
|
+
}))
|
|
14410
|
+
};
|
|
14326
14411
|
}
|
|
14327
14412
|
async fetchSchemaDetail(name) {
|
|
14328
14413
|
await touchOpsxProjectDeps(this.projectDir);
|
|
@@ -14346,7 +14431,15 @@ var OpsxKernel = class {
|
|
|
14346
14431
|
await touchOpsxProjectDeps(this.projectDir);
|
|
14347
14432
|
const result = await this.cliExecutor.templates(schema$6);
|
|
14348
14433
|
if (!result.success) throw new Error(result.stderr || `openspec templates failed (exit ${result.exitCode ?? "null"})`);
|
|
14349
|
-
|
|
14434
|
+
const templates = parseCliJson(result.stdout, TemplatesSchema, "openspec templates");
|
|
14435
|
+
return Object.fromEntries(Object.entries(templates).map(([artifactId, info]) => [artifactId, {
|
|
14436
|
+
...info,
|
|
14437
|
+
path: toAbsoluteProjectPath(this.projectDir, info.path),
|
|
14438
|
+
displayPath: toOpsxDisplayPath(info.path, {
|
|
14439
|
+
source: info.source,
|
|
14440
|
+
projectDir: this.projectDir
|
|
14441
|
+
})
|
|
14442
|
+
}]));
|
|
14350
14443
|
}
|
|
14351
14444
|
async fetchTemplateContents(schema$6) {
|
|
14352
14445
|
await this.ensureTemplates(schema$6);
|
|
@@ -14355,6 +14448,10 @@ var OpsxKernel = class {
|
|
|
14355
14448
|
return [artifactId, {
|
|
14356
14449
|
content: await reactiveReadFile(info.path),
|
|
14357
14450
|
path: info.path,
|
|
14451
|
+
displayPath: info.displayPath ?? toOpsxDisplayPath(info.path, {
|
|
14452
|
+
source: info.source,
|
|
14453
|
+
projectDir: this.projectDir
|
|
14454
|
+
}),
|
|
14358
14455
|
source: info.source
|
|
14359
14456
|
}];
|
|
14360
14457
|
}));
|
|
@@ -25370,4 +25467,4 @@ async function startServer$1(options = {}) {
|
|
|
25370
25467
|
}
|
|
25371
25468
|
|
|
25372
25469
|
//#endregion
|
|
25373
|
-
export { SchemaInfoSchema as a,
|
|
25470
|
+
export { SchemaInfoSchema as a, toOpsxDisplayPath as c, DEFAULT_CONFIG as d, OpenSpecAdapter as f, SchemaDetailSchema as i, CliExecutor as l, __toESM$1 as m, createServer$2 as n, SchemaResolutionSchema as o, __commonJS$1 as p, require_dist as r, TemplatesSchema as s, startServer$1 as t, ConfigManager as u };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openspecui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "OpenSpec UI - Visual interface for spec-driven development",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.mjs",
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
"typescript": "^5.7.2",
|
|
34
34
|
"vitest": "^2.1.8",
|
|
35
35
|
"yargs": "^18.0.0",
|
|
36
|
-
"@openspecui/server": "1.
|
|
37
|
-
"@openspecui/web": "1.
|
|
36
|
+
"@openspecui/server": "1.6.0",
|
|
37
|
+
"@openspecui/web": "1.6.0"
|
|
38
38
|
},
|
|
39
39
|
"scripts": {
|
|
40
40
|
"build": "pnpm run build:web && pnpm run build:copy-web && tsdown",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{y as U,z as g,A as c,B as S,D as _,F as m,H as I,J as p}from"./index-
|
|
1
|
+
import{y as U,z as g,A as c,B as S,D as _,F as m,H as I,J as p}from"./index-CKfGQiMr.js";const x={name:"local-uniform-bit",vertex:{header:`
|
|
2
2
|
|
|
3
3
|
struct LocalUniforms {
|
|
4
4
|
uTransformMatrix:mat3x3<f32>,
|