@pmate/cli 0.17.8 → 0.19.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/index.d.ts CHANGED
@@ -133,6 +133,22 @@ type QuizCommandArgs = {
133
133
  paperId: string;
134
134
  apiBaseUrl?: string;
135
135
  linkBaseUrl?: string;
136
+ } | {
137
+ type: Command.Quiz;
138
+ action: "view";
139
+ paperId: string;
140
+ apiBaseUrl?: string;
141
+ linkBaseUrl?: string;
142
+ } | {
143
+ type: Command.Quiz;
144
+ action: "edit-step";
145
+ paperId: string;
146
+ stepId: string;
147
+ filePath: string;
148
+ newPaperId?: string;
149
+ apiBaseUrl?: string;
150
+ linkBaseUrl?: string;
151
+ activate: boolean;
136
152
  };
137
153
  type LinearPrCommandArgs = {
138
154
  type: Command.Linear;
package/dist/index.js CHANGED
@@ -13043,6 +13043,7 @@ var LoginCommandHandler = class {
13043
13043
  const callback = await this.startCallbackServer();
13044
13044
  const appId = args.app?.trim() || DEFAULT_APP_ID;
13045
13045
  const loginUrl = buildHostedLoginUrl(callback.redirectUrl, appId);
13046
+ let savedSession = null;
13046
13047
  ui.info("Opening browser for login.", {
13047
13048
  url: loginUrl,
13048
13049
  app: appId
@@ -13058,11 +13059,15 @@ var LoginCommandHandler = class {
13058
13059
  try {
13059
13060
  const token = await callback.waitForToken;
13060
13061
  const loginResponse = await this.fetchSession(token, appId);
13061
- const saved = saveLoginSession(loginResponse, appId);
13062
- ui.success(`Logged in to ${saved.app}. Session saved to ${saved.sessionPath}`);
13062
+ savedSession = saveLoginSession(loginResponse, appId);
13063
13063
  } finally {
13064
13064
  await callback.close();
13065
13065
  }
13066
+ if (savedSession) {
13067
+ ui.success(
13068
+ `Logged in to ${savedSession.app}. Session saved to ${savedSession.sessionPath}`
13069
+ );
13070
+ }
13066
13071
  }
13067
13072
  async fetchSession(token, appId) {
13068
13073
  const url = new URL(`${AUTH_API_BASE_URL}/session`);
@@ -18058,6 +18063,60 @@ async function activateQuizPaper({
18058
18063
  data: activated
18059
18064
  };
18060
18065
  }
18066
+ async function viewQuizPaper({
18067
+ fetchImpl = fetch,
18068
+ apiBaseUrl,
18069
+ linkBaseUrl,
18070
+ paperId,
18071
+ token
18072
+ }) {
18073
+ const trimmedApiBaseUrl = apiBaseUrl.replace(/\/+$/, "");
18074
+ const trimmedLinkBaseUrl = linkBaseUrl.replace(/\/+$/, "");
18075
+ const previewLink = `${trimmedLinkBaseUrl}/?paper=${encodeURIComponent(paperId)}`;
18076
+ const data = await requestJson(
18077
+ fetchImpl,
18078
+ `${trimmedApiBaseUrl}/api/quiz/papers/${encodeURIComponent(paperId)}`,
18079
+ {
18080
+ method: "GET",
18081
+ headers: {
18082
+ Authorization: `Bearer ${token}`
18083
+ }
18084
+ },
18085
+ "\u67E5\u770B\u8BD5\u5377"
18086
+ );
18087
+ const payload = data ?? {};
18088
+ const status = payload.status;
18089
+ const paperStatus = status === "draft" || status === "active" ? status : "unknown";
18090
+ const stepCount = typeof payload.stepCount === "number" ? payload.stepCount : Array.isArray(payload.steps) ? payload.steps.length : void 0;
18091
+ return {
18092
+ apiBaseUrl: trimmedApiBaseUrl,
18093
+ paperId,
18094
+ previewLink,
18095
+ paperStatus,
18096
+ stepCount,
18097
+ data
18098
+ };
18099
+ }
18100
+ async function getQuizPaper({
18101
+ fetchImpl = fetch,
18102
+ apiBaseUrl,
18103
+ linkBaseUrl,
18104
+ paperId,
18105
+ token
18106
+ }) {
18107
+ const viewed = await viewQuizPaper({
18108
+ fetchImpl,
18109
+ apiBaseUrl,
18110
+ linkBaseUrl,
18111
+ paperId,
18112
+ token
18113
+ });
18114
+ const paper = viewed.data;
18115
+ if (!paper || !Array.isArray(paper.steps)) {
18116
+ throw new Error("\u67E5\u770B\u8BD5\u5377\u5931\u8D25\uFF1A\u8FD4\u56DE\u6570\u636E\u4E0D\u5305\u542B\u6709\u6548 steps\u3002");
18117
+ }
18118
+ return paper;
18119
+ }
18061
18120
 
18062
18121
  // src/commands/QuizCommandHandler.ts
18063
18122
  var QuizCommandHandler = class {
@@ -18075,6 +18134,65 @@ var QuizCommandHandler = class {
18075
18134
  ui.success("Quiz \u5DF2\u6FC0\u6D3B\u53D1\u5E03", result2.link);
18076
18135
  return;
18077
18136
  }
18137
+ if (args.action === "view") {
18138
+ const result2 = await viewQuizPaper({
18139
+ apiBaseUrl,
18140
+ linkBaseUrl,
18141
+ paperId: args.paperId,
18142
+ token: session.token
18143
+ });
18144
+ ui.success("Quiz \u8BD5\u5377\u5DF2\u627E\u5230", result2.previewLink);
18145
+ ui.info("\u72B6\u6001", result2.paperStatus);
18146
+ if (typeof result2.stepCount === "number") {
18147
+ ui.info("\u9898\u76EE\u6570", String(result2.stepCount));
18148
+ }
18149
+ return;
18150
+ }
18151
+ if (args.action === "edit-step") {
18152
+ const basePaper = await getQuizPaper({
18153
+ apiBaseUrl,
18154
+ linkBaseUrl,
18155
+ paperId: args.paperId,
18156
+ token: session.token
18157
+ });
18158
+ const targetIndex = basePaper.steps.findIndex((item) => item.stepId === args.stepId);
18159
+ if (targetIndex < 0) {
18160
+ throw new Error(`\u672A\u627E\u5230 stepId=${args.stepId}\uFF0C\u65E0\u6CD5\u66F4\u65B0\u8BE5\u9898\u76EE\u3002`);
18161
+ }
18162
+ const resolvedFilePath2 = import_node_path14.default.resolve(process.cwd(), args.filePath);
18163
+ const nextStep = JSON.parse((0, import_node_fs9.readFileSync)(resolvedFilePath2, "utf8"));
18164
+ nextStep.stepId = args.stepId;
18165
+ const nextSteps = [...basePaper.steps];
18166
+ nextSteps[targetIndex] = nextStep;
18167
+ const nextPaperId = args.newPaperId || `${args.paperId}-rev-${this.timestampTag()}`;
18168
+ const result2 = await publishQuizPaper({
18169
+ apiBaseUrl,
18170
+ linkBaseUrl,
18171
+ paperId: nextPaperId,
18172
+ payload: {
18173
+ eyebrow: basePaper.eyebrow ?? "",
18174
+ completionTitle: basePaper.completionTitle ?? "",
18175
+ completionSummaryPerfect: basePaper.completionSummaryPerfect ?? "",
18176
+ completionSummaryReview: basePaper.completionSummaryReview ?? "",
18177
+ steps: nextSteps
18178
+ },
18179
+ token: session.token,
18180
+ activate: args.activate
18181
+ });
18182
+ if (result2.status === "draft") {
18183
+ ui.success("Quiz \u5355\u9898\u4FEE\u8BA2\u7A3F\u5DF2\u521B\u5EFA", result2.previewLink);
18184
+ ui.info("\u6765\u6E90\u8BD5\u5377", args.paperId);
18185
+ ui.info("\u4FEE\u8BA2\u9898\u76EE", args.stepId);
18186
+ ui.info("\u65B0\u8BD5\u5377", nextPaperId);
18187
+ ui.info("\u4E0B\u4E00\u6B65", `pmate quiz activate --paper-id ${nextPaperId}`);
18188
+ return;
18189
+ }
18190
+ ui.success("Quiz \u5355\u9898\u4FEE\u8BA2\u7A3F\u5DF2\u53D1\u5E03", result2.link);
18191
+ ui.info("\u6765\u6E90\u8BD5\u5377", args.paperId);
18192
+ ui.info("\u4FEE\u8BA2\u9898\u76EE", args.stepId);
18193
+ ui.info("\u65B0\u8BD5\u5377", nextPaperId);
18194
+ return;
18195
+ }
18078
18196
  const resolvedFilePath = import_node_path14.default.resolve(process.cwd(), args.filePath);
18079
18197
  const payload = JSON.parse((0, import_node_fs9.readFileSync)(resolvedFilePath, "utf8"));
18080
18198
  const result = await publishQuizPaper({
@@ -18093,6 +18211,19 @@ var QuizCommandHandler = class {
18093
18211
  }
18094
18212
  ui.success("Quiz \u5DF2\u53D1\u5E03", result.link);
18095
18213
  }
18214
+ timestampTag() {
18215
+ const now = /* @__PURE__ */ new Date();
18216
+ const pad = (value) => String(value).padStart(2, "0");
18217
+ return [
18218
+ now.getFullYear(),
18219
+ pad(now.getMonth() + 1),
18220
+ pad(now.getDate()),
18221
+ "-",
18222
+ pad(now.getHours()),
18223
+ pad(now.getMinutes()),
18224
+ pad(now.getSeconds())
18225
+ ].join("");
18226
+ }
18096
18227
  };
18097
18228
 
18098
18229
  // src/commands/CollectCommandHandler.ts
@@ -21769,9 +21900,11 @@ var HELP_TEXT = [
21769
21900
  ` ${import_chalk3.default.gray(" [--push] [--cwd <dir>]")}`,
21770
21901
  ` ${import_chalk3.default.cyan("summary")} Summarize a GitHub PR with AI`,
21771
21902
  ` ${import_chalk3.default.gray(" --pr <id> [--cwd <dir>]")}`,
21772
- ` ${import_chalk3.default.cyan("quiz")} Publish quiz papers to quiz-api`,
21903
+ ` ${import_chalk3.default.cyan("quiz")} Manage quiz papers on quiz-api`,
21773
21904
  ` ${import_chalk3.default.gray(" publish --file <json> --paper-id <id> [--activate]")}`,
21774
21905
  ` ${import_chalk3.default.gray(" activate --paper-id <id>")}`,
21906
+ ` ${import_chalk3.default.gray(" view --paper-id <id>")}`,
21907
+ ` ${import_chalk3.default.gray(" edit-step --paper-id <id> --step-id <step-id> --file <step.json> [--new-paper-id <id>] [--activate]")}`,
21775
21908
  ` ${import_chalk3.default.cyan("linear")} Create Linear issues`,
21776
21909
  ` ${import_chalk3.default.gray(" (default) create via REPL")}`,
21777
21910
  ` ${import_chalk3.default.gray(" create --repl [--cwd <dir>]")}`,
@@ -22368,7 +22501,7 @@ function parseQuizArgs(tokens) {
22368
22501
  const rest = [...tokens];
22369
22502
  const action = rest.shift();
22370
22503
  if (!action) {
22371
- throw new Error("Missing quiz action. Usage: quiz publish|activate");
22504
+ throw new Error("Missing quiz action. Usage: quiz publish|activate|view");
22372
22505
  }
22373
22506
  if (action === "publish") {
22374
22507
  let filePath;
@@ -22445,6 +22578,94 @@ function parseQuizArgs(tokens) {
22445
22578
  linkBaseUrl
22446
22579
  };
22447
22580
  }
22581
+ if (action === "view") {
22582
+ let paperId;
22583
+ let apiBaseUrl;
22584
+ let linkBaseUrl;
22585
+ while (rest.length > 0) {
22586
+ const token = rest.shift();
22587
+ switch (token) {
22588
+ case "--paper-id":
22589
+ paperId = requireValue8(rest, token);
22590
+ break;
22591
+ case "--base-url":
22592
+ apiBaseUrl = requireValue8(rest, token);
22593
+ break;
22594
+ case "--link-base":
22595
+ linkBaseUrl = requireValue8(rest, token);
22596
+ break;
22597
+ default:
22598
+ throw new Error(`Unknown quiz view option: ${token}`);
22599
+ }
22600
+ }
22601
+ if (!paperId) {
22602
+ throw new Error("Missing --paper-id. Usage: quiz view --paper-id <id>.");
22603
+ }
22604
+ return {
22605
+ type: "quiz" /* Quiz */,
22606
+ action: "view",
22607
+ paperId,
22608
+ apiBaseUrl,
22609
+ linkBaseUrl
22610
+ };
22611
+ }
22612
+ if (action === "edit-step") {
22613
+ let paperId;
22614
+ let stepId;
22615
+ let filePath;
22616
+ let newPaperId;
22617
+ let apiBaseUrl;
22618
+ let linkBaseUrl;
22619
+ let activate = false;
22620
+ while (rest.length > 0) {
22621
+ const token = rest.shift();
22622
+ switch (token) {
22623
+ case "--paper-id":
22624
+ paperId = requireValue8(rest, token);
22625
+ break;
22626
+ case "--step-id":
22627
+ stepId = requireValue8(rest, token);
22628
+ break;
22629
+ case "--file":
22630
+ filePath = requireValue8(rest, token);
22631
+ break;
22632
+ case "--new-paper-id":
22633
+ newPaperId = requireValue8(rest, token);
22634
+ break;
22635
+ case "--base-url":
22636
+ apiBaseUrl = requireValue8(rest, token);
22637
+ break;
22638
+ case "--link-base":
22639
+ linkBaseUrl = requireValue8(rest, token);
22640
+ break;
22641
+ case "--activate":
22642
+ activate = true;
22643
+ break;
22644
+ default:
22645
+ throw new Error(`Unknown quiz edit-step option: ${token}`);
22646
+ }
22647
+ }
22648
+ if (!paperId) {
22649
+ throw new Error("Missing --paper-id. Usage: quiz edit-step --paper-id <id> --step-id <id> --file <step.json>.");
22650
+ }
22651
+ if (!stepId) {
22652
+ throw new Error("Missing --step-id. Usage: quiz edit-step --paper-id <id> --step-id <id> --file <step.json>.");
22653
+ }
22654
+ if (!filePath) {
22655
+ throw new Error("Missing --file. Usage: quiz edit-step --paper-id <id> --step-id <id> --file <step.json>.");
22656
+ }
22657
+ return {
22658
+ type: "quiz" /* Quiz */,
22659
+ action: "edit-step",
22660
+ paperId,
22661
+ stepId,
22662
+ filePath,
22663
+ newPaperId,
22664
+ apiBaseUrl,
22665
+ linkBaseUrl,
22666
+ activate
22667
+ };
22668
+ }
22448
22669
  throw new Error(`Unknown quiz action: ${action}`);
22449
22670
  }
22450
22671
  function parseLinearArgs(tokens) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pmate/cli",
3
- "version": "0.17.8",
3
+ "version": "0.19.0",
4
4
  "description": "CLI utilities for the pMate project",
5
5
  "license": "MIT",
6
6
  "bin": {