@newlandnpt/pep-cli 0.9.0-rc.2 → 0.9.0-rc.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +14 -0
  2. package/dist/pep.cjs +123 -16
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -77,6 +77,20 @@ pep setup --skip-skills # only sign in and configure Maven
77
77
  pep setup group/sub -p # a different group, into THIS project
78
78
  ```
79
79
 
80
+ ### Seeing what a run would do
81
+
82
+ ```powershell
83
+ pep setup --json
84
+ ```
85
+
86
+ Prints the plan and **installs nothing**: which repositories the group holds right now, which of them
87
+ are new, where each would land, and which entries on your list are no longer in the group — together
88
+ with the skill names those occupy, which is what makes one block another. Useful before a first run,
89
+ and when an install fails for a reason you would rather understand than guess at.
90
+
91
+ It never opens a browser: with no valid sign-in it reports `"login": "required"` and exits non-zero.
92
+ It does read the group, so it may refresh your token, exactly as `pep auth status` does.
93
+
80
94
  `--issuer`, `--client-id` and `--resource` work here exactly as on `pep auth login`, for pointing a
81
95
  run at another deployment:
82
96
 
package/dist/pep.cjs CHANGED
@@ -1176,6 +1176,13 @@ never deleted. \`pep skills remove <repo>\` drops one for good.
1176
1176
  The group is baked into this build and is not remembered between runs: pass one
1177
1177
  to install from a different group, just for this run.
1178
1178
 
1179
+ --json prints what this run would do \u2014 which repositories the group holds, which
1180
+ of them are new, where each would land, and which entries on your list are no
1181
+ longer in the group (with the skill names they occupy, which is what makes one
1182
+ block another). It installs nothing, writes nothing and never opens a browser.
1183
+ It does need a valid sign-in to read the group, so it may refresh your token,
1184
+ exactly as \`pep auth status\` does.
1185
+
1179
1186
  --issuer, --client-id and --resource are passed to step 1, exactly as on
1180
1187
  \`pep auth login\`, and are not remembered either. If you are already signed in to
1181
1188
  a different deployment, setup signs in again rather than silently using the one
@@ -1188,13 +1195,15 @@ replaces it \u2014 run \`pep auth login\` to go back.`,
1188
1195
  "--dir <path> Install skills into this directory instead",
1189
1196
  `--issuer <url> PEP server for this run (default: ${DEFAULT_ISSUER})`,
1190
1197
  "--client-id <id> Registered OAuth client for this run",
1198
+ "--json Print what this run would do, install nothing",
1191
1199
  "--resource <uri> Token audience; repeat for multiple resources"
1192
1200
  ],
1193
1201
  examples: [
1194
1202
  "pep setup",
1195
1203
  "pep setup --skip-nexus",
1196
1204
  `pep setup ${DEFAULT_SKILLS_GROUP} -p`,
1197
- "pep setup --issuer https://pep-dev.example --client-id <that deployment's id>"
1205
+ "pep setup --issuer https://pep-dev.example --client-id <that deployment's id>",
1206
+ "pep setup --json"
1198
1207
  ]
1199
1208
  }
1200
1209
  };
@@ -1834,6 +1843,17 @@ async function installGroup(dependencies, remote) {
1834
1843
  if (repos.length === 0) {
1835
1844
  dependencies.onProgress(` ${group} lists no repositories \u2014 nothing to install.`);
1836
1845
  }
1846
+ const drifted = driftedFromGroup(
1847
+ dependencies.installed.map((one) => one.source),
1848
+ repos,
1849
+ group
1850
+ );
1851
+ if (drifted.length > 0) {
1852
+ dependencies.onProgress(` \u26A0 On your list but no longer in ${group}:`);
1853
+ for (const source of drifted) dependencies.onProgress(` ${source}`);
1854
+ dependencies.onProgress(" Nothing was deleted. If one of them blocks an install below,");
1855
+ dependencies.onProgress(" `pep skills remove <repo>` drops it for good.");
1856
+ }
1837
1857
  const installed = [];
1838
1858
  const failed = [];
1839
1859
  for (const [index, source] of repos.entries()) {
@@ -1846,13 +1866,7 @@ async function installGroup(dependencies, remote) {
1846
1866
  dependencies.onProgress(` \u2717 ${source}: ${message}`);
1847
1867
  }
1848
1868
  }
1849
- return {
1850
- status: "installed",
1851
- group,
1852
- installed,
1853
- failed,
1854
- drifted: driftedFromGroup(dependencies.installedSources, repos, group)
1855
- };
1869
+ return { status: "installed", group, installed, failed, drifted };
1856
1870
  }
1857
1871
  async function runSetup(dependencies) {
1858
1872
  dependencies.onProgress("\u2460 Signing in to PEP\u2026");
@@ -1922,9 +1936,74 @@ function setupFailureSummary(report) {
1922
1936
  if (failed.length === 0) return void 0;
1923
1937
  return `Setup finished with problems: ${failed.join(", ")} did not complete.`;
1924
1938
  }
1939
+ async function planSetup(dependencies) {
1940
+ const group = dependencies.group;
1941
+ const nexus = dependencies.skipNexus ? "skipped" : "planned";
1942
+ const bySource = new Map(dependencies.installed.map((one) => [one.source, one]));
1943
+ let current;
1944
+ try {
1945
+ current = await dependencies.auth.status();
1946
+ } catch {
1947
+ }
1948
+ if (current === void 0) {
1949
+ return {
1950
+ group,
1951
+ issuer: null,
1952
+ account: null,
1953
+ login: "required",
1954
+ skills: { status: "unavailable", reason: "not signed in \u2014 run `pep auth login` first" },
1955
+ nexus
1956
+ };
1957
+ }
1958
+ const issuer = current.authorization.issuer;
1959
+ if (dependencies.requestedIssuer !== void 0 && !sameIssuer(issuer, dependencies.requestedIssuer)) {
1960
+ return {
1961
+ group,
1962
+ issuer,
1963
+ account: accountOf(current.user),
1964
+ login: "different-deployment",
1965
+ skills: {
1966
+ status: "unavailable",
1967
+ reason: `signed in to ${issuer}, but --issuer names ${dependencies.requestedIssuer}; a real run would sign in again first`
1968
+ },
1969
+ nexus
1970
+ };
1971
+ }
1972
+ const plan = {
1973
+ group,
1974
+ issuer,
1975
+ account: accountOf(current.user),
1976
+ login: "reuse",
1977
+ nexus
1978
+ };
1979
+ if (dependencies.skipSkills) {
1980
+ return { ...plan, skills: { status: "skipped", reason: "--skip-skills" } };
1981
+ }
1982
+ let repos;
1983
+ try {
1984
+ repos = await dependencies.listGroupRepos({
1985
+ issuer,
1986
+ accessToken: current.authorization.accessToken
1987
+ });
1988
+ } catch (error) {
1989
+ return { ...plan, skills: { status: "unavailable", reason: messageOf(error) } };
1990
+ }
1991
+ const repositories = repos.map((path) => {
1992
+ const known = bySource.get(path);
1993
+ return known === void 0 ? { path, state: "new", directory: dependencies.directoryFor(path) } : {
1994
+ path,
1995
+ state: "installed",
1996
+ directory: known.directory,
1997
+ ...known.commit !== void 0 ? { commit: known.commit } : {}
1998
+ };
1999
+ });
2000
+ const gone = new Set(driftedFromGroup(dependencies.installed.map((one) => one.source), repos, group));
2001
+ const drifted = dependencies.installed.filter((one) => gone.has(one.source));
2002
+ return { ...plan, skills: { status: "planned", repositories, drifted } };
2003
+ }
1925
2004
 
1926
2005
  // src/cli.ts
1927
- var VERSION = false ? "0.0.0-dev" : "0.9.0-rc.2";
2006
+ var VERSION = false ? "0.0.0-dev" : "0.9.0-rc.4";
1928
2007
  var SKILLS_ADD_DEPRECATION = "\u26A0 `pep skills add` will be removed in a future release.";
1929
2008
  function reportUpdate(result) {
1930
2009
  const conflicts = () => {
@@ -2108,12 +2187,14 @@ function reportSetup(report) {
2108
2187
  console.error(` \u2717 ${source}: ${message}`);
2109
2188
  }
2110
2189
  if (skills.drifted.length > 0) {
2111
- console.error(`
2112
- \u26A0 Still on your list, but no longer in ${skills.group}:`);
2113
- for (const source of skills.drifted) console.error(` ${source}`);
2114
- console.error(" They were left in place \u2014 nothing was deleted. `pep skills update` will");
2115
- console.error(" report 404 for each of them (which reads exactly like a typo, but is not).");
2116
- console.error(" `pep skills remove <repo>` drops one for good.");
2190
+ const n = skills.drifted.length;
2191
+ console.error(
2192
+ `
2193
+ \u26A0 ${n} ${n === 1 ? "repository" : "repositories"} on your list ${n === 1 ? "is" : "are"} no longer in ${skills.group} (listed above).`
2194
+ );
2195
+ console.error(" Nothing was deleted. `pep skills update` will report 404 for each of them");
2196
+ console.error(" (which reads exactly like a typo, but is not). `pep skills remove <repo>`");
2197
+ console.error(" drops one for good.");
2117
2198
  }
2118
2199
  }
2119
2200
  if (report.nexus.status === "skipped") {
@@ -2141,11 +2222,37 @@ async function main() {
2141
2222
  const skipNexus = flag(args, "--skip-nexus");
2142
2223
  const skipSkills = flag(args, "--skip-skills");
2143
2224
  const loginOptions = takeLoginOptions(args);
2225
+ const wantsJson = flag(args, "--json");
2144
2226
  const { requested, chosenTarget } = parseSkillsArgs("setup", args);
2145
2227
  const skillsGroup = requested ?? DEFAULT_SKILLS_GROUP;
2146
2228
  const { configStore: configStore2, auth: auth2 } = createCliServices();
2147
2229
  const stateStore = fileSkillsStateStore();
2148
2230
  const state = await stateStore.read();
2231
+ const installed = Object.entries(state?.packages ?? {}).map(([source, pkg]) => ({
2232
+ source,
2233
+ directory: pkg.directory,
2234
+ skills: Object.keys(pkg.skills).sort(),
2235
+ ...pkg.commit !== void 0 ? { commit: pkg.commit } : {}
2236
+ }));
2237
+ if (wantsJson) {
2238
+ const plan = await planSetup({
2239
+ auth: auth2,
2240
+ ...issuerOf(loginOptions) !== void 0 ? { requestedIssuer: issuerOf(loginOptions) } : {},
2241
+ group: skillsGroup,
2242
+ skipSkills,
2243
+ skipNexus,
2244
+ listGroupRepos: (remote) => listGroupRepos({ ...remote, group: skillsGroup }),
2245
+ installed,
2246
+ // ⚠ 与真跑**同一条推导**(`targetForSource`):干跑报的落点要是和真跑落的地方
2247
+ // 不一样,那它比没有还坏。
2248
+ directoryFor: (source) => targetForSource(chosenTarget, state?.packages[source]).directory
2249
+ });
2250
+ console.log(JSON.stringify(plan, null, 2));
2251
+ if (plan.skills.status === "unavailable") {
2252
+ throw new Error(`--json could not plan the skills step: ${plan.skills.reason}`);
2253
+ }
2254
+ return;
2255
+ }
2149
2256
  const report = await runSetup({
2150
2257
  auth: auth2,
2151
2258
  // ⚠ 传 `loginOptions` 而不是 `args`:后者到这里**恒为空**(`parseSkillsArgs` 要么抛、
@@ -2171,7 +2278,7 @@ async function main() {
2171
2278
  },
2172
2279
  // 漂移比对的另一半。取**这一趟开始前**的账 —— 装完再读的话,本轮刚装上的那些也在
2173
2280
  // 里头,而它们当然都还在群里,比出来一样,只是白读一遍。
2174
- installedSources: Object.keys(state?.packages ?? {}),
2281
+ installed,
2175
2282
  skipNexus,
2176
2283
  setupNexus: (remote) => setupNexusCredential(remote),
2177
2284
  onProgress: (line) => console.error(line),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newlandnpt/pep-cli",
3
- "version": "0.9.0-rc.2",
3
+ "version": "0.9.0-rc.4",
4
4
  "description": "PEP CLI —— 凭 PEP 身份读取 Newland 开发者文档与 agent skills",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",