@clawos-dev/clawd 0.2.742 → 0.2.744

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
@@ -6491,6 +6491,7 @@ var init_persona_schemas = __esm({
6491
6491
  gatewayUrl: external_exports.string().min(1),
6492
6492
  personaId: external_exports.string().min(1),
6493
6493
  publishedByMe: external_exports.boolean().optional(),
6494
+ collaboratedByMe: external_exports.boolean().optional(),
6494
6495
  /** Display name (persona.json label) registered on the gateway; the row's top-level label stays the gateway id. */
6495
6496
  label: external_exports.string().optional(),
6496
6497
  environment: DeployEnvironmentSchema.optional(),
@@ -6661,7 +6662,7 @@ function changedConfigKeys(a2, b3) {
6661
6662
  function repoSlug(url) {
6662
6663
  return url.trim().replace(/^git@[^:]+:/, "").replace(/^https?:\/\/[^/]+\//, "").replace(/\.git$/, "");
6663
6664
  }
6664
- var DeployFileSchema, SCAN_BASE_KINDS, ScanBaseKindSchema, DeployScanStatusSchema, DeployScanSchema, DeployReleasedSchema, DEPLOY_PROMOTION_PHASES, DeployPromotionSchema, DeployEngineChannelSchema, DeployEngineChannelsSchema, DeployScanFileSchema, DeployRepoSchema, DeploySecretPresenceSchema, ENGINE_AUTH_SECRETS, ENGINE_AUTH_PARTIAL_BLOCKER, DEPLOY_STAGE_STATUSES, DeployStageStatusSchema, DEPLOY_VERIFY_STAGE, DEPLOY_STAGE_KEYS, DeployStageKeySchema, DeployJobSchema, DeployRunSchema, CLOUD_ENGINES, DeployUnpublishRunSchema, ReservationStateSchema, DeployCloudStatusSchema, DeployStateSchema, DeployUnpublishResultSchema, DeployLocalStateSchema, DeployPatchResultSchema, DeploySecretsResultSchema, DEPLOY_WORKFLOW_VERSION, workflowVersionForConfigKeys, DEFAULT_TASK_CONCURRENCY, CONFIG_KEYS, DeployReleaseResultSchema, DeployPersonaArgsSchema, DeployGetArgsSchema, DeployScanArgsSchema, DeployReleaseArgsSchema, DeployConfirmTestArgsSchema, DeployPromoteArgsSchema, DeployApplyConfigArgsSchema, DeployOpenTestArgsSchema, DeployEnvironmentArgsSchema, DeployConfirmTestResultSchema, DeployPromoteResultSchema, DeployOpenTestResultSchema, DeployPatchArgsSchema, DeployPutSecretArgsSchema, DeploySetEngineAuthArgsSchema, DeployRerunJobArgsSchema, DEPLOY_STALE_BASE_ERROR, DeployReportFindingArgsSchema, DeployFinishScanArgsSchema, PERSONA_REPO_ORG;
6665
+ var DeployFileSchema, SCAN_BASE_KINDS, ScanBaseKindSchema, DeployScanStatusSchema, DeployScanSchema, DeployReleasedSchema, DEPLOY_PROMOTION_PHASES, DeployPromotionSchema, DeployEngineChannelSchema, DeployEngineChannelsSchema, DeployScanFileSchema, DeployRepoSchema, DeploySecretPresenceSchema, ENGINE_AUTH_SECRETS, ENGINE_AUTH_PARTIAL_BLOCKER, DEPLOY_STAGE_STATUSES, DeployStageStatusSchema, DEPLOY_VERIFY_STAGE, DEPLOY_STAGE_KEYS, DeployStageKeySchema, DeployJobSchema, DeployRunSchema, CLOUD_ENGINES, DeployUnpublishRunSchema, ReservationStateSchema, DeployCloudStatusSchema, DeployStateSchema, DeployUnpublishResultSchema, DeployLocalStateSchema, DeployPatchResultSchema, DeploySecretsResultSchema, DEPLOY_WORKFLOW_VERSION, workflowVersionForConfigKeys, DEFAULT_TASK_CONCURRENCY, CONFIG_KEYS, DeployReleaseResultSchema, DeployPersonaArgsSchema, DeployPersonaRoleSchema, DeployEditLockSchema, DeployRepoAccessSchema, DeployCollaboratorSchema, DeployAccessSchema, DeployCollaboratorAddResultSchema, DeployCollaboratorAddArgsSchema, DeployCollaboratorRemoveArgsSchema, DeployEditLockArgsSchema, DeployEditLockResultSchema, DeployTakeoverResultSchema, DeployGetArgsSchema, DeployScanArgsSchema, DeployReleaseArgsSchema, DeployConfirmTestArgsSchema, DeployPromoteArgsSchema, DeployApplyConfigArgsSchema, DeployOpenTestArgsSchema, DeployEnvironmentArgsSchema, DeployConfirmTestResultSchema, DeployPromoteResultSchema, DeployOpenTestResultSchema, DeployPatchArgsSchema, DeployPutSecretArgsSchema, DeploySetEngineAuthArgsSchema, DeployRerunJobArgsSchema, DEPLOY_STALE_BASE_ERROR, DeployReportFindingArgsSchema, DeployFinishScanArgsSchema, PERSONA_REPO_ORG;
6665
6666
  var init_deploy_schemas = __esm({
6666
6667
  "../protocol/src/deploy-schemas.ts"() {
6667
6668
  "use strict";
@@ -6885,6 +6886,38 @@ var init_deploy_schemas = __esm({
6885
6886
  DeployPersonaArgsSchema = external_exports.object({
6886
6887
  personaId: external_exports.string().min(1)
6887
6888
  }).strict();
6889
+ DeployPersonaRoleSchema = external_exports.enum(["owner", "editor", "admin"]);
6890
+ DeployEditLockSchema = external_exports.object({
6891
+ displayName: external_exports.string(),
6892
+ /** 持有人是不是本人 */
6893
+ mine: external_exports.boolean(),
6894
+ acquiredAt: external_exports.string(),
6895
+ expiresAt: external_exports.string()
6896
+ }).strict();
6897
+ DeployRepoAccessSchema = external_exports.enum(["invited", "active"]);
6898
+ DeployCollaboratorSchema = external_exports.object({
6899
+ unionId: external_exports.string(),
6900
+ displayName: external_exports.string(),
6901
+ githubLogin: external_exports.string().nullable(),
6902
+ createdAt: external_exports.string(),
6903
+ repoAccess: DeployRepoAccessSchema.optional()
6904
+ }).strict();
6905
+ DeployAccessSchema = external_exports.object({
6906
+ role: DeployPersonaRoleSchema,
6907
+ collaborators: external_exports.array(DeployCollaboratorSchema),
6908
+ /** 没人持有或租约已过期 = null */
6909
+ lock: DeployEditLockSchema.nullable(),
6910
+ /** 有协作者的 persona 才走锁 */
6911
+ lockRequired: external_exports.boolean()
6912
+ }).strict();
6913
+ DeployCollaboratorAddResultSchema = DeployAccessSchema.extend({
6914
+ production: external_exports.object({ ok: external_exports.boolean(), reason: external_exports.string().optional() }).strict()
6915
+ }).strict();
6916
+ DeployCollaboratorAddArgsSchema = DeployPersonaArgsSchema.extend({ deviceId: external_exports.string().min(1) }).strict();
6917
+ DeployCollaboratorRemoveArgsSchema = DeployPersonaArgsSchema.extend({ unionId: external_exports.string().min(1) }).strict();
6918
+ DeployEditLockArgsSchema = DeployPersonaArgsSchema.extend({ action: external_exports.enum(["acquire", "renew", "release"]) }).strict();
6919
+ DeployEditLockResultSchema = external_exports.object({ required: external_exports.boolean(), lock: DeployEditLockSchema.nullable() }).strict();
6920
+ DeployTakeoverResultSchema = external_exports.object({ personaId: external_exports.string().min(1) }).strict();
6888
6921
  DeployGetArgsSchema = DeployPersonaArgsSchema.extend({
6889
6922
  releaseHead: external_exports.string().min(1).optional(),
6890
6923
  releaseId: external_exports.string().min(1).optional(),
@@ -9393,6 +9426,30 @@ var init_methods = __esm({
9393
9426
  summary: "\u4E0B\u7EBF\uFF1A\u89E6\u53D1 persona \u4ED3\u5E93\u91CC\u7684 clawd-unpublish.yml\uFF08workflow_dispatch\uFF09\uFF0C\u7531\u6D41\u6C34\u7EBF\u5728 gateway \u64A4\u6389\u6B63\u5F0F\u767B\u8BB0\u2014\u2014\u522B\u4EBA\u770B\u4E0D\u5230\u3001\u7528\u4E0D\u4E86\uFF1B\u4ED3\u5E93 / \u955C\u50CF / \u672C\u673A\u4E0A\u4E91\u8BB0\u5F55\u90FD\u4E0D\u52A8\uFF0C\u91CD\u65B0\u53D1\u5E03\u5C31\u56DE\u6765\u3002\u6700\u540E\u4E00\u6B21\u53D1\u5E03\u7684 commit \u91CC\u6CA1\u6709\u8FD9\u4EFD workflow\uFF08\u65E7\u6A21\u677F\uFF09\u65F6\u62D2\uFF0C\u8981\u5148\u91CD\u65B0\u53D1\u5E03\u4E00\u6B21\u3002\u56DE { before }\uFF1A\u89E6\u53D1\u4E4B\u524D\u73B0\u67E5\u7684\u6700\u65B0\u4E0B\u7EBF run id\uFF08\u6CA1\u4E0B\u7EBF\u8FC7 = null\uFF09\uFF0C\u9875\u9762\u62FF\u5B83\u8BA4\u65B0 run\uFF1Bowner-only\u3001human-only",
9394
9427
  args: DeployEnvironmentArgsSchema
9395
9428
  },
9429
+ "deploy:access": {
9430
+ summary: "\u6211\u5728\u8FD9\u4E2A\u4E91 persona \u4E0A\u7684\u89D2\u8272\uFF08owner / editor / admin\uFF09\u3001\u534F\u4F5C\u8005\u540D\u5355\u3001\u5F53\u524D\u7F16\u8F91\u9501\uFF08\u8C01\u5728\u7F16\u8F91\uFF09\u3002\u8BFB\u6D4B\u8BD5\u73AF\u5883\u7684\u4E2D\u5FC3\uFF08\u6BCF\u4E2A persona \u90FD\u5148\u8FC7\u6D4B\u8BD5\u73AF\u5883\uFF09\uFF1B\u534F\u4F5C\u8005\u7684 daemon \u987A\u624B\u628A\u672C\u673A GitHub \u767B\u5F55\u540D\u62A5\u7ED9\u4E2D\u5FC3\uFF0Cowner \u51ED\u5B83\u9080\u8BF7\u8FDB\u4ED3\u5E93\u3002\u4E2D\u5FC3\u62D2\u7EDD\uFF08\u4E0D\u662F\u53D1\u5E03\u8005\u4E5F\u4E0D\u662F\u534F\u4F5C\u8005\uFF09= \u62A5\u9519\uFF1Bowner-only",
9431
+ args: DeployPersonaArgsSchema
9432
+ },
9433
+ "deploy:collaboratorAdd": {
9434
+ summary: "\u52A0\u534F\u4F5C\u8005\uFF08\u4EC5 persona \u7684\u53D1\u5E03\u8005\uFF09\uFF1A\u5165\u53C2\u662F\u8054\u7CFB\u4EBA\u7684 deviceId\uFF0C\u4E2D\u5FC3\u7ECF\u8BBE\u5907\u7ED1\u5B9A\u89E3\u6790\u98DE\u4E66\u8EAB\u4EFD\uFF0C\u5BF9\u65B9\u8FD8\u6CA1\u7ED1\u98DE\u4E66\u8D26\u53F7\u4F1A\u62D2\u3002\u4EE5\u6D4B\u8BD5\u73AF\u5883\u7684\u4E2D\u5FC3\u4E3A\u51C6\uFF0C\u6B63\u5F0F\u73AF\u5883\u7684\u4E2D\u5FC3\u518D\u767B\u8BB0\u4E00\u4EFD\uFF08\u6CA1\u767B\u8BB0\u6210\u53EA\u5728\u54CD\u5E94\u7684 production \u91CC\u63D0\u793A\uFF09\u3002\u56DE\u52A0\u5B8C\u4E4B\u540E\u7684 deploy:access \u89C6\u56FE\u52A0 production\uFF1Bowner-only\u3001human-only",
9435
+ args: DeployCollaboratorAddArgsSchema
9436
+ },
9437
+ "deploy:collaboratorRemove": {
9438
+ summary: "\u79FB\u9664\u534F\u4F5C\u8005\uFF08\u4EC5\u53D1\u5E03\u8005\uFF09\uFF1A\u6D4B\u8BD5\u3001\u6B63\u5F0F\u4E24\u4E2A\u4E2D\u5FC3\u90FD\u79FB\u9664\u5E76\u653E\u6389\u5BF9\u65B9\u7684\u7F16\u8F91\u9501\u3002GitHub \u4ED3\u5E93\u91CC\u7684\u534F\u4F5C\u8005\u8EAB\u4EFD\u4E0D\u81EA\u52A8\u64A4\uFF0C\u8981\u53BB\u4ED3\u5E93\u8BBE\u7F6E\u91CC\u79FB\u9664\u3002\u56DE deploy:access \u89C6\u56FE\uFF1Bowner-only\u3001human-only",
9439
+ args: DeployCollaboratorRemoveArgsSchema
9440
+ },
9441
+ "deploy:collaboratorSync": {
9442
+ summary: "\u4EC5\u53D1\u5E03\u8005\uFF1A\u628A\u5DF2\u4E0A\u62A5 GitHub \u767B\u5F55\u540D\u7684\u534F\u4F5C\u8005\u9010\u4E2A\u9080\u8BF7\u4E3A\u4ED3\u5E93\u534F\u4F5C\u8005\uFF08write\uFF0C\u5E42\u7B49\uFF09\u3002\u56DE deploy:access \u89C6\u56FE\uFF0C\u534F\u4F5C\u8005\u5E26 repoAccess\uFF1Ainvited = \u9080\u8BF7\u5DF2\u53D1\u3001\u7B49\u5BF9\u65B9\u63A5\u53D7\uFF1Bactive = \u5DF2\u80FD\u63A8\u9001\uFF1Bowner-only",
9443
+ args: DeployPersonaArgsSchema
9444
+ },
9445
+ "deploy:editLock": {
9446
+ summary: "\u7F16\u8F91\u9501\uFF08\u53EA\u6709\u5E26\u534F\u4F5C\u8005\u7684 persona \u624D\u8D70\uFF09\uFF1Aacquire \u53D6\u9501\uFF08\u672C\u4EBA\u6301\u6709\u5373\u7EED\u7EA6\uFF09\u3001renew \u5FC3\u8DF3\uFF08\u9875\u9762\u5F00\u7740\u6BCF 30 \u79D2\u4E00\u6B21\uFF0C90 \u79D2\u6CA1\u5FC3\u8DF3\u9501\u81EA\u52A8\u8FC7\u671F\uFF09\u3001release \u91CA\u653E\u3002\u522B\u4EBA\u6301\u6709\u65F6 acquire / renew \u62A5\u9519\uFF0C\u6587\u6848\u5E26\u6301\u6709\u4EBA\u59D3\u540D\u3002\u56DE { required, lock }\uFF1Bowner-only",
9447
+ args: DeployEditLockArgsSchema
9448
+ },
9449
+ "deploy:takeover": {
9450
+ summary: "\u534F\u4F5C\u8005\u63A5\u624B\uFF1A\u672C\u673A\u6CA1\u6709\u8FD9\u4E2A persona \u65F6\uFF0C\u7528\u672C\u673A GitHub \u8EAB\u4EFD\u628A ottin4ttc/<personaId> clone \u6210\u672C\u5730 persona \u76EE\u5F55\uFF0C\u4E4B\u540E\u6CBF\u7528 deploy:*\u3002\u672C\u673A\u5DF2\u6709\u540C\u540D persona \u5C31\u62D2\uFF1B\u8981\u5148\u5728\u4ED3\u5E93\u91CC\u88AB\u9080\u8BF7\u5E76\u63A5\u53D7\uFF1Bowner-only\u3001human-only",
9451
+ args: DeployPersonaArgsSchema
9452
+ },
9396
9453
  "githubAuth:get": {
9397
9454
  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",
9398
9455
  args: NO_ARGS
@@ -53728,7 +53785,8 @@ async function centralRequest(opts, path112, init) {
53728
53785
  let detail = "";
53729
53786
  try {
53730
53787
  const body = await res.json();
53731
- if (typeof body?.message === "string" && body.message) detail = `: ${body.message}`;
53788
+ const message = Array.isArray(body?.message) ? body.message.filter((m3) => typeof m3 === "string").join("\uFF1B") : body?.message;
53789
+ if (typeof message === "string" && message) detail = `: ${message}`;
53732
53790
  } catch {
53733
53791
  }
53734
53792
  throw new CentralClientError("API_ERROR", `central server HTTP ${res.status}${detail}`, void 0, res.status);
@@ -53918,7 +53976,36 @@ async function centralVisiblePersonas(opts) {
53918
53976
  if (typeof body.all !== "boolean" || !Array.isArray(body.personaIds) || !body.personaIds.every((p3) => typeof p3 === "string")) {
53919
53977
  throw new CentralClientError("BAD_RESPONSE", "personas/visible: expected { all: boolean, personaIds: string[] }");
53920
53978
  }
53921
- return { all: body.all, personaIds: body.personaIds };
53979
+ const ids = body.personaIds;
53980
+ const given = body.roles && typeof body.roles === "object" ? body.roles : void 0;
53981
+ const roles = {};
53982
+ for (const id of ids) roles[id] = given ? given[id] === "owner" ? "owner" : "editor" : "owner";
53983
+ return { all: body.all, personaIds: ids, roles };
53984
+ }
53985
+ var personaPath = (personaId2) => `/api/persona-cloud/personas/${encodeURIComponent(personaId2)}`;
53986
+ async function centralPersonaAccess(opts) {
53987
+ const body = await centralRequest(opts, `${personaPath(opts.personaId)}/access`, { method: "GET", bearer: opts.loginPass });
53988
+ if (body.role !== "owner" && body.role !== "editor" && body.role !== "admin" || !Array.isArray(body.collaborators) || typeof body.lockRequired !== "boolean" || body.lock !== null && (typeof body.lock !== "object" || typeof body.lock?.displayName !== "string")) {
53989
+ throw new CentralClientError("BAD_RESPONSE", "personas/access: unexpected shape");
53990
+ }
53991
+ return body;
53992
+ }
53993
+ async function centralAddCollaborator(opts) {
53994
+ await centralRequest(opts, `${personaPath(opts.personaId)}/collaborators`, { method: "PUT", bearer: opts.loginPass, body: { deviceId: opts.deviceId } });
53995
+ }
53996
+ async function centralRemoveCollaborator(opts) {
53997
+ await centralRequest(opts, `${personaPath(opts.personaId)}/collaborators/${encodeURIComponent(opts.unionId)}`, { method: "DELETE", bearer: opts.loginPass });
53998
+ }
53999
+ async function centralEditLock(opts) {
54000
+ const method = opts.action === "acquire" ? "POST" : opts.action === "renew" ? "PUT" : "DELETE";
54001
+ try {
54002
+ const body = await centralRequest(opts, `${personaPath(opts.personaId)}/edit-lock`, { method, bearer: opts.loginPass });
54003
+ if (opts.action === "release") return { held: true, required: true, lock: null };
54004
+ return { held: true, required: body.required === void 0 ? true : body.required === true, lock: body.lock ?? null };
54005
+ } catch (err) {
54006
+ if (err instanceof CentralClientError && err.status === 409) return { held: false, message: err.message.replace(/^central server HTTP 409:? ?/, "") || "\u522B\u4EBA\u6B63\u5728\u7F16\u8F91" };
54007
+ throw err;
54008
+ }
53922
54009
  }
53923
54010
  async function centralPersonaThreads(opts) {
53924
54011
  const f2 = opts.fetchImpl ?? globalThis.fetch;
@@ -57665,7 +57752,7 @@ function createA2aHost(deps) {
57665
57752
  key,
57666
57753
  versions: delivery.versions
57667
57754
  });
57668
- const mine = views.filter((v3) => acked.includes(v3.taskId));
57755
+ const mine = views.filter((v3) => acked.includes(v3.taskId) && !v3.line.handoff);
57669
57756
  if (mine.length) {
57670
57757
  const text = resultMessage(mine, (v3) => v3.targetDeviceId && v3.targetDeviceId !== deps.deviceId() ? `${v3.targetPersonaId}@${v3.targetDeviceId}` : v3.targetPersonaId);
57671
57758
  if (!deps.injectResult(sessionId, text)) {
@@ -58767,7 +58854,7 @@ var CloudApiKeyRequiredError = class extends Error {
58767
58854
  }
58768
58855
  };
58769
58856
  var CLOUD_PERSONA_ID_SUFFIX = "-cloud";
58770
- function cloudPersonaToFile(p3, publishedByMe = false) {
58857
+ function cloudPersonaToFile(p3, publishedByMe = false, collaboratedByMe = false) {
58771
58858
  const at3 = p3.registeredAt ?? 0;
58772
58859
  return {
58773
58860
  personaId: p3.personaId + CLOUD_PERSONA_ID_SUFFIX,
@@ -58778,7 +58865,7 @@ function cloudPersonaToFile(p3, publishedByMe = false) {
58778
58865
  public: false,
58779
58866
  createdAt: at3,
58780
58867
  updatedAt: at3,
58781
- cloud: { gatewayUrl: p3.gatewayUrl, personaId: p3.personaId, ...publishedByMe ? { publishedByMe: true } : {}, ...p3.label ? { label: p3.label } : {} }
58868
+ cloud: { gatewayUrl: p3.gatewayUrl, personaId: p3.personaId, ...publishedByMe ? { publishedByMe: true } : {}, ...collaboratedByMe ? { collaboratedByMe: true } : {}, ...p3.label ? { label: p3.label } : {} }
58782
58869
  };
58783
58870
  }
58784
58871
  var CloudGatewayClient = class {
@@ -63205,6 +63292,12 @@ function createGithubApi(args) {
63205
63292
  async dispatchWorkflow(slug, file, ref, inputs) {
63206
63293
  await must("POST", `/repos/${slug}/actions/workflows/${encodeURIComponent(file)}/dispatches`, "\u89E6\u53D1\u6D41\u6C34\u7EBF", { ref, ...inputs ? { inputs } : {} });
63207
63294
  },
63295
+ async addCollaborator(slug, login) {
63296
+ const r2 = await request("PUT", `/repos/${slug}/collaborators/${encodeURIComponent(login)}`, { permission: "push" });
63297
+ if (r2.status === 201) return "invited";
63298
+ if (r2.status === 204) return "active";
63299
+ throw new GithubApiError(r2.status, explain(r2.status, `\u9080\u8BF7 ${login} \u8FDB\u4ED3\u5E93`, r2.json));
63300
+ },
63208
63301
  async setDefaultBranch(slug, branch) {
63209
63302
  await must("PATCH", `/repos/${slug}`, "\u8BBE\u7F6E\u53D1\u5E03\u9ED8\u8BA4\u5206\u652F", { default_branch: branch });
63210
63303
  }
@@ -63940,6 +64033,25 @@ async function writeDockerfile(dir, personaId2, image) {
63940
64033
  const deployFile = await readDeployFileRaw(dir);
63941
64034
  if (deployFile !== void 0) await import_promises4.default.writeFile(import_node_path43.default.join(dir, INSTALL_FILE_PATH), renderInstallFile(deployFile));
63942
64035
  }
64036
+ async function cloneRepo(exec, args) {
64037
+ const { org, name, dir } = args;
64038
+ try {
64039
+ await import_promises4.default.stat(dir);
64040
+ throw new RepoError("\u672C\u673A\u5DF2\u7ECF\u6709\u8FD9\u4E2A\u76EE\u5F55\u4E86\uFF0C\u4E0D\u8986\u76D6");
64041
+ } catch (err) {
64042
+ if (err.code !== "ENOENT") throw err;
64043
+ }
64044
+ const parent = import_node_path43.default.dirname(dir);
64045
+ await import_promises4.default.mkdir(parent, { recursive: true });
64046
+ try {
64047
+ await networkGit(exec, parent, args.auth, ["clone", `https://github.com/${org}/${name}.git`, dir], "\u514B\u9686\u4ED3\u5E93");
64048
+ } catch (err) {
64049
+ if (err instanceof RepoError && /not found|404|Authentication failed|could not read/i.test(err.message)) {
64050
+ throw new RepoError(`\u514B\u9686\u4E0D\u4E86 ${org}/${name}\uFF1A\u4ED3\u5E93\u4E0D\u5B58\u5728\uFF0C\u6216\u8FD8\u6CA1\u6709\u8BBF\u95EE\u6743\u9650\u2014\u2014\u8BF7\u5148\u63A5\u53D7 owner \u53D1\u6765\u7684 GitHub \u9080\u8BF7\uFF08\u90AE\u7BB1\u6216 github.com/notifications\uFF09`);
64051
+ }
64052
+ throw err;
64053
+ }
64054
+ }
63943
64055
  var RepoAlreadyExistsError = class extends RepoError {
63944
64056
  constructor(slug) {
63945
64057
  super(`\u7EC4\u7EC7\u91CC\u5DF2\u7ECF\u6709 https://github.com/${slug} \u4E86`);
@@ -65399,6 +65511,7 @@ function buildDeployHandlers(deps) {
65399
65511
  checkPublisherTest();
65400
65512
  }
65401
65513
  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");
65514
+ await holdEditLock(cloudPersonaIdOf(repo2.url));
65402
65515
  const releaseId = (0, import_node_crypto17.randomUUID)();
65403
65516
  try {
65404
65517
  const result = await release(deps.exec, api, { dir, now: deps.now, auth, manifest, rescanOnRemoteChange: !rollback2, onPrepared: async (head) => {
@@ -65457,6 +65570,7 @@ function buildDeployHandlers(deps) {
65457
65570
  const dir = mustPersona(personaId2);
65458
65571
  const repo2 = await repoInfo(deps.exec, dir);
65459
65572
  if (!repo2) throw invalid("\u8FD9\u4E2A persona \u8FD8\u6CA1\u4E0A\u8FC7\u4E91");
65573
+ await assertNotEditor(cloudPersonaIdOf(repo2.url));
65460
65574
  const { api } = await requireGithub();
65461
65575
  const last = [...(await readHistory(dir)).releases].reverse().find((r2) => (r2.environment ?? "production") === environment);
65462
65576
  if (!last || !await fileInCommit(deps.exec, dir, last.head, UNPUBLISH_WORKFLOW_PATH)) throw invalid(UNPUBLISH_NEEDS_REPUBLISH);
@@ -65472,6 +65586,157 @@ function buildDeployHandlers(deps) {
65472
65586
  invalidateCloud(personaId2 + ":" + environment);
65473
65587
  return { response: { type: "deploy:unpublish", before: before?.runId ?? null } };
65474
65588
  };
65589
+ const centralMessage = (err) => err instanceof CentralClientError && err.code === "API_ERROR" ? err.message.replace(/^central server HTTP \d+:? ?/, "") || err.message : err instanceof Error ? err.message : String(err);
65590
+ const centralInvalid = (err) => invalid(centralMessage(err));
65591
+ const mustCollaboration = () => {
65592
+ if (!deps.collaboration) throw invalid("\u534F\u4F5C\u8005\u529F\u80FD\u7528\u4E0D\u4E86\uFF1A\u672C\u673A\u8FD8\u6CA1\u63A5\u4E0A\u4E2D\u5FC3");
65593
+ return deps.collaboration;
65594
+ };
65595
+ async function cloudIdOf(personaId2) {
65596
+ const repo2 = await repoInfo(deps.exec, mustPersona(personaId2));
65597
+ if (!repo2) throw invalid("\u8FD9\u4E2A persona \u8FD8\u6CA1\u4E0A\u8FC7\u4E91");
65598
+ return cloudPersonaIdOf(repo2.url);
65599
+ }
65600
+ const accessView = async (cloudId) => {
65601
+ try {
65602
+ return await mustCollaboration().access("integration", cloudId);
65603
+ } catch (err) {
65604
+ throw centralInvalid(err);
65605
+ }
65606
+ };
65607
+ async function holdEditLock(cloudId) {
65608
+ if (!deps.collaboration) return;
65609
+ let got;
65610
+ try {
65611
+ got = await deps.collaboration.editLock("integration", cloudId, "acquire");
65612
+ } catch (err) {
65613
+ throw centralInvalid(err);
65614
+ }
65615
+ if (!got.held) throw invalid(got.message);
65616
+ }
65617
+ async function assertProductionKnowsPersona(cloudId) {
65618
+ if (!deps.collaboration) return;
65619
+ let mine;
65620
+ try {
65621
+ mine = await deps.collaboration.access("integration", cloudId);
65622
+ } catch {
65623
+ return;
65624
+ }
65625
+ if (mine.role !== "editor") return;
65626
+ try {
65627
+ await deps.collaboration.access("production", cloudId);
65628
+ } catch (err) {
65629
+ if (err instanceof CentralClientError && err.status === 403) throw invalid("\u8FD9\u4E2A persona \u8FD8\u6CA1\u5728\u7EBF\u4E0A\u73AF\u5883\u767B\u8BB0\u534F\u4F5C\u8005\uFF1A\u8BF7 owner \u5148\u4E0A\u7EBF\u4E00\u6B21\uFF0C\u6216\u91CD\u65B0\u6DFB\u52A0\u4F60");
65630
+ throw centralInvalid(err);
65631
+ }
65632
+ }
65633
+ async function assertNotEditor(cloudId) {
65634
+ if (!deps.collaboration) return;
65635
+ let info;
65636
+ try {
65637
+ info = await deps.collaboration.access("integration", cloudId);
65638
+ } catch (err) {
65639
+ if (err instanceof CentralClientError && err.status === 403) return;
65640
+ throw centralInvalid(err);
65641
+ }
65642
+ if (info.role === "editor") throw invalid("\u4E0B\u7EBF\u53EA\u6709\u8FD9\u4E2A persona \u7684\u53D1\u5E03\u8005\u80FD\u505A");
65643
+ }
65644
+ const accessHandler = async (frame, _client, ctx) => {
65645
+ ensureOwner(ctx);
65646
+ const { personaId: personaId2 } = parseArgs2(DeployPersonaArgsSchema, frame, "deploy:access");
65647
+ const cloudId = await cloudIdOf(personaId2);
65648
+ const view2 = await accessView(cloudId);
65649
+ return { response: { type: "deploy:access", ...view2 } };
65650
+ };
65651
+ const collaboratorAdd = async (frame, _client, ctx) => {
65652
+ ensureOwner(ctx);
65653
+ const { personaId: personaId2, deviceId } = parseArgs2(DeployCollaboratorAddArgsSchema, frame, "deploy:collaboratorAdd");
65654
+ const cloudId = await cloudIdOf(personaId2);
65655
+ const collaboration = mustCollaboration();
65656
+ try {
65657
+ await collaboration.add("integration", cloudId, deviceId);
65658
+ } catch (err) {
65659
+ throw centralInvalid(err);
65660
+ }
65661
+ let production;
65662
+ try {
65663
+ await collaboration.add("production", cloudId, deviceId);
65664
+ production = { ok: true };
65665
+ } catch (err) {
65666
+ production = { ok: false, reason: centralMessage(err) };
65667
+ }
65668
+ return { response: { type: "deploy:collaboratorAdd", ...await accessView(cloudId), production } };
65669
+ };
65670
+ const collaboratorRemove = async (frame, _client, ctx) => {
65671
+ ensureOwner(ctx);
65672
+ const { personaId: personaId2, unionId } = parseArgs2(DeployCollaboratorRemoveArgsSchema, frame, "deploy:collaboratorRemove");
65673
+ const cloudId = await cloudIdOf(personaId2);
65674
+ const collaboration = mustCollaboration();
65675
+ try {
65676
+ await collaboration.remove("integration", cloudId, unionId);
65677
+ } catch (err) {
65678
+ throw centralInvalid(err);
65679
+ }
65680
+ await collaboration.remove("production", cloudId, unionId).catch((err) => deps.warn?.("remove collaborator from production failed", { cloudId, error: centralMessage(err) }));
65681
+ return { response: { type: "deploy:collaboratorRemove", ...await accessView(cloudId) } };
65682
+ };
65683
+ const collaboratorSync = async (frame, _client, ctx) => {
65684
+ ensureOwner(ctx);
65685
+ const { personaId: personaId2 } = parseArgs2(DeployPersonaArgsSchema, frame, "deploy:collaboratorSync");
65686
+ const dir = mustPersona(personaId2);
65687
+ const repo2 = await repoInfo(deps.exec, dir);
65688
+ if (!repo2) throw invalid("\u8FD9\u4E2A persona \u8FD8\u6CA1\u4E0A\u8FC7\u4E91");
65689
+ const view2 = await accessView(cloudPersonaIdOf(repo2.url));
65690
+ if (view2.role !== "owner") throw invalid("\u53EA\u6709\u8FD9\u4E2A persona \u7684\u53D1\u5E03\u8005\u80FD\u9080\u8BF7\u534F\u4F5C\u8005\u8FDB\u4ED3\u5E93");
65691
+ const { api } = await requireGithub();
65692
+ const slug = repoSlug(repo2.url);
65693
+ const collaborators = [];
65694
+ for (const c2 of view2.collaborators) {
65695
+ if (!c2.githubLogin) {
65696
+ collaborators.push(c2);
65697
+ continue;
65698
+ }
65699
+ try {
65700
+ collaborators.push({ ...c2, repoAccess: await api.addCollaborator(slug, c2.githubLogin) });
65701
+ } catch (err) {
65702
+ throw toClawdError(err);
65703
+ }
65704
+ }
65705
+ return { response: { type: "deploy:collaboratorSync", ...view2, collaborators } };
65706
+ };
65707
+ const editLockHandler = async (frame, _client, ctx) => {
65708
+ ensureOwner(ctx);
65709
+ const { personaId: personaId2, action } = parseArgs2(DeployEditLockArgsSchema, frame, "deploy:editLock");
65710
+ const cloudId = await cloudIdOf(personaId2);
65711
+ let got;
65712
+ try {
65713
+ got = await mustCollaboration().editLock("integration", cloudId, action);
65714
+ } catch (err) {
65715
+ throw centralInvalid(err);
65716
+ }
65717
+ if (!got.held) throw invalid(got.message);
65718
+ return { response: { type: "deploy:editLock", required: got.required, lock: got.lock } };
65719
+ };
65720
+ const takeover = async (frame, _client, ctx) => {
65721
+ ensureOwner(ctx);
65722
+ const { personaId: personaId2 } = parseArgs2(DeployPersonaArgsSchema, frame, "deploy:takeover");
65723
+ if (deps.hasPersona(personaId2)) throw invalid("\u672C\u673A\u5DF2\u7ECF\u6709\u8FD9\u4E2A persona \u4E86");
65724
+ if (!/^[a-z0-9-]{1,100}$/.test(personaId2)) throw invalid("persona \u540D\u53EA\u8BA4\u5C0F\u5199\u5B57\u6BCD\u3001\u6570\u5B57\u548C\u8FDE\u5B57\u7B26");
65725
+ try {
65726
+ await mustCollaboration().access("integration", personaId2);
65727
+ } catch (err) {
65728
+ if (err instanceof CentralClientError && err.status === 403) throw invalid("\u4F60\u4E0D\u662F\u8FD9\u4E2A persona \u7684\u534F\u4F5C\u8005\uFF1A\u8BF7 owner \u5148\u628A\u4F60\u52A0\u4E3A\u534F\u4F5C\u8005");
65729
+ throw centralInvalid(err);
65730
+ }
65731
+ const { auth } = await requireGithub();
65732
+ try {
65733
+ await cloneRepo(deps.exec, { org: deps.org, name: personaId2, dir: deps.personaDir(personaId2), auth });
65734
+ } catch (err) {
65735
+ throw toClawdError(err);
65736
+ }
65737
+ deps.onPersonaCloned?.(personaId2);
65738
+ return { response: { type: "deploy:takeover", personaId: personaId2 } };
65739
+ };
65475
65740
  function releaseArgs(frame, schema) {
65476
65741
  const { type: _type, requestId: _requestId, ...args } = frame;
65477
65742
  return parseArgs2({ safeParse: schema.safeParse.bind(schema) }, args, String(frame.type));
@@ -65547,6 +65812,8 @@ function buildDeployHandlers(deps) {
65547
65812
  if (!recovering) await assertConfigTested(personaId2, record4);
65548
65813
  const repo2 = await repoInfo(deps.exec, mustPersona(personaId2));
65549
65814
  if (!repo2) throw invalid("\u4ED3\u5E93\u4E0D\u5B58\u5728");
65815
+ await holdEditLock(cloudPersonaIdOf(repo2.url));
65816
+ await assertProductionKnowsPersona(cloudPersonaIdOf(repo2.url));
65550
65817
  if (await deps.claimPublisher(cloudPersonaIdOf(repo2.url), "production") === "taken") throw invalid("\u7EBF\u4E0A\u73AF\u5883\u91CC\u7684\u8FD9\u4E2A\u540D\u5B57\u5DF2\u7531\u522B\u4EBA\u53D1\u5E03");
65551
65818
  const { api } = await requireGithub();
65552
65819
  const names = await targetSecrets(personaId2, mustPersona(personaId2), api, "production");
@@ -65701,7 +65968,13 @@ function buildDeployHandlers(deps) {
65701
65968
  "deploy:finishScan": serial(finishScan),
65702
65969
  "deploy:release": serial(releaseHandler),
65703
65970
  "deploy:rerunJob": serial(rerunJobHandler),
65704
- "deploy:unpublish": serial(unpublishHandler)
65971
+ "deploy:unpublish": serial(unpublishHandler),
65972
+ "deploy:access": accessHandler,
65973
+ "deploy:collaboratorAdd": serial(collaboratorAdd),
65974
+ "deploy:collaboratorRemove": serial(collaboratorRemove),
65975
+ "deploy:collaboratorSync": serial(collaboratorSync),
65976
+ "deploy:editLock": editLockHandler,
65977
+ "deploy:takeover": serial(takeover)
65705
65978
  };
65706
65979
  }
65707
65980
 
@@ -75896,6 +76169,12 @@ var METHOD_GRANT_MAP = {
75896
76169
  "deploy:openTest": ADMIN_ANY,
75897
76170
  "deploy:rerunJob": ADMIN_ANY,
75898
76171
  "deploy:unpublish": ADMIN_ANY,
76172
+ "deploy:access": ADMIN_ANY,
76173
+ "deploy:collaboratorAdd": ADMIN_ANY,
76174
+ "deploy:collaboratorRemove": ADMIN_ANY,
76175
+ "deploy:collaboratorSync": ADMIN_ANY,
76176
+ "deploy:editLock": ADMIN_ANY,
76177
+ "deploy:takeover": ADMIN_ANY,
75899
76178
  // Connector handlers use the owner login to fetch or fill credentials; exclude guests.
75900
76179
  "cloud:connectors:list": ADMIN_ANY,
75901
76180
  "dashboard:openUrl": ADMIN_ANY,
@@ -76076,6 +76355,10 @@ var HUMAN_ONLY_METHODS = [
76076
76355
  "deploy:setEngineAuth",
76077
76356
  "deploy:rerunJob",
76078
76357
  "deploy:unpublish",
76358
+ "deploy:collaboratorAdd",
76359
+ "deploy:collaboratorRemove",
76360
+ "deploy:collaboratorSync",
76361
+ "deploy:takeover",
76079
76362
  "githubAuth:oauthStart",
76080
76363
  "githubAuth:authorize"
76081
76364
  ];
@@ -76120,7 +76403,7 @@ function computeMethodAccess(args) {
76120
76403
  }
76121
76404
 
76122
76405
  // src/version.ts
76123
- var version = "0.2.742".length > 0 ? "0.2.742" : "dev";
76406
+ var version = "0.2.744".length > 0 ? "0.2.744" : "dev";
76124
76407
 
76125
76408
  // src/cli-probe/probe.ts
76126
76409
  var fs87 = __toESM(require("fs"), 1);
@@ -79475,15 +79758,16 @@ async function startDaemon(config) {
79475
79758
  const list = await cloudGateway.listPersonas();
79476
79759
  const loginPass = ownerIdentityStore.read()?.loginPass;
79477
79760
  const publishedIds = /* @__PURE__ */ new Set();
79761
+ const collaboratedIds = /* @__PURE__ */ new Set();
79478
79762
  if (loginPass) {
79479
79763
  try {
79480
79764
  const visible = await centralVisiblePersonas({ api: config.clawosApi ?? DEFAULT_CLAWOS_API, loginPass });
79481
- for (const id of visible.personaIds) publishedIds.add(id);
79765
+ for (const id of visible.personaIds) (visible.roles[id] === "editor" ? collaboratedIds : publishedIds).add(id);
79482
79766
  } catch (err) {
79483
79767
  logger.warn("cloud bot publisher lookup failed; binding controls hidden", { reason: err.message });
79484
79768
  }
79485
79769
  }
79486
- const shadowed = personaRegistry.setCloudSnapshot(list.map((p3) => cloudPersonaToFile(p3, publishedIds.has(p3.personaId))));
79770
+ const shadowed = personaRegistry.setCloudSnapshot(list.map((p3) => cloudPersonaToFile(p3, publishedIds.has(p3.personaId), collaboratedIds.has(p3.personaId) && !personaStore.has(p3.personaId))));
79487
79771
  if (shadowed.length > 0) {
79488
79772
  logger.warn("cloud personas shadowed by local personas with the same id", { personaIds: shadowed });
79489
79773
  }
@@ -80055,6 +80339,23 @@ async function startDaemon(config) {
80055
80339
  throw new Error(`\u5411\u4E2D\u5FC3\u767B\u8BB0\u53D1\u5E03\u8005\u5931\u8D25\uFF1A${err.message}`);
80056
80340
  }
80057
80341
  },
80342
+ collaboration: /* @__PURE__ */ (() => {
80343
+ const base = (environment) => {
80344
+ const loginPass = targetLoginPass(environment);
80345
+ if (!loginPass) throw new Error("\u8FD8\u6CA1\u767B\u5F55 clawd\uFF1A\u5148\u5728\u8BBE\u7F6E\u91CC\u767B\u5F55\u98DE\u4E66");
80346
+ return { api: targetApi(environment), loginPass };
80347
+ };
80348
+ return {
80349
+ access: async (environment, personaId2) => centralPersonaAccess({ ...base(environment), personaId: personaId2 }),
80350
+ add: async (environment, personaId2, deviceId) => centralAddCollaborator({ ...base(environment), personaId: personaId2, deviceId }),
80351
+ remove: async (environment, personaId2, unionId) => centralRemoveCollaborator({ ...base(environment), personaId: personaId2, unionId }),
80352
+ editLock: async (environment, personaId2, action) => centralEditLock({ ...base(environment), personaId: personaId2, action })
80353
+ };
80354
+ })(),
80355
+ onPersonaCloned: () => {
80356
+ personaRegistry.reload();
80357
+ void refreshCloudPersonas();
80358
+ },
80058
80359
  now: () => Date.now(),
80059
80360
  warn: (msg, meta) => logger.warn(msg, meta),
80060
80361
  logger,