@hiroleague/taskmanager 0.0.1 → 0.0.3
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/LICENSE +21 -0
- package/README.md +41 -52
- package/dist/assets/architecture-YZFGNWBL-DoE0KxgG.js +1 -0
- package/dist/assets/architectureDiagram-Q4EWVU46-DeuBhy7X.js +36 -0
- package/dist/assets/{blockDiagram-DXYQGD6D-DfOGNphI.js → blockDiagram-DXYQGD6D-BDBy9ns9.js} +1 -1
- package/dist/assets/{c4Diagram-AHTNJAMY-B2Yfcwbo.js → c4Diagram-AHTNJAMY-CpqJj_8a.js} +1 -1
- package/dist/assets/channel-PHRyjspt.js +1 -0
- package/dist/assets/{chunk-2KRD3SAO-9yt00aGC.js → chunk-2KRD3SAO-DEpUsxdZ.js} +1 -1
- package/dist/assets/chunk-336JU56O-BGQvSwLk.js +2 -0
- package/dist/assets/chunk-426QAEUC-Cl9nQN9c.js +1 -0
- package/dist/assets/{chunk-4TB4RGXK-DF8yJBFl.js → chunk-4TB4RGXK-Dq7aiIrZ.js} +2 -2
- package/dist/assets/{chunk-5FUZZQ4R-XEga0hMC.js → chunk-5FUZZQ4R-B_HuuUjf.js} +1 -1
- package/dist/assets/{chunk-5PVQY5BW-BrmXs2Gs.js → chunk-5PVQY5BW-cGfZCZGU.js} +2 -2
- package/dist/assets/{chunk-67CJDMHE-5wFKo04G.js → chunk-67CJDMHE-BMYAVZfw.js} +1 -1
- package/dist/assets/{chunk-7N4EOEYR-BRRGX_NC.js → chunk-7N4EOEYR-Ct-EY7Nc.js} +1 -1
- package/dist/assets/{chunk-AA7GKIK3-DUZv_pNI.js → chunk-AA7GKIK3-Bd4HFpeo.js} +1 -1
- package/dist/assets/{chunk-CIAEETIT-mA5aM_d7.js → chunk-CIAEETIT-CrFUkPMT.js} +1 -1
- package/dist/assets/{chunk-EDXVE4YY-DxUqDyxy.js → chunk-EDXVE4YY-DMDyt0NF.js} +1 -1
- package/dist/assets/{chunk-ENJZ2VHE-BgZKYo1l.js → chunk-ENJZ2VHE-DrWzOrpd.js} +1 -1
- package/dist/assets/{chunk-FOC6F5B3-B-cqGCPC.js → chunk-FOC6F5B3-Bemzq96j.js} +1 -1
- package/dist/assets/{chunk-ICPOFSXX-BNR1V8rT.js → chunk-ICPOFSXX-DkUVjrLw.js} +5 -5
- package/dist/assets/{chunk-K5T4RW27-BLRDzioh.js → chunk-K5T4RW27-ALKIf000.js} +5 -5
- package/dist/assets/{chunk-KGLVRYIC-CTkQSeKy.js → chunk-KGLVRYIC-Bg6HNTZ-.js} +1 -1
- package/dist/assets/{chunk-LIHQZDEY-Cf34Nu3J.js → chunk-LIHQZDEY-DeyGongE.js} +1 -1
- package/dist/assets/{chunk-ORNJ4GCN-D3uXgbay.js → chunk-ORNJ4GCN-Bx83s1bJ.js} +1 -1
- package/dist/assets/{chunk-OYMX7WX6-syQho5jf.js → chunk-OYMX7WX6-BqRUtRpL.js} +1 -1
- package/dist/assets/{chunk-U2HBQHQK-DTJPeU7W.js → chunk-U2HBQHQK-DogcerR6.js} +1 -1
- package/dist/assets/{chunk-X2U36JSP-CrTnmMqG.js → chunk-X2U36JSP-CwVWdmZV.js} +1 -1
- package/dist/assets/chunk-XPW4576I-DQpNCogT.js +32 -0
- package/dist/assets/{chunk-YZCP3GAM-9wq0QKUn.js → chunk-YZCP3GAM-crQSbji9.js} +1 -1
- package/dist/assets/{chunk-ZZ45TVLE-D3I1kLlo.js → chunk-ZZ45TVLE-Bk1S1YtS.js} +1 -1
- package/dist/assets/classDiagram-6PBFFD2Q-B_TabGaU.js +1 -0
- package/dist/assets/classDiagram-v2-HSJHXN6E-CGnZkUWw.js +1 -0
- package/dist/assets/clone-D4ka472w.js +1 -0
- package/dist/assets/{cose-bilkent-S5V4N54A-BygGvZGW.js → cose-bilkent-S5V4N54A-RBTHUit8.js} +1 -1
- package/dist/assets/cytoscape.esm-BGJwlmkf.js +321 -0
- package/dist/assets/dagre-B32eYLtm.js +1 -0
- package/dist/assets/{dagre-KV5264BT-BBqulDtd.js → dagre-KV5264BT-nX7tuXXn.js} +1 -1
- package/dist/assets/diagram-5BDNPKRD-DRxMXlQr.js +10 -0
- package/dist/assets/diagram-G4DWMVQ6-CoojevGm.js +24 -0
- package/dist/assets/diagram-MMDJMWI5-CWtJyfVW.js +43 -0
- package/dist/assets/diagram-TYMM5635-CsDJC4Hq.js +24 -0
- package/dist/assets/{erDiagram-SMLLAGMA-BN5eJerP.js → erDiagram-SMLLAGMA-Cf7Xtd9A.js} +2 -2
- package/dist/assets/{flatten-C5NL-f24.js → flatten-CYX_pHZ7.js} +1 -1
- package/dist/assets/{flowDiagram-DWJPFMVM-CbFskc8S.js → flowDiagram-DWJPFMVM-DQaeR16a.js} +3 -3
- package/dist/assets/{ganttDiagram-T4ZO3ILL-OCTvbRxF.js → ganttDiagram-T4ZO3ILL-8EIcztcH.js} +1 -1
- package/dist/assets/gitGraph-7Q5UKJZL-BH9A1SAZ.js +1 -0
- package/dist/assets/{gitGraphDiagram-UUTBAWPF-wpqI2kyI.js → gitGraphDiagram-UUTBAWPF-DO9ODqYw.js} +1 -1
- package/dist/assets/graphlib-bPBqlJKT.js +1 -0
- package/dist/assets/identity-Me9aart9.js +1 -0
- package/dist/assets/index-oKG1C41_.js +273 -0
- package/dist/assets/info-OMHHGYJF-BvKR-zWh.js +1 -0
- package/dist/assets/infoDiagram-42DDH7IO-pRTXCm5C.js +2 -0
- package/dist/assets/isEmpty-Cu0k-j1j.js +1 -0
- package/dist/assets/{ishikawaDiagram-UXIWVN3A-Epc23N_0.js → ishikawaDiagram-UXIWVN3A-BP2YE5QI.js} +2 -2
- package/dist/assets/{journeyDiagram-VCZTEJTY-BkMxoaPq.js → journeyDiagram-VCZTEJTY-B3l2juoL.js} +1 -1
- package/dist/assets/{kanban-definition-6JOO6SKY-C8dW_26n.js → kanban-definition-6JOO6SKY-BpIpEOZZ.js} +4 -4
- package/dist/assets/{line-DNzQATGr.js → line-otOkzGl8.js} +1 -1
- package/dist/assets/mermaid-parser.core-xWsW24Gq.js +4 -0
- package/dist/assets/{mindmap-definition-QFDTVHPH-CvpNtrKT.js → mindmap-definition-QFDTVHPH-B9khyC7X.js} +3 -3
- package/dist/assets/packet-4T2RLAQJ-D8Dw3nmf.js +1 -0
- package/dist/assets/pie-ZZUOXDRM-ZghowlAE.js +1 -0
- package/dist/assets/{pieDiagram-DEJITSTG-eENymoXZ.js → pieDiagram-DEJITSTG-v32hL3i7.js} +1 -1
- package/dist/assets/{quadrantDiagram-34T5L4WZ-c0iZxo2I.js → quadrantDiagram-34T5L4WZ-DIL3GDFt.js} +1 -1
- package/dist/assets/radar-PYXPWWZC-D-PK3JOd.js +1 -0
- package/dist/assets/reduce-CImcgAcU.js +1 -0
- package/dist/assets/{requirementDiagram-MS252O5E-CmRO3hLp.js → requirementDiagram-MS252O5E-D8os2-4y.js} +2 -2
- package/dist/assets/{sankeyDiagram-XADWPNL6-woJZoQ58.js → sankeyDiagram-XADWPNL6-BV70D4l5.js} +1 -1
- package/dist/assets/{sequenceDiagram-FGHM5R23-B7qNcwNo.js → sequenceDiagram-FGHM5R23-Cwu8hQW1.js} +1 -1
- package/dist/assets/stateDiagram-FHFEXIEX-oYUWv7Fb.js +1 -0
- package/dist/assets/stateDiagram-v2-QKLJ7IA2-CFUTpFu-.js +1 -0
- package/dist/assets/{timeline-definition-GMOUNBTQ-CQWqDPGG.js → timeline-definition-GMOUNBTQ-CxSdKxpL.js} +1 -1
- package/dist/assets/treeView-SZITEDCU-uVgaJQzG.js +1 -0
- package/dist/assets/treemap-W4RFUUIX-Dcad_9AN.js +1 -0
- package/dist/assets/vennDiagram-DHZGUBPP-D4wgD7QI.js +34 -0
- package/dist/assets/wardley-RL74JXVD-CFXrK8mx.js +1 -0
- package/dist/assets/{wardleyDiagram-NUSXRM2D-DNhPIFCg.js → wardleyDiagram-NUSXRM2D-5Q201ea3.js} +1 -1
- package/dist/assets/{xychartDiagram-5P7HB3ND-BDblAZ11.js → xychartDiagram-5P7HB3ND-BPZv_axd.js} +3 -3
- package/dist/index.html +16 -12
- package/package.json +99 -92
- package/scripts/stubs/node-domexception/index.cjs +18 -0
- package/scripts/stubs/node-domexception/package.json +7 -0
- package/skills/hiro-task-manager-cli/SKILL.md +97 -0
- package/skills/hiro-task-manager-cli/reference/boards.md +143 -0
- package/skills/hiro-task-manager-cli/reference/cli-access-policy.md +72 -0
- package/skills/hiro-task-manager-cli/reference/errors.md +85 -0
- package/skills/hiro-task-manager-cli/reference/lists.md +106 -0
- package/skills/hiro-task-manager-cli/reference/releases.md +87 -0
- package/skills/hiro-task-manager-cli/reference/search.md +38 -0
- package/skills/hiro-task-manager-cli/reference/statuses.md +25 -0
- package/skills/hiro-task-manager-cli/reference/tasks.md +144 -0
- package/skills/hiro-task-manager-cli/reference/trash.md +50 -0
- package/src/cli/bootstrap/launcher.test.ts +66 -0
- package/src/cli/bootstrap/launcher.ts +389 -35
- package/src/cli/bootstrap/program.test.ts +46 -0
- package/src/cli/bootstrap/program.ts +54 -1
- package/src/cli/bootstrap/runtime.test.ts +15 -0
- package/src/cli/bootstrap/runtime.ts +27 -1
- package/src/cli/commands/query.ts +56 -56
- package/src/cli/commands/server.ts +27 -19
- package/src/cli/handlers/boards.test.ts +669 -669
- package/src/cli/handlers/cli-wiring.test.ts +1 -1
- package/src/cli/handlers/search.test.ts +374 -374
- package/src/cli/handlers/search.ts +17 -17
- package/src/cli/handlers/server.test.ts +55 -13
- package/src/cli/handlers/server.ts +16 -3
- package/src/cli/lib/api-client.test.ts +35 -2
- package/src/cli/lib/api-client.ts +43 -10
- package/src/cli/lib/cli-http-errors.test.ts +85 -85
- package/src/cli/lib/command-helpers.ts +161 -154
- package/src/cli/lib/config.ts +4 -0
- package/src/cli/lib/launcherUi.test.ts +74 -0
- package/src/cli/lib/launcherUi.ts +213 -0
- package/src/cli/lib/process.test.ts +24 -5
- package/src/cli/lib/process.ts +86 -55
- package/src/cli/ports/process.ts +8 -2
- package/src/cli/subprocess.real-stack.test.ts +611 -598
- package/src/cli/subprocess.smoke.test.ts +954 -969
- package/src/cli/types/config.ts +2 -6
- package/src/client/components/auth/AuthScreen.tsx +3 -3
- package/src/client/components/board/BoardStatsChips.tsx +233 -233
- package/src/client/components/board/BoardStatsContext.tsx +41 -41
- package/src/client/components/board/boardHeaderButtonStyles.ts +38 -38
- package/src/client/components/board/shortcuts/useBoardShortcutKeydown.ts +49 -49
- package/src/client/components/board/useBoardCanvasPanScroll.ts +108 -108
- package/src/client/components/board/useBoardTaskContainerDroppableReact.ts +33 -33
- package/src/client/components/board/useBoardTaskSortableReact.ts +26 -26
- package/src/client/components/multi-select.tsx +1206 -1206
- package/src/client/components/routing/BoardPage.tsx +20 -20
- package/src/client/components/routing/NavigationRegistrar.tsx +13 -13
- package/src/client/components/settings/SettingsPage.tsx +1 -1
- package/src/client/components/task/TaskCard.tsx +643 -643
- package/src/client/components/ui/badge.tsx +49 -49
- package/src/client/components/ui/button.tsx +65 -65
- package/src/client/components/ui/command.tsx +193 -193
- package/src/client/components/ui/dialog.tsx +163 -163
- package/src/client/components/ui/input-group.tsx +155 -155
- package/src/client/components/ui/input.tsx +19 -19
- package/src/client/components/ui/popover.tsx +87 -87
- package/src/client/components/ui/separator.tsx +28 -28
- package/src/client/components/ui/textarea.tsx +18 -18
- package/src/client/index.css +248 -248
- package/src/client/lib/appNavigate.ts +16 -16
- package/src/client/lib/taskCardDate.ts +111 -111
- package/src/client/lib/utils.ts +6 -6
- package/src/server/auth.ts +351 -302
- package/src/server/bootstrapDev.ts +11 -2
- package/src/server/bootstrapInstalled.ts +6 -1
- package/src/server/index.ts +33 -7
- package/src/server/migrations/013_cli_policy_and_provenance.ts +2 -2
- package/src/server/migrations/019_cli_global_create_board_default_on.ts +14 -0
- package/src/server/migrations/registry.ts +43 -41
- package/src/server/parseBootstrapProfile.ts +42 -0
- package/src/server/storage/cliPolicy.ts +2 -1
- package/src/shared/runtimeConfig.ts +256 -237
- package/src/shared/runtimeIdentity.test.ts +47 -0
- package/src/shared/runtimeIdentity.ts +35 -0
- package/src/shared/serverStatus.ts +21 -0
- package/src/shared/skillsInstall.ts +70 -0
- package/src/shared/terminalColors.ts +24 -0
- package/dist/assets/architecture-YZFGNWBL-3h1eIYfB.js +0 -1
- package/dist/assets/architectureDiagram-Q4EWVU46-DSQ1_74_.js +0 -36
- package/dist/assets/channel-yBmN_ln0.js +0 -1
- package/dist/assets/classDiagram-6PBFFD2Q-CotFZI8-.js +0 -1
- package/dist/assets/classDiagram-v2-HSJHXN6E-DAPzeDGn.js +0 -1
- package/dist/assets/clone-BRQpYu_n.js +0 -1
- package/dist/assets/cytoscape.esm-BIYWHPG0.js +0 -321
- package/dist/assets/dagre-rhyPjnsQ.js +0 -1
- package/dist/assets/diagram-5BDNPKRD-Ky3EXXj0.js +0 -10
- package/dist/assets/diagram-G4DWMVQ6-t7LbT0Uz.js +0 -24
- package/dist/assets/diagram-MMDJMWI5-CdnLXEMx.js +0 -43
- package/dist/assets/diagram-TYMM5635-CnzTqJBM.js +0 -24
- package/dist/assets/gitGraph-7Q5UKJZL-CG8f8JF7.js +0 -1
- package/dist/assets/graphlib-COiJG5Qv.js +0 -1
- package/dist/assets/identity-D4WOnl_h.js +0 -1
- package/dist/assets/index-lyyIVcc_.js +0 -304
- package/dist/assets/info-OMHHGYJF-C8_SHoRO.js +0 -1
- package/dist/assets/infoDiagram-42DDH7IO-BbvTdpSV.js +0 -2
- package/dist/assets/mermaid-parser.core-6Tn8epr_.js +0 -4
- package/dist/assets/packet-4T2RLAQJ-BvpAX0kJ.js +0 -1
- package/dist/assets/pie-ZZUOXDRM-Ow26Yf-E.js +0 -1
- package/dist/assets/radar-PYXPWWZC-e_ron5jQ.js +0 -1
- package/dist/assets/reduce-BDOBPIXr.js +0 -1
- package/dist/assets/stateDiagram-FHFEXIEX-CYfGMoR8.js +0 -1
- package/dist/assets/stateDiagram-v2-QKLJ7IA2-CO1W_n55.js +0 -1
- package/dist/assets/treeView-SZITEDCU-DsEr3xeq.js +0 -1
- package/dist/assets/treemap-W4RFUUIX-DV7nk2AB.js +0 -1
- package/dist/assets/vennDiagram-DHZGUBPP-BjTbuhcb.js +0 -34
- package/dist/assets/wardley-RL74JXVD-CrrFU9AE.js +0 -1
- /package/dist/assets/{chunk-4BX2VUAB-ean5NKtU.js → chunk-4BX2VUAB-C70mcfQR.js} +0 -0
- /package/dist/assets/{chunk-55IACEB6-CvSRyJqy.js → chunk-55IACEB6-CWfnqcLM.js} +0 -0
- /package/dist/assets/{chunk-BSJP7CBP-D8kBlJsf.js → chunk-BSJP7CBP-B0LrXV9y.js} +0 -0
- /package/dist/assets/{chunk-FMBD7UC4-DrNhFt1N.js → chunk-FMBD7UC4-_mV71Mwu.js} +0 -0
- /package/dist/assets/{chunk-QZHKN3VN-Csp3OYJY.js → chunk-QZHKN3VN-t2nrsegL.js} +0 -0
- /package/dist/assets/{katex-8mXVa4k3.js → katex-B2dtGfSp.js} +0 -0
- /package/dist/assets/{rough.esm-DtEqI08j.js → rough.esm-DEh6Frf9.js} +0 -0
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { runSearch } from "../lib/read/search";
|
|
2
|
-
import type { CliContext } from "./context";
|
|
3
|
-
|
|
4
|
-
export async function handleSearch(
|
|
5
|
-
ctx: CliContext,
|
|
6
|
-
queryParts: string[],
|
|
7
|
-
options: {
|
|
8
|
-
board?: string;
|
|
9
|
-
limit?: string;
|
|
10
|
-
offset?: string;
|
|
11
|
-
noPrefix?: boolean;
|
|
12
|
-
pageAll?: boolean;
|
|
13
|
-
fields?: string;
|
|
14
|
-
},
|
|
15
|
-
): Promise<void> {
|
|
16
|
-
await runSearch(ctx, queryParts, options);
|
|
17
|
-
}
|
|
1
|
+
import { runSearch } from "../lib/read/search";
|
|
2
|
+
import type { CliContext } from "./context";
|
|
3
|
+
|
|
4
|
+
export async function handleSearch(
|
|
5
|
+
ctx: CliContext,
|
|
6
|
+
queryParts: string[],
|
|
7
|
+
options: {
|
|
8
|
+
board?: string;
|
|
9
|
+
limit?: string;
|
|
10
|
+
offset?: string;
|
|
11
|
+
noPrefix?: boolean;
|
|
12
|
+
pageAll?: boolean;
|
|
13
|
+
fields?: string;
|
|
14
|
+
},
|
|
15
|
+
): Promise<void> {
|
|
16
|
+
await runSearch(ctx, queryParts, options);
|
|
17
|
+
}
|
|
@@ -4,6 +4,8 @@ import { createTestCliRuntime } from "../lib/runtime";
|
|
|
4
4
|
import { createDefaultCliContext } from "./context";
|
|
5
5
|
import { handleServerStart, handleServerStatus, handleServerStop } from "./server";
|
|
6
6
|
import type { CliContext } from "./context";
|
|
7
|
+
import type { ServerStartMode } from "../ports/process";
|
|
8
|
+
import type { RunningServerStatus } from "../../shared/serverStatus";
|
|
7
9
|
|
|
8
10
|
function baseCtx(overrides: Partial<CliContext> = {}): CliContext {
|
|
9
11
|
return {
|
|
@@ -19,7 +21,14 @@ function baseCtx(overrides: Partial<CliContext> = {}): CliContext {
|
|
|
19
21
|
|
|
20
22
|
describe("handleServerStatus", () => {
|
|
21
23
|
test("prints status from readServerStatus", async () => {
|
|
22
|
-
const status = {
|
|
24
|
+
const status = {
|
|
25
|
+
pid: 42,
|
|
26
|
+
port: 3020,
|
|
27
|
+
running: true,
|
|
28
|
+
runtime: "installed",
|
|
29
|
+
source: "installed",
|
|
30
|
+
url: "http://127.0.0.1:3020",
|
|
31
|
+
} satisfies RunningServerStatus;
|
|
23
32
|
let printed: unknown;
|
|
24
33
|
const ctx = baseCtx({
|
|
25
34
|
printJson: (d) => {
|
|
@@ -37,15 +46,22 @@ describe("handleServerStatus", () => {
|
|
|
37
46
|
describe("handleServerStart / handleServerStop", () => {
|
|
38
47
|
test("background start passes dataDir + port and prints JSON status", async () => {
|
|
39
48
|
let startArgs: ConfigOverrides | undefined;
|
|
40
|
-
let
|
|
41
|
-
const status = {
|
|
49
|
+
let startMode: ServerStartMode | undefined;
|
|
50
|
+
const status = {
|
|
51
|
+
pid: 99,
|
|
52
|
+
port: 3040,
|
|
53
|
+
running: true,
|
|
54
|
+
runtime: "installed",
|
|
55
|
+
source: "installed",
|
|
56
|
+
url: "http://127.0.0.1:3040",
|
|
57
|
+
} satisfies RunningServerStatus;
|
|
42
58
|
let printed: unknown;
|
|
43
59
|
const ctx = baseCtx({
|
|
44
60
|
resolveDataDir: () => "/custom/data",
|
|
45
61
|
resolvePort: () => 3040,
|
|
46
|
-
startServer: async (opts,
|
|
62
|
+
startServer: async (opts, mode) => {
|
|
47
63
|
startArgs = opts;
|
|
48
|
-
|
|
64
|
+
startMode = mode;
|
|
49
65
|
return status;
|
|
50
66
|
},
|
|
51
67
|
printJson: (d) => {
|
|
@@ -59,26 +75,52 @@ describe("handleServerStart / handleServerStop", () => {
|
|
|
59
75
|
dataDir: "/custom/data",
|
|
60
76
|
port: 3040,
|
|
61
77
|
} satisfies ConfigOverrides);
|
|
62
|
-
expect(
|
|
78
|
+
expect(startMode).toBe("background");
|
|
63
79
|
expect(printed).toEqual(status);
|
|
64
80
|
});
|
|
65
81
|
|
|
66
|
-
test("
|
|
67
|
-
let
|
|
82
|
+
test("default start uses background mode", async () => {
|
|
83
|
+
let startMode: ServerStartMode | undefined;
|
|
84
|
+
let printed: unknown;
|
|
68
85
|
const ctx = baseCtx({
|
|
69
|
-
startServer: async (_opts,
|
|
70
|
-
|
|
71
|
-
return {
|
|
86
|
+
startServer: async (_opts, mode) => {
|
|
87
|
+
startMode = mode;
|
|
88
|
+
return {
|
|
89
|
+
pid: 88,
|
|
90
|
+
port: 3020,
|
|
91
|
+
running: true,
|
|
92
|
+
runtime: "installed",
|
|
93
|
+
source: "installed",
|
|
94
|
+
url: "http://127.0.0.1:3020",
|
|
95
|
+
} satisfies RunningServerStatus;
|
|
96
|
+
},
|
|
97
|
+
printJson: (d) => {
|
|
98
|
+
printed = d;
|
|
72
99
|
},
|
|
73
100
|
});
|
|
74
101
|
|
|
75
102
|
await handleServerStart(ctx, {});
|
|
76
103
|
|
|
77
|
-
expect(
|
|
104
|
+
expect(startMode).toBe("background");
|
|
105
|
+
expect(printed).toBeTruthy();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("foreground start awaits startServer with foreground mode", async () => {
|
|
109
|
+
let startMode: ServerStartMode | undefined;
|
|
110
|
+
const ctx = baseCtx({
|
|
111
|
+
startServer: async (_opts, mode) => {
|
|
112
|
+
startMode = mode;
|
|
113
|
+
return { running: false as const };
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
await handleServerStart(ctx, { foreground: true });
|
|
118
|
+
|
|
119
|
+
expect(startMode).toBe("foreground");
|
|
78
120
|
});
|
|
79
121
|
|
|
80
122
|
test("stop prints status from stopServer", async () => {
|
|
81
|
-
const status = { running: false };
|
|
123
|
+
const status = { running: false as const };
|
|
82
124
|
let printed: unknown;
|
|
83
125
|
const ctx = baseCtx({
|
|
84
126
|
stopServer: async () => status,
|
|
@@ -1,23 +1,36 @@
|
|
|
1
|
+
import { CLI_ERR } from "../types/errors";
|
|
2
|
+
import { CliError } from "../lib/output";
|
|
1
3
|
import type { CliContext } from "./context";
|
|
2
4
|
|
|
3
5
|
export async function handleServerStart(
|
|
4
6
|
ctx: CliContext,
|
|
5
7
|
options: {
|
|
6
8
|
background?: boolean;
|
|
9
|
+
foreground?: boolean;
|
|
7
10
|
dataDir?: string;
|
|
8
11
|
},
|
|
9
12
|
): Promise<void> {
|
|
10
13
|
const port = ctx.resolvePort();
|
|
11
14
|
const dataDir = ctx.resolveDataDir({ dataDir: options.dataDir });
|
|
12
15
|
|
|
13
|
-
if (options.background) {
|
|
14
|
-
|
|
16
|
+
if (options.background && options.foreground) {
|
|
17
|
+
throw new CliError("Choose either --background or --foreground", 2, {
|
|
18
|
+
code: CLI_ERR.invalidValue,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Default to background so `hirotm server start` stays script/agent-friendly
|
|
23
|
+
// unless the caller explicitly asks to keep logs attached.
|
|
24
|
+
const startMode = options.foreground ? "foreground" : "background";
|
|
25
|
+
|
|
26
|
+
if (startMode === "background") {
|
|
27
|
+
const status = await ctx.startServer({ dataDir, port }, startMode);
|
|
15
28
|
ctx.printJson(status);
|
|
16
29
|
return;
|
|
17
30
|
}
|
|
18
31
|
|
|
19
32
|
// Run in production mode so the installed CLI uses a stable home data directory by default.
|
|
20
|
-
await ctx.startServer({ dataDir, port },
|
|
33
|
+
await ctx.startServer({ dataDir, port }, startMode);
|
|
21
34
|
}
|
|
22
35
|
|
|
23
36
|
export async function handleServerStatus(ctx: CliContext): Promise<void> {
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
fetchApiMutate,
|
|
6
6
|
fetchApiTrashMutate,
|
|
7
7
|
fetchHealth,
|
|
8
|
+
fetchHealthStatus,
|
|
8
9
|
} from "./api-client";
|
|
9
10
|
import { CliError } from "./output";
|
|
10
11
|
|
|
@@ -200,7 +201,14 @@ describe("api-client (mock fetch)", () => {
|
|
|
200
201
|
|
|
201
202
|
test("fetchHealth true when ok JSON", async () => {
|
|
202
203
|
setMockFetch(async () =>
|
|
203
|
-
new Response(JSON.stringify({
|
|
204
|
+
new Response(JSON.stringify({
|
|
205
|
+
pid: 4321,
|
|
206
|
+
port: 20000,
|
|
207
|
+
running: true,
|
|
208
|
+
runtime: "installed",
|
|
209
|
+
source: "installed",
|
|
210
|
+
url: "http://127.0.0.1:20000",
|
|
211
|
+
}), {
|
|
204
212
|
status: 200,
|
|
205
213
|
headers: { "content-type": "application/json" },
|
|
206
214
|
}),
|
|
@@ -209,9 +217,34 @@ describe("api-client (mock fetch)", () => {
|
|
|
209
217
|
await expect(fetchHealth({ port: 20000 })).resolves.toBe(true);
|
|
210
218
|
});
|
|
211
219
|
|
|
220
|
+
test("fetchHealthStatus returns runtime/source metadata when provided", async () => {
|
|
221
|
+
setMockFetch(async () =>
|
|
222
|
+
new Response(JSON.stringify({
|
|
223
|
+
pid: 12345,
|
|
224
|
+
port: 20000,
|
|
225
|
+
running: true,
|
|
226
|
+
runtime: "installed",
|
|
227
|
+
source: "repo",
|
|
228
|
+
url: "http://127.0.0.1:20000",
|
|
229
|
+
}), {
|
|
230
|
+
status: 200,
|
|
231
|
+
headers: { "content-type": "application/json" },
|
|
232
|
+
}),
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
await expect(fetchHealthStatus({ port: 20000 })).resolves.toEqual({
|
|
236
|
+
pid: 12345,
|
|
237
|
+
port: 20000,
|
|
238
|
+
running: true,
|
|
239
|
+
runtime: "installed",
|
|
240
|
+
source: "repo",
|
|
241
|
+
url: "http://127.0.0.1:20000",
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
|
|
212
245
|
test("fetchHealth false when not ok", async () => {
|
|
213
246
|
setMockFetch(async () =>
|
|
214
|
-
new Response(JSON.stringify({
|
|
247
|
+
new Response(JSON.stringify({ running: false }), {
|
|
215
248
|
status: 200,
|
|
216
249
|
headers: { "content-type": "application/json" },
|
|
217
250
|
}),
|
|
@@ -23,6 +23,7 @@ import { CLI_DEFAULTS } from "./constants";
|
|
|
23
23
|
import { CLI_ERR } from "../types/errors";
|
|
24
24
|
import { mapHttpStatusToCliFailure } from "./cli-http-errors";
|
|
25
25
|
import { CliError } from "./output";
|
|
26
|
+
import type { RunningServerStatus } from "../../shared/serverStatus";
|
|
26
27
|
|
|
27
28
|
/** Uses `CLI_DEFAULTS.API_FETCH_TIMEOUT_MS`; health polling uses short waits in `process.ts`. */
|
|
28
29
|
// Aligns with docs/cli-error-handling.md: timeouts surface as exit 7 + code request_timeout.
|
|
@@ -54,16 +55,16 @@ function buildBaseUrl(overrides: ConfigOverrides = {}): string {
|
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
function buildStartCommand(overrides: ConfigOverrides = {}): string {
|
|
57
|
-
const command = ["hirotm", "server", "start"
|
|
58
|
+
const command = ["hirotm", "server", "start"];
|
|
58
59
|
const profile = resolveProfileName(overrides);
|
|
59
60
|
const runtime = resolveRuntimeKind(overrides);
|
|
60
61
|
|
|
61
|
-
if (
|
|
62
|
+
if (profile !== "default") {
|
|
62
63
|
command.push("--profile", profile);
|
|
63
64
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
if (runtime === "dev") {
|
|
66
|
+
command.push("--dev");
|
|
67
|
+
}
|
|
67
68
|
|
|
68
69
|
return command.join(" ");
|
|
69
70
|
}
|
|
@@ -177,6 +178,8 @@ async function apiRequest<T>(
|
|
|
177
178
|
return (await response.json()) as T;
|
|
178
179
|
}
|
|
179
180
|
|
|
181
|
+
export type HealthStatus = RunningServerStatus;
|
|
182
|
+
|
|
180
183
|
export async function fetchApi<T>(
|
|
181
184
|
endpoint: string,
|
|
182
185
|
overrides: ConfigOverrides = {},
|
|
@@ -211,17 +214,47 @@ export async function fetchApiTrashMutate<T>(
|
|
|
211
214
|
});
|
|
212
215
|
}
|
|
213
216
|
|
|
214
|
-
export async function
|
|
217
|
+
export async function fetchHealthStatus(
|
|
218
|
+
overrides: ConfigOverrides = {},
|
|
219
|
+
): Promise<HealthStatus | null> {
|
|
215
220
|
const baseUrl = buildBaseUrl(overrides);
|
|
216
221
|
|
|
217
222
|
try {
|
|
218
223
|
const response = await fetch(`${baseUrl}/api/health`, {
|
|
219
224
|
headers: taskManagerClientHeaders(),
|
|
220
225
|
});
|
|
221
|
-
if (!response.ok) return
|
|
222
|
-
const body = (await response.json()) as {
|
|
223
|
-
|
|
226
|
+
if (!response.ok) return null;
|
|
227
|
+
const body = (await response.json()) as {
|
|
228
|
+
pid?: unknown;
|
|
229
|
+
port?: unknown;
|
|
230
|
+
running?: unknown;
|
|
231
|
+
runtime?: unknown;
|
|
232
|
+
source?: unknown;
|
|
233
|
+
url?: unknown;
|
|
234
|
+
};
|
|
235
|
+
if (
|
|
236
|
+
typeof body.pid !== "number" ||
|
|
237
|
+
typeof body.port !== "number" ||
|
|
238
|
+
body.running !== true ||
|
|
239
|
+
(body.runtime !== "dev" && body.runtime !== "installed") ||
|
|
240
|
+
(body.source !== "repo" && body.source !== "installed") ||
|
|
241
|
+
typeof body.url !== "string"
|
|
242
|
+
) {
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
return {
|
|
246
|
+
pid: body.pid,
|
|
247
|
+
port: body.port,
|
|
248
|
+
running: true,
|
|
249
|
+
runtime: body.runtime,
|
|
250
|
+
source: body.source,
|
|
251
|
+
url: body.url,
|
|
252
|
+
};
|
|
224
253
|
} catch {
|
|
225
|
-
return
|
|
254
|
+
return null;
|
|
226
255
|
}
|
|
227
256
|
}
|
|
257
|
+
|
|
258
|
+
export async function fetchHealth(overrides: ConfigOverrides = {}): Promise<boolean> {
|
|
259
|
+
return (await fetchHealthStatus(overrides))?.running === true;
|
|
260
|
+
}
|
|
@@ -1,85 +1,85 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { CLI_ERR } from "../types/errors";
|
|
3
|
-
import { enrichNotFoundError, mapHttpStatusToCliFailure } from "./cli-http-errors";
|
|
4
|
-
import { CliError } from "./output";
|
|
5
|
-
|
|
6
|
-
describe("mapHttpStatusToCliFailure", () => {
|
|
7
|
-
test.each([
|
|
8
|
-
[400, CLI_ERR.badRequest, 9, undefined],
|
|
9
|
-
[401, CLI_ERR.unauthenticated, 10, undefined],
|
|
10
|
-
[403, CLI_ERR.forbidden, 4, undefined],
|
|
11
|
-
[404, CLI_ERR.notFound, 3, undefined],
|
|
12
|
-
[408, CLI_ERR.requestTimeout, 7, true],
|
|
13
|
-
[409, CLI_ERR.conflict, 5, undefined],
|
|
14
|
-
[422, CLI_ERR.badRequest, 9, undefined],
|
|
15
|
-
[426, CLI_ERR.versionMismatch, 8, undefined],
|
|
16
|
-
[429, CLI_ERR.rateLimited, 1, true],
|
|
17
|
-
[502, CLI_ERR.internalError, 1, true],
|
|
18
|
-
[418, CLI_ERR.httpError, 1, undefined],
|
|
19
|
-
] as const)(
|
|
20
|
-
"status %i → code %s exit %i retryable %j",
|
|
21
|
-
(status, code, exit, retryable) => {
|
|
22
|
-
const { exitCode, details } = mapHttpStatusToCliFailure(status, {
|
|
23
|
-
status,
|
|
24
|
-
url: "http://127.0.0.1:1/api/x",
|
|
25
|
-
});
|
|
26
|
-
expect(exitCode).toBe(exit);
|
|
27
|
-
expect(details.code).toBe(code);
|
|
28
|
-
if (retryable === undefined) {
|
|
29
|
-
expect(details.retryable).toBeUndefined();
|
|
30
|
-
} else {
|
|
31
|
-
expect(details.retryable).toBe(retryable);
|
|
32
|
-
}
|
|
33
|
-
},
|
|
34
|
-
);
|
|
35
|
-
|
|
36
|
-
test("599 maps to internal_error (5xx branch)", () => {
|
|
37
|
-
const { exitCode, details } = mapHttpStatusToCliFailure(599, { status: 599 });
|
|
38
|
-
expect(exitCode).toBe(1);
|
|
39
|
-
expect(details.code).toBe(CLI_ERR.internalError);
|
|
40
|
-
expect(details.retryable).toBe(true);
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
test("preserves API code as serverCode, not duplicate top-level code", () => {
|
|
44
|
-
const { details } = mapHttpStatusToCliFailure(400, {
|
|
45
|
-
code: "CUSTOM",
|
|
46
|
-
status: 400,
|
|
47
|
-
});
|
|
48
|
-
expect(details.code).toBe(CLI_ERR.badRequest);
|
|
49
|
-
expect(details.serverCode).toBe("CUSTOM");
|
|
50
|
-
expect((details as Record<string, unknown>).code).toBe(CLI_ERR.badRequest);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
test("non-string code in body is ignored for serverCode", () => {
|
|
54
|
-
const { details } = mapHttpStatusToCliFailure(404, {
|
|
55
|
-
code: 123,
|
|
56
|
-
status: 404,
|
|
57
|
-
} as Record<string, unknown>);
|
|
58
|
-
expect(details.serverCode).toBeUndefined();
|
|
59
|
-
});
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
describe("enrichNotFoundError", () => {
|
|
63
|
-
test("merges context when details.code is not_found", () => {
|
|
64
|
-
const err = new CliError("Whatever", 3, { code: CLI_ERR.notFound });
|
|
65
|
-
try {
|
|
66
|
-
enrichNotFoundError(err, { board: "my-board", taskId: 9 });
|
|
67
|
-
expect.unreachable();
|
|
68
|
-
} catch (e) {
|
|
69
|
-
expect(e).toBeInstanceOf(CliError);
|
|
70
|
-
const c = e as CliError;
|
|
71
|
-
expect(c.message).toBe("Whatever");
|
|
72
|
-
expect(c.exitCode).toBe(3);
|
|
73
|
-
expect(c.details).toMatchObject({
|
|
74
|
-
code: CLI_ERR.notFound,
|
|
75
|
-
board: "my-board",
|
|
76
|
-
taskId: 9,
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
test("rethrows unchanged when code is not not_found", () => {
|
|
82
|
-
const err = new CliError("Nope", 4, { code: CLI_ERR.forbidden });
|
|
83
|
-
expect(() => enrichNotFoundError(err, { board: "x" })).toThrow(err);
|
|
84
|
-
});
|
|
85
|
-
});
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { CLI_ERR } from "../types/errors";
|
|
3
|
+
import { enrichNotFoundError, mapHttpStatusToCliFailure } from "./cli-http-errors";
|
|
4
|
+
import { CliError } from "./output";
|
|
5
|
+
|
|
6
|
+
describe("mapHttpStatusToCliFailure", () => {
|
|
7
|
+
test.each([
|
|
8
|
+
[400, CLI_ERR.badRequest, 9, undefined],
|
|
9
|
+
[401, CLI_ERR.unauthenticated, 10, undefined],
|
|
10
|
+
[403, CLI_ERR.forbidden, 4, undefined],
|
|
11
|
+
[404, CLI_ERR.notFound, 3, undefined],
|
|
12
|
+
[408, CLI_ERR.requestTimeout, 7, true],
|
|
13
|
+
[409, CLI_ERR.conflict, 5, undefined],
|
|
14
|
+
[422, CLI_ERR.badRequest, 9, undefined],
|
|
15
|
+
[426, CLI_ERR.versionMismatch, 8, undefined],
|
|
16
|
+
[429, CLI_ERR.rateLimited, 1, true],
|
|
17
|
+
[502, CLI_ERR.internalError, 1, true],
|
|
18
|
+
[418, CLI_ERR.httpError, 1, undefined],
|
|
19
|
+
] as const)(
|
|
20
|
+
"status %i → code %s exit %i retryable %j",
|
|
21
|
+
(status, code, exit, retryable) => {
|
|
22
|
+
const { exitCode, details } = mapHttpStatusToCliFailure(status, {
|
|
23
|
+
status,
|
|
24
|
+
url: "http://127.0.0.1:1/api/x",
|
|
25
|
+
});
|
|
26
|
+
expect(exitCode).toBe(exit);
|
|
27
|
+
expect(details.code).toBe(code);
|
|
28
|
+
if (retryable === undefined) {
|
|
29
|
+
expect(details.retryable).toBeUndefined();
|
|
30
|
+
} else {
|
|
31
|
+
expect(details.retryable).toBe(retryable);
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
test("599 maps to internal_error (5xx branch)", () => {
|
|
37
|
+
const { exitCode, details } = mapHttpStatusToCliFailure(599, { status: 599 });
|
|
38
|
+
expect(exitCode).toBe(1);
|
|
39
|
+
expect(details.code).toBe(CLI_ERR.internalError);
|
|
40
|
+
expect(details.retryable).toBe(true);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("preserves API code as serverCode, not duplicate top-level code", () => {
|
|
44
|
+
const { details } = mapHttpStatusToCliFailure(400, {
|
|
45
|
+
code: "CUSTOM",
|
|
46
|
+
status: 400,
|
|
47
|
+
});
|
|
48
|
+
expect(details.code).toBe(CLI_ERR.badRequest);
|
|
49
|
+
expect(details.serverCode).toBe("CUSTOM");
|
|
50
|
+
expect((details as Record<string, unknown>).code).toBe(CLI_ERR.badRequest);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("non-string code in body is ignored for serverCode", () => {
|
|
54
|
+
const { details } = mapHttpStatusToCliFailure(404, {
|
|
55
|
+
code: 123,
|
|
56
|
+
status: 404,
|
|
57
|
+
} as Record<string, unknown>);
|
|
58
|
+
expect(details.serverCode).toBeUndefined();
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
describe("enrichNotFoundError", () => {
|
|
63
|
+
test("merges context when details.code is not_found", () => {
|
|
64
|
+
const err = new CliError("Whatever", 3, { code: CLI_ERR.notFound });
|
|
65
|
+
try {
|
|
66
|
+
enrichNotFoundError(err, { board: "my-board", taskId: 9 });
|
|
67
|
+
expect.unreachable();
|
|
68
|
+
} catch (e) {
|
|
69
|
+
expect(e).toBeInstanceOf(CliError);
|
|
70
|
+
const c = e as CliError;
|
|
71
|
+
expect(c.message).toBe("Whatever");
|
|
72
|
+
expect(c.exitCode).toBe(3);
|
|
73
|
+
expect(c.details).toMatchObject({
|
|
74
|
+
code: CLI_ERR.notFound,
|
|
75
|
+
board: "my-board",
|
|
76
|
+
taskId: 9,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("rethrows unchanged when code is not not_found", () => {
|
|
82
|
+
const err = new CliError("Nope", 4, { code: CLI_ERR.forbidden });
|
|
83
|
+
expect(() => enrichNotFoundError(err, { board: "x" })).toThrow(err);
|
|
84
|
+
});
|
|
85
|
+
});
|