@hunsu/bridge 0.1.3 → 0.2.0-next.1

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/package.json CHANGED
@@ -1,24 +1,29 @@
1
1
  {
2
2
  "name": "@hunsu/bridge",
3
- "version": "0.1.3",
3
+ "version": "0.2.0-next.1",
4
4
  "private": false,
5
- "description": "Hunsu Bridge localhost runtime and Studio API server.",
5
+ "description": "Headless Hunsu Bridge daemon, authenticated CLI, and local Web API.",
6
6
  "license": "Apache-2.0",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/lhj6102/hunsu.git",
10
+ "directory": "apps/bridge"
11
+ },
7
12
  "type": "module",
8
13
  "bin": {
9
14
  "hunsu-bridge": "./dist/cli.js"
10
15
  },
11
16
  "exports": {
12
17
  ".": {
13
- "development": "./src/index.ts",
14
- "types": "./src/index.ts",
18
+ "types": "./dist/index.d.ts",
15
19
  "import": "./dist/index.js",
16
20
  "default": "./dist/index.js"
17
21
  }
18
22
  },
19
23
  "files": [
20
24
  "dist",
21
- "src"
25
+ "README.md",
26
+ "LICENSE"
22
27
  ],
23
28
  "publishConfig": {
24
29
  "access": "public"
@@ -26,17 +31,19 @@
26
31
  "engines": {
27
32
  "node": ">=22.18"
28
33
  },
29
- "dependencies": {
34
+ "devDependencies": {
35
+ "esbuild": "0.27.7",
30
36
  "@hunsu/codex-runner": "0.1.1",
37
+ "@hunsu/config": "0.1.1",
31
38
  "@hunsu/core": "0.1.1",
32
- "@hunsu/protocol-registry": "0.1.1",
33
39
  "@hunsu/protocol": "0.1.1",
34
- "@hunsu/config": "0.1.1"
40
+ "@hunsu/protocol-registry": "0.1.1"
35
41
  },
36
42
  "scripts": {
37
- "dev": "node --watch src/index.ts",
43
+ "dev": "node --conditions=development src/cli.ts dev",
38
44
  "bridge": "node --conditions=development src/cli.ts",
39
- "build": "tsc -p tsconfig.build.json",
45
+ "build": "node scripts/build.mjs",
46
+ "pack-smoke": "node scripts/pack-smoke.mjs",
40
47
  "typecheck": "tsc -p tsconfig.json --noEmit"
41
48
  }
42
49
  }
package/dist/cli.d.ts DELETED
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env node
2
- export {};
@@ -1,50 +0,0 @@
1
- import type { CommitSha } from "@hunsu/core";
2
- import type { ExecutionPlan, PathId, Result, MemberPath } from "@hunsu/protocol";
3
- export type ExecuteOrchestrationErrorCode = "invalid_start_state" | "active_execute_exists" | "team_output_invalid" | "execution_plan_invalid" | "goal_evaluation_invalid" | "path_dependency_unmet" | "terminal_output_missing" | "max_attempts_exhausted" | "side_effect_failed";
4
- export type ExecuteOrchestrationError = {
5
- code: ExecuteOrchestrationErrorCode;
6
- message: string;
7
- details?: unknown;
8
- };
9
- export type ExecuteStepResult<T> = Result<T, ExecuteOrchestrationError>;
10
- export type ExecutionPlanStepResult = {
11
- type: "done";
12
- terminalPath?: MemberPath;
13
- finalResponse?: string;
14
- } | {
15
- type: "next";
16
- execution: ExecutionPlan;
17
- terminalPath?: MemberPath;
18
- finalResponse?: string;
19
- } | {
20
- type: "fail";
21
- reason: ExecuteOrchestrationError;
22
- terminalPath?: MemberPath;
23
- finalResponse?: string;
24
- };
25
- export type ExecuteRunStatus = "running" | "paused" | "arrived" | "accident" | "failed" | "discarded" | "finished" | "stopped";
26
- export type PathRunPlan = {
27
- path: MemberPath;
28
- dependencyPathIds: PathId[];
29
- dependencyOutputs: string[];
30
- isTerminal: boolean;
31
- };
32
- export type PathRunOutcome = {
33
- pathId: PathId;
34
- finalResponse: string;
35
- commit: CommitSha;
36
- parentCommit?: CommitSha;
37
- treeChanged: boolean;
38
- };
39
- export type ExecuteLoopDecision = {
40
- type: "run_team_planning";
41
- } | {
42
- type: "run_member_path";
43
- path: PathRunPlan;
44
- } | {
45
- type: "record_accident";
46
- reason: ExecuteOrchestrationError;
47
- };
48
- export declare function executeError(code: ExecuteOrchestrationErrorCode, message: string, details?: unknown): ExecuteOrchestrationError;
49
- export declare function executeErrorToError(error: ExecuteOrchestrationError): Error;
50
- export declare function unwrapExecuteResult<T>(result: ExecuteStepResult<T>): T;
@@ -1,17 +0,0 @@
1
- export function executeError(code, message, details) {
2
- return details === undefined ? { code, message } : { code, message, details };
3
- }
4
- export function executeErrorToError(error) {
5
- const wrapped = new Error(error.message);
6
- wrapped.name = `ExecuteOrchestrationError:${error.code}`;
7
- if (error.details !== undefined) {
8
- wrapped.details = error.details;
9
- }
10
- return wrapped;
11
- }
12
- export function unwrapExecuteResult(result) {
13
- if (result.ok) {
14
- return result.value;
15
- }
16
- throw executeErrorToError(result.error);
17
- }
@@ -1,27 +0,0 @@
1
- import { type BoardProjection, type Destination, type LineRecord, type NodeRecord, type PositiveInteger } from "@hunsu/protocol";
2
- import { type ExecuteRunStatus, type ExecuteLoopDecision, type ExecuteStepResult } from "./execute-model.ts";
3
- export type ExecuteStartExistingRun = {
4
- sourceNodeId?: string;
5
- status: ExecuteRunStatus;
6
- };
7
- export type ExecuteStartPlanInput = {
8
- board: BoardProjection;
9
- line: LineRecord;
10
- lineNode: NodeRecord | undefined;
11
- existingRuns: readonly ExecuteStartExistingRun[];
12
- activeDestinations: readonly Destination[];
13
- selectedDestinationIds?: readonly string[];
14
- formatMovePosition?: (node: NodeRecord) => string;
15
- };
16
- export type ExecuteStartPlan = {
17
- lineNode: NodeRecord;
18
- selectedDestinationIds: string[];
19
- targetMoveOrdinal: PositiveInteger;
20
- };
21
- export declare function planExecuteStart(input: ExecuteStartPlanInput): ExecuteStepResult<ExecuteStartPlan>;
22
- export declare function ensureTerminalOutput(finalResponse: string | undefined): ExecuteStepResult<string>;
23
- export declare function planMaxAttemptsExceeded(input: {
24
- maxAttemptCount: PositiveInteger;
25
- }): Extract<ExecuteLoopDecision, {
26
- type: "record_accident";
27
- }>;
@@ -1,65 +0,0 @@
1
- import { err, makePositiveInteger, ok } from "@hunsu/protocol";
2
- import { executeError } from "./execute-model.js";
3
- export function planExecuteStart(input) {
4
- const { line, lineNode } = input;
5
- if (!lineNode) {
6
- return err(executeError("invalid_start_state", `No execute-ready MOVE found for Team route ${line.id}`));
7
- }
8
- if (line.status === "failed" || line.status === "complete" || line.status === "abandoned") {
9
- return err(executeError("invalid_start_state", `Cannot start Execute from ${line.id} because the route is ${line.status}`));
10
- }
11
- const nodesWithNextMove = new Set(input.board.edges.filter(edge => edge.type === "move").map(edge => edge.fromNodeId));
12
- if (nodesWithNextMove.has(lineNode.id)) {
13
- return err(executeError("invalid_start_state", `Cannot start Execute from ${formatMovePosition(lineNode, input.formatMovePosition)} because Arrived or Accident already exists`));
14
- }
15
- const activeRun = input.existingRuns.find(existing => existing.sourceNodeId === lineNode.id && (existing.status === "running" || existing.status === "paused"));
16
- if (activeRun) {
17
- return err(executeError("active_execute_exists", `A Execute is already active for ${formatMovePosition(lineNode, input.formatMovePosition)}`));
18
- }
19
- const targetMoveOrdinal = makePositiveInteger(lineNode.ordinal + 1, "targetMoveOrdinal");
20
- if (!targetMoveOrdinal.ok) {
21
- return err(executeError("invalid_start_state", targetMoveOrdinal.error.message));
22
- }
23
- const selectedDestinationIds = normalizeSelectedDestinationIds(input.selectedDestinationIds, input.activeDestinations);
24
- if (!selectedDestinationIds.ok) {
25
- return selectedDestinationIds;
26
- }
27
- return ok({
28
- lineNode,
29
- selectedDestinationIds: selectedDestinationIds.value,
30
- targetMoveOrdinal: targetMoveOrdinal.value
31
- });
32
- }
33
- export function ensureTerminalOutput(finalResponse) {
34
- if (!finalResponse) {
35
- return err(executeError("terminal_output_missing", "ExecutionPlan completed without a terminal output"));
36
- }
37
- return ok(finalResponse);
38
- }
39
- export function planMaxAttemptsExceeded(input) {
40
- return {
41
- type: "record_accident",
42
- reason: executeError("max_attempts_exhausted", `Execute exceeded max attempt count ${input.maxAttemptCount} without recording an Arrived MOVE`, { maxAttemptCount: input.maxAttemptCount })
43
- };
44
- }
45
- function normalizeSelectedDestinationIds(selectedDestinationIds, activeDestinations) {
46
- const nextDestination = nextQueuedDestination(activeDestinations);
47
- if (selectedDestinationIds && selectedDestinationIds.length > 0) {
48
- if (selectedDestinationIds.length !== 1) {
49
- return err(executeError("invalid_start_state", "Execute must select exactly one Destination"));
50
- }
51
- return nextDestination && selectedDestinationIds[0] === nextDestination.id
52
- ? ok([...selectedDestinationIds])
53
- : err(executeError("invalid_start_state", "Execute must select the next Destination in the queue"));
54
- }
55
- return ok(nextDestination ? [nextDestination.id] : []);
56
- }
57
- function nextQueuedDestination(activeDestinations) {
58
- return [...activeDestinations].sort(destinationQueueSort)[0];
59
- }
60
- function destinationQueueSort(left, right) {
61
- return (right.priority ?? 0) - (left.priority ?? 0);
62
- }
63
- function formatMovePosition(node, formatter) {
64
- return formatter ? formatter(node) : `${node.teamName ?? "Team"} MOVE ${node.ordinal}`;
65
- }
@@ -1,6 +0,0 @@
1
- import { type GoalEvaluation, type GoalExecutionPlan, type ExecutionPlan, type MemberPath, type QueueExecutionPlan } from "@hunsu/protocol";
2
- import { type ExecuteStepResult } from "./execute-model.ts";
3
- export declare function parseExecutionPlan(finalResponse: string): ExecuteStepResult<ExecutionPlan>;
4
- export declare function parseGoalEvaluation(finalResponse: string): ExecuteStepResult<GoalEvaluation>;
5
- export declare function memberPathForGoalRole(execution: GoalExecutionPlan, role: "assignee" | "evaluator", pathId: string, requires: MemberPath["requires"]): MemberPath;
6
- export declare function nextQueueExecution(queue: QueueExecutionPlan, headResult: ExecutionPlan | undefined): ExecutionPlan | undefined;
@@ -1,337 +0,0 @@
1
- import { err, makeNonEmptyText, makeNonNegativeInteger, ok } from "@hunsu/protocol";
2
- import { executeError } from "./execute-model.js";
3
- export function parseExecutionPlan(finalResponse) {
4
- const parsed = parseJsonObject(finalResponse, "Team planning final response must be a JSON ExecutionPlan object");
5
- if (!parsed.ok) {
6
- return parsed;
7
- }
8
- return decodeExecutionPlan(parsed.value, "execution");
9
- }
10
- export function parseGoalEvaluation(finalResponse) {
11
- const parsed = parseJsonObject(finalResponse, "Goal evaluator final response must be a JSON object");
12
- if (!parsed.ok) {
13
- return parsed;
14
- }
15
- const record = parsed.value;
16
- if (record.type === "pass") {
17
- if (typeof record.summary !== "string" || record.summary.trim() === "") {
18
- return err(executeError("goal_evaluation_invalid", "pass evaluation must include a non-empty summary"));
19
- }
20
- const evidence = decodeOptionalStringArray(record.evidence, "evidence");
21
- if (!evidence.ok) {
22
- return evidence;
23
- }
24
- return ok({
25
- type: "pass",
26
- summary: record.summary,
27
- evidence: evidence.value
28
- });
29
- }
30
- if (record.type === "fail") {
31
- if (typeof record.reason !== "string" || record.reason.trim() === "") {
32
- return err(executeError("goal_evaluation_invalid", "fail evaluation must include a non-empty reason"));
33
- }
34
- if (typeof record.feedback !== "string" || record.feedback.trim() === "") {
35
- return err(executeError("goal_evaluation_invalid", "fail evaluation must include non-empty feedback"));
36
- }
37
- if (record.nextGoal !== undefined && typeof record.nextGoal !== "string") {
38
- return err(executeError("goal_evaluation_invalid", "fail evaluation nextGoal must be a string when present"));
39
- }
40
- const evidence = decodeOptionalStringArray(record.evidence, "evidence");
41
- if (!evidence.ok) {
42
- return evidence;
43
- }
44
- return ok({
45
- type: "fail",
46
- reason: record.reason,
47
- feedback: record.feedback,
48
- nextGoal: typeof record.nextGoal === "string" && record.nextGoal.trim() !== "" ? record.nextGoal : undefined,
49
- evidence: evidence.value
50
- });
51
- }
52
- return err(executeError("goal_evaluation_invalid", "Goal evaluator response type must be pass or fail"));
53
- }
54
- export function memberPathForGoalRole(execution, role, pathId, requires) {
55
- if (role === "assignee") {
56
- return {
57
- id: pathId,
58
- executorId: execution.assignee.executorId,
59
- goal: execution.assignee.goal,
60
- requires
61
- };
62
- }
63
- const roleConfig = execution.evaluator;
64
- if (!roleConfig) {
65
- throw new Error(`Goal ${execution.id} does not define an evaluator`);
66
- }
67
- return {
68
- id: pathId,
69
- executorId: roleConfig.executorId,
70
- goal: roleConfig.prompt,
71
- requires
72
- };
73
- }
74
- export function nextQueueExecution(queue, headResult) {
75
- const [, ...tail] = queue.items;
76
- if (headResult) {
77
- return { ...queue, items: [headResult, ...tail] };
78
- }
79
- if (tail.length === 0) {
80
- return undefined;
81
- }
82
- return { ...queue, items: tail };
83
- }
84
- function decodeExecutionPlan(value, path) {
85
- if (!isRecord(value)) {
86
- return err(executeError("execution_plan_invalid", `${path} must be an object`));
87
- }
88
- if (value.kind === "queue") {
89
- return decodeQueueExecutionPlan(value, path);
90
- }
91
- if (value.kind === "goal") {
92
- return decodeGoalExecutionPlan(value, path);
93
- }
94
- if (value.kind === "continuation") {
95
- return decodeExecutionContinuation(value, path);
96
- }
97
- return err(executeError("execution_plan_invalid", `${path}.kind must be queue, goal, or continuation`));
98
- }
99
- function decodeQueueExecutionPlan(record, path) {
100
- const id = decodeNonEmptyString(record.id, `${path}.id`);
101
- if (!id.ok) {
102
- return id;
103
- }
104
- if (!Array.isArray(record.items)) {
105
- return err(executeError("execution_plan_invalid", `${path}.items must be an array`));
106
- }
107
- const items = [];
108
- for (const [index, item] of record.items.entries()) {
109
- const decoded = decodeExecutionPlan(item, `${path}.items[${index}]`);
110
- if (!decoded.ok) {
111
- return decoded;
112
- }
113
- items.push(decoded.value);
114
- }
115
- return ok({ kind: "queue", id: id.value, items });
116
- }
117
- function decodeGoalExecutionPlan(record, path) {
118
- if (record.stage === "needs_evaluation") {
119
- return decodeGoalPathEvaluationStage(record, path);
120
- }
121
- if (record.stage === "needs_execution") {
122
- return decodeGoalExecutionPlanStage(record, path);
123
- }
124
- return err(executeError("execution_plan_invalid", `${path}.stage must be needs_evaluation or needs_execution`));
125
- }
126
- function decodeExecutionContinuation(record, path) {
127
- const id = decodePathId(record.id, `${path}.id`);
128
- if (!id.ok) {
129
- return id;
130
- }
131
- const teamScopeId = decodeMemberId(record.teamScopeId, `${path}.teamScopeId`);
132
- if (!teamScopeId.ok) {
133
- return teamScopeId;
134
- }
135
- const execution = decodeExecutionPlan(record.execution, `${path}.execution`);
136
- if (!execution.ok) {
137
- return execution;
138
- }
139
- const continuation = record.continuation === undefined
140
- ? ok(undefined)
141
- : isRecord(record.continuation)
142
- ? decodeGoalExecutionPlan(record.continuation, `${path}.continuation`)
143
- : err(executeError("execution_plan_invalid", `${path}.continuation must be an object`));
144
- if (!continuation.ok) {
145
- return continuation;
146
- }
147
- return ok({
148
- kind: "continuation",
149
- id: id.value,
150
- teamScopeId: teamScopeId.value,
151
- execution: execution.value,
152
- continuation: continuation.value
153
- });
154
- }
155
- function decodeGoalPathEvaluationStage(record, path) {
156
- const id = decodePathId(record.id, `${path}.id`);
157
- if (!id.ok) {
158
- return id;
159
- }
160
- const assignee = decodeGoalRole(record.assignee, `${path}.assignee`, "goal");
161
- if (!assignee.ok) {
162
- return assignee;
163
- }
164
- const evaluator = record.evaluator === undefined || record.evaluator === null ? ok(undefined) : decodeGoalRole(record.evaluator, `${path}.evaluator`, "prompt");
165
- if (!evaluator.ok) {
166
- return evaluator;
167
- }
168
- const remainingAttempts = makeNonNegativeInteger(record.remainingAttempts, `${path}.remainingAttempts`);
169
- if (!remainingAttempts.ok) {
170
- return err(executeError("execution_plan_invalid", remainingAttempts.error.message));
171
- }
172
- const requires = decodePathRequires(record.requires, `${path}.requires`);
173
- if (!requires.ok) {
174
- return requires;
175
- }
176
- return ok({
177
- kind: "goal",
178
- stage: "needs_evaluation",
179
- id: id.value,
180
- assignee: {
181
- executorId: assignee.value.executorId,
182
- goal: assignee.value.text
183
- },
184
- evaluator: evaluator.value
185
- ? {
186
- executorId: evaluator.value.executorId,
187
- prompt: evaluator.value.text
188
- }
189
- : undefined,
190
- remainingAttempts: remainingAttempts.value,
191
- requires: requires.value
192
- });
193
- }
194
- function decodeGoalExecutionPlanStage(record, path) {
195
- const decoded = decodeGoalPathEvaluationStage({ ...record, stage: "needs_evaluation" }, path);
196
- if (!decoded.ok) {
197
- return decoded;
198
- }
199
- const evaluationPathId = decodePathId(record.evaluationPathId, `${path}.evaluationPathId`);
200
- if (!evaluationPathId.ok) {
201
- return evaluationPathId;
202
- }
203
- const evaluation = decodeFailGoalEvaluation(record.evaluation, `${path}.evaluation`);
204
- if (!evaluation.ok) {
205
- return evaluation;
206
- }
207
- if (!Array.isArray(record.requires)) {
208
- return err(executeError("execution_plan_invalid", `${path}.requires must be an array for needs_execution`));
209
- }
210
- const requires = decodePathIdArray(record.requires, `${path}.requires`);
211
- if (!requires.ok) {
212
- return requires;
213
- }
214
- if (requires.value.length !== 1 || requires.value[0] !== evaluationPathId.value) {
215
- return err(executeError("execution_plan_invalid", `${path}.requires must contain only ${path}.evaluationPathId`));
216
- }
217
- return ok({
218
- ...decoded.value,
219
- stage: "needs_execution",
220
- evaluationPathId: evaluationPathId.value,
221
- evaluation: evaluation.value,
222
- requires: requires.value
223
- });
224
- }
225
- function decodeGoalRole(value, path, textField) {
226
- if (!isRecord(value)) {
227
- return err(executeError("execution_plan_invalid", `${path} must be an object`));
228
- }
229
- const executorId = decodeMemberId(value.executorId, `${path}.executorId`);
230
- if (!executorId.ok) {
231
- return executorId;
232
- }
233
- const text = decodeNonEmptyString(value[textField], `${path}.${textField}`);
234
- if (!text.ok) {
235
- return text;
236
- }
237
- return ok({ executorId: executorId.value, text: text.value });
238
- }
239
- function decodeFailGoalEvaluation(value, path) {
240
- if (!isRecord(value)) {
241
- return err(executeError("execution_plan_invalid", `${path} must be an object`));
242
- }
243
- if (value.type !== "fail") {
244
- return err(executeError("execution_plan_invalid", `${path}.type must be fail`));
245
- }
246
- if (typeof value.reason !== "string" || value.reason.trim() === "") {
247
- return err(executeError("execution_plan_invalid", `${path}.reason must be a non-empty string`));
248
- }
249
- if (typeof value.feedback !== "string" || value.feedback.trim() === "") {
250
- return err(executeError("execution_plan_invalid", `${path}.feedback must be a non-empty string`));
251
- }
252
- if (value.nextGoal !== undefined && typeof value.nextGoal !== "string") {
253
- return err(executeError("execution_plan_invalid", `${path}.nextGoal must be a string when present`));
254
- }
255
- const evidence = decodeOptionalStringArray(value.evidence, `${path}.evidence`);
256
- if (!evidence.ok) {
257
- return evidence;
258
- }
259
- return ok({
260
- type: "fail",
261
- reason: value.reason,
262
- feedback: value.feedback,
263
- nextGoal: typeof value.nextGoal === "string" && value.nextGoal.trim() !== "" ? value.nextGoal : undefined,
264
- evidence: evidence.value
265
- });
266
- }
267
- function decodeNonEmptyString(value, path) {
268
- if (typeof value !== "string" || value.trim() === "") {
269
- return err(executeError("execution_plan_invalid", `${path} must be a non-empty string`));
270
- }
271
- return ok(value);
272
- }
273
- function decodePathId(value, path) {
274
- const decoded = makeNonEmptyText(value, path);
275
- if (!decoded.ok) {
276
- return err(executeError("execution_plan_invalid", decoded.error.message));
277
- }
278
- return ok(decoded.value);
279
- }
280
- function decodeMemberId(value, path) {
281
- const decoded = makeNonEmptyText(value, path);
282
- if (!decoded.ok) {
283
- return err(executeError("execution_plan_invalid", decoded.error.message));
284
- }
285
- return ok(decoded.value);
286
- }
287
- function decodePathRequires(value, path) {
288
- if (value === "PrevMove") {
289
- return ok("PrevMove");
290
- }
291
- return decodePathIdArray(value, path);
292
- }
293
- function decodePathIdArray(value, path) {
294
- const decoded = decodeStringArray(value, path, "execution_plan_invalid");
295
- if (!decoded.ok) {
296
- return decoded;
297
- }
298
- const pathIds = [];
299
- for (const [index, item] of decoded.value.entries()) {
300
- const pathId = makeNonEmptyText(item, `${path}[${index}]`);
301
- if (!pathId.ok) {
302
- return err(executeError("execution_plan_invalid", pathId.error.message));
303
- }
304
- pathIds.push(pathId.value);
305
- }
306
- return ok(pathIds);
307
- }
308
- function decodeOptionalStringArray(value, path) {
309
- if (value === undefined) {
310
- return ok(undefined);
311
- }
312
- return decodeStringArray(value, path, "goal_evaluation_invalid");
313
- }
314
- function decodeStringArray(value, path, code) {
315
- if (!Array.isArray(value)) {
316
- return err(executeError(code, `${path} must be an array of strings`));
317
- }
318
- if (!value.every(item => typeof item === "string")) {
319
- return err(executeError(code, `${path} must be an array of strings`));
320
- }
321
- return ok([...value]);
322
- }
323
- function parseJsonObject(text, errorMessage) {
324
- try {
325
- const parsed = JSON.parse(text);
326
- if (!isRecord(parsed)) {
327
- return err(executeError("execution_plan_invalid", errorMessage));
328
- }
329
- return ok(parsed);
330
- }
331
- catch (error) {
332
- return err(executeError("execution_plan_invalid", errorMessage, { cause: error instanceof Error ? error.message : String(error) }));
333
- }
334
- }
335
- function isRecord(value) {
336
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
337
- }
package/src/cli.ts DELETED
@@ -1,82 +0,0 @@
1
- #!/usr/bin/env node
2
- import { startStudioBridge } from "./index.ts";
3
-
4
- type ParsedArgs = {
5
- command?: string;
6
- flags: Map<string, string | boolean>;
7
- };
8
-
9
- async function main(argv = process.argv.slice(2)): Promise<number> {
10
- const parsed = parseArgs(argv);
11
- if (parsed.command === "help" || hasFlag(parsed, "help")) {
12
- printHelp();
13
- return 0;
14
- }
15
- if (parsed.command && parsed.command !== "start" && parsed.command !== "studio") {
16
- throw new Error(`Unknown command: ${parsed.command}`);
17
- }
18
-
19
- await startStudioBridge({
20
- cwd: getFlag(parsed, "cwd") ?? process.cwd(),
21
- webUrl: getFlag(parsed, "web-url"),
22
- noOpen: hasFlag(parsed, "no-open"),
23
- dryRun: hasFlag(parsed, "dry-run"),
24
- json: hasFlag(parsed, "json")
25
- });
26
- return 0;
27
- }
28
-
29
- function parseArgs(argv: string[]): ParsedArgs {
30
- const flags = new Map<string, string | boolean>();
31
- let command: string | undefined;
32
- for (let index = 0; index < argv.length; index += 1) {
33
- const token = argv[index];
34
- if (!token.startsWith("--")) {
35
- command ??= token;
36
- continue;
37
- }
38
- const rawName = token.slice(2);
39
- const equalsIndex = rawName.indexOf("=");
40
- if (equalsIndex >= 0) {
41
- flags.set(rawName.slice(0, equalsIndex), rawName.slice(equalsIndex + 1));
42
- continue;
43
- }
44
- const next = argv[index + 1];
45
- if (next && !next.startsWith("--")) {
46
- flags.set(rawName, next);
47
- index += 1;
48
- continue;
49
- }
50
- flags.set(rawName, true);
51
- }
52
- return { command, flags };
53
- }
54
-
55
- function getFlag(parsed: ParsedArgs, name: string): string | undefined {
56
- const value = parsed.flags.get(name);
57
- return typeof value === "string" ? value : undefined;
58
- }
59
-
60
- function hasFlag(parsed: ParsedArgs, name: string): boolean {
61
- return parsed.flags.get(name) === true;
62
- }
63
-
64
- function printHelp(): void {
65
- console.log(`Usage: hunsu-bridge [start|studio] [options]
66
-
67
- Options:
68
- --cwd <path> Repository or workspace path to open. Defaults to the current directory.
69
- --web-url <url> Studio URL to pair with. Defaults to HUNSU_WEB_URL or https://hunsu.app/studio.
70
- --no-open Print the paired Studio URL without opening a browser.
71
- --dry-run Print start information without starting the Bridge server.
72
- --json Print start information as JSON.
73
- --help Show this help.
74
- `);
75
- }
76
-
77
- main().then(code => {
78
- process.exitCode = code;
79
- }).catch(error => {
80
- console.error(error instanceof Error ? error.message : String(error));
81
- process.exitCode = 1;
82
- });