@indigoai-us/hq-cloud 6.15.4 → 6.15.6
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/cli/push-scan-exclusions.test.d.ts +2 -0
- package/dist/cli/push-scan-exclusions.test.d.ts.map +1 -0
- package/dist/cli/push-scan-exclusions.test.js +87 -0
- package/dist/cli/push-scan-exclusions.test.js.map +1 -0
- package/dist/cli/rescue-exec-bit-preserve.test.js +8 -0
- package/dist/cli/rescue-exec-bit-preserve.test.js.map +1 -1
- package/dist/cli/share.d.ts.map +1 -1
- package/dist/cli/share.js +21 -1
- package/dist/cli/share.js.map +1 -1
- package/dist/cli/share.test.js +82 -0
- package/dist/cli/share.test.js.map +1 -1
- package/dist/cli/sync.d.ts +23 -0
- package/dist/cli/sync.d.ts.map +1 -1
- package/dist/cli/sync.js +9 -0
- package/dist/cli/sync.js.map +1 -1
- package/dist/lib/net-errors.d.ts.map +1 -1
- package/dist/lib/net-errors.js +4 -0
- package/dist/lib/net-errors.js.map +1 -1
- package/dist/machine-auth.test.js +7 -1
- package/dist/machine-auth.test.js.map +1 -1
- package/dist/object-io.d.ts +21 -0
- package/dist/object-io.d.ts.map +1 -1
- package/dist/object-io.js +229 -77
- package/dist/object-io.js.map +1 -1
- package/dist/object-io.test.js +211 -1
- package/dist/object-io.test.js.map +1 -1
- package/dist/outposts/billing.d.ts +41 -0
- package/dist/outposts/billing.d.ts.map +1 -0
- package/dist/outposts/billing.js +53 -0
- package/dist/outposts/billing.js.map +1 -0
- package/dist/outposts/cli-billing.d.ts +59 -0
- package/dist/outposts/cli-billing.d.ts.map +1 -0
- package/dist/outposts/cli-billing.js +93 -0
- package/dist/outposts/cli-billing.js.map +1 -0
- package/dist/outposts/cli.d.ts +13 -0
- package/dist/outposts/cli.d.ts.map +1 -0
- package/dist/outposts/cli.js +13 -0
- package/dist/outposts/cli.js.map +1 -0
- package/dist/outposts/client.d.ts +140 -0
- package/dist/outposts/client.d.ts.map +1 -0
- package/dist/outposts/client.js +271 -0
- package/dist/outposts/client.js.map +1 -0
- package/dist/outposts/client.test.d.ts +9 -0
- package/dist/outposts/client.test.d.ts.map +1 -0
- package/dist/outposts/client.test.js +226 -0
- package/dist/outposts/client.test.js.map +1 -0
- package/dist/outposts/command.d.ts +74 -0
- package/dist/outposts/command.d.ts.map +1 -0
- package/dist/outposts/command.js +661 -0
- package/dist/outposts/command.js.map +1 -0
- package/dist/outposts/command.test.d.ts +18 -0
- package/dist/outposts/command.test.d.ts.map +1 -0
- package/dist/outposts/command.test.js +933 -0
- package/dist/outposts/command.test.js.map +1 -0
- package/dist/outposts/errors.d.ts +32 -0
- package/dist/outposts/errors.d.ts.map +1 -0
- package/dist/outposts/errors.js +72 -0
- package/dist/outposts/errors.js.map +1 -0
- package/dist/outposts/heartbeat-command.d.ts +106 -0
- package/dist/outposts/heartbeat-command.d.ts.map +1 -0
- package/dist/outposts/heartbeat-command.js +186 -0
- package/dist/outposts/heartbeat-command.js.map +1 -0
- package/dist/outposts/host-deps.d.ts +49 -0
- package/dist/outposts/host-deps.d.ts.map +1 -0
- package/dist/outposts/host-deps.js +93 -0
- package/dist/outposts/host-deps.js.map +1 -0
- package/dist/outposts/index.d.ts +36 -0
- package/dist/outposts/index.d.ts.map +1 -0
- package/dist/outposts/index.js +32 -0
- package/dist/outposts/index.js.map +1 -0
- package/dist/outposts/node.d.ts +23 -0
- package/dist/outposts/node.d.ts.map +1 -0
- package/dist/outposts/node.js +22 -0
- package/dist/outposts/node.js.map +1 -0
- package/dist/outposts/remote-command.d.ts +30 -0
- package/dist/outposts/remote-command.d.ts.map +1 -0
- package/dist/outposts/remote-command.js +39 -0
- package/dist/outposts/remote-command.js.map +1 -0
- package/dist/outposts/replica-sync.d.ts +40 -0
- package/dist/outposts/replica-sync.d.ts.map +1 -0
- package/dist/outposts/replica-sync.js +188 -0
- package/dist/outposts/replica-sync.js.map +1 -0
- package/dist/outposts/replica-sync.test.d.ts +2 -0
- package/dist/outposts/replica-sync.test.d.ts.map +1 -0
- package/dist/outposts/replica-sync.test.js +224 -0
- package/dist/outposts/replica-sync.test.js.map +1 -0
- package/dist/outposts/self-deploy.d.ts +47 -0
- package/dist/outposts/self-deploy.d.ts.map +1 -0
- package/dist/outposts/self-deploy.js +188 -0
- package/dist/outposts/self-deploy.js.map +1 -0
- package/dist/outposts/self-deploy.test.d.ts +2 -0
- package/dist/outposts/self-deploy.test.d.ts.map +1 -0
- package/dist/outposts/self-deploy.test.js +186 -0
- package/dist/outposts/self-deploy.test.js.map +1 -0
- package/dist/outposts/session-heartbeat-bounds.test.d.ts +25 -0
- package/dist/outposts/session-heartbeat-bounds.test.d.ts.map +1 -0
- package/dist/outposts/session-heartbeat-bounds.test.js +156 -0
- package/dist/outposts/session-heartbeat-bounds.test.js.map +1 -0
- package/dist/outposts/session-heartbeat-guard.test.d.ts +16 -0
- package/dist/outposts/session-heartbeat-guard.test.d.ts.map +1 -0
- package/dist/outposts/session-heartbeat-guard.test.js +88 -0
- package/dist/outposts/session-heartbeat-guard.test.js.map +1 -0
- package/dist/outposts/session-heartbeat-publisher.d.ts +76 -0
- package/dist/outposts/session-heartbeat-publisher.d.ts.map +1 -0
- package/dist/outposts/session-heartbeat-publisher.js +117 -0
- package/dist/outposts/session-heartbeat-publisher.js.map +1 -0
- package/dist/outposts/session-heartbeat-publisher.test.d.ts +10 -0
- package/dist/outposts/session-heartbeat-publisher.test.d.ts.map +1 -0
- package/dist/outposts/session-heartbeat-publisher.test.js +153 -0
- package/dist/outposts/session-heartbeat-publisher.test.js.map +1 -0
- package/dist/outposts/session-heartbeat-retain-guard.test.d.ts +18 -0
- package/dist/outposts/session-heartbeat-retain-guard.test.d.ts.map +1 -0
- package/dist/outposts/session-heartbeat-retain-guard.test.js +106 -0
- package/dist/outposts/session-heartbeat-retain-guard.test.js.map +1 -0
- package/dist/outposts/session-heartbeat.d.ts +210 -0
- package/dist/outposts/session-heartbeat.d.ts.map +1 -0
- package/dist/outposts/session-heartbeat.js +657 -0
- package/dist/outposts/session-heartbeat.js.map +1 -0
- package/dist/outposts/session-heartbeat.test.d.ts +11 -0
- package/dist/outposts/session-heartbeat.test.d.ts.map +1 -0
- package/dist/outposts/session-heartbeat.test.js +370 -0
- package/dist/outposts/session-heartbeat.test.js.map +1 -0
- package/dist/outposts/ssh.d.ts +29 -0
- package/dist/outposts/ssh.d.ts.map +1 -0
- package/dist/outposts/ssh.js +66 -0
- package/dist/outposts/ssh.js.map +1 -0
- package/dist/outposts/transport.d.ts +76 -0
- package/dist/outposts/transport.d.ts.map +1 -0
- package/dist/outposts/transport.js +91 -0
- package/dist/outposts/transport.js.map +1 -0
- package/dist/outposts/transport.test.d.ts +10 -0
- package/dist/outposts/transport.test.d.ts.map +1 -0
- package/dist/outposts/transport.test.js +116 -0
- package/dist/outposts/transport.test.js.map +1 -0
- package/dist/outposts/types.d.ts +125 -0
- package/dist/outposts/types.d.ts.map +1 -0
- package/dist/outposts/types.js +13 -0
- package/dist/outposts/types.js.map +1 -0
- package/dist/personal-vault.d.ts +69 -0
- package/dist/personal-vault.d.ts.map +1 -1
- package/dist/personal-vault.js +100 -0
- package/dist/personal-vault.js.map +1 -1
- package/dist/personal-vault.test.js +140 -1
- package/dist/personal-vault.test.js.map +1 -1
- package/dist/s3.d.ts.map +1 -1
- package/dist/s3.js +62 -0
- package/dist/s3.js.map +1 -1
- package/dist/s3.test.js +126 -0
- package/dist/s3.test.js.map +1 -1
- package/package.json +39 -3
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The small local-environment surface the box-side commands touch, injected so
|
|
3
|
+
* they are testable without an actual EC2 host.
|
|
4
|
+
*
|
|
5
|
+
* Node-only. `self-deploy` needs the base set; `replica-sync` extends it with two
|
|
6
|
+
* filesystem probes for the repo clone, kept as an extension so the self-deploy
|
|
7
|
+
* surface stays minimal.
|
|
8
|
+
*/
|
|
9
|
+
import { spawnSync } from "node:child_process";
|
|
10
|
+
import * as fs from "node:fs";
|
|
11
|
+
import * as os from "node:os";
|
|
12
|
+
import * as path from "node:path";
|
|
13
|
+
import * as readline from "node:readline";
|
|
14
|
+
import { loadCachedTokens } from "../cognito-auth.js";
|
|
15
|
+
export const defaultSelfDeployDependencies = {
|
|
16
|
+
spawnSync: (command, args, options) => spawnSync(command, args, options),
|
|
17
|
+
readTextFile: (file) => fs.readFileSync(file, "utf8"),
|
|
18
|
+
loadCachedTokens: () => loadCachedTokens() ?? undefined,
|
|
19
|
+
getUid: () => process.getuid?.(),
|
|
20
|
+
isStdinTty: () => process.stdin.isTTY === true,
|
|
21
|
+
confirm: async () => {
|
|
22
|
+
const rl = readline.createInterface({
|
|
23
|
+
input: process.stdin,
|
|
24
|
+
output: process.stdout,
|
|
25
|
+
});
|
|
26
|
+
return new Promise((resolve) => {
|
|
27
|
+
rl.question("", (answer) => {
|
|
28
|
+
rl.close();
|
|
29
|
+
resolve(/^y(es)?$/i.test(answer.trim()));
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
},
|
|
33
|
+
defaultHqRoot: () => process.env.HQ_ROOT ?? path.join(os.homedir(), "hq"),
|
|
34
|
+
invokingUser: () => process.env.SUDO_USER ?? process.env.USER ?? os.userInfo().username,
|
|
35
|
+
};
|
|
36
|
+
export const defaultReplicaSyncDependencies = {
|
|
37
|
+
...defaultSelfDeployDependencies,
|
|
38
|
+
pathExists: (p) => fs.existsSync(p),
|
|
39
|
+
mkdirp: (p) => {
|
|
40
|
+
fs.mkdirSync(p, { recursive: true });
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Run a child and throw a plain description on any failure.
|
|
45
|
+
*
|
|
46
|
+
* Child output is deliberately excluded from the thrown message: even though
|
|
47
|
+
* these callers never pass credentials in argv, several of them stream secrets
|
|
48
|
+
* through a child's stdin or environment, so the error path stays secret-safe by
|
|
49
|
+
* construction rather than by review.
|
|
50
|
+
*/
|
|
51
|
+
export function runChecked(deps, command, args, description, options) {
|
|
52
|
+
let result;
|
|
53
|
+
try {
|
|
54
|
+
result = deps.spawnSync(command, args, options);
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
throw new Error(`${description} could not be started.`);
|
|
58
|
+
}
|
|
59
|
+
if (!result || result.error || result.status !== 0) {
|
|
60
|
+
throw new Error(`${description} failed. Resolve the error and try again.`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/** `runChecked` under `sudo` unless already root. */
|
|
64
|
+
export function runPrivileged(deps, command, args, description, options) {
|
|
65
|
+
if (deps.getUid() === 0) {
|
|
66
|
+
runChecked(deps, command, args, description, options);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
runChecked(deps, "sudo", [command, ...args], description, options);
|
|
70
|
+
}
|
|
71
|
+
/** True when `command args` exits 0. Never throws. */
|
|
72
|
+
export function commandSucceeded(deps, command, args) {
|
|
73
|
+
try {
|
|
74
|
+
const result = deps.spawnSync(command, args, { encoding: "utf8" });
|
|
75
|
+
return !!result && !result.error && result.status === 0;
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/** Trimmed stdout of `command args`, or undefined when it failed. Never throws. */
|
|
82
|
+
export function commandOutput(deps, command, args) {
|
|
83
|
+
try {
|
|
84
|
+
const result = deps.spawnSync(command, args, { encoding: "utf8" });
|
|
85
|
+
if (!result || result.error || result.status !== 0)
|
|
86
|
+
return undefined;
|
|
87
|
+
return String(result.stdout ?? "").trim();
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=host-deps.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host-deps.js","sourceRoot":"","sources":["../../src/outposts/host-deps.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@indigoai-us/hq-cloud/outposts` — the portable Outpost control-plane client.
|
|
3
|
+
*
|
|
4
|
+
* This entry point is deliberately free of Node built-ins, CLI dependencies, and
|
|
5
|
+
* module-level side effects, so it can be imported straight into a Next.js server
|
|
6
|
+
* action, a route handler, or an edge function. It is also why the subpath exists
|
|
7
|
+
* at all: the package root (`@indigoai-us/hq-cloud`) pulls in the AWS SDK,
|
|
8
|
+
* `chokidar`, and `pino`, none of which belong in a web app's module graph.
|
|
9
|
+
*
|
|
10
|
+
* Server-side only. Every call carries a bearer token, so none of this may run in
|
|
11
|
+
* a client component.
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* // app/lib/outposts.ts
|
|
15
|
+
* import { OutpostsClient, createOutpostTransport } from "@indigoai-us/hq-cloud/outposts";
|
|
16
|
+
*
|
|
17
|
+
* export const outposts = new OutpostsClient({
|
|
18
|
+
* transport: createOutpostTransport(),
|
|
19
|
+
* token: async () => (await auth())!.idToken,
|
|
20
|
+
* });
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* Node-only helpers (SSH exec, the self-hosting installer, the box-side
|
|
24
|
+
* heartbeat) live at `@indigoai-us/hq-cloud/outposts/node`. The `hq outposts`
|
|
25
|
+
* command tree lives at `@indigoai-us/hq-cloud/outposts/cli`.
|
|
26
|
+
*/
|
|
27
|
+
export { OutpostsClient, EXEC_MAX_TIMEOUT_SECONDS } from "./client.js";
|
|
28
|
+
export type { OutpostsClientOptions, OutpostTokenSource, WaitForExecOptions, } from "./client.js";
|
|
29
|
+
export { createOutpostTransport, defaultOutpostApiUrl, } from "./transport.js";
|
|
30
|
+
export type { FetchLike, OutpostRequest, OutpostTransport, } from "./transport.js";
|
|
31
|
+
export { OutpostHttpError, parseCappedPayload, parseBillingPayload, isBillingSetupAction, } from "./errors.js";
|
|
32
|
+
export { joinCommandParts, withRemoteHqDir, REMOTE_HQ_DIR_PREFIX, } from "./remote-command.js";
|
|
33
|
+
export { AGENT_PRICE_CENTS, OUTPOST_PRICE_CENTS, formatUsd, mintPaymentLink, } from "./billing.js";
|
|
34
|
+
export type { PaidResource } from "./billing.js";
|
|
35
|
+
export type { BillingErrorPayload, BillingSetupAction, OutpostAgentRuntime, OutpostCappedPayload, OutpostExecAsyncResult, OutpostExecResult, OutpostExecStage, OutpostExecSubmission, OutpostSshAccess, OutpostSummary, ProvisionOutpostInput, } from "./types.js";
|
|
36
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/outposts/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,cAAc,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AACvE,YAAY,EACV,qBAAqB,EACrB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,SAAS,EACT,cAAc,EACd,gBAAgB,GACjB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,SAAS,EACT,eAAe,GAChB,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjD,YAAY,EACV,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,EACpB,sBAAsB,EACtB,iBAAiB,EACjB,gBAAgB,EAChB,qBAAqB,EACrB,gBAAgB,EAChB,cAAc,EACd,qBAAqB,GACtB,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@indigoai-us/hq-cloud/outposts` — the portable Outpost control-plane client.
|
|
3
|
+
*
|
|
4
|
+
* This entry point is deliberately free of Node built-ins, CLI dependencies, and
|
|
5
|
+
* module-level side effects, so it can be imported straight into a Next.js server
|
|
6
|
+
* action, a route handler, or an edge function. It is also why the subpath exists
|
|
7
|
+
* at all: the package root (`@indigoai-us/hq-cloud`) pulls in the AWS SDK,
|
|
8
|
+
* `chokidar`, and `pino`, none of which belong in a web app's module graph.
|
|
9
|
+
*
|
|
10
|
+
* Server-side only. Every call carries a bearer token, so none of this may run in
|
|
11
|
+
* a client component.
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* // app/lib/outposts.ts
|
|
15
|
+
* import { OutpostsClient, createOutpostTransport } from "@indigoai-us/hq-cloud/outposts";
|
|
16
|
+
*
|
|
17
|
+
* export const outposts = new OutpostsClient({
|
|
18
|
+
* transport: createOutpostTransport(),
|
|
19
|
+
* token: async () => (await auth())!.idToken,
|
|
20
|
+
* });
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* Node-only helpers (SSH exec, the self-hosting installer, the box-side
|
|
24
|
+
* heartbeat) live at `@indigoai-us/hq-cloud/outposts/node`. The `hq outposts`
|
|
25
|
+
* command tree lives at `@indigoai-us/hq-cloud/outposts/cli`.
|
|
26
|
+
*/
|
|
27
|
+
export { OutpostsClient, EXEC_MAX_TIMEOUT_SECONDS } from "./client.js";
|
|
28
|
+
export { createOutpostTransport, defaultOutpostApiUrl, } from "./transport.js";
|
|
29
|
+
export { OutpostHttpError, parseCappedPayload, parseBillingPayload, isBillingSetupAction, } from "./errors.js";
|
|
30
|
+
export { joinCommandParts, withRemoteHqDir, REMOTE_HQ_DIR_PREFIX, } from "./remote-command.js";
|
|
31
|
+
export { AGENT_PRICE_CENTS, OUTPOST_PRICE_CENTS, formatUsd, mintPaymentLink, } from "./billing.js";
|
|
32
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/outposts/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,cAAc,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAOvE,OAAO,EACL,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,gBAAgB,CAAC;AAOxB,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,SAAS,EACT,eAAe,GAChB,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@indigoai-us/hq-cloud/outposts/node` — Node-only Outpost helpers.
|
|
3
|
+
*
|
|
4
|
+
* Everything here touches the filesystem, spawns processes, or talks to AWS IoT,
|
|
5
|
+
* so it cannot run on an edge runtime and is kept out of the portable
|
|
6
|
+
* `@indigoai-us/hq-cloud/outposts` entry point. A Next.js app running on the Node
|
|
7
|
+
* runtime *can* import this, but almost certainly wants the portable client
|
|
8
|
+
* instead — these are host-side and box-side operations.
|
|
9
|
+
*/
|
|
10
|
+
export { execViaSsh } from "./ssh.js";
|
|
11
|
+
export type { SshExecResult } from "./ssh.js";
|
|
12
|
+
export { defaultSelfDeployDependencies, defaultReplicaSyncDependencies, runChecked, runPrivileged, commandSucceeded, commandOutput, } from "./host-deps.js";
|
|
13
|
+
export type { SelfDeployDependencies, ReplicaSyncDependencies, } from "./host-deps.js";
|
|
14
|
+
export { selfDeployOutpost, selfDeployPreflight, hqIdentityFromSession, renderSelfDeploySyncScript, renderSelfDeployService, } from "./self-deploy.js";
|
|
15
|
+
export { replicaSyncOutpost, parseReposManifest } from "./replica-sync.js";
|
|
16
|
+
export type { ReplicaRepo } from "./replica-sync.js";
|
|
17
|
+
export { runHeartbeatLoop, resolveIntervalSeconds, writeHeartbeatState, DEFAULT_HEARTBEAT_STATE_FILE, } from "./heartbeat-command.js";
|
|
18
|
+
export type { HeartbeatLoopDeps, HeartbeatLoopOptions, HeartbeatCommandDeps, } from "./heartbeat-command.js";
|
|
19
|
+
export { DEFAULT_HEARTBEAT_INTERVAL_SECONDS, collectSessions, nodeFileSystem, sessionsTopicForPerson, assertNoSecretsInPayload, } from "./session-heartbeat.js";
|
|
20
|
+
export type { FileSystemPort, PublishPort, SessionsHeartbeatPayload, } from "./session-heartbeat.js";
|
|
21
|
+
export { createIotPublishPort, defaultRealtimeCredentialsFetcher, } from "./session-heartbeat-publisher.js";
|
|
22
|
+
export type { RealtimeCredentialsResponse } from "./session-heartbeat-publisher.js";
|
|
23
|
+
//# sourceMappingURL=node.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../src/outposts/node.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,YAAY,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAG9C,OAAO,EACL,6BAA6B,EAC7B,8BAA8B,EAC9B,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,aAAa,GACd,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,qBAAqB,EACrB,0BAA0B,EAC1B,uBAAuB,GACxB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC3E,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,mBAAmB,EACnB,4BAA4B,GAC7B,MAAM,wBAAwB,CAAC;AAChC,YAAY,EACV,iBAAiB,EACjB,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,kCAAkC,EAClC,eAAe,EACf,cAAc,EACd,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EACV,cAAc,EACd,WAAW,EACX,wBAAwB,GACzB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,oBAAoB,EACpB,iCAAiC,GAClC,MAAM,kCAAkC,CAAC;AAC1C,YAAY,EAAE,2BAA2B,EAAE,MAAM,kCAAkC,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@indigoai-us/hq-cloud/outposts/node` — Node-only Outpost helpers.
|
|
3
|
+
*
|
|
4
|
+
* Everything here touches the filesystem, spawns processes, or talks to AWS IoT,
|
|
5
|
+
* so it cannot run on an edge runtime and is kept out of the portable
|
|
6
|
+
* `@indigoai-us/hq-cloud/outposts` entry point. A Next.js app running on the Node
|
|
7
|
+
* runtime *can* import this, but almost certainly wants the portable client
|
|
8
|
+
* instead — these are host-side and box-side operations.
|
|
9
|
+
*/
|
|
10
|
+
// SSH fallback for boxes without an SSM agent (Lightsail).
|
|
11
|
+
export { execViaSsh } from "./ssh.js";
|
|
12
|
+
// Injected host-environment surface shared by the box-side commands.
|
|
13
|
+
export { defaultSelfDeployDependencies, defaultReplicaSyncDependencies, runChecked, runPrivileged, commandSucceeded, commandOutput, } from "./host-deps.js";
|
|
14
|
+
// Turn the current host into an unmanaged, self-hosted outpost.
|
|
15
|
+
export { selfDeployOutpost, selfDeployPreflight, hqIdentityFromSession, renderSelfDeploySyncScript, renderSelfDeployService, } from "./self-deploy.js";
|
|
16
|
+
// Make a managed box a full replica of the operator's HQ.
|
|
17
|
+
export { replicaSyncOutpost, parseReposManifest } from "./replica-sync.js";
|
|
18
|
+
// Box-side agent-session heartbeat.
|
|
19
|
+
export { runHeartbeatLoop, resolveIntervalSeconds, writeHeartbeatState, DEFAULT_HEARTBEAT_STATE_FILE, } from "./heartbeat-command.js";
|
|
20
|
+
export { DEFAULT_HEARTBEAT_INTERVAL_SECONDS, collectSessions, nodeFileSystem, sessionsTopicForPerson, assertNoSecretsInPayload, } from "./session-heartbeat.js";
|
|
21
|
+
export { createIotPublishPort, defaultRealtimeCredentialsFetcher, } from "./session-heartbeat-publisher.js";
|
|
22
|
+
//# sourceMappingURL=node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node.js","sourceRoot":"","sources":["../../src/outposts/node.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,2DAA2D;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAGtC,qEAAqE;AACrE,OAAO,EACL,6BAA6B,EAC7B,8BAA8B,EAC9B,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,aAAa,GACd,MAAM,gBAAgB,CAAC;AAMxB,gEAAgE;AAChE,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,qBAAqB,EACrB,0BAA0B,EAC1B,uBAAuB,GACxB,MAAM,kBAAkB,CAAC;AAE1B,0DAA0D;AAC1D,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAG3E,oCAAoC;AACpC,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,mBAAmB,EACnB,4BAA4B,GAC7B,MAAM,wBAAwB,CAAC;AAOhC,OAAO,EACL,kCAAkC,EAClC,eAAe,EACf,cAAc,EACd,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,wBAAwB,CAAC;AAOhC,OAAO,EACL,oBAAoB,EACpB,iCAAiC,GAClC,MAAM,kCAAkC,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure string helpers for shaping a command before it is handed to a box.
|
|
3
|
+
*
|
|
4
|
+
* No imports, no I/O — these are shared verbatim between the CLI and any other
|
|
5
|
+
* caller that needs to build the same remote command line.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Preserve a single command string; safely join argv when an arg parser split it.
|
|
9
|
+
*
|
|
10
|
+
* A single element is passed through untouched so an already-quoted command
|
|
11
|
+
* survives intact; multiple elements are individually quoted and joined, which
|
|
12
|
+
* is what makes `outposts exec -- ls "my dir"` behave.
|
|
13
|
+
*/
|
|
14
|
+
export declare function joinCommandParts(commandParts: string[]): string;
|
|
15
|
+
/**
|
|
16
|
+
* Prefix that best-effort `cd`s into the box's HQ checkout before running the
|
|
17
|
+
* caller's command. `exec` runs over two transports with two different default
|
|
18
|
+
* working directories — SSM runs as root with no `$HOME` (cwd `/usr/bin`) and
|
|
19
|
+
* SSH lands in the login user's home — so without this, `exec -- pwd` printed an
|
|
20
|
+
* unhelpful, transport-dependent directory. Initialize a real root home for the
|
|
21
|
+
* SSM case before resolving the HQ folder: tools run by the caller (notably
|
|
22
|
+
* `gh`) otherwise treat the HQ checkout as their home and can create root-owned
|
|
23
|
+
* machine state inside it. The trailing `|| true` keeps the command running from
|
|
24
|
+
* the default directory when no HQ checkout is present, so exec never fails
|
|
25
|
+
* merely because the box has no HQ folder.
|
|
26
|
+
*/
|
|
27
|
+
export declare const REMOTE_HQ_DIR_PREFIX = "export HOME=\"${HOME:-/root}\"; cd \"$HOME/hq\" 2>/dev/null || cd ~ec2-user/hq 2>/dev/null || true";
|
|
28
|
+
/** Wrap `command` so it runs from the box's HQ folder (see {@link REMOTE_HQ_DIR_PREFIX}). */
|
|
29
|
+
export declare function withRemoteHqDir(command: string): string;
|
|
30
|
+
//# sourceMappingURL=remote-command.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote-command.d.ts","sourceRoot":"","sources":["../../src/outposts/remote-command.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,MAAM,CAG/D;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,oBAAoB,uGACiE,CAAC;AAEnG,6FAA6F;AAC7F,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEvD"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure string helpers for shaping a command before it is handed to a box.
|
|
3
|
+
*
|
|
4
|
+
* No imports, no I/O — these are shared verbatim between the CLI and any other
|
|
5
|
+
* caller that needs to build the same remote command line.
|
|
6
|
+
*/
|
|
7
|
+
function shellQuote(part) {
|
|
8
|
+
return `'${part.replace(/'/g, `'\\''`)}'`;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Preserve a single command string; safely join argv when an arg parser split it.
|
|
12
|
+
*
|
|
13
|
+
* A single element is passed through untouched so an already-quoted command
|
|
14
|
+
* survives intact; multiple elements are individually quoted and joined, which
|
|
15
|
+
* is what makes `outposts exec -- ls "my dir"` behave.
|
|
16
|
+
*/
|
|
17
|
+
export function joinCommandParts(commandParts) {
|
|
18
|
+
if (commandParts.length === 1)
|
|
19
|
+
return commandParts[0];
|
|
20
|
+
return commandParts.map(shellQuote).join(" ");
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Prefix that best-effort `cd`s into the box's HQ checkout before running the
|
|
24
|
+
* caller's command. `exec` runs over two transports with two different default
|
|
25
|
+
* working directories — SSM runs as root with no `$HOME` (cwd `/usr/bin`) and
|
|
26
|
+
* SSH lands in the login user's home — so without this, `exec -- pwd` printed an
|
|
27
|
+
* unhelpful, transport-dependent directory. Initialize a real root home for the
|
|
28
|
+
* SSM case before resolving the HQ folder: tools run by the caller (notably
|
|
29
|
+
* `gh`) otherwise treat the HQ checkout as their home and can create root-owned
|
|
30
|
+
* machine state inside it. The trailing `|| true` keeps the command running from
|
|
31
|
+
* the default directory when no HQ checkout is present, so exec never fails
|
|
32
|
+
* merely because the box has no HQ folder.
|
|
33
|
+
*/
|
|
34
|
+
export const REMOTE_HQ_DIR_PREFIX = 'export HOME="${HOME:-/root}"; cd "$HOME/hq" 2>/dev/null || cd ~ec2-user/hq 2>/dev/null || true';
|
|
35
|
+
/** Wrap `command` so it runs from the box's HQ folder (see {@link REMOTE_HQ_DIR_PREFIX}). */
|
|
36
|
+
export function withRemoteHqDir(command) {
|
|
37
|
+
return `${REMOTE_HQ_DIR_PREFIX}; ${command}`;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=remote-command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote-command.js","sourceRoot":"","sources":["../../src/outposts/remote-command.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,SAAS,UAAU,CAAC,IAAY;IAC9B,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC;AAC5C,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,YAAsB;IACrD,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,YAAY,CAAC,CAAC,CAAC,CAAC;IACtD,OAAO,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAChD,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAC/B,gGAAgG,CAAC;AAEnG,6FAA6F;AAC7F,MAAM,UAAU,eAAe,CAAC,OAAe;IAC7C,OAAO,GAAG,oBAAoB,KAAK,OAAO,EAAE,CAAC;AAC/C,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `outposts replica-sync` — make a MANAGED box a full replica of the operator's HQ.
|
|
3
|
+
*
|
|
4
|
+
* A box provisioned via `outposts provision` runs this on a systemd timer:
|
|
5
|
+
* 1. `hq auth refresh` — freshen the seeded Cognito session
|
|
6
|
+
* 2. `hq sync pull --personal` — the personal vault (repos.yaml, policies, …)
|
|
7
|
+
* 3. `hq rescue` — re-lay the core kernel (needs `companies/`)
|
|
8
|
+
* 4. clone `repos.yaml` — the operator's OWN repos, gh-authed via vault
|
|
9
|
+
*
|
|
10
|
+
* Unlike `self-deploy` (a manual, unmanaged, all-company watcher on a box you
|
|
11
|
+
* own), replica-sync is GENERIC: it works for every HQ user off their own personal
|
|
12
|
+
* vault, with no per-operator setup script.
|
|
13
|
+
*
|
|
14
|
+
* It is deliberately best-effort end to end. A repeating timer must never leave
|
|
15
|
+
* the systemd unit in `failed` just because one transient step — a mirror blip, a
|
|
16
|
+
* not-yet-signed-in box — didn't complete this cycle. Every step logs its outcome
|
|
17
|
+
* and the run continues.
|
|
18
|
+
*
|
|
19
|
+
* Node-only: shells out to `hq`, `git`, and `gh`, and probes the filesystem.
|
|
20
|
+
*/
|
|
21
|
+
import type { ReplicaSyncDependencies } from "./host-deps.js";
|
|
22
|
+
/** One repo entry resolved from `personal/data/repos.yaml`. */
|
|
23
|
+
export interface ReplicaRepo {
|
|
24
|
+
url: string;
|
|
25
|
+
visibility: "public" | "private";
|
|
26
|
+
name: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Parse `personal/data/repos.yaml` into a clone list.
|
|
30
|
+
*
|
|
31
|
+
* Mirrors `setup.sh`'s `clone_repos`: `visibility` defaults to public, `name`
|
|
32
|
+
* defaults to the URL basename. Malformed entries are skipped rather than thrown,
|
|
33
|
+
* so one bad row cannot stop the replica cycle.
|
|
34
|
+
*/
|
|
35
|
+
export declare function parseReposManifest(yamlText: string): ReplicaRepo[];
|
|
36
|
+
/** Run one replica cycle. Best-effort throughout — see the module doc comment. */
|
|
37
|
+
export declare function replicaSyncOutpost(opts: {
|
|
38
|
+
hqRoot?: string;
|
|
39
|
+
}, deps: ReplicaSyncDependencies): Promise<void>;
|
|
40
|
+
//# sourceMappingURL=replica-sync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replica-sync.d.ts","sourceRoot":"","sources":["../../src/outposts/replica-sync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAG9D,+DAA+D;AAC/D,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,QAAQ,GAAG,SAAS,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC;CACd;AAsBD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,EAAE,CAyBlE;AAiHD,kFAAkF;AAClF,wBAAsB,kBAAkB,CACtC,IAAI,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,EACzB,IAAI,EAAE,uBAAuB,GAC5B,OAAO,CAAC,IAAI,CAAC,CAgDf"}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `outposts replica-sync` — make a MANAGED box a full replica of the operator's HQ.
|
|
3
|
+
*
|
|
4
|
+
* A box provisioned via `outposts provision` runs this on a systemd timer:
|
|
5
|
+
* 1. `hq auth refresh` — freshen the seeded Cognito session
|
|
6
|
+
* 2. `hq sync pull --personal` — the personal vault (repos.yaml, policies, …)
|
|
7
|
+
* 3. `hq rescue` — re-lay the core kernel (needs `companies/`)
|
|
8
|
+
* 4. clone `repos.yaml` — the operator's OWN repos, gh-authed via vault
|
|
9
|
+
*
|
|
10
|
+
* Unlike `self-deploy` (a manual, unmanaged, all-company watcher on a box you
|
|
11
|
+
* own), replica-sync is GENERIC: it works for every HQ user off their own personal
|
|
12
|
+
* vault, with no per-operator setup script.
|
|
13
|
+
*
|
|
14
|
+
* It is deliberately best-effort end to end. A repeating timer must never leave
|
|
15
|
+
* the systemd unit in `failed` just because one transient step — a mirror blip, a
|
|
16
|
+
* not-yet-signed-in box — didn't complete this cycle. Every step logs its outcome
|
|
17
|
+
* and the run continues.
|
|
18
|
+
*
|
|
19
|
+
* Node-only: shells out to `hq`, `git`, and `gh`, and probes the filesystem.
|
|
20
|
+
*/
|
|
21
|
+
import * as path from "node:path";
|
|
22
|
+
import * as yaml from "js-yaml";
|
|
23
|
+
function repoNameFromUrl(url) {
|
|
24
|
+
const tail = url.split("/").pop() ?? "";
|
|
25
|
+
return tail.replace(/\.git$/, "").trim();
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* A repo `name` becomes a path segment under `repos/<visibility>/`. A synced
|
|
29
|
+
* repos.yaml is operator data, but a malformed or hostile name must never escape
|
|
30
|
+
* that directory, so constrain it to a single, non-traversal component.
|
|
31
|
+
*/
|
|
32
|
+
function isSafePathComponent(value) {
|
|
33
|
+
return (value.length > 0 &&
|
|
34
|
+
!value.includes("/") &&
|
|
35
|
+
!value.includes("\\") &&
|
|
36
|
+
value !== "." &&
|
|
37
|
+
value !== "..");
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Parse `personal/data/repos.yaml` into a clone list.
|
|
41
|
+
*
|
|
42
|
+
* Mirrors `setup.sh`'s `clone_repos`: `visibility` defaults to public, `name`
|
|
43
|
+
* defaults to the URL basename. Malformed entries are skipped rather than thrown,
|
|
44
|
+
* so one bad row cannot stop the replica cycle.
|
|
45
|
+
*/
|
|
46
|
+
export function parseReposManifest(yamlText) {
|
|
47
|
+
let doc;
|
|
48
|
+
try {
|
|
49
|
+
doc = yaml.load(yamlText);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return [];
|
|
53
|
+
}
|
|
54
|
+
const rawRepos = doc?.repos;
|
|
55
|
+
if (!Array.isArray(rawRepos))
|
|
56
|
+
return [];
|
|
57
|
+
const out = [];
|
|
58
|
+
for (const entry of rawRepos) {
|
|
59
|
+
if (!entry || typeof entry !== "object")
|
|
60
|
+
continue;
|
|
61
|
+
const e = entry;
|
|
62
|
+
const url = typeof e.url === "string" ? e.url.trim() : "";
|
|
63
|
+
if (!url)
|
|
64
|
+
continue;
|
|
65
|
+
const visibility = e.visibility === "private" ? "private" : "public";
|
|
66
|
+
const name = typeof e.name === "string" && e.name.trim()
|
|
67
|
+
? e.name.trim()
|
|
68
|
+
: repoNameFromUrl(url);
|
|
69
|
+
if (!isSafePathComponent(name))
|
|
70
|
+
continue;
|
|
71
|
+
out.push({ url, visibility, name });
|
|
72
|
+
}
|
|
73
|
+
return out;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Best-effort spawn: log a one-line outcome and carry on. NEVER throws and NEVER
|
|
77
|
+
* echoes child output — the same secret-safety stance as `runChecked`, since these
|
|
78
|
+
* children (auth refresh, secrets exec) touch credentials.
|
|
79
|
+
*/
|
|
80
|
+
function runBestEffort(deps, command, args, description, options) {
|
|
81
|
+
let result;
|
|
82
|
+
try {
|
|
83
|
+
result = deps.spawnSync(command, args, options ?? { stdio: "inherit" });
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
console.warn(`replica-sync: ${description} could not run — continuing.`);
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
if (!result || result.error || result.status !== 0) {
|
|
90
|
+
console.warn(`replica-sync: ${description} did not complete cleanly — continuing.`);
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Authenticate `gh` for private clones by streaming GITHUB_TOKEN through the vault
|
|
97
|
+
* into the child's ENV ONLY, unsetting it in-shell, and piping it to
|
|
98
|
+
* `gh auth login --with-token` over stdin. The token never touches argv or this
|
|
99
|
+
* process's logs. Mirrors `personal/scripts/setup.sh`'s `configure_gh`.
|
|
100
|
+
*/
|
|
101
|
+
function authGitHubViaVault(deps) {
|
|
102
|
+
const ghLogin = [
|
|
103
|
+
'TOKEN="$GITHUB_TOKEN"',
|
|
104
|
+
"unset GITHUB_TOKEN",
|
|
105
|
+
'printf "%s" "$TOKEN" | gh auth login --with-token',
|
|
106
|
+
].join("\n");
|
|
107
|
+
const authed = runBestEffort(deps, "hq", [
|
|
108
|
+
"secrets",
|
|
109
|
+
"--personal",
|
|
110
|
+
"exec",
|
|
111
|
+
"--only",
|
|
112
|
+
"GITHUB_TOKEN",
|
|
113
|
+
"--",
|
|
114
|
+
"bash",
|
|
115
|
+
"-c",
|
|
116
|
+
ghLogin,
|
|
117
|
+
], "GitHub auth via vault");
|
|
118
|
+
if (authed) {
|
|
119
|
+
// Let plain `git clone https://github.com/...` reuse gh's token so private
|
|
120
|
+
// repos don't prompt for a username/password.
|
|
121
|
+
runBestEffort(deps, "gh", ["auth", "setup-git"], "gh auth setup-git");
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/** Clone any repos from `personal/data/repos.yaml` that aren't on disk yet. */
|
|
125
|
+
function replicateRepos(deps, hqRoot) {
|
|
126
|
+
const manifestPath = path.join(hqRoot, "personal", "data", "repos.yaml");
|
|
127
|
+
let manifestText;
|
|
128
|
+
try {
|
|
129
|
+
manifestText = deps.readTextFile(manifestPath);
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
console.log("replica-sync: no personal/data/repos.yaml — skipping repo clone.");
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const repos = parseReposManifest(manifestText);
|
|
136
|
+
if (repos.length === 0) {
|
|
137
|
+
console.log("replica-sync: repos.yaml lists no repos — skipping.");
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const missing = repos.filter((repo) => !deps.pathExists(path.join(hqRoot, "repos", repo.visibility, repo.name, ".git")));
|
|
141
|
+
if (missing.length === 0) {
|
|
142
|
+
console.log("replica-sync: all repos already present.");
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
// Private repos need gh auth first; public-only manifests skip the vault call.
|
|
146
|
+
if (missing.some((repo) => repo.visibility === "private")) {
|
|
147
|
+
authGitHubViaVault(deps);
|
|
148
|
+
}
|
|
149
|
+
for (const repo of missing) {
|
|
150
|
+
deps.mkdirp(path.join(hqRoot, "repos", repo.visibility));
|
|
151
|
+
const dest = path.join(hqRoot, "repos", repo.visibility, repo.name);
|
|
152
|
+
console.log(`replica-sync: cloning ${repo.visibility}/${repo.name}…`);
|
|
153
|
+
runBestEffort(deps, "git", ["clone", "--recurse-submodules", repo.url, dest], `clone ${repo.visibility}/${repo.name}`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/** Run one replica cycle. Best-effort throughout — see the module doc comment. */
|
|
157
|
+
export async function replicaSyncOutpost(opts, deps) {
|
|
158
|
+
const hqRoot = opts.hqRoot ?? deps.defaultHqRoot();
|
|
159
|
+
// Gate: only replicate on a box that has an HQ session. A box that was never
|
|
160
|
+
// signed in has nothing to pull — exit cleanly so the timer stays green.
|
|
161
|
+
const session = deps.loadCachedTokens();
|
|
162
|
+
if (!session?.refreshToken) {
|
|
163
|
+
console.log("replica-sync: no HQ session on this box yet — nothing to replicate.");
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
// Auth refresh gates the run: if the seeded refresh token is dead, every
|
|
167
|
+
// authenticated step below (pull, secrets exec) would just cascade 401s. Stop
|
|
168
|
+
// the cycle cleanly instead — the timer retries next tick.
|
|
169
|
+
if (!runBestEffort(deps, "hq", ["auth", "refresh"], "HQ auth refresh")) {
|
|
170
|
+
console.warn("replica-sync: auth refresh failed — skipping this cycle (token likely expired).");
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
runBestEffort(deps, "hq", [
|
|
174
|
+
"sync",
|
|
175
|
+
"pull",
|
|
176
|
+
"--personal",
|
|
177
|
+
"--hq-root",
|
|
178
|
+
hqRoot,
|
|
179
|
+
"--on-conflict",
|
|
180
|
+
"keep",
|
|
181
|
+
], "personal vault pull");
|
|
182
|
+
// rescue lays down the core kernel and expects companies/ to exist.
|
|
183
|
+
deps.mkdirp(path.join(hqRoot, "companies"));
|
|
184
|
+
runBestEffort(deps, "hq", ["rescue", "--hq-root", hqRoot, "--yes"], "HQ kernel rescue");
|
|
185
|
+
replicateRepos(deps, hqRoot);
|
|
186
|
+
console.log("replica-sync: complete.");
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=replica-sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replica-sync.js","sourceRoot":"","sources":["../../src/outposts/replica-sync.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replica-sync.test.d.ts","sourceRoot":"","sources":["../../src/outposts/replica-sync.test.ts"],"names":[],"mappings":""}
|