chainlesschain 0.51.0 → 0.66.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/package.json +1 -1
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AppLayout-Rvi759IS.js → AppLayout-6SPt_8Y_.js} +1 -1
- package/src/assets/web-panel/assets/{Dashboard-DBhFxXYQ.js → Dashboard-Br7kCwKJ.js} +2 -2
- package/src/assets/web-panel/assets/Dashboard-CKeMmCoT.css +1 -0
- package/src/assets/web-panel/assets/{index-uL0cZ8N_.js → index-tN-8TosE.js} +2 -2
- package/src/assets/web-panel/index.html +2 -2
- package/src/commands/agent-network.js +785 -0
- package/src/commands/automation.js +654 -0
- package/src/commands/dao.js +565 -0
- package/src/commands/did-v2.js +620 -0
- package/src/commands/economy.js +578 -0
- package/src/commands/evolution.js +391 -0
- package/src/commands/hmemory.js +442 -0
- package/src/commands/perf.js +433 -0
- package/src/commands/pipeline.js +449 -0
- package/src/commands/plugin-ecosystem.js +517 -0
- package/src/commands/sandbox.js +401 -0
- package/src/commands/social.js +311 -0
- package/src/commands/sso.js +798 -0
- package/src/commands/workflow.js +320 -0
- package/src/commands/zkp.js +227 -1
- package/src/index.js +21 -0
- package/src/lib/agent-economy.js +479 -0
- package/src/lib/agent-network.js +1121 -0
- package/src/lib/automation-engine.js +948 -0
- package/src/lib/dao-governance.js +569 -0
- package/src/lib/did-v2-manager.js +1127 -0
- package/src/lib/evolution-system.js +453 -0
- package/src/lib/hierarchical-memory.js +481 -0
- package/src/lib/perf-tuning.js +734 -0
- package/src/lib/pipeline-orchestrator.js +928 -0
- package/src/lib/plugin-ecosystem.js +1109 -0
- package/src/lib/sandbox-v2.js +306 -0
- package/src/lib/social-graph-analytics.js +707 -0
- package/src/lib/sso-manager.js +841 -0
- package/src/lib/workflow-engine.js +454 -1
- package/src/lib/zkp-engine.js +249 -20
- package/src/assets/web-panel/assets/Dashboard-BS-tzGNj.css +0 -1
|
@@ -17,6 +17,26 @@ import {
|
|
|
17
17
|
getCapabilities,
|
|
18
18
|
getModels,
|
|
19
19
|
exportModel,
|
|
20
|
+
// Phase 100 V2
|
|
21
|
+
CAPABILITY_DIMENSION,
|
|
22
|
+
DIAGNOSIS_SEVERITY,
|
|
23
|
+
REPAIR_STRATEGY,
|
|
24
|
+
GROWTH_MILESTONE,
|
|
25
|
+
assessCapabilityV2,
|
|
26
|
+
getCapabilityV2,
|
|
27
|
+
listCapabilitiesV2,
|
|
28
|
+
trainIncrementalV2,
|
|
29
|
+
listTrainingLogV2,
|
|
30
|
+
selfDiagnoseV2,
|
|
31
|
+
getDiagnosisV2,
|
|
32
|
+
listDiagnosesV2,
|
|
33
|
+
selfRepairV2,
|
|
34
|
+
predictBehaviorV2,
|
|
35
|
+
recordMilestone,
|
|
36
|
+
getGrowthLogV2,
|
|
37
|
+
configureEvolution,
|
|
38
|
+
getEvolutionConfig,
|
|
39
|
+
getEvolutionStatsV2,
|
|
20
40
|
} from "../lib/evolution-system.js";
|
|
21
41
|
|
|
22
42
|
export function registerEvolutionCommand(program) {
|
|
@@ -395,4 +415,375 @@ export function registerEvolutionCommand(program) {
|
|
|
395
415
|
process.exit(1);
|
|
396
416
|
}
|
|
397
417
|
});
|
|
418
|
+
|
|
419
|
+
// ═══════════════════════════════════════════════════════════
|
|
420
|
+
// Phase 100 — Self-Evolving AI V2 subcommands
|
|
421
|
+
// ═══════════════════════════════════════════════════════════
|
|
422
|
+
|
|
423
|
+
evolution
|
|
424
|
+
.command("dimensions")
|
|
425
|
+
.description("List CAPABILITY_DIMENSION enum")
|
|
426
|
+
.option("--json", "Output as JSON")
|
|
427
|
+
.action((options) => {
|
|
428
|
+
const list = Object.values(CAPABILITY_DIMENSION);
|
|
429
|
+
if (options.json) console.log(JSON.stringify(list, null, 2));
|
|
430
|
+
else list.forEach((d) => logger.log(d));
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
evolution
|
|
434
|
+
.command("severities")
|
|
435
|
+
.description("List DIAGNOSIS_SEVERITY enum")
|
|
436
|
+
.option("--json", "Output as JSON")
|
|
437
|
+
.action((options) => {
|
|
438
|
+
const list = Object.values(DIAGNOSIS_SEVERITY);
|
|
439
|
+
if (options.json) console.log(JSON.stringify(list, null, 2));
|
|
440
|
+
else list.forEach((d) => logger.log(d));
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
evolution
|
|
444
|
+
.command("strategies")
|
|
445
|
+
.description("List REPAIR_STRATEGY enum")
|
|
446
|
+
.option("--json", "Output as JSON")
|
|
447
|
+
.action((options) => {
|
|
448
|
+
const list = Object.values(REPAIR_STRATEGY);
|
|
449
|
+
if (options.json) console.log(JSON.stringify(list, null, 2));
|
|
450
|
+
else list.forEach((d) => logger.log(d));
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
evolution
|
|
454
|
+
.command("milestones")
|
|
455
|
+
.description("List GROWTH_MILESTONE enum")
|
|
456
|
+
.option("--json", "Output as JSON")
|
|
457
|
+
.action((options) => {
|
|
458
|
+
const list = Object.values(GROWTH_MILESTONE);
|
|
459
|
+
if (options.json) console.log(JSON.stringify(list, null, 2));
|
|
460
|
+
else list.forEach((d) => logger.log(d));
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
evolution
|
|
464
|
+
.command("assess-v2")
|
|
465
|
+
.description("Assess capability by canonical dimension")
|
|
466
|
+
.argument("<dimension>", "Dimension (use `evolution dimensions`)")
|
|
467
|
+
.argument("<score>", "Score 0..1")
|
|
468
|
+
.option("-m, --metadata <json>", "Metadata JSON", "{}")
|
|
469
|
+
.option("--json", "Output as JSON")
|
|
470
|
+
.action((dimension, score, options) => {
|
|
471
|
+
try {
|
|
472
|
+
const metadata = JSON.parse(options.metadata);
|
|
473
|
+
const r = assessCapabilityV2({
|
|
474
|
+
dimension,
|
|
475
|
+
score: parseFloat(score),
|
|
476
|
+
metadata,
|
|
477
|
+
});
|
|
478
|
+
if (options.json) console.log(JSON.stringify(r, null, 2));
|
|
479
|
+
else {
|
|
480
|
+
logger.log(chalk.bold(`Capability ${r.dimension}`));
|
|
481
|
+
logger.log(` Score: ${r.score}`);
|
|
482
|
+
logger.log(` Previous: ${r.previousScore}`);
|
|
483
|
+
logger.log(` Trend: ${r.trend}`);
|
|
484
|
+
logger.log(` Samples: ${r.sampleCount}`);
|
|
485
|
+
}
|
|
486
|
+
} catch (err) {
|
|
487
|
+
logger.error(`Failed: ${err.message}`);
|
|
488
|
+
process.exit(1);
|
|
489
|
+
}
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
evolution
|
|
493
|
+
.command("capabilities-v2")
|
|
494
|
+
.description("List V2 capabilities")
|
|
495
|
+
.option("--json", "Output as JSON")
|
|
496
|
+
.action((options) => {
|
|
497
|
+
const list = listCapabilitiesV2();
|
|
498
|
+
if (options.json) console.log(JSON.stringify(list, null, 2));
|
|
499
|
+
else {
|
|
500
|
+
if (list.length === 0) logger.log("(no capabilities)");
|
|
501
|
+
for (const c of list) {
|
|
502
|
+
logger.log(
|
|
503
|
+
`${c.dimension.padEnd(14)} score=${c.score.toFixed(3)} trend=${c.trend} samples=${c.sampleCount}`,
|
|
504
|
+
);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
evolution
|
|
510
|
+
.command("train-v2")
|
|
511
|
+
.description("Record V2 incremental training run")
|
|
512
|
+
.requiredOption(
|
|
513
|
+
"-s, --strategy <s>",
|
|
514
|
+
"replay|elastic-weight|knowledge-distill",
|
|
515
|
+
)
|
|
516
|
+
.requiredOption("--data-size <n>", "Data size")
|
|
517
|
+
.requiredOption("--loss-before <n>", "Loss before training")
|
|
518
|
+
.requiredOption("--loss-after <n>", "Loss after training")
|
|
519
|
+
.option("--duration-ms <n>", "Duration in ms", "0")
|
|
520
|
+
.option("--json", "Output as JSON")
|
|
521
|
+
.action((options) => {
|
|
522
|
+
try {
|
|
523
|
+
const r = trainIncrementalV2({
|
|
524
|
+
strategy: options.strategy,
|
|
525
|
+
dataSize: parseFloat(options.dataSize),
|
|
526
|
+
lossBefore: parseFloat(options.lossBefore),
|
|
527
|
+
lossAfter: parseFloat(options.lossAfter),
|
|
528
|
+
durationMs: parseFloat(options.durationMs),
|
|
529
|
+
});
|
|
530
|
+
if (options.json) console.log(JSON.stringify(r, null, 2));
|
|
531
|
+
else {
|
|
532
|
+
logger.log(chalk.bold(`Training ${r.id}`));
|
|
533
|
+
logger.log(` Strategy: ${r.strategy}`);
|
|
534
|
+
logger.log(` KnowledgeRetention ${r.knowledgeRetention.toFixed(4)}`);
|
|
535
|
+
logger.log(` Status: ${r.status}`);
|
|
536
|
+
}
|
|
537
|
+
} catch (err) {
|
|
538
|
+
logger.error(`Failed: ${err.message}`);
|
|
539
|
+
process.exit(1);
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
evolution
|
|
544
|
+
.command("training-log-v2")
|
|
545
|
+
.description("List V2 training runs")
|
|
546
|
+
.option("-s, --strategy <s>", "Filter by strategy")
|
|
547
|
+
.option("-l, --limit <n>", "Limit")
|
|
548
|
+
.option("--json", "Output as JSON")
|
|
549
|
+
.action((options) => {
|
|
550
|
+
const limit = options.limit ? parseInt(options.limit, 10) : undefined;
|
|
551
|
+
const list = listTrainingLogV2({ strategy: options.strategy, limit });
|
|
552
|
+
if (options.json) console.log(JSON.stringify(list, null, 2));
|
|
553
|
+
else {
|
|
554
|
+
if (list.length === 0) logger.log("(no training log)");
|
|
555
|
+
for (const t of list) {
|
|
556
|
+
logger.log(
|
|
557
|
+
`${new Date(t.createdAt).toISOString()} ${t.strategy.padEnd(20)} retention=${t.knowledgeRetention.toFixed(3)} status=${t.status}`,
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
evolution
|
|
564
|
+
.command("diagnose-v2")
|
|
565
|
+
.description("Run V2 self-diagnosis")
|
|
566
|
+
.option("--scope <s>", "Scope", "system")
|
|
567
|
+
.option("--depth <d>", "Depth", "shallow")
|
|
568
|
+
.option("--json", "Output as JSON")
|
|
569
|
+
.action((options) => {
|
|
570
|
+
const r = selfDiagnoseV2({ scope: options.scope, depth: options.depth });
|
|
571
|
+
if (options.json) console.log(JSON.stringify(r, null, 2));
|
|
572
|
+
else {
|
|
573
|
+
logger.log(chalk.bold(`Diagnosis ${r.id}`));
|
|
574
|
+
logger.log(` Severity: ${r.severity}`);
|
|
575
|
+
logger.log(` AnomaliesFound: ${r.anomaliesDetected}`);
|
|
576
|
+
if (r.rootCause) logger.log(` RootCause: ${r.rootCause}`);
|
|
577
|
+
if (r.repairSuggestion)
|
|
578
|
+
logger.log(` RepairSuggestion ${r.repairSuggestion}`);
|
|
579
|
+
}
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
evolution
|
|
583
|
+
.command("diagnoses-v2")
|
|
584
|
+
.description("List V2 diagnoses")
|
|
585
|
+
.option("-s, --severity <s>", "Filter by severity")
|
|
586
|
+
.option("--json", "Output as JSON")
|
|
587
|
+
.action((options) => {
|
|
588
|
+
const list = listDiagnosesV2({ severity: options.severity });
|
|
589
|
+
if (options.json) console.log(JSON.stringify(list, null, 2));
|
|
590
|
+
else {
|
|
591
|
+
if (list.length === 0) logger.log("(no diagnoses)");
|
|
592
|
+
for (const d of list) {
|
|
593
|
+
logger.log(
|
|
594
|
+
`${d.id} severity=${d.severity} anomalies=${d.anomaliesDetected} status=${d.repairStatus}`,
|
|
595
|
+
);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
evolution
|
|
601
|
+
.command("diagnosis-show")
|
|
602
|
+
.description("Show a specific diagnosis")
|
|
603
|
+
.argument("<id>", "Diagnosis ID")
|
|
604
|
+
.option("--json", "Output as JSON")
|
|
605
|
+
.action((id, options) => {
|
|
606
|
+
const d = getDiagnosisV2(id);
|
|
607
|
+
if (!d) {
|
|
608
|
+
logger.error(`Diagnosis not found: ${id}`);
|
|
609
|
+
process.exit(1);
|
|
610
|
+
}
|
|
611
|
+
if (options.json) console.log(JSON.stringify(d, null, 2));
|
|
612
|
+
else {
|
|
613
|
+
logger.log(chalk.bold(`Diagnosis ${d.id}`));
|
|
614
|
+
logger.log(` Scope: ${d.scope}`);
|
|
615
|
+
logger.log(` Severity: ${d.severity}`);
|
|
616
|
+
logger.log(` AnomaliesFound: ${d.anomaliesDetected}`);
|
|
617
|
+
logger.log(` RepairStatus: ${d.repairStatus}`);
|
|
618
|
+
}
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
evolution
|
|
622
|
+
.command("repair-v2")
|
|
623
|
+
.description("Apply V2 self-repair to a diagnosis")
|
|
624
|
+
.argument("<diagnosis-id>", "Diagnosis ID")
|
|
625
|
+
.requiredOption(
|
|
626
|
+
"-s, --strategy <s>",
|
|
627
|
+
"parameter_tune|model_rollback|cache_rebuild|full_reset",
|
|
628
|
+
)
|
|
629
|
+
.option("--json", "Output as JSON")
|
|
630
|
+
.action((diagnosisId, options) => {
|
|
631
|
+
try {
|
|
632
|
+
const r = selfRepairV2({ diagnosisId, strategy: options.strategy });
|
|
633
|
+
if (options.json) console.log(JSON.stringify(r, null, 2));
|
|
634
|
+
else {
|
|
635
|
+
logger.log(chalk.green(`Repaired ${r.diagnosisId}`));
|
|
636
|
+
logger.log(` Strategy: ${r.strategy}`);
|
|
637
|
+
for (const a of r.actions) logger.log(` - ${a}`);
|
|
638
|
+
}
|
|
639
|
+
} catch (err) {
|
|
640
|
+
logger.error(`Failed: ${err.message}`);
|
|
641
|
+
process.exit(1);
|
|
642
|
+
}
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
evolution
|
|
646
|
+
.command("predict-v2")
|
|
647
|
+
.description("V2 behavior prediction")
|
|
648
|
+
.option("--horizon-ms <n>", "Time horizon in ms")
|
|
649
|
+
.option("--json", "Output as JSON")
|
|
650
|
+
.action((options) => {
|
|
651
|
+
const horizon = options.horizonMs
|
|
652
|
+
? parseFloat(options.horizonMs)
|
|
653
|
+
: undefined;
|
|
654
|
+
const r = predictBehaviorV2({ timeHorizonMs: horizon });
|
|
655
|
+
if (options.json) console.log(JSON.stringify(r, null, 2));
|
|
656
|
+
else {
|
|
657
|
+
logger.log(chalk.bold("Prediction"));
|
|
658
|
+
logger.log(` Horizon: ${r.horizonMs}ms`);
|
|
659
|
+
logger.log(` Confidence: ${r.confidence}`);
|
|
660
|
+
for (const p of r.predictions) {
|
|
661
|
+
logger.log(` ${p.type.padEnd(24)} p=${p.probability}`);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
});
|
|
665
|
+
|
|
666
|
+
evolution
|
|
667
|
+
.command("record-milestone")
|
|
668
|
+
.description("Record a growth milestone explicitly")
|
|
669
|
+
.argument("<type>", "Milestone type (use `evolution milestones`)")
|
|
670
|
+
.requiredOption("-d, --description <s>", "Description")
|
|
671
|
+
.option("--capability-id <id>", "Capability ID")
|
|
672
|
+
.option("-m, --details <json>", "Details JSON", "{}")
|
|
673
|
+
.option("--json", "Output as JSON")
|
|
674
|
+
.action((type, options) => {
|
|
675
|
+
try {
|
|
676
|
+
const details = JSON.parse(options.details);
|
|
677
|
+
const r = recordMilestone({
|
|
678
|
+
type,
|
|
679
|
+
description: options.description,
|
|
680
|
+
capabilityId: options.capabilityId || null,
|
|
681
|
+
details,
|
|
682
|
+
});
|
|
683
|
+
if (options.json) console.log(JSON.stringify(r, null, 2));
|
|
684
|
+
else {
|
|
685
|
+
logger.log(chalk.green(`Milestone ${r.id}`));
|
|
686
|
+
logger.log(` Type: ${r.type}`);
|
|
687
|
+
logger.log(` Description: ${r.description}`);
|
|
688
|
+
}
|
|
689
|
+
} catch (err) {
|
|
690
|
+
logger.error(`Failed: ${err.message}`);
|
|
691
|
+
process.exit(1);
|
|
692
|
+
}
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
evolution
|
|
696
|
+
.command("growth-v2")
|
|
697
|
+
.description("List V2 growth milestones")
|
|
698
|
+
.option("-t, --type <t>", "Filter by milestone type")
|
|
699
|
+
.option("--from <ms>", "From timestamp (ms)")
|
|
700
|
+
.option("--to <ms>", "To timestamp (ms)")
|
|
701
|
+
.option("-l, --limit <n>", "Limit")
|
|
702
|
+
.option("--json", "Output as JSON")
|
|
703
|
+
.action((options) => {
|
|
704
|
+
const period = {};
|
|
705
|
+
if (options.from) period.fromMs = parseFloat(options.from);
|
|
706
|
+
if (options.to) period.toMs = parseFloat(options.to);
|
|
707
|
+
const list = getGrowthLogV2({
|
|
708
|
+
milestoneType: options.type,
|
|
709
|
+
period: Object.keys(period).length ? period : undefined,
|
|
710
|
+
limit: options.limit ? parseInt(options.limit, 10) : undefined,
|
|
711
|
+
});
|
|
712
|
+
if (options.json) console.log(JSON.stringify(list, null, 2));
|
|
713
|
+
else {
|
|
714
|
+
if (list.length === 0) logger.log("(no milestones)");
|
|
715
|
+
for (const m of list) {
|
|
716
|
+
logger.log(
|
|
717
|
+
`${new Date(m.timestamp).toISOString()} ${m.type.padEnd(24)} ${m.description}`,
|
|
718
|
+
);
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
});
|
|
722
|
+
|
|
723
|
+
evolution
|
|
724
|
+
.command("configure")
|
|
725
|
+
.description("Update evolution config")
|
|
726
|
+
.requiredOption("-k, --key <k>", "Config key")
|
|
727
|
+
.requiredOption("-v, --value <v>", "Config value")
|
|
728
|
+
.option("--json", "Output as JSON")
|
|
729
|
+
.action((options) => {
|
|
730
|
+
try {
|
|
731
|
+
let value = options.value;
|
|
732
|
+
// Coerce booleans and numbers for typed keys
|
|
733
|
+
if (value === "true") value = true;
|
|
734
|
+
else if (value === "false") value = false;
|
|
735
|
+
else if (!Number.isNaN(Number(value)) && value !== "") {
|
|
736
|
+
const asNum = Number(value);
|
|
737
|
+
if (Number.isFinite(asNum) && String(asNum) === value) value = asNum;
|
|
738
|
+
}
|
|
739
|
+
const c = configureEvolution({ key: options.key, value });
|
|
740
|
+
if (options.json) console.log(JSON.stringify(c, null, 2));
|
|
741
|
+
else logger.log(chalk.green("Updated"));
|
|
742
|
+
} catch (err) {
|
|
743
|
+
logger.error(`Failed: ${err.message}`);
|
|
744
|
+
process.exit(1);
|
|
745
|
+
}
|
|
746
|
+
});
|
|
747
|
+
|
|
748
|
+
evolution
|
|
749
|
+
.command("config")
|
|
750
|
+
.description("Show evolution config")
|
|
751
|
+
.option("--json", "Output as JSON")
|
|
752
|
+
.action((options) => {
|
|
753
|
+
const c = getEvolutionConfig();
|
|
754
|
+
if (options.json) console.log(JSON.stringify(c, null, 2));
|
|
755
|
+
else {
|
|
756
|
+
for (const [k, v] of Object.entries(c)) {
|
|
757
|
+
logger.log(`${k.padEnd(30)} ${Array.isArray(v) ? v.join(",") : v}`);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
});
|
|
761
|
+
|
|
762
|
+
evolution
|
|
763
|
+
.command("stats-v2")
|
|
764
|
+
.description("V2 evolution stats")
|
|
765
|
+
.option("--json", "Output as JSON")
|
|
766
|
+
.action((options) => {
|
|
767
|
+
const s = getEvolutionStatsV2();
|
|
768
|
+
if (options.json) console.log(JSON.stringify(s, null, 2));
|
|
769
|
+
else {
|
|
770
|
+
logger.log(chalk.bold("Evolution Stats V2"));
|
|
771
|
+
logger.log(` Capabilities: ${s.capabilityCount}`);
|
|
772
|
+
logger.log(` TrainingRuns: ${s.trainingRuns}`);
|
|
773
|
+
logger.log(
|
|
774
|
+
` Diagnoses: ${s.diagnoses.total} (${Object.entries(
|
|
775
|
+
s.diagnoses.bySeverity,
|
|
776
|
+
)
|
|
777
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
778
|
+
.join(", ")})`,
|
|
779
|
+
);
|
|
780
|
+
logger.log(
|
|
781
|
+
` Milestones: ${s.milestones.total} (${Object.entries(
|
|
782
|
+
s.milestones.byType,
|
|
783
|
+
)
|
|
784
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
785
|
+
.join(", ")})`,
|
|
786
|
+
);
|
|
787
|
+
}
|
|
788
|
+
});
|
|
398
789
|
}
|