@clawops/cli 2.0.1 → 2.1.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.
Files changed (38) hide show
  1. package/README.md +862 -807
  2. package/dist/{apply-KYFPWI5U.js → apply-HJ5GAKHW.js} +7 -7
  3. package/dist/{automation-RMAIBNVC.js → automation-MKCJ46I6.js} +3 -3
  4. package/dist/{bootstrap-HNGKY6WW.js → bootstrap-NN647RCY.js} +2 -2
  5. package/dist/{chunk-HSPHXG6W.js → chunk-3Z5TDXCJ.js} +2 -2
  6. package/dist/{chunk-CX5SL5HP.js → chunk-64PONSA5.js} +10 -1
  7. package/dist/{chunk-STLRHLLV.js → chunk-A2DS5SB7.js} +12 -9
  8. package/dist/{chunk-KC3GC3PE.js → chunk-AYUX4FOF.js} +127 -13
  9. package/dist/{chunk-LEVIW2PV.js → chunk-BELTQGHT.js} +16 -2
  10. package/dist/{preflight-MWWM5JT3.js → chunk-BTW7NHOH.js} +3 -2
  11. package/dist/{chunk-A2I76FTA.js → chunk-ELU3XQEA.js} +1 -1
  12. package/dist/{chunk-IVWP4QUJ.js → chunk-GFUK6KDR.js} +10 -10
  13. package/dist/{chunk-W3OLNNJW.js → chunk-PSOSBJVO.js} +1 -1
  14. package/dist/{chunk-5XT5IAJK.js → chunk-QDNFADJI.js} +12 -5
  15. package/dist/{chunk-LBE6COC4.js → chunk-R3DTA35B.js} +22 -3
  16. package/dist/{preflight-EF52S3WW.js → chunk-TC2VYPLH.js} +3 -2
  17. package/dist/chunk-X56CQ6GB.js +157 -0
  18. package/dist/{chunk-BSLIYB5Q.js → chunk-XFN3YDZE.js} +1 -1
  19. package/dist/{cli-WLD3EBEM.js → cli-NH7VUNOU.js} +1 -1
  20. package/dist/cli.js +156 -82
  21. package/dist/{context-7EVC6W45.js → context-7VI3R6EK.js} +4 -4
  22. package/dist/flows-RI7FGHAM.js +120 -0
  23. package/dist/{generate-I6YQ5V2T.js → generate-YEM73KVB.js} +6 -6
  24. package/dist/{harden-72JMRT4E.js → harden-3KKCR7BL.js} +541 -3
  25. package/dist/{mcp-wire-3SWBM7D6.js → mcp-wire-TE2ZL6HD.js} +10 -10
  26. package/dist/{package-5BCMEBTE.js → package-JWDMWPV3.js} +47 -10
  27. package/dist/preflight-7W23GP4R.js +19 -0
  28. package/dist/preflight-IE2YWBAC.js +13 -0
  29. package/dist/private-only-DIA26I2E.js +74 -0
  30. package/dist/{server-GBA7BJGK.js → server-ZJPXPRZT.js} +10 -10
  31. package/dist/{stack-config-R4SPSUQZ.js → stack-config-LUTS2XAD.js} +2 -2
  32. package/dist/{state-PRBIIN7I.js → state-AOAYLYY5.js} +2 -2
  33. package/dist/{store-SDUR52Z5.js → store-OVB3DAJR.js} +1 -1
  34. package/dist/tailscale-cutover-6YP2QKFM.js +113 -0
  35. package/dist/{validate-YVI3BJ55.js → validate-JXKN5WD6.js} +1 -1
  36. package/package.json +39 -8
  37. package/spec/deploy-plan.schema.json +16 -1
  38. package/spec/mcp-tools.yaml +67 -9
@@ -0,0 +1,120 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/harden/flows.ts
4
+ async function cutOverToTailnet(ctx, conn, config) {
5
+ const { withRemoteExec } = await import("./harden-3KKCR7BL.js");
6
+ const { verifyTailnetAddress, probeSsh } = await import("./tailscale-cutover-6YP2QKFM.js");
7
+ const { setConfig } = await import("./store-OVB3DAJR.js");
8
+ const result = await withRemoteExec(
9
+ conn,
10
+ void 0,
11
+ (exec) => verifyTailnetAddress(conn, { exec, probe: (target) => probeSsh(target) })
12
+ );
13
+ if (!result.ok) return result;
14
+ const stack = config.stacks[ctx.stackName];
15
+ if (!stack) {
16
+ return {
17
+ ok: false,
18
+ reason: `Stack "${ctx.stackName}" is not in config, so the tailnet address could not be recorded.`
19
+ };
20
+ }
21
+ setConfig({
22
+ ...config,
23
+ stacks: {
24
+ ...config.stacks,
25
+ [ctx.stackName]: {
26
+ ...stack,
27
+ tailscale: {
28
+ ip: result.ip,
29
+ ...result.hostname ? { hostname: result.hostname } : {},
30
+ verifiedAt: (/* @__PURE__ */ new Date()).toISOString()
31
+ }
32
+ }
33
+ }
34
+ });
35
+ return {
36
+ ok: true,
37
+ ip: result.ip,
38
+ ...result.hostname ? { hostname: result.hostname } : {},
39
+ pinned: result.pinned,
40
+ message: `clawops now reaches "${ctx.stackName}" at ${result.ip} over the tailnet (${result.pinned} host key${result.pinned === 1 ? "" : "s"} pinned through the public connection). The public address is still open; nothing has been closed.`
41
+ };
42
+ }
43
+ async function revertTailnet(ctx, config) {
44
+ const override = config.stacks[ctx.stackName]?.tailscale;
45
+ if (!override) {
46
+ return {
47
+ ok: true,
48
+ noop: true,
49
+ message: `Stack "${ctx.stackName}" is not using a tailnet address; nothing to revert.`
50
+ };
51
+ }
52
+ const { buildContext } = await import("./context-7VI3R6EK.js");
53
+ const { extractBaseOutputs } = await import("./outputs-DJHBY7EE.js");
54
+ const { probeSsh, leaveTailnet } = await import("./tailscale-cutover-6YP2QKFM.js");
55
+ const { withRemoteExec } = await import("./harden-3KKCR7BL.js");
56
+ const { getConfig, setConfig } = await import("./store-OVB3DAJR.js");
57
+ const { forgetHost } = await import("./known-hosts-file-I3Q3NBYQ.js");
58
+ const direct = buildContext({ stack: ctx.stackName, ignoreTailnet: true });
59
+ const outputMap = await (await direct.getStack()).outputs();
60
+ const raw = Object.fromEntries(Object.entries(outputMap).map(([k, v]) => [k, v.value]));
61
+ const publicConn = direct.adapter.getConnectionInfo({
62
+ ...extractBaseOutputs(raw),
63
+ privateKeyPath: config.ssh.keyPath,
64
+ knownHostsPath: config.ssh.knownHostsPath
65
+ });
66
+ if (!await probeSsh(publicConn)) {
67
+ return override.privateOnly ? {
68
+ ok: false,
69
+ needsReopen: true,
70
+ reason: `"${ctx.stackName}" is private-only: its public ports are closed, so there is no way to leave the tailnet without losing the host. Reopen SSH first, then run this again:
71
+ clawops plan --stack ${ctx.stackName} --ssh-cidr auto --out <abs-path>/plan.json
72
+ clawops apply <abs-path>/plan.json
73
+ Nothing was changed.`
74
+ } : {
75
+ ok: false,
76
+ needsReopen: false,
77
+ reason: `This machine cannot reach "${ctx.stackName}" at its public address ${publicConn.host}, and leaving the tailnet would leave no way in. Nothing was changed.`
78
+ };
79
+ }
80
+ const left = await withRemoteExec(publicConn, void 0, (exec) => leaveTailnet(exec));
81
+ if (!left.ok) {
82
+ return {
83
+ ok: false,
84
+ needsReopen: false,
85
+ reason: `${left.reason}. clawops still uses the tailnet address; nothing else was changed.`
86
+ };
87
+ }
88
+ const fresh = getConfig() ?? config;
89
+ const stack = fresh.stacks[ctx.stackName];
90
+ if (stack) {
91
+ const { tailscale: _dropped, ...rest } = stack;
92
+ void _dropped;
93
+ setConfig({ ...fresh, stacks: { ...fresh.stacks, [ctx.stackName]: rest } });
94
+ }
95
+ forgetHost(expandHome(config.ssh.knownHostsPath), override.ip, publicConn.port);
96
+ return {
97
+ ok: true,
98
+ publicHost: publicConn.host,
99
+ tailnetIp: override.ip,
100
+ message: `"${ctx.stackName}" has left the tailnet; clawops reaches it at ${publicConn.host} again (the pinned key for ${override.ip} was forgotten).`
101
+ };
102
+ }
103
+ async function connectionFor(ctx, config) {
104
+ const { extractBaseOutputs } = await import("./outputs-DJHBY7EE.js");
105
+ const outputMap = await (await ctx.getStack()).outputs();
106
+ const raw = Object.fromEntries(Object.entries(outputMap).map(([k, v]) => [k, v.value]));
107
+ return ctx.adapter.getConnectionInfo({
108
+ ...extractBaseOutputs(raw),
109
+ privateKeyPath: config.ssh.keyPath,
110
+ knownHostsPath: config.ssh.knownHostsPath
111
+ });
112
+ }
113
+ function expandHome(p) {
114
+ return p.replace(/^~/, process.env["HOME"] ?? "~");
115
+ }
116
+ export {
117
+ connectionFor,
118
+ cutOverToTailnet,
119
+ revertTailnet
120
+ };
@@ -5,19 +5,19 @@ import {
5
5
  isInstanceAlias,
6
6
  parseDiffForTest,
7
7
  planId
8
- } from "./chunk-STLRHLLV.js";
9
- import "./chunk-BSLIYB5Q.js";
10
- import "./chunk-5XT5IAJK.js";
8
+ } from "./chunk-A2DS5SB7.js";
9
+ import "./chunk-XFN3YDZE.js";
10
+ import "./chunk-QDNFADJI.js";
11
11
  import "./chunk-JW5A7E3X.js";
12
- import "./chunk-LEVIW2PV.js";
12
+ import "./chunk-BELTQGHT.js";
13
13
  import "./chunk-GIEVAGEK.js";
14
14
  import "./chunk-VOEJYRIK.js";
15
15
  import "./chunk-EI7CQQ7D.js";
16
16
  import "./chunk-FEK7VJAY.js";
17
17
  import "./chunk-RHOWYIWT.js";
18
18
  import "./chunk-N4WZLWJ4.js";
19
- import "./chunk-A2I76FTA.js";
20
- import "./chunk-CX5SL5HP.js";
19
+ import "./chunk-ELU3XQEA.js";
20
+ import "./chunk-64PONSA5.js";
21
21
  import "./chunk-KGXPLI7W.js";
22
22
  import "./chunk-VUFMZEIM.js";
23
23
  export {