@clawos-dev/clawd 0.2.647 → 0.2.648

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.
@@ -31292,6 +31292,8 @@ var EnvConnectorSchema = external_exports.object({
31292
31292
  id: external_exports.string().min(1),
31293
31293
  credentialSource: external_exports.enum(CREDENTIAL_SOURCES).optional(),
31294
31294
  auth: external_exports.enum(CONNECTOR_AUTHS).optional(),
31295
+ // Stamped by deploy:release from the authenticated owner, never chosen by the UI.
31296
+ publisherUnionId: external_exports.string().regex(/^on_[A-Za-z0-9_-]+$/).optional(),
31295
31297
  fields: external_exports.array(ConnectorFieldSchema).default([]),
31296
31298
  http: external_exports.object({ url: external_exports.string().min(1), headers: external_exports.record(external_exports.string(), external_exports.string()) }).optional()
31297
31299
  });
@@ -31523,6 +31525,11 @@ var DeployStateSchema = external_exports.object({
31523
31525
  personaId: external_exports.string().min(1),
31524
31526
  /** 本机 GitHub 身份(机器级,githubAuth:* 的同一份;上云页顶上那张卡用)。老 daemon 不给(可选只为兼容),UI 缺席时按未知处理 */
31525
31527
  github: GithubAuthStatusSchema.optional(),
31528
+ /** Current publisher's Feishu tool grant, including authorization links before first release. */
31529
+ publisherFeishuAuth: external_exports.object({
31530
+ integration: external_exports.object({ connected: external_exports.boolean(), authUrl: external_exports.string().optional(), error: external_exports.string().optional() }).strict(),
31531
+ production: external_exports.object({ connected: external_exports.boolean(), authUrl: external_exports.string().optional(), error: external_exports.string().optional() }).strict()
31532
+ }).strict().optional(),
31526
31533
  repo: DeployRepoSchema.optional(),
31527
31534
  scans: external_exports.array(DeployScanSchema).default([]),
31528
31535
  releases: external_exports.array(DeployReleasedSchema).default([]),
package/dist/cli.cjs CHANGED
@@ -6022,9 +6022,14 @@ var init_github_auth_schemas = __esm({
6022
6022
  function isFeishuBotConnector(c2) {
6023
6023
  return c2.name === "feishu" && c2.runner === "cli" && c2.kind === "env" && c2.credentialSource === "shared";
6024
6024
  }
6025
+ function isFeishuPublisherConnector(c2) {
6026
+ return c2.name === "feishu" && c2.id === "feishu" && c2.runner === "cli" && c2.kind === "proxy" && c2.protocol === "lark-sidecar" && c2.credentialSource === "shared" && c2.auth === "oauth" && c2.fields.length === 0;
6027
+ }
6025
6028
  function connectorBlockers(c2) {
6026
6029
  if (!c2.credentialSource) return [`\u8FDE\u63A5\u5668 ${c2.name} \u8FD8\u6CA1\u9009\u7528\u8C01\u7684\u8D26\u53F7`];
6027
6030
  const out = [];
6031
+ if (isFeishuPublisherConnector(c2)) return out;
6032
+ if (c2.protocol === "lark-sidecar" && c2.credentialSource === "shared") return ["\u8FDE\u63A5\u5668 feishu \u7684\u53D1\u5E03\u8005 OAuth \u914D\u7F6E\u4E0D\u5B8C\u6574\uFF0C\u8BF7\u91CD\u65B0\u9009\u62E9\u8D26\u53F7\u8EAB\u4EFD"];
6028
6033
  if (isFeishuBotConnector(c2)) {
6029
6034
  return FEISHU_BOT_FIELDS.filter((f2) => !c2.fields.includes(f2)).map((f2) => `\u8FDE\u63A5\u5668 feishu \u7684 Lark App Bot \u7F3A\u5C11 ${f2 === "LARKSUITE_CLI_APP_ID" ? "App ID" : "App Secret"}`);
6030
6035
  }
@@ -6138,6 +6143,8 @@ var init_environment_schemas = __esm({
6138
6143
  id: external_exports.string().min(1),
6139
6144
  credentialSource: external_exports.enum(CREDENTIAL_SOURCES).optional(),
6140
6145
  auth: external_exports.enum(CONNECTOR_AUTHS).optional(),
6146
+ // Stamped by deploy:release from the authenticated owner, never chosen by the UI.
6147
+ publisherUnionId: external_exports.string().regex(/^on_[A-Za-z0-9_-]+$/).optional(),
6141
6148
  fields: external_exports.array(ConnectorFieldSchema).default([]),
6142
6149
  http: external_exports.object({ url: external_exports.string().min(1), headers: external_exports.record(external_exports.string(), external_exports.string()) }).optional()
6143
6150
  });
@@ -6434,6 +6441,11 @@ var init_deploy_schemas = __esm({
6434
6441
  personaId: external_exports.string().min(1),
6435
6442
  /** 本机 GitHub 身份(机器级,githubAuth:* 的同一份;上云页顶上那张卡用)。老 daemon 不给(可选只为兼容),UI 缺席时按未知处理 */
6436
6443
  github: GithubAuthStatusSchema.optional(),
6444
+ /** Current publisher's Feishu tool grant, including authorization links before first release. */
6445
+ publisherFeishuAuth: external_exports.object({
6446
+ integration: external_exports.object({ connected: external_exports.boolean(), authUrl: external_exports.string().optional(), error: external_exports.string().optional() }).strict(),
6447
+ production: external_exports.object({ connected: external_exports.boolean(), authUrl: external_exports.string().optional(), error: external_exports.string().optional() }).strict()
6448
+ }).strict().optional(),
6437
6449
  repo: DeployRepoSchema.optional(),
6438
6450
  scans: external_exports.array(DeployScanSchema).default([]),
6439
6451
  releases: external_exports.array(DeployReleasedSchema).default([]),
@@ -57576,7 +57588,7 @@ function cliConnectorFromFinding(item) {
57576
57588
  }
57577
57589
  function mergeConnector(existing, incoming) {
57578
57590
  if (!existing) return incoming;
57579
- if (isFeishuBotConnector(existing) && incoming.name === existing.name && incoming.runner === "cli") return existing;
57591
+ if ((isFeishuBotConnector(existing) || isFeishuPublisherConnector(existing)) && incoming.name === existing.name && incoming.runner === "cli") return existing;
57580
57592
  const reviewed = existing.credentialSource !== void 0;
57581
57593
  return {
57582
57594
  ...incoming,
@@ -62939,9 +62951,22 @@ function buildDeployHandlers(deps) {
62939
62951
  run = historical ? selected?.environment === "integration" ? intRun : prodRun ?? run : prodTarget.run;
62940
62952
  }
62941
62953
  if (!historical && latestProductionRelease(file.releases) && (!api || github.status !== "authorized")) online = (await cachedCloud(personaId2, "production", run, unpublish, repo2 ? cloudPersonaIdOf(repo2.url) : personaId2)).online;
62954
+ let publisherFeishuAuth;
62955
+ if (scans.at(-1)?.manifest.connectors.some(isFeishuPublisherConnector)) {
62956
+ const query2 = async (environment2) => {
62957
+ try {
62958
+ return await deps.publisherFeishuAuth(repo2 ? cloudPersonaIdOf(repo2.url) : personaId2, environment2);
62959
+ } catch (e) {
62960
+ return { connected: false, error: e instanceof Error ? e.message : "\u98DE\u4E66\u6388\u6743\u72B6\u6001\u67E5\u8BE2\u5931\u8D25" };
62961
+ }
62962
+ };
62963
+ const [integration, production] = await Promise.all([query2("integration"), query2("production")]);
62964
+ publisherFeishuAuth = { integration, production };
62965
+ }
62942
62966
  return {
62943
62967
  personaId: personaId2,
62944
62968
  github,
62969
+ ...publisherFeishuAuth ? { publisherFeishuAuth } : {},
62945
62970
  targets,
62946
62971
  promotions: file.promotions,
62947
62972
  ...repo2 ? { repo: repo2 } : {},
@@ -63161,7 +63186,7 @@ function buildDeployHandlers(deps) {
63161
63186
  }
63162
63187
  if (args.kind === "connector") {
63163
63188
  const incoming = cliConnectorFromFinding(item);
63164
- const previous = scan.manifest.connectors.some((c2) => c2.name === incoming.name) ? void 0 : [...file.scans].reverse().find((s2) => s2.status === "done")?.manifest.connectors.find((c2) => c2.name === incoming.name && isFeishuBotConnector(c2));
63189
+ const previous = scan.manifest.connectors.some((c2) => c2.name === incoming.name) ? void 0 : [...file.scans].reverse().find((s2) => s2.status === "done")?.manifest.connectors.find((c2) => c2.name === incoming.name && (isFeishuBotConnector(c2) || isFeishuPublisherConnector(c2)));
63165
63190
  item = mergeConnector(previous, incoming);
63166
63191
  }
63167
63192
  if (args.kind === "package") item = await settlePackage(item);
@@ -63291,11 +63316,23 @@ function buildDeployHandlers(deps) {
63291
63316
  blockers.push(cloudEngine.tool === "claude" ? "\u8FD8\u6CA1\u9009\u4E91\u4E0A\u6A21\u578B\uFF08Claude \u7684\u4E91\u4E0A\u6A21\u578B\u4E0E\u672C\u673A\u5206\u5F00\u5B58\uFF09\u2014\u2014\u5728\u4E0A\u4E91\u9875\u300C\u5F15\u64CE\u4E0E\u6A21\u578B\u300D\u91CC\u9009\u4E00\u4E2A" : "\u8FD8\u6CA1\u9009\u6A21\u578B\uFF08\u5207\u5F15\u64CE\u65F6\u4F1A\u6E05\u7A7A\uFF09\u2014\u2014\u5728\u4E0A\u4E91\u9875\u300C\u5F15\u64CE\u4E0E\u6A21\u578B\u300D\u91CC\u9009\u4E00\u4E2A");
63292
63317
  }
63293
63318
  if (blockers.length > 0) throw invalid(`\u8FD8\u5DEE\uFF1A${blockers.join("\uFF1B")}`);
63319
+ const publisherUnionId = ctx?.principal.feishuUnionId;
63320
+ if (entry.manifest.connectors.some(isFeishuPublisherConnector) && !/^on_[A-Za-z0-9_-]+$/.test(publisherUnionId ?? "")) {
63321
+ throw invalid("\u4F7F\u7528\u53D1\u5E03\u8005\u8D26\u53F7\u524D\uFF0C\u8BF7\u5148\u767B\u5F55\u98DE\u4E66");
63322
+ }
63323
+ if (entry.manifest.connectors.some(isFeishuPublisherConnector)) {
63324
+ const status = await deps.publisherFeishuAuth?.(cloudPersonaIdOf(repo2.url), "integration");
63325
+ if (!status?.connected) throw invalid("\u8BF7\u5148\u5728\u8FDE\u63A5\u5668\u533A\u5173\u8054\u53D1\u5E03\u8005\u7684\u6D4B\u8BD5\u73AF\u5883\u98DE\u4E66\u8D26\u53F7\uFF0C\u518D\u53D1\u5E03");
63326
+ }
63327
+ const manifest = { ...entry.manifest, connectors: entry.manifest.connectors.map((c2) => {
63328
+ const { publisherUnionId: _untrusted, ...connector } = c2;
63329
+ return isFeishuPublisherConnector(c2) ? { ...connector, publisherUnionId } : connector;
63330
+ }) };
63294
63331
  await configureTestIdentity(api, repoSlug(repo2.url));
63295
63332
  if (await deps.claimPublisher(cloudPersonaIdOf(repo2.url), "integration") === "taken") throw invalid("\u6D4B\u8BD5\u73AF\u5883\u91CC\u7684\u8FD9\u4E2A\u540D\u5B57\u5DF2\u7531\u522B\u4EBA\u53D1\u5E03");
63296
63333
  const releaseId = (0, import_node_crypto14.randomUUID)();
63297
63334
  try {
63298
- const result = await release(deps.exec, api, { dir, now: deps.now, auth, manifest: entry.manifest, onPrepared: async (head) => {
63335
+ const result = await release(deps.exec, api, { dir, now: deps.now, auth, manifest, onPrepared: async (head) => {
63299
63336
  const latest2 = await readHistory(dir);
63300
63337
  await writeScanFile(dir, { ...latest2, releases: [...latest2.releases, { id: releaseId, environment: "integration", head, contentHead: entry.head, scanId: entry.id, at: deps.now(), pushed: false }] });
63301
63338
  } });
@@ -63440,6 +63477,7 @@ function buildDeployHandlers(deps) {
63440
63477
  const scan = file.scans.find((s2) => s2.id === record4.scanId);
63441
63478
  if (!scan || scan.status !== "done") throw invalid("\u627E\u4E0D\u5230\u672C\u6B21\u6D4B\u8BD5\u7684\u5B8C\u6574\u626B\u63CF\uFF0C\u8BF7\u91CD\u65B0\u626B\u63CF");
63442
63479
  const missing = requiredEnvNames(scan.manifest).filter((name) => !names.find((s2) => s2.env === name)?.setAt);
63480
+ if (scan.manifest.connectors.some(isFeishuPublisherConnector) && !(await deps.publisherFeishuAuth?.(cloudPersonaIdOf(repo2.url), "production"))?.connected) throw invalid("\u8BF7\u5148\u5728\u8FDE\u63A5\u5668\u533A\u5173\u8054\u53D1\u5E03\u8005\u7684\u7EBF\u4E0A\u73AF\u5883\u98DE\u4E66\u8D26\u53F7\uFF0C\u518D\u4E0A\u7EBF");
63443
63481
  if (missing.length || engineAuthState(names) === "partial") throw invalid("\u7EBF\u4E0A\u73AF\u5883\u51ED\u636E\u5C1A\u672A\u586B\u9F50\uFF1A" + (missing.join(" / ") || ENGINE_AUTH_PARTIAL_BLOCKER));
63444
63482
  const reuse = old && old.phase !== "failed" && (recovering || !old.verified || old.verified.verification.fingerprint === record4.verified.verification.fingerprint);
63445
63483
  if (old && !reuse && old.phase === "prepared") {
@@ -73942,17 +73980,17 @@ function buildCloudConnectorsHandlers(deps) {
73942
73980
  if (!loginPass) throw new ClawdError(ERROR_CODES.FEISHU_LOGIN_REQUIRED, "FEISHU_LOGIN_REQUIRED: \u8FD8\u6CA1\u767B\u5F55\u98DE\u4E66\uFF0C\u4E91\u4E0A persona \u7684\u8FDE\u63A5\u5668\u7528\u4E0D\u4E86");
73943
73981
  return { loginPass };
73944
73982
  }
73945
- async function personalConnectors(personaId2) {
73983
+ async function personalConnectors(personaId2, publisherUnionId) {
73946
73984
  const persona = deps.getPersona(personaId2);
73947
73985
  if (!persona) throw new ClawdError(ERROR_CODES.PERSONA_NOT_FOUND, `PERSONA_NOT_FOUND: \u6CA1\u6709\u8FD9\u4E2A persona\uFF08${personaId2}\uFF09`);
73948
73986
  if (!persona.cloud) throw invalid2(`${personaId2} \u4E0D\u662F\u4E91\u4E0A persona\uFF0C\u6CA1\u6709\u8FDE\u63A5\u5668\u533A`);
73949
73987
  const all = await deps.gatewayConnectors(persona.cloud.personaId, persona.cloud.gatewayUrl);
73950
- return { environment: persona.cloud.environment ?? "production", gatewayPersonaId: persona.cloud.personaId, connectors: all.filter((c2) => c2.credentialSource === "personal") };
73988
+ return { environment: persona.cloud.environment ?? "production", gatewayPersonaId: persona.cloud.personaId, connectors: all.flatMap((c2) => c2.credentialSource === "personal" ? [c2] : isFeishuPublisherConnector(c2) && publisherUnionId && c2.publisherUnionId === publisherUnionId ? [{ ...c2, credentialSource: "personal" }] : []) };
73951
73989
  }
73952
73990
  const list = async (frame, _client, ctx) => {
73953
73991
  ensureOwner6(ctx);
73954
73992
  const { personaId: personaId2 } = parseArgs2(CloudConnectorsListArgsSchema, frame, "cloud:connectors:list");
73955
- const { gatewayPersonaId, connectors, environment } = await personalConnectors(personaId2);
73993
+ const { gatewayPersonaId, connectors, environment } = await personalConnectors(personaId2, ctx?.principal.feishuUnionId);
73956
73994
  if (connectors.length === 0) return { response: { type: "cloud:connectors:list:ok", connectors: [] } };
73957
73995
  const login = requireLogin(environment);
73958
73996
  const r2 = await (deps.centralForEnvironment?.(environment) ?? deps.central).mine({ ...login, personaId: gatewayPersonaId, connectors });
@@ -74297,7 +74335,7 @@ function computeMethodAccess(args) {
74297
74335
  }
74298
74336
 
74299
74337
  // src/version.ts
74300
- var version = "0.2.647".length > 0 ? "0.2.647" : "dev";
74338
+ var version = "0.2.648".length > 0 ? "0.2.648" : "dev";
74301
74339
 
74302
74340
  // src/cli-probe/probe.ts
74303
74341
  var fs85 = __toESM(require("fs"), 1);
@@ -76836,6 +76874,7 @@ async function startDaemon(config) {
76836
76874
  contactStore.load();
76837
76875
  const serverKeyStore = new ServerKeyStore(config.dataDir);
76838
76876
  const buildConnectAuthDeps = () => ({
76877
+ ownerFeishuUnionId,
76839
76878
  isOwnerToken: (x2) => resolvedAuthToken != null && constantTimeEqual(x2, resolvedAuthToken),
76840
76879
  ownerPrincipalId,
76841
76880
  ownerDisplayName,
@@ -77957,6 +77996,18 @@ async function startDaemon(config) {
77957
77996
  const persona = list.find((p3) => p3.personaId === personaId2);
77958
77997
  return { online: persona !== void 0, ...persona?.image ? { image: persona.image } : {} };
77959
77998
  },
77999
+ publisherFeishuAuth: async (personaId2, environment) => {
78000
+ const loginPass = requireEnvironmentIdentity(environmentStores, environment).loginPass;
78001
+ const result = await centralConnectorsMine({
78002
+ api: targetApi(environment),
78003
+ loginPass,
78004
+ personaId: personaId2,
78005
+ connectors: [{ name: "feishu", id: "feishu", kind: "proxy", runner: "cli", protocol: "lark-sidecar", credentialSource: "personal", auth: "oauth", fields: [] }]
78006
+ });
78007
+ const status = result.connectors.find((c2) => !!c2 && typeof c2 === "object" && "name" in c2 && c2.name === "feishu");
78008
+ if (!status) throw new Error("\u4E2D\u5FC3\u672A\u8FD4\u56DE\u53D1\u5E03\u8005\u98DE\u4E66\u6388\u6743\u72B6\u6001");
78009
+ return { connected: status.status === "connected", ...status.authUrl ? { authUrl: status.authUrl } : {} };
78010
+ },
77960
78011
  testLoginPass: async () => {
77961
78012
  const loginPass = requireEnvironmentIdentity(environmentStores, "integration").loginPass;
77962
78013
  const credentials = await centralGetApiKey({ api: targetApi("integration"), loginPass });