@clawos-dev/clawd 0.2.653 → 0.2.655
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 +67 -30
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -57093,6 +57093,7 @@ function buildShiftInternalHandlers(deps) {
|
|
|
57093
57093
|
|
|
57094
57094
|
// src/handlers/deploy.ts
|
|
57095
57095
|
var import_node_crypto14 = require("crypto");
|
|
57096
|
+
var import_node_perf_hooks = require("perf_hooks");
|
|
57096
57097
|
|
|
57097
57098
|
// src/deploy/persona-test.ts
|
|
57098
57099
|
var import_node_fs33 = __toESM(require("fs"), 1);
|
|
@@ -62855,6 +62856,7 @@ function reconcileInterruptedScans(deps) {
|
|
|
62855
62856
|
var SECRETS_CACHE_MS = 2e4;
|
|
62856
62857
|
var RUN_CACHE_MS = 4e3;
|
|
62857
62858
|
var ONLINE_CACHE_MS = 2e4;
|
|
62859
|
+
var directRead = async (_stage, action) => action();
|
|
62858
62860
|
var UNPUBLISH_NEEDS_REPUBLISH = "\u8FD9\u4E2A persona \u7684\u6D41\u6C34\u7EBF\u662F\u65E7\u7248\u7684\uFF0C\u5148\u91CD\u65B0\u626B\u63CF\u3001\u53D1\u5E03\u4E00\u6B21\u518D\u4E0B\u7EBF";
|
|
62859
62861
|
function ensureOwner(ctx) {
|
|
62860
62862
|
if (!ctx || ctx.principal.kind !== "owner") {
|
|
@@ -62995,12 +62997,12 @@ function buildDeployHandlers(deps) {
|
|
|
62995
62997
|
for (const env of ["integration", "production"]) secretsCache.delete(personaId2 + ":" + env);
|
|
62996
62998
|
secretsCache.delete(personaId2);
|
|
62997
62999
|
}
|
|
62998
|
-
async function stateOf(personaId2, selection = {}) {
|
|
63000
|
+
async function stateOf(personaId2, selection = {}, measure = directRead) {
|
|
62999
63001
|
const { releaseHead, releaseId, environment } = selection;
|
|
63000
63002
|
const historical = releaseHead !== void 0 || releaseId !== void 0;
|
|
63001
63003
|
const dir = deps.personaDir(personaId2);
|
|
63002
|
-
const repo2 = await repoInfo(deps.exec, dir);
|
|
63003
|
-
const github = await deps.githubIdentity.status();
|
|
63004
|
+
const repo2 = await measure("repo", () => repoInfo(deps.exec, dir));
|
|
63005
|
+
const github = await measure("github.identity", () => deps.githubIdentity.status());
|
|
63004
63006
|
const auth = deps.githubIdentity.cached();
|
|
63005
63007
|
const rawApi = auth ? deps.githubApi(auth.token) : void 0;
|
|
63006
63008
|
const requests = /* @__PURE__ */ new Map();
|
|
@@ -63008,11 +63010,18 @@ function buildDeployHandlers(deps) {
|
|
|
63008
63010
|
const key = (strict ? "strict:" : "") + url;
|
|
63009
63011
|
const hit = requests.get(key);
|
|
63010
63012
|
if (hit) return hit;
|
|
63011
|
-
const
|
|
63013
|
+
const resource = url.includes("clawd-unpublish.yml") ? "unpublish" : url.includes("/artifacts") ? "artifacts" : url.includes("/jobs") ? "jobs" : url.includes("/runs?") ? "runs" : url.includes("/runs/") ? "run" : "other";
|
|
63014
|
+
const promise = measure("github.read", () => strict ? rawApi.getJsonStrict(url) : rawApi.getJson(url), { resource });
|
|
63012
63015
|
requests.set(key, promise);
|
|
63013
63016
|
return promise;
|
|
63014
63017
|
};
|
|
63015
|
-
const api = rawApi ? {
|
|
63018
|
+
const api = rawApi ? {
|
|
63019
|
+
...rawApi,
|
|
63020
|
+
getJson: (url) => query(url),
|
|
63021
|
+
getJsonStrict: (url) => query(url, true),
|
|
63022
|
+
listSecrets: (slug, environment2) => measure("github.secrets", () => rawApi.listSecrets(slug, environment2), { environment: environment2, resource: environment2 ? "environment" : "repository" }),
|
|
63023
|
+
downloadArtifact: (slug, artifactId) => measure("github.artifact-download", () => rawApi.downloadArtifact(slug, artifactId))
|
|
63024
|
+
} : void 0;
|
|
63016
63025
|
const snapshots = /* @__PURE__ */ new Map();
|
|
63017
63026
|
const observe = (environment2) => {
|
|
63018
63027
|
let promise = snapshots.get(environment2);
|
|
@@ -63025,11 +63034,11 @@ function buildDeployHandlers(deps) {
|
|
|
63025
63034
|
let file = emptyScanFile();
|
|
63026
63035
|
let historyError;
|
|
63027
63036
|
try {
|
|
63028
|
-
file = await readHistory(dir);
|
|
63037
|
+
file = await measure("history", () => readHistory(dir));
|
|
63029
63038
|
} catch (err) {
|
|
63030
63039
|
historyError = err instanceof Error ? err.message : String(err);
|
|
63031
63040
|
}
|
|
63032
|
-
file = await settleStale(personaId2, dir, file);
|
|
63041
|
+
file = await measure("history.settle", () => settleStale(personaId2, dir, file));
|
|
63033
63042
|
const pending = file.promotions.at(-1);
|
|
63034
63043
|
const bound = pending && file.releases.find((r2) => r2.id === pending.releaseId);
|
|
63035
63044
|
if (!historical && pending && !pending.verified && !["completed", "failed"].includes(pending.phase) && bound?.verified && bound.confirmation?.fingerprint === bound.verified.verification.fingerprint) {
|
|
@@ -63043,8 +63052,8 @@ function buildDeployHandlers(deps) {
|
|
|
63043
63052
|
let unpublish;
|
|
63044
63053
|
if (repo2 && api && github.status === "authorized") {
|
|
63045
63054
|
const legacy = historical ? selected : latestProductionRelease(file.releases);
|
|
63046
|
-
run = legacy?.legacy ? await cachedRun(personaId2, api, repoSlug(repo2.url), legacy.head) : void 0;
|
|
63047
|
-
if (!historical) unpublish = await cachedUnpublish(personaId2, api, repoSlug(repo2.url));
|
|
63055
|
+
run = legacy?.legacy ? await measure("run", () => cachedRun(personaId2, api, repoSlug(repo2.url), legacy.head), { environment: legacy.environment }) : void 0;
|
|
63056
|
+
if (!historical) unpublish = await measure("unpublish", () => cachedUnpublish(personaId2, api, repoSlug(repo2.url)), { environment: "production" });
|
|
63048
63057
|
}
|
|
63049
63058
|
let online;
|
|
63050
63059
|
const targets = {};
|
|
@@ -63053,13 +63062,14 @@ function buildDeployHandlers(deps) {
|
|
|
63053
63062
|
const integration = historical ? selected?.environment === "integration" ? selected : void 0 : [...file.releases].reverse().find((r2) => r2.environment === "integration");
|
|
63054
63063
|
let intRun;
|
|
63055
63064
|
if (integration) {
|
|
63056
|
-
intRun = integration.runId ?
|
|
63065
|
+
intRun = await measure("run", async () => integration.runId ? runById(api, slug, integration.runId, "integration") : await findReleaseRun(api, slug, { environment: "integration", head: integration.head }) ?? void 0, { environment: "integration" });
|
|
63057
63066
|
if (intRun && intRun.headSha === integration.head) {
|
|
63058
63067
|
let updated = { ...integration, runId: intRun.runId, runAttempt: intRun.runAttempt };
|
|
63059
63068
|
if (integration.runAttempt !== intRun.runAttempt) updated = { ...updated, verified: void 0, artifactId: void 0, confirmation: void 0 };
|
|
63060
63069
|
if (intRun.status === "completed" && intRun.conclusion === "success" && !updated.verified) {
|
|
63061
63070
|
try {
|
|
63062
|
-
const
|
|
63071
|
+
const verifiedRun = intRun;
|
|
63072
|
+
const result = await measure("artifact", () => readVerifiedRelease(api, slug, verifiedRun));
|
|
63063
63073
|
updated = { ...updated, artifactId: result.artifactId, verified: result.release };
|
|
63064
63074
|
} catch {
|
|
63065
63075
|
}
|
|
@@ -63073,27 +63083,27 @@ function buildDeployHandlers(deps) {
|
|
|
63073
63083
|
const promotion = file.promotions.at(-1);
|
|
63074
63084
|
const source = promotion && file.releases.find((r2) => r2.id === promotion.releaseId);
|
|
63075
63085
|
if (!historical && promotion?.verified && source && ["dispatched", "dispatching"].includes(promotion.phase)) {
|
|
63076
|
-
await continuePromotion(personaId2, { ...source, verified: promotion.verified }, promotion, false, api, async () => (await observe("production")).image);
|
|
63086
|
+
await measure("promotion", () => continuePromotion(personaId2, { ...source, verified: promotion.verified }, promotion, false, api, async () => (await observe("production")).image));
|
|
63077
63087
|
file = await readHistory(dir);
|
|
63078
63088
|
}
|
|
63079
63089
|
const latestPromotion = file.promotions.at(-1);
|
|
63080
63090
|
let prodRun;
|
|
63081
63091
|
if (historical && selected?.environment === "production" && !selected.legacy && selected.runId) {
|
|
63082
|
-
prodRun = await runById(api, slug, selected.runId, "production", file.promotions.find((p3) => p3.id + "-production" === selected.id)?.id);
|
|
63092
|
+
prodRun = await measure("run", () => runById(api, slug, selected.runId, "production", file.promotions.find((p3) => p3.id + "-production" === selected.id)?.id), { environment: "production" });
|
|
63083
63093
|
if (prodRun?.headSha !== selected.head) prodRun = void 0;
|
|
63084
63094
|
} else if (!historical && latestPromotion) {
|
|
63085
63095
|
const release2 = file.releases.find((r2) => r2.id === latestPromotion.releaseId);
|
|
63086
|
-
if (release2) prodRun = await findReleaseRun(api, slug, { environment: "production", head: release2.head, promotionId: latestPromotion.id }) ?? void 0;
|
|
63096
|
+
if (release2) prodRun = await measure("run", async () => await findReleaseRun(api, slug, { environment: "production", head: release2.head, promotionId: latestPromotion.id }) ?? void 0, { environment: "production" });
|
|
63087
63097
|
}
|
|
63088
63098
|
const loadTarget = async (environment2, targetRun) => {
|
|
63089
63099
|
if (historical) return targetRun ? { run: targetRun } : {};
|
|
63090
63100
|
let targetSecretsList;
|
|
63091
63101
|
try {
|
|
63092
|
-
targetSecretsList = await targetSecrets(personaId2, dir, api, environment2);
|
|
63102
|
+
targetSecretsList = await measure("secrets", () => targetSecrets(personaId2, dir, api, environment2), { environment: environment2 });
|
|
63093
63103
|
} catch {
|
|
63094
63104
|
}
|
|
63095
|
-
const targetUnpublish = await cachedUnpublish(personaId2, api, slug, environment2);
|
|
63096
|
-
const cloud = file.releases.some((r2) => r2.environment === environment2) || environment2 === "production" && latestPromotion ? await cachedCloud(personaId2, environment2, targetRun, targetUnpublish, cloudName, () => observe(environment2), snapshots.has(environment2)) : {};
|
|
63105
|
+
const targetUnpublish = await measure("unpublish", () => cachedUnpublish(personaId2, api, slug, environment2), { environment: environment2 });
|
|
63106
|
+
const cloud = file.releases.some((r2) => r2.environment === environment2) || environment2 === "production" && latestPromotion ? await measure("cloud", () => cachedCloud(personaId2, environment2, targetRun, targetUnpublish, cloudName, () => observe(environment2), snapshots.has(environment2)), { environment: environment2 }) : {};
|
|
63097
63107
|
const { image, online: targetOnline } = cloud;
|
|
63098
63108
|
return { ...targetUnpublish ? { unpublish: targetUnpublish } : {}, ...targetRun ? { run: targetRun } : {}, ...targetSecretsList ? { secrets: targetSecretsList } : {}, ...image ? { image } : {}, ...targetOnline !== void 0 ? { online: targetOnline } : {} };
|
|
63099
63109
|
};
|
|
@@ -63107,12 +63117,12 @@ function buildDeployHandlers(deps) {
|
|
|
63107
63117
|
}
|
|
63108
63118
|
run = historical ? selected?.environment === "integration" ? intRun : prodRun ?? run : prodTarget.run;
|
|
63109
63119
|
}
|
|
63110
|
-
if (!historical && latestProductionRelease(file.releases) && (!api || github.status !== "authorized")) online = (await cachedCloud(personaId2, "production", run, unpublish, repo2 ? cloudPersonaIdOf(repo2.url) : personaId2)).online;
|
|
63120
|
+
if (!historical && latestProductionRelease(file.releases) && (!api || github.status !== "authorized")) online = (await measure("cloud", () => cachedCloud(personaId2, "production", run, unpublish, repo2 ? cloudPersonaIdOf(repo2.url) : personaId2), { environment: "production" })).online;
|
|
63111
63121
|
let publisherFeishuAuth;
|
|
63112
63122
|
if (scans.at(-1)?.manifest.connectors.some(isFeishuPublisherConnector)) {
|
|
63113
63123
|
const query2 = async (environment2) => {
|
|
63114
63124
|
try {
|
|
63115
|
-
return await deps.publisherFeishuAuth(repo2 ? cloudPersonaIdOf(repo2.url) : personaId2, environment2);
|
|
63125
|
+
return await measure("feishu.auth", () => deps.publisherFeishuAuth(repo2 ? cloudPersonaIdOf(repo2.url) : personaId2, environment2), { environment: environment2 });
|
|
63116
63126
|
} catch (e) {
|
|
63117
63127
|
return { connected: false, error: e instanceof Error ? e.message : "\u98DE\u4E66\u6388\u6743\u72B6\u6001\u67E5\u8BE2\u5931\u8D25" };
|
|
63118
63128
|
}
|
|
@@ -63136,8 +63146,8 @@ function buildDeployHandlers(deps) {
|
|
|
63136
63146
|
...historyError ? { historyError } : {}
|
|
63137
63147
|
};
|
|
63138
63148
|
}
|
|
63139
|
-
const view = async (personaId2, selection) => ({
|
|
63140
|
-
response: { type: "deploy:state", ...await stateOf(personaId2, selection) }
|
|
63149
|
+
const view = async (personaId2, selection, measure) => ({
|
|
63150
|
+
response: { type: "deploy:state", ...await stateOf(personaId2, selection, measure) }
|
|
63141
63151
|
});
|
|
63142
63152
|
async function beginScan(personaId2, dir, file, contentHead, want) {
|
|
63143
63153
|
liveScans.set(personaId2, {});
|
|
@@ -63197,12 +63207,6 @@ function buildDeployHandlers(deps) {
|
|
|
63197
63207
|
}));
|
|
63198
63208
|
}
|
|
63199
63209
|
}
|
|
63200
|
-
const get = async (frame, _client, ctx) => {
|
|
63201
|
-
ensureOwner(ctx);
|
|
63202
|
-
const { personaId: personaId2, ...selection } = parseArgs2(DeployGetArgsSchema, frame, "deploy:get");
|
|
63203
|
-
mustPersona(personaId2);
|
|
63204
|
-
return view(personaId2, selection);
|
|
63205
|
-
};
|
|
63206
63210
|
const patch = async (frame, _client, ctx) => {
|
|
63207
63211
|
ensureOwner(ctx);
|
|
63208
63212
|
const args = parseArgs2(DeployPatchArgsSchema, frame, "deploy:patch");
|
|
@@ -63652,14 +63656,46 @@ function buildDeployHandlers(deps) {
|
|
|
63652
63656
|
return { response: { type: "deploy:openTest", personaId: id, expectedImage: record4.verified.image } };
|
|
63653
63657
|
};
|
|
63654
63658
|
const getInFlight = /* @__PURE__ */ new Map();
|
|
63655
|
-
const serialGet = serial(get);
|
|
63656
63659
|
const coalescedGet = async (frame, client, ctx) => {
|
|
63657
63660
|
ensureOwner(ctx);
|
|
63658
63661
|
const args = parseArgs2(DeployGetArgsSchema, frame, "deploy:get");
|
|
63659
63662
|
const key = JSON.stringify(args);
|
|
63660
63663
|
const hit = getInFlight.get(key);
|
|
63661
63664
|
if (hit) return hit;
|
|
63662
|
-
const
|
|
63665
|
+
const started = import_node_perf_hooks.performance.now();
|
|
63666
|
+
const traceId = (0, import_node_crypto14.randomUUID)();
|
|
63667
|
+
const log = (message, meta) => {
|
|
63668
|
+
try {
|
|
63669
|
+
deps.logger?.info(message, { personaId: args.personaId, traceId, ...meta });
|
|
63670
|
+
} catch {
|
|
63671
|
+
}
|
|
63672
|
+
};
|
|
63673
|
+
const measure = async (stage, action, details) => {
|
|
63674
|
+
const stageStarted = stage === "total" ? started : import_node_perf_hooks.performance.now();
|
|
63675
|
+
let status = "error";
|
|
63676
|
+
try {
|
|
63677
|
+
const result = await action();
|
|
63678
|
+
status = "ok";
|
|
63679
|
+
return result;
|
|
63680
|
+
} finally {
|
|
63681
|
+
const finished = import_node_perf_hooks.performance.now();
|
|
63682
|
+
log(stage === "total" ? "deploy.get.finished" : "deploy.get.stage", {
|
|
63683
|
+
stage,
|
|
63684
|
+
status,
|
|
63685
|
+
...details,
|
|
63686
|
+
durationMs: Math.round((finished - stageStarted) * 100) / 100,
|
|
63687
|
+
elapsedMs: Math.round((finished - started) * 100) / 100
|
|
63688
|
+
});
|
|
63689
|
+
}
|
|
63690
|
+
};
|
|
63691
|
+
log("deploy.get.started", { historical: args.releaseHead !== void 0 || args.releaseId !== void 0, ...args.environment ? { environment: args.environment } : {} });
|
|
63692
|
+
const promise = measure("total", () => enqueue(args.personaId, async () => {
|
|
63693
|
+
const durationMs = Math.round((import_node_perf_hooks.performance.now() - started) * 100) / 100;
|
|
63694
|
+
log("deploy.get.stage", { stage: "queue", status: "ok", durationMs, elapsedMs: durationMs });
|
|
63695
|
+
const { personaId: personaId2, ...selection } = args;
|
|
63696
|
+
mustPersona(personaId2);
|
|
63697
|
+
return view(personaId2, selection, measure);
|
|
63698
|
+
}));
|
|
63663
63699
|
getInFlight.set(key, promise);
|
|
63664
63700
|
void promise.finally(() => {
|
|
63665
63701
|
if (getInFlight.get(key) === promise) getInFlight.delete(key);
|
|
@@ -74493,7 +74529,7 @@ function computeMethodAccess(args) {
|
|
|
74493
74529
|
}
|
|
74494
74530
|
|
|
74495
74531
|
// src/version.ts
|
|
74496
|
-
var version = "0.2.
|
|
74532
|
+
var version = "0.2.655".length > 0 ? "0.2.655" : "dev";
|
|
74497
74533
|
|
|
74498
74534
|
// src/cli-probe/probe.ts
|
|
74499
74535
|
var fs85 = __toESM(require("fs"), 1);
|
|
@@ -78146,6 +78182,7 @@ async function startDaemon(config) {
|
|
|
78146
78182
|
},
|
|
78147
78183
|
now: () => Date.now(),
|
|
78148
78184
|
warn: (msg, meta) => logger.warn(msg, meta),
|
|
78185
|
+
logger,
|
|
78149
78186
|
// 在不在线上 = gateway 正式登记表里有没有它(design 2026-09-24-deploy-unpublish §3.3):跟 persona 列表的云卡片同一个来源。
|
|
78150
78187
|
// 拉不到(没登录飞书 / gateway 不通 / healthz 5s 没回)→ undefined,页面不给下线按钮也不撤「线上」标。
|
|
78151
78188
|
// 失败上界:中心那一跳自带 15s,healthz 这一趟 5s——deploy:get 要保证页面打得开,gateway 接了连接不回时不能挂到 undici 默认的 300s
|