@myelinbridge/cli 0.10.0 → 0.11.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/README.md +27 -0
- package/bin/myelin.js +73 -1
- package/package.json +23 -23
package/README.md
CHANGED
|
@@ -13,6 +13,8 @@ npx @myelinbridge/cli ping # verifies auth, prints your projects
|
|
|
13
13
|
npx @myelinbridge/cli datasets # what you can deliver to, and whose move it is
|
|
14
14
|
npx @myelinbridge/cli contract --dataset onco1-wes # what is expected of your delivery
|
|
15
15
|
npx @myelinbridge/cli sample-depth 1 --dataset onco1-wes # once, before your first submit
|
|
16
|
+
npx @myelinbridge/cli roles --dataset onco1-wes # which file plays which role
|
|
17
|
+
npx @myelinbridge/cli roles set samplesheet /metadata/samplesheet.csv --dataset onco1-wes
|
|
16
18
|
npx @myelinbridge/cli check ./run_042 --dataset onco1-wes # validate BEFORE uploading a byte
|
|
17
19
|
npx @myelinbridge/cli push ./run_042 --dataset onco1-wes --submit
|
|
18
20
|
```
|
|
@@ -27,6 +29,31 @@ npx @myelinbridge/cli push ./run_042 --dataset onco1-wes --submit
|
|
|
27
29
|
deliveries — after that, re-asserting the current value still succeeds and only a
|
|
28
30
|
*change* exits `2`. This is the only dataset field you can write.
|
|
29
31
|
|
|
32
|
+
- **Declare which file plays which role — whenever you like (0.11.0).** Quality
|
|
33
|
+
checks read files by ROLE (`samplesheet`, `checksum_manifest`, `qc_report`,
|
|
34
|
+
`subject_roster`, `capture_bed`), not by a filing convention you never agreed
|
|
35
|
+
to. `roles` shows what is declared and what Myelin detected in your
|
|
36
|
+
deliveries; `roles set <role> <path>` confirms a file; `roles none <role>`
|
|
37
|
+
states your dataset has no such file (a statement, not a gap). Nothing is
|
|
38
|
+
ever required to deliver — an undeclared role just means the checks that read
|
|
39
|
+
it report *"could not check"* instead of running, so declaring is how you
|
|
40
|
+
activate more checks before upload and catch problems before review does.
|
|
41
|
+
Unlike `sample-depth` it **never locks**, and it is idempotent, so a pipeline
|
|
42
|
+
can assert it on every run. The samplesheet is **sticky**: send it once and
|
|
43
|
+
every later delivery reconciles against it — a partial delivery (93 of 96) is
|
|
44
|
+
reported as a state, never as a failure.
|
|
45
|
+
|
|
46
|
+
- **A missing path in `roles set` is refused, not guessed (0.11.1).** Flags are
|
|
47
|
+
not positional arguments: `roles set samplesheet --dataset onco1-wes` (path
|
|
48
|
+
omitted) used to declare the samplesheet at the literal path `--dataset` and
|
|
49
|
+
print a tick. Every samplesheet check then reported *"could not check — the
|
|
50
|
+
declared file is not in the delivery"* on a delivery that was fine. The
|
|
51
|
+
command now exits with its usage line instead.
|
|
52
|
+
|
|
53
|
+
- **`--dataset` takes the slug in any case (0.11.0).** Slugs are matched
|
|
54
|
+
case-insensitively (`onco1-wes` and `ONCO1-WES` name the same dataset); an id
|
|
55
|
+
or an exact name works too.
|
|
56
|
+
|
|
30
57
|
- **Resume = re-run.** `push` is idempotent: already-uploaded files are skipped
|
|
31
58
|
(path + size), and within a large file, parts that already landed are skipped
|
|
32
59
|
too (S3 multipart). Uploads go direct to storage over short-lived presigned
|
package/bin/myelin.js
CHANGED
|
@@ -191,9 +191,15 @@ async function resolveDataset(ref) {
|
|
|
191
191
|
die(`Dataset ${ref} not found in this key's scope`)
|
|
192
192
|
}
|
|
193
193
|
const me = expectOk(await api('GET', '/me'), 'auth')
|
|
194
|
+
// Slugs are matched case-insensitively: seeded datasets carry lowercase slugs
|
|
195
|
+
// while the portal generates uppercase ones, and the slug is a handle a human
|
|
196
|
+
// types — the case of the fleet should never be their problem.
|
|
197
|
+
const want = ref.toLowerCase()
|
|
194
198
|
for (const p of me.projects) {
|
|
195
199
|
const r = expectOk(await api('GET', `/projects/${p.id}/datasets`), 'datasets')
|
|
196
|
-
const hit = r.datasets.find(
|
|
200
|
+
const hit = r.datasets.find(
|
|
201
|
+
(d) => (typeof d.slug === 'string' && d.slug.toLowerCase() === want) || d.name === ref,
|
|
202
|
+
)
|
|
197
203
|
if (hit) return hit
|
|
198
204
|
}
|
|
199
205
|
die(`No dataset with slug or name "${ref}" in this key's scope`)
|
|
@@ -896,6 +902,69 @@ async function cmdSampleDepth() {
|
|
|
896
902
|
}
|
|
897
903
|
}
|
|
898
904
|
|
|
905
|
+
// Which delivered file plays each role (sprint 19). Checks read files by ROLE
|
|
906
|
+
// — declaring one activates every check that was waiting on it, and nothing
|
|
907
|
+
// here is ever required to deliver. Unlike sample-depth this never locks:
|
|
908
|
+
// declarations apply forward, and re-declaring is allowed for the life of the
|
|
909
|
+
// dataset (idempotent — assert it on every run).
|
|
910
|
+
async function cmdRoles() {
|
|
911
|
+
const dsRef = opt('dataset')
|
|
912
|
+
if (!dsRef) die('Usage: myelin roles [set <role> <path> | none <role> | clear <role>] --dataset <slug|id>')
|
|
913
|
+
const dataset = await resolveDataset(dsRef)
|
|
914
|
+
const sub = args[1]
|
|
915
|
+
|
|
916
|
+
if (sub === 'set' || sub === 'none' || sub === 'clear') {
|
|
917
|
+
const role = args[2]
|
|
918
|
+
if (!role) die(`Usage: myelin roles ${sub} <role>${sub === 'set' ? ' <path>' : ''} --dataset <slug|id>`)
|
|
919
|
+
const body = { role }
|
|
920
|
+
if (sub === 'set') {
|
|
921
|
+
const path = args[3]
|
|
922
|
+
// ⚠ `args` only strips --json, so an omitted path lets the NEXT FLAG slide
|
|
923
|
+
// into its place: `roles set samplesheet --dataset onco1-wes` used to
|
|
924
|
+
// declare the samplesheet at `/--dataset`. The API accepts it (printable,
|
|
925
|
+
// under 255 chars), the CLI printed a tick, and every samplesheet check
|
|
926
|
+
// then answered role_file_missing on a delivery that was fine.
|
|
927
|
+
if (!path || path.startsWith('--')) {
|
|
928
|
+
die('Usage: myelin roles set <role> <path> --dataset <slug|id>')
|
|
929
|
+
}
|
|
930
|
+
body.path = path
|
|
931
|
+
} else if (sub === 'none') {
|
|
932
|
+
body.none = true
|
|
933
|
+
} else {
|
|
934
|
+
body.clear = true
|
|
935
|
+
}
|
|
936
|
+
const j = expectOk(await api('PUT', `/datasets/${dataset.id}/file-roles`, body), 'file-roles')
|
|
937
|
+
emit(j)
|
|
938
|
+
if (!JSON_MODE) {
|
|
939
|
+
out(
|
|
940
|
+
j.changed
|
|
941
|
+
? `✓ ${role} ${sub === 'set' ? `→ ${body.path}` : sub === 'none' ? 'declared: no such file in this dataset' : 'declaration cleared'} for ${dataset.name}.`
|
|
942
|
+
: `${role} already declared that way for ${dataset.name} — nothing to change.`,
|
|
943
|
+
)
|
|
944
|
+
}
|
|
945
|
+
return
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
const j = expectOk(await api('GET', `/datasets/${dataset.id}/file-roles`), 'file-roles')
|
|
949
|
+
emit(j)
|
|
950
|
+
if (JSON_MODE) return
|
|
951
|
+
out(`${dataset.name} — delivery file roles`)
|
|
952
|
+
const declaredBy = new Map(j.declarations.map((d) => [d.role, d]))
|
|
953
|
+
for (const role of ['samplesheet', 'checksum_manifest', 'qc_report', 'subject_roster', 'capture_bed']) {
|
|
954
|
+
const d = declaredBy.get(role)
|
|
955
|
+
if (d) {
|
|
956
|
+
out(` ${role.padEnd(18)} ${d.declared_none ? '(no such file in this dataset — declared)' : d.path}`)
|
|
957
|
+
} else {
|
|
958
|
+
const cands = j.candidates.filter((c) => c.role === role)
|
|
959
|
+
out(` ${role.padEnd(18)} not declared${cands.length ? ` — looks like: ${cands.map((c) => c.path).join(', ')}` : ''}`)
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
const undeclared = 5 - j.declarations.length
|
|
963
|
+
if (undeclared > 0) {
|
|
964
|
+
out(`\nDeclaring a role activates the checks that read it — run: myelin roles set <role> <path> --dataset ${dsRef}`)
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
|
|
899
968
|
function cmdHelp() {
|
|
900
969
|
console.log(`myelin — Partner Ingestion CLI
|
|
901
970
|
|
|
@@ -910,6 +979,8 @@ Commands:
|
|
|
910
979
|
projects list scoped projects
|
|
911
980
|
datasets list datasets with "your move" hints
|
|
912
981
|
sample-depth <0-5> --dataset <slug|id> declare the folder depth a sample sits at (locks after 1st submit)
|
|
982
|
+
roles [set|none|clear …] --dataset <…> which file plays each role (samplesheet, checksum list, …) —
|
|
983
|
+
declaring one activates the checks that read it; never locks
|
|
913
984
|
contract --dataset <slug|id> what this dataset expects of your delivery
|
|
914
985
|
check <dir> --dataset <slug|id> preflight local files against quality rules (no upload)
|
|
915
986
|
push <dir> --dataset <slug|id> create/resume a delivery and upload (resumable; re-run to resume)
|
|
@@ -1008,6 +1079,7 @@ const commands = {
|
|
|
1008
1079
|
projects: cmdProjects,
|
|
1009
1080
|
datasets: cmdDatasets,
|
|
1010
1081
|
'sample-depth': cmdSampleDepth,
|
|
1082
|
+
roles: cmdRoles,
|
|
1011
1083
|
contract: cmdContract,
|
|
1012
1084
|
check: cmdCheck,
|
|
1013
1085
|
push: cmdPush,
|
package/package.json
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@myelinbridge/cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Myelin Partner Ingestion CLI — push R&D data deliveries from a pipeline: preflight against the client's quality rules, resumable upload, submit, track review outcomes.",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"bin": {
|
|
7
|
-
"myelin": "bin/myelin.js"
|
|
8
|
-
},
|
|
9
|
-
"files": [
|
|
10
|
-
"bin/",
|
|
11
|
-
"README.md"
|
|
12
|
-
],
|
|
13
|
-
"engines": {
|
|
14
|
-
"node": ">=20"
|
|
15
|
-
},
|
|
16
|
-
"keywords": [
|
|
17
|
-
"myelin",
|
|
18
|
-
"pharma",
|
|
19
|
-
"ingestion",
|
|
20
|
-
"s3"
|
|
21
|
-
],
|
|
22
|
-
"license": "UNLICENSED"
|
|
23
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@myelinbridge/cli",
|
|
3
|
+
"version": "0.11.1",
|
|
4
|
+
"description": "Myelin Partner Ingestion CLI — push R&D data deliveries from a pipeline: preflight against the client's quality rules, resumable upload, submit, track review outcomes.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"myelin": "bin/myelin.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin/",
|
|
11
|
+
"README.md"
|
|
12
|
+
],
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": ">=20"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"myelin",
|
|
18
|
+
"pharma",
|
|
19
|
+
"ingestion",
|
|
20
|
+
"s3"
|
|
21
|
+
],
|
|
22
|
+
"license": "UNLICENSED"
|
|
23
|
+
}
|