@indigoai-us/hq-cli 5.8.5 → 5.9.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 +30 -0
- package/dist/commands/files.js +94 -48
- package/dist/commands/run.d.ts +3 -0
- package/dist/commands/run.js +119 -0
- package/dist/commands/secrets.d.ts +3 -9
- package/dist/commands/secrets.js +5 -56
- package/dist/index.js +6 -3
- package/dist/run/discover-schemas.d.ts +12 -0
- package/dist/run/discover-schemas.js +64 -0
- package/dist/run/hq-plugin.d.ts +26 -0
- package/dist/run/hq-plugin.js +144 -0
- package/dist/utils/vault-api.d.ts +10 -0
- package/dist/utils/vault-api.js +58 -0
- package/package.json +7 -3
- package/src/commands/files.ts +110 -50
- package/src/commands/run.env-local.test.ts +84 -0
- package/src/commands/run.ts +137 -0
- package/src/commands/secrets.ts +4 -88
- package/src/index.ts +5 -1
- package/src/run/__fixtures__/discover-schemas/example.env.schema +4 -0
- package/src/run/discover-schemas.test.ts +153 -0
- package/src/run/discover-schemas.ts +79 -0
- package/src/run/hq-plugin.test.ts +125 -0
- package/src/run/hq-plugin.ts +174 -0
- package/src/run/varlock-shape.test.ts +57 -0
- package/src/utils/vault-api.ts +80 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { beforeAll, describe, expect, it } from 'vitest';
|
|
2
|
+
import { internal } from 'varlock';
|
|
3
|
+
// Resolver is a type-only export in varlock@1.0.0's plugin-lib.js (d.ts/JS mismatch);
|
|
4
|
+
// the value is extracted at runtime from graph.registeredResolverFunctions below.
|
|
5
|
+
import type { Resolver } from 'varlock/plugin-lib';
|
|
6
|
+
|
|
7
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
8
|
+
let RuntimeResolver: typeof Resolver;
|
|
9
|
+
|
|
10
|
+
beforeAll(async () => {
|
|
11
|
+
// Spin up a minimal graph to access built-in resolvers registered by varlock.
|
|
12
|
+
// No schema files are needed — varlock registers built-in resolvers in afterInit.
|
|
13
|
+
await internal.loadEnvGraph({
|
|
14
|
+
entryFilePaths: [],
|
|
15
|
+
afterInit: async (graph) => {
|
|
16
|
+
const fns = (graph as any).registeredResolverFunctions as Record<string, unknown>;
|
|
17
|
+
const firstClass = Object.values(fns)[0];
|
|
18
|
+
if (firstClass == null) throw new Error('registeredResolverFunctions is empty — varlock API shape changed');
|
|
19
|
+
// Each registered class extends Resolver; its prototype chain gives us the base class.
|
|
20
|
+
RuntimeResolver = Object.getPrototypeOf(firstClass) as typeof Resolver;
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe('varlock 1.0.0 API shape smoke test', () => {
|
|
26
|
+
it('internal.loadEnvGraph is a function', () => {
|
|
27
|
+
expect(typeof internal.loadEnvGraph).toBe('function');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('internal.EnvGraph is a function (class)', () => {
|
|
31
|
+
expect(typeof internal.EnvGraph).toBe('function');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('EnvGraph.prototype.registerRootDecorator exists', () => {
|
|
35
|
+
expect(typeof internal.EnvGraph.prototype.registerRootDecorator).toBe('function');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('EnvGraph.prototype.registerResolver exists', () => {
|
|
39
|
+
expect(typeof internal.EnvGraph.prototype.registerResolver).toBe('function');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('EnvGraph.prototype.registerItemDecorator exists', () => {
|
|
43
|
+
expect(typeof internal.EnvGraph.prototype.registerItemDecorator).toBe('function');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('EnvGraph.prototype.registerDataType exists', () => {
|
|
47
|
+
expect(typeof internal.EnvGraph.prototype.registerDataType).toBe('function');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('Resolver.prototype.process exists', () => {
|
|
51
|
+
expect(typeof RuntimeResolver.prototype.process).toBe('function');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('Resolver.prototype.resolve exists', () => {
|
|
55
|
+
expect(typeof RuntimeResolver.prototype.resolve).toBe('function');
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { DEFAULT_VAULT_API_URL } from './cognito-session.js';
|
|
2
|
+
|
|
3
|
+
export interface VaultApiOptions {
|
|
4
|
+
token: string;
|
|
5
|
+
path: string;
|
|
6
|
+
method?: string;
|
|
7
|
+
body?: Record<string, unknown>;
|
|
8
|
+
query?: Record<string, string>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export async function vaultApiFetch(opts: VaultApiOptions): Promise<Response> {
|
|
12
|
+
const url = new URL(opts.path, DEFAULT_VAULT_API_URL);
|
|
13
|
+
if (opts.query) {
|
|
14
|
+
for (const [k, v] of Object.entries(opts.query)) {
|
|
15
|
+
url.searchParams.set(k, v);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return fetch(url.toString(), {
|
|
19
|
+
method: opts.method ?? 'GET',
|
|
20
|
+
headers: {
|
|
21
|
+
Authorization: `Bearer ${opts.token}`,
|
|
22
|
+
'Content-Type': 'application/json',
|
|
23
|
+
},
|
|
24
|
+
body: opts.body ? JSON.stringify(opts.body) : undefined,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface MembershipEntry {
|
|
29
|
+
companyUid: string;
|
|
30
|
+
role: string;
|
|
31
|
+
status: string;
|
|
32
|
+
membershipKey: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function resolveCompanyUid(token: string, slug: string): Promise<string> {
|
|
36
|
+
const res = await vaultApiFetch({
|
|
37
|
+
token,
|
|
38
|
+
path: `/entity/by-slug/company/${encodeURIComponent(slug)}`,
|
|
39
|
+
});
|
|
40
|
+
if (!res.ok) {
|
|
41
|
+
const body = await res.json().catch(() => ({}));
|
|
42
|
+
throw new Error(
|
|
43
|
+
`Failed to resolve company slug '${slug}': ${(body as Record<string, string>).error ?? res.statusText}`,
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
const data = (await res.json()) as { entity: { uid: string } };
|
|
47
|
+
return data.entity.uid;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function resolveCompanyFromMemberships(token: string): Promise<string> {
|
|
51
|
+
const res = await vaultApiFetch({
|
|
52
|
+
token,
|
|
53
|
+
path: '/membership/me',
|
|
54
|
+
});
|
|
55
|
+
if (!res.ok) {
|
|
56
|
+
throw new Error("Failed to fetch memberships — run `hq login` and try again");
|
|
57
|
+
}
|
|
58
|
+
const data = (await res.json()) as { memberships: MembershipEntry[] };
|
|
59
|
+
const active = data.memberships.filter((m) => m.status === 'active');
|
|
60
|
+
if (active.length === 0) {
|
|
61
|
+
throw new Error('No active company memberships found. Use --company <slug> to specify.');
|
|
62
|
+
}
|
|
63
|
+
if (active.length === 1) {
|
|
64
|
+
return active[0].companyUid;
|
|
65
|
+
}
|
|
66
|
+
const uids = active.map((m) => m.companyUid).join(', ');
|
|
67
|
+
throw new Error(
|
|
68
|
+
`Multiple companies found (${uids}). Use --company <slug> to specify which one.`,
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export async function getCompanyUid(
|
|
73
|
+
token: string,
|
|
74
|
+
companySlug: string | undefined,
|
|
75
|
+
): Promise<string> {
|
|
76
|
+
if (companySlug) {
|
|
77
|
+
return resolveCompanyUid(token, companySlug);
|
|
78
|
+
}
|
|
79
|
+
return resolveCompanyFromMemberships(token);
|
|
80
|
+
}
|