@clawos-dev/clawd 0.2.583 → 0.2.585

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.
@@ -21787,6 +21787,10 @@ var DeployPutSecretArgsSchema = external_exports.object({
21787
21787
  // 环境变量名 → 值。key 必须是该 secret 声明过的 env 之一,handler 校验。
21788
21788
  values: external_exports.record(external_exports.string(), external_exports.string())
21789
21789
  }).strict();
21790
+ var DeploySetEngineAuthArgsSchema = external_exports.object({
21791
+ personaId: external_exports.string().min(1),
21792
+ auth: external_exports.object({ baseUrl: external_exports.string(), apiKey: external_exports.string() }).strict().nullable()
21793
+ }).strict();
21790
21794
  var DeployRerunJobArgsSchema = external_exports.object({
21791
21795
  personaId: external_exports.string().min(1),
21792
21796
  /** GitHub 的 job databaseId。**是数字**——字符串会在拼命令时静默出错 */
@@ -24273,9 +24277,13 @@ var METHOD_DOCS = {
24273
24277
  args: DeployPatchArgsSchema
24274
24278
  },
24275
24279
  "deploy:putSecret": {
24276
- summary: "\u628A\u4E00\u6761\u51ED\u636E\u7684\u503C\u5199\u8FDB\u4ED3\u5E93\u7684 GitHub Secrets\uFF08GitHub REST\uFF0C\u503C\u7528\u4ED3\u5E93\u516C\u94A5\u5C01\u7BB1\u540E\u4E0A\u4F20\uFF09\u3002**\u8FD9\u662F\u503C\u552F\u4E00\u7684\u5165\u53E3**\uFF1A\u5165\u53C2\u4E0D\u8FDB\u65E5\u5FD7\u3001\u54CD\u5E94\u4E0D\u56DE\u663E\u503C\u3001\u672C\u673A\u4E0D\u843D\u76D8\u3002env \u540D\u5FC5\u987B\u662F scanId \u90A3\u6B21\u6E05\u5355\u91CC\u8BE5\u51ED\u636E\u58F0\u660E\u8FC7\u7684\uFF1Bowner-only",
24280
+ summary: "\u628A\u4E00\u6761\u51ED\u636E\u7684\u503C\u5199\u8FDB\u4ED3\u5E93\u7684 GitHub Secrets\uFF08GitHub REST\uFF0C\u503C\u7528\u4ED3\u5E93\u516C\u94A5\u5C01\u7BB1\u540E\u4E0A\u4F20\uFF09\u3002**\u8FD9\u662F\u6E05\u5355\u51ED\u636E\u503C\u552F\u4E00\u7684\u5165\u53E3**\uFF08\u6A21\u578B\u8BA2\u9605\u8D70 deploy:setEngineAuth\uFF09\uFF1A\u5165\u53C2\u4E0D\u8FDB\u65E5\u5FD7\u3001\u54CD\u5E94\u4E0D\u56DE\u663E\u503C\u3001\u672C\u673A\u4E0D\u843D\u76D8\u3002env \u540D\u5FC5\u987B\u662F scanId \u90A3\u6B21\u6E05\u5355\u91CC\u8BE5\u51ED\u636E\u58F0\u660E\u8FC7\u7684\uFF1Bowner-only",
24277
24281
  args: DeployPutSecretArgsSchema
24278
24282
  },
24283
+ "deploy:setEngineAuth": {
24284
+ summary: "\u6A21\u578B\u8BA2\u9605\uFF08\u4E91\u4E0A\u8DD1\u8FD9\u4E2A persona \u82B1\u8C01\u7684\u6A21\u578B\u989D\u5EA6\uFF09\uFF1Aauth = { baseUrl, apiKey } \u5199\u8FDB\u4ED3\u5E93 GitHub Secrets \u7684 CLAWD_ENGINE_BASE_URL / CLAWD_ENGINE_API_KEY\uFF08\u5730\u5740\u89C4\u8303\u6210 \u2026/v1\uFF09\uFF0Cnull = \u5220\u8FD9\u4E24\u6761\u3001\u6539\u56DE\u5E73\u53F0\u9ED8\u8BA4\u3002\u4E0B\u6B21\u53D1\u5E03\u751F\u6548\u3002\u540C putSecret \u7684\u7EAA\u5F8B\uFF1A\u5165\u53C2\u4E0D\u8FDB\u65E5\u5FD7\u3001\u54CD\u5E94\u4E0D\u56DE\u663E\u503C\u3001\u672C\u673A\u4E0D\u843D\u76D8\uFF1B\u56DE deploy:get \u540C\u5F62\u7684\u72B6\u6001\uFF1Bowner-only\u3001human-only",
24285
+ args: DeploySetEngineAuthArgsSchema
24286
+ },
24279
24287
  "deploy:reportFinding": {
24280
24288
  summary: "\u626B\u63CF\u4E2D\u7684 butler \u9010\u6761\u4E0A\u62A5\u4E00\u4E2A\u53D1\u73B0\uFF08cli / secret / asset\uFF09\u3002**\u8FB9\u626B\u8FB9\u62A5**\uFF0C\u9875\u9762\u968F\u4E4B\u4E00\u70B9\u70B9\u586B\u5145\uFF1B\u540C (kind,name) \u91CD\u590D\u4E0A\u62A5\u5373\u8986\u76D6\u3002owner-only\u2014\u2014butler \u4F1A\u8BDD\u672C\u8EAB\u8DD1\u5728 owner \u6A21\u5F0F",
24281
24289
  args: DeployReportFindingArgsSchema
package/dist/cli.cjs CHANGED
@@ -6154,6 +6154,26 @@ var init_environment_schemas = __esm({
6154
6154
  });
6155
6155
 
6156
6156
  // ../protocol/src/deploy-schemas.ts
6157
+ function engineAuthState(secrets) {
6158
+ if (!secrets) return void 0;
6159
+ const has = (env) => secrets.some((p3) => p3.env === env && p3.setAt !== void 0);
6160
+ const n2 = [ENGINE_AUTH_SECRETS.baseUrl, ENGINE_AUTH_SECRETS.apiKey].filter(has).length;
6161
+ return n2 === 2 ? "own" : n2 === 0 ? "platform" : "partial";
6162
+ }
6163
+ function normalizeEngineBaseUrl(raw) {
6164
+ const s2 = raw.trim();
6165
+ let url;
6166
+ try {
6167
+ url = new URL(s2);
6168
+ } catch {
6169
+ throw new Error("\u8BA2\u9605\u5730\u5740\u8981\u4EE5 http:// \u6216 https:// \u5F00\u5934\uFF0C\u5F62\u5982 https://cpa.example.com");
6170
+ }
6171
+ if (url.protocol !== "http:" && url.protocol !== "https:" || !url.hostname) {
6172
+ throw new Error("\u8BA2\u9605\u5730\u5740\u8981\u4EE5 http:// \u6216 https:// \u5F00\u5934\uFF0C\u5F62\u5982 https://cpa.example.com");
6173
+ }
6174
+ const base = `${url.origin}${url.pathname}`.replace(/\/+$/, "");
6175
+ return base.endsWith("/v1") ? base : `${base}/v1`;
6176
+ }
6157
6177
  function contentHeadOf(repo2, released) {
6158
6178
  return released && repo2.head === released.head ? released.contentHead : repo2.head;
6159
6179
  }
@@ -6168,7 +6188,7 @@ function cloudEngineOk(tool) {
6168
6188
  function repoSlug(url) {
6169
6189
  return url.trim().replace(/^git@[^:]+:/, "").replace(/^https?:\/\/[^/]+\//, "").replace(/\.git$/, "");
6170
6190
  }
6171
- var DeployFileSchema, SCAN_BASE_KINDS, ScanBaseKindSchema, DeployScanStatusSchema, DeployScanSchema, DeployReleasedSchema, DeployScanFileSchema, DeployRepoSchema, DeploySecretPresenceSchema, DEPLOY_STAGE_STATUSES, DeployStageStatusSchema, DEPLOY_STAGE_KEYS, DeployStageKeySchema, DeployJobSchema, DeployRunSchema, CLOUD_ENGINES, DeployUnpublishRunSchema, DeployStateSchema, DeployUnpublishResultSchema, DeployReleaseResultSchema, DeployPersonaArgsSchema, DeployGetArgsSchema, DeployScanArgsSchema, DeployReleaseArgsSchema, DeployPatchArgsSchema, DeployPutSecretArgsSchema, DeployRerunJobArgsSchema, DeployReportFindingArgsSchema, DeployFinishScanArgsSchema, PERSONA_REPO_ORG;
6191
+ var DeployFileSchema, SCAN_BASE_KINDS, ScanBaseKindSchema, DeployScanStatusSchema, DeployScanSchema, DeployReleasedSchema, DeployScanFileSchema, DeployRepoSchema, DeploySecretPresenceSchema, ENGINE_AUTH_SECRETS, ENGINE_AUTH_PARTIAL_BLOCKER, DEPLOY_STAGE_STATUSES, DeployStageStatusSchema, DEPLOY_STAGE_KEYS, DeployStageKeySchema, DeployJobSchema, DeployRunSchema, CLOUD_ENGINES, DeployUnpublishRunSchema, DeployStateSchema, DeployUnpublishResultSchema, DeployReleaseResultSchema, DeployPersonaArgsSchema, DeployGetArgsSchema, DeployScanArgsSchema, DeployReleaseArgsSchema, DeployPatchArgsSchema, DeployPutSecretArgsSchema, DeploySetEngineAuthArgsSchema, DeployRerunJobArgsSchema, DeployReportFindingArgsSchema, DeployFinishScanArgsSchema, PERSONA_REPO_ORG;
6172
6192
  var init_deploy_schemas = __esm({
6173
6193
  "../protocol/src/deploy-schemas.ts"() {
6174
6194
  "use strict";
@@ -6253,6 +6273,8 @@ var init_deploy_schemas = __esm({
6253
6273
  env: external_exports.string().min(1),
6254
6274
  setAt: external_exports.number().optional()
6255
6275
  }).strict();
6276
+ ENGINE_AUTH_SECRETS = { baseUrl: "CLAWD_ENGINE_BASE_URL", apiKey: "CLAWD_ENGINE_API_KEY" };
6277
+ ENGINE_AUTH_PARTIAL_BLOCKER = "\u6A21\u578B\u8BA2\u9605\u53EA\u5199\u8FDB\u53BB\u4E00\u534A\u2014\u2014\u91CD\u586B\u6216\u6539\u56DE\u5E73\u53F0\u9ED8\u8BA4";
6256
6278
  DEPLOY_STAGE_STATUSES = ["pending", "running", "done", "failed", "waiting", "skipped"];
6257
6279
  DeployStageStatusSchema = external_exports.enum(DEPLOY_STAGE_STATUSES);
6258
6280
  DEPLOY_STAGE_KEYS = ["build", "smoke", "deploy"];
@@ -6358,6 +6380,10 @@ var init_deploy_schemas = __esm({
6358
6380
  // 环境变量名 → 值。key 必须是该 secret 声明过的 env 之一,handler 校验。
6359
6381
  values: external_exports.record(external_exports.string(), external_exports.string())
6360
6382
  }).strict();
6383
+ DeploySetEngineAuthArgsSchema = external_exports.object({
6384
+ personaId: external_exports.string().min(1),
6385
+ auth: external_exports.object({ baseUrl: external_exports.string(), apiKey: external_exports.string() }).strict().nullable()
6386
+ }).strict();
6361
6387
  DeployRerunJobArgsSchema = external_exports.object({
6362
6388
  personaId: external_exports.string().min(1),
6363
6389
  /** GitHub 的 job databaseId。**是数字**——字符串会在拼命令时静默出错 */
@@ -9137,9 +9163,13 @@ var init_methods = __esm({
9137
9163
  args: DeployPatchArgsSchema
9138
9164
  },
9139
9165
  "deploy:putSecret": {
9140
- summary: "\u628A\u4E00\u6761\u51ED\u636E\u7684\u503C\u5199\u8FDB\u4ED3\u5E93\u7684 GitHub Secrets\uFF08GitHub REST\uFF0C\u503C\u7528\u4ED3\u5E93\u516C\u94A5\u5C01\u7BB1\u540E\u4E0A\u4F20\uFF09\u3002**\u8FD9\u662F\u503C\u552F\u4E00\u7684\u5165\u53E3**\uFF1A\u5165\u53C2\u4E0D\u8FDB\u65E5\u5FD7\u3001\u54CD\u5E94\u4E0D\u56DE\u663E\u503C\u3001\u672C\u673A\u4E0D\u843D\u76D8\u3002env \u540D\u5FC5\u987B\u662F scanId \u90A3\u6B21\u6E05\u5355\u91CC\u8BE5\u51ED\u636E\u58F0\u660E\u8FC7\u7684\uFF1Bowner-only",
9166
+ summary: "\u628A\u4E00\u6761\u51ED\u636E\u7684\u503C\u5199\u8FDB\u4ED3\u5E93\u7684 GitHub Secrets\uFF08GitHub REST\uFF0C\u503C\u7528\u4ED3\u5E93\u516C\u94A5\u5C01\u7BB1\u540E\u4E0A\u4F20\uFF09\u3002**\u8FD9\u662F\u6E05\u5355\u51ED\u636E\u503C\u552F\u4E00\u7684\u5165\u53E3**\uFF08\u6A21\u578B\u8BA2\u9605\u8D70 deploy:setEngineAuth\uFF09\uFF1A\u5165\u53C2\u4E0D\u8FDB\u65E5\u5FD7\u3001\u54CD\u5E94\u4E0D\u56DE\u663E\u503C\u3001\u672C\u673A\u4E0D\u843D\u76D8\u3002env \u540D\u5FC5\u987B\u662F scanId \u90A3\u6B21\u6E05\u5355\u91CC\u8BE5\u51ED\u636E\u58F0\u660E\u8FC7\u7684\uFF1Bowner-only",
9141
9167
  args: DeployPutSecretArgsSchema
9142
9168
  },
9169
+ "deploy:setEngineAuth": {
9170
+ summary: "\u6A21\u578B\u8BA2\u9605\uFF08\u4E91\u4E0A\u8DD1\u8FD9\u4E2A persona \u82B1\u8C01\u7684\u6A21\u578B\u989D\u5EA6\uFF09\uFF1Aauth = { baseUrl, apiKey } \u5199\u8FDB\u4ED3\u5E93 GitHub Secrets \u7684 CLAWD_ENGINE_BASE_URL / CLAWD_ENGINE_API_KEY\uFF08\u5730\u5740\u89C4\u8303\u6210 \u2026/v1\uFF09\uFF0Cnull = \u5220\u8FD9\u4E24\u6761\u3001\u6539\u56DE\u5E73\u53F0\u9ED8\u8BA4\u3002\u4E0B\u6B21\u53D1\u5E03\u751F\u6548\u3002\u540C putSecret \u7684\u7EAA\u5F8B\uFF1A\u5165\u53C2\u4E0D\u8FDB\u65E5\u5FD7\u3001\u54CD\u5E94\u4E0D\u56DE\u663E\u503C\u3001\u672C\u673A\u4E0D\u843D\u76D8\uFF1B\u56DE deploy:get \u540C\u5F62\u7684\u72B6\u6001\uFF1Bowner-only\u3001human-only",
9171
+ args: DeploySetEngineAuthArgsSchema
9172
+ },
9143
9173
  "deploy:reportFinding": {
9144
9174
  summary: "\u626B\u63CF\u4E2D\u7684 butler \u9010\u6761\u4E0A\u62A5\u4E00\u4E2A\u53D1\u73B0\uFF08cli / secret / asset\uFF09\u3002**\u8FB9\u626B\u8FB9\u62A5**\uFF0C\u9875\u9762\u968F\u4E4B\u4E00\u70B9\u70B9\u586B\u5145\uFF1B\u540C (kind,name) \u91CD\u590D\u4E0A\u62A5\u5373\u8986\u76D6\u3002owner-only\u2014\u2014butler \u4F1A\u8BDD\u672C\u8EAB\u8DD1\u5728 owner \u6A21\u5F0F",
9145
9175
  args: DeployReportFindingArgsSchema
@@ -53313,6 +53343,23 @@ function stringValue(value) {
53313
53343
  return typeof value === "string" && value.length > 0 ? value : void 0;
53314
53344
  }
53315
53345
 
53346
+ // src/tools/first-byte.ts
53347
+ var VISIBLE = /* @__PURE__ */ new Set(["thinking", "tool_call", "text"]);
53348
+ function createFirstByteClock(now = Date.now) {
53349
+ let at3 = null;
53350
+ return {
53351
+ reset() {
53352
+ at3 = null;
53353
+ },
53354
+ see(events) {
53355
+ if (at3 === null && events.some((e) => VISIBLE.has(e.kind))) at3 = now();
53356
+ },
53357
+ afterFirstByteMs() {
53358
+ return at3 === null ? void 0 : Math.max(0, Math.round(now() - at3));
53359
+ }
53360
+ };
53361
+ }
53362
+
53316
53363
  // src/tools/codex-app-server-params.ts
53317
53364
  function resolveApprovalPolicy(ctx) {
53318
53365
  if (ctx.personaMode === "owner" || ctx.personaMode === "guest") return "never";
@@ -53448,6 +53495,7 @@ function driveCodexThread(opts) {
53448
53495
  let threadId = ctx.toolSessionId;
53449
53496
  let turnId;
53450
53497
  let turnOpen = false;
53498
+ const firstByte = createFirstByteClock(opts.now);
53451
53499
  const approvalIds = /* @__PURE__ */ new Map();
53452
53500
  const subagents = new CodexSubagentTracker();
53453
53501
  const effort = ctx.effort;
@@ -53479,10 +53527,13 @@ function driveCodexThread(opts) {
53479
53527
  ...subagents.handleParentNotification(method, params),
53480
53528
  ...translateNotification(method, params)
53481
53529
  ] : [];
53482
- if (events.length) sink.pushEvents(events);
53530
+ if (events.length) {
53531
+ if (turnOpen) firstByte.see(events);
53532
+ sink.pushEvents(events);
53533
+ }
53483
53534
  if (route === "foreground" && method === "turn/completed") {
53484
53535
  turnOpen = false;
53485
- opts.onTurnCompleted?.(turnOutcomeOf(params));
53536
+ opts.onTurnCompleted?.(turnOutcomeOf(params), firstByte.afterFirstByteMs());
53486
53537
  }
53487
53538
  },
53488
53539
  onServerRequest: (id, method, params) => {
@@ -53531,6 +53582,7 @@ function driveCodexThread(opts) {
53531
53582
  const opened = !turnOpen;
53532
53583
  if (opened) {
53533
53584
  turnOpen = true;
53585
+ firstByte.reset();
53534
53586
  opts.onTurnStarting?.();
53535
53587
  }
53536
53588
  void client.request("turn/start", {
@@ -53547,7 +53599,7 @@ function driveCodexThread(opts) {
53547
53599
  ]);
53548
53600
  if (!opened) return;
53549
53601
  turnOpen = false;
53550
- opts.onTurnCompleted?.("failure");
53602
+ opts.onTurnCompleted?.("failure", firstByte.afterFirstByteMs());
53551
53603
  });
53552
53604
  }
53553
53605
  });
@@ -60843,6 +60895,11 @@ function createGithubApi(args) {
60843
60895
  const encrypted_value = await seal(pk.key, value);
60844
60896
  await must("PUT", `/repos/${slug}/actions/secrets/${encodeURIComponent(name)}`, `\u5199 ${name}`, { encrypted_value, key_id: pk.key_id });
60845
60897
  },
60898
+ async deleteSecret(slug, name) {
60899
+ const r2 = await request("DELETE", `/repos/${slug}/actions/secrets/${encodeURIComponent(name)}`);
60900
+ if (r2.status === 404) return;
60901
+ if (r2.status < 200 || r2.status >= 300) throw new GithubApiError(r2.status, explain(r2.status, `\u5220 ${name}`, r2.json));
60902
+ },
60846
60903
  async listSecrets(slug) {
60847
60904
  const j2 = await must("GET", `/repos/${slug}/actions/secrets?per_page=100`, "\u67E5\u4ED3\u5E93 Secrets");
60848
60905
  const rows = Array.isArray(j2?.secrets) ? j2.secrets : [];
@@ -61408,6 +61465,15 @@ async function setSecret(exec, api, args) {
61408
61465
  throw new RepoError(err instanceof Error ? err.message : String(err));
61409
61466
  }
61410
61467
  }
61468
+ async function deleteSecret(exec, api, args) {
61469
+ const info = await repoInfo(exec, args.dir);
61470
+ if (!info) throw new RepoError("\u8FD8\u6CA1\u6709\u4ED3\u5E93\uFF0CSecrets \u6CA1\u5730\u65B9\u5220");
61471
+ try {
61472
+ await api.deleteSecret(repoSlug(info.url), args.env);
61473
+ } catch (err) {
61474
+ throw new RepoError(err instanceof Error ? err.message : String(err));
61475
+ }
61476
+ }
61411
61477
  async function listSecrets(exec, api, dir) {
61412
61478
  const info = await repoInfo(exec, dir);
61413
61479
  if (!info) return [];
@@ -61827,6 +61893,36 @@ function buildDeployHandlers(deps) {
61827
61893
  secretsCache.delete(args.personaId);
61828
61894
  return view(args.personaId);
61829
61895
  };
61896
+ const setEngineAuth = async (frame, _client, ctx) => {
61897
+ ensureOwner(ctx);
61898
+ const args = parseArgs2(DeploySetEngineAuthArgsSchema, frame, "deploy:setEngineAuth");
61899
+ const dir = mustPersona(args.personaId);
61900
+ let writes;
61901
+ if (args.auth) {
61902
+ let baseUrl;
61903
+ try {
61904
+ baseUrl = normalizeEngineBaseUrl(args.auth.baseUrl);
61905
+ } catch (err) {
61906
+ throw invalid(err instanceof Error ? err.message : String(err));
61907
+ }
61908
+ const apiKey = args.auth.apiKey.trim();
61909
+ if (!apiKey) throw invalid("key \u4E0D\u80FD\u662F\u7A7A\u7684");
61910
+ if (/[\s\p{Cc}]/u.test(apiKey)) throw invalid("key \u91CC\u4E0D\u80FD\u6709\u7A7A\u767D\u6216\u63A7\u5236\u5B57\u7B26\uFF08\u591A\u534A\u662F\u7C98\u8D34\u65F6\u5E26\u8FDB\u4E86\u6362\u884C\u6216\u7A7A\u683C\uFF09");
61911
+ writes = [[ENGINE_AUTH_SECRETS.apiKey, apiKey], [ENGINE_AUTH_SECRETS.baseUrl, baseUrl]];
61912
+ }
61913
+ const { api } = await requireGithub();
61914
+ try {
61915
+ if (writes) {
61916
+ await deleteSecret(deps.exec, api, { dir, env: ENGINE_AUTH_SECRETS.baseUrl });
61917
+ for (const [env, value] of writes) await setSecret(deps.exec, api, { dir, env, value });
61918
+ } else for (const env of [ENGINE_AUTH_SECRETS.apiKey, ENGINE_AUTH_SECRETS.baseUrl]) await deleteSecret(deps.exec, api, { dir, env });
61919
+ } catch (err) {
61920
+ throw toClawdError(err);
61921
+ } finally {
61922
+ secretsCache.delete(args.personaId);
61923
+ }
61924
+ return view(args.personaId);
61925
+ };
61830
61926
  async function settlePackage(pkg) {
61831
61927
  const localVersion = await deps.probeLocalVersion(pkg);
61832
61928
  const { artifact: reported, ...rest } = pkg;
@@ -61970,14 +62066,16 @@ function buildDeployHandlers(deps) {
61970
62066
  throw invalid(repo2.dirty ? "\u6709\u672A\u63D0\u4EA4\u7684\u6539\u52A8\uFF0C\u6E05\u5355\u662F\u6539\u52A8\u524D\u626B\u7684\u2014\u2014\u91CD\u65B0\u626B\u63CF" : "\u8FD9\u6B21\u626B\u63CF\u5BF9\u5E94\u7684\u4E0D\u662F\u5F53\u524D\u8FD9\u4E00\u7248 persona\u2014\u2014\u91CD\u65B0\u626B\u63CF");
61971
62067
  }
61972
62068
  const blockers = [];
61973
- let present;
62069
+ let secrets;
61974
62070
  try {
61975
- present = new Set((await cachedSecrets(personaId2, dir, api, true)).filter((p3) => p3.setAt).map((p3) => p3.env));
62071
+ secrets = await cachedSecrets(personaId2, dir, api, true);
61976
62072
  } catch (err) {
61977
62073
  throw toClawdError(err);
61978
62074
  }
62075
+ const present = new Set(secrets.filter((p3) => p3.setAt).map((p3) => p3.env));
61979
62076
  const missing = requiredEnvNames(entry.manifest).filter((e) => !present.has(e));
61980
62077
  if (missing.length > 0) blockers.push(`${missing.join(" / ")} \u8FD8\u6CA1\u586B`);
62078
+ if (engineAuthState(secrets) === "partial") blockers.push(ENGINE_AUTH_PARTIAL_BLOCKER);
61981
62079
  for (const c2 of entry.manifest.connectors) blockers.push(...connectorBlockers(c2));
61982
62080
  for (const p3 of entry.manifest.packages) blockers.push(...packageBlockers(p3));
61983
62081
  if (!deps.personaEngine(personaId2)?.model) {
@@ -62053,6 +62151,7 @@ function buildDeployHandlers(deps) {
62053
62151
  "deploy:scan": scanHandler,
62054
62152
  "deploy:patch": patch,
62055
62153
  "deploy:putSecret": putSecret,
62154
+ "deploy:setEngineAuth": setEngineAuth,
62056
62155
  "deploy:reportFinding": reportFinding,
62057
62156
  "deploy:finishScan": finishScan,
62058
62157
  "deploy:release": releaseHandler,
@@ -64007,7 +64106,7 @@ function openHttpLineStream(opts) {
64007
64106
  if (closed || !sessionId) return;
64008
64107
  const url = gatewayUrl + GATEWAY_ROUTES.sessionMirror(personaId2, sessionId);
64009
64108
  try {
64010
- const res = await fetchImpl(url, { method: "POST", headers, body: turn ? JSON.stringify({ turn: { outcome: turn.outcome } }) : "" });
64109
+ const res = await fetchImpl(url, { method: "POST", headers, body: turn ? JSON.stringify({ turn: { outcome: turn.outcome, ...turn.afterFirstByteMs !== void 0 ? { afterFirstByteMs: turn.afterFirstByteMs } : {} } }) : "" });
64011
64110
  if (!res.ok) logger?.warn("cloud session mirror failed", { status: res.status });
64012
64111
  } catch (e) {
64013
64112
  if (!closed) logger?.warn("cloud session mirror error", { message: e.message });
@@ -64226,7 +64325,9 @@ function createCloudCodexSession(ctx, sink, deps) {
64226
64325
  kill: () => stream.close(),
64227
64326
  // 云 persona 运行看板(design 2026-09-23-cloud-persona-metrics §3.7):提问那一行带「一轮开始」头,轮末镜像顺带报这一轮的结果
64228
64327
  onTurnStarting: () => channel?.markNextLineTurnStart(),
64229
- onTurnCompleted: (outcome) => void channel?.mirror({ outcome })
64328
+ // 轮末镜像带上这一轮的结果与首字节(design 2026-09-23-cloud-persona-metrics §3.7)
64329
+ onTurnCompleted: (outcome, afterFirstByteMs) => void channel?.mirror({ outcome, ...afterFirstByteMs !== void 0 ? { afterFirstByteMs } : {} }),
64330
+ now: deps.now
64230
64331
  });
64231
64332
  if (!personaId2) {
64232
64333
  queueMicrotask(() => {
@@ -64563,6 +64664,7 @@ function drivePiSession(opts) {
64563
64664
  let turnErrored = false;
64564
64665
  let turnInterrupted = false;
64565
64666
  let turnOpen = false;
64667
+ const firstByte = createFirstByteClock(opts.now);
64566
64668
  let handshakeFailed = false;
64567
64669
  let exited = false;
64568
64670
  const finish = (code) => {
@@ -64574,7 +64676,7 @@ function drivePiSession(opts) {
64574
64676
  sink.pushEvents([{ kind: "error", message }, { kind: "turn_end", reason: "completed" }]);
64575
64677
  if (!opened) return;
64576
64678
  turnOpen = false;
64577
- opts.onTurnCompleted?.("failure");
64679
+ opts.onTurnCompleted?.("failure", firstByte.afterFirstByteMs());
64578
64680
  };
64579
64681
  const handleUiRequest = (ev) => {
64580
64682
  const method = str2(ev.method);
@@ -64601,14 +64703,17 @@ function drivePiSession(opts) {
64601
64703
  return;
64602
64704
  }
64603
64705
  const events = translatePiEvent(ev, st2);
64604
- if (events.length) sink.pushEvents(events);
64706
+ if (events.length) {
64707
+ if (turnOpen) firstByte.see(events);
64708
+ sink.pushEvents(events);
64709
+ }
64605
64710
  for (const e of events) {
64606
64711
  if (e.kind === "error") turnErrored = true;
64607
64712
  if (e.kind === "turn_end" && e.reason === "interrupted") turnInterrupted = true;
64608
64713
  }
64609
64714
  if (ev.type === "agent_settled") {
64610
64715
  turnOpen = false;
64611
- opts.onTurnCompleted?.(turnInterrupted ? "interrupted" : turnErrored ? "failure" : "success");
64716
+ opts.onTurnCompleted?.(turnInterrupted ? "interrupted" : turnErrored ? "failure" : "success", firstByte.afterFirstByteMs());
64612
64717
  }
64613
64718
  },
64614
64719
  onClose: () => finish(opts.exitCodeOnClose?.() ?? null)
@@ -64644,6 +64749,7 @@ function drivePiSession(opts) {
64644
64749
  opened = !turnOpen;
64645
64750
  if (opened) {
64646
64751
  turnOpen = true;
64752
+ firstByte.reset();
64647
64753
  opts.onTurnStarting?.();
64648
64754
  }
64649
64755
  const r2 = await client.request({
@@ -64949,7 +65055,9 @@ function createCloudPiSession(ctx, sink, deps) {
64949
65055
  kill: () => stream.close(),
64950
65056
  // 云 persona 运行看板(design 2026-09-23-cloud-persona-metrics §3.7):提问那一行带「一轮开始」头,轮末镜像顺带报这一轮的结果
64951
65057
  onTurnStarting: () => channel?.markNextLineTurnStart(),
64952
- onTurnCompleted: (outcome) => void channel?.mirror({ outcome })
65058
+ // 轮末镜像带上这一轮的结果与首字节(design 2026-09-23-cloud-persona-metrics §3.7)
65059
+ onTurnCompleted: (outcome, afterFirstByteMs) => void channel?.mirror({ outcome, ...afterFirstByteMs !== void 0 ? { afterFirstByteMs } : {} }),
65060
+ now: deps.now
64953
65061
  });
64954
65062
  if (!personaId2) {
64955
65063
  queueMicrotask(() => {
@@ -71990,6 +72098,7 @@ var METHOD_GRANT_MAP = {
71990
72098
  "deploy:scan": ADMIN_ANY,
71991
72099
  "deploy:patch": ADMIN_ANY,
71992
72100
  "deploy:putSecret": ADMIN_ANY,
72101
+ "deploy:setEngineAuth": ADMIN_ANY,
71993
72102
  "deploy:reportFinding": ADMIN_ANY,
71994
72103
  "deploy:finishScan": ADMIN_ANY,
71995
72104
  "deploy:release": ADMIN_ANY,
@@ -72207,6 +72316,7 @@ var HUMAN_ONLY_METHODS = [
72207
72316
  "deploy:scan",
72208
72317
  "deploy:release",
72209
72318
  "deploy:putSecret",
72319
+ "deploy:setEngineAuth",
72210
72320
  "deploy:rerunJob",
72211
72321
  "deploy:unpublish",
72212
72322
  "githubAuth:oauthStart",
@@ -72253,7 +72363,7 @@ function computeMethodAccess(args) {
72253
72363
  }
72254
72364
 
72255
72365
  // src/version.ts
72256
- var version = "0.2.583".length > 0 ? "0.2.583" : "dev";
72366
+ var version = "0.2.585".length > 0 ? "0.2.585" : "dev";
72257
72367
 
72258
72368
  // src/cli-probe/probe.ts
72259
72369
  var fs80 = __toESM(require("fs"), 1);