@miller-tech/uap 2.36.1 → 2.39.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/config/model-profiles/qwen38.json +3 -3
- package/config/model-registry.json +96 -6
- package/dist/.tsbuildinfo +1 -1
- package/dist/bin/cli.js +57 -0
- package/dist/bin/cli.js.map +1 -1
- package/dist/cli/deliver.d.ts +46 -0
- package/dist/cli/deliver.d.ts.map +1 -1
- package/dist/cli/deliver.js +155 -36
- package/dist/cli/deliver.js.map +1 -1
- package/dist/cli/models.d.ts +11 -1
- package/dist/cli/models.d.ts.map +1 -1
- package/dist/cli/models.js +164 -3
- package/dist/cli/models.js.map +1 -1
- package/dist/cli/workflow.d.ts +36 -0
- package/dist/cli/workflow.d.ts.map +1 -0
- package/dist/cli/workflow.js +246 -0
- package/dist/cli/workflow.js.map +1 -0
- package/dist/delivery/run-state.d.ts +1 -1
- package/dist/delivery/run-state.d.ts.map +1 -1
- package/dist/delivery/run-state.js +1 -1
- package/dist/delivery/run-state.js.map +1 -1
- package/dist/delivery/team-solve-deliver.d.ts +83 -0
- package/dist/delivery/team-solve-deliver.d.ts.map +1 -0
- package/dist/delivery/team-solve-deliver.js +191 -0
- package/dist/delivery/team-solve-deliver.js.map +1 -0
- package/dist/inference/model-files.d.ts +111 -0
- package/dist/inference/model-files.d.ts.map +1 -0
- package/dist/inference/model-files.js +172 -0
- package/dist/inference/model-files.js.map +1 -0
- package/dist/placement/registry.d.ts +650 -0
- package/dist/placement/registry.d.ts.map +1 -1
- package/dist/placement/registry.js +21 -0
- package/dist/placement/registry.js.map +1 -1
- package/dist/workflow/loader.d.ts +34 -0
- package/dist/workflow/loader.d.ts.map +1 -0
- package/dist/workflow/loader.js +232 -0
- package/dist/workflow/loader.js.map +1 -0
- package/dist/workflow/runner.d.ts +90 -0
- package/dist/workflow/runner.d.ts.map +1 -0
- package/dist/workflow/runner.js +416 -0
- package/dist/workflow/runner.js.map +1 -0
- package/dist/workflow/types.d.ts +400 -0
- package/dist/workflow/types.d.ts.map +1 -0
- package/dist/workflow/types.js +147 -0
- package/dist/workflow/types.js.map +1 -0
- package/docs/INDEX.md +1 -0
- package/docs/architecture/adr/0013-declarative-workflow-layer.md +99 -0
- package/docs/architecture/adr/0014-loop-steps-and-team-solve-deliver.md +142 -0
- package/docs/benchmarks/LCB_DROID_SMOKE.md +111 -0
- package/docs/guides/DELIVER.md +3 -1
- package/docs/guides/WORKFLOWS.md +179 -0
- package/docs/plans/model-performance-proof-options.md +39 -0
- package/docs/reference/CLI.md +33 -0
- package/docs/reference/CONFIGURATION.md +1 -0
- package/docs/reference/FEATURES.md +18 -0
- package/examples/auto-coordinator-example.js +127 -0
- package/examples/demo-order-solid.ts +19 -0
- package/examples/order-processing-strategy-pattern.ts +186 -0
- package/examples/order-processor-refactored.ts +473 -0
- package/examples/order-solid-refactor.ts +830 -0
- package/examples/refactored-order-processing.ts +106 -0
- package/examples/run-order-processing.ts +48 -0
- package/examples/solid-order-processing.ts +324 -0
- package/examples/strategy-pattern-example.ts +100 -0
- package/examples/workflows/team-solve/README.md +85 -0
- package/examples/workflows/team-solve/check-samples.sh +50 -0
- package/examples/workflows/team-solve/sample/task.md +22 -0
- package/examples/workflows/team-solve/sample/tests/1.in +2 -0
- package/examples/workflows/team-solve/sample/tests/1.out +1 -0
- package/examples/workflows/team-solve/sample/tests/2.in +2 -0
- package/examples/workflows/team-solve/sample/tests/2.out +1 -0
- package/examples/workflows/team-solve/sample/tests/3.in +2 -0
- package/examples/workflows/team-solve/sample/tests/3.out +1 -0
- package/examples/workflows/team-solve/sample/tests/4.in +2 -0
- package/examples/workflows/team-solve/sample/tests/4.out +1 -0
- package/examples/workflows/team-solve/sample/tests/5.in +1 -0
- package/examples/workflows/team-solve/sample/tests/5.out +1 -0
- package/examples/workflows/team-solve/steps/ideate-a.md +26 -0
- package/examples/workflows/team-solve/steps/ideate-b.md +28 -0
- package/examples/workflows/team-solve/steps/plan.md +20 -0
- package/examples/workflows/team-solve/steps/repair.md +25 -0
- package/examples/workflows/team-solve/steps/review.md +29 -0
- package/examples/workflows/team-solve/steps/solve.md +26 -0
- package/examples/workflows/team-solve/workflow.yaml +78 -0
- package/examples/workflows/team-solve-deliver/README.md +84 -0
- package/examples/workflows/team-solve-deliver/steps/ideate-a.md +25 -0
- package/examples/workflows/team-solve-deliver/steps/ideate-b.md +28 -0
- package/examples/workflows/team-solve-deliver/steps/plan.md +25 -0
- package/examples/workflows/team-solve-deliver/steps/repair.md +20 -0
- package/examples/workflows/team-solve-deliver/steps/review.md +31 -0
- package/examples/workflows/team-solve-deliver/steps/solve.md +28 -0
- package/examples/workflows/team-solve-deliver/steps/verify.md +9 -0
- package/examples/workflows/team-solve-deliver/workflow.yaml +74 -0
- package/package.json +2 -1
- package/src/policies/enforcers/__pycache__/_common.cpython-311.pyc +0 -0
- package/src/policies/enforcers/__pycache__/expert_review_required.cpython-311.pyc +0 -0
- package/src/policies/enforcers/__pycache__/quality_metrics_gate.cpython-311.pyc +0 -0
- package/templates/hooks/__pycache__/deliver_autoroute.cpython-311.pyc +0 -0
- package/tools/agents/scripts/__pycache__/toolcall_path_normalizer.cpython-311.pyc +0 -0
- package/tools/agents/scripts/anthropic_proxy.py +181 -15
- package/tools/agents/tests/test_signature_quality.py +51 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Auto-Coordinator Example
|
|
4
|
+
*
|
|
5
|
+
* Demonstrates automatic agent registration and multi-agent coordination.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* node examples/auto-coordinator-example.js
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { createAutoAgent } from '../src/coordination/auto-agent.js';
|
|
12
|
+
import { TaskService } from '../src/tasks/service.js';
|
|
13
|
+
import { TaskCoordinator } from '../src/tasks/coordination.js';
|
|
14
|
+
|
|
15
|
+
async function main() {
|
|
16
|
+
console.log('=== Multi-Agent Coordination Example ===\n');
|
|
17
|
+
|
|
18
|
+
// Example 1: Create an auto-agent
|
|
19
|
+
console.log('1. Creating auto-agent with automatic registration...');
|
|
20
|
+
const autoAgent = createAutoAgent({
|
|
21
|
+
name: 'dev-agent-1',
|
|
22
|
+
capabilities: ['coding', 'testing', 'review'],
|
|
23
|
+
worktreeBranch: 'feature/new-dashboard',
|
|
24
|
+
heartbeatIntervalMs: 30000,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const result = await autoAgent.start();
|
|
28
|
+
console.log(` ✓ Agent registered: ${result.agent.id}`);
|
|
29
|
+
console.log(` ✓ Agent name: ${result.agent.name}`);
|
|
30
|
+
console.log(` ✓ Status: ${result.agent.status}`);
|
|
31
|
+
console.log(` ✓ Worktree: ${result.agent.worktreeBranch}\n`);
|
|
32
|
+
|
|
33
|
+
// Example 2: Announce work on a resource
|
|
34
|
+
console.log('2. Announcing work on resource...');
|
|
35
|
+
const announcementResult = autoAgent.announceWork('src/components/Dashboard.tsx', 'editing', {
|
|
36
|
+
description: 'Implementing new dashboard component',
|
|
37
|
+
filesAffected: ['src/components/Dashboard.tsx', 'src/types/dashboard.ts'],
|
|
38
|
+
estimatedMinutes: 120,
|
|
39
|
+
});
|
|
40
|
+
console.log(` ✓ Announced: ${announcementResult.announcement.resource}`);
|
|
41
|
+
console.log(` ✓ Intent: ${announcementResult.announcement.intentType}`);
|
|
42
|
+
console.log(` ✓ Overlaps detected: ${announcementResult.overlaps.length}\n`);
|
|
43
|
+
|
|
44
|
+
// Example 3: Check for overlaps
|
|
45
|
+
console.log('3. Checking for overlaps...');
|
|
46
|
+
const overlaps = autoAgent.checkOverlaps('src/components/Dashboard.tsx');
|
|
47
|
+
if (overlaps.length > 0) {
|
|
48
|
+
console.log(` ⚠️ Found ${overlaps.length} overlap(s):`);
|
|
49
|
+
for (const overlap of overlaps) {
|
|
50
|
+
console.log(` - ${overlap.conflictRisk}: ${overlap.agents.length} agents`);
|
|
51
|
+
}
|
|
52
|
+
} else {
|
|
53
|
+
console.log(' ✓ No overlaps detected\n');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Example 4: Integrate with task coordination
|
|
57
|
+
console.log('4. Integrating with task coordination...');
|
|
58
|
+
const taskService = new TaskService();
|
|
59
|
+
const taskCoordinator = new TaskCoordinator({
|
|
60
|
+
taskService,
|
|
61
|
+
coordinationService: autoAgent.getService(),
|
|
62
|
+
agentId: autoAgent.getAgentId(),
|
|
63
|
+
agentName: autoAgent.getAgentId().slice(0, 8),
|
|
64
|
+
worktreeBranch: 'feature/new-dashboard',
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Suggest next task
|
|
68
|
+
const suggestedTask = taskCoordinator.suggestNextTask();
|
|
69
|
+
if (suggestedTask) {
|
|
70
|
+
console.log(
|
|
71
|
+
` ✓ Suggested task: ${suggestedTask.title} (Priority: ${suggestedTask.priority})`
|
|
72
|
+
);
|
|
73
|
+
} else {
|
|
74
|
+
console.log(' ✓ No ready tasks found\n');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Example 5: Claim a task with coordination
|
|
78
|
+
console.log('5. Claiming task with coordination...');
|
|
79
|
+
try {
|
|
80
|
+
// First, create a test task
|
|
81
|
+
const testTask = taskService.create({
|
|
82
|
+
title: 'Test Task for Coordination',
|
|
83
|
+
type: 'task',
|
|
84
|
+
priority: 1,
|
|
85
|
+
labels: ['coordination', 'test'],
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Claim it
|
|
89
|
+
const claimResult = await taskCoordinator.claim(testTask.id);
|
|
90
|
+
if (claimResult) {
|
|
91
|
+
console.log(` ✓ Task claimed: ${claimResult.task.title}`);
|
|
92
|
+
console.log(` ✓ Worktree: ${claimResult.worktreeBranch}`);
|
|
93
|
+
console.log(` ✓ Announced work to coordination system`);
|
|
94
|
+
}
|
|
95
|
+
} catch (error) {
|
|
96
|
+
console.log(` ⚠️ Could not claim task: ${error.message}`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Example 6: Simulate another agent
|
|
100
|
+
console.log('\n6. Simulating second agent...');
|
|
101
|
+
const autoAgent2 = createAutoAgent({
|
|
102
|
+
name: 'dev-agent-2',
|
|
103
|
+
capabilities: ['testing', 'review'],
|
|
104
|
+
worktreeBranch: 'fix/login-bug',
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const result2 = await autoAgent2.start();
|
|
108
|
+
console.log(` ✓ Agent 2 registered: ${result2.agent.id}`);
|
|
109
|
+
|
|
110
|
+
// Announce work on same resource
|
|
111
|
+
const announcement2 = autoAgent2.announceWork('src/components/Dashboard.tsx', 'reviewing', {
|
|
112
|
+
description: 'Reviewing dashboard component',
|
|
113
|
+
estimatedMinutes: 30,
|
|
114
|
+
});
|
|
115
|
+
console.log(` ✓ Agent 2 announced work on same resource`);
|
|
116
|
+
console.log(` ✓ Overlaps detected: ${announcement2.overlaps.length}`);
|
|
117
|
+
|
|
118
|
+
// Example 7: Cleanup
|
|
119
|
+
console.log('\n7. Cleaning up agents...');
|
|
120
|
+
result.cleanup();
|
|
121
|
+
result2.cleanup();
|
|
122
|
+
console.log(' ✓ Agents marked as completed');
|
|
123
|
+
|
|
124
|
+
console.log('\n=== Example Complete ===');
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
main().catch(console.error);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Demo script to showcase the SOLID-refactored order processing system.
|
|
3
|
+
*
|
|
4
|
+
* Run with: tsx examples/demo-order-solid.ts
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { runExamples } from './order-solid-refactor.js';
|
|
8
|
+
|
|
9
|
+
console.log('========================================');
|
|
10
|
+
console.log('SOLID Order Processing System Demo');
|
|
11
|
+
console.log('========================================');
|
|
12
|
+
console.log('');
|
|
13
|
+
|
|
14
|
+
// Run all examples
|
|
15
|
+
runExamples();
|
|
16
|
+
|
|
17
|
+
console.log('========================================');
|
|
18
|
+
console.log('Demo Complete');
|
|
19
|
+
console.log('========================================');
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
// ============================================
|
|
2
|
+
// Strategy Pattern: Order Processing Refactor
|
|
3
|
+
// ============================================
|
|
4
|
+
|
|
5
|
+
// Strategy interface
|
|
6
|
+
interface OrderProcessingStrategy {
|
|
7
|
+
process(order: Order): Order;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// Order interface for type safety
|
|
11
|
+
interface Order {
|
|
12
|
+
type: string;
|
|
13
|
+
status?: string; // Status is optional, computed during processing
|
|
14
|
+
[key: string]: any; // Allow additional properties
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// ===== Concrete Strategy Implementations =====
|
|
18
|
+
|
|
19
|
+
class DigitalOrderStrategy implements OrderProcessingStrategy {
|
|
20
|
+
process(order: Order): Order {
|
|
21
|
+
// Assume: order is a valid digital order with required fields
|
|
22
|
+
// Condition: order.type should be 'digital'
|
|
23
|
+
// Edge case: Order might be null/undefined - we return a new object to maintain immutability
|
|
24
|
+
return { ...order, status: 'delivered' };
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
class PhysicalOrderStrategy implements OrderProcessingStrategy {
|
|
29
|
+
process(order: Order): Order {
|
|
30
|
+
// Assume: order is a valid physical order with shipping address
|
|
31
|
+
// Condition: order.type should be 'physical'
|
|
32
|
+
return { ...order, status: 'shipped' };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
class SubscriptionOrderStrategy implements OrderProcessingStrategy {
|
|
37
|
+
process(order: Order): Order {
|
|
38
|
+
// Assume: order is a valid subscription order with billing info
|
|
39
|
+
// Condition: order.type should be 'subscription'
|
|
40
|
+
return { ...order, status: 'active' };
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Default/fallback strategy for unknown order types
|
|
45
|
+
class DefaultOrderStrategy implements OrderProcessingStrategy {
|
|
46
|
+
process(order: Order): Order {
|
|
47
|
+
// Assume: order is valid but type is unknown
|
|
48
|
+
// Condition: Handles any unrecognized order type gracefully
|
|
49
|
+
return { ...order, status: 'pending' };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ===== Strategy Factory =====
|
|
54
|
+
|
|
55
|
+
class OrderProcessingStrategyFactory {
|
|
56
|
+
private strategies: Map<string, OrderProcessingStrategy> = new Map();
|
|
57
|
+
|
|
58
|
+
constructor() {
|
|
59
|
+
// Register default strategies
|
|
60
|
+
this.registerStrategy('digital', new DigitalOrderStrategy());
|
|
61
|
+
this.registerStrategy('physical', new PhysicalOrderStrategy());
|
|
62
|
+
this.registerStrategy('subscription', new SubscriptionOrderStrategy());
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
registerStrategy(type: string, strategy: OrderProcessingStrategy): void {
|
|
66
|
+
// Assume: type is a non-empty string, strategy is valid
|
|
67
|
+
// Condition: Adds strategy to registry, overwriting if type exists
|
|
68
|
+
this.strategies.set(type, strategy);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
getStrategy(type: string): OrderProcessingStrategy {
|
|
72
|
+
// Assume: type is a string
|
|
73
|
+
// Condition: Returns strategy if found, else returns default
|
|
74
|
+
// Edge case: type might be undefined - handled by returning default
|
|
75
|
+
return this.strategies.get(type) || new DefaultOrderStrategy();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
hasStrategy(type: string): boolean {
|
|
79
|
+
return this.strategies.has(type);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// ===== Context (Order Processor) =====
|
|
84
|
+
|
|
85
|
+
class OrderProcessor {
|
|
86
|
+
private factory: OrderProcessingStrategyFactory;
|
|
87
|
+
|
|
88
|
+
constructor(factory?: OrderProcessingStrategyFactory) {
|
|
89
|
+
// Assume: factory is provided or create default
|
|
90
|
+
// Condition: Uses dependency injection for testability
|
|
91
|
+
this.factory = factory || new OrderProcessingStrategyFactory();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
processOrder(order: Order): Order {
|
|
95
|
+
// Assume: order is a valid object with at least a 'type' property
|
|
96
|
+
// Condition: Processes order using appropriate strategy
|
|
97
|
+
// Edge case: order is undefined/null - we handle defensively
|
|
98
|
+
if (!order || typeof order !== 'object') {
|
|
99
|
+
throw new Error('Invalid order: must be an object');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const strategy = this.factory.getStrategy(order.type);
|
|
103
|
+
return strategy.process(order);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ============================================
|
|
108
|
+
// Usage Examples
|
|
109
|
+
// ============================================
|
|
110
|
+
|
|
111
|
+
// Create processor instance
|
|
112
|
+
const processor = new OrderProcessor();
|
|
113
|
+
|
|
114
|
+
// Process different order types
|
|
115
|
+
const digitalOrder = processor.processOrder({
|
|
116
|
+
type: 'digital',
|
|
117
|
+
id: 'DIG-001',
|
|
118
|
+
items: ['software license']
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
const physicalOrder = processor.processOrder({
|
|
122
|
+
type: 'physical',
|
|
123
|
+
id: 'PHY-001',
|
|
124
|
+
items: ['laptop'],
|
|
125
|
+
shippingAddress: '123 Main St'
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const subscriptionOrder = processor.processOrder({
|
|
129
|
+
type: 'subscription',
|
|
130
|
+
id: 'SUB-001',
|
|
131
|
+
items: ['monthly service'],
|
|
132
|
+
billingCycle: 'monthly'
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// Processing unknown order type (falls back to DefaultOrderStrategy)
|
|
136
|
+
const unknownOrder = processor.processOrder({
|
|
137
|
+
type: 'unknown',
|
|
138
|
+
id: 'UNK-001',
|
|
139
|
+
items: ['something']
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
console.log('Digital Order:', digitalOrder); // { type: 'digital', status: 'delivered', ... }
|
|
143
|
+
console.log('Physical Order:', physicalOrder); // { type: 'physical', status: 'shipped', ... }
|
|
144
|
+
console.log('Subscription Order:', subscriptionOrder); // { type: 'subscription', status: 'active', ... }
|
|
145
|
+
console.log('Unknown Order:', unknownOrder); // { type: 'unknown', status: 'pending', ... }
|
|
146
|
+
|
|
147
|
+
// ============================================
|
|
148
|
+
// Extensibility Example: Adding a New Order Type
|
|
149
|
+
// ============================================
|
|
150
|
+
|
|
151
|
+
// Define a new strategy for a new order type
|
|
152
|
+
class ServiceOrderStrategy implements OrderProcessingStrategy {
|
|
153
|
+
process(order: Order): Order {
|
|
154
|
+
return { ...order, status: 'scheduled' };
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Register the new strategy with the factory
|
|
159
|
+
const customFactory = new OrderProcessingStrategyFactory();
|
|
160
|
+
customFactory.registerStrategy('service', new ServiceOrderStrategy());
|
|
161
|
+
|
|
162
|
+
// Create processor with custom factory
|
|
163
|
+
const customProcessor = new OrderProcessor(customFactory);
|
|
164
|
+
|
|
165
|
+
// Now we can process service orders
|
|
166
|
+
const serviceOrder = customProcessor.processOrder({
|
|
167
|
+
type: 'service',
|
|
168
|
+
id: 'SRV-001',
|
|
169
|
+
items: ['consultation']
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
console.log('Service Order:', serviceOrder); // { type: 'service', status: 'scheduled', ... }
|
|
173
|
+
|
|
174
|
+
// ============================================
|
|
175
|
+
// Benefits of Strategy Pattern:
|
|
176
|
+
// ============================================
|
|
177
|
+
/*
|
|
178
|
+
1. Open/Closed Principle: Easy to add new order types without modifying existing code
|
|
179
|
+
2. Single Responsibility: Each strategy handles one specific order type
|
|
180
|
+
3. Testability: Each strategy can be tested independently
|
|
181
|
+
4. Flexibility: Strategies can be swapped at runtime
|
|
182
|
+
5. Type Safety: TypeScript interfaces ensure contract compliance
|
|
183
|
+
6. Immutability: Returns new objects instead of mutating input
|
|
184
|
+
7. Extensibility: New strategies can be registered via factory
|
|
185
|
+
8. Encapsulation: Each strategy hides its implementation details
|
|
186
|
+
*/
|