@theholocron/cli 2.0.0-alpha.20 → 2.0.0-alpha.21
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 +14 -14
- package/dist/capabilities/index.d.mts +2 -12
- package/dist/capabilities/index.mjs +1 -16
- package/dist/cli.mjs +258 -191
- package/dist/index.d.mts +8 -1
- package/dist/index.mjs +82 -71
- package/package.json +7 -6
package/README.md
CHANGED
|
@@ -19,11 +19,11 @@ Holocron reads `holocron.config.{json,js,ts}` from the project root
|
|
|
19
19
|
```jsonc
|
|
20
20
|
// holocron.config.json
|
|
21
21
|
{
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
"project": { "name": "my-app" },
|
|
23
|
+
"providers": {
|
|
24
|
+
"vault": ["1password", { "vault": "my-app" }],
|
|
25
|
+
"source": "github"
|
|
26
|
+
}
|
|
27
27
|
}
|
|
28
28
|
```
|
|
29
29
|
|
|
@@ -33,11 +33,11 @@ Holocron reads `holocron.config.{json,js,ts}` from the project root
|
|
|
33
33
|
import { defineConfig } from '@theholocron/cli'
|
|
34
34
|
|
|
35
35
|
export default defineConfig({
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
project: { name: 'my-app' },
|
|
37
|
+
providers: {
|
|
38
|
+
vault: ['1password', { vault: 'my-app' }],
|
|
39
|
+
source: 'github',
|
|
40
|
+
},
|
|
41
41
|
})
|
|
42
42
|
```
|
|
43
43
|
|
|
@@ -50,8 +50,8 @@ top (project wins):
|
|
|
50
50
|
|
|
51
51
|
```ts
|
|
52
52
|
providers: {
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
vault: '@acme/holocron-vault', // preset only
|
|
54
|
+
source: ['@acme/holocron-github', { repo: 'x' }], // preset + override
|
|
55
55
|
}
|
|
56
56
|
```
|
|
57
57
|
|
|
@@ -59,8 +59,8 @@ A capability config package exports a `CapabilityConfigPackage` default:
|
|
|
59
59
|
```ts
|
|
60
60
|
import type { CapabilityConfigPackage } from '@theholocron/cli'
|
|
61
61
|
export default {
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
provider: '1password',
|
|
63
|
+
options: { vault: 'acme-app' },
|
|
64
64
|
} satisfies CapabilityConfigPackage
|
|
65
65
|
```
|
|
66
66
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { ProviderApiError } from "@theholocron/http-client";
|
|
2
|
+
|
|
1
3
|
//#region src/capabilities/index.d.ts
|
|
2
4
|
/**
|
|
3
5
|
* Capability interfaces — the contracts that providers implement.
|
|
@@ -39,18 +41,6 @@ interface ProviderIdentity {
|
|
|
39
41
|
readonly key: CapabilityKey;
|
|
40
42
|
readonly providerName: string;
|
|
41
43
|
}
|
|
42
|
-
/**
|
|
43
|
-
* Surfaced from every capability call that hits a vendor API. Wraps
|
|
44
|
-
* the underlying error with `status` (HTTP) and `details` so
|
|
45
|
-
* orchestrators (`holocron setup`, `doctor`) can soft-skip rather
|
|
46
|
-
* than abort.
|
|
47
|
-
*/
|
|
48
|
-
declare class ProviderApiError extends Error {
|
|
49
|
-
readonly status: number | undefined;
|
|
50
|
-
readonly details?: unknown | undefined;
|
|
51
|
-
name: string;
|
|
52
|
-
constructor(message: string, status: number | undefined, details?: unknown | undefined);
|
|
53
|
-
}
|
|
54
44
|
interface Ruleset {
|
|
55
45
|
id: number;
|
|
56
46
|
name: string;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ProviderApiError } from "@theholocron/http-client";
|
|
1
2
|
//#region src/capabilities/index.ts
|
|
2
3
|
const CARDINALITY = {
|
|
3
4
|
source: "single",
|
|
@@ -21,22 +22,6 @@ const CARDINALITY = {
|
|
|
21
22
|
* own requirements at call time.
|
|
22
23
|
*/
|
|
23
24
|
const REQUIRED_CAPABILITIES = [];
|
|
24
|
-
/**
|
|
25
|
-
* Surfaced from every capability call that hits a vendor API. Wraps
|
|
26
|
-
* the underlying error with `status` (HTTP) and `details` so
|
|
27
|
-
* orchestrators (`holocron setup`, `doctor`) can soft-skip rather
|
|
28
|
-
* than abort.
|
|
29
|
-
*/
|
|
30
|
-
var ProviderApiError = class extends Error {
|
|
31
|
-
status;
|
|
32
|
-
details;
|
|
33
|
-
name = "ProviderApiError";
|
|
34
|
-
constructor(message, status, details) {
|
|
35
|
-
super(message);
|
|
36
|
-
this.status = status;
|
|
37
|
-
this.details = details;
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
25
|
var WebhookVerificationError = class extends Error {
|
|
41
26
|
name = "WebhookVerificationError";
|
|
42
27
|
};
|
package/dist/cli.mjs
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
3
|
+
import path, { basename, dirname, join } from "node:path";
|
|
3
4
|
import yargs from "yargs";
|
|
4
5
|
import { hideBin } from "yargs/helpers";
|
|
6
|
+
import { ProviderApiError } from "@theholocron/http-client";
|
|
5
7
|
import { Entry, findCredentials } from "@napi-rs/keyring";
|
|
6
|
-
import path, { dirname, join } from "node:path";
|
|
7
8
|
import { createHash } from "node:crypto";
|
|
8
9
|
import { spawnSync } from "node:child_process";
|
|
9
10
|
import { readFile, stat } from "node:fs/promises";
|
|
@@ -31,22 +32,6 @@ const CARDINALITY = {
|
|
|
31
32
|
* own requirements at call time.
|
|
32
33
|
*/
|
|
33
34
|
const REQUIRED_CAPABILITIES = [];
|
|
34
|
-
/**
|
|
35
|
-
* Surfaced from every capability call that hits a vendor API. Wraps
|
|
36
|
-
* the underlying error with `status` (HTTP) and `details` so
|
|
37
|
-
* orchestrators (`holocron setup`, `doctor`) can soft-skip rather
|
|
38
|
-
* than abort.
|
|
39
|
-
*/
|
|
40
|
-
var ProviderApiError = class extends Error {
|
|
41
|
-
status;
|
|
42
|
-
details;
|
|
43
|
-
name = "ProviderApiError";
|
|
44
|
-
constructor(message, status, details) {
|
|
45
|
-
super(message);
|
|
46
|
-
this.status = status;
|
|
47
|
-
this.details = details;
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
35
|
//#endregion
|
|
51
36
|
//#region src/config.ts
|
|
52
37
|
/**
|
|
@@ -687,6 +672,161 @@ async function runNpmBumpVersions(input) {
|
|
|
687
672
|
};
|
|
688
673
|
}
|
|
689
674
|
//#endregion
|
|
675
|
+
//#region src/commands/upgrade-node.ts
|
|
676
|
+
const SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
677
|
+
"node_modules",
|
|
678
|
+
".git",
|
|
679
|
+
"dist",
|
|
680
|
+
"coverage",
|
|
681
|
+
"build",
|
|
682
|
+
".turbo",
|
|
683
|
+
".next",
|
|
684
|
+
"out"
|
|
685
|
+
]);
|
|
686
|
+
function patchPackageJson(content, from, to) {
|
|
687
|
+
let pkg;
|
|
688
|
+
try {
|
|
689
|
+
pkg = JSON.parse(content);
|
|
690
|
+
} catch {
|
|
691
|
+
return null;
|
|
692
|
+
}
|
|
693
|
+
let changed = false;
|
|
694
|
+
const engines = pkg.engines;
|
|
695
|
+
if (engines?.node === `>=${from}.0.0`) {
|
|
696
|
+
engines.node = `>=${to}.0.0`;
|
|
697
|
+
changed = true;
|
|
698
|
+
}
|
|
699
|
+
for (const field of ["devDependencies", "dependencies"]) {
|
|
700
|
+
const deps = pkg[field];
|
|
701
|
+
if (!deps?.["@types/node"]) continue;
|
|
702
|
+
if (deps["@types/node"] === `^${from}.0.0`) {
|
|
703
|
+
deps["@types/node"] = `^${to}.0.0`;
|
|
704
|
+
changed = true;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
return changed ? JSON.stringify(pkg, null, 2) + "\n" : null;
|
|
708
|
+
}
|
|
709
|
+
function patchYaml(content, from, to) {
|
|
710
|
+
const updated = content.replace(/node-version:\s+['"]?(\d+)['"]?/g, (match, ver) => ver === String(from) ? match.replace(String(from), String(to)) : match);
|
|
711
|
+
return updated !== content ? updated : null;
|
|
712
|
+
}
|
|
713
|
+
function patchPinFile(content, from, to) {
|
|
714
|
+
const trimmed = content.trim();
|
|
715
|
+
if (trimmed === String(from) || trimmed.startsWith(`${from}.`)) return `${to}\n`;
|
|
716
|
+
return null;
|
|
717
|
+
}
|
|
718
|
+
function patchDockerfile(content, from, to) {
|
|
719
|
+
const updated = content.replace(/^(FROM\s+node:)(\d+)/gm, (match, prefix, ver) => ver === String(from) ? `${prefix}${to}` : match);
|
|
720
|
+
return updated !== content ? updated : null;
|
|
721
|
+
}
|
|
722
|
+
function patchToolVersions(content, from, to) {
|
|
723
|
+
const updated = content.replace(/^(nodejs\s+)(\d+)/gm, (match, prefix, ver) => ver === String(from) ? `${prefix}${to}` : match);
|
|
724
|
+
return updated !== content ? updated : null;
|
|
725
|
+
}
|
|
726
|
+
const PATTERNS = [
|
|
727
|
+
{
|
|
728
|
+
matches: (n) => n === "package.json",
|
|
729
|
+
patch: patchPackageJson
|
|
730
|
+
},
|
|
731
|
+
{
|
|
732
|
+
matches: (n) => n.endsWith(".yml") || n.endsWith(".yaml"),
|
|
733
|
+
patch: patchYaml
|
|
734
|
+
},
|
|
735
|
+
{
|
|
736
|
+
matches: (n) => n === ".nvmrc" || n === ".node-version",
|
|
737
|
+
patch: patchPinFile
|
|
738
|
+
},
|
|
739
|
+
{
|
|
740
|
+
matches: (n) => n === "Dockerfile" || n.startsWith("Dockerfile."),
|
|
741
|
+
patch: patchDockerfile
|
|
742
|
+
},
|
|
743
|
+
{
|
|
744
|
+
matches: (n) => n === ".tool-versions",
|
|
745
|
+
patch: patchToolVersions
|
|
746
|
+
}
|
|
747
|
+
];
|
|
748
|
+
function detectFrom(cwd, _readFile) {
|
|
749
|
+
for (const name of [".nvmrc", ".node-version"]) try {
|
|
750
|
+
const major = parseInt(_readFile(join(cwd, name)).trim(), 10);
|
|
751
|
+
if (!isNaN(major)) return major;
|
|
752
|
+
} catch {}
|
|
753
|
+
try {
|
|
754
|
+
const node = JSON.parse(_readFile(join(cwd, "package.json"))).engines?.node;
|
|
755
|
+
if (node) {
|
|
756
|
+
const m = node.match(/(\d+)/);
|
|
757
|
+
if (m) return parseInt(m[1], 10);
|
|
758
|
+
}
|
|
759
|
+
} catch {}
|
|
760
|
+
return null;
|
|
761
|
+
}
|
|
762
|
+
function defaultWalkFiles(dir) {
|
|
763
|
+
const results = [];
|
|
764
|
+
function walk(current) {
|
|
765
|
+
let entries;
|
|
766
|
+
try {
|
|
767
|
+
entries = readdirSync(current);
|
|
768
|
+
} catch {
|
|
769
|
+
return;
|
|
770
|
+
}
|
|
771
|
+
for (const entry of entries) {
|
|
772
|
+
if (SKIP_DIRS.has(entry)) continue;
|
|
773
|
+
const abs = join(current, entry);
|
|
774
|
+
try {
|
|
775
|
+
if (statSync(abs).isDirectory()) walk(abs);
|
|
776
|
+
else results.push(abs);
|
|
777
|
+
} catch {}
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
walk(dir);
|
|
781
|
+
return results;
|
|
782
|
+
}
|
|
783
|
+
async function runUpgradeNode(input) {
|
|
784
|
+
const print = input.print ?? ((line) => console.log(line));
|
|
785
|
+
const cwd = input.cwd ?? process.cwd();
|
|
786
|
+
const { to, dryRun = false, extra = [] } = input;
|
|
787
|
+
const _readFile = input.readFile ?? ((p) => readFileSync(p, "utf8"));
|
|
788
|
+
const _writeFile = input.writeFile ?? ((p, c) => writeFileSync(p, c));
|
|
789
|
+
const _walkFiles = input.walkFiles ?? defaultWalkFiles;
|
|
790
|
+
const from = input.from ?? detectFrom(cwd, _readFile);
|
|
791
|
+
if (from === null) return {
|
|
792
|
+
status: "fail",
|
|
793
|
+
updated: [],
|
|
794
|
+
message: "could not detect current Node version — pass --from <major>"
|
|
795
|
+
};
|
|
796
|
+
if (from === to) {
|
|
797
|
+
print(`Already at Node.js ${to} — nothing to do.`);
|
|
798
|
+
return {
|
|
799
|
+
status: "ok",
|
|
800
|
+
updated: []
|
|
801
|
+
};
|
|
802
|
+
}
|
|
803
|
+
print(`Upgrading Node.js ${from} → ${to}${dryRun ? " (dry-run)" : ""}…`);
|
|
804
|
+
const updated = [];
|
|
805
|
+
const scanned = [..._walkFiles(cwd), ...extra.map((p) => join(cwd, p))];
|
|
806
|
+
for (const abs of scanned) {
|
|
807
|
+
const name = basename(abs);
|
|
808
|
+
const pattern = PATTERNS.find((p) => p.matches(name));
|
|
809
|
+
if (!pattern) continue;
|
|
810
|
+
let content;
|
|
811
|
+
try {
|
|
812
|
+
content = _readFile(abs);
|
|
813
|
+
} catch {
|
|
814
|
+
continue;
|
|
815
|
+
}
|
|
816
|
+
const patched = pattern.patch(content, from, to);
|
|
817
|
+
if (patched === null) continue;
|
|
818
|
+
const rel = abs.startsWith(cwd + "/") ? abs.slice(cwd.length + 1) : abs;
|
|
819
|
+
if (!dryRun) _writeFile(abs, patched);
|
|
820
|
+
print(` ${dryRun ? "~" : "✓"} ${rel}`);
|
|
821
|
+
updated.push(rel);
|
|
822
|
+
}
|
|
823
|
+
if (updated.length === 0) print(` · no files contained Node.js ${from} pins`);
|
|
824
|
+
return {
|
|
825
|
+
status: dryRun ? "dry-run" : "ok",
|
|
826
|
+
updated
|
|
827
|
+
};
|
|
828
|
+
}
|
|
829
|
+
//#endregion
|
|
690
830
|
//#region src/templates/index.ts
|
|
691
831
|
/**
|
|
692
832
|
* All reusable workflow and composite action content bundled as string
|
|
@@ -2253,48 +2393,19 @@ function deriveDefaults(input) {
|
|
|
2253
2393
|
//#endregion
|
|
2254
2394
|
//#region src/commands/plugin-create/templates/auth.ts
|
|
2255
2395
|
function render$17(inputs) {
|
|
2256
|
-
return
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
export class AuthError extends Error {
|
|
2271
|
-
override name = "AuthError";
|
|
2272
|
-
}
|
|
2273
|
-
|
|
2274
|
-
export interface ResolveTokenInput {
|
|
2275
|
-
/** From \`--token\` CLI flag. */
|
|
2276
|
-
cliToken?: string;
|
|
2277
|
-
/** Env vars; passed in for testability. Defaults to \`process.env\`. */
|
|
2278
|
-
env?: NodeJS.ProcessEnv;
|
|
2279
|
-
/** Keyring lookup fn; passed in for testability. Defaults to \`getToken(provider)\`. */
|
|
2280
|
-
keyring?: (provider: string) => string | null;
|
|
2281
|
-
}
|
|
2282
|
-
|
|
2283
|
-
export function resolveToken(input: ResolveTokenInput = {}): string {
|
|
2284
|
-
const env = input.env ?? process.env;
|
|
2285
|
-
const keyring = input.keyring ?? getKeyringToken;
|
|
2286
|
-
// Bracket access so numeric-prefixed slugs (e.g., env.HOLOCRON_1PASSWORD_TOKEN
|
|
2287
|
-
// which is invalid JS) still produce syntactically valid code.
|
|
2288
|
-
const token =
|
|
2289
|
-
input.cliToken || env["${inputs.tokenEnv}"] || env["${inputs.vendorEnv}"] || keyring("${inputs.slug}");
|
|
2290
|
-
if (!token) {
|
|
2291
|
-
throw new AuthError(
|
|
2292
|
-
"no ${inputs.vendorName} token found. Pass --token <TOKEN>, set ${inputs.tokenEnv} / ${inputs.vendorEnv}, " +
|
|
2293
|
-
"or run: holocron auth set ${inputs.slug} <TOKEN>"
|
|
2294
|
-
);
|
|
2295
|
-
}
|
|
2296
|
-
return token;
|
|
2297
|
-
}
|
|
2396
|
+
return `import { AuthError, createResolveToken, type ResolveTokenInput } from "@theholocron/cli";
|
|
2397
|
+
|
|
2398
|
+
export { AuthError };
|
|
2399
|
+
export type { ResolveTokenInput };
|
|
2400
|
+
|
|
2401
|
+
export const resolveToken = createResolveToken({
|
|
2402
|
+
\tenvName: "${inputs.tokenEnv}",
|
|
2403
|
+
\tvendorEnvName: "${inputs.vendorEnv}",
|
|
2404
|
+
\tkeyringService: "${inputs.slug}",
|
|
2405
|
+
\terrorMessage:
|
|
2406
|
+
\t\t"no ${inputs.vendorName} token found. Pass --token <TOKEN>, set ${inputs.tokenEnv} / ${inputs.vendorEnv}, " +
|
|
2407
|
+
\t\t"or run: holocron auth set ${inputs.slug} <TOKEN>",
|
|
2408
|
+
});
|
|
2298
2409
|
`;
|
|
2299
2410
|
}
|
|
2300
2411
|
//#endregion
|
|
@@ -2720,148 +2831,79 @@ Not yet published; capability methods are stubs.
|
|
|
2720
2831
|
//#endregion
|
|
2721
2832
|
//#region src/commands/plugin-create/templates/rest.ts
|
|
2722
2833
|
function render$7(inputs) {
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
}
|
|
2739
|
-
|
|
2740
|
-
export interface RequestOptions {
|
|
2741
|
-
method?: "GET" | "POST" | "PATCH" | "PUT" | "DELETE";
|
|
2742
|
-
body?: unknown;
|
|
2743
|
-
query?: Record<string, string>;
|
|
2744
|
-
/** Treat this response as void even if 200 is returned. */
|
|
2745
|
-
expectNoContent?: boolean;
|
|
2746
|
-
}
|
|
2747
|
-
|
|
2748
|
-
export class ${clientClass} {
|
|
2749
|
-
private readonly token: string;
|
|
2750
|
-
private readonly fetchImpl: typeof fetch;
|
|
2751
|
-
readonly baseUrl: string;
|
|
2752
|
-
|
|
2753
|
-
constructor(opts: RestClientOptions) {
|
|
2754
|
-
this.token = opts.token;
|
|
2755
|
-
this.fetchImpl = opts.fetch ?? globalThis.fetch;
|
|
2756
|
-
// Manual trailing-slash trim — CodeQL flags regex on library
|
|
2757
|
-
// input as polynomial ReDoS. O(n) loop, no backtracking risk.
|
|
2758
|
-
let url = opts.baseUrl ?? "${inputs.baseUrl}";
|
|
2759
|
-
while (url.endsWith("/")) url = url.slice(0, -1);
|
|
2760
|
-
this.baseUrl = url;
|
|
2761
|
-
}
|
|
2762
|
-
|
|
2763
|
-
async request<T>(path: string, opts: RequestOptions = {}): Promise<T> {
|
|
2764
|
-
const url = new URL(\`\${this.baseUrl}\${path.startsWith("/") ? path : "/" + path}\`);
|
|
2765
|
-
for (const [k, v] of Object.entries(opts.query ?? {})) url.searchParams.set(k, v);
|
|
2766
|
-
const fullUrl = url.toString();
|
|
2767
|
-
|
|
2768
|
-
const headers: Record<string, string> = {
|
|
2769
|
-
authorization: \`Bearer \${this.token}\`,
|
|
2770
|
-
accept: "application/json",
|
|
2771
|
-
};
|
|
2772
|
-
const init: RequestInit = {
|
|
2773
|
-
method: opts.method ?? "GET",
|
|
2774
|
-
headers,
|
|
2775
|
-
};
|
|
2776
|
-
if (opts.body !== undefined) {
|
|
2777
|
-
headers["content-type"] = "application/json";
|
|
2778
|
-
init.body = JSON.stringify(opts.body);
|
|
2779
|
-
}
|
|
2780
|
-
|
|
2781
|
-
let res: Response;
|
|
2782
|
-
try {
|
|
2783
|
-
res = await this.fetchImpl(fullUrl, init);
|
|
2784
|
-
} catch (err) {
|
|
2785
|
-
const detail = err instanceof Error ? \`\${err.name}: \${err.message}\` : String(err);
|
|
2786
|
-
throw new ProviderApiError(\`${inputs.vendorName} \${init.method} \${path} failed: \${detail}\`, 0, undefined);
|
|
2787
|
-
}
|
|
2788
|
-
if (!res.ok) {
|
|
2789
|
-
const body = await res.text().catch(() => "");
|
|
2790
|
-
throw new ProviderApiError(\`${inputs.vendorName} \${init.method} \${path} → \${res.status}\`, res.status, body);
|
|
2791
|
-
}
|
|
2792
|
-
if (opts.expectNoContent || res.status === 204) return undefined as T;
|
|
2793
|
-
const text = await res.text();
|
|
2794
|
-
if (!text) return undefined as T;
|
|
2795
|
-
return JSON.parse(text) as T;
|
|
2796
|
-
}
|
|
2834
|
+
return `import { createRestClient, type RequestOptions, type RestClient } from "@theholocron/cli";
|
|
2835
|
+
|
|
2836
|
+
export type { RequestOptions, RestClient };
|
|
2837
|
+
|
|
2838
|
+
export function ${`create${inputs.vendorName}RestClient`}(opts: {
|
|
2839
|
+
\ttoken: string;
|
|
2840
|
+
\tbaseUrl?: string;
|
|
2841
|
+
\tfetch?: typeof fetch;
|
|
2842
|
+
}): RestClient {
|
|
2843
|
+
\treturn createRestClient({
|
|
2844
|
+
\t\tbaseUrl: opts.baseUrl ?? "${inputs.baseUrl}",
|
|
2845
|
+
\t\ttoken: opts.token,
|
|
2846
|
+
\t\tvendor: "${inputs.vendorName}",
|
|
2847
|
+
\t\tfetch: opts.fetch,
|
|
2848
|
+
\t});
|
|
2797
2849
|
}
|
|
2798
2850
|
`;
|
|
2799
2851
|
}
|
|
2800
2852
|
//#endregion
|
|
2801
2853
|
//#region src/commands/plugin-create/templates/rest-test.ts
|
|
2802
2854
|
function render$6(inputs) {
|
|
2803
|
-
const
|
|
2855
|
+
const factoryName = `create${inputs.vendorName}RestClient`;
|
|
2804
2856
|
return `import { ProviderApiError } from "@theholocron/cli";
|
|
2805
2857
|
import { describe, expect, it } from "vitest";
|
|
2806
2858
|
|
|
2807
|
-
import { ${
|
|
2859
|
+
import { ${factoryName} } from "../rest.js";
|
|
2808
2860
|
import { stubFetch } from "./helpers.js";
|
|
2809
2861
|
|
|
2810
|
-
describe("${
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
} catch (err) {
|
|
2856
|
-
expect(err).toBeInstanceOf(ProviderApiError);
|
|
2857
|
-
expect((err as ProviderApiError).status).toBe(0);
|
|
2858
|
-
}
|
|
2859
|
-
});
|
|
2860
|
-
|
|
2861
|
-
it("trims trailing slashes from the base URL", () => {
|
|
2862
|
-
const client = new ${clientClass}({ token: "t", baseUrl: "${inputs.baseUrl}//" });
|
|
2863
|
-
expect(client.baseUrl).toBe("${inputs.baseUrl}");
|
|
2864
|
-
});
|
|
2862
|
+
describe("${factoryName}", () => {
|
|
2863
|
+
\tit("sends bearer + accept headers and returns the parsed body", async () => {
|
|
2864
|
+
\t\tconst stub = stubFetch([{ status: 200, body: { ok: true } }]);
|
|
2865
|
+
\t\tconst client = ${factoryName}({ token: "t", fetch: stub.fetch });
|
|
2866
|
+
\t\tconst res = await client.request<{ ok: boolean }>("/me");
|
|
2867
|
+
\t\texpect(res.ok).toBe(true);
|
|
2868
|
+
\t\texpect(stub.calls[0]?.headers["authorization"]).toBe("Bearer t");
|
|
2869
|
+
\t\texpect(stub.calls[0]?.headers["accept"]).toBe("application/json");
|
|
2870
|
+
\t});
|
|
2871
|
+
|
|
2872
|
+
\tit("serializes body as JSON and sets content-type when present", async () => {
|
|
2873
|
+
\t\tconst stub = stubFetch([{ status: 200, body: {} }]);
|
|
2874
|
+
\t\tconst client = ${factoryName}({ token: "t", fetch: stub.fetch });
|
|
2875
|
+
\t\tawait client.request<unknown>("/resource", { method: "POST", body: { name: "demo" } });
|
|
2876
|
+
\t\texpect(stub.calls[0]?.method).toBe("POST");
|
|
2877
|
+
\t\texpect(stub.calls[0]?.headers["content-type"]).toBe("application/json");
|
|
2878
|
+
\t\texpect(stub.calls[0]?.body).toEqual({ name: "demo" });
|
|
2879
|
+
\t});
|
|
2880
|
+
|
|
2881
|
+
\tit("returns undefined on 204", async () => {
|
|
2882
|
+
\t\tconst stub = stubFetch([{ status: 204 }]);
|
|
2883
|
+
\t\tconst client = ${factoryName}({ token: "t", fetch: stub.fetch });
|
|
2884
|
+
\t\texpect(await client.request<unknown>("/whatever")).toBeUndefined();
|
|
2885
|
+
\t});
|
|
2886
|
+
|
|
2887
|
+
\tit("throws ProviderApiError with the HTTP status on non-2xx", async () => {
|
|
2888
|
+
\t\tconst stub = stubFetch([{ status: 401, body: { messages: ["invalid"] } }]);
|
|
2889
|
+
\t\tconst client = ${factoryName}({ token: "bad", fetch: stub.fetch });
|
|
2890
|
+
\t\tconst err = await client.request<unknown>("/me").catch((e: unknown) => e);
|
|
2891
|
+
\t\texpect(err).toBeInstanceOf(ProviderApiError);
|
|
2892
|
+
\t\texpect((err as ProviderApiError).status).toBe(401);
|
|
2893
|
+
\t});
|
|
2894
|
+
|
|
2895
|
+
\tit("wraps transport-level failures with status 0", async () => {
|
|
2896
|
+
\t\tconst throwing: typeof fetch = async () => { throw new TypeError("fetch failed"); };
|
|
2897
|
+
\t\tconst client = ${factoryName}({ token: "t", fetch: throwing });
|
|
2898
|
+
\t\tconst err = await client.request<unknown>("/me").catch((e: unknown) => e);
|
|
2899
|
+
\t\texpect(err).toBeInstanceOf(ProviderApiError);
|
|
2900
|
+
\t\texpect((err as ProviderApiError).status).toBe(0);
|
|
2901
|
+
\t});
|
|
2902
|
+
|
|
2903
|
+
\tit("trims trailing slashes from the base URL", () => {
|
|
2904
|
+
\t\tconst client = ${factoryName}({ token: "t", baseUrl: "${inputs.baseUrl}//" });
|
|
2905
|
+
\t\texpect(client.baseUrl).toBe("${inputs.baseUrl}");
|
|
2906
|
+
\t});
|
|
2865
2907
|
});
|
|
2866
2908
|
`;
|
|
2867
2909
|
}
|
|
@@ -4183,7 +4225,32 @@ await yargs(hideBin(process.argv)).scriptName("holocron").usage("$0 <command> [o
|
|
|
4183
4225
|
}
|
|
4184
4226
|
throw err;
|
|
4185
4227
|
}
|
|
4186
|
-
}).command("
|
|
4228
|
+
}).command("upgrade", "Upgrade toolchain version pins across the repo", (y) => y.command("node <to>", "Scan the repo and update every Node.js version pin to a new major", (yy) => yy.positional("to", {
|
|
4229
|
+
type: "number",
|
|
4230
|
+
demandOption: true,
|
|
4231
|
+
describe: "Target Node.js major version (e.g., 22)"
|
|
4232
|
+
}).option("from", {
|
|
4233
|
+
type: "number",
|
|
4234
|
+
describe: "Current major version to replace. Auto-detected from .nvmrc / engines.node when omitted."
|
|
4235
|
+
}), async (argv) => {
|
|
4236
|
+
let extra = [];
|
|
4237
|
+
try {
|
|
4238
|
+
const raw = readFileSync(join(argv.cwd, "holocron.config.json"), "utf8");
|
|
4239
|
+
const upgradeNode = JSON.parse(raw).upgrade?.node;
|
|
4240
|
+
if (Array.isArray(upgradeNode?.extra)) extra = upgradeNode.extra;
|
|
4241
|
+
} catch {}
|
|
4242
|
+
const report = await runUpgradeNode({
|
|
4243
|
+
to: argv.to,
|
|
4244
|
+
...argv.from != null ? { from: argv.from } : {},
|
|
4245
|
+
cwd: argv.cwd,
|
|
4246
|
+
dryRun: argv.dryRun,
|
|
4247
|
+
extra
|
|
4248
|
+
});
|
|
4249
|
+
if (report.status === "fail") {
|
|
4250
|
+
if (report.message) console.error(`upgrade node: ${report.message}`);
|
|
4251
|
+
process.exitCode = 1;
|
|
4252
|
+
}
|
|
4253
|
+
}).demandCommand(1, "Run `holocron upgrade --help` to see available upgrade subcommands."), () => {}).command("auth <subcommand>", "Manage bootstrap credentials in the OS keyring", (y) => y.command("set <provider> [token]", "Verify + store a bootstrap token for a provider", (yy) => yy.positional("provider", {
|
|
4187
4254
|
type: "string",
|
|
4188
4255
|
demandOption: true
|
|
4189
4256
|
}).positional("token", { type: "string" }), async (argv) => {
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { Analytics, Auth, AuthDescription, AuthEvent, AuthEventType, AuthIdentity, AuthUser, CARDINALITY, CapabilityImpls, CapabilityKey, Cardinality, CardinalityFor, Ci, CiRun, CiRunFilter, CiRunStatus, ConnectionStringOptions, CreateAuthUserInput, Deployment, DeploymentProject, DeploymentProjectSettings, DeploymentRecord, DeploymentTarget, DeploymentTrigger, Dns, DnsRecord, DnsRecordType, EnsureResult, Environment, EnvironmentReviewer, Environments, Issue, IssueSearchFilter, Issues, LifecycleResult, LifecycleSlot, NormalizedAuthUser, Notifications, Observability, ParseWebhookInput, ProviderApiError, ProviderIdentity, REQUIRED_CAPABILITIES, RepoRef, RepoSettings, ResolvedCapability, Ruleset, SecretScope, Secrets, Source, StatusCategory, Storage, StorageBranch, Tooling, ToolingDoctorReport, TrackerDoctorReport, TrackerUser, Vault, WebhookDashboardInfo, WebhookVerificationError, isMulti } from "./capabilities/index.mjs";
|
|
2
|
+
import { AuthError, RequestOptions, ResolveTokenConfig as ResolveTokenConfig$1, ResolveTokenInput, RestClient, RestClientConfig, createRestClient } from "@theholocron/http-client";
|
|
2
3
|
|
|
4
|
+
//#region src/auth-resolver.d.ts
|
|
5
|
+
type ResolveTokenConfig = Omit<ResolveTokenConfig$1, "getKeyringToken">;
|
|
6
|
+
/** Wraps `createResolveToken` from `@theholocron/http` and injects the
|
|
7
|
+
* system keyring so plugins stay at a one-liner call site. */
|
|
8
|
+
declare function createResolveToken(config: ResolveTokenConfig): (input?: import("@theholocron/http-client").ResolveTokenInput) => string;
|
|
9
|
+
//#endregion
|
|
3
10
|
//#region src/config.d.ts
|
|
4
11
|
type ProviderOptions = Record<string, unknown>;
|
|
5
12
|
/**
|
|
@@ -183,4 +190,4 @@ interface LoadedConfig {
|
|
|
183
190
|
*/
|
|
184
191
|
declare function loadConfig(cwd: string): Promise<LoadedConfig>;
|
|
185
192
|
//#endregion
|
|
186
|
-
export { Analytics, AppConfig, Auth, AuthDescription, AuthEvent, AuthEventType, AuthIdentity, AuthUser, CARDINALITY, CapabilityConfigPackage, CapabilityImpls, CapabilityKey, Cardinality, CardinalityFor, Ci, CiRun, CiRunFilter, CiRunStatus, ConfigError, ConfigFileError, ConnectionStringOptions, CreateAuthUserInput, Deployment, DeploymentProject, DeploymentProjectSettings, DeploymentRecord, DeploymentTarget, DeploymentTrigger, Dns, DnsRecord, DnsRecordType, DoctorConfig, EnsureResult, Environment, EnvironmentReviewer, Environments, HolocronConfig, Issue, IssueSearchFilter, Issues, LifecycleResult, LifecycleSlot, LoadedConfig, MultiEntry, NormalizedAuthUser, Notifications, Observability, ParseWebhookInput, ProviderApiError, ProviderIdentity, ProviderOptions, REQUIRED_CAPABILITIES, RawProviderEntry, RawProvidersConfig, RepoPolicyConfig, RepoRef, RepoSettings, ResolvedCapability, ResolvedHolocronConfig, ResolvedProviderEntry, ResolvedProvidersConfig, ResolvedTuple, Ruleset, SecretScope, Secrets, SingleEntry, Source, StatusCategory, Storage, StorageBranch, Tooling, ToolingDoctorReport, TrackerDoctorReport, TrackerUser, Vault, WebhookDashboardInfo, WebhookVerificationError, defineConfig, deleteToken, getToken, isMulti, listStoredProviders, loadConfig, resolveConfig, resolveEntry, resolvePluginPackage, setToken };
|
|
193
|
+
export { Analytics, AppConfig, Auth, AuthDescription, AuthError, AuthEvent, AuthEventType, AuthIdentity, AuthUser, CARDINALITY, CapabilityConfigPackage, CapabilityImpls, CapabilityKey, Cardinality, CardinalityFor, Ci, CiRun, CiRunFilter, CiRunStatus, ConfigError, ConfigFileError, ConnectionStringOptions, CreateAuthUserInput, Deployment, DeploymentProject, DeploymentProjectSettings, DeploymentRecord, DeploymentTarget, DeploymentTrigger, Dns, DnsRecord, DnsRecordType, DoctorConfig, EnsureResult, Environment, EnvironmentReviewer, Environments, HolocronConfig, Issue, IssueSearchFilter, Issues, LifecycleResult, LifecycleSlot, LoadedConfig, MultiEntry, NormalizedAuthUser, Notifications, Observability, ParseWebhookInput, ProviderApiError, ProviderIdentity, ProviderOptions, REQUIRED_CAPABILITIES, RawProviderEntry, RawProvidersConfig, RepoPolicyConfig, RepoRef, RepoSettings, type RequestOptions, ResolveTokenConfig, type ResolveTokenInput, ResolvedCapability, ResolvedHolocronConfig, ResolvedProviderEntry, ResolvedProvidersConfig, ResolvedTuple, type RestClient, type RestClientConfig, Ruleset, SecretScope, Secrets, SingleEntry, Source, StatusCategory, Storage, StorageBranch, Tooling, ToolingDoctorReport, TrackerDoctorReport, TrackerUser, Vault, WebhookDashboardInfo, WebhookVerificationError, createResolveToken, createRestClient, defineConfig, deleteToken, getToken, isMulti, listStoredProviders, loadConfig, resolveConfig, resolveEntry, resolvePluginPackage, setToken };
|
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,89 @@
|
|
|
1
1
|
import { CARDINALITY, ProviderApiError, REQUIRED_CAPABILITIES, WebhookVerificationError, isMulti } from "./capabilities/index.mjs";
|
|
2
|
+
import { AuthError, createResolveToken as createResolveToken$1, createRestClient } from "@theholocron/http-client";
|
|
2
3
|
import { Entry, findCredentials } from "@napi-rs/keyring";
|
|
3
4
|
import { readFile, stat } from "node:fs/promises";
|
|
4
5
|
import { join } from "node:path";
|
|
5
6
|
import { pathToFileURL } from "node:url";
|
|
7
|
+
//#region src/keyring.ts
|
|
8
|
+
/**
|
|
9
|
+
* Keyring-backed bootstrap credential store.
|
|
10
|
+
*
|
|
11
|
+
* Every holocron plugin's bootstrap token (the one it needs before it
|
|
12
|
+
* can talk to its vendor's API) can be stored in the OS keyring under
|
|
13
|
+
* a single reverse-DNS service scope. Managed via `holocron auth`
|
|
14
|
+
* subcommands; consulted at position 4 in every plugin's auth
|
|
15
|
+
* precedence chain (after --token / HOLOCRON_<X>_TOKEN / <native>_TOKEN).
|
|
16
|
+
*
|
|
17
|
+
* See `.notes/tech-auth-bootstrap.spec.md` for the design rationale.
|
|
18
|
+
*
|
|
19
|
+
* Failure model: keyring access is best-effort. Platforms without a
|
|
20
|
+
* supported credential store (some Linux CI images, sandboxed
|
|
21
|
+
* environments) will throw from the underlying library. Every export
|
|
22
|
+
* here catches and returns a null/empty result rather than propagating
|
|
23
|
+
* — the plugin's precedence chain then falls through to
|
|
24
|
+
* env-var-only paths, which is exactly how CI is meant to work.
|
|
25
|
+
*/
|
|
26
|
+
const SERVICE = "com.theholocron.cli";
|
|
27
|
+
/**
|
|
28
|
+
* Store or overwrite a bootstrap token for a provider. Returns true on
|
|
29
|
+
* success, false when the underlying keyring is unsupported or errored.
|
|
30
|
+
*/
|
|
31
|
+
function setToken(provider, token) {
|
|
32
|
+
try {
|
|
33
|
+
new Entry(SERVICE, provider).setPassword(token);
|
|
34
|
+
return true;
|
|
35
|
+
} catch {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Read the bootstrap token for a provider. Returns `null` for both
|
|
41
|
+
* "not stored" and "keyring unavailable" — callers can treat them the
|
|
42
|
+
* same way (fall through to env-var precedence).
|
|
43
|
+
*/
|
|
44
|
+
function getToken(provider) {
|
|
45
|
+
try {
|
|
46
|
+
return new Entry(SERVICE, provider).getPassword();
|
|
47
|
+
} catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Delete a stored token. Returns true when a token was removed, false
|
|
53
|
+
* when there was nothing to delete or the keyring is unavailable.
|
|
54
|
+
* Distinguishing the two cases isn't worth the surface area — the
|
|
55
|
+
* command output makes the situation clear either way.
|
|
56
|
+
*/
|
|
57
|
+
function deleteToken(provider) {
|
|
58
|
+
try {
|
|
59
|
+
return new Entry(SERVICE, provider).deletePassword();
|
|
60
|
+
} catch {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* List provider slugs with a stored token in this service scope.
|
|
66
|
+
* Uses the library's `findCredentials(service)` — supported on all
|
|
67
|
+
* platforms the underlying credential store supports.
|
|
68
|
+
*/
|
|
69
|
+
function listStoredProviders() {
|
|
70
|
+
try {
|
|
71
|
+
return findCredentials(SERVICE).map((c) => c.account);
|
|
72
|
+
} catch {
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
//#endregion
|
|
77
|
+
//#region src/auth-resolver.ts
|
|
78
|
+
/** Wraps `createResolveToken` from `@theholocron/http` and injects the
|
|
79
|
+
* system keyring so plugins stay at a one-liner call site. */
|
|
80
|
+
function createResolveToken(config) {
|
|
81
|
+
return createResolveToken$1({
|
|
82
|
+
...config,
|
|
83
|
+
getKeyringToken: getToken
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
//#endregion
|
|
6
87
|
//#region src/config.ts
|
|
7
88
|
/**
|
|
8
89
|
* `holocron.config.json` schema, parser, and provider resolution.
|
|
@@ -119,76 +200,6 @@ function defineConfig(config) {
|
|
|
119
200
|
return config;
|
|
120
201
|
}
|
|
121
202
|
//#endregion
|
|
122
|
-
//#region src/keyring.ts
|
|
123
|
-
/**
|
|
124
|
-
* Keyring-backed bootstrap credential store.
|
|
125
|
-
*
|
|
126
|
-
* Every holocron plugin's bootstrap token (the one it needs before it
|
|
127
|
-
* can talk to its vendor's API) can be stored in the OS keyring under
|
|
128
|
-
* a single reverse-DNS service scope. Managed via `holocron auth`
|
|
129
|
-
* subcommands; consulted at position 4 in every plugin's auth
|
|
130
|
-
* precedence chain (after --token / HOLOCRON_<X>_TOKEN / <native>_TOKEN).
|
|
131
|
-
*
|
|
132
|
-
* See `.notes/tech-auth-bootstrap.spec.md` for the design rationale.
|
|
133
|
-
*
|
|
134
|
-
* Failure model: keyring access is best-effort. Platforms without a
|
|
135
|
-
* supported credential store (some Linux CI images, sandboxed
|
|
136
|
-
* environments) will throw from the underlying library. Every export
|
|
137
|
-
* here catches and returns a null/empty result rather than propagating
|
|
138
|
-
* — the plugin's precedence chain then falls through to
|
|
139
|
-
* env-var-only paths, which is exactly how CI is meant to work.
|
|
140
|
-
*/
|
|
141
|
-
const SERVICE = "com.theholocron.cli";
|
|
142
|
-
/**
|
|
143
|
-
* Store or overwrite a bootstrap token for a provider. Returns true on
|
|
144
|
-
* success, false when the underlying keyring is unsupported or errored.
|
|
145
|
-
*/
|
|
146
|
-
function setToken(provider, token) {
|
|
147
|
-
try {
|
|
148
|
-
new Entry(SERVICE, provider).setPassword(token);
|
|
149
|
-
return true;
|
|
150
|
-
} catch {
|
|
151
|
-
return false;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
/**
|
|
155
|
-
* Read the bootstrap token for a provider. Returns `null` for both
|
|
156
|
-
* "not stored" and "keyring unavailable" — callers can treat them the
|
|
157
|
-
* same way (fall through to env-var precedence).
|
|
158
|
-
*/
|
|
159
|
-
function getToken(provider) {
|
|
160
|
-
try {
|
|
161
|
-
return new Entry(SERVICE, provider).getPassword();
|
|
162
|
-
} catch {
|
|
163
|
-
return null;
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
/**
|
|
167
|
-
* Delete a stored token. Returns true when a token was removed, false
|
|
168
|
-
* when there was nothing to delete or the keyring is unavailable.
|
|
169
|
-
* Distinguishing the two cases isn't worth the surface area — the
|
|
170
|
-
* command output makes the situation clear either way.
|
|
171
|
-
*/
|
|
172
|
-
function deleteToken(provider) {
|
|
173
|
-
try {
|
|
174
|
-
return new Entry(SERVICE, provider).deletePassword();
|
|
175
|
-
} catch {
|
|
176
|
-
return false;
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
/**
|
|
180
|
-
* List provider slugs with a stored token in this service scope.
|
|
181
|
-
* Uses the library's `findCredentials(service)` — supported on all
|
|
182
|
-
* platforms the underlying credential store supports.
|
|
183
|
-
*/
|
|
184
|
-
function listStoredProviders() {
|
|
185
|
-
try {
|
|
186
|
-
return findCredentials(SERVICE).map((c) => c.account);
|
|
187
|
-
} catch {
|
|
188
|
-
return [];
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
//#endregion
|
|
192
203
|
//#region src/load-config.ts
|
|
193
204
|
/**
|
|
194
205
|
* `holocron.config.{json,js,ts}` file loader.
|
|
@@ -265,4 +276,4 @@ async function fileExists(path) {
|
|
|
265
276
|
}
|
|
266
277
|
}
|
|
267
278
|
//#endregion
|
|
268
|
-
export { CARDINALITY, ConfigError, ConfigFileError, ProviderApiError, REQUIRED_CAPABILITIES, WebhookVerificationError, defineConfig, deleteToken, getToken, isMulti, listStoredProviders, loadConfig, resolveConfig, resolveEntry, resolvePluginPackage, setToken };
|
|
279
|
+
export { AuthError, CARDINALITY, ConfigError, ConfigFileError, ProviderApiError, REQUIRED_CAPABILITIES, WebhookVerificationError, createResolveToken, createRestClient, defineConfig, deleteToken, getToken, isMulti, listStoredProviders, loadConfig, resolveConfig, resolveEntry, resolvePluginPackage, setToken };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theholocron/cli",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.21",
|
|
4
4
|
"description": "The Holocron CLI — a pluggable, capability-based orchestrator for spinning up and operating software projects.",
|
|
5
5
|
"homepage": "https://github.com/theholocron/holocron/tree/main/packages/cli#readme",
|
|
6
6
|
"bugs": "https://github.com/theholocron/holocron/issues",
|
|
@@ -34,22 +34,23 @@
|
|
|
34
34
|
],
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@napi-rs/keyring": "^1.3.0",
|
|
37
|
+
"@theholocron/http-client": "^0.1.0",
|
|
37
38
|
"tsx": "^4.22.4",
|
|
38
39
|
"yargs": "^18.0.0"
|
|
39
40
|
},
|
|
40
41
|
"devDependencies": {
|
|
41
|
-
"@theholocron/eslint-config": "^5.
|
|
42
|
-
"@theholocron/tsconfig": "^
|
|
43
|
-
"@
|
|
42
|
+
"@theholocron/eslint-config": "^5.2.0",
|
|
43
|
+
"@theholocron/tsconfig": "^6.0.0",
|
|
44
|
+
"@theholocron/vitest-config": "^5.2.0",
|
|
45
|
+
"@types/node": "^26",
|
|
44
46
|
"@types/yargs": "^17.0.35",
|
|
45
|
-
"@vitest/coverage-v8": "^3.2.6",
|
|
46
47
|
"@vitest/eslint-plugin": "^1.6.23",
|
|
47
48
|
"eslint": "^10.7.0",
|
|
48
49
|
"eslint-plugin-n": "^18.2.2",
|
|
49
50
|
"globals": "^17.7.0",
|
|
50
51
|
"tsdown": "^0.22.3",
|
|
51
52
|
"typescript": "^5.9.3",
|
|
52
|
-
"vitest": "^
|
|
53
|
+
"vitest": "^4.1.10",
|
|
53
54
|
"@theholocron/cli-utils": "0.0.0"
|
|
54
55
|
},
|
|
55
56
|
"publishConfig": {
|