superpowers-manager 0.9.1 → 0.10.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/README.md +35 -16
- package/dist/cli-arguments.js +2 -1
- package/dist/cli.js +12 -9
- package/dist/harnesses/hermes/harness.js +36 -0
- package/dist/harnesses/hermes/install.js +235 -0
- package/dist/harnesses/hermes/native.js +40 -0
- package/dist/harnesses/hermes/observe.js +178 -0
- package/dist/harnesses/hermes/paths.js +61 -0
- package/dist/harnesses/hermes/prepare.js +78 -0
- package/dist/harnesses/hermes/presentation.js +23 -0
- package/dist/harnesses/hermes/state.js +144 -0
- package/dist/snapshot-prepare.js +2 -0
- package/node_modules/yaml/LICENSE +13 -0
- package/node_modules/yaml/README.md +172 -0
- package/node_modules/yaml/bin.mjs +11 -0
- package/node_modules/yaml/browser/dist/compose/compose-collection.js +88 -0
- package/node_modules/yaml/browser/dist/compose/compose-doc.js +43 -0
- package/node_modules/yaml/browser/dist/compose/compose-node.js +109 -0
- package/node_modules/yaml/browser/dist/compose/compose-scalar.js +86 -0
- package/node_modules/yaml/browser/dist/compose/composer.js +219 -0
- package/node_modules/yaml/browser/dist/compose/resolve-block-map.js +115 -0
- package/node_modules/yaml/browser/dist/compose/resolve-block-scalar.js +198 -0
- package/node_modules/yaml/browser/dist/compose/resolve-block-seq.js +49 -0
- package/node_modules/yaml/browser/dist/compose/resolve-end.js +37 -0
- package/node_modules/yaml/browser/dist/compose/resolve-flow-collection.js +207 -0
- package/node_modules/yaml/browser/dist/compose/resolve-flow-scalar.js +225 -0
- package/node_modules/yaml/browser/dist/compose/resolve-props.js +146 -0
- package/node_modules/yaml/browser/dist/compose/util-contains-newline.js +34 -0
- package/node_modules/yaml/browser/dist/compose/util-empty-scalar-position.js +26 -0
- package/node_modules/yaml/browser/dist/compose/util-flow-indent-check.js +15 -0
- package/node_modules/yaml/browser/dist/compose/util-map-includes.js +13 -0
- package/node_modules/yaml/browser/dist/doc/Document.js +335 -0
- package/node_modules/yaml/browser/dist/doc/anchors.js +71 -0
- package/node_modules/yaml/browser/dist/doc/applyReviver.js +55 -0
- package/node_modules/yaml/browser/dist/doc/createNode.js +88 -0
- package/node_modules/yaml/browser/dist/doc/directives.js +176 -0
- package/node_modules/yaml/browser/dist/errors.js +57 -0
- package/node_modules/yaml/browser/dist/index.js +17 -0
- package/node_modules/yaml/browser/dist/log.js +11 -0
- package/node_modules/yaml/browser/dist/nodes/Alias.js +116 -0
- package/node_modules/yaml/browser/dist/nodes/Collection.js +147 -0
- package/node_modules/yaml/browser/dist/nodes/Node.js +38 -0
- package/node_modules/yaml/browser/dist/nodes/Pair.js +36 -0
- package/node_modules/yaml/browser/dist/nodes/Scalar.js +24 -0
- package/node_modules/yaml/browser/dist/nodes/YAMLMap.js +144 -0
- package/node_modules/yaml/browser/dist/nodes/YAMLSeq.js +113 -0
- package/node_modules/yaml/browser/dist/nodes/addPairToJSMap.js +63 -0
- package/node_modules/yaml/browser/dist/nodes/identity.js +36 -0
- package/node_modules/yaml/browser/dist/nodes/toJS.js +37 -0
- package/node_modules/yaml/browser/dist/parse/cst-scalar.js +214 -0
- package/node_modules/yaml/browser/dist/parse/cst-stringify.js +61 -0
- package/node_modules/yaml/browser/dist/parse/cst-visit.js +97 -0
- package/node_modules/yaml/browser/dist/parse/cst.js +98 -0
- package/node_modules/yaml/browser/dist/parse/lexer.js +721 -0
- package/node_modules/yaml/browser/dist/parse/line-counter.js +39 -0
- package/node_modules/yaml/browser/dist/parse/parser.js +975 -0
- package/node_modules/yaml/browser/dist/public-api.js +102 -0
- package/node_modules/yaml/browser/dist/schema/Schema.js +37 -0
- package/node_modules/yaml/browser/dist/schema/common/map.js +17 -0
- package/node_modules/yaml/browser/dist/schema/common/null.js +15 -0
- package/node_modules/yaml/browser/dist/schema/common/seq.js +17 -0
- package/node_modules/yaml/browser/dist/schema/common/string.js +14 -0
- package/node_modules/yaml/browser/dist/schema/core/bool.js +19 -0
- package/node_modules/yaml/browser/dist/schema/core/float.js +43 -0
- package/node_modules/yaml/browser/dist/schema/core/int.js +38 -0
- package/node_modules/yaml/browser/dist/schema/core/schema.js +23 -0
- package/node_modules/yaml/browser/dist/schema/json/schema.js +62 -0
- package/node_modules/yaml/browser/dist/schema/tags.js +96 -0
- package/node_modules/yaml/browser/dist/schema/yaml-1.1/binary.js +58 -0
- package/node_modules/yaml/browser/dist/schema/yaml-1.1/bool.js +26 -0
- package/node_modules/yaml/browser/dist/schema/yaml-1.1/float.js +46 -0
- package/node_modules/yaml/browser/dist/schema/yaml-1.1/int.js +71 -0
- package/node_modules/yaml/browser/dist/schema/yaml-1.1/merge.js +67 -0
- package/node_modules/yaml/browser/dist/schema/yaml-1.1/omap.js +74 -0
- package/node_modules/yaml/browser/dist/schema/yaml-1.1/pairs.js +78 -0
- package/node_modules/yaml/browser/dist/schema/yaml-1.1/schema.js +39 -0
- package/node_modules/yaml/browser/dist/schema/yaml-1.1/set.js +93 -0
- package/node_modules/yaml/browser/dist/schema/yaml-1.1/timestamp.js +101 -0
- package/node_modules/yaml/browser/dist/stringify/foldFlowLines.js +146 -0
- package/node_modules/yaml/browser/dist/stringify/stringify.js +129 -0
- package/node_modules/yaml/browser/dist/stringify/stringifyCollection.js +153 -0
- package/node_modules/yaml/browser/dist/stringify/stringifyComment.js +20 -0
- package/node_modules/yaml/browser/dist/stringify/stringifyDocument.js +85 -0
- package/node_modules/yaml/browser/dist/stringify/stringifyNumber.js +25 -0
- package/node_modules/yaml/browser/dist/stringify/stringifyPair.js +150 -0
- package/node_modules/yaml/browser/dist/stringify/stringifyString.js +336 -0
- package/node_modules/yaml/browser/dist/util.js +11 -0
- package/node_modules/yaml/browser/dist/visit.js +233 -0
- package/node_modules/yaml/browser/index.js +5 -0
- package/node_modules/yaml/browser/package.json +3 -0
- package/node_modules/yaml/dist/cli.d.ts +8 -0
- package/node_modules/yaml/dist/cli.mjs +201 -0
- package/node_modules/yaml/dist/compose/compose-collection.d.ts +11 -0
- package/node_modules/yaml/dist/compose/compose-collection.js +90 -0
- package/node_modules/yaml/dist/compose/compose-doc.d.ts +7 -0
- package/node_modules/yaml/dist/compose/compose-doc.js +45 -0
- package/node_modules/yaml/dist/compose/compose-node.d.ts +29 -0
- package/node_modules/yaml/dist/compose/compose-node.js +112 -0
- package/node_modules/yaml/dist/compose/compose-scalar.d.ts +5 -0
- package/node_modules/yaml/dist/compose/compose-scalar.js +88 -0
- package/node_modules/yaml/dist/compose/composer.d.ts +63 -0
- package/node_modules/yaml/dist/compose/composer.js +224 -0
- package/node_modules/yaml/dist/compose/resolve-block-map.d.ts +6 -0
- package/node_modules/yaml/dist/compose/resolve-block-map.js +117 -0
- package/node_modules/yaml/dist/compose/resolve-block-scalar.d.ts +11 -0
- package/node_modules/yaml/dist/compose/resolve-block-scalar.js +200 -0
- package/node_modules/yaml/dist/compose/resolve-block-seq.d.ts +6 -0
- package/node_modules/yaml/dist/compose/resolve-block-seq.js +51 -0
- package/node_modules/yaml/dist/compose/resolve-end.d.ts +6 -0
- package/node_modules/yaml/dist/compose/resolve-end.js +39 -0
- package/node_modules/yaml/dist/compose/resolve-flow-collection.d.ts +7 -0
- package/node_modules/yaml/dist/compose/resolve-flow-collection.js +209 -0
- package/node_modules/yaml/dist/compose/resolve-flow-scalar.d.ts +10 -0
- package/node_modules/yaml/dist/compose/resolve-flow-scalar.js +227 -0
- package/node_modules/yaml/dist/compose/resolve-props.d.ts +23 -0
- package/node_modules/yaml/dist/compose/resolve-props.js +148 -0
- package/node_modules/yaml/dist/compose/util-contains-newline.d.ts +2 -0
- package/node_modules/yaml/dist/compose/util-contains-newline.js +36 -0
- package/node_modules/yaml/dist/compose/util-empty-scalar-position.d.ts +2 -0
- package/node_modules/yaml/dist/compose/util-empty-scalar-position.js +28 -0
- package/node_modules/yaml/dist/compose/util-flow-indent-check.d.ts +3 -0
- package/node_modules/yaml/dist/compose/util-flow-indent-check.js +17 -0
- package/node_modules/yaml/dist/compose/util-map-includes.d.ts +4 -0
- package/node_modules/yaml/dist/compose/util-map-includes.js +15 -0
- package/node_modules/yaml/dist/doc/Document.d.ts +141 -0
- package/node_modules/yaml/dist/doc/Document.js +337 -0
- package/node_modules/yaml/dist/doc/anchors.d.ts +24 -0
- package/node_modules/yaml/dist/doc/anchors.js +76 -0
- package/node_modules/yaml/dist/doc/applyReviver.d.ts +9 -0
- package/node_modules/yaml/dist/doc/applyReviver.js +57 -0
- package/node_modules/yaml/dist/doc/createNode.d.ts +17 -0
- package/node_modules/yaml/dist/doc/createNode.js +90 -0
- package/node_modules/yaml/dist/doc/directives.d.ts +49 -0
- package/node_modules/yaml/dist/doc/directives.js +178 -0
- package/node_modules/yaml/dist/errors.d.ts +21 -0
- package/node_modules/yaml/dist/errors.js +62 -0
- package/node_modules/yaml/dist/index.d.ts +25 -0
- package/node_modules/yaml/dist/index.js +50 -0
- package/node_modules/yaml/dist/log.d.ts +3 -0
- package/node_modules/yaml/dist/log.js +19 -0
- package/node_modules/yaml/dist/nodes/Alias.d.ts +29 -0
- package/node_modules/yaml/dist/nodes/Alias.js +118 -0
- package/node_modules/yaml/dist/nodes/Collection.d.ts +73 -0
- package/node_modules/yaml/dist/nodes/Collection.js +151 -0
- package/node_modules/yaml/dist/nodes/Node.d.ts +53 -0
- package/node_modules/yaml/dist/nodes/Node.js +40 -0
- package/node_modules/yaml/dist/nodes/Pair.d.ts +22 -0
- package/node_modules/yaml/dist/nodes/Pair.js +39 -0
- package/node_modules/yaml/dist/nodes/Scalar.d.ts +47 -0
- package/node_modules/yaml/dist/nodes/Scalar.js +27 -0
- package/node_modules/yaml/dist/nodes/YAMLMap.d.ts +53 -0
- package/node_modules/yaml/dist/nodes/YAMLMap.js +147 -0
- package/node_modules/yaml/dist/nodes/YAMLSeq.d.ts +60 -0
- package/node_modules/yaml/dist/nodes/YAMLSeq.js +115 -0
- package/node_modules/yaml/dist/nodes/addPairToJSMap.d.ts +4 -0
- package/node_modules/yaml/dist/nodes/addPairToJSMap.js +65 -0
- package/node_modules/yaml/dist/nodes/identity.d.ts +23 -0
- package/node_modules/yaml/dist/nodes/identity.js +53 -0
- package/node_modules/yaml/dist/nodes/toJS.d.ts +29 -0
- package/node_modules/yaml/dist/nodes/toJS.js +39 -0
- package/node_modules/yaml/dist/options.d.ts +350 -0
- package/node_modules/yaml/dist/parse/cst-scalar.d.ts +64 -0
- package/node_modules/yaml/dist/parse/cst-scalar.js +218 -0
- package/node_modules/yaml/dist/parse/cst-stringify.d.ts +8 -0
- package/node_modules/yaml/dist/parse/cst-stringify.js +63 -0
- package/node_modules/yaml/dist/parse/cst-visit.d.ts +39 -0
- package/node_modules/yaml/dist/parse/cst-visit.js +99 -0
- package/node_modules/yaml/dist/parse/cst.d.ts +109 -0
- package/node_modules/yaml/dist/parse/cst.js +112 -0
- package/node_modules/yaml/dist/parse/lexer.d.ts +87 -0
- package/node_modules/yaml/dist/parse/lexer.js +723 -0
- package/node_modules/yaml/dist/parse/line-counter.d.ts +22 -0
- package/node_modules/yaml/dist/parse/line-counter.js +41 -0
- package/node_modules/yaml/dist/parse/parser.d.ts +84 -0
- package/node_modules/yaml/dist/parse/parser.js +980 -0
- package/node_modules/yaml/dist/public-api.d.ts +44 -0
- package/node_modules/yaml/dist/public-api.js +107 -0
- package/node_modules/yaml/dist/schema/Schema.d.ts +17 -0
- package/node_modules/yaml/dist/schema/Schema.js +39 -0
- package/node_modules/yaml/dist/schema/common/map.d.ts +2 -0
- package/node_modules/yaml/dist/schema/common/map.js +19 -0
- package/node_modules/yaml/dist/schema/common/null.d.ts +4 -0
- package/node_modules/yaml/dist/schema/common/null.js +17 -0
- package/node_modules/yaml/dist/schema/common/seq.d.ts +2 -0
- package/node_modules/yaml/dist/schema/common/seq.js +19 -0
- package/node_modules/yaml/dist/schema/common/string.d.ts +2 -0
- package/node_modules/yaml/dist/schema/common/string.js +16 -0
- package/node_modules/yaml/dist/schema/core/bool.d.ts +4 -0
- package/node_modules/yaml/dist/schema/core/bool.js +21 -0
- package/node_modules/yaml/dist/schema/core/float.d.ts +4 -0
- package/node_modules/yaml/dist/schema/core/float.js +47 -0
- package/node_modules/yaml/dist/schema/core/int.d.ts +4 -0
- package/node_modules/yaml/dist/schema/core/int.js +42 -0
- package/node_modules/yaml/dist/schema/core/schema.d.ts +1 -0
- package/node_modules/yaml/dist/schema/core/schema.js +25 -0
- package/node_modules/yaml/dist/schema/json/schema.d.ts +2 -0
- package/node_modules/yaml/dist/schema/json/schema.js +64 -0
- package/node_modules/yaml/dist/schema/json-schema.d.ts +69 -0
- package/node_modules/yaml/dist/schema/tags.d.ts +48 -0
- package/node_modules/yaml/dist/schema/tags.js +99 -0
- package/node_modules/yaml/dist/schema/types.d.ts +92 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/binary.d.ts +2 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/binary.js +70 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/bool.d.ts +7 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/bool.js +29 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/float.d.ts +4 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/float.js +50 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/int.d.ts +5 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/int.js +76 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/merge.d.ts +9 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/merge.js +71 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/omap.d.ts +22 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/omap.js +77 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/pairs.d.ts +10 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/pairs.js +82 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/schema.d.ts +1 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/schema.js +41 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/set.d.ts +28 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/set.js +96 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/timestamp.d.ts +6 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/timestamp.js +105 -0
- package/node_modules/yaml/dist/stringify/foldFlowLines.d.ts +34 -0
- package/node_modules/yaml/dist/stringify/foldFlowLines.js +151 -0
- package/node_modules/yaml/dist/stringify/stringify.d.ts +21 -0
- package/node_modules/yaml/dist/stringify/stringify.js +132 -0
- package/node_modules/yaml/dist/stringify/stringifyCollection.d.ts +17 -0
- package/node_modules/yaml/dist/stringify/stringifyCollection.js +155 -0
- package/node_modules/yaml/dist/stringify/stringifyComment.d.ts +10 -0
- package/node_modules/yaml/dist/stringify/stringifyComment.js +24 -0
- package/node_modules/yaml/dist/stringify/stringifyDocument.d.ts +4 -0
- package/node_modules/yaml/dist/stringify/stringifyDocument.js +87 -0
- package/node_modules/yaml/dist/stringify/stringifyNumber.d.ts +2 -0
- package/node_modules/yaml/dist/stringify/stringifyNumber.js +27 -0
- package/node_modules/yaml/dist/stringify/stringifyPair.d.ts +3 -0
- package/node_modules/yaml/dist/stringify/stringifyPair.js +152 -0
- package/node_modules/yaml/dist/stringify/stringifyString.d.ts +9 -0
- package/node_modules/yaml/dist/stringify/stringifyString.js +338 -0
- package/node_modules/yaml/dist/test-events.d.ts +4 -0
- package/node_modules/yaml/dist/test-events.js +134 -0
- package/node_modules/yaml/dist/util.d.ts +16 -0
- package/node_modules/yaml/dist/util.js +28 -0
- package/node_modules/yaml/dist/visit.d.ts +102 -0
- package/node_modules/yaml/dist/visit.js +236 -0
- package/node_modules/yaml/package.json +97 -0
- package/node_modules/yaml/util.js +2 -0
- package/package.json +13 -7
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
import { assertNoFollowType } from "../../safe-path.js";
|
|
4
|
+
function trimHome(value) {
|
|
5
|
+
const whitespace = (index) => {
|
|
6
|
+
const code = value.charCodeAt(index);
|
|
7
|
+
return ((code >= 0x1c && code <= 0x1f) ||
|
|
8
|
+
/\p{White_Space}/u.test(value.charAt(index)));
|
|
9
|
+
};
|
|
10
|
+
let start = 0;
|
|
11
|
+
let end = value.length;
|
|
12
|
+
while (start < end && whitespace(start))
|
|
13
|
+
start++;
|
|
14
|
+
while (end > start && whitespace(end - 1))
|
|
15
|
+
end--;
|
|
16
|
+
return value.slice(start, end);
|
|
17
|
+
}
|
|
18
|
+
function expandHome(value, env, home) {
|
|
19
|
+
const expanded = trimHome(value).replace(/\$([A-Za-z0-9_]+|\{[^}]*\})/gu, (token, name) => {
|
|
20
|
+
const variable = name.startsWith("{") ? name.slice(1, -1) : name;
|
|
21
|
+
return Object.hasOwn(env, variable) ? (env[variable] ?? token) : token;
|
|
22
|
+
});
|
|
23
|
+
if (expanded === "~")
|
|
24
|
+
return home;
|
|
25
|
+
if (expanded.startsWith("~/"))
|
|
26
|
+
return join(home, expanded.slice(2));
|
|
27
|
+
if (expanded.startsWith("~"))
|
|
28
|
+
throw new Error("cannot resolve HERMES_HOME: named user homes are unsupported");
|
|
29
|
+
return expanded;
|
|
30
|
+
}
|
|
31
|
+
export function hermesPaths(env, cwd) {
|
|
32
|
+
const configured = trimHome(env.HERMES_HOME ?? "");
|
|
33
|
+
const home = env.HOME && env.HOME.length > 0 ? env.HOME : homedir();
|
|
34
|
+
const hermesHome = configured.length > 0
|
|
35
|
+
? resolve(cwd, expandHome(configured, env, home))
|
|
36
|
+
: join(resolve(cwd, home), ".hermes");
|
|
37
|
+
if (expandHome(hermesHome, env, home) !== hermesHome)
|
|
38
|
+
throw new Error("cannot resolve HERMES_HOME: home expansion is not stable");
|
|
39
|
+
const managerRoot = join(hermesHome, "superpowers-manager");
|
|
40
|
+
const pluginsRoot = join(hermesHome, "plugins");
|
|
41
|
+
return {
|
|
42
|
+
hermesHome,
|
|
43
|
+
managerRoot,
|
|
44
|
+
preparedRoot: join(managerRoot, "prepared"),
|
|
45
|
+
pluginsRoot,
|
|
46
|
+
pluginRoot: join(pluginsRoot, "superpowers"),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
export async function assertHermesStorageAncestorsSafe(paths) {
|
|
50
|
+
for (const path of [
|
|
51
|
+
paths.hermesHome,
|
|
52
|
+
paths.managerRoot,
|
|
53
|
+
paths.preparedRoot,
|
|
54
|
+
paths.pluginsRoot,
|
|
55
|
+
])
|
|
56
|
+
await assertNoFollowType(path, ["directory", "missing"]);
|
|
57
|
+
}
|
|
58
|
+
export async function assertHermesStorageSafe(paths) {
|
|
59
|
+
await assertHermesStorageAncestorsSafe(paths);
|
|
60
|
+
await assertNoFollowType(paths.pluginRoot, ["directory", "missing"]);
|
|
61
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { readdir, rename, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { failureResult, successResult, } from "../../adapter-result.js";
|
|
4
|
+
import { readArtifactFile, validateNativeSkill } from "../../artifact-tree.js";
|
|
5
|
+
import { assertNoFollowType, classifyPathNoFollow } from "../../safe-path.js";
|
|
6
|
+
import { SafetyError } from "../../safety-error.js";
|
|
7
|
+
import { validateSource } from "../../selection.js";
|
|
8
|
+
import { createSnapshotReceipts, } from "../../snapshot-package.js";
|
|
9
|
+
import { createSnapshotPreparation } from "../../snapshot-prepare.js";
|
|
10
|
+
import { assertHermesStorageSafe, hermesPaths } from "./paths.js";
|
|
11
|
+
async function flattenHermesPlugin(root) {
|
|
12
|
+
const nativeRoot = join(root, ".hermes-plugin");
|
|
13
|
+
if ((await classifyPathNoFollow(nativeRoot)) !== "directory")
|
|
14
|
+
return;
|
|
15
|
+
const entries = await readdir(nativeRoot);
|
|
16
|
+
if (entries.some((entry) => entry === "skills" || entry === "LICENSE"))
|
|
17
|
+
throw new SafetyError("hermes-package", `Hermes plugin layout collides with shared content: ${root}`);
|
|
18
|
+
for (const entry of await readdir(root))
|
|
19
|
+
if (entry !== ".hermes-plugin" && entry !== "skills" && entry !== "LICENSE")
|
|
20
|
+
await rm(join(root, entry), { recursive: true, force: true });
|
|
21
|
+
for (const entry of entries)
|
|
22
|
+
await rename(join(nativeRoot, entry), join(root, entry));
|
|
23
|
+
await rm(nativeRoot, { recursive: true });
|
|
24
|
+
await writeFile(join(root, "__pycache__"), "", { flag: "wx" });
|
|
25
|
+
}
|
|
26
|
+
async function assessHermesCompatibility(root, selection) {
|
|
27
|
+
try {
|
|
28
|
+
validateSource(selection.effectiveSource);
|
|
29
|
+
await readArtifactFile(root, join(root, "__pycache__"), 0);
|
|
30
|
+
for (const relative of [
|
|
31
|
+
"plugin.yaml",
|
|
32
|
+
"__init__.py",
|
|
33
|
+
"skills/using-superpowers/references/hermes-tools.md",
|
|
34
|
+
])
|
|
35
|
+
await assertNoFollowType(join(root, relative), ["regular-file"]);
|
|
36
|
+
await validateNativeSkill(root);
|
|
37
|
+
return {
|
|
38
|
+
kind: "supported",
|
|
39
|
+
generation: "hermes-flat-plugin-v1",
|
|
40
|
+
reason: "native Hermes plugin",
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return {
|
|
45
|
+
kind: "unsupported",
|
|
46
|
+
reason: `Hermes package does not provide a native Hermes plugin: ${root}`,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const receipts = createSnapshotReceipts({
|
|
51
|
+
harness: "hermes",
|
|
52
|
+
label: "Hermes",
|
|
53
|
+
strictGeneration: true,
|
|
54
|
+
assessCompatibility: assessHermesCompatibility,
|
|
55
|
+
});
|
|
56
|
+
export const readHermesReceipt = receipts.readReceipt;
|
|
57
|
+
export const readHermesPackageAssessment = receipts.readAssessment;
|
|
58
|
+
export async function validateHermesPreparationBeforeFetch(ctx) {
|
|
59
|
+
try {
|
|
60
|
+
await assertHermesStorageSafe(hermesPaths(ctx.env ?? {}, process.cwd()));
|
|
61
|
+
return successResult("prepare", null, []);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return failureResult("prepare", "invalid-package", "cannot validate Hermes artifact storage", [], []);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const preparation = createSnapshotPreparation({
|
|
68
|
+
harness: "hermes",
|
|
69
|
+
label: "Hermes",
|
|
70
|
+
paths: hermesPaths,
|
|
71
|
+
assessCompatibility: assessHermesCompatibility,
|
|
72
|
+
readAssessment: readHermesPackageAssessment,
|
|
73
|
+
layout: flattenHermesPlugin,
|
|
74
|
+
});
|
|
75
|
+
export const hermesPreparationLocation = preparation.preparationLocation;
|
|
76
|
+
export const prepareHermesCandidate = preparation.prepareCandidate;
|
|
77
|
+
export const inspectHermesPrepared = preparation.inspectPrepared;
|
|
78
|
+
export const readHermesPrepared = preparation.readPrepared;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { displaySource } from "../../selection.js";
|
|
2
|
+
import { createSnapshotPresentation, probeOutput, } from "../../snapshot-presentation.js";
|
|
3
|
+
import { displayPath } from "../../validator.js";
|
|
4
|
+
const renderProbe = (facts) => {
|
|
5
|
+
const fields = [
|
|
6
|
+
["harness", "hermes"],
|
|
7
|
+
["desired_commit", facts.selection.desiredCommit],
|
|
8
|
+
["upstream_source_origin", facts.selection.upstreamSourceOrigin],
|
|
9
|
+
["effective_source", displaySource(facts.selection.effectiveSource)],
|
|
10
|
+
["prepared_identity", facts.prepared.observedIdentity],
|
|
11
|
+
["installed_identity", facts.installed.observedIdentity],
|
|
12
|
+
["installation_state", facts.installed.kind],
|
|
13
|
+
["resource_state", facts.resourceState ?? "idle"],
|
|
14
|
+
["ownership", facts.ownership.presentationValue],
|
|
15
|
+
["conflicts", (facts.ownership.presentationConflicts ?? []).join("; ")],
|
|
16
|
+
["update_control", facts.control.presentationValue],
|
|
17
|
+
["compatibility", facts.compatibility.kind],
|
|
18
|
+
["compatibility_reason", facts.compatibility.reason],
|
|
19
|
+
["status", facts.status],
|
|
20
|
+
].map(([key, value]) => [key, displayPath(value)]);
|
|
21
|
+
return probeOutput(fields);
|
|
22
|
+
};
|
|
23
|
+
export const hermesPresentation = createSnapshotPresentation("Hermes", renderProbe, (input) => input.ownership === "absent");
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { readdir } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { inspectionFailure, successResult, } from "../../adapter-result.js";
|
|
4
|
+
import { classifyPathNoFollow } from "../../safe-path.js";
|
|
5
|
+
import { observeSnapshot, sameSnapshotSource } from "../../snapshot-package.js";
|
|
6
|
+
import { displayPath } from "../../validator.js";
|
|
7
|
+
import { findSuperpowersManifests, projectPluginsEnabled, readHermesStatus, } from "./observe.js";
|
|
8
|
+
import { assertHermesStorageAncestorsSafe, hermesPaths, } from "./paths.js";
|
|
9
|
+
import { readHermesPackageAssessment, readHermesReceipt, } from "./prepare.js";
|
|
10
|
+
export async function observeHermesOwnership(paths) {
|
|
11
|
+
await assertHermesStorageAncestorsSafe(paths);
|
|
12
|
+
const snapshot = await observeSnapshot(paths.pluginRoot, readHermesReceipt);
|
|
13
|
+
return snapshot.kind === "unverified" ? { kind: "foreign" } : snapshot;
|
|
14
|
+
}
|
|
15
|
+
export async function otherSuperpowersManifests(paths) {
|
|
16
|
+
await assertHermesStorageAncestorsSafe(paths);
|
|
17
|
+
return (await findSuperpowersManifests(paths)).filter((root) => root !== paths.pluginRoot);
|
|
18
|
+
}
|
|
19
|
+
async function observeFacts(ctx) {
|
|
20
|
+
const env = ctx.env ?? {};
|
|
21
|
+
const paths = hermesPaths(env, process.cwd());
|
|
22
|
+
const ownership = await observeHermesOwnership(paths);
|
|
23
|
+
const status = await readHermesStatus(paths, env);
|
|
24
|
+
// A foreign leaf is already a blocking decision. Do not scan its contents
|
|
25
|
+
// or pass a symlink to the manifest observer.
|
|
26
|
+
const manifests = ownership.kind === "foreign" ? [] : await findSuperpowersManifests(paths);
|
|
27
|
+
return {
|
|
28
|
+
paths,
|
|
29
|
+
ownership,
|
|
30
|
+
status,
|
|
31
|
+
manifests,
|
|
32
|
+
conflicts: manifests.filter((root) => root !== paths.pluginRoot),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function blocked(...stderr) {
|
|
36
|
+
return {
|
|
37
|
+
kind: "blocked",
|
|
38
|
+
output: { stdout: [], stderr: stderr.map(displayPath) },
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function installDecision(facts, env) {
|
|
42
|
+
if (facts.ownership.kind === "foreign")
|
|
43
|
+
return blocked("error: another Superpowers copy is present in Hermes; remove it manually, then retry:", " hermes plugins remove superpowers");
|
|
44
|
+
if (facts.conflicts.length > 0)
|
|
45
|
+
return blocked("error: Hermes discovers another plugin named superpowers; remove it manually, then retry:", ...facts.conflicts.map((path) => ` ${path}`));
|
|
46
|
+
if (projectPluginsEnabled(env))
|
|
47
|
+
return blocked("error: HERMES_ENABLE_PROJECT_PLUGINS is set; a project-local Superpowers copy would override the managed one. Unset HERMES_ENABLE_PROJECT_PLUGINS, then retry.");
|
|
48
|
+
return { kind: "allowed" };
|
|
49
|
+
}
|
|
50
|
+
export async function inspectHermesOwnership(ctx) {
|
|
51
|
+
const operation = "inspect-hermes-ownership";
|
|
52
|
+
try {
|
|
53
|
+
const facts = await observeFacts(ctx);
|
|
54
|
+
const listed = facts.status !== "not enabled";
|
|
55
|
+
return successResult(operation, {
|
|
56
|
+
installEligibility: installDecision(facts, ctx.env ?? {}),
|
|
57
|
+
removalInput: { ownership: facts.ownership.kind, listed },
|
|
58
|
+
removalVerification: facts.ownership.kind !== "absent" || listed
|
|
59
|
+
? blocked("error: Hermes still lists Superpowers or its plugin directory is still present after removal")
|
|
60
|
+
: { kind: "allowed" },
|
|
61
|
+
postRemovalOutput: { stdout: [], stderr: [] },
|
|
62
|
+
presentationValue: facts.ownership.kind === "owned"
|
|
63
|
+
? `managed ${facts.ownership.digest}`
|
|
64
|
+
: facts.ownership.kind === "foreign"
|
|
65
|
+
? "foreign plugin directory"
|
|
66
|
+
: "absent",
|
|
67
|
+
presentationConflicts: facts.conflicts,
|
|
68
|
+
}, []);
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return inspectionFailure(operation, "Hermes ownership");
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
export async function inspectHermesInstalled(selection, ctx) {
|
|
75
|
+
const operation = "inspect-hermes-installed";
|
|
76
|
+
let facts;
|
|
77
|
+
try {
|
|
78
|
+
facts = await observeFacts(ctx);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return inspectionFailure(operation, "Hermes installed state");
|
|
82
|
+
}
|
|
83
|
+
if (facts.ownership.kind === "absent" &&
|
|
84
|
+
facts.status === "not enabled" &&
|
|
85
|
+
facts.manifests.length === 0)
|
|
86
|
+
return successResult(operation, { kind: "absent", observedIdentity: "" }, []);
|
|
87
|
+
const observed = facts.ownership.kind === "owned"
|
|
88
|
+
? facts.ownership.digest
|
|
89
|
+
: facts.ownership.kind === "foreign"
|
|
90
|
+
? "foreign plugin directory"
|
|
91
|
+
: "listed without an installed snapshot";
|
|
92
|
+
const mismatch = () => successResult(operation, { kind: "mismatch", observedIdentity: observed }, []);
|
|
93
|
+
if (facts.ownership.kind !== "owned" ||
|
|
94
|
+
facts.status !== "enabled" ||
|
|
95
|
+
!facts.manifests.includes(facts.paths.pluginRoot) ||
|
|
96
|
+
facts.conflicts.length > 0)
|
|
97
|
+
return mismatch();
|
|
98
|
+
try {
|
|
99
|
+
const prepared = await readHermesPackageAssessment(facts.paths.preparedRoot);
|
|
100
|
+
if (prepared.compatibility.kind !== "supported" ||
|
|
101
|
+
prepared.receipt.commit !== selection.desiredCommit ||
|
|
102
|
+
!sameSnapshotSource(prepared.receipt.source, selection.effectiveSource) ||
|
|
103
|
+
facts.ownership.receipt.commit !== selection.desiredCommit ||
|
|
104
|
+
!sameSnapshotSource(facts.ownership.receipt.source, selection.effectiveSource) ||
|
|
105
|
+
facts.ownership.digest !== prepared.receipt.digest)
|
|
106
|
+
return mismatch();
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return mismatch();
|
|
110
|
+
}
|
|
111
|
+
return successResult(operation, { kind: "current", observedIdentity: observed }, []);
|
|
112
|
+
}
|
|
113
|
+
export async function leftoverHermesPublication(paths) {
|
|
114
|
+
await assertHermesStorageAncestorsSafe(paths);
|
|
115
|
+
if ((await classifyPathNoFollow(paths.managerRoot)) === "missing")
|
|
116
|
+
return [];
|
|
117
|
+
return (await readdir(paths.managerRoot))
|
|
118
|
+
.filter((name) => name.startsWith("publish."))
|
|
119
|
+
.sort()
|
|
120
|
+
.map((name) => join(paths.managerRoot, name));
|
|
121
|
+
}
|
|
122
|
+
export async function inspectHermesControl(ctx) {
|
|
123
|
+
const operation = "inspect-hermes-control";
|
|
124
|
+
try {
|
|
125
|
+
const paths = hermesPaths(ctx.env ?? {}, process.cwd());
|
|
126
|
+
const leftovers = await leftoverHermesPublication(paths);
|
|
127
|
+
if (leftovers.length === 0)
|
|
128
|
+
return successResult(operation, {
|
|
129
|
+
probeEligibility: { kind: "allowed" },
|
|
130
|
+
mutationEligibility: { kind: "allowed" },
|
|
131
|
+
presentationValue: "clear",
|
|
132
|
+
}, []);
|
|
133
|
+
const decision = blocked("error: Hermes recovery required; inspect and remove leftover publication material manually:", ...leftovers.map((path) => ` ${path}`));
|
|
134
|
+
return successResult(operation, {
|
|
135
|
+
probeEligibility: decision,
|
|
136
|
+
mutationEligibility: decision,
|
|
137
|
+
presentationValue: displayPath(`recovery required at ${leftovers.join(", ")}`),
|
|
138
|
+
recoveryState: "required",
|
|
139
|
+
}, []);
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
return inspectionFailure(operation, "Hermes update control");
|
|
143
|
+
}
|
|
144
|
+
}
|
package/dist/snapshot-prepare.js
CHANGED
|
@@ -21,6 +21,8 @@ export function createSnapshotPreparation(b) {
|
|
|
21
21
|
async prepareCandidate(input) {
|
|
22
22
|
try {
|
|
23
23
|
await materializeGitTree(input.upstreamRoot, input.selection.desiredCommit, input.candidateRoot);
|
|
24
|
+
if (b.layout !== undefined)
|
|
25
|
+
await b.layout(input.candidateRoot);
|
|
24
26
|
const compatibility = await b.assessCompatibility(input.candidateRoot, input.selection);
|
|
25
27
|
if (compatibility.kind === "unsupported" ||
|
|
26
28
|
compatibility.kind === "unknown")
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Copyright Eemeli Aro <eemeli@gmail.com>
|
|
2
|
+
|
|
3
|
+
Permission to use, copy, modify, and/or distribute this software for any purpose
|
|
4
|
+
with or without fee is hereby granted, provided that the above copyright notice
|
|
5
|
+
and this permission notice appear in all copies.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
8
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
9
|
+
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
10
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
|
11
|
+
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
|
12
|
+
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
|
13
|
+
THIS SOFTWARE.
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# YAML <a href="https://www.npmjs.com/package/yaml"><img align="right" src="https://badge.fury.io/js/yaml.svg" title="npm package" /></a>
|
|
2
|
+
|
|
3
|
+
`yaml` is a definitive library for [YAML](https://yaml.org/), the human friendly data serialization standard.
|
|
4
|
+
This library:
|
|
5
|
+
|
|
6
|
+
- Supports both YAML 1.1 and YAML 1.2 and all common data schemas,
|
|
7
|
+
- Passes all of the [yaml-test-suite](https://github.com/yaml/yaml-test-suite) tests,
|
|
8
|
+
- Can accept any string as input without throwing, parsing as much YAML out of it as it can, and
|
|
9
|
+
- Supports parsing, modifying, and writing YAML comments and blank lines.
|
|
10
|
+
|
|
11
|
+
The library is released under the ISC open source license, and the code is [available on GitHub](https://github.com/eemeli/yaml/).
|
|
12
|
+
It has no external dependencies and runs on Node.js as well as modern browsers.
|
|
13
|
+
|
|
14
|
+
For the purposes of versioning, any changes that break any of the documented endpoints or APIs will be considered semver-major breaking changes.
|
|
15
|
+
Undocumented library internals may change between minor versions, and previous APIs may be deprecated (but not removed).
|
|
16
|
+
|
|
17
|
+
The minimum supported TypeScript version of the included typings is 3.9;
|
|
18
|
+
for use in earlier versions you may need to set `skipLibCheck: true` in your config.
|
|
19
|
+
This requirement may be updated between minor versions of the library.
|
|
20
|
+
|
|
21
|
+
For more information, see the project's documentation site: [**eemeli.org/yaml**](https://eemeli.org/yaml/)
|
|
22
|
+
|
|
23
|
+
For build instructions and contribution guidelines, see [docs/CONTRIBUTING.md](docs/CONTRIBUTING.md).
|
|
24
|
+
|
|
25
|
+
To install:
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
npm install yaml
|
|
29
|
+
# or
|
|
30
|
+
deno add jsr:@eemeli/yaml
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Note:** These docs are for `yaml@2`. For v1, see the [v1.10.0 tag](https://github.com/eemeli/yaml/tree/v1.10.0) for the source and [eemeli.org/yaml/v1](https://eemeli.org/yaml/v1/) for the documentation.
|
|
34
|
+
|
|
35
|
+
## API Overview
|
|
36
|
+
|
|
37
|
+
The API provided by `yaml` has three layers, depending on how deep you need to go: [Parse & Stringify](https://eemeli.org/yaml/#parse-amp-stringify), [Documents](https://eemeli.org/yaml/#documents), and the underlying [Lexer/Parser/Composer](https://eemeli.org/yaml/#parsing-yaml).
|
|
38
|
+
The first has the simplest API and "just works", the second gets you all the bells and whistles supported by the library along with a decent [AST](https://eemeli.org/yaml/#content-nodes), and the third lets you get progressively closer to YAML source, if that's your thing.
|
|
39
|
+
|
|
40
|
+
A [command-line tool](https://eemeli.org/yaml/#command-line-tool) is also included.
|
|
41
|
+
|
|
42
|
+
### Parse & Stringify
|
|
43
|
+
|
|
44
|
+
```js
|
|
45
|
+
import { parse, stringify } from 'yaml'
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
- [`parse(str, reviver?, options?): value`](https://eemeli.org/yaml/#yaml-parse)
|
|
49
|
+
- [`stringify(value, replacer?, options?): string`](https://eemeli.org/yaml/#yaml-stringify)
|
|
50
|
+
|
|
51
|
+
### Documents
|
|
52
|
+
|
|
53
|
+
<!-- prettier-ignore -->
|
|
54
|
+
```js
|
|
55
|
+
import {
|
|
56
|
+
Document,
|
|
57
|
+
isDocument,
|
|
58
|
+
parseAllDocuments,
|
|
59
|
+
parseDocument
|
|
60
|
+
} from 'yaml'
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
- [`Document`](https://eemeli.org/yaml/#documents)
|
|
64
|
+
- [`constructor(value, replacer?, options?)`](https://eemeli.org/yaml/#creating-documents)
|
|
65
|
+
- [`#contents`](https://eemeli.org/yaml/#content-nodes)
|
|
66
|
+
- [`#directives`](https://eemeli.org/yaml/#stream-directives)
|
|
67
|
+
- [`#errors`](https://eemeli.org/yaml/#errors)
|
|
68
|
+
- [`#warnings`](https://eemeli.org/yaml/#errors)
|
|
69
|
+
- [`isDocument(foo): boolean`](https://eemeli.org/yaml/#identifying-node-types)
|
|
70
|
+
- [`parseAllDocuments(str, options?): Document[]`](https://eemeli.org/yaml/#parsing-documents)
|
|
71
|
+
- [`parseDocument(str, options?): Document`](https://eemeli.org/yaml/#parsing-documents)
|
|
72
|
+
|
|
73
|
+
### Content Nodes
|
|
74
|
+
|
|
75
|
+
<!-- prettier-ignore -->
|
|
76
|
+
```js
|
|
77
|
+
import {
|
|
78
|
+
isAlias, isCollection, isMap, isNode,
|
|
79
|
+
isPair, isScalar, isSeq, Scalar,
|
|
80
|
+
visit, visitAsync, YAMLMap, YAMLSeq
|
|
81
|
+
} from 'yaml'
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
- [`isAlias(foo): boolean`](https://eemeli.org/yaml/#identifying-node-types)
|
|
85
|
+
- [`isCollection(foo): boolean`](https://eemeli.org/yaml/#identifying-node-types)
|
|
86
|
+
- [`isMap(foo): boolean`](https://eemeli.org/yaml/#identifying-node-types)
|
|
87
|
+
- [`isNode(foo): boolean`](https://eemeli.org/yaml/#identifying-node-types)
|
|
88
|
+
- [`isPair(foo): boolean`](https://eemeli.org/yaml/#identifying-node-types)
|
|
89
|
+
- [`isScalar(foo): boolean`](https://eemeli.org/yaml/#identifying-node-types)
|
|
90
|
+
- [`isSeq(foo): boolean`](https://eemeli.org/yaml/#identifying-node-types)
|
|
91
|
+
- [`new Scalar(value)`](https://eemeli.org/yaml/#scalar-values)
|
|
92
|
+
- [`new YAMLMap()`](https://eemeli.org/yaml/#collections)
|
|
93
|
+
- [`new YAMLSeq()`](https://eemeli.org/yaml/#collections)
|
|
94
|
+
- [`doc.createAlias(node, name?): Alias`](https://eemeli.org/yaml/#creating-nodes)
|
|
95
|
+
- [`doc.createNode(value, options?): Node`](https://eemeli.org/yaml/#creating-nodes)
|
|
96
|
+
- [`doc.createPair(key, value): Pair`](https://eemeli.org/yaml/#creating-nodes)
|
|
97
|
+
- [`visit(node, visitor)`](https://eemeli.org/yaml/#finding-and-modifying-nodes)
|
|
98
|
+
- [`visitAsync(node, visitor)`](https://eemeli.org/yaml/#finding-and-modifying-nodes)
|
|
99
|
+
|
|
100
|
+
### Parsing YAML
|
|
101
|
+
|
|
102
|
+
```js
|
|
103
|
+
import { Composer, Lexer, Parser } from 'yaml'
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
- [`new Lexer().lex(src)`](https://eemeli.org/yaml/#lexer)
|
|
107
|
+
- [`new Parser(onNewLine?).parse(src)`](https://eemeli.org/yaml/#parser)
|
|
108
|
+
- [`new Composer(options?).compose(tokens)`](https://eemeli.org/yaml/#composer)
|
|
109
|
+
|
|
110
|
+
## YAML.parse
|
|
111
|
+
|
|
112
|
+
```yaml
|
|
113
|
+
# file.yml
|
|
114
|
+
YAML:
|
|
115
|
+
- A human-readable data serialization language
|
|
116
|
+
- https://en.wikipedia.org/wiki/YAML
|
|
117
|
+
yaml:
|
|
118
|
+
- A complete JavaScript implementation
|
|
119
|
+
- https://www.npmjs.com/package/yaml
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
```js
|
|
123
|
+
import fs from 'fs'
|
|
124
|
+
import YAML from 'yaml'
|
|
125
|
+
|
|
126
|
+
YAML.parse('3.14159')
|
|
127
|
+
// 3.14159
|
|
128
|
+
|
|
129
|
+
YAML.parse('[ true, false, maybe, null ]\n')
|
|
130
|
+
// [ true, false, 'maybe', null ]
|
|
131
|
+
|
|
132
|
+
const file = fs.readFileSync('./file.yml', 'utf8')
|
|
133
|
+
YAML.parse(file)
|
|
134
|
+
// { YAML:
|
|
135
|
+
// [ 'A human-readable data serialization language',
|
|
136
|
+
// 'https://en.wikipedia.org/wiki/YAML' ],
|
|
137
|
+
// yaml:
|
|
138
|
+
// [ 'A complete JavaScript implementation',
|
|
139
|
+
// 'https://www.npmjs.com/package/yaml' ] }
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## YAML.stringify
|
|
143
|
+
|
|
144
|
+
```js
|
|
145
|
+
import YAML from 'yaml'
|
|
146
|
+
|
|
147
|
+
YAML.stringify(3.14159)
|
|
148
|
+
// '3.14159\n'
|
|
149
|
+
|
|
150
|
+
YAML.stringify([true, false, 'maybe', null])
|
|
151
|
+
// `- true
|
|
152
|
+
// - false
|
|
153
|
+
// - maybe
|
|
154
|
+
// - null
|
|
155
|
+
// `
|
|
156
|
+
|
|
157
|
+
YAML.stringify({ number: 3, plain: 'string', block: 'two\nlines\n' })
|
|
158
|
+
// `number: 3
|
|
159
|
+
// plain: string
|
|
160
|
+
// block: |
|
|
161
|
+
// two
|
|
162
|
+
// lines
|
|
163
|
+
// `
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
Browser testing provided by:
|
|
169
|
+
|
|
170
|
+
<a href="https://www.browserstack.com/open-source">
|
|
171
|
+
<img width=200 src="https://eemeli.org/yaml/images/browserstack.svg" alt="BrowserStack" />
|
|
172
|
+
</a>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { UserError, cli, help } from './dist/cli.mjs'
|
|
4
|
+
|
|
5
|
+
cli(process.stdin, error => {
|
|
6
|
+
if (error instanceof UserError) {
|
|
7
|
+
if (error.code === UserError.ARGS) console.error(`${help}\n`)
|
|
8
|
+
console.error(error.message)
|
|
9
|
+
process.exitCode = error.code
|
|
10
|
+
} else if (error) throw error
|
|
11
|
+
})
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { isNode } from '../nodes/identity.js';
|
|
2
|
+
import { Scalar } from '../nodes/Scalar.js';
|
|
3
|
+
import { YAMLMap } from '../nodes/YAMLMap.js';
|
|
4
|
+
import { YAMLSeq } from '../nodes/YAMLSeq.js';
|
|
5
|
+
import { resolveBlockMap } from './resolve-block-map.js';
|
|
6
|
+
import { resolveBlockSeq } from './resolve-block-seq.js';
|
|
7
|
+
import { resolveFlowCollection } from './resolve-flow-collection.js';
|
|
8
|
+
|
|
9
|
+
function resolveCollection(CN, ctx, token, onError, tagName, tag) {
|
|
10
|
+
const coll = token.type === 'block-map'
|
|
11
|
+
? resolveBlockMap(CN, ctx, token, onError, tag)
|
|
12
|
+
: token.type === 'block-seq'
|
|
13
|
+
? resolveBlockSeq(CN, ctx, token, onError, tag)
|
|
14
|
+
: resolveFlowCollection(CN, ctx, token, onError, tag);
|
|
15
|
+
const Coll = coll.constructor;
|
|
16
|
+
// If we got a tagName matching the class, or the tag name is '!',
|
|
17
|
+
// then use the tagName from the node class used to create it.
|
|
18
|
+
if (tagName === '!' || tagName === Coll.tagName) {
|
|
19
|
+
coll.tag = Coll.tagName;
|
|
20
|
+
return coll;
|
|
21
|
+
}
|
|
22
|
+
if (tagName)
|
|
23
|
+
coll.tag = tagName;
|
|
24
|
+
return coll;
|
|
25
|
+
}
|
|
26
|
+
function composeCollection(CN, ctx, token, props, onError) {
|
|
27
|
+
const tagToken = props.tag;
|
|
28
|
+
const tagName = !tagToken
|
|
29
|
+
? null
|
|
30
|
+
: ctx.directives.tagName(tagToken.source, msg => onError(tagToken, 'TAG_RESOLVE_FAILED', msg));
|
|
31
|
+
if (token.type === 'block-seq') {
|
|
32
|
+
const { anchor, newlineAfterProp: nl } = props;
|
|
33
|
+
const lastProp = anchor && tagToken
|
|
34
|
+
? anchor.offset > tagToken.offset
|
|
35
|
+
? anchor
|
|
36
|
+
: tagToken
|
|
37
|
+
: (anchor ?? tagToken);
|
|
38
|
+
if (lastProp && (!nl || nl.offset < lastProp.offset)) {
|
|
39
|
+
const message = 'Missing newline after block sequence props';
|
|
40
|
+
onError(lastProp, 'MISSING_CHAR', message);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const expType = token.type === 'block-map'
|
|
44
|
+
? 'map'
|
|
45
|
+
: token.type === 'block-seq'
|
|
46
|
+
? 'seq'
|
|
47
|
+
: token.start.source === '{'
|
|
48
|
+
? 'map'
|
|
49
|
+
: 'seq';
|
|
50
|
+
// shortcut: check if it's a generic YAMLMap or YAMLSeq
|
|
51
|
+
// before jumping into the custom tag logic.
|
|
52
|
+
if (!tagToken ||
|
|
53
|
+
!tagName ||
|
|
54
|
+
tagName === '!' ||
|
|
55
|
+
(tagName === YAMLMap.tagName && expType === 'map') ||
|
|
56
|
+
(tagName === YAMLSeq.tagName && expType === 'seq')) {
|
|
57
|
+
return resolveCollection(CN, ctx, token, onError, tagName);
|
|
58
|
+
}
|
|
59
|
+
let tag = ctx.schema.tags.find(t => t.tag === tagName && t.collection === expType);
|
|
60
|
+
if (!tag) {
|
|
61
|
+
const kt = ctx.schema.knownTags[tagName];
|
|
62
|
+
if (kt?.collection === expType) {
|
|
63
|
+
ctx.schema.tags.push(Object.assign({}, kt, { default: false }));
|
|
64
|
+
tag = kt;
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
if (kt) {
|
|
68
|
+
onError(tagToken, 'BAD_COLLECTION_TYPE', `${kt.tag} used for ${expType} collection, but expects ${kt.collection ?? 'scalar'}`, true);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
onError(tagToken, 'TAG_RESOLVE_FAILED', `Unresolved tag: ${tagName}`, true);
|
|
72
|
+
}
|
|
73
|
+
return resolveCollection(CN, ctx, token, onError, tagName);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const coll = resolveCollection(CN, ctx, token, onError, tagName, tag);
|
|
77
|
+
const res = tag.resolve?.(coll, msg => onError(tagToken, 'TAG_RESOLVE_FAILED', msg), ctx.options) ?? coll;
|
|
78
|
+
const node = isNode(res)
|
|
79
|
+
? res
|
|
80
|
+
: new Scalar(res);
|
|
81
|
+
node.range = coll.range;
|
|
82
|
+
node.tag = tagName;
|
|
83
|
+
if (tag?.format)
|
|
84
|
+
node.format = tag.format;
|
|
85
|
+
return node;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export { composeCollection };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Document } from '../doc/Document.js';
|
|
2
|
+
import { composeNode, composeEmptyNode } from './compose-node.js';
|
|
3
|
+
import { resolveEnd } from './resolve-end.js';
|
|
4
|
+
import { resolveProps } from './resolve-props.js';
|
|
5
|
+
|
|
6
|
+
function composeDoc(options, directives, { offset, start, value, end }, onError) {
|
|
7
|
+
const opts = Object.assign({ _directives: directives }, options);
|
|
8
|
+
const doc = new Document(undefined, opts);
|
|
9
|
+
const ctx = {
|
|
10
|
+
atKey: false,
|
|
11
|
+
atRoot: true,
|
|
12
|
+
directives: doc.directives,
|
|
13
|
+
options: doc.options,
|
|
14
|
+
schema: doc.schema
|
|
15
|
+
};
|
|
16
|
+
const props = resolveProps(start, {
|
|
17
|
+
indicator: 'doc-start',
|
|
18
|
+
next: value ?? end?.[0],
|
|
19
|
+
offset,
|
|
20
|
+
onError,
|
|
21
|
+
parentIndent: 0,
|
|
22
|
+
startOnNewline: true
|
|
23
|
+
});
|
|
24
|
+
if (props.found) {
|
|
25
|
+
doc.directives.docStart = true;
|
|
26
|
+
if (value &&
|
|
27
|
+
(value.type === 'block-map' || value.type === 'block-seq') &&
|
|
28
|
+
!props.hasNewline)
|
|
29
|
+
onError(props.end, 'MISSING_CHAR', 'Block collection cannot start on same line with directives-end marker');
|
|
30
|
+
}
|
|
31
|
+
// @ts-expect-error If Contents is set, let's trust the user
|
|
32
|
+
doc.contents = value
|
|
33
|
+
? composeNode(ctx, value, props, onError)
|
|
34
|
+
: composeEmptyNode(ctx, props.end, start, null, props, onError);
|
|
35
|
+
const contentEnd = doc.contents.range[2];
|
|
36
|
+
const re = resolveEnd(end, contentEnd, false, onError);
|
|
37
|
+
if (re.comment)
|
|
38
|
+
doc.comment = re.comment;
|
|
39
|
+
doc.range = [offset, contentEnd, re.offset];
|
|
40
|
+
return doc;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export { composeDoc };
|