@prestack/thread 0.0.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/CHANGELOG.md +192 -0
- package/dist/attach.d.ts +1 -0
- package/dist/attach.js +1 -0
- package/dist/core/actor.d.ts +12 -0
- package/dist/core/actor.js +8 -0
- package/dist/core/asking.d.ts +180 -0
- package/dist/core/asking.js +1109 -0
- package/dist/core/attach.d.ts +54 -0
- package/dist/core/attach.js +510 -0
- package/dist/core/chain.d.ts +16 -0
- package/dist/core/chain.js +114 -0
- package/dist/core/cli.d.ts +2 -0
- package/dist/core/cli.js +1682 -0
- package/dist/core/condition.d.ts +52 -0
- package/dist/core/condition.js +198 -0
- package/dist/core/descend.d.ts +81 -0
- package/dist/core/descend.js +879 -0
- package/dist/core/finding.d.ts +12 -0
- package/dist/core/finding.js +23 -0
- package/dist/core/generators/meta.d.ts +2 -0
- package/dist/core/generators/meta.js +823 -0
- package/dist/core/init.d.ts +15 -0
- package/dist/core/init.js +134 -0
- package/dist/core/invoked.d.ts +9 -0
- package/dist/core/invoked.js +41 -0
- package/dist/core/round.d.ts +1 -0
- package/dist/core/round.js +57 -0
- package/dist/core/rows.d.ts +26 -0
- package/dist/core/rows.js +354 -0
- package/dist/core/scope.d.ts +6 -0
- package/dist/core/scope.js +12 -0
- package/dist/core/thread.d.ts +26 -0
- package/dist/core/thread.js +5 -0
- package/dist/core/tree.d.ts +12 -0
- package/dist/core/tree.js +56 -0
- package/dist/core/types.d.ts +49 -0
- package/dist/core/types.js +311 -0
- package/dist/derivation/attach.d.ts +2 -0
- package/dist/derivation/attach.js +61 -0
- package/dist/derivation/generators/build.d.ts +16 -0
- package/dist/derivation/generators/build.js +217 -0
- package/dist/derivation/generators/check.d.ts +18 -0
- package/dist/derivation/generators/check.js +78 -0
- package/dist/derivation/generators/derivation.d.ts +3 -0
- package/dist/derivation/generators/derivation.js +273 -0
- package/dist/derivation/generators/determinism.d.ts +20 -0
- package/dist/derivation/generators/determinism.js +110 -0
- package/dist/derivation/generators/release.d.ts +3 -0
- package/dist/derivation/generators/release.js +183 -0
- package/dist/derivation/makers/release.d.ts +1 -0
- package/dist/derivation/makers/release.js +107 -0
- package/dist/derivation/release.d.ts +10 -0
- package/dist/derivation/release.js +12 -0
- package/dist/integrations/attach.d.ts +6 -0
- package/dist/integrations/attach.js +177 -0
- package/dist/integrations/generators/read.d.ts +1 -0
- package/dist/integrations/generators/read.js +92 -0
- package/dist/integrations/generators/sdk.d.ts +1 -0
- package/dist/integrations/generators/sdk.js +148 -0
- package/dist/integrations/generators/secret.d.ts +1 -0
- package/dist/integrations/generators/secret.js +233 -0
- package/dist/integrations/generators/target.d.ts +1 -0
- package/dist/integrations/generators/target.js +765 -0
- package/dist/integrations/generators/vault.d.ts +1 -0
- package/dist/integrations/generators/vault.js +164 -0
- package/dist/integrations/sdk.d.ts +6 -0
- package/dist/integrations/sdk.js +151 -0
- package/dist/integrations/secret.d.ts +16 -0
- package/dist/integrations/secret.js +30 -0
- package/dist/integrations/target.d.ts +49 -0
- package/dist/integrations/target.js +225 -0
- package/dist/integrations/vault.d.ts +12 -0
- package/dist/integrations/vault.js +104 -0
- package/dist/integrity/attach.d.ts +2 -0
- package/dist/integrity/attach.js +213 -0
- package/dist/integrity/check-threads.d.ts +10 -0
- package/dist/integrity/check-threads.js +153 -0
- package/dist/integrity/constraint.d.ts +16 -0
- package/dist/integrity/constraint.js +1105 -0
- package/dist/integrity/generators/compiler.d.ts +1 -0
- package/dist/integrity/generators/compiler.js +130 -0
- package/dist/integrity/generators/gate.d.ts +1 -0
- package/dist/integrity/generators/gate.js +172 -0
- package/dist/integrity/generators/issues.d.ts +1 -0
- package/dist/integrity/generators/issues.js +100 -0
- package/dist/integrity/generators/publish.d.ts +1 -0
- package/dist/integrity/generators/publish.js +186 -0
- package/dist/integrity/generators/rule.d.ts +2 -0
- package/dist/integrity/generators/rule.js +213 -0
- package/dist/integrity/generators/seal.d.ts +4 -0
- package/dist/integrity/generators/seal.js +101 -0
- package/dist/integrity/generators/trial.d.ts +39 -0
- package/dist/integrity/generators/trial.js +254 -0
- package/dist/integrity/lint.d.ts +50 -0
- package/dist/integrity/lint.js +161 -0
- package/dist/integrity/lsp/bin.d.ts +2 -0
- package/dist/integrity/lsp/bin.js +35 -0
- package/dist/integrity/lsp/diagnostics.d.ts +25 -0
- package/dist/integrity/lsp/diagnostics.js +57 -0
- package/dist/integrity/lsp/findings.d.ts +20 -0
- package/dist/integrity/lsp/findings.js +39 -0
- package/dist/integrity/lsp/lint.d.ts +7 -0
- package/dist/integrity/lsp/lint.js +95 -0
- package/dist/integrity/lsp/protocol.d.ts +20 -0
- package/dist/integrity/lsp/protocol.js +57 -0
- package/dist/integrity/lsp/server.d.ts +15 -0
- package/dist/integrity/lsp/server.js +171 -0
- package/dist/integrity/sealed.d.ts +1 -0
- package/dist/integrity/sealed.js +56 -0
- package/dist/integrity/slopnator.d.ts +90 -0
- package/dist/integrity/slopnator.js +267 -0
- package/dist/integrity/slopnators/sl_broken_lineage.d.ts +2 -0
- package/dist/integrity/slopnators/sl_broken_lineage.js +35 -0
- package/dist/integrity/slopnators/sl_declared_elsewhere.d.ts +2 -0
- package/dist/integrity/slopnators/sl_declared_elsewhere.js +33 -0
- package/dist/integrity/slopnators/sl_derived_drift.d.ts +8 -0
- package/dist/integrity/slopnators/sl_derived_drift.js +91 -0
- package/dist/integrity/slopnators/sl_hardcoded_style.d.ts +2 -0
- package/dist/integrity/slopnators/sl_hardcoded_style.js +46 -0
- package/dist/integrity/slopnators/sl_inert_island.d.ts +2 -0
- package/dist/integrity/slopnators/sl_inert_island.js +85 -0
- package/dist/integrity/slopnators/sl_inert_slopnator.d.ts +2 -0
- package/dist/integrity/slopnators/sl_inert_slopnator.js +76 -0
- package/dist/integrity/slopnators/sl_missing_row.d.ts +2 -0
- package/dist/integrity/slopnators/sl_missing_row.js +180 -0
- package/dist/integrity/slopnators/sl_no_barrel.d.ts +2 -0
- package/dist/integrity/slopnators/sl_no_barrel.js +41 -0
- package/dist/integrity/slopnators/sl_no_child_process.d.ts +2 -0
- package/dist/integrity/slopnators/sl_no_child_process.js +61 -0
- package/dist/integrity/slopnators/sl_no_comment.d.ts +2 -0
- package/dist/integrity/slopnators/sl_no_comment.js +41 -0
- package/dist/integrity/slopnators/sl_no_dispatch_bypass.d.ts +2 -0
- package/dist/integrity/slopnators/sl_no_dispatch_bypass.js +301 -0
- package/dist/integrity/slopnators/sl_no_import_cycle.d.ts +2 -0
- package/dist/integrity/slopnators/sl_no_import_cycle.js +42 -0
- package/dist/integrity/slopnators/sl_no_such_command.d.ts +2 -0
- package/dist/integrity/slopnators/sl_no_such_command.js +55 -0
- package/dist/integrity/slopnators/sl_only_takes_a_literal.d.ts +2 -0
- package/dist/integrity/slopnators/sl_only_takes_a_literal.js +59 -0
- package/dist/integrity/slopnators/sl_secret_literal.d.ts +2 -0
- package/dist/integrity/slopnators/sl_secret_literal.js +89 -0
- package/dist/integrity/slopnators/sl_stale_path.d.ts +2 -0
- package/dist/integrity/slopnators/sl_stale_path.js +92 -0
- package/dist/integrity/slopnators/sl_twice_meant.d.ts +2 -0
- package/dist/integrity/slopnators/sl_twice_meant.js +30 -0
- package/dist/integrity/slopnators/sl_twice_parented.d.ts +2 -0
- package/dist/integrity/slopnators/sl_twice_parented.js +34 -0
- package/dist/integrity/slopnators/sl_undeclared_crossing.d.ts +2 -0
- package/dist/integrity/slopnators/sl_undeclared_crossing.js +78 -0
- package/dist/integrity/slopnators/sl_undeclared_destination.d.ts +2 -0
- package/dist/integrity/slopnators/sl_undeclared_destination.js +56 -0
- package/dist/integrity/slopnators/sl_undeclared_element.d.ts +2 -0
- package/dist/integrity/slopnators/sl_undeclared_element.js +34 -0
- package/dist/integrity/slopnators/sl_undeclared_node.d.ts +2 -0
- package/dist/integrity/slopnators/sl_undeclared_node.js +59 -0
- package/dist/integrity/slopnators/sl_undeclared_operation.d.ts +2 -0
- package/dist/integrity/slopnators/sl_undeclared_operation.js +120 -0
- package/dist/integrity/slopnators/sl_undeclared_variant.d.ts +2 -0
- package/dist/integrity/slopnators/sl_undeclared_variant.js +117 -0
- package/dist/integrity/slopnators/sl_undescribed.d.ts +2 -0
- package/dist/integrity/slopnators/sl_undescribed.js +54 -0
- package/dist/integrity/slopnators/sl_ungoverned_island.d.ts +2 -0
- package/dist/integrity/slopnators/sl_ungoverned_island.js +58 -0
- package/dist/integrity/slopnators/sl_unkept_rows.d.ts +2 -0
- package/dist/integrity/slopnators/sl_unkept_rows.js +104 -0
- package/dist/integrity/slopnators/sl_unnamed_island.d.ts +2 -0
- package/dist/integrity/slopnators/sl_unnamed_island.js +37 -0
- package/dist/integrity/slopnators/sl_unpaired_member.d.ts +2 -0
- package/dist/integrity/slopnators/sl_unpaired_member.js +114 -0
- package/dist/integrity/slopnators/sl_unpaired_slopnator.d.ts +2 -0
- package/dist/integrity/slopnators/sl_unpaired_slopnator.js +60 -0
- package/dist/integrity/slopnators/sl_unprefixed_member.d.ts +2 -0
- package/dist/integrity/slopnators/sl_unprefixed_member.js +126 -0
- package/dist/integrity/slopnators/sl_unseamed_reach.d.ts +2 -0
- package/dist/integrity/slopnators/sl_unseamed_reach.js +73 -0
- package/dist/integrity/slopnators/sl_unthreaded.d.ts +56 -0
- package/dist/integrity/slopnators/sl_unthreaded.js +352 -0
- package/dist/integrity/slopnators/sl_untranslated_key.d.ts +2 -0
- package/dist/integrity/slopnators/sl_untranslated_key.js +157 -0
- package/dist/integrity/slopnators/sl_unversioned.d.ts +2 -0
- package/dist/integrity/slopnators/sl_unversioned.js +31 -0
- package/dist/integrity/sweep.d.ts +24 -0
- package/dist/integrity/sweep.js +264 -0
- package/dist/knowledge/attach.d.ts +2 -0
- package/dist/knowledge/attach.js +339 -0
- package/dist/knowledge/generators/docsgen.d.ts +1 -0
- package/dist/knowledge/generators/docsgen.js +145 -0
- package/dist/knowledge/generators/glossary.d.ts +1 -0
- package/dist/knowledge/generators/glossary.js +56 -0
- package/dist/knowledge/generators/skills.d.ts +1 -0
- package/dist/knowledge/generators/skills.js +222 -0
- package/dist/knowledge/glossary.d.ts +6 -0
- package/dist/knowledge/glossary.js +1 -0
- package/dist/operations/access.d.ts +27 -0
- package/dist/operations/access.js +61 -0
- package/dist/operations/attach.d.ts +6 -0
- package/dist/operations/attach.js +414 -0
- package/dist/operations/capabilities.d.ts +37 -0
- package/dist/operations/capabilities.js +35 -0
- package/dist/operations/cost.d.ts +14 -0
- package/dist/operations/cost.js +37 -0
- package/dist/operations/dispatch.d.ts +14 -0
- package/dist/operations/dispatch.js +327 -0
- package/dist/operations/error-results.d.ts +4 -0
- package/dist/operations/error-results.js +25 -0
- package/dist/operations/errors/catalog.d.ts +58 -0
- package/dist/operations/errors/catalog.js +91 -0
- package/dist/operations/errors/classes.d.ts +73 -0
- package/dist/operations/errors/classes.js +22 -0
- package/dist/operations/errors/errors.d.ts +52 -0
- package/dist/operations/errors/errors.js +257 -0
- package/dist/operations/generators/capability.d.ts +5 -0
- package/dist/operations/generators/capability.js +603 -0
- package/dist/operations/generators/compile.d.ts +2 -0
- package/dist/operations/generators/compile.js +541 -0
- package/dist/operations/generators/grant.d.ts +1 -0
- package/dist/operations/generators/grant.js +267 -0
- package/dist/operations/generators/operation.d.ts +1 -0
- package/dist/operations/generators/operation.js +811 -0
- package/dist/operations/generators/permits.d.ts +1 -0
- package/dist/operations/generators/permits.js +196 -0
- package/dist/operations/generators/seed.d.ts +20 -0
- package/dist/operations/generators/seed.js +285 -0
- package/dist/operations/generators/trial.d.ts +53 -0
- package/dist/operations/generators/trial.js +376 -0
- package/dist/operations/http.d.ts +30 -0
- package/dist/operations/http.js +121 -0
- package/dist/operations/makers/capability.d.ts +1 -0
- package/dist/operations/makers/capability.js +117 -0
- package/dist/operations/makers/trial.d.ts +1 -0
- package/dist/operations/makers/trial.js +413 -0
- package/dist/operations/memory.d.ts +104 -0
- package/dist/operations/memory.js +365 -0
- package/dist/operations/metering.d.ts +55 -0
- package/dist/operations/metering.js +247 -0
- package/dist/operations/nodes.d.ts +35 -0
- package/dist/operations/nodes.js +15 -0
- package/dist/operations/operations.d.ts +18 -0
- package/dist/operations/operations.js +5 -0
- package/dist/operations/queue-policy.d.ts +3 -0
- package/dist/operations/queue-policy.js +8 -0
- package/dist/operations/result.d.ts +19 -0
- package/dist/operations/result.js +10 -0
- package/dist/operations/seams.d.ts +239 -0
- package/dist/operations/seams.js +45 -0
- package/dist/operations/store.d.ts +14 -0
- package/dist/operations/store.js +1 -0
- package/package.json +16 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { stdout } from 'node:process';
|
|
2
|
+
import { exit } from "../../core/invoked.js";
|
|
3
|
+
import { wordsIn } from "../../core/asking.js";
|
|
4
|
+
import { descend } from "../../core/descend.js";
|
|
5
|
+
const words = wordsIn([]);
|
|
6
|
+
const looking = words.join(' ').trim().toLowerCase();
|
|
7
|
+
const pass = descend();
|
|
8
|
+
const said = (one) => (one.describes ?? '').replace(/\s+/g, ' ');
|
|
9
|
+
const carries = (one, list, file) => {
|
|
10
|
+
if (looking === '')
|
|
11
|
+
return true;
|
|
12
|
+
const held = [one.id ?? '', one.at ?? '', said(one), one.by ?? '', one.runs ?? '', list, file, ...(one.requires ?? []), ...(one.takes ?? [])];
|
|
13
|
+
return held.some((each) => each.toLowerCase().includes(looking));
|
|
14
|
+
};
|
|
15
|
+
const shown = (one, list, pad, alone = false) => {
|
|
16
|
+
const marks = [
|
|
17
|
+
...(one.by === undefined ? [] : [`written by \`${one.by}\``]),
|
|
18
|
+
...(one.runs === undefined ? [] : [`checked by \`${one.runs}\``]),
|
|
19
|
+
...((one.requires ?? []).length === 0 ? [] : [`requires ${(one.requires ?? []).join(', ')}`]),
|
|
20
|
+
...((one.takes ?? []).length === 0 ? [] : [`takes ${(one.takes ?? []).join(', ')}`]),
|
|
21
|
+
];
|
|
22
|
+
const sentence = said(one);
|
|
23
|
+
return [
|
|
24
|
+
`${pad}${one.id ?? '(unnamed)'} [${list}]`,
|
|
25
|
+
...(marks.length === 0 ? [] : [`${pad} ${marks.join(' · ')}`]),
|
|
26
|
+
...(sentence === ''
|
|
27
|
+
? []
|
|
28
|
+
: [`${pad} ${alone || sentence.length <= 160 ? sentence : `${sentence.slice(0, 157)}...`}`]),
|
|
29
|
+
];
|
|
30
|
+
};
|
|
31
|
+
const files = [...pass.threaded.keys()].sort();
|
|
32
|
+
const matched = files.reduce((sum, file) => sum + (pass.threaded.get(file) ?? []).filter((each) => carries(each.row, each.list, file)).length, 0) +
|
|
33
|
+
[...pass.stated.entries()].filter(([, one]) => !files.some((file) => (pass.threaded.get(file) ?? []).some((each) => each.row === one)) && carries(one, 'stated', one.at ?? '')).length;
|
|
34
|
+
const only = matched === 1;
|
|
35
|
+
let rows = 0;
|
|
36
|
+
const out = [];
|
|
37
|
+
if (looking === '') {
|
|
38
|
+
for (const file of files) {
|
|
39
|
+
const held = pass.threaded.get(file) ?? [];
|
|
40
|
+
if (held.length === 0)
|
|
41
|
+
continue;
|
|
42
|
+
rows += held.length;
|
|
43
|
+
out.push(` ${String(held.length).padStart(4)} ${file}`);
|
|
44
|
+
}
|
|
45
|
+
out.unshift(`${String(files.length)} sheet(s) in this tree, and what each holds:`, '');
|
|
46
|
+
out.push('', 'Name a word to read the rows themselves — every row whose id, sentence, path, command or', 'requirement carries it, each with what it means:', '', ' npx thread inspect element', ' npx thread inspect variant', ' npx thread inspect <any word above, or any word this tree coins>', '');
|
|
47
|
+
stdout.write(`${out.join('\n')}\n`);
|
|
48
|
+
stdout.write(`read: ${String(rows)} row(s) across ${String(pass.declarations)} declaration(s), ` +
|
|
49
|
+
`and ${String(pass.findings.length)} finding(s) the gate would report.\n`);
|
|
50
|
+
exit(0);
|
|
51
|
+
}
|
|
52
|
+
for (const file of files) {
|
|
53
|
+
const held = (pass.threaded.get(file) ?? []).filter((each) => carries(each.row, each.list, file));
|
|
54
|
+
if (held.length === 0)
|
|
55
|
+
continue;
|
|
56
|
+
out.push(file);
|
|
57
|
+
for (const each of held) {
|
|
58
|
+
rows += 1;
|
|
59
|
+
out.push(...shown(each.row, each.list, ' ', only));
|
|
60
|
+
}
|
|
61
|
+
out.push('');
|
|
62
|
+
}
|
|
63
|
+
const loose = [...pass.stated.entries()]
|
|
64
|
+
.filter(([, one]) => !files.some((file) => (pass.threaded.get(file) ?? []).some((each) => each.row === one)))
|
|
65
|
+
.filter(([, one]) => carries(one, 'stated', one.at ?? ''));
|
|
66
|
+
if (loose.length > 0) {
|
|
67
|
+
out.push('stated outright, in no parent\'s list');
|
|
68
|
+
for (const [, one] of loose) {
|
|
69
|
+
rows += 1;
|
|
70
|
+
out.push(...shown(one, 'stated', ' ', only));
|
|
71
|
+
}
|
|
72
|
+
out.push('');
|
|
73
|
+
}
|
|
74
|
+
if (looking !== '') {
|
|
75
|
+
const coined = [...pass.coined.entries()].filter(([word]) => word.toLowerCase().includes(looking));
|
|
76
|
+
if (coined.length > 0) {
|
|
77
|
+
out.push('words this tree coins');
|
|
78
|
+
for (const [word, held] of coined)
|
|
79
|
+
out.push(` ${word} — declared under ${held.under}`);
|
|
80
|
+
out.push('');
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
stdout.write(`${out.join('\n')}\n`);
|
|
84
|
+
if (rows === 0) {
|
|
85
|
+
stdout.write(looking === ''
|
|
86
|
+
? 'read: this tree declares nothing yet. Attach a package with `thread attach`.\n'
|
|
87
|
+
: `read: nothing in this tree carries "${looking}" — not an id, a sentence, a path, a command, a requirement or a thing taken.\n`);
|
|
88
|
+
exit(0);
|
|
89
|
+
}
|
|
90
|
+
stdout.write(`read: ${String(rows)} row(s)${looking === '' ? '' : ` carrying "${looking}"`} across ` +
|
|
91
|
+
`${String(pass.declarations)} declaration(s), and ${String(pass.findings.length)} finding(s) the gate would report.\n`);
|
|
92
|
+
exit(0);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
2
|
+
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
3
|
+
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
4
|
+
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
5
|
+
});
|
|
6
|
+
}
|
|
7
|
+
return path;
|
|
8
|
+
};
|
|
9
|
+
import { spawnSync } from 'node:child_process';
|
|
10
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
11
|
+
import { relative } from 'node:path';
|
|
12
|
+
import { stdout } from 'node:process';
|
|
13
|
+
import { generators } from "../../core/chain.js";
|
|
14
|
+
import { exit } from "../../core/invoked.js";
|
|
15
|
+
import { asking, given, nouns, refuse, slug, wordsIn } from "../../core/asking.js";
|
|
16
|
+
import { listBounds, withRow } from "../../core/rows.js";
|
|
17
|
+
import { named as packageOf } from "../../core/scope.js";
|
|
18
|
+
import { findTree, inTree } from "../../core/tree.js";
|
|
19
|
+
const TREE = findTree();
|
|
20
|
+
const FILE = 'derivation.ts';
|
|
21
|
+
const LIST = 'GENERATORS';
|
|
22
|
+
const OWNER = 'integrations';
|
|
23
|
+
const DECLARES = 'declares';
|
|
24
|
+
const NAME = /^[a-z][a-z0-9_]*$/;
|
|
25
|
+
const PACKAGE = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/;
|
|
26
|
+
const PREFIX = 'sdk_';
|
|
27
|
+
const here = (one) => relative(TREE.at, one).split('\\').join('/');
|
|
28
|
+
const path = inTree(TREE, FILE);
|
|
29
|
+
const at = here(path);
|
|
30
|
+
import { quoted } from "../../core/rows.js";
|
|
31
|
+
const words = wordsIn([]);
|
|
32
|
+
const rows = await generators(TREE);
|
|
33
|
+
const already = rows.filter((one) => typeof one.id === 'string' && one.id.startsWith(PREFIX));
|
|
34
|
+
const first = words[0];
|
|
35
|
+
if (first === undefined) {
|
|
36
|
+
stdout.write(already.length === 0
|
|
37
|
+
? 'sdk: no SDK declared yet.\n'
|
|
38
|
+
: `sdk: ${String(already.length)} declared:\n${already
|
|
39
|
+
.map((one) => ` ${(one.by ?? '').slice('thread run '.length)} — ${one.describes ?? ''}`)
|
|
40
|
+
.join('\n')}\n`);
|
|
41
|
+
stdout.write('\nthread create sdk <name> — installs an npm package, asks what it is for and which declared secret it\n' +
|
|
42
|
+
'needs, and declares it. Then `thread run <name> <method>` calls it, resolving the credential through the\n' +
|
|
43
|
+
'vault at the moment of the call. `<method>` is dot-separated for a namespaced one, e.g. `customers.list`.\n');
|
|
44
|
+
exit(0);
|
|
45
|
+
}
|
|
46
|
+
const name = slug(first);
|
|
47
|
+
if (name === '')
|
|
48
|
+
refuse('an SDK has to be called something — `thread create sdk <name>`');
|
|
49
|
+
if (!NAME.test(name))
|
|
50
|
+
refuse(`"${name}" cannot be the name of an SDK — it is written in lower_snake_case`);
|
|
51
|
+
const claimed = nouns(rows, 'run').get(name);
|
|
52
|
+
if (claimed !== undefined) {
|
|
53
|
+
refuse(`\`thread run ${name}\` already resolves to ${claimed} — that name is taken`);
|
|
54
|
+
}
|
|
55
|
+
const { ask, shut } = asking(given());
|
|
56
|
+
const npmPackage = (await ask('package', 'npm package to install: ')).trim();
|
|
57
|
+
if (npmPackage === '') {
|
|
58
|
+
shut();
|
|
59
|
+
refuse('an SDK installs something — the npm package is what `npm install` is given');
|
|
60
|
+
}
|
|
61
|
+
if (!PACKAGE.test(npmPackage)) {
|
|
62
|
+
shut();
|
|
63
|
+
refuse(`"${npmPackage}" does not read as an npm package name`);
|
|
64
|
+
}
|
|
65
|
+
const describes = (await ask('describes', 'what is it for? ')).trim();
|
|
66
|
+
if (describes === '') {
|
|
67
|
+
shut();
|
|
68
|
+
refuse('an SDK says what it is for — the sentence is what somebody reads before running it');
|
|
69
|
+
}
|
|
70
|
+
let secrets = [];
|
|
71
|
+
try {
|
|
72
|
+
const taken = (await import(__rewriteRelativeImportExtension(new URL(`file://${inTree(TREE, 'secrets.ts')}`).href)));
|
|
73
|
+
secrets = taken.SECRETS ?? [];
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
secrets = [];
|
|
77
|
+
}
|
|
78
|
+
const carrying = (await ask('requires', `which secret does it need — ${secrets.map((one) => one.id).join(', ') || '(none declared yet)'}\n` +
|
|
79
|
+
' If this SDK needs a url, a key, a token or a password to work, IT GOES HERE and nowhere else.\n' +
|
|
80
|
+
' Declare it first with `thread create secret`, then name it here, comma-separated. It is resolved\n' +
|
|
81
|
+
' through the vault at the moment of the call and is never written into any file.\n' +
|
|
82
|
+
' Blank only if the SDK genuinely needs no credential at all — a declaration with the wrong answer\n' +
|
|
83
|
+
' here does not fail now, it fails at the call with the vendor\'s own error.\n' +
|
|
84
|
+
' > '))
|
|
85
|
+
.split(',')
|
|
86
|
+
.map((one) => one.trim())
|
|
87
|
+
.filter((one) => one !== '');
|
|
88
|
+
shut();
|
|
89
|
+
for (const one of carrying) {
|
|
90
|
+
if (!secrets.some((each) => each.id === one)) {
|
|
91
|
+
refuse(`\`${one}\` is not a secret this app declares. Declare it first with \`thread create secret\` — a credential is given to something that said it needed it, or not at all.`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
let text;
|
|
95
|
+
try {
|
|
96
|
+
text = readFileSync(path, 'utf8');
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
refuse(`${at} could not be read. Attach \`${packageOf('derivation')}\` first — that is what writes it.`);
|
|
100
|
+
}
|
|
101
|
+
const where = listBounds(text, LIST);
|
|
102
|
+
if (where === undefined)
|
|
103
|
+
refuse(`${at} states no \`${LIST}\` list to declare a generator in`);
|
|
104
|
+
stdout.write(`sdk: installing ${npmPackage} …\n`);
|
|
105
|
+
const installed = spawnSync('npm', ['install', npmPackage], { cwd: TREE.at, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] });
|
|
106
|
+
if (installed.status !== 0) {
|
|
107
|
+
refuse(`\`npm install ${npmPackage}\` failed, so nothing was declared:\n${`${installed.stdout ?? ''}${installed.stderr ?? ''}`.trim()}`);
|
|
108
|
+
}
|
|
109
|
+
stdout.write(`sdk: installed ${npmPackage}.\n`);
|
|
110
|
+
const fileAt = `_threads/sdk_${name}.ts`;
|
|
111
|
+
const SOURCE = `import { run } from '../sdk.ts'
|
|
112
|
+
|
|
113
|
+
await run({ name: ${quoted(name)}, module: ${quoted(npmPackage)}, requires: [${carrying.map(quoted).join(', ')}] })
|
|
114
|
+
`;
|
|
115
|
+
writeFileSync(inTree(TREE, `sdk_${name}.ts`), SOURCE, 'utf8');
|
|
116
|
+
const row = [
|
|
117
|
+
' {',
|
|
118
|
+
` id: ${quoted(`${PREFIX}${name}`)},`,
|
|
119
|
+
` at: ${quoted(fileAt)},`,
|
|
120
|
+
` describes: ${quoted(describes)},`,
|
|
121
|
+
' version: 1,',
|
|
122
|
+
` by: ${quoted(`thread run ${name}`)},`,
|
|
123
|
+
...(carrying.length === 0 ? [] : [` requires: [${carrying.map(quoted).join(', ')}],`]),
|
|
124
|
+
' },',
|
|
125
|
+
].join('\n');
|
|
126
|
+
writeFileSync(path, withRow(text, where, row), 'utf8');
|
|
127
|
+
const owner = inTree(TREE, `${OWNER}.ts`);
|
|
128
|
+
let owned = '';
|
|
129
|
+
try {
|
|
130
|
+
owned = readFileSync(owner, 'utf8');
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
refuse(`${OWNER}.ts could not be read, so there is nothing to declare this under.`);
|
|
134
|
+
}
|
|
135
|
+
const under = listBounds(owned, DECLARES);
|
|
136
|
+
if (under === undefined)
|
|
137
|
+
refuse(`${OWNER}.ts states no \`${DECLARES}\` list to declare this in`);
|
|
138
|
+
writeFileSync(owner, withRow(owned, under, [
|
|
139
|
+
' {',
|
|
140
|
+
` id: ${quoted(`${PREFIX}${name}`)},`,
|
|
141
|
+
` at: ${quoted(fileAt)},`,
|
|
142
|
+
` describes: ${quoted(describes)},`,
|
|
143
|
+
' version: 1,',
|
|
144
|
+
' },',
|
|
145
|
+
].join('\n')), 'utf8');
|
|
146
|
+
stdout.write(`sdk: declared ${name} — ${npmPackage}, reached with \`thread run ${name} <method>\`. ` +
|
|
147
|
+
`${carrying.length === 0 ? 'It needs no secret.' : `It resolves ${carrying.join(', ')} through the vault at call time.`}\n`);
|
|
148
|
+
exit(0);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
2
|
+
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
3
|
+
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
4
|
+
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
5
|
+
});
|
|
6
|
+
}
|
|
7
|
+
return path;
|
|
8
|
+
};
|
|
9
|
+
import { spawnSync } from 'node:child_process';
|
|
10
|
+
import { appendFileSync, existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
11
|
+
import { join, relative } from 'node:path';
|
|
12
|
+
import { argv, stdin, stdout } from 'node:process';
|
|
13
|
+
import { createInterface } from 'node:readline';
|
|
14
|
+
import { exit } from "../../core/invoked.js";
|
|
15
|
+
import { ELSEWHERE, RECEIPT, answer, asking, asksOf, given, issue, needed, only, ownAnswers, refuse, rowLines, seeding, slug, spend, stating, wordsIn } from "../../core/asking.js";
|
|
16
|
+
import { holdsRow, listBounds, withRow } from "../../core/rows.js";
|
|
17
|
+
import { named as packageOf } from "../../core/scope.js";
|
|
18
|
+
import { findTree, inTree } from "../../core/tree.js";
|
|
19
|
+
import { SAID, SECRECY, secrecyOf } from "../secret.js";
|
|
20
|
+
import { IGNORES, KEY_AT, SEALED_AT, keyOf, seal, sealedIn } from "../vault.js";
|
|
21
|
+
const TREE = findTree();
|
|
22
|
+
const FILE = 'secrets.ts';
|
|
23
|
+
const LIST = 'SECRETS';
|
|
24
|
+
const NAME = /^[a-z][a-z0-9_]*$/;
|
|
25
|
+
const ENVS = ['sandbox', 'production'];
|
|
26
|
+
const WORDS = Object.keys(SECRECY);
|
|
27
|
+
const ASKS = [
|
|
28
|
+
{ key: 'name', says: ['lower_snake_case, and the prefix is added for you'] },
|
|
29
|
+
{ key: 'holder', says: [`who may hold it — ${WORDS.join(', ')}`] },
|
|
30
|
+
{ key: 'describes', says: ['what it is for, in one sentence'], edits: true },
|
|
31
|
+
{ key: 'header', says: ['blank if this value IS the address of what requires it'], edits: true },
|
|
32
|
+
{ key: 'prefix', says: ['what precedes it in that header, trailing space and all'], edits: true },
|
|
33
|
+
];
|
|
34
|
+
const TAKES = ASKS.map((one) => one.key);
|
|
35
|
+
const EDITS = ASKS.filter((one) => one.edits === true).map((one) => one.key);
|
|
36
|
+
const ASKED = ASKS.flatMap((one) => one.says.map((line, at) => ` ${at === 0 ? `${one.key}:`.padEnd(32) : ' '.repeat(32)}${line}`));
|
|
37
|
+
const here = (one) => relative(TREE.at, one).split('\\').join('/');
|
|
38
|
+
const path = inTree(TREE, FILE);
|
|
39
|
+
const at = here(path);
|
|
40
|
+
import { quoted } from "../../core/rows.js";
|
|
41
|
+
let secrets = [];
|
|
42
|
+
try {
|
|
43
|
+
const taken = (await import(__rewriteRelativeImportExtension(new URL(`file://${path}`).href)));
|
|
44
|
+
secrets = taken.SECRETS ?? [];
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
refuse(`${at} could not be read. Attach \`${packageOf('integrations')}\` first — an app states its secrets there.`);
|
|
48
|
+
}
|
|
49
|
+
const held = new Map(ENVS.map((env) => [env, new Set(sealedIn(TREE.at, env))]));
|
|
50
|
+
const setIn = (id) => {
|
|
51
|
+
const found = ENVS.filter((env) => held.get(env)?.has(id) === true);
|
|
52
|
+
return found.length === 0 ? 'no value anywhere yet' : `set for ${found.join(' and ')}`;
|
|
53
|
+
};
|
|
54
|
+
async function sealing(id) {
|
|
55
|
+
const hidden = async (question) => {
|
|
56
|
+
stdout.write(question);
|
|
57
|
+
const rl = createInterface({ input: stdin, output: stdout, terminal: true });
|
|
58
|
+
rl._writeToOutput = () => { };
|
|
59
|
+
const value = await new Promise((done) => rl.question('', done));
|
|
60
|
+
rl.close();
|
|
61
|
+
stdout.write('\n');
|
|
62
|
+
return value;
|
|
63
|
+
};
|
|
64
|
+
let sealed = 0;
|
|
65
|
+
for (const env of ENVS) {
|
|
66
|
+
const value = await hidden(` ${id} for ${env} (paste it, or press enter to skip): `);
|
|
67
|
+
if (value.trim() === '')
|
|
68
|
+
continue;
|
|
69
|
+
seal(TREE.at, id, env, value);
|
|
70
|
+
sealed += 1;
|
|
71
|
+
}
|
|
72
|
+
if (sealed === 0)
|
|
73
|
+
return 0;
|
|
74
|
+
const ignore = join(TREE.at, '.gitignore');
|
|
75
|
+
const already = existsSync(ignore) ? readFileSync(ignore, 'utf8') : '';
|
|
76
|
+
const missing = IGNORES.filter((one) => !already.split('\n').some((line) => line.trim() === one));
|
|
77
|
+
if (missing.length > 0) {
|
|
78
|
+
appendFileSync(ignore, `${already.endsWith('\n') || already === '' ? '' : '\n'}${missing.join('\n')}\n`);
|
|
79
|
+
stdout.write(`secret: added ${missing.join(', ')} to .gitignore — the key is never committed.\n`);
|
|
80
|
+
}
|
|
81
|
+
stdout.write(`secret: sealed ${id} for ${String(sealed)} environment(s) into ${SEALED_AT}.\n`);
|
|
82
|
+
return sealed;
|
|
83
|
+
}
|
|
84
|
+
const listing = secrets.length === 0
|
|
85
|
+
? 'no secrets yet.'
|
|
86
|
+
: [
|
|
87
|
+
`${String(secrets.length)} secret(s):`,
|
|
88
|
+
...secrets.flatMap((one) => [
|
|
89
|
+
` ${one.id} — ${secrecyOf(one.id) ?? 'wearing no prefix, which is refused'}, ${setIn(one.id)}`,
|
|
90
|
+
` ${one.sends === undefined ? 'no `sends`, so it is an address' : `sent as ${one.sends.header}: ${one.sends.prefix ?? ''}<value>`}`,
|
|
91
|
+
]),
|
|
92
|
+
].join('\n');
|
|
93
|
+
const words = wordsIn(['new', 'set']);
|
|
94
|
+
const verb = words[0];
|
|
95
|
+
stdout.write(`${listing}\n`);
|
|
96
|
+
if (keyOf(TREE.at) === undefined && secrets.length > 0) {
|
|
97
|
+
stdout.write(`\nthere is no vault key at ${KEY_AT}, so nothing above can be opened here. It is never committed — ` +
|
|
98
|
+
'whoever set this app up has it.\n');
|
|
99
|
+
}
|
|
100
|
+
const method = (nonce) => [
|
|
101
|
+
'',
|
|
102
|
+
'what this makes:',
|
|
103
|
+
'',
|
|
104
|
+
` thread create secret new <name> ${RECEIPT} ${nonce}`,
|
|
105
|
+
` thread create secret set <id> ${RECEIPT} ${nonce}`,
|
|
106
|
+
'',
|
|
107
|
+
...ELSEWHERE,
|
|
108
|
+
'',
|
|
109
|
+
` ${RECEIPT} is spent by one run and refused if ${at} moved after it was issued.`,
|
|
110
|
+
'',
|
|
111
|
+
'who may hold a value. The word is what you send; the prefix is what the id wears, and the prefix',
|
|
112
|
+
'IS who may hold it — no field says so:',
|
|
113
|
+
'',
|
|
114
|
+
...WORDS.flatMap((one) => [` ${one.padEnd(7)} → ${SECRECY[one]}<name>`, ` ${SAID[one]}`, '']),
|
|
115
|
+
'`new` asks, in this order:',
|
|
116
|
+
'',
|
|
117
|
+
...ASKED,
|
|
118
|
+
'',
|
|
119
|
+
'and then, at a terminal only, it asks for the value itself — echo off, sealed as you press enter.',
|
|
120
|
+
'A piped run declares the row and says so instead, because there is no terminal to read one from.',
|
|
121
|
+
'',
|
|
122
|
+
'`edit` asks the same three, and a blank answer keeps what is already there — except that naming',
|
|
123
|
+
'a DIFFERENT header drops the old prefix unless you give a new one, because a prefix belongs to',
|
|
124
|
+
'the scheme its header names. `-` as the header removes the header and makes this an address.',
|
|
125
|
+
'',
|
|
126
|
+
'It will not change who may hold a value. That is the prefix, the prefix is the id, and an id that',
|
|
127
|
+
'changes is a different secret — every target requiring the old name would go on requiring a name',
|
|
128
|
+
'nothing declares. Declare the other one and move what requires it.',
|
|
129
|
+
'',
|
|
130
|
+
'`set` is the same prompt on its own, for changing a value later or giving one a `new` run skipped.',
|
|
131
|
+
'It takes no answers and cannot be piped. The value goes from the keyboard into the sealed store and',
|
|
132
|
+
`nowhere else — not into a row, not into this output, not into ${SEALED_AT} in any readable form.`,
|
|
133
|
+
'',
|
|
134
|
+
'So an agent may declare a secret and may never give it one. That is the point of the split.',
|
|
135
|
+
].join('\n');
|
|
136
|
+
const flagged = argv.indexOf(RECEIPT);
|
|
137
|
+
if (verb === undefined && flagged === -1) {
|
|
138
|
+
const nonce = issue(TREE.at, listing);
|
|
139
|
+
stdout.write(`${method(nonce)}\n`);
|
|
140
|
+
stdout.write(`\nsecret: receipt ${nonce} — spent on use, and stale the moment ${at} changes.\n`);
|
|
141
|
+
exit(0);
|
|
142
|
+
}
|
|
143
|
+
spend(TREE.at, listing, argv[flagged + 1], at);
|
|
144
|
+
if (verb !== 'new' && verb !== 'set' && verb !== 'edit') {
|
|
145
|
+
refuse(`there is nothing called "${verb ?? ''}" to do here — say new, edit or set`);
|
|
146
|
+
}
|
|
147
|
+
const text = readFileSync(path, 'utf8');
|
|
148
|
+
const where = listBounds(text, LIST);
|
|
149
|
+
if (where === undefined)
|
|
150
|
+
refuse(`${at} states no \`${LIST}\` list to declare a secret in`);
|
|
151
|
+
if (verb === 'set') {
|
|
152
|
+
const id = words[1] ?? refuse('`set` takes the id of a secret this app already declares');
|
|
153
|
+
const one = secrets.find((each) => each.id === id);
|
|
154
|
+
if (one === undefined) {
|
|
155
|
+
refuse(`\`${id}\` is not a secret this app declares. Declare it first with \`thread create secret new\` — a value is ` +
|
|
156
|
+
'given to something that said it needed it, or not at all.');
|
|
157
|
+
}
|
|
158
|
+
if (stdin.isTTY !== true) {
|
|
159
|
+
refuse('a value is typed by a person at a terminal, and this run is piped. Nothing was written. Run ' +
|
|
160
|
+
`\`thread create secret set ${id}\` yourself — an agent may declare a secret and may never give it one.`);
|
|
161
|
+
}
|
|
162
|
+
if ((await sealing(id)) === 0) {
|
|
163
|
+
stdout.write('secret: nothing typed, so nothing was sealed.\n');
|
|
164
|
+
exit(0);
|
|
165
|
+
}
|
|
166
|
+
const kept = spawnSync('npx', ['thread', 'vault'], { cwd: TREE.at, encoding: 'utf8' });
|
|
167
|
+
stdout.write(`${`${kept.stdout ?? ''}${kept.stderr ?? ''}`.trim()}\n`);
|
|
168
|
+
exit(kept.status === 0 ? 0 : 1);
|
|
169
|
+
}
|
|
170
|
+
const piped = given();
|
|
171
|
+
only(piped, TAKES, 'a secret');
|
|
172
|
+
const { ask, line, shut } = asking(piped);
|
|
173
|
+
const own = ownAnswers(piped);
|
|
174
|
+
const name = slug(words[1] ?? (await ask('name', 'name: ')));
|
|
175
|
+
if (name === '') {
|
|
176
|
+
shut();
|
|
177
|
+
refuse('a secret has to be called something');
|
|
178
|
+
}
|
|
179
|
+
if (!NAME.test(name)) {
|
|
180
|
+
shut();
|
|
181
|
+
refuse(`"${name}" cannot be the name of a secret — it is written in lower_snake_case`);
|
|
182
|
+
}
|
|
183
|
+
const holder = (piped.length > 0 ? needed(own, 'holder', 'a secret') : await ask('holder', `who may hold it — ${WORDS.join(', ')}: `)).trim();
|
|
184
|
+
if (!WORDS.includes(holder)) {
|
|
185
|
+
shut();
|
|
186
|
+
refuse(`there is no such holder as "${holder}". A secret is held by a ${WORDS.join(' or a ')}, and that prefix is the ` +
|
|
187
|
+
'only thing standing between a server credential and a browser bundle.');
|
|
188
|
+
}
|
|
189
|
+
const word = holder;
|
|
190
|
+
const id = `${SECRECY[word]}${name}`;
|
|
191
|
+
if (secrets.some((one) => one.id === id) || holdsRow(text, where, id)) {
|
|
192
|
+
shut();
|
|
193
|
+
refuse(`\`${id}\` is already declared in ${at}`);
|
|
194
|
+
}
|
|
195
|
+
const describes = (piped.length > 0 ? needed(own, 'describes', 'a secret') : await ask('describes', 'what is it for? ')).trim();
|
|
196
|
+
if (describes === '') {
|
|
197
|
+
shut();
|
|
198
|
+
refuse('a secret says what it is for — the sentence is what somebody reads before requiring it');
|
|
199
|
+
}
|
|
200
|
+
const header = (piped.length > 0 ? (answer(own, 'header') ?? '') : await ask('header', 'header (blank if this value IS an address): ')).trim();
|
|
201
|
+
const typed = header === '' ? '' : (piped.length > 0 ? (answer(own, 'prefix') ?? '') : await ask('prefix', 'prefix in that header, e.g. "Bearer ": '));
|
|
202
|
+
const { stated, kids } = await stating(asksOf('secret', 'Secret', {}, ['id', 'at', 'describes', 'version', 'sends']), piped, piped.length > 0, ask, line, []);
|
|
203
|
+
shut();
|
|
204
|
+
const prefix = typed === '' || typed.endsWith(' ') ? typed : `${typed} `;
|
|
205
|
+
const row = [
|
|
206
|
+
' {',
|
|
207
|
+
` id: ${quoted(id)},`,
|
|
208
|
+
` at: '_threads/${FILE}',`,
|
|
209
|
+
` describes: ${quoted(describes)},`,
|
|
210
|
+
' version: 1,',
|
|
211
|
+
...(header === ''
|
|
212
|
+
? []
|
|
213
|
+
: [` sends: { header: ${quoted(header.toLowerCase())}${prefix === '' ? '' : `, prefix: ${quoted(prefix)}`} },`]),
|
|
214
|
+
...rowLines(' ', stated, kids),
|
|
215
|
+
' },',
|
|
216
|
+
].join('\n');
|
|
217
|
+
writeFileSync(path, withRow(text, where, row), 'utf8');
|
|
218
|
+
for (const told of seeding(kids, TREE.at))
|
|
219
|
+
stdout.write(`secret: ${told}\n`);
|
|
220
|
+
stdout.write(`secret: declared ${id} in ${at}${header === '' ? ' — it has no `sends`, so it is the address of whatever requires it' : ''}.\n`);
|
|
221
|
+
if (stdin.isTTY === true) {
|
|
222
|
+
stdout.write(`\n${SAID[word]}\n\nThe value is not echoed and is sealed straight into ${SEALED_AT}.\n`);
|
|
223
|
+
if ((await sealing(id)) === 0) {
|
|
224
|
+
stdout.write(`secret: nothing typed, so ${id} has no value yet. Run \`thread create secret set ${id}\` when you have it.\n`);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
stdout.write(`secret: it has no value anywhere yet, and this run is piped — a value is typed by a person at a terminal. ` +
|
|
229
|
+
`Run \`thread create secret set ${id}\` yourself.\n`);
|
|
230
|
+
}
|
|
231
|
+
const kept = spawnSync('npx', ['thread', 'vault'], { cwd: TREE.at, encoding: 'utf8' });
|
|
232
|
+
stdout.write(`${`${kept.stdout ?? ''}${kept.stderr ?? ''}`.trim()}\n`);
|
|
233
|
+
exit(kept.status === 0 ? 0 : 1);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|