cognitive-runtime 0.3.0 → 0.5.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/README.md +39 -0
- package/dist/cli.js +1 -1
- package/dist/commands/pipe.js +8 -6
- package/dist/commands/run.js +23 -6
- package/dist/index.d.ts +1 -1
- package/dist/modules/loader.d.ts +4 -0
- package/dist/modules/loader.js +121 -15
- package/dist/modules/runner.d.ts +4 -4
- package/dist/modules/runner.js +186 -50
- package/dist/types.d.ts +83 -0
- package/dist/types.js +1 -0
- package/package.json +1 -1
- package/src/cli.ts +1 -1
- package/src/commands/pipe.ts +8 -6
- package/src/commands/run.ts +21 -6
- package/src/index.ts +5 -0
- package/src/modules/loader.ts +127 -14
- package/src/modules/runner.ts +211 -54
- package/src/types.ts +121 -1
package/src/modules/runner.ts
CHANGED
|
@@ -1,15 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Module Runner - Execute Cognitive Modules
|
|
3
|
+
* v2.1: Envelope format support, clean input mapping
|
|
3
4
|
*/
|
|
4
5
|
|
|
5
|
-
import type {
|
|
6
|
+
import type {
|
|
7
|
+
Provider,
|
|
8
|
+
CognitiveModule,
|
|
9
|
+
ModuleResult,
|
|
10
|
+
Message,
|
|
11
|
+
ModuleInput,
|
|
12
|
+
EnvelopeResponse,
|
|
13
|
+
ModuleResultData
|
|
14
|
+
} from '../types.js';
|
|
6
15
|
|
|
7
16
|
export interface RunOptions {
|
|
17
|
+
// Clean input (v2 style)
|
|
18
|
+
input?: ModuleInput;
|
|
19
|
+
|
|
20
|
+
// Legacy CLI args (v1 compatibility) - mapped to input.code or input.query
|
|
8
21
|
args?: string;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
validateOutput?: boolean;
|
|
22
|
+
|
|
23
|
+
// Runtime options
|
|
12
24
|
verbose?: boolean;
|
|
25
|
+
|
|
26
|
+
// Force envelope format (default: auto-detect from module.output.envelope)
|
|
27
|
+
useEnvelope?: boolean;
|
|
13
28
|
}
|
|
14
29
|
|
|
15
30
|
export async function runModule(
|
|
@@ -17,46 +32,90 @@ export async function runModule(
|
|
|
17
32
|
provider: Provider,
|
|
18
33
|
options: RunOptions = {}
|
|
19
34
|
): Promise<ModuleResult> {
|
|
20
|
-
const { args, input, verbose = false } = options;
|
|
35
|
+
const { args, input, verbose = false, useEnvelope } = options;
|
|
21
36
|
|
|
22
|
-
//
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
37
|
+
// Determine if we should use envelope format
|
|
38
|
+
const shouldUseEnvelope = useEnvelope ?? (module.output?.envelope === true || module.format === 'v2');
|
|
39
|
+
|
|
40
|
+
// Build clean input data (v2 style: no $ARGUMENTS pollution)
|
|
41
|
+
const inputData: ModuleInput = input || {};
|
|
42
|
+
|
|
43
|
+
// Map legacy --args to clean input
|
|
44
|
+
if (args && !inputData.code && !inputData.query) {
|
|
45
|
+
// Determine if args looks like code or natural language
|
|
46
|
+
if (looksLikeCode(args)) {
|
|
47
|
+
inputData.code = args;
|
|
48
|
+
} else {
|
|
49
|
+
inputData.query = args;
|
|
50
|
+
}
|
|
26
51
|
}
|
|
27
52
|
|
|
28
|
-
// Build prompt
|
|
53
|
+
// Build prompt with clean substitution
|
|
29
54
|
const prompt = buildPrompt(module, inputData);
|
|
30
55
|
|
|
31
56
|
if (verbose) {
|
|
57
|
+
console.error('--- Module ---');
|
|
58
|
+
console.error(`Name: ${module.name} (${module.format})`);
|
|
59
|
+
console.error(`Responsibility: ${module.responsibility}`);
|
|
60
|
+
console.error(`Envelope: ${shouldUseEnvelope}`);
|
|
61
|
+
console.error('--- Input ---');
|
|
62
|
+
console.error(JSON.stringify(inputData, null, 2));
|
|
32
63
|
console.error('--- Prompt ---');
|
|
33
64
|
console.error(prompt);
|
|
34
|
-
console.error('--- End
|
|
65
|
+
console.error('--- End ---');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Build system message based on module config
|
|
69
|
+
const systemParts: string[] = [
|
|
70
|
+
`You are executing the "${module.name}" Cognitive Module.`,
|
|
71
|
+
'',
|
|
72
|
+
`RESPONSIBILITY: ${module.responsibility}`,
|
|
73
|
+
];
|
|
74
|
+
|
|
75
|
+
if (module.excludes.length > 0) {
|
|
76
|
+
systemParts.push('', 'YOU MUST NOT:');
|
|
77
|
+
module.excludes.forEach(e => systemParts.push(`- ${e}`));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (module.constraints) {
|
|
81
|
+
systemParts.push('', 'CONSTRAINTS:');
|
|
82
|
+
if (module.constraints.no_network) systemParts.push('- No network access');
|
|
83
|
+
if (module.constraints.no_side_effects) systemParts.push('- No side effects');
|
|
84
|
+
if (module.constraints.no_file_write) systemParts.push('- No file writes');
|
|
85
|
+
if (module.constraints.no_inventing_data) systemParts.push('- Do not invent data');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (module.output?.require_behavior_equivalence) {
|
|
89
|
+
systemParts.push('', 'BEHAVIOR EQUIVALENCE:');
|
|
90
|
+
systemParts.push('- You MUST set behavior_equivalence=true ONLY if the output is functionally identical');
|
|
91
|
+
systemParts.push('- If unsure, set behavior_equivalence=false and explain in rationale');
|
|
92
|
+
|
|
93
|
+
const maxConfidence = module.constraints?.behavior_equivalence_false_max_confidence ?? 0.7;
|
|
94
|
+
systemParts.push(`- If behavior_equivalence=false, confidence MUST be <= ${maxConfidence}`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Add envelope format instructions
|
|
98
|
+
if (shouldUseEnvelope) {
|
|
99
|
+
systemParts.push('', 'RESPONSE FORMAT (Envelope):');
|
|
100
|
+
systemParts.push('- Wrap your response in the envelope format');
|
|
101
|
+
systemParts.push('- Success: { "ok": true, "data": { ...your output... } }');
|
|
102
|
+
systemParts.push('- Error: { "ok": false, "error": { "code": "ERROR_CODE", "message": "..." } }');
|
|
103
|
+
systemParts.push('- Include "confidence" (0-1) and "rationale" in data');
|
|
104
|
+
if (module.output?.require_behavior_equivalence) {
|
|
105
|
+
systemParts.push('- Include "behavior_equivalence" (boolean) in data');
|
|
106
|
+
}
|
|
107
|
+
} else {
|
|
108
|
+
systemParts.push('', 'OUTPUT FORMAT:');
|
|
109
|
+
systemParts.push('- Respond with ONLY valid JSON');
|
|
110
|
+
systemParts.push('- Include "confidence" (0-1) and "rationale" fields');
|
|
111
|
+
if (module.output?.require_behavior_equivalence) {
|
|
112
|
+
systemParts.push('- Include "behavior_equivalence" (boolean) field');
|
|
113
|
+
}
|
|
35
114
|
}
|
|
36
115
|
|
|
37
|
-
// Build messages
|
|
38
116
|
const messages: Message[] = [
|
|
39
|
-
{
|
|
40
|
-
|
|
41
|
-
content: `You are executing the "${module.name}" Cognitive Module.
|
|
42
|
-
|
|
43
|
-
RESPONSIBILITY: ${module.responsibility}
|
|
44
|
-
|
|
45
|
-
YOU MUST NOT:
|
|
46
|
-
${module.excludes.map(e => `- ${e}`).join('\n')}
|
|
47
|
-
|
|
48
|
-
REQUIRED OUTPUT FORMAT:
|
|
49
|
-
You MUST respond with a valid JSON object. Include these fields:
|
|
50
|
-
- All fields required by the output schema
|
|
51
|
-
- "confidence": a number between 0 and 1
|
|
52
|
-
- "rationale": a string explaining your reasoning
|
|
53
|
-
|
|
54
|
-
Respond with ONLY valid JSON, no markdown code blocks.`,
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
role: 'user',
|
|
58
|
-
content: prompt,
|
|
59
|
-
},
|
|
117
|
+
{ role: 'system', content: systemParts.join('\n') },
|
|
118
|
+
{ role: 'user', content: prompt },
|
|
60
119
|
];
|
|
61
120
|
|
|
62
121
|
// Invoke provider
|
|
@@ -73,49 +132,147 @@ Respond with ONLY valid JSON, no markdown code blocks.`,
|
|
|
73
132
|
}
|
|
74
133
|
|
|
75
134
|
// Parse response
|
|
76
|
-
let
|
|
135
|
+
let parsed: unknown;
|
|
77
136
|
try {
|
|
78
|
-
// Try to extract JSON from markdown code blocks
|
|
79
137
|
const jsonMatch = result.content.match(/```(?:json)?\s*([\s\S]*?)\s*```/);
|
|
80
138
|
const jsonStr = jsonMatch ? jsonMatch[1] : result.content;
|
|
81
|
-
|
|
139
|
+
parsed = JSON.parse(jsonStr.trim());
|
|
82
140
|
} catch {
|
|
83
141
|
throw new Error(`Failed to parse JSON response: ${result.content.substring(0, 500)}`);
|
|
84
142
|
}
|
|
85
143
|
|
|
86
|
-
//
|
|
144
|
+
// Handle envelope format
|
|
145
|
+
if (shouldUseEnvelope && isEnvelopeResponse(parsed)) {
|
|
146
|
+
return parseEnvelopeResponse(parsed, result.content);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Handle legacy format (non-envelope)
|
|
150
|
+
return parseLegacyResponse(parsed, result.content);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Check if response is in envelope format
|
|
155
|
+
*/
|
|
156
|
+
function isEnvelopeResponse(obj: unknown): obj is EnvelopeResponse {
|
|
157
|
+
if (typeof obj !== 'object' || obj === null) return false;
|
|
158
|
+
const o = obj as Record<string, unknown>;
|
|
159
|
+
return typeof o.ok === 'boolean';
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Parse envelope format response
|
|
164
|
+
*/
|
|
165
|
+
function parseEnvelopeResponse(response: EnvelopeResponse, raw: string): ModuleResult {
|
|
166
|
+
if (response.ok) {
|
|
167
|
+
const data = response.data as ModuleResultData;
|
|
168
|
+
return {
|
|
169
|
+
ok: true,
|
|
170
|
+
data: {
|
|
171
|
+
...data,
|
|
172
|
+
confidence: typeof data.confidence === 'number' ? data.confidence : 0.5,
|
|
173
|
+
rationale: typeof data.rationale === 'string' ? data.rationale : '',
|
|
174
|
+
behavior_equivalence: data.behavior_equivalence,
|
|
175
|
+
},
|
|
176
|
+
raw,
|
|
177
|
+
};
|
|
178
|
+
} else {
|
|
179
|
+
return {
|
|
180
|
+
ok: false,
|
|
181
|
+
error: response.error,
|
|
182
|
+
partial_data: response.partial_data,
|
|
183
|
+
raw,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Parse legacy (non-envelope) format response
|
|
190
|
+
*/
|
|
191
|
+
function parseLegacyResponse(output: unknown, raw: string): ModuleResult {
|
|
87
192
|
const outputObj = output as Record<string, unknown>;
|
|
88
193
|
const confidence = typeof outputObj.confidence === 'number' ? outputObj.confidence : 0.5;
|
|
89
194
|
const rationale = typeof outputObj.rationale === 'string' ? outputObj.rationale : '';
|
|
195
|
+
const behaviorEquivalence = typeof outputObj.behavior_equivalence === 'boolean'
|
|
196
|
+
? outputObj.behavior_equivalence
|
|
197
|
+
: undefined;
|
|
198
|
+
|
|
199
|
+
// Check if this is an error response (has error.code)
|
|
200
|
+
if (outputObj.error && typeof outputObj.error === 'object') {
|
|
201
|
+
const errorObj = outputObj.error as Record<string, unknown>;
|
|
202
|
+
if (typeof errorObj.code === 'string') {
|
|
203
|
+
return {
|
|
204
|
+
ok: false,
|
|
205
|
+
error: {
|
|
206
|
+
code: errorObj.code,
|
|
207
|
+
message: typeof errorObj.message === 'string' ? errorObj.message : 'Unknown error',
|
|
208
|
+
},
|
|
209
|
+
raw,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
}
|
|
90
213
|
|
|
91
214
|
return {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
215
|
+
ok: true,
|
|
216
|
+
data: {
|
|
217
|
+
...outputObj,
|
|
218
|
+
confidence,
|
|
219
|
+
rationale,
|
|
220
|
+
behavior_equivalence: behaviorEquivalence,
|
|
221
|
+
} as ModuleResultData,
|
|
222
|
+
raw,
|
|
96
223
|
};
|
|
97
224
|
}
|
|
98
225
|
|
|
99
|
-
|
|
226
|
+
/**
|
|
227
|
+
* Build prompt with clean variable substitution
|
|
228
|
+
*/
|
|
229
|
+
function buildPrompt(module: CognitiveModule, input: ModuleInput): string {
|
|
100
230
|
let prompt = module.prompt;
|
|
101
231
|
|
|
102
|
-
//
|
|
103
|
-
const
|
|
232
|
+
// v2 style: substitute ${variable} placeholders
|
|
233
|
+
for (const [key, value] of Object.entries(input)) {
|
|
234
|
+
const strValue = typeof value === 'string' ? value : JSON.stringify(value);
|
|
235
|
+
prompt = prompt.replace(new RegExp(`\\$\\{${key}\\}`, 'g'), strValue);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// v1 compatibility: substitute $ARGUMENTS
|
|
239
|
+
const argsValue = input.code || input.query || '';
|
|
104
240
|
prompt = prompt.replace(/\$ARGUMENTS/g, argsValue);
|
|
105
241
|
|
|
106
|
-
// Substitute $N placeholders
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
242
|
+
// Substitute $N placeholders (v1 compatibility)
|
|
243
|
+
if (typeof argsValue === 'string') {
|
|
244
|
+
const argsList = argsValue.split(/\s+/);
|
|
245
|
+
argsList.forEach((arg, i) => {
|
|
246
|
+
prompt = prompt.replace(new RegExp(`\\$${i}\\b`, 'g'), arg);
|
|
247
|
+
});
|
|
248
|
+
}
|
|
112
249
|
|
|
113
|
-
//
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
250
|
+
// Append input summary if not already in prompt
|
|
251
|
+
if (!prompt.includes(argsValue) && argsValue) {
|
|
252
|
+
prompt += '\n\n## Input\n\n';
|
|
253
|
+
if (input.code) {
|
|
254
|
+
prompt += '```\n' + input.code + '\n```\n';
|
|
255
|
+
}
|
|
256
|
+
if (input.query) {
|
|
257
|
+
prompt += input.query + '\n';
|
|
258
|
+
}
|
|
259
|
+
if (input.language) {
|
|
260
|
+
prompt += `\nLanguage: ${input.language}\n`;
|
|
117
261
|
}
|
|
118
262
|
}
|
|
119
263
|
|
|
120
264
|
return prompt;
|
|
121
265
|
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Heuristic to detect if input looks like code
|
|
269
|
+
*/
|
|
270
|
+
function looksLikeCode(str: string): boolean {
|
|
271
|
+
const codeIndicators = [
|
|
272
|
+
/^(def|function|class|const|let|var|import|export|public|private)\s/,
|
|
273
|
+
/[{};()]/,
|
|
274
|
+
/=>/,
|
|
275
|
+
/\.(py|js|ts|go|rs|java|cpp|c|rb)$/,
|
|
276
|
+
];
|
|
277
|
+
return codeIndicators.some(re => re.test(str));
|
|
278
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Cognitive Runtime - Core Types
|
|
3
|
+
* Version 2.1 - With envelope format, tools policy, failure contract
|
|
3
4
|
*/
|
|
4
5
|
|
|
5
6
|
// Provider interface - all LLM providers implement this
|
|
@@ -30,23 +31,133 @@ export interface InvokeResult {
|
|
|
30
31
|
};
|
|
31
32
|
}
|
|
32
33
|
|
|
33
|
-
// Module types
|
|
34
|
+
// Module types (v2.1)
|
|
34
35
|
export interface CognitiveModule {
|
|
36
|
+
// Core identity
|
|
35
37
|
name: string;
|
|
36
38
|
version: string;
|
|
37
39
|
responsibility: string;
|
|
40
|
+
|
|
41
|
+
// Constraints
|
|
38
42
|
excludes: string[];
|
|
43
|
+
constraints?: ModuleConstraints;
|
|
44
|
+
|
|
45
|
+
// Unified policies (v2.1)
|
|
46
|
+
policies?: ModulePolicies;
|
|
47
|
+
|
|
48
|
+
// Tools policy
|
|
49
|
+
tools?: ToolsPolicy;
|
|
50
|
+
|
|
51
|
+
// Output contract
|
|
52
|
+
output?: OutputContract;
|
|
53
|
+
|
|
54
|
+
// Failure contract
|
|
55
|
+
failure?: FailureContract;
|
|
56
|
+
|
|
57
|
+
// Runtime requirements
|
|
58
|
+
runtimeRequirements?: RuntimeRequirements;
|
|
59
|
+
|
|
60
|
+
// Execution context
|
|
39
61
|
context?: 'fork' | 'main';
|
|
62
|
+
|
|
63
|
+
// Prompt (from prompt.md or MODULE.md body)
|
|
40
64
|
prompt: string;
|
|
65
|
+
|
|
66
|
+
// Schemas
|
|
41
67
|
inputSchema?: object;
|
|
42
68
|
outputSchema?: object;
|
|
69
|
+
errorSchema?: object;
|
|
70
|
+
|
|
71
|
+
// Metadata
|
|
43
72
|
location: string;
|
|
73
|
+
format: 'v1' | 'v2'; // v1 = MODULE.md, v2 = module.yaml + prompt.md
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface ModuleConstraints {
|
|
77
|
+
no_network?: boolean;
|
|
78
|
+
no_side_effects?: boolean;
|
|
79
|
+
no_file_write?: boolean;
|
|
80
|
+
no_inventing_data?: boolean;
|
|
81
|
+
behavior_equivalence_false_max_confidence?: number;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface ModulePolicies {
|
|
85
|
+
network?: 'allow' | 'deny';
|
|
86
|
+
filesystem_write?: 'allow' | 'deny';
|
|
87
|
+
side_effects?: 'allow' | 'deny';
|
|
88
|
+
code_execution?: 'allow' | 'deny';
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface ToolsPolicy {
|
|
92
|
+
policy?: 'allow_by_default' | 'deny_by_default';
|
|
93
|
+
allowed: string[];
|
|
94
|
+
denied?: string[];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface OutputContract {
|
|
98
|
+
format?: 'json_strict' | 'json_lenient' | 'text';
|
|
99
|
+
envelope?: boolean; // v2.1: Use {ok, data/error} wrapper
|
|
100
|
+
require?: string[];
|
|
101
|
+
require_confidence?: boolean;
|
|
102
|
+
require_rationale?: boolean;
|
|
103
|
+
require_behavior_equivalence?: boolean;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface FailureContract {
|
|
107
|
+
contract?: 'error_union' | 'throw';
|
|
108
|
+
partial_allowed?: boolean;
|
|
109
|
+
must_return_error_schema?: boolean;
|
|
110
|
+
schema?: object;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export interface RuntimeRequirements {
|
|
114
|
+
structured_output?: boolean;
|
|
115
|
+
max_input_tokens?: number;
|
|
116
|
+
preferred_capabilities?: string[];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Envelope response format (v2.1)
|
|
120
|
+
export interface EnvelopeSuccess<T = unknown> {
|
|
121
|
+
ok: true;
|
|
122
|
+
data: T;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export interface EnvelopeError {
|
|
126
|
+
ok: false;
|
|
127
|
+
error: {
|
|
128
|
+
code: string;
|
|
129
|
+
message: string;
|
|
130
|
+
};
|
|
131
|
+
partial_data?: unknown;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export type EnvelopeResponse<T = unknown> = EnvelopeSuccess<T> | EnvelopeError;
|
|
135
|
+
|
|
136
|
+
// Module result types
|
|
137
|
+
export interface ModuleResultData {
|
|
138
|
+
[key: string]: unknown;
|
|
139
|
+
confidence: number;
|
|
140
|
+
rationale: string;
|
|
141
|
+
behavior_equivalence?: boolean;
|
|
44
142
|
}
|
|
45
143
|
|
|
46
144
|
export interface ModuleResult {
|
|
145
|
+
ok: boolean;
|
|
146
|
+
data?: ModuleResultData;
|
|
147
|
+
error?: {
|
|
148
|
+
code: string;
|
|
149
|
+
message: string;
|
|
150
|
+
};
|
|
151
|
+
partial_data?: unknown;
|
|
152
|
+
raw?: string;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Legacy result format (for backward compatibility)
|
|
156
|
+
export interface LegacyModuleResult {
|
|
47
157
|
output: unknown;
|
|
48
158
|
confidence: number;
|
|
49
159
|
rationale: string;
|
|
160
|
+
behaviorEquivalence?: boolean;
|
|
50
161
|
raw?: string;
|
|
51
162
|
}
|
|
52
163
|
|
|
@@ -62,3 +173,12 @@ export interface CommandResult {
|
|
|
62
173
|
data?: unknown;
|
|
63
174
|
error?: string;
|
|
64
175
|
}
|
|
176
|
+
|
|
177
|
+
// Module input (clean, no CLI pollution)
|
|
178
|
+
export interface ModuleInput {
|
|
179
|
+
code?: string;
|
|
180
|
+
query?: string;
|
|
181
|
+
language?: string;
|
|
182
|
+
options?: Record<string, unknown>;
|
|
183
|
+
[key: string]: unknown;
|
|
184
|
+
}
|