agor-live 0.7.1 → 0.7.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.
- package/dist/core/permissions/index.cjs +34 -2
- package/dist/core/permissions/index.d.cts +9 -1
- package/dist/core/permissions/index.d.ts +9 -1
- package/dist/core/permissions/index.js +34 -2
- package/dist/core/tools/index.cjs +34 -25
- package/dist/core/tools/index.js +34 -25
- package/dist/core/utils/errors.cjs +45 -0
- package/dist/core/utils/errors.d.cts +38 -1
- package/dist/core/utils/errors.d.ts +38 -1
- package/dist/core/utils/errors.js +40 -0
- package/dist/daemon/adapters/drizzle.d.ts +5 -0
- package/dist/daemon/adapters/drizzle.js +6 -12
- package/dist/daemon/index.js +26 -18
- package/dist/daemon/services/board-comments.js +7 -12
- package/dist/daemon/services/boards.js +7 -12
- package/dist/daemon/services/health-monitor.js +8 -3
- package/dist/daemon/services/mcp-servers.js +7 -12
- package/dist/daemon/services/messages.js +7 -12
- package/dist/daemon/services/repos.js +7 -12
- package/dist/daemon/services/sessions.js +7 -12
- package/dist/daemon/services/tasks.js +7 -12
- package/dist/daemon/services/worktrees.js +11 -15
- package/dist/ui/assets/{index-CEucdq9r.js → index-Dmo92jKC.js} +127 -127
- package/dist/ui/index.html +1 -1
- package/package.json +1 -1
|
@@ -50,9 +50,10 @@ var PermissionService = class {
|
|
|
50
50
|
*
|
|
51
51
|
* @param requestId - Unique permission request ID
|
|
52
52
|
* @param taskId - Task waiting for permission (used for timeout/cancel fallback)
|
|
53
|
+
* @param sessionId - Session ID for tracking pending requests per session
|
|
53
54
|
* @param signal - AbortSignal for cancellation
|
|
54
55
|
*/
|
|
55
|
-
waitForDecision(requestId, taskId, signal) {
|
|
56
|
+
waitForDecision(requestId, taskId, sessionId, signal) {
|
|
56
57
|
return new Promise((resolve) => {
|
|
57
58
|
signal.addEventListener("abort", () => {
|
|
58
59
|
const pending = this.pendingRequests.get(requestId);
|
|
@@ -86,7 +87,7 @@ var PermissionService = class {
|
|
|
86
87
|
// System-initiated timeout
|
|
87
88
|
});
|
|
88
89
|
}, 6e4);
|
|
89
|
-
this.pendingRequests.set(requestId, { resolve, timeout });
|
|
90
|
+
this.pendingRequests.set(requestId, { sessionId, resolve, timeout });
|
|
90
91
|
});
|
|
91
92
|
}
|
|
92
93
|
/**
|
|
@@ -105,6 +106,37 @@ var PermissionService = class {
|
|
|
105
106
|
console.warn(`\u26A0\uFE0F No pending request found for ${decision.requestId}`);
|
|
106
107
|
}
|
|
107
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* Cancel all pending permission requests for a session
|
|
111
|
+
* Used when a permission is denied and we need to stop task execution
|
|
112
|
+
*
|
|
113
|
+
* @param sessionId - Session to cancel pending requests for
|
|
114
|
+
*/
|
|
115
|
+
cancelPendingRequests(sessionId) {
|
|
116
|
+
let cancelledCount = 0;
|
|
117
|
+
for (const [requestId, pending] of this.pendingRequests.entries()) {
|
|
118
|
+
if (pending.sessionId === sessionId) {
|
|
119
|
+
clearTimeout(pending.timeout);
|
|
120
|
+
pending.resolve({
|
|
121
|
+
requestId,
|
|
122
|
+
taskId: "",
|
|
123
|
+
// Will be ignored since we're cancelling
|
|
124
|
+
allow: false,
|
|
125
|
+
reason: "Cancelled due to previous permission denial",
|
|
126
|
+
remember: false,
|
|
127
|
+
scope: "once" /* ONCE */,
|
|
128
|
+
decidedBy: "system"
|
|
129
|
+
});
|
|
130
|
+
this.pendingRequests.delete(requestId);
|
|
131
|
+
cancelledCount++;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (cancelledCount > 0) {
|
|
135
|
+
console.log(
|
|
136
|
+
`\u{1F6E1}\uFE0F Cancelled ${cancelledCount} pending permission request(s) for session ${sessionId.substring(0, 8)}`
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
108
140
|
};
|
|
109
141
|
// Annotate the CommonJS export names for ESM import in node:
|
|
110
142
|
0 && (module.exports = {
|
|
@@ -69,13 +69,21 @@ declare class PermissionService {
|
|
|
69
69
|
*
|
|
70
70
|
* @param requestId - Unique permission request ID
|
|
71
71
|
* @param taskId - Task waiting for permission (used for timeout/cancel fallback)
|
|
72
|
+
* @param sessionId - Session ID for tracking pending requests per session
|
|
72
73
|
* @param signal - AbortSignal for cancellation
|
|
73
74
|
*/
|
|
74
|
-
waitForDecision(requestId: string, taskId: TaskID, signal: AbortSignal): Promise<PermissionDecision>;
|
|
75
|
+
waitForDecision(requestId: string, taskId: TaskID, sessionId: SessionID, signal: AbortSignal): Promise<PermissionDecision>;
|
|
75
76
|
/**
|
|
76
77
|
* Resolve a pending permission request with a decision from the UI
|
|
77
78
|
*/
|
|
78
79
|
resolvePermission(decision: PermissionDecision): void;
|
|
80
|
+
/**
|
|
81
|
+
* Cancel all pending permission requests for a session
|
|
82
|
+
* Used when a permission is denied and we need to stop task execution
|
|
83
|
+
*
|
|
84
|
+
* @param sessionId - Session to cancel pending requests for
|
|
85
|
+
*/
|
|
86
|
+
cancelPendingRequests(sessionId: SessionID): void;
|
|
79
87
|
}
|
|
80
88
|
|
|
81
89
|
export { type PermissionDecision, type PermissionRequest, PermissionService };
|
|
@@ -69,13 +69,21 @@ declare class PermissionService {
|
|
|
69
69
|
*
|
|
70
70
|
* @param requestId - Unique permission request ID
|
|
71
71
|
* @param taskId - Task waiting for permission (used for timeout/cancel fallback)
|
|
72
|
+
* @param sessionId - Session ID for tracking pending requests per session
|
|
72
73
|
* @param signal - AbortSignal for cancellation
|
|
73
74
|
*/
|
|
74
|
-
waitForDecision(requestId: string, taskId: TaskID, signal: AbortSignal): Promise<PermissionDecision>;
|
|
75
|
+
waitForDecision(requestId: string, taskId: TaskID, sessionId: SessionID, signal: AbortSignal): Promise<PermissionDecision>;
|
|
75
76
|
/**
|
|
76
77
|
* Resolve a pending permission request with a decision from the UI
|
|
77
78
|
*/
|
|
78
79
|
resolvePermission(decision: PermissionDecision): void;
|
|
80
|
+
/**
|
|
81
|
+
* Cancel all pending permission requests for a session
|
|
82
|
+
* Used when a permission is denied and we need to stop task execution
|
|
83
|
+
*
|
|
84
|
+
* @param sessionId - Session to cancel pending requests for
|
|
85
|
+
*/
|
|
86
|
+
cancelPendingRequests(sessionId: SessionID): void;
|
|
79
87
|
}
|
|
80
88
|
|
|
81
89
|
export { type PermissionDecision, type PermissionRequest, PermissionService };
|
|
@@ -24,9 +24,10 @@ var PermissionService = class {
|
|
|
24
24
|
*
|
|
25
25
|
* @param requestId - Unique permission request ID
|
|
26
26
|
* @param taskId - Task waiting for permission (used for timeout/cancel fallback)
|
|
27
|
+
* @param sessionId - Session ID for tracking pending requests per session
|
|
27
28
|
* @param signal - AbortSignal for cancellation
|
|
28
29
|
*/
|
|
29
|
-
waitForDecision(requestId, taskId, signal) {
|
|
30
|
+
waitForDecision(requestId, taskId, sessionId, signal) {
|
|
30
31
|
return new Promise((resolve) => {
|
|
31
32
|
signal.addEventListener("abort", () => {
|
|
32
33
|
const pending = this.pendingRequests.get(requestId);
|
|
@@ -60,7 +61,7 @@ var PermissionService = class {
|
|
|
60
61
|
// System-initiated timeout
|
|
61
62
|
});
|
|
62
63
|
}, 6e4);
|
|
63
|
-
this.pendingRequests.set(requestId, { resolve, timeout });
|
|
64
|
+
this.pendingRequests.set(requestId, { sessionId, resolve, timeout });
|
|
64
65
|
});
|
|
65
66
|
}
|
|
66
67
|
/**
|
|
@@ -79,6 +80,37 @@ var PermissionService = class {
|
|
|
79
80
|
console.warn(`\u26A0\uFE0F No pending request found for ${decision.requestId}`);
|
|
80
81
|
}
|
|
81
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* Cancel all pending permission requests for a session
|
|
85
|
+
* Used when a permission is denied and we need to stop task execution
|
|
86
|
+
*
|
|
87
|
+
* @param sessionId - Session to cancel pending requests for
|
|
88
|
+
*/
|
|
89
|
+
cancelPendingRequests(sessionId) {
|
|
90
|
+
let cancelledCount = 0;
|
|
91
|
+
for (const [requestId, pending] of this.pendingRequests.entries()) {
|
|
92
|
+
if (pending.sessionId === sessionId) {
|
|
93
|
+
clearTimeout(pending.timeout);
|
|
94
|
+
pending.resolve({
|
|
95
|
+
requestId,
|
|
96
|
+
taskId: "",
|
|
97
|
+
// Will be ignored since we're cancelling
|
|
98
|
+
allow: false,
|
|
99
|
+
reason: "Cancelled due to previous permission denial",
|
|
100
|
+
remember: false,
|
|
101
|
+
scope: "once" /* ONCE */,
|
|
102
|
+
decidedBy: "system"
|
|
103
|
+
});
|
|
104
|
+
this.pendingRequests.delete(requestId);
|
|
105
|
+
cancelledCount++;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (cancelledCount > 0) {
|
|
109
|
+
console.log(
|
|
110
|
+
`\u{1F6E1}\uFE0F Cancelled ${cancelledCount} pending permission request(s) for session ${sessionId.substring(0, 8)}`
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
82
114
|
};
|
|
83
115
|
export {
|
|
84
116
|
PermissionService
|
|
@@ -408,7 +408,8 @@ var SDKMessageProcessor = class {
|
|
|
408
408
|
enableTokenStreaming: options.enableTokenStreaming ?? true,
|
|
409
409
|
idleTimeoutMs: options.idleTimeoutMs ?? 3e4,
|
|
410
410
|
// 30s default
|
|
411
|
-
contentBlockStack: []
|
|
411
|
+
contentBlockStack: [],
|
|
412
|
+
toolInputChunkCount: 0
|
|
412
413
|
};
|
|
413
414
|
}
|
|
414
415
|
/**
|
|
@@ -516,13 +517,11 @@ var SDKMessageProcessor = class {
|
|
|
516
517
|
const hasText = Array.isArray(content) && content.some((b) => b.type === "text");
|
|
517
518
|
if (hasToolResult) {
|
|
518
519
|
const toolResults = content.filter((b) => b.type === "tool_result");
|
|
520
|
+
const errorCount = toolResults.filter((tr) => tr.is_error).length;
|
|
521
|
+
const successCount = toolResults.length - errorCount;
|
|
519
522
|
console.log(
|
|
520
|
-
`\u{1F527} SDK user message with ${toolResults.length} tool result(s) (
|
|
523
|
+
`\u{1F527} SDK user message with ${toolResults.length} tool result(s) (\u2705 ${successCount}, \u274C ${errorCount})`
|
|
521
524
|
);
|
|
522
|
-
toolResults.forEach((tr, i) => {
|
|
523
|
-
const preview = typeof tr.content === "string" ? tr.content.substring(0, 100) : JSON.stringify(tr.content).substring(0, 100);
|
|
524
|
-
console.log(` Result ${i + 1}: ${tr.is_error ? "\u274C ERROR" : "\u2705"} ${preview}`);
|
|
525
|
-
});
|
|
526
525
|
return [
|
|
527
526
|
{
|
|
528
527
|
type: "complete",
|
|
@@ -630,9 +629,9 @@ var SDKMessageProcessor = class {
|
|
|
630
629
|
agentSessionId: this.state.capturedAgentSessionId
|
|
631
630
|
});
|
|
632
631
|
} else if (delta?.type === "input_json_delta") {
|
|
633
|
-
|
|
634
|
-
if (
|
|
635
|
-
console.debug(`\u{1F527} Tool input chunk
|
|
632
|
+
this.state.toolInputChunkCount++;
|
|
633
|
+
if (this.state.toolInputChunkCount % 10 === 0) {
|
|
634
|
+
console.debug(`\u{1F527} Tool input chunk (${this.state.toolInputChunkCount})`);
|
|
636
635
|
}
|
|
637
636
|
}
|
|
638
637
|
}
|
|
@@ -1598,6 +1597,7 @@ function createPreToolUseHook(sessionId, taskId, deps) {
|
|
|
1598
1597
|
const decision = await deps.permissionService.waitForDecision(
|
|
1599
1598
|
requestId,
|
|
1600
1599
|
taskId,
|
|
1600
|
+
sessionId,
|
|
1601
1601
|
options.signal
|
|
1602
1602
|
);
|
|
1603
1603
|
if (deps.messagesService) {
|
|
@@ -1618,11 +1618,23 @@ function createPreToolUseHook(sessionId, taskId, deps) {
|
|
|
1618
1618
|
await deps.tasksService.patch(taskId, {
|
|
1619
1619
|
status: decision.allow ? TaskStatus.RUNNING : TaskStatus.FAILED
|
|
1620
1620
|
});
|
|
1621
|
-
if (
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1621
|
+
if (!decision.allow) {
|
|
1622
|
+
console.log(`\u{1F6D1} Permission denied for ${input.tool_name}, stopping task execution...`);
|
|
1623
|
+
deps.permissionService.cancelPendingRequests(sessionId);
|
|
1624
|
+
if (deps.sessionsService) {
|
|
1625
|
+
await deps.sessionsService.patch(sessionId, {
|
|
1626
|
+
status: "idle"
|
|
1627
|
+
});
|
|
1628
|
+
console.log(`\u2705 Session ${sessionId} set to idle after permission denial`);
|
|
1629
|
+
}
|
|
1630
|
+
throw new Error(`Permission denied for tool: ${input.tool_name}`);
|
|
1631
|
+
} else {
|
|
1632
|
+
if (deps.sessionsService) {
|
|
1633
|
+
await deps.sessionsService.patch(sessionId, {
|
|
1634
|
+
status: "running"
|
|
1635
|
+
});
|
|
1636
|
+
console.log(`\u2705 Session ${sessionId} restored to running after permission approval`);
|
|
1637
|
+
}
|
|
1626
1638
|
}
|
|
1627
1639
|
if (decision.remember) {
|
|
1628
1640
|
const freshSession = await deps.sessionsRepo.findById(sessionId);
|
|
@@ -1684,21 +1696,18 @@ function createPreToolUseHook(sessionId, taskId, deps) {
|
|
|
1684
1696
|
try {
|
|
1685
1697
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1686
1698
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1699
|
+
const isPermissionDenial = error instanceof Error && error.message.startsWith("Permission denied for tool:");
|
|
1700
|
+
if (!isPermissionDenial) {
|
|
1701
|
+
await deps.tasksService.patch(taskId, {
|
|
1702
|
+
status: TaskStatus.FAILED,
|
|
1703
|
+
report: `Error: ${errorMessage}
|
|
1690
1704
|
Timestamp: ${timestamp}`
|
|
1691
|
-
|
|
1705
|
+
});
|
|
1706
|
+
}
|
|
1692
1707
|
} catch (updateError) {
|
|
1693
1708
|
console.error("Failed to update task status:", updateError);
|
|
1694
1709
|
}
|
|
1695
|
-
|
|
1696
|
-
hookSpecificOutput: {
|
|
1697
|
-
hookEventName: "PreToolUse",
|
|
1698
|
-
permissionDecision: "deny",
|
|
1699
|
-
permissionDecisionReason: `Permission hook failed: ${error instanceof Error ? error.message : String(error)}`
|
|
1700
|
-
}
|
|
1701
|
-
};
|
|
1710
|
+
throw error;
|
|
1702
1711
|
} finally {
|
|
1703
1712
|
if (releaseLock) {
|
|
1704
1713
|
releaseLock();
|
package/dist/core/tools/index.js
CHANGED
|
@@ -350,7 +350,8 @@ var SDKMessageProcessor = class {
|
|
|
350
350
|
enableTokenStreaming: options.enableTokenStreaming ?? true,
|
|
351
351
|
idleTimeoutMs: options.idleTimeoutMs ?? 3e4,
|
|
352
352
|
// 30s default
|
|
353
|
-
contentBlockStack: []
|
|
353
|
+
contentBlockStack: [],
|
|
354
|
+
toolInputChunkCount: 0
|
|
354
355
|
};
|
|
355
356
|
}
|
|
356
357
|
/**
|
|
@@ -458,13 +459,11 @@ var SDKMessageProcessor = class {
|
|
|
458
459
|
const hasText = Array.isArray(content) && content.some((b) => b.type === "text");
|
|
459
460
|
if (hasToolResult) {
|
|
460
461
|
const toolResults = content.filter((b) => b.type === "tool_result");
|
|
462
|
+
const errorCount = toolResults.filter((tr) => tr.is_error).length;
|
|
463
|
+
const successCount = toolResults.length - errorCount;
|
|
461
464
|
console.log(
|
|
462
|
-
`\u{1F527} SDK user message with ${toolResults.length} tool result(s) (
|
|
465
|
+
`\u{1F527} SDK user message with ${toolResults.length} tool result(s) (\u2705 ${successCount}, \u274C ${errorCount})`
|
|
463
466
|
);
|
|
464
|
-
toolResults.forEach((tr, i) => {
|
|
465
|
-
const preview = typeof tr.content === "string" ? tr.content.substring(0, 100) : JSON.stringify(tr.content).substring(0, 100);
|
|
466
|
-
console.log(` Result ${i + 1}: ${tr.is_error ? "\u274C ERROR" : "\u2705"} ${preview}`);
|
|
467
|
-
});
|
|
468
467
|
return [
|
|
469
468
|
{
|
|
470
469
|
type: "complete",
|
|
@@ -572,9 +571,9 @@ var SDKMessageProcessor = class {
|
|
|
572
571
|
agentSessionId: this.state.capturedAgentSessionId
|
|
573
572
|
});
|
|
574
573
|
} else if (delta?.type === "input_json_delta") {
|
|
575
|
-
|
|
576
|
-
if (
|
|
577
|
-
console.debug(`\u{1F527} Tool input chunk
|
|
574
|
+
this.state.toolInputChunkCount++;
|
|
575
|
+
if (this.state.toolInputChunkCount % 10 === 0) {
|
|
576
|
+
console.debug(`\u{1F527} Tool input chunk (${this.state.toolInputChunkCount})`);
|
|
578
577
|
}
|
|
579
578
|
}
|
|
580
579
|
}
|
|
@@ -1540,6 +1539,7 @@ function createPreToolUseHook(sessionId, taskId, deps) {
|
|
|
1540
1539
|
const decision = await deps.permissionService.waitForDecision(
|
|
1541
1540
|
requestId,
|
|
1542
1541
|
taskId,
|
|
1542
|
+
sessionId,
|
|
1543
1543
|
options.signal
|
|
1544
1544
|
);
|
|
1545
1545
|
if (deps.messagesService) {
|
|
@@ -1560,11 +1560,23 @@ function createPreToolUseHook(sessionId, taskId, deps) {
|
|
|
1560
1560
|
await deps.tasksService.patch(taskId, {
|
|
1561
1561
|
status: decision.allow ? TaskStatus.RUNNING : TaskStatus.FAILED
|
|
1562
1562
|
});
|
|
1563
|
-
if (
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1563
|
+
if (!decision.allow) {
|
|
1564
|
+
console.log(`\u{1F6D1} Permission denied for ${input.tool_name}, stopping task execution...`);
|
|
1565
|
+
deps.permissionService.cancelPendingRequests(sessionId);
|
|
1566
|
+
if (deps.sessionsService) {
|
|
1567
|
+
await deps.sessionsService.patch(sessionId, {
|
|
1568
|
+
status: "idle"
|
|
1569
|
+
});
|
|
1570
|
+
console.log(`\u2705 Session ${sessionId} set to idle after permission denial`);
|
|
1571
|
+
}
|
|
1572
|
+
throw new Error(`Permission denied for tool: ${input.tool_name}`);
|
|
1573
|
+
} else {
|
|
1574
|
+
if (deps.sessionsService) {
|
|
1575
|
+
await deps.sessionsService.patch(sessionId, {
|
|
1576
|
+
status: "running"
|
|
1577
|
+
});
|
|
1578
|
+
console.log(`\u2705 Session ${sessionId} restored to running after permission approval`);
|
|
1579
|
+
}
|
|
1568
1580
|
}
|
|
1569
1581
|
if (decision.remember) {
|
|
1570
1582
|
const freshSession = await deps.sessionsRepo.findById(sessionId);
|
|
@@ -1626,21 +1638,18 @@ function createPreToolUseHook(sessionId, taskId, deps) {
|
|
|
1626
1638
|
try {
|
|
1627
1639
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1628
1640
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1641
|
+
const isPermissionDenial = error instanceof Error && error.message.startsWith("Permission denied for tool:");
|
|
1642
|
+
if (!isPermissionDenial) {
|
|
1643
|
+
await deps.tasksService.patch(taskId, {
|
|
1644
|
+
status: TaskStatus.FAILED,
|
|
1645
|
+
report: `Error: ${errorMessage}
|
|
1632
1646
|
Timestamp: ${timestamp}`
|
|
1633
|
-
|
|
1647
|
+
});
|
|
1648
|
+
}
|
|
1634
1649
|
} catch (updateError) {
|
|
1635
1650
|
console.error("Failed to update task status:", updateError);
|
|
1636
1651
|
}
|
|
1637
|
-
|
|
1638
|
-
hookSpecificOutput: {
|
|
1639
|
-
hookEventName: "PreToolUse",
|
|
1640
|
-
permissionDecision: "deny",
|
|
1641
|
-
permissionDecisionReason: `Permission hook failed: ${error instanceof Error ? error.message : String(error)}`
|
|
1642
|
-
}
|
|
1643
|
-
};
|
|
1652
|
+
throw error;
|
|
1644
1653
|
} finally {
|
|
1645
1654
|
if (releaseLock) {
|
|
1646
1655
|
releaseLock();
|
|
@@ -20,6 +20,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/utils/errors.ts
|
|
21
21
|
var errors_exports = {};
|
|
22
22
|
__export(errors_exports, {
|
|
23
|
+
AgorError: () => AgorError,
|
|
24
|
+
AlreadyExistsError: () => AlreadyExistsError,
|
|
25
|
+
NotFoundError: () => NotFoundError,
|
|
26
|
+
UnauthorizedError: () => UnauthorizedError,
|
|
27
|
+
ValidationError: () => ValidationError,
|
|
23
28
|
createErrorResponse: () => createErrorResponse,
|
|
24
29
|
formatUserError: () => formatUserError,
|
|
25
30
|
getErrorDetails: () => getErrorDetails,
|
|
@@ -29,6 +34,41 @@ __export(errors_exports, {
|
|
|
29
34
|
logError: () => logError
|
|
30
35
|
});
|
|
31
36
|
module.exports = __toCommonJS(errors_exports);
|
|
37
|
+
var AgorError = class extends Error {
|
|
38
|
+
constructor(message, code) {
|
|
39
|
+
super(message);
|
|
40
|
+
this.code = code;
|
|
41
|
+
this.name = this.constructor.name;
|
|
42
|
+
if (Error.captureStackTrace) {
|
|
43
|
+
Error.captureStackTrace(this, this.constructor);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
var NotFoundError = class extends AgorError {
|
|
48
|
+
constructor(resourceType, id) {
|
|
49
|
+
super(`${resourceType} not found: ${id}`, "NOT_FOUND");
|
|
50
|
+
this.resourceType = resourceType;
|
|
51
|
+
this.id = id;
|
|
52
|
+
}
|
|
53
|
+
resourceType;
|
|
54
|
+
id;
|
|
55
|
+
};
|
|
56
|
+
var AlreadyExistsError = class extends AgorError {
|
|
57
|
+
constructor(resourceType, identifier) {
|
|
58
|
+
super(`${resourceType} already exists: ${identifier}`, "ALREADY_EXISTS");
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
var ValidationError = class extends AgorError {
|
|
62
|
+
constructor(message, field) {
|
|
63
|
+
super(message, "VALIDATION_ERROR");
|
|
64
|
+
this.field = field;
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
var UnauthorizedError = class extends AgorError {
|
|
68
|
+
constructor(message = "Not authorized") {
|
|
69
|
+
super(message, "UNAUTHORIZED");
|
|
70
|
+
}
|
|
71
|
+
};
|
|
32
72
|
function getErrorMessage(error) {
|
|
33
73
|
if (error instanceof Error) {
|
|
34
74
|
return error.message;
|
|
@@ -90,6 +130,11 @@ function logError(error, context, metadata = {}) {
|
|
|
90
130
|
}
|
|
91
131
|
// Annotate the CommonJS export names for ESM import in node:
|
|
92
132
|
0 && (module.exports = {
|
|
133
|
+
AgorError,
|
|
134
|
+
AlreadyExistsError,
|
|
135
|
+
NotFoundError,
|
|
136
|
+
UnauthorizedError,
|
|
137
|
+
ValidationError,
|
|
93
138
|
createErrorResponse,
|
|
94
139
|
formatUserError,
|
|
95
140
|
getErrorDetails,
|
|
@@ -4,6 +4,43 @@
|
|
|
4
4
|
* Provides standardized error formatting, logging, and categorization.
|
|
5
5
|
* Used by daemon services and API handlers for uniform error responses.
|
|
6
6
|
*/
|
|
7
|
+
/**
|
|
8
|
+
* Custom error classes for type-safe error handling
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Base class for all Agor errors
|
|
12
|
+
*/
|
|
13
|
+
declare class AgorError extends Error {
|
|
14
|
+
readonly code?: string | undefined;
|
|
15
|
+
constructor(message: string, code?: string | undefined);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Thrown when a resource is not found in the database
|
|
19
|
+
*/
|
|
20
|
+
declare class NotFoundError extends AgorError {
|
|
21
|
+
constructor(resourceType: string, id: string);
|
|
22
|
+
readonly resourceType: string;
|
|
23
|
+
readonly id: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Thrown when a resource already exists
|
|
27
|
+
*/
|
|
28
|
+
declare class AlreadyExistsError extends AgorError {
|
|
29
|
+
constructor(resourceType: string, identifier: string);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Thrown when validation fails
|
|
33
|
+
*/
|
|
34
|
+
declare class ValidationError extends AgorError {
|
|
35
|
+
readonly field?: string | undefined;
|
|
36
|
+
constructor(message: string, field?: string | undefined);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Thrown when an operation is not authorized
|
|
40
|
+
*/
|
|
41
|
+
declare class UnauthorizedError extends AgorError {
|
|
42
|
+
constructor(message?: string);
|
|
43
|
+
}
|
|
7
44
|
/**
|
|
8
45
|
* Extract error message from any value
|
|
9
46
|
* Handles Error objects, strings, and unknown types
|
|
@@ -48,4 +85,4 @@ declare function createErrorResponse(error: unknown, defaultMessage?: string): {
|
|
|
48
85
|
*/
|
|
49
86
|
declare function logError(error: unknown, context: string, metadata?: Record<string, unknown>): void;
|
|
50
87
|
|
|
51
|
-
export { createErrorResponse, formatUserError, getErrorDetails, getErrorMessage, handlePromiseError, isBusinessLogicError, logError };
|
|
88
|
+
export { AgorError, AlreadyExistsError, NotFoundError, UnauthorizedError, ValidationError, createErrorResponse, formatUserError, getErrorDetails, getErrorMessage, handlePromiseError, isBusinessLogicError, logError };
|
|
@@ -4,6 +4,43 @@
|
|
|
4
4
|
* Provides standardized error formatting, logging, and categorization.
|
|
5
5
|
* Used by daemon services and API handlers for uniform error responses.
|
|
6
6
|
*/
|
|
7
|
+
/**
|
|
8
|
+
* Custom error classes for type-safe error handling
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Base class for all Agor errors
|
|
12
|
+
*/
|
|
13
|
+
declare class AgorError extends Error {
|
|
14
|
+
readonly code?: string | undefined;
|
|
15
|
+
constructor(message: string, code?: string | undefined);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Thrown when a resource is not found in the database
|
|
19
|
+
*/
|
|
20
|
+
declare class NotFoundError extends AgorError {
|
|
21
|
+
constructor(resourceType: string, id: string);
|
|
22
|
+
readonly resourceType: string;
|
|
23
|
+
readonly id: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Thrown when a resource already exists
|
|
27
|
+
*/
|
|
28
|
+
declare class AlreadyExistsError extends AgorError {
|
|
29
|
+
constructor(resourceType: string, identifier: string);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Thrown when validation fails
|
|
33
|
+
*/
|
|
34
|
+
declare class ValidationError extends AgorError {
|
|
35
|
+
readonly field?: string | undefined;
|
|
36
|
+
constructor(message: string, field?: string | undefined);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Thrown when an operation is not authorized
|
|
40
|
+
*/
|
|
41
|
+
declare class UnauthorizedError extends AgorError {
|
|
42
|
+
constructor(message?: string);
|
|
43
|
+
}
|
|
7
44
|
/**
|
|
8
45
|
* Extract error message from any value
|
|
9
46
|
* Handles Error objects, strings, and unknown types
|
|
@@ -48,4 +85,4 @@ declare function createErrorResponse(error: unknown, defaultMessage?: string): {
|
|
|
48
85
|
*/
|
|
49
86
|
declare function logError(error: unknown, context: string, metadata?: Record<string, unknown>): void;
|
|
50
87
|
|
|
51
|
-
export { createErrorResponse, formatUserError, getErrorDetails, getErrorMessage, handlePromiseError, isBusinessLogicError, logError };
|
|
88
|
+
export { AgorError, AlreadyExistsError, NotFoundError, UnauthorizedError, ValidationError, createErrorResponse, formatUserError, getErrorDetails, getErrorMessage, handlePromiseError, isBusinessLogicError, logError };
|
|
@@ -1,4 +1,39 @@
|
|
|
1
1
|
// src/utils/errors.ts
|
|
2
|
+
var AgorError = class extends Error {
|
|
3
|
+
constructor(message, code) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.code = code;
|
|
6
|
+
this.name = this.constructor.name;
|
|
7
|
+
if (Error.captureStackTrace) {
|
|
8
|
+
Error.captureStackTrace(this, this.constructor);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
var NotFoundError = class extends AgorError {
|
|
13
|
+
constructor(resourceType, id) {
|
|
14
|
+
super(`${resourceType} not found: ${id}`, "NOT_FOUND");
|
|
15
|
+
this.resourceType = resourceType;
|
|
16
|
+
this.id = id;
|
|
17
|
+
}
|
|
18
|
+
resourceType;
|
|
19
|
+
id;
|
|
20
|
+
};
|
|
21
|
+
var AlreadyExistsError = class extends AgorError {
|
|
22
|
+
constructor(resourceType, identifier) {
|
|
23
|
+
super(`${resourceType} already exists: ${identifier}`, "ALREADY_EXISTS");
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
var ValidationError = class extends AgorError {
|
|
27
|
+
constructor(message, field) {
|
|
28
|
+
super(message, "VALIDATION_ERROR");
|
|
29
|
+
this.field = field;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
var UnauthorizedError = class extends AgorError {
|
|
33
|
+
constructor(message = "Not authorized") {
|
|
34
|
+
super(message, "UNAUTHORIZED");
|
|
35
|
+
}
|
|
36
|
+
};
|
|
2
37
|
function getErrorMessage(error) {
|
|
3
38
|
if (error instanceof Error) {
|
|
4
39
|
return error.message;
|
|
@@ -59,6 +94,11 @@ function logError(error, context, metadata = {}) {
|
|
|
59
94
|
}
|
|
60
95
|
}
|
|
61
96
|
export {
|
|
97
|
+
AgorError,
|
|
98
|
+
AlreadyExistsError,
|
|
99
|
+
NotFoundError,
|
|
100
|
+
UnauthorizedError,
|
|
101
|
+
ValidationError,
|
|
62
102
|
createErrorResponse,
|
|
63
103
|
formatUserError,
|
|
64
104
|
getErrorDetails,
|
|
@@ -40,6 +40,10 @@ interface DrizzleAdapterOptions {
|
|
|
40
40
|
* Allow multi-record operations (patch/remove without ID)
|
|
41
41
|
*/
|
|
42
42
|
multi?: boolean | string[];
|
|
43
|
+
/**
|
|
44
|
+
* Resource type name for error messages (e.g., 'Worktree', 'Session')
|
|
45
|
+
*/
|
|
46
|
+
resourceType?: string;
|
|
43
47
|
}
|
|
44
48
|
/**
|
|
45
49
|
* Repository interface that the adapter expects
|
|
@@ -63,6 +67,7 @@ declare class DrizzleService<T = any, D = Partial<T>, P extends Params = Params>
|
|
|
63
67
|
id: string;
|
|
64
68
|
paginate?: PaginationOptions;
|
|
65
69
|
multi: boolean | string[];
|
|
70
|
+
resourceType: string;
|
|
66
71
|
emit?: (event: string, ...args: any[]) => boolean;
|
|
67
72
|
constructor(repository: Repository<T>, options?: DrizzleAdapterOptions);
|
|
68
73
|
/**
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
// src/adapters/drizzle.ts
|
|
2
|
+
import { NotFoundError } from "@agor/core/utils/errors";
|
|
2
3
|
var DrizzleService = class {
|
|
3
4
|
constructor(repository, options = {}) {
|
|
4
5
|
this.repository = repository;
|
|
5
6
|
this.id = options.id ?? "id";
|
|
6
7
|
this.paginate = options.paginate;
|
|
7
8
|
this.multi = options.multi ?? false;
|
|
9
|
+
this.resourceType = options.resourceType ?? "Record";
|
|
8
10
|
}
|
|
9
11
|
id;
|
|
10
12
|
paginate;
|
|
11
13
|
multi;
|
|
14
|
+
resourceType;
|
|
12
15
|
// Event emitter for FeathersJS (will be injected by framework)
|
|
13
16
|
// biome-ignore lint/suspicious/noExplicitAny: FeathersJS event system
|
|
14
17
|
emit;
|
|
@@ -121,7 +124,7 @@ var DrizzleService = class {
|
|
|
121
124
|
async get(id, _params) {
|
|
122
125
|
const result = await this.repository.findById(String(id));
|
|
123
126
|
if (!result) {
|
|
124
|
-
throw new
|
|
127
|
+
throw new NotFoundError(this.resourceType, String(id));
|
|
125
128
|
}
|
|
126
129
|
return result;
|
|
127
130
|
}
|
|
@@ -146,10 +149,7 @@ var DrizzleService = class {
|
|
|
146
149
|
* Update a record (complete replacement)
|
|
147
150
|
*/
|
|
148
151
|
async update(id, data, params) {
|
|
149
|
-
|
|
150
|
-
if (!existing) {
|
|
151
|
-
throw new Error(`No record found for id '${id}'`);
|
|
152
|
-
}
|
|
152
|
+
await this.get(id, params);
|
|
153
153
|
const result = await this.repository.update(String(id), data);
|
|
154
154
|
this.emit?.("updated", result, params);
|
|
155
155
|
this.emit?.("patched", result, params);
|
|
@@ -179,10 +179,7 @@ var DrizzleService = class {
|
|
|
179
179
|
}
|
|
180
180
|
return results;
|
|
181
181
|
}
|
|
182
|
-
|
|
183
|
-
if (!existing) {
|
|
184
|
-
throw new Error(`No record found for id '${id}'`);
|
|
185
|
-
}
|
|
182
|
+
await this.get(id, params);
|
|
186
183
|
const result = await this.repository.update(String(id), data);
|
|
187
184
|
this.emit?.("patched", result, params);
|
|
188
185
|
return result;
|
|
@@ -205,9 +202,6 @@ var DrizzleService = class {
|
|
|
205
202
|
return records;
|
|
206
203
|
}
|
|
207
204
|
const existing = await this.get(id, params);
|
|
208
|
-
if (!existing) {
|
|
209
|
-
throw new Error(`No record found for id '${id}'`);
|
|
210
|
-
}
|
|
211
205
|
await this.repository.delete(String(id));
|
|
212
206
|
this.emit?.("removed", existing, params);
|
|
213
207
|
return existing;
|