pi-harness-delegate 0.1.0 → 0.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.
@@ -6,16 +6,16 @@ import type { DelegateTemplate } from './templates.ts';
6
6
  */
7
7
 
8
8
  export interface DelegateCommandArgs {
9
- task: string;
10
- harness?: string;
11
- mode?: string;
12
- model?: string;
13
- scope?: string;
14
- budget?: number;
15
- /** Resume an existing delegated session (--resume=<id>). */
16
- sessionId?: string;
17
- /** GitHub PR number/URL to review (--pr=). */
18
- pr?: string;
9
+ task: string;
10
+ harness?: string;
11
+ mode?: string;
12
+ model?: string;
13
+ scope?: string;
14
+ budget?: number;
15
+ /** Resume an existing delegated session (--resume=<id>). */
16
+ sessionId?: string;
17
+ /** GitHub PR number/URL to review (--pr=). */
18
+ pr?: string;
19
19
  }
20
20
 
21
21
  export type ClaudeCommandArgs = DelegateCommandArgs;
@@ -23,69 +23,69 @@ export type ClaudeCommandArgs = DelegateCommandArgs;
23
23
  const KNOWN_HARNESSES = new Set(['claude', 'codex', 'opencode', 'amp', 'omp']);
24
24
 
25
25
  export function parseDelegateCommand(
26
- raw: string,
27
- knownModes: ReadonlySet<string>,
28
- knownHarnesses: ReadonlySet<string> = KNOWN_HARNESSES,
26
+ raw: string,
27
+ knownModes: ReadonlySet<string>,
28
+ knownHarnesses: ReadonlySet<string> = KNOWN_HARNESSES,
29
29
  ): DelegateCommandArgs {
30
- const flags: Record<string, string> = {};
31
- const rest = raw.replace(/--([a-zA-Z-]+)=(\S+)/g, (_m, k: string, v: string) => {
32
- flags[k] = v;
33
- return '';
34
- });
30
+ const flags: Record<string, string> = {};
31
+ const rest = raw.replace(/--([a-zA-Z-]+)=(\S+)/g, (_m, k: string, v: string) => {
32
+ flags[k] = v;
33
+ return '';
34
+ });
35
35
 
36
- let harness = flags.harness?.toLowerCase();
37
- let mode = flags.mode;
38
- let task = rest.trim();
36
+ let harness = flags.harness?.toLowerCase();
37
+ let mode = flags.mode;
38
+ let task = rest.trim();
39
39
 
40
- // First word handling: harness, mode, or both
41
- const words = task.split(/\s+/).filter(Boolean);
42
- let idx = 0;
43
- if (!harness && words[idx] && knownHarnesses.has(words[idx].toLowerCase())) {
44
- harness = words[idx].toLowerCase();
45
- if (harness === 'omp') harness = 'amp';
46
- idx++;
47
- }
48
- if (!mode && words[idx] && knownModes.has(words[idx])) {
49
- mode = words[idx];
50
- idx++;
51
- }
52
- if (idx > 0) task = words.slice(idx).join(' ').trim();
40
+ // First word handling: harness, mode, or both
41
+ const words = task.split(/\s+/).filter(Boolean);
42
+ let idx = 0;
43
+ if (!harness && words[idx] && knownHarnesses.has(words[idx].toLowerCase())) {
44
+ harness = words[idx].toLowerCase();
45
+ if (harness === 'omp') harness = 'amp';
46
+ idx++;
47
+ }
48
+ if (!mode && words[idx] && knownModes.has(words[idx])) {
49
+ mode = words[idx];
50
+ idx++;
51
+ }
52
+ if (idx > 0) task = words.slice(idx).join(' ').trim();
53
53
 
54
- const out: DelegateCommandArgs = { task };
55
- if (harness) out.harness = harness;
56
- if (mode) out.mode = mode;
57
- if (flags.model) out.model = flags.model;
58
- if (flags.scope) out.scope = flags.scope;
59
- if (flags.budget !== undefined) {
60
- const budget = Number(flags.budget);
61
- if (Number.isFinite(budget) && budget > 0) out.budget = budget;
62
- }
63
- if (flags.resume) out.sessionId = flags.resume;
64
- if (flags.pr) out.pr = flags.pr;
65
- return out;
54
+ const out: DelegateCommandArgs = { task };
55
+ if (harness) out.harness = harness;
56
+ if (mode) out.mode = mode;
57
+ if (flags.model) out.model = flags.model;
58
+ if (flags.scope) out.scope = flags.scope;
59
+ if (flags.budget !== undefined) {
60
+ const budget = Number(flags.budget);
61
+ if (Number.isFinite(budget) && budget > 0) out.budget = budget;
62
+ }
63
+ if (flags.resume) out.sessionId = flags.resume;
64
+ if (flags.pr) out.pr = flags.pr;
65
+ return out;
66
66
  }
67
67
 
68
68
  export function parseClaudeCommand(raw: string, knownModes: ReadonlySet<string>): ClaudeCommandArgs {
69
- return parseDelegateCommand(raw, knownModes);
69
+ return parseDelegateCommand(raw, knownModes);
70
70
  }
71
71
 
72
72
  /**
73
73
  * Apply template defaults when the prompt is empty.
74
74
  */
75
75
  export function resolveDefaults(
76
- args: DelegateCommandArgs,
77
- templates: ReadonlyMap<string, DelegateTemplate>,
76
+ args: DelegateCommandArgs,
77
+ templates: ReadonlyMap<string, DelegateTemplate>,
78
78
  ): { task: string; scope?: string } | null {
79
- if (args.task) {
80
- return args.scope ? { task: args.task, scope: args.scope } : { task: args.task };
81
- }
82
- if (args.mode) {
83
- const t = templates.get(args.mode);
84
- if (t?.defaultTask) {
85
- const scope = args.scope ?? t.defaultScope;
86
- return scope ? { task: t.defaultTask, scope } : { task: t.defaultTask };
87
- }
88
- return null;
89
- }
90
- return null;
79
+ if (args.task) {
80
+ return args.scope ? { task: args.task, scope: args.scope } : { task: args.task };
81
+ }
82
+ if (args.mode) {
83
+ const t = templates.get(args.mode);
84
+ if (t?.defaultTask) {
85
+ const scope = args.scope ?? t.defaultScope;
86
+ return scope ? { task: t.defaultTask, scope } : { task: t.defaultTask };
87
+ }
88
+ return null;
89
+ }
90
+ return null;
91
91
  }
@@ -4,155 +4,161 @@ import { join } from 'node:path';
4
4
  import { DEFAULT_TIMEOUT_MS } from './harnesses/types.ts';
5
5
 
6
6
  export interface HarnessConfig {
7
- model?: string;
8
- timeoutMs?: number;
9
- allowDangerous?: boolean;
10
- maxBudgetUsd?: number;
7
+ model?: string;
8
+ timeoutMs?: number;
9
+ allowDangerous?: boolean;
10
+ maxBudgetUsd?: number;
11
11
  }
12
12
 
13
13
  export interface DelegateConfig {
14
- model?: string;
15
- timeoutMs: number;
16
- defaultMode: string;
17
- defaultHarness: string;
18
- allowDangerous: boolean;
19
- inspectThinking: boolean;
20
- maxBudgetUsd?: number;
21
- autoDelegateHints: boolean;
22
- modelAliases: Record<string, string>;
23
- maxConcurrent: number | { global?: number; perHarness?: Record<string, number> };
24
- maxTranscripts: number;
25
- harnesses: Record<string, HarnessConfig>;
14
+ model?: string;
15
+ timeoutMs: number;
16
+ defaultMode: string;
17
+ defaultHarness: string;
18
+ allowDangerous: boolean;
19
+ inspectThinking: boolean;
20
+ maxBudgetUsd?: number;
21
+ autoDelegateHints: boolean;
22
+ modelAliases: Record<string, string>;
23
+ maxConcurrent: number | { global?: number; perHarness?: Record<string, number> };
24
+ maxTranscripts: number;
25
+ harnesses: Record<string, HarnessConfig>;
26
26
  }
27
27
 
28
28
  export function agentDir(): string {
29
- return process.env.PI_CODING_AGENT_DIR ?? join(homedir(), '.pi', 'agent');
29
+ return process.env.PI_CODING_AGENT_DIR ?? join(homedir(), '.pi', 'agent');
30
30
  }
31
31
 
32
32
  export function outputsDir(harness?: string): string {
33
- const base = join(agentDir(), 'delegate', 'outputs');
34
- return harness ? join(base, harness) : base;
33
+ const base = join(agentDir(), 'delegate', 'outputs');
34
+ return harness ? join(base, harness) : base;
35
35
  }
36
36
 
37
37
  export function legacyOutputsDir(): string {
38
- return join(agentDir(), 'claude-delegate', 'outputs');
38
+ return join(agentDir(), 'claude-delegate', 'outputs');
39
39
  }
40
40
 
41
41
  export function loadConfig(): DelegateConfig {
42
- const cfg: DelegateConfig = {
43
- timeoutMs: DEFAULT_TIMEOUT_MS,
44
- defaultMode: 'general',
45
- defaultHarness: 'claude',
46
- allowDangerous: false,
47
- inspectThinking: false,
48
- autoDelegateHints: false,
49
- modelAliases: { economy: 'haiku', balanced: 'sonnet', max: 'opus' },
50
- maxConcurrent: 1,
51
- maxTranscripts: 100,
52
- harnesses: {},
53
- };
54
- try {
55
- const file = join(agentDir(), 'settings.json');
56
- if (!existsSync(file)) return cfg;
57
- const settings = JSON.parse(readFileSync(file, 'utf8')) as {
58
- delegate?: Partial<DelegateConfig & { harnesses: Record<string, HarnessConfig> }>;
59
- claudeDelegate?: Partial<DelegateConfig & { model?: string }>;
60
- };
61
- // Legacy claudeDelegate -> delegate.harnesses.claude migration
62
- if (settings.claudeDelegate && !settings.delegate) {
63
- const c = settings.claudeDelegate as Partial<DelegateConfig>;
64
- if (typeof c.model === 'string') cfg.harnesses['claude'] = { ...(cfg.harnesses['claude'] ?? {}), model: c.model };
65
- if (typeof c.timeoutMs === 'number' && c.timeoutMs > 0) cfg.timeoutMs = c.timeoutMs;
66
- if (typeof c.defaultMode === 'string') cfg.defaultMode = c.defaultMode;
67
- if (typeof c.allowDangerous === 'boolean') cfg.allowDangerous = c.allowDangerous;
68
- if (typeof c.inspectThinking === 'boolean') cfg.inspectThinking = c.inspectThinking;
69
- if (typeof (c as DelegateConfig).maxBudgetUsd === 'number' && (c as DelegateConfig).maxBudgetUsd! > 0) cfg.maxBudgetUsd = (c as DelegateConfig).maxBudgetUsd;
70
- if (typeof c.autoDelegateHints === 'boolean') cfg.autoDelegateHints = c.autoDelegateHints;
71
- if ((c as DelegateConfig).modelAliases && typeof (c as DelegateConfig).modelAliases === 'object') {
72
- for (const [k, v] of Object.entries((c as DelegateConfig).modelAliases!)) {
73
- if (typeof v === 'string' && v) cfg.modelAliases[k] = v;
74
- }
75
- }
76
- if (typeof c.maxConcurrent === 'number' && c.maxConcurrent >= 0) cfg.maxConcurrent = c.maxConcurrent;
77
- else if (c.maxConcurrent && typeof c.maxConcurrent === 'object') {
78
- const obj = c.maxConcurrent as { global?: unknown; perHarness?: unknown };
79
- const out: { global?: number; perHarness?: Record<string, number> } = {};
80
- if (typeof obj.global === 'number' && obj.global >= 0) out.global = obj.global;
81
- if (obj.perHarness && typeof obj.perHarness === 'object') {
82
- const ph: Record<string, number> = {};
83
- for (const [k, v] of Object.entries(obj.perHarness as Record<string, unknown>)) {
84
- if (typeof v === 'number' && v >= 0) ph[k] = v;
85
- }
86
- if (Object.keys(ph).length > 0) out.perHarness = ph;
87
- }
88
- if (out.global !== undefined || out.perHarness) cfg.maxConcurrent = out;
89
- }
90
- if (typeof c.maxTranscripts === 'number' && c.maxTranscripts >= 0) cfg.maxTranscripts = c.maxTranscripts;
91
- if (c.harnesses && typeof c.harnesses === 'object') {
92
- for (const [k, v] of Object.entries(c.harnesses)) {
93
- if (v && typeof v === 'object') cfg.harnesses[k] = { ...(cfg.harnesses[k] ?? {}), ...(v as HarnessConfig) };
94
- }
95
- }
96
- // also map harnesses.claude if any
97
- return cfg;
98
- }
99
- const d = settings.delegate ?? {};
100
- if (typeof d.defaultHarness === 'string' && d.defaultHarness) cfg.defaultHarness = d.defaultHarness;
101
- if (typeof d.defaultMode === 'string') cfg.defaultMode = d.defaultMode;
102
- if (typeof d.model === 'string') cfg.model = d.model;
103
- if (typeof d.timeoutMs === 'number' && d.timeoutMs > 0) cfg.timeoutMs = d.timeoutMs;
104
- if (typeof d.allowDangerous === 'boolean') cfg.allowDangerous = d.allowDangerous;
105
- if (typeof d.inspectThinking === 'boolean') cfg.inspectThinking = d.inspectThinking;
106
- if (typeof d.maxBudgetUsd === 'number' && d.maxBudgetUsd > 0) cfg.maxBudgetUsd = d.maxBudgetUsd;
107
- if (typeof d.autoDelegateHints === 'boolean') cfg.autoDelegateHints = d.autoDelegateHints;
108
- if (d.modelAliases && typeof d.modelAliases === 'object') {
109
- for (const [k, v] of Object.entries(d.modelAliases)) {
110
- if (typeof v === 'string' && v) cfg.modelAliases[k] = v;
111
- }
112
- }
113
- if (typeof d.maxConcurrent === 'number' && d.maxConcurrent >= 0) cfg.maxConcurrent = d.maxConcurrent;
114
- else if (d.maxConcurrent && typeof d.maxConcurrent === 'object') {
115
- const obj = d.maxConcurrent as { global?: unknown; perHarness?: unknown };
116
- const out: { global?: number; perHarness?: Record<string, number> } = {};
117
- if (typeof obj.global === 'number' && obj.global >= 0) out.global = obj.global;
118
- if (obj.perHarness && typeof obj.perHarness === 'object') {
119
- const ph: Record<string, number> = {};
120
- for (const [k, v] of Object.entries(obj.perHarness as Record<string, unknown>)) {
121
- if (typeof v === 'number' && v >= 0) ph[k] = v;
122
- }
123
- if (Object.keys(ph).length > 0) out.perHarness = ph;
124
- }
125
- if (out.global !== undefined || out.perHarness) cfg.maxConcurrent = out;
126
- }
127
- if (typeof d.maxTranscripts === 'number' && d.maxTranscripts >= 0) cfg.maxTranscripts = d.maxTranscripts;
128
- if (d.harnesses && typeof d.harnesses === 'object') {
129
- for (const [k, v] of Object.entries(d.harnesses)) {
130
- if (v && typeof v === 'object') cfg.harnesses[k] = { ...(v as HarnessConfig) };
131
- }
132
- }
133
- // also support legacy claudeDelegate merged when delegate also present (delegate wins)
134
- if (settings.claudeDelegate) {
135
- const c = settings.claudeDelegate as Partial<DelegateConfig>;
136
- if (typeof c.model === 'string' && !cfg.harnesses['claude']?.model) {
137
- cfg.harnesses['claude'] = { ...(cfg.harnesses['claude'] ?? {}), model: c.model };
138
- }
139
- }
140
- } catch {
141
- // invalid settings — fall back to defaults
142
- }
143
- return cfg;
42
+ const cfg: DelegateConfig = {
43
+ timeoutMs: DEFAULT_TIMEOUT_MS,
44
+ defaultMode: 'general',
45
+ defaultHarness: 'claude',
46
+ allowDangerous: false,
47
+ inspectThinking: false,
48
+ autoDelegateHints: false,
49
+ modelAliases: { economy: 'haiku', balanced: 'sonnet', max: 'opus' },
50
+ maxConcurrent: 1,
51
+ maxTranscripts: 100,
52
+ harnesses: {},
53
+ };
54
+ try {
55
+ const file = join(agentDir(), 'settings.json');
56
+ if (!existsSync(file)) return cfg;
57
+ const settings = JSON.parse(readFileSync(file, 'utf8')) as {
58
+ delegate?: Partial<DelegateConfig & { harnesses: Record<string, HarnessConfig> }>;
59
+ claudeDelegate?: Partial<DelegateConfig & { model?: string }>;
60
+ };
61
+ // Legacy claudeDelegate -> delegate.harnesses.claude migration
62
+ if (settings.claudeDelegate && !settings.delegate) {
63
+ const c = settings.claudeDelegate as Partial<DelegateConfig>;
64
+ if (typeof c.model === 'string') cfg.harnesses['claude'] = { ...(cfg.harnesses['claude'] ?? {}), model: c.model };
65
+ if (typeof c.timeoutMs === 'number' && c.timeoutMs > 0) cfg.timeoutMs = c.timeoutMs;
66
+ if (typeof c.defaultMode === 'string') cfg.defaultMode = c.defaultMode;
67
+ if (typeof c.allowDangerous === 'boolean') cfg.allowDangerous = c.allowDangerous;
68
+ if (typeof c.inspectThinking === 'boolean') cfg.inspectThinking = c.inspectThinking;
69
+ if (typeof (c as DelegateConfig).maxBudgetUsd === 'number' && (c as DelegateConfig).maxBudgetUsd! > 0)
70
+ cfg.maxBudgetUsd = (c as DelegateConfig).maxBudgetUsd;
71
+ if (typeof c.autoDelegateHints === 'boolean') cfg.autoDelegateHints = c.autoDelegateHints;
72
+ if ((c as DelegateConfig).modelAliases && typeof (c as DelegateConfig).modelAliases === 'object') {
73
+ for (const [k, v] of Object.entries((c as DelegateConfig).modelAliases!)) {
74
+ if (typeof v === 'string' && v) cfg.modelAliases[k] = v;
75
+ }
76
+ }
77
+ if (typeof c.maxConcurrent === 'number' && c.maxConcurrent >= 0) cfg.maxConcurrent = c.maxConcurrent;
78
+ else if (c.maxConcurrent && typeof c.maxConcurrent === 'object') {
79
+ const obj = c.maxConcurrent as { global?: unknown; perHarness?: unknown };
80
+ const out: { global?: number; perHarness?: Record<string, number> } = {};
81
+ if (typeof obj.global === 'number' && obj.global >= 0) out.global = obj.global;
82
+ if (obj.perHarness && typeof obj.perHarness === 'object') {
83
+ const ph: Record<string, number> = {};
84
+ for (const [k, v] of Object.entries(obj.perHarness as Record<string, unknown>)) {
85
+ if (typeof v === 'number' && v >= 0) ph[k] = v;
86
+ }
87
+ if (Object.keys(ph).length > 0) out.perHarness = ph;
88
+ }
89
+ if (out.global !== undefined || out.perHarness) cfg.maxConcurrent = out;
90
+ }
91
+ if (typeof c.maxTranscripts === 'number' && c.maxTranscripts >= 0) cfg.maxTranscripts = c.maxTranscripts;
92
+ if (c.harnesses && typeof c.harnesses === 'object') {
93
+ for (const [k, v] of Object.entries(c.harnesses)) {
94
+ if (v && typeof v === 'object') cfg.harnesses[k] = { ...(cfg.harnesses[k] ?? {}), ...(v as HarnessConfig) };
95
+ }
96
+ }
97
+ // also map harnesses.claude if any
98
+ return cfg;
99
+ }
100
+ const d = settings.delegate ?? {};
101
+ if (typeof d.defaultHarness === 'string' && d.defaultHarness) cfg.defaultHarness = d.defaultHarness;
102
+ if (typeof d.defaultMode === 'string') cfg.defaultMode = d.defaultMode;
103
+ if (typeof d.model === 'string') cfg.model = d.model;
104
+ if (typeof d.timeoutMs === 'number' && d.timeoutMs > 0) cfg.timeoutMs = d.timeoutMs;
105
+ if (typeof d.allowDangerous === 'boolean') cfg.allowDangerous = d.allowDangerous;
106
+ if (typeof d.inspectThinking === 'boolean') cfg.inspectThinking = d.inspectThinking;
107
+ if (typeof d.maxBudgetUsd === 'number' && d.maxBudgetUsd > 0) cfg.maxBudgetUsd = d.maxBudgetUsd;
108
+ if (typeof d.autoDelegateHints === 'boolean') cfg.autoDelegateHints = d.autoDelegateHints;
109
+ if (d.modelAliases && typeof d.modelAliases === 'object') {
110
+ for (const [k, v] of Object.entries(d.modelAliases)) {
111
+ if (typeof v === 'string' && v) cfg.modelAliases[k] = v;
112
+ }
113
+ }
114
+ if (typeof d.maxConcurrent === 'number' && d.maxConcurrent >= 0) cfg.maxConcurrent = d.maxConcurrent;
115
+ else if (d.maxConcurrent && typeof d.maxConcurrent === 'object') {
116
+ const obj = d.maxConcurrent as { global?: unknown; perHarness?: unknown };
117
+ const out: { global?: number; perHarness?: Record<string, number> } = {};
118
+ if (typeof obj.global === 'number' && obj.global >= 0) out.global = obj.global;
119
+ if (obj.perHarness && typeof obj.perHarness === 'object') {
120
+ const ph: Record<string, number> = {};
121
+ for (const [k, v] of Object.entries(obj.perHarness as Record<string, unknown>)) {
122
+ if (typeof v === 'number' && v >= 0) ph[k] = v;
123
+ }
124
+ if (Object.keys(ph).length > 0) out.perHarness = ph;
125
+ }
126
+ if (out.global !== undefined || out.perHarness) cfg.maxConcurrent = out;
127
+ }
128
+ if (typeof d.maxTranscripts === 'number' && d.maxTranscripts >= 0) cfg.maxTranscripts = d.maxTranscripts;
129
+ if (d.harnesses && typeof d.harnesses === 'object') {
130
+ for (const [k, v] of Object.entries(d.harnesses)) {
131
+ if (v && typeof v === 'object') cfg.harnesses[k] = { ...(v as HarnessConfig) };
132
+ }
133
+ }
134
+ // also support legacy claudeDelegate merged when delegate also present (delegate wins)
135
+ if (settings.claudeDelegate) {
136
+ const c = settings.claudeDelegate as Partial<DelegateConfig>;
137
+ if (typeof c.model === 'string' && !cfg.harnesses['claude']?.model) {
138
+ cfg.harnesses['claude'] = { ...(cfg.harnesses['claude'] ?? {}), model: c.model };
139
+ }
140
+ }
141
+ } catch {
142
+ // invalid settings — fall back to defaults
143
+ }
144
+ return cfg;
144
145
  }
145
146
 
146
- export function resolveModelForHarness(cfg: DelegateConfig, harness: string, model?: string, templateModel?: string): string | undefined {
147
- const resolve = (m?: string) => (m ? (cfg.modelAliases[m] ?? m) : undefined);
148
- return resolve(model) ?? resolve(templateModel) ?? resolve(cfg.harnesses[harness]?.model) ?? resolve(cfg.model);
147
+ export function resolveModelForHarness(
148
+ cfg: DelegateConfig,
149
+ harness: string,
150
+ model?: string,
151
+ templateModel?: string,
152
+ ): string | undefined {
153
+ const resolve = (m?: string) => (m ? (cfg.modelAliases[m] ?? m) : undefined);
154
+ return resolve(model) ?? resolve(templateModel) ?? resolve(cfg.harnesses[harness]?.model) ?? resolve(cfg.model);
149
155
  }
150
156
 
151
157
  export function getMaxConcurrent(cfg: DelegateConfig, harness?: string): number {
152
- if (typeof cfg.maxConcurrent === 'number') return cfg.maxConcurrent;
153
- // if object shape {global, perHarness}
154
- const mc = cfg.maxConcurrent as unknown as { global?: number; perHarness?: Record<string, number> };
155
- if (harness && mc.perHarness && typeof mc.perHarness[harness] === 'number') return mc.perHarness[harness]!;
156
- if (typeof mc.global === 'number') return mc.global;
157
- return 1;
158
+ if (typeof cfg.maxConcurrent === 'number') return cfg.maxConcurrent;
159
+ // if object shape {global, perHarness}
160
+ const mc = cfg.maxConcurrent as unknown as { global?: number; perHarness?: Record<string, number> };
161
+ if (harness && mc.perHarness && typeof mc.perHarness[harness] === 'number') return mc.perHarness[harness]!;
162
+ if (typeof mc.global === 'number') return mc.global;
163
+ return 1;
158
164
  }