arkgate 4.2.1 → 4.4.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/CHANGELOG.md +85 -3
- package/README.md +24 -8
- package/bin/ark-check-runtime.mjs +16 -1
- package/bin/ark-mcp-runtime.mjs +64 -0
- package/bin/ark.mjs +55 -1
- package/bin/lib/adapter-contract.mjs +88 -5
- package/bin/lib/agent-projection-command.mjs +396 -0
- package/bin/lib/agent-projection.mjs +319 -0
- package/bin/lib/agent-skills-package.mjs +266 -0
- package/bin/lib/baseline-key.mjs +32 -0
- package/bin/lib/ci-and-commands.mjs +55 -5
- package/bin/lib/diagnostic-catalog.mjs +155 -0
- package/bin/lib/doctor-plan.mjs +25 -0
- package/bin/lib/html-report-advisories.mjs +33 -0
- package/bin/lib/html-report-depth.mjs +24 -0
- package/bin/lib/improvement-compass-doctor.mjs +106 -0
- package/bin/lib/improvement-compass.mjs +630 -0
- package/bin/lib/status-command.mjs +369 -0
- package/bin/lib/status-manifest.mjs +431 -0
- package/dist/eslint/index.cjs +3 -3
- package/dist/eslint/index.js +3 -3
- package/dist/index.cjs +46 -11
- package/dist/index.d.ts +886 -6
- package/dist/index.js +46 -11
- package/docs/README.md +9 -8
- package/docs/agent-guide.md +128 -14
- package/docs/configuration.md +7 -0
- package/docs/develop.md +12 -1
- package/docs/diagnostics.md +606 -0
- package/docs/package-surface.md +44 -31
- package/docs/product-voice.md +71 -0
- package/docs/use.md +60 -1
- package/package.json +7 -1
- package/schemas/ark.analysis-result.schema.json +14 -1
- package/schemas/ark.status-manifest.schema.json +270 -0
- package/server.json +2 -2
- package/templates/agent-skills/README.md +59 -0
- package/templates/agent-skills/ark-adopt/SKILL.md +191 -0
- package/templates/agent-skills/ark-architect/SKILL.md +195 -0
- package/templates/agent-skills/ark-autopilot/SKILL.md +262 -0
- package/templates/agent-skills/ark-contract/SKILL.md +156 -0
- package/templates/agent-skills/ark-coverage/SKILL.md +187 -0
- package/templates/agent-skills/ark-explain/SKILL.md +230 -0
- package/templates/agent-skills/ark-explore/SKILL.md +397 -0
- package/templates/agent-skills/ark-fix/SKILL.md +205 -0
- package/templates/agent-skills/ark-loop/SKILL.md +200 -0
- package/templates/agent-skills/ark-place/SKILL.md +182 -0
- package/templates/agent-skills/ark-runtime/SKILL.md +127 -0
- package/templates/agent-skills/ark-think/SKILL.md +153 -0
- package/templates/agent-skills/ark-upgrade/SKILL.md +238 -0
- package/templates/skills/ark-adopt.md +20 -0
- package/templates/skills/ark-architect.md +21 -1
- package/templates/skills/ark-autopilot.md +25 -5
- package/templates/skills/ark-contract.md +20 -0
- package/templates/skills/ark-coverage.md +20 -0
- package/templates/skills/ark-explain.md +20 -0
- package/templates/skills/ark-explore.md +23 -3
- package/templates/skills/ark-fix.md +22 -2
- package/templates/skills/ark-loop.md +22 -2
- package/templates/skills/ark-place.md +20 -0
- package/templates/skills/ark-runtime.md +7 -0
- package/templates/skills/ark-think.md +20 -0
- package/templates/skills/ark-upgrade.md +20 -0
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ACS03 — gather session/project evidence for `ark status` / MCP `ark_status`.
|
|
3
|
+
*
|
|
4
|
+
* Fail-closed and CI-safe: never prompts (no readline), never invents hard write,
|
|
5
|
+
* never invents a numeric score. Pure assembly lives in Domain statusManifest.
|
|
6
|
+
*/
|
|
7
|
+
import { createHash } from 'node:crypto';
|
|
8
|
+
import fs from 'node:fs';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import { fileURLToPath } from 'node:url';
|
|
11
|
+
|
|
12
|
+
import { buildStatusManifest } from './status-manifest.mjs';
|
|
13
|
+
import { createProjectId } from './project-identity.mjs';
|
|
14
|
+
import { resolveEffectiveProjectRoot } from './project-root.mjs';
|
|
15
|
+
import { detectWritePathCapabilities } from './write-path-detect.mjs';
|
|
16
|
+
import { buildWritePathHonesty } from './enforcement-honesty.mjs';
|
|
17
|
+
import { HOST_SUPPORT_MATRIX } from './host-support-matrix.mjs';
|
|
18
|
+
import { detectActiveAgentHost } from './skill-install.mjs';
|
|
19
|
+
import { readBaseline } from './violations.mjs';
|
|
20
|
+
import { reportsDir, readJsonSafe } from './html-report.mjs';
|
|
21
|
+
import { summarizeRulesUnderContract } from './rules-under-contract.mjs';
|
|
22
|
+
|
|
23
|
+
function sha256Hex(value) {
|
|
24
|
+
return createHash('sha256').update(value, 'utf8').digest('hex');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function packageVersion() {
|
|
28
|
+
try {
|
|
29
|
+
const pkgPath = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', '..', 'package.json');
|
|
30
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
31
|
+
return typeof pkg.version === 'string' ? pkg.version : 'unknown';
|
|
32
|
+
} catch {
|
|
33
|
+
return 'unknown';
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function realpathOrResolve(p) {
|
|
38
|
+
try {
|
|
39
|
+
return fs.realpathSync(p);
|
|
40
|
+
} catch {
|
|
41
|
+
return path.resolve(p);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Classify expectedRoot against resolvedRoot after canonicalization.
|
|
47
|
+
* @param {string} resolvedRoot
|
|
48
|
+
* @param {string|undefined} expectedRoot
|
|
49
|
+
* @returns {'exact'|'descendant'|'outside'|'unknown'}
|
|
50
|
+
*/
|
|
51
|
+
export function classifyExpectedRootRelation(resolvedRoot, expectedRoot) {
|
|
52
|
+
if (expectedRoot == null || expectedRoot === '') return 'unknown';
|
|
53
|
+
if (typeof expectedRoot !== 'string' || !path.isAbsolute(expectedRoot)) return 'unknown';
|
|
54
|
+
let expected;
|
|
55
|
+
let resolved;
|
|
56
|
+
try {
|
|
57
|
+
expected = realpathOrResolve(expectedRoot);
|
|
58
|
+
resolved = realpathOrResolve(resolvedRoot);
|
|
59
|
+
} catch {
|
|
60
|
+
return 'unknown';
|
|
61
|
+
}
|
|
62
|
+
if (expected === resolved) return 'exact';
|
|
63
|
+
const rel = path.relative(resolved, expected);
|
|
64
|
+
if (rel === '') return 'exact';
|
|
65
|
+
if (!rel.startsWith('..') && !path.isAbsolute(rel)) return 'descendant';
|
|
66
|
+
return 'outside';
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Count baseline keys that belong to the ArkRules plane only.
|
|
71
|
+
* Full baseline size is used for lastCheck.frozenResidual (all frozen debt);
|
|
72
|
+
* rules.frozenResidual must not over-count layer/capability freezes as ArkRules residual.
|
|
73
|
+
* @param {{ exists?: boolean, keys?: Set<string>|Iterable<string> }|null|undefined} baseline
|
|
74
|
+
* @returns {number|null}
|
|
75
|
+
*/
|
|
76
|
+
export function countArkruleFrozenKeys(baseline) {
|
|
77
|
+
if (!baseline?.exists || !baseline.keys) return baseline?.exists ? 0 : null;
|
|
78
|
+
let n = 0;
|
|
79
|
+
for (const key of baseline.keys) {
|
|
80
|
+
if (typeof key !== 'string' || key.length === 0) continue;
|
|
81
|
+
// baselineKey format: ruleId|file|fromLayer|toLayer|target
|
|
82
|
+
if (
|
|
83
|
+
key.startsWith('ARKRULE_') ||
|
|
84
|
+
key.startsWith('INVARIANT_UNCOVERED|')
|
|
85
|
+
) {
|
|
86
|
+
n += 1;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return n;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Map latest report snapshot + baseline into last-check facts.
|
|
94
|
+
* @param {object|null} latest
|
|
95
|
+
* @param {{ exists: boolean, keys: Set<string> }} baseline
|
|
96
|
+
*/
|
|
97
|
+
export function lastCheckFactsFromSnapshot(latest, baseline) {
|
|
98
|
+
const frozenResidual = baseline?.exists ? baseline.keys.size : null;
|
|
99
|
+
if (!latest || typeof latest !== 'object') {
|
|
100
|
+
return {
|
|
101
|
+
lastCheckAt: null,
|
|
102
|
+
lastCheckVerdict: null,
|
|
103
|
+
activeViolations: null,
|
|
104
|
+
frozenResidual,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const at =
|
|
108
|
+
typeof latest.generatedAt === 'string'
|
|
109
|
+
? latest.generatedAt
|
|
110
|
+
: typeof latest.at === 'string'
|
|
111
|
+
? latest.at
|
|
112
|
+
: null;
|
|
113
|
+
const active =
|
|
114
|
+
typeof latest.activeViolations === 'number'
|
|
115
|
+
? latest.activeViolations
|
|
116
|
+
: typeof latest.violations?.active === 'number'
|
|
117
|
+
? latest.violations.active
|
|
118
|
+
: null;
|
|
119
|
+
let verdict = null;
|
|
120
|
+
if (latest.ok === true && (active == null || active === 0)) verdict = 'pass';
|
|
121
|
+
else if (latest.ok === false || (typeof active === 'number' && active > 0)) verdict = 'fail';
|
|
122
|
+
else if (latest.completeness === 'partial' || latest.completeness === 'unavailable') {
|
|
123
|
+
verdict = 'incomplete';
|
|
124
|
+
} else if (latest.ok === true) verdict = 'pass';
|
|
125
|
+
return {
|
|
126
|
+
lastCheckAt: at,
|
|
127
|
+
lastCheckVerdict: verdict,
|
|
128
|
+
activeViolations: active,
|
|
129
|
+
frozenResidual,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Collect status facts from disk (no prompts).
|
|
135
|
+
* @param {{
|
|
136
|
+
* root?: string,
|
|
137
|
+
* config?: string,
|
|
138
|
+
* expectedRoot?: string,
|
|
139
|
+
* expectedProjectId?: string,
|
|
140
|
+
* host?: string,
|
|
141
|
+
* arkgateVersion?: string,
|
|
142
|
+
* env?: NodeJS.ProcessEnv,
|
|
143
|
+
* }} [options]
|
|
144
|
+
*/
|
|
145
|
+
export function collectStatusFacts(options = {}) {
|
|
146
|
+
const startRoot = path.resolve(options.root || process.cwd());
|
|
147
|
+
const configName = options.config || 'ark.config.json';
|
|
148
|
+
const resolved = resolveEffectiveProjectRoot(startRoot, {
|
|
149
|
+
configName,
|
|
150
|
+
writeMode: false,
|
|
151
|
+
});
|
|
152
|
+
const resolvedRoot = path.resolve(resolved.root || startRoot);
|
|
153
|
+
const configPath = resolved.configFound
|
|
154
|
+
? path.resolve(resolved.configPath)
|
|
155
|
+
: path.join(resolvedRoot, typeof configName === 'string' ? path.basename(configName) : 'ark.config.json');
|
|
156
|
+
const configExists = fs.existsSync(configPath);
|
|
157
|
+
|
|
158
|
+
let config = null;
|
|
159
|
+
if (configExists) {
|
|
160
|
+
try {
|
|
161
|
+
config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
162
|
+
} catch {
|
|
163
|
+
config = null;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const projectId = configExists
|
|
168
|
+
? createProjectId(realpathOrResolve(resolvedRoot), realpathOrResolve(configPath), sha256Hex)
|
|
169
|
+
: null;
|
|
170
|
+
|
|
171
|
+
const expectation =
|
|
172
|
+
options.expectedRoot != null || options.expectedProjectId != null
|
|
173
|
+
? {
|
|
174
|
+
...(options.expectedRoot != null ? { expectedRoot: options.expectedRoot } : {}),
|
|
175
|
+
...(options.expectedProjectId != null
|
|
176
|
+
? { expectedProjectId: options.expectedProjectId }
|
|
177
|
+
: {}),
|
|
178
|
+
}
|
|
179
|
+
: null;
|
|
180
|
+
|
|
181
|
+
const expectedRootRelation = expectation?.expectedRoot
|
|
182
|
+
? classifyExpectedRootRelation(resolvedRoot, expectation.expectedRoot)
|
|
183
|
+
: null;
|
|
184
|
+
|
|
185
|
+
const env = options.env || process.env;
|
|
186
|
+
const activeHost =
|
|
187
|
+
(typeof options.host === 'string' && options.host.trim()) ||
|
|
188
|
+
detectActiveAgentHost(env) ||
|
|
189
|
+
'unknown';
|
|
190
|
+
|
|
191
|
+
let writePath = null;
|
|
192
|
+
let writePathUnavailable = false;
|
|
193
|
+
try {
|
|
194
|
+
writePath = detectWritePathCapabilities(resolvedRoot, activeHost === 'unknown' ? undefined : activeHost);
|
|
195
|
+
} catch {
|
|
196
|
+
writePathUnavailable = true;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const hardWriteActive = writePath?.enforcementState?.localWrite?.hard === true;
|
|
200
|
+
const hostKey =
|
|
201
|
+
typeof writePath?.activeHost === 'string'
|
|
202
|
+
? writePath.activeHost.trim().toLowerCase()
|
|
203
|
+
: String(activeHost).trim().toLowerCase();
|
|
204
|
+
const matrix = HOST_SUPPORT_MATRIX[hostKey] ?? null;
|
|
205
|
+
// Soft only when the host is known and matrix hard-write is false (Cursor/Codex/OpenCode).
|
|
206
|
+
const softWriteHost = Boolean(matrix && matrix.capabilities?.['hard-write'] !== true);
|
|
207
|
+
const writePathHonesty = buildWritePathHonesty(hostKey, hardWriteActive, {
|
|
208
|
+
packageInstalled: writePath?.enforcementState?.localWrite?.installed !== false,
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
const latestPath = path.join(reportsDir(resolvedRoot), 'latest.json');
|
|
212
|
+
const latest = readJsonSafe(latestPath);
|
|
213
|
+
const baseline = readBaseline(resolvedRoot, '.ark-baseline.json');
|
|
214
|
+
const lastCheck = lastCheckFactsFromSnapshot(latest, baseline);
|
|
215
|
+
|
|
216
|
+
const arkRulesLoaded = Boolean(
|
|
217
|
+
config?.arkRules && typeof config.arkRules === 'object' && Object.keys(config.arkRules).length > 0
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
let rulesInventoried = null;
|
|
221
|
+
let rulesUnderContract = null;
|
|
222
|
+
let rulesFrozenResidual = null;
|
|
223
|
+
if (configExists && config) {
|
|
224
|
+
try {
|
|
225
|
+
const summary = summarizeRulesUnderContract(resolvedRoot, config);
|
|
226
|
+
if (summary?.active === false) {
|
|
227
|
+
rulesInventoried = 0;
|
|
228
|
+
rulesUnderContract = 0;
|
|
229
|
+
rulesFrozenResidual = 0;
|
|
230
|
+
} else if (summary && summary.active !== false) {
|
|
231
|
+
const structure = Number(summary.structureRules) || 0;
|
|
232
|
+
const invariants = Number(summary.invariants) || 0;
|
|
233
|
+
const covered = Number(summary.coveredInvariants) || 0;
|
|
234
|
+
rulesInventoried = structure + invariants;
|
|
235
|
+
rulesUnderContract = structure + covered;
|
|
236
|
+
rulesFrozenResidual = baseline.exists ? countArkruleFrozenKeys(baseline) : 0;
|
|
237
|
+
}
|
|
238
|
+
} catch {
|
|
239
|
+
// Counts stay null when inventory cannot be loaded — honest absence, not zero score.
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const arkruleFrozenFallback = countArkruleFrozenKeys(baseline);
|
|
244
|
+
|
|
245
|
+
return {
|
|
246
|
+
arkgateVersion: options.arkgateVersion || packageVersion(),
|
|
247
|
+
resolvedRoot: realpathOrResolve(resolvedRoot),
|
|
248
|
+
resolvedConfigPath: configExists ? realpathOrResolve(configPath) : null,
|
|
249
|
+
projectId,
|
|
250
|
+
expectation,
|
|
251
|
+
expectedRootRelation,
|
|
252
|
+
activeHost: hostKey || null,
|
|
253
|
+
hardWriteActive,
|
|
254
|
+
softWriteHost: softWriteHost || writePathHonesty.softWriteHost === true,
|
|
255
|
+
writePathUnavailable,
|
|
256
|
+
honestLabel: writePathHonesty.message || null,
|
|
257
|
+
lastCheckAt: lastCheck.lastCheckAt,
|
|
258
|
+
lastCheckVerdict: lastCheck.lastCheckVerdict,
|
|
259
|
+
activeViolations: lastCheck.activeViolations,
|
|
260
|
+
frozenResidual: lastCheck.frozenResidual,
|
|
261
|
+
arkRulesLoaded,
|
|
262
|
+
rulesInventoried,
|
|
263
|
+
rulesUnderContract,
|
|
264
|
+
rulesFrozenResidual:
|
|
265
|
+
rulesFrozenResidual != null
|
|
266
|
+
? rulesFrozenResidual
|
|
267
|
+
: arkRulesLoaded && arkruleFrozenFallback != null
|
|
268
|
+
? arkruleFrozenFallback
|
|
269
|
+
: arkRulesLoaded
|
|
270
|
+
? 0
|
|
271
|
+
: null,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Build the public status manifest for a project root.
|
|
277
|
+
* @param {Parameters<typeof collectStatusFacts>[0]} [options]
|
|
278
|
+
*/
|
|
279
|
+
export function buildProjectStatusManifest(options = {}) {
|
|
280
|
+
return buildStatusManifest(collectStatusFacts(options));
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* CLI entry: always non-interactive. Prefer JSON for agents; human lines without --json.
|
|
285
|
+
* Exit 0 when identity is not mismatch; exit 1 on mismatch (stale / wrong project).
|
|
286
|
+
* @param {{
|
|
287
|
+
* root?: string,
|
|
288
|
+
* config?: string,
|
|
289
|
+
* json?: boolean,
|
|
290
|
+
* expectedRoot?: string,
|
|
291
|
+
* expectedProjectId?: string,
|
|
292
|
+
* host?: string,
|
|
293
|
+
* arkgateVersion?: string,
|
|
294
|
+
* write?: (line: string) => void,
|
|
295
|
+
* writeErr?: (line: string) => void,
|
|
296
|
+
* }} args
|
|
297
|
+
*/
|
|
298
|
+
export function runStatusCommand(args = {}) {
|
|
299
|
+
const write = args.write ?? ((line) => console.log(line));
|
|
300
|
+
const writeErr = args.writeErr ?? ((line) => console.error(line));
|
|
301
|
+
|
|
302
|
+
// CI / non-TTY: never hang. Status never uses readline.
|
|
303
|
+
const asJson = args.json === true || process.env.CI === '1' || process.env.CI === 'true';
|
|
304
|
+
|
|
305
|
+
try {
|
|
306
|
+
const manifest = buildProjectStatusManifest({
|
|
307
|
+
root: args.root,
|
|
308
|
+
config: args.config,
|
|
309
|
+
expectedRoot: args.expectedRoot,
|
|
310
|
+
expectedProjectId: args.expectedProjectId,
|
|
311
|
+
host: args.host,
|
|
312
|
+
arkgateVersion: args.arkgateVersion,
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
if (asJson || args.json) {
|
|
316
|
+
write(JSON.stringify(manifest, null, 2));
|
|
317
|
+
} else {
|
|
318
|
+
write(`ArkGate status ${manifest.arkgateVersion} — schema ${manifest.schemaVersion}`);
|
|
319
|
+
write(
|
|
320
|
+
` identity: ${manifest.projectIdentity.binding}` +
|
|
321
|
+
(manifest.projectIdentity.authoritative ? ' (authoritative)' : '') +
|
|
322
|
+
(manifest.projectIdentity.projectId
|
|
323
|
+
? ` · ${manifest.projectIdentity.projectId.slice(0, 18)}…`
|
|
324
|
+
: ' · (no project id)')
|
|
325
|
+
);
|
|
326
|
+
write(` root: ${manifest.projectIdentity.resolvedRoot}`);
|
|
327
|
+
write(
|
|
328
|
+
` activation: ${manifest.activation.writePath}` +
|
|
329
|
+
(manifest.activation.host ? ` · host=${manifest.activation.host}` : '')
|
|
330
|
+
);
|
|
331
|
+
write(` ${manifest.activation.honestLabel}`);
|
|
332
|
+
const lc = manifest.lastCheck;
|
|
333
|
+
write(
|
|
334
|
+
` lastCheck: ${lc.verdict ?? 'none'}` +
|
|
335
|
+
(lc.at ? ` @ ${lc.at}` : '') +
|
|
336
|
+
(lc.activeViolations != null ? ` · active=${lc.activeViolations}` : '') +
|
|
337
|
+
(lc.frozenResidual != null ? ` · frozen=${lc.frozenResidual}` : '')
|
|
338
|
+
);
|
|
339
|
+
write(
|
|
340
|
+
` rules: loaded=${manifest.rules.arkRulesLoaded}` +
|
|
341
|
+
(manifest.rules.inventoried != null ? ` · inventoried=${manifest.rules.inventoried}` : '') +
|
|
342
|
+
(manifest.rules.underContract != null
|
|
343
|
+
? ` · underContract=${manifest.rules.underContract}`
|
|
344
|
+
: '') +
|
|
345
|
+
(manifest.rules.frozenResidual != null
|
|
346
|
+
? ` · frozen=${manifest.rules.frozenResidual}`
|
|
347
|
+
: '')
|
|
348
|
+
);
|
|
349
|
+
write(` next: [${manifest.nextAction.id}] ${manifest.nextAction.summary}`);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if (manifest.projectIdentity.binding === 'mismatch') return 1;
|
|
353
|
+
return 0;
|
|
354
|
+
} catch (error) {
|
|
355
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
356
|
+
if (asJson || args.json) {
|
|
357
|
+
write(
|
|
358
|
+
JSON.stringify({
|
|
359
|
+
schemaVersion: '1.0',
|
|
360
|
+
error: message,
|
|
361
|
+
ok: false,
|
|
362
|
+
})
|
|
363
|
+
);
|
|
364
|
+
} else {
|
|
365
|
+
writeErr(message);
|
|
366
|
+
}
|
|
367
|
+
return 2;
|
|
368
|
+
}
|
|
369
|
+
}
|