@clawos-dev/clawd 0.2.517 → 0.2.518

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
@@ -5950,6 +5950,27 @@ var init_contact_handshake = __esm({
5950
5950
  }
5951
5951
  });
5952
5952
 
5953
+ // ../protocol/src/github-auth-schemas.ts
5954
+ var GithubAuthStatusSchema, GithubAuthAuthorizeArgsSchema, GithubAuthOauthStartArgsSchema;
5955
+ var init_github_auth_schemas = __esm({
5956
+ "../protocol/src/github-auth-schemas.ts"() {
5957
+ "use strict";
5958
+ init_zod();
5959
+ GithubAuthStatusSchema = external_exports.discriminatedUnion("status", [
5960
+ external_exports.object({ status: external_exports.literal("authorized"), login: external_exports.string().min(1), source: external_exports.enum(["git", "file"]) }).strict(),
5961
+ external_exports.object({ status: external_exports.literal("missing") }).strict(),
5962
+ external_exports.object({ status: external_exports.literal("invalid"), message: external_exports.string().min(1) }).strict(),
5963
+ external_exports.object({ status: external_exports.literal("pending"), authUrl: external_exports.string().url() }).strict()
5964
+ ]);
5965
+ GithubAuthAuthorizeArgsSchema = external_exports.object({
5966
+ token: external_exports.string().min(1)
5967
+ }).strict();
5968
+ GithubAuthOauthStartArgsSchema = external_exports.object({
5969
+ returnTo: external_exports.string().url().optional()
5970
+ }).strict();
5971
+ }
5972
+ });
5973
+
5953
5974
  // ../protocol/src/environment-schemas.ts
5954
5975
  function connectorBlockers(c2) {
5955
5976
  if (!c2.credentialSource) return [`\u8FDE\u63A5\u5668 ${c2.name} \u8FD8\u6CA1\u9009\u7528\u8C01\u7684\u8D26\u53F7`];
@@ -6077,11 +6098,12 @@ function cloudEngineOk(tool) {
6077
6098
  function repoSlug(url) {
6078
6099
  return url.trim().replace(/^git@[^:]+:/, "").replace(/^https?:\/\/[^/]+\//, "").replace(/\.git$/, "");
6079
6100
  }
6080
- var DeployFileSchema, SCAN_BASE_KINDS, ScanBaseKindSchema, DeployScanStatusSchema, DeployScanSchema, DeployRepoSchema, DeployGithubStatusSchema, DeploySecretPresenceSchema, DEPLOY_STAGE_STATUSES, DeployStageStatusSchema, DEPLOY_STAGE_KEYS, DeployStageKeySchema, DeployJobSchema, DeployRunSchema, DeployStateSchema, DeployReleaseResultSchema, DeployPersonaArgsSchema, DeployStartArgsSchema, DeployPatchArgsSchema, DeployPutSecretArgsSchema, DeployGithubAuthorizeArgsSchema, DeployGithubOauthStartArgsSchema, DeployRerunJobArgsSchema, DeployReportFindingArgsSchema, DeployFinishScanArgsSchema, PERSONA_REPO_ORG;
6101
+ var DeployFileSchema, SCAN_BASE_KINDS, ScanBaseKindSchema, DeployScanStatusSchema, DeployScanSchema, DeployRepoSchema, DeploySecretPresenceSchema, DEPLOY_STAGE_STATUSES, DeployStageStatusSchema, DEPLOY_STAGE_KEYS, DeployStageKeySchema, DeployJobSchema, DeployRunSchema, DeployStateSchema, DeployReleaseResultSchema, DeployPersonaArgsSchema, DeployStartArgsSchema, DeployPatchArgsSchema, DeployPutSecretArgsSchema, DeployRerunJobArgsSchema, DeployReportFindingArgsSchema, DeployFinishScanArgsSchema, PERSONA_REPO_ORG;
6081
6102
  var init_deploy_schemas = __esm({
6082
6103
  "../protocol/src/deploy-schemas.ts"() {
6083
6104
  "use strict";
6084
6105
  init_zod();
6106
+ init_github_auth_schemas();
6085
6107
  init_environment_schemas();
6086
6108
  DeployFileSchema = external_exports.object({
6087
6109
  version: external_exports.literal(1),
@@ -6148,12 +6170,6 @@ var init_deploy_schemas = __esm({
6148
6170
  /** 本地领先远端几个提交 */
6149
6171
  ahead: external_exports.number()
6150
6172
  }).strict();
6151
- DeployGithubStatusSchema = external_exports.discriminatedUnion("status", [
6152
- external_exports.object({ status: external_exports.literal("authorized"), login: external_exports.string().min(1), source: external_exports.enum(["git", "file"]) }).strict(),
6153
- external_exports.object({ status: external_exports.literal("missing") }).strict(),
6154
- external_exports.object({ status: external_exports.literal("invalid"), message: external_exports.string().min(1) }).strict(),
6155
- external_exports.object({ status: external_exports.literal("pending"), authUrl: external_exports.string().url() }).strict()
6156
- ]);
6157
6173
  DeploySecretPresenceSchema = external_exports.object({
6158
6174
  env: external_exports.string().min(1),
6159
6175
  setAt: external_exports.number().optional()
@@ -6183,8 +6199,8 @@ var init_deploy_schemas = __esm({
6183
6199
  }).strict();
6184
6200
  DeployStateSchema = external_exports.object({
6185
6201
  personaId: external_exports.string().min(1),
6186
- /** 本机 GitHub 身份。daemon 每次现算;老 daemon 不给(可选只为兼容),UI 缺席时按未知处理 */
6187
- github: DeployGithubStatusSchema.optional(),
6202
+ /** 本机 GitHub 身份(机器级,githubAuth:* 的同一份;上云页顶上那张卡用)。老 daemon 不给(可选只为兼容),UI 缺席时按未知处理 */
6203
+ github: GithubAuthStatusSchema.optional(),
6188
6204
  repo: DeployRepoSchema.optional(),
6189
6205
  file: DeployFileSchema.optional(),
6190
6206
  scan: DeployScanSchema.optional(),
@@ -6229,14 +6245,6 @@ var init_deploy_schemas = __esm({
6229
6245
  // 环境变量名 → 值。key 必须是该 secret 声明过的 env 之一,handler 校验。
6230
6246
  values: external_exports.record(external_exports.string(), external_exports.string())
6231
6247
  }).strict();
6232
- DeployGithubAuthorizeArgsSchema = external_exports.object({
6233
- personaId: external_exports.string().min(1),
6234
- token: external_exports.string().min(1)
6235
- }).strict();
6236
- DeployGithubOauthStartArgsSchema = external_exports.object({
6237
- personaId: external_exports.string().min(1),
6238
- returnTo: external_exports.string().url().optional()
6239
- }).strict();
6240
6248
  DeployRerunJobArgsSchema = external_exports.object({
6241
6249
  personaId: external_exports.string().min(1),
6242
6250
  /** GitHub 的 job databaseId。**是数字**——字符串会在拼命令时静默出错 */
@@ -8672,6 +8680,7 @@ var init_methods = __esm({
8672
8680
  init_attachment_schemas();
8673
8681
  init_contact_handshake();
8674
8682
  init_deploy_schemas();
8683
+ init_github_auth_schemas();
8675
8684
  init_contact_remote_access();
8676
8685
  init_cloud_connector_schemas();
8677
8686
  init_contact();
@@ -8909,13 +8918,18 @@ var init_methods = __esm({
8909
8918
  summary: "\u539F\u6837\u91CD\u8DD1\u6700\u65B0\u90A3\u6761\u6D41\u6C34\u7EBF\u91CC\u5931\u8D25\u7684\u67D0\u4E00\u6BB5\uFF08GitHub REST rerun job\uFF09\u3002**\u540C\u4E00\u4E2A commit\u3001\u540C\u4E00\u4EFD\u8F93\u5165**\uFF0C\u4E0D\u4EA7\u751F\u65B0\u63D0\u4EA4\u2014\u2014\u6539\u4E86 persona \u5185\u5BB9\u6216\u51ED\u636E\u8981\u8D70 deploy:release\u3002jobId \u5FC5\u987B\u5C5E\u4E8E\u6700\u65B0\u90A3\u6761 run\uFF1Bowner-only",
8910
8919
  args: DeployRerunJobArgsSchema
8911
8920
  },
8912
- "deploy:githubAuthorize": {
8913
- summary: "\u628A\u7528\u6237\u7C98\u7684 GitHub token \u5B58\u6210\u672C\u673A git \u7684\u8EAB\u4EFD\uFF1A\u5148 GET /user \u9A8C\u8BC1\uFF0C\u518D git credential approve \u4EA4\u7ED9 git \u7684 helper \u5B58\uFF08git \u6CA1\u914D helper \u65F6\u9000\u5230\u672C\u673A\u51ED\u636E\u8868\uFF09\u3002\u4E0A\u4E91\u7684\u5EFA\u4ED3 / \u63A8\u9001 / \u5199 Secrets / \u67E5\u6D41\u6C34\u7EBF\u90FD\u7528\u5B83\u3002\u5165\u53C2\u4E0D\u8FDB\u65E5\u5FD7\u3001\u54CD\u5E94\u4E0D\u56DE\u663E token\uFF1Bowner-only",
8914
- args: DeployGithubAuthorizeArgsSchema
8921
+ // ---- githubAuth:*(本机 GitHub 身份,机器级;spec 2026-09-15-github-auth-unify-design §4.3)----
8922
+ "githubAuth:get": {
8923
+ summary: "\u672C\u673A\u7684 GitHub \u8EAB\u4EFD\uFF1Aauthorized\uFF08@login\uFF0C\u6765\u81EA git \u51ED\u636E\u5B58\u50A8 / \u672C\u673A\u51ED\u636E\u8868\uFF09/ missing / invalid\uFF08GitHub \u4E0D\u8BA4\uFF09/ pending\uFF08\u7B49 GitHub \u8DF3\u56DE\u4E2D\u5FC3\uFF09\u3002\u8BBE\u7F6E\u9875\u300C\u8D26\u53F7\u300D\u3001\u4E0A\u4E91\u9875\u9876\u4E0A\u90A3\u5F20\u5361\u3001PR \u6536\u4EF6\u7BB1\u90FD\u770B\u5B83\uFF1Bpending \u671F\u9875\u9762\u6BCF 2 \u79D2\u8F6E\u8BE2\uFF0Cdaemon \u987A\u624B\u5411\u4E2D\u5FC3\u9886 token\u3002\u65E0\u53C2\u6570",
8924
+ args: NO_ARGS
8925
+ },
8926
+ "githubAuth:oauthStart": {
8927
+ summary: "\u8BBE\u7F6E\u9875\u300C\u5173\u8054 GitHub\u300D\uFF1Adaemon \u5E26\u672C\u673A\u767B\u5F55\u51ED\u8BC1\u5411\u4E2D\u5FC3\u8981\u4E00\u6761 GitHub OAuth \u6388\u6743\u94FE\u63A5\u5E76\u56DE\u5728\u72B6\u6001\uFF08pending\uFF09\u91CC\uFF0C\u9875\u9762\u6253\u5F00\u5B83\uFF1B\u7528\u6237\u5728 GitHub \u70B9\u540C\u610F\u540E\u56DE\u8DF3\u4E2D\u5FC3\uFF0C\u4E2D\u5FC3\u6362\u597D token \u5B58\u4E00\u4EFD\uFF0Cdaemon \u5728\u4E4B\u540E\u7684 githubAuth:get \u91CC\u9886\u56DE\u6765\u5B58\u8FDB\u672C\u673A git\u3002\u4E2D\u5FC3\u5DF2\u6709\u672C\u4EBA\u7684 token \u65F6\u4E0D\u8DF3 GitHub\uFF0C\u76F4\u63A5\u9886\uFF1B\u8981\u767B\u5F55\u98DE\u4E66\uFF1Bowner-only",
8928
+ args: GithubAuthOauthStartArgsSchema
8915
8929
  },
8916
- "deploy:githubOauthStart": {
8917
- summary: "\u4E0A\u4E91\u9875\u300C\u7528 GitHub \u6388\u6743\u300D\uFF1Adaemon \u5E26\u672C\u673A\u767B\u5F55\u51ED\u8BC1\u5411\u4E2D\u5FC3\u8981\u4E00\u6761 GitHub OAuth \u6388\u6743\u94FE\u63A5\u5E76\u56DE\u5728 DeployState.github\uFF08pending\uFF09\u91CC\uFF0C\u9875\u9762\u6253\u5F00\u5B83\uFF1B\u7528\u6237\u5728 GitHub \u70B9\u540C\u610F\u540E\u56DE\u8DF3\u4E2D\u5FC3\uFF0C\u4E2D\u5FC3\u6362\u597D token \u5B58\u4E00\u4EFD\uFF0Cdaemon \u5728\u4E4B\u540E\u7684 deploy:get \u91CC\u9886\u56DE\u6765\u5B58\u8FDB\u672C\u673A git\u3002\u4E2D\u5FC3\u5DF2\u6709\u672C\u4EBA\u7684 token \u65F6\u4E0D\u8DF3 GitHub\uFF0C\u76F4\u63A5\u9886\uFF1B\u8981\u767B\u5F55\u98DE\u4E66\uFF1Bowner-only",
8918
- args: DeployGithubOauthStartArgsSchema
8930
+ "githubAuth:authorize": {
8931
+ summary: "\u628A\u7528\u6237\u7C98\u7684 GitHub token \u5B58\u6210\u672C\u673A\u8EAB\u4EFD\uFF08\u70B9\u94FE\u63A5\u8D70\u4E0D\u901A\u65F6\u7684\u9000\u8DEF\uFF09\uFF1A\u5148 GET /user \u9A8C\u8BC1\uFF0C\u518D git credential approve \u4EA4\u7ED9 git \u7684 helper \u5B58\uFF08git \u6CA1\u914D helper \u65F6\u9000\u5230\u672C\u673A\u51ED\u636E\u8868 secrets/github.env\uFF09\u3002\u5165\u53C2\u4E0D\u8FDB\u65E5\u5FD7\u3001\u54CD\u5E94\u4E0D\u56DE\u663E token\uFF1Bowner-only",
8932
+ args: GithubAuthAuthorizeArgsSchema
8919
8933
  },
8920
8934
  // ---- cloud:connectors:*(云 persona 卡片上的连接器;design 2026-09-11-mcp-connector §3.8)----
8921
8935
  "cloud:connectors:list": {
@@ -9592,6 +9606,7 @@ var init_runtime = __esm({
9592
9606
  init_environment_schemas();
9593
9607
  init_cloud_connector_schemas();
9594
9608
  init_deploy_schemas();
9609
+ init_github_auth_schemas();
9595
9610
  init_persona_schemas();
9596
9611
  init_persona_mode();
9597
9612
  init_attachment_schemas();
@@ -54810,19 +54825,25 @@ var DEFAULT_TIMEOUT_MS2 = 15e3;
54810
54825
  var MAX_BUFFER = 4 * 1024 * 1024;
54811
54826
  function createGhRunner(defaults) {
54812
54827
  const baseTimeoutMs = defaults?.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
54828
+ const spawn14 = defaults?.execFile ?? import_node_child_process7.execFile;
54813
54829
  return {
54814
54830
  run(args, opts) {
54831
+ const token = defaults?.token?.();
54815
54832
  return new Promise((resolve9, reject) => {
54816
- (0, import_node_child_process7.execFile)(
54833
+ spawn14(
54817
54834
  "gh",
54818
54835
  args,
54819
- { timeout: opts?.timeoutMs ?? baseTimeoutMs, maxBuffer: MAX_BUFFER },
54836
+ {
54837
+ timeout: opts?.timeoutMs ?? baseTimeoutMs,
54838
+ maxBuffer: MAX_BUFFER,
54839
+ env: { ...process.env, ...token ? { GH_TOKEN: token } : {} }
54840
+ },
54820
54841
  (err, stdout, stderr) => {
54821
- if (!err) return resolve9(stdout);
54842
+ if (!err) return resolve9(String(stdout));
54822
54843
  if (err.code === "ENOENT") {
54823
54844
  return reject(new GhError("gh-missing", "GitHub CLI (gh) \u672A\u5B89\u88C5"));
54824
54845
  }
54825
- const detail = (stderr ?? "").trim();
54846
+ const detail = String(stderr ?? "").trim();
54826
54847
  reject(new GhError("gh-error", detail !== "" ? detail : err.message, detail));
54827
54848
  }
54828
54849
  );
@@ -58949,8 +58970,8 @@ function createGithubApi(args) {
58949
58970
  }
58950
58971
 
58951
58972
  // src/deploy/github-auth.ts
58952
- var DEPLOY_TOKEN_FILE = "github-deploy.env";
58953
- var DEPLOY_TOKEN_VAR = "GITHUB_DEPLOY_TOKEN";
58973
+ var GITHUB_TOKEN_FILE = "github.env";
58974
+ var GITHUB_TOKEN_VAR = "GITHUB_TOKEN";
58954
58975
  var HOST = "github.com";
58955
58976
  var TIMEOUT_MS2 = 15e3;
58956
58977
  var record3 = (fields) => `${Object.entries(fields).map(([k3, v3]) => `${k3}=${v3}`).join("\n")}
@@ -59580,8 +59601,7 @@ var invalid = (msg) => new ClawdError(ERROR_CODES.VALIDATION_ERROR, `VALIDATION_
59580
59601
  function wrongEngine(tool) {
59581
59602
  return cloudEngineOk(tool) ? void 0 : `\u5F15\u64CE\u662F ${tool || "claude"}\uFF0C\u4E91\u4E0A\u53EA\u8DD1 Codex\u2014\u2014\u53BB persona \u8BBE\u7F6E\u91CC\u628A\u5F15\u64CE\u5207\u6210 Codex\uFF0C\u518D\u9009\u4E2A\u6A21\u578B`;
59582
59603
  }
59583
- var NO_GITHUB_AUTH = "\u8FD8\u6CA1\u6388\u6743 GitHub\uFF1A\u5728\u4E0A\u4E91\u9875\u9762\u9876\u4E0A\u90A3\u5F20\u300CGitHub \u6388\u6743\u300D\u5361\u91CC\u70B9\u300C\u7528 GitHub \u6388\u6743\u300D";
59584
- var OAUTH_PENDING_MS = 10 * 6e4;
59604
+ var NO_GITHUB_AUTH = "\u8FD8\u6CA1\u5173\u8054 GitHub\uFF1A\u5728\u8BBE\u7F6E \u2192 \u8D26\u53F7\uFF08\u6216\u4E0A\u4E91\u9875\u9876\u4E0A\u90A3\u5F20\u5361\uFF09\u91CC\u70B9\u300C\u5173\u8054 GitHub\u300D";
59585
59605
  function toClawdError(err) {
59586
59606
  if (err instanceof ClawdError) return err;
59587
59607
  if (err instanceof RepoError || err instanceof GithubApiError) return new ClawdError(ERROR_CODES.INTERNAL, err.message);
@@ -59597,62 +59617,11 @@ function buildDeployHandlers(deps) {
59597
59617
  const cacheMs = deps.secretsCacheMs ?? SECRETS_CACHE_MS;
59598
59618
  const secretsCache = /* @__PURE__ */ new Map();
59599
59619
  const runCache = /* @__PURE__ */ new Map();
59600
- const githubStatusCache = /* @__PURE__ */ new Map();
59601
- let pendingOauth = null;
59602
- let staleCenterToken;
59603
- async function adoptToken(token) {
59604
- await deps.github.store(token);
59605
- githubStatusCache.clear();
59606
- secretsCache.clear();
59607
- pendingOauth = null;
59608
- }
59609
- async function claimCenterToken() {
59610
- let got;
59611
- try {
59612
- got = await deps.github.oauth.token();
59613
- } catch {
59614
- return false;
59615
- }
59616
- if (!got || got.token === staleCenterToken) return false;
59617
- try {
59618
- await deps.github.api(got.token).whoami();
59619
- } catch (err) {
59620
- if (err instanceof GithubApiError && err.status === 401) staleCenterToken = got.token;
59621
- return false;
59622
- }
59623
- await adoptToken(got.token);
59624
- return true;
59625
- }
59626
- async function settlePendingOauth() {
59627
- if (!pendingOauth) return;
59628
- if (deps.now() > pendingOauth.expiresAt) {
59629
- pendingOauth = null;
59630
- return;
59631
- }
59632
- try {
59633
- await claimCenterToken();
59634
- } catch {
59635
- pendingOauth = null;
59636
- }
59637
- }
59620
+ deps.githubIdentity.onChange(() => secretsCache.clear());
59638
59621
  async function requireGithub() {
59639
- const auth = await deps.github.resolve();
59622
+ const auth = await deps.githubIdentity.refresh();
59640
59623
  if (!auth) throw new ClawdError(ERROR_CODES.INTERNAL, NO_GITHUB_AUTH);
59641
- return { auth, api: deps.github.api(auth.token) };
59642
- }
59643
- async function githubStatus(auth) {
59644
- if (!auth) return { status: "missing" };
59645
- const hit = githubStatusCache.get(auth.token);
59646
- if (hit && deps.now() - hit.at < cacheMs) return hit.status;
59647
- let status;
59648
- try {
59649
- const { login } = await deps.github.api(auth.token).whoami();
59650
- status = { status: "authorized", login, source: auth.source };
59651
- } catch (err) {
59652
- status = { status: "invalid", message: err instanceof Error ? err.message : String(err) };
59653
- }
59654
- githubStatusCache.set(auth.token, { at: deps.now(), status });
59655
- return status;
59624
+ return { auth, api: deps.githubApi(auth.token) };
59656
59625
  }
59657
59626
  function mustPersona(personaId2) {
59658
59627
  if (!deps.hasPersona(personaId2)) {
@@ -59670,11 +59639,9 @@ function buildDeployHandlers(deps) {
59670
59639
  async function stateOf(personaId2) {
59671
59640
  const dir = deps.personaDir(personaId2);
59672
59641
  const repo2 = await repoInfo(deps.exec, dir);
59673
- await settlePendingOauth();
59674
- const auth = await deps.github.resolve();
59675
- const current = await githubStatus(auth);
59676
- const github = pendingOauth && current.status !== "authorized" ? { status: "pending", authUrl: pendingOauth.authUrl } : current;
59677
- const api = auth ? deps.github.api(auth.token) : void 0;
59642
+ const github = await deps.githubIdentity.status();
59643
+ const auth = deps.githubIdentity.cached();
59644
+ const api = auth ? deps.githubApi(auth.token) : void 0;
59678
59645
  let file;
59679
59646
  let fileError;
59680
59647
  try {
@@ -59944,33 +59911,6 @@ function buildDeployHandlers(deps) {
59944
59911
  }
59945
59912
  return view(personaId2);
59946
59913
  };
59947
- const githubAuthorize = async (frame, _client, ctx) => {
59948
- ensureOwner(ctx);
59949
- const args = parseArgs2(DeployGithubAuthorizeArgsSchema, frame, "deploy:githubAuthorize");
59950
- mustPersona(args.personaId);
59951
- const token = args.token.trim();
59952
- try {
59953
- await deps.github.api(token).whoami();
59954
- } catch (err) {
59955
- throw invalid(`GitHub \u4E0D\u8BA4\u8FD9\u4E2A token\uFF1A${err instanceof Error ? err.message : String(err)}`);
59956
- }
59957
- await adoptToken(token);
59958
- return view(args.personaId);
59959
- };
59960
- const githubOauthStart = async (frame, _client, ctx) => {
59961
- ensureOwner(ctx);
59962
- const args = parseArgs2(DeployGithubOauthStartArgsSchema, frame, "deploy:githubOauthStart");
59963
- mustPersona(args.personaId);
59964
- const local = await deps.github.resolve();
59965
- if (!local && await claimCenterToken()) return view(args.personaId);
59966
- try {
59967
- staleCenterToken = (await deps.github.oauth.token())?.token;
59968
- } catch {
59969
- }
59970
- const { authUrl } = await deps.github.oauth.start(args.returnTo);
59971
- pendingOauth = { authUrl, expiresAt: deps.now() + OAUTH_PENDING_MS };
59972
- return view(args.personaId);
59973
- };
59974
59914
  return {
59975
59915
  "deploy:start": start,
59976
59916
  "deploy:get": get,
@@ -59979,9 +59919,156 @@ function buildDeployHandlers(deps) {
59979
59919
  "deploy:reportFinding": reportFinding,
59980
59920
  "deploy:finishScan": finishScan,
59981
59921
  "deploy:release": releaseHandler,
59982
- "deploy:rerunJob": rerunJobHandler,
59983
- "deploy:githubAuthorize": githubAuthorize,
59984
- "deploy:githubOauthStart": githubOauthStart
59922
+ "deploy:rerunJob": rerunJobHandler
59923
+ };
59924
+ }
59925
+
59926
+ // src/github/identity.ts
59927
+ var GithubTokenRejected = class extends Error {
59928
+ };
59929
+ var STATUS_CACHE_MS = 2e4;
59930
+ var OAUTH_PENDING_MS = 10 * 6e4;
59931
+ function secretsWithGithubIdentity(secrets, auth) {
59932
+ return auth ? { ...secrets, GITHUB_TOKEN: auth.token } : secrets;
59933
+ }
59934
+ function createGithubIdentity(deps) {
59935
+ const cacheMs = deps.statusCacheMs ?? STATUS_CACHE_MS;
59936
+ let auth;
59937
+ let seq = 0;
59938
+ const statusCache = /* @__PURE__ */ new Map();
59939
+ let pendingOauth = null;
59940
+ let staleCenterToken;
59941
+ const listeners = /* @__PURE__ */ new Set();
59942
+ async function refresh() {
59943
+ const mine = ++seq;
59944
+ const resolved = await deps.resolve();
59945
+ if (mine === seq) auth = resolved;
59946
+ return resolved;
59947
+ }
59948
+ async function whoami(a2) {
59949
+ const hit = statusCache.get(a2.token);
59950
+ if (hit && deps.now() - hit.at < cacheMs) return hit.status;
59951
+ let status2;
59952
+ try {
59953
+ const { login } = await deps.api(a2.token).whoami();
59954
+ status2 = { status: "authorized", login, source: a2.source };
59955
+ } catch (err) {
59956
+ status2 = { status: "invalid", message: err instanceof Error ? err.message : String(err) };
59957
+ }
59958
+ statusCache.set(a2.token, { at: deps.now(), status: status2 });
59959
+ return status2;
59960
+ }
59961
+ async function adopt(token) {
59962
+ auth = await deps.store(token);
59963
+ seq++;
59964
+ statusCache.clear();
59965
+ pendingOauth = null;
59966
+ for (const cb of listeners) cb();
59967
+ }
59968
+ async function claimCenterToken() {
59969
+ let got;
59970
+ try {
59971
+ got = await deps.oauth.token();
59972
+ } catch {
59973
+ return false;
59974
+ }
59975
+ if (!got || got.token === staleCenterToken) return false;
59976
+ try {
59977
+ await deps.api(got.token).whoami();
59978
+ } catch (err) {
59979
+ if (err instanceof GithubApiError && err.status === 401) staleCenterToken = got.token;
59980
+ return false;
59981
+ }
59982
+ await adopt(got.token);
59983
+ return true;
59984
+ }
59985
+ async function settlePendingOauth() {
59986
+ if (!pendingOauth) return;
59987
+ if (deps.now() > pendingOauth.expiresAt) {
59988
+ pendingOauth = null;
59989
+ return;
59990
+ }
59991
+ try {
59992
+ await claimCenterToken();
59993
+ } catch (err) {
59994
+ deps.logger?.warn("github.identity.claim-failed", { message: err instanceof Error ? err.message : String(err) });
59995
+ pendingOauth = null;
59996
+ }
59997
+ }
59998
+ async function status() {
59999
+ await settlePendingOauth();
60000
+ const a2 = await refresh();
60001
+ const current = a2 ? await whoami(a2) : { status: "missing" };
60002
+ return pendingOauth && current.status !== "authorized" ? { status: "pending", authUrl: pendingOauth.authUrl } : current;
60003
+ }
60004
+ return {
60005
+ cached: () => auth,
60006
+ refresh,
60007
+ status,
60008
+ adopt,
60009
+ async authorize(raw) {
60010
+ const token = raw.trim();
60011
+ try {
60012
+ await deps.api(token).whoami();
60013
+ } catch (err) {
60014
+ throw new GithubTokenRejected(`GitHub \u4E0D\u8BA4\u8FD9\u4E2A token\uFF1A${err instanceof Error ? err.message : String(err)}`);
60015
+ }
60016
+ await adopt(token);
60017
+ return status();
60018
+ },
60019
+ /**
60020
+ * 本机还没有身份时先问中心有没有本人现成的 token(换台机器授权过 / 上次跳回来了但 daemon 没轮到):
60021
+ * 有且 GitHub 认就直接领、不跳 GitHub。本机已有身份(能用的 = 用户要换一个;失效的 = 要重新授权,而中心那份就是这把死的)
60022
+ * 一律跳 GitHub 重来。进 pending 之后每次 status() 顺手问一次中心领没领到。
60023
+ */
60024
+ async oauthStart(returnTo) {
60025
+ const local = await refresh();
60026
+ if (!local && await claimCenterToken()) return status();
60027
+ try {
60028
+ staleCenterToken = (await deps.oauth.token())?.token;
60029
+ } catch {
60030
+ }
60031
+ const { authUrl } = await deps.oauth.start(returnTo);
60032
+ pendingOauth = { authUrl, expiresAt: deps.now() + OAUTH_PENDING_MS };
60033
+ return status();
60034
+ },
60035
+ onChange(cb) {
60036
+ listeners.add(cb);
60037
+ return () => {
60038
+ listeners.delete(cb);
60039
+ };
60040
+ }
60041
+ };
60042
+ }
60043
+
60044
+ // src/handlers/github-auth.ts
60045
+ init_protocol();
60046
+ function ensureOwner2(ctx) {
60047
+ if (!ctx || ctx.principal.kind !== "owner") {
60048
+ throw new ClawdError(ERROR_CODES.UNAUTHORIZED, "UNAUTHORIZED: githubAuth:* requires owner ctx");
60049
+ }
60050
+ }
60051
+ function buildGithubAuthHandlers(deps) {
60052
+ const get = async () => ({ response: await deps.identity.status() });
60053
+ const oauthStart = async (frame, _client, ctx) => {
60054
+ ensureOwner2(ctx);
60055
+ const args = parseArgs2(GithubAuthOauthStartArgsSchema, frame, "githubAuth:oauthStart");
60056
+ return { response: await deps.identity.oauthStart(args.returnTo) };
60057
+ };
60058
+ const authorize = async (frame, _client, ctx) => {
60059
+ ensureOwner2(ctx);
60060
+ const args = parseArgs2(GithubAuthAuthorizeArgsSchema, frame, "githubAuth:authorize");
60061
+ try {
60062
+ return { response: await deps.identity.authorize(args.token) };
60063
+ } catch (err) {
60064
+ if (err instanceof GithubTokenRejected) throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, `VALIDATION_ERROR: ${err.message}`);
60065
+ throw err;
60066
+ }
60067
+ };
60068
+ return {
60069
+ "githubAuth:get": get,
60070
+ "githubAuth:oauthStart": oauthStart,
60071
+ "githubAuth:authorize": authorize
59985
60072
  };
59986
60073
  }
59987
60074
 
@@ -62208,23 +62295,23 @@ async function centralConnectorFill(opts) {
62208
62295
  });
62209
62296
  if (body.ok !== true) throw new CentralClientError("BAD_RESPONSE", "connectors/fields: server did not ack ok");
62210
62297
  }
62211
- async function centralGithubDeployOauthStart(opts) {
62212
- const body = await centralRequest(opts, "/api/persona-cloud/github-deploy/oauth/start", {
62298
+ async function centralGithubAuthOauthStart(opts) {
62299
+ const body = await centralRequest(opts, "/api/persona-cloud/github/oauth/start", {
62213
62300
  method: "POST",
62214
62301
  bearer: opts.ttcJwt,
62215
62302
  body: { provider: opts.provider, ...opts.returnTo ? { returnTo: opts.returnTo } : {} }
62216
62303
  });
62217
- if (typeof body.authUrl !== "string" || !body.authUrl) throw new CentralClientError("BAD_RESPONSE", "github-deploy/oauth/start: missing authUrl");
62304
+ if (typeof body.authUrl !== "string" || !body.authUrl) throw new CentralClientError("BAD_RESPONSE", "github/oauth/start: missing authUrl");
62218
62305
  return { authUrl: body.authUrl };
62219
62306
  }
62220
- async function centralGithubDeployToken(opts) {
62221
- const body = await centralRequest(opts, "/api/persona-cloud/github-deploy/oauth/token", {
62307
+ async function centralGithubAuthToken(opts) {
62308
+ const body = await centralRequest(opts, "/api/persona-cloud/github/oauth/token", {
62222
62309
  method: "POST",
62223
62310
  bearer: opts.ttcJwt,
62224
62311
  body: { provider: opts.provider }
62225
62312
  });
62226
62313
  if (body.token === null || body.token === void 0) return void 0;
62227
- if (typeof body.token !== "string" || !body.token) throw new CentralClientError("BAD_RESPONSE", "github-deploy/oauth/token: malformed token");
62314
+ if (typeof body.token !== "string" || !body.token) throw new CentralClientError("BAD_RESPONSE", "github/oauth/token: malformed token");
62228
62315
  return { token: body.token, login: typeof body.login === "string" ? body.login : "" };
62229
62316
  }
62230
62317
 
@@ -67767,7 +67854,7 @@ async function syncOrgMesh(deps) {
67767
67854
 
67768
67855
  // src/handlers/device.ts
67769
67856
  init_protocol();
67770
- function ensureOwner2(ctx) {
67857
+ function ensureOwner3(ctx) {
67771
67858
  if (!ctx || ctx.principal.kind !== "owner") {
67772
67859
  throw new ClawdError(ERROR_CODES.UNAUTHORIZED, "UNAUTHORIZED: device:* requires owner ctx");
67773
67860
  }
@@ -67862,14 +67949,14 @@ function createConnectToDevice(deps) {
67862
67949
  function buildDeviceHandlers(deps) {
67863
67950
  const connectToDevice = createConnectToDevice(deps);
67864
67951
  const list = async (_frame, _client, ctx) => {
67865
- ensureOwner2(ctx);
67952
+ ensureOwner3(ctx);
67866
67953
  const devices = await deps.listDevices().catch(rethrowLoginExpired);
67867
67954
  return {
67868
67955
  response: { type: "device:list:ok", devices }
67869
67956
  };
67870
67957
  };
67871
67958
  const connect = async (frame, _client, ctx) => {
67872
- ensureOwner2(ctx);
67959
+ ensureOwner3(ctx);
67873
67960
  const { type: _t2, requestId: _r2, ...rest } = frame;
67874
67961
  const args = DeviceConnectArgsSchema.parse(rest);
67875
67962
  const contact = await connectToDevice(args);
@@ -67884,7 +67971,7 @@ function buildDeviceHandlers(deps) {
67884
67971
  };
67885
67972
  };
67886
67973
  const connections = async (_frame, client, ctx) => {
67887
- ensureOwner2(ctx);
67974
+ ensureOwner3(ctx);
67888
67975
  const entries = deps.listOwnerConnections();
67889
67976
  return {
67890
67977
  response: {
@@ -69609,7 +69696,7 @@ function upsertPeerContact(args) {
69609
69696
  }
69610
69697
 
69611
69698
  // src/handlers/contact.ts
69612
- function ensureOwner3(ctx) {
69699
+ function ensureOwner4(ctx) {
69613
69700
  if (!ctx || ctx.principal.kind !== "owner") {
69614
69701
  throw new ClawdError(
69615
69702
  ERROR_CODES.UNAUTHORIZED,
@@ -69619,7 +69706,7 @@ function ensureOwner3(ctx) {
69619
69706
  }
69620
69707
  function buildContactHandlers(deps) {
69621
69708
  const list = async (_frame, _client, ctx) => {
69622
- ensureOwner3(ctx);
69709
+ ensureOwner4(ctx);
69623
69710
  return {
69624
69711
  response: {
69625
69712
  type: "contact:list:ok",
@@ -69628,7 +69715,7 @@ function buildContactHandlers(deps) {
69628
69715
  };
69629
69716
  };
69630
69717
  const pin = async (frame, _client, ctx) => {
69631
- ensureOwner3(ctx);
69718
+ ensureOwner4(ctx);
69632
69719
  const args = parseArgs2(ContactPinArgsSchema, frame, "contact:pin");
69633
69720
  const pinnedAt = args.pinned ? deps.now() : null;
69634
69721
  const hit = deps.store.setPin(args.deviceId, pinnedAt);
@@ -69652,7 +69739,7 @@ function buildContactHandlers(deps) {
69652
69739
  };
69653
69740
  };
69654
69741
  const setNote = async (frame, _client, ctx) => {
69655
- ensureOwner3(ctx);
69742
+ ensureOwner4(ctx);
69656
69743
  const args = parseArgs2(ContactSetNoteArgsSchema, frame, "contact:setNote");
69657
69744
  const hit = deps.store.setNote(args.deviceId, args.note);
69658
69745
  if (!hit) {
@@ -69675,13 +69762,13 @@ function buildContactHandlers(deps) {
69675
69762
  };
69676
69763
  };
69677
69764
  const orgPeers = async (_frame, _client, ctx) => {
69678
- ensureOwner3(ctx);
69765
+ ensureOwner4(ctx);
69679
69766
  return {
69680
69767
  response: { type: "org:peers:ok", peers: getCachedOrgPeers() }
69681
69768
  };
69682
69769
  };
69683
69770
  const remove = async (frame, _client, ctx) => {
69684
- ensureOwner3(ctx);
69771
+ ensureOwner4(ctx);
69685
69772
  const args = parseArgs2(ContactRemoveArgsSchema, frame, "contact:remove");
69686
69773
  deps.store.removeByDeviceId(args.deviceId);
69687
69774
  deps.broadcast({
@@ -69755,7 +69842,7 @@ function buildContactHandlers(deps) {
69755
69842
 
69756
69843
  // src/handlers/contact-remote-access.ts
69757
69844
  init_protocol();
69758
- function ensureOwner4(ctx) {
69845
+ function ensureOwner5(ctx) {
69759
69846
  if (!ctx || ctx.principal.kind !== "owner") {
69760
69847
  throw new ClawdError(
69761
69848
  ERROR_CODES.UNAUTHORIZED,
@@ -69765,7 +69852,7 @@ function ensureOwner4(ctx) {
69765
69852
  }
69766
69853
  function buildContactRemoteAccessHandlers(deps) {
69767
69854
  const setRemoteAccess = async (frame, _client, ctx) => {
69768
- ensureOwner4(ctx);
69855
+ ensureOwner5(ctx);
69769
69856
  const args = parseArgs2(ContactSetRemoteAccessArgsSchema, frame, "contact:setRemoteAccess");
69770
69857
  const hit = deps.store.setRemoteAccess(args.deviceId, {
69771
69858
  remoteAccessAllowed: args.remoteAccessAllowed
@@ -69925,7 +70012,7 @@ function buildFeishuAuthHandlers(deps) {
69925
70012
 
69926
70013
  // src/handlers/cloud-connectors.ts
69927
70014
  init_protocol();
69928
- function ensureOwner5(ctx) {
70015
+ function ensureOwner6(ctx) {
69929
70016
  if (!ctx || ctx.principal.kind !== "owner") {
69930
70017
  throw new ClawdError(ERROR_CODES.UNAUTHORIZED, "UNAUTHORIZED: cloud:connectors:* requires owner ctx");
69931
70018
  }
@@ -69945,7 +70032,7 @@ function buildCloudConnectorsHandlers(deps) {
69945
70032
  return { gatewayPersonaId: persona.cloud.personaId, connectors: all.filter((c2) => c2.credentialSource === "personal") };
69946
70033
  }
69947
70034
  const list = async (frame, _client, ctx) => {
69948
- ensureOwner5(ctx);
70035
+ ensureOwner6(ctx);
69949
70036
  const { personaId: personaId2 } = parseArgs2(CloudConnectorsListArgsSchema, frame, "cloud:connectors:list");
69950
70037
  const { gatewayPersonaId, connectors } = await personalConnectors(personaId2);
69951
70038
  if (connectors.length === 0) return { response: { type: "cloud:connectors:list:ok", connectors: [] } };
@@ -69956,7 +70043,7 @@ function buildCloudConnectorsHandlers(deps) {
69956
70043
  return { response: { type: "cloud:connectors:list:ok", connectors: parsed.data.connectors } };
69957
70044
  };
69958
70045
  const fill = async (frame, _client, ctx) => {
69959
- ensureOwner5(ctx);
70046
+ ensureOwner6(ctx);
69960
70047
  const args = parseArgs2(CloudConnectorFillArgsSchema, frame, "cloud:connectors:fill");
69961
70048
  const { connectors } = await personalConnectors(args.personaId);
69962
70049
  const connector = connectors.find((c2) => c2.name === args.name);
@@ -70027,8 +70114,6 @@ var METHOD_GRANT_MAP = {
70027
70114
  "deploy:finishScan": ADMIN_ANY,
70028
70115
  "deploy:release": ADMIN_ANY,
70029
70116
  "deploy:rerunJob": ADMIN_ANY,
70030
- "deploy:githubAuthorize": ADMIN_ANY,
70031
- "deploy:githubOauthStart": ADMIN_ANY,
70032
70117
  // cloud:connectors:*(云 persona 卡片上的连接器;design 2026-09-11-mcp-connector):
70033
70118
  // 拿本机登录凭证向中心代查 / 代填使用者本人的凭据,guest 一律够不着
70034
70119
  "cloud:connectors:list": ADMIN_ANY,
@@ -70117,7 +70202,11 @@ var METHOD_GRANT_MAP = {
70117
70202
  "project:create": ADMIN_ANY,
70118
70203
  "project:patch": ADMIN_ANY,
70119
70204
  "project:reveal": ADMIN_ANY,
70120
- // github:*(PR 收件箱,spec 2026-08-11):走 owner 本机 gh 凭据,owner-only 一刀切
70205
+ // githubAuth:*(本机 GitHub 身份,机器级;spec 2026-09-15-github-auth-unify):关联 / 粘 token 只认人手,guest 够不着
70206
+ "githubAuth:get": ADMIN_ANY,
70207
+ "githubAuth:oauthStart": ADMIN_ANY,
70208
+ "githubAuth:authorize": ADMIN_ANY,
70209
+ // github:*(PR 收件箱,spec 2026-08-11):凭据是 githubAuth 那份身份(GH_TOKEN),owner-only 一刀切
70121
70210
  "github:status": ADMIN_ANY,
70122
70211
  "github:prList": ADMIN_ANY,
70123
70212
  "github:prDetail": ADMIN_ANY,
@@ -70229,8 +70318,8 @@ var HUMAN_ONLY_METHODS = [
70229
70318
  "deploy:release",
70230
70319
  "deploy:putSecret",
70231
70320
  "deploy:rerunJob",
70232
- "deploy:githubAuthorize",
70233
- "deploy:githubOauthStart"
70321
+ "githubAuth:oauthStart",
70322
+ "githubAuth:authorize"
70234
70323
  ];
70235
70324
  var HUMAN_ONLY = new Set(HUMAN_ONLY_METHODS);
70236
70325
  function isHumanOnlyDenied(method, transport) {
@@ -70273,7 +70362,7 @@ function computeMethodAccess(args) {
70273
70362
  }
70274
70363
 
70275
70364
  // src/version.ts
70276
- var version = "0.2.517".length > 0 ? "0.2.517" : "dev";
70365
+ var version = "0.2.518".length > 0 ? "0.2.518" : "dev";
70277
70366
 
70278
70367
  // src/cli-probe/probe.ts
70279
70368
  var fs77 = __toESM(require("fs"), 1);
@@ -71830,9 +71919,8 @@ function parsePrDetailResponse(json, repoRef) {
71830
71919
  }
71831
71920
 
71832
71921
  // src/handlers/github.ts
71833
- var HOSTNAME = "github.com";
71834
71922
  function buildGithubHandlers(deps) {
71835
- const { gh } = deps;
71923
+ const { gh, identity } = deps;
71836
71924
  return {
71837
71925
  "github:status": async () => {
71838
71926
  try {
@@ -71843,13 +71931,9 @@ function buildGithubHandlers(deps) {
71843
71931
  }
71844
71932
  throw err;
71845
71933
  }
71846
- try {
71847
- await gh.run(["auth", "status", "--active", "--hostname", HOSTNAME]);
71848
- } catch {
71849
- return { response: { installed: true, authenticated: false } };
71850
- }
71851
- const login = (await gh.run(["api", "user", "--jq", ".login"])).trim();
71852
- const response = { installed: true, authenticated: true, login };
71934
+ const st2 = await identity.status();
71935
+ if (st2.status !== "authorized") return { response: { installed: true, authenticated: false } };
71936
+ const response = { installed: true, authenticated: true, login: st2.login };
71853
71937
  return { response };
71854
71938
  },
71855
71939
  "github:prList": async (frame) => {
@@ -71937,7 +72021,7 @@ function buildGithubHandlers(deps) {
71937
72021
 
71938
72022
  // src/handlers/visitor.ts
71939
72023
  init_protocol();
71940
- function ensureOwner6(ctx) {
72024
+ function ensureOwner7(ctx) {
71941
72025
  if (!ctx || ctx.principal.kind !== "owner") {
71942
72026
  throw new ClawdError(
71943
72027
  ERROR_CODES.UNAUTHORIZED,
@@ -71947,7 +72031,7 @@ function ensureOwner6(ctx) {
71947
72031
  }
71948
72032
  function buildVisitorHandlers(deps) {
71949
72033
  const list = async (_frame, _client, ctx) => {
71950
- ensureOwner6(ctx);
72034
+ ensureOwner7(ctx);
71951
72035
  return {
71952
72036
  response: {
71953
72037
  type: "visitor:list",
@@ -72265,7 +72349,7 @@ function buildMethodHandlers(deps) {
72265
72349
  ownerIdProvider: deps.shiftOwnerIdProvider,
72266
72350
  now: () => Date.now()
72267
72351
  }),
72268
- ...buildGithubHandlers({ gh: deps.ghRunner }),
72352
+ ...buildGithubHandlers({ gh: deps.ghRunner, identity: deps.githubIdentity }),
72269
72353
  ...buildVisitorHandlers({ visitorStore: deps.visitorStore })
72270
72354
  };
72271
72355
  handlers["meta:methods"] = buildMetaMethodsHandler({
@@ -73084,13 +73168,46 @@ async function startDaemon(config) {
73084
73168
  const secretsBundleRoot = findSecretsBundleRoot(logger);
73085
73169
  const localSecretsDir = import_node_path79.default.join(config.dataDir, LOCAL_SECRETS_DIRNAME);
73086
73170
  const readSecrets = () => readSecretTable({ bundleRoot: secretsBundleRoot, overrideDir: localSecretsDir, logger });
73171
+ const githubIdentity = createGithubIdentity({
73172
+ resolve: () => resolveGithubAuth({ exec: runCommand, fileToken: () => readSecrets()[GITHUB_TOKEN_VAR] }),
73173
+ store: (token) => storeGithubAuth({
73174
+ exec: runCommand,
73175
+ token,
73176
+ writeFileToken: (t2) => {
73177
+ upsertEnvFileVar(import_node_path79.default.join(localSecretsDir, GITHUB_TOKEN_FILE), GITHUB_TOKEN_VAR, t2);
73178
+ logger.warn("github.identity.token-in-file", { reason: "git \u6CA1\u914D credential helper\uFF0Ctoken \u843D\u672C\u673A\u51ED\u636E\u8868", file: GITHUB_TOKEN_FILE });
73179
+ }
73180
+ }),
73181
+ api: (token) => createGithubApi({ token }),
73182
+ oauth: {
73183
+ start: async (returnTo) => {
73184
+ const r2 = ownerIdentityStore.read();
73185
+ if (!r2) throw new ClawdError(ERROR_CODES.FEISHU_LOGIN_REQUIRED, "FEISHU_LOGIN_REQUIRED: \u8FD8\u6CA1\u767B\u5F55\u98DE\u4E66\uFF0C\u5173\u8054 GitHub \u8981\u7ECF\u4E2D\u5FC3\uFF1B\u5148\u767B\u5F55\uFF0C\u6216\u5728\u5361\u7247\u4E0A\u624B\u52A8\u7C98 token");
73186
+ try {
73187
+ return await centralGithubAuthOauthStart({ api: config.clawosApi ?? DEFAULT_CLAWOS_API, ttcJwt: r2.ttcToken, provider: r2.identity.provider, ...returnTo ? { returnTo } : {} });
73188
+ } catch (err) {
73189
+ throw new ClawdError(ERROR_CODES.INTERNAL, `\u5411\u4E2D\u5FC3\u8981 GitHub \u6388\u6743\u94FE\u63A5\u6CA1\u6210\uFF1A${err instanceof Error ? err.message : String(err)}`);
73190
+ }
73191
+ },
73192
+ token: async () => {
73193
+ const r2 = ownerIdentityStore.read();
73194
+ if (!r2) return void 0;
73195
+ return centralGithubAuthToken({ api: config.clawosApi ?? DEFAULT_CLAWOS_API, ttcJwt: r2.ttcToken, provider: r2.identity.provider });
73196
+ }
73197
+ },
73198
+ now: () => Date.now(),
73199
+ logger
73200
+ });
73201
+ void githubIdentity.refresh();
73087
73202
  let refreshAppBuilderMcpConfig;
73088
73203
  const resolvePersonaMcpConfig = (personaId2) => {
73089
73204
  refreshAppBuilderMcpConfig?.(personaId2);
73205
+ const secrets = secretsWithGithubIdentity(readSecrets(), githubIdentity.cached());
73206
+ void githubIdentity.refresh();
73090
73207
  return renderPersonaMcpSecrets({
73091
73208
  personaId: personaId2,
73092
73209
  personaDir: import_node_path79.default.join(config.dataDir, "personas", personaId2),
73093
- secrets: readSecrets(),
73210
+ secrets,
73094
73211
  outDir: import_node_path79.default.join(config.dataDir, "persona-mcp"),
73095
73212
  logger
73096
73213
  });
@@ -73928,8 +74045,9 @@ async function startDaemon(config) {
73928
74045
  // daemon 稳定身份),跟我们 ShiftStore boot 时用的 provider 一致。
73929
74046
  shiftStore,
73930
74047
  shiftOwnerIdProvider: () => ownerPrincipalId,
73931
- // PR 收件箱:github:* handler spawn 本机 gh CLI(凭据复用 gh 登录态)
73932
- ghRunner: createGhRunner(),
74048
+ // PR 收件箱:github:* handler spawn 本机 gh CLI,凭据是 clawd 的 GitHub 身份(GH_TOKEN),没有才是 gh 自己的登录态
74049
+ ghRunner: createGhRunner({ token: () => githubIdentity.cached()?.token }),
74050
+ githubIdentity,
73933
74051
  // persona web 分享:visitor:list owner RPC 读访客名单(同 exchange upsert 的实例)
73934
74052
  visitorStore
73935
74053
  })
@@ -74044,38 +74162,8 @@ async function startDaemon(config) {
74044
74162
  return p3 ? { ...p3.tool ? { tool: p3.tool } : {}, ...p3.model ? { model: p3.model } : {} } : void 0;
74045
74163
  },
74046
74164
  exec: runCommand,
74047
- // GitHub 的身份 = 本机 git 的身份(spec 2026-09-14-persona-deploy-github-api §3.1):先问 git 的凭据存储,
74048
- // 没有退到本机凭据表里上云专用的那条(GITHUB_DEPLOY_TOKEN,与 app-builder 的 GITHUB_TOKEN 刻意不同名);
74049
- // 授权时先交给 git 存,git 没配 helper 才写 <dataDir>/secrets/github-deploy.env。顺序 / 退路的规则在 github-auth.ts,有测试
74050
- github: {
74051
- resolve: () => resolveGithubAuth({ exec: runCommand, fileToken: () => readSecrets()[DEPLOY_TOKEN_VAR] }),
74052
- store: (token) => storeGithubAuth({
74053
- exec: runCommand,
74054
- token,
74055
- writeFileToken: (t2) => {
74056
- upsertEnvFileVar(import_node_path79.default.join(localSecretsDir, DEPLOY_TOKEN_FILE), DEPLOY_TOKEN_VAR, t2);
74057
- logger.warn("deploy.github.token-in-file", { reason: "git \u6CA1\u914D credential helper\uFF0Ctoken \u843D\u672C\u673A\u51ED\u636E\u8868", file: DEPLOY_TOKEN_FILE });
74058
- }
74059
- }),
74060
- api: (token) => createGithubApi({ token }),
74061
- // 点链接的 OAuth 经中心(spec §8):登录凭证只在 daemon 内部传给中心,与 cloudConnectors 同一条纪律
74062
- oauth: {
74063
- start: async (returnTo) => {
74064
- const r2 = ownerIdentityStore.read();
74065
- if (!r2) throw new ClawdError(ERROR_CODES.FEISHU_LOGIN_REQUIRED, "FEISHU_LOGIN_REQUIRED: \u8FD8\u6CA1\u767B\u5F55\u98DE\u4E66\uFF0C\u7528 GitHub \u6388\u6743\u8981\u7ECF\u4E2D\u5FC3\uFF1B\u5148\u767B\u5F55\uFF0C\u6216\u5728\u5361\u7247\u4E0A\u624B\u52A8\u7C98 token");
74066
- try {
74067
- return await centralGithubDeployOauthStart({ api: config.clawosApi ?? DEFAULT_CLAWOS_API, ttcJwt: r2.ttcToken, provider: r2.identity.provider, ...returnTo ? { returnTo } : {} });
74068
- } catch (err) {
74069
- throw new ClawdError(ERROR_CODES.INTERNAL, `\u5411\u4E2D\u5FC3\u8981 GitHub \u6388\u6743\u94FE\u63A5\u6CA1\u6210\uFF1A${err instanceof Error ? err.message : String(err)}`);
74070
- }
74071
- },
74072
- token: async () => {
74073
- const r2 = ownerIdentityStore.read();
74074
- if (!r2) return void 0;
74075
- return centralGithubDeployToken({ api: config.clawosApi ?? DEFAULT_CLAWOS_API, ttcJwt: r2.ttcToken, provider: r2.identity.provider });
74076
- }
74077
- }
74078
- },
74165
+ githubIdentity,
74166
+ githubApi: (token) => createGithubApi({ token }),
74079
74167
  org: PERSONA_REPO_ORG,
74080
74168
  now: () => Date.now(),
74081
74169
  startScan: (personaId2, scanBase) => {
@@ -74122,6 +74210,7 @@ async function startDaemon(config) {
74122
74210
  }
74123
74211
  });
74124
74212
  Object.assign(handlers, deployHandlers);
74213
+ Object.assign(handlers, buildGithubAuthHandlers({ identity: githubIdentity }));
74125
74214
  const shiftSpawnFn = createShiftSpawnFn({ sessionManager: manager });
74126
74215
  const shiftScheduler = createShiftScheduler({
74127
74216
  store: shiftStore,