@oneharness/sdk 0.16.2 → 0.17.1
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/generated/capabilities.d.ts +6 -1
- package/dist/generated/capabilities.js +7 -1
- package/dist/generated/schemas.json +97 -1
- package/dist/generated/sync-options.d.ts +6 -0
- package/dist/generated/sync-report.d.ts +59 -0
- package/dist/generated/zod.d.ts +4 -1
- package/dist/generated/zod.js +15 -0
- package/package.json +2 -2
|
@@ -632,7 +632,7 @@ export declare const CAPABILITIES: {
|
|
|
632
632
|
readonly output: "sync_report";
|
|
633
633
|
readonly stdout: "json";
|
|
634
634
|
readonly stdin: false;
|
|
635
|
-
readonly rust: "oneharness_core::io::sync::
|
|
635
|
+
readonly rust: "oneharness_core::io::sync::sync_with";
|
|
636
636
|
readonly always: readonly ["--compact", "--format", "json"];
|
|
637
637
|
readonly bindings: readonly [{
|
|
638
638
|
readonly option: "cwd";
|
|
@@ -649,6 +649,11 @@ export declare const CAPABILITIES: {
|
|
|
649
649
|
readonly flag: "--check";
|
|
650
650
|
readonly kind: "switch";
|
|
651
651
|
readonly unless: null;
|
|
652
|
+
}, {
|
|
653
|
+
readonly option: "exact";
|
|
654
|
+
readonly flag: "--exact";
|
|
655
|
+
readonly kind: "switch";
|
|
656
|
+
readonly unless: null;
|
|
652
657
|
}, {
|
|
653
658
|
readonly option: "global";
|
|
654
659
|
readonly flag: "--global";
|
|
@@ -705,7 +705,7 @@ export const CAPABILITIES = {
|
|
|
705
705
|
output: "sync_report",
|
|
706
706
|
stdout: "json",
|
|
707
707
|
stdin: false,
|
|
708
|
-
rust: "oneharness_core::io::sync::
|
|
708
|
+
rust: "oneharness_core::io::sync::sync_with",
|
|
709
709
|
always: ["--compact", "--format", "json"],
|
|
710
710
|
bindings: [
|
|
711
711
|
{
|
|
@@ -726,6 +726,12 @@ export const CAPABILITIES = {
|
|
|
726
726
|
kind: "switch",
|
|
727
727
|
unless: null,
|
|
728
728
|
},
|
|
729
|
+
{
|
|
730
|
+
option: "exact",
|
|
731
|
+
flag: "--exact",
|
|
732
|
+
kind: "switch",
|
|
733
|
+
unless: null,
|
|
734
|
+
},
|
|
729
735
|
{
|
|
730
736
|
option: "global",
|
|
731
737
|
flag: "--global",
|
|
@@ -732,7 +732,7 @@
|
|
|
732
732
|
"output": "sync_report",
|
|
733
733
|
"stdout": "json",
|
|
734
734
|
"stdin": false,
|
|
735
|
-
"rust": "oneharness_core::io::sync::
|
|
735
|
+
"rust": "oneharness_core::io::sync::sync_with",
|
|
736
736
|
"always": [
|
|
737
737
|
"--compact",
|
|
738
738
|
"--format",
|
|
@@ -757,6 +757,12 @@
|
|
|
757
757
|
"kind": "switch",
|
|
758
758
|
"unless": null
|
|
759
759
|
},
|
|
760
|
+
{
|
|
761
|
+
"option": "exact",
|
|
762
|
+
"flag": "--exact",
|
|
763
|
+
"kind": "switch",
|
|
764
|
+
"unless": null
|
|
765
|
+
},
|
|
760
766
|
{
|
|
761
767
|
"option": "global",
|
|
762
768
|
"flag": "--global",
|
|
@@ -16592,6 +16598,10 @@
|
|
|
16592
16598
|
"cwd": {
|
|
16593
16599
|
"type": "string"
|
|
16594
16600
|
},
|
|
16601
|
+
"exact": {
|
|
16602
|
+
"description": "Hold each harness's allow/deny list to exactly the configured one:\nstale and hand-added entries are removed, every other key is merged as\nusual, and the report names each added and removed entry.",
|
|
16603
|
+
"type": "boolean"
|
|
16604
|
+
},
|
|
16595
16605
|
"global": {
|
|
16596
16606
|
"type": "boolean"
|
|
16597
16607
|
},
|
|
@@ -16624,6 +16634,10 @@
|
|
|
16624
16634
|
"type": "string"
|
|
16625
16635
|
}
|
|
16626
16636
|
},
|
|
16637
|
+
"exact": {
|
|
16638
|
+
"description": "True under `--exact`: each permission list was held to exactly the\nsource's, and each result names the entries that added and removed.\nOmitted when false.",
|
|
16639
|
+
"type": "boolean"
|
|
16640
|
+
},
|
|
16627
16641
|
"results": {
|
|
16628
16642
|
"type": "array",
|
|
16629
16643
|
"items": {
|
|
@@ -16677,10 +16691,56 @@
|
|
|
16677
16691
|
"status"
|
|
16678
16692
|
]
|
|
16679
16693
|
},
|
|
16694
|
+
"RuleChange": {
|
|
16695
|
+
"description": "One entry an `--exact` sync added to, or removed from, a harness's rules.",
|
|
16696
|
+
"type": "object",
|
|
16697
|
+
"properties": {
|
|
16698
|
+
"list": {
|
|
16699
|
+
"description": "The list the entry belongs to; absent for a statement in a Codex rules\nfile that no unified list renders (a hand-written `decision=\"prompt\"`).",
|
|
16700
|
+
"anyOf": [
|
|
16701
|
+
{
|
|
16702
|
+
"$ref": "#/$defs/RuleList"
|
|
16703
|
+
},
|
|
16704
|
+
{
|
|
16705
|
+
"type": "null"
|
|
16706
|
+
}
|
|
16707
|
+
]
|
|
16708
|
+
},
|
|
16709
|
+
"rule": {
|
|
16710
|
+
"description": "The entry as it appears in the harness's file: the rule string for a\nJSON list, the whole `prefix_rule(...)` statement for a Codex rules file.",
|
|
16711
|
+
"type": "string"
|
|
16712
|
+
}
|
|
16713
|
+
},
|
|
16714
|
+
"required": [
|
|
16715
|
+
"rule"
|
|
16716
|
+
]
|
|
16717
|
+
},
|
|
16718
|
+
"RuleList": {
|
|
16719
|
+
"description": "Which unified rule list an entry belongs to.",
|
|
16720
|
+
"oneOf": [
|
|
16721
|
+
{
|
|
16722
|
+
"description": "`allowed_tools`.",
|
|
16723
|
+
"type": "string",
|
|
16724
|
+
"const": "allowed_tools"
|
|
16725
|
+
},
|
|
16726
|
+
{
|
|
16727
|
+
"description": "`denied_tools`.",
|
|
16728
|
+
"type": "string",
|
|
16729
|
+
"const": "denied_tools"
|
|
16730
|
+
}
|
|
16731
|
+
]
|
|
16732
|
+
},
|
|
16680
16733
|
"SyncResult": {
|
|
16681
16734
|
"description": "What one harness's sync did (or would do).",
|
|
16682
16735
|
"type": "object",
|
|
16683
16736
|
"properties": {
|
|
16737
|
+
"added_rules": {
|
|
16738
|
+
"description": "Under `--exact`: each entry the sync added (or would add) to this\nharness's permission lists. Omitted when empty.",
|
|
16739
|
+
"type": "array",
|
|
16740
|
+
"items": {
|
|
16741
|
+
"$ref": "#/$defs/RuleChange"
|
|
16742
|
+
}
|
|
16743
|
+
},
|
|
16684
16744
|
"file": {
|
|
16685
16745
|
"description": "The permission/settings config file written (or that would be written);\n`null` when nothing of that kind is configured for this harness.",
|
|
16686
16746
|
"type": [
|
|
@@ -16698,6 +16758,13 @@
|
|
|
16698
16758
|
"$ref": "#/$defs/HookFileResult"
|
|
16699
16759
|
}
|
|
16700
16760
|
},
|
|
16761
|
+
"removed_rules": {
|
|
16762
|
+
"description": "Under `--exact`: each entry the sync removed (or would remove) from\nthis harness's permission lists — stale and hand-added alike. Omitted\nwhen empty.",
|
|
16763
|
+
"type": "array",
|
|
16764
|
+
"items": {
|
|
16765
|
+
"$ref": "#/$defs/RuleChange"
|
|
16766
|
+
}
|
|
16767
|
+
},
|
|
16701
16768
|
"status": {
|
|
16702
16769
|
"$ref": "#/$defs/SyncStatus"
|
|
16703
16770
|
},
|
|
@@ -16707,6 +16774,13 @@
|
|
|
16707
16774
|
"items": {
|
|
16708
16775
|
"type": "string"
|
|
16709
16776
|
}
|
|
16777
|
+
},
|
|
16778
|
+
"unmapped_rules": {
|
|
16779
|
+
"description": "Individual configured rules this harness cannot express (a Codex\nexecpolicy pattern has no wildcards and no exact-length form), each left\nout of the file with the reason — never widened, and never silently\ndropped. Omitted when empty.",
|
|
16780
|
+
"type": "array",
|
|
16781
|
+
"items": {
|
|
16782
|
+
"$ref": "#/$defs/UnmappedRule"
|
|
16783
|
+
}
|
|
16710
16784
|
}
|
|
16711
16785
|
},
|
|
16712
16786
|
"required": [
|
|
@@ -16741,6 +16815,28 @@
|
|
|
16741
16815
|
"const": "skipped"
|
|
16742
16816
|
}
|
|
16743
16817
|
]
|
|
16818
|
+
},
|
|
16819
|
+
"UnmappedRule": {
|
|
16820
|
+
"description": "A configured rule the harness cannot express, left out of what was written.",
|
|
16821
|
+
"type": "object",
|
|
16822
|
+
"properties": {
|
|
16823
|
+
"list": {
|
|
16824
|
+
"$ref": "#/$defs/RuleList"
|
|
16825
|
+
},
|
|
16826
|
+
"reason": {
|
|
16827
|
+
"description": "Why the harness cannot express it.",
|
|
16828
|
+
"type": "string"
|
|
16829
|
+
},
|
|
16830
|
+
"rule": {
|
|
16831
|
+
"description": "The rule exactly as configured.",
|
|
16832
|
+
"type": "string"
|
|
16833
|
+
}
|
|
16834
|
+
},
|
|
16835
|
+
"required": [
|
|
16836
|
+
"list",
|
|
16837
|
+
"rule",
|
|
16838
|
+
"reason"
|
|
16839
|
+
]
|
|
16744
16840
|
}
|
|
16745
16841
|
}
|
|
16746
16842
|
},
|
|
@@ -8,6 +8,12 @@ export interface SyncOptions {
|
|
|
8
8
|
check?: boolean | undefined;
|
|
9
9
|
config?: string | undefined;
|
|
10
10
|
cwd?: string | undefined;
|
|
11
|
+
/**
|
|
12
|
+
* Hold each harness's allow/deny list to exactly the configured one:
|
|
13
|
+
* stale and hand-added entries are removed, every other key is merged as
|
|
14
|
+
* usual, and the report names each added and removed entry.
|
|
15
|
+
*/
|
|
16
|
+
exact?: boolean | undefined;
|
|
11
17
|
global?: boolean | undefined;
|
|
12
18
|
harnesses?: string[] | undefined;
|
|
13
19
|
noConfig?: boolean | undefined;
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which unified rule list an entry belongs to.
|
|
3
|
+
*/
|
|
4
|
+
export type RuleList = "allowed_tools" | "denied_tools";
|
|
1
5
|
/**
|
|
2
6
|
* What applying a fragment did (or, under `check`, would do) to one file.
|
|
3
7
|
*
|
|
@@ -26,6 +30,12 @@ export interface SyncReport {
|
|
|
26
30
|
* The oneharness config files the synced settings came from.
|
|
27
31
|
*/
|
|
28
32
|
config_files: string[];
|
|
33
|
+
/**
|
|
34
|
+
* True under `--exact`: each permission list was held to exactly the
|
|
35
|
+
* source's, and each result names the entries that added and removed.
|
|
36
|
+
* Omitted when false.
|
|
37
|
+
*/
|
|
38
|
+
exact?: boolean | undefined;
|
|
29
39
|
results: SyncResult[];
|
|
30
40
|
schema_version: string;
|
|
31
41
|
[k: string]: unknown;
|
|
@@ -34,6 +44,11 @@ export interface SyncReport {
|
|
|
34
44
|
* What one harness's sync did (or would do).
|
|
35
45
|
*/
|
|
36
46
|
export interface SyncResult {
|
|
47
|
+
/**
|
|
48
|
+
* Under `--exact`: each entry the sync added (or would add) to this
|
|
49
|
+
* harness's permission lists. Omitted when empty.
|
|
50
|
+
*/
|
|
51
|
+
added_rules?: RuleChange[] | undefined;
|
|
37
52
|
/**
|
|
38
53
|
* The permission/settings config file written (or that would be written);
|
|
39
54
|
* `null` when nothing of that kind is configured for this harness.
|
|
@@ -45,6 +60,12 @@ export interface SyncResult {
|
|
|
45
60
|
* writes two). Empty when no `[[hooks]]` entry targets it.
|
|
46
61
|
*/
|
|
47
62
|
hooks: HookFileResult[];
|
|
63
|
+
/**
|
|
64
|
+
* Under `--exact`: each entry the sync removed (or would remove) from
|
|
65
|
+
* this harness's permission lists — stale and hand-added alike. Omitted
|
|
66
|
+
* when empty.
|
|
67
|
+
*/
|
|
68
|
+
removed_rules?: RuleChange[] | undefined;
|
|
48
69
|
status: SyncStatus;
|
|
49
70
|
/**
|
|
50
71
|
* Top-level settings that have no mapping for this harness (e.g. a
|
|
@@ -52,6 +73,29 @@ export interface SyncResult {
|
|
|
52
73
|
* — visible here and warned on stderr, never silently dropped.
|
|
53
74
|
*/
|
|
54
75
|
unmapped: string[];
|
|
76
|
+
/**
|
|
77
|
+
* Individual configured rules this harness cannot express (a Codex
|
|
78
|
+
* execpolicy pattern has no wildcards and no exact-length form), each left
|
|
79
|
+
* out of the file with the reason — never widened, and never silently
|
|
80
|
+
* dropped. Omitted when empty.
|
|
81
|
+
*/
|
|
82
|
+
unmapped_rules?: UnmappedRule[] | undefined;
|
|
83
|
+
[k: string]: unknown;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* One entry an `--exact` sync added to, or removed from, a harness's rules.
|
|
87
|
+
*/
|
|
88
|
+
export interface RuleChange {
|
|
89
|
+
/**
|
|
90
|
+
* The list the entry belongs to; absent for a statement in a Codex rules
|
|
91
|
+
* file that no unified list renders (a hand-written `decision="prompt"`).
|
|
92
|
+
*/
|
|
93
|
+
list?: RuleList | null | undefined;
|
|
94
|
+
/**
|
|
95
|
+
* The entry as it appears in the harness's file: the rule string for a
|
|
96
|
+
* JSON list, the whole `prefix_rule(...)` statement for a Codex rules file.
|
|
97
|
+
*/
|
|
98
|
+
rule: string;
|
|
55
99
|
[k: string]: unknown;
|
|
56
100
|
}
|
|
57
101
|
/**
|
|
@@ -62,3 +106,18 @@ export interface HookFileResult {
|
|
|
62
106
|
status: FileStatus;
|
|
63
107
|
[k: string]: unknown;
|
|
64
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* A configured rule the harness cannot express, left out of what was written.
|
|
111
|
+
*/
|
|
112
|
+
export interface UnmappedRule {
|
|
113
|
+
list: RuleList;
|
|
114
|
+
/**
|
|
115
|
+
* Why the harness cannot express it.
|
|
116
|
+
*/
|
|
117
|
+
reason: string;
|
|
118
|
+
/**
|
|
119
|
+
* The rule exactly as configured.
|
|
120
|
+
*/
|
|
121
|
+
rule: string;
|
|
122
|
+
[k: string]: unknown;
|
|
123
|
+
}
|
package/dist/generated/zod.d.ts
CHANGED
|
@@ -27,7 +27,7 @@ import type { HistoryLabels, OutputFormat, PermissionMode, RunMode, RunOptions }
|
|
|
27
27
|
import type { HarnessInfo, ListReport, ModeInfo, VariantInfo } from "./registry.js";
|
|
28
28
|
import type { RunStreamEnvelope } from "./run-stream-envelope.js";
|
|
29
29
|
import type { SyncOptions } from "./sync-options.js";
|
|
30
|
-
import type { FileStatus, HookFileResult, SyncReport, SyncResult, SyncStatus } from "./sync-report.js";
|
|
30
|
+
import type { FileStatus, HookFileResult, RuleChange, RuleList, SyncReport, SyncResult, SyncStatus, UnmappedRule } from "./sync-report.js";
|
|
31
31
|
import type { UsageOptions } from "./usage-options.js";
|
|
32
32
|
import type { AuthMode, QuotaAmount, QuotaCounters, QuotaUnit, UnavailableReason, UnknownReason, UsageAvailability, UsageIdentity, UsageReport, UsageWindow, WindowUsage, Windows } from "./usage-report.js";
|
|
33
33
|
export type AbsolutePath = ControlReport["socket"];
|
|
@@ -109,6 +109,8 @@ export declare const PermissionModeSchema: z.ZodType<PermissionMode>;
|
|
|
109
109
|
export declare const QuotaAmountSchema: z.ZodType<QuotaAmount>;
|
|
110
110
|
export declare const QuotaCountersSchema: z.ZodType<QuotaCounters>;
|
|
111
111
|
export declare const QuotaUnitSchema: z.ZodType<QuotaUnit>;
|
|
112
|
+
export declare const RuleChangeSchema: z.ZodType<RuleChange>;
|
|
113
|
+
export declare const RuleListSchema: z.ZodType<RuleList>;
|
|
112
114
|
export declare const RunModeSchema: z.ZodType<RunMode>;
|
|
113
115
|
export declare const RunOptionsSchema: z.ZodType<RunOptions>;
|
|
114
116
|
export declare const RunReportSchema: z.ZodType<RunReport>;
|
|
@@ -126,6 +128,7 @@ export declare const TimingSourceSchema: z.ZodType<TimingSource>;
|
|
|
126
128
|
export declare const ToolCallStatusSchema: z.ZodType<ToolCallStatus>;
|
|
127
129
|
export declare const UnavailableReasonSchema: z.ZodType<UnavailableReason>;
|
|
128
130
|
export declare const UnknownReasonSchema: z.ZodType<UnknownReason>;
|
|
131
|
+
export declare const UnmappedRuleSchema: z.ZodType<UnmappedRule>;
|
|
129
132
|
export declare const UsageSchema: z.ZodType<Usage>;
|
|
130
133
|
export declare const UsageAvailabilitySchema: z.ZodType<UsageAvailability>;
|
|
131
134
|
export declare const UsageIdentitySchema: z.ZodType<UsageIdentity>;
|
package/dist/generated/zod.js
CHANGED
|
@@ -2337,6 +2337,11 @@ export const QuotaCountersSchema = z.looseObject({
|
|
|
2337
2337
|
used: z.lazy(() => QuotaAmountSchema).refine((value) => value !== undefined, { message: "Required" }),
|
|
2338
2338
|
});
|
|
2339
2339
|
export const QuotaUnitSchema = z.union([z.literal("ai_credits"), z.literal("unspecified")]);
|
|
2340
|
+
export const RuleChangeSchema = z.looseObject({
|
|
2341
|
+
list: z.union([z.lazy(() => RuleListSchema), z.null()]).optional(),
|
|
2342
|
+
rule: z.string().refine((value) => value !== undefined, { message: "Required" }),
|
|
2343
|
+
});
|
|
2344
|
+
export const RuleListSchema = z.union([z.literal("allowed_tools"), z.literal("denied_tools")]);
|
|
2340
2345
|
export const RunModeSchema = z.union([z.literal("parallel"), z.literal("fallback")]);
|
|
2341
2346
|
export const RunOptionsSchema = z.strictObject({
|
|
2342
2347
|
all: z.boolean().optional(),
|
|
@@ -2492,6 +2497,7 @@ export const SyncOptionsSchema = z.strictObject({
|
|
|
2492
2497
|
check: z.boolean().optional(),
|
|
2493
2498
|
config: z.string().optional(),
|
|
2494
2499
|
cwd: z.string().optional(),
|
|
2500
|
+
exact: z.boolean().optional(),
|
|
2495
2501
|
global: z.boolean().optional(),
|
|
2496
2502
|
harnesses: z.array(z.string()).optional(),
|
|
2497
2503
|
noConfig: z.boolean().optional(),
|
|
@@ -2499,15 +2505,19 @@ export const SyncOptionsSchema = z.strictObject({
|
|
|
2499
2505
|
export const SyncReportSchema = z.looseObject({
|
|
2500
2506
|
check: z.boolean().refine((value) => value !== undefined, { message: "Required" }),
|
|
2501
2507
|
config_files: z.array(z.string()).refine((value) => value !== undefined, { message: "Required" }),
|
|
2508
|
+
exact: z.boolean().optional(),
|
|
2502
2509
|
results: z.array(z.lazy(() => SyncResultSchema)).refine((value) => value !== undefined, { message: "Required" }),
|
|
2503
2510
|
schema_version: z.string().refine((value) => value !== undefined, { message: "Required" }),
|
|
2504
2511
|
});
|
|
2505
2512
|
export const SyncResultSchema = z.looseObject({
|
|
2513
|
+
added_rules: z.array(z.lazy(() => RuleChangeSchema)).optional(),
|
|
2506
2514
|
file: z.union([z.string(), z.null()]).refine((value) => value !== undefined, { message: "Required" }),
|
|
2507
2515
|
harness: z.string().refine((value) => value !== undefined, { message: "Required" }),
|
|
2508
2516
|
hooks: z.array(z.lazy(() => HookFileResultSchema)).refine((value) => value !== undefined, { message: "Required" }),
|
|
2517
|
+
removed_rules: z.array(z.lazy(() => RuleChangeSchema)).optional(),
|
|
2509
2518
|
status: z.lazy(() => SyncStatusSchema).refine((value) => value !== undefined, { message: "Required" }),
|
|
2510
2519
|
unmapped: z.array(z.string()).refine((value) => value !== undefined, { message: "Required" }),
|
|
2520
|
+
unmapped_rules: z.array(z.lazy(() => UnmappedRuleSchema)).optional(),
|
|
2511
2521
|
});
|
|
2512
2522
|
export const SyncStatusSchema = z.union([
|
|
2513
2523
|
z.literal("created"),
|
|
@@ -2545,6 +2555,11 @@ export const UnknownReasonSchema = z.union([
|
|
|
2545
2555
|
kind: z.literal("binary_missing").refine((value) => value !== undefined, { message: "Required" }),
|
|
2546
2556
|
}),
|
|
2547
2557
|
]);
|
|
2558
|
+
export const UnmappedRuleSchema = z.looseObject({
|
|
2559
|
+
list: z.lazy(() => RuleListSchema).refine((value) => value !== undefined, { message: "Required" }),
|
|
2560
|
+
reason: z.string().refine((value) => value !== undefined, { message: "Required" }),
|
|
2561
|
+
rule: z.string().refine((value) => value !== undefined, { message: "Required" }),
|
|
2562
|
+
});
|
|
2548
2563
|
export const UsageSchema = z.looseObject({
|
|
2549
2564
|
cache_read_tokens: z
|
|
2550
2565
|
.union([z.int().gte(0), z.null()])
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oneharness/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.1",
|
|
4
4
|
"description": "Typed Node.js SDK for oneharness",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"node": ">=20"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"oneharness-cli": "0.
|
|
22
|
+
"oneharness-cli": "0.17.1",
|
|
23
23
|
"zod": "^4.4.3"
|
|
24
24
|
}
|
|
25
25
|
}
|