@indigoai-us/hq-cloud 5.1.0 → 5.1.9
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/dist/bin/sync-runner.d.ts +134 -0
- package/dist/bin/sync-runner.d.ts.map +1 -0
- package/dist/bin/sync-runner.js +360 -0
- package/dist/bin/sync-runner.js.map +1 -0
- package/dist/bin/sync-runner.test.d.ts +10 -0
- package/dist/bin/sync-runner.test.d.ts.map +1 -0
- package/dist/bin/sync-runner.test.js +648 -0
- package/dist/bin/sync-runner.test.js.map +1 -0
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/share.js +2 -2
- package/dist/cli/share.js.map +1 -1
- package/dist/cli/share.test.js +9 -1
- package/dist/cli/share.test.js.map +1 -1
- package/dist/cli/sync.d.ts +28 -0
- package/dist/cli/sync.d.ts.map +1 -1
- package/dist/cli/sync.js +33 -10
- package/dist/cli/sync.js.map +1 -1
- package/dist/cli/sync.test.js +15 -4
- package/dist/cli/sync.test.js.map +1 -1
- package/dist/cognito-auth.d.ts.map +1 -1
- package/dist/cognito-auth.js +19 -1
- package/dist/cognito-auth.js.map +1 -1
- package/dist/cognito-auth.test.d.ts +9 -0
- package/dist/cognito-auth.test.d.ts.map +1 -0
- package/dist/cognito-auth.test.js +113 -0
- package/dist/cognito-auth.test.js.map +1 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +1 -0
- package/dist/context.js.map +1 -1
- package/dist/daemon-worker.d.ts +6 -1
- package/dist/daemon-worker.d.ts.map +1 -1
- package/dist/daemon-worker.js +12 -16
- package/dist/daemon-worker.js.map +1 -1
- package/dist/daemon.d.ts +2 -0
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +2 -0
- package/dist/daemon.js.map +1 -1
- package/dist/ignore.d.ts +13 -2
- package/dist/ignore.d.ts.map +1 -1
- package/dist/ignore.js +69 -12
- package/dist/ignore.js.map +1 -1
- package/dist/index.d.ts +24 -28
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -134
- package/dist/index.js.map +1 -1
- package/dist/journal.d.ts +20 -4
- package/dist/journal.d.ts.map +1 -1
- package/dist/journal.js +45 -8
- package/dist/journal.js.map +1 -1
- package/dist/journal.test.d.ts +9 -0
- package/dist/journal.test.d.ts.map +1 -0
- package/dist/journal.test.js +114 -0
- package/dist/journal.test.js.map +1 -0
- package/dist/s3.d.ts +18 -6
- package/dist/s3.d.ts.map +1 -1
- package/dist/s3.js +57 -56
- package/dist/s3.js.map +1 -1
- package/dist/types.d.ts +34 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/vault-client.d.ts +59 -0
- package/dist/vault-client.d.ts.map +1 -1
- package/dist/vault-client.js +72 -0
- package/dist/vault-client.js.map +1 -1
- package/dist/vault-client.test.js +160 -0
- package/dist/vault-client.test.js.map +1 -1
- package/dist/watcher.d.ts +7 -1
- package/dist/watcher.d.ts.map +1 -1
- package/dist/watcher.js +11 -5
- package/dist/watcher.js.map +1 -1
- package/package.json +15 -3
- package/src/bin/sync-runner.test.ts +804 -0
- package/src/bin/sync-runner.ts +499 -0
- package/src/cli/accept.ts +97 -0
- package/src/cli/conflict.ts +119 -0
- package/src/cli/index.ts +25 -0
- package/src/cli/invite.test.ts +247 -0
- package/src/cli/invite.ts +180 -0
- package/src/cli/promote.ts +123 -0
- package/src/cli/share.test.ts +155 -0
- package/src/cli/share.ts +212 -0
- package/src/cli/sync.test.ts +225 -0
- package/src/cli/sync.ts +225 -0
- package/src/cognito-auth.test.ts +156 -0
- package/src/cognito-auth.ts +18 -1
- package/src/context.test.ts +202 -0
- package/src/context.ts +178 -0
- package/src/daemon-worker.ts +13 -19
- package/src/daemon.ts +2 -0
- package/src/ignore.ts +76 -12
- package/src/index.ts +94 -165
- package/src/journal.test.ts +146 -0
- package/src/journal.ts +53 -11
- package/src/s3.ts +76 -66
- package/src/types.ts +37 -0
- package/src/vault-client.test.ts +563 -0
- package/src/vault-client.ts +478 -0
- package/src/watcher.ts +12 -5
- package/test/invite-flow.integration.test.ts +244 -0
- package/test/share-sync.integration.test.ts +210 -0
package/src/context.ts
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Entity context resolution (VLT-5 US-001).
|
|
3
|
+
*
|
|
4
|
+
* Resolves an entity (company) via vault-service, vends STS-scoped credentials,
|
|
5
|
+
* and returns an EntityContext for S3 operations. Handles auto-refresh when
|
|
6
|
+
* credentials are within 2 minutes of expiry.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { EntityContext, VaultServiceConfig } from "./types.js";
|
|
10
|
+
|
|
11
|
+
/** Minimum remaining TTL before auto-refresh triggers (2 minutes). */
|
|
12
|
+
const REFRESH_THRESHOLD_MS = 2 * 60 * 1000;
|
|
13
|
+
|
|
14
|
+
/** STS session duration requested from vault-service (15 minutes). */
|
|
15
|
+
const DEFAULT_SESSION_DURATION_SECONDS = 900;
|
|
16
|
+
|
|
17
|
+
/** Cached contexts keyed by entity UID. */
|
|
18
|
+
const contextCache = new Map<string, EntityContext>();
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Look up an entity by slug or UID via vault-service, then vend STS-scoped
|
|
22
|
+
* credentials for that entity. Returns an EntityContext ready for S3 ops.
|
|
23
|
+
*
|
|
24
|
+
* Caches the result and auto-refreshes when the credentials are within
|
|
25
|
+
* 2 minutes of expiry.
|
|
26
|
+
*/
|
|
27
|
+
export async function resolveEntityContext(
|
|
28
|
+
companyUidOrSlug: string,
|
|
29
|
+
config: VaultServiceConfig,
|
|
30
|
+
): Promise<EntityContext> {
|
|
31
|
+
// Check cache — return if credentials still fresh
|
|
32
|
+
const cached = contextCache.get(companyUidOrSlug);
|
|
33
|
+
if (cached && !isExpiringSoon(cached.expiresAt)) {
|
|
34
|
+
return cached;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Step 1: Resolve entity — if it looks like a UID (cmp_*), fetch directly;
|
|
38
|
+
// otherwise look up by slug
|
|
39
|
+
const entity = companyUidOrSlug.startsWith("cmp_")
|
|
40
|
+
? await fetchEntity(companyUidOrSlug, config)
|
|
41
|
+
: await fetchEntityBySlug("company", companyUidOrSlug, config);
|
|
42
|
+
|
|
43
|
+
if (!entity.bucketName) {
|
|
44
|
+
throw new Error(
|
|
45
|
+
`Entity ${entity.uid} (${entity.slug}) has no bucket provisioned. ` +
|
|
46
|
+
`Run VLT-2 bucket provisioning first.`,
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Step 2: Vend STS-scoped credentials
|
|
51
|
+
const vendResult = await vendCredentials(entity.uid, config);
|
|
52
|
+
|
|
53
|
+
const ctx: EntityContext = {
|
|
54
|
+
uid: entity.uid,
|
|
55
|
+
slug: entity.slug,
|
|
56
|
+
bucketName: entity.bucketName,
|
|
57
|
+
region: config.region ?? "us-east-1",
|
|
58
|
+
credentials: {
|
|
59
|
+
accessKeyId: vendResult.credentials.accessKeyId,
|
|
60
|
+
secretAccessKey: vendResult.credentials.secretAccessKey,
|
|
61
|
+
sessionToken: vendResult.credentials.sessionToken,
|
|
62
|
+
},
|
|
63
|
+
expiresAt: vendResult.expiresAt,
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// Cache by both UID and slug for fast lookups
|
|
67
|
+
contextCache.set(entity.uid, ctx);
|
|
68
|
+
contextCache.set(entity.slug, ctx);
|
|
69
|
+
|
|
70
|
+
return ctx;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Check if credentials are expiring within the refresh threshold.
|
|
75
|
+
*/
|
|
76
|
+
export function isExpiringSoon(expiresAt: string): boolean {
|
|
77
|
+
const expiryMs = new Date(expiresAt).getTime();
|
|
78
|
+
const nowMs = Date.now();
|
|
79
|
+
return expiryMs - nowMs < REFRESH_THRESHOLD_MS;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Force-refresh a cached context. Useful when an S3 operation fails with
|
|
84
|
+
* an expired credentials error.
|
|
85
|
+
*/
|
|
86
|
+
export async function refreshEntityContext(
|
|
87
|
+
companyUidOrSlug: string,
|
|
88
|
+
config: VaultServiceConfig,
|
|
89
|
+
): Promise<EntityContext> {
|
|
90
|
+
// Evict cache entry to force fresh resolution
|
|
91
|
+
contextCache.delete(companyUidOrSlug);
|
|
92
|
+
return resolveEntityContext(companyUidOrSlug, config);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Clear the entire context cache. Useful for tests.
|
|
97
|
+
*/
|
|
98
|
+
export function clearContextCache(): void {
|
|
99
|
+
contextCache.clear();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ---------------------------------------------------------------------------
|
|
103
|
+
// Vault-service API calls
|
|
104
|
+
// ---------------------------------------------------------------------------
|
|
105
|
+
|
|
106
|
+
interface EntityResponse {
|
|
107
|
+
uid: string;
|
|
108
|
+
slug: string;
|
|
109
|
+
bucketName?: string;
|
|
110
|
+
status: string;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
interface VendResponse {
|
|
114
|
+
credentials: {
|
|
115
|
+
accessKeyId: string;
|
|
116
|
+
secretAccessKey: string;
|
|
117
|
+
sessionToken: string;
|
|
118
|
+
expiration: string;
|
|
119
|
+
};
|
|
120
|
+
expiresAt: string;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async function fetchEntity(
|
|
124
|
+
uid: string,
|
|
125
|
+
config: VaultServiceConfig,
|
|
126
|
+
): Promise<EntityResponse> {
|
|
127
|
+
const res = await fetch(`${config.apiUrl}/entity/${uid}`, {
|
|
128
|
+
headers: { Authorization: `Bearer ${config.authToken}` },
|
|
129
|
+
});
|
|
130
|
+
if (!res.ok) {
|
|
131
|
+
const body = await res.text();
|
|
132
|
+
throw new Error(`Failed to fetch entity ${uid}: ${res.status} ${body}`);
|
|
133
|
+
}
|
|
134
|
+
const data = (await res.json()) as { entity: EntityResponse };
|
|
135
|
+
return data.entity;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async function fetchEntityBySlug(
|
|
139
|
+
type: string,
|
|
140
|
+
slug: string,
|
|
141
|
+
config: VaultServiceConfig,
|
|
142
|
+
): Promise<EntityResponse> {
|
|
143
|
+
const res = await fetch(`${config.apiUrl}/entity/by-slug/${type}/${slug}`, {
|
|
144
|
+
headers: { Authorization: `Bearer ${config.authToken}` },
|
|
145
|
+
});
|
|
146
|
+
if (!res.ok) {
|
|
147
|
+
const body = await res.text();
|
|
148
|
+
throw new Error(
|
|
149
|
+
`Failed to find entity by slug ${type}/${slug}: ${res.status} ${body}`,
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
const data = (await res.json()) as { entity: EntityResponse };
|
|
153
|
+
return data.entity;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async function vendCredentials(
|
|
157
|
+
companyUid: string,
|
|
158
|
+
config: VaultServiceConfig,
|
|
159
|
+
): Promise<VendResponse> {
|
|
160
|
+
const res = await fetch(`${config.apiUrl}/sts/vend`, {
|
|
161
|
+
method: "POST",
|
|
162
|
+
headers: {
|
|
163
|
+
"Content-Type": "application/json",
|
|
164
|
+
Authorization: `Bearer ${config.authToken}`,
|
|
165
|
+
},
|
|
166
|
+
body: JSON.stringify({
|
|
167
|
+
companyUid,
|
|
168
|
+
durationSeconds: DEFAULT_SESSION_DURATION_SECONDS,
|
|
169
|
+
}),
|
|
170
|
+
});
|
|
171
|
+
if (!res.ok) {
|
|
172
|
+
const body = await res.text();
|
|
173
|
+
throw new Error(
|
|
174
|
+
`STS vend failed for ${companyUid}: ${res.status} ${body}`,
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
return (await res.json()) as VendResponse;
|
|
178
|
+
}
|
package/src/daemon-worker.ts
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Daemon worker — runs as a detached child process
|
|
3
3
|
* Watches HQ directory and syncs changes to S3
|
|
4
|
+
*
|
|
5
|
+
* Day 1: not invoked by CLI surface; retained for future automatic-sync milestone.
|
|
6
|
+
* When re-enabled, this worker will need to resolve an EntityContext before
|
|
7
|
+
* constructing the SyncWatcher. The process argv will need to include company
|
|
8
|
+
* context (slug or UID) and vault-service config.
|
|
4
9
|
*/
|
|
5
10
|
|
|
6
|
-
|
|
11
|
+
// Day 1: SyncWatcher now requires an EntityContext.
|
|
12
|
+
// This file is retained for the automatic-sync milestone but is not functional
|
|
13
|
+
// until the daemon startup path is updated to resolve entity context.
|
|
7
14
|
|
|
8
15
|
const hqRoot = process.argv[2];
|
|
9
16
|
|
|
@@ -12,21 +19,8 @@ if (!hqRoot) {
|
|
|
12
19
|
process.exit(1);
|
|
13
20
|
}
|
|
14
21
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
process.
|
|
20
|
-
watcher.stop();
|
|
21
|
-
process.exit(0);
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
process.on("SIGINT", () => {
|
|
25
|
-
watcher.stop();
|
|
26
|
-
process.exit(0);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
// Keep process alive
|
|
30
|
-
setInterval(() => {
|
|
31
|
-
// Heartbeat — could add remote change polling here
|
|
32
|
-
}, 30_000);
|
|
22
|
+
console.error(
|
|
23
|
+
"Day 1: daemon-worker is not yet wired to entity context resolution. " +
|
|
24
|
+
"Use 'hq share' and 'hq sync' for manual sync.",
|
|
25
|
+
);
|
|
26
|
+
process.exit(1);
|
package/src/daemon.ts
CHANGED
package/src/ignore.ts
CHANGED
|
@@ -1,42 +1,106 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Ignore
|
|
3
|
-
*
|
|
2
|
+
* Ignore-file parser for cloud sync.
|
|
3
|
+
*
|
|
4
|
+
* Three layers, evaluated in order (later patterns override earlier ones):
|
|
5
|
+
* 1. Built-in defaults — things that should *never* sync (VCS, node_modules,
|
|
6
|
+
* build artifacts, caches, env files). Cover the common stacks so that a
|
|
7
|
+
* first-time sync over a random project folder doesn't try to push
|
|
8
|
+
* `target/`, `node_modules/`, or `.next/` to S3.
|
|
9
|
+
* 2. Repo `.gitignore` at hqRoot — reuses the user's existing exclusions so
|
|
10
|
+
* we don't re-list every build directory ourselves. Root-level only; we
|
|
11
|
+
* do not recurse like real git.
|
|
12
|
+
* 3. `.hqignore` (preferred) or `.hqsyncignore` (legacy name) at hqRoot —
|
|
13
|
+
* sync-specific overrides. Use `!pattern` to re-include something an
|
|
14
|
+
* earlier layer excluded.
|
|
4
15
|
*/
|
|
5
16
|
|
|
6
17
|
import * as fs from "fs";
|
|
7
18
|
import * as path from "path";
|
|
8
19
|
import ignore from "ignore";
|
|
9
20
|
|
|
10
|
-
//
|
|
21
|
+
// Patterns that must never sync regardless of project type.
|
|
22
|
+
// Grouped by ecosystem so new stacks are easy to add.
|
|
11
23
|
const DEFAULT_IGNORES = [
|
|
24
|
+
// VCS + OS
|
|
12
25
|
".git/",
|
|
13
26
|
".git",
|
|
14
|
-
"node_modules/",
|
|
15
|
-
"dist/",
|
|
16
27
|
".DS_Store",
|
|
17
28
|
"Thumbs.db",
|
|
29
|
+
|
|
30
|
+
// Node / JS
|
|
31
|
+
"node_modules/",
|
|
32
|
+
"dist/",
|
|
33
|
+
"build/",
|
|
34
|
+
".next/",
|
|
35
|
+
".nuxt/",
|
|
36
|
+
".svelte-kit/",
|
|
37
|
+
".turbo/",
|
|
38
|
+
".parcel-cache/",
|
|
39
|
+
".vite/",
|
|
40
|
+
"coverage/",
|
|
41
|
+
|
|
42
|
+
// Rust / Tauri
|
|
43
|
+
"target/",
|
|
44
|
+
|
|
45
|
+
// Python
|
|
46
|
+
"__pycache__/",
|
|
47
|
+
"*.pyc",
|
|
48
|
+
".pytest_cache/",
|
|
49
|
+
".mypy_cache/",
|
|
50
|
+
".ruff_cache/",
|
|
51
|
+
".venv/",
|
|
52
|
+
"venv/",
|
|
53
|
+
|
|
54
|
+
// Go / JVM / other
|
|
55
|
+
"vendor/",
|
|
56
|
+
"out/",
|
|
57
|
+
"*.class",
|
|
58
|
+
|
|
59
|
+
// Generic caches / temp
|
|
60
|
+
".cache/",
|
|
61
|
+
"tmp/",
|
|
62
|
+
".tmp/",
|
|
63
|
+
|
|
64
|
+
// HQ sync internal state (never round-trip these)
|
|
18
65
|
"*.pid",
|
|
19
66
|
".hq-sync.pid",
|
|
20
67
|
".hq-sync-journal.json",
|
|
21
68
|
".hq-sync-state.json",
|
|
22
69
|
"modules.lock",
|
|
70
|
+
|
|
71
|
+
// HQ repos directory (managed separately, not synced)
|
|
23
72
|
"repos/",
|
|
73
|
+
|
|
74
|
+
// Secrets / env
|
|
24
75
|
".env",
|
|
25
76
|
".env.*",
|
|
26
77
|
];
|
|
27
78
|
|
|
79
|
+
function readIgnoreFile(filePath: string): string | null {
|
|
80
|
+
if (!fs.existsSync(filePath)) return null;
|
|
81
|
+
try {
|
|
82
|
+
return fs.readFileSync(filePath, "utf-8");
|
|
83
|
+
} catch {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
28
88
|
export function createIgnoreFilter(hqRoot: string): (filePath: string) => boolean {
|
|
29
89
|
const ig = ignore();
|
|
30
90
|
|
|
31
|
-
//
|
|
91
|
+
// Layer 1: baseline defaults
|
|
32
92
|
ig.add(DEFAULT_IGNORES);
|
|
33
93
|
|
|
34
|
-
//
|
|
35
|
-
const
|
|
36
|
-
if (
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
94
|
+
// Layer 2: repo's .gitignore (common case — covers most build dirs already)
|
|
95
|
+
const gitignore = readIgnoreFile(path.join(hqRoot, ".gitignore"));
|
|
96
|
+
if (gitignore) ig.add(gitignore);
|
|
97
|
+
|
|
98
|
+
// Layer 3: sync-specific overrides. .hqignore is the documented name;
|
|
99
|
+
// .hqsyncignore is the legacy name we still honor.
|
|
100
|
+
const hqignore =
|
|
101
|
+
readIgnoreFile(path.join(hqRoot, ".hqignore")) ??
|
|
102
|
+
readIgnoreFile(path.join(hqRoot, ".hqsyncignore"));
|
|
103
|
+
if (hqignore) ig.add(hqignore);
|
|
40
104
|
|
|
41
105
|
return (filePath: string): boolean => {
|
|
42
106
|
const relative = path.relative(hqRoot, filePath);
|
package/src/index.ts
CHANGED
|
@@ -1,182 +1,111 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @indigoai-us/hq-cloud — public API
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
|
+
* VLT-5: Entity-aware sync engine. Operations resolve their target bucket
|
|
5
|
+
* and credentials from the vault-service entity registry + STS vending.
|
|
4
6
|
*/
|
|
5
7
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
8
|
+
export {
|
|
9
|
+
resolveEntityContext,
|
|
10
|
+
refreshEntityContext,
|
|
11
|
+
clearContextCache,
|
|
12
|
+
isExpiringSoon,
|
|
13
|
+
} from "./context.js";
|
|
14
|
+
|
|
15
|
+
export {
|
|
16
|
+
uploadFile,
|
|
17
|
+
downloadFile,
|
|
18
|
+
listRemoteFiles,
|
|
19
|
+
deleteRemoteFile,
|
|
20
|
+
headRemoteFile,
|
|
21
|
+
} from "./s3.js";
|
|
22
|
+
|
|
23
|
+
export type { RemoteFile } from "./s3.js";
|
|
24
|
+
|
|
25
|
+
export {
|
|
26
|
+
readJournal,
|
|
27
|
+
writeJournal,
|
|
28
|
+
hashFile,
|
|
29
|
+
updateEntry,
|
|
30
|
+
getEntry,
|
|
31
|
+
removeEntry,
|
|
32
|
+
getJournalPath,
|
|
33
|
+
} from "./journal.js";
|
|
18
34
|
|
|
19
|
-
export
|
|
35
|
+
export {
|
|
36
|
+
createIgnoreFilter,
|
|
37
|
+
isWithinSizeLimit,
|
|
38
|
+
} from "./ignore.js";
|
|
20
39
|
|
|
21
|
-
// Cognito
|
|
22
|
-
// that needs a valid HQ access token (deploy skill, onboarding, etc.).
|
|
40
|
+
// Cognito browser-OAuth (VLT-9)
|
|
23
41
|
export {
|
|
24
42
|
browserLogin,
|
|
25
43
|
refreshTokens,
|
|
26
|
-
getValidAccessToken,
|
|
27
44
|
loadCachedTokens,
|
|
28
45
|
saveCachedTokens,
|
|
29
46
|
clearCachedTokens,
|
|
30
47
|
isExpiring,
|
|
48
|
+
getValidAccessToken,
|
|
31
49
|
CognitoAuthError,
|
|
32
50
|
} from "./cognito-auth.js";
|
|
33
51
|
export type { CognitoAuthConfig, CognitoTokens } from "./cognito-auth.js";
|
|
34
52
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
*/
|
|
95
|
-
export async function pushAll(hqRoot: string): Promise<PushResult> {
|
|
96
|
-
const shouldSync = createIgnoreFilter(hqRoot);
|
|
97
|
-
const journal = readJournal(hqRoot);
|
|
98
|
-
let filesUploaded = 0;
|
|
99
|
-
let bytesUploaded = 0;
|
|
100
|
-
|
|
101
|
-
const files = walkDir(hqRoot, hqRoot, shouldSync);
|
|
102
|
-
|
|
103
|
-
for (const { absolutePath, relativePath } of files) {
|
|
104
|
-
if (!isWithinSizeLimit(absolutePath)) continue;
|
|
105
|
-
|
|
106
|
-
try {
|
|
107
|
-
const hash = hashFile(absolutePath);
|
|
108
|
-
const stat = fs.statSync(absolutePath);
|
|
109
|
-
|
|
110
|
-
await uploadFile(absolutePath, relativePath);
|
|
111
|
-
updateEntry(journal, relativePath, hash, stat.size, "up");
|
|
112
|
-
filesUploaded++;
|
|
113
|
-
bytesUploaded += stat.size;
|
|
114
|
-
} catch (err) {
|
|
115
|
-
console.error(
|
|
116
|
-
` Failed: ${relativePath} — ${err instanceof Error ? err.message : err}`
|
|
117
|
-
);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
writeJournal(hqRoot, journal);
|
|
122
|
-
return { filesUploaded, bytesUploaded };
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Force pull all remote files to local
|
|
127
|
-
*/
|
|
128
|
-
export async function pullAll(hqRoot: string): Promise<PullResult> {
|
|
129
|
-
const journal = readJournal(hqRoot);
|
|
130
|
-
let filesDownloaded = 0;
|
|
131
|
-
let bytesDownloaded = 0;
|
|
132
|
-
|
|
133
|
-
const remoteFiles = await listRemoteFiles();
|
|
134
|
-
|
|
135
|
-
for (const file of remoteFiles) {
|
|
136
|
-
try {
|
|
137
|
-
const localPath = path.join(hqRoot, file.relativePath);
|
|
138
|
-
await downloadFile(file.relativePath, localPath);
|
|
139
|
-
|
|
140
|
-
const hash = hashFile(localPath);
|
|
141
|
-
updateEntry(journal, file.relativePath, hash, file.size, "down");
|
|
142
|
-
filesDownloaded++;
|
|
143
|
-
bytesDownloaded += file.size;
|
|
144
|
-
} catch (err) {
|
|
145
|
-
console.error(
|
|
146
|
-
` Failed: ${file.relativePath} — ${err instanceof Error ? err.message : err}`
|
|
147
|
-
);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
writeJournal(hqRoot, journal);
|
|
152
|
-
return { filesDownloaded, bytesDownloaded };
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Helper: recursively walk a directory
|
|
156
|
-
function walkDir(
|
|
157
|
-
dir: string,
|
|
158
|
-
root: string,
|
|
159
|
-
filter: (p: string) => boolean
|
|
160
|
-
): { absolutePath: string; relativePath: string }[] {
|
|
161
|
-
const results: { absolutePath: string; relativePath: string }[] = [];
|
|
162
|
-
|
|
163
|
-
if (!fs.existsSync(dir)) return results;
|
|
164
|
-
|
|
165
|
-
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
166
|
-
for (const entry of entries) {
|
|
167
|
-
const absolutePath = path.join(dir, entry.name);
|
|
168
|
-
|
|
169
|
-
if (!filter(absolutePath)) continue;
|
|
170
|
-
|
|
171
|
-
if (entry.isDirectory()) {
|
|
172
|
-
results.push(...walkDir(absolutePath, root, filter));
|
|
173
|
-
} else if (entry.isFile()) {
|
|
174
|
-
results.push({
|
|
175
|
-
absolutePath,
|
|
176
|
-
relativePath: path.relative(root, absolutePath),
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
return results;
|
|
182
|
-
}
|
|
53
|
+
// VaultClient SDK (VLT-7)
|
|
54
|
+
export { VaultClient } from "./vault-client.js";
|
|
55
|
+
export {
|
|
56
|
+
VaultClientError,
|
|
57
|
+
VaultAuthError,
|
|
58
|
+
VaultPermissionDeniedError,
|
|
59
|
+
VaultNotFoundError,
|
|
60
|
+
VaultConflictError,
|
|
61
|
+
} from "./vault-client.js";
|
|
62
|
+
export type {
|
|
63
|
+
MembershipRole,
|
|
64
|
+
MembershipStatus,
|
|
65
|
+
Membership,
|
|
66
|
+
CreateInviteInput,
|
|
67
|
+
CreateInviteResult,
|
|
68
|
+
AcceptInviteResult,
|
|
69
|
+
UpdateRoleInput,
|
|
70
|
+
EntityInfo,
|
|
71
|
+
CreateEntityInput,
|
|
72
|
+
CreateEntityResult,
|
|
73
|
+
PendingInviteByEmail,
|
|
74
|
+
} from "./vault-client.js";
|
|
75
|
+
|
|
76
|
+
// STS child vending (VLT-8)
|
|
77
|
+
export type {
|
|
78
|
+
TaskAction,
|
|
79
|
+
TaskScope,
|
|
80
|
+
VendChildInput,
|
|
81
|
+
VendChildResult,
|
|
82
|
+
StsChildCredentials,
|
|
83
|
+
} from "./vault-client.js";
|
|
84
|
+
|
|
85
|
+
// CLI commands
|
|
86
|
+
export { share, sync } from "./cli/index.js";
|
|
87
|
+
export type { ShareOptions, ShareResult, SyncOptions, SyncResult, SyncProgressEvent } from "./cli/index.js";
|
|
88
|
+
export { resolveConflict, showDiff } from "./cli/index.js";
|
|
89
|
+
export type { ConflictStrategy, ConflictInfo, ConflictResolution } from "./cli/index.js";
|
|
90
|
+
|
|
91
|
+
// Membership CLI commands (VLT-7)
|
|
92
|
+
export { invite, listInvites, revokeInvite } from "./cli/index.js";
|
|
93
|
+
export type { InviteOptions, InviteResult, InviteListOptions, InviteRevokeOptions } from "./cli/index.js";
|
|
94
|
+
export { accept, parseToken } from "./cli/index.js";
|
|
95
|
+
export type { AcceptOptions, AcceptResult } from "./cli/index.js";
|
|
96
|
+
export { promote } from "./cli/index.js";
|
|
97
|
+
export type { PromoteOptions, PromoteResult } from "./cli/index.js";
|
|
98
|
+
|
|
99
|
+
export type {
|
|
100
|
+
EntityContext,
|
|
101
|
+
VaultCredentials,
|
|
102
|
+
VaultServiceConfig,
|
|
103
|
+
SyncConfig,
|
|
104
|
+
Credentials,
|
|
105
|
+
JournalEntry,
|
|
106
|
+
SyncJournal,
|
|
107
|
+
SyncStatus,
|
|
108
|
+
PushResult,
|
|
109
|
+
PullResult,
|
|
110
|
+
DaemonState,
|
|
111
|
+
} from "./types.js";
|