@clawos-dev/clawd 0.2.313 → 0.2.315

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.cjs CHANGED
@@ -6584,6 +6584,32 @@ var init_shift_internal = __esm({
6584
6584
  }
6585
6585
  });
6586
6586
 
6587
+ // ../protocol/src/github.ts
6588
+ var GITHUB_PR_ROLES, REPO_SEGMENT, owner, repo, number, GithubPrListArgs, GithubPrDetailArgs, GithubPrMergeArgs, GithubPrCommentArgs, GithubPrSetReadyArgs, GithubCollaboratorsArgs, GithubPrRequestReviewersArgs;
6589
+ var init_github = __esm({
6590
+ "../protocol/src/github.ts"() {
6591
+ "use strict";
6592
+ init_zod();
6593
+ GITHUB_PR_ROLES = ["created", "review-requested"];
6594
+ REPO_SEGMENT = /^[A-Za-z0-9._-]+$/;
6595
+ owner = external_exports.string().min(1).regex(REPO_SEGMENT);
6596
+ repo = external_exports.string().min(1).regex(REPO_SEGMENT);
6597
+ number = external_exports.number().int().positive();
6598
+ GithubPrListArgs = external_exports.object({ role: external_exports.enum(GITHUB_PR_ROLES) });
6599
+ GithubPrDetailArgs = external_exports.object({ owner, repo, number });
6600
+ GithubPrMergeArgs = external_exports.object({ owner, repo, number, expectedHeadSha: external_exports.string().min(1) });
6601
+ GithubPrCommentArgs = external_exports.object({ owner, repo, number, body: external_exports.string().min(1) });
6602
+ GithubPrSetReadyArgs = external_exports.object({ owner, repo, number, ready: external_exports.boolean() });
6603
+ GithubCollaboratorsArgs = external_exports.object({ owner, repo });
6604
+ GithubPrRequestReviewersArgs = external_exports.object({
6605
+ owner,
6606
+ repo,
6607
+ number,
6608
+ reviewers: external_exports.array(external_exports.string().min(1)).min(1)
6609
+ });
6610
+ }
6611
+ });
6612
+
6587
6613
  // ../protocol/src/lark-bot-schemas.ts
6588
6614
  var LarkBotPersonaArgs, LarkBotUnbindArgs, LarkBotStatusArgs, LarkBotProvisionArgs, LarkBotProvisionCancelArgs, LARK_BOT_PROVISION_ERROR_REASONS, LarkBotProvisionErrorReasonSchema, LarkBotBindManualArgs, LarkBotGroupSchema, LarkBotStatusResultSchema, LarkBotStateFrameSchema;
6589
6615
  var init_lark_bot_schemas = __esm({
@@ -7878,6 +7904,7 @@ var init_methods = __esm({
7878
7904
  init_inbox();
7879
7905
  init_peer_exec();
7880
7906
  init_shift_internal();
7907
+ init_github();
7881
7908
  init_lark_bot_schemas();
7882
7909
  init_persona_schemas();
7883
7910
  init_schemas();
@@ -8041,6 +8068,38 @@ var init_methods = __esm({
8041
8068
  summary: "\u5217\u51FA owner \u7684\u5168\u90E8 shift\uFF08\u6392\u73ED\uFF09\uFF0C\u7ED9 UI \u65E5\u5386\u6E32\u67D3\u7528\uFF1Bowner-only",
8042
8069
  args: NO_ARGS
8043
8070
  },
8071
+ "github:status": {
8072
+ summary: "\u63A2\u6D4B\u672C\u673A gh CLI\uFF08PR \u6536\u4EF6\u7BB1\u524D\u7F6E\uFF09\uFF1A\u662F\u5426\u5B89\u88C5\u3001\u662F\u5426\u5DF2\u767B\u5F55 github.com\u3001\u767B\u5F55\u540D\u3002\u65E0\u53C2\u6570",
8073
+ args: NO_ARGS
8074
+ },
8075
+ "github:prList": {
8076
+ summary: "\u5217 open PR\uFF08\u542B draft\uFF09\u3002role=created \u7531\u6211\u521B\u5EFA / review-requested \u8BF7\u6C42\u6211\u8BC4\u5BA1\uFF1BGitHub \u5168\u57DF search\uFF0Cupdated \u5012\u5E8F",
8077
+ args: GithubPrListArgs
8078
+ },
8079
+ "github:prDetail": {
8080
+ summary: "\u5355\u4E2A PR \u8BE6\u60C5\uFF1A\u5206\u652F/diffstat/\u5BA1\u67E5\u8005/\u8BC4\u8BBA\u6570/checks \u9010\u9879/mergeable/headRefOid\uFF08\u5408\u5E76\u5E76\u53D1\u4FDD\u62A4\u7528\uFF09",
8081
+ args: GithubPrDetailArgs
8082
+ },
8083
+ "github:prMerge": {
8084
+ summary: "squash \u5408\u5E76\u3002expectedHeadSha \u4E0E\u670D\u52A1\u7AEF\u4E0D\u7B26\u65F6\u4E0D\u5408\u5E76\uFF0C\u8FD4\u56DE merged:false + reason=head-changed",
8085
+ args: GithubPrMergeArgs
8086
+ },
8087
+ "github:prComment": {
8088
+ summary: "\u53D1 issue \u8BC4\u8BBA\u5230 PR",
8089
+ args: GithubPrCommentArgs
8090
+ },
8091
+ "github:prSetReady": {
8092
+ summary: "draft\u2194ready \u5207\u6362\uFF1Aready=true \u8F6C\u53EF\u8BC4\u5BA1\uFF0Cfalse \u8F6C\u8349\u7A3F",
8093
+ args: GithubPrSetReadyArgs
8094
+ },
8095
+ "github:collaborators": {
8096
+ summary: "\u5217 repo collaborators \u767B\u5F55\u540D\uFF08\u5BA1\u67E5\u8005\u9009\u4EBA\u540D\u5355\uFF09",
8097
+ args: GithubCollaboratorsArgs
8098
+ },
8099
+ "github:prRequestReviewers": {
8100
+ summary: "\u7ED9 PR \u8BF7\u6C42\u5BA1\u67E5\u8005\uFF08\u8FFD\u52A0\uFF0C\u4E0D\u8986\u76D6\u5DF2\u6709\uFF09",
8101
+ args: GithubPrRequestReviewersArgs
8102
+ },
8044
8103
  "shift-internal:add": {
8045
8104
  summary: "\u6392\u4E00\u4E2A shift\uFF08\u5230\u70B9\u81EA\u52A8\u62C9\u8D77 persona \u5E72\u6D3B\uFF09\u3002\u7701\u7565 targetPersona = \u6D3E\u7ED9\u8C03\u7528\u65B9\u81EA\u5DF1",
8046
8105
  args: ShiftInternalAddArgs
@@ -8529,6 +8588,7 @@ var init_runtime = __esm({
8529
8588
  init_dispatch();
8530
8589
  init_log();
8531
8590
  init_visitor_schemas();
8591
+ init_github();
8532
8592
  init_shift_internal();
8533
8593
  init_lark_bot_schemas();
8534
8594
  init_lark_bot_format();
@@ -11987,11 +12047,11 @@ var require_safe_stable_stringify = __commonJS({
11987
12047
  }
11988
12048
  return value === void 0 ? Infinity : value;
11989
12049
  }
11990
- function getItemCount(number) {
11991
- if (number === 1) {
12050
+ function getItemCount(number2) {
12051
+ if (number2 === 1) {
11992
12052
  return "1 item";
11993
12053
  }
11994
- return `${number} items`;
12054
+ return `${number2} items`;
11995
12055
  }
11996
12056
  function getUniqueReplacerSet(replacerArray) {
11997
12057
  const replacerSet = /* @__PURE__ */ new Set();
@@ -12708,8 +12768,8 @@ var require_pino = __commonJS({
12708
12768
  bindings(bindings) {
12709
12769
  return bindings;
12710
12770
  },
12711
- level(label, number) {
12712
- return { level: number };
12771
+ level(label, number2) {
12772
+ return { level: number2 };
12713
12773
  }
12714
12774
  }),
12715
12775
  hooks: {
@@ -22167,19 +22227,19 @@ var require_luxon = __commonJS({
22167
22227
  return Math.floor(f);
22168
22228
  }
22169
22229
  }
22170
- function roundTo(number, digits, rounding = "round") {
22230
+ function roundTo(number2, digits, rounding = "round") {
22171
22231
  const factor = 10 ** digits;
22172
22232
  switch (rounding) {
22173
22233
  case "expand":
22174
- return number > 0 ? Math.ceil(number * factor) / factor : Math.floor(number * factor) / factor;
22234
+ return number2 > 0 ? Math.ceil(number2 * factor) / factor : Math.floor(number2 * factor) / factor;
22175
22235
  case "trunc":
22176
- return Math.trunc(number * factor) / factor;
22236
+ return Math.trunc(number2 * factor) / factor;
22177
22237
  case "round":
22178
- return Math.round(number * factor) / factor;
22238
+ return Math.round(number2 * factor) / factor;
22179
22239
  case "floor":
22180
- return Math.floor(number * factor) / factor;
22240
+ return Math.floor(number2 * factor) / factor;
22181
22241
  case "ceil":
22182
- return Math.ceil(number * factor) / factor;
22242
+ return Math.ceil(number2 * factor) / factor;
22183
22243
  default:
22184
22244
  throw new RangeError(`Value rounding ${rounding} is out of range`);
22185
22245
  }
@@ -51598,6 +51658,43 @@ function createShiftStore(deps) {
51598
51658
  return { load, flushNow, list, get, add, update, remove, appendRun, patchState };
51599
51659
  }
51600
51660
 
51661
+ // src/github/gh.ts
51662
+ var import_node_child_process7 = require("child_process");
51663
+ var GhError = class extends Error {
51664
+ constructor(kind, message, stderr) {
51665
+ super(message);
51666
+ this.kind = kind;
51667
+ this.stderr = stderr;
51668
+ this.name = "GhError";
51669
+ }
51670
+ kind;
51671
+ stderr;
51672
+ };
51673
+ var DEFAULT_TIMEOUT_MS2 = 15e3;
51674
+ var MAX_BUFFER = 4 * 1024 * 1024;
51675
+ function createGhRunner(defaults) {
51676
+ const baseTimeoutMs = defaults?.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
51677
+ return {
51678
+ run(args, opts) {
51679
+ return new Promise((resolve6, reject) => {
51680
+ (0, import_node_child_process7.execFile)(
51681
+ "gh",
51682
+ args,
51683
+ { timeout: opts?.timeoutMs ?? baseTimeoutMs, maxBuffer: MAX_BUFFER },
51684
+ (err, stdout, stderr) => {
51685
+ if (!err) return resolve6(stdout);
51686
+ if (err.code === "ENOENT") {
51687
+ return reject(new GhError("gh-missing", "GitHub CLI (gh) \u672A\u5B89\u88C5"));
51688
+ }
51689
+ const detail = (stderr ?? "").trim();
51690
+ reject(new GhError("gh-error", detail !== "" ? detail : err.message, detail));
51691
+ }
51692
+ );
51693
+ });
51694
+ }
51695
+ };
51696
+ }
51697
+
51601
51698
  // src/shift/scheduler.ts
51602
51699
  function cleanupOrphanRunningRuns(store, now) {
51603
51700
  for (const shift of store.list()) {
@@ -56435,7 +56532,7 @@ function readSpawnedByDesktopFromEnv(env = process.env) {
56435
56532
  var import_node_fs40 = __toESM(require("fs"), 1);
56436
56533
  var import_node_path44 = __toESM(require("path"), 1);
56437
56534
  var import_node_crypto10 = __toESM(require("crypto"), 1);
56438
- var import_node_child_process9 = require("child_process");
56535
+ var import_node_child_process10 = require("child_process");
56439
56536
 
56440
56537
  // src/tunnel/tunnel-store.ts
56441
56538
  var import_node_fs37 = __toESM(require("fs"), 1);
@@ -56571,7 +56668,7 @@ function escape(v2) {
56571
56668
  var import_node_fs38 = __toESM(require("fs"), 1);
56572
56669
  var import_node_os13 = __toESM(require("os"), 1);
56573
56670
  var import_node_path42 = __toESM(require("path"), 1);
56574
- var import_node_child_process7 = require("child_process");
56671
+ var import_node_child_process8 = require("child_process");
56575
56672
  var import_node_stream3 = require("stream");
56576
56673
  var import_promises5 = require("stream/promises");
56577
56674
  var FRPC_VERSION = "0.68.0";
@@ -56671,7 +56768,7 @@ async function extractFrpcFromTarball(tarball, binDir, version2, platform, destB
56671
56768
  import_node_fs38.default.mkdirSync(work, { recursive: true });
56672
56769
  try {
56673
56770
  await new Promise((resolve6, reject) => {
56674
- const proc = (0, import_node_child_process7.spawn)("tar", ["xzf", tarball, "-C", work], { stdio: "pipe" });
56771
+ const proc = (0, import_node_child_process8.spawn)("tar", ["xzf", tarball, "-C", work], { stdio: "pipe" });
56675
56772
  proc.on("error", reject);
56676
56773
  proc.on("exit", (code) => code === 0 ? resolve6() : reject(new Error(`tar exited ${code}`)));
56677
56774
  });
@@ -56689,7 +56786,7 @@ async function extractFrpcFromTarball(tarball, binDir, version2, platform, destB
56689
56786
  // src/tunnel/frpc-process.ts
56690
56787
  var import_node_fs39 = __toESM(require("fs"), 1);
56691
56788
  var import_node_path43 = __toESM(require("path"), 1);
56692
- var import_node_child_process8 = require("child_process");
56789
+ var import_node_child_process9 = require("child_process");
56693
56790
  function frpcPidFilePath(dataDir) {
56694
56791
  return import_node_path43.default.join(dataDir, "frpc.pid");
56695
56792
  }
@@ -56775,7 +56872,7 @@ async function killStaleFrpc(deps) {
56775
56872
  async function defaultScanFrpcPidsByCmdline(tomlPath, logger) {
56776
56873
  if (process.platform === "win32") return [];
56777
56874
  return new Promise((resolve6) => {
56778
- const ps = (0, import_node_child_process8.spawn)("ps", ["-axo", "pid=,command="], { stdio: ["ignore", "pipe", "ignore"] });
56875
+ const ps = (0, import_node_child_process9.spawn)("ps", ["-axo", "pid=,command="], { stdio: ["ignore", "pipe", "ignore"] });
56779
56876
  let buf = "";
56780
56877
  ps.stdout.on("data", (c) => {
56781
56878
  buf += c.toString();
@@ -56944,7 +57041,7 @@ var TunnelManager = class {
56944
57041
  logLevel: "info"
56945
57042
  });
56946
57043
  await import_node_fs40.default.promises.writeFile(tomlPath, toml, { mode: 384 });
56947
- const proc = (this.deps.spawnImpl ?? import_node_child_process9.spawn)(frpcBin, ["-c", tomlPath], {
57044
+ const proc = (this.deps.spawnImpl ?? import_node_child_process10.spawn)(frpcBin, ["-c", tomlPath], {
56948
57045
  stdio: ["ignore", "pipe", "pipe"]
56949
57046
  });
56950
57047
  const logDir = import_node_path44.default.join(this.deps.dataDir, "log");
@@ -57436,11 +57533,11 @@ var DeviceBindingClientError = class extends Error {
57436
57533
  code;
57437
57534
  cause;
57438
57535
  };
57439
- var DEFAULT_TIMEOUT_MS2 = 8e3;
57536
+ var DEFAULT_TIMEOUT_MS3 = 8e3;
57440
57537
  async function upsertDeviceBinding(opts) {
57441
57538
  const doFetch = opts.fetchImpl ?? fetch;
57442
57539
  const ac = new AbortController();
57443
- const timer = setTimeout(() => ac.abort(), opts.timeoutMs ?? DEFAULT_TIMEOUT_MS2);
57540
+ const timer = setTimeout(() => ac.abort(), opts.timeoutMs ?? DEFAULT_TIMEOUT_MS3);
57444
57541
  const body = { unionId: opts.unionId };
57445
57542
  if (opts.avatarUrl != null) body.avatarUrl = opts.avatarUrl;
57446
57543
  let res;
@@ -58406,11 +58503,11 @@ init_protocol();
58406
58503
  init_protocol();
58407
58504
 
58408
58505
  // src/workspace/git.ts
58409
- var import_node_child_process10 = require("child_process");
58506
+ var import_node_child_process11 = require("child_process");
58410
58507
  var import_node_fs45 = __toESM(require("fs"), 1);
58411
58508
  var import_node_path50 = __toESM(require("path"), 1);
58412
58509
  var import_node_util = require("util");
58413
- var pexec = (0, import_node_util.promisify)(import_node_child_process10.execFile);
58510
+ var pexec = (0, import_node_util.promisify)(import_node_child_process11.execFile);
58414
58511
  function normalizePath(p2) {
58415
58512
  const resolved = import_node_path50.default.resolve(p2);
58416
58513
  try {
@@ -58912,7 +59009,7 @@ function buildWhoamiHandler(deps) {
58912
59009
  }
58913
59010
  };
58914
59011
  }
58915
- const owner = {
59012
+ const owner2 = {
58916
59013
  ...makeOwnerPrincipal(deps.ownerPrincipalId, identity.displayName),
58917
59014
  ownerId: identity.ownerId,
58918
59015
  provider: identity.provider
@@ -58956,7 +59053,7 @@ function buildWhoamiHandler(deps) {
58956
59053
  return {
58957
59054
  response: {
58958
59055
  type: "whoami:ok",
58959
- owner,
59056
+ owner: owner2,
58960
59057
  capability,
58961
59058
  grantedPersonas
58962
59059
  }
@@ -59210,6 +59307,15 @@ var METHOD_GRANT_MAP = {
59210
59307
  // ---- shift:* (排班 mock,owner-only 读) ----
59211
59308
  // mock 阶段只 owner 用;接 guest 后再考虑分级
59212
59309
  "shift:list": ADMIN_ANY,
59310
+ // github:*(PR 收件箱,spec 2026-08-11):走 owner 本机 gh 凭据,owner-only 一刀切
59311
+ "github:status": ADMIN_ANY,
59312
+ "github:prList": ADMIN_ANY,
59313
+ "github:prDetail": ADMIN_ANY,
59314
+ "github:prMerge": ADMIN_ANY,
59315
+ "github:prComment": ADMIN_ANY,
59316
+ "github:prSetReady": ADMIN_ANY,
59317
+ "github:collaborators": ADMIN_ANY,
59318
+ "github:prRequestReviewers": ADMIN_ANY,
59213
59319
  // ---- shift-internal:* + inbox:sendDm + personaDispatch complete/list ----
59214
59320
  // 统一 RPC 鉴权(spec 2026-07-28):原 /api/rpc 白名单 method 收编。调用方 = 本机
59215
59321
  // MCP server 持 owner token → ADMIN_ANY 即「owner token 即可」;业务归因(sendDm 的
@@ -59356,7 +59462,7 @@ function computeMethodAccess(args) {
59356
59462
  }
59357
59463
 
59358
59464
  // src/version.ts
59359
- var version = "0.2.313".length > 0 ? "0.2.313" : "dev";
59465
+ var version = "0.2.315".length > 0 ? "0.2.315" : "dev";
59360
59466
 
59361
59467
  // src/cli-probe/probe.ts
59362
59468
  var fs56 = __toESM(require("fs"), 1);
@@ -60372,7 +60478,7 @@ function buildExtensionHandlers(deps) {
60372
60478
 
60373
60479
  // src/app-builder/project-store.ts
60374
60480
  var import_node_fs46 = require("fs");
60375
- var import_node_child_process11 = require("child_process");
60481
+ var import_node_child_process12 = require("child_process");
60376
60482
  var import_node_path57 = require("path");
60377
60483
  init_protocol();
60378
60484
  var PROJECTS_DIR = "projects";
@@ -60477,7 +60583,7 @@ var ProjectStore = class {
60477
60583
  async scaffold(name, templateSrcDir, scaffoldScriptPath) {
60478
60584
  const destDir = this.projectDir(name);
60479
60585
  return await new Promise((resolve6, reject) => {
60480
- const child = (0, import_node_child_process11.spawn)("bash", [scaffoldScriptPath, name, templateSrcDir, destDir], {
60586
+ const child = (0, import_node_child_process12.spawn)("bash", [scaffoldScriptPath, name, templateSrcDir, destDir], {
60481
60587
  env: { ...process.env, PATH: process.env.PATH ?? "" },
60482
60588
  stdio: ["ignore", "pipe", "pipe"]
60483
60589
  });
@@ -60581,7 +60687,7 @@ var ProjectStore = class {
60581
60687
  };
60582
60688
 
60583
60689
  // src/app-builder/kill-port.ts
60584
- var import_node_child_process12 = require("child_process");
60690
+ var import_node_child_process13 = require("child_process");
60585
60691
  async function killPortOccupants(port, ownedPids, logger) {
60586
60692
  let pids;
60587
60693
  try {
@@ -60623,7 +60729,7 @@ async function killPortOccupants(port, ownedPids, logger) {
60623
60729
  }
60624
60730
  function listPidsOnPort(port) {
60625
60731
  return new Promise((resolve6, reject) => {
60626
- (0, import_node_child_process12.execFile)(
60732
+ (0, import_node_child_process13.execFile)(
60627
60733
  "lsof",
60628
60734
  ["-ti", `:${port}`],
60629
60735
  { timeout: 3e3 },
@@ -60695,7 +60801,7 @@ var PublishJobRegistry = class {
60695
60801
  };
60696
60802
 
60697
60803
  // src/app-builder/publish-job-runner.ts
60698
- var import_node_child_process13 = require("child_process");
60804
+ var import_node_child_process14 = require("child_process");
60699
60805
  var import_node_fs47 = require("fs");
60700
60806
  var import_node_path58 = require("path");
60701
60807
 
@@ -60724,7 +60830,7 @@ function tailStderrLines(buf, n) {
60724
60830
  // src/app-builder/publish-job-runner.ts
60725
60831
  async function startPublishJob(deps, args) {
60726
60832
  const { registry: registry2, projectDir } = deps;
60727
- const spawn12 = deps.spawnImpl ?? import_node_child_process13.spawn;
60833
+ const spawn12 = deps.spawnImpl ?? import_node_child_process14.spawn;
60728
60834
  if (registry2.has(args.name)) {
60729
60835
  return { jobId: registry2.get(args.name).jobId, status: "already-publishing" };
60730
60836
  }
@@ -61494,6 +61600,250 @@ function buildShiftHandlers(deps) {
61494
61600
  };
61495
61601
  }
61496
61602
 
61603
+ // src/handlers/github.ts
61604
+ init_protocol();
61605
+
61606
+ // src/github/graphql.ts
61607
+ function buildPrSearchQuery(role) {
61608
+ const roleQualifier = role === "created" ? "author:@me" : "review-requested:@me";
61609
+ return `is:pr is:open ${roleQualifier} sort:updated-desc`;
61610
+ }
61611
+ var PR_LIST_QUERY = `
61612
+ query($searchQuery: String!, $first: Int!) {
61613
+ search(query: $searchQuery, type: ISSUE, first: $first) {
61614
+ nodes {
61615
+ ... on PullRequest {
61616
+ number title isDraft updatedAt additions deletions headRefName
61617
+ author { login }
61618
+ repository { nameWithOwner }
61619
+ commits(last: 1) { nodes { commit { statusCheckRollup { state } } } }
61620
+ }
61621
+ }
61622
+ }
61623
+ }`;
61624
+ var PR_DETAIL_QUERY = `
61625
+ query($owner: String!, $repo: String!, $number: Int!) {
61626
+ repository(owner: $owner, name: $repo) {
61627
+ pullRequest(number: $number) {
61628
+ number title body state isDraft updatedAt additions deletions
61629
+ headRefName baseRefName headRefOid mergeable
61630
+ author { login }
61631
+ reviewRequests(first: 50) { nodes { requestedReviewer { ... on User { login } } } }
61632
+ comments { totalCount }
61633
+ commits(last: 1) { nodes { commit { statusCheckRollup {
61634
+ state
61635
+ contexts(first: 100) { nodes {
61636
+ __typename
61637
+ ... on CheckRun { name status conclusion }
61638
+ ... on StatusContext { context state }
61639
+ } }
61640
+ } } } }
61641
+ }
61642
+ }
61643
+ }`;
61644
+ function mapRollupState(state) {
61645
+ if (state == null) return "none";
61646
+ if (state === "SUCCESS") return "success";
61647
+ if (state === "PENDING" || state === "EXPECTED") return "pending";
61648
+ return "failure";
61649
+ }
61650
+ function mapCheckNode(node) {
61651
+ if (node.__typename === "CheckRun") {
61652
+ const name = typeof node.name === "string" ? node.name : "";
61653
+ if (name === "") return null;
61654
+ if (node.status !== "COMPLETED") return { name, conclusion: "pending" };
61655
+ return { name, conclusion: node.conclusion === "SUCCESS" ? "success" : "failure" };
61656
+ }
61657
+ if (node.__typename === "StatusContext") {
61658
+ const name = typeof node.context === "string" ? node.context : "";
61659
+ if (name === "") return null;
61660
+ if (node.state === "SUCCESS") return { name, conclusion: "success" };
61661
+ if (node.state === "PENDING" || node.state === "EXPECTED") return { name, conclusion: "pending" };
61662
+ return { name, conclusion: "failure" };
61663
+ }
61664
+ return null;
61665
+ }
61666
+ function mapPrState(state, isDraft) {
61667
+ if (state === "MERGED") return "merged";
61668
+ if (state === "CLOSED") return "closed";
61669
+ return isDraft === true ? "draft" : "open";
61670
+ }
61671
+ function asRecord(v2) {
61672
+ return typeof v2 === "object" && v2 !== null ? v2 : {};
61673
+ }
61674
+ function lastCommitRollup(node) {
61675
+ const first = asRecord(node.commits).nodes?.[0];
61676
+ return asRecord(asRecord(first).commit).statusCheckRollup;
61677
+ }
61678
+ function parsePrListResponse(json) {
61679
+ const nodes = asRecord(asRecord(asRecord(json).data).search).nodes ?? [];
61680
+ const items = [];
61681
+ for (const raw of nodes) {
61682
+ const n = asRecord(raw);
61683
+ if (typeof n.number !== "number") continue;
61684
+ const nameWithOwner = String(asRecord(n.repository).nameWithOwner ?? "");
61685
+ const slash = nameWithOwner.indexOf("/");
61686
+ if (slash <= 0) continue;
61687
+ const rollup = lastCommitRollup(n);
61688
+ items.push({
61689
+ owner: nameWithOwner.slice(0, slash),
61690
+ repo: nameWithOwner.slice(slash + 1),
61691
+ number: n.number,
61692
+ title: String(n.title ?? ""),
61693
+ state: mapPrState("OPEN", n.isDraft),
61694
+ headRef: String(n.headRefName ?? ""),
61695
+ additions: typeof n.additions === "number" ? n.additions : 0,
61696
+ deletions: typeof n.deletions === "number" ? n.deletions : 0,
61697
+ updatedAt: String(n.updatedAt ?? ""),
61698
+ author: String(asRecord(n.author).login ?? ""),
61699
+ checksRollup: rollup == null ? "none" : mapRollupState(asRecord(rollup).state)
61700
+ });
61701
+ }
61702
+ return items;
61703
+ }
61704
+ function parsePrDetailResponse(json, repoRef) {
61705
+ const pr = asRecord(asRecord(asRecord(json).data).repository).pullRequest;
61706
+ if (pr == null || typeof pr !== "object") return null;
61707
+ const n = asRecord(pr);
61708
+ const rollup = lastCommitRollup(n);
61709
+ const contextNodes = asRecord(asRecord(rollup).contexts).nodes ?? [];
61710
+ const checks = [];
61711
+ for (const c of contextNodes) {
61712
+ const mapped = mapCheckNode(asRecord(c));
61713
+ if (mapped) checks.push(mapped);
61714
+ }
61715
+ const reviewerNodes = asRecord(n.reviewRequests).nodes ?? [];
61716
+ const reviewers = reviewerNodes.map((r) => String(asRecord(asRecord(r).requestedReviewer).login ?? "")).filter((s) => s !== "");
61717
+ const mergeable = n.mergeable === "MERGEABLE" ? "mergeable" : n.mergeable === "CONFLICTING" ? "conflicting" : "unknown";
61718
+ const commentCountRaw = asRecord(n.comments).totalCount;
61719
+ return {
61720
+ owner: repoRef.owner,
61721
+ repo: repoRef.repo,
61722
+ number: typeof n.number === "number" ? n.number : 0,
61723
+ title: String(n.title ?? ""),
61724
+ body: String(n.body ?? ""),
61725
+ state: mapPrState(n.state, n.isDraft),
61726
+ author: String(asRecord(n.author).login ?? ""),
61727
+ updatedAt: String(n.updatedAt ?? ""),
61728
+ headRef: String(n.headRefName ?? ""),
61729
+ baseRef: String(n.baseRefName ?? ""),
61730
+ headSha: String(n.headRefOid ?? ""),
61731
+ additions: typeof n.additions === "number" ? n.additions : 0,
61732
+ deletions: typeof n.deletions === "number" ? n.deletions : 0,
61733
+ mergeable,
61734
+ reviewers,
61735
+ commentCount: typeof commentCountRaw === "number" ? commentCountRaw : 0,
61736
+ checksRollup: rollup == null ? "none" : mapRollupState(asRecord(rollup).state),
61737
+ checks
61738
+ };
61739
+ }
61740
+
61741
+ // src/handlers/github.ts
61742
+ var HOSTNAME = "github.com";
61743
+ function buildGithubHandlers(deps) {
61744
+ const { gh } = deps;
61745
+ return {
61746
+ "github:status": async () => {
61747
+ try {
61748
+ await gh.run(["--version"]);
61749
+ } catch (err) {
61750
+ if (err instanceof GhError && err.kind === "gh-missing") {
61751
+ return { response: { installed: false, authenticated: false } };
61752
+ }
61753
+ throw err;
61754
+ }
61755
+ try {
61756
+ await gh.run(["auth", "status", "--active", "--hostname", HOSTNAME]);
61757
+ } catch {
61758
+ return { response: { installed: true, authenticated: false } };
61759
+ }
61760
+ const login = (await gh.run(["api", "user", "--jq", ".login"])).trim();
61761
+ const response = { installed: true, authenticated: true, login };
61762
+ return { response };
61763
+ },
61764
+ "github:prList": async (frame) => {
61765
+ const { role } = GithubPrListArgs.parse(frame);
61766
+ const stdout = await gh.run([
61767
+ "api",
61768
+ "graphql",
61769
+ "-f",
61770
+ `query=${PR_LIST_QUERY}`,
61771
+ "-f",
61772
+ `searchQuery=${buildPrSearchQuery(role)}`,
61773
+ "-F",
61774
+ // 上限 50:超出即静默截断(收件箱语义按 updated 倒序取最近的,够用;真溢出再谈分页)
61775
+ "first=50"
61776
+ ]);
61777
+ const prs = parsePrListResponse(JSON.parse(stdout));
61778
+ return { response: { prs } };
61779
+ },
61780
+ "github:prDetail": async (frame) => {
61781
+ const { owner: owner2, repo: repo2, number: number2 } = GithubPrDetailArgs.parse(frame);
61782
+ const stdout = await gh.run([
61783
+ "api",
61784
+ "graphql",
61785
+ "-f",
61786
+ `query=${PR_DETAIL_QUERY}`,
61787
+ "-f",
61788
+ `owner=${owner2}`,
61789
+ "-f",
61790
+ `repo=${repo2}`,
61791
+ "-F",
61792
+ `number=${number2}`
61793
+ ]);
61794
+ const pr = parsePrDetailResponse(JSON.parse(stdout), { owner: owner2, repo: repo2 });
61795
+ return { response: { pr } };
61796
+ },
61797
+ "github:prMerge": async (frame) => {
61798
+ const { owner: owner2, repo: repo2, number: number2, expectedHeadSha } = GithubPrMergeArgs.parse(frame);
61799
+ try {
61800
+ await gh.run([
61801
+ "pr",
61802
+ "merge",
61803
+ String(number2),
61804
+ "--repo",
61805
+ `${owner2}/${repo2}`,
61806
+ "--squash",
61807
+ "--match-head-commit",
61808
+ expectedHeadSha
61809
+ ]);
61810
+ } catch (err) {
61811
+ if (err instanceof GhError && /head/i.test(err.message) && /modif|match/i.test(err.message)) {
61812
+ return { response: { merged: false, reason: "head-changed", message: err.message } };
61813
+ }
61814
+ throw err;
61815
+ }
61816
+ return { response: { merged: true } };
61817
+ },
61818
+ "github:prComment": async (frame) => {
61819
+ const { owner: owner2, repo: repo2, number: number2, body } = GithubPrCommentArgs.parse(frame);
61820
+ await gh.run(["api", `repos/${owner2}/${repo2}/issues/${number2}/comments`, "-f", `body=${body}`]);
61821
+ return { response: { posted: true } };
61822
+ },
61823
+ "github:prSetReady": async (frame) => {
61824
+ const { owner: owner2, repo: repo2, number: number2, ready } = GithubPrSetReadyArgs.parse(frame);
61825
+ const args = ["pr", "ready", String(number2), "--repo", `${owner2}/${repo2}`];
61826
+ if (!ready) args.push("--undo");
61827
+ await gh.run(args);
61828
+ return { response: { updated: true } };
61829
+ },
61830
+ "github:collaborators": async (frame) => {
61831
+ const { owner: owner2, repo: repo2 } = GithubCollaboratorsArgs.parse(frame);
61832
+ const stdout = await gh.run(["api", `repos/${owner2}/${repo2}/collaborators?per_page=100`]);
61833
+ const arr = JSON.parse(stdout);
61834
+ const logins = arr.map((c) => String(c.login ?? "")).filter((s) => s !== "");
61835
+ return { response: { logins } };
61836
+ },
61837
+ "github:prRequestReviewers": async (frame) => {
61838
+ const { owner: owner2, repo: repo2, number: number2, reviewers } = GithubPrRequestReviewersArgs.parse(frame);
61839
+ const args = ["api", "-X", "POST", `repos/${owner2}/${repo2}/pulls/${number2}/requested_reviewers`];
61840
+ for (const r of reviewers) args.push("-f", `reviewers[]=${r}`);
61841
+ await gh.run(args);
61842
+ return { response: { requested: true } };
61843
+ }
61844
+ };
61845
+ }
61846
+
61497
61847
  // src/handlers/visitor.ts
61498
61848
  init_protocol();
61499
61849
  function ensureOwner4(ctx) {
@@ -61611,15 +61961,15 @@ var import_node_crypto18 = require("crypto");
61611
61961
  init_protocol();
61612
61962
 
61613
61963
  // src/peer-ops/run-command.ts
61614
- var import_node_child_process14 = require("child_process");
61964
+ var import_node_child_process15 = require("child_process");
61615
61965
  var import_node_os21 = __toESM(require("os"), 1);
61616
- var DEFAULT_TIMEOUT_MS3 = 3e4;
61966
+ var DEFAULT_TIMEOUT_MS4 = 3e4;
61617
61967
  var MAX_TIMEOUT_MS = 3e5;
61618
61968
  var MAX_OUTPUT_BYTES = 1048576;
61619
61969
  function runCommand(command, opts = {}) {
61620
- const timeoutMs = Math.min(opts.timeoutMs ?? DEFAULT_TIMEOUT_MS3, MAX_TIMEOUT_MS);
61970
+ const timeoutMs = Math.min(opts.timeoutMs ?? DEFAULT_TIMEOUT_MS4, MAX_TIMEOUT_MS);
61621
61971
  return new Promise((resolve6) => {
61622
- const child = (0, import_node_child_process14.spawn)("bash", ["-c", command], { cwd: import_node_os21.default.homedir() });
61972
+ const child = (0, import_node_child_process15.spawn)("bash", ["-c", command], { cwd: import_node_os21.default.homedir() });
61623
61973
  const out = [];
61624
61974
  const err = [];
61625
61975
  let outBytes = 0;
@@ -61824,6 +62174,7 @@ function buildMethodHandlers(deps) {
61824
62174
  ownerIdProvider: deps.shiftOwnerIdProvider,
61825
62175
  now: () => Date.now()
61826
62176
  }),
62177
+ ...buildGithubHandlers({ gh: deps.ghRunner }),
61827
62178
  ...buildVisitorHandlers({ visitorStore: deps.visitorStore })
61828
62179
  };
61829
62180
  handlers["meta:methods"] = buildMetaMethodsHandler({
@@ -61834,7 +62185,7 @@ function buildMethodHandlers(deps) {
61834
62185
  }
61835
62186
 
61836
62187
  // src/app-builder/dev-server-supervisor.ts
61837
- var import_node_child_process15 = require("child_process");
62188
+ var import_node_child_process16 = require("child_process");
61838
62189
  var import_node_events2 = require("events");
61839
62190
  var DEFAULT_READY_PATTERN = /Local:\s+https?:\/\/|Nest application successfully started|server listening on/i;
61840
62191
  var DevServerSupervisor = class extends import_node_events2.EventEmitter {
@@ -61871,7 +62222,7 @@ var DevServerSupervisor = class extends import_node_events2.EventEmitter {
61871
62222
  tunnelHost: args.tunnelHost,
61872
62223
  devCommand: cmd
61873
62224
  });
61874
- const child = (0, import_node_child_process15.spawn)("sh", ["-c", cmd], {
62225
+ const child = (0, import_node_child_process16.spawn)("sh", ["-c", cmd], {
61875
62226
  cwd: args.cwd,
61876
62227
  env,
61877
62228
  stdio: "pipe",
@@ -62123,7 +62474,7 @@ async function dispatchRpc(method, frame, client, ctx, deps) {
62123
62474
  }
62124
62475
 
62125
62476
  // src/extension/runtime.ts
62126
- var import_node_child_process16 = require("child_process");
62477
+ var import_node_child_process17 = require("child_process");
62127
62478
  var import_node_path62 = __toESM(require("path"), 1);
62128
62479
  var import_promises12 = require("timers/promises");
62129
62480
 
@@ -62231,7 +62582,7 @@ var Runtime = class {
62231
62582
  CLAWOS_EXT_PORT: String(port),
62232
62583
  CLAWOS_EXT_ID: extId
62233
62584
  };
62234
- const child = (0, import_node_child_process16.spawn)("sh", ["-c", cmd], {
62585
+ const child = (0, import_node_child_process17.spawn)("sh", ["-c", cmd], {
62235
62586
  cwd: dir,
62236
62587
  env,
62237
62588
  stdio: ["ignore", "pipe", "pipe"],
@@ -63284,6 +63635,8 @@ async function startDaemon(config) {
63284
63635
  // daemon 稳定身份),跟我们 ShiftStore boot 时用的 provider 一致。
63285
63636
  shiftStore,
63286
63637
  shiftOwnerIdProvider: () => ownerPrincipalId,
63638
+ // PR 收件箱:github:* handler spawn 本机 gh CLI(凭据复用 gh 登录态)
63639
+ ghRunner: createGhRunner(),
63287
63640
  // persona web 分享:visitor:list owner RPC 读访客名单(同 exchange upsert 的实例)
63288
63641
  visitorStore
63289
63642
  })