@arcadeai/hypotenuse 0.11.0 → 0.12.0
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/hypotenuse.js +95 -2
- package/package.json +2 -2
- package/skills/hypotenuse.md +1 -0
package/dist/hypotenuse.js
CHANGED
|
@@ -343,7 +343,7 @@ import { Command } from "commander";
|
|
|
343
343
|
// package.json
|
|
344
344
|
var package_default = {
|
|
345
345
|
name: "@arcadeai/hypotenuse",
|
|
346
|
-
version: "0.
|
|
346
|
+
version: "0.12.0",
|
|
347
347
|
description: "Hypotenuse \u2014 run coding agents on your machine for Linear agent sessions",
|
|
348
348
|
type: "module",
|
|
349
349
|
license: "Apache-2.0",
|
|
@@ -386,7 +386,7 @@ var package_default = {
|
|
|
386
386
|
commander: "^14.0.3",
|
|
387
387
|
nanospinner: "^1.2.2",
|
|
388
388
|
"smol-toml": "^1.9.1",
|
|
389
|
-
upgradr: "^0.4.
|
|
389
|
+
upgradr: "^0.4.1",
|
|
390
390
|
zod: "^4.3.6"
|
|
391
391
|
},
|
|
392
392
|
devDependencies: {
|
|
@@ -10201,6 +10201,98 @@ function registerDoctorCommand(program) {
|
|
|
10201
10201
|
});
|
|
10202
10202
|
}
|
|
10203
10203
|
|
|
10204
|
+
// src/commands/event.ts
|
|
10205
|
+
function routedAs(event) {
|
|
10206
|
+
const s = event.session;
|
|
10207
|
+
if (!s?.role)
|
|
10208
|
+
return "-";
|
|
10209
|
+
const why = s.routingRule ? `rule ${s.routingRule.name}` : s.dispatched ? "dispatched" : "default";
|
|
10210
|
+
return `${s.role} (${why})`;
|
|
10211
|
+
}
|
|
10212
|
+
function turnPlace(event) {
|
|
10213
|
+
const t = event.turn;
|
|
10214
|
+
if (!t)
|
|
10215
|
+
return "-";
|
|
10216
|
+
if (t.status === "queued") {
|
|
10217
|
+
return `queued: ${t.waitingCode ?? t.waitingReason ?? "waiting"}`;
|
|
10218
|
+
}
|
|
10219
|
+
if (t.status === "cancelled") {
|
|
10220
|
+
return `cancelled${t.cancelReason ? `: ${t.cancelReason}` : ""}`;
|
|
10221
|
+
}
|
|
10222
|
+
return t.runner ? `${t.status} on ${t.runner.name}` : t.status;
|
|
10223
|
+
}
|
|
10224
|
+
function describeEvent(event, payload) {
|
|
10225
|
+
const lines = [
|
|
10226
|
+
`${event.eventType} ${event.action}${event.issueIdentifier ? ` ${event.issueIdentifier}` : ""}`,
|
|
10227
|
+
`outcome ${event.outcome}`,
|
|
10228
|
+
`summary ${event.summary ?? "-"}`,
|
|
10229
|
+
`received ${when(event.receivedAt)}`,
|
|
10230
|
+
`processed ${when(event.processedAt)}`,
|
|
10231
|
+
`delivery ${event.deliveryId}`
|
|
10232
|
+
];
|
|
10233
|
+
if (event.error)
|
|
10234
|
+
lines.push(`error ${event.error}`);
|
|
10235
|
+
if (event.session) {
|
|
10236
|
+
lines.push(`session ${event.session.id} (${event.session.status})`);
|
|
10237
|
+
lines.push(`routed as ${routedAs(event)}`);
|
|
10238
|
+
}
|
|
10239
|
+
if (event.turn) {
|
|
10240
|
+
lines.push(`turn ${event.turn.id} ${event.turn.kind}, ${turnPlace(event)}`);
|
|
10241
|
+
if (event.turn.waitingReason && event.turn.status === "queued") {
|
|
10242
|
+
lines.push(`waiting ${event.turn.waitingReason}`);
|
|
10243
|
+
}
|
|
10244
|
+
}
|
|
10245
|
+
lines.push("", payload ? JSON.stringify(payload, null, 2) : "payload (project admins only)");
|
|
10246
|
+
return lines.join(`
|
|
10247
|
+
`);
|
|
10248
|
+
}
|
|
10249
|
+
function registerEventCommands(program) {
|
|
10250
|
+
const event = program.command("event").description("The selected project's event log: Linear's webhooks and how each was routed");
|
|
10251
|
+
withPaging(event.command("list").description("List webhook events, newest first").option("--type <eventType>", "only this event type, e.g. AgentSessionEvent, Issue, PermissionChange").option("--outcome <outcome>", "only this outcome: handled, ignored, duplicate_of_dispatch, failed, pending").option("--hide-ignored", "leave out events Hypotenuse did not act on").option("--session <id>", "only events about this agent session").option("--since <time>", "only events since: ISO-8601, epoch ms, or 7d / 12h / 30m")).action(async function() {
|
|
10252
|
+
await runCommand(this, async (ctx) => {
|
|
10253
|
+
const opts = this.opts();
|
|
10254
|
+
const projectId = await ctx.projectId();
|
|
10255
|
+
const result = await ctx.signedInClient().request("GET", "/trigger-events", {
|
|
10256
|
+
query: {
|
|
10257
|
+
projectId,
|
|
10258
|
+
eventType: opts.type,
|
|
10259
|
+
outcome: opts.outcome,
|
|
10260
|
+
hideIgnored: opts.hideIgnored ? true : undefined,
|
|
10261
|
+
agentSessionId: opts.session,
|
|
10262
|
+
since: opts.since ? parseTime(opts.since) : undefined,
|
|
10263
|
+
...pageQuery(this)
|
|
10264
|
+
}
|
|
10265
|
+
});
|
|
10266
|
+
emit(ctx, result, () => table(result.triggerEvents.map((e) => ({
|
|
10267
|
+
id: e.id,
|
|
10268
|
+
received: when(e.receivedAt),
|
|
10269
|
+
event: `${e.eventType} ${e.action}`,
|
|
10270
|
+
issue: e.issueIdentifier ?? "-",
|
|
10271
|
+
outcome: e.outcome,
|
|
10272
|
+
"routed as": routedAs(e),
|
|
10273
|
+
turn: turnPlace(e),
|
|
10274
|
+
summary: e.summary ?? ""
|
|
10275
|
+
})), [
|
|
10276
|
+
{ key: "id" },
|
|
10277
|
+
{ key: "received" },
|
|
10278
|
+
{ key: "event" },
|
|
10279
|
+
{ key: "issue" },
|
|
10280
|
+
{ key: "outcome" },
|
|
10281
|
+
{ key: "routed as" },
|
|
10282
|
+
{ key: "turn", maxWidth: 40 },
|
|
10283
|
+
{ key: "summary", maxWidth: 70 }
|
|
10284
|
+
]) + pageFooter(result.pagination));
|
|
10285
|
+
});
|
|
10286
|
+
});
|
|
10287
|
+
event.command("view <id>").description("Show one event: what it did, how it was routed, and (admins) its payload").action(async function(id) {
|
|
10288
|
+
await runCommand(this, async (ctx) => {
|
|
10289
|
+
const projectId = await ctx.projectId();
|
|
10290
|
+
const result = await ctx.signedInClient().request("GET", `/trigger-event/${id}`, { query: { projectId } });
|
|
10291
|
+
emit(ctx, result, () => describeEvent(result.triggerEvent, result.payload));
|
|
10292
|
+
});
|
|
10293
|
+
});
|
|
10294
|
+
}
|
|
10295
|
+
|
|
10204
10296
|
// src/commands/identity.ts
|
|
10205
10297
|
function linkedViaText(via) {
|
|
10206
10298
|
return via === "email" ? "e-mail match" : via === "oauth" ? "Linear sign-in" : "admin";
|
|
@@ -11995,6 +12087,7 @@ function buildProgram() {
|
|
|
11995
12087
|
registerAuditCommands(program);
|
|
11996
12088
|
registerIntegrationCommands(program);
|
|
11997
12089
|
registerSessionCommands(program);
|
|
12090
|
+
registerEventCommands(program);
|
|
11998
12091
|
registerRunnerCommands(program);
|
|
11999
12092
|
registerRoutingCommands(program);
|
|
12000
12093
|
registerIdentityCommands(program);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arcadeai/hypotenuse",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Hypotenuse — run coding agents on your machine for Linear agent sessions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"commander": "^14.0.3",
|
|
44
44
|
"nanospinner": "^1.2.2",
|
|
45
45
|
"smol-toml": "^1.9.1",
|
|
46
|
-
"upgradr": "^0.4.
|
|
46
|
+
"upgradr": "^0.4.1",
|
|
47
47
|
"zod": "^4.3.6"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
package/skills/hypotenuse.md
CHANGED
|
@@ -35,6 +35,7 @@ server. It calls the same HTTP actions the dashboard does, as the person who sig
|
|
|
35
35
|
| `hypotenuse audit list [--since 7d] [--until <time>] [--action tag:edit]` | Who changed what, newest first (admin). Times are ISO-8601, epoch ms, or `7d` / `12h` / `30m`. |
|
|
36
36
|
| `hypotenuse integration list\|view\|connect\|disconnect` | The selected project's trigger integrations — today the only type is `linear`, its Linear workspace connection. `list` shows each type this server supports and whether the project is connected. `view <type>` shows the workspace, the `@hypotenuse` app user, scopes, and token health (never a token). `connect <type>` (admin) prints the consent URL — a **workspace admin** of that system (for `linear`, a Linear workspace admin) must approve it in a browser — and waits until the integration is active (`--no-browser`, `--timeout <seconds>`). `disconnect <type> --yes` (admin) revokes the tokens; the agent stops answering there at once. |
|
|
37
37
|
| `hypotenuse session list\|view\|cancel` | The selected project's agent sessions — each time `@hypotenuse` was delegated an issue or mentioned, in any trigger type (Linear today). `list [--status <status>] [--role manager\|worker] [--type linear] [--issue ENG-123] [--runner <id>]`, newest first; `view <id\|ENG-123>` (an issue identifier names its newest session) shows the session (issue, status, origin, role, runner, issuer, links), its turns (kind, status, runner, and for a waiting turn its reason code such as `offline` or `no_capacity`), and every activity in order, both directions, with each write's delivery status (`pending`, `sent`, `failed`, `superseded`); `--transcript` prints only the activities; `--follow [--interval <seconds>]` keeps printing new ones until interrupted (one JSON object per line under `--json`); `cancel <id>` stops its work as Stop in Linear does (work not started is cancelled; a running turn is told to stop and report). |
|
|
38
|
+
| `hypotenuse event list\|view` | The selected project's event log: every webhook its Linear workspace sent in the last 14 days and how each was routed. `list [--type AgentSessionEvent\|Issue\|…] [--outcome handled\|ignored\|duplicate_of_dispatch\|failed\|pending] [--hide-ignored] [--session <id>] [--since 12h]`, newest first, shows each event's issue, outcome, what it did, the role it was routed as and why (`worker (default)`, `manager (rule /manage)`, `worker (dispatched)`), and where its turn is now (`queued: no_capacity`, `running on <runner>`); `view <id>` shows one in full, with the payload Linear sent for project admins only. |
|
|
38
39
|
| `hypotenuse runner register\|list\|view\|edit\|revoke` | The selected project's runners — machines that run coding agents. `register [--name <name>] [--replace] [--force]` registers **this** machine as the signed-in person, saves the runner token to `credentials.json` (never printed), and sends a first heartbeat; `--replace` revokes the runner of that name first. `list [--status online\|offline\|draining\|revoked] [--all]`; `view <id\|name>` shows capabilities, subscriptions, and whether its socket is attached; `edit <id\|name> [--name] [--enable\|--disable]` renames or pauses it; `revoke <id\|name> --yes` revokes it for good (its owner or an admin). |
|
|
39
40
|
| `hypotenuse routing list\|add\|edit\|remove\|reorder\|preview\|settings` | The selected project's routing rules — the first enabled rule whose match holds decides whether a new session gets a **manager** or a **worker** (no match: worker). `list`; `add --name <name> --role manager\|worker [--label <l>] [--team <key>] [--linear-project <id>] [--prefix /manage] [--has-parent\|--no-parent] [--origin delegation\|mention] [--repository <repo>] [--disabled]` (labels and team keys are checked against the connected workspace); `edit <id>` with the same flags (any match flag replaces the whole match) plus `--enable\|--disable` and `--clear-repository`; `remove <id>`; `reorder <id...>` lists every rule id in the new order; `preview <ENG-123> [--prompt "/manage ..."] [--with-repository]` shows how the issue would route if delegated now — the matching rule, the role, every runner with whether it could take the work and its reason codes, and the repository (any member; `--with-repository` asks Linear to rank repositories, spending its API budget); `settings` shows the routing timers, `settings [--queue-timeout 30m] [--sticky-timeout 10m] [--offer-ttl 10s] [--stop-grace 30s] [--repository-confidence 0.7] [--max-review-rounds 3] [--reset <key>]` changes them. Changes are admin-only. |
|
|
40
41
|
| `hypotenuse identity list\|link\|unlink` | Links between people in the project's Linear workspace and project members; routing prefers the runners of whoever started a session. `list [--user <id>]` shows each link and how it was made (e-mail match, Linear sign-in, admin); `link --user <id> --linear-user <id> [--name] [--email]` links one (admin); `unlink <id>` removes your own link, or anyone's as an admin. |
|