role-os 1.0.2 → 1.2.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/CHANGELOG.md +74 -0
- package/README.md +55 -23
- package/bin/roleos.mjs +8 -1
- package/package.json +13 -3
- package/src/conflicts.mjs +217 -0
- package/src/dispatch.mjs +310 -0
- package/src/escalation.mjs +288 -0
- package/src/evidence.mjs +288 -0
- package/src/packs-cmd.mjs +143 -0
- package/src/packs.mjs +331 -0
- package/src/review.mjs +12 -0
- package/src/route.mjs +477 -82
- package/src/trial.mjs +252 -0
package/src/evidence.mjs
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured evidence for verdicts.
|
|
3
|
+
*
|
|
4
|
+
* A verdict is not just an opinion. It is a decision bound to explicit evidence.
|
|
5
|
+
* This module defines evidence schemas, role-aware requirements, sufficiency
|
|
6
|
+
* checks, and verdict-to-recovery continuity.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// ── Evidence item kinds ───────────────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
export const EVIDENCE_KINDS = [
|
|
12
|
+
"spec", // specification, requirements, scope doc
|
|
13
|
+
"code", // source code, implementation
|
|
14
|
+
"test", // test results, coverage data
|
|
15
|
+
"output", // build output, CLI output, logs
|
|
16
|
+
"packet", // another packet (upstream/downstream)
|
|
17
|
+
"diff", // code diff, before/after
|
|
18
|
+
"doc", // documentation, README, handbook
|
|
19
|
+
"reasoning", // architectural decision, tradeoff analysis
|
|
20
|
+
"dependency", // upstream dependency status
|
|
21
|
+
"user-intent", // user request, product requirement
|
|
22
|
+
"measurement", // metric, benchmark, performance data
|
|
23
|
+
"review", // prior review, verdict, feedback
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
export const EVIDENCE_STATUSES = [
|
|
27
|
+
"supports", // evidence directly supports the verdict
|
|
28
|
+
"partial", // evidence partially supports — gaps remain
|
|
29
|
+
"contradicts", // evidence contradicts the verdict (must be explained)
|
|
30
|
+
"missing", // expected evidence was not provided
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
export const VERDICT_TYPES = [
|
|
34
|
+
"accept", // work meets bar, move forward
|
|
35
|
+
"accept-with-notes", // work meets bar with documented caveats
|
|
36
|
+
"reject", // work does not meet bar, fundamental problems
|
|
37
|
+
"blocked", // cannot proceed — external dependency or ambiguity
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
export const CONFIDENCE_LEVELS = ["high", "medium", "low"];
|
|
41
|
+
|
|
42
|
+
// ── Role-aware evidence requirements ──────────────────────────────────────────
|
|
43
|
+
// What evidence each reviewer role MUST provide. Derived from role contracts.
|
|
44
|
+
|
|
45
|
+
export const ROLE_EVIDENCE_REQUIREMENTS = {
|
|
46
|
+
"Critic Reviewer": {
|
|
47
|
+
required: ["spec", "reasoning"],
|
|
48
|
+
recommended: ["test", "code", "review"],
|
|
49
|
+
description: "Must cite quality bar items checked, contract compliance, and cross-contamination assessment",
|
|
50
|
+
},
|
|
51
|
+
"Test Engineer": {
|
|
52
|
+
required: ["test", "output"],
|
|
53
|
+
recommended: ["code", "measurement"],
|
|
54
|
+
description: "Must cite test results (pass/fail), coverage data, and edge cases verified",
|
|
55
|
+
},
|
|
56
|
+
"Backend Engineer": {
|
|
57
|
+
required: ["code", "test"],
|
|
58
|
+
recommended: ["diff", "output", "dependency"],
|
|
59
|
+
description: "Must cite changed files, test status, and API contract compliance",
|
|
60
|
+
},
|
|
61
|
+
"Frontend Developer": {
|
|
62
|
+
required: ["code", "test"],
|
|
63
|
+
recommended: ["diff", "output"],
|
|
64
|
+
description: "Must cite changed components, test status, and interaction verification",
|
|
65
|
+
},
|
|
66
|
+
"UI Designer": {
|
|
67
|
+
required: ["spec", "reasoning"],
|
|
68
|
+
recommended: ["doc"],
|
|
69
|
+
description: "Must cite design decisions, hierarchy rationale, and user flow justification",
|
|
70
|
+
},
|
|
71
|
+
"Product Strategist": {
|
|
72
|
+
required: ["user-intent", "reasoning"],
|
|
73
|
+
recommended: ["spec", "review"],
|
|
74
|
+
description: "Must cite requirement fit, scope alignment, and tradeoff decisions",
|
|
75
|
+
},
|
|
76
|
+
"Security Reviewer": {
|
|
77
|
+
required: ["code", "reasoning"],
|
|
78
|
+
recommended: ["test", "measurement"],
|
|
79
|
+
description: "Must cite patterns checked, threats assessed, and mitigations verified",
|
|
80
|
+
},
|
|
81
|
+
"Performance Engineer": {
|
|
82
|
+
required: ["measurement", "output"],
|
|
83
|
+
recommended: ["code", "diff"],
|
|
84
|
+
description: "Must cite profiling results, metrics before/after, and budget compliance",
|
|
85
|
+
},
|
|
86
|
+
"Coverage Auditor": {
|
|
87
|
+
required: ["test", "measurement"],
|
|
88
|
+
recommended: ["code", "reasoning"],
|
|
89
|
+
description: "Must cite coverage metrics, false confidence findings, and untested paths",
|
|
90
|
+
},
|
|
91
|
+
"Release Engineer": {
|
|
92
|
+
required: ["output", "doc"],
|
|
93
|
+
recommended: ["test", "diff"],
|
|
94
|
+
description: "Must cite version, changelog, build output, and publish status",
|
|
95
|
+
},
|
|
96
|
+
"Deployment Verifier": {
|
|
97
|
+
required: ["output", "doc"],
|
|
98
|
+
recommended: ["test"],
|
|
99
|
+
description: "Must cite live artifact status, badge checks, and spot-check results",
|
|
100
|
+
},
|
|
101
|
+
"Dependency Auditor": {
|
|
102
|
+
required: ["measurement", "reasoning"],
|
|
103
|
+
recommended: ["output"],
|
|
104
|
+
description: "Must cite audit results, vulnerability findings, and supply-chain assessment",
|
|
105
|
+
},
|
|
106
|
+
"Brand Guardian": {
|
|
107
|
+
required: ["reasoning", "doc"],
|
|
108
|
+
recommended: ["diff"],
|
|
109
|
+
description: "Must cite terminology audit, contamination findings, and identity assessment",
|
|
110
|
+
},
|
|
111
|
+
"Repo Researcher": {
|
|
112
|
+
required: ["doc", "reasoning"],
|
|
113
|
+
recommended: ["code"],
|
|
114
|
+
description: "Must cite repo map, entrypoints found, build/test commands verified",
|
|
115
|
+
},
|
|
116
|
+
"Spec Writer": {
|
|
117
|
+
required: ["spec", "reasoning"],
|
|
118
|
+
recommended: ["user-intent"],
|
|
119
|
+
description: "Must cite acceptance criteria, edge cases enumerated, and NFRs defined",
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
// Default for roles without explicit requirements
|
|
124
|
+
const DEFAULT_REQUIREMENTS = {
|
|
125
|
+
required: ["reasoning"],
|
|
126
|
+
recommended: [],
|
|
127
|
+
description: "Must provide reasoning for the verdict",
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
// ── Evidence sufficiency check ────────────────────────────────────────────────
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* @typedef {Object} EvidenceItem
|
|
134
|
+
* @property {string} kind - One of EVIDENCE_KINDS
|
|
135
|
+
* @property {string} reference - File, path, packet ID, section
|
|
136
|
+
* @property {string} claim - What this evidence supports
|
|
137
|
+
* @property {string} status - One of EVIDENCE_STATUSES
|
|
138
|
+
* @property {string} [notes] - Additional context
|
|
139
|
+
*/
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* @typedef {Object} StructuredVerdict
|
|
143
|
+
* @property {string} verdict - One of VERDICT_TYPES
|
|
144
|
+
* @property {string} reviewerRole - Who is reviewing
|
|
145
|
+
* @property {string} summary - Brief verdict summary
|
|
146
|
+
* @property {EvidenceItem[]} evidence - Structured evidence items
|
|
147
|
+
* @property {string[]} gaps - What's missing or weak
|
|
148
|
+
* @property {string[]} risks - Identified risks
|
|
149
|
+
* @property {string} [requiredNextArtifact] - What the next role must produce (for non-approve)
|
|
150
|
+
* @property {string} confidence - One of CONFIDENCE_LEVELS
|
|
151
|
+
*/
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* @typedef {Object} SufficiencyResult
|
|
155
|
+
* @property {boolean} sufficient - Whether evidence meets role requirements
|
|
156
|
+
* @property {string[]} missingRequired - Required evidence kinds not provided
|
|
157
|
+
* @property {string[]} missingRecommended - Recommended evidence kinds not provided
|
|
158
|
+
* @property {string[]} contradictions - Evidence items that contradict the verdict
|
|
159
|
+
* @property {string[]} warnings - Non-blocking issues
|
|
160
|
+
*/
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Check whether a structured verdict has sufficient evidence for the reviewer role.
|
|
164
|
+
*
|
|
165
|
+
* @param {StructuredVerdict} verdict
|
|
166
|
+
* @returns {SufficiencyResult}
|
|
167
|
+
*/
|
|
168
|
+
export function checkSufficiency(verdict) {
|
|
169
|
+
const reqs = ROLE_EVIDENCE_REQUIREMENTS[verdict.reviewerRole] || DEFAULT_REQUIREMENTS;
|
|
170
|
+
const providedKinds = new Set(verdict.evidence.map(e => e.kind));
|
|
171
|
+
const warnings = [];
|
|
172
|
+
|
|
173
|
+
// Check required evidence kinds
|
|
174
|
+
const missingRequired = reqs.required.filter(kind => !providedKinds.has(kind));
|
|
175
|
+
|
|
176
|
+
// Check recommended evidence kinds
|
|
177
|
+
const missingRecommended = reqs.recommended.filter(kind => !providedKinds.has(kind));
|
|
178
|
+
|
|
179
|
+
// Check for contradictions
|
|
180
|
+
const contradictions = verdict.evidence
|
|
181
|
+
.filter(e => e.status === "contradicts")
|
|
182
|
+
.map(e => `${e.kind}: ${e.claim} (${e.reference})`);
|
|
183
|
+
|
|
184
|
+
if (contradictions.length > 0 && verdict.verdict === "accept") {
|
|
185
|
+
warnings.push("Verdict is 'approve' but evidence contains contradictions — review carefully");
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Check for missing evidence items on non-approve verdicts
|
|
189
|
+
const missingItems = verdict.evidence.filter(e => e.status === "missing");
|
|
190
|
+
if (missingItems.length > 0 && verdict.verdict === "accept") {
|
|
191
|
+
warnings.push("Verdict is 'approve' but some evidence items are marked 'missing'");
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Non-approve verdicts should have gaps or requiredNextArtifact
|
|
195
|
+
if (verdict.verdict !== "approve" && verdict.verdict !== "accept-with-notes") {
|
|
196
|
+
if (verdict.gaps.length === 0 && !verdict.requiredNextArtifact) {
|
|
197
|
+
warnings.push("Non-approve verdict should specify gaps or requiredNextArtifact for recovery");
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Low confidence + approve is suspicious
|
|
202
|
+
if (verdict.confidence === "low" && verdict.verdict === "accept") {
|
|
203
|
+
warnings.push("Low confidence approve — consider whether evidence is actually sufficient");
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const sufficient = missingRequired.length === 0 && contradictions.length === 0;
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
sufficient,
|
|
210
|
+
missingRequired,
|
|
211
|
+
missingRecommended,
|
|
212
|
+
contradictions,
|
|
213
|
+
warnings,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Get evidence requirements for a role.
|
|
219
|
+
*
|
|
220
|
+
* @param {string} roleName
|
|
221
|
+
* @returns {{required: string[], recommended: string[], description: string}}
|
|
222
|
+
*/
|
|
223
|
+
export function getRequirements(roleName) {
|
|
224
|
+
return ROLE_EVIDENCE_REQUIREMENTS[roleName] || DEFAULT_REQUIREMENTS;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Validate an evidence item's structure.
|
|
229
|
+
*
|
|
230
|
+
* @param {EvidenceItem} item
|
|
231
|
+
* @returns {string[]} validation errors (empty = valid)
|
|
232
|
+
*/
|
|
233
|
+
export function validateEvidenceItem(item) {
|
|
234
|
+
const errors = [];
|
|
235
|
+
if (!item.kind || !EVIDENCE_KINDS.includes(item.kind)) {
|
|
236
|
+
errors.push(`Invalid evidence kind: "${item.kind}". Valid: ${EVIDENCE_KINDS.join(", ")}`);
|
|
237
|
+
}
|
|
238
|
+
if (!item.reference || item.reference.trim().length === 0) {
|
|
239
|
+
errors.push("Evidence item must have a reference (file, path, section, or packet ID)");
|
|
240
|
+
}
|
|
241
|
+
if (!item.claim || item.claim.trim().length === 0) {
|
|
242
|
+
errors.push("Evidence item must have a claim (what it supports)");
|
|
243
|
+
}
|
|
244
|
+
if (!item.status || !EVIDENCE_STATUSES.includes(item.status)) {
|
|
245
|
+
errors.push(`Invalid evidence status: "${item.status}". Valid: ${EVIDENCE_STATUSES.join(", ")}`);
|
|
246
|
+
}
|
|
247
|
+
return errors;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Format a sufficiency result for operator display.
|
|
252
|
+
*
|
|
253
|
+
* @param {SufficiencyResult} result
|
|
254
|
+
* @param {string} roleName
|
|
255
|
+
* @returns {string}
|
|
256
|
+
*/
|
|
257
|
+
export function formatSufficiency(result, roleName) {
|
|
258
|
+
const lines = [];
|
|
259
|
+
const reqs = ROLE_EVIDENCE_REQUIREMENTS[roleName] || DEFAULT_REQUIREMENTS;
|
|
260
|
+
|
|
261
|
+
if (result.sufficient) {
|
|
262
|
+
lines.push(` ✓ Evidence sufficient for ${roleName}`);
|
|
263
|
+
} else {
|
|
264
|
+
lines.push(` ✗ Evidence insufficient for ${roleName}`);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (result.missingRequired.length > 0) {
|
|
268
|
+
lines.push(` missing (required): ${result.missingRequired.join(", ")}`);
|
|
269
|
+
lines.push(` ${roleName} requirement: ${reqs.description}`);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (result.missingRecommended.length > 0) {
|
|
273
|
+
lines.push(` missing (recommended): ${result.missingRecommended.join(", ")}`);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (result.contradictions.length > 0) {
|
|
277
|
+
lines.push(` contradictions:`);
|
|
278
|
+
for (const c of result.contradictions) {
|
|
279
|
+
lines.push(` - ${c}`);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
for (const w of result.warnings) {
|
|
284
|
+
lines.push(` ! ${w}`);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return lines.join("\n");
|
|
288
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { listPacks, getPack, suggestPack, TEAM_PACKS } from "./packs.mjs";
|
|
2
|
+
import { readFileSafe } from "./fs-utils.mjs";
|
|
3
|
+
import { basename } from "node:path";
|
|
4
|
+
|
|
5
|
+
// ── List ──────────────────────────────────────────────────────────────────────
|
|
6
|
+
|
|
7
|
+
function runList() {
|
|
8
|
+
const packs = listPacks();
|
|
9
|
+
console.log(`\nroleos packs — ${packs.length} packs available\n`);
|
|
10
|
+
for (const p of packs) {
|
|
11
|
+
const key = p.key.padEnd(12);
|
|
12
|
+
const name = p.name.padEnd(30);
|
|
13
|
+
const count = `(${p.roleCount} roles)`.padEnd(12);
|
|
14
|
+
console.log(` ${key}${name}${count}${p.description}`);
|
|
15
|
+
}
|
|
16
|
+
console.log();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// ── Suggest ───────────────────────────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
function runSuggest(packetFile) {
|
|
22
|
+
if (!packetFile) {
|
|
23
|
+
const err = new Error("Usage: roleos packs suggest <packet-file>");
|
|
24
|
+
err.exitCode = 1;
|
|
25
|
+
err.hint = "Provide the path to a packet file.";
|
|
26
|
+
throw err;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const content = readFileSafe(packetFile);
|
|
30
|
+
if (content === null) {
|
|
31
|
+
const err = new Error(`Packet not found: ${packetFile}`);
|
|
32
|
+
err.exitCode = 1;
|
|
33
|
+
err.hint = "Check the file path and try again.";
|
|
34
|
+
throw err;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const result = suggestPack(content);
|
|
38
|
+
const filename = basename(packetFile);
|
|
39
|
+
|
|
40
|
+
console.log(`\nroleos packs suggest — ${filename}\n`);
|
|
41
|
+
|
|
42
|
+
if (!result) {
|
|
43
|
+
console.log("No pack match found for this packet.");
|
|
44
|
+
console.log(" The packet has no recognizable keyword signals.");
|
|
45
|
+
console.log(" Add context or task description to improve routing.\n");
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
console.log(`Suggested pack: ${result.pack}`);
|
|
50
|
+
console.log(`Confidence: ${result.confidence}`);
|
|
51
|
+
|
|
52
|
+
if (Object.keys(result.scores).length > 0) {
|
|
53
|
+
console.log("\nScores:");
|
|
54
|
+
const sorted = Object.entries(result.scores).sort((a, b) => b[1] - a[1]);
|
|
55
|
+
for (const [packKey, score] of sorted) {
|
|
56
|
+
console.log(` ${packKey.padEnd(14)}${score}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
console.log(`\nNext: roleos route ${packetFile} --pack ${result.pack}\n`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ── Show ──────────────────────────────────────────────────────────────────────
|
|
64
|
+
|
|
65
|
+
function runShow(key) {
|
|
66
|
+
if (!key) {
|
|
67
|
+
const err = new Error("Usage: roleos packs show <pack-key>");
|
|
68
|
+
err.exitCode = 1;
|
|
69
|
+
err.hint = "Provide a pack key. Run 'roleos packs list' for options.";
|
|
70
|
+
throw err;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const pack = getPack(key);
|
|
74
|
+
if (!pack) {
|
|
75
|
+
const validKeys = Object.keys(TEAM_PACKS).join(", ");
|
|
76
|
+
const err = new Error(`Unknown pack: ${key}`);
|
|
77
|
+
err.exitCode = 1;
|
|
78
|
+
err.hint = `Valid pack keys: ${validKeys}`;
|
|
79
|
+
throw err;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
console.log(`\nroleos packs show — ${key}\n`);
|
|
83
|
+
console.log(`${pack.name}`);
|
|
84
|
+
console.log(`${pack.description}\n`);
|
|
85
|
+
|
|
86
|
+
console.log(`Roles (${pack.roles.length}):`);
|
|
87
|
+
pack.roles.forEach((r, i) => console.log(` ${i + 1}. ${r}`));
|
|
88
|
+
console.log();
|
|
89
|
+
|
|
90
|
+
if (pack.optionalRoles.length > 0) {
|
|
91
|
+
console.log("Optional roles:");
|
|
92
|
+
for (const r of pack.optionalRoles) console.log(` - ${r}`);
|
|
93
|
+
} else {
|
|
94
|
+
console.log("Optional roles: None");
|
|
95
|
+
}
|
|
96
|
+
console.log();
|
|
97
|
+
|
|
98
|
+
console.log(`Chain order:\n ${pack.chainOrder}\n`);
|
|
99
|
+
|
|
100
|
+
console.log("Required artifacts:");
|
|
101
|
+
for (const a of pack.requiredArtifacts) console.log(` - ${a}`);
|
|
102
|
+
console.log();
|
|
103
|
+
|
|
104
|
+
console.log("Stop conditions:");
|
|
105
|
+
for (const s of pack.stopConditions) console.log(` - ${s}`);
|
|
106
|
+
console.log();
|
|
107
|
+
|
|
108
|
+
console.log(`Escalation owner: ${pack.escalationOwner}\n`);
|
|
109
|
+
|
|
110
|
+
const d = pack.dispatchDefaults;
|
|
111
|
+
console.log("Dispatch defaults:");
|
|
112
|
+
console.log(` model: ${d.model}`);
|
|
113
|
+
console.log(` maxTurns: ${d.maxTurns}`);
|
|
114
|
+
console.log(` maxBudgetUsd: $${d.maxBudgetUsd.toFixed(2)}`);
|
|
115
|
+
console.log();
|
|
116
|
+
|
|
117
|
+
console.log(`Trial evidence: ${pack.trialEvidence}\n`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ── Command entry point ───────────────────────────────────────────────────────
|
|
121
|
+
|
|
122
|
+
export async function packsCommand(args) {
|
|
123
|
+
const sub = args[0];
|
|
124
|
+
|
|
125
|
+
switch (sub) {
|
|
126
|
+
case undefined:
|
|
127
|
+
case "list":
|
|
128
|
+
runList();
|
|
129
|
+
break;
|
|
130
|
+
case "suggest":
|
|
131
|
+
runSuggest(args[1]);
|
|
132
|
+
break;
|
|
133
|
+
case "show":
|
|
134
|
+
runShow(args[1]);
|
|
135
|
+
break;
|
|
136
|
+
default: {
|
|
137
|
+
const err = new Error(`Unknown packs subcommand: ${sub}`);
|
|
138
|
+
err.exitCode = 1;
|
|
139
|
+
err.hint = "Run 'roleos packs list' for available subcommands.";
|
|
140
|
+
throw err;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|