@the-open-engine/zeroshot 6.31.3 → 6.32.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.
Files changed (47) hide show
  1. package/README.md +66 -98
  2. package/cli/index.js +251 -252
  3. package/cli/lib/setup-provider-readiness.js +86 -0
  4. package/cli/lib/setup-scanner-worker.js +120 -0
  5. package/cli/lib/setup-scanner.js +185 -0
  6. package/cli/lib/setup-wizard-input.js +146 -0
  7. package/cli/lib/setup-wizard-model.js +205 -0
  8. package/cli/lib/setup-wizard-plan-view.js +157 -0
  9. package/cli/lib/setup-wizard-scan-view.js +144 -0
  10. package/cli/lib/setup-wizard-terminal.js +237 -0
  11. package/cli/lib/setup-wizard-view.js +180 -0
  12. package/cli/lib/setup-wizard.js +281 -0
  13. package/cli/message-formatters-normal.js +14 -18
  14. package/cli/message-formatters-watch.js +53 -141
  15. package/lib/agent-cli-provider/adapters/codex.d.ts.map +1 -1
  16. package/lib/agent-cli-provider/adapters/codex.js +8 -2
  17. package/lib/agent-cli-provider/adapters/codex.js.map +1 -1
  18. package/lib/agent-cli-provider/provider-registry.d.ts +4 -2
  19. package/lib/agent-cli-provider/provider-registry.d.ts.map +1 -1
  20. package/lib/agent-cli-provider/provider-registry.js +13 -3
  21. package/lib/agent-cli-provider/provider-registry.js.map +1 -1
  22. package/lib/agent-cli-provider/single-agent-runtime.d.ts.map +1 -1
  23. package/lib/agent-cli-provider/single-agent-runtime.js +7 -4
  24. package/lib/agent-cli-provider/single-agent-runtime.js.map +1 -1
  25. package/lib/agent-cli-provider/types.d.ts +2 -0
  26. package/lib/agent-cli-provider/types.d.ts.map +1 -1
  27. package/lib/agent-cli-provider/types.js.map +1 -1
  28. package/lib/completion.js +102 -153
  29. package/lib/settings.js +10 -2
  30. package/lib/setup-apply.js +62 -55
  31. package/lib/setup-plan.js +32 -52
  32. package/lib/start-cluster.js +65 -25
  33. package/npm-shrinkwrap.json +2 -2
  34. package/package.json +3 -3
  35. package/scripts/postinstall.js +54 -0
  36. package/src/agent/agent-lifecycle.js +11 -1
  37. package/src/agent/agent-task-executor.js +25 -7
  38. package/src/agent/structured-output-error.js +42 -0
  39. package/src/agent-cli-provider/adapters/codex.ts +8 -12
  40. package/src/agent-cli-provider/provider-registry.ts +15 -3
  41. package/src/agent-cli-provider/single-agent-runtime.ts +11 -11
  42. package/src/agent-cli-provider/types.ts +2 -0
  43. package/src/preflight.js +27 -1
  44. package/src/status-footer.js +19 -12
  45. package/task-lib/commands/list.js +90 -78
  46. package/task-lib/commands/status.js +97 -40
  47. package/task-lib/effective-status.js +52 -0
@@ -0,0 +1,86 @@
1
+ const { getProviderMetadata, providerSupportsCapability } = require('../../lib/provider-names');
2
+
3
+ const PROVIDER_READINESS = Object.freeze([
4
+ 'ready',
5
+ 'login-required',
6
+ 'incompatible',
7
+ 'unavailable',
8
+ 'unknown',
9
+ ]);
10
+
11
+ function incompatible(reason) {
12
+ return { status: 'incompatible', selectable: false, reason };
13
+ }
14
+
15
+ function executionCompatibility(providerId, isolation, settings) {
16
+ const capability = isolation === 'docker' ? 'dockerIsolation' : 'worktreeIsolation';
17
+ if (isolation !== 'none' && !providerSupportsCapability(providerId, capability)) {
18
+ return incompatible(`${isolation} isolation is not supported`);
19
+ }
20
+ const metadata = getProviderMetadata(providerId);
21
+ const providerSettings = settings.providerSettings?.[providerId];
22
+ if (!metadata.settingsValidator || !providerSettings || typeof providerSettings !== 'object') {
23
+ return null;
24
+ }
25
+ const executionContext = isolation === 'docker' ? 'docker' : 'detached';
26
+ try {
27
+ const error = metadata.settingsValidator(providerSettings, { executionContext });
28
+ return error ? incompatible(error) : null;
29
+ } catch (error) {
30
+ return { status: 'unknown', selectable: false, reason: error.message };
31
+ }
32
+ }
33
+ function unavailableReadiness(probe) {
34
+ const status = probe.commandAvailable ? 'unknown' : 'unavailable';
35
+ const fallback = probe.commandAvailable ? 'CLI probe failed' : 'CLI is not installed';
36
+ return { status, selectable: false, reason: probe.error || fallback };
37
+ }
38
+
39
+ function authReadiness(probe) {
40
+ if (probe.authStatus === 'login-required') {
41
+ return { status: 'login-required', selectable: false, reason: probe.authReason };
42
+ }
43
+ if (probe.authStatus !== 'ready') {
44
+ return {
45
+ status: 'unknown',
46
+ selectable: false,
47
+ reason: probe.authReason || 'readiness is unknown',
48
+ };
49
+ }
50
+ return { status: 'ready', selectable: true, reason: probe.path || 'available' };
51
+ }
52
+
53
+ function assessProviderReadiness({ providerId, probe, isolation, settings }) {
54
+ if (!probe) return { status: 'unknown', selectable: false, reason: 'probe did not complete' };
55
+ if (!probe.available) return unavailableReadiness(probe);
56
+ const compatibility = executionCompatibility(providerId, isolation, settings);
57
+ return compatibility || authReadiness(probe);
58
+ }
59
+
60
+ function providerChoices({ plan, probes, isolation, settings }) {
61
+ return Object.keys(plan.facts.providers).map((providerId) => {
62
+ const metadata = getProviderMetadata(providerId);
63
+ const readiness = assessProviderReadiness({
64
+ providerId,
65
+ probe: probes[`provider:${providerId}`],
66
+ isolation,
67
+ settings,
68
+ });
69
+ return {
70
+ value: providerId,
71
+ label: metadata.displayName,
72
+ detail: readiness.reason,
73
+ status: readiness.status,
74
+ disabled: !readiness.selectable,
75
+ installInstructions: metadata.installInstructions,
76
+ authInstructions: metadata.authInstructions,
77
+ };
78
+ });
79
+ }
80
+
81
+ module.exports = {
82
+ PROVIDER_READINESS,
83
+ assessProviderReadiness,
84
+ executionCompatibility,
85
+ providerChoices,
86
+ };
@@ -0,0 +1,120 @@
1
+ const fs = require('fs');
2
+ const os = require('os');
3
+ const path = require('path');
4
+ const { parentPort, workerData } = require('worker_threads');
5
+
6
+ const { commandExists, getCommandPath } = require('../../lib/provider-detection');
7
+ const { getProviderMetadata, resolveProviderCommand } = require('../../lib/provider-names');
8
+ const { execSync } = require('../../src/lib/safe-exec');
9
+
10
+ function safeExec(command, cwd) {
11
+ try {
12
+ return execSync(command, { cwd, stdio: 'pipe', encoding: 'utf8' }).trim() || null;
13
+ } catch {
14
+ return null;
15
+ }
16
+ }
17
+
18
+ function probeGit() {
19
+ const cwd = workerData.payload.cwd || process.cwd();
20
+ const isRepo = safeExec('git rev-parse --is-inside-work-tree', cwd) === 'true';
21
+ if (!isRepo) {
22
+ return { isRepo: false, branch: null, remote: null, defaultBranch: null, clean: null };
23
+ }
24
+ const defaultRef = safeExec('git rev-parse --abbrev-ref origin/HEAD', cwd);
25
+ return {
26
+ isRepo: true,
27
+ branch: safeExec('git rev-parse --abbrev-ref HEAD', cwd),
28
+ remote: safeExec('git remote get-url origin', cwd),
29
+ defaultBranch: defaultRef ? defaultRef.replace(/^origin\//, '') : null,
30
+ clean: safeExec('git status --porcelain', cwd) === null,
31
+ };
32
+ }
33
+
34
+ function probeDocker() {
35
+ const { checkDocker } = require('../../src/preflight');
36
+ return checkDocker();
37
+ }
38
+
39
+ function probeIssue() {
40
+ const { checkGhAuth } = require('../../src/preflight');
41
+ return checkGhAuth();
42
+ }
43
+
44
+ function expandCredentialPath(value) {
45
+ if (value === '~') return os.homedir();
46
+ if (value.startsWith('~/')) return path.join(os.homedir(), value.slice(2));
47
+ return value;
48
+ }
49
+
50
+ function hasCredentialEvidence(metadata) {
51
+ const hasEnvironment = metadata.credentialEnvKeys.some((key) => {
52
+ const value = process.env[key];
53
+ return typeof value === 'string' && value.trim().length > 0;
54
+ });
55
+ if (hasEnvironment) return true;
56
+ return metadata.credentialPaths.some((item) => fs.existsSync(expandCredentialPath(item)));
57
+ }
58
+
59
+ function providerAuthStatus(id, metadata, available) {
60
+ if (!available) return { authStatus: 'unknown', authReason: null };
61
+ if (id === 'gateway') return { authStatus: 'ready', authReason: null };
62
+ if (id === 'claude') {
63
+ const { checkClaudeAuth } = require('../../src/preflight');
64
+ const auth = checkClaudeAuth();
65
+ return auth.authenticated
66
+ ? { authStatus: 'ready', authReason: null }
67
+ : { authStatus: 'login-required', authReason: auth.error || 'authentication not found' };
68
+ }
69
+ return hasCredentialEvidence(metadata)
70
+ ? { authStatus: 'ready', authReason: null }
71
+ : { authStatus: 'login-required', authReason: metadata.authInstructions };
72
+ }
73
+
74
+ function probeProvider() {
75
+ const id = workerData.payload.id;
76
+ const metadata = getProviderMetadata(id);
77
+ const { command } = resolveProviderCommand(id);
78
+ const commandAvailable = commandExists(command);
79
+ let available = false;
80
+ let error = null;
81
+ try {
82
+ const { getProvider } = require('../../src/providers');
83
+ available = getProvider(id).isAvailable() === true;
84
+ } catch (probeError) {
85
+ error = probeError.message;
86
+ }
87
+ const auth = providerAuthStatus(id, metadata, available);
88
+ return {
89
+ id,
90
+ available,
91
+ commandAvailable,
92
+ command,
93
+ path: available ? getCommandPath(command) : null,
94
+ displayName: metadata.displayName,
95
+ authStatus: auth.authStatus,
96
+ authReason: auth.authReason,
97
+ error,
98
+ };
99
+ }
100
+
101
+ function runProbe() {
102
+ switch (workerData.kind) {
103
+ case 'git':
104
+ return probeGit();
105
+ case 'docker':
106
+ return probeDocker();
107
+ case 'issue':
108
+ return probeIssue();
109
+ case 'provider':
110
+ return probeProvider();
111
+ default:
112
+ throw new Error(`Unknown setup probe: ${workerData.kind}`);
113
+ }
114
+ }
115
+
116
+ try {
117
+ parentPort.postMessage({ ok: true, result: runProbe() });
118
+ } catch (error) {
119
+ parentPort.postMessage({ ok: false, error: error.message });
120
+ }
@@ -0,0 +1,185 @@
1
+ const path = require('path');
2
+ const { Worker } = require('worker_threads');
3
+
4
+ const { buildSetupPlan } = require('../../lib/setup-plan');
5
+ const { getProviderDefaults } = require('../../lib/provider-defaults');
6
+ const {
7
+ getDefaultProviderId,
8
+ getProviderMetadata,
9
+ listProviderMetadata,
10
+ resolveProviderCommand,
11
+ } = require('../../lib/provider-names');
12
+ const packageJson = require('../../package.json');
13
+
14
+ const PROBE_TIMEOUT_MS = 15_000;
15
+
16
+ function workerProbe(kind, payload = {}) {
17
+ return new Promise((resolve) => {
18
+ const worker = new Worker(path.join(__dirname, 'setup-scanner-worker.js'), {
19
+ workerData: { kind, payload },
20
+ });
21
+ let settled = false;
22
+ let timer;
23
+ const settle = (result) => {
24
+ if (settled) return;
25
+ settled = true;
26
+ clearTimeout(timer);
27
+ worker.terminate().catch(() => {});
28
+ resolve(result);
29
+ };
30
+ timer = setTimeout(() => settle({ ok: false, error: 'probe timed out' }), PROBE_TIMEOUT_MS);
31
+ timer.unref?.();
32
+ worker.once('message', settle);
33
+ worker.once('error', (error) => settle({ ok: false, error: error.message }));
34
+ worker.once('exit', (code) => {
35
+ if (code !== 0) settle({ ok: false, error: `probe worker exited ${code}` });
36
+ });
37
+ });
38
+ }
39
+
40
+ function commandResult(command, providers, issue) {
41
+ if (command === 'gh') return issue.installed;
42
+ const provider = Object.values(providers).find((item) => item.command === command);
43
+ return provider ? provider.commandAvailable : false;
44
+ }
45
+
46
+ function commandPath(command, providers) {
47
+ const provider = Object.values(providers).find((item) => item.command === command);
48
+ return provider?.path || null;
49
+ }
50
+
51
+ function gitExecResult(command, git) {
52
+ if (command.includes('is-inside-work-tree')) return git.isRepo ? 'true\n' : null;
53
+ if (command.includes('abbrev-ref origin/HEAD')) {
54
+ return git.defaultBranch ? `origin/${git.defaultBranch}\n` : null;
55
+ }
56
+ if (command.includes('abbrev-ref HEAD')) return git.branch ? `${git.branch}\n` : null;
57
+ if (command.includes('remote get-url origin')) return git.remote ? `${git.remote}\n` : null;
58
+ return null;
59
+ }
60
+
61
+ function createPlanDeps({ git, docker, issue, providers }) {
62
+ const providerIds = Object.keys(providers);
63
+ return {
64
+ commandExists: (command) => commandResult(command, providers, issue),
65
+ getCommandPath: (command) => commandPath(command, providers),
66
+ checkDocker: () => ({ available: docker.available }),
67
+ checkGhAuth: () => ({ authenticated: issue.authenticated }),
68
+ execSync: (command) => {
69
+ const result = gitExecResult(command, git);
70
+ if (result === null) throw new Error(`setup scan has no result for: ${command}`);
71
+ return result;
72
+ },
73
+ listProviders: () => providerIds,
74
+ getProvider: (name) => ({
75
+ cliCommand: providers[name].command,
76
+ isAvailable: () => providers[name].available,
77
+ }),
78
+ getProviderDefaults,
79
+ getDefaultProviderId,
80
+ getProviderMetadata,
81
+ getNodeVersion: () => process.version,
82
+ getPackageVersion: () => packageJson.version,
83
+ };
84
+ }
85
+
86
+ function fallbackResult(probe) {
87
+ if (probe.kind === 'git') {
88
+ return { isRepo: false, branch: null, remote: null, defaultBranch: null, clean: null };
89
+ }
90
+ if (probe.kind === 'docker') return { available: false, error: probe.error };
91
+ if (probe.kind === 'issue') {
92
+ return { installed: false, authenticated: false, error: probe.error };
93
+ }
94
+ const metadata = getProviderMetadata(probe.id);
95
+ const { command } = resolveProviderCommand(probe.id);
96
+ return {
97
+ id: probe.id,
98
+ available: false,
99
+ commandAvailable: false,
100
+ command,
101
+ path: null,
102
+ authStatus: 'unknown',
103
+ authReason: probe.error,
104
+ displayName: metadata.displayName,
105
+ };
106
+ }
107
+
108
+ async function trackedProbe(spec, probe, onProgress, startedAt, cwd) {
109
+ let response;
110
+ try {
111
+ response = await probe(spec.kind, { cwd, ...(spec.id ? { id: spec.id } : {}) });
112
+ } catch (error) {
113
+ response = { ok: false, error: error.message };
114
+ }
115
+ const result = response?.ok
116
+ ? response.result
117
+ : fallbackResult({ ...spec, error: response?.error });
118
+ onProgress?.({
119
+ type: 'complete',
120
+ id: spec.id ? `provider:${spec.id}` : spec.kind,
121
+ kind: spec.kind,
122
+ providerId: spec.id || null,
123
+ ok: response?.ok === true,
124
+ elapsedMs: Date.now() - startedAt,
125
+ result,
126
+ });
127
+ return [spec, result];
128
+ }
129
+ function probeSpecs(metadata) {
130
+ return [
131
+ { kind: 'git' },
132
+ { kind: 'docker' },
133
+ { kind: 'issue' },
134
+ ...metadata.map((provider) => ({ kind: 'provider', id: provider.id })),
135
+ ];
136
+ }
137
+
138
+ function indexProbeResults(entries, metadata) {
139
+ const probes = Object.fromEntries(
140
+ entries.map(([spec, result]) => [spec.id ? `provider:${spec.id}` : spec.kind, result])
141
+ );
142
+ const providers = Object.fromEntries(
143
+ metadata.map((provider) => [provider.id, probes[`provider:${provider.id}`]])
144
+ );
145
+ return { probes, providers };
146
+ }
147
+
148
+ function planFromScan({ cwd, settings, repoSettings, env, probes, providers, deps }) {
149
+ const planDeps = (deps.createPlanDeps || createPlanDeps)({
150
+ git: probes.git,
151
+ docker: probes.docker,
152
+ issue: probes.issue,
153
+ providers,
154
+ });
155
+ return (deps.buildSetupPlan || buildSetupPlan)({
156
+ cwd,
157
+ settings,
158
+ repoSettings,
159
+ env: { ...env, __isTTY: true },
160
+ deps: planDeps,
161
+ });
162
+ }
163
+
164
+ async function scanSetupEnvironment({ cwd, settings, repoSettings, env, onProgress, deps = {} }) {
165
+ const probe = deps.probe || workerProbe;
166
+ const metadata = (deps.listProviderMetadata || listProviderMetadata)();
167
+ const specs = probeSpecs(metadata);
168
+ const startedAt = Date.now();
169
+ if (onProgress) onProgress({ type: 'start', probes: specs, elapsedMs: 0 });
170
+ const entries = await Promise.all(
171
+ specs.map((spec) => trackedProbe(spec, probe, onProgress, startedAt, cwd))
172
+ );
173
+ const { probes, providers } = indexProbeResults(entries, metadata);
174
+ const plan = planFromScan({ cwd, settings, repoSettings, env, probes, providers, deps });
175
+ const elapsedMs = Date.now() - startedAt;
176
+ if (onProgress) onProgress({ type: 'finish', elapsedMs, plan, probes });
177
+ return { plan, probes, elapsedMs };
178
+ }
179
+
180
+ module.exports = {
181
+ PROBE_TIMEOUT_MS,
182
+ createPlanDeps,
183
+ scanSetupEnvironment,
184
+ workerProbe,
185
+ };
@@ -0,0 +1,146 @@
1
+ const CANCEL_KEYS = new Set(['escape', 'ctrl-c', 'q']);
2
+
3
+ function keyForCharacter(character) {
4
+ if (character === '\x03') return 'ctrl-c';
5
+ if (character === '\x1b') return 'escape';
6
+ if (character === '\r' || character === '\n') return 'enter';
7
+ if (character === ' ') return 'space';
8
+ if (/^[1-9]$/.test(character)) return character;
9
+ const aliases = { j: 'down', k: 'up', h: 'left', l: 'right', q: 'q' };
10
+ return aliases[character] || null;
11
+ }
12
+
13
+ function parseKeys(chunk) {
14
+ const text = Buffer.isBuffer(chunk) ? chunk.toString('utf8') : String(chunk);
15
+ const keys = [];
16
+ let index = 0;
17
+ while (index < text.length) {
18
+ if (text.startsWith('\x1b[A', index)) {
19
+ keys.push('up');
20
+ index += 3;
21
+ continue;
22
+ }
23
+ if (text.startsWith('\x1b[B', index)) {
24
+ keys.push('down');
25
+ index += 3;
26
+ continue;
27
+ }
28
+ if (text.startsWith('\x1b[C', index)) {
29
+ keys.push('right');
30
+ index += 3;
31
+ continue;
32
+ }
33
+ if (text.startsWith('\x1b[D', index)) {
34
+ keys.push('left');
35
+ index += 3;
36
+ continue;
37
+ }
38
+ const key = keyForCharacter(text[index]);
39
+ if (key) keys.push(key);
40
+ index += 1;
41
+ }
42
+ return keys;
43
+ }
44
+
45
+ function createKeyReader(stdin, stdout) {
46
+ const queued = [];
47
+ const waiters = [];
48
+ const deliver = (key) => {
49
+ const waiter = waiters.shift();
50
+ if (waiter) waiter(key);
51
+ else queued.push(key);
52
+ };
53
+ const onData = (chunk) => {
54
+ for (const key of parseKeys(chunk)) deliver(key);
55
+ };
56
+ const onResize = () => deliver('resize');
57
+ stdin.on('data', onData);
58
+ if (stdout && typeof stdout.on === 'function') stdout.on('resize', onResize);
59
+ return {
60
+ read() {
61
+ if (queued.length > 0) return Promise.resolve(queued.shift());
62
+ return new Promise((resolve) => waiters.push(resolve));
63
+ },
64
+ close() {
65
+ stdin.off('data', onData);
66
+ if (stdout && typeof stdout.off === 'function') stdout.off('resize', onResize);
67
+ },
68
+ };
69
+ }
70
+
71
+ function beginTerminal(stdin, stdout) {
72
+ const wasRaw = stdin.isRaw === true;
73
+ let restored = false;
74
+ if (typeof stdin.setRawMode === 'function') stdin.setRawMode(true);
75
+ if (typeof stdin.resume === 'function') stdin.resume();
76
+ stdout.write('\x1b[?25l');
77
+ return () => {
78
+ if (restored) return;
79
+ restored = true;
80
+ stdout.write('\x1b[?25h');
81
+ if (typeof stdin.setRawMode === 'function') stdin.setRawMode(wasRaw);
82
+ if (typeof stdin.pause === 'function') stdin.pause();
83
+ };
84
+ }
85
+
86
+ function firstEnabledChoice(choices, initial) {
87
+ for (let offset = 0; offset < choices.length; offset += 1) {
88
+ const index = (Math.max(0, initial) + offset) % choices.length;
89
+ if (!choices[index].disabled) return index;
90
+ }
91
+ return -1;
92
+ }
93
+
94
+ function nextEnabledChoice(choices, selected, direction) {
95
+ if (choices.length === 0 || selected < 0) return selected;
96
+ let next = selected;
97
+ do {
98
+ next = (next + direction + choices.length) % choices.length;
99
+ if (!choices[next].disabled) return next;
100
+ } while (next !== selected);
101
+ return selected;
102
+ }
103
+
104
+ function createSelectionState(choices, initial = 0) {
105
+ return { selected: firstEnabledChoice(choices, initial), status: 'active', value: undefined };
106
+ }
107
+
108
+ function numberedSelection(state, key, choices) {
109
+ if (!/^[1-9]$/.test(key)) return null;
110
+ const index = Number(key) - 1;
111
+ if (!choices[index] || choices[index].disabled) return state;
112
+ return { selected: index, status: 'confirmed', value: choices[index].value };
113
+ }
114
+
115
+ function selectionDirection(key, orientation) {
116
+ if (orientation === 'horizontal') {
117
+ if (key === 'left' || key === 'up') return -1;
118
+ if (key === 'right' || key === 'down') return 1;
119
+ return 0;
120
+ }
121
+ if (key === 'up') return -1;
122
+ if (key === 'down') return 1;
123
+ return 0;
124
+ }
125
+
126
+ function reduceSelection(state, key, choices, orientation = 'vertical') {
127
+ if (state.status !== 'active') return state;
128
+ if (CANCEL_KEYS.has(key)) return { ...state, status: 'cancelled', value: null };
129
+ const numbered = numberedSelection(state, key, choices);
130
+ if (numbered) return numbered;
131
+ const direction = selectionDirection(key, orientation);
132
+ if (direction !== 0) {
133
+ return { ...state, selected: nextEnabledChoice(choices, state.selected, direction) };
134
+ }
135
+ if (key !== 'enter' || state.selected < 0 || choices[state.selected].disabled) return state;
136
+ return { ...state, status: 'confirmed', value: choices[state.selected].value };
137
+ }
138
+
139
+ module.exports = {
140
+ CANCEL_KEYS,
141
+ beginTerminal,
142
+ createKeyReader,
143
+ createSelectionState,
144
+ parseKeys,
145
+ reduceSelection,
146
+ };