@hivefloor/cli 0.1.1 → 0.1.2

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.
Files changed (29) hide show
  1. package/dist/{chunk-Q2TS3GT3.js → chunk-DQV6FAJU.js} +2 -2
  2. package/dist/{chunk-MMWX62ZU.js → chunk-DWHMBUCA.js} +2 -2
  3. package/dist/{chunk-NCRQGIJY.js → chunk-E4V47VEV.js} +3 -9
  4. package/dist/{chunk-WO7NALQO.js → chunk-HT5KXWXM.js} +1 -1
  5. package/dist/{chunk-WLHRUZTR.js → chunk-LF2NWQ4B.js} +2 -1
  6. package/dist/{chunk-WHHV2NZA.js → chunk-NKMVO5BP.js} +4 -10
  7. package/dist/{chunk-YCZ7XJJF.js → chunk-NLZXB26Y.js} +2 -2
  8. package/dist/{chunk-SGDM7MAT.js → chunk-O2TC3WSS.js} +68 -2
  9. package/dist/{chunk-U7PMOZGI.js → chunk-O6VSA2JT.js} +4 -3
  10. package/dist/{chunk-76HDATOB.js → chunk-OOMBZN4M.js} +13 -13
  11. package/dist/{chunk-3IMV5EHP.js → chunk-T7TG6WA4.js} +1 -1
  12. package/dist/{chunk-ZYRPQPGL.js → chunk-TH4ANHSG.js} +333 -190
  13. package/dist/{chunk-WNDUGQDX.js → chunk-VNCQFFQO.js} +8 -0
  14. package/dist/{chunk-CWEEIRW2.js → chunk-YRNMECUF.js} +1 -1
  15. package/dist/{cli-FFAZRKLR.js → cli-F3F7SQLV.js} +13 -10
  16. package/dist/connect-TG2H5SMB.js +23 -0
  17. package/dist/{disconnect-VWUOUZP6.js → disconnect-FI76IEZJ.js} +10 -10
  18. package/dist/{hook-WSJXG2XU.js → hook-N4ZTQEWM.js} +53 -19
  19. package/dist/index.js +2 -2
  20. package/dist/{link-TFITMC6H.js → link-JX4YLF2Z.js} +9 -9
  21. package/dist/{machine-connect-I3OZCTUZ.js → machine-connect-NOQ3VZOO.js} +24 -24
  22. package/dist/{mcp-47WTNQNN.js → mcp-EUBTVJNX.js} +7 -7
  23. package/dist/run-CKNHDPNX.js +1883 -0
  24. package/dist/{status-GXZ267BD.js → status-SMAVPW4D.js} +14 -14
  25. package/dist/{token-C4K23KXH.js → token-AUEAWQDH.js} +4 -4
  26. package/dist/where-7NI7UEEF.js +70 -0
  27. package/package.json +1 -1
  28. package/dist/connect-FM4BXTQQ.js +0 -23
  29. package/dist/run-JO4NEHWG.js +0 -629
@@ -19786,14 +19786,21 @@ var hookEventSchema = external_exports.object({
19786
19786
  model: external_exports.string().min(1).max(200).optional(),
19787
19787
  repo: repoInfoSchema.optional(),
19788
19788
  /** ISO time on the client; display only (the server clock decides). */
19789
- client_at: external_exports.iso.datetime({ offset: true })
19789
+ client_at: external_exports.iso.datetime({ offset: true }),
19790
+ /** The CLI sets it on the event it posts for a hook whose output the tool reads as context (Claude's UserPromptSubmit and the like): only then does the answer carry the session's pending nudges. */
19791
+ deliver: external_exports.boolean().optional()
19790
19792
  });
19791
19793
  var hookBatchSchema = external_exports.object({ events: external_exports.array(hookEventSchema).min(1).max(HOOK_BATCH_MAX) });
19792
19794
  var hookBatchResponseSchema = external_exports.object({
19793
19795
  accepted: external_exports.number().int().nonnegative(),
19794
- dropped: external_exports.number().int().nonnegative()
19796
+ dropped: external_exports.number().int().nonnegative(),
19797
+ /** F2: nudges a person sent to the sessions of a `deliver` event, by the tool's own session id. The runner prints the text as the hook's `additionalContext`. */
19798
+ nudges: external_exports.array(external_exports.object({ session_id: external_exports.string(), text: external_exports.string() })).optional()
19795
19799
  });
19796
19800
 
19801
+ // ../shared/src/machine-health.ts
19802
+ var MACHINE_HEALTH_RULES = ["offline_with_claims", "disk_full", "disk_high", "memory_high", "cli_behind"];
19803
+
19797
19804
  // ../shared/src/api/projects.ts
19798
19805
  var idSchema = external_exports.string().regex(/^[0-9]{1,15}$/, "must be a numeric id");
19799
19806
  var projectNameSchema = external_exports.string().trim().min(1).max(100);
@@ -19964,6 +19971,10 @@ var machineAgentSchema = external_exports.object({
19964
19971
  live_sessions: external_exports.number().int().nonnegative(),
19965
19972
  last_seen_at: external_exports.string().nullable()
19966
19973
  });
19974
+ var machineHealthViewSchema = external_exports.object({
19975
+ level: external_exports.enum(["warn", "critical"]),
19976
+ findings: external_exports.array(external_exports.object({ rule: external_exports.enum(MACHINE_HEALTH_RULES), level: external_exports.enum(["warn", "critical"]), text: external_exports.string() }))
19977
+ });
19967
19978
  var machineSchema = external_exports.object({
19968
19979
  id: external_exports.string(),
19969
19980
  name: external_exports.string(),
@@ -19975,7 +19986,9 @@ var machineSchema = external_exports.object({
19975
19986
  default_project_id: external_exports.string().nullable(),
19976
19987
  projects: external_exports.array(external_exports.object({ id: external_exports.string(), key: external_exports.string(), name: external_exports.string(), default: external_exports.boolean() })),
19977
19988
  stats: machineStatsSchema.nullable(),
19978
- agents: external_exports.array(machineAgentSchema)
19989
+ agents: external_exports.array(machineAgentSchema),
19990
+ /** The rules that fire for this machine; null or absent when healthy (older servers omit it). */
19991
+ health: machineHealthViewSchema.nullable().optional()
19979
19992
  });
19980
19993
  var patchMachineBody = external_exports.object({
19981
19994
  name: external_exports.string().trim().min(1).max(100).optional(),
@@ -19995,121 +20008,44 @@ function cliNpxCommand(args = "", opts = {}) {
19995
20008
  }
19996
20009
  var CLI_CONNECT_COMMAND = cliNpxCommand("connect");
19997
20010
 
19998
- // ../shared/src/task-status.ts
19999
- var TASK_STATUSES = ["backlog", "ready", "claimed", "in_review", "done", "blocked", "parked"];
20000
- var taskStatusSchema = external_exports.enum(TASK_STATUSES);
20001
-
20002
- // ../shared/src/remote.ts
20003
- var SCHEME = /^([a-z][a-z0-9+.-]*):\/\//i;
20004
- function normalizeRemote(remote) {
20005
- let s = remote.trim();
20006
- if (!s || s.length > 2048 || /[\s\0]/.test(s)) return null;
20007
- let host2;
20008
- let path;
20009
- if (SCHEME.test(s)) {
20010
- s = s.replace(SCHEME, "");
20011
- const slash = s.indexOf("/");
20012
- if (slash < 0) return null;
20013
- host2 = s.slice(0, slash);
20014
- path = s.slice(slash + 1);
20015
- } else {
20016
- const colon = s.indexOf(":");
20017
- const slash = s.indexOf("/");
20018
- if (colon >= 0 && (slash < 0 || colon < slash)) {
20019
- host2 = s.slice(0, colon);
20020
- path = s.slice(colon + 1);
20021
- } else if (slash >= 0) {
20022
- host2 = s.slice(0, slash);
20023
- path = s.slice(slash + 1);
20024
- } else {
20025
- return null;
20026
- }
20027
- }
20028
- const at = host2.lastIndexOf("@");
20029
- if (at >= 0) host2 = host2.slice(at + 1);
20030
- host2 = host2.replace(/:\d*$/, "").toLowerCase();
20031
- if (!/^[a-z0-9]([a-z0-9.-]*[a-z0-9])?$/.test(host2)) return null;
20032
- path = path.replace(/[?#].*$/, "");
20033
- const parts = path.split("/").filter((p) => p.length > 0);
20034
- if (parts.length < 2) return null;
20035
- let name = parts[parts.length - 1];
20036
- if (name.toLowerCase().endsWith(".git")) name = name.slice(0, -4);
20037
- const ownerParts = parts.slice(0, -1);
20038
- if (!name || [...ownerParts, name].some((p) => p === "." || p === ".." || !/^[A-Za-z0-9_.@+-]+$/.test(p))) return null;
20039
- return { host: host2, owner: ownerParts.join("/"), name };
20040
- }
20041
- function formatRemote(r) {
20042
- return `${r.host}/${r.owner}/${r.name}`;
20043
- }
20044
-
20045
- // ../shared/src/redact.ts
20046
- var tag = (kind) => `[redacted:${kind}]`;
20047
- function isPlaceholder(value) {
20048
- const v = value.replace(/^["']|["']$/g, "");
20049
- return v === "" || /^(\$|<|\{|%|\*+$|x+$|\.\.\.|\[redacted)/i.test(v) || /^(process\.env|os\.environ|env\.|ENV\[|config\.|settings\.)/i.test(v) || /^(null|none|undefined|true|false|changeme|example|your[_-])/i.test(v);
20050
- }
20051
- var KEY_PREFIX = String.raw`(?<![A-Za-z0-9])`;
20052
- var VALUE = String.raw`("[^"\r\n]*"|'[^'\r\n]*'|[^\s,;"'&)}\]]+)`;
20053
- var RULES = [
20054
- { kind: "pem", re: /-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----[\s\S]*?(?:-----END [A-Z0-9 ]*PRIVATE KEY-----|$)/g },
20055
- { kind: "github", re: /\bgithub_pat_[A-Za-z0-9_]{20,}/g },
20056
- { kind: "github", re: /\bgh[pousr]_[A-Za-z0-9]{20,}/g },
20057
- { kind: "anthropic", re: /\bsk-ant-[A-Za-z0-9_-]{10,}/g },
20058
- // sk- keys are random base62; the digit lookahead keeps hyphenated words ("sk-learn-compatible-...") out.
20059
- { kind: "openai", re: /\bsk-(?=[A-Za-z0-9_-]*\d)[A-Za-z0-9_-]{20,}/g },
20060
- { kind: "aws", re: /\b(?:AKIA|ASIA)[A-Z0-9]{16}\b/g },
20061
- { kind: "slack", re: /\bxox[abprs]-[A-Za-z0-9-]{10,}/g },
20062
- // hf_ tokens are 32 random bytes in base64url: long, with a digit or capital. Snake_case identifiers have neither.
20063
- { kind: "hivefloor", re: /\bhf_(?=[A-Za-z0-9_-]*[A-Z0-9])[A-Za-z0-9_-]{32,}/g },
20064
- // A fleet push token (hff_ + 32 random bytes) or machine token (hfm_ + 32 random bytes): must never end up in a status line or a title.
20065
- { kind: "hivefloor", re: /\bhf[fm]_[A-Za-z0-9_-]{20,}/g },
20066
- { kind: "webhook", re: /\bwhsec_[A-Za-z0-9_-]{16,}/g },
20067
- { kind: "vqb", re: /\bvqb_[A-Za-z0-9_-]{16,}/g },
20068
- { kind: "jwt", re: /\beyJ[A-Za-z0-9_-]{8,}\.eyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]*/g },
20069
- {
20070
- kind: "bearer",
20071
- re: /\b(Bearer)\s+(?=[A-Za-z0-9._~+/=-]*\d)[A-Za-z0-9._~+/=-]{20,}/g,
20072
- replace: (_m, word) => `${word} ${tag("bearer")}`
20073
- },
20074
- // password=hunter2, "passwd": "x", DB_PASSWORD='x'. Any non-placeholder value counts.
20075
- {
20076
- kind: "password",
20077
- re: new RegExp(String.raw`${KEY_PREFIX}(password|passwd|pwd)(["']?\s*[:=]\s*)${VALUE}`, "gi"),
20078
- replace: (m, _key, sep = "", value = "") => isAssignedSecret(sep, value, 1) ? `${m.slice(0, m.length - value.length)}${tag("password")}` : null
20079
- },
20080
- // token=..., client_secret: ..., api_key=...: the value has to be long enough to be a credential.
20081
- {
20082
- kind: "secret",
20083
- re: new RegExp(String.raw`${KEY_PREFIX}(secret|token|api[_-]?key|apikey)(["']?\s*[:=]\s*)${VALUE}`, "gi"),
20084
- replace: (m, _key, sep = "", value = "") => isAssignedSecret(sep, value, 8) ? `${m.slice(0, m.length - value.length)}${tag("secret")}` : null
20085
- }
20086
- ];
20087
- function isAssignedSecret(sep, value, minLen) {
20088
- if (isPlaceholder(value)) return false;
20089
- const quoted = /^["']/.test(value);
20090
- const v = value.replace(/^["']|["']$/g, "");
20091
- if (v.length < minLen) return false;
20092
- if (sep.includes("=") || quoted) return true;
20093
- return v.length >= 6 && /[^A-Za-z]/.test(v);
20011
+ // ../shared/src/usage.ts
20012
+ var MIN_SPAN_MS = 5 * 6e4;
20013
+ var MAX_ETA_MS = 31 * 24 * 36e5;
20014
+ var USAGE_COUNTERS = ["tokens_in", "tokens_out", "cost_usd"];
20015
+ function isUsageCounter(value) {
20016
+ return USAGE_COUNTERS.includes(value);
20094
20017
  }
20095
- function redact(input2) {
20096
- let text2 = input2;
20097
- let count = 0;
20098
- for (const rule of RULES) {
20099
- text2 = text2.replace(rule.re, (...args) => {
20100
- const match = args[0];
20101
- const groups = args.slice(1, -2).filter((g) => typeof g === "string");
20102
- const out = rule.replace ? rule.replace(match, ...groups) : tag(rule.kind);
20103
- if (out === null) return match;
20104
- count += 1;
20105
- return out;
20106
- });
20018
+ function parseUsageKey(key2) {
20019
+ const parts = key2.split(":");
20020
+ if (parts.length < 3) return null;
20021
+ const tool = parts[0];
20022
+ const profile = parts[1];
20023
+ if (!TOOLS.includes(tool)) return null;
20024
+ if (!PROFILE_KEY_RE.test(profile)) return null;
20025
+ if (parts.length === 3) {
20026
+ const counter2 = parts[2];
20027
+ return isUsageCounter(counter2) ? { tool, profile, session: null, counter: counter2 } : null;
20107
20028
  }
20108
- return { text: text2, count };
20029
+ if (parts[2] !== "s" || parts.length < 5) return null;
20030
+ const session = parts.slice(3, -1).join(":");
20031
+ const counter = parts[parts.length - 1];
20032
+ if (!session || !isUsageCounter(counter)) return null;
20033
+ return { tool, profile, session, counter };
20109
20034
  }
20110
20035
 
20036
+ // ../shared/src/usage-alerts.ts
20037
+ var WASTE_WITHIN_MS = 3 * 36e5;
20038
+
20039
+ // ../shared/src/task-status.ts
20040
+ var TASK_STATUSES = ["backlog", "ready", "claimed", "in_review", "done", "blocked", "parked"];
20041
+ var taskStatusSchema = external_exports.enum(TASK_STATUSES);
20042
+
20111
20043
  // ../shared/src/health.ts
20112
- var SESSION_HEALTH = ["ok", "stalled", "lost", "looping"];
20044
+ var PARKED_AFTER_SEC = 20 * 60;
20045
+ var WAITING_LONG_AFTER_SEC = 15 * 60;
20046
+ var MIN_STALL_THRESHOLD_SEC = 60;
20047
+ var MAX_STALL_THRESHOLD_SEC = 7 * 24 * 3600;
20048
+ var SESSION_HEALTH = ["ok", "stalled", "lost", "looping", "parked", "waiting_long"];
20113
20049
 
20114
20050
  // ../shared/src/api/rules.ts
20115
20051
  var entityId = external_exports.string().regex(/^\d{1,20}$/, "an id is a decimal string");
@@ -20134,6 +20070,10 @@ var rulesConfigSchema = external_exports.object({
20134
20070
  wip_per_agent: cap,
20135
20071
  wip_per_machine: cap,
20136
20072
  stall_release_seconds: external_exports.number().int().min(MIN_STALL_RELEASE_SECONDS).max(MAX_STALL_RELEASE_SECONDS).nullable(),
20073
+ /** Seconds an idle session holds a claim before it shows as parked (default 20 min); null or omitted = default. An omitted field keeps what is stored. */
20074
+ parked_after_seconds: external_exports.number().int().min(MIN_STALL_THRESHOLD_SEC).max(MAX_STALL_THRESHOLD_SEC).nullable().optional(),
20075
+ /** Seconds a wait on a person lasts before it shows as waiting_long and the owner is pushed again (default 15 min); the admins hear at 4 times this. */
20076
+ waiting_long_after_seconds: external_exports.number().int().min(MIN_STALL_THRESHOLD_SEC).max(MAX_STALL_THRESHOLD_SEC).nullable().optional(),
20137
20077
  rules: external_exports.array(ruleSchema).max(MAX_RULES).refine((rs) => new Set(rs.map((r) => r.id)).size === rs.length, "rule ids must be unique")
20138
20078
  }).strict();
20139
20079
  var steerBody = external_exports.object({
@@ -20204,9 +20144,11 @@ var taskCardSchema = external_exports.object({
20204
20144
  done_at: isoTimeSchema.nullable(),
20205
20145
  archived_at: isoTimeSchema.nullable(),
20206
20146
  /** Tasks this one waits on (stage 2). It is claimable only when every one of them is done. */
20207
- blocked_by: external_exports.array(blockerRefSchema).default([])
20147
+ blocked_by: external_exports.array(blockerRefSchema).default([]),
20148
+ /** A claimed task whose holder's machine is offline: the work may be stuck there. Absent or false otherwise. */
20149
+ at_risk: external_exports.boolean().optional()
20208
20150
  });
20209
- var TASK_TIMELINE_KINDS = ["created", "claimed", "note", "blocked", "released", "lease_expired", "pr", "in_review", "done", "answered", "archived", "unarchived", "unblocked", "steered"];
20151
+ var TASK_TIMELINE_KINDS = ["created", "claimed", "note", "blocked", "released", "lease_expired", "pr", "in_review", "done", "answered", "archived", "unarchived", "unblocked", "steered", "nudged"];
20210
20152
  var taskTimelineKindSchema = external_exports.enum(TASK_TIMELINE_KINDS);
20211
20153
  var taskTimelineEventSchema = external_exports.object({
20212
20154
  id: entityIdSchema,
@@ -20264,7 +20206,7 @@ var taskStatusLineQuerySchema = external_exports.object({
20264
20206
  }).strict();
20265
20207
  var taskResultSchema = external_exports.object({ task: taskCardSchema });
20266
20208
  var answerBodySchema = external_exports.object({ text: external_exports.string().trim().min(1).max(500) });
20267
- var NOTIFICATION_KINDS = ["task_done", "task_blocked", "agent_waiting", "invite_received", "member_added", "repo_link_requested", "agent_question", "feedback"];
20209
+ var NOTIFICATION_KINDS = ["task_done", "task_blocked", "agent_waiting", "invite_received", "member_added", "repo_link_requested", "agent_question", "feedback", "usage_waste", "usage_high", "wait_escalated", "machine_unhealthy"];
20268
20210
  var notificationKindSchema = external_exports.enum(NOTIFICATION_KINDS);
20269
20211
  var notificationViewSchema = external_exports.object({
20270
20212
  id: entityIdSchema,
@@ -20357,50 +20299,6 @@ var releaseBody = external_exports.object({
20357
20299
  }).strict();
20358
20300
  var completeBody = external_exports.object({ claim_token: claimToken.optional(), summary: external_exports.string().min(1).max(5e3), pr_url: external_exports.string().max(1e3).optional(), session: sessionRef.optional() }).strict();
20359
20301
 
20360
- // ../shared/src/api/memory.ts
20361
- var CONTEXT_DECISIONS = 10;
20362
- var MEMORY_KINDS = ["handoff", "decision"];
20363
- var memoryKindSchema = external_exports.enum(MEMORY_KINDS);
20364
- var memoryEntrySchema = external_exports.object({
20365
- id: entityIdSchema,
20366
- kind: memoryKindSchema,
20367
- /** The task the entry is about; always set for a handoff. */
20368
- task_id: entityIdSchema.nullable(),
20369
- /** The task as people write it (`HF-12`). */
20370
- task_key: external_exports.string().nullable(),
20371
- text: external_exports.string(),
20372
- author: external_exports.object({ type: external_exports.enum(["agent", "user", "system"]), name: external_exports.string() }),
20373
- created_at: isoTimeSchema
20374
- });
20375
- var memoryContextSchema = external_exports.object({
20376
- handoff: memoryEntrySchema.nullable(),
20377
- decisions: external_exports.array(memoryEntrySchema).max(CONTEXT_DECISIONS)
20378
- });
20379
- var memoryListSchema = external_exports.object({
20380
- items: external_exports.array(memoryEntrySchema),
20381
- next_cursor: external_exports.string().nullable()
20382
- });
20383
- var taskIdSchema2 = external_exports.union([external_exports.string().regex(/^[0-9]{1,15}$/), external_exports.number().int().positive()]).transform((v) => String(v));
20384
- var sessionRef2 = external_exports.string().trim().min(1).max(200);
20385
- var addDecisionBody = external_exports.object({
20386
- project: projectRefSchema.optional(),
20387
- /** The task the decision is about (optional). */
20388
- task_id: taskIdSchema2.optional(),
20389
- text: external_exports.string().min(1).max(2e3),
20390
- session: sessionRef2.optional()
20391
- }).strict();
20392
- var getContextBody = external_exports.object({
20393
- project: projectRefSchema.optional(),
20394
- /** The task whose last handoff to include. */
20395
- task_id: taskIdSchema2.optional()
20396
- }).strict();
20397
- var humanDecisionBody = external_exports.object({
20398
- text: external_exports.string().trim().min(1).max(2e3),
20399
- /** The task number (not the id) the decision is about. */
20400
- task_num: external_exports.number().int().positive().max(2e9).optional()
20401
- }).strict();
20402
- var decisionResultSchema = external_exports.object({ entry: memoryEntrySchema });
20403
-
20404
20302
  // ../shared/src/api/usage.ts
20405
20303
  var isoTime = external_exports.iso.datetime({ offset: true });
20406
20304
  var USAGE_PROVIDERS = [
@@ -20522,6 +20420,210 @@ var taskUsageSchema = external_exports.object({
20522
20420
  agents: external_exports.array(taskUsageAgentSchema).max(50)
20523
20421
  }).strict();
20524
20422
 
20423
+ // ../shared/src/usage-route.ts
20424
+ var ROUTE_HORIZON_MS = 2 * 36e5;
20425
+ function usageProviderForTool(tool) {
20426
+ switch (tool) {
20427
+ case "claude":
20428
+ case "codex":
20429
+ case "cursor":
20430
+ case "gemini":
20431
+ case "grok":
20432
+ return tool;
20433
+ default:
20434
+ return null;
20435
+ }
20436
+ }
20437
+ function routeProviderFromParam(param) {
20438
+ const v = param.trim().toLowerCase();
20439
+ const fromTool = usageProviderForTool(v);
20440
+ if (fromTool) return fromTool;
20441
+ const p = usageProviderSchema.safeParse(v);
20442
+ return p.success ? p.data : null;
20443
+ }
20444
+ var isoTime2 = external_exports.iso.datetime({ offset: true });
20445
+ var routeAccountSchema = external_exports.object({
20446
+ id: external_exports.string().max(120),
20447
+ label: external_exports.string().max(60),
20448
+ plan: external_exports.string().max(40).optional(),
20449
+ usable: external_exports.boolean(),
20450
+ score: external_exports.number().nullable(),
20451
+ window: external_exports.string().max(40).optional(),
20452
+ percent_left: external_exports.number().min(0).max(100).optional(),
20453
+ resets_at: isoTime2.optional(),
20454
+ live_sessions: external_exports.number().int().nonnegative(),
20455
+ waste: external_exports.boolean(),
20456
+ reason: external_exports.string().max(200)
20457
+ }).strict();
20458
+ var routeProviderSchema = external_exports.object({
20459
+ provider: usageProviderSchema,
20460
+ name: external_exports.string().max(40),
20461
+ /** The account to use next: the first of `accounts` when it is usable, else null. */
20462
+ best: external_exports.string().max(120).nullable(),
20463
+ accounts: external_exports.array(routeAccountSchema).max(100)
20464
+ }).strict();
20465
+ var usageRouteSchema = external_exports.object({
20466
+ generated_at: isoTime2,
20467
+ /** The provider asked for, or null for all of them. */
20468
+ provider: usageProviderSchema.nullable(),
20469
+ providers: external_exports.array(routeProviderSchema).max(USAGE_PROVIDERS.length),
20470
+ /** Live sessions that could not be counted against an account (no label, unknown label, a tool with no usage). */
20471
+ unlinked_sessions: external_exports.number().int().nonnegative()
20472
+ }).strict();
20473
+
20474
+ // ../shared/src/remote.ts
20475
+ var SCHEME = /^([a-z][a-z0-9+.-]*):\/\//i;
20476
+ function normalizeRemote(remote) {
20477
+ let s = remote.trim();
20478
+ if (!s || s.length > 2048 || /[\s\0]/.test(s)) return null;
20479
+ let host2;
20480
+ let path;
20481
+ if (SCHEME.test(s)) {
20482
+ s = s.replace(SCHEME, "");
20483
+ const slash = s.indexOf("/");
20484
+ if (slash < 0) return null;
20485
+ host2 = s.slice(0, slash);
20486
+ path = s.slice(slash + 1);
20487
+ } else {
20488
+ const colon = s.indexOf(":");
20489
+ const slash = s.indexOf("/");
20490
+ if (colon >= 0 && (slash < 0 || colon < slash)) {
20491
+ host2 = s.slice(0, colon);
20492
+ path = s.slice(colon + 1);
20493
+ } else if (slash >= 0) {
20494
+ host2 = s.slice(0, slash);
20495
+ path = s.slice(slash + 1);
20496
+ } else {
20497
+ return null;
20498
+ }
20499
+ }
20500
+ const at = host2.lastIndexOf("@");
20501
+ if (at >= 0) host2 = host2.slice(at + 1);
20502
+ host2 = host2.replace(/:\d*$/, "").toLowerCase();
20503
+ if (!/^[a-z0-9]([a-z0-9.-]*[a-z0-9])?$/.test(host2)) return null;
20504
+ path = path.replace(/[?#].*$/, "");
20505
+ const parts = path.split("/").filter((p) => p.length > 0);
20506
+ if (parts.length < 2) return null;
20507
+ let name = parts[parts.length - 1];
20508
+ if (name.toLowerCase().endsWith(".git")) name = name.slice(0, -4);
20509
+ const ownerParts = parts.slice(0, -1);
20510
+ if (!name || [...ownerParts, name].some((p) => p === "." || p === ".." || !/^[A-Za-z0-9_.@+-]+$/.test(p))) return null;
20511
+ return { host: host2, owner: ownerParts.join("/"), name };
20512
+ }
20513
+ function formatRemote(r) {
20514
+ return `${r.host}/${r.owner}/${r.name}`;
20515
+ }
20516
+
20517
+ // ../shared/src/redact.ts
20518
+ var tag = (kind) => `[redacted:${kind}]`;
20519
+ function isPlaceholder(value) {
20520
+ const v = value.replace(/^["']|["']$/g, "");
20521
+ return v === "" || /^(\$|<|\{|%|\*+$|x+$|\.\.\.|\[redacted)/i.test(v) || /^(process\.env|os\.environ|env\.|ENV\[|config\.|settings\.)/i.test(v) || /^(null|none|undefined|true|false|changeme|example|your[_-])/i.test(v);
20522
+ }
20523
+ var KEY_PREFIX = String.raw`(?<![A-Za-z0-9])`;
20524
+ var VALUE = String.raw`("[^"\r\n]*"|'[^'\r\n]*'|[^\s,;"'&)}\]]+)`;
20525
+ var RULES = [
20526
+ { kind: "pem", re: /-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----[\s\S]*?(?:-----END [A-Z0-9 ]*PRIVATE KEY-----|$)/g },
20527
+ { kind: "github", re: /\bgithub_pat_[A-Za-z0-9_]{20,}/g },
20528
+ { kind: "github", re: /\bgh[pousr]_[A-Za-z0-9]{20,}/g },
20529
+ { kind: "anthropic", re: /\bsk-ant-[A-Za-z0-9_-]{10,}/g },
20530
+ // sk- keys are random base62; the digit lookahead keeps hyphenated words ("sk-learn-compatible-...") out.
20531
+ { kind: "openai", re: /\bsk-(?=[A-Za-z0-9_-]*\d)[A-Za-z0-9_-]{20,}/g },
20532
+ { kind: "aws", re: /\b(?:AKIA|ASIA)[A-Z0-9]{16}\b/g },
20533
+ { kind: "slack", re: /\bxox[abprs]-[A-Za-z0-9-]{10,}/g },
20534
+ // hf_ tokens are 32 random bytes in base64url: long, with a digit or capital. Snake_case identifiers have neither.
20535
+ { kind: "hivefloor", re: /\bhf_(?=[A-Za-z0-9_-]*[A-Z0-9])[A-Za-z0-9_-]{32,}/g },
20536
+ // A fleet push token (hff_ + 32 random bytes) or machine token (hfm_ + 32 random bytes): must never end up in a status line or a title.
20537
+ { kind: "hivefloor", re: /\bhf[fm]_[A-Za-z0-9_-]{20,}/g },
20538
+ { kind: "webhook", re: /\bwhsec_[A-Za-z0-9_-]{16,}/g },
20539
+ { kind: "vqb", re: /\bvqb_[A-Za-z0-9_-]{16,}/g },
20540
+ { kind: "jwt", re: /\beyJ[A-Za-z0-9_-]{8,}\.eyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]*/g },
20541
+ {
20542
+ kind: "bearer",
20543
+ re: /\b(Bearer)\s+(?=[A-Za-z0-9._~+/=-]*\d)[A-Za-z0-9._~+/=-]{20,}/g,
20544
+ replace: (_m, word) => `${word} ${tag("bearer")}`
20545
+ },
20546
+ // password=hunter2, "passwd": "x", DB_PASSWORD='x'. Any non-placeholder value counts.
20547
+ {
20548
+ kind: "password",
20549
+ re: new RegExp(String.raw`${KEY_PREFIX}(password|passwd|pwd)(["']?\s*[:=]\s*)${VALUE}`, "gi"),
20550
+ replace: (m, _key, sep = "", value = "") => isAssignedSecret(sep, value, 1) ? `${m.slice(0, m.length - value.length)}${tag("password")}` : null
20551
+ },
20552
+ // token=..., client_secret: ..., api_key=...: the value has to be long enough to be a credential.
20553
+ {
20554
+ kind: "secret",
20555
+ re: new RegExp(String.raw`${KEY_PREFIX}(secret|token|api[_-]?key|apikey)(["']?\s*[:=]\s*)${VALUE}`, "gi"),
20556
+ replace: (m, _key, sep = "", value = "") => isAssignedSecret(sep, value, 8) ? `${m.slice(0, m.length - value.length)}${tag("secret")}` : null
20557
+ }
20558
+ ];
20559
+ function isAssignedSecret(sep, value, minLen) {
20560
+ if (isPlaceholder(value)) return false;
20561
+ const quoted = /^["']/.test(value);
20562
+ const v = value.replace(/^["']|["']$/g, "");
20563
+ if (v.length < minLen) return false;
20564
+ if (sep.includes("=") || quoted) return true;
20565
+ return v.length >= 6 && /[^A-Za-z]/.test(v);
20566
+ }
20567
+ function redact(input2) {
20568
+ let text2 = input2;
20569
+ let count = 0;
20570
+ for (const rule of RULES) {
20571
+ text2 = text2.replace(rule.re, (...args) => {
20572
+ const match = args[0];
20573
+ const groups = args.slice(1, -2).filter((g) => typeof g === "string");
20574
+ const out = rule.replace ? rule.replace(match, ...groups) : tag(rule.kind);
20575
+ if (out === null) return match;
20576
+ count += 1;
20577
+ return out;
20578
+ });
20579
+ }
20580
+ return { text: text2, count };
20581
+ }
20582
+
20583
+ // ../shared/src/api/memory.ts
20584
+ var CONTEXT_DECISIONS = 10;
20585
+ var MEMORY_KINDS = ["handoff", "decision"];
20586
+ var memoryKindSchema = external_exports.enum(MEMORY_KINDS);
20587
+ var memoryEntrySchema = external_exports.object({
20588
+ id: entityIdSchema,
20589
+ kind: memoryKindSchema,
20590
+ /** The task the entry is about; always set for a handoff. */
20591
+ task_id: entityIdSchema.nullable(),
20592
+ /** The task as people write it (`HF-12`). */
20593
+ task_key: external_exports.string().nullable(),
20594
+ text: external_exports.string(),
20595
+ author: external_exports.object({ type: external_exports.enum(["agent", "user", "system"]), name: external_exports.string() }),
20596
+ created_at: isoTimeSchema
20597
+ });
20598
+ var memoryContextSchema = external_exports.object({
20599
+ handoff: memoryEntrySchema.nullable(),
20600
+ decisions: external_exports.array(memoryEntrySchema).max(CONTEXT_DECISIONS)
20601
+ });
20602
+ var memoryListSchema = external_exports.object({
20603
+ items: external_exports.array(memoryEntrySchema),
20604
+ next_cursor: external_exports.string().nullable()
20605
+ });
20606
+ var taskIdSchema2 = external_exports.union([external_exports.string().regex(/^[0-9]{1,15}$/), external_exports.number().int().positive()]).transform((v) => String(v));
20607
+ var sessionRef2 = external_exports.string().trim().min(1).max(200);
20608
+ var addDecisionBody = external_exports.object({
20609
+ project: projectRefSchema.optional(),
20610
+ /** The task the decision is about (optional). */
20611
+ task_id: taskIdSchema2.optional(),
20612
+ text: external_exports.string().min(1).max(2e3),
20613
+ session: sessionRef2.optional()
20614
+ }).strict();
20615
+ var getContextBody = external_exports.object({
20616
+ project: projectRefSchema.optional(),
20617
+ /** The task whose last handoff to include. */
20618
+ task_id: taskIdSchema2.optional()
20619
+ }).strict();
20620
+ var humanDecisionBody = external_exports.object({
20621
+ text: external_exports.string().trim().min(1).max(2e3),
20622
+ /** The task number (not the id) the decision is about. */
20623
+ task_num: external_exports.number().int().positive().max(2e9).optional()
20624
+ }).strict();
20625
+ var decisionResultSchema = external_exports.object({ entry: memoryEntrySchema });
20626
+
20525
20627
  // ../shared/src/api/floor.ts
20526
20628
  var FLOOR_TOOLS = ["claude", "codex", "cursor", "gemini", "grok", "other"];
20527
20629
  var floorToolSchema = external_exports.enum(FLOOR_TOOLS);
@@ -20549,8 +20651,12 @@ var floorSessionSchema = external_exports.object({
20549
20651
  agent_id: entityIdSchema,
20550
20652
  parent_id: entityIdSchema.nullable(),
20551
20653
  state: floorSessionStateSchema,
20552
- /** Derived from the clock (`stalled`: a working session gone quiet; `lost`: a subagent that never reported done; `looping`: the same error repeating, or no status change while the claim keeps being extended). See `sessionHealth`. */
20654
+ /** Derived from the clock (`stalled`: a working session gone quiet; `lost`: a subagent that never reported done; `looping`: the same error repeating, or no status change while the claim keeps being extended; `parked`: idle for a while holding a claim; `waiting_long`: waiting on a person past the threshold). See `sessionHealth`. */
20553
20655
  health: external_exports.enum(SESSION_HEALTH),
20656
+ /** When the state or status line last changed: the start of the current wait or park (age labels count from it). Older servers omit it; use `last_event_at`. */
20657
+ state_since: isoTimeSchema.optional(),
20658
+ /** For a `waiting` session: false once somebody answered this very wait. Older servers omit it (read as true). */
20659
+ wait_open: external_exports.boolean().optional(),
20554
20660
  /** The tool's own session id (what `claude --resume` takes), main sessions only. */
20555
20661
  tool_session_id: external_exports.string().nullable(),
20556
20662
  model: external_exports.string().nullable(),
@@ -20578,7 +20684,15 @@ var floorPodSchema = external_exports.object({
20578
20684
  agents: external_exports.array(external_exports.object({ agent: floorAgentSchema, sessions: external_exports.array(floorSessionSchema) }))
20579
20685
  });
20580
20686
  var floorSnapshotSchema = external_exports.object({
20581
- project: external_exports.object({ id: entityIdSchema, key: external_exports.string(), name: external_exports.string() }),
20687
+ project: external_exports.object({
20688
+ id: entityIdSchema,
20689
+ key: external_exports.string(),
20690
+ name: external_exports.string(),
20691
+ /** The project's thresholds for parked and waiting_long, seconds (the defaults when it sets none). The browser judges health with them. */
20692
+ stall: external_exports.object({ parked_after_sec: external_exports.number(), waiting_long_after_sec: external_exports.number() }).optional(),
20693
+ /** Health of the machines this project's agents run on, by machine name; only machines with something to report. Older servers omit it. */
20694
+ machine_health: external_exports.record(external_exports.string(), machineHealthViewSchema).optional()
20695
+ }),
20582
20696
  pods: external_exports.array(floorPodSchema),
20583
20697
  shelves: external_exports.object({
20584
20698
  /** Every ready task, most urgent first (at most 200). */
@@ -20715,6 +20829,8 @@ var projectAgentSchema = external_exports.object({
20715
20829
  ...agentBaseShape,
20716
20830
  /** True when the signed-in person owns the agent (they may revoke it even as a viewer). */
20717
20831
  mine: external_exports.boolean(),
20832
+ /** The tool profile (the AI account) the agent runs under, when known; older servers omit it. */
20833
+ profile: external_exports.string().nullable().optional(),
20718
20834
  sessions: external_exports.array(floorSessionSchema)
20719
20835
  });
20720
20836
  var projectAgentList = listOf(projectAgentSchema);
@@ -20737,7 +20853,7 @@ var LOOP_NO_CHANGE_SEC = 30 * 60;
20737
20853
  var LOOP_LEASE_FRESH_SEC = 5 * 60;
20738
20854
 
20739
20855
  // ../shared/src/api/webhooks.ts
20740
- var WEBHOOK_EVENTS = ["needs_you", "task_done", "agent_lost"];
20856
+ var WEBHOOK_EVENTS = ["needs_you", "task_done", "agent_lost", "usage_waste", "usage_high", "machine_unhealthy"];
20741
20857
  var webhookEventSchema = external_exports.enum(WEBHOOK_EVENTS);
20742
20858
  var putWebhookBody = external_exports.object({
20743
20859
  url: external_exports.string().trim().min(1).max(2048),
@@ -20762,31 +20878,6 @@ var webhookGetResponse = external_exports.object({ webhook: webhookViewSchema.nu
20762
20878
  var webhookPutResponse = external_exports.object({ webhook: webhookViewSchema, secret: external_exports.string().optional() }).strict();
20763
20879
  var webhookTestResponse = external_exports.object({ ok: external_exports.literal(true), queued: external_exports.literal(true) }).strict();
20764
20880
 
20765
- // ../shared/src/usage.ts
20766
- var MIN_SPAN_MS = 5 * 6e4;
20767
- var MAX_ETA_MS = 31 * 24 * 36e5;
20768
- var USAGE_COUNTERS = ["tokens_in", "tokens_out", "cost_usd"];
20769
- function isUsageCounter(value) {
20770
- return USAGE_COUNTERS.includes(value);
20771
- }
20772
- function parseUsageKey(key2) {
20773
- const parts = key2.split(":");
20774
- if (parts.length < 3) return null;
20775
- const tool = parts[0];
20776
- const profile = parts[1];
20777
- if (!TOOLS.includes(tool)) return null;
20778
- if (!PROFILE_KEY_RE.test(profile)) return null;
20779
- if (parts.length === 3) {
20780
- const counter2 = parts[2];
20781
- return isUsageCounter(counter2) ? { tool, profile, session: null, counter: counter2 } : null;
20782
- }
20783
- if (parts[2] !== "s" || parts.length < 5) return null;
20784
- const session = parts.slice(3, -1).join(":");
20785
- const counter = parts[parts.length - 1];
20786
- if (!session || !isUsageCounter(counter)) return null;
20787
- return { tool, profile, session, counter };
20788
- }
20789
-
20790
20881
  // ../shared/src/api/inbox.ts
20791
20882
  var INBOX_KINDS = ["blocked_task", "waiting", "question"];
20792
20883
  var inboxKindSchema = external_exports.enum(INBOX_KINDS);
@@ -20801,7 +20892,9 @@ var inboxItemSchema = external_exports.object({
20801
20892
  text: external_exports.string(),
20802
20893
  waiting_since: isoTimeSchema,
20803
20894
  /** Whether the caller may answer it (role, the project's answer setting, and for a member: owning the agent). */
20804
- can_answer: external_exports.boolean()
20895
+ can_answer: external_exports.boolean(),
20896
+ /** Whether the caller may nudge the item's session now (F2): owner or admin, and the session is stalled, parked, waiting too long or looping. */
20897
+ can_nudge: external_exports.boolean().optional()
20805
20898
  });
20806
20899
  var inboxListSchema = external_exports.object({
20807
20900
  items: external_exports.array(inboxItemSchema),
@@ -21089,6 +21182,31 @@ var digestAgentItemSchema = external_exports.object({
21089
21182
  /** A short-lived run agent (a mirrored fleet/vq lane, or named `*-run-*` / `vq-*`). */
21090
21183
  is_run: external_exports.boolean()
21091
21184
  }).strict();
21185
+ var digestWaitItemSchema = external_exports.object({
21186
+ session_id: idSchema,
21187
+ project_id: idSchema,
21188
+ project_key: external_exports.string(),
21189
+ agent_name: external_exports.string(),
21190
+ machine: external_exports.string().nullable(),
21191
+ /** `waiting` = a wait on a person (or blocked); `parked` = idle while holding a claimed task. */
21192
+ kind: external_exports.enum(["waiting", "parked"]),
21193
+ /** How long the wait has lasted so far (whole seconds). */
21194
+ seconds: external_exports.number().int().nonnegative(),
21195
+ /** Always true today: only waits still open are known (no state history is kept). */
21196
+ open: external_exports.boolean()
21197
+ }).strict();
21198
+ var digestLostTimeSchema = external_exports.object({
21199
+ waiting_hours: external_exports.number().nonnegative(),
21200
+ parked_hours: external_exports.number().nonnegative(),
21201
+ longest_waits: external_exports.array(digestWaitItemSchema).max(3)
21202
+ }).strict();
21203
+ var digestWastedAccountSchema = external_exports.object({ account: external_exports.string(), unused_points: external_exports.number().nonnegative(), resets: external_exports.number().int().positive() }).strict();
21204
+ var digestQuotaSchema = external_exports.object({
21205
+ wasted_points: external_exports.number().nonnegative(),
21206
+ accounts: external_exports.array(digestWastedAccountSchema).max(DIGEST_ITEMS_LIMIT),
21207
+ alerts_waste: external_exports.number().int().nonnegative(),
21208
+ alerts_high: external_exports.number().int().nonnegative()
21209
+ }).strict();
21092
21210
  var digestBucketSchema = (item) => external_exports.object({
21093
21211
  count: external_exports.number().int().nonnegative(),
21094
21212
  items: external_exports.array(item).max(DIGEST_ITEMS_LIMIT)
@@ -21112,7 +21230,11 @@ var meDigestResponseSchema = external_exports.object({
21112
21230
  prs_merged: digestBucketSchema(digestTaskItemSchema),
21113
21231
  tasks_blocked: digestBucketSchema(digestTaskItemSchema),
21114
21232
  needs_you: digestBucketSchema(digestNeedsYouItemSchema),
21115
- new_agents: digestBucketSchema(digestAgentItemSchema)
21233
+ new_agents: digestBucketSchema(digestAgentItemSchema),
21234
+ /** F7. `total` also counts one for each of `lost_time` and `quota` that has something to show. */
21235
+ lost_time: digestLostTimeSchema,
21236
+ /** Null on a project-scoped digest (accounts are not a project's). */
21237
+ quota: digestQuotaSchema.nullable()
21116
21238
  }).strict();
21117
21239
  var meDigestSeenBodySchema = external_exports.object({
21118
21240
  before: isoTimeSchema.optional()
@@ -21157,6 +21279,24 @@ var feedbackListSchema = external_exports.object({
21157
21279
  total: external_exports.number().int().min(0)
21158
21280
  });
21159
21281
 
21282
+ // ../shared/src/api/nudge.ts
21283
+ var NUDGE_TTL_SEC = 24 * 3600;
21284
+ var NUDGE_NOTE_MAX = 300;
21285
+ var nudgeBody = external_exports.object({ note: external_exports.string().trim().max(NUDGE_NOTE_MAX).optional() }).strict();
21286
+ var nudgeResponse = external_exports.object({
21287
+ nudge: external_exports.object({
21288
+ id: external_exports.string(),
21289
+ session_id: external_exports.string(),
21290
+ created_at: isoTimeSchema,
21291
+ /** When the next nudge of this session is allowed. */
21292
+ next_allowed_at: isoTimeSchema,
21293
+ /** The owner got a push (with the resume command when the tool has one). */
21294
+ pushed: external_exports.boolean(),
21295
+ resume_command: external_exports.string().nullable()
21296
+ })
21297
+ });
21298
+ var agentNudgeSchema = external_exports.object({ id: external_exports.string(), text: external_exports.string(), sent_at: isoTimeSchema });
21299
+
21160
21300
  // ../shared/src/index.ts
21161
21301
  var ROLES = ["owner", "admin", "member", "viewer"];
21162
21302
  var roleSchema2 = external_exports.enum(ROLES);
@@ -21268,7 +21408,10 @@ export {
21268
21408
  CLI_INSTALL_COMMAND,
21269
21409
  cliNpxCommand,
21270
21410
  redact,
21411
+ usageReadingSchema,
21271
21412
  parseUsageKey,
21413
+ routeProviderFromParam,
21414
+ usageRouteSchema,
21272
21415
  DEFAULT_HOST,
21273
21416
  defaultCtx,
21274
21417
  hostFromEnv,