@guiho/runx 0.2.7 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/DOCS.md +127 -128
- package/README.md +56 -75
- package/devops/build-binaries.ts +44 -62
- package/devops/devops.xdocs.md +8 -5
- package/devops/install.ps1 +182 -152
- package/devops/install.sh +197 -262
- package/devops/installers.spec.ts +31 -0
- package/devops/verify-release-assets.ts +17 -0
- package/docs/docs.xdocs.md +3 -1
- package/docs/plans/plans.xdocs.md +7 -0
- package/docs/plans/rfc-0034-cli-compliance-migration.md +586 -0
- package/docs/plans/upgrade-reliability-implementation.md +95 -0
- package/docs/reviews/implementation/implementation.xdocs.md +2 -0
- package/docs/reviews/implementation/rfc-0034-cli-compliance-migration-review.md +59 -0
- package/docs/reviews/plans/plans.xdocs.md +6 -0
- package/docs/reviews/plans/rfc-0034-cli-compliance-migration-review.md +80 -0
- package/docs/reviews/plans/upgrade-reliability-implementation-review.md +65 -0
- package/docs/superpowers/specs/2026-07-15-upgrade-reliability-design.md +662 -0
- package/docs/superpowers/specs/specs.xdocs.md +24 -0
- package/docs/superpowers/superpowers.xdocs.md +21 -0
- package/docs/todo/rfc-0034-cli-compliance-migration-implementation.md +58 -0
- package/docs/todo/rfc-0034-cli-compliance-migration.md +151 -0
- package/docs/todo/todo.xdocs.md +6 -2
- package/docs/validation/rfc-0034-cli-compliance-migration.md +67 -0
- package/docs/validation/upgrade-reliability.md +124 -0
- package/docs/validation/validation.xdocs.md +5 -0
- package/library/agents.d.ts +28 -4
- package/library/agents.d.ts.map +1 -1
- package/library/agents.js +143 -41
- package/library/cli.d.ts.map +1 -1
- package/library/cli.js +296 -334
- package/library/configuration.d.ts +57 -0
- package/library/configuration.d.ts.map +1 -0
- package/library/configuration.js +111 -0
- package/library/embedded-resources.d.ts +6 -1
- package/library/embedded-resources.d.ts.map +1 -1
- package/library/embedded-resources.js +10 -4
- package/library/executor.d.ts +5 -1
- package/library/executor.d.ts.map +1 -1
- package/library/executor.js +10 -10
- package/library/help.d.ts +8 -4
- package/library/help.d.ts.map +1 -1
- package/library/help.js +70 -46
- package/library/init.d.ts +8 -27
- package/library/init.d.ts.map +1 -1
- package/library/init.js +37 -159
- package/library/manifest.d.ts +4 -43
- package/library/manifest.d.ts.map +1 -1
- package/library/manifest.js +4 -125
- package/library/path-utils.d.ts +13 -0
- package/library/path-utils.d.ts.map +1 -0
- package/library/path-utils.js +82 -0
- package/library/recovery.d.ts +7 -0
- package/library/recovery.d.ts.map +1 -0
- package/library/recovery.js +23 -0
- package/library/release-catalog.d.ts +32 -0
- package/library/release-catalog.d.ts.map +1 -0
- package/library/release-catalog.js +124 -0
- package/library/self-management.d.ts +24 -4
- package/library/self-management.d.ts.map +1 -1
- package/library/self-management.js +279 -99
- package/library/storage.d.ts +13 -0
- package/library/storage.d.ts.map +1 -0
- package/library/storage.js +38 -0
- package/library/types.d.ts +11 -16
- package/library/types.d.ts.map +1 -1
- package/library/types.js +3 -0
- package/library/update-cache.d.ts +21 -0
- package/library/update-cache.d.ts.map +1 -0
- package/library/update-cache.js +68 -0
- package/library/upgrade-reporting.d.ts +11 -0
- package/library/upgrade-reporting.d.ts.map +1 -0
- package/library/upgrade-reporting.js +67 -0
- package/library/upgrade-types.d.ts +72 -0
- package/library/upgrade-types.d.ts.map +1 -0
- package/library/upgrade-types.js +3 -0
- package/package.json +6 -3
- package/scripts/runx-bin.mjs +49 -0
- package/scripts/runx-bin.spec.ts +62 -0
- package/scripts/scripts.xdocs.md +3 -3
- package/skills/guiho-s-runx/SKILL.md +64 -59
- package/skills/guiho-s-runx/guiho-s-runx.xdocs.md +7 -4
- package/skills/skills.xdocs.md +1 -1
- package/docs/todo/implement-runx-alpha.md +0 -36
- package/scripts/runx-bin.ts +0 -24
package/library/init.js
CHANGED
|
@@ -1,165 +1,43 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @copyright Copyright © 2026 GUIHO Technologies as represented by Cristóvão GUIHO. All Rights Reserved.
|
|
3
3
|
*/
|
|
4
|
-
import { randomUUID } from 'node:crypto';
|
|
5
|
-
import { rename, rm, writeFile } from 'node:fs/promises';
|
|
6
|
-
import { basename, isAbsolute, relative, resolve } from 'node:path';
|
|
7
|
-
import { createInterface } from 'node:readline/promises';
|
|
8
4
|
import { RunXError } from './errors.js';
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
export
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const overwrite = await activePrompter.confirm({
|
|
23
|
-
message: 'runx.yaml already exists. Replace it?',
|
|
24
|
-
initialValue: false,
|
|
25
|
-
});
|
|
26
|
-
if (overwrite !== true)
|
|
27
|
-
return cancelInitialization(activePrompter);
|
|
28
|
-
}
|
|
29
|
-
const projectName = await activePrompter.text({
|
|
30
|
-
message: 'Project name',
|
|
31
|
-
initialValue: defaultProjectName,
|
|
32
|
-
validate: (value) => value?.trim() ? undefined : 'Enter a project name.',
|
|
33
|
-
});
|
|
34
|
-
if (projectName === undefined)
|
|
35
|
-
return cancelInitialization(activePrompter);
|
|
36
|
-
const scriptsDirectory = await activePrompter.text({
|
|
37
|
-
message: 'Scripts directory',
|
|
38
|
-
initialValue: 'scripts',
|
|
39
|
-
validate: (value) => validateScriptsDirectory(value, root),
|
|
40
|
-
});
|
|
41
|
-
if (scriptsDirectory === undefined)
|
|
42
|
-
return cancelInitialization(activePrompter);
|
|
43
|
-
const manifest = createInitialManifest(projectName.trim(), scriptsDirectory.trim());
|
|
44
|
-
const yaml = renderInitialManifest(manifest);
|
|
45
|
-
activePrompter.preview(yaml);
|
|
46
|
-
const confirmed = await activePrompter.confirm({
|
|
47
|
-
message: 'Create this runx.yaml?',
|
|
48
|
-
initialValue: true,
|
|
49
|
-
});
|
|
50
|
-
if (confirmed !== true)
|
|
51
|
-
return cancelInitialization(activePrompter);
|
|
52
|
-
await writeValidatedManifest(root, path, yaml);
|
|
53
|
-
activePrompter.outro(`Created ${path}`);
|
|
54
|
-
return { status: 'created', path, manifest };
|
|
55
|
-
}
|
|
56
|
-
finally {
|
|
57
|
-
activePrompter.close?.();
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
|
-
export const createInitialManifest = (projectName, scriptsDirectory) => ({
|
|
61
|
-
version: manifestVersion,
|
|
62
|
-
project: { name: projectName },
|
|
63
|
-
scripts: { directory: scriptsDirectory },
|
|
64
|
-
groups: {
|
|
65
|
-
public: { summary: 'Default public project commands.' },
|
|
66
|
-
},
|
|
67
|
-
commands: [],
|
|
68
|
-
});
|
|
69
|
-
export const renderInitialManifest = (manifest) => [
|
|
70
|
-
`version: ${JSON.stringify(manifest.version)}`,
|
|
71
|
-
'',
|
|
72
|
-
'project:',
|
|
73
|
-
` name: ${JSON.stringify(manifest.project?.name ?? '')}`,
|
|
74
|
-
'',
|
|
75
|
-
'scripts:',
|
|
76
|
-
` directory: ${JSON.stringify(manifest.scripts.directory)}`,
|
|
77
|
-
'',
|
|
78
|
-
'groups:',
|
|
79
|
-
' public:',
|
|
80
|
-
` summary: ${JSON.stringify(manifest.groups.public.summary)}`,
|
|
81
|
-
'',
|
|
82
|
-
'commands: []',
|
|
83
|
-
'',
|
|
84
|
-
].join('\n');
|
|
85
|
-
const createTerminalPrompter = () => {
|
|
86
|
-
const terminal = createInterface({ input: process.stdin, output: process.stdout, terminal: true });
|
|
87
|
-
const accent = (value) => process.env['NO_COLOR'] ? value : `\u001b[36m${value}\u001b[0m`;
|
|
88
|
-
const success = (value) => process.env['NO_COLOR'] ? value : `\u001b[32m${value}\u001b[0m`;
|
|
89
|
-
const muted = (value) => process.env['NO_COLOR'] ? value : `\u001b[2m${value}\u001b[0m`;
|
|
90
|
-
const error = (value) => process.env['NO_COLOR'] ? value : `\u001b[31m${value}\u001b[0m`;
|
|
91
|
-
const write = (value) => {
|
|
92
|
-
process.stdout.write(value);
|
|
93
|
-
};
|
|
94
|
-
const ask = async (label) => {
|
|
95
|
-
try {
|
|
96
|
-
return await terminal.question(label);
|
|
97
|
-
}
|
|
98
|
-
catch {
|
|
99
|
-
return undefined;
|
|
100
|
-
}
|
|
101
|
-
};
|
|
5
|
+
import { baseName, resolvePath } from './path-utils.js';
|
|
6
|
+
import { writeTextFile } from './storage.js';
|
|
7
|
+
export { createInitialManifest, initializeRunXManifest, renderInitialManifest, };
|
|
8
|
+
async function initializeRunXManifest(options) {
|
|
9
|
+
const cwd = resolvePath(options.cwd);
|
|
10
|
+
const path = resolvePath(cwd, options.config ?? 'runx.yaml');
|
|
11
|
+
if (await Bun.file(path).exists())
|
|
12
|
+
throw new RunXError(`Configuration already exists: ${path}`, 5);
|
|
13
|
+
const manifest = createInitialManifest(baseName(cwd) || 'my-project', 'scripts');
|
|
14
|
+
await writeTextFile(path, renderInitialManifest(manifest));
|
|
15
|
+
return { status: 'created', path, manifest };
|
|
16
|
+
}
|
|
17
|
+
function createInitialManifest(projectName, scriptsDirectory) {
|
|
102
18
|
return {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
return undefined;
|
|
109
|
-
const selected = value.trim() || options.initialValue;
|
|
110
|
-
const validation = options.validate(selected);
|
|
111
|
-
if (!validation)
|
|
112
|
-
return selected;
|
|
113
|
-
write(`${error('▲')} ${validation}\n`);
|
|
114
|
-
}
|
|
115
|
-
},
|
|
116
|
-
confirm: async (options) => {
|
|
117
|
-
while (true) {
|
|
118
|
-
const defaultLabel = options.initialValue ? 'Y/n' : 'y/N';
|
|
119
|
-
const value = await ask(`${accent('◆')} ${options.message} ${muted(`[${defaultLabel}]`)}\n `);
|
|
120
|
-
if (value === undefined)
|
|
121
|
-
return undefined;
|
|
122
|
-
const answer = value.trim().toLowerCase();
|
|
123
|
-
if (!answer)
|
|
124
|
-
return options.initialValue;
|
|
125
|
-
if (answer === 'y' || answer === 'yes')
|
|
126
|
-
return true;
|
|
127
|
-
if (answer === 'n' || answer === 'no')
|
|
128
|
-
return false;
|
|
129
|
-
write(`${error('▲')} Enter yes or no.\n`);
|
|
130
|
-
}
|
|
131
|
-
},
|
|
132
|
-
preview: (manifest) => write(`\n${accent('┌─ runx.yaml preview ─────────────────────────────────')}\n${manifest.trimEnd().split('\n').map((line) => `${accent('│')} ${line}`).join('\n')}\n${accent('└────────────────────────────────────────────────────')}\n\n`),
|
|
133
|
-
cancel: (message) => write(`${error('■')} ${message}\n`),
|
|
134
|
-
outro: (message) => {
|
|
135
|
-
write(`${success('◆')} ${message}\n`);
|
|
136
|
-
},
|
|
137
|
-
close: () => terminal.close(),
|
|
19
|
+
version: '1.0.0',
|
|
20
|
+
project: { name: projectName },
|
|
21
|
+
scripts: { directory: scriptsDirectory },
|
|
22
|
+
groups: { public: { summary: 'Default public project commands.' } },
|
|
23
|
+
commands: [],
|
|
138
24
|
};
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
await writeFile(temporaryPath, yaml, { encoding: 'utf8', flag: 'wx' });
|
|
159
|
-
await readManifest(root, temporaryPath);
|
|
160
|
-
await rename(temporaryPath, path);
|
|
161
|
-
}
|
|
162
|
-
finally {
|
|
163
|
-
await rm(temporaryPath, { force: true });
|
|
164
|
-
}
|
|
165
|
-
};
|
|
25
|
+
}
|
|
26
|
+
function renderInitialManifest(manifest) {
|
|
27
|
+
return [
|
|
28
|
+
`version: ${JSON.stringify(manifest.version)}`,
|
|
29
|
+
'',
|
|
30
|
+
'project:',
|
|
31
|
+
` name: ${JSON.stringify(manifest.project?.name ?? '')}`,
|
|
32
|
+
'',
|
|
33
|
+
'scripts:',
|
|
34
|
+
` directory: ${JSON.stringify(manifest.scripts.directory)}`,
|
|
35
|
+
'',
|
|
36
|
+
'groups:',
|
|
37
|
+
' public:',
|
|
38
|
+
` summary: ${JSON.stringify(manifest.groups.public.summary)}`,
|
|
39
|
+
'',
|
|
40
|
+
'commands: []',
|
|
41
|
+
'',
|
|
42
|
+
].join('\n');
|
|
43
|
+
}
|
package/library/manifest.d.ts
CHANGED
|
@@ -1,44 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
group: import("@sinclair/typebox").TString;
|
|
6
|
-
summary: import("@sinclair/typebox").TString;
|
|
7
|
-
description: import("@sinclair/typebox").TString;
|
|
8
|
-
command: import("@sinclair/typebox").TString;
|
|
9
|
-
cwd: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
10
|
-
shell: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"auto">, import("@sinclair/typebox").TLiteral<"bash">, import("@sinclair/typebox").TLiteral<"sh">, import("@sinclair/typebox").TLiteral<"powershell">, import("@sinclair/typebox").TLiteral<"cmd">]>>;
|
|
11
|
-
tags: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>>;
|
|
12
|
-
confirm: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"never">, import("@sinclair/typebox").TLiteral<"always">]>>;
|
|
13
|
-
}>;
|
|
14
|
-
export declare const ManifestSchema: import("@sinclair/typebox").TObject<{
|
|
15
|
-
version: import("@sinclair/typebox").TString;
|
|
16
|
-
project: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
|
|
17
|
-
name: import("@sinclair/typebox").TString;
|
|
18
|
-
}>>;
|
|
19
|
-
scripts: import("@sinclair/typebox").TObject<{
|
|
20
|
-
directory: import("@sinclair/typebox").TString;
|
|
21
|
-
}>;
|
|
22
|
-
groups: import("@sinclair/typebox").TRecord<import("@sinclair/typebox").TString, import("@sinclair/typebox").TObject<{
|
|
23
|
-
summary: import("@sinclair/typebox").TString;
|
|
24
|
-
}>>;
|
|
25
|
-
commands: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
|
|
26
|
-
uid: import("@sinclair/typebox").TString;
|
|
27
|
-
id: import("@sinclair/typebox").TString;
|
|
28
|
-
group: import("@sinclair/typebox").TString;
|
|
29
|
-
summary: import("@sinclair/typebox").TString;
|
|
30
|
-
description: import("@sinclair/typebox").TString;
|
|
31
|
-
command: import("@sinclair/typebox").TString;
|
|
32
|
-
cwd: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
33
|
-
shell: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"auto">, import("@sinclair/typebox").TLiteral<"bash">, import("@sinclair/typebox").TLiteral<"sh">, import("@sinclair/typebox").TLiteral<"powershell">, import("@sinclair/typebox").TLiteral<"cmd">]>>;
|
|
34
|
-
tags: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>>;
|
|
35
|
-
confirm: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"never">, import("@sinclair/typebox").TLiteral<"always">]>>;
|
|
36
|
-
}>>;
|
|
37
|
-
}>;
|
|
38
|
-
export declare const findManifest: (cwd: string, explicitFile?: string) => Promise<string>;
|
|
39
|
-
export declare const readManifest: (cwd: string, explicitFile?: string) => Promise<{
|
|
40
|
-
manifest: RunXManifest;
|
|
41
|
-
path: string;
|
|
42
|
-
}>;
|
|
43
|
-
export declare const resolveCommand: (manifest: RunXManifest, manifestPath: string, selector: string) => ResolvedCommand;
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Copyright © 2026 GUIHO Technologies as represented by Cristóvão GUIHO. All Rights Reserved.
|
|
3
|
+
*/
|
|
4
|
+
export { commandSchema as CommandSchema, findConfiguration as findManifest, manifestSchema as ManifestSchema, readManifest, resolveCommand, } from './configuration.js';
|
|
44
5
|
//# sourceMappingURL=manifest.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../source/manifest.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../source/manifest.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,aAAa,IAAI,aAAa,EAC9B,iBAAiB,IAAI,YAAY,EACjC,cAAc,IAAI,cAAc,EAChC,YAAY,EACZ,cAAc,GACf,MAAM,oBAAoB,CAAA"}
|
package/library/manifest.js
CHANGED
|
@@ -1,125 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const identifier = '^[a-z][a-z0-9-]*$';
|
|
6
|
-
const semver = '^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$';
|
|
7
|
-
export const CommandSchema = Type.Object({
|
|
8
|
-
uid: Type.String({ pattern: identifier }),
|
|
9
|
-
id: Type.String({ pattern: identifier }),
|
|
10
|
-
group: Type.String({ pattern: identifier }),
|
|
11
|
-
summary: Type.String({ minLength: 1 }),
|
|
12
|
-
description: Type.String({ minLength: 1 }),
|
|
13
|
-
command: Type.String({ minLength: 1 }),
|
|
14
|
-
cwd: Type.Optional(Type.String({ minLength: 1 })),
|
|
15
|
-
shell: Type.Optional(Type.Union([
|
|
16
|
-
Type.Literal('auto'),
|
|
17
|
-
Type.Literal('bash'),
|
|
18
|
-
Type.Literal('sh'),
|
|
19
|
-
Type.Literal('powershell'),
|
|
20
|
-
Type.Literal('cmd'),
|
|
21
|
-
])),
|
|
22
|
-
tags: Type.Optional(Type.Array(Type.String({ minLength: 1 }))),
|
|
23
|
-
confirm: Type.Optional(Type.Union([Type.Literal('never'), Type.Literal('always')])),
|
|
24
|
-
}, { additionalProperties: false });
|
|
25
|
-
export const ManifestSchema = Type.Object({
|
|
26
|
-
version: Type.String({ pattern: semver }),
|
|
27
|
-
project: Type.Optional(Type.Object({ name: Type.String({ minLength: 1 }) }, { additionalProperties: false })),
|
|
28
|
-
scripts: Type.Object({ directory: Type.String({ minLength: 1 }) }, { additionalProperties: false }),
|
|
29
|
-
groups: Type.Record(Type.String({ pattern: identifier }), Type.Object({ summary: Type.String({ minLength: 1 }) }, { additionalProperties: false })),
|
|
30
|
-
commands: Type.Array(CommandSchema),
|
|
31
|
-
}, { additionalProperties: false });
|
|
32
|
-
const manifestCheck = TypeCompiler.Compile(ManifestSchema);
|
|
33
|
-
export const findManifest = async (cwd, explicitFile) => {
|
|
34
|
-
if (explicitFile) {
|
|
35
|
-
const candidate = resolve(cwd, explicitFile);
|
|
36
|
-
if (await Bun.file(candidate).exists())
|
|
37
|
-
return candidate;
|
|
38
|
-
throw new RunXError(`Manifest not found: ${candidate}`);
|
|
39
|
-
}
|
|
40
|
-
let current = resolve(cwd);
|
|
41
|
-
while (true) {
|
|
42
|
-
const candidate = resolve(current, 'runx.yaml');
|
|
43
|
-
if (await Bun.file(candidate).exists())
|
|
44
|
-
return candidate;
|
|
45
|
-
const parent = dirname(current);
|
|
46
|
-
if (parent === current)
|
|
47
|
-
break;
|
|
48
|
-
current = parent;
|
|
49
|
-
}
|
|
50
|
-
throw new RunXError('No runx.yaml found. Run this command inside a configured project or pass --file <path>.');
|
|
51
|
-
};
|
|
52
|
-
export const readManifest = async (cwd, explicitFile) => {
|
|
53
|
-
const path = await findManifest(cwd, explicitFile);
|
|
54
|
-
let parsed;
|
|
55
|
-
try {
|
|
56
|
-
parsed = Bun.YAML.parse(await Bun.file(path).text());
|
|
57
|
-
}
|
|
58
|
-
catch (error) {
|
|
59
|
-
const message = error instanceof Error ? error.message : 'Unknown YAML parsing error.';
|
|
60
|
-
throw new RunXError(`Invalid YAML in ${path}: ${message}`);
|
|
61
|
-
}
|
|
62
|
-
if (!manifestCheck.Check(parsed)) {
|
|
63
|
-
const errors = [...manifestCheck.Errors(parsed)].map((error) => `${error.path || '/'}: ${error.message}`);
|
|
64
|
-
throw new RunXError(`Invalid RunX manifest ${path}:\n${errors.join('\n')}`);
|
|
65
|
-
}
|
|
66
|
-
const manifest = parsed;
|
|
67
|
-
validateManifestSemantics(manifest, path);
|
|
68
|
-
return { manifest, path };
|
|
69
|
-
};
|
|
70
|
-
export const resolveCommand = (manifest, manifestPath, selector) => {
|
|
71
|
-
const commands = manifest.commands;
|
|
72
|
-
let command = commands.find((entry) => entry.uid === selector);
|
|
73
|
-
if (!command && selector.includes('/')) {
|
|
74
|
-
command = commands.find((entry) => `${entry.group}/${entry.id}` === selector);
|
|
75
|
-
}
|
|
76
|
-
if (!command && /^\d+$/.test(selector)) {
|
|
77
|
-
command = commands[Number.parseInt(selector, 10) - 1];
|
|
78
|
-
}
|
|
79
|
-
if (!command) {
|
|
80
|
-
const matches = commands.filter((entry) => entry.id === selector);
|
|
81
|
-
if (matches.length > 1) {
|
|
82
|
-
throw new RunXError(`Ambiguous command ID "${selector}". Use one of: ${matches.map((entry) => `${entry.group}/${entry.id} (${entry.uid})`).join(', ')}`);
|
|
83
|
-
}
|
|
84
|
-
command = matches[0];
|
|
85
|
-
}
|
|
86
|
-
if (!command)
|
|
87
|
-
throw new RunXError(`Unknown command selector: ${selector}`);
|
|
88
|
-
const manifestDirectory = dirname(manifestPath);
|
|
89
|
-
const commandCwd = resolve(manifestDirectory, command.cwd ?? '.');
|
|
90
|
-
const relativeCwd = relative(manifestDirectory, commandCwd);
|
|
91
|
-
invariant(!isAbsolute(relativeCwd) && !relativeCwd.startsWith('..'), `Command ${command.uid} has a cwd outside the manifest directory.`);
|
|
92
|
-
return {
|
|
93
|
-
...command,
|
|
94
|
-
index: commands.indexOf(command) + 1,
|
|
95
|
-
selector: `${command.group}/${command.id}`,
|
|
96
|
-
manifestPath,
|
|
97
|
-
cwd: commandCwd,
|
|
98
|
-
};
|
|
99
|
-
};
|
|
100
|
-
const validateManifestSemantics = (manifest, path) => {
|
|
101
|
-
const versionMajor = manifest.version.split('.', 1)[0];
|
|
102
|
-
if (versionMajor !== '1') {
|
|
103
|
-
throw new RunXError(`Unsupported RunX manifest version "${manifest.version}" in ${path}. Supported major version: 1.`);
|
|
104
|
-
}
|
|
105
|
-
const manifestDirectory = dirname(path);
|
|
106
|
-
const scriptsDirectory = resolve(manifestDirectory, manifest.scripts.directory);
|
|
107
|
-
const relativeScriptsDirectory = relative(manifestDirectory, scriptsDirectory);
|
|
108
|
-
invariant(relativeScriptsDirectory !== '' && relativeScriptsDirectory !== '.' && !isAbsolute(relativeScriptsDirectory) && !relativeScriptsDirectory.startsWith('..'), `Scripts directory "${manifest.scripts.directory}" must be a relative subdirectory inside the manifest directory in ${path}.`);
|
|
109
|
-
if (!manifest.groups.public) {
|
|
110
|
-
throw new RunXError(`RunX manifest ${path} must define the default "public" group.`);
|
|
111
|
-
}
|
|
112
|
-
const uids = new Set();
|
|
113
|
-
const selectors = new Set();
|
|
114
|
-
for (const command of manifest.commands) {
|
|
115
|
-
if (!manifest.groups[command.group])
|
|
116
|
-
throw new RunXError(`Command ${command.uid} references unknown group "${command.group}" in ${path}.`);
|
|
117
|
-
if (uids.has(command.uid))
|
|
118
|
-
throw new RunXError(`Duplicate command UID "${command.uid}" in ${path}.`);
|
|
119
|
-
const selector = `${command.group}/${command.id}`;
|
|
120
|
-
if (selectors.has(selector))
|
|
121
|
-
throw new RunXError(`Duplicate command selector "${selector}" in ${path}.`);
|
|
122
|
-
uids.add(command.uid);
|
|
123
|
-
selectors.add(selector);
|
|
124
|
-
}
|
|
125
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Copyright © 2026 GUIHO Technologies as represented by Cristóvão GUIHO. All Rights Reserved.
|
|
3
|
+
*/
|
|
4
|
+
export { commandSchema as CommandSchema, findConfiguration as findManifest, manifestSchema as ManifestSchema, readManifest, resolveCommand, } from './configuration.js';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Copyright © 2026 GUIHO Technologies as represented by Cristóvão GUIHO. All Rights Reserved.
|
|
3
|
+
*/
|
|
4
|
+
export { baseName, directoryName, homeDirectory, isAbsolutePath, joinPath, normalizePath, relativePath, resolvePath, };
|
|
5
|
+
declare function normalizePath(value: string): string;
|
|
6
|
+
declare function isAbsolutePath(value: string): boolean;
|
|
7
|
+
declare function resolvePath(...values: string[]): string;
|
|
8
|
+
declare function joinPath(...values: string[]): string;
|
|
9
|
+
declare function directoryName(value: string): string;
|
|
10
|
+
declare function baseName(value: string): string;
|
|
11
|
+
declare function relativePath(from: string, to: string): string;
|
|
12
|
+
declare function homeDirectory(): string;
|
|
13
|
+
//# sourceMappingURL=path-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path-utils.d.ts","sourceRoot":"","sources":["../source/path-utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,QAAQ,EACR,aAAa,EACb,aAAa,EACb,cAAc,EACd,QAAQ,EACR,aAAa,EACb,YAAY,EACZ,WAAW,GACZ,CAAA;AAQD,iBAAS,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAoB5C;AAED,iBAAS,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE9C;AAED,iBAAS,WAAW,CAAC,GAAG,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAQhD;AAED,iBAAS,QAAQ,CAAC,GAAG,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAE7C;AAED,iBAAS,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQ5C;AAED,iBAAS,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIvC;AAED,iBAAS,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAYtD;AAED,iBAAS,aAAa,IAAI,MAAM,CAI/B"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Copyright © 2026 GUIHO Technologies as represented by Cristóvão GUIHO. All Rights Reserved.
|
|
3
|
+
*/
|
|
4
|
+
export { baseName, directoryName, homeDirectory, isAbsolutePath, joinPath, normalizePath, relativePath, resolvePath, };
|
|
5
|
+
const windowsDrivePattern = /^[A-Za-z]:[\\/]/;
|
|
6
|
+
function pathSeparator(value = process.platform) {
|
|
7
|
+
return value === 'win32' ? '\\' : '/';
|
|
8
|
+
}
|
|
9
|
+
function normalizePath(value) {
|
|
10
|
+
const separator = pathSeparator();
|
|
11
|
+
const normalized = value.replace(/[\\/]+/g, separator);
|
|
12
|
+
const drive = windowsDrivePattern.test(normalized) ? normalized.slice(0, 2) : '';
|
|
13
|
+
const rooted = drive !== '' || normalized.startsWith(separator);
|
|
14
|
+
const parts = normalized.slice(drive.length).split(separator);
|
|
15
|
+
const output = [];
|
|
16
|
+
for (const part of parts) {
|
|
17
|
+
if (!part || part === '.')
|
|
18
|
+
continue;
|
|
19
|
+
if (part === '..') {
|
|
20
|
+
if (output.length > 0 && output.at(-1) !== '..')
|
|
21
|
+
output.pop();
|
|
22
|
+
else if (!rooted)
|
|
23
|
+
output.push(part);
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
output.push(part);
|
|
27
|
+
}
|
|
28
|
+
const prefix = drive ? `${drive}${separator}` : rooted ? separator : '';
|
|
29
|
+
return `${prefix}${output.join(separator)}` || (rooted ? prefix : '.');
|
|
30
|
+
}
|
|
31
|
+
function isAbsolutePath(value) {
|
|
32
|
+
return windowsDrivePattern.test(value) || value.startsWith('/') || value.startsWith('\\');
|
|
33
|
+
}
|
|
34
|
+
function resolvePath(...values) {
|
|
35
|
+
let current = '';
|
|
36
|
+
for (const value of values) {
|
|
37
|
+
if (!value)
|
|
38
|
+
continue;
|
|
39
|
+
current = isAbsolutePath(value) ? value : current ? joinPath(current, value) : value;
|
|
40
|
+
}
|
|
41
|
+
if (!isAbsolutePath(current))
|
|
42
|
+
current = joinPath(process.cwd(), current);
|
|
43
|
+
return normalizePath(current);
|
|
44
|
+
}
|
|
45
|
+
function joinPath(...values) {
|
|
46
|
+
return normalizePath(values.filter(Boolean).join(pathSeparator()));
|
|
47
|
+
}
|
|
48
|
+
function directoryName(value) {
|
|
49
|
+
const normalized = normalizePath(value);
|
|
50
|
+
const separator = pathSeparator();
|
|
51
|
+
const index = normalized.lastIndexOf(separator);
|
|
52
|
+
if (index < 0)
|
|
53
|
+
return '.';
|
|
54
|
+
if (index === 0)
|
|
55
|
+
return separator;
|
|
56
|
+
if (index === 2 && windowsDrivePattern.test(normalized))
|
|
57
|
+
return normalized.slice(0, 3);
|
|
58
|
+
return normalized.slice(0, index);
|
|
59
|
+
}
|
|
60
|
+
function baseName(value) {
|
|
61
|
+
const normalized = normalizePath(value);
|
|
62
|
+
const index = normalized.lastIndexOf(pathSeparator());
|
|
63
|
+
return index < 0 ? normalized : normalized.slice(index + 1);
|
|
64
|
+
}
|
|
65
|
+
function relativePath(from, to) {
|
|
66
|
+
const separator = pathSeparator();
|
|
67
|
+
const fromParts = normalizePath(from).split(separator);
|
|
68
|
+
const toParts = normalizePath(to).split(separator);
|
|
69
|
+
const insensitive = process.platform === 'win32';
|
|
70
|
+
let index = 0;
|
|
71
|
+
while (index < fromParts.length
|
|
72
|
+
&& index < toParts.length
|
|
73
|
+
&& (insensitive ? fromParts[index].toLowerCase() === toParts[index].toLowerCase() : fromParts[index] === toParts[index]))
|
|
74
|
+
index += 1;
|
|
75
|
+
return [...Array(fromParts.length - index).fill('..'), ...toParts.slice(index)].join(separator) || '.';
|
|
76
|
+
}
|
|
77
|
+
function homeDirectory() {
|
|
78
|
+
const home = Bun.env.HOME ?? Bun.env.USERPROFILE;
|
|
79
|
+
if (!home)
|
|
80
|
+
throw new Error('RunX requires HOME or USERPROFILE to resolve global storage.');
|
|
81
|
+
return resolvePath(home);
|
|
82
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Copyright © 2026 GUIHO Technologies as represented by Cristóvão GUIHO. All Rights Reserved.
|
|
3
|
+
*/
|
|
4
|
+
import type { RecoveryInstructions, UpgradeOs } from './upgrade-types.js';
|
|
5
|
+
export { createRecoveryInstructions };
|
|
6
|
+
declare const createRecoveryInstructions: (targetVersion: string, os: UpgradeOs, targetSource?: RecoveryInstructions['targetSource'], installerBaseUrl?: string) => RecoveryInstructions;
|
|
7
|
+
//# sourceMappingURL=recovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recovery.d.ts","sourceRoot":"","sources":["../source/recovery.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAEzE,OAAO,EAAE,0BAA0B,EAAE,CAAA;AAIrC,QAAA,MAAM,0BAA0B,kBAAmB,MAAM,MAAM,SAAS,iBAAgB,oBAAoB,CAAC,cAAc,CAAC,gCAA4C,oBAevK,CAAA"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Copyright © 2026 GUIHO Technologies as represented by Cristóvão GUIHO. All Rights Reserved.
|
|
3
|
+
*/
|
|
4
|
+
export { createRecoveryInstructions };
|
|
5
|
+
const rawBase = 'https://raw.githubusercontent.com/CGuiho/runx/main/devops';
|
|
6
|
+
const createRecoveryInstructions = (targetVersion, os, targetSource = 'resolved', installerBaseUrl = rawBase) => {
|
|
7
|
+
if (os === 'windows') {
|
|
8
|
+
return {
|
|
9
|
+
targetVersion,
|
|
10
|
+
targetSource,
|
|
11
|
+
installCommand: `powershell.exe -NoProfile -ExecutionPolicy Bypass -Command '& ([scriptblock]::Create((Invoke-RestMethod "${installerBaseUrl}/install.ps1"))) -Version "${escapePowerShell(targetVersion)}"'`,
|
|
12
|
+
stopProcessCommand: 'powershell.exe -NoProfile -Command "Get-Process runx -ErrorAction SilentlyContinue | Stop-Process -Force"',
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
return {
|
|
16
|
+
targetVersion,
|
|
17
|
+
targetSource,
|
|
18
|
+
installCommand: `curl -fsSL ${installerBaseUrl}/install.sh | sh -s -- --version '${escapeShell(targetVersion)}'`,
|
|
19
|
+
stopProcessCommand: 'pkill -x runx',
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
const escapePowerShell = (value) => value.replace(/`/g, '``').replace(/"/g, '`"');
|
|
23
|
+
const escapeShell = (value) => value.replace(/'/g, `'"'"'`);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Copyright © 2026 GUIHO Technologies as represented by Cristóvão GUIHO. All Rights Reserved.
|
|
3
|
+
*/
|
|
4
|
+
import type { ReleaseAsset, ReleaseCatalog, UpgradeArch, UpgradeOs, UpgradeVariant } from './upgrade-types.js';
|
|
5
|
+
export { assetCandidates, fetchReleaseCatalog, findCompatibleAsset, normalizeReleaseVersion, resolveUpgradePlatform, };
|
|
6
|
+
declare const githubReleaseSchema: import("@sinclair/typebox").TObject<{
|
|
7
|
+
tag_name: import("@sinclair/typebox").TString;
|
|
8
|
+
draft: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
9
|
+
prerelease: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
10
|
+
published_at: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TString, import("@sinclair/typebox").TNull]>>;
|
|
11
|
+
created_at: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TString, import("@sinclair/typebox").TNull]>>;
|
|
12
|
+
assets: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
|
|
13
|
+
name: import("@sinclair/typebox").TString;
|
|
14
|
+
browser_download_url: import("@sinclair/typebox").TString;
|
|
15
|
+
}>>;
|
|
16
|
+
}>;
|
|
17
|
+
export type GitHubRelease = import('@sinclair/typebox').Static<typeof githubReleaseSchema>;
|
|
18
|
+
export type ReleasePlatform = {
|
|
19
|
+
os: UpgradeOs;
|
|
20
|
+
arch: UpgradeArch;
|
|
21
|
+
variant: UpgradeVariant;
|
|
22
|
+
};
|
|
23
|
+
type FetchCatalogOptions = ReleasePlatform & {
|
|
24
|
+
currentVersion: string;
|
|
25
|
+
fetchImpl?: typeof fetch;
|
|
26
|
+
};
|
|
27
|
+
declare const fetchReleaseCatalog: (options: FetchCatalogOptions) => Promise<ReleaseCatalog>;
|
|
28
|
+
declare const findCompatibleAsset: (release: GitHubRelease, platform: ReleasePlatform) => ReleaseAsset | null;
|
|
29
|
+
declare const assetCandidates: ({ os, arch, variant }: ReleasePlatform) => string[];
|
|
30
|
+
declare const resolveUpgradePlatform: (platform?: NodeJS.Platform, architecture?: NodeJS.Architecture) => ReleasePlatform;
|
|
31
|
+
declare const normalizeReleaseVersion: (tag: string) => string;
|
|
32
|
+
//# sourceMappingURL=release-catalog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"release-catalog.d.ts","sourceRoot":"","sources":["../source/release-catalog.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAuB,WAAW,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAEnI,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,mBAAmB,EACnB,uBAAuB,EACvB,sBAAsB,GACvB,CAAA;AAED,QAAA,MAAM,mBAAmB;;;;;;;;;;EAUS,CAAA;AAIlC,MAAM,MAAM,aAAa,GAAG,OAAO,mBAAmB,EAAE,MAAM,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAE1F,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,SAAS,CAAA;IACb,IAAI,EAAE,WAAW,CAAA;IACjB,OAAO,EAAE,cAAc,CAAA;CACxB,CAAA;AAED,KAAK,mBAAmB,GAAG,eAAe,GAAG;IAC3C,cAAc,EAAE,MAAM,CAAA;IACtB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;CACzB,CAAA;AAID,QAAA,MAAM,mBAAmB,YAAmB,mBAAmB,KAAG,OAAO,CAAC,cAAc,CAoCvF,CAAA;AAkBD,QAAA,MAAM,mBAAmB,YAAa,aAAa,YAAY,eAAe,KAAG,YAAY,GAAG,IAM/F,CAAA;AAED,QAAA,MAAM,eAAe,0BAA2B,eAAe,KAAG,MAAM,EAUvE,CAAA;AAED,QAAA,MAAM,sBAAsB,sEAA+D,eAM1F,CAAA;AAED,QAAA,MAAM,uBAAuB,QAAS,MAAM,KAAG,MAA6D,CAAA"}
|