yam-flow 2.6.0 → 2.7.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 +22 -0
- package/COMMANDS.md +30 -0
- package/README.md +9 -1
- package/ROADMAP.md +3 -1
- package/dist/bin/yam.js +183 -6
- package/dist/lib/media-provider-receipt.d.ts +79 -0
- package/dist/lib/media-provider-receipt.js +498 -0
- package/dist/lib/skill-installation.js +337 -97
- package/dist/lib/verification-closure.d.ts +97 -0
- package/dist/lib/verification-closure.js +728 -0
- package/package.json +3 -2
- package/src/bin/yam.ts +179 -5
- package/src/lib/media-provider-receipt.ts +548 -0
- package/src/lib/skill-installation.ts +462 -95
- package/src/lib/verification-closure.ts +738 -0
- package/yam.manifest.json +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "yam-flow",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.0",
|
|
4
4
|
"description": "Progressive proof-first agent flow: start fast, deepen deliberately, stay honest by design.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "0kim0bos",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"doctor": "node ./dist/bin/yam.js doctor",
|
|
53
53
|
"list": "node ./dist/bin/yam.js list",
|
|
54
54
|
"examples": "node ./dist/bin/yam.js examples",
|
|
55
|
-
"verify:self": "npm run build && node --check ./dist/bin/yam.js && node --check ./dist/lib/trust-kernel.js && node --check ./dist/lib/skill-installation.js && node --check ./dist/lib/external-updates.js && node --check ./dist/lib/gate-result.js && node --check ./dist/lib/benchmark-promotion.js && node --check ./dist/lib/design-production.js && npm run hook-latency-smoke && node ./scripts/external-updates-smoke.mjs && node ./scripts/install-transaction-smoke.mjs && node ./scripts/trust-adoptions-smoke.mjs && node ./scripts/design-production-smoke.mjs && npm run package-secrets:check && npm run package-secrets-smoke && node ./dist/bin/yam.js verify && node ./dist/bin/yam.js doctor",
|
|
55
|
+
"verify:self": "npm run build && node --check ./dist/bin/yam.js && node --check ./dist/lib/trust-kernel.js && node --check ./dist/lib/skill-installation.js && node --check ./dist/lib/external-updates.js && node --check ./dist/lib/gate-result.js && node --check ./dist/lib/benchmark-promotion.js && node --check ./dist/lib/design-production.js && node --check ./dist/lib/verification-closure.js && node --check ./dist/lib/media-provider-receipt.js && npm run hook-latency-smoke && node ./scripts/external-updates-smoke.mjs && node ./scripts/install-transaction-smoke.mjs && node ./scripts/trust-adoptions-smoke.mjs && node ./scripts/design-production-smoke.mjs && node ./scripts/phase-adoptions-smoke.mjs && npm run package-secrets:check && npm run package-secrets-smoke && node ./dist/bin/yam.js verify && node ./dist/bin/yam.js doctor",
|
|
56
56
|
"prepare": "npm run build",
|
|
57
57
|
"prepack": "npm run verify:self",
|
|
58
58
|
"prepublishOnly": "npm run verify:self",
|
|
@@ -69,6 +69,7 @@
|
|
|
69
69
|
"external-updates-smoke": "npm run build && node ./scripts/external-updates-smoke.mjs",
|
|
70
70
|
"trust-adoptions-smoke": "npm run build && node ./scripts/trust-adoptions-smoke.mjs",
|
|
71
71
|
"design-production-smoke": "npm run build && node ./scripts/design-production-smoke.mjs",
|
|
72
|
+
"phase-adoptions-smoke": "npm run build && node ./scripts/phase-adoptions-smoke.mjs",
|
|
72
73
|
"hook-latency-smoke": "node ./scripts/hook-latency-smoke.mjs",
|
|
73
74
|
"install-transaction-smoke": "npm run build && node ./scripts/install-transaction-smoke.mjs",
|
|
74
75
|
"release:check": "npm run typecheck && npm run forbidden-names:check && npm run verify:self && npm run dist:freshness && npm run package-boundary:check && npm run registry:check && npm run cli-smoke",
|
package/src/bin/yam.ts
CHANGED
|
@@ -66,6 +66,14 @@ import {
|
|
|
66
66
|
buildStrictGateResult,
|
|
67
67
|
verifyStrictGateResult
|
|
68
68
|
} from '../lib/gate-result.js';
|
|
69
|
+
import {
|
|
70
|
+
createVerificationClosureReceipt,
|
|
71
|
+
verifyVerificationClosureReceiptFile
|
|
72
|
+
} from '../lib/verification-closure.js';
|
|
73
|
+
import {
|
|
74
|
+
createMediaProviderReceipt,
|
|
75
|
+
verifyMediaProviderReceipt
|
|
76
|
+
} from '../lib/media-provider-receipt.js';
|
|
69
77
|
import {
|
|
70
78
|
BoundedInputError,
|
|
71
79
|
GENERAL_STDIN_MAX_BYTES,
|
|
@@ -215,6 +223,7 @@ Usage:
|
|
|
215
223
|
yam mission queue [--agent-id id] [--scope text] [--changed file] [--verification-hint text] [--json]
|
|
216
224
|
yam mission receipt [--thread-id id] [--role reviewer] [--lifecycle stopped] [--outcome passed] [--evidence text] [--json]
|
|
217
225
|
yam mission gate [--expected-thread id] [--receipt file] [--json]
|
|
226
|
+
yam verification closure <create|verify> [options]
|
|
218
227
|
yam update check [--json]
|
|
219
228
|
yam update apply --component yam|scrapling|insane-search [--json]
|
|
220
229
|
yam update apply --all [--json]
|
|
@@ -1412,6 +1421,8 @@ function buildCapabilityMatrix(readinessRows = [], liteHook: AnyRecord = {}, stu
|
|
|
1412
1421
|
capabilityRow('bounded_design_revisions', 'beta', 'ready', 'maximum two reviewer-finding-backed revision rounds', 'round state does not replace Ueye artifact hash verification'),
|
|
1413
1422
|
capabilityRow('final_gallery_manifest', 'beta', 'ready', 'path-confined hash-verified packaging manifest', 'gallery completeness does not prove visual or license correctness'),
|
|
1414
1423
|
capabilityRow('demand_gated_design_production', 'beta', 'ready', 'pre-recorded operator trigger, Canvas, chronological bounded revisions, gallery, and digest-verifiable phase receipt', 'demand evidence is operator-asserted and overall phase truth remains partial'),
|
|
1424
|
+
capabilityRow('final_scope_verification_closure', 'beta', 'ready', 'explicit Git-scope and selected/executed command receipt with digest verification', 'does not execute checks; unavailable or unplanned Git scope blocks and successful operator evidence remains partial'),
|
|
1425
|
+
capabilityRow('media_provider_execution_receipt', 'beta', 'ready', 'optional demand-gated exact execution boundary plus identity-bound local assets', 'never contacts providers and does not prove visual or implementation correctness'),
|
|
1415
1426
|
capabilityRow('yam_lite_hook', 'beta', String(liteHook.state || readiness.get('yam-lite hook') || 'unknown'), 'optional hook health inspection', 'advisory only; disabled unless explicitly enabled'),
|
|
1416
1427
|
capabilityRow('study_note_hook', 'beta', String(studyNoteHook.state || readiness.get('yam-study-note hook') || 'unknown'), 'optional prompt and bounded Stop check', 'does not generate the Study Note or run verification')
|
|
1417
1428
|
];
|
|
@@ -3158,15 +3169,55 @@ async function readProductionSpecs(value: unknown, label: string) {
|
|
|
3158
3169
|
return specs;
|
|
3159
3170
|
}
|
|
3160
3171
|
|
|
3172
|
+
async function readOptionalSpecs(value: unknown, label: string) {
|
|
3173
|
+
const files = arrayFlag(value);
|
|
3174
|
+
const specs: AnyRecord[] = [];
|
|
3175
|
+
for (const file of files) specs.push(await readBoundedJsonSpec(path.resolve(expandHome(file)), label));
|
|
3176
|
+
return specs;
|
|
3177
|
+
}
|
|
3178
|
+
|
|
3161
3179
|
async function readBoundedJsonSpec(file: string, label: string) {
|
|
3162
|
-
const
|
|
3163
|
-
if (!
|
|
3164
|
-
if (
|
|
3165
|
-
const
|
|
3180
|
+
const before = await fsp.lstat(file);
|
|
3181
|
+
if (before.isSymbolicLink() || !before.isFile()) throw new Error(`${label} must be a regular non-symlink file: ${file}`);
|
|
3182
|
+
if (before.size > 256 * 1024) throw new Error(`${label} exceeds the 256 KiB limit: ${file}`);
|
|
3183
|
+
const noFollow = process.platform !== 'win32' && typeof fs.constants.O_NOFOLLOW === 'number'
|
|
3184
|
+
? fs.constants.O_NOFOLLOW
|
|
3185
|
+
: 0;
|
|
3186
|
+
const handle = await fsp.open(file, fs.constants.O_RDONLY | noFollow);
|
|
3187
|
+
let text = '';
|
|
3188
|
+
try {
|
|
3189
|
+
const opened = await handle.stat();
|
|
3190
|
+
if (!opened.isFile() || !sameSpecFileIdentity(before, opened)) throw new Error(`${label} identity changed before open: ${file}`);
|
|
3191
|
+
if (opened.size > 256 * 1024) throw new Error(`${label} exceeds the 256 KiB limit: ${file}`);
|
|
3192
|
+
const bytes = Buffer.alloc(opened.size);
|
|
3193
|
+
let offset = 0;
|
|
3194
|
+
while (offset < bytes.length) {
|
|
3195
|
+
const { bytesRead } = await handle.read(bytes, offset, bytes.length - offset, offset);
|
|
3196
|
+
if (!bytesRead) throw new Error(`${label} became shorter while reading: ${file}`);
|
|
3197
|
+
offset += bytesRead;
|
|
3198
|
+
}
|
|
3199
|
+
const openedAfter = await handle.stat();
|
|
3200
|
+
const after = await fsp.lstat(file);
|
|
3201
|
+
if (!sameSpecFileIdentity(opened, openedAfter) || openedAfter.size !== opened.size
|
|
3202
|
+
|| after.isSymbolicLink() || !after.isFile() || !sameSpecFileIdentity(opened, after)) {
|
|
3203
|
+
throw new Error(`${label} changed size or identity while reading: ${file}`);
|
|
3204
|
+
}
|
|
3205
|
+
text = bytes.toString('utf8');
|
|
3206
|
+
} finally {
|
|
3207
|
+
await handle.close();
|
|
3208
|
+
}
|
|
3209
|
+
const value = JSON.parse(text);
|
|
3166
3210
|
if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error(`${label} must contain one JSON object: ${file}`);
|
|
3167
3211
|
return value;
|
|
3168
3212
|
}
|
|
3169
3213
|
|
|
3214
|
+
function sameSpecFileIdentity(
|
|
3215
|
+
left: { dev: number | bigint; ino: number | bigint },
|
|
3216
|
+
right: { dev: number | bigint; ino: number | bigint }
|
|
3217
|
+
) {
|
|
3218
|
+
return left.dev === right.dev && left.ino === right.ino;
|
|
3219
|
+
}
|
|
3220
|
+
|
|
3170
3221
|
async function existingProductionPath(root: string, value: string, label: string) {
|
|
3171
3222
|
if (!value || path.isAbsolute(value) || value.includes('\0')) throw new Error(`${label} must be a non-empty project-relative path`);
|
|
3172
3223
|
const realRoot = await fsp.realpath(root);
|
|
@@ -3738,10 +3789,77 @@ function normalizeUeyeComparisonResult(value = '') {
|
|
|
3738
3789
|
return 'not-verified';
|
|
3739
3790
|
}
|
|
3740
3791
|
|
|
3792
|
+
async function verification(args = []) {
|
|
3793
|
+
const surface = String(args[0] || 'help');
|
|
3794
|
+
const action = String(args[1] || 'help');
|
|
3795
|
+
if (surface === 'help' || action === 'help' || surface === '--help' || surface === '-h') return verificationUsage();
|
|
3796
|
+
if (surface !== 'closure') {
|
|
3797
|
+
console.error(`unknown verification command: ${surface}`);
|
|
3798
|
+
return verificationUsage();
|
|
3799
|
+
}
|
|
3800
|
+
const flags = parseSimpleFlags(args.slice(2), new Set([
|
|
3801
|
+
'root', 'receipt-path', 'planned-scope', 'changed-file', 'selected-spec',
|
|
3802
|
+
'executed-spec', 'skipped-spec', 'truncated-spec', 'json'
|
|
3803
|
+
]));
|
|
3804
|
+
const root = path.resolve(expandHome(flags.root || process.cwd()));
|
|
3805
|
+
try {
|
|
3806
|
+
let result: AnyRecord;
|
|
3807
|
+
if (action === 'create') {
|
|
3808
|
+
requireProductionFlags(flags, ['receipt_path', 'planned_scope', 'changed_file', 'selected_spec']);
|
|
3809
|
+
result = await createVerificationClosureReceipt({
|
|
3810
|
+
root,
|
|
3811
|
+
receipt_path: String(flags.receipt_path),
|
|
3812
|
+
planned_scope: String(flags.planned_scope) as 'affected' | 'release',
|
|
3813
|
+
changed_files: arrayFlag(flags.changed_file),
|
|
3814
|
+
selected_commands: await readProductionSpecs(flags.selected_spec, 'selected command spec') as any,
|
|
3815
|
+
executed_commands: await readOptionalSpecs(flags.executed_spec, 'executed command spec') as any,
|
|
3816
|
+
skipped: await readOptionalSpecs(flags.skipped_spec, 'skipped command spec') as any,
|
|
3817
|
+
truncated: await readOptionalSpecs(flags.truncated_spec, 'truncated output spec') as any
|
|
3818
|
+
});
|
|
3819
|
+
} else if (action === 'verify') {
|
|
3820
|
+
requireProductionFlags(flags, ['receipt_path']);
|
|
3821
|
+
result = await verifyVerificationClosureReceiptFile({ root, receipt_path: String(flags.receipt_path) });
|
|
3822
|
+
} else {
|
|
3823
|
+
throw new Error(`unknown verification closure command: ${action}`);
|
|
3824
|
+
}
|
|
3825
|
+
printJsonOrHuman(result, Boolean(flags.json), 'Verification closure');
|
|
3826
|
+
if (result.truth_status === 'blocked') process.exitCode = 1;
|
|
3827
|
+
} catch (error) {
|
|
3828
|
+
const result = {
|
|
3829
|
+
schema: 'yam.verification-closure-error.v1',
|
|
3830
|
+
action,
|
|
3831
|
+
status: 'blocked',
|
|
3832
|
+
reason: summarizeCheckOutput(errorMessage(error)),
|
|
3833
|
+
next_action: 'repair changed-file scope, selected/executed command evidence, skipped/truncated reasons, or receipt integrity before retrying',
|
|
3834
|
+
truth_status: 'blocked'
|
|
3835
|
+
};
|
|
3836
|
+
printJsonOrHuman(result, Boolean(flags.json), 'Verification closure');
|
|
3837
|
+
process.exitCode = 1;
|
|
3838
|
+
}
|
|
3839
|
+
}
|
|
3840
|
+
|
|
3841
|
+
function verificationUsage() {
|
|
3842
|
+
console.log(`yam verification closure
|
|
3843
|
+
|
|
3844
|
+
Explicit, immutable completion receipts. Create records operator-supplied results; it does not execute commands or replace advisory yam detect output.
|
|
3845
|
+
|
|
3846
|
+
Usage:
|
|
3847
|
+
yam verification closure create [--root dir] --receipt-path .yam/verification/closure.json --planned-scope affected|release --changed-file file --selected-spec file [--executed-spec file] [--skipped-spec file] [--truncated-spec file] [--json]
|
|
3848
|
+
yam verification closure verify [--root dir] --receipt-path .yam/verification/closure.json [--json]
|
|
3849
|
+
|
|
3850
|
+
Selected spec: {"id":"typecheck","command":"npm run typecheck","scope":"affected","required":true}
|
|
3851
|
+
Executed spec: {"id":"typecheck","command":"npm run typecheck","exit_code":0,"evidence":"exit 0 observed"}
|
|
3852
|
+
Skipped/truncated spec: {"check_id":"browser","reason":"no UI surface changed"}
|
|
3853
|
+
|
|
3854
|
+
Repeat spec and changed-file flags as needed. Release-sensitive paths promote affected to release and require an executed required release-scope command. Required selected commands cannot be skipped. Digest verification does not upgrade operator-declared execution results beyond partial truth.
|
|
3855
|
+
`);
|
|
3856
|
+
}
|
|
3857
|
+
|
|
3741
3858
|
async function media(args = []) {
|
|
3742
3859
|
const subcommand = args[0] || 'help';
|
|
3743
3860
|
if (subcommand === 'help' || subcommand === '--help' || subcommand === '-h') return mediaUsage();
|
|
3744
3861
|
if (subcommand === 'proof') return mediaProof(args.slice(1));
|
|
3862
|
+
if (subcommand === 'provider') return mediaProvider(args.slice(1));
|
|
3745
3863
|
console.error(`unknown media command: ${subcommand}`);
|
|
3746
3864
|
return mediaUsage();
|
|
3747
3865
|
}
|
|
@@ -3753,9 +3871,63 @@ Opt-in media generation proof helpers. Generated media can support visual direct
|
|
|
3753
3871
|
|
|
3754
3872
|
Usage:
|
|
3755
3873
|
yam media proof [--tool name] [--requested] [--attempted] [--output file] [--wait-loop] [--blocked-reason text] [--json]
|
|
3874
|
+
yam media provider create [--root dir] --receipt-path .yam/media/run.json --demand-kind media_generation|media_transformation --demand-evidence text --provider name --model name --provider-calls n [--provider-execution] [--dry-run] [--submit] --asset-spec file [--json]
|
|
3875
|
+
yam media provider verify [--root dir] --receipt-path .yam/media/run.json [--json]
|
|
3876
|
+
|
|
3877
|
+
Provider receipts are optional and demand-gated. This command hashes local assets and records an operator-asserted boundary; it never contacts a provider. Dry runs require zero calls and no submit. Explicit execution requires at least one call, submit, and a provider-provenance output asset.
|
|
3756
3878
|
`);
|
|
3757
3879
|
}
|
|
3758
3880
|
|
|
3881
|
+
async function mediaProvider(args = []) {
|
|
3882
|
+
const action = String(args[0] || 'help');
|
|
3883
|
+
if (action === 'help' || action === '--help' || action === '-h') return mediaUsage();
|
|
3884
|
+
const flags = parseSimpleFlags(args.slice(1), new Set([
|
|
3885
|
+
'root', 'receipt-path', 'demand-kind', 'demand-evidence', 'provider', 'model',
|
|
3886
|
+
'provider-calls', 'provider-execution', 'dry-run', 'submit', 'asset-spec', 'json'
|
|
3887
|
+
]));
|
|
3888
|
+
const root = path.resolve(expandHome(flags.root || process.cwd()));
|
|
3889
|
+
try {
|
|
3890
|
+
let result: AnyRecord;
|
|
3891
|
+
if (action === 'create') {
|
|
3892
|
+
requireProductionFlags(flags, [
|
|
3893
|
+
'receipt_path', 'demand_kind', 'demand_evidence', 'provider', 'model', 'provider_calls', 'asset_spec'
|
|
3894
|
+
]);
|
|
3895
|
+
result = await createMediaProviderReceipt({
|
|
3896
|
+
root,
|
|
3897
|
+
receipt_path: String(flags.receipt_path),
|
|
3898
|
+
demand_trigger: {
|
|
3899
|
+
kind: String(flags.demand_kind) as 'media_generation' | 'media_transformation',
|
|
3900
|
+
evidence: String(flags.demand_evidence)
|
|
3901
|
+
},
|
|
3902
|
+
provider: { name: String(flags.provider), model: String(flags.model) },
|
|
3903
|
+
provider_calls: Number(flags.provider_calls),
|
|
3904
|
+
provider_execution: Boolean(flags.provider_execution),
|
|
3905
|
+
dry_run: Boolean(flags.dry_run),
|
|
3906
|
+
submit: Boolean(flags.submit),
|
|
3907
|
+
assets: await readProductionSpecs(flags.asset_spec, 'media asset spec') as any
|
|
3908
|
+
});
|
|
3909
|
+
} else if (action === 'verify') {
|
|
3910
|
+
requireProductionFlags(flags, ['receipt_path']);
|
|
3911
|
+
result = await verifyMediaProviderReceipt({ root, receipt_path: String(flags.receipt_path) });
|
|
3912
|
+
} else {
|
|
3913
|
+
throw new Error(`unknown media provider command: ${action}`);
|
|
3914
|
+
}
|
|
3915
|
+
printJsonOrHuman(result, Boolean(flags.json), 'Media provider receipt');
|
|
3916
|
+
if (result.truth_status === 'blocked') process.exitCode = 1;
|
|
3917
|
+
} catch (error) {
|
|
3918
|
+
const result = {
|
|
3919
|
+
schema: 'yam.media-provider-receipt-error.v1',
|
|
3920
|
+
action,
|
|
3921
|
+
status: 'blocked',
|
|
3922
|
+
reason: summarizeCheckOutput(errorMessage(error)),
|
|
3923
|
+
next_action: 'repair the demand, exact execution fields, confined asset provenance, or receipt integrity before retrying',
|
|
3924
|
+
truth_status: 'blocked'
|
|
3925
|
+
};
|
|
3926
|
+
printJsonOrHuman(result, Boolean(flags.json), 'Media provider receipt');
|
|
3927
|
+
process.exitCode = 1;
|
|
3928
|
+
}
|
|
3929
|
+
}
|
|
3930
|
+
|
|
3759
3931
|
async function mediaProof(args = []) {
|
|
3760
3932
|
if (args[0] === 'help' || args[0] === '--help' || args[0] === '-h') return mediaUsage();
|
|
3761
3933
|
const flags = parseSimpleFlags(args, new Set(['tool', 'requested', 'attempted', 'output', 'wait-loop', 'blocked-reason', 'next-action', 'json']));
|
|
@@ -4257,7 +4429,7 @@ function parseSimpleFlags(args = [], allowed = new Set<string>()) {
|
|
|
4257
4429
|
const key = rawKey.slice(2);
|
|
4258
4430
|
if (allowed.size && !allowed.has(key)) continue;
|
|
4259
4431
|
const normalizedKey = key.replace(/-/g, '_');
|
|
4260
|
-
if (['json', 'full-page', 'requested', 'attempted', 'available', 'wait-loop', 'cleanup-checked', 'cleanup-observed', 'left-running-intentionally', 'strict', 'preserved-state', 'states-checked', 'mobile-checked', 'responsive-checked', 'contrast-checked', 'accessibility-checked', 'cta-checked', 'direction-locked', 'reference-read', 'invented-metric', 'placeholder-copy', 'generic-visual', 'operator-provided', 'do-not-replace', 'allowed-for-edit', 'replace'].includes(key)) {
|
|
4432
|
+
if (['json', 'full-page', 'requested', 'attempted', 'available', 'wait-loop', 'cleanup-checked', 'cleanup-observed', 'left-running-intentionally', 'strict', 'preserved-state', 'states-checked', 'mobile-checked', 'responsive-checked', 'contrast-checked', 'accessibility-checked', 'cta-checked', 'direction-locked', 'reference-read', 'invented-metric', 'placeholder-copy', 'generic-visual', 'operator-provided', 'do-not-replace', 'allowed-for-edit', 'replace', 'provider-execution', 'dry-run', 'submit'].includes(key)) {
|
|
4261
4433
|
flags[normalizedKey] = true;
|
|
4262
4434
|
continue;
|
|
4263
4435
|
}
|
|
@@ -5812,6 +5984,7 @@ function showRequestedHelp(args: string[]) {
|
|
|
5812
5984
|
loop: loopUsage,
|
|
5813
5985
|
ueye: ueyeUsage,
|
|
5814
5986
|
media: mediaUsage,
|
|
5987
|
+
verification: verificationUsage,
|
|
5815
5988
|
runtime: runtimeUsage,
|
|
5816
5989
|
mission: missionUsage,
|
|
5817
5990
|
update: updateUsage,
|
|
@@ -5842,6 +6015,7 @@ async function main() {
|
|
|
5842
6015
|
if (command === 'loop') return loop(process.argv.slice(3));
|
|
5843
6016
|
if (command === 'ueye') return ueye(process.argv.slice(3));
|
|
5844
6017
|
if (command === 'media') return media(process.argv.slice(3));
|
|
6018
|
+
if (command === 'verification') return verification(process.argv.slice(3));
|
|
5845
6019
|
if (command === 'runtime') return runtime(process.argv.slice(3));
|
|
5846
6020
|
if (command === 'mission') return mission(process.argv.slice(3));
|
|
5847
6021
|
if (command === 'update') return updateCommand(process.argv.slice(3));
|